// Copyright 1998,1999 Macromedia, Inc. All rights reserved.

// Localization strings
var TRACK_system_not_found = "Tracking system not found.";

//Constructs an Interaction
function MM_interaction(theSelf,
		theJudgeOnSel, theAllowMultiSel, theAllThatApply, theUnknownIsCorrect,
		theDisabled, theTriesLimit, theTimeLimit,
		theTrackIntId, theTrackObjectiveId,
		theTrackQType,theTrackWeight, theKTrack,LMSstd) {
	// properties
	this.judgeOnSel = theJudgeOnSel;
	this.allowMultiSel = theAllowMultiSel;
	this.allThatApply = theAllThatApply;
	this.unknownIsCorrect = theUnknownIsCorrect;
	this.disabled = theDisabled;
	this.tries = 0;
	this.triesLimit = (theTriesLimit)?theTriesLimit:0;
	this.triesAtLimit = false;
	this.time = 0;
	this.timeLeft = 0;
	this.timeLimit = (theTimeLimit)?theTimeLimit:0;
	this.timeAtLimit = false;
	this.trackIntId = theTrackIntId;
	this.trackObjectiveId = theTrackObjectiveId;
	this.trackQType = theTrackQType;
	this.trackWeight = theTrackWeight;
	this.knowledgeTrack = theKTrack;
	this.totalElems = 0;
	this.possCorrect = 0;
	this.possIncorrect = 0;
	this.knownResponse = false;
	this.totalCorrect = 0;
	this.totalIncorrect = 0;
	this.correct = false;
	this.score = 0;
	if(!document.LMSstd || document.LMSstd=="")
		document.LMSstd = setStd(LMSstd);

	this._self = theSelf;
	this._timeStart = 0;
	this._timerID = 0;

	this.browserIsNS = (navigator.appName.indexOf('Netscape') != -1);
	this.browserIsIE = (navigator.appName.indexOf('Microsoft') != -1);
	this.osIsWindows = (navigator.appVersion.indexOf('Win') != -1);
	this.osIsMac = (navigator.appVersion.indexOf('Mac') != -1); // ????
	this.browserVersion = parseFloat(navigator.appVersion);

	this.e = new Array();
	this.b = new Array();

	// methods
	this.init = MM_intInit;
	this.reset = MM_intReset;
	this.resetElems = MM_intResetElems;
	this.enable = MM_intEnable;
	this.disable = MM_intDisable;
	this.setDisabled = MM_intSetDisabled;
	this.update = MM_intUpdate;
	this.add = MM_intAdd;
	this.setTries = MM_intSetTries;
	this.setTriesLimit = MM_intSetTriesLimit;
	this.setTime = MM_intSetTime;
	this.setTimeLimit = MM_intSetTimeLimit;
	this.track = MM_intTrack
	this.getTime = MM_intGetTime;
	this.am = MM_intAm;
	this.judge = MM_intJudge;
	this.resetActionMgr = MM_intResetActionMgr;
	this.setSegmNode = MM_intSetSegmNode;
	this.getSegmNode = MM_intGetSegmNode;
	this.setSegmDisabled = MM_intSetSegmDisabled;
	this.getSegmDisabled = MM_intGetSegmDisabled;
}

function setStd(LMSstd){
	var retVal="";
	if(LMSstd=="AICC")
		retVal = new AICC();
	else if(LMSstd == "SCORM1.2")
		retVal = new SCORM();
	else if(LMSstd == "SCORM2.0")
		retVal = new SCORM();
	return retVal;
}
//Calls the element init funtions if they exist, and then does a reset
function MM_intInit() {
	var i,j,localPC;

	with (this) {
		// init elems, and set totalElems, possCorrect, possIncorrect;
		totalElems = 0;
		possCorrect = 0;
		possIncorrect = 0;
		for (i in e) if (i != 'length') {
			if (e[i].init != null) e[i].init();
			totalElems++;
			localPC = 0;
			for (j in e[i].c) if (j != 'length' && e[i].c[j].isCorrect != null)
				(e[i].c[j].isCorrect) ? localPC++ : possIncorrect++;
			if (e[i]._singleChoice != null && localPC > 1)
				localPC = 1;
			possCorrect += localPC;
		}
		reset();
		if(knowledgeTrack){ 
			if (document.LMSstd.type=="AICC") {
				var frm = findcmiframe(null);
				if (frm == null) {
					installcmi(window); //layers in NS
					if (!CMIIsPresent()) {
						var cmi = cmiinit(window);
						if (cmi) CMIInitialize();
						else if (!window.trackwarning) {
							alert(TRACK_system_not_found);
							window.trackwarning = true;
						} 
					} 
				} 
				else {
					if (window.CMIInitialize == null) frm.installcmi(window);
					if (window.CMIInitialize != null) CMIInitialize();
					if (!CMIIsPresent() && !window.trackwarnings) {
						alert(TRACK_system_not_found);
						window.trackwarning = true; } }
			} 
			else{
				var	API = getAPI(0);
				if(!API){
					alert(TRACK_system_not_found);
					window.trackwarning = true;
				}
				else{
					CMIInitialize();
				}
			} 
		}
	}
	window["'"+this._self+"'"] = this._self; //redeclare global on window in case inserted in layer
}

//Called to reset the interaction
function MM_intReset() {
	with (this) {
		tries = 0;
		triesAtLimit = false;
		_timeStart = Math.floor((new Date()).getTime()/1000);
		if (_timerID) clearTimeout(_timerID);
		if (!disabled && timeLimit) _timerID = setTimeout(_self+".judge()",(timeLimit+1)*1000);
		time = 0;
		timeLeft = timeLimit;
		timeAtLimit = false;

		resetActionMgr();
		resetElems();
		update(true);
	}
}

//Calls the reset for the individual elements
function MM_intResetElems() {
	with (this) {
		for (var i in e) if (i != 'length')
			if (e[i].reset != null)  e[i].reset();
		update(true);
	}
}

//Enables the interaction
function MM_intEnable() {
	if (this.disabled) with (this) {
		_timeStart = Math.floor((new Date()).getTime()/1000) - time;
		if (timeLimit)
			_timerID = setTimeout(_self+".judge()",Math.max(0,(timeLimit-time)+1)*1000);
		disabled = false;
		update(true);
		for (var i in e) if (i != 'length')
			if (e[i].enable != null)  e[i].enable();
	}
}

//Disables the interaction
function MM_intDisable() {
	if (!this.disabled) with (this) {
		update(true);  // update 'time'
		if (_timerID) clearTimeout(_timerID);  // clear the timer
		disabled = true;
		for (var i in e) if (i != 'length')
			if (e[i].disable != null)  e[i].disable();
	}
}

//Calls the approppriate disable or enable function
function MM_intSetDisabled(theDisabled) {
	if (theDisabled) this.disable();
	else this.enable();
}

//Update the interaction state
// Note: tries will be updated by the judge method, and time will
//       be updated by both this method and the judge method.
function MM_intUpdate(noJudge) {
	if (!this.disabled) with (this) {
		knownResponse = false;
		totalCorrect = 0;
		totalIncorrect = 0;
		correct = false;
		score = 0;
		for (var i in e) if (i != 'length') {
			for (var j in e[i].c) if (j != 'length') {
				if (e[i].c[j].selected) {
					knownResponse = true;
					score += e[i].c[j].score;
					if (e[i].c[j].isCorrect != null)
						(e[i].c[j].isCorrect) ? totalCorrect++ : totalIncorrect++;
				} } }
		if (!knownResponse) correct = unknownIsCorrect;
		else if (totalIncorrect != 0) correct = false;
		else if (totalCorrect == 0) correct = null; // not judged
		else correct = (!allThatApply || totalCorrect >= possCorrect);

		time = Math.floor((new Date()).getTime()/1000) - _timeStart;
		if (timeLimit && !timeAtLimit) {
			timeLeft = Math.max(0, timeLimit - time);
			timeAtLimit = (time > timeLimit);
		}

		if (judgeOnSel && !noJudge)  judge();
	}
}

function MM_intSetTries(theTries) {
	with (this) {
		tries = theTries;
		triesAtLimit = (triesLimit) ? (tries >= triesLimit) : false;
	}
}

function MM_intSetTriesLimit(theTriesLimit) {
	with (this) {
		triesLimit = theTriesLimit;
		triesAtLimit = (triesLimit) ? (tries >= triesLimit) : false;
	}
}

function MM_intSetTime(theTime) {
	with (this) {
		time = Math.max(0, theTime);
		_timeStart = Math.floor((new Date()).getTime()/1000) - time;
		timeLeft = (timeLimit) ? Math.max(0, timeLimit - time) : timeLimit;
		timeAtLimit = (timeLimit) ? (time > timeLimit) : false;
		if (_timerID) clearTimeout(_timerID);  // clear the timer
		if (!disabled && timeLimit)
			_timerID = setTimeout(_self+".judge()",Math.max(0,(timeLimit-time)+1)*1000);
	}
}

function MM_intSetTimeLimit(theTimeLimit) {
	with (this) {
		if (!disabled)
			time = Math.floor((new Date()).getTime()/1000) - _timeStart;
		timeLimit = theTimeLimit;
		timeLeft = (timeLimit) ? Math.max(0, timeLimit - time) : timeLimit;
		timeAtLimit = (timeLimit) ? (time > timeLimit) : false;
		if (_timerID) clearTimeout(_timerID);  // clear the timer
		if (!disabled && timeLimit)
			_timerID = setTimeout(_self+".judge()",Math.max(0,(timeLimit-time)+1)*1000);
	}
}

function MM_intAdd(theType, A, B, C, D, E, F, G, H, I, J, K, L, M) {
	var theObj = eval("new MM_" + theType + "(" + this._self + ", A, B, C, D, E, F, G, H, I, J, K, L, M)");
	if (theObj._isChoice != null)
		this.e[A].c[B] = theObj;
	else
		this.e[A] = theObj;
}

function MM_intTrack() {
	var  aDt= new Date();
	var curHr=aDt.getHours()+'', curMin=aDt.getMinutes()+'', curSec=aDt.getSeconds()+'';
	var curDay=aDt.getDate()+'', curMonth=aDt.getMonth()+1+'', curYear=aDt.getYear(), dmy;
	var lat = Math.floor(aDt.getTime()/1000) - this._timeStart;
	var x=3600;
	var y=60;
	var hrs=Math.round(lat/x - lat%x/x)+'';
	var min=Math.round((lat-hrs*x)/y-(lat-hrs*x)%y/y)+'';
	var sec=Math.round(lat-hrs*x-min*y)+'';
	var sRes,cRes,res,aName,bName,isC,isNC,isSel,iType;

	if (curYear < 1900) curYear += 1900;
	if (curDay.toString().length==1) curDay = '0'+curDay;
	if (curMonth.toString().length==1) curMonth = '0'+curMonth;
	dmy = curDay+"/"+curMonth+"/"+curYear;

	sRes=cRes=res=aName=bName=""

		with (this) {  
			if (trackQType.length==0) return;
			iType=trackQType.charAt(0).toLowerCase();
			if (allThatApply&&possCorrect>1) sRes=cRes='{';

			for (var i in e) if (i != 'length') {
				for (var j in e[i].c) if (j != 'length') {
					isC=e[i].c[j].isCorrect;
					isSel=e[i].c[j].selected;
					if (iType=='m') {
						aName=e[i].c[j]._elem._name;
						bName=e[i].c[j]._target._name;
						if (isSel) sRes+=aName+'.'+bName+',';
						if (isC==1) cRes+=aName+'.'+bName+',';
						else if(isC==null)cRes=sRes;
					}
					else if(iType=='s'){
						aName=e[i].c[j]._elem._name;
						bName=e[i].c[j]._target._name;
						if (isC) cRes+=bName+',';
						if (isSel) sRes+=bName+',';
					}
					else if (iType=='f') {
						aName=e[i].value;
						bName=MM_textDeencrypt(e[i].c[j].expectedValue);
						if (isC) cRes+=bName+',';
						 sRes=aName+',';
					}
					else if ((iType=='c')||(iType=='t')||(iType=='l')) {
						if (e[i]._trkObj!=null) {
							aName=e[i].c[j].expectedValue;
							x=aName.indexOf(':');
							aName=aName.substring(0,x)+'-'+aName.substring(x+1,aName.length)
								bName=e[i].value
						}
						else if (e[i]._tick!=null) {
							aName=e[i].c[j].expectedValue;
							if (isSel) bName = aName;
						}
						else if (e[i]._stateMask!=null && totalElems==1) {
							aName=e[i].expectedValue==1?"true":"false";
							bName=e[i].value;
							isSel=true;
						}
						else
							aName=bName=e[i].c[j]._name;

						if (isC) cRes+=aName+',';
						if (isSel) sRes+=bName+',';
					}
					else {
						aName=bName='';
						if (isC) cRes+=',';
						if (isSel) sRes+=',';
					}

					isNC=(isC==false)&&isSel;
					isC=isC&&isSel;
					if (possCorrect>1)  res=res+(isC?'c,':isNC?'w,':isSel?'n,':'');
					else if (isSel) res = isC?'c':isNC?'w':'n';
				}
			}

			if (sRes.charAt(sRes.length-1)==',') sRes=sRes.substring(0,sRes.length-1);
			if (cRes.charAt(cRes.length-1)==',') cRes=cRes.substring(0,cRes.length-1);
			if (res.charAt(res.length-1)==',') res=res.substring(0,res.length-1);
			if (allThatApply&&possCorrect>1) {
				sRes=sRes+'}';
				cRes=cRes+'}';
			}
			if(trackQType == 'l')cRes='';
			if (window.CMIIsPresent && CMIIsPresent()) {
				if (hrs.toString().length==1) hrs = '0'+hrs;
				if (min.toString().length==1) min = '0'+min;
				if (sec.toString().length==1) sec = '0'+sec;
				if (curHr.toString().length==1) curHr = '0'+curHr;
				if (curMin.toString().length==1) curMin = '0'+curMin;
				if (curSec.toString().length==1) curSec = '0'+curSec;
				CMIAddInteraction(dmy, curHr+":"+curMin+":"+curSec, 
						trackIntId, trackObjectiveId, trackQType, 
						cRes, sRes, correct, trackWeight, hrs+":"+min+":"+sec,knownResponse);
			}
		}  
}


function MM_intGetTime() { 
	var date = new Date();
	this.time = (date.getTime()/1000 - this._timeStart)*1000;
	return this.time/1000;
}


//Finds any object in either browser using recursion.
//Only pass the first argument, the name of the object to find.
//Returns a pointer the object if found, else an empty string.
//  MM_intFindObject('bar') returns the object
//  document.layers['foo'].document.layers['bar']

function MM_intFindObject(objName,  parentObj) {
	var i,tempObj="",found=false,curObj = "";
	var NS = (navigator.appName.indexOf("Netscape") != -1);
	var allNodes = document.getElementsByTagName("*");
	curObj = allNodes[objName];
	//if (!NS && document.all) curObj = document.all[objName]; //IE4
	if (!curObj)
	{
		parentObj = (parentObj != null)? parentObj.document : document;
		curObj = parentObj.getElementById(objName);
		if(navigator.appVersion.indexOf("Safari") != -1)      // Safari gives a different NodeList than other browsers
		{
		curObj1 = document.getElementsByName(objName);
		for(var j = 0; j< curObj1.length; j++)
			curObj = curObj1.item(j);
		}
	} // remove this as you remove following comment
	/*
	//alert("parent object  " + parentObj);
	alert("my object " + parentObj[objName])
	if (parentObj[objName] != null) curObj = parentObj[objName]; //at top level

	else { //if in form
	if (parentObj.forms) for (i=0; i<parentObj.forms.length; i++) {  //search level for form object
	if (parentObj.forms[i][objName]) {
	curObj = parentObj.forms[i][objName];
	found = true; break;
	} }
	if (!found && NS && parentObj.layers && parentObj.layers.length > 0) {
	parentObj = parentObj.layers;
	for (i=0; i<parentObj.length; i++) { //else search for child layers
	tempObj = MM_intFindObject(objName,parentObj[i]); //recurse
	if (tempObj) { curObj = tempObj; break;} //if found, done
	} } } }
	*/
//  alert("returning current object  " + curObj);
// alert("current object ID "  + curObj.id);
return curObj;
}

//Called from within conditions to check document properties
function MM_getDocProp(theName, theProp, theType) {
	var theObj = MM_intFindObject(theName);
	if (theObj) return eval('theObj.' + theProp);
	else return null;
}


//*********  ACTION MGR METHODS  *********

function MM_intJudge(treeRoot,curRoot,level) {
	var i=0,theNode,retVal=true,firstCond = true;
	level = (curRoot == null)?0:level+1;
	if (!level) {
		curRoot = this; //set tree to highest level
		if (this.disabled) retVal=false;
		else {
			this.time = Math.floor((new Date()).getTime()/1000) - this._timeStart;
			if (this.timeLimit && !this.timeAtLimit) {
				this.timeLeft = Math.max(0, this.timeLimit - this.time);
				this.timeAtLimit = (this.time > this.timeLimit);
			}
			this.tries++;
			if (this.triesLimit)
				this.triesAtLimit = (this.tries >= this.triesLimit);
		} } else if (treeRoot.curNode != null && curRoot == treeRoot.curNode) //if at curNode
		i = treeRoot.curIndex;  //offset by index
		for (i; (retVal && i<curRoot.b.length); i++) {
			theNode = curRoot.b[i];
			if (this.disabled) retVal=false;
			else if (theNode.disabled) continue;
			else if (theNode.type == "segm") { //SEGMENT
				curNode = (theNode.curNode)?theNode.curNode:theNode;
				if (curNode != 'done')
					retVal = MM_intJudge(theNode,curNode,level);   //start from that node
				else if (!level) continue;
			} else if (theNode.type == 'cond') { //CONDITION
				if (eval(theNode.data)) {
					if (!treeRoot.data) treeRoot.curNode = 'done';  //if not "auto-reset tree", set curNode
					retVal = MM_intJudge(treeRoot,theNode,level);
					if (level) break;
				} else if (firstCond) {
					firstCond = false;
					if (!treeRoot.data) {treeRoot.curNode = curRoot;  treeRoot.curIndex = i;}
				}
			} else if (theNode.type == 'actn') { //ACTION
				if (!treeRoot.data) //not auto-reset
					if (i<(curRoot.b.length-1)) { //not last node
						treeRoot.curNode = curRoot;  treeRoot.curIndex = i+1; //set curNode to next node
					} else treeRoot.curNode = 'done'; //last node, clear curNode
				if (theNode.data == 'stop') retVal=false; //STOP
				else eval(theNode.data);
			} }
			if (!level && curRoot!=null && curRoot.knowledgeTrack) curRoot.track();
			return retVal
}


//Disables or enables ActionMgr segments. If no segment passed,
//disables *all* segments. Returns false if segment not found.

function MM_intSetSegmDisabled(segmName,disable) {
	var i, retVal=false;
	for (i=0; i<this.b.length; i++) //search for segment
		if (!segmName || this.b[i].name==segmName) {
			this.b[i].disabled=disable; retVal = true;
			if (segmName) break;
		}
	return retVal
}


//Returns the disabled flag from an ActionMgr segment.
//Returns null if segment not found.

function MM_intGetSegmDisabled(segmName) {
	var i, retVal=null;
	for (i=0; i<this.b.length; i++) //search for segment
		if (this.b[i].name==segmName) {
			retVal = this.b[i].disabled; break;
		}
	return retVal
}


function MM_intResetActionMgr(segmName) {
	var i;
	if (segmName != null) MM_intTreeSetCurNode(segmName,null,this); //clear single curNode
	else for (i=0;i<this.b.length;i++) this.b[i].curNode = null; //clear all curNodes
}


function MM_intSetSegmNode(segmName,condName) {
	var i, curRoot=null;
	if (!condName) this.resetActionMgr(segmName);
	else {
		for (i=0; i<this.b.length; i++) //search for segment, set as curRoot
			if (this.b[i].name == segmName) { curRoot = this.b[i]; break; }
		MM_intTreeSetCurNode(condName,null,curRoot,curRoot);
	}
}


function MM_intTreeSetCurNode(nodeName,newVal,treeRoot,curRoot) {
	var i,theNode;
	if (curRoot==null) curRoot = treeRoot; //if first time, use treeRoot
	for (i=0; i<curRoot.b.length; i++) {
		theNode = curRoot.b[i];
		if (theNode.type == "segm") { //SEGMENT
			if (theNode.name == nodeName) theNode.curNode  = newVal;
			else MM_intTreeSetCurNode(nodeName,newVal,theNode,theNode);
		} else if (theNode.type == 'cond') { //CONDITION
			if (theNode.name == nodeName) {  //if name found
				treeRoot.curNode = curRoot;  treeRoot.curIndex = i;
				break;
			} else MM_intTreeSetCurNode(nodeName,newVal,treeRoot,theNode);
		} }
}


//Returns the current node for the given Segment, returned as:
// top of segment    - "" (empty string)
// middle of segment - condition name
// end of segment    - "done"

function MM_intGetSegmNode(segmName) {
	var i, curNode='';
	for (i=0; i<this.b.length; i++) //search for segment
		if (this.b[i].name == segmName) { 
			curNode = this.b[i].curNode;
			if (curNode==null) curNode = "";
			else if (curNode!="done") curNode = curNode.b[this.b[i].curIndex].name;
			break; 
		}
	return curNode
}



//*********  ACTION MGR NODE CLASS  *********

function MM_intNode(theType,theName,theData) {
	this.type = (theType)?theType:'';
	this.name = (theName)?theName:'';
	this.data = (theData)?theData:'';
	this.b = new Array();
}


//Create Action Manager (tree)

function MM_intAm(theType,theName,theData) {
	if (theType == "segm") {
		if (!this.tParent) { //initialize system
			this.tParent = new Array(); //tree parent nodes
			this.tIndex = new Array();  //child indexes
			this.tIndex[0] = -1;        //set index for root depth
		}
		this.tLevel = 0;            //reset level to 0
		this.tIndex[this.tLevel]++; //increment the index
		this.b[this.tIndex[this.tLevel]] = new MM_intNode("segm", theName,theData); //create tree
		this.tParent[this.tLevel] = this.b[this.tIndex[this.tLevel]]; //put this tree root at top level
		this.tIndex[this.tLevel+1] = -1;                              //reset child level index

	} else if (theType == "actn") {
		var parentNode = this.tParent[this.tLevel];    //get parent
		var levelIndex = ++this.tIndex[this.tLevel+1]; //get index
		parentNode.b[levelIndex] = new MM_intNode('actn',theName,theData); //add action

	} else if (theType == "cond") {
		var parentNode = this.tParent[this.tLevel];    //get parent
		var levelIndex = ++this.tIndex[this.tLevel+1]; //get index
		parentNode.b[levelIndex] = new MM_intNode('cond',theName,theData); //add condition

		this.tLevel++; //go down a level
		this.tParent[this.tLevel] = parentNode.b[levelIndex]; //save this node as parent
		this.tIndex[this.tLevel+1] = -1;                      //reset child level index

	} else if (theType == "end") {
		this.tLevel--;  //go up a level
	}
}

