/**
 *	@version 1.2
 *	@author Milovan Zogovic
 *	@modified 2009-03-08
 *	@desc
 *    Requires following scrollbar structure
 *    <div id="scrollbar_id">
 *      <div class="track"><div class="handle"></div></div>
 *      <a class="scrollBackward"></a>
 *      <a class="scrollForward"></a>
 *    </div>
 *    <div id="scrollarea_id">
 *      content goes here
 *    </div>
 */

var ZScroll = Class.create({
	startDiff: 5, //px -> scrollBy when button clicked

	// Elements
	eScrollbar: null,
	eScrollarea: null,
	eTrack: null,
	eHandle: null,
	eScrollBackward: null,
	eScrollForward: null,
	
	type: null,	
	slider: null,
	diff: null,
	periodicScroller: null,
	
	initialize: function(type, scrollbarID, scrollareaID) {
		this.diff = this.startDiff;
		this.type = type;
		this.eScrollbar = $(scrollbarID);
		this.eScrollarea = $(scrollareaID);
		this.eTrack = this.eScrollbar.select('div.track').first();
		this.eHandle = this.eTrack.select('div.handle').first();
		this.eScrollBackward = this.eScrollbar.select('a.scrollBackward').first();
		this.eScrollForward = this.eScrollbar.select('a.scrollForward').first();

		this.createSlider();
		this.showOrHideSlider();
		this.setEventHandlers();
	},
	
	reset: function()	{
		this.slider.dispose();
		this.eScrollbar.show();
		this.createSlider();
		this.showOrHideSlider();
		
		switch(this.type){
			case "vertical":
				this.eScrollarea.scrollTop = 0;
				break;
			case "horizontal":
				this.eScrollarea.scrollLeft = 0;
				break;
		}
	},
	
	createSlider: function() {
		var oSelf = this;
		
		switch(this.type)
		{
			case "vertical":
				this.slider = new Control.Slider(this.eHandle, this.eTrack, {
					axis: 'vertical',
					onSlide:  function(v) { oSelf.scrollVertical(v, oSelf.eScrollarea, oSelf.slider); },
					onChange: function(v) { oSelf.scrollVertical(v, oSelf.eScrollarea, oSelf.slider); }
				});		
				break;
				
			case "horizontal":
				this.slider = new Control.Slider(this.eHandle, this.eTrack, {
					onSlide:  function(v) { oSelf.scrollHorizontal(v, oSelf.eScrollarea, oSelf.slider); },
					onChange: function(v) { oSelf.scrollHorizontal(v, oSelf.eScrollarea, oSelf.slider); }
				});
				break;
		}
	},
	
	showOrHideSlider: function() {
		switch(this.type)
		{
			case "vertical":
				if (this.eScrollarea.scrollHeight <= this.eScrollarea.offsetHeight) {
					this.slider.setDisabled();
					this.eScrollbar.hide();
				}
				break;
				
			case "horizontal":
				if (this.eScrollarea.scrollWidth <= this.eScrollarea.offsetWidth) {
					this.slider.setDisabled();
					this.eScrollbar.hide();
				}
				break;
		}
	},
	
	
	setEventHandlers: function(){
		this.bindButtons(this.eScrollBackward, this.eScrollForward);

		Event.observe(this.eScrollarea, 'DOMMouseScroll', this.onMouseWheel.bindAsEventListener(this)); // mozilla
		Event.observe(this.eScrollarea, 'mousewheel', this.onMouseWheel.bindAsEventListener(this)); // IE/Opera
	},
	
	bindButtons: function(btnBackward, btnForward) {
		Event.observe(btnBackward, 'mousedown', this.scrollBackward.bindAsEventListener(this));
		Event.observe(btnBackward, 'mouseup', this.stopScrolling.bindAsEventListener(this));
		
		Event.observe(btnForward, 'mousedown', this.scrollForward.bindAsEventListener(this));
		Event.observe(btnForward, 'mouseup', this.stopScrolling.bindAsEventListener(this));
	},
	
	scrollVertical: function(value, element, slider) {
		element.scrollTop = Math.round(value/slider.maximum*(element.scrollHeight-element.offsetHeight));
	},
	
	scrollHorizontal: function(value, element, slider) {
		element.scrollLeft = Math.round(value/slider.maximum*(element.scrollWidth-element.offsetWidth));
	},
	
	scrollBackward: function(event) {
		this.diff = -Math.abs(this.startDiff);
		this.startScrolling();
	},
	
	scrollForward: function(event) {
		this.diff = +Math.abs(this.startDiff);
		this.startScrolling();
	},
	
	startScrolling: function() {
		this.periodicScroller = new PeriodicalExecuter(this.scrollSlider.bindAsEventListener(this), 0.05);
	},
	
	scrollSlider: function()	{
		this.diff *= 1.05; //acceleration
		this.scrollByPx(this.diff);
	},
	
	stopScrolling: function() {
		if(this.periodicScroller) this.periodicScroller.stop();
	},
	
	scrollByPx: function(diff) {
		var value = null;
		switch(this.type) {
			case "vertical":
				value = diff / (this.eScrollarea.scrollHeight - this.eScrollarea.offsetHeight);
				break;
			case "horizontal":
				value = diff / (this.eScrollarea.scrollWidth - this.eScrollarea.offsetWidth);
				break;
		}
		this.slider.setValueBy(value);
	},
	
	// Event handler for mouse wheel event.
	onMouseWheel: function(event) {
		var delta = 0;
		if (!event) // For IE.
			event = window.event;
		if (event.wheelDelta) { // IE/Opera.
			delta = event.wheelDelta/120;
			// In Opera 9, delta differs in sign as compared to IE.
			if (window.opera)
				delta = -delta;
		} else if (event.detail) { // Mozilla case. 
			// In Mozilla, sign of delta is different than in IE.
			// Also, delta is multiple of 3.
			delta = -event.detail/3;
		}
		
		// If delta is nonzero, handle it.
		// Basically, delta is now positive if wheel was scrolled up,
		// and negative, if wheel was scrolled down.
		if (delta) this.handleMouseWheel(delta);

		// Prevent default actions caused by mouse wheel.
		// That might be ugly, but we handle scrolls somehow
		// anyway, so don't bother here..
		if (event.preventDefault) event.preventDefault();

		event.returnValue = false;
	},
	
	handleMouseWheel: function(delta) {
		this.scrollByPx(-delta*10);
	}
	
});