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,upButton,downButton, scrollIncrement) {
 this.containerElement=containerElement;
 this.contentElement=contentElement;
 this.sliderbarElement=sliderbarElement;
 this.sliderElement=sliderElement;
 this.upButton=upButton;
 this.downButton=downButton;
 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;
 this.mask=this.buildMask();
 this.animation=new Animation.SmoothVMove(this.contentElement, this.mask);
 this.initSlider();
 this.initButtons();
}
 
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.initButtons=function() {
 var obj=this;
 var mousedownInterval;
  
 DOMEvent.addDomListener(this.downButton,"mousedown",function() {
  mousedownInterval=window.setInterval(function () {obj.scrollDown(obj.scrollIncrement, true)},10);
 });
 
 DOMEvent.addDomListener(this.downButton,"mouseout",function() {
  window.clearInterval(mousedownInterval);
 });
 
 DOMEvent.addDomListener(this.downButton,"mouseup",function() {
  window.clearInterval(mousedownInterval);
 });
  
 DOMEvent.addDomListener(this.upButton,"mousedown",function() {
  mousedownInterval=window.setInterval(function () {obj.scrollUp(obj.scrollIncrement, true)},10);
 });
 
 DOMEvent.addDomListener(this.upButton,"mouseup",function() {
  window.clearInterval(mousedownInterval);
 });
 
 DOMEvent.addDomListener(this.upButton,"mouseout",function() {
  window.clearInterval(mousedownInterval);
 });
}
 
VScroll.prototype.resize=function (width, height) {
 this.resizeElements(width, height);
 this.recalculateAfterResize(width, height);
}
 
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);
  }
  /*this.animation.setPosition(targetPosY);
  this.animation.addListener(this);*/
  //this.slider.setValue(targetPosY);
}
 
VScroll.prototype.scrollDown=function (scrollIncrement, refreshSliderPos) {
 var targetPosY;
 if ((this.getCurrentScrollPosition() - scrollIncrement) < -this.invisibleContentHeight) {
   targetPosY=-this.invisibleContentHeight;
  } else {
   targetPosY=this.getCurrentScrollPosition()- scrollIncrement;
  }
  //this.slider.setValue(targetPosY);
  if (refreshSliderPos) {
   this.animation.setPosition(targetPosY);
   this.slider.setValue(targetPosY);
  } else {
   this.animation.setPosition(targetPosY);
   this.animation.addListener(this);
  }
  /*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) {
 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;
}