/**************************************************
 * dom-drag.js
 * 09.25.2001
 * www.youngpup.net
 **************************************************
 * 10.28.2001 - fixed minor bug where events
 * sometimes fired off the handle, not the root.
 **************************************************/

var Drag = Class.create();
Drag.prototype = {
    //obj : null,
    //objects: null,
    
    initialize : function(drag_object, grab_object, oRoot, minX, maxX, minY, maxY,
                           bSwapHorzRef, bSwapVertRef, fXMapper, fYMapper)
    {
      this.drag_object = drag_object;
      this.callbacks = $H({});
      
      if (grab_object) {
        grab_object.onmousedown = this.start.bind(this);
      }
      else
      {
        drag_object.onmousedown    = this.start.bind(this);
      }

      var o = drag_object;
      
      o.hmode            = bSwapHorzRef ? false : true ;
      o.vmode            = bSwapVertRef ? false : true ;

      o.root = oRoot && oRoot != null ? oRoot : o ;

      if (o.hmode  && isNaN(parseInt(o.root.style.left  ))) o.root.style.left   = "0px";
      if (o.vmode  && isNaN(parseInt(o.root.style.top   ))) o.root.style.top    = "0px";
      if (!o.hmode && isNaN(parseInt(o.root.style.right ))) o.root.style.right  = "0px";
      if (!o.vmode && isNaN(parseInt(o.root.style.bottom))) o.root.style.bottom = "0px";

      o.minX    = typeof minX != 'undefined' ? minX : null;
      o.minY    = typeof minY != 'undefined' ? minY : null;
      o.maxX    = typeof maxX != 'undefined' ? maxX : null;
      o.maxY    = typeof maxY != 'undefined' ? maxY : null;

      o.xMapper = fXMapper ? fXMapper : null;
      o.yMapper = fYMapper ? fYMapper : null;

      o.root.onDragStart    = new Function();
      o.root.onDragEnd    = new Function();
      o.root.onDrag        = new Function();
      o = null;
    },

    start : function(e)
    {
      var o = this.drag_object;
      
      e = this.fixE(e);
      
      //We shan't allow this event to bubble. We want the nearest elem to drag:
      Event.stop(e);

      var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom);
      var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
      o.root.onDragStart(x, y);

      o.lastMouseX    = e.clientX;
      o.lastMouseY    = e.clientY;

      if (o.hmode) {
          if (o.minX != null)    o.minMouseX    = e.clientX - x + o.minX;
          if (o.maxX != null)    o.maxMouseX    = o.minMouseX + o.maxX - o.minX;
      } else {
          if (o.minX != null) o.maxMouseX = -o.minX + e.clientX + x;
          if (o.maxX != null) o.minMouseX = -o.maxX + e.clientX + x;
      }

      if (o.vmode) {
          if (o.minY != null)    o.minMouseY    = e.clientY - y + o.minY;
          if (o.maxY != null)    o.maxMouseY    = o.minMouseY + o.maxY - o.minY;
      } else {
          if (o.minY != null) o.maxMouseY = -o.minY + e.clientY + y;
          if (o.maxY != null) o.minMouseY = -o.maxY + e.clientY + y;
      }

      document.onmousemove = this.drag.bind(this);
      document.onmouseup   = this.end.bind(this);
      o=null;

      if (this.callbacks.get('mousedown') != null)
      {
        this.callbacks.get('mousedown')(e);
      }
      
      return false;
    },

    drag : function(e)
    {
      
      if (typeof e == 'undefined') e = window.event;
      if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
      if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;
      //var myself = this; // looking for mem leak
      //e = this.fixE(e);
      //e = myself.fixE(e);
      var o = this.drag_object;

      var ey    = e.clientY;
      var ex    = e.clientX;
      var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom);
      var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
      var nx, ny;

      if (o.minX != null) ex = o.hmode ? Math.max(ex, o.minMouseX) : Math.min(ex, o.maxMouseX);
      if (o.maxX != null) ex = o.hmode ? Math.min(ex, o.maxMouseX) : Math.max(ex, o.minMouseX);
      if (o.minY != null) ey = o.vmode ? Math.max(ey, o.minMouseY) : Math.min(ey, o.maxMouseY);
      if (o.maxY != null) ey = o.vmode ? Math.min(ey, o.maxMouseY) : Math.max(ey, o.minMouseY);

      nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
      ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));

      if (o.xMapper)        nx = o.xMapper(x)
      else if (o.yMapper)    ny = o.yMapper(y)

      o.root.style[o.hmode ? "left" : "right"] = nx + "px";
      o.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
      o.lastMouseX    = ex;
      o.lastMouseY    = ey;

      o.root.onDrag(nx, ny);
      o = null; // is this nec to eliminate mem leak?
      
      if (this.callbacks.get('mousemove') != null)
      {
        this.callbacks.get('mousemove')(e);
      }
      
      return false;
    },

    end : function(e)
    {
      e = this.fixE(e);
      
      document.onmousemove = null;
      document.onmouseup   = null;
      
      var o = this.drag_object;
      o.root.onDragEnd(parseInt(o.root.style[o.hmode ? "left" : "right"]), 
                        parseInt(o.root.style[o.vmode ? "top" : "bottom"]));
      
      if (this.callbacks.get('mouseup') != null)
      {
        this.callbacks.get('mouseup')(e);
      }
    },

    fixE : function(e)
    {
        if (typeof e == 'undefined') e = window.event;
        if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
        if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;
        return e;
    },
    
    // this method accepts a hash of callback funcs for:
    // 'mousedown', 'mousemove', 'mouseup', etc
    setCallbacks: function(funcHash)
    {
      this.callbacks = $H(funcHash);
    }
};
