
function MarkerClusterer(map,opt_markers,opt_options){this.extend(MarkerClusterer,google.maps.OverlayView);this.map_=map;this.overlayViewHelper.setMap(map);this.markers_=[];this.clusters_=[];this.sizes=[53,56,66,78,90];this.styles_=[];this.ready_=false;var options=opt_options||{};this.visible_=true;if(options['visible']!==undefined){this.visible_=options['visible'];}
this.callback_=options['callback'];this.gridSize_=options['gridSize']||60;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.displayInfoWindowOnClick_=false;if(options['displayInfoWindowOnClick']!==undefined){this.displayInfoWindowOnClick_=options['displayInfoWindowOnClick'];}
this.flareOnMaxZoom_=false;if(options['flareOnMaxZoom']!==undefined){this.flareOnMaxZoom_=options['flareOnMaxZoom'];}
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.infoWindow=new google.maps.InfoWindow({maxWidth:530});MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_='http://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]);};function OverlayViewHelper(){}
MarkerClusterer.prototype.extend(OverlayViewHelper,google.maps.OverlayView);OverlayViewHelper.prototype.onAdd=function(){}
OverlayViewHelper.prototype.draw=function(){}
OverlayViewHelper.prototype.onRemove=function(){}
MarkerClusterer.prototype.overlayViewHelper=new OverlayViewHelper();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.setStyles=function(styles){this.styles_=styles;};MarkerClusterer.prototype.getStyles=function(){return this.styles_;};MarkerClusterer.prototype.isZoomOnClick=function(){return this.zoomOnClick_;};MarkerClusterer.prototype.displayInfoWindowOnClick=function(){return this.displayInfoWindowOnClick_;};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.setVisible(false);marker.setMap(null);marker.isAdded=false;var that=this;if(marker['draggable']){google.maps.event.addListener(marker,'dragend',function(){marker.isAdded=false;that.resetViewport();that.redraw();});}
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);marker.setVisible(false);marker.setMap(null);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.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();this.markers_=[];};MarkerClusterer.prototype.hide=function(){this.visible_=false;this.resetViewport();};MarkerClusterer.prototype.show=function(){this.visible_=true;this.redraw();};MarkerClusterer.prototype.resetViewport=function(){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;marker.setMap(null);marker.setVisible(false);}
this.clusters_=[];};MarkerClusterer.prototype.redraw=function(){this.createClusters_();};MarkerClusterer.prototype.createClusters_=function(){if(!this.ready_||!this.visible_){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++){var added=false;if(!marker.isAdded&&this.isMarkerInBounds_(marker,bounds)){for(var j=0,cluster;cluster=this.clusters_[j];j++){if(!added&&cluster.getCenter()&&cluster.isMarkerInClusterBounds(marker)){added=true;cluster.addMarker(marker);break;}}
if(!added){var cluster=new Cluster(this);cluster.addMarker(marker);this.clusters_.push(cluster);}}}
var that=this;if(this.callback_){setTimeout(function(){that.callback_(that.clusters_);},400);}};function Cluster(markerClusterer){this.markerClusterer_=markerClusterer;this.map_=markerClusterer.getMap();this.gridSize_=markerClusterer.getGridSize();this.averageCenter_=markerClusterer.isAverageCenter();this.center_=null;this.markers_=[];this.bounds_=null;this.clusterIcon_=null;}
Cluster.prototype.getClusterIcon=function(){return this.clusterIcon_;}
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 latDiff=(this.center_.lat()-marker.getPosition().lat())/l;var lngDiff=(this.center_.lng()-marker.getPosition().lng())/l;var lat=this.center_.lat()+latDiff;var lng=this.center_.lng()+lngDiff;this.center_=new google.maps.LatLng(lat,lng);this.calculateBounds_();}}
if(this.markers_.length==0){marker.setMap(this.map_);marker.setVisible(true);}else if(this.markers_.length==1){this.clusterIcon_=new ClusterIcon(this,this.markerClusterer_.getStyles(),this.markerClusterer_.getGridSize());this.markers_[0].setMap(null);this.markers_[0].setVisible(false);}
marker.isAdded=true;this.markers_.push(marker);this.updateIcon();return true;};Cluster.prototype.getMarkerClusterer=function(){return this.markerClusterer_;};Cluster.prototype.getBounds=function(){this.calculateBounds_();return this.bounds_;};Cluster.prototype.remove=function(){this.clusterIcon_&&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||this.markers_.length<2){for(var i=0,marker;marker=this.markers_[i];i++){marker.setMap(this.map_);marker.setVisible(this.markerClusterer_.visible_);}
this.clusterIcon_&&this.clusterIcon_.hide();return;}
var numStyles=this.markerClusterer_.getStyles().length;var sums=this.markerClusterer_.getCalculator()(this.markers_,numStyles);this.clusterIcon_.setPosition(this.center_);this.clusterIcon_.setSums(sums);if(this.markerClusterer_.flareOnMaxZoom_&&this.map_.maxZoom<=mz&&mz==zoom){this.clusterIcon_.subMarkers=[];for(var i=0,marker;marker=this.markers_[i];i++){this.clusterIcon_.addSubMarker(marker);}}
this.markerClusterer_.visible_?this.clusterIcon_.show():this.clusterIcon_.hide();};function OffsetableMarker(mkrOpts){if(!mkrOpts)mkrOpts={};this.poi=mkrOpts.position;this.opts=mkrOpts;this.color=mkrOpts.line_color||"";this.width=mkrOpts.line_width||1;this.opacity=mkrOpts.line_opacity||1.0;this.dx=mkrOpts.dx||0;this.dy=mkrOpts.dy||0;this.line=null;this.subMarkers=[];google.maps.event.addListener(this,"mouseover",this.onMouseOver);google.maps.Marker.call(this,mkrOpts);}
MarkerClusterer.prototype.extend(OffsetableMarker,google.maps.Marker);OffsetableMarker.prototype.addSubMarker=function(mkr){this.subMarkers.push(mkr);}
OffsetableMarker.prototype.getMarkerLatLngBounds=function(){var markerll=this.getPosition();var markerPt=MarkerClusterer.prototype.overlayViewHelper.getProjection().fromLatLngToContainerPixel(markerll);var ic=this.getIcon();var ancPt;var icSz;if(typeof(ic)==="object"){ancPt=ic.anchor;icSz=ic.size;}
if(!icSz)icSz=this.getImageSize(ic);if(!ancPt)ancPt=new google.maps.Point(parseInt(icSz.width/2),icSz.height);var sw=MarkerClusterer.prototype.overlayViewHelper.getProjection().fromContainerPixelToLatLng(new google.maps.Point(markerPt.x-ancPt.x,markerPt.y+(icSz.height-ancPt.y)));var ne=MarkerClusterer.prototype.overlayViewHelper.getProjection().fromContainerPixelToLatLng(new google.maps.Point(markerPt.x+(icSz.width-ancPt.x),markerPt.y-ancPt.y));var bds=new google.maps.LatLngBounds(sw,ne);bds.extend(markerll);if(this.subMarkers){for(var i=0;i<this.subMarkers.length;i++){if(!this.subMarkers[i].getVisible())continue;var sb=this.subMarkers[i].getMarkerLatLngBounds();bds.union(sb);}}
return bds;}
OffsetableMarker.prototype.onMouseOut=function(latlng){this.color="";this.width=1;this.redraw(true);}
OffsetableMarker.prototype.getImageSize=function(url){var img=new Image();img.src=url;return new google.maps.Size(img.width,img.height);}
OffsetableMarker.prototype.onMouseOver=function(latlng){this.color="#FF0000";this.width=2;var len=this.subMarkers.length;var dontRedrawChildren=true;if(len>0){var ic=this.getIcon();var icoSz;if(typeof(ic)==="object"){icoSz=ic.size;}
if(!icoSz)icoSz=this.getImageSize(ic);var lineLen=parseInt(Math.sqrt(icoSz.width*icoSz.width+icoSz.height*icoSz.height))*Math.ceil(len/2/Math.PI);if(isNaN(lineLen))lineLen=50;for(var i=0,marker;i<len;i++){marker=this.subMarkers[i];if(!marker.getMap()||!marker.getVisible()){dontRedrawChildren=false;marker.dx=lineLen*Math.cos(2*i*Math.PI/len-Math.PI/4);marker.dy=lineLen*Math.sin(2*i*Math.PI/len-Math.PI/4);marker.poi=this.getPosition();marker.setMap(this.getMap());marker.setVisible(true);}}}
this.redraw(dontRedrawChildren);if(!this.mouseOutListener){this.mouseOutListener=google.maps.event.addListener(this,"mouseout",this.onMouseOut);};if(len<=0)return;var mkr=this;if(!this.zoomChangeListener){this.zoomChangeListener=google.maps.event.addListener(this.getMap(),"zoom_changed",function(){mkr.hideChildren();});}
if(!this.mouseMoveListener){this.mouseMoveListener=google.maps.event.addListener(this.getMap(),"mousemove",function(latlng){var bnds=mkr.getMarkerLatLngBounds();if(bnds.contains(latlng.latLng))return;var infowP=null;MarkerClusterer.prototype.infoWindow.anchor&&(infowP=MarkerClusterer.prototype.infoWindow.anchor.getPosition());if(MarkerClusterer.prototype.infoWindow.getMap()&&infowP&&bnds.contains(infowP))return;mkr.hideChildren();});}}
OffsetableMarker.prototype.hideChildren=function(){if(this.mouseMoveListener){google.maps.event.removeListener(this.mouseMoveListener);this.mouseMoveListener=null;}
if(this.zoomChangeListener){google.maps.event.removeListener(this.zoomChangeListener);this.zoomChangeListener=null;}
for(var i=0;i<this.subMarkers.length;i++){var subMkr=this.subMarkers[i];subMkr.dx=subMkr.dy=0;subMkr.poi=subMkr.opts.position;subMkr.setPosition(subMkr.poi);subMkr.hide();}}
OffsetableMarker.prototype.onDblclick=function(){if(!this.map_.getInfoWindow().isHidden())
this.map_.closeInfoWindow();this.setPoint(this.poi);this.hideLine();this.dx=0;this.dy=0;var mkr=this;window.setTimeout(function(){mkr.onDragStart.call(mkr);},100);return false;}
OffsetableMarker.prototype.hide=function(){this.setVisible(false);this.hideLine();this.hideChildren();}
OffsetableMarker.prototype.hideLine=function(){if(this.line!=null){this.line.setMap(null);}}
OffsetableMarker.prototype.redraw=function(excludeChildren){excludeChildren=excludeChildren||false;if((this.dx!=0)||(this.dy!=0)){var pt=MarkerClusterer.prototype.overlayViewHelper.getProjection().fromLatLngToDivPixel(this.poi);pt.x+=this.dx;pt.y+=this.dy;var adjustedMkrPoint=MarkerClusterer.prototype.overlayViewHelper.getProjection().fromDivPixelToLatLng(pt);this.setPosition(adjustedMkrPoint);var pts=new Array();pts[0]=this.poi;pts[1]=adjustedMkrPoint;var lc=this.color||null;if(!this.line){this.line=new google.maps.Polyline();}
this.line.setOptions({path:pts,strokeColor:lc,map:this.getMap(),strokeWeight:this.width,strokeOpacity:this.opacity});var map_=this.getMap();var scrBnd=map_.getBounds();var b=this.getMarkerLatLngBounds();if(!scrBnd.contains(b.getNorthEast())||!scrBnd.contains(b.getSouthWest())){map_.panToBounds(b);}}
if(excludeChildren)return;for(var i=0,marker;marker=this.subMarkers[i];i++){marker.poi=this.getPosition();marker.redraw&&marker.redraw();}}
function ClusterIcon(cluster,styles,opt_padding){this.styles_=styles;this.padding_=opt_padding||0;this.cluster_=cluster;this.div_=null;this.sums_=null;this.description_=null;this.setMap(cluster.getMap());this.overlayViewHelper=new OverlayViewHelper();this.overlayViewHelper.onAdd=this.onAdd(this);this.overlayViewHelper.draw=this.draw(this);this.overlayViewHelper.onRemove=this.onRemove(this);this.overlayViewHelper.setMap(cluster.getMap());OffsetableMarker.call(this,{});var that=this;google.maps.event.addListener(this,'click',function(){that.triggerClusterClick();});}
MarkerClusterer.prototype.extend(ClusterIcon,OffsetableMarker);ClusterIcon.prototype.setDescription=function(description){this.description_=description;}
ClusterIcon.prototype.setIconImage=function(imageUrl){var ic=this.getIcon()||new google.maps.MarkerImage(imageUrl);ic.url=imageUrl;this.setIcon(ic);}
ClusterIcon.prototype.triggerClusterClick=function(){var markerClusterer=this.cluster_.getMarkerClusterer();google.maps.event.trigger(markerClusterer,'clusterclick',this.cluster_);if(this.cluster_.getMarkerClusterer().displayInfoWindowOnClick()){var iw=this.cluster_.getMarkerClusterer().infoWindow;var cnt="<h3 id='infoWindowTitle' class='clusterInfowWindowTitle'>"+this.getTitle()+"</h3>";cnt+=this.description_;iw.setContent(cnt);iw.open(this.getMap(),this);}
if(markerClusterer.isZoomOnClick()){var map_=this.getMap();var zoom=map_.getZoom();var mz=this.cluster_.markerClusterer_.getMaxZoom();if(zoom<mz||!map_.maxZoom||map_.maxZoom>mz){this.map.panTo(this.cluster_.getCenter());this.map.fitBounds(this.cluster_.getBounds());}}};ClusterIcon.prototype.onAdd=function(that){return function(){that.div_=document.createElement('DIV');if(that.getVisible()){var pos=that.getPosFromLatLng_(that.getPosition());that.div_.style.cssText=that.createCss(pos);if(that.showSum)that.div_.innerHTML=that.sums_.text;}
var panes=this.getPanes();panes.overlayImage.appendChild(that.div_);};};ClusterIcon.prototype.getPosFromLatLng_=function(latlng){var pos=this.overlayViewHelper.getProjection().fromLatLngToDivPixel(latlng);return pos;};ClusterIcon.prototype.draw=function(that){return function(){if(that.getVisible()){var pos=that.getPosFromLatLng_(that.getPosition());that.div_.style.top=pos.y+'px';that.div_.style.left=pos.x+'px';}};};ClusterIcon.prototype.hide=function(){if(this.div_){this.div_.style.display='none';}
this.setVisible(false);};ClusterIcon.prototype.show=function(){if(this.div_){var pos=this.getPosFromLatLng_(this.getPosition());this.div_.style.cssText=this.createCss(pos);this.div_.style.display='';}
this.setVisible(true);};ClusterIcon.prototype.remove=function(){this.setMap(null);this.overlayViewHelper.setMap(null);};ClusterIcon.prototype.onRemove=function(that){return function(){if(that.div_&&that.div_.parentNode){that.hide();that.div_.parentNode.removeChild(that.div_);that.div_=null;}};};ClusterIcon.prototype.setSums=function(sums){this.sums_=sums;this.text_=sums.text;this.index_=sums.index;this.useStyle();if(this.div_){this.div_.innerHTML=sums.text;if(!this.showSum){this.div_.innerHTML="";}}};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];var sz=null;var anchor=null;if(style['height']&&style['width']){sz=new google.maps.Size(style['width'],style['height']);}
else{sz=this.getImageSize(style['url']);}
anchor=new google.maps.Point(sz.width/2,sz.height/2);if(sz.width==0||sz.height==0){sz=null;}
if(style['anchor']){anchor=new google.maps.Point(style['anchor'][0],style['anchor'][1]);}
this.setIcon(new google.maps.MarkerImage(style['url'],sz,null,anchor));this.zIndex_=style['zIndex']||1000;this.textColor_=style['textColor'];this.textSize_=style['textSize'];this.showSum=(style['showSum']==null)?true:style['showSum'];};ClusterIcon.prototype.createCss=function(pos){var style=[];var txtColor=this.textColor_?this.textColor_:'black';var txtSize=this.textSize_?this.textSize_:11;style.push('top:'+pos.y+'px; left:'+
pos.x+'px; color:'+txtColor+';width:40px;text-align:center;margin-left:-20px;margin-top:-'+(txtSize/2)+'px; position:absolute; font-size:'+
txtSize+'px; font-family:Arial,sans-serif; font-weight:bold;');if(this.zIndex_){style.push('z-index:'+this.zIndex_+';');}
return style.join('');};
