

/*******************************************************************************
**
** FileName: APIWrapper.js (SCORM 1.2 version)
**
*******************************************************************************/

/*******************************************************************************
**
** 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.
**
*******************************************************************************/

	var _Debug = true;  // set this to false to turn debugging off

	// Define exception/error codes
	var _NoError 			= 0;
	var _GeneralException 		= 101;
	var _ServerBusy 		= 102;
	var _InvalidArgumentError 	= 201;
	var _ElementCannotHaveChildren 	= 202;
	var _ElementIsNotAnArray 	= 203;
	var _NotInitialized 			= 301;
	var _NotImplementedError 	= 401;
	var _InvalidSetValue 			= 402;
	var _ElementIsReadOnly 		= 403;
	var _ElementIsWriteOnly 	= 404;
	var _IncorrectDataType 		= 405;

	// local variable definitions
	var apiHandle 		= null;
	var API 		= null;
	var findAPITries 	= 0;

/*******************************************************************************
**
** Function: doLMSInitialize()
** 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.
**
*******************************************************************************/
function doLMSInitialize()
{
	var api = getAPIHandle();
	displayDebugText("doLMSInitialize called", 0);
	displayDebugText("Function path: ", 1, false, true);

	// if lms found
	if (api != null)
	{
		// check if already found
		if( LMSIsInitialized() == true ){
			return "true";
		}
		// otherwise look for and initialize
		result = api.LMSInitialize("");
		if (result.toString() != "true")
		{
			var err = ErrorHandler();
			displayDebugText("err = " + doLMSGetErrorString(err), 1, false, false);
		}
		else
		{
			return result.toString();
		}
	}
	return "false";
}

/*******************************************************************************
**
** Function doLMSFinish()
** Inputs:  None
** Return:  CMIBoolean true if successful
**          CMIBoolean false if failed.
**
** Description:
** Close communication with LMS by calling the LMSFinish
** function which will be implemented by the LMS
**
*******************************************************************************/
function doLMSFinish()
{
	var api = getAPIHandle();
	displayDebugText("doLMSFinish called", 0);
	displayDebugText("Function path: ", 1, false, false);

	if (api != null)
	{
		// call the LMSFinish function that should be implemented by the API
		var result = api.LMSFinish("");
		displayDebugText("doLMSFinish() api: ", 1, false, false);
		if (result.toString() != "true")
		{
			var err = ErrorHandler();
		}
		return result.toString();
	}
	return "false";
}

/*******************************************************************************
**
** Function doLMSGetValue(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 doLMSGetValue(name)
{
	displayDebugText("doLMSGetValue(name: " + name + ")", 1, false, false);
	var api = getAPIHandle();
	if (api != null)
	{
		var value = api.LMSGetValue(name);
		var errCode = api.LMSGetLastError().toString();
		if (errCode != _NoError)
		{
			// an error was encountered so display the error description
			var errDescription = api.LMSGetErrorString(errCode);
			displayDebugText("doLMSGetValue('name: "+name+"') failed: \n"+ errDescription, 3, false, false);
			return "";
		}
		else
		{
			displayDebugText("doLMSGetValue[name: " + name + "] = " + String(value), 1, false, false);
			if( value == null )
			{
				return "";
			}
			else
			{
				return String(value);
			}
		}
	}
	return "";
}

/*******************************************************************************
**
** Function doLMSSetValue(name, value)
** Inputs:  name -string representing the data model defined category or element
**          value -the value that the named element or category will be assigned
** Return:  CMIBoolean true if successful
**          CMIBoolean false if failed.
**
** Description:
** Wraps the call to the LMS LMSSetValue function
**
*******************************************************************************/
function doLMSSetValue(name, value)
{
	displayDebugText("doLMSSetValue(name: " + name + ", value: " + value + ")", 1, false, true);
	var api = getAPIHandle();
	if (api != null)
	{

		var result = api.LMSSetValue(name, value);
		if (String(result) != "true")
		{
			var err = ErrorHandler();
			var errDescription = api.LMSGetErrorString(err);
			displayDebugText("doLMSSetValue('name: "+name+"','value: "+value+"') failed: \n"+ errDescription, 3, api, true);
		}
	}
	return;
}

/*******************************************************************************
**
** Function doLMSCommit()
** Inputs:  None
** Return:  None
**
** Description:
** Call the LMSCommit function
**
*******************************************************************************/
function doLMSCommit()
{
	var api = getAPIHandle();
	displayDebugText("doLMSCommit called", 0);
	displayDebugText("Function path: ", 1, false, false);

	if (api != null)
	{
		var result = api.LMSCommit("");
		if (result != "true")
		{
			var err = ErrorHandler();
		}
	}
	return String(result);
}

/*******************************************************************************
**
** Function doLMSGetLastError()
** Inputs:  None
** Return:  The error code that was set by the last LMS function call
**
** Description:
** Call the LMSGetLastError function
**
*******************************************************************************/
function doLMSGetLastError()
{
	displayDebugText("doLMSGetLastError()", 1, false, false);
	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
		displayDebugText("doLMSGetLastError(_GeneralError: " + _GeneralError + ") failed.", 3, false, true);
		return _GeneralError;
	}
	return String(api.LMSGetLastError());
}

/*******************************************************************************
**
** Function doLMSGetErrorString(errorCode)
** Inputs:  errorCode - Error Code
** Return:  The textual description that corresponds to the input error code
**
** Description:
** Call the LMSGetErrorString function
**
********************************************************************************/
function doLMSGetErrorString(errorCode)
{
	displayDebugText("doLMSGetErrorString(errorCode: " + errorCode + ")", 1, false, false);
	var api = getAPIHandle();
	if (api == null)
	{
		// alert("Unable to locate the LMS's API Implementation.\nLMSGetErrorString was not successful.");
		displayDebugText("doLMSGetErrorString(errorCode: " + errorCode + ") failed.", 3, false, true);
	}
	return String(api.LMSGetErrorString(errorCode));
}

/*******************************************************************************
**
** Function doLMSGetDiagnostic(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
**
*******************************************************************************/
function doLMSGetDiagnostic(errorCode)
{
	displayDebugText("doLMSGetDiagnostic(" + errorCode + ") failed.", 3, false, true);
	var api = getAPIHandle();
	if (api == null)
	{
		//alert("Unable to locate the LMS's API Implementation.\nLMSGetDiagnostic was not successful.");
		displayDebugText("doLMSGetDiagnostic(" + errorCode + ") failed.", 3, false, true);
	}
	return String(api.LMSGetDiagnostic(errorCode));
}

/*******************************************************************************
**
** 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()
{
	displayDebugText("LMSIsInitialized()", 1, false, false);
	// 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)
	//{
		//var value = api.LMSGetValue("cmi.core.student_name");
		//var errCode = String(api.LMSGetLastError());
		//if ((errCode == _NotInitialized) || ( !value ))
		//{
		//	return false;
		//}
		//else
		//{
		//	return true;
		//}
	//}
	//alert("Unable to locate the LMS's API Implementation.\nLMSIsInitialized() failed.");
	return false;
}

/*******************************************************************************
**
** 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 also displayed.
**
*******************************************************************************/
function ErrorHandler()
{
	displayDebugText("ErrorHandler()", 1, false, false);
	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.
		}
		//alert(errDescription);
		displayDebugText("ErrorHandler(), errDescription: " + errDescription, 3, false, true);
	}
	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()
{
	displayDebugText("getAPIHandle()", 1, false, false);
	if (apiHandle == null)
	{
		apiHandle = getAPI();
	}
	return apiHandle;
}


/*******************************************************************************
**
** Function findAPI(win)
** Inputs:  win - a Window Object
** Return:  If an API object is found, it's returned, otherwise null is returned
**
** Description:
** This function looks for an object named API in parent and opener windows
**
*******************************************************************************/
function findAPI( thisWin )
{
	// -- Search the window hierarchy for an object named "API"
	// -- Look in the current window (win) and recursively look in any child frames

	// first check to see if this window has API in it
	displayDebugText("1 findAPI(thisWin: " + thisWin.location.href + ")", 1, false, true);

	if (thisWin.API)
	{
		displayDebugText("2 found api in this window, thisWin.API: " + thisWin.API + ", thisWin.location: " + thisWin.location + ", thisWin.API.LMSCommit: " + thisWin.API.LMSCommit, 2, thisWin.API, true);

		return thisWin.API;
	}
	// check if the current screen has a parent
	if ( thisWin.parent )
	{
		// check for API
		displayDebugText("3 parentWindow is: "+ thisWin.parent.location.href, 1, false, true);

		if (thisWin.parent.API)
		{
			displayDebugText("4 found api in this parent", 2, false, true);

			return thisWin.parent.API;
		}
		// loop through the parent frames
		if (thisWin.parent.frames.length > 0)  // does the window have frames?
		{
			displayDebugText("5 looking for api in windows frames", 1, false, true);

			for (var i=0; i < thisWin.parent.frames.length; i++)
			{
				displayDebugText("6 looking for api in frames["+i+"]", 1, false, true);

				if (thisWin.parent.frames[i].API)
				{
					displayDebugText("7 found api in this parent.frames["+i+"]", 2, false, true);

					return thisWin.parent.frames[i].API;
				}
			}
		}
		// look to see if the window.parent.opener contains the api
		if ( thisWin.parent.opener )
		{
			displayDebugText("8 checking this windows opener", 1, false, true);

			if (thisWin.parent.opener != null)
			{
				theAPI = findAPI(thisWin.parent.opener);
			}
			if( theAPI != null )
			{
				return theAPI;
			}
		}
	}

	// 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 ( thisWin.opener )
	{
		displayDebugText("9 checking this windows opener", 1, false, true);

		if (thisWin.opener != null)
		{
			theAPI = findAPI(thisWin.opener);
		}
		if( theAPI != null )
		{
			return theAPI;
		}
	}
	// if api no found, return null

	displayDebugText("10 didn't find api in this window (or its children)", 2, false, true);

	return null;
} // end of findAPI()


/*******************************************************************************
**
** Function getAPI()
** Inputs:  none
** Return:  If an API object is found, it's returned, otherwise null is returned
**
** Description:
** This function looks for an object named API, first in the current window's
** frame hierarchy and then, if necessary, in the current window's opener window
** hierarchy (if there is an opener window).
**
*******************************************************************************/
function getAPI()
{
	displayDebugText("in getAPI()", 1, false, true);

	// call find api function
	var theAPI = findAPI( this );
	// if api found
	return theAPI;
}

/*******************************************************************************
**
** Function function displayDebugText(inText)
** Inputs:  text string
** Return: display value in the required form
**
** Description:
** This function output the debug information to the screen
**
*******************************************************************************/
function displayDebugText(inText, priority, whatObj, showCallee)
{
	if (_Debug)
	{
		if (DEBUG)
			DEBUG.lert(inText, priority, whatObj, showCallee, "api_wrapper.js")
	}
}
