
/*********************************************************************\
*                                                                     *
* egeoxml.js                                         by Mike Williams *
*                                                                     *
* A Google Maps API Extension                                         *
*                                                                     *
* Renders the contents of a My Maps (or similar) KML file             *
*                                                                     *
* Documentation: http://econym.org.uk/gmap/egeoxml.htm                * 
*                                                                     *
***********************************************************************
*                                                                     *
*   This Javascript is provided by Mike Williams                      *
*   Community Church Javascript Team                                  *
*   http://www.bisphamchurch.org.uk/                                  *
*   http://econym.org.uk/gmap/                                        *
*                                                                     *
*   This work is licenced under a Creative Commons Licence            *
*   http://creativecommons.org/licenses/by/2.0/uk/                    *
*                                                                     *
\*********************************************************************/


// Version 0.0   17 Apr 2007 - Initial testing, just markers
// Version 0.1   17 Apr 2007 - Sensible shadows, and a few general improvements
// Version 0.2   18 Apr 2007 - Polylines (non-clickable, no sidebar)
// Version 0.3   18 Apr 2007 - Polygons (non-clickable, no sidebar)
// Version 0.4   18 Apr 2007 - Sidebar entries for polygons
// Version 0.5   19 Apr 2007 - Accept an array of XML filenames, and add the {sortbyname} option
// Version 0.6   19 Apr 2007 - Sidebar entries for polylines, get directions and search nearby
// Version 0.7   20 Apr 2007 - Info Window Styles
// Version 0.8   21 Apr 2007 - baseicon
// Version 0.9   21 Apr 2007 - iwoptions and markeroptions
// Version 1.0   21 Apr 2007 - Launched
// Version 1.1   25 Apr 2007 - Bugfix - would crash if no options were specified
// Version 1.2   25 Apr 2007 - If the description begins with "http://" make it into a link.
// Version 1.3   30 Apr 2007 - printgif, dropbox
// Version 1.4   14 May 2007 - Elabels
// Version 1.5   17 May 2007 - Default values for width, fill and outline
// Version 1.6   21 May 2007 - GGroundOverlay (needs API V2.79+)
// Version 1.7   22 May 2007 - Better icon positioning for MyMaps icons
// Version 1.8   31 May 2007 - polyline bugfix
// Version 1.9   23 Jun 2007 - add .parseString() method
// Version 2.0   23 Jun 2007 - .parseString() handles an array of strings
// Version 2.1   25 Jul 2007 - imagescan
// Version 2.2   10 Aug 2007 - Support new My Maps icons
// Version 2.3   25 Nov 2007 - Clear variables used by .parse() so that it can be rerun
// Version 2.4   08 Dec 2007 - polylineoptions and polygonoptions
// Version 2.5   11 Dec 2007 - EGeoXml.value() trims leading and trailing whitespace 
// Version 2.6   08 Feb 2008 - Trailing whitespace wasn't removed in the previous change
// Version 2.7   14 Oct 2008 - If there's no <description> try looking for <text> instead
// Version 2.8   12 Jan 2009 - Bugfix - last point of poly was omitted
// Version 2.9   05 Feb 2009 - add .hide() .show()

// Constructor

function EGeoXml(myvar, map, url, opts) {
  // store the parameters
  this.myvar = myvar;
  this.map = map;
  this.url = url;
  if (typeof url == "string") {
    this.urls = [url];
  } else {
    this.urls = url;
  }
  this.opts = opts || {};
  // infowindow styles
  this.titlestyle = this.opts.titlestyle || 'style = "font-family: arial, sans-serif;font-size: medium;font-weight:bold;font-size: 100%;"';
  this.descstyle = this.opts.descstyle || 'style = "font-family: arial, sans-serif;font-size: small;padding-bottom:.7em;"';
  this.directionstyle = this.opts.directionstyle || 'style="font-family: arial, sans-serif;font-size: small;padding-left: 1px;padding-top: 1px;padding-right: 4px;"';
  // sidebar/dropbox functions
  this.sidebarfn = this.opts.sidebarfn || EGeoXml.addSidebar;
  this.dropboxfn = this.opts.dropboxfn || EGeoXml.addDropdown;
  // elabel options 
  this.elabelopacity = this.opts.elabelopacity || 100;
  // other useful "global" stuff
  this.bounds = new GLatLngBounds();
  this.gmarkers = [];
  this.gpolylines = [];
  this.gpolygons = [];
  this.groundoverlays = [];
  this.side_bar_html = "";
  this.side_bar_list = [];
  this.styles = []; // associative array
  this.iwwidth = this.opts.iwwidth || 250;
  this.progress = 0;
  this.lastmarker = {};   
  this.myimages = [];
  this.imageNum =0;
}
               
// uses GXml.value, then removes leading and trailing whitespace
EGeoXml.value = function(e) {
  a = GXml.value(e);
  a = a.replace(/^\s*/,"");
  a = a.replace(/\s*$/,"");
  return a;
}

// Create Marker

EGeoXml.prototype.createMarker = function(point,name,desc,style) {
  var icon = G_DEFAULT_ICON;
  var myvar=this.myvar;
  var iwoptions = this.opts.iwoptions || {};
  var markeroptions = this.opts.markeroptions || {};
  var icontype = this.opts.icontype || "style";
  if (icontype == "style") {
    if (!!this.styles[style]) {
      icon = this.styles[style];
    }
  }
  if (!markeroptions.icon) {
    markeroptions.icon = icon;
  }
  var m = new GMarker(point, markeroptions);

  // Attempt to preload images
  if (this.opts.preloadimages) {
    var text = desc;
    var pattern = /<\s*img/ig;
    var result;
    var pattern2 = /src\s*=\s*[\'\"]/;
    var pattern3 = /[\'\"]/;

    while ((result = pattern.exec(text)) != null) {
      var stuff = text.substr(result.index);
      var result2 = pattern2.exec(stuff);
      if (result2 != null) {
        stuff = stuff.substr(result2.index+result2[0].length);
        var result3 = pattern3.exec(stuff);
        if (result3 != null) {
          var imageUrl = stuff.substr(0,result3.index);
          this.myimages[this.imageNum] = new Image();
          this.myimages[this.imageNum].src = imageUrl;
          this.imageNum++;
        }
      }
    }
  }



  if (this.opts.elabelclass) {
    var l = new ELabel(point, name, this.opts.elabelclass, this.opts.elabeloffset, this.elabelopacity, true);
    this.map.addOverlay(l);
  }

  var html = "<div style = 'width:"+this.iwwidth+"px'>"
               + "<h1 "+this.titlestyle+">"+name+"</h1>"
               +"<div "+this.descstyle+">"+desc+"</div>";

  if (this.opts.directions) {
    var html1 = html + '<div '+this.directionstyle+'>'
                     + 'Get Directions: <a href="javascript:GEvent.trigger(' + this.myvar +'.lastmarker,\'click2\')">To Here</a> - ' 
                     + '<a href="javascript:GEvent.trigger(' + this.myvar +'.lastmarker,\'click3\')">From Here</a><br>'
                     + '<a href="javascript:GEvent.trigger(' + this.myvar +'.lastmarker,\'click4\')">Search nearby</a></div>';
    var html2 = html + '<div '+this.directionstyle+'>'
                     + 'Get Directions: To here - '
                     + '<a href="javascript:GEvent.trigger(' + this.myvar +'.lastmarker,\'click3\')">From Here</a><br>'
                     + 'Start address:<form action="http://maps.google.com/maps" method="get" target="_blank">'
                     + '<input type="text" SIZE=35 MAXLENGTH=80 name="saddr" id="saddr" value="" />'
                     + '<INPUT value="Go" TYPE="SUBMIT">'
                     + '<input type="hidden" name="daddr" value="' + point.lat() + ',' + point.lng() + "(" + name + ")" + '"/>'
                     + '<br><a href="javascript:GEvent.trigger(' + this.myvar +'.lastmarker,\'click\')">« Back</a></div>';
    var html3 = html + '<div '+this.directionstyle+'>'
                     + 'Get Directions: <a href="javascript:GEvent.trigger(' + this.myvar +'.lastmarker,\'click2\')">To Here</a> - ' 
                     + 'From Here<br>'
                     + 'End address:<form action="http://maps.google.com/maps" method="get"" target="_blank">'
                     + '<input type="text" SIZE=35 MAXLENGTH=80 name="daddr" id="daddr" value="" />'
                     + '<INPUT value="Go" TYPE="SUBMIT">'
                     + '<input type="hidden" name="saddr" value="' + point.lat() + ',' + point.lng() +  "(" + name + ")" + '"/>'
                     + '<br><a href="javascript:GEvent.trigger(' + this.myvar +'.lastmarker,\'click\')">« Back</a></div>';
    var html4 = html + '<div '+this.directionstyle+'>'
                     + 'Search nearby: e.g. "pizza"<br>'
                     + '<form action="http://maps.google.com/maps" method="get"" target="_blank">'
                     + '<input type="text" SIZE=35 MAXLENGTH=80 name="q" id="q" value="" />'
                     + '<INPUT value="Go" TYPE="SUBMIT">'
                     + '<input type="hidden" name="near" value="' + name + ' @' + point.lat() + ',' + point.lng() + '"/>'
                   //  + '<input type="hidden" name="near" value="' +  point.lat() + ',' + point.lng() +  "(" + name + ")" + '"/>';
                     + '<br><a href="javascript:GEvent.trigger(' + this.myvar +'.lastmarker,\'click\')">« Back</a></div>';
    GEvent.addListener(m, "click2", function() {
      m.openInfoWindowHtml(html2 + "</div>",iwoptions);
    });
    GEvent.addListener(m, "click3", function() {
      m.openInfoWindowHtml(html3 + "</div>",iwoptions);
    });
    GEvent.addListener(m, "click4", function() {
      m.openInfoWindowHtml(html4 + "</div>",iwoptions);
    });
  } else {
    var html1 = html;
  }

  GEvent.addListener(m, "click", function() {
    eval(myvar+".lastmarker = m");
    m.openInfoWindowHtml(html1 + "</div>",iwoptions);
  });
  if (!!this.opts.addmarker) {
    this.opts.addmarker(m,name,desc,icon.image,this.gmarkers.length)
  } else {
    this.map.addOverlay(m);
  }
  this.gmarkers.push(m);
  if (this.opts.sidebarid || this.opts.dropboxid) {
    var n = this.gmarkers.length-1;
    this.side_bar_list.push (name + "$$$marker$$$" + n +"$$$" );
  }
}

// Create Polyline

EGeoXml.prototype.createPolyline = function(points,color,width,opacity,pbounds,name,desc) {
  var thismap = this.map;
  var iwoptions = this.opts.iwoptions || {};
  var polylineoptions = this.opts.polylineoptions || {};
  var p = new GPolyline(points,color,width,opacity,polylineoptions);
  this.map.addOverlay(p);
  this.gpolylines.push(p);
  var html = "<div style='font-weight: bold; font-size: medium; margin-bottom: 0em;'>"+name+"</div>"
             +"<div style='font-family: Arial, sans-serif;font-size: small;width:"+this.iwwidth+"px'>"+desc+"</div>";
  GEvent.addListener(p,"click", function() {
    thismap.openInfoWindowHtml(p.getVertex(Math.floor(p.getVertexCount()/2)),html,iwoptions);
  } );
  if (this.opts.sidebarid) {
    var n = this.gpolylines.length-1;
    var blob = '&nbsp;&nbsp;<span style=";border-left:'+width+'px solid '+color+';">&nbsp;</span> ';
    this.side_bar_list.push (name + "$$$polyline$$$" + n +"$$$" + blob );
  }
}

// Create Polygon

EGeoXml.prototype.createPolygon = function(points,color,width,opacity,fillcolor,fillopacity,pbounds, name, desc) {
  var thismap = this.map;
  var iwoptions = this.opts.iwoptions || {};
  var polygonoptions = this.opts.polygonoptions || {};
  var p = new GPolygon(points,color,width,opacity,fillcolor,fillopacity,polygonoptions)
  this.map.addOverlay(p);
  this.gpolygons.push(p);
  var html = "<div style='font-weight: bold; font-size: medium; margin-bottom: 0em;'>"+name+"</div>"
             +"<div style='font-family: Arial, sans-serif;font-size: small;width:"+this.iwwidth+"px'>"+desc+"</div>";
  GEvent.addListener(p,"click", function() {
    thismap.openInfoWindowHtml(pbounds.getCenter(),html,iwoptions);
  } );
  if (this.opts.sidebarid) {
    var n = this.gpolygons.length-1;
    var blob = '<span style="background-color:' +fillcolor + ';border:2px solid '+color+';">&nbsp;&nbsp;&nbsp;&nbsp;</span> ';
    this.side_bar_list.push (name + "$$$polygon$$$" + n +"$$$" + blob );
  }
}


// Sidebar factory method One - adds an entry to the sidebar
EGeoXml.addSidebar = function(myvar,name,type,i,graphic) {
  if (type == "marker") {
    return '<a href="javascript:GEvent.trigger(' + myvar+ '.gmarkers['+i+'],\'click\')">' + name + '</a><br>';
  }
  if (type == "polyline") {
    return '<div style="margin-top:6px;"><a href="javascript:GEvent.trigger(' + myvar+ '.gpolylines['+i+'],\'click\')">' + graphic + name + '</a></div>';
  }
  if (type == "polygon") {
    return '<div style="margin-top:6px;"><a href="javascript:GEvent.trigger(' + myvar+ '.gpolygons['+i+'],\'click\')">' + graphic + name + '</a></div>';
  }
}

// Dropdown factory method
EGeoXml.addDropdown = function(myvar,name,type,i,graphic) {
    return '<option value="' + i + '">' + name +'</option>';
}

  
// Request to Parse an XML file

EGeoXml.prototype.parse = function() {
  // clear some variables
  this.gmarkers = [];
  this.gpolylines = [];
  this.gpolygons = [];
  this.groundoverlays = [];
  this.side_bar_html = "";
  this.side_bar_list = [];
  this.styles = []; // associative array
  this.lastmarker = {};   
  this.myimages = [];
  this.imageNum =0;
  var that = this;
  this.progress = this.urls.length;
  
  var sFileUrl = "";
  var sParams = "";
  var iParamIndex = 0;
  
  for (u=0; u<this.urls.length; u++) {
  	sFileUrl = "";
	sParams = "";
	
	iParamIndex = this.urls[u].indexOf("?");
	if (iParamIndex != -1) {
		sFileUrl = this.urls[u].substr(0,iParamIndex);
		sParams = this.urls[u].substr(iParamIndex + 1);
	}
	else {
		sFileUrl = this.urls[u];
	}
	
	
    GDownloadUrl(sFileUrl, function(doc) {that.processing(doc)}, sParams);
  }
}

EGeoXml.prototype.parseString = function(doc) {
  // clear some variables
  this.gmarkers = [];
  this.gpolylines = [];
  this.gpolygons = [];
  this.groundoverlays = [];
  this.side_bar_html = "";
  this.side_bar_list = [];
  this.styles = []; // associative array
  this.lastmarker = {};   
  this.myimages = [];
  this.imageNum =0;
  if (typeof doc == "string") {
    this.docs = [doc];
  } else {
    this.docs = doc;
  }
  this.progress = this.docs.length;
  for (u=0; u<this.docs.length; u++) {
    this.processing(this.docs[u]);
  }
}


EGeoXml.prototype.processing = function(doc) {
    var that = this;
    var xmlDoc = GXml.parse(doc)
    // Read through the Styles
    var styles = xmlDoc.documentElement.getElementsByTagName("Style");
    for (var i = 0; i <styles.length; i++) {
      var styleID = styles[i].getAttribute("id");
      var icons=styles[i].getElementsByTagName("Icon");
      // This might not be am icon style
      if (icons.length > 0) {
        var href=EGeoXml.value(icons[0].getElementsByTagName("href")[0]);
        if (!!href) {
          if (!!that.opts.baseicon) {
            that.styles["#"+styleID] = new GIcon(that.opts.baseicon,href);
          } else {
            that.styles["#"+styleID] = new GIcon(G_DEFAULT_ICON,href);
            that.styles["#"+styleID].iconSize = new GSize(32,32);
            that.styles["#"+styleID].shadowSize = new GSize(59,32);
            that.styles["#"+styleID].dragCrossAnchor = new GPoint(2,8);
            that.styles["#"+styleID].iconAnchor = new GPoint(16,32);
            if (that.opts.printgif) {
              var bits = href.split("/");
              var gif = bits[bits.length-1];
              gif = that.opts.printgifpath + gif.replace(/.png/i,".gif");
              that.styles["#"+styleID].printImage = gif;
              that.styles["#"+styleID].mozPrintImage = gif;
            }
            if (!!that.opts.noshadow) {
              that.styles["#"+styleID].shadow="";
            } else {
              // Try to guess the shadow image
              if (href.indexOf("/red.png")>-1 
               || href.indexOf("/blue.png")>-1 
               || href.indexOf("/green.png")>-1 
               || href.indexOf("/yellow.png")>-1 
               || href.indexOf("/lightblue.png")>-1 
               || href.indexOf("/purple.png")>-1 
               || href.indexOf("/pink.png")>-1 
               || href.indexOf("/orange.png")>-1 
               || href.indexOf("-dot.png")>-1 ) {
                  that.styles["#"+styleID].shadow="http://maps.google.com/mapfiles/ms/micons/msmarker.shadow.png";
              }
              else if (href.indexOf("-pushpin.png")>-1) {
                  that.styles["#"+styleID].shadow="http://maps.google.com/mapfiles/ms/micons/pushpin_shadow.png";
              }
              else {
                var shadow = href.replace(".png",".shadow.png");
                that.styles["#"+styleID].shadow=shadow;
              }
            }
          }
        }
      }
      // is it a LineStyle ?
      var linestyles=styles[i].getElementsByTagName("LineStyle");
      if (linestyles.length > 0) {
        var width = parseInt(GXml.value(linestyles[0].getElementsByTagName("width")[0]));
        if (width < 1) {width = 5;}
        var color = EGeoXml.value(linestyles[0].getElementsByTagName("color")[0]);
        var aa = color.substr(0,2);
        var bb = color.substr(2,2);
        var gg = color.substr(4,2);
        var rr = color.substr(6,2);
        color = "#" + rr + gg + bb;
        var opacity = parseInt(aa,16)/256;
        if (!that.styles["#"+styleID]) {
          that.styles["#"+styleID] = {};
        }
        that.styles["#"+styleID].color=color;
        that.styles["#"+styleID].width=width;
        that.styles["#"+styleID].opacity=opacity;
      }
      // is it a PolyStyle ?
      var polystyles=styles[i].getElementsByTagName("PolyStyle");
      if (polystyles.length > 0) {
        var fill = parseInt(GXml.value(polystyles[0].getElementsByTagName("fill")[0]));
        var outline = parseInt(GXml.value(polystyles[0].getElementsByTagName("outline")[0]));
        var color = EGeoXml.value(polystyles[0].getElementsByTagName("color")[0]);

        if (polystyles[0].getElementsByTagName("fill").length == 0) {fill = 1;}
        if (polystyles[0].getElementsByTagName("outline").length == 0) {outline = 1;}
        var aa = color.substr(0,2);
        var bb = color.substr(2,2);
        var gg = color.substr(4,2);
        var rr = color.substr(6,2);
        color = "#" + rr + gg + bb;

        var opacity = parseInt(aa,16)/256;
        if (!that.styles["#"+styleID]) {
          that.styles["#"+styleID] = {};
        }
        that.styles["#"+styleID].fillcolor=color;
        that.styles["#"+styleID].fillopacity=opacity;
        if (!fill) that.styles["#"+styleID].fillopacity = 0; 
        if (!outline) that.styles["#"+styleID].opacity = 0; 
      }
    }

    // Read through the Placemarks
    var placemarks = xmlDoc.documentElement.getElementsByTagName("Placemark");
    for (var i = 0; i < placemarks.length; i++) {
      var name=EGeoXml.value(placemarks[i].getElementsByTagName("name")[0]);
      var desc=EGeoXml.value(placemarks[i].getElementsByTagName("description")[0]);
      if (desc=="") {
        var desc=EGeoXml.value(placemarks[i].getElementsByTagName("text")[0]);
        desc = desc.replace(/\$\[name\]/,name);
        desc = desc.replace(/\$\[geDirections\]/,"");
      }
      if (desc.match(/^http:\/\//i)) {
        desc = '<a href="' + desc + '">' + desc + '</a>';
      }
      if (desc.match(/^https:\/\//i)) {
        desc = '<a href="' + desc + '">' + desc + '</a>';
      }
      var style=EGeoXml.value(placemarks[i].getElementsByTagName("styleUrl")[0]);
      var coords=GXml.value(placemarks[i].getElementsByTagName("coordinates")[0]);
      coords=coords.replace(/\s+/g," "); // tidy the whitespace
      coords=coords.replace(/^ /,"");    // remove possible leading whitespace
      coords=coords.replace(/ $/,"");    // remove possible trailing whitespace
      coords=coords.replace(/, /,",");   // tidy the commas
      var path = coords.split(" ");

      // Is this a polyline/polygon?
      if (path.length > 1) {
        // Build the list of points
        var points = [];
        var pbounds = new GLatLngBounds();
        for (var p=0; p<path.length; p++) {
          var bits = path[p].split(",");
          var point = new GLatLng(parseFloat(bits[1]),parseFloat(bits[0]));
          points.push(point);
          that.bounds.extend(point);
          pbounds.extend(point);
        }
        var linestring=placemarks[i].getElementsByTagName("LineString");
        if (linestring.length) {
          // it's a polyline grab the info from the style
          if (!!that.styles[style]) {
            var width = that.styles[style].width; 
            var color = that.styles[style].color; 
            var opacity = that.styles[style].opacity; 
          } else {
            var width = 5;
            var color = "#0000ff";
            var opacity = 0.45;
          }
          // Does the user have their own createmarker function?
          if (!!that.opts.createpolyline) {
            that.opts.createpolyline(points,color,width,opacity,pbounds,name,desc);
          } else {
            that.createPolyline(points,color,width,opacity,pbounds,name,desc);
          }
        }

        var polygons=placemarks[i].getElementsByTagName("Polygon");
        if (polygons.length) {
          // it's a polygon grab the info from the style
          if (!!that.styles[style]) {
            var width = that.styles[style].width; 
            var color = that.styles[style].color; 
            var opacity = that.styles[style].opacity; 
            var fillopacity = that.styles[style].fillopacity; 
            var fillcolor = that.styles[style].fillcolor; 
          } else {
            var width = 5;
            var color = "#0000ff";
            var opacity = 0.45;
            var fillopacity = 0.25;
            var fillcolor = "#0055ff";
          }
          // Does the user have their own createmarker function?
          if (!!that.opts.createpolygon) {
            that.opts.createpolygon(points,color,width,opacity,fillcolor,fillopacity,pbounds,name,desc);
          } else {
            that.createPolygon(points,color,width,opacity,fillcolor,fillopacity,pbounds,name,desc);
          }
        }


      } else {
        // It's not a poly, so I guess it must be a marker
        var bits = path[0].split(",");
        var point = new GLatLng(parseFloat(bits[1]),parseFloat(bits[0]));
        that.bounds.extend(point);
        // Does the user have their own createmarker function?
        if (!!that.opts.createmarker) {
          that.opts.createmarker(point, name, desc, style);
        } else {
          that.createMarker(point, name, desc, style);
        }
      }
    }
    
    // Scan through the Ground Overlays
    var grounds = xmlDoc.documentElement.getElementsByTagName("GroundOverlay");
    for (var i = 0; i < grounds.length; i++) {
      var url=EGeoXml.value(grounds[i].getElementsByTagName("href")[0]);
      var north=parseFloat(GXml.value(grounds[i].getElementsByTagName("north")[0]));
      var south=parseFloat(GXml.value(grounds[i].getElementsByTagName("south")[0]));
      var east=parseFloat(GXml.value(grounds[i].getElementsByTagName("east")[0]));
      var west=parseFloat(GXml.value(grounds[i].getElementsByTagName("west")[0]));
      var sw = new GLatLng(south,west);
      var ne = new GLatLng(north,east);                           
      var ground = new GGroundOverlay(url, new GLatLngBounds(sw,ne));
      that.bounds.extend(sw); 
      that.bounds.extend(ne); 
      that.groundoverlays.push(ground);
      that.map.addOverlay(ground);
    }

    // Is this the last file to be processed?
    that.progress--;
    if (that.progress == 0) {
      // Shall we zoom to the bounds?
      if (!that.opts.nozoom) {
        that.map.setZoom(that.map.getBoundsZoomLevel(that.bounds));
        that.map.setCenter(that.bounds.getCenter());
      }
      // Shall we display the sidebar?
      if (that.opts.sortbyname) {
        that.side_bar_list.sort();
      }
      if (that.opts.sidebarid) {
        for (var i=0; i<that.side_bar_list.length; i++) {
          var bits = that.side_bar_list[i].split("$$$",4);
          that.side_bar_html += that.sidebarfn(that.myvar,bits[0],bits[1],bits[2],bits[3]); 
        }
        document.getElementById(that.opts.sidebarid).innerHTML += that.side_bar_html;
      }
      if (that.opts.dropboxid) {
        for (var i=0; i<that.side_bar_list.length; i++) {
          var bits = that.side_bar_list[i].split("$$$",4);
          if (bits[1] == "marker") {
            that.side_bar_html += that.dropboxfn(that.myvar,bits[0],bits[1],bits[2],bits[3]); 
          }
        }
        document.getElementById(that.opts.dropboxid).innerHTML = '<select onChange="var I=this.value;if(I>-1){GEvent.trigger('+that.myvar+'.gmarkers[I],\'click\'); }">'
          + '<option selected> - Select a location - </option>'
          + that.side_bar_html
          + '</select>';
      }

      GEvent.trigger(that,"parsed");
    }
}

EGeoXml.prototype.hide = function() {
  for (var i=0; i<this.gmarkers.length; i++) {
    this.gmarkers[i].hide();
  }
  for (var i=0; i<this.gpolylines.length; i++) {
    this.gpolylines[i].hide();
  }
  for (var i=0; i<this.gpolygons.length; i++) {
    this.gpolygons[i].hide();
  }
  for (var i=0; i<this.groundoverlays.length; i++) {
    this.groundoverlays[i].hide();
  }
  if (this.opts.sidebarid) {
    document.getElementById(this.opts.sidebarid).style.display="none";
  }
  if (this.opts.dropboxid) {
    document.getElementById(this.opts.dropboxid).style.display="none";
  }
}

EGeoXml.prototype.show = function() {
  for (var i=0; i<this.gmarkers.length; i++) {
    this.gmarkers[i].show();
  }
  for (var i=0; i<this.gpolylines.length; i++) {
    this.gpolylines[i].show();
  }
  for (var i=0; i<this.gpolygons.length; i++) {
    this.gpolygons[i].show();
  }
  for (var i=0; i<this.groundoverlays.length; i++) {
    this.groundoverlays[i].show();
  }
  if (this.opts.sidebarid) {
    document.getElementById(this.opts.sidebarid).style.display="";
  }
  if (this.opts.dropboxid) {
    document.getElementById(this.opts.dropboxid).style.display="";
  }
}

EGeoXml.prototype.removeOverlay = function (){ 
	if(this.gmarkers.length != 0) { 
		for(i=0;i<this.gmarkers.length;i++) { 
			map.removeOverlay(this.gmarkers[i]); 
		} 
	} 
	if(this.gpolylines.length != 0) { 
		for(i=0;i<this.gpolylines.length;i++) { 
			map.removeOverlay(this.gpolylines[i]); 
		} 
	} 
	if(this.gpolygons.length != 0) { 
		for(i=0;i<this.gpolygons.length;i++) { 
			map.removeOverlay(this.gpolygons[i]); 
		} 
	} 
}
/**
 * @name MarkerClusterer
 * @version 1.0
 * @author Xiaoxi Wu
 * @copyright (c) 2009 Xiaoxi Wu
 * @fileoverview
 * This javascript library creates and manages per-zoom-level 
 * clusters for large amounts of markers (hundreds or thousands).
 * This library was inspired by the <a href="http://www.maptimize.com">
 * Maptimize</a> hosted clustering solution.
 * <br /><br/>
 * <b>How it works</b>:<br/>
 * The <code>MarkerClusterer</code> will group markers into clusters according to
 * their distance from a cluster's center. When a marker is added,
 * the marker cluster will find a position in all the clusters, and 
 * if it fails to find one, it will create a new cluster with the marker.
 * The number of markers in a cluster will be displayed
 * on the cluster marker. When the map viewport changes,
 * <code>MarkerClusterer</code> will destroy the clusters in the viewport 
 * and regroup them into new clusters.
 *
 */

/**
 * Method initialize was changed for the application "Reiseapplikation"
 * since the Markers have to open a special InfoWindow when clicked.
 */
 
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


/**
 * @name MarkerClustererOptions
 * @class This class represents optional arguments to the {@link MarkerClusterer}
 * constructor.
 * @property {Number} [maxZoom] The max zoom level monitored by a
 * marker cluster. If not given, the marker cluster assumes the maximum map
 * zoom level. When maxZoom is reached or exceeded all markers will be shown
 * without cluster.
 * @property {Number} [gridSize=60] The grid size of a cluster in pixel. Each
 * cluster will be a square. If you want the algorithm to run faster, you can set
 * this value larger.
 * @property {Array of MarkerStyleOptions} [styles]
 * Custom styles for the cluster markers.
 * The array should be ordered according to increasing cluster size,
 * with the style for the smallest clusters first, and the style for the
 * largest clusters last.
 */

/**
 * @name MarkerStyleOptions
 * @class An array of these is passed into the {@link MarkerClustererOptions}
 * styles option.
 * @property {String} [url] Image url.
 * @property {Number} [height] Image height.
 * @property {Number} [height] Image width.
 * @property {Array of Number} [opt_anchor] Anchor for label text, like [24, 12]. 
 *    If not set, the text will align center and middle.
 * @property {String} [opt_textColor="black"] Text color.
 */

/**
 * Creates a new MarkerClusterer to cluster markers on the map.
 *
 * @constructor
 * @param {GMap2} map The map that the markers should be added to.
 * @param {Array of GMarker} opt_markers Initial set of markers to be clustered.
 * @param {MarkerClustererOptions} opt_opts A container for optional arguments.
 */
function MarkerClusterer(map, opt_markers, opt_opts) {
  // private members
  var clusters_ = [];
  var map_ = null;
  if (!map_) {
  	map_ = map;
  }
  var maxZoom_ = null;
  var me_ = this;
  var gridSize_ = 60;
  var sizes = [53, 56, 66, 78, 90];
  var styles_ = [];
  var leftMarkers_ = [];
  var mcfn_ = null;
  var divClick_ = null;
  this.markers = opt_markers;
  this.sw = null;
  this.ne = null;

  var i = 0;
  for (i = 1; i <= 5; ++i) {
    styles_.push({
      'url': "http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/images/m" + i + ".png",
      'height': sizes[i - 1],
      'width': sizes[i - 1]
    });
  }

  if (typeof opt_opts === "object" && opt_opts !== null) {
    if (typeof opt_opts.gridSize === "number" && opt_opts.gridSize > 0) {
      gridSize_ = opt_opts.gridSize;
    }
    if (typeof opt_opts.maxZoom === "number") {
      maxZoom_ = opt_opts.maxZoom;
    }
    if (typeof opt_opts.styles === "object" && opt_opts.styles !== null && opt_opts.styles.length !== 0) {
      styles_ = opt_opts.styles;
    }
    if (typeof opt_opts.divClick !== null) {
      divClick_ = opt_opts.divClick;
    }
  }

  /**
   * When we add a marker, the marker may not in the viewport of map, then we don't deal with it, instead
   * we add the marker into a array called leftMarkers_. When we reset MarkerClusterer we should add the
   * leftMarkers_ into MarkerClusterer.
   */
  function addLeftMarkers_() {
    if (leftMarkers_.length === 0) {
      return;
    }
    var leftMarkers = [];
    for (i = 0; i < leftMarkers_.length; ++i) {
      me_.addMarker(leftMarkers_[i], true, null, null, true);
    }
    leftMarkers_ = leftMarkers;
  }

  /**
   * Get cluster marker images of this marker cluster. Mostly used by {@link Cluster}
   * @private
   * @return {Array of String}
   */
  this.getStyles_ = function () {
    return styles_;
  };


  /**
   * Remove all markers from MarkerClusterer.
   */
  this.clearMarkers = function () {
    for (var i = 0; i < clusters_.length; ++i) {
      if (typeof clusters_[i] !== "undefined" && clusters_[i] !== null) {
        clusters_[i].clearMarkers();
      }
    }
    clusters_ = [];
    leftMarkers_ = [];
    GEvent.removeListener(mcfn_);
  };

  /**
   * Check a marker, whether it is in current map viewport.
   * @private
   * @return {Boolean} if it is in current map viewport
   */
  function isMarkerInViewport_(marker) {
    return map_.getBounds().containsLatLng(marker.getLatLng());
  }

  /**
   * When reset MarkerClusterer, there will be some markers get out of its cluster.
   * These markers should be add to new clusters.
   * @param {Array of GMarker} markers Markers to add.
   */
  function reAddMarkers_(markers) {
    var len = markers.length;
    var clusters = [];
    for (var i = len - 1; i >= 0; --i) {
      me_.addMarker(markers[i].marker, true, markers[i].isAdded, clusters, true);
    }
    addLeftMarkers_();
  }

  /**
   * Add a marker.
   * @private
   * @param {GMarker} marker Marker you want to add
   * @param {Boolean} opt_isNodraw Whether redraw the cluster contained the marker
   * @param {Boolean} opt_isAdded Whether the marker is added to map. Never use it.
   * @param {Array of Cluster} opt_clusters Provide a list of clusters, the marker
   *     cluster will only check these cluster where the marker should join.
   */
  this.addMarker = function (marker, opt_isNodraw, opt_isAdded, opt_clusters, opt_isNoCheck) {
    if (opt_isNoCheck !== true) {
      if (!isMarkerInViewport_(marker)) {
        leftMarkers_.push(marker);
        return;
      }
    }

    var isAdded = opt_isAdded;
    var clusters = opt_clusters;
    var pos = map_.fromLatLngToDivPixel(marker.getLatLng());

    if (typeof isAdded !== "boolean") {
      isAdded = false;
    }
    if (typeof clusters !== "object" || clusters === null) {
      clusters = clusters_;
    }

    var length = clusters.length;
    var cluster = null;
    for (var i = length - 1; i >= 0; i--) {
      cluster = clusters[i];
      var center = cluster.getCenter();
      if (center === null) {
        continue;
      }
      center = map_.fromLatLngToDivPixel(center);

      // Found a cluster which contains the marker.
      if (pos.x >= center.x - gridSize_ && pos.x <= center.x + gridSize_ &&
          pos.y >= center.y - gridSize_ && pos.y <= center.y + gridSize_) {
        cluster.addMarker({
          'isAdded': isAdded,
          'marker': marker
        });
        if (!opt_isNodraw) {
          cluster.redraw_();
        }
        return;
      }
    }

    // No cluster contain the marker, create a new cluster.
    cluster = new Cluster(this, map);
    cluster.addMarker({
      'isAdded': isAdded,
      'marker': marker
    });
    if (!opt_isNodraw) {
      cluster.redraw_();
    }

    // Add this cluster both in clusters provided and clusters_
    clusters.push(cluster);
    if (clusters !== clusters_) {
      clusters_.push(cluster);
    }
  };

  /**
   * Remove a marker.
   *
   * @param {GMarker} marker The marker you want to remove.
   */

  this.removeMarker = function (marker) {
    for (var i = 0; i < clusters_.length; ++i) {
      if (clusters_[i].removeMarker(marker)) {
        clusters_[i].redraw_();
        return;
      }
    }
  };

  /**
   * Redraw all clusters in viewport.
   */
  this.redraw_ = function () {
    var clusters = this.getClustersInViewport_();
    for (var i = 0; i < clusters.length; ++i) {
      clusters[i].redraw_(true);
    }
  };

  /**
   * Get all clusters in viewport.
   * @return {Array of Cluster}
   */
  this.getClustersInViewport_ = function () {
    var clusters = [];
    var curBounds = map_.getBounds();
    for (var i = 0; i < clusters_.length; i ++) {
      if (clusters_[i].isInBounds(curBounds)) {
        clusters.push(clusters_[i]);
      }
    }
    return clusters;
  };

  /**
   * Get max zoom level.
   * @private
   * @return {Number}
   */
  this.getMaxZoom_ = function () {
    return maxZoom_;
  };

  /**
   * Get map object.
   * @private
   * @return {GMap2}
   */
  this.getMap_ = function () {
    return map_;
  };

  /**
   * Get grid size
   * @private
   * @return {Number}
   */
  this.getGridSize_ = function () {
    return gridSize_;
  };

  /**
   * Get grid size
   * @private
   * @return {Number}
   */
  this.getDivClick_ = function () {
    return divClick_;
  };

  /**
   * Get total number of markers.
   * @return {Number}
   */
  this.getTotalMarkers = function () {
    var result = 0;
    for (var i = 0; i < clusters_.length; ++i) {
      result += clusters_[i].getTotalMarkers();
    }
    return result;
  };

  /**
   * Get total number of clusters.
   * @return {int}
   */
  this.getTotalClusters = function () {
    return clusters_.length;
  };

  /**
   * Get total number of clusters.
   * @return {int}
   */
  this.getTotalClusters = function () {
    return clusters_.length;
  };

  /**

   * Collect all markers of clusters in viewport and regroup them.
   */
  this.resetViewport = function () {
    var clusters = this.getClustersInViewport_();
    var tmpMarkers = [];
    var removed = 0;

    for (var i = 0; i < clusters.length; ++i) {
      var cluster = clusters[i];
      var oldZoom = cluster.getCurrentZoom();
      if (oldZoom === null) {
        continue;
      }
      var curZoom = map_.getZoom();
      if (curZoom !== oldZoom) {

        // If the cluster zoom level changed then destroy the cluster
        // and collect its markers.
        var mks = cluster.getMarkers();
        for (var j = 0; j < mks.length; ++j) {
          var newMarker = {
            'isAdded': false,
            'marker': mks[j].marker
          };
          tmpMarkers.push(newMarker);
        }
        cluster.clearMarkers();
        removed++;
        for (j = 0; j < clusters_.length; ++j) {
          if (cluster === clusters_[j]) {
            clusters_.splice(j, 1);
          }
        }
      }
    }

    // Add the markers collected into marker cluster to reset
    reAddMarkers_(tmpMarkers);
    this.redraw_();
  };


  /**
   * Add a set of markers.
   *
   * @param {Array of GMarker} markers The markers you want to add.
   */
  this.addMarkers = function (markers) {
    for (var i = 0; i < markers.length; ++i) {
      this.addMarker(markers[i], true);
    }
    this.redraw_();
  };

  // initialize
  if (typeof opt_markers === "object" && opt_markers !== null) {
    this.addMarkers(opt_markers);
  }

  // when map move end, regroup.
  mcfn_ = GEvent.addListener(map_, "moveend", function () {
    me_.resetViewport();
  });
  
}

/**
 * Create a cluster to collect markers.
 * A cluster includes some markers which are in a block of area.
 * If there are more than one markers in cluster, the cluster
 * will create a {@link ClusterMarker_} and show the total number
 * of markers in cluster.
 *
 * @constructor
 * @private
 * @param {MarkerClusterer} markerClusterer The marker cluster object
 */
function Cluster(markerClusterer) {
  var center_ = null;
  var markers_ = [];
  var markerClusterer_ = markerClusterer;
  var map_ = markerClusterer.getMap_();
  var clusterMarker_ = null;
  var zoom_ = map_.getZoom();

  /**
   * Get markers of this cluster.
   *
   * @return {Array of GMarker}
   */
  this.getMarkers = function () {
    return markers_;
  };

  /**
   * If this cluster intersects certain bounds.
   *
   * @param {GLatLngBounds} bounds A bounds to test
   * @return {Boolean} Is this cluster intersects the bounds
   */
  this.isInBounds = function (bounds) {
    if (center_ === null) {
      return false;
    }

    if (!bounds) {
      bounds = map_.getBounds();
    }
    var sw = map_.fromLatLngToDivPixel(bounds.getSouthWest());
    var ne = map_.fromLatLngToDivPixel(bounds.getNorthEast());

    var centerxy = map_.fromLatLngToDivPixel(center_);
    var inViewport = true;
    var gridSize = markerClusterer.getGridSize_();
    if (zoom_ !== map_.getZoom()) {
      var dl = map_.getZoom() - zoom_;
      gridSize = Math.pow(2, dl) * gridSize;
    }
    if (ne.x !== sw.x && (centerxy.x + gridSize < sw.x || centerxy.x - gridSize > ne.x)) {
      inViewport = false;
    }
    if (inViewport && (centerxy.y + gridSize < ne.y || centerxy.y - gridSize > sw.y)) {
      inViewport = false;
    }
    return inViewport;
  };

  /**
   * Get cluster center.
   *
   * @return {GLatLng}
   */
  this.getCenter = function () {
    return center_;
  };

  /**
   * Add a marker.
   *
   * @param {Object} marker An object of marker you want to add:
   *   {Boolean} isAdded If the marker is added on map.
   *   {GMarker} marker The marker you want to add.
   */
   
  this.addMarker = function (marker) {
    if (center_ === null) {
      /*var pos = marker['marker'].getLatLng();
       pos = map.fromLatLngToContainerPixel(pos);
       pos.x = parseInt(pos.x - pos.x % (GRIDWIDTH * 2) + GRIDWIDTH);
       pos.y = parseInt(pos.y - pos.y % (GRIDWIDTH * 2) + GRIDWIDTH);
       center = map.fromContainerPixelToLatLng(pos);*/
      center_ = marker.marker.getLatLng();
    }
    markers_.push(marker);
  };

  /**
   * Remove a marker from cluster.
   *
   * @param {GMarker} marker The marker you want to remove.
   * @return {Boolean} Whether find the marker to be removed.
   */
  this.removeMarker = function (marker) {
    for (var i = 0; i < markers_.length; ++i) {
      if (marker === markers_[i].marker) {
        if (markers_[i].isAdded) {
          map_.removeOverlay(markers_[i].marker);
        }
        markers_.splice(i, 1);
        return true;
      }
    }
    return false;
  };

  /**
   * Get current zoom level of this cluster.
   * Note: the cluster zoom level and map zoom level not always the same.
   *
   * @return {Number}
   */
  this.getCurrentZoom = function () {
    return zoom_;
  };

  /**
   * Redraw a cluster.
   * @private
   * @param {Boolean} isForce If redraw by force, no matter if the cluster is
   *     in viewport.
   */
  this.redraw_ = function (isForce) {
    if (!isForce && !this.isInBounds()) {
      return;
    }

    // Set cluster zoom level.
    zoom_ = map_.getZoom();
    var i = 0;
    var mz = markerClusterer.getMaxZoom_();
    if (mz === null) {
      mz = map_.getCurrentMapType().getMaximumResolution();
    }
    if (zoom_ >= mz || this.getTotalMarkers() === 1) {

      // If current zoom level is beyond the max zoom level or the cluster
      // have only one marker, the marker(s) in cluster will be showed on map.
      for (i = 0; i < markers_.length; ++i) {
        if (markers_[i].isAdded) {
          if (markers_[i].marker.isHidden()) {
            markers_[i].marker.show();
          }
        } else {
          map_.addOverlay(markers_[i].marker);
          markers_[i].isAdded = true;
        }
      }

      if (clusterMarker_ !== null) {
        clusterMarker_.hide();
      }
    } else {
      // Else add a cluster marker on map to show the number of markers in
      // this cluster.
      for (i = 0; i < markers_.length; ++i) {
        if (markers_[i].isAdded && (!markers_[i].marker.isHidden())) {
          markers_[i].marker.hide();
        }
      }
      if (clusterMarker_ === null) {
        clusterMarker_ = new ClusterMarker_(center_, this.getTotalMarkers(), markerClusterer_.getStyles_(), markerClusterer_.getGridSize_(), this);
        map_.addOverlay(clusterMarker_);
      } else {
        if (clusterMarker_.isHidden()) {
          clusterMarker_.show();
        }
        clusterMarker_.redraw(true);
      }
    }
  };

  /**
   * Remove all the markers from this cluster.
   */
  this.clearMarkers = function () {
    if (clusterMarker_ !== null) {
      map_.removeOverlay(clusterMarker_);
    }
    for (var i = 0; i < markers_.length; ++i) {
      if (markers_[i].isAdded) {
        map_.removeOverlay(markers_[i].marker);
      }
    }
    markers_ = [];
  };

  /**
   * Get number of markers.
   * @return {Number}
   */
  this.getTotalMarkers = function () {
    return markers_.length;
  };
  
  
}

/**
 * ClusterMarker_ creates a marker that shows the number of markers that
 * a cluster contains.
 *
 * @constructor
 * @private
 * @param {GLatLng} latlng Marker's lat and lng.
 * @param {Number} count Number to show.
 * @param {Array of Object} styles The image list to be showed:
 *   {String} url Image url.
 *   {Number} height Image height.
 *   {Number} width Image width.
 *   {Array of Number} anchor Text anchor of image left and top.
 *   {String} textColor text color.
 * @param {Number} padding Padding of marker center.
 * @param {MarkerClusterer} markerClusterer MarkerClusterer object.
 */
function ClusterMarker_(latlng, count, styles, padding, markerCluster) {
  var index = 0;
  var dv = count;
  while (dv !== 0) {
    dv = parseInt(dv / 10, 10);
    index ++;
  }

  if (styles.length < index) {
    index = styles.length;
  }
  
  if (index == 0) {
  	this.url_ = "";
	this.height_ = 0;
	this.width_ = 0;
	this.textColor_ = "";
	this.anchor_ = "";
  }
  else {
  	this.url_ = styles[index - 1].url;
    this.height_ = styles[index - 1].height;
    this.width_ = styles[index - 1].width;
    this.textColor_ = styles[index - 1].opt_textColor;
    this.anchor_ = styles[index - 1].opt_anchor;
  }
  
  /*
  this.url_ = styles[index - 1].url;
  this.height_ = styles[index - 1].height;
  this.width_ = styles[index - 1].width;
  this.textColor_ = styles[index - 1].opt_textColor;
  this.anchor_ = styles[index - 1].opt_anchor;
  */
  this.latlng_ = latlng;
  this.index_ = index;
  this.styles_ = styles;
  this.text_ = count;
  this.padding_ = padding;
  this.count_ = count;
  this.markerCluster_ = markerCluster;
}

ClusterMarker_.prototype = new GOverlay();

/**
 * Initialize cluster marker.
 * @private
 */
ClusterMarker_.prototype.initialize = function (map) {
  this.map_ = map;
  var div = document.createElement("div");
  var latlng = this.latlng_;
  var pos = map.fromLatLngToDivPixel(latlng);
  pos.x -= parseInt(this.width_ / 2, 10);
  pos.y -= parseInt(this.height_ / 2, 10);
  var mstyle = "";
  if (document.all) {
    mstyle = 'filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale,src="' + this.url_ + '");';
  } else {
    mstyle = "background:url(" + this.url_ + ");";
  }
  if (typeof this.anchor_ === "object") {
    if (typeof this.anchor_[0] === "number" && this.anchor_[0] > 0 && this.anchor_[0] < this.height_) {
      mstyle += 'height:' + (this.height_ - this.anchor_[0]) + 'px;padding-top:' + this.anchor_[0] + 'px;';
    } else {
      mstyle += 'height:' + this.height_ + 'px;line-height:' + this.height_ + 'px;';
    }
    if (typeof this.anchor_[1] === "number" && this.anchor_[1] > 0 && this.anchor_[1] < this.width_) {
      mstyle += 'width:' + (this.width_ - this.anchor_[1]) + 'px;padding-left:' + this.anchor_[1] + 'px;';
    } else {
      mstyle += 'width:' + this.width_ + 'px;text-align:center;';
    }
  } else {
    mstyle += 'height:' + this.height_ + 'px;line-height:' + this.height_ + 'px;';
    mstyle += 'width:' + this.width_ + 'px;text-align:center;';
  }
  var txtColor = this.textColor_ ? this.textColor_ : 'black';

  div.style.cssText = mstyle + 'cursor:pointer;top:' + pos.y + "px;left:" +
      pos.x + "px;color:" + txtColor +  ";position:absolute;font-size:11px;" +
      'font-family:Arial,sans-serif;font-weight:bold';
  div.innerHTML = this.text_;
  map.getPane(G_MAP_MARKER_MOUSE_TARGET_PANE).appendChild(div);
  var padding = this.padding_;
  var count = this.count_;

	var currentCluster = this.markerCluster_;
	
	// Add click-event to marker-elements so they display a little overview
	// and center the map to a point where all markers / clusters are visible
	
	GEvent.addDomListener(div, "click", function() {		
		var iTop, iBottom, iLeft, iRight;
		var markers = currentCluster.getMarkers();
		var listItems = new Array();
		for (var i=0; i<markers.length; ++i) {
		
			var marker = markers[i].marker.getPoint();
			if (!iLeft || (iLeft > marker.x)) {
			
				iLeft = marker.x;
			}
			if (!iRight || (iRight < marker.x)) {
			
				iRight = marker.x;
			}
			if (!iTop || (iTop < marker.y)) {
			
				iTop = marker.y;
			}
			if (!iBottom || (iBottom > marker.y)) {
			
				iBottom = marker.y;
			}
			listItems.push(markers[i].marker.getTitle());
		}
		var sw = new GLatLng(iTop, iLeft);
		sw = map.fromLatLngToContainerPixel(sw);
		sw = map.fromContainerPixelToLatLng(new GPoint(sw.x - 1, sw.y - 1));
		var ne = new GLatLng(iBottom, iRight);
		ne = map.fromLatLngToContainerPixel(ne);
		ne = map.fromContainerPixelToLatLng(new GPoint(ne.x + 1, ne.y + 1));
		var zoom = map.getBoundsZoomLevel(new GLatLngBounds(sw, ne), map.getSize());
		var center = new GLatLng(iBottom + ((iTop - iBottom) / 2), iLeft + ((iRight - iLeft) / 2));
		map.setCenter(center, zoom); 
		map.panBy(new GSize(0, 18));
	});
	// }
  this.div_ = div;
};

/**
 * Remove this overlay.
 * @private
 */
ClusterMarker_.prototype.remove = function () {
	if (this.div_.parentNode) {
  		this.div_.parentNode.removeChild(this.div_);
	}
};

/**
 * Copy this overlay.
 * @private
 */
ClusterMarker_.prototype.copy = function () {
  return new ClusterMarker_(this.latlng_, this.index_, this.text_, this.styles_, this.padding_, this.divClick_);
};

/**
 * Redraw this overlay.
 * @private
 */
ClusterMarker_.prototype.redraw = function (force) {
  if (!force) {
    return;
  }
  var pos = this.map_.fromLatLngToDivPixel(this.latlng_);
  pos.x -= parseInt(this.width_ / 2, 10);
  pos.y -= parseInt(this.height_ / 2, 10);
  this.div_.style.top =  pos.y + "px";
  this.div_.style.left = pos.x + "px";
};

/**
 * Hide this cluster marker.
 */
ClusterMarker_.prototype.hide = function () {
  this.div_.style.display = "none";
};

/**
 * Show this cluster marker.
 */
ClusterMarker_.prototype.show = function () {
  this.div_.style.display = "";
};

/**
 * Get whether the cluster marker is hidden.
 * @return {Boolean}
 */
ClusterMarker_.prototype.isHidden = function () {
  return this.div_.style.display === "none";
};

MarkerClusterer.prototype.getDivClick = function () {
    return divClick;
  };

MarkerClusterer.prototype.setCenter = function () {
	var iTop, iBottom, iLeft, iRight;
	var markers = this.markers;
	var listItems = new Array();
	var map = this.getMap_();
	
	for (var i=0; i<markers.length; ++i) {
	
		var marker = markers[i].getPoint();
		if (!iLeft || (iLeft > marker.x)) {
		
			iLeft = marker.x;
		}
		if (!iRight || (iRight < marker.x)) {
		
			iRight = marker.x;
		}
		if (!iTop || (iTop < marker.y)) {
		
			iTop = marker.y;
		}
		if (!iBottom || (iBottom > marker.y)) {
		
			iBottom = marker.y;
		}
	}
	var sw = this.sw;
	if (!sw) {
		sw = new GLatLng(iTop, iLeft);
		sw = map.fromLatLngToContainerPixel(sw);
		sw = map.fromContainerPixelToLatLng(new GPoint(sw.x - 1, sw.y - 1));
		this.sw = sw;
	}
	var ne = this.ne;
	if (!ne) {
		ne = new GLatLng(iBottom, iRight);
		ne = map.fromLatLngToContainerPixel(ne);
		ne = map.fromContainerPixelToLatLng(new GPoint(ne.x + 1, ne.y + 1));
		this.ne = ne;
	}
	var zoom = map.getBoundsZoomLevel(new GLatLngBounds(sw, ne), map.getSize());
	var center = new GLatLng(iBottom + ((iTop - iBottom) / 2), iLeft + ((iRight - iLeft) / 2));
	map.setCenter(center, zoom);
	map.panBy(new GSize(0, 18));
	//alert ("sw: "+ s);
	//alert("iLeft: "+iLeft + "; iRight: " + iRight + "; iTop: " + iTop + "; iBottom: " + iBottom);
	//alert("#markers: " + markers.length + ";sw: " + sw + "; ne: " + ne + "; zoom level: "+zoom + "; center: "+center);
}

// EWindow.js 
//
//   This Javascript is provided by Mike Williams
//   Community Church Javascript Team
//   http://www.bisphamchurch.org.uk/   
//   http://econym.org.uk/gmap/
//
//   This work is licenced under a Creative Commons Licence
//   http://creativecommons.org/licenses/by/2.0/uk/
//
// Version 0.0  Initial version 
// Version 0.1  10/10/2006 Added E_STYLE_7 
// Version 0.2  17/05/2007 Added .isHidden() and .supportsHide()
// Version 0.3  14/09/2007 added .zindex()
// Version 0.4  09/02/2008 moved the window to the G_MAP_FLOAT_PANE
// Version 0.5  16/10/2008 updated the fudge factors


      function EStyle(stemImage, stemSize, boxClass, boxOffset) {
        this.stemImage = stemImage;
        this.stemSize = stemSize;
        this.boxClass = boxClass;
        this.boxOffset = boxOffset;
        //this.border = border;
        
        // Known fudge factors are:
        // Firefox 1.0, 1.5, 2.0      5, -1
        // Firefox 3.0                4, -1
        // IE 6.0                     0, -1
        // Opera 8.54                 3, -1
        // Opera 9                    4, -1
        // Netscape (7.2, 8.0)        5, -1
        // Safari                     5, -1
        // Chrome                     5, -1        
        
        var agent = navigator.userAgent.toLowerCase();
        
        var fudge = 5;  // assume Netscape if no match found
       
        if (agent.indexOf("opera/9") > -1) {
          fudge = 4;
        }   
        else if (agent.indexOf("opera") > -1) {
          fudge = 3;
        }   
        else if (agent.indexOf("firefox/3") > -1) {
          fudge = 4;
        }   
        else if (agent.indexOf("firefox") > -1) {
          fudge = 5;
        }   
        else if (agent.indexOf("chrome") > -1) {
          fudge = 5;
        }   
        else if (agent.indexOf("safari") > -1) {
          fudge = 5;
        }   
        else if (agent.indexOf("msie") > -1) {
          fudge = 0;
        }
        this.fudge = fudge;
      }
      
      var E_STYLE_1 = new EStyle("", new GSize(81,87),  "estyle1", new GPoint(-30,87-3));
      var E_STYLE_2 = new EStyle("", new GSize(81,87),  "estyle2", new GPoint(-30,87-1));
      var E_STYLE_3 = new EStyle("", new GSize(81,87),  "estyle3", new GPoint(-30,87-10));
      var E_STYLE_4 = new EStyle("", new GSize(81,87),  "estyle4", new GPoint(-30,87-10));
      var E_STYLE_5 = new EStyle("", new GSize(81,87),  "estyle5", new GPoint(-30,87-3));
      var E_STYLE_6 = new EStyle("", new GSize(100,50), "estyle6", new GPoint(100-2,20));
      var E_STYLE_7 = new EStyle("", new GSize(24,24),  "estyle2", new GPoint(-10,23));


      function EWindow(map,estyle) {
        // parameters
        this.map=map;
        this.estyle=estyle;
        // internal variables
        this.visible = false;
        // browser - specific variables
        this.ie = false;
        var agent = navigator.userAgent.toLowerCase();
        if ((agent.indexOf("msie") > -1) && (agent.indexOf("opera") < 1)){ this.ie = true} else {this.ie = false}
      } 
      
      EWindow.prototype = new GOverlay();

      EWindow.prototype.initialize = function(map) {
        var div1 = document.createElement("div");
        div1.style.position = "absolute";
        map.getPane(G_MAP_FLOAT_PANE).appendChild(div1);
        var div2 = document.createElement("div");
        div2.style.position = "absolute";
        div2.style.width = this.estyle.stemSize.width+"px";
        map.getPane(G_MAP_FLOAT_PANE).appendChild(div2);
        this.div1 = div1;
        this.div2 = div2;
      }

      EWindow.prototype.openOnMap = function(point, html, offset) {
        this.offset = offset||new GPoint(0,0);
        this.point = point;
        this.div1.innerHTML = '<div class="' + this.estyle.boxClass + '"><nobr>' + html + '</nobr></div>';
        if (this.ie && this.estyle.stemImage.toLowerCase().indexOf(".png")>-1) {
          var loader = "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+this.estyle.stemImage+"', sizingMethod='scale');";
          this.div2.innerHTML = '<div style="height:' +this.estyle.stemSize.height+ 'px; width:'+this.estyle.stemSize.width+'px; ' +loader+ '" ></div>';
        } else {
          this.div2.innerHTML = '<img src="' + this.estyle.stemImage + '" width="' + this.estyle.stemSize.width +'" height="' + this.estyle.stemSize.height +'">';
        }
        var z = GOverlay.getZIndex(this.point.lat());
        this.div1.style.zIndex = z;
        this.div2.style.zIndex = z+1;
        this.visible = true;
        this.show();
        this.redraw(true);
      }
      
      EWindow.prototype.openOnMarker = function(marker,html) {
        var vx = marker.getIcon().iconAnchor.x - marker.getIcon().infoWindowAnchor.x;
        var vy = marker.getIcon().iconAnchor.y - marker.getIcon().infoWindowAnchor.y;
        this.openOnMap(marker.getPoint(), html, new GPoint(vx,vy));
      }
      

      EWindow.prototype.redraw = function(force) {
        if (!this.visible) {return;}
        var p = this.map.fromLatLngToDivPixel(this.point);
        this.div2.style.left   = (p.x + this.offset.x) + "px";
        this.div2.style.bottom = (-p.y + this.offset.y -this.estyle.fudge) + "px";
        this.div1.style.left   = (p.x + this.offset.x + this.estyle.boxOffset.x) + "px";
        this.div1.style.bottom = (-p.y + this.offset.y + this.estyle.boxOffset.y) + "px";
      }

      EWindow.prototype.remove = function() {
        this.div1.parentNode.removeChild(this.div1);
        this.div2.parentNode.removeChild(this.div2);
        this.visible = false;
      }

      EWindow.prototype.copy = function() {
        return new EWindow(this.map, this.estyle);
      }

      EWindow.prototype.show = function() {
        this.div1.style.display="";
        this.div2.style.display="";
        this.visible = true;
      }
      
      EWindow.prototype.hide = function() {
        this.div1.style.display="none";
        this.div2.style.display="none";
        this.visible = false;
      }
      
      EWindow.prototype.isHidden = function() {
        return !this.visible;
      }
      
      EWindow.prototype.supportsHide = function() {
        return true;
      }

      EWindow.prototype.zindex = function(zin) {
        var z = GOverlay.getZIndex(this.point.lat());
        this.div1.style.zIndex = z+zin;
        this.div2.style.zIndex = z+1+zin;
      }

/**
 * @name MarkerManager
 * @version 1.1
 * @copyright (c) 2007 Google Inc.
 * @author Doug Ricket, others
 *
 * @fileoverview Marker manager is an interface between the map and the user,
 * designed to manage adding and removing many points when the viewport changes.
 * <br /><br />
 * <b>How it Works</b>:<br/> 
 * The MarkerManager places its markers onto a grid, similar to the map tiles.
 * When the user moves the viewport, it computes which grid cells have
 * entered or left the viewport, and shows or hides all the markers in those
 * cells.
 * (If the users scrolls the viewport beyond the markers that are loaded,
 * no markers will be visible until the <code>EVENT_moveend</code> 
 * triggers an update.)
 * In practical consequences, this allows 10,000 markers to be distributed over
 * a large area, and as long as only 100-200 are visible in any given viewport,
 * the user will see good performance corresponding to the 100 visible markers,
 * rather than poor performance corresponding to the total 10,000 markers.
 * Note that some code is optimized for speed over space,
 * with the goal of accommodating thousands of markers.
 */

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */

/**
 * @name MarkerManagerOptions
 * @class This class represents optional arguments to the {@link MarkerManager}
 *     constructor.
 * @property {Number} [maxZoom] Sets the maximum zoom level monitored by a
 *     marker manager. If not given, the manager assumes the maximum map zoom
 *     level. This value is also used when markers are added to the manager
 *     without the optional {@link maxZoom} parameter.
 * @property {Number} [borderPadding] Specifies, in pixels, the extra padding
 *     outside the map's current viewport monitored by a manager. Markers that
 *     fall within this padding are added to the map, even if they are not fully
 *     visible.
 * @property {Boolean} [trackMarkers=false] Indicates whether or not a marker
 *     manager should track markers' movements. If you wish to move managed
 *     markers using the {@link setPoint}/{@link setLatLng} methods, 
 *     this option should be set to {@link true}.
 */

/**
 * Creates a new MarkerManager that will show/hide markers on a map.
 *
 * @constructor
 * @param {Map} map The map to manage.
 * @param {Object} opt_opts A container for optional arguments:
 *   {Number} maxZoom The maximum zoom level for which to create tiles.
 *   {Number} borderPadding The width in pixels beyond the map border,
 *                   where markers should be display.
 *   {Boolean} trackMarkers Whether or not this manager should track marker
 *                   movements.
 */
function MarkerManager(map, opt_opts) {
  var me = this;
  me.map_ = map;
  me.mapZoom_ = map.getZoom();
  me.projection_ = map.getCurrentMapType().getProjection();

  opt_opts = opt_opts || {};
  me.tileSize_ = MarkerManager.DEFAULT_TILE_SIZE_;

  var mapTypes = map.getMapTypes();
  var mapMaxZoom = mapTypes[0].getMaximumResolution();
  for (var i = 0; i < mapTypes.length; i++) {
    var mapTypeMaxZoom = mapTypes[i].getMaximumResolution();
    if (mapTypeMaxZoom > mapMaxZoom) {
      mapMaxZoom = mapTypeMaxZoom;
    }
  }
  me.maxZoom_  = opt_opts.maxZoom || mapMaxZoom;

  me.trackMarkers_ = opt_opts.trackMarkers;
  me.show_ = opt_opts.show || true;

  var padding;
  if (typeof opt_opts.borderPadding === "number") {
    padding = opt_opts.borderPadding;
  } else {
    padding = MarkerManager.DEFAULT_BORDER_PADDING_;
  }
  // The padding in pixels beyond the viewport, where we will pre-load markers.
  me.swPadding_ = new GSize(-padding, padding);
  me.nePadding_ = new GSize(padding, -padding);
  me.borderPadding_ = padding;

  me.gridWidth_ = [];

  me.grid_ = [];
  me.grid_[me.maxZoom_] = [];
  me.numMarkers_ = [];
  me.numMarkers_[me.maxZoom_] = 0;

  GEvent.bind(map, "moveend", me, me.onMapMoveEnd_);

  // NOTE: These two closures provide easy access to the map.
  // They are used as callbacks, not as methods.
  me.removeOverlay_ = function (marker) {
    map.removeOverlay(marker);
    me.shownMarkers_--;
  };
  me.addOverlay_ = function (marker) {
    if (me.show_) {
	  map.addOverlay(marker);
      me.shownMarkers_++;
    }
  };

  me.resetManager_();
  me.shownMarkers_ = 0;

  me.shownBounds_ = me.getMapGridBounds_();
}

// Static constants:
MarkerManager.DEFAULT_TILE_SIZE_ = 1024;
MarkerManager.DEFAULT_BORDER_PADDING_ = 100;
MarkerManager.MERCATOR_ZOOM_LEVEL_ZERO_RANGE = 256;


/**
 * Initializes MarkerManager arrays for all zoom levels
 * Called by constructor and by clearAllMarkers
 */
MarkerManager.prototype.resetManager_ = function () {
  var me = this;
  var mapWidth = MarkerManager.MERCATOR_ZOOM_LEVEL_ZERO_RANGE;
  for (var zoom = 0; zoom <= me.maxZoom_; ++zoom) {
    me.grid_[zoom] = [];
    me.numMarkers_[zoom] = 0;
    me.gridWidth_[zoom] = Math.ceil(mapWidth / me.tileSize_);
    mapWidth <<= 1;
  }
};

/**
 * Removes all markers in the manager, and
 * removes any visible markers from the map.
 */
MarkerManager.prototype.clearMarkers = function () {
  var me = this;
  me.processAll_(me.shownBounds_, me.removeOverlay_);
  me.resetManager_();
};


/**
 * Gets the tile coordinate for a given latlng point.
 *
 * @param {LatLng} latlng The geographical point.
 * @param {Number} zoom The zoom level.
 * @param {GSize} padding The padding used to shift the pixel coordinate.
 *               Used for expanding a bounds to include an extra padding
 *               of pixels surrounding the bounds.
 * @return {GPoint} The point in tile coordinates.
 *
 */
MarkerManager.prototype.getTilePoint_ = function (latlng, zoom, padding) {
  var pixelPoint = this.projection_.fromLatLngToPixel(latlng, zoom);
  return new GPoint(
      Math.floor((pixelPoint.x + padding.width) / this.tileSize_),
      Math.floor((pixelPoint.y + padding.height) / this.tileSize_));
};


/**
 * Finds the appropriate place to add the marker to the grid.
 * Optimized for speed; does not actually add the marker to the map.
 * Designed for batch-processing thousands of markers.
 *
 * @param {Marker} marker The marker to add.
 * @param {Number} minZoom The minimum zoom for displaying the marker.
 * @param {Number} maxZoom The maximum zoom for displaying the marker.
 */
MarkerManager.prototype.addMarkerBatch_ = function (marker, minZoom, maxZoom) {
  var mPoint = marker.getPoint();
  marker.MarkerManager_minZoom = minZoom;
  // Tracking markers is expensive, so we do this only if the
  // user explicitly requested it when creating marker manager.
  if (this.trackMarkers_) {
    GEvent.bind(marker, "changed", this, this.onMarkerMoved_);
  }

  var gridPoint = this.getTilePoint_(mPoint, maxZoom, GSize.ZERO);

  for (var zoom = maxZoom; zoom >= minZoom; zoom--) {
    var cell = this.getGridCellCreate_(gridPoint.x, gridPoint.y, zoom);
    cell.push(marker);

    gridPoint.x = gridPoint.x >> 1;
    gridPoint.y = gridPoint.y >> 1;
  }
};


/**
 * Returns whether or not the given point is visible in the shown bounds. This
 * is a helper method that takes care of the corner case, when shownBounds have
 * negative minX value.
 *
 * @param {Point} point a point on a grid.
 * @return {Boolean} Whether or not the given point is visible in the currently
 * shown bounds.
 */
MarkerManager.prototype.isGridPointVisible_ = function (point) {
  var me = this;
  var vertical = me.shownBounds_.minY <= point.y &&
      point.y <= me.shownBounds_.maxY;
  var minX = me.shownBounds_.minX;
  var horizontal = minX <= point.x && point.x <= me.shownBounds_.maxX;
  if (!horizontal && minX < 0) {
    // Shifts the negative part of the rectangle. As point.x is always less
    // than grid width, only test shifted minX .. 0 part of the shown bounds.
    var width = me.gridWidth_[me.shownBounds_.z];
    horizontal = minX + width <= point.x && point.x <= width - 1;
  }
  return vertical && horizontal;
};


/**
 * Reacts to a notification from a marker that it has moved to a new location.
 * It scans the grid all all zoom levels and moves the marker from the old grid
 * location to a new grid location.
 *
 * @param {Marker} marker The marker that moved.
 * @param {LatLng} oldPoint The old position of the marker.
 * @param {LatLng} newPoint The new position of the marker.
 */
MarkerManager.prototype.onMarkerMoved_ = function (marker, oldPoint, newPoint) {
  // NOTE: We do not know the minimum or maximum zoom the marker was
  // added at, so we start at the absolute maximum. Whenever we successfully
  // remove a marker at a given zoom, we add it at the new grid coordinates.
  var me = this;
  var zoom = me.maxZoom_;
  var changed = false;
  var oldGrid = me.getTilePoint_(oldPoint, zoom, GSize.ZERO);
  var newGrid = me.getTilePoint_(newPoint, zoom, GSize.ZERO);
  while (zoom >= 0 && (oldGrid.x !== newGrid.x || oldGrid.y !== newGrid.y)) {
    var cell = me.getGridCellNoCreate_(oldGrid.x, oldGrid.y, zoom);
    if (cell) {
      if (me.removeFromArray_(cell, marker)) {
        me.getGridCellCreate_(newGrid.x, newGrid.y, zoom).push(marker);
      }
    }
    // For the current zoom we also need to update the map. Markers that no
    // longer are visible are removed from the map. Markers that moved into
    // the shown bounds are added to the map. This also lets us keep the count
    // of visible markers up to date.
    if (zoom === me.mapZoom_) {
      if (me.isGridPointVisible_(oldGrid)) {
        if (!me.isGridPointVisible_(newGrid)) {
          me.removeOverlay_(marker);
          changed = true;
        }
      } else {
        if (me.isGridPointVisible_(newGrid)) {
          me.addOverlay_(marker);
          changed = true;
        }
      }
    }
    oldGrid.x = oldGrid.x >> 1;
    oldGrid.y = oldGrid.y >> 1;
    newGrid.x = newGrid.x >> 1;
    newGrid.y = newGrid.y >> 1;
    --zoom;
  }
  if (changed) {
    me.notifyListeners_();
  }
};


/**
 * Removes marker from the manager and from the map
 * (if it's currently visible).
 * @param {GMarker} marker The marker to delete.
 */
MarkerManager.prototype.removeMarker = function (marker) {
  var me = this;
  var zoom = me.maxZoom_;
  var changed = false;
  var point = marker.getPoint();
  var grid = me.getTilePoint_(point, zoom, GSize.ZERO);
  while (zoom >= 0) {
    var cell = me.getGridCellNoCreate_(grid.x, grid.y, zoom);

    if (cell) {
      me.removeFromArray_(cell, marker);
    }
    // For the current zoom we also need to update the map. Markers that no
    // longer are visible are removed from the map. This also lets us keep the count
    // of visible markers up to date.
    if (zoom === me.mapZoom_) {
      if (me.isGridPointVisible_(grid)) {
        me.removeOverlay_(marker);
        changed = true;
      }
    }
    grid.x = grid.x >> 1;
    grid.y = grid.y >> 1;
    --zoom;
  }
  if (changed) {
    me.notifyListeners_();
  }
  me.numMarkers_[marker.MarkerManager_minZoom]--;
};


/**
 * Add many markers at once.
 * Does not actually update the map, just the internal grid.
 *
 * @param {Array of Marker} markers The markers to add.
 * @param {Number} minZoom The minimum zoom level to display the markers.
 * @param {Number} opt_maxZoom The maximum zoom level to display the markers.
 */
MarkerManager.prototype.addMarkers = function (markers, minZoom, opt_maxZoom) {
  var maxZoom = this.getOptMaxZoom_(opt_maxZoom);
  for (var i = markers.length - 1; i >= 0; i--) {
    this.addMarkerBatch_(markers[i], minZoom, maxZoom);
  }

  this.numMarkers_[minZoom] += markers.length;
};


/**
 * Returns the value of the optional maximum zoom. This method is defined so
 * that we have just one place where optional maximum zoom is calculated.
 *
 * @param {Number} opt_maxZoom The optinal maximum zoom.
 * @return The maximum zoom.
 */
MarkerManager.prototype.getOptMaxZoom_ = function (opt_maxZoom) {
  return opt_maxZoom || this.maxZoom_;
};


/**
 * Calculates the total number of markers potentially visible at a given
 * zoom level.
 *
 * @param {Number} zoom The zoom level to check.
 */
MarkerManager.prototype.getMarkerCount = function (zoom) {
  var total = 0;
  for (var z = 0; z <= zoom; z++) {
    total += this.numMarkers_[z];
  }
  return total;
};

/** 
 * Returns a marker given latitude, longitude and zoom. If the marker does not 
 * exist, the method will return a new marker. If a new marker is created, 
 * it will NOT be added to the manager. 
 * 
 * @param {Number} lat - the latitude of a marker. 
 * @param {Number} lng - the longitude of a marker. 
 * @param {Number} zoom - the zoom level 
 * @return {GMarker} marker - the marker found at lat and lng 
 */ 
MarkerManager.prototype.getMarker = function(lat, lng, zoom) { 
  var me = this; 
  var mPoint = new GLatLng(lat, lng); 
  var gridPoint = me.getTilePoint_(mPoint, zoom, GSize.ZERO); 

  var marker = new GMarker(mPoint); 
  var cellArray = me.getGridCellNoCreate_(gridPoint.x, gridPoint.y, zoom); 
  if(cellArray != undefined){ 
    for (var i = 0; i < cellArray.length; i++) 
    { 
      if(lat == cellArray[i].getLatLng().lat() && 
         lng == cellArray[i].getLatLng().lng()) 
      { 
        marker = cellArray[i]; 
      } 
    } 
  } 
  return marker; 
}; 

/**
 * Add a single marker to the map.
 *
 * @param {Marker} marker The marker to add.
 * @param {Number} minZoom The minimum zoom level to display the marker.
 * @param {Number} opt_maxZoom The maximum zoom level to display the marker.
 */
MarkerManager.prototype.addMarker = function (marker, minZoom, opt_maxZoom) {
  var me = this;
  var maxZoom = this.getOptMaxZoom_(opt_maxZoom);
  me.addMarkerBatch_(marker, minZoom, maxZoom);
  var gridPoint = me.getTilePoint_(marker.getPoint(), me.mapZoom_, GSize.ZERO);
  if (me.isGridPointVisible_(gridPoint) &&
      minZoom <= me.shownBounds_.z &&
      me.shownBounds_.z <= maxZoom) {
    me.addOverlay_(marker);
    me.notifyListeners_();
  }
  this.numMarkers_[minZoom]++;
};

/**
 * Returns true if this bounds (inclusively) contains the given point.
 * @param {Point} point  The point to test.
 * @return {Boolean} This Bounds contains the given Point.
 */
GBounds.prototype.containsPoint = function (point) {
  var outer = this;
  return (outer.minX <= point.x &&
          outer.maxX >= point.x &&
          outer.minY <= point.y &&
          outer.maxY >= point.y);
};

/**
 * Get a cell in the grid, creating it first if necessary.
 *
 * Optimization candidate
 *
 * @param {Number} x The x coordinate of the cell.
 * @param {Number} y The y coordinate of the cell.
 * @param {Number} z The z coordinate of the cell.
 * @return {Array} The cell in the array.
 */
MarkerManager.prototype.getGridCellCreate_ = function (x, y, z) {
  var grid = this.grid_[z];
  if (x < 0) {
    x += this.gridWidth_[z];
  }
  var gridCol = grid[x];
  if (!gridCol) {
    gridCol = grid[x] = [];
    return (gridCol[y] = []);
  }
  var gridCell = gridCol[y];
  if (!gridCell) {
    return (gridCol[y] = []);
  }
  return gridCell;
};


/**
 * Get a cell in the grid, returning undefined if it does not exist.
 *
 * NOTE: Optimized for speed -- otherwise could combine with getGridCellCreate_.
 *
 * @param {Number} x The x coordinate of the cell.
 * @param {Number} y The y coordinate of the cell.
 * @param {Number} z The z coordinate of the cell.
 * @return {Array} The cell in the array.
 */
MarkerManager.prototype.getGridCellNoCreate_ = function (x, y, z) {
  var grid = this.grid_[z];
  if (x < 0) {
    x += this.gridWidth_[z];
  }
  var gridCol = grid[x];
  return gridCol ? gridCol[y] : undefined;
};


/**
 * Turns at geographical bounds into a grid-space bounds.
 *
 * @param {LatLngBounds} bounds The geographical bounds.
 * @param {Number} zoom The zoom level of the bounds.
 * @param {GSize} swPadding The padding in pixels to extend beyond the
 * given bounds.
 * @param {GSize} nePadding The padding in pixels to extend beyond the
 * given bounds.
 * @return {GBounds} The bounds in grid space.
 */
MarkerManager.prototype.getGridBounds_ = function (bounds, zoom, swPadding, nePadding) {
  zoom = Math.min(zoom, this.maxZoom_);

  var bl = bounds.getSouthWest();
  var tr = bounds.getNorthEast();
  var sw = this.getTilePoint_(bl, zoom, swPadding);
  var ne = this.getTilePoint_(tr, zoom, nePadding);
  var gw = this.gridWidth_[zoom];

  // Crossing the prime meridian requires correction of bounds.
  if (tr.lng() < bl.lng() || ne.x < sw.x) {
    sw.x -= gw;
  }
  if (ne.x - sw.x  + 1 >= gw) {
    // Computed grid bounds are larger than the world; truncate.
    sw.x = 0;
    ne.x = gw - 1;
  }
  var gridBounds = new GBounds([sw, ne]);
  gridBounds.z = zoom;
  return gridBounds;
};


/**
 * Gets the grid-space bounds for the current map viewport.
 *
 * @return {Bounds} The bounds in grid space.
 */
MarkerManager.prototype.getMapGridBounds_ = function () {
  var me = this;
  return me.getGridBounds_(me.map_.getBounds(), me.mapZoom_, me.swPadding_, me.nePadding_);
};


/**
 * Event listener for map:movend.
 * NOTE: Use a timeout so that the user is not blocked
 * from moving the map.
 *
 */
MarkerManager.prototype.onMapMoveEnd_ = function () {
  var me = this;
  me.objectSetTimeout_(this, this.updateMarkers_, 0);
};


/**
 * Call a function or evaluate an expression after a specified number of
 * milliseconds.
 *
 * Equivalent to the standard window.setTimeout function, but the given
 * function executes as a method of this instance. So the function passed to
 * objectSetTimeout can contain references to this.
 *    objectSetTimeout(this, function () { alert(this.x) }, 1000);
 *
 * @param {Object} object  The target object.
 * @param {Function} command  The command to run.
 * @param {Number} milliseconds  The delay.
 * @return {Boolean}  Success.
 */
MarkerManager.prototype.objectSetTimeout_ = function (object, command, milliseconds) {
  return window.setTimeout(function () {
    command.call(object);
  }, milliseconds);
};


/**
 * Is this layer visible?
 *
 * Returns visibility setting
 *
 * @return {Boolean} Visible
 */
MarkerManager.prototype.visible = function () {
  return this.show_ ? true : false;
};


/**
 * Returns true if the manager is hidden.
 * Otherwise returns false.
 * @return {Boolean} Hidden
 */
MarkerManager.prototype.isHidden = function () {
  return !this.show_;
};


/**
 * Shows the manager if it's currently hidden.
 */
MarkerManager.prototype.show = function () {
  this.show_ = true;
  this.refresh();
};


/**
 * Hides the manager if it's currently visible
 */
MarkerManager.prototype.hide = function () {
  this.show_ = false;
  this.refresh();
};


/**
 * Toggles the visibility of the manager.
 */
MarkerManager.prototype.toggle = function () {
  this.show_ = !this.show_;
  this.refresh();
};


/**
 * Refresh forces the marker-manager into a good state.
 * <ol>
 *   <li>If never before initialized, shows all the markers.</li>
 *   <li>If previously initialized, removes and re-adds all markers.</li>
 * </ol>
 */
MarkerManager.prototype.refresh = function () {
  var me = this;
  if (me.shownMarkers_ > 0) {
    me.processAll_(me.shownBounds_, me.removeOverlay_);
  }
  // An extra check on me.show_ to increase performance (no need to processAll_)
  if (me.show_) {
    me.processAll_(me.shownBounds_, me.addOverlay_);
  }
  me.notifyListeners_();
};


/**
 * After the viewport may have changed, add or remove markers as needed.
 */
MarkerManager.prototype.updateMarkers_ = function () {
  var me = this;
  me.mapZoom_ = this.map_.getZoom();
  var newBounds = me.getMapGridBounds_();

  // If the move does not include new grid sections,
  // we have no work to do:
  if (newBounds.equals(me.shownBounds_) && newBounds.z === me.shownBounds_.z) {
    return;
  }

  if (newBounds.z !== me.shownBounds_.z) {
    me.processAll_(me.shownBounds_, me.removeOverlay_);
    if (me.show_) { // performance
      me.processAll_(newBounds, me.addOverlay_);
    }
  } else {
    // Remove markers:
    me.rectangleDiff_(me.shownBounds_, newBounds, me.removeCellMarkers_);

    // Add markers:
    if (me.show_) { // performance
      me.rectangleDiff_(newBounds, me.shownBounds_, me.addCellMarkers_);
    }
  }
  me.shownBounds_ = newBounds;

  me.notifyListeners_();
};


/**
 * Notify listeners when the state of what is displayed changes.
 */
MarkerManager.prototype.notifyListeners_ = function () {
  GEvent.trigger(this, "changed", this.shownBounds_, this.shownMarkers_);
};


/**
 * Process all markers in the bounds provided, using a callback.
 *
 * @param {Bounds} bounds The bounds in grid space.
 * @param {Function} callback The function to call for each marker.
 */
MarkerManager.prototype.processAll_ = function (bounds, callback) {
  for (var x = bounds.minX; x <= bounds.maxX; x++) {
    for (var y = bounds.minY; y <= bounds.maxY; y++) {
      this.processCellMarkers_(x, y,  bounds.z, callback);
    }
  }
};


/**
 * Process all markers in the grid cell, using a callback.
 *
 * @param {Number} x The x coordinate of the cell.
 * @param {Number} y The y coordinate of the cell.
 * @param {Number} z The z coordinate of the cell.
 * @param {Function} callback The function to call for each marker.
 */
MarkerManager.prototype.processCellMarkers_ = function (x, y, z, callback) {
  var cell = this.getGridCellNoCreate_(x, y, z);
  if (cell) {
    for (var i = cell.length - 1; i >= 0; i--) {
      callback(cell[i]);
    }
  }
};


/**
 * Remove all markers in a grid cell.
 *
 * @param {Number} x The x coordinate of the cell.
 * @param {Number} y The y coordinate of the cell.
 * @param {Number} z The z coordinate of the cell.
 */
MarkerManager.prototype.removeCellMarkers_ = function (x, y, z) {
  this.processCellMarkers_(x, y, z, this.removeOverlay_);
};


/**
 * Add all markers in a grid cell.
 *
 * @param {Number} x The x coordinate of the cell.
 * @param {Number} y The y coordinate of the cell.
 * @param {Number} z The z coordinate of the cell.
 */
MarkerManager.prototype.addCellMarkers_ = function (x, y, z) {
  this.processCellMarkers_(x, y, z, this.addOverlay_);
};


/**
 * Use the rectangleDiffCoords_ function to process all grid cells
 * that are in bounds1 but not bounds2, using a callback, and using
 * the current MarkerManager object as the instance.
 *
 * Pass the z parameter to the callback in addition to x and y.
 *
 * @param {Bounds} bounds1 The bounds of all points we may process.
 * @param {Bounds} bounds2 The bounds of points to exclude.
 * @param {Function} callback The callback function to call
 *                   for each grid coordinate (x, y, z).
 */
MarkerManager.prototype.rectangleDiff_ = function (bounds1, bounds2, callback) {
  var me = this;
  me.rectangleDiffCoords_(bounds1, bounds2, function (x, y) {
    callback.apply(me, [x, y, bounds1.z]);
  });
};


/**
 * Calls the function for all points in bounds1, not in bounds2
 *
 * @param {Bounds} bounds1 The bounds of all points we may process.
 * @param {Bounds} bounds2 The bounds of points to exclude.
 * @param {Function} callback The callback function to call
 *                   for each grid coordinate.
 */
MarkerManager.prototype.rectangleDiffCoords_ = function (bounds1, bounds2, callback) {
  var minX1 = bounds1.minX;
  var minY1 = bounds1.minY;
  var maxX1 = bounds1.maxX;
  var maxY1 = bounds1.maxY;
  var minX2 = bounds2.minX;
  var minY2 = bounds2.minY;
  var maxX2 = bounds2.maxX;
  var maxY2 = bounds2.maxY;

  var x, y;
  for (x = minX1; x <= maxX1; x++) {  // All x in R1
    // All above:
    for (y = minY1; y <= maxY1 && y < minY2; y++) {  // y in R1 above R2
      callback(x, y);
    }
    // All below:
    for (y = Math.max(maxY2 + 1, minY1);  // y in R1 below R2
         y <= maxY1; y++) {
      callback(x, y);
    }
  }

  for (y = Math.max(minY1, minY2);
       y <= Math.min(maxY1, maxY2); y++) {  // All y in R2 and in R1
    // Strictly left:
    for (x = Math.min(maxX1 + 1, minX2) - 1;
         x >= minX1; x--) {  // x in R1 left of R2
      callback(x, y);
    }
    // Strictly right:
    for (x = Math.max(minX1, maxX2 + 1);  // x in R1 right of R2
         x <= maxX1; x++) {
      callback(x, y);
    }
  }
};


/**
 * Removes value from array. O(N).
 *
 * @param {Array} array  The array to modify.
 * @param {any} value  The value to remove.
 * @param {Boolean} opt_notype  Flag to disable type checking in equality.
 * @return {Number}  The number of instances of value that were removed.
 */
MarkerManager.prototype.removeFromArray_ = function (array, value, opt_notype) {
  var shift = 0;
  for (var i = 0; i < array.length; ++i) {
    if (array[i] === value || (opt_notype && array[i] === value)) {
      array.splice(i--, 1);
      shift++;
    }
  }
  return shift;
};

// Google Maps "more" control
function POISelectorControl() {
}

POISelectorControl.prototype = new GControl();


POISelectorControl.prototype.initialize = function(map) {
	// show only when there is a checkbox
	bHasVisibleCheckbox = false;
	
	// create dom structure
	var container 	= document.createElement("div");
		container.id  = "poiSelectorHolder";
  	
	if (!POISelector.controlVisible) {
		container.style.display = 'none';
	}	
		
	var innerBorder = document.createElement("div");
	container.appendChild(innerBorder);


	var hoverLink = document.createElement("a");
		hoverLink.href = "javascript:void(0)";
		hoverLink.id = "hoverlink";
		//hoverLink.appendChild(document.createTextNode("pi&#249;..."));
		hoverLink.innerHTML = "pi&#249;...";
  
	innerBorder.appendChild(hoverLink);
  
  
	var list = document.createElement("ul");
  
	var listElement = null;
	var checkbox = null;
	var label = null;
	var i = 0;
  
	for (i = 0; i < POISelector.poiTypes.length; i++) {
	
  		// generate list element for each poi type
		listElement = document.createElement("li");
	
		checkbox = document.createElement("input");
		checkbox.type = "checkbox";
		checkbox.className = "poiselectorcheckbox";
	
		POISelectorControl.addCheckBoxListener(checkbox, i);
	
		POISelector.selectorCheckboxes.push({"checkbox": checkbox, "poiKey": i});
	
	
		label = document.createElement("label");
		label.innerHTML = POISelector.poiTypes[i].name;
	
		listElement.appendChild(checkbox);
	
		if (POISelector.poiTypes[i].name == "selected") {
		  	listElement.style.display = 'none';
			POISelector.offerPoiType = i;
		}
		else {
			bHasVisibleCheckbox = true;
		}
		
		if (POISelector.poiTypes[i].visible) {
			checkbox.checked = true;	
			checkbox.defaultChecked = true;
		}
		
		listElement.appendChild(label);
		
		list.appendChild(listElement);
	
	}
	
	// add kml categories to list
	for (var key in kmlCategoriesConnections) {
		if (oKmlCategories[key] && oKmlCategories[key].length > 0) {
			listElement = document.createElement("li");
	
			checkbox = document.createElement("input");
			checkbox.type = "checkbox";
			checkbox.checked = true;	
			checkbox.defaultChecked = true;
			
			label = document.createElement("label");
			label.innerHTML = oKmlCategories[key];
		
			listElement.appendChild(checkbox);
			listElement.appendChild(label);
		
			list.appendChild(listElement);
			
			POISelectorControl.addCheckBoxListenerForKmls(checkbox, key);
			
			bHasVisibleCheckbox = true;
			
		}
	}
	
	if (!bHasVisibleCheckbox) {
		container.style.display = 'none';
	}
	
	
	hoverLink.appendChild(list);
	
	map.getContainer().appendChild(container);
  
	// needed for older browsers to make box visible on mouseover / hide on mouseout
	GEvent.addDomListener(container, "mouseover", function() {
		document.getElementById("hoverlink").className = "hover";
	});
  
	GEvent.addDomListener(container, "mouseout", function() {
		document.getElementById("hoverlink").className = "";
	});
  
  	// paint the currently selected poi types
	POISelector.rePaint();
  
	GEvent.addListener(map, "zoomend", function() {
  		// certain browers need a timeout to cleanly repaint the pois
		setTimeout(function() {
			POISelector.rePaint();
		},10);
	});
  
  
	return container;
};

POISelectorControl.addCheckBoxListener = function(checkbox, poiIndex) {
	// repaint all pois on selection change (needed as clean removal fron MarkerClusterer is not possible)
	GEvent.addDomListener(checkbox, "click", function(event) {
		POISelector.rePaint();
	});
}

POISelectorControl.addCheckBoxListenerForKmls = function(checkbox, category) {
	// show or hide kml category
	GEvent.addDomListener(checkbox, "click", function(event) {
		for (var i = 0; kmlCategoriesConnections[category] && i < kmlCategoriesConnections[category].length; i++) {
			if (checkbox.checked) {
				kmlCategoriesConnections[category][i].show();
			}
			else {
				kmlCategoriesConnections[category][i].hide();
			}
		}
	});
}

// used to position the new control
POISelectorControl.prototype.getDefaultPosition = function() {
	return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(215, 7));
};


// the POISelector handles and stores all pois
var POISelector = {
	selectorCheckboxes: [],
	poiTypes: [],
	offerPoiType: -1,
	centered: false,
	controlVisible: (document.location.toString().indexOf("pag-angebot-detail.htm")!=-1),
	clear: function() {
		// remove all POIs and all checkboxes from the POISelector
		POISelector.poiTypes = [];
		POISelector.selectorCheckboxes = [];
	},
	rePaint: function() {
		// repaint pois that should be currently visible
		
		// if markers are already painted; remove
		if (markerCluster != null && markerCluster.getTotalMarkers() > 0) {
			markerCluster.clearMarkers();
		}
		
		var visibleMarkers = [];
		
		// walk through checkboxes and display markers of checked checkboxes
		for (var i = 0; i < POISelector.selectorCheckboxes.length; i++) {
			if ((POISelector.selectorCheckboxes[i].checkbox.checked || i == POISelector.offerPoiType) && map.getZoom() >= POISelector.poiTypes[POISelector.selectorCheckboxes[i].poiKey].minZoom) {
				for (var j = 0; j < POISelector.poiTypes[POISelector.selectorCheckboxes[i].poiKey].markers.length; j++) {
					visibleMarkers.push(POISelector.poiTypes[POISelector.selectorCheckboxes[i].poiKey].markers[j]);
				}
			
			}
		}
		
		// detail pages should focus on offer pois and not global pois
		if (!POISelector.centered && document.location.toString().indexOf("pag-angebot-detail.htm")!=-1) //  && POISelector.offerPoiType>-1
		{ 
			// hide all except offer pois
			
			if (markerCluster && markerCluster.getTotalMarkers() > 0)
			{
				markerCluster.clearMarkers();
			}
			
			var offerMarkers = [];
			
			for (var i = 0; i < POISelector.selectorCheckboxes.length; i++) {
				if (POISelector.selectorCheckboxes[i].checkbox.checked) {
					for (var j = 0; j < POISelector.poiTypes[POISelector.selectorCheckboxes[i].poiKey].markers.length; j++) {
						if (!POISelector.poiTypes[POISelector.selectorCheckboxes[i].poiKey].markers[j].isGlobal) {
							offerMarkers.push(POISelector.poiTypes[POISelector.selectorCheckboxes[i].poiKey].markers[j]);
						}
					}
				
				}
			}
			
			
			if (offerMarkers.length > 0) {
				markerCluster = new MarkerClusterer(map, offerMarkers, {"styles": clusterStyle, "maxZoom": 20});
				markerCluster.setCenter();
				
			}
			
			// repaint all markers on the now centerd map
			if (markerCluster && markerCluster.getTotalMarkers() > 0) {
				markerCluster.clearMarkers();
			}
			markerCluster = new MarkerClusterer(map, visibleMarkers, {"styles": clusterStyle, "maxZoom": 20});
			
			POISelector.centered = true;
		}
		else
		{
			markerCluster = new MarkerClusterer(map, visibleMarkers, {"styles": clusterStyle, "maxZoom": 20});
		}
	},
	addMarker: function(marker, poiName, visible, global, minzoom) {
		if (!minzoom || typeof minzoom == "undefined") {
			minzoom = 0;
		}
	
		// when marker is added, new centering is necessary
		POISelector.centered = false;
		
		// store marker in POISelector
		var index = POISelector.findType(poiName);
		
		if (index == -1) { // generate new type
			POISelector.poiTypes.push({"name": poiName, "visible": visible, "minZoom": minzoom, "poiIndex": POISelector.poiTypes.length, "markers": [marker]});
		}
		else { // add to existing type
			POISelector.poiTypes[index].markers.push(marker);
		}
		
		marker.isGlobal = global;
		
	},
	findType: function(poiName) {
		// find type by name
		for (var i = 0; i < POISelector.poiTypes.length; i++) {
			
			if (POISelector.poiTypes[i].name == poiName) {
				return i;
			}
		}
		return -1;
	},
	initPois: function(poiTypes) {
		
		
		// init the whole thing
		map.addControl(new POISelectorControl());
		
	}
};

 
	
	// <![CDATA[
	// Define global variables
	
	var oEGeoXml, map, name, desc, oRootElement, ew, eMoveMap, markerCluster, eTermsHide, markerManager;
	var bOverview = false;
	var aoPoiMarkers = new Array();
	
	
	// Define style for cluster-icons
	
	var clusterStyle = [{
	
		url: 'poi_cluster.gif',
		height: 40,
		width: 26,
		opt_anchor: [0, 15],
		opt_textColor: '#000000'
	},
	{
	
		url: 'poi_cluster_large.gif',
		height: 40,
		width: 32,
		opt_anchor: [0, 15],
		opt_textColor: '#000000'
	}];
	
	var iPoiHeight = 16;
	var iPoiWidth = 16;
	var oPoiIconSize = new GSize(iPoiWidth, iPoiHeight);
	var iPoiMaxHeight = iPoiHeight;
	
	var iStopHeight = 24;
	var iStopWidth = 23;
	var oStopIconSize = new GSize(iStopWidth, iStopHeight);
	var iStopMaxHeight = iStopHeight;
	
	// define categories
	var oCategories = {};
	oCategories[101] = {id:101, iconId:9806, alttext:"Cultura/Curiosit&agrave;", show:1, minzoom:0};
oCategories[123] = {id:123, iconId:9869, alttext:"Hotel/Pernottamento", show:1, minzoom:0};
oCategories[124] = {id:124, iconId:9870, alttext:"Ristoranti/Vitto", show:1, minzoom:0};
oCategories[125] = {id:125, iconId:9871, alttext:"Treni ecc.", show:1, minzoom:0};
oCategories[126] = {id:126, iconId:9872, alttext:"Acqua/Wellness", show:1, minzoom:0};
oCategories[127] = {id:127, iconId:10167, alttext:"Escursionismo", show:1, minzoom:0};
oCategories[128] = {id:128, iconId:9874, alttext:"Sport/Divertimento", show:1, minzoom:0};
oCategories[129] = {id:129, iconId:9875, alttext:"Natura", show:1, minzoom:0};
oCategories[130] = {id:130, iconId:10166, alttext:"Passi/Montagne", show:1, minzoom:0};

	var oKmlCategories = {};
	oKmlCategories[121] = "PostAuto-Linien";
oKmlCategories[101] = "Wanderungen";
oKmlCategories[161] = "";

/**
 * Builds the map 
 *
 * @param {Integer} Offer- or POI-ID
 * @param {String} Type (possible values are "POI" or "offer"
 * @param {String} Comma-separated String of POI-IDs (Used for overview-KML)
 * @param {String} Comma-separated String of KML-IDs (used for offer-attached kml routes)
 */

	function GMapsLoad(iId, sType, sPOIs, oAdditionalKMLs) 
	{	
		if (GBrowserIsCompatible() && $("googleMap")) {
			
			// clear pois in poi-selector
			POISelector.clear();
			
			var iWidth, iHeight;
			iHeight = 330;
			if (!iId) {
			
				bOverview = true;
				iWidth = 750;
			}
			else {
			
				bOverview = false;
				iWidth = 545;
			}	
			
			if ($('map_load')) {
				$('map_load').setStyle({
					display: "block"
					});
			}
		
			var sIdString = "";
			
			// Set map size for overview or detail
			
			var oSize = new GSize(iWidth,iHeight);
			
			// Set ID-parameter for KML
			var sKmlBaseUrl = "ra.kml";
			if (sKmlBaseUrl.indexOf("?") > -1) {
			
				sKmlBaseUrl += "&";
			}
			else {
			
				sKmlBaseUrl += "?";
			}
			
			
			switch (sType) {
			
				case "POI": 
					sIdString = "poiId=" + iId;
					break;
					
				case "offer":
					sIdString = "offerId=" + iId;
					break;
					
				default:
					sIdString = "showoverview=" + sPOIs;
					break;
			}
			aoPoiMarkers = new Array();
			
			
			map = new GMap2($("googleMap"), { size: oSize });
						
			// actually, we don't need this method - BUT: google says that this stupid thing must be
			// called before *any* other operation with google maps, so here we go (dummy-center):
			map.setCenter(new GLatLng(46.80121, 8.226692), 7); 
			map.clearOverlays();
			map.addControl(new GLargeMapControl());
			map.addControl(new GMapTypeControl());
			
			// Open overlay from KML-file - We need to overwrite the addMarker and createMarker-functions
			// since we are using the eWindow and MarkerClusterer-libraries
			var oGeoOpts = new Object();
			oGeoOpts.addmarker = function() {};
			oGeoOpts.createmarker = createKmlMarker;
			oEGeoXml = new EGeoXml("oEGeoXml", map, sKmlBaseUrl + sIdString, oGeoOpts);
			
			
		    // Create an EWindow since we need a non-standard infoWindow. We also have to define to close the
			// window if the user clicks on the map.
			
			ew = new EWindow(map, E_STYLE_7);      
			map.addOverlay(ew);
		     
			GEvent.addListener(map, "click", function(overlay,point) {
				if (overlay) {
					closeInfoWindow();
				}
			});
			
			// Now we are ready to parse the KML.
			// As soon as the KML is parsed, we call the wonderful MarkerClusterer library
			
 			GEvent.addListener(oEGeoXml, "parsed", function() {			
			 
			 	markerManager = new MarkerManager(map);
				
			 
				// set center
				map.setCenter(new GLatLng(46.89, 8.27), 7);
				
				
				if ($('map_load')) {
					$('map_load').style.display = "none";
				}
				
				// add type-control
				map.addControl(new POISelectorControl());
				
				// hack for google-function 'return to last result'
				// button element has to be loaded manually...
				if ($$('div[log=center_result]').length > 0) {
					$$('div[log=center_result]')[0].observe('click', function(element, event) {
						markerCluster.setCenter();
					});
				}
			});
			
					
			// add kmls
			window.kmlCategoriesConnections = {};
			
			for (var i = 0; oAdditionalKMLs && i < oAdditionalKMLs.length; i++) {
				window["additionalKML_" + i] = new EGeoXml("additionalKML_" + i, map, "getkmlfile.kml" + "?id=" + oAdditionalKMLs[i].id, null);	
				window["additionalKML_" + i].parse();
				if (oAdditionalKMLs[i].cat) {
					if (window.kmlCategoriesConnections[oAdditionalKMLs[i].cat]) {
						window.kmlCategoriesConnections[oAdditionalKMLs[i].cat].push(window["additionalKML_" + i]);
					}
					else {
						window.kmlCategoriesConnections[oAdditionalKMLs[i].cat] = [];
						window.kmlCategoriesConnections[oAdditionalKMLs[i].cat].push(window["additionalKML_" + i]);
					}
				}
			}
			
			
			// parse pois 
			oEGeoXml.parse();
			
		}
		
		
	}
	
/**
 * Creates a marker based on the data from eGeoXML and adds it to the Array aoPoiMarkers or aoStopMarkers
 *
 * @param {GLatLng} Position of the marker
 * @param {String} Name of the marker
 * @param {String} Description of the marker
 * @param {Object} Marker style
 * @param {Object} Options
 * @param {Object} Marker options
 */ 
	
	function createKmlMarker(point, name, desc, style, opts, markeroptions) {
		var bGlobal = false;
		
		if (desc && desc.indexOf("globalpoi") != -1) {
			bGlobal = true;
		}
		
		if (style && style.indexOf("#stylePOI") == 0) {
		
		    var icon;
			if (style == "#stylePOI") {
				icon = new GIcon(G_DEFAULT_ICON, "poi.gif");
			}
			else {
				var styleId = parseInt(style.substr(style.indexOf("_cat_") + 5), 10);
				var category = oCategories[styleId];
				if (category === undefined || !oCategories[styleId].iconId || !oCategories[styleId].iconId == "null") {
					icon = new GIcon(G_DEFAULT_ICON, "poi.gif");
				}
				else {
					icon = new GIcon(G_DEFAULT_ICON, "imagecontroller?img=" + oCategories[styleId].iconId);
				}
				
			}
			icon.iconSize = oPoiIconSize;
			icon.iconAnchor = new GPoint(8, 8);
			icon.shadowSize = oPoiIconSize;
			icon.maxHeight = iPoiMaxHeight;
			icon.imageMap = null;
			icon.dragCrossSize = oPoiIconSize;
		}
		if (!markeroptions) {
		
			markeroptions = new Object();
		}
		markeroptions.icon = icon;
		markeroptions.title = name;
		if (style && style.indexOf("#stylePOI") == 0) {
			markeroptions.zIndexProcess = importanceOrder;
		}
		name = name;
		desc = desc;
		var marker = new GMarker(point, markeroptions);
		if (style && style.indexOf("#stylePOI") == 0) {
		
			GEvent.addListener(marker, "click", function() {
			
				doMarkerClick(point, desc);
			}); 
		}
		if (style && style.indexOf("#stylePOI") == 0) { // starts with #stylePOI
			var iCatId;	
			if (style.indexOf("_cat_") > 0) {
				
				iCatId = parseInt(style.substr(style.indexOf("_cat_") + 5), 10);
				
								
				var oCat = oCategories[iCatId];
				
				if (oCat !== undefined) {
					POISelector.addMarker(marker, oCat.alttext, oCat.show, bGlobal, oCat.minzoom);
				}
				else {
					POISelector.addMarker(marker, "selected", true, bGlobal);
				}
			}
			else {
				POISelector.addMarker(marker, "selected", true, bGlobal);
			
			}
		
			aoPoiMarkers.push(marker);
		}
	}
	
	function importanceOrder (marker, b) {
		return GOverlay.getZIndex(marker.getPoint().lat()) + 2*1000000;
	}

/**
 * Wraps the HTML-markup for the infoWindow around the content.
 *
 * @param {String} content
 *
 * @return Content including the InfoWindow frame
 * @type String
 */
	
	function getBubble(sContent) {
	
		var sReturn = '<div id="infowin_placeholder">'
					+ '<div id="infowin_top"><\/div>'
					+ '<div id="infowin_middle">'
					+ '		<div class="close_button">'
					+ '			<a onclick="closeInfoWindow(); return false;"><img src="ra_close_btn.gif" \/><\/a>'
					+ '		<\/div>'	
					+ sContent
					+ '<\/div>'
					+ '<div id="infowin_bottom"><\/div>';
		return sReturn;			
	}
	
/**
 * Closes the InfoWindow 
 */
	
	function closeInfoWindow() {
	
		ew.hide();
		map.enableDragging();
	}
	
/**
 * Opens the InfoWindow 
 */

	function openInfoWindow() {
	
		ew.show();
		map.disableDragging();
	}
	
/**
 * Action when a marker is clicked 
 *
 * @param {GLatLng} Position of the marker
 * @param {String} Content of the InfoWindow
 */
		
	
	function doMarkerClick(point, desc) {
	
		if (map && ew) {
		
			// We need to define the point where the map will move to when the info-window is displayed:...
			
			ew.openOnMap(point, getBubble(desc), new GPoint(-170, 0));
			var iWinHeight = $("infowin_content").getHeight();
			var iOuterWinHeight = $("infowin_placeholder").getHeight();
			var iMapHeight = map.getSize().height;
			var oPoint = map.fromLatLngToContainerPixel(point);
			var iLeftPadding = 20;
			if (bOverview) {
			
				iLeftPadding = 120;
			}
			
			var oPixelPos;
			if(navigator.appName.toLowerCase().indexOf("microsoft") > -1) {
			
				oPixelPos = map.fromContainerPixelToLatLng(new GPoint(oPoint.x + iLeftPadding, (oPoint.y - 30) - ((iWinHeight / 2) + (iOuterWinHeight - iWinHeight))));
			}
			else {
			
				oPixelPos = map.fromContainerPixelToLatLng(new GPoint(oPoint.x + iLeftPadding, oPoint.y - ((iWinHeight / 2) + (iOuterWinHeight - iWinHeight))));
			}
	
			// Tell to show as soon as we finished moving the map - and don't get to remove this listener when done
			
			if (desc) {
			
				if (map.getCenter().x != oPixelPos.x || map.getCenter().y != oPixelPos.y) {
				
					var eWinHide = GEvent.addListener(map, "moveend", function() {
					
						openInfoWindow();
						GEvent.removeListener(eWinHide);
					});
					map.panTo(oPixelPos);
				}
				else {
				
					window.setTimeout("openInfoWindow()", 100);
				}
			}
			else {
			
				map.panTo(oPixelPos)
			}
		}
	}
	
	
	//]]>
