MousePositionOnPage = {
 getX: function (mouseEvent) {
  return mouseEvent.pageX || mouseEvent.clientX+DHTMLApi.Browser.getScrollX();
 },
 getY: function (mouseEvent) {
  return mouseEvent.pageY || mouseEvent.clientY+DHTMLApi.Browser.getScrollY();
 }
};
 
function implementsInterface() {
 var prototypeObj=new Object();
 for (var i=0; i<arguments.length; i++) {
  for (var method in arguments[i]) {
   prototypeObj[method]=arguments[i][method];
  } 
 }
 return prototypeObj;
}
 
//////////////////////////
// Interface Observable //
//////////////////////////
 
Observable = {
 addListener : function(listenerObj) {
  if (typeof this.listeners == 'undefined') {
   this.listeners=new Array();
  }
  for (var i=0; i<this.listeners.length; i++) {
   if (this.listeners[i]==listenerObj) return;
  }
  this.listeners[this.listeners.length]=listenerObj;
 },
 
 removeListener : function(listenerObj) {
  for (var i=0; i<this.listeners.length; i++) {
   if (this.listeners[i]==listenerObj) {
    this.listeners.splice(i,1);
    return;
   }
  }
 },
 
 notifyListeners : function (eventName,eventObj) {
  if (typeof this.listeners == 'undefined') return;
  for (var i=0; i< this.listeners.length; i++) {
   if (typeof this.listeners[i][eventName]!="undefined") {
    this.listeners[i][eventName](eventObj);
   }
  }
 }
}
 
/////////////////////////////////////////////
// Class Draggable (implements Observable) //
/////////////////////////////////////////////
 
/************************************
 listenerObj must implement methods: 
 onDragStop(DraggableEventObj event)
 onDrag(DraggableEventObj event)
************************************/
 
DraggableMode= {HORIZONTAL : 1,VERTICAL : 2,HORIZONTAL_VERTICAL : 3};
 
function Draggable(dragObj,dragHandleObj,boundingObj,dragMode) {
 this.dragObj=dragObj;
 if (dragHandleObj==null) {
  this.dragHandle=this.dragObj;
 } else {
  this.dragHandle=dragHandleObj;
 }
 this.mode=dragMode;
 this.boundingObj = boundingObj;   
 this.boundingBox = this.getBoundingBox();
 
 this.mousePosXOnHandle=null;
 this.mousePosYOnHandle=null;
 
 this.mouseDownHandler=null;
 this.mouseUpHandler=null;
 this.mouseMoveHandler=null;
};
 
Draggable.prototype = implementsInterface(Observable);
 
Draggable.prototype.set=function() {
 var obj=this;
 
 function dragStart(e) {
  obj.notifyListeners("onDragStart",obj.getDraggableEventObj());
  obj.boundingBox = obj.getBoundingBox();
  obj.mousePosXOnHandle=MousePositionOnPage.getX(e)-DHTMLApi.Position.getXPosOnPage(obj.dragObj);
  obj.mousePosYOnHandle=MousePositionOnPage.getY(e)-DHTMLApi.Position.getYPosOnPage(obj.dragObj);
  obj.mouseMoveHandler=DOMEvent.addDomListener(document, "mousemove" , drag);
  obj.mouseUpHandler=DOMEvent.addDomListener(document, "mouseup" , dragEnd);
  DOMEvent.preventDefault(e);
 }
 
 function dragEnd(e) {
  DOMEvent.removeListener(obj.mouseMoveHandler);
  DOMEvent.removeListener(obj.mouseUpHandler);
  obj.notifyListeners("onDragStop",obj.getDraggableEventObj());
 }
 
 function drag(e) {
  var dragObjPosX,dragObjPosY;
  DOMEvent.preventDefault(e);
  dragObjPosX=obj.calculatePosX(MousePositionOnPage.getX(e));
  dragObjPosY=obj.calculatePosY(MousePositionOnPage.getY(e));
  if (obj.mode!=DraggableMode.VERTICAL) {
   if (obj.boundingBox.tlX<=dragObjPosX && dragObjPosX<=obj.boundingBox.brX) {
    DHTMLApi.Position.setXPos(obj.dragObj, dragObjPosX-obj.boundingBox.tlX, obj.boundingObj);
   } else if (obj.boundingBox.tlX>dragObjPosX) {
    DHTMLApi.Position.setXPos(obj.dragObj, 0, obj.boundingObj);
   } else {
    DHTMLApi.Position.setXPos(obj.dragObj, obj.boundingBox.brX-obj.boundingBox.tlX, obj.boundingObj);
   }
  }  
  if (obj.mode!=DraggableMode.HORIZONTAL) {
   if (obj.boundingBox.tlY<=dragObjPosY && dragObjPosY<=obj.boundingBox.brY) {
    DHTMLApi.Position.setYPos(obj.dragObj, dragObjPosY-obj.boundingBox.tlY, obj.boundingObj);
   } else if (obj.boundingBox.tlY>dragObjPosY) {
    DHTMLApi.Position.setYPos(obj.dragObj, 0, obj.boundingObj);
   } else {
    DHTMLApi.Position.setYPos(obj.dragObj, obj.boundingBox.brY-obj.boundingBox.tlY, obj.boundingObj);
   }
  }
  obj.notifyListeners("onDrag",obj.getDraggableEventObj());
 }
 
 this.mouseDownHandler=DOMEvent.addDomListener(this.dragHandle, "mousedown" , dragStart);
};
 
Draggable.prototype.unset=function() {
 DOMEvent.removeListener(this.mouseDownHandler);
 DOMEvent.removeListener(this.mouseMoveHandler);
 DOMEvent.removeListener(this.mouseUpHandler); 
};
 
Draggable.prototype.getXBounds=function() {
 return (this.boundingBox.brX-this.boundingBox.tlX);
}
 
Draggable.prototype.getYBounds=function() {
 return (this.boundingBox.brY-this.boundingBox.tlY);
}
 
Draggable.prototype.setDragObjXPos=function(xPos) {
 DHTMLApi.Position.setXPos(this.dragObj, xPos, this.boundingObj);
}
 
Draggable.prototype.setDragObjYPos=function(yPos) {
 DHTMLApi.Position.setYPos(this.dragObj, yPos, this.boundingObj);
}
 
Draggable.prototype.getDragObjXPos=function() {
 return  DHTMLApi.Position.getXPosOnPage(this.dragObj)-DHTMLApi.Position.getXPosOnPage(this.boundingObj);
}
 
Draggable.prototype.getDragObjYPos=function() {
 return  DHTMLApi.Position.getYPosOnPage(this.dragObj)-DHTMLApi.Position.getYPosOnPage(this.boundingObj);
}
 
Draggable.prototype.refreshAfterResize=function() {
 this.boundingBox = this.getBoundingBox();
}
 
// private
 
Draggable.prototype.calculatePosX=function (mousePosX) {
 return mousePosX-this.mousePosXOnHandle;
};
 
Draggable.prototype.calculatePosY=function (mousePosY) {
 return mousePosY-this.mousePosYOnHandle;
};
 
Draggable.prototype.getBoundingBox= function () {
 return {
  tlX: DHTMLApi.Position.getXPosOnPage(this.boundingObj),
  tlY: DHTMLApi.Position.getYPosOnPage(this.boundingObj),
  brX: DHTMLApi.Position.getXPosOnPage(this.boundingObj)+DHTMLApi.Size.getElementWidth(this.boundingObj)-DHTMLApi.Size.getElementWidth(this.dragObj),
  brY: DHTMLApi.Position.getYPosOnPage(this.boundingObj)+DHTMLApi.Size.getElementHeight(this.boundingObj)-DHTMLApi.Size.getElementHeight(this.dragObj)};
}
 
Draggable.prototype.getDraggableEventObj=function() {
 return new DraggableEventObj(DHTMLApi.Position.getXPosInElement(this.dragObj,this.boundingObj), DHTMLApi.Position.getYPosInElement(this.dragObj,this.boundingObj), this.boundingBox.brX-this.boundingBox.tlX, this.boundingBox.brY-this.boundingBox.tlY);
}
 
/////////////////////////////
// Class DraggableEventObj //
/////////////////////////////
 
function DraggableEventObj (objPosX, objPosY, boundWidth, boundHeight) {
 this.posX=objPosX;
 this.posY=objPosY;
 this.boundWidth=boundWidth;
 this.boundHeight=boundHeight;
}
 
///////////////////
// Class VSlider //
///////////////////
 
/************************************
 listenerObj must implement methods: 
 onChange(ChangeEventObj event)
 onUpperSliderBarClick(ChangeEventObj event)
 onLowerSliderBarClick(ChangeEventObj event)
************************************/
 
function VSlider(dragObj,boundingObj,topValue,bottomValue,initValue) {
 this.topValue=topValue;
 this.bottomValue=bottomValue;
 this.sliderBar=boundingObj;
 this.sliderBarClickHandler=null;
 this.draggableObject=new Draggable(dragObj,null,boundingObj,DraggableMode.VERTICAL);
 this.draggableObject.set();
 this.draggableObject.addListener(this);
 if (typeof initValue!= 'undefined') {
  this.setValue(initValue);
 }
 this.initSliderBar();
}
 
VSlider.prototype = implementsInterface(Observable);
 
VSlider.prototype.initSliderBar=function() {
 var obj=this;
 this.sliderBarClickHandler=DOMEvent.addDomListener(this.sliderBar, "click" , function (e) {
  var clickPosY=MousePositionOnPage.getY(e)-DHTMLApi.Position.getYPosOnPage(obj.sliderBar);
  if (clickPosY<obj.draggableObject.getDragObjYPos()) {
   obj.notifyListeners("onUpperSliderBarClick",new SliderEventObj(obj.getValue(obj.draggableObject.getDraggableEventObj())));
  }
  if (clickPosY>obj.draggableObject.getDragObjYPos()+DHTMLApi.Size.getElementHeight(obj.draggableObject.dragObj)) {
   obj.notifyListeners("onLowerSliderBarClick",new SliderEventObj(obj.getValue(obj.draggableObject.getDraggableEventObj())));
  }
 });
}
 
VSlider.prototype.getValue=function(draggableEventObj) {
 return this.topValue+(draggableEventObj.posY/draggableEventObj.boundHeight)*1.0*(this.bottomValue-this.topValue);
}
 
VSlider.prototype.setBoundaryValues=function(topValue,bottomValue) {
 this.topValue=topValue;
 this.bottomValue=bottomValue;
}
 
VSlider.prototype.setValue=function(value) {
 var dragObjYPos=Math.round((value-this.topValue)*this.draggableObject.getYBounds()/(this.bottomValue-this.topValue));
 this.draggableObject.setDragObjYPos(dragObjYPos);
}
 
VSlider.prototype.onDragStop = function (draggableEventObj) {
 this.notifyListeners("onChange",new SliderEventObj(this.getValue(draggableEventObj)));
}
 
VSlider.prototype.onDrag = function (draggableEventObj) {
 this.notifyListeners("onChange",new SliderEventObj(this.getValue(draggableEventObj)));
}
 
function SliderEventObj (value) {
 this.value=value;
}
 
///////////////////
// Class VScroll //
///////////////////

function VScroll(containerElement,contentElement,sliderbarElement,sliderElement,viewport,scrollIncrement) {
	this.containerElement=containerElement;
	this.contentElement=contentElement;
	this.sliderbarElement=sliderbarElement;
	this.sliderElement=sliderElement;
	this.viewportWidth=viewport.width;
	this.viewportHeight=viewport.height;
	this.viewportPosX=viewport.x;
	this.viewportPosY=viewport.y;
	this.bottomViewportPadding=DHTMLApi.Size.getElementHeight(this.containerElement)-this.viewportHeight-this.viewportPosY;
	this.rightViewportPadding=DHTMLApi.Size.getElementWidth(this.containerElement)-this.viewportWidth-this.viewportPosX;
	this.invisibleContentHeight=null;
	this.scrollIncrement=scrollIncrement;
	this.slider=null;
	DHTMLApi.CSS.setProperties(this.sliderbarElement,{height: this.viewportHeight+"px"});	
	this.mask=this.buildMask();
	this.animation=new Animation.SmoothVMove(this.contentElement, this.mask);
	this.initSlider();
	if (this.viewportHeight>DHTMLApi.Size.getElementHeight(this.contentElement)) {
		this.hideControls();
		this.controlsHidden=true;
	} else {
		this.controlsHidden=false;
	}
}

VScroll.prototype.buildMask=function() {
	var maskDiv=document.createElement("div");
	DHTMLApi.CSS.setProperties(maskDiv, {position: "absolute", left: this.viewportPosX+"px", top: this.viewportPosY+"px", width: this.viewportWidth+"px", height: this.viewportHeight+"px", overflow:'hidden'});
	this.contentElement=this.containerElement.replaceChild(maskDiv,this.contentElement);
	maskDiv.appendChild(this.contentElement);
	return maskDiv;
}

VScroll.prototype.initSlider=function() {
	var obj=this;
	this.invisibleContentHeight=DHTMLApi.Size.getElementHeight(this.contentElement)-this.viewportHeight;
	this.slider=new VSlider(this.sliderElement,this.sliderbarElement,0,-this.invisibleContentHeight,0);
	this.slider.onDragStop = function (draggableEventObj) {
		var value=this.getValue(draggableEventObj);
		obj.animation.setPosition(value);
		this.notifyListeners("onChange",new SliderEventObj(value));
	}
	this.slider.onDrag=function (draggableEventObj) {
		if (obj.animation.getAnimationStep()===null || obj.animation.getAnimationStep()>0) {
			obj.animation.setPosition(this.getValue(draggableEventObj));
		}
		this.notifyListeners("onChange",new SliderEventObj(this.getValue(draggableEventObj)));
	}
	this.slider.addListener(this);
}

VScroll.prototype.hideControls=function () {
	DHTMLApi.Visibility.hide(this.sliderElement);
	DHTMLApi.Visibility.hide(this.sliderbarElement);
	DHTMLApi.Visibility.hide(this.sliderbarElement.parentNode);
}

VScroll.prototype.showControls=function () {
	DHTMLApi.Visibility.show(this.sliderbarElement.parentNode);
	DHTMLApi.Visibility.show(this.sliderbarElement);
	DHTMLApi.Visibility.show(this.sliderElement);
}

VScroll.prototype.setPosition=function (numOfPixels) {
	DHTMLApi.Position.setYPos(this.contentElement, -1*numOfPixels, this.containerElement);
	this.slider.setValue(-1*numOfPixels);
}

VScroll.prototype.setBottomPosition=function () {
	this.setPosition(this.invisibleContentHeight);
}

VScroll.prototype.resize=function (width, height) {
	if (height<DHTMLApi.Size.getElementHeight(this.contentElement)) {
		if (this.controlsHidden) {
			DHTMLApi.CSS.setProperties(this.mask, {overflow:'hidden'});
			this.showControls();
			this.controlsHidden=false;
		}
		this.resizeElements(width, height);
		this.recalculateAfterResize(width, height);
	} else {
		if (!this.controlsHidden) {
			this.hideControls();
			this.setPosition(0);
			this.controlsHidden=true;
			DHTMLApi.CSS.setProperties(this.mask, {overflow:'visible'});
		}
	}
}

VScroll.prototype.resizeElements=function(width, height) {
	var heightDifference;
	heightDifference=height-DHTMLApi.Size.getElementHeight(this.containerElement);
	DHTMLApi.CSS.setProperties(this.containerElement, {width: width+"px", height: height+"px"});
	DHTMLApi.CSS.setProperties(this.mask, {width: (width-this.rightViewportPadding-this.viewportPosX)+"px", height: (height-this.bottomViewportPadding-this.viewportPosY)+"px"});
	DHTMLApi.CSS.setProperties(this.sliderbarElement, {height: (DHTMLApi.Size.getElementHeight(this.sliderbarElement)+heightDifference)+"px"});
	DHTMLApi.CSS.setProperties(this.sliderbarElement.parentNode, {height: height+"px"});
	//DHTMLApi.CSS.setProperties(this.downButton, {top: (height-(DHTMLApi.Size.getElementHeight(this.downButton)))+"px", height: DHTMLApi.Size.getElementHeight(this.downButton)+"px", width: DHTMLApi.Size.getElementWidth(this.downButton)+"px"});
}

VScroll.prototype.recalculateAfterResize=function(width, height) {
	this.viewportWidth=width-this.rightViewportPadding-this.viewportPosX;
	this.viewportHeight=height-this.bottomViewportPadding-this.viewportPosY;
	this.invisibleContentHeight=DHTMLApi.Size.getElementHeight(this.contentElement)-this.viewportHeight;
	this.slider.setBoundaryValues(0,-this.invisibleContentHeight);
	this.slider.draggableObject.refreshAfterResize();
	if (DHTMLApi.Position.getYPosInElement(this.contentElement,this.containerElement)<-1*this.invisibleContentHeight) {
		DHTMLApi.Position.setYPos(this.contentElement, -1*this.invisibleContentHeight, this.containerElement);
	} 
	this.slider.setValue(DHTMLApi.Position.getYPosInElement(this.contentElement,this.containerElement));
	
}

VScroll.prototype.scrollUp=function (scrollIncrement, refreshSliderPos) {
	var targetPosY;
	if ((this.getCurrentScrollPosition()+scrollIncrement) >= 0) {
			targetPosY=0;
		} else {
			targetPosY=this.getCurrentScrollPosition()+scrollIncrement;
		}
		if (refreshSliderPos) {
			this.animation.setPosition(targetPosY);
			this.slider.setValue(targetPosY);
		} else {
			this.animation.setPosition(targetPosY);
			this.animation.addListener(this);
		}
}

VScroll.prototype.scrollDown=function (scrollIncrement, refreshSliderPos) {
	var targetPosY;
	if ((this.getCurrentScrollPosition() - scrollIncrement) < -this.invisibleContentHeight) {
			targetPosY=-this.invisibleContentHeight;
		} else {
			targetPosY=this.getCurrentScrollPosition()- scrollIncrement;
		}
		if (refreshSliderPos) {
			this.animation.setPosition(targetPosY);
			this.slider.setValue(targetPosY);
		} else {
			this.animation.setPosition(targetPosY);
			this.animation.addListener(this);
		}
}

VScroll.prototype.getCurrentScrollPosition=function() {
	return DHTMLApi.Position.getYPosInElement(this.contentElement,this.mask);
}

VScroll.prototype.scrollToTop=function() {
	this.scrollTo(0);
}

VScroll.prototype.scrollTo=function (pixelValue) {
	if (pixelValue<this.invisibleContentHeight) {
		this.animation.setPosition(-1*pixelValue);
	} else {
		this.animation.setPosition(-1*this.invisibleContentHeight);
	}
	this.animation.addListener(this);
}

VScroll.prototype.onUpperSliderBarClick=function(event) {
	this.scrollUp(this.viewportHeight,false);
}

VScroll.prototype.onLowerSliderBarClick=function(event) {
	this.scrollDown(this.viewportHeight,false);
}

VScroll.prototype.onAnimationStep=function() {
	this.slider.setValue(this.getCurrentScrollPosition());
}

VScroll.prototype.onAnimationEnd=function() {
	this.animation.removeListener(this);
}

 
///////////////////////
// Package Animation //
///////////////////////
 
Animation= new Object();
 
Animation.FRAME_RATE=50; // miliseconds
 
/////////////////////////////////
// Class Animation.SmoothVMove //
/////////////////////////////////

Animation.SmoothVMove=function(movingObject, relativeToObject) {
	this.movingObject=movingObject;
	this.relativeToObject=relativeToObject;
	this.currentYPos=DHTMLApi.Position.getYPosInElement(movingObject,relativeToObject);
	this.targetYPos=null;
	this.interval=null;
	this.numOfAnimationStep=null;
}

Animation.SmoothVMove.prototype=implementsInterface(Observable);

Animation.SmoothVMove.prototype.setPosition=function (targetPosition) {
	this.currentYPos=DHTMLApi.Position.getYPosInElement(this.movingObject,this.relativeToObject);
	if (this.interval!==null) {
		window.clearInterval(this.interval);
		this.notifyListeners("onAnimationEnd",null);
	}
	var animationObject=this;
	this.targetYPos=targetPosition;
	this.numOfAnimationStep=0;
	this.notifyListeners("onAnimationStart",null);
	this.animate();
	this.interval=window.setInterval(function() {animationObject.animate()},Animation.FRAME_RATE);
}

Animation.SmoothVMove.prototype.animate=function () {
	var stepDistance=(this.targetYPos-this.currentYPos)/3;
	if (Math.abs(stepDistance)>0.3) {
		this.currentYPos+=stepDistance;
		DHTMLApi.Position.setYPos(this.movingObject, this.currentYPos, this.relativeToObject);
		++this.numOfAnimationStep;
		this.notifyListeners("onAnimationStep",this.numOfAnimationSteps);
		return this.numOfAnimationStep;
	} else {
		this.currentYPos=this.targetYPos;
		DHTMLApi.Position.setYPos(this.movingObject, this.currentYPos, this.relativeToObject);
		this.notifyListeners("onAnimationStep",++this.numOfAnimationSteps);
		window.clearInterval(this.interval);
		this.notifyListeners("onAnimationEnd",null);
		this.numOfAnimationStep=null;
		return false;
	}
}

Animation.SmoothVMove.prototype.getAnimationStep=function () {
	return this.numOfAnimationStep;
}



/////////////////////////////////////
// Class Animation.ContinuousVMove //
/////////////////////////////////////

Animation.ContinuousVMove=function(movingObject, relativeToObject, increment) {
	this.movingObject=movingObject;
	this.relativeToObject=relativeToObject;
	this.currentYPos=DHTMLApi.Position.getYPosInElement(movingObject,relativeToObject);
	this.interval=null;
	if (typeof increment != "undefined") {
		this.increment=increment;
	} else {
		this.increment=1;
	}
}

Animation.ContinuousVMove.prototype=implementsInterface(Observable);

Animation.ContinuousVMove.prototype.start=function (increment) {
	if (this.interval!==null) {
		window.clearInterval(this.interval);
		this.notifyListeners("onAnimationEnd",null);
	}
	var animationObject=this;
	if (typeof increment != "undefined") {
		this.increment=increment;
	}
	this.notifyListeners("onAnimationStart",null);
	this.interval=window.setInterval(function() {animationObject.animate()},Animation.FRAME_RATE);
}

Animation.ContinuousVMove.prototype.stop=function () {
	window.clearInterval(this.interval);
	this.notifyListeners("onAnimationEnd",null);
}

Animation.ContinuousVMove.prototype.animate=function () {
	this.currentYPos+=this.increment;
	DHTMLApi.Position.setYPos(this.movingObject, this.currentYPos, this.relativeToObject);
	this.notifyListeners("onAnimationStep",this.currentYPos);
}

Animation.ContinuousVMove.prototype.setYPosition=function (yPosition) {
	this.currentYPos=yPosition;
	DHTMLApi.Position.setYPos(this.movingObject, this.currentYPos, this.relativeToObject);
}


///////////////////////
// Class AutoVScroll //
///////////////////////

function AutoVScroll(maskElement,contentElement,increment) {
	this.mask=maskElement;
	this.content=contentElement;
	this.container=null;
	this.animation=null;
	this.contentHeight=null;
	this.build();
	this.init(-1*increment);
}

AutoVScroll.prototype.build=function () {
	var duplicate, contentWidth;
	if (DHTMLApi.Size.getElementHeight(this.mask)>DHTMLApi.Size.getElementHeight(this.content)) return;
	this.contentHeight=DHTMLApi.Size.getElementHeight(this.content);
	this.container=document.createElement("DIV");
	this.container.appendChild(this.mask.removeChild(this.content));
	duplicate=this.content.cloneNode(true);
	this.container.appendChild(duplicate);
	contentWidth=DHTMLApi.Size.getElementWidth(this.content);
	DHTMLApi.CSS.setProperties(this.container, {width: contentWidth+"px", height: this.contentHeight+"px"});
	this.mask.appendChild(this.container);
}

AutoVScroll.prototype.init=function (increment) {
	var obj=this;
	this.animation=new Animation.ContinuousVMove(this.container, this.mask, increment);
	this.animation.addListener(this);
	this.animation.start();
	DOMEvent.addDomListener(this.mask, "mouseover", function () {
		obj.animation.stop();
	});
	DOMEvent.addDomListener(this.mask,"mouseout", function () {
		obj.animation.start();
	});
}

AutoVScroll.prototype.onAnimationStep=function (currentYPostion) {
	if (Math.abs(currentYPostion)>=this.contentHeight) {
		this.animation.setYPosition(this.contentHeight-Math.abs(currentYPostion));
	}
}


