package fr.lteconsulting.hexa.client.ui.uploadjs;
import com.google.gwt.core.client.JavaScriptObject;
public class XMLHttpRequestEx extends JavaScriptObject
{
public interface Callback
{
void onProgress( int percentage );
}
public interface ReadyStateChangeHandler
{
/**
* This is called whenever the state of the XMLHttpRequest changes. See
* {@link XMLHttpRequest#setOnReadyStateChange}.
*
* @param xhr
* the object whose state has changed.
*/
void onReadyStateChange( XMLHttpRequestEx xhr );
}
/*
* NOTE: Testing discovered that for some bizarre reason, on Mozilla, the
* JavaScript <code>XmlHttpRequest.onreadystatechange</code> handler
* function maybe still be called after it is deleted. The theory is that
* the callback is cached somewhere. Setting it to null or an empty function
* does seem to work properly, though.
*
* On IE, there are two problems: Setting onreadystatechange to null (as
* opposed to an empty function) sometimes throws an exception. With
* particular (rare) versions of jscript.dll, setting onreadystatechange
* from within onreadystatechange causes a crash. Setting it from within a
* timeout fixes this bug (see issue 1610).
*
* End result: *always* set onreadystatechange to an empty function (never
* to null). Never set onreadystatechange from within onreadystatechange
* (always in a setTimeout()).
*/
/**
* When constructed, the XMLHttpRequest object must be in the UNSENT state.
*/
public static final int UNSENT = 0;
/**
* The OPENED state is the state of the object when the open() method has
* been successfully invoked. During this state request headers can be set
* using setRequestHeader() and the request can be made using send().
*/
public static final int OPENED = 1;
/**
* The HEADERS_RECEIVED state is the state of the object when all response
* headers have been received.
*/
public static final int HEADERS_RECEIVED = 2;
/**
* The LOADING state is the state of the object when the response entity
* body is being received.
*/
public static final int LOADING = 3;
/**
* The DONE state is the state of the object when either the data transfer
* has been completed or something went wrong during the transfer (infinite
* redirects for instance).
*/
public static final int DONE = 4;
/**
* Creates an XMLHttpRequest object.
*
* @return the created object
*/
public static native XMLHttpRequestEx create() /*-{
// Don't check window.XMLHttpRequest, because it can
// cause cross-site problems on IE8 if window's URL
// is javascript:'' .
if ($wnd.XMLHttpRequest) {
return new $wnd.XMLHttpRequest();
} else {
try {
return new $wnd.ActiveXObject('MSXML2.XMLHTTP.3.0');
} catch (e) {
return new $wnd.ActiveXObject("Microsoft.XMLHTTP");
}
}
}-*/;
protected XMLHttpRequestEx()
{
}
/**
* Aborts the current request.
* <p>
* See <a href="http://www.w3.org/TR/XMLHttpRequest/#abort"
* >http://www.w3.org/TR/XMLHttpRequest/#abort</a>.
*/
public final native void abort() /*-{
this.abort();
}-*/;
/**
* Clears the {@link ReadyStateChangeHandler}.
* <p>
* See <a href="http://www.w3.org/TR/XMLHttpRequest/#onreadystatechange"
* >http://www.w3.org/TR/XMLHttpRequest/#onreadystatechange</a>.
*
* @see #clearOnReadyStateChange()
*/
public final native void clearOnReadyStateChange() /*-{
var self = this;
$wnd.setTimeout(function() {
// Using a function literal here leaks memory on ie6
// Using the same function object kills HtmlUnit
self.onreadystatechange = new Function();
}, 0);
}-*/;
/**
* Gets all the HTTP response headers, as a single string.
* <p>
* See <a href="http://www.w3.org/TR/XMLHttpRequest/#getallresponseheaders"
* >http://www.w3.org/TR/XMLHttpRequest/#getallresponseheaders</a>.
*
* @return the response headers.
*/
public final native String getAllResponseHeaders() /*-{
return this.getAllResponseHeaders();
}-*/;
/**
* Get's the current ready-state.
* <p>
* See <a href="http://www.w3.org/TR/XMLHttpRequest/#readystate"
* >http://www.w3.org/TR/XMLHttpRequest/#readystate</a>.
*
* @return the ready-state constant
*/
public final native int getReadyState() /*-{
return this.readyState;
}-*/;
/**
* Gets an HTTP response header.
* <p>
* See <a href="http://www.w3.org/TR/XMLHttpRequest/#getresponseheader"
* >http://www.w3.org/TR/XMLHttpRequest/#getresponseheader</a>.
*
* @param header
* the response header to be retrieved
* @return the header value
*/
public final native String getResponseHeader( String header ) /*-{
return this.getResponseHeader(header);
}-*/;
/**
* Gets the response text.
* <p>
* See <a href="http://www.w3.org/TR/XMLHttpRequest/#responsetext"
* >http://www.w3.org/TR/XMLHttpRequest/#responsetext</a>.
*
* @return the response text
*/
public final native String getResponseText() /*-{
return this.responseText;
}-*/;
/**
* Gets the status code.
* <p>
* See <a href="http://www.w3.org/TR/XMLHttpRequest/#status"
* >http://www.w3.org/TR/XMLHttpRequest/#status</a>.
*
* @return the status code
*/
public final native int getStatus() /*-{
return this.status;
}-*/;
/**
* Gets the status text.
* <p>
* See <a href="http://www.w3.org/TR/XMLHttpRequest/#statustext"
* >http://www.w3.org/TR/XMLHttpRequest/#statustext</a>.
*
* @return the status text
*/
public final native String getStatusText() /*-{
return this.statusText;
}-*/;
/**
* Opens an asynchronous connection.
* <p>
* See <a href="http://www.w3.org/TR/XMLHttpRequest/#open"
* >http://www.w3.org/TR/XMLHttpRequest/#open</a>.
*
* @param httpMethod
* the HTTP method to use
* @param url
* the URL to be opened
*/
public final native void open( String httpMethod, String url ) /*-{
this.open(httpMethod, url, true);
}-*/;
/**
* Opens an asynchronous connection.
* <p>
* See <a href="http://www.w3.org/TR/XMLHttpRequest/#open"
* >http://www.w3.org/TR/XMLHttpRequest/#open</a>.
*
* @param httpMethod
* the HTTP method to use
* @param url
* the URL to be opened
* @param user
* user to use in the URL
*/
public final native void open( String httpMethod, String url, String user ) /*-{
this.open(httpMethod, url, true, user);
}-*/;
/**
* Opens an asynchronous connection.
* <p>
* See <a href="http://www.w3.org/TR/XMLHttpRequest/#open"
* >http://www.w3.org/TR/XMLHttpRequest/#open</a>.
*
* @param httpMethod
* the HTTP method to use
* @param url
* the URL to be opened
* @param user
* user to use in the URL
* @param password
* password to use in the URL
*/
public final native void open( String httpMethod, String url, String user, String password ) /*-{
this.open(httpMethod, url, true, user, password);
}-*/;
/**
* Initiates a request with no request data. This simply calls
* {@link #send(String)} with <code>null</code> as an argument, because the
* no-argument <code>send()</code> method is unavailable on Firefox.
*/
public final void send()
{
send( null );
}
/**
* Initiates a request with data. If there is no data, specify null.
* <p>
* See <a href="http://www.w3.org/TR/XMLHttpRequest/#send"
* >http://www.w3.org/TR/XMLHttpRequest/#send</a>.
*
* @param requestData
* the data to be sent with the request
*/
public final native void send( String requestData ) /*-{
this.send(requestData);
}-*/;
public final void sendAsBinary( String requestData, final Callback callback )
{
setOnReadyStateChange( new ReadyStateChangeHandler()
{
public void onReadyStateChange( XMLHttpRequestEx xhr )
{
if( xhr.getReadyState() == XMLHttpRequestEx.DONE )
{
if( XMLHttpRequestEx.getBrowserSpecificFailure( xhr ) != null )
{
callback.onProgress( -1 );
}
else
{
xhr.clearOnReadyStateChange();
callback.onProgress( 100 );
}
}
}
} );
_sendAsBinary( this, requestData, callback );
}
private final native void _sendAsBinary( XMLHttpRequestEx xhr, String requestData, Callback callback )
/*-{
var upload = this.upload;
upload.addEventListener( "progress", function( event ) {
if (event.lengthComputable)
{
var percentage = Math.round( (event.loaded * 100) / event.total );
if (percentage < 100)
callback.@fr.lteconsulting.hexa.client.ui.uploadjs.XMLHttpRequestEx.Callback::onProgress(I)( percentage );
}
}, false );
if( ! xhr.sendAsBinary )
{
function byteValue(x) {
return x.charCodeAt(0) & 0xff;
}
var ords = Array.prototype.map.call(requestData, byteValue);
var ui8a = new Uint8Array(ords);
this.send(ui8a.buffer);
}
else
xhr.sendAsBinary( requestData );
}-*/;
/**
* Sets the {@link ReadyStateChangeHandler} to be notified when the object's
* ready-state changes.
* <p>
* See <a href="http://www.w3.org/TR/XMLHttpRequest/#onreadystatechange"
* >http://www.w3.org/TR/XMLHttpRequest/#onreadystatechange</a>.
*
* <p>
* Note: Applications <em>must</em> call {@link #clearOnReadyStateChange()}
* when they no longer need this object, to ensure that it is cleaned up
* properly. Failure to do so will result in memory leaks on some browsers.
* </p>
*
* @param handler
* the handler to be called when the ready state changes
* @see #clearOnReadyStateChange()
*/
public final native void setOnReadyStateChange( ReadyStateChangeHandler handler ) /*-{
// The 'this' context is always supposed to point to the xhr object in the
// onreadystatechange handler, but we reference it via closure to be extra sure.
var _this = this;
this.onreadystatechange = $entry(function() {
handler.@fr.lteconsulting.hexa.client.ui.uploadjs.XMLHttpRequestEx.ReadyStateChangeHandler::onReadyStateChange(Lfr/lteconsulting/hexa/client/ui/uploadjs/XMLHttpRequestEx;)( _this );
});
}-*/;
/**
* Sets a request header.
* <p>
* See <a href="http://www.w3.org/TR/XMLHttpRequest/#setrequestheader"
* >http://www.w3.org/TR/XMLHttpRequest/#setrequestheader</a>.
*
* @param header
* the header to be set
* @param value
* the header's value
*/
public final native void setRequestHeader( String header, String value ) /*-{
this.setRequestHeader(header, value);
}-*/;
private native static String getBrowserSpecificFailure( XMLHttpRequestEx xhr ) /*-{
try {
if (xhr.status === undefined) {
return "XmlHttpRequest.status == undefined, please see Safari bug " +
"http://bugs.webkit.org/show_bug.cgi?id=3810 for more details";
}
return null;
} catch (e) {
return "Unable to read XmlHttpRequest.status; likely causes are a " +
"networking error or bad cross-domain request. Please see " +
"https://bugzilla.mozilla.org/show_bug.cgi?id=238559 for more " +
"details";
}
}-*/;
}