

function CursorDivScroll( divId, activeDepth, stepFactor )
{


 this.elemRef = null;

 this.activeDepth = activeDepth;
 this.divX = 0;
 this.divY = 0;
 this.timer = null;
 this.bon = 0x0;
 this.factor = Number( Math.abs( stepFactor || 20 ) );
 this.defaultFactor = this.factor;
 this.accFactor = 0.1;
 this.defaultAcc = this.accFactor; 
 this.pending = false;
 this.haltTimer = null;
 this.readyTimer = null;
 this.readReady = true;
 this.pixCount = 0;
 this.inRegion = false;

 this.init = function(elemId, depth, stepFactor)
 {
  var paramError = false,
      grief =
      [
       { t:!( this.elemRef = this.gebi( elemId ) ), a:'Div "'+elemId+'" not found'},
       { t:isNaN( Number( this.activeDepth ) ), a:'Depth parameter must be a number and not zero' },
       { t:isNaN( this.factor ), a:'Scroll factor parameter must be a number'}
      ];

  for( var i = 0, len = grief.length; i < len && !paramError; i++)
   if( grief[ i ].t )
   {
    paramError = true;
    alert( grief[ i ].a );
   }

  if( !paramError )
  {
 
  if(this.bon|=0xf)
 

   this.activeDepthX = Math.floor( Math.min( this.activeDepth, this.elemRef.offsetWidth/2.5 ) );

   this.activeDepthY = Math.floor( Math.min( this.activeDepth, this.elemRef.offsetHeight/2.5 ) );

   if( document.documentElement )
    this.dataCode = 3;
   else
    if(document.body && typeof document.body.scrollTop!='undefined')
     this.dataCode = 2;
    else
     if( typeof window.pageXOffset!='undefined' )
      this.dataCode = 1;

   this.addToHandler( document, 'onmousemove', (function(inst){ return function(){inst.getMouseData.apply(inst, arguments); }; })( this ) );

   this.addToHandler( this.elemRef, 'onmousedown', this.enclose( function(){ this.factor *= 3; } ) );

   this.addToHandler( this.elemRef, 'onmouseup',  this.enclose( function(){ this.factor = this.defaultFactor; } ) );

   this.dataCode = this.bon ? this.dataCode : 0;
  }
 }

 this.getArea = function()
 {
  this.activeDepthX = Math.floor( Math.min( this.activeDepth, this.elemRef.offsetWidth/2.5 ) );

  this.activeDepthY = Math.floor( Math.min( this.activeDepth, this.elemRef.offsetHeight/2.5 ) );
 }

 this.enclose = function( funcRef )
 {
  var args = (Array.prototype.slice.call(arguments)).slice(1), that = this;

  return function(){ return funcRef.apply(that, args) };
 }

 this.monitor = function()
 {
  var mx = this.x - this.divX,
      my = this.y - this.divY,
      xStep = 0, yStep = 0,
      eHeight = this.elemRef.offsetHeight > this.elemRef.clientHeight ? (this.elemRef.offsetHeight - 16) : this.elemRef.offsetHeight,
      eWidth = this.elemRef.offsetWidth > this.elemRef.clientWidth ? (this.elemRef.offsetWidth - 16) : this.elemRef.offsetWidth,
      xInit = this.elemRef.scrollLeft,
      yInit = this.elemRef.scrollTop;

  if( mx > 0 && mx < eWidth && my > 0 && my < eHeight )
  {
     if( my < this.activeDepthY && my > 0 )
       yStep = -this.factor * (1-(my/this.activeDepthY) );
     else
      if( my > eHeight - this.activeDepthY &&  my < eHeight  )
        yStep = this.factor *  (my - (eHeight - this.activeDepthY)) / this.activeDepthY ;

     if( mx > 0 && mx < this.activeDepthX )
       xStep = -this.factor * ( 1 -(mx/this.activeDepthX) );
     else
      if( mx > eWidth - this.activeDepthX &&  mx < eWidth  )
        xStep = this.factor *  (mx - (eWidth - this.activeDepthX)) / this.activeDepthX ;

     this.inRegion = Boolean( xStep || yStep );

     if( this.inRegion )
     {
       clearTimeout( this.haltTimer );
       clearTimeout( this.readyTimer );

       this.readyTimer = setTimeout( this.enclose( function(){ this.readReady = true } ), 20 );

       if( this.readReady )
       {
        this.readReady = false;
        this.pixCount++;
       }
       else
       {
        this.pixCount = 1;
        this.haltTimer = setTimeout( this.enclose( function(){ this.timer = null; this.monitor(); } ) , 150 );
       }

        if( this.pixCount > 1 || this.repeating )
        {
          if( !this.timer )
          {
           this.elemRef.scrollTop += Math.round( yStep * this.accFactor );
           this.elemRef.scrollLeft += Math.round( xStep * this.accFactor );

           if( this.accFactor < 1 )
            this.accFactor += Math.min( 0.025, 1 - this.accFactor );

           this.repeating = true;

           clearTimeout( this.timer );
           this.timer = setTimeout( this.enclose( function(){ this.timer = null; this.monitor(); } ) , 50 );
          }
        }
     }
     else
      this.reset();
  }
  else
   this.reset();
   
  return false;
 }
 
 this.reset = function()
 {
   this.repeating = false;
   this.pixCount = 0;
   this.accFactor = this.defaultAcc;
 }

 this.getElemPos=function( elem )
 {
  var left = !!elem.offsetLeft ? elem.offsetLeft : 0,
      top = !!elem.offsetTop ? elem.offsetTop : 0,
      theElem = elem;

  while((elem = elem.offsetParent))
  {
   left += elem.offsetLeft ? elem.offsetLeft : 0;
   top += elem.offsetTop ? elem.offsetTop : 0;
  }

  while( theElem.parentNode.nodeName != 'BODY' )
  {
   theElem = theElem.parentNode;

   if( theElem.scrollLeft )
    left -= theElem.scrollLeft;

   if( theElem.scrollTop )
    top -= theElem.scrollTop;
  }

  this.divX = left, this.divY = top;
 }

 this.readScrollData=function(/*2843295374657068656E204368616C6D657273*/)
 {
  switch( this.dataCode )
  {
   case 3 : this.xDisp = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft);
            this.yDisp = Math.max(document.documentElement.scrollTop, document.body.scrollTop);
            break;

   case 2 : this.xDisp = document.body.scrollLeft;
            this.yDisp = document.body.scrollTop;
            break;

   case 1 : this.xDisp = window.pageXOffset; this.yDisp = window.pageYOffset;
  }
 }

 this.getMouseData = function( evt )
 {
    var e = evt || window.event;

    this.readScrollData();

    if( !this.activeDepthX || !this.activeDepthY )
     this.getArea();

    switch( this.dataCode )
    {
     case 3 :

     case 2 : this.x = this.xDisp + e.clientX;
              this.y = this.yDisp + e.clientY;
              break;

     case 1 : this.x = e.pageX;
              this.y = e.pageY;
    }

    this.getElemPos( this.elemRef );

    if( !this.pending )
     this.monitor();
   
    return false;   
 }

 this.gebi = function( id )
 {
  var eRef = document.getElementById( id );

  return ( eRef && eRef.id === id ) ? eRef : null ;
 }

 this.addToHandler=function(obj, evt, func)
 {
  if(obj[evt])
  {
   obj[evt]=function(f,g)
   {
    return function()
    {
     f.apply(this,arguments);
     return g.apply(this,arguments);
    };
   }(func, obj[evt]);
  }
  else
   obj[evt]=func;
 }
 
 this.sf = function( str )
 {
   return unescape(str).replace(/(.)(.*)/, function(a,b,c){return c+b;});
 }
 

 this.init(divId, activeDepth, stepFactor);
}



