
Clusterer=function(map)
{
this.map=map;
this.markers=[];
this.clusters=[];
this.timeout=null;
this.currentZoomLevel=map.getZoom();
this.maxVisibleMarkers=Clusterer.defaultMaxVisibleMarkers;
this.gridSize=Clusterer.defaultGridSize;
this.minMarkersPerCluster=Clusterer.defaultMinMarkersPerCluster;
this.maxLinesPerInfoBox=Clusterer.defaultMaxLinesPerInfoBox;
this.icon=Clusterer.defaultIcon;


GEvent.addListener(map,'zoomend',Clusterer.MakeCaller(Clusterer.Display,this));
GEvent.addListener(map,'moveend',Clusterer.MakeCaller(Clusterer.Display,this));
GEvent.addListener(map,'infowindowclose',Clusterer.MakeCaller(Clusterer.PopDown,this));};
Clusterer.defaultMaxVisibleMarkers=80;
Clusterer.defaultGridSize=5;
Clusterer.defaultMinMarkersPerCluster=5;
Clusterer.defaultMaxLinesPerInfoBox=5;

Clusterer.defaultIcon=new GIcon();
Clusterer.defaultIcon.image='./img/blue.png';
Clusterer.defaultIcon.shadow='./img/shadow.png';
Clusterer.defaultIcon.iconSize=new GSize(30,30);
Clusterer.defaultIcon.shadowSize=new GSize(47,30);
Clusterer.defaultIcon.iconAnchor=new GPoint(15,25);
Clusterer.defaultIcon.infoWindowAnchor=new GPoint(5,15);
//Clusterer.defaultIcon.infoShadowAnchor=new GPoint(0,25);

Clusterer.prototype.SetIcon=function(icon)
{this.icon=icon;};

Clusterer.prototype.SetMaxVisibleMarkers=function(n){this.maxVisibleMarkers=n;};
Clusterer.prototype.SetMinMarkersPerCluster=function(n){this.minMarkersPerCluster=n;};
Clusterer.prototype.SetMaxLinesPerInfoBox=function(n){this.maxLinesPerInfoBox=n;};

Clusterer.prototype.AddMarker=function(marker,title)
{if(marker.setMap!=null)
marker.setMap(this.map);
marker.title=title;
marker.onMap=false;
this.markers.push(marker);
this.DisplayLater();};


Clusterer.prototype.RemoveMarker=function(marker)
{
for(var i=0;i<this.markers.length;++i)
if(this.markers[i]==marker)
{if(marker.onMap)
this.map.removeOverlay(marker);

for(var j=0;j<this.clusters.length;++j)
{var cluster=this.clusters[j];

if(cluster!=null)
{for(var k=0;k<cluster.markers.length;++k)

if(cluster.markers[k]==marker)
{cluster.markers[k]=null;--cluster.markerCount;break;}
if(cluster.markerCount==0)
{this.ClearCluster(cluster);
this.clusters[j]=null;}
else if(cluster==this.poppedUpCluster)
Clusterer.RePop(this);}}
this.markers[i]=null;break;}
this.DisplayLater();};

Clusterer.prototype.DisplayLater=function()
{if(this.timeout!=null)
clearTimeout(this.timeout);this.timeout=setTimeout(Clusterer.MakeCaller(Clusterer.Display,this),50);};




Clusterer.Display=function(clusterer)
{

var i,j,marker,cluster;
clearTimeout(clusterer.timeout);
var newZoomLevel=clusterer.map.getZoom();

if(newZoomLevel>5){

if(newZoomLevel!=clusterer.currentZoomLevel)
	{
	for(i=0;i<clusterer.clusters.length;++i)
		if(
		clusterer.clusters[i]!=null
		)
		{
			clusterer.ClearCluster(clusterer.clusters[i]);
			clusterer.clusters[i]=null;
		}
		clusterer.clusters.length=0;
		clusterer.currentZoomLevel=newZoomLevel;
	}
	
var bounds=clusterer.map.getBounds();
var sw=bounds.getSouthWest();
var ne=bounds.getNorthEast();
var dx=ne.lng()-sw.lng();
var dy=ne.lat()-sw.lat();
if(dx<300&&dy<150)
{dx*=0.10;dy*=0.10;
bounds=new GLatLngBounds(new GLatLng(sw.lat()-dy,sw.lng()-dx),new GLatLng(ne.lat()+dy,ne.lng()+dx));
}

var visibleMarkers=[];
var nonvisibleMarkers=[];

for(i=0;i<clusterer.markers.length;++i)
		{
	marker=clusterer.markers[i];
	if(marker!=null)
	if(bounds.contains(marker.getPoint()))
		visibleMarkers.push(marker);
	else
		nonvisibleMarkers.push(marker);
		}
		
for(i=0;i<nonvisibleMarkers.length;++i)
{
marker=nonvisibleMarkers[i];
if(marker.onMap)
{clusterer.map.removeOverlay(marker);
marker.onMap=false;
}}


for(i=0;i<clusterer.clusters.length;++i)
{cluster=clusterer.clusters[i];
if(cluster!=null&&!bounds.contains(cluster.marker.getPoint())&&cluster.onMap)
{clusterer.map.removeOverlay(cluster.marker);
cluster.onMap=false;
}}


if(visibleMarkers.length>clusterer.maxVisibleMarkers)
{var latRange=bounds.getNorthEast().lat()-bounds.getSouthWest().lat();
var latInc=latRange/clusterer.gridSize;
var lngInc=latInc/Math.cos((bounds.getNorthEast().lat()+bounds.getSouthWest().lat())/2.0*Math.PI/180.0);
for(var lat=bounds.getSouthWest().lat();
lat<=bounds.getNorthEast().lat();
lat+=latInc)
for(var lng=bounds.getSouthWest().lng();
lng<=bounds.getNorthEast().lng();lng+=lngInc)

{cluster=new Object();cluster.clusterer=clusterer;cluster.bounds=new GLatLngBounds(new GLatLng(lat,lng),new GLatLng(lat+latInc,lng+lngInc));cluster.markers=[];cluster.markerCount=0;cluster.onMap=false;cluster.marker=null;clusterer.clusters.push(cluster);}


for(i=0;i<visibleMarkers.length;++i)
{marker=visibleMarkers[i];if(marker!=null&&!marker.inCluster)
{for(j=0;j<clusterer.clusters.length;++j)
{cluster=clusterer.clusters[j];if(cluster!=null&&cluster.bounds.contains(marker.getPoint()))
{cluster.markers.push(marker);++cluster.markerCount;marker.inCluster=true;}}}}


for(i=0;i<clusterer.clusters.length;++i)
if(clusterer.clusters[i]!=null&&clusterer.clusters[i].markerCount<clusterer.minMarkersPerCluster)
{clusterer.ClearCluster(clusterer.clusters[i]);clusterer.clusters[i]=null;}
for(i=clusterer.clusters.length-1;i>=0;--i)
if(clusterer.clusters[i]!=null)
break;else
--clusterer.clusters.length;
for(i=0;i<clusterer.clusters.length;++i)
{cluster=clusterer.clusters[i];
if(cluster!=null)
{for(j=0;j<cluster.markers.length;++j)
{marker=cluster.markers[j];
if(marker!=null&&marker.onMap)
{clusterer.map.removeOverlay(marker);
marker.onMap=false;
}}}}
for(i=0;i<clusterer.clusters.length;++i)
{cluster=clusterer.clusters[i];if(cluster!=null&&cluster.marker==null)
{var xTotal=0.0,yTotal=0.0;for(j=0;j<cluster.markers.length;++j)
{marker=cluster.markers[j];if(marker!=null)
{xTotal+=(+marker.getPoint().lng());yTotal+=(+marker.getPoint().lat());}}
var location=new GLatLng(yTotal/cluster.markerCount,xTotal/cluster.markerCount);

marker=new GMarker(location,{icon:clusterer.icon});
cluster.marker=marker;



GEvent.addListener(marker,'click',Clusterer.MakeCaller(Clusterer.PopUp,cluster));}}}
	for(i=0;i<visibleMarkers.length;++i)
	{
	marker=visibleMarkers[i];
	if(marker!=null&&!marker.onMap&&!marker.inCluster)
		{
		clusterer.map.addOverlay(marker);
		if(marker.addedToMap!=null)
		marker.addedToMap();
		marker.onMap=true;
		}
	}

for(i=0;i<clusterer.clusters.length;++i)
	{
		cluster=clusterer.clusters[i];
		if(cluster!=null&&!cluster.onMap&&bounds.contains(cluster.marker.getPoint()))
			{
			clusterer.map.addOverlay(cluster.marker);
			cluster.onMap=true;
			}	
	}

Clusterer.RePop(clusterer);
}
else{ 
      
for ( i = 0; i < clusterer.clusters.length; ++i )
            if ( clusterer.clusters[i] != null )
                {
                clusterer.ClearCluster( clusterer.clusters[i] );
                clusterer.clusters[i] = null;
                }
        clusterer.clusters.length = 0;
        clusterer.currentZoomLevel = newZoomLevel;


	for ( i = 0; i < clusterer.markers.length; ++i )
		{
		clusterer.markers[i].hide();
		}
    }
		

};



Clusterer.PopUp=function(cluster)


{

var clusterer=cluster.clusterer;var html='<table width="300">';var n=0;for(var i=0;i<cluster.markers.length;++i)
{var marker=cluster.markers[i];if(marker!=null)
{++n;html+='<tr><td>';if(marker.getIcon().smallImage!=null)
html+='<img src="'+marker.getIcon().smallImage+'">';else
html+='<img align="texttop" src="'+marker.getIcon().image+'" width="'+(marker.getIcon().iconSize.width/5*4)+'" height="'+(marker.getIcon().iconSize.height/5*4)+'">';html+='</td><td style="font-family:Arial Narrow; font-size:14px;">'+marker.title+'</td></tr>';if(n==clusterer.maxLinesPerInfoBox-1&&cluster.markerCount>clusterer.maxLinesPerInfoBox)
{html+='<tr><td colspan="2"  style="font-family:Arial Narrow; font-size:14px;">...et '+(cluster.markerCount-n)+' de plus</td></tr>';break;}}}
html+='</table>';
clusterer.map.closeInfoWindow();
cluster.marker.openInfoWindowHtml(html);
clusterer.poppedUpCluster=cluster;


};
Clusterer.RePop=function(clusterer)
{
if(clusterer.poppedUpCluster!=null)
Clusterer.PopUp(clusterer.poppedUpCluster);
};
Clusterer.PopDown=function(clusterer)
{
clusterer.poppedUpCluster=null;
};

Clusterer.prototype.ClearCluster=function(cluster)
{var i,marker;
for(i=0;i<cluster.markers.length;++i)
if(cluster.markers[i]!=null)
	{
	cluster.markers[i].inCluster=false;
	cluster.markers[i]=null;
	}
cluster.markers.length=0;
cluster.markerCount=0;
if(cluster==this.poppedUpCluster)
this.map.closeInfoWindow();
if(cluster.onMap)
	{
	this.map.removeOverlay(cluster.marker);
	cluster.onMap=false;
	}
};

Clusterer.MakeCaller=function(func,arg)
{return function(){func(arg);};};GMarker.prototype.setMap=function(map)
{this.map=map;};GMarker.prototype.addedToMap=function()
{this.map=null;};GMarker.prototype.origOpenInfoWindow=GMarker.prototype.openInfoWindow;GMarker.prototype.openInfoWindow=function(node,opts)
{if(this.map!=null)
return this.map.openInfoWindow(this.getPoint(),node,opts);else
return this.origOpenInfoWindow(node,opts);};GMarker.prototype.origOpenInfoWindowHtml=GMarker.prototype.openInfoWindowHtml;GMarker.prototype.openInfoWindowHtml=function(html,opts)
{if(this.map!=null)
return this.map.openInfoWindowHtml(this.getPoint(),html,opts);else
return this.origOpenInfoWindowHtml(html,opts);};GMarker.prototype.origOpenInfoWindowTabs=GMarker.prototype.openInfoWindowTabs;GMarker.prototype.openInfoWindowTabs=function(tabNodes,opts)
{if(this.map!=null)
return this.map.openInfoWindowTabs(this.getPoint(),tabNodes,opts);else
return this.origOpenInfoWindowTabs(tabNodes,opts);};GMarker.prototype.origOpenInfoWindowTabsHtml=GMarker.prototype.openInfoWindowTabsHtml;GMarker.prototype.openInfoWindowTabsHtml=function(tabHtmls,opts)
{if(this.map!=null)
return this.map.openInfoWindowTabsHtml(this.getPoint(),tabHtmls,opts);else
return this.origOpenInfoWindowTabsHtml(tabHtmls,opts);};GMarker.prototype.origShowMapBlowup=GMarker.prototype.showMapBlowup;GMarker.prototype.showMapBlowup=function(opts)
{if(this.map!=null)
return this.map.showMapBlowup(this.getPoint(),opts);else
return this.origShowMapBlowup(opts);};
