/*------------------------------------------------------------------------------
ckAjax.js
Generic utilities designed for Ajax enabled applications.  Utilities are designed
for both IE and Mozilla/Firefox compatibility
----------------------------------------------------------------------------------
*/
function ckAjaxObj() {
	try {
		this.uniqueID = this.__getUniqueID(); //since more than one ajax object can be declared on a page, need a way to uniquely identify each
		this.oXMLDOM = null;
		this.responseText = "";
		this.responseXML = "";
		this.responseDocType = "";
		this.responseObj = null;
		this.requestMethod = "post";
		this._returnHandler = null;
		this._returnContext = null;
		this.retryAttempts = 0;
		
		this.oWaitEl = null;
		
		this.lastURL = "";
		this.lastMsgBody = "";
		this.lastReturnHandler = null;
		this.lastGetFormDataVariant = null; //how was the data built for the most recent post. This will either be a FORM object or name attribute and is used to find the proper form fields and validation elements after a server side validation return
		
		this.errorDisplayID = "floatingWindow"; //can pass in where to show the error. Implemented for JSON errors only right now. By default, for Ajax calls, displays as floating window error. 
		this.errorDisplayTab = "";
	
		this.aRequestHeaders = null;
		this.aScriptBlocks = null;
		this.aScriptFiles = null;
		
		this.oDebug = new ckAjaxDebugObj();
	} catch(err) {
		throwConsoleError("ckAjaxObj Init",err);
	}
	return this;
}

/*-------------------------------------------------------------------------------------------------
Setters
-------------------------------------------------------------------------------------------------*/
ckAjaxObj.prototype.setErrorDisplayID = function(sID) {
	this.errorDisplayID = sID;
}
ckAjaxObj.prototype.setErrorDisplayTab = function(sLabel) {
	this.errorDisplayTab = sLabel;
}

ckAjaxObj.prototype.setDebug = function(bDebug) {
	this.oDebug.setActive(bDebug);
}

/*-------------------------------------------------------------------------------------------------
addRequestHeader	-all headers are cleared after response returned
-------------------------------------------------------------------------------------------------*/
ckAjaxObj.prototype.addRequestHeader = function(name,value) {
	if (!this.aRequestHeaders) this.aRequestHeaders = new Array();
	this.aRequestHeaders[name] = value;
}
/*-------------------------------------------------------------------------------------------------
UI 
-------------------------------------------------------------------------------------------------*/
ckAjaxObj.prototype.showWait = function(containerID) {
	var oEl;
	if (oEl = document.getElementById(containerID)) {
		if  (oEl.innerHTML=="") {
			var oImg = document.createElement("IMG")
			oImg.src="/_ckcommon/images/loading_16x16.gif"
			oImg.alt="Please wait..."
			oImg.align="absMiddle";
			oImg.hspace="6"
			oImg.border="0"
			oEl.appendChild(oImg);
			var oSpan = document.createElement("SPAN")
			oSpan.innerHTML="Please wait...";
			oEl.appendChild(oSpan);
		}
		oEl.style.display="";
		this.oWaitEl = oEl;
	}
}

/*-------------------------------------------------------------------------------------------------
postForm
-------------------------------------------------------------------------------------------------*/
ckAjaxObj.prototype.postForm = function(oForm,returnHandler) {
	var oContext = (arguments.length ==3 ? arguments[2] : null);
	this.postFormData(oForm.action,getFormDataAsString(oForm),returnHandler,oContext,oForm);
}

ckAjaxObj.prototype.postFormDataByName = function(URL,sNameAttr,returnHandler) {
	var oContext = (arguments.length ==4 ? arguments[3] : null);
	this.postFormData(URL,getFormDataAsString(sNameAttr),returnHandler,oContext,sNameAttr)
}

ckAjaxObj.prototype.postFormData = function(URL,msgBody,returnHandler) {  //returnContext, lastGetFormDataVariant
	//broke postForm() into two functions so we can also directly pass in form encoded data from a web page
	try {
		if ((URL=="") || (!URL)) throw ("URL to post to is blank");
		
		var oParent = this; //so oXMLHttp onreadystatechange function can access elements in this object
		this._returnHandler = returnHandler
		if (arguments.length >= 4) this._returnContext = arguments[3];
		this.lastGetFormDataVariant = (arguments.length==5 ? arguments[4] : null); //setup as param instead of just setting in postForm() and postFormDataByName() because this function can be called directly. Need some way to have the value explicitly set before each call
		
		var bAsynch = true;
		var oXMLHttp = this.__createXMLHttpObject();
		
		this.responseText= ""; //clear before each call
		this.responseXML = "";		
		this.responseObj = null;

		if (oXMLHttp) {
			oXMLHttp.open(this.requestMethod,URL,bAsynch);
			oXMLHttp.onreadystatechange = function() {
				if (oXMLHttp.readyState==4) {
					switch (oXMLHttp.status) {
						case 200 : 
							oParent._loadResponse(oXMLHttp); 
							break;
						case 12030 :
							if (oParent.retryAttempts==0) {
								oParent.retryAttempts++;
								oParent.oDebug.setDebugReceive(oParent.uniqueID,'HTTP Response Status:' + oXMLHttp.status + '. Attempt #' + oParent.retryAttempts + ' resending data.');
								oParent.rePostLast();
								return;
							} else {
								throwConsoleError("ckAjaxObj.postFormData","WinHTTP Connection Error (12030). The connection with the server has been reset or terminated, or an incompatible SSL protocol was enountered. There was " + oParent.retryAttempts + " attempt(s) to resend the data.");
							}
							break;
						default: //404,500...
						
							var statusText = ""
							if (oXMLHttp.status==0) statusText = "ckAjaxObj.postFormData. An HTTP status code of zero ('0') was returned. No status text information is available";
							else statusText = "ckAjaxObj.postFormData. oXMLHttp Response Status " + oXMLHttp.status + ". " + oXMLHttp.statusText;
								
							oParent.oDebug.setDebugReceive(oParent.uniqueID,statusText);
								
							if (oFloatingWindow) oFloatingWindow.showError(statusText);
							else {
								var oErrorLocal = new ckErrorObj();
								oErrorLocal.add(statusText);
								oErrorLocal.displayErrors();
							}
					}
						
					//don't null out return handler. Just don't allow if not 200 response. If ajax object not recreated on calling page, it will always fail to work properly afterwards
					if ((oXMLHttp.status==200) && (oParent._returnHandler)) {
						if (oParent._returnContext) {	
							oParent._returnHandler.call(oParent._returnContext,oParent);
						}
						else oParent._returnHandler(oParent);
					}
					if (oParent.oWaitEl) oParent.oWaitEl.style.display="none";
				}
				
			}

			this.addRequestHeader("Connection","close");  //fix for 12030 http status?
			if (msgBody) this.addRequestHeader("Content-length",msgBody.length);
			
			if (!this.aRequestHeaders["Content-Type"]) this.addRequestHeader("Content-Type", "application/x-www-form-urlencoded"); //only if not manually set before this
			if (this.aRequestHeaders) for (var key in this.aRequestHeaders) oXMLHttp.setRequestHeader(key,this.aRequestHeaders[key]);
			if (this.errorDisplayID != "") {
				var oEl = document.getElementById(this.errorDisplayID);
				if (oEl) oEl.style.display="none";
			}
			
			oXMLHttp.send(msgBody);
			this.oDebug.setDebugSend(this.uniqueID,URL,msgBody,this.aRequestHeaders);

			this.lastURL = URL
			this.lastMsgBody= msgBody;
			this.lastReturnHandler = returnHandler;
		} else throwConsoleError("ckAjaxObj.postFormData","Unable to create oXMLHttp")
	} catch(err) {
		throwConsoleError("ckAjaxObj.postFormData",err);
	}
}
ckAjaxObj.prototype.rePostLast = function() {
	this.postFormData(this.lastURL,this.lastMsgBody,this.lastReturnHandler);
}

/*-------------------------------------------------------------------------------------------------
loadXML
Makes a call to retrieve an XML document. Used for calling third party news, weather, rss
-------------------------------------------------------------------------------------------------*/
ckAjaxObj.prototype.loadXML = function(URL,returnHandler) {
	this.requestMethod = "get"
	this.postFormData(URL,null,returnHandler);
}



/*-------------------------------------------------------------------------------------------------
PRIVATE loadResponse
-------------------------------------------------------------------------------------------------*/
ckAjaxObj.prototype._loadResponse = function(oXMLHttp) {
	this.responseText = oXMLHttp.responseText;

	
			
	//check for standardized messages that must be processed immediately, before any return handlers are called
	var sContentType = oXMLHttp.getResponseHeader("Content-Type");
	var aContentType = sContentType.split(";"); //need to strip any charset values
	sContentType = aContentType[0];
	
	switch (sContentType) {
		case "text/xml" : 
			this.oXMLDOM = oXMLHttp.responseXML; 
			break;
		case "application/json" :
			this.responseObj= eval("(" + this.responseText + ")");
			if ((this.responseObj.error.aError.length >  0) || (this.responseObj.error.aBlank) || (this.responseObj.error.aInvalid))  {
				this._returnHandler=null;
				this.__processResponse_JSONerror(this.responseObj);
			} else if (this.responseObj.type=="forcedRedirect") {
				this._returnHandler=null;
				document.location.href=this.responseObj.payload.url;
			}
			this.oXMLDOM = null;
			 break;
		default:
			//extract and eval script blocks
			var sScript, sSrc;
			var regex = /\s*<script type="text\/javascript" src="(.+?)".+?><\/script>/g;
			var aMatch = regex.exec(this.responseText);
		    while (aMatch != null) {
				if (!this.aScriptFiles) this.aScriptFiles = Array();
				
				sSrc = RegExp.$1;

				this.aScriptFiles[this.aScriptFiles.length] = sSrc; 
				aMatch = regex.exec(this.responseText);
			}
			this.responseText = this.responseText.replace(regex,"");
			this.embedScriptFiles();
			
			var regex = /\s*<script type="text\/javascript.*?">([\s\S]+?)<\/script>/gm;
			aMatch = regex.exec(this.responseText);
		    while (aMatch != null) {
				if (!this.aScriptBlocks) this.aScriptBlocks = Array();
				
				sScript = RegExp.$1;
				sScript = sScript.replace(/\t/g,"");

				this.aScriptBlocks[this.aScriptBlocks.length] = sScript; //save so they can be evaled on command by calling page since in many cases it's likely the content needs to be rendered before calling certain scripts
				aMatch = regex.exec(this.responseText);
			}
			this.responseText = this.responseText.replace(regex,"");
			break;

		/*
		Commented out 1/26/2010. Now that we're not processing anything based on XML returned, no need to force everything into an XML object.
		If the contentType is text/xml, this object's oXMLDOM property will be set in switch statement above
		
		default:
			if (!this.oXMLDOM) this.oXMLDOM = this._createXMLDOM();
		
			if (this.oXMLDOM)  {
				if (window.ActiveXObject) {
					this.oXMLDOM.loadXML(this.responseText)
				} else {
					//there is no loadXML in Firefox
					var oParser = new DOMParser();
					this.oXMLDOM = oParser.parseFromString(this.responseText,"text/xml");
					//this.oXMLDOM.load(this.responseText); //may or may not be XML
				}
			} 
		*/
	}
	//header information is for debug only
	if (this.oDebug.bActive) {
		var sHeaders = oXMLHttp.getAllResponseHeaders();
		var aHeaders = sHeaders.split(/\r?\n/);
		sHeaders = "";
		for (var index=0; index < aHeaders.length; index++) sHeaders+=aHeaders[index] + "\n";
		this.oDebug.setDebugReceive(this.uniqueID,sHeaders + "----------------------------------------\n" + this.responseText);
	}
	this.lastGetFormDataVariant = null; //all processing that requires this occurs in this method, so clear out now before any more calls come in

}
/*-------------------------------------------------------------------------------------------------
scripts
-------------------------------------------------------------------------------------------------*/
ckAjaxObj.prototype.embedScriptFiles = function() {
	//still needs further testing in Safari and IE6
	var oEl, aHead,oHead;
	if (this.aScriptFiles) {
		var aHead = document.getElementsByTagName("head");
		if (aHead.length > 0) oHead = aHead[0];
		if (oHead) {
			for (var index=0; index < this.aScriptFiles.length; index++) {
				oEl = document.createElement("script")
				oEl.src = this.aScriptFiles[index];
				oEl.type = "text/javascript"
			
				oHead.appendChild(oEl);  
			}
			alert(oHead.innerHTML);
		}
	}
	this.aScriptFiles = null;
}

ckAjaxObj.prototype.executeScripts = function() {
	//only problem is this executes within the oAjax context, so global variables defined in scripts are useless
	if (this.aScriptBlocks) {
		for (var scriptIndex=0; scriptIndex < this.aScriptBlocks.length; scriptIndex++) { //unique indexer important in case normal index var used in any of the eval blocks
			eval(this.aScriptBlocks[scriptIndex]);
		}
	}
	this.aScriptBlocks = null;
}
/*-------------------------------------------------------------------------------------------------
PRIVATE processResponse_JSONError
-------------------------------------------------------------------------------------------------*/
ckAjaxObj.prototype.__processResponse_JSONerror = function(oResponse) {
	try {
		//Since there are multiple ajax calls at once, we can't have a global error object handle everything because errors	could be added for one call, then cleared by another different call
		var oErrorLocal = new ckErrorObj();
		
		var oParams = null;
		if (this.errorDisplayID != "") oParams= ({"displayID": this.errorDisplayID});
		if (this.errorDisplayTab != "") oParams= ({"tab": this.errorDisplayTab});

		//allow private call since this and oError are ck objects. Need to build validation elements before calls to add blank or invalid
		oErrorLocal.__buildValidationElements(this.lastGetFormDataVariant);  
		
		var aErr;
		if (aErr = oResponse.error.aBlank) for (var index=0; index < aErr.length; index++) oErrorLocal.addBlankErr(aErr[index],oParams);
		if (aErr = oResponse.error.aBlankMsgs) for (var index=0; index < aErr.length; index++) oErrorLocal.addBlankMsg(aErr[index]);
		if (aErr = oResponse.error.aInvalid) for (var index=0; index < aErr.length; index++) oErrorLocal.addInvalidErr(aErr[index],oParams);
		if (aErr = oResponse.error.aInvalidMsgs) for (var index=0; index < aErr.length; index++) oErrorLocal.addInvalidMsg(aErr[index]);
		if (aErr = oResponse.error.aError) for (var index=0; index < aErr.length; index++) oErrorLocal.add(aErr[index].desc,oParams);

		oErrorLocal.displayErrors();
	
		return true;

	} catch(err) {
		throwConsoleError("ckAjaxObj._processResponse_JSONerror",err);
	}
}


/*-------------------------------------------------------------------------------------------------
PRIVATE createXMLHttpObject
-------------------------------------------------------------------------------------------------*/
ckAjaxObj.prototype.__createXMLHttpObject = function() {
	if (typeof XMLHttpRequest != "undefined") return new XMLHttpRequest();
	else if (window.ActiveXObject) {
		var aVersions = ["MSXML2.XMLHttp.5.0","MSXML2.XMLHttp.4.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp","Microsoft.XMLHttp"];
		for (var index=0; index < aVersions.length; index++) {
			try {
				var oXMLHttp = new ActiveXObject(aVersions[index]);
				return oXMLHttp;
			} catch(err) {
				throwConsoleError("ckAjaxObj.__createXMLHttpObject",err);
			}
		}
	}
	throwConsoleError("ckAjaxObj.__createXMLHttpObject","XMLHttp object could not be created");
	
}
/*-------------------------------------------------------------------------------------------------
PRIVATE Utilities
-------------------------------------------------------------------------------------------------*/
ckAjaxObj.prototype.__getUniqueID = function () {
     var oDate = new Date();
     var uniqueId = oDate.getMonth() + '' + oDate.getDate() + '' + oDate.getTime() + '';

     return uniqueId;
}




/*-------------------------------------------------------------------------------------------------
PRIVATE createXMLDom
-------------------------------------------------------------------------------------------------*/

/***01.26.2010
ckAjaxObj.prototype._createXMLDOM = function() {
	if (window.ActiveXObject) {
		var aVersions = ["MSXML2.DOMDocument.5.0","MSXML2.DOMDocument.4.0","MSXML2.DOMDocument.3.0","MSXML2.DOMDocument","Microsoft.XmlDom"];
		for (var index=0; index < aVersions.length; index++) {
			try {
				var oXMLDom = new ActiveXObject(aVersions[index]);
				return oXMLDom;
			} catch (err) {
				//alert("version:" + aVersions[index] + " failed");
			}
		}
	} else if ((document.implementation) && (document.implementation.createDocument)) {
		//Mozilla
		var oXMLDom = document.implementation.createDocument("","",null);
		return oXMLDom;
		
	}
	throwConsoleError("ckAjaxObj._createXMLDOM","MSXML is not installed.");
}
**/