MyGMap = function(id_div_carte) {
    this.filtresCamp = new Array();
    this.templateContenuBulle = "NOM : ${data.nom}<br/>ETOILE : ${data.rate}<br/>REGION : ${data.region}<br/>PHOTO : ${data.photo}<br/>NUM CAMP ${data.num_camp}<br/>";
    this.mapCircle = null;
    this.div_carte = document.getElementById(id_div_carte);
    this.gmap = new GMap2(this.div_carte);
    this.gmap.addControl(new GMapTypeControl());
    this.gmap.addMapType(G_PHYSICAL_MAP);
    this.gmap.setMapType(G_PHYSICAL_MAP);
    this.gmap.enableScrollWheelZoom();
    this.geoXml = new GeoXml(this);
    this.markerCamps = new Array();
    this.champTitreBulle = "nom";
    this.defaultStyle = new GIcon();
    this.SetClusterer = function(urlImgClusterer, maxVisible, minPerMarker) {
        this.clusterer = new Clusterer(this.gmap);
        this.clusterer.SetMaxVisibleMarkers(maxVisible);
        this.clusterer.SetMinMarkersPerCluster(minPerMarker);
        this.clusterer.SetMaxLinesPerInfoBox(7);
        if (urlImgClusterer)
            this.clusterer.SetIcon(this.geoXml.makeIcon(urlImgClusterer, null));
    };
    this.SetForcedClusterer = function(data, MaxZoomToMakeCluster) {
        this.clusterer = new Clusterer(this.gmap);
        this.clusterer.MaxZoomToMakeCluster = MaxZoomToMakeCluster;
        this.clusterer.ForcedClusters = data;
    };
    this.SetCenter = function(latitude, longitude, zoom) {
        this.gmap.setCenter(new GLatLng(latitude, longitude), zoom);
    }
    this.GoBestCenter = function() {
        var bou = new GLatLngBounds();
        for (var i = 0; i < this.markerCamps.length; i++) {
            var m = this.markerCamps[i].marker;
            bou.extend(m.getLatLng());
        }
        if (this.markerCamps.length == 1)
            this.gmap.setCenter(this.markerCamps[0].marker.getLatLng(), 7);
        else
            this.gmap.setCenter(bou.getCenter(), this.gmap.getBoundsZoomLevel(bou));
    }
    this.CreateDescription = function(donnee) {

        return this.templateContenuBulle.process({ data: donnee });
    }
    this.GetTitle = function(donnee) {
        return (donnee) ? donnee[this.champTitreBulle] : "";
    }
    this.AddMarker = function(marker, data) {
        this.markerCamps.push(new MarkerCamp(marker, data));
        if (this.clusterer)
            this.clusterer.AddMarker(marker, marker.getTitle());
        else
            this.gmap.addOverlay(marker);
    }
    this.RetourDisplayDataXML = function() {
        this.GoBestCenter();
    }
    this.SetDefaultIcon = function(url, largeur) {
        this.defaultStyle = this.geoXml.makeIcon(url);
        if (largeur)
            this.largeurIcone = largeur;
    }
    this.EnableSelectionCercle = function(id_champ_rayon, id_tb_latitude, id_tb_longitude) {
        this.longitude = document.getElementById(id_tb_longitude);
        this.latitude = document.getElementById(id_tb_latitude);
        InitMarkerCentre.call(this);
        var that = this;
        GEvent.addListener(this.gmap, 'click',
			function(overlay, point) {
			    if (!overlay) {
			        if (!that.markerCenter) {
			            that.markerCenter = new GMarker(point, { draggable: true });
			            that.gmap.addOverlay(that.markerCenter);
			        }
			        else {
			            that.markerCenter.setLatLng(point);
			        }
			        that.mapCircle.UpdateCircle();
			        setLatLng.call(that, point);
			    }
			}
		);
        GEvent.addListener(this.gmap, 'move',
			function() {
			    that.mapCircle.dessineCadre();
			}
		);

        this.mapCircle = new MapCircle(this, this.markerCenter, id_champ_rayon);
    }
    function InitMarkerCentre() {
        var point = new GLatLng(this.latitude.value, this.longitude.value);
        if (!this.markerCenter) {
            this.markerCenter = new GMarker(point, { draggable: true });
            var that = this;
            GEvent.addListener(this.markerCenter, 'drag',
					function() {
					    that.mapCircle.UpdateCircle();
					    setLatLng.call(that, this.getPoint());
					}
				);
            this.gmap.addOverlay(this.markerCenter);
        }
        else {
            setLatLng.call(this, point);
        }
    }
    function setLatLng(point) {
        if (this.longitude && this.latitude) {
            this.latitude.value = point.lat();
            this.longitude.value = point.lng();
        }
    }
    this.AddFiltre = function(filtre) {
        this.filtresCamp.push(filtre);
        this.filtresCamp[filtre.cham_filtre] = filtre;
    }
    this.Filtre = function() {
        for (var i = 0; i < this.markerCamps.length; i++) {
            this.markerCamps[i].montrer();
        }
        for (var i = 0; i < this.filtresCamp.length; i++) {
            for (var j = 0; j < this.markerCamps.length; j++) {
                if (!this.filtresCamp[i].EstOK(this.markerCamps[j]))
                    this.markerCamps[j].cacher();
            }
        }
    }
}

//cette classe se charge de charger la carte a partir d'un fichier kml / json
GeoXml = function(myGMap) {
    this.myGMap = myGMap;
    this.stylesGMap = [];
    function manageStyle(styles) {
        var styles_temps = makeArrayIfNot(styles);
        for (var i = 0; i < styles_temps.length; i++) {
            var sid = styles_temps[i].id;
            if (sid) {
                var href = styles_temps[i].iconstyle.icon.href;
                if (!!href)
                    this.stylesGMap["#" + sid] = this.makeIcon(href);
            }
        }
    }
    function ImageSize(src) {
        var newImg1 = new Image();
        newImg1.src = src;
        return { height: newImg1.height, width: newImg1.width };
    }
    this.makeIcon = function(href, largeur) {
        var tempstyle = new GIcon();
        var taille = ImageSize(href);
        if (taille.width == 0 || taille.height == 0) {
            taille = { width: 32, height: 32 };
        }
        if (taille.width > 32) {
            old_width = taille.width;
            taille.width = 32;
            taille.height = (taille.height * taille.width) / old_width;
        }
        //cas ou je done une largeur mais que ça n'agrandi pas l'icone
        //  if (this.opts.width_icon && this.opts.width_icon < taille.width) {
        //si on a pas pu renseigner les dimension de l'icone		
        /* }
        else {
        height_icon = taille.height;
        }*/

        tempstyle.infoWindowAnchor = new GPoint(taille.width / 2, taille.height / 2);
        tempstyle.iconSize = new GSize(taille.width, taille.height);
        tempstyle.iconAnchor = new GPoint(taille.width / 2, taille.height / 2);
        tempstyle.imageMap = [0, 0, (taille.width - 1), 0, (taille.height - 1), (taille.width - 1), 0, (taille.height - 1)];
        tempstyle.image = href;
        tempstyle.shadow = "";
        tempstyle.name = "hey";
        tempstyle.id = "image_style";
        return tempstyle;
    }

    function makeArrayIfNot(objet) {

        if (!objet.length) {
            var array_temp = new Array();
            array_temp.push(objet);
            return array_temp;
        }
        return objet;

    }
    function creerPlacemarks(node) {
        var node_temp = makeArrayIfNot(node);
        var len = node_temp.length;
        for (i = 0; i < len; i++) {
            creerMarker.call(this, node_temp[i]);
        }
    }
    function creerMarker(marker) {
        var coordonnees = marker.point.coordinates.split(',');
        var desc = (marker.description) ? marker.description : "";
        var style = (marker.styleurl) ? this.stylesGMap[marker.styleurl] : this.myGMap.defaultStyle;
        var point = new GLatLng(parseFloat(coordonnees[1]), parseFloat(coordonnees[0]));
        var data = (marker.extendeddata) ? eval("(" + marker.extendeddata + ")") : {};
        var titre_mark = this.myGMap.GetTitle(data);
        var gMarker = new GMarker(point, { icon: style, title: titre_mark }); /*, zIndexProcess:
			function(marker, b) {
			    return GOverlay.getZIndex(marker.getPoint().lat()) + marker.importance * 1000000;
			}
        }
		);*/
        gMarker.getIcon().shadow = "";
        gMarker.title = "";
        var desc = this.myGMap.CreateDescription(data);
        var me = this;
        if (!this.onClickMarker) {
            GEvent.addListener(gMarker, "click", function() {
                gMarker.openInfoWindow(desc);
            });
        }
        else {
            GEvent.addListener(gMarker, "click", function() {
                me.onClickMarker(desc);
            });
        }
        this.myGMap.AddMarker(gMarker, data);
        gMarker.SHData = data;
    }
    this.DisplayData = function(url) {
        var util = new UtilAjax();
        var data = util.GetXmlDataToJson(url);
        if (!data) return;
        var root = data.kml;
        var placemarks = [];
        if (root.document.style) {
            manageStyle.call(this, root.document.style);
        }
        creerPlacemarks.call(this, root.document.placemark);
        this.myGMap.RetourDisplayDataXML();
    }

}
//cette classe se charge d'afficher une cercle sur la carte
MapCircle = function(mygmap, marker_centre, id_champ_rayon) {
    this.color_ligne = "#000000";

    this.mygmap = mygmap;
    this.marker_centre = marker_centre;
    this.rayon = document.getElementById(id_champ_rayon);
    function getRayon() {
        if (this.rayon)
            return this.rayon.value * 2;
        return 100;
    }
    function getGMap() {
        return this.mygmap.gmap;
    }
    // -Encode a signed number in the encode format.
    function encodeSignedNumber(num) {
        var sgn_num = num << 1;

        if (num < 0) {
            sgn_num = ~(sgn_num);
        }

        return (encodeNumber(sgn_num));
    }

    //- Encode an unsigned number in the encode format.
    function encodeNumber(num) {
        var encodeString = "";

        while (num >= 0x20) {
            encodeString += (String.fromCharCode((0x20 | (num & 0x1f)) + 63));
            num >>= 5;
        }

        encodeString += (String.fromCharCode(num + 63));
        return encodeString;
    }
    function createPointForEncode(lat, lon, zoom) {
        return { Latitude: lat, Longitude: lon, Level: zoom };
    }
    function createEncodingsPoints(points) {
        var encoded_points = "";
        var plat = 0;
        var plng = 0;
        for (i = 0; i < points.length; ++i) {
            var point = points[i];
            var lat = point.Latitude;
            var lng = point.Longitude;
            var late5 = Math.floor(lat * 1e5);
            var lnge5 = Math.floor(lng * 1e5);
            dlat = late5 - plat;
            dlng = lnge5 - plng;
            plat = late5;
            plng = lnge5;
            encoded_points += encodeSignedNumber(dlat) + encodeSignedNumber(dlng);
        }
        return encoded_points;
    }
    function createEncodingsLevel(points) {
        var encoded_levels = "";
        for (i = 0; i < points.length; ++i) {
            encoded_levels += "P";
        }
        return encoded_levels;
    }
    function getTabFramePoints() {
        var zoom = getGMap.call(this).getZoom();
        var sw = getGMap.call(this).getBounds().getSouthWest();
        var ne = getGMap.call(this).getBounds().getNorthEast();
        var x_min = sw.lng();
        var x_max = ne.lng();
        var y_min = sw.lat();
        var y_max = ne.lat();
        var cadreLine = new Array();
        cadreLine.push(createPointForEncode(y_max, x_max, zoom));
        cadreLine.push(createPointForEncode(y_max, x_min, zoom));
        cadreLine.push(createPointForEncode(y_min, x_min, zoom));
        cadreLine.push(createPointForEncode(y_min, x_max, zoom));
        cadreLine.push(createPointForEncode(y_max, x_max, zoom));
        return cadreLine;
    }
    function dessineCercle(latitude, longitude, rayon) {
        var normalProj = G_NORMAL_MAP.getProjection();
        var zoom = getGMap.call(this).getZoom();
        var centerPt = normalProj.fromLatLngToPixel(this.marker_centre.getPoint(), zoom);
        var latRadius = this.marker_centre.getLatLng().lat() + getRayon.call(this) / 111;
        var radiusPt = normalProj.fromLatLngToPixel(new GLatLng(latRadius, this.marker_centre.getLatLng().lng()), zoom);
        var circlePoints = Array();
        var circlePoints2 = Array();
        with (Math) {
            var radiusDist = floor(sqrt(pow((centerPt.x - radiusPt.x), 2) + pow((centerPt.y - radiusPt.y), 2)));
            radiusB = radiusDist - (min(255, radiusDist) / 2) * 1;
            for (var a = 10; a < 181; a += 10) {
                var aRad = a * 2 * (PI / 180);
                y = centerPt.y + radiusB * sin(aRad)
                x = centerPt.x + radiusB * cos(aRad)
                var l = normalProj.fromPixelToLatLng(new GPoint(x, y), zoom);
                var p2 = createPointForEncode(l.lat(), l.lng(), zoom);
                circlePoints2.push(p2);
            }
            circlePoints2.push(circlePoints2[0]);
            var framePoints = getTabFramePoints.call(this);
            this.parameterCircle = { polylines: [
                    {//polilyne du cadre
                        points: createEncodingsPoints(framePoints),
                        levels: createEncodingsLevel(framePoints),
                        color: this.color_ligne,
                        opacity: 1,
                        weight: 2,
                        numLevels: 18,
                        zoomFactor: 2
                    },
                    {//polilyne du cercle
                        points: createEncodingsPoints(circlePoints2),
                        levels: createEncodingsLevel(circlePoints2),
                        color: this.color_ligne,
                        opacity: 1,
                        weight: 4,
                        numLevels: 18,
                        zoomFactor: 2
                    }
                ],
                fill: true,
                color: this.color_ligne,
                opacity: 0.2,
                outline: true
            };
            this.dessineCadre();
        }
    }
    function setEncodedPolygon() {
        if (this.polygoneCercle) {
            getGMap.call(this).removeOverlay(this.polygoneCercle);
        }
        this.polygoneCercle = new GPolygon.fromEncoded(this.parameterCircle);
        getGMap.call(this).addOverlay(this.polygoneCercle);
        var gmap = getGMap.call(this);
        GEvent.addListener(this.polygoneCercle, 'click',
			function(latlng) {
			    GEvent.trigger(this.gmap, "click", null, latlng);
			}
		);
    }
    this.dessineCadre = function() {
        if (this.parameterCircle && this.parameterCircle.polylines) {
            var framePoints = getTabFramePoints.call(this);
            var points1 = createEncodingsPoints(framePoints);
            var level1 = createEncodingsLevel(framePoints);
            this.parameterCircle.polylines[0] =
					{ points: points1, levels: level1,
					    color: this.color_ligne,
					    opacity: 1,
					    weight: 2,
					    numLevels: 18,
					    zoomFactor: 2
					};
            setEncodedPolygon.call(this);
        }
    }
    this.UpdateCircle = function() {
        if (this.marker_centre)
            dessineCercle.call(this, this.marker_centre.getLatLng().lat(), this.marker_centre.getLatLng().lng(), getRayon.call(this));
    }
}

//cette classe contient les informations sur un camping et le marker qui lui est lié

MarkerCamp = function(markerGMap, infos_camp) {
    this.marker = markerGMap;
    this.infos = infos_camp;
    this.montrer = function() {
        this.marker.show();
    }
    this.cacher = function() {
        this.marker.hide();
    }
}
UtilAjax = function() {
    this.GetXmlDataToJson = function(url) {
        if (window.XMLHttpRequest) {
            AJAX = new XMLHttpRequest();
        } else {
            AJAX = new ActiveXObject("Microsoft.XMLHTTP");
        }
        if (AJAX) {
            AJAX.open("GET", url, false);
            AJAX.send(null);
            return xml2json.parser(AJAX.responseText);
        } else {
            return false;
        }
    }
}
FiltreCamp = function(champ_filtre, valeur, doit_avoir_toute_les_valeurs, operateur_compare) {
    this.champ_filtre = champ_filtre;
    this.valeur = valeur;
    this.doit_avoir_tout_les_valeurs = doit_avoir_toute_les_valeurs;
    this.operateur = operateur_compare;
    this.EstOK = function(camp) {
        var donnee = camp.infos[this.champ_filtre];
        if (typeof (donnee) == "object") {
            return ifArrayInArrayShowMarker(this.valeur, donnee, this.doit_avoir_tout_les_valeurs);
        }
        else if (donnee) {
            return eval("donnee " + this.operateur + " this.valeur  ");
        }
        return true;
    }
    function ifArrayInArrayShowMarker(array, array2, isOr) {
        if (typeof (isOr) == "undefined") isOr = false;
        show = !isOr;
        if (array.length > 0) {
            for (var j = 0; j < array.length && (!isOr || (isOr && !show)); j++) {

                if (!parseInt(array[j])) {
                    if (!isOr) {
                        if (!stringSearch(array[j], array2)) {
                            show = false;
                        }
                    }
                    else {
                        if (stringSearch(array[j], array2)) {
                            show = true;
                        }
                    }
                }
                else {
                    if (!isOr) {
                        if (!dichotomicSearch(array[j], array2)) {
                            show = false;
                        }
                    }
                    else {
                        if (dichotomicSearch(array[j], array2)) {
                            show = true;
                        }
                    }
                }
            }
            return show;
        }
    }
    function stringSearch(X, list) {
        return list.indexOf(X) >= 0;
    }
    function dichotomicSearch(X, list) {
        var middle, start, end, elt, debut;
        end = list.length;
        start = 0;
        while (start <= end) {
            middle = Math.floor((start + end) / 2);
            elt = list[middle];
            if (X == elt) return true;
            if (parseInt(X) < parseInt(elt)) {
                end = middle - 1;
            }
            else {
                start = middle + 1;
            }
        }
        return false;
    }
}

//script recopié de http://www.numabilis.com/blog/2007-11-21-google_maps_api_gestionnaire_automatique_de_marqueurs
Clusterer = function(map) {
    if (!Clusterer.defaultMaxVisibleMarkers) {
        Clusterer.defaultMaxVisibleMarkers = 150;
        Clusterer.defaultGridSize = 5;
        Clusterer.defaultMinMarkersPerCluster = 5;
        Clusterer.defaultMaxLinesPerInfoBox = 10;
        Clusterer.defaultIcon = new GIcon();
        Clusterer.defaultIcon.image = 'http://www.acme.com/resources/images/markers/blue_large.PNG';
        Clusterer.defaultIcon.shadow = 'http://www.acme.com/resources/images/markers/shadow_large.PNG';
        Clusterer.defaultIcon.iconSize = new GSize(30, 51);
        Clusterer.defaultIcon.shadowSize = new GSize(56, 51);
        Clusterer.defaultIcon.iconAnchor = new GPoint(13, 34);
        Clusterer.defaultIcon.infoWindowAnchor = new GPoint(13, 3);
        Clusterer.defaultIcon.infoShadowAnchor = new GPoint(27, 37);
        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);
        };
    }
    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.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 != 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 (clusterer.ForcedClusters) {
        clusterer.maxVisibleMarkers = -1;
        clusterer.minMarkersPerCluster = 0;
    }
    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);
        if (!clusterer.ForcedClusters) {
            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;
                        }
                    }
                }
            }
        }
        else {
            if (clusterer.MaxZoomToMakeCluster >= clusterer.map.getZoom()) {
                for (j = 0; j < clusterer.ForcedClusters.length; j++) {
                    cluster = new Object();
                    cluster.clusterer = clusterer;
                    cluster.markers = [];
                    cluster.markerCount = 0;
                    cluster.onMap = false;
                    cluster.marker = null;
                    clusterer.clusters.push(cluster);
                    for (i = 0; i < visibleMarkers.length; ++i) {
                        var marker = visibleMarkers[i];
                        for (x = 0; x < clusterer.ForcedClusters[j].length; x++) {
                            if (marker.SHData.num_camp == clusterer.ForcedClusters[j][x]) {
                                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);
};

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 src="' + marker.getIcon().image + '" width="' + (marker.getIcon().iconSize.width / 2) + '" height="' + (marker.getIcon().iconSize.height / 2) + '">'; html += '</td><td>' + marker.title + '</td></tr>';
            if (n == clusterer.maxLinesPerInfoBox - 1 && cluster.markerCount > clusterer.maxLinesPerInfoBox) {
                html += '<tr><td colspan="2">...and ' + (cluster.markerCount - n) + ' more</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);
    };
};
