WBM_XHR = {};

WBM_XHR = {
	
	_arr_progid : [
		'MSXML2.XMLHTTP.5.0',
		'MSXML2.XMLHTTP.4.0',
		'MSXML2.XMLHTTP.3.0',
		'MSXML2.XMLHTTP',
		'Microsoft.XMLHTTP'
	],
	
	_arr_http_header : [],
	
	_has_http_headers : false,
	
	_polling_freq : 50,
	
	//Collection of polling references to the polling mechanism in handleReadyState.
	_poll:{},
	
	//Queue of timeout values for each transaction callback with a defined timeout value.
	_timeOut : {},
	
	//A transaction counter that increments the transaction id for each transaction.
	_transaction_id : 0,

	
	createXMLHttpRequestObject : function(transactionId)
	{
		var _obj, _xmlhttp;
		try
		{
			// Mozilla / Safari
			_xmlhttp = new XMLHttpRequest();
			
			_obj = { conn: _xmlhttp, tId: transactionId };
		}
		catch(e)
		{
			// Internet Explorer
			for(var i = 0; i < this._arr_progid.length; ++i)
			{
				try
				{
					_xmlhttp = new ActiveXObject(this._arr_progid[i]);

					_obj = { conn: _xmlhttp, tId: transactionId };
					
					break;
				}
				catch(e)
				{
					
				}
			}
		}
		finally
		{
			return _obj;
		}
	},
	
	getXMLHttpRequestObject: function()
	{
		var _obj;
		var tId = this._transaction_id;
		
		try
		{
			_obj = this.createXMLHttpRequestObject(tId);
			
			if(_obj)
			{
				this._transaction_id++;
			}
		}
		catch(e)
		{
			
		}
		finally
		{
			return _obj;
		}
	}, 
	
	asyncRequest : function(method, url, callback, postData)
	{
		var _obj = this.getXMLHttpRequestObject();
		
		if(!_obj)
		{
			return null;
		}
		else
		{
			_obj.conn.open(method, url, true);
			
		    this.handleReadyState(_obj, callback);

		    if(postData)
			{
				this.addHeader('Content-Type','application/x-www-form-urlencoded');
			}

			//Verify whether the transaction has any user-defined HTTP headers and set them.
			if(this._has_http_headers)
			{
				this.setHeader(_obj);
			}

			_obj.conn.send(postData || null);
			
			return _obj;
		}
	},

	handleReadyState : function(_obj, callback)
	{
		var objXHR = this;
		
		if(callback && callback.timeout)
		{
			this._timeOut[_obj.tId] = window.setTimeout(function(){ objXHR.abort(_obj, callback, true); }, callback.timeout);
		}
		
		this._poll[_obj.tId] = window.setInterval
		(
			function()
			{
				if(_obj.conn && _obj.conn.readyState === 4)
				{
					window.clearInterval(objXHR._poll[_obj.tId]);
					delete objXHR._poll[_obj.tId];
		
					if(callback && callback.timeout)
					{
						delete objXHR._timeOut[_obj.tId];
					}
		
					objXHR.handleTransactionResponse(_obj, callback);
				}
			}, 
			this._polling_freq
		);
	},

	handleTransactionResponse : function(_obj, callback, isAborted)
	{
		var httpStatus, responseObject;

		if(!callback)
		{
			this.releaseObject(_obj);
			return;
		}

		try
		{
			if(_obj.conn.status !== undefined && _obj.conn.status != 0)
			{
				httpStatus = _obj.conn.status;
			}
			else
			{
				httpStatus = 13030;
			}
		}
		catch(e)
		{
			// 13030 is the custom code to indicate the condition -- in Mozilla/FF --
			// when the o object's status and statusText properties are
			// unavailable, and a query attempt throws an exception.
			httpStatus = 13030;
		}
		
		if(httpStatus >= 200 && httpStatus < 300 || httpStatus === 1223)
		{
			try
			{
				responseObject = this.createResponseObject(_obj, callback.argument);
				
				if(callback.success)
				{
					if(!callback.scope)
					{
						callback.success(responseObject);
					}
					else{
						// If a scope property is defined, the callback will be fired from
						// the context of the object.
						callback.success.apply(callback.scope, [responseObject]);
					}
				}
			}
			catch(e)
			{
				
			}
		}
		else
		{
			try
			{
				switch(httpStatus)
				{
					// The following case labels are wininet.dll error codes that may be encountered.
					// Server timeout
					case 12002:
					// 12029 to 12031 correspond to dropped connections.
					case 12029:
					case 12030:
					case 12031:
					// Connection closed by server.
					case 12152:
					// See above comments for variable status.
					case 13030:
						responseObject = this.createErrorObject(_obj.tId, callback.argument, (isAborted ? isAborted : false));
						
						if(callback.failure)
						{
							if(!callback.scope)
							{
								callback.failure(responseObject);
							}
							else
							{
								callback.failure.apply(callback.scope, [responseObject]);
							}
						}
						break;
					default:
						responseObject = this.createResponseObject(_obj, callback.argument);
						
						if(callback.failure)
						{
							if(!callback.scope)
							{
								callback.failure(responseObject);
							}
							else
							{
								callback.failure.apply(callback.scope, [responseObject]);
							}
						}
				}
			}
			catch(e)
			{
				
			}
		}

		this.releaseObject(_obj);
		responseObject = null;
	},

	createResponseObject : function(_obj, callbackArg)
	{
		var tmp_obj = {};
		var tmp_header_obj = {};

		try
		{
			var str_header = _obj.conn.getAllResponseHeaders();
			var header = str_header.split('\n');
			for(var i = 0; i < header.length; i++)
			{
				var delimitPos = header[i].indexOf(':');
				if(delimitPos != -1)
				{
					tmp_header_obj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
				}
			}
		}
		catch(e)
		{
			
		}
		
		tmp_obj.tId = _obj.tId;
		tmp_obj.status = _obj.conn.status;
		tmp_obj.statusText = _obj.conn.statusText;
		tmp_obj.responseHeader = tmp_header_obj;
		tmp_obj.allResponseHeaders = str_header;
		tmp_obj.responseText = _obj.conn.responseText;
		tmp_obj.responseXML = _obj.conn.responseXML;
		if(typeof callbackArg !== undefined)
		{
			tmp_obj.argument = callbackArg;
		}

		return tmp_obj;
	},

	createErrorObject : function(tId, callbackArg, isAborted)
	{
		var COMM_CODE = 0;
		var COMM_ERROR = 'communication failure';
		var ABORT_CODE = -1;
		var ABORT_ERROR = 'transaction aborted';

		var tmp_obj = {};

		tmp_obj.tId = tId;
		
		if(isAborted)
		{
			tmp_obj.status = ABORT_CODE;
			tmp_obj.statusText = ABORT_ERROR;
		}
		else
		{
			tmp_obj.status = COMM_CODE;
			tmp_obj.statusText = COMM_ERROR;
		}

		if(typeof callbackArg !== undefined)
		{
			tmp_obj.argument = callbackArg;
		}

		return tmp_obj;
	},

	abort : function(_obj, callback, isTimeout)
	{
		if(this.isCallInProgress(_obj))
		{
			_obj.conn.abort();
			window.clearInterval(this._poll[_obj.tId]);
			delete this._poll[_obj.tId];
			if(isTimeout)
			{
				delete this._timeOut[_obj.tId];
			}
		
			this.handleTransactionResponse(_obj, callback, true);
		
			return true;
		}
		else
		{
			return false;
		}
	},

	releaseObject : function(_obj)
	{
		//dereference the XHR instance.
		_obj.conn = null;
		//dereference the connection object.
		_obj = null;
		
	}, 

	isCallInProgress : function(_obj)
	{
		if(_obj.conn)
		{
			return _obj.conn.readyState != 4 && _obj.conn.readyState != 0;
		}
		else
		{
			return false;
		}
	},

	setHeader : function(_obj)
	{
		for(var label in this._arr_http_header)
		{
			_obj.conn.setRequestHeader(label, this._arr_http_header[label])
		}
		delete this._arr_http_header;
		
		this._arr_http_header = [];
		this._has_http_headers = false;
	},

	addHeader : function(label, value)
	{
		if(this._arr_http_header[label] === undefined)
		{
			this._arr_http_header[label] = value;
		}
		else
		{
			this._arr_http_header[label] =  value + "," + this._arr_http_header[label];
		}

		this._has_http_headers = true;
	},
	
	//Member to modify the default polling freq.
	setPollingFreq : function(i)
	{
		if(typeof i == 'number' && isFinite(i))
		{
			this._polling_freq = i;
		}
	},

	// add another progid to the _arr_progid
	addProgID : function(progid)
	{
		this._arr_progid.unshift(progid);
	}
};