/*
	Control.Scrollbar, v1.01

	This script is inspired by the following scrolling widgets:

	scrollbar.js	by Ryan Johnson		[http://livepipe.net/control/scrollbar]
	scrollbox.js	by Dave Grijalva	[http://theblogthatnoonereads.tunasoft.com/2007/04/08/scrollbox_js/]


	***************************************************************************
	CHANGELOG:
		v1.02	: Fixed custom events handling issue with FF 3.5
		v1.01	: Fixed the recalculateLayout function to compute the slider handleLength

	TODO:
		- insert an option for scrolling of a fixed amount when clicking the empty track (look startDrag in slider.js)
		- fix textarea: dont scroll while pressing keys (use -moz-scrollbars-none to let the base scrolling behavior without displaying default system scrollbars ?)
*/

if (!Control) var Control = { };


Control.ScrollBar = Class.create({
	initialize:				function(content, scrollbar, options) {
		this.enabled						= false;

		this.content						= $(content);
		this.scrollbar						= $(scrollbar);
		this.track							= this.scrollbar.down('.track');
		this.handle							= this.scrollbar.down('.handle');
		this.upButton						= this.scrollbar.down('.up');
		this.downButton						= this.scrollbar.down('.down');

		this.boundMouseWheelEvent			= this.onMouseWheel.bindAsEventListener(this);
		this.boundResizeObserver			= this.onWindowResize.bind(this);

		this.options						= Object.extend({
			active_class_name:				'scrolling',	// className applied while scrolling - useful for css fx
			apply_active_class_name_to:		this.content,	// The element onto the active_class_name should be applied
			handle_minimum_height:			25,				// If proportional is true, this is the minimum height in pixels.
			scroll_to_smoothing:			0.01,			// The increment for smooth scrolling
			scroll_to_steps:				15,				// Number of pixels used to scroll by steps
			hold_delay:						500,			// The delay when holding the mouse down before it starts auto scrolling
			hold_interval:					100,			// The interval between auto scroll increments
			proportional:					false,			// Draw the handle proportional to (container height / length of track).
			slider_options:					{}				// Will be passed to Control.Slider
		},options || {});

		this.slider = new Control.Slider(this.handle, this.track, Object.extend({
			axis:		'vertical',
			onSlide:	this.onChange.bind(this),
			onChange:	this.onChange.bind(this)
		}, this.options.slider_options));

		this.recalculateLayout();

		Event.observe(window, 'resize', this.boundResizeObserver);

		// Explore this for textarea
		// Event.observe(this.content, 'keydown', this.boundResizeObserver);

		this.handle.observe('mousedown', function() {
		    if(this.auto_sliding_executer)
    			this.auto_sliding_executer.stop();
		}.bind(this));

		try {
			this.upButton.observe('mousedown', function(e) {
				this.onButtonDown(e, this.scrollUp.bind(this));
			}.bind(this));

			this.downButton.observe('mousedown', function(e) {
				this.onButtonDown(e, this.scrollDown.bind(this));
			}.bind(this));
		} catch(E) {};

		Event.observe(document, 'mouseup', this.onButtonUp.bind(this));

	},
	destroy:				function() {
		Event.stopObserving(window,'resize',this.boundResizeObserver);
	},
	enable:					function() {
		this.enabled	= true;
		this.content.observe('mouse:wheel', this.boundMouseWheelEvent);
		this.slider.setEnabled();
		this.scrollbar.show();
		if(this.options.active_class_name)
			$(this.options.apply_active_class_name_to).addClassName(this.options.active_class_name);
	},
	disable:				function() {
		this.enabled	= false;
		this.content.stopObserving('mouse:wheel', this.boundMouseWheelEvent);
		this.slider.setDisabled();
		this.scrollbar.hide();
		if(this.options.active_class_name)
			$(this.options.apply_active_class_name_to).removeClassName(this.options.active_class_name);
		this.reset();
	},
	reset:					function() {
		this.slider.setValue(0);
	},
	recalculateLayout:		function() {
		/*
		// May help to automatically calculate Layout on an hidden (display:none) content
		if (this.content.offsetHeight == 0) {
			this.content.observe('mouseover', function() {
				this.recalculateLayout();
				this.content.stopObserving('mouseover');
			}.bind(this));
		}
		*/
		(this.content.scrollHeight > this.content.offsetHeight)	? this.enable() : this.disable();

		this.slider.trackLength		= this.slider.maximumOffset() - this.slider.minimumOffset();

		if(this.options.proportional){
			this.handle.style.height	= Math.max(this.content.offsetHeight * (this.content.offsetHeight / this.content.scrollHeight),this.options.handle_minimum_height) + 'px';
			//this.slider.handleLength	= this.handle.style.height.replace(/px/,'');
		}
		this.slider.handleLength	= (this.slider.handles[0].offsetHeight != 0) ?
										this.slider.handles[0].offsetHeight :
										this.slider.handles[0].style.height.replace(/px$/,"");

	},
	onWindowResize:			function() {
		this.recalculateLayout();
		this.scrollBy(0);

		// Explore this for textarea
		// this.scrollTo('bottom');
	},
	onMouseWheel:			function(event) {
	    if(this.auto_sliding_executer)
			this.auto_sliding_executer.stop();
		this.slider.setValueBy(-(event.memo.delta / 20)); //put in math to account for the window height
		event.stop();
		return false;
	},
	onChange:				function(value) {
		this.content.scrollTop	= Math.round(value / this.slider.maximum * (this.content.scrollHeight - this.content.offsetHeight));
	},

	onButtonDown:			function(event, action) {
		action();
		this.timeout = setTimeout(function(){
			action();
			this.timeout = null;
			if(this.interval){ clearInterval(this.interval);}
			this.interval = setInterval(action, this.options.hold_interval);
		}.bind(this), this.options.hold_delay);
		Event.stop(event);
	},
	onButtonUp:				function(event) {
		if(this.timeout){
			clearTimeout(this.timeout);
		}
		if(this.interval){
			clearInterval(this.interval);
		}
		this.timeout = null;
		this.interval = null;
		this.down_position = null;
	},

	getCurrentMaximumDelta:	function() {
		return this.slider.maximum * (this.content.scrollHeight - this.content.offsetHeight);
	},
	getDeltaToElement:		function(element) {
		return this.slider.maximum * ((element.positionedOffset().top + (element.getHeight() / 2)) - (this.content.getHeight() / 2));
	},
	scrollTo:				function(y, animate){
		var current_maximum_delta	= this.getCurrentMaximumDelta();
		if(y == 'top')
			y	= 0;
		else if(y == 'bottom')
			y	= current_maximum_delta;
		else if(typeof(y) != "number")
			y	= this.getDeltaToElement($(y));
		if(this.enabled) {
			y	= Math.max(0, Math.min(y, current_maximum_delta));
			if(this.auto_sliding_executer)
				this.auto_sliding_executer.stop();
			var target_value			= y / current_maximum_delta;
			var original_slider_value	= this.slider.value;
			var delta					= (target_value - original_slider_value) * current_maximum_delta;
			if (animate) {
				this.auto_sliding_executer	= new PeriodicalExecuter(function(){
					if (Math.round(this.slider.value * 100) / 100 < Math.round(target_value * 100) / 100 || Math.round(this.slider.value * 100) / 100 > Math.round(target_value * 100) / 100){
						this.scrollBy(delta / this.options.scroll_to_steps);
					} else {
						this.auto_sliding_executer.stop();
						this.auto_sliding_executer	= null;
						if(typeof(animate) == "function")
							animate();
					}
				}.bind(this), this.options.scroll_to_smoothing);
			} else
				this.scrollBy(delta);
		} else if(typeof(animate) == "function")
			animate();
	},
	scrollBy:				function(y) {
		if (!this.enabled)
			return false;
		this.slider.setValueBy(y / this.getCurrentMaximumDelta());
	},
	scrollUp:				function(y) {
		if (!this.enabled)
			return false;
		this.scrollBy(-this.options.scroll_to_steps);
	},
	scrollDown:				function(y) {
		if (!this.enabled)
			return false;
		this.scrollBy(this.options.scroll_to_steps);
	}
});

//mouse:wheel
(function() {
	function wheel(event){
		var delta;
		// normalize the delta
		if (event.wheelDelta) // IE & Opera
			delta			= event.wheelDelta / 120;
		else if (event.detail) // W3C
			delta			=- event.detail / 3;
		if (!delta)
			return;
		var custom_event	= $(Event.element(event)).fire('mouse:wheel',{
			delta: delta
		});
		if (custom_event.stopped) {
			Event.stop(event);
			return false;
		}
	}
	document.observe('mousewheel', wheel);
	document.observe('DOMMouseScroll', wheel);
})();