/*******************************************************************************
**
** 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.
**
*******************************************************************************/


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;
var hasFinished = false;



function LMSInitialize()
{
   hasFinished = false;
   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 initResult = api.LMSInitialize("");

   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()
{
   var api = getAPIHandle();
   if (api == null)
   {
      //alert("Unable to locate the LMS's API Implementation.\nLMSFinish was not successful.");
   }
   else
   {
      // call the LMSFinish function that should be implemented by the API
      if( hasFinished == false )
      {
      	//var emptyString = new String("");
      	api.LMSFinish("");
      	hasFinished = true;
		}
	
      var err = ErrorHandler();
   }

   return;

}

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
   {
		if( hasFinished == false )
			{
      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)
{
   var api = getAPIHandle();
   if (api == null)
   {
      //alert("Unable to locate the LMS's API Implementation.\nLMSSetValue was not successful.");
   }
   else
   {
	if( hasFinished == false )
	{
      api.LMSSetValue(name, value);
      var err = ErrorHandler();
   }
	}

   return;
}

function LMSCommit()
{
   var api = getAPIHandle();
   if (api == null)
   {
      //alert("Unable to locate the LMS's API Implementation.\nLMSCommit was not successful.");
   }
   else
   {
      // call the LMSCommit function that should be implemented by the API
      var emptyString = new String("");
      api.LMSCommit(emptyString);
      var err = ErrorHandler();
   }

   return;

}

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)
{
   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)
{
   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()
{
   // 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;
      }
   }
}


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.
      }

      //alert(errDescription);
   }

   return errCode;
}

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
	if (_Debug) 
	{
		alert("win is: "+win.location.href);
	}

	if (win.API != null) 
	{
		if (_Debug) 
		{
			alert("found api in this window");
		}
		return win.API;
	}

	// does the window have frames?
	if (win.length > 0) 
	{
		if (_Debug) 
		{
			alert("looking for api in windows frames");
		}
		for (var i=0;i<win.length;i++) 
		{
			if (_Debug) 
			{
				alert("looking for api in frames["+i+"]");
			}
			var theAPI = findAPI(win.frames[i]);
			if (theAPI != null) {
				return theAPI;
			}
		}
	}

	if (_Debug) 
	{
		alert("didn't find api in this window (or its children)");
	}
	return null;
	
}

/**
 *  The following variables are used in findProxyAPI function
 *     proxyVisitedLoc              => href locations already visited to find proxyAPI
 *     proxyRecursionLimit      => How many iterations findProxyAPI can use to try and find proxyAPI
 *     proxyRecursionCount    => Current interation count
 */
var proxyVisitedLoc = new Array();
var proxyRecursionLimit = 10;
var proxyRecursionCount = 0;

/**
 * initProxySearch
 * Responsible for resetting values used when running the findProxyAPI function
 */
function initProxySearch(){
    proxyVisitedLoc = new Array();
    proxyRecursionCount = 0;
}

/**
 * isInProxyVisitedLoc
 * Responsible for looking in proxyVisitedLoc array for winLoc param value
 * Returns true or false
 */
function isInProxyVisitedLoc( winLoc ){
    for( var w = 0; w < proxyVisitedLoc.length; w++ ){
	    if( proxyVisitedLoc[ w ] == winLoc ){
	        return true;
	    }
	}
	
	return false;
}

/******************************************************************************************
**
** Function findProxyAPI( win, topDownSearch )
** Inputs:  win - a Window Object
**                topDownSearch - a boolean value
** Return:  If an API object is found, it is returned, otherwise null is returned.
**
** Description:
** This function looks for an object named proxyAPI in the supported window hierarchy
**
******************************************************************************************/
function findProxyAPI( win, topDownSearch ){
    proxyRecursionCount++;
    
    if( null == topDownSearch ){
	    topDownSearch = true;
	}
    
    if( proxyRecursionCount <= proxyRecursionLimit ){
    	//Add this location to visited location list if it has not already been added
    	var winVisited = isInProxyVisitedLoc( win.location.href );
    	
    	if( !winVisited ){
    	   proxyVisitedLoc.push( win.location.href );
    	}
    	
    	// Search the window hierarchy for an object named "proxyAPI"
    	// Look in the current window (win) and recursively look in any child frames
    	if( _Debug ){
    		alert( "win is: "+win.location.href );
    	}
        
        //Is proxyAPI defined in this location
    	if( null != win.proxyAPI ){
    		if ( _Debug ) {
    			alert( "found proxyAPI in this window" );
    		}
    		
    		return win.proxyAPI;
    	}
    	
    	//The proxyAPI var was not found here - look in children or parent
        if( topDownSearch ){
            //Top-Down Search - topDownSearch = true
            
        	//Does the window have frames?
        	if( win.length > 0 ){
        		if( _Debug ){
        			alert("looking for proxyAPI in window's frames");
        		}
        		
        		for( var i = 0; i < win.length; i++ ){
        			if( _Debug ) {
        				alert( "looking for proxyAPI in frames["+i+"]" );
        			}
        			
        			var theAPI = findProxyAPI( win.frames[i], true );
        			if( theAPI != null ){
        				return theAPI;
        			}
        		} //END for loop
        	} //END if( win.length > 0 )
        	
        } else{
            //Bottom-Up Search - topDownSearch = false
            
            //Check child frames if any
            if ( win.length > 0 ) {
        		if (_Debug) {
        			alert("looking for proxyAPI in window's frames");
        		}
        		
        		//Check each frame
        		for ( var i = 0; i < win.length; i++ ) {
        		    //If the child frame has not already been searched
        			if( !isInProxyVisitedLoc( win.frames[ i ] ) ){
            			if ( _Debug ) {
            				alert("looking for proxyAPI in frames["+i+"]");
            			}
            			
            			//Is proxyAPI defined in this frame
            			if( null != win.frames[ i ].proxyAPI ){
            			    return win.frames[ i ].proxyAPI;
            			}
            			
            			//proxyAPI still not found - check children of this child for proxyAPI using top-down search
            			var theAPI = findProxyAPI( win.frames[ i ], true );
            			if( theAPI != null ){
            			    return theAPI;
            			}
        			} else{
        			    if ( _Debug ) {
            				alert("Already looked for proxyAPI in frames["+i+"]");
            			}
        			}
        		}
        	} // END if (win.length > 0) {
        	
        	//proxyAPI still not found - checking parent
        	if( null != win.parent ){
        		var theAPI = findProxyAPI( win.parent, false );
        		if ( theAPI != null ) {
        				return theAPI;
        		}
        	}
        } //END else - topDownSearch != true
	}
    
    //The proxyAPI object was not found - returning NULL
	if (_Debug) {
		alert( "didn't find proxyAPI in this window (or its children)" );
	}
	
	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(){
	//First attempt to find proxy API - bottom up search
	initProxySearch();
	var theAPI = findProxyAPI( this.parent, false );
	if ( theAPI != null ){ 
	   return theAPI;
	}
    
    //The proxyAPI was not found - continue looking for it
	if (_Debug) {
		alert("checking to see if this winodow has an opener");
		alert("window.opener typeof is> " + typeof(window.opener));
	}
	
	//Case of pop-up window
	if ( typeof( top.opener ) != "undefined" ) {
		if (_Debug) {
			alert("checking this window's opener");
		}
		
		if (top.opener != null) {
			if (_Debug) {
				alert("this window's opener is NOT null - looking there");
			}
			
			try{
			    //May cause security error
			    initProxySearch();
	            theAPI = findProxyAPI( top.opener.top, true );
	            if ( theAPI != null ){
            	    return theAPI;
            	}
            	
			} catch( e ){
			    // Catch exception - likely cross domain security error
			    
			    //Commented out since this stage was causing additional
			    // cross domain security error on University of California 
			    // and Microsoft ED Advantage sites.
			    /*initProxySearch();
			    theAPI = findProxyAPI( top.opener, false );
			    if ( theAPI != null ){
            	    return theAPI;
            	}*/
			}
			
		} else {
			if (_Debug) {
				alert("this window's opener is null");
			}
		}
	}

   // No proxy API, check for 'real' API
   // start at the topmost window - findAPI will recurse down through all of the child frames
   theAPI = findAPI( this.top );
   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 (_Debug) {
		alert("checking to see if this window has an opener");
		alert("window.opener typeof is> "+typeof(window.opener));
	}
	
	if ( typeof( top.opener ) != "undefined" ) {
		if (_Debug) {
			alert("checking this windows opener");
		}
		
		if (top.opener != null) {
			if (_Debug) {
				alert("this windows opener is NOT null - looking there");
			}
			
			theAPI = findAPI( top.opener.top );
		} else {
			if (_Debug) {
				alert("this windows opener is null");
			}
		}
	}

	return theAPI;
}
