function hbToken (
  icon,
  point,
  zoomSettings,
  visibility,
  resizable,
  minZoom,
  maxZoom,
  tooltip)
{
  this.iconFilename = icon.getText ();
  this.point = point;
  this.markers = new Array ();
  this.visibility = visibility ? visibility : "always";
  this.resizable = resizable;
  this.minZoom = minZoom ? parseInt (minZoom [0].getText (), 10) : 0;
  this.maxZoom = maxZoom ? parseInt (maxZoom [0].getText (), 10) : 17;
  this.shown = null;
  this.guid = null; // wird von hbReport gesetzt
  this.mouseOverHandler = new Array ();
  this.mouseOutHandler = new Array ();
  this.clickHandler = new Array ();
  this.isUnfoldEnabled = false;
  this.unfoldMinZoom = 0;

  if (zoomSettings)
  {
    for (var zs = 0; zs < zoomSettings.length; ++zs)
    {
      var size = zoomSettings [zs] ["value"] ["tokenSize"];

      if (0 < size)
      {
        var icon = new GIcon ();
        icon.image = this.iconFilename;
        icon.shadow = this.iconFilename;

        if (undefined == this.resizable || true == this.resizable || "true" == this.resizable)
        {
          icon.iconSize = new GSize (size, size);
          icon.shadowSize = new GSize (size, size);
          icon.iconAnchor = new GPoint (size / 2, size / 2);
          icon.infoWindowAnchor = new GPoint (size, 0);
        }

        var pane = G_MAP_MARKER_SHADOW_PANE;

        if ("always" != this.visibility)
        {
          pane = G_MAP_FLOAT_SHADOW_PANE;
        }

        var marker = new hbTokenMarker (this.point, icon, false, pane);

        if (tooltip)
        {
            marker.setTooltip (tooltip);
        }

        this.markers.push (
          {"minZoom"    : Math.max (this.minZoom, parseInt (zoomSettings [zs] ["minZoom"], 10)),
           "maxZoom"    : Math.min (this.maxZoom, parseInt (zoomSettings [zs] ["maxZoom"], 10)),
           "marker"     : marker});
      }
    }
  }
}

hbToken.prototype.destroy = function ()
{
  for (var h = 0; h < this.mouseOverHandler.length; ++h)
  {
    GEvent.removeListener (this.mouseOverHandler [h]);
    delete this.mouseOverHandler [h];
  }

  for (var h = 0; h < this.mouseOutHandler.length; ++h)
  {
    GEvent.removeListener (this.mouseOutHandler [h]);
    delete this.mouseOutHandler [h];
  }

  for (var h = 0; h < this.clickHandler.length; ++h)
  {
    GEvent.removeListener (this.clickHandler [h]);
    delete this.clickHandler [h];
  }

  this.mouseOverHandler = undefined;
  this.mouseOutHandler = undefined;
  this.clickHandler = undefined;

  this.iconFilename = undefined;
  this.point = undefined;
  this.visibility = undefined;
  this.resizable = undefined;
  this.shown = undefined;

  for (var m = 0; m < this.markers.length; ++m)
  {
    this.markers [m].marker.destroy ();
  }

  this.markers = undefined;
}

hbToken.prototype.addMarkers = function (markerMgr)
{
  if ("always" == this.visibility)
  {
    for (var markerObj = 0; markerObj < this.markers.length; ++markerObj)
    {
      markerMgr.addMarkers ([this.markers [markerObj] ["marker"]], this.markers [markerObj] ["minZoom"], this.markers [markerObj] ["maxZoom"]);
    }
  }
  else if (this.isUnfoldEnabled)
  {
    var realMinZoom;

    for (var markerObj = 0; markerObj < this.markers.length; ++markerObj)
    {
      if (this.unfoldMinZoom <= this.markers [markerObj] ["maxZoom"])
      {
        realMinZoom = Math.max (this.markers [markerObj] ["minZoom"], this.unfoldMinZoom);
        markerMgr.addMarkers ([this.markers [markerObj] ["marker"]], realMinZoom, this.markers [markerObj] ["maxZoom"]);
      }
    }
  }
}

// static member for identification of "opened" marker
hbToken.prototype.visible = null;

hbToken.prototype.clear = function ()
{
  if ("onmouseover" == this.visibility)
  {
    this.clearOnMouseOver ();
  }
  else if ("onclick" == this.visibility)
  {
    this.clearOnClick ();
  }
}

hbToken.prototype.clearOnMouseOver = function ()
{
  if ("onmouseover" == this.visibility)
  {
    if (null != this.shown)
    {
      map.map.removeOverlay (this.shown);
    }

    hbToken.prototype.visible = null;
    this.shown = null;
  }
}

hbToken.prototype.clearOnClick = function ()
{
  if ("onclick" == this.visibility)
  {
    if (null != this.shown)
    {
      map.map.removeOverlay (this.shown);
    }

    hbToken.prototype.visible = null;
    this.shown = null;
  }
}

hbToken.prototype.onClick = function (marker)
{
  if ("onclick" == this.visibility)
  {
    if (this.isUnfoldEnabled && (this.unfoldMinZoom <= map.map.getZoom ()))
    {
      return;
    }

    if (null == this.shown)
    {
      var zoom = map.map.getZoom ();
      var myToken = null;

      for (var tokenObj in this.markers)
      {
        if ((zoom >= this.markers [tokenObj] ["minZoom"]) && (zoom <= this.markers [tokenObj] ["maxZoom"]))
        {
          myToken = this.markers [tokenObj] ["marker"];
          break;
        }
      }

      if (null != myToken)
      {
        if (null != hbToken.prototype.visible)
        {
          hbToken.prototype.visible.clear ();
        }

        if (true == map.isInfoWindowOpened)
        {
          map.map.closeInfoWindow ();
        }

        var markerIcon = marker.getIcon ();
        var tokenIcon = myToken.getIcon ();
        tokenIcon.iconAnchor = markerIcon.iconAnchor;
        tokenIcon.infoWindowAnchor = markerIcon.infoWindowAnchor;
        map.map.addOverlay (myToken);
        this.shown = myToken;
        hbToken.prototype.visible = this;
        myToken.setTooltip (marker.getTitle ());

        if (mapFeedback)
        {
          mapFeedback (this.guid);
        }
      }
    }
  }
}

hbToken.prototype.addListener = function (marker)
{
  var myMarker = marker;
  var self = this;

  if ("onmouseover" == this.visibility)
  {
    this.mouseOverHandler.push (GEvent.addListener (myMarker, "mouseover", function ()
    {
      var zoom = map.map.getZoom ();
      var myToken = null;

      for (var tokenObj in self.markers)
      {
        if ((zoom >= self.markers [tokenObj] ["minZoom"]) && (zoom <= self.markers [tokenObj] ["maxZoom"]))
        {
          myToken = self.markers [tokenObj] ["marker"];
          break;
        }
      }

      if (null != myToken)
      {
        if (null != hbToken.prototype.visible)
        {
          if (hbToken.prototype.visible instanceof Array)
          {
            hbToken.prototype.visible.push (self);
          }
          else
          {
            hbToken.prototype.visible = new Array (hbToken.prototype.visible, self);
          }
        }
        else
        {
          hbToken.prototype.visible = self;
        }

        map.map.addOverlay (myToken);
        self.shown = myToken;
      }
    }));

    this.mouseOutHandler.push (GEvent.addListener (myMarker, "mouseout", function ()
    {
      self.clearOnMouseOver ();
    }));
  }
  else if ("onclick" == this.visibility)
  {
    this.clickHandler.push (GEvent.addListener (myMarker, "click", function ()
    {
      if (null == self.shown)
      {
        self.onClick (myMarker);
      }
      else
      {
        self.clearOnClick ();
      }
    }));
  }
  else if ("always" == this.visibility)
  {
    this.mouseOverHandler.push (GEvent.addListener (myMarker, "mouseover", function ()
    {
      var zoom = map.map.getZoom ();

      for (var tokenObj in self.markers)
      {
        if ((zoom >= self.markers [tokenObj] ["minZoom"]) && (zoom <= self.markers [tokenObj] ["maxZoom"]))
        {
          if (self.markers [tokenObj] ["marker"].setPane)
          {
            self.markers [tokenObj] ["marker"].setPane (G_MAP_MARKER_PANE);
          }
          break;
        }
      }
    }));

    this.mouseOverHandler.push (GEvent.addListener (myMarker, "mouseout", function ()
    {
      var zoom = map.map.getZoom ();

      for (var tokenObj in self.markers)
      {
        if ((zoom >= self.markers [tokenObj] ["minZoom"]) && (zoom <= self.markers [tokenObj] ["maxZoom"]))
        {
          if (self.markers [tokenObj] ["marker"].setPane)
          {
            self.markers [tokenObj] ["marker"].setPane (G_MAP_MARKER_SHADOW_PANE);
          }
          break;
        }
      }
    }));
  }
}

hbToken.prototype.unfoldEnabled = function (yesorno, minZoom, markerMgr)
{
  if (undefined == this.resizable || true == this.resizable || "true" == this.resizable)
  {
    return;
  }

  var realMinZoom;
  var realMaxZoom;

  if (true == yesorno)
  {
    if (this.isUnfoldEnabled)
    {
      if (this.unfoldMinZoom < minZoom)
      {
        for (var markerObj = 0; markerObj < this.markers.length; ++markerObj)
        {
          realMinZoom = Math.max (this.unfoldMinZoom, this.markers [markerObj] ["minZoom"]);
          realMaxZoom = Math.min (minZoom - 1, this.markers [markerObj] ["maxZoom"]);

          if (realMinZoom <= realMaxZoom)
          {
            markerMgr.removeMarker (this.markers [markerObj] ["marker"]);
          }
        }
      }
      else if (this.unfoldMinZoom > minZoom)
      {
        for (var markerObj = 0; markerObj < this.markers.length; ++markerObj)
        {
          realMinZoom = Math.max (minZoom, this.markers [markerObj] ["minZoom"]);
          realMaxZoom = Math.min (this.unfoldMinZoom - 1, this.markers [markerObj] ["maxZoom"]);

          if (realMinZoom <= realMaxZoom)
          {
            markerMgr.addMarkers ([this.markers [markerObj] ["marker"]], realMinZoom, realMaxZoom);
          }
        }
      }
    }
    else
    {
      for (var markerObj = 0; markerObj < this.markers.length; ++markerObj)
      {
        realMinZoom = Math.max (minZoom, this.markers [markerObj] ["minZoom"]);

        if (realMinZoom <= this.markers [markerObj] ["maxZoom"])
        {
          markerMgr.addMarkers ([this.markers [markerObj] ["marker"]], realMinZoom, this.markers [markerObj] ["maxZoom"]);
        }
      }
    }
  }
  else if (this.isUnfoldEnabled)
  {
    for (var markerObj = 0; markerObj < this.markers.length; ++markerObj)
    {
      realMinZoom = Math.max (this.unfoldMinZoom, this.markers [markerObj] ["minZoom"]);

      if (realMinZoom <= this.markers [markerObj] ["maxZoom"])
      {
        markerMgr.removeMarker (this.markers [markerObj] ["marker"]);
      }
    }
  }

  this.isUnfoldEnabled = yesorno;
  this.unfoldMinZoom = minZoom;
}
