/* Copyright (c) 2006 Kelvin Luck (kelvin AT kelvinluck DOT com || http://www.kelvinluck.com)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) 
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 * 
 * See http://kelvinluck.com/assets/jquery/jScrollPane/
 * $Id: jScrollPane.js 1567 2007-03-23 01:34:29Z kelvin $
 */

/**
 * Replace the vertical scroll bars on any matched elements with a fancy
 * styleable (via CSS) version. With JS disabled the elements will
 * gracefully degrade to the browsers own implementation of overflow:auto.
 * If the mousewheel plugin has been included on the page then the scrollable areas will also
 * respond to the mouse wheel.
 *
 * @example jQuery(".scroll-pane").jScrollPane();
 *
 * @name jScrollPane
 * @type jQuery
 * @param Object	settings	hash with options, described below.
 *								scrollbarWidth	-	The width of the generated scrollbar in pixels
 *								scrollbarMargin	-	The amount of space to leave on the side of the scrollbar in pixels
 *								wheelSpeed		-	The speed the pane will scroll in response to the mouse wheel in pixels
 *								showArrows		-	Whether to display arrows for the user to scroll with
 *								arrowSize		-	The height of the arrow buttons if showArrows=true
 *								animateTo		-	Whether to animate when calling scrollTo and scrollBy
 *								dragMinHeight	-	The minimum height to allow the drag bar to be
 *								dragMaxHeight	-	The maximum height to allow the drag bar to be
 * @return jQuery
 * @cat Plugins/jScrollPane
 * @author Kelvin Luck (kelvin AT kelvinluck DOT com || http://www.kelvinluck.com)
 */
jQuery.jScrollPane = {
	active : []
};
jQuery.fn.jScrollPane = function(settings)
{
	settings = jQuery.extend(
		{
			scrollbarWidth : 10,
			scrollbarMargin : 5,
			wheelSpeed : 18,
			showArrows : false,
			arrowSize : undefined,
			animateTo : false,
			dragMinHeight : 1,
			dragMaxHeight : 99999,
			direction : "vertical",
			trackHeight : 620
		}, settings
	);
	return this.each(
		function()
		{
			if( $("#header")[0] ) {
				container = document.createElement('div');
				container.id = "containerForJscrollPane"
				$(container).css({ height: $(window).height() + "px", width: $(window).width() + "px", position: "absolute", left: 0, top: 0, "z-index": 1, background: "white" });
				$("#main").css("z-index","10");
				$("body").append(container);
				utils.addResizeEvent(function(){ $("#containerForJscrollPane").css("height", $(window).height() + "px"); });
			} else {
				container = document.createElement('div');
				container.id = "containerForJscrollPane"
				$(container).css({ height: $(window).height() + "px", width: $(window).width() + "px", position: "absolute", left: 0, top: 0, "z-index": 1});
				$("#main").css({ "z-index": 10, position: "relative" });
				$("body").append(container);
			}
			
			var $this = jQuery(this);
			
			if (jQuery(this).parent().is('.jScrollPaneContainer')) {
				var $c = jQuery(this).parent();
				var paneWidth = $c.innerWidth();
				var paneHeight = $c.outerHeight();
				var trackHeight = paneHeight;
				if ($c.unmousewheel) {
					$c.unmousewheel();
				}
				jQuery('>.jScrollPaneTrack, >.jScrollArrowUp, >.jScrollArrowDown', $c).remove();
				$this.css({'top':0});
			} else {
				this.originalPadding = $this.css('paddingTop') + ' ' + $this.css('paddingRight') + ' ' + $this.css('paddingBottom') + ' ' + $this.css('paddingLeft');
				this.originalSidePaddingTotal = (parseInt($this.css('paddingLeft')) || 0) + (parseInt($this.css('paddingRight')) || 0);
				var paneWidth = $this.innerWidth();
				
				if(settings.direction == "vertical")
					var paneHeight = $this.innerHeight();
				else if(settings.direction == "horizontal")
					var paneHeight = $this.parent().innerWidth();
				
				if(settings.direction == "vertical")
					var trackHeight = paneHeight;
				else if(settings.direction == "horizontal")
					var trackHeight = settings.trackHeight;
				
				$this.wrap(
					jQuery('<div>').attr(
						{'className':'jScrollPaneContainer'}
					).css(
						{
							'height':$this.innerHeight()+'px', 
							'width':paneWidth+'px'
						}
					)
				);
			}
			var p = this.originalSidePaddingTotal;
			
			if(settings.direction == "vertical")
				var cssWidth = paneWidth - settings.scrollbarWidth - settings.scrollbarMargin - p + 'px';
			else if(settings.direction == "horizontal")
				var cssWidth = "auto";
			
			$this.css(
				{
					'height':'auto',
					'width':cssWidth,
					'paddingRight':settings.scrollbarMargin + 'px'
				}
			);
			
			if(settings.direction == "vertical")
				var contentHeight = $this.outerHeight();
			else if(settings.direction == "horizontal")
				var contentHeight = $this.outerWidth();
			
			var percentInView = paneHeight / contentHeight;
			
			if (percentInView < .98) {
				var $container = $this.parent();
				
				if(settings.direction == "vertical") {
					$container.append(
						jQuery('<div>').attr({'className':'jScrollPaneTrack'}).css({'width':settings.scrollbarWidth+'px'}).append(
							jQuery('<div>').attr({'className':'jScrollPaneDrag'}).css({'width':settings.scrollbarWidth+'px'}).append(
								jQuery('<div>').attr({'className':'jScrollPaneDragTop'}).css({'width':settings.scrollbarWidth+'px'}),
								jQuery('<div>').attr({'className':'jScrollPaneDragBottom'}).css({'width':settings.scrollbarWidth+'px'})
							)
						)
					);
				} else if(settings.direction == "horizontal") {
					$container.append(
						jQuery('<div>').attr({'className':'jScrollPaneTrack'}).css({'height':settings.scrollbarHeight+'px','width':settings.scrollbarWidth+'px'}).append(
							jQuery('<div>').attr({'className':'jScrollPaneDrag'}).css({'height':settings.scrollbarHeight+'px'}).append(
								jQuery('<div>').attr({'className':'jScrollPaneDragTop'}).css({'height':settings.scrollbarHeight+'px'}),
								jQuery('<div>').attr({'className':'jScrollPaneDragBottom'}).css({'height':settings.scrollbarHeight+'px'})
							)
						)
					);
				}
				
				var $track = jQuery('>.jScrollPaneTrack', $container);
				var $drag = jQuery('>.jScrollPaneTrack .jScrollPaneDrag', $container);
				
				if (settings.showArrows) {
					
					var currentArrowButton;
					var currentArrowDirection;
					var currentArrowInterval;
					var currentArrowInc;
					var whileArrowButtonDown = function()
					{
						if (currentArrowInc > 4 || currentArrowInc%4==0) {
							positionDrag(dragPosition + currentArrowDirection * mouseWheelMultiplier);
						}
						currentArrowInc ++;
					};
					var onArrowMouseUp = function(event)
					{
						jQuery('body').unbind('mouseup', onArrowMouseUp);
						currentArrowButton.removeClass('jScrollActiveArrowButton');
						clearInterval(currentArrowInterval);
						//currentArrowButton.parent().removeClass('jScrollArrowUpClicked jScrollArrowDownClicked');
					};
					var onArrowMouseDown = function() {
						//currentArrowButton = $(this);
						jQuery('body').bind('mouseup', onArrowMouseUp);
						currentArrowButton.addClass('jScrollActiveArrowButton');
						currentArrowInc = 0;
						whileArrowButtonDown();
						currentArrowInterval = setInterval(whileArrowButtonDown, 100);
					};
					$container
						.append(
							jQuery('<a>')
								.attr({'href':'javascript:;', 'className':'jScrollArrowUp'})
								.css({'width':settings.scrollbarWidth+'px'})
								.html('Scroll up')
								.bind('mousedown', function()
								{
									currentArrowButton = $(this);
									currentArrowDirection = -1;
									onArrowMouseDown();
									this.blur();
									return false;
								}),
							jQuery('<a>')
								.attr({'href':'javascript:;', 'className':'jScrollArrowDown'})
								.css({'width':settings.scrollbarWidth+'px'})
								.html('Scroll down')
								.bind('mousedown', function()
								{
									currentArrowButton = $(this);
									currentArrowDirection = 1;
									onArrowMouseDown();
									this.blur();
									return false;
								})
						);
					if (settings.arrowSize) {
						trackHeight = paneHeight - settings.arrowSize - settings.arrowSize;
						$track
							.css({'height': trackHeight+'px', top:settings.arrowSize+'px'})
					} else {
						var topArrowHeight = jQuery('>.jScrollArrowUp', $container).height();
						trackHeight = paneHeight - topArrowHeight - jQuery('>.jScrollArrowDown', $container).height();
						$track
							.css({'height': trackHeight+'px', top:topArrowHeight+'px'})
					}
				}
				
				var $pane = jQuery(this).css({'position':'absolute', 'overflow':'visible'});
				
				var currentOffset;
				var maxY;
				var mouseWheelMultiplier;
				// store this in a seperate variable so we can keep track more accurately than just updating the css property..
				var dragPosition = 0;
				var dragMiddle = percentInView*paneHeight/2;
				
				// pos function borrowed from tooltip plugin and adapted...
				var getPos = function (event, c) {
					var p = c == 'X' ? 'Left' : 'Top';
					return event['page' + c] || (event['client' + c] + (document.documentElement['scroll' + p] || document.body['scroll' + p])) || 0;
				};
				
				var ignoreNativeDrag = function() {	return false; };
				
				var initDrag = function()
				{
					ceaseAnimation();
					currentOffset = $drag.offset(false);
					
					if(settings.direction == "vertical")
						currentOffset.top -= dragPosition;
					else if(settings.direction == "horizontal")
						currentOffset.left -= dragPosition;
					
					if(settings.direction == "vertical")
						maxY = trackHeight - $drag[0].offsetHeight;
					else if(settings.direction == "horizontal")
						maxY = trackHeight - $drag[0].offsetWidth;
					
					mouseWheelMultiplier = 2 * settings.wheelSpeed * maxY / contentHeight;
				};
				
				var onStartDrag = function(event)
				{
					initDrag();
					
					if(settings.direction == "vertical")
						dragMiddle = getPos(event, 'Y') - dragPosition - currentOffset.top;
					else if(settings.direction == "horizontal")
						dragMiddle = getPos(event, 'X') - dragPosition - currentOffset.left;
					
					jQuery('body').bind('mouseup', onStopDrag).bind('mousemove', updateScroll);
					
					if( !$("#header")[0] ) {
						$("#main").css({ "z-index": 1 });
						$("#containerForJscrollPane").css({ "z-index": 10 });
					}
					
					jQuery('#containerForJscrollPane').bind('mouseout', onStopDrag);
					
					if (jQuery.browser.msie) {
						jQuery('body').bind('dragstart', ignoreNativeDrag).bind('selectstart', ignoreNativeDrag);
					}
					return false;
				};
				var onStopDrag = function()
				{
					jQuery('body').unbind('mouseup', onStopDrag).unbind('mousemove', updateScroll);
					
					if( !$("#header")[0] ) {
						$("#main").css({ "z-index": 10 });
						$("#containerForJscrollPane").css({ "z-index": 1 });
					}
					
					jQuery('#containerForJscrollPane').unbind('mouseover', onStopDrag);
					
					if(settings.direction == "vertical")
						dragMiddle = percentInView*paneHeight/2;
					else if(settings.direction == "horizontal")
						dragMiddle = percentInView*paneWidth/2;
					
					if (jQuery.browser.msie) {
						jQuery('body').unbind('dragstart', ignoreNativeDrag).unbind('selectstart', ignoreNativeDrag);
					}
				};
				var positionDrag = function(destY)
				{
					destY = destY < 0 ? 0 : (destY > maxY ? maxY : destY);
					dragPosition = destY;
					
					if(settings.direction == "vertical")
						$drag.css({'top':destY+'px'});
					else if(settings.direction == "horizontal")
						$drag.css({'left':destY+'px'});
						
					
					var p = destY / maxY;
					
					if(settings.direction == "vertical")
						$pane.css({'top':((paneHeight-contentHeight)*p) + 'px'});
					else if(settings.direction == "horizontal")
						$pane.css({'left':((paneHeight-contentHeight)*p) + 'px'});
				};
				var updateScroll = function(e)
				{
					if(settings.direction == "vertical")
						positionDrag(getPos(e, 'Y') - currentOffset.top - dragMiddle);
					else if(settings.direction == "horizontal")
						positionDrag(getPos(e, 'X') - currentOffset.left - dragMiddle);
				};
				
				var dragH = Math.max(Math.min(percentInView*paneHeight, settings.dragMaxHeight), settings.dragMinHeight);
				$drag.css(
					{'height':dragH+'px'}
				).bind('mousedown', onStartDrag);
				
				var trackScrollMousePos;
				var doTrackScroll = function(event)
				{
					if(settings.direction == "vertical")
						trackScrollMousePos = getPos(event, 'Y') - currentOffset.top - ($drag[0].offsetHeight/2);
					else if(settings.direction == "horizontal")
						trackScrollMousePos = getPos(event, 'X') - currentOffset.left - ($drag[0].offsetWidth/2);
					
					positionDrag(trackScrollMousePos);
				};
				var onTrackClick = function(event)
				{
					initDrag();
					doTrackScroll(event);
				};
				
				$track.bind('mousedown', onTrackClick);
				
				// if the mousewheel plugin has been included then also react to the mousewheel
				if ($container.mousewheel) {
					$container.mousewheel(
						function (event, delta) {
							initDrag();
							ceaseAnimation();
							var d = dragPosition;
							positionDrag(dragPosition - delta * mouseWheelMultiplier);
							var dragOccured = d != dragPosition;
							return !dragOccured;
						},
						false
					);					
				}
				var _animateToPosition;
				var _animateToInterval;
				function animateToPosition()
				{
					var diff = (_animateToPosition - dragPosition) / 3;
					if (diff > 1 || diff < -1) {
						positionDrag(dragPosition + diff);
					} else {
						positionDrag(_animateToPosition);
						ceaseAnimation();
					}
				}
				var ceaseAnimation = function()
				{
					if (_animateToInterval) {
						clearInterval(_animateToInterval);
						delete _animateToPosition;
					}
				};
				var scrollTo = function(pos)
				{
					ceaseAnimation();
					var destDragPosition = -pos/(paneHeight-contentHeight) * maxY;
					if (settings.animateTo) {
						_animateToPosition = destDragPosition;
						_animateToInterval = setInterval(animateToPosition, 100);
					} else {
						positionDrag(destDragPosition);
					}
				};
				$this[0].scrollTo = scrollTo;
				
				$this[0].scrollBy = function(delta)
				{
					var currentPos = -parseInt($pane.css('top')) || 0;
					scrollTo(currentPos + delta);
				};
				
				initDrag();
				
				jQuery.jScrollPane.active.push($this[0]);

			} else {
				if(settings.direction == "vertical"){
					$this.css(
						{
							'height':paneHeight+'px',
							'width':paneWidth-this.originalSidePaddingTotal+'px',
							'padding':this.originalPadding
						}
					);
				} else if(settings.direction == "horizontal"){
					$this.css(
						{
							'width':paneHeight+'px',
							'height':paneWidth-this.originalSidePaddingTotal+'px',
							'padding':this.originalPadding
						}
					);
				}
				// remove from active list?
			}
		}
	)
};

// clean up the scrollTo expandos
jQuery(window)
	.bind('unload', function() {
		var els = jQuery.jScrollPane.active; 
		for (var i=0; i<els.length; i++) {
			els[i].scrollTo = els[i].scrollBy = null;
		}
	}
);