package com.gwt.ui.client.gwtupld;
import java.util.HashMap;
import java.util.Map;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.FormElement;
import com.google.gwt.dom.client.FrameElement;
import com.google.gwt.dom.client.InputElement;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.dom.client.LoadEvent;
import com.google.gwt.event.dom.client.LoadHandler;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Frame;
import com.google.gwt.user.client.ui.RootPanel;
import com.gwt.ui.client.gwtupld.utils.UUID;
public class UploadHandlerForm extends UploadHandlerAbstract {
private Map<String, InputElement> inputs;
public UploadHandlerForm(UploadProgressHandlers progressHandlers, Options options) {
super(progressHandlers, options);
this.inputs = new HashMap<String, InputElement>();
}
@Override
protected String add(Object o) {
InputElement fileInput = (InputElement) o;
// TODO: what is it for? why qqfile?
fileInput.setAttribute("name", "qqfile");
String id = "qq-upload-handler-iframe" + UUID.uuid();
inputs.put(id, fileInput);
fileInput.removeFromParent();
return id;
}
@Override
protected String getName(String id) {
// get input value and remove path to normalize
// replace all back slashes with forward slashes
final String result = inputs.get(id).getValue().replaceAll("\\\\", "/");
// get everything after last slash (it is file name)
return result.substring(result.lastIndexOf('/') + 1);
}
@Override
protected int getSize(String id) {
return -1;
}
@Override
protected String _upload(String id, Map<String, String> params) {
InputElement inputElement = inputs.get(id);
if (inputElement == null) {
throw new RuntimeException("File with passed id was not added, or" +
"already uploaded or cancelled");
}
final String fileName = getName(id);
final Frame iframe = createFrame(id);
final FormElement form = _createForm(iframe.getElement().getAttribute("name"), params);
form.appendChild(inputElement);
_attachLoadEvent(iframe, id, fileName);
form.submit();
log("submit");
form.removeFromParent();
return id;
}
private void _attachLoadEvent(final Frame iframe, final String id,
final String fileName) {
iframe.addLoadHandler(new LoadHandler() {
@Override
public void onLoad(LoadEvent event) {
final Element e = Element.as(event.getNativeEvent().getEventTarget());
if (e == iframe.getElement()) {
// when we remove iframe from dom
// the request stops, but in IE load
// event fires
if (!iframe.getElement().hasParentElement()) {
return;
}
// fixing Opera 10.53
if (iframe.getElement().getOwnerDocument() != null
&& iframe.getElement().getOwnerDocument().getBody() != null
&& iframe.getElement().getOwnerDocument().getBody().getInnerHTML().equals("false")
) {
// In Opera event is fired second time
// when body.innerHTML changed from false
// to server response approx. after 1 sec
// when we upload file with iframe
return;
}
onComplete(iframe, id, fileName);
}
}
});
}
private void onComplete(final Frame iframe, String id, String fileName) {
log("Iframe loaded; file with id `" + id + "` has been successfully uploaded");
final JSONValue jsonValue = _getIframeContentJSON(iframe);
progressHandlers.onComplete(id, fileName, jsonValue);
_dequeue(id);
inputs.remove(id);
// timeout added to fix busy state in FF3.6
Timer t = new Timer() {
@Override
public void run() {
iframe.removeFromParent();
}
};
t.schedule(1);
}
private JSONValue _getIframeContentJSON(Frame iframe) {
final FrameElement frameElement = (FrameElement)iframe.getElement().cast();
return JSONParser.parseStrict(
frameElement.getContentDocument().getBody().getInnerText()
);
}
@Override
protected void _cancel(String id) {
// TODO implement
/**
this._options.onCancel(id, this.getName(id));
delete this._inputs[id];
var iframe = document.getElementById(id);
if (iframe){
// to cancel request set src to something else
// we use src="javascript:false;" because it doesn't
// trigger ie6 prompt on https
iframe.setAttribute('src', 'javascript:false;');
qq.remove(iframe);
}
*/
}
/**
* Creates iframe with unique name that will be the form target and
* will get server response on form post.
*
* @param id upload id
* @return created frame
*/
private Frame createFrame(String id) {
// We can't use following code as the name attribute
// won't be properly registered in IE6, and new window
// on form submit will open
// var iframe = document.createElement('iframe');
// iframe.setAttribute('name', id);
// TODO: test upload in IE6
Frame frame = new Frame("javascript:false;");
// src="javascript:false;" removes ie6 prompt on https
// TODO: consider using NamedFrame GWT class as it already incapsulates
// this fix
frame.getElement().setAttribute("name", id);
frame.getElement().setAttribute("id", id);
frame.getElement().getStyle().setDisplay(Style.Display.NONE);
RootPanel.get().add(frame);
return frame;
}
/**
* Creates form, that will be submitted to iframe
*/
private FormElement _createForm(String iframeName, Map<String, String> params) {
// We can't use the following code in IE6
// var form = document.createElement('form');
// form.setAttribute('method', 'post');
// form.setAttribute('enctype', 'multipart/form-data');
// Because in this case file won't be attached to request
// TODO: test upload in IE6
final FormElement form = Document.get().createFormElement();
form.setMethod("post");
form.setEnctype("multipart/form-data");
form.setAction(appendParamsToAction(
options.getAction(),
options.getParams()
));
form.setTarget(iframeName);
form.getStyle().setDisplay(Style.Display.NONE);
Document.get().getBody().appendChild(form);
return form;
}
}