function Feature()
{
	var self = this;
	
	this.XmlReqDetails; // XMLHttpRequest object
	this.XmlReqIdentify; // XMLHttpRequest object
	
	// methods
	this.processIdentify = processIdentify;
	this.processIdentifyData = processIdentifyData;
	this.getFeatureDetails = getFeatureDetails;
	this.processFeatureDetailData = processFeatureDetailData;
	this.checkForZoomToFeature = checkForZoomToFeature;
	
	function processIdentify(x, y, dist)
	{
		var layerId;
		var spatialFilter;
		
		for( var i = 0; i < layerArray.length; i++ )
		{
			if( layerArray[i].LayerPosition == map.ActiveLayer )
			{
				layerId = layerArray[i].Name;
				break;
			}	
		}
		
		spatialFilter = new SpatialFilter();
		spatialFilter.Points = "";
		spatialFilter.Points += convertMapPoints(x, y);
		
		buffer = new Buffer();
		buffer.BufferUnits = map.Units;
		buffer.BufferDistance = dist;
		spatialFilter.Buffer = buffer;
		
		var str = spatialFilter.Points.split(",");
		var x = str[0];
		var y = str[1];
		
		map.getIdentifyMap(x, y, dist, layerId);
		
		url = server + "FeatureService.asmx/GetFeatureIdentify?x=" + x + "&y=" + y + "&distance=" + buffer.BufferDistance + "&layerId=" + layerId;
			
		if(window.ActiveXObject)
		{
			self.XmlReqIdentify = new ActiveXObject("Microsoft.XMLHTTP");
			
			if(self.XmlReqIdentify)
			{
				self.XmlReqIdentify.onreadystatechange = processIdentifyData;
				self.XmlReqIdentify.open("GET", url, true); //async call
				self.XmlReqIdentify.send();
			}
		}
	}

	function processIdentifyData()
	{
		if(self.XmlReqIdentify.readyState == 4)
		{
			if(self.XmlReqIdentify.status == 200)
			{
				// parse response and place data into html table
				var infoTable = document.getElementById("divInformationList");
				infoTable.innerHTML = "";
				
				var response = self.XmlReqIdentify.responseXML.documentElement;
				var features = response.getElementsByTagName('FEATURES');
				var className = "";
				var html = "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" style=\"margin-top:5px;\">";
				
				// get request specific top level values (layer, layerHyperlink, response feature count, hasMore features)
				var layer = features[0].attributes.getNamedItem('layer').value;
				var layerHyperlink = features[0].attributes.getNamedItem('hyperlink').value;
				var count = features[0].attributes.getNamedItem('count').value;
				var hasMore = features[0].attributes.getNamedItem('hasmore').value;
				
				
				if( count == 0 )
				{
					spnInformationInfo.innerHTML = "No Features Found";
				}
				else
				{

					if( count == 1 )
						spnInformationInfo.innerHTML = "1 Feature Found";
					else
						spnInformationInfo.innerHTML = count + " Features Found";
				
					// looks at FEATURES elements
					for( var i = 0; i < features.length; i++ )
					{
						// looks at FEATURE elements
						for( var x = 0; x < features[i].childNodes.length; x++ )
						{
							var gpin = "";
							
							// get feature specific top level values (uniqueName, uniqueValue, primaryDisplay)
							var uniqueName = features[i].childNodes[x].attributes.getNamedItem('uniquename').value;
							var uniqueValue = features[i].childNodes[x].attributes.getNamedItem('uniquevalue').value;
							var pdisplay = features[i].childNodes[x].attributes.getNamedItem('pdisplay').value;
							
							// get envelope information
							var minx = features[i].childNodes[x].childNodes[0].attributes.getNamedItem('minx').value;
							var miny = features[i].childNodes[x].childNodes[0].attributes.getNamedItem('miny').value;
							var maxx = features[i].childNodes[x].childNodes[0].attributes.getNamedItem('maxx').value;
							var maxy = features[i].childNodes[x].childNodes[0].attributes.getNamedItem('maxy').value;
							
							// setup primary display field in HTML
							html += "<tr class=\"fieldPrimary\">";
							
							if( pdisplay != '' )
								html += "<td>" + layer + ": " + pdisplay + "</td>";
							else
								html += "<td>" + layer + ": " + uniqueValue + "</td>";
							
							html += "<td align=\"right\">";
							
							// check for Parcel layer and add getAdjoiningParcels link to OBJECTID
							if( layer == 'Parcels' )
								html += "<img src=\"images/adjoiners.gif\" onmousedown=\"var sFilter = new SpatialFilter(); sFilter.getAdjoiningParcels('" + uniqueValue + "');\" alt=\"Get Adjoining Parcels\" />";
								
							html += "<a href=\"feature_xml.aspx?objectId=" + uniqueValue + "&layer=" + layer + "&reportType=SHORTREPORT\" target=\"_new\" border=\"0\"><img src=\"images/excel.gif\" alt=\"Export to Excel\" border=\"0\" /></a>";								
							html += "<img src=\"images/feature_zoom_to.gif\" onmousedown=\"map.zoomToFeature('" + minx + "', '" + miny + "', '" + maxx + "', '" + maxy + "');\" alt=\"Zoom to Feature\" />";													
							html += "</td></tr>";
							
							// looks at FIELDGROUP elements
							// ** start at y = 1 because 0 index would be the ENVELOPE tag already discovered above
							for( var y = 1; y < features[i].childNodes[x].childNodes.length; y++ )
							{
								var fieldGroupName = features[i].childNodes[x].childNodes[y].attributes.getNamedItem('name').value;
								var fieldGroupHyperlink = features[i].childNodes[x].childNodes[y].attributes.getNamedItem('hyperlink').value;
								
								// looks at FIELD elements
								for( var z = 0; z < features[i].childNodes[x].childNodes[y].childNodes.length; z++ )
								{
									var fieldName = features[i].childNodes[x].childNodes[y].childNodes[z].attributes.getNamedItem('name').value;
									var fieldAlias = features[i].childNodes[x].childNodes[y].childNodes[z].attributes.getNamedItem('alias').value;
									var fieldNameHyperlink = features[i].childNodes[x].childNodes[y].childNodes[z].attributes.getNamedItem('namehyperlink').value;
									var fieldValue = features[i].childNodes[x].childNodes[y].childNodes[z].attributes.getNamedItem('value').value;
									var fieldValueHyperlink = features[i].childNodes[x].childNodes[y].childNodes[z].attributes.getNamedItem('valuehyperlink').value;
									
									// add each field to the HTML
									className = (z % 2 == 1)?"Odd":"Even";
									html += "<tr>";
									
									// add field name (alias)
									if( fieldNameHyperlink != "" )
									{
										html += "<td class=\"fieldValue" + className + "\">";
										html += "<a class=\"fieldLink\" href=\"" + fieldNameHyperlink + "\" target=\"_new\">";
										html += fieldAlias + "</a></td>";
									}
									else
										html += "<td class=\"field" + className + "\">" + fieldAlias + "</td>";
									
									// add field value
									if( fieldValue != null )
									{
										// check for hyperlink!
										if( fieldValueHyperlink != "" )
										{
											html += "<td class=\"fieldValue" + className + "\">";
											html += "<a class=\"fieldLink\" href=\"" + fieldValueHyperlink + "\" target=\"_new\">";
											html += fieldValue + "</a></td>";
										}
										else
											html += "<td class=\"fieldValue" + className + "\">" + fieldValue + "</a></td>";
									}
									else
										html += "<td></td></tr>";
								}
							}
						}
					}
					
					html += "</table>";
					infoTable.innerHTML = html;
				}
				
				activateTab("tabInformation");
				trInformationInfo.style.display = 'block';
				divInformationMessage.style.display = 'none';
				divInformationList.style.display = 'block';
				subTabInformationMessage.className = 'subTabNormal';
				subTabInformationList.className = 'subTabActive';
				
				if( hasMore == 'True' )
					alert("This search returned more than the number of features allowed.  Please refine your search for more detailed results.");
			}
		}
	}

	function getFeatureDetails(layerId,objectIdValue)
	{
		url = server + "FeatureService.asmx/GetFeatureDetails?layerId=" + layerId + "&objectIdValue=" + objectIdValue;
		
		if(window.ActiveXObject)
		{
			self.XmlReqDetails = new ActiveXObject("Microsoft.XMLHTTP");
			
			if(self.XmlReqDetails)
			{
				self.XmlReqDetails.onreadystatechange = processFeatureDetailData;
				self.XmlReqDetails.open("GET", url, true); //async call
				self.XmlReqDetails.send();
			}
		}
	}

	function processFeatureDetailData()
	{
		if(self.XmlReqDetails.readyState == 4)
		{
			if(self.XmlReqDetails.status == 200)
			{
				// parse response and place data into html table
				var detailsTable = document.getElementById("divDetailsList");
				detailsTable.innerHTML = "";
				
				var response = self.XmlReqDetails.responseXML.documentElement;
				var features = response.getElementsByTagName('FEATURES');
				var className = "";
				var html = "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" style=\"margin-top:5px;\">";
				
				// get request specific top level values (layer, layerHyperlink, response feature count, hasMore features)
				var layer = features[0].attributes.getNamedItem('layer').value;
				var layerHyperlink = features[0].attributes.getNamedItem('hyperlink').value;
				var count = features[0].attributes.getNamedItem('count').value;
				var hasMore = features[0].attributes.getNamedItem('hasmore').value;
				
				spnDetailsInfo.innerHTML = "Feature Details";
				
				// looks at FEATURES elements
				for( var i = 0; i < features.length; i++ )
				{
					// looks at FEATURE elements
					for( var x = 0; x < features[i].childNodes.length; x++ )
					{
						var gpin = "";
						
						// get feature specific top level values (uniqueName, uniqueValue, primaryDisplay)
						var uniqueName = features[i].childNodes[x].attributes.getNamedItem('uniquename').value;
						var uniqueValue = features[i].childNodes[x].attributes.getNamedItem('uniquevalue').value;
						var pdisplay = features[i].childNodes[x].attributes.getNamedItem('pdisplay').value;
						
						// get envelope information
						var minx = features[i].childNodes[x].childNodes[0].attributes.getNamedItem('minx').value;
						var miny = features[i].childNodes[x].childNodes[0].attributes.getNamedItem('miny').value;
						var maxx = features[i].childNodes[x].childNodes[0].attributes.getNamedItem('maxx').value;
						var maxy = features[i].childNodes[x].childNodes[0].attributes.getNamedItem('maxy').value;
						
						// setup primary display field in HTML
						html += "<tr class=\"fieldPrimary\">";
						
						if( pdisplay != '' )
							html += "<td>" + layer + ": " + pdisplay + "</td>";
						else
							html += "<td>" + layer + ": " + uniqueValue + "</td>";
							
						html += "<td align=\"right\">";
							
						// check for Parcel layer and add getAdjoiningParcels link to OBJECTID
						if( layer == 'Parcels' )
							html += "<img src=\"images/adjoiners.gif\" onmousedown=\"var sFilter = new SpatialFilter(); sFilter.getAdjoiningParcels('" + uniqueValue + "');\" alt=\"Get Adjoining Parcels\" />";
							
						html += "<a href=\"feature_xml.aspx?objectId=" + uniqueValue + "&layer=" + layer + "&reportType=SHORTREPORT\" target=\"_new\" border=\"0\"><img src=\"images/excel.gif\" alt=\"Export to Excel\" border=\"0\" /></a>";								
						html += "<img src=\"images/feature_zoom_to.gif\" onmousedown=\"map.zoomToFeature('" + minx + "', '" + miny + "', '" + maxx + "', '" + maxy + "');\" alt=\"Zoom to Feature\" />";													
						html += "</td></tr>";
						
						// looks at FIELDGROUP elements
						// ** start at y = 1 because 0 index would be the ENVELOPE tag already discovered above
						for( var y = 1; y < features[i].childNodes[x].childNodes.length; y++ )
						{
							var fieldGroupName = features[i].childNodes[x].childNodes[y].attributes.getNamedItem('name').value;
							var fieldGroupHyperlink = features[i].childNodes[x].childNodes[y].attributes.getNamedItem('hyperlink').value;
							
							if( fieldGroupHyperlink != null && fieldGroupHyperlink != '' )
								html += "<tr><td colspan=\"2\" class=\"fieldGroupHeading\"><a href=\"" + fieldGroupHyperlink + "\" target=\"_new\">" + fieldGroupName + "</a></td></tr>";
							else
								html += "<tr><td colspan=\"2\" class=\"fieldGroupHeading\">" + fieldGroupName + "</td></tr>";
							
							// looks at FIELD elements
							for( var z = 0; z < features[i].childNodes[x].childNodes[y].childNodes.length; z++ )
							{
								var fieldName = features[i].childNodes[x].childNodes[y].childNodes[z].attributes.getNamedItem('name').value;
								var fieldAlias = features[i].childNodes[x].childNodes[y].childNodes[z].attributes.getNamedItem('alias').value;
								var fieldNameHyperlink = features[i].childNodes[x].childNodes[y].childNodes[z].attributes.getNamedItem('namehyperlink').value;
								var fieldValue = features[i].childNodes[x].childNodes[y].childNodes[z].attributes.getNamedItem('value').value;
								var fieldValueHyperlink = features[i].childNodes[x].childNodes[y].childNodes[z].attributes.getNamedItem('valuehyperlink').value;
								
								// add each field to the HTML
								className = (z % 2 == 1)?"Odd":"Even";
								html += "<tr>";
								
								// add field name (alias)
								if( fieldNameHyperlink != "" )
								{
									html += "<td class=\"fieldValue" + className + "\">";
									html += "<a class=\"fieldLink\" href=\"" + fieldNameHyperlink + "\" target=\"_new\">";
									html += fieldAlias + "</a></td>";
								}
								else
									html += "<td class=\"field" + className + "\">" + fieldAlias + "</td>";
								
								// add field value
								if( fieldValue != null )
								{
									// check for hyperlink!
									if( fieldValueHyperlink != "" )
									{
										html += "<td class=\"fieldValue" + className + "\">";
										html += "<a class=\"fieldLink\" href=\"" + fieldValueHyperlink + "\" target=\"_new\">";
										html += fieldValue + "</a></td>";
									}
									else
										html += "<td class=\"fieldValue" + className + "\">" + fieldValue + "</a></td>";
								}
								else
									html += "<td></td></tr>";
							}
						}
					}
				}
				
				html += "</table>";
				detailsTable.innerHTML = html;
			}
								
			activateTab("tabResults");
			trResultsInfo.style.display='none';
			trDetailsInfo.style.display='block';
			divResultsList.style.display='none';
			divDetailsList.style.display='block';
			subTabResultsList.className='subTabNormal';
			subTabDetailsList.className='subTabActive';
				
			if( hasMore == 'True' )
				alert("This search returned more than the number of features allowed.  Please refine your search for more detailed results.");
		}
	}
	
	function checkForZoomToFeature()
	{
		// need to check for zoomToParcel url param
		// if it exists, pull the gpin value and zoom to that parcel
		// if it doesn't, continue with loading the first map
		
		// ie: default.aspx?layer=Parcels&field=GPIN&value=2567-30-6782
		
		var layer = getURLParameters("layer");
		var field = getURLParameters("field");
		var value = getURLParameters("value");
		
		if( layer != null )
		{
			// need to filter the vars for spaces from the url (%20)
			layer = layer.replace("%20", " ");
			field = field.replace("%20", " ");
			value = value.replace("%20", " ");
		
			showLoading();
			url = server + "FeatureService.asmx/GetZoomToFeatureWithGeom?layerId=" + layer + "&fieldName=" + field + "&fieldValue=" + value;
			
			if( window.ActiveXObject )
			{
				var XmlReqZoomToFeature = new ActiveXObject("Microsoft.XMLHTTP");
				
				if( XmlReqZoomToFeature )
				{
					XmlReqZoomToFeature.open("GET", url, false); //sync call
					XmlReqZoomToFeature.send();
					var res = XmlReqZoomToFeature.responseXML.documentElement;
					
					var envelopeItems = res.getElementsByTagName('ENVELOPE');
					
					if( envelopeItems.length > 0 )
					{
						map.setMinX(envelopeItems[0].attributes.getNamedItem('minx').value);
						map.setMinY(envelopeItems[0].attributes.getNamedItem('miny').value);
						map.setMaxX(envelopeItems[0].attributes.getNamedItem('maxx').value);
						map.setMaxY(envelopeItems[0].attributes.getNamedItem('maxy').value);
					}
					
					// with points, we can just call "function spatialSelect(command, points)" 
					// to make the map highlight the feature and get the new map
					var finalPoints = new Array();
					
					var points = res.getElementsByTagName('POINTS');
					var pointsStr = points[0].attributes.getNamedItem('coords').value;
					
					var pointsArray = pointsStr.split(';');
					var cnt = 0;
					
					for( var i = 0; i < pointsArray.length; i++ )
					{
						var temp = pointsArray[i].split(' ');
						finalPoints[cnt++] = temp[0];
						finalPoints[cnt++] = temp[1];
					}
					
					// set Parcels as active layer
					for( var y = 0; y < layerArray.length; y++ )
					{
						if( layerArray[y].Name == layer )
						{
							setActiveLayerNoClick(layerArray[y].LayerPosition);
							break;
						}	
					}
					
					spatialSelect("SELECTPOLY", finalPoints, true);
				}
			}
		}
	}
}