// Bremer Familienstadtplan
//
// Copyright 2006-2009 Heike Burkhardt
// www.heikeburkhardt.de

function MapNotLoaded() {
  this.closeIntro = function() {
    setTimeout ("map.closeIntro", 500);
  };
}
var map = new MapNotLoaded();
if (!window.GBrowserIsCompatible || !GBrowserIsCompatible()) window.location.replace ('index.html');

window.onresize = function () {
  if (map) map.onResize();
};

function initMap() {
  if (getQueryParam('Show') > '') {
    var intro = document.getElementById("intro");
    intro.style.display = 'none';
  }
  map = new GMap2(document.getElementById("map"));
  map.initMyMap();
}

/********************************************************************************
class GMap2 (Erweiterungen der GoogleMaps-Klasse)
*********************************************************************************/
GMap2.prototype.initMyMap = function() {
  var cartogis = new GCopyright(1, new GLatLngBounds(new GLatLng(-90, -180),  new GLatLng(90, 180)), 0, "&copy; 2006 cartogis");
  var copyrights = new GCopyrightCollection('');
  copyrights.addCopyright (cartogis);

  var myTileLayer = new GTileLayer(copyrights, 10, 17);
  myTileLayer.isPng = function() {return false;};
  myTileLayer.getTileUrl = function (pos,z) {
    return ("maptiles/" + pos.x + "_" + pos.y + "_" + z + ".gif");
  };

  this.G_BIKETRAILS_MAP = new GMapType([myTileLayer], G_SATELLITE_MAP.getProjection(), "Wege", {errorMessage:"No data available", getCopyrights: function() {return (new Array('cartogis'));}});
  this.addMapType(this.G_BIKETRAILS_MAP);
  this.addControl(this.overviewMapCtrl = new GOverviewMapControl(new GSize(150,150)));

//	this.enableContinuousZoom();
  this.enableDoubleClickZoom();

  this.Div = document.getElementById("map");
  this.nMaxZoomLevel = 19;
  this.nMinZoomLevel = 10;
  this.setCenter (new GLatLng(53.0758, 8.8071), 14);
  this.searchBounds = new GLatLngBounds(new GLatLng(53.0073, 8.4800), new GLatLng(53.2471, 9.069));
  this.progressbar = new ProgressBar(this);
  this.menu = null;
  this.initSize();

  this.aMarkers = new Array();
  this.oActiveMarkerImg = null;
  this.oClosedActiveMarkerImg = null;
  this.cExtendedInfoGroups = '50500 50100 55200';
  this.aOverlays = new Array();
  GEvent.addListener (this, 'moveend', this.showMarkers);
  GEvent.addListener (this, 'click', this.closeActiveMarker);

  if (getQueryParam('Show') > '') {
    setTimeout ('map.closeIntro()', 100);
    setTimeout ('map.remote.openMenu(true)', 800);
  }
};

GMap2.prototype.initSize = function() {
  if (!this.onResize()) setTimeout ('map.initSize()', 500);
};

GMap2.prototype.onResize = function() {
  var size = this.getBestSize();
  if (size) {
      this.Div.style.width = size.width + 'px';
      this.Div.style.height = size.height + 'px';
      this.checkResize();
      if (this.progressBar) this.progressBar.reposition();
      if (this.remote) this.remote.checkPosition();
      return (true);
  }
  else
    return (false);
};

GMap2.prototype.getBestSize = function() {
  var size = null;
  if (window.innerWidth)
    size = new GSize(window.innerWidth, window.innerHeight);
  else if (document.body && document.body.offsetWidth)
    size = new GSize(document.body.offsetWidth, document.body.offsetHeight);

  if (this.remote && this.remote.docked) size.width = Math.max (size.width, 1020);

  if (size) {
    // Raender fuer Navigationspfeile abziehen
    size.width -= 2*50;
    size.height -= 2*50;
  }

  return (size);
};


/*
Fehler im IE: Wird waehrend des Ladens der Map ein Link angelickt, unbricht
das Laden. Ueber das kurzfristige Aendern des Zoomlevels wird das Laden fortgesetzt.
*/
GMap2.prototype.refresh = function() {
  if (document.all) {
    var nZoom = this.getZoom();
    var nTmpZoom = nZoom+1;
    if (nTmpZoom > this.nMaxZoomLevel) nTmpZoom = nZoom-1;
    this.setZoom(nTmpZoom);
    this.setZoom(nZoom);
  }
};

GMap2.prototype.pan = function (cDirection) {
  switch (cDirection) {
    case 'up':
      this.panDirection(0, 1);
      break;

    case 'down':
      this.panDirection(0, -1);
      break;

    case 'left':
      this.panDirection(1, 0);
      break;

    case 'right':
      this.panDirection(-1, 0);
      break;
  }
};


GMap2.prototype.zoom = function (cInOut) {
  switch (cInOut) {
    case 'in':
      if (this.getZoom() < this.nMaxZoomLevel) this.zoomIn();
      break;
    case 'out':
      if (this.getZoom() > this.nMinZoomLevel) this.zoomOut();
      break;
  }
  this.remote.updateZoomSlider();
};

GMap2.prototype.selectMap = function (cType) {
  var mapType = this.getCurrentMapType();
  if (cType == 'biketrails' && mapType != this.G_BIKETRAILS_MAP) this.lastMapType = mapType;
  var newMapType = mapType;

  switch (cType) {
    case 'satellite':
      if (mapType == G_HYBRID_MAP || mapType == G_SATELLITE_MAP)
        newMapType = G_NORMAL_MAP;
      else if (mapType == G_NORMAL_MAP)
        newMapType = G_HYBRID_MAP;
      else
        newMapType = G_SATELLITE_MAP;
      break;

    case 'map':
      if (mapType == G_HYBRID_MAP || mapType == G_NORMAL_MAP)
        newMapType = G_SATELLITE_MAP;
      else if (mapType == G_SATELLITE_MAP)
        newMapType = G_HYBRID_MAP;
      else
        newMapType = G_NORMAL_MAP;
      break;

    case 'biketrails':
      if (mapType == this.G_BIKETRAILS_MAP)
        newMapType = this.lastMapType;
      else
        newMapType = this.G_BIKETRAILS_MAP;
      break;
  }

  var selectmap = document.getElementById("selectmap");
  if (selectmap) {
    cSrc = selectmap.src.replace (/[01]\.gif/, '');
    selectmap.src = cSrc + (newMapType == G_NORMAL_MAP || newMapType == G_HYBRID_MAP ? '1' : '0') + '.gif';
  }
  var selectsatellite = document.getElementById("selectsatellite");
  if (selectsatellite) {
    cSrc = selectsatellite.src.replace (/[01]\.gif/, '');
    selectsatellite.src = cSrc + (newMapType == G_SATELLITE_MAP || newMapType == G_HYBRID_MAP ? '1' : '0') + '.gif';
  }

  var form = document.getElementById("MarkersForm");
  if (form) for (var i=0; i<form.elements.length; i++) {
    var element = form.elements[i];
    if (element.name == 'biketrails') {
      element.checked = (newMapType == this.G_BIKETRAILS_MAP);
    }
  }

  this.setMapType (newMapType);
};



GMap2.prototype.loadMarkers = function (cClass) {
  var _this = this;
  GDownloadUrl("markers/" + cClass + ".xml",
    function(data, responseCode) {
      var xml = GXml.parse(data).documentElement;
      var nWidth = parseInt(xml.getAttribute ('width'));
      var nHeight = parseInt(xml.getAttribute ('height'));
      if (nWidth == 0) nWidth = 35;
      if (nHeight == 0) nHeight = 35;
      _this.aMarkers[cClass] = new Array();

      var markers = xml.getElementsByTagName("marker");
      var lastMarkerID = '';
      var lastMarkerCnt = 0;
      for (var i = 0; i < markers.length; i++) {
        var xmlMarker = markers[i];
        var markerID = xmlMarker.getAttribute ("id");
        if (markerID == lastMarkerID) {
          lastMarkerCnt++;
          markerID+= '.' + lastMarkerCnt;
        }
        else {
          lastMarkerID = markerID;
          lastMarkerCnt = 0;
        }

        var lat = parseFloat(xmlMarker.getAttribute("lat"));
        var lng = parseFloat(xmlMarker.getAttribute("lng"));
        var infotext = xmlMarker.firstChild.nodeValue;
        _this.aMarkers[cClass].push (map.createMarker (cClass, markerID, new GLatLng(lat, lng), infotext));
      }
      _this.showMarkers();
    });
};

GMap2.prototype.unloadMarkers = function (cClass) {
  for (var i in this.aMarkers) if (i == cClass) {
    while (this.aMarkers[cClass].length > 0) {
      var oMarker = this.aMarkers[cClass].pop();
      if (oMarker.shown) this.removeTMarker (oMarker);
    }
  }
};

GMap2.prototype.showMarkers = function() {
  var oMarker, i;
  var bounds = this.getBounds();
  var aShowMarkers = new Array();
  for (var cClass in this.aMarkers) {
    for (i in this.aMarkers[cClass]) {
      oMarker = this.aMarkers[cClass][i];
      if (!oMarker.shown && bounds.contains (oMarker.anchorLatLng)) aShowMarkers.push (oMarker);
    }
  }

  if (aShowMarkers.length > 10) {
    this.progressBar.begin (aShowMarkers.length);
    this.showMarkersAsync();
  }
  else {
    for (i in aShowMarkers) {
      oMarker = aShowMarkers[i];
      this.addTMarker (oMarker);
      oMarker.shown = true;
    }
  }
};

var showMarkersTO = null;

GMap2.prototype.showMarkersAsync = function() {
  clearTimeout (showMarkersTO);
  var nShowMarkers = 1;
  var oMarker, i;
  var bounds = this.getBounds();
  var nFound = 0;
  for (var cClass in this.aMarkers) {
    for (i in this.aMarkers[cClass]) {
      oMarker = this.aMarkers[cClass][i];
      if (!oMarker.shown && bounds.contains (oMarker.anchorLatLng)) {
        this.addTMarker (oMarker);
        oMarker.shown = true;
        this.progressBar.update();
        nFound++;
        if (nFound >= nShowMarkers) {
          clearTimeout (showMarkersTO);
          showMarkersTO = setTimeout ('map.showMarkersAsync()', 5);
          return;
        }
      }
    }
  }
  if (nFound < nShowMarkers) this.progressBar.end();
};

var nMarkerID = 1;

GMap2.prototype.createMarker = function(cClass, id, LatLng, cText) {
  var image = 'markers/' + cClass + (document.all ? '.gif' : '.png');
  var marker = new TMarker();
  marker.id = cClass + id;
  marker.anchorLatLng = LatLng;
  marker.markerOffset = this.getMarkerIconOffset(cClass);
  cText = cText.replace (/(^ | $)/, '');
  if (cText == '') cText = 'Keine weiteren Infos verf&uuml;gbar.<br/>';
  marker.content = '<div id="M' + id + '" class="marker"><img id="I' + id + '" onclick="map.expandMarker(this)" src="' + image + '" align="left"><span>' + cText + '<br/></span></div>';
  marker.shown = false;
  return (marker);
};

GMap2.prototype.createSearchMarker = function(LatLng, cText) {
  var marker = new TMarker();
  marker.id = 'Search' + (nMarkerID++);
  marker.anchorLatLng = LatLng;
  marker.markerOffset = new GSize(15, 15);
  marker.content = '<div class="searchMarker">' + cText + '</div>';
  marker.shown = false;
  return (marker);
};


GMap2.prototype.getMarkerIconOffset = function(cClass) {
  return (new GSize(22, 25));
};

GMap2.prototype.closeActiveMarker = function(ov, point) {
  if (this.oActiveMarkerImg) {
    this.oClosedActiveMarkerImg = this.oActiveMarkerImg;
    this.oActiveMarkerImg.onclick();
  }
  else
    this.oClosedActiveMarkerImg = null;
  if (this.search) this.search.removeMarkers();
};

GMap2.prototype.expandMarker = function(oImg) {
  var oDiv = oImg.parentNode;
  var marker = oDiv.parentNode;
  var oSpan = oImg.nextSibling;
  while (oSpan.nextSibling && oSpan.nodeName != 'SPAN') {
    oSpan = oSpan.nextSibling;
  }
  var bOpen = (oSpan.style.display == 'inline' || (oImg == this.oClosedActiveMarkerImg));
  oSpan.style.display = (bOpen ? 'none' : 'inline');
  marker.style.zIndex = (bOpen ? 10 : 1000);
  oDiv.className = oDiv.className.replace (/ ?active/, '') + (bOpen ? '' : ' active');
  if (oDiv.className.match (/detail/)) {
    oDiv.className = (bOpen ? oDiv.className.replace (/ ?detailActive/, '') : oDiv.className + ' detailActive');
  }

  if (!bOpen) {
    this.closeActiveMarker();
    this.oActiveMarkerImg = oImg;
    var id = oDiv.getAttribute('id').replace(/^M/, '').replace('.', '_');
    var match = id.match (/^[0-9]+/);
    if (match && this.cExtendedInfoGroups.search (match[0]) >= 0) {
      var url = "details/" + id + ".html";
      GDownloadUrl (url,
        function(data, responseCode) {
          if (data.length > 0 && !(responseCode + '').match(/^40[0-9]/)) {
            var sw = map.fromDivPixelToLatLng(new GPoint (parseInt(marker.style.left), parseInt(marker.style.top)+420));
            var ne = map.fromDivPixelToLatLng(new GPoint (parseInt(marker.style.left)+620, parseInt(marker.style.top)));
            var bounds = new GLatLngBounds (sw, ne);
            var center = bounds.getCenter();
            map.panTo (center);

            marker.innerHTML = data.getBody();
            map.oActiveMarkerImg = null;
            oDiv = marker.firstChild;
            while (oDiv && oDiv.nodeName != 'DIV') oDiv = oDiv.nextSibling;
            if (oDiv) {
              oImg = oDiv.firstChild;
              while (oImg && oImg.nodeName != 'IMG') oImg = oImg.nextSibling;
              if (oImg) map.oActiveMarkerImg = oImg;
              oDiv.className += ' detailActive';
            }

            try {
              map.overviewMapCtrl.hide();
            }
            catch (err) {}
          }
        });
    }
  }
  else
    this.oActiveMarkerImg = null;
};


GMap2.prototype.expandDetails = function(oImg) {
  var oDiv = oImg.parentNode;
  var marker = oDiv.parentNode;
  var child = oImg.nextSibling;
  while (child && !child.style) child = child.nextSibling;
  var bOpen = (child.style.display != 'none' || (oImg == this.oClosedActiveMarkerImg));
  var divClassName = oDiv.className;
  while (child) {
    if (child.style) child.style.display = (bOpen ? 'none' : 'block');
    child = child.nextSibling;
  }
  marker.style.zIndex = (bOpen ? 10 : 1000);
  oDiv.className = (bOpen ? oDiv.className.replace (/ ?active/, '') : oDiv.className + ' active');
  if (oDiv.className.match (/detail/)) {
    oDiv.className = (bOpen ? oDiv.className.replace (/ ?detailActive/, '') : oDiv.className + ' detailActive');
  }
  if (!bOpen) {
    this.closeActiveMarker();
    this.oActiveMarkerImg = oImg;
  }
  else
    this.oActiveMarkerImg = null;

  if (bOpen && divClassName.match (/active detail/)) this.expandDetails(oImg);
};


GMap2.prototype.loadKmz = function (filename) {
  var found = false;
  for (var i in this.aOverlays) {
    if (i == filename) {
      found = true;
      break;
    }
  }
  var url = window.location.protocol + '//' + window.location.host + window.location.pathname;
  url = url.replace (/\/[^\/]+$/, '/');
  if (url.search(/localhost/) > 0) url = 'http://www.heikeburkhardt.de/Map/';
  url += 'markers/' + filename + '.kmz';
  if (!found)
    this.aOverlays[filename] = new GGeoXml(url, function(){map.kmzLoaded(filename);});
  else
    this.addOverlay(this.aOverlays[filename]);
};


GMap2.prototype.kmzLoaded = function(filename) {
  var kmz = this.aOverlays[filename];
  kmz.Zk = function() {};
  kmz.aU = function() {};
  kmz.bN = function() {};

  this.addOverlay (kmz);
  kmz.gotoDefaultViewport(this);
};


GMap2.prototype.unloadKmz = function (filename) {
  for (var i in this.aOverlays) {
    if (i == filename) this.removeOverlay(this.aOverlays[i]);
  }
};


GMap2.prototype.closeIntro = function () {
  var intro = document.getElementById("intro");
  var remote = document.getElementById("remote");
  if (intro && remote) {
    if (remote.firstChild.nodeName == 'A') {
      var _this = this;
      var url = remote.firstChild.getAttribute ('href');
      remote.innerHTML = '';
      GDownloadUrl (url,
        function(data, responseCode) {
          remote.innerHTML = data.getBody();
          _this.remote = new Remote(_this);
        });
    }
    intro.style.display = 'none';
    remote.style.display = 'block';
//		this.showSearch();
  }
  this.refresh();
};

GMap2.prototype.showImprint = function () {
  var intro = document.getElementById("intro");
  var imprint = document.getElementById("imprint");
  if (imprint && imprint.firstChild.nodeName == 'A') {
      var url = imprint.firstChild.getAttribute ('href');
      imprint.innerHTML = '';
      GDownloadUrl (url,
        function(data, responseCode) {
          imprint.innerHTML = data.getBody();
        });
  }
  if (intro && imprint) {
    if (imprint.style.display == 'none') {
      this.remote.hide();
      intro.style.display = 'none';
      imprint.style.display = 'block';
    }
    else
      this.closeImprint();
  }
};

GMap2.prototype.closeImprint = function () {
  var intro = document.getElementById("imprint");
  if (intro) {
    intro.style.display = 'none';
    this.remote.show();
    this.onResize();
  }
  this.refresh();
};


GMap2.prototype.showSearch = function () {
  var search = document.getElementById("search");
  var imprint = document.getElementById("imprint");
  if (search && imprint) {
    if (search.style.display == 'none') {
      imprint.style.display = 'none';
      search.style.display = 'block';
      var selectSearch = document.getElementById("selectsearch");
      if (selectSearch) selectSearch.src = selectSearch.src.replace (/[01]\.gif/, '1.gif');
      if (this.search) this.search.initPosition();
    }
    else {
      this.closeSearch();
      return;
    }
  }

  if (search && search.firstChild.nodeName == 'A') {
    var url = search.firstChild.getAttribute ('href');
    var _this = this;
    search.innerHTML = '';
    GDownloadUrl (url,
      function(data, responseCode) {
        search.innerHTML = data.getBody();
        _this.search = new Search(_this);
        _this.search.initPosition();
      });
  }
};

GMap2.prototype.closeSearch = function () {
  var search = document.getElementById("search");
  if (search) {
    search.style.display = 'none';
    var selectSearch = document.getElementById("selectsearch");
    if (selectSearch) selectSearch.src = selectSearch.src.replace (/[01]\.gif/, '0.gif');
    if (this.search) this.search.removeMarkers();
    this.onResize();
  }
  this.refresh();
};


GMap2.prototype.addTMarker = function(marker){
  marker.initialize(this);
};

GMap2.prototype.removeTMarker = function (marker){
  var span=document.getElementById(marker.id);
  if (span) {
    this.getPane(G_MAP_MAP_PANE).removeChild(span);
    delete (span);
  }
};


/********************************************************************************
class String
*********************************************************************************/
String.prototype.getBody = function () {
  html = this.substr(this.search (/<body>/i) + 8);
  html = html.substr(0, html.search (/<\/body>/i));
  return (html);
};

/********************************************************************************
class TMarker
// based on:
// TLabel() GMaps API extension copyright 2005-2006 Tom Mangan
// http://gmaps.tommangan.us/tmarker.html
// licensed for use on www.bremerfamilienstadtplan.de
*********************************************************************************/
function TMarker() {}

TMarker.prototype.initialize=function (map) {
  this.parentMap=map;
  var span=document.createElement('span');
  span.setAttribute('id', this.id);
  span.innerHTML=this.content;
  document.body.appendChild(span);
  span.style.position='absolute';
  span.style.zIndex=1;
  this.w = document.getElementById(this.id).offsetWidth;
  this.h = document.getElementById(this.id).offsetHeight;
  this.mapTray = this.parentMap.getPane(G_MAP_MAP_PANE);
  this.mapTray.appendChild (span);
  if(!this.markerOffset) this.markerOffset=new GSize(0,0);
  this.setPosition();
  GEvent.bind(this.parentMap,"zoomend",this,function(){this.setPosition();});
  GEvent.bind(this.parentMap,"moveend",this,function(){this.setPosition();});
};

TMarker.prototype.setPosition=function() {
  var b=this.parentMap.fromLatLngToDivPixel(this.anchorLatLng);
  var x=parseInt(b.x);
  var y=parseInt(b.y);
  var span=document.getElementById(this.id);
  if (span) {
    span.style.left=x-this.markerOffset.width+'px';
    span.style.top=y-this.markerOffset.height+'px';
  }
  else {
    this.shown = false;
    this.parentMap.showMarkersAsync();
  }
};


/********************************************************************************
class ProgressBar
*********************************************************************************/
function ProgressBar (map) {
  this.Map = map;
  this.MaxLen = 100;
  this.MaxValue = 100;
  this.Value = 0;

  this.bar = document.createElement("div");
  this.bar.style.backgroundColor = '#64A138';
  this.bar.className = 'progressBar';
  this.bar.style.position = 'absolute';
  this.bar.style.left = '0px';
  this.bar.style.top = '10px';
  this.bar.style.width = '0px';
  this.bar.style.height = '7px';
  this.end();

  this.Map.getContainer().appendChild(this.bar);
  this.Map.progressBar = this;
  return (this);
}

ProgressBar.prototype.reposition = function() {
  var size = this.Map.getSize();
  this.bar.style.top = size.height - 7 + 'px';
  this.MaxLen = size.width;
  this.update (this.Value);
};

ProgressBar.prototype.begin = function (nMaxValue) {
  this.MaxValue = nMaxValue;
  this.Value = 0;
  this.update();
  this.bar.style.visibility = 'visible';
};

ProgressBar.prototype.end = function () {
  this.bar.style.visibility = 'hidden';
};

ProgressBar.prototype.update = function(nValue) {
  if (nValue == null)
    this.Value++;
  else
    this.Value = nValue;
  var nWidth = Math.min (this.MaxLen, this.Value / this.MaxValue * this.MaxLen);
  this.bar.style.width = nWidth + 'px';
};



/********************************************************************************
class Remote
*********************************************************************************/
function Remote(map) {
  this.Map = map;
  this.div = document.getElementById('remote');
  this.oRemoteWin = null;
  this.left = 29;
  this.top = 300;
  this.docked = null;
  this.dock();
  this.top = 100;
  //this.undock();
  this.initZoomSlider();
  this.dragging = false;
}

Remote.prototype.hide = function() {
  this.div.style.display = 'none';
  this.Map.closeSearch();
};

Remote.prototype.show = function() {
  this.div.style.display = 'block';
};

Remote.prototype.undock = function() {
  this.div.className = 'undocked';
  this.div.style.left = this.left + 'px';
  this.div.style.top = this.top + 'px';
  this.docked = false;
  this.Map.onResize();
  this.Map.refresh();
};

Remote.prototype.dock = function() {
  this.left = parseInt (this.div.style.left);
  this.top = parseInt (this.div.style.top);
  this.div.className = 'docked';
  this.div.style.left = '303px';
  this.div.style.top = '0px';
  this.docked = true;
  this.Map.onResize();
  this.Map.refresh();
};

Remote.prototype.startDrag = function(event) {
  if (!this.docked && !this.dragging) {
    if (document.body && document.documentElement && window.event) {
      this.cursorStartX = window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft;
        this.cursorStartY = window.event.clientY + document.documentElement.scrollTop + document.body.scrollTop;
     }
    else if (event.clientX) {
        this.cursorStartX = event.clientX + window.scrollX;
        this.cursorStartY = event.clientY + window.scrollY;
    }

    this.dragStartX = parseInt(this.div.style.left, 10);
    this.dragStartY = parseInt(this.div.style.top,  10);
    if (document.addEventListener) {
      document.addEventListener("mousemove", onDragRemote, true);
      document.addEventListener("mouseup", stopDragRemote, true);
    }
    else if (document.attachEvent) {
        document.attachEvent("onmousemove", onDragRemote);
       document.attachEvent("onmouseup", stopDragRemote);
    }

    if (event && event.preventDefault)
      event.preventDefault();
    else if (window.event) {
      window.event.cancelBubble = true;
       window.event.returnValue = false;
    }
  }
};

Remote.prototype.onDrag = function(event) {
  var x,y;
  if (document.body && document.documentElement && window.event) {
      x = window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft;
      y = window.event.clientY + document.documentElement.scrollTop + document.body.scrollTop;
  }
  else {
      x = event.clientX + window.scrollX;
      y = event.clientY + window.scrollY;
    }
  this.left = x - this.cursorStartX + this.dragStartX;
  this.div.style.left = this.left + 'px';
  this.top = y - this.cursorStartY + this.dragStartY;
  this.div.style.top  = this.top + 'px';
    if (event && event.preventDefault)
      event.preventDefault();
    else if (window.event) {
      window.event.cancelBubble = true;
       window.event.returnValue = false;
    }
};

Remote.prototype.stopDrag = function (event) {
  if (document.removeEventListener) {
    document.removeEventListener("mousemove", onDragRemote, true);
      document.removeEventListener("mouseup", stopDragRemote, true);
    }
    else if (document.detachEvent) {
        document.detachEvent("onmousemove", onDragRemote);
      document.detachEvent("onmouseup", stopDragRemote);
    }
};

Remote.prototype.initZoomSlider = function () {
  var nZoomWidth = 81 - 5;
  var nZoomRange = this.Map.nMaxZoomLevel - this.Map.nMinZoomLevel + 1;
  this.nZoomSliderStep = nZoomWidth / nZoomRange;
  this.updateZoomSlider();
  this.nDragZoomLevel = 0;
  GEvent.addListener (this.Map, 'zoomend', function() {map.remote.updateZoomSlider();});
};

Remote.prototype.updateZoomSlider = function (nZoomLevel) {
  var slider = document.getElementById('zoomslider');
  var nZoom = nZoomLevel;
  if (nZoom == null) nZoom = this.Map.getZoom();
  nZoom = Math.min (this.Map.nMaxZoomLevel, Math.max (this.Map.nMinZoomLevel, nZoom));
  var nZoomOffset = nZoom - this.Map.nMinZoomLevel;
  var nOffset = Math.round(nZoomOffset * this.nZoomSliderStep);
  slider.style.left = nOffset + 'px';
};

Remote.prototype.startZoomDrag = function (event) {
    this.dragging = true;
    this.nDragZoomLevel = this.Map.getZoom();
    this.updateZoomSlider();
    if (document.body && document.documentElement && window.event) {
      this.cursorStartX = window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft;
     }
    else if (event.clientX) {
        this.cursorStartX = event.clientX + window.scrollX;
    }

    this.dragStartZoomLevel = this.Map.getZoom();
    if (document.addEventListener) {
      document.addEventListener("mousemove", onDragRemoteZoom, true);
      document.addEventListener("mouseup", stopDragRemoteZoom, true);
    }
    else if (document.attachEvent) {
        document.attachEvent("onmousemove", onDragRemoteZoom);
       document.attachEvent("onmouseup", stopDragRemoteZoom);
    }

    if (event && event.preventDefault)
      event.preventDefault();
    else if (window.event) {
      window.event.cancelBubble = true;
       window.event.returnValue = false;
    }

};

Remote.prototype.onDragZoom = function(event) {
  var x;
  if (document.body && document.documentElement && window.event)
    x = window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft;
  else
    x = event.clientX + window.scrollX;

  var nOffset = x - this.cursorStartX;
  var nZoomOffset = Math.floor(nOffset / this.nZoomSliderStep);
  var nCalcZoom = this.dragStartZoomLevel + nZoomOffset;
  var nZoom = Math.min (this.Map.nMaxZoomLevel, Math.max (this.Map.nMinZoomLevel, nCalcZoom));
  var bOutOfSlider = (nZoom != nCalcZoom);

  if (nZoom != this.nDragZoomLevel) {
    this.nDragZoomLevel = nZoom;
    this.updateZoomSlider (this.nDragZoomLevel);
  }

  if (event && event.preventDefault)
    event.preventDefault();
  else if (window.event) {
    window.event.cancelBubble = true;
    window.event.returnValue = false;
  }

  if (bOutOfSlider) this.stopDragZoom();
};

Remote.prototype.stopDragZoom = function (event) {
  if (document.removeEventListener) {
    document.removeEventListener("mousemove", onDragRemoteZoom, true);
     document.removeEventListener("mouseup", stopDragRemoteZoom, true);
   }
   else if (document.detachEvent) {
       document.detachEvent("onmousemove", onDragRemoteZoom);
     document.detachEvent("onmouseup", stopDragRemoteZoom);
   }
   this.dragging = false;
   this.Map.setZoom (this.nDragZoomLevel);
   this.updateZoomSlider();
};




Remote.prototype.checkPosition = function(bInit) {
  var mapdiv = document.getElementById('map');
  if (mapdiv) {
    var mapwidth = parseInt(mapdiv.style.width);
    var mapheight = parseInt(mapdiv.style.height);
    if (parseInt(this.div.style.left) > 0 && bInit) {
      var left = parseInt (this.div.style.left);
      if (left > mapwidth || left < 0 || top > mapheight || top < 0) this.openWindow();
    }
  }
};

Remote.prototype.toggleMenu = function() {
  var menuDiv = document.getElementById('menu');
  if (menuDiv.style.display == 'none')
    this.openMenu(false);
  else
    this.closeMenu();
}

Remote.prototype.openMenu = function(bHide) {
  var menuDiv = document.getElementById('menu');
  if (menuDiv && menuDiv.firstChild.nodeName == 'A') {
      var url = menuDiv.firstChild.getAttribute ('href');
      var _this = this;
      menuDiv.innerHTML = '';
      GDownloadUrl (url,
        function(data, responseCode) {
          menuDiv.innerHTML = data.getBody();
          _this.Map.menu = new Menu();
        });
  }
  var intro = document.getElementById("imprint");
  if (intro) intro.style.display = 'none';
  if (menuDiv) {
    if (bHide)
      menuDiv.style.display = 'none';
    else
      menuDiv.style.display = 'block';
    var selectMenu = document.getElementById("selectMenu");
    if (selectMenu) selectMenu.src = selectMenu.src.replace (/[01]\.gif/, (bHide ? '0' : '1') + '.gif');
  }
};

Remote.prototype.closeMenu = function() {
  var menu = document.getElementById('menu');
  if (menu) menu.style.display = 'none';
  var selectMenu = document.getElementById("selectMenu");
  if (selectMenu) selectMenu.src = selectMenu.src.replace (/[01]\.gif/, '0.gif');
//	this.show();
  this.Map.refresh();
};

function onDragRemote(event) {
  map.remote.onDrag(event);
}

function stopDragRemote(event) {
  map.remote.stopDrag(event);
}

function onDragRemoteZoom(event) {
  map.remote.onDragZoom(event);
}

function stopDragRemoteZoom(event) {
  map.remote.stopDragZoom(event);
}



/********************************************************************************
class Search
*********************************************************************************/
function Search(map) {
  this.Map = map;
  this.div = document.getElementById('search');
  this.placesFound = new Array();
  this.left = 40;
  this.top = 400;
  this.dragging = false;
}

Search.prototype.initPosition = function() {
  if (!this.Map.remote.docked) {
    this.left = map.remote.left + 170;
    this.top = map.remote.top;
  }
  this.div.style.left = this.left + 'px';
  this.div.style.top = this.top + 'px';
  if (document.forms['search'] && document.forms['search'].elements[0]) document.forms['search'].elements[0].focus();
};

Search.prototype.onFound = function (data) {
  var html = '';
  this.placesFound = [];
  if (data.Status.code < 400 && data.Placemark.length > 0) {
    for (var i=0; i<data.Placemark.length; i++) {
      var place = data.Placemark[i];
      var point = new GLatLng (place.Point.coordinates[1], place.Point.coordinates[0]);
      if (this.Map.searchBounds.contains (point)) {
        var bUse = true;
        if (place['AddressDetails'] && place.AddressDetails['Accuracy']) {
          var nAccuracy = place.AddressDetails.Accuracy;
          if (nAccuracy <= 1 || nAccuracy >= 8) bUse = false;
        }

        if (bUse) {
          var n = this.placesFound.length;
          this.placesFound[n] = place;
          html += '<li><a href="Javascript:map.search.showPlace(' +  n + ')">' + this.getAddress(n) + '</a></li>';
        }
      }
    }
    if (this.placesFound.length > 0) html = '<ul>' + html + '</ul>';
    if (this.placesFound.length == 1) this.showPlace(0);
  }
  if (this.placesFound.length == 0)	html = '<div class="error">Die Suche lieferte in Bremen kein Ergebnis.</div>';

  var resultDiv = document.getElementById('SearchResults');
  resultDiv.innerHTML = html;
};

Search.prototype.getAddress = function (i) {
  var place = this.placesFound[i];
  var address = place.address.replace (/, Bundesrepublik Deutschland/, '');
  var address = address.replace (/, (\d+ )?Bremen/, '');
  return (address);
};


Search.prototype.removeMarkers = function() {
  this.Map.unloadMarkers('Search');
};

Search.prototype.showPlace = function (i) {
  var place = this.placesFound[i];
  var latlng = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);
  this.Map.unloadMarkers('Search');
  this.Map.aMarkers['Search'] = new Array();
  this.Map.aMarkers['Search'].push (this.Map.createSearchMarker (latlng, this.getAddress(i)));
  this.Map.showMarkers();
  this.Map.panTo (latlng);
};

Search.prototype.find = function (address) {
  address += ', Bremen, Germany';
  var geocoder = new GClientGeocoder();
  var _this = this;
  geocoder.getLocations (address, function(data) {_this.onFound(data);});
  return (false);
};

Search.prototype.startDrag = function(event) {
  if (!this.dragging) {
    if (document.body && document.documentElement && window.event) {
      this.cursorStartX = window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft;
        this.cursorStartY = window.event.clientY + document.documentElement.scrollTop + document.body.scrollTop;
     }
    else if (event.clientX) {
        this.cursorStartX = event.clientX + window.scrollX;
        this.cursorStartY = event.clientY + window.scrollY;
    }

    this.dragStartX = parseInt(this.div.style.left, 10);
      this.dragStartY = parseInt(this.div.style.top,  10);
      if (document.addEventListener) {
        document.addEventListener("mousemove", onDragSearch, true);
        document.addEventListener("mouseup", stopDragSearch, true);
      }
      else if (document.attachEvent) {
          document.attachEvent("onmousemove", onDragSearch);
         document.attachEvent("onmouseup", stopDragSearch);
      }

      if (event && event.preventDefault)
        event.preventDefault();
      else if (window.event) {
        window.event.cancelBubble = true;
         window.event.returnValue = false;
      }
  }
};

Search.prototype.onDrag = function(event) {
  var x,y;
  if (document.body && document.documentElement && window.event) {
      x = window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft;
      y = window.event.clientY + document.documentElement.scrollTop + document.body.scrollTop;
  }
  else {
      x = event.clientX + window.scrollX;
      y = event.clientY + window.scrollY;
    }
  this.div.style.left = x - this.cursorStartX + this.dragStartX + 'px';
  this.div.style.top  = y - this.cursorStartY + this.dragStartY + 'px';
    if (event && event.preventDefault)
      event.preventDefault();
    else if (window.event) {
      window.event.cancelBubble = true;
       window.event.returnValue = false;
    }
};

Search.prototype.stopDrag = function (event) {
  if (document.removeEventListener) {
    document.removeEventListener("mousemove", onDragSearch, true);
      document.removeEventListener("mouseup", stopDragSearch, true);
    }
    else if (document.detachEvent) {
        document.detachEvent("onmousemove", onDragSearch);
      document.detachEvent("onmouseup", stopDragSearch);
    }
};

function onDragSearch(event) {
  map.search.onDrag(event);
}

function stopDragSearch(event) {
  map.search.stopDrag(event);
}



/********************************************************************************
class Menu
*********************************************************************************/
function Menu(map) {
  this.Map = map;
  this.aShowElements = new Array();
  var _this = this;

  GDownloadUrl("categories.xml",
      function(data, responseCode) {
        var xml = GXml.parse(data).documentElement;
        _this.categoriesXmlLoaded (xml);
      });
}

Menu.prototype.categoriesXmlLoaded = function (xml) {
  this.aShowElements = getQueryParam("Show").split(',');

  this.buttonsDiv = document.getElementById('categories');
  while (this.buttonsDiv.firstChild) this.buttonsDiv.removeChild(this.buttonsDiv.firstChild);
  this.elementsDiv = document.getElementById('cat_elements');
  while (this.elementsDiv.firstChild) this.elementsDiv.removeChild(this.elementsDiv.firstChild);

  for (var i=0; i<xml.childNodes.length; i++) {
    var categoryNode = xml.childNodes[i];
    if (categoryNode.nodeType == 1) this.parseCategory (categoryNode);
  }
  if(navigator.appVersion.indexOf("MSIE") != -1) {
    // Fixes the name issue, event handling, and rendering bugs!
    var menuDiv = document.getElementById('menu');
     menuDiv.innerHTML = menuDiv.innerHTML;
    this.buttonsDiv = document.getElementById('categories');
    this.elementsDiv = document.getElementById('cat_elements');
  }

  if (this.aShowElements.length > 0) setTimeout ('map.menu.showElements()', 100);
  map.refresh();
}

Menu.prototype.showElements = function() {
  var form = document.MarkersForm;
  for (var i=0; i < form.elements.length; i++) {
    var element = form.elements[i];
    for (var j=0; j<this.aShowElements.length; j++) {
      if (this.aShowElements[j] == element.value) {
        element.checked = true;
        element.onclick();
      }
    }
  }
}

Menu.prototype.parseCategory = function(xml) {
  var catID = this.buttonsDiv.childNodes.length;
  var title = xml.getAttribute ('title');

  var button = document.createElement('div');
  this.buttonsDiv.appendChild (button);
  button.className = 'category' + (catID == 0 ? ' current' : '');
  var a = document.createElement('a');
  button.appendChild(a);
  a.setAttribute ('href', 'Javascript:map.menu.showCategory(' + catID + ')');
  a.appendChild(document.createTextNode(title));

  var categoryDiv = document.createElement('div');
  this.elementsDiv.appendChild (categoryDiv);
  categoryDiv.className = 'cat_elements' + (catID == 0 ? ' current' : '');
  var table = document.createElement('table');
  categoryDiv.appendChild(table);
  table.setAttribute ('cellspacing', '0');
  var tbody = document.createElement('tbody');
  table.appendChild (tbody);

  for (var i=0; i<xml.childNodes.length; i++) {
    var childNode = xml.childNodes[i];
    if (childNode.nodeType == 1 && childNode.nodeName == 'item') {
      this.addCatItemRow(childNode, tbody);
    }
  }
};


Menu.prototype.addCatItemRow = function (catItem, tbody) {
  var name = catItem.getAttribute('name');
  var title = catItem.getAttribute ('title');
  var text = (catItem.firstChild == null ? '' : catItem.firstChild.nodeValue);
  var type = catItem.getAttribute ('type');

  var tr = document.createElement ('tr');
  tbody.appendChild (tr);
  var td = document.createElement('td');
  tr.appendChild(td);
  td.className = 'check';
  var input = document.createElement('input');
  input.setAttribute ('type', 'checkbox');
  input.setAttribute ('value', (type == 'biketrails' ? 'biketrails' : name));
  input.setAttribute ('alt', title);
  input.setAttribute ('name', type);
  input.setAttribute ('onclick', "map.menu.onClickElement(this, '" + type + "')");

  td.appendChild(input);

  td = document.createElement('td');
  tr.appendChild(td);
  td.className = 'icon';
  var img = document.createElement('img');
  td.appendChild (img);
  img.setAttribute ('src', 'markers/' + name + '.gif');
  img.setAttribute ('alt', title);

  td = document.createElement ('td');
  tr.appendChild(td);
  td.setAttribute ('class', 'text');
  var div = document.createElement ('div');
  td.appendChild(div);
  div.appendChild(document.createTextNode(title));
  td.appendChild(document.createTextNode(text));
  return (tr);
};


Menu.prototype.onClickElement = function (element, name) {
  if (name == 'biketrails')
    map.selectMap ('biketrails');
  else if (name == 'kmz') {
    if (element.checked)
      map.loadKmz (element.value);
    else
      map.unloadKmz (element.value);
  }
  else {
    if (element.checked)
      map.loadMarkers (element.value);
    else
      map.unloadMarkers (element.value);
  }

  for (var i=0; i<element.form.elements.length; i++) {
    var elem = element.form.elements[i];
    if (elem.checked != element.checked && (elem.value == element.value || (element.name == 'biketrails' && elem.name == 'biketrails'))) {
      elem.checked = element.checked;
    }
  }
};


Menu.prototype.showCategory = function(nCategory) {
  for (var i=0; i<this.buttonsDiv.childNodes.length; i++) {
    this.buttonsDiv.childNodes[i].className = this.buttonsDiv.childNodes[i].className.replace(/ ?current/, '');
  }
  this.buttonsDiv.childNodes[nCategory].className += ' current';

  for (var i=0; i<this.elementsDiv.childNodes.length; i++) {
    this.elementsDiv.childNodes[i].className = this.elementsDiv.childNodes[i].className.replace(/ ?current/, '');
  }
  this.elementsDiv.childNodes[nCategory].className += ' current';
};


function getSiblingNode (n, name) {
  while (n != null && (n.nodeType != 1 || n.nodeName != name)) n = n.nextSibling;
  return (n);
}



function showObject (obj, nIndent) {
  html = '';
  if (nIndent == null) nIndent = 0;
  var cIndent = '' + nIndent;
  for (var i=0; i<nIndent; i++) cIndent+= '_';
  for(var x in obj) {
    if (typeof obj[x] == "object") {
      GLog.write (cIndent + x + ' = [object]:');
      showObject (obj[x], nIndent + 1);
    }
    else
      GLog.write (cIndent + x + '= [' + (typeof obj[x]) + '] ' + obj[x]);
  }
  return (html);
}

function getQueryParam (param)
{
  param = param.replace(/[\[]/,'\\\[').replace(/[\]]/,'\\\]');
  var reg = new RegExp('[\\?&]' + param + '=([^&#]*)');
  var results = reg.exec (window.location.href);
  if (results == null)
    return '';
  else
    return results[1];
}