var AjaxTCR = {};



AjaxTCR.comm = {

/* readyState constants as defined by w3c */

 UNSENT : 0,

 OPEN :   1,

 SENT : 2,

 LOADING : 3,

 DONE : 4,

  

/* Default Request Content Type */

DEFAULT_CONTENT_TYPE : "application/x-www-form-urlencoded",



/* Default timeout in ms */

DEFAULT_TIMEOUT : false,

 

/* Default number of retries if retrying requests */

DEFAULT_RETRIES : false,

 

/* Default time to revisit our progress callback if monitoring progress */

DEFAULT_PROGRESS_INTERVAL : 1000,



DEFAULT_REQUEST_SIGNATURE : "X-Signature",

 

/* the request id counter */	

 _requestID : 0,  	



/* request counter shows outstanding requests */

_requestsOutstanding : 0,

 

/* the statuses for possible network errors */

/* 3507 = library error flag */

 _networkErrorStatus : new Array(0, 408, 504, 3507, 12002, 12007, 12029, 12030, 12031, 12152),

 

 

 /*****************************************  GETTERS/SETTERS ***********************************/

								

setDefault : function(option, value){

	AjaxTCR.comm[option] = value;

},



getDefault : function(option){

	return AjaxTCR.comm[option]

},	

 	 

 /* 

  * _createXHR - private wrapper function from chapter 3 

  */

 _createXHR : function() { 

                          

 						  try { return new XMLHttpRequest(); } catch(e) {}

                          try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) {}

                          try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e) {}

                          try { return new ActiveXObject("Msxml2.XMLHTTP"); } 	  catch (e) {}

                          try { return new ActiveXObject("Microsoft.XMLHTTP"); }  catch (e) {}

						  	           

                          return null;

		 				 },



/*

 * sendRequest(url, object of communications options) - public method to create an Ajax request

 * 

 */

 sendRequest : function (url,options) {

 	

	var request = new Object();

		

	/* increment our requestId number */  

	request.requestID = ++AjaxTCR.comm._requestID;

	

	/* basic communication defaults */

	request.method = "GET";

    request.async = true;

	request.preventCache = false;

	request.requestContentType = AjaxTCR.comm.DEFAULT_CONTENT_TYPE;

	request.requestContentTransferEncoding = "";

	request.payload = "";



	/* header management */

	request.headers = new Array();

	request.supressHeaders = false;



	/* standard callbacks */

	request.onSuccess = function(){};

	request.onFail = function(){};

	

	/* callbacks associated with readyState changes */

	request.onCreate = null;

	request.onOpen = null;

	request.onSent = null;

	request.onLoading = null;

	request.onReceived = null;



	/* communication status flags */    

    request.abort = false;

	request.inProgress = true;

	request.received = false;

	

	/* progress management */

	request.showProgress = false;

	request.progressInterval = AjaxTCR.comm.DEFAULT_PROGRESS_INTERVAL;

	request.onProgress = function (){};

	request.progressTimerID = null;

	

	/* timeout parameters */

	request.timespent = 0;

	request.timeout = AjaxTCR.comm.DEFAULT_TIMEOUT;

	request.onTimeout = function(){};

	request.timeoutTimerID = null;



    /*  retry parameters */

	request.retries = AjaxTCR.comm.DEFAULT_RETRIES;

	request.retryCount = 1;

	request.onRetry = function (){};

	

	/* sequencing */

	request.inQueue = false;

	request.responseQueueID = 0;

	request.enforceOrder = false;



	/* cache management */



	request.cacheResponse = false;

	request.fromCache = false;

	

	/* Prefetch */

	request.onPrefetch = function(){};

	request.isPrefetch = false;



    /* payload serialization */

	request.serializeForm = null;

	request.hasFile = false;



    /* output handling */

	request.outputTarget = null;

	request.useRaw = true;

	

	/* transmission type */

	request.oneway = false;

	

	/* authentication */

 	request.username = null;

 	request.password = null;

 	

 	/* security */

 	request.requestSignature = AjaxTCR.comm.DEFAULT_REQUEST_SIGNATURE;

 	request.signRequest = null;

 	request.signedResponse = false;



	

    /* apply options defined by user */

    for (option in options)

      request[option] = options[option];

	  

	if (request.isPrefetch)

		request.cacheResponse = true;

	  

	/* Serialize the given form if request.serialize is set */

	if (request.serializeForm)

	{

		/* Serialize given form */

		var newPayload = AjaxTCR.data.serializeForm(request.serializeForm,request.requestContentType);

		

		/* check to see if we have a fileupload situation */

		if (typeof(newPayload) == "string" && newPayload == "fileupload")

			request.hasFile = true;

		else

		{

			/* Check to see if payload exists */

			if (request.payload)

			{

				/* If payload is an object, use serializeObject otherwise append to end of the new payload */

				if (typeof(request.payload) == "object") 

					newPayload = AjaxTCR.data.serializeObject(newPayload, request.payload, request.requestContentType);

				else if (request.requestContentType == AjaxTCR.comm.DEFAULT_CONTENT_TYPE)

					newPayload += "&" + request.payload;  

			}

			

			request.payload = newPayload;

			

			/* Get all values into string format */

			if (request.requestContentType == "application/json")

	  			request.payload = AjaxTCR.data.encodeJSON(request.payload);

			else if (request.requestContentType == "text/xml")

				request.payload = AjaxTCR.data.serializeXML(request.payload);

				

			/* Encode it in base64 if that's set */

			if (request.requestContentTransferEncoding == "base64")

				request.payload = AjaxTCR.data.encode64(request.payload);

		}

	}

  

  	/* If there is a file, we need to handle differently */

	if (request.hasFile)

		AjaxTCR.comm._sendFormWithFile(request);

	else

	{

		/* address payload depending on method */

	    if (request.method.toUpperCase() == "GET")

	      request.url = url + "?" + request.payload;

		else

		  request.url = url;

		 

		if (request.method.toUpperCase() == "POST")

	      request.postBody = request.payload;

		else

		  request.postBody = null;

	    	

		

		/* Add a queueID if necessary */

		if (request.enforceOrder)

			request.responseQueueID = AjaxTCR.comm.queue._responseQueue.maxID++;

		

		var cachedResponse = null;

		/* Check if the item is in the cache first */

		if (request.cacheResponse)

		{

			/* Check to see if we have a key for our cache */

			if (request.cacheKey == undefined)

				request.cacheKey = request.url;

			

			cachedResponse = AjaxTCR.comm.cache.get(request.cacheKey);

			if (cachedResponse)

				AjaxTCR.comm.cache._handleCacheResponse(request, cachedResponse);

		}

		

	    /* invoke the request */

		if (!cachedResponse)

			AjaxTCR.comm._makeRequest(request);

	}



    /* return object for local control */

    return request;	

 },		

 

 

 /*

  * abortRequest(requestObj)

  * 

  * Public method that will abort any passed request object and clean up

  * any timers for showing requqest state

  * 

  */

 abortRequest : function(request) {

 	                                 /* set the abort flag */

 	                                 request.abort = true;

									 

									 /* clear inProgress flag */

 	                                 request.inProgress = false;

									 

									 /* abort the request */	

								     request.xhr.abort();

									 

									 /* decrement outstand request count */

									 AjaxTCR.comm._requestsOutstanding--;

									 

									 /* clear any timeout timers */

									 clearTimeout(request.timeoutTimerID);

									 request.timeoutTimerID = null;

									 

									 /* stop showing progress */

		                             if (request.progressTimerID)

									 {

										clearTimeout(request.progressTimerID);

										request.progressTimerID = null;

									 }

										

									/* Remove Progress Indicators */

									if (request.statusIndicator)

										AjaxTCR.comm._removeProgressStatus(request.statusIndicator);

										

								   },



/*

 * _makeRequest(requestObj) 

 * 

 * Private method that actually creates XHR, binds callbacks and sends request

 * 

 */



_makeRequest : function (request) {		



									/* make basic XHR */

									request.xhr = AjaxTCR.comm._createXHR();

									if (!request.xhr)

      								  { /* raise exception */

	   									return;

	 								  } 

									/* Increment total requests */

									AjaxTCR.comm.stats._commResults.totalRequests++;

									

									/* Display status and start Progress Callback */

									if (!request.oneway)

										AjaxTCR.comm._initSend(request);

									  

									/* Call back for ready state 0 if set */

									if (request.onCreate)

									  request.onCreate(request);

										 

	                              	/* open the request */

	                                request.xhr.open(request.method, request.url, request.async, request.username, request.password);

									

									/* clear an abort flag in case this is a retry */

									request.abort = false;

									

									/* set headers indicating we did this with Ajax and what 

									   our transaction id is */

									if (!request.supressHeaders)

									  {

									   request.xhr.setRequestHeader("X-Requested-By","XHR");

									   request.xhr.setRequestHeader("X-Request-Id",request.requestID);

									  }

									  

									/* set header(s) for POST */

									if (request.method.toUpperCase() == "POST")

									  {

									   request.xhr.setRequestHeader("Content-Type", request.requestContentType);

									   if (request.requestContentTransferEncoding != "")

										 request.xhr.setRequestHeader("Content-Transfer-Encoding", request.requestContentTransferEncoding);

									  }

									  

									/* Prevent Caching if set */

									if (request.preventCache)

										request.xhr.setRequestHeader("If-Modified-Since", "Wed, 15 Nov 1995 04:58:08 GMT");

									  

									/* set user defined headers */

									request.headerObj = {};

									for (var i=0; i<request.headers.length;i++)

									{

										if (request.headers[i].name.toUpperCase() == "COOKIE")

											document.cookie = request.headers[i].value;

										else if(request.headerObj[request.headers[i].name] === undefined)

											request.headerObj[request.headers[i].name] = request.headers[i].value;

										else	

											request.headerObj[request.headers[i].name] =  request.headers[i].value + "," + request.headerObj[request.headers[i].name];

									}

									

									for (var header in request.headerObj)

										request.xhr.setRequestHeader(header, request.headerObj[header]);

									

									/* Set signature header */

 									 if (request.signRequest)

 									 	request.xhr.setRequestHeader(request.requestSignature, request.signRequest);

	

									if (!request.oneway)

									{

										/* bind the success callback */

										request.xhr.onreadystatechange = function () {AjaxTCR.comm._handleReadyStateChange(request);};

										

										/* set a timeout if set */

										if (request.async && request.timeout &&  request.timeoutTimerID == null)

		  									 request.timeoutTimerID = window.setTimeout( function(){AjaxTCR.comm._timeoutRequest(request);}, request.timeout);

		

	    							}

									

									/* send the request */

									request.xhr.send(request.postBody);

								 },

 /*

  * initSend(requestObject)

  * 

  * Private method called to init items that should be initialized no matter what communication technique is used.

  *  

  */

 

 _initSend : function(request){

									/* Set Progress Indicators */ 

									if (request.statusIndicator) 

										request.statusIndicator.element = AjaxTCR.comm._setProgressStatus(request.statusIndicator);					

									

									/* set the time spent in case this is a retry */

									request.timespent = 0;

									

									/* set progress indicator to show something nearly every second */

									if (request.showProgress && request.progressTimerID == null)

										 request.progressTimerID = window.setTimeout( function(){AjaxTCR.comm._progressRequest(request);}, request.progressInterval);

									

									/* Record start time of request */

									request.startTime = (new Date()).getTime();

									

									/* increment requests outstanding */

									AjaxTCR.comm._requestsOutstanding++;

									

						},



 /*

  * sendFormWithFile(requestObject)

  * 

  * Private method called to submit form if there is a file in it.

  *  

  */

 

 _sendFormWithFile : function(request) { 

 										 /* Display status and start Progress Callback */

										 AjaxTCR.comm._initSend(request);

										

										 /* Increment total requests */

										 AjaxTCR.comm.stats._commResults.totalRequests++;

										

 										 var iframeID = "AjaxTCRIframe_" + request.requestID;

										 

										 /* IE does not handle document.createElement("iframe"); */

										 if(window.ActiveXObject)

											var iframe = document.createElement('<iframe id="' + iframeID + '" name="' + iframeID + '" />');

										 else

										 { 

 										 	var iframe = document.createElement("iframe");

											iframe.id = iframeID; 

										    iframe.name = iframeID;

										 }

										 

										 iframe.style.height = "1px";

										 iframe.style.visibility = "hidden";

										 

										 document.body.appendChild(iframe);

										 

										 var callback = function(){AjaxTCR.comm._handleIFrameResponse(request, iframe);};

		

										 /* IE does not recognize iframe.onload */

										 if(window.attachEvent)	

										 	iframe.attachEvent('onload', callback);

										 else

											iframe.addEventListener('load', callback, false);

		

		

										 request.serializeForm.target = iframe.id;

										 request.serializeForm.submit();

									   },

									   

									   

_handleIFrameResponse : function(response, iframe){

													response.xhr = {};

													response.xhr.responseText = null;

													response.xhr.status = 200;

													

													

													if (iframe.contentWindow.document.body)

														response.xhr.responseText = iframe.contentWindow.document.body.innerHTML;

													

													if (iframe.contentWindow.document.XMLDocument)

														response.xhr.responseXML = iframe.contentWindow.document.XMLDocument;

													else

														response.xhr.responseXML = iframe.contentWindow.document;

										

													AjaxTCR.comm._handleResponse(response);

													

													/* This line causes the page to not stop spinning */

													//document.body.removeChild(iframe);

											      },



 /*

  * timeoutRequest(requestObject)

  * 

  * Private method called if timeout set after 5000 ms or a user defined time.

  * Uses public abortRequest method to clean up timers and invokes any special timeout

  * callback that may be defined.

  *  

  */

 

 _timeoutRequest : function(request) { 

                                      /* make sure it is a proper time to abort */

                                      if (request.xhr.readyState != AjaxTCR.comm.DONE && 

									      request.xhr.readyState != AjaxTCR.comm.UNSENT)

                                       {

									   	/* abort the request */

		                                AjaxTCR.comm.abortRequest(request);

										

										/* Increment total timeouts */

										AjaxTCR.comm.stats._commResults.totalTimeouts++;

										

										/* do we need to retry? */

										if (request.retries)

										  AjaxTCR.comm._retryRequest(request); 	

										else  	  

										{

											if (request.statusIndicator) 

												AjaxTCR.comm._setErrorStatus(request.statusIndicator);

										  request.onTimeout(request);  /* invoke any timeout callback */

										  AjaxTCR.comm.queue._checkRequestQueue(request);

										}											

                                       }

                                     },



/*

  * progressRequest(requestObject)

  * 

  * Private method called each second if showProgress is set.

  * Updates public timespent variable and invokes any special progress

  * callback that may be defined.

  *  

  */

 

_progressRequest: function(request){

									if (!request.abort && !request.received)

   									  {



										request.timespent =  Math.round((request.timespent + (parseInt(request.progressInterval) / 1000)) * 1000) / 1000;

										request.onProgress(request);

										/* Yes it is ridiculous that we have to clear the timeout that we are in a callback for, but such is IE */

										clearTimeout(request.progressTimerID);

										request.progressTimerID = null;

										request.progressTimerID = window.setTimeout( function(){AjaxTCR.comm._progressRequest(request);}, request.progressInterval);

									  }

								   },

									 

_retryRequest : function (request) {

	 									   /* up our retry count */

										   request.retryCount++; 	

										  

										   /* make sure we aren't done retrying */

										   if (request.retryCount <= request.retries)

										   {

											  if (request.statusIndicator) 

												AjaxTCR.comm._setRetryStatus(request.statusIndicator);

												

											 /* Increment total retries */

										     AjaxTCR.comm.stats._commResults.totalRetries++;

										     AjaxTCR.comm._makeRequest(request);

											 request.onRetry(request);

										   }

										   else /* stop trying and perform callback */

										   {

										    if (request.statusIndicator) 

												AjaxTCR.comm._setErrorStatus(request.statusIndicator);

											request.onTimeout(request);

											AjaxTCR.comm.queue._checkRequestQueue(request);

										   }

										 },



/*

  * handleResponse(requestObject)

  * 

  * Private method called after response comes back no matter what method of communication

  */								

_handleResponse : function(response){

										/* Record end time of request */

										response.endTime = (new Date()).getTime();

										

										/* set a received flag to ensure you don't perform a progress callback after received. */

										response.received = true;

										

										 /* clear any timeouts */

		                                 if (response.timeoutTimerID)

										 {

		                                   clearTimeout(response.timeoutTimerID);

										   response.timeoutTimerID = null;

										 }

			   

			                             /* clear our progress indicator */

			                             if (response.progressTimerID)

										 {

										   clearTimeout(response.progressTimerID);

										   response.progressTimerID = null;

										 }

										   

										 /* decrement outstand request count */

										 AjaxTCR.comm._requestsOutstanding--;

										 

										 /* Cache Response */

										if (!response.fromCache && response.cacheResponse && response.xhr.status == 200  && !response.fail)

											AjaxTCR.comm.cache.add(response.cacheKey, response.xhr.responseText);

										

										/* Remove Progress Indicators */

										if (response.statusIndicator)

										{

											AjaxTCR.comm._removeProgressStatus(response.statusIndicator);

											AjaxTCR.comm._removeRetryStatus(response.statusIndicator);

										}

											

										/* Check to see if we need to wait for another request */

										/* Otherwise just handle callbacks */

										 if (response.enforceOrder)

										   AjaxTCR.comm.queue._handleQueue(response);

										 else

										   AjaxTCR.comm._handleCallbacks(response);

										   

										 /* If Request Queue is being used, send next request */

										AjaxTCR.comm.queue._checkRequestQueue(response);

									},

/*

  * handleCallbacks(requestObject)

  * 

  * Private method called after response comes in to run numerous callbacks.

  * Checks http and response status to see which callbacks should be employed.

  */

 									 

_handleCallbacks : function(response) {

										 /* Danger: Firefox problems so we try-catch here */

										 try { response.httpStatus = response.xhr.status; response.httpStatusText = response.xhr.statusText} catch(e) {response.httpStatus=3507;response.httpStatusText="Unknown Loss";}

										 

										 /* clear inProgress flag */

 	                                 	 response.inProgress = false;

										

										 /* Check if user wants to automatically consume output */

										 if (response.outputTarget && response.useRaw)

										 {

											var outputTarget = response.outputTarget;

											if (outputTarget && typeof(outputTarget) == "string")

												outputTarget = document.getElementById(outputTarget);

										 

										 	if (response.fail)

 												outputTarget.innerHTML = response.fail;

 											else

 										 		outputTarget.innerHTML = response.xhr.responseText;

										 }

											

										

										 /* check to see if the user wants a specific callback for this request */

										 if (response["on" + response.httpStatus])

 										 	response["on" + response.httpStatus](response);

										 

										 /* see if it is one of our retry statuses */

										 if (response.retries)

										 {

											for (var i=0;i<AjaxTCR.comm._networkErrorStatus.length;i++)

											{

												if (response.httpStatus == AjaxTCR.comm._networkErrorStatus[i])

												{

													AjaxTCR.comm._retryRequest(response);

													return;

												}

											}

										 }

										 

										

										/* call either success or fail callback */

										if (response.httpStatus == 200)

										{

											/*if they specified expected content type, we check for that.*/

										    if (response.fail)

 												AjaxTCR.comm._handleFail(response, response.fail);

 										    else if (response.responseContentType)

										    {

												var responseContentType = response.xhr.getResponseHeader("Content-Type");

												responseContentType = responseContentType.substring(0, responseContentType.indexOf(";"));

												if (responseContentType != response.responseContentType)

										            AjaxTCR.comm._handleFail(response, "Wrong Content-Type: " + responseContentType );

												else if (response.responseContentType == "text/xml" && (response.xhr.responseXML == null || response.xhr.responseXML.childNodes.length == 0 || response.xhr.responseXML.childNodes[0].nodeName == "parsererror"))

													AjaxTCR.comm._handleFail(response, "Invalid XML Data");

												else

													AjaxTCR.comm._handleSuccess(response);

											}

											else											

												AjaxTCR.comm._handleSuccess(response);

										}

										else

										 	AjaxTCR.comm._handleFail(response, response.httpStatus + " " + response.httpStatusText);

										

										

										

										  

									    /* clear out the response */

										response = null;

                               	      },

									  

 /*

  * handleFail(requestObject)

  * 

  * Private method to call fail callback

  *  

  */

 

 _handleFail : function(response, message) {

											/* Increment total fails */

										    AjaxTCR.comm.stats._commResults.totalFails++;

											

											/* Save fail details */

											var fail = {};

											fail.url = response.url;

											fail.status = response.httpStatus;

											fail.message = message;

											AjaxTCR.comm.stats._commResults.requestFails.push(fail);

											

											if (request.statusIndicator) 

												AjaxTCR.comm._setErrorStatus(request.statusIndicator);

												

											response.onFail(response, message);

										   },

										   

/*

  * handleSuccess(requestObject)

  * 

  * Private method to handle success responses

  *  

  */

 

 _handleSuccess : function(response) {

										/* Increment total success */

									    AjaxTCR.comm.stats._commResults.totalSuccess++;

										if (response.isPrefetch)

											response.onPrefetch(response);

										else

											response.onSuccess(response);

									 },

 

 /*

  * handleReadyStatueChange(requestObject)

  * 

  * Private method to check for response status, clear any timeouts and invoke success callback

  *  

  */

 

 _handleReadyStateChange : function(response) { 	

		         					  /* check if abort flag is set, if so bail out */	

		                              if (response.abort)

		                                 return; 

										 

									   /* Check each readyState */

									  if (response.xhr.readyState == AjaxTCR.comm.OPEN && response.onOpen)

									  	 response.onOpen(response);

								      else if (response.xhr.readyState == AjaxTCR.comm.SENT && response.onSent)

									  	 response.onSent(response);

									  else if (response.xhr.readyState == AjaxTCR.comm.LOADING && response.onLoading)

									  {

										 //update status

										 if (response.statusIndicator)

										 	AjaxTCR.comm._setReceivingDataStatus(response.statusIndicator); 

									  	 response.onLoading(response);

									  }

 	                                  else if (response.xhr.readyState == AjaxTCR.comm.DONE) 

									  {

										if (response.signedResponse)

 										{

 											var signature = response.xhr.getResponseHeader("Content-MD5");

 											var verifySignature = AjaxTCR.data.encodeMD5(response.xhr.responseText);

 											if (signature != verifySignature)

 												response.fail = "Response Packet Compromised."; 											 

 										}

 										

 										if (response.onReceived && !response.fail)

											response.onReceived(response);

											

	                                    AjaxTCR.comm._handleResponse(response);

									  }

										

									 },



_setStatus : function(statusIndicator){

								if (statusIndicator.target)

								{

									if (typeof(statusIndicator.target) == "string")

										statusIndicator.target = document.getElementById(statusIndicator.target);

											

								    if (statusIndicator.type == "text")

								    {

								        var statusDiv = document.createElement("div");

										if (statusIndicator.sending)

											statusDiv.innerHTML = statusIndicator.sending.text;

										else

								        	statusDiv.innerHTML = statusIndicator.text;

											

								        if (statusIndicator.className)

											statusDiv.className = statusIndicator.className;

										statusIndicator.target.appendChild(statusDiv);

										statusIndicator.element = statusDiv;

									}

								    else if (statusIndicator.type == "image")

								    {

								        var statusImg = document.createElement("img");

								        statusImg.id = "progressBar";

										if (statusIndicator.border)

								        	statusImg.border=statusIndicator.border;

										if (statusIndicator.className)

											statusImg.className = statusIndicator.className;

										if (statusIndicator.sending)

											statusImg.src = statusIndicator.sending.imgSrc;

										else

											statusImg.src = statusIndicator.imgSrc;

								        statusIndicator.target.appendChild(statusImg);

										statusIndicator.element = statusImg;

								    }

									

									if (statusIndicator.toggleTargetVisibility)

										statusIndicator.target.style.visibility = "visible";

									

								 }

							},

							

_setReceivingDataStatus : function(statusIndicator){

								if (statusIndicator.progress && statusIndicator.progress.receiving)

								{

									if (statusIndicator.progress.type == "text")

								        statusIndicator.progress.element.innerHTML = statusIndicator.progress.receiving.text;

									else if (statusIndicator.progress.type == "image")

								        statusIndicator.progress.element.src = statusIndicator.progress.receiving.imgSrc;

								}

							},			

_setProgressStatus : function(statusIndicator){

								if (statusIndicator.progress)

									return AjaxTCR.comm._setStatus(statusIndicator.progress);

									

							},

_setRetryStatus : function(statusIndicator){

								if (statusIndicator.retry)

								{

									AjaxTCR.comm._removeRetryStatus(statusIndicator);

									AjaxTCR.comm._setStatus(statusIndicator.retry);

								}

									

							},

							

							

_setErrorStatus : function(statusIndicator){

								if (statusIndicator.retry)

									AjaxTCR.comm._removeRetryStatus(statusIndicator);

									

								if (statusIndicator.error)

									AjaxTCR.comm._setStatus(statusIndicator.error);

									

							},



_removeStatus : function(statusIndicator){

								if (statusIndicator.element)

								{

									if (statusIndicator.toggleTargetVisibility)

										statusIndicator.target.style.visibility = "hidden";

										

									statusIndicator.element.parentNode.removeChild(statusIndicator.element);

									statusIndicator.element = null;

								}

							},



_removeProgressStatus : function (statusIndicator){

									if (statusIndicator.progress)

										AjaxTCR.comm._removeStatus(statusIndicator.progress);

								},

								

_removeRetryStatus : function (statusIndicator){

									if (statusIndicator.retry)

										AjaxTCR.comm._removeStatus(statusIndicator.retry);

								},

								

_removeErrorStatus : function (statusIndicator){

									if (statusIndicator.error)

										AjaxTCR.comm._removeStatus(statusIndicator.error);

								}

};





/*************************************  AjaxTCR.comm.cache *****************************/

AjaxTCR.comm.cache = {



/****************************************** Private Properties ****************************************************/



/* The cache object */

_cache : new Array(),



/* Caching Options w/defaults */

_cacheOptions : {

	/* The max number of items to store in the cache */

	size : 100,

	/* The default algorithm for removing items.  The choices are LRU, FIFO, and LFU */

	algorithm: "LRU",

	/* The default number of minutes an item can stay in the cache.  Set to -1 for forever */

	expires: 60

},



/*************************************  Public Cache Methods *****************************/

	

/*

 * add

 * 

 * Public method to add a key/value pair to the cache

 * 

 */		

add : function(key, val){

	if (AjaxTCR.comm.cache._cache.length >= AjaxTCR.comm.cache._cacheOptions.size)

	{

		var algorithm = AjaxTCR.comm.cache._cacheOptions.algorithm;

		//we need to remove an item before adding another one.

		if ( algorithm == "FIFO")

			AjaxTCR.comm.cache._cache.splice(0, 1);

		else if (algorithm == "LFU")

		{

			var removeIndex = -1;

			for (var i=0;i<AjaxTCR.comm.cache._cache.length;i++)

			{

				if (removeIndex == -1 || AjaxTCR.comm.cache._cache[removeIndex].totalAccessed > AjaxTCR.comm.cache._cache[i].totalAccessed)

					removeIndex = i;

			}

			

			AjaxTCR.comm.cache._cache.splice(removeIndex,1);

		}

		else if (algorithm == "LRU")

		{

			var removeIndex = -1;

			for (var i=0;i<AjaxTCR.comm.cache._cache.length;i++)

			{

				if (removeIndex == -1 || AjaxTCR.comm.cache._cache[removeIndex].lastAccessed > AjaxTCR.comm.cache._cache[i].lastAccessed)

					removeIndex = i;

			}

			

			AjaxTCR.comm.cache._cache.splice(removeIndex,1);

		}

	} 



	var item = AjaxTCR.comm.cache._createCacheItem(key, val);

	AjaxTCR.comm.cache._cache.push(item);

},



/**

 * clear - Public method that resets the caches

 * 

 */		

clear : function(){

	AjaxTCR.comm.cache._cache = new Array();

},



/*

 * get

 * 

 * Public method to fetch an object based on the key

 * 

 */	

get: function(key){

	var cacheObject = null;

	/* Search for item */

	for (var i=0;i<AjaxTCR.comm.cache._cache.length;i++)

	{

		if (AjaxTCR.comm.cache._cache[i].key == key)

		{

			cacheObject = AjaxTCR.comm.cache._cache[i];

			break;

		}

	}

	

	if (cacheObject)

	{

		/* Update the properties */

		cacheObject.lastAccessed = new Date();

		cacheObject.totalAccessed++;

		

		/* Ensure it hasn't expired */

		if (AjaxTCR.comm.cache._cacheOptions.expires != -1)

		{

			var timeAdded = cacheObject.added;

			var now = new Date();

			now.setMinutes(now.getMinutes() - AjaxTCR.comm.cache._cacheOptions.expires);

			if (now > timeAdded)

			{

				AjaxTCR.comm.cache.remove(item.key);

				cacheObject = null;

			}			

		}

	}

	

	if (cacheObject)

		return cacheObject.value;

	else 

		return null;

},



												  

getAll : function(){ 

	return AjaxTCR.comm.cache._cache;

},



getSize : function(){ 

	return AjaxTCR.comm.cache._cache.length;

},



/*

 * remove

 * 

 * Public method to remove an item from the cache based on the key

 * 

 */	

remove : function(key){

	for (var i=0;i<AjaxTCR.comm.cache._cache.length;i++)

	{

		if (AjaxTCR.comm.cache._cache[i].key == key)

		{

			AjaxTCR.comm.cache._cache.splice(i,1);

			break;

		}

	}

},



setOptions : function(cacheOptions){

	/* apply options defined by user */

    for (option in cacheOptions)

    	AjaxTCR.comm.cache._cacheOptions[option] = cacheOptions[option];

 },

 

								



/*************************************  Private Cache Methods *****************************/



_handleCacheResponse : function(response, responseText){

	response.xhr = {};

	response.xhr.responseText = response.responseText = responseText;

	response.xhr.responseXML = response.responseXML = null;

	

	response.xhr.status = response.httpStatus = 200; 

	response.xhr.statusText = response.httpStatusText = "OK";

	

	response.fromCache = true;

	AjaxTCR.comm._handleResponse(response);

},





/**

 * createCacheItem - Private method that creates a cache object based on the given key and val

 * 

 */								

_createCacheItem : function(key, val){

	var cacheObject = {};

	cacheObject.key = key;

	cacheObject.value = val;

	cacheObject.lastAccessed = new Date();

	cacheObject.added = new Date();

	cacheObject.totalAccessed = 1;

	return cacheObject;

}





};



/*************************************  AjaxTCR.comm.queue *****************************/

AjaxTCR.comm.queue = {



/****************************************** Private Properties ****************************************************/



/* The responseQueue Object */ 

 _responseQueue : {  queue: new Array(), currentIndex: 0, maxID: 0},

 

 /* The requestQueue Array */ 

 _requestQueue : new Array(),

 

 /* The requestQueue counter */

 _requestQueueID: 0,

 

 /* The number of active requests when using request Queue */

 requestQueueConcurrentRequests : 1,

 

/****************************************** Public Queue Methods ****************************************************/

									 

/**

 * add - Public method to add a request to the request queue.

 * 

 * @param url - the url to add to the request queue

 * @param options - the options to use for the call

 * @param priority - the priority level of the entry

 */

add : function(url, options, priority) { 

	if (options)

		options.inQueue = true;

	else 

		options = {inQueue:true};

										

	if (!priority)

		options.priority = "normal";

	else

		options.priority = priority.toLowerCase();

		

	/* Add Id */

	options.requestQueueID = ++AjaxTCR.comm.queue._requestQueueID;

		

	/* See if we should send it or add it to the queue */			

	if (AjaxTCR.comm.stats.getRequestCount("active") >=  AjaxTCR.comm.queue.requestQueueConcurrentRequests)

	{	

		var request = {url: url, options: options};

		if (options.priority == "next")

			AjaxTCR.comm.queue._requestQueue.unshift(request);

		else if (priority && priority == "faster")

		{

			var set = false;

			for (var i=0;i<AjaxTCR.comm.queue._requestQueue.length;i++)

			{

				if (AjaxTCR.comm.queue._requestQueue[i].options.priority == "normal")

				{

					AjaxTCR.comm.queue._requestQueue.splice(i, 0, request);

					set = true;

					break;

				}

			}

			/* If nothing is normal, add to the end */

			if (!set)

				AjaxTCR.comm.queue._requestQueue.push(request);

		}

		else

			AjaxTCR.comm.queue._requestQueue.push(request);

	}

	else

		AjaxTCR.comm.sendRequest(url, options);

		

	return options.requestQueueID;

},	 



/**

 * clearRequestQueue - A public method that clears out the request queue of any pending requests 

 * 

 */										   

clear: function()	{ 

	AjaxTCR.comm.queue._requestQueue.length = 0; 

},



/**

 * get - Returns the item from the queue  

 * 

 *  @param ID - ID of request that you wish to check

 *  @return  The object that is stored in the queue

 * 

 */										   

get : function(ID)	{ 

	for (var i=0;i<AjaxTCR.comm.queue._requestQueue.length;i++)

	{

		if ( AjaxTCR.comm.queue._requestQueue[i].options.requestQueueID == ID)

			return AjaxTCR.comm.queue._requestQueue[i];

	}

	

	return null;

},	  

	

/**

 * getAll - returns the request queue

 * 

 * @return the request queue

 */								

getAll : function(){

	return AjaxTCR.comm.queue._requestQueue;

},

	

/**

 * getPosition - Returns the position in the queue.  

 * 

 *  @param ID - ID of request that you wish to check

 *  @return  Returns -1 if not in queue.  Starts at 0.

 * 

 */										   

getPosition : function(ID)	{ 

	for (var i=0;i<AjaxTCR.comm.queue._requestQueue.length;i++)

	{

		if ( AjaxTCR.comm.queue._requestQueue[i].options.requestQueueID == ID)

			return i;

	}

	

	return -1;

},	  

	

/**

 * getSize - returns the length request queue

 * 

 * @return the request queue length

 */								

getSize : function(){

	return AjaxTCR.comm.queue._requestQueue.length;

},

	

/**

 * remove - A public method that removes the option with the given requestQueueID

 * 

 *  @param ID - ID of request to be removed from queue

 * 

 */										   

remove : function(ID)	{ 

	for (var i=0;i<AjaxTCR.comm.queue._requestQueue.length;i++)

	{

		if ( AjaxTCR.comm.queue._requestQueue[i].options.requestQueueID == ID)

		{

			var ret = AjaxTCR.comm.queue._requestQueue[i];

			AjaxTCR.comm.queue._requestQueue.splice(i, 1);

			return ret;

		}

	}

	

	return false;

},		

									

								   

/****************************************** Private Queue Methods ****************************************************/									   



/*

 * _checkRequestQueue(requestObject)

 * 

 * A private method that looks to see if a request queue is in use and sends the next

 * request off for processing if there is one

 * 

 */									   

_checkRequestQueue : function(response){

	/* If Request Queue is being used, send next request */

	if (response.inQueue && AjaxTCR.comm.queue._requestQueue.length > 0)

	{		

		var nextRequest = AjaxTCR.comm.queue._requestQueue.shift();

		AjaxTCR.comm.sendRequest(nextRequest.url, nextRequest.options);

	}

},

							

/*

  * handleQueue(requestObject)

  * 

  * Private method called after response comes in if request is in queue.

  * Adds response to queue and then calls callbacks for any that are able to move forward

  */

 									 

_handleQueue: function(response){

	/* add response into queue */

	AjaxTCR.comm.queue._responseQueue.queue[response.responseQueueID] = response;

									

	/* loop thru queue handling any received requests up to current point  */

	while (AjaxTCR.comm.queue._responseQueue.queue[AjaxTCR.comm.queue._responseQueue.currentIndex] != undefined)

	{

	  AjaxTCR.comm._handleCallbacks(AjaxTCR.comm.queue._responseQueue.queue[AjaxTCR.comm.queue._responseQueue.currentIndex]);

	  AjaxTCR.comm.queue._responseQueue.currentIndex++;

	}

}





};



/*************************************  AjaxTCR.comm.stats *****************************/



AjaxTCR.comm.stats = {



 /* Collect data across all requests */

 _commResults : {

 totalRequests : 0,

 totalTimeouts : 0,

 totalRetries : 0,

 totalSuccess : 0,

 totalFails : 0,

 requestFails : new Array()},

 

 

collect : function (url)	{

	var sendConnectionStats = function(){

		var results = AjaxTCR.comm.stats.get();

		if (results.totalRequests > 0)

		{

			var payload = AjaxTCR.data.encodeJSON(results);

			AjaxTCR.comm.sendRequest(url, {method:"POST",payload:payload,requestContentType:"application/json",oneway:true});

		}

	};

	

	if(window.attachEvent)	

 		window.attachEvent('onunload', sendConnectionStats);

 	else

		window.addEventListener('unload', sendConnectionStats, false);



},

										

										

get : function()	{

	return AjaxTCR.comm.stats._commResults;

},





/**

 * getRequestCount - Public method acting as simple getter for the count of requests currently out

 * 

 * @return - the number of outstanding requests

 */

getRequestCount : function(type) { 

	if (type == "queued")

		return AjaxTCR.comm.queue.getSize();

	else if (type == "active")

		return AjaxTCR.comm._requestsOutstanding;

	else	

		return (AjaxTCR.comm.queue.getSize() + AjaxTCR.comm._requestsOutstanding); 

}





};



/*************************************  AjaxTCR.comm.cookie *****************************/



AjaxTCR.comm.cookie = {

get : function(name){

	var fullname = name + "=";

	var cookies = document.cookie.split(';');

	for(var i=0;i < cookies.length;i++)

	{

		var cookieNV = cookies[i];

	    while (cookieNV.charAt(0)==' ') 

											cookieNV = cookieNV.substring(1);

	    if (cookieNV.indexOf(fullname) == 0) 

											return cookieNV.substring(fullname.length);

	}

	return null;

}



};







AjaxTCR.data = {

						

/*

 * encodeValue(str) 

 * 

 * Public method to encode passed string values to make the URL safe.  

 * Strictly encodes to x-www-urlencoded format vs. native methods.

 */									 

									 

encodeValue : function(val) {

                             var encodedVal;



                             if (!encodeURIComponent)

                               {

                                encodedVal = escape(val);

                                /* fix the omissions */

	                            encodedVal = encodedVal.replace(/@/g, '%40');

	                            encodedVal = encodedVal.replace(/\//g, '%2F');

	                            encodedVal = encodedVal.replace(/\+/g, '%2B');

                               }

                            else

                               {

                                encodedVal = encodeURIComponent(val);

	                            /* fix the omissions */

	                            encodedVal = encodedVal.replace(/~/g, '%7E');

	                            encodedVal = encodedVal.replace(/!/g, '%21');

	                            encodedVal = encodedVal.replace(/\(/g, '%28');

	                            encodedVal = encodedVal.replace(/\)/g, '%29');

	                            encodedVal = encodedVal.replace(/'/g, '%27');

                               }



                            /* clean up the spaces and return */

                            return encodedVal.replace(/\%20/g,'+'); 

                           } ,



/*

 * encodeAsHTML(str) 

 * 

 * Public method to convert tag characters to &lt; and &gt; and change \n to <br />  

 */	

encodeAsHTML : function(str) {

								var convertedString = str.replace(/<([^>]*)>/g, "&lt;$1&gt;")

								convertedString = convertedString.replace(/\n/g, "<br/>");

								return convertedString;

						   }, 

						

/*

 * encode64(str) 

 * 

 * Public method to encode passed string values into base64.  

 */	

encode64 : function(inputStr) 

{

   var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

   var outputStr = "";

   var i = 0;

   

   while (i<inputStr.length)

   {

      var byte1 = inputStr.charCodeAt(i++);

      var byte2 = inputStr.charCodeAt(i++);

      var byte3 = inputStr.charCodeAt(i++);



      var enc1 = byte1 >> 2;

      var enc2 = ((byte1 & 3) << 4) | (byte2 >> 4);

	  

	  var enc3, enc4;

	  if (isNaN(byte2))

		enc3 = enc4 = 64;

	  else

	  {

      	enc3 = ((byte2 & 15) << 2) | (byte3 >> 6);

		if (isNaN(byte3))

         	enc4 = 64;

		else

	      	enc4 = byte3 & 63;

	  }



      outputStr +=  b64.charAt(enc1) + b64.charAt(enc2) + b64.charAt(enc3) + b64.charAt(enc4);

   } 

   

   return outputStr;

}, 



/*

 * decode64(str) 

 * 

 * Public method to decode passed string values from base64.  

 */	

decode64 : function(inputStr) 

{

   var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

   var outputStr = "";

   var i = 0;

   inputStr = inputStr.replace(/[^A-Za-z0-9\+\/\=]/g, "");



   while (i<inputStr.length)

   {

      var dec1 = b64.indexOf(inputStr.charAt(i++));

      var dec2 = b64.indexOf(inputStr.charAt(i++));

      var dec3 = b64.indexOf(inputStr.charAt(i++));

      var dec4 = b64.indexOf(inputStr.charAt(i++));



      var byte1 = (dec1 << 2) | (dec2 >> 4);

      var byte2 = ((dec2 & 15) << 4) | (dec3 >> 2);

      var byte3 = ((dec3 & 3) << 6) | dec4;



      outputStr += String.fromCharCode(byte1);

      if (dec3 != 64) 

	  	outputStr += String.fromCharCode(byte2);

      if (dec4 != 64)

         outputStr += String.fromCharCode(byte3);

   }



   return outputStr;

},



/*

 * serializeForm(form, encoding, trigger, evt) 

 * 

 * Public method to create a serialized payload based on the contents of a form and the specified encoding value.  

 * trigger and evt are optional and used to firm up the accuracy between what the browser would send and what we send.

 */									 

	

serializeForm : function(form, encoding, trigger, evt) {	

														 if (typeof(form) == "string")

														 {

															var formObject = document.forms[form]; 

															if (formObject == null)

																formObject = document.getElementById(form);

																

															form = formObject;																

														 }

														 var x=0,y=0;

														 if (trigger && trigger.type == "image" && trigger.name)

														 {

														 	if (window.event)

															{

																x = window.event.offsetX;

																y = window.event.offsetY;

															}

															else if (evt.target) 

															{

																var coords = {x: 0, y: 0 };

																var elmt = trigger;

																while (elmt)

																{

																	coords.x += elmt.offsetLeft;

																	coords.y += elmt.offsetTop;

																	elmt = elmt.offsetParent;

																}

															

																x = evt.clientX + window.scrollX - coords.x - 1 ;

																y = evt.clientY + window.scrollY - coords.y - 1;

															}

														 }

														 

														 var formValues = AjaxTCR.data._beginEncode(encoding);

														 for (var i =0; i < form.elements.length; i++)

														 {

														  var currentField = form.elements[i];

														  var fieldName = currentField.name;

														  var fieldType = currentField.type;

														

														  /* Disabled and unnamed fields are not sent by browsers so ignore them */

														  if ((!currentField.disabled) && fieldName) 

														    {

															 switch (fieldType)

															  {

															   case "text":

															   case "password":

															   case "hidden":

															   case "textarea": formValues = AjaxTCR.data._encode(formValues, fieldName, currentField.value, encoding);

														                            break;

															   case "radio":

															   case "checkbox": if (currentField.checked) 

																		formValues = AjaxTCR.data._encode(formValues, fieldName, currentField.value, encoding);

														                            break;

														       case 'select-one':

															   case 'select-multiple': for (var j=0; j< currentField.options.length; j++)

																	            if (currentField.options[j].selected)

														                                    {

																			formValues = AjaxTCR.data._encode(formValues, fieldName, (currentField.options[j].value) ? currentField.options[j].value : currentField.options[j].text , encoding);

														                                    }

																	            break;

															   case "file": if (currentField.value)

																	    return "fileupload";

																	else

																	     formValues = AjaxTCR.data._encode(formValues, fieldName, currentField.value, encoding);

																        break;

															   case "submit": if (currentField == trigger)

																  	     formValues = AjaxTCR.data._encode(formValues, fieldName, currentField.value, encoding);

																	  break;

															   default: continue;  /* everything else like fieldset you don't want */

															  }

														   }

														

														 }

														

														 if (trigger && trigger.type == "image" && trigger.name)

														 {

															/* this is where we need to insert the trigger image information */

															formValues = AjaxTCR.data._encode(formValues, trigger.name + ".x", x, encoding);

															formValues = AjaxTCR.data._encode(formValues, trigger.name + ".y", y, encoding);

															formValues = AjaxTCR.data._encode(formValues, trigger.name, trigger.value, encoding);

														 }

														 

														 /* Clean up payload string */

														 formValues = AjaxTCR.data._completeEncoding(formValues, encoding);

														

														 return formValues;

														},



/*

 * serializeObject(payload, obj, encoding) 

 * 

 * Public method to create/modify a serialized object based on an object

 * payload is current object that will be appended to.

 * obj is the object to add to the payload

 * encoding is the encoding method

 */									 

	

serializeObject : function(payload, obj, encoding){

													/* Take care of any necessary bits if payload isn't empty */

													payload = AjaxTCR.data._continueEncoding(payload, encoding);

													

													/* encode each value in the object */

												    for (var key in obj)

												        payload = AjaxTCR.data._encode(payload, key, obj[key], encoding);

												

													/* Clean up payload string */

													payload = AjaxTCR.data._completeEncoding(payload, encoding);

												    return payload;

												},



/*

 * serializeObject(payload, fieldName, fieldValue encoding) 

 * 

 * private method to encode one name/value pair and add it to a payload.

 * payload is current object that will be appended to.  It can be null.

 * fieldName and fieldValue are the name/value pair.

 * encoding is the encoding method

 */	

_encode : function(payload, fieldName, fieldValue, encoding){

																switch(encoding)

																{

															    	case "application/json":

															    	   	  payload[fieldName] = fieldValue;

																	  break;

															    	case "application/x-www-form-urlencoded":

															    	   	  payload+=AjaxTCR.data.encodeValue(fieldName)+"="+AjaxTCR.data.encodeValue(fieldValue)+"&"

																	  break;

																	case "text/plain":

															    	   	  payload+=fieldName.replace(/,/g, "%2C") + "=" + fieldValue.replace(/,/g, "%2C") +","

																	  break;

																	case "text/xml":

																		var node = payload.createElement(fieldName);

																		node.appendChild(payload.createTextNode(fieldValue));

																		payload.lastChild.appendChild(node);

	

																	  break;

																}

															

															    return payload;

															},

															

/*

 * beginEncode(encoding) 

 * 

 * private method to create the base for the encoding object

 */	

_beginEncode : function(encoding){

																switch(encoding)

																{

															    	case "application/json":

															    	  if (payload == null)

																    	payload = {};

															

																  	  break;

															    	case "application/x-www-form-urlencoded":

															    	  if (payload == null)

																    	payload = "";

															

																	  break;

																	case "text/plain":

															    	  if (payload == null)

																    	payload = "";

															

																	  break;

																	case "text/xml":

																		if (payload == null)

																		{

																			var payload = AjaxTCR.data._createXMLDocument();

																			if (window.navigator.userAgent.indexOf('Opera') == -1)

																			{

																			  var xmlStmt = payload.createProcessingInstruction("xml"," version=\"1.0\" encoding=\"UTF-8\" ");

																		      payload.appendChild(xmlStmt);

																			}

																			

																			var root = payload.createElement("payload");

																			payload.appendChild(root);

																		}

								

																		

																	  break;

																}

															

															    return payload;

															},



/*

 * completeEncoding(payload, encoding) 

 * 

 * private method to clean up payload string when finished encoding.

 */	

_completeEncoding : function (payload, encoding ){

													 /* Trim off the end & but avoid edge case problems with an empty form */

													 if ((encoding == "application/x-www-form-urlencoded" || encoding == "text/plain") && payload.length > 0)

													   payload = payload.substring(0,payload.length-1);

													   

													 

													 return payload;

												 },

/*

 * continueEncoding(payload, encoding) 

 * 

 * private method to get payload ready to be appended to.

 */	

_continueEncoding : function(payload, encoding){

												if (payload != "")

												  {

													if (encoding == "application/x-www-form-urlencoded")

														payload += "&";

													else if (encoding == "text/plain")

														payload += ",";

												  }

												return payload;

											   },



/*

 * createXMLDocument() 

 * 

 * Finds the best native or ActiveX object to use for an XML Document

 */

_createXMLDocument : function(){

								var xmlDoc = null;

								if (window.ActiveXObject)

								  {

								  	var versions = ["Msxml2.DOMDocument.6.0", "Msxml2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"];

								

								    for (var i=0;i<versions.length;i++)

								     {

									   try

									    {

										 xmlDoc = new ActiveXObject(versions[i]);

										 break;

									    }

									   catch(err){}

								      }

								  }

								else	

									xmlDoc = document.implementation.createDocument("", "", null);

									

								return xmlDoc;

							  },



/*

 * serializeXML(xmlObject) 

 * 

 * Returns the string version of the given XML Object

 */							  

serializeXML : function(xmlObject){

							var xmlString = "";

							

							if (typeof XMLSerializer != "undefined")

						  		xmlString = (new XMLSerializer()).serializeToString(xmlObject); 

							else if (xmlObject.xml) 

								xmlString = xmlObject.xml;

								

							return xmlString;

						},

						

						

						

decodeJSON : function(jsonString){

						            var j;

									

									if (jsonString.length > 1 && jsonString.substring(0,2) == "/*")

 										jsonString = jsonString.substring(2,jsonString.lastIndexOf("*/"));

						

						            if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.

						                    test(jsonString)) {

						

						              try {

						                    j = eval('(' + jsonString + ')');

						                } catch (e) {

						                    throw new SyntaxError('parseJSON');

						                }

						            } else {

						                throw new SyntaxError('parseJSON');

						            }

									

									return j;

						    	},



encodeJSON : function(o){

							var useHasOwn = {}.hasOwnProperty ? true : false;

						    

						    var pad = function(n) {

						        return n < 10 ? '0' + n : n;

						    };

						    

						    var m = {

						        '\b': '\\b',

						        '\t': '\\t',

						        '\n': '\\n',

						        '\f': '\\f',

						        '\r': '\\r',

						        '"' : '\\"',

						        '\\': '\\\\'

						    };

							

					        if(typeof o == 'undefined' || o === null)

					            return 'null';

					        else if(o instanceof Array)

							{

					            var a = ['['], b, i, l = o.length, v;

					            for (i = 0; i < l; i += 1) {

					                v = o[i];

					                switch (typeof v) {

					                    case 'undefined':

					                    case 'function':

					                    case 'unknown':

					                        break;

					                    default:

					                        if (b) {

					                            a.push(',');

					                        }

					                        a.push(v === null ? "null" : AjaxTCR.data.encodeJSON(v));

					                        b = true;

					                }

					            }

					            a.push(']');

					            return a.join('');

					        }

							else if(o instanceof Date)

							{

					            return '"' + o.getFullYear() + '-' +

					                pad(o.getMonth() + 1) + '-' +

					                pad(o.getDate()) + 'T' +

					                pad(o.getHours()) + ':' +

					                pad(o.getMinutes()) + ':' +

					                pad(o.getSeconds()) + '"';

					        }

							else if(typeof o == 'string')

							{

								var s = o;

					            if (/["\\\x00-\x1f]/.test(s)) 

								{

					            	return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) 

									{

						                var c = m[b];

						                if(c){

						                    return c;

						                }

						                c = b.charCodeAt();

					                	return '\\u00' +

					                    	Math.floor(c / 16).toString(16) +

					                    (c % 16).toString(16);

					            	}) + '"';

					        	}

					        	return '"' + s + '"';

					        }

							else if(typeof o == 'number')

							{

					            return isFinite(o) ? String(o) : "null";

					        }

							else if(typeof o == 'boolean')

							{

					            return String(o);

					        }

							else 

							{

					            var a = ['{'], b, i, v;

					            for (var i in o) {

					                if(!useHasOwn || o.hasOwnProperty(i)) {

					                    v = o[i];

					                    switch (typeof v) {

					                    case 'undefined':

					                    case 'function':

					                    case 'unknown':

					                        break;

					                    default:

					                        if(b)

												a.push(',');

					                        

					                        a.push(AjaxTCR.data.encodeJSON(i), ':', v === null ? "null" : AjaxTCR.data.encodeJSON(v));

					                        b = true;

					                    }

					                }

					            }

					            a.push('}');

					            return a.join('');

					        }

    },

	

/*

 * encodeMD5(string) 

 * 

 * public method to get md5 encode a string

 * Based on Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.

 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet

 * Distributed under the BSD License

 * See http://pajhome.org.uk/crypt/md5 for more info.

 */	

encodeMD5 : function(str){

	var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */

	var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */

	var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */



	var len = str.length * chrsz;

	

	var x = Array();

  	var mask = (1 << chrsz) - 1;

  	for(var i = 0; i < len; i += chrsz)

    	x[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);

		

		

	/* append padding */

  	x[len >> 5] |= 0x80 << ((len) % 32);

  	x[(((len + 64) >>> 9) << 4) + 14] = len;



  	var a =  1732584193;

  	var b = -271733879;

  	var c = -1732584194;

  	var d =  271733878;



  	for(var i = 0; i < x.length; i += 16)

  	{

    	var olda = a;

    	var oldb = b;

    	var oldc = c;

    	var oldd = d;



	    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);

	    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);

	    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);

	    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);

	    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);

	    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);

	    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);

	    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);

	    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);

	    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);

	    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);

	    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);

	    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);

	    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);

	    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);

	    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);

	

	    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);

	    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);

	    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);

	    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);

	    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);

	    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);

	    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);

	    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);

	    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);

	    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);

	    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);

	    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);

	    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);

	    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);

	    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);

	    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);

	

	    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);

	    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);

	    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);

	    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);

	    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);

	    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);

	    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);

	    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);

	    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);

	    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);

	    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);

	    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);

	    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);

	    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);

	    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);

	    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);

	

	    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);

	    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);

	    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);

	    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);

	    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);

	    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);

	    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);

	    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);

	    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);

	    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);

	    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);

	    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);

	    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);

	    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);

	    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);

	    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);

	

	    a = safe_add(a, olda);

	    b = safe_add(b, oldb);

	    c = safe_add(c, oldc);

	    d = safe_add(d, oldd);

	}

  

  	var binarray = Array(a, b, c, d);

  	var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";

  	var str = "";

  	for(var i = 0; i < binarray.length * 4; i++)

  	{

    	str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) + hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);

  	}

  	

	return str;

  

	/*

	 * Add integers, wrapping at 2^32. This uses 16-bit operations internally

	 * to work around bugs in some JS interpreters.

	 */

	function safe_add(x, y)

	{

	  var lsw = (x & 0xFFFF) + (y & 0xFFFF);

	  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);

	  return (msw << 16) | (lsw & 0xFFFF);

	}

	

	/*

	 * Bitwise rotate a 32-bit number to the left.

	 */

	function bit_rol(num, cnt)

	{

	  return (num << cnt) | (num >>> (32 - cnt));

	}

	

	/*

	 * These functions implement the four basic operations the algorithm uses.

	 */

	function md5_cmn(q, a, b, x, s, t)

	{

	  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);

	}

	function md5_ff(a, b, c, d, x, s, t)

	{

	  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);

	}

	function md5_gg(a, b, c, d, x, s, t)

	{

	  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);

	}

	function md5_hh(a, b, c, d, x, s, t)

	{

	  return md5_cmn(b ^ c ^ d, a, b, x, s, t);

	}

	function md5_ii(a, b, c, d, x, s, t)

	{

	  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);

	}



}



};



AjaxTCR.util = {};

AjaxTCR.util.DOM = {

	enableDOMShorthand : true,



	/**

	 * Find elements by class, will be overriden by native if found

	 * If startNode is specified, starts the search there, otherwise starts at document. 

	 * 

	 * @param 	classToFind	the string class name to search for 

	 * @param	startNode	the DOM node to start the search at.  Default is the document node. 

	 * @return 	array of elements that match the given class name.

	 */

	getElementsByClassName : function(classToFind,startNode){ 	

								  if (document.getElementsByClassName)

								    return document.getElementsByClassName(classToFind,startNode);

										

								  /* find all the elements within a particular document or in the whole document */

								  var elements;

								  if (startNode)

								   elements = startNode.getElementsByTagName("*");

								  else

								   elements = document.getElementsByTagName("*");

								   

								  var classElements = new Array();

								  var classCount = 0;  

								  

								  var pattern = new RegExp("(^|\\s)"+classToFind+"(\\s|$)");

								  

								  /* look over the elements and find those who match the class passed */

								  for (var i = 0; i < elements.length; i++)

								    if (pattern.test(elements[i].className) )

								        classElements[classCount++] = elements[i];

										

								  return classElements;

							},	



	/**

	 * Returns element or element array specified by given string or strings.

	 * 

	 * @param	element(s) 	strings to search for element.

	 * @param 	startNode	the DOM node to start the search at.  Default is the document node.

	 * @return	if single string, it returns the element.  Otherwise it returns an array of elements

	 */

	getElementsById : function(){

									var elements = new Array();

									var startNode = document;

									var length = arguments.length;

									if (typeof(arguments[length-1]) == "object" && arguments[length-1] != document)

									{

										startNode = arguments[length-1];

										length--;

										var allElements = startNode.getElementsByTagName("*");

										for (var j=0; j<allElements.length; j++)

										{

											for (var i=0;i<length;i++)

											{

								   				if (allElements[j].getAttribute("id") == arguments[i])

												{

								        			elements.push(allElements[j]);

													break;

												}

											}

										}

									}

									else

									{

										if (arguments[length-1] == document)

											length--;

											

										for (var i=0; i<length; i++)

										{

											var elm = document.getElementById(arguments[i]);

											if (elm != null)

									    		elements.push(elm);

										}

									}

									

									if (elements.length == 1)

										return elements[0];

									else if (elements.length > 0)

										return elements;

									else

										return null;

								},

		

	/**

	 * Modified version of getElementById to return single node match.

	 * If startNode is not set to document, it starts the search at the node

	 * If deepSearch is set to true, it does not use getElementById, but instead loops through the whole structure.

	 * 

	 * @param id 			the string to match with the id attribute

	 * @param startNode		the DOM node to start searching in the document

	 * @param deepSearch	true if wanted to search node by node instead of document.getElementById

	 */						

	getElementById : function(id, startNode, deepSearch){

								if (!startNode)

									startNode = document;

								

								if (startNode == document && !deepSearch)

									return document.getElementById(id);

								else

								{

									var allElements = startNode.getElementsByTagName("*");

									for (var j=0; j<allElements.length; j++)

									{

										if (allElements[j].getAttribute("id") == id)

										{

									   		return allElements[j];

											break;

										}

									}

								}







	},

	

		

	/**

	 * 

	 * @param 	selector		string indicating the selection to match

	 * @param 	treeRoot		DOM element to start search.  Default is the document node

	 * @return 	array of matching elements

	 * 

	 */						

	getElementsBySelector : function(selector,treeRoot){

								var matches = new Array();

								var parents = new Array();

								var savematches = new Array();

								if (treeRoot)

								{

									if (treeRoot.length)

									{

										for (var i=0;i<treeRoot.length;i++)

											parents.push(treeRoot[i]);

									}

									else

										parents.push(treeRoot);

								}

								else

									parents.push(document);

									

								selector = selector.replace(/([>\+,])/g, " $1 ").replace(/[\s]+/g," ");

								 

								var selectors = selector.split(" ");

								while (selectors.length > 0)

								{

										var curSelector = selectors.shift();

										if (curSelector == "")

											continue;

											

										/* check for expressions */

										var options = {};

										switch(curSelector.charAt(0))

										{

											case(">"):

												options.type = "childOnly";

											break;

											case("+"):

												options.type = "nextSibling";

											break;

											case ("~"):

												options.type = "futureSibling";

											break;

											case(","):

												while(matches.length > 0)

													savematches.push(matches.shift());

								

												parents.length = 0;					

												if (treeRoot)

													parents.push(treeRoot);

												else

													parents.push(document);

								

												continue;

											break;

										}

										

										if (options.type)

										{

											if (curSelector.length == 1)

												curSelector = selectors.shift();

											else

												curSelector = curSelector.substring(1);

										}

										

										/* Check to see if we already looped though.  If so, we have a different starting point */

										if (matches.length)

										{

											parents.length = 0;

											while(matches.length > 0)

												parents.push(matches.shift());

										}

										

										

										/* Check for Pseudo-classes */

										if (curSelector.indexOf(":") > -1)

										{

											var newSelector = curSelector.substring(0, curSelector.indexOf(":"));

											var optionsType = curSelector.substring(curSelector.indexOf(":")+1);

											

											curSelector = newSelector;

											options.type = optionsType.toLowerCase();

											

											if (options.type.indexOf("nth-child") == 0)

											{

												options.childNumber = options.type.substring(10,options.type.length-1);

												options.type = "nth-child";

											}

											else if (options.type.indexOf("not") == 0)

											{

												//use optionsType to preserve case

												options.notString = optionsType.substring(4,options.type.length-1).replace(/^\s+|\s+$/g,"");

												options.type = "not";

												var notSelector = curSelector;

												if (notSelector == "*")

													notSelector = "";

												if (/^[:#\[\.].*/.test(options.notString))

													options.notSelector = notSelector + options.notString;

												else

													options.notSelector = notSelector + " " + options.notString;

												

												options.notObjects = AjaxTCR.util.DOM.getElementsBySelector(options.notSelector, parents);	

											}

										}

										

										/* Check for Attributes */

										if (curSelector.indexOf("[") > -1)

										{

											var tokens = curSelector.split("[");

											curSelector = tokens[0];

											options.type = "attribute";

											options.attribute = tokens[1].substring(0,tokens[1].length-1).toLowerCase();

										}

										

										if (curSelector == "")

											curSelector = "*";

										

										/* Inspect class selectors */

										if (curSelector.indexOf(".") > -1)

										{

											/* Cases:

											 * p.class1

											 * .class2

											 * div.class1.class2

											 */

											var classNames = curSelector.split(".");

											var elementName = classNames.shift();

											/* First get the element at the beginning if necessary */

											if (elementName != "")

											{

												for (var j=0;j<parents.length;j++)

												{

													var elms = AjaxTCR.util.DOM._getElementsByTagName(parents[j],elementName,options);

													for (var k=0;k<elms.length;k++)

													{

														if (checkFilter(elms[k], parents[j], options))

															matches.push(elms[k]);

													}

												}

											}

											else if (classNames.length > 0)

											{

												/* if no element is specified, use getElementsByClassName for the first class */

												var firstClass = classNames.shift();

												for (var j=0;j<parents.length;j++)

												{

													var elms = AjaxTCR.util.DOM.getElementsByClassName(firstClass, parents[j]);

													for (var k=0;k<elms.length;k++)

													{

														if (checkFilter(elms[k],parents[j],options))

															matches.push(elms[k]);

													}

												}

											}

										

											/* Now get the (rest of the) classes */

											for (var j=matches.length-1;j>=0;j--)

											{

												for (var k=0;k<classNames.length;k++)

												{

													var pattern = new RegExp("(^|\\s)"+classNames[k]+"(\\s|$)");

													if (!pattern.test(matches[j].className))

													{

														matches.splice(j,1);

														break;

													} 

												}

											}

										}

										

										/* Inspect id selectors */

										else if (curSelector.indexOf("#") > -1)

										{

											/* Cases:

											 * p#id1

											 * #id2

											 */

											var idNames = curSelector.split("#");

											var elementName = idNames[0];

											var id = idNames[1];

											

											/* First get the element at the beginning if necessary */

											if (elementName != "")

											{

												for (var j=0;j<parents.length;j++)

												{

													var elms = AjaxTCR.util.DOM._getElementsByTagName(parents[j],elementName,options);

													for (var k=0;k<elms.length;k++)

													{

														if (elms[k].id == id && checkFilter(elms[k], parents[j], options))  

															matches.push(elms[k]);

													}

												}

											}

											else

											{

												for (var j=0;j<parents.length;j++)

												{

													var elms = AjaxTCR.util.DOM.getElementsById(id, parents[j]);

													if (checkFilter(elms, parents[j], options))

														matches.push(elms);

												}

											}

										}

										/* Simple tagname selects */

										else

										{

											for (var j=0;j<parents.length;j++)

											{

												var elms =AjaxTCR.util.DOM._getElementsByTagName(parents[j],curSelector,options);

												for (var k=0;k<elms.length;k++)

												{

													if (checkFilter(elms[k], parents[j], options))

														matches.push(elms[k]);

												}

											}

										}						

								}

								

								

								function checkFilter(element, parent, options)

								{

									var valid = false;

									

									if (element == null)

										return false;

									else if (!options.type)

										return true;

										

									//handle the case of the parent element being the document	

									if (parent == document)

									{

										var allElms = document.getElementsByTagName("*");

										for (var i=0;i<allElms.length;i++)

										{

											if( checkFilter(element, allElms[i], options))

											{

												valid = true;

												break;

											}

										}

									

										return valid;

									}

									

									

									if (options.type == "childOnly")

										valid = (element.parentNode == parent);

									else if (options.type == "nextSibling")

									{

										var elm = parent.nextSibling;

										while (elm != null && elm.nodeType != 1)

											elm = elm.nextSibling;

										valid = (elm == element);

									}

									else if (options.type == "futureSibling")

									{

										var elm = parent.nextSibling;

										while (elm != null)

										{

											if (elm == element)

											{

												valid = true;

												break;

											}

											elm = elm.nextSibling;

										}

									}	

									else if (options.type == "first-child")

									{

										var elm = parent.firstChild;

										while (elm != null && elm.nodeType != 1)

											elm = elm.nextSibling;

										valid = (elm == element); 

									}		

									else if (options.type == "last-child")

									{

										var elm = parent.lastChild;

										while (elm != null && elm.nodeType != 1)

											elm = elm.previousSibling;

										valid = (elm == element); 

									}

									else if (options.type == "only-child")

									{

										var elm = parent.firstChild;

										while (elm != null && elm.nodeType != 1)

											elm = elm.nextSibling;

										

										if (elm == element)

										{

											var elm = parent.lastChild;

											while (elm != null && elm.nodeType != 1)

												elm = elm.previousSibling;

										}

										

										valid = (elm == element);

									}

									else if (options.type == "nth-child")

									{

										var count = 0;

										var elm = parent.firstChild;

										while (elm != null  && count < options.childNumber)

										{

											if (elm.nodeType == 1)

												count++;

											

											if (count == options.childNumber)

												break;

											

											elm = elm.nextSibling;

										}

										 

										valid = (elm == element);

									}

									else if (options.type == "empty")

										valid = (element.childNodes.length == 0);

									else if (options.type == "enabled")

										valid = (!element.disabled);

									else if (options.type == "disabled")

										valid = (element.disabled);

									else if (options.type == "checked")

										valid = (element.checked);

									else if (options.type == "selected")

										valid = (element.selected);

									else if (options.type == "attribute")

									{

										var pattern = /^\s*([\w-]+)\s*([!*$^~=]*)\s*(['|\"]?)(.*)\3/;

										var attRules = pattern.exec(options.attribute);

										

										if (attRules[2] == "")

											valid = element.getAttribute(attRules[1]);

										else if (attRules[2] == "=")

											valid = (element.getAttribute(attRules[1]) && element.getAttribute(attRules[1]).toLowerCase() == attRules[4].toLowerCase());

										else if (attRules[2] == "^=")

											valid = (element.getAttribute(attRules[1]) && element.getAttribute(attRules[1]).toLowerCase().indexOf(attRules[4].toLowerCase()) == 0);

										else if (attRules[2] == "*=")

											valid = (element.getAttribute(attRules[1]) && element.getAttribute(attRules[1]).toLowerCase().indexOf(attRules[4].toLowerCase()) > -1);

										else if (attRules[2] == "$=")

										{

											var att =element.getAttribute(attRules[1]);

											if (att)

												valid =  (att.toLowerCase().substring(att.length - attRules[4].length) == attRules[4].toLowerCase()); 

										}										

									}

									else if (options.type == "not")

									{

										valid = true;

										for (var j=0;j<options.notObjects.length;j++)

										{

											if (options.notObjects[j] == element)

											{

												valid = false;

												break;

											}

										}

									}

									

									

									return valid;					

								}

								

								//get the results in the correct order

								if (savematches.length)

								{

									while(matches.length > 0)

										savematches.push(matches.shift());

									while(savematches.length > 0)

										matches.push(savematches.shift());

								}

								return matches;

							},

/**

 * Custom getElementsByTagName that takes various options into consideration before returning the values

 * 

 * @param parentElm	element to begin the search at

 * @param tag		string to match tagName to

 * @param options

 */					

_getElementsByTagName : function(parentElm, tag, options){

	var matches = new Array();

	if (!options.type)

		return parentElm.getElementsByTagName(tag);

	

	

	if (options.type == "nextSibling")

	{

		var elm = parentElm.nextSibling;

		while (elm && elm.nodeType != 1)

			elm = elm.nextSibling;

		

		if (checkTagMatch(elm, tag))

			matches.push(elm);

	}

	else if (options.type == "futureSibling")

	{

		var elm = parentElm.nextSibling;

		while (elm)

		{

			if (checkTagMatch(elm, tag))

			{

				matches.push(elm);

				//break;

			}

			elm = elm.nextSibling;

		}	

	}

	else

		matches = parentElm.getElementsByTagName(tag);

	

	function checkTagMatch(element, tag)

	{

		return (element && element.tagName && (tag == "*" || element.tagName.toUpperCase() == tag.toUpperCase()));

	}

			

	return matches;

}

};



AjaxTCR.util.event = {

/**

 * addWindowLoadEvent - simple method to allow for safe addition of window.onload called functions.  Assumes everyone else plays nicely though.

 * 

 * @param newFunction - function to call upon page load

 */					



addWindowLoadEvent: function(newFunction) {

	

	var oldFunction = window.onload;

	if (typeof window.onload != "function")

	  {

	   window.onload = newFunction;

	  }

	else 

	  {

  	   window.onload = function () {

	     if (oldFunction)

		   {

		   	oldFunction();

		   }

		 newFunction();

	    };

	  }	

}



};





AjaxTCR.util.misc = {

	/**

	 * generateUID : Generates a unique value.  If 'prefix' is set to -1, only returns the numerical value. 

	 * 				 If prefix isn't set, it sets it to "AjaxTCR".

	 * 

	 * @param prefix the string value to prepend to the uniquevalue

	 * @return the string consisting of the prefix and the uniquevalue

	 */

	generateUID : function(prefix){

		if (prefix == "-1")

			prefix = "";

		else if (!prefix)

			prefix = "AjaxTCR";

		

		var uniquevalue = new Date().getTime().toString() + Math.floor(Math.random()*100);

		return prefix + uniquevalue;



	}	

};



if (AjaxTCR.util.DOM.enableDOMShorthand)

{

   if (typeof($id) == "undefined") $id = AjaxTCR.util.DOM.getElementsById;

   if (typeof($class) == "undefined") $class = AjaxTCR.util.DOM.getElementsByClassName;  

   if (typeof($selector) == "undefined") $selector = AjaxTCR.util.DOM.getElementsBySelector;

}



 



