function SpatialFilter()
{
	var self = this;
	
	// properties
	this.Type; // Rectange, Polygon, Line
	this.Points; // Points: -23.0,-12.0,12.0,23.0
	this.ObjectIdArray = new Array(); // ObjectIds to 'highlight' in this spatial filter
	this.XmlReqFilter; // XMLHttpRequest object used to request the spatial filter queries
	this.Buffer = null;//buffer object
	
	// methods
	this.getSpatialFilterResults = getSpatialFilterResults;
	this.processSpatialFilterResults = processSpatialFilterResults;
	this.getAdjoiningParcels = getAdjoiningParcels;
	this.processAdjoiningParcels = processAdjoiningParcels;
	
	function getSpatialFilterResults()
	{
		showLoading();		
		url = server + "FeatureService.asmx/GetFeatureShortReport?layers=" + getVisibleLayerString();
		
		if(window.ActiveXObject)
		{
			self.XmlReqFilter = new ActiveXObject("Microsoft.XMLHTTP");
			
			if(self.XmlReqFilter)
			{
				self.XmlReqFilter.onreadystatechange = processSpatialFilterResults;
				self.XmlReqFilter.open("GET", url, true); //async call
				self.XmlReqFilter.send();
			}
		}
	}
	
	function processSpatialFilterResults()
	{
		if( self.XmlReqFilter.readyState == 4 )
		{
			if( self.XmlReqFilter.status == 200 )
			{
				// parse response and place data into html table
				var resultTable = document.getElementById("divResultsList");
				resultTable.innerHTML = "";
				
				var response = self.XmlReqFilter.responseXML.documentElement;
				var features = response.getElementsByTagName('FEATURES');
				var featureID = "";
				var html = "";
				
				// 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 )
					spnResultsInfo.innerHTML = "No Features Found";
				else if( count == 1 )
					spnResultsInfo.innerHTML = "1 Feature Found";
				else
					spnResultsInfo.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 fieldCount = 0;
						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;
						
						// for parcels, need to add 1 to this value
						var totalFieldsInFeature = features[i].childNodes[x].attributes.getNamedItem('fields').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
						featureID = "feature" + x;
						className = "fieldPrimary";
						
						html = "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" style=\"margin-top:5px;\">";
						html += "<tr id=\"" + featureID + "\" class=\"" + className + "\">";
						html += "<td><img class=\"collapseImage\" src=\"images/plus.gif\" onmousedown=\"collapseFeatureData('" + featureID + "'," + totalFieldsInFeature + ");\" />";
						
						if( pdisplay != '' )
							html += layer + ": " + pdisplay + "</td>";
						else
							html += 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 += "<img src=\"images/feature_report.gif\" alt=\"Feature Details\" onmousedown=\"var featD = new Feature(); featD.getFeatureDetails('" + layer + "','" + uniqueValue + "');\"/>";
						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 = (fieldCount % 2 == 1)?"Odd":"Even";
								featureID = "feature" + x + "field" + fieldCount;
								html += "<tr id=\"" + featureID + "\" style=\"display:none;\">";
								
								// add field name (alias)
								if( fieldNameHyperlink != "" )
								{
									html += "<td class=\"field" + 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>";
								
								fieldCount++;
							}
						}
						
						html += "</table>";
						resultTable.innerHTML += html;
					}
				}
				
				activateTab("tabResults");
				trResultsInfo.style.display='block';
				trDetailsInfo.style.display='none';
				divResultsList.style.display='block';
				divDetailsList.style.display='none';
				subTabResultsList.className='subTabActive';
				subTabDetailsList.className='subTabNormal';
				
				if( hasMore == 'True' )
					alert("This search returned more than the number of features allowed.  Please refine your search for more detailed results.");
			}
		}
	}
	
	function getAdjoiningParcels(objectId)
	{
		showLoading();
		
		//clear current spatial filters, if necessary
		if( selectMode == 0 )
			clearSelection(false);
		
		// send request to get new map image
		map.getAdjoiningParcelsMap(objectId);
		
		// send request to get adjoining feature data
		url = server + "FeatureService.asmx/GetAdjoiningParcels?objectId=" + objectId;
		
		if(window.ActiveXObject)
		{
			self.XmlReqFilter = new ActiveXObject("Microsoft.XMLHTTP");
			
			if(self.XmlReqFilter)
			{
				self.XmlReqFilter.onreadystatechange = processAdjoiningParcels;
				self.XmlReqFilter.open("GET", url, true); //async call
				self.XmlReqFilter.send();
			}
		}
	}
	
	function processAdjoiningParcels()
	{
		if(self.XmlReqFilter.readyState == 4)
		{
			if(self.XmlReqFilter.status == 200)
			{
				// parse response and place data into html table
				var resultTable = document.getElementById("divResultsList");
				resultTable.innerHTML = "";
				
				var spFilterObj = new SpatialFilter();
				
				// find the 'Parcels' layer in the LayerArray, and pull out the SpatialFilter object
				for( var j = 0; j < layerArray.length; j++ )
				{
					if( layerArray[j].Name == 'Parcels' )
					{
						spFilterObj = layerArray[j].SpatialFilter;
						
						if( spFilterObj.ObjectIdArray == null || spFilterObj.ObjectIdArray.length > 0 )
							spFilterObj.ObjectIdArray = new Array();
					}
				}
				
				var response = self.XmlReqFilter.responseXML.documentElement;
				var features = response.getElementsByTagName('FEATURES');
				var featureID = "";
				var html = "";
				
				// 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 )
					spnResultsInfo.innerHTML = "No Features Found";
				else if( count == 1 )
					spnResultsInfo.innerHTML = "1 Feature Found";
				else
					spnResultsInfo.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 fieldCount = 0;
						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;
						
						// for parcels, need to add 1 to this value
						var totalFieldsInFeature = features[i].childNodes[x].attributes.getNamedItem('fields').value;
						
						// to keep the adjoined parcels selected when the map pans/zooms, we need to add 
						// each objectid for the parcel set to the SpatialFilter 'ObjectIds' array
						spFilterObj.ObjectIdArray.push(uniqueValue);
					
						// 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
						featureID = "feature" + x;
						className = "fieldPrimary";
						
						html = "<table width=\"100%\" cellspacing=\"0\" cellpadding=\"2\" style=\"margin-top:5px;\">";
						html += "<tr id=\"" + featureID + "\" class=\"" + className + "\">";
						html += "<td><img class=\"collapseImage\" src=\"images/plus.gif\" onmousedown=\"collapseFeatureData('" + featureID + "'," + totalFieldsInFeature + ");\" />";
						
						if( pdisplay != '' )
							html += layer + ": " + pdisplay + "</td>";
						else
							html += layer + ": " + uniqueValue + "</td>";
							
						html += "<td align=\"right\">";
						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 += "<img src=\"images/feature_report.gif\" alt=\"Feature Details\" onmousedown=\"var featD = new Feature(); featD.getFeatureDetails('" + layer + "','" + uniqueValue + "');\"/>";
						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 = (fieldCount % 2 == 1)?"Odd":"Even";
								featureID = "feature" + x + "field" + fieldCount;
								html += "<tr id=\"" + featureID + "\" style=\"display:none;\">";
								
								// add field name (alias)
								if( fieldNameHyperlink != "" )
								{
									html += "<td class=\"field" + 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>";
								
								fieldCount++;
							}
						}
						
						html += "</table>";
						resultTable.innerHTML += html;
					}
				}
				
				activateTab("tabResults");
				trResultsInfo.style.display='block';
				trDetailsInfo.style.display='none';
				divResultsList.style.display='block';
				divDetailsList.style.display='none';
				subTabResultsList.className='subTabActive';
				subTabDetailsList.className='subTabNormal';
				
				if( hasMore == 'True' )
					alert("This search returned more than the number of features allowed.  Please refine your search for more detailed results.");
			}
		}
	}
}