(function($) 
{
	$.fn.jScroll = function(action, options) 
	{
		var defaults = 
		{
			animate: true,
			debug: false,
			position: 'right',
			scrollDelta: 10
		};
		
		// Simple debug function.
		function log($panel) 
		{
			if (options.debug) 
			{
				var $list = $panel.children('.jscroll-list');
				var $bar = $panel.children('.jscroll-bar');
				var $output = $panel.children('.jscroll-output');
				if ($output.length == 0) 
				{
					$output = $panel.prepend('<div class="jscroll-output"></div>').children(':first');
				}
				$output.text('panelHeight: ' + $panel.get(0).offsetHeight + ' ; listHeight: ' + $list.get(0).offsetHeight + ' ; max: ' + $bar.slider('option', 'max') + ' ; value: ' + $bar.slider('value'));
			}
		}
		
		// Get the size of the hidden part.
		function getScrollDelta($list) 
		{
			return $list.get(0).offsetHeight - $list.parent('.jscroll').get(0).offsetHeight;
		}
		
		if (action == null || typeof action == 'object') 
		{
			options = action;
			options = $.extend(defaults, options);
			
			return this.each(function() 
			{
				var $list = $(this).addClass('jscroll-list').css('position', 'relative').css('float', 'left').css('top', 0);
				var $panel = $list.wrap('<div class="jscroll"></div>').parent().css('position', 'relative').css('overflow', 'hidden');
				var $bar;
				if (options.position == 'right') 
				{
					$bar = $list.after('<div class="jscroll-bar"></div>').next();
				} else 
				{
					$bar = $list.before('<div class="jscroll-bar"></div>').prev();
				}
				$bar.css('float', 'left');
				var sDelta = getScrollDelta($list);
				
				$bar.slider(
				{
					animate: options.animate,
					orientation: 'vertical',
					range: false,
					min: 0,
					max: sDelta,
					value: sDelta,
					slide: function(event, ui) {
						$panel.children('.jscroll-list').css('top', parseInt('-' + ($bar.slider('option', 'max') - ui.value)));
						log($panel);
					},
					change: function(event, ui) {
						// call by the mousewheel
						var max = $bar.slider('option', 'max');
						$panel.children('.jscroll-list').css('top', parseInt('-' + (max - ((ui.value > max) ? max : (ui.value < 0) ? 0 : ui.value))));
						log($panel);
					}
				});
				if (typeof $list.mousewheel != 'undefined') {
					$list.mousewheel(function(event, delta) {
						if ($bar.css('display') == 'block') {
							// do not update anything if the scrollbar is hidden.
							$bar.slider('value', $bar.slider('value') + ((delta > 0) ? options.scrollDelta : parseInt('-' + options.scrollDelta)));
							event.preventDefault();
						}
					});
				}
				if (sDelta <= 0) $bar.css('display', 'none');
			});
		} else {
			return this.each(function() {
				var $list = $(this);
				switch (action) {
					case 'destroy':
						// Remove scroll tags.
						var $panel = $list.parent('.jscroll');
						$list.removeClass('jscroll-list').attr('style', '');
						$panel.children('.jscroll-bar').slider('destroy').remove();
						$panel.children('.jscroll-output').remove();
						$panel.replaceWith($panel.html());
						break;
					case 'update':
						// Update the size of the hidden part.
						var $bar = $list.parent('.jscroll').children('.jscroll-bar');
						var sDelta = getScrollDelta($list);
						if (sDelta > 0) {
							$bar.slider('option', 'max', sDelta);
							$bar.slider('value', sDelta);
							$bar.css('display', 'block');
						} else {
							$bar.css('display', 'none');
						}
						break;
				}
			});
		}
	};
})(jQuery);
