Subversion Repositories JSX

Compare Revisions

Last modification

Ignore whitespace Rev 148 → Rev 149

/trunk/httprequest.js
5,48 → 5,97
*
* @idl
*
* interface HTTPResponseHandler :: Function
* interface HTTPResponseListener :: Function
* {
* boolean HTTPResponseHandler(IXMLHttpRequest x);
* boolean HTTPResponseListener(IXMLHttpRequest x);
* // Handles the response for the HTTP request initiated with x.send()
* // SHOULD return a true-value if successful, a false-value otherwise
* }
*
* interface HTTPMethod
* {
* const string GET="GET"
* const string POST="POST"
* }
*
* interface HTTPRequestReadyState
* {
* const int COMPLETED=4
* }
*
* interface HTTPStatus {
* const RegExp OK_EXPR=/\b(0|2\d\d)\b/
* const RegExp FAILED_EXPR=/\b[45]\d\d\b/
* }
*
* interface HTTPRequest
* {
* attribute string requestType setter=setRequestType(string)
* default=setRequestType();
* attribute string URL
* setter=setURL(string)
* default=setURL();
* attribute HTTPMethod method
* setter=setMethod(string)
* default=setMethod();
* attribute boolean async
* setter=setAsync(boolean)
* default=setAsync();
* attribute string data
* setter=setData(string)
* default=setData();
* attribute string requestType
* setter=setRequestType(string)
* default=setRequestType();
* readonly attribute HTTPRequestReadyState readyState
* readonly attribute HTTPStatus status
* attribute HTTPResponseListener responseListener
* attribute HTTPResponseListener successListener
* attribute HTTPResponseListener errorListener
*
* void HTTPRequest();
* // URL=document.URL, method="GET", async=true, responseHandler=null,
* // errorHandler=null
* HTTPRequest HTTPRequest();
* // URL=document.URL, method="GET", async=true, successListener=null,
* // errorListener=null
*
* void HTTPRequest(string sURL);
* // URL=sURL||document.URL, method="GET", async=true, responseHandler=null,
* // errorHandler=null
* HTTPRequest HTTPRequest(string sURL);
* // URL=sURL||document.URL, method="GET", async=true,
* // successListener=null, errorListener=null
*
* void HTTPRequest(string sURL, string sMethod);
* HTTPRequest HTTPRequest(string sURL, string sMethod);
* // URL=sURL||document.URL, method=sMethod||"GET", async=true,
* // responseHandler=null, errorHandler=null
* // successListener=null, errorListener=null
*
* void HTTPRequest(string sURL, string sMethod, boolean bAsync);
* HTTPRequest HTTPRequest(string sURL, string sMethod, boolean bAsync);
* // URL=sURL||document.URL, method=sMethod||"GET", async=bAsync,
*
* void HTTPRequest(string sURL, string sMethod, boolean bAsync,
* HTTPResponseHandler fResponseHandler);
* HTTPRequest HTTPRequest(string sURL, string sMethod, boolean bAsync,
* HTTPResponseListener fSuccessListener);
* // URL=sURL||document.URL, method=sMethod||"GET", async=bAsync,
* // responseHandler=fResponseHandler, errorHandler=null
* // successListener=fSuccessListener, errorListener=null
*
* void HTTPRequest(string sURL, string sMethod, boolean bAsync,
* HTTPResponseHandler fResponseHandler,
* HTTPResponseHandler fErrorHandler);
* HTTPRequest HTTPRequest(string sURL, string sMethod, boolean bAsync,
* HTTPResponseListener fSuccessListener,
* HTTPResponseListener fErrorListener);
* // URL=sURL||document.URL, method=sMethod||"GET", async=bAsync,
* // responseHandler=fResponseHandler, errorHandler=fErrorHandler
* // successListener=fSuccessListener, errorListener=fErrorListener
*
* boolean setURL(string sURL default="");
* // URL=sURL
*
* boolean setMethod(string sMethod default="GET");
* // method=sMethod
*
* boolean setAsync(boolean bAsync default=true);
* // async=bAsync
*
* boolean setData(string sData default="");
* // data=sData
*
* boolean setRequestType(string sRequestType
* default="application/x-www-form-urlencoded");
* // requestType=sRequestType
*
*
* boolean send(string sData, string sURL, string sMethod, boolean bAsync);
* // sData=null, URL=sURL||document.URL, method=sMethod||"GET",
* // bAsync=true
* }
*
* @end
53,12 → 102,15
*/
 
/**
* Creates a new HTTPRequest object when called as constructor.
* Set up response handlers per argument (see below), or
* {@link HTTPRequest.prototype#setResponseHandler} and
* {@link HTTPRequest.prototype#setErrorHandler}, then call
* {@link HTTPRequest.prototype#send} method to send the request.
* Creates a new HTTPRequest object.
*
* You can set up response listeners per argument (see below), or
* {@link HTTPRequest.prototype#setResponseListener setResponseListener()},
* {@link HTTPRequest.prototype#setSuccessListener setSuccessListener()},
* and {@link HTTPRequest.prototype#setErrorListener setErrorListener()},
* then call {@link HTTPRequest.prototype#send send()} to submit
* the request.
*
* @param sURL : optional string=document.URL
* Request URL. The default is the URL of the sending resource.
* @param sMethod : optional string=HTTPRequest.method.GET
69,22 → 121,29
* Pass <code>true</code> to make an asynchronous request (default),
* that is, a request that is processed in the background and does
* not interrupt user operation.
* @param fResponseHandler : optional HTTPResponseHandler=null
* @param fSuccessListener : optional HTTPResponseListener=null
* The function to handle the response of a successful request
* (default: <code>null</code>).
* @param fErrorHandler : optional HTTPResponseHandler=null
* @param fErrorListener : optional HTTPResponseListener=null
* The function to handle the response of a request that failed
* (default: <code>null</code>).
* @constructor
* @return undefined
* @type HTTPRequest
*/
function HTTPRequest(sURL, sMethod, bAsync, fResponseHandler, fErrorHandler)
function HTTPRequest(sURL, sMethod, bAsync, fSuccessListener, fErrorListener)
{
this.setURL(sURL);
/* Enables factory use */
var me = arguments.callee;
if (this.constructor !== me)
{
return me.construct(arguments);
}
this.setURL(sURL, true);
this.setMethod(sMethod);
this.setAsync(bAsync);
this.setResponseHandler(fResponseHandler);
// this.setErrorHandler(fErrorHandler);
this.setSuccessListener(fSuccessListener);
this.setErrorListener(fErrorListener);
this.setData();
this.setRequestType();
}
109,7 → 168,11
},
status: {
OK_EXPR: /\b(0|2\d\d)\b/,
/*
* NOTE: MSXML translates 204 to 1223, see
* https://prototype.lighthouseapp.com/projects/8886/tickets/129-ie-mangles-http-response-status-code-204-to-1223
*/
OK_EXPR: /\b(0|2\d\d|1223)\b/,
LOCAL_NONE: 0,
 
CONTINUE: 100,
132,6 → 195,7
TEMP_REDIR: 307,
FAILED_EXPR: /\b[45]\d\d\b/,
CLIENT_ERROR_EXPR: /\b4\d\d\b/,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
167,6 → 231,38
constructor: HTTPRequest,
 
/**
* Method to be called onreadystatechange
*
* @param x : XMLHttpRequest
*/
responseListener: function(x) {
var C = this.constructor;
if (x.readyState === C.readyState.COMPLETED)
{
var
jsx_object = jsx.object,
oStatus = C.status,
reqStatus = x.status;
if (oStatus.OK_EXPR.test(reqStatus))
{
if (jsx_object.isMethod(this.successListener))
{
this.successListener(x);
}
}
else if (oStatus.FAILED_EXPR.test(reqStatus))
{
if (jsx_object.isMethod(this.errorListener))
{
this.errorListener(x);
}
}
}
},
/**
* Sets the <code>URL</code> property.
*
* @param sURL : string
173,22 → 269,22
* If not provided or a false-value, the
* URL of the sending recource is set.
* @param bDontEncode : optional boolean
* If <code>true</code>, do not encode the request URL
* (with <code>escURI()</code> or <code>escape()</code>).
* If <code>true</code>, do not encode the request URI
* with {@link jsx.string#escURI()}.
*/
setURL: function(sURL, bDontEncode) {
if (!bDontEncode && sURL) sURL = esc(sURL);
if (!bDontEncode && sURL) sURL = escURI(sURL);
this.URL = (sURL || document.URL);
},
 
/**
* Sets the <code>method</code> property. Use the
* <code>HTTPRequest.(GET|POST)</code> properties
* <code>HTTPRequest.method.(GET|POST)</code> properties
* to avoid problems with character case and typos.
*
* @param sMethod : optional string
* If not provided or a false-value, the value
* of <code>HTTPRequest.GET</code> is used.
* of <code>HTTPRequest.method.GET</code> is used.
*/
setMethod: function(sMethod) {
this.method =
211,64 → 307,109
},
/**
* Defines the response handler method to be used for handling
* successful requests. A HTTPRequest object is always initialized
* with a dummy response handler that does nothing, if you do not
* specify one. Once initialized, passing a reference to a non-callable
* object as argument, throws an InvalidArgumentException.
* Defines the response Listener method to be used for handling
* requests.
*
* @param fResponseHandler : HTTPResponseHandler
* @throws InvalidArgumentException
* A <code>HTTPRequest</code> object is always initialized with
* an inherited default response Listener that calls
* {@link HTTPRequest.prototype#successListener() successListener()}
* on success, and
* {@link HTTPRequest.prototype#errorListener() errorListener()}
* on failure. Once initialized, passing a reference to a
* non-callable object as argument, throws an
* {@link jsx#InvalidArgumentError InvalidArgumentError}
* exception.
*
* @param fResponseListener : HTTPResponseListener
* @throws jsx.InvalidArgumentError
*/
setResponseHandler: function(fResponseHandler) {
// initialization
if (typeof this.responseHandler == "undefined")
setResponseListener: function(fResponseListener) {
/* initialization */
if (typeof this.responseListener == "undefined")
{
this.responseHandler = new HTTPResponseHandler();
this.responseListener = new HTTPResponseListener();
return true;
}
else if (jsx.object.isMethod(fResponseHandler))
else if (jsx.object.isMethod(fResponseListener))
{
this.responseHandler = fResponseHandler;
return (this.responseHandler == fResponseHandler);
this.responseListener = fResponseListener;
return (this.responseListener == fResponseListener);
}
else
{
_global.setErrorHandler();
eval('throw new Error('
+ '"jsx:HTTPRequest::setResponseHandler: Argument is not a method");');
_global.clearErrorHandler();
jsx.throwThis("jsx.InvalidArgumentError",
"jsx:HTTPRequest::setResponseListener: Argument is not a method");
return false;
}
},
 
/**
* Defines the response handler method to be used for handling
* Defines the response Listener method to be used for handling
* successful requests.
*
* A <code>HTTPRequest</code> object is always initialized with
* an inherited dummy success Listener that does nothing, if you
* do not specify one. Once initialized, passing a reference
* to a non-callable object as argument throws an
* {@link jsx#InvalidArgumentError InvalidArgumentError}
* exception.
*
* @param fSuccessListener : HTTPResponseListener
* @throws jsx.InvalidArgumentError
*/
setSuccessListener: function(fSuccessListener) {
/* initialization */
if (typeof this.responseListener == "undefined")
{
this.successListener = new HTTPResponseListener();
return true;
}
else if (jsx.object.isMethod(fSuccessListener))
{
this.successListener = fSuccessListener;
return (this.successListener == fSuccessListener);
}
else
{
jsx.throwThis("jsx.InvalidArgumentError",
"jsx:HTTPRequest::setResponseListener: Argument is not a method");
return false;
}
},
/**
* Defines the response Listener method to be used for handling
* unsuccessful requests.
*
* @param : HTTPResponseHandler
* A <code>HTTPRequest</code> object is always initialized with
* an inherited dummy error Listener that does nothing, if you
* do not specify one. Once initialized, passing a reference
* to a non-callable object as argument throws an
* {@link jsx#InvalidArgumentError InvalidArgumentError}
* exception.
*
* @param fErrorListener : HTTPResponseListener
* @throws jsx.InvalidArgumentError
*/
/*
setErrorHandler: function(fErrorHandler) {
if (typeof this.errorHandler == "undefined")
setErrorListener: function(fErrorListener) {
if (typeof this.errorListener == "undefined")
{
this.errorHandler = new HTTPResponseHandler();
this.errorListener = new HTTPResponseListener();
return true;
}
else if (jsx.object.isMethod(fErrorHandler))
else if (jsx.object.isMethod(fErrorListener))
{
this.errorHandler = fErrorHandler;
return (this.errorHandler == this.errorHandler);
this.errorListener = fErrorListener;
return (this.errorListener == this.errorListener);
}
else
{
_global.setErrorHandler();
eval('throw new Error('
+ '"jsx:HTTPRequest::setErrorHandler: Argument is not a method");');
_global.clearErrorHandler();
jsx.throwThis('jsx.InvalidArgumentError',
"jsx:HTTPRequest::setErrorListener: Argument is not a method");
return false;
}
},
*/
 
/**
* Sets the <code>data</code> property.
*
286,8 → 427,7
*
* @see HTTPRequest.prototype#setData()
*/
resetData: function()
{
resetData: function() {
this.setData();
},
 
302,8 → 442,7
* is <code>false</code>.
* @return boolean
*/
getDataFromForm: function(f, bUseFormMethod)
{
getDataFromForm: function(f, bUseFormMethod) {
var result = false, es, len;
 
if (f && (es = f.elements) && (len = es.length))
343,6 → 482,8
},
 
/**
* Submits the HTTP request.
*
* @param sData : optional string
* The data to form the request body. If the request method is "GET",
* this argument is ignored and <code>null</code> is used instead (no body).
361,11 → 502,20
* if <code>false</code> is passed. If not provided, this value defaults
* to that of the <code>async</code> property, which is <code>true</code>
* if not set different previously.
* @type boolean
* @return <code>true</code> if the XHR object could be created
* and <code>IXMLHTTPRequest::send()</code> was successful;
* <code>false</code> otherwise. Note that "successful" does
* not imply that the server has actually received the message,
* and responded with an OK status code, only that the method
* could be called successfully.
*/
send: function(sData, sURL, sMethod, bAsync) {
var
result = false,
jsx_global = jsx.global,
jsx_object = jsx.object,
C = this.constructor,
x = null;
 
/*
379,7 → 529,7
* currently not for `file:' URIs, so we don't prefer that wrapper (see
* <http://xhab.blogspot.com/2006/11/ie7-support-for-xmlhttprequest.html>).
*/
if (jsx_object.isMethod(jsx.global, "ActiveXObject"))
if (jsx_object.isMethod(jsx_global, "ActiveXObject"))
{
/*@cc_on @*/
/*@if (@_jscript_version >= 5)
396,13 → 546,11
}
/* Gecko and Opera 8.1+ */
if (!x && jsx_object.isMethod(jsx.global, "XMLHttpRequest"))
if (!x && jsx_object.isMethod(jsx_global, "XMLHttpRequest"))
{
/*
* eval() is needed here so that this compiles in ECMAScript < 3
* (e.g. IE 4, NS 4)
*/
eval('try { x = new XMLHttpRequest(); } catch (e) { x = null; }');
jsx.tryThis(
function() { x = new XMLHttpRequest(); },
function() { x = null; });
}
/* IceBrowser */
409,18 → 557,23
if (!x && typeof window != "undefined"
&& jsx_object.isMethod(window, "createRequest"))
{
/* see above */
eval('try { x = window.createRequest(); } catch (e) { x = null; }');
jsx.tryThis(
function() { x = window.createRequest(); },
function() { x = null; });
}
if (x) // && jsx_object.isMethod(x.open)
if (x && jsx_object.isMethod(x, "open"))
{
/* Assume everything goes smoothly from here */
result = true;
if (arguments.length < 1) sData = this.data;
if (arguments.length < 2) sURL = this.URL;
 
if (arguments.length < 3) sMethod = this.method;
var bGET = (sMethod == this.constructor.method.GET);
var bGET = (sMethod == C.method.GET);
 
bAsync = (arguments.length > 3) ? !!bAsync : this.async;
bAsync = (arguments.length > 3) ? !!bAsync : this.async;
x.open(
sMethod.toUpperCase(),
432,36 → 585,33
+ sData
: ""),
bAsync);
 
// if (jsx_object.isMethod(x.setRequestHeader))
// {
// see above
eval('try { x.setRequestHeader("Content-Type", this.requestType); }'
+ 'catch (e) {}');
// }
if (jsx_object.isMethod(x, "setRequestHeader"))
{
/* NOTE: Failure to call this method is _not_ considered a fatal error. */
jsx.tryThis(
function() {
x.setRequestHeader("Content-Type", this.requestType);
}
);
}
if (bAsync)
{
var me = this;
x.onreadystatechange = function()
{
x.onreadystatechange = function() {
// alert(x.readyState);
// alert(x.status);
// console.log("readyState = %i, status = %i", x.readyState, x.status);
// console.log(me.constructor.status.OK_EXPR);
// console.log(C.status.OK_EXPR);
// if (x.readyState > HTTPRequest.readyState.LOADED
// && me.constructor.status.OK_EXPR.test(x.status))
// {
me.responseHandler(x);
// }
// else
// {
// me.errorHandler(x);
// }
if (jsx_object.isMethod(me.responseListener))
{
me.responseListener(x);
}
if (x.readyState == me.constructor.readyState.COMPLETED)
if (x.readyState == C.readyState.COMPLETED)
{
x = null;
}
468,27 → 618,27
};
}
eval('try { x.send(bGET ? null : (sData || this.data)); }'
+ 'catch (e) { /*this.errorHandler();*/ }');
jsx.tryThis(
function() { x.send(bGET ? null : (sData || this.data)); },
function() { result = false; this.errorListener(x); });
if (!bAsync)
{
// if (this.constructor.status.OK_EXPR.test(x.status)
// && jsx_object.isMethod(this.responseHandler))
// {
this.responseHandler(x);
if (jsx_object.isMethod(this.responseListener))
{
this.responseListener(x);
}
// Handle stopped servers
eval('try { if (this.constructor.status.OK_EXPR.test(x.status)) {'
+ 'result = true;'
+ '} } catch {}');
// }
// else if (this.constructor.status.FAILED_EXPR.test(x.status)
// && jsx_object.isMethod(this.errorHandler))
// {
// this.errorHandler(x);
// }
/* Handle stopped servers */
jsx.tryThis(
function() {
if (C.status.OK_EXPR.test(x.status)) {
result = true;
}
}
);
 
/* TODO: Is this error-prone? */
x = null;
}
}
498,13 → 648,15
};
 
/**
* A HTTPResponseHandler object is a specialized Function object
* that takes an IXMLHttpRequest object <code>x</code>as its only
* Creates a new HTTPResponseListener object.
*
* A HTTPResponseListener object is a specialized Function object
* that takes an IXMLHttpRequest object <var>x</var> as its only
* argument. This method is a factory to create such an object.
*
* Recommended usage:
* <pre><code>
* var f = HTTPResponseHandler(
* var f = HTTPResponseListener(
* new Array(
* 'statement;',
* 'statement;'
512,22 → 664,15
* </code></pre>
*
* @param sCode
* @return Function
* A new <code>HTTPResponseHandler</code> object
* @type Function
* @return A new <code>HTTPResponseListener</code> object
*/
function HTTPResponseHandler(sCode)
function HTTPResponseListener(sCode)
{
return Function("x", sCode || "");
}
 
/**
* @param x
*/
function processResponse(x)
{
/* ... */
}
 
// var x = new HTTPRequest("", HTTPRequest.GET, processResponse);
/* Usage: */
// var x = new HTTPRequest("", HTTPRequest.method.GET, true, processResponse);
/* ... */
// x.send();