/* ScrollingDiv 0.1
 * by Peter Swan - pdswan-at-gmail.com
 * Liscensed under Creative Commons: http://creativecommons.org/licenses/by-nc-sa/3.0/
 * Please include original header in derivative work
 *
 * The ScrollingDiv class uses the prototype (http://prototypejs.org/) and scriptaculous (http://script.aculo.us/) 
 * libraries to create cross browser custom scrollbars for div elements.
 *
 * Setting overflow: auto or overflow: scroll on the parent element will give default OS
 * scrollbars in the case that javascript is not enabled.
 *
 * ScrollingDiv( <element to scroll>, <parent element>, <clip width>, <clip height>, <vertical scrollbar>, <horizontal scrollbar>)
 *	- instantiates new scrolling div element.
 *	- element to scroll will scroll within the parent element with using clip width and clip height to create the
 *	  clipping window (defaults to parentElement.width, parentElement.height)
 *
 * ScrollingDiv.bindButtons( <up button element>, <down button element>, <left button element>, <right button element>, delta)
 *	- on click of <up button element <element to scroll> will scroll up by delta = {x | 0 <= x <= 1}
 *
 * ScrollingDiv.resetContentDim()
 *	- can theoretically be used to reset the width and height of the scrolling element in the case of an asynchronous
 *	  update to the content.
 *
 * This class has been tested on the following browsers:
 * Firefox 2.0.0.3 (PC)
 * Opera 8.54
 * IE 6.0.2900
 * Safari
 * Firefox
 */

var DEFAULT_SCROLL_TIME = 0.2;


var ScrollingDiv = Class.create();
ScrollingDiv.prototype = {
	initialize: function( e, p, w, h, vs, hs){
		this.scrollElement = $(e);
		this.parentElement = $(p);
		this.hs = hs;
		this.vs = vs;
		
		if( (!this.scrollElement || !this.parentElement ) ||
			(!this.hs && !this.vs ) ){
			return null;
		}
		this.ubtn = null;
		this.dbtn = null;
		this.lbtn = null;
		this.rbtn = null;
		this.delta = 0;
		this.disabled = false;
		
		// Make sure parent element is not scrolling
		this.parentElement.setStyle({overflow: 'hidden'});
		
		// Set position relative to allow for scrolling
		this.scrollElement.makePositioned();
		this.parentElement.makePositioned();
		
		// Set zindex so scroll element is beneath parent
		this.scrollElement.setStyle({zindex: 1});
		this.parentElement.setStyle({zindex: 2});
		
		/* Set the width and height of the clipping window
		   Default is width and height of parent element
		   */
		this.clipWidth = (w?w:$(p).getWidth());
		this.clipHeight = (h?h:$(p).getHeight());
		
		// Get content width and height
		this.resetContentDim();
		
		/* If there is a horizontal scrollbar set up
		   onslide and onchange functions
		   */
		if( this.hs ){
			this.hs = hs;
			this.hs.options.onChange = this.hs.options.onSlide = this.scrollHorizontal.bind(this);
		}
		
		if( this.vs ){
			this.vs = vs
			this.vs.options.onChange = this.vs.options.onSlide = this.scrollVertical.bind(this);
		}
		
		/* Functions to manage a periodical executer that
		   manages up/down/left/right button functionality
		   */
		this.startUp = function(){
			this.stop();
			this.vs.setValueBy(-this.delta);
			this.btnPe = new PeriodicalExecuter(
				function(){
					this.vs.setValueBy(-this.delta);
				}.bind(this), DEFAULT_SCROLL_TIME);
		};
		this.startDown = function(){
			this.stop();
			this.vs.setValueBy(this.delta);
			this.btnPe = new PeriodicalExecuter(
				function(){
					this.vs.setValueBy(this.delta);
				}.bind(this), DEFAULT_SCROLL_TIME);
		};
		this.startLeft = function(){
			this.stop();
			this.hs.setValueBy(-this.delta);
			this.btnPe = new PeriodicalExecuter(
				function(){
					this.hs.setValueBy(-this.delta);
				}.bind(this), DEFAULT_SCROLL_TIME);
		};
		this.startRight = function(){
			this.stop();
			this.hs.setValueBy(this.delta);
			this.btnPe = new PeriodicalExecuter(
				function(){
					this.hs.setValueBy(this.delta);
				}.bind(this), DEFAULT_SCROLL_TIME);
		};
		this.stop = function(){
			if( this.btnPe ){
				this.btnPe.stop();
				delete this.btnPe;
			}
		};
	},
	
	/* BUG? if language="javascript" is not included in IE Math.round is unavailable */
	scrollHorizontal: function(v){
		var left = Math.round((v / this.hs.maximum) * (this.clipWidth - this.contentWidth));
		this.scrollElement.setStyle({left: left + 'px'});
	},
	
	scrollVertical: function(v){
		var top = Math.round((v / this.vs.maximum) * (this.clipHeight - this.contentHeight));
		this.scrollElement.setStyle({top: top + 'px'});
	},
	
	/* Get dimensions of content element, if dimensions are less than
	   the clipping dimensions, disable the scrollbars */
	resetContentDim: function(){
		// Get width and height of content
		this.contentHeight = this.scrollElement.getHeight();
		this.contentWidth = this.scrollElement.getWidth();
		
		if( this.vs ){
			if( this.contentHeight <= this.clipHeight ){
				this.vs.setDisabled();
				this.vs.track.setStyle({visibility: 'hidden'});
				this.toggleVerticalButtons('hidden');
			}else{
				this.vs.setEnabled();
				this.vs.track.setStyle({visibility: 'visible'});
				this.toggleVerticalButtons('visible');
			}
		}
		if( this.hs ){
			if( this.contentWidth <= this.clipWidth ){
				this.hs.setDisabled();
				this.hs.track.setStyle({visibility: 'hidden'});
				this.toggleHorizontalButtons('hidden');
			}else{
				this.hs.setEnabled();
				this.hs.track.setStyle({visibility: 'visible'});
				this.toggleHorizontalButtons('visible');
			}
		}
	},
	
	toggleHorizontalButtons: function(state){
		if( this.rbtn ){
			this.rbtn.setStyle({visibility: state});
		}
		if( this.lbtn ){
			this.lbtn.setStyle({visibility: state});
		}
	},
	
	toggleVerticalButtons: function(state){
		if( this.ubtn ){
			this.ubtn.setStyle({visibility: state});
		}
		if( this.dbtn ){
			this.dbtn.setStyle({visibility: state});
		}
	},
	
	/* Takes elements that will act as up/down/left/right buttons as well
	   as a delta value which controls rate of scrolling */
	bindButtons: function( ubtn, dbtn, lbtn, rbtn, delta){
		this.ubtn = $(ubtn);
		this.dbtn = $(dbtn);
		this.lbtn = $(lbtn);
		this.rbtn = $(rbtn);
		this.delta = delta;
		
		if( this.ubtn ){
			if( this.vs ){
				Event.observe( this.ubtn, 'mousedown', this.startUp.bind(this));
				Event.observe( this.ubtn, 'mouseout', this.stop.bind(this));
				Event.observe( document, 'mouseup', this.stop.bind(this));
			}
		}
		if( this.dbtn ){
			if( this.vs ){
				Event.observe( this.dbtn, 'mousedown', this.startDown.bind(this));
				Event.observe( this.dbtn, 'mouseout', this.stop.bind(this));
				Event.observe( document, 'mouseup', this.stop.bind(this));
			}
		}
		if( this.lbtn ){
			if( this.hs ){
				Event.observe( this.lbtn, 'mousedown', this.startLeft.bind(this));
				Event.observe( this.lbtn, 'mouseout', this.stop.bind(this));
				Event.observe( document, 'mouseup', this.stop.bind(this));
			}
		}
		if( this.rbtn ){
			if( this.hs ){
				Event.observe( this.rbtn, 'mousedown', this.startRight.bind(this));
				Event.observe( this.rbtn, 'mouseout', this.stop.bind(this));
				Event.observe( document, 'mouseup', this.stop.bind(this));
			}
		}
		if( this.vs && this.vs.disabled ){
			this.toggleVerticalButtons('hidden');
		}
		if( this.hs && this.hs.disabled ){
			this.toggleHorizontalButtons('hidden');
		}
	}
};

