

/*******************************************************************************
**
** FileName: APIWrapper.js
**
*******************************************************************************/

/*******************************************************************************
**
** 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 Sharable
** Content 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 = doLMSInitialize();
**          if (result != true) 
**          {
**             // handle error
**          }
**
**    authorware
**          result := ReadURL("javascript:doLMSInitialize()", 100)
**
*******************************************************************************/
/*
Copyright © Interactive Services 2013 - 2015 All Rights Reserved.  
No part of this code may be used, with or without modification, without prior consent from Interactive Services. 
Version 2.1, 01 March 2015
*/

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 _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(){
	logText("doLMSInitialize() has been invoked", "info")
	var api = getAPIHandle();
	if (api == null){
		if(COMMUNICATION_ERRORMESSAGE){generateErrorAlert(apierror)}
		return "false";
	}
	
	var result = api.LMSInitialize("");
	if (result.toString() != "true"){
		var errorCode = getErrorCode();
		var errorDesc = getErrorDesc(errorCode);
		if(COMMUNICATION_ERRORMESSAGE){generateErrorAlert(errorMessage1, errorCode, errorDesc)}
		logText("doLMSInitialize() failed. Error Code: "+errorCode, "failure")
	}else{
		logText("doLMSInitialize() finished successfully", "success")
	}
	
	return result.toString();
}

/*******************************************************************************
**
** 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(){
	logText("<br/>doLMSFinish() has been invoked", "info")
	var api = getAPIHandle();
	if (api == null){
		if(COMMUNICATION_ERRORMESSAGE){generateErrorAlert(apierror)}
		return "false";
	}
   
	// call the LMSFinish function that should be implemented by the API
	var result = api.LMSFinish("");
	if (result.toString() != "true"){
		var errorCode = getErrorCode();
		var errorDesc = getErrorDesc(errorCode);
		if(COMMUNICATION_ERRORMESSAGE){generateErrorAlert(errorMessage3, errorCode, errorDesc)}
		logText("doLMSFinish() failed. Error Code: "+errorCode, "failure")
	}else{
		logText("doLMSFinish() finished successfully", "success")
	}
  return result.toString();
}

/*******************************************************************************
**
** 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){
	logText("<br/>doLMSGetValue("+name+") has been invoked", "info")
	var api = getAPIHandle();
	if (api == null){
		if(COMMUNICATION_ERRORMESSAGE){generateErrorAlert(apierror)}
		return "false";
	}
	var value = api.LMSGetValue(name);
	
	var errorCode = getErrorCode();
	if(errorCode != _NoError){
		var errorDesc = getErrorDesc(errorCode);
		var txt = errorMessage4.replace('[%%get%%]',name);
		if(COMMUNICATION_ERRORMESSAGE){generateErrorAlert(txt, errorCode, errorDesc)}
		logText("doLMSGetValue() failed. Error Code: "+errorCode, "failure")
	}else{
		logText("Value returned from LMS: ["+value+"]", "info");
		logText("doLMSGetValue() finished successfully", "success");
	}
	
	return value.toString();
}

/*******************************************************************************
**
** 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){	
	logText("<br/>doLMSSetValue("+name+","+value+") has been invoked", "info")
	var api = getAPIHandle();
	if (api == null){
		if(COMMUNICATION_ERRORMESSAGE){generateErrorAlert(apierror)}
		return "false";
	}
	
	var result = api.LMSSetValue(name, value);
	
	var errorCode = getErrorCode();
	if(errorCode != _NoError){
		var errorDesc = getErrorDesc(errorCode);
		var txt = errorMessage5.replace('[%%set%%]',name);
		txt = txt.replace('[%%setvalue%%]',value);
		if(COMMUNICATION_ERRORMESSAGE){generateErrorAlert(txt, errorCode, errorDesc)}
		logText("doLMSSetValue() failed. Error Code: "+errorCode, "failure")
	}else{
		logText("doLMSSetValue() finished successfully", "success");
	}
	
  return result;
}

/*******************************************************************************
**
** Function doLMSCommit()
** Inputs:  None
** Return:  None
**
** Description:
** Call the LMSCommit function 
**
*******************************************************************************/
function doLMSCommit(){
	logText("<br/>doLMSCommit() has been invoked", "info")
	var api = getAPIHandle();
	if (api == null){
		if(COMMUNICATION_ERRORMESSAGE){generateErrorAlert(apierror)}
		return "false";
	}
	
  var result = api.LMSCommit("");
	var errorCode = getErrorCode();
	if(errorCode != _NoError){
		var errorDesc = getErrorDesc(errorCode);
		if(COMMUNICATION_ERRORMESSAGE){generateErrorAlert(errorMessage6, errorCode, errorDesc)}
		logText("doLMSCommit() failed. Error Code: "+errorCode, "failure")
	}else{
		logText("doLMSCommit() finished successfully", "success");
	}
	
	return result.toString();
}

/*******************************************************************************
**
** Function doLMSGetLastError()
** Inputs:  None
** Return:  The error code that was set by the last LMS function call
**
** Description:
** Call the LMSGetLastError function 
**
*******************************************************************************/
function doLMSGetLastError(){
	logText("<br/>doLMSGetLastError() has been invoked", "info");
   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 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){
	logText("<br/>doLMSGetErrorString("+errorCode+") has been invoked", "info");
   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 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){
	logText("<br/>doLMSGetDiagnostic("+errorCode+") has been invoked", "info");
   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(){
	logText("<br/>LMSIsInitialized() has been invoked", "info");
   // 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.");
      return false;
   }
   else
   {
      var value = api.LMSGetValue("cmi.core.student_name");
      var errCode = api.LMSGetLastError().toString();
      if (errCode == _NotInitialized)
      {
         return false;
      }
      else
      {
         return true;
      }
   }
}

/*******************************************************************************
**
** 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 getErrorCode(){
	var api = getAPIHandle();
	if (api == null){
		if(COMMUNICATION_ERRORMESSAGE){generateErrorAlert(apierror)}
		return;
	}
	
	// check for errors caused by or from the LMS
	var errCode = api.LMSGetLastError().toString();

	return errCode;
}

function getErrorDesc(code){
	var api = getAPIHandle();
	if (api == null){
		if(COMMUNICATION_ERRORMESSAGE){generateErrorAlert(apierror)}
		return;
	}
	
	var errDescription = api.LMSGetErrorString(code);
	if(code != _NoError){
		var txt = api.LMSGetDiagnostic(code);
		if(txt.indexOf("No details available for Error") == -1){
			errDescription +="<br/>"+txt;
		}
	}
	
	return errDescription;
}



/******************************************************************************
**
** 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()
{
	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
**
*******************************************************************************/

/*++++++++++++++++++++++++++++++++++++  findAPI() +++++++++++++++++++++++++++++++++++++
UPDATED: 		14th September 2010
BY: 				John Enright
COMMENTS:		There was a new problem introduced by updates to internet explorer that
						warranted a change in the programming. This update was adapted from:
						http://www.ostyn.com/standards/scorm/samples/api_discovery_ff_issue.htm
UPDATED: 		04th October 2012
						Updated to find a SCORM 2004 API.
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
function findAPI(win){
  var nFindAPITries = 500; // paranoid to prevent runaway
  var objAPI = null;
  var bOK = true;
  var wndParent = null;
  while ((!objAPI)&&(bOK)&&(nFindAPITries>0))
  {
    nFindAPITries--;
    var targetApi = (delivery.toLowerCase() == "scorm") ? win.API : win.API_1484_11
    try { objAPI = targetApi; } catch (e) { bOK = false; }
    if ((!objAPI)&&(bOK))
    {
      try { wndParent = win.parent; } catch (e) { bOK = false; }
      if ((!bOK)||(!wndParent)||(wndParent==win))
      {
        break;
      }
      win = wndParent;
    }
  }
  return objAPI;
}

function getFrame()
{
	var frm = window.parent.frames;
	var returnValue;
	for (m=0; m < frm.length; m++) 
	{
	    if (frm(m).API)
	    {
			returnValue = frm(m).API;
		}
	}
	return returnValue;
}

/*******************************************************************************
**
** 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).
**
*******************************************************************************/

/*++++++++++++++++++++++++++++++++++++  getAPI() +++++++++++++++++++++++++++++++++++++
UPDATED: 		14th September 2010
BY: 				John Enright
COMMENTS:		There was a new problem introduced by updates to internet explorer that
						warranted a change in the programming. This update was adapted from:
						http://www.ostyn.com/standards/scorm/samples/api_discovery_ff_issue.htm
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

function getAPI(){
	
		var theAPI = null
		var wndParent = null;
		var wndOpener = null;
		var wndOpener2 = null;
		var wndSelf = null;
		try { wndSelf = window.self; } catch(e) { }
		try { wndParent = window.parent; } catch(e) { }
		try { wndOpener = window.opener; } catch(e) { }
		try { wndOpener2 = window.opener.opener; } catch(e) { }
		
		
		if (wndSelf != null){
				theAPI = findAPI(wndSelf);
		}
		if ((theAPI == null) && (wndParent != null) && (wndParent != window)){
				theAPI = findAPI(wndParent);
		}
		if ((theAPI == null) && (wndOpener != null)){
				theAPI = findAPI(wndOpener);
		}
		if ((theAPI == null) && (wndOpener2 != null)){
				theAPI = findAPI(wndOpener2);
		}
		
		return theAPI
   
}


