/* Lightmapper/v, version 1.0
 *
 * (C) 2007 Dmitriy Khudorozhkov (mailto:kh_dmitry2001@mail.ru)
 *
 * This software is provided "as-is", without any express or implied warranty.
 * In no event will the author be held liable for any damages arising from the
 * use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 *
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 *
 * 3. This notice may not be removed or altered from any source distribution.
 *
 */

function LightmapperV(imgId,     // id of the IMAGE element the map is bound to,
                      mouseOver, // global onMouseOver handler,
                      mouseOut,  // global onMouseOut handler
                      bindings)  // array of arrays of area/color/handler bindings:
                                 //
                                 // [area_id, color, x_displacement, y_displacement,
                                 //   on_mouse_over, on_mouse_out, linked_area_ids...]
                                 //
                                 // where:
                                 //
                                 // area_id         - id of the area,
                                 //
                                 // color           - colors (in #xxx or #xxxxxx format) that will be used
                                 //                   to highlight the area,
                                 // 
                                 // x_displacement  - horiz. displacement (found experimentally) required for
                                 //                   correct alignment of mouse-over image and the original map,
                                 //
                                 // y_displacement  - vertical displacement,
                                 // 
                                 // on_mouse_over   - onMouseOver handler for current (area_id) area
                                 //                  (replaces the global handler),
                                 //
                                 // on_mouse_out    - onMouseOut handler for current (area_id) area
                                 //                  (replaces the global handler),
                                 //
                                 // linked_area_ids - ids, comma separated, of the linked areas
                                 //                  (=pop up with the current area).
{
  this.imgId = imgId;

  this.mouseOver = mouseOver || null;
  this.mouseOut  = mouseOut  || null;

  this.binds = [];

  for(var i = 0, l = bindings.length; i < l; i++)
  {
    var elem = this.binds[i] = [];
    var ref  = bindings[i];

    elem["area"] = ref[0];
    elem["colr"] = ref[1] || "#0F0";
    elem["xdsp"] = ref[2] || null;
    elem["ydsp"] = ref[3] || null;
    elem["musi"] = ref[4] || null;
    elem["muso"] = ref[5] || null;

    elem["link"] = [];
    for(var j = 6; j < bindings[i].length; j++)
      elem["link"][elem["link"].length] = bindings[i][j];
  }

  this._construct();
}

LightmapperV.prototype = {

  _construct: function()
  {
    var ct  = document.getElementById(this.imgId), _ct_ = ct.parentNode;
    var div = this.canvas = document.createElement("DIV");

    this._reposition(this);

    _ct_.appendChild(div);

    var jvg = new VectorGraphics(div);
    jvg.SetOpacity(0);
    jvg.SetStrokeWidth(0);

    // create floating DIVs:
    for(var i = 0, l = this.binds.length; i < l; i++)
    {
      var elem = this.binds[i], area = document.getElementById(elem["area"]);

      var coords = area.coords.split(","), coords2 = [];

      var m = coords.length;
      while(m)
      {
        coords2[m / 2 - 1] = [+coords[--m - 1] + elem["xdsp"],
                              +coords[--m + 1] + elem["ydsp"]];
      }

      jvg.SetFillColor(elem["colr"]);
      jvg.SetStrokeColor(elem["colr"]);

      var polygon = this.binds[i].overlay = jvg.Polygon(coords2);

      polygon.area   = elem["area"];
      polygon.links  = [];
      polygon.parent = this;

      var mi = elem["musi"] || this.mouseOver;
      var mo = elem["muso"] || this.mouseOut;

      this._setup_event(polygon, "mouseover", this._callLater(this._fade, polygon, 1, 0.5, 0.1, mi));
      this._setup_event(polygon, "mouseout",  this._callLater(this._fade, polygon, 0, 0.5, 0.1, mo));

      if(area.href)
      {
        function go(href) { return function() { window.location.href = href; } }

        this._setup_event(polygon, "mousedown",  go(area.href));
      }
    }

    var bl = this.binds.length;
    for(var i = 0; i < l; i++)
    {
      var obj   = this.binds[i].overlay;
      var links = this.binds[i]["link"], ll = links.length;

      for(var k = 0; k < ll; k++)
      {
        for(var j = 0; j < bl; j++)
        {
          var elem = this.binds[j].overlay;

          if(links[k] == elem["area"])
          {
            obj.links[obj.links.length] = elem;
            break;
          }
        }
      }
    }

    this._setup_event(window, "resize",  this._callLater(this._reposition, this));
  },

  _reposition: function(obj)
  {
    var ct  = document.getElementById(obj.imgId);
    var div = obj.canvas, ds = div.style;

    // Retrieve the position of the target image:
    var initX = 0, initY = 0, ct_ = ct;

    if(ct_.offsetParent)
    {
      while (ct_.offsetParent)
      {
        initX += ct_.offsetLeft;
        initY += ct_.offsetTop;

        ct_ = ct_.offsetParent;
      }
    }
    else if (ct_.x)
    {
      initX += ct_.x;
      initY += ct_.y;
    }

    ds.position = "absolute";
    ds.top = initY, ds.left = initX;
  },

  _setup_event: function(elem, eventType, handler)
  {    
    return (elem.attachEvent ? elem.attachEvent("on" + eventType, handler) :
      ((elem.addEventListener) ? elem.addEventListener(eventType, handler, false) : null));
  },

  _callLater: function(func, param1, param2, param3, param4, param5)
  {
    return function() { func(param1, param2, param3, param4, param5); };
  },

  _getSetOpacity: function(polygon, opacity)
  {
    if(polygon.hasAttributes && polygon.hasAttributes("fill-opacity"))
    {
      if(opacity != undefined)
      {
        polygon.setAttribute("fill-opacity", opacity);
        polygon.setAttribute("stroke-opacity", opacity);

        return opacity;
      }
      else
        return polygon.getAttribute("fill-opacity");
    }
    else if(polygon.tagName.toLowerCase() == "shape")
    {
      var c = polygon.childNodes.length;

      for(var i = 0; i < c; i++)
      {
        var child = polygon.childNodes[i], tag = child.tagName.toLowerCase();

        if(opacity != undefined)
        {
          var f = false, s = false;

          if(tag == "fill")
          {
            child.opacity = opacity;
            f = true;
          }
          if(tag == "stroke")
          {
            child.opacity = opacity;
            s = true;
          }

          if(f && s) return opacity;
        }
        else
          if((tag == "fill") || (tag == "stroke"))
            return child.opacity;
      }
    }

    return 1;
  },

  _fade: function(obj, destOp, rate, delta, callback)
  {
    if(obj.timer) clearTimeout(obj.timer);

    var proto = LightmapperV.prototype;

    var curOp = parseFloat(proto._getSetOpacity(obj));
    var direction = (curOp <= destOp) ? 1 : -1;

    var links = obj.links, bindings = obj.parent.binds;
    var bl = bindings.length, ll = links.length;

    if((destOp > curOp) && (curOp == 0))
    {
      for(var i = 0; i < bl; i++)
      {
        var elem = bindings[i].overlay;

        if(elem != obj)
        {
          for(var j = 0; j < ll; j++)
            if(links[j] == elem)
              break;
			  
          if(j == ll) proto._fade(elem, 0, 0.5, 0.1);
        }
      }
    }

    delta  = Math.min(direction * (destOp - curOp), delta);
    curOp += direction * delta;

    curOp = Math.round(curOp * 10) / 10;

    proto._getSetOpacity(obj, curOp);

    for(var j = 0; j < ll; j++)
      proto._getSetOpacity(links[j], curOp);

    if(curOp != destOp)
      obj.timer = setTimeout(function() { proto._fade(obj, destOp, rate, delta, callback); }, rate);
    else
    {
      if(callback)
        callback(obj.area);
    }
  }
};