/* License: GNU Lesser General Public License (http://www.gnu.org/licenses/lgpl.html) Copyright (C) 2005 tagnetic.org. Please do not remove this copyright/license comment. */ //************************************************************************* //Start SvrReq useSvrReq //************************************************************************* /** * Class to make server requests via the XMLHTTPRequest object. * * http://developer.apple.com/internet/webcontent/xmlhttpreq.html * served as a reference for the core of this class. * * For XML responses, this object does NOT automatically call XMLHTTPRequest.overrideMimeType. * It is expected that the server is configured correctly to send the correct Content-type * mime type, particularly if a specific charset is desired. The instance method * .overrideMimeType(mimeType) can be used if you want to force a particular mime type, but * it is not supported for MSIE. Make sure the HTTP server is configured correctly to return * the right Content-Type header. * * If you need direct access to the underlying browser's implementation of * XMLHTTPRequest object, you can use the .impl member property of this object. For instance: * var myrequest = new SvrReq(); * myrequest.impl.responseText //to access browser's implementation of XMLHTTPRequest * * There seems to be an issue with MSIE (at least version 6.0 on XP SP2) where if you * use XMLHTTPRequest from a local file on disk to get XML from another local file on * disk. It doesn't seem to return a fully valid DOM. In that case, use the impl.responseText * and feed it to the DOMParser class defined in SDom.js. * * @class */ SvrReq = function() { this._id = 'id' + (SvrReq._idCounter++); this.impl = SvrReq.createReqObject(); SvrReq._inst[this._id] = this; }; //******************************************************************* //static (class) methods/properties //******************************************************************* /** * Override this value if you want to send a different * mime type (like wanting to add a charset). */ SvrReq.postMimeType = 'application/x-www-form-urlencoded'; //******************************************************************* /** * You can call this method without using a SvrReq instance, if you * want total control over the request implementation, instead of * using a SvrReq instance. * * This method may throw exceptions. */ SvrReq.createReqObject = function() { var req = null; if(window.XMLHttpRequest) req = new XMLHttpRequest(); else if(window.ActiveXObject) { try { req = new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) { req = new ActiveXObject("Microsoft.XMLHTTP"); } } return req; } //******************************************************************* /** * @private variable to hold onto SvrReq instances (used to * route request callbacks). */ SvrReq._inst = new Object(); //******************************************************************* /** * @private method used to get callbacks from the underlying request object. * */ SvrReq._forwardStateChange = function(id) { SvrReq._inst[id]._stateChange(); } //******************************************************************* SvrReq._idCounter = 1; //******************************************************************* /** * Instance methods/properties. */ SvrReq.prototype = { //******************************************************************* /** * Use this instance method to force a particular mime type for the * response data. Call this method before calling send(). * This method does nothing for MSIE browsers, since it does not support * the method on their XMLHTTPRequest implementation. * Ideally this method would not be needed if the HTTP server always sends * the correct mime type in the HTTP headers (with the appropriate charset * encoding). You should make sure the HTTP server is configured correctly * before using this method. */ overrideMimeType: function(mimeType) { this._mimeType = mimeType; }, //******************************************************************* /** * Adds a script src to a page. * * @param {String} url: The URL for the request. * * @param {String} type: Optional. * Type of request. The following values end up with the associated object * being passed back to the onOk method: * - 'xml': the responseXML (should be a DOM object). * - 'js': the responseText is passed to eval(), and the result of * eval() is passed to onOk. * - any other value (including null): the responseText is passed as * the first argument to onOk. * * @param {Object} listener: An object that contains function callbacks * for the different events: * - onOk(result): Callback function when request successfully completes. * See info about the 'type' parameter above for the format of the result object. * - onError(error): Optional. Recommended. Callback function when an error occurs. * The error/exception will be passed as the only parameter to this function. * - onTimeout(): Callback function when request takes longer than timeout specified in next parameter. * - timeout: integer as number of seconds to wait until timeout. * * Example listener object: * * var listener = { * onOk: function() { alert('loaded'); }, * onError: function(requestStatus, error) { alert('error: ' + error); }, * onTimeout: function() { alert('timeout'); }, * timeout: 30 * }; * * @param {String} postData: Optional.Data to be sent via an HTTP * POST. If it is specified, then HTTP POST will be used for the request. * If it is not, HTTP GET will be used. Other HTTP methods are not supported. * For other HTTP methods, use the static method SvrReq.createReqObject() * to get a plain XMLHTTPRequest object, and configure it as you need to. * * The format of the postData should be the normal url encoded form: * name=value&name=value&name=value * * @param {Object} headers: Optional. An object that has properties * that are HTTP header names, and values that correspond to the * values for the HTTP header names. Example: * * var headers = { * 'Content-Type': 'text/foo', * 'X-MySpecialHeader': 'bar' * }; */ send: function(url, type, listener, postData, headers) { var req = this.impl; this._type = type; this._listener = listener; //Set request headers. if (postData) req.setRequestHeader('Content-Type', SvrReq.postMimeType); if (headers) { for (var param in headers) req.setRequestHeader(param, headers[param]); } var stateChangeFunction; eval ('stateChangeFunction = function() { SvrReq._forwardStateChange("' + this._id + '"); }'); req.onreadystatechange = stateChangeFunction; //req.onreadystatechange = function() {SvrReq._forwardStateChange(this._id);}; req.open(postData ? 'POST' : 'GET', url, true); if (this._mimeType && typeof(this.impl.overrideMimeType) != 'undefined') this.impl.overrideMimeType(this._mimeType); if (this._listener.onTimeout && this._listener.timeout) { var timeoutFunction; eval('timeoutFunction = function() { SvrReq._inst["' + this._id + '"]._timeout(); }'); this._timeoutId = setTimeout(timeoutFunction, (this._listener.timeout * 1000)); } req.send(postData); }, //******************************************************************* /** * Use this instance method to cancel a request that has already been * sent, but before a response is received. If this method is called, * no callback handler will be called on the request. */ abort: function() { this._clearTimeout(); this.impl.abort(); }, //******************************************************************* _ok: function() { this._clearTimeout(); var result = (this._type == 'xml' ? this.impl.responseXML : this.impl.responseText); if (this._type == 'js') result = eval(result); this._listener.onOk(result); }, //******************************************************************* _error: function(error) { this._clearTimeout(); if (this._listener.onError) this._listener.onError(this.impl.status, error); else throw error; }, //******************************************************************* _timeout: function() { this._timeoutId = 0; this.abort(); this._listener.onTimeout(); }, //******************************************************************* _clearTimeout: function() { if (this._timeoutId) { clearTimeout(this._timeoutId); this._timeoutId = 0; } }, //******************************************************************* _stateChange: function() { var req = this.impl; if (req.readyState == 4) { //0 sent for cached responses? The req.status undefined is for Safari 2.0.1, //which seems to return that for local file requests. if (req.status == 200 || req.status == 0 || (typeof(req.status == 'undefined' && location.protocol == 'file:'))) { try { this._ok(); } catch (exception) { this._error(exception); } } else this._error('Invalid request status: ' + req.status); } } } //************************************************************************* //End SvrReq //*************************************************************************