function hbReport (guid,
    title,
    rssTitle,
    messages,
    linkUrls,
    linkTitles,
    point,
    icon,
    minZoom,
    maxZoom,
    tokens,
    lines,
    areas,
    startTime,
    stopTime,
    reportInfo,
    itemIndex,
    zoomSettings,
    infoWindowSettings,
    urls,
    isUnfoldEnabled,
    unfoldMinZoom)
{
  this.iconFilename = icon.getText ();
  this.minZoom = minZoom ? parseInt (minZoom.getText (), 10) : 0;
  this.maxZoom = maxZoom ? parseInt (maxZoom.getText (), 10) : 17;
//  this.iconHeightFactor = 1.0;
//  this.iconWidthFactor = 1.0;
//  this.iconHeightFactorOnMouseOver = 1.0;
//  this.iconWidthFactorOnMouseOver = 1.0;

  this.info = reportInfo;
  
  if (startTime && (0 < startTime.length))
  {
    this.startTime = startTime [0].getText ();
  }
  if (stopTime && (0 < stopTime.length))
  {
    this.stopTime = stopTime [0].getText ();
  }

/*  if (icon ["relativeHeight"])
  {
    this.iconHeightFactor = parseFloat (icon ["relativeHeight"]);
  }

  if (icon ["relativeWidth"])
  {
    this.iconWidthFactor = parseFloat (icon ["relativeWidth"]);
  }

  if (icon ["relativeHeightOnMouseOver"])
  {
    this.iconHeightFactorOnMouseOver = parseFloat (icon ["relativeHeightOnMouseOver"]);
  }

  if (icon ["relativeWidthOnMouseOver"])
  {
    this.iconWidthFactorOnMouseOver = parseFloat (icon ["relativeWidthOnMouseOver"]);
  }
*/
  this.guid = guid;
  this.title = title;
  this.rssTitle = rssTitle;
  this.messages = messages;
  this.linkUrls = linkUrls;
  this.linkTitles = linkTitles;
  this.point = point;
  this.markers = new Array ();
  this.tokens = new Array ();
  this.lines = lines;
  this.areas = areas;
  this.itemIndex = itemIndex;
  this.urls = urls;
  this.isUnfoldEnabled = isUnfoldEnabled;
  this.unfoldMinZoom = unfoldMinZoom;

  for (var t = 0; t < tokens.length; ++t)
  {
    var token = tokens [t];
    token.guid = guid;

//    if (token.point.equals (this.point.lat))
//    {
//    }
//    else
//    {
      this.tokens.push (token);
      token.isUnfoldEnabled = this.isUnfoldEnabled;
      token.unfoldMinZoom = this.unfoldMinZoom;
//    }
  }

  this.infoTabs = new Array (this.messages.length + this.linkUrls.length);;
  this.infoTabsOpts = null;

  for (var i = 0; i < this.messages.length; ++i)
  {
    this.infoTabs [i] = new GInfoWindowTab (infoWindowSettings ["tabTitle"] + (i + 1), this.messages [i]);
  }

  for (; i < this.messages.length + this.linkUrls.length; ++i)
  {
    this.infoTabs [i] = new GInfoWindowTab (
        this.linkTitles [i - this.messages.length] ? this.linkTitles [i - this.messages.length] : "untitled",
        "<frameset><frame src=\"" + this.linkUrls [i - this.messages.length] + "\"/></frameset>");
  }

  this.infoTabsOpts = {maxWidth : infoWindowSettings ["maxWidth"]};

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

      if (0 < size)
      {
        //var width = this.iconWidthFactor * size;
        //var height = this.iconHeightFactor * size;

        var icon = new GIcon ();
        icon.image = this.iconFilename;
        icon.shadow = this.iconFilename;
        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 markerOpts = {"icon": icon, "title": this.title};
        var marker = new GMarker (this.point, markerOpts);
        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
            });

        this.addPointListener (marker, this.infoTabs, this.infoTabsOpts);

        for (var token = 0; token < this.tokens.length; ++token)
        {
          this.tokens [token].addListener (marker);
        }

        for (var line = 0; line < this.lines.length; ++line)
        {
          this.lines [line].addListener (marker);
        }

        if (null != this.areas)
        {
          for (var area = 0; area < this.areas.length; ++area)
          {
            this.areas [area].addAreaListener (marker);
          }
        }
      }
    }
  }
}

hbReport.prototype.infoWindowOpenedOfReport = null;

hbReport.prototype.destroy = function ()
{
  this.iconFilename = undefined;
  this.info = undefined;
  this.startTime = undefined;
  this.stopTime = undefined;
  this.guid = undefined;
  this.title = undefined;
  this.rssTitle = undefined;
  this.messages = undefined;
  this.linkUrls = undefined;
  this.linkTitles = undefined;
  this.point = undefined;
  this.itemIndex = undefined;
  this.infoTabs = undefined;
  this.infoTabsOpts = undefined;

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

  this.markers = undefined;

  for (var token = 0; token < this.tokens.length; ++token)
  {
    this.tokens [token].destroy ();
  }

  this.tokens = undefined;

  for (var line = 0; line < this.lines.length; ++line)
  {
    this.lines [line].destroy ();
  }

  this.lines = undefined;

  for (var area = 0; area < this.areas.length; ++area)
  {
    this.areas [area].destroy ();
  }

  this.areas = undefined;
}

hbReport.prototype.getFirstTokenIconFilename = function ()
{
  if (0 == this.tokens.length)
  {
    return "";
  }

  return this.tokens [0].iconFilename;
}

hbReport.prototype.addMarkers = function (markerMgr)
{
  if (this.isUnfoldEnabled)
  {
    var realMaxZoom;

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

hbReport.prototype.addOverlays = function (overlayMgr)
{
  for (var line = 0; line < this.lines.length; ++line)
  {
    this.lines [line].addOverlays (overlayMgr);
  }

  for (var area = 0; area < this.areas.length; ++area)
  {
    this.areas [area].addOverlays (overlayMgr);
  }
}

hbReport.prototype.addPointListener = function (marker, infoTabs, infoTabsOpts)
{
  var myMarker = marker;
  var myInfoTabs = infoTabs;
  var myInfoTabsOpts = infoTabsOpts;
  var myMessages = this.messages;
  var myId = this.guid;
  var myUrls = this.urls;

  if (0 < myInfoTabs.length)
  {
    GEvent.addListener (myMarker, "click", function ()
    {
      hbReport.prototype.infoWindowOpenedOfReport = myId;
      myMarker.openInfoWindowTabsHtml (myInfoTabs, myInfoTabsOpts);

      if (mapFeedback)
      {
        mapFeedback (myId);
      }
      //myMarker.openExtInfoWindow (map.map, "snow_window", myMessages [0], {beakOffset: 3});
    });
  }
  else if (this.urls && (0 == this.tokens.length))
  {
    GEvent.addListener (myMarker, "click", function ()
    {
      window.open (myUrls [0]);
    });
  }
}

hbReport.prototype.hasId = function (reportId)
{
  return reportId == this.guid;
}

hbReport.prototype.getPrimaryLocation = function ()
{
  return this.point;
}

hbReport.prototype.openInfoWindow = function (index)
{
  if ((null != this.infoTabs) && (0 < this.infoTabs.length) && (null != this.infoTabsOpts))
  {
    var zoom = map.getZoom ();

    for (var m = 0; m < this.markers.length; ++m)
    {
      if ((zoom >= this.markers [m] ["minZoom"]) && (zoom <= this.markers [m] ["maxZoom"]))
      {
        this.infoTabsOpts.selectedTab = index;
        var openedIW = map.map.getInfoWindow();

        if ((hbReport.prototype.infoWindowOpenedOfReport == this.guid) && (!openedIW.isHidden()))
        {
          openedIW.selectTab (index);
        }
        else
        {
          this.markers [m] ["marker"].openInfoWindowTabsHtml (this.infoTabs, this.infoTabsOpts);
          //this.markers [m] ["marker"].openExtInfoWindow (map.map, "extInfoWindow_coolBlues", this.messages [0], {beakOffset: 3});
        }

        hbReport.prototype.infoWindowOpenedOfReport = this.guid;
        this.infoTabsOpts.selectedTab = undefined;
        return;
      }
    }
  }
  else
  {
    for (var t = 0; t < this.tokens.length; ++t)
    {
      if ("onclick" == this.tokens [0].visibility)
      {
        var zoom = map.getZoom ();

        for (var m = 0; m < this.markers.length; ++m)
        {
          if ((zoom >= this.markers [m] ["minZoom"]) && (zoom <= this.markers [m] ["maxZoom"]))
          {
            this.tokens [0].onClick (this.markers [m] ["marker"]);
            return;
          }
        }
      }
    }
  }
}

hbReport.prototype.unfoldEnabled = function (yesorno, minZoom, markerMgr)
{
  for (var token = 0; token < this.tokens.length; ++token)
  {
    this.tokens [token].unfoldEnabled (yesorno, minZoom, markerMgr);
  }

  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.addMarkers ([this.markers [markerObj] ["marker"]], realMinZoom, realMaxZoom);
          }
        }
      }
      else if (this.unfoldMinZoom > minZoom)
      {
        for (var markerObj = 0; markerObj < this.markers.length; ++markerObj)
        {
          if (minZoom <= this.markers [markerObj] ["maxZoom"])
          {
            markerMgr.removeMarker (this.markers [markerObj] ["marker"]);

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

          if (minZoom > this.markers [markerObj] ["minZoom"])
          {
            markerMgr.addMarkers ([this.markers [markerObj] ["marker"]], this.markers [markerObj] ["minZoom"], minZoom - 1);
          }
        }
      }
    }
  }
  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.addMarkers ([this.markers [markerObj] ["marker"]], realMinZoom, this.markers [markerObj] ["maxZoom"]);
      }
    }
  }

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