function MarkerClusterer(map,opt_markers,opt_options){this.extend(MarkerClusterer,google.maps.OverlayView);this.map_=map;this.markers_=[];this.clusters_=[];this.sizes=[53,56,66,78,90];this.styles_=[];this.ready_=false;var options=opt_options||{};this.gridSize_=options["gridSize"]||60;this.minClusterSize_=options["minimumClusterSize"]||2;this.maxZoom_=options["maxZoom"]||null;this.styles_=options["styles"]||[];this.imagePath_=options["imagePath"]||this.MARKER_CLUSTER_IMAGE_PATH_;this.imageExtension_=options["imageExtension"]||this.MARKER_CLUSTER_IMAGE_EXTENSION_;this.zoomOnClick_=true;if(options["zoomOnClick"]!=undefined)this.zoomOnClick_=options["zoomOnClick"];this.averageCenter_=false;if(options["averageCenter"]!=undefined)this.averageCenter_=options["averageCenter"];this.setupStyles_();this.setMap(map);this.prevZoom_=this.map_.getZoom();var that=this;google.maps.event.addListener(this.map_,"zoom_changed",function(){var maxZoom=that.map_.mapTypes[that.map_.getMapTypeId()].maxZoom;var zoom=that.map_.getZoom();if(zoom<0||zoom>maxZoom)return;if(that.prevZoom_!=zoom){that.prevZoom_=that.map_.getZoom();that.resetViewport()}});google.maps.event.addListener(this.map_,"idle",function(){that.redraw()});if(opt_markers&&opt_markers.length)this.addMarkers(opt_markers,false)}MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_="//google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/"+"images/m";MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_="png";MarkerClusterer.prototype.extend=function(obj1,obj2){return function(object){for(var property in object.prototype)this.prototype[property]=object.prototype[property];return this}.apply(obj1,[obj2])};MarkerClusterer.prototype.onAdd=function(){this.setReady_(true)};MarkerClusterer.prototype.draw=function(){};MarkerClusterer.prototype.setupStyles_=function(){if(this.styles_.length)return;for(var i=0,size;size=this.sizes[i];i++)this.styles_.push({url:this.imagePath_+(i+1)+"."+this.imageExtension_,height:size,width:size})};MarkerClusterer.prototype.fitMapToMarkers=function(){var markers=this.getMarkers();var bounds=new google.maps.LatLngBounds;for(var i=0,marker;marker=markers[i];i++)bounds.extend(marker.getPosition());this.map_.fitBounds(bounds)};MarkerClusterer.prototype.setStyles=function(styles){this.styles_=styles};MarkerClusterer.prototype.getStyles=function(){return this.styles_};MarkerClusterer.prototype.isZoomOnClick=function(){return this.zoomOnClick_};MarkerClusterer.prototype.isAverageCenter=function(){return this.averageCenter_};MarkerClusterer.prototype.getMarkers=function(){return this.markers_};MarkerClusterer.prototype.getTotalMarkers=function(){return this.markers_.length};MarkerClusterer.prototype.setMaxZoom=function(maxZoom){this.maxZoom_=maxZoom};MarkerClusterer.prototype.getMaxZoom=function(){return this.maxZoom_||this.map_.mapTypes[this.map_.getMapTypeId()].maxZoom};MarkerClusterer.prototype.calculator_=function(markers,numStyles){var index=0;var count=markers.length;var dv=count;while(dv!==0){dv=parseInt(dv/10,10);index++}index=Math.min(index,numStyles);return{text:count,index:index}};MarkerClusterer.prototype.setCalculator=function(calculator){this.calculator_=calculator};MarkerClusterer.prototype.getCalculator=function(){return this.calculator_};MarkerClusterer.prototype.addMarkers=function(markers,opt_nodraw){for(var i=0,marker;marker=markers[i];i++)this.pushMarkerTo_(marker);if(!opt_nodraw)this.redraw()};MarkerClusterer.prototype.pushMarkerTo_=function(marker){marker.isAdded=false;if(marker["draggable"]){var that=this;google.maps.event.addListener(marker,"dragend",function(){marker.isAdded=false;that.repaint()})}this.markers_.push(marker)};MarkerClusterer.prototype.addMarker=function(marker,opt_nodraw){this.pushMarkerTo_(marker);if(!opt_nodraw)this.redraw()};MarkerClusterer.prototype.removeMarker_=function(marker){var index=-1;if(this.markers_.indexOf)index=this.markers_.indexOf(marker);else for(var i=0,m;m=this.markers_[i];i++)if(m==marker){index=i;break}if(index==-1)return false;this.markers_.splice(index,1);return true};MarkerClusterer.prototype.removeMarker=function(marker,opt_nodraw){var removed=this.removeMarker_(marker);if(!opt_nodraw&&removed){this.resetViewport();this.redraw();return true}else return false};MarkerClusterer.prototype.removeMarkers=function(markers,opt_nodraw){var removed=false;for(var i=0,marker;marker=markers[i];i++){var r=this.removeMarker_(marker);removed=removed||r}if(!opt_nodraw&&removed){this.resetViewport();this.redraw();return true}};MarkerClusterer.prototype.setReady_=function(ready){if(!this.ready_){this.ready_=ready;this.createClusters_()}};MarkerClusterer.prototype.getTotalClusters=function(){return this.clusters_.length};MarkerClusterer.prototype.getMap=function(){return this.map_};MarkerClusterer.prototype.setMap=function(map){this.map_=map};MarkerClusterer.prototype.getGridSize=function(){return this.gridSize_};MarkerClusterer.prototype.setGridSize=function(size){this.gridSize_=size};MarkerClusterer.prototype.getMinClusterSize=function(){return this.minClusterSize_};MarkerClusterer.prototype.setMinClusterSize=function(size){this.minClusterSize_=size};MarkerClusterer.prototype.getExtendedBounds=function(bounds){var projection=this.getProjection();var tr=new google.maps.LatLng(bounds.getNorthEast().lat(),bounds.getNorthEast().lng());var bl=new google.maps.LatLng(bounds.getSouthWest().lat(),bounds.getSouthWest().lng());var trPix=projection.fromLatLngToDivPixel(tr);trPix.x+=this.gridSize_;trPix.y-=this.gridSize_;var blPix=projection.fromLatLngToDivPixel(bl);blPix.x-=this.gridSize_;blPix.y+=this.gridSize_;var ne=projection.fromDivPixelToLatLng(trPix);var sw=projection.fromDivPixelToLatLng(blPix);bounds.extend(ne);bounds.extend(sw);return bounds};MarkerClusterer.prototype.isMarkerInBounds_=function(marker,bounds){return bounds.contains(marker.getPosition())};MarkerClusterer.prototype.clearMarkers=function(){this.resetViewport(true);this.markers_=[]};MarkerClusterer.prototype.resetViewport=function(opt_hide){for(var i=0,cluster;cluster=this.clusters_[i];i++)cluster.remove();for(var i=0,marker;marker=this.markers_[i];i++){marker.isAdded=false;if(opt_hide)marker.setMap(null)}this.clusters_=[]};MarkerClusterer.prototype.repaint=function(){var oldClusters=this.clusters_.slice();this.clusters_.length=0;this.resetViewport();this.redraw();window.setTimeout(function(){for(var i=0,cluster;cluster=oldClusters[i];i++)cluster.remove()},0)};MarkerClusterer.prototype.redraw=function(){this.createClusters_()};MarkerClusterer.prototype.distanceBetweenPoints_=function(p1,p2){if(!p1||!p2)return 0;var R=6371;var dLat=(p2.lat()-p1.lat())*Math.PI/180;var dLon=(p2.lng()-p1.lng())*Math.PI/180;var a=Math.sin(dLat/2)*Math.sin(dLat/2)+Math.cos(p1.lat()*Math.PI/180)*Math.cos(p2.lat()*Math.PI/180)*Math.sin(dLon/2)*Math.sin(dLon/2);var c=2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a));var d=R*c;return d};MarkerClusterer.prototype.addToClosestCluster_=function(marker){var distance=4E4;var clusterToAddTo=null;var pos=marker.getPosition();for(var i=0,cluster;cluster=this.clusters_[i];i++){var center=cluster.getCenter();if(center){var d=this.distanceBetweenPoints_(center,marker.getPosition());if(d<distance){distance=d;clusterToAddTo=cluster}}}if(clusterToAddTo&&clusterToAddTo.isMarkerInClusterBounds(marker))clusterToAddTo.addMarker(marker);else{var cluster=new Cluster(this);cluster.addMarker(marker);this.clusters_.push(cluster)}};MarkerClusterer.prototype.createClusters_=function(){if(!this.ready_)return;var mapBounds=new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(),this.map_.getBounds().getNorthEast());var bounds=this.getExtendedBounds(mapBounds);for(var i=0,marker;marker=this.markers_[i];i++)if(!marker.isAdded&&this.isMarkerInBounds_(marker,bounds))this.addToClosestCluster_(marker)};function Cluster(markerClusterer){this.markerClusterer_=markerClusterer;this.map_=markerClusterer.getMap();this.gridSize_=markerClusterer.getGridSize();this.minClusterSize_=markerClusterer.getMinClusterSize();this.averageCenter_=markerClusterer.isAverageCenter();this.center_=null;this.markers_=[];this.bounds_=null;this.clusterIcon_=new ClusterIcon(this,markerClusterer.getStyles(),markerClusterer.getGridSize())}Cluster.prototype.isMarkerAlreadyAdded=function(marker){if(this.markers_.indexOf)return this.markers_.indexOf(marker)!=-1;else for(var i=0,m;m=this.markers_[i];i++)if(m==marker)return true;return false};Cluster.prototype.addMarker=function(marker){if(this.isMarkerAlreadyAdded(marker))return false;if(!this.center_){this.center_=marker.getPosition();this.calculateBounds_()}else if(this.averageCenter_){var l=this.markers_.length+1;var lat=(this.center_.lat()*(l-1)+marker.getPosition().lat())/l;var lng=(this.center_.lng()*(l-1)+marker.getPosition().lng())/l;this.center_=new google.maps.LatLng(lat,lng);this.calculateBounds_()}marker.isAdded=true;this.markers_.push(marker);var len=this.markers_.length;if(len<this.minClusterSize_&&marker.getMap()!=this.map_)marker.setMap(this.map_);if(len==this.minClusterSize_)for(var i=0;i<len;i++)this.markers_[i].setMap(null);if(len>=this.minClusterSize_)marker.setMap(null);this.updateIcon();return true};Cluster.prototype.getMarkerClusterer=function(){return this.markerClusterer_};Cluster.prototype.getBounds=function(){var bounds=new google.maps.LatLngBounds(this.center_,this.center_);var markers=this.getMarkers();for(var i=0,marker;marker=markers[i];i++)bounds.extend(marker.getPosition());return bounds};Cluster.prototype.remove=function(){this.clusterIcon_.remove();this.markers_.length=0;delete this.markers_};Cluster.prototype.getSize=function(){return this.markers_.length};Cluster.prototype.getMarkers=function(){return this.markers_};Cluster.prototype.getCenter=function(){return this.center_};Cluster.prototype.calculateBounds_=function(){var bounds=new google.maps.LatLngBounds(this.center_,this.center_);this.bounds_=this.markerClusterer_.getExtendedBounds(bounds)};Cluster.prototype.isMarkerInClusterBounds=function(marker){return this.bounds_.contains(marker.getPosition())};Cluster.prototype.getMap=function(){return this.map_};Cluster.prototype.updateIcon=function(){var zoom=this.map_.getZoom();var mz=this.markerClusterer_.getMaxZoom();if(zoom>mz){for(var i=0,marker;marker=this.markers_[i];i++)marker.setMap(this.map_);return}if(this.markers_.length<this.minClusterSize_){this.clusterIcon_.hide();return}var numStyles=this.markerClusterer_.getStyles().length;var sums=this.markerClusterer_.getCalculator()(this.markers_,numStyles);this.clusterIcon_.setCenter(this.center_);this.clusterIcon_.setSums(sums);this.clusterIcon_.show()};function ClusterIcon(cluster,styles,opt_padding){cluster.getMarkerClusterer().extend(ClusterIcon,google.maps.OverlayView);this.styles_=styles;this.padding_=opt_padding||0;this.cluster_=cluster;this.center_=null;this.map_=cluster.getMap();this.div_=null;this.sums_=null;this.visible_=false;this.setMap(this.map_)}ClusterIcon.prototype.triggerClusterClick=function(){var markerClusterer=this.cluster_.getMarkerClusterer();google.maps.event.trigger(markerClusterer,"clusterclick",this.cluster_);if(markerClusterer.isZoomOnClick())this.map_.fitBounds(this.cluster_.getBounds())};ClusterIcon.prototype.onAdd=function(){this.div_=document.createElement("DIV");if(this.visible_){var pos=this.getPosFromLatLng_(this.center_);this.div_.style.cssText=this.createCss(pos);this.div_.innerHTML=this.sums_.text}var panes=this.getPanes();panes.overlayMouseTarget.appendChild(this.div_);var that=this;this.div_.addEventListener("click",function(){that.triggerClusterClick()})};ClusterIcon.prototype.getPosFromLatLng_=function(latlng){var pos=this.getProjection().fromLatLngToDivPixel(latlng);pos.x-=parseInt(this.width_/2,10);pos.y-=parseInt(this.height_/2,10);return pos};ClusterIcon.prototype.draw=function(){if(this.visible_){var pos=this.getPosFromLatLng_(this.center_);this.div_.style.top=pos.y+"px";this.div_.style.left=pos.x+"px"}};ClusterIcon.prototype.hide=function(){if(this.div_)this.div_.style.display="none";this.visible_=false};ClusterIcon.prototype.show=function(){if(this.div_){var pos=this.getPosFromLatLng_(this.center_);this.div_.style.cssText=this.createCss(pos);this.div_.style.display=""}this.visible_=true};ClusterIcon.prototype.remove=function(){this.setMap(null)};ClusterIcon.prototype.onRemove=function(){if(this.div_&&this.div_.parentNode){this.hide();this.div_.parentNode.removeChild(this.div_);this.div_=null}};ClusterIcon.prototype.setSums=function(sums){this.sums_=sums;this.text_=sums.text;this.index_=sums.index;if(this.div_)this.div_.innerHTML=sums.text;this.useStyle()};ClusterIcon.prototype.useStyle=function(){var index=Math.max(0,this.sums_.index-1);index=Math.min(this.styles_.length-1,index);var style=this.styles_[index];this.url_=style["url"];this.height_=style["height"];this.width_=style["width"];this.textColor_=style["textColor"];this.anchor_=style["anchor"];this.textSize_=style["textSize"];this.backgroundPosition_=style["backgroundPosition"]};ClusterIcon.prototype.setCenter=function(center){this.center_=center};ClusterIcon.prototype.createCss=function(pos){var style=[];style.push("background-image:url("+this.url_+");");var backgroundPosition=this.backgroundPosition_?this.backgroundPosition_:"0 0";style.push("background-position:"+backgroundPosition+";");if(typeof this.anchor_==="object"){if(typeof this.anchor_[0]==="number"&&this.anchor_[0]>0&&this.anchor_[0]<this.height_)style.push("height:"+(this.height_-this.anchor_[0])+"px; padding-top:"+this.anchor_[0]+"px;");else style.push("height:"+this.height_+"px; line-height:"+this.height_+"px;");if(typeof this.anchor_[1]==="number"&&this.anchor_[1]>0&&this.anchor_[1]<this.width_)style.push("width:"+(this.width_-this.anchor_[1])+"px; padding-left:"+this.anchor_[1]+"px;");else style.push("width:"+this.width_+"px; text-align:center;")}else style.push("height:"+this.height_+"px; line-height:"+this.height_+"px; width:"+this.width_+"px; text-align:center;");var txtColor=this.textColor_?this.textColor_:"black";var txtSize=this.textSize_?this.textSize_:11;style.push("cursor:pointer; top:"+pos.y+"px; left:"+pos.x+"px; color:"+txtColor+"; position:absolute; font-size:"+txtSize+"px; font-family:Arial,sans-serif; font-weight:bold");return style.join("")};window["MarkerClusterer"]=MarkerClusterer;MarkerClusterer.prototype["addMarker"]=MarkerClusterer.prototype.addMarker;MarkerClusterer.prototype["addMarkers"]=MarkerClusterer.prototype.addMarkers;MarkerClusterer.prototype["clearMarkers"]=MarkerClusterer.prototype.clearMarkers;MarkerClusterer.prototype["fitMapToMarkers"]=MarkerClusterer.prototype.fitMapToMarkers;MarkerClusterer.prototype["getCalculator"]=MarkerClusterer.prototype.getCalculator;MarkerClusterer.prototype["getGridSize"]=MarkerClusterer.prototype.getGridSize;MarkerClusterer.prototype["getExtendedBounds"]=MarkerClusterer.prototype.getExtendedBounds;MarkerClusterer.prototype["getMap"]=MarkerClusterer.prototype.getMap;MarkerClusterer.prototype["getMarkers"]=MarkerClusterer.prototype.getMarkers;MarkerClusterer.prototype["getMaxZoom"]=MarkerClusterer.prototype.getMaxZoom;MarkerClusterer.prototype["getStyles"]=MarkerClusterer.prototype.getStyles;MarkerClusterer.prototype["getTotalClusters"]=MarkerClusterer.prototype.getTotalClusters;MarkerClusterer.prototype["getTotalMarkers"]=MarkerClusterer.prototype.getTotalMarkers;MarkerClusterer.prototype["redraw"]=MarkerClusterer.prototype.redraw;MarkerClusterer.prototype["removeMarker"]=MarkerClusterer.prototype.removeMarker;MarkerClusterer.prototype["removeMarkers"]=MarkerClusterer.prototype.removeMarkers;MarkerClusterer.prototype["resetViewport"]=MarkerClusterer.prototype.resetViewport;MarkerClusterer.prototype["repaint"]=MarkerClusterer.prototype.repaint;MarkerClusterer.prototype["setCalculator"]=MarkerClusterer.prototype.setCalculator;MarkerClusterer.prototype["setGridSize"]=MarkerClusterer.prototype.setGridSize;MarkerClusterer.prototype["onAdd"]=MarkerClusterer.prototype.onAdd;MarkerClusterer.prototype["draw"]=MarkerClusterer.prototype.draw;Cluster.prototype["getCenter"]=Cluster.prototype.getCenter;Cluster.prototype["getSize"]=Cluster.prototype.getSize;Cluster.prototype["getMarkers"]=Cluster.prototype.getMarkers;ClusterIcon.prototype["onAdd"]=ClusterIcon.prototype.onAdd;ClusterIcon.prototype["draw"]=ClusterIcon.prototype.draw;ClusterIcon.prototype["onRemove"]=ClusterIcon.prototype.onRemove;