// ajax function to get datasets from server
// eventually this will support users, so we can show different ones 
// to different people
// ajax callback is handled by handleLoadDatasets()
function loadDatasets()
{
	var dataVars = new Object;
	dataVars.hgh2_mode = "getDatasets";
	$.ajax({
			type: "POST",
			url: "/cgi-bin/hgHeatmap2",
			data: dataVars,
			dataType: "json",
			success: handleLoadDatasets,
			error: ajaxError,
			cache: true
	});
	
}
// function to handle the callback from loadDatasets
// expects a JSON obj in the data param
function handleLoadDatasets(data)
{
	if(data.err)
		return hghError('none',data.err);
	
	var firstDS = '';
	
	$.each(data, function(group,dataset)
		{
			var row1,row2;
			$('#datasetsInnerTableBody').append("<tr class='heatmapControlHeader'><th colspan='5'>"+group+"</th></tr>");
		$.each(dataset, function(i,dataset)
		{
			if((i % 5) == 0)
			{
				if(row1)
					$('#datasetsInnerTableBody').append(row1).append(row2);
				
				row1 = $("<tr></tr>");
				row2 = $("<tr></tr>");
			}
			var label = "";
			if(dataset.local_url != "")
				label = $("<a href='"+dataset.local_url+"' target='_blank'>"+dataset.shortLabel+"</a>");
			else
				label = dataset.shortLabel;
			var labelCell = $("<td></td>").append(label).appendTo(row1);
			var select = $("<select></select>").attr('id',dataset.name).attr('name',dataset.name)
			.attr('fullname',dataset.longLabel).attr('num',dataset.N).addClass('hiddenText')
			.change(function() {updateDataset(this); })
			.append("<option value='hide'>Hide</option><option value='heatmap'>Heatmap</option><option value='summary'>Summary</option>");
			//select.append(options);
			var selectCell = $("<td></td>").append(select).appendTo(row2);
			if(firstDS == '')
				firstDS = dataset.name;
			
		});
		if(row1)
			$('#datasetsInnerTableBody').append(row1).append(row2);
	});
	
	$("#datasetsInnerTable select").each(function(index, domElement) { updateDataset(domElement); });

	if(firstDS != '')
	{
		$("#"+firstDS).val('heatmap');
		updateDataset(document.getElementById(firstDS));
	}

}

function updateDataset(el)
{
	if($(el).val() != "hide")
	{
		// hide the tooltip if it's up 
		clearTimeout(toolTipTimeout);
		if($("#tooltip").css('display') != "none") { $("#tooltip").fadeOut('fast'); }
		
		$(el).removeClass('hiddenText').addClass('normalText');
		
		// enable dataset, mark it as loading and make the ajax call
		var dsID = el.name;
		$('#'+dsID+"AjaxError").remove();
		if(datasetsFeatureData[dsID].visible && !datasetsFeatureData[dsID].justLoaded)
		{
			//$('#'+dsID+"HeatmapRow").remove();
			//$('#'+dsID+"HeatmapHeader").after("<tr id='"+dsID+"HeatmapLoading' ><td colspan='4' align='center'><img src='loading.gif'/></td></tr>");
			//console.log($('#'+dsID+"HeatmapRow").position());
			var loadingOverlay = $("<div></div>").attr("id",dsID+"LoadingOverlay").css("position","absolute");
			if($('#'+dsID+"HeatmapImage").length > 0)
			{
			loadingOverlay.css("top",$('#'+dsID+"HeatmapImage").position().top);
			var divLeft = $('#'+dsID+"HeatmapImage").position().left + 2;
			loadingOverlay.css("left",divLeft);
			var width = $('#'+dsID+"HeatmapImage").width() - 5;
			loadingOverlay.width(width);
			loadingOverlay.height($('#'+dsID+"HeatmapImage").height());
			loadingOverlay.css("background","white");
			loadingOverlay.css("opacity",0.75);
			var imgLeft = ($('#'+dsID+"HeatmapImage").width() / 2) - 110;
			var imgTop = ($('#'+dsID+"HeatmapImage").height() / 2 ) - 10;
			$("<img src='loading.gif'/>").css("position","relative").css('left',imgLeft).css('top',imgTop).appendTo(loadingOverlay);
			}
			else
			{
			loadingOverlay.css("left",divLeft);
			var width = $('#'+dsID+"HeatmapImage1").width() - 5;
			loadingOverlay.width(width);
			var height = $('#'+dsID+"HeatmapImage1").height() + $('#'+dsID+"HeatmapImage2").height();
			loadingOverlay.height(height);
			loadingOverlay.css("background","white");
			loadingOverlay.css("opacity",0.75);
			var imgLeft = ($('#'+dsID+"HeatmapImage1").width() / 2) - 110;
			var imgTop = (height / 2 ) - 10;
			}
			loadingOverlay.appendTo("body");
		}
		else
		{
			$('#'+dsID+"Heatmap").remove();

			var newRow = jQuery("<tbody id='"+dsID+"Heatmap'><tr id='"+dsID+"HeatmapHeader'><td colspan='4' style='text-align:center;' class='heatmapDatasetHeader'>"+$(el).attr('fullName')+" (N="+$(el).attr('num')+")<img src='settings.gif' id='"+dsID+"SettingsImg' class='clickme'/></td></tr><tr id='"+dsID+"HeatmapLoading' ><td colspan='4' align='center'><img src='loading.gif'/></td></tr></tbody>");
			newRow.hide().appendTo($('#heatmapTable')).fadeIn('normal');
			$('#'+dsID+"SettingsImg").click(function() { $('#'+dsID+"DatasetSettings").toggle(); } );
		}
		
		var dataVars = new Object;
		
		if($(el).val() == "heatmap")
		{
			dataVars.hgh2_mode = "getHeatmaps";
			datasetsFeatureData[dsID].dropdown = "heatmap";
		}
		else
		{
			dataVars.hgh2_mode = "getSummary";
			datasetsFeatureData[dsID].dropdown = "summary";
			var subgroup1Features = [];
			var subgroup1Mins = [];
			var subgroup1Maxs = [];
			var subgroup1FeatureCodes = [];
			var subgroup1Codes = [];
			var subgroup2Features = [];
			var subgroup2Mins = [];
			var subgroup2Maxs = [];
			var subgroup2FeatureCodes = [];
			var subgroup2Codes = [];
			
			$('#'+dsID+"FeatureSubgroupListTableBody tr").each( function() {
				if($(this).attr('minA'))
				{
				subgroup1Features.push($(this).attr('featName'));
				subgroup2Features.push($(this).attr('featName'));
				subgroup1Mins.push($(this).attr('minA'));
				subgroup2Mins.push($(this).attr('minB'));
				subgroup1Maxs.push($(this).attr('maxA'));
				subgroup2Maxs.push($(this).attr('maxB'));
				}
				else
				{
				var featureName = $(this).attr('featName');
				var codesA =$(this).attr('codesA').split(",");
				$.each(codesA,function(i,code) {
					if(code)
					{
						subgroup1Codes.push(code);
						subgroup1FeatureCodes.push(featureName);
					}
					});
				var codesB =$(this).attr('codesB').split(",");
				$.each(codesB,function(i,code) {
					if(code)
					{
						subgroup2Codes.push(code);
						subgroup2FeatureCodes.push(featureName);
					}
					});
				}
			});
			dataVars.hgh2_subgroup1Features  =  subgroup1Features.join(",");
			dataVars.hgh2_subgroup1Mins =  subgroup1Mins.join(",");
			dataVars.hgh2_subgroup1Maxs =  subgroup1Maxs.join(",");
			dataVars.hgh2_subgroup2Features  =  subgroup2Features.join(",");
			dataVars.hgh2_subgroup2Mins =  subgroup2Mins.join(",");
			dataVars.hgh2_subgroup2Maxs =  subgroup2Maxs.join(",");
			dataVars.hgh2_subgroup1CodedFeatures = subgroup1FeatureCodes.join(",");
			dataVars.hgh2_subgroup2CodedFeatures = subgroup2FeatureCodes.join(",");
			dataVars.hgh2_subgroup1Codes = subgroup1Codes.join(",");
			dataVars.hgh2_subgroup2Codes = subgroup2Codes.join(",");
			datasetsFeatureData[dsID].subgroup1Features  =  subgroup1Features;
			datasetsFeatureData[dsID].subgroup1Mins =  subgroup1Mins;
			datasetsFeatureData[dsID].subgroup1Maxs =  subgroup1Maxs;
			datasetsFeatureData[dsID].subgroup2Features  =  subgroup2Features;
			datasetsFeatureData[dsID].subgroup2Mins =  subgroup2Mins;
			datasetsFeatureData[dsID].subgroup2Maxs =  subgroup2Maxs;
			datasetsFeatureData[dsID].subgroup1CodedFeatures = subgroup1FeatureCodes;
			datasetsFeatureData[dsID].subgroup2CodedFeatures = subgroup2FeatureCodes;
			datasetsFeatureData[dsID].subgroup1Codes = subgroup1Codes;
			datasetsFeatureData[dsID].subgroup2Codes = subgroup2Codes;
		}
		
		if($('#'+dsID+'DatasetColorRadio').val() && $('#'+dsID+'DatasetColorRadio').val().length > 0)
		{
			var colArray = $('#'+dsID+'DatasetColorRadio').val().split(";");
			dataVars.hgh2_highColor = colArray[0];
			dataVars.hgh2_zeroColor = colArray[1];
			dataVars.hgh2_lowColor = colArray[2];
			
		}
		
		dataVars.hgh2_dataset = dsID;		
		dataVars.hgHeatmap_tableName = dsID; // for legacy
		dataVars.hgh2_type = $('#dsDisplayAs').val();
		if($('#dsDisplayAs').val()=='geneset')
		{
			var geneSetA = [];
			$('#geneSearchDisplayOL span').each(function() {
					geneSetA.push($(this).text());
		 	});
			dataVars.hgh2_geneSets = geneSetA.join(",");
			datasetsFeatureData[dsID].geneSets = geneSetA;
		}
		else
		{
			/*if($("#chromStartPos").val())
				dataVars.hgh2_chromStart = $("#chromStartPos").val();
			if($("#chromEndPos").val())
				dataVars.hgh2_chromEnd = $("#chromEndPos").val();	
			*/
			if($("#chromPos").val())
			{
				var chromPosArray = $("#chromPos").val().replace("CHR","chr").split(/-/);
				dataVars.hgh2_chromStart = chromPosArray[0];
				dataVars.hgh2_chromEnd = chromPosArray[1];
			}
			datasetsFeatureData[dsID].chromStart = dataVars.hgh2_chromStart;
			datasetsFeatureData[dsID].chromEnd = dataVars.hgh2_chromEnd;
		}
		dataVars.hgh2_feature = true;
		
		// figure out sorting
		if(datasetsFeatureData[dsID] && datasetsFeatureData[dsID].sortFeatures)
		{
			dataVars.hgh2_sortType = "feature";
			dataVars.hgh2_featureSort = datasetsFeatureData[dsID].sortFeatures.join(",");
			dataVars.hgh2_featureSortDir = datasetsFeatureData[dsID].sortFeaturesDirs.join(",");			 
		}
		else if(datasetsFeatureData[dsID] && datasetsFeatureData[dsID].sortGene)
		{
			dataVars.hgh2_sortType = "gene";
			dataVars.hgh2_sortGene = datasetsFeatureData[dsID].sortGene;
			dataVars.hgh2_sortDir = datasetsFeatureData[dsID].sortDir;
		}
 		else if(datasetsFeatureData[dsID] && datasetsFeatureData[dsID].sortChromStart)
		{
			dataVars.hgh2_sortType = "chrom";
			dataVars.hgh2_sortChromStart = datasetsFeatureData[dsID].sortChromStart;
			dataVars.hgh2_sortChromEnd = datasetsFeatureData[dsID].sortChromEnd;
			dataVars.hgh2_sortDir = datasetsFeatureData[dsID].sortDir;
		}
		
		
		if(datasetsFeatureData[dsID] && datasetsFeatureData[dsID].enabledFeatures)
		{
			var featureList = [];
			$.each(datasetsFeatureData[dsID].enabledFeatures,function(i,feat) {
					featureList.push(feat.name);
			});
			dataVars.hgh2_featureList = featureList.join(",");
		}
		//console.log(dataVars);

		if(current_user_id > 0)
		{
			dataVars.hgh2_tokenId = current_token_id;
			dataVars.hgh2_userId = current_user_id;
		}

		$.ajax({
				type: "POST",
				url: "/cgi-bin/hgHeatmap2",
				data: dataVars,
				dataType: "json",
				success: insertImagesForDataset,
				error: ajaxError,
				datasetId: el.name,
				viewType: dataVars.hgh2_type,
				justLoaded: datasetsFeatureData[dsID].justLoaded,
				cache: true
		});
		
	}
	else
	{
		// disable dataset
		var id = el.name+"Heatmap";
		$('#'+id).fadeOut("normal",function() {$(this).remove();});
		datasetsFeatureData[el.name] = new Object(); // reset the data array
		$(el).removeClass('normalText').addClass('hiddenText');
	}
}
function ajaxError(XMLHttpRequest, textStatus, errorThrown)
{
	//console.log(textStatus);
	//console.log(errorThrown);
	var dsID = this.datasetId;
	$('#'+dsID+"Heatmap").remove();
	$('#'+dsID+"HeatmapLoading").remove();
	$('#'+dsID+"LoadingOverlay").remove();
	//$('#'+id+"HeatmapHeader").fadeOut("normal",function() {$(this).remove();});
	//$('#'+dsID+'Heatmap').append("<tr id='"+dsID+"AjaxError'><td class='ajaxError'>An error has occured.  Please try again later. (status: "+textStatus+"; err: "+errorThrown+")</td></tr>");
	$('#error').show();
	$('#errorMsg').html("dsID: "+dsID+"\nstatus: "+textStatus+"\nerr: "+errorThrown);
}
function hghError(dsID,errMsg)
{
	$('#'+dsID+"Heatmap").remove();
	$('#'+dsID+"HeatmapLoading").remove();
	$('#'+dsID+"LoadingOverlay").remove();
	$('#error').show();
	$('#errorMsg').html("dsID: "+dsID+"\nerr: "+errMsg);
}

var toolTipTimeout;
var keypressTimeout;
var datasetsFeatureData = new Array();

function insertImagesForDataset(data)
{
	var dsID = this.datasetId;
	//console.log(data);
	var newEl;

	if(data.err)
		return hghError(dsID,data.err);	
	
	// cache the results in a global obj
	if(!datasetsFeatureData[dsID])
		datasetsFeatureData[dsID] = new Object();
	datasetsFeatureData[dsID].type = this.viewType;
	datasetsFeatureData[dsID].chromInfo = data.chromInfo;
	datasetsFeatureData[dsID].geneSetInfo = data.geneSetInfo;
	datasetsFeatureData[dsID].enabledFeatures = data.featureInfo;
	datasetsFeatureData[dsID].samples = data.samples;
	datasetsFeatureData[dsID].dsID = dsID;
	datasetsFeatureData[dsID].sampleHeight = data.sampleHeight;
	
	$('#'+dsID+"HeatmapLoading").remove();
	$('#'+dsID+"LoadingOverlay").remove();
	
	if(datasetsFeatureData[dsID].visible && !this.justLoaded)
	{				
		if($("#"+dsID+"HeatmapImage").length > 0)
		{
			$('#'+dsID+"HeatmapImageScaleCell").empty();
			$("#"+dsID+"HeatmapImageCell").html("");
			if(data.heatmap)
			{
				$("<img/>").addClass("heatmapImg").addClass("clickme").attr("dataset",dsID).attr("src",data.heatmap).attr("id",dsID+"HeatmapImage").appendTo("#"+dsID+"HeatmapImageCell");

				if(data.scale)
					$("<img/>").attr("src",data.scale).appendTo('#'+dsID+"HeatmapImageScaleCell");
			}
			else
			{	
				if (data.heatmap1)
				{
					$("<img/>").addClass("heatmapImg").addClass("clickme").attr("dataset",dsID).attr("src",data.heatmap1).attr("id",dsID+"HeatmapImage1").appendTo("#"+dsID+"HeatmapImageCell");
				}
				if (data.heatmap2)
				{
					$("<br/>").appendTo("#"+dsID+"HeatmapImageCell");
					$("<img/>").addClass("heatmapImg").addClass("clickme").attr("dataset",dsID).attr("src",data.heatmap2).attr("id",dsID+"HeatmapImage2").appendTo("#"+dsID+"HeatmapImageCell");
				}
				if (data.scale1)
				{
					$("<img/>").attr("src",data.scale1).appendTo('#'+dsID+"HeatmapImageScaleCell");
				}
				if (data.scale2)		
				{
					$("<br/>").appendTo("#"+dsID+"HeatmapImageScaleCell");
					$("<img/>").attr("src",data.scale2).appendTo('#'+dsID+"HeatmapImageScaleCell");
				}
			}

		}
		else
		{
			$('#'+dsID+"HeatmapImageScaleCell").empty();
			$("#"+dsID+"HeatmapImageCell").html("");
			if(data.heatmap)
			{
				$("<img/>").addClass("heatmapImg").attr("dataset",dsID).addClass("clickme").attr("src",data.heatmap).attr("id",dsID+"HeatmapImage").appendTo("#"+dsID+"HeatmapImageCell");
				if(data.scale)
					$("<img/>").attr("src",data.scale).appendTo('#'+dsID+"HeatmapImageScaleCell");
			}
		
			if (data.heatmap1)
			{
				$("<img/>").addClass("heatmapImg").addClass("clickme").attr("dataset",dsID).attr("src",data.heatmap1).attr("id",dsID+"HeatmapImage1").appendTo("#"+dsID+"HeatmapImageCell");
			}
			if (data.heatmap2)
			{
				$("<br/>").appendTo("#"+dsID+"HeatmapImageCell");
				$("<img/>").addClass("heatmapImg").addClass("clickme").attr("dataset",dsID).attr("src",data.heatmap2).attr("id",dsID+"HeatmapImage2").appendTo("#"+dsID+"HeatmapImageCell");
			}
			if (data.scale1)
			{
				$("#"+dsID+"HeatmapImage1").attr("src",data.heatmap1).addClass("clickme");
				$("<img/>").attr("src",data.scale1).appendTo('#'+dsID+"HeatmapImageScaleCell");
			}
			if (data.scale2)	
			{
				$("#"+dsID+"HeatmapImage2").attr("src",data.heatmap2).addClass("clickme");
				$("<br/>").appendTo("#"+dsID+"HeatmapImageScaleCell");
				$("<img/>").attr("src",data.scale2).appendTo('#'+dsID+"HeatmapImageScaleCell");
			}
		}
		$("#"+dsID+"HeatmapRowInnerLabel").remove();
		$("#"+dsID+"HeatmapRowInnerFeatureCell").empty();		        
		if(data.feature)
		{
		  $("<img/>").addClass("heatmapImg").addClass("clickme").attr("dataset",dsID).attr("src",data.feature).attr("id",dsID+"FeatureImage").appendTo("#"+dsID+"HeatmapRowInnerFeatureCell");
		  //$("#"+dsID+"FeatureImage").attr("src",data.feature);
		}
		if(data.feature0)
		{
		$("#"+dsID+"HeatmapRowInnerFeatureCell").empty();		        
		  $("<img/>").addClass("heatmapImg").addClass("clickme").attr("dataset",dsID).attr("src",data.feature0).attr("id",dsID+"FeatureImage1").appendTo("#"+dsID+"HeatmapRowInnerFeatureCell");
		}
		if (data.feature1)
		{
		  $("<br/>").appendTo("#"+dsID+"HeatmapRowInnerFeatureCell");
		  $("<img/>").addClass("heatmapImg").addClass("clickme").attr("dataset",dsID).attr("src",data.feature1).attr("id",dsID+"FeatureImage0").appendTo("#"+dsID+"HeatmapRowInnerFeatureCell");
		}
	}
	else
	{
		// add the main dataset table after the dataset's header
		newEl = $("<tr></tr>").attr("id",this.datasetId+"HeatmapRow");
		var cellEl = $("<td></td>").appendTo(newEl);
		var table = $("<table></table>").attr("id",this.datasetId+"HeatmapRowTable").addClass('heatmapRowTable').appendTo(cellEl);
		var tbody = $("<tbody></tbody>").attr("id",this.datasetId+"HeatmapRowTableBody").appendTo(table);	 
		$('#'+dsID+"HeatmapHeader").after(newEl);
		
		// add the heatmap image row
		newEl = $("<tr></tr>").attr("id",dsID+"HeatmapRowInner");
		var scaleCell = $("<td></td>").attr('id',dsID+"HeatmapImageScaleCell").addClass('ImageScaleCell');
		if(data.scale)
			$("<img/>").attr("src",data.scale).appendTo(scaleCell);
		scaleCell.appendTo(newEl);
		var cellEl = $("<td></td>").attr("id",dsID+"HeatmapImageCell").css('width',700).appendTo(newEl);
		cellEl.css('height',data.samples.length * data.sampleHeight);
		if(data.heatmap)
			$("<img/>").addClass("heatmapImg").addClass("clickme").attr("dataset",dsID).attr("src",data.heatmap).attr("id",dsID+"HeatmapImage").appendTo(cellEl);
		else
		{
			$("<img/>").addClass("heatmapImg").addClass("clickme").attr("dataset",dsID).attr("src",data.heatmap1).attr("id",dsID+"HeatmapImage1").appendTo(cellEl);
			$("<br/>").appendTo(cellEl);
			$("<img/>").addClass("heatmapImg").addClass("clickme").attr("dataset",dsID).attr("src",data.heatmap2).attr("id",dsID+"HeatmapImage2").appendTo(cellEl);
		}
		$("<td></td>").appendTo(newEl);
		$('#'+dsID+"HeatmapRowTableBody").append(newEl);
		
		// add the cell for the subgrp img
		newEl = $("<td></td>").attr("id",dsID+"HeatmapRowInnerSubgroupCell");
		$('#'+dsID+"HeatmapRowInner").append(newEl);
		
		// add the feature img cell
		newEl = $("<td></td>").attr('rowspan',3).attr("id",dsID+'HeatmapRowInnerFeatureCell').css('width',250);
		newEl.css('height',(data.samples.length * data.sampleHeight) + 85);
		if(data.feature)
			$("<img/>").addClass('heatmapImg').addClass("clickme").attr("src",data.feature).attr("dataset",dsID).attr("id",dsID+"FeatureImage").appendTo(newEl);
		$('#'+dsID+"HeatmapRowInner").append(newEl);
		
		// add the stats row hidden after the images row
		newEl = $("<tr></tr>").attr("id",this.datasetId+"HeatmapRowInnerStats").hide();
		$("<td></td>").attr("id",dsID+"HeatmapRowInnerStatsScale").appendTo(newEl);
		$("<td></td>").attr("id",dsID+"HeatmapRowInnerStatsCell").appendTo(newEl);
		//$("<td></td>").attr("colspan","4").appendTo(newEl);
		$('#'+dsID+"HeatmapRowTableBody").append(newEl);

	}

	// build the row to contain the chrom/geneset labels
	newEl = $("<tr></tr>").attr("id",this.datasetId+"HeatmapRowInnerLabel");
	$("<td></td>").appendTo(newEl);
	$("<td></td>").attr("id",dsID+"HeatmapRowInnerLabelCell").appendTo(newEl);

	//$("<td></td>").attr("colspan","4").appendTo(newEl);
	$('#'+dsID+"HeatmapRowTableBody").append(newEl);
	//$("<div></div>").addClass("HeatmapRowInnerLabelCellDiv").attr("id",dsID+"HeatmapRowInnerLabelCellDiv").appendTo("#"+dsID+"HeatmapRowInnerLabelCell");
	$("<div></div>").attr("id",dsID+"HeatmapRowInnerLabelCellDiv").addClass("HeatmapRowInnerLabelCellDiv").appendTo("#"+dsID+"HeatmapRowInnerLabelCell");
	
	getHeatmapLabels(dsID);
	if($('#'+dsID+"DatasetSettings").length == 0)
		setupDatasetSettings(dsID);
	
	if(data.chromInfo && data.chromInfo.length == 1)
		$('#viewInGenomeBrowser').attr('disabled',0);
	else
		$('#viewInGenomeBrowser').attr('disabled',1);
	
	if(data.chromInfo)
	{
		var totalBases = 0;
		$.each(data.chromInfo, function(i,chromArray) {
				totalBases += chromArray.baseEnd - chromArray.baseStart;
		});
		$("#chromPosSize").html("size "+addCommas(totalBases)+" bp.");
	}
	else
		$("#chromPosSize").html('');
	
	// add the click handler to the heatmap img
	$('#'+dsID+"HeatmapImage").click(heatmapClick);
	$('#'+dsID+"HeatmapImage1").click(heatmapClick);
	$('#'+dsID+"HeatmapImage2").click(heatmapClick);
		
	// if we haven't pulled up the main list of features for the dataset yet, 
	// do the necessary ajax query
	if(datasetsFeatureData[dsID].visible != true || this.justLoaded)
	{
		var dataVars = new Object;
		dataVars.hgh2_mode = "getFeatures";
		dataVars.hgh2_dataset = dsID;
		if(current_user_id > 0)
		{
			dataVars.hgh2_tokenId = current_token_id;
			dataVars.hgh2_userId = current_user_id;
		}
		$.ajax({
				type: "POST",
				url: "/cgi-bin/hgHeatmap2",
				data: dataVars,
				dataType: "json",
				success: setupFeatures,
				error: ajaxError,
				datasetId: dsID,
				cache: true
		});
		//datasetsFeatureData[dsID].justLoaded = false;
	}
	else
	{
		// if the data heatmap already exists on the page, just update the features
		// and call the subgrouping (for new sorts, etc)
		updateFeatures(dsID);
		//refreshImageWithSubgrouping(dsID); //this is called already at the end of updateFeatures()
	}
	
	
	// this needs to be set late here case we use it earlier in the fxn
	datasetsFeatureData[dsID].visible = true;	 
	
	// add the two tooltip hover events, and the feature click sort event
	//$('#'+this.datasetId+"HeatmapImage").hover(function() { $(this).mousemove(heatmapImageMouseMove); } ,function() { clearTimeout(toolTipTimeout); $(this).unbind('mousemove'); $("#tooltip").fadeOut('fast') } );
	//$('#'+this.datasetId+"HeatmapImage1").hover(function() { $(this).mousemove(heatmapImageMouseMove); } ,function() { clearTimeout(toolTipTimeout); $(this).unbind('mousemove'); $("#tooltip").fadeOut('fast') } );
	//$('#'+this.datasetId+"HeatmapImage2").hover(function() { $(this).mousemove(heatmapImageMouseMove); } ,function() { clearTimeout(toolTipTimeout); $(this).unbind('mousemove'); $("#tooltip").fadeOut('fast') } );
	$(".heatmapImg").hover(function() { $(this).mousemove(heatmapImageMouseMove); } ,function() { clearTimeout(toolTipTimeout); $(this).unbind('mousemove'); $("#tooltip").fadeOut('fast') } );
	$('#'+this.datasetId+"FeatureImage").hover(function() { $(this).mousemove(featureImageMouseMove); } ,function() { clearTimeout(toolTipTimeout); $(this).unbind('mousemove'); $("#tooltip").fadeOut('fast') } );
	$('#'+this.datasetId+"FeatureImage0").hover(function() { $(this).mousemove(featureImageMouseMove); } ,function() { clearTimeout(toolTipTimeout); $(this).unbind('mousemove'); $("#tooltip").fadeOut('fast') } );
	$('#'+this.datasetId+"FeatureImage1").hover(function() { $(this).mousemove(featureImageMouseMove); } ,function() { clearTimeout(toolTipTimeout); $(this).unbind('mousemove'); $("#tooltip").fadeOut('fast') } );
	$('#'+this.datasetId+"FeatureImage").click(sortFeature);
	
}

// !!DEPRECATED!!
// function to build out the labels based on the chrom / geneset info
// some minor hacks to make sure the divs don't bounce to the next line
// !!DEPRECATED!!
function buildHeatmapLabels(dsID)
{
	if(datasetsFeatureData[dsID].type == "chrom")
	{
		$.each(datasetsFeatureData[dsID].chromInfo, function(i,chrom)
			{
				var marginRight = 1;
				var width = chrom.pixelEnd - chrom.pixelStart;
				if(datasetsFeatureData[dsID].chromInfo.length == (i+1))
				{
					marginRight = 0;
					width--;
				}
				if(width > 0) // sometimes you get to a 0 width if something gets wonky
				{
					$("<div></div>")
					.addClass("heatmapLabel").css('width',width).css('margin-right',marginRight)
					.html(chrom.chromNum).appendTo('#'+dsID+'HeatmapRowInnerLabelCellDiv');
				}
			});
	}
	else
	{
		$.each(datasetsFeatureData[dsID].geneSetInfo, function(i,gs)
			{
				var marginRight = 1;
				var width = gs.pixelEnd - gs.pixelStart;
				if(datasetsFeatureData[dsID].geneSetInfo.length == (i+1))
				{
					marginRight = 0;
					//width--;
				}
				$("<div></div>").html(gs.name)
				.addClass("heatmapLabel").css('width',width).css('margin-right',marginRight)
				.appendTo('#'+dsID+'HeatmapRowInnerLabelCellDiv');
			});
		
	}	 
	
}

// function to get labels for the chrom / geneset
// replaced the fxn buildHeatmapLabels()
function getHeatmapLabels(dsID)
{
	if(datasetsFeatureData['label'] && datasetsFeatureData['label'].img)
		return putHeatmapLabelIn(dsID);
		
	var dataVars = new Object;
	dataVars.hgh2_mode = "getLabels";
	dataVars.hgh2_type = $('#dsDisplayAs').val();
	if($('#dsDisplayAs').val()=='geneset')
	{
		var geneSetA = [];
		$('#geneSearchDisplayOL span').each(function() {
				geneSetA.push($(this).text());
		});
		dataVars.hgh2_geneSets = geneSetA.join(",");
		datasetsFeatureData[dsID].geneSets = geneSetA;
	}
	else
	{
		if($("#chromPos").val())
		{
			var chromPosArray = $("#chromPos").val().replace("CHR","chr").split(/-/);
			dataVars.hgh2_chromStart = chromPosArray[0];
			dataVars.hgh2_chromEnd = chromPosArray[1];
		}
		datasetsFeatureData[dsID].chromStart = dataVars.hgh2_chromStart;
		datasetsFeatureData[dsID].chromEnd = dataVars.hgh2_chromEnd;
	}

	$.ajax({
			type: "POST",
			url: "/cgi-bin/hgHeatmap2",
			data: dataVars,
			dataType: "json",
			success: getHeatmapLabels_callback,
			error: ajaxError,
			datasetId: dsID,
			cache: true
	});
	
}

function getHeatmapLabels_callback(data)
{
	// cache this
	datasetsFeatureData['label'] = new Object;
	datasetsFeatureData['label'].img = data.labels;
	putHeatmapLabelIn(this.datasetId);
}

function putHeatmapLabelIn(dsID)
{
	if(!datasetsFeatureData['label'] || !datasetsFeatureData['label'].img)
		return false;
	$("<img/>").attr("src",datasetsFeatureData['label'].img).appendTo('#'+dsID+'HeatmapRowInnerLabelCellDiv');
}

function setupDatasetSettings(dsID)
{
	$('#'+dsID+"Heatmap").append("<tr id='"+dsID+"DatasetSettings' class='datasetSettingsControl' style='display:none;'><td colspan='4' id='"+dsID+"DatasetSettingsCell'><table id='"+dsID+"DatasetSettingsInnerTable' class='featureControlInnerTable'><thead><tr class='heatmapControlHeader'><td colspan='3'>Dataset Settings</td></tr></thead><tbody id='"+dsID+"DatasetSettingsInnerTableBody' class='FeatureControlInnerTableBody'></tbody></table></td></tr>");
	$('#'+dsID+"DatasetSettingsInnerTableBody").append("<tr><td>Colors:</td><td><select name='"+dsID+"DatasetColorRadio' id='"+dsID+"DatasetColorRadio'><option value=''>Default</option><option value='255,0,0;0,0,0;0,255,0'>Red / Black / Green</option><option value='255,0,0;255,255,255;0,0,255'>Red / White / Blue</option><option value='255,255,0;0,0,0;0,255,255'>Yellow / Black / Cyan</option></select></td><td style='width: 50%;'></td></tr>");
	$('#'+dsID+"DatasetSettingsInnerTableBody").append("<tr><td colspan='2'></td><td><input type='button' name='"+dsID+"UpdateSettings' id='"+dsID+"UpdateSettings' value='update'/></td></tr>");
	$('#'+dsID+"UpdateSettings").click(function() {  updateDataset(document.getElementById(dsID)); } );
}

function setupFeatures(data)
{
	var dsID = this.datasetId;
	if(data.features.length == 0)
		return;
	if(data.err)
		return hghError(dsID,data.err);	
	
	$('#'+dsID+"Heatmap").append("<tr id='"+dsID+"FeatureControl' class='featureControl' style='display:none;'><td colspan='4' id='"+dsID+"FeatureControlCell'><table id='"+dsID+"FeatureControlInnerTable' class='featureControlInnerTable'><thead><tr class='heatmapControlHeader'><td colspan='3'>Feature Settings</td></tr></thead><tbody id='"+dsID+"FeatureControlInnerTableBody' class='FeatureControlInnerTableBody'></tbody></table></td></tr>");
	$('#'+dsID+"FeatureControlInnerTableBody").append("<tr><td>Select Features</td><td colspan='2'>Subgrouping</td></tr>");
	$('#'+dsID+"FeatureControlInnerTableBody").append("<tr><td id='"+dsID+"FeatureSelectCell' class='FeatureSelectCell' rowspan='3'></td><td id='"+dsID+"SubgroupConfigureCell' class='SubgroupConfigureCell'></td><td id='"+dsID+"FeatureSubgroupListCell' class='FeatureSubgroupListCell'><table id='"+dsID+"FeatureSubgroupListTable' class='FeatureSubgroupListTable' width='100%' style='display:none;'><thead><tr><th colspan='4'>Current Subgroups</th></tr><tr><th>&nbsp;</th><th style='color:red;'>Group 1</th><th style='color:green;'>Group 2</th><th></th></tr></thead><tbody id='"+dsID+"FeatureSubgroupListTableBody'></tbody></table></td></tr>");
	$('#'+dsID+"FeatureControlInnerTableBody").append("<tr id='"+dsID+"FeatureStatsHeader' style='display:none;'><td colspan='2'>Statistic</td></tr>");
	$('#'+dsID+"FeatureControlInnerTableBody").append("<tr id='"+dsID+"FeatureStatsRow' style='display:none;'><td colspan='2' id='"+dsID+"FeatureStatsCell' ><select name='"+dsID+"StatsMethod' id='"+dsID+"StatsMethod'><option value='diffMean'>Difference in Mean</option><option value='ttest'>Student's T-Test</option><option value='wilcoxon'>Wilcoxon</option><option value='fishersExact'>Fishers Exact (for N<300)</option><option value='fishersLinearDisc'>Fishers Linear Disc</option><option value='jarqueBera'>Jarque Bera Normality</option><option value='levene'>Levene(HOV)</option><option value='brownForsythe'>Brown-Forsythe(HOV)</option></select><select name='"+dsID+"StatsCorrectionMethod' id='"+dsID+"StatsCorrectionMethod'><option value='none'>None</option><option value='bonferroni'>Bonferroni</option></select><p/><input type='button' onclick='generateStats(\""+dsID+"\");' value='Generate Statistics'><br><a href='../../cancerGenomics/stats-info/' target='_blank'>Click for more information about stats options</a></td></tr>");
	var selectText = "<div id=\""+dsID+"FeatureSelectDiv\" class='featureSelectDiv'><select name=\""+dsID+"FeatureSelect\" id=\""+dsID+"FeatureSelect\" class='FeatureSelect' multiple=\"multiple\" title=\"Select Features to Show\">";

	
	//console.log(datasetsFeatureData[dsID].featureInfo);
	datasetsFeatureData[dsID].featureInfo = data.features;
	$.each(data.features, function(i,feat) {
			selectText += "<option value='"+feat.name+"' dataset='"+dsID+"'>"+feat.shortLabel+"</option>";
	});
	selectText += "</select></div><a onclick='saveFeatureUpdate(this);' href='javascript:void(0);' dataset='"+dsID+"'>Update Features</a>";
	$('#'+dsID+"FeatureSelectCell").html(selectText);
	
	//$('#'+dsID+"Heatmap").append("<tr class='spacerRow'><td colspan='4'></td></tr>");
	$("#"+dsID+"FeatureSelect").asmSelect({ sortable: true, removeLabel: "<img src='close.gif'>" });
	updateFeatures(dsID);
}

function updateFeatures(dsID)
{
	var enabledFeatures = new Array();
	if(!datasetsFeatureData[dsID].enabledFeatures || datasetsFeatureData[dsID].enabledFeatures.length == 0)
		return;
	
	if($("#"+dsID+"ConfigureFeatureCell").length == 0)
		$('#'+dsID+"HeatmapRowInner").append("<td id='"+dsID+"ConfigureFeatureCell' class='configureFeatureCell' onclick='$(\"#"+dsID+"FeatureControl\").toggle();'>&nbsp;&nbsp;&nbsp;</td>");
	
	$("#"+dsID+"FeatureSelect option").each(function() {
			var currEl = this;
			$.each(datasetsFeatureData[dsID].enabledFeatures, function(i, feat) {
					if(feat.name == $(currEl).val())
					{
						$(currEl).attr("selected", true);
					}
			});
	});
	$("#"+dsID+"FeatureSelect").change();
	
	// handle re-adding of features to subgrouping table here if we just loaded
	if(datasetsFeatureData[dsID].subgroup1Features || datasetsFeatureData[dsID].subgroup2Features)
	{
		//console.log(datasetsFeatureData[dsID].subgroup1Features);
		$('#'+dsID+"FeatureSubgroupListTableBody tr").remove();
		
		$.each(datasetsFeatureData[dsID].subgroup1Features, function(i,feature) {
			$('#'+dsID+"FeatureSubgroupListTableBody").append("<tr dataset='"+dsID+"' featName='"+feature+"' minA='"+datasetsFeatureData[dsID].subgroup1Mins[i]+"' maxA='"+datasetsFeatureData[dsID].subgroup1Maxs[i]+"' minB='"+datasetsFeatureData[dsID].subgroup2Mins[i]+"' maxB='"+datasetsFeatureData[dsID].subgroup2Maxs[i]+"'><td >"+feature+"</td><td style='color:red;'>"+datasetsFeatureData[dsID].subgroup1Mins[i]+" to "+datasetsFeatureData[dsID].subgroup1Maxs[i]+"</td><td style='color:green;'>"+datasetsFeatureData[dsID].subgroup2Mins[i]+" to "+datasetsFeatureData[dsID].subgroup2Maxs[i]+"</td><td><a href='javascript:void(0);' onclick='removeSubgroupRow(this);'><img src='close.gif'></a></td></tr>");
		});
		$.each(datasetsFeatureData[dsID].subgroup1CodedFeatures, function(i,feature) {
			$('#'+dsID+"FeatureSubgroupListTableBody").append("<tr dataset='"+dsID+"' featName='"+feature+"' codesA='"+datasetsFeatureData[dsID].subgroup1Codes[i]+"' codesB='' ><td >"+feature+"</td><td style='color:red;'>"+datasetsFeatureData[dsID].subgroup1Codes[i]+"</td><td style='color:green;'>&nbsp;</td><td><a href='javascript:void(0);' onclick='removeSubgroupRow(this);'><img src='close.gif'></a></td></tr>");
		});
		$.each(datasetsFeatureData[dsID].subgroup2CodedFeatures, function(i,feature) {
			$('#'+dsID+"FeatureSubgroupListTableBody").append("<tr dataset='"+dsID+"' featName='"+feature+"' codesA='' codesB='"+datasetsFeatureData[dsID].subgroup2Codes[i]+"' ><td >"+feature+"</td><td style='color:red;'>&nbsp;</td><td style='color:green;'>"+datasetsFeatureData[dsID].subgroup2Codes[i]+"</td><td><a href='javascript:void(0);' onclick='removeSubgroupRow(this);'><img src='close.gif'></a></td></tr>");
		});
	
		$('#'+dsID+"FeatureSubgroupListTable").show();
		$('#'+dsID+"FeatureStatsHeader").show();
		$('#'+dsID+"FeatureStatsRow").show();
		
		if(datasetsFeatureData[dsID].stats && $('#'+dsID+"FeatureSubgroupListTableBody tr").length > 0)
		{
			$('#'+dsID+"StatsMethod").val(datasetsFeatureData[dsID].stats);
			generateStats(dsID);
		}
	}
	
	if($('#'+dsID+"FeatureSubgroupListTableBody tr").length > 0)
		refreshImageWithSubgrouping(dsID);
	else
	{
		$('#'+dsID+"HeatmapRowInnerSubgroupCell").html("");
		$('#'+dsID+"HeatmapRowInnerStats").hide();
	}
}

// ajax call to update the list of current features, in the current sort order
function saveFeatureUpdate(el)
{
	var dsID = $(el).attr("dataset");
	//$('#'+dsID+"ConfigureFeatureCell").remove();
	
	var dataVars = new Object;
	dataVars.hgh2_mode = "updateFeatures";
	dataVars.hgh2_dataset = dsID;
	dataVars.hgh2_featureList = $('#'+dsID+'FeatureSelect').val().join(",");
		// figure out sorting
		if(datasetsFeatureData[dsID] && datasetsFeatureData[dsID].sortFeatures)
		{
			dataVars.hgh2_sortType = "feature";
			dataVars.hgh2_featureSort = datasetsFeatureData[dsID].sortFeatures.join(",");
			dataVars.hgh2_featureSortDir = datasetsFeatureData[dsID].sortFeaturesDirs.join(",");			 
		}
		else if(datasetsFeatureData[dsID] && datasetsFeatureData[dsID].sortGene)
		{
			dataVars.hgh2_sortType = "gene";
			dataVars.hgh2_sortGene = datasetsFeatureData[dsID].sortGene;
			dataVars.hgh2_sortDir = datasetsFeatureData[dsID].sortDir;
		}
		else if(datasetsFeatureData[dsID] && datasetsFeatureData[dsID].sortChromStart)
		{
			dataVars.hgh2_sortType = "chrom";
			dataVars.hgh2_sortChromStart = datasetsFeatureData[dsID].sortChromStart;
			dataVars.hgh2_sortChromEnd = datasetsFeatureData[dsID].sortChromEnd;
			dataVars.hgh2_sortDir = datasetsFeatureData[dsID].sortDir;
		}

		if(current_user_id > 0)
		{
			dataVars.hgh2_tokenId = current_token_id;
			dataVars.hgh2_userId = current_user_id;
		}
	$.ajax({
			type: "POST",
			url: "/cgi-bin/hgHeatmap2",
			data: dataVars,
			dataType: "json",
			success: featureUpdateCallback,
			error: ajaxError,
			datasetId: dsID,
			cache: true
	});
	
}

// callback to handle the feature update, which just passes the data off to updateFeatures()
function featureUpdateCallback(data)
{
	//console.log(data);
	var dsID = this.datasetId;
	if(data.err)
		return hghError(dsID,data.err);	

	$("#"+dsID+"FeatureImage").attr("src",data.feature);
	datasetsFeatureData[dsID].enabledFeatures = data.featureInfo;
	updateFeatures(dsID);
}

// this can be written much more concisely, but works for now
// basically shift-click adds additional sorts
function sortFeature(event)
{
	//var x = event.pageX - event.target.x;
	var x = event.pageX - $(event.target).offset().left;
	var dsID = $(event.target).attr('dataset');
	var featureName;
	
	// loop through each feature to find the one we're sorting
	$.each(datasetsFeatureData[dsID].enabledFeatures, function(i,feat) {
			if(feat.pixelStart <= x && feat.pixelEnd >= x)
			{
				featureName = feat.name;
			}
	});
	
	// do we already have sorts set?
	if(!datasetsFeatureData[dsID].sortFeatures)
	{
		datasetsFeatureData[dsID].sortFeatures = new Array();
		datasetsFeatureData[dsID].sortFeaturesDirs = new Array();
	}
	
	var featureIndex = -1;
	var featureDir;
	// see if we're already sorting on this feature
	// if so, we should just be flipping the direction
	$.each(datasetsFeatureData[dsID].sortFeatures, function(i,sortingFeature)
		{
			if(sortingFeature == featureName)
			{
				featureIndex = i;
				featureDir = datasetsFeatureData[dsID].sortFeaturesDirs[i];
			}
		});
	
	// check for shift, which means seconary sort
	if(!event.shiftKey)
	{
		datasetsFeatureData[dsID].sortFeatures = new Array();
		datasetsFeatureData[dsID].sortFeaturesDirs = new Array();
		datasetsFeatureData[dsID].sortFeatures.push(featureName);
		if(featureDir == "ASC")
		 	datasetsFeatureData[dsID].sortFeaturesDirs.push("DESC");
		else
		 	datasetsFeatureData[dsID].sortFeaturesDirs.push("ASC");
		
	}
	else
	{
		if(featureIndex == -1)
		{
			datasetsFeatureData[dsID].sortFeatures.push(featureName);
			datasetsFeatureData[dsID].sortFeaturesDirs.push("ASC");
		}
		else
		{
			if(featureDir == "ASC")
				datasetsFeatureData[dsID].sortFeaturesDirs[featureIndex] = "DESC";
			else
				datasetsFeatureData[dsID].sortFeaturesDirs[featureIndex] = "ASC";
		}
	}
	
	// call the main update, which rebuilds the data and feature heatmaps
	updateDataset(document.getElementById(dsID));
}


// test fxn to check out animation
function zoomImage(img)
{
	//console.log(img);
	var savedHeight = $(img).height();
	var currWidth = $(img).width();
	// img.width += 100;
	//img.height = savedHeight;
	$(img).animate({ 
			width: "150%",
			height: savedHeight,
			left: -1*currWidth*.25
	});
	/* $(img).animate({ 
	width: "100%",
	height: savedHeight,
	left: 0
	});*/
}

// two functions to check if the mouse has stopped over the image
// for half a second, in which case we show the tooltip
// we could probably combine these into a single fxn
function heatmapImageMouseMove(event)
{
	if($("#tooltip").css('display') != "none") { $("#tooltip").fadeOut('fast'); }
	clearTimeout(toolTipTimeout);
	toolTipTimeout = setTimeout( function() { showInfo(event); },500);
}
function featureImageMouseMove(event)
{
	if($("#tooltip").css('display') != "none") { $("#tooltip").fadeOut('fast'); }
	clearTimeout(toolTipTimeout);
	toolTipTimeout = setTimeout( function() { showFeatureTooltip(event); },500);
}

// show the tooltip for the genomic heatmap
// genesets show the particular gene we're over, chrom shows the position
function showInfo(event)
{
	//var x = event.pageX - event.target.x;
	var x = event.pageX - $(event.target).offset().left;
	//var y = event.pageY - event.target.y;
	var y = event.pageY - $(event.target).offset().top;
	var dsID = $(event.target).attr('dataset');
	
	// fade in, if it's not shown already
	if($("#tooltip").css('display') == "none") { $("#tooltip").css('opacity',.9).fadeIn('fast'); }
	
	$("#tooltip").css('left',event.pageX+15).css('top',event.pageY+15);
	
	var baseStart;
	var baseEnd;

	if(datasetsFeatureData[dsID].type == 'chrom')
	{
		var chromNum;
		var base;
		var chromStart;
		var chromEnd;
		$.each(datasetsFeatureData[dsID].chromInfo, function(i,chrom) {
				if(chrom.pixelStart <= x && chrom.pixelEnd >= x)
				{
					chromNum = chrom.chromNum;
					var numPixels = chrom.pixelEnd - chrom.pixelStart;
					var numBases = chrom.baseEnd - chrom.baseStart;
					var basesPerPixel = numBases / numPixels;
					base = Math.floor((x-chrom.pixelStart)*basesPerPixel)+chrom.baseStart;
					chromStart = base;
					base = base+"-"+Math.floor(base+basesPerPixel);
					chromEnd = Math.floor(chromStart+basesPerPixel);
				}
		});
		var sampleID;
		if($("#"+dsID).val() == 'summary')
			$("#tooltip").html("<div class='heatmapControlHeader'>Info:</div>Chrom: "+chromNum+"<br>Bases:"+base);
		else
		{
			var ttText = "<div class='heatmapControlHeader'>Info:</div>";
			if(datasetsFeatureData[dsID].samples[Math.ceil(y/datasetsFeatureData[dsID].sampleHeight)-1])
			{
				ttText += "Sample: "+datasetsFeatureData[dsID].samples[Math.ceil(y/datasetsFeatureData[dsID].sampleHeight)-1]+"<br>";
				sampleID = datasetsFeatureData[dsID].samples[Math.ceil(y/datasetsFeatureData[dsID].sampleHeight)-1];
			}
			ttText += "Chrom: "+chromNum+"<br>Bases:"+base;
			
			$("#tooltip").html(ttText);
		}
		
		// tooltip info query
		if(chromStart && chromEnd && $("#"+dsID).val() != 'summary')
		{
			var requestTime = (new Date()).getTime();
			$("#tooltip").attr('lastRequest',requestTime);
			var dataVars = new Object;
			dataVars.hgh2_mode = "tooltipQuery";
			dataVars.hgh2_dataset = dsID;
			dataVars.hgh2_sample = sampleID;
			dataVars.hgh2_chromStart = "chr"+chromNum+":"+chromStart;
			dataVars.hgh2_chromEnd = "chr"+chromNum+":"+chromEnd;
			if(current_user_id > 0)
			{
				dataVars.hgh2_tokenId = current_token_id;
				dataVars.hgh2_userId = current_user_id;
			}
			
			$.ajax({
					type: "POST",
					url: "/cgi-bin/hgHeatmap2",
					data: dataVars,
					dataType: "json",
					success: tooltipQuery_callback,
					error: ajaxError,
					datasetId: dsID,
					requestTime: requestTime,
					ttString: $("#tooltip").html(),
					cache: true
			});
		}
	}
	else
	{
		var probeName;
		var geneName;
		$.each(datasetsFeatureData[dsID].geneSetInfo, function(i,geneSet) {
				if(!probeName)
				{
					$.each(geneSet.geneInfo, function(i, gene) {
							if(gene.pixelStart <= x && gene.pixelEnd >= x)
							{
								probeName = gene.probeName;
								geneName = gene.geneName;
							}
					});
				}
		});
		var sampleID;
		if($("#"+dsID).val() == 'summary')
			$("#tooltip").html("<div class='heatmapControlHeader'>Info:</div>Gene: "+geneName+"<br>Probe: "+probeName);
		else
		{
			var ttText = "<div class='heatmapControlHeader'>Info:</div>";
			if(datasetsFeatureData[dsID].samples[Math.ceil(y/datasetsFeatureData[dsID].sampleHeight)-1])
			{
				ttText += "Sample: "+datasetsFeatureData[dsID].samples[Math.ceil(y/datasetsFeatureData[dsID].sampleHeight)-1]+"<br>";
				sampleID = datasetsFeatureData[dsID].samples[Math.ceil(y/datasetsFeatureData[dsID].sampleHeight)-1];
			}
			ttText += "Gene: "+geneName+"<br>Probe: "+probeName;
			$("#tooltip").html(ttText);
		}
		
		// tooltip info query
		if(sampleID && probeName && $("#"+dsID).val() != 'summary')
		{
			var requestTime = (new Date()).getTime();
			$("#tooltip").attr('lastRequest',requestTime);
			var dataVars = new Object;
			dataVars.hgh2_mode = "tooltipQuery";
			dataVars.hgh2_dataset = dsID;
			dataVars.hgh2_sample = sampleID;
			dataVars.hgh2_probe = probeName;
			if(current_user_id > 0)
			{
				dataVars.hgh2_tokenId = current_token_id;
				dataVars.hgh2_userId = current_user_id;
			}
				
			$.ajax({
					type: "POST",
					url: "/cgi-bin/hgHeatmap2",
					data: dataVars,
					dataType: "json",
					success: tooltipQuery_callback,
					error: ajaxError,
					datasetId: dsID,
					requestTime: requestTime,
					ttString: $("#tooltip").html(),
					cache: true
			});
		}
	}
	
}

// display the feature tootip, showing the patient and feature
function showFeatureTooltip(event)
{
	//var x = event.pageX - event.target.x;
	var x = event.pageX - $(event.target).offset().left;
	//var y = event.pageY - event.target.y;
	var y = event.pageY - $(event.target).offset().top;
	//console.log($("#featureControls").width());
	var featureControlWidth = $("#tooltip").width(); // need to do this here cause once fadeIn starts it's different
	var dsID = $(event.target).attr('dataset');
	
	// fade in, if it's not shown already
	if($("#tooltip").css('display') == "none") { $("#tooltip").css('opacity',.9).fadeIn('fast'); }
	$("#tooltip").css('left',event.pageX-featureControlWidth).css('top',event.pageY+15);
	
	var featureName;
	var featureText;
	$.each(datasetsFeatureData[dsID].enabledFeatures, function(i,feat) {
			//console.log(chrom);
			//console.log(x);
			if(feat.pixelStart <= x && feat.pixelEnd >= x)
			{
				if(datasetsFeatureData[dsID].featureInfo)
				{
					$.each(datasetsFeatureData[dsID].featureInfo, function(i,feature) {
							if(feat.name == feature.name)
							{
								featureName = feature.name;
								featureText = feature.longLabel;
							}
					});
				}
				else
				{
					featureName = feat.name;
					featureText = feat.longLabel;
				}
			}
	});
	
	var featureString = "<div class='heatmapControlHeader'>Info:</div>";
        var sampleID;

        if(datasetsFeatureData[dsID].samples[Math.ceil(y/datasetsFeatureData[dsID].sampleHeight)-1] != undefined && $("#"+dsID).val() != 'summary')
	{
		featureString += "Sample: "+datasetsFeatureData[dsID].samples[Math.ceil(y/datasetsFeatureData[dsID].sampleHeight)-1]+"<br>";
		sampleID = datasetsFeatureData[dsID].samples[Math.ceil(y/datasetsFeatureData[dsID].sampleHeight)-1];
	}
	featureString += "Feature: "+featureText;
	//featureString += "Feature: "+featureText+"<div class='heatmapControlHeader' style='text-align:center;'>Click to Sort<div>";
	$("#tooltip").html(featureString+"<div class='heatmapControlHeader' style='text-align:center;'>Click to Sort<div>");

	// tooltip info query
	if(sampleID)
	{
		var requestTime = (new Date()).getTime();
		$("#tooltip").attr('lastRequest',requestTime);
		var dataVars = new Object;
		dataVars.hgh2_mode = "tooltipQuery";
		dataVars.hgh2_dataset = dsID;
		dataVars.hgh2_sample = sampleID;
		dataVars.hgh2_feature = featureName;
		if(current_user_id > 0)
		{
			dataVars.hgh2_tokenId = current_token_id;
			dataVars.hgh2_userId = current_user_id;
		}
		
		$.ajax({
				type: "POST",
				url: "/cgi-bin/hgHeatmap2",
				data: dataVars,
				dataType: "json",
				success: tooltipQuery_callback,
				error: ajaxError,
				datasetId: dsID,
				requestTime: requestTime,
				featureString: featureString,
				cache: true
		});
	}
}

function tooltipQuery_callback(data)
{
	if(this.requestTime != $("#tooltip").attr('lastRequest'))
		return false;

	if(data.err)
		return hghError('none',data.err);

	if(this.ttString)
		$("#tooltip").html(this.ttString+"<br>Value: "+data.value+"<br>Patient: "+data.patient);
	else
		$("#tooltip").html(this.featureString+"<br>Value: "+data.value+"<br>Patient: "+data.patient+"<div class='heatmapControlHeader' style='text-align:center;'>Click to Sort<div>");
}

// click handler for the features, used to show the subgrouping controls
function selectClicked(event,ui)
{
	var dsID = $(event.target).attr("dataset");
	var featureName = $(event.target).attr("value");
	var longLabel = '';
	var minVal = 0;
	var maxVal = 100;
	var codes = new Array;
	if(datasetsFeatureData[dsID] && datasetsFeatureData[dsID].featureInfo)
	{
		$.each(datasetsFeatureData[dsID].featureInfo, function(i,feature) {
				if(featureName == feature.name)
				{
					if(feature.codes)
					{
						codes = feature.codes;
					}
					else
					{
						minVal = feature.minVal;
						maxVal = feature.maxVal;
					}
					longLabel = feature.longLabel;
				}
		});
		if(!longLabel)
			longLabel = featureName;
	}	
	if(codes.length > 0)
	{
		//console.log(codes);
		var codesSelect = "<select multiple name='"+featureName+"Select' id='"+featureName+"Select' style='width: 130px;'>";
		$.each(codes, function(i,codeName) { codesSelect += "<option value='"+codeName+"'>"+codeName+"</option>";  } );
		codesSelect += "</select>";
		
		var codesSelectA = $(codesSelect).clone();
		codesSelectA.attr('id',dsID+codesSelectA.attr('id')+'A');
		var codesSelectB = $(codesSelect).clone();
		codesSelectB.attr('id',dsID+codesSelectB.attr('id')+'B');
		$('#'+dsID+"SubgroupConfigureCell").html(longLabel+"<p/>");
		$('#'+dsID+"SubgroupConfigureCell").append("<span style='color:red;'>Group 1</span>").append("<p/>").append(codesSelectA).append("<p/>");
		$('#'+dsID+"SubgroupConfigureCell").append("<span style='color:green;'>Group 2</span>").append("<p/>").append(codesSelectB);
		$('#'+dsID+"SubgroupConfigureCell").append("<p/><input type='button' value='Add Subgrouping' onclick='addSubgroupCodes(\""+dsID+"\",\""+featureName+"\");'>");
	}
	else
	{
		var featureControlSliderA = $('#featureControlsSlider').clone()
		$(featureControlSliderA).attr("id",dsID+$(featureControlSliderA).attr("id")+"A");
		var featureControlSliderB = $('#featureControlsSlider').clone()
		$(featureControlSliderB).attr("id",dsID+$(featureControlSliderB).attr("id")+"B");
		$('#'+dsID+"SubgroupConfigureCell").html(longLabel+"<p/>");
		$('#'+dsID+"SubgroupConfigureCell").append("<span style='color:red;'>Group 1</span>").append(featureControlSliderA).append("<span id='"+dsID+"featureControlsSliderAVal'></span>").append("<p/>"+"<span style='color:green;'>Group 2</span>").append(featureControlSliderB).append("<span id='"+dsID+"featureControlsSliderBVal'></span>").append("<p/><input type='button' value='Add Subgrouping' onclick='addSubgroup(\""+dsID+"\",\""+featureName+"\");'>");
		$('#'+dsID+'featureControlsSliderA').slider({range: true, min: minVal*1000, max: maxVal*1000, 
			slide: function(e,ui) {  return (featureSliderChanging(e,ui,dsID,"A") / 1000); },
			change: function(e,ui) {  featureSliderChanged(e,ui,dsID,"A"); }});
		$('#'+dsID+'featureControlsSliderB').slider({range: true, min: minVal * 1000, max: maxVal * 1000, 
			slide: function(e,ui) {  return (featureSliderChanging(e,ui,dsID,"B") / 1000) },
			change: function(e,ui) {  featureSliderChanged(e,ui,dsID,"B"); } });
	}
	//console.log($(event.target).attr("value"));
}

// update the text around the slider while it's changing
function featureSliderChanging(e,ui,dsID,slider)
{
	var min = $('#'+dsID+'featureControlsSlider'+slider).slider('value',0) / 1000;
	var max = $('#'+dsID+'featureControlsSlider'+slider).slider('value',1) / 1000;
	$('#'+dsID+'featureControlsSlider'+slider+'Val').html("Contains samples from "+min+" to "+max);
	return true;
}

// update the text around the slider when it's changed
function featureSliderChanged(e,ui,dsID,slider)
{
	var min = $('#'+dsID+'featureControlsSlider'+slider).slider('value',0) / 1000;
	var max = $('#'+dsID+'featureControlsSlider'+slider).slider('value',1) / 1000;
	$('#'+dsID+'featureControlsSlider'+slider+'Val').html("Contains samples from "+min+" to "+max);
}

// add the subgroup to the table when the add button is clicked
function addSubgroup(dsID,featName)
{
	var minA = $('#'+dsID+'featureControlsSliderA').slider('value',0) / 1000;
	var maxA = $('#'+dsID+'featureControlsSliderA').slider('value',1) / 1000;
	var minB = $('#'+dsID+'featureControlsSliderB').slider('value',0) / 1000;
	var maxB = $('#'+dsID+'featureControlsSliderB').slider('value',1) / 1000;
	$('#'+dsID+"FeatureSubgroupListTable").show();
	$('#'+dsID+"FeatureSubgroupListTableBody").append("<tr dataset='"+dsID+"' featName='"+featName+"' minA='"+minA+"' maxA='"+maxA+"' minB='"+minB+"' maxB='"+maxB+"'><td >"+featName+"</td><td style='color:red;'>"+minA+" to "+maxA+"</td><td style='color:green;'>"+minB+" to "+maxB+"</td><td><a href='javascript:void(0);' onclick='removeSubgroupRow(this);'><img src='close.gif'></a></td></tr>");
	
	$('#'+dsID+"FeatureStatsHeader").show();
	$('#'+dsID+"FeatureStatsRow").show();
	
	if($("#"+dsID).val() == "summary")
		updateDataset(document.getElementById(dsID));
	else
		refreshImageWithSubgrouping(dsID);
}

// add the subgroup to the table when the add button is clicked
function addSubgroupCodes(dsID,featName)
{

	var aCodes = $('#'+dsID+featName+"SelectA").val();
	var bCodes = $('#'+dsID+featName+"SelectB").val();
	var codesA = '';
	var codesB = '';
	if(aCodes)
		codesA = aCodes.join(",");
	if(bCodes)
		codesB = bCodes.join(",");
	$('#'+dsID+"FeatureSubgroupListTable").show();
	$('#'+dsID+"FeatureSubgroupListTableBody").append("<tr dataset='"+dsID+"' featName='"+featName+"' codesA='"+codesA+"' codesB='"+codesB+"'><td >"+featName+"</td><td style='color:red;'>"+codesA+"</td><td style='color:green;'>"+codesB+"</td><td><a href='javascript:void(0);' onclick='removeSubgroupRow(this);'><img src='close.gif'></a></td></tr>");
	
	$('#'+dsID+"FeatureStatsHeader").show();
	$('#'+dsID+"FeatureStatsRow").show();
	
	if($("#"+dsID).val() == "summary")
		updateDataset(document.getElementById(dsID));
	else
		refreshImageWithSubgrouping(dsID);
}

// remove the subgroup
function removeSubgroupRow(el)
{
	var dsID = $(el).parent().parent().attr("dataset");
	$(el).parent().parent().remove();
	if($("#"+dsID).val() == "summary")
		updateDataset(document.getElementById(dsID));
	else
		refreshImageWithSubgrouping(dsID);
}

// ajax function to get the subgrouping img
// (red/green bars next to clinical heatmap)
function refreshImageWithSubgrouping(dsID)
{
	var subgroup1Features = [];
	var subgroup1Mins = [];
	var subgroup1Maxs = [];
	var subgroup1FeatureCodes = [];
	var subgroup1Codes = [];
	var subgroup2Features = [];
	var subgroup2Mins = [];
	var subgroup2Maxs = [];
	var subgroup2FeatureCodes = [];
	var subgroup2Codes = [];
	
	$('#'+dsID+"HeatmapRowInnerStats").hide(); // hide the stats if the subgrouping changes

	// don't need the subgrp image if you're in summary mode
	if($("#"+dsID).val() == 'summary')
	{
		$('#'+dsID+"HeatmapRowInnerSubgroupCell").html("");	
		return void(0);
	}
	
	
	$('#'+dsID+"FeatureSubgroupListTableBody tr").each( function() {
			if($(this).attr('minA'))
			{
			subgroup1Features.push($(this).attr('featName'));
			subgroup2Features.push($(this).attr('featName'));
			subgroup1Mins.push($(this).attr('minA'));
			subgroup2Mins.push($(this).attr('minB'));
			subgroup1Maxs.push($(this).attr('maxA'));
			subgroup2Maxs.push($(this).attr('maxB'));
			}
			else
			{
			var featureName = $(this).attr('featName');
			var codesA =$(this).attr('codesA').split(",");
			$.each(codesA,function(i,code) {
				if(code)
				{
					subgroup1Codes.push(code);
					subgroup1FeatureCodes.push(featureName);
				}
				});
			var codesB =$(this).attr('codesB').split(",");
			$.each(codesB,function(i,code) {
				if(code)
				{
					subgroup2Codes.push(code);
					subgroup2FeatureCodes.push(featureName);
				}
				});
			}
	});
	
	var dataVars = new Object;
	dataVars.hgh2_mode = "subgroup";
	dataVars.hgh2_dataset = dsID;
	dataVars.hgh2_subgroup1Features  =  subgroup1Features.join(",");
	dataVars.hgh2_subgroup1Mins =  subgroup1Mins.join(",");
	dataVars.hgh2_subgroup1Maxs =  subgroup1Maxs.join(",");
	dataVars.hgh2_subgroup2Features  =  subgroup2Features.join(",");
	dataVars.hgh2_subgroup2Mins =  subgroup2Mins.join(",");
	dataVars.hgh2_subgroup2Maxs =  subgroup2Maxs.join(",");
	dataVars.hgh2_subgroup1CodedFeatures = subgroup1FeatureCodes.join(",");
	dataVars.hgh2_subgroup2CodedFeatures = subgroup2FeatureCodes.join(",");
	dataVars.hgh2_subgroup1Codes = subgroup1Codes.join(",");
	dataVars.hgh2_subgroup2Codes = subgroup2Codes.join(",");

	if(datasetsFeatureData[dsID] && datasetsFeatureData[dsID].enabledFeatures)
	{
		var featureList = [];
		$.each(datasetsFeatureData[dsID].enabledFeatures,function(i,feat) {
				featureList.push(feat.name);
		});
		dataVars.hgh2_featureList = featureList.join(",");
	}
		// figure out sorting
		if(datasetsFeatureData[dsID] && datasetsFeatureData[dsID].sortFeatures)
		{
			dataVars.hgh2_sortType = "feature";
			dataVars.hgh2_featureSort = datasetsFeatureData[dsID].sortFeatures.join(",");
			dataVars.hgh2_featureSortDir = datasetsFeatureData[dsID].sortFeaturesDirs.join(",");			 
		}
		else if(datasetsFeatureData[dsID] && datasetsFeatureData[dsID].sortGene)
		{
			dataVars.hgh2_sortType = "gene";
			dataVars.hgh2_sortGene = datasetsFeatureData[dsID].sortGene;
			dataVars.hgh2_sortDir = datasetsFeatureData[dsID].sortDir;
		}
		else if(datasetsFeatureData[dsID] && datasetsFeatureData[dsID].sortChromStart)
		{
			dataVars.hgh2_sortType = "chrom";
			dataVars.hgh2_sortChromStart = datasetsFeatureData[dsID].sortChromStart;
			dataVars.hgh2_sortChromEnd = datasetsFeatureData[dsID].sortChromEnd;
			dataVars.hgh2_sortDir = datasetsFeatureData[dsID].sortDir;
		}
	
	// keep track for loads
	datasetsFeatureData[dsID].subgroup1Features  =  subgroup1Features;
	datasetsFeatureData[dsID].subgroup1Mins =  subgroup1Mins;
	datasetsFeatureData[dsID].subgroup1Maxs =  subgroup1Maxs;
	datasetsFeatureData[dsID].subgroup2Features  =  subgroup2Features;
	datasetsFeatureData[dsID].subgroup2Mins =  subgroup2Mins;
	datasetsFeatureData[dsID].subgroup2Maxs =  subgroup2Maxs;
	datasetsFeatureData[dsID].subgroup1CodedFeatures = subgroup1FeatureCodes;
	datasetsFeatureData[dsID].subgroup2CodedFeatures = subgroup2FeatureCodes;
	datasetsFeatureData[dsID].subgroup1Codes = subgroup1Codes;
	datasetsFeatureData[dsID].subgroup2Codes = subgroup2Codes;

	if(current_user_id > 0)
	{
		dataVars.hgh2_tokenId = current_token_id;
		dataVars.hgh2_userId = current_user_id;
	}
		
	$.ajax({
			type: "POST",
			url: "/cgi-bin/hgHeatmap2",
			data: dataVars,
			dataType: "json",
			success: subgroupImgCallback,
			error: ajaxError,
			datasetId: dsID,
			cache: true
	});
	
	//console.log(subgroup1Mins);
}

// similar to the function above, but with a stats function which means we
// should generate the stats bar below the heatmap
function generateStats(dsID)
{
	var subgroup1Features = [];
	var subgroup1Mins = [];
	var subgroup1Maxs = [];
	var subgroup1FeatureCodes = [];
	var subgroup1Codes = [];
	var subgroup2Features = [];
	var subgroup2Mins = [];
	var subgroup2Maxs = [];
	var subgroup2FeatureCodes = [];
	var subgroup2Codes = [];
	
	$('#'+dsID+"FeatureSubgroupListTableBody tr").each( function() {
			if($(this).attr('minA'))
			{
			subgroup1Features.push($(this).attr('featName'));
			subgroup2Features.push($(this).attr('featName'));
			subgroup1Mins.push($(this).attr('minA'));
			subgroup2Mins.push($(this).attr('minB'));
			subgroup1Maxs.push($(this).attr('maxA'));
			subgroup2Maxs.push($(this).attr('maxB'));
			}
			else
			{
			var featureName = $(this).attr('featName');
			var codesA =$(this).attr('codesA').split(",");
			$.each(codesA,function(i,code) {
				if(code)
				{
					subgroup1Codes.push(code);
					subgroup1FeatureCodes.push(featureName);
				}
				});
			var codesB =$(this).attr('codesB').split(",");
			$.each(codesB,function(i,code) {
				if(code)
				{
					subgroup2Codes.push(code);
					subgroup2FeatureCodes.push(featureName);
				}
				});
			}
	});
	
	var dataVars = new Object;
	dataVars.hgh2_mode = "stats";
	dataVars.hgh2_dataset = dsID;
	dataVars.hgh2_type = $('#dsDisplayAs').val();
	// console.log($('#'+dsID+'StatsMethod'));
	dataVars.hgh2_statsFxn = $('#'+dsID+'StatsMethod').val();
	if($('#'+dsID+'StatsCorrectionMethod').val() != "None")
		dataVars.hgh2_statsCorrection = $('#'+dsID+'StatsCorrectionMethod').val();
	dataVars.hgh2_subgroup1Features  =  subgroup1Features.join(",");
	dataVars.hgh2_subgroup1Mins =  subgroup1Mins.join(",");
	dataVars.hgh2_subgroup1Maxs =  subgroup1Maxs.join(",");
	dataVars.hgh2_subgroup2Features  =  subgroup2Features.join(",");
	dataVars.hgh2_subgroup2Mins =  subgroup2Mins.join(",");
	dataVars.hgh2_subgroup2Maxs =  subgroup2Maxs.join(",");
	dataVars.hgh2_subgroup1CodedFeatures = subgroup1FeatureCodes.join(",");
	dataVars.hgh2_subgroup2CodedFeatures = subgroup2FeatureCodes.join(",");
	dataVars.hgh2_subgroup1Codes = subgroup1Codes.join(",");
	dataVars.hgh2_subgroup2Codes = subgroup2Codes.join(",");
	if($('#dsDisplayAs').val()=='geneset')
	{
		var geneSetA = [];
		$('#geneSearchDisplayOL span').each(function() {
				geneSetA.push($(this).text());
		});
		dataVars.hgh2_geneSets = geneSetA.join(",");
	}
	else
	{
		/*if($("#chromStartPos").val())
			dataVars.hgh2_chromStart = $("#chromStartPos").val();
		if($("#chromEndPos").val())
			dataVars.hgh2_chromEnd = $("#chromEndPos").val();*/
		if($("#chromPos").val())
		{
			var chromPosArray = $("#chromPos").val().replace("CHR","chr").split(/-/);
			dataVars.hgh2_chromStart = chromPosArray[0];
			dataVars.hgh2_chromEnd = chromPosArray[1];
		}
	}
	
	// keep track of the stats fxn
	datasetsFeatureData[dsID].stats = $('#'+dsID+'StatsMethod').val();
	if(current_user_id > 0)
	{
		dataVars.hgh2_tokenId = current_token_id;
		dataVars.hgh2_userId = current_user_id;
	}
	
	$.ajax({
			type: "POST",
			url: "/cgi-bin/hgHeatmap2",
			data: dataVars,
			dataType: "json",
			success: statsImgCallback,
			error: ajaxError,
			datasetId: dsID,
			cache: true
	});
	
}

function subgroupImgCallback(data)
{
	var dsID = this.datasetId;
	if(data.err)
		return hghError(dsID,data.err);	
	$('#'+dsID+"HeatmapRowInnerSubgroupCell").html("<img src='"+data.subgroupImg+"'>");
}

function statsImgCallback(data)
{
	var dsID = this.datasetId;
	if(data.err)
		return hghError(dsID,data.err);	
	$('#'+dsID+"HeatmapRowInnerStats").show();
	$('#'+dsID+"HeatmapRowInnerStatsCell").html("<img src='"+data.statsImg+"' style='border-top:1px black dotted; border-bottom:1px black dotted;'>");
	$('#'+dsID+'HeatmapRowInnerFeatureCell').attr('rowspan',3);
	$('#'+dsID+"HeatmapRowInnerStatsScale").html("<img src='"+data.scale+"' style='border-top:1px white dotted; border-bottom:1px white dotted;'>");
}

function updateAllDatasets()
{
	// a little cleanup
	datasetsFeatureData['label'] = new Object;
	
	$("#datasetsInnerTable select").each(function(index, domElement) { updateDataset(domElement); });
	updateAnnotationTrack();
}

function searchGenesetsKeypress(event)
{
	clearTimeout(keypressTimeout);
	keypressTimeout = setTimeout( function() { searchGenes(event); },500);
}
function searchGenesKeypress(event)
{
	clearTimeout(keypressTimeout);
	keypressTimeout = setTimeout( function() { userSearchGenes(event); },500);
}
function searchGenes(event)
{
	//console.log($(event.target).val());
	var dataVars = new Object;
	dataVars.hgh2_mode = "getGenesets";
	if($('#geneSearchType').val() == 'geneset')
	{
		dataVars.hgh2_matchingGenesets = $(event.target).val();
	}
	else
	{
		dataVars.hgh2_matchingGenes = $(event.target).val();
	}
	
	if(current_user_id > 0)
	{
		dataVars.hgh2_tokenId = current_token_id;
		dataVars.hgh2_userId = current_user_id;
	}
	$.ajax({
			type: "POST",
			url: "/cgi-bin/hgHeatmap2",
			data: dataVars,
			dataType: "json",
			success: geneSearchCallback,
			error: ajaxError,
			cache: true
	});
	
}

function geneSearchCallback(data)
{
	//console.log(data);
	if(data.err)
		return hghError('none',data.err);	
	var geneSetResultHTML = '';
	$.each(data.geneSets, function(i,geneSet) {
			geneSetResultHTML += "<li class='hghListItem' geneset='"+geneSet+"'><span class='hghListItemLabel' geneset='"+geneSet+"'>"+geneSet+"</span></li>";
	});
	$('#geneSearchResultOL').html(geneSetResultHTML);
	$('#geneSearchResultOL li').click(function(ev) { 
			
			if($(ev.target).parent('li').length == 0)
				$(ev.target).clone().append("<a href='javascript:void(0);' class='hghListItemRemove' onclick='$(this).parent(\"li\").remove();'><img src='close.gif'></a>").hide().appendTo($("#geneSearchDisplayOL")).fadeIn("fast");
			else
				$(ev.target).parent('li').clone().append("<a href='javascript:void(0);' class='hghListItemRemove' onclick='$(this).parent(\"li\").remove();'><img src='close.gif'></a>").hide().appendTo($("#geneSearchDisplayOL")).fadeIn("fast");
			getGenesetGenes(ev);
	});
	
}

function getGenesetGenes(event)
{
	//console.log(event.target);
	var dataVars = new Object;
	dataVars.hgh2_mode = "getGenesInGenesets";
	dataVars.hgh2_geneSets = $(event.target).attr('geneset');
	
	if(current_user_id > 0)
	{
		dataVars.hgh2_tokenId = current_token_id;
		dataVars.hgh2_userId = current_user_id;
	}
	$.ajax({
			type: "POST",
			url: "/cgi-bin/hgHeatmap2",
			data: dataVars,
			dataType: "json",
			success: getGenesetGenesCallback,
			error: ajaxError,
			cache: true
	});
	
}

function getGenesetGenesCallback(data)
{
	var geneSetResultHTML = '';
	if(data.err)
		return hghError('none',data.err);	

	$('#genesetSearchGeneList').empty();
	$.each(data,function(geneSetName,geneSetArray) {
		$('#genesetSearchGenesetName').html(geneSetName);
		$.each(geneSetArray,function(i,geneName) {
			geneSetResultHTML += "<li class='genesetGeneListItem'>"+geneName+"</li>";
				
		});
	});
	/*$.each(data.geneSets, function(i,geneSet) {
			geneSetResultHTML += "<li class='hghListItem'><span class='hghListItemLabel'>"+geneSet+"</span></li>";
	});*/
	$('#genesetSearchGeneList').html(geneSetResultHTML);
	/*$('#genesetSearchGeneList li').click(function(ev) { 
			$(ev.target).parent('li').clone().append("<a href='javascript:void(0);' class='hghListItemRemove' onclick='$(this).parent(\"li\").remove();'><img src='close.gif'></a>").hide().appendTo($("#geneSearchDisplayOL")).fadeIn("fast");
	});*/
	$('#addGeneset').show();
}

function userSearchGenes(event)
{
	//console.log($(event.target).val());
	var dataVars = new Object;
	dataVars.hgh2_mode = "getGenes";
	dataVars.hgh2_matchingGenes = $(event.target).val();
	
	if(current_user_id > 0)
	{
		dataVars.hgh2_tokenId = current_token_id;
		dataVars.hgh2_userId = current_user_id;
	}
	
	$.ajax({
			type: "POST",
			url: "/cgi-bin/hgHeatmap2",
			data: dataVars,
			dataType: "json",
			success: userGeneSearchCallback,
			error: ajaxError,
			cache: true
	});
	
}

function userGeneSearchCallback(data)
{
	//console.log(data);
	var geneSetResultHTML = '';
	if(data.err)
		return hghError('none',data.err);	
	$.each(data.genes, function(i,gene) {
			geneSetResultHTML += "<li class='hghListItem' gene='"+gene+"'><span class='hghListItemLabel' gene='"+gene+"'>"+gene+"</span></li>";
	});
	$('#userGeneSearchResultOL').html(geneSetResultHTML);
	$('#userGeneSearchResultOL li').click(function(ev) { 
			if($(ev.target).parent('li').length == 0)
				$(ev.target).clone().append("<a href='javascript:void(0);' class='hghListItemRemove' onclick='$(this).parent(\"li\").remove();'><img src='close.gif'></a>").hide().appendTo($("#userGenesetSearchGeneList")).fadeIn("fast");
			else
				$(ev.target).parent('li').clone().append("<a href='javascript:void(0);' class='hghListItemRemove' onclick='$(this).parent(\"li\").remove();'><img src='close.gif'></a>").hide().appendTo($("#userGenesetSearchGeneList")).fadeIn("fast");
			$('#addGeneset').show();
			//getGenesetGenes(ev);
	});
	
}

function saveUserGeneset(geneList)
{
	//console.log('saving');
	var geneArray = new Array();
	$("#"+geneList+" li").each(function(i,li) {
			geneArray.push($(li).attr('gene'));
	});
	//console.log(geneArray.length);
	//console.log($('#userGenesetName').val());
	if(geneArray.length > 0 && $('#'+geneList+'userGenesetName').val())
	{
		var dataVars = new Object;
		dataVars.hgh2_mode = "storeUserGeneset";
		dataVars.hgh2_userGenesetName = $('#'+geneList+'userGenesetName').val();
		dataVars.hgh2_userGenesetMembers = geneArray.join(',');
				
		if(current_user_id > 0)
		{
			dataVars.hgh2_tokenId = current_token_id;
			dataVars.hgh2_userId = current_user_id;
		}

		$.ajax({
				type: "POST",
				url: "/cgi-bin/hgHeatmap2",
				data: dataVars,
				dataType: "json",
				success: saveUserGenesetCallback,
				error: ajaxError,
				cache: true
		});
		
		$("#"+geneList).empty();
		$('#'+geneList+'userGenesetName').val('');
	}
	else if(!$('#'+geneList+'userGenesetName').val())
	{
		alert("Please name your user geneset before saving");
	}
	
}

function saveUserGenesetCallback(data)
{
	//console.log(this);
	if(data.err)
		return hghError('none',data.err);	
	var liEl = $("<li></li>").addClass('hghListItem').attr('geneset','user_'+data.genesetSaved);
	$('<span>'+data.genesetSaved+'</span>').addClass('hghListItemLabel').attr('geneset','user_'+data.genesetSaved).appendTo(liEl);
	liEl.append("<a href='javascript:void(0);' class='hghListItemRemove' onclick='$(this).parent(\"li\").remove();'><img src='close.gif'></a>").hide().appendTo($("#geneSearchDisplayOL")).fadeIn("fast");
}

// function which handles the zooming, figuring out what the start and end
// of the new heatmap img should be (in chrom positions)
// this doesn't do anything for genesets, but maybe will zoom into an individual
// geneset or even a gene at some point
function heatmapClick(event)
{
	//console.log(event);
	var dsID = $(event.target).attr('dataset');
	//var x = event.pageX - event.target.x;
	var x = event.pageX - $(event.target).offset().left;
	//alert("PageX: "+event.pageX+" TargetX: "+event.target.x+" "+$(event.target).offset().left);
	if(datasetsFeatureData[dsID].type == 'chrom')
	{
		if($("#hmClick").val() == "zoom")
		{
		
			if(!event.shiftKey)
			{
				//zoomImage($('#'+dsID+"HeatmapImage"));
				var startPix = x-100;
				if(startPix < 0)
					startPix = 0;
				var endPix = x+100;
				if(endPix>700)
					endPix = 700;
				var startChromNum;
				var startBase;
				var endChromNum;
				var endBase;
				$.each(datasetsFeatureData[dsID].chromInfo, function(i,chrom) {
						//console.log(chrom);
						//console.log(x);
						if(chrom.pixelStart <= startPix && chrom.pixelEnd >= startPix)
						{
							startChromNum = chrom.chromNum;
							var numPixels = chrom.pixelEnd - chrom.pixelStart;
							var numBases = chrom.baseEnd - chrom.baseStart;
							var basesPerPixel = numBases / numPixels;
							startBase = Math.floor((startPix-chrom.pixelStart)*basesPerPixel)+chrom.baseStart;
						}
						if(chrom.pixelStart <= endPix && chrom.pixelEnd >= endPix)
						{
							endChromNum = chrom.chromNum;
							var numPixels = chrom.pixelEnd - chrom.pixelStart;
							var numBases = chrom.baseEnd - chrom.baseStart;
							var basesPerPixel = numBases / numPixels;
							endBase = Math.floor((endPix-chrom.pixelStart)*basesPerPixel)+chrom.baseStart;
						}
				});
				//$("#chromStartPos").val(startChromNum+":"+startBase);
				//$("#chromEndPos").val(endChromNum+":"+endBase);
				$("#chromPos").val('chr'+startChromNum+":"+startBase+'-'+'chr'+endChromNum+":"+endBase);
				if(startChromNum == endChromNum)
					$('#viewInGenomeBrowser').attr('disabled',0);

			}
			else
			{
				var chromStart = datasetsFeatureData[dsID].chromInfo[0].chromNum;
				if(chromStart == "X")
					chromStart = 23;
				else if(chromStart == "Y")
					chromStart = 24;
				else
					chromStart = parseInt(chromStart,10);
				var chromEnd = datasetsFeatureData[dsID].chromInfo[datasetsFeatureData[dsID].chromInfo.length-1].chromNum;
				if(chromEnd == "X")
					chromEnd = 23;
				else if(chromEnd == "Y")
					chromEnd = 24;
				else
					chromEnd = parseInt(chromEnd,10);
				
				if((chromEnd - chromStart) >= 12)
				{
					chromStart = "";
					chromEnd = "";
				}
				else if((chromEnd - chromStart) > 1)
				{
					chromStart -= 2;
					chromEnd += 2;
					if(chromStart < 1) chromStart = 1;
					else if(chromStart == 23) chromStart = "X";
					else if(chromStart >= 24) chromStart = "Y";
				}
				else
				{
					if(parseInt(datasetsFeatureData[dsID].chromInfo[0].baseStart,10) == 0)
					{
						chromStart -= 1;
						chromEnd += 1;
					}
					if(chromStart < 1) chromStart = 1;
					else if(chromStart == 23) chromStart = "X";
					else if(chromStart >= 24) chromStart = "Y";
				}
				//$("#chromStartPos").val(chromStart);
				//$("#chromEndPos").val(chromEnd);
				if(chromStart || chromEnd)
					$("#chromPos").val('chr'+chromStart+'-'+'chr'+chromEnd);
				else
					$("#chromPos").val("");
				if(chromStart != chromEnd)
					$('#viewInGenomeBrowser').attr('disabled',1);
			}
			return updateAllDatasets();
		}
		else
		{
			datasetsFeatureData[dsID].sortFeatures = undefined;
			datasetsFeatureData[dsID].sortFeaturesDir = undefined;
			datasetsFeatureData[dsID].sortGene = undefined;
			var chromNum;
			var startBase;
			var endBase;
			$.each(datasetsFeatureData[dsID].chromInfo, function(i,chrom) {
					if(chrom.pixelStart <= x && chrom.pixelEnd >= x)
					{
						chromNum = chrom.chromNum;
						var numPixels = chrom.pixelEnd - chrom.pixelStart;
						var numBases = chrom.baseEnd - chrom.baseStart;
						var basesPerPixel = numBases / numPixels;
						startBase = Math.floor((x-chrom.pixelStart)*basesPerPixel)+chrom.baseStart;
						endBase = Math.floor(startBase+basesPerPixel);
					}
			});
			if(datasetsFeatureData[dsID].sortChromStart == "chr"+chromNum+":"+startBase && datasetsFeatureData[dsID].sortChromEnd == "chr"+chromNum+":"+endBase)
			{
				if(datasetsFeatureData[dsID].sortDir == "ASC")
					datasetsFeatureData[dsID].sortDir = "DESC";
				else
					datasetsFeatureData[dsID].sortDir = "ASC";
			}
			else
			{
				datasetsFeatureData[dsID].sortChromStart = 'chr'+chromNum+":"+startBase;
				datasetsFeatureData[dsID].sortChromEnd = 'chr'+chromNum+":"+endBase;
				datasetsFeatureData[dsID].sortDir = "ASC";
			}
			
			return updateDataset(document.getElementById(dsID));
		}
	}
	else
	{
		if($("#hmClick").val() == "sort")
		{
		
		// genesets just sort, for now
		var sortGene;
		$.each(datasetsFeatureData[dsID].geneSetInfo, function(i,geneSet) {
			if(x > geneSet.pixelStart && x < geneSet.pixelEnd)
			{
				$.each(geneSet.geneInfo, function(i,geneSetGene) {
					if(x >= geneSetGene.pixelStart && x <= geneSetGene.pixelEnd)
					{
						sortGene = geneSetGene.geneName;
					}
				});
			}
		});

		// clear out the feature sort
		datasetsFeatureData[dsID].sortFeatures = undefined;
		datasetsFeatureData[dsID].sortFeaturesDir = undefined;
		datasetsFeatureData[dsID].sortChromStart = undefined;
		datasetsFeatureData[dsID].sortChromEnd = undefined;
		
		if(datasetsFeatureData[dsID].sortGene == sortGene)
		{
			if(datasetsFeatureData[dsID].sortDir == "ASC")
				datasetsFeatureData[dsID].sortDir = "DESC";
			else
				datasetsFeatureData[dsID].sortDir = "ASC";
		}
		else
		{
			datasetsFeatureData[dsID].sortGene = sortGene;
			datasetsFeatureData[dsID].sortDir = "ASC";
		}
		
		return updateDataset(document.getElementById(dsID));
		}
		else
		{
			alert("Geneset zooming is not yet supported");
		}
	}
}

function viewRegionInGenomeBrowser()
{
	/*var chrom = "1";
	var start = $("#chromStartPos").val();
	if(!start) start = "1";
	else 
	{
		var chromStartA = start.split(':');
		start = chromStartA[1];
		chrom = chromStartA[0];
	}
	var end = $("#chromEndPos").val();
	if(!end) end = "10000";
	else end = end.split(':')[1];*/
	var chromPosA = $("#chromPos").val().split(/-/);
	var chromStart = chromPosA[0];
	var chromEndPos = "-1";
	if(chromPosA.length == 2)
	{
		var chromEnd = chromPosA[1].split(/:/);
		//console.log(chromPosA);
		if(chromEnd.length == 2)
			chromEndPos = chromEnd[1];
		else
			chromEndPos = chromPosA[1];
	}
	
	var trackList = "refGene=hide&mgcFullMrna=hide&mrna=hide&intronEst=hide&multiz28way=hide&snp129=hide&rmsk=hide&wgEncodeSangerGencode=hide&knownGene=full";
	$("#datasetsInnerTable select").each(function(index, domElement) 
		{
			if($(domElement).val() != "hide")
				trackList += "&"+$(domElement).attr("id")+"=full";
			else
				trackList += "&"+$(domElement).attr("id")+"=hide";
		});
	var currLoc = window.location+"";
	var locArray = currLoc.split('/');
	//var genomeLoc = locArray[0]+"//"+locArray[2]+"/cgi-bin/hgTracks?position=chr"+chrom+":"+start+"-"+end;
	var genomeLoc = locArray[0]+"//"+locArray[2]+"/cgi-bin/hgTracks?"+trackList+"&position="+chromStart
	if (chromEndPos != "-1")
		genomeLoc += "-"+chromEndPos;
	//console.log(start);
	window.open(genomeLoc);
}

function save()
{
	var dataVars = new Object;
	dataVars.hgh2_mode = "save";
	//dataVars.hgh2_vars = new Array();
	var datasets = new Array();

	$("#datasetsInnerTable select").each(function(index, domElement) 
	{
		//updateDataset(domElement);
		if(datasetsFeatureData[domElement.id].visible)
		{
			//console.log(serialize(datasetsFeatureData[domElement.id]));
			datasets.push(datasetsFeatureData[domElement.id]);
		}
	});

	//dataVars.hgh2_vars = datasets.join(",");
	dataVars.hgh2_vars = JSON.stringify(datasets);
	
	$.ajax({
			type: "POST",
			url: "/cgi-bin/hgHeatmap2",
			data: dataVars,
			dataType: "json",
			success: saveCallback,
			error: ajaxError,
			cache: true
	});
	
}

function saveCallback(data)
{
	//var baseURL = window.location.protocol+"//"+window.location.host+window.location.pathname;
	//console.log(baseURL);
	//var savedURL = baseURL.split(/\?/)[0]+data.key;
	//$("#shareUrl").val(baseURL+"?"+data.key);
	$("#shareUrl").val(data.key);
	if(data.err)
		return hghError('none',data.err);	
}

function load(key)
{
	
	var dataVars = new Object;
	dataVars.hgh2_mode = "load";

	if(key)
		dataVars.hgh2_key = key;
	else if($("#shareUrl").val())
		dataVars.hgh2_key = $("#shareUrl").val();
		//dataVars.hgh2_key = $("#shareUrl").val().split(/\?/)[1];
	
	$.ajax({
			type: "POST",
			url: "/cgi-bin/hgHeatmap2",
			data: dataVars,
			dataType: "json",
			success: loadCallback,
			error: ajaxError,
			cache: true
	});
	
}

function loadCallback(data)
{
	if(data.err)
		return hghError('none',data.err);	
	var mode = "chrom";
	$.each(data, function(i,ds) {
		
		datasetsFeatureData[ds.dsID] = ds;
		//datasetsFeatureData[ds.dsID].visible = false;
		datasetsFeatureData[ds.dsID].justLoaded = true;
		$("#"+ds.dsID).val(datasetsFeatureData[ds.dsID].dropdown);
		//console.log(datasetsFeatureData[ds.dsID].mode);
		$("#dsDisplayAs").val(datasetsFeatureData[ds.dsID].type);
		if(datasetsFeatureData[ds.dsID].type == "chrom")
		{
			$('#dsConfigureChromDisplayCell').show();
			$('#dsConfigureGSDisplayCell').hide();
			//$("#chromStartPos").val(datasetsFeatureData[ds.dsID].chromStart);
			//$("#chromEndPos").val(datasetsFeatureData[ds.dsID].chromEnd);
			if(datasetsFeatureData[ds.dsID].chromStart)
				$("#chromPos").val(datasetsFeatureData[ds.dsID].chromStart+"-"+datasetsFeatureData[ds.dsID].chromEnd);
		}
		else
		{
			$('#dsConfigureChromDisplayCell').hide();
			$('#dsConfigureGSDisplayCell').show();
			$('#geneSearchDisplayOL').empty();
			$.each(datasetsFeatureData[ds.dsID].geneSets, function(i,geneSet)
			{
				var span = $("<span></span>").addClass("hghListItemLabel").text(geneSet);
				var a = $("<a href='javascript:void(0);' class='hghListItemRemove' onclick='$(this).parent(\"li\").remove();'><img src='close.gif'></a>")
				$("<li></li>").addClass("hghListItem").append(span).append(a).appendTo("#geneSearchDisplayOL");
			});
		}
	});
	
	updateAllDatasets();
	
	// clear out the just loaded flags
	$("#datasetsInnerTable select").each(function(index, domElement) 
	{
		if(datasetsFeatureData[domElement.id].justLoaded)
			datasetsFeatureData[domElement.id].justLoaded = false;
	});
}


function userGenesetListValidate()
{
	var dataVars = new Object;
	dataVars.hgh2_mode = "checkGeneList";
	dataVars.hgh2_matchingGenes = $('#userGenesetListTextarea').val();
	
	$.ajax({
			type: "POST",
			url: "/cgi-bin/hgHeatmap2",
			data: dataVars,
			dataType: "json",
			success: userGenesetListValidateCallback,
			error: ajaxError,
			cache: true
	});	
}

function userGenesetListValidateCallback(data)
{
	//if(data.err)
	//	return hghError('none',data.err);	
	var geneSetResultHTML = '';
	$('#userGenesetListSearchGeneList').empty();
	$('#userGenesetListTextarea').val('');
	$.each(data.validated ,function(i,gene) {
		geneSetResultHTML += "<li class='hghListItem' gene='"+gene+"'><span class='hghListItemLabel' gene='"+gene+"'>"+gene+"</span></li>";
		$('#addUserGenesetList').show();
	});
	$('#userGenesetListSearchGeneList').append(geneSetResultHTML);
	
}

var current_token_id = 0;
var current_user_id = 0;

function login()
{
	$('#incorrectLogin').hide();
	
	var dataVars = new Object;
	dataVars.hgh2_mode = "getToken";
	
	$.ajax({
			type: "POST",
			url: "/cgi-bin/hgHeatmap2",
			data: dataVars,
			dataType: "json",
			success: loginGotToken,
			error: ajaxError,
			cache: true
	});	
	
}

function loginGotToken(data)
{
	//console.log(data);
	var dataVars = new Object;
	dataVars.hgh2_mode = "login";
	dataVars.hgh2_login = $("#username").val();
	dataVars.hgh2_pass = MD5(MD5($("#rawPassword").val())+data.token);
	dataVars.hgh2_tokenId = data.token_id;
	
	current_token_id = data.token_id;
	
	$.ajax({
			type: "POST",
			url: "/cgi-bin/hgHeatmap2",
			data: dataVars,
			dataType: "json",
			success: loginCallback,
			error: ajaxError,
			cache: true
	});	
}

function loginCallback(data)
{
	//console.log(data);
	if(data.login == "success")
	{
		current_user_id = data.user_id;

		var dataVars = new Object;
		dataVars.hgh2_mode = "getUserDatasets";
		dataVars.hgh2_tokenId = current_token_id;
		dataVars.hgh2_userId = current_user_id;
			
		$.ajax({
				type: "POST",
				url: "/cgi-bin/hgHeatmap2",
				data: dataVars,
				dataType: "json",
				success: handleLoadUserDatasets,
				error: ajaxError,
				cache: true
		});	
	}
	else
		$('#incorrectLogin').show();
}


function handleLoadUserDatasets(data)
{
	if(data.err)
		return hghError('none',data.err);

	$('#datasetsInnerTableBodyPrivate').empty();

	if(data.warning)
		return; // eventually display this to the user
	
	$.each(data, function(group,dataset)
		{
			var row1,row2;
			$('#datasetsInnerTableBodyPrivate').append("<tr class='heatmapControlHeader'><th colspan='5'>"+group+"</th></tr>");
		$.each(dataset, function(i,dataset)
		{
			if((i % 5) == 0)
			{
				if(row1)
					$('#datasetsInnerTableBodyPrivate').append(row1).append(row2);
				
				row1 = $("<tr></tr>");
				row2 = $("<tr></tr>");
			}
			var label = "";
			if(dataset.local_url != "")
				label = $("<a href='"+dataset.local_url+"' target='_blank'>"+dataset.shortLabel+"</a>");
			else
				label = dataset.shortLabel;
			var labelCell = $("<td></td>").append(label).appendTo(row1);
			var select = $("<select></select>").attr('id',dataset.name).attr('name',dataset.name)
			.attr('fullname',dataset.longLabel).attr('num',dataset.N).addClass('hiddenText')
			.change(function() {updateDataset(this); })
			.append("<option value='hide'>Hide</option><option value='heatmap'>Heatmap</option><option value='summary'>Summary</option>");
			//select.append(options);
			var selectCell = $("<td></td>").append(select).appendTo(row2);
			
		});
		if(row1)
			$('#datasetsInnerTableBodyPrivate').append(row1).append(row2);
	});
	
	$("#datasetsInnerTableBodyPrivate select").each(function(index, domElement) { updateDataset(domElement); });

}

function updateAnnotationTrack()
{
	if($('#refGene').val() != "hide" && $('#dsDisplayAs').val() == 'chrom')
	{
		$('#refGene').removeClass('hiddenText').addClass('normalText');
		var dsID = "refGene";
				
		if($('#'+dsID+"HeatmapImage").length > 0)
		{
			var loadingOverlay = $("<div></div>").attr("id",dsID+"LoadingOverlay").css("position","absolute");
			loadingOverlay.css("top",$('#'+dsID+"HeatmapImage").position().top);
			var divLeft = $('#'+dsID+"HeatmapImage").position().left + 2;
			loadingOverlay.css("left",divLeft);
			var width = $('#'+dsID+"HeatmapImage").width() - 5;
			loadingOverlay.width(width);
			loadingOverlay.height($('#'+dsID+"HeatmapImage").height());
			loadingOverlay.css("background","white");
			loadingOverlay.css("opacity",0.75);
			var imgLeft = ($('#'+dsID+"HeatmapImage").width() / 2) - 110;
			var imgTop = ($('#'+dsID+"HeatmapImage").height() / 2 ) - 10;
			$("<img src='loading.gif'/>").css("position","relative").css('left',imgLeft).css('top',imgTop).appendTo(loadingOverlay);
			loadingOverlay.appendTo("body");
		}
		else
		{
			$('#'+dsID+"Heatmap").remove();

			var newRow = $("<tbody id='"+dsID+"Heatmap'><tr id='"+dsID+"HeatmapHeader'><td colspan='4' style='text-align:center;' class='heatmapDatasetHeader'>RefSeq Genes</td></tr><tr id='"+dsID+"HeatmapLoading' ><td colspan='4' align='center'><img src='loading.gif'/></td></tr></tbody>");
			newRow.hide().appendTo($('#heatmapTable')).fadeIn('normal');
			$('#'+dsID+"SettingsImg").click(function() { $('#'+dsID+"DatasetSettings").toggle(); } );
		}
		
		var dataVars = new Object;
		dataVars.hgh2_mode = "getAnnotation";
		dataVars.hgh2_type = "chrom";
		if($("#chromPos").val())
		{
			var chromPosArray = $("#chromPos").val().replace("CHR","chr").split(/-/);
			dataVars.hgh2_chromStart = chromPosArray[0];
			dataVars.hgh2_chromEnd = chromPosArray[1];
		}		
		$.ajax({
				type: "POST",
				url: "/cgi-bin/hgHeatmap2",
				data: dataVars,
				dataType: "json",
				success: insertImagesForDataset_Annotation,
				error: ajaxError,
				datasetId: dsID,
				viewType: dataVars.hgh2_type,  // JZS: this fixed zoom bug
				justLoaded: false,
				cache: true
		});
	}
	else
	{
		// disable dataset
		var id = "refGeneHeatmap";
		$('#'+id).fadeOut("normal",function() {$(this).remove();});
		datasetsFeatureData["refGene"] = new Object(); // reset the data array
		$('#refGene').removeClass('normalText').addClass('hiddenText');
	}
}
function insertImagesForDataset_Annotation(data)
{
	var dsID = this.datasetId;
	//console.log(data);
	var newEl;

	if(data.err)
		return hghError(dsID,data.err);	
	
	// cache the results in a global obj
	if(!datasetsFeatureData[dsID])
		datasetsFeatureData[dsID] = new Object();
	datasetsFeatureData[dsID].type = this.viewType;  
	datasetsFeatureData[dsID].chromInfo = data.chromInfo;
	datasetsFeatureData[dsID].geneSetInfo = data.geneSetInfo;
	datasetsFeatureData[dsID].enabledFeatures = data.featureInfo;
	datasetsFeatureData[dsID].samples = "";  // JZS: set to empty string to avoid undefined error
	datasetsFeatureData[dsID].dsID = dsID;
	datasetsFeatureData[dsID].sampleHeight = data.sampleHeight;
	
	$('#'+dsID+"HeatmapLoading").remove();
	$('#'+dsID+"LoadingOverlay").remove();
	
	if(datasetsFeatureData[dsID].visible && !this.justLoaded)
	{
		
		if($("#"+dsID+"HeatmapImage").length > 0)
		{
			$('#'+dsID+"HeatmapImageScaleCell").empty();
			$("#"+dsID+"HeatmapImage").attr("src",data.annotation).addClass("clickme");
		}
		else
		{
			$('#'+dsID+"HeatmapImageScaleCell").empty();
			$("#"+dsID+"HeatmapImageCell").html("");
			$("<img/>").addClass("heatmapImg").attr("dataset",dsID).addClass("clickme").attr("src",data.annotation).attr("id",dsID+"HeatmapImage").appendTo("#"+dsID+"HeatmapImageCell");
		}
		$("#"+dsID+"HeatmapRowInnerLabel").remove();
	}
	else
	{
		// add the main dataset table after the dataset's header
		newEl = $("<tr></tr>").attr("id",this.datasetId+"HeatmapRow");
		var cellEl = $("<td></td>").appendTo(newEl);
		var table = $("<table></table>").attr("id",this.datasetId+"HeatmapRowTable").addClass('heatmapRowTable').appendTo(cellEl);
		var tbody = $("<tbody></tbody>").attr("id",this.datasetId+"HeatmapRowTableBody").appendTo(table);	 
		$('#'+dsID+"HeatmapHeader").after(newEl);
		
		// add the heatmap image row
		newEl = $("<tr></tr>").attr("id",dsID+"HeatmapRowInner");
		var scaleCell = $("<td></td>").attr('id',dsID+"HeatmapImageScaleCell").addClass('ImageScaleCell');
		scaleCell.appendTo(newEl);
		var cellEl = $("<td></td>").attr("id",dsID+"HeatmapImageCell").css('width',700).appendTo(newEl);
		cellEl.css('height',1);
		$("<img/>").addClass("heatmapImg").addClass("clickme").attr("dataset",dsID).attr("src",data.annotation).attr("id",dsID+"HeatmapImage").appendTo(cellEl);
		$("<td></td>").appendTo(newEl);
		$('#'+dsID+"HeatmapRowTableBody").append(newEl);
		
		// add the cell for the subgrp img
		newEl = $("<td></td>").attr("id",dsID+"HeatmapRowInnerSubgroupCell");
		$('#'+dsID+"HeatmapRowInner").append(newEl);
		
		// add the feature img cell
		newEl = $("<td></td>").attr('rowspan',3).attr("id",dsID+'HeatmapRowInnerFeatureCell').css('width',250);
		$('#'+dsID+"HeatmapRowInner").append(newEl);
		
	}

	// build the row to contain the chrom/geneset labels
	newEl = $("<tr></tr>").attr("id",this.datasetId+"HeatmapRowInnerLabel");
	$("<td></td>").appendTo(newEl);
	$("<td></td>").attr("id",dsID+"HeatmapRowInnerLabelCell").appendTo(newEl);

	//$("<td></td>").attr("colspan","4").appendTo(newEl);
	$('#'+dsID+"HeatmapRowTableBody").append(newEl);
	//$("<div></div>").addClass("HeatmapRowInnerLabelCellDiv").attr("id",dsID+"HeatmapRowInnerLabelCellDiv").appendTo("#"+dsID+"HeatmapRowInnerLabelCell");
	$("<div></div>").attr("id",dsID+"HeatmapRowInnerLabelCellDiv").addClass("HeatmapRowInnerLabelCellDiv").appendTo("#"+dsID+"HeatmapRowInnerLabelCell");
	
	getHeatmapLabels(dsID);
	
	if(data.chromInfo && data.chromInfo.length == 1)
		$('#viewInGenomeBrowser').attr('disabled',0);
	else
		$('#viewInGenomeBrowser').attr('disabled',1);
	
	if(data.chromInfo)
	{
		var totalBases = 0;
		$.each(data.chromInfo, function(i,chromArray) {
				totalBases += chromArray.baseEnd - chromArray.baseStart;
		});
		$("#chromPosSize").html("size "+addCommas(totalBases)+" bp.");
	}
	else
		$("#chromPosSize").html('');
	
	// add the click handler to the heatmap img
	$('#'+dsID+"HeatmapImage").click(heatmapClick);
	
	// this needs to be set late here case we use it earlier in the fxn
	datasetsFeatureData[dsID].visible = true;	 
	
	// add the two tooltip hover events, and the feature click sort event
	$(".heatmapImg").hover(function() { $(this).mousemove(heatmapImageMouseMove); } ,function() { clearTimeout(toolTipTimeout); $(this).unbind('mousemove'); $("#tooltip").fadeOut('fast') } );
}

function positionKeyPress(event)
{
	//console.log(event);
	if(event.keyCode == 13)
		return updateAllDatasets();
}

// utility fxn to add commas to a number str
// found at: http://www.mredkj.com/javascript/numberFormat.html
function addCommas(nStr)
{
	nStr += '';
	x = nStr.split('.');
	x1 = x[0];
	x2 = x.length > 1 ? '.' + x[1] : '';
	var rgx = /(\d+)(\d{3})/;
	while (rgx.test(x1)) {
		x1 = x1.replace(rgx, '$1' + ',' + '$2');
	}
	return x1 + x2;
}

/**
*
*  Secure Hash Algorithm (SHA1)
*  http://www.webtoolkit.info/
*
**/

function SHA1 (msg) {

    function rotate_left(n,s) {
        var t4 = ( n<<s ) | (n>>>(32-s));
        return t4;
    };

    function lsb_hex(val) {
        var str="";
        var i;
        var vh;
        var vl;

        for( i=0; i<=6; i+=2 ) {
            vh = (val>>>(i*4+4))&0x0f;
            vl = (val>>>(i*4))&0x0f;
            str += vh.toString(16) + vl.toString(16);
        }
        return str;
    };

    function cvt_hex(val) {
        var str="";
        var i;
        var v;

        for( i=7; i>=0; i-- ) {
            v = (val>>>(i*4))&0x0f;
            str += v.toString(16);
        }
        return str;
    };


    function Utf8Encode(string) {
        string = string.replace(/\r\n/g,"\n");
        var utftext = "";

        for (var n = 0; n < string.length; n++) {

            var c = string.charCodeAt(n);

            if (c < 128) {
                utftext += String.fromCharCode(c);
            }
            else if((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            }
            else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }

        }

        return utftext;
    };

    var blockstart;
    var i, j;
    var W = new Array(80);
    var H0 = 0x67452301;
    var H1 = 0xEFCDAB89;
    var H2 = 0x98BADCFE;
    var H3 = 0x10325476;
    var H4 = 0xC3D2E1F0;
    var A, B, C, D, E;
    var temp;

    msg = Utf8Encode(msg);

    var msg_len = msg.length;

    var word_array = new Array();
    for( i=0; i<msg_len-3; i+=4 ) {
        j = msg.charCodeAt(i)<<24 | msg.charCodeAt(i+1)<<16 |
        msg.charCodeAt(i+2)<<8 | msg.charCodeAt(i+3);
        word_array.push( j );
    }

    switch( msg_len % 4 ) {
        case 0:
            i = 0x080000000;
        break;
        case 1:
            i = msg.charCodeAt(msg_len-1)<<24 | 0x0800000;
        break;

        case 2:
            i = msg.charCodeAt(msg_len-2)<<24 | msg.charCodeAt(msg_len-1)<<16 | 0x08000;
        break;

        case 3:
            i = msg.charCodeAt(msg_len-3)<<24 | msg.charCodeAt(msg_len-2)<<16 | msg.charCodeAt(msg_len-1)<<8    | 0x80;
        break;
    }

    word_array.push( i );

    while( (word_array.length % 16) != 14 ) word_array.push( 0 );

    word_array.push( msg_len>>>29 );
    word_array.push( (msg_len<<3)&0x0ffffffff );


    for ( blockstart=0; blockstart<word_array.length; blockstart+=16 ) {

        for( i=0; i<16; i++ ) W[i] = word_array[blockstart+i];
        for( i=16; i<=79; i++ ) W[i] = rotate_left(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1);

        A = H0;
        B = H1;
        C = H2;
        D = H3;
        E = H4;

        for( i= 0; i<=19; i++ ) {
            temp = (rotate_left(A,5) + ((B&C) | (~B&D)) + E + W[i] + 0x5A827999) & 0x0ffffffff;
            E = D;
            D = C;
            C = rotate_left(B,30);
            B = A;
            A = temp;
        }

        for( i=20; i<=39; i++ ) {
            temp = (rotate_left(A,5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff;
            E = D;
            D = C;
            C = rotate_left(B,30);
            B = A;
            A = temp;
        }

        for( i=40; i<=59; i++ ) {
            temp = (rotate_left(A,5) + ((B&C) | (B&D) | (C&D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff;
            E = D;
            D = C;
            C = rotate_left(B,30);
            B = A;
            A = temp;
        }

        for( i=60; i<=79; i++ ) {
            temp = (rotate_left(A,5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff;
            E = D;
            D = C;
            C = rotate_left(B,30);
            B = A;
            A = temp;
        }

        H0 = (H0 + A) & 0x0ffffffff;
        H1 = (H1 + B) & 0x0ffffffff;
        H2 = (H2 + C) & 0x0ffffffff;
        H3 = (H3 + D) & 0x0ffffffff;
        H4 = (H4 + E) & 0x0ffffffff;

    }

    var temp = cvt_hex(H0) + cvt_hex(H1) + cvt_hex(H2) + cvt_hex(H3) + cvt_hex(H4);

    return temp.toLowerCase();

}

/**
*
*  MD5 (Message-Digest Algorithm)
*  http://www.webtoolkit.info/
*
**/

var MD5 = function (string) {

    function RotateLeft(lValue, iShiftBits) {
        return (lValue<<iShiftBits) | (lValue>>>(32-iShiftBits));
    }

    function AddUnsigned(lX,lY) {
        var lX4,lY4,lX8,lY8,lResult;
        lX8 = (lX & 0x80000000);
        lY8 = (lY & 0x80000000);
        lX4 = (lX & 0x40000000);
        lY4 = (lY & 0x40000000);
        lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF);
        if (lX4 & lY4) {
            return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
        }
        if (lX4 | lY4) {
            if (lResult & 0x40000000) {
                return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
            } else {
                return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
            }
        } else {
            return (lResult ^ lX8 ^ lY8);
        }
     }

     function F(x,y,z) { return (x & y) | ((~x) & z); }
     function G(x,y,z) { return (x & z) | (y & (~z)); }
     function H(x,y,z) { return (x ^ y ^ z); }
    function I(x,y,z) { return (y ^ (x | (~z))); }

    function FF(a,b,c,d,x,s,ac) {
        a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
        return AddUnsigned(RotateLeft(a, s), b);
    };

    function GG(a,b,c,d,x,s,ac) {
        a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
        return AddUnsigned(RotateLeft(a, s), b);
    };

    function HH(a,b,c,d,x,s,ac) {
        a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
        return AddUnsigned(RotateLeft(a, s), b);
    };

    function II(a,b,c,d,x,s,ac) {
        a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
        return AddUnsigned(RotateLeft(a, s), b);
    };

    function ConvertToWordArray(string) {
        var lWordCount;
        var lMessageLength = string.length;
        var lNumberOfWords_temp1=lMessageLength + 8;
        var lNumberOfWords_temp2=(lNumberOfWords_temp1-(lNumberOfWords_temp1 % 64))/64;
        var lNumberOfWords = (lNumberOfWords_temp2+1)*16;
        var lWordArray=Array(lNumberOfWords-1);
        var lBytePosition = 0;
        var lByteCount = 0;
        while ( lByteCount < lMessageLength ) {
            lWordCount = (lByteCount-(lByteCount % 4))/4;
            lBytePosition = (lByteCount % 4)*8;
            lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount)<<lBytePosition));
            lByteCount++;
        }
        lWordCount = (lByteCount-(lByteCount % 4))/4;
        lBytePosition = (lByteCount % 4)*8;
        lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80<<lBytePosition);
        lWordArray[lNumberOfWords-2] = lMessageLength<<3;
        lWordArray[lNumberOfWords-1] = lMessageLength>>>29;
        return lWordArray;
    };

    function WordToHex(lValue) {
        var WordToHexValue="",WordToHexValue_temp="",lByte,lCount;
        for (lCount = 0;lCount<=3;lCount++) {
            lByte = (lValue>>>(lCount*8)) & 255;
            WordToHexValue_temp = "0" + lByte.toString(16);
            WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2);
        }
        return WordToHexValue;
    };

    function Utf8Encode(string) {
        string = string.replace(/\r\n/g,"\n");
        var utftext = "";

        for (var n = 0; n < string.length; n++) {

            var c = string.charCodeAt(n);

            if (c < 128) {
                utftext += String.fromCharCode(c);
            }
            else if((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            }
            else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }

        }

        return utftext;
    };

    var x=Array();
    var k,AA,BB,CC,DD,a,b,c,d;
    var S11=7, S12=12, S13=17, S14=22;
    var S21=5, S22=9 , S23=14, S24=20;
    var S31=4, S32=11, S33=16, S34=23;
    var S41=6, S42=10, S43=15, S44=21;

    string = Utf8Encode(string);

    x = ConvertToWordArray(string);

    a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;

    for (k=0;k<x.length;k+=16) {
        AA=a; BB=b; CC=c; DD=d;
        a=FF(a,b,c,d,x[k+0], S11,0xD76AA478);
        d=FF(d,a,b,c,x[k+1], S12,0xE8C7B756);
        c=FF(c,d,a,b,x[k+2], S13,0x242070DB);
        b=FF(b,c,d,a,x[k+3], S14,0xC1BDCEEE);
        a=FF(a,b,c,d,x[k+4], S11,0xF57C0FAF);
        d=FF(d,a,b,c,x[k+5], S12,0x4787C62A);
        c=FF(c,d,a,b,x[k+6], S13,0xA8304613);
        b=FF(b,c,d,a,x[k+7], S14,0xFD469501);
        a=FF(a,b,c,d,x[k+8], S11,0x698098D8);
        d=FF(d,a,b,c,x[k+9], S12,0x8B44F7AF);
        c=FF(c,d,a,b,x[k+10],S13,0xFFFF5BB1);
        b=FF(b,c,d,a,x[k+11],S14,0x895CD7BE);
        a=FF(a,b,c,d,x[k+12],S11,0x6B901122);
        d=FF(d,a,b,c,x[k+13],S12,0xFD987193);
        c=FF(c,d,a,b,x[k+14],S13,0xA679438E);
        b=FF(b,c,d,a,x[k+15],S14,0x49B40821);
        a=GG(a,b,c,d,x[k+1], S21,0xF61E2562);
        d=GG(d,a,b,c,x[k+6], S22,0xC040B340);
        c=GG(c,d,a,b,x[k+11],S23,0x265E5A51);
        b=GG(b,c,d,a,x[k+0], S24,0xE9B6C7AA);
        a=GG(a,b,c,d,x[k+5], S21,0xD62F105D);
        d=GG(d,a,b,c,x[k+10],S22,0x2441453);
        c=GG(c,d,a,b,x[k+15],S23,0xD8A1E681);
        b=GG(b,c,d,a,x[k+4], S24,0xE7D3FBC8);
        a=GG(a,b,c,d,x[k+9], S21,0x21E1CDE6);
        d=GG(d,a,b,c,x[k+14],S22,0xC33707D6);
        c=GG(c,d,a,b,x[k+3], S23,0xF4D50D87);
        b=GG(b,c,d,a,x[k+8], S24,0x455A14ED);
        a=GG(a,b,c,d,x[k+13],S21,0xA9E3E905);
        d=GG(d,a,b,c,x[k+2], S22,0xFCEFA3F8);
        c=GG(c,d,a,b,x[k+7], S23,0x676F02D9);
        b=GG(b,c,d,a,x[k+12],S24,0x8D2A4C8A);
        a=HH(a,b,c,d,x[k+5], S31,0xFFFA3942);
        d=HH(d,a,b,c,x[k+8], S32,0x8771F681);
        c=HH(c,d,a,b,x[k+11],S33,0x6D9D6122);
        b=HH(b,c,d,a,x[k+14],S34,0xFDE5380C);
        a=HH(a,b,c,d,x[k+1], S31,0xA4BEEA44);
        d=HH(d,a,b,c,x[k+4], S32,0x4BDECFA9);
        c=HH(c,d,a,b,x[k+7], S33,0xF6BB4B60);
        b=HH(b,c,d,a,x[k+10],S34,0xBEBFBC70);
        a=HH(a,b,c,d,x[k+13],S31,0x289B7EC6);
        d=HH(d,a,b,c,x[k+0], S32,0xEAA127FA);
        c=HH(c,d,a,b,x[k+3], S33,0xD4EF3085);
        b=HH(b,c,d,a,x[k+6], S34,0x4881D05);
        a=HH(a,b,c,d,x[k+9], S31,0xD9D4D039);
        d=HH(d,a,b,c,x[k+12],S32,0xE6DB99E5);
        c=HH(c,d,a,b,x[k+15],S33,0x1FA27CF8);
        b=HH(b,c,d,a,x[k+2], S34,0xC4AC5665);
        a=II(a,b,c,d,x[k+0], S41,0xF4292244);
        d=II(d,a,b,c,x[k+7], S42,0x432AFF97);
        c=II(c,d,a,b,x[k+14],S43,0xAB9423A7);
        b=II(b,c,d,a,x[k+5], S44,0xFC93A039);
        a=II(a,b,c,d,x[k+12],S41,0x655B59C3);
        d=II(d,a,b,c,x[k+3], S42,0x8F0CCC92);
        c=II(c,d,a,b,x[k+10],S43,0xFFEFF47D);
        b=II(b,c,d,a,x[k+1], S44,0x85845DD1);
        a=II(a,b,c,d,x[k+8], S41,0x6FA87E4F);
        d=II(d,a,b,c,x[k+15],S42,0xFE2CE6E0);
        c=II(c,d,a,b,x[k+6], S43,0xA3014314);
        b=II(b,c,d,a,x[k+13],S44,0x4E0811A1);
        a=II(a,b,c,d,x[k+4], S41,0xF7537E82);
        d=II(d,a,b,c,x[k+11],S42,0xBD3AF235);
        c=II(c,d,a,b,x[k+2], S43,0x2AD7D2BB);
        b=II(b,c,d,a,x[k+9], S44,0xEB86D391);
        a=AddUnsigned(a,AA);
        b=AddUnsigned(b,BB);
        c=AddUnsigned(c,CC);
        d=AddUnsigned(d,DD);
    }

    var temp = WordToHex(a)+WordToHex(b)+WordToHex(c)+WordToHex(d);

    return temp.toLowerCase();
}
