var debug = true;

function MapPoint(point, fromRange, toRange) {
  var pos = { x:0, y:0 };
  
  if (point.x <= fromRange.min.x) {
    pos.x = toRange.min.x;
  }
  else if (point.x >= fromRange.max.x) {
    pos.x = toRange.max.x;
  }
  else if (point.x < fromRange.avg.x) {
    if (toRange.avg.x == toRange.min.x) {
      pos.x = toRange.avg.x;
    }
    else {
      pos.x = toRange.min.x + (toRange.avg.x-toRange.min.x)*(point.x-fromRange.min.x)/(fromRange.avg.x-fromRange.min.x);
    }
  }
  else {
    if (toRange.avg.x == toRange.max.x) {
      pos.x = toRange.avg.x;
    }
    else {
      pos.x = toRange.avg.x + (toRange.max.x-toRange.avg.x)*(point.x-fromRange.avg.x)/(fromRange.max.x-fromRange.avg.x);
    }
  }
  
  if (point.y <= fromRange.min.y) {
    pos.y = toRange.min.y;
    pos.ydebug = "Below minimum";
  }
  else if (point.y >= fromRange.max.y) {
    pos.y = toRange.max.y;
    pos.ydebug = "Above maximum";
  }
  else if (point.y < fromRange.avg.y) {
    if (toRange.avg.y == toRange.min.y) {
      pos.y = toRange.avg.y;
      pos.ydebug = "Average (below)";
    }
    else {
      pos.y = toRange.min.y + (toRange.avg.y-toRange.min.y)*(point.y-fromRange.min.y)/(fromRange.avg.y-fromRange.min.y);
      pos.ydebug = "Between min and avg";
    }
  }
  else {
    if (toRange.avg.y == toRange.max.y) {
      pos.y = toRange.avg.y;
      pos.ydebug = "Average (above)";
    }
    else {
      pos.y = toRange.avg.y + (toRange.max.y-toRange.avg.y)*(point.y-fromRange.avg.y)/(fromRange.max.y-fromRange.avg.y);
      pos.ydebug = "Between avg and max";
    }  
  }
  
  return pos;
}

function OffsetPoint(point, offset) {
  return {
    x : (point.x || parseInt(point) || 0) + (offset.x || parseInt(offset) || 0),
    y : (point.y || parseInt(point) || 0) + (offset.y || parseInt(offset) || 0)
  };
}

function Pt(point) {
  return {
    x : point.x || parseInt(point) || 0,
    y : point.y || parseInt(point) || 0
  };
}

function ClampPoint(point, min, max) {
  var minp = Pt(min);
  var maxp = Pt(max);
  var ptp  = Pt(point);
  if (ptp.x < minp.x) { ptp.x = minp.x } else { if (ptp.x > maxp.x) { ptp.x = maxp.x } }
  if (ptp.y < minp.y) { ptp.y = minp.y } else { if (ptp.y > maxp.y) { ptp.y = maxp.y } }
  return ptp;
}

function ElementScroller(attach, hscroll, vscroll, params) {
  this.attach      = attach;
  this.scrolling   = false;
  this.startPos    = null;
  this.startScroll = null;
  this.captured    = false;
  this.movecursor  = "move";
  this.hscroll     = hscroll;
  this.vscroll     = vscroll;

  for (x in params) {
    this[x] = params[x];
  }
  
  this.active      = false;
  
  // Create dummy values if no scrollers
  if (!this.attach) return;
   
  addClassName(this.attach, "scrolling-off");
  
  if (!this.hscroll) {
    this.hscroll = {
      scrollLeft : 0,
      scrollWidth : 100,
      offsetWidth : 50
    }
  }
  if (!this.vscroll) {
    this.vscroll = {
      scrollLeft : 0,
      scrollWidth : 100,
      offsetWidth : 50
    }
  }
  
  this.getScrollInfo = function() {
    var elpos = getElementPos(this.attach);
    
    info = {
    
      scrollrange : {
        min : { x:0, y:0},
        avg : this.startScroll,
        max : ClampPoint({ x:this.hscroll.scrollWidth  - this.hscroll.offsetWidth, y:this.vscroll.scrollHeight - this.vscroll.offsetHeight }, 0, 9999999)
      },
  
      mouserange : {
        min : OffsetPoint(elpos, 32),
        avg : this.startPos,
        max : OffsetPoint({ x:this.attach.offsetWidth, y:this.attach.offsetHeight }, OffsetPoint(elpos, -32))
      },
  
      pagesize : /*ClampPoint(*/{
        x : (this.hscroll.offsetWidth / (this.hscroll.scrollWidth/* - this.hscroll.offsetWidth*/)),
        y : (this.vscroll.offsetHeight / (this.vscroll.scrollHeight/* - this.vscroll.offsetHeight*/))
      }/*, 0, 1)*/,
      
      scrollpos : {
        x : this.hscroll.scrollLeft,
        y : this.vscroll.scrollTop 
      }
      
    };
    
    
    if (info.scrollrange.max.x <= 0) { info.scrollrange.max.x = 0; info.pagesize.x = 1; }
    if (info.scrollrange.max.y <= 0) { info.scrollrange.max.y = 0; info.pagesize.y = 1; }
        
    return info;
    
  }

  this.doMouseDown = function(e) {
    if (!this.active) return;  
    
    var e = eventInfo(e);
    
    this.startPos    = { x:e.pointerX, y:e.pointerY };
    this.startScroll = { x:this.hscroll.scrollLeft, y:this.vscroll.scrollTop };

    removeClassName(this.attach, "scrolling-off");    
    addClassName(this.attach, "scrolling-on");
    //this.attach.style.cursor = this.movecursor;
    
    if (this.onstartscroll) { this.onstartscroll(); }
    
    this.scrolling = true;
  }
  
  this.doMouseUp = function(e) {
    if (!this.active) return;  
    
    var e = eventInfo(e);
    
    this.scrolling = false;
    //this.attach.style.cursor = "";
    removeClassName(this.attach, "scrolling-on");    
    addClassName(this.attach, "scrolling-off");
    
    if ((this.captured) && (this.attach.releaseCapture)) {
      this.captured = false;
      this.attach.releaseCapture();
    }
    
    if (this.onendscroll) { this.onendscroll(); }
    
    if (this.cookiename) {
      var expires = fixDate( new Date() ) + 365 * 24 * 60 * 60 * 1000;
      setCookie(this.cookiename, "{ x : " + this.hscroll.scrollLeft + ", y : " + this.vscroll.scrollTop + "}", expires);
    }
  }
  
  this.doMouseMove = function(e) {
    if (!this.active) return;  
    
    var e = eventInfo(e);
    
    if (!this.scrolling) return;

    if ((this.captured==false) && (this.attach.setCapture)) {
       this.captured=true;
       this.attach.setCapture();
    }
    
    // Scrolling range
    var scrollinfo = this.getScrollInfo();
    
    // Current position
    var mousepos = {
      x : e.pointerX, y : e.pointerY
    };
    
    var scrollpos = MapPoint(mousepos, scrollinfo.mouserange, scrollinfo.scrollrange);
    
    if (this.onscroll) {
      this.onscroll(scrollpos, scrollinfo, mousepos);
    }

    
    this.hscroll.scrollLeft = scrollpos.x;
    this.vscroll.scrollTop  = scrollpos.y;

  }
  
  
  this.begin = function() {
    if (this.active) return;
    addEvent(this.attach, "mousedown", this.doMouseDown.bindAsEventListener(this), false);
    addEvent(this.attach, "mouseup"  , this.doMouseUp.bindAsEventListener(this), false);
    addEvent(this.attach, "mousemove", this.doMouseMove.bindAsEventListener(this), false);
    if (this.attach.setCapture) {
      addEvent(this.attach, "losecapture", this.doMouseUp.bindAsEventListener(this), false);
    }
    
    this.active = true;
  }
  
  this.end = function() {
    if (!this.active) return;    
    this.active = false;
  }
  
  if (false && this.cookiename) {
    var cookie;
    if (cookie = getCookie(this.cookiename)) {
      var scrollpos = eval(cookie);
      
      this.hscroll.scrollLeft = scrollpos.x;
      this.vscroll.scrollTop  = scrollpos.y;
    }

  }
  
  // And begin!
  if (!this.defer) { this.begin(); }
  
}


