/************************************************************************(c) 2009 by BookRags, Inc.
A simple AJAX class library.

TO USE:
1. Create a new BasicAJAX object. This object will probably be a single global
object, or perhaps a pool of objects.

2. Call get or post, supplying it with a URL, the postdata (if it's post), and
the name of a callback function to receive the server response.

 Date	Who	Changes
------- --- ---------------------------------------------------------------------------------------
06jan2009 jls Cloned from Jenny Simonds' Mini-MessageBoard project.
24aug2009
**************************************************************************************************/
// REQUIRES: <script src="brutils.1.js"></script>
// REQUIRES: <script src="json.js"></script>

// Class variables.
BasicAJAX.urlLoading = "http://images.bookrags.com/qa/loading-sm.gif";


/**************************************************************************************************
Constructor.

PARAMETERS:
	urlLoading=(use default)	The url of the loading image.
**************************************************************************************************/
function BasicAJAX (p_urlLoading)
	{
	this.isDebug = false;
	this.urlLoading = (arguments.length >= 1) ? p_urlLoading : BasicAJAX.urlLoading;
	this.imgLoading = new Image ();
	this.imgLoading.src = this.urlLoading;

	this.url = "";
	this.sPostData = "";
	this.xmlhttp = null;
	this.aaHeaders = {};
	this.aaHeaders['Content-Type'] = 'application/x-www-form-urlencoded';
	}

/****************************************************************************
oAJAX.get (url, onResponse, oExtraParams);

This sends a GET request to the server, & calls the callback function when it
gets a response.

The callback function is of the form: onAjaxRs (oAjax), where oAjax is the
reference to this object.

PARAMETERS:
	url				The URL to send the request to. This should not be
					url-encoded.
	onResponse		The callback to receive the response. Depending on the 1st
					non-whitespace character in the received text, the callback's
					parameter is a DOM fragment, JSON object, or plaintext.

	{// optional named params.
	onSent			The callback for when the request has been successfully sent.
	onFail			The callback for when there's an error.
	isAsync=true	Whether the request will be async or sync.
	reportErr=false	true to throw up an alert msg on an xmlhttp exception, else
					false to silently call the callback function & let it handle
					the condition.
	idLoading=null	An HTML element to overlay with the "busy" graphic until the
					request is satisfied (one way or another).
	urlLoading=this.urlLoading
					The graphic to show while loading.
	}

Ex.: g.oAjax.get ("docmds.php?cmd=update&id=1234", bindMethodToCallback(this,"onAjaxOK")),
				   {onFail: bindMethodToCallback(this,"onAjaxFail",1234)});

RETURNS:
	True if the request was sent OK, else false if not or if couldn't open XHR.
****************************************************************************/
BasicAJAX.prototype.get = function (p_url, p_onResponse, p_oxp)
	{
	// Parameters.
	this.url		= p_url;
	this.sPostData	= encodeURI(p_sPostData);
	this.onResponse	= p_onResponse;
	this.onSent		= bru.getExtraParam (p_oxp, "onSent",	  null);
	this.onFail		= bru.getExtraParam (p_oxp, "onFail",  null);
	this.isAsync	= bru.getExtraParam (p_oxp, "isAsync",	  true);
	this.bReportErr	= bru.getExtraParam (p_oxp, "reportErr", false);
	this.idLoading	= bru.getExtraParam (p_oxp, "idLoading",	"");
	this.elmLoading	= document.getElementById(this.idLoading);
	this.urlLoading	= bru.getExtraParam (p_oxp, "urlLoading", this.urlLoading);

	if (!this.xmlhttp)
		{this._getXMLHTTPRequest ();
		}
	if (!this.xmlhttp)
		{return false;
		}

	try {
		this.xmlhttp.abort (); // If we're already doing a request via this ajax object, it's a moot point now.

		this.xmlhttp.open ("GET", this.url, true);
		if (this.xmlhttp.overrideMimeType)
			{this.xmlhttp.overrideMimeType('text/xml');
			}
		for (var sHdrName in this.aaHeaders)
			{this.xmlhttp.setRequestHeader (sHdrName, this.aaHeaders[sHdrName]);
			}
		this.xmlhttp.onreadystatechange = bru.bindMethodToEvent(this,"_onReadyStateChange");
		this.xmlhttp.send ();

		if (this.idLoading)
			{this._showLoadingImg ();
			}
		}
	catch (err)
		{
		if (this.bReportErr)
			{alert ("BasicAJAX: GET request exception: "+err);
			}
		this._hideLoadingImg ();
		if (this.onFail)
			{this.onFail(this.xmlhttp.statusText);
			}
		return false;
		}

	return true;
	}

/*****************************************************************************
oAJAX.post (url, sPostData, onResponse, oExtraParams);

This sends a POST request to the server, & calls the callback function when it
gets a response. The postdata should not be url-encoded. This function will
encode it itself.

The callback function is of the form: onAjaxRs (oAjax), where oAjax is the
reference to this object.

PARAMETERS:
	url				The URL to send the request to. This should not be
					url-encoded.
	sPostData		The POST data to send to the server. (Not urlencoded.)
	onResponse		The callback to receive the response. Depending on the 1st
					non-whitespace character in the received text, the callback's
					parameter is a DOM fragment, JSON object, or plaintext.

	{// optional named params.
	onSent			The callback for when the request has been successfully sent.
	onFail		The callback for when there's an error.
	isAsync=true	Whether the request will be async or sync.
	reportErr=false	true to throw up an alert msg on an xmlhttp exception, else
					false to silently call the callback function & let it handle
					the condition.
	idLoading=null	An HTML element to overlay with the "busy" graphic until the
					request is satisfied (one way or another).
	urlLoading=this.urlLoading
					The graphic to show while loading.
	}

Ex.: g.oAjax.post ("docmds.php", "cmd=update&id=1234", onUpdatedRs(1234),
				   {onFail: bindMethodToEvent(this,"onUpdatedFail",1234)});

RETURNS:
	True if the request was sent OK, else false if not or if couldn't open XHR.
*****************************************************************************/
BasicAJAX.prototype.post = function (p_url, p_sPostData, p_onResponse, p_oxp)
	{
	// Parameters.
	this.url		= p_url;
	this.sPostData	= encodeURI(p_sPostData);
	this.onResponse	= p_onResponse;
	this.onSent		= bru.getExtraParam (p_oxp, "onSent",	  null);
	this.onFail		= bru.getExtraParam (p_oxp, "onFail",	  null);
	this.isAsync	= bru.getExtraParam (p_oxp, "isAsync",	  true);
	this.bReportErr	= bru.getExtraParam (p_oxp, "reportErr", false);
	this.idLoading	= bru.getExtraParam (p_oxp, "idLoading",	"");
	this.elmLoading	= document.getElementById(this.idLoading);
	this.urlLoading	= bru.getExtraParam (p_oxp, "urlLoading", this.urlLoading);
	this.imgLoading = new Image ();

	if (!this.xmlhttp)
		{this._getXMLHTTPRequest ();
		}
	if (!this.xmlhttp)
		{return false;
		}

	try {
		this.xmlhttp.abort (); // If we're already doing a request via this ajax object, it's a moot point now.

		this.xmlhttp.open ("POST", this.url, true);
//		if (this.xmlhttp.overrideMimeType)
//			{this.xmlhttp.overrideMimeType('text/xml');
//			}
		for (var sHdrName in this.aaHeaders)
			{
//alert ('sHdrName='+sHdrName+'\nvalue='+this.aaHeaders[sHdrName]+'\npostdata='+this.sPostData);
			this.xmlhttp.setRequestHeader (sHdrName, this.aaHeaders[sHdrName]);
			}
		this.xmlhttp.onreadystatechange = bru.bindMethodToEvent(this,"_onReadyStateChange");
		this.xmlhttp.send (this.sPostData);

		if (this.idLoading)
			{this._showLoadingImg ();
			}
		}
	catch (err)
		{
		if (this.bReportErr)
			{alert ("BasicAJAX: POST request exception: "+err);
			}
		this._hideLoadingImg ();
		if (this.onFail)
			{this.onFail(this.xmlhttp.statusText);
			}
		return false;
		}

	return true;
	}

/****************************************************************************/
BasicAJAX.prototype._getXMLHTTPRequest = function ()
	{
	if (!this.xmlhttp)
		{
		if (window.XMLHttpRequest)
			{this.xmlhttp = new XMLHttpRequest ();
			}
		else if (window.ActiveXObject)
			{
			try
				{this.xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
				}
			catch (err1)
				{
				try
					{this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
					}
				catch (err2)
					{return null;
					}
				}
			}
		}
	return this.xmlhttp;
	}


/****************************************************************************/
BasicAJAX.prototype._showLoadingImg = function ()
	{
	var o;

	if (!this.idLoading || !this.urlLoading || !this.elmLoading)
		{return;
		}

//	this.imgLoading = new Image ();
	this.imgLoading.src = this.urlLoading;
	this.imgLoading.style.position = "absolute";

	// Add the image to the notify element's first ancestor that isn't hidden.
	o = this.elmLoading.parentNode;
	while (o)
		{
		if (o.nodeType==1 && bru.getComputedStyle(o).visibility=="visible" && bru.getComputedStyle(o).display!="none")
			{
			o.appendChild (this.imgLoading);
			break;
			}
		o = o.parentNode;
		}

	this.imgLoading.style.top  = bru.getTrueOffsetTop (this.elmLoading)+"px";
	this.imgLoading.style.left = bru.getTrueOffsetLeft(this.elmLoading)+"px";
	}

/****************************************************************************/
BasicAJAX.prototype._hideLoadingImg = function (p_bSuccess)
	{
	if (this.imgLoading)
		{if (this.imgLoading.parentNode)
			{this.imgLoading.parentNode.removeChild (this.imgLoading);
			}

		this.imgLoading.src = "";
		}
	}


/****************************************************************************/
BasicAJAX.prototype._onReadyStateChange = function ()
	{
	switch (this.xmlhttp.readyState)
		{
	case 2:
		// onSent callback:
		if (this.onSent)
			{this.onSent();
			}
		break;

	case 4:
		if (this.isDebug)
			{alert ("HTTP code = " + this.xmlhttp.status + " ("+this.xmlhttp.statusText+")\n\Response is:\n\n"+this.xmlhttp.responseText);
			}
		if (this.xmlhttp.status>200)
			{
			// onFail callback:
			this._hideLoadingImg ();
			if (this.onFail)
				{this.onFail(this.xmlhttp.status + ": " + this.xmlhttp.statusText);
				}
			if (this.bReportErr)
				{alert ("Error getting response: status = "+this.xmlhttp.status+" ("+this.xmlhttp.statusText+")");
				}
			}
		else if (this.xmlhttp.status==200||this.xmlhttp.status==0)
			{
			this._hideLoadingImg ();

			// onResponse callback:
			if (this.onResponse)
				{
				if (this.xmlhttp.responseText.search(/^\s*</) > -1)
					{// It's XML/HTML...
					this.onResponse (this.xmlhttp.responseXML);
					}
				else if (this.xmlhttp.responseText.search(/^\s*[{\[]/) > -1)
					{// It's JSON...
					// See http://www.ietf.org/rfc/rfc4627.txt?number=4627 section 6
//					var responseJSON = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(this.xmlhttp.responseText.replace(/"(\\.|[^"\\])*"/g, '')))
//									   && eval('(' + this.xmlhttp.responseText + ')');
//alert("BasicAJAX._onReadyStateChange - \n\nRaw:\n"+this.xmlhttp.responseText+"\n\nEscaped:\n"+responseJSON);
//					sResponseText = this.xmlhttp.responseText.replace(/([\b\t\n\v\f\r])/g, "\\$1");
					sResponseText = this.xmlhttp.responseText;
					sResponseText = sResponseText.replace(/\t/g, ""); // "\\t");
					sResponseText = sResponseText.replace(/\n/g, ""); // "\\n");
					sResponseText = sResponseText.replace(/\v/g, ""); // "\\v");
					sResponseText = sResponseText.replace(/\f/g, ""); // "\\f");
					sResponseText = sResponseText.replace(/\r/g, ""); // "\\r");
//					sResponseText = sResponseText.replace(/\\\'/g, "'"); // "\\'");
//					sResponseText = sResponseText.replace(/\\\"/g, '\"'); // "\\"");
//alert("BasicAJAX._onReadyStateChange - \n\nRaw:\n"+this.xmlhttp.responseText+"\n\nEscaped:\n"+sResponseText);
					var responseJSON = eval("("+sResponseText+")");
//responseJSON = JSON.parse (sResponseText);
					this.onResponse (responseJSON);
					}
				else
					{// It's plaintext or garbage...
					this.onResponse (this.xmlhttp.responseText);
					}
				}
			}
		break;
		}
	}

/****************************************************************************/

