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(
+ $('', {
+ 'name': 'user_data',
+ 'data-remote': 'false',
+ 'data-params': 'data1=value1',
+ 'data-url': '/echo'
+ })
+ .append($('', {value: 'optionValue1', text: 'option1'}))
+ .append($('', {value: 'optionValue2', text: 'option2'}))
+ );
+
+ $('select[data-remote=false]:first')
+ .bind('ajax:beforeSend', function() {
+ ok(false, 'ajax should not be triggered');
+ })
+ .val('optionValue2')
+ .trigger('change');
+
+ setTimeout(function(){ start(); }, 20);
+});
From 5c1fe589dc1fff9f45c70a001e4bbdfea47baf60 Mon Sep 17 00:00:00 2001
From: Leif Gustafson
Date: Mon, 29 Jun 2015 18:22:09 -0800
Subject: [PATCH 12/52] Fix for #426 along with tests
---
src/rails.js | 11 +++++++----
test/public/test/call-remote.js | 23 +++++++++++++++++++++--
2 files changed, 28 insertions(+), 6 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index b8636b96..65f8add0 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -189,10 +189,13 @@
// 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);
+ // If URL protocol *and* host are false, assume it is not a
+ // cross-domain request (should only be the case for IE7 and IE
+ // compatibility mode). Otherwise, evaluate protocol and host of the
+ // URL against the origin protocol and host
+ 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;
diff --git a/test/public/test/call-remote.js b/test/public/test/call-remote.js
index 94316e88..a93bfc4b 100644
--- a/test/public/test/call-remote.js
+++ b/test/public/test/call-remote.js
@@ -122,10 +122,10 @@ asyncTest('sends CSRF token in custom header', 1, function() {
});
});
-asyncTest('intelligently guesses crossDomain behavior when target URL is a different domain', 1, function(e, xhr) {
+asyncTest('intelligently guesses crossDomain behavior when target URL has a protocol and hostname', 1, function(e, xhr) {
// Don't set data-cross-domain here, just set action to be a different domain than localhost
- buildForm({ action: 'http://www.alfajango.com' });
+ buildForm({ action: 'http://www.alfajango.com/' });
$('#qunit-fixture').append('');
$('#qunit-fixture').find('form')
@@ -140,4 +140,23 @@ asyncTest('intelligently guesses crossDomain behavior when target URL is a diffe
setTimeout(function() { start(); }, 13);
});
+
+asyncTest('intelligently guesses crossDomain behavior when target URL consists of only a path', 1, function(e, xhr) {
+
+ // Don't set data-cross-domain here, just set action to be a different domain than localhost
+ buildForm({ action: '/just/a/path' });
+ $('#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);
+});
})();
From d41edc8abf90a3132c85c6b494135810c02f126c Mon Sep 17 00:00:00 2001
From: Leif Gustafson
Date: Mon, 29 Jun 2015 18:25:52 -0800
Subject: [PATCH 13/52] Update wording of test
---
test/public/test/call-remote.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/public/test/call-remote.js b/test/public/test/call-remote.js
index a93bfc4b..80d801c5 100644
--- a/test/public/test/call-remote.js
+++ b/test/public/test/call-remote.js
@@ -122,7 +122,7 @@ asyncTest('sends CSRF token in custom header', 1, function() {
});
});
-asyncTest('intelligently guesses crossDomain behavior when target URL has a protocol and hostname', 1, function(e, xhr) {
+asyncTest('intelligently guesses crossDomain behavior when target URL has a different protocol and/or hostname', 1, function(e, xhr) {
// Don't set data-cross-domain here, just set action to be a different domain than localhost
buildForm({ action: 'http://www.alfajango.com/' });
From d7856b9ad16d9afc5fd772f460d570b2ae182f70 Mon Sep 17 00:00:00 2001
From: Alexander Kaupanin
Date: Tue, 30 Jun 2015 00:12:06 -0500
Subject: [PATCH 14/52] Added .jsnihtrc set up to comply with current styles of
the project
---
.jshintrc | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 71 insertions(+)
create mode 100644 .jshintrc
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 00000000..9bfd6603
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,71 @@
+{
+ "maxerr" : 50, // {int} Maximum error before stopping
+
+ // Enforcing
+ "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
+ "camelcase" : true, // true: Identifiers must be in camelCase
+ "curly" : false, // true: Require {} for every new block or scope
+ "eqeqeq" : true, // true: Require triple equals (===) for comparison
+ "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty()
+ "freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc.
+ "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
+ "indent" : 2, // {int} Number of spaces to use for indentation
+ "latedef" : false, // true: Require variables/functions to be defined before being used
+ "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
+ "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
+ "noempty" : true, // true: Prohibit use of empty blocks
+ "nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters.
+ "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
+ "plusplus" : false, // true: Prohibit use of `++` & `--`
+ "quotmark" : "single", // Quotation mark consistency:
+ // false : do nothing (default)
+ // true : ensure whatever is used is consistent
+ // "single" : require single quotes
+ // "double" : require double quotes
+ "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
+ "unused" : true, // Unused variables:
+ // true : all variables, last function parameter
+ // "vars" : all variables only
+ // "strict" : all variables, all function parameters
+ "strict" : true, // true: Requires all functions run in ES5 Strict Mode
+ "maxparams" : false, // {int} Max number of formal params allowed per function
+ "maxdepth" : false, // {int} Max depth of nested blocks (within functions)
+ "maxstatements" : false, // {int} Max number statements per function
+ "maxcomplexity" : false, // {int} Max cyclomatic complexity per function
+ "maxlen" : false, // {int} Max number of characters per line
+
+ // Relaxing
+ "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
+ "boss" : false, // true: Tolerate assignments where comparisons would be expected
+ "debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
+ "eqnull" : false, // true: Tolerate use of `== null`
+ "es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
+ "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`)
+ "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
+ // (ex: `for each`, multiple try/catch, function expression…)
+ "evil" : false, // true: Tolerate use of `eval` and `new Function()`
+ "expr" : false, // true: Tolerate `ExpressionStatement` as Programs
+ "funcscope" : false, // true: Tolerate defining variables inside control statements
+ "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
+ "iterator" : false, // true: Tolerate using the `__iterator__` property
+ "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
+ "laxbreak" : false, // true: Tolerate possibly unsafe line breakings
+ "laxcomma" : false, // true: Tolerate comma-first style coding
+ "loopfunc" : false, // true: Tolerate functions being defined in loops
+ "multistr" : false, // true: Tolerate multi-line strings
+ "noyield" : false, // true: Tolerate generator functions with no yield statement in them.
+ "notypeof" : false, // true: Tolerate invalid typeof operator values
+ "proto" : false, // true: Tolerate using the `__proto__` property
+ "scripturl" : false, // true: Tolerate script-targeted URLs
+ "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
+ "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
+ "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
+ "validthis" : false, // true: Tolerate using this in a non-constructor function
+
+ // Environments
+ "browser" : true, // Web Browser (window, document, etc)
+ "jquery" : true,
+ "devel" : true, // Development/debugging (alert, confirm, etc)
+
+ "globals" : {}
+}
From 4fd45f9644350ab76705a882d608ace217fc008e Mon Sep 17 00:00:00 2001
From: Alexander Kaupanin
Date: Tue, 30 Jun 2015 00:53:40 -0500
Subject: [PATCH 15/52] Made style check part of ci execution command
---
.travis.yml | 2 ++
script/cibuild | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index 985c8a05..21eb4736 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,7 @@
language: ruby
script: ./script/cibuild
+before_install:
+ - "npm install jshint -g"
env:
- JQUERY_VERSION: 1.8.0
- JQUERY_VERSION: 1.8.1
diff --git a/script/cibuild b/script/cibuild
index cc661ece..45c97099 100755
--- a/script/cibuild
+++ b/script/cibuild
@@ -8,7 +8,7 @@ start_server() {
}
run_tests() {
- phantomjs script/runner.js http://localhost:$port/
+ jshint src/*.js && phantomjs script/runner.js http://localhost:$port/
}
server_started() {
From c8798aab03e5c6a6b8f3e5d2719e5a7fbcc9c887 Mon Sep 17 00:00:00 2001
From: Alexander Kaupanin
Date: Tue, 30 Jun 2015 00:13:53 -0500
Subject: [PATCH 16/52] jshint: fixed quotes
---
src/rails.js | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index b8636b96..77873406 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -124,12 +124,12 @@
method = element.data('method');
url = element.data('url');
data = element.serialize();
- if (element.data('params')) data = data + "&" + element.data('params');
+ if (element.data('params')) data = data + '&' + element.data('params');
} else if (element.is(rails.buttonClickSelector)) {
method = element.data('method') || 'get';
url = element.data('url');
data = element.serialize();
- if (element.data('params')) data = data + "&" + element.data('params');
+ if (element.data('params')) data = data + '&' + element.data('params');
} else {
method = element.data('method');
url = rails.href(element);
@@ -180,9 +180,9 @@
// Determines if the request is a cross domain request.
isCrossDomain: function(url) {
- var originAnchor = document.createElement("a");
+ var originAnchor = document.createElement('a');
originAnchor.href = location.href;
- var urlAnchor = document.createElement("a");
+ var urlAnchor = document.createElement('a');
try {
urlAnchor.href = url;
@@ -191,8 +191,8 @@
// 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);
+ (originAnchor.protocol + '//' + originAnchor.host !==
+ urlAnchor.protocol + '//' + urlAnchor.host);
} catch (e) {
// If there is an error parsing the URL, assume it is crossDomain.
return true;
@@ -363,11 +363,11 @@
//
// See https://github.com/rails/jquery-ujs/issues/357
// See https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching
- $(window).on("pageshow.rails", function () {
+ $(window).on('pageshow.rails', function () {
$($.rails.enableSelector).each(function () {
var element = $(this);
- if (element.data("ujs:enable-with")) {
+ if (element.data('ujs:enable-with')) {
$.rails.enableFormElement(element);
}
});
@@ -375,7 +375,7 @@
$($.rails.linkDisableSelector).each(function () {
var element = $(this);
- if (element.data("ujs:enable-with")) {
+ if (element.data('ujs:enable-with')) {
$.rails.enableElement(element);
}
});
From 3825c523a7ac6ed942aea7a332a5f2c551f1914c Mon Sep 17 00:00:00 2001
From: Alexander Kaupanin
Date: Tue, 30 Jun 2015 00:15:16 -0500
Subject: [PATCH 17/52] jshint: strict equality
---
src/rails.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index 77873406..dfa9e13f 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -447,7 +447,7 @@
if (!rails.allowAction(form)) return rails.stopEverything(e);
// skip other logic when required values are missing or file upload is present
- if (form.attr('novalidate') == undefined) {
+ if (form.attr('novalidate') === undefined) {
blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector);
if (blankRequiredInputs && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) {
return rails.stopEverything(e);
@@ -490,11 +490,11 @@
});
$document.delegate(rails.formSubmitSelector, 'ajax:send.rails', function(event) {
- if (this == event.target) rails.disableFormElements($(this));
+ if (this === event.target) rails.disableFormElements($(this));
});
$document.delegate(rails.formSubmitSelector, 'ajax:complete.rails', function(event) {
- if (this == event.target) rails.enableFormElements($(this));
+ if (this === event.target) rails.enableFormElements($(this));
});
$(function(){
From b0aac281c309b961503601e9d4ebf7b8d59a19ce Mon Sep 17 00:00:00 2001
From: Alexander Kaupanin
Date: Tue, 30 Jun 2015 00:26:16 -0500
Subject: [PATCH 18/52] jshint: use strict
---
src/rails.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/rails.js b/src/rails.js
index dfa9e13f..2e222787 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -12,6 +12,8 @@
// Cut down on the number of issues from people inadvertently including jquery_ujs twice
// by detecting and raising an error when it happens.
+ 'use strict';
+
if ( $.rails !== undefined ) {
$.error('jquery-ujs has already been loaded!');
}
From b50baaddcaa368289841c756b8ea23530bd49058 Mon Sep 17 00:00:00 2001
From: Alexander Kaupanin
Date: Tue, 30 Jun 2015 00:39:15 -0500
Subject: [PATCH 19/52] jshint: confusing use of '!'. `valueToCheck` to always
be boolean in comparison; set value to `nonBlank` on invocation.
---
src/rails.js | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index 2e222787..2dc31e30 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -304,9 +304,8 @@
allInputs.each(function() {
input = $(this);
- valueToCheck = input.is('input[type=checkbox],input[type=radio]') ? input.is(':checked') : input.val();
- // If nonBlank and valueToCheck are both truthy, or nonBlank and valueToCheck are both falsey
- if (!valueToCheck === !nonBlank) {
+ valueToCheck = input.is('input[type=checkbox],input[type=radio]') ? input.is(':checked') : !!input.val();
+ if (valueToCheck === nonBlank) {
// 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) {
@@ -450,7 +449,7 @@
// skip other logic when required values are missing or file upload is present
if (form.attr('novalidate') === undefined) {
- blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector);
+ blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector, false);
if (blankRequiredInputs && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) {
return rails.stopEverything(e);
}
From 4f2d946d2fa34b41f4c4b58682117c2985962c97 Mon Sep 17 00:00:00 2001
From: lulalala
Date: Sat, 4 Jul 2015 00:02:19 +0800
Subject: [PATCH 20/52] Ignore disabled file input
Disabled file input form should be ignored,
however currently submitting a form with disabled file input
will cause issue on Rails' side.
---
src/rails.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/rails.js b/src/rails.js
index 2dc31e30..90d50bd4 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]',
+ fileInputSelector: 'input[type=file]:not([disabled])',
// Link onClick disable selector with possible reenable after remote submission
linkDisableSelector: 'a[data-disable-with], a[data-disable]',
From c7e5f8f59db7e37410610658b7c3f1652fc01cbb Mon Sep 17 00:00:00 2001
From: Jens Balvig
Date: Thu, 9 Jul 2015 09:41:39 +0200
Subject: [PATCH 21/52] Disabled blank inputs didn't reset on ajax submit
Submitting a remote form with a blank input using [data-disable-with]
would cause the disable-with message to get stuck since if("") == false
---
src/rails.js | 2 +-
test/public/test/data-disable-with.js | 18 ++++++++++++++++++
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/src/rails.js b/src/rails.js
index 2dc31e30..7feda212 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -266,7 +266,7 @@
enableFormElement: function(element) {
var method = element.is('button') ? 'html' : 'val';
- if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with'));
+ if (typeof element.data('ujs:enable-with') !== 'undefined') element[method](element.data('ujs:enable-with'));
element.prop('disabled', false);
},
diff --git a/test/public/test/data-disable-with.js b/test/public/test/data-disable-with.js
index 5e2a67da..0366f81e 100644
--- a/test/public/test/data-disable-with.js
+++ b/test/public/test/data-disable-with.js
@@ -61,6 +61,24 @@ asyncTest('form input field with "data-disable-with" attribute', 7, function() {
App.checkDisabledState(input, 'processing ...');
});
+asyncTest('blank form input field with "data-disable-with" attribute', 7, function() {
+ var form = $('form[data-remote]'), input = form.find('input[type=text]');
+
+ input.val('');
+ App.checkEnabledState(input, '');
+
+ form.bind('ajax:success', function(e, data) {
+ setTimeout(function() {
+ App.checkEnabledState(input, '');
+ equal(data.params.user_name, '');
+ 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);
From 3d58ddddba7222786a65b99e945980adfa648dd1 Mon Sep 17 00:00:00 2001
From: Leif Gustafson
Date: Thu, 30 Jul 2015 14:17:15 -0800
Subject: [PATCH 22/52] Additional cross-domain fixes related to IE8 in
compatibility mode (thanks to @dankohn and @blargoner)
---
src/rails.js | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index 65f8add0..4ffdf10a 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -189,11 +189,12 @@
// This is a workaround to a IE bug.
urlAnchor.href = urlAnchor.href;
- // If URL protocol *and* host are false, assume it is not a
- // cross-domain request (should only be the case for IE7 and IE
- // compatibility mode). Otherwise, evaluate protocol and host of the
- // URL against the origin protocol and host
- return !((!urlAnchor.protocol && !urlAnchor.host) ||
+ // If URL protocol is false or is a string containing a single colon
+ // *and* host are false, assume it is not a cross-domain request
+ // (should only be the case for IE7 and IE compatibility mode).
+ // Otherwise, evaluate protocol and host of the URL against the origin
+ // protocol and host
+ return !(((!urlAnchor.protocol || urlAnchor.protocol === ':') && !urlAnchor.host) ||
(originAnchor.protocol + "//" + originAnchor.host ===
urlAnchor.protocol + "//" + urlAnchor.host));
} catch (e) {
From e228a365686d5f1358c28feec8d81325cf45fa8b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?=
Date: Tue, 1 Sep 2015 02:21:56 -0300
Subject: [PATCH 23/52] Release 1.1.0
---
CHANGELOG.md | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2a90470c..028ef2fa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,15 @@
+## v1.1.0
+
+* Extract `$.rails.csrfToken` and `$.rails.csrfParam` functions to allow external usage.
+
+* Treat exception in `$.rails.confirm` as a false answer.
+
+* Don't fire Ajax requests if `data-remote` is `false`.
+
+* Fix IE7 bug with the cross domain check.
+
+* Ignore disabled file inputs when submitting the form.
+
## v1.0.4
* Fix CSP bypass vulnerability.
From 32bb07042b051d372fd12e3149896abfc513d9c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?=
Date: Tue, 1 Sep 2015 02:24:12 -0300
Subject: [PATCH 24/52] Remove CHANGELOG
We are using GitHub releases now.
---
CHANGELOG.md | 44 --------------------------------------------
1 file changed, 44 deletions(-)
delete mode 100644 CHANGELOG.md
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index 028ef2fa..00000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,44 +0,0 @@
-## v1.1.0
-
-* Extract `$.rails.csrfToken` and `$.rails.csrfParam` functions to allow external usage.
-
-* Treat exception in `$.rails.confirm` as a false answer.
-
-* Don't fire Ajax requests if `data-remote` is `false`.
-
-* Fix IE7 bug with the cross domain check.
-
-* Ignore disabled file inputs when submitting the form.
-
-## v1.0.4
-
-* Fix CSP bypass vulnerability.
-
- CVE-2015-1840.
-
- *Rafael Mendonça França*
-
-## v1.0.3
-
-* Replace deprecated `deferred.error()` with `fail()`.
-
- *Carlos Antonio da Silva*
-## v1.0.2
-
-* Re-enables buttons and links after going back through the `pageshow` event.
-
- *Gabriel Sobrinho*
-
-## v1.0.1
-
-* `confirm` is no longer called twice for `button` elements inside a `form`.
-
- *Lucas Mazza*
-
-* `button` or submit inputs using the `form` attribute are properly bound.
-
- *Marnen Laibow-Koser*
-
-## v1.0.0
-
-* First tagged release.
From b516178a9edd3d16eb5549ad57bce62909266ed2 Mon Sep 17 00:00:00 2001
From: maclover7
Date: Tue, 1 Sep 2015 12:08:59 -0400
Subject: [PATCH 25/52] Add CONTRIBUTING.md [ci skip]
---
CONTRIBUTING.md | 110 +++++++++++++++++++++++++++++++++
MIT-LICENSE.txt => MIT-LICENSE | 2 +-
README.md | 11 +++-
3 files changed, 121 insertions(+), 2 deletions(-)
create mode 100644 CONTRIBUTING.md
rename MIT-LICENSE.txt => MIT-LICENSE (94%)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..64e6864d
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,110 @@
+Contributing to jquery-ujs
+=====================
+
+[](https://travis-ci.org/rails/jquery-ujs)
+
+jquery-ujs is work of [many contributors](https://github.com/rails/jquery-ujs/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/rails/jquery-ujs/pulls), [propose features and discuss issues](https://github.com/rails/jquery-ujs/issues).
+
+#### Fork the Project
+
+Fork the [project on Github](https://github.com/rails/jquery-ujs) and check out your copy.
+
+```
+git clone https://github.com/contributor/jquery-ujs.git
+cd jquery-ujs
+git remote add upstream https://github.com/rails/jquery-ujs.git
+```
+
+#### Create a Topic Branch
+
+Make sure your fork is up-to-date and create a topic branch for your feature or bug fix.
+
+```
+git checkout master
+git pull upstream master
+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.
+
+```
+bundle install
+rake test
+ruby test/server.rb
+```
+
+#### Write Tests
+
+Try to write a test that reproduces the problem you're trying to fix or describes a feature that you want to build. Add to [test](test).
+
+Here are some additional notes to keep in mind when developing your patch for jquery-ujs.
+
+* The tests can be found in:
+```
+ |~test
+ |~public
+ |~test
+```
+* Some tests ensure consistent behavior across the major browsers, meaning it is possible for tests to pass in Firefox, but fail in Internet Explorer. If possible, it helps if you can run the test suite in multiple browsers before submitting patches.
+
+We definitely appreciate pull requests that highlight or reproduce a problem, even without a fix.
+
+#### Write Code
+
+Implement your feature or bug fix.
+
+Make sure that `bundle exec rake test` completes without errors.
+
+#### Write Documentation
+
+Document any external behavior in the [README](README.md).
+
+#### Commit Changes
+
+Make sure git knows your name and email address:
+
+```
+git config --global user.name "Your Name"
+git config --global user.email "contributor@example.com"
+```
+
+Writing good commit logs is important. A commit log should describe what changed and why.
+
+```
+git add ...
+git commit
+```
+
+#### Push
+
+```
+git push origin my-feature-branch
+```
+
+#### Make a Pull Request
+
+Go to https://github.com/contributor/jquery-ujs and select your feature branch. Click the 'Pull Request' button and fill out the form. Pull requests are usually reviewed within a few days.
+
+#### Rebase
+
+If you've been working on a change for a while, rebase with upstream/master.
+
+```
+git fetch upstream
+git rebase upstream/master
+git push origin my-feature-branch -f
+```
+
+#### Check on Your Pull Request
+
+Go back to your pull request after a few minutes and see whether it passed muster with Travis-CI. Everything should look green, otherwise fix issues and amend your commit as described above.
+
+#### Be Patient
+
+It's likely that your change will not be merged and that the nitpicky maintainers will ask you to do more, or fix seemingly benign problems. Hang on there!
+
+#### Thank You
+
+Please do know that we really appreciate and value your time and work. We love you, really.
diff --git a/MIT-LICENSE.txt b/MIT-LICENSE
similarity index 94%
rename from MIT-LICENSE.txt
rename to MIT-LICENSE
index ed37a233..ed4c3901 100644
--- a/MIT-LICENSE.txt
+++ b/MIT-LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2007-2010 Contributors at http://github.com/rails/jquery-ujs/contributors
+Copyright (c) 2007-2015 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
diff --git a/README.md b/README.md
index 29ea432d..a9fd2ab4 100644
--- a/README.md
+++ b/README.md
@@ -60,8 +60,17 @@ Require both `jquery` and `jquery-ujs` into your application.js manifest.
How to run tests
------------
-Follow [this wiki](https://github.com/rails/jquery-ujs/wiki/Running-Tests-and-Contributing) to run tests .
+Follow [this wiki](https://github.com/rails/jquery-ujs/wiki/Running-Tests-and-Contributing) to run tests.
+## Contributing to jquery-ujs
+
+jquery-ujs is work of many contributors. You're encouraged to submit pull requests, propose
+features and discuss issues.
+
+See [CONTRIBUTING](CONTRIBUTING.md).
+
+## License
+jquery-ujs is released under the [MIT License](MIT-LICENSE).
[data]: http://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes "Embedding custom non-visible data with the data-* attributes"
[wiki]: https://github.com/rails/jquery-ujs/wiki
From 2830202d52dc073c8b6c2337755ad4ffbcbe8bf2 Mon Sep 17 00:00:00 2001
From: Andy Lampert
Date: Mon, 14 Sep 2015 19:10:04 -0600
Subject: [PATCH 26/52] uniform comment capitalization, fix small grammar
issues
---
README.md | 2 +-
src/rails.js | 20 ++++++++++----------
2 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/README.md b/README.md
index 29ea432d..872d9127 100644
--- a/README.md
+++ b/README.md
@@ -60,7 +60,7 @@ Require both `jquery` and `jquery-ujs` into your application.js manifest.
How to run tests
------------
-Follow [this wiki](https://github.com/rails/jquery-ujs/wiki/Running-Tests-and-Contributing) to run tests .
+Follow [this wiki](https://github.com/rails/jquery-ujs/wiki/Running-Tests-and-Contributing) to run tests.
[data]: http://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes "Embedding custom non-visible data with the data-* attributes"
diff --git a/src/rails.js b/src/rails.js
index ffbc0ef0..490a760c 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -72,7 +72,7 @@
if (token) xhr.setRequestHeader('X-CSRF-Token', token);
},
- // making sure that all forms have actual up-to-date token(cached forms contain old one)
+ // Make sure that all forms have actual up-to-date tokens (cached forms contain old ones)
refreshCSRFTokens: function(){
$('form input[name="' + rails.csrfParam() + '"]').val(rails.csrfToken());
},
@@ -334,7 +334,7 @@
return false;
},
- // replace element's html with the 'data-disable-with' after storing original html
+ // Replace element's html with the 'data-disable-with' after storing original html
// and prevent clicking on it
disableElement: function(element) {
var replacement = element.data('disable-with');
@@ -349,7 +349,7 @@
});
},
- // restore element to its original state which was disabled by 'disableElement' above
+ // Restore element to its original state which was disabled by 'disableElement' above
enableElement: function(element) {
if (element.data('ujs:enable-with') !== undefined) {
element.html(element.data('ujs:enable-with')); // set to old enabled state
@@ -404,7 +404,7 @@
if (metaClick && (!method || method === 'GET') && !data) { return true; }
var handleRemote = rails.handleRemote(link);
- // response from rails.handleRemote() will either be false or a deferred object promise.
+ // Response from rails.handleRemote() will either be false or a deferred object promise.
if (handleRemote === false) {
rails.enableElement(link);
} else {
@@ -426,7 +426,7 @@
if (button.is(rails.buttonDisableSelector)) rails.disableFormElement(button);
var handleRemote = rails.handleRemote(button);
- // response from rails.handleRemote() will either be false or a deferred object promise.
+ // Response from rails.handleRemote() will either be false or a deferred object promise.
if (handleRemote === false) {
rails.enableFormElement(button);
} else {
@@ -451,7 +451,7 @@
if (!rails.allowAction(form)) return rails.stopEverything(e);
- // skip other logic when required values are missing or file upload is present
+ // Skip other logic when required values are missing or file upload is present
if (form.attr('novalidate') === undefined) {
blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector, false);
if (blankRequiredInputs && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) {
@@ -462,12 +462,12 @@
if (remote) {
nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector);
if (nonBlankFileInputs) {
- // slight timeout so that the submit button gets properly serialized
+ // Slight timeout so that the submit button gets properly serialized
// (make it easy for event handler to serialize form without disabled values)
setTimeout(function(){ rails.disableFormElements(form); }, 13);
var aborted = rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]);
- // re-enable form elements if event bindings return false (canceling normal form submission)
+ // Re-enable form elements if event bindings return false (canceling normal form submission)
if (!aborted) { setTimeout(function(){ rails.enableFormElements(form); }, 13); }
return aborted;
@@ -477,7 +477,7 @@
return false;
} else {
- // slight timeout so that the submit button gets properly serialized
+ // Slight timeout so that the submit button gets properly serialized
setTimeout(function(){ rails.disableFormElements(form); }, 13);
}
});
@@ -487,7 +487,7 @@
if (!rails.allowAction(button)) return rails.stopEverything(event);
- // register the pressed submit button
+ // Register the pressed submit button
var name = button.attr('name'),
data = name ? {name:name, value:button.val()} : null;
From c61ae519820079b75195adb1beac73ff3fcc6e3c Mon Sep 17 00:00:00 2001
From: Chris Howlett
Date: Tue, 6 Oct 2015 12:41:26 +0100
Subject: [PATCH 27/52] Enable UJS on controls using the 'form' attribute
* Ensure disassociated inputs are serialized with the form
* Allow disassociated buttons to submit the form
---
src/rails.js | 10 ++++---
test/public/test/data-remote.js | 48 ++++++++++++++++++++++++++++++++-
2 files changed, 54 insertions(+), 4 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index 490a760c..aaf2ba69 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -27,7 +27,7 @@
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]:not(form button), button[data-confirm]:not(form button)',
+ buttonClickSelector: 'button[data-remote]:not([form]):not(form button), button[data-confirm]:not([form]):not(form button)',
// Select elements bound by jquery-ujs
inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]',
@@ -115,7 +115,7 @@
if (element.is('form')) {
method = element.attr('method');
url = element.attr('action');
- data = element.serializeArray();
+ data = data = $(element[0].elements).serializeArray();
// memoized value from clicked submit button
var button = element.data('ujs:submit-button');
if (button) {
@@ -491,7 +491,11 @@
var name = button.attr('name'),
data = name ? {name:name, value:button.val()} : null;
- button.closest('form').data('ujs:submit-button', data);
+ var form = button.closest('form');
+ if (form.length === 0) {
+ form = $('#' + button.attr('form'));
+ }
+ form.data('ujs:submit-button', data);
});
$document.delegate(rails.formSubmitSelector, 'ajax:send.rails', function(event) {
diff --git a/test/public/test/data-remote.js b/test/public/test/data-remote.js
index c0b47586..6d2cda74 100644
--- a/test/public/test/data-remote.js
+++ b/test/public/test/data-remote.js
@@ -16,7 +16,8 @@ module('data-remote', {
.append($('', {
action: '/echo',
'data-remote': 'true',
- method: 'post'
+ method: 'post',
+ id: 'my-remote-form'
}))
.find('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() });
+
+ $('', {
+ type: "submit",
+ name: "user_data",
+ value: "value1",
+ form: "my-remote-form"
+ })
+ .appendTo($('#qunit-fixture'));
+
+ $('', {
+ type: "submit",
+ name: "user_data",
+ value: "value2",
+ form: "my-remote-form"
+ })
+ .appendTo($('#qunit-fixture'))
+ .trigger('click');
+});
+
asyncTest('form\'s submit bindings in browsers that don\'t support submit bubbling', 5, function() {
var form = $('form[data-remote]'), directBindingCalled = false;
From be31d8b0905424d60ff1e9a051ece877123e33c7 Mon Sep 17 00:00:00 2001
From: Rune Schjellerup Philosof