Created
July 6, 2012 11:31
-
-
Save sody/3059664 to your computer and use it in GitHub Desktop.
Tapestry Ajax Upload Fixin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.ui.mixins; | |
import org.apache.tapestry5.annotations.AfterRender; | |
import org.apache.tapestry5.annotations.BindParameter; | |
import org.apache.tapestry5.annotations.Import; | |
import org.apache.tapestry5.annotations.InjectContainer; | |
import org.apache.tapestry5.corelib.components.Form; | |
import org.apache.tapestry5.ioc.annotations.Inject; | |
import org.apache.tapestry5.json.JSONObject; | |
import org.apache.tapestry5.services.javascript.JavaScriptSupport; | |
/** | |
* This class represents fixin for form component that adds file uploading possibility when upload component | |
* are placed inside tapestry zone. It will submit form in multipart request within separate iframe | |
* and mark request with {@code 'XHR_EMULATION'} flag. Then client-side zone will be updated with json response | |
* from server. If no file was selected the form will be submitted in a standard way. | |
* <p/> | |
* If browser supports html5 this component will submit form using FormData. | |
* <p/> | |
* NOTE: {@link com.example.ui.internal.FileUploadFilter} request filter should be configured. | |
* | |
* @author Ivan Khalopik | |
* @since 1.0 | |
*/ | |
@Import( | |
library = { | |
"context:js/jquery.ajax.upload.js" | |
}) | |
public class AjaxUpload { | |
@BindParameter | |
private String zone; | |
@InjectContainer | |
private Form form; | |
@Inject | |
private JavaScriptSupport javascriptSupport; | |
/** | |
* Renders fixin's JavaScript if form's zone parameter is bound. | |
*/ | |
@AfterRender | |
void renderScript() { | |
// render script only if form's zone parameter is bound | |
if (zone != null) { | |
javascriptSupport.addInitializerCall("ajaxUpload", new JSONObject( | |
"formId", form.getClientId() | |
)); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.ui.internal; | |
import org.apache.tapestry5.ContentType; | |
import org.apache.tapestry5.SymbolConstants; | |
import org.apache.tapestry5.ioc.annotations.Symbol; | |
import org.apache.tapestry5.services.*; | |
import org.apache.tapestry5.util.ResponseWrapper; | |
import java.io.IOException; | |
import java.io.OutputStream; | |
import java.io.PrintWriter; | |
/** | |
* This class represents filter that emulates xhr request when 'XHR_EMULATION' parameter is set. | |
* | |
* @author Ivan Khalopik | |
* @since 1.0 | |
*/ | |
public class FileUploadFilter implements RequestFilter { | |
private static final String XHR_EMULATION_PARAMETER = "XHR_EMULATION"; | |
private final ContentType emulatedContentType; | |
public FileUploadFilter(@Symbol(SymbolConstants.CHARSET) String outputEncoding) { | |
emulatedContentType = new ContentType("text/plain", outputEncoding); | |
} | |
public boolean service(final Request request, final Response response, final RequestHandler handler) throws IOException { | |
//separate handler for FileUpload requests only | |
if (!request.isXHR() && request.getParameter(XHR_EMULATION_PARAMETER) != null) { | |
return handler.service(new FileUploadRequest(request), new FileUploadResponse(response)); | |
} | |
return handler.service(request, response); | |
} | |
public class FileUploadRequest extends DelegatingRequest { | |
public FileUploadRequest(final Request request) { | |
super(request); | |
} | |
@Override | |
public boolean isXHR() { | |
// now request is xhr | |
return true; | |
} | |
} | |
public class FileUploadResponse extends ResponseWrapper { | |
public FileUploadResponse(final Response response) { | |
super(response); | |
} | |
@Override | |
public OutputStream getOutputStream(final String contentType) throws IOException { | |
// now content type is always text/plain | |
return super.getOutputStream(emulatedContentType.toString()); | |
} | |
@Override | |
public PrintWriter getPrintWriter(final String contentType) throws IOException { | |
// now content type is always text/plain | |
return super.getPrintWriter(emulatedContentType.toString()); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var supportsFormData = typeof(FormData) == "function" && typeof(FormData.prototype) == "object"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$.ajax({ | |
url:self.attr("action"), | |
type:"post", | |
data:new FormData(self.get(0)), | |
// options to tell jquery not to process data or worry about content-type | |
cache:false, | |
contentType:false, | |
processData:false | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var $originalForm = $("form"), | |
$file = $originalForm.find("input:file"), | |
$target = $("<iframe>", { | |
name:"upload_frame" | |
}).hide().appendTo("body"), | |
$form = $("<form>", { | |
target:$target.attr("name"), | |
method:"post", | |
enctype:"multipart/form-data", | |
encoding:"multipart/form-data", | |
action:$originalForm.attr("action") | |
}).hide().appendTo("body"), | |
data = $originalForm.serializeArray(); | |
// clone file inputs and attach original to newly created form | |
$file.after(function() { | |
return this.clone(); | |
}).appendTo($form); | |
// create hidden inputs for all data entries and attach them to form | |
$.each(data, function () { | |
$("<input>", { | |
type:"hidden", | |
name:this.name, | |
value:this.value | |
}).appendTo($form); | |
}); | |
// submit form | |
$form.on("submit",function () { | |
$target.on("load", function () { | |
// when iframe is loaded cache its contents and remove it | |
var content = $target.contents().text(), | |
response = $.parseJSON(content); | |
$form.remove(); | |
$target.remove(); | |
// do something with response data | |
}); | |
}).submit(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @author Ivan Khalopik | |
* @version 0.1 | |
*/ | |
(function ($) { | |
var uuid = 0; | |
var supportsFormData = typeof(FormData) == "function" && typeof(FormData.prototype) == "object"; | |
if (supportsFormData) { | |
// for html5 we can use FormData approach | |
$.fn.upload = function (options) { | |
var self = this, | |
data = new FormData(self.get(0)), | |
deferred = $.Deferred(); | |
options = options || {}; | |
self.promise = deferred.promise; | |
// add additional parameters to form data | |
$.each(options.data || {}, function (name) { | |
data.append(name, this); | |
}); | |
// ajax post | |
$.ajax({ | |
url:self.attr("action"), | |
type:"post", | |
data:new FormData(self.get(0)), | |
// options to tell jquery not to process data or worry about content-type | |
cache:false, | |
contentType:false, | |
processData:false | |
}).then(deferred.resolve, deferred.reject, deferred.notify); | |
return this; | |
}; | |
} else { | |
// for no html5 we can emulate ajax request via iframe | |
$.fn.upload = function (options) { | |
var self = this, | |
data = self.serializeArray(), | |
deferred = $.Deferred(), | |
$file = self.find("input:file"), | |
$target = $("<iframe>", { | |
name:"upload_" + ++uuid | |
}).hide().appendTo("body"), | |
$form = $("<form>", { | |
target:$target.attr("name"), | |
method:"post", | |
enctype:"multipart/form-data", | |
encoding:"multipart/form-data", | |
action:self.attr("action") | |
}).hide().appendTo("body"); | |
options = options || {}; | |
self.promise = deferred.promise; | |
// add additional parameters to form data | |
$.each(options.data || {}, function (name) { | |
data.push({ name:name, value:this }); | |
}); | |
// clone file inputs and attach original to newly created form | |
$file.after(function() { | |
return this.clone(); | |
}).appendTo($form); | |
// add flag to simulate ajax request | |
data.push({ name:"XHR_EMULATION", value:true }); | |
// create hidden inputs for all data entries and attach them to form | |
$.each(data, function () { | |
$("<input>", { | |
type:"hidden", | |
name:this.name, | |
value:this.value | |
}).appendTo($form); | |
}); | |
// submit form | |
$form.on("submit",function () { | |
$target.on("load", function () { | |
// when iframe is loaded cache its contents and remove it | |
var content = $target.contents().text(), | |
response = $.parseJSON(content); | |
$form.remove(); | |
$target.remove(); | |
deferred.resolve(response); | |
}); | |
}).submit(); | |
return this; | |
}; | |
} | |
})(jQuery); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<t:zone t:id="formZone" id="formZone" update="show"> | |
<t:form t:id="form" zone="formZone" t:mixins="ajaxupload"> | |
<t:upload t:id="file" value="file"/> | |
<t:submit value="Upload"/> | |
</t:form> | |
</t:zone> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
T5.extendInitializers({ | |
ajaxUpload:function (spec) { | |
// find tapestry form | |
var tapestryForm = $(spec.formId); | |
// bind on submit form event | |
tapestryForm.observe(Tapestry.FORM_PREPARE_FOR_SUBMIT_EVENT, function () { | |
// stop observing submit event to prevent usual tapestry form submission | |
tapestryForm.stopObserving(Tapestry.FORM_PROCESS_SUBMIT_EVENT); | |
// connect to tapestry form submission event | |
tapestryForm.observe(Tapestry.FORM_PROCESS_SUBMIT_EVENT, function () { | |
// clear submit event listener to prevent infinite loop | |
tapestryForm.onsubmit = null; | |
// upload files in iframe using jquery plugin | |
jQuery("#" + spec.formId).upload().promise().done(function (response) { | |
// update zone with POST result | |
var zone = Tapestry.findZoneManager(spec.formId); | |
zone.processReply(response); | |
}); | |
}); | |
}); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment