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

// local variable definitions used for finding the API
var apiHandle = null;
var findAPITries = 0;
var noAPIFound = "false";

// local variable used to keep from calling Terminate() more than once
var terminated = "false";

// local variable used by the content developer to debug
// This should be set to true during development to find errors.  However,
// This should be set to false prior to deployment.
var _debug = false;


/******************************************************************************************
**
** 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)
   {
      alert("Unable to locate the LMS's API Implementation.\nLMSInitialize was not successful.");
      return false;
   }

   // call the LMSInitialize function that should be implemented by the API
  // var emptyString = new String("");

   var initResult = api.LMSInitialize("");
	//alert("initResult "+initResult)
   if (initResult.toString() != "true")
   {
      // 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 terminateCommunication()
{
   var api = getAPIHandle();

   if ( api == null )
   {
      return "false";
   }
   else
   {
      // call Terminate only if it was not previously called
      if ( terminated != "true" )
      {
         // call the Terminate function that should be implemented by
         // the API
         var result = api.Terminate("");

         if ( result != "true" )
         {
            var errCode = retrieveLastErrorCode();

            displayErrorInfo( errCode );

            // may want to do some error handling
         }
         else  // terminate was successful
         {
            terminated = "true";
         }
      }
   }

   return result;
}

/******************************************************************************************
**
** 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 )
{
   // do not call a set after finish was called
   if ( terminated != "true" )
   {
      var api = getAPIHandle();

      if ( api == null )
      {
         return "";
      }
      else
      {
         var value = api.GetValue( name );

         var errCode = api.GetLastError();
		if ( errCode != "0" )
         {
            var errCode = retrieveLastErrorCode();

            displayErrorInfo( errCode );
         }
         else
         {
            return value;
         }
      }
   }

   return;
}

/******************************************************************************************
**
** 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 )
{
   // do not call a set after finish was called
   if ( terminated != "true" )
   {
      var api = getAPIHandle();

      if ( api == null )
      {
         return;
      }
      else
      {
         var result = api.SetValue( name, value );

         if ( result != "true" )
         {
            var errCode = retrieveLastErrorCode();

            displayErrorInfo( errCode );

            // may want to do some error handling
         }
      }
   }

   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 persistData()
{
   // do not call a set after Terminate() was called
   if ( terminated != "true" )
   {
      var api = getAPIHandle();

      if ( api == null )
      {
         return "";
      }
      else
      {
         return api.Commit("");
      }
   }
   else
   {
      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 retrieveLastErrorCode()
{
   // It is permitted to call GetLastError() after Terminate()

   var api = getAPIHandle();

   if ( api == null )
   {
      return "";
   }
   else
   {
      return api.GetLastError();
   }
}

/******************************************************************************************
**
** 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 retrieveErrorInfo( errCode )
{
   // It is permitted to call GetLastError() after Terminate()

   var api = getAPIHandle();

   if ( api == null )
   {
      return "";
   }
   else
   {

      return api.GetErrorString( errCode );
   }
}

/******************************************************************************************
**
** 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 retrieveDiagnosticInfo( error )
{
   // It is permitted to call GetLastError() after Terminate()

   var api = getAPIHandle();

   if ( api == null )
   {
      return "";
   }
   else
   {
      return api.GetDiagnostic( error );
   }
}

/*******************************************************************************
**
** 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 initializeCommunication()
{
   var api = getAPIHandle();
  
	if ( api == null )
   {
      return "false";
   }
   else
   { 
	 //alert("api "+api.Initialize)
      var result = api.Initialize("");
	 
      if ( result != "true" )
      {
         var errCode = retrieveLastErrorCode();

         displayErrorInfo( errCode );

         // may want to do some error handling
      }
   }

   return result;
}

/*******************************************************************************
**
** Display the last error code, error description and diagnostic information.
**
** Inputs:  String - The error code
**
** Return:  None
**
*******************************************************************************/
function displayErrorInfo( errCode )
{
   if ( _debug )
   {
      var errString = retrieveErrorInfo( errCode );
      var errDiagnostic = retrieveDiagnosticInfo( errCode );
	
      alert( "ERROR: " + errCode + " - " + errString + "\n" +
             "DIAGNOSTIC: " + errDiagnostic );
   }
}


/******************************************************************************************
**
** 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)
{
   while ((win.API_1484_11 == null) && (win.parent != null) && (win.parent != win))
   {
      findAPITries++;
      // Note: 7 is an arbitrary number, but should be more than sufficient
      if (findAPITries > 7) 
      {
         alert("Error finding API -- too deeply nested.");
         return null;
      }
      win = win.parent;
}
   return win.API_1484_11;
}


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 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(){	
  var myAPI = null;
  var tries = 0, triesMax = 10;  
  while (tries < triesMax && myAPI == null){
    myAPI = findAPI(window);
    if (myAPI == null && window.parent != null) {
    	//alert("searching window.parent")
    	myAPI = findAPI(window.parent)
    }
    
    if (myAPI == null && window.top != null){
    //alert("searching window.top")
    myAPI = findAPI(window.top);
    }
    
    if (myAPI == null && window.opener != null){
        if (window.opener != null && !window.opener.closed) {
       // alert("searching window.opener")
    	  myAPI = findAPI(window.opener);      
    	}
    }

    if (myAPI == null && window.opener != null){
     	if (myAPI == null && window.opener.parent != null) {
    		//alert("searching window.opener.parent")
    	    	myAPI = findAPI(window.opener.parent)
    	    }
	   
	   
	   if (myAPI == null && window.opener.top != null){
		if (window.opener.top != null && !window.opener.top.closed){
			// alert("searching window.opener.top")
			myAPI = findAPI(window.opener.top);
		}
		
		if (myAPI == null && window.opener.top.opener != null && !window.opener.top.opener.closed){
			 //alert("searching window.opener.top.opener")
			 myAPI = findAPI(window.opener.top.opener);			
			
			if (myAPI == null && window.opener.top.opener.top != null && !window.opener.top.opener.top.closed){
				// alert("searching window.opener.top.opener.top")
				myAPI = findAPI(window.opener.top.opener.top);
			}			
		}	
	    }
    }
    tries++;
  }
  
  if (myAPI == null){    
    //alert('JavaScript Warning: API object not found in window or opener. (' + tries + ')');
  }
  else{
    //bvSCORM_API = myAPI;
     //alert('api found');
  }
  return myAPI;
}


