/**
 *
 *	Requires W
 *	Requires W.Dom
 *	Requires W.Event
 *	Requires W.Drag
 *
 *	Usage:
 *	<div id="scroller"></div>
 *	<script type="text/javascript" language="javascript">
 *		var scroller	= new W.Scrollable('scroller', {bgcolor: '#5b5342', bgwidth: '3px', bgmargin: '0 4px', color: '#7a9a9d', width: '11px', height: 'auto', offset: '-4px', up: 'images/scrollbar_up.gif', down: 'images/scrollbar_down.gif'});
 *	</script>
 */
W.Scrollable = function(id, config)
{
	this.init(id, config);
};



W.Scrollable.prototype = {


	id					: null,			// id for div containing content to be scrolled
	div					: null,			// div HTMLElement with id of this.id
	container			: null,			// container for html
	config				: null,			// config for scrollbar appearance
	scrollbar_bg		: null,			// scrollbar div
	handle				: null,			// draggable scrollbar handle
	scroll_max			: 0,			// max y distance this.container can scroll
	handle_max			: 0,			// max y distance this.handle can travel
	overlap				: 12,			// when jumping, the amount of overlap between the 2 views
	
	
	
	init : function(id, config)
	{
		this.id		= id;
		this.div	= W.$(this.id);
		this.config	= config;
		
		
		if (this.config.displayDiv) W.$(this.config.displayDiv).style.display = 'block';	// get current display - div display cannot be none because 
																							// offsetHeight and scrollHeight would be 0

		var scroll = this.init_container();
		
		if (scroll) {
			
			this.init_config();
			this.init_scrollbar_bg();
			this.init_scrollbar_handle();
		}
		
		
		if (this.config.displayDiv) W.$(this.config.displayDiv).style.display = 'none';		// restore display to its original value
	},
	
	
	
	init_config : function()
	{
		var config	= this.config;
		
		config.bgcolor	= config.bgcolor || '#000000';
		config.bgwidth	= config.bgwidth || '15px';
		config.bgmargin	= config.bgmargin || '0 0 0 0';
		config.bgoffset	= config.bgoffset || '0';
		
		config.color	= config.color || '#ffffff';
		config.width	= config.width || '15px';
		config.height	= config.height || 'auto';
		config.offset	= config.offset || '0';
	},
	
	
	
	// the content to be scrolled is put into a containing div which is then moved up and down to create the scrolling effect
	init_container : function()
	{
		var obj						= this;
		var div						= this.div;
		var scroll					= true;
		
		var container				= document.createElement('div');
		container.id				= this.id + '_container';
		container.style.position	= 'relative';
		container.style.height		= div.offsetHeight;						// IE7 truncates the div unless the height is set
		container.innerHTML			= div.innerHTML;
		
		div.innerHTML				= '';
		this.container				= div.appendChild(container);

		this.max					= div.scrollHeight;

		if (div.offsetHeight >= container.scrollHeight) scroll = false;		// content fits in the div, no need to scroll
		
		W.Event.add(container, 'mousewheel', function(e) { obj.scrollwheel(e); });
		
		return scroll;
	},
	
	
	
	init_scrollbar_bg : function()
	{
		var config					= this.config;
		var obj						= this;
		
		var bg						= document.createElement('div');
		bg.style.backgroundColor	= config.bgcolor;
		bg.style.height				= W.Dom.getStyle(this.div, 'height');
		bg.style.width				= config.bgwidth;
		bg.style.margin				= config.bgmargin;
		
		bg.style.position			= 'absolute';
		bg.style.right				= config.bgoffset;
		bg.style.top				= 0;
		
		bg.id						= 'scrollbar_bg';
		
		this.scrollbar_bg			= this.div.appendChild(bg);
		//this.scrollbar_bg.onclick	= function(e) { obj.jump(e); };
		W.Event.add(this.scrollbar_bg, 'click', function(e) { obj.jump(e); });
	},
	
	
	
	init_scrollbar_handle : function()
	{
		var config						= this.config;
		var obj							= this;
		
		var handle						= document.createElement('div');
		handle.style.backgroundColor	= config.color;
		handle.style.width				= config.width;
		handle.style.position			= 'absolute';
		handle.style.left				= config.offset;
		
		handle = this.handle			= this.scrollbar_bg.appendChild(handle);
		
		if (config.up) {
			var up				= document.createElement('img');
			up.src				= config.up;
			up.style.position	= 'absolute';
			up.style.top		= '0';
			
			handle.appendChild(up);
		}
		
		if (config.down) {
			var down			= document.createElement('img');
			down.src			= config.down;
			down.style.position	= 'absolute';
			down.style.bottom	= '0';
			
			handle.appendChild(down);
		}
		
		
		if (config.height == 'auto') {
			var div_height			= this.div.offsetHeight;
			var height				= Math.round(div_height * div_height / this.div.scrollHeight);
			handle.style.height		= height + 'px';
		} else {
			handle.style.height		= config.height;
		}
		
		
		var handle_max = this.handle_max = parseInt(W.Dom.getStyle(this.div, 'height')) - parseInt(handle.style.height);
		W.Drag.init(handle, null, 0, parseInt(config.offset, 10), 0, handle_max);
		handle.onDrag = function(nx, ny) { obj.scroll(nx, ny); };
	},
	
	
	
	scroll : function(nx, ny)
	{
		if (!this.scroll_max) { this.scroll_max = this.div.scrollHeight - this.div.offsetHeight; }
		var dy	= Math.round(ny * this.scroll_max / this.handle_max);
		
		this.container.style.top	= -dy + 'px';
	},
	
	
	
	scrollwheel : function(e)
	{
		e	= e || window.event;
		
		var top		= W.Dom.getOffsets(this.handle).top;
		var scroll	= e.detail ? e.detail * -1 : e.wheelDelta / 40;
		var ny		= 0;
		
		if (scroll < 0) {															// scroll down
			ny		= this.handle.offsetTop + this.handle.offsetHeight + scroll;
			ny		= ny > this.handle_max ? this.handle_max : ny;

			this.handle.style.top	= ny + 'px';
			this.scroll(0, ny);
		} else if (scroll > 0) {													// scroll up
			ny		= this.handle.offsetTop - this.handle.offsetHeight + scroll;
			ny		= ny < 0 ? 0 : ny;

			this.handle.style.top	= ny + 'px';
			this.scroll(0, ny);
		}
	},
	
	
	
	jump : function(e)
	{
		e	= e || window.event;
		
		//alert(e.clientX + ', ' + e.clientY + ', ' + W.Dom.getOffsets(this.handle).top + ', ' + this.handle.offsetHeight);
		
		var top		= W.Dom.getOffsets(this.handle).top;
		var ny		= 0;
		
		if (e.clientY > (top + this.handle.offsetHeight)) {
			ny		= this.handle.offsetTop + this.handle.offsetHeight - this.overlap;
			ny		= ny > this.handle_max ? this.handle_max : ny;
		
			this.handle.style.top	= ny + 'px';
			this.scroll(0, ny);
		} else if (e.clientY < top) {
			ny		= this.handle.offsetTop - this.handle.offsetHeight + this.overlap;
			ny		= ny < 0 ? 0 : ny;
		
			this.handle.style.top	= ny + 'px';
			this.scroll(0, ny);
		}
	}

};