var SCORM_TRUE = "true";
var SCORM_FALSE = "false";


//Error Numbers
var SCORM_ERROR_NONE                = 0;
var SCORM_ERROR_GENERAL             = 101;
var SCORM_ERROR_INVALID_ARG         = 201;
var SCORM_ERROR_NO_CHILDREN         = 202;
var SCORM_ERROR_NO_COUNT            = 203;
var SCORM_ERROR_NOT_INITIALIZED     = 301;
var SCORM_ERROR_NOT_IMPLEMENTED     = 401;
var SCORM_ERROR_ELEMENT_IS_KEYWORD  = 402;
var SCORM_ERROR_READ_ONLY           = 403;
var SCORM_ERROR_WRITE_ONLY          = 404;
var SCORM_ERROR_INCORRECT_DATA_TYPE = 405;

//Error Strings
var SCORM_ErrorStrings = new Array(11);

SCORM_ErrorStrings[SCORM_ERROR_NONE]                = "No Error";
SCORM_ErrorStrings[SCORM_ERROR_GENERAL]             = "General exception";
SCORM_ErrorStrings[SCORM_ERROR_INVALID_ARG]         = "Invalid argument error";
SCORM_ErrorStrings[SCORM_ERROR_NO_CHILDREN]         = "Element cannot have children";
SCORM_ErrorStrings[SCORM_ERROR_NO_COUNT]            = "Element not an array - cannot have count";
SCORM_ErrorStrings[SCORM_ERROR_NOT_INITIALIZED]     = "Not Initialized";
SCORM_ErrorStrings[SCORM_ERROR_NOT_IMPLEMENTED]     = "Not implemented error";
SCORM_ErrorStrings[SCORM_ERROR_ELEMENT_IS_KEYWORD]  = "Invalid set value, element is a keyword";
SCORM_ErrorStrings[SCORM_ERROR_READ_ONLY]           = "Element is read only";
SCORM_ErrorStrings[SCORM_ERROR_WRITE_ONLY]          = "Element is write only";
SCORM_ErrorStrings[SCORM_ERROR_INCORRECT_DATA_TYPE] = "Incorrect Data Type";

//supported elements

//create an array with an item for each element in the SCORM data model
//each item contains a sub-array that tells us if the element
//is supported, can be read, written or both

function DataModelSupport(supported, supportsRead, supportsWrite){
	this.Supported = supported;
	this.SupportsRead = supportsRead;
	this.SupportsWrite = supportsWrite;
}

var arySupportedElements = new Array(49);

arySupportedElements["cmi.core._children"]					= new DataModelSupport(true, true, false);
arySupportedElements["cmi.core.student_id"]					= new DataModelSupport(true, true, false);
arySupportedElements["cmi.core.student_name"]				= new DataModelSupport(true, true, false);
arySupportedElements["cmi.core.lesson_location"]			= new DataModelSupport(true, true, true);
arySupportedElements["cmi.core.credit"]						= new DataModelSupport(true, true, false);
arySupportedElements["cmi.core.lesson_status"]				= new DataModelSupport(true, true, true);
arySupportedElements["cmi.core.entry"]						= new DataModelSupport(true, true, false);
arySupportedElements["cmi.core.score._children"]			= new DataModelSupport(true, true, false);
arySupportedElements["cmi.core.score.raw"]					= new DataModelSupport(true, true, true);
arySupportedElements["cmi.core.total_time"]					= new DataModelSupport(true, true, false);
arySupportedElements["cmi.core.lesson_mode"]				= new DataModelSupport(true, true, false);
arySupportedElements["cmi.core.exit"]						= new DataModelSupport(true, false, true);
arySupportedElements["cmi.core.session_time"]				= new DataModelSupport(true, false, true);
arySupportedElements["cmi.suspend_data"]					= new DataModelSupport(true, true, true);
arySupportedElements["cmi.launch_data"]						= new DataModelSupport(true, true, false);

arySupportedElements["cmi.objectives._children"]            = new DataModelSupport(true, true, false);
arySupportedElements["cmi.objectives._count"]               = new DataModelSupport(true, true, false);
arySupportedElements["cmi.objectives.n.id"]                 = new DataModelSupport(true, true, true);
arySupportedElements["cmi.objectives.n.score._children"]	= new DataModelSupport(true, true, false);
arySupportedElements["cmi.objectives.n.score.raw"]          = new DataModelSupport(true, true, true);
arySupportedElements["cmi.objectives.n.score.max"]          = new DataModelSupport(true, true, true);
arySupportedElements["cmi.objectives.n.score.min"]          = new DataModelSupport(true, true, true);
arySupportedElements["cmi.objectives.n.status"]             = new DataModelSupport(true, true, true);

arySupportedElements["cmi.core.score.max"]                             = new DataModelSupport(true, true, true);
arySupportedElements["cmi.core.score.min"]                             = new DataModelSupport(true, true, true);
arySupportedElements["cmi.comments"]                                   = new DataModelSupport(true, true, true);
arySupportedElements["cmi.comments_from_lms"]                          = new DataModelSupport(true, true, false);

arySupportedElements["cmi.student_data._children"]                     = new DataModelSupport(true, true, false);
arySupportedElements["cmi.student_data.mastery_score"]                 = new DataModelSupport(true, true, false);
arySupportedElements["cmi.student_data.max_time_allowed"]              = new DataModelSupport(true, true, false);
arySupportedElements["cmi.student_data.time_limit_action"]             = new DataModelSupport(true, true, false);

arySupportedElements["cmi.student_preference._children"]               = new DataModelSupport(true, true, false);
arySupportedElements["cmi.student_preference.audio"]                   = new DataModelSupport(true, true, true);
arySupportedElements["cmi.student_preference.language"]                = new DataModelSupport(true, true, true);
arySupportedElements["cmi.student_preference.speed"]                   = new DataModelSupport(true, true, true);
arySupportedElements["cmi.student_preference.text"]                    = new DataModelSupport(true, true, true);

arySupportedElements["cmi.interactions._children"]                     = new DataModelSupport(true, true,  false);
arySupportedElements["cmi.interactions._count"]                        = new DataModelSupport(true, true,  false);
arySupportedElements["cmi.interactions.n.id"]                          = new DataModelSupport(true, false, true);
arySupportedElements["cmi.interactions.n.objectives._count"]           = new DataModelSupport(true, true,  false);
arySupportedElements["cmi.interactions.n.objectives.n.id"]             = new DataModelSupport(true, false, true);
arySupportedElements["cmi.interactions.n.time"]                        = new DataModelSupport(true, false, true);
arySupportedElements["cmi.interactions.n.type"]                        = new DataModelSupport(true, false, true);
arySupportedElements["cmi.interactions.n.correct_responses._count"]    = new DataModelSupport(true, true,  false);
arySupportedElements["cmi.interactions.n.correct_responses.n.pattern"] = new DataModelSupport(true, false, true);
arySupportedElements["cmi.interactions.n.weighting"]                   = new DataModelSupport(true, false, true);
arySupportedElements["cmi.interactions.n.student_response"]            = new DataModelSupport(true, false, true);
arySupportedElements["cmi.interactions.n.result"]                      = new DataModelSupport(true, false, true);
arySupportedElements["cmi.interactions.n.latency"]                     = new DataModelSupport(true, false, true);


//writable cmi vocabularies
var aryVocabularies = new Array(4);

aryVocabularies["exit"]		     = new Array(SCORM_EXIT_TIME_OUT, SCORM_EXIT_SUSPEND, SCORM_EXIT_LOGOUT, SCORM_EXIT_UNKNOWN);
aryVocabularies["status"]        = new Array(SCORM_STATUS_PASSED, SCORM_STATUS_COMPLETED, SCORM_STATUS_FAILED, 
									         SCORM_STATUS_INCOMPLETE, SCORM_STATUS_BROWSED, SCORM_STATUS_NOT_ATTEMPTED);
aryVocabularies["interaction"]   = new Array(SCORM_TRUE_FALSE, SCORM_CHOICE, SCORM_FILL_IN, 
											 SCORM_MATCHING, SCORM_PERFORMANCE, SCORM_SEQUENCING, 
											 SCORM_LIKERT, SCORM_NUMERIC);
aryVocabularies["result"]        = new Array(SCORM_CORRECT, SCORM_WRONG, SCORM_UNANTICIPATED, SCORM_NEUTRAL);

//children constants
var SCORM_CORE_CHILDREN = "student_id,student_name,lesson_location,credit,lesson_status,entry,total_time,lesson_mode,exit,session_time,score";
var SCORM_CORE_SCORE_CHILDREN = "raw,min,max";
var SCORM_STUDENT_DATA_CHILDREN = "mastery_score,max_time_allowed,time_limit_action";
var SCORM_STUDENT_PREFERENCE_CHILDREN = "audio,language,speed,text";
var SCORM_OBJECTIVES_CHILDREN = "id,score,status";
var SCORM_OBJECTIVES_SCORE_CHILDREN = "raw,min,max";
var SCORM_INTERACTIONS_CHILDREN = "id,objectives,time,type,correct_responses,weighting,student_response,result,latency";


function RunTimeApi(learnerId, learnerName){

	this.LearnerId = learnerId;
	this.LearnerName = learnerName;

	this.ErrorNumber = SCORM_ERROR_NONE;
	this.ErrorString = "";
	this.ErrorDiagnostic = "";

	this.TrackedStartDate = null;
	this.TrackedEndDate = null;
	
	this.Initialized = false;
	this.ScoCalledFinish = false;
	
	this.RunTimeData = null;
	this.LearningObject = null;
	this.Activity = null;
	
	this.StatusSetInCurrentSession = false;
	
}

//-------------------------------------------------------------------------------
//public interface exposed to controller

RunTimeApi.prototype.GetNavigationRequest = RunTimeApi_GetNavigationRequest;
RunTimeApi.prototype.ResetState = RunTimeApi_ResetState;
RunTimeApi.prototype.InitializeForDelivery = RunTimeApi_InitializeForDelivery;
RunTimeApi.prototype.SetDirtyData = RunTimeApi_SetDirtyData;
RunTimeApi.prototype.WriteAuditLog = RunTimeApi_WriteAuditLog;
RunTimeApi.prototype.WriteAuditReturnValue = RunTimeApi_WriteAuditReturnValue;
RunTimeApi.prototype.WriteDetailedLog = RunTimeApi_WriteDetailedLog;
RunTimeApi.prototype.CloseOutSession = RunTimeApi_CloseOutSession;
RunTimeApi.prototype.NeedToCloseOutSession = RunTimeApi_NeedToCloseOutSession;
RunTimeApi.prototype.AccumulateTotalTimeTracked = RunTimeApi_AccumulateTotalTrackedTime;
RunTimeApi.prototype.InitTrackedTimeStart = RunTimeApi_InitTrackedTimeStart;

function RunTimeApi_GetNavigationRequest(){
	return null;
}

function RunTimeApi_ResetState(){
	
	this.ErrorNumber = SCORM_ERROR_NONE;
	this.ErrorString = "";
	this.ErrorDiagnostic = "";

	this.TrackedStartDate = null;
	this.TrackedEndDate = null;
	
	this.Initialized = false;
	this.Terminated = false;
	
	this.RunTimeData = null;
	this.LearningObject = null;
	this.Activity = null;
	
	this.StatusSetInCurrentSession = false;
	this.ScoCalledFinish = false;
}

function RunTimeApi_InitializeForDelivery(activity){

	this.ErrorNumber = SCORM_ERROR_NONE;
	this.ErrorString = "";
	this.ErrorDiagnostic = "";

	this.TrackedStartDate = null;
	this.TrackedEndDate = null;
	
	this.Initialized = false;
	
	this.StatusSetInCurrentSession = false;
	this.ScoCalledFinish = false;
	
	this.RunTimeData = activity.RunTime;
	this.LearningObject = activity.LearningObject;
	this.Activity = activity;	
	
	if (Control.Package.Properties.ResetRunTimeDataTiming == RESET_RT_DATA_TIMING_WHEN_EXIT_IS_NOT_SUSPEND ||
		Control.Package.Properties.ResetRunTimeDataTiming == RESET_RT_DATA_TIMING_ON_EACH_NEW_SEQUENCING_ATTEMPT){
		if (this.RunTimeData.Exit != SCORM_EXIT_SUSPEND && this.RunTimeData.Exit != SCORM_EXIT_LOGOUT){
			this.RunTimeData.ResetState();
		}
	}	

}

function RunTimeApi_SetDirtyData(){
	this.Activity.DataState = DATA_STATE_DIRTY;
}

function RunTimeApi_WriteAuditLog(str){
	Debug.WriteRteAudit(str);
}

function RunTimeApi_WriteAuditReturnValue(str){
	Debug.WriteRteAuditReturnValue(str);
}

function RunTimeApi_WriteDetailedLog(str){
	Debug.WriteRteDetailed(str);
}

//Note the value for NeedToCloseOutSession is a bit misleading, false means we do need to close out the session
function RunTimeApi_NeedToCloseOutSession(){
	return ((this.Initialized === false) || this.ScoCalledFinish);
}

//-------------------------------------------------------------------------------
//public items specific to this API

RunTimeApi.prototype.LMSInitialize = RunTimeApi_Initialize;
RunTimeApi.prototype.LMSFinish = RunTimeApi_Finish;
RunTimeApi.prototype.LMSSetValue = RunTimeApi_SetValue;
RunTimeApi.prototype.LMSGetValue = RunTimeApi_GetValue;
RunTimeApi.prototype.LMSCommit = RunTimeApi_Commit;
RunTimeApi.prototype.LMSGetLastError = RunTimeApi_GetLastError;
RunTimeApi.prototype.LMSGetErrorString = RunTimeApi_GetErrorString;
RunTimeApi.prototype.LMSGetDiagnostic = RunTimeApi_GetDiagnostic;

function RunTimeApi_Initialize(arg){
	
	this.WriteAuditLog("LMSInitialize('" + arg + "')");
	
	var callIsErrorFree;
	var returnValue;
	
	arg = CleanExternalString(arg);
	
	this.ClearErrorState();
	
	callIsErrorFree = this.CheckForInitializeError(arg);
	
	if (! callIsErrorFree){
		returnValue = SCORM_FALSE;
	}
	else{
		// the TrackedStartDate is now being set when the content is launched by the browser (inside the ScoLauncher or PopupLauncher)
		//this.TrackedStartDate = new Date();
		this.Initialized = true;
		
		returnValue = SCORM_TRUE;
	}
	
	this.WriteAuditReturnValue(returnValue);
	return returnValue;	
}

function RunTimeApi_Finish(arg){
	
	this.WriteAuditLog("LMSFinish('" + arg + "')");
	
	var callIsErrorFree;
	
	arg = CleanExternalString(arg);
	
	this.ClearErrorState();
	
	callIsErrorFree = this.CheckForFinishError(arg);
	
	//make sure that the Control is only notified to unload once
	var canNotifyControlToUnload = (callIsErrorFree && (this.ScoCalledFinish === false));
	
	if (callIsErrorFree === false){
		returnValue = SCORM_FALSE;
	}
	else{
		
		this.CloseOutSession();
		this.SetDirtyData();
		this.Initialized = false;
		this.ScoCalledFinish = true;
		returnValue = SCORM_TRUE;
	}
	
	//TODO: test this when the browser is closed unexpectedly
	
	//Signal the controler to unload this sco after a brief interval has passed to allow for any cleanup. 

	//In SCORM 1.2, we use rudamentary sequencing, so we can always assume that there is a navigation request (the sequencing action) that
	//will occur once we unload the SCO.
	if (canNotifyControlToUnload === true && Control.IsThereAPendingNavigationRequest() === false){
		
		//do a pre-evaluation of the expected exit action to determine if it is "Do Nothing" in which case, the content should not be unloaded
		//KNOWN LIMITATION: this pre-evaluation does not perform roll up, so if the Do Nothing action is only set on a FinalScoCourseSatisfied item, 
		//the SCO will still be unloaded.
		
		var exitAction = Control.Sequencer.GetExitAction(this.Activity, true, Control.Sequencer.LogSeqAudit("Pre-evaluation of exit action"));
		
		if (exitAction != EXIT_ACTION_DO_NOTHING){
			window.setTimeout("Control.ScoHasTerminatedSoUnload();", 150);
		}
	}
	
	Control.SignalTerminated();
	
	this.WriteAuditReturnValue(returnValue);
	return returnValue;
}

function RunTimeApi_SetValue(element, value){
	
	this.WriteAuditLog("LMSSetValue('" + element + "', '" + value +"')");
	
	var callIsErrorFree;
	var returnValue;
	
	this.ClearErrorState();
	
	element = CleanExternalString(element);
	value = CleanExternalString(value);

	var elementWithOutIndex = RemoveIndiciesFromCmiElement(element); 

	var primaryIndex = ExtractIndex(element);
	var secondaryIndex = ExtractSecondaryIndex(element);
		
	callIsErrorFree = this.CheckForSetValueError(element, value, elementWithOutIndex, primaryIndex, secondaryIndex);
	
	if (! callIsErrorFree){
		returnValue = SCORM_FALSE;
	}
	else{
		this.StoreValue(element, value, elementWithOutIndex, primaryIndex, secondaryIndex);
		
		this.SetDirtyData();
		
		returnValue = SCORM_TRUE;
	}
	
	this.WriteAuditReturnValue(returnValue);
	return returnValue;
}


function RunTimeApi_GetValue(element){
	
	this.WriteAuditLog("LMSGetValue('" + element + "')");
	
	var returnValue;
	var callIsErrorFree;
	
	this.ClearErrorState();
	
	element = CleanExternalString(element);
	
	var elementWithOutIndex = RemoveIndiciesFromCmiElement(element); 

	var primaryIndex = ExtractIndex(element);
	var secondaryIndex = ExtractSecondaryIndex(element);
	
	callIsErrorFree = this.CheckForGetValueError(element, elementWithOutIndex, primaryIndex, secondaryIndex);
	
	if (! callIsErrorFree){
		returnValue = "";
	}
	else{
	
		returnValue = this.RetrieveGetValueData(element, elementWithOutIndex, primaryIndex, secondaryIndex);
	
		//out internal representation of some data elements will store the values as null if they have not yet been set
		//in SCORM 1.2, this should just be returned as an empty string
		if (returnValue === null){
			returnValue = "";
		}
	}
	
	this.WriteAuditReturnValue(returnValue);
	return returnValue;	

}



function RunTimeApi_Commit(arg){
	
	this.WriteAuditLog("LMSCommit('" + arg + "')");
	
	var callIsErrorFree;
	
	arg = CleanExternalString(arg);
	
	//TODO - check for a fatal server error here, if found, return false...do this in all API functions
	
	this.ClearErrorState();
	
	callIsErrorFree = this.CheckForCommitError(arg);
	
	if (callIsErrorFree === false){
		returnValue = SCORM_FALSE;
	}
	else{
		returnValue = SCORM_TRUE;
	}
	
	this.WriteAuditReturnValue(returnValue);
	return returnValue;
}

function RunTimeApi_GetLastError(){
	this.WriteAuditLog("LMSGetLastError()");
	
	var returnValue = this.ErrorNumber;
	
	this.WriteAuditReturnValue(returnValue);
	return returnValue;
}

function RunTimeApi_GetErrorString(errorNumber){
	
	this.WriteAuditLog("LMSGetErrorString('" + errorNumber + "')");
	
	var returnValue;
	
	errorNumber = CleanExternalString(errorNumber);
	
	if (SCORM_ErrorStrings[errorNumber] === undefined || SCORM_ErrorStrings[errorNumber] === null){
		returnValue = "";
	}
	else{
		returnValue = SCORM_ErrorStrings[errorNumber];
	}
	
	this.WriteAuditReturnValue(returnValue);
	return returnValue;
}

function RunTimeApi_GetDiagnostic(requestedErrorNumber){
	
	this.WriteAuditLog("LMSGetDiagnostic('" + requestedErrorNumber + "')");
	
	var returnValue;
	
	requestedErrorNumber = CleanExternalString(requestedErrorNumber);
	
	if (requestedErrorNumber == this.ErrorNumber || requestedErrorNumber === "" || requestedErrorNumber === null){
		if (this.ErrorDiagnostic.length > 0){
			returnValue = this.ErrorDiagnostic;
		}
		else {
			returnValue = "No diagnostic information is available.";
		}
	}
	else{
		returnValue = "No diagnostic information available for error number (" + requestedErrorNumber + ") ";
	}
	
	this.WriteAuditReturnValue(returnValue);
	return returnValue;
}


//-------------------------------------------------------------------------------
//private items internal to this API

RunTimeApi.prototype.SetErrorState = RunTimeApi_SetErrorState;
RunTimeApi.prototype.ClearErrorState = RunTimeApi_ClearErrorState;

RunTimeApi.prototype.CheckForInitializeError = RunTimeApi_CheckForInitializeError;
RunTimeApi.prototype.CheckForFinishError = RunTimeApi_CheckForFinishError;
RunTimeApi.prototype.CheckForCommitError = RunTimeApi_CheckForCommitError;

RunTimeApi.prototype.CheckForSetValueError = RunTimeApi_CheckForSetValueError;
RunTimeApi.prototype.StoreValue = RunTimeApi_StoreValue;

RunTimeApi.prototype.CheckForGetValueError = RunTimeApi_CheckForGetValueError;
RunTimeApi.prototype.RetrieveGetValueData = RunTimeApi_RetrieveGetValueData;



RunTimeApi.prototype.JoinCommentsArray = RunTimeApi_JoinCommentsArray;
RunTimeApi.prototype.IsValidVocabElement = RunTimeApi_IsValidVocabElement;

function RunTimeApi_SetErrorState(errorNumber, errorDiagnostic){
	
	if (errorNumber != SCORM_ERROR_NONE){
		this.WriteDetailedLog("SCORM ERROR FOUND - Set Error State: " + errorNumber + " - " + errorDiagnostic);
	}
	
	this.ErrorNumber = errorNumber;
	this.ErrorDiagnostic = errorDiagnostic;
}

function RunTimeApi_ClearErrorState(){
	this.ErrorNumber = SCORM_ERROR_NONE;
	this.ErrorDiagnostic = "";
}

function RunTimeApi_CheckForInitializeError(arg){
	
	this.WriteDetailedLog("Checking for Initialize Error");
	
	if (arg !== ""){
		this.SetErrorState(SCORM_ERROR_INVALID_ARG, "Invalid argument to LMSInitialize (arg=" + arg + ")");
		return false;
	}
	
	if (this.Initialized === true){
		this.SetErrorState(SCORM_ERROR_GENERAL,  "LMSInitialize has already been called.");
		return false;
	}
	
	this.WriteDetailedLog("Call is error free.")
	return true;
}

function RunTimeApi_CheckForFinishError(arg){
	
	this.WriteDetailedLog("Checking for Finish Error");
	
	if (this.Initialized === false){
		
		this.SetErrorState(SCORM_ERROR_NOT_INITIALIZED, "Finished called when not initialized.");
		return false;
	}

	if (arg !== ""){
		
		this.SetErrorState(SCORM_ERROR_INVALID_ARG, "Invalid argument to LMSFinish (arg=" + arg + ")");
		return false;
	}
	
	this.WriteDetailedLog("Call is error free.")
	return true;
}

function RunTimeApi_CheckForCommitError(arg){

	this.WriteDetailedLog("Checking for Commit Error");
	
	if (this.Initialized === false){

		this.SetErrorState(SCORM_ERROR_NOT_INITIALIZED, "Commit called when not initialized.");
		return false;
	}

	if (arg !== ""){

		this.SetErrorState(SCORM_ERROR_INVALID_ARG, "Invalid argument to LMSCommit (arg=" + arg + ")");
		return false;
	}
	
	this.WriteDetailedLog("Call is error free.")
	return true;
}



function RunTimeApi_CheckForSetValueError(element, value, elementWithOutIndex, primaryIndex, secondaryIndex){

	this.WriteDetailedLog("CheckForSetValueError (" + element + ", " + value + ", " + elementWithOutIndex + ", " + primaryIndex + ", " + secondaryIndex + ") ");
	
	//TODO - improvement - tighten up this function...there's a lot of consolidation that could happen in the big switch statement below
	
	/*
	Check for errors
	
	if not initialized
		return 301
	if the element is a valid SCORM element (we have it in our array)
		if it is not supported
			return 401
		if it is a key word
			return 402
		if it is read only
			return 403
		else
			success condition			
	else - not a valid SCORM element
		if it ends in "_children"
			return 202
		if it ends in "_count"
			return 203
		else - we have no idea what they're trying to do
			return 201
	*/	
	
	if (this.Initialized === false){
		//WriteToDebug("ERROR: SetValue called when not initialized");
		this.SetErrorState(SCORM_ERROR_NOT_INITIALIZED, "SetValue called when not initialized. strElement-" + element + " strValue-" + value);
		return false;
	}
	
	if (! (arySupportedElements[elementWithOutIndex] === undefined || arySupportedElements[elementWithOutIndex] === null) ) {
		//WriteToDebug("Found Element in array");
		
		if ( arySupportedElements[elementWithOutIndex].Supported === false){
			//WriteToDebug("ERROR: element is not implemented");
			Debug.AssertError("Should not have any un-implemented vocab elements");
			this.SetErrorState(SCORM_ERROR_NOT_IMPLEMENTED, "The parameter '" + element + "' is not implemented.");
			return false;
		}
		
		if (elementWithOutIndex.search(/_children$/) > 0 || elementWithOutIndex.search(/_count$/) > 0){
			//WriteToDebug("ERROR: Can't write to a keyword element");
			this.SetErrorState(SCORM_ERROR_ELEMENT_IS_KEYWORD, "The parameter '" + element + "' is a keyword and cannot be written to.");
			return false;			
		}
		
		if (arySupportedElements[elementWithOutIndex].SupportsWrite === false){
			//WriteToDebug("ERROR: Element is Read Only");
			this.SetErrorState(SCORM_ERROR_READ_ONLY, "The parameter '" + element + "' is read-only.");
			return false;
		}
				
	}	
	else{
		
		if (elementWithOutIndex.search(/._children$/) > 0 ){
			//WriteToDebug("Element is _children");
			
			elementWithOutIndex = elementWithOutIndex.replace("._children", "");
			
			if (arySupportedElements[elementWithOutIndex] !== undefined && arySupportedElements[elementWithOutIndex] !== null ){
				//WriteToDebug("ERROR: Parameter does not support _children");
				this.SetErrorState(SCORM_ERROR_NO_CHILDREN, "The parameter '" + elementWithOutIndex + "' does not support the _children keyword.");
				return false;				
			}
		}
		else if (elementWithOutIndex.search(/._count$/) > 0 ){
			//WriteToDebug("Element is _count");
			
			elementWithOutIndex = elementWithOutIndex.replace("._count", "");
			
			if (arySupportedElements[strBaseElement] !== undefined && arySupportedElements[strBaseElement] !== null ){
				//WriteToDebug("Element does not support _count");
				this.SetErrorState(SCORM_ERROR_NO_COUNT, "The parameter '" + elementWithOutIndex + "' does not support the _count keyword.");
				return false;				
			}
		}
		
		//WriteToDebug("ERROR: Element not recognized");
		this.SetErrorState(SCORM_ERROR_INVALID_ARG, "The parameter '" + element + "' is not recognized.");
		return false;
			
	}
	
	var returnValue = null;
	var intTemp;
	
	switch(elementWithOutIndex){
		
		case "cmi.core.lesson_location":
			this.WriteDetailedLog("Element is: lesson_location");
			
			//validate string length
			if (value.length > 255){
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.core.lesson_location may not be greater than 255 characters, your value (" + value + ") is " + value.length + " characters.");
				returnValue = false;
			}
			break;
			
		case "cmi.core.lesson_status":
		
			this.WriteDetailedLog("Element is: lesson_statuc");
					
			// validate that value is in data model
			if (! this.IsValidVocabElement(value, "status")){
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "The value for cmi.core.lesson_status is not in the CMI vocabulary. Your value: " + value);
				returnValue = false;		
			}	
			
			//don't allow to change to "not attempted" - this state may only be set by the LMS upon initialization
			if (value == SCORM_STATUS_NOT_ATTEMPTED){
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.core.lesson_status cannot be set to 'not attempted'.  This value may only be set by the LMS upon initialization.");
				returnValue = false;
			}
			
			break;		
			
		case "cmi.core.exit":
			
			this.WriteDetailedLog("Element is: exit");
			
			// validate that value is in data model
			if (! this.IsValidVocabElement(value, "exit")){
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "The value for cmi.core.exit is not in the CMI vocabulary. Your value: " + value);
				returnValue = false;		
			}

			break;	
			
		case "cmi.core.session_time":
			
			this.WriteDetailedLog("Element is: session_time");
			
			//validate that value is of the correct form
			if (! IsValidCMITimeSpan(value)){
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "The value for cmi.core.session_time is not formatted properly. Your value: " + value);
				returnValue = false;			
			}
			break;	
			
		case "cmi.core.score.raw":
			
			this.WriteDetailedLog("Element is: score.raw");
			
			// validate that value is a decimal between 0-100 or an empty string
			if (value !== ""){
				if (IsValidCMIDecimal(value)){
				
					intTemp = parseFloat(value);
					
					if (intTemp < 0 || intTemp > 100){
						this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.core.score.raw must be a valid decimal between 0 and 100.  Your value is: " + intTemp);
						returnValue = false;					
					}
				}
				else{	
					this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.core.score.raw must be a valid decimal.  Your value is: " + value);
					returnValue = false;			
				}
			}
			
			break;	

		case "cmi.core.score.max":
		
			this.WriteDetailedLog("Element is: score.max");
			
			// validate that value is a decimal between 0-100 or an empty string
			if (value !== ""){
				if (IsValidCMIDecimal(value)){
				
					intTemp = parseFloat(value);
					
					if (intTemp < 0 || intTemp > 100){
						this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.core.score.max must be a valid decimal between 0 and 100.  Your value is: " + intTemp);
						returnValue = false;					
					}
				}
				else{	
					this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.core.score.max must be a valid decimal.  Your value is: " + value);
					returnValue = false;			
				}
			}
			
			break;
			
		case "cmi.core.score.min":
		
			this.WriteDetailedLog("Element is: score.min");
			
			// validate that value is a decimal between 0-100 or an empty string 
			if (value !== ""){
				if (IsValidCMIDecimal(value)){
				
					intTemp = parseFloat(value);
					
					if (intTemp < 0 || intTemp > 100){
						this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.core.score.min must be a valid decimal between 0 and 100.  Your value is: " + intTemp);
						returnValue = false;					
					}
				}
				else{	
					this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.core.score.min must be a valid decimal.  Your value is: " + value);
					returnValue = false;			
				}
			}
			
			break;

														
		case "cmi.suspend_data":
			
			this.WriteDetailedLog("Element is: suspend_data");
			
			//validate string length
			if (value.length > 4096){
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.core.suspend_data may not be greater than 4096 characters, your value is " + value.length + " characters. Your value\n" + value);
				returnValue = false;
			}
			
			break;	
		
		case "cmi.objectives.n.id":
			this.WriteDetailedLog("Element is: objectives.id");

			//validate data type
			if (! IsValidCMIIdentifier(value)){
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "The value '" + value + "' is not a valid CMI Identifier");
				returnValue = false;
			}
			
			//validate objective index
			if (! this.RunTimeData.IsValidObjectiveIndex(primaryIndex)){
				this.SetErrorState(SCORM_ERROR_INVALID_ARG, "The index '" + primaryIndex + "' is not valid, objective indicies must be set sequentially starting with 0");
				returnValue = false;
			}
			
			break;
			
		case "cmi.objectives.n.status":
		
			this.WriteDetailedLog("Element is: objectives.status");
			
			//validate objective index
			if (! this.RunTimeData.IsValidObjectiveIndex(primaryIndex)){
				this.SetErrorState(SCORM_ERROR_INVALID_ARG, "The index '" + primaryIndex + "' is not valid, objective indicies must be set sequentially starting with 0");
				returnValue = false;
			}

			// validate that value is in data model
			if (! this.IsValidVocabElement(value, "status")){
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "The value for cmi.objectives.n.status is not in the CMI vocabulary. Your value: " + value);
				returnValue = false;		
			}	
			
			break;
			
		case "cmi.objectives.n.score.raw":
		
			this.WriteDetailedLog("Element is: objectives.score.raw");
			
			//validate objective index
			if (! this.RunTimeData.IsValidObjectiveIndex(primaryIndex)){
				this.SetErrorState(SCORM_ERROR_INVALID_ARG, "The index '" + primaryIndex + "' is not valid, objective indicies must be set sequentially starting with 0");
				returnValue = false;
			}

			// validate that value is a decimal between 0-100 or an empty string 
			if (value !== ""){
				if (IsValidCMIDecimal(value)){
				
					intTemp = parseFloat(value);
					
					if (intTemp < 0 || intTemp > 100){
						this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.objectives.n.score.raw must be a valid decimal between 0 and 100.  Your value is: " + intTemp);
						returnValue = false;					
					}
				}
				else{	
					this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.objectives.score.raw must be a valid decimal.  Your value is: " + value);
					returnValue = false;			
				}	
			}
			
			break;
			
		case "cmi.objectives.n.score.min":
		
			this.WriteDetailedLog("Element is: objectives.score.min");
			
			//validate objective index
			if (! this.RunTimeData.IsValidObjectiveIndex(primaryIndex)){
				this.SetErrorState(SCORM_ERROR_INVALID_ARG, "The index '" + primaryIndex + "' is not valid, objective indicies must be set sequentially starting with 0");
				returnValue = false;
			}

			// validate that value is a decimal between 0-100 or an empty string 
			if (value !== ""){
				if (IsValidCMIDecimal(value)){
				
					intTemp = parseFloat(value);
					
					if (intTemp < 0 || intTemp > 100){
						this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.objectives.n.score.min must be a valid decimal between 0 and 100.  Your value is: " + intTemp);
						returnValue = false;					
					}
				}
				else{	
					this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.objectives.score.min must be a valid decimal.  Your value is: " + value);
					returnValue = false;			
				}
			}
	
			break;
			
		case "cmi.objectives.n.score.max":
		
			this.WriteDetailedLog("Element is: objectives.score.max");
			
			//validate objective index
			if (! this.RunTimeData.IsValidObjectiveIndex(primaryIndex)){
				this.SetErrorState(SCORM_ERROR_INVALID_ARG, "The index '" + primaryIndex + "' is not valid, objective indicies must be set sequentially starting with 0");
				returnValue = false;
			}

			// validate that value is a decimal between 0-100 or an empty string 
			if (value !== ""){
				if (IsValidCMIDecimal(value)){
				
					intTemp = parseFloat(value);
					
					if (intTemp < 0 || intTemp > 100){
						this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.objectives.n.score.max must be a valid decimal between 0 and 100.  Your value is: " + intTemp);
						returnValue = false;					
					}
				}
				else{	
					this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.objectives.score.max must be a valid decimal.  Your value is: " + value);
					returnValue = false;			
				}
			}
			
			break;								
		

			
		case "cmi.comments":
			this.WriteDetailedLog("Element is: comments");
			
			//validate string length
			if (value.length > 4096){
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.comments may not be greater than 4096 characters, your value (" + value + ") is " + value.length + " characters.");
				returnValue = false;
			}
			
			break;
			
		case "cmi.student_preference.audio":
			this.WriteDetailedLog("Element is: audio");
			
			// validate that value is a decimal between 0-100
			if (IsValidCMISInteger(value)){
			
				intTemp = parseInt(value, 10);
				
				if (intTemp < -1 || intTemp > 100){
					this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.student_preference.audio must be a valid integer between -1 and 100.  Your value is: " + intTemp);
					returnValue = false;					
				}
			}
			else{	
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.student_preference.audio must be a valid signed integer.  Your value is: " + value);
				returnValue = false;			
			}

			break;
			
		case "cmi.student_preference.language":
			this.WriteDetailedLog("Element is: language");
			
			//validate string length
			if (value.length > 255){
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.student_preference.language may not be greater than 255 characters, your value (" + value + ") is " + value.length + " characters.");
				returnValue = false;
			}

			break;
			
			
		case "cmi.student_preference.speed":
		
			this.WriteDetailedLog("Element is: speed");
			
			// validate that value is a decimal between -100 - 100 
			if (IsValidCMISInteger(value)){
			
				intTemp = parseInt(value, 10);
				
				if (intTemp < -100 || intTemp > 100){
					this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.student_preference.audio must be a valid integer between -100 and 100.  Your value is: " + intTemp);
					returnValue = false;					
				}
			}
			else{
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.student_preference.audio must be a valid signed integer.  Your value is: " + value);
				returnValue = false;			
			}	
			break;
			
		case "cmi.student_preference.text":
		
			this.WriteDetailedLog("Element is: text");
			
			// validate that value is a decimal between -1 - 1
			if (IsValidCMISInteger(value)){
			
				intTemp = parseInt(value, 10);
				
				if (intTemp < -1 || intTemp > 1){
					this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.student_preference.audio must be a valid integer between -1 and 1.  Your value is: " + intTemp);
					returnValue = false;					
				}
			}
			else{	
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.student_preference.audio must be a valid signed integer.  Your value is: " + value);
				returnValue = false;			
			}	
			
			break;
			
		case "cmi.interactions.n.id":
		
			this.WriteDetailedLog("Element is: interactions.id");

			//validate data type
			if (! IsValidCMIIdentifier(value)){
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "The value '" + value + "' is not a valid CMI Identifier");
				returnValue = false;
			}
			
			//validate interaction index is in correct position
			if (! this.RunTimeData.IsValidInteractionIndex(primaryIndex)){
				this.SetErrorState(SCORM_ERROR_INVALID_ARG, "The index '" + primaryIndex + "' is not valid, interaction indicies must be set sequentially starting with 0");
				returnValue = false;
			}
			
			break;
			
		case "cmi.interactions.n.objectives.n.id":
		
			this.WriteDetailedLog("Element is: interactions.objectives.id");

			//validate data type
			if (! IsValidCMIIdentifier(value)){
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "The value '" + value + "' is not a valid CMI Identifier");
				returnValue = false;
			}
			
			//validate interaction index is in correct position
			if (! this.RunTimeData.IsValidInteractionIndex(primaryIndex)){
				this.SetErrorState(SCORM_ERROR_INVALID_ARG, "The index '" + primaryIndex + "' is not valid, interaction indicies must be set sequentially starting with 0");
				returnValue = false;
			}
		
			if (! this.RunTimeData.IsValidInteractionObjectiveIndex(primaryIndex, secondaryIndex)){
				this.SetErrorState(SCORM_ERROR_INVALID_ARG, "The index '" + secondaryIndex + "' is not valid, interaction objective indicies must be set sequentially starting with 0");
				returnValue = false;
			}
			
			break;
			
		case "cmi.interactions.n.time":
		
			this.WriteDetailedLog("Element is: interactions.time");
			
			//validate interaction index
			if (! this.RunTimeData.IsValidInteractionIndex(primaryIndex)){
				this.SetErrorState(SCORM_ERROR_INVALID_ARG, "The index '" + primaryIndex + "' is not valid, interaction indicies must be set sequentially starting with 0");
				returnValue = false;
			}

			// validate that value is a valid CMI Time
			if (! IsValidCMITime(value)){	
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.interactions.n.time must be a valid time.  Your value is: " + value);
				returnValue = false;			
			}
			
			break;
			
		case "cmi.interactions.n.type":
		
			this.WriteDetailedLog("Element is: interacitons.type");
			
			//validate interaction index
			if (! this.RunTimeData.IsValidInteractionIndex(primaryIndex)){
				this.SetErrorState(SCORM_ERROR_INVALID_ARG, "The index '" + primaryIndex + "' is not valid, interaction indicies must be set sequentially starting with 0");
				returnValue = false;
			}

			// validate that value is in data model
			if ( ! this.IsValidVocabElement(value, "interaction")){
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "The value for cmi.interactions.n.type is not in the CMI vocabulary. Your value: " + value);
				returnValue = false;		
			}	



			//validate consistency with previously recorded correct response(s) and student response
			//note - if this causes problems, it can be removed since we are not currently doing anything with interactions

			//TODO - improvement - add parameter to remove this check for bad SCOs (maybe just check the parameter in the IsValidCMIFeedback function)

			if (this.RunTimeData.Interactions[primaryIndex] !== undefined){
				
				if (this.RunTimeData.Interactions[primaryIndex].LearnerResponse !== null){
					if (! IsValidCMIFeedback(value, this.RunTimeData.Interactions[primaryIndex].LearnerResponse)){	
						this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.interactions.n.type must be consistent with previously recorded student response.  Your value is: " + value);
						returnValue = false;			
					}
				}

				for (var i=0; i < this.RunTimeData.Interactions[primaryIndex].CorrectResponses.length; i++){
					if (! IsValidCMIFeedback(value, this.RunTimeData.Interactions[primaryIndex].CorrectResponses[i])){	
						this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.interactions.n.type must be consistent with previously recorded correct response (" + i + " - " + this.RunTimeData.Interactions[primaryIndex].CorrectResponses[i] + ").  Your value is: " + value);
						returnValue = false;			
					}
				}
			}	
			
			break;
			
		case "cmi.interactions.n.correct_responses.n.pattern":
		
			this.WriteDetailedLog("Element is: interactions.correct responses.pattern");
			
			//validate interaction index is in correct position
			if (! this.RunTimeData.IsValidInteractionIndex(primaryIndex)){
				this.SetErrorState(SCORM_ERROR_INVALID_ARG, "The index '" + primaryIndex + "' is not valid, interaction indicies must be set sequentially starting with 0");
				returnValue = false;
			}
			
			if (! this.RunTimeData.IsValidInteractionCorrectResponseIndex(primaryIndex, secondaryIndex)){
				this.SetErrorState(SCORM_ERROR_INVALID_ARG, "The index '" + secondaryIndex + "' is not valid, interaction correct response indicies must be set sequentially starting with 0");
				returnValue = false;
			}
			
			
			//note - if this causes problems, it can be removed since we are not currently doing anything with interactions
			//TODO - improvement - add parameter to remove this check for bad SCOs (maybe just check the parameter in the IsValidCMIFeedback function)
			if (this.RunTimeData.Interactions[primaryIndex] !== undefined){
				if (! IsValidCMIFeedback(this.RunTimeData.Interactions[primaryIndex].Type, value)){	
					this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.interactions.n.student_response must be a valid CMIFeedback - value must be consistent with interaction type.  Your value is: " + value);
					returnValue = false;			
				}	
			}
			
			break;
			
		case "cmi.interactions.n.weighting":
		
			this.WriteDetailedLog("Element is: interactions.weighting");
			
			//validate interaction index
			if (! this.RunTimeData.IsValidInteractionIndex(primaryIndex)){
				this.SetErrorState(SCORM_ERROR_INVALID_ARG, "The index '" + primaryIndex + "' is not valid, interaction indicies must be set sequentially starting with 0");
				returnValue = false;
			}

			// validate that value is a valid decimal
			if (! IsValidCMIDecimal(value)){	
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.interactions.n.weighting must be a valid decimal.  Your value is: " + value);
				returnValue = false;			
			}
			
			break;		
			
		case "cmi.interactions.n.student_response":
		
			this.WriteDetailedLog("Element is: interactions.student_response");
			
			//validate interaction index
			if (! this.RunTimeData.IsValidInteractionIndex(primaryIndex)){
				this.SetErrorState(SCORM_ERROR_INVALID_ARG, "The index '" + primaryIndex + "' is not valid, interaction indicies must be set sequentially starting with 0");
				returnValue = false;
			}
			
			
			//note - if this causes problems, it can be removed since we are not currently doing anything with interactions
			//TODO - improvement - add parameter to remove this check for bad SCOs (maybe just check the parameter in the IsValidCMIFeedback function)
			if (this.RunTimeData.Interactions[primaryIndex] !== undefined){
				if (! IsValidCMIFeedback(this.RunTimeData.Interactions[primaryIndex].Type, value)){	
					this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.interactions.n.student_response must be a valid CMIFeedback - value must be consistent with interaction type.  Your value is: " + value);
					returnValue = false;			
				}	
			}
			
			break;
			
		case "cmi.interactions.n.result":
		
			this.WriteDetailedLog("Element is: interactions.result");
			
			//validate objective index
			if (! this.RunTimeData.IsValidInteractionIndex(primaryIndex)){
				this.SetErrorState(SCORM_ERROR_INVALID_ARG, "The index '" + primaryIndex + "' is not valid, interaction indicies must be set sequentially starting with 0");
				returnValue = false;
			}

			// validate that value is in data model - can be a vocab element or a CMIDecimal
			if ( !this.IsValidVocabElement(value, "result") && !IsValidCMIDecimal(value)){
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "The value for cmi.interactions.n.result is not in the CMI vocabulary. Your value: " + value);
				returnValue = false;		
			}	
			
			break;
			
		case "cmi.interactions.n.latency":
		
			this.WriteDetailedLog("Element is: interactions.latency");
			
			//validate interaction index
			if (! this.RunTimeData.IsValidInteractionIndex(primaryIndex)){
				this.SetErrorState(SCORM_ERROR_INVALID_ARG, "The index '" + primaryIndex + "' is not valid, interaction indicies must be set sequentially starting with 0");
				returnValue = false;
			}

			// validate that value is a valid CMI Timespan
			if (! IsValidCMITimeSpan(value)){
				this.SetErrorState(SCORM_ERROR_INCORRECT_DATA_TYPE, "cmi.interactions.n.latency must be a valid timespan.  Your value is: " + value);
				returnValue = false;			
			}
			
			break;	
			
		default:
		
			this.WriteDetailedLog("Element Not Matched");
						
			this.SetErrorState(SCORM_ERROR_GENERAL, "Setting the data element you requested is not supported although it is listed as being supported, please contact technical support.  Element-" + element);
			returnValue = false;
			
			break;				
	}	
	
	if (returnValue === null){
		returnValue = true;
	}
	else{
		returnValue = false;
	}
	
	if (returnValue == true){
		this.WriteDetailedLog("Call is error free.")
	}
	
	return returnValue;
}







































function RunTimeApi_StoreValue(element, value, elementWithOutIndex, primaryIndex, secondaryIndex){
	
	this.WriteDetailedLog("StoreValue (" + element + ", " + value + ", " + elementWithOutIndex + ", " + primaryIndex + ", " + secondaryIndex + ") ");
	
	switch(elementWithOutIndex){
		
		case "cmi.core.lesson_location":
			this.WriteDetailedLog("Element is: lesson_location");
			
			this.RunTimeData.Location = value;
		break;
			
		case "cmi.core.lesson_status":
			
			this.WriteDetailedLog("Element is: lesson_status");
			
			//don't allow status to change from something that represents complete, to something that represents incomplete
			//they CAN change it, if they set it to complete, and want to change it to something else, but not if it was complete
			//after the last taking of the content
			//they are also allowed to change from completed to passed (necessary for proper display of split status)
			
			this.WriteDetailedLog("StatusSetInCurrentSession = " + this.StatusSetInCurrentSession + ", CompletionStatus=" + this.RunTimeData.CompletionStatus);
			
			if (this.StatusSetInCurrentSession || 
				this.RunTimeData.CompletionStatus != SCORM_STATUS_COMPLETED || 
				(this.RunTimeData.CompletionStatus == SCORM_STATUS_COMPLETED && this.RunTimeData.SuccessStatus == SCORM_STATUS_FAILED) || 
				value == "passed"){
				
				this.WriteDetailedLog("Allowing status change");
				
				//WriteToDebug("Prior status is not complete - allowing to set status");
				this.StatusSetInCurrentSession = true;
				
				this.RunTimeData.SuccessStatus = TranslateSingleStatusIntoSuccess(value);
				this.RunTimeData.CompletionStatus = TranslateSingleStatusIntoCompletion(value);
			}
			
		break;
			
		case "cmi.core.exit":
			this.WriteDetailedLog("Element is: exit");
			this.RunTimeData.Exit = value;
		break;	
			
		case "cmi.core.session_time":
			this.WriteDetailedLog("Element is: session time");
			this.RunTimeData.SessionTime = ConvertCmiTimeSpanToIso8601TimeSpan(value);
			this.WriteDetailedLog("Stored as: " + this.RunTimeData.SessionTime);
		break;	
			
		case "cmi.core.score.raw":
			this.WriteDetailedLog("Element is: score.raw");
			if (value === ""){value = null;}
			this.RunTimeData.ScoreRaw = value;
		break;	

		case "cmi.core.score.max":
			this.WriteDetailedLog("Element is: score.max");
			if (value === ""){value = null;}
			this.RunTimeData.ScoreMax = value;
		break;
			
		case "cmi.core.score.min":
			this.WriteDetailedLog("Element is: score.min");
			if (value === ""){value = null;}
			this.RunTimeData.ScoreMin = value;
		break;
										
		case "cmi.suspend_data":
			this.WriteDetailedLog("Element is: suspend data");
			this.RunTimeData.SuspendData = value;
		break;	
		
		case "cmi.objectives.n.id":
			
			this.WriteDetailedLog("Element is: objectives.id");
			
			//if we don't have an objective at this spot yet, create one
			if (this.RunTimeData.Objectives.length <= primaryIndex){
				this.WriteDetailedLog("Adding new objective at index " + primaryIndex);
				this.RunTimeData.AddObjective();
			}
					
			this.RunTimeData.Objectives[primaryIndex].Identifier = value;
			
		break;
			
		case "cmi.objectives.n.status":
			
			this.WriteDetailedLog("Element is: objectives.status");
			
			//if we don't have an objective at this spot yet, create one
			if (this.RunTimeData.Objectives.length <= primaryIndex){
				this.WriteDetailedLog("Adding new objective at index " + primaryIndex);
				this.RunTimeData.AddObjective();
			}

			this.RunTimeData.Objectives[primaryIndex].SuccessStatus = TranslateSingleStatusIntoSuccess(value);
			this.RunTimeData.Objectives[primaryIndex].CompletionStatus = TranslateSingleStatusIntoCompletion(value);
			
		break;
			
		case "cmi.objectives.n.score.raw":
			
			this.WriteDetailedLog("Element is: objectives.score.raw");
			
			//if we don't have an objective at this spot yet, create one
			if (this.RunTimeData.Objectives.length <= primaryIndex){
				this.WriteDetailedLog("Adding new objective at index " + primaryIndex);
				this.RunTimeData.AddObjective();
			}
			
			if (value === ""){value = null;}
			this.RunTimeData.Objectives[primaryIndex].ScoreRaw = value;
			
		break;
			
		case "cmi.objectives.n.score.min":
			
			this.WriteDetailedLog("Element is: objectives.score.min");
			
			//if we don't have an objective at this spot yet, create one
			if (this.RunTimeData.Objectives.length <= primaryIndex){
				this.WriteDetailedLog("Adding new objective at index " + primaryIndex);
				this.RunTimeData.AddObjective();
			}
			if (value === ""){value = null;}
			this.RunTimeData.Objectives[primaryIndex].ScoreMin = value;
			
		break;
			
		case "cmi.objectives.n.score.max":
			
			this.WriteDetailedLog("Element is: objectives.score.max");
			
			//if we don't have an objective at this spot yet, create one
			if (this.RunTimeData.Objectives.length <= primaryIndex){
				this.WriteDetailedLog("Adding new objective at index " + primaryIndex);
				this.RunTimeData.AddObjective();
			}
			if (value === ""){value = null;}
			this.RunTimeData.Objectives[primaryIndex].ScoreMax = value;
			
		break;								
		
		case "cmi.comments":
			this.WriteDetailedLog("Element is: comments, storing at position " + this.RunTimeData.Comments.length);
			
			var newComment = new ActivityRunTimeComment(null, null, null, null, null, null);
			
			newComment.SetCommentValue(value);
			
			this.RunTimeData.Comments[this.RunTimeData.Comments.length] = newComment;

		break;
			
		case "cmi.student_preference.audio":
			this.WriteDetailedLog("Element is: audio");
			this.RunTimeData.AudioLevel = value;
		break;
			
		case "cmi.student_preference.language":
			this.WriteDetailedLog("Element is: language");
			this.RunTimeData.LanguagePreference = value;
		break;

		case "cmi.student_preference.speed":
			this.WriteDetailedLog("Element is: speed");
			this.RunTimeData.DeliverySpeed = value;
		break;
			
		case "cmi.student_preference.text":
		this.WriteDetailedLog("Element is: text");
			this.RunTimeData.AudioCaptioning = value;
		break;
			
		case "cmi.interactions.n.id":
			this.WriteDetailedLog("Element is: interactions.id");
			
			//if we don't have an interaction at this spot yet, create one
			if (this.RunTimeData.Interactions.length <= primaryIndex){
				this.WriteDetailedLog("Adding new interaction at position " + primaryIndex);
				this.RunTimeData.AddInteraction();
			}
					
			this.RunTimeData.Interactions[primaryIndex].Id = value;
			
		break;
			
		case "cmi.interactions.n.objectives.n.id":
			this.WriteDetailedLog("Element is: interactions.objectives.id");
			
			//if we don't have an interaction at this spot yet, create one
			if (this.RunTimeData.Interactions.length <= primaryIndex){
				this.WriteDetailedLog("Adding new interaction at position " + primaryIndex);
				this.RunTimeData.AddInteraction();
			}
					
			this.RunTimeData.Interactions[primaryIndex].Objectives[secondaryIndex] = value;
			
		break;
			
		case "cmi.interactions.n.time":
			
			this.WriteDetailedLog("Element is: interactions.time");
			
			//if we don't have an interaction at this spot yet, create one
			if (this.RunTimeData.Interactions.length <= primaryIndex){
				this.WriteDetailedLog("Adding new interaction at position " + primaryIndex);
				this.RunTimeData.AddInteraction();
			}
					
			this.RunTimeData.Interactions[primaryIndex].Timestamp = ConvertCmiTimeToIso8601Time(value);
			
		break;
			
		case "cmi.interactions.n.type":
			this.WriteDetailedLog("Element is: interacitons.type");
			
			//if we don't have an interaction at this spot yet, create one
			if (this.RunTimeData.Interactions.length <= primaryIndex){
				this.WriteDetailedLog("Adding new interaction at position " + primaryIndex);
				this.RunTimeData.AddInteraction();
			}
					
			this.RunTimeData.Interactions[primaryIndex].Type = value;		
			
		break;
			
		case "cmi.interactions.n.correct_responses.n.pattern":
			this.WriteDetailedLog("Element is: interactions.correct_responses.pattern");
			
			//if we don't have an interaction at this spot yet, create one
			if (this.RunTimeData.Interactions.length <= primaryIndex){
				this.WriteDetailedLog("Adding new interaction at position " + primaryIndex);
				this.RunTimeData.AddInteraction();
			}
					
			this.RunTimeData.Interactions[primaryIndex].CorrectResponses[secondaryIndex] = value;
			
		break;
			
		case "cmi.interactions.n.weighting":
			this.WriteDetailedLog("Element is: interactions.weighting");
			
			//if we don't have an interaction at this spot yet, create one
			if (this.RunTimeData.Interactions.length <= primaryIndex){
				this.WriteDetailedLog("Adding new interaction at position " + primaryIndex);
				this.RunTimeData.AddInteraction();
			}
					
			this.RunTimeData.Interactions[primaryIndex].Weighting = value;	
			
		break;		
			
		case "cmi.interactions.n.student_response":
			this.WriteDetailedLog("Element is: interactions.student_response");
			
			//if we don't have an interaction at this spot yet, create one
			if (this.RunTimeData.Interactions.length <= primaryIndex){
				this.WriteDetailedLog("Adding new interaction at position " + primaryIndex);
				this.RunTimeData.AddInteraction();
			}
					
			this.RunTimeData.Interactions[primaryIndex].LearnerResponse = value;	
			
		break;
			
		case "cmi.interactions.n.result":
			this.WriteDetailedLog("Element is: interactions.result");
			
			//if we don't have an interaction at this spot yet, create one
			if (this.RunTimeData.Interactions.length <= primaryIndex){
				this.WriteDetailedLog("Adding new interaction at position " + primaryIndex);
				this.RunTimeData.AddInteraction();
			}
			
			//translate between 1.2 and 2004
			if (value == SCORM_WRONG){
				value = SCORM_INCORRECT;
			}
			
			this.RunTimeData.Interactions[primaryIndex].Result = value;
			
		break;
			
		case "cmi.interactions.n.latency":
			this.WriteDetailedLog("Element is: interactions.latency");
			
			//if we don't have an interaction at this spot yet, create one
			if (this.RunTimeData.Interactions.length <= primaryIndex){
				this.WriteDetailedLog("Adding new interaction at position " + primaryIndex);
				this.RunTimeData.AddInteraction();
			}
					
			this.RunTimeData.Interactions[primaryIndex].Latency = ConvertCmiTimeSpanToIso8601TimeSpan(value);
			
			break;	
			
		default:
		
			Debug.AssertError("Unrecognized data model element in StoreData");
			this.SetErrorState(SCORM_ERROR_GENERAL, "Setting the data element you requested is not supported although it is listed as being supported, please contact technical support.  Element-" + strElement);
			return false;

	}

	
	return true;
}






































function RunTimeApi_CheckForGetValueError(element, elementWithOutIndex, primaryIndex, secondaryIndex){
	
	//TODO: if a collection is passed with "n" instead of a number, will we return an error. this should never happen, but we should check for it anyway
	
	this.WriteDetailedLog("CheckForGetValueError (" + element + ", " + elementWithOutIndex + ", " + primaryIndex + ", " + secondaryIndex + ") ");
	
	/*
	Check for SCORM errors
		
		if not intialized
			return 301
		if we have the element in our list of SCORM data elements
			if we didn't implement the data element
				return 401
			if the element can't be read
				return 404
			else
				success condition
		else - element is not a SCORM data element
			if element ends in "_children"
				return 202
			if element ends in "_count"
				return 203
			else - we have no idea what this is
				return 201
	*/
	
	if (this.Initialized === false){
		this.SetErrorState(SCORM_ERROR_NOT_INITIALIZED, "GetValue called when not initialized. element-" + element);
		return false;
	}
	
	if (arySupportedElements[elementWithOutIndex] !== undefined && arySupportedElements[elementWithOutIndex] !== null ) {
		
		if ( ! arySupportedElements[elementWithOutIndex].Supported){
			this.SetErrorState(SCORM_ERROR_NOT_IMPLEMENTED, "The parameter '" + element + "' is not implemented.");
			return false;
		}

		if ( ! arySupportedElements[elementWithOutIndex].SupportsRead){
			this.SetErrorState(SCORM_ERROR_WRITE_ONLY, "The parameter '" + element + "' is write-only.");
			return false;
		}
				
	}
	else{

		if (elementWithOutIndex.search(/._children$/) > 0 ){
			
			strBaseElement = elementWithOutIndex.replace("._children", "");
			
			if (arySupportedElements[strBaseElement] !== undefined && arySupportedElements[element] !== null ){
				this.SetErrorState(SCORM_ERROR_NO_CHILDREN, "The parameter '" + element + "' does not support the _children keyword.");
				return false;				
			}
		}
		else if (elementWithOutIndex.search(/._count$/) > 0 ){
			
			strBaseElement = elementWithOutIndex.replace("._count", "");
			
			if (arySupportedElements[strBaseElement] !== undefined && arySupportedElements[element] !== null ){
				this.SetErrorState(SCORM_ERROR_NO_COUNT, "The parameter '" + element + "' does not support the _count keyword.");
				return false;				
			}
		}
		
		this.SetErrorState(SCORM_ERROR_INVALID_ARG, "The parameter '" + element + "' is not recognized.");
		return false;
		
	}
	
	this.WriteDetailedLog("Call is error free.")
	
	return true;
}


function RunTimeApi_RetrieveGetValueData(element, elementWithOutIndex, primaryIndex, secondaryIndex){
	
	this.WriteDetailedLog("RetrieveGetValueData (" + element + ", " + elementWithOutIndex + ", " + primaryIndex + ", " + secondaryIndex + ") ");
	
	var returnData = "";
	
	switch(elementWithOutIndex){
		
		case "cmi.core._children":
			this.WriteDetailedLog("Element is: core._children");
			returnData = SCORM_CORE_CHILDREN;
			break;
			
		case "cmi.core.student_id":
			this.WriteDetailedLog("Element is: student id");
			returnData = this.LearnerId;
			break;
			
		case "cmi.core.student_name":
			this.WriteDetailedLog("Element is: student name");
			returnData = this.LearnerName;
			break;	
									
		case "cmi.core.lesson_location":
			this.WriteDetailedLog("Element is: lesson location");
			returnData = this.RunTimeData.Location;
			break;
			
		case "cmi.core.credit":
			this.WriteDetailedLog("Element is: credit");
			returnData = this.RunTimeData.Credit;
			break;
			
		case "cmi.core.lesson_status":
			this.WriteDetailedLog("Element is: lesson status");
			
			//need to translate from the dual status representation of SCORM 2004 (our internal representation) to the single status of SCORM 1.2
			
			returnData = TranslateDualStausToSingleStatus(this.RunTimeData.CompletionStatus, this.RunTimeData.SuccessStatus);
			break;
			
		case "cmi.core.entry":
			this.WriteDetailedLog("Element is: entry");
			returnData = this.RunTimeData.Entry;
			break;
			
		case "cmi.core.score._children":
			this.WriteDetailedLog("Element is: score._children");
			returnData = SCORM_CORE_SCORE_CHILDREN;
			break;
		
		case "cmi.core.score.raw":
			this.WriteDetailedLog("Element is: score.raw");
			returnData = this.RunTimeData.ScoreRaw;
			break;
			
		case "cmi.core.score.max":
			this.WriteDetailedLog("Element is: score.max");
			returnData = this.RunTimeData.ScoreMax;
			break;
			
		case "cmi.core.score.min":
			this.WriteDetailedLog("Element is: score.min");
			returnData = this.RunTimeData.ScoreMin;
			break;
						
		case "cmi.core.total_time":
			this.WriteDetailedLog("Element is: total time");
			returnData = ConvertIso8601TimeSpanToCmiTimeSpan(this.RunTimeData.TotalTime);
			break;
			
		case "cmi.core.lesson_mode":
			this.WriteDetailedLog("Element is: lesson mode");
			returnData = this.RunTimeData.Mode;
			break;
			
		case "cmi.suspend_data":
			this.WriteDetailedLog("Element is: suspend data");
			returnData = this.RunTimeData.SuspendData;
			break;
			
		case "cmi.launch_data":
			this.WriteDetailedLog("Element is: launch data");
			returnData = this.LearningObject.DataFromLms;
			break;																								


		case "cmi.objectives._children":
			this.WriteDetailedLog("Element is: objectives._children");
			returnData = SCORM_OBJECTIVES_CHILDREN;
			break;

		case "cmi.objectives._count":
			this.WriteDetailedLog("Element is: objectives._count");
			returnData = this.RunTimeData.Objectives.length;
			break;		

		case "cmi.objectives.n.id":
		
			this.WriteDetailedLog("Element is: objectives.id");
			
			if (this.RunTimeData.Objectives[primaryIndex] === null || 
				this.RunTimeData.Objectives[primaryIndex] === undefined ||
				this.RunTimeData.Objectives[primaryIndex].Identifier === null){
				this.WriteDetailedLog("Objective element is undefined, returning empty string.");
				returnData = "";
			}
			else{
				returnData = this.RunTimeData.Objectives[primaryIndex].Identifier;
			}
			
			break;	

		case "cmi.objectives.n.status":
		
			this.WriteDetailedLog("Element is: objectives.status");
			
			if (this.RunTimeData.Objectives.length < (primaryIndex + 1)  ||
			    this.RunTimeData.Objectives[primaryIndex] === null || 
				this.RunTimeData.Objectives[primaryIndex] === undefined || 
				this.RunTimeData.Objectives[primaryIndex].CompletionStatus === null ||
				this.RunTimeData.Objectives[primaryIndex].SuccessStatus === null){

				this.WriteDetailedLog("Objective element is undefined, returning empty string.");
				returnData = "";
			}
			else{
				
				returnData = TranslateDualStausToSingleStatus(this.RunTimeData.Objectives[primaryIndex].CompletionStatus, 
														this.RunTimeData.Objectives[primaryIndex].SuccessStatus);
				
			}
			
			break;		
			
		case "cmi.objectives.n.score._children":
			this.WriteDetailedLog("Element is: objectives.score._children");
			returnData = SCORM_OBJECTIVES_SCORE_CHILDREN;
			break;	
									
		case "cmi.objectives.n.score.raw":
			
			this.WriteDetailedLog("Element is: objectives.score.raw");
			
			if (this.RunTimeData.Objectives.length < (primaryIndex + 1)  ||
			    this.RunTimeData.Objectives[primaryIndex] === null || 
				this.RunTimeData.Objectives[primaryIndex] === undefined || 
				this.RunTimeData.Objectives[primaryIndex].ScoreRaw === null){
				
				this.WriteDetailedLog("Objective element is undefined, returning empty string.");
				returnData = "";
			}
			else{
				returnData = this.RunTimeData.Objectives[primaryIndex].ScoreRaw;
			}
			
			break;
				
		case "cmi.objectives.n.score.max":
		
			this.WriteDetailedLog("Element is: objectives.score.max");

			if (this.RunTimeData.Objectives.length < (primaryIndex + 1)  ||
			    this.RunTimeData.Objectives[primaryIndex] === null || 
				this.RunTimeData.Objectives[primaryIndex] === undefined || 
				this.RunTimeData.Objectives[primaryIndex].ScoreMax === null){
				
				this.WriteDetailedLog("Objective element is undefined, returning empty string.");
				returnData = "";
			}
			else{

				returnData = this.RunTimeData.Objectives[primaryIndex].ScoreMax;
			}
			
			break;	
			
		case "cmi.objectives.n.score.min":
		
			this.WriteDetailedLog("Element is: objectives.score.min");
			
			if (this.RunTimeData.Objectives.length < (primaryIndex + 1)  ||
			    this.RunTimeData.Objectives[primaryIndex] === null || 
				this.RunTimeData.Objectives[primaryIndex] === undefined || 
				this.RunTimeData.Objectives[primaryIndex].ScoreMin === null){
				
				this.WriteDetailedLog("Objective element is undefined, returning empty string.");
				returnData = "";
			}
			else{
				returnData = this.RunTimeData.Objectives[primaryIndex].ScoreMin;
			}
			
			break;							
					


			
		case "cmi.comments":
			this.WriteDetailedLog("Element is: comments");
			returnData = this.JoinCommentsArray(this.RunTimeData.Comments);
			break;
			
		case "cmi.comments_from_lms":
			this.WriteDetailedLog("Element is: comments from lms");
			returnData = this.JoinCommentsArray(this.RunTimeData.CommentsFromLMS);
			break;
			
		case "cmi.student_data._children":
			this.WriteDetailedLog("Element is: student_data._children");
			returnData = SCORM_STUDENT_DATA_CHILDREN;
			break;
			
		case "cmi.student_data.mastery_score":
			this.WriteDetailedLog("Element is: mastery score");
			if (this.LearningObject.MasteryScore === null){
				returnData = "";
			}
			else{
				returnData = this.LearningObject.MasteryScore;
			}
			break;
			
		case "cmi.student_data.max_time_allowed":
			this.WriteDetailedLog("Element is: max time allowed");
			returnData = ConvertIso8601TimeSpanToCmiTimeSpan(this.LearningObject.MaxTimeAllowed);
			break;
			
		case "cmi.student_data.time_limit_action":
			this.WriteDetailedLog("Element is: time limit action");
			returnData = this.LearningObject.TimeLimitAction;
			break;
			
		case "cmi.student_preference._children":
			
			this.WriteDetailedLog("Element is: student_preference._children");
			returnData = SCORM_STUDENT_PREFERENCE_CHILDREN;
			break;
			
		case "cmi.student_preference.audio":

			this.WriteDetailedLog("Element is: audio");
			if (this.RunTimeData.AudioLevel === null){
				returnData = "";
			}
			else{
				returnData = Math.round(this.RunTimeData.AudioLevel);
			}
			break;
			
		case "cmi.student_preference.language":

			this.WriteDetailedLog("Element is: language");
			returnData = this.RunTimeData.LanguagePreference;
			break;
			
		case "cmi.student_preference.speed":

			this.WriteDetailedLog("Element is: speed");
			if (this.RunTimeData.DeliverySpeed === null){
				returnData = "";
			}
			else{
				returnData = Math.round(this.RunTimeData.DeliverySpeed);
			}
			break;
			
		case "cmi.student_preference.text":

			this.WriteDetailedLog("Element is: text");
			returnData = this.RunTimeData.AudioCaptioning;
			break;
			
		case "cmi.interactions._children":
			this.WriteDetailedLog("Element is: interactions._children");
			returnData = SCORM_INTERACTIONS_CHILDREN;
			break;
			
		case "cmi.interactions._count":
			this.WriteDetailedLog("Element is: interactions._count");
			returnData = this.RunTimeData.Interactions.length;
			break;	
			
		case "cmi.interactions.n.objectives._count":
	
			this.WriteDetailedLog("Element is: interactions.objectives._count");
			
			if (this.RunTimeData.Interactions.length < (primaryIndex + 1) ||
				this.RunTimeData.Interactions[primaryIndex] === null || 
				this.RunTimeData.Interactions[primaryIndex] === undefined){
				
				this.WriteDetailedLog("No interaction at " + primaryIndex + ", returning 0");
				returnData = 0;
			}
			else{
				returnData = this.RunTimeData.Interactions[primaryIndex].Objectives.length;
			}
			
			break;	
			
			
		case "cmi.interactions.n.correct_responses._count":
		
			this.WriteDetailedLog("Element is: interactions.correct_responses._count");
			
			if (this.RunTimeData.Interactions.length < (primaryIndex + 1) ||
				this.RunTimeData.Interactions[primaryIndex] === null || 
				this.RunTimeData.Interactions[primaryIndex] === undefined){
				
				this.WriteDetailedLog("No interaction at " + primaryIndex + ", returning 0");
				returnData = 0;
			}
			else{
				returnData = this.RunTimeData.Interactions[primaryIndex].CorrectResponses.length;
			}
			
			break;	
				
		default:
					
			//WriteToDebug("Element not matched");
			Debug.AssertError("An unsupported data model element slipped through GetValue error detection.");
			SetErrorInfo(SCORM_ERROR_GENERAL, "Getting the data element you requested is not supported although it is listed as being supported, please contact technical support.  Element-" + strElement);		

			returnData = "";
			break;
	}
	
	return returnData;
}


function RunTimeApi_CloseOutSession(){
	
	this.WriteDetailedLog("Close Out Session");
	
	var masteryScore = this.LearningObject.MasteryScore;
	
	this.WriteDetailedLog("Mode = " + this.RunTimeData.Mode);
	this.WriteDetailedLog("Credit = " + this.RunTimeData.Credit);
	this.WriteDetailedLog("CompletionStatus = " + this.RunTimeData.CompletionStatus);
	this.WriteDetailedLog("SuccessStatus = " + this.RunTimeData.SuccessStatus);
	this.WriteDetailedLog("MasteryScore = " + masteryScore);
	this.WriteDetailedLog("Score = " + this.RunTimeData.ScoreRaw);
	
	if (this.RunTimeData.Mode == SCORM_MODE_REVIEW){
		//mode is review so don't change
		this.WriteDetailedLog("Mode is review so don't change");
	}
	else if (this.RunTimeData.Mode == SCORM_MODE_BROWSE && this.RunTimeData.Credit == SCORM_CREDIT_NO){
		//don't save browsed mode, because it screws up our ability to resume from the last SCO in the 1.2 Test Suite
		//this.WriteDetailedLog("Setting completion status to browsed");
		//this.RunTimeData.CompletionStatus = SCORM_STATUS_BROWSED;
	}
	else if (this.RunTimeData.Credit == SCORM_CREDIT){
		
		this.WriteDetailedLog("Sco was taken for credit");
		
		if (this.RunTimeData.CompletionStatus == SCORM_STATUS_UNKNOWN){		//equilivant to not attempted
			
			this.WriteDetailedLog("Current status is not attempted so changing based on score");
			
			if (masteryScore === null || this.RunTimeData.ScoreRaw === null || this.RunTimeData.ScoreRaw === ""){
				this.WriteDetailedLog("No mastery score, setting to completed");
				this.RunTimeData.CompletionStatus = SCORM_STATUS_COMPLETED;
			}
			else{
				if (this.RunTimeData.ScoreRaw >= masteryScore){
					this.WriteDetailedLog("Score exceeds mastery, setting to passed");
					this.RunTimeData.SuccessStatus = SCORM_STATUS_PASSED;
					this.RunTimeData.CompletionStatus = SCORM_STATUS_COMPLETED;
				}
				else{
					this.WriteDetailedLog("Score less than mastery, setting to failed");
					this.RunTimeData.SuccessStatus = SCORM_STATUS_FAILED;
					this.RunTimeData.CompletionStatus = SCORM_STATUS_COMPLETED;
				}
			}
		}
		else{
			//TODO: improvement - add parameter here to determine if score status can override a set status
			
			this.WriteDetailedLog("Status has been set, checking to override to passed/failed based on score");
			
			if (masteryScore !== null && 
				masteryScore !== "" && 
				this.RunTimeData.ScoreRaw !== null && 
				this.RunTimeData.ScoreRaw !== "") {
			
				if (this.RunTimeData.ScoreRaw >= masteryScore){
					this.WriteDetailedLog("Score exceeds mastery, setting to passed");
					this.RunTimeData.SuccessStatus = SCORM_STATUS_PASSED;
					this.RunTimeData.CompletionStatus = SCORM_STATUS_COMPLETED;
				}
				else{
					this.WriteDetailedLog("Score less than mastery, setting to failed");
					this.RunTimeData.SuccessStatus = SCORM_STATUS_FAILED;
					this.RunTimeData.CompletionStatus = SCORM_STATUS_COMPLETED;
				}
			
			}
		}
	}
	
	//if there is a score, normalize it and put it in the scaled score data
	if (this.RunTimeData.ScoreRaw !== null && 
		this.RunTimeData.ScoreRaw !== ""){
		this.RunTimeData.ScoreScaled = NormalizeRawScore(this.RunTimeData.ScoreRaw, this.RunTimeData.ScoreMin, this.RunTimeData.ScoreMax);
	}
	
	//after completion, credit is always "no-credit" and lesson_mode is always "review"
	if (this.RunTimeData.CompletionStatus == SCORM_STATUS_COMPLETED){
		
		this.WriteDetailedLog("Sco is completed so resetting credit to no-credit and mode to review");
		
		this.RunTimeData.Credit = SCORM_CREDIT_NO;
		this.RunTimeData.Mode = SCORM_MODE_REVIEW;
	}
	
	if (this.RunTimeData.CompletionStatus == SCORM_STATUS_COMPLETED ||
		this.RunTimeData.Exit != SCORM_EXIT_SUSPEND){
		
		this.WriteDetailedLog("Next entry is normal");
		this.RunTimeData.Entry = SCORM_ENTRY_NORMAL;
	}
	else{
		this.WriteDetailedLog("Next entry is resume");
		this.RunTimeData.Entry = SCORM_ENTRY_RESUME;
	}

	if (this.RunTimeData.Exit == SCORM_EXIT_SUSPEND){
		this.Activity.SetSuspended(true);
	}
	else{
		this.Activity.SetSuspended(false);
	}
	
//TOTEST: reported time accumulation	
	
	//accumulate the session time into the total time
	var sessionTimeHundredths = ConvertIso8601TimeSpanToHundredths(this.RunTimeData.SessionTime);
	var previousTimeHundredths = ConvertIso8601TimeSpanToHundredths(this.RunTimeData.TotalTime);
	
	var totalTimeHundredths = sessionTimeHundredths + previousTimeHundredths;
	
	var totalTimeIso = ConvertHundredthsToIso8601TimeSpan(totalTimeHundredths);

	this.WriteDetailedLog("Session Time: " + this.RunTimeData.SessionTime + " (" + sessionTimeHundredths + " hundredths)");
	this.WriteDetailedLog("Previous Time: " + this.RunTimeData.TotalTime + " (" + previousTimeHundredths + " hundredths)");
	this.WriteDetailedLog("New Total Time: " + totalTimeIso + " (" + totalTimeHundredths + " hundredths)");
	
	this.RunTimeData.TotalTime = totalTimeIso;
	this.RunTimeData.SessionTime = "";

	this.AccumulateTotalTimeTracked();

	this.WriteDetailedLog("New Tracked Total Time: " + this.RunTimeData.TotalTimeTracked);		
}



function RunTimeApi_JoinCommentsArray(commentsArray){
	
	var commentsString = "";
	
	for (var i=0; i < commentsArray.length; i++){
		commentsString += commentsArray[i].GetCommentValue();
	}
	
	return commentsString;
}


function RunTimeApi_IsValidVocabElement(value, dataElement){
		
	var aryElements;
	var i;
	
	//check to see if this value is in the arrays we defined in Constants.js
	
	aryElements = aryVocabularies[dataElement];

	if (aryElements === undefined || aryElements === null){
		return false;
	}
	else{
	
		for (i=0; i < aryElements.length; i++){
			if (aryElements[i] == value){
				return true;
			}
		}

		return false;
	}
}


// This is a public function that exists mainly to get around a "bug" in IE where if you set this.TrackedStartTime directly
// with a new Date() object created in a different frame/window, you get an error.
function RunTimeApi_InitTrackedTimeStart() {
	this.TrackedStartDate = new Date();
}

function RunTimeApi_AccumulateTotalTrackedTime() {
	
	this.TrackedEndDate = new Date();
	var trackedSessionTimeHundredths = Math.round((this.TrackedEndDate - this.TrackedStartDate) / 10);
	var previousTrackedTimeHundredths = ConvertIso8601TimeSpanToHundredths(this.RunTimeData.TotalTimeTracked);
	
	var totalTrackedHundredths = trackedSessionTimeHundredths + previousTrackedTimeHundredths;
	
	this.RunTimeData.TotalTimeTracked = ConvertHundredthsToIso8601TimeSpan(totalTrackedHundredths);
}


