/* scrollcontrol */

function scrollControl(outer, inner){
  this.outer = (typeof(outer) == 'object') ? outer : document.getElementById(outer);
  this.inner = (typeof(inner) == 'object') ? inner : document.getElementById(inner);

  this.scroller = new Object();
  this.scroller['up'] = new Array();
  this.scroller['down'] = new Array();
  this.jumper = new Object();
  this.jumper['up'] = new Array();
  this.jumper['down'] = new Array();
  
  this._defaultSpeed = 30;
  this._defaultStep = 5;
  
  this.mousewheel = true;
  this.mousewheelScrollStep = 35;
  
  this.grabberEnabled = false;
  this.grabberLoc = 'grab.cur';
  this.grabMode = 0;
  this.initCursorPos = -1;
  
  this.softScrolling = false;
  this.softFactor = 5;
  this.softCounter = 0;
  this.actSpeed;
  
  this.actPos;
  this.actDirection;
  this.timeOut = null;
  
  this.marginMode = false;
}

scrollControl.prototype._destruct = function(){
    for(var i in this)
        delete this[i];
    delete this;
}

scrollControl.prototype.init = function(offset){
    if(!offset)
        offset = 0;
    this.actPos = -offset;
    
    this.outer.style.overflow = "hidden";
    this.inner.style.position = "relative";
    this.inner.style.top = this.actPos+"px";
	if(this.marginMode)
		this.inner.style.marginTop = this.inner.style.top;
    this.inner.style.left = "0px";
    
    var _parent = this;
    
    for(var i in this.scroller){
        for(var k=0 ; k<this.scroller[i].length ; k++){
            var actObj = this.scroller[i][k];
                        
            if(this.getDiffHeight() < 0)
                actObj.style.visibility = 'hidden';
            else{
                if(actObj.scrollStartEvent == actObj.scrollStopEvent){
                    actObj["on"+actObj.scrollStartEvent] = function(){ _parent.toggleScrolling(this); };
                }else{					
                    actObj["on"+actObj.scrollStartEvent] = function(){ _parent.startScrolling(this, true); };
                    actObj["on"+actObj.scrollStopEvent] = function(){ _parent.stopScrolling(); };
                }
            }
        }
    }
    
    for(var i in this.jumper){
        for(var k=0 ; k<this.jumper[i].length ; k++){
            var actObj = this.jumper[i][k];
            actObj["on"+actObj.jumpEvent] = function(){ _parent.jump(this); };
        }
    }
	
	if(this.mousewheel && this.scroller['down'].length != 0 && this.scroller['up'].length != 0){
		if (this.outer.addEventListener)
			this.outer.addEventListener('DOMMouseScroll', function(e){ _parent.scrollPerWheel(e, _parent); }, false);
		this.outer.onmousewheel = function(e){ _parent.scrollPerWheel(e, _parent); };
	}
}

scrollControl.prototype.activateGrabbing = function(e){
    e = e || window.event;
    if (e.preventDefault) e.preventDefault();
    this.grabMode = 1;
}

scrollControl.prototype.deactivateGrabbing = function(){
    this.grabMode = 0;
    this.initCursorPos = -1;
    this.actPos = this.getInnerPosition();
}

scrollControl.prototype.grabScrolling = function(e){
    if(this.grabMode != 1) return;

    e = e || window.event;
    var cursory = 0;
    if (e.pageY) {
        cursory = e.pageY;
    } 
    else {
        var de = document.documentElement;
        var b = document.body;
        cursory = e.clientY + 
            (de.scrollTop || b.scrollTop) - (de.clientTop || 0);
    }
    if(this.initCursorPos == -1)
        this.initCursorPos = Math.abs(cursory - this.actPos);
    else{
        var curDiff = this.initCursorPos - cursory;
        //cross top
        if(curDiff < 0)
            curDiff = 0;
        else if(curDiff > this.getDiffHeight())
            curDiff = this.getDiffHeight();
        this.inner.style.top = ((curDiff > 0) ? -curDiff : curDiff)+"px";
		
		if(this.marginMode)
			this.inner.style.marginTop = this.inner.style.top;
    }    
}

scrollControl.prototype.scrollUp = function(){
	this.scrollOneStep("up");
}

scrollControl.prototype.scrollDown = function(){
	this.scrollOneStep("down");
}

scrollControl.prototype.scrollOneStep = function(direction){	
	var diff = this.getDiffHeight();
	
	if(diff < 0)
		return;
		
    this.actPos = this.getInnerPosition();
	
	if(direction == 'down' && (this.actPos <= -diff || (Math.abs(diff) - Math.abs(this.actPos) < this.mousewheelScrollStep))){
		this.actPos = -diff;
		this.inner.style.top = -diff+"px";
		if(this.marginMode)
			this.inner.style.marginTop = this.inner.style.top;
		return;
	}else if(direction == 'up' && (this.actPos >= 0 || (this.actPos > (this.mousewheelScrollStep * -1)))){
		this.actPos = 0;
		this.inner.style.top = "0px";
		if(this.marginMode)
			this.inner.style.marginTop = this.inner.style.top;
        return;
    }
	
	if(direction == 'down'){
        this.inner.style.top = (this.actPos - this.mousewheelScrollStep)+'px';
    }else{
        this.inner.style.top = (this.actPos + this.mousewheelScrollStep)+'px';
    }
	if(this.marginMode)
		this.inner.style.marginTop = this.inner.style.top;
}

scrollControl.prototype.toggleScrolling = function(callerObj){
    if(this.timeOut){
        this.stopScrolling();
        if(this.actDirection != callerObj.scrollDirection)
            this.startScrolling(callerObj, true);
    }else
        this.startScrolling(callerObj, true);
}

scrollControl.prototype.startScrolling = function(callerObj, initCall){
    this.actDirection = callerObj.scrollDirection;
	
    var now = new Date();
	
    if(initCall){
        this.initTime = now.getTime();
        this.actStep = callerObj.scrollStep;
    }
    
    var diff = this.getDiffHeight();
    this.actPos = this.getInnerPosition();
    
    //alert(diff);
    
    if((this.actDirection == 'down' && this.actPos <= -diff) ||
       (this.actDirection == 'up' && this.actPos >= 0)){
        this.stopScrolling();
        return;
    }
    
    if(callerObj.scrollDirection == 'down'){
        this.inner.style.top = (this.actPos - this.actStep)+'px';
    }else{
        this.inner.style.top = (this.actPos + this.actStep)+'px';
    }
	
	if(this.marginMode)
		this.inner.style.marginTop = this.inner.style.top;
		
    this.actPos = this.getInnerPosition();
    
    var _parent = this;
    
    //default: static speed parameter
    scrollSpeed = callerObj.scrollSpeed;
    
    if(callerObj.scrollTimeGradient == true){
        if((now.getTime()-this.initTime) >= callerObj.scrollTimeGradientOffset){
            if(this.gradientStart == null) this.gradientStart = Math.abs(this.actPos);
            
            if(callerObj.scrollTimeGradientSpeedDiff < 0){
                scrollSpeed = scrollSpeed + Math.abs((Math.abs(this.actPos) - this.gradientStart)) * callerObj.scrollTimeGradientStepWidth;
                if(scrollSpeed >= callerObj.scrollTimeGradientEndSpeed)
                    scrollSpeed = callerObj.scrollTimeGradientEndSpeed;
            }else{
                scrollSpeed = scrollSpeed - Math.abs((Math.abs(this.actPos) - this.gradientStart)) * callerObj.scrollTimeGradientStepWidth;
                if(scrollSpeed <= callerObj.scrollTimeGradientEndSpeed)
                    scrollSpeed = callerObj.scrollTimeGradientEndSpeed;
            }
        }
    }
    
    //ensuring min-speed from 1ms
    scrollSpeed = (scrollSpeed < 1) ? 1 : scrollSpeed;
    this.actSpeed = Math.ceil(this.actStep * (1000 / scrollSpeed));
    
    this.timeOut = setTimeout(function(){ _parent.startScrolling(callerObj);}, scrollSpeed);
}

scrollControl.prototype.scrollOut = function(){
    var diff = this.getDiffHeight();
    this.actPos = this.getInnerPosition();
    
    if((this.actDirection == 'down' && this.actPos <= -diff) ||
       (this.actDirection == 'up' && this.actPos >= 0)){
        this.stopScrolling(true);
        return;
    }
        
    if(this.actDirection == 'down')
        this.inner.style.top = (this.actPos - this.actStep)+"px";
    else
        this.inner.style.top = (this.actPos + this.actStep)+"px";
		
	if(this.marginMode)
		this.inner.style.marginTop = this.inner.style.top;
		
    this.actPos = this.getInnerPosition();

    var _divisor = this.actSpeed - ((this.actSpeed / this.softFactor) * this.softCounter);
    //alert(_divisor);
    if(_divisor <= 0 || _actSpeed > 250){
        this.stopScrolling(true);
        return;
    }
    this.softCounter++;

    var _actSpeed = Math.ceil((this.actStep * 1000) / _divisor);
    if(_actSpeed > 250){
        this.stopScrolling(true);
        return;
    }
    //alert(_divisor +' - '+ _actSpeed);
    
    var _parent = this;
    //alert(_actSpeed);
    this.timeOut = setTimeout(function(){ _parent.scrollOut();}, _actSpeed);
}

scrollControl.prototype.stopScrolling = function(ignoreSoft){
    if(this.timeOut){
        clearTimeout(this.timeOut);
        this.timeOut = null;
    }
    
    if(this.gradientStart)
        this.gradientStart = null;
        
    if(this.softScrolling == true && ignoreSoft == null){
        this.scrollOut();
        return;
    }
    this.actSpeed = null;
    this.softCounter = 0;
}

scrollControl.prototype.jump = function(callerObj){
    var diff = this.getDiffHeight();
    if(callerObj.scrollDirection == 'down')
        this.actPos = -(Math.abs(diff));
    else
        this.actPos = 0;
        
    this.inner.style.top = this.actPos+"px";
	if(this.marginMode)
		this.inner.style.marginTop = this.inner.style.top;
    this.stopScrolling();
}

scrollControl.prototype.getInnerPosition = function(){
    return parseInt(this.inner.style.top);
}

scrollControl.prototype.getInnerHeight = function(){
    return parseInt(this.inner.offsetHeight);
}

scrollControl.prototype.getOuterHeight = function(){
    return parseInt(this.outer.offsetHeight);
}

scrollControl.prototype.getDiffHeight = function(){
    return this.getInnerHeight() - this.getOuterHeight() + 4;
}

scrollControl.prototype.getCurrentPosition = function(){
    return this.actPos;
}

scrollControl.prototype.bindScroller = function(obj, direction, enterevent, leaveevent, speed, pixelstep, faster){
    var htmlObj = (typeof(obj) == 'object') ? obj : document.getElementById(obj);
    htmlObj.scrollDirection = direction;
    htmlObj.scrollSpeed = (!speed || speed=='') ? this._defaultSpeed : speed;
    htmlObj.scrollStep = (!pixelstep || pixelstep=='') ? this._defaultStep : pixelstep;
    htmlObj.scrollStartEvent = enterevent;
    htmlObj.scrollStopEvent = leaveevent;
    this.scroller[direction].push(htmlObj);
}

scrollControl.prototype.bindJumper = function(obj, direction, jumpevent){
    var htmlObj = (typeof(obj) == 'object') ? obj : document.getElementById(obj);
    htmlObj.jumpDirection = direction;
    htmlObj.jumpEvent = jumpevent;
    this.jumper[direction].push(htmlObj);
}

scrollControl.prototype.enableGrabber = function(location){
    this.grabberEnabled = true;
    if(location != null)
        this.grabberLoc = location;
    var instance = this;
    this.inner.style.cursor = "url("+this.grabberLoc+"), auto";
    this.inner.onmousedown = function(e){ instance.activateGrabbing(e); };
    this.inner.onmouseup = function(){ instance.deactivateGrabbing(); };
    this.inner.onmousemove = function(e){ instance.grabScrolling(e); };
}

scrollControl.prototype.disableGrabber = function(){
    this.grabberEnabled = false;
    this.inner.onmousedown = function(){};
    this.inner.onmouseup = function(){};
    this.inner.onmousemove = function(){};
    this.inner.style.cursor = "auto";
}

scrollControl.prototype.toggleGrabber = function(){
    if(this.grabberEnabled == true)
        this.disableGrabber();
    else
        this.enableGrabber();
}

scrollControl.prototype.enableMarginMode = function(){
    this.marginMode = true;
}

scrollControl.prototype.disableMarginMode = function(){
    this.marginMode = false;
}

scrollControl.prototype.toggleMarginMode = function(){
    this.marginMode = (this.marginMode == true) ? false : true;
}

scrollControl.prototype.enableSoftScrolling = function(factor){
    this.softScrolling = true;
    if(factor != null)
        this.softFactor = parseInt(factor);
}

scrollControl.prototype.disableSoftScrolling = function(){
    this.softScrolling = false;
}

scrollControl.prototype.toggleSoftScrolling = function(){
    this.softScrolling = (this.softScrolling == true) ? false : true;
}

scrollControl.prototype.setMousewheelScrollStep = function(step){
	this.mousewheelScrollStep = step;
}

scrollControl.prototype.enableMousewheelSupport = function(step){
    this.mousewheel = true;
	
	if(step)
		this.mousewheelScrollStep = step;
}

scrollControl.prototype.disableMousewheelSupport = function(){
    this.mousewheel = false;
}

scrollControl.prototype.toggleMousewheelSupport = function(){
    this.mousewheel = (this.mousewheel == true) ? false : true;
}

scrollControl.prototype.setScrollGradientByPosition = function(obj, endspeed, startoffset, endoffset){
    var htmlObj = (typeof(obj) == 'object') ? obj : document.getElementById(obj);
    if(!htmlObj.scrollSpeed){
        alert("First bind the scroller, then modify scroll gradient!");
        return;
    }
    
    htmlObj.scrollPosGradient = true;
    htmlObj.scrollPosGradientEndSpeed = 1;
    htmlObj.scrollPosGradientStartOffset = 0.0;
    htmlObj.scrollPosGradientEndOffset = 1.0;
    
    if(endspeed && endspeed != '')
        htmlObj.scrollPosGradientEndSpeed = endspeed;
    if(startoffset && startoffset != '')
        htmlObj.scrollPosGradientStartOffset = startoffset;
    if(endoffset && endoffset != '')
        htmlObj.scrollPosGradientEndOffset = endoffset;
    
    if(htmlObj.scrollPosGradientStartOffset > htmlObj.scrollPosGradientEndOffset){
        var tmp = htmlObj.scrollPosGradientStartOffset;
        htmlObj.scrollPosGradientStartOffset = htmlObj.scrollPosGradientEndOffset;
        htmlObj.scrollPosGradientEndOffset = tmp;
    }
        
    htmlObj.scrollPosGradientFactor = ((htmlObj.scrollSpeed - htmlObj.scrollPosGradientEndSpeed) /
                                   (this.getInnerHeight() * (htmlObj.scrollPosGradientEndOffset - htmlObj.scrollPosGradientStartOffset))).toFixed(2);
}

scrollControl.prototype.setScrollGradientByTime = function(obj, timeoffset, stepwidth, endspeed){
    var htmlObj = (typeof(obj) == 'object') ? obj : document.getElementById(obj);
    if(!htmlObj.scrollSpeed){
        alert("First bind the scroller, then modify scroll gradient!");
        return;
    }
    
    htmlObj.scrollTimeGradient = true;
    htmlObj.scrollTimeGradientOffset = 3;
    htmlObj.scrollTimeGradientEndSpeed = 1;
    htmlObj.scrollTimeGradientStepWidth = htmlObj.scrollSpeed - htmlObj.scrollTimeGradientEndSpeed;
    
    if(timeoffset && timeoffset != '')
        htmlObj.scrollTimeGradientOffset = timeoffset;
    if(stepwidth && stepwidth != '' && stepwidth != 0)
        htmlObj.scrollTimeGradientStepWidth = Math.abs(parseInt(stepwidth));
    if(endspeed && endspeed != '')
        htmlObj.scrollTimeGradientEndSpeed = endspeed;

    htmlObj.scrollTimeGradientSpeedDiff = htmlObj.scrollSpeed - htmlObj.scrollTimeGradientEndSpeed;
}

scrollControl.prototype.scrollPerWheel = function(event, _parent){
	var delta = 0;
	
	if (!event)
		event = window.event;
	if (event.wheelDelta) { /* IE/Opera. */
		delta = event.wheelDelta/120;
	} else if (event.detail) { /** Mozilla case. */
		delta = -event.detail/3;
	}

	if (event.preventDefault)
		event.preventDefault();
	event.returnValue = false;	
	
	if (delta){
		if(delta > 0)
			_parent.scrollUp();
		else
			_parent.scrollDown();
	}
		
	else return null;
}


