/*******************************************************************************
**
** FileName: APIWrapper.js
**
*******************************************************************************/

/******************************************************************************

Added by SimonC

*******************************************************************************/


/* End Added by SimonC */

/*******************************************************************************
**
** Concurrent Technologies Corporation (CTC) grants you ("Licensee") a non-
** exclusive, royalty free, license to use, modify and redistribute this
** software in source and binary code form, provided that i) this copyright
** notice and license appear on all copies of the software; and ii) Licensee does
** not utilize the software in a manner which is disparaging to CTC.
**
** This software is provided "AS IS," without a warranty of any kind.  ALL
** EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
** IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-
** INFRINGEMENT, ARE HEREBY EXCLUDED.  CTC AND ITS LICENSORS SHALL NOT BE LIABLE
** FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
** DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL CTC  OR ITS
** LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
** INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
** CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
** OR INABILITY TO USE SOFTWARE, EVEN IF CTC  HAS BEEN ADVISED OF THE POSSIBILITY
** OF SUCH DAMAGES.
**
*******************************************************************************/

/*******************************************************************************
** This file is part of the ADL Sample API Implementation intended to provide
** an elementary example of the concepts presented in the ADL Shareable
** Courseware Object Reference Model (SCORM).
**
** The purpose in wrapping the calls to the API is to (1) provide a 
** consistent means of finding the LMS API implementation within the window
** hierarchy and (2) to validate that the data being exchanged via the
** API conforms to the defined CMI data types.
** 
** This is just one possible example for implementing the API guidelines for
** runtime communication between an LMS and executable content components. 
** There are several other possible implementations.
**
** Usage: Executable course content can call the API Wrapper 
**		  functions as follows:
**
**		javascript:
**				var result = LMSInitialize();
**				if (result != true) {
**					//handle error
**				  }
**		
**		authorware
**				result := ReadURL("javascript:apiWrapper.LMSInitialize()", 100)
** 
******************************************************************************************/

var _Debug = false;  // set this to false to turn debugging off
// and get rid of those annoying alert boxes.

// Define exception/error codes
var _NoError = 0;
var _GeneralException = 101; 
var _InvalidArgumentError = 201;
var _NotInitialized = 301;
var _NotImplementedError = 401;


// local variable definitions
var apiHandle = null;



//variables with sco at the start means this is a local copy of a scorm varaible
//these will be got by LMSStart if an LMS is detected.
var scoStudentID;
var scoStudentName;
var scoEntryMode;
var scoCredit;
var scoLessonMode;
//status maybe overridden by LMS value
var scoStatus = "browsed";
var scoLaunchData;
var scoCommentsFromLms;
var scoTotalTime;
var scoPreviousSessionTime = 0;

// on resume the following are also avial
var scoLessonLocation;
//scoSuspendData is spilt into an array by our getFunction
//scoSuspendData[0] = completion status
//scoSuspendData[1 and above] = future expansion
var scoSuspendData = new Array();
var scoCommentsFromUser = new Array();;
var lmsCommentsFromUser = new Array();;

var student_preferences_children;
var core_children;



/******************************************************************************************
**
** Function: LMSInitialize()
** Inputs:	None
** Return:	CMIBoolean true if the initialization was successful, or
**			CMIBoolean false if the initialization failed.
**
** Description:
** Initialize communication with LMS by calling the LMSInitialize 
** function which will be implemented by the LMS, if the LMS is 
** compliant with the SCORM.
**
******************************************************************************************/
function LMSInitialize() 
{
   var api = getAPIHandle();
   if (api == null)
   {
      //confirm("The content was unable to communicate with a learning environment.\n \n It will now run in offline mode. You will still be able to view training material, however your progress will not be recorded.");
      return false;
   }

   // call the LMSInitialize function that should be implemented by the API
   var emptyString = new String("");

   var initResult = api.LMSInitialize(emptyString);

   if (initResult.toString() != "1")
   {
      // LMSInitialize did not complete successfully.

      // Note: An assumption is made that if LMSInitialize returns a non-true
      //		 value, then and only then, an error was raised.

      // Note: Each function could define its own error handler, but we'll 
      // just implement a generic one in this example.
      var err = ErrorHandler();
   }

   return initResult;
   
} 

/******************************************************************************************
**
** Function LMSFinish()
** Inputs:	None
** Return:	None
**
** Description:
** Close communication with LMS by calling the LMSFinish 
** function which will be implemented by the LMS, if the LMS is 
** compliant with the SCORM.
**
******************************************************************************************/
function LMSFinish()
{
   var api = getAPIHandle();
   if (api == null)
   {
      alert("Unable to locate the LMS's API Implementation.\nLMSFinish was not successful.");
   }
   else
   {
      // call the LMSInitialize function that should be implemented by the API
      var emptyString = new String("");
      api.LMSFinish(emptyString);
      var err = ErrorHandler();
   }   

   return;
   
} 

/******************************************************************************************
**
** Function LMSGetValue(name) 
** Inputs:	name - string representing the cmi data model defined category or 
**				   element (e.g. cmi.core.student_id)
** Return:	The value presently assigned by the LMS to the cmi data model 
**			element defined by the element or category identified by the name
**			input value.
**
** Description:	
** Wraps the call to the LMS LMSGetValue method
**
******************************************************************************************/
function LMSGetValue(name)
{
   var api = getAPIHandle();
   if (api == null)
   {
      alert("Unable to locate the LMS's API Implementation.\nLMSGetValue was not successful.");
      return null;
   }
   else
   {
      var value = api.LMSGetValue(name);
      var err = ErrorHandler();
      // if an error was encountered, then return null, 
      // else return the retrieved value
      if (err != _NoError)
      {
         return null;
      }
      else
      {
         return value.toString();
      }
   }   
}

/******************************************************************************************
**
** Function LMSSetValue(name, value) 
** Inputs:	name - string representing the cmi data model defined category or element
**			value - the value that the named element or category will be assigned
** Return:	None
**
** Description:
** Wraps the call to the LMS LMSSetValue method
**
******************************************************************************************/
function LMSSetValue(name, value) 
{
   var api = getAPIHandle();
   if (api == null)
   {
      alert("Unable to locate the LMS's API Implementation.\nLMSSetValue was not successful.");
   }
   else
   {
      sco_debug(5,'API.SetValue()' +name + ' ' + value);
	  api.LMSSetValue(name, value);
      var err = ErrorHandler();
   }   

   return;
}

/******************************************************************************************
**
** Function LMSCommit() 
** Inputs:	None
** Return:	None
**
** Description:
** Call the LMSCommit function which will be implemented by the LMS, 
** if the LMS is compliant with the SCORM.
**
******************************************************************************************/
function LMSCommit()
{
   var api = getAPIHandle();
   if (api == null)
   {
      alert("Unable to locate the LMS's API Implementation.\nLMSCommit was not successful.");
   }
   else
   {
      // call the LMSInitialize function that should be implemented by the API
      var emptyString = new String("");
      api.LMSCommit(emptyString);
      var err = ErrorHandler();
   }   

   return;
   
} 

/******************************************************************************************
**
** Function LMSGetLastError() 
** Inputs:	None
** Return:	The error code (integer format) that was set by the last LMS function call
**
** Description:
** Call the LMSGetLastError function which will be implemented by the LMS, 
** if the LMS is compliant with the SCORM.
**
******************************************************************************************/
function LMSGetLastError() 
{
   var api = getAPIHandle();
   if (api == null)
   {
      alert("Unable to locate the LMS's API Implementation.\nLMSGetLastError was not successful.");
      //since we can't get the error code from the LMS, return a general error
      return _GeneralError;
   }


   return api.LMSGetLastError().toString();
   
} 

/******************************************************************************************
**
** Function LMSGetErrorString(errorCode)
** Inputs:	errorCode - Error Code(integer format)
** Return:	The textual description that corresponds to the input error code 
**
** Description:
** Call the LMSGetErrorString function which will be implemented by the LMS, 
** if the LMS is compliant with the SCORM.
**
******************************************************************************************/
function LMSGetErrorString(errorCode) 
{
   var api = getAPIHandle();
   if (api == null)
   {
      alert("Unable to locate the LMS's API Implementation.\nLMSGetErrorString was not successful.");
   }

   return api.LMSGetErrorString(errorCode).toString();
   
} 

/******************************************************************************************
**
** Function LMSGetDiagnostic(errorCode) 
** Inputs:	errorCode - Error Code(integer format), or null
** Return:	The vendor specific textual description that corresponds to the input error code 
**
** Description:
** Call the LMSGetDiagnostic function which will be implemented by the LMS, 
** if the LMS is compliant with the SCORM.
**
******************************************************************************************/
function LMSGetDiagnostic(errorCode) 
{
   var api = getAPIHandle();
   if (api == null)
   {
      alert("Unable to locate the LMS's API Implementation.\nLMSGetDiagnostic was not successful.");
   }

   return api.LMSGetDiagnostic(errorCode).toString();
   
} 

/*******************************************************************************
**
** Function LMSIsInitialized() 
** Inputs:	none
** Return:	true if the LMS API is currently initialized, otherwise false 
**
** Description:
** Determines if the LMS API is currently initialized or not.
**
*******************************************************************************/
function LMSIsInitialized()
{
   // there is no direct method for determining if the LMS API is initialized
   // for example an LMSIsInitialized function defined on the API so we'll try
   // a simple LMSGetValue and trap for the LMS Not Initialized Error
   
   var api = getAPIHandle();
   if (api == null)
   {
      alert("Unable to locate the LMS's API Implementation.\nLMSIsInitialized() failed.");
      // no choice but to return false.
      return false;
   }
   else
   {
      var value = api.LMSGetValue("cmi.core.student_name");
      var errCode = api.LMSGetLastError().toString();
      if (errCode == _NotInitialized)
      {
         return false;
      }
      else
      {
         return true;
      }
   }   
}

/******************************************************************************************
** APIWrapper Private function implementations
** Note: This is javascript so there is no way to really prevent someone 
**	   from calling the other methods in this file, but they are really 
**	   intended to be private methods.  Only the methods above
**       are intended to be called directly by the learning 
**       content components.
******************************************************************************************/

/******************************************************************************************
**
** Function ErrorHandler()
** Inputs:	None
** Return:	The current value of the LMS Error Code
**
** Description:
** Determines if an error was encountered by the previous API call
** and if so, displays a message to the user.  If the error code
** has associated text it is displayed.  
**
** Side Effects: Displays an alert window with the appropriate error information
**
******************************************************************************************/
function ErrorHandler() 
{
   var api = getAPIHandle();
   if (api == null)
   {
      alert("Unable to locate the LMS's API Implementation.\nCannot determine LMS error code.");
      return;
   }

   // check for errors caused by or from the LMS
   var errCode = api.LMSGetLastError().toString();
   if (errCode != _NoError)
   {
      // an error was encountered so display the error description
      var errDescription = api.LMSGetErrorString(errCode);
      
      if (_Debug == true)
      {
         errDescription += "\n";
         errDescription += api.LMSGetDiagnostic(null);
         // by passing null to LMSGetDiagnostic, we get any available diagnostics
         // on the previous error.
      }
   }

   return errCode;
}

/******************************************************************************************
**
** Function getAPIHandle()
** Inputs:	None
** Return:	value contained by APIHandle
**
** Description:
** Returns the handle to API object if it was previously set, 
** otherwise it returns null
**
******************************************************************************************/
function getAPIHandle() 
{
   if (apiHandle == null)
   {
      apiHandle = getAPI();
   } 

   return apiHandle;
}


/******************************************************************************************
**
** Function findAPI(win)
** Inputs:	win - a Window Object
** Return:	If an API object is found, it is returned, otherwise null is returned.
**
** Description:
** This function looks for an object named API in the supported window hierarchy, 
** 
******************************************************************************************/
function findAPI(win) 
{

   // Search the window hierarchy for an object named "API"  
   // Look in the current window (win) and recursively look in any child frames
   

   //sco_debug(5,"win is: "+win.location.href);
   try{
		if (win.API != null)
		{
			sco_debug(5,"found api in this window");
			return win.API;
		}

	   if (win.length > 0)  // does the window have frames?
	   {
			sco_debug(5,"looking for api in windows frames");
			for (var i=0;i<win.length;i++)
			{
	        sco_debug(5,"looking for api in frames["+i+"]");
	        var theAPI = findAPI(win.frames[i]);
	        if (theAPI != null)
	        {
				return theAPI;
	        }
	      }
		}

		sco_debug(5,"didn't find api in this window (or its children)");
		return null;
		}	  
	catch(err)
	{
		return null;
	}

}


/******************************************************************************************
**
** Function getAPI()
** Inputs:	none
** Return:	If an API object is found, it is returned, otherwise null is returned.
**
** Description:
** This function looks for an object named API, first in the current window's hierarchy, 
**  and then, if necessary, in the current window's opener window hierarchy (if there is
**  an opener window).
******************************************************************************************/

function getAPI()
{

   // start at the topmost window - findAPI will recurse down through
   // all of the child frames
   var theAPI = findAPI(this.top);

   if (theAPI == null)
   {
      // the API wasn't found in the current window's hierarchy.  If the
      // current window has an opener (was launched by another window),
      // check the opener's window hierarchy. 
      if (_Debug)
      {
        sco_debug(5,'checking to see if this window has an opener');
        sco_debug(5,"window.opener typeof is> "+typeof(window.opener));
      }

      if (typeof(this.opener) != "undefined")
      {
		 sco_debug(5,'checking this windows opener');
         if (this.opener != null)
         {
			sco_debug(5,'this windows opener is NOT null - looking there');
            theAPI = findAPI(this.opener.top);
         }
         else
         {
            sco_debug(5,'this windows opener is null');
         }
      }
   }

   return theAPI;
}


/*
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
###################   6                                        #############################################################################################
*****************************   LMS Support functions ********************************************************************************************************************************************
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
*/

//Created by Simon Crozier - 26 April 2006
// Allows the script to quickly check if its running in an LMS
// Probably just return a value but more advanced check maybe require later
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function inLMS()
{	return hasLMS;
}

	

//Created by Simon Crozier - 24 March 2006
// Checks LMS start and initalise SCO details
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function scorm_start()
{
	if (LMSInitialize())
		{
		hasLMS = true;
		sco_debug(2,"start(): LMS connection started");
				
		getLMSSupported_core();
			getLMSStudentInfo();
			getLMSEntry();
			getLMSCredit();
			getLMSLessonMode();
			getLMSLessonStatus();
		
		getLMSLaunchData();
		
		getLMSCommentsFromLms();
		
		getLMSSupported_student_preferences();		
			getLMSSoundPref();
			getLMSTextPref();
			getLMSLanguagePref();
		
		if ((scoEntryMode == "resume") || (scoLessonMode == "review"))
			{
			sco_debug(7,"scorm_start(): entry mode was Resume");
			
			sco_debug(7,"scorm_start(): getting suspend data");
				getLMSSuspendData();
				sco_debug(7,"scorm_start(): restoring completion information");
					getLMSPageCompleltion();
					pageJump();
					getLMSErrorLog();
			
			sco_debug(7,"scorm_start(): getting user comments");
			getLMSCommentsFromUser();
			
			sco_debug(7,"scorm_start(): restoring page location");
			getLMSLessonLocation();
			currentPage = scoLessonLocation;
			
			getLMSTotalTime();
			lmsTotalTimeToMillis(scoTotalTime)
		
			sco_debug(7,"scorm_start(): restoring score information");
			//getLMSScore();
			
			}
		else
			{
			sco_debug(2,"Start(): entry mode was not resume, starting SCO afresh");
			setLMSSessionTime("0000:00:00.00");
			scoSuspendData[0] = "";
			currentPage = 0;
			setLMSLessonLocation(currentPage);
			}
			
		document.getElementById("userName").innerHTML = "<b>Student Name</b> "+scoStudentName; 
		document.getElementById("userID").innerHTML = "<b>Student ID</b>"+scoStudentID; 
		startSession();
		Commit();
		}
	else 
		{
		sco_debug(2,"start(): ******** NO LMS Connection ******");	
		hasLMS = false;
		
		currentPage = 0;
		if(demoDefault)
			{
			sco_debug(1,"This content is running outside of an LMS only demo pages will be available: ******** NO LMS Connection ******");	
			pages = demoPages;
			}
		}
}

function scorm_save()
{
	sco_debug(5,"scorm_save(): saving infom");
	//stop session, get current time, convert into correct format and store in cmi.core.session_time
	//see LMSTime Functions lower
	sco_debug(5,"scorm_save(): setting end time");
	finishSession();
	
	//set the location in LMS so user's can be brought back here on restart
	sco_debug(5,"scorm_save(): setting page location to - "+currentPage);
	setLMSLessonLocation(currentPage);
		
	sco_debug(5,"scorm_save(): decide lesson status");
	decideLessonStatus();
	
	sco_debug(5,"scorm_save(): setting lesson status");
	setLMSLessonStatus();
	
	sco_debug(5,"scorm_Finish(): setting exit mode");
	setLMSExitMode("suspend");
	
	sco_debug(5,"scorm_save(): setting lesson score");
	setLMSScore();
			
	sco_debug(5,"scorm_save(): adding SCO completion data to suspendData");
	if (scoCredit) buildCompletionData();
	
	sco_debug(5,"scorm_save(): storing suspend data");
	setLMSSuspendData();

	//check preferences have been synched with LMS
	setLMSCommentsFromUser();
	setLMSSoundPref();
	setLMSTextPref();
	setLMSLanguagePref();
	
	Commit();
}
	
//Created by Simon Crozier - 24 March 2006
// Checks LMS start and closes all SCO details
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function scorm_finish()
{ 
	if (hasLMS)
		{
		sco_debug(5,"scorm_Finish(): finishing");
		//stop session, get current time, convert into correct format and store in cmi.core.session_time
		//see LMSTime Functions lower
		sco_debug(5,"scorm_Finish(): setting end time");
		finishSession();
		
		//set the location in LMS so user's can be brought back here on restart
		sco_debug(5,"scorm_Finish(): setting page location to - "+currentPage);
		setLMSLessonLocation(currentPage);
		
		sco_debug(5,"scorm_Finish(): setting exit mode");
		setLMSExitMode("suspend");
		
		sco_debug(5,"scorm_Finish(): decide lesson status");
		decideLessonStatus();
		
		sco_debug(5,"scorm_Finish(): setting lesson status");
		setLMSLessonStatus();
		
		sco_debug(5,"scorm_Finish(): setting lesson score");
		setLMSScore();
				
		sco_debug(5,"scorm_Finish(): adding SCO completion data to suspendData");
		if (scoCredit) buildCompletionData();
		
		sco_debug(5,"scorm_Finish(): storing suspend data");
		setLMSSuspendData();

		sco_debug(5,"scorm_Finish(): storing Comments data");
		setLMSCommentsFromUser();
		//check preferences have been synched with LMS

		
		setLMSSoundPref();
		setLMSTextPref();
		setLMSLanguagePref();
		
		LMSFinish();
		hasLMS = false;
		}
}


//Created by Simon Crozier - 24 March 2006
//Trigger LMSCommit if still has a LMS <- to avoid on unload problems from LMS finish
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function Commit()
{
	if (hasLMS) LMSCommit();
}

/*
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
###################   7               ######################################################################################################
*****************************  LMS Gets *********************************************************************************************************************************************************
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
*/

function getLMSSupported_core()
{	core_children = LMSGetValue("cmi.core._children");
	sco_debug(5,"getLMSSupported_core(): = "+core_children);
	if (core_children  == null) core_children  = "";
}

function getLMSSupported_student_preferences()
{	student_preferences_children = LMSGetValue("cmi.student_preference._children");
	sco_debug(5,"getLMSSupportedStudentPref(): = "+student_preferences_children);
	if (student_preferences_children  == null) student_preferences_children  = "";
}


//cmi.core
//***********************
//Created by Simon Crozier - 24 March 2006
//interogates the LMS for the current students StudID, StudName
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function getLMSStudentInfo()
{	getLMSStudent_id();
	getLMSStudent_name();
}

//Created by Simon Crozier - 24 March 2006
//interogates the LMS for the current students StudID
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function getLMSStudent_id()
{	if (core_children.indexOf("student_id") >= 0) 
		{
		scoStudentID = LMSGetValue("cmi.core.student_id");
		sco_debug(5,"getLMSStudentInfo(): = "+scoStudentID);
		}
	else {
		sco_debug(1,"* warning * getLMSStudentInfo(): cmi.core.student_id not supported, this is a mandatory field");	
		reportError("101"," * warning * getLMSStudentInfo(): cmi.core.student_id not supported, this is a mandatory field");
		}
}
//Created by Simon Crozier - 24 March 2006
//interogates the LMS for the current students StudName
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit	
function getLMSStudent_name()
{	if (core_children.indexOf("student_name") >= 0) 
		{
		scoStudentName = LMSGetValue("cmi.core.student_name");
		sco_debug(5,"getLMSStudentInfo(): = "+scoStudentName);
		}
	else {
		sco_debug(1,"* warning * getLMSStudentInfo(): cmi.core.student_name not supported, this is a mandatory field");	
		reportError("102"," * warning * getLMSStudentInfo(): cmi.core.student_name not supported, this is a mandatory field");
		}
}


//Created by Simon Crozier - 24 March 2006
//interogates the LMS for the SCO's stored postion, if one found calls load page and places the user there
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function getLMSLessonLocation()
{	if (core_children.indexOf("lesson_location") >= 0) 
		{
		scoLessonLocation = LMSGetValue("cmi.core.lesson_location");
		scoLessonLocation = parseInt (scoLessonLocation);
		if (scoLessonLocation  > 0) sco_debug(5,"getLMSLessonLocation(): = "+scoLessonLocation);
		else 
			{
			sco_debug(5,"getLMSLessonLocation(): lesson location not a number setting location to 0");
			scoLessonLocation = 0;
			}
		}
	else {
		sco_debug(1,"* warning * getLMSLessonLocation(): cmi.core.lesson_location not supported, this is a mandatory field");	
		reportError("103"," * warning * getLMSLessonLocation(): cmi.core.lesson_location not supported, this is a mandatory field");
		}
	return scoLessonLocation;
}

//Created by Simon Crozier - 24 July 2006
//Enquires off the LMS to see if we are doing the module for credit or not. If its not for credit we don't actually act any differently at the min but it could be built into the system later.
//Possibly allowing the content to go into demo mode. - i would expect that to be done via lessonMode.
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function getLMSCredit()
{	if (core_children.indexOf("credit") >= 0) 
		{
		credit =  LMSGetValue("cmi.core.credit");
		sco_debug(5,"getLMSCredit(): credit "+credit);
		credit = credit.toLowerCase();
		if ((credit == "credit") || (credit =="no-credit"))
			{
			if (credit == "credit") scoCredit = true;
			else scoCredit = false;
			}
		else {
			sco_debug(1,"getLMSCredit(): *warning* credit must be either 'credit' or 'no-credit' not - "+credit);
			reportError("104"," *warning* credit must be either 'credit' or 'no-credit' not - "+credit);
			}
		}
	else {
		sco_debug(1,"* warning * getLMSCredit(): cmi.core.credit not supported, this is a mandatory field");	
		reportError("105"," * warning * getLMSCredit(): cmi.core.credit not supported, this is a mandatory field");
		}
	return scoCredit;	
}

//Created by Simon Crozier - 24 March 2006
//interogates the LMS for the SCO's current lesson status
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function getLMSLessonStatus()
{	if (core_children.indexOf("lesson_status") >= 0) 
		{
		scoStatus = LMSGetValue("cmi.core.lesson_status");
		sco_debug(5,"getLMSLessonStatus(): = "+scoStatus);	
		}
	else {
		sco_debug(1,"* warning * getLMSLessonStatus(): cmi.core.lesson_status not supported, this is a mandatory field");	
		reportError("106"," * warning * getLMSLessonStatus(): cmi.core.lesson_status not supported, this is a mandatory field");
		}
	return scoStatus;
}

//Created by Simon Crozier - 04 April 2006
//interogates the LMS to see whether SCO is in suspended or of if this is the "ab_initio" 
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function getLMSEntry()
{	if (core_children.indexOf("entry") >= 0) 
		{
		scoEntryMode = LMSGetValue("cmi.core.entry");
		scoEntryMode = scoEntryMode.toLowerCase();
		sco_debug(5,"getLMSEntryMode(): = "+scoEntryMode);
		}
	else {
		sco_debug(1,"* warning * getLMSEntryMode(): cmi.core.entry not supported, this is a mandatory field");
		reportError("107"," * warning * getLMSEntryMode(): cmi.core.entry not supported, this is a mandatory field");
		}
	return scoEntryMode;
}

//Created by Simon Crozier - 26 July 2006
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function getLMSTotalTime()
{	if (core_children.indexOf("total_time") >= 0) 
		{
		scoTotalTime = LMSGetValue("cmi.core.total_time");
		sco_debug(5,"getLMSTotalTime(): = "+scoTotalTime);
		}
	else {
		sco_debug(1,"* warning * getLMSTotalTime(): cmi.core.total_time not supported, this is a mandatory field");
		reportError("108"," * warning * getLMSTotalTime(): cmi.core.total_time not supported, this is a mandatory field");
		}
	return scoTotalTime;
}

//Created by Simon Crozier - 26 July 2006
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function getLMSLessonMode()
{	if (core_children.indexOf("lesson_mode") >= 0) 
		{
		LessonMode =  LMSGetValue("cmi.core.lesson_mode");
		LessonMode = LessonMode.toLowerCase();
		sco_debug(5,"getLMSLessonMode(): getLMSLessonMode "+LessonMode);
		
		//lesson mode not compulsory but if is set has to be a certain type.
		if ((LessonMode == "review") || (LessonMode =="normal") || (LessonMode =="browse") ||(LessonMode =="") ||(LessonMode == null)) scoLessonMode = LessonMode;
		else 
			{
			sco_debug(0,"getLMSLessonMode(): ***ERROR*** cmi.core.lesson_mode IF SET (not mandatory) must be either 'browse','review' or 'normal' not - "+LessonMode);
			reportError("117","getLMSLessonMode(): ***ERROR*** cmi.core.lesson_mode IF SET (not mandatory) must be either 'browse','review' or 'normal' not - "+LessonMode);
			}
		}
	else 
		{
		sco_debug(5,"getLMSLessonMode(): cmi.core.lesson_mode not supported BUT this is not a mandatory field");
		reportError("117","getLMSLessonMode(): *** warning *** cmi.core.lesson_mode (not mandatory) not supported");
		scoLessonMode = "normal";
		}
	return scoLessonMode;
}

// cmi.core.score
//***********************

//Created by Simon Crozier - 26 July 2006
//interogates the LMS for the SCO's last score,
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function getLMSScore()
{	scoScore = LMSGetValue("cmi.core.score.raw");
	sco_debug(5,"getLMSScore(): = "+scoScore);
}

//cmi.suspend_data
//***********************

//Created by Simon Crozier - 04 April 2006
//interogates the LMS to get the previously stored suspend data
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function getLMSSuspendData()
{ 	var x = LMSGetValue("cmi.suspend_data");
	scoSuspendData = x.split("|");
	sco_debug(5,"getLMSSuspendData(): = " +scoSuspendData[0]);
}

//Created by Simon Crozier - 24 July 2006
//Processess the suspend data to reuild the completion data.
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function getLMSPageCompleltion()
{	sco_debug(5,"getLMSPageCompleltion(): completion status is "+scoSuspendData[0]);
	if (scoSuspendData[0] !="")
		{
		//break completion string down into an array using \ as the break character
		completionArray = scoSuspendData[0].split("/");
		
		//add completion info from suspend data to the pages array - just keeps it bit neater than getting straight from suspend data everytime
		for (k = 0; k < completionArray.length; k++) pages[k].complete = completionArray[k];
		}
}

function getLMSErrorLog()
{	sco_debug(5,"getLMSErrorLog(): ErrorLog is "+scoSuspendData[1]);
	errorLog = errorLog + scoSuspendData[1];
}


//cmi.launch_data
//***********************

//Created by Simon Crozier - 04 April 2006
//interogates the LMS to get the launch data provided by the LMS
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function getLMSLaunchData()
{ 	scoLaunchData = LMSGetValue("cmi.launch_data");
	sco_debug(5,"getLMSLaunchData(): = "+scoLaunchData);
}

//cmi.comments
//***********************

//Created by Simon Crozier - 04 April 2006
//interogates the LMS to get the comments add by the LMS for this user
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function getLMSCommentsFromLms()
{	scoCommentsFromLMS = LMSGetValue("cmi.comments_from_lms");
	sco_debug(5,"getLMSCommentsFromLms(): = "+scoCommentsFromLMS);
}

//Created by Simon Crozier - 04 April 2006
//interogates the LMS to get the comments add by the LMS for this user
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function getLMSCommentsFromUser()
{	a = LMSGetValue("cmi.comments");
	lmsCommentsFromUser = a.split("|");
	sco_debug(5,"getLMSCommentsFromUser(): = "+lmsCommentsFromUser);
}

// cmi.STUDENT_PREFERNCES
//***********************

//Created by Simon Crozier - 27 March 2006
//interogates the LMS for the user's stored sound pref
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function getLMSSoundPref()
{	if (student_preferences_children.indexOf("audio") >= 0) 
		{
		LMSSound = LMSGetValue("cmi.student_preference.audio");
		sco_debug(5,"getLMSSoundPref(): audio= "+LMSSound );
		if (LMSSound == "-1")
			{
			sound = false;
			volumePercent = 0;
			}
		else if (LMSSound == "0") sco_debug(5,"getLMSSoundPref(): audio no change");
		else 
			{
			sound = true;	
			volumePercent = LMSSound;
			}
		}
	else {
		sco_debug(1,"* warning * getLMSSoundPref(): cmi.student_preference.audio is not supported BUT this is not a mandatory field");
		reportError("109","* warning * getLMSSoundPref(): cmi.student_preference.audio is not supported BUT this is not a mandatory field");		
		}
}

//Created by Simon Crozier - 27 March 2006
//interogates the LMS for the user's stored text pref
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function getLMSTextPref()
{	if (student_preferences_children.indexOf("text") >= 0) 
		{
		LMSText = LMSGetValue("cmi.student_preference.text");
		sco_debug(5,"getLMSTextPref(): text = "+LMSText);
		if (LMSText == "-1") text = false;
		else if (LMSText == "0") sco_debug(5,"getLMSTextPref(): text no change");
		else text = true;
		}
	else {
		sco_debug(1,"getLMSTextPref(): cmi.student_preference.text is not supported BUT this is not a mandatory field");
		reportError("110","* warning * getLMSSoundPref(): cmi.student_preference.audio is not supported BUT this is not a mandatory field");
		}
}

function getLMSLanguagePref()
{	if (student_preferences_children.indexOf("language") >= 0) 
		{
		LMSLanguage = LMSGetValue("cmi.student_preference.language");
		sco_debug(5,"getLMSLanguagePref(): language: = "+LMSLanguage);
		LMSLanguage = LMSLanguage.toLowerCase();
		if (LMSLanguage.indexOf("english" != "-1"))
			{
			if (pageDetails[0][0].indexOf("english") != "-1") 
				{
				sco_debug(0,"getLMSLanguagePref(): *** ERROR *** Your LMS has requested "+LMSLanguage+" but this content doesnt have that language available defaulting to English");
				reportError("112","*** ERROR *** Your LMS has requested "+LMSLanguage+" but this content doesnt have that language available defaulting to English");
				}
			else language = 1;
			}
		else if (LMSLanguage.indexOf("french" != "-1"))			
			{
			if (pageDetails[0][0].indexOf("french") != "-1") 
				{
				sco_debug(0,"getLMSLanguagePref(): *** ERROR *** Your LMS has requested "+LMSLanguage+" but this content doesnt have that language available defaulting to English");
				reportError("112","*** ERROR *** Your LMS has requested "+LMSLanguage+" but this content doesnt have that language available defaulting to English");
				}
			else language = 2;
			}
		else if (LMSLanguage.indexOf("german" != "-1"))
			{
			if (pageDetails[0][0].indexOf("german") != "-1")
				{
				sco_debug(0,"getLMSLanguagePref(): *** ERROR *** Your LMS has requested "+LMSLanguage+" but this content doesnt have that language available defaulting to English");
				reportError("112","*** ERROR *** Your LMS has requested "+LMSLanguage+" but this content doesnt have that language available defaulting to English");
				}
			else language = 3;
			}
		else if (LMSLanguage.indexOf("russian" != "-1"))
			{
			if (pageDetails[0][0].indexOf("russian") != "-1") 
				{
				sco_debug(0,"getLMSLanguagePref(): *** ERROR *** Your LMS has requested "+LMSLanguage+" but this content doesnt have that language available defaulting to English");
				reportError("112","*** ERROR *** Your LMS has requested "+LMSLanguage+" but this content doesnt have that language available defaulting to English");
				}
			else language = 4;
			}
		}
	else 
		{
		sco_debug(1,"getLMSTextPref(): cmi.student_preference.language is not supported BUT this is not a mandatory field");
		reportError("111","* warning * getLMSTextPref(): cmi.student_preference.language is not supported BUT this is not a mandatory field");
		}
}

/*
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
###################   8               ######################################################################################################
*****************************  LMS Sets *********************************************************************************************************************************************************
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
*/

//Created by Simon Crozier - 24 March 2006
//reports the exit mode to the LMS
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function setLMSExitMode(exit)
{	sco_debug(5,"setExitMode(): setting exit mode to "+exit);
	LMSSetValue("cmi.core.exit",exit);
}

//Created by Simon Crozier - 24 March 2006
//sets the SCO's lesson status in the LMS, from the stored value scoStatus
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function setLMSLessonStatus()
{	sco_debug(5,"setLessonStatus(): setting "+scoStatus);
	if (scoLessonMode.toLowerCase() != "review") LMSSetValue("cmi.core.lesson_status",scoStatus);
}

//Created by Simon Crozier - 24 March 2006
//set the lesson status to an explicit value
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function setLMSLessonStatusTo(status)
{	sco_debug(5,"setLessonStatusTo(): setting lesson status to "+status);
	if (scoLessonMode.toLowerCase() != "review") LMSSetValue("cmi.core.lesson_status",status);
}	
//Created by Simon Crozier - 24 March 2006
//set the SCO's location (bookmark) so that the user can be placed here next time they load
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function setLMSLessonLocation(page)
{	sco_debug(5,"setLMSLessonLocation(): Location - "+page);
	LMSSetValue("cmi.core.lesson_location",page);
}
//Created by Simon Crozier - 24 March 2006
//reports the user's score back to the LMS
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function setLMSScore()
{	if (scoScore != 0)
	{	sco_debug(5,"setLMSScore(): Score - "+scoScore);
		LMSSetValue("cmi.core.score.raw",scoScore);
	}
}

//Created by Simon Crozier - 27 March 2006
//sets the LMS's stored text pref for user
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function setLMSSoundPref()
{	sco_debug(5,"setLMSSoundPref(): setting cmi.student_preference.audio");
	if (sound == false)
		{
		LMSSetValue("cmi.student_preference.audio",-1);
		sco_debug(5,"setting cmi.student_preference.audio -1");
		}
	else {
		LMSSetValue("cmi.student_preference.audio",volumePercent);
		sco_debug(5,"setting cmi.student_preference.audio " + volumePercent);
		}
}

//Created by Simon Crozier - 27 March 2006
//sets the LMS's stored text pref for user
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function setLMSTextPref()
{	sco_debug(5,"setLMSTextPref(): setting cmi.student_preference.text");
	if (text == false) LMSSetValue("cmi.student_preference.text",-1);
	else LMSSetValue("cmi.student_preference.text",1);
}

//Created by Simon Crozier - 27 March 2006
//sets the LMS's stored language pref for user
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function setLMSLanguagePref()
{	sco_debug(5,"setLMSTextPref(): setting cmi.student_preference.language");
	LMSLanguage = LMSGetValue("cmi.student_preference.language");
	
	LMSLanguage = LMSLanguage.toLowerCase();
	if (language == 1) LMSLanguage ="english";
	if (language == 2) LMSLanguage ="french";
	if (language == 3) LMSLanguage ="german";
	if (language == 4) LMSLanguage ="russian";
	sco_debug(5,"setLMSLanguagePref(): setting language to:b "+LMSLanguage);
	LMSSetValue("cmi.student_preference.language",LMSLanguage);
}

//Created by Simon Crozier - 01 June 2006
//sets the users's comments back into the scorm information.
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function setLMSCommentsFromUser()
{	if (scoCommentsFromUser.length > 0)
		{
		a = "|" + scoCommentsFromUser.join("|");
		sco_debug(5,"setLMSCommentsFromUser(): comments - "+a);
		LMSSetValue("cmi.comments",a);
		for (k = 0; k < scoCommentsFromUser.length; k++) lmsCommentsFromUser.push(scoCommentsFromUser[k]);
		scoCommentsFromUser = new Array();
		}
}

//Created by Simon Crozier - 24 July 2006
//resets the compeltion data ready to be stored in the suspend data
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function buildCompletionData()
{	//initallise the completeion string to the first element - had to do this to cope with the break characters, subsequent elements just add the break character and their value
	var completionString = pages[0].complete;

	//build a completion string from the pages array
	for(k=1;k < pages.length; k++) completionString = completionString +("/"+pages[k].complete);
	
	//store the completion string in the suspenddata array ready to be concat'd into one string
	scoSuspendData[0] = completionString;
	sco_debug(5,"buildCompletionData(): scoSuspendData[0] ="+ scoSuspendData[0]);
	
}

//Created by Simon Crozier - 24 July 2006
//converts the suspend data array into one string and then stores that in that using LMSSet
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function setLMSSuspendData()
{	//initallise the suspend data string to the first element - had to do this to cope with the break characters, subsequent elements just add the break character and their value
	var suspendDataString = scoSuspendData[0];
	
	//take the suspend data array build it into a string using | to seperate the array elements - to allow it be rebuilt on relaunch
	for(k=1;k < scoSuspendData.length; k++) 
		{
		suspendDataString = suspendDataString +("|"+scoSuspendData[k]);
		sco_debug(5,"setLMSSuspendData(): suspendData ="+ suspendDataString);
		}
		
	suspendDataString = suspendDataString+"|"+errorLog;
	sco_debug(5,"setLMSSuspendData(): suspendData with errors ="+ suspendDataString);
	
	//store the suspend dada string into the LMS
	LMSSetValue("cmi.suspend_data",suspendDataString);	
}
/*
***************************************************************** LMS Time Functions ************************************************************
*/

/*
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
###################   9                                 ################################################################################################
*****************************  LMS Time functions ************************************************************************************************************************************************
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
*/
//Created by Simon Crozier - 24 March 2006
//records the elasped time for the session
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function setLMSSessionTime(a)
{	sco_debug(5,"setLMSSessionTime(): time - "+a);
	LMSSetValue("cmi.core.session_time",a);
}

//Created by Simon Crozier - 24 March 2006
//records the start time of the learner's session
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function startSession()
{	startSessionTime = new Date().getTime();
	sco_debug(5,"startSession(): setting learner's session start to - "+startSessionTime);
}

//Created by Simon Crozier - 24 March 2006
//records the finish time of the learner's session, calculates the session time and sends this back to the LMS
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function finishSession()
{	if (startSession != 0 )
		{
		var endSession = new Date().getTime();
		var elapsedSeconds = ( (endSession - startSessionTime) / 1000 );
		var formattedTime = convertTotalSeconds( elapsedSeconds );
		}
	else formattedTime = "00:00:00.0";
	
	sco_debug(5,"finishSession(): setting learner end time to - "+endSession);
	sco_debug(5,"finishSession(): session time - "+formattedTime);
	setLMSSessionTime(formattedTime);
}

function lmsTotalTimeToMillis(totalTime)
{	sco_debug(5,"lmsTotalTimeToMillis(): "+totalTime);
	var lTotalTime = 0.0; 
	var numArray; 
	var hourVal, minVal, secVal; 

	numArray = totalTime.split(":"); 
	hourVal = parseFloat(numArray[0]); 
	hourVal *= 3600; //3600 equals 60 times 60 (hours times minutes) 

	minVal = parseFloat(numArray[1]); 
	minVal *= 60; 

	secVal = parseFloat(numArray[2]); 
	scoPreviousSessionTime = hourVal + minVal + secVal; 
	scoPreviousSessionTime *= 1000;
	sco_debug(5,"lmsTotalTimeToMillis(): time in milli ="+scoPreviousSessionTime);
} 

//Created by Simon Crozier - 24 March 2006
//this function will convert seconds into hours, minutes, and seconds in CMITimespan type format - HHHH:MM:SS.SS (Hours has a max of 4 digits & Min of 2 digits
//AUDIT LOG - Edited by :name, On: date, Because: Reason for edit
function convertTotalSeconds(ts)
{	var sec = (ts % 60);
	ts -= sec;
	var tmp = (ts % 3600);  //# of seconds in the total # of minutes
	ts -= tmp;              //# of seconds in the total # of hours

	// convert seconds to conform to CMITimespan type (e.g. SS.00)
	sec = Math.round(sec*100)/100;
   
	var strSec = new String(sec);
	var strWholeSec = strSec;
	var strFractionSec = "";

	if (strSec.indexOf(".") != -1)
		{
		strWholeSec =  strSec.substring(0, strSec.indexOf("."));
		strFractionSec = strSec.substring(strSec.indexOf(".")+1, strSec.length);
		}
		
	if (strWholeSec.length < 2) strWholeSec = "0" + strWholeSec;
	
	strSec = strWholeSec;
    
	if (strFractionSec.length) strSec = strSec+ "." + strFractionSec;

	if ((ts % 3600) != 0 ) var hour = 0;
	else var hour = (ts / 3600);
	if ( (tmp % 60) != 0 ) var min = 0;
	else var min = (tmp / 60);

	if ((new String(hour)).length < 2) hour = "0"+hour;
	if ((new String(min)).length < 2) min = "0"+min;

	var rtnVal = hour+":"+min+":"+strSec;

	return rtnVal;
}


