//David Fouhey
//Middlebury College
//Abandon all hope ye who enter here.
/*
Documentation information (for future generations of maintainers):

This code is huge. Accordingly, it's been split into sections to indicate
what most of the functions are doing. That way, if you don't have the time
or are fortunate not to get to know it personally, you really won't have to
scan through all the many lines to find the functionality you want to
modify or add to. Instead, use the section headers to try to guide you.


//*********************************************************
//Section information


/////////////////////////////////////////////////
//Function name
//Description
//[Arguments:
//   argumentName - argumentDescription]
//[Returns:
//  return description]
//




*/



//The default textual representation of the image size in urls
imageSize = "s";
//and its corresponding image size index
imageSizeIndex = 1;
//the set of textual descriptors for images
containerDescriptors = new Array("t","s","m","l","h");
//the pixel sizes of each image (width,height)
imageSizes = new Array( new Array(150, 202), new Array(256,345), new Array(473,619), new Array(691, 902), new Array(908, 1187));
//and the pixel sizes of the containers (width,height)
//the small image size container deviates from the standard derivation for width and height so that it can fit the graphs
containerSizes = new Array( new Array(368, 257),  new Array(600,500), new Array(984,674), new Array(1420, 957), new Array(1854,1242)); 
//the image sizes for the graph images for tiny (element 0) and small and up (element 1) 
graphImageSizes = new Array( new Array(316, 237), new Array(576, 432));



//*********************************************************
//Helper functions

/////////////////////////////////////////////////
//formatFloatString
//format a float into a string with a certain width
//Arguments:
//  sFloat - the float (in a string or number form)
//  width - how many characters wide the float should be
//Returns:
//  a string of width characters (potentially more) which represents
//  the sFloat 
function formatFloatString(sFloat,width){
	var returnStr = "";
	//normalize whatever we've been given.
	//if we've been passed '34.0000000', then we don't
	//want to use that as the string form
	var nForm = Number(sFloat);
	var sForm = String(nForm);
	if(sForm.length == width){
		return sForm;		
	}	
	if(sForm.length > width){
		cutAt = sForm.length - 1;
		while(cutAt != width){
			if((sForm.charAt(cutAt) == '.') && (cutAt != width+1)){
				//if we've hit a decimal point such that
				//we can't resize the string representation without
				//making the number plain wrong (ie. cut the last 0
				//off of 340000), cut our losses and chop up till then 
				cutAt--;	
				break;
			}
			cutAt--;
		}	
		return sForm.substring(0,cutAt);
	}
	else{
		if((sForm.search(/\./) == -1) && (sForm.length != width-1)){
			//we'll let some strings pass through which aren't the proper
			//length, but that's a lot better than stuff like "100.", which
			//just looks stupid and arises if, for example, we pass in '100'
			//and width=4
			sForm += ".";
		}
		else if(sForm.length == width - 1){
			return sForm; 
		}
		while(sForm.length < width){
			sForm += "0";
		}
		return sForm;
	} 
}

/////////////////////////////////////////////////
//getAjax
//Returns an XMLHttp Request
//Returns:
//  An XMLHttp request object.
function getAjax(){
	//Kudos to wikipedia's simple Ajax example
	//The try/catch chain will ultimately fail if none of these exist
	//However, this is checked at page load and if it fails at the
	//load, the rest of the page won't appear and thus this will never
	//get called again
	var transfer;
	try{
        	transfer = new XMLHttpRequest();
	}
	catch(ex){
        	try{
                	transfer = new ActiveXObject("Msxml2.XMLHTTP");
	        }
        	catch (ex){
                	transfer = new ActiveXObject("Microsoft.XMLHTTP");
	        }
	}
	return transfer;
} 



var ajaxDater = new Date();
/////////////////////////////////////////////////
//oneWayDataRequest
//Sets up a data request
//Arguments:
//  request-an XMLHttpRequest object
//  target-the target url
//  method-the method that is to be used (i.e. "GET")
//  dataHandler-a function with one parameter, to which will be
//              passed the response text
function oneWayDataRequest(request,target,method,dataHandler){
	target = target + "&nocache="+ajaxDater.getTime();
	request.onreadystatechange = function(){
		if(request.readyState == 4){
                	try{
                        	if(request.status == 200){
                                	var returnData = request.responseText;
        	                	dataHandler(returnData);
				}
                	}
                	catch(ex){
				//alert("An ajax exception occurred: "+ex.message);
			}
        	}
	}
	request.open(method,target);
	request.send(null);
}


//*********************************************************
//Table data control section
//The functions below control the loading of the data into the 
//main table on the page


/////////////////////////////////////////////////
//isDefined
//Determines whether there is a test for a particular method and data set
//Arguments:
//  methodId-the method id
//  dataSetId-the data set id
function isDefined(methodId,dataSetId){
	//defined so we can change the internal javascript
	//representation of whether data is defined 
	if(typeof(fd[methodId][dataSetId]) != typeof(0) || fd[methodId][dataSetId] === 1){
		return 1;
	}
	return 0;
}
	
/////////////////////////////////////////////////
//getInformation
//Gets the associative array storing the information about a particular
//test.
//Arguments:
//  methodId-the method of the test
//  dataSetId-the data set of the test
function getInformation(methodId,dataSetId){
	if(typeof(fd[methodId][dataSetId]) == typeof(0)){
		if(fd[methodId][dataSetId] === 0){
			return null;
		}
		else{
			//if we've hit this something's gone terribly wrong.
		}
	}
	else{
		return fd[methodId][dataSetId];
	}
}

/////////////////////////////////////////////////
//testField
//Checks whether the data for a particular field has been loaded or not
//Arguments:
//  field-an identifier for an accuracy or completeness field
function testField(field){
	return Boolean(fields[field]);
}


/////////////////////////////////////////////////
//getBoxData
//loads the data for accuracy and completeness data for the boxes
//Both an accuracy and completeness id are mandatory, but 0 can be passed
//to indicate that there is no accuracy/completeness data to be loaded
//Arguments:
//  accuracyId-the accuracy id 
//  completenessId-the completeness id
function getBoxData(accuracyId, completenessId){
	//hooray for anonymous functions!
	dataHandler = function(returnData){
		var splitData = returnData.split("\n");
		var lineSplit;
		for(var i = 0; i < splitData.length; i++){
			if(splitData[i] == ""){
				continue;
			}
			lineSplit = splitData[i].split(" ");
			methodId = Number(lineSplit[1]);
			dataSetId = Number(lineSplit[2]);
			try{
				var target = fd[methodId][dataSetId];
			}
			catch(ex){
			} 
			if(lineSplit[0].charAt(0) == "a"){
				target[accuracyId] = Number(lineSplit[3]);
			}
			else if(lineSplit[0].charAt(0) == "c"){
				target[completenessId] = Number(lineSplit[3]);
			}
		}
		if(accuracyId !== 0){
			fields[accuracyId] = 1;
		}
		if(completenessId !== 0){
			fields[completenessId] = 1;
		}
		updateBoxes(1);
	} 
	if((accuracyId != 0) && (completenessId != 0)){	
		oneWayDataRequest(getAjax(),"dataServer.php?a0="+accuracyId+"&a1="+completenessId,"GET",dataHandler);
	}
	else if(accuracyId != 0){
		oneWayDataRequest(getAjax(),"dataServer.php?a0="+accuracyId,"GET",dataHandler);
	}
	else if(completenessId != 0){
		oneWayDataRequest(getAjax(),"dataServer.php?a0="+completenessId,"GET",dataHandler);
	}
}
			

/////////////////////////////////////////////////
//updateBoxes
//Updates the table boxes.
//Arguments
//  override-whether to override the check on whether the data exists
//
//Call flow for changing the data to be displayed (i.e. accuracy):
// Event calls updateBoxes(0)
// updateBoxes checks whether the data requested is already stored
// by the client. If it is, then it just continues and loads the data, 
// into the table. 
//
// If it isn't, then then updateBoxes calls getBoxData() with the relevant
// accuracyId/completenessId stuff. getBoxData then sets up an ajax
// call, which calls back a function defined in getBoxData. Finally,
// the anonymous function takes the passed data, loads it to the client's
// data storage, and then finally calls updateBoxes(1). This overrides the
// check for data availability (just to save time), and then loads the data
// into the table. 

function updateBoxes(override){
	var accuracyId = document.getElementById('acc').value; 
	var completenessId = document.getElementById('comp').value;
	if(!override){
		var accPresent = testField(accuracyId);
		var compPresent = testField(completenessId);
		if((!compPresent) || (!accPresent)){ 	
			if(accPresent){
				accuracyId = 0;
			}
			if(compPresent){
				completenessId = 0;
			}  
			//call the functions to load data which will call back
			getBoxData(accuracyId,completenessId);
			return;
		}
	}
	for(x in activeMethods){
		var m = activeMethods[x];
		for(y in activeDataSets){
			var d = activeDataSets[y];
			var relevantData = getInformation(m,d);
			if(relevantData != null){
				try{
					document.getElementById("m"+m+"d"+d+"a").innerHTML = formatFloatString(relevantData[accuracyId],4);
				}
				catch(ex){

					//alert(m+" "+d+" "+accuracyId+"\n"+relevantData);

					var apple = new Array();
					apple.charAt(2);	
				}
				document.getElementById("m"+m+"d"+d+"c").innerHTML = formatFloatString(relevantData[completenessId],4);
			}
			else{
				document.getElementById("m"+m+"d"+d).innerHTML = "";
			}
		}
	}
	//we call it now, otherwise the filters will get called before the ajax has loaded
	updateFilters();

} 	



//*********************************************************
//Table sorting 
//The functions below have to do with sorting/filtering the table 

/////////////////////////////////////////////////
//isBetter
//Checks whether n1 is better than n2 for a certain propery
//Arguments
//  n1-number 1
//  n2-number 2
//  property-the property (i.e. a90, c125) that the numbers are
//Returns 
//  true if n1 is better than n2, false otherwise
//This is done because completeness numbers are better if they're high
//and accuracy numbers are better if they're low. 
function isBetter(n1, n2, property){
	return property.charAt(0) == "a" ? n1 < n2 : n1 > n2;
}


/////////////////////////////////////////////////
//determineSort
//figure out the sort method
//Returns 
//  a string of the form (a|c)(\d) where the first part indicates accuracy
//  or completeness and the second  indicates which column. It may also be 
//  blank, which indicates that no sort should be done, which leaves the
//  methods in their original state.  
function determineSort(){
	//assume alphabetic sort and check otherwise
	var sortBy = "";	
	for(x in activeDataSets){
		var i = activeDataSets[x];
		if(document.getElementById("sortbyc"+i).checked){
			sortBy = "c"+i;
		}
		else if(document.getElementById("sortbya"+i).checked){
			sortBy = "a"+i;
		}
	}
	return sortBy;
}


/////////////////////////////////////////////////
//doSort
//Arguments
// sortBy-the thing to sort with (i.e. a1 / c2)
function doSort(sortBy){
	if(sortBy == ""){
		//sort all the methods by method name
		var methods = new Array();
		for(x in activeMethods){
			var methodId = activeMethods[x];
			methods.push(methodId);
		}
		for(var j = 0; j < methods.length; j++){
			var minimumIndex = j;
			var minimumName = mn[methods[j]];
			for(var k = j+1; k < methods.length; k++){
				if(minimumName > mn[methods[k]]){
					minimumIndex = k;
					minimumName = mn[methods[k]];
				}
			}
			var temp = methods[j];
			methods[j] = methods[minimumIndex];
			methods[minimumIndex] = temp;
		}
		return new Array(0,methods);
	}
	var property = "";
	//then we're doing accuracy
	if(sortBy.charAt(0) == "a"){
		property = document.getElementById('acc').value; 
	}
	else if(sortBy.charAt(0) == "c"){
		property = document.getElementById('comp').value;
	}
	var targetDataSet = parseInt(sortBy.substring(1));
	//this is basically a selection sort, (upgrade this is we have >100 methods)

	//basically we filter the active methods into implementing and unimplementing
	//categories, then sort the unimplementers by method name and the implementers
	//by data
	var unimplementingMethods = new Array();
	var implementingMethods = new Array();
	for(x in activeMethods){
		var methodId = activeMethods[x];
		if(!isDefined(methodId,targetDataSet)){
			unimplementingMethods.push(methodId);
		}
		else{
			implementingMethods.push(methodId);
		}
	}
	//alright sort the unimplementers by method name
	for(var j = 0; j < unimplementingMethods.length-1; j++){
		var minimumIndex = j;
		var minimumName = mn[unimplementingMethods[j]];
		for(var k = j+1; k < unimplementingMethods.length; k++){
			if(minimumName > mn[unimplementingMethods[k]]){
				minimumIndex = k;
				minimumName = mn[unimplementingMethods[k]];
			}
		}
		var temp = unimplementingMethods[j];
		unimplementingMethods[j] = unimplementingMethods[minimumIndex];
		unimplementingMethods[minimumIndex] = temp;
	}
	//so now we have to sort the implementing methods
	for(var j = 0; j < implementingMethods.length-1; j++){
		var minimumIndex = j;
		var minimumData = fd[implementingMethods[j]][targetDataSet][property];
		for(var k = j+1; k < implementingMethods.length; k++){
			var relevantData = fd[implementingMethods[k]][targetDataSet][property];
			if(isBetter(relevantData,minimumData,property)){
				minimumIndex = k;	
				minimumData = relevantData;
			}
		}
		var temp = implementingMethods[j];
		implementingMethods[j] = implementingMethods[minimumIndex];
		implementingMethods[minimumIndex] = temp;
	}
	var sorted = new Array(implementingMethods.length+unimplementingMethods.length);
	for(var j = 0; j < implementingMethods.length; j++){
		sorted[j] = implementingMethods[j];
	}
	var impLength = implementingMethods.length;
	for(var j =0; j < unimplementingMethods.length; j++){
		sorted[impLength+j] = unimplementingMethods[j];
	}
	return new Array(impLength-1,sorted);
}

//the last sort we did
//this indicates the column to remove or add sort (i.e. highlighting) colors
lastSort = "";		

/////////////////////////////////////////////////
//removeLastSortColors
//removes the last sort's highlighting colors  
function removeLastSortColors(){
	if(lastSort == ""){
		return;
	} 
	var type = lastSort.charAt(0);
	var dataSetId = lastSort.substr(1);
	for(x in activeMethods){
		var i = activeMethods[x];
		try{
			document.getElementById("m"+i+"d"+dataSetId+type).style.backgroundColor="";
		}
		catch(err){}
	}	
}
/////////////////////////////////////////////////
//updateSortColors
//update the sorting highlight colors
//Arguments:
//  sortBy-the new sorting column that should be highlighted
function updateSortColors(sortBy){
	var type = sortBy.charAt(0);
	var dataSetId = sortBy.substr(1);
	removeLastSortColors();
	for(x in activeMethods){	
		i = activeMethods[x];
		//we need a try/catch because if the test doesn't exist, then the id isn't defined
		try{
			document.getElementById("m"+i+"d"+dataSetId+type).style.backgroundColor="#7CCD7C";
		}
		catch(err){}
	}
	lastSort = sortBy; 
}

/////////////////////////////////////////////////
//updateFilters
//update the display of boxes in the table:
//1) filter out stuff (which may not appear
//in the final version)
//2) sort the stuff with another method
//3) update the row colors 
function updateFilters(){
	var sortBy = determineSort();
	updateSortColors(sortBy);
	var dataSetFilterOn = new Array();
	var sortData = doSort(sortBy);
	var includedUpTo = sortData[0];
	var positionsToMethods = sortData[1]; 
	for(x in activeMethods){	
		m = activeMethods[x];
		document.getElementById("row"+m).style.top = "0px";
		document.getElementById("row"+m).style.visibility='hidden';
	} 
	var rowNum;
	for(var i = 0; i < positionsToMethods.length; i++){
		var rowNum = positionsToMethods[i];
		var row = document.getElementById("row"+rowNum);
		row.style.top = getRowOffset(i)+"px";
		row.style.visibility = "visible";
		if(i != (positionsToMethods.length-1)){
			row.style.borderBottomWidth = "0px";
		}
		else{
			row.style.borderBottomWidth = borderWidth+"px";
		}
		nonPublic = 0;
		for(var j = 0; j < nonPublicMethods.length; j++){
			if(nonPublicMethods[j] == rowNum){
				nonPublic = 1;
				break;
			} 
		}
		if(nonPublic){
			try{
				row.style.backgroundColor = "#ffff99";
			}
			catch(err){}
		}
		else if((i%2) == 0){
			try{
				row.style.backgroundColor = "#c8e8ff";
			}
			catch(err){
			}
		}
		else{
			try{
				row.style.backgroundColor = "#FFFFFF";
			}
			catch(err){}
				
		}
	}
}




//*********************************************************
//View manipulation section
//The functions below control the views (the window/div 
//handling, the image options, etc.) 



//viewOption
//0 is views 1 and 2
//1 is view 1 and ground truth
//2 is view 2 and ground truth
//3 is vrml (not implemented yet)
//4 is unsigned accuracy
//5 is signed accuracy
//6 is unsigned completeness
//7 is signed completeness
//8 is unsigned cdf
//9 is signed cdf
//10 is text data
viewOption = 0;


/////////////////////////////////////////////////
//updateDataDisplay
//updates what we're displaying and then updates the view elements
//accordingly  
function updateDataDisplay(){
	viewOption = document.getElementById("dataView").value;
	if(viewOption == 0){
		showHelp(0);
	}
	else{
		clearHelp(0);
	}
	updateImageSize();	
}

/////////////////////////////////////////////////
//getRelevantContainerSize
//determine the container size based on the viewOption and
//image size
//Returns:
//  A 2 element array (width,height) specifying the container
//  height.
function getRelevantContainerSize(){
	viewOption = document.getElementById("dataView").value; 
	imageSizeIndex = document.getElementById("imageSize").value;
	if((viewOption >= 0) && (viewOption < 3)){
		return containerSizes[imageSizeIndex];
	}
	if((viewOption >= 4) && (viewOption < 10)){
		//return containerSizes[Number(imageSizeIndex >= 1)];
		//override preferences
		return containerSizes[1];
	}
	if((viewOption == 10)){
		return containerSizes[1];
	}
} 

/////////////////////////////////////////////////
//updateImageSize
//updates the image size variable, and resizes data containers
function updateImageSize(){
	imageSizeIndex = document.getElementById("imageSize").value;
	imageSize = containerDescriptors[imageSizeIndex];
	//resize the displaying elements
	if((dataTarget === dataBox) && (dataBox != null)){
		//if our target is the data box on the main page
		drawDataBox();
		updateViewElements();
	}
	else if(dataTarget !== null){
		var containerSize = getRelevantContainerSize();
		//if our target is the window
		//try to resize the window, but if that fails, open a new one
		dataWindow.resizeTo(containerSize[0],containerSize[1]);
		var currentOuterWidth = dataWindow.outerWidth;
		if(Math.abs(currentOuterWidth - containerSize[0]) > 50){
			//we neeed to set to null to avoid the onunload event handler
			//which switches to the box-in-the-main-page mode
			dataWindow.onunload = null;
			dataWindow.close();
			updateDataTarget();
		}	
		else{
			updateViewElements();
		}
	}
}


//data target is the place to which we're writing, i.e.
//the window or the side div
dataTarget = null;

//An array of divs which functions can change
viewElements = null;

//We're going to write this into the window
winscript  = "<script type='text/javascript'>\ndocument.onkeydown = opener.gShowGroundTruth;\ndocument.onkeyup = opener.gRevertGroundTruth;\n";
//firefox fires the window.onunload just before the page closes
//ie seems to do it it afterwards, when it seems that the window is null and thus has no opener
//so we'll set both
winscript += "window.onunload = window.onbeforeunload = opener.gOnCloseOfComparisonWindow;\n";
//winscript += "window.onunload = opener.gOnCloseOfComparisonWindow;\n";
//firefox chokes on having script inside of the script body...
//even if it's in quotes.  

//and write in the aliases for unveilImages in the child window
winscript += "unveilImages = opener.gUnveilImages;\nhideImages=opener.gHideImages;\nhideText=opener.gHideText;\n";
winscript += "<";
winscript += "/script>";

/////////////////////////////////////////////////
//onCloseOfComparisonWindow
//What to do when the comparison window closes (i.e. move all the
//data to the page)
function onCloseOfComparisonWindow(){
	document.getElementById("dataTarget").checked = false;
	dataWindow = null;
	updateDataTarget();
}

var gOnCloseOfComparisonWindow = this.onCloseOfComparisonWindow;

/////////////////////////////////////////////////
//updateDataTarget
//update the place where we're updating images
//closes/opens windows and redraws the side div
function updateDataTarget(){
	if(document.getElementById("dataTarget").checked == false){
		if(dataWindow != null){
			dataWindow.close();
			dataWindow = null;
		}
		drawDataBox();
		document.onkeydown = catchKeyDown;
		document.onkeyup = catchKeyUp;	
	}
	else{
		var containerSize = getRelevantContainerSize();
		destroyDataBox();
		dataWindow = window.open("about:blank","datawindow","width="+containerSize[0]+",height="+containerSize[1]+",toolbar=no,location=no,addressbar=no,resizable=yes"); 
		if(dataWindow === null){
			document.getElementById("dataTarget").checked = false;
			//reopen the box
			updateDataTarget();
			alert("The window couldn't be opened. Please enable popups\nor select \"Data in New Window\" to continue");
			return;
		}
		dataWindow.document.write("<html><head><title>Data Window</title>");
		dataWindow.document.write("<style type='text/css'>body{font-size:12px;}</style>");
		dataWindow.document.write("</head>");
		dataWindow.document.write(winscript);
		dataWindow.document.write("<body>");
		dataWindow.document.write("<div id='display' style='position:absolute; width: 100%; height: 100%; padding: 0px; border: 0px; top: 0px; left: 0px;'></div>");
		dataWindow.document.write("</body></html>");
		dataWindow.document.close();
		dataWindow.focus();
		dataTarget = dataWindow.document.getElementById("display");
	} 
	updateViewElements();
}

/////////////////////////////////////////////////
//openComparisonWindow
//set our data target to a window, and then run updateDataTarget
//called by the open data window button.
function openComparisonWindow(){
	document.getElementById("dataTarget").checked = true;
	updateDataTarget();
}

//dataBox is the side div on the main page
dataBox = null;
//dataWindow is the data child window we open
dataWindow = null;


/////////////////////////////////////////////////
//drawDataBox
//draw the side data box
function drawDataBox(){
	var relContainerSizes = getRelevantContainerSize();
	dataBox.style.left='637px';
	dataBox.style.top=topOffset+"px";
	dataBox.style.border="1px solid red";
	dataBox.style.width = relContainerSizes[0]+"px";
	dataBox.style.height = relContainerSizes[1]+"px";
	inner = "";
	dataBox.innerHTML = inner;
	dataBox.style.visibility='visible';
	dataTarget = dataBox
}

/////////////////////////////////////////////////
//destroyDataBox
//hide the data box
function destroyDataBox(){
	dataBox.style.visibility = 'hidden';
	dataBox.style.left = '0px';
	dataBox.style.top = '0px';
}

//the current data set, we use this to figure out which ground truth image we need
currentDataSetId = -1;
//the last images we loaded
//we need this for when we revert the ground truth images
lastImages = new Array(); 
//the last title we showed
//we need this for when we revert the ground truth stuff
lastTitle = "";


/////////////////////////////////////////////////
//getRelevantDocument
//determine which document has the div we're going to manipulate
//Returns:
//  the window's document node if we're showing data in the window, the
//  current document node otherwise
function getRelevantDocument(){
	if(dataTarget == dataBox){
		return document;
	}
	else if(dataTarget != null){
		//then it's the window's display element
		return dataWindow.document;
	}
}

////////////////////////////////////////////
//getStandard2ImageViewElements
//Common code for setting the viewElements for the 2 image pairs
function getStandard2ImageViewElements(){
	var targetDoc = getRelevantDocument();
	viewElements[0] = targetDoc.getElementById("img1");
	viewElements[1] = targetDoc.getElementById("img2");
	viewElements[2] = targetDoc.getElementById("text1");
}

/////////////////////////////////////////////////
//getStandard1ImageViewElements
//Common code for setting the viewElements for the 1 image graphs
function getStandard1ImageViewElements(){
	var targetDoc = getRelevantDocument();
	viewElements[0] = targetDoc.getElementById("text1");
	viewElements[1] = targetDoc.getElementById("img1");
}

/////////////////////////////////////////////////
//getTextViewElements
//Code to set the view elements for the text data display option
function getTextViewElements(){
	var targetDoc = getRelevantDocument();
	for(var i = 0; i < vIdElts.length; i++){
		viewElements[i] = targetDoc.getElementById(vIdElts[i]);
	}
	//the text display thing
	viewElements[vIdElts.length] = targetDoc.getElementById("text1");
}

/*

We can either procedurally generate this and give future maintainers a hope of
being able to maintain this, or we can have it be a huge static block of text
with absolute positioning, which would be a pain to maintain.

*/

//text data element/display
//in order to cut redundant typing of style stuff, we're going to procedurally
//generate the text data display
//Believe it or not, but initial investigations into typing the entire style
//stuff out indicated that procedural generation was significantly less work
//(plus it eliminates those oh so fun-to-find mistypes!)
//ElementData is [text to show, left offset, top offset, width, id]
//each row has a height of vRowHeight


//Identifiers for the varying boxes of the text data display. They correspond
//to the properties of each test. 
vIdElts = new Array("accuracyMeanUnsignedDistance","completenessMeanUnsignedDistance","accuracyMedianUnsignedDistance","completenessMedianUnsignedDistance","accuracyMeanSignedDistance","completenessMeanSignedDistance","accuracyMedianSignedDistance","completenessMedianSignedDistance","accuracyStandardDeviation","completenessStandardDeviation","translationMagnitude","translationVector","rotationAxis","rotationAngle","removeMedian","confThresh","onEdgeDist","processorName","processorFrequency","normalizedRuntime","runtime");

vRowHeight = 20;
vElements = new Array();
vElements[0]  = new Array("<b>Statistics</b>",0,0,300,"");
vElements[1]  = new Array("<b>Alignment Transformation</b>",305,0,260,"");
vElements[2]  = new Array(" ",0,vRowHeight,100,"");
vElements[3]  = new Array("<b>Accuracy (mm) </b>",100,vRowHeight,90,"");
vElements[4]  = new Array("<b>Completeness (mm) </b>",190,vRowHeight,110,"");
vElements[5]  = new Array("Translation Magnitude",305,vRowHeight,120,"");
vElements[6]  = new Array(" ",425,vRowHeight,140,"translationMagnitude");
vElements[7]  = new Array("Mean Unsigned",0,vRowHeight*2,100,"");
vElements[8]  = new Array(" ",100,vRowHeight*2,90,"accuracyMeanUnsignedDistance");
vElements[9]  = new Array(" ",190,vRowHeight*2,110,"completenessMeanUnsignedDistance");
vElements[10] = new Array("Translation Vector",305,vRowHeight*2,120,"");
vElements[11] = new Array(" ",425,vRowHeight*2,140,"translationVector");
vElements[12] = new Array("Mean Signed",0,vRowHeight*3,100,"");
vElements[13] = new Array(" ",100,vRowHeight*3,90,"accuracyMeanSignedDistance");
vElements[14] = new Array(" ",190,vRowHeight*3,110,"completenessMeanSignedDistance");
vElements[15] = new Array("Rotation Angle",305,vRowHeight*3,120,"");
vElements[16] = new Array(" ",425,vRowHeight*3,140,"rotationAngle");
vElements[17] = new Array("Median Unsigned",0,vRowHeight*4,100,"");
vElements[18] = new Array(" ",100,vRowHeight*4,90,"accuracyMedianUnsignedDistance");
vElements[19] = new Array(" ",190,vRowHeight*4,110,"completenessMedianUnsignedDistance");
vElements[20] = new Array("Rotation Axis",305,vRowHeight*4,120,"");
vElements[21] = new Array(" ",425,vRowHeight*4,140,"rotationAxis");
vElements[22] = new Array("Median Signed",0,vRowHeight*5,100,"");
vElements[23] = new Array(" ",100,vRowHeight*5,90,"accuracyMedianSignedDistance");
vElements[24] = new Array(" ",190,vRowHeight*5,110,"completenessMedianSignedDistance");
vElements[25] = new Array("<b>Global Parameters</b>",305,vRowHeight*5,260,"");
vElements[26] = new Array("Standard Deviation",0,vRowHeight*6,100,"");
vElements[27] = new Array(" ",100,vRowHeight*6,90,"accuracyStandardDeviation");
vElements[28] = new Array(" ",190,vRowHeight*6,110,"completenessStandardDeviation");
vElements[29] = new Array("removeMedian",305,vRowHeight*6,120,"");
vElements[30] = new Array(" ",425,vRowHeight*6,140,"removeMedian");
vElements[31] = new Array("confThresh",305,vRowHeight*7,120,"");
vElements[32] = new Array(" ",425,vRowHeight*7,140,"confThresh");
vElements[33] = new Array("onEdgeDist",305,vRowHeight*8,120,"");
vElements[34] = new Array(" ",425,vRowHeight*8,140,"onEdgeDist");
//timings
vElements[35] = new Array("<b>Time Information</b>",0,(vRowHeight*7),300,"");
vElements[36] = new Array("Processor",0,(vRowHeight*8),150,"");
vElements[37] = new Array("Frequency (GHz)",0,(vRowHeight*9),150,"");
vElements[38] = new Array("",150,(vRowHeight*8),150,"processorName");
vElements[39] = new Array("",150,(vRowHeight*9),150,"processorFrequency");
vElements[40] = new Array("Normalized Time at 3.0GHz",0,(vRowHeight*11),150,"");
vElements[41] = new Array("",150,(vRowHeight*11),150,"normalizedRuntime");
vElements[42] = new Array("Runtime (H:M:S)",0,(vRowHeight*10),150,"");
vElements[43] = new Array("",150,(vRowHeight*10),150,"runtime");

var v = "<div style='font-size:12px;'>";
//the position relative thing is ESSENTIAL. Otherwise the internal divs don't recognize
//the bounding div and then overlap the textinfo thing
v += "<div id='text1'>Text Info</div><div style='position:relative;'>";
for(var i = 0; i < vElements.length; i++){
	var elt = vElements[i];
	var vAdd = "<div style='position:absolute;left:"+elt[1]+"px;top:"+elt[2]+"px;width:"+elt[3]+"px;border: 1px solid black;";
	vAdd += "text-align: center;height:"+vRowHeight+"px;font-size:10px;' id='"+elt[4]+"'>"+elt[0]+"</div>\n";
	v += vAdd;
}
v += "</div></div>";




//The base paths of the ground truth images (before the appending of the size portion of the url
var gtmImages = new Array();
gtmImages[0] = new Array();
gtmImages[0][0] = 'http://vision.middlebury.edu/mview/eval/data/gtm/dino/view1';
gtmImages[0][1] = 'http://vision.middlebury.edu/mview/eval/data/gtm/dino/view2';
gtmImages[1] = new Array();
gtmImages[1][0] = 'http://vision.middlebury.edu/mview/eval/data/gtm/temple/view1';
gtmImages[1][1] = 'http://vision.middlebury.edu/mview/eval/data/gtm/temple/view2';


/////////////////////////////////////////////////
//showDefaultImages
//Show the default images for various view options
//These are loaded between when the user selects a new view option
//(or the page loads and one is selected for him) and when the user
//actually mouseovers the table
function showDefaultImages(){
	if(viewOption == 0){
		viewElements[0].src=gtmImages[0][0]+imageSize+".jpg";
		viewElements[1].src=gtmImages[0][1]+imageSize+".jpg";
		viewElements[2].innerHTML = "<b>Ground Truth</b>";
	}
	else if((viewOption == 1) || (viewOption ==2)){		
		viewElements[0].src=gtmImages[0][(viewOption-1)]+imageSize+".jpg";	
		viewElements[1].src=gtmImages[0][(viewOption-1)]+imageSize+".jpg";	
		viewElements[2].innerHTML = "<b>Ground Truth and Ground Truth</b>";
	}
	else if((viewOption > 2) && (viewOption < 10)){
		viewElements[1].src="def.png";
	}
	
}

/////////////////////////////////////////////////
//updateViewElements
//update the view elements references to correspond
//to the current data selection
function updateViewElements(){
	if((viewElements !== undefined) && (viewElements !== null)){
		for(var i = 0; i < 3; i++){
			if(viewElements[i] !== undefined){
				try{
					viewElements[i].style.visibility = 'hidden';
				}	
				catch(ex){}
			}
		}	
	}

	var wWidth = imageSizes[imageSizeIndex][0];
	var wHeight = imageSizes[imageSizeIndex][1];

	//we generate this code in this subroutine because the width of the image containers depend on user selection
	//and thus aren't known at page load time, because they can change.
	var sLeftOffset = String(Number(wWidth+3)); 
	var s = "<div id='text1' style='position:relative;'></div>";
	s += "<div id='holder1' style='position: absolute; top:15px; left: 0px;'><img id='img1' src='' onload='unveilImages(1)' width='";
	s += wWidth+"' height='"+wHeight+"'></div><div id='holder2' style='position:absolute; left:"+sLeftOffset;
	s += "px;top: 15px;width:"+wWidth+"px;height:"+wHeight+"px;'><img id='img2' src='' width='";
	s += wWidth+"' height='"+wHeight+"' onload='unveilImages(2)'> \n</div>\n";
	s += "<div id='cover1' style='position: absolute; top:15px; left: 0px; background-color: #000000;height:"+wHeight+"px;width:"+wWidth+"px;z-index:4;color:#ffffff;'>Loading...</div>";
	s += "<div id='cover2' style='position: absolute; top:15px; left: "+sLeftOffset+"px; background-color: #000000;height:"+wHeight+"px;width:"+wWidth+"px;z-index: 4;color:#ffffff;''>Loading...</div>";

	//var graphImageSize = graphImageSizes[Number(imageSizeIndex >= 1)];  					
	var graphImageSize = graphImageSizes[1];	
	//this is for the 1 image graphs
	//we have this here just so the html is all together
	var s2 = "<div id='text1' style='position:relative;left:0px;top:0px'></div><div id='holder1' top:25px; left:0px;'><img id='img1' style='width:"+graphImageSize[0]+"px;";
	s2 += "height:"+graphImageSize[1]+"px;' onload='unveilImages(1)'></div>";
	s2 += "<div id='cover1' style='position:absolute; left:0px; top:15px;background-color: #FFFFFF;height:"+graphImageSize[1]+"px;width:"+graphImageSize[0]+"px;color:#000000;z-index:4;visibility:hidden'>Loading...</div>";

	viewElements = new Array();
	if((viewOption >= 0) && (viewOption < 3)){
		dataTarget.innerHTML = s;	
		getStandard2ImageViewElements();
	}
	else if((viewOption >= 4) && (viewOption < 10)){
		dataTarget.innerHTML = s2;
		getStandard1ImageViewElements();
		
	}
	else if(viewOption ==  10){
		dataTarget.innerHTML = v;
		getTextViewElements();
	}
	showDefaultImages();
}	


//The way this mouseoverCounter/loaded deal works:
//So we delay covering the image for a tiny bit (in case it's in the cache and thus doesn't
//need to be loaded, and thus shouldn't be loaded). So, we only cover if loaded = 0. (It's
//set to 0 when we in theory should hide the images, and set to 1 when an image loads). However
//there's potential havoc here: if we mouseover multiple boxes, it seems possible that a load
//event might fire for an image which isn't actually being displayed. To guard against this
//we have a mouseoverCounter, so that when the hide events go out, a counter is bumped up
//and as they come back in it's decremented. Thus, if mouseovercounter = 1 (or, after decrementing
//it = 0), then we're getting the last call, so we should handle it.  

//two variables involved in counting mouseover hide requests 
var mouseoverCounter = 0;
var loaded = 0;
//involved in making sure that changeImage fires only once
//per mouseover of a box
var lastHandledMethod = -1;
var lastHandledDataSet = -1;

var gUnveilImages = this.unveilImages;
var gHideImages = this.hideImages;
var gHideText = this.hideText;

/////////////////////////////////////////////////
//unveilImages
//unveil the images (by removing the black blockers)
function unveilImages(imId){
	var targetDoc = getRelevantDocument();
	targetDoc.getElementById('text1').style.visibility='visible';
	if((viewOption >= 0) && (viewOption < 3)){
		if((imId == 1) || (viewOption == 0)){
			targetDoc.getElementById('cover'+imId).style.visibility='hidden';
			loaded = 1;
		}
		if((imId == 1) && (viewOption != 0)){
			//i.e. the method image loaded
			targetDoc.getElementById('cover2').style.visibility='hidden';
			loaded = 1;
		}	
	}
	else if((viewOption >= 4) && (viewOption < 10)){
		targetDoc.getElementById('cover1').style.visibility='hidden';
		loaded = 1;
	}
} 

/////////////////////////////////////////////////
//hideImages
//hide images (by putting up the black blockers) 
function hideImages(){
	var targetDoc = getRelevantDocument();
	mouseoverCounter--;
	if(mouseoverCounter == 0){
		if(loaded == 0){
			targetDoc.getElementById('cover1').style.visibility='visible';
			if((viewOption >= 0) && (viewOption < 3)){
				targetDoc.getElementById('cover2').style.visibility='visible';
			} 
		}
	}
}

/////////////////////////////////////////////////
//hideText
function hideText(){
	var targetDoc = getRelevantDocument();
	if(mouseoverCounter == 0){
		if(loaded == 0){
			targetDoc.getElementById('text1').style.visibility='hidden';
		}
	}
}


//rather than have a huge switch thing in changeImage we take advantage of our 
//data option numbering scheme and thus just use the id for the display to determine
//an index for the array, and then derive a file name
var graphSources = new Array("histTestToGTMU.png","histTestToGTM.png","histGTMToTestU.png","histGTMToTest.png","cdfBothU.png","cdfBoth.png");

/////////////////////////////////////////////////
//changeImage
//changes the displayed image to what we want to display for a given method/dataSet id
//Arguments:
//  methodId - the method of the data to be displayed
//  dataSetId - the data set of the data to be displayed
function changeImage(methodId,dataSetId){
	if((methodId == lastHandledMethod) && (dataSetId == lastHandledDataSet)){
		return;
	}
	if((viewElements !== undefined) && (viewElements !== null)){	
		//this has to go here, because otherwise the onload event for the images will fire
		//while the script is still inside the if statements and then it'll load
		//and then the loaded = 0 and then hide statements will execute and hide it
		loaded = 0;
		mouseoverCounter++;
		if(viewOption == 0){
			var methodName = mn[methodId];
			var dataSetDirectory = ds[dataSetId];
			var baseDirectory = imageDirectory+methodId+"/"+dataSetDirectory+"/";
			lastImages[0] = baseDirectory+"view1"+imageSize+".jpg";
			lastImages[1] = baseDirectory+"view2"+imageSize+".jpg";
			lastTitle = methodName;
			viewElements[0].src = lastImages[0]; 
			viewElements[1].src = lastImages[1]; 
			viewElements[2].innerHTML = "<b>"+methodName+"<b/>"; 
			currentDataSetId = dataSetId;
		}
		else if((viewOption == 1) || (viewOption ==2)){
			var methodName = mn[methodId]; 
			var dataSetDirectory = ds[dataSetId];
			var baseDirectory = imageDirectory+methodId+"/"+dataSetDirectory+"/";
			var gtmId = determineGroundTruthUrls(dataSetId);
			lastImages[0] = baseDirectory+"view"+viewOption+imageSize+".jpg";
			lastTitle = mn[methodId]+" and Ground Truth";
			viewElements[0].src = lastImages[0]; 
			viewElements[1].src = gtmImages[gtmId][(viewOption-1)]+imageSize+".jpg";
			viewElements[2].innerHTML = "<b>"+lastTitle+"</b>";
			currentDataSetId = dataSetId;
		}	
		else if((viewOption > 3) && (viewOption <= 9)){
			var methodName = mn[methodId];
			var dataSetDirectory = ds[dataSetId];
			var dataSetName = dn[dataSetId];
			var imageTarget = imageDirectory+methodId+"/"+dataSetDirectory+"/"+graphSources[viewOption-4];
			viewElements[0].innerHTML = "<b>"+methodName+" "+dataSetName+"</b>";
			viewElements[1].src = imageTarget;	
		}	
		setTimeout("hideText()",40);
		setTimeout("hideImages()",100);
		lastHandledMethod = methodId;
		lastHandledDataSet = dataSetId;
	}
}



//*********************************************************
//Text Data section
//The following functions have to do with dynamically loading the
//information (i.e. parameters statistics) about the methods 


/////////////////////////////////////////////////
//splitTime
//Splits a time value in seconds into hours minutes and seconds components
//Arguments:
//  time - the number of seconds
//Returns:
//  a 3-item array [0] => hours [1] => minues [2] => seconds
function splitTime(time){
	var retTime = new Array();
	retTime[0] = Math.floor(time / 3600);
	time = time % 3600;
	var mins = Math.floor(time / 60);
	if(mins < 10){
		mins = "0"+mins;
	}	
	retTime[1] = mins;
	var secs = time % 60;
	if(secs < 10){
		secs = "0"+secs;
	}
	retTime[2] = secs;
	return retTime;
}

/////////////////////////////////////////////////
//textDataLoaded
//Checks whether the text data for a test is loaded 
//Arguments:
//  methodId - the method of the test to check for loaded text data
//  dataSetId - the data set of the test to check for loaded text data
//Returns:
//  True if the text data for that test has been loaded, False otherwise.
function textDataLoaded(methodId,dataSetId){
	return fd[methodId][dataSetId]['rotationAxis'] !== undefined;
}

//stores info for the last text view request so that we don't get an ajax cascade
//i.e. the user moves their mouse down the column and then we send out 30 requests
//and then they all come back sequentially and cause the references to go haywire

//the last text view request, so we can cancel it 
lastTextViewRequest = 0;
lastTextViewRequestAlive = 0;



/////////////////////////////////////////////////
//doTextViewDataRequest
//Get the text data for a particular test,
//update the page, and store it for future use.
//Unlike the other example of ajax on this page, the data displaying
//is done in the data handling function, mainly because it takes 3 lines
//compared to the dozens of the other one 
//Arguments:
//  methodId - the method of the test
//  dataSetId - the data set of the test
function doTextViewDataRequest(methodId,dataSetId){
	dataHandler = function(returnData){
		try{
		var splitData = returnData.split("\n");
		var parsedData = new Array();
		var transVec = new Array();
		var rotationAxis = new Array();
		var procInfo = new Array();
		//this is nearly clean, but the two vector types need to be reassembled
		//client side after transfer
		//and also the time stuff
		for(var i = 0; i < splitData.length; i++){
			var lineData = splitData[i].split(" ");
			var id = lineData[0];
			if(id.indexOf("translationVector") != -1){
				transVec[id.charAt(id.length-1)] = Number(lineData[3]);	
			}
			else if(id.indexOf("rotationAxis") != -1){
				rotationAxis[id.charAt(id.length-1)] = Number(lineData[3]);
			}
			else if(id.indexOf("processorName") != -1){
				var jstr = "";
				for(var j = 3; j < lineData.length; j++){
					jstr += lineData[j]+" ";
				}	
				procInfo[0] = jstr;
			}		
			else if(id.indexOf("processorFrequency") != -1){
				procInfo[1] = formatFloatString(lineData[3],4);
			}
			else if(id.indexOf("processorEstimate") != -1){
				procInfo[2] = Number(lineData[3]);
			}
			//catches both runtime and normalizedRuntime, both of which 
			//need to be converted into human form
			else if(id.indexOf("untime") != -1){
				var humanTime = splitTime(Number(lineData[3]));
				parsedData[id] = humanTime[0]+":"+humanTime[1]+":"+humanTime[2];
			}
			else{ 
				parsedData[id] = Number(lineData[3]);
			}
		}
		//in the end, we basically want a copy of the processor info
		//in both procInfo and proc[methodId]. We just test to see
		//from where to where we should copy it	
		if(procs[methodId] == 0){
			procs[methodId] = procInfo;	
		}
		else{
			procInfo = procs[methodId];
		}
		//copy it
		parsedData['processorName'] = procInfo[0];
		parsedData['processorFrequency'] = procInfo[1];
		parsedData['processorEstimate'] = procInfo[2];
		if(procInfo[2] > 0){
			parsedData['processorFrequency'] += "&nbsp;(estimate)";
			parsedData['runtime'] += " &nbsp;(estimate)";
			parsedData['normalizedRuntime'] += " &nbsp;(estimate)";
		}
		else if(parsedData['estimate'] >= 2){
			parsedData['runtime'] += " &nbsp;(estimate)";
			//because if the counted runtime is an estimate, it's
			//absurd to claim that normalizing the runtime made it
			//not one
			parsedData['normalizedRuntime'] += " &nbsp;(estimate)";
		}
		//alright, now we need to deal with estimates
		//because we don't actually have an estimate field on our page
		//but we do need the data
		delete parsedData['estimate'];
		delete parsedData['processorEstimate'];
		//change the parsedData to the textual representation of the vectors and other things
		//which require units (most other units are displayed in a column in the html table)
		parsedData['translationVector'] = "["+transVec[0]+" "+transVec[1]+" "+transVec[2]+"] mm";
		parsedData['rotationAxis'] = "["+rotationAxis[0]+" "+rotationAxis[1]+" "+rotationAxis[2]+"]";
		parsedData['rotationAngle'] += " degrees";
		parsedData['translationMagnitude'] += "mm";	
		//now update the the elemenets on the page
		for(var i = 0; i < vIdElts.length; i++){
			viewElements[i].innerHTML = parsedData[vIdElts[i]];
		}
		viewElements[vIdElts.length].innerHTML = "<b>"+mn[methodId]+" - "+dn[dataSetId]+" &nbsp; statistics</b>";
		//merge the two associative arrays so we'll have this data for future use
		var target = fd[methodId][dataSetId]; 
		for(property in parsedData){
			target[property] = parsedData[property];
		}
		//we don't need to load the estimate information into fd because
		//we've already modified the time fields so that they have (estimate) in them

		//now set lastTextViewRequest to 0 indicating that there isn't a preceding text view
		//ajax request so we can safely proceed without cancelling the older one
		lastTextViewRequestAlive = 0;
		}
		catch(ex){
			//alert(ex);
		}
			
	}
	//we might be requesting after another request. In that case, then the old request is invalid
	//so we need to cancel it
	if(lastTextViewRequestAlive != 0){	
		if(lastTextViewRequest != 0){
			lastTextViewRequest.onreadystatechange = null;
			lastTextViewRequest.abort();
			lastTextViewRequestAlive = 0;
		}
	} 	
	var procAppend = "";
	if(procs[methodId] === 0){
		procAppend = "&proc=1";
	}
	lastTextViewRequest = getAjax();
	lastTextViewRequestAlive = 1;
	oneWayDataRequest(lastTextViewRequest,"dataServer.php?tvdata=1&fm="+methodId+"&fd="+dataSetId+procAppend,"GET",dataHandler);
}

/////////////////////////////////////////////////
//updateTextViewData
//Update the text data displayed. 
//If we have the data, display it, if not, put in a request for it
//Arguments:
//  methodId - the method of the test
//  dataSetId - the data set of the test
function updateTextViewData(methodId,dataSetId){
	if(textDataLoaded(methodId,dataSetId)){
		var relevantData = fd[methodId][dataSetId];
		for(var i = 0; i < vIdElts.length; i++){
			viewElements[i].innerHTML = relevantData[vIdElts[i]];
		}
		viewElements[vIdElts.length].innerHTML = "<b>"+mn[methodId]+" - "+dn[dataSetId]+" &nbsp; statistics</b>";

	}
	else{
		doTextViewDataRequest(methodId,dataSetId);
	}	
}	

/////////////////////////////////////////////////
//showReference
//Update the reference box to show a given method's reference
//Arguments:
//  methodId - the method id of the method to get the reference for
function showReference(methodId){
	if(refs[methodId] === 0){
		dataHandler = function(data){
			refs[methodId] = "<b>Reference:</b> &nbsp; "+data;
			document.getElementById('refBox').innerHTML = refs[methodId];
		}
		oneWayDataRequest(getAjax(),"dataServer.php?rdata=1&mid="+methodId,"GET",dataHandler);
	}
	else{
		document.getElementById('refBox').innerHTML = refs[methodId];
	}			
} 



/////////////////////////////////////////////////
//showNormalizedTime
//Update the normalized time
function showNormalizedTime(methodId,dataSetId){
        //alright check to see if we have the info in fd already
        if(fd[methodId][dataSetId]['normalizedRuntime'] !== undefined){
                //then we have all the data we need
                document.getElementById('timeDisplay').innerHTML = fd[methodId][dataSetId]['normalizedRuntime'];
        }
        else{
                dataHandler = function(returnData){
                        var splitData = returnData.split("!!");
                        var humanTimeTuple = splitTime(splitData[0]);
                        var estimate = Number(splitData[1]);
                        var humanTime = humanTimeTuple[0]+":"+humanTimeTuple[1]+":"+humanTimeTuple[2];
                        if(estimate >= 1){
                                humanTime += " (estimate)";
                        }
                        fd[methodId][dataSetId]['normalizedRuntime'] = humanTime;
                        //we dont' have to worry about messing up
                        //the workings of textDataLoaded as that checks rotationAxis rather than normalizedRuntime
                        document.getElementById('timeDisplay').innerHTML = humanTime;                                       
                }
                oneWayDataRequest(getAjax(),"dataServer.php?ntime=1&fm="+methodId+"&fd="+dataSetId,"GET",dataHandler);
        }
}


//*********************************************************
//UI Ground Truth Interface
//The following functions are used for changing the display
//of the the ground truth images 


/////////////////////////////////////////////////
//determineGroundTruthUrls
//Determines which ground truth urls to use.
//While having a function seems silly, this hides the mechanism of determining which urls to use
//and simplifies future changes
//Arguments:
//  dataSetId - the data set in question
//Returns:
//  the index of the set of ground truth urls which are to be used
function determineGroundTruthUrls(dataSetId){
	if(dataSetId <= 3){
		return 1;
	}
	return 0;
}
	
/////////////////////////////////////////////////
//showGroundTruth
//Shows the ground truth in the viewElements, if applicable 
function showGroundTruth(){
	var gtmId = determineGroundTruthUrls(currentDataSetId);
	if(viewOption == 0){
		//we're only displaying a separate ground truth thing 
		//if we're displaying non-ground truth images  
		viewElements[0].src = gtmImages[gtmId][0]+imageSize+".jpg";
		viewElements[1].src = gtmImages[gtmId][1]+imageSize+".jpg";	
		viewElements[2].innerHTML = "<b>Ground Truth</b>";
	}
	if((viewOption == 1) || (viewOption == 2)){
		viewElements[0].src = gtmImages[gtmId][(viewOption-1)]+imageSize+".jpg";
		viewElements[2].innerHTML = "<b>Ground Truth</b>";
	}
}

/////////////////////////////////////////////////
//revertGroundTruth
//Reverts the ground truth back to the originally displayed
//images if applicable
function revertGroundTruth(){
	if(viewOption == 0){
		viewElements[0].src = lastImages[0];
		viewElements[1].src = lastImages[1];
		viewElements[2].innerHTML = "<b>"+lastTitle+"</b>";
	}
	if((viewOption == 1) || (viewOption == 2)){
		viewElements[0].src = lastImages[0];
		viewElements[2].innerHTML = "<b>"+lastTitle+"</b>";
	}	
}

//we need references for our child window to be able 
//use the show/revert ground truth
//g stands for global 
var gShowGroundTruth = this.showGroundTruth;
var gRevertGroundTruth = this.revertGroundTruth;


//*********************************************************
//UI event handlers 
//keyDown etc.

/////////////////////////////////////////////////
//catchKeyDown
//Handle key down events
//Arguments:
//  evt - some browsers pass the event to the handler
//        instead of IE's window.event method
function catchKeyDown(evt){
	var keyId;
	if(window.event){
		keyId = window.event.keyCode;
	}
	else{
		keyId = evt.keyCode;
	}		
	if(viewOption < 3){
		//71 is g
		if(keyId == 71){
			showGroundTruth();
		}
	} 
}

/////////////////////////////////////////////////
//catchKeyUP
//Handle key up events
//Arguments:
//  evt - some browsers pass events through this
function catchKeyUp(evt){
	var KeyId;
	if(window.event){
		keyId = window.event.keyCode;
	}
	else{
		keyId = evt.keyCode;
	}	
	if(viewOption < 3){
		//71 is g
		if(keyId == 71){
			revertGroundTruth();
		}
	}	
}

/////////////////////////////////////////////////
//mouseoverHandler
//Handle mouseover events from the table. Just
//calls other functions
//Arguments:
//  methodId - the method the mouse is over (row)
//  dataSetId - the data set the mouse is over (column)
function mouseoverHandler(methodId,dataSetId){
	if(isDefined(methodId,dataSetId)){
		if((viewOption >= 0) && (viewOption < 10)){
			changeImage(methodId,dataSetId);
		}
		if((viewOption == 10)){
			updateTextViewData(methodId,dataSetId);
		}
		showNormalizedTime(methodId,dataSetId);
	}
	showReference(methodId);
}

/////////////////////////////////////////////////
//mouseDownHandler
//Handle mousedown events from the table. Just calls
//other functions.
//Arguments:
//  methodId - the method we're handling
//  dataSetId - the data set we're handling
function mousedownHandler(methodId,dataSetId){
	if(isDefined(methodId,dataSetId)){
		if((viewOption < 3)){
			showGroundTruth();
		}
	}
}

/////////////////////////////////////////////////
//mouseupHandler
//Handle mouseup events from the table. Just calls
//other functions.
//Arguments:
//  methodId - the method we're handling
//  dataSetId - the data set we're handling 
function mouseupHandler(methodId,dataSetId){
	if(isDefined(methodId,dataSetId)){
		if((viewOption < 3)){
			revertGroundTruth();
		}
	}
}


//*********************************************************
//Help display code


//Help display
helpContent = new Array();
helpContent.push("Holding down your mouse button or G will show ground truth images");

lastHelp = -1;



/////////////////////////////////////////////////
//showHelp
//shows a particular help text
//Arguments:
//  helpId - the id of the help text to show
function showHelp(helpId){
	if(helpId < helpContent.length){
		document.getElementById('helpContent').innerHTML = helpContent[helpId];		
		lastHelp = helpId;
	} 
}
/////////////////////////////////////////////////
//clearHelp
//hides a help text if it's the current one being displayed
//This is so that if say:
//User switches to view 1 and view 2, help message about clicking appears
//User switches to view 1 and ground truth, help message about clicking is wrong
//  at this point and should be cleared
//However, if the user did something which put another message on the screen, then
//we don't want to remove that, so we only clear if the to clear id is the same as
//the last thing put on the screen
//Arguments:
//   helpId - the help to clear if it was the last put up
function clearHelp(helpId){
	if(helpId == lastHelp){
		document.getElementById('helpContent').innerHTML = "Mousing over any portion of a method's row will show its reference";
	}
}


//*********************************************************
//

function selectMethods(){ 
	var methodSelectBox = document.getElementById('methodSelectBox');
	if(methodSelectBox == null){
		return;
	}
	methodSelectBox.style.left = "200px";
	methodSelectBox.style.top = "200px";
	methodSelectBox.style.visibility='visible';
} 

function hideSelectMethods(){
	document.getElementById('methodSelectBox').style.visibility='hidden';
}

//*********************************************************
//Window load/close code 

/////////////////////////////////////////////////
//makeVisible
//make a particular element visible
//Arguments:
//  elt - the element to make visible
function makeVisible(elt){
	elt.style.visibility = 'visible';
}

//The divs we need to unveil if scripting is on (i.e. they're hidden
//so that if scripting is off, then we don't look like idiots)
//more are added in the body of the main page 
var toUnveil = new Array('selector','windowControl','refBox','helpBox','timeBox','newPageBox');

/////////////////////////////////////////////////
//unveil
//Unveil all of the divs which we've hidden so we can
//handle not having javascript in a graceful manner
function unveil(){
	for(var i = 0; i < toUnveil.length; i++){
		makeVisible(document.getElementById(toUnveil[i])); 
	} 
}

/////////////////////////////////////////////////
//toDoOnLoad
//Stuff we want to do on load.
//This is here rather than being global level code lower in the document
//not only because it's more correct this way, but also because placing
//it in the lower portion of the body seems to cause IE to crash, apparently
//from some race condition.   
function toDoOnLoad(){
	if(canHandleSite()){
		unveil();
		dataBox = document.getElementById("dataBox");
		updateDataTarget();
		updateImageSize();
		updateFilters();
		updateBoxes(0);
		updateDataDisplay();
	}
}
//set the above function to run when the page loads
window.onload=toDoOnLoad;

/////////////////////////////////////////////////
//cleanUp
//What we have to do to clean up the page when the user
//browses away from or closes the table page
function cleanUp(){
	if(dataWindow !== null){
		if(dataWindow.closed == false){
			dataWindow.close();
		}
	}
}

//run when we're closing. This is set to fire on two events
//because different browsers seem to handle this differently
//sometimes 
window.onunload = window.onbeforeunload = cleanUp;


/////////////////////////////////////////////////
//openRuntimeTable
//Open the runtime table page
function openRuntimeWindow(){
	window.open("runtimeTable.php","");
}

/////////////////////////////////////////////////
//openReferenceWindow
//Open the reference table page
function openReferenceWindow(){
	window.open("referenceTable.php","");
}








//*********************************************************
//Browser capability check
	

function getException(){
	document.getElementById('asdf').style.fail.asdf.awef = 'awef';
}


/////////////////////////////////////////////////
//canHandleSite
//Checks whether the user's current browser can handle
//the site.
//Returns:
//  True if it can, False otherwise. 
function canHandleSite(){
	//basically do an assume true, prove false approach
	//first candidate is that they're using something like IE4 or some other
	//old browser that doesn't support document.getElementById
	//This is actually testable! Firefox actually sets document.getElementById to null
	//if you ask it to
	if(!document.getElementById){
		document.write("<p>Sorry, you need to upgrade your browser in order to see this site!</p>");
		document.write("<p>An alternate site is also available <a href='tableAlt.php'>here</a></p>");
		document.close();
		return false;	
	} 
	//alright, so they are using a modern enough browser: now set the faillog
	//to hidden
	document.getElementById('failMessageBox').style.visibility = 'hidden';
	//second candidate is Ajax (we pickup on a lack of javascript
	//by making the site (except for the noscript stuff) invisible
	//and then setting it to visible with javascript
	try{
		getAjax();
	}
	catch(ex){
		document.getElementById('specificMessage').innerHTML = specificMessagePrefix+"Your browser does not appear to support Ajax (Asynchronous JavaScript and XML)";
		document.getElementById('failMessageBox').style.visibility = 'visible';
		return false;		
	}
	return true;
}






