updateProgress();

utils = null;


function StringBuffer(string) { 
	this.buffer = [];
	if (string) this.buffer.push(string); 
} 

StringBuffer.prototype.append = function append(string) { 
	this.buffer.push(string); 
	return this; 
}; 

StringBuffer.prototype.toString = function toString() { 
	return this.buffer.join(""); 
}; 
 
 
function Constants() {

    this.GAPMARKER = "type=\"gap\"";
    this.MC = "MC";
    this.GAPTEXT = "GAPTEXT";
    this.DND = "DND";

}

function Point(x, y) {
	this.x = x;
	this.y = y;
}

function Line(x1, y1, x2, y2) {
	this.x1 = x1;
	this.y1 = y1;
	this.x2 = x2;
	this.y2 = y2;
}

function Rectangle(x1, y1, width, height) {
	this.x1 = x1;
	this.y1 = y1;
	this.width = width;
	this.height = height;
	this.x2 = x1 + width;
	this.y2 = y1 + height;
	
	this.getPointN = getPointN;
	this.getPointE = getPointE;
	this.getPointS = getPointS;
	this.getPointW = getPointW;
	
	function getPointN() {
		return new Point(this.x1 + ((this.x2 - this.x1) / 2), this.y1);
	}
	
	function getPointE() {
		return new Point(this.x2, this.y1 + ((this.y2 - this.y1) / 2));
	}
	
	function getPointS() {
		return new Point(this.x1 + ((this.x2 - this.x1) / 2), this.y2);
	}
	
	function getPointW() {
		return new Point(this.x1, this.y1 + ((this.y2 - this.y1) / 2), this.y1);
	}
	
}

updateProgress();



function QuickQuestionPoolManager(metaXmlDocument) {

	this.createQuestionPoolOnXML = createQuestionPoolOnXML;
	var xParser = new XParser(metaXmlDocument);
	
	function createQuestionPoolOnXML() {
		
		//alert(metaXmlDocument.xml);
		
		var meta = xParser.evaluate("meta")[0];
		var result = xParser.evaluate("meta//structure[@type='testunit']");
	   	if (result.length == 0) return metaXmlDocument;
	   	   	  	    
	    // for each testunit 
	    for (var i = 0; i < result.length; i++) {
	    
	    	var myTestunit = result[i];
	    	var mixQuestions = myTestunit.getAttribute("mixQuestions") == "true";
	       	var mixAnswers = myTestunit.getAttribute("mixAnswers") == "true";
	       	var ownQuestions = myTestunit.getAttribute("ownQuestions") == "true";
	       	
	       	// EXTERNAL QUESTIONPOOL
	       	if (!ownQuestions) {
	       		
	       		// remove all structures in testunit
	       		var followingStructures = xParser.evaluate("structure[@type!='step']", myTestunit);
	       		for (var ii = 0; ii < followingStructures.length; ii++) {
	       			myTestunit.removeChild(followingStructures[ii]);
	       		}
	       		
	       		// collect external structures
	       		var siblingStructures = xParser.evaluate("preceding-sibling::structure[@type!='testunit']", myTestunit);
				for (var ii = 0; ii < siblingStructures.length; ii++) {
					
					// make a copy of the structure
					var myNode = siblingStructures[ii].cloneNode(true);
					modifyIDs(myNode, myTestunit);
					
					// remove normal-steps
					var mySteps = xParser.evaluate("//step[@type='Normal_Step']", myNode);
					for (var iii = 0; iii < mySteps.length; iii++) {
						var stepStructure = mySteps[iii].parentNode;
						var parent = stepStructure.parentNode;
						parent.removeChild(stepStructure);
					}
															
					myTestunit.appendChild(myNode);
					
				}
				
				
				// DEBUG ONLY !!!!
				/*
				var siblingStructures = xParser.evaluate("preceding-sibling::structure[@type!='testunit']", myTestunit);
				for (var ii = 0; ii < siblingStructures.length; ii++) {
					meta.removeChild(siblingStructures[ii]);
					
				}
				*/
				
				
				
				
				
	       	}
	       	
	       	
	       	// QUESTIONPOOL
	       	modifyStructure(myTestunit, mixQuestions, mixAnswers);
	       	
	       	// "flatten" the testunit
	       	
	       	// temporarily store all steps
	       	var normalSteps = new Array();
	       	var scoreSteps = new Array();
	       	var remainingSteps = new Array();
	       	
	       	var allStepStructures = xParser.evaluate(".//structure[@type='step']", myTestunit);
	       	
	       	for (var ii = 0; ii < allStepStructures.length; ii++) {
	       		allStepStructures[ii].setAttribute("mixAnswers", mixAnswers + "");
	       		allStepStructures[ii].getElementsByTagName("step")[0].setAttribute("mixAnswers", mixAnswers + "");
	       		
	       		var stepType = allStepStructures[ii].getElementsByTagName("step")[0].getAttribute("type");
	       		if (stepType == "Normal_Step") {
	       			normalSteps.push(allStepStructures[ii].cloneNode(true));
	       		}
	       		else if (stepType == "Score") {
	       			scoreSteps.push(allStepStructures[ii].cloneNode(true));
	       		}
	       		else {
	       			remainingSteps.push(allStepStructures[ii].cloneNode(true));
	       		}
	       	}
	       	
	       	/*	       	
			if (mixQuestions) {
				remainingSteps = utils.getRandomElements(remainingSteps, remainingSteps.length);
			}
			*/
			       	
	       	
	       	
	       	// remove all structures
	       	var allStructureChildren = xParser.evaluate("structure", myTestunit);
	       	for (var ii = 0; ii < allStructureChildren.length; ii++) {
	       		myTestunit.removeChild(allStructureChildren[ii]);
	       	}
	       
	       	// append normal steps
	       	for (var ii = 0; ii < normalSteps.length; ii++) {
	       		normalSteps[ii].setAttribute("structureId", myTestunit.getAttribute("id"));
	       		myTestunit.appendChild(normalSteps[ii]);
	       	}
	       	
	       	// append remaining steps
	       	for (var ii = 0; ii < remainingSteps.length; ii++) {
	       		remainingSteps[ii].setAttribute("structureId", myTestunit.getAttribute("id"));
	       		remainingSteps[ii].setAttribute("level", parseInt(myTestunit.getAttribute("level")) + 1);
	       		myTestunit.appendChild(remainingSteps[ii]);
	       	}
	       	
	       	
	       	
	       	modifyStructure(myTestunit, mixQuestions, mixAnswers);
	       	
	       	var allExercises = xParser.evaluate("structure/step[@type!='Normal_Step']", myTestunit);
	       	
	       	
	       	// append score
	       	for (var ii = 0; ii < scoreSteps.length; ii++) {
	       		scoreSteps[ii].setAttribute("structureId", myTestunit.getAttribute("id"));
	       		
	       		//var stepresult = xParser.evaluate("preceding-sibling::step[@type!='Normal_Step']", scoreStep);
	        	
	        	for (var n = 0; n < allExercises.length; n++) {
	        		var id = allExercises[n].getAttribute("id");
	        		var evaluateElement = metaXmlDocument.createElement("evaluate");
	        		var evaluateElementValue = metaXmlDocument.createTextNode(id);
	        		evaluateElement.appendChild(evaluateElementValue);
	        		scoreSteps[ii].getElementsByTagName("step")[0].appendChild(evaluateElement);
	        	}
	       		myTestunit.appendChild(scoreSteps[ii]);
	       	}
	       	
	       	
	       	
	       	
	       	
	       	/*
	       	for (var ii = 0; ii < childrenStructures.length; ii++) {
				modifyStructure(childrenStructures[ii], mixQuestions, mixAnswers);
			}
			*/
			
			
			/*	       	
	       	var scoreSteps = xParser.evaluate("structure/step[@type='Score']", myTestunit);
	    	if (scoreSteps.length == 0) {
	    		alert("no score was defined in testunit");
	    		return metaXmlDocument;
	    	}
	    	
	    	
	    	var scoreSteps = xParser.evaluate("structure/step[@type='Score']", myTestunit);
	    	if (scoreSteps.length == 0) {
	    		alert("no score was defined in testunit");
	    		return metaXmlDocument;
	    	}
	    	*/
	       	// process children-structures
			/*
			var childrenStructures = xParser.evaluate("structure", myTestunit);
			
			var newChildrenStructures = new Array();
			for (var ii = 0; ii < childrenStructures.length; ii++) {
				var myChildrenStructure = modifyStructure(childrenStructures[ii], mixQuestions, mixAnswers);
				newChildrenStructures.push(myChildrenStructure);
				
			}
			childrenStructures = newChildrenStructures;
			
			var scoreSteps = xParser.evaluate("structure/step[@type='Score']", myTestunit);
	    	if (scoreSteps.length == 0) {
	    		alert("no score was defined in testunit");
	    		return metaXmlDocument;
	    	}
	    	var scoreStep = scoreSteps[0];
	    	myTestunit.removeChild(scoreStep.parentNode);
	    	
	    	var welcomeSteps = xParser.evaluate("structure/step[@type='Normal_Step']", myTestunit);
	    	for (var ii = 0; ii < welcomeSteps.length; ii++) {
	    		myTestunit.removeChild(welcomeSteps[ii].parentNode);
	    	}
	    	
	    		    	
	    	// modifiy testunit and mix it if necessary
	       	myTestunit = modifyStructure(myTestunit, mixQuestions, mixAnswers);
	       	
	       	var allTestunitSteps = xParser.evaluate("structure/step", myTestunit);
	       	for (var ii = 0; ii < allTestunitSteps.length; ii++) {
	       		myTestunit.removeChild(allTestunitSteps[ii].parentNode);
	       	}
	       	
	       	var allsteps = xParser.evaluate("structure//step[@type!='Score']", myTestunit);
	    	
	    	if (mixQuestions) {
	    		allsteps = utils.getRandomElements(allsteps, allsteps.length);
	    	}
	    	
	    	for (var ii = 0; ii < welcomeSteps.length; ii++) {
	    		myTestunit.appendChild(welcomeSteps[ii].parentNode);
	    	}
	    	
	    	for (var ii = 0; ii < allTestunitSteps.length; ii++) {
	    		myTestunit.appendChild(allTestunitSteps[ii].parentNode);
	    	}
	    	
	    	// add remaining steps
	    	
	    	for (var ii = 0; ii < allsteps.length; ii++) {
				allsteps[ii].setAttribute("structureId", myTestunit.getAttribute("id"));
				myTestunit.appendChild(allsteps[ii]);
			}
	    	
	    	// remove all children structures
	    	for (var ii = 0; ii < childrenStructures.length; ii++) {
				result[i].removeChild(childrenStructures[ii]);
			}
			
			
	    	// add score-step if exists
	    	if (scoreStep) {
	    		myTestunit.appendChild(scoreStep);
	    		var stepresult = xParser.evaluate("preceding-sibling::step[@type!='Normal_Step']", scoreStep);
	        	
	        	for (var n = 0; n < stepresult.length; n++) {
	        		var id = stepresult[n].getAttribute("id");
	        		var evaluateElement = metaXmlDocument.createElement("evaluate");
	        		var evaluateElementValue = metaXmlDocument.createTextNode(id);
	        		evaluateElement.appendChild(evaluateElementValue);
	        		scoreStep.appendChild(evaluateElement);
	        	}
		    	
	    	}*/
	    }
	    //alert(metaXmlDocument.xml);
	    return metaXmlDocument;
	}
	
	
	
	function modifyIDs(structure, parent) {
		var id = structure.getAttribute("id") + "_copy";
		structure.setAttribute("id", id);
		structure.setAttribute("structureId", parent.getAttribute("id"));
		
		var steps = xParser.evaluate("step", structure);
		if (steps[0]) {
			steps[0].setAttribute("id", id);
			steps[0].setAttribute("structureId", id);
		}
		
		var childrenStructures = xParser.evaluate("structure", structure);
		
		for (var i = 0; i < childrenStructures.length; i++) {
			//childrenStructures[i].setAttribute("numberOfQuestions", structure.getAttribute("numberOfQuestions") + "");
			//childrenStructures[i].setAttribute("structureId", id);
			modifyIDs(childrenStructures[i], structure);
		}
	}
	
	function modifyStructure(structure, mixQuestions, mixAnswers) {
		
		var childrenStructures = xParser.evaluate("structure[@type!='step']", structure);
		for (var i = 0; i < childrenStructures.length; i++) {
			modifyStructure(childrenStructures[i], mixQuestions, mixAnswers);
		}
		
		// remove steps to exclude
		var stepsToExclude = xParser.evaluate("structure/step[@excluded='true']", structure);
		for (var i = 0; i < stepsToExclude.length; i++) {
			structure.removeChild(stepsToExclude[i].parentNode);
		}
		
		// determine number of questions
		var numberOfQuestionsToAsk = parseInt(structure.getAttribute("numberOfQuestions"));
		var numSteps = xParser.evaluate("structure[@type='step']", structure).length;
			
		//var numberOfQuestions = 0;
		if (!numberOfQuestionsToAsk || numberOfQuestionsToAsk == 0) {
			if (numSteps > 0) {
				numberOfQuestionsToAsk = numSteps;
			}
		}
		//alert("numberOfQuestionsToAsk: " + numberOfQuestionsToAsk);
		
		// temporarily store normal steps
		var normalStepsArray = new Array();
		var normalSteps =  xParser.evaluate("structure/step[@type='Normal_Step']", structure);
		for (var i = 0; i < normalSteps.length; i++) {
			normalStepsArray.push(normalSteps[i].parentNode.cloneNode(true));
			structure.removeChild(normalSteps[i].parentNode);
		}
		
		// temporarily store score steps
		var scoreStepsArray = new Array();
		var scoreSteps =  xParser.evaluate("structure/step[@type='Score']", structure);
		for (var i = 0; i < scoreSteps.length; i++) {
			scoreStepsArray.push(scoreSteps[i].parentNode.cloneNode(true));
			structure.removeChild(scoreSteps[i].parentNode);
		}
				
		// remove from parent and mix the remaining steps if specified
		var remainingStepsArray = new Array();
		var remainingSteps =  xParser.evaluate("structure/step", structure);
		for (var i = 0; i < remainingSteps.length; i++) {
			var myNode = remainingSteps[i].parentNode.cloneNode(true);
			remainingStepsArray.push(myNode);
			structure.removeChild(remainingSteps[i].parentNode);
		}
					
		if (mixQuestions) {
			remainingStepsArray = utils.getRandomElements(remainingStepsArray, remainingSteps.length);
		}
		
		// remove non-mandatory steps according to "numberOfQuestionsToAsk"
		var mandatorySteps =  xParser.evaluate("structure/step[@mandatory='true']", structure);
		var numElementsToRemove = remainingStepsArray.length - mandatorySteps.length;
		numElementsToRemove -= numberOfQuestionsToAsk;
		if (numElementsToRemove < 0) numElementsToRemove = 0;
				
		//alert("remove " + numElementsToRemove + " from " + remainingStepsArray.length);
		
		var removed = 0;
		for (var i = 0; i < remainingStepsArray.length; i++) {
			//alert(remainingStepsArray[i].firstChild);
			var mandatory = remainingStepsArray[i].getElementsByTagName("step")[0].getAttribute("mandatory") == "true";
			if (!mandatory) {
				if (removed < numElementsToRemove) {
					remainingStepsArray[i] = null;
					//alert("removed one");
					removed++;
				}
			}
		}
		
		// append normal steps
		for (var i = 0; i < normalStepsArray.length; i++) {
			structure.appendChild(normalStepsArray[i]);
		}
		
		// append exercise steps
		for (var i = 0; i < remainingStepsArray.length; i++) {
			if (remainingStepsArray[i]) {
				structure.appendChild(remainingStepsArray[i]);
			}
		}
			
		// append score steps
		for (var i = 0; i < scoreStepsArray.length; i++) {
			structure.appendChild(scoreStepsArray[i]);
		}
		
		//alert("children length: " + structure.childNodes.length);
		
		
		
	
	}
	
	function testIt() {
		alert("test");
	}
	
	
	
	// this function determines if an element exists in an array
	function arrayContains(array, element) {
		var contains = false;
		for (var i = 0; i < array.length; i++) {
			if (array[i].getAttribute("id") == element.getAttribute("id")) {
				contains = true;
				break;
			}
		}
		return contains;
	}
}

updateProgress();




// this function is a poor and uncomplete JavaScript-Port of java.util.Vector
function Vector(array) {
	
	var myArray = array;
	
	this.add = add;
	this.getRowForElement = getRowForElement;
	this.remove = remove;
	
	
	// ADD AN ELEMENT
	function add(element) {
		myArray.push(element);
	};
	
	// GET ROW FOR ELEMENT
	function getRowForElement(element) {
		var row = -1;
		for (var i = 0; i < myArray.length; i++) {
			if (myArray[i] == element) {
				row = i;
				break;
			}
		}
		return row;
	};
	
	// REMOVE AN ELEMENT
	function remove(row) {
		if (row > myArray.length - 1) return;
		var tempArray = new Array();
		var n = 0;
		for (var i = 0; i < myArray.length; i++) {
			if (i != row) {
				tempArray[n] = myArray[i];
				n++;
			}
		}
		
		myArray = tempArray;
	};
	
	// GET SIZE
	this.size = function() {
		return myArray.length;
	};
	
	this.get = function(row) {
		if (row > myArray.length - 1) return null;
		return myArray[row];
	};
}

function Background(image, type) {
    this.image = image;
    this.type = type;
}

function Color(red, green, blue, alpha) {
    this.red = red;
    this.green = green;
    this.blue = blue;
    this.alpha = alpha;
    
    this.getHexValue = getHexValue;
    
    function getHexValue() {
        var redH = parseInt(this.red).toString(16);
        var greenH = parseInt(this.green).toString(16);
        var blueH = parseInt(this.blue).toString(16);

	    if (redH.length == 1) redH = "0" + redH;
	    if (greenH.length == 1) greenH = "0" + greenH;
	    if (blueH.length == 1) blueH = "0" + blueH;

        return "#" + redH + greenH + blueH;
    }

}

updateProgress();

function AudioPlayer() {
    
    this.play = play;
    this.stop = stop;
    this.setAudioLoadProgressMaximum = setAudioLoadProgressMaximum;
    this.setAudioLoadProgress = setAudioLoadProgress;
    this.audioFinished = audioFinished;
    
    function setAudioLoadProgressMaximum(duration) {

	}
	function setAudioLoadProgress(position) {

	}
	function audioFinished() {
	}
	
	function createAudioEmbed(filename) {
		var html = "<embed src=\"components/audioplayer.swf?audiofile=" + filename + "&refreshrate=10&isie=1\" quality=high width=\"1\" height=\"1\" id=\"audioplayer\" name=\"audioplayer\" swLiveConnect=\"true\" type=\"application/x-shockwave-flash\" pluginspage=\"http://www.macromedia.com/go/getflashplayer\"></embed>";
		return html;
	}
	
	function play() {
		if (!engine.isInAudioMode() || !engine.getCurrentAudio()) return;
		var filename = engine.getCurrentAudio().value;
		if (filename == "") return;
	    
		var soundlayer = document.getElementById("soundlayer");
	    	    
	    if (soundlayer) {
	    	var html = createAudioEmbed(filename);
	    	soundlayer.innerHTML = html;
	    }
  	}
    
    function stop() {
		var soundlayer = document.getElementById("soundlayer");
		if (soundlayer) {
			soundlayer.innerHTML = "";
		}
	}
}

updateProgress();

function Structure(id, title, position, type, level, parentId, backwardEnabled, timeLimit, timeLimitAlert, hideNavigationTree, mixQuestions, mixAnswers, numberOfQuestions, visible) {
	
	this.id = id;
	this.title = title;
	this.position = position;
	this.type = type;
	this.parentId = parentId;
	this.visited = false;
	this.level = level;
	
	this.backwardEnabled = backwardEnabled;
	this.timeLimit = timeLimit;
	this.timeLimitAlert = timeLimitAlert;
	this.hideNavigationTree = hideNavigationTree;
	this.mixQuestions = mixQuestions;
	this.mixAnswers = mixAnswers;
	this.numberOfQuestions = numberOfQuestions;
	
	this.visible = visible;
	
	this.addStep = addStep;
	this.getSteps = getSteps;
	this.setSteps = setSteps;
	this.getVisibleSteps = getVisibleSteps;
	
	var steps = new Array();
	var structures = new Array();
	this.addStructure = addStructure;
	this.getStructures = getStructures;
	this.getVisibleStructures = getVisibleStructures;
	this.getAncestors = getAncestors;
	
	
	function addStructure(structure) {
		//alert("add " + structure.title + " to " + this.title);
		structures.push(structure);
	}
	
	function getStructures() {
		return structures;
	}
	
	function getVisibleStructures(type) {
		var visibleStructures = new Array();
		var push = false;
		for (var i = 0; i < structures.length; i++) {
			push = false;
			if (structures[i].visible) {
				if (type) {
					if (structures[i].type == type) {
						push = true
					}
				}
				else {
					push = true;	
				}
			}
			if (push) {
				visibleStructures.push(structures[i]);
			}
		}
		return visibleStructures;
	}
	
	
	function getAncestors() {
		var tempParentId = parentId;
		var ancestors = new Array();
		var ancestor = null;
		
		while (true) {
			ancestor = database.getStructureById(tempParentId);
			if (ancestor != null) {
				ancestors.push(ancestor);
				tempParentId = ancestor.parentId;
			}
			else {
				break;
			}
		}
		
		ancestors.push(database.getStructureById("root"));
		return ancestors;
	}
	
	function addStep(step) {
	    steps.push(step);
	}
	
	function getSteps() {
	    return steps;
	}
	
	function getVisibleSteps() {
		var visibleSteps = new Array();
		for (var i = 0; i < steps.length; i++) {
			if (!steps[i].hidden) {
				visibleSteps.push(steps[i]);
			}
		}
		return visibleSteps;
	}
	
	function setSteps(_steps) {
		steps = _steps;
	}
}

function CTStep(id, type, title, structureId, mandatory, excluded, hidden, tries, mixAnswers) {
    this.id = id;
    this.type = type;
	this.title = title;
	this.structureId = structureId;
	this.mandatory = mandatory;
	this.excluded = excluded;
	this.hidden = hidden;
	this.tries = tries;
	this.mixAnswers = mixAnswers;
	this.attempt = 0;
	this.singleStep = false;
	
	this.loaded = false;
	this.load = load;
	this.update = update;
	
	this.setBackground = setBackground;
	this.getBackground = getBackground;
	
	this.getMaxWidth = getMaxWidth;
	this.getMaxHeight = getMaxHeight;
	this.getMinLeft = getMinLeft;
	this.getMinTop = getMinTop;
	
	this.addContent = addContent;
	this.getContentById = getContentById;
	this.getContents = getContents;
	this.hasContents = hasContents;
	this.getAllContents = getAllContents;
	this.getContentsByClassname = getContentsByClassname;
	this.getMediaContents = getMediaContents;
	this.addEvaluation = addEvaluation;
	this.getEvaluations = getEvaluations;
	this.visited = false;
	this.isAnExercise = isAnExercise;
	this.getAncestors = getAncestors;
	this.addEvent = addEvent;
	this.hideFeedback = hideFeedback;
	
	this.isFeedbackEnabled = isFeedbackEnabled;
	
	var xml = null;
	var contents = new Array();
	var evaluations = new Array();
	var background = null;
	var isExercise = false;
	var feedbackEnabled = false;
	
	var SINGLE_CHOICE_TYPE = "Single_Choice";
	var MULTIPLE_CHOICE_TYPE = "Multiple_Choice";
	var DRAG_DROP_TYPE = "Drag_Drop";
	var GAPTEXT_TYPE = "Gap_Text";
	
	this.events = new Array();
	
	function addEvent(event) {
		this.events.push(event);
	}
	
	function hideFeedback() {
		var feedbacks = getContentsByClassname("Feedback");
		for (var i = 0; i < feedbacks.length; i++) {
			feedbacks[i].visible = false;
			feedbacks[i].update();
		}
	}

	function isAnExercise() {
		return (this.type == SINGLE_CHOICE_TYPE || 
			this.type == MULTIPLE_CHOICE_TYPE || 
			this.type == DRAG_DROP_TYPE ||
			this.type == GAPTEXT_TYPE);
	}
	
	function getAncestors() {
		var ancestors = new Array();
		var parent = database.getStructureById(this.structureId);
		var anyElementHidden = false;
		while (parent) {
			ancestors.push(parent);
			if (!parent.visible) {
				return new Array();
			}
			parent = database.getStructureById(parent.parentId);
		}
		ancestors = ancestors.reverse();
		return ancestors;
	}
	
	function getMinLeft() {
		var minLeftEdge = Number.MAX_VALUE;
		var contents = getContents();
		for (var i = 0; i < contents.length; i++) {
			var left = contents[i].left;
			if (left < minLeftEdge) minLeftEdge = left;
		}
		return minLeftEdge;
	}
	
	function getMinTop() {
		var minTopEdge = Number.MAX_VALUE;
		var contents = getContents();
		for (var i = 0; i < contents.length; i++) {
			var top = contents[i].top;
			if (top < minTopEdge) minTopEdge = top;
		}
		return minTopEdge;
	}
	
	function getMaxWidth() {
		var maxRightEdge = 0;
		var contents = getContents();
		for (var i = 0; i < contents.length; i++) {
			var width = contents[i].width;
			var left = contents[i].left;
			var rightEdge = parseInt(left) + parseInt(width);
			//alert(rightEdge);
			if (rightEdge > maxRightEdge) maxRightEdge = rightEdge;
		}
		return maxRightEdge;
	}
	
	function getMaxHeight() {
		var maxBottomEdge = 0;
		var contents = getContents();
		for (var i = 0; i < contents.length; i++) {
			var height = contents[i].height;
			var top = contents[i].top;
			var bottomEdge = parseInt(top) + parseInt(height);
			if (bottomEdge > maxBottomEdge) maxBottomEdge = bottomEdge;
		}
		return maxBottomEdge;
	}
	
	function addEvaluation(id) {
		evaluations.push(id);
	}
	
	function getEvaluations() {
		return evaluations;
	}
	
	function load() {
		// determine step-id to load
		var idToLoad = id;
		var loadCopy = false;
		/*
		if this step was copied by the questionpool it does not exist in the filesystem. 
		We truncate the filename and load the original step.xml
		*/
		var a = idToLoad.indexOf("_copy"); 
		if (a != -1) {
			idToLoad = idToLoad.substring(0, a);
			loadCopy = true;
		}
		
		// now load the xml
		var xml = utils.getXmlDocument("data/step" + idToLoad + ".xml");
		
		rootNode = xml.getElementsByTagName("step")[0];
		
		// and set the new id (*_copy) if this was a copied step
		if (loadCopy) {
			rootNode.setAttribute("id", id);
		}	
		
	    var xParser = new XParser(xml);
	    
	    rootNode = xml.getElementsByTagName("step")[0];
	    
	    var backgroundImage = xParser.evaluateNodeValue("background", rootNode);
	    var backgroundType = xParser.evaluateAttribute("background", "type", rootNode);
	    var background = new Background(backgroundImage, backgroundType);
	    setBackground(background);  
	    
	    utils.attachEvents(rootNode, this, xParser); 
	    
	    var contentNodes = xParser.evaluate("content", rootNode);
	    contents = new Array();
	    utils.attachContents(this, contentNodes, xParser);
	    
	    this.loaded = true; 
	}
	
	function update() {
		for (var i = 0; i < contents.length; i++) {
			contents[i].update();
		}
	
	}
	
	function setBackground(_background) {
	    background = _background;
	}
	
	function getBackground() {
	    return background;
	}
	
	function isFeedbackEnabled() {
		return feedbackEnabled;
	}
		    
	function addContent(content) {
	    if (!isExercise) {
	        isExercise = determineExercise(content);
	    }
	    if (!feedbackEnabled) {
	        feedbackEnabled = determineFeedback(content);
	    }
	    content.step = this;
	    contents.push(content);
	}
	
	
		
	
	
	
	function determineExercise(content) {
	    return (type == "Multiple_Choice" || type == "Single_Choice" || type == "Drag_Drop");
	}
	
	function determineFeedback(content) {
	    var enabled = false;
		if (content instanceof Feedback) {
			var feedbackRanges = content.getFeedbackRanges();
			if (feedbackRanges.length > 0) {
		        enabled = true;
		    }
		}
	    return enabled;
	}
	
	
	function getContentById(id) {
	    var myContent = null;
	    var contentsLength = contents.length;
	    for (var i = 0; i < contentsLength; i++) {
	        if (contents[i].id == id) {
	            myContent = contents[i];
	            break;
	        }
			if (contents[i] instanceof Container) {
	        	myContent = contents[i].getContentById(id);
		        if (myContent != null) {
		            break;
		        }
			}
	    }
	    return myContent;
	}
	
	function getContentsByClassname(classname) {
	    var myContents = new Array();
	    var allContents = getAllContents();
	    var contentsLength = allContents.length;
	    
	    for (var i = 0; i < contentsLength; i++) {
	        var push = false;
	        if (classname == "Feedback" && allContents[i] instanceof Feedback) {
	            push = true;
	        }
	        else if (classname == "Answer" && allContents[i] instanceof Answer) {
	            push = true;
	        }
	        else if (classname == "Content" && allContents[i] instanceof Content) {
	            push = true;
	        }
	        else if (classname == "Container" && allContents[i] instanceof Container) {
	            push = true;
	        }
	        else if (classname == "Text" && allContents[i] instanceof Text) {
	            push = true;
	        }
	        else if (classname == "Media" && allContents[i] instanceof Media) {
	            push = true;
	        }
	        else if (classname == "ActionButton" && allContents[i] instanceof ActionButton) {
	            push = true;
	        }
	        else if (classname == "Checkbox" && allContents[i] instanceof Checkbox && !(allContents[i] instanceof ActionButton)) {
	            push = true;
	        }
	        else if (classname == "FeedbackRange" && allContents[i] instanceof FeedbackRange) {
	            push = true;
	        }
	        else if (classname == "ToggleButton" && allContents[i] instanceof ToggleButton) {
	            push = true;
	        }
	        if (push) {
	            myContents.push(allContents[i]);
	        }
	    }
	    
	    return myContents;
	}
	
	
	
	function getMediaContents() {
		var allContents = getAllContents();
		var mediaContents = new Array();
		for (var i = 0; i < allContents.length; i++) {
			var allContent = allContents[i];
			if (allContent instanceof Media) {
				mediaContents.push(allContent);
			}
		}
		return mediaContents;
	}
	
	function getAllContents() {
	    var myContents = new Array();
	    var contentsLength = contents.length;
	    for (var i = 0; i < contentsLength; i++) {
	        myContents.push(contents[i]);
	        if (contents[i] instanceof Container) {
		        var innerContents = contents[i].getAllContents();
		        for (var ii = 0; ii < innerContents.length; ii++) {
		        	myContents.push(innerContents[ii]);
		       	}
		    }
	    }
	    return myContents;
	}
	
	function getContents() {
	    return contents;
	}
	
	function hasContents() {
	    return contents.length > 0;
	}
	
}

updateProgress();

function Hotspot(shape, event, target, type, href, coords) {
    this.shape = shape;
    this.event = event;
    this.target = target;
    this.type = type;
    this.href = href;
    
    if (shape == "circle") {
        var coordsArray = coords.split(",");
        
        var x1 = parseInt(coordsArray[0]);
        var y1 = parseInt(coordsArray[1]);
        var x2 = parseInt(coordsArray[2]);
        var y2 = parseInt(coordsArray[3]);
        
        var width = x2 - x1;
        var height = y2 - y1;
        
        this.coords = (utils.ellipseToPoints(x1, y1, width, height, 60)).toString();
        this.shape = "poly";
    }
    else {
        this.coords = coords;
    }
}

function ListpageEntry(id, key, value, selected) {
	this.id = id;
	this.key = key;
	value = utils.convertImageTags(value);
	value = utils.convertLinks(value);
	this.value = value;
	this.selected = selected;
}

updateProgress();

function ExerciseManager() {

    this.checkAttempt = checkAttempt;
    this.hideFeedback = hideFeedback;
    this.updateGapValue = updateGapValue;
    this.dissolveExercise = dissolveExercise;
    this.repeatExercise = repeatExercise;
    this.drawRelationLines = drawRelationLines;
    this.calculateScore = calculateScore;
    this.mixAnswers = mixAnswers;
    this.shutdown = shutdown;
    this.currentMaxPoints = 0;
    this.currentPoints = 0;
    
    
    function getRelationLine(rect1, rect2) {
		var point1 = null;
		var point2 = null;
		var line = null;
		
		if (rect1.x2 < rect1.x1) {
			point1 = rect1.getPointE();
			point2 = rect2.getPointW();
		}
		else if (rect2.x2 < rect1.x1) {
			point1 = rect1.getPointW();
			point2 = rect2.getPointE();
		}
		else if (rect1.x2 >= rect2.x1 && rect1.x1 < rect2.x2) {
			if (rect1.y1 > rect2.y2) {
				point1 = rect1.getPointN();
				point2 = rect2.getPointS();
			}
			else {
				point1 = rect1.getPointS();
				point2 = rect2.getPointN();
			}
		}
		else if (rect1.x2 <= rect2.x1 && rect2.x1 > rect1.x2) {
			point1 = rect1.getPointE();
			point2 = rect2.getPointW();
		}
	
		line = new Line(point1.x, point1.y, point2.x, point2.y);
		return line;
		
	}
        
    function drawRelationLines(currentStep, index) {
    	var layer = "content";
    	if (currentStep && currentStep.type == "Drag_Drop") {
    		layer = "protocolContents" + index;
    	}
    	if (!currentStep) currentStep = engine.getCurrentStep();
    	
    	var myGraphics = new jsGraphics(layer);
    	
    	myGraphics.setStroke(RELATION_LINE_STROKE);
    	myGraphics.setColor(RELATION_LINE_COLOR);

    	var answers = currentStep.getContentsByClassname("Answer");
    	for (var i = 0; i < answers.length; i++) {
    		if (answers[i].isSource && !answers[i].correct) {
    			var correctAnswer = database.getContentById(answers[i].dropTargetId);	
    			if (correctAnswer) {
    				var rect1 = new Rectangle(parseInt(answers[i].left), parseInt(answers[i].top), parseInt(answers[i].width), parseInt(answers[i].height));
    				var rect2 = new Rectangle(parseInt(correctAnswer.left), parseInt(correctAnswer.top), parseInt(correctAnswer.width), parseInt(correctAnswer.height));
    				
    				var line = getRelationLine(rect1, rect2);
					myGraphics.drawLine(line.x1, line.y1, line.x2, line.y2);
    				
    			}
    		}
    	}
    	myGraphics.paint();
    }
     
    function repeatExercise() {
    	var myStep = engine.getCurrentStep();
       	myStep.loaded = false;
        engine.updatePage();
    }
    
    function dissolveExercise() {
		engine.setFeedbackMode(true);
		engine.updateCurrentStep();
		
		var currentStep = engine.getCurrentStep();
		if (currentStep.type == "Drag_Drop" && engine.isInFeedbackMode()) {
	    	var answers = currentStep.getContentsByClassname("Answer");
	    	for (var i = 0; i < answers.length; i++) {
	    		answers[i].reset();
	    	}
	    	drawRelationLines();
	    }
	    
		engine.setFeedbackMode(false);
    }
    
    function hideFeedbackButton() {
    	
    	var currentStep = engine.getCurrentStep();
		var feedbacks = currentStep.getContentsByClassname("Feedback");
		var feedbackId = "";
		if (feedbacks.length > 0) {
			feedbackId = feedbacks[0].id;
		}
		var buttons = currentStep.getContentsByClassname("ActionButton");
		for (var i = 0; i < buttons.length; i++) {
			var events = buttons[i].events;
			if (events.length > 0) {
				var action = buttons[i].events[0].action;
				var objectid = buttons[i].events[0].objectId;
				if (action == "Trigger" && objectid == feedbackId) {
					if (buttons[i].divRef) {
						buttons[i].divRef.style.visibility = "hidden";
					}
					break;
				}
			}
		}
		
	}
    
    function updateGapValue(value) {
	    var id = value.substring(0, value.indexOf("|"));
	    var index = value.substring(value.indexOf("|") + 1);
	    var currentStep = engine.getCurrentStep();
	    var content = currentStep.getContentById(id);
	    var gapField = document.getElementById("gap_" + value);
	    var gapValue = gapField.value;
	    content.userValues[index] = gapValue;
	}
	
	function hideFeedback() {
	}
	
    function checkAttempt(feedback) {
        
        var currentStep = engine.getCurrentStep();
	    var attempt = currentStep.attempt;
	    var tries = currentStep.tries;
	    
	    if (tries == 0) return;
	    
	    var feedbackRange = feedback.getCurrentFeedbackRange();
        
        if (feedbackRange == null) {
            return;
        }
        
        var feedbackRangeContents = feedbackRange.contents;
                
       	// make dissolve-Button visible
        var dissolveButton = null;
        var repeatButton = null;
        for (var i = 0; i < feedbackRangeContents.length; i++) {
            if (feedbackRangeContents[i] instanceof ActionButton) {
            	var events = feedbackRangeContents[i].events;
            	for (var ii = 0; ii < events.length; ii++) {
            		if (events[ii].action == "Dissolve") {
            			dissolveButton = feedbackRangeContents[i];
            		}
            		if (events[ii].action == "Repeat") {
            			repeatButton = feedbackRangeContents[i];
            		}
            	}
            }
        }
        
                  
        if (dissolveButton != null) {
        	if (attempt == tries - 1) {
        		dissolveButton.visible = true;
        		dissolveButton.update();
        	}
        	else {
        		dissolveButton.visible = false;
        		dissolveButton.update();
        	}
        }
        
        if (repeatButton != null) {
        	if (attempt == tries - 1) {
        		repeatButton.visible = false;
        		repeatButton.update();
        	}
        	else {
        		repeatButton.visible = true;
        		repeatButton.update();
        	}
        }
        
        currentStep.attempt++;
        if (attempt >= tries - 1) {
        	currentStep.attempt = 0;
        }
       
	}
	
	function hideAllFeedbackRanges(feedback) {
	    var feedbackRanges = feedback.getFeedbackRanges();
	    for (var i = 0; i < feedbackRanges.length; i++) {
	        var feedbackRangeContents = feedbackRanges[i].contents;
	        for (var ii = 0; ii < feedbackRangeContents.length; ii++) {
                feedbackRangeContents[ii].visible = false;
                feedbackRangeContents[ii].update();   
            }
        }
	}
	
	function calculateScore(step) {
	    var type = step.type;
	    var points = 0;
	    var maxpoints = 0;
	    
	    var stepsToEvaluate = new Array();
	    
	    if (type == "Score") {
	    	var evaluations = step.getEvaluations();
	    	if (evaluations.length == 0) {
	    		var structure = database.getStructureById(step.structureId);
	    		var tempStepsToEvaluate = structure.getSteps();
	    		for (var i = 0; i < tempStepsToEvaluate.length; i++) {
	    			if (tempStepsToEvaluate[i] != step) {
	    				if (tempStepsToEvaluate[i].isAnExercise()) {
	    					step.addEvaluation(tempStepsToEvaluate[i].id);
	    				}
	    			}
	    			else {
	    				break;
	    			}	
	    		}
	    		evaluations = step.getEvaluations();
	    	}
	    	
	    	if (evaluations.length > 0) {
		    	for (var i = 0; i < evaluations.length; i++) {
		    		stepsToEvaluate[stepsToEvaluate.length] = database.getStepById(evaluations[i]);
		    	}
		    }
		}
	    else {
	    	stepsToEvaluate[stepsToEvaluate.length] = step;
	    }
	    
	    for (var n = 0; n < stepsToEvaluate.length; n++) {
	        
		    var contents = stepsToEvaluate[n].getContents();
		    var exerciseType = determineExerciseType(stepsToEvaluate[n]);
	    	if (exerciseType == "MC") {
	    	    for (var i = 0; i < contents.length; i++) {
	    	        if (contents[i] instanceof Answer) {
	    	            maxpoints += parseInt(contents[i].points);
	    	            
	    	            if (contents[i].state == "active" && contents[i].correct) {
	    	                points += parseInt(contents[i].points);
	    	            }
	    	            if (contents[i].state == "default" && !contents[i].correct) {
	    	                points += parseInt(contents[i].points);
	    	            }
	    	            if (contents[i].state == "default" && contents[i].correct) {
	    	                points -= parseInt(contents[i].points);
	    	            }
	    	            if (contents[i].state == "active" && !contents[i].correct) {
	    	                points -= parseInt(contents[i].points);
	    	            }
	    	        }
	    	    }
		    }
		    else if (exerciseType == "DND") {
		        
		        for (var i = 0; i < contents.length; i++) {
	    	        if (contents[i] instanceof Answer) {
	    	            if (contents[i].isSource) {
	    	            	maxpoints += parseInt(contents[i].points);
	    	                if (contents[i].boundObject) {
	    	                	if (contents[i].boundObject.id == contents[i].dropTargetId) {
	    	                        points += parseInt(contents[i].points);
	    	                    }
	    	                }
	    	                else {
	    	                	if (contents[i].dropTargetId == null) {
	    	                		points += parseInt(contents[i].points);
	    	                	}
	    	                }
	    	            }
	    	        }
	    	    }    
		    }
		    
		    else if (exerciseType == "GAPTEXT") {
		        for (var i = 0; i < contents.length; i++) {
	    	        if (contents[i] instanceof Text) {
	    	            if (contents[i].gaptext) {
	    	                var gapText = contents[i];
	    	                for (var ii = 0; ii < gapText.userValues.length; ii++) {
	    	                    maxpoints += gapText.gaps[ii].points;
	    	                    var userWord = gapText.userValues[ii].toLowerCase();
	    	                    var wordsArray = gapText.gaps[ii].words;
	    	                    for (var m = 0; m < wordsArray.length; m++) {
	    	                        if (wordsArray[m].toLowerCase() == userWord) {
	    	                            points += gapText.gaps[ii].points;
	    	                            break;
	    	                        }
	    	                    }
	    	                }
	    	            }
	    	        }
	    	    }     
		    }
		    
	    }
	    
	    if (points < 0) points = 0;
	    if (maxpoints == 0) maxpoints = 1;
	    
	    this.currentMaxPoints = maxpoints;
	    this.currentPoints = points;
	    var percent = Math.round(points / maxpoints * 100);
	    return percent;	
	}
	
	
	
	function determineExerciseType(step) {
	    var type = null;
	    var contents = step.getContents();
	    
	    // check for D+D
	    for (var i = 0; i < contents.length; i++) {
	        if (contents[i] instanceof Answer) {
	            if (contents[i].isSource) {
	                type = "DND";
	                break;
	            }
	        }
	    }
	    if (type) return type;
	    
	    
	    // check for MC
	    for (var i = 0; i < contents.length; i++) {
	        if (contents[i] instanceof Answer) {
	            type = "MC";
	            break;
	        }
	    }
	    if (type) return type;
	    
	    
	    // check for GAPTEXT
	    for (var i = 0; i < contents.length; i++) {
	        if (contents[i] instanceof Text) {
	            if (contents[i].value.indexOf(Constants.GAPMARKER)) {
	                type = "GAPTEXT";
	                break;
	            }
	        }
	    }
	    return type;
	}
	
	function mixAnswers(step) {
		var contents = step.getContentsByClassname("Answer");
		if (step.type == "Drag_Drop") {
			var dragObjects = new Array();
			var targetObjects = new Array();
			for (var i = 0; i < contents.length; i++) {
				if (contents[i].isSource) {
					dragObjects.push(contents[i]);
				}
				else {
					targetObjects.push(contents[i]);
				}
			}
			
			// mix dragObjects
			var tempContents = utils.getRandomElements(dragObjects, dragObjects.length);
			var myPositions = new Array();
			for (var i = 0; i < tempContents.length; i++) {
				var rectangle = new Rectangle(tempContents[i].left, tempContents[i].top, tempContents[i].width, tempContents[i].height);
				myPositions.push(rectangle);
			}
			
			for (var i = 0; i < dragObjects.length; i++) {
				var rect = myPositions[i];
				dragObjects[i].left = rect.x1;
				dragObjects[i].top = rect.y1;
				dragObjects[i].width = rect.width;
				dragObjects[i].height = rect.height;
			}
			
			// mix targetObjects
			tempContents = utils.getRandomElements(targetObjects, targetObjects.length);
			myPositions = new Array();
			for (var i = 0; i < tempContents.length; i++) {
				var rectangle = new Rectangle(tempContents[i].left, tempContents[i].top, tempContents[i].width, tempContents[i].height);
				myPositions.push(rectangle);
			}
			
			for (var i = 0; i < targetObjects.length; i++) {
				var rect = myPositions[i];
				targetObjects[i].left = rect.x1;
				targetObjects[i].top = rect.y1;
				targetObjects[i].width = rect.width;
				targetObjects[i].height = rect.height;
			}
		
		}
		else {
			var myContents = new Array();
			for (var i = 0; i < contents.length; i++) {
				myContents.push(contents[i]);
			}
			var tempContents = utils.getRandomElements(myContents, myContents.length);
			
			var myPositions = new Array();
			for (var i = 0; i < tempContents.length; i++) {
				var rectangle = new Rectangle(tempContents[i].left, tempContents[i].top, tempContents[i].width, tempContents[i].height);
				myPositions.push(rectangle);
			}
			
			for (var i = 0; i < contents.length; i++) {
				var rect = myPositions[i];
				contents[i].left = rect.x1;
				contents[i].top = rect.y1;
				contents[i].width = rect.width;
				contents[i].height = rect.height;
			}
		}
		
	}
	
	function shutdown() {
		// save test-result
		if (standardapi) {
			standardapi.setLMSValue("cmi.core.score", engine.getVariable("$result"));
		}
	}
	
	
	
}

updateProgress();

function GaptextUtilities() {

	this.createGaptext = createGaptext;
	this.fillGaptext = fillGaptext;
	this.resolveGaptext = resolveGaptext;
	this.createUserValues = createUserValues;
	this.createGapArray = createGapArray;
	
	function createGaptext(content) {
	    var myValue = content.value;
	    var gapMarker = constants.GAPMARKER;
	    var gapMarkerStart = 0;
	    var gapMarkerEnd = 0;
	    var gapLinkEnd = 0;
	    var gapString = "";
	    var gapIndex = 0;
	    while (myValue.indexOf(gapMarker) != -1) {
	        gapMarkerStart = myValue.indexOf(gapMarker);
	        for (var i = gapMarkerStart; i >= 0; i--) {
	        	if (myValue.charAt(i) == "<") {
	        		gapMarkerStart = i;
	        		break;
	        	}
	        }
	        
	        gapMarkerEnd = myValue.indexOf(">", gapMarkerStart);
	        gapLinkEnd = myValue.indexOf("</a>", gapMarkerStart);
	        gapString = myValue.substring(gapMarkerEnd + 1, gapLinkEnd);
	        var newValue = myValue.substring(0, gapMarkerStart);
	        
	        var id = "gap_" + content.id + "|" + gapIndex;
	        newValue += "<input class=\"gaptext\" type=\"text\" id=\"" + id + "\" name=\"" + id + "\" size=\"" + gapString.length + "\" onkeyup=\"exercisemanager.updateGapValue('" + content.id + "|" + gapIndex + "')\"/>";
	        newValue += myValue.substring(gapLinkEnd + 4);
	        myValue = newValue;
	        gapIndex++;
	    }
	    return myValue;
	}
	
	function fillGaptext(content) {
	    for (var i = 0; i < content.userValues.length; i++) {
	        var inputField = document.getElementById("gap_" + content.id + "|" + i);
	        inputField.value = content.userValues[i];
	    }
	}
	
	function resolveGaptext(content) {
		var myValue = content.gaptext;
	    var gapMarker = constants.GAPMARKER;
	    var gapMarkerStart = 0;
	    var gapMarkerEnd = 0;
	    var gapLinkEnd = 0;
	    var gapString = "";
	    var gapIndex = 0;
	    while (myValue.indexOf(gapMarker) != -1) {
	        gapMarkerStart = myValue.indexOf(gapMarker);
	        for (var i = gapMarkerStart; i >= 0; i--) {
	        	if (myValue.charAt(i) == "<") {
	        		gapMarkerStart = i;
	        		break;
	        	}
	        }
	        
	        gapMarkerEnd = myValue.indexOf(">", gapMarkerStart);
	        gapLinkEnd = myValue.indexOf("</a>", gapMarkerStart);
	        gapString = myValue.substring(gapMarkerEnd + 1, gapLinkEnd);
	        var newValue = myValue.substring(0, gapMarkerStart);
	        
	        var correctGapAlternatives = content.gaps[gapIndex].words[0];
	        var imax = content.gaps[gapIndex].words.length;
	        if (imax > 1) correctGapAlternatives += " (";
	        for (var i = 1; i < imax; i++) {
	        	correctGapAlternatives += content.gaps[gapIndex].words[i];
	        	if (i < imax - 1) correctGapAlternatives += ", ";	
	        }
	        if (imax > 1) correctGapAlternatives += ")";
	        
	        var correct = checkIfCorrect(content.userValues[gapIndex], content.gaps[gapIndex].words);
	        var userValue = content.userValues[gapIndex];
	        if (userValue == "") {
	        	userValue = utils.getI18nValue("noGapEntry");
	        }
	        
	        if (!correct) {
	        	newValue += "<span class=\"gaptextWrong\">" + userValue + "</span> <span class=\"gaptextCorrect\">" + correctGapAlternatives + "</span>";	        
	        }
	        else {
	        	newValue += "<span class=\"gaptextCorrect\">" + userValue + "</span>";
	        }
	         
	        newValue += myValue.substring(gapLinkEnd + 4);
	        myValue = newValue;
	        gapIndex++;
	    }
	    content.divRef.innerHTML = myValue;
		
	}
	
	function checkIfCorrect(uservalue, alternatives) {
		var correct = false;
		for (var i = 0; i < alternatives.length; i++) {
			if (uservalue.toLowerCase() == alternatives[i].toLowerCase()) {
				correct = true;
				break;
			}
		}
		return correct;
	}
	
	function createUserValues(gaptext) {
	    var gapmarker = constants.GAPMARKER;
	    var array = new Array();
	    var n = 0;
	    for (var i = 0; i < gaptext.length; i++) {
	        if (gaptext.indexOf(gapmarker, i) == i) {
	            array[n] = "";
	            n++;
	        }
	    }
	    return array;
	}
	
	function createGapArray(gaptext) {
	    var gapmarker = constants.GAPMARKER;
	    var array = new Array();
	    
	    var n = 0;
	    while (true) {
    	    var a = gaptext.indexOf(gapmarker, n);
    	    for (var i = a; i > 0; i--) {
	        	if (gaptext.charAt(i) == "<") {
	        		a = i;
	        		break;
	        	}
	        }
    	    var a1 = gaptext.indexOf(">", a);
    	    var b1 = gaptext.indexOf("<", a1);
    	    
    	    if (a == -1 || a1 == -1 || b1 == -1) break;
    	    
    	    var anchorTagOpen = gaptext.substring(a, a1);
    	    
    	    var c = anchorTagOpen.indexOf("points=\"");
    	    var c1 = anchorTagOpen.indexOf("\"", c + 9);
    	    
    	    var pointsValue = anchorTagOpen.substring(c + 8, c1);
    	    var points = parseInt(pointsValue);
    	    
    	    var defaultWord = gaptext.substring(a1 + 1, b1);
    	    
    	    var aa1 = anchorTagOpen.indexOf("href=\"") + 6;
    	    var bb1 = anchorTagOpen.indexOf("\"", aa1);
    	    var alternativeWordsString = anchorTagOpen.substring(aa1, bb1);
    	    var wordsArray = new Array();
    	    if (alternativeWordsString != "") {
    	    	wordsArray = alternativeWordsString.split("|");
    	    }
    	    wordsArray.unshift(defaultWord);
    	    var gapEntry = new GapEntry(points, wordsArray);
    	    array.push(gapEntry);
    	    
    	    
    	    n = b1;
        }
	    
	    return array;
	}




}



updateProgress();


function MouseController() {
	    
	this.mouseDown = mouseDown;
	this.mouseUp = mouseUp;
	this.mouseMove = mouseMove;
	this.mouseOver = mouseOver;
	this.mouseOut = mouseOut;
	this.registerContent = registerContent;
	
	var contents = new Array();
	function registerContent(content) {
		contents[content.id] = content;
	}
	
	function mouseDown(event, id) {
		if (contents[id]) {
		    contents[id].mouseDown(event);
		}
	}
	function mouseUp(event, id) {
	    if (contents[id]) {
		    contents[id].mouseUp(event);
		}
	}
	function mouseMove(event, id) {
	    if (contents[id]) {
		    contents[id].mouseMove(event);
		}
	}
	function mouseOver(event, id) {
	    if (contents[id]) {
		    contents[id].mouseOver(event);
		}
		
	}
	function mouseOut(event, id) {
	    if (contents[id]) {
		    contents[id].mouseOut(event);
		}
	}



}

function EventController() {
	    
	this.fireEvent = fireEvent;
	
	var eventTimeout = null;
	
	function fireEvent(content, eventType) {
		
		if (content.id.indexOf("-") != -1) {
			if (eventType == "On_Release" || eventType == "On_Press" || eventType == "On_Init" || eventType == "On_Stop") {
				//engine.log("Content " + content.value + " fired event " + eventType);
			}
		}
			
		var events = content.events;
		
		if (content instanceof CTStep) {
			engine.log(events.length);
		}
		
		for (var i = 0; i < events.length; i++) {
			
			if (events[i].type == eventType) {
				if (eventType == "On_Variable") {
					var variableName = events[i].varname;
					if (variableName.indexOf("$") != 0) variableName = "$" + variableName;
					var variableValue = events[i].varvalue;
					
					if (variableValue.indexOf("<") == 0 || variableValue.indexOf(">") == 0) {
						var realValue = engine.getVariable(variableName);
						if (realValue) {
							realValue = parseInt(realValue);
							if (variableValue.indexOf("<") == 0) {
								variableValue = variableValue.substring(1);
								if (realValue < parseInt(variableValue)) {
									performEvent(content, events[i]);
								}
							}
							else {
								variableValue = variableValue.substring(1);
								if (realValue > parseInt(variableValue)) {
									performEvent(content, events[i]);
								}
							}	
						} 
					}
					else {
						if (engine.getVariable(variableName) == variableValue) {
							performEvent(content, events[i]);
						}
					}
				}
				else {	
					performEvent(content, events[i]);
				}
			}
		} 
	}
	
	function performEvent(content, event) {
		var type = event.type;
		var action = event.action;
		var objectId = event.objectId;
		var stepId = event.stepId;
		var varname = event.varname;
		var varvalue = event.varvalue;
		
			
		if (action == "Trigger") {
			if (objectId != "") {
				trigger(objectId);
			}
			else if (stepId != "") {
				engine.showHotspotContent(null,stepId, true);
			}
		}
		else if (action == "Remove") {
			remove(objectId);
		}
		else if (action == "Goto") {
			doGoto(stepId);
		}
		else if (action == "Stop") {
			stop(objectId);
		}
		else if (action == "Repeat") {
			repeatExercise();
		}
		else if (action == "Dissolve") {
			dissolveExercise();
		}
		else if (action == "Toggle") {
			toggle(objectId);
		}
		else if (action == "GoForward") {
			goForward();
		}
		else if (action == "GoBackward") {
			goBackward();
		}
		else if (action == "ReloadStep") {
			reloadStep();
		}
		else if (action == "OpenFAQ") {
            openFAQ();
        }
        else if (action == "OpenGlossary") {
            openGlossary();
        }
        else if (action == "OpenHelp") {
            openHelp();
        }
        else if (action == "ToggleAudio") {
            toggleAudio();
        }
        else if (action == "Set") {
        	engine.setUserVariable(varname, varvalue);
        }
		else {
			alert("Event is not defined properly and cannot be processed.\nPlease check the event-properties.");
		}
	}
	
	function toggleAudio() {
		eventTimeout = window.setTimeout('engine.toggleAudio();', 1);
	}
	
	function openFAQ() {
		eventTimeout = window.setTimeout('engine.openFAQ();', 1);
	}
	
	function openGlossary() {
		eventTimeout = window.setTimeout('engine.openGlossary();', 1);
	}
	
	function openHelp() {
		eventTimeout = window.setTimeout('engine.openHelp();', 1);
	}
	
	function goForward() {
		eventTimeout = window.setTimeout('engine.goForward()', 1);
	}
	
	function goBackward() {
		eventTimeout = window.setTimeout('engine.goBackward()', 1);
	}
	
	function reloadStep() {
		eventTimeout = window.setTimeout('engine.reloadStep()', 1);
	}
	
	function repeatExercise() {
		eventTimeout = window.setTimeout('exercisemanager.repeatExercise()', 1);
	}
	
	function dissolveExercise() {
		eventTimeout = window.setTimeout('exercisemanager.dissolveExercise()', 1);
	}
		
	function trigger(id) {
		eventTimeout = window.setTimeout("engine.trigger('" + id + "')", 1);
	}
	
	function toggle(id) {
		eventTimeout = window.setTimeout("engine.toggle('" + id + "')", 1);
	
		
	}
	
	function remove(id) {
		eventTimeout = window.setTimeout("engine.remove('" + id + "')", 1);
		
	}
	
	function doGoto(id) {
		engine.jumpToStep(id);
	}
	
	function stop(id) {
		eventTimeout = window.setTimeout("engine.stop('" + id + "')", 1);
	}
	
}

updateProgress();


updateProgress();

// Content --------------------------------------------------------------------------------

function Content(id, top, left, width, height, draggable, visible, triggered) {
	this.init(id, top, left, width, height, draggable, visible, triggered);
}
Content.prototype.init = function (id, top, left, width, height, draggable, visible, triggered) {
	this.id = id;
	this.top = top;
	this.left = left;
	this.width = width;
	this.height = height;
	this.draggable = draggable;
	this.visible = visible;
	this.triggered = triggered;
	
	this.defaultTop = top;
	this.defaultLeft = left;
	this.defaultWidth = width;
	this.defaultHeight = height;
	
	this.divRef = null;
	this.oldX = 0;
	this.oldY = 0;
	this.objectX = 0;
	this.zIndex = 1000;
	this.objectY = 0;
	this.opaque = false;
	this.isMouseDown = false;
	this.popup = false;
	this.parent = null;
	this.events = new Array();
	this.properties = new Array();
	this.component = null;
	this.locked = false;
	this.context = "content";
	this.step = null;
	this.title = null;
	this.hExpand = width == -1;
	this.vExpand = height == -1;
	//this.overflow = "hidden";
	
	if (this.triggered) this.visible = false;
	
}

Content.prototype.setProperty = function(name, value) {
	properties[name] = value;
}

Content.prototype.setComponent = function(component) {
	this.component = component;
}


Content.prototype.addEvent = function(event) {
	this.events.push(event);
}

Content.prototype.toFront = function() {
	var currentStep = engine.getCurrentStep();
	var maxZ = 0;
	var contents = currentStep.getAllContents();
	for (var i = 0; i < contents.length; i++) {
		if (parseInt(contents[i].zIndex) > maxZ) maxZ = parseInt(contents[i].zIndex);
	}
	maxZ++;
	this.zIndex = maxZ;
	this.divRef.style.zIndex = this.zIndex;
}

Content.prototype.reset = function() {
	this.top = this.defaultTop;
	this.left = this.defaultLeft;
	this.width = this.defaultWidth;
	this.height = this.defaultHeight;
	this.update();
}

Content.prototype.createDivStart = function(id, top, left, width, height, draggable, visible) {
	var html = new StringBuffer();
	var mousehandler = new StringBuffer();
	if (!this.hotspots || this.hotspots.length == 0) { // workaround for hotspots to set the cursor on mouseover
	    mousehandler.append("onmousedown=\"mousecontroller.mouseDown(event,'");
	    mousehandler.append(id);
	    mousehandler.append("');return false\" onmouseup=\"mousecontroller.mouseUp(event,'");
	    mousehandler.append(id);
	    mousehandler.append("');return false\" onmousemove=\"mousecontroller.mouseMove(event,'");
	    mousehandler.append(id);
	    mousehandler.append("');return false\" onmouseover=\"mousecontroller.mouseOver(event,'");
	    mousehandler.append(id);
	    mousehandler.append("')\" onmouseout=\"mousecontroller.mouseOut(event,'");
	    mousehandler.append(id);
	    mousehandler.append("')\"");
	}
	
	
	var positionType = "absolute";
	var floatString = "";
	var visibility = this.visible ? "visible" : "hidden";
	
	var style = new StringBuffer();
	/*
	if (this.id == "content") {
		style.append("border:1px green solid;");
	}
	*/
	style.append("position:");
	style.append(positionType);
	style.append(";overflow:");
	style.append(this.overflow);
	style.append(";vertical-align:top;top:");
	style.append(this.top);
	style.append(";left:");
	style.append(this.left);
	style.append(";width:");
	style.append(this.width);
	style.append(";height:");
	style.append(this.height);
	style.append(";z-index:");
	style.append(this.zIndex);
	style.append(";visibility:");
	style.append(visibility);
	style.append(";");
	style.append(floatString);
	if (debug) style.append(";border:1px red solid");
	
	// !!!!!!!!!!!!!!!!!! CHANGE THAT FUCKED UP QUICK HACK BELOW !!!!!!!!!!!!!!!!!!
	var myClass = "";
	if (this.getClassName() == "Feedback" || this.getClassName() == "FeedbackRange" ) {
		//if (this.visible) myClass = "";
		myClass = "default" + this.getClassName();
	}
	else {
		if (this.id.indexOf("-") == -1) {
			myClass = this.id;
		}
	}
	
	html.append("<div id=\"");
	html.append(this.id);
	html.append("\" style=\"");
	html.append(style.toString());
	html.append("\" class=\"");
	html.append(myClass);
	html.append("\" ");
	html.append(mousehandler.toString());
	html.append(">");
	
	//alert(html.toString());
	return html.toString();
}
Content.prototype.createDivEnd = function() {
	return "</div>";
}
Content.prototype.update = function(myDocument) {
	if (!myDocument) {
		myDocument = window.document;
	}
	
		
	this.divRef = myDocument.getElementById("" + this.id);
	if (!this.divRef) {
		return;
	}
	if (!this.popup) {
		this.divRef.style.top = this.top;
    	this.divRef.style.left = this.left;
    }
    
    var realHeight = this.height;
    var realWidth = this.width;
    
    if (this.vExpand == true) {
    	if (this.parent != null) {
    		realHeight = this.parent.height - this.top;
    		//alert(this.parent.id + " " + realHeight);
    	}
    }
    if (this.hExpand == true) {
    	if (this.parent != null) {
    		realWidth = this.parent.width;
    	}
    }
    
    
    //alert("realHeight: " + realHeight);
    
    /*
    var bodyTag = document.getElementsByTagName("body")[0];
    if (realHeight == -1) {
    	if (this.parent != null) {
    		realHeight = this.parent.height;
    	}
    	else {
    		realHeight = parseInt(bodyTag.offsetHeight);
    	}
    }
    if (realWidth == -1) {
    	if (this.parent != null) {
    		realWidth = this.parent.width;
    	}
    	else {
    		realWidth = parseInt(bodyTag.offsetWidth);
    	}
    }
    */
    
    //alert(realWidth + " x " + realHeight + " " + this.id);
    
    this.height = realHeight;
    this.width = realWidth;
    
    this.divRef.style.width = this.width;
    this.divRef.style.height = this.height;
    this.divRef.style.zIndex = this.zIndex;
    this.divRef.style.visibility = this.visible ? "visible" : "hidden";
    
	this.locked = engine.isInFeedbackMode();
	
	if (this.visible && !this.locked && !(this instanceof Media)) {
		eventcontroller.fireEvent(this, "On_Init");
	}
	else if (!this.locked && !(this instanceof Media)) {
		eventcontroller.fireEvent(this, "On_Stop");
	}
	
	
}
Content.prototype.draw = function() {
	var html = this.createDivStart.call(this, this.id, this.top, this.left, this.width, this.height, this.draggable, this.visible) + this.createDivEnd.call(this);
	return html;
}
Content.prototype.mouseDown = function(event) {
	if (engine.isInPreviewMode()) return;
	
	if ((this.draggable && this.context == "skin") || (this.draggable && !this.parent && !this.locked)) {
		this.oldX = event.clientX;
	    this.oldY = event.clientY;
	    this.objectX = parseInt(this.divRef.style.left);
	    this.objectY = parseInt(this.divRef.style.top);
	    this.oldZ = this.zIndex;
	    this.toFront();
	    this.isMouseDown = true;
	}
	eventcontroller.fireEvent(this, "On_Press");
}
Content.prototype.mouseUp = function(event) {
	if (engine.isInPreviewMode()) return;
	if (this.draggable && !this.locked) {
		this.isMouseDown = false;
	}
	eventcontroller.fireEvent(this, "On_Release");
}
Content.prototype.mouseMove = function(event) {
	if (engine.isInPreviewMode()) return;
	if (event) {
		engine.currentMouseX = event.clientX;
		engine.currentMouseY = event.clientY;
	}
	
	if (this.draggable && this.isMouseDown && !this.locked) {
		
		var contextLayer = contentlayer;
		if (this.context == "skin") {
			contextLayer = mainlayer;
		}
		var contextlayerWidth = parseInt(contextLayer.style.width);
        var contextlayerHeight = parseInt(contextLayer.style.height);
		var contextlayerLeft = parseInt(contextLayer.style.left);
        var contextlayerTop = parseInt(contextLayer.style.top);
        
        var left = this.objectX + event.clientX - this.oldX;
    	var top = this.objectY + event.clientY - this.oldY;
		
        if (left < 0) left = 0;
        if (top < 0) top = 0;
        
        var thisWidth = parseInt(this.width);
        var thisHeight = parseInt(this.height); 
				
		if (left + thisWidth > contextlayerWidth) {
			left = contextlayerWidth - thisWidth;
		}
		if (top + thisHeight > contextlayerHeight) {
			top = contextlayerHeight - thisHeight;
		}
        		
		this.left = left;
		this.top = top;
		
		// we do this for performance reasons
		this.divRef.style.left = left;
		this.divRef.style.top = top;
		
		
	}
}
Content.prototype.mouseOver = function(event) {
	if (engine.isInPreviewMode()) return;
	if (this.locked) return;
	
	var eventsLength = this.events.length;
	
	if (!this.draggable) {
		if (this.parent) {
			this.draggable = this.parent.draggable;
		}
	}
	if (this.draggable) {
		utils.showCursor(this.divRef, "move");
	}
	else if (eventsLength > 0) {
		for (var i = 0; i < eventsLength; i++) {
			if (this.events[i].type == "On_Release" || this.events[i].type == "On_Press" || this.events[i].type == "On_MouseEnter") {
				utils.showCursor(this.divRef, "pointer");
				break;
			}
		}
	}
	if (!this.draggable) {
		eventcontroller.fireEvent(this, "On_MouseEnter");
	}
}
Content.prototype.mouseOut = function(event) {
	if (engine.isInPreviewMode()) return;
	if (this.locked) return;
	utils.showCursor(this.divRef, "default");
	if (!this.draggable) {
		if (this.parent) {
			this.draggable = this.parent.draggable;
		}
	}
	if (!this.draggable) {
		eventcontroller.fireEvent(this, "On_MouseLeave");
	}
}
Content.prototype.moveTo = function(x, y) {
	this.left = x;
	this.top = y;
	this.update();
}
Content.prototype.getParent = function() {
	return this.parent;
}
Content.prototype.getClassName = function() {
	return "Content";
}

updateProgress();

// Container --------------------------------------------------------------------------------
Container.prototype = new Content();
Container.prototype.constructor = Content;
Container.superclass = Content.prototype;

function Container(id, top, left, width, height, draggable, visible, triggered) {
	this.init(id, top, left, width, height, draggable, visible, triggered);
}
Container.prototype.init = function (id, top, left, width, height, draggable, visible, triggered) {
	Container.superclass.init.call(this, id, top, left, width, height, draggable, visible, triggered);
	this.contents = new Array();
}
Container.prototype.draw = function() {
	var html = new StringBuffer();
	html.append(Container.superclass.createDivStart.call(this, this.id, this.top, this.left, this.width, this.height, this.draggable, this.visible));
	var contentsLength = this.contents.length;
	for (var i = 0; i < contentsLength; i++) {
		if (!this.contents[i].popup) {
			html.append(this.contents[i].draw());
		}
	}
	html.append(Container.superclass.createDivEnd.call(this));
	return html.toString();
}
Container.prototype.update = function(myDocument) {
	if (!myDocument) {
		myDocument = window.document;
	}
	
	Container.superclass.update.call(this, myDocument);
	if (this.visible) {
		this.divRef.style.visibility = "visible";
	}
	else {
		this.divRef.style.visibility = "hidden";
	}
	var contentsLength = this.contents.length;
	for (var i = 0; i < contentsLength; i++) {
		if (!this.contents[i].popup) {
			this.contents[i].update(myDocument);
		}
	}
	if (this.component) {
		this.component.update(this);
	}
}

Container.prototype.toFront = function(content) {
	Container.superclass.toFront.call(this, content);
	var contentsLength = this.contents.length;
	for (var i = 0; i < contentsLength; i++) {
		this.contents[i].toFront(content);
	}
}

Container.prototype.addContent = function(content) {
	content.parent = this;
	this.contents.push(content);
}
Container.prototype.getAllContents = function() {
	var allContents = new Array();
	var contentsLength = this.contents.length;
	for (var i = 0; i < contentsLength; i++) {
		allContents.push(this.contents[i]);
		if (this.contents[i] instanceof Container) {
			allContents.push(this.contents[i].getAllContents());
		}
	}
	return allContents;
}
Container.prototype.getContentById = function(id) {
	var myContent = null;
	for (var i = 0; i < this.contents.length; i++) {
		if (this.contents[i].id == id) {
			myContent = this.contents[i];
			break;
		}
		else {
			if (this.contents[i] instanceof Container) {
				myContent = this.contents[i].getContentById(id);
				if (myContent) break;
			}
		}
	}
	return myContent;
}

Container.prototype.getContents = function() {
	return this.contents;
}

Container.prototype.setContents = function(contents) {
	this.contents = contents;
}


Container.prototype.getClassName = function() {
	return "Container";
}


updateProgress();

// Media --------------------------------------------------------------------------------
Media.prototype = new Content();
Media.prototype.constructor = Content;
Media.superclass = Content.prototype;

function Media(id, top, left, width, height, draggable, visible, triggered, value, audiotext, type) {
	this.init(id, top, left, width, height, draggable, visible, triggered, value, audiotext, type);
}
Media.prototype.init = function(id, top, left, width, height, draggable, visible, triggered, value, audiotext, type) {
	Media.superclass.init.call(this, id, top, left, width, height, draggable, visible, triggered);
	this.value = value;
	this.type = type;
	this.audiotext = audiotext;
	this.hotspots = new Array();
	this.player = null;
	this.isPlaying = false;
	this.hotspotBordersVisible = false;
	
	utils.preloadFile(value, false);
		
}

Media.prototype.draw = function() {
	var html = new StringBuffer();
	html.append(Container.superclass.createDivStart.call(this, this.id, this.top, this.left, this.width, this.height, this.draggable, this.visible));
	if (this.type && this.type == "image") {
		html.append(utils.createMediaHTML(this));
		if (this.hotspotBordersVisible) {
			html.append(utils.drawHotspotBorders(this));
		}
	}
	else if (this.type) {
		this.player = new MediaPlayer(this);
		mediaTracker.registerPlayer(this.player);
		html.append(this.player.getHTML(this.value, this.width, this.height));
	}
	else {
		html.append(utils.createMediaHTML(this));
	}
	html.append(Container.superclass.createDivEnd.call(this));
    return html.toString();
}

Media.prototype.update = function(myDocument) {
	if (!myDocument) {
		myDocument = window.document;
	}
	
    Media.superclass.update.call(this, myDocument);
	if (this.locked || !this.type) {
		return;
	}
	if (this.type == "image") {
		if (this.visible) eventcontroller.fireEvent(this, "On_Init");
		return;
	}
	
	if (this.visible) {
		
			if (this.type == "audio") {
				engine.setAudio(this);
				
				if (engine.isInAudioMode()) {
					mediaTracker.muteAll(this.player);
					this.player.play();
					this.track();
					this.isPlaying = true;
				}
				eventcontroller.fireEvent(this, "On_Init");
			}
			else {
				this.player.play();
				this.track();
				this.isPlaying = true;
				eventcontroller.fireEvent(this, "On_Init");
			}
			
			
	}
	else {
		if (this.isPlaying) {
			this.player.stop();
			this.isPlaying = false;
			
			if (this.type == "audio") {
				engine.setAudio(null);
			}
		}
		
	}
}
Media.prototype.track = function() {
	if (this.player.getPlayState() == "FINISHED") {
		if (!this.player.muted) {
			eventcontroller.fireEvent(this, "On_Stop");	
		}		
	}
	else {
		window.setTimeout("mediaTracker.trackMedia('" + this.id + "')", 1000);
	}
}

Media.prototype.pause = function() {
	if (this.player) {
		this.player.pause();
	}
}


Media.prototype.addHotspot = function(hotspot) {
	this.hotspots.push(hotspot);
}
Media.prototype.setHotspotBordersVisible = function(visible) {
	this.hotspotBordersVisible = visible;
}

Media.prototype.mouseOver = function(event) {
	Media.superclass.mouseOver.call(this);
	if (engine.isInPreviewMode()) return;
	if (this.hotspots.length > 0) return true;
	
}

Media.prototype.getClassName = function() {
	return "Media";
}

// DrawObject --------------------------------------------------------------------------------
DrawObject.prototype = new Content();
DrawObject.prototype.constructor = Content;
DrawObject.superclass = Content.prototype;

function DrawObject(id, top, left, width, height, draggable, visible, triggered, drawtype, color) {
	this.init(id, top, left, width, height, draggable, visible, triggered, drawtype, color);
}
DrawObject.prototype.init = function(id, top, left, width, height, draggable, visible, triggered, drawtype, color) {
	DrawObject.superclass.init.call(this, id, top, left, width, height, draggable, visible, triggered);
	this.drawtype = drawtype;
	this.color = color;
	this.graphics = null;
	this.drawn = false;
}
DrawObject.prototype.update = function(myDocument) {
	if (!myDocument) {
		myDocument = window.document;
	}
	
    if (this.drawn) return;
    DrawObject.superclass.update.call(this, myDocument);
    this.graphics = new jsGraphics(this.divRef.id);
    this.graphics.setColor(this.color.getHexValue());
    if (this.drawtype == "Ellipse") {
        this.graphics.fillEllipse(parseInt(this.left), parseInt(this.top), parseInt(this.width), parseInt(this.height));    
    }
    if (this.drawtype == "Rectangle") {
        this.graphics.fillRect(this.left, this.top, this.width, this.height);    
    }
    this.graphics.paint();
}
DrawObject.prototype.getClassName = function() {
	return "DrawObject";
}

// Timer
Timer.prototype = new Content();
Timer.prototype.constructor = Content;
Timer.superclass = Content.prototype;

function Timer(id, value, triggered) {
	this.init(id, value, triggered);
}
Timer.prototype.init = function(id, value, triggered) {
	Timer.superclass.init.call(this, id, 0, 0, 0, 0, false, true, triggered);
	this.value = value;
	this.ticking = false;
}

Timer.prototype.update = function(myDocument) {
	if (!myDocument) {
		myDocument = window.document;
	}
	
	if (!this.ticking && this.visible) {
		this.start();
	}
}

Timer.prototype.start = function() {
	this.ticking = true;
	eventcontroller.fireEvent(this, "On_Init");
	engine.addTask("engine.stopTimer('" + this.id + "')", this.value);
}

Timer.prototype.stop = function() {
	eventcontroller.fireEvent(this, "On_Release");
}

Timer.prototype.getClassName = function() {
	return "Timer";
}

updateProgress();


// Text --------------------------------------------------------------------------------
Text.prototype = new Content();
Text.prototype.constructor = Content;
Text.superclass = Content.prototype;

function Text(id, top, left, width, height, draggable, visible, triggered, value) {
	this.init(id, top, left, width, height, draggable, visible, triggered, value);
}
Text.prototype.init = function(id, top, left, width, height, draggable, visible, triggered, value) {
	Text.superclass.init.call(this, id, top, left, width, height, draggable, visible, triggered);
	if (value && value.indexOf("$") == 0) {
	    this.value = value;
	}
	else if (utils) {
		this.value = utils.convertBodyStyle(value);
	    this.value = utils.convertImageTags(this.value);
	    this.value = utils.convertLinks(this.value);
	}
	this.text = this.value;
	
	
	
}
Text.prototype.draw = function() {
	//alert(this.id + " " + this.overflow);
	if (this.overflow == "") this.overflow = TEXTBOX_OVERFLOW;
	var html = new StringBuffer();
	html.append(Text.superclass.createDivStart.call(this, this.id, this.top, this.left, this.width, this.height, this.draggable, this.visible));
	var myValue = this.value;
	if (myValue.indexOf("$") != -1) {
	    myValue = engine.evaluateVariables(myValue, this);
	}
	html.append(myValue);
	html.append(Text.superclass.createDivEnd.call(this));
	
	this.text = myValue;
	
	return html;
	
}
Text.prototype.update = function(myDocument) {
	
	if (!myDocument) {
		myDocument = window.document;
	}
	
	Text.superclass.update.call(this, myDocument);
	
	
	var myValue = this.value;
	if (myValue.indexOf("$") > -1) {
		myValue = engine.evaluateVariables(myValue, this);
	}
	
	if (!this.divRef) {
		return;
	}
	
	// handle background-color
	var colorString = "<body bgcolor=\"";
	var a = myValue.indexOf(colorString);
	var b = myValue.indexOf("\"", a + colorString.length);
	if (a != -1 && b != -1) {
		var bgcolor = myValue.substring(a + colorString.length, b);
		this.divRef.style.backgroundColor = bgcolor;
	}
	
	this.divRef.innerHTML = myValue;
	
	
	if (this.overflow == "") {
		this.divRef.style.overflow = TEXTBOX_OVERFLOW;
	}
	
	
}
Text.prototype.getClassName = function() {
	return "Text";
}



// GapText --------------------------------------------------------------------------------
GapText.prototype = new Text();
GapText.prototype.constructor = Text;
GapText.superclass = Text.prototype;

function GapText(id, top, left, width, height, draggable, visible, triggered, value) {
	this.init(id, top, left, width, height, draggable, visible, triggered, value);
}
GapText.prototype.init = function(id, top, left, width, height, draggable, visible, triggered, value) {
	GapText.superclass.init.call(this, id, top, left, width, height, draggable, visible, triggered, value);
	this.gaptext = value;
	this.value = gaptextutilities.createGaptext(this);
	this.userValues = gaptextutilities.createUserValues(this.gaptext);
	this.gaps = gaptextutilities.createGapArray(this.gaptext);
}
GapText.prototype.update = function(myDocument) {
	if (!myDocument) {
		myDocument = window.document;
	}
	
	GapText.superclass.update.call(this, myDocument);
	if (engine.isInFeedbackMode()) {
		gaptextutilities.resolveGaptext(this);
	}
	else {
		gaptextutilities.fillGaptext(this);
	}
}
GapText.prototype.getClassName = function() {
	return "GapText";
}

// GapTextEntry -----------------------------------------------------------------------------
function GapEntry(points, words) {
    this.points = points;
    this.words = words;
}





// Answer --------------------------------------------------------------------------------
Answer.prototype = new Container();
Answer.prototype.constructor = Container;
Answer.superclass = Container.prototype;

function Answer(id, top, left, width, height, draggable, visible, triggered, value, isSource, dropTargetId, correct, points) {
	this.init(id, top, left, width, height, draggable, visible, triggered, value, isSource, dropTargetId, correct, points);
}
Answer.prototype.init = function(id, top, left, width, height, draggable, visible, triggered, value, isSource, dropTargetId, correct, points) {
	Answer.superclass.init.call(this, id, top, left, width, height, draggable, visible, triggered);
	if (points == 0) points = 1;
	this.value = value;
	this.isSource = isSource;
	this.dropTargetId = dropTargetId;
	this.correct = correct;
	this.points = points;
	this.boundObject = null;
	this.feedbacks = new Array();
	this.state = "default";
	if (isSource) this.draggable = true;
	
}
Answer.prototype.addFeedback = function(feedback) {
	this.feedbacks.push(feedback);
}
Answer.prototype.mouseUp = function(event) {
	Answer.superclass.mouseUp.call(this);
	if (engine.isInPreviewMode()) return;
	if (this.isSource) {
    	engine.dropAction(this.id);
    }
}

Answer.prototype.update = function(myDocument) {
	if (!myDocument) {
		myDocument = window.document;
	}
	
	Answer.superclass.update.call(this, myDocument);
	
	if (engine.isInFeedbackMode()) {
		var classname = "defaultAnswer";	
			
		if (this.isSource) {
			var correct = false;
			this.locked = true;
			
			if (this.boundObject != null) {
				if (this.dropTargetId == this.boundObject.id) {
					correct = true;
				}
			}
			else {
				if (this.dropTargetId == null) {
					correct = true;
				}
			}
			
			if (!correct) {
				classname = "answerSourceInFeedbackWrong";
			}
			else {
				classname = "answerSourceInFeedbackCorrect";
			}
		}
		else {
			if (this.step) {
				if (this.step.type == "Drag_Drop") {
					classname = "answerTargetInFeedback";
				}
			}
		}
		this.divRef.className = classname;
	}
	else {
		this.locked = false;
	}
	
	
}
Answer.prototype.getClassName = function() {
	return "Answer";
}

// Feedback --------------------------------------------------------------------------------
Feedback.prototype = new Container();
Feedback.prototype.constructor = Container;
Feedback.superclass = Container.prototype;

function Feedback(id, top, left, width, height, draggable, visible, triggered) {
	this.init(id, top, left, width, height, draggable, visible, triggered);
}
Feedback.prototype.init = function(id, top, left, width, height, draggable, visible, triggered) {
	Feedback.superclass.init.call(this, id, top, left, width, height, draggable, visible, triggered);
	this.currentFeedbackRange = null;
}
Feedback.prototype.getCurrentFeedbackRange = function() {
	return this.currentFeedbackRange;
}


Feedback.prototype.update = function(myDocument) {
	if (!myDocument) {
		myDocument = window.document;
	}
	
	Feedback.superclass.update.call(this, myDocument);
	
	if (!this.visible) {
		var feedbackRanges = this.getFeedbackRanges();
	    for (var i = 0; i < feedbackRanges.length; i++) {
	        var feedbackRangeContents = feedbackRanges[i].contents;
	        for (var ii = 0; ii < feedbackRangeContents.length; ii++) {
                feedbackRangeContents[ii].visible = false;
                feedbackRangeContents[ii].update();   
            }
        }
        eventcontroller.fireEvent(this, "On_Stop");
	}
	else {
		var currentStep = engine.getCurrentStep();
	    var percent = exercisemanager.calculateScore(currentStep);
	    var attempt = currentStep.attempt;
	    var tries = currentStep.tries;
	    
	    engine.setCurrentResult(percent);
	    
		var feedbackRange = this.getFeedbackRangeByScore(percent);
        if (feedbackRange == null) {
            alert("no fitting feedback found for " + percent + " percent");
            return;
        }
        this.currentFeedbackRange = feedbackRange;
        
        // show feedback-contents
        var feedbackRangeContents = feedbackRange.contents;
        for (var i = 0; i < feedbackRangeContents.length; i++) {
            if (!feedbackRangeContents[i].triggered) {
            	feedbackRangeContents[i].visible = true;
            	feedbackRangeContents[i].update();
            }
        }
        this.toFront();
        
        // hide dissolve-button if there are more attempts
		exercisemanager.checkAttempt(this);
		
        eventcontroller.fireEvent(this, "On_Init");
	
	}
	
	var currentStep = engine.getCurrentStep();
	if (currentStep.type == "Score") {
		this.divRef.className = "scoreFeedback";
	}
	else {
		this.divRef.className = "singleFeedback";
	}
}


Feedback.prototype.addFeedbackRange = function(feedbackRange) {
    this.feedbackRanges.push(feedbackRange);
}
Feedback.prototype.getFeedbackRangeByScore = function(score) {
    var feedbackRange = null;
    var allRanges = this.getFeedbackRanges();
    for (var i = 0; i < allRanges.length; i++) {
        if (allRanges[i].rangeMin <= score && allRanges[i].rangeMax >= score) {
            feedbackRange = allRanges[i];
            break;
        }
    }
    return feedbackRange;
}
Feedback.prototype.getFeedbackRanges = function() {
	var myContents = new Array();
	for (var i = 0; i < this.contents.length; i++) {
		if (this.contents[i] instanceof FeedbackRange) {
			myContents[myContents.length] = this.contents[i];
		}
	}
	return myContents;
}
Feedback.prototype.getClassName = function() {
	return "Feedback";
}


// FeedbackRange --------------------------------------------------------------------------------
FeedbackRange.prototype = new Container();
FeedbackRange.prototype.constructor = Container;
FeedbackRange.superclass = Container.prototype;

function FeedbackRange(id, top, left, width, height, draggable, visible, triggered, rangeMin, rangeMax) {
	this.init(id, top, left, width, height, draggable, visible, triggered, rangeMin, rangeMax);
}
FeedbackRange.prototype.init = function(id, top, left, width, height, draggable, visible, triggered, rangeMin, rangeMax) {
	FeedbackRange.superclass.init.call(this, id, top, left, width, height, draggable, visible, triggered);
	this.rangeMin = rangeMin;
	this.rangeMax = rangeMax;
	this.visible = false;
}
FeedbackRange.prototype.draw = function() {
	var html = new StringBuffer();
	html.append(FeedbackRange.superclass.createDivStart.call(this, this.id, this.top, this.left, this.width, this.height, this.draggable, this.visible));
	var contentsLength = this.contents.length;
	for (var i = 0; i < contentsLength; i++) {
		this.contents[i].visible = false;
		html.append(this.contents[i].draw());
	}
	html.append(FeedbackRange.superclass.createDivEnd.call(this));
	return html.toString();
}

FeedbackRange.prototype.update = function(myDocument) {
	if (!myDocument) {
		myDocument = window.document;
	}
	
	FeedbackRange.superclass.update.call(this, myDocument);
		
	if (this.visible) {
		eventcontroller.fireEvent(this, "On_Init");
	}
	else {
		eventcontroller.fireEvent(this, "On_Stop");
	}
}
FeedbackRange.prototype.getClassName = function() {
	return "FeedbackRange";
}



// Checkbox --------------------------------------------------------------------------------
Checkbox.prototype = new Content();
Checkbox.prototype.constructor = Content;
Checkbox.superclass = Content.prototype;

function Checkbox(id, top, left, width, height, draggable, visible, triggered, defaultIcon, activeIcon, value) {
	this.init(id, top, left, width, height, draggable, visible, triggered, defaultIcon, activeIcon, value);
}
Checkbox.prototype.init = function(id, top, left, width, height, draggable, visible, triggered, defaultIcon, activeIcon, value) {
	Checkbox.superclass.init.call(this, id, top, left, width, height, draggable, visible, triggered);
	this.defaultIcon = defaultIcon;
	this.activeIcon = activeIcon;
	this.value = value;
	this.state = "default";
	this.recentState = this.state;
	
}


Checkbox.prototype.draw = function() {
	var html = new StringBuffer();
	html.append(Checkbox.superclass.createDivStart.call(this, this.id, this.top, this.left, this.width, this.height, this.draggable, this.visible));
	html.append(utils.createCheckbox(this));
	html.append(Checkbox.superclass.createDivEnd.call(this));
	return html.toString();
}
Checkbox.prototype.update = function(myDocument) {
	if (!myDocument) {
		myDocument = window.document;
	}
	
	Checkbox.superclass.update.call(this, myDocument);
	var icon = "checkbox_unselected.gif";
	if (this.state == "active") {
		icon = "checkbox_selected.gif";
	}
	var mediaRef = myDocument.getElementById("media_" + this.id);
	if (mediaRef) {
		mediaRef.src = "img/" + icon;
	}
	
	var answer = this.getParent();
	
	if (answer != null && answer instanceof Answer && engine.isInFeedbackMode()) {
		var correct = false;
		this.locked = true;
		
		var classname = "";
		if (answer.correct && this.state == "active") classname = "checkboxFeedbackCorrect";
		if (answer.correct && this.state != "active") classname = "checkboxFeedbackCorrect";
		if (!answer.correct && this.state == "active") classname = "checkboxFeedbackWrong";
		if (mediaRef) mediaRef.className = classname;
		
	}
	else {
		this.locked = false;
	}
	
}
Checkbox.prototype.mouseDown = function(event) {
	Checkbox.superclass.mouseDown.call(this);
	if (engine.isInPreviewMode()) return;
	if (this.locked || this.state == "disabled") return;
	if (this.state == "default") {
		this.state = "active";
		if (this.parent) this.parent.state = "active";
		engine.checkboxSelected(this);
	}
	else if (this.state == "active" && !engine.isSingleChoice()) {
		this.state = "default";
		if (this.parent) {
		    this.parent.state = "default";
		}
	}
	this.update();
}
Checkbox.prototype.mouseOver = function(event) {
	Checkbox.superclass.mouseOver.call(this);
	if (engine.isInPreviewMode()) return;
	if (this.locked || this.state == "disabled") return;
	if (this.state != "disabled") {
	    utils.showCursor(this.divRef, "pointer");
	}
	else {
	    utils.showCursor(this.divRef, "default");
	}
}
Checkbox.prototype.getClassName = function() {
	return "Checkbox";
}


updateProgress();


// ActionButton --------------------------------------------------------------------------------
ActionButton.prototype = new Content();
ActionButton.prototype.constructor = Content;
ActionButton.superclass = Content.prototype;

function ActionButton(id, top, left, width, height, draggable, visible, triggered, defaultIcon, activeIcon, rolloverIcon, disabledIcon, highlightedIcon, tooltip, caption) {
	this.init(id, top, left, width, height, draggable, visible, triggered, defaultIcon, activeIcon, rolloverIcon, disabledIcon, highlightedIcon, tooltip, caption);
}
ActionButton.prototype.init = function(id, top, left, width, height, draggable, visible, triggered, defaultIcon, activeIcon, rolloverIcon, disabledIcon, highlightedIcon, tooltip, caption) {
	ActionButton.superclass.init.call(this, id, top, left, width, height, draggable, visible, triggered);
	this.defaultIcon = defaultIcon;
	this.activeIcon = activeIcon;
	this.state = "default";
	this.recentState = this.state;
	this.rolloverIcon = rolloverIcon;
	this.disabledIcon = disabledIcon;
	this.highlightedIcon = highlightedIcon;
	this.tooltip = tooltip;
	this.caption = caption;
	
	
	
}
ActionButton.prototype.draw = function() {
	var html = new StringBuffer();
	html.append(ActionButton.superclass.createDivStart.call(this, this.id, this.top, this.left, this.width, this.height, this.draggable, this.visible));
	html.append(utils.createButton(this));
	html.append(ActionButton.superclass.createDivEnd.call(this));
	return html.toString();
}

ActionButton.prototype.update = function(myDocument) {
	if (!myDocument) {
		myDocument = window.document;
	}
	
	var icon = null;
		
	var enabled = engine.checkEnabled(this);
	if (!enabled) {
	    this.state = "disabled";
	}
	else {
	    if (this.state != "rollover" && this.state != "active" && this.state != "highlighted") {
	        this.state = "default";
	    }
	}
	ActionButton.superclass.update.call(this, myDocument);
		
	var styleClass = "";	
		
	if (this.state == "default") {
		if (this.defaultIcon) {
			icon = this.defaultIcon;
		}
		else {
			styleClass = "defaultButton";
		}
	}
	else if (this.state == "active") {
		if (this.activeIcon) {
			icon = this.activeIcon;
		}
		else {
			styleClass = "activeButton";
		}
	}
	else if (this.state == "rollover") {
		if (this.rolloverIcon) {
			icon = this.rolloverIcon;
		}
		else {
			styleClass = "rolloverButton";
		}
	}
	else if (this.state == "disabled") {
		if (this.disabledIcon) {
			icon = this.disabledIcon;
		}
		else {
			styleClass = "disabledButton";
		}
	}	
	else if (this.state == "highlighted") {
		if (this.highlightedIcon != "") {
			icon = this.highlightedIcon;
		}
		else {
			styleClass = "highlightedButton";
		}
	}	
	
	if (styleClass) {
		this.divRef.className = styleClass;
	}
	
	var mediaRef = myDocument.getElementById("media_" + this.id);
	
	if (icon) {
		if (this.context == "skin") {
			mediaRef.src = "img/" + icon;
		}
		else {
			mediaRef.src = icon;
		}
	}
	else {
		mediaRef.width = 0;
		mediaRef.height = 0;
	}
	
	if (this.state == "disabled") {
		utils.showCursor(this.divRef, "default");
	}
	else if (this.state == "default") {
		utils.showCursor(this.divRef, "pointer");
	}
	else if (this.state == "active") {
		utils.showCursor(this.divRef, "pointer");
	}
	else if (this.state == "rollover") {
		utils.showCursor(this.divRef, "pointer");
	}
	else if (this.state == "highlighted") {
		utils.showCursor(this.divRef, "pointer");
	}
	
	
}
ActionButton.prototype.mouseOver = function(event) {
	ActionButton.superclass.mouseOver.call(this);
	if (engine.isInPreviewMode()) return;
	if (this.state == "disabled") {
		utils.showCursor(this.divRef, "default");
		return false;
	}
	if (this.state != "active") {
		this.state = "rollover";
	}
	engine.showTooltip(event, this);
	
	this.update();
}

ActionButton.prototype.mouseOut = function(event) {
	ActionButton.superclass.mouseOut.call(this);
	if (engine.isInPreviewMode()) return;
	engine.hideTooltip();	
	if (this.state == "disabled") return false;
	this.state = "default";
	this.update();
}

ActionButton.prototype.mouseDown = function(event) {
	if (this.state == "disabled") return false;
	ActionButton.superclass.mouseDown.call(this);	
	if (engine.isInPreviewMode()) return;
	this.recentState = this.state;
	this.state = "active";
	this.update();
	
}

ActionButton.prototype.mouseUp = function(event) {
	if (this.state == "disabled") return false;
	ActionButton.superclass.mouseUp.call(this);
	if (engine.isInPreviewMode()) return;
	this.state = this.recentState;
	this.update();
}


ActionButton.prototype.doAction = function() {
	engine.currentContent = this;
	window.setTimeout('engine.callAction()', 10);
}

ActionButton.prototype.getClassName = function() {
	return "ActionButton";
}

// ToggleButton --------------------------------------------------------------------------------
ToggleButton.prototype = new ActionButton();
ToggleButton.prototype.constructor = ActionButton;
ToggleButton.superclass = ActionButton.prototype;

function ToggleButton(id, top, left, width, height, draggable, visible, triggered, defaultIcon, activeIcon, rolloverIcon, disabledIcon, highlightedIcon, tooltip, caption) {
	this.init(id, top, left, width, height, draggable, visible, triggered, defaultIcon, activeIcon, rolloverIcon, disabledIcon, highlightedIcon, tooltip, caption);
}

ToggleButton.prototype.mouseDown = function(event) {
	if (engine.isInPreviewMode()) return false;
	if (this.state == "disabled") return false;
	
	if (this.state == "rollover") {
		this.state = this.recentState;
	}
	if (this.state == "default") {
		this.state = "active";
	}
	else if (this.state == "active") {
		this.state = "default";
	}
	this.update();
	eventcontroller.fireEvent(this, "On_Press");
	
}


ToggleButton.prototype.mouseUp = function(event) {
	if (engine.isInPreviewMode()) return false;
	if (this.state == "disabled") return false;
	eventcontroller.fireEvent(this, "On_Release");
}


ToggleButton.prototype.mouseOver = function(event) {
	if (engine.isInPreviewMode()) return false;
	engine.showTooltip(event, this);
	if (this.state == "disabled") return false;
	this.recentState = this.state;
	if (this.state != "active" && this.state != "rollover") {
		this.state = "rollover";
	}
	this.update();
	eventcontroller.fireEvent(this, "On_MouseOver");
}

ToggleButton.prototype.mouseOut = function(event) {
	if (engine.isInPreviewMode()) return;
	if (this.state == "rollover") {
		this.state = this.recentState;
		this.update();
	}
	engine.hideTooltip();	
	eventcontroller.fireEvent(this, "On_MouseOut");
}
ToggleButton.prototype.getClassName = function() {
	return "ToggleButton";
}

// EVENT
function Event(type, action, step, object, varname, varvalue) {
	this.init(type, action, step, object, varname, varvalue);
}
Event.prototype.init = function (type, action, stepId, objectId, varname, varvalue) {
	this.type = type;
	this.action = action;
	this.stepId = stepId;
	this.objectId = objectId;
	this.varname = varname;
	this.varvalue = varvalue;
}
Event.prototype.getClassName = function() {
	return "Event";
}

// PROTOCOL

Protocol.prototype = new Content();
Protocol.prototype.constructor = Content;
Protocol.superclass = Content.prototype;

function Protocol(id, top, left, width, height, draggable, visible, triggered, viewtype) {
	this.init(id, top, left, width, height, draggable, visible, triggered, viewtype);
}
Protocol.prototype.init = function(id, top, left, width, height, draggable, visible, triggered, viewtype) {
	Protocol.superclass.init.call(this, id, top, left, width, height, draggable, visible, triggered);
	this.viewtype = viewtype;
	this.steps = new Array();
}

Protocol.prototype.update = function(myDocument) {
	if (!myDocument) {
		myDocument = window.document;
	}
	
	Protocol.superclass.update.call(this, myDocument);
	
	this.divRef.style.overflow = "auto";
		
	var step = engine.getCurrentStep();
	var evaluations = step.getEvaluations();
	var html = new StringBuffer();
	var contentsToUpdate = new Array();
	var top = 0;
	
	var exerciseI18n = utils.getI18nValue("exercise");
			
	for (var i = 0; i < evaluations.length; i++) {
		var exerciseStep = database.getStepById(evaluations[i]);
		html.append("<div style=\"position:absolute;left:0px;top:" + top + "px;width:100%;height:100%;border:0px red solid\"><table class=\"shortProtocolTable\" cellspacing=\"0\" cellpadding=\"0\">");
		exercisemanager.calculateScore(exerciseStep);
		//exerciseStep.loaded = false;
		var points = exercisemanager.currentPoints;
		var maxPoints = exercisemanager.currentMaxPoints;
		var icon = "img/correct.gif";
		if (points < maxPoints) icon = "img/wrong.gif";
		var stepTitle = exerciseStep.title;
		html.append("<tr><td class=\"protocolStep\"><img src=\"");
		html.append(icon);
		html.append("\"></td><td width=\"100%\" class=\"protocolStep\">");
		html.append(stepTitle);
		html.append("</td></tr>");
		html.append("</table></div>");
		top += PROTOCOL_STEPS_VERTICAL_GAP;
		
		if (this.viewtype == "Detailed" || this.viewtype == "Solution") {		
			var contents = exerciseStep.getContents();
			var id = "";
			if (exerciseStep.type == "Drag_Drop") {
				id = "protocolContents" + i;
			}
			html.append("<div id=\"");
			html.append(id);
			html.append("\" style=\"position:absolute;left:0px;top:");
			html.append(top);
			html.append("px;width:100%;height:100%;border:0px red solid\">");
			
			var m = 0;
			for (m = 0; m < contents.length; m++) {
				var z = contents[m].draw();
				html.append(z);
				contentsToUpdate.push(contents[m]);
			}
			top += exerciseStep.getMaxHeight() + PROTOCOL_STEPS_VERTICAL_GAP;
			html.append("<div style=\"clear:both\"></div></div><br/>");
		}
		
	}
	
	//alert(html.toString());
	
	this.divRef.innerHTML = html.toString();
	
	engine.positionsRelative = true;
	if (this.viewtype == "Solution") {
		engine.inFeedbackMode = true;
	}
	
	for (var i = 0; i < contentsToUpdate.length; i++) {
		contentsToUpdate[i].update();
	}
	
	if (this.viewtype == "Solution") {
		for (var i = 0; i < evaluations.length; i++) {
			var exerciseStep = database.getStepById(evaluations[i]);
			if (exerciseStep.type == "Drag_Drop") {
				var answers = exerciseStep.getContentsByClassname("Answer");
		    	for (var ii = 0; ii < answers.length; ii++) {
		    		answers[ii].reset();
		    	}
				exercisemanager.drawRelationLines(exerciseStep, i);
			}
		}
	}
		
	engine.positionsRelative = false;
	engine.inFeedbackMode = false;
	
	
}
Protocol.prototype.addStep = function(step) {
    this.steps.push(steps);
}

Protocol.prototype.getClassName = function() {
	return "Protocol";
}




