From 72c9c277094018620fdb4d78f316d91ca96d136c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20de=20Metz?=
Date: Thu, 30 Dec 2010 16:22:09 +0100
Subject: [PATCH 1/7] initial tests upload
---
successupload.php | 8 +++++
tests.js | 76 +++++++++++++++++++++++++++++++++++++++++++++++
testsupload.html | 26 ++++++++++++++++
3 files changed, 110 insertions(+)
create mode 100644 successupload.php
create mode 100644 tests.js
create mode 100644 testsupload.html
diff --git a/successupload.php b/successupload.php
new file mode 100644
index 00000000..669ecc04
--- /dev/null
+++ b/successupload.php
@@ -0,0 +1,8 @@
+ $_FILES,
+ 'post' => $_POST,
+ 'get' => $_GET
+);
+echo json_encode($request);
diff --git a/tests.js b/tests.js
new file mode 100644
index 00000000..50c81d47
--- /dev/null
+++ b/tests.js
@@ -0,0 +1,76 @@
+QUnit.config.autostart = false;
+
+$(document).ready(function() {
+ $("#forms-ready").click(function(e) {
+ e.preventDefault();
+ QUnit.start();
+ });
+});
+
+asyncTest("upload 1 file", function() {
+ var f = $("#form1").ajaxSubmit(
+ {
+ dataType: 'json',
+ success: function(data) {
+ start();
+ ok(true);
+ ok(data.files.upload);
+ equal(data.files.upload.error, 0);
+ },
+ error: function() {
+ start();
+ ok(false, "test failed")
+ }
+ });
+});
+
+asyncTest("upload error when wrong datatype", function() {
+ var f = $("#form1").attr('action', '404.php').ajaxSubmit(
+ {
+ dataType: 'json',
+ success: function(data) {
+ start();
+ ok(false);
+ },
+ error: function() {
+ start();
+ ok(true, "error");
+ }
+ }).attr('action', 'successupload.php');
+});
+
+asyncTest("upload with iframe forced: iframe = true", function() {
+ var f = $("#form1").ajaxSubmit(
+ {
+ dataType: 'json',
+ iframe : true,
+ success: function(data) {
+ start();
+ ok(true);
+ ok(data.files.upload);
+ },
+ error: function() {
+ start();
+ ok(false, "test failed")
+ }
+ });
+});
+
+asyncTest("upload with extra params", function() {
+ var f = $("#form1").ajaxSubmit(
+ {
+ dataType: 'json',
+ iframe : true,
+ data: {chuck: "norris"},
+ success: function(data) {
+ start();
+ ok(true);
+ ok(data.files.upload);
+ equal(data.post.chuck, "norris");
+ },
+ error: function() {
+ start();
+ ok(false, "test failed")
+ }
+ });
+});
diff --git a/testsupload.html b/testsupload.html
new file mode 100644
index 00000000..738d8306
--- /dev/null
+++ b/testsupload.html
@@ -0,0 +1,26 @@
+
+
+
+
+ jquery.form.js Unit Test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 4463c1c43b71460b2f64cfe96893d8d0e00b4064 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20de=20Metz?=
Date: Thu, 30 Dec 2010 17:58:07 +0100
Subject: [PATCH 2/7] use FormData object when browser support fileapi
(doesn't work for firefox 3.6)
---
jquery.form.js | 94 ++++++++++++++++++++++++++++++++++----------------
tests.js | 1 +
2 files changed, 66 insertions(+), 29 deletions(-)
diff --git a/jquery.form.js b/jquery.form.js
index ddc5fab3..ac8b5d81 100644
--- a/jquery.form.js
+++ b/jquery.form.js
@@ -144,7 +144,7 @@ $.fn.ajaxSubmit = function(options) {
}
options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
- var context = options.context || options; // jQuery 1.4+ supports scope context
+ var context = options.context || options; // jQuery 1.4+ supports scope context
for (var i=0, max=callbacks.length; i < max; i++) {
callbacks[i].apply(context, [data, status, xhr || $form, $form]);
}
@@ -154,30 +154,36 @@ $.fn.ajaxSubmit = function(options) {
var fileInputs = $('input:file', this).length > 0;
var mp = 'multipart/form-data';
var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
-
- // options.iframe allows user to force iframe mode
- // 06-NOV-09: now defaulting to iframe mode if file input is detected
- if (options.iframe !== false && (fileInputs || options.iframe || multipart)) {
- // hack to fix Safari hang (thanks to Tim Molendijk for this)
- // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
- if (options.closeKeepAlive) {
- $.get(options.closeKeepAlive, fileUpload);
- }
- else {
- fileUpload();
- }
- }
- else {
- $.ajax(options);
- }
+ var fileAPI = !!(fileInputs && $('input:file', this).get(0).files);
+ log("fileAPI :" + fileAPI);
+ var shouldUseFrame = (fileInputs || multipart) && !fileAPI;
+
+ // options.iframe allows user to force iframe mode
+ // 06-NOV-09: now defaulting to iframe mode if file input is detected
+ if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
+ // hack to fix Safari hang (thanks to Tim Molendijk for this)
+ // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
+ if (options.closeKeepAlive) {
+ $.get(options.closeKeepAlive, fileUploadIframe);
+ }
+ else {
+ fileUploadIframe();
+ }
+ }
+ else if ((fileInputs || multipart) && fileAPI) {
+ fileUploadXhr();
+ }
+ else {
+ $.ajax(options);
+ }
// fire 'notify' event
this.trigger('form-submit-notify', [this, options]);
return this;
- // private function for handling file uploads (hat tip to YAHOO!)
- function fileUpload() {
+ // private function for handling file uploads in iframe (hat tip to YAHOO!)
+ function fileUploadIframe() {
var form = $form[0];
if ($(':input[name=submit],:input[id=submit]', form).length) {
@@ -186,7 +192,7 @@ $.fn.ajaxSubmit = function(options) {
alert('Error: Form elements must not have name or id of "submit".');
return;
}
-
+
var s = $.extend(true, {}, $.ajaxSettings, options);
s.context = s.context || s;
var id = 'jqFormIO' + (new Date().getTime()), fn = '_'+id;
@@ -228,7 +234,7 @@ $.fn.ajaxSubmit = function(options) {
}
if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
- if (s.global) {
+ if (s.global) {
$.active--;
}
return;
@@ -315,7 +321,7 @@ $.fn.ajaxSubmit = function(options) {
else {
setTimeout(doSubmit, 10); // this lets dom updates render
}
-
+
var data, doc, domCheckCount = 50;
function cb() {
@@ -324,7 +330,7 @@ $.fn.ajaxSubmit = function(options) {
}
$io.removeData('form-plugin-onload');
-
+
var ok = true;
try {
if (timedOut) {
@@ -332,7 +338,7 @@ $.fn.ajaxSubmit = function(options) {
}
// extract the server response from the iframe
doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
-
+
var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
log('isXml='+isXml);
if (!isXml && window.opera && (doc.body == null || doc.body.innerHTML == '')) {
@@ -350,7 +356,7 @@ $.fn.ajaxSubmit = function(options) {
//log('response detected');
cbInvoked = true;
- xhr.responseText = doc.documentElement ? doc.documentElement.innerHTML : null;
+ xhr.responseText = doc.documentElement ? doc.documentElement.innerHTML : null;
xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
xhr.getResponseHeader = function(header){
var headers = {'content-type': s.dataType};
@@ -374,7 +380,7 @@ $.fn.ajaxSubmit = function(options) {
else if (b) {
xhr.responseText = b.innerHTML;
}
- }
+ }
}
else if (s.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
xhr.responseXML = toXml(xhr.responseText);
@@ -387,7 +393,7 @@ $.fn.ajaxSubmit = function(options) {
xhr.error = e;
$.handleError(s, xhr, 'error', e);
}
-
+
if (xhr.aborted) {
log('upload aborted');
ok = false;
@@ -430,6 +436,36 @@ $.fn.ajaxSubmit = function(options) {
return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
}
}
+
+ // private function for handling file uploads with xmlhttprequest (hat type to jquery-sexypost)
+ function fileUploadXhr() {
+ // this function will POST the contents of the selected form via XmlHttpRequest.
+
+ var data = new FormData();
+
+ $("input:text, input:hidden, input:password, textarea", $form).each(function(){
+ data.append($(this).attr("name"), $(this).val());
+ });
+
+ $("input:file", $form).each(function(){
+ var files = this.files;
+ for (i=0; i< max; i++) {
el = els[i];
diff --git a/tests.js b/tests.js
index 50c81d47..cae6dee2 100644
--- a/tests.js
+++ b/tests.js
@@ -1,4 +1,5 @@
QUnit.config.autostart = false;
+$.fn.ajaxSubmit.debug = true;
$(document).ready(function() {
$("#forms-ready").click(function(e) {
From 436e509acc69cf55dbc5fbaeb85e72e480e96b88 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20de=20Metz?=
Date: Thu, 30 Dec 2010 20:59:59 +0100
Subject: [PATCH 3/7] save previous beforeSend when uploading with FormData
---
jquery.form.js | 2 ++
tests.js | 13 ++++++++++---
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/jquery.form.js b/jquery.form.js
index ac8b5d81..136f3132 100644
--- a/jquery.form.js
+++ b/jquery.form.js
@@ -460,8 +460,10 @@ $.fn.ajaxSubmit = function(options) {
data.append($(this).attr("name"), $(this).val());
});
options.data = null;
+ var originalBeforeSend = options.beforeSend;
options.beforeSend = function(xhr, options) { // et toc !
options.data = data;
+ if (originalBeforeSend) originalBeforeSend(xhr, options);
}
$.ajax(options);
}
diff --git a/tests.js b/tests.js
index cae6dee2..273b08af 100644
--- a/tests.js
+++ b/tests.js
@@ -9,14 +9,21 @@ $(document).ready(function() {
});
asyncTest("upload 1 file", function() {
+ var beforeSendCalled = 0;
var f = $("#form1").ajaxSubmit(
{
dataType: 'json',
+ beforeSend: function(xhr, options) {
+ beforeSendCalled++;
+ ok(xhr, "beforeSend xhr ok");
+ ok(options, "beforeSend options ok");
+ },
success: function(data) {
start();
- ok(true);
- ok(data.files.upload);
- equal(data.files.upload.error, 0);
+ ok(true, "fine");
+ equal(beforeSendCalled, 1, "beforeSend called");
+ ok(data.files.upload, "file uploaded");
+ equal(data.files.upload.error, 0, "file uploaded without errors");
},
error: function() {
start();
From d62ee31a9ee2a6e6fb35605af9c63d5ca81d30b5 Mon Sep 17 00:00:00 2001
From: _gr_
Date: Fri, 7 Jan 2011 17:07:56 +0100
Subject: [PATCH 4/7] add brutal progress callback on xhr2
---
jquery.form.js | 7 ++++++-
tests.js | 32 +++++++++++++++++++++++++++++++-
2 files changed, 37 insertions(+), 2 deletions(-)
diff --git a/jquery.form.js b/jquery.form.js
index 136f3132..38998cca 100644
--- a/jquery.form.js
+++ b/jquery.form.js
@@ -171,6 +171,7 @@ $.fn.ajaxSubmit = function(options) {
}
}
else if ((fileInputs || multipart) && fileAPI) {
+ options.progress = options.progress || $.noop();
fileUploadXhr();
}
else {
@@ -442,7 +443,6 @@ $.fn.ajaxSubmit = function(options) {
// this function will POST the contents of the selected form via XmlHttpRequest.
var data = new FormData();
-
$("input:text, input:hidden, input:password, textarea", $form).each(function(){
data.append($(this).attr("name"), $(this).val());
});
@@ -461,8 +461,13 @@ $.fn.ajaxSubmit = function(options) {
});
options.data = null;
var originalBeforeSend = options.beforeSend;
+ _options = options;
options.beforeSend = function(xhr, options) { // et toc !
options.data = data;
+ xhr.upload.onprogress = function(event) {
+ console.log(event);
+ _options.progress(event.position, event.total);
+ }
if (originalBeforeSend) originalBeforeSend(xhr, options);
}
$.ajax(options);
diff --git a/tests.js b/tests.js
index 273b08af..adfeac54 100644
--- a/tests.js
+++ b/tests.js
@@ -7,7 +7,7 @@ $(document).ready(function() {
QUnit.start();
});
});
-
+/*
asyncTest("upload 1 file", function() {
var beforeSendCalled = 0;
var f = $("#form1").ajaxSubmit(
@@ -82,3 +82,33 @@ asyncTest("upload with extra params", function() {
}
});
});
+*/
+asyncTest("upload 1 file and got at least a progress event ", function() {
+ var beforeSendCalled = 0;
+ var f = $("#form1").ajaxSubmit(
+ {
+ dataType: 'json',
+ beforeSend: function(xhr, options) {
+ beforeSendCalled++;
+ ok(xhr, "beforeSend xhr ok");
+ ok(options, "beforeSend options ok");
+ },
+ success: function(data) {
+ start();
+ ok(true, "fine");
+ equal(beforeSendCalled, 1, "beforeSend called");
+ ok(data.files.upload, "file uploaded");
+ equal(data.files.upload.error, 0, "file uploaded without errors");
+ },
+ error: function() {
+ start();
+ ok(false, "test failed")
+ },
+ progress: function(position, size) {
+ start();
+ ok(true, "No progress event received");
+ console.log("position : " + position + " size : " + size)
+ }
+ });
+});
+
From 7912eaeeded589f67db3ec879af7c55d73fe7bc0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20de=20Metz?=
Date: Wed, 12 Jan 2011 18:45:26 +0100
Subject: [PATCH 5/7] fix detection of fileapi support with FormData
---
jquery.form.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/jquery.form.js b/jquery.form.js
index 38998cca..be3d650d 100644
--- a/jquery.form.js
+++ b/jquery.form.js
@@ -154,7 +154,7 @@ $.fn.ajaxSubmit = function(options) {
var fileInputs = $('input:file', this).length > 0;
var mp = 'multipart/form-data';
var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
- var fileAPI = !!(fileInputs && $('input:file', this).get(0).files);
+ var fileAPI = !!(fileInputs && $('input:file', this).get(0).files && window.FormData);
log("fileAPI :" + fileAPI);
var shouldUseFrame = (fileInputs || multipart) && !fileAPI;
From cd302b07dfea12be5c825a129519e0d5c4da6766 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20de=20Metz?=
Date: Wed, 12 Jan 2011 18:45:56 +0100
Subject: [PATCH 6/7] removed console.log
---
jquery.form.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/jquery.form.js b/jquery.form.js
index be3d650d..742034b6 100644
--- a/jquery.form.js
+++ b/jquery.form.js
@@ -465,7 +465,6 @@ $.fn.ajaxSubmit = function(options) {
options.beforeSend = function(xhr, options) { // et toc !
options.data = data;
xhr.upload.onprogress = function(event) {
- console.log(event);
_options.progress(event.position, event.total);
}
if (originalBeforeSend) originalBeforeSend(xhr, options);
From 766fe8def1aa002887dc8f94591856baf0c25d8e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20de=20Metz?=
Date: Mon, 24 Jan 2011 14:51:44 +0100
Subject: [PATCH 7/7] Add support of firefox 3.6 with a fake FormData object.
See https://github.com/francois2metz/html5-formdata.
---
jquery.form.js | 11 +++++++++++
testsupload.html | 1 +
2 files changed, 12 insertions(+)
diff --git a/jquery.form.js b/jquery.form.js
index 742034b6..0ff4ba6a 100644
--- a/jquery.form.js
+++ b/jquery.form.js
@@ -467,6 +467,17 @@ $.fn.ajaxSubmit = function(options) {
xhr.upload.onprogress = function(event) {
_options.progress(event.position, event.total);
}
+ /**
+ * You can use https://github.com/francois2metz/html5-formdata for a fake FormData object
+ * Only work with Firefox 3.6
+ */
+ if (data.fake) {
+ xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary="+ data.boundary);
+ // with fake FormData object, we must use sendAsBinary
+ xhr.send = function(data) {
+ xhr.sendAsBinary(data.toString());
+ }
+ }
if (originalBeforeSend) originalBeforeSend(xhr, options);
}
$.ajax(options);
diff --git a/testsupload.html b/testsupload.html
index 738d8306..3f748085 100644
--- a/testsupload.html
+++ b/testsupload.html
@@ -6,6 +6,7 @@
+