/*!
 * ShowNearby Component Library v1.0
 * http://api.shownearby.com/
 * Copyright (c) 2009 ShowNearby Pte Ltd
 *
 * Tested browsers: IE 6, Firefox 3.5, Safari 4, Opera 9.6, Chrome 3.0
 * Untested browsers: IE 7, IE 8, Firefox 3
 * 
 * Known Bugs:
 * IE 6 		- marker.SetDescription(html); causes error when changing from 2D to 3D mode
 * Safari 4  	- Pushpin onclick causes the following error
 *		  		  Safari can't use JavaScript for this action.
 *		  		  Safari can't run the script "//pushin hover" because Safari doesn’t allow JavaScript to be used in this way.
 *				  Opera 9.6 onclick fails for microsoft map
 *				  Opera 9.6 clusterer icon click can't zoom in.
 * To Do:		- Directions Object
 *				- Location Detection Object
 */
(function() {

var Snb = window.Snb = {};
Snb.url = 'http://api.shownearby.com/api/1.0/';

// default stylesheet
var stylesheet = document.createElement('link');
stylesheet.type = 'text/css';
stylesheet.rel = 'stylesheet';
stylesheet.href = Snb.url+'styles.css';
var head = document.getElementsByTagName('head')[0];
head.insertBefore(stylesheet, head.firstChild);

/**
 * Map Class
 */
Snb.Map = function(mapid, api, opts) { return new Snb.map(mapid, api, opts) }
Snb.map = function() {
	// properties
	this._mapid = arguments[0];
	this._api = arguments[1] || 'google';
	this._mapdom = document.getElementById(arguments[0]);
	this._size = arguments[2] ? arguments[2].size ? arguments[2].size : Snb.Size(600, 400) : Snb.Size(600, 400);
	this._mapdom.style.width = this.size().width()+'px';
	this._mapdom.style.height = this.size().height()+'px';
	this._clusterer = null;
	this._clustererIcons = {
		// google map uses width and height, microsoft map uses offset x and y
		'c1':{'url':Snb.url+'images/m1.png', 'width':53, 'height':52, 'offset':{'x':24, 'y':20}},
		'c2':{'url':Snb.url+'images/m2.png', 'width':56, 'height':55, 'offset':{'x':22, 'y':21}},
		'c3':{'url':Snb.url+'images/m3.png', 'width':66, 'height':65, 'offset':{'x':24, 'y':26}},
		'c4':{'url':Snb.url+'images/m4.png', 'width':66, 'height':65, 'offset':{'x':22, 'y':26}},
		'c5':{'url':Snb.url+'images/m5.png', 'width':66, 'height':65, 'offset':{'x':19, 'y':26}}
	}
	this._clustererGrid = 60;
	// events
	this.onmaptypechange = Snb.Event();
	this.onmovestart = Snb.Event();
	this.onmoveend = Snb.Event();
	this.onzoomend = Snb.Event();
	this.onleftclick = Snb.Event();
	this.onrightclick = Snb.Event();
	// logo
	var logo = document.createElement("div");
	logo.innerHTML = '<a href="http://widgets.shownearby.com/" target="_blank" style="width:99px; height:25px; display:block; filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale,src='+Snb.url+'images/snb-logo.png)"><img style="filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0); border:none;" src="'+Snb.url+'images/snb-logo.png" alt="" /></a>';
	// loading
	this.loading = document.createElement("div");
	this.loading.id = this._mapid+'_loading';
	this.loading.className = 'shownearby_map_loading';
	this.loading.innerHTML = '<img src="'+Snb.url+'images/loading.gif" alt="" style="float:left;" /> Loading...';
	switch(this._api) {
		case 'google':
			// init
			this._map = new GMap2(this._mapdom);
			// logo
			function ShowNearbyMapLogo() {};
			ShowNearbyMapLogo.prototype = new GControl();
			ShowNearbyMapLogo.prototype.initialize = function(map) { map.getContainer().appendChild(logo); return logo; };
			ShowNearbyMapLogo.prototype.getDefaultPosition = function() { return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(10, 35)); };
			// loading
			this._map.getContainer().appendChild(this.loading);
			new GControlPosition(G_ANCHOR_BOTTOM_LEFT, new GSize(this.size().width()/2-50, 0)).apply(this.loading);
			// controls
			this._map.addControl(new ShowNearbyMapLogo());
			this._smallMapTypeControl = new GMenuMapTypeControl();
			this._largeMapTypeControl = new GMapTypeControl();
			this._scaleControl = new GScaleControl();
			this._smallMapControl = new GSmallZoomControl3D();
			this._largeMapControl = new GLargeMapControl3D();
			this.size().width() < 300 ? this._map.addControl(this._smallMapTypeControl) : this._map.addControl(this._largeMapTypeControl);
			if (this.size().width() > 450) { this._map.addControl(this._scaleControl); }
			this.size().height() < 350 ? this._map.addControl(this._smallMapControl) : this._map.addControl(this._largeMapControl);
			// settings
			this._map.enableContinuousZoom();
			this._map.enableScrollWheelZoom();
			// events
			GEvent.addListener(this._map, "maptypechanged", bind(this, function() { this.onmaptypechange.trigger(this.maptype()); }));
			GEvent.addListener(this._map, "movestart", bind(this, function() { this.onmovestart.trigger(this.center()); }));
			GEvent.addListener(this._map, "moveend", bind(this, function() { this.onmoveend.trigger(this.center()); }));
			GEvent.addListener(this._map, "zoomend", bind(this, function() { this.onzoomend.trigger(this.zoom()); }));
			GEvent.addListener(this._map, "click", bind(this, function(overlay, latlng, overlaylatlng) { if (overlay == null) this.onleftclick.trigger(this.center()); }));
			GEvent.addListener(this._map, "singlerightclick", bind(this, function(point, src, overlay) { if (overlay == null) this.onrightclick.trigger(this.center()); }));
			// clusterer
			this._mapMarkers = new Array();
			this._clustererMarkers = new Array();
		break;
		case 'microsoft':
			this._mapdom.style.position = 'relative';
			this._map = new VEMap(this._mapid);
			this.size().width() < 300 ? this._map.SetDashboardSize(VEDashboardSize.Tiny) : this._map.SetDashboardSize(VEDashboardSize.Small); 
			this._map.LoadMap();
			// firefox 3.5 window scrolling bug fix
			if (navigator.userAgent.indexOf("Firefox/3.5") != -1) { 
				this._mapdom.addEventListener('DOMMouseScroll', function(e) {           
					e.stopPropagation();
					e.preventDefault();
					e.cancelBubble = false;
					return false; 
				}, false);
			}
			// logo
			logo.id = this.div_id+'_logo'; // top right logo
			logo.style.position = 'absolute'; 
			logo.style.bottom = '45px'; 
			logo.style.left = '10px'; 
			logo.style.zIndex = '50'; 
			this._mapdom.appendChild(logo);
			// loading
			this.loading.style.position = 'absolute'; // bottom center loading status
			this.loading.style.bottom = '0'; 
			this.loading.style.left = (this.size().width()/2-50)+'px'; 
			this.loading.style.zIndex = '50'; 
			this._mapdom.appendChild(this.loading);
			// settings
			this._map.SetMouseWheelZoomToCenter(false);
			// events
			this._map.AttachEvent("onchangemapstyle", bind(this, function(e) { this.onmaptypechange.trigger(this.maptype()); }));
			this._map.AttachEvent("onstartpan", bind(this, function(e) { this._onmovestart = true; }));
			this._map.AttachEvent("onendpan", bind(this, function(e) { this.onmoveend.trigger(this.center()); }));
			this._map.AttachEvent("onmousemove", bind(this, function(e) { 
				if (this._onmovestart) {
					this._onmovestart = false;
					this._onpan = true; 
					this.onmovestart.trigger(this.center());
				}
			}));
			this._map.AttachEvent("onendzoom", bind(this, function(e) { 
				this._onmovestart = false; 
				this.onzoomend.trigger(this.zoom()); 
				this.onmoveend.trigger(this.center()); 
				e.zoomLevel == 17 ? // off clustering at maximum zoom level
					this._clusterer.SetClusteringConfiguration(VEClusteringType.None) :
						this._clusterer.SetClusteringConfiguration(VEClusteringType.Grid, this._clusteringOpts);
			}));
			this._rightclickcount = 0; 
			this._map.AttachEvent("onclick", bind(this, function(e) {
				if (e.leftMouseButton) { 
					var marker = this._map.GetShapeByID(e.elementID);
					if (marker) {
						if (marker.isClusterShape) {
							var point = marker.GetPoints()[0];
							this.onleftclick.trigger(this.center());
							this.setCenterAndZoom(Snb.Point(point.Latitude, point.Longitude), this.zoom()+1);
						}
						else {
							marker.ref.onleftclick.trigger(marker.ref);
						}
					}
					else {
						this._onpan ? this._onpan = false : this.onleftclick.trigger(this._map.PixelToLatLong(new VEPixel(e.mapX, e.mapY)));
					}		
				}
				else if (e.rightMouseButton) {
					var marker = this._map.GetShapeByID(e.elementID);
					this._rightclickcount++;
					if (this._leftclicktimer) clearTimeout(this._leftclicktimer);
					this._leftclicktimer = setTimeout(bind(this, function(e) {
						this._rightclickcount = 0;	
						marker ?
							marker.ref.onrightclick.trigger(marker.ref) :
								this._onpan ? this._onpan = false : this.onrightclick.trigger(this._map.PixelToLatLong(new VEPixel(e.mapX, e.mapY)));
					}), 250);
					if (this._rightclickcount > 1) {
						this._rightclickcount = 0;
						clearTimeout(this._leftclicktimer);
						this.zoomOut();
					}
				}
			}));
			this._map.AttachEvent("onmouseover", bind(this, function(e) {
				var marker = this._map.GetShapeByID(e.elementID);
				if (marker) {
					this._map.AttachEvent("ondoubleclick", disableMicrosoftEvent);
					if (!marker.isClusterShape) {
						marker.ref.onmouseover.trigger(marker.ref);
						marker.ref.openWindow(this);						
					}
				}
			}));
			this._map.AttachEvent("onmousedown", bind(this, function(e) {
				var marker = this._map.GetShapeByID(e.elementID);
				if (marker) {
					if (!marker.isClusterShape) marker.ref.onmousedown.trigger(marker.ref);
				}
			}));
			this._map.AttachEvent("onmouseup", bind(this, function(e) {
				this._onmovestart = false;
				var marker = this._map.GetShapeByID(e.elementID);
				if (marker) {
					if (!marker.isClusterShape) marker.ref.onmouseup.trigger(marker.ref);
				}
			}));
			this._map.AttachEvent("onmouseout", bind(this, function(e) {
				var marker = this._map.GetShapeByID(e.elementID);
				if (marker) {
					this._map.DetachEvent("ondoubleclick", disableMicrosoftEvent);
					if (!marker.isClusterShape) marker.ref.onmouseout.trigger(marker.ref);
				}
			}));
			// maplayer
			this._maplayer = new VEShapeLayer();
			this._map.AddShapeLayer(this._maplayer);
			// clusterer
			this._clusterer = new VEShapeLayer();
			this._map.AddShapeLayer(this._clusterer);
			this._clusteringOpts =  new VEClusteringOptions();
			this._clusteringOpts.Callback = bind(this, function(clusters) {
				for (var i=0; i<clusters.length; i++) {
					var cluster = clusters[i];
					var marker = cluster.GetClusterShape();
					var num = cluster.Shapes.length;
					var customIcon = new VECustomIconSpecification();
					customIcon.TextFont = 'Arial'
					customIcon.ForeColor = new VEColor(0, 0, 0, 1.0);
					customIcon.TextBold = true;
					customIcon.TextSize = 8;
					customIcon.TextContent = num.toString();
					if (num < 10) {
						customIcon.TextOffset = new VEPixel(this._clustererIcons.c1.offset.x, this._clustererIcons.c1.offset.y);
						customIcon.Image = this._clustererIcons.c1.url;
					}
					else if (num < 100) {
						customIcon.TextOffset = new VEPixel(this._clustererIcons.c2.offset.x, this._clustererIcons.c2.offset.y);
						customIcon.Image = this._clustererIcons.c2.url;
					}
					else if (num < 1000) {
						customIcon.TextOffset = new VEPixel(this._clustererIcons.c3.offset.x, this._clustererIcons.c3.offset.y);
						customIcon.Image = this._clustererIcons.c3.url;
					}
					else if (num < 10000) {
						customIcon.TextOffset = new VEPixel(this._clustererIcons.c4.offset.x, this._clustererIcons.c4.offset.y);
						customIcon.Image = this._clustererIcons.c4.url;
					}
					else {
						customIcon.TextOffset = new VEPixel(this._clustererIcons.c5.offset.x, this._clustererIcons.c5.offset.y);
						customIcon.Image = this._clustererIcons.c5.url;
					}
					marker.SetCustomIcon(customIcon);
					marker.SetTitle('');
					marker.SetDescription('');
					marker.isClusterShape = true;
				}
			});
		break;
	}
	this.setCenterAndZoom(
		arguments[2] ? arguments[2].center ? arguments[2].center : Snb.Point(1.352083, 103.819836) : Snb.Point(1.352083, 103.819836), 
		arguments[2] ? arguments[2].zoom ? arguments[2].zoom : 11 : 11);
	this.savePosition();
}
Snb.map.prototype = {
	/*
	 * Set get maptype
	 */
	maptype : function() {
		if (arguments.length) {
			switch(this._api) {
				case 'google': 
					switch(arguments[0]) {
						case 'map': this._map.setMapType(G_NORMAL_MAP); break;
						case 'satellite': this._map.setMapType(G_SATELLITE_MAP); break;
						case 'hybrid': this._map.setMapType(G_HYBRID_MAP); break;
						default: this._map.setMapType(G_NORMAL_MAP);
					}
				break;
				case 'microsoft': 
					switch(arguments[0]) {
						case 'map': this._map.SetMapStyle(VEMapStyle.Road); break;
						case 'satellite': this._map.SetMapStyle(VEMapStyle.Aerial); break;
						case 'hybrid': this._map.SetMapStyle(VEMapStyle.Hybrid); break;
						default: this._map.setMapType(G_NORMAL_MAP);
					}
				break;
			}
		}
		else {
			switch(this._api) {
				case 'google': 
					switch(this._map.getCurrentMapType()) {
						case G_NORMAL_MAP: return 'map'; break;
						case G_SATELLITE_MAP: return 'satellite'; break;
						case G_HYBRID_MAP: return 'hybrid'; break;
					} 
				break;
				case 'microsoft':
					switch(this._map.GetMapStyle()) {
						case VEMapStyle.Road: return 'map'; break;
						case VEMapStyle.Aerial: return 'satellite'; break;
						case VEMapStyle.Hybrid: return 'hybrid'; break;
					} 
				break;
			}
			return 'custom';
		}
	},
	/*
	 * Set get center
	 */
	center : function() {
		if (arguments.length) {
			if (arguments[0] instanceof Snb.point) {
				switch(this._api) {
					case 'google': this._map.setCenter(new GLatLng(arguments[0].lat(), arguments[0].lng())); break;
					case 'microsoft': this._map.SetCenter(new VELatLong(arguments[0].lat(), arguments[0].lng())); break;
				}
			}
		}
		else {
			switch(this._api) {
				case 'google': return Snb.Point(this._map.getCenter().lat(), this._map.getCenter().lng()); break;
				case 'microsoft': return Snb.Point(this._map.GetCenter().Latitude, this._map.GetCenter().Longitude); break;
			}
		}
	},
	/*
	 * Set get zoom
	 */
	zoom : function() {
		if (arguments.length) {
			switch(this._api) {
				case 'google': this._map.setZoom(arguments[0]); break;
				case 'microsoft': this._map.SetZoomLevel(arguments[0]); break;
			}
		}
		else {
			switch(this._api) {
				case 'google': return this._map.getZoom(); break;
				case 'microsoft': return this._map.GetZoomLevel(); break;
			}
		}
	},
	/*
	 * Zoom in
	 */
	zoomIn : function() { this.zoom(this.zoom()+1); },
	/*
	 * Zoom out
	 */
	zoomOut : function() { this.zoom(this.zoom()-1); },
	/*
	 * Set center and zoom
	 */
	setCenterAndZoom : function() {
		if (arguments[0] instanceof Snb.point) {
			switch(this._api) {
				case 'google': this._map.setCenter(new GLatLng(arguments[0].lat(), arguments[0].lng()), arguments[1]); break;
				case 'microsoft': this._map.SetCenterAndZoom(new VELatLong(arguments[0].lat(), arguments[0].lng()), arguments[1]); break;
			}
		}
	},
	/*
	 * Save position
	 */
	savePosition : function() {
		switch(this._api) {
			case 'google': this._map.savePosition(); break;
			case 'microsoft': this._savePosition = {'center':this.center(), 'zoom':this.zoom()}; break;
		}
	},
	/*
	 * Restore Position
	 */
	restorePosition : function() {
		switch(this._api) {
			case 'google': this._map.returnToSavedPosition(); break;
			case 'microsoft': this.setCenterAndZoom(this._savePosition.center, this._savePosition.zoom); break;
		}
	},
	/*
	 * Pan map to lat lng
	 */
	panTo : function() {
		if (arguments[0] instanceof Snb.point) {
			switch(this._api) {
				case 'google': this._map.panTo(new GLatLng(arguments[0].lat(), arguments[0].lng())); break;
				case 'microsoft': this._map.PanToLatLong(new VELatLong(arguments[0].lat(), arguments[0].lng())); break;
			}
		}
	},
	/*
	 * Set get size
	 */
	size : function() {
		if (arguments.length) {
			if (arguments[0] instanceof Snb.size) {
				this._size = arguments[0];
				var center = this.center();
				this._mapdom.style.width = this._size.width()+'px';
				this._mapdom.style.height = this._size.height()+'px';
				switch(this._api) {
					case 'google': 
						this._map.checkResize(); 
						this.panTo(center);
						new GControlPosition(G_ANCHOR_BOTTOM_LEFT, new GSize(this.size().width()/2-50, 0)).apply(this.loading);
						if (this.size().width() < 300) { 
							this._map.removeControl(this._largeMapTypeControl);
							this._map.addControl(this._smallMapTypeControl);
						}
						else {
							this._map.removeControl(this._smallMapTypeControl);
							this._map.addControl(this._largeMapTypeControl);
						}
						if (this.size().width() > 450) { 
							this._map.addControl(this._scaleControl); 
						}
						else {
							this._map.removeControl(this._scaleControl); 
						}
						if (this.size().height() < 350) {
							this._map.removeControl(this._largeMapControl);
							this._map.addControl(this._smallMapControl);
						}
						else { 
							this._map.removeControl(this._smallMapControl);
							this._map.addControl(this._largeMapControl);
						}
					break;
					case 'microsoft': 
						this._map.Resize();
						this.loading.style.left = (this.size().width()/2-50)+'px';
						this.size().width() < 300 ? this._map.HideScalebar() : this._map.ShowScalebar();
					break;
				}
			}
		}
		else {
			return Snb.Size(this._size.width(), this._size.height());
		}
	},
	/**
	 * Set get scroll wheel zoom
	 */
	scrollWheelZoom : function() {
		if (arguments.length) {
			this._scrollWheelZoom = arguments[0];
			switch(this._api) {
				case 'google': 
					this._scrollWheelZoom ? 
						this._map.enableScrollWheelZoom() : 
							this._map.disableScrollWheelZoom();
				break;
				case 'microsoft': 
					this._scrollWheelZoom ? 
						this._map.DetachEvent("onmousewheel", disableMicrosoftEvent) : 
							this._map.AttachEvent("onmousewheel", disableMicrosoftEvent); 
				break;
			}
		}
		else {
			switch(this._api) {
				case 'google': return this._map.scrollWheelZoomEnabled(); break;
				case 'microsoft': return this._scrollWheelZoom; break;
			}
		}
	},
	/**
	 * Set get scroll wheel zoom
	 */
	loadingStatus : function() {
		var loading = document.getElementById(this._mapid+'_loading');
		if (arguments.length) {
			arguments[0] ? 
				loading.style.visibility = 'visible' :
					loading.style.visibility = 'hidden';
		}
		else {
			return loading.style.visibility == 'visible' ? true : false;
		}
	},
	/*
	 * Add marker(s) to map
	 */
	addMarkerToMap : function() {
		switch(this._api) {
			case 'google': 
				if (arguments[0] instanceof Array) {
					for (var i=0; i<arguments[0].length; i++) {
						if (arguments[0][i] instanceof Snb.marker) {
							var j = getIndex(this._mapMarkers, arguments[0][i]._marker);
							if (j == -1) {
								this._mapMarkers.push(arguments[0][i]._marker);
								this._map.addOverlay(arguments[0][i]._marker);
							}
						}
					}
				}
				else if (arguments[0] instanceof Snb.marker) {
					var j = getIndex(this._mapMarkers, arguments[0]._marker);
					if (j == -1) {
						this._mapMarkers.push(arguments[0]._marker);
						this._map.addOverlay(arguments[0]._marker);
					}
				}
			break;
			case 'microsoft':
				if (arguments[0] instanceof Array) {
					var markers = new Array(); 
					for (var i=0; i<arguments[0].length; i++) {
						if (arguments[0][i] instanceof Snb.marker) {
							if (arguments[0][i]._marker) this.deleteMarkerOnMap(arguments[0][i]);
							arguments[0][i]._marker = new VEShape(VEShapeType.Pushpin, new VELatLong(arguments[0][i]._point.lat(), arguments[0][i]._point.lng())); 
							arguments[0][i]._marker.ref = arguments[0][i]; // allow VEShape to ref back to Snb.marker
							arguments[0][i]._marker.SetDescription(arguments[0][i]._html);
							arguments[0][i]._marker.SetCustomIcon(arguments[0][i]._icon);
							markers[i] = arguments[0][i]._marker;
						}
					}
					this._maplayer.AddShape(markers);
				}
				else if (arguments[0] instanceof Snb.marker) {
					if (arguments[0]._marker) this.deleteMarkerOnMap(arguments[0]);
					arguments[0]._marker = new VEShape(VEShapeType.Pushpin, new VELatLong(arguments[0]._point.lat(), arguments[0]._point.lng())); 
					arguments[0]._marker.ref = arguments[0]; // allow VEShape to ref back to Snb.marker
					arguments[0]._marker.SetDescription(arguments[0]._html);
					arguments[0]._marker.SetCustomIcon(arguments[0]._icon);
					this._maplayer.AddShape(arguments[0]._marker);
				}
			break;
		}
	},
	/*
	 * Delete marker(s) on map
	 */
	deleteMarkerOnMap : function() {
		switch(this._api) {
			case 'google':
				if (arguments[0] instanceof Array) {
					for (var i=0; i<arguments[0].length; i++) {
						if (arguments[0][i] instanceof Snb.marker) {
							var j = getIndex(this._mapMarkers, arguments[0][i]._marker);
							if (j != -1) {
								removeIndex(this._mapMarkers, j);
								this._map.removeOverlay(arguments[0][i]._marker);
							}
						}
					}
				}
				else if (arguments[0] instanceof Snb.marker) {
					var j = getIndex(this._mapMarkers, arguments[0]._marker);
					if (j != -1) {
						removeIndex(this._mapMarkers, j);
						this._map.removeOverlay(arguments[0]._marker);
					}
				}
			break;
			case 'microsoft':
				if (arguments[0] instanceof Array) {
					for (var i=0; i<arguments[0].length; i++) {
						if (arguments[0][i] instanceof Snb.marker) {
							if (arguments[0][i]._marker) this._maplayer.DeleteShape(arguments[0][i]._marker); 
						}
					}
				}
				else if (arguments[0] instanceof Snb.marker) {
					if (arguments[0]._marker) this._maplayer.DeleteShape(arguments[0]._marker); 
				}
			break;
		}
	},
	/*
	 * Delete all markers on map
	 */
	deleteAllMarkersOnMap : function() {
		switch(this._api) {
			case 'google':
				this._mapMarkers = new Array(); 
				this._map.clearOverlays();
				if (this._clustererMarkers.length > 0) {
					this._clusterer.clearMarkers();
					this._clusterer = new MarkerClusterer(this._map, this._clustererMarkers, { gridSize:this._clustererGrid, maxZoom:17, styles:[
						{ url:this._clustererIcons.c1.url, width:this._clustererIcons.c1.width, height:this._clustererIcons.c1.height, opt_fontSize:this._clustererIcons.c1.fontSize, opt_textColor:this._clustererIcons.c1.fontColor },
						{ url:this._clustererIcons.c2.url, width:this._clustererIcons.c2.width, height:this._clustererIcons.c2.height, opt_fontSize:this._clustererIcons.c2.fontSize, opt_textColor:this._clustererIcons.c2.fontColor },
						{ url:this._clustererIcons.c3.url, width:this._clustererIcons.c3.width, height:this._clustererIcons.c3.height, opt_fontSize:this._clustererIcons.c3.fontSize, opt_textColor:this._clustererIcons.c3.fontColor },
						{ url:this._clustererIcons.c4.url, width:this._clustererIcons.c4.width, height:this._clustererIcons.c4.height, opt_fontSize:this._clustererIcons.c4.fontSize, opt_textColor:this._clustererIcons.c4.fontColor },
						{ url:this._clustererIcons.c5.url, width:this._clustererIcons.c5.width, height:this._clustererIcons.c5.height, opt_fontSize:this._clustererIcons.c5.fontSize, opt_textColor:this._clustererIcons.c5.fontColor }
					] });
				}
			break;
			case 'microsoft': 
				this._maplayer.DeleteAllShapes(); 
			break;
		}
	},
	/*
	 * Get total number of markers on the map
	 */
	totalMarkersOnMap : function() {
		switch(this._api) {
			case 'google': return this._mapMarkers.length; break;
			case 'microsoft': return this._maplayer.GetShapeCount(); break;
		}
	},
	/*
	 * Get map marker bounds
	 */
	getMapBounds : function() {
		var points = new Array();
		switch(this._api) {
			case 'google':
				for (var i=0; i<this._mapMarkers.length; i++) {
					points.push(this._mapMarkers[i].ref.point());
				}
			break;
			case 'microsoft':
				var boundingRectangle = this._maplayer.GetBoundingRectangle();
				points[0] = Snb.Point(boundingRectangle.TopLeftLatLong.Latitude, boundingRectangle.TopLeftLatLong.Longitude);
				points[1] = Snb.Point(boundingRectangle.BottomRightLatLong.Latitude, boundingRectangle.BottomRightLatLong.Longitude);
			break;
		}
		return Snb.Bounds(points);
	},
	/*
	 * Add marker(s) to clusterer
	 */
	addMarkerToClusterer : function() {
		switch(this._api) {
			case 'google': 
				if (arguments[0] instanceof Array) {
					for (var i=0; i<arguments[0].length; i++) {
						if (arguments[0][i] instanceof Snb.marker) {
							var j = getIndex(this._clustererMarkers, arguments[0][i]._marker);
							if (j == -1) this._clustererMarkers.push(arguments[0][i]._marker);
						}
					}
				}
				else if (arguments[0] instanceof Snb.marker) {
					var j = getIndex(this._clustererMarkers, arguments[0]._marker);
					if (j == -1) this._clustererMarkers.push(arguments[0]._marker);
				}
				if (this._clusterer) this._clusterer.clearMarkers();
				this._clusterer = new MarkerClusterer(this._map, this._clustererMarkers, { gridSize:this._clustererGrid, maxZoom:17, styles:[
					{ url:this._clustererIcons.c1.url, width:this._clustererIcons.c1.width, height:this._clustererIcons.c1.height, opt_fontSize:this._clustererIcons.c1.fontSize, opt_textColor:this._clustererIcons.c1.fontColor },
					{ url:this._clustererIcons.c2.url, width:this._clustererIcons.c2.width, height:this._clustererIcons.c2.height, opt_fontSize:this._clustererIcons.c2.fontSize, opt_textColor:this._clustererIcons.c2.fontColor },
					{ url:this._clustererIcons.c3.url, width:this._clustererIcons.c3.width, height:this._clustererIcons.c3.height, opt_fontSize:this._clustererIcons.c3.fontSize, opt_textColor:this._clustererIcons.c3.fontColor },
					{ url:this._clustererIcons.c4.url, width:this._clustererIcons.c4.width, height:this._clustererIcons.c4.height, opt_fontSize:this._clustererIcons.c4.fontSize, opt_textColor:this._clustererIcons.c4.fontColor },
					{ url:this._clustererIcons.c5.url, width:this._clustererIcons.c5.width, height:this._clustererIcons.c5.height, opt_fontSize:this._clustererIcons.c5.fontSize, opt_textColor:this._clustererIcons.c5.fontColor }
				] });
			break;
			case 'microsoft': 
				if (arguments[0] instanceof Array) {
					var markers = new Array(); 
					for (var i=0; i<arguments[0].length; i++) {
						if (arguments[0][i] instanceof Snb.marker) {
							if (arguments[0][i]._marker) this.deleteMarkerOnClusterer(arguments[0][i]);
							arguments[0][i]._marker = new VEShape(VEShapeType.Pushpin, new VELatLong(arguments[0][i]._point.lat(), arguments[0][i]._point.lng())); 
							arguments[0][i]._marker.ref = arguments[0][i]; // allow VEShape to ref back to Snb.marker
							arguments[0][i]._marker.SetDescription(arguments[0][i]._html);
							arguments[0][i]._marker.SetCustomIcon(arguments[0][i]._icon);
							markers[i] = arguments[0][i]._marker;
						}
					}
					this._clusterer.AddShape(markers);
				}
				else if (arguments[0] instanceof Snb.marker) {
					if (arguments[0]._marker) this.deleteMarkerOnClusterer(arguments[0]);
					arguments[0]._marker = new VEShape(VEShapeType.Pushpin, new VELatLong(arguments[0]._point.lat(), arguments[0]._point.lng())); 
					arguments[0]._marker.ref = arguments[0]; // allow VEShape to ref back to Snb.marker
					arguments[0]._marker.SetDescription(arguments[0]._html);
					arguments[0]._marker.SetCustomIcon(arguments[0]._icon);
					this._clusterer.AddShape(arguments[0]._marker);
				}
			break;
		}
	},
	/*
	 * Delete marker(s) on clusterer
	 */
	deleteMarkerOnClusterer : function() {
		switch(this._api) {
			case 'google':
				if (arguments[0] instanceof Array) {
					var markers = new Array();
					for (var i=0; i<arguments[0].length; i++) {
						if (arguments[0][i] instanceof Snb.marker) {
							var j = getIndex(this._clustererMarkers, arguments[0][i]._marker);
							if (j != -1) removeIndex(this._clustererMarkers, j);
						}
					}
				}
				else if (arguments[0] instanceof Snb.marker) {
					var i = getIndex(this._clustererMarkers, arguments[0]._marker);
					if (i != -1) removeIndex(this._clustererMarkers, i);
				}
				if (this._clusterer) this._clusterer.clearMarkers();
				this._clusterer = new MarkerClusterer(this._map, this._clustererMarkers, { gridSize:this._clustererGrid, maxZoom:17, styles:[
					{ url:this._clustererIcons.c1.url, width:this._clustererIcons.c1.width, height:this._clustererIcons.c1.height, opt_fontSize:this._clustererIcons.c1.fontSize, opt_textColor:this._clustererIcons.c1.fontColor },
					{ url:this._clustererIcons.c2.url, width:this._clustererIcons.c2.width, height:this._clustererIcons.c2.height, opt_fontSize:this._clustererIcons.c2.fontSize, opt_textColor:this._clustererIcons.c2.fontColor },
					{ url:this._clustererIcons.c3.url, width:this._clustererIcons.c3.width, height:this._clustererIcons.c3.height, opt_fontSize:this._clustererIcons.c3.fontSize, opt_textColor:this._clustererIcons.c3.fontColor },
					{ url:this._clustererIcons.c4.url, width:this._clustererIcons.c4.width, height:this._clustererIcons.c4.height, opt_fontSize:this._clustererIcons.c4.fontSize, opt_textColor:this._clustererIcons.c4.fontColor },
					{ url:this._clustererIcons.c5.url, width:this._clustererIcons.c5.width, height:this._clustererIcons.c5.height, opt_fontSize:this._clustererIcons.c5.fontSize, opt_textColor:this._clustererIcons.c5.fontColor }
				] });
			break;
			case 'microsoft':
				if (arguments[0] instanceof Array) {
					for (var i=0; i<arguments[0].length; i++) {
						if (arguments[0][i] instanceof Snb.marker) {
							if (arguments[0][i]._marker) this._clusterer.DeleteShape(arguments[0][i]._marker); 
						}
					}
				}
				else if (arguments[0] instanceof Snb.marker) {
					if (arguments[0]._marker) this._clusterer.DeleteShape(arguments[0]._marker); 
				}
			break;
		}
	},
	/*
	 * Delete all markers on clusterer
	 */
	deleteAllMarkersOnClusterer : function() {
		switch(this._api) {
			case 'google':
				this._clustererMarkers = new Array(); 
				if (this._clusterer) this._clusterer.clearMarkers(); 
			break;
			case 'microsoft':
				this._clusterer.DeleteAllShapes(); 
			break;
		}
	},
	/*
	 * Get total number of markers on the clusterer
	 */
	totalMarkersOnClusterer : function() {
		switch(this._api) {
			case 'google': return this._clustererMarkers.length; break;
			case 'microsoft': return this._clusterer.GetShapeCount(); break;
		}
	},
	/*
	 * Get clusterer marker bounds
	 */
	getClustererBounds : function() {
		var points = new Array();
		switch(this._api) {
			case 'google':
				for (var i=0; i<this._clustererMarkers.length; i++) {
					points.push(this._clustererMarkers[i].ref.point());
				}
			break;
			case 'microsoft':
				var boundingRectangle = this._clusterer.GetBoundingRectangle();
				points[0] = Snb.Point(boundingRectangle.TopLeftLatLong.Latitude, boundingRectangle.TopLeftLatLong.Longitude);
				points[1] = Snb.Point(boundingRectangle.BottomRightLatLong.Latitude, boundingRectangle.BottomRightLatLong.Longitude);
			break;
		}
		return Snb.Bounds(points);
	},
	/*
	 * Set map bounds
	 */
	showBounds : function() {
		if (arguments[0] instanceof Snb.bounds) {
			switch(this._api) {
				case 'google':
					var sw = new GLatLng(arguments[0].getSouthWest().lat(), arguments[0].getSouthWest().lng());
					var ne = new GLatLng(arguments[0].getNorthEast().lat(), arguments[0].getNorthEast().lng());
					var bounds = new GLatLngBounds(sw, ne);
					this._map.showBounds(bounds, {top:30,right:10,bottom:10,left:50});
				break;
				case 'microsoft':
					var nw = new VELatLong(arguments[0].getNorthWest().lat(), arguments[0].getNorthWest().lng());
					var se = new VELatLong(arguments[0].getSouthEast().lat(), arguments[0].getSouthEast().lng());
					var bounds = new VELatLongRectangle(nw, se);
					this._map.SetMapView(bounds);
				break;
			}
		}
	},
	/*
	 * Set clusterer icons
	 */
	clustererIcons : function() {
		if (arguments.length) {
			this._clustererIcons = arguments[0];
		}
		else {
			return this._clustererIcons;
		}
	}
}

/**
 * Marker Class
 */
Snb.Marker = function(point, window_html, window_size, api) { return new Snb.marker(point, window_html, window_size, api) }
Snb.marker = function() {
	// properties
	this._point = arguments[0];
	this._html = arguments[1];
	this._size = arguments[2];
	this._api = arguments[3] || 'google';
	this._visible = true;
	// events
	this.onmouseover = Snb.Event();
	this.onmousedown = Snb.Event();
	this.onmouseup = Snb.Event();
	this.onmouseout = Snb.Event();
	this.onmovestart = Snb.Event();
	this.onmoveend = Snb.Event();
	this.onleftclick = Snb.Event();
	this.onrightclick = Snb.Event();
	this.onwindowopen = Snb.Event();
	this.onwindowclose = Snb.Event();
	switch(this._api) {
		case 'google': 
			this._icon = new GIcon();
			this._icon.image = Snb.url+'images/default-image.png';
			this._icon.iconSize = new GSize(32,37);
			this._icon.iconAnchor = new GPoint(16,37);
			this._icon.infoWindowAnchor = new GPoint(16,0);
			this._marker = new GMarker(new GLatLng(this._point.lat(), this._point.lng()), {'icon':this._icon, 'draggable':true}); 
			this._marker.ref = this; // allow GMarker to ref back to Snb.marker
			this._marker.disableDragging();
			GEvent.addListener(this._marker, "click", bind(this, function() { 
				this._isleftclick = true;
				this.onleftclick.trigger(this);
			}));
			GEvent.addListener(this._marker, "mouseover", bind(this, function() { this.onmouseover.trigger(this) }));
			GEvent.addListener(this._marker, "mousedown", bind(this, function() { this.onmousedown.trigger(this) }));
			GEvent.addListener(this._marker, "mouseup", bind(this, function() { 
				this.onmouseup.trigger(this);
				this._isleftclick = false;
				setTimeout(bind(this, function(e) {
					if (!this._isleftclick) this.onrightclick.trigger(this);
				}), 100);
			}));
			GEvent.addListener(this._marker, "mouseout", bind(this, function() { this.onmouseout.trigger(this) }));
		break;
		case 'microsoft': 
			this._icon = new VECustomIconSpecification();
			this._icon.Image = Snb.url+'images/default-image.png';
		break;
	}
}
Snb.marker.prototype = {
	/**
	 * Set get marker point
	 */
	point : function() {
		if (arguments.length) {
			if (arguments[0] instanceof Snb.point) {
				this._point = arguments[0];
				switch(this._api) {
					case 'google': this._marker.setLatLng(new GLatLng(this._point.lat(), this._point.lng())); break;
					case 'microsoft': if (this._marker) this._marker.SetPoints(new VELatLong(this._point.lat(), this._point.lng())); break;
				}
			}
		}
		else {
			switch(this._api) {
				case 'google': 
					var point = this._marker.getLatLng();
					this._point = Snb.Point(point.lat(), point.lng());
					return this._point;
				break;
				case 'microsoft': 
					if (this._marker) {
						var point = this._marker.GetPoints();
						this._point = Snb.Point(point[0].Latitude, point[0].Longitude);
					}
					return this._point;	
				break;
			}
		}
	},
	/**
	 * Set get html
	 */
	html : function() {
		if (arguments.length) {
			this._html = arguments[0];
		}
		else {
			return this._html;
		}
	},
	/**
	 * Set get html
	 */
	size : function() {
		if (arguments.length) {
			this._size = arguments[0];
		}
		else {
			return this._size;
		}
	},
	/**
	 * Open window
	 */
	openWindow : function() {
		if (arguments[0] instanceof Snb.map) {
			if (this.html() || arguments[1]) {
				switch(this._api) {
					case 'google':
						var height = 'auto';
						if (this.scrollHeight != undefined) {
							if (this.scrollHeight < this.size().height()) {
								height = this.size().height;
							}
						}
						var html = '<div style="max-width:'+this.size().width()+'px; _width:expression(this.scrollWeight<'+this.size().width()+'?\''+this.size().width()+'px\':\'auto\'); '+
									'max-height:'+this.size().height()+'px; _height:'+height+'; '+
									'overflow:auto; padding-right:5px; font:12px Arial, Helvetica, sans-serif; color:#666; background-color:#fff; text-align:left;">'+(arguments[1]?arguments[1]:this.html())+'</div>';
						this._marker.openInfoWindowHtml(html);
						this.onwindowopen.trigger(this);
					break;
					case 'microsoft':
						var html = '<div style="max-height:'+this.size().height()+'px; _height:expression(this.scrollHeight<'+this.size().height()+'?\''+this.size().height()+'px\':\'auto\'); '+
									'overflow:auto; padding-right:5px; font:12px Arial, Helvetica, sans-serif; color:#666; background-color:#fff; text-align:left;">'+(arguments[1]?arguments[1]:this.html())+'</div>';
						var px = arguments[0]._map.LatLongToPixel(new VELatLong(this.point().lat(), this.point().lng())); px.x += 15;
						this._marker.SetDescription(html);
						arguments[0]._map.ShowInfoBox(this._marker, px);
						this.onwindowopen.trigger(this);
					break;
				}
			}
		}
	},
	/**
	 * Close window
	 */
	closeWindow : function() {
		switch(this._api) {
			case 'google': 
				this._marker.closeInfoWindow(); 
				this.onwindowclose.trigger(this);
			break;
			case 'microsoft':
				arguments[0]._map.HideInfoBox(this._marker);
				this.onwindowclose.trigger(this);
			break;
		}
	},
	visible : function() {
		if (arguments.length) {
			this._visible = arguments[0];
			switch(this._api) {
				case 'google': arguments[0] ? this._marker.show() : this._marker.hide(); break;
				case 'microsoft': arguments[0] ? this._marker.Show() : this._marker.Hide(); break;
			}
		}
		else {
			switch(this._api) {
				case 'google': this._visible = !this._marker.isHidden(); // fall through
				case 'microsoft': return this._visible; break;
			}
		}
	},
	icon : function() {
		return this._icon;
	}
}

/**
 * Point Class
 */
Snb.Point = function(lat, lng) { return new Snb.point(lat, lng) }
Snb.point = function() {
	this._lat = arguments[0];
	this._lng = arguments[1];
}
Snb.point.prototype = {
	lat : function() { return arguments[0] ? this._lat = arguments[0] : this._lat; },
	lng : function() { return arguments[0] ? this._lng = arguments[0] : this._lng; },
	toString : function() { return this._lat+', '+this._lng; },	
	equals : function() { return arguments[0] instanceof Snb.point && this._lat === arguments[0].lat() && this._lng === arguments[0].lng() ? true : false; }
}

/**
 * Bounds Class
 */
Snb.Bounds = function(points) { return new Snb.bounds(points) }
Snb.bounds = function() {
	if (arguments[0] instanceof Array) {
		if (arguments[0][0] instanceof Snb.point) {
			this._n = arguments[0][0].lat();
			this._s = arguments[0][0].lat();
			this._e = arguments[0][0].lng();
			this._w = arguments[0][0].lng();
			this.extend(arguments[0]);
		}
	}
	else {
		var pt = arguments[0].split(', ');
		this._n = pt[0];
		this._s = pt[1];
		this._e = pt[2];
		this._w = pt[3];
	}
}
Snb.bounds.prototype = {
	getNorthWest : function() { return Snb.Point(this._n, this._w); }, 
	getNorthEast : function() { return Snb.Point(this._n, this._e); }, 
	getSouthEast : function() { return Snb.Point(this._s, this._e); }, 
	getSouthWest : function() { return Snb.Point(this._s, this._w); }, 
	toString : function() { return this._n+', '+this._s+', '+this._e+', '+this._w; },	
	equals : function() {
		return 	arguments[0].getNorthWest().equals(this.getNorthWest()) && 
				arguments[0].getSouthEast().equals(this.getSouthEast()) ? true : false;
	},
	getCenter : function() { 
		return Snb.Point((this._n+this._s)/2, (this._e+this._w)/2); // round 8 decimal
	},
	contains : function() { 
		if (arguments[0] instanceof Snb.point) {
			return	arguments[0].lat() <= this._n && 
					arguments[0].lat() >= this._s && 
					arguments[0].lng() <= this._e && 
					arguments[0].lng() >= this._w ? true : false;
		}
		else if (arguments[0] instanceof Snb.bounds) {
			return 	arguments[0].getNorthWest().lat() <= this.getNorthWest().lat() && 
					arguments[0].getNorthWest().lng() >= this.getNorthWest().lng() && 
					arguments[0].getSouthEast().lat() >= this.getSouthEast().lat() && 
					arguments[0].getSouthEast().lng() <= this.getSouthEast().lng() ? true : false;
		}
	},
	extend : function() {
		if (arguments[0] instanceof Array) {
			for (var i=0; i<arguments[0].length; i++) {
				if (arguments[0][i] instanceof Snb.point) {
					if (arguments[0][i].lat() > this._n) this._n = arguments[0][i].lat();
					if (arguments[0][i].lat() < this._s) this._s = arguments[0][i].lat();
					if (arguments[0][i].lng() > this._e) this._e = arguments[0][i].lng();
					if (arguments[0][i].lng() < this._w) this._w = arguments[0][i].lng();
				}
				else if (arguments[0][i] instanceof Snb.bounds) {
					if (arguments[0][i].getNorthWest().lat() > this.getNorthWest().lat()) this._n = arguments[0][i].getNorthWest().lat();
					if (arguments[0][i].getNorthWest().lng() < this.getNorthWest().lng()) this._w = arguments[0][i].getNorthWest().lng();
					if (arguments[0][i].getSouthEast().lat() < this.getSouthEast().lat()) this._s = arguments[0][i].getSouthEast().lat();
					if (arguments[0][i].getSouthEast().lng() > this.getSouthEast().lng()) this._e = arguments[0][i].getSouthEast().lng();
				}
			}
		}
		else if (arguments[0] instanceof Snb.point) {
			if (arguments[0].lat() > this._n) this._n = arguments[0].lat();
			if (arguments[0].lat() < this._s) this._s = arguments[0].lat();
			if (arguments[0].lng() > this._e) this._e = arguments[0].lng();
			if (arguments[0].lng() < this._w) this._w = arguments[0].lng();
		}
		else if (arguments[0] instanceof Snb.bounds) {
			if (arguments[0].getNorthWest().lat() > this.getNorthWest().lat()) this._n = arguments[0].getNorthWest().lat();
			if (arguments[0].getNorthWest().lng() < this.getNorthWest().lng()) this._w = arguments[0].getNorthWest().lng();
			if (arguments[0].getSouthEast().lat() < this.getSouthEast().lat()) this._s = arguments[0].getSouthEast().lat();
			if (arguments[0].getSouthEast().lng() > this.getSouthEast().lng()) this._e = arguments[0].getSouthEast().lng();
		}
	}
}

/**
 * Size Class
 */
Snb.Size = function(width, height) { return new Snb.size(width, height) }
Snb.size = function() {
	this._width = arguments[0];
	this._height = arguments[1];
}
Snb.size.prototype = {
	width : function() { return arguments[0] ? this._width = arguments[0] : this._width; },
	height : function() { return arguments[1] ? this._height = arguments[1] : this._height; },
	toString : function() { return this._width+', '+this._height; },	
	equals : function() { return arguments[0] instanceof Snb.size && this._width === arguments[0].width() && this._height === arguments[0].height() ? true : false; }
}

/**
 * Event Class
 */
Snb.Event = function() {
	return new Snb.event();
}
Snb.event = function() {
	this.listeners = new Array();
}
Snb.event.prototype = {
	addListener : function() {
		this.listeners.push(arguments[0]);
	},
	removeListener : function() {
		var i = getIndex(this.listeners, arguments[0]);
		if (i != -1) removeIndex(this.listeners, i);
	},
	removeAllListeners : function() {
		this.listeners = new Array();
	},
	trigger : function() {
		for (var i=0; i<this.listeners.length; i++) {
			this.listeners[i](arguments[0]);
		}
	}
}

/**
 * Autocomplete class
 */
Snb.Autocomplete = function(ref, input, div, zindex, autoFill) {
	return new Snb.autocomplete(ref, input, div, zindex, autoFill); 
}
Snb.autocomplete = function() {
	this.default_text = 'Enter your location...';
	this.loading_text = 'Loading...';
	this.no_results_text = 'No matches found.';
	this.ref = arguments[0];
	this.ac = document.getElementById(arguments[1]);
	this.ac.value = this.default_text;
	this.ac.setAttribute('autocomplete', 'off');
	this.pn = document.getElementById(arguments[2]);
	this.results = new Array();
	this.onselect = Snb.Event();
	this.onclear = Snb.Event();
	this.wrap = document.createElement('div');
	var parent = this.ac.parentNode;
	parent.replaceChild(this.wrap, this.ac);
	this.wrap.appendChild(this.ac);
	this.wrap.appendChild(this.pn);
	this.wrap.style.zIndex = arguments[3] || 200;
	this.wrap.className = 'snb-autocomplete';
	this.gc = Snb.Geocoder(this.ref+'.gc');
	if(arguments[4]!=null){
		this.autoFill = (arguments[4]<0||arguments[4]>1) ? 1 : arguments[4];
	}else{
		this.autoFill = 1;
	}	
	this.gc.oncallback.addListener(bind(this, function(obj) {
		// if changeblock is true, invoke callapi()
		if (this.ac.changeblock) { 
			this.callapi();
		}
		else {
			this.ac.callbackblock=0; // set callbackblock to false
			this.ac.prevvalue = this.ac.value;
			this.results = new Array();
			if (obj.status == 200) {
				var items = '';
				for (var i=0; i<obj.pois.length; i++) {
					if (i < 10) {
						this.results[i] = obj.pois[i];
						items += '<li onclick="'+this.ref+'.select('+i+');">'+obj.pois[i].name+'</li>';
					}
				}
				this.pn.innerHTML = '<div class="border"><ul>'+items+'</ul></div>';
				if(this.autoFill==1){
					this.select(0);
				}
			}
			else {
				this.pn.innerHTML = '<div class="border"><ul><li>'+this.no_results_text+'</li></ul></div>';	
			}
		}
	}));
	this.ac.onkeydown = bind(this, function(e) {
		this.keycode = window.event ? window.event.keyCode : e.which; 
		if (navigator.appName!='Opera') this.onkey();
	});
	this.ac.onkeypress = bind(this, function(e) { 
		if (navigator.appName=='Opera') this.onkey(); // only opera
	});
	this.ac.onmouseover = bind(this, function(e) { this.toggle(1) });
	this.pn.onmouseover = bind(this, function(e) { this.toggle(1) });
	this.ac.onmouseout = bind(this, function(e) { this.toggle(0) });
	this.pn.onmouseout = bind(this, function(e) { this.toggle(0) });
	this.ac.onfocus = bind(this, function(e) { if (this.ac.value == this.default_text) this.ac.value = ''; this.toggle(1) });
	this.ac.onblur = bind(this, function(e) { if (this.ac.value == '') this.ac.value = this.default_text; this.toggle(0) });
}
Snb.autocomplete.prototype = {
	onkey : function() {
		switch(this.keycode) {
			case 38: // up
				if (this.results.length) {
					this.cursor = this.cursor-1==-1 ? this.results.length-1 : this.cursor-1;
					this.select(this.cursor);
					this.ac.value = this.results[this.cursor].name.replace(/&#34;/g, '"').replace(/&#39;/g, "'");
				} break;
			case 40: // down
				if (this.results.length) {
					this.cursor = this.cursor+1==this.results.length ? 0 : this.cursor+1;
					this.select(this.cursor);
					this.ac.value = this.results[this.cursor].name.replace(/&#34;/g, '"').replace(/&#39;/g, "'");
				} break;
			default:
				// if keyblock is true, stop timer
				if (this.ac.keyblock) clearTimeout(this.ac.keyblock);
				var ref = this;
				this.ac.keyblock = setTimeout( function() {								
					// if callbackblock is true, set changeblock to true, else invoke callapi()
					ref.ac.callbackblock ? ref.ac.changeblock=1 : ref.callapi();
				}, 500); break;
		}
	},
	callapi : function() {
		if (this.ac.value && this.ac.value != this.ac.prevvalue) {
			this.pn.innerHTML = '<div class="border"><ul><li>'+this.loading_text+'</li></ul></div>';
			this.ac.changeblock=0; // set changeblock to false
			this.ac.callbackblock=1; // set callbackblock to true
			this.gc.setQuery(this.ac.value).getPOIs();
		}
		else {
			if (!this.ac.value) this.clear();
			this.ac.callbackblock=0; // set callbackblock to false
		}
	},
	select : function() {
		var index = arguments[0];
		var li = this.pn.getElementsByTagName('li');
		for (var i=0; i<li.length; i++) {
			li[i].className = i == index ? 'highlight' : '';
		}
		var result = this.results[index];
		this.cursor = index;
		this.ac.value = result.name.replace(/&#34;/g, '"').replace(/&#39;/g, "'");
		this.onselect.trigger(result);
	},
	toggle : function() { // toggle panel
		this.pn.toggle = arguments[0];
		if (this.pn.timer) clearTimeout(this.pn.timer); // clear existing timer
		var ref = this;
		this.pn.timer = setTimeout(function() { ref.pn.style.display = (ref.pn.toggle ? 'block' : 'none') }, 250);
	},
	clear : function() {
		this.results = new Array();
		this.pn.innerHTML = '';
		this.ac.prevvalue = ''; 
		this.ac.value = '';
		this.onclear.trigger(this);
		return this;
	},
	populate : function() {
		this.ac.value = arguments[0].replace(/&#34;/g, '"').replace(/&#39;/g, "'");
		this.onkey();
		return this;
	}
}

/**
 * Geocoder class
 */
Snb.Geocoder = function(ref) {
	return new Snb.geocoder(ref);
}
Snb.geocoder = function() {
	this.ref = arguments[0];
	this.query = '';
	this.tags = '';
	this.owners = '';
	this.locale = '';
	this.country = '';
	this.oncallback = Snb.Event();
}
Snb.geocoder.prototype = {
	getQuery 	: function() { return this.query; },
	getTags 	: function() { return this.tags; },
	getOwners 	: function() { return this.owners; },
	getLocale 	: function() { return this.locale; },
	getCountry 	: function() { return this.country; },
	setQuery 	: function() { this.query = arguments[0]; return this; },
	setTags 	: function() { this.tags = arguments[0]; return this; },
	setOwners 	: function() { this.owners = arguments[0]; return this; },
	setLocale 	: function() { this.locale = arguments[0]; return this; },
	setCountry 	: function() { this.country = arguments[0]; return this; },
	clear 		: function() { this.query, this.tags, this.owners, this.locale, this.country = ''; return this; },
	getPOIs		: function() {
		var el = document.createElement('script');
		el.type = 'text/javascript';
		el.src = 'http://api.shownearby.com/search/?q='+this.query+'&t='+this.tags+'&a='+this.owners+'&l='+this.locale+'&c='+this.country+'&callback='+this.ref+'.callback';
		document.getElementsByTagName("head")[0].appendChild(el); return this;
	},
	callback	: function() {
		var obj = eval('('+arguments[0]+')');
		if (obj.pois) {
			for (var i=0; i<obj.pois.length; i++) {
				var directions = '';
				obj.pois[i].country ? directions += obj.pois[i].country+' ':'';
				obj.pois[i].postal ? directions += obj.pois[i].postal+' ':'';
				obj.pois[i].state ? directions += obj.pois[i].state+' ':'';
				obj.pois[i].city ? directions += obj.pois[i].city+' ':'';
				obj.pois[i].locality ? directions += obj.pois[i].locality+' ':'';
				obj.pois[i].road ? directions += obj.pois[i].road+' ':'';
				obj.pois[i].block ? directions += obj.pois[i].block+' ':'';
				obj.pois[i].directions = strToSlug(directions);
				if (!obj.pois[i].address) {
					var address = '';
					obj.pois[i].country ? address += obj.pois[i].country+', ':'';
					obj.pois[i].postal ? address += obj.pois[i].postal+'<br />':'';
					obj.pois[i].state ? address += obj.pois[i].state+', ':'';
					obj.pois[i].city ? address += obj.pois[i].city+', ':'';
					obj.pois[i].locality ? address += obj.pois[i].locality+', ':'';
					obj.pois[i].road ? address += obj.pois[i].road+', ':'';
					obj.pois[i].block ? address += obj.pois[i].block+', ':'';
					obj.pois[i].building ? address += obj.pois[i].building+', ':'';
					obj.pois[i].floor ? address += obj.pois[i].floor+', ':'';
					obj.pois[i].unit ? address += obj.pois[i].unit+', ':'';
					if (address.substr(address.length-2, 2) == ', ')
						address = address.substr(0, address.length-2);
					obj.pois[i].address = address;
				}
				if (obj.pois[i].attributes) {
					var xml = parseXML(obj.pois[i].attributes.replace(/&#34;/g,'"'));
					var children = xml.documentElement.childNodes;
					obj.pois[i].attributes = new Array();
					for (var j=0; j<children.length; j++) {
						obj.pois[i].attributes[j] = new Object;
						obj.pois[i].attributes[j].key = children[j].getAttribute('key');
						obj.pois[i].attributes[j].value = children[j].getAttribute('value');
					}
				}
				if (obj.pois[i].pattributes) {
					var xml = parseXML(obj.pois[i].pattributes.replace(/&#34;/g,'"'));
					var children = xml.documentElement.childNodes;
					obj.pois[i].pattributes = new Array();
					for (var j=0; j<children.length; j++) {
						obj.pois[i].pattributes[children[j].getAttribute('key')] = children[j].getAttribute('value');
					}
				}
				obj.pois[i].html = '<div style="font-weight:bold; color:#333;">'+obj.pois[i].name+'</div>'+obj.pois[i].address+'<br />';
				if (obj.pois[i].website) obj.pois[i].html += '<small style="font-size:10px; color:#333;">Website:</small> <a href="'+(obj.pois[i].website.substr(0,7)!='http://'?'http://':'')+obj.pois[i].website+'" target="_blank">'+obj.pois[i].website+'</a><br />';
				if (obj.pois[i].email) obj.pois[i].html += '<small style="font-size:10px; color:#333;">Email:</small> <a href="mailto:'+obj.pois[i].email+'">'+obj.pois[i].email+'</a><br />';
				if (obj.pois[i].phone) obj.pois[i].html += '<small style="font-size:10px; color:#333;">Phone:</small> '+obj.pois[i].phone+'<br />';
				if (obj.pois[i].attributes) for (var j=0; j<obj.pois[i].attributes.length; j++) obj.pois[i].html += '<small style="font-size:10px; color:#333;">'+obj.pois[i].attributes[j].key+':</small> '+obj.pois[i].attributes[j].value+'<br />';
			}
		}
		this.oncallback.trigger(obj);
	}
}

/**
 * Convert string to slug
 */
function strToSlug(str) {
	str = str.replace(/&#34;/g, ' ').replace(/&#39;/g, ' ') // replace encoded single and double quotes
	.replace(/[~!@#\$%\^&\*\(\)_\+\{\}\|:"<>\?`\-=\[\]\\;',\.\/]/g, ' ') // replace the following characters ~!@#$%^&*()_+{}|:"<>?`-=[]\;',./
	.replace(/^\s\s*/, '').replace(/\s\s*$/, '') // trim left and right
	.replace(/\s+/g, '-') // collapse whitespaces and replace with hyphen
	.toLowerCase();
	return str;
}

/**
 * Array removeIndex
 * By John Resig (MIT Licensed) http://ejohn.org/blog/javascript-array-remove/
 */
function removeIndex(array, from, to) {
  var rest = array.slice((to || from) + 1 || array.length);
  array.length = from < 0 ? array.length + from : from;
  return array.push.apply(array, rest);
};

/**
 * Array getIndex
 */
function getIndex(array, obj) {
	for (var i=0; i<array.length; i++) {
		if (array[i] === obj) return i;
	}
	return -1;
}

/**
 * Bind scope to function
 */
function bind(scope, fn) {
	return function () {
		fn.apply(scope, arguments);
	};
};

/**
 * Cross browser XML String to Object
 */
function parseXML(str) {
	var obj;
	try { //Internet Explorer
		obj = new ActiveXObject("Microsoft.XMLDOM");
		obj.async = "false";
		obj.loadXML(str);
	}
	catch (e) {
		try { //Firefox, Mozilla, Opera, etc.
			parser = new DOMParser();
			obj = parser.parseFromString(str,"text/xml");
		}
		catch (e) {
			alert(e.message); 
			return;
		}
	}
	return obj;
}

/**
 * Disable microsoft event
 */
function disableMicrosoftEvent() { 
	return true; 
}

/**
 * Google map function
 */
if (typeof(GMap2) == 'function') {
	// MarkerClusterer by (c) Xiaoxi Wu - http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/1.0/docs/reference.html
	//eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('7 3u(n,x,y){4 o=[];4 m=n;4 u=A;4 r=3;4 s=2r;4 z=[3t,3o,3l,3g,3a];4 t=[];4 v=[];4 p=A;4 q=F;4 w=7(a){4 d=0;4 b=a.9;4 c=b;3s(c!==0){c=1A(c/10,10);d++}4 e=3.1i().9;6(e<d){d=e}8{\'V\':b,\'16\':d}};4 i=0;C(i=1;i<=5;++i){t.L({\'1n\':\'2L://2K-2H-2D.2A.2w/2t/2s/2q/2p/m\'+i+\'.2n\',\'U\':z[i-1],\'Z\':z[i-1]})}6(G y===\'12\'&&y!==A){6(G y.1G===\'18\'&&y.1G>0){s=y.1G}6(G y.2e===\'18\'){u=y.2e}6(G y.1c===\'12\'&&y.1c!==A&&y.1c.9!==0){t=y.1c}6(G y.2b===\'7\'){w=y.2b}6(G y.28===\'27\'){q=y.28}}3.3m=7(a){w=a};3.26=7(){8 19.3k(3,w)};3.25=7(){8 q};7 24(){6(v.9===0){8}4 a=[];C(i=0;i<v.9;++i){6(1t(v[i])){r.Q(v[i],F,A,A,F)}M{a.L(v[i])}}v=a}3.1i=7(){8 t};3.15=7(){C(4 i=0;i<o.9;++i){6(G o[i]!=="39"&&o[i]!==A){o[i].15()}}o=[];v=[]};7 1t(a){8 m.1o().37(a.1l())}7 1Y(a){4 c=a.9;4 b=[];C(4 i=c-1;i>=0;--i){r.Q(a[i].B,F,a[i].K,b,F)}24()}3.Q=7(g,j,b,h,a){6(a!==F){6(!1t(g)){v.L(g);8}}4 f=b;4 d=h;4 e=m.T(g.1l());6(G f!=="27"){f=O}6(G d!=="12"||d===A){d=o}4 k=d.9;4 c=A;C(4 i=k-1;i>=0;i--){c=d[i];4 l=c.1U();6(l===A){1T}l=m.T(l);6(e.x>=l.x-s&&e.x<=l.x+s&&e.y>=l.y-s&&e.y<=l.y+s){c.Q({\'K\':f,\'B\':g});6(!j){c.N()}8}}c=S 1P(3,n);c.Q({\'K\':f,\'B\':g});6(!j){c.N()}d.L(c);6(d!==o){o.L(c)}};3.1B=7(a){C(4 i=0;i<o.9;++i){6(o[i]&&o[i].1B(a)){o[i].N();8}}};3.N=7(){4 a=3.1u();C(4 i=0;i<a.9;++i){a[i].N(F)}};3.1u=7(){4 b=[];4 a=m.1o();C(4 i=0;i<o.9;i++){6(o[i].1j(a)){b.L(o[i])}}8 b};3.23=7(){8 u};3.1O=7(){8 m};3.1w=7(){8 s};3.X=7(){4 a=0;C(4 i=0;i<o.9;++i){a+=o[i].X()}8 a};3.2u=7(){8 o.9};3.1N=7(){4 d=3.1u();4 e=[];4 f=0;C(4 i=0;i<d.9;++i){4 c=d[i];4 b=c.1L();6(b===A){1T}4 a=m.Y();6(a!==b){4 h=c.1K();C(4 j=0;j<h.9;++j){4 g={\'K\':O,\'B\':h[j].B};e.L(g)}c.15();f++;C(j=0;j<o.9;++j){6(c===o[j]){o.1J(j,1)}}}}1Y(e);3.N()};3.1I=7(a){C(4 i=0;i<a.9;++i){3.Q(a[i],F)}3.N()};3.2o=7(a){8 a.14};6(G x==="12"&&x!==A){3.1I(x)}p=19.2m(m,"2l",7(){r.1N()})}7 1P(h){4 p=A;4 o=[];4 n=h;4 j=h.1O();4 m=A;4 k=j.Y();4 l=3;3.1K=7(){8 o};3.1g=7(){8 n};3.1j=7(c){6(p===A){8 O}6(!c){c=j.1o()}4 g=j.T(c.2k());4 a=j.T(c.2j());4 b=j.T(p);4 e=F;4 f=h.1w();6(k!==j.Y()){4 d=j.Y()-k;f=2i.2h(2,d)*f}6(a.x!==g.x&&(b.x+f<g.x||b.x-f>a.x)){e=O}6(e&&(b.y+f<a.y||b.y-f>g.y)){e=O}8 e};3.1U=7(){8 p};3.Q=7(a){6(p===A){p=a.B.1l()}a.B.14=l;o.L(a)};3.1B=7(a){C(4 i=0;i<o.9;++i){6(a===o[i].B){6(o[i].K){j.1f(o[i].B)}1H o[i].B.14;o.1J(i,1);8 F}}8 O};3.1L=7(){8 k};3.N=7(a){6(!a&&!3.1j()){8}k=j.Y();4 i=0;4 b=h.23();6(b===A){b=j.2g().2f()}6(k>b||3.X()===1){C(i=0;i<o.9;++i){6(o[i].K){6(o[i].B.1d()){o[i].B.1D()}}M{j.2d(o[i].B);o[i].K=F}}6(m!==A){m.1C()}}M 6(3.X()>1){C(i=0;i<o.9;++i){6(o[i].K&&(!o[i].B.1d())){o[i].B.1C()}}4 c=n.26()(3.2c());6(m===A){m=S E(p,c,n.1i(),n.1w(),l);j.2d(m)}M{6(m.1d()){m.1D()}m.2a(c);m.29(F)}}};3.15=7(){6(m!==A){j.1f(m)}C(4 i=0;i<o.9;++i){6(o[i].K){j.1f(o[i].B)}1H o[i].B.14}o=[]};3.X=7(){8 o.9};3.2c=7(){4 a=[];C(4 i=0;i<o.9;++i){a.L(o[i].B)}8 a}}7 E(a,d,c,b,f){4 e=d.16;3.1F(c[e-1]);3.1b=O;3.1a=a;3.17=e;3.1z=c;3.13=d.V;3.1y=b;3.1e=d;3.1x=f}E.I=S 3i();E.I.1F=7(a){3.1h=a.1n;3.P=a.U;3.W=a.Z;3.1v=a.3h;3.H=a.3e;3.1s=a.3d};E.I.3c=7(i){3.22=i;4 j=21.3b("38");4 h=3.1a;4 g=3.1p(h);j.R.20=3.1m(g);j.1Z=3.13;i.36(35).34(j);4 f=3.1y;4 e=3.1x;19.33(j,"32",7(){19.31(e.1g(),"30",e);6(e.1g().25()){4 a=i.T(h);4 d=S 1W(a.x-f,a.y+f);d=i.1V(d);4 b=S 1W(a.x+f,a.y-f);b=i.1V(b);4 c=i.2Y(S 2X(d,b),i.2W());i.2V(h,c)}});3.J=j};E.I.1p=7(b){4 a=3.22.T(b);a.x-=1A(3.W/2,10);a.y-=1A(3.P/2,10);8 a};E.I.1m=7(a){4 b="";6(21.2U){b=\'2T:2S:2R.2Q.2P(2O=2Z,2N="\'+3.1h+\'");\'}M{b="2M:1n("+3.1h+");"}6(G 3.H==="12"){6(G 3.H[0]==="18"&&3.H[0]>0&&3.H[0]<3.P){b+=\'U:\'+(3.P-3.H[0])+\'D;1X-1q:\'+3.H[0]+\'D;\'}M{b+=\'U:\'+3.P+\'D;1S-U:\'+3.P+\'D;\'}6(G 3.H[1]==="18"&&3.H[1]>0&&3.H[1]<3.W){b+=\'Z:\'+(3.W-3.H[1])+\'D;1X-1k:\'+3.H[1]+\'D;\'}M{b+=\'Z:\'+3.W+\'D;V-1R:1Q;\'}}M{b+=\'U:\'+3.P+\'D;1S-U:\'+3.P+\'D;\';b+=\'Z:\'+3.W+\'D;V-1R:1Q;\'}4 d=3.1v?3.1v:\'2J\';4 c=3.1s?3.1s:11;8 b+\'2I:2G;1q:\'+a.y+"D;1k:"+a.x+"D;2F:"+d+";2E:3f;1r-2C:"+c+"D;"+\'1r-2B:3j,2z-2y;1r-2x:3n\'};E.I.2v=7(){3.J.3p.3q(3.J)};E.I.3r=7(){8 S E(3.1a,3.1e,3.13,3.1z,3.1y,3.1x)};E.I.29=7(a){6(!a){8}4 b=3.1p(3.1a);6(3.1b){3.1b=O;3.1F(3.1z[3.17-1]);3.J.R.20=3.1m(b)}M{3.J.R.1q=b.y+"D";3.J.R.1k=b.x+"D"}};E.I.1C=7(){3.J.R.1E="1M"};E.I.1D=7(){3.J.R.1E=""};E.I.1d=7(){8 3.J.R.1E==="1M"};E.I.2a=7(a){6(a.16!==3.17){3.1b=F}3.1e=a;3.13=a.V;3.17=a.16;3.J.1Z=a.V};',62,217,'|||this|var||if|function|return|length|||||||||||||||||||||||||||null|marker|for|px|ClusterMarker_|true|typeof|anchor_|prototype|div_|isAdded|push|else|redraw_|false|height_|addMarker|style|new|fromLatLngToDivPixel|height|text|width_|getTotalMarkers|getZoom|width|||object|text_|parentCluster_|clearMarkers|index|index_|number|GEvent|latlng_|styleDirty_|styles|isHidden|sums_|removeOverlay|getMarkerClusterer|url_|getStyles|isInBounds|left|getLatLng|createCss|url|getBounds|getPosFromLatLng|top|font|textSize_|isMarkerInViewport_|getClustersInViewport_|textColor_|getGridSize_|cluster_|padding_|styles_|parseInt|removeMarker|hide|show|display|useStyle|gridSize|delete|addMarkers|splice|getMarkers|getCurrentZoom|none|resetViewport|getMap_|Cluster|center|align|line|continue|getCenter|fromDivPixelToLatLng|GPoint|padding|reAddMarkers_|innerHTML|cssText|document|map_|getMaxZoom_|addLeftMarkers_|isZoomOnClick|getCalculator|boolean|zoomOnClick|redraw|setSums|calculator|getRealMarkers|addOverlay|maxZoom|getMaximumResolution|getCurrentMapType|pow|Math|getNorthEast|getSouthWest|moveend|addListener|png|getParentCluster|images|markerclusterer|60|trunk|svn|getTotalClusters|remove|com|weight|serif|sans|googlecode|family|size|library|position|color|pointer|utility|cursor|black|gmaps|http|background|src|sizingMethod|AlphaImageLoader|Microsoft|DXImageTransform|progid|filter|all|setCenter|getSize|GLatLngBounds|getBoundsZoomLevel|scale|clusterclick|trigger|click|addDomListener|appendChild|G_MAP_MAP_PANE|getPane|containsLatLng|div|undefined|90|createElement|initialize|opt_textSize|opt_anchor|absolute|78|opt_textColor|GOverlay|Arial|callback|66|setCalculator|bold|56|parentNode|removeChild|copy|while|53|MarkerClusterer'.split('|'),0,{}))

	// modified - added fontsize
	var a;
	function MarkerClusterer(f,j,c){function i(){if(v.length!==0){for(r=0;r<v.length;++r)s.addMarker(v[r],true,null,null,true);v=[]}}function e(d){return h.getBounds().containsLatLng(d.getLatLng())}function l(d){for(var g=[],k=d.length-1;k>=0;--k)s.addMarker(d[k].marker,true,d[k].isAdded,g,true);i()}var b=[],h=f,t=null,s=this,n=60,w=[53,56,66,78,90],x=[],v=[],y=null,r=0;for(r=1;r<=5;++r)x.push({url:"http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/images/m"+r+".png",height:w[r-1],
	width:w[r-1]});if(typeof c==="object"&&c!==null){if(typeof c.gridSize==="number"&&c.gridSize>0)n=c.gridSize;if(typeof c.maxZoom==="number")t=c.maxZoom;if(typeof c.styles==="object"&&c.styles!==null&&c.styles.length!==0)x=c.styles}this.getStyles_=function(){return x};this.clearMarkers=function(){for(var d=0;d<b.length;++d)typeof b[d]!=="undefined"&&b[d]!==null&&b[d].clearMarkers();b=[];v=[];GEvent.removeListener(y)};this.addMarker=function(d,g,k,o,q){if(q!==true)if(!e(d)){v.push(d);return}k=k;o=o;
	q=h.fromLatLngToDivPixel(d.getLatLng());if(typeof k!=="boolean")k=false;if(typeof o!=="object"||o===null)o=b;for(var m=null,p=o.length-1;p>=0;p--){m=o[p];var u=m.getCenter();if(u!==null){u=h.fromLatLngToDivPixel(u);if(q.x>=u.x-n&&q.x<=u.x+n&&q.y>=u.y-n&&q.y<=u.y+n){m.addMarker({isAdded:k,marker:d});g||m.redraw_();return}}}m=new Cluster(this,f);m.addMarker({isAdded:k,marker:d});g||m.redraw_();o.push(m);o!==b&&b.push(m)};this.removeMarker=function(d){for(var g=0;g<b.length;++g)if(b[g].remove(d)){b[g].redraw_();
	return}};this.redraw_=function(){for(var d=this.getClustersInViewport_(),g=0;g<d.length;++g)d[g].redraw_(true)};this.getClustersInViewport_=function(){for(var d=[],g=h.getBounds(),k=0;k<b.length;k++)b[k].isInBounds(g)&&d.push(b[k]);return d};this.getMaxZoom_=function(){return t};this.getMap_=function(){return h};this.getGridSize_=function(){return n};this.getTotalMarkers=function(){for(var d=0,g=0;g<b.length;++g)d+=b[g].getTotalMarkers();return d};this.getTotalClusters=function(){return b.length};
	this.resetViewport=function(){for(var d=this.getClustersInViewport_(),g=[],k=0,o=0;o<d.length;++o){var q=d[o],m=q.getCurrentZoom();if(m!==null)if(h.getZoom()!==m){m=q.getMarkers();for(var p=0;p<m.length;++p)g.push({isAdded:false,marker:m[p].marker});q.clearMarkers();k++;for(p=0;p<b.length;++p)q===b[p]&&b.splice(p,1)}}l(g);this.redraw_()};this.addMarkers=function(d){for(var g=0;g<d.length;++g)this.addMarker(d[g],true);this.redraw_()};typeof j==="object"&&j!==null&&this.addMarkers(j);y=GEvent.addListener(h,
	"moveend",function(){s.resetViewport()})}
	function Cluster(f){var j=null,c=[],i=f.getMap_(),e=null,l=i.getZoom();this.getMarkers=function(){return c};this.isInBounds=function(b){if(j===null)return false;b||(b=i.getBounds());var h=i.fromLatLngToDivPixel(b.getSouthWest());b=i.fromLatLngToDivPixel(b.getNorthEast());var t=i.fromLatLngToDivPixel(j),s=true,n=f.getGridSize_();if(l!==i.getZoom()){var w=i.getZoom()-l;n=Math.pow(2,w)*n}if(b.x!==h.x&&(t.x+n<h.x||t.x-n>b.x))s=false;if(s&&(t.y+n<b.y||t.y-n>h.y))s=false;return s};this.getCenter=function(){return j};
	this.addMarker=function(b){if(j===null)j=b.marker.getLatLng();c.push(b)};this.removeMarker=function(b){for(var h=0;h<c.length;++h)if(b===c[h].marker){c[h].isAdded&&i.removeOverlay(c[h].marker);c.splice(h,1);return true}return false};this.getCurrentZoom=function(){return l};this.redraw_=function(b){if(b||this.isInBounds()){l=i.getZoom();b=0;b=f.getMaxZoom_();if(b===null)b=i.getCurrentMapType().getMaximumResolution();if(l>=b||this.getTotalMarkers()===1){for(b=0;b<c.length;++b)if(c[b].isAdded)c[b].marker.isHidden()&&
	c[b].marker.show();else{i.addOverlay(c[b].marker);c[b].isAdded=true}e!==null&&e.hide()}else{for(b=0;b<c.length;++b)c[b].isAdded&&!c[b].marker.isHidden()&&c[b].marker.hide();if(e===null){e=new ClusterMarker_(j,this.getTotalMarkers(),f.getStyles_(),f.getGridSize_());i.addOverlay(e)}else{e.isHidden()&&e.show();e.redraw(true)}}}};this.clearMarkers=function(){e!==null&&i.removeOverlay(e);for(var b=0;b<c.length;++b)c[b].isAdded&&i.removeOverlay(c[b].marker);c=[]};this.getTotalMarkers=function(){return c.length}}
	function ClusterMarker_(f,j,c,i){for(var e=0,l=j;l!==0;){l=parseInt(l/10,10);e++}if(c.length<e)e=c.length;this.url_=c[e-1].url;this.height_=c[e-1].height;this.width_=c[e-1].width;this.textColor_=c[e-1].opt_textColor;this.fontSize_=c[e-1].opt_fontSize;this.anchor_=c[e-1].opt_anchor;this.latlng_=f;this.index_=e;this.styles_=c;this.text_=j;this.padding_=i}ClusterMarker_.prototype=new GOverlay;a=ClusterMarker_.prototype;
	a.initialize=function(f){this.map_=f;var j=document.createElement("div"),c=this.latlng_,i=f.fromLatLngToDivPixel(c);i.x-=parseInt(this.width_/2,10);i.y-=parseInt(this.height_/2,10);var e="";e=document.all?'filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale,src="'+this.url_+'");':"background:url("+this.url_+");";if(typeof this.anchor_==="object"){e+=typeof this.anchor_[0]==="number"&&this.anchor_[0]>0&&this.anchor_[0]<this.height_?"height:"+(this.height_-this.anchor_[0])+
	"px;padding-top:"+this.anchor_[0]+"px;":"height:"+this.height_+"px;line-height:"+this.height_+"px;";e+=typeof this.anchor_[1]==="number"&&this.anchor_[1]>0&&this.anchor_[1]<this.width_?"width:"+(this.width_-this.anchor_[1])+"px;padding-left:"+this.anchor_[1]+"px;":"width:"+this.width_+"px;text-align:center;"}else{e+="height:"+this.height_+"px;line-height:"+this.height_+"px;";e+="width:"+this.width_+"px;text-align:center;"}j.style.cssText=e+"cursor:pointer;top:"+i.y+"px;left:"+i.x+"px;color:"+(this.textColor_?
	this.textColor_:"black")+";position:absolute;font-size:"+(this.fontSize_?this.fontSize_:"11px")+";font-family:Arial,sans-serif;font-weight:bold";j.innerHTML=this.text_;f.getPane(G_MAP_MAP_PANE).appendChild(j);var l=this.padding_;GEvent.addDomListener(j,"click",function(){var b=f.fromLatLngToDivPixel(c),h=new GPoint(b.x-l,b.y+l);h=f.fromDivPixelToLatLng(h);b=new GPoint(b.x+l,b.y-l);b=f.fromDivPixelToLatLng(b);h=f.getBoundsZoomLevel(new GLatLngBounds(h,b),f.getSize());f.setCenter(c,h)});this.div_=j};
	a.remove=function(){this.div_.parentNode.removeChild(this.div_)};a.copy=function(){return new ClusterMarker_(this.latlng_,this.index_,this.text_,this.styles_,this.padding_)};a.redraw=function(f){if(f){f=this.map_.fromLatLngToDivPixel(this.latlng_);f.x-=parseInt(this.width_/2,10);f.y-=parseInt(this.height_/2,10);this.div_.style.top=f.y+"px";this.div_.style.left=f.x+"px"}};a.hide=function(){this.div_.style.display="none"};a.show=function(){this.div_.style.display=""};
	a.isHidden=function(){return this.div_.style.display==="none"};	
	
	// GMap2 showBounds() by (c) Esa - http://esa.ilmari.googlepages.com/showbounds.htm
	eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('m.n.s=r(c,6){4 3=6||{};3.a=6.a*1||0;3.8=6.8*1||0;3.7=6.7*1||0;3.9=6.9*1||0;4 f=5.p();4 d=5.q();4 g=i o(d.k-3.8-3.9,d.l-3.a-3.7);5.u(f.t(c,g));4 j=(3.8-3.9)/2;4 e=(3.a-3.7)/2;4 b=5.C(c.v());4 h=5.w(i A(b.x-j,b.y-e));5.B(h);5.z()}',39,39,'|||opts|var|this|opt_options|bottom|left|right|top|bPxCenter|bounds|pt|yOffs|mt|vp|newCenter|new|xOffs|width|height|GMap2|prototype|GSize|getCurrentMapType|getSize|function|showBounds|getBoundsZoomLevel|setZoom|getCenter|fromDivPixelToLatLng|||savePosition|GPoint|panTo|fromLatLngToDivPixel'.split('|'),0,{}))
}

	

})();