///*
//	ClusterMarker Version 2
//	
//	A marker manager for the Google Maps API
//	http://googlemapsapi.martinpearman.co.uk/clustermarker
//	
//	Copyright Martin Pearman 2009
//	Last updated 30th December 2009
//
//	This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
//
//	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License along with this program.  If not, see <http://www.gnu.org/licenses/>.
//	
//*/
//
//function ClusterMarker($map){
//	var $this=this;
//	$this.map=$map;
//	$this.markers=[];
//	$this.clusterMarkers=[];
//	$this.clusteringEnabled=true;
//	//	using the plain green icon for cluster marker icons
//	//	you can change this icon here in ClusterMarker.js
//	//	or in map.php you could set:
//	//	cluster.clusterMarkerIcon=some new icon
//	//	$this.clusterMarkerIcon=new GIcon(G_DEFAULT_ICON, 'http://maps.gstatic.com/intl/en_ALL/mapfiles/marker_green.png');
//	var $icon=new GIcon();
//	$icon.image='images/kml_blue_cross.png';
//	$icon.iconSize=new GSize(32, 32);
//	$icon.iconAnchor=new GPoint(15, 16);
//	$icon.infoWindowAnchor=new GPoint(15, 16);
//	$this.clusterMarkerIcon=$icon;
//	GEvent.bind($map, 'moveend', this, $this.moveEnd);
//	GEvent.bind($map, 'zoomend', this, $this.zoomEnd);
//	GEvent.bind($map, 'maptypechanged', $this, $this.mapTypeChanged);
//}
//
////	add markers and initialise some custome properties for the markers
////	this method adds markers but does not delete/remove any existing markers
//ClusterMarker.prototype.addMarkers=function($markers, $areOverlaid){
//	var $this=this, $indexOffset=$this.markers.length, $length=$markers.length, $marker;
//	if(typeof($areOverlaid)==='undefined'){
//		$areOverlaid=false;
//	}
//	while($length--){
//		$marker=$markers[$length];
//		$marker._ClusterMarker_={id:$length+$indexOffset, isActive:true, doNotCluster:false, anchorPoint:[], intersectTable:[], isOverlaid:$areOverlaid, instance:$this};
//	}
//	$this.markers=$this.markers.concat($markers);
//};
//
////	the refresh() method is the method that does all the updating
//ClusterMarker.prototype.refresh=function(){
//	var $this=this;
//	function compareArrays($array1, $array2){
//		if($array1.length!==$array2.length){
//			return false;
//		}
//		var i, $length=$array1.length<$array2.length?$array1.length:$array2.length;
//		for (i=0; i<$length; i++){
//			if($array1[i]!==$array2[i]){
//				return false;
//			}
//		}
//		return true;
//	}
//	function createClusterMarker($indexes){
//		var $clusterBounds=new GLatLngBounds(), $length=$indexes.length, $markers=$this.markers;
//		while($length--){
//			$clusterBounds.extend($markers[$indexes[$length]].getLatLng());										   
//		}
//		var $clusterMarker=new GMarker($markers[$indexes[0]].getLatLng(), {icon:$this.clusterMarkerIcon, title:$indexes.length+' locations clustered here'});
//		GEvent.addListener($clusterMarker, 'click', function(){
//			$this.clusterMarkerClickHandler($clusterMarker);
//		});
//		$clusterMarker._markerIndexes=$indexes;
//		$clusterMarker._clusterBounds=$clusterBounds;
//		$length=$indexes.length;
//		while($length--){
//			$markers[$indexes[$length]]._clusterMarker=$clusterMarker;
//		}
//		return $clusterMarker;
//	}
//	
//	//	filter active markers
//	var $map=$this.map, $zoom=$map.getZoom(), $bounds=$map.getBounds(), $markers=$this.markers, $length=$markers.length, $marker, $isActive;
//	var $mapSize=$map.getSize(), $borderPaddingX=$mapSize.width/2, $borderPaddingY=$mapSize.height/2;
//	
//	var $projection=$map.getCurrentMapType().getProjection();
//	var $mapSwPoint=$projection.fromLatLngToPixel($bounds.getSouthWest(), $zoom);
//	var $mapNePoint=$projection.fromLatLngToPixel($bounds.getNorthEast(), $zoom);
//	var $swX=$mapSwPoint.x-$borderPaddingX, $swY=$mapSwPoint.y+$borderPaddingY, $neX=$mapNePoint.x+$borderPaddingX, $neY=$mapNePoint.y-$borderPaddingY;
//	var $activeSwLatLng=$projection.fromPixelToLatLng(new GPoint($swX, $swY), $zoom);
//	var $activeNeLatLng=$projection.fromPixelToLatLng(new GPoint($neX, $neY), $zoom);
//	
//	$bounds.extend($activeSwLatLng);
//	$bounds.extend($activeNeLatLng);
//	
//	//	if a marker is within the map bounds plus the border padding then it's isActive and makeVisible properties are set to true
//	//	the makeVisible property is not part of the _ClusterMarker_ property
//	//	(this is an early version 2 of ClusterMarker and i haven't decided upon the exact format to use for each marker's custom properties)
//	while($length--){
//		$marker=$markers[$length];
//		$isActive=false;
//		if(!$marker.isHidden() && $bounds.containsLatLng($marker.getLatLng())){
//			$isActive=true;
//		}
//		$marker._ClusterMarker_.isActive=$isActive;
//		$marker._makeVisible=$isActive;
//	}
//	
//		//	filter clustered markers
//	var $newClusterIndexes=[];
//	if($this.clusteringEnabled){
//		var i=$markers.length, j, $indexes, $cancelCluster;
//		//	loop thru all markers, if a marker is visible and it's icon intersects the icon of the marker it is being compared to then push it to the $indexes array
//		while(i--){
//			if($markers[i]._makeVisible){
//				$indexes=[i];
//				j=i;
//				while(j--){
//					if($markers[j]._makeVisible && $this.markerIconsIntersect($markers[i], $markers[j])){
//						$indexes.push(j);
//					}
//				}
//				//	if the marker icon doesn't intersect any other marker icons then $index will have a single element
//				//	so if $index.length is greater than 1 then a cluster of 2 or more markers with intersecting markers has been found
//				if($indexes.length>1){
//					$cancelCluster=false;
//					j=$indexes.length;
//					//	loop thru the cluster of markers whose icons intersect
//					//	if any marker in that cluster has it's doNotCluster property set to true then we do not want to create a cluster marker
//					//	instead the marker whose doNotCluster property is true will be displayed and any other markers that are in this cluster are not displayed
//					while(j--){
//						if($markers[$indexes[j]]._ClusterMarker_.doNotCluster){
//							$cancelCluster=true;
//						} else {
//							$markers[$indexes[j]]._makeVisible=false;
//						}
//					}
//					//	$indexes is an array of markers whose icons intersect
//					//	$newClusterIndexes is an array of arrays
//					if(!$cancelCluster){
//						$newClusterIndexes.push($indexes);
//					}
//				}
//			}
//		}
//	}
//	
//	//	update map
//	
//	//	add or remove cluster markers
//	var $clusterMarkers=$this.clusterMarkers, $clusterMarkersLength=$clusterMarkers.length, $clusterMarker, $newClusterMarkers=[], i, j, $indexes;
//	var $newClusterIndexesLength=$newClusterIndexes.length, $clusterIndex;
//	
//	//	remove cluster markers whose markerIndexes no longer exist and flag the related index array not to be created
//	for(i=0; i<$clusterMarkersLength; i++){
//		$clusterMarker=$clusterMarkers[i];
//		$clusterIndex=$clusterMarker._markerIndexes;
//		for(j=0; j<$newClusterIndexesLength; j++){
//			if(compareArrays($clusterIndex, $newClusterIndexes[j])){
//				//	no need to create a cluster marker if a cluster marker already exists and that cluster marker contains the same markers that we want to create a cluster marker for
//				$newClusterMarkers.push($clusterMarker);
//				//	to flag this array of markers as belonging to a cluster marker that already exists change it's value from the array to false
//				$newClusterIndexes[j]=false;
//				break;	//	break out of j loop
//			}
//		}
//		if($newClusterIndexes[j]!==false){
//			//	this cluster marker is not going to be reused/recycled - it is going to be removed from the map
//			//	so delete the _clusterMarker property of each marker within that cluster
//			$indexes=$clusterMarker._markerIndexes;
//			$length=$indexes.length;
//			while($length--){
//				delete $this.markers[$indexes[$length]]._clusterMarker;
//			}
//			$map.removeOverlay($clusterMarker);
//		}
//		
//	}
//	//	loop thru $newClusterIndexes creating cluster markers for any index which is not FALSE
//	while($newClusterIndexesLength--){
//		$indexes=$newClusterIndexes[$newClusterIndexesLength];
//		if($indexes!==false){
//			$clusterMarker=createClusterMarker($newClusterIndexes[$newClusterIndexesLength]);
//			$newClusterMarkers.push($clusterMarker);
//			$map.addOverlay($clusterMarker);
//		}
//	}
//	$this.clusterMarkers=$newClusterMarkers;
//		
//	//	add or remove active markers
//	$length=$markers.length;
//	while($length--){
//		$marker=$markers[$length];
//		if($marker._makeVisible && !$marker._ClusterMarker_.isOverlaid){
//			//	if a marker's makeVisible property is true and it's isOverlaid property is false then add it to the map
//			$map.addOverlay($marker);
//			$marker._ClusterMarker_.isOverlaid=true;
//		} else if(!$marker._makeVisible && $marker._ClusterMarker_.isOverlaid){
//			//	if a marker's makeVisible property is false and it's isOverlaid property is true then remove it from the map
//			$map.removeOverlay($marker);
//			$marker._ClusterMarker_.isOverlaid=false;
//		}
//	}
//	//	trigger a custom event 'refreshed' each time ClusterMarker's refresh() method has finished executing
//	//	this is commented out - it is intended to be used when a dynamic sidebar is attached to the map
//	//	the sidebar would listen for the 'refreshed' event and then update itself to reflect the state of the map markers
//	//	GEvent.trigger($this, 'refreshed');
//};
//
////	this method returns pixel coordinates of a marker's lat/lng position on the map
////	if this calculation has already been done then it's value will have been cached and the cached value is returned
////	otherwise the value is calculated and cached before being returned
//ClusterMarker.prototype.getMarkerAnchorPoint=function($marker, $zoom){
//	if(typeof($marker._ClusterMarker_.anchorPoint[$zoom])!=='undefined'){
//		return $marker._ClusterMarker_.anchorPoint[$zoom];
//	} else {
//		var $anchorPoint=this.map.getCurrentMapType().getProjection().fromLatLngToPixel($marker.getLatLng(), $zoom);
//		$marker._ClusterMarker_.anchorPoint[$zoom]=$anchorPoint;
//		return $anchorPoint;
//	}
//};
//
////	this method handles any clicks on a cluster marker
//ClusterMarker.prototype.clusterMarkerClickHandler=function($clusterMarker){
//	var $this=this, $map=$this.map, $indexes=$clusterMarker._markerIndexes;
//	function $sortByMarkerTitle(a, b){
//		var title1=a.getTitle(), title2=b.getTitle();
//		if(title1<title2){
//			return -1;
//		} else if (title1>title2){
//			return 1;
//		} else {
//			return 0;
//		}
//	}
//	//	this function handles clicks on links to markers in a cluster marker infowindow
//	function $clusterMarkerInfoWindowClickHandler($marker){
//		return function(){
//			GEvent.trigger($marker, 'click');
//		};
//	}
//	
//	var i, $length=$indexes.length, $maxContent=document.createElement('div'), $minContent=document.createElement('div'), $link, $img, $marker, $markers=[];
//	
//	i=$length;
//	while(i--){
//		$markers.push($this.markers[$indexes[i]]);
//	}
//	$markers.sort($sortByMarkerTitle);
//	
//	for(i=0; i<$length; i++){
//		$marker=$markers[i];
//		$link=document.createElement('a');
//		$link.href='javascript:void(0)';
//		$link.onclick=$clusterMarkerInfoWindowClickHandler($marker);
//		$link.appendChild(document.createTextNode($marker.getTitle()));
//		$maxContent.appendChild($link);
//		if(i<$length-1){
//			$maxContent.appendChild(document.createElement('br'));
//		}
//	}
//	$minContent.appendChild(document.createElement('br'));
//	$link=document.createElement('a');
//	$link.href='javascript:void(0)';
//	$link.onclick=function(){
//		$this.map.getInfoWindow().maximize();
//	};
//	$link.appendChild(document.createTextNode('Show '+$length+' links'));
//	$minContent.appendChild($link);
//	
//	//	if map fully zoomed in then no need for zoom in link - in fact the 'Show links' link could be omitted and all links displayed when the infowindow first opens
//	//	(that is if there are not too many links - otherwise the infowindow will likely overflow)
//	if($map.getZoom()<$map.getCurrentMapType().getMaximumResolution()){
//		$minContent.appendChild(document.createTextNode(' | '));
//		$link=document.createElement('a');
//		$link.href='javascript:void(0)';
//		$link.onclick=function(){
//			$map.setCenter($clusterMarker._clusterBounds.getCenter(), $map.getBoundsZoomLevel($clusterMarker._clusterBounds));
//		};
//		$link.appendChild(document.createTextNode('Fit map to locations'));
//		$minContent.appendChild($link);
//		$minContent.appendChild(document.createTextNode(' | '));
//		$link=document.createElement('a');
//		$link.href='javascript:void(0)';
//		$link.onclick=function(){
//			$map.zoomIn();
//		};
//		$link.appendChild(document.createTextNode('Zoom in'));
//		$minContent.appendChild($link);
//	}
//	$clusterMarker.openInfoWindow($minContent, {maxContent:$maxContent, maxTitle:"Select a location:"});
//};
//
////	zoomEnd, moveEnd and mapTypeChanged are all GMap2 events that ClusterMarker needs to listen for and update itself when any of these events occurs
//ClusterMarker.prototype.zoomEnd=function(){
//	this._cancelMoveEnd=true;
//	this.refresh();
//};
//
////	a map zoom will trigger both a zoomEnd and moveEnd event/
//// no need to execute ClusterMarker's refresh() event twice so the cancelMoveEnd property is used as a flag to avoid executing refresh() twice
//ClusterMarker.prototype.moveEnd=function(){
//	if(this._cancelMoveEnd){
//		this._cancelMoveEnd=false;
//	} else {
//		this.refresh();
//	}
//};
//
//ClusterMarker.prototype.mapTypeChanged=function(){
//	this.refresh();
//};
//
////	here we build a pair of pixel coordinates for each marker's icon
////	the pair representing the marker's icon's south-west and north-east points on the map
////	we can then calculate whether or not two marker icons intersect
////	all calculated values are cached so that they do not need to be re-caculated if required again
//ClusterMarker.prototype.markerIconsIntersect=function($marker1, $marker2, $zoom){
//	var $this=this, $map=$this.map;
//	function getIconPointBounds($marker){
//		var $icon=$marker.getIcon();
//		var $iconSize=$icon.iconSize;
//		var $iconAnchorPoint=$icon.iconAnchor;
//		var $markerAnchorPoint=$this.getMarkerAnchorPoint($marker, $zoom);
//		
//		var $swIconAnchorPoint=new GPoint($markerAnchorPoint.x-$iconAnchorPoint.x, $markerAnchorPoint.y-$iconAnchorPoint.y+$iconSize.height);
//		var $neIconAnchorPoint=new GPoint($markerAnchorPoint.x-$iconAnchorPoint.x+$iconSize.width, $markerAnchorPoint.y-$iconAnchorPoint.y);
//		return {sw:$swIconAnchorPoint, ne:$neIconAnchorPoint};
//	}
//	
//	if(typeof($zoom)==='undefined'){
//		$zoom=$map.getZoom();
//	}
//	if(typeof($marker1._ClusterMarker_.intersectTable[$zoom])!=='undefined' && typeof($marker1._ClusterMarker_.intersectTable[$zoom][$marker2._ClusterMarker_.id])!=='undefined'){
//		return $marker1._ClusterMarker_.intersectTable[$zoom][$marker2._ClusterMarker_.id];
//	}
//	if(typeof($marker1)==='undefined' || typeof($marker2)==='undefined'){
//		GLog.write('Undefined markers');	
//	}
//	var $bounds1=getIconPointBounds($marker1), $bounds2=getIconPointBounds($marker2);
//	var $intersects=!($bounds2.sw.x>$bounds1.ne.x || $bounds2.ne.x<$bounds1.sw.x || $bounds2.ne.y>$bounds1.sw.y || $bounds2.sw.y<$bounds1.ne.y);
//	if(typeof($marker1._ClusterMarker_.intersectTable[$zoom])==='undefined'){
//		$marker1._ClusterMarker_.intersectTable[$zoom]=[];
//	}
//	$marker1._ClusterMarker_.intersectTable[$zoom][$marker2._ClusterMarker_.id]=$intersects;
//	return $intersects;
//};
//
////	this method is not complete - if you require to remove markers from ClusterMarker then i shall finish this method for you
////	custom marker properties ought to be deleted and cluster marker event listeners removed
//ClusterMarker.prototype.removeMarkers=function($markers){
//	if(!$markers){
//		for(var i=0; i<this.markers.length; i++){
//			this.map.removeOverlay(this.markers[i]);	//	marker properties to delete
//		}
//		this.markers=[];
//		for(i=0; i<this.clusterMarkers.length; i++){
//			this.map.removeOverlay(this.clusterMarkers[i]);	//	event listener property to implement
//		}
//		this.clusterMarkers=[];
//	}
//};
//
////	this method will zoom and pan the map to show all markers in the $markers array that is passed to it
////	if no array of markers is passed to this method then the map will zoom and pan to fit all markers added to ClusterMarker
////	if a value is passed for $maxZoom then the map will not be zoomed in any more than this zoom level
//ClusterMarker.prototype.fitMapToMarkers=function($markers, $maxZoom){
//	var $this=this, $bounds=new GLatLngBounds(), $refresh=false; 
//	if(typeof($markers)==='undefined' || $markers===null){
//		$markers=this.markers;
//	}
//	var $length=$markers.length;
//	while($length--){
//		if(!$markers[$length].isHidden()){
//			$bounds.extend($markers[$length].getLatLng());
//			$refresh=true;
//		}
//	}
//	if($refresh){
//		var $zoom=$this.map.getBoundsZoomLevel($bounds);
//		if(typeof($maxZoom)!=='undefined'){
//			$zoom=$zoom>$maxZoom?$maxZoom:$zoom;
//		}
//		$this.map.setCenter($bounds.getCenter(), $zoom);	
//	}
//};
//
////	this method will return the minimum zoom level at which a marker is not clustered
////	if the marker is currently part of a cluster then start searching for the minimum unclustered zoom level from the current map zoom level plus one
////	(no need to search for the zoom level at lower zoom levels as we know the marker is clustered at this zoom level)
////	otherwise search for the minimum unclustered zoom level from map zoom level zero
//ClusterMarker.prototype.getMinUnclusterLevel=function($marker){
//	var $this=this, $map=$this.map, $maxZoomLevel=$map.getCurrentMapType().getMaximumResolution(), $isClustered, $markers=$this.markers, $length=$markers.length, $indexes=[], i, $zoomLevel;
//	while($length--){
//		if($marker!==$markers[$length]){
//			$indexes.push($markers[$length]._ClusterMarker_.id);
//		}
//	}
//	if($marker._clusterMarker){
//		$zoomLevel=$map.getZoom()+1;
//	} else {
//		$zoomLevel=0;
//	}
//	$length=$indexes.length;
//	while($zoomLevel<=$maxZoomLevel){
//		$isClustered=false;
//		i=$length;
//		while(!$isClustered && i--){
//			if($this.markerIconsIntersect($marker, $markers[$indexes[i]], $zoomLevel)){
//				$isClustered=true;
//			}
//		}
//		if(!$isClustered){
//			break;
//		}
//		$zoomLevel++;
//	}
//	return $zoomLevel;
//};
//
////	set a marker's doNotCluster property to true or false
////	if the doNotCluster property is set to true then listen for the GMap2 'infowindowclose' event so we can then set doNotCluster to false
//ClusterMarker.prototype.setDoNotCluster=function($marker, $state){
//	if($state && !$marker._ClusterMarker_.doNotCluster){
//		var $this=this;
//		$marker._ClusterMarker_.doNotCluster=true;
//		$marker._ClusterMarker_.infowindowcloseListener=GEvent.addListener($this.map, 'infowindowclose', function(){
//			$marker._ClusterMarker_.doNotCluster=false;
//			$this.refresh();
//			GEvent.removeListener($marker._ClusterMarker_.infowindowcloseListener);
//		});
//	} else if (!$state && $marker._ClusterMarker_.doNotCluster){
//		$marker._ClusterMarker_.doNotCluster=false;
//		$this.refresh();
//		GEvent.removeListener($marker._ClusterMarker_.infowindowcloseListener);
//	}
//};
//











//================================================================================================================
/*
	ClusterMarker Version 1.3.2
	
	A marker manager for the Google Maps API
	http://googlemapsapi.martinpearman.co.uk/clustermarker
	
	Copyright Martin Pearman 2008
	Last updated 29th September 2008

	This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

	You should have received a copy of the GNU General Public License along with this program.  If not, see <http://www.gnu.org/licenses/>.
	
*/

function ClusterMarker($map, $options){
	this._map=$map;
	this._mapMarkers=[];
	this._iconBounds=[];
	this._clusterMarkers=[];
	this._eventListeners=[];
	if(typeof($options)==='undefined'){
		$options={};
	}
	this.borderPadding=($options.borderPadding)?$options.borderPadding:256;
	this.clusteringEnabled=($options.clusteringEnabled===false)?false:true;
	if($options.clusterMarkerClick){
		this.clusterMarkerClick=$options.clusterMarkerClick;
	}
	if($options.clusterMarkerIcon){
		this.clusterMarkerIcon=$options.clusterMarkerIcon;
	}else{
		this.clusterMarkerIcon=new GIcon();
		this.clusterMarkerIcon.image='http://maps.google.com/mapfiles/arrow.png';
		this.clusterMarkerIcon.iconSize=new GSize(39, 34);
		this.clusterMarkerIcon.iconAnchor=new GPoint(9, 31);
		this.clusterMarkerIcon.infoWindowAnchor=new GPoint(9, 31);
		this.clusterMarkerIcon.shadow='http://www.google.com/intl/en_us/mapfiles/arrowshadow.png';
		this.clusterMarkerIcon.shadowSize=new GSize(39, 34);
	}
	this.clusterMarkerTitle=($options.clusterMarkerTitle)?$options.clusterMarkerTitle:'Click to zoom in and see more';//%count markers';
	if($options.fitMapMaxZoom){
		this.fitMapMaxZoom=$options.fitMapMaxZoom;
	}
	this.intersectPadding=($options.intersectPadding)?$options.intersectPadding:0;
	if($options.markers){
		this.addMarkers($options.markers);
	}
	GEvent.bind(this._map, 'moveend', this, this._moveEnd);
	GEvent.bind(this._map, 'zoomend', this, this._zoomEnd);
	GEvent.bind(this._map, 'maptypechanged', this, this._mapTypeChanged);
}

ClusterMarker.prototype.addMarkers=function($markers){
	var i;
	if(!$markers[0]){
		//	assume $markers is an associative array and convert to a numerically indexed array
		var $numArray=[];
		for(i in $markers){
			$numArray.push($markers[i]);
		}
		$markers=$numArray;
	}
	for(i=$markers.length-1; i>=0; i--){
		$markers[i]._isVisible=false;
		$markers[i]._isActive=false;
		$markers[i]._makeVisible=false;
	}
	this._mapMarkers=this._mapMarkers.concat($markers);
};

ClusterMarker.prototype._clusterMarker=function($clusterGroupIndexes){
	function $newClusterMarker($location, $icon, $title){
		return new GMarker($location, {icon:$icon, title:$title});
	}
	var $clusterGroupBounds=new GLatLngBounds(), i, $clusterMarker, $clusteredMarkers=[], $marker, $this=this, $mapMarkers=this._mapMarkers;
	//alert("Cluster:"+$clusterGroupIndexes.length);
	//---------------------  khanchai  -----------------
		var $limit = 20;
		if($clusterGroupIndexes.length-1 <= $limit){
			$limit = $clusterGroupIndexes.length;
		}
			$limit = $limit - 1;
	//---------------------  khanchai  -----------------
	//for(i=$clusterGroupIndexes.length-1; i>=0; i--){
	for(i=$limit; i>=0; i--){
		$marker=$mapMarkers[$clusterGroupIndexes[i]];
		$marker.index=$clusterGroupIndexes[i];
		$clusterGroupBounds.extend($marker.getLatLng());
		$clusteredMarkers.push($marker);
	}
	$clusterMarker=$newClusterMarker($clusterGroupBounds.getCenter(), this.clusterMarkerIcon, this.clusterMarkerTitle.replace(/%count/gi, $clusterGroupIndexes.length));
	$clusterMarker.clusterGroupBounds=$clusterGroupBounds;	//	only req'd for default cluster marker click action
	this._eventListeners.push(GEvent.addListener($clusterMarker, 'click', function(){
		$this.clusterMarkerClick({clusterMarker:$clusterMarker, clusteredMarkers:$clusteredMarkers });
	}));
	$clusterMarker._childIndexes=$clusterGroupIndexes;
	//for(i=$clusterGroupIndexes.length-1; i>=0; i--){		
	for(i=$limit; i>=0; i--){
		$mapMarkers[$clusterGroupIndexes[i]]._parentCluster=$clusterMarker;
	}
	return $clusterMarker;
};

ClusterMarker.prototype.clusterMarkerClick=function($args){
	this._map.setCenter($args.clusterMarker.getLatLng(), this._map.getBoundsZoomLevel($args.clusterMarker.clusterGroupBounds));
};

ClusterMarker.prototype._filterActiveMapMarkers=function(){
	var $borderPadding=this.borderPadding, $mapZoomLevel=this._map.getZoom(), $mapProjection=this._map.getCurrentMapType().getProjection(), $mapPointSw, $activeAreaPointSw, $activeAreaLatLngSw, $mapPointNe, $activeAreaPointNe, $activeAreaLatLngNe, $activeAreaBounds=this._map.getBounds(), i, $marker, $uncachedIconBoundsIndexes=[], $oldState, $mapMarkers=this._mapMarkers, $iconBounds=this._iconBounds;
	if($borderPadding){
		$mapPointSw=$mapProjection.fromLatLngToPixel($activeAreaBounds.getSouthWest(), $mapZoomLevel);
		$activeAreaPointSw=new GPoint($mapPointSw.x-$borderPadding, $mapPointSw.y+$borderPadding);
		$activeAreaLatLngSw=$mapProjection.fromPixelToLatLng($activeAreaPointSw, $mapZoomLevel);
		$mapPointNe=$mapProjection.fromLatLngToPixel($activeAreaBounds.getNorthEast(), $mapZoomLevel);
		$activeAreaPointNe=new GPoint($mapPointNe.x+$borderPadding, $mapPointNe.y-$borderPadding);
		$activeAreaLatLngNe=$mapProjection.fromPixelToLatLng($activeAreaPointNe, $mapZoomLevel);
		$activeAreaBounds.extend($activeAreaLatLngSw);
		$activeAreaBounds.extend($activeAreaLatLngNe);
	}
	this._activeMarkersChanged=false;
//	alert("$mapMarkers.length :"+$mapMarkers.length);
//	if ($mapMarkers.length > 0) {
		if (typeof($iconBounds[$mapZoomLevel]) === 'undefined') {
			//	no iconBounds cached for this zoom level
			//	no need to check for existence of individual iconBounds elements
			this._iconBounds[$mapZoomLevel] = [];
			this._activeMarkersChanged = true; //	force refresh(true) as zoomed to uncached zoom level
//			if ($mapMarkers.length > 0) {
				for (i = $mapMarkers.length - 1; i >= 0; i--) {
					$marker = $mapMarkers[i];
					$marker._isActive = $activeAreaBounds.containsLatLng($marker.getLatLng()) ? true : false;
					$marker._makeVisible = $marker._isActive;
					if ($marker._isActive) {
						$uncachedIconBoundsIndexes.push(i);
					}
				}
//			}
		}
		else {
			//	icondBounds array exists for this zoom level
			//	check for existence of individual iconBounds elements
//			if ($mapMarkers.length > 0) {
				for (i = $mapMarkers.length - 1; i >= 0; i--) {
					$marker = $mapMarkers[i];
					$oldState = $marker._isActive;
					$marker._isActive = $activeAreaBounds.containsLatLng($marker.getLatLng()) ? true : false;
					$marker._makeVisible = $marker._isActive;
					if (!this._activeMarkersChanged && $oldState !== $marker._isActive) {
						this._activeMarkersChanged = true;
					}
					if ($marker._isActive && typeof($iconBounds[$mapZoomLevel][i]) === 'undefined') {
						$uncachedIconBoundsIndexes.push(i);
					}
				}
//			}
		}
//	}
	return $uncachedIconBoundsIndexes;
};

ClusterMarker.prototype._filterIntersectingMapMarkers=function(){
	var $clusterGroup, i, j, $mapZoomLevel=this._map.getZoom(), $mapMarkers=this._mapMarkers, $iconBounds=this._iconBounds;
	for(i=$mapMarkers.length-1; i>0; i--)
	{
		if($mapMarkers[i]._makeVisible){
			$clusterGroup=[];
			for(j=i-1; j>=0; j--){
				if($mapMarkers[j]._makeVisible && $iconBounds[$mapZoomLevel][i].intersects($iconBounds[$mapZoomLevel][j])){
					$clusterGroup.push(j);
				}
			}
			if($clusterGroup.length!==0){
				$clusterGroup.push(i);
				for(j=$clusterGroup.length-1; j>=0; j--){
					$mapMarkers[$clusterGroup[j]]._makeVisible=false;
				}
				this._clusterMarkers.push(this._clusterMarker($clusterGroup));
			}
		}
	}
};

ClusterMarker.prototype.fitMapToMarkers=function(){
	var $mapMarkers=this._mapMarkers, $markersBounds=new GLatLngBounds(), i;
	//alert($mapMarkers.length-1);
	for(i=$mapMarkers.length-1; i>=0; i--){
		$markersBounds.extend($mapMarkers[i].getLatLng());
	}
	var $fitMapToMarkersZoom=this._map.getBoundsZoomLevel($markersBounds);
		
//	if(this.fitMapMaxZoom && $fitMapToMarkersZoom>this.fitMapMaxZoom){
//		$fitMapToMarkersZoom=this.fitMapMaxZoom;
//	}
//	this._map.setCenter($markersBounds.getCenter(), $fitMapToMarkersZoom);
	this.refresh();
};

ClusterMarker.prototype._mapTypeChanged=function(){
	this.refresh(true);
};

ClusterMarker.prototype._moveEnd=function(){
	if(!this._cancelMoveEnd){
		this.refresh();
	}else{
		this._cancelMoveEnd=false;
	}
};

ClusterMarker.prototype._preCacheIconBounds=function($indexes, $mapZoomLevel){
	var $mapProjection=this._map.getCurrentMapType().getProjection(), i, $marker, $iconSize, $iconAnchorPoint, $iconAnchorPointOffset, $iconBoundsPointSw, $iconBoundsPointNe, $iconBoundsLatLngSw, $iconBoundsLatLngNe, $intersectPadding=this.intersectPadding, $mapMarkers=this._mapMarkers;
	for(i=$indexes.length-1; i>=0; i--){
		$marker=$mapMarkers[$indexes[i]];
		$iconSize=$marker.getIcon().iconSize;
		$iconAnchorPoint=$mapProjection.fromLatLngToPixel($marker.getLatLng(), $mapZoomLevel);
		$iconAnchorPointOffset=$marker.getIcon().iconAnchor;
		$iconBoundsPointSw=new GPoint($iconAnchorPoint.x-$iconAnchorPointOffset.x-$intersectPadding, $iconAnchorPoint.y-$iconAnchorPointOffset.y+$iconSize.height+$intersectPadding);
		$iconBoundsPointNe=new GPoint($iconAnchorPoint.x-$iconAnchorPointOffset.x+$iconSize.width+$intersectPadding, $iconAnchorPoint.y-$iconAnchorPointOffset.y-$intersectPadding);
		$iconBoundsLatLngSw=$mapProjection.fromPixelToLatLng($iconBoundsPointSw, $mapZoomLevel);
		$iconBoundsLatLngNe=$mapProjection.fromPixelToLatLng($iconBoundsPointNe, $mapZoomLevel);
		this._iconBounds[$mapZoomLevel][$indexes[i]]=new GLatLngBounds($iconBoundsLatLngSw, $iconBoundsLatLngNe);
	}
};

ClusterMarker.prototype.refresh=function($forceFullRefresh){
	var i, $marker, $zoomLevel=this._map.getZoom(), $uncachedIconBoundsIndexes=this._filterActiveMapMarkers();
	if ($zoomLevel == 19 && $zoomLevel > document.getElementById('lastzoomlevel').value && ($zoomLevel - document.getElementById('lastzoomlevel').value)> 1 ){
//		alert(this._map.getCenter());
		this._map.setCenter(this._map.getCenter(),18);
	}
	if ((this._activeMarkersChanged || $forceFullRefresh)) {
		//if ($zoomLevel < 18){// || ($zoomLevel - document.getElementById('lastzoomlevel').value)==1) {
			this._removeClusterMarkers();
			//alert($zoomLevel);
		//}
		//alert("maps zoom:"+$zoomLevel +"\nlastzoom:"+ document.getElementById('lastzoomlevel').value);
			if (this.clusteringEnabled && $zoomLevel < this._map.getCurrentMapType().getMaximumResolution()) {
			//if (this.clusteringEnabled && $zoomLevel < 20 && $zoomLevel >= document.getElementById('lastzoomlevel').value) {
				//alert($uncachedIconBoundsIndexes.length);
				if ($uncachedIconBoundsIndexes.length > 0 ) {
					this._preCacheIconBounds($uncachedIconBoundsIndexes, $zoomLevel);
				}
				this._filterIntersectingMapMarkers();
			}
			for (i = this._clusterMarkers.length - 1; i >= 0; i--) {
				this._map.addOverlay(this._clusterMarkers[i]);
			}
			for (i = this._mapMarkers.length - 1; i >= 0; i--) {
				$marker = this._mapMarkers[i];
//				if ($marker.isHidden()){ $marker.show();}				
//					if ($zoomLevel >= 18) {
//						$marker.hide();
//						}else{ $marker.show();}
				
				if (!$marker._isVisible && $marker._makeVisible) {  
				// Inside Cluster
					//if ($zoomLevel >= 19 ) {
						//$marker.hide();
						//$marker._isVisible = false;
					//	this._map.removeOverlay($marker);
					//	$marker._isVisible = false;
					//}else{
					this._map.addOverlay($marker);
						$marker._isVisible = true;						
					//}
				}
				if ($marker._isVisible && !$marker._makeVisible) {
					this._map.removeOverlay($marker);
						$marker._isVisible = false;
				}
			}
		
	}
	
	document.getElementById('lastzoomlevel').value=$zoomLevel;
};

ClusterMarker.prototype._removeClusterMarkers=function(){
	var i, j, $map=this._map, $eventListeners=this._eventListeners, $clusterMarkers=this._clusterMarkers, $childIndexes, $mapMarkers=this._mapMarkers;
	//alert("Cluster Marker:"+$clusterMarkers.length);
	for(i=$clusterMarkers.length-1; i>=0; i--){
		$childIndexes=$clusterMarkers[i]._childIndexes;
		for(j=$childIndexes.length-1; j>=0; j--){
			delete $mapMarkers[$childIndexes[j]]._parentCluster;
		}
		$map.removeOverlay($clusterMarkers[i]);
	}
	for(i=$eventListeners.length-1; i>=0; i--){
		GEvent.removeListener($eventListeners[i]);
	}
	this._clusterMarkers=[];
	this._eventListeners=[];
};

ClusterMarker.prototype.removeMarkers=function(){
	var i, $mapMarkers=this._mapMarkers, $map=this._map;
	for(i=$mapMarkers.length-1; i>=0; i--){
		if($mapMarkers[i]._isVisible){
			$map.removeOverlay($mapMarkers[i]);
		}
		delete $mapMarkers[i]._isVisible;
		delete $mapMarkers[i]._isActive;
		delete $mapMarkers[i]._makeVisible;
	}
	this._removeClusterMarkers();
	this._mapMarkers=[];
	this._iconBounds=[];
};

ClusterMarker.prototype.triggerClick=function($index){
	var $marker=this._mapMarkers[$index];
	if($marker._isVisible){
		//	$marker is visible
		GEvent.trigger($marker, 'click');
	}
	else if($marker._isActive){
		//	$marker is clustered
		var $clusteredMarkersIndexes=$marker._parentCluster._childIndexes, $intersectDetected=true, $uncachedIconBoundsIndexes, i, $mapZoomLevel=this._map.getZoom(), $clusteredMarkerIndex, $iconBounds=this._iconBounds, $mapMaxZoomLevel=this._map.getCurrentMapType().getMaximumResolution();
		//$mapMaxZoomLevel = 20;
		while($intersectDetected && $mapZoomLevel<$mapMaxZoomLevel){
			$intersectDetected=false;
			$mapZoomLevel++;
			if(typeof($iconBounds[$mapZoomLevel])==='undefined'){
				//	no iconBounds cached for this zoom level
				//	no need to check for existence of individual iconBounds elements
				$iconBounds[$mapZoomLevel]=[];
				// need to create cache for all clustered markers at $mapZoomLevel
				this._preCacheIconBounds($clusteredMarkersIndexes, $mapZoomLevel);
			}else{
				//	iconBounds array exists for this zoom level
				//	check for existence of individual iconBounds elements
				$uncachedIconBoundsIndexes=[];
				for(i=$clusteredMarkersIndexes.length-1; i>=0; i--){
					if(typeof($iconBounds[$mapZoomLevel][$clusteredMarkersIndexes[i]])==='undefined'){
						$uncachedIconBoundsIndexes.push($clusteredMarkersIndexes[i]);
					}
				}
				if($uncachedIconBoundsIndexes.length>=1){
					this._preCacheIconBounds($uncachedIconBoundsIndexes, $mapZoomLevel);
				}
			}
			for(i=$clusteredMarkersIndexes.length-1; i>=0; i--){
				$clusteredMarkerIndex=$clusteredMarkersIndexes[i];
				if($clusteredMarkerIndex!==$index && $iconBounds[$mapZoomLevel][$clusteredMarkerIndex].intersects($iconBounds[$mapZoomLevel][$index])){	
					$intersectDetected=true;
					break;
				}
			}
			
		};
		this._map.setCenter($marker.getLatLng(), $mapZoomLevel);
		this.triggerClick($index);
	}else{
		// $marker is not within active area (map bounds + border padding)
		this._map.setCenter($marker.getLatLng());
		this.triggerClick($index);
	}
};

ClusterMarker.prototype._zoomEnd=function(){
	this._cancelMoveEnd=true;
	this.refresh(true);
};

