function ListingsByMap(mapContainerId, dataUrl, formSuffix, summaryUrl, searchPanelId, filterPanelId) {
	
	var MAX_ZOOM_LEVEL = 19; //current max zoom level for gmaps(sat. view, map view is 17)
	var GRID_PAGE_SIZE = 50;
	var map;
	var manager;
	var initialized = false;
	var baseSummaryUrl;
	var gridUrl;
	var filterValues;
	var loadedListings = {};
	var loadedClusters = new Array();
	var zoomLevelsLoaded = new Array();
	var startZoom = 0;
	var purgeListingData = false;
	var startLat;
	var startLng;
	var containingBounds;
	var editMode = false;
	var userLatId;
	var userLngId;
	var userZoomId;
	var customStart = false;
	var gridPage = 1;
	var lastScroll = 0;
	var fetchingGridRows = false;
	var fetchingMapData = false
	var allRowsLoaded = false;
	var purgeOldRows = false;	
	var currentSort = {};
	var reverseSort = false;
	var moreListings = false;
	var imageUrls = {};
	
	this.map = function() {
		return map;
	}
	
	this.initIcons = function(icons) {		
		this.listingIcon = new GIcon();
		this.listingIcon.image = icons.listing;
		this.listingIcon.iconSize=new GSize(28,28);
		this.listingIcon.iconAnchor=new GPoint(12,22);			
		this.cluster5Icon = new GIcon();
		this.cluster5Icon.image = icons.cluster5;
		this.cluster5Icon.iconSize=new GSize(38,38);
		this.cluster5Icon.iconAnchor=new GPoint(16,30);			
		this.cluster10Icon = new GIcon();
		this.cluster10Icon.image = icons.cluster10;
		this.cluster10Icon.iconSize=new GSize(38,38);
		this.cluster10Icon.iconAnchor=new GPoint(16,30);	
		this.cluster25Icon = new GIcon();
		this.cluster25Icon.image = icons.cluster25;
		this.cluster25Icon.iconSize=new GSize(38,38);
		this.cluster25Icon.iconAnchor=new GPoint(16,30);		
		this.cluster50Icon = new GIcon();
		this.cluster50Icon.image = icons.cluster50;
		this.cluster50Icon.iconSize=new GSize(38,38);
		this.cluster50Icon.iconAnchor=new GPoint(16,30);
	}
	
	this.initStartCenterAndZoom = function(lat,lng,zoom) {
		startLat = lat;
		startLng = lng;
		startZoom = zoom;
	}
	
	this.startPointSpecified = function() {
		return (startLat && startLng && startZoom);
	}
	
	this.init = function() {
		baseSummaryUrl = summaryUrl;
		map = new GMap2($(mapContainerId));
		map.addControl(new GLargeMapControl());
		map.addControl(new GMapTypeControl());
		map.enableDoubleClickZoom();
		map.enableScrollWheelZoom();
		map.enableContinuousZoom();
			
		if(this.startPointSpecified()) {
			map.setCenter(new GLatLng(startLat,startLng),startZoom);
		}
		if($(mapContainerId).addEventListener){ 
			$(mapContainerId).addEventListener('DOMMouseScroll', function(e) {e.preventDefault();}, false);
		}
		else if($(mapContainerId).attachEvent) {
			$(mapContainerId).attachEvent('onmousewheel', function(){event.returnValue=false;});
		} 	
		this.initZoom();
		this.initMapCover();		
		this.clearListingsLoaded();		
		var searchButton = $('searchButton'+formSuffix);
		if($(filterPanelId)){				
			var priceHigh = document.forms[0]['priceHigh' + formSuffix];
			if(priceHigh.selectedIndex == 0){
				priceHigh.selectedIndex = priceHigh.options.length - 1;
			}			
			if($(filterPanelId).style.display == 'block') {		
				searchButton.addEvent('click',this.filterListings.bind(this).bindWithEvent(searchButton));
				this.initInfoPanel();			
			} else if($('summaryDetails' + formSuffix)) {
				$('summaryDetails' + formSuffix).style.height = "475px";
				$$('div.SummaryToggle')[0].className += " First";
			}
		}		
		var moreLink = $('moreListings' + formSuffix);
		if(moreLink) {
			moreLink.addEvent('click',this.moreListings.bind(this).bindWithEvent(moreLink));
		}
		if(editMode){			
			this.handleMapData(window["_mapData" + formSuffix]);
		} else {
			this.setSearchFilterValues();
			if(this.loadState()){
				this.fetchMapData();
			} else {				
				this.handleMapData(window["_mapData" + formSuffix]);
			}
		}
		GEvent.addListener(map,'zoomend',this.onZoomEnd.bind(this));
		GEvent.addListener(map,'moveend',this.onMoveEnd.bind(this));			
	}
	
	this.editModePreInit = function(latId, lngId, zoomId, autoId, customId){
		editMode = true;
		userLatId = latId;
		userLngId = lngId;
		userZoomId = zoomId;
		$(autoId).addEvent('click', this.toggleCustomStart.bindWithEvent(this,false));
		$(customId).addEvent('click', this.toggleCustomStart.bindWithEvent(this,true));
		var cb = $$('td.ShowSummary input')[0];
		var toggleOptions = function(disable){
			var els = $$('#configureMap .DisableNoGrid');
			for(var ii=0;ii<els.length;ii++){
				els[ii].disabled = disable;
			}
		}
		toggleOptions(!cb.checked);
		cb.addEvent('click', function(event){
			event = new Event(event);
			toggleOptions(!event.target.checked);
		}); 		
		customStart = !($(autoId).checked);
	}
	
	this.editModePostInit = function() {		
		if(!map.isLoaded()) {
			GEvent.addListener(map,'load',function(){
				GEvent.addListener(map,'moveend',function(){
					$(userZoomId).value = this.getZoom();
					$(userLatId).value = this.getCenter().lat();
					$(userLngId).value = this.getCenter().lng();
				});	
			});
		} else {
			GEvent.addListener(map,'moveend',function(){
				$(userZoomId).value = this.getZoom();
				$(userLatId).value = this.getCenter().lat();
				$(userLngId).value = this.getCenter().lng();
			});	
		}
	}
	
	this.toggleCustomStart = function (e,custom){		
		if(custom){
			customStart = true;
			this.hideMapCover();
			var lat = $(userLatId).value;
			var lng = $(userLngId).value;
			var zoom = parseInt($(userZoomId).value);
			if(lat && lng && zoom) {
				map.setCenter(new GLatLng(lat,lng),zoom);
			} else {
				$(userLatId).value = map.getCenter().lat();
				$(userLngId).value = map.getCenter().lng();
				$(userZoomId).value = map.getZoom();
			}							
		} else {
			customStart = false;
			this.setMapToContainingBounds();
			this.showMapCover();
		}
	}
	
	this.initZoom = function() {
		var zoomOptions = {
                sButtonHTML:'<img src="/Images/ToolPan.png">',
                sButtonZoomingHTML:'<img src="/Images/ToolZoom.png">',
                oButtonStartingStyle:{},
                oButtonTop:280,
                oButtonLeft:11,
                oButtonStyle:{background:"#fff"},
                oButtonZoomingStyle:{background:"#fff"},
                bStickyZoom:false,
                bStartInZoom:true,
                nOverlayRemoveMS:2000
              }
        var gZoom = new GZoomControl(null,zoomOptions,null);
        map.addControl(gZoom);
	}
	
	this.initInfoPanel = function() {
		var options = {
						opacity: false,
						alwaysHide:false,
						fixedHeight:452,
						show:0,
						onActive: function(toggler, element){							
							if(element.offsetHeight == 0) {
								element.style.height = '1px';
							}
							element.style.display = 'block';
							toggler.className = toggler.className.replace(/ Closed/, " Open");
							if(toggler.className.indexOf("SummaryToggle") != -1) {
								this.summaryOpen = true;
							}    
						},
						onBackground: function(toggler, element){
								toggler.className = toggler.className.replace(/ Open/, " Closed");
								setTimeout(function(){element.style.display = 'none';},500);
								if(toggler.className.indexOf("SummaryToggle") != -1) {
									this.summaryOpen = false;
								} 
							}
					    }
		this.infoAccordion = new Accordion($(searchPanelId).getElementsByClassName('Title'),$(searchPanelId).getElementsByClassName('Box'),options);
		//create slides for checkbox sections, attach events
		var expandables = $$('div.Expandable');
		var statusSlide = new Fx.Slide(expandables[0],{duration:250});
		var statusRadios = $$(document.forms[0]['specifyStatus'+formSuffix]);
		statusRadios[0].addEvent('click',function(){this.slideOut();}.bind(statusSlide));
		statusRadios[1].addEvent('click',function(){this.slideIn();}.bind(statusSlide));
		if(statusRadios[0].checked){
			statusSlide.hide();
		}		
		var typeSlide = new Fx.Slide(expandables[1],{duration:250});
		var typeRadios = $$(document.forms[0]['specifyTypes'+formSuffix]);
		typeRadios[0].addEvent('click',function(){this.slideOut();}.bind(typeSlide));
		typeRadios[1].addEvent('click',function(){this.slideIn();}.bind(typeSlide));
		if(typeRadios[0].checked){
			typeSlide.hide();
		}		
		//attach validation events to price lists
		$(document.forms[0]['priceLow' + formSuffix]).addEvent('change',function(){this.validatePrice();}.bind(this));	
		$(document.forms[0]['priceHigh' + formSuffix]).addEvent('change',function(){this.validatePrice();}.bind(this));	
		//attach validation events to status,style,type checkboxes
		$$(document.forms[0]['status' + formSuffix]).each(function(item){item.addEvent('click',function(){this.validateStatus();return true;}.bind(this))}.bind(this));
		$$(document.forms[0]['type' + formSuffix]).each(function(item){item.addEvent('click',function(){this.validateType();return true;}.bind(this))}.bind(this));
	}
	
	this.initMapCover = function(){
		var coverDiv = $(document.createElement('div'));
		coverDiv.id = 'mapCover' + formSuffix;
		coverDiv.className = 'MapCover';		
		map.getContainer().appendChild(coverDiv);
		coverDiv.style.backgroundColor = '#000';
		coverDiv.style.width = coverDiv.offsetParent.offsetWidth;
		coverDiv.style.height = coverDiv.offsetParent.offsetHeight;
		coverDiv.setOpacity(.2);
	}
	
	this.initSummaryGrid = function(url, defaultSort, sortOrders, imgUrls) {
		gridUrl = url;
		imageUrls = imgUrls;
		var summaryGrid = $('summaryGrid' + formSuffix);
		function onScroll(event){
			event = new Event(event);
			var scrollBottom = (event.target.scrollTop + event.target.offsetHeight);
			var scrollingDown = (lastScroll < scrollBottom);
			var scrollThreshold = event.target.scrollHeight-2*event.target.offsetHeight;
			if(!fetchingGridRows && !allRowsLoaded && scrollingDown && (scrollBottom > scrollThreshold)) {
				var me = this;
				var qs = Object.toQueryString(currentSort);
				window.setTimeout(function(){me.getSummaryGridPage(false,qs);},500);
				var loadingRow = document.createElement('tr');
				var cell = document.createElement('td');
				cell.innerHTML = '<div class="LoadingListings"></div>';
				cell.colSpan = 99;
				loadingRow.appendChild(cell);
				summaryGrid.tBodies[0].appendChild(loadingRow);
				fetchingGridRows = true;
			}
		}
		if(summaryGrid) {		
			//ie scrolling is on div, moz is on tbody, so have to wire events to both
			$(summaryGrid.parentNode).addEvent('scroll',onScroll.bind(this));
			$(summaryGrid.tBodies[0]).addEvent('scroll',onScroll.bind(this));
			summaryGrid.startHeight = summaryGrid.parentNode.offsetHeight;	
			summaryGrid.tBodies[0].startHeight = summaryGrid.tBodies[0].offsetHeight;
		} 		
		var sortLinks = $$('#summaryGrid' + formSuffix + ' a');
		for(var ii=0;ii<sortLinks.length;ii++){
			var sortBy = sortLinks[ii].className;
			var sortOrder = sortOrders[sortBy];
			if(sortBy == defaultSort.SortBy) {
				sortLinks[ii].style.paddingRight = '14px';
				sortLinks[ii].style.backgroundImage = 'url('+ imageUrls[defaultSort.SortOrder] + ')';
				reverseSort = (defaultSort.SortOrder != sortOrder);
			}
			sortLinks[ii].className = "SortLink";
			sortLinks[ii].naturalSort = sortOrder;
			sortLinks[ii].sortBy = sortBy;
			sortLinks[ii].addEvent('click',this.sortGrid.bind(this));
		}
		this.getSummaryGridPage();
	}
	
	this.showMapCover = function(){
		var coverDiv = $('mapCover' + formSuffix);
		coverDiv.style.width = coverDiv.offsetParent.offsetWidth;
		coverDiv.style.height = coverDiv.offsetParent.offsetHeight;
		coverDiv.style.display = 'block';
		this.map().disableScrollWheelZoom();
	}
	
	this.hideMapCover = function(){
		$('mapCover' + formSuffix).style.display = 'none';
		this.map().enableScrollWheelZoom();
	}
	
	this.listingsLoadedFor = function(zoom) {
		return zoomLevelsLoaded[zoom];
	}
	
	this.setListingsLoadedFor = function(zoom,clustered,unload) {
		zoomLevelsLoaded[zoom] = {loaded:!unload,clustered:clustered};
	}
	
	this.clearListingsLoaded = function() {
		for(var ii=0;ii<=MAX_ZOOM_LEVEL;ii++){
			zoomLevelsLoaded[ii] = {loaded:false,clustered:false};
		}
	}
	
	this.callbackRequiredFor = function(zoom) {
		if(this.listingsLoadedFor(zoom) && this.listingsLoadedFor(zoom).loaded) {
			return false;
		} else {
			for(var ii=zoom;ii>=0;ii--) {
				var zoomLevel = this.listingsLoadedFor(ii);
				if(zoomLevel && zoomLevel.loaded && !zoomLevel.clustered) {
					return false;
				}
			}
		}
		return true;
	}
	
	this.onZoomEnd = function(oldZoom, newZoom) {
		if(this.callbackRequiredFor(newZoom)){
			this.fetchMapData();
		}
	}
	
	this.onMoveEnd = function() {
		if(this.listingsLoadedFor(this.map().getZoom())){
			this.updateCount();
		}
		if(initialized) {
			this.saveState();
		}
	}

	this.fetchMapData = function(params) {
		var fv = this.getSearchFilterValues();
		var qs = (fv) ? '&' + fv : '';
		qs = (params) ? qs + '&' + params : qs;
		qs = (moreListings) ? qs + '&ml=true' : qs;
		if(map.isLoaded()){
			var bounds = map.getBounds();
			var boundsParams = new Object();
			boundsParams.swLat = bounds.getSouthWest().lat();
			boundsParams.swLng = bounds.getSouthWest().lng();
			boundsParams.neLat = bounds.getNorthEast().lat();
			boundsParams.neLng = bounds.getNorthEast().lng();
			boundsParams.zoom = map.getZoom();
			qs = qs + '&' + Object.toQueryString(boundsParams);
		}
		var url = dataUrl + qs;
		if(this.dataRequest && this.dataRequest.running) {
			this.dataRequest.cancel();
		}
		fetchingMapData = true;
		this.dataRequest = new Ajax(url,{method:'get',autoCancel:true,onComplete: this.handleMapData.bind(this)});
		this.dataRequest.request();
		var countLabel = $('mapCount' + formSuffix);
		if(countLabel) {
			countLabel.innerHTML = '&nbsp;Loading listings...&nbsp;';
		}		
	}
	
	this.handleMapData = function(data) {
		fetchingMapData = false;
		var mapData = Json.evaluate(data);			
		if(mapData.bounds) {
			var sw = new GLatLng(mapData.bounds.southWest.lat,mapData.bounds.southWest.lng);
			var ne = new GLatLng(mapData.bounds.northEast.lat,mapData.bounds.northEast.lng);
			var gBounds = new GLatLngBounds(sw,ne);
			containingBounds = gBounds;
			if(!map.isLoaded()) {			
				this.setMapCenterAndZoom(gBounds);
			}
		}
		var zoom = (mapData.zoom) ? mapData.zoom : this.map().getZoom();
		this.addMarkersToMap(this.createMarkers(mapData.markers),zoom);	
		if(!editMode){
			this.hideMapCover();
		} else {
			this.toggleCustomStart(null,customStart);
		}
		initialized = true;
		this.updateCount();
	}
	
	this.fetchSummary = function(listingId) {
		$('summaryDetails' + formSuffix).innerHTML = '<div class="Loading"></div>';
		if(this.infoAccordion && !this.infoAccordion.summaryOpen) {
			this.infoAccordion.display(1);
			this.infoAccordion.summaryOpen = true;
		}
		if(this.summaryRequest && this.summaryRequest.running) {
			this.summaryRequest.cancel();
		}
		
		var url = baseSummaryUrl + "&lid=" + listingId;
		this.summaryRequest = new Ajax(url,{method:'get',onComplete:this.handleSummary.bind(this)});
        this.summaryRequest.request();
	}
	
	this.handleSummary = function(summary,marker) {
		$('summaryDetails' + formSuffix).innerHTML = summary;
	}
	
	this.fetchGridRows = function(params) {
			var fv = this.getSearchFilterValues();
			var qs = (fv) ? '&' + fv : '';
			qs = (params) ? qs + '&' + params : qs;
			qs = (gridPage > 1) ? qs + '&Page=' + gridPage : qs;
			qs = (moreListings) ? qs + '&ml=true' : qs;
			var url = gridUrl + qs;
			if(this.gridRequest && this.gridRequest.running) {
				this.gridRequest.cancel();
			}
			this.gridRequest = new Ajax(url,{method:'get',autoCancel:true,onComplete: this.handleGridRows.bind(this)});
			this.gridRequest.request();
	}
	
	this.handleGridRows = function(tableData) {
		var summaryPanel = $('summaryPanel' + formSuffix);				
		var summaryGrid = $('summaryGrid' + formSuffix);				
		if(fetchingGridRows) {//if fetching from scrolling, delete loading row
			summaryGrid.deleteRow(summaryGrid.rows.length-1);
		}
		fetchingGridRows = false;				
		var noListings = $('noListings' + formSuffix);
		if(purgeOldRows) {
			var oldRowCount = summaryGrid.rows.length;
			for(var ii=1;ii<oldRowCount;ii++) {
				summaryGrid.deleteRow(1);
			}
			purgeOldRows = false;
		}
		var currentRowCount = summaryGrid.rows.length;
		var div = document.createElement('DIV');
		div.innerHTML = tableData;
		var table = div.childNodes[0];		
		if(table && table.nodeName.toLowerCase() == 'table') {
			noListings.style.display = 'none';
			summaryPanel.style.display = 'block';
			summaryPanel.style.borderLeft = 'none';
			try {//ie throws an error here
				summaryGrid.tBodies[0].style.display = 'table-row-group';
			} catch (ex) {
				summaryGrid.tBodies[0].style.display = 'block';
			}
			var newRowCount = table.rows.length;
			allRowsLoaded = (newRowCount < GRID_PAGE_SIZE); 
			var rowHeight = 0;
			for(var ii=0;ii<newRowCount;ii++) {				
				summaryGrid.tBodies[0].appendChild(table.rows[0]);
				var newRow = $(summaryGrid.rows[ii+currentRowCount]);
				newRow.title = 'Click to view listing on map';
				rowHeight += Math.max(newRow.offsetHeight,0);
				newRow.addEvent('mouseover',function(ev){
					var event = new Event(ev);
					var t = event.target;
					for(var ii=0;ii<50;ii++){
						if(t.tagName.toLowerCase() == 'tr' && (t.className.indexOf("Item") != -1)) break;
						t = t.parentNode;
					}	
					t.style.backgroundColor = '#eee';
					}.bind(this));
				newRow.addEvent('mouseout',function(ev){
					var event = new Event(ev);
					var t = event.target;
					for(var ii=0;ii<50;ii++){
						if(t.tagName.toLowerCase() == 'tr' && (t.className.indexOf("Item") != -1)) break;
						t = t.parentNode;
					}	
					t.style.backgroundColor = '#fff';
					}.bind(this));
				newRow.addEvent('click',function(ev){
					var event = new Event(ev);
					var t = event.target;
					for(var ii=0;ii<50;ii++){
						if(t.tagName.toLowerCase() == 'tr') break;
						if(t.tagName.toLowerCase() == 'a') break;
						t = t.parentNode;
					}
					if(t && t.tagName.toLowerCase() == 'tr') {	
						var inputs = t.getElementsByTagName('input');
						var x = Json.evaluate(inputs[0].value);				
						this.map().setCenter(new GLatLng(x.lat,x.lng), 16);
						this.fetchSummary(x.lid);
					}
					}.bind(this));
			}	
			summaryGrid.tBodies[0].style.height = Math.min(rowHeight,summaryGrid.tBodies[0].startHeight) + 'px';	
			summaryGrid.parentNode.style.height = Math.min(summaryGrid.offsetHeight,summaryGrid.startHeight) + 'px';	
		} else if (currentRowCount <= 1) {			
			summaryPanel.style.display = 'none';
			summaryGrid.tBodies[0].style.display = 'none';
			summaryGrid.parentNode.style.height = summaryGrid.offsetHeight + 'px';
			noListings.style.display = 'block';
			
		}
		this.map().checkResize();
	}
	
	this.createMarkers = function(markerData) {
		var markers = new Array();
		for(var ii=0;ii<markerData.length;ii++) {			
			var marker = this.createMarker(markerData[ii]);
			markers.push(marker);
		}
		return markers;
	}
	
	this.createMarker = function(marker) {
		var icon = (marker.listingId) ? this.listingIcon : this.getClusterIcon(marker.count);
		var title = this.getMarkerTitle(marker);
		var gMarker = new GMarker(new GLatLng(marker.point.lat,marker.point.lng),{title:title,icon:icon});
		if(marker.listingId) {
			gMarker.listingId = marker.listingId;
			var me = this;
			if(summaryUrl) {
				GEvent.addListener(gMarker,"click",function(){me.fetchSummary(this.listingId);});
			}	
		} else {
			GEvent.addListener(gMarker,"click",function(){map.setCenter(this.getPoint(),map.getZoom()+1);});
			gMarker.count = marker.count;
		}
		return gMarker;
	}
	
	this.getMarkerTitle = function(marker) {
		var title = '';
		if(marker.listingId) {
			title = (marker.address) ? marker.address + ", " + marker.price : marker.price; 
		} else if (marker.count) {
			title = "Click to view " + marker.count + " more listings";
		} 
		return title;
	}
	
	this.getClusterIcon = function(count){
		if(count >= 50){
			return this.cluster50Icon;
		} else if (count >= 25){
			return this.cluster25Icon;
		} else if (count >= 10){
			return this.cluster10Icon;
		} else {
			return this.cluster5Icon;
		}
	}
	
	this.setMapCenterAndZoom = function(bounds) {
		var lat = (bounds.getSouthWest().lat() + bounds.getNorthEast().lat()) / 2;
		var lng = (bounds.getSouthWest().lng() + bounds.getNorthEast().lng()) / 2;
		var center = new GLatLng(lat,lng);
		startZoom = map.getBoundsZoomLevel(bounds);
		this.setListingsLoadedFor(startZoom);//have to call this prior to setCenter() or data loaded twice during intial load	
		map.setCenter(center,startZoom);
	}
	
	this.setMapToContainingBounds = function() {
		//setCenter will cause the moveend to fire, which will result in the user lat, lng, and zoom being overwritten
		//with the containingbounds settings. The editMode conditionals are to save and restore the user settings
		//after the call to setCenter.
		if(containingBounds) {
			if(editMode) {
				var oldLat = $(userLatId).value;
				var oldLng = $(userLngId).value;
				var oldZoom = $(userZoomId).value;
			}
			var lat = (containingBounds.getSouthWest().lat() + containingBounds.getNorthEast().lat()) / 2;
			var lng = (containingBounds.getSouthWest().lng() + containingBounds.getNorthEast().lng()) / 2;
			var zoom = map.getBoundsZoomLevel(containingBounds);
			map.setCenter(new GLatLng(lat,lng),zoom);
			if(editMode) {
				$(userLatId).value = oldLat;
				$(userLngId).value = oldLng;
				$(userZoomId).value = oldZoom;
			}
		}
	}

	this.addMarkersToMap = function(markers,currZoom){
		if(!manager) manager = new MarkerManager(map,{borderPadding: 0});
		if(purgeListingData) {
			manager.clearMarkers();
			this.clearListingsLoaded();
			loadedClusters = new Array();
			loadedListings = {};
			purgeListingData = false;
		}
		var minZoom = (this.startPointSpecified() || initialized) ? currZoom : 0;
		var clustered = false;		
		for(var ii=0;ii<markers.length;ii++) {				
			if(markers[ii].listingId){
				var maxZoom = this.maxZoomForListing(markers[ii], currZoom); //returns -1 if no effective maximum
				if (maxZoom == Number.POSITIVE_INFINITY) {
					//No action needed
				} else if(maxZoom > 0){
					manager.addMarker(markers[ii], minZoom, maxZoom);
				} else {
					manager.addMarker(markers[ii], minZoom);
				}
			} else {
				manager.addMarker(markers[ii], minZoom, currZoom);
				clustered = true;
				loadedClusters.push({zoom:currZoom,count:markers[ii].count,latlng:markers[ii].getPoint()});
			}
		}
		this.setListingsLoadedFor(currZoom,clustered);
		manager.refresh();
	}
	
	this.maxZoomForListing = function(marker,zoom) {
		var listing = loadedListings[marker.listingId];
		var max = -1; //listing not found
		if(listing) {
			if(listing.minZoom > zoom) {
				max = listing.minZoom--;
				listing.minZoom = zoom;
			} else {
				max = Number.POSITIVE_INFINITY; //no effective maximum
			}
		} else {			
			var minzoom = (this.startPointSpecified() || initialized) ? zoom : 0;
			loadedListings[marker.listingId] = {listingId:marker.listingId,minZoom:minzoom,latlng:marker.getPoint()};
		}
		return max;				
	}
	
	this.filterListings = function(ev) {
		ev.stopPropagation();
		ev.preventDefault;
		ev.stop();
		if(this.validateSearchFilter()){
			this.total = null;
			purgeListingData = true;
			this.setSearchFilterValues();
			this.fetchMapData();
			this.getSummaryGridPage(true,Object.toQueryString(currentSort));
			this.saveState();
		}
	}
	
	this.moreListings = function(ev) {
		ev.stopPropagation();
		ev.preventDefault;
		ev.stop();
		var link = ev.target;
		moreListings = !moreListings;
		if(moreListings) {
			link.innerHTML = "Original listings";
		} else {
			link.innerHTML = "More listings";
		}
		this.total = null;
		purgeListingData = true;
		this.saveState();
		this.fetchMapData();
		this.getSummaryGridPage(true);
	}
	
	this.sortGrid = function(event) {
		var ev = new Event(event);
		var el = ev.target;
		ev.stopPropagation();
		ev.preventDefault;
		ev.stop();
		var sortLinks = $$('#summaryGrid' + formSuffix + ' a.SortLink');
		//reset arrow on all sort links
		for(var ii=0;ii<sortLinks.length;ii++){
			sortLinks[ii].style.backgroundImage = '';
			sortLinks[ii].style.paddingRight = '0';
			sortLinks[ii].parentNode.className = '';
		}
		//determine currentSort and set callback and indicator
		el.style.paddingRight = '14px';
		el.parentNode.className = "HeaderSelected";
		var sort = "SortBy=" + el.sortBy;
		if(el.sortBy == currentSort.sortBy) {
			reverseSort = !reverseSort;
			if(reverseSort) {
				sort = sort + "&reverseSort=true";			
				el.style.backgroundImage = 'url('+ imageUrls[((el.naturalSort == "Descending") ? "Ascending" : "Descending")] + ')';
			} else {
				el.style.backgroundImage = 'url('+ imageUrls[el.naturalSort] + ')';
			} 			
		} else {
			reverseSort = false;
			el.style.backgroundImage = 'url('+ imageUrls[el.naturalSort] + ')';			
		}
		currentSort.sortBy = el.sortBy;
		currentSort.reverseSort = reverseSort;
		this.getSummaryGridPage(true,Object.toQueryString(currentSort));
	}
	
	this.getSummaryGridPage = function(purgeCurrent,params) {
		if(gridUrl){
			if(purgeCurrent) {
				purgeOldRows = true;
				gridPage = 1;
			} 
			this.fetchGridRows(params);
			gridPage++;
		}		
	}
	
	this.updateCount = function() {
		var countLabel = $('mapCount' + formSuffix);
		if(countLabel && !fetchingMapData) {
			var count = 0;
			var visible = 0;
			var bounds = this.map().getBounds();
			var currZoom = this.map().getZoom();
			for(var i in loadedListings) {
				var listing = loadedListings[i];
				count += 1;
				if(listing.minZoom <= currZoom && bounds.contains(listing.latlng)){
					visible += 1;
				}
			}
			for(var ii=0;ii<loadedClusters.length;ii++) {
				var cluster = loadedClusters[ii];	
				count += cluster.count;			
				if (cluster.zoom == currZoom && bounds.contains(cluster.latlng)){
					visible += cluster.count;
				}				 
			}
			if(!this.total){
				this.total = count;	
			}		
			countLabel.innerHTML = (this.total>0) ?	'&nbsp;Showing ' + visible + ' of ' + this.total + '&nbsp;' : '&nbsp;No listings found&nbsp;';
		}
	}
	
	this.saveState = function() {	
		var state = {};
		if($(filterPanelId)){
			for(i in filterValues) {
				state[i] = filterValues[i];
			}
		}
		state.zoom = map.getZoom();
		state.lat = map.getCenter().lat();		
		state.lng = map.getCenter().lng();
		if(moreListings) {
			state.ml = 'true';
		}
		var stateCookie = new Hash.Cookie('mapState',{duration:0});
		stateCookie.empty();
		stateCookie.extend(state);
		stateCookie.save();
	}
	
	this.loadState = function() {
		var state = new Hash.Cookie('mapState');		
		if(state.length > 0) {
			var lat = state.get('lat'); 			
			var lng = state.get('lng');
			var zoom = state.get('zoom');
			map.setCenter(new GLatLng(lat,lng),zoom);
			if(state.get('ml') === 'true') {
				moreListings = true;
				$('moreListings' + formSuffix).setText("Original listings");
			}
			if($(filterPanelId)){
				var status = state.get('status');
				if(status){
					status = status.split(',');
					$$(document.forms[0]['specifyStatus' + formSuffix])[1].checked = true;
					var statusCBs = document.forms[0]['status' + formSuffix];
					for(var ii=0;ii<statusCBs.length;ii++) {
						statusCBs[ii].checked = status.contains(statusCBs[ii].value); 
					}
				} else {
					$$(document.forms[0]['specifyStatus' + formSuffix])[0].checked = true;
					$$(document.forms[0]['specifyStatus' + formSuffix])[0].fireEvent('click');
				}
				var type = state.get('type');
				if(type && type.length > 0){					
					type = type.split(',');
					$$(document.forms[0]['specifyTypes' + formSuffix])[1].checked = true;
					var typeCBs = document.forms[0]['type' + formSuffix];
					for(var ii=0;ii<typeCBs.length;ii++) {
						typeCBs[ii].checked = type.contains(typeCBs[ii].value);
					}
				} else {
					$$(document.forms[0]['specifyTypes' + formSuffix])[0].checked = true;
					$$(document.forms[0]['specifyTypes' + formSuffix])[0].fireEvent('click');
				}
				var priceLow = state.get('priceLow');
				if(priceLow){
					var opts = $(document.forms[0]['priceLow' + formSuffix]).options;
					for(var ii=0;ii<opts.length;ii++) {
						if(opts[ii].value == priceLow) {
							$(document.forms[0]['priceLow' + formSuffix]).selectedIndex = ii;
							break;
						}
					}
				}
				var priceHigh = state.get('priceHigh');
				if(priceHigh){
					var opts = $(document.forms[0]['priceHigh' + formSuffix]).options;
					for(var ii=0;ii<opts.length;ii++) {
						if(opts[ii].value == priceHigh) {
							$(document.forms[0]['priceHigh' + formSuffix]).selectedIndex = ii;
							break;
						}
					}
				}
				var minBeds = state.get('beds');
				if(minBeds){
					var opts = $(document.forms[0]['minBeds' + formSuffix]).options;
					for(var ii=0;ii<opts.length;ii++) {
						if(opts[ii].value == minBeds) {
							$(document.forms[0]['minBeds' + formSuffix]).selectedIndex = ii;
							break;
						}
					}
				}
				var minBaths = state.get('baths');
				if(minBaths){
					var opts = $(document.forms[0]['minBaths' + formSuffix]).options;
					for(var ii=0;ii<opts.length;ii++) {
						if(opts[ii].value == minBaths) {
							$(document.forms[0]['minBaths' + formSuffix]).selectedIndex = ii;
							break;
						}
					}
				}
			}
			return true;			
		} else {
			return false;
		}
	}
	
	this.setSearchFilterValues = function() {
		var obj = {};
		var priceLow = $(document.forms[0]['priceLow' + formSuffix]).getValue();		
		var priceHigh = $(document.forms[0]['priceHigh' + formSuffix]).getValue();
		var minBeds = $(document.forms[0]['minBeds' + formSuffix]).getValue();
		var minBaths = $(document.forms[0]['minBaths' + formSuffix]).getValue();

		var selectedTypes = new Array();
		if($$(document.forms[0]['specifyTypes' + formSuffix])[1].checked){
			var type = document.forms[0]['type' + formSuffix];
			for(var ii=0;ii<type.length;ii++) {
				if(type[ii].checked) {
					selectedTypes.push(type[ii].value);
				}
			}
			if(selectedTypes.length == type.length) {
				selectedTypes = new Array();
			}
		}
		
		var selectedStatus = new Array();
		if($$(document.forms[0]['specifyStatus' + formSuffix])[1].checked){
			var status = document.forms[0]['status' + formSuffix];
			for(var ii=0;ii<status.length;ii++) {
				if(status[ii].checked) {
					selectedStatus.push(status[ii].value);
				}
			}
			if(selectedStatus.length == status.length) {
				selectedStatus = new Array();
			}
		}

		if(priceLow > 0) obj.priceLow = priceLow;
		if(priceHigh < 99999999999) obj.priceHigh = priceHigh;
		if(minBeds > 1) obj.beds = minBeds;
		if(minBaths > 1) obj.baths = minBaths;
		if(selectedStatus.length > 0) obj.status = selectedStatus.join(',');
		if(selectedTypes.length > 0) obj.type = selectedTypes.join(',');
		if(moreListings) obj.moreListings = 'true';
		filterValues = obj;
	}
	
	this.getSearchFilterValues = function() {		
		return Object.toQueryString(filterValues);	
	}
	
	this.validatePrice = function(){
		var isValid = false;
		var errMsg = $('priceError'+formSuffix);
		var priceLow = $(document.forms[0]['priceLow' + formSuffix]).getValue().toInt();		
		var priceHigh = $(document.forms[0]['priceHigh' + formSuffix]).getValue().toInt();
		if(priceHigh > priceLow){
			isValid = true;
			errMsg.style.display = 'none';
		} else {
			errMsg.style.display = 'inline';
		}   
		return isValid;
	}
	
	//Why all the all the parentNode.parentNode stuff?
	//The status, style, and type validation messages are contained inside the Moo sliders (fixed height 
	//and overflow:hidden), so the height of the slider has to be adjusted to accomodate the message. 
	this.validateType = function(){
		var isValid = $$(document.forms[0]['specifyTypes' + formSuffix])[0].checked;
		var errMsg = $('typeError'+formSuffix);
		if(!isValid){		
			var type = document.forms[0]['type' + formSuffix];
			for(var ii=0;ii<type.length;ii++) {
				if(type[ii].checked) {
					isValid = true;
					errMsg.parentNode.parentNode.style.height = errMsg.parentNode.parentNode.offsetHeight - errMsg.offsetHeight + 'px';
					errMsg.style.display = 'none';
					break;
				}
			}
		}
		if(!isValid){
			var expandSlide = (errMsg.style.display == 'none');
			errMsg.style.display = 'block';
			if(expandSlide) {
				errMsg.parentNode.parentNode.style.height = errMsg.parentNode.parentNode.offsetHeight + errMsg.offsetHeight + 'px';
			}
		}
		return isValid; 
	}
	
	this.validateStatus = function(){
		var isValid = $$(document.forms[0]['specifyStatus' + formSuffix])[0].checked;
		var errMsg = $('statusError'+formSuffix);
		if(!isValid){		
			var status = document.forms[0]['status' + formSuffix];
			for(var ii=0;ii<status.length;ii++) {
				if(status[ii].checked) {
					isValid = true;					
					errMsg.parentNode.parentNode.style.height = errMsg.parentNode.parentNode.offsetHeight - errMsg.offsetHeight + 'px';
					errMsg.style.display = 'none';
					break;
				}
			}
		}
		if(!isValid){			
			var expandSlide = (errMsg.style.display == 'none');
			errMsg.style.display = 'block';	
			if(expandSlide){	
				errMsg.parentNode.parentNode.style.height = errMsg.parentNode.parentNode.offsetHeight + errMsg.offsetHeight + 'px';
			}
		}
		return isValid;  
	}
   
	this.validateSearchFilter = function(){     
		var isValid = this.validatePrice();
		var isValid = (this.validateStatus() && isValid);
		var isValid = (this.validateType() && isValid);
		if(!isValid) alert('Please complete missing or incorrect information before submitting your search');
		return isValid;			            
	}
}
