/************************************************************************************************
** FileName: LMSInteraction.js
** Created Date: March 18, 2004
** Designer: Phil
** Implementer: Hunter
** Description:
** This JS must be included in the top window of the course frameset. This is the only way to 
** ensure that the LMSInteraction data structures and functions are always present.
**
** The top window needs to capture onBeforeUnload() event and execute
** the function SendQueueToLMSInteraction()
************************************************************************************************/

/************************************************************************************************
**Functions 
**1.InitializeCourseVars()

**--Interaction Entry point functions--
**2.InteractionInitialize()
**3.InteractionSetValue(param,value)
**4.InteractionGetValue(param)
**5.InteractionFinish()
**6.InteractionCommit()
**7.InteractionGetLastError()
**8.InteractionGetErrorString(param)
**9.InteractionGetDiagnostic(param)

**--Queue Management functions--
**10.pushValueToQueue()
**11.setValueToQueue(param,value)
**12.retrieveQueueArray()
**13.getQueueSize()
**14.sendQueueToLMSInteraction()
**15.resetQueue()

**--Local CacheBuffer data storage functions--
**16.setValueToCacheBuffer(param, value) 
**17.getValueFromCacheBuffer(param) 

**--LMS Interaction functions
**18.LMSInteractionIsAvailable(nTimeOut)
**19.LMSExecuteAsync(LMSCommand, param1, param2, callBackFunction)
**20.beginLMSInteraction(LMSCommand, param1, param2, callBackFunction)
**21.LMSInteractionProgress(params)
**22.OnEndLMSInteraction()

**--Other functions
**23.Sleep(millSeconds)
**24.resizeWindow()
************************************************************************************************/

/************************************************************************************************
** Declarations 
************************************************************************************************/
// Player Window configuration:
var PlayerWidth=800;
var PlayerHeight=600;

// State variables
var nQueueSize=0;        // Current queue size (bytes)
var nQueueLength=0;      // Number of instructions waiting to be updated

// LMSInteraction variables
var LMSProxy=null, LMSConnectionProxy=null;	 // reference to the LMS Proxy (modal/modaless window)


var LMSInteractionTimeout=30000; // Number of milliseconds to wait during LMSProxy, before it kills itself

// other state variable and flags to manage the LMSInteraction
var LMSInteraction="";		 // Variable to hold the name of the Pending LMSInteraction
var LMSInteractionReturnVal=null;// Variable to hold return value after LMS Interaction has completed
var LMSPendingItem=null;
var LMSMissingQuestion=null;
var LMSLastErrorCode=0;		 // LMSGetLastError error code

var LMSParam1, LMSParam2, SuspendData, PlayerCallBack; 
var isConnected=true, isChecking=false;
var connectionInterval, checkTimeout;
var numOfCheck=0;

var objErrWindow = null;
var nErrCounter = 0;
var N, E, D, nPHI, P, Q;

//only allow synchronous mode for realtime update
if (top.frames.DisplayFrame.LMSUpdateMode==4) top.frames.DisplayFrame.LMSInteractionMode=1;

P = 13;
Q = 17;
N = P*Q;
nPHI = (P-1)*(Q-1);

APIWin = returnAPIWin();
if (APIWin == null)
{
	open("SeedPlayer.html", "_self");
	//alert("Unable to find an API adapter");
}


/************************************************************************************************
// Interaction Entry point functions
************************************************************************************************/

//used to Initializes the course DATA (i.e., during launch)
function InteractionInitializeData(callBackFunction) {
	if (top.frames.DisplayFrame.LMSUpdateMode!=0) {
		connectionInterval = window.setInterval(LMSCheckConnection, checkConnectionTime);
		// Send command to LMS Server
		LMSExecuteAsync("InitializeData",CourseVars["ENROLLMENTID"],null,callBackFunction);		
	}
	else{//offline mode
		eval(callBackFunction);
	}
}

//***************************************************************************
//used to start an interaction session
function InteractionInitialize() {
	
	var retval=false;
	var startTime = new Date();
	var curTime;
	var nTimeOut=3000;	

	// if the LMSInteractionMode=1, then wait here until LMSInteractionMode=0 (or timeout)
	if (isChecking==true) {	
		while (isChecking==true) {
			curTime = new Date();
			if (curTime-startTime<nTimeOut){
				// Sleep() literally pauses for specified milliseconds
				Sleep(100);
			} 
			else break;
		}
	}
	else retval=true;

	//if the connection is loss
	if (!isConnected){
		LMSLastErrorCode = 101;
		return false;
	}

	// return true or false
	return retval;
}

//***************************************************************************
function InteractionSetValue(param,value) {
	var retval=false;

	switch (top.frames.DisplayFrame.LMSUpdateMode){
/*		case 4 :
			// Realtime interaction with LMS
//			retval = setValueToCacheBuffer(param,value);
//			retval = LMSExecuteAsync("LMSSetValue",param,value);
//			break;
*/			
		default:// other modes 
			// set the value for param directly to the local CacheBuffer data
			retval = setValueToCacheBuffer(param,value);
	}
	return retval;
}

//***************************************************************************
function InteractionGetValue(param) {
	var retval=false;	

	switch (top.frames.DisplayFrame.LMSUpdateMode)
	{
/*		case 4 :
			// Realtime interaction with LMS
			retval=LMSExecuteAsync("LMSGetValue",param);
			break;
*/
		default :
			// retrieve the value for param directly from the local CacheBuffer data
			retval=getValueFromCacheBuffer(param);
	}
	return retval;
}

//***************************************************************************
function InteractionFinish() {
	//An opportunity for the LMSInteraction to clean up, up to the developer
	pushValueToQueue();
	return true;
}

//***************************************************************************
//Used to commit an interaction session
function InteractionCommit() {

	switch (top.frames.DisplayFrame.LMSUpdateMode)
	{
		case 0 :
			// Offline Mode - Do Nothing
			break;
		case 4 :
			// Realtime interaction with LMS - Do Nothing
			break;
		default :
			// Forces a direct LMS server update of currently queued data
			sendQueueToLMSInteraction();
			return (InteractionGetLastError()==0);
	}
}

//***************************************************************************
function InteractionGetLastError() {
	// Special interaction function for wrapping LMSGetLastError.
	// We provide an opportunity for the LMSInteraction code
	// to handle its own GetLastError management.  LMSInteraction can
	// decide whether or not to connnect to the LMS.

	// If appropriate, retrieve LMSGetLastError from the LMS:
	//var nLMSLastErrorCode = LMSExecute("LMSGetLastError");

	var nLMSLastErrorCode;
	nLMSLastErrorCode=LMSLastErrorCode;
	
	// Reset LMSLastErrorCode state variable.
	LMSLastErrorCode=0;

	return nLMSLastErrorCode;
}

//***************************************************************************
function InteractionGetErrorString(param) {
	// Special interaction function for wrapping LMSGeErrorString.
	// We provide an opportunity for the LMSInteraction code
	// to handle its own GeErrorString management.  LMSInteraction can
	// decide whether or not to connnect to the LMS.
	
	var cErrorString;
	
	// Perhaps do some code to lookup error code param and retrieve error string
	cErrorString="We looked up the error for code " + param;

	// If appropriate, retrieve LMSGeErrorString from the LMS:
	//cErrorString=LMSExecute("LMSGeErrorString",param);
	switch (param){
		case 0:
			cErrorString = "No Error";
			break;
		case 101:
			cErrorString = "General Exception";
			break;
		case 201:
			cErrorString = "Invalid argument error";
			break;
		case 202:
			cErrorString = "Element can not have children";
			break;
		case 203:
			cErrorString = "Element not an array. Can not have count";
			break;
		case 301:
			cErrorString = "Not initialized";
			break;
		case 401:
			cErrorString = "Not implemented error";
			break;
		case 402:
			cErrorString = "Invalid set value, element is a keyword";			
			break;
		case 403:
			cErrorString = "Element is read only";
			break;
		case 404:
			cErrorString = "Element is write only";
			break;
		case 405:
			cErrorString = "Incorrect data type";
			break;
	}

	return cErrorString;
}

//***************************************************************************
function InteractionGetDiagnostic(param) {
	// Special interaction function for wrapping LMSGetDiagnostic.
	// We provide an opportunity for the LMSInteraction code
	// to handle its own GetDiagnostic management.  LMSInteraction can
	// decide whether or not to connnect to the LMS.
	
	var cDiagnosticString;	
	cDiagnosticString="We looked up the Diagnostic for code " + param;

	switch (param){
		case 0:
			cDiagnosticString = "No errors encountered. Successful API call";
			break;
		case 101:
			cDiagnosticString = "General Exception";
			break;
		case 201:
			cDiagnosticString = "Data model element does not exist";
			break;
		case 202:
			cDiagnosticString = "Data model category or element does not support _children";
			break;
		case 203:
			cDiagnosticString = "Data model category or element does not support _count";
			break;
		case 301:
			cDiagnosticString = "LMSInitialize must be called before calling any other API functions";
			break;
		case 401:
			cDiagnosticString = "Data model element is not supported by the LMS";
			break;
		case 402:
			cDiagnosticString = "A keyword can not be set value";			
			break;
		case 403:
			cDiagnosticString = "A read-only element can not be set value";
			break;
		case 404:
			cDiagnosticString = "A write-only element can not be get value";
			break;
		case 405:
			cDiagnosticString = "Incorrect data type";
			break;
	}	

	return cDiagnosticString;
}

//***************************************************************************
function UnloadCoursePlayer() {
	// At the very least we must do an InteractionCommit(), perhaps there are other tasks too
	LMSInteractionMode = 1;
	return InteractionCommit();
}

/*******************************************************************************
** Queue Management functions
*******************************************************************************/
var QueueBuffer = "";
var QueueCommand = 0;
var PendingItemIndex = -1

//***************************************************************************	
function pushValueToQueue(){
	
	if (PendingItemIndex < 0) {
		return true;
	}
	
	// save value of the pending item to the queue
	QueueBuffer = QueueBuffer + "-" + top.frames.DisplayFrame.rootObj.children[PendingItemIndex].itemID + "*";	
	// substitute sessionTime for masteryTime to send to LMS
	QueueBuffer = QueueBuffer + top.frames.DisplayFrame.rootObj.children[PendingItemIndex].sessionTime + "*";	
	//QueueBuffer = QueueBuffer + top.frames.DisplayFrame.rootObj.children[PendingItemIndex].masteryTime + "*";	
	QueueBuffer = QueueBuffer + top.frames.DisplayFrame.rootObj.children[PendingItemIndex].itemStatus + "*";				
	if (top.frames.DisplayFrame.rootObj.children[PendingItemIndex].itemType == "Question Pool"){
		QueueBuffer = QueueBuffer + top.frames.DisplayFrame.rootObj.children[PendingItemIndex].rawScore + "*";	
		QueueBuffer = QueueBuffer + top.frames.DisplayFrame.rootObj.children[PendingItemIndex].maxScore;			
	}
	else{
		QueueBuffer = QueueBuffer + "0*0";	
	}
	
	switch (top.frames.DisplayFrame.LMSUpdateMode){
		case 0: //Do nothing			
			break;
		case 1: 
			if (QueueBuffer.length > top.frames.DisplayFrame.MaxQueueSize){
				sendQueueToLMSInteraction();
			}
			break;	
		case 2:			
			QueueCommand = QueueCommand + 1;
			if (QueueCommand > MaxQueueLength){
				sendQueueToLMSInteraction();
			}
			break;
		case 3: //Do nothing. Data will be sent when the time is out
			break;
		case 4: //Realtime mode
			sendQueueToLMSInteraction();
			break;
	}
}

//***************************************************************************
function setValueToQueue(param, value){
	// We also update the local CacheBuffer data storage
	if (setValueToCacheBuffer(param, value)) {
		// note, to remove an item, send the NULL for the value
		return true;
	} else return false;
}

//***************************************************************************
function retrieveQueueArray(){	
	if (QueueBuffer.length>0) return QueueBuffer.substring(1, QueueBuffer.length);
	else return "";
}

//***************************************************************************
function getQueueSize()
{
	// recalculate queue size and set nQueueSize
	return QueueBuffer.length;
}

//***************************************************************************
function sendQueueToLMSInteraction()
{
	// immediately process all currently queued instructions
	//    call LMSExecute() as appropriate

	var queueArray = retrieveQueueArray(); // Retrieve Data from Queue
	if (queueArray != ""){
		// Send data to LMS Server	
		LMSExecuteAsync("LMSUpdateData", queueArray);
		//Reset SessionTime to 0
		top.frames.DisplayFrame.rootObj.children[PendingItemIndex].sessionTime = 0;
	}

	resetQueue();
}

//***************************************************************************
//used to empty QueueBuffer
function resetQueue(){
	
	QueueBuffer = "";
	switch (top.frames.DisplayFrame.LMSUpdateMode){
		case 0: //Do nothing
			break;
		case 1:	//Do nothing		
			break;
		case 2: //Reset command
			QueueCommand = 0;
			break;
		case 3: //Do nothing
			break;
		case 4: //Do nothing
			break;
	}
}

//***************************************************************************
//***************************************************************************
//** local CacheBuffer data storage functions
//***************************************************************************/

function setValueToCacheBuffer(param, value) 
{
	// save the value of the param to the local CacheBuffer data storage
	// note, to remove an item, send NULL for the value

	var i;

	if (param=="cmi.objectives.0.id"){
		// find and set PendingItemIndex 
		// which will be used for set other parameters
		top.frames.DisplayFrame.PendingItemID = value;

		for (i=0; i<top.frames.DisplayFrame.rootObj.nChildren; i++){
			if (top.frames.DisplayFrame.rootObj.children[i].itemID == top.frames.DisplayFrame.PendingItemID){			
				PendingItemIndex = i;
				top.frames.DisplayFrame.rootObj.children[i].dirtyFlag = 1;
				break;
			}
		}
	}
	else{ //Set other param base on PendingItemIndex
		if (PendingItemIndex < 0) {
			LMSLastErrorCode = 201;
			return false;
		}

		switch(param){
			case "cmi.objectives.0.mastery_time":
				//only store as second, not milisecond for save space
				// substitute sessionTime for masteryTime
				top.frames.DisplayFrame.rootObj.children[PendingItemIndex].sessionTime = parseInt(top.frames.DisplayFrame.rootObj.children[PendingItemIndex].sessionTime) + Math.round(value/1000);
				top.frames.DisplayFrame.rootObj.children[PendingItemIndex].masteryTime = parseInt(top.frames.DisplayFrame.rootObj.children[PendingItemIndex].masteryTime) + Math.round(value/1000);
				break;
			case "cmi.objectives.0.status.0":
				switch(value){
					case "completed":
						top.frames.DisplayFrame.rootObj.children[PendingItemIndex].itemStatus = 3;
						updateParentStatus(top.frames.DisplayFrame.rootObj.children[PendingItemIndex].parentID);
						break;
					case "uncompleted":
						top.frames.DisplayFrame.rootObj.children[PendingItemIndex].itemStatus = 2;
						break;
					case "not attempted":
						top.frames.DisplayFrame.rootObj.children[PendingItemIndex].itemStatus = 1;
						break;
				}
				break;
			case "cmi.objectives.0.scores.0.raw":
				top.frames.DisplayFrame.rootObj.children[PendingItemIndex].rawScore = value;
				break;
			case "cmi.objectives.0.scores.0.max":
				top.frames.DisplayFrame.rootObj.children[PendingItemIndex].maxScore = value;
				break;	
			case "cmi.suspend_data":
				break;
			default:
				LMSLastErrorCode = 401;
				return false;
				break;
		}
	}
	return true;
}

//****************************************************************************
function getValueFromCacheBuffer(param)
{
	// retrieve the value for the param from the local CacheBuffer data storage

	if (PendingItemIndex < 0) {
		LMSLastErrorCode = 201;
		return "false";
	}

	var returnValue;

	switch(param){
		case "cmi.objectives.0.mastery_time":
			returnValue = top.frames.DisplayFrame.rootObj.children[PendingItemIndex].masteryTime
			break;
		case "cmi.objectives.0.status.0" :
			switch (top.frames.DisplayFrame.rootObj.children[PendingItemIndex].itemStatus.toString()){
				case "1": 
					returnValue = "not attempted";
					break;
				case "2":
					returnValue = "incompleted";
					break;
				case "3":
					returnValue = "completed";
					break;
			}
			break;
		
			case "cmi.objectives.0.scores.0.raw" :
			returnValue = parseInt(top.frames.DisplayFrame.rootObj.children[PendingItemIndex].rawScore);
			break;
		case "cmi.objectives.0.scores.0.max" :
			returnValue = parseInt(top.frames.DisplayFrame.rootObj.children[PendingItemIndex].maxScore);
			break;	
		default:
			//process for returning error code
			LMSLastErrorCode = 401;			
			returnValue = "false";
			break;
	}

	return returnValue;
}

/*******************************************************************************
** LMS Interaction functions
** These are called by utilities that rely on LMSInteraction
*******************************************************************************/

function LMSCheckConnection(){
/* oVeRLoRDz removed this feature Sep 21 2004	
	//alert("LMSCheckConnection");
	if ((LMSConnectionProxy == null) && (objErrWindow==null)){
		//alert("LMSConnectionProxy is null");
		isChecking=true;
		LMSInteractionReturnVal=null;
		LMSInteraction="LMSCheckConnection";
		PlayerCallBack = "LMSConnectionOK()";
		//alert("open CheckConnectionWindow");
		LMSConnectionProxy = window.showModelessDialog("scripts/LMSCheckConnection.html",window,"help:0;status:0;dialogLeft:0;dialogTop:0;dialogWidth:300px;dialogHeight:100px");	
		//LMSConnectionProxy = window.showModelessDialog("scripts/LMSCheckConnection.html",window,"help:0;status:0;dialogLeft:2400px;dialogTop:1800px;dialogWidth:100px;dialogHeight:100px");
		//document.all("ifrCheckConnection").src= "scripts/LMSCheckConnection.html";		
		//LMSConnectionProxy = document.all("ifrCheckConnection").document;
		checkTimeout = window.setTimeout(LMSRemoveChecking, checkLossConnectionTime);
	}
*/
}

//**********************************************************************
function LMSConnectionOK(){	
	
	if (LMSConnectionProxy) {
		// Close the LMSProxy window for asynchronous mode		
		LMSConnectionProxy.close();		
	}		
	isChecking=false;
	isConnected=true;		
	window.clearTimeout(checkTimeout);

	LMSConnectionProxy = null;
	
	//The connection check must result in a success code:
	if (!(typeof LMSInteractionReturnVal == 'string' && LMSInteractionReturnVal == 'OK')) {
		if (numOfCheck==totalOfCheck-1){
			isConnected=false;
			isChecking=true;
			LMSLostConnectionProcess();
			if (objErrWindow) objErrWindow.focus();
		}
		else numOfCheck = numOfCheck + 1;
	}
	else numOfCheck=0;
}

//**********************************************************************
function LMSRemoveChecking(){

	if (LMSConnectionProxy) {
		// Close the LMSProxy window for asynchronous mode		
		numOfCheck = numOfCheck + 1;
		if (numOfCheck==totalOfCheck){
			isConnected=false;
			//VuNA			
			isChecking = true;
			LMSConnectionProxy.close();
			LMSConnectionProxy = null;
			window.clearTimeout(checkTimeout);
			LMSLostConnectionProcess();
		}
		else{
			window.clearTimeout(checkTimeout);
			checkTimeout = window.setTimeout(LMSRemoveChecking, checkLossConnectionTime);
		}
	}	
}

//**********************************************************************
function LMSLostConnectionProcess()
{	
	var ExitURL, TransferData, EncryptTransferData, PendingItem, TransferDataRaw;
	var szWindowContent="";
	var txtSuportEmail = "";

	TransferData = retrieveQueueArray();
	PendingItem = top.frames.DisplayFrame.PendingItemID;
	
	E = tofindE(nPHI,P,Q);

	if ((TransferData != "") && (TransferData != null)){
		EncryptTransferData = EncryptString(TransferData);
	}		
	
	if (objErrWindow == null){
			szWindowContent = "<HTML><HEAD><TITLE></TITLE></HEAD><BODY>" +	  
						   "<form name='frm' method='POST' action='"+updateURL+"'>\n" +
						   "<input type='hidden' name='EnrollmentID' value='"+EnrollmentID+"'>\n" +
						   "<input type='hidden' name='TransferData' value='"+EncryptTransferData+"'>\n" +
						   "<input type='hidden' name='PendingItem' value='"+PendingItem+"'>\n" +
						   "<input type='submit' value='Submit Data'>\n" +
						   "</form></BODY></HTML>\n";
													   																	  		  					  
			objErrWindow = window.open("","saveCourseInformation","toolbar=no,left=0, top=0, location=0,directories=no,status=no,menubar=1,scrollbars=no,resizable=1,height=220,width=400")
			objErrWindow.document.write("<HTML><HEAD><TITLE>" + getPlayerStr('DISCONNECTION','TITLE') + "</TITLE>\n")
			objErrWindow.document.write("<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>\n")
			objErrWindow.document.write("<script language='javascript'>window.focus();</script>\n")
			objErrWindow.document.write("<script language='javascript'>\n")
			objErrWindow.document.write("function doTryAgain() {\n")
			objErrWindow.document.write(" if (window.opener && !window.opener.closed) { \n")
			objErrWindow.document.write("   window.opener.TryAgain();");
			objErrWindow.document.write("   this.close();");
			objErrWindow.document.write(" } \n")			
			objErrWindow.document.write("} \n")
			objErrWindow.document.write("function doExit() {\n")
			objErrWindow.document.write(" if (window.opener && !window.opener.closed) { \n")
			objErrWindow.document.write("   window.opener.TryAgain();");
			objErrWindow.document.write(" } \n")			
			objErrWindow.document.write("} \n")
			objErrWindow.document.write("function doSendMail() { \n")
			objErrWindow.document.write(" document.frmMail.action='mailto:" + mailAddress + "'; \n")
			objErrWindow.document.write(" document.frmMail.encoding='text/plain'; \n")					
			objErrWindow.document.write(" document.frmMail.submit(); \n")	
			objErrWindow.document.write(" alert('" + getPlayerStr('DISCONNECTION','MAILSENT') + " " + mailAddress + "'); \n")					
			objErrWindow.document.write(" if (window.opener && !window.opener.closed) { \n")
			objErrWindow.document.write("   opener.close();");
			objErrWindow.document.write(" } \n")
			objErrWindow.document.write(" this.close();");
			objErrWindow.document.write("} \n")
			objErrWindow.document.write("</script>\n")					
			objErrWindow.document.write("</HEAD><BODY onUnload='javascript:doExit();'>\n")
			objErrWindow.document.write("<p><font face='arial' size='2'>" + getPlayerStr('DISCONNECTION','SENTENCE1') + "</font></p>\n")
			objErrWindow.document.write("<p><font face='arial' size='2'>" + getPlayerStr('DISCONNECTION','SENTENCE2') + "&nbsp;<input type=\"button\" style=\"font-family:arial;font-size:10pt\" name=\"btnTry\" value=\"" + getPlayerStr('DISCONNECTION','TRYAGAINBUTTON') + "\" onclick=\"javascript:doTryAgain();\"></font></p>\n")			
			if (TransferData != "")
				objErrWindow.document.write("<p><font face='arial' size='2'>" + getPlayerStr('DISCONNECTION','SENTENCE3') + "&nbsp;<input type=\"button\" style=\"font-family:arial;font-size:10pt\" name=\"btnSendMail\" value=\"" + getPlayerStr('DISCONNECTION','SENDMAILBUTTON') + "\" onclick=\"javascript:doSendMail();\"></font></p>\n")
			objErrWindow.document.write("<p><font face='arial' size='2'>\n")			
			objErrWindow.document.write("<form name='frmMail' method=POST>\n")					
			objErrWindow.document.write("<input name=subject TYPE=hidden VALUE='" + getPlayerStr('DISCONNECTION','MAILSUBJECT') + "'>\n")
			objErrWindow.document.write("<input name=body TYPE=hidden value=\""+szWindowContent+"\">\n")										
			objErrWindow.document.write("</form>\n")					
			objErrWindow.document.write("</BODY></HTML>\n")
			objErrWindow.document.close()					
	}	
}

//**********************************************************************
function TryAgain(){
	isChecking=false;
	objErrWindow = null;
	numOfCheck=0;
	LMSCheckConnection();	
}

//**********************************************************************
function LMSInteractionIsAvailable(nTimeOut) {
	// We need to determine if the LMSInteraction structures are available so that LMSInitialize can allow
	// the course to interact with the API adapter

	// if the LMSInteractionMode=1, then wait here until LMSInteractionMode=0 (or timeout)
	var retval=false;
	if (LMSInteractionMode==1) {
		var startTime = new Date();
		var curTime, nTimeOut;
		if (!nTimeOut) nTimeOut=3000;		// 3 seconds
		while (LMSInteractionMode==1) {
			// A timeout check here so that we can eventually break out of this
			curTime = new Date();
			if (curTime-startTime<nTimeOut) {
				// Sleep() literally pauses for specified milliseconds
				Sleep(100);
			} else break;
		}
		retval=(LMSInteractionMode==0);
	} else retval=true;
	return retval;
}

//**********************************************************************
function LMSExecuteAsync(LMSCommand, param1, param2, callBackFunction) {
	// Wraps the LMS Interaction functions
	// Sends off the appropriate command to the LMS Synchronous or Asynchronous.

	beginLMSInteraction(LMSCommand,param1,param2,callBackFunction);
	return true;

}

//**********************************************************************
function beginLMSInteraction(LMSCommand, param1, param2, callBackFunction) {

	// set variables here that modal dialog can read or write:
	// LMS API command to execute, data to pass, return data, status,...
	LMSInteractionReturnVal=null;
	LMSPendingItem=null;
	top.frames.DisplayFrame.LMSInteraction=LMSCommand;	
	LMSParam1 = param1;
	LMSParam2 = param2;
	PlayerCallBack = callBackFunction;

	// Mr TNT _ March 11 th, 2004
	var e;
	var blnResult;

	if (top.frames.DisplayFrame.LMSInteractionMode == 0){
		// Open the modaless dialog window for asynchronous mode
		// Send data to LMS by Href of new Window
		LMSProxy = window.showModelessDialog(top.frames.DisplayFrame.urlCourseHref + "/scripts/LMSInteraction.html",window,"help:0;status:0;dialogLeft:0;dialogTop:0;dialogWidth:300px;dialogHeight:100px");
	}
	else{
		// Open the modal dialog window for synchronous mode
		
		// Mr TNT _ March 11 th, 2004
		blnResult = false;
		while(blnResult == false)
		{
			try
			{
				LMSProxy = window.showModalDialog(top.frames.DisplayFrame.urlCourseHref + "/scripts/LMSInteraction.html",window,"help:0;status:0;dialogLeft:0;dialogTop:0;dialogWidth:300px;dialogHeight:100px");				
				blnResult = true;
			}
			catch(e)
			{
				blnResult = false;
				alert("You must turn off Pop-up Blocker before continueing, please ...\n(Tip: open another IE window, check Tools\\Pop-up Blocker\\Turn Off Pop-up Blocker)");
			}
		}
		
		// Call OnEndLMSInteraction to get data to buffer
		OnEndLMSInteraction();		
		if (PlayerCallBack) {
			eval(PlayerCallBack);			
		}	
	}	

	if(LMSCommand=="LMSUpdateData"){		
		UpdatePercentComplete();
	}
}

//*******************************************************************
function OnEndLMSInteraction() {
	
	if (LMSProxy) {
		// Close the LMSProxy window for asynchronous mode		
		LMSProxy.close();
	}	

	if (LMSInteraction == "InitializeData"){
		SuspendData = LMSInteractionReturnVal;
		top.frames.DisplayFrame.PendingItemID = LMSPendingItem;
		
		//if mode 3 => set interval function
		if (top.frames.DisplayFrame.LMSUpdateMode==3){
			window.setInterval(sendQueueToLMSInteraction, QueueTime * 1000);
		}	
	}
	window.focus();
}

/*******************************************************************************
** Encrypt Data
*******************************************************************************/
function GCD(e,PHI){
	if (e > PHI) {
		while (e%PHI != 0) {
			a = e%PHI;
			e = PHI;
			PHI = a;
		}
		great = PHI;
	} 
	else {
		while (PHI%e != 0) {
			a = PHI%e;
			PHI = e;
			e = a;
		}
		great = e;
	}
	return great;
}

//*******************************************************************
function tofindE(PHI,P,Q){
	great = 0;
	e = 2;
	while (great != 1) {
		e = e + 1;
		great = GCD(e,PHI);
		PHI = (P - 1) * (Q - 1);
	}
	return e;
}

//*******************************************************************
function extend(E,PHI) {
	u1 = 1;
	u2 = 0;
	u3 = PHI;
	v1 = 0;
	v2 = 1;
	v3 = E;
  	while (v3 != 0) {
		q = Math.floor(u3/v3);
		t1 = u1 - q * v1;	
		t2 = u2 - q * v2;
		t3 = u3 - q * v3;		
		u1 = v1;
		u2 = v2;
		u3 = v3;
		v1 = t1;
		v2 = t2;
		v3 = t3;
		z = 1;
	}
	uu = u1;
	vv = u2;
	if (vv < 0) {
		inverse = vv + PHI;
	} 
	else {
		inverse = vv;	
	}
	return inverse;
}    

//*******************************************************************
function EncryptChar(M,E,N){
	var C = Math.pow(M,E) % N;
	return C;
}

//*******************************************************************
function EncryptString(s)
{
	var strEncrypt = "";
	for (var intCount=0; intCount<s.length; intCount++)
	{
		if (isNaN(parseInt(s[intCount])))
		{
			//alphabet
			strEncrypt = strEncrypt + EncryptChar(s.charCodeAt(intCount),E,N);
			strEncrypt = strEncrypt + "_";
		}
		else {
			//numeric
			strEncrypt = strEncrypt + EncryptChar(s[intCount],E,N);
			strEncrypt = strEncrypt + "_";
		}
	}
	return strEncrypt;
}

/*******************************************************************************
** Other functions
*******************************************************************************/

function Sleep(millSeconds) {
	// IE Only!
	var dialogScript = 'window.setTimeout(function () { window.close(); }, ' + millSeconds + ');';
//	var result = window.showModalDialog('javascript:document.writeln("<script>' + dialogScript + '<' + '/script>")');
	var result = window.showModalDialog('javascript:document.writeln("<script>' + dialogScript + '<' + '/script>")',window,"help:0;status:0;dialogLeft:2400px;dialogTop:1800px;dialogWidth:100px;dialogHeight:100px");
}

//*******************************************************************
function resizeWindow() {
	if (CourseVars["PLAYERWIDTH"])
		PlayerWidth=CourseVars["PLAYERWIDTH"];
	if (CourseVars["PLAYERHEIGHT"])
		PlayerHeight=CourseVars["PLAYERHEIGHT"];
	window.resizeTo(PlayerWidth,PlayerHeight);
}
