/* markermanager.js 7/23/2010 1:37:37 PM */

function MarkerManager(map,opt_opts){var me=this;me.map_=map;me.mapZoom_=map.getZoom();me.projection_=map.getCurrentMapType().getProjection();opt_opts=opt_opts||{};me.tileSize_=MarkerManager.DEFAULT_TILE_SIZE_;var maxZoom=MarkerManager.DEFAULT_MAX_ZOOM_;if(opt_opts.maxZoom!=undefined){maxZoom=opt_opts.maxZoom;}
me.maxZoom_=maxZoom;me.trackMarkers_=opt_opts.trackMarkers;var padding;if(typeof opt_opts.borderPadding=="number"){padding=opt_opts.borderPadding;}else{padding=MarkerManager.DEFAULT_BORDER_PADDING_;}
me.swPadding_=new GSize(-padding,padding);me.nePadding_=new GSize(padding,-padding);me.borderPadding_=padding;me.gridWidth_=[];me.grid_=[];me.grid_[maxZoom]=[];me.numMarkers_=[];me.numMarkers_[maxZoom]=0;GEvent.bind(map,"moveend",me,me.onMapMoveEnd_);me.removeOverlay_=function(marker){if(!marker.isInfoWindowOpened){map.removeOverlay(marker);me.shownMarkers_--;}};me.addOverlay_=function(marker){if(!marker.isInfoWindowOpened){marker.isInfoWindowOpened=false;map.addOverlay(marker);me.shownMarkers_++;}};me.resetManager_();me.shownMarkers_=0;me.shownBounds_=me.getMapGridBounds_();};MarkerManager.DEFAULT_TILE_SIZE_=1024;MarkerManager.DEFAULT_MAX_ZOOM_=17;MarkerManager.DEFAULT_BORDER_PADDING_=100;MarkerManager.MERCATOR_ZOOM_LEVEL_ZERO_RANGE=256;MarkerManager.prototype.resetManager_=function(){var me=this;var mapWidth=MarkerManager.MERCATOR_ZOOM_LEVEL_ZERO_RANGE;for(var zoom=0;zoom<=me.maxZoom_;++zoom){me.grid_[zoom]=[];me.numMarkers_[zoom]=0;me.gridWidth_[zoom]=Math.ceil(mapWidth/me.tileSize_);mapWidth<<=1;}};MarkerManager.prototype.clearMarkers=function(){var me=this;me.processAll_(me.shownBounds_,me.removeOverlay_);me.resetManager_();};MarkerManager.prototype.getTilePoint_=function(latlng,zoom,padding){var pixelPoint=this.projection_.fromLatLngToPixel(latlng,zoom);return new GPoint(Math.floor((pixelPoint.x+padding.width)/this.tileSize_),Math.floor((pixelPoint.y+padding.height)/this.tileSize_));};MarkerManager.prototype.addMarkerBatch_=function(marker,minZoom,maxZoom){var mPoint=marker.getPoint();if(this.trackMarkers_){GEvent.bind(marker,"changed",this,this.onMarkerMoved_);}
GEvent.addListener(marker,"infowindowopen",function(){marker.isInfoWindowOpened=true;});GEvent.addListener(marker,"infowindowclose",function(){marker.isInfoWindowOpened=false;});var gridPoint=this.getTilePoint_(mPoint,maxZoom,GSize.ZERO);for(var zoom=maxZoom;zoom>=minZoom;zoom--){var cell=this.getGridCellCreate_(gridPoint.x,gridPoint.y,zoom);cell.push(marker);gridPoint.x=gridPoint.x>>1;gridPoint.y=gridPoint.y>>1;}};MarkerManager.prototype.isGridPointVisible_=function(point){var me=this;var vertical=me.shownBounds_.minY<=point.y&&point.y<=me.shownBounds_.maxY;var minX=me.shownBounds_.minX;var horizontal=minX<=point.x&&point.x<=me.shownBounds_.maxX;if(!horizontal&&minX<0){var width=me.gridWidth_[me.shownBounds_.z];horizontal=minX+width<=point.x&&point.x<=width-1;}
return vertical&&horizontal;}
MarkerManager.prototype.onMarkerMoved_=function(marker,oldPoint,newPoint){var me=this;var zoom=me.maxZoom_;var changed=false;var oldGrid=me.getTilePoint_(oldPoint,zoom,GSize.ZERO);var newGrid=me.getTilePoint_(newPoint,zoom,GSize.ZERO);while(zoom>=0&&(oldGrid.x!=newGrid.x||oldGrid.y!=newGrid.y)){var cell=me.getGridCellNoCreate_(oldGrid.x,oldGrid.y,zoom);if(cell){if(me.removeFromArray(cell,marker)){me.getGridCellCreate_(newGrid.x,newGrid.y,zoom).push(marker);}}
if(zoom==me.mapZoom_){if(me.isGridPointVisible_(oldGrid)){if(!me.isGridPointVisible_(newGrid)){me.removeOverlay_(marker);changed=true;}}else{if(me.isGridPointVisible_(newGrid)){me.addOverlay_(marker);changed=true;}}}
oldGrid.x=oldGrid.x>>1;oldGrid.y=oldGrid.y>>1;newGrid.x=newGrid.x>>1;newGrid.y=newGrid.y>>1;--zoom;}
if(changed){me.notifyListeners_();}};MarkerManager.prototype.removeMarker=function(marker){var me=this;var zoom=me.maxZoom_;var changed=false;var point=marker.getPoint();var grid=me.getTilePoint_(point,zoom,GSize.ZERO);while(zoom>=0){var cell=me.getGridCellNoCreate_(grid.x,grid.y,zoom);if(cell){me.removeFromArray(cell,marker);}
if(zoom==me.mapZoom_){if(me.isGridPointVisible_(grid)){me.removeOverlay_(marker);changed=true;}}
grid.x=grid.x>>1;grid.y=grid.y>>1;--zoom;}
if(changed){me.notifyListeners_();}};MarkerManager.prototype.addMarkers=function(markers,minZoom,opt_maxZoom){var maxZoom=this.getOptMaxZoom_(opt_maxZoom);for(var i=markers.length-1;i>=0;i--){this.addMarkerBatch_(markers[i],minZoom,maxZoom);}
this.numMarkers_[minZoom]+=markers.length;};MarkerManager.prototype.getOptMaxZoom_=function(opt_maxZoom){return opt_maxZoom!=undefined?opt_maxZoom:this.maxZoom_;}
MarkerManager.prototype.getMarkerCount=function(zoom){var total=0;for(var z=0;z<=zoom;z++){total+=this.numMarkers_[z];}
return total;};MarkerManager.prototype.addMarker=function(marker,minZoom,opt_maxZoom){var me=this;var maxZoom=this.getOptMaxZoom_(opt_maxZoom);me.addMarkerBatch_(marker,minZoom,maxZoom);var gridPoint=me.getTilePoint_(marker.getPoint(),me.mapZoom_,GSize.ZERO);if(me.isGridPointVisible_(gridPoint)&&minZoom<=me.shownBounds_.z&&me.shownBounds_.z<=maxZoom){me.addOverlay_(marker);me.notifyListeners_();}
this.numMarkers_[minZoom]++;};GBounds.prototype.containsPoint=function(point){var outer=this;return(outer.minX<=point.x&&outer.maxX>=point.x&&outer.minY<=point.y&&outer.maxY>=point.y);}
MarkerManager.prototype.getGridCellCreate_=function(x,y,z){var grid=this.grid_[z];if(x<0){x+=this.gridWidth_[z];}
var gridCol=grid[x];if(!gridCol){gridCol=grid[x]=[];return gridCol[y]=[];}
var gridCell=gridCol[y];if(!gridCell){return gridCol[y]=[];}
return gridCell;};MarkerManager.prototype.getGridCellNoCreate_=function(x,y,z){var grid=this.grid_[z];if(x<0){x+=this.gridWidth_[z];}
var gridCol=grid[x];return gridCol?gridCol[y]:undefined;};MarkerManager.prototype.getGridBounds_=function(bounds,zoom,swPadding,nePadding){zoom=Math.min(zoom,this.maxZoom_);var bl=bounds.getSouthWest();var tr=bounds.getNorthEast();var sw=this.getTilePoint_(bl,zoom,swPadding);var ne=this.getTilePoint_(tr,zoom,nePadding);var gw=this.gridWidth_[zoom];if(tr.lng()<bl.lng()||ne.x<sw.x){sw.x-=gw;}
if(ne.x-sw.x+1>=gw){sw.x=0;ne.x=gw-1;}
var gridBounds=new GBounds([sw,ne]);gridBounds.z=zoom;return gridBounds;};MarkerManager.prototype.getMapGridBounds_=function(){var me=this;return me.getGridBounds_(me.map_.getBounds(),me.mapZoom_,me.swPadding_,me.nePadding_);};MarkerManager.prototype.onMapMoveEnd_=function(){var me=this;me.objectSetTimeout_(this,this.updateMarkers_,0);};MarkerManager.prototype.objectSetTimeout_=function(object,command,milliseconds){return window.setTimeout(function(){command.call(object);},milliseconds);};MarkerManager.prototype.refresh=function(){var me=this;if(me.shownMarkers_>0){me.processAll_(me.shownBounds_,me.removeOverlay_);}
me.processAll_(me.shownBounds_,me.addOverlay_);me.notifyListeners_();};MarkerManager.prototype.updateMarkers_=function(){var me=this;me.mapZoom_=this.map_.getZoom();var newBounds=me.getMapGridBounds_();if(newBounds.equals(me.shownBounds_)&&newBounds.z==me.shownBounds_.z){return;}
if(newBounds.z!=me.shownBounds_.z){me.processAll_(me.shownBounds_,me.removeOverlay_);me.processAll_(newBounds,me.addOverlay_);}else{me.rectangleDiff_(me.shownBounds_,newBounds,me.removeCellMarkers_);me.rectangleDiff_(newBounds,me.shownBounds_,me.addCellMarkers_);}
me.shownBounds_=newBounds;me.notifyListeners_();};MarkerManager.prototype.notifyListeners_=function(){GEvent.trigger(this,"changed",this.shownBounds_,this.shownMarkers_);};MarkerManager.prototype.processAll_=function(bounds,callback){for(var x=bounds.minX;x<=bounds.maxX;x++){for(var y=bounds.minY;y<=bounds.maxY;y++){this.processCellMarkers_(x,y,bounds.z,callback);}}};MarkerManager.prototype.processCellMarkers_=function(x,y,z,callback){var cell=this.getGridCellNoCreate_(x,y,z);if(cell){for(var i=cell.length-1;i>=0;i--){callback(cell[i]);}}};MarkerManager.prototype.removeCellMarkers_=function(x,y,z){this.processCellMarkers_(x,y,z,this.removeOverlay_);};MarkerManager.prototype.addCellMarkers_=function(x,y,z){this.processCellMarkers_(x,y,z,this.addOverlay_);};MarkerManager.prototype.rectangleDiff_=function(bounds1,bounds2,callback){var me=this;me.rectangleDiffCoords(bounds1,bounds2,function(x,y){callback.apply(me,[x,y,bounds1.z]);});};MarkerManager.prototype.rectangleDiffCoords=function(bounds1,bounds2,callback){var minX1=bounds1.minX;var minY1=bounds1.minY;var maxX1=bounds1.maxX;var maxY1=bounds1.maxY;var minX2=bounds2.minX;var minY2=bounds2.minY;var maxX2=bounds2.maxX;var maxY2=bounds2.maxY;for(var x=minX1;x<=maxX1;x++){for(var y=minY1;y<=maxY1&&y<minY2;y++){callback(x,y);}
for(var y=Math.max(maxY2+1,minY1);y<=maxY1;y++){callback(x,y);}}
for(var y=Math.max(minY1,minY2);y<=Math.min(maxY1,maxY2);y++){for(var x=Math.min(maxX1+1,minX2)-1;x>=minX1;x--){callback(x,y);}
for(var x=Math.max(minX1,maxX2+1);x<=maxX1;x++){callback(x,y);}}};MarkerManager.prototype.removeFromArray=function(array,value,opt_notype){var shift=0;for(var i=0;i<array.length;++i){if(array[i]===value||(opt_notype&&array[i]==value)){array.splice(i--,1);shift++;}}
return shift;};

/* sorttable.js 7/23/2010 1:37:37 PM */

var sortTable;function createTable(json)
{var tbl=$("results-tbl");var tbody=$("results-tbl").tBodies[0];while(tbody.lastChild){tbody.removeChild(tbody.lastChild);}
var listingCount=json.Listings.length;for(var i=0;i<listingCount;i++){tr=tbody.insertRow(-1);tr.setAttribute("id","grid_"+json.Listings[i].Index);row=json.Listings[i];row.Extra=decodeURIComponent(infoWindowExtra);var tdIndex=tr.insertCell(0);var tdAddress=tr.insertCell(1);var tdLocation=tr.insertCell(2);var tdPrice=tr.insertCell(3);var tdBeds=tr.insertCell(4);var tdBaths=tr.insertCell(5);var tdSqFt=tr.insertCell(6);var tdAcres=tr.insertCell(7);var tdExtra=tr.insertCell(8);tdIndex.appendChild(document.createTextNode(row.Index));tdAddress.appendChild(document.createTextNode(row.Address));tdLocation.appendChild(document.createTextNode(row.City));tdPrice.appendChild(document.createTextNode(row.Price));tdBeds.appendChild(document.createTextNode(row.Beds));tdBaths.appendChild(document.createTextNode(row.Baths));tdAcres.appendChild(document.createTextNode(row.Acreage));tdSqFt.appendChild(document.createTextNode(row.SqFt));tdExtra.innerHTML=row.Extra;setClass(tdIndex,"index");setClass(tdAddress,"address");setClass(tdLocation,"location");setClass(tdPrice,"price");setClass(tdBeds,"other");setClass(tdBaths,"other");setClass(tdSqFt,"other");setClass(tdAcres,"other");setClass(tdExtra,"other");}
$("results-container").show();setRowHovers(tbl,json);Utils.stripeTable(tbl);setHeaderWidth();sortTable=new SortTable("results-tbl");}
function setClass(cell,className){if(cell.attributes["class"])cell.attributes["class"].value=className;else cell.setAttribute("class",className);}
function setRowHovers(tbl,json)
{var trs=tbl.getElementsByTagName("TR");var html;for(var i=0;i<trs.length;i++)
{trs[i].onmouseover=function(){Element.addClassName(this,"hover");}
trs[i].onmouseout=function(){Element.removeClassName(this,"hover");}
trs[i].onclick=function(){var thisIndex=parseInt(this.id.replace('grid_',''));var thisID=lastJSON.Listings[thisIndex-1].ID;showDetails(thisID,thisIndex,true);selectTableRow(thisIndex,false);openInfoWindow(thisIndex,json);}}}
function selectTableRow(index,shouldScroll)
{var trID='grid_'+index;$("results-tbl").firstDescendant().immediateDescendants().each(function(item){item.removeClassName("selected");if(item.id==trID){item.addClassName("selected");if(shouldScroll)$("results").scrollTop=item.offsetTop;}});}
function SortTable(tableId)
{this.tableId=tableId;this.headerSelector="#results-header a";this.that=this;this.sort=function(el,colIndex){this.tbl=document.getElementById(this.tableId)
this.tbody=this.tbl.tBodies[0];var trs=this.tbody.rows;var minVal;var minIndex;var testVal;var tempRow;var cmp;if(this.tbody.reverseSort==null)this.tbody.reverseSort=new Array();if(colIndex==this.tbody.lastColumn)this.tbody.reverseSort[colIndex]=!this.tbody.reverseSort[colIndex];this.tbody.lastColumn=colIndex;$$(this.headerSelector).each(function(item){item.className="";});if(this.tbody.reverseSort[colIndex])el.className="descending";else el.className="ascending";for(var i=0;i<trs.length-1;i++){minIndex=i;minVal=this.getValue(i,colIndex);for(var j=i+1;j<trs.length;j++){testVal=this.getValue(j,colIndex);cmp=this.compare(minVal,testVal);if(this.tbody.reverseSort[colIndex])cmp=-cmp;if(cmp>0){minIndex=j;minVal=testVal;}}
if(minIndex>i){tempRow=this.tbody.removeChild(this.tbody.rows[minIndex]);this.tbody.insertBefore(tempRow,this.tbody.rows[i]);}}}
this.newSort=function(headerElement,colIndex){var tbl=document.getElementById(this.tableId);var arr=new Array();var that=sortTable.that;var reverse=false;if(that.colIndex==colIndex){arr=that.rows;arr.reverse();reverse=true;}
else{that.colIndex=colIndex;for(var i=0;i<tbl.tBodies[0].rows.length;i++){arr[i]=tbl.tBodies[0].rows[i];}
arr.sort(this.compareRows);this.rows=arr;}
for(var i=0;i<arr.length;i++){var row=arr[i];tbl.tBodies[0].appendChild(row);}
$$(this.headerSelector).each(function(item){item.className="";if(item==headerElement){if(reverse){headerElement.toggleClassName("descending");headerElement.toggleClassName("ascending");}
else{headerElement.addClassName("ascending");}}});}
this.getValue=function(rowIndex,cellIndex){var val=this.tbl.tBodies[0].rows[rowIndex].cells[cellIndex].innerHTML;return this.clean(val);}
this.clean=function(val){var regex=/[\s,\$]+/g
return val.replace(regex,"");}
this.compare=function(val1,val2){var float1;var float2;float1=parseFloat(val1);float2=parseFloat(val2);if(!isNaN(float1)&&!isNaN(float2)){val1=float1;val2=float2;}
if(val1==val2)return 0;if(val1>val2)return 1;return-1;}
this.compareRows=function(a,b){var that=sortTable.that;var colIndex=that.colIndex;var aa=that.clean(a.cells[colIndex].innerHTML);var bb=that.clean(b.cells[colIndex].innerHTML);if(isNaN(aa))aa=0;if(isNaN(bb))bb=0;return aa-bb;}}

/* mapemails.js 7/23/2010 1:37:37 PM */

var Email={message:function(text,isError){$("msg").show();$("msg").innerHTML=text;$("msg").className=isError?"error":"success";},validate:function(){if(this.to.value==""){this.message("Please enter an email address to send to",true);this.to.focus();return false;}
if(this.from.value==""){this.message("Please enter the From email address",true);this.from.focus();return false;}
return true;},setBody:function(){var data={maplink:$("save").href};var template=new Template("\n----\nCheck out this real estate map search at http://"+location.host+":\n\n#{maplink}");$("body").value=template.evaluate(data);},send:function(){this.to=$("to");this.from=$("from");this.body=$("body");this.copy=$("copy");if(this.validate()){new Ajax.Request("/mapresults.aspx/SendEmail",{method:"post",contentType:"application/json",postBody:Object.toJSON({to:this.to.value,from:this.from.value,body:this.body.value}),onCreate:function(){$("btn-emailmap").value="Sending...";$("btn-emailmap").disable();},onSuccess:function(transport){Email.message("Message sent",false);setTimeout(Email.reset,3000);},onFailure:function(transport){this.message(transport.responseText,true);}});}},reset:function(){Email.to.value="type here to send to someone else";Email.to.activate();Email.copy.checked=false;$("msg").hide();$("btn-emailmap").value="Send";$("btn-emailmap").enable();}}

/* maputils.js 7/23/2010 1:37:37 PM */

var Utils={stripeTable:function(table){var trs=table.getElementsByTagName("TR");for(var i=0;i<trs.length;i++)
{if(trs[i].parentNode.tagName=="TBODY")
if(i%2!=0)
trs[i].className=trs[i].className+" alt";}},formatCurrency:function(num){var sign;var cents;num=num.toString().replace(/\$|\,/g,'');if(isNaN(num))num="0";sign=(num==(num=Math.abs(num)));num=Math.floor(num*100+0.50000000001);cents=num%100;num=Math.floor(num/100).toString();if(cents<10)cents="0"+cents;for(var i=0;i<Math.floor((num.length-(1+i))/3);i++)
{num=num.substring(0,num.length-(4*i+3))+','+num.substring(num.length-(4*i+3));}
return(((sign)?'':'-')+'$'+num);},calcAverage:function(arr){var total=0;for(var i=0;i<arr.length;i++){var temp=parseFloat(arr[i]);if(!isNaN(temp))total+=temp;}
return total/arr.length;},round:function(val,decimals){var multiplier=1;if(decimals==1)multiplier=10;if(decimals==2)multiplier=100;if(decimals==3)multiplier=1000;if(!val)return"";if(val=="")return"";if(isNaN(parseFloat(val)))return val;return Math.round(val*multiplier)/multiplier;},bookmark:function(title,url){if(window.sidebar)window.sidebar.addPanel(title,url,"");else if(window.opera&&window.print){var mbm=document.createElement("a");mbm.setAttribute("rel","sidebar");mbm.setAttribute("href",url);mbm.setAttribute("title",title);mbm.click();}
else if(document.all){try{window.external.AddFavorite(url,title);}
catch(e){alert("Your browser's security settings are preventing this. Please use Ctrl + D instead");}}}}

/* map.js 7/23/2010 1:37:37 PM */
/* GLOBALS */
var map;
var mgr;
var maxCount = 150;
var mapControl; // global ref so we can remove it if the screen gets too small
var gmarkers = []; // copy of markers added to map so that table events can trigger and work with them
var activeMarker;
var getData = true; // bool for whether or not to call out and get more via map events
var isFirstMapLoad = true;
var lastJSON;   // global JSON var
var disableMapListeners = false;
var plotPointsCount = 0;
var queuedEvents = 0; // keeps track of the # of unhandled 'moveend' events

function initMap()
{
	map = new GMap2(document.getElementById("map"));
	
	map.addControl(new GMapTypeControl());
	addZoomControl();
	map.setCenter(new GLatLng(defaultMapLat, defaultMapLng), defaultMapZoom);
	map.enableDoubleClickZoom();
	map.enableScrollWheelZoom();

	GEvent.addListener(map, "dragstart", function(){
	    showLoading();
	    
	    $(locationDropName).selectedIndex = 0;
	});
	GEvent.addListener(map, "zoomend", showLoading);
	
	// movend covers dragend, zoomend, and panning via the overview control
	GEvent.addListener(map, "moveend", function(){
	    if (!disableMapListeners){
	        queuedEvents++;
		    // lag it to give the map a chance to do it's thing
		    setTimeout('updateOnRest()',200);
	    }
	});

	mgr = new MarkerManager(map, { borderPadding: -50, trackMarkers: false } );
}

function showLoading()
{
    Element.hide('mapalert');
    Element.show('map-loading');
}

function getMapSettingsAndResults(queryString, logSearch)
{
    showLoading();

    // pass these to .NET	
	var center = map.getCenter();
	var bounds = map.getBounds();
	
	updateButtonUrls(queryString);
	
	quickSearch.createCookie('LastSearch', queryString);
	
	new Ajax.Request("/mapresults.aspx/GetMapSettingsAndResults", {
	    asynchronous: true,
	    method: "post",
	    contentType: "application/json",
	    postBody: Object.toJSON( 
	    { 
	        zoom: map.getZoom(), 
	        midLat: center.lat(),
	        midLng: center.lng(),
	        swLat: bounds.getSouthWest().lat(), 
	        swLng: bounds.getSouthWest().lng(), 
	        neLat: bounds.getNorthEast().lat(), 
	        neLng: bounds.getNorthEast().lng(),
	        queryString: queryString,
	        logSearch: logSearch
	    }),
	    onSuccess: function(transport) {
	        json = parseJSON(transport.responseText);
	        if(json != null){
	            disableMapListeners = true;
	            map.setCenter(new GLatLng(parseFloat(json.MedLat), parseFloat(json.MedLng)), parseInt(json.ZoomLevel));
	            disableMapListeners = false;
	        }
	        lastJSON = json;
	        showData();
	    },
	    onFailure: function(transport){
	    alert(transport.responseJSON.d);
		    return null;
	    }
    });
}

// only update the results if this is the last 'moveend'
// event in the event queue
function updateOnRest(){
    queuedEvents--;    
    if (queuedEvents == 0)
    {
        showLoading();
        updateResults(false);
    }
}

// adds a small zoom control if the map is short
// a long one otherwise
function addZoomControl(){
	var minHeight = 300;
	
	if(!map) return;
	
	// remove the existing zoom control if it's there
	if(mapControl) map.removeControl(mapControl);
	
	if($("map").offsetHeight < minHeight) mapControl = new GSmallMapControl();
	else mapControl = new GLargeMapControl();
	
	map.addControl(mapControl);
}

function getResults(queryString, logSearch) 
{
	// don't do the exact same search
	if (!isFirstMapLoad && queryString == quickSearch.readCookie("LastSearch"))
	    return;
	
	var json;
	
	updateButtonUrls(queryString);
	
	quickSearch.createCookie('LastSearch', queryString);
	
	new Ajax.Request("/mapresults.aspx/GetResults", {
		asynchronous: true,
		method: "post",
		contentType: "application/json",
		postBody: Object.toJSON( { queryString: queryString, firstLoad: isFirstMapLoad, logSearch: logSearch } ),
		onCreate: function(){
			$("totalcount").up().toggleClassName("busy");
		},
		onSuccess: function(transport) {
		    isFirstMapLoad = false;
		    $("totalcount").up().toggleClassName("busy");
		    json = parseJSON(transport.responseText);
		    lastJSON = json;
		    window.setTimeout('showData()', 0);
		},
		onFailure: function(transport){
			//$("debug").innerHTML = transport.responseText;
			return null;
		}
	});
}

// TODO - add this to QuickSearch
function queryHasLocationInfo(queryString)
{
    var hash = $H(queryString.toQueryParams());
    var keys = hash.keys();
    
    for (var i = 0; i < keys.length; i++)
    {
        if (keys[i] == "neighborhood" ||
            keys[i] == "city" ||
            keys[i] == "hood" ||
            keys[i] == "area" ||
            keys[i] == "county" ||
            keys[i] == "custom" ||
            keys[i] == "street" ||
            keys[i] == "postalcode" ||
            keys[i] == "neighborhood" ||
            keys[i] == "keyword")
        {
            return true;
        }
    }
    return false;
}

// TODO - add this to QuickSearch
function clearLocationInfo(hash)
{
    hash.remove("keyword");
	hash.remove("neighborhood");
	hash.remove("custom");
	hash.remove("county");
	hash.remove("city");
	hash.remove("hood");
	hash.remove("area");
    hash.remove("street");
    hash.remove("neighborhood");
    hash.remove("postalcode");
    
    return hash;
}

function updateResults(logSearch)
{
    showLoading();

	var bounds = map.getBounds();
	var h;
	var json;
	
	favsMessage(false);
	closeOverlay();
	
	quickSearch.setQueryString();
	
	h = $H(quickSearch.getQueryString().toQueryParams());
	
	// since we're dragging, location isn't needed
	h = clearLocationInfo(h);	
	
	// add bounds of map window so we can get everything
	// within them that match the search
	h["swLat"] = bounds.getSouthWest().lat();
	h["swLng"] = bounds.getSouthWest().lng();
	h["neLat"] = bounds.getNorthEast().lat();
	h["neLng"] = bounds.getNorthEast().lng();
	
	// pass in custom querystring
	getResults(h.toQueryString(), logSearch);
}

function showData()
{
    json = lastJSON;
	
	Element.hide("mapalert");
	
	if (json)
	{	
	    Element.hide('results-container');
		updateTotalCount(json);
		
		mgr.clearMarkers();
		
		if(json.Listings)
		{	
			plotPoints(json);
			
			$("results-container").show();
			
			// if we have <= 7 results, make the 
			// table stretch all the way and disable
			// scrolling of containing div
			if(parseInt(json.Count) <= 7){
				$("results-tbl").style.width = "100%";
				
				if(json.Listings.length == 1){
					showSingleResult(json.Listings[0].ID, 1, json);
				}
			}
		}
		else if (json.Cities || json.Bounds) 
		{
			plotClusters(json);
			adjustHeight();
		}

	    // special case for showing the message if we have no listings (too many)
	    // and no clusters on the current map view
	    var listingCount = parseInt(json.Count);
	    
	    if (listingCount > maxCount && map.getZoom() > 12)
	    {
	        message("<h4>Too many results to display</h4> Try zooming in or narrowing your search criteria.", true);
	    }
	    
	    // if maxToDisplay < 150 (set in mapresults.aspx), that means a board has restricted 
	    // how many can be shown, and we need to show a message saying as much
	    if(maxToDisplay < 150 && listingCount > maxToDisplay)
	    {
			message("<h4>MLS rules prevent us from displaying more than " + maxToDisplay + " at a time. Please zoom in to see all results.", true);
	    }
		
		mgr.refresh();
		
		// hide loading graphic after x seconds
		Element.hide('map-loading');
		
		if (json.Listings){
		    createTable(json);
		    adjustHeight();
		    //window.setTimeout(createTable(json), 0);
		}
    }
	else 
	{
	    // No listings were found for the current search
        var area = 'Found';
        
        var location = $(locationDropName);
        
        if (location.selectedIndex != 0)
            area = "in " + location.options[location.selectedIndex].text;
        else
        {
            // only clear the markers and hide the results table
            // if this was not a location jump
            mgr.clearMarkers();
            Element.hide('results-container');
        }
        
        message("<h4>No Matches " + area + "</h4> To broaden your search, <b>zoom out</b>, or adjust your search criteria.", true);
        
        Element.hide('map-loading');
	
	    // NO JSON RESPONSE
		$("totalcount").innerHTML = "0 Matches";
		
		$("totalcount2").innerHTML = "0";
	}
	
	closeOverlay();
	
	queryString = quickSearch.getQueryString();
	
	// if we're mapping favorites, set getData to false
	// so that zooming, dragging, etc doesn't get fresh 
	// results and wipe out their mapped favorites
	if(queryString.indexOf("favs=1") >= 0)
	{
		favsMessage(true);
	}
}

function showSingleResult(listingID, index, json){
	showDetails(listingID, index, false);
	openInfoWindow(index, json);
	selectTableRow(index, true);
}

function plotPoints(json)
{
    plotPointsCount++;
	var point;
	var marker;
	
	// clear markers in manager
	// and global array
	// mgr.clearMarkers();
	gmarkers = [];
	
	for(var i = 0; i < json.Listings.length; i++)
	{
		point = new GLatLng(
			parseFloat(json.Listings[i].Latitude), 
			parseFloat(json.Listings[i].Longitude)
		);
		marker = createMarker(point, json.Listings[i], (i+1));
		
		// add to array so that table
		// events can open infoWindow
		gmarkers.push(marker);
	}
	
	mgr.addMarkers(gmarkers, 0);
}

function plotClusters(json){
	var point;
	var marker;

	gmarkers = [];
	
	if (json.Cities)
	{
	    for(var i = 0; i < json.Cities.length; i++){
		    point = new GLatLng(
			    parseFloat(json.Cities[i].Latitude),
			    parseFloat(json.Cities[i].Longitude)
		    );
    		
		    marker = createClusterMarker(point, json.Cities[i]);
		    gmarkers.push(marker);
	    }
	    
	    mgr.addMarkers(gmarkers, 0);
	}
}

function updateButtonUrls(queryString){
	var listUrl = "http://" + location.host + "/results.aspx" + "?" + queryString;
	var mapUrl = "http://" + location.host + location.pathname + "?" + queryString;
	
	$("save").href = mapUrl;
	$("toggleview-list").href = listUrl;
}


/*************************
* MARKERS AND WINDOWS
*************************/
function openInfoWindow(index, json)
{
	// find the gmarker[] with this id
	for(var i = 0; i < gmarkers.length; i++){
		if (gmarkers[i].id == index)
		{    
		    // array indexes start at 0 rather than 1
		    var rowIndex = index - 1;
		    
		    // open up the info window for this tr
		    var html = createInfoWindowHtml(json.Listings[rowIndex]);
		    createOverlay(gmarkers[i], html);
    		
		    // pan to marker if it's out of the viewport
		    if(!map.getBounds().containsLatLng(gmarkers[i].getLatLng()))
			    map.panTo(gmarkers[i].getLatLng());
		}
	}
}

//function getPhotoUrl(row)
//{
//	// url for when we don't have an image
//	var url = "/images/comingsoon.jpg";
//	row.Area = boardAlias; // TODO: change to more flexible
//	
//	// if we have an image, change url to what it should be
//	var template = new Template("/photos/#{Area}/thumb/#{PhotoMain}");
//	if(row.PhotoMain) url = template.evaluate(row);
//	
//	return url;
//}

function createInfoWindowHtml(row)
{
	row.photoUrl = row.PhotoMain;
	row.markerTitle = createMarkerTitle(row);
	// if ActionDate isn't empty, it's a fav, and is-fav class needs
	// to be added to the class attribute in the template below
	row.favCssClass = row.ActionDate != "" ? " is-fav" : "";
	row.Extra = unescape(infoWindowExtra);

	if (showOfficeName && row.OfficeName)
	    row.OfficeName = '<div class="office">' + row.OfficeName + '</div>';
	else row.OfficeName = '';
	
	// TODO - store these templates in HtmlTemplates.js, that way we
	// don't have to recreate them every time an infowindow is opened
	var html = '\
		<div class="content">\
			<a class="close" onclick="closeOverlay(); return false">close</a>\
			<a href="/listing.aspx?listingid=#{ID}&index=#{Index}"><img class="thumb" src="#{photoUrl}" alt="#{markerTitle}" /></a>\
			<b class="price">#{Price}</b><br />\
			<div class="address">#{Address}</div>\
			#{Extra}\
			<div class="citystate">#{City}, #{State}</div>\
			#{OfficeName}\
			<div class="stats">';
	
	// different stats for land vs. houses
	var statsTemplate = "<b>#{Beds}</b> Beds | <b>#{Baths}</b> Baths | <b>#{SqFt}</b> SqFt";
	if(row.CategoryID == "2") statsTemplate = "<b>#{Acreage}</b> Acres";
	
	html += statsTemplate;
	
	html += '</div>\
			<a href="#" title="add to favorites" id="results-fav-#{ID}" class="add-fav#{favCssClass}" onclick="toggleFavorite(this, #{ID}); return false"></a>\
			<a href="/listing.aspx?listingid=#{ID}&index=#{Index}">View Full Details</a>\
		</div>\
	';
    return new Template(html).evaluate(row);
}

function createMarker(point, row)
{
	var title = createMarkerTitle(row);
	
	var domainName = location.hostname;
	if(location.port) domainName += ":" + location.port;
	
	var icon = new GIcon();
	icon.image = 'http://' + domainName + imagePath + 'tack.png';
	icon.iconSize = new GSize(23, 29);
	icon.iconAnchor = new GPoint(0, 29);
	
	icon.shadow = 'http://' + domainName + imagePath + 'tack-shadow.png';
	icon.shadowSize = new GSize(40, 29);
	
	var options = { title: title, icon: icon };
	var marker = new GMarker(point, options);
	
	// give it a unique id (row.Index)
	// so that table event can find and 
	// work with this marker
	marker.id = row.Index;
	
	GEvent.addListener(marker, "click", function(){
		openInfoWindow(row.Index, lastJSON);
		selectTableRow(row.Index, true);
		showDetails(row.ID, row.Index, true);
	});

	return marker;
}

function setCenterAndZoomByBounds(swLat, swLng, neLat, neLng){

    disableMapListeners = true;

    // set the zoom level
	var sw = new GLatLng(swLat, swLng);
	var ne = new GLatLng(neLat, neLng);
	var bounds = new GLatLngBounds(sw, ne);
	var zoom = map.getBoundsZoomLevel(bounds);
	
	var centerPoint = bounds.getCenter();
	map.setCenter(centerPoint);
	map.setZoom(zoom);
	
    disableMapListeners = false;
}

function createClusterMarker(point, row)
{
	var width = 28;
	var height = 28;
	var cssClass = "tooltip";
	
	var domainName = location.hostname;
	if(location.port) domainName += ":" + location.port;
	
	var icon = new GIcon();
	icon.image = 'http://' + domainName + imagePath + 'cluster.gif';
	icon.iconSize = new GSize(width, height);
	icon.iconAnchor = new GPoint(10, height+5);
	
	var options = { icon: icon };
	var marker = new GMarker(point, options);
	var listingText = parseInt(row.ListingCount) > 1 ? "matches" : "match";
	row.ListingText = listingText;
	var html = new Template('\
		<div class="rnd"><div class="rnd1"></div><div class="rnd2"></div></div>\
		<div id="tooltip-content">#{DisplayName}, #{State} - <b>#{ListingCount} #{ListingText}</b></div>\
		<div class="rnd"><div class="rnd2"></div><div class="rnd1"></div></div>\
	').evaluate(row);
	
	marker.id = row.CityID;
	
	GEvent.addListener(marker, "click", function(){
	    h = $H(quickSearch.getQueryString().toQueryParams());
	    
	    // since we've selected a city, location info is not needed
	    h = clearLocationInfo(h);
	    
	    queryString = h.toQueryString() + "&city=" + marker.id;
	    
	    getMapSettingsAndResults(queryString, true);
	    
	    closeOverlay();
	    
	    showLoading();
	});
	
	GEvent.addListener(marker, "mouseover", function(){
		if(activeMarker) closeOverlay();
		if(!marker.overlay)
			marker.overlay = new CustomWindow(map, marker, 0, 0, cssClass, html, -22, 30);
		activeMarker = marker;
		map.addOverlay(marker.overlay);
	});
	
	return marker;
}

function createOverlay(marker, html){
	var position = getMarkerPosition(marker);
	if(activeMarker) closeOverlay();
	if(!marker.overlay) marker.overlay = new CustomWindow(map, marker, 324, 110, position.className, html, position.offsetX, position.offsetY);
	activeMarker = marker;
	map.addOverlay(marker.overlay);
}

function createMarkerTitle(row)
{
	// convert price into something usable
	row.Price = Utils.formatCurrency(row.Price);
	
	var template = new Template("#{Price} - #{Address}: #{Beds} BR, #{Baths} BA");
	return template.evaluate(row);
}

function getMarkerPosition(marker){
	var className = "infowindow";
	var center = map.getCenter();
	var latLng = marker.getLatLng();
	
	// defaults set, incase there's no match
	// below
	var result = {
		className: className + " up-right",
		offsetX: 315,// horizontal offset
		offsetY: 15 // vertical offset
	};
	
	// figure out where in the map the marker is, and update
	// className and offset x & y accordingly so that we 
	// can display the right bg image and coordinates for it 
	// to display at
	if(latLng.lat() > center.lat() && latLng.lng() > center.lng()){
		result.className = className + " up-right";
		result.offsetX = 315;
		result.offsetY = 15;
	}
	
	if(latLng.lat() < center.lat() && latLng.lng() < center.lng()){
		result.className = className + " low-left";
		result.offsetX = -15;
		result.offsetY = 130;
	}
	if(latLng.lat() > center.lat() && latLng.lng() < center.lng()){
		result.className = className + " up-left";
		result.offsetX = -15;
		result.offsetY = 15;
	}
	if(latLng.lat() < center.lat() && latLng.lng() > center.lng()){
		result.className = className + " low-right";
		result.offsetX = 315;
		result.offsetY = 130;
	}
	
	return result;
}

function closeOverlay(){
	if (activeMarker) 
	{
		// close any marker overlays that are active
		map.removeOverlay(activeMarker.overlay);
		activeMarker.show();
	}
}

/**********************
* INTERFACE
**********************/

function favsMessage(showMessage){
	if(showMessage){
		$("mid").show();
		$("mid").className = "fav";
		$("mid").innerHTML = "Viewing your " + $("totalcount2").innerHTML + " favorites";
	}
	else 
	{
		$("mid").hide();
	}
}

function updateTotalCount(json)
{
	var count = 0;
	if(json) count = parseInt(json.Count);
	$("totalcount").innerHTML = count + " Matches";
	
	var count_container = $('count');
	
	if (count_container.visible() == false)
	    new Effect.Appear(count_container);
		

	$("totalcount2").innerHTML = count;
	new Effect.Pulsate("totalcount2", { duration: 0.5, pulses: 2 });
}

function showTool(id)
{
	// hide any that might be showing, except the one
	// that relates to this click event
	$("help", "link", "emailmap", "legend").each(function(item){
		if(item.id != id) item.hide();
	});
	
	$(id).toggle();
}

function setHeaderWidth()
{
	var headers = $$("#results-header span");
	var trs = $$("#results-tbl tr");
	var offset = 4;
	
	if(trs.length > 0 && headers.length > 0){
		if(trs.length <= 7) offset = 6;
		
		for(var i = 0; i < trs[0].cells.length; i++){
			headers[i].style.width = "auto";
			var cellWidth = trs[0].cells[i].offsetWidth;
			var width = cellWidth - offset;
			headers[i].style.width = width + "px";	
		}
	}
}

function adjustHeight()
{
	var center = $("center");
	var map = $("map");
	var compliance = $('compliance');
	
	if(center && map){
	    // height of table under map
	    var offsetPadding = 20; // don't know why it needs this extra padding
	    var offset = $('results-container').offsetHeight + $$('#footer li')[0].offsetHeight + offsetPadding;
	    
	    // if we have some compliance crap, take the height 
	    // of that off the total height too
	    if(compliance && compliance.innerHTML.length > 0)
	        offset += compliance.offsetHeight;
	    
	    var height = center.offsetHeight - offset;
	    if(height > 0) map.style.height = height + 'px';
	}
}

function parseJSON(str){
    if(!str) return null;
    var obj = str.evalJSON();
    if(obj == null) return null;
    if(obj.d) return obj.d.evalJSON();
    return null;
}

function message(text, isError){
	$("mapalert").innerHTML = text;
	$("mapalert").className = isError ? "error" : "success";
	$("mapalert").show();
}

/***********************
* CUSTOM INFO WINDOW
************************/
function CustomWindow(map, marker, width, height, className, html, anchorOffsetX, anchorOffsetY){
	this.marker = marker;
	this.html = html;
	this.width = width;
	this.height = height;
	this.className = className;
	this.anchorOffsetX = anchorOffsetX;
	this.anchorOffsetY = anchorOffsetY;
	this.map = map;
	
	if(!anchorOffsetX) this.anchorOffsetX = 0;
	if(!anchorOffsetY) this.anchorOffsetY = 0;
}
CustomWindow.prototype = new GOverlay();
CustomWindow.prototype.initialize = function(){
	var div = document.createElement("div");
	div.style.position = "absolute";
	div.style.height = this.height + "px";
	div.style.width = this.width + "px";
	div.className = this.className;
	div.innerHTML = this.html;
	
	div.style.top = (this.map.fromLatLngToDivPixel(this.marker.getPoint()).y - this.anchorOffsetY) + "px";
	div.style.left = (this.map.fromLatLngToDivPixel(this.marker.getPoint()).x - this.anchorOffsetX) + "px";
	
	this.map.getPane(G_MAP_FLOAT_PANE).appendChild(div);
	
	this.div_ = div;
}
CustomWindow.prototype.remove = function(){
	this.div_.parentNode.removeChild(this.div_);
}
CustomWindow.prototype.redraw = function(){
	this.div_.style.top = (this.map.fromLatLngToDivPixel(this.marker.getPoint()).y - this.anchorOffsetY) + "px";
	this.div_.style.left = (this.map.fromLatLngToDivPixel(this.marker.getPoint()).x - this.anchorOffsetX) + "px";
}



/******************************
* PAGE EVENTS
******************************/
Event.observe(window, "load", function(){
    if (!$("maptop")) return;

    fixLayout();
    adjustHeight();

    quickSearch = new QuickSearch(locationDropName, false);

    // watch for changes in QuickSearch controls
    $$("#quicksearch select").each(function(item) {
        Event.observe(item, "change", function() {
            quickSearch.filters["favs"] = 0;
            quickSearch.filters["rid"] = 0;

            // if this was a location jump,
            // move the map location
            if (item.id == locationDropName) {
                var loc_ddl = $(locationDropName);


                if (loc_ddl.selectedIndex > 1) {
                    quickSearch.filters["favs"] = 0;
                    quickSearch.filters["rid"] = 0;
                    var key = $('keyword');
                    key.value = '';
                    quickSearch.setQueryString();
                    getMapSettingsAndResults(quickSearch.getQueryString(), true);
                }
            }
            else {
                $(locationDropName).selectedIndex = 0;
                updateResults(true);
            }
        });
    });
    
    $$('.distressed input').each(function(item){
        Event.observe(item, 'change', function(){
            updateResults(true);
        });
    });

    Event.observe("keyword", "keypress", function(event){
        var key = event.which || event.keyCode;
        if (key == Event.KEY_RETURN) {
            quickSearch.filters["favs"] = 0;
            quickSearch.filters["rid"] = 0;
            var loc = $(locationDropName);
            loc.options[0].selected = true;
            quickSearch.setQueryString();
            getMapSettingsAndResults(quickSearch.getQueryString(), true);
            //updateResults(true);
            Event.stop(event);
        }
    });

    // add item to location dropdown and select it
    var loc = $(locationDropName);
    loc.options[0].text = "Change Map Location...";
    loc.options[0].selected = true;

    if (GBrowserIsCompatible()) {
        initMap();

        var json;
        var queryString;
        var logSearch = true;

        var lastSearch = quickSearch.readCookie("LastSearch");

        if (lastSearch != null) {
            queryString = lastSearch;

            if (lastSearch.indexOf('swLat') > 0 && lastSearch.indexOf('swLng') > 0 &&
		        lastSearch.indexOf('neLat') > 0 && lastSearch.indexOf('neLng') > 0) {
                // center the map on the last position
                h = $H(queryString.toQueryParams());

                // add bounds of map window so we can get everything
                // within them that match the search
                setCenterAndZoomByBounds(h["swLat"], h["swLng"], h["neLat"], h["neLng"]);

                // last search was a map search - go back to the same position
                getResults(queryString, true);
                return;
            }
        }
        else
            queryString = location.search.replace("?", "");

        if (queryString == null || queryString.length == 0) {
            logSearch = false;
            var bounds = map.getBounds();
            var sw = bounds.getSouthWest();
            var ne = bounds.getNorthEast();
            queryString = "?swLat=" + sw.lat() + "&swLng=" + sw.lng() + "&neLat=" + ne.lat() + "&neLng=" + ne.lng();
        }

        updateButtonUrls(queryString);

        var hasLocInfo = queryHasLocationInfo(queryString);

        if (hasLocInfo){
            // LIST RESULTS - With location information
            getMapSettingsAndResults(queryString, logSearch);
            return;
        }

        getResults(queryString, logSearch);
    }
});

Event.observe(window, "resize", function(){ 
	if(!$("maptop")) return;
	
	adjustHeight();
	scalePreview();
	addZoomControl();
	setHeaderWidth();
});

// prototype Event.observe won't fire this, so do
// it old school
window.onunload = GUnload;






