
function BNMap() {
	this.map = null;
	this.markerMgr = null;
	this.cachedMarkers = null;
	this.jsonRequests = { };
	this.options = [];
	this.drawers = { };
	this.drawerIds = [ ];
	this.LastAutosetMapType = G_PHYSICAL_MAP;
	this.mapDivId = null;
	return this;
};
BNMap.prototype.CacheMarker = function(zoomLevel,id,marker) {
	if( this.cachedMarkers==null )
		this.cachedMarkers = [];
	if( this.cachedMarkers[zoomLevel] == null )
		this.cachedMarkers[zoomLevel] = [];
	if( this.cachedMarkers[zoomLevel][id] == null )
		this.cachedMarkers[zoomLevel][id] = marker;
};
BNMap.prototype.GetCachedMarker = function(zoomLevel,id) {
	if( this.cachedMarkers==null )
		return null;
	if( this.cachedMarkers[zoomLevel] == null )
		return null;
	return this.cachedMarkers[zoomLevel][id]; // may be null if not cached
};
BNMap.prototype.RemoveFilteredMarkers = function(markerFilterFunc) {
	if (this.cachedMarkers) {
		for(var i=0;i<30;i++) {
			if( this.cachedMarkers[i] ) {
				for( var id in this.cachedMarkers[i] ) {
					var m = this.cachedMarkers[i][id];
					if( m && markerFilterFunc(m) ) {
						this.markerMgr.removeMarker(m);
						this.cachedMarkers[i][id] = null;
					}
				}
			}
		}
	}
}
BNMap.prototype.RefreshData = function(bClearAllMarkers) {
	this.onMapLocationChanged(bClearAllMarkers);
}
BNMap.prototype.onMapLocationChanged = function(optClearAllMarkersOnComplete) {
	optClearAllMarkersOnComplete = optClearAllMarkersOnComplete || false;
	var bounds = this.map.getBounds();
	var zoom = this.map.getZoom();		
	BNDebugLog("MapLocationChanged: " + bounds + " : " + zoom);
	
	// let drawers handle the potential zoom change first, it may have an impact on the parameters used in the json request below.
	if( this.prevZoomLevel!=zoom ) {			
		for(var i=0;i<this.drawerIds.length;i++) {
			this.drawers[this.drawerIds[i]].onMapZoomChanged(zoom);
		}
		
		// if the map is stil in the last automatically-set map type (user didn't interactively switch to something else)
		// "help the user by automatically setting the map to standard type when zoom>=13 and back to terrain when zoom<=12
		var currMapType = this.map.getCurrentMapType();
		if( currMapType == this.LastAutosetMapType ) {
			if( zoom>=13 ) {
				if(currMapType!=G_NORMAL_MAP) {
					this.map.setMapType(G_NORMAL_MAP);
					this.LastAutosetMapType = G_NORMAL_MAP;
				}
			}
			else {
				if(currMapType!=G_PHYSICAL_MAP) {
					this.map.setMapType(G_PHYSICAL_MAP);
					this.LastAutosetMapType = G_PHYSICAL_MAP;
				}	
			}
		}
	}
	
	var jsonParam = { 
		bounds: { NELat: bounds.getNorthEast().lat(), NELng: bounds.getNorthEast().lng(), SWLat: bounds.getSouthWest().lat(), SWLng: bounds.getSouthWest().lng() },
		zoom: zoom,
		search: this.drawers.PropertySearch.searchParameters,
		layers: []
	};
	for(var i=0;i<this.drawerIds.length;i++) {
		jsonParam.layers.merge(this.drawers[this.drawerIds[i]].getDrawerEnabledLayers());
	}
	
	var _bnmap = this; // create enclosure
	if( this.jsonRequests.MapLocationChangedRequest!=null ) {
		this.jsonRequests.MapLocationChangedRequest.cancel();
		if(this.jsonRequests.MapLocationChangedRequest.statusIndicator) {
			this.jsonRequests.MapLocationChangedRequest.statusIndicator.remove();
			this.jsonRequests.MapLocationChangedRequest.statusIndicator=null;
		}
	}
	
	var statusElem = new Element('img', { src: this.options.waitIcon, width: this.options.waitIconsize.width, height: this.options.waitIconsize.height });
	statusElem.injectInside($("map_status"));	
	this.jsonRequests.MapLocationChangedRequest = new Json.Remote(this.options.baseUrl + "AdvSearchJSON.aspx?Cmd=GetMarkers", {
	 onComplete: function(response){
	 	if(this.statusIndicator) { this.statusIndicator.remove(); this.statusIndicator=null; }
	 	BNDebugLog("COMPLETE");	 		 	
	 	//BNDebugLog(odump(response));	 	
		
		if( optClearAllMarkersOnComplete ) {
			_bnmap.markerMgr.clearMarkers();
			_bnmap.cachedMarkers = [];
		}

		_bnmap.drawers["FilmStrip"].resetMarkers();

	 	for(var i=0;i<response.markers.length;i++) {
	 		if( _bnmap.GetCachedMarker(zoom,response.markers[i].uid)==null ) {
	 			if( response.markers[i].type.substring(0,5)=="Prop:" ) {	 				
	 				var propIconOptions = null;
					var typeLwr = response.markers[i].type.substring(5).toLowerCase();
					if(typeLwr=="residentialproperty")
						propIconOptions = _bnmap.options.propertyIcons.Residential || _bnmap.options.propertyIcons.Default;
					else if(typeLwr=="commoninterest")
						propIconOptions = _bnmap.options.propertyIcons.Condo || _bnmap.options.propertyIcons.Default;
					else if(typeLwr=="lotsandland")
						propIconOptions = _bnmap.options.propertyIcons.Lot || _bnmap.options.propertyIcons.Default;
					else if(typeLwr.indexOf("rental") >=0 )
						propIconOptions = _bnmap.options.propertyIcons.Rental || _bnmap.options.propertyIcons.Default;
					else
						propIconOptions = _bnmap.options.propertyIcons.Default;					
					
					if( propIconOptions.cachedGIcon == null ) {
						propIconOptions.cachedGIcon = new google.maps.Icon();
						propIconOptions.cachedGIcon.image = propIconOptions.main;
						propIconOptions.cachedGIcon.transparent = propIconOptions.transparent;
						propIconOptions.cachedGIcon.shadow = propIconOptions.shadow;
						propIconOptions.cachedGIcon.iconSize = new google.maps.Size(propIconOptions.width,propIconOptions.height);
						propIconOptions.cachedGIcon.shadowSize = new google.maps.Size(propIconOptions.width,propIconOptions.height);
						propIconOptions.cachedGIcon.iconAnchor = new google.maps.Point(propIconOptions.anchorX,propIconOptions.anchorY);
						propIconOptions.cachedGIcon.infoWindowAnchor = new google.maps.Point(propIconOptions.infoWindowAnchorX,propIconOptions.infoWindowAnchorY);	
					}
					
					var m = new google.maps.Marker(		 		
			 			new google.maps.LatLng(response.markers[i].lat,response.markers[i].lng),
			 			{ 
			 				title: response.markers[i].title,
							icon: propIconOptions.cachedGIcon,
							zIndexProcess: function(mrkr,b) 
								{
        							return google.maps.Overlay.getZIndex(mrkr.getPoint().lat()) + 1000000;
      							}
			 		  	} 
			 		);
					m.BN_standardIconImage = propIconOptions.main;
					m.BN_selectedIconImage = propIconOptions.selected;					
			 		m.BN_UID = response.markers[i].uid;
			 		m.BN_TYPE = response.markers[i].type;
			 		google.maps.Event.addListener(m,'click',function() { _bnmap.DisplayMarkerInfo(this); });
					google.maps.Event.addListener(m, "infowindowclose", function() {this.BN_SelectImage('standard'); this.BN_InfoWindowShowing = false;} );
			 		_bnmap.markerMgr.addMarker(m,zoom,zoom);
			 		_bnmap.CacheMarker(zoom,response.markers[i].uid,m);
					
	 			}
				else if( response.markers[i].type=="Office" ) {
					if( _bnmap.drawers["Offices"] ) {
						var m = new google.maps.Marker(		 		
							new google.maps.LatLng(response.markers[i].lat,response.markers[i].lng),
							{ 
								title: response.markers[i].title,
								icon: _bnmap.drawers["Offices"].getIcon(),
								zIndexProcess: function(mrkr,b) 
									{
										return google.maps.Overlay.getZIndex(mrkr.getPoint().lat()) + 10000000;
									}
							} 
						);
						m.BN_UID = response.markers[i].uid;
						m.BN_TYPE = response.markers[i].type;
						m.BN_INFODIV = new Element('div');
						m.BN_INFODIV.innerHTML = '<strong>' + response.markers[i].title.replace(/[\r\n]+/,'</strong><br />').replace(/[\r\n]+/g,"<br />");					
						var oid = m.BN_UID.split('$')[1];
						m.BN_INFODIV.innerHTML  += "<br /><br /><a href='officeDetail.aspx?lid="+ escape(oid)+ "&ps=500&pg=0'>More details</a>";
						google.maps.Event.addListener(m,'click',function() { _bnmap.DisplayMarkerInfo(this); });
						_bnmap.markerMgr.addMarker(m,zoom,zoom);
						_bnmap.CacheMarker(zoom,response.markers[i].uid,m);
					}
				}
				else if( response.markers[i].type=="School" ) {
					var m = new google.maps.Marker(		 		
			 			new google.maps.LatLng(response.markers[i].lat,response.markers[i].lng),
			 			{ 
			 				title: response.markers[i].title,
			 				icon: _bnmap.drawers["Schools"].getIcon(),
							zIndexProcess: function(mrkr,b) 
								{
        							return google.maps.Overlay.getZIndex(mrkr.getPoint().lat()) + 900000;
      							}
				 		} 
			 		);
			 		m.BN_UID = response.markers[i].uid;
			 		m.BN_TYPE = response.markers[i].type;					
					
					google.maps.Event.addListener(m,'click',function() { var schoolId = this.BN_UID.split("$")[1]; _bnmap.iBoxOpenFrame(_bnmap.options.baseUrl + "AdvSchool.aspx?uid=" + escape(schoolId), "School Information", {width: 700, height:600 }); return false;});
			 		_bnmap.markerMgr.addMarker(m,zoom,zoom);
			 		_bnmap.CacheMarker(zoom,response.markers[i].uid,m);
				}
	 			else {
		 			var icon = new google.maps.Icon();
		 			icon.image = _bnmap.options.baseUrl + 'MapIcon.aspx/default/' + escape(response.markers[i].uid.replace(/\|/g,'/')) + '/icon.png' ;
					icon.transparent = _bnmap.options.baseUrl + 'Images/MapIcons/Transparent.png';
					icon.shadow = _bnmap.options.baseUrl + 'Images/MapIcons/Shadow.png';
		 			//var maxDim = (response.markers[i].type=='Region' ? 200 : (response.markers[i].type=='AreaGroup' ? 170 : 140) );
					var maxDim = 100;
					if(response.markers[i].type=='Region') {
						maxDim = zoom < 9 ? 80 : 110;
					}
					else if (response.markers[i].type=='AreaGroup') {
						maxDim = zoom < 11 ? 75 : 105;	
					}
					else {
						maxDim = zoom < 11 ? 70 : 90;
					}		
					var minDim = 60
					/*var iconW = 0.25 *_bnmap.map.getSize().width * response.markers[i].dx / (bounds.getNorthEast().lng()-bounds.getSouthWest().lng()); 
		 			var iconH = 0.25 *_bnmap.map.getSize().height * response.markers[i].dy / (bounds.getNorthEast().lat()-bounds.getSouthWest().lat()); 		 			
		 			iconH = iconW = Math.max(40,Math.min(maxDim,Math.max(iconW,iconH)));		 			
					*/
					var iconSz = minDim + response.markers[i].sz * (maxDim-minDim); //sz is [0-1] ratio
					var iconW, iconH;
					iconW=iconH=iconSz;
					icon.iconSize = new google.maps.Size(iconW,iconH);					
					icon.shadowSize = new google.maps.Size(iconW *1.1,iconH *1.1);
					icon.iconAnchor = new google.maps.Point(iconW/2,iconH/2);
					icon.infoWindowAnchor = new google.maps.Point(iconW/2,iconH/2);
			 		var m = new google.maps.Marker(		 		
			 			new google.maps.LatLng(response.markers[i].lat,response.markers[i].lng),
			 			{ 
			 				title: response.markers[i].title,
			 				icon: icon,
							clickable: (_bnmap.options.communityInfoEnabled ? true:false)
				 		} 
			 		);
			 		m.BN_UID = response.markers[i].uid;
			 		m.BN_TYPE = response.markers[i].type;
			 		if( _bnmap.options.communityInfoEnabled ) {
						google.maps.Event.addListener(m,'click',function() { _bnmap.DisplayMarkerInfo(this); });
					}
			 		_bnmap.markerMgr.addMarker(m,zoom,zoom);
			 		_bnmap.CacheMarker(zoom,response.markers[i].uid,m);
			 	}
		 	}
			
			if( response.markers[i].type.substring(0,5)=="Prop:" ) {
				// needs to be re-done for cached markers too
				_bnmap.drawers["FilmStrip"].addMarker(response.markers[i]);
			}
	 	}
		_bnmap.drawers["FilmStrip"].validateDisplay();
	 },
	 onFailure: function(response){
	 	if(this.statusIndicator) { this.statusIndicator.remove(); this.statusIndicator=null; }
	 	BNDebugLog("FAILED:");	
	 	BNDebugLog(response.statusText);
	 	BNDebugShowHTML(response.responseText);
	 }
	});
	this.jsonRequests.MapLocationChangedRequest.statusIndicator = statusElem;
	this.jsonRequests.MapLocationChangedRequest.send(jsonParam);
};
BNMap.prototype.onResize = function() {
	var map = this;
	
	this.RefreshData(false);		
}
BNMap.prototype.DisplayMarkerInfo = function(marker) {		
	marker.BN_SelectImage("selected");	
	if( marker.BN_INFODIV ) {
		marker.openInfoWindow(marker.BN_INFODIV);
		marker.BN_InfoWindowShowing = true;
	}
	else {
		marker.BN_INFODIV = new Element('div');
		marker.BN_INFODIV.innerHTML = "<center><img src='"+ this.options.waitIcon+"' /></center>";
		marker.openInfoWindow(marker.BN_INFODIV);
		marker.BN_InfoWindowShowing = true;
		var _bnmap = this;
		var infoReq = new Json.Remote(this.options.baseUrl + "AdvSearchJSON.aspx?Cmd=GetMarkerInfo", {
			onComplete: function(response) {
					marker.closeInfoWindow();				
					marker.BN_INFODIV.innerHTML = response;						
					if( marker.BN_TYPE.indexOf("Prop:")==0 ) {
						var d = new Element('div');
						var l = new Element('a', { 
							'href': '#',
							'events': {
								'click': function() {
									var latlng = marker.getLatLng();
									//alert('open street view:' + latlng.lat() + ',' + latlng.lng());
									var myPano = new GStreetviewPanorama($('mapDrawer_StreetView_canvas'), {'latlng': latlng } );
									google.maps.Event.addListener(myPano, "error", function(errorCode) {
										  if (errorCode == FLASH_UNAVAILABLE) {
											alert("Error: Flash doesn't appear to be supported by your browser");
											return;
										  }
									});
									$('mapDrawer_StreetView').setStyle('display','block');
									return false;
								}
							}
						});
						l.setText('Street view');
						d.adopt(l);
						d.adopt(new Element('br'));
						var lz = new Element('a', { 
							'href': '#',
							'events': {
								'click': function() {
									var latlng = marker.getLatLng();
									_bnmap.map.setCenter(latlng);
									_bnmap.map.setZoom(19);
									return false;
								}
							}
						});
						lz.setText('Zoom in');
						/*(new Element('img'), {
							src: _bnmap.options.imageResources.zoomInIcon,
							border: 0,
							valign: 'absmiddle'
						}).injectTop(lz);*/						
						d.adopt(lz);
						$(marker.BN_INFODIV).adopt(d);
					}
					// hook up ibox links to a elements with class MapIboxLink
					$(marker.BN_INFODIV).getElements(".MapIboxLink").each(function(linkEl) {
						linkEl.addEvent('click', function() {
							_bnmap.iBoxOpenFrame(linkEl.href, linkEl.title, {width: 850, height:700 });
							return false;								  
						});
					});
					
					// need to wait for any/all images to be loaded before we open the info window
					var _marker = marker;
					marker.BN_INFODIV.check_state_and_open = function() {
						var waitImages = $(_marker.BN_INFODIV).getElements('img');
						var loaded = true;
						for(var i=0;waitImages!=null && i<waitImages.length;i++) {
							if (waitImages[i].naturalHeight == 0 || waitImages[i].naturalWidth == 0 || waitImages[i].complete == false) {      
								loaded = false;
								break;
							}
						}
						if( loaded ) {
							_marker.openInfoWindow(_marker.BN_INFODIV); // reopen to set size properly
							_marker.BN_InfoWindowShowing = true;
							_marker.BN_SelectImage("selected");
						}
						else {
							_marker.BN_INFODIV.check_state_and_open.delay(300);	
						}
					}
					
					marker.BN_INFODIV.check_state_and_open();					
			},
			onFailure: function(response) {				 	
				BNDebugLog("FAILED:");	
				BNDebugLog(response.statusText);
				BNDebugShowHTML(response.responseText);
				marker.BN_INFODIV.innerHTML = "An error occurred";
		  }
		});
		infoReq.send({ 'type': marker.BN_TYPE, 'uid': marker.BN_UID });
	}
};
BNMap.prototype.RegisterDrawer = function(drawer) {
	drawer._bnmap = this;
	this.drawerIds.push(drawer.id);
	this.drawers[drawer.id] = drawer;
};
BNMap.prototype.iBoxOpenFrame = function(url, title, param) {
    if(param==null)
    	param={height:500, width:900};
    var htm = "<iframe frameborder=\"0\" id=\"tmFrame\" width=\"99%\" height=\"97%\" src=\""+url+"\"></iframe>"
    iBox.show(htm, title, param);	
}

// Map Drawers
var BNMapDrawer = new Class({
	initialize: function(id, elem, title, options) {
		this._bnmap = null;
		this.id = id;
		this.elem = elem;
		this.title = title;
		this.options = options;
	},
	onMapZoomChanged: Class.empty,	
	getDrawerEnabledLayers: function() { return []; },
	initHelp: function() {
		if( this.options!=null && this.options.callout!=null ) {
			var trgImg = $(this.getDrawerElemId(this.options.callout.triggerImgId));
			var contentDivId = this.getDrawerElemId(this.options.callout.contentDivId);
			var templateDivId = this.getDrawerElemId(this.options.callout.templateDivId);
			var handlePosition = this.options.callout.handlePosition;
			var title = this.options.callout.title;
			var _bnmap = this._bnmap;
			var _drawer = this;
			trgImg.ShowCallout = function() {
				var calloutDivId = _bnmap.mapDivId + '__' + contentDivId + "__calloutDiv";
				var mapDiv = $(_bnmap.mapDivId);
				var d = new Element('div', {
					id: calloutDivId,
					styles: {
						position: 'absolute',
						top: '0px',
						left: '0px',
						color: 'black',
						opacity: 0,
						'z-index': 2147483647
					},
					className: 'AdvMapCalloutDiv'
				});
				d.closeCallout = function() {
					d.effect('opacity', {
						duration: 100,
						transition: Fx.Transitions.linear,
						wait: false,
						onComplete: function() {
							d.remove();
						}
					}).start(1,0);
					_drawer.ACTIVE_CALLOUT_DIV = null;
				}
				d.setHTML($(templateDivId).innerHTML);
				
				d.getElement('*[class=AdvMapCalloutTitle]').setText(title);		
				d.getElement('*[class=AdvMapCalloutContent]').setHTML($(contentDivId).innerHTML);
				d.getElement('*[id=mapTemplate_callout_handleCell]').setStyle('verticalAlign',handlePosition); 
				
				var oldCalloutDiv = $(document.body).getElement('div[id='+calloutDivId+']')
				if( oldCalloutDiv ) {
					oldCalloutDiv.closeCallout();	
				}				
				d.injectInside($(document.body));

				var handleImgCoords = d.getElement('img[id=mapTemplate_callout_handleImg]').getCoordinates();				
				var trgImageCoords = trgImg.getCoordinates();
				var topPos = trgImageCoords.top + Math.ceil(trgImageCoords.height/2) - (handleImgCoords.top + Math.ceil(handleImgCoords.height/2));
				var leftPos = trgImageCoords.left + trgImageCoords.width;
				d.setStyles({ top: topPos + 'px', left: leftPos + 'px'});
				_drawer.ACTIVE_CALLOUT_DIV = d;
				d.effect('opacity', {duration: 100,transition: Fx.Transitions.linear, wait: false}).start(0,1);				
			};
			trgImg.addEvent('click',trgImg.ShowCallout);
			if( this.options.callout.autoShow == true ) {
				var cookieName = "callout_"+contentDivId+"_autoshown";
				if( Cookie.get(cookieName) == false ) {
					window.setTimeout(trgImg.ShowCallout,1000);
					Cookie.set(cookieName,"true"); // session cookie
				}
			}
		}
	},
	getDrawerElemId: function(elemId) {
		return elemId.replace("~",this.elem.id);
	}
});


var BNMapPropertySearchDrawer = BNMapDrawer.extend({
    initialize: function(id, elem, title, options) {
		this.parent(id, elem, title, options);
		this.searchParameters = {
			PropertyTypes: [ 'RES' ],
			Price: {'min': -1, 'max': -1},
			Bed: {'min': -1, 'max': -1},
			Bath: {'min': -1, 'max': -1}
		}
	},
	onMapZoomChanged: function(zoom) {
		if(this.ACTIVE_CALLOUT_DIV && this.ACTIVE_CALLOUT_DIV.closeCallout)
			this.ACTIVE_CALLOUT_DIV.closeCallout();
			
		if( zoom < 13 ) {
			if( this.elem.style.display== 'block' )
				this.elem.style.display='none';
		}
		else { // if( zoom >= 14 ) 
			this.elem.style.display = 'block';
			if( !this.UIInitialized ) {
				var baseElemId = this.elem.id;	
				this.initHelp();
				var searchDrawer = this; // create enclosure for event handlers
				// assign type cb event handlers				
				$(baseElemId + '_cbRES').addEvent('click',function() { searchDrawer.setSearchPropertyType('RES', $(baseElemId + '_cbRES').checked); });
				$(baseElemId + '_cbCOND').addEvent('click',function() { searchDrawer.setSearchPropertyType('COND', $(baseElemId + '_cbCOND').checked); });
				$(baseElemId + '_cbLAND').addEvent('click',function() { searchDrawer.setSearchPropertyType('LAND', $(baseElemId + '_cbLAND').checked); });
				
				// beds
				this.BedSlider = new RangeSlider( 
					$(baseElemId + '_BedSlider_Container'),
					$(baseElemId + '_BedSlider_MinKnob'),
					{
						mode: 'horizontal',
						offset: 0,
						start: 0,
						end: 8,
						steps: 8,
						onChange: function(value) {
							var lbl = '';
							if( value.minpos==this.options.start && value.maxpos==this.options.end )
								lbl = "Any";
							else if( value.minpos==this.options.start )
								lbl = value.maxpos + " or fewer";
							else if( value.maxpos==this.options.end )
								lbl = value.minpos + " or more";
							else
								lbl = value.minpos + ' to ' + value.maxpos;
								
							$(baseElemId +"_BedLabel").innerHTML = lbl;
						},
						onComplete: function(value) {				
							searchDrawer.setSearchSliderVal('Bed', value.minpos==this.options.start ? -1 : value.minpos, value.maxpos==this.options.end ? -1 : value.maxpos);
							BNDebugLog('bed slider update: ' +  value.minpos + ',' + value.maxpos);
						}
					},
					$(baseElemId + '_BedSlider_MaxKnob')
				);
				this.BedSlider.setMin(0);
				this.BedSlider.setMax(9);
				
				// baths
				this.BathSlider = new RangeSlider( 
					$(baseElemId + '_BathSlider_Container'),
					$(baseElemId + '_BathSlider_MinKnob'),
					{
						mode: 'horizontal',
						offset: 0,
						start: 0,
						end: 8,
						steps: 8,
						onChange: function(value) {
							var lbl = '';
							if( value.minpos==this.options.start && value.maxpos==this.options.end )
								lbl = "Any";
							else if( value.minpos==this.options.start )
								lbl = value.maxpos + " or fewer";
							else if( value.maxpos==this.options.end )
								lbl = value.minpos + " or more";
							else
								lbl = value.minpos + ' to ' + value.maxpos;
								
							$(baseElemId +"_BathLabel").innerHTML = lbl;
						},
						onComplete: function(value) {	
							searchDrawer.setSearchSliderVal('Bath', value.minpos==this.options.start ? -1 : value.minpos, value.maxpos==this.options.end ? -1 : value.maxpos);
							//BNDebugLog('bath slider update: ' +  value.minpos + ',' + value.maxpos);
						}
					},
					$(baseElemId + '_BathSlider_MaxKnob')
				);
				this.BathSlider.setMin(0);
				this.BathSlider.setMax(9);
				
				// price
				this.PriceSlider = new RangeSlider( 
					$(baseElemId + '_PriceSlider_Container'),
					$(baseElemId + '_PriceSlider_MinKnob'),
					{
						mode: 'horizontal',
						offset: 0,
						start: 0,
						end: 32,
						steps: 32,
						stepValueMap: [0,140000,150000,160000,170000,180000,190000,
									   200000,225000,250000,275000,300000,325000,350000,375000,400000,450000,500000,
									   550000,600000,650000,700000,750000,800000,850000,900000,1000000,1500000,
									   2000000,3000000,4000000,5000000,10000000],
						valueToDisplayFormat: function(num) {
							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 + (cents>0 ? '.' + cents : ''));
						},
						onChange: function(value) {
							var lbl = '';
							if( value.minpos==this.options.start && value.maxpos==this.options.end )
								lbl = "Any";
							else if( value.minpos==this.options.start )
								lbl = this.options.valueToDisplayFormat(this.options.stepValueMap[value.maxpos]) + " or less";
							else if( value.maxpos==this.options.end )
								lbl = this.options.valueToDisplayFormat(this.options.stepValueMap[value.minpos]) + " or more";
							else
								lbl = this.options.valueToDisplayFormat(this.options.stepValueMap[value.minpos]) + ' to ' + this.options.valueToDisplayFormat(this.options.stepValueMap[value.maxpos]);
								
							$(baseElemId +"_PriceLabel").innerHTML = lbl;
						},
						onComplete: function(value) {	
							searchDrawer.setSearchSliderVal('Price', value.minpos==this.options.start ? -1 : this.options.stepValueMap[value.minpos], value.maxpos==this.options.end ? -1 : this.options.stepValueMap[value.maxpos]);						
							//BNDebugLog('price slider update: ' +  value.minpos + ',' + value.maxpos);
						}
					},
					$(baseElemId + '_PriceSlider_MaxKnob')
				);
				this.PriceSlider.setMin(0);
				this.PriceSlider.setMax(36);
				
				
				this.UIInitialized = true;
			}
		}		
	},
	setSearchSliderVal: function(sliderName,minval,maxval) {
		if( this.searchParameters[sliderName].min!=minval || this.searchParameters[sliderName].max!=maxval ) {
			this.searchParameters[sliderName] = {'min': minval, 'max': maxval};
			if(this._bnmap)
				this._bnmap.RefreshData(true);
		}
	},
	setSearchPropertyType: function(typeName,bChecked) {
		if( bChecked ) {
			if( this.searchParameters.PropertyTypes.indexOf(typeName) < 0 ) {
				this.searchParameters.PropertyTypes.push(typeName);
				if(this._bnmap)
					this._bnmap.RefreshData(false); // no need to drop all markers because a class was ADDED
			}
			else {
				// nothing to do.	
			}
		}
		else {
			if( this.searchParameters.PropertyTypes.indexOf(typeName) < 0 ) {
				// nothing to do
			}
			else {
				this.searchParameters.PropertyTypes.remove(typeName);
				if(this._bnmap)
					this._bnmap.RefreshData(true); // need to drop all markers because a class was removed
			}
		}
	}
});

var BNMapLegendDrawer = BNMapDrawer.extend({
	onMapZoomChanged: function(zoom) {
		if(this.ACTIVE_CALLOUT_DIV && this.ACTIVE_CALLOUT_DIV.closeCallout)
			this.ACTIVE_CALLOUT_DIV.closeCallout();
		if( !this.UIInitialized ) {
			this.initHelp();			
			this.UIInitialized = true;
		}
	}
});

var BNMapQuickfindDrawer = BNMapDrawer.extend({
	onMapZoomChanged: function(zoom) {
		if(this.ACTIVE_CALLOUT_DIV && this.ACTIVE_CALLOUT_DIV.closeCallout)
			this.ACTIVE_CALLOUT_DIV.closeCallout();
		if( zoom >= 13 ) {
			if( this.elem.style.display== 'block' )
				this.elem.style.display='none';
		}
		else { // if( zoom < 13 ) 
			this.elem.style.display = 'block';
			if( !this.UIInitialized ) {
				this.initHelp();
				var baseElemId = this.elem.id;								
				var findDrawer = this; // create enclosure for event handlers
				// assign type cb event handlers				
				$(baseElemId + '_btn').addEvent('click',function() { findDrawer.doFind($(baseElemId + '_arg').value); });	
				$(baseElemId + '_arg').addEvent('keydown', function(event){
					event = new Event(event);
					if (event.key == 'enter') {
						findDrawer.doFind($(baseElemId + '_arg').value);
					}
				});
				this.UIInitialized = true;
			}
		}		
	},
	doFind: function(s) {
		if( s.trim()=="" ) {
			alert("Please enter a city name, neighborhood name or zip code");	
		}
		else {
			var _bnmap = this._bnmap; // enclosure
			var infoReq = new Json.Remote(this._bnmap.options.baseUrl + "AdvSearchJSON.aspx?Cmd=QuickFind", {
				onComplete: function(response) {
					if( response==null || response.latitude == 0 && response.longitude == 0 ) {
						alert("Location not found, please try a different search");
					}
					else {
						//alert(response.latitude + ',' + response.longitude);
						_bnmap.map.setCenter(new google.maps.LatLng(response.latitude, response.longitude),14);
					}
				},
				onFailure: function(response) {				 	
					BNDebugLog("FAILED:");	
					BNDebugLog(response.statusText);
					BNDebugShowHTML(response.responseText);
				 	alert("An error occurred");
			  }
			});
			infoReq.send(s);
		}
	}
});


var BNMapFilmStripDrawer = BNMapDrawer.extend({
    initialize: function(id, elem, title, options) {
		this.parent(id, elem, title, options);
		this.stripTableRowElem = $E('tr', elem);
		this.resetMarkers();
	},											  
	onMapZoomChanged: function(zoom) {
		this.validateDisplay(zoom);
	},	
	validateDisplay: function(zoom) {
		zoom = zoom || this._bnmap.map.getZoom();	
		if( zoom < 13 || this.stripMarkers==null || this.stripMarkers.length==0) {
			if( this.elem.style.display== 'block' )
				this.elem.style.display='none';
		}
		else { // if( zoom >= 13 ) 
			this.elem.style.display = 'block';
			var baseElemId = this.elem.id;	
			if( !this.UIInitialized ) {											
				var findDrawer = this; // create enclosure for event handlers
				var baseElem = this.elem;
				baseElem.setStyle('height','40px');				
				$(baseElemId + "_bundle").addEvent('mouseenter', function() {
					baseElem.setStyle('height','300px');	// room for fisheye effect																		
					new Fx.Style(baseElemId + '_bundle', 'bottom', {duration:300}).start(-12, 0);
				});
				$("mapDrawer_FilmStrip_bundle").addEvent('mouseleave', function() {
					baseElem.setStyle('height','40px');																
					new Fx.Style(baseElemId + '_bundle', 'bottom', {duration:300}).start(0, -12);
				});
				var filmstripDrawer = this; // enclosure;
				this.sliderPos = 0;
				this.filmStripSlider = new Slider($(baseElemId + '_slider'),$(baseElemId + '_slider_knob'),
					{
						steps: 100,
						mode: 'horizontal',
						onChange: function(value) {
							var pos = Math.round(filmstripDrawer.stripMarkers.length * value / 100);
							var lbl = '';
							if( filmstripDrawer.stripMarkers.length > 0 && pos < filmstripDrawer.stripMarkers.length ) {
								lbl = filmstripDrawer.stripMarkers[pos].title;
							}							
							$(baseElemId +"_slider_knob").title = lbl;
						},
						onComplete: function(value) {	
							var pos = Math.round(filmstripDrawer.stripMarkers.length * value / 100);
							filmstripDrawer.sliderPos = pos;
							filmstripDrawer.markersValidated = false;
							if( filmstripDrawer.UIInitialized )
								filmstripDrawer.validateDisplay(zoom);							
						}
					});
				this.filmStripSlider.set(0);				
				
				/*$E('table', this.elem).makeDraggable();{
					container: 	$(baseElemId + '_strip'),
					overflown: [ this.elem ]
				});*/
				
				this.UIInitialized = true;
			}
			
			if( !this.markersValidated ) 
			{
				if( this.stripTableRowElem ) 
				{
					var tab = $E('table', this.elem);
					if( this.lastFishEyeObj ) 
						this.lastFishEyeObj.detachEvents();
						
					tab.deleteRow(0);
					tab.insertRow(0);
					this.stripTableRowElem = $E('tr', this.elem); //	remove all elements in the table row.				
					if( this.filmStripSlider ) {												
						var winSize = 15;
						var start = Math.max(0,Math.min(this.sliderPos,this.stripMarkers.length-winSize));
						var end = Math.min(start + winSize,this.stripMarkers.length);
						for(var i=start;i<end;i++) {
							var cell = this.stripTableRowElem.insertCell(i-start);
							var marker = this.stripMarkers[i];
							cell.innerHTML = '<div class="iFishEyeCaption">' +marker.title+ '<br />'+ marker.subtitle +'</div><img class="iFishEyeImg" src="' + marker.img + '" />';
							var eCell = $(cell);
							eCell.BN_MARKER = this._bnmap.GetCachedMarker(this._bnmap.map.getZoom(), marker.uid);
							eCell.addEvent('mouseenter', function() { this.BN_MARKER.BN_SelectImage('selected'); });
							eCell.addEvent('mouseleave', function() { if(!this.BN_MARKER.BN_InfoWindowShowing) this.BN_MARKER.BN_SelectImage('standard'); });
							var _bnmap = this._bnmap;
							eCell.addEvent('click', function() { _bnmap.DisplayMarkerInfo(this.BN_MARKER); });
						}
						
						this.lastFishEyeObj = new iFishEye({   
							container: $(baseElemId + "_strip"), 
							dimThumb: this.options.thumbSize,
							dimFocus: this.options.focusSize,  
							eyeRadius: Math.round(this.options.focusSize.width * 0.8), 
							pupilRadius: Math.round(this.options.focusSize.width * 0.4),  
							norm: "L2"  
						});   						
					}
				}
			}
			
			this.markersValidated = true;
		}
	},		
	resetMarkers: function() {
		this.stripMarkers = [];	
		this.markersValidated = false;
	},
	addMarker: function(marker) {
		this.stripMarkers.push(marker);		
		this.markersValidated = false;
	}		
});

var BNMapOfficesDrawer = BNMapDrawer.extend({					  
	initialize: function(id, elem, title, options) {
		this.parent(id, elem, title, options);
		var drawer = this; // enclosure
		var baseElemId = this.elem.id;
		$(baseElemId + '_cbShow').addEvent('click', function() {
			if( $(baseElemId + '_cbShow').checked==false ) {				
				drawer._bnmap.RemoveFilteredMarkers(function(mrkr) { return (mrkr.BN_TYPE=='Office'); });
			}
			else {
				drawer._bnmap.RefreshData( false );										 
			}
		});
	},							
	getDrawerEnabledLayers: function() {
		var baseElemId = this.elem.id;
		if($(baseElemId + '_cbShow').checked) {
			return ['Offices'];	
		}
		return [];
	},
	getIcon: function() {
		if(!this.icon)
		{
			this.icon = new google.maps.Icon();
			
			this.icon.image = this.options.officeIcon.main;
			this.icon.transparent = this.options.officeIcon.transparent;
			this.icon.shadow = this.options.officeIcon.shadow;
			this.icon.iconSize = new google.maps.Size(this.options.officeIcon.width,this.options.officeIcon.height);
			this.icon.shadowSize = new google.maps.Size(this.options.officeIcon.width,this.options.officeIcon.height);
			this.icon.iconAnchor = new google.maps.Point(this.options.officeIcon.anchorX,this.options.officeIcon.anchorY);
			this.icon.infoWindowAnchor = new google.maps.Point(this.options.officeIcon.infoWindowAnchorX,this.options.officeIcon.infoWindowAnchorY);
		}
		return this.icon;
	}
});

var BNMapSchoolsDrawer = BNMapDrawer.extend({					  
	initialize: function(id, elem, title, options) {
		this.parent(id, elem, title, options);
		var drawer = this; // enclosure
		var baseElemId = this.elem.id;
		$(baseElemId + '_cbShow').addEvent('click', function() {
			if( $(baseElemId + '_cbShow').checked==false ) {				
				drawer._bnmap.RemoveFilteredMarkers(function(mrkr) { return (mrkr.BN_TYPE=='School'); });
			}
			else {
				drawer._bnmap.RefreshData( false );										 
			}
		});
	},		
	onMapZoomChanged: function(zoom) {
		if( zoom < 13 ) {
			if( this.elem.style.display=='block' || this.elem.style.display=='' ) {
				this.elem.style.display='none';
			}
		}
		else { // if( zoom >= 16 ) 
			this.elem.style.display = 'block';
		}
	},	
	getDrawerEnabledLayers: function() {
		var baseElemId = this.elem.id;
		if($(baseElemId + '_cbShow').checked && this._bnmap.map.getZoom() >= 13) {
			return ['Schools'];	
		}
		return [];
	},
	getIcon: function() {
		if(!this.icon)
		{
			this.icon = new google.maps.Icon();
			
			this.icon.image = this.options.schoolIcon.main;
			this.icon.transparent = this.options.schoolIcon.transparent;
			this.icon.shadow = this.options.schoolIcon.shadow;
			this.icon.iconSize = new google.maps.Size(this.options.schoolIcon.width,this.options.schoolIcon.height);
			this.icon.shadowSize = new google.maps.Size(this.options.schoolIcon.width,this.options.schoolIcon.height);
			this.icon.iconAnchor = new google.maps.Point(this.options.schoolIcon.anchorX,this.options.schoolIcon.anchorY);
			this.icon.infoWindowAnchor = new google.maps.Point(this.options.schoolIcon.infoWindowAnchorX,this.options.schoolIcon.infoWindowAnchorY);
		}
		return this.icon;
	}
});


var BNMapParcelLinesDrawer = BNMapDrawer.extend({					  
	onMapZoomChanged: function(zoom) {
		this.validateDisplay(zoom);
	},	
	validateDisplay: function(zoom) {
		zoom = zoom || this._bnmap.map.getZoom();	
		if( zoom < 16 ) {
			if( this.elem.style.display=='block' || this.elem.style.display=='' ) {
				this.elem.style.display='none';
				this.setLinesVisible(false);
			}
		}
		else { // if( zoom >= 16 ) 
			this.elem.style.display = 'block';
			var baseElemId = this.elem.id;	
			if( !this.UIInitialized ) {											
				var drawer = this; // create enclosure for event handlers

				// add parcelStream parcels layer (optional)
				var layers = ["Parcels"];
				this.tileLayer = new TiledLayer(this._bnmap.map , layers);
				
				// Turn it on for zoom levels 16-20
				this.tileLayer.setMinZoomLevel(16);
				this.tileLayer.setMaxZoomLevel(20);
				this.tileLayer.initialize();
	
				$(baseElemId + '_cbShow').addEvent('click', function() {
					if( $(baseElemId + '_cbShow').checked==false ) {				
						drawer.setLinesVisible(false);
					}
					else {
						drawer.setLinesVisible(true);										 
					}
				});
				
				this.UIInitialized = true;
				this.setLinesVisible($(baseElemId + '_cbShow').checked);				
			}
		}
	},
	setLinesVisible: function(bVisible) {
		if( this.UIInitialized && this.tileLayer!=null ) {
			this.tileLayer.setVisible(bVisible);	
		}
	}
});


//var _m = null;
/* supported options:
	baseUrl: url
	waitIcon: url
	waitIconSize: {width: n, height: n}
*/
function BNInitializeAdvMap(mapCanvasId,options) {
	var _m = new BNMap();
	_m.mapDivId = mapCanvasId;
	var hMapDiv = document.getElementById(mapCanvasId)
	_m.map = new google.maps.Map2(hMapDiv); 	
	
	// default options
	options= options || [];
	options.baseUrl = options.baseUrl || "/";
	options.waitIcon = options.waitIcon || "/MapImages/waitAnim.gif";
	options.waitIconSize = options.waitIconSize || {width: 16, height: 16};
	
	_m.options = options;
	
	// setup map controls
	_m.map.addMapType(G_PHYSICAL_MAP); 	
	var typeCtl = new google.maps.MenuMapTypeControl();
	_m.map.addControl(typeCtl);		
  if( hMapDiv.offsetWidth > 250 ) {
		_m.map.addControl(new google.maps.OverviewMapControl());
		if( window.TrafficMapTypeControl ){
			_m.map.addControl(new TrafficMapTypeControl({showTraffic: true, showTrafficKey: true}), new google.maps.ControlPosition(G_ANCHOR_TOP_RIGHT, new google.maps.Size(87,7)) );
		}
	}	
	var bHasLargeMapControl = false;
	if( hMapDiv.offsetHeight > 250 ) {
			 _m.map.addControl(new google.maps.LargeMapControl());
			 bHasLargeMapControl = true;
	}
	else {
			_m.map.addControl(new google.maps.SmallMapControl());
	}		
	_m.map.enableDoubleClickZoom();
	
	// initialize map
	if( options.defaultBounds ) {
		var zoomLev = _m.map.getBoundsZoomLevel(new google.maps.LatLngBounds(
				new google.maps.LatLng(options.defaultBounds.southWest.lat,options.defaultBounds.southWest.lon), 
				new google.maps.LatLng(options.defaultBounds.northEast.lat,options.defaultBounds.northEast.lon)));		
		/*if( zoomLev > 11 )
			zoomLev = 11;*/ // cap at a reasonable level
		_m.map.setCenter(new google.maps.LatLng((options.defaultBounds.northEast.lat+options.defaultBounds.southWest.lat)/2, (options.defaultBounds.northEast.lon+options.defaultBounds.southWest.lon)/2), zoomLev);
	}
	else {
		_m.map.setCenter(new google.maps.LatLng(37.4419, -122.1419), 8);
	}
	_m.map.setMapType(G_PHYSICAL_MAP); 
	_m.LastAutosetMapType = G_PHYSICAL_MAP;
	BNAddDragZoom(_m.map,bHasLargeMapControl);

	_m.markerMgr = new MarkerManager(_m.map);		
	google.maps.Event.addListener(_m.map, "moveend", function() {_m.onMapLocationChanged();} );
	google.maps.Event.addListener(_m.map, "zoomend", function() {_m.onMapLocationChanged();} );

	var drawer = new BNMapPropertySearchDrawer("PropertySearch", $(options.searchDrawer.baseDivId), "Property Search", options.searchDrawer.options );
	_m.RegisterDrawer(drawer);
	drawer = new BNMapQuickfindDrawer("QuickFind", $(options.quickFindDrawer.baseDivId), "Quick Find", options.quickFindDrawer.options );
	_m.RegisterDrawer(drawer);
	drawer = new BNMapLegendDrawer("Legend", $(options.legendDrawer.baseDivId), "Legend", options.legendDrawer.options );
	_m.RegisterDrawer(drawer);
	drawer = new BNMapFilmStripDrawer("FilmStrip", $(options.filmStripDrawer.baseDivId), "Result Pictures", options.filmStripDrawer.options );
	_m.RegisterDrawer(drawer);
	if( options.officesDrawer ) {
		drawer = new BNMapOfficesDrawer("Offices", $(options.officesDrawer.baseDivId), "Offices", options.officesDrawer.options );
		_m.RegisterDrawer(drawer);
	}
	if( options.schoolsDrawer ) {
		drawer = new BNMapSchoolsDrawer("Schools", $(options.schoolsDrawer.baseDivId), "Schools", options.schoolsDrawer.options );
		_m.RegisterDrawer(drawer);
	}
	if( options.parcelLinesDrawer ) {
		drawer = new BNMapParcelLinesDrawer("Parcel Lines", $(options.parcelLinesDrawer.baseDivId), "Parcel Lines", options.parcelLinesDrawer.options );
		_m.RegisterDrawer(drawer);
	}

	// load initial locations
	_m.onMapLocationChanged();	
	window.addEvent('resize',function() {
		_m.onResize();							  						  
	});
}

function BNAddDragZoom(hMap,bHasLargeMapControl) {
	var styleOpts = {};
	var otherOpts = {};
	otherOpts.buttonHTML = 'Drag-Zoom';
	otherOpts.buttonZoomingHTML = 'Drag a region on the map to zoom';
	otherOpts.overlayRemoveTime = 1500;
	otherOpts.buttonStyle = {background: '#FFFFFF' };
	otherOpts.buttonStartingStyle = {width: '63px', border: '2px outset #CCCCCC', borderLeft: '1px solid black', borderTop: '1px solid black', padding: '1px 3px 1px 2px', fontFamily: 'tahoma', fontSize: '12px' };
	otherOpts.buttonZoomingStyle = {border: '1px dashed black', width: '95px', background: '#CCCCCC' };
	zcontrol = new DragZoomControl(styleOpts, otherOpts, {});
	hMap.addControl(zcontrol,new google.maps.ControlPosition(G_ANCHOR_TOP_LEFT,new GSize(2, bHasLargeMapControl ? 275 : 105 )));
}

/* prototype additions for google maps objects */
google.maps.Marker.prototype.BN_SelectImage = function(sImageType) {
	try {
		if( sImageType=="selected" && this.BN_selectedIconImage) {
			this.setImage(this.BN_selectedIconImage);	
		}
		else if( this.BN_standardIconImage ) {
			this.setImage(this.BN_standardIconImage);
		}
	}
	catch(ex1) {
	}
};




function BNDebugLog(s) {
	var output = $("output") 
	if( output )
		output.value += s + '\n';
}
function BNDebugShowHTML(s) {
	var output = $("debugDiv") 
	if( output )
		output.innerHTML = s;
}
function odump(object, depth, max){
  depth = depth || 0;
  max = max || 2;

  if (depth > max)
    return false;

  var indent = "";
  for (var i = 0; i < depth; i++)
    indent += "  ";

  var output = "";  
  for (var key in object){
    output += "\n" + indent + key + ": ";
    switch (typeof object[key]){
      case "object": output += odump(object[key], depth + 1, max); break;
      case "function": output += "function"; break;
      default: output += object[key]; break;        
    }
  }
  return output;
}
