From 85412da5f41038e365c77cd6d7ba4a165e76bc80 Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Sat, 13 Nov 2010 14:01:53 -0500
Subject: [PATCH 001/364] add rubygems.org to Gemfile and Gemfile.lock
---
Gemfile | 3 +++
Gemfile.lock | 1 +
2 files changed, 4 insertions(+)
diff --git a/Gemfile b/Gemfile
index a1767e96..d1793e4c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,2 +1,5 @@
+source 'http://rubygems.org'
+
gem "json"
+
gem "sinatra", "= 1.0"
diff --git a/Gemfile.lock b/Gemfile.lock
index 6e7745e3..4d10d04d 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,4 +1,5 @@
GEM
+ remote: http://rubygems.org/
specs:
json (1.4.6)
rack (1.2.1)
From e1ae2de9a5adf6612ad3a2d7388d63e50893721e Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Sat, 13 Nov 2010 22:39:04 -0500
Subject: [PATCH 002/364] remove lint warning by replacing != with !==
---
src/rails.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/rails.js b/src/rails.js
index 223fe7ea..9588bfbe 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -108,7 +108,7 @@ jQuery(function ($) {
form = $(''),
metadata_input = ' ';
- if (csrf_param != null && csrf_token != null) {
+ if (csrf_param !== undefined && csrf_token !== undefined) {
metadata_input += ' ';
}
From b1f0e58dc79e867ee68effb422205ed2459aca8b Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Sat, 13 Nov 2010 22:41:54 -0500
Subject: [PATCH 003/364] add ; to fix lint warning
---
src/rails.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/rails.js b/src/rails.js
index 9588bfbe..7abbb0ca 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -150,7 +150,7 @@ jQuery(function ($) {
var jqueryVersion = $().jquery;
if ( (jqueryVersion === '1.4') || (jqueryVersion === '1.4.1') || (jqueryVersion === '1.4.2') ){
- alert('This rails.js does not support the jQuery version you are using. Please read documentation.')
+ alert('This rails.js does not support the jQuery version you are using. Please read documentation.');
}
});
From 512691e9cd3108788ee138216a23b16d05407f69 Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Sat, 13 Nov 2010 22:50:36 -0500
Subject: [PATCH 004/364] fix indentation
---
src/rails.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index 7abbb0ca..ba4b2b56 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -44,7 +44,7 @@ jQuery(function ($) {
dataType = el.attr('data-type') || 'script';
if (url === undefined) {
- throw "No URL specified for remote call (action or href must be present).";
+ throw "No URL specified for remote call (action or href must be present).";
} else {
if (el.triggerAndReturn('ajax:before')) {
var data = el.is('form') ? el.serializeArray() : [];
@@ -109,7 +109,7 @@ jQuery(function ($) {
metadata_input = ' ';
if (csrf_param !== undefined && csrf_token !== undefined) {
- metadata_input += ' ';
+ metadata_input += ' ';
}
form.hide()
@@ -150,7 +150,7 @@ jQuery(function ($) {
var jqueryVersion = $().jquery;
if ( (jqueryVersion === '1.4') || (jqueryVersion === '1.4.1') || (jqueryVersion === '1.4.2') ){
- alert('This rails.js does not support the jQuery version you are using. Please read documentation.');
+ alert('This rails.js does not support the jQuery version you are using. Please read documentation.');
}
});
From 17dec3864174ffefadc012c23e470c303d95861f Mon Sep 17 00:00:00 2001
From: Steve Schwartz
Date: Sun, 14 Nov 2010 23:41:33 -0500
Subject: [PATCH 005/364] Added 'rails' namespace to all live and delegate
event bindings.
Signed-off-by: Neeraj Singh
---
src/rails.js | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index ba4b2b56..595de557 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -77,7 +77,7 @@ jQuery(function ($) {
* confirmation handler
*/
- $('body').delegate('a[data-confirm], button[data-confirm], input[data-confirm]', 'click', function () {
+ $('body').delegate('a[data-confirm], button[data-confirm], input[data-confirm]', 'click.rails', function () {
var el = $(this);
if (el.triggerAndReturn('confirm')) {
if (!confirm(el.attr('data-confirm'))) {
@@ -91,17 +91,17 @@ jQuery(function ($) {
/**
* remote handlers
*/
- $('form[data-remote]').live('submit', function (e) {
+ $('form[data-remote]').live('submit.rails', function (e) {
$(this).callRemote();
e.preventDefault();
});
- $('a[data-remote],input[data-remote]').live('click', function (e) {
+ $('a[data-remote],input[data-remote]').live('click.rails', function (e) {
$(this).callRemote();
e.preventDefault();
});
- $('a[data-method]:not([data-remote])').live('click', function (e){
+ $('a[data-method]:not([data-remote])').live('click.rails', function (e){
var link = $(this),
href = link.attr('href'),
method = link.attr('data-method'),
@@ -136,10 +136,10 @@ jQuery(function ($) {
});
};
- $(disable_with_form_remote_selector).live('ajax:before', disable_with_input_function);
- $(disable_with_form_not_remote_selector).live('submit', disable_with_input_function);
+ $(disable_with_form_remote_selector).live('ajax:before.rails', disable_with_input_function);
+ $(disable_with_form_not_remote_selector).live('submit.rails', disable_with_input_function);
- $(disable_with_form_remote_selector).live('ajax:complete', function () {
+ $(disable_with_form_remote_selector).live('ajax:complete.rails', function () {
$(this).find(disable_with_input_selector).each(function () {
var input = $(this);
input.removeAttr('disabled')
From 4ece456c505b6929cde996100770a67b1a805cfc Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Mon, 15 Nov 2010 12:39:40 -0500
Subject: [PATCH 006/364] respect global ajaxSettings declared via $.ajaxSetup
---
src/rails.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/rails.js b/src/rails.js
index 595de557..427e0c2f 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -41,7 +41,7 @@ jQuery(function ($) {
var el = this,
method = el.attr('method') || el.attr('data-method') || 'GET',
url = el.attr('action') || el.attr('href'),
- dataType = el.attr('data-type') || 'script';
+ dataType = el.attr('data-type') || ($.ajaxSettings && $.ajaxSettings.dataType) || 'script';
if (url === undefined) {
throw "No URL specified for remote call (action or href must be present).";
From ff185db3198d4891ea5606e9dd9ef950a897f349 Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Thu, 18 Nov 2010 14:54:51 -0500
Subject: [PATCH 007/364] get closer to jQuery. Do not set data-type as
'script' arbitrarily.
Possibly might break a few apps. However passing
data-type is always a good idea. Be in control.
---
src/rails.js | 2 +-
test/server.rb | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index 427e0c2f..62f257d5 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -41,7 +41,7 @@ jQuery(function ($) {
var el = this,
method = el.attr('method') || el.attr('data-method') || 'GET',
url = el.attr('action') || el.attr('href'),
- dataType = el.attr('data-type') || ($.ajaxSettings && $.ajaxSettings.dataType) || 'script';
+ dataType = el.attr('data-type') || ($.ajaxSettings && $.ajaxSettings.dataType);
if (url === undefined) {
throw "No URL specified for remote call (action or href must be present).";
diff --git a/test/server.rb b/test/server.rb
index fbc8b760..2816797e 100644
--- a/test/server.rb
+++ b/test/server.rb
@@ -9,8 +9,7 @@
FileUtils.cp(source_file, dest_file)
after do
- ctype = request.xhr? ? 'application/json' : 'text/html'
- content_type ctype, :charset => 'utf-8'
+ content_type 'text/html', :charset => 'utf-8'
end
get '/' do
From b6a3500bfb4b845d2c5e2f81b3c57a62fffd0845 Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Tue, 23 Nov 2010 20:55:05 -0500
Subject: [PATCH 008/364] If an ajax request is being sent then HTTP_ACCEPT
must have text/javascript.
Fixes #52
---
src/rails.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/rails.js b/src/rails.js
index 62f257d5..3db37270 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -54,6 +54,7 @@ jQuery(function ($) {
dataType: dataType,
type: method.toUpperCase(),
beforeSend: function (xhr) {
+ xhr.setRequestHeader("Accept", "text/javascript");
el.trigger('ajax:loading', xhr);
},
success: function (data, status, xhr) {
From 866a1031c4049896fdae724059595dba85b4cc40 Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Tue, 23 Nov 2010 21:14:22 -0500
Subject: [PATCH 009/364] fix module name in test file
---
test/public/test/call-remote-callbacks.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/public/test/call-remote-callbacks.js b/test/public/test/call-remote-callbacks.js
index aecd37dd..3265ecd0 100644
--- a/test/public/test/call-remote-callbacks.js
+++ b/test/public/test/call-remote-callbacks.js
@@ -18,7 +18,7 @@ App.build_form = function(opt) {
}));
};
-module('call-remote', {
+module('call-remote-callbacks', {
teardown: App.teardown,
From f0d2e561d4a80e6933c83ff7fea91f690e2e4c15 Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Fri, 26 Nov 2010 19:15:19 -0500
Subject: [PATCH 010/364] add comments to data-method
---
src/rails.js | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/rails.js b/src/rails.js
index 3db37270..e59a480e 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -102,6 +102,11 @@ jQuery(function ($) {
e.preventDefault();
});
+ /**
+ * <%= link_to "Delete", user_path(@user), :method => :delete, :confirm => "Are you sure?" %>
+ *
+ * Delete
+ */
$('a[data-method]:not([data-remote])').live('click.rails', function (e){
var link = $(this),
href = link.attr('href'),
From 3d4f74fc07d7c09dbbb2e37aa87300682b92ab00 Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Fri, 26 Nov 2010 20:17:18 -0500
Subject: [PATCH 011/364] fix the wrong module name inside test
---
test/public/test/data-method-iframe.js | 2 +-
test/public/test/data-method.js | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/public/test/data-method-iframe.js b/test/public/test/data-method-iframe.js
index 06dcd12d..5bedae97 100644
--- a/test/public/test/data-method-iframe.js
+++ b/test/public/test/data-method-iframe.js
@@ -1,4 +1,4 @@
-module('data-remote-iframe', {
+module('data-method-iframe', {
teardown: App.teardown,
diff --git a/test/public/test/data-method.js b/test/public/test/data-method.js
index e76a6f2a..9b41fa78 100644
--- a/test/public/test/data-method.js
+++ b/test/public/test/data-method.js
@@ -1,4 +1,4 @@
-module('data-remote');
+module('data-method');
test('clicking on a link with data-method attribute', function() {
expect(1);
From 1c2c0db532b2d2fd3c0c49e93818b77a948aefd1 Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Fri, 26 Nov 2010 23:08:28 -0500
Subject: [PATCH 012/364] remove the unused @app_base_url
---
test/views/index.erb | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/views/index.erb b/test/views/index.erb
index 0ad5ebba..dbbe662c 100644
--- a/test/views/index.erb
+++ b/test/views/index.erb
@@ -34,7 +34,7 @@
-
+
From 70c79807c2dfad23a312ed065fa82fc21e5f13a6 Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Fri, 26 Nov 2010 23:11:28 -0500
Subject: [PATCH 013/364] remove unused markup from test file
---
test/views/index.erb | 3 ---
1 file changed, 3 deletions(-)
diff --git a/test/views/index.erb b/test/views/index.erb
index dbbe662c..5b966dd4 100644
--- a/test/views/index.erb
+++ b/test/views/index.erb
@@ -35,9 +35,6 @@
From c284be39c752cebfc9c0b12425a371660af4686d Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Fri, 26 Nov 2010 23:12:30 -0500
Subject: [PATCH 014/364] remove unused markup from test file
---
test/views/index.erb | 3 ---
1 file changed, 3 deletions(-)
diff --git a/test/views/index.erb b/test/views/index.erb
index 5b966dd4..21d34bf9 100644
--- a/test/views/index.erb
+++ b/test/views/index.erb
@@ -40,9 +40,6 @@
From d4899b3bafad4b939d69e6da692779ea559a989a Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Fri, 26 Nov 2010 23:34:57 -0500
Subject: [PATCH 015/364] Add comment to add where the assertion is actually
taking place
---
test/public/test/data-method-iframe.js | 6 ++++++
test/public/test/data-method.js | 4 ++--
test/views/iframe.erb | 4 ++++
3 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/test/public/test/data-method-iframe.js b/test/public/test/data-method-iframe.js
index 5bedae97..7af34292 100644
--- a/test/public/test/data-method-iframe.js
+++ b/test/public/test/data-method-iframe.js
@@ -14,6 +14,12 @@ module('data-method-iframe', {
});
test('clicking on a link with data-method attribute', function() {
+
+ /*
+ * There is nothing to verify here. The trigger clicks the link
+ * which submits to /delete. The response given by /delete is asserted in
+ * data-method-iframe.js
+ */
expect(0);
stop();
diff --git a/test/public/test/data-method.js b/test/public/test/data-method.js
index 9b41fa78..a852f48b 100644
--- a/test/public/test/data-method.js
+++ b/test/public/test/data-method.js
@@ -13,7 +13,7 @@ test('clicking on a link with data-method attribute', function() {
start();
};
- //Nothing to do. Just wait for iframe to load and do its thing. And then verify
+ //index.erb loads iframe.erb . Just wait for iframe to load and do its thing and then verify.
if(iframe[0].loaded) {
iframeCallback();
} else {
@@ -36,7 +36,7 @@ test('clicking on a link with data-method attribute and csrf', function() {
start();
};
- //Nothing to do. Just wait for iframe to load and do its thing. And then verify
+ //index.erb load iframe-csrf.eb . Just wait for iframe to load and do its thing and then verify .
if(iframe[0].loaded) {
iframeCallback();
} else {
diff --git a/test/views/iframe.erb b/test/views/iframe.erb
index e3f06abb..3c5536da 100644
--- a/test/views/iframe.erb
+++ b/test/views/iframe.erb
@@ -11,6 +11,10 @@
+
From 085d910a5ec07b69f31beabce286141aa26f3005 Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Mon, 29 Nov 2010 11:37:19 -0500
Subject: [PATCH 016/364] Remove trailing spaces and check for right version
closes #51
---
src/rails.js | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index e59a480e..3e71bfda 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -5,7 +5,7 @@
*
* This rails.js file supports jQuery 1.4.3 and 1.4.4 .
*
- */
+ */
jQuery(function ($) {
var csrf_token = $('meta[name=csrf-token]').attr('content'),
@@ -35,7 +35,7 @@ jQuery(function ($) {
* - ajax:success - is executed when status is success
* - ajax:complete - is execute when status is complete
* - ajax:failure - is execute in case of error
- * - ajax:after - is execute every single time at the end of ajax call
+ * - ajax:after - is execute every single time at the end of ajax call
*/
callRemote: function () {
var el = this,
@@ -86,7 +86,7 @@ jQuery(function ($) {
}
}
});
-
+
/**
@@ -155,8 +155,9 @@ jQuery(function ($) {
var jqueryVersion = $().jquery;
- if ( (jqueryVersion === '1.4') || (jqueryVersion === '1.4.1') || (jqueryVersion === '1.4.2') ){
- alert('This rails.js does not support the jQuery version you are using. Please read documentation.');
- }
+ if (!( (jqueryVersion === '1.4.3') || (jqueryVersion === '1.4.4'))){
+ alert('This rails.js does not support the jQuery version you are using. Please read documentation.');
+ }
+
});
From eac77fcba67f53d808b5adfac70cdb8eac55e2be Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Tue, 23 Nov 2010 21:09:34 -0500
Subject: [PATCH 017/364] Be consistent with the names of callbacks
jQuery ajax call has callback named error.
jquery-ujs has callback ajax:failure.
Changing that to ajax:error
---
src/rails.js | 6 +++---
test/public/test/call-remote-callbacks.js | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index 3e71bfda..e9a42f27 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -34,8 +34,8 @@ jQuery(function ($) {
* - ajax:loading - is executed before firing ajax call
* - ajax:success - is executed when status is success
* - ajax:complete - is execute when status is complete
- * - ajax:failure - is execute in case of error
- * - ajax:after - is execute every single time at the end of ajax call
+ * - ajax:error - is execute in case of error
+ * - ajax:after - is execute every single time at the end of ajax call
*/
callRemote: function () {
var el = this,
@@ -64,7 +64,7 @@ jQuery(function ($) {
el.trigger('ajax:complete', xhr);
},
error: function (xhr, status, error) {
- el.trigger('ajax:failure', [xhr, status, error]);
+ el.trigger('ajax:error', [xhr, status, error]);
}
});
}
diff --git a/test/public/test/call-remote-callbacks.js b/test/public/test/call-remote-callbacks.js
index 3265ecd0..fcb51a08 100644
--- a/test/public/test/call-remote-callbacks.js
+++ b/test/public/test/call-remote-callbacks.js
@@ -66,8 +66,8 @@ test('before, loading, error, complete and after callbacks should be called in c
$('form')
.bind('ajax:before', function() { ok(true, 'ajax:before'); return true; })
.bind('ajax:loading', function(arg) { ok(true, 'ajax:loading'); })
- .bind('ajax:failure', function(e, xhr, status, error) {
- ok(true, 'ajax:failure');
+ .bind('ajax:error', function(e, xhr, status, error) {
+ ok(true, 'ajax:error');
equals(xhr.status, 403, 'status code should be 403');
})
.bind('ajax:complete', function(arg) { ok(true, 'ajax:complete'); start(); })
From eb8ad739df4916f1f2583ab1ca336cc9fdee97e8 Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Tue, 23 Nov 2010 21:17:08 -0500
Subject: [PATCH 018/364] bring clarity to ajax:complete callback documentation
---
src/rails.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/rails.js b/src/rails.js
index e9a42f27..e3420238 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -33,7 +33,7 @@ jQuery(function ($) {
* - ajax:before - is execute before the whole thing begings
* - ajax:loading - is executed before firing ajax call
* - ajax:success - is executed when status is success
- * - ajax:complete - is execute when status is complete
+ * - ajax:complete - is executed when the request finishes, whether in failure or success.
* - ajax:error - is execute in case of error
* - ajax:after - is execute every single time at the end of ajax call
*/
From 9446379704a65faa0c94b3803bc751740c43fdf1 Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Tue, 23 Nov 2010 21:21:00 -0500
Subject: [PATCH 019/364] Since ajax:complete callback is fired when ajax
request completes, whether the request ended in success or error.
I do not see a need for
another callback called ajax:after.
---
src/rails.js | 3 ---
test/public/test/call-remote-callbacks.js | 14 ++++++--------
2 files changed, 6 insertions(+), 11 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index e3420238..ec401dd7 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -35,7 +35,6 @@ jQuery(function ($) {
* - ajax:success - is executed when status is success
* - ajax:complete - is executed when the request finishes, whether in failure or success.
* - ajax:error - is execute in case of error
- * - ajax:after - is execute every single time at the end of ajax call
*/
callRemote: function () {
var el = this,
@@ -68,8 +67,6 @@ jQuery(function ($) {
}
});
}
-
- el.trigger('ajax:after');
}
}
});
diff --git a/test/public/test/call-remote-callbacks.js b/test/public/test/call-remote-callbacks.js
index fcb51a08..26e80a05 100644
--- a/test/public/test/call-remote-callbacks.js
+++ b/test/public/test/call-remote-callbacks.js
@@ -44,22 +44,21 @@ test('if ajax:before callback returns false then do not proceed', function() {
App.short_timeout();
});
-test('before, loading, success, complete and after callbacks should be called', function() {
- expect(5);
+test('before, loading, success and complete callbacks should be called', function() {
+ expect(4);
stop(App.ajax_timeout);
$('form')
.bind('ajax:before', function() { ok(true, 'ajax:before'); return true; })
.bind('ajax:loading', function(arg) { ok(true, 'ajax:loading'); })
.bind('ajax:success', function(arg) { ok(true, 'ajax:success'); })
- .bind('ajax:complete', function(arg) { ok(true, 'ajax:complete'); start(); })
- .bind('ajax:after', function() { ok(true, 'ajax:after'); });
+ .bind('ajax:complete', function(arg) { ok(true, 'ajax:complete'); start(); });
$('form[data-remote]').trigger('submit');
});
-test('before, loading, error, complete and after callbacks should be called in case of error', function() {
- expect(6);
+test('before, loading, error and complete callbacks should be called in case of error', function() {
+ expect(5);
$('form').attr('action', App.url('error'));
stop(App.ajax_timeout);
@@ -70,8 +69,7 @@ test('before, loading, error, complete and after callbacks should be called in c
ok(true, 'ajax:error');
equals(xhr.status, 403, 'status code should be 403');
})
- .bind('ajax:complete', function(arg) { ok(true, 'ajax:complete'); start(); })
- .bind('ajax:after', function() { ok(true, 'ajax:after'); });
+ .bind('ajax:complete', function(arg) { ok(true, 'ajax:complete'); start(); });
$('form[data-remote]').trigger('submit');
});
From ac94b45fad0fe4b2658e3a0112b89aa65b79e95c Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Tue, 23 Nov 2010 21:24:37 -0500
Subject: [PATCH 020/364] jQuery provides callback named beforeSend.
jquery-ujs fires ajax:loading for beforeSend.
A few times I have been confused if loading is
fired after the request has already been made or
after.
I guess the best solution is to be as close to
jQuery as possible. No need to invent new names.
This patch changes callback named ajax:loading to
ajax:beforeSend.
---
src/rails.js | 4 ++--
test/public/test/call-remote-callbacks.js | 10 +++++-----
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index ec401dd7..3eb9ac97 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -31,7 +31,7 @@ jQuery(function ($) {
* Handles execution of remote calls. Provides following callbacks:
*
* - ajax:before - is execute before the whole thing begings
- * - ajax:loading - is executed before firing ajax call
+ * - ajax:beforeSend - is executed before firing ajax call
* - ajax:success - is executed when status is success
* - ajax:complete - is executed when the request finishes, whether in failure or success.
* - ajax:error - is execute in case of error
@@ -54,7 +54,7 @@ jQuery(function ($) {
type: method.toUpperCase(),
beforeSend: function (xhr) {
xhr.setRequestHeader("Accept", "text/javascript");
- el.trigger('ajax:loading', xhr);
+ el.trigger('ajax:beforeSend', xhr);
},
success: function (data, status, xhr) {
el.trigger('ajax:success', [data, status, xhr]);
diff --git a/test/public/test/call-remote-callbacks.js b/test/public/test/call-remote-callbacks.js
index 26e80a05..619ed9c2 100644
--- a/test/public/test/call-remote-callbacks.js
+++ b/test/public/test/call-remote-callbacks.js
@@ -35,7 +35,7 @@ test('if ajax:before callback returns false then do not proceed', function() {
$('form')
.bind('ajax:before', function() { return false; })
- .bind('ajax:loading', function(){
+ .bind('ajax:beforeSend', function(){
ok(false, 'ajax call should not have been made since ajax:before callback returns false');
});
@@ -44,27 +44,27 @@ test('if ajax:before callback returns false then do not proceed', function() {
App.short_timeout();
});
-test('before, loading, success and complete callbacks should be called', function() {
+test('before, beforeSend, success and complete callbacks should be called', function() {
expect(4);
stop(App.ajax_timeout);
$('form')
.bind('ajax:before', function() { ok(true, 'ajax:before'); return true; })
- .bind('ajax:loading', function(arg) { ok(true, 'ajax:loading'); })
+ .bind('ajax:beforeSend', function(arg) { ok(true, 'ajax:beforeSend'); })
.bind('ajax:success', function(arg) { ok(true, 'ajax:success'); })
.bind('ajax:complete', function(arg) { ok(true, 'ajax:complete'); start(); });
$('form[data-remote]').trigger('submit');
});
-test('before, loading, error and complete callbacks should be called in case of error', function() {
+test('before, beforeSend, error and complete callbacks should be called in case of error', function() {
expect(5);
$('form').attr('action', App.url('error'));
stop(App.ajax_timeout);
$('form')
.bind('ajax:before', function() { ok(true, 'ajax:before'); return true; })
- .bind('ajax:loading', function(arg) { ok(true, 'ajax:loading'); })
+ .bind('ajax:beforeSend', function(arg) { ok(true, 'ajax:loading'); })
.bind('ajax:error', function(e, xhr, status, error) {
ok(true, 'ajax:error');
equals(xhr.status, 403, 'status code should be 403');
From 8b9863b285937c08e333e7ded3876732922fdda0 Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Sat, 27 Nov 2010 00:01:36 -0500
Subject: [PATCH 021/364] Callback ajax:before is called before any ajax call.
jQuery already provides beforeSend callback which jquery-ujs uses to provide
ajax:beforeSend callback.
Anything can be done in ajax:before can also be done
in ajax:beforeSend callback.
I have been attempting to get the callbacks in
jquery-ujs close to jQuery and this was the last
hurdle.
---
src/rails.js | 9 +++++----
test/public/test/call-remote-callbacks.js | 20 +++++++++-----------
2 files changed, 14 insertions(+), 15 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index 3eb9ac97..3f6bfadb 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -45,8 +45,8 @@ jQuery(function ($) {
if (url === undefined) {
throw "No URL specified for remote call (action or href must be present).";
} else {
- if (el.triggerAndReturn('ajax:before')) {
- var data = el.is('form') ? el.serializeArray() : [];
+ var $this = $(this), data = el.is('form') ? el.serializeArray() : [];
+
$.ajax({
url: url,
data: data,
@@ -54,7 +54,9 @@ jQuery(function ($) {
type: method.toUpperCase(),
beforeSend: function (xhr) {
xhr.setRequestHeader("Accept", "text/javascript");
- el.trigger('ajax:beforeSend', xhr);
+ if ($this.triggerHandler('ajax:beforeSend') === false) {
+ return false;
+ }
},
success: function (data, status, xhr) {
el.trigger('ajax:success', [data, status, xhr]);
@@ -66,7 +68,6 @@ jQuery(function ($) {
el.trigger('ajax:error', [xhr, status, error]);
}
});
- }
}
}
});
diff --git a/test/public/test/call-remote-callbacks.js b/test/public/test/call-remote-callbacks.js
index 619ed9c2..e7de5b6e 100644
--- a/test/public/test/call-remote-callbacks.js
+++ b/test/public/test/call-remote-callbacks.js
@@ -29,14 +29,14 @@ module('call-remote-callbacks', {
}
});
-test('if ajax:before callback returns false then do not proceed', function() {
+test('ajax:beforeSend returns false do not proceed', function() {
expect(0);
stop();
$('form')
- .bind('ajax:before', function() { return false; })
- .bind('ajax:beforeSend', function(){
- ok(false, 'ajax call should not have been made since ajax:before callback returns false');
+ .bind('ajax:beforeSend', function() { return false; })
+ .bind('ajax:complete', function(){
+ ok(false, 'ajax call should not have been made since ajax:beforeSend callback returns false');
});
$('form[data-remote]').trigger('submit');
@@ -44,12 +44,11 @@ test('if ajax:before callback returns false then do not proceed', function() {
App.short_timeout();
});
-test('before, beforeSend, success and complete callbacks should be called', function() {
- expect(4);
+test('beforeSend, success and complete callbacks should be called', function() {
+ expect(3);
stop(App.ajax_timeout);
$('form')
- .bind('ajax:before', function() { ok(true, 'ajax:before'); return true; })
.bind('ajax:beforeSend', function(arg) { ok(true, 'ajax:beforeSend'); })
.bind('ajax:success', function(arg) { ok(true, 'ajax:success'); })
.bind('ajax:complete', function(arg) { ok(true, 'ajax:complete'); start(); });
@@ -57,14 +56,13 @@ test('before, beforeSend, success and complete callbacks should be called', func
$('form[data-remote]').trigger('submit');
});
-test('before, beforeSend, error and complete callbacks should be called in case of error', function() {
- expect(5);
+test('beforeSend, error and complete callbacks should be called in case of error', function() {
+ expect(4);
$('form').attr('action', App.url('error'));
stop(App.ajax_timeout);
$('form')
- .bind('ajax:before', function() { ok(true, 'ajax:before'); return true; })
- .bind('ajax:beforeSend', function(arg) { ok(true, 'ajax:loading'); })
+ .bind('ajax:beforeSend', function(arg) { ok(true, 'ajax:beforeSend'); })
.bind('ajax:error', function(e, xhr, status, error) {
ok(true, 'ajax:error');
equals(xhr.status, 403, 'status code should be 403');
From 72d875a8d57c6bb466170980a5142c66ac74e8f0 Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Sat, 27 Nov 2010 15:42:46 -0500
Subject: [PATCH 022/364] remove ajax:before from the documentation
---
src/rails.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/rails.js b/src/rails.js
index 3f6bfadb..4ed98998 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -30,7 +30,6 @@ jQuery(function ($) {
/**
* Handles execution of remote calls. Provides following callbacks:
*
- * - ajax:before - is execute before the whole thing begings
* - ajax:beforeSend - is executed before firing ajax call
* - ajax:success - is executed when status is success
* - ajax:complete - is executed when the request finishes, whether in failure or success.
From 96bd1328856b0a266c2f4cb2c9c3f99fbf2690a2 Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Tue, 14 Dec 2010 22:59:13 -0500
Subject: [PATCH 023/364] too many issues regarding https. It is a solved
problem. Use the latest version.
---
README.rdoc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.rdoc b/README.rdoc
index 1ed16791..f4255b53 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -22,7 +22,7 @@ rails.js file from v1.4 branch can be accessed at https://github.com/rails/jquer
Add this line to your Gemfile:
- gem 'jquery-rails'
+ gem 'jquery-rails', '>= 0.2.6'
=== Step 2
From 5c5eac8b671c31720f4f29bb4a1a639c8d96ecb4 Mon Sep 17 00:00:00 2001
From: Justin Schier
Date: Thu, 9 Dec 2010 13:07:38 +0800
Subject: [PATCH 024/364] Final README clarifications.
---
README.rdoc | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/README.rdoc b/README.rdoc
index f4255b53..b173c8bb 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -45,10 +45,20 @@ Copy rails.js from http://github.com/rails/jquery-ujs/raw/master/src/rails.js in
=== Step 3 (optional)
-Switch the javascript_include_tag :defaults to use jquery instead of the default prototype helpers. Uncomment following line from file config/application.rb
+Uncomment following line from file config/application.rb
config.action_view.javascript_expansions[:defaults] = %w(jquery rails application)
+To load jQuery from a CDN such as Google, just specify the full path. Change the above to
+
+ config.action_view.javascript_expansions[:defaults] = %w(https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js rails application)
+
+Alternatively, you can specify the exact files to load in app/views/layouts/application.html.erb . Change javascript_include_tag :defaults to use jQuery
+ <%= javascript_include_tag 'jquery' %>
+ or
+ <%= javascript_include_tag 'https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js' %>
+instead of the default prototype helpers.
+
= Testing
== Installation
From 02d0a7076976c100883c10d35034100c704fbd13 Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Wed, 22 Dec 2010 14:56:47 -0500
Subject: [PATCH 025/364] remove TODO
---
src/rails.js | 3 ---
1 file changed, 3 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index 4ed98998..10b72e1a 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -16,9 +16,6 @@ jQuery(function ($) {
* Triggers a custom event on an element and returns the event result
* this is used to get around not being able to ensure callbacks are placed
* at the end of the chain.
- *
- * TODO: deprecate with jQuery 1.4.2 release, in favor of subscribing to our
- * own events and placing ourselves at the end of the chain.
*/
triggerAndReturn: function (name, data) {
var event = new $.Event(name);
From 565ed5e54eada1523b060a2b041fea40671f5c38 Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Wed, 22 Dec 2010 14:58:21 -0500
Subject: [PATCH 026/364] formatting
---
src/rails.js | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index 10b72e1a..3e6d21a8 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -27,10 +27,10 @@ jQuery(function ($) {
/**
* Handles execution of remote calls. Provides following callbacks:
*
- * - ajax:beforeSend - is executed before firing ajax call
- * - ajax:success - is executed when status is success
- * - ajax:complete - is executed when the request finishes, whether in failure or success.
- * - ajax:error - is execute in case of error
+ * - ajax:beforeSend - is executed before firing ajax call
+ * - ajax:success - is executed when status is success
+ * - ajax:complete - is executed when the request finishes, whether in failure or success
+ * - ajax:error - is execute in case of error
*/
callRemote: function () {
var el = this,
@@ -69,9 +69,8 @@ jQuery(function ($) {
});
/**
- * confirmation handler
+ * confirmation handler
*/
-
$('body').delegate('a[data-confirm], button[data-confirm], input[data-confirm]', 'click.rails', function () {
var el = $(this);
if (el.triggerAndReturn('confirm')) {
@@ -153,5 +152,4 @@ jQuery(function ($) {
alert('This rails.js does not support the jQuery version you are using. Please read documentation.');
}
-
});
From cff3fa97168cdac1f124395826bcdff967298f96 Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Wed, 22 Dec 2010 14:59:35 -0500
Subject: [PATCH 027/364] do not set accept header. leave the value as is set
by browser
---
src/rails.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/rails.js b/src/rails.js
index 3e6d21a8..6419fd44 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -49,7 +49,6 @@ jQuery(function ($) {
dataType: dataType,
type: method.toUpperCase(),
beforeSend: function (xhr) {
- xhr.setRequestHeader("Accept", "text/javascript");
if ($this.triggerHandler('ajax:beforeSend') === false) {
return false;
}
From 3fbeaf64f6189fa7449b438a79d7588f71e7c8b4 Mon Sep 17 00:00:00 2001
From: Santiago Pastorino
Date: Sun, 26 Dec 2010 08:32:40 -0200
Subject: [PATCH 028/364] Remove ^M DOS characters
---
src/rails.js | 308 +++++++++++++++++++++++++--------------------------
1 file changed, 154 insertions(+), 154 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index 6419fd44..668cffa7 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -1,154 +1,154 @@
-/*
- * jquery-ujs
- *
- * http://github.com/rails/jquery-ujs/blob/master/src/rails.js
- *
- * This rails.js file supports jQuery 1.4.3 and 1.4.4 .
- *
- */
-
-jQuery(function ($) {
- var csrf_token = $('meta[name=csrf-token]').attr('content'),
- csrf_param = $('meta[name=csrf-param]').attr('content');
-
- $.fn.extend({
- /**
- * Triggers a custom event on an element and returns the event result
- * this is used to get around not being able to ensure callbacks are placed
- * at the end of the chain.
- */
- triggerAndReturn: function (name, data) {
- var event = new $.Event(name);
- this.trigger(event, data);
-
- return event.result !== false;
- },
-
- /**
- * Handles execution of remote calls. Provides following callbacks:
- *
- * - ajax:beforeSend - is executed before firing ajax call
- * - ajax:success - is executed when status is success
- * - ajax:complete - is executed when the request finishes, whether in failure or success
- * - ajax:error - is execute in case of error
- */
- callRemote: function () {
- var el = this,
- method = el.attr('method') || el.attr('data-method') || 'GET',
- url = el.attr('action') || el.attr('href'),
- dataType = el.attr('data-type') || ($.ajaxSettings && $.ajaxSettings.dataType);
-
- if (url === undefined) {
- throw "No URL specified for remote call (action or href must be present).";
- } else {
- var $this = $(this), data = el.is('form') ? el.serializeArray() : [];
-
- $.ajax({
- url: url,
- data: data,
- dataType: dataType,
- type: method.toUpperCase(),
- beforeSend: function (xhr) {
- if ($this.triggerHandler('ajax:beforeSend') === false) {
- return false;
- }
- },
- success: function (data, status, xhr) {
- el.trigger('ajax:success', [data, status, xhr]);
- },
- complete: function (xhr) {
- el.trigger('ajax:complete', xhr);
- },
- error: function (xhr, status, error) {
- el.trigger('ajax:error', [xhr, status, error]);
- }
- });
- }
- }
- });
-
- /**
- * confirmation handler
- */
- $('body').delegate('a[data-confirm], button[data-confirm], input[data-confirm]', 'click.rails', function () {
- var el = $(this);
- if (el.triggerAndReturn('confirm')) {
- if (!confirm(el.attr('data-confirm'))) {
- return false;
- }
- }
- });
-
-
-
- /**
- * remote handlers
- */
- $('form[data-remote]').live('submit.rails', function (e) {
- $(this).callRemote();
- e.preventDefault();
- });
-
- $('a[data-remote],input[data-remote]').live('click.rails', function (e) {
- $(this).callRemote();
- e.preventDefault();
- });
-
- /**
- * <%= link_to "Delete", user_path(@user), :method => :delete, :confirm => "Are you sure?" %>
- *
- * Delete
- */
- $('a[data-method]:not([data-remote])').live('click.rails', function (e){
- var link = $(this),
- href = link.attr('href'),
- method = link.attr('data-method'),
- form = $(''),
- metadata_input = ' ';
-
- if (csrf_param !== undefined && csrf_token !== undefined) {
- metadata_input += ' ';
- }
-
- form.hide()
- .append(metadata_input)
- .appendTo('body');
-
- e.preventDefault();
- form.submit();
- });
-
- /**
- * disable-with handlers
- */
- var disable_with_input_selector = 'input[data-disable-with]',
- disable_with_form_remote_selector = 'form[data-remote]:has(' + disable_with_input_selector + ')',
- disable_with_form_not_remote_selector = 'form:not([data-remote]):has(' + disable_with_input_selector + ')';
-
- var disable_with_input_function = function () {
- $(this).find(disable_with_input_selector).each(function () {
- var input = $(this);
- input.data('enable-with', input.val())
- .attr('value', input.attr('data-disable-with'))
- .attr('disabled', 'disabled');
- });
- };
-
- $(disable_with_form_remote_selector).live('ajax:before.rails', disable_with_input_function);
- $(disable_with_form_not_remote_selector).live('submit.rails', disable_with_input_function);
-
- $(disable_with_form_remote_selector).live('ajax:complete.rails', function () {
- $(this).find(disable_with_input_selector).each(function () {
- var input = $(this);
- input.removeAttr('disabled')
- .val(input.data('enable-with'));
- });
- });
-
- var jqueryVersion = $().jquery;
-
- if (!( (jqueryVersion === '1.4.3') || (jqueryVersion === '1.4.4'))){
- alert('This rails.js does not support the jQuery version you are using. Please read documentation.');
- }
-
-});
+/*
+ * jquery-ujs
+ *
+ * http://github.com/rails/jquery-ujs/blob/master/src/rails.js
+ *
+ * This rails.js file supports jQuery 1.4.3 and 1.4.4 .
+ *
+ */
+
+jQuery(function ($) {
+ var csrf_token = $('meta[name=csrf-token]').attr('content'),
+ csrf_param = $('meta[name=csrf-param]').attr('content');
+
+ $.fn.extend({
+ /**
+ * Triggers a custom event on an element and returns the event result
+ * this is used to get around not being able to ensure callbacks are placed
+ * at the end of the chain.
+ */
+ triggerAndReturn: function (name, data) {
+ var event = new $.Event(name);
+ this.trigger(event, data);
+
+ return event.result !== false;
+ },
+
+ /**
+ * Handles execution of remote calls. Provides following callbacks:
+ *
+ * - ajax:beforeSend - is executed before firing ajax call
+ * - ajax:success - is executed when status is success
+ * - ajax:complete - is executed when the request finishes, whether in failure or success
+ * - ajax:error - is execute in case of error
+ */
+ callRemote: function () {
+ var el = this,
+ method = el.attr('method') || el.attr('data-method') || 'GET',
+ url = el.attr('action') || el.attr('href'),
+ dataType = el.attr('data-type') || ($.ajaxSettings && $.ajaxSettings.dataType);
+
+ if (url === undefined) {
+ throw "No URL specified for remote call (action or href must be present).";
+ } else {
+ var $this = $(this), data = el.is('form') ? el.serializeArray() : [];
+
+ $.ajax({
+ url: url,
+ data: data,
+ dataType: dataType,
+ type: method.toUpperCase(),
+ beforeSend: function (xhr) {
+ if ($this.triggerHandler('ajax:beforeSend') === false) {
+ return false;
+ }
+ },
+ success: function (data, status, xhr) {
+ el.trigger('ajax:success', [data, status, xhr]);
+ },
+ complete: function (xhr) {
+ el.trigger('ajax:complete', xhr);
+ },
+ error: function (xhr, status, error) {
+ el.trigger('ajax:error', [xhr, status, error]);
+ }
+ });
+ }
+ }
+ });
+
+ /**
+ * confirmation handler
+ */
+ $('body').delegate('a[data-confirm], button[data-confirm], input[data-confirm]', 'click.rails', function () {
+ var el = $(this);
+ if (el.triggerAndReturn('confirm')) {
+ if (!confirm(el.attr('data-confirm'))) {
+ return false;
+ }
+ }
+ });
+
+
+
+ /**
+ * remote handlers
+ */
+ $('form[data-remote]').live('submit.rails', function (e) {
+ $(this).callRemote();
+ e.preventDefault();
+ });
+
+ $('a[data-remote],input[data-remote]').live('click.rails', function (e) {
+ $(this).callRemote();
+ e.preventDefault();
+ });
+
+ /**
+ * <%= link_to "Delete", user_path(@user), :method => :delete, :confirm => "Are you sure?" %>
+ *
+ * Delete
+ */
+ $('a[data-method]:not([data-remote])').live('click.rails', function (e){
+ var link = $(this),
+ href = link.attr('href'),
+ method = link.attr('data-method'),
+ form = $(''),
+ metadata_input = ' ';
+
+ if (csrf_param !== undefined && csrf_token !== undefined) {
+ metadata_input += ' ';
+ }
+
+ form.hide()
+ .append(metadata_input)
+ .appendTo('body');
+
+ e.preventDefault();
+ form.submit();
+ });
+
+ /**
+ * disable-with handlers
+ */
+ var disable_with_input_selector = 'input[data-disable-with]',
+ disable_with_form_remote_selector = 'form[data-remote]:has(' + disable_with_input_selector + ')',
+ disable_with_form_not_remote_selector = 'form:not([data-remote]):has(' + disable_with_input_selector + ')';
+
+ var disable_with_input_function = function () {
+ $(this).find(disable_with_input_selector).each(function () {
+ var input = $(this);
+ input.data('enable-with', input.val())
+ .attr('value', input.attr('data-disable-with'))
+ .attr('disabled', 'disabled');
+ });
+ };
+
+ $(disable_with_form_remote_selector).live('ajax:before.rails', disable_with_input_function);
+ $(disable_with_form_not_remote_selector).live('submit.rails', disable_with_input_function);
+
+ $(disable_with_form_remote_selector).live('ajax:complete.rails', function () {
+ $(this).find(disable_with_input_selector).each(function () {
+ var input = $(this);
+ input.removeAttr('disabled')
+ .val(input.data('enable-with'));
+ });
+ });
+
+ var jqueryVersion = $().jquery;
+
+ if (!( (jqueryVersion === '1.4.3') || (jqueryVersion === '1.4.4'))){
+ alert('This rails.js does not support the jQuery version you are using. Please read documentation.');
+ }
+
+});
From fbbefa0773d791d3a67b7a3bb971c10ca750131b Mon Sep 17 00:00:00 2001
From: Neeraj Singh
Date: Tue, 28 Dec 2010 11:49:58 -0500
Subject: [PATCH 029/364] closes #52
The current code by default doest not set any HTTP ACCEPT header since I put in this commit
https://github.com/rails/jquery-ujs/commit/cff3fa97168cdac1f124395826bcdff967298f96
Now I am going to assume that you have following code
def destroy
@user = User.find(params[:id])
@user.destroy
respond_to do |format|
format.html { redirect_to(users_url) }
format.xml { head :ok }
format.js { }
end
end
Note that the index page has :remote => true
<%= link_to 'Destroy', user, :confirm => 'Are you sure?', :method => :delete, :remote => true %>
If you click on Destroy then browser will send */* and format.html will be executed. Your goal is to execute format.js.
Add following lines in your javascript right after jquery is loaded.
jQuery.ajaxSetup({ beforeSend: function (xhr) { xhr.setRequestHeader("Accept", "text/javascript"); } });
Now format.js will be executed.
---
src/rails.js | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/rails.js b/src/rails.js
index 668cffa7..1089647d 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -52,6 +52,11 @@ jQuery(function ($) {
if ($this.triggerHandler('ajax:beforeSend') === false) {
return false;
}
+ // if user has used jQuery.ajaxSetup then call beforeSend callback
+ var beforeSendGlobalCallback = $.ajaxSettings && $.ajaxSettings.beforeSend;
+ if (beforeSendGlobalCallback !== undefined) {
+ beforeSendGlobalCallback(xhr);
+ }
},
success: function (data, status, xhr) {
el.trigger('ajax:success', [data, status, xhr]);
From 1008c33b3775d6ceb20a1c5d19086eda972d66fa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Thu, 6 Jan 2011 19:58:50 +0100
Subject: [PATCH 030/364] rake tasks for testing
E.g. shotgun-powered reloadable server:
$ rake test:reloadable
If you don't want to use this, install the bundle without it:
$ bundle install --without reloadable
---
Gemfile | 7 ++++---
Gemfile.lock | 16 ++++++++++++++--
Rakefile | 47 +++++++++++++++++++++++++++++++++++++++++++++++
test/config.ru | 3 +++
4 files changed, 68 insertions(+), 5 deletions(-)
create mode 100644 Rakefile
create mode 100644 test/config.ru
diff --git a/Gemfile b/Gemfile
index d1793e4c..e32e523b 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,5 +1,6 @@
source 'http://rubygems.org'
-gem "json"
-
-gem "sinatra", "= 1.0"
+gem 'sinatra', '~> 1.0'
+gem 'shotgun', :group => :reloadable
+gem 'thin', :group => :reloadable
+gem 'json'
diff --git a/Gemfile.lock b/Gemfile.lock
index 4d10d04d..42460e58 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,14 +1,26 @@
GEM
remote: http://rubygems.org/
specs:
+ daemons (1.1.0)
+ eventmachine (0.12.10)
json (1.4.6)
rack (1.2.1)
- sinatra (1.0)
+ shotgun (0.8)
rack (>= 1.0)
+ sinatra (1.1.2)
+ rack (~> 1.1)
+ tilt (~> 1.2)
+ thin (1.2.7)
+ daemons (>= 1.0.9)
+ eventmachine (>= 0.12.6)
+ rack (>= 1.0.0)
+ tilt (1.2.1)
PLATFORMS
ruby
DEPENDENCIES
json
- sinatra (= 1.0)
+ shotgun
+ sinatra (~> 1.0)
+ thin
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 00000000..e0e44e67
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,47 @@
+desc %(Starts the test server and opens it in a web browser)
+multitask :default => ['test:server', 'test:open']
+
+PORT = 4567
+
+namespace :test do
+ desc %(Starts the test server)
+ task :server do
+ system 'bundle exec ruby test/server.rb'
+ end
+
+ desc %(Starts the test server which reloads everything on each refresh)
+ task :reloadable do
+ exec "bundle exec shotgun test/config.ru -p #{PORT} --server thin"
+ end
+
+ task :open do
+ url = "http://localhost:#{PORT}"
+ puts "Opening test app at #{url} ..."
+ sleep 3
+ system( *browse_cmd(url) )
+ end
+end
+
+# Returns an array e.g.: ['open', 'http://example.com']
+def browse_cmd(url)
+ require 'rbconfig'
+ browser = ENV['BROWSER'] ||
+ (RbConfig::CONFIG['host_os'].include?('darwin') && 'open') ||
+ (RbConfig::CONFIG['host_os'] =~ /msdos|mswin|djgpp|mingw|windows/ && 'start') ||
+ %w[xdg-open x-www-browser firefox opera mozilla netscape].find { |comm| which comm }
+
+ abort('ERROR: no web browser detected') unless browser
+ Array(browser) << url
+end
+
+# which('ruby') #=> /usr/bin/ruby
+def which cmd
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
+ exts.each { |ext|
+ exe = "#{path}/#{cmd}#{ext}"
+ return exe if File.executable? exe
+ }
+ end
+ return nil
+end
diff --git a/test/config.ru b/test/config.ru
new file mode 100644
index 00000000..44fb4f03
--- /dev/null
+++ b/test/config.ru
@@ -0,0 +1,3 @@
+$LOAD_PATH.unshift File.expand_path('..', __FILE__)
+require 'server'
+run Sinatra::Application
From 74aad2687be8cdd97d1fa5442998ab0edebce490 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Thu, 6 Jan 2011 20:01:00 +0100
Subject: [PATCH 031/364] refactor the test server & HTML layout
Changes:
- configure the server to return application/json for JSON responses
- remove jQuery version-specific routes
- remove the super-ugly hack of copying "src/rails.js" under the "test" directory
- fix test suite when running on port other than 4567
---
test/public/test/call-remote.js | 12 ++---
test/public/test/data-confirm.js | 4 +-
test/public/test/data-remote.js | 6 +--
test/public/test/settings.js | 12 +----
test/public/{ => vendor}/qunit.css | 0
test/server.rb | 85 ++++++++++++++++--------------
test/views/iframe.erb | 27 +++-------
test/views/iframe_csrf.erb | 16 ------
test/views/index.erb | 63 +++++++---------------
test/views/layout.erb | 23 ++++++++
10 files changed, 107 insertions(+), 141 deletions(-)
rename test/public/{ => vendor}/qunit.css (100%)
delete mode 100644 test/views/iframe_csrf.erb
create mode 100644 test/views/layout.erb
diff --git a/test/public/test/call-remote.js b/test/public/test/call-remote.js
index 3e707527..a5c9b058 100644
--- a/test/public/test/call-remote.js
+++ b/test/public/test/call-remote.js
@@ -35,7 +35,7 @@ test('method should be picked up from method attribute and not from data-method'
$('form[data-remote]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = $.parseJSON(data)['request_env'];
+ var request_env = data.request_env;
App.assert_post_request(request_env);
start();
@@ -56,7 +56,7 @@ test('method should be picked up from data-method attribute if method is missing
$('form[data-remote]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = $.parseJSON(data)['request_env'];
+ var request_env = data.request_env;
App.assert_post_request(request_env);
start();
@@ -76,7 +76,7 @@ test('default method GET should be picked up if no method or data-method is supp
$('form[data-remote]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = $.parseJSON(data)['request_env'];
+ var request_env = data.request_env;
App.assert_get_request(request_env);
start();
@@ -96,7 +96,7 @@ test('url should be picked up from action', function() {
$('form[data-remote]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = $.parseJSON(data)['request_env'];
+ var request_env = data.request_env;
App.assert_request_path(request_env, '/show');
start();
@@ -116,7 +116,7 @@ test('url should be picked up from action if both action and href are mentioned
$('form[data-remote]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = $.parseJSON(data)['request_env'];
+ var request_env = data.request_env;
App.assert_request_path(request_env, '/show');
start();
@@ -136,7 +136,7 @@ test('url should be picked up from href if no action is provided', function() {
$('form[data-remote]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = $.parseJSON(data)['request_env'];
+ var request_env = data.request_env;
App.assert_request_path(request_env, '/show');
start();
diff --git a/test/public/test/data-confirm.js b/test/public/test/data-confirm.js
index f6249ed5..21243aa5 100644
--- a/test/public/test/data-confirm.js
+++ b/test/public/test/data-confirm.js
@@ -46,7 +46,7 @@ test('clicking on a link with data-confirm attribute. Confirm yes.', function()
$('a[data-confirm]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = $.parseJSON(data)['request_env'];
+ var request_env = data.request_env;
App.assert_request_path(request_env, '/show');
App.assert_get_request(request_env);
@@ -98,7 +98,7 @@ test('clicking on Submit input tag with data-confirm attribute. Confirm yes.', f
$('input[data-confirm]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = $.parseJSON(data)['request_env'];
+ var request_env = data.request_env;
App.assert_request_path(request_env, '/show');
App.assert_get_request(request_env);
diff --git a/test/public/test/data-remote.js b/test/public/test/data-remote.js
index cc64c8fa..b51affd8 100644
--- a/test/public/test/data-remote.js
+++ b/test/public/test/data-remote.js
@@ -43,7 +43,7 @@ test('clicking on a link with data-remote attribute', function() {
$('a[data-remote]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = $.parseJSON(data)['request_env'];
+ var request_env = data.request_env;
App.assert_request_path(request_env, '/show');
App.assert_get_request(request_env);
@@ -60,7 +60,7 @@ test('clicking on Submit input tag with data-remote attribute', function() {
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = $.parseJSON(data)['request_env'];
+ var request_env = data.request_env;
App.assert_request_path(request_env, '/show');
App.assert_get_request(request_env);
@@ -78,7 +78,7 @@ test('Submitting form with data-remote attribute', function() {
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = $.parseJSON(data)['request_env'],
+ var request_env = data.request_env,
params = request_env['rack.request.query_hash'];
App.assert_request_path(request_env, '/update');
diff --git a/test/public/test/settings.js b/test/public/test/settings.js
index ffccadbd..006da678 100644
--- a/test/public/test/settings.js
+++ b/test/public/test/settings.js
@@ -1,19 +1,11 @@
var App = App || {};
-
-// ######## SETTINGS ###################################
App.host = 'http://localhost';
App.port = 4567;
-App.ajax_timeout = 5000;
-
-
-//
-// ######## helper methods ###########################
-//
-App.base_url = App.host + ':' + App.port;
+App.ajax_timeout = 1000;
App.url = function(url) {
- return App.base_url + '/' + url;
+ return App.host + ':' + App.port + '/' + url;
};
App.confirmation_message = 'Are you absolutely sure?';
diff --git a/test/public/qunit.css b/test/public/vendor/qunit.css
similarity index 100%
rename from test/public/qunit.css
rename to test/public/vendor/qunit.css
diff --git a/test/server.rb b/test/server.rb
index 2816797e..bab4d74d 100644
--- a/test/server.rb
+++ b/test/server.rb
@@ -1,66 +1,69 @@
-require 'rubygems'
require 'sinatra'
require 'json'
-require 'fileutils'
-# copy rails.js file from src to vendor directory
-source_file = File.join( File.dirname(__FILE__) , '..', 'src', 'rails.js')
-dest_file = File.join(File.dirname(__FILE__), 'public', 'vendor', 'rails.js')
-FileUtils.cp(source_file, dest_file)
-
-after do
- content_type 'text/html', :charset => 'utf-8'
-end
-
-get '/' do
- FileUtils.cp(source_file, dest_file)
- params[:version] ||= '1.4.4'
- erb :index
-end
-
-get '/jquery/:version' do
- FileUtils.cp(source_file, dest_file)
- erb :index
-end
+use Rack::Static, :urls => ["/src"], :root => File.expand_path('..', settings.root)
helpers do
def jquery_link version
if params[:version] == version
"[#{version}]"
else
- "#{version} "
+ "#{version} "
end
end
-end
-
-get '/show' do
- if request.xhr?
- {:hello => :sexy, :request_env => request.env}.to_json
- else
- '/show requested without ajax'
+
+ def jquery_src
+ "http://code.jquery.com/jquery-#{params[:version]}.js"
+ end
+
+ def test *names
+ names = ["/vendor/qunit.js", "settings"] + names
+ names.map { |name| script_tag name }.join("\n")
+ end
+
+ def script_tag src
+ src = "/test/#{src}.js" unless src.index('/')
+ %()
+ end
+
+ def ajax_json_or_error
+ if request.xhr?
+ content_type 'application/json'
+ data = yield
+ String === data ? data : data.to_json
+ else
+ content_type 'text/plain'
+ status 400
+ "#{request.path} requested without ajax"
+ end
end
end
-get '/error' do
- status 403
+get '/' do
+ params[:version] ||= '1.4.4'
+ erb :index
end
-post '/update' do
- if request.xhr?
- {:hello => :sexy, :request_env => request.env}.to_json
- else
- '/update requested without ajax'
- end
+get '/iframe' do
+ erb :iframe
end
-get '/iframe/:version' do
- erb :iframe
+get '/show' do
+ ajax_json_or_error do
+ { :hello => :sexy, :request_env => request.env }
+ end
end
-get '/iframe-csrf/:version' do
- erb :iframe_csrf
+post '/update' do
+ ajax_json_or_error do
+ { :hello => :sexy, :request_env => request.env }
+ end
end
delete '/delete' do
"/delete was invoked with delete verb. params is #{params.inspect}"
end
+
+get '/error' do
+ status 403
+end
diff --git a/test/views/iframe.erb b/test/views/iframe.erb
index 3c5536da..83b1d631 100644
--- a/test/views/iframe.erb
+++ b/test/views/iframe.erb
@@ -1,22 +1,9 @@
-
-
-
- jquery-ujs iframe
-
-
-
-
-
-
+<% @title = "jquery-ujs iframe" %>
-
+<%= test 'data-method-iframe' %>
-
-
-
-
-
-
+
+
diff --git a/test/views/iframe_csrf.erb b/test/views/iframe_csrf.erb
deleted file mode 100644
index 9c12b75d..00000000
--- a/test/views/iframe_csrf.erb
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
- jquery-ujs iframe
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/test/views/index.erb b/test/views/index.erb
index 21d34bf9..ae604930 100644
--- a/test/views/index.erb
+++ b/test/views/index.erb
@@ -1,50 +1,27 @@
-
-
-
- jquery-ujs test
-
-
-
-
-
+<% @title = "jquery-ujs test" %>
-
-
-
-
-
-
-
-
+<%= test 'data-confirm', 'data-remote', 'data-disable', 'call-remote', 'call-remote-callbacks', 'data-method' %>
-
-
-
-
-
-
+
+
+
-
+
-
-
-
-
+
-
-
-
-
+
+
+
+
-
+
+
+
+
-
-
-
+
diff --git a/test/views/layout.erb b/test/views/layout.erb
new file mode 100644
index 00000000..e727eb6e
--- /dev/null
+++ b/test/views/layout.erb
@@ -0,0 +1,23 @@
+
+
+
+ <%= @title %>
+
+
+
+ <% if params[:csrf] %>
+
+
+ <% end %>
+
+ <%= script_tag jquery_src %>
+ <%= script_tag "/src/rails.js" %>
+
+
+
+ <%= yield %>
+
+
From 9d77cb79d46fe5f31d3d09654f93a0def8696247 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Fri, 7 Jan 2011 12:14:53 +0100
Subject: [PATCH 032/364] whitespace: convert indentation to tabs
---
src/rails.js | 284 +++++++++++++++++++++++++--------------------------
1 file changed, 140 insertions(+), 144 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index 1089647d..cdb92e34 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -7,150 +7,146 @@
*
*/
-jQuery(function ($) {
- var csrf_token = $('meta[name=csrf-token]').attr('content'),
- csrf_param = $('meta[name=csrf-param]').attr('content');
-
- $.fn.extend({
- /**
- * Triggers a custom event on an element and returns the event result
- * this is used to get around not being able to ensure callbacks are placed
- * at the end of the chain.
- */
- triggerAndReturn: function (name, data) {
- var event = new $.Event(name);
- this.trigger(event, data);
-
- return event.result !== false;
- },
-
- /**
- * Handles execution of remote calls. Provides following callbacks:
- *
- * - ajax:beforeSend - is executed before firing ajax call
- * - ajax:success - is executed when status is success
- * - ajax:complete - is executed when the request finishes, whether in failure or success
- * - ajax:error - is execute in case of error
- */
- callRemote: function () {
- var el = this,
- method = el.attr('method') || el.attr('data-method') || 'GET',
- url = el.attr('action') || el.attr('href'),
- dataType = el.attr('data-type') || ($.ajaxSettings && $.ajaxSettings.dataType);
-
- if (url === undefined) {
- throw "No URL specified for remote call (action or href must be present).";
- } else {
- var $this = $(this), data = el.is('form') ? el.serializeArray() : [];
-
- $.ajax({
- url: url,
- data: data,
- dataType: dataType,
- type: method.toUpperCase(),
- beforeSend: function (xhr) {
- if ($this.triggerHandler('ajax:beforeSend') === false) {
- return false;
- }
- // if user has used jQuery.ajaxSetup then call beforeSend callback
- var beforeSendGlobalCallback = $.ajaxSettings && $.ajaxSettings.beforeSend;
- if (beforeSendGlobalCallback !== undefined) {
- beforeSendGlobalCallback(xhr);
- }
- },
- success: function (data, status, xhr) {
- el.trigger('ajax:success', [data, status, xhr]);
- },
- complete: function (xhr) {
- el.trigger('ajax:complete', xhr);
- },
- error: function (xhr, status, error) {
- el.trigger('ajax:error', [xhr, status, error]);
- }
- });
- }
- }
- });
-
- /**
- * confirmation handler
- */
- $('body').delegate('a[data-confirm], button[data-confirm], input[data-confirm]', 'click.rails', function () {
- var el = $(this);
- if (el.triggerAndReturn('confirm')) {
- if (!confirm(el.attr('data-confirm'))) {
- return false;
- }
- }
- });
-
-
-
- /**
- * remote handlers
- */
- $('form[data-remote]').live('submit.rails', function (e) {
- $(this).callRemote();
- e.preventDefault();
- });
-
- $('a[data-remote],input[data-remote]').live('click.rails', function (e) {
- $(this).callRemote();
- e.preventDefault();
- });
-
- /**
- * <%= link_to "Delete", user_path(@user), :method => :delete, :confirm => "Are you sure?" %>
- *
- * Delete
- */
- $('a[data-method]:not([data-remote])').live('click.rails', function (e){
- var link = $(this),
- href = link.attr('href'),
- method = link.attr('data-method'),
- form = $(''),
- metadata_input = ' ';
-
- if (csrf_param !== undefined && csrf_token !== undefined) {
- metadata_input += ' ';
- }
-
- form.hide()
- .append(metadata_input)
- .appendTo('body');
-
- e.preventDefault();
- form.submit();
- });
-
- /**
- * disable-with handlers
- */
- var disable_with_input_selector = 'input[data-disable-with]',
- disable_with_form_remote_selector = 'form[data-remote]:has(' + disable_with_input_selector + ')',
- disable_with_form_not_remote_selector = 'form:not([data-remote]):has(' + disable_with_input_selector + ')';
-
- var disable_with_input_function = function () {
- $(this).find(disable_with_input_selector).each(function () {
- var input = $(this);
- input.data('enable-with', input.val())
- .attr('value', input.attr('data-disable-with'))
- .attr('disabled', 'disabled');
- });
- };
-
- $(disable_with_form_remote_selector).live('ajax:before.rails', disable_with_input_function);
- $(disable_with_form_not_remote_selector).live('submit.rails', disable_with_input_function);
-
- $(disable_with_form_remote_selector).live('ajax:complete.rails', function () {
- $(this).find(disable_with_input_selector).each(function () {
- var input = $(this);
- input.removeAttr('disabled')
- .val(input.data('enable-with'));
- });
- });
-
- var jqueryVersion = $().jquery;
+jQuery(function($) {
+ var csrf_token = $('meta[name=csrf-token]').attr('content'),
+ csrf_param = $('meta[name=csrf-param]').attr('content');
+
+ $.fn.extend({
+ /**
+ * Triggers a custom event on an element and returns the event result
+ * this is used to get around not being able to ensure callbacks are placed
+ * at the end of the chain.
+ */
+ triggerAndReturn: function(name, data) {
+ var event = new $.Event(name);
+ this.trigger(event, data);
+
+ return event.result !== false;
+ },
+
+ /**
+ * Handles execution of remote calls. Provides following callbacks:
+ *
+ * - ajax:beforeSend - is executed before firing ajax call
+ * - ajax:success - is executed when status is success
+ * - ajax:complete - is executed when the request finishes, whether in failure or success
+ * - ajax:error - is execute in case of error
+ */
+ callRemote: function() {
+ var el = this,
+ method = el.attr('method') || el.attr('data-method') || 'GET',
+ url = el.attr('action') || el.attr('href'),
+ dataType = el.attr('data-type') || ($.ajaxSettings && $.ajaxSettings.dataType);
+
+ if (url === undefined) {
+ throw "No URL specified for remote call (action or href must be present).";
+ } else {
+ var $this = $(this), data = el.is('form') ? el.serializeArray() : [];
+
+ $.ajax({
+ url: url,
+ data: data,
+ dataType: dataType,
+ type: method.toUpperCase(),
+ beforeSend: function(xhr) {
+ if ($this.triggerHandler('ajax:beforeSend') === false) {
+ return false;
+ }
+
+ // if user has used jQuery.ajaxSetup then call beforeSend callback
+ var beforeSendGlobalCallback = $.ajaxSettings && $.ajaxSettings.beforeSend;
+ if (beforeSendGlobalCallback !== undefined) {
+ beforeSendGlobalCallback(xhr);
+ }
+ },
+ success: function(data, status, xhr) {
+ el.trigger('ajax:success', [data, status, xhr]);
+ },
+ complete: function(xhr) {
+ el.trigger('ajax:complete', xhr);
+ },
+ error: function(xhr, status, error) {
+ el.trigger('ajax:error', [xhr, status, error]);
+ }
+ });
+ }
+ }
+ });
+
+ /**
+ * confirmation handler
+ */
+ $('body').delegate('a[data-confirm], button[data-confirm], input[data-confirm]', 'click.rails', function() {
+ var el = $(this);
+ if (el.triggerAndReturn('confirm')) {
+ if (!confirm(el.attr('data-confirm'))) {
+ return false;
+ }
+ }
+ });
+
+ /**
+ * remote handlers
+ */
+ $('form[data-remote]').live('submit.rails', function(e) {
+ $(this).callRemote();
+ e.preventDefault();
+ });
+
+ $('a[data-remote],input[data-remote]').live('click.rails', function(e) {
+ $(this).callRemote();
+ e.preventDefault();
+ });
+
+ /**
+ * <%= link_to "Delete", user_path(@user), :method => :delete, :confirm => "Are you sure?" %>
+ *
+ * Delete
+ */
+ $('a[data-method]:not([data-remote])').live('click.rails', function(e) {
+ var link = $(this),
+ href = link.attr('href'),
+ method = link.attr('data-method'),
+ form = $(''),
+ metadata_input = ' ';
+
+ if (csrf_param !== undefined && csrf_token !== undefined) {
+ metadata_input += ' ';
+ }
+
+ form.hide().append(metadata_input).appendTo('body');
+
+ e.preventDefault();
+ form.submit();
+ });
+
+ /**
+ * disable-with handlers
+ */
+ var disable_with_input_selector = 'input[data-disable-with]',
+ disable_with_form_remote_selector = 'form[data-remote]:has(' + disable_with_input_selector + ')',
+ disable_with_form_not_remote_selector = 'form:not([data-remote]):has(' + disable_with_input_selector + ')';
+
+ var disable_with_input_function = function() {
+ $(this).find(disable_with_input_selector).each(function() {
+ var input = $(this);
+ input.data('enable-with', input.val())
+ .attr('value', input.attr('data-disable-with'))
+ .attr('disabled', 'disabled');
+ });
+ };
+
+ $(disable_with_form_remote_selector).live('ajax:before.rails', disable_with_input_function);
+ $(disable_with_form_not_remote_selector).live('submit.rails', disable_with_input_function);
+
+ $(disable_with_form_remote_selector).live('ajax:complete.rails', function() {
+ $(this).find(disable_with_input_selector).each(function() {
+ var input = $(this);
+ input.removeAttr('disabled').val(input.data('enable-with'));
+ });
+ });
+
+ var jqueryVersion = $().jquery;
if (!( (jqueryVersion === '1.4.3') || (jqueryVersion === '1.4.4'))){
alert('This rails.js does not support the jQuery version you are using. Please read documentation.');
From b4cd9c351cf7a873514669d4730d0a49f84c8218 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Fri, 7 Jan 2011 12:27:59 +0100
Subject: [PATCH 033/364] remove jQuery version check
It's silly. Users should read the docs.
Also, the version check would fail for every new version of jQuery.
---
src/rails.js | 7 -------
1 file changed, 7 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index cdb92e34..9f43d92f 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -145,11 +145,4 @@ jQuery(function($) {
input.removeAttr('disabled').val(input.data('enable-with'));
});
});
-
- var jqueryVersion = $().jquery;
-
- if (!( (jqueryVersion === '1.4.3') || (jqueryVersion === '1.4.4'))){
- alert('This rails.js does not support the jQuery version you are using. Please read documentation.');
- }
-
});
From b95f1ed91033df3fd38ca558732004e526e8838c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Fri, 7 Jan 2011 12:46:12 +0100
Subject: [PATCH 034/364] ability to test against an edge version of jQuery
simply put jQuery in "test/public/test/jquery.js"
and select "edge" in the header
---
.gitignore | 2 +-
test/server.rb | 4 +++-
test/views/index.erb | 1 +
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/.gitignore b/.gitignore
index 50880aa7..3330f6be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-test/public/vendor/rails.js
+test/public/vendor/jquery.js
*.swp
*.swo
.#*
diff --git a/test/server.rb b/test/server.rb
index bab4d74d..ea821e0f 100644
--- a/test/server.rb
+++ b/test/server.rb
@@ -13,7 +13,9 @@ def jquery_link version
end
def jquery_src
- "http://code.jquery.com/jquery-#{params[:version]}.js"
+ if params[:version] == 'edge' then "/vendor/jquery.js"
+ else "http://code.jquery.com/jquery-#{params[:version]}.js"
+ end
end
def test *names
diff --git a/test/views/index.erb b/test/views/index.erb
index ae604930..90497bde 100644
--- a/test/views/index.erb
+++ b/test/views/index.erb
@@ -6,6 +6,7 @@
jquery-ujs test
<%= jquery_link '1.4.3' %>
<%= jquery_link '1.4.4' %>
+ <%= jquery_link 'edge' if File.exist?(settings.root + '/public/vendor/jquery.js') %>
From 429866a6acd64d086dbec9ec3fcb49d96f4ad885 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Fri, 7 Jan 2011 13:10:36 +0100
Subject: [PATCH 035/364] prettier jquery version links
---
test/views/index.erb | 6 ++++--
test/views/layout.erb | 1 +
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/test/views/index.erb b/test/views/index.erb
index 90497bde..5f9d03bf 100644
--- a/test/views/index.erb
+++ b/test/views/index.erb
@@ -4,9 +4,11 @@
diff --git a/test/views/layout.erb b/test/views/layout.erb
index e727eb6e..4801e7c8 100644
--- a/test/views/layout.erb
+++ b/test/views/layout.erb
@@ -6,6 +6,7 @@
<% if params[:csrf] %>
From adbd972dbc20085190d6718edf99d5280f6bdfeb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Fri, 7 Jan 2011 13:11:00 +0100
Subject: [PATCH 036/364] remove unused fixture settings
---
test/views/index.erb | 2 --
1 file changed, 2 deletions(-)
diff --git a/test/views/index.erb b/test/views/index.erb
index 5f9d03bf..a7d08239 100644
--- a/test/views/index.erb
+++ b/test/views/index.erb
@@ -26,5 +26,3 @@
-
-
From 51272c68714748420d8c35984b3e2532ab14f748 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Fri, 7 Jan 2011 14:19:00 +0100
Subject: [PATCH 037/364] rails.js doesn't have to wait until DOM ready
---
src/rails.js | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index 9f43d92f..9674415c 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -7,10 +7,7 @@
*
*/
-jQuery(function($) {
- var csrf_token = $('meta[name=csrf-token]').attr('content'),
- csrf_param = $('meta[name=csrf-param]').attr('content');
-
+(function($) {
$.fn.extend({
/**
* Triggers a custom event on an element and returns the event result
@@ -76,7 +73,7 @@ jQuery(function($) {
/**
* confirmation handler
*/
- $('body').delegate('a[data-confirm], button[data-confirm], input[data-confirm]', 'click.rails', function() {
+ $('a[data-confirm], button[data-confirm], input[data-confirm]').live('click.rails', function() {
var el = $(this);
if (el.triggerAndReturn('confirm')) {
if (!confirm(el.attr('data-confirm'))) {
@@ -107,6 +104,8 @@ jQuery(function($) {
var link = $(this),
href = link.attr('href'),
method = link.attr('data-method'),
+ csrf_token = $('meta[name=csrf-token]').attr('content'),
+ csrf_param = $('meta[name=csrf-param]').attr('content'),
form = $(''),
metadata_input = ' ';
@@ -145,4 +144,4 @@ jQuery(function($) {
input.removeAttr('disabled').val(input.data('enable-with'));
});
});
-});
+})( jQuery );
From 498b35e24cdb14f2d94486e8a1f4a1f661091426 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Fri, 7 Jan 2011 18:10:31 +0100
Subject: [PATCH 038/364] small test overhaul: "/echo" resource, remote forms
Changes:
- replace "/show", "/update" actions with "/echo"
- changed some expectations about "remote" form method lookup
- stop using unnecessary `App.url()`
---
test/public/test/call-remote-callbacks.js | 101 +++++++---------
test/public/test/call-remote.js | 135 +++++----------------
test/public/test/data-confirm.js | 138 +++++++++++-----------
test/public/test/data-disable.js | 98 +++++++--------
test/public/test/data-method-iframe.js | 2 +-
test/public/test/data-method.js | 26 ++--
test/public/test/data-remote.js | 66 +++++------
test/public/test/settings.js | 11 --
test/server.rb | 20 ++--
9 files changed, 246 insertions(+), 351 deletions(-)
diff --git a/test/public/test/call-remote-callbacks.js b/test/public/test/call-remote-callbacks.js
index e7de5b6e..b0935928 100644
--- a/test/public/test/call-remote-callbacks.js
+++ b/test/public/test/call-remote-callbacks.js
@@ -1,75 +1,56 @@
-var App = App || {};
-
-App.build_form = function(opt) {
- var defaults = {
- 'data-remote': 'true'
- };
-
- var options = $.extend(defaults, opt);
-
- $('#fixtures').append($('', options));
-
- $('form').append($(' ', {
- id: 'user_name',
- type: 'text',
- size: '30',
- 'name': 'user_name',
- 'value': 'john'
- }));
-};
+(function(){
module('call-remote-callbacks', {
-
- teardown: App.teardown,
-
- setup: function() {
- App.build_form({
- 'action': App.url('show')
- });
- }
+ setup: function() {
+ $('#fixtures').append($('', {
+ action: '/echo', method: 'get', 'data-remote': 'true'
+ }));
+ },
+ teardown: App.teardown
});
-test('ajax:beforeSend returns false do not proceed', function() {
- expect(0);
- stop();
-
- $('form')
- .bind('ajax:beforeSend', function() { return false; })
+function submit(fn) {
+ stop(App.ajax_timeout);
+
+ var form = $('form')
.bind('ajax:complete', function(){
- ok(false, 'ajax call should not have been made since ajax:beforeSend callback returns false');
+ ok(true, 'ajax:complete');
+ start();
});
+
+ if (fn) fn(form);
+ form.trigger('submit');
+}
- $('form[data-remote]').trigger('submit');
-
- App.short_timeout();
+test('stopping the "ajax:beforeSend" event aborts the request', function() {
+ expect(0);
+ submit(function(form) {
+ form.bind('ajax:beforeSend', function() { return false });
+ form.unbind('ajax:complete').bind('ajax:complete', function() {
+ ok(false, 'ajax:complete should not run');
+ });
+ });
+ setTimeout(function(){ start() }, 200);
});
-test('beforeSend, success and complete callbacks should be called', function() {
- expect(3);
- stop(App.ajax_timeout);
-
- $('form')
- .bind('ajax:beforeSend', function(arg) { ok(true, 'ajax:beforeSend'); })
- .bind('ajax:success', function(arg) { ok(true, 'ajax:success'); })
- .bind('ajax:complete', function(arg) { ok(true, 'ajax:complete'); start(); });
-
- $('form[data-remote]').trigger('submit');
+test('"ajax:beforeSend", "ajax:success" and "ajax:complete" are triggered', function() {
+ expect(3);
+ submit(function(form) {
+ form.bind('ajax:beforeSend', function(arg) { ok(true, 'ajax:beforeSend') });
+ form.bind('ajax:success', function(arg) { ok(true, 'ajax:success') });
+ });
});
-test('beforeSend, error and complete callbacks should be called in case of error', function() {
- expect(4);
- $('form').attr('action', App.url('error'));
- stop(App.ajax_timeout);
-
- $('form')
- .bind('ajax:beforeSend', function(arg) { ok(true, 'ajax:beforeSend'); })
- .bind('ajax:error', function(e, xhr, status, error) {
+test('"ajax:beforeSend", "ajax:error" and "ajax:complete" are triggered on error', function() {
+ expect(4);
+ submit(function(form) {
+ form.attr('action', '/error');
+ form.bind('ajax:beforeSend', function(arg) { ok(true, 'ajax:beforeSend') });
+ form.bind('ajax:error', function(e, xhr, status, error) {
ok(true, 'ajax:error');
equals(xhr.status, 403, 'status code should be 403');
- })
- .bind('ajax:complete', function(arg) { ok(true, 'ajax:complete'); start(); });
-
- $('form[data-remote]').trigger('submit');
+ });
+ });
});
-
+})();
diff --git a/test/public/test/call-remote.js b/test/public/test/call-remote.js
index a5c9b058..9b69d51b 100644
--- a/test/public/test/call-remote.js
+++ b/test/public/test/call-remote.js
@@ -1,181 +1,110 @@
-var App = App || {};
+(function(){
-App.build_form = function(opt) {
+function build_form(attrs) {
+ attrs = $.extend({ action: '/echo', 'data-remote': 'true' }, attrs);
- var defaults = {
- 'data-remote': 'true'
- };
+ $('#fixtures').append($('', attrs));
- var options = $.extend(defaults, opt);
-
- $('#fixtures').append($('', options));
-
- $('#fixtures form').append($(' ', {
- id: 'user_name',
- type: 'text',
- size: '30',
- 'name': 'user_name',
- 'value': 'john'
- }));
+ $('#fixtures form').append($(' ', {
+ id: 'user_name',
+ type: 'text',
+ size: '30',
+ 'name': 'user_name',
+ 'value': 'john'
+ }));
};
module('call-remote', {
teardown: App.teardown
});
-test('method should be picked up from method attribute and not from data-method', function() {
+test('form method is read from "method" and not from "data-method"', function() {
expect(2);
- App.build_form({
- 'method': 'post',
- 'data-method': 'get',
- 'action': App.url('update')
- });
+ build_form({ method: 'post', 'data-method': 'get' });
stop(App.ajax_timeout);
$('form[data-remote]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = data.request_env;
- App.assert_post_request(request_env);
-
- start();
- })
- .trigger('submit');
-});
-
-test('method should be picked up from data-method attribute if method is missing', function() {
- expect(2);
-
- App.build_form({
- 'data-method': 'post',
- 'action': App.url('update')
- });
-
- stop(App.ajax_timeout);
-
- $('form[data-remote]')
- .live('ajax:success', function(e, data, status, xhr) {
- App.assert_callback_invoked('ajax:success');
- var request_env = data.request_env;
- App.assert_post_request(request_env);
-
+ App.assert_post_request(data.request_env);
start();
})
.trigger('submit');
});
-test('default method GET should be picked up if no method or data-method is supplied', function() {
+test('form method is not read from "data-method" attribute in case of missing "method"', function() {
expect(2);
-
- App.build_form({
- action: App.url('show')
- });
-
+ build_form({ 'data-method': 'put' });
stop(App.ajax_timeout);
$('form[data-remote]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = data.request_env;
- App.assert_get_request(request_env);
+ App.assert_post_request(data.request_env);
start();
})
.trigger('submit');
});
-test('url should be picked up from action', function() {
+test('form default method is POST', function() {
expect(2);
-
- App.build_form({
- 'action': App.url('show')
- });
-
+ build_form();
stop(App.ajax_timeout);
$('form[data-remote]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = data.request_env;
- App.assert_request_path(request_env, '/show');
-
+ App.assert_post_request(data.request_env);
start();
})
.trigger('submit');
});
-test('url should be picked up from action if both action and href are mentioned ', function() {
+test('form url is picked up from "action"', function() {
expect(2);
-
- App.build_form({
- 'action': App.url('show'),
- 'href': 'http://example.org'
- });
+ build_form();
stop(App.ajax_timeout);
$('form[data-remote]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = data.request_env;
- App.assert_request_path(request_env, '/show');
-
+ App.assert_request_path(data.request_env, '/echo');
start();
})
.trigger('submit');
});
-test('url should be picked up from href if no action is provided', function() {
+test('form url is read from "action" not "href"', function() {
expect(2);
-
- App.build_form({
- 'href': App.url('show')
- });
+ build_form({ href: '/echo2' });
stop(App.ajax_timeout);
-
$('form[data-remote]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = data.request_env;
- App.assert_request_path(request_env, '/show');
-
+ App.assert_request_path(data.request_env, '/echo');
start();
})
.trigger('submit');
});
-test('exception should be thrown if both action and url are missing', function() {
- expect(1);
- var exception_was_raised = false;
-
- App.build_form({});
-
- try {
- $('form[data-remote]').trigger('submit');
- } catch(err) {
- exception_was_raised = true;
- }
-
- ok(exception_was_raised, 'exception should have been raised');
-});
-
-test('data should be availabe in JSON format if datat-type is json', function() {
+test('data should be availabe in JSON format if data-type is json', function() {
expect(2);
- App.build_form({
+ build_form({
'method': 'post',
'data-method': 'get',
- 'data-type': 'json',
- 'action': App.url('update')
+ 'data-type': 'json'
});
stop(App.ajax_timeout);
$('form[data-remote]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = data['request_env'];
- App.assert_post_request(request_env);
-
+ App.assert_post_request(data.request_env);
start();
})
.trigger('submit');
});
+
+})();
diff --git a/test/public/test/data-confirm.js b/test/public/test/data-confirm.js
index 21243aa5..a1016933 100644
--- a/test/public/test/data-confirm.js
+++ b/test/public/test/data-confirm.js
@@ -2,52 +2,52 @@ module('data-confirm', {
teardown: App.teardown,
- setup: function() {
-
- $('#fixtures').append($(' ', {
- href: App.url('show'),
- 'data-remote': 'true',
- 'data-confirm': 'Are you absolutely sure?',
- text: 'my social security number'
- }));
-
- $('#fixtures').append($(' ', {
- 'data-confirm': App.confirmation_message,
- 'data-remote': 'true',
- href: App.url('show'),
- name: 'submit',
- type: 'submit',
- value: 'Click me'
- }));
-
-
- $('#fixtures').append($(' ', {
- 'data-confirm': App.confirmation_message,
- 'data-remote': 'true',
- href: App.url('show'),
- name: 'submit',
- type: 'button',
- value: 'Click me'
- }));
-
- }
+ setup: function() {
+
+ $('#fixtures').append($(' ', {
+ href: '/echo',
+ 'data-remote': 'true',
+ 'data-confirm': 'Are you absolutely sure?',
+ text: 'my social security number'
+ }));
+
+ $('#fixtures').append($(' ', {
+ 'data-confirm': App.confirmation_message,
+ 'data-remote': 'true',
+ href: '/echo',
+ name: 'submit',
+ type: 'submit',
+ value: 'Click me'
+ }));
+
+
+ $('#fixtures').append($(' ', {
+ 'data-confirm': App.confirmation_message,
+ 'data-remote': 'true',
+ href: '/echo',
+ name: 'submit',
+ type: 'button',
+ value: 'Click me'
+ }));
+
+ }
});
test('clicking on a link with data-confirm attribute. Confirm yes.', function() {
- expect(4);
+ expect(4);
- window.confirm = function(msg) {
- $(document.body).data('confirmation-message', msg);
- return true;
- };
+ window.confirm = function(msg) {
+ $(document.body).data('confirmation-message', msg);
+ return true;
+ };
stop(App.ajax_timeout);
- $('a[data-confirm]')
+ $('a[data-confirm]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
var request_env = data.request_env;
- App.assert_request_path(request_env, '/show');
+ App.assert_request_path(request_env, '/echo');
App.assert_get_request(request_env);
equals( $(document.body).data('confirmation-message'),
@@ -62,13 +62,13 @@ test('clicking on a link with data-confirm attribute. Confirm yes.', function()
test('clicking on a link with data-confirm attribute. Confirm No.', function() {
expect(1);
- window.confirm = function(msg) {
- $(document.body).data('confirmation-message', msg);
- return false;
- };
+ window.confirm = function(msg) {
+ $(document.body).data('confirmation-message', msg);
+ return false;
+ };
stop();
- $('a[data-confirm]')
+ $('a[data-confirm]')
.live('ajax:before', function(e, data, status, xhr) {
App.assert_callback_not_invoked('ajax:before');
})
@@ -77,7 +77,7 @@ test('clicking on a link with data-confirm attribute. Confirm No.', function() {
// I don't have idea how to do it without timeout on "confirm: no", will need
// to think about that
setTimeout(function() {
- equals( $(document.body).data('confirmation-message'),
+ equals( $(document.body).data('confirmation-message'),
App.confirmation_message,
'confirmation message should be same');
@@ -86,20 +86,20 @@ test('clicking on a link with data-confirm attribute. Confirm No.', function() {
});
test('clicking on Submit input tag with data-confirm attribute. Confirm yes.', function() {
- expect(4);
+ expect(4);
- window.confirm = function(msg) {
- $(document.body).data('confirmation-message', msg);
- return true;
- };
+ window.confirm = function(msg) {
+ $(document.body).data('confirmation-message', msg);
+ return true;
+ };
stop(App.ajax_timeout);
- $('input[data-confirm]')
+ $('input[data-confirm]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
var request_env = data.request_env;
- App.assert_request_path(request_env, '/show');
+ App.assert_request_path(request_env, '/echo');
App.assert_get_request(request_env);
equals( $(document.body).data('confirmation-message'),
@@ -112,22 +112,22 @@ test('clicking on Submit input tag with data-confirm attribute. Confirm yes.', f
});
test('clicking on Submit input tag with data-confirm attribute. Confirm no.', function() {
- expect(1);
+ expect(1);
stop(App.ajax_timeout);
- window.confirm = function(msg) {
- $(document.body).data('confirmation-message', msg);
- return false;
- };
+ window.confirm = function(msg) {
+ $(document.body).data('confirmation-message', msg);
+ return false;
+ };
- $('input[data-confirm]')
+ $('input[data-confirm]')
.live('ajax:before', function(e, data, status, xhr) {
App.assert_callback_not_invoked('ajax:before');
})
.trigger('click');
- setTimeout(function() {
- equals( $(document.body).data('confirmation-message'),
+ setTimeout(function() {
+ equals( $(document.body).data('confirmation-message'),
App.confirmation_message,
'confirmation message should be same');
@@ -137,14 +137,14 @@ test('clicking on Submit input tag with data-confirm attribute. Confirm no.', fu
});
test('clicking on button tag with data-confirm attribute. Confirm yes.', function() {
- expect(1);
+ expect(1);
- window.confirm = function(msg) {
- $(document.body).data('confirmation-message', msg);
- return true;
- };
+ window.confirm = function(msg) {
+ $(document.body).data('confirmation-message', msg);
+ return true;
+ };
- $('button[data-confirm]').trigger('click');
+ $('button[data-confirm]').trigger('click');
//clicking a button does not submit anything. Just assert that confirmation message was same.
equals( $(document.body).data('confirmation-message'),
@@ -153,14 +153,14 @@ test('clicking on button tag with data-confirm attribute. Confirm yes.', functio
});
test('clicking on a button tag with data-confirm attribute. Confirm no.', function() {
- expect(1);
+ expect(1);
- window.confirm = function(msg) {
- $(document.body).data('confirmation-message', msg);
- return false;
- };
+ window.confirm = function(msg) {
+ $(document.body).data('confirmation-message', msg);
+ return false;
+ };
- $('button[data-confirm]').trigger('click');
+ $('button[data-confirm]').trigger('click');
equals( $(document.body).data('confirmation-message'),
App.confirmation_message,
diff --git a/test/public/test/data-disable.js b/test/public/test/data-disable.js
index 04a65c47..ac2c3395 100644
--- a/test/public/test/data-disable.js
+++ b/test/public/test/data-disable.js
@@ -1,68 +1,68 @@
module('data-disable', {
- teardown: App.teardown,
- setup: function() {
-
- $('#fixtures').append($('', {
- action: App.url('update'),
- 'data-remote': 'true',
- method: 'post'
- }));
-
- $('form').append($(' ', {
- id: 'user_name',
- 'data-disable-with': 'processing ...',
- type: 'text',
- size: '30',
- 'name': 'user_name',
- 'value': 'john'
- }));
-
- $('#fixtures').append($('', {
- action: App.url('update'),
- method: 'post'
- }));
-
- $('form:not([data-remote])').append($(' ', {
- id: 'submit',
- 'data-disable-with': 'submitting ...',
- type: 'submit',
- name: 'submit',
- value: 'Submit'
- }));
-
- }
+ teardown: App.teardown,
+ setup: function() {
+
+ $('#fixtures').append($('', {
+ action: '/echo',
+ 'data-remote': 'true',
+ method: 'post'
+ }));
+
+ $('form').append($(' ', {
+ id: 'user_name',
+ 'data-disable-with': 'processing ...',
+ type: 'text',
+ size: '30',
+ 'name': 'user_name',
+ 'value': 'john'
+ }));
+
+ $('#fixtures').append($('', {
+ action: '/echo',
+ method: 'post'
+ }));
+
+ $('form:not([data-remote])').append($(' ', {
+ id: 'submit',
+ 'data-disable-with': 'submitting ...',
+ type: 'submit',
+ name: 'submit',
+ value: 'Submit'
+ }));
+
+ }
});
test('triggering ajax callbacks on a form with data-disable attribute', function() {
- expect(6);
+ expect(6);
- equals($('input:disabled').size(), 0, 'input field should not be disabled');
- equals($('input').val(), 'john', 'input field should have value given to it');
+ equals($('input:disabled').size(), 0, 'input field should not be disabled');
+ equals($('input').val(), 'john', 'input field should have value given to it');
- $('form').trigger('ajax:before');
+ $('form').trigger('ajax:before');
- equals($('input:disabled').size(), 1, 'input field should be disabled');
- equals($('input:disabled').val(), 'processing ...', 'input field should have disabled value given to it');
+ equals($('input:disabled').size(), 1, 'input field should be disabled');
+ equals($('input:disabled').val(), 'processing ...', 'input field should have disabled value given to it');
- $('form').trigger('ajax:complete');
+ $('form').trigger('ajax:complete');
- equals($('input:disabled').size(), 0, 'input field should not be disabled');
- equals($('input').val(), 'john', 'input field should have value given to it');
+ equals($('input:disabled').size(), 0, 'input field should not be disabled');
+ equals($('input').val(), 'john', 'input field should have value given to it');
});
test('clicking on non-ajax Submit input tag with data-disable-with attribute', function(){
- expect(4);
+ expect(4);
- equals($('input:disabled').size(), 0, 'input field should not be disabled');
- equals($('input[type=submit]').val(), 'Submit', 'input field should have value given to it');
+ equals($('input:disabled').size(), 0, 'input field should not be disabled');
+ equals($('input[type=submit]').val(), 'Submit', 'input field should have value given to it');
- $('form:not([data-remote])').live('submit', function (e) {
- e.preventDefault();
- }).trigger('submit');
+ $('form:not([data-remote])').live('submit', function (e) {
+ e.preventDefault();
+ }).trigger('submit');
- equals($('input:disabled').size(), 1, 'input field should be disabled');
- equals($('input:disabled').val(), 'submitting ...', 'input field should have disabled value given to it');
+ equals($('input:disabled').size(), 1, 'input field should be disabled');
+ equals($('input:disabled').val(), 'submitting ...', 'input field should have disabled value given to it');
});
diff --git a/test/public/test/data-method-iframe.js b/test/public/test/data-method-iframe.js
index 7af34292..ddb38b98 100644
--- a/test/public/test/data-method-iframe.js
+++ b/test/public/test/data-method-iframe.js
@@ -5,7 +5,7 @@ module('data-method-iframe', {
setup: function() {
$('#fixtures-iframe').append($(' ', {
- href: App.url('delete'),
+ href: '/delete',
'data-method': 'delete',
text: 'Destroy'
}));
diff --git a/test/public/test/data-method.js b/test/public/test/data-method.js
index a852f48b..73d907cf 100644
--- a/test/public/test/data-method.js
+++ b/test/public/test/data-method.js
@@ -1,19 +1,19 @@
module('data-method');
test('clicking on a link with data-method attribute', function() {
- expect(1);
- stop(App.ajax_timeout);
+ expect(1);
+ stop(App.ajax_timeout);
var iframe = $('#fixtures-iframe iframe');
iframeCallback = function() {
- var data = iframe.contents().find('body').text();
- equals(data, "/delete was invoked with delete verb. params is {\"_method\"=>\"delete\"}", 'iframe should have proper response message');
+ var data = iframe.contents().find('body').text();
+ equals(data, "/delete was invoked with delete verb. params is {\"_method\"=>\"delete\"}", 'iframe should have proper response message');
- start();
- };
+ start();
+ };
- //index.erb loads iframe.erb . Just wait for iframe to load and do its thing and then verify.
+ //index.erb loads iframe.erb . Just wait for iframe to load and do its thing and then verify.
if(iframe[0].loaded) {
iframeCallback();
} else {
@@ -23,20 +23,20 @@ test('clicking on a link with data-method attribute', function() {
test('clicking on a link with data-method attribute and csrf', function() {
- expect(1);
- stop(App.ajax_timeout);
+ expect(1);
+ stop(App.ajax_timeout);
var iframe = $('#fixtures-iframe-csrf iframe');
var iframeCallback = function() {
- var data = iframe.contents().find('body').text();
- equals(data, "/delete was invoked with delete verb. params is {\"_method\"=>\"delete\", \"authenticity_token\"=>\"cf50faa3fe97702ca1ae\"}",
+ var data = iframe.contents().find('body').text();
+ equals(data, "/delete was invoked with delete verb. params is {\"_method\"=>\"delete\", \"authenticity_token\"=>\"cf50faa3fe97702ca1ae\"}",
'iframe should be proper response message');
- start();
+ start();
};
- //index.erb load iframe-csrf.eb . Just wait for iframe to load and do its thing and then verify .
+ //index.erb load iframe-csrf.eb . Just wait for iframe to load and do its thing and then verify .
if(iframe[0].loaded) {
iframeCallback();
} else {
diff --git a/test/public/test/data-remote.js b/test/public/test/data-remote.js
index b51affd8..8ec2b01f 100644
--- a/test/public/test/data-remote.js
+++ b/test/public/test/data-remote.js
@@ -1,39 +1,39 @@
module('data-remote', {
- teardown: App.teardown,
-
- setup: function() {
- $('#fixtures').append($(' ', {
- href: App.url('show'),
- 'data-remote': 'true',
- text: 'my address'
- }));
-
- $('#fixtures').append($(' ', {
- href: App.url('show'),
- 'data-remote': 'true',
- name: 'submit',
- type: 'submit',
- value: 'Click me'
- }));
-
+ teardown: App.teardown,
+
+ setup: function() {
+ $('#fixtures').append($(' ', {
+ href: '/echo',
+ 'data-remote': 'true',
+ text: 'my address'
+ }));
+
+ $('#fixtures').append($(' ', {
+ href: '/echo',
+ 'data-remote': 'true',
+ name: 'submit',
+ type: 'submit',
+ value: 'Click me'
+ }));
+
var form = $('', {
- action: App.url('update'),
- 'data-remote': 'true',
- method: 'post'
- });
-
- form.append($(' ', {
- id: 'user_name',
- type: 'text',
- size: '30',
- 'name': 'user_name',
- 'value': 'john'
- }));
+ action: '/echo',
+ 'data-remote': 'true',
+ method: 'post'
+ });
+
+ form.append($(' ', {
+ id: 'user_name',
+ type: 'text',
+ size: '30',
+ 'name': 'user_name',
+ 'value': 'john'
+ }));
$('#fixtures').append(form);
- }
+ }
});
test('clicking on a link with data-remote attribute', function() {
@@ -44,7 +44,7 @@ test('clicking on a link with data-remote attribute', function() {
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
var request_env = data.request_env;
- App.assert_request_path(request_env, '/show');
+ App.assert_request_path(request_env, '/echo');
App.assert_get_request(request_env);
start();
@@ -62,7 +62,7 @@ test('clicking on Submit input tag with data-remote attribute', function() {
var request_env = data.request_env;
- App.assert_request_path(request_env, '/show');
+ App.assert_request_path(request_env, '/echo');
App.assert_get_request(request_env);
start();
@@ -81,7 +81,7 @@ test('Submitting form with data-remote attribute', function() {
var request_env = data.request_env,
params = request_env['rack.request.query_hash'];
- App.assert_request_path(request_env, '/update');
+ App.assert_request_path(request_env, '/echo');
equals(params['user_name'], 'john', 'ajax arguments shouldh ave key user_name with right value');
App.assert_post_request(request_env);
diff --git a/test/public/test/settings.js b/test/public/test/settings.js
index 006da678..5b3008cb 100644
--- a/test/public/test/settings.js
+++ b/test/public/test/settings.js
@@ -1,19 +1,8 @@
var App = App || {};
-App.host = 'http://localhost';
-App.port = 4567;
App.ajax_timeout = 1000;
-
-App.url = function(url) {
- return App.host + ':' + App.port + '/' + url;
-};
-
App.confirmation_message = 'Are you absolutely sure?';
-App.short_timeout = function(){
- setTimeout(function() { start(); }, 100);
-};
-
App.timeout = function(){
setTimeout(function() { start(); }, App.ajax_timeout);
};
diff --git a/test/server.rb b/test/server.rb
index ea821e0f..f7cf0942 100644
--- a/test/server.rb
+++ b/test/server.rb
@@ -46,20 +46,16 @@ def ajax_json_or_error
erb :index
end
-get '/iframe' do
- erb :iframe
-end
-
-get '/show' do
- ajax_json_or_error do
- { :hello => :sexy, :request_env => request.env }
- end
+[:get, :post, :put, :delete].each do |method|
+ send(method, '/echo') {
+ ajax_json_or_error do
+ { :request_env => request.env }
+ end
+ }
end
-post '/update' do
- ajax_json_or_error do
- { :hello => :sexy, :request_env => request.env }
- end
+get '/iframe' do
+ erb :iframe
end
delete '/delete' do
From c592fb7c454881d4cc26b33b5594815ab759c68c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Fri, 7 Jan 2011 18:19:47 +0100
Subject: [PATCH 039/364] refactor handling of "remote" forms and links
- forms now only respect "method" and not "data-method"
- jQuery.fn is not augmented with extra methods anymore
- "ajax:beforeSend" now bubbles and can abort the ajax request
---
src/rails.js | 112 ++++++++++++++++++++-------------------------------
1 file changed, 44 insertions(+), 68 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index 9674415c..e25c08d2 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -1,81 +1,57 @@
-/*
- * jquery-ujs
- *
- * http://github.com/rails/jquery-ujs/blob/master/src/rails.js
- *
- * This rails.js file supports jQuery 1.4.3 and 1.4.4 .
+/**
+ * Unobtrusive scripting adapter for jQuery
*
+ * Requires jQuery 1.4.3 or later.
+ * https://github.com/rails/jquery-ujs
*/
(function($) {
- $.fn.extend({
- /**
- * Triggers a custom event on an element and returns the event result
- * this is used to get around not being able to ensure callbacks are placed
- * at the end of the chain.
- */
- triggerAndReturn: function(name, data) {
- var event = new $.Event(name);
- this.trigger(event, data);
-
- return event.result !== false;
- },
-
- /**
- * Handles execution of remote calls. Provides following callbacks:
- *
- * - ajax:beforeSend - is executed before firing ajax call
- * - ajax:success - is executed when status is success
- * - ajax:complete - is executed when the request finishes, whether in failure or success
- * - ajax:error - is execute in case of error
- */
- callRemote: function() {
- var el = this,
- method = el.attr('method') || el.attr('data-method') || 'GET',
- url = el.attr('action') || el.attr('href'),
- dataType = el.attr('data-type') || ($.ajaxSettings && $.ajaxSettings.dataType);
-
- if (url === undefined) {
- throw "No URL specified for remote call (action or href must be present).";
- } else {
- var $this = $(this), data = el.is('form') ? el.serializeArray() : [];
-
- $.ajax({
- url: url,
- data: data,
- dataType: dataType,
- type: method.toUpperCase(),
- beforeSend: function(xhr) {
- if ($this.triggerHandler('ajax:beforeSend') === false) {
- return false;
- }
+ // Triggers an event on an element and returns the event result
+ function fire(obj, name, data) {
+ var event = new $.Event(name);
+ obj.trigger(event, data);
+ return event.result !== false;
+ }
+
+ // Submits "remote" forms and links with ajax
+ function handleRemote(element) {
+ var method, url, data,
+ dataType = element.attr('data-type') || ($.ajaxSettings && $.ajaxSettings.dataType);
+
+ if (element.is('form')) {
+ method = element.attr('method') || 'POST';
+ url = element.attr('action');
+ data = element.serializeArray();
+ } else {
+ method = element.attr('data-method') || 'GET';
+ url = element.attr('href');
+ data = null;
+ }
- // if user has used jQuery.ajaxSetup then call beforeSend callback
- var beforeSendGlobalCallback = $.ajaxSettings && $.ajaxSettings.beforeSend;
- if (beforeSendGlobalCallback !== undefined) {
- beforeSendGlobalCallback(xhr);
- }
- },
- success: function(data, status, xhr) {
- el.trigger('ajax:success', [data, status, xhr]);
- },
- complete: function(xhr) {
- el.trigger('ajax:complete', xhr);
- },
- error: function(xhr, status, error) {
- el.trigger('ajax:error', [xhr, status, error]);
- }
- });
+ $.ajax({
+ url: url, type: method, data: data, dataType: dataType,
+ // stopping the "ajax:beforeSend" event will cancel the ajax request
+ beforeSend: function(xhr) {
+ return fire(element, 'ajax:beforeSend', xhr);
+ },
+ success: function(data, status, xhr) {
+ element.trigger('ajax:success', [data, status, xhr]);
+ },
+ complete: function(xhr) {
+ element.trigger('ajax:complete', xhr);
+ },
+ error: function(xhr, status, error) {
+ element.trigger('ajax:error', [xhr, status, error]);
}
- }
- });
+ });
+ }
/**
* confirmation handler
*/
$('a[data-confirm], button[data-confirm], input[data-confirm]').live('click.rails', function() {
var el = $(this);
- if (el.triggerAndReturn('confirm')) {
+ if (fire(el, 'confirm')) {
if (!confirm(el.attr('data-confirm'))) {
return false;
}
@@ -86,12 +62,12 @@
* remote handlers
*/
$('form[data-remote]').live('submit.rails', function(e) {
- $(this).callRemote();
+ handleRemote($(this));
e.preventDefault();
});
$('a[data-remote],input[data-remote]').live('click.rails', function(e) {
- $(this).callRemote();
+ handleRemote($(this));
e.preventDefault();
});
From 0ff66c4321f90eab246b644496f7e29f229b6381 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Fri, 7 Jan 2011 18:52:03 +0100
Subject: [PATCH 040/364] test that "ajax:beforeSend" bubbles and can cancel
the ajax request
closes #63, #71, #73
---
test/public/test/call-remote-callbacks.js | 15 +++++++++++++++
test/public/test/settings.js | 2 +-
2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/test/public/test/call-remote-callbacks.js b/test/public/test/call-remote-callbacks.js
index b0935928..da1c1170 100644
--- a/test/public/test/call-remote-callbacks.js
+++ b/test/public/test/call-remote-callbacks.js
@@ -33,6 +33,21 @@ test('stopping the "ajax:beforeSend" event aborts the request', function() {
setTimeout(function(){ start() }, 200);
});
+test('"ajax:beforeSend" can be observed and stopped with event delegation', function() {
+ expect(1);
+ $('form[data-remote]').live('ajax:beforeSend', function() {
+ ok(true, 'ajax:beforeSend observed with event delegation');
+ return false;
+ });
+
+ submit(function(form) {
+ form.unbind('ajax:complete').bind('ajax:complete', function() {
+ ok(false, 'ajax:complete should not run');
+ });
+ });
+ setTimeout(function(){ start() }, 200);
+});
+
test('"ajax:beforeSend", "ajax:success" and "ajax:complete" are triggered', function() {
expect(3);
submit(function(form) {
diff --git a/test/public/test/settings.js b/test/public/test/settings.js
index 5b3008cb..a0f24704 100644
--- a/test/public/test/settings.js
+++ b/test/public/test/settings.js
@@ -30,7 +30,7 @@ App.assert_request_path = function(request_env, path) {
App.die_live_events = function(){
$('a[data-remote]').die();
$('input[data-remote]').die();
- $('form[data-remote]').die('ajax:success');
+ $('form[data-remote]').die('ajax:success').die('ajax:beforeSend');
$('a[data-confirm]').die();
$('input[data-confirm]').die();
};
From 42e39e9837b4000ebf466bf2a2befac30c013df8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Fri, 7 Jan 2011 21:04:17 +0100
Subject: [PATCH 041/364] simplify main event handlers; remove "data-remote"
support for submit buttons
- now with fewer `live()` listeners (2 instead of 4)
- "data-remote" for submit buttons is not supported anymore
---
src/rails.js | 67 ++++++++++++++++-----------------
test/public/test/data-remote.js | 26 -------------
2 files changed, 32 insertions(+), 61 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index e25c08d2..5a7ddd39 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -46,39 +46,10 @@
});
}
- /**
- * confirmation handler
- */
- $('a[data-confirm], button[data-confirm], input[data-confirm]').live('click.rails', function() {
- var el = $(this);
- if (fire(el, 'confirm')) {
- if (!confirm(el.attr('data-confirm'))) {
- return false;
- }
- }
- });
-
- /**
- * remote handlers
- */
- $('form[data-remote]').live('submit.rails', function(e) {
- handleRemote($(this));
- e.preventDefault();
- });
-
- $('a[data-remote],input[data-remote]').live('click.rails', function(e) {
- handleRemote($(this));
- e.preventDefault();
- });
-
- /**
- * <%= link_to "Delete", user_path(@user), :method => :delete, :confirm => "Are you sure?" %>
- *
- * Delete
- */
- $('a[data-method]:not([data-remote])').live('click.rails', function(e) {
- var link = $(this),
- href = link.attr('href'),
+ // Handles "data-method" on links such as:
+ // Delete
+ function handleMethod(link) {
+ var href = link.attr('href'),
method = link.attr('data-method'),
csrf_token = $('meta[name=csrf-token]').attr('content'),
csrf_param = $('meta[name=csrf-param]').attr('content'),
@@ -90,9 +61,35 @@
}
form.hide().append(metadata_input).appendTo('body');
-
- e.preventDefault();
form.submit();
+ }
+
+ function allowAction(element) {
+ var message = element.attr('data-confirm');
+ return !message || (fire(element, 'confirm') && confirm(message));
+ }
+
+ $('a[data-confirm], a[data-method], a[data-remote]').live('click.rails', function(e) {
+ var link = $(this);
+ if (!allowAction(link)) return false;
+
+ if (link.attr('data-remote')) {
+ handleRemote(link);
+ return false;
+ } else if (link.attr('data-method')) {
+ handleMethod(link);
+ return false;
+ }
+ });
+
+ $('form').live('submit.rails', function(e) {
+ var form = $(this);
+ if (!allowAction(form)) return false;
+
+ if (form.attr('data-remote')) {
+ handleRemote(form);
+ return false;
+ }
});
/**
diff --git a/test/public/test/data-remote.js b/test/public/test/data-remote.js
index 8ec2b01f..3e9ba9c6 100644
--- a/test/public/test/data-remote.js
+++ b/test/public/test/data-remote.js
@@ -8,14 +8,6 @@ module('data-remote', {
'data-remote': 'true',
text: 'my address'
}));
-
- $('#fixtures').append($(' ', {
- href: '/echo',
- 'data-remote': 'true',
- name: 'submit',
- type: 'submit',
- value: 'Click me'
- }));
var form = $('', {
action: '/echo',
@@ -52,24 +44,6 @@ test('clicking on a link with data-remote attribute', function() {
.trigger('click');
});
-test('clicking on Submit input tag with data-remote attribute', function() {
- expect(3);
- stop(App.ajax_timeout);
-
- $('input[data-remote]')
- .live('ajax:success', function(e, data, status, xhr) {
- App.assert_callback_invoked('ajax:success');
-
- var request_env = data.request_env;
-
- App.assert_request_path(request_env, '/echo');
- App.assert_get_request(request_env);
-
- start();
- })
- .trigger('click');
-});
-
test('Submitting form with data-remote attribute', function() {
expect(4);
stop(App.ajax_timeout);
From 947871305bb1d25eb4befbe69526dafa314e296a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Fri, 7 Jan 2011 23:16:10 +0100
Subject: [PATCH 042/364] include pressed submit button for "data-remote" forms
Had to trash "data-confirm" tests for submit buttons. They were brittle
and incorrect, but real tests can't be written for now because of a bug
in jQuery's trigger('click') implementation for submit buttons
Closes #70
---
src/rails.js | 11 ++++
test/public/test/data-confirm.js | 96 ++------------------------------
2 files changed, 16 insertions(+), 91 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index 5a7ddd39..3e6246b0 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -22,6 +22,9 @@
method = element.attr('method') || 'POST';
url = element.attr('action');
data = element.serializeArray();
+ // memoized value from clicked submit button
+ var button = element.data('ujs:submit-button');
+ if (button) data.push(button);
} else {
method = element.attr('data-method') || 'GET';
url = element.attr('href');
@@ -92,6 +95,14 @@
}
});
+ $('form input[type=submit], form button[type=submit], form button:not([type])').live('click', function() {
+ var button = $(this);
+ if (!allowAction(button)) return false;
+ // register the pressed submit button
+ var name = button.attr('name'), data = name ? {name:name, value:button.val()} : null;
+ button.closest('form').data('ujs:submit-button', data);
+ });
+
/**
* disable-with handlers
*/
diff --git a/test/public/test/data-confirm.js b/test/public/test/data-confirm.js
index a1016933..7a8adcfa 100644
--- a/test/public/test/data-confirm.js
+++ b/test/public/test/data-confirm.js
@@ -11,23 +11,20 @@ module('data-confirm', {
text: 'my social security number'
}));
- $('#fixtures').append($(' ', {
+ $('#fixtures').append($('', { action: '/echo', 'data-remote': 'true' }));
+
+ $('#fixtures form').append($(' ', {
'data-confirm': App.confirmation_message,
- 'data-remote': 'true',
- href: '/echo',
name: 'submit',
type: 'submit',
value: 'Click me'
}));
-
- $('#fixtures').append($(' ', {
+ $('#fixtures form').append($(' ', {
'data-confirm': App.confirmation_message,
- 'data-remote': 'true',
- href: '/echo',
name: 'submit',
type: 'button',
- value: 'Click me'
+ value: 'Press me'
}));
}
@@ -84,86 +81,3 @@ test('clicking on a link with data-confirm attribute. Confirm No.', function() {
start();
}, 100);
});
-
-test('clicking on Submit input tag with data-confirm attribute. Confirm yes.', function() {
- expect(4);
-
- window.confirm = function(msg) {
- $(document.body).data('confirmation-message', msg);
- return true;
- };
-
- stop(App.ajax_timeout);
-
- $('input[data-confirm]')
- .live('ajax:success', function(e, data, status, xhr) {
- App.assert_callback_invoked('ajax:success');
- var request_env = data.request_env;
- App.assert_request_path(request_env, '/echo');
- App.assert_get_request(request_env);
-
- equals( $(document.body).data('confirmation-message'),
- App.confirmation_message,
- 'confirmation message should be same');
-
- start();
- })
- .trigger('click');
-});
-
-test('clicking on Submit input tag with data-confirm attribute. Confirm no.', function() {
- expect(1);
- stop(App.ajax_timeout);
-
- window.confirm = function(msg) {
- $(document.body).data('confirmation-message', msg);
- return false;
- };
-
- $('input[data-confirm]')
- .live('ajax:before', function(e, data, status, xhr) {
- App.assert_callback_not_invoked('ajax:before');
- })
- .trigger('click');
-
- setTimeout(function() {
- equals( $(document.body).data('confirmation-message'),
- App.confirmation_message,
- 'confirmation message should be same');
-
- start();
- }, 100);
-
-});
-
-test('clicking on button tag with data-confirm attribute. Confirm yes.', function() {
- expect(1);
-
- window.confirm = function(msg) {
- $(document.body).data('confirmation-message', msg);
- return true;
- };
-
- $('button[data-confirm]').trigger('click');
-
- //clicking a button does not submit anything. Just assert that confirmation message was same.
- equals( $(document.body).data('confirmation-message'),
- App.confirmation_message,
- 'confirmation message should be same');
-});
-
-test('clicking on a button tag with data-confirm attribute. Confirm no.', function() {
- expect(1);
-
- window.confirm = function(msg) {
- $(document.body).data('confirmation-message', msg);
- return false;
- };
-
- $('button[data-confirm]').trigger('click');
-
- equals( $(document.body).data('confirmation-message'),
- App.confirmation_message,
- 'confirmation message should be same');
-
-});
From 7c00c3c859f3072f9cded930728f18f3bf9e3c52 Mon Sep 17 00:00:00 2001
From: Steve Schwartz
Date: Sat, 8 Jan 2011 17:23:40 -0500
Subject: [PATCH 043/364] missing parameters for "ajax:beforeSend" and
"ajax:complete"
---
src/rails.js | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index 3e6246b0..7f967bde 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -34,14 +34,14 @@
$.ajax({
url: url, type: method, data: data, dataType: dataType,
// stopping the "ajax:beforeSend" event will cancel the ajax request
- beforeSend: function(xhr) {
- return fire(element, 'ajax:beforeSend', xhr);
+ beforeSend: function(xhr, settings) {
+ return fire(element, 'ajax:beforeSend', [xhr, settings]);
},
success: function(data, status, xhr) {
element.trigger('ajax:success', [data, status, xhr]);
},
- complete: function(xhr) {
- element.trigger('ajax:complete', xhr);
+ complete: function(xhr, status) {
+ element.trigger('ajax:complete', [xhr, status]);
},
error: function(xhr, status, error) {
element.trigger('ajax:error', [xhr, status, error]);
From ec96408a746d3b0692da9249f218a3943fbffc28 Mon Sep 17 00:00:00 2001
From: Steve Schwartz
Date: Fri, 7 Jan 2011 21:15:19 -0500
Subject: [PATCH 044/364] default "Accept" header prefers JS, but accepts any
format available
---
src/rails.js | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/rails.js b/src/rails.js
index 7f967bde..2dafdfa3 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -35,6 +35,9 @@
url: url, type: method, data: data, dataType: dataType,
// stopping the "ajax:beforeSend" event will cancel the ajax request
beforeSend: function(xhr, settings) {
+ if (settings.dataType == undefined) {
+ xhr.setRequestHeader("Accept", settings.accepts.script + ", */*; q=0.5");
+ }
return fire(element, 'ajax:beforeSend', [xhr, settings]);
},
success: function(data, status, xhr) {
From ecd9beab7dc531b00d43e2b58a55ed507fce31c3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Sun, 9 Jan 2011 13:42:30 +0100
Subject: [PATCH 045/364] new README
---
README.md | 68 ++++++++++++++++++++++++++++++++++++++++++++++++
README.rdoc | 75 -----------------------------------------------------
2 files changed, 68 insertions(+), 75 deletions(-)
create mode 100644 README.md
delete mode 100644 README.rdoc
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..738d582d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,68 @@
+Unobtrusive scripting adapter for jQuery
+========================================
+
+This unobtrusive scripting support file is developed for the Ruby on Rails framework, but is not strictly tied to any specific backend. You can drop this into any application to:
+
+- force confirmation dialogs for various actions;
+- make non-GET requests from hyperlinks;
+- make forms or hyperlinks submit data asynchronously with Ajax;
+- have submit buttons become automatically disabled on form submit to prevent double-clicking.
+
+These features are achieved by adding certain ["data" attributes][data] to your HTML markup. In Rails, they are added by the framework's template helpers.
+
+Full [documentation is on the wiki][wiki], including the [list of published Ajax events][events].
+
+Requirements
+------------
+
+- [jQuery 1.4.3][jquery] or later;
+- for Ruby on Rails only: `<%= csrf_meta_tag %>` in the HEAD of your HTML layout;
+- HTML5 doctype (optional).
+
+If you don't use HTML5, adding "data" attributes to your HTML4 or XHTML pages might make them fail [W3C markup validation][validator]. However, this shouldn't create any issues for web browsers or other user agents.
+
+In Ruby on Rails 3, the `csrf_meta_tag` helper generates two meta tags containing values necessary for [cross-site request forgery protection][csrf] built into Rails. If you're using Rails 2, here is how to implement that helper:
+
+ # app/helpers/application_helper.rb
+ def csrf_meta_tag
+ if protect_against_forgery?
+ out = %( \n)
+ out << %( )
+ out % [ Rack::Utils.escape_html(request_forgery_protection_token),
+ Rack::Utils.escape_html(form_authenticity_token) ]
+ end
+ end
+
+Installation
+------------
+
+For automated installation, use the "jquery-rails" generator:
+
+ # Gemfile
+ gem 'jquery-rails', '>= 0.2.6'
+
+And run this command (add `-ui` if you want jQuery UI):
+
+ $ bundle install
+ $ rails generate jquery:install
+
+This will remove the Prototype.js library from Rails, add latest jQuery library and fetch the adapter. Be sure to choose to overwrite the "rails.js" file.
+
+### Manual installation
+
+[Download jQuery][jquery] and ["rails.js"][adapter] and place them in your "javascripts" directory.
+
+Configure the following in your application startup file:
+
+ config.action_view.javascript_expansions[:defaults] = %w(jquery rails application)
+
+Now the template helper `javascript_include_tag :defaults` will generate SCRIPT tags to load jQuery and rails.js.
+
+
+[data]: http://dev.w3.org/html5/spec/elements.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
+[events]: https://github.com/rails/jquery-ujs/wiki/ajax
+[jquery]: http://docs.jquery.com/Downloading_jQuery
+[validator]: http://validator.w3.org/
+[csrf]: http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html
+[adapter]: https://github.com/rails/jquery-ujs/raw/master/src/rails.js
diff --git a/README.rdoc b/README.rdoc
deleted file mode 100644
index b173c8bb..00000000
--- a/README.rdoc
+++ /dev/null
@@ -1,75 +0,0 @@
-= jquery-ujs
-
-Unobtrusive jQuery with Rails 3
-
-== The rails.js file from master branch supports following versions of jQuery:
-
-* 1.4.3
-* 1.4.4
-
-== If you are using one of the following version of jQuery then use branch v1.4 .
-
-* 1.4
-* 1.4.1
-* 1.4.2
-
-rails.js file from v1.4 branch can be accessed at https://github.com/rails/jquery-ujs/blob/v1.4/src/rails.js .
-
-
-== Automated Installation
-
-=== Step 1
-
-Add this line to your Gemfile:
-
- gem 'jquery-rails', '>= 0.2.6'
-
-=== Step 2
-
-Run this command:
-
- $ rails generate jquery:install # --ui if you want jQuery UI
-
-
-== Manual installation
-
-=== Step 1
-
-Download jQuery from http://docs.jquery.com/Downloading_jQuery and put the file in public/javascripts. For example, the file might look like:
-
- public/javascripts/jquery-1.4.4.min.js
-
-=== Step 2
-
-Copy rails.js from http://github.com/rails/jquery-ujs/raw/master/src/rails.js into public/javascripts - overwriting the prototype one (you can also delete the other prototype files if you don't need them for anything else.)
-
-=== Step 3 (optional)
-
-Uncomment following line from file config/application.rb
-
- config.action_view.javascript_expansions[:defaults] = %w(jquery rails application)
-
-To load jQuery from a CDN such as Google, just specify the full path. Change the above to
-
- config.action_view.javascript_expansions[:defaults] = %w(https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js rails application)
-
-Alternatively, you can specify the exact files to load in app/views/layouts/application.html.erb . Change javascript_include_tag :defaults to use jQuery
- <%= javascript_include_tag 'jquery' %>
- or
- <%= javascript_include_tag 'https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js' %>
-instead of the default prototype helpers.
-
-= Testing
-
-== Installation
-
- $ gem install bundler
- $ bundle install
-
-== Running tests
-
- $ bundle exec ruby test/server.rb
-
-Visit http://localhost:4567 and all the tests should pass.
-
-At the top of the page you will see links to jQuery 1.4.3 and 1.4.4 . By clicking on those links you will be executing the tests against the clicked version of jquery. By default test uses jQuery 1.4.4 .
From 956bf96ad4b75c141b850dd7e7fb939868a61d88 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Sun, 9 Jan 2011 15:30:40 +0100
Subject: [PATCH 046/364] refactor call-remote tests
---
test/public/test/call-remote.js | 98 ++++++++++++---------------------
test/public/test/settings.js | 2 +-
2 files changed, 37 insertions(+), 63 deletions(-)
diff --git a/test/public/test/call-remote.js b/test/public/test/call-remote.js
index 9b69d51b..194f42df 100644
--- a/test/public/test/call-remote.js
+++ b/test/public/test/call-remote.js
@@ -18,93 +18,67 @@ module('call-remote', {
teardown: App.teardown
});
-test('form method is read from "method" and not from "data-method"', function() {
- expect(2);
- build_form({ method: 'post', 'data-method': 'get' });
+function submit(fn) {
stop(App.ajax_timeout);
$('form[data-remote]')
- .live('ajax:success', function(e, data, status, xhr) {
- App.assert_callback_invoked('ajax:success');
- App.assert_post_request(data.request_env);
- start();
- })
+ .live('ajax:success', fn)
+ .live('ajax:complete', function() { start() })
.trigger('submit');
+}
+
+test('form method is read from "method" and not from "data-method"', function() {
+ expect(1);
+ build_form({ method: 'post', 'data-method': 'get' });
+
+ submit(function(e, data, status, xhr) {
+ App.assert_post_request(data.request_env);
+ });
});
test('form method is not read from "data-method" attribute in case of missing "method"', function() {
- expect(2);
+ expect(1);
build_form({ 'data-method': 'put' });
- stop(App.ajax_timeout);
- $('form[data-remote]')
- .live('ajax:success', function(e, data, status, xhr) {
- App.assert_callback_invoked('ajax:success');
- App.assert_post_request(data.request_env);
-
- start();
- })
- .trigger('submit');
+ submit(function(e, data, status, xhr) {
+ App.assert_post_request(data.request_env);
+ });
});
test('form default method is POST', function() {
- expect(2);
+ expect(1);
build_form();
- stop(App.ajax_timeout);
- $('form[data-remote]')
- .live('ajax:success', function(e, data, status, xhr) {
- App.assert_callback_invoked('ajax:success');
- App.assert_post_request(data.request_env);
- start();
- })
- .trigger('submit');
+ submit(function(e, data, status, xhr) {
+ App.assert_post_request(data.request_env);
+ });
});
test('form url is picked up from "action"', function() {
- expect(2);
- build_form();
- stop(App.ajax_timeout);
+ expect(1);
+ build_form({ method: 'post' });
- $('form[data-remote]')
- .live('ajax:success', function(e, data, status, xhr) {
- App.assert_callback_invoked('ajax:success');
- App.assert_request_path(data.request_env, '/echo');
- start();
- })
- .trigger('submit');
+ submit(function(e, data, status, xhr) {
+ App.assert_request_path(data.request_env, '/echo');
+ });
});
test('form url is read from "action" not "href"', function() {
- expect(2);
- build_form({ href: '/echo2' });
- stop(App.ajax_timeout);
+ expect(1);
+ build_form({ method: 'post', href: '/echo2' });
- $('form[data-remote]')
- .live('ajax:success', function(e, data, status, xhr) {
- App.assert_callback_invoked('ajax:success');
- App.assert_request_path(data.request_env, '/echo');
- start();
- })
- .trigger('submit');
+ submit(function(e, data, status, xhr) {
+ App.assert_request_path(data.request_env, '/echo');
+ });
});
-test('data should be availabe in JSON format if data-type is json', function() {
- expect(2);
- build_form({
- 'method': 'post',
- 'data-method': 'get',
- 'data-type': 'json'
- });
- stop(App.ajax_timeout);
+test('accept application/json if "data-type" is json', function() {
+ expect(1);
+ build_form({ method: 'post', 'data-type': 'json' });
- $('form[data-remote]')
- .live('ajax:success', function(e, data, status, xhr) {
- App.assert_callback_invoked('ajax:success');
- App.assert_post_request(data.request_env);
- start();
- })
- .trigger('submit');
+ submit(function(e, data, status, xhr) {
+ equals(data.request_env['HTTP_ACCEPT'], 'application/json, text/javascript, */*; q=0.01');
+ });
});
})();
diff --git a/test/public/test/settings.js b/test/public/test/settings.js
index a0f24704..a3e5416c 100644
--- a/test/public/test/settings.js
+++ b/test/public/test/settings.js
@@ -30,7 +30,7 @@ App.assert_request_path = function(request_env, path) {
App.die_live_events = function(){
$('a[data-remote]').die();
$('input[data-remote]').die();
- $('form[data-remote]').die('ajax:success').die('ajax:beforeSend');
+ $('form[data-remote]').die();
$('a[data-confirm]').die();
$('input[data-confirm]').die();
};
From 22877193b17f2c257a83faca5ea5b4efa9c42943 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Sun, 9 Jan 2011 15:32:04 +0100
Subject: [PATCH 047/364] improve the default HTTP "Accept" value
Now the value is:
*/*;q=0.5, text/javascript, application/javascript
This is more correct than the previous value:
text/javascript, application/javascript, */*; q=0.5
New value is also safer against the jQuery 1.4.4 (and below) + WebKit bug
where the resulting value would be:
*/*, text/javascript, application/javascript, */*; q=0.5
Which starts to be very confusing for the backend to parse.
Closes #74
---
src/rails.js | 4 ++--
test/public/test/call-remote.js | 12 ++++++++++++
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index 2dafdfa3..cec7f43e 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -35,8 +35,8 @@
url: url, type: method, data: data, dataType: dataType,
// stopping the "ajax:beforeSend" event will cancel the ajax request
beforeSend: function(xhr, settings) {
- if (settings.dataType == undefined) {
- xhr.setRequestHeader("Accept", settings.accepts.script + ", */*; q=0.5");
+ if (settings.dataType === undefined) {
+ xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script);
}
return fire(element, 'ajax:beforeSend', [xhr, settings]);
},
diff --git a/test/public/test/call-remote.js b/test/public/test/call-remote.js
index 194f42df..fea6178f 100644
--- a/test/public/test/call-remote.js
+++ b/test/public/test/call-remote.js
@@ -72,6 +72,18 @@ test('form url is read from "action" not "href"', function() {
});
});
+test('prefer JS, but accept any format', function() {
+ expect(1);
+ build_form({ method: 'post' });
+
+ submit(function(e, data, status, xhr) {
+ var accept = data.request_env['HTTP_ACCEPT'];
+ // HACK to normalize header sent by jQuery 1.4.4 and below:
+ accept = accept.replace('*/*, */*', '*/*');
+ equals(accept, '*/*;q=0.5, text/javascript, application/javascript');
+ });
+});
+
test('accept application/json if "data-type" is json', function() {
expect(1);
build_form({ method: 'post', 'data-type': 'json' });
From d2155893635feab7a8a1e81540153ff277dc1f62 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Sun, 9 Jan 2011 16:23:50 +0100
Subject: [PATCH 048/364] fix ajax test for Opera
---
test/public/test/call-remote-callbacks.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/test/public/test/call-remote-callbacks.js b/test/public/test/call-remote-callbacks.js
index da1c1170..9cdb7a12 100644
--- a/test/public/test/call-remote-callbacks.js
+++ b/test/public/test/call-remote-callbacks.js
@@ -63,7 +63,8 @@ test('"ajax:beforeSend", "ajax:error" and "ajax:complete" are triggered on error
form.bind('ajax:beforeSend', function(arg) { ok(true, 'ajax:beforeSend') });
form.bind('ajax:error', function(e, xhr, status, error) {
ok(true, 'ajax:error');
- equals(xhr.status, 403, 'status code should be 403');
+ // Opera returns "0" for HTTP code
+ equals(xhr.status, window.opera ? 0 : 403, 'status code should be 403');
});
});
});
From 5d1d95b93424d39ffafe4c61234dd0d7515d42ea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Sun, 9 Jan 2011 20:19:57 +0100
Subject: [PATCH 049/364] fix "data-disable-with" support for remote forms
---
src/rails.js | 4 ++--
test/public/test/data-disable.js | 31 ++++++++++++++++---------------
2 files changed, 18 insertions(+), 17 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index cec7f43e..1795d2f6 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -98,7 +98,7 @@
}
});
- $('form input[type=submit], form button[type=submit], form button:not([type])').live('click', function() {
+ $('form input[type=submit], form button[type=submit], form button:not([type])').live('click.rails', function() {
var button = $(this);
if (!allowAction(button)) return false;
// register the pressed submit button
@@ -122,7 +122,7 @@
});
};
- $(disable_with_form_remote_selector).live('ajax:before.rails', disable_with_input_function);
+ $(disable_with_form_remote_selector).live('ajax:beforeSend.rails', disable_with_input_function);
$(disable_with_form_not_remote_selector).live('submit.rails', disable_with_input_function);
$(disable_with_form_remote_selector).live('ajax:complete.rails', function() {
diff --git a/test/public/test/data-disable.js b/test/public/test/data-disable.js
index ac2c3395..2311eb91 100644
--- a/test/public/test/data-disable.js
+++ b/test/public/test/data-disable.js
@@ -36,33 +36,34 @@ module('data-disable', {
test('triggering ajax callbacks on a form with data-disable attribute', function() {
expect(6);
+ var form = $('form[data-remote]'), input = form.find('input[type=text]');
- equals($('input:disabled').size(), 0, 'input field should not be disabled');
- equals($('input').val(), 'john', 'input field should have value given to it');
+ ok(!input.is(':disabled'), 'input field should not be disabled');
+ equals(input.val(), 'john', 'input field should have value given to it');
- $('form').trigger('ajax:before');
+ form.trigger('ajax:beforeSend');
- equals($('input:disabled').size(), 1, 'input field should be disabled');
- equals($('input:disabled').val(), 'processing ...', 'input field should have disabled value given to it');
+ ok(input.is(':disabled'), 'input field should be disabled');
+ equals(input.val(), 'processing ...', 'input field should have disabled value given to it');
- $('form').trigger('ajax:complete');
-
- equals($('input:disabled').size(), 0, 'input field should not be disabled');
- equals($('input').val(), 'john', 'input field should have value given to it');
+ form.trigger('ajax:complete');
+ ok(!input.is(':disabled'), 'input field should not be disabled');
+ equals(input.val(), 'john', 'input field should have value given to it');
});
test('clicking on non-ajax Submit input tag with data-disable-with attribute', function(){
expect(4);
+ var form = $('form:not([data-remote])'), input = form.find('input[type=submit]');
- equals($('input:disabled').size(), 0, 'input field should not be disabled');
- equals($('input[type=submit]').val(), 'Submit', 'input field should have value given to it');
+ ok(!input.is(':disabled'), 'input field should not be disabled');
+ equals(input.val(), 'Submit', 'input field should have value given to it');
- $('form:not([data-remote])').live('submit', function (e) {
+ $('form:not([data-remote])').live('submit', function(e) {
+ // prevent the submit navigating away from the test suite
e.preventDefault();
}).trigger('submit');
- equals($('input:disabled').size(), 1, 'input field should be disabled');
- equals($('input:disabled').val(), 'submitting ...', 'input field should have disabled value given to it');
-
+ ok(input.is(':disabled'), 'input field should not be disabled');
+ equals(input.val(), 'submitting ...', 'input field should have disabled value given to it');
});
From c0d6b6cf0ed0be3f206a2a93d50091658f68c33c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Sun, 9 Jan 2011 20:31:29 +0100
Subject: [PATCH 050/364] refactor "data-disable-with" implementation
---
src/rails.js | 48 ++++++++++++++++++++++++------------------------
1 file changed, 24 insertions(+), 24 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index 1795d2f6..f2c38ce6 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -70,6 +70,22 @@
form.submit();
}
+ function disableFormElements(form) {
+ form.find('input[data-disable-with]').each(function() {
+ var input = $(this);
+ input.data('ujs:enable-with', input.val())
+ .val(input.attr('data-disable-with'))
+ .attr('disabled', 'disabled');
+ });
+ }
+
+ function enableFormElements(form) {
+ form.find('input[data-disable-with]').each(function() {
+ var input = $(this);
+ input.val(input.data('ujs:enable-with')).removeAttr('disabled');
+ });
+ }
+
function allowAction(element) {
var message = element.attr('data-confirm');
return !message || (fire(element, 'confirm') && confirm(message));
@@ -95,6 +111,8 @@
if (form.attr('data-remote')) {
handleRemote(form);
return false;
+ } else {
+ disableFormElements(form);
}
});
@@ -105,30 +123,12 @@
var name = button.attr('name'), data = name ? {name:name, value:button.val()} : null;
button.closest('form').data('ujs:submit-button', data);
});
+
+ $('form').live('ajax:beforeSend.rails', function(event) {
+ if (this == event.target) disableFormElements($(this));
+ });
- /**
- * disable-with handlers
- */
- var disable_with_input_selector = 'input[data-disable-with]',
- disable_with_form_remote_selector = 'form[data-remote]:has(' + disable_with_input_selector + ')',
- disable_with_form_not_remote_selector = 'form:not([data-remote]):has(' + disable_with_input_selector + ')';
-
- var disable_with_input_function = function() {
- $(this).find(disable_with_input_selector).each(function() {
- var input = $(this);
- input.data('enable-with', input.val())
- .attr('value', input.attr('data-disable-with'))
- .attr('disabled', 'disabled');
- });
- };
-
- $(disable_with_form_remote_selector).live('ajax:beforeSend.rails', disable_with_input_function);
- $(disable_with_form_not_remote_selector).live('submit.rails', disable_with_input_function);
-
- $(disable_with_form_remote_selector).live('ajax:complete.rails', function() {
- $(this).find(disable_with_input_selector).each(function() {
- var input = $(this);
- input.removeAttr('disabled').val(input.data('enable-with'));
- });
+ $('form').live('ajax:complete.rails', function(event) {
+ if (this == event.target) enableFormElements($(this));
});
})( jQuery );
From de5a8226df6d59873ea3daab8bdf01c1d571c181 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Sun, 9 Jan 2011 21:13:36 +0100
Subject: [PATCH 051/364] tests: remove unnecessary DOM setup that is broken in
IE
---
test/public/test/data-confirm.js | 18 ------------------
1 file changed, 18 deletions(-)
diff --git a/test/public/test/data-confirm.js b/test/public/test/data-confirm.js
index 7a8adcfa..2ef61e55 100644
--- a/test/public/test/data-confirm.js
+++ b/test/public/test/data-confirm.js
@@ -3,30 +3,12 @@ module('data-confirm', {
teardown: App.teardown,
setup: function() {
-
$('#fixtures').append($(' ', {
href: '/echo',
'data-remote': 'true',
'data-confirm': 'Are you absolutely sure?',
text: 'my social security number'
}));
-
- $('#fixtures').append($('', { action: '/echo', 'data-remote': 'true' }));
-
- $('#fixtures form').append($(' ', {
- 'data-confirm': App.confirmation_message,
- name: 'submit',
- type: 'submit',
- value: 'Click me'
- }));
-
- $('#fixtures form').append($(' ', {
- 'data-confirm': App.confirmation_message,
- name: 'submit',
- type: 'button',
- value: 'Press me'
- }));
-
}
});
From fc639928d1e15c885b85de5b517346db7f963f44 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Sun, 9 Jan 2011 21:16:09 +0100
Subject: [PATCH 052/364] change default form method from POST to GET
per HTML spec
http://www.w3.org/TR/html401/interact/forms.html#h-17.3
---
src/rails.js | 6 +++---
test/public/test/call-remote.js | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/rails.js b/src/rails.js
index f2c38ce6..55ee2e4d 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -19,20 +19,20 @@
dataType = element.attr('data-type') || ($.ajaxSettings && $.ajaxSettings.dataType);
if (element.is('form')) {
- method = element.attr('method') || 'POST';
+ method = element.attr('method');
url = element.attr('action');
data = element.serializeArray();
// memoized value from clicked submit button
var button = element.data('ujs:submit-button');
if (button) data.push(button);
} else {
- method = element.attr('data-method') || 'GET';
+ method = element.attr('data-method');
url = element.attr('href');
data = null;
}
$.ajax({
- url: url, type: method, data: data, dataType: dataType,
+ url: url, type: method || 'GET', data: data, dataType: dataType,
// stopping the "ajax:beforeSend" event will cancel the ajax request
beforeSend: function(xhr, settings) {
if (settings.dataType === undefined) {
diff --git a/test/public/test/call-remote.js b/test/public/test/call-remote.js
index fea6178f..d396a2bf 100644
--- a/test/public/test/call-remote.js
+++ b/test/public/test/call-remote.js
@@ -41,16 +41,16 @@ test('form method is not read from "data-method" attribute in case of missing "m
build_form({ 'data-method': 'put' });
submit(function(e, data, status, xhr) {
- App.assert_post_request(data.request_env);
+ App.assert_get_request(data.request_env);
});
});
-test('form default method is POST', function() {
+test('form default method is GET', function() {
expect(1);
build_form();
submit(function(e, data, status, xhr) {
- App.assert_post_request(data.request_env);
+ App.assert_get_request(data.request_env);
});
});
From 7fcc1fef22a04d5aecc180bb0f5801ccd0361037 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Tue, 11 Jan 2011 11:54:38 +0100
Subject: [PATCH 053/364] clear the memoized submit button value after
serializing
---
src/rails.js | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/rails.js b/src/rails.js
index 55ee2e4d..031ad479 100644
--- a/src/rails.js
+++ b/src/rails.js
@@ -24,7 +24,10 @@
data = element.serializeArray();
// memoized value from clicked submit button
var button = element.data('ujs:submit-button');
- if (button) data.push(button);
+ if (button) {
+ data.push(button);
+ element.data('ujs:submit-button', null);
+ }
} else {
method = element.attr('data-method');
url = element.attr('href');
From 25eb1fd96ed94e153558710cc666745f907b1779 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Tue, 11 Jan 2011 11:56:17 +0100
Subject: [PATCH 054/364] don't include "application" in
`javascript_expansions`
thanks, Jesus De Meyer
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 738d582d..e3fba57b 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,7 @@ This will remove the Prototype.js library from Rails, add latest jQuery library
Configure the following in your application startup file:
- config.action_view.javascript_expansions[:defaults] = %w(jquery rails application)
+ config.action_view.javascript_expansions[:defaults] = %w(jquery rails)
Now the template helper `javascript_include_tag :defaults` will generate SCRIPT tags to load jQuery and rails.js.
From 513eda30ae7506eb896e642499249a52cde38ca5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Wed, 12 Jan 2011 01:20:09 +0100
Subject: [PATCH 055/364] update QUnit to edge version
---
test/public/vendor/qunit.css | 294 ++++++----
test/public/vendor/qunit.js | 1051 ++++++++++++++++++++++------------
2 files changed, 864 insertions(+), 481 deletions(-)
diff --git a/test/public/vendor/qunit.css b/test/public/vendor/qunit.css
index 9eafd240..87a5f820 100644
--- a/test/public/vendor/qunit.css
+++ b/test/public/vendor/qunit.css
@@ -1,118 +1,196 @@
-ol#qunit-tests {
- font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
- margin:0;
- padding:0;
- list-style-position:inside;
+/** Font Family and Sizes */
- font-size: smaller;
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
+ font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
}
-ol#qunit-tests li{
- padding:0.4em 0.5em 0.4em 2.5em;
- border-bottom:1px solid #fff;
- font-size:small;
- list-style-position:inside;
+
+#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
+#qunit-tests { font-size: smaller; }
+
+
+/** Resets */
+
+#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
+ margin: 0;
+ padding: 0;
+}
+
+
+/** Header */
+
+#qunit-header {
+ padding: 0.5em 0 0.5em 1em;
+
+ color: #8699a4;
+ background-color: #0d3349;
+
+ font-size: 1.5em;
+ line-height: 1em;
+ font-weight: normal;
+
+ border-radius: 15px 15px 0 0;
+ -moz-border-radius: 15px 15px 0 0;
+ -webkit-border-top-right-radius: 15px;
+ -webkit-border-top-left-radius: 15px;
+}
+
+#qunit-header a {
+ text-decoration: none;
+ color: #c2ccd1;
+}
+
+#qunit-header a:hover,
+#qunit-header a:focus {
+ color: #fff;
+}
+
+#qunit-banner {
+ height: 5px;
+}
+
+#qunit-testrunner-toolbar {
+ padding: 0em 0 0.5em 2em;
}
-ol#qunit-tests li ol{
+
+#qunit-userAgent {
+ padding: 0.5em 0 0.5em 2.5em;
+ background-color: #2b81af;
+ color: #fff;
+ text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
+}
+
+
+/** Tests: Pass/Fail */
+
+#qunit-tests {
+ list-style-position: inside;
+}
+
+#qunit-tests li {
+ padding: 0.4em 0.5em 0.4em 2.5em;
+ border-bottom: 1px solid #fff;
+ list-style-position: inside;
+}
+
+#qunit-tests li strong {
+ cursor: pointer;
+}
+
+#qunit-tests ol {
+ margin-top: 0.5em;
+ padding: 0.5em;
+
+ background-color: #fff;
+
+ border-radius: 15px;
+ -moz-border-radius: 15px;
+ -webkit-border-radius: 15px;
+
box-shadow: inset 0px 2px 13px #999;
-moz-box-shadow: inset 0px 2px 13px #999;
-webkit-box-shadow: inset 0px 2px 13px #999;
- margin-top:0.5em;
- margin-left:0;
- padding:0.5em;
- background-color:#fff;
- border-radius:15px;
- -moz-border-radius: 15px;
- -webkit-border-radius: 15px;
}
-ol#qunit-tests li li{
- border-bottom:none;
- margin:0.5em;
- background-color:#fff;
+
+#qunit-tests table {
+ border-collapse: collapse;
+ margin-top: .2em;
+}
+
+#qunit-tests th {
+ text-align: right;
+ vertical-align: top;
+ padding: 0 .5em 0 0;
+}
+
+#qunit-tests td {
+ vertical-align: top;
+}
+
+#qunit-tests pre {
+ margin: 0;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+}
+
+#qunit-tests del {
+ background-color: #e0f2be;
+ color: #374e0c;
+ text-decoration: none;
+}
+
+#qunit-tests ins {
+ background-color: #ffcaca;
+ color: #500;
+ text-decoration: none;
+}
+
+/*** Test Counts */
+
+#qunit-tests b.counts { color: black; }
+#qunit-tests b.passed { color: #5E740B; }
+#qunit-tests b.failed { color: #710909; }
+
+#qunit-tests li li {
+ margin: 0.5em;
+ padding: 0.4em 0.5em 0.4em 0.5em;
+ background-color: #fff;
+ border-bottom: none;
list-style-position: inside;
- padding:0.4em 0.5em 0.4em 0.5em;
-}
-
-ol#qunit-tests li li.pass{
- border-left:26px solid #C6E746;
- background-color:#fff;
- color:#5E740B;
- }
-ol#qunit-tests li li.fail{
- border-left:26px solid #EE5757;
- background-color:#fff;
- color:#710909;
-}
-ol#qunit-tests li.pass{
- background-color:#D2E0E6;
- color:#528CE0;
-}
-ol#qunit-tests li.fail{
- background-color:#EE5757;
- color:#000;
-}
-ol#qunit-tests li strong {
- cursor:pointer;
-}
-h1#qunit-header{
- background-color:#0d3349;
- margin:0;
- padding:0.5em 0 0.5em 1em;
- color:#fff;
- font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
- border-top-right-radius:15px;
- border-top-left-radius:15px;
- -moz-border-radius-topright:15px;
- -moz-border-radius-topleft:15px;
- -webkit-border-top-right-radius:15px;
- -webkit-border-top-left-radius:15px;
- text-shadow: rgba(0, 0, 0, 0.5) 4px 4px 1px;
-}
-h2#qunit-banner{
- font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
- height:5px;
- margin:0;
- padding:0;
-}
-h2#qunit-banner.qunit-pass{
- background-color:#C6E746;
-}
-h2#qunit-banner.qunit-fail, #qunit-testrunner-toolbar {
- background-color:#EE5757;
}
-#qunit-testrunner-toolbar {
- font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
- padding:0;
- /*width:80%;*/
- padding:0em 0 0.5em 2em;
- font-size: small;
-}
-h2#qunit-userAgent {
- font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
- background-color:#2b81af;
- margin:0;
- padding:0;
- color:#fff;
- font-size: small;
- padding:0.5em 0 0.5em 2.5em;
- text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
+
+/*** Passing Styles */
+
+#qunit-tests li li.pass {
+ color: #5E740B;
+ background-color: #fff;
+ border-left: 26px solid #C6E746;
+}
+
+#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
+#qunit-tests .pass .test-name { color: #366097; }
+
+#qunit-tests .pass .test-actual,
+#qunit-tests .pass .test-expected { color: #999999; }
+
+#qunit-banner.qunit-pass { background-color: #C6E746; }
+
+/*** Failing Styles */
+
+#qunit-tests li li.fail {
+ color: #710909;
+ background-color: #fff;
+ border-left: 26px solid #EE5757;
+}
+
+#qunit-tests .fail { color: #000000; background-color: #EE5757; }
+#qunit-tests .fail .test-name,
+#qunit-tests .fail .module-name { color: #000000; }
+
+#qunit-tests .fail .test-actual { color: #EE5757; }
+#qunit-tests .fail .test-expected { color: green; }
+
+#qunit-banner.qunit-fail,
+#qunit-testrunner-toolbar { background-color: #EE5757; }
+
+
+/** Footer */
+
+#qunit-testresult {
+ padding: 0.5em 0.5em 0.5em 2.5em;
+
+ color: #2b81af;
+ background-color: #D2E0E6;
+
+ border-radius: 0 0 15px 15px;
+ -moz-border-radius: 0 0 15px 15px;
+ -webkit-border-bottom-right-radius: 15px;
+ -webkit-border-bottom-left-radius: 15px;
+}
+
+/** Fixture */
+
+#qunit-fixture {
+ position: absolute;
+ top: -10000px;
+ left: -10000px;
}
-p#qunit-testresult{
- font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
- margin:0;
- font-size: small;
- color:#2b81af;
- border-bottom-right-radius:15px;
- border-bottom-left-radius:15px;
- -moz-border-radius-bottomright:15px;
- -moz-border-radius-bottomleft:15px;
- -webkit-border-bottom-right-radius:15px;
- -webkit-border-bottom-left-radius:15px;
- background-color:#D2E0E6;
- padding:0.5em 0.5em 0.5em 2.5em;
-}
-strong b.fail{
- color:#710909;
- }
-strong b.pass{
- color:#5E740B;
- }
diff --git a/test/public/vendor/qunit.js b/test/public/vendor/qunit.js
index 9ef5f8d6..f9db71c1 100644
--- a/test/public/vendor/qunit.js
+++ b/test/public/vendor/qunit.js
@@ -3,61 +3,240 @@
*
* http://docs.jquery.com/QUnit
*
- * Copyright (c) 2009 John Resig, Jörn Zaefferer
+ * Copyright (c) 2011 John Resig, Jörn Zaefferer
* Dual licensed under the MIT (MIT-LICENSE.txt)
- * and GPL (GPL-LICENSE.txt) licenses.
+ * or GPL (GPL-LICENSE.txt) licenses.
*/
(function(window) {
-var QUnit = {
+var defined = {
+ setTimeout: typeof window.setTimeout !== "undefined",
+ sessionStorage: (function() {
+ try {
+ return !!sessionStorage.getItem;
+ } catch(e){
+ return false;
+ }
+ })()
+}
- // Initialize the configuration options
- init: function() {
- config = {
- stats: { all: 0, bad: 0 },
- moduleStats: { all: 0, bad: 0 },
- started: +new Date,
- updateRate: 1000,
- blocking: false,
- autorun: false,
- assertions: [],
- filters: [],
- queue: []
- };
+var testId = 0;
- var tests = id("qunit-tests"),
- banner = id("qunit-banner"),
- result = id("qunit-testresult");
+var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {
+ this.name = name;
+ this.testName = testName;
+ this.expected = expected;
+ this.testEnvironmentArg = testEnvironmentArg;
+ this.async = async;
+ this.callback = callback;
+ this.assertions = [];
+};
+Test.prototype = {
+ init: function() {
+ var tests = id("qunit-tests");
+ if (tests) {
+ var b = document.createElement("strong");
+ b.innerHTML = "Running " + this.name;
+ var li = document.createElement("li");
+ li.appendChild( b );
+ li.id = this.id = "test-output" + testId++;
+ tests.appendChild( li );
+ }
+ },
+ setup: function() {
+ if (this.module != config.previousModule) {
+ if ( config.previousModule ) {
+ QUnit.moduleDone( config.previousModule, config.moduleStats.bad, config.moduleStats.all );
+ }
+ config.previousModule = this.module;
+ config.moduleStats = { all: 0, bad: 0 };
+ QUnit.moduleStart( this.module, this.moduleTestEnvironment );
+ }
- if ( tests ) {
- tests.innerHTML = "";
+ config.current = this;
+ this.testEnvironment = extend({
+ setup: function() {},
+ teardown: function() {}
+ }, this.moduleTestEnvironment);
+ if (this.testEnvironmentArg) {
+ extend(this.testEnvironment, this.testEnvironmentArg);
}
- if ( banner ) {
- banner.className = "";
+ QUnit.testStart( this.testName, this.testEnvironment );
+
+ // allow utility functions to access the current test environment
+ // TODO why??
+ QUnit.current_testEnvironment = this.testEnvironment;
+
+ try {
+ if ( !config.pollution ) {
+ saveGlobal();
+ }
+
+ this.testEnvironment.setup.call(this.testEnvironment);
+ } catch(e) {
+ // TODO use testName instead of name for no-markup message?
+ QUnit.ok( false, "Setup failed on " + this.name + ": " + e.message );
+ }
+ },
+ run: function() {
+ if ( this.async ) {
+ QUnit.stop();
}
- if ( result ) {
- result.parentNode.removeChild( result );
+ try {
+ this.callback.call(this.testEnvironment);
+ } catch(e) {
+ // TODO use testName instead of name for no-markup message?
+ fail("Test " + this.name + " died, exception and test follows", e, this.callback);
+ QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) );
+ // else next test will carry the responsibility
+ saveGlobal();
+
+ // Restart the tests if they're blocking
+ if ( config.blocking ) {
+ start();
+ }
}
},
-
- // call on start of module test to prepend name to all tests
- module: function(name, testEnvironment) {
- config.currentModule = name;
+ teardown: function() {
+ try {
+ checkPollution();
+ this.testEnvironment.teardown.call(this.testEnvironment);
+ } catch(e) {
+ // TODO use testName instead of name for no-markup message?
+ QUnit.ok( false, "Teardown failed on " + this.name + ": " + e.message );
+ }
+ },
+ finish: function() {
+ if ( this.expected && this.expected != this.assertions.length ) {
+ QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );
+ }
+
+ var good = 0, bad = 0,
+ tests = id("qunit-tests");
- synchronize(function() {
- if ( config.currentModule ) {
- QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );
+ config.stats.all += this.assertions.length;
+ config.moduleStats.all += this.assertions.length;
+
+ if ( tests ) {
+ var ol = document.createElement("ol");
+
+ for ( var i = 0; i < this.assertions.length; i++ ) {
+ var assertion = this.assertions[i];
+
+ var li = document.createElement("li");
+ li.className = assertion.result ? "pass" : "fail";
+ li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed");
+ ol.appendChild( li );
+
+ if ( assertion.result ) {
+ good++;
+ } else {
+ bad++;
+ config.stats.bad++;
+ config.moduleStats.bad++;
+ }
}
- config.currentModule = name;
- config.moduleTestEnvironment = testEnvironment;
- config.moduleStats = { all: 0, bad: 0 };
+ // store result when possible
+ defined.sessionStorage && sessionStorage.setItem("qunit-" + this.testName, bad);
- QUnit.moduleStart( name, testEnvironment );
+ if (bad == 0) {
+ ol.style.display = "none";
+ }
+
+ var b = document.createElement("strong");
+ b.innerHTML = this.name + " (" + bad + " , " + good + " , " + this.assertions.length + ") ";
+
+ addEvent(b, "click", function() {
+ var next = b.nextSibling, display = next.style.display;
+ next.style.display = display === "none" ? "block" : "none";
+ });
+
+ addEvent(b, "dblclick", function(e) {
+ var target = e && e.target ? e.target : window.event.srcElement;
+ if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
+ target = target.parentNode;
+ }
+ if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
+ window.location.search = "?" + encodeURIComponent(getText([target]).replace(/\(.+\)$/, "").replace(/(^\s*|\s*$)/g, ""));
+ }
+ });
+
+ var li = id(this.id);
+ li.className = bad ? "fail" : "pass";
+ li.style.display = resultDisplayStyle(!bad);
+ li.removeChild( li.firstChild );
+ li.appendChild( b );
+ li.appendChild( ol );
+
+ if ( bad ) {
+ var toolbar = id("qunit-testrunner-toolbar");
+ if ( toolbar ) {
+ toolbar.style.display = "block";
+ id("qunit-filter-pass").disabled = null;
+ }
+ }
+
+ } else {
+ for ( var i = 0; i < this.assertions.length; i++ ) {
+ if ( !this.assertions[i].result ) {
+ bad++;
+ config.stats.bad++;
+ config.moduleStats.bad++;
+ }
+ }
+ }
+
+ try {
+ QUnit.reset();
+ } catch(e) {
+ // TODO use testName instead of name for no-markup message?
+ fail("reset() failed, following Test " + this.name + ", exception and reset fn follows", e, QUnit.reset);
+ }
+
+ QUnit.testDone( this.testName, bad, this.assertions.length );
+ },
+
+ queue: function() {
+ var test = this;
+ synchronize(function() {
+ test.init();
});
+ function run() {
+ // each of these can by async
+ synchronize(function() {
+ test.setup();
+ });
+ synchronize(function() {
+ test.run();
+ });
+ synchronize(function() {
+ test.teardown();
+ });
+ synchronize(function() {
+ test.finish();
+ });
+ }
+ // defer when previous test run passed, if storage is available
+ var bad = defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.testName);
+ if (bad) {
+ run();
+ } else {
+ synchronize(run);
+ };
+ }
+
+}
+
+var QUnit = {
+
+ // call on start of module test to prepend name to all tests
+ module: function(name, testEnvironment) {
+ config.currentModule = name;
+ config.currentModuleTestEnviroment = testEnvironment;
},
asyncTest: function(testName, expected, callback) {
@@ -70,7 +249,7 @@ var QUnit = {
},
test: function(testName, expected, callback, async) {
- var name = testName, testEnvironment, testEnvironmentArg;
+ var name = '' + testName + ' ', testEnvironmentArg;
if ( arguments.length === 2 ) {
callback = expected;
@@ -83,178 +262,24 @@ var QUnit = {
}
if ( config.currentModule ) {
- name = config.currentModule + " module: " + name;
+ name = '' + config.currentModule + " : " + name;
}
- if ( !validTest(name) ) {
+ if ( !validTest(config.currentModule + ": " + testName) ) {
return;
}
-
- synchronize(function() {
- QUnit.testStart( testName );
-
- testEnvironment = extend({
- setup: function() {},
- teardown: function() {}
- }, config.moduleTestEnvironment);
- if (testEnvironmentArg) {
- extend(testEnvironment,testEnvironmentArg);
- }
-
- // allow utility functions to access the current test environment
- QUnit.current_testEnvironment = testEnvironment;
-
- config.assertions = [];
- config.expected = expected;
-
- try {
- if ( !config.pollution ) {
- saveGlobal();
- }
-
- testEnvironment.setup.call(testEnvironment);
- } catch(e) {
- QUnit.ok( false, "Setup failed on " + name + ": " + e.message );
- }
-
- if ( async ) {
- QUnit.stop();
- }
-
- try {
- callback.call(testEnvironment);
- } catch(e) {
- fail("Test " + name + " died, exception and test follows", e, callback);
- QUnit.ok( false, "Died on test #" + (config.assertions.length + 1) + ": " + e.message );
- // else next test will carry the responsibility
- saveGlobal();
-
- // Restart the tests if they're blocking
- if ( config.blocking ) {
- start();
- }
- }
- });
-
- synchronize(function() {
- try {
- checkPollution();
- testEnvironment.teardown.call(testEnvironment);
- } catch(e) {
- QUnit.ok( false, "Teardown failed on " + name + ": " + e.message );
- }
-
- try {
- QUnit.reset();
- } catch(e) {
- fail("reset() failed, following Test " + name + ", exception and reset fn follows", e, reset);
- }
-
- if ( config.expected && config.expected != config.assertions.length ) {
- QUnit.ok( false, "Expected " + config.expected + " assertions, but " + config.assertions.length + " were run" );
- }
-
- var good = 0, bad = 0,
- tests = id("qunit-tests");
-
- config.stats.all += config.assertions.length;
- config.moduleStats.all += config.assertions.length;
-
- if ( tests ) {
- var ol = document.createElement("ol");
- ol.style.display = "none";
-
- for ( var i = 0; i < config.assertions.length; i++ ) {
- var assertion = config.assertions[i];
-
- var li = document.createElement("li");
- li.className = assertion.result ? "pass" : "fail";
- li.appendChild(document.createTextNode(assertion.message || "(no message)"));
- ol.appendChild( li );
-
- if ( assertion.result ) {
- good++;
- } else {
- bad++;
- config.stats.bad++;
- config.moduleStats.bad++;
- }
- }
-
- var b = document.createElement("strong");
- b.innerHTML = name + " (" + bad + " , " + good + " , " + config.assertions.length + ") ";
-
- addEvent(b, "click", function() {
- var next = b.nextSibling, display = next.style.display;
- next.style.display = display === "none" ? "block" : "none";
- });
-
- addEvent(b, "dblclick", function(e) {
- var target = e && e.target ? e.target : window.event.srcElement;
- if ( target.nodeName.toLowerCase() === "strong" ) {
- var text = "", node = target.firstChild;
-
- while ( node.nodeType === 3 ) {
- text += node.nodeValue;
- node = node.nextSibling;
- }
-
- text = text.replace(/(^\s*|\s*$)/g, "");
-
- if ( window.location ) {
- window.location.href = window.location.href.match(/^(.+?)(\?.*)?$/)[1] + "?" + encodeURIComponent(text);
- }
- }
- });
-
- var li = document.createElement("li");
- li.className = bad ? "fail" : "pass";
- li.appendChild( b );
- li.appendChild( ol );
- tests.appendChild( li );
-
- if ( bad ) {
- var toolbar = id("qunit-testrunner-toolbar");
- if ( toolbar ) {
- toolbar.style.display = "block";
- id("qunit-filter-pass").disabled = null;
- id("qunit-filter-missing").disabled = null;
- }
- }
-
- } else {
- for ( var i = 0; i < config.assertions.length; i++ ) {
- if ( !config.assertions[i].result ) {
- bad++;
- config.stats.bad++;
- config.moduleStats.bad++;
- }
- }
- }
-
- QUnit.testDone( testName, bad, config.assertions.length );
-
- if ( !window.setTimeout && !config.queue.length ) {
- done();
- }
- });
-
- if ( window.setTimeout && !config.doneTimer ) {
- config.doneTimer = window.setTimeout(function(){
- if ( !config.queue.length ) {
- done();
- } else {
- synchronize( done );
- }
- }, 13);
- }
+
+ var test = new Test(name, testName, expected, testEnvironmentArg, async, callback);
+ test.module = config.currentModule;
+ test.moduleTestEnvironment = config.currentModuleTestEnviroment;
+ test.queue();
},
/**
* Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
*/
expect: function(asserts) {
- config.expected = asserts;
+ config.current.expected = asserts;
},
/**
@@ -262,10 +287,15 @@ var QUnit = {
* @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
*/
ok: function(a, msg) {
- QUnit.log(a, msg);
-
- config.assertions.push({
- result: !!a,
+ a = !!a;
+ var details = {
+ result: a,
+ message: msg
+ };
+ msg = escapeHtml(msg);
+ QUnit.log(a, msg, details);
+ config.current.assertions.push({
+ result: a,
message: msg
});
},
@@ -283,32 +313,74 @@ var QUnit = {
* @param String message (optional)
*/
equal: function(actual, expected, message) {
- push(expected == actual, actual, expected, message);
+ QUnit.push(expected == actual, actual, expected, message);
},
notEqual: function(actual, expected, message) {
- push(expected != actual, actual, expected, message);
+ QUnit.push(expected != actual, actual, expected, message);
},
- deepEqual: function(a, b, message) {
- push(QUnit.equiv(a, b), a, b, message);
+ deepEqual: function(actual, expected, message) {
+ QUnit.push(QUnit.equiv(actual, expected), actual, expected, message);
},
- notDeepEqual: function(a, b, message) {
- push(!QUnit.equiv(a, b), a, b, message);
+ notDeepEqual: function(actual, expected, message) {
+ QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message);
},
strictEqual: function(actual, expected, message) {
- push(expected === actual, actual, expected, message);
+ QUnit.push(expected === actual, actual, expected, message);
},
notStrictEqual: function(actual, expected, message) {
- push(expected !== actual, actual, expected, message);
+ QUnit.push(expected !== actual, actual, expected, message);
},
+
+ raises: function(block, expected, message) {
+ var actual, ok = false;
+ if (typeof expected === 'string') {
+ message = expected;
+ expected = null;
+ }
+
+ try {
+ block();
+ } catch (e) {
+ actual = e;
+ }
+
+ if (actual) {
+ // we don't want to validate thrown error
+ if (!expected) {
+ ok = true;
+ // expected is a regexp
+ } else if (QUnit.objectType(expected) === "regexp") {
+ ok = expected.test(actual);
+ // expected is a constructor
+ } else if (actual instanceof expected) {
+ ok = true;
+ // expected is a validation function which returns true is validation passed
+ } else if (expected.call({}, actual) === true) {
+ ok = true;
+ }
+ }
+
+ QUnit.ok(ok, message);
+ },
+
start: function() {
+ config.semaphore--;
+ if (config.semaphore > 0) {
+ // don't start until equal number of stop-calls
+ return;
+ }
+ if (config.semaphore < 0) {
+ // ignore if start is called more often then stop
+ config.semaphore = 0;
+ }
// A slight delay, to avoid any current callbacks
- if ( window.setTimeout ) {
+ if ( defined.setTimeout ) {
window.setTimeout(function() {
if ( config.timeout ) {
clearTimeout(config.timeout);
@@ -324,59 +396,18 @@ var QUnit = {
},
stop: function(timeout) {
+ config.semaphore++;
config.blocking = true;
- if ( timeout && window.setTimeout ) {
+ if ( timeout && defined.setTimeout ) {
+ clearTimeout(config.timeout);
config.timeout = window.setTimeout(function() {
QUnit.ok( false, "Test timed out" );
QUnit.start();
}, timeout);
}
- },
-
- /**
- * Resets the test setup. Useful for tests that modify the DOM.
- */
- reset: function() {
- if ( window.jQuery ) {
- jQuery("#main").html( config.fixture );
- jQuery.event.global = {};
- jQuery.ajaxSettings = extend({}, config.ajaxSettings);
- }
- },
-
- /**
- * Trigger an event on an element.
- *
- * @example triggerEvent( document.body, "click" );
- *
- * @param DOMElement elem
- * @param String type
- */
- triggerEvent: function( elem, type, event ) {
- if ( document.createEvent ) {
- event = document.createEvent("MouseEvents");
- event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
- 0, 0, 0, 0, 0, false, false, false, false, 0, null);
- elem.dispatchEvent( event );
+ }
- } else if ( elem.fireEvent ) {
- elem.fireEvent("on"+type);
- }
- },
-
- // Safe object type checking
- is: function( type, obj ) {
- return Object.prototype.toString.call( obj ) === "[object "+ type +"]";
- },
-
- // Logging callbacks
- done: function(failures, total) {},
- log: function(result, message) {},
- testStart: function(name) {},
- testDone: function(name, failures, total) {},
- moduleStart: function(name, testEnvironment) {},
- moduleDone: function(name, failures, total) {}
};
// Backwards compatibility, deprecated
@@ -426,11 +457,168 @@ if ( typeof exports === "undefined" || typeof require === "undefined" ) {
exports.QUnit = QUnit;
}
+// define these after exposing globals to keep them in these QUnit namespace only
+extend(QUnit, {
+ config: config,
+
+ // Initialize the configuration options
+ init: function() {
+ extend(config, {
+ stats: { all: 0, bad: 0 },
+ moduleStats: { all: 0, bad: 0 },
+ started: +new Date,
+ updateRate: 1000,
+ blocking: false,
+ autostart: true,
+ autorun: false,
+ filters: [],
+ queue: [],
+ semaphore: 0
+ });
+
+ var tests = id("qunit-tests"),
+ banner = id("qunit-banner"),
+ result = id("qunit-testresult");
+
+ if ( tests ) {
+ tests.innerHTML = "";
+ }
+
+ if ( banner ) {
+ banner.className = "";
+ }
+
+ if ( result ) {
+ result.parentNode.removeChild( result );
+ }
+ },
+
+ /**
+ * Resets the test setup. Useful for tests that modify the DOM.
+ *
+ * If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
+ */
+ reset: function() {
+ if ( window.jQuery ) {
+ jQuery( "#main, #qunit-fixture" ).html( config.fixture );
+ } else {
+ var main = id( 'main' ) || id( 'qunit-fixture' );
+ if ( main ) {
+ main.innerHTML = config.fixture;
+ }
+ }
+ },
+
+ /**
+ * Trigger an event on an element.
+ *
+ * @example triggerEvent( document.body, "click" );
+ *
+ * @param DOMElement elem
+ * @param String type
+ */
+ triggerEvent: function( elem, type, event ) {
+ if ( document.createEvent ) {
+ event = document.createEvent("MouseEvents");
+ event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
+ 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ elem.dispatchEvent( event );
+
+ } else if ( elem.fireEvent ) {
+ elem.fireEvent("on"+type);
+ }
+ },
+
+ // Safe object type checking
+ is: function( type, obj ) {
+ return QUnit.objectType( obj ) == type;
+ },
+
+ objectType: function( obj ) {
+ if (typeof obj === "undefined") {
+ return "undefined";
+
+ // consider: typeof null === object
+ }
+ if (obj === null) {
+ return "null";
+ }
+
+ var type = Object.prototype.toString.call( obj )
+ .match(/^\[object\s(.*)\]$/)[1] || '';
+
+ switch (type) {
+ case 'Number':
+ if (isNaN(obj)) {
+ return "nan";
+ } else {
+ return "number";
+ }
+ case 'String':
+ case 'Boolean':
+ case 'Array':
+ case 'Date':
+ case 'RegExp':
+ case 'Function':
+ return type.toLowerCase();
+ }
+ if (typeof obj === "object") {
+ return "object";
+ }
+ return undefined;
+ },
+
+ push: function(result, actual, expected, message) {
+ var details = {
+ result: result,
+ message: message,
+ actual: actual,
+ expected: expected
+ };
+
+ message = escapeHtml(message) || (result ? "okay" : "failed");
+ message = '' + message + " ";
+ expected = escapeHtml(QUnit.jsDump.parse(expected));
+ actual = escapeHtml(QUnit.jsDump.parse(actual));
+ var output = message + 'Expected: ' + expected + ' ';
+ if (actual != expected) {
+ output += 'Result: ' + actual + ' ';
+ output += 'Diff: ' + QUnit.diff(expected, actual) +' ';
+ }
+ if (!result) {
+ var source = sourceFromStacktrace();
+ if (source) {
+ details.source = source;
+ output += 'Source: ' + source +' ';
+ }
+ }
+ output += "
";
+
+ QUnit.log(result, message, details);
+
+ config.current.assertions.push({
+ result: !!result,
+ message: output
+ });
+ },
+
+ // Logging callbacks
+ begin: function() {},
+ done: function(failures, total) {},
+ log: function(result, message) {},
+ testStart: function(name, testEnvironment) {},
+ testDone: function(name, failures, total) {},
+ moduleStart: function(name, testEnvironment) {},
+ moduleDone: function(name, failures, total) {}
+});
+
if ( typeof document === "undefined" || document.readyState === "complete" ) {
config.autorun = true;
}
addEvent(window, "load", function() {
+ QUnit.begin();
+
// Initialize the config, saving the execution queue
var oldconfig = extend({}, config);
QUnit.init();
@@ -442,6 +630,19 @@ addEvent(window, "load", function() {
if ( userAgent ) {
userAgent.innerHTML = navigator.userAgent;
}
+ var banner = id("qunit-header");
+ if ( banner ) {
+ var paramsIndex = location.href.lastIndexOf(location.search);
+ if ( paramsIndex > -1 ) {
+ var mainPageLocation = location.href.slice(0, paramsIndex);
+ if ( mainPageLocation == location.href ) {
+ banner.innerHTML = ' ' + banner.innerHTML + ' ';
+ } else {
+ var testName = decodeURIComponent(location.search.slice(1));
+ banner.innerHTML = '' + banner.innerHTML + ' › ' + testName + ' ';
+ }
+ }
+ }
var toolbar = id("qunit-testrunner-toolbar");
if ( toolbar ) {
@@ -465,57 +666,19 @@ addEvent(window, "load", function() {
label.setAttribute("for", "qunit-filter-pass");
label.innerHTML = "Hide passed tests";
toolbar.appendChild( label );
-
- var missing = document.createElement("input");
- missing.type = "checkbox";
- missing.id = "qunit-filter-missing";
- missing.disabled = true;
- addEvent( missing, "click", function() {
- var li = document.getElementsByTagName("li");
- for ( var i = 0; i < li.length; i++ ) {
- if ( li[i].className.indexOf("fail") > -1 && li[i].innerHTML.indexOf('missing test - untested code is broken code') > - 1 ) {
- li[i].parentNode.parentNode.style.display = missing.checked ? "none" : "block";
- }
- }
- });
- toolbar.appendChild( missing );
-
- label = document.createElement("label");
- label.setAttribute("for", "qunit-filter-missing");
- label.innerHTML = "Hide missing tests (untested code is broken code)";
- toolbar.appendChild( label );
}
- var main = id('main');
+ var main = id('main') || id('qunit-fixture');
if ( main ) {
config.fixture = main.innerHTML;
}
- if ( window.jQuery ) {
- config.ajaxSettings = window.jQuery.ajaxSettings;
+ if (config.autostart) {
+ QUnit.start();
}
-
- QUnit.start();
});
function done() {
- if ( config.doneTimer && window.clearTimeout ) {
- window.clearTimeout( config.doneTimer );
- config.doneTimer = null;
- }
-
- if ( config.queue.length ) {
- config.doneTimer = window.setTimeout(function(){
- if ( !config.queue.length ) {
- done();
- } else {
- synchronize( done );
- }
- }, 13);
-
- return;
- }
-
config.autorun = true;
// Log the last module results
@@ -577,9 +740,41 @@ function validTest( name ) {
return run;
}
-function push(result, actual, expected, message) {
- message = message || (result ? "okay" : "failed");
- QUnit.ok( result, result ? message + ": " + QUnit.jsDump.parse(expected) : message + ", expected: " + QUnit.jsDump.parse(expected) + " result: " + QUnit.jsDump.parse(actual) );
+// so far supports only Firefox, Chrome and Opera (buggy)
+// could be extended in the future to use something like https://github.com/csnover/TraceKit
+function sourceFromStacktrace() {
+ try {
+ throw new Error();
+ } catch ( e ) {
+ if (e.stacktrace) {
+ // Opera
+ return e.stacktrace.split("\n")[6];
+ } else if (e.stack) {
+ // Firefox, Chrome
+ return e.stack.split("\n")[4];
+ }
+ }
+}
+
+function resultDisplayStyle(passed) {
+ return passed && id("qunit-filter-pass") && id("qunit-filter-pass").checked ? 'none' : '';
+}
+
+function escapeHtml(s) {
+ if (!s) {
+ return "";
+ }
+ s = s + "";
+ return s.replace(/[\&"<>\\]/g, function(s) {
+ switch(s) {
+ case "&": return "&";
+ case "\\": return "\\\\";
+ case '"': return '\"';
+ case "<": return "<";
+ case ">": return ">";
+ default: return s;
+ }
+ });
}
function synchronize( callback ) {
@@ -596,12 +791,14 @@ function process() {
while ( config.queue.length && !config.blocking ) {
if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {
config.queue.shift()();
-
} else {
- setTimeout( process, 13 );
+ window.setTimeout( process, 13 );
break;
}
}
+ if (!config.blocking && !config.queue.length) {
+ done();
+ }
}
function saveGlobal() {
@@ -621,13 +818,13 @@ function checkPollution( name ) {
var newGlobals = diff( old, config.pollution );
if ( newGlobals.length > 0 ) {
ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
- config.expected++;
+ config.current.expected++;
}
var deletedGlobals = diff( config.pollution, old );
if ( deletedGlobals.length > 0 ) {
ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
- config.expected++;
+ config.current.expected++;
}
}
@@ -690,60 +887,11 @@ QUnit.equiv = function () {
var callers = []; // stack to decide between skip/abort functions
var parents = []; // stack to avoiding loops from circular referencing
-
- // Determine what is o.
- function hoozit(o) {
- if (QUnit.is("String", o)) {
- return "string";
-
- } else if (QUnit.is("Boolean", o)) {
- return "boolean";
-
- } else if (QUnit.is("Number", o)) {
-
- if (isNaN(o)) {
- return "nan";
- } else {
- return "number";
- }
-
- } else if (typeof o === "undefined") {
- return "undefined";
-
- // consider: typeof null === object
- } else if (o === null) {
- return "null";
-
- // consider: typeof [] === object
- } else if (QUnit.is( "Array", o)) {
- return "array";
-
- // consider: typeof new Date() === object
- } else if (QUnit.is( "Date", o)) {
- return "date";
-
- // consider: /./ instanceof Object;
- // /./ instanceof RegExp;
- // typeof /./ === "function"; // => false in IE and Opera,
- // true in FF and Safari
- } else if (QUnit.is( "RegExp", o)) {
- return "regexp";
-
- } else if (typeof o === "object") {
- return "object";
-
- } else if (QUnit.is( "Function", o)) {
- return "function";
- } else {
- return undefined;
- }
- }
-
// Call the o related callback with the given arguments.
function bindCallbacks(o, callbacks, args) {
- var prop = hoozit(o);
+ var prop = QUnit.objectType(o);
if (prop) {
- if (hoozit(callbacks[prop]) === "function") {
+ if (QUnit.objectType(callbacks[prop]) === "function") {
return callbacks[prop].apply(callbacks, args);
} else {
return callbacks[prop]; // or undefined
@@ -777,11 +925,11 @@ QUnit.equiv = function () {
},
"date": function (b, a) {
- return hoozit(b) === "date" && a.valueOf() === b.valueOf();
+ return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf();
},
"regexp": function (b, a) {
- return hoozit(b) === "regexp" &&
+ return QUnit.objectType(b) === "regexp" &&
a.source === b.source && // the regex itself
a.global === b.global && // and its modifers (gmi) ...
a.ignoreCase === b.ignoreCase &&
@@ -802,7 +950,7 @@ QUnit.equiv = function () {
var len;
// b could be an object literal here
- if ( ! (hoozit(b) === "array")) {
+ if ( ! (QUnit.objectType(b) === "array")) {
return false;
}
@@ -880,7 +1028,7 @@ QUnit.equiv = function () {
return (function (a, b) {
if (a === b) {
return true; // catch the most you can
- } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || hoozit(a) !== hoozit(b)) {
+ } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || QUnit.objectType(a) !== QUnit.objectType(b)) {
return false; // don't lose time with error prone cases
} else {
return bindCallbacks(a, callbacks, [b, a]);
@@ -953,7 +1101,7 @@ QUnit.jsDump = (function() {
type = "date";
} else if (QUnit.is("Function", obj)) {
type = "function";
- } else if (obj.setInterval && obj.document && !obj.nodeType) {
+ } else if (typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined") {
type = "window";
} else if (obj.nodeType === 9) {
type = "document";
@@ -1007,31 +1155,31 @@ QUnit.jsDump = (function() {
ret += ' ' + name;
ret += '(';
- ret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join('');
- return join( ret, this.parse(fn,'functionCode'), '}' );
+ ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join('');
+ return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' );
},
array: array,
nodelist: array,
arguments: array,
object:function( map ) {
var ret = [ ];
- this.up();
+ QUnit.jsDump.up();
for ( var key in map )
- ret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) );
- this.down();
+ ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(map[key]) );
+ QUnit.jsDump.down();
return join( '{', ret, '}' );
},
node:function( node ) {
- var open = this.HTML ? '<' : '<',
- close = this.HTML ? '>' : '>';
+ var open = QUnit.jsDump.HTML ? '<' : '<',
+ close = QUnit.jsDump.HTML ? '>' : '>';
var tag = node.nodeName.toLowerCase(),
ret = open + tag;
- for ( var a in this.DOMAttrs ) {
- var val = node[this.DOMAttrs[a]];
+ for ( var a in QUnit.jsDump.DOMAttrs ) {
+ var val = node[QUnit.jsDump.DOMAttrs[a]];
if ( val )
- ret += ' ' + a + '=' + this.parse( val, 'attribute' );
+ ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' );
}
return ret + close + open + '/' + tag + close;
},
@@ -1059,11 +1207,168 @@ QUnit.jsDump = (function() {
'class':'className'
},
HTML:false,//if true, entities are escaped ( <, >, \t, space and \n )
- indentChar:' ',//indentation unit
- multiline:false //if true, items in a collection, are separated by a \n, else just a space.
+ indentChar:' ',//indentation unit
+ multiline:true //if true, items in a collection, are separated by a \n, else just a space.
};
return jsDump;
})();
+// from Sizzle.js
+function getText( elems ) {
+ var ret = "", elem;
+
+ for ( var i = 0; elems[i]; i++ ) {
+ elem = elems[i];
+
+ // Get the text from text nodes and CDATA nodes
+ if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
+ ret += elem.nodeValue;
+
+ // Traverse everything else, except comment nodes
+ } else if ( elem.nodeType !== 8 ) {
+ ret += getText( elem.childNodes );
+ }
+ }
+
+ return ret;
+};
+
+/*
+ * Javascript Diff Algorithm
+ * By John Resig (http://ejohn.org/)
+ * Modified by Chu Alan "sprite"
+ *
+ * Released under the MIT license.
+ *
+ * More Info:
+ * http://ejohn.org/projects/javascript-diff-algorithm/
+ *
+ * Usage: QUnit.diff(expected, actual)
+ *
+ * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick brown fox jumped jumps over"
+ */
+QUnit.diff = (function() {
+ function diff(o, n){
+ var ns = new Object();
+ var os = new Object();
+
+ for (var i = 0; i < n.length; i++) {
+ if (ns[n[i]] == null)
+ ns[n[i]] = {
+ rows: new Array(),
+ o: null
+ };
+ ns[n[i]].rows.push(i);
+ }
+
+ for (var i = 0; i < o.length; i++) {
+ if (os[o[i]] == null)
+ os[o[i]] = {
+ rows: new Array(),
+ n: null
+ };
+ os[o[i]].rows.push(i);
+ }
+
+ for (var i in ns) {
+ if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) {
+ n[ns[i].rows[0]] = {
+ text: n[ns[i].rows[0]],
+ row: os[i].rows[0]
+ };
+ o[os[i].rows[0]] = {
+ text: o[os[i].rows[0]],
+ row: ns[i].rows[0]
+ };
+ }
+ }
+
+ for (var i = 0; i < n.length - 1; i++) {
+ if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null &&
+ n[i + 1] == o[n[i].row + 1]) {
+ n[i + 1] = {
+ text: n[i + 1],
+ row: n[i].row + 1
+ };
+ o[n[i].row + 1] = {
+ text: o[n[i].row + 1],
+ row: i + 1
+ };
+ }
+ }
+
+ for (var i = n.length - 1; i > 0; i--) {
+ if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null &&
+ n[i - 1] == o[n[i].row - 1]) {
+ n[i - 1] = {
+ text: n[i - 1],
+ row: n[i].row - 1
+ };
+ o[n[i].row - 1] = {
+ text: o[n[i].row - 1],
+ row: i - 1
+ };
+ }
+ }
+
+ return {
+ o: o,
+ n: n
+ };
+ }
+
+ return function(o, n){
+ o = o.replace(/\s+$/, '');
+ n = n.replace(/\s+$/, '');
+ var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/));
+
+ var str = "";
+
+ var oSpace = o.match(/\s+/g);
+ if (oSpace == null) {
+ oSpace = [" "];
+ }
+ else {
+ oSpace.push(" ");
+ }
+ var nSpace = n.match(/\s+/g);
+ if (nSpace == null) {
+ nSpace = [" "];
+ }
+ else {
+ nSpace.push(" ");
+ }
+
+ if (out.n.length == 0) {
+ for (var i = 0; i < out.o.length; i++) {
+ str += '' + out.o[i] + oSpace[i] + "";
+ }
+ }
+ else {
+ if (out.n[0].text == null) {
+ for (n = 0; n < out.o.length && out.o[n].text == null; n++) {
+ str += '' + out.o[n] + oSpace[n] + "";
+ }
+ }
+
+ for (var i = 0; i < out.n.length; i++) {
+ if (out.n[i].text == null) {
+ str += '' + out.n[i] + nSpace[i] + " ";
+ }
+ else {
+ var pre = "";
+
+ for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) {
+ pre += '' + out.o[n] + oSpace[n] + "";
+ }
+ str += " " + out.n[i].text + nSpace[i] + pre;
+ }
+ }
+ }
+
+ return str;
+ };
+})();
+
})(this);
From 520368efccbbc9e22626b342c5851205445dd861 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?=
Date: Wed, 12 Jan 2011 01:06:29 +0100
Subject: [PATCH 056/364] significant refactoring in tests
Changes:
- use "#qunit-fixture" container instead of "#fixtures"
- use `equal()` instead of deprecated `equals()`
- use `asyncTest()` for tests that called `stop()` immediately
- stop using 'expect()'; provide expected number by test definition args
- trash convoluted tests implemented to run in iframes
- prevent forms from navigating away from the test suite by submitting
them async in an iframe and publishing a custom event ("iframe:loaded")
- intelligent undelegating of `live` handlers in App.teardown
---
test/public/test/call-remote-callbacks.js | 23 ++++----
test/public/test/call-remote.js | 50 ++++++-----------
test/public/test/data-confirm.js | 28 +++-------
test/public/test/data-disable.js | 22 ++++----
test/public/test/data-method-iframe.js | 30 ----------
test/public/test/data-method.js | 68 ++++++++++-------------
test/public/test/data-remote.js | 58 +++++++------------
test/public/test/settings.js | 36 +++++++-----
test/server.rb | 35 +++++-------
test/views/iframe.erb | 9 ---
test/views/index.erb | 22 ++------
test/views/layout.erb | 13 ++---
12 files changed, 139 insertions(+), 255 deletions(-)
delete mode 100644 test/public/test/data-method-iframe.js
delete mode 100644 test/views/iframe.erb
diff --git a/test/public/test/call-remote-callbacks.js b/test/public/test/call-remote-callbacks.js
index 9cdb7a12..86c010ab 100644
--- a/test/public/test/call-remote-callbacks.js
+++ b/test/public/test/call-remote-callbacks.js
@@ -2,7 +2,7 @@
module('call-remote-callbacks', {
setup: function() {
- $('#fixtures').append($('', {
+ $('#qunit-fixture').append($('', {
action: '/echo', method: 'get', 'data-remote': 'true'
}));
},
@@ -10,8 +10,6 @@ module('call-remote-callbacks', {
});
function submit(fn) {
- stop(App.ajax_timeout);
-
var form = $('form')
.bind('ajax:complete', function(){
ok(true, 'ajax:complete');
@@ -22,10 +20,12 @@ function submit(fn) {
form.trigger('submit');
}
-test('stopping the "ajax:beforeSend" event aborts the request', function() {
- expect(0);
+asyncTest('stopping the "ajax:beforeSend" event aborts the request', 1, function() {
submit(function(form) {
- form.bind('ajax:beforeSend', function() { return false });
+ form.bind('ajax:beforeSend', function() {
+ ok(true, 'aborting request in ajax:beforeSend')
+ return false;
+ });
form.unbind('ajax:complete').bind('ajax:complete', function() {
ok(false, 'ajax:complete should not run');
});
@@ -33,8 +33,7 @@ test('stopping the "ajax:beforeSend" event aborts the request', function() {
setTimeout(function(){ start() }, 200);
});
-test('"ajax:beforeSend" can be observed and stopped with event delegation', function() {
- expect(1);
+asyncTest('"ajax:beforeSend" can be observed and stopped with event delegation', 1, function() {
$('form[data-remote]').live('ajax:beforeSend', function() {
ok(true, 'ajax:beforeSend observed with event delegation');
return false;
@@ -48,23 +47,21 @@ test('"ajax:beforeSend" can be observed and stopped with event delegation', func
setTimeout(function(){ start() }, 200);
});
-test('"ajax:beforeSend", "ajax:success" and "ajax:complete" are triggered', function() {
- expect(3);
+asyncTest('"ajax:beforeSend", "ajax:success" and "ajax:complete" are triggered', 3, function() {
submit(function(form) {
form.bind('ajax:beforeSend', function(arg) { ok(true, 'ajax:beforeSend') });
form.bind('ajax:success', function(arg) { ok(true, 'ajax:success') });
});
});
-test('"ajax:beforeSend", "ajax:error" and "ajax:complete" are triggered on error', function() {
- expect(4);
+asyncTest('"ajax:beforeSend", "ajax:error" and "ajax:complete" are triggered on error', 4, function() {
submit(function(form) {
form.attr('action', '/error');
form.bind('ajax:beforeSend', function(arg) { ok(true, 'ajax:beforeSend') });
form.bind('ajax:error', function(e, xhr, status, error) {
ok(true, 'ajax:error');
// Opera returns "0" for HTTP code
- equals(xhr.status, window.opera ? 0 : 403, 'status code should be 403');
+ equal(xhr.status, window.opera ? 0 : 403, 'status code should be 403');
});
});
});
diff --git a/test/public/test/call-remote.js b/test/public/test/call-remote.js
index d396a2bf..9634b124 100644
--- a/test/public/test/call-remote.js
+++ b/test/public/test/call-remote.js
@@ -3,15 +3,8 @@
function build_form(attrs) {
attrs = $.extend({ action: '/echo', 'data-remote': 'true' }, attrs);
- $('#fixtures').append($('', attrs));
-
- $('#fixtures form').append($(' ', {
- id: 'user_name',
- type: 'text',
- size: '30',
- 'name': 'user_name',
- 'value': 'john'
- }));
+ $('#qunit-fixture').append($('', attrs))
+ .find('form').append($(' '));
};
module('call-remote', {
@@ -19,77 +12,68 @@ module('call-remote', {
});
function submit(fn) {
- stop(App.ajax_timeout);
-
$('form[data-remote]')
.live('ajax:success', fn)
.live('ajax:complete', function() { start() })
.trigger('submit');
}
-test('form method is read from "method" and not from "data-method"', function() {
- expect(1);
+asyncTest('form method is read from "method" and not from "data-method"', 1, function() {
build_form({ method: 'post', 'data-method': 'get' });
submit(function(e, data, status, xhr) {
- App.assert_post_request(data.request_env);
+ App.assert_post_request(data);
});
});
-test('form method is not read from "data-method" attribute in case of missing "method"', function() {
- expect(1);
+asyncTest('form method is not read from "data-method" attribute in case of missing "method"', 1, function() {
build_form({ 'data-method': 'put' });
submit(function(e, data, status, xhr) {
- App.assert_get_request(data.request_env);
+ App.assert_get_request(data);
});
});
-test('form default method is GET', function() {
- expect(1);
+asyncTest('form default method is GET', 1, function() {
build_form();
submit(function(e, data, status, xhr) {
- App.assert_get_request(data.request_env);
+ App.assert_get_request(data);
});
});
-test('form url is picked up from "action"', function() {
- expect(1);
+asyncTest('form url is picked up from "action"', 1, function() {
build_form({ method: 'post' });
submit(function(e, data, status, xhr) {
- App.assert_request_path(data.request_env, '/echo');
+ App.assert_request_path(data, '/echo');
});
});
-test('form url is read from "action" not "href"', function() {
- expect(1);
+asyncTest('form url is read from "action" not "href"', 1, function() {
build_form({ method: 'post', href: '/echo2' });
submit(function(e, data, status, xhr) {
- App.assert_request_path(data.request_env, '/echo');
+ App.assert_request_path(data, '/echo');
});
});
-test('prefer JS, but accept any format', function() {
- expect(1);
+asyncTest('prefer JS, but accept any format', 1, function() {
build_form({ method: 'post' });
submit(function(e, data, status, xhr) {
- var accept = data.request_env['HTTP_ACCEPT'];
+ var accept = data.HTTP_ACCEPT;
// HACK to normalize header sent by jQuery 1.4.4 and below:
accept = accept.replace('*/*, */*', '*/*');
- equals(accept, '*/*;q=0.5, text/javascript, application/javascript');
+ equal(accept, '*/*;q=0.5, text/javascript, application/javascript');
});
});
-test('accept application/json if "data-type" is json', function() {
- expect(1);
+asyncTest('accept application/json if "data-type" is json', 1, function() {
build_form({ method: 'post', 'data-type': 'json' });
submit(function(e, data, status, xhr) {
- equals(data.request_env['HTTP_ACCEPT'], 'application/json, text/javascript, */*; q=0.01');
+ equal(data.HTTP_ACCEPT, 'application/json, text/javascript, */*; q=0.01');
});
});
diff --git a/test/public/test/data-confirm.js b/test/public/test/data-confirm.js
index 2ef61e55..fe05f559 100644
--- a/test/public/test/data-confirm.js
+++ b/test/public/test/data-confirm.js
@@ -1,35 +1,28 @@
module('data-confirm', {
-
- teardown: App.teardown,
-
setup: function() {
- $('#fixtures').append($(' ', {
+ $('#qunit-fixture').append($(' ', {
href: '/echo',
'data-remote': 'true',
'data-confirm': 'Are you absolutely sure?',
text: 'my social security number'
}));
- }
+ },
+ teardown: App.teardown
});
-test('clicking on a link with data-confirm attribute. Confirm yes.', function() {
- expect(4);
-
+asyncTest('clicking on a link with data-confirm attribute. Confirm yes.', 4, function() {
window.confirm = function(msg) {
$(document.body).data('confirmation-message', msg);
return true;
};
- stop(App.ajax_timeout);
-
$('a[data-confirm]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = data.request_env;
- App.assert_request_path(request_env, '/echo');
- App.assert_get_request(request_env);
+ App.assert_request_path(data, '/echo');
+ App.assert_get_request(data);
- equals( $(document.body).data('confirmation-message'),
+ equal( $(document.body).data('confirmation-message'),
App.confirmation_message,
'confirmation message should be same');
@@ -38,15 +31,12 @@ test('clicking on a link with data-confirm attribute. Confirm yes.', function()
.trigger('click');
});
-test('clicking on a link with data-confirm attribute. Confirm No.', function() {
- expect(1);
-
+asyncTest('clicking on a link with data-confirm attribute. Confirm No.', 1, function() {
window.confirm = function(msg) {
$(document.body).data('confirmation-message', msg);
return false;
};
- stop();
$('a[data-confirm]')
.live('ajax:before', function(e, data, status, xhr) {
App.assert_callback_not_invoked('ajax:before');
@@ -56,7 +46,7 @@ test('clicking on a link with data-confirm attribute. Confirm No.', function() {
// I don't have idea how to do it without timeout on "confirm: no", will need
// to think about that
setTimeout(function() {
- equals( $(document.body).data('confirmation-message'),
+ equal( $(document.body).data('confirmation-message'),
App.confirmation_message,
'confirmation message should be same');
diff --git a/test/public/test/data-disable.js b/test/public/test/data-disable.js
index 2311eb91..abe5f108 100644
--- a/test/public/test/data-disable.js
+++ b/test/public/test/data-disable.js
@@ -3,7 +3,7 @@ module('data-disable', {
teardown: App.teardown,
setup: function() {
- $('#fixtures').append($('', {
+ $('#qunit-fixture').append($('', {
action: '/echo',
'data-remote': 'true',
method: 'post'
@@ -18,7 +18,7 @@ module('data-disable', {
'value': 'john'
}));
- $('#fixtures').append($('', {
+ $('#qunit-fixture').append($('', {
action: '/echo',
method: 'post'
}));
@@ -34,36 +34,34 @@ module('data-disable', {
}
});
-test('triggering ajax callbacks on a form with data-disable attribute', function() {
- expect(6);
+test('triggering ajax callbacks on a form with data-disable attribute', 6, function() {
var form = $('form[data-remote]'), input = form.find('input[type=text]');
ok(!input.is(':disabled'), 'input field should not be disabled');
- equals(input.val(), 'john', 'input field should have value given to it');
+ equal(input.val(), 'john', 'input field should have value given to it');
form.trigger('ajax:beforeSend');
ok(input.is(':disabled'), 'input field should be disabled');
- equals(input.val(), 'processing ...', 'input field should have disabled value given to it');
+ equal(input.val(), 'processing ...', 'input field should have disabled value given to it');
form.trigger('ajax:complete');
ok(!input.is(':disabled'), 'input field should not be disabled');
- equals(input.val(), 'john', 'input field should have value given to it');
+ equal(input.val(), 'john', 'input field should have value given to it');
});
-test('clicking on non-ajax Submit input tag with data-disable-with attribute', function(){
- expect(4);
+test('clicking on non-ajax Submit input tag with data-disable-with attribute', 4, function(){
var form = $('form:not([data-remote])'), input = form.find('input[type=submit]');
ok(!input.is(':disabled'), 'input field should not be disabled');
- equals(input.val(), 'Submit', 'input field should have value given to it');
+ equal(input.val(), 'Submit', 'input field should have value given to it');
- $('form:not([data-remote])').live('submit', function(e) {
+ form.live('submit', function(e) {
// prevent the submit navigating away from the test suite
e.preventDefault();
}).trigger('submit');
ok(input.is(':disabled'), 'input field should not be disabled');
- equals(input.val(), 'submitting ...', 'input field should have disabled value given to it');
+ equal(input.val(), 'submitting ...', 'input field should have disabled value given to it');
});
diff --git a/test/public/test/data-method-iframe.js b/test/public/test/data-method-iframe.js
deleted file mode 100644
index ddb38b98..00000000
--- a/test/public/test/data-method-iframe.js
+++ /dev/null
@@ -1,30 +0,0 @@
-module('data-method-iframe', {
-
- teardown: App.teardown,
-
- setup: function() {
-
- $('#fixtures-iframe').append($(' ', {
- href: '/delete',
- 'data-method': 'delete',
- text: 'Destroy'
- }));
-
- }
-});
-
-test('clicking on a link with data-method attribute', function() {
-
- /*
- * There is nothing to verify here. The trigger clicks the link
- * which submits to /delete. The response given by /delete is asserted in
- * data-method-iframe.js
- */
- expect(0);
- stop();
-
- $('a[data-method]')
- .trigger('click');
-
- App.timeout();
-});
diff --git a/test/public/test/data-method.js b/test/public/test/data-method.js
index 73d907cf..5823bc4b 100644
--- a/test/public/test/data-method.js
+++ b/test/public/test/data-method.js
@@ -1,45 +1,35 @@
-module('data-method');
+(function(){
-test('clicking on a link with data-method attribute', function() {
- expect(1);
- stop(App.ajax_timeout);
-
- var iframe = $('#fixtures-iframe iframe');
-
- iframeCallback = function() {
- var data = iframe.contents().find('body').text();
- equals(data, "/delete was invoked with delete verb. params is {\"_method\"=>\"delete\"}", 'iframe should have proper response message');
-
- start();
- };
-
- //index.erb loads iframe.erb . Just wait for iframe to load and do its thing and then verify.
- if(iframe[0].loaded) {
- iframeCallback();
- } else {
- iframe.live("load", iframeCallback);
- }
+module('data-method', {
+ teardown: function() { $(document).unbind('iframe:loaded') }
});
-
-test('clicking on a link with data-method attribute and csrf', function() {
- expect(1);
- stop(App.ajax_timeout);
-
- var iframe = $('#fixtures-iframe-csrf iframe');
-
- var iframeCallback = function() {
- var data = iframe.contents().find('body').text();
- equals(data, "/delete was invoked with delete verb. params is {\"_method\"=>\"delete\", \"authenticity_token\"=>\"cf50faa3fe97702ca1ae\"}",
- 'iframe should be proper response message');
-
+function submit(fn) {
+ $(document).bind('iframe:loaded', function(e, data) {
+ fn(data);
start();
- };
+ });
+
+ $('#qunit-fixture').
+ append($(' ', { href: '/echo', 'data-method': 'delete', text: 'destroy!' }))
+ .find('a').trigger('click');
+}
+
+asyncTest('link with "data-method" set to "delete"', 2, function() {
+ submit(function(data) {
+ equal(data.REQUEST_METHOD, 'DELETE');
+ strictEqual(data.params.authenticity_token, undefined);
+ });
+});
- //index.erb load iframe-csrf.eb . Just wait for iframe to load and do its thing and then verify .
- if(iframe[0].loaded) {
- iframeCallback();
- } else {
- iframe.live("load", iframeCallback);
- }
+asyncTest('link with "data-method" and CSRF', 1, function() {
+ $('#qunit-fixture')
+ .append(' ')
+ .append(' ');
+
+ submit(function(data) {
+ equal(data.params.authenticity_token, 'cf50faa3fe97702ca1ae');
+ });
});
+
+})();
\ No newline at end of file
diff --git a/test/public/test/data-remote.js b/test/public/test/data-remote.js
index 3e9ba9c6..02f3e38d 100644
--- a/test/public/test/data-remote.js
+++ b/test/public/test/data-remote.js
@@ -3,61 +3,41 @@ module('data-remote', {
teardown: App.teardown,
setup: function() {
- $('#fixtures').append($(' ', {
- href: '/echo',
- 'data-remote': 'true',
- text: 'my address'
- }));
-
- var form = $('', {
- action: '/echo',
- 'data-remote': 'true',
- method: 'post'
- });
-
- form.append($(' ', {
- id: 'user_name',
- type: 'text',
- size: '30',
- 'name': 'user_name',
- 'value': 'john'
- }));
-
- $('#fixtures').append(form);
-
+ $('#qunit-fixture')
+ .append($(' ', {
+ href: '/echo',
+ 'data-remote': 'true',
+ text: 'my address'
+ }))
+ .append($('', {
+ action: '/echo',
+ 'data-remote': 'true',
+ method: 'post'
+ }))
+ .find('form').append($(' '));
}
});
-test('clicking on a link with data-remote attribute', function() {
- expect(3);
- stop(App.ajax_timeout);
-
+asyncTest('clicking on a link with data-remote attribute', 3, function() {
$('a[data-remote]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = data.request_env;
- App.assert_request_path(request_env, '/echo');
- App.assert_get_request(request_env);
+ App.assert_request_path(data, '/echo');
+ App.assert_get_request(data);
start();
})
.trigger('click');
});
-test('Submitting form with data-remote attribute', function() {
- expect(4);
- stop(App.ajax_timeout);
-
+asyncTest('Submitting form with data-remote attribute', 4, function() {
$('form[data-remote]')
.live('ajax:success', function(e, data, status, xhr) {
App.assert_callback_invoked('ajax:success');
- var request_env = data.request_env,
- params = request_env['rack.request.query_hash'];
-
- App.assert_request_path(request_env, '/echo');
- equals(params['user_name'], 'john', 'ajax arguments shouldh ave key user_name with right value');
- App.assert_post_request(request_env);
+ App.assert_request_path(data, '/echo');
+ equal(data.params.user_name, 'john', 'ajax arguments should have key user_name with right value');
+ App.assert_post_request(data);
start();
})
diff --git a/test/public/test/settings.js b/test/public/test/settings.js
index a3e5416c..8f1f503a 100644
--- a/test/public/test/settings.js
+++ b/test/public/test/settings.js
@@ -16,26 +16,34 @@ App.assert_callback_not_invoked = function(callback_name) {
};
App.assert_get_request = function(request_env){
- equals(request_env['REQUEST_METHOD'], 'GET', 'request type should be GET');
+ equal(request_env['REQUEST_METHOD'], 'GET', 'request type should be GET');
};
App.assert_post_request = function(request_env){
- equals(request_env['REQUEST_METHOD'], 'POST', 'request type should be POST');
+ equal(request_env['REQUEST_METHOD'], 'POST', 'request type should be POST');
};
App.assert_request_path = function(request_env, path) {
- equals(request_env['PATH_INFO'], path, 'request should be sent to right url');
-};
-
-App.die_live_events = function(){
- $('a[data-remote]').die();
- $('input[data-remote]').die();
- $('form[data-remote]').die();
- $('a[data-confirm]').die();
- $('input[data-confirm]').die();
+ equal(request_env['PATH_INFO'], path, 'request should be sent to right url');
};
App.teardown = function(){
- App.die_live_events();
- $('#fixtures').html('');
-};
+ // undelegate all live handlers except ones namespaced with "rails"
+ var data = $.data(document);
+ if (data.events && data.events.live)
+ $.each(data.events.live.slice(0), function() {
+ if (this.origType.split('.').indexOf('rails') < 0) $(this.selector).die();
+ })
+};
+
+$(document).bind('submit', function(e) {
+ if (!e.isDefaultPrevented()) {
+ var form = $(e.target), action = form.attr('action'),
+ name = 'form-frame' + jQuery.guid++,
+ iframe = $('