/*******************************************************************************
 *
 *	JS Layer Class
 *
 ******************************************************************************/


//	CONSTANTS
var RAMPtype_CATEGORICAL	= 'categorical';							//	Discrete categories, e.g. 'state, territory, self-governed'
var RAMPtype_RANGE			= 'range';									//	Continuous categories, e.g. 'pop size from 0-100,100-200,etc.'
var RAMPtype_RANGE_SYMMETRICAL	= 'range_symmetrical';					//	Continuous categories centered on 0 with no separate infinity value, e.g. 'pop change from -100,-10,0,10,100.'
var RAMPtype_SINGLE			= 'single';									//	Just one fill color, e.g. lakes
var RAMPtype_LINE			= 'line';									//	Line, e.g. rivers, railroads
var RAMPtype_POINT			= 'point';									//	Point data, e.g. Big Cities


/***************************************************************************
 *	LEGENDMGR CLASS
 *
 */
function LegendMgr( newMapID ) {													//	This might need a better name...this is instantiated as 'legendMgr'
	var self				= this;
	
	this.mapID				= newMapID;
	this.layerArray			= new Array();
	this.selectedLegendLayerID	= 'none';										//	String.  AKA 'selectedLegendLayer'.  Used to keep track of the currently selected layer.  LAYER->METADATA->LYRNAME, e.g. 'World'

	this.mainLayerCheckBoxID = 'empty';											/*	Points to the checkbox next to the main data layer menu.
																					We need this so that when a new menu item is selected, we can
																					go find the checkbox and change its values to reflect the new
																					layer.
																				*/
	
	this.delayedDoSubmit = '';													/*	Point this to the DoSubmit() function when a checkbox
																					is clicked.  This is necessary b/c we need to set the
																					currently selected layer first, then call DoSubmit.
																				*/
	
	this.loadFromJSON = function( jsonObject, selectedLegendLayer ) {
		self.layerArray = new Array()											//	Clear the existing array first
		
		self.selectedLegendLayerID = selectedLegendLayer;
		
		var u = jsonObject.length;
		for (var i=0; i<u; i++) {
			var l	= new layer;		
			l.loadFromJSON( jsonObject[i] );
			self.addLayer( l );
		}
	}
	
	
	this.getQueryResults = function() {
		var qResultMap = new queryResultsMap();										//	Defined in jsQueryResult.js
		
		var u = self.layerArray.length;
		for (var i=0; i<u; i++) {
			qResultMap.addLayer( self.layerArray[i].getQueryResults() );
		}
		
		return qResultMap;
	}
	
	
	this.initListeners = function() {
		var legendTable = document.getElementById( 'legendTable' );
		addEvent(legendTable, 'click', self.handleClick, false)
		addEvent(legendTable, 'mouseover', self.handleMouseOver, false);		//	Can't use css "hover" here b/c IE doesn't support hover in Tables
		addEvent(legendTable, 'mouseout', self.handleMouseOut, false);			//	Can't use css "hover" here b/c IE doesn't support hover in Tables
	}

	this.handleClick = function( event ) {
		var row;
		
		var selectedRow = ascendDOM( GetElementFromEvent(event), 'tr');
		if (selectedRow.id != '') {
			//	If we're unchecking, then don't select this row
			var id = GetMapIDPre(self.mapID) + 'lyr' + selectedRow.id
			var layerEl = document.getElementById( id );
			//Debug( 'layerEl value = ' + layerEl.value + ' checked =' + layerEl.checked);
			if ( !layerEl.checked ) {
				//	Unchecking it, so don't do anything
				//Debug('unchecking');
			} else {
				//	Checking it and/or selecting it
				
				//	Unselect all rows
				var u = self.layerArray.length;
				for (var i=0; i<u; i++) {
					row = document.getElementById( self.layerArray[i].name );
					if (row != null) {
						row.className = 'legendRow';										//	Clear the "selected" state */
					}
				}
				//	Set the row
				self.setSelectedRow( selectedRow.id );
				
				//	Update disabled state of Timeline Buttons for the current layer
				timelineMgr.updateButtonsState();
				
			}				
			if (self.delayedDoSubmit!='') {
				//Debug('about to call delayedDoSubmit');
				DoSubmit( self.delayedDoSubmit );								//	If set, self.delayedDoSubmit points to the mapID
				self.delayedDoSubmit = '';
			}
		}
	}
	
	this.handleMouseOver = function( event ) {
		//	Select the row
		var selectedRow = ascendDOM( GetElementFromEvent(event), 'tr');
		if (selectedRow.nodeName.toLowerCase() == 'tr') {
			//	Only hover if this is a layer (e.g. id is set)
			if (selectedRow.id != '') {
				selectedRow.className += ' hover';
			}
		}
	}
	
	
	this.handleMouseOut = function( event ) {
		//	Select the row
		var selectedRow = ascendDOM( GetElementFromEvent(event), 'tr');
		if (selectedRow.nodeName.toLowerCase() == 'tr') {
			selectedRow.className = selectedRow.className.replace(' hover','');
		}
	}
	
	this.setSelectedRow = function( rowID ) {
//alert('To Do: need to set selectedRow: jsLayer.js.setSelectedRow');
		selectedRow = document.getElementById( rowID );

		if (selectedRow) {
			//	Select the row
			selectedRow.className += ' selected';
		
			//	Save off the currently selected row
			self.selectedLegendLayerID = selectedRow.id;							//	Using the el's id, which should be the same as LAYER->Name

			//	Save it to the form
			var selectedLegendLayerEl = document.getElementById( GetMapIDPre( self.mapID ) + 'selectedLegendLayer' );
			selectedLegendLayerEl.value = self.selectedLegendLayerID;
		}
	}
	

	/*	We need to change the checkBox ID and VALUE to the newly selected layer so that its visibility is properly set
		when the form is submitted (and the old layer is hidden).
	 */
/*	Old method requiring paramteers
this.handleMainLayerMenuChange = function( mainLayerCheckBoxID, mapID ) {
alert('handleMainLayerMenuchange called by ' + mainLayerCheckBoxID );
		self.changeMainLayerMenu( mainLayerCheckBoxID, mapID );
		DoSubmit( mapID );
	}
*/
/* New method using legendmgr's parameters */
	this.handleMainLayerMenuChange = function() {
		self.changeMainLayerMenu( self.mainLayerCheckBoxID, self.mapID );
		DoSubmit( self.mapID );
	}
	/*	This updates the menu item without doing the submit.
		*	the mainLayerMenu calls self.handleMainLayerMenuChange() directly, which then calls this.
		*	the TimelineTable calls this one directly so that the DoSubmit() is not called.
		We need to change the checkBox ID and VALUE to the newly selected layer so that its visibility is properly set
		when the form is submitted (and the old layer is hidden).
	 */
	this.changeMainLayerMenu = function( mainLayerCheckBoxID, mapID ) {
		var id = GetMapIDPre( mapID );
		
		var theMainLayerMenu = document.getElementById( id + 'selectedMainLayer' );
		var newSelectedLayerName = theMainLayerMenu.options[ theMainLayerMenu.selectedIndex ].value;
		
		var theMainLayerCheckBox = document.getElementById( mainLayerCheckBoxID );
		//	change the id to the new layer's id
		theMainLayerCheckBox.id = GetMapIDPre( mapID ) + 'lyr' + newSelectedLayerName;
		
		//	change the value to the new layer's info
		valueArray = theMainLayerCheckBox.value.split(',');
		theMainLayerCheckBox.value = newSelectedLayerName + ',' + valueArray[1];
		
		//	turn the checkbox on if it isn't already on.  Assume that if you're changing to another layer you want it on by default.
		theMainLayerCheckBox.checked = true;
		
		//	Change the id of the TR for the menu to the new id
		var selectedRow = ascendDOM( theMainLayerMenu, 'tr');
		if (selectedRow.nodeName.toLowerCase() == 'tr') {
			selectedRow.id = newSelectedLayerName;
		} else {
			alert('jsLayer.js->changeMainLayerMenu could not find parent "TR"');
		}
		
		//	Set this as the new selected legend row
		self.setSelectedRow( newSelectedLayerName );
		
		//	Enable/Disable layers based on available data dates
		self.updateMainLayerMenu( mapID );
		
		//	Enable/Disable dates based on available data dates and currently selected layer
		UpdateTimelineDisabledState( mapID, newSelectedLayerName );
	}


	/*	Find the index of the option */
	this.findMenuIndexByValue = function( layerID, mapID ) {
		var id = GetMapIDPre( mapID );
		var theMainLayerMenu = document.getElementById( id + 'selectedMainLayer' );
		var u = theMainLayerMenu.options.length;
		for( var i=0; i<u; i++ ) {
			if ( theMainLayerMenu.options[i].value == layerID ) {
				return i;
			}
		}
		//	Not found
		return -1;
	}
	
	/*	Enable/Disable layers in the menu based on available data dates */
	this.updateMainLayerMenu = function( mapID ) {
		var disabledStatus;
		var datesAvailable;
		var option;
		
		//	Get current map date
		var dateObject = document.getElementById( mapID + '_mapDate' );
		var currentMapDate = dateObject.value;

		//	Get the menu object
		var id = GetMapIDPre( mapID );
		var theMainLayerMenu = document.getElementById( id + 'selectedMainLayer' );

		var u = theMainLayerMenu.options.length;
		for (var i=0; i<u; i++) {
			//	Is there data available for this layer?
			datesAvailable = GetTimelineDataForLayerName( theMainLayerMenu.options[i].value );
			disabledStatus = 'disabled';
			if ( datesAvailable != null ) {
//			if ( typeof( datesAvailable ) != undefined ) {
				if ( datesAvailable.indexOf( currentMapDate ) > -1 ) {
					disabledStatus = '';	
				}
			}
			theMainLayerMenu.options[i].disabled = disabledStatus;
		}		
	}
	
	
	/*
	 *	@param	jsLayerMgr.layer Object		newLayer
	 */
	this.addLayer = function( newLayer ) {
		self.layerArray.push( newLayer );
	}
	
	
	this.getHTML = function( width ) {
		var s = '';
		var u = self.layerArray.length;
		for (var i=0; i<u; i++) {
			if (self.layerArray[i].group == MAINLAYERGROUPNAME) {
				//	Main data layer
				//	Draw the main menu item if it's the selected one.
				if ( self.layerArray[i].getIsSelectedInMenu() ) {
					self.mainLayerCheckBoxID = GetMapIDPre( self.mapID ) + 'lyr' + self.layerArray[i].getName();		// Save this off for reference later
					s = self.layerArray[i].getHTMLwithRamp( self.getMenuHTML( self.mapID, width ), width, self.mapID ) + s;													//	reverse layer order
				}
			} else if ((self.layerArray[i].rampType == RAMPtype_POINT ) || (self.layerArray[i].rampType == RAMPtype_CATEGORICAL)) {
				//	Point Layer
				s = self.layerArray[i].getHTMLwithRamp( '', width, self.mapID ) + s;					// reverse layer order
			} else {
				//	Regular Layer
				s = self.layerArray[i].getHTML( width, self.mapID ) + s;					// reverse layer order
			}
		}
		
		if (mapnote != '') {
			var notes = '<tr class="legendRow"><td colspan="7" class="legendCell small"> NOTES: ' + mapnote + '</td></tr>';
		} else {
			var notes = '';
		}
		
		var head = '';
		//	Add Layer Description Area
		var head = '<tr>';
		head += '<td class="legendTitle centered small">SEL</td>';
		head += '<td class="legendTitle centered small">ON/OFF</td>';
		head += '<td colspan="2" class="legendTitle centered small">LAYERS</td>';
		head += '<td class="legendTitle centered small">Labels</td>';
		head += '<td class="legendTitle centered"></td>';
//		head += '<td width="158px" rowspan="' + u + '"><div id="layerDescriptionAreaLabel">LAYER DESCRIPTION</div><div id="layerDescriptionArea">Move the mouse over a layer to see its description. (Not implemented yet).</div></td>';
		head += '</tr>';

		return '<table id="legendTable" cellspacing="0" border="0" width="450">' + head + s + notes + '</table>';
		/*	We add cellspacing=0 to address the fact that IE doesn't support border-spacing.
			border-collapse doesn't really work here b/c it screws up the borders.  */
	}
	

	/*
	 *	Build the array of layer buttons on the fly after creation
	 */
	this.getLabelBtnIDArray = function() {
		var id = '';
		var a = new Array();
		var u = self.layerArray.length;
		for (var i=0; i<u; i++) {
			id = self.layerArray[i].getLabelBtnID();
			if ( (self.layerArray[i].getLabelsAreAvailable()) && (id != '') && (!self.layerArray[i].isHidden()) ){		//	Only add to the list if labels are available for the layer, and the layer is not hidden
				a.push( id );
			}
		}
		return a;
	}
	
	/**
	 *	Returns the html code for the main layer selection menu.
	 *	Note that this is a recursively used to generate submenus.
	 *
	 *	@parm	string array	menuArray
	 *	@parm	string			selectedLayer	ID of the currently selected layer
	 *	@parm	string			$id				Prefix for the legend.  All parameters will use the prefix.  This allows for each legend to have it's own values.
	 *	@return	string							HTML string
	 *	@access	public
	 */
	this.getMenuHTML = function( mapID, width ) {
		var s = '';
		var id = GetMapIDPre( mapID );
		//Debug( 'setmenu.mainLayerCheckBoxID = ' + mainLayerCheckBoxID );

		var m = self.getMenuOptionHTML( mapID, isIE );	// *** MUST RUN THIS BEFORE DEFINING THE SELECTION or else MainLayerCheckBoxID isn't set.
		/*	On change, the menu calls HandleMainLayerMenuChange (in hmap_MainPage.js) to change the checkbox id and value dynamically. */
		s +='<select name="' + id + 'selectedMainLayer" id="' + id + 'selectedMainLayer" onChange="legendMgr.handleMainLayerMenuChange()" style="font-size:9px; width:' + width + 'px;">';
		s += m;
		s += '</select>';
		s += '<div style="clear:both;"></div>';

		return s;
	}

	/*	Revised pure SELECT method
		This is based on the original SELECT method, but removes all the browser-specific hacks 
		This really ought to be updated to use pure javascript, rather than strings
	*/
	this.getMenuOptionHTML = function( mapID, isIE ) {
		var s = '';
		var value;
		var data;
		var endTag = '';
		var groupNlabelArr;
		var selected;
		var labelGroup;
		var datesAvailable;
		var currentGroup	= '';
		var selectedLayerName;
		
		var disabled;
		
		//	Get current map date
		var dateObject = document.getElementById( mapID + '_mapDate' );
		var currentMapDate = dateObject.value;
		
		var u = self.layerArray.length;
		for (var i=0; i<u; i++ ) {
			if (self.layerArray[i].group != MAINLAYERGROUPNAME) {
				continue;														//	Skip this layer if it's not in the main data layer group
			}
			value	= self.layerArray[i].getName();
			label	= self.layerArray[i].getTitle();
			
			if ( self.layerArray[i].getIsSelectedInMenu() ) {
				selected = ' selected';
				selectedLayerName = value;
			} else {
				selected = '';
			}

			var groupNlabelArr = label.split('::');
			if (groupNlabelArr.length > 1) {
				labelGroup = groupNlabelArr[0];
				label = groupNlabelArr[1];
			} else {
				labelGroup = '';
			}
			//	In the midst of working on subgroup
			if (currentGroup != '') {
				if (labelGroup == currentGroup) {
					//	Label IS the same subgroup => continue working
				} else {
					//	Label does not have a group label => exit subgroup
					//	or Label is a different subgroup => exit subgroup
					currentGroup = '';
				}
			//	Not currently working on subgroup
			}
			if ( (labelGroup != '') && (currentGroup =='') ) {
				//	Label has a subgroup => create new subgroup
				s +=	'<optgroup  label="' + labelGroup + '" style="font-size:10px">';
				currentGroup = labelGroup;
			}
			
			//	Is there data available for this layer?
			datesAvailable = timelineData[value]['dates'];
			//	All other browsers use the 'disabled' parameter
			if ( datesAvailable.indexOf( currentMapDate ) > -1 ) {
				disabled = '';
			} else {
				disabled = 'disabled="disabled"';
			}	
			s +=	'<option value="' + value + '"' + selected + ' style="font-size:9px" ' + disabled + ' >' + label + '</option>';
		}		
		
		//	Update the timeline buttons
		UpdateTimelineDisabledState( mapID, selectedLayerName );
		
		return s;
	}
}




/***************************************************************************
 *	LAYER
 *
 */
function layer() {	
	var self 					= this;
	
	//	PROPERTIES
	var name					= 'untitled';								//	The name of the layer, e.g. .map's LAYER>NAME
	var title					= 'untitled';								//	The label of the layer displayed in the legend
	var group					= 'untitled';								//	Group as defined in .map
	var rampType				= RAMPtype_CATEGORICAL;
	var classes					= new Array();
	var layerState				= '';										//	Value of the GET for the layer, to set visibility and label, e.g. 'm1_lyrStateNames=lyrStateNames,lon'
	var	isVisible				= false;									//	boolean The layer is visible in the map
	var legendDisplay			= '';										//	Display the layer in the legend?
	var labelsAreAvailable		= false;									//	Labels for shape objects are available to be turned on/off.  This deteremines whether or not we show the label button at all.
	var labelBtnID				= '';										//	ID of the Label Button DOM object, if available
	var isSelectedInMenu		= false;									//	Used to set whether this layer is the currently selected Main Data layer.  Also might be useful for alternative approaches w/ multiple menus.
	var queryResults;

	//	METHODS
	this.loadFromJSON = function( jsonObject ) {
		self.setName( 				jsonObject.name );
		self.setTitle( 				jsonObject.title );
		self.setGroup( 				jsonObject.group );
		self.setRampType( 			jsonObject.type );
		self.setLayerState( 		jsonObject.layerState );
		self.setIsVisible( 			jsonObject.isVisible );
		self.setLegendDisplay( 		jsonObject.legendDisplay );
		self.setLabelsAreAvailable( jsonObject.labelsAreAvailable );
		self.setIsSelectedInMenu( 	jsonObject.isSelectedInMenu );
		self.setQueryResults(		jsonObject.queryResult );

		var jsonClassArray	= jsonObject.classes;
		var u = jsonClassArray.length;
		for (var i=0; i<u; i++) {
			var lc	= new lclass;
			lc.label			= 	jsonClassArray[i].classRangeLabel;
			lc.setRangeMin( 		jsonClassArray[i].classRangeMin );
			lc.setRangeMax( 		jsonClassArray[i].classRangeMax );
			lc.fillColor		= 	RGBHexfromDec( jsonClassArray[i].classColor );
			lc.size				=	jsonClassArray[i].classSize;
			
			self.addClass( lc );
		}
	}
	
	
	this.addClass				= function(newlclass) {
		classes.push( newlclass );
	}
	
	this.getClassesLength		= function()		{	return classes.length; }
	
	//	SETTERS/GETTERS
	/*	@param	string	newID */
	this.setName		 		= function(newName)	{	self.name = newName; }
	this.getName	 			= function()		{	return self.name; }
	/*	@param	string	newName */
	this.setTitle		 		= function(newName)	{	self.title = newName; }
	this.getTitle	 			= function()		{	return self.title; }
	/*	@param	string	newName */
	this.setGroup		 		= function(newName)	{	self.group = newName; }
	this.getGroup	 			= function()		{	return self.group; }

	/*	@param	string	rampType	Use the constants defined above. */
	this.setRampType	 		= function(newType)	{	self.rampType = newType; }
	this.getRampType 			= function()		{	return self.rampType; }

	/*	@param	boolean	newStatus */
	this.setLayerState	 		= function(newStr)	{	self.layerState = newStr; }
	this.getLayerState	 		= function()		{	return self.layerState; }

	/*	@param	boolean	newStatus */
	this.setIsVisible	 		= function(newStatus)	{	self.isVisible = newStatus; }
	this.getIsVisible	 		= function()		{	return self.isVisible; }

	/*	@param	boolean	newStatus */
	this.setLegendDisplay	 	= function(newStatus)	{	self.legendDisplay = newStatus; }
	this.getLegendDisplay	 	= function()		{	return self.legendDisplay; }
	this.isHidden				= function() {
		if (self.getLegendDisplay().toLowerCase() == 'hidden') {
			return true;
		} else {
			return false;
		}
	}

	/*	@param	boolean	newStatus */
	this.setLabelsAreAvailable	= function(newStatus)	{	self.labelsAreAvailable = newStatus; }
	this.getLabelsAreAvailable	= function()		{	return self.labelsAreAvailable; }		

	this.setLabelBtnID			= function(newID)	{ self.labelBtnID = newID; }
	this.getLabelBtnID			= function()		{ return self.labelBtnID; }
	
	/*	@param	boolean	newStatus */
	this.setIsSelectedInMenu	= function(newStatus)	{	self.isSelectedInMenu = newStatus; }
	this.getIsSelectedInMenu	= function()		{	return self.isSelectedInMenu; }
	
	/*	@param	array(string)	arr */
	this.setQueryResults			= function(qrArray)		{	
		/*	qrArray comes in with this form:
		 *		Layerarray(	Marker0array( label, data),
		 *					Marker1array( label, data),
		 *					Marker2array( label, data)
		 *				)
		 */
		var qrl	= new queryResultsLayer();
		qrl.loadFromArray(qrArray);
		self.queryResults = qrl;
	}
	this.getQueryResults		= function()		{	return self.queryResults; }



	/*	@param	integer	width		The width of the HTML color bar, in pixels
	 *  @access public				Call this function, don't call the other get*HTML functions directly.
	 */
	this.getHTML = function( width, mapID ) {
		if (self.isHidden()) {
			return '';
		}
	
		var s = '';
		s += '<tr id="' + self.getName() + '" class="legendRow">';
		s +=	'<td class="legendCell centered start">&nbsp;</td>';						//	Need the nbsp to force IE to draw borders
		s += 	'<td class="legendCell centered">' + self.getVisibleHTML( mapID ) + '</td>';
		s += 	'<td class="legendCell">' + self.getRampHTML( width ) + '</td>';
		s +=	'<td class="legendCell"><div class="legendLayerLabel">' + self.getTitle() + '</td>';
		s += 	'<td class="legendCell centered">' + self.getLabelBtnHTML( mapID ) + '</td>';
		s += 	'<td class="legendCell end">&nbsp;</td>';
		s += '</tr>';		
/*		s += '<tr>';	
		s += 	'<td class="legendCell end">&nbsp;</td>';						//	Need the nbsp to force IE to draw borders
		s += 	'<td colspan="2"></td>';		
		s += 	'<td class="legendCell" colspan="2"><div class="rangeLabelValue">' + self.queryResult[0] + '</div></td>';
		s += 	'<td class="legendCell end">&nbsp;</td>';						//	Need the nbsp to force IE to draw borders
		s += '</tr>';		
*/
/*		s += '<div class="legendLayerLabel">' + self.getVisibleHTML( mapID ) + ' ' + self.getLabelBtnHTML( mapID ) + ' ' + self.getTitle();
		s += self.getRampHTML( width );
		s += self.queryResult[0];
		s += '</div>';
*/
		return s;
	}
			
	/*
	 *	This is used to return Main Data Layers and Big City Layers
	 *
	 *	@param	string	menu		HTML menu
	 *	@param	integer	width		The width of the HTML color bar, in pixels
	 *  @access public				Call this function, don't call the other get*HTML functions directly.
	 */
	this.getHTMLwithRamp = function( menu, width, mapID ) {
		var s = '';
		s += '<tr id="' + self.getName() + '" class="legendRow">';
		s +=	'<td class="legendCell start">&nbsp;</td>';						//	Need the nbsp to force IE to draw borders
		s += 	'<td class="legendCell centered">' + self.getVisibleHTML( mapID ) + '</td>';
		s += 	'<td class="legendCell">&nbsp;</td>';							//	Need the nbsp to force IE to draw borders
		if (menu == '') {
			//	Biggest Cities
			s += '<td class="legendCell"><div class="legendLayerLabel">' + self.getTitle() + '<br>' + self.getRampHTML( width ) + '</td>';		
		} else {
//			s += '<td class="legendCell"><div class="legendLayerLabel">' + menu + '</td>';
			s += '<td class="legendCell"><div class="legendLayerLabel">' + menu + '<br>' + self.getRampHTML( width ) + '</td>';
		}
		s += 	'<td class="legendCell centered">' + self.getLabelBtnHTML( mapID ) + '</td>';
		s +=	'<td class="legendCell end">&nbsp;</td>';						//	Need the nbsp to force IE to draw borders
		s += '</tr>';		
/*		s += '<tr>';	
		s += 	'<td class="legendCell end"></td>';
		s += 	'<td></td>';		
		s += 	'<td colspan="3">' + self.getRampHTML( width ) + '</td>';
		s += 	'<td class="legendCell end"></td>';
		s += '</tr>';		
		s += '<tr>';	
		s += 	'<td class="legendCell end"></td>';
		s += 	'<td colspan="2"></td>';		
		s += 	'<td class="legendCell" colspan="2"><div class="rangeLabelValue">' + self.queryResult[0] + '</div></td>';
		s += 	'<td class="legendCell end"></td>';
*/
		s += '</tr>';		

		return s;
	}

	/*	
	 *	@param	integer	width	The width of the HTML color bar, in pixels
	 *	@access private
	 */
	this.getRampHTML			= function( width )		{			

		//	If the layer is not visible, don't show the legend ramp
		if (!self.getIsVisible()) {
			return '';
		}
				
		switch (self.getRampType()) {
			case RAMPtype_CATEGORICAL:
				//	Calculate position of the selection
				if (self.queryResults) {
					var tickMarkValue = self.queryResults.getFirstItemValue('string');
Debug('categorial self.queryResult = ' + self.queryResults);
Debug('categorial tickMarkValue = ' + tickMarkValue);
				}
				var rampArr	= self.getCategoricalHTML( width, tickMarkValue );
				//Debug('tickmark html = <code>' + rampArr[2] + '</code>'); 
				break;
			case RAMPtype_RANGE:
				//	Calculate position of the selection
				if (self.queryResults) {
					var tickMarkValue = self.queryResults.getFirstItemValue('float');
				}
				var rampArr	= self.getRangeHTML( width, tickMarkValue );
				break;
			case RAMPtype_RANGE_SYMMETRICAL:
				//	Calculate position of the selection
				if (self.queryResults) {
					var tickMarkValue = self.queryResults.getFirstItemValue('float');
				}
				var rampArr	= self.getRangeHTML( width, tickMarkValue, true );
				break;
			case RAMPtype_SINGLE:
				var rampArr	= self.getSingleHTML( width );
				break;
			case RAMPtype_LINE:
				var rampArr	= self.getLineHTML( width );
				/*	Draw a graphic w/ a line set to transparent, and everything else white, then set bg color */
				break;
			case RAMPtype_POINT:
				if (self.queryResults) {
					var tickMarkValue = self.queryResults.getFirstItemValue('float');
				}
				var rampArr	= self.getPointHTML( width, tickMarkValue );
				/*	Draw a div square corresponding to size? */
				break;
			default:
				var rampArr = new Array('' , '');
				break;
		}
		
//Debug( '  ramp type was: ' + self.getRampType() );
		var s	=	'<table border="0" cellpadding="1" cellspacing="0" style="margin-top:1em;">';
		if (rampArr.length > 2) {	//	has a tickmark defined
			s 		+=		'<tr>' + rampArr[2] + '</tr>';
		}
		s 		+=		'<tr>' + rampArr[0] + '</tr>';
		s 		+=		'<tr>' + rampArr[1] + '</tr>';
		s 		+=	'</table>';

		
		return s;
	}
	

	/*	
	 *	@param	integer	width	The width of the HTML color bar, in pixels
	 *	@access private
	 */
	this.getCategoricalHTML		= function( width, tickMarkValue )		{
		var ramp = '';
		var label = '';
		var tickMark = '';
		var halfTickMarkWidth = 5;
		var tickMarkOffset;														//	Offset of tickmark from the beginning of the range in the color bar.


		var tickMarkPadWidth = 10;
/*		//	Add padding to ramp and label to allow for tickmark overlap
		ramp += '<td width="' + tickMarkPadWidth + '"></td>';
		label += '<td width="' + tickMarkPadWidth + '"></td>';
		tickMark += '<td width="' + tickMarkPadWidth + '"></td>';
*/		
		var currentWidth = 0;
		var u = self.getClassesLength();
		var w = Math.floor(width / u);
		ramp += '<td width="' + width + '">';
		label += '<td width="' + width + '">';
		tickMark += '<td width="' + width + '">';
		for (var i=0; i<u; i++) {
			//	TickMark
			/*	Have to draw tickMark BEFORE the current width changes */
Debug( '-- comparing tickMarkValue='+tickMarkValue+' to getRangeMin='+classes[i].getRangeMin() );
			if ( (tickMarkValue!=null) && (tickMarkValue==classes[i].getRangeMin()) ) {
Debug('matched');
				tickMarkOffset = (w/2);
				if ( currentWidth > width/3 ) {
					//	Label on left
					tickMark += '<div style="float:left; width:' + Math.round(tickMarkOffset + (currentWidth-halfTickMarkWidth-42)) + 'px; height:12px;"></div>';		//	Padding
					tickMark += '<span style="float:left; width:40px; height:12px; vertical-align:bottom; padding-left:2px;" class="rangeLabelValue">'  + NumberFormat(tickMarkValue) + '</span>';
					tickMark += '<img src="../../hmap/img/RampValuePointer.gif" alt="v" style="float:left; height:12px; vertical-align:bottom;" >';		
				} else {
					//	Label on right
					tickMark += '<div style="float:left; width:' + Math.round(tickMarkOffset + (currentWidth-halfTickMarkWidth)) + 'px; height:12px;"></div>';		//	Padding
					tickMark += '<img src="../../hmap/img/RampValuePointer.gif" alt="v" style="float:left; height:12px; vertical-align:bottom;" >';		
					tickMark += '<span style="float:left; width:40px; height:12px; vertical-align:bottom; padding-left:2px;" class="rangeLabelValue">'  + NumberFormat(tickMarkValue) + '</span>';
				}
			}

			ramp += '<div title="' + classes[i].label + '" style="float:left; width: ' + w + 'px; height: 12px; background-color:' + classes[i].fillColor + ';"></div>';
			label += '<div class="rangeLabel left" style="float:left; width:'+w+'px; text-align:center;">' + classes[i].label + '</div>';
			currentWidth += w;
		}
		ramp += '</td>';		
		label += '</td>';		
		tickMark += '</td>';		
		
/*		//	Add padding to ramp and label to allow for tickmark overlap
		ramp += '<td width="' + tickMarkPadWidth + '"></td>';
		label += '<td width="' + tickMarkPadWidth + '"></td>';
		tickMark += '<td width="' + tickMarkPadWidth + '"></td>';
*/
		return new Array( ramp, label, tickMark);
	}


	/*	
	 *	@param	integer		width				The width of the HTML color bar, in pixels
	 *	@param	float		tickMarkValue		Value of the query result
	 *	@access private
	 */
	this.getRangeHTML		= function( width, tickMarkValue, isSymmetrical )		{
		var ramp = '';
		var label = '';
		var rampUnitWidth = 4;													//	Minimum width of a color ramp bar
		var numberOfRampUnits = width/rampUnitWidth;							//	Number of rampUnits available in the color ramp
		var tickMark = '';
		var halfTickMarkWidth = rampUnitWidth*2;
		var tickMarkOffset;														//	Offset of tickmark from the beginning of the range in the color bar.
		var tickMarkPadWidth = rampUnitWidth*3;
		//	First walk through and total up the ranges
		var rangeTotal = 0;
		var u = self.getClassesLength();
		for (var i=0; i<u; i++) {												
			rangeTotal += classes[i].getRange();
		}
		
		//	Set width according to percentage of range
		var w = 0;																//	w is the width of the tickmark cell we're currently working on
		var currentWidth = 0;													//	currentWidth keeps track of how much width of the whole color ramp we've used up (and how much is left)
		var numberOfRampUnits = numberOfRampUnits - 13;							//	This is the width - all the padding for the NaN and Infinity values.
		var rangeWidth = (numberOfRampUnits+5) * rampUnitWidth;
		ramp += '<td width="' + width + '">';
		label += '<td width="' + width + '">';
		tickMark += '<td width="' + width + '">';
		for (var i=0; i<u; i++) {
			if ((classes[i].getRangeMin()=='') || (classes[i].getRangeMax()=='')) {
				//	Negative or Positive Infinity
				w = tickMarkPadWidth; 											//	Default width
			} else {
				//	All others
				w = Math.max( Math.round( (classes[i].getRange()/rangeTotal) * numberOfRampUnits), 1) * rampUnitWidth;
			}
			
			//	Draw the TickMark
			/*	Have to draw tickMark BEFORE the current width changes */
			if (tickMarkValue!=null) {
				var okToDraw = false;
				//	Make sure it's valid
				if ( (classes[i].getRangeMin()=='') && (classes[i].getRangeMax()=='') ) {
					//	NaN
					okToDraw = false;
				} else if (	(tickMarkValue >= classes[i].getRangeMin()) 
						 	&& ( (tickMarkValue < classes[i].getRangeMax())
						 		 || (classes[i].getRangeMax()=='') 
						 	   ) 
						  ) {
					okToDraw = true;
				}				
				if (okToDraw) {
					if (i==u-1) {
						//	Infinity
						tickMarkOffset = 3;	//	Put it in the middle of the tickmark
					} else {
						tickMarkOffset = ( (tickMarkValue-classes[i].getRangeMin()) / classes[i].getRange()) * w;
					}
					if ( currentWidth+tickMarkOffset > width/3 ) {
						//	Label on left
						tickMark += '<div style="float:left; width:' + Math.round(tickMarkOffset + (currentWidth-halfTickMarkWidth-62)) + 'px; height:12px;"></div>';		//	Padding
						tickMark += '<span style="float:left; width:60px; height:12px; text-align:right; vertical-align:bottom; padding-right:2px;" class="rangeLabelValue">'  + NumberFormat(tickMarkValue) + '</span>';
						tickMark += '<img src="../../hmap/img/RampValuePointer.gif" alt="v" style="float:left; height:12px; vertical-align:bottom;" >';		
					} else {
						//	Label on right
						tickMark += '<div style="float:left; width:' + Math.round(tickMarkOffset + (currentWidth-halfTickMarkWidth)) + 'px; height:12px;"></div>';		//	Padding
						tickMark += '<img src="../../hmap/img/RampValuePointer.gif" alt="v" style="float:left; height:12px; vertical-align:bottom;" >';		
						tickMark += '<span style="float:left; width:40px; height:12px; vertical-align:bottom; padding-left:2px;" class="rangeLabelValue">'  + NumberFormat(tickMarkValue) + '</span>';
					}
				}
			}
			
			//	Draw the Color Ramp
			//	We assume that the first value is NaN
			if (i==0) {
				//	NaN
				w = tickMarkPadWidth;
				ramp += this.getRampBarHTML( w, classes[i].label, classes[i].fillColor);
				ramp += '<div class="rampBar" style="width: ' + halfTickMarkWidth + 'px;"></div>';	//	Filler
				label += '<div class="rampBar" style="width: ' + (w + halfTickMarkWidth) + 'px;"></div>';	//	Filler
				currentWidth += w + halfTickMarkWidth;
			} else if (i==1) {
				//	First Label
				ramp += this.getRampBarHTML( w, classes[i].label, classes[i].fillColor);
				label += this.getRampBarLabelHTML( rangeWidth/2, classes[i].label, true);
				currentWidth += w;
			} else {
				//	Regular ramp
				ramp += this.getRampBarHTML( w, classes[i].label, classes[i].fillColor);
				if (i==u-1) {
					// Last item, so add label
					label += this.getRampBarLabelHTML( rangeWidth/2, classes[i].label, true, true);
				}
				currentWidth += w;
			}			
		}
		ramp += '</td>';		
		label += '</td>';		
		tickMark += '</td>';		
		return new Array( ramp, label, tickMark);
	}
	
	this.getRampBarHTML		= function( width, label, fillColor )		{
		return '<div title="' + label + '" class="rampBar" style="width: ' + width + 'px; background-color:' + fillColor + ';"></div>';
	}	
	this.getRampBarLabelHTML		= function( width, label, isOn, rightJustify)		{
		if (rightJustify) {
			var styleRight = ' text-align: right;';
		} else {
			var styleRight;
		}
		if (isOn) {
			return '<div class="rampBarLabel on" style="width: ' + width + 'px;' + styleRight + '">' + label + '</div>';
		} else {
			return '<div class="rampBarLabel" style="width: ' + width + 'px;' + styleRight + '"></div>';
		}
	}	


	/*	
	 *	@param	integer	width	The width of the HTML color bar, in pixels
	 *	@access private
	 */
	this.getSingleHTML		= function( width )		{
		var ramp = '';
		var label = '';
		
		var u = self.getClassesLength();
		if (u>0) {																
			/* Make sure a class is defined.
			/  Singles should have only one class
		    */
			ramp 	= '<td title="' + classes[0].label + '" style="width: 12px; height: 12px; vertical-align: bottom;"><div style="width: 12px; height: 12px; background-color:' + classes[0].fillColor + ';"></div></td>';
			label	= '<td class="rangeLabel left" style="padding-left: 5px">' + classes[0].label + '</td>';
		}
		
//		return new Array( ramp +label, '' );									//	Put the ramp and label on the same line.
		return new Array( ramp, '' );											//	No label for singles
	}


	/*	
	 *	@param	integer	width	The width of the HTML color bar, in pixels
	 *	@access private
	 */
	this.getLineHTML		= function( width )		{
		var ramp = '';
		var label = '';
		
		var u = self.getClassesLength();
		if (u>0) {																
			/* Make sure a class is defined.
			/  Lines should have only one class
		    */
			ramp 	= '<td title="' + classes[0].label + '" style="width: 12px; height: 12px; vertical-align: middle;"><div style="width: 12px; height: ' + classes[0].size + 'px; font-size: ' + classes[0].size + 'px; background-color:' + classes[0].fillColor + ';"></div></td>';
			label	= '<td class="rangeLabel left" style="padding-left: 5px">' + classes[0].label + '</td>';
		}
		
//		return new Array( ramp +label, '' );									//	Put the ramp and label on the same line.
		return new Array( ramp, '' );											//	No label for singles
	}

	/*	
	 *	@param	integer	width	The width of the HTML color bar, in pixels
	 *	@access private
	 */
	this.getPointHTML		= function( width, tickMarkValue )		{
		var ramp = '';
		var label = '';
		var tickMark = '';
		var halfTickMarkWidth = 5;
		var tickMarkOffset;														//	Offset of tickmark from the beginning of the range in the color bar.
		var tickMarkPadWidth = 10;
/*		//	Add padding to ramp and label to allow for tickmark overlap
		ramp 		+= '<td width="' + tickMarkPadWidth + '"></td>';
		label 		+= '<td width="' + tickMarkPadWidth + '"></td>';
		tickMark 	+= '<td width="' + tickMarkPadWidth + '"></td>';
*/		
		var currentWidth = 0;
		var u = self.getClassesLength();
		var w = Math.floor(width / u);
		ramp 		+= '<td width="' + width + '" style="vertical-align:middle;">';
		label 		+= '<td width="' + width + '">';
		tickMark 	+= '<td width="' + width + '">';
		for (var i=0; i<u; i++) {
			//	TickMark
			/*	Have to draw tickMark BEFORE the current width changes */
			if ( (tickMarkValue!=null) && (self.tickIsWithinRange( tickMarkValue, classes[i].getRangeMin(), classes[i].getRangeMax() )) ) {
				tickMarkOffset = (w/2);
				if ( currentWidth > width/3 ) {
					//	Label on left
					tickMark += '<div style="float:left; width:' + Math.round(tickMarkOffset + (currentWidth-halfTickMarkWidth-42)) + 'px; height:12px;"></div>';		//	Padding
					tickMark += '<span style="float:left; width:40px; height:12px; vertical-align:bottom; padding-left:2px;" class="rangeLabelValue">'  + NumberFormat(tickMarkValue) + '</span>';
					tickMark += '<img src="../../hmap/img/RampValuePointer.gif" alt="v" style="float:left; height:12px; vertical-align:bottom;" >';		
				} else {
					//	Label on right
					tickMark += '<div style="float:left; width:' + Math.round(tickMarkOffset + (currentWidth-halfTickMarkWidth)) + 'px; height:12px;"></div>';		//	Padding
					tickMark += '<img src="../../hmap/img/RampValuePointer.gif" alt="v" style="float:left; height:12px; vertical-align:bottom;" >';		
					tickMark += '<span style="float:left; width:40px; height:12px; vertical-align:bottom; padding-left:2px;" class="rangeLabelValue">'  + NumberFormat(tickMarkValue) + '</span>';
				}
			}
			ramp += '<div title="' + classes[i].label + '" class="rampPoint" style="width: ' + w + 'px; "><div style="width: ' + classes[i].size + 'px; height: ' + classes[i].size + 'px; font-size: ' + classes[i].size + 'px; background-color:' + classes[i].fillColor + '; vertical-align:middle;"></div></div>';

			if (i==0) {
				//	First Label
				label += '<div class="rangeLabel left" style="float:left; width: ' + Math.floor(width/2) + 'px;">' + classes[i].label + '</div>';
			} else if (i==u-1) {
				//	Last Label
				//	(-5px so that the lable doesn't wrap to the next line).
				label += '<div class="rangeLabel right" style="float:left; width: ' + (Math.floor(width/2)-5) + 'px;">' + classes[i].label + '</div>';
			}

			currentWidth += w;
		}
		ramp 		+= '</td>';		
		label 		+= '</td>';		
		tickMark 	+= '</td>';		
		
		return new Array( ramp, label, tickMark);
		}


	//	@param	number		rangeMin,rangeMax		Assumes the ranges are numbers.  This won't work on categorical ramps where the ranges are strings
	this.tickIsWithinRange = function( tickMarkValue, rangeMin, rangeMax) {
		if (rangeMin=='') {
			//	First value (all values less than x)
			if (tickMarkValue < rangeMax) return true;
		} else if (rangeMax=='') {
			//	Last value (all values greater than x)
			if (tickMarkValue >= rangeMin) return true;
		} else {
			//	Middle value
			if ( (tickMarkValue >= rangeMin) && (tickMarkValue < rangeMax) ) return true;
		}
		return false
	}


	this.getVisibleHTML = function( mapID ) {
		var s = '';
		
		var checked = self.getIsVisible() ? ' checked' : '';
		//Debug('checked = ' + self.getIsVisible() );
		var elID =  GetMapIDPre( mapID ) + 'lyr' + self.getName();
		s += '<input type="checkbox" name="' + elID + '" id="' + elID + '" ';		
		s += 'value="' + self.getLayerState() + '"' + checked + ' onClick="legendMgr.delayedDoSubmit=\'' + mapID + '\';">';
		
		return s;
	}
	
	this.getLabelBtnHTML = function( mapID ) {
		if (self.getLabelsAreAvailable() ) {
			var elID =  GetMapIDPre( mapID ) + 'lyr' + self.getName() + '_label';	
			self.setLabelBtnID( elID );
			//Debug('getLabelBtnHTML: getLayerState() = ' + self.getLayerState() );
			if (self.getLayerState().split(',')[1] == LABELbtn_ON) {
				//Debug('getLabelBtnHTML: Turning jsBtn On = ' + self.getLayerState() );
				return ' <span id="' + elID + '" class="jsBtn On">L</span>';
			} else {
				//Debug('getLabelBtnHTML:  Turning jsBtn Off = ' + self.getLayerState() );
				return ' <span id="' + elID + '" class="jsBtn Off">L</span>';
			}
		} else {
			self.setLabelBtnID( '' );											//	Not strictly necessary, but just in case...
			return '&nbsp;';													//	Return something so that IE will draw a border
		}
	}
}


/***************************************************************************
 *	LAYER CLASS
 *
 *	This is the js equivalent of the .map file's 'classes' within each layer
 *
 */
function lclass() {
	var self				= this;
	
	var label				= 'untitled';
	var rangeMin			= 0;
	var rangeMax			= 1;
	var fillColor			= '#fff';
	var outlineColor		= '';
	var size				= 0;
	
	this.setRangeMin		= function( newMin )	{	rangeMin = newMin; }
	this.getRangeMin		= function()			{	return rangeMin; }

	this.setRangeMax		= function( newMax)		{	rangeMax = newMax; }
	this.getRangeMax		= function()			{	return rangeMax; }
	
	this.getRange 			= function() {	
		if ((rangeMin=='') || (rangeMax=='')) {
			//	If the range goes to negative infinity or positive infinity, then return a minimum range value of 1
			return 1; 
		} else {
			return Math.abs(rangeMax - rangeMin);
		}
	}
	
}



/***************************************************************************
 *	GENERAL FUNCTIONS
 *
 */

/*
 *	Returns the mapID prefix to be used with GET posting
 *	ex: GetMapIDPre( 'm1' ) => 'm1_'
 */
function GetMapIDPre( mapID ) {
	return mapID + '_';
}
/*
 *	Converts '255 255 255' to '#FFFFFF'
 */
function RGBHexfromDec( decString ) {
	try {
		var rgb = decString.split(' ');
		var r = parseInt( rgb[0] );
		var g = parseInt( rgb[1] );
		var b = parseInt( rgb[2] );
	
		return '#' + HexFromDec(r) + HexFromDec(g) + HexFromDec(b); 
	}
	catch (err) {
		//alert (err + ': TypeOf(decString) = ' + typeof( decString ) );
		// typeof( decString ) = undefined
		return '#000000';
	}
}

/*
 *	Converts a decimal value to a two digit hex value
 *	This will add a leading 0, e.g. 16 => 0F
 *
 *	@parm	integer		n
 */
function HexFromDec( n ) { 
	return (n<16) ? '0' + n.toString(16) : n.toString(16);
}




