//====================================================================================
//====================================================================================
//== EEM6Session.js
//====================================================================================
//== JavaScript file that contains the objects used to interface with the
//==	server (inlcuding dataminer.dll) through a session object
//====================================================================================
//== IMPORTANT NOTE: 
//== 	There should only be one reference to this file for the entire EEM6 website
//==		only the main "frameset.html" should include this file
//====================================================================================
//====================================================================================

//== FILE_DATAMINER_DLL			full path to the dataminer.dll file
var FILE_DATAMINER_DLL			= PATH_DLL + FILE_DLL;

//== Common Errors that dataminer.dll outputs that we want to look for
var ERR_PAGE_ACCESS_DENIED		= "<H1>ACCESS DENIED</H1>";
var ERR_PAGE_NOT_FOUND			= "<H1>PAGE NOT FOUND</H1>";
var ERR_RESOURCE_NOT_FOUND		= "RESOURCE NOT FOUND";
var ERR_PAGE_MISSING_SESSION	= "<H1>MISSING SESSION INFORMATION</H1>";
var ERR_PAGE_NO_DATA			= "<B>DATA UNAVAILABLE </B>";

var REQUEST_ARRAY_MAX			= 100;
var REQUEST_ARRAY_TRIM			= 10;
var REQUEST_POLL_MILLISECONDS	= 250;


//====================================================================================
//== SessionObj object
//====================================================================================
function SessionObj(ObjectName){
	this.ObjectName							= ObjectName;
	this.SessionParam						= "";
	this.DLLFile 							= FILE_DATAMINER_DLL;
	//== Requests							array of past, current and future request objects
	this.Requests							= [];
	//== TopRequestID						A sequence counter for the request objects
	this.TopRequestID						= 0;
	//== TimeoutID							any IE timeout ID that we have outstanding (for checking on progress)
	this.TimeoutID							= null;
	//== onIdle								JavaScript code to execute (once) the next time the session becomes idle
	this.onIdle								= "";
	//== Global "Error Handler" function for the entire session object
	//==	assign to any function you want called for any errors
	this.ErrorHandler						= null;
	//== Global "Idle Handler" function for the entire session object
	//==	assign to any function you want called each time the session becomes idle
	this.IdleHandler						= null;
	//== PopupRequestID						the request we want the Popup.html file to make for us
	this.PopupRequestID						= null;
	this.Processing							= false;
	this.ArrayHasChanged					= false;
	//== FormParams							gather up the query string parameters for a <form>
	//==										should provide the same query string that a "submit" would
	this.FormParams							= function(f){
		var sRet							= "";
		var sName;
		var sVal;
		var bAddIt;
		for (var iLoop=0; iLoop < f.elements.length; iLoop++){
			bAddIt							= true;
			sName							= f.elements[iLoop].name;
			if (sName.length < 1){
				sName						= f.elements[iLoop].id;
			}
			switch (f.elements[iLoop].type){
				case "hidden":
					if (sName.substr(sName.length-4) == "_cbx") {
						//== Ignore the old hidden "<fieldname>_cbx" checkbox fields
						//==	they were there only for when we used to do submits of the forms
						bAddIt				= false;
					}else{
						sVal				= f.elements[iLoop].value;
					}
					break;
				case "checkbox":
					//== use the "checked" value
					sVal				= f.elements[iLoop].checked;
					break;
				case "radio":
					//== Only send the "checked" radio buttons
					bAddIt					= (f.elements[iLoop].checked);
					sVal					= f.elements[iLoop].value;
					break;
				default:
					sVal					= f.elements[iLoop].value;
			}
			if (bAddIt){
				sRet						= sRet + "&" + sName + "=" + sVal;
			}
		}
		if (sRet.length > 0){
			sRet							= sRet.substr(1);
		}
		return sRet;
	}
	this.SetSessionID 						= function(sSession){
		this.SessionParam 					= sSession;
	}
	//== RequestFromID						returns a request object referred to by the RequestID
	this.RequestFromID						= function(RequestID){
		for (var iLoop=0; iLoop < this.Requests.length; iLoop++){
			if (this.Requests[iLoop].RequestID == RequestID){
				return this.Requests[iLoop];
			}
		}
	}
	//== RequestCompleted					call this from "onload" events of windows/frames to kick off
	//==										the "ProcessRequests" without waiting for the next timeout
	//==									not absolutely needed, but it can speed up things
	this.RequestCompleted					= function(){
		this.ProcessRequests();
	}
	//== ErrorSet							set the error information
	this.ErrorSet							= function(oReq, iCode, sText){
		oReq.ErrorCode						= iCode;
		oReq.ErrorText						= sText;
		var bDoGlobalHandler				= true;
		switch (oReq.ErrorAction){
			case REQUEST_ACTION_NONE:
				break;
			case REQUEST_ACTION_DELETE:
				this.DeleteRequests();
				break;
		}
		//== First see if the system will handle the error with the global error handler
		if (oReq.Status == REQUEST_STATUS_TIMEOUT){
			if (oReq.TimeoutHandler){
				oReq.TimeoutHandler(oReq);
				bDoGlobalHandler			= false;
			}
		}
		if (bDoGlobalHandler){
			if (this.ErrorHandler){
				oReq.ErrorHandled			= this.ErrorHandler(oReq);
			}
		}
		if (!oReq.ErrorHandled){
			//== If the global error handler did not handle the error, see if the
			//==	request has a specific error handler
			oReq.onError				= this.EventCodeExecute("onError", oReq.onError);
		}
	}
	//== ResultsLoad						load the results from a file like fEmptyResult.html (all of the <a> elements)
	this.ResultsLoad						= function(oReq, sTagName){
		var id;
		var oElem;
		var aElements						= oReq.Destination.document.getElementsByTagName(sTagName);
		for (var iLoop=0; iLoop < aElements.length; iLoop++){
			oElem							= aElements[iLoop];
			try{
				if (oElem.id.length > 0){
					id								= oReq.Results.length;
					oReq.Results[id]				= new RequestResultObj(oElem.id, oElem.innerHTML);
					DebugText("Result #" + id + " for req#" + oReq.RequestID + ": " + oElem.id + "=" + oElem.innerHTML);
				}
			}catch(err){
				//== Ignore this error, problems accessing the "id" property
			}
		}
	}
	//== SetContentOld						sets the content of the destination as "old"
	//==										meaning that we know any existing content is not from the current request
	//==										but from a previous request
	this.SetContentOld						= function(oReq){
		//== Mark any existing document in the destination frame/window as "old"
		try{
			oReq.Destination.contentWindow.EEM6ContentOld 	= true;
		}catch(err){
			try{
				oReq.Destination.EEM6ContentOld				= true;
			}catch(err){
				//== Nothing
			}
		}
	}
	//== ContentIsOld						returns whether or not the content for a destination is old
	//==										this helps us avoid declaring a "reused" window from being loaded
	//==										when really we are still waiting for the new request to populate it
	//==										and what we are looking at is the old content
	this.ContentIsOld						= function(oReq){
		var bRet							= false;
		try{
			bRet							= oReq.Destination.contentWindow.EEM6ContentOld;
		}catch(err){
			try{
				bRet						= oReq.Destination.EEM6ContentOld;
			}catch(err){
				//== Nothing
			}
		}
		return bRet;
	}
	//== CheckResults						the automated checking for things like fEmptyResult.html
	this.CheckResults						= function(oReq){
		var bRet							= false;
		if (oReq.CheckResults){
			//== HTML originated from fEmptyResult.html
			//== 	check for <a> values
			DebugText("Doing an automatic check on req#" + oReq.RequestID + " results, assuming the HTML is from fEmptyResults.html");
			this.ResultsLoad(oReq, "a");
			if ("true" == oReq.ResultValue("received")){
				sMsg						= oReq.ResultValue("ErrorMessage");
				if (sMsg.length > 0){
					//== Found something like: 		<a id="ErrorMsg">Some message</a>
					oReq.Status				= REQUEST_STATUS_ERROR;
					this.ErrorSet(oReq, REQUEST_ERROR_FOUND_MSG, "Server returned error of: " + sMsg);
					DebugText("[SERV REQ #" + oReq.RequestID + "] Server returned error of: " + sMsg);
				}else{
					bRet					= true;
				}
			}else{
				oReq.Status					= REQUEST_STATUS_ERROR;
				this.ErrorSet(oReq, REQUEST_ERROR_NOT_RECEIVED, "Server never acknowledged they received the request");
				DebugText("[SERV REQ #" + oReq.RequestID + "] Request not received from server");
			}
		}else{
			bRet 							= true;
		}
		return bRet;
	}
	this.RequestDocument					= function(oReq){
		var oDoc							= null;
		try{
			//== try to get the document for this request
			oDoc							= oReq.Destination.document;
			//== Now try to get the document if we are in a frame
			oDoc							= oReq.Destination.contentWindow.document;
		}catch(err){
			//== No big deal, this happens if we are not in a frame
		}
		return oDoc;
	}
	this.CheckRequestStatus					= function(oReq){
		var oDoc;
		var sState;
		var sBody;

		if (oReq.RequestType == REQUEST_TYPE_POPUP){
			//== Don't do anything on pop-ups
		}else{
			oDoc								= this.RequestDocument(oReq);
			try{
				sState							= oDoc.readyState;
			}catch(err){
				sState							= "doc_access";
			}
		}
		if (this.ContentIsOld(oReq)){
			sState								= "loading";
		}
		if (sState == "complete"){
			try{
				var oA							= oDoc.createElement("a");
				oA.style.zIndex					= "999";
				oA								= oDoc.body.appendChild(oA);
				oA.appendChild(oDoc.createTextNode(""));
			}catch(err){
				sState							= "loading";
			}
		}
		if (oReq.NoResult){
			sState								= "noresult";
		}
		switch (sState){
			case "doc_access":
				oReq.Status						= REQUEST_STATUS_ERROR;
				this.ErrorSet(oReq, REQUEST_ERROR_DOCACCESS, "Document Access");
				DebugText("[SERV REQ #" + oReq.RequestID + "] Document Access");
				DebugError("Could not access the document for request #" + oReq.RequestID + " (" + oReq.DestName + ")", err);
				break;
			case "":
				break;
			case "uninitialized":
			case "interactive":
			case "loading":
			case "loaded":
				//== Default the status to "Waiting"
				oReq.Status					= REQUEST_STATUS_WAITING;
				if (!oReq.WaitForever){
					var dNow					= new Date();
					if (((dNow - oReq.TimeRequested) / 1000) > oReq.TimeoutSeconds){
						oReq.Status				= REQUEST_STATUS_TIMEOUT;
						this.ErrorSet(oReq, REQUEST_ERROR_TIMEOUT, "Request timed out");
						DebugText("[SERV REQ#" + oReq.RequestID + "] Timed out");
					}
				}
				break;
			case "complete":
				sBody						= DefaultValue(oDoc.documentElement.innerHTML, "");
				sBody						= sBody.toUpperCase();
				if (sBody.indexOf(ERR_PAGE_ACCESS_DENIED) >= 0){
					oReq.Status				= REQUEST_STATUS_ERROR;
					this.ErrorSet(oReq, REQUEST_ERROR_ACCESS, "Access Denied");
					DebugText("[SERV REQ #" + oReq.RequestID + "] Access Denied");
				}else if (sBody.indexOf(ERR_PAGE_MISSING_SESSION) >= 0){
					oReq.Status				= REQUEST_STATUS_ERROR;
					this.ErrorSet(oReq, REQUEST_ERROR_SESSION, "Missing Session ID");
					DebugText("[SERV REQ #" + oReq.RequestID + "] Missing Session ID");
				}else if (sBody.indexOf(ERR_PAGE_NOT_FOUND) >= 0){
					oReq.Status				= REQUEST_STATUS_ERROR;
					this.ErrorSet(oReq, REQUEST_ERROR_NOTFOUND, "Page Not Found");
					DebugText("[SERV REQ #" + oReq.RequestID + "] Page Not Found");
				}else if (sBody.indexOf(ERR_RESOURCE_NOT_FOUND) >= 0){
					oReq.Status				= REQUEST_STATUS_ERROR;
					this.ErrorSet(oReq, REQUEST_ERROR_NOTFOUND, "Resource Not Found");
					DebugText("[SERV REQ #" + oReq.RequestID + "] Resource Not Found");
				}else if (sBody.indexOf(ERR_PAGE_NO_DATA) >= 0){
					oReq.Status				= REQUEST_STATUS_ERROR;
					this.ErrorSet(oReq, REQUEST_ERROR_NODATA, "No Data Available");
					DebugText("[SERV REQ #" + oReq.RequestID + "] No Data Available");
				}else{
					oReq.Status				= REQUEST_STATUS_DONE;
					if (this.CheckResults(oReq)){
						oReq.TimeCompleted		= new Date();
						DebugText("[SERV REQ #" + oReq.RequestID + "] Processed in " + (oReq.TimeCompleted - oReq.TimeRequested)/1000 + " seconds");
						oReq.onLoad				= this.EventCodeExecute("onLoad", oReq.onLoad);
					}
				}
				//== Trick browser Progress Bar into clearing out, getting rid of any "endless blue progress bar"
//				EEM6().setTimeout("EEM6().ClearProgressBar();", 250);
				break;
			case "noresult":
				oReq.Status					= REQUEST_STATUS_DONE;
				oReq.TimeCompleted			= new Date();				
				DebugText("[SERV REQ #" + oReq.RequestID + "] Processed in " + (oReq.TimeCompleted - oReq.TimeRequested)/1000 + " seconds (no result checked)");
				oReq.onLoad					= this.EventCodeExecute("onLoad", oReq.onLoad);
				break;
			default:
				DebugText("[SERV REQ #" + oReq.RequestID + "] Unknown loading state of '" + sState + "'.");
		}
	}
	this.EventCodeExecute						= function(sName, sCode){
		if (sCode.length > 0){
			DebugText(sName + ": executing command " + sCode);
			eval(sCode);
		}
		sCode									= "";
		return sCode;
	}
	this.PopupLoaded							= function(){
		var oReq								= this.RequestFromID(this.PopupRequestID);
		oReq.Status								= REQUEST_STATUS_DONE;
		oReq.TimeCompleted						= new Date();
		DebugText("[SERV REQ #" + oReq.RequestID + "] Popup loaded in " + (oReq.TimeCompleted - oReq.TimeRequested)/1000 + " seconds");
		oReq.onLoad								= this.EventCodeExecute("onLoad", oReq.onLoad);
		this.PopupRequestID						= null;
	}
	//== ProcessRequests						go through the requests array and see if there is anything to do
	this.ProcessRequests						= function(){	
		if (this.Processing){
			return false;
		}
		this.Processing							= true;
		var oReq;
		var bRequestMore						= true;
		var bWaiting							= false;
		var bCheckAgain							= false;
		var bClearRequests						= false;
		if (NotNull(this.TimeoutID)){
			self.clearTimeout(this.TimeoutID);
		}
		for (var iLoop = 0; iLoop < this.Requests.length; iLoop++){
			if (this.ArrayHasChanged){
				iLoop 							= 0;
				this.ArrayHasChanged			= false;
			}
			oReq								= this.Requests[iLoop];
			bCheckAgain							= false;
			switch (oReq.Status) {
				case REQUEST_STATUS_NONE:
					oReq.URL					= this.ServerRequest(oReq.Request, oReq.Params);
					if (bClearRequests){
						//== Previous request timed out and we want to clear any after it
						oReq.Status				= REQUEST_STATUS_ABORTED;
						DebugText("[SERV REQ #" + oReq.RequestID + "] aborted " + oReq.DestName + "=" + oReq.URL);
						break;
					}else if(oReq.RequestType == REQUEST_TYPE_POPUP){
						//== Popup Window
						this.PopupRequestID		= oReq.RequestID;
						DebugText("[SERV REQ #" + oReq.RequestID + "] requesting " + oReq.DestName + "=" + oReq.URL);
						oReq.TimeRequested		= new Date();
						try{
							//== Put the result from the server into a popup window
							oReq.Status			= REQUEST_STATUS_WAITING;
							WindowOpenModal(oReq.URL, oReq.WindowParams);
						}catch(err){
							//
						}
						break;
					}else{
						//== OK to request another
						DebugText("[SERV REQ #" + oReq.RequestID + "] requesting " + oReq.DestName + "=" + oReq.URL);
						oReq.TimeRequested		= new Date();
						if (oReq.RequestType == REQUEST_TYPE_WINDOW){
							//== Open up a new window and set the contents to the result of the
							//==	server request
							WindowOpen(oReq.URL, oReq.WindowParams);							
						}else{
							//== Existing window/frame, make sure to mark existing
							//==	content as "old"
							this.SetContentOld(oReq);
							try{
								//== Depending upon Destination type, user either .location or .src
								//== For window, frame use .location
								oReq.Destination.location	= oReq.URL;
								//== For iframe use .src
								oReq.Destination.src		= oReq.URL;
							}catch(err){
								//
							}
						}
						oReq.Status				= REQUEST_STATUS_WAITING;
						//== Don't "break" here, continue on to the
						//==	REQUEST_STATUS_WAITING code	
					}
				case REQUEST_STATUS_WAITING:
					bCheckAgain					= true;
					this.CheckRequestStatus(oReq);
			}
			if (bCheckAgain){
				switch (oReq.Status) {
					case REQUEST_STATUS_WAITING:
						bWaiting				= true;
						if (oReq.WaitForResponse){
							bRequestMore 		= false;
						}
						break;
					case REQUEST_STATUS_TIMEOUT:
						//== If we timed out on this one, clear any after it
						bClearRequests			= true;
				}
			}
			if (!bRequestMore){
				break;
			}
		}
		if (bWaiting){
			//== Set a timeout to call this same function again
			this.TimeoutID 						= self.setTimeout(this.ObjectName + ".ProcessRequests()", REQUEST_POLL_MILLISECONDS);
		}else{
			//== run the one-time "on idle" code, if any
			this.onIdle							= this.EventCodeExecute("Session.onIdle", this.onIdle);
			//== run the global "on idle" handler, if any
			if (this.IdleHandler){
				this.IdleHandler();
			}
			WindowEnable(EEM6());
		}
		this.Processing							= false;
	}
	//======================================================================================
	//== AddRequest									adds a new request to our request queue
	//======================================================================================
	this.AddRequest									= function(oReq, bProcess){
		//== Default bProcess to true if it was not passed in
		bProcess											= DefaultValue(bProcess, true);
		//== For now, always send each request out one at a time
		//==		therefore, even if they set oReq's WaitForResponse to false, override it for now
		oReq.WaitForResponse					= true;
		//== Disable the browser windows/framesets
		WindowDisable(EEM6());
		//== Indicate that we have changed the array contents
		this.ArrayHasChanged					= true;
		//== determine the index for the new request in the request array
		var id												= this.Requests.length;
		//== Is it time to trim down the request array?
		if (id > REQUEST_ARRAY_MAX) {
			DebugText("Removing " + REQUEST_ARRAY_TRIM + " items from the request array");
			this.Requests.splice(0, REQUEST_ARRAY_TRIM);
			id													= this.Requests.length;
		}
		//== Set the RequestID (different from the array index) to a unique sequence number
		oReq.RequestID								= this.TopRequestID++;
		//== Store the new request into the request array
		this.Requests[id]							= oReq;
		DebugText("Request object added: " + oReq.DestName 
									+ ", " + oReq.Request + ", " + oReq.Params + ", " + oReq.WaitForResponse);
		//== Do we want to go ahead and start processing this (and any other) requests?
		if (bProcess){
			this.ProcessRequests();
		}
		//== Return the newly added request object
		return oReq;
	}
	//======================================================================================
	//== DeleteRequests							deletes all remaining requests that have not been sent yet
	//======================================================================================
	this.DeleteRequests							= function(){
		var oReq;
		for (var iLoop=0; iLoop < this.Requests.length; iLoop++){
			oReq								= this.Requests[iLoop];
			if (oReq.Status == REQUEST_STATUS_NONE){
				oReq.Status						= REQUEST_STATUS_ABORTED;
				DebugText("[SERV REQ #" + oReq.RequestID + "] aborted " + oReq.DestName + "=" + oReq.URL);
			}
		}
	}
	this.ServerRequest = function(sRequest, sParams){
		var sRet;
		sRet			= this.DLLFile + sRequest;
		if (NotNull(this.SessionParam)){
			sRet 			= sRet + this.SessionParam;
		}
		if (NotNull(sParams)){
			if (sParams.substr(0,1) != "&"){
				sRet		= sRet + "&";
			}
			sRet 			= sRet + sParams;
		}
		return sRet;
	}
	this.init									= function(){
		if (Debugging()){
			//== Debugging, give us an hour if we are stepping through the dataminer.dll code
			TIMEOUT_SERVER						= 60*60;	
		}
	}
	this.init();
}
