(function($){
	$.fn.removeScrollBox=function(){
		var $this=$(this);
		$this.find('.sbar').remove();
		$this.find('.content').each(function(){
			$(this).replaceWith($(this).html());
		});
		$this.height('auto');
		return this;
	}
	$.fn.scrollBox=function(options){
		if (this.length > 1) return this.each(function(){$(this).scrollBox(options)});
		var options=$.extend({
				boxSize:100,
				arrowScroll:20,//amount to scroll with arrows
				wheelScroll:40,//amount to scroll with wheel
				scrollBarWidth:20,
				arrowSize:20,//the final size of arrows is (scrollBarWidth x arrowSize)
				sliderSize:0,//if 0 or false then proportional
				sliderSizeMin:10,
				scrollTimeout:250
			}, options);
		var $box=$(this);
		var $content;
		var $sbar=$('<div class="sbar" />');
		var $up=$('<div class="up" />');
		var $down=$('<div class="down" />');
		var $slider=$('<div class="slider" />');
		var ratio=1;
		var scrollTimerId=false;
		var scrollSize=options.arrowScroll;
		var scrollY=0;
		
		this.initialize = function(){
			$box
				.css('overflow', 'hidden')
				.wrapInner('<div class="content" />')
				.prepend($sbar);
			$content=$box.find('.content')
				.css('position', 'relative')
				.css('margin-right', options.scrollBarWidth);
			if($content.height()){
				options.boxSize=Math.min($content.height(), options.boxSize);
			}
			$box.height(options.boxSize);
			$up
				.css('position', 'absolute')
				.css('top', 0)
				.height(options.arrowSize);
			$down
				.css('position', 'absolute')
				.css('bottom', 0)
				.height(options.arrowSize);
			$slider
				.css('position', 'absolute')
				.css('top', options.arrowSize);
			$sbar
				.height(options.boxSize)
				.width(options.scrollBarWidth)
				.css('position', 'absolute')
				.css('right', 0)
				.css('top', 0)
				.append($up)
				.append($slider)
				.append($down);
			
			this.update();
			setEvents();
		}
		this.update = function(){
			var sh=(options.sliderSize
						?options.sliderSize
						:($content.height()>options.boxSize
							? Math.round(
								0.5
								*($content.height()+options.boxSize-4*options.arrowSize
									-Math.sqrt(
										($content.height()+3*options.boxSize-8*options.arrowSize)
										*($content.height()-options.boxSize)
									)
								)
							)
							: options.boxSize-2*options.arrowSize
						)
					);
			$slider
				.css('top', options.arrowSize)
				.height(sh);
			$slider.height(sh, options.sliderSizeMin);
			ratio=(options.boxSize-2*options.arrowSize-$slider.height())/($content.height()-options.boxSize);
			$sbar.toggleClass('disabled', 0==ratio || isNaN(ratio));
			$content.css('top', 0);
		}
		function setEvents(){
			if($box[0].addEventListener){
				$box[0].addEventListener('DOMMouseScroll', wheel, false);
				$box[0].addEventListener('mousewheel', wheel, false );
			}
			else{
				$box[0].onmousewheel = wheel;
			}
			$sbar.click(function(e){return false});
			$sbar.bind('mousedown', startScroll);
			$slider.bind('mousedown', startDrag);
		}
		function wheel(e){
			e = $.event.fix(e || window.event);
			var delta = e.wheelDelta ? e.wheelDelta/120 : -e.detail/3;
			e.preventDefault();
			addScroll(-delta*options.wheelScroll);
		}
		function startScroll(e){
			var dir;
			var delta;
			
			if($(e.target).hasClass('down')){
				dir=1;
				delta=options.arrowScroll;
			}
			else{
				dir=(e.offsetY<$slider.position().top) ? -1 : 1;
				delta=( e.offsetY<options.arrowSize || e.offsetY>(options.boxSize-options.arrowSize) )
						? options.arrowScroll
						: options.wheelScroll;
			}
			scrollSize=dir*delta;
			scrollY=e.offsetY;
			$sbar.bind('mousemove', function(e){scrollY=e.offsetY;});
			$sbar.bind('mouseup', endScroll);
			$sbar.bind('mouseleave', endScroll);
			$sbar.find('div').bind('mouseenter', endScroll);
			doScroll();
			return false;
		}
		function checkScroll(){
			return !( scrollY>=$slider.position().top && scrollY<($slider.position().top+$slider.height()) );
		}
		function doScroll(){
			if(!checkScroll()){
				endScroll();
			}
			else{
				addScroll(scrollSize);
				clearTimeout(scrollTimerId);
				scrollTimerId=setTimeout(doScroll, options.scrollTimeout);
			}
		}
		function endScroll(){
			clearTimeout(scrollTimerId);
			$sbar.unbind('mousemove');
			$sbar.unbind('mouseup', endScroll);
			$sbar.unbind('mouseleave', endScroll);
			$sbar.find('div').unbind('mouseenter', endScroll);
		}
		function startDrag(e){
			$slider[0].pageY=e.pageY;
			$slider[0].posY=$slider.position().top;
			$(document).bind('mousemove', doDrag);
			$(document).bind('mouseup', endDrag);
			return false;
		}
		function doDrag(e){
			var delta=$slider[0].pageY-e.pageY;
			var sPos=-delta+$slider[0].posY;
			if(sPos<options.arrowSize) sPos=options.arrowSize;
			if(sPos>options.boxSize-options.arrowSize-$slider.height()) sPos=options.boxSize-options.arrowSize-$slider.height();
			$slider.css('top', sPos);
			$content.css('top', (options.arrowSize-sPos)/ratio);
			return false;
		}
		function endDrag(){
			$(document).unbind('mousemove');
			$(document).unbind('mouseup');
			return false;
		}
		this.addScroll = function(delta){addScroll(delta);}
		function addScroll(delta){
			if($sbar.hasClass('disabled')) return false;
			var t=$content.position().top;
			if( t>delta ) delta=t;
			if( t+$content.height()-options.boxSize<delta  ) delta=$content.height()-options.boxSize+t;
			$content.css('top', t-delta);
			$slider.css('top', options.arrowSize-ratio*$content.position().top);
		}
		this.initialize();
		return $(this);
	}
})(jQuery);
