
var ScrollBox = Class.create();
ScrollBox.prototype = {
	/**
	 *	OPTION CONTSTANTS
	 */

	// bar_action: what happens when you click on the blank part of the scrollbar?
	//		pass this in when creating a ScrollBox or change in the default object set
	//		found in the initialze method to change the default behavior
	NO_ACTION		:	0,	//Do nothing
	PAGE_ACTION		:	1,	//Jump forward or back one full page
	JUMP_ACTION		:	2,	//Jump to the position on the bar that was clicked

	options			:	[],
	scroll_pos		:	0,

	initialize: function(element, options){
 		this.element = $(element);
              //  alert("ScrollBox " +this.element.id);
    		Element.addClassName(this.element, 'scrollbox');

		this.options = Object.extend({
			barCss					:	null,
			upCss					:	null,
			downCss					:	null,
			scrollCss				:	null,
			scroll_increment		:	30,
			hold_delay				:	500,
			hold_interval			:	100,
			drag_interval			:	100,
			update_check_interval	:	100,
			auto_hide				:	false,
			bar_action				:	ScrollBox.JUMP_ACTION
		}, options || {});

		//Move content into content div
		this.content_div = document.createElement('div');
		Element.extend(this.content_div);
		this.content_div.addClassName('scrollbox_content');
		$A(this.element.childNodes).each(function(n){ this.content_div.appendChild(n);}.bind(this));
		this.element.appendChild(this.content_div);
		this.content_div.setStyle( {overflow: 'hidden', height: '100%'});

		//Add scrollbar div to element
		this.scrollbar = document.createElement('div');
		Element.extend(this.scrollbar);
		this.scrollbar.addClassName('scrollbox_scrollbar');
		this.element.appendChild(this.scrollbar);
		this.scrollbar.setStyle( {position: 'absolute', top: '0', right: '0', height: this.content_div.getHeight() + 'px'});

		//Add up button
		this.up_button = document.createElement('div');
		Element.extend(this.up_button);
		this.up_button.addClassName('scrollbox_up_button');
//		this.up_button.setStyle( {position: 'absolute', width: '100%', top: '0', right: '0'});
		this.up_button.setStyle( {position: 'absolute', top: '0', right: '0'});
		this.scrollbar.appendChild(this.up_button);

		//Add down button
		this.down_button = document.createElement('div');
		Element.extend(this.down_button);
		this.down_button.addClassName('scrollbox_down_button');
//		this.down_button.setStyle( {position: 'absolute', width: '100%', bottom: '0', right: '0'});
		this.down_button.setStyle( {position: 'absolute', bottom: '0', right: '0'});
		this.scrollbar.appendChild(this.down_button);

		//Add Scroll Handle
		this.handle = document.createElement('div');
		Element.extend(this.handle);
		this.handle.addClassName('scrollbox_handle');
//		this.handle.setStyle( {position: 'absolute', width: '100%', right: '0'});
		this.handle.setStyle( {position: 'absolute', right: '0'});
		this.scrollbar.appendChild(this.handle);

		//Setup State Info
		this.setSizes();

		//buttons actions
		Event.observe(this.up_button, 'mousedown', function(e){this.buttonDown(e, this.scrollUp.bind(this));}.bindAsEventListener(this));
		Event.observe(this.down_button, 'mousedown', function(e){this.buttonDown(e, this.scrollDown.bind(this));}.bindAsEventListener(this));
		Event.observe(document, 'mouseup', this.buttonUp.bindAsEventListener(this));

		//handle actions
		Event.observe(document, 'mousemove', this.setMousePos.bindAsEventListener(this));
		Event.observe(this.handle, 'mousedown', this.handleDown.bindAsEventListener(this));
		this.handle_update_interval = setInterval(this.updateCheck.bind(this), this.options.update_check_interval);

		//bar actions
		Event.observe(this.up_button, 'click', function(e){Event.stop(e);}.bindAsEventListener(this));
		Event.observe(this.down_button, 'click', function(e){Event.stop(e);}.bindAsEventListener(this));
		Event.observe(this.handle, 'click', function(e){Event.stop(e);}.bindAsEventListener(this));
		Event.observe(this.scrollbar, 'click', this.scrollBarClick.bindAsEventListener(this));

		//handle keypress events
		this.keyboard_events = [
			[document, 'keypress', this.keyboardEvent.bindAsEventListener(this)]
		];
		Event.observe(this.element, 'click', this.enableKeyboardEvents.bindAsEventListener(this));
		Event.observe(document, 'click', this.disableKeyboardEvents.bindAsEventListener(this));

		//handle scroll wheel
		Event.observe(this.content_div, 'mousewheel', this.scrollWheel.bindAsEventListener(this), true);
		Event.observe(this.content_div, 'DOMMouseScroll', this.scrollWheel.bindAsEventListener(this), true);



	},


	scrollDown: function(){
		if(this.scroll_pos  < this.scroll_max){
			this.scrollTo(this.scroll_pos + this.options.scroll_increment < this.scroll_max ? this.scroll_pos + this.options.scroll_increment : this.scroll_max);
			return true;
		}
		else{
			return false;
		}
	},
	scrollUp: function(){
		if(this.scroll_pos > 0){
			this.scrollTo(this.scroll_pos > this.options.scroll_increment ? this.scroll_pos - this.options.scroll_increment : 0);
			return true;
		}
		else{
			return false;
		}
	},
	scrollTo: function(new_pos){
		// console.log(new_pos, this.content_div);
		if(new_pos < 0){
			new_pos = 0;
		}
		if(new_pos > this.scroll_max){
			new_pos = this.scroll_max;
		}
		this.content_div.scrollTop = new_pos;
		this.scroll_pos = new_pos;
		this.updateHandle();
	},
	buttonDown: 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);
	},
	buttonUp: function(event){
		if(this.timeout){
			clearTimeout(this.timeout);
		}
		if(this.interval){
			clearInterval(this.interval);
		}
		this.timeout = null;
		this.interval = null;
		this.down_position = null;
	},
	updateHandle: function(){
		if(this.scroll_max){
			this.handle_height = Math.floor(this.bar_height / this.scroll_height_ratio);
		} else {
			this.handle_height = this.bar_height;
		}

		if(this.options.auto_hide){
			if(this.handle_height == this.bar_height){
				this.scrollbar.style.visibility = 'hidden';
			}
			else{
				this.scrollbar.style.visibility = '';
			}
		}

		var handle_top = this.up_button.offsetHeight;
		var handle_bottom = this.up_button.offsetHeight + (this.bar_height - this.handle_height);
		var bar_dist_height = handle_bottom - handle_top;
		if(this.scroll_max) {
			this.handle_pos = handle_top + Math.floor(bar_dist_height * (this.scroll_pos / this.scroll_max));
		} else {
			this.handle_pos = handle_top;
		}

		this.handle.setStyle({height: this.handle_height + 'px'});
		this.handle.style.top = this.handle_pos + 'px';
	},
	handleDown: function(){
		this.down_position = this.raw_mouse_pos - Position.cumulativeOffset(this.handle)[1];
		// console.log('Down at: ' , this.down_position, Position.cumulativeOffset(this.handle)[1], this.mouse_pos);
		if(this.interval){ clearInterval(this.interval);}
		this.interval = setInterval(function(){
			this.scrollTo(this.mouse_pos - (this.down_position * this.scroll_height_ratio));
		}.bindAsEventListener(this), this.options.drag_interval);
	},
	setMousePos: function(e){
		if (document.all) { // grab the x-y pos.s if browser is IE
			tempY = event.clientY + document.body.scrollTop;
		} else {  // grab the x-y pos.s if browser is NS
			tempY = e.pageY;
		}
		// catch possible negative values
		if (tempY < 0){tempY = 0;}

		this.raw_mouse_pos = tempY;
		this.mouse_pos = Math.floor((tempY - this.scrollbar_top) * this.scroll_height_ratio);
	},
	setSizes: function(){

		this.scroll_max = this.content_div.scrollHeight - this.content_div.offsetHeight;
		if(this.scroll_max < 0) this.scroll_max = 0;

		if(this.scroll_pos > this.scroll_max){
			this.scrollTo(this.scroll_max);
		}


		this.bar_height = this.scrollbar.offsetHeight - (this.up_button.offsetHeight + this.down_button.offsetHeight);

		if(!this.bar_height){
			setTimeout(this.setSizes.bind(this), 100);
		}

		this.scroll_height_ratio = (this.content_div.scrollHeight / this.bar_height);
		this.scroll_height_ratio = this.scroll_height_ratio >= 1 ? this.scroll_height_ratio : 1;

		this.scrollbar_top = Position.cumulativeOffset(this.scrollbar)[1] + this.up_button.offsetHeight;
		this.scrollbar_bottom = this.scrollbar_top + this.bar_height;

		this.updateHandle();
	},
	scrollBarClick: function(event){
		switch(this.options.bar_action){
			case ScrollBox.PAGE_ACTION:
				//clicked above the handle
				if(this.mouse_pos < this.handle_pos * this.scroll_height_ratio){
					this.pageUp();
				}
				//clicked below the handle
				else{
					this.pageDown();
				}
				break;
			case ScrollBox.JUMP_ACTION:
				this.scrollTo(this.mouse_pos);
				break;
		}
	},
	pageUp: function(){
		this.scrollTo(this.scroll_pos - this.content_div.offsetHeight);
	},
	pageDown: function(){
		this.scrollTo(this.scroll_pos + this.content_div.offsetHeight);
	},
	scrollWheel: function(event){
		var scroll_amount = Event.wheel(event);
		if(scroll_amount > 0){
			for(var i = 0; i < Math.ceil(scroll_amount); ++i){
				this.scrollUp();
			}
			if(this.scroll_pos > 0){
				Event.stop(event);
			}
		}
		else if(scroll_amount < 0){
			for(var i = 0; i > Math.floor(scroll_amount); --i){
				this.scrollDown();
			}
			if(this.scroll_pos < this.scroll_max){
				Event.stop(event);
			}
		}
	},
	updateCheck: function(){
		//Has the scroll pos been changed by something else?
		if(this.content_div.scrollTop != this.scroll_pos){
			this.scrollTo(this.content_div.scrollTop);
		}

		if(this.scroll_max != this.content_div.scrollHeight - this.content_div.offsetHeight){
			this.setSizes();
		}
	},
	enableKeyboardEvents: function(event){
		this.disableKeyboardEvents(event);

		this.keyboard_events.each(function(ke){
			Event.observe(ke[0], ke[1], ke[2]);
		});

		this.within_enable_event = true;
	},
	disableKeyboardEvents: function(event){
		if(!this.within_enable_event){
			this.keyboard_events.each(function(ke){
				Event.stopObserving(ke[0], ke[1], ke[2]);
			});
		}
		else{
			this.within_enable_event = false;
		}
	},
	keyboardEvent: function(event){
		switch(event.keyCode){
			case Event.KEY_HOME:
				this.scrollTo(0);
				break;
			case Event.KEY_END:
				this.scrollTo(this.scroll_max);
				break;
			case Event.KEY_PAGEUP:
				this.pageUp();
				break;
			case Event.KEY_PAGEDOWN:
				this.pageDown();
				break;
			case Event.KEY_UP:
				this.scrollUp();
				break;
			case Event.KEY_DOWN:
				this.scrollDown();
				break;
			default:
				return;
		}
		Event.stop(event);
	}
}

// Add mouse wheel support to prototype
Object.extend(Event, {
        wheel:function (event){
                var delta = 0;
                if (!event) event = window.event;
                if (event.wheelDelta) {
                        delta = event.wheelDelta/120;
                        if (window.opera) delta = -delta;
                } else if (event.detail) { delta = -event.detail/3;     }
                return delta; //Safari Round
        }
});