/* 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 2762 2007-08-18 11:04:47Z kelvin.luck $
 */

/**
 * 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
 *								animateInterval	-	The interval in milliseconds to update an animating scrollPane (default 100)
 *								animateStep		-	The amount to divide the remaining scroll distance by when animating (default 3)
 *								maintainPosition-	Whether you want the contents of the scroll pane to maintain it's position when you re-initialise it - so it doesn't scroll as you add more content (default true)
 * @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 : 0,
			animateTo : false,
			dragMinHeight : 1,
			dragMaxHeight : 99999,
			dragMinWidth : 1,
			dragMaxWidth : 99999,
			animateInterval : 100,
			animateStep: 3,
			maintainPosition: true,
			horizontalScroller: false
		}, settings
	);
	return this.each(
		function()
		{
			var $this = jQuery(this);
			
			if (jQuery(this).parent().is('.jScrollPaneContainer')) {
			
				var currentScrollPosition = settings.maintainPosition ? $this.offset({relativeTo:jQuery(this).parent()[0]}).top : 0;
				var $c = jQuery(this).parent();
				var paneWidth = $c.innerWidth();
				var paneHeight = $c.outerHeight();
				var trackHeight = paneHeight;
				var trackWidth = paneWidth;
				if ($c.unmousewheel) {
					$c.unmousewheel();
				}
				if(!settings.horizontalScroller){
					jQuery('>.jScrollPaneTrack, >.jScrollArrowUp, >.jScrollArrowDown', $c).remove();
					$this.css({'top':0});
				} else {
					jQuery('>.jScrollPaneTrack, >.jScrollArrowUp, >.jScrollArrowDown', $c).remove();
					$this.css({'left':0});
				}
			} else {
				var currentScrollPosition = 0;
				this.originalPadding = $this.css('paddingTop') + ' ' + $this.css('paddingRight') + ' ' + $this.css('paddingBottom') + ' ' + $this.css('paddingLeft');

				if(!settings.horizontalScroller){
					this.originalSidePaddingTotal = (parseInt($this.css('paddingLeft')) || 0) + (parseInt($this.css('paddingRight')) || 0);
				}
				else{
					this.originalSidePaddingTotal = (parseInt($this.css('paddingTop')) || 0) + (parseInt($this.css('paddingBottom')) || 0);
				}

				this.originalMargin = $this.css('marginTop') + ' ' + $this.css('marginRight') + ' ' + $this.css('marginBottom') + ' ' + $this.css('marginLeft');

				var paneWidth = $this.innerWidth();
				var paneHeight = $this.innerHeight();
				var trackHeight = paneHeight;
				
				var trackWidth = paneWidth;
				$this.wrap(
					jQuery('<div>').attr(
						{'className':'jScrollPaneContainer'}
					).css(
						{
							'height':paneHeight+'px', 
							'width':paneWidth+'px',
							'margin':this.originalMargin
						}
					)
				);
				$this.css({'margin':0});
				// deal with text size changes (if the jquery.em plugin is included)
				// and re-initialise the scrollPane so the track maintains the
				// correct size
				$(document).bind(
					'emchange', 
					function(e, cur, prev)
					{
						$this.jScrollPane(settings);
					}
				);	
			}
			var p = this.originalSidePaddingTotal;
			if (!settings.horizontalScroller){
			
				$this.css(
					{
						//Mis en commentaire par Riad
						'height':'auto',
						//'width':paneWidth - settings.scrollbarWidth - settings.scrollbarMargin - p + 'px',
						'paddingRight':settings.scrollbarMargin + 'px'
					}
				);
								
								
				var contentHeight = $this.outerHeight();										
				var contentWidth = $this.outerWidth();
				var percentInView = paneHeight / contentHeight;

				if (percentInView < .99) 
				{
					var $container = $this.parent();
					$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'})
							)
						)
					);
					
					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);
							//console.log($(event.target));
							//currentArrowButton.parent().removeClass('jScrollArrowUpClicked jScrollArrowDownClicked');
						};
						var onArrowMouseDown = function() {
							//console.log(direction);
							//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 = jQuery(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 = jQuery(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();
							settings.arrowSize = topArrowHeight;
							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);
						currentOffset.top -= dragPosition;
						maxY = trackHeight - $drag[0].offsetHeight;
						mouseWheelMultiplier = 2 * settings.wheelSpeed * maxY / contentHeight;
					};
					
					var onStartDrag = function(event)
					{
						initDrag();
						dragMiddle = getPos(event, 'Y') - dragPosition - currentOffset.top;
						jQuery('body').bind('mouseup', onStopDrag).bind('mousemove', updateScroll);
						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);
						dragMiddle = percentInView*paneHeight/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;
						$drag.css({'top':destY+'px'});
						var p = destY / maxY;
						$pane.css({'top':((paneHeight-contentHeight)*p) + 'px'});
						$this.trigger('scroll');
					};
					var updateScroll = function(e)
					{
						positionDrag(getPos(e, 'Y') - currentOffset.top - dragMiddle);
					};
					
					var dragH = Math.max(Math.min(percentInView*(paneHeight-settings.arrowSize*2), settings.dragMaxHeight), settings.dragMinHeight);
					
					$drag.css(
						{'height':dragH+'px'}
					).bind('mousedown', onStartDrag);
					
					var trackScrollInterval;
					var trackScrollInc;
					var trackScrollMousePos;
					var doTrackScroll = function()
					{
						if (trackScrollInc > 8 || trackScrollInc%4==0) {
							positionDrag((dragPosition - ((dragPosition - trackScrollMousePos) / 2)));
						}
						trackScrollInc ++;
					};
					var onStopTrackClick = function()
					{
						clearInterval(trackScrollInterval);
						jQuery('body').unbind('mouseup', onStopTrackClick).unbind('mousemove', onTrackMouseMove);
					};
					var onTrackMouseMove = function(event)
					{
						trackScrollMousePos = getPos(event, 'Y') - currentOffset.top - dragMiddle;
					};
					var onTrackClick = function(event)
					{
						initDrag();
						onTrackMouseMove(event);
						trackScrollInc = 0;
						jQuery('body').bind('mouseup', onStopTrackClick).bind('mousemove', onTrackMouseMove);
						trackScrollInterval = setInterval(doTrackScroll, 100);
						doTrackScroll();
					};
					
					$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) / settings.animateStep;
						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, preventAni)
					{
						if (typeof pos == "string") {
							$e = $(pos, this);
							if (!$e.length) return;
							pos = $e.offset({relativeTo:this}).top;
						}
						ceaseAnimation();
						var destDragPosition = -pos/(paneHeight-contentHeight) * maxY;
						if (!preventAni || settings.animateTo) {
							_animateToPosition = destDragPosition;
							_animateToInterval = setInterval(animateToPosition, settings.animateInterval);
						} else {
							positionDrag(destDragPosition);
						}
					};
					$this[0].scrollTo = scrollTo;
					
					$this[0].scrollBy = function(delta)
					{
						var currentPos = -parseInt($pane.css('top')) || 0;
						scrollTo(currentPos + delta);
					};
					
					initDrag();
					
					scrollTo(-currentScrollPosition, true);
					
					jQuery.jScrollPane.active.push($this[0]);
	
				}
				else {
					/*
					$this.css(
						{
							'height':paneHeight+'px',
							'width':paneWidth-this.originalSidePaddingTotal+'px',
							'padding':this.originalPadding
						}
					);*/
					// remove from active list?
				}
			}
			else{

				$this.css(
					{
						//'width':'auto',
						'height':paneHeight - settings.scrollbarWidth - settings.scrollbarMargin - p + 'px',
						'paddingBottom':settings.scrollbarMargin + 'px'
					}
				);
				
				var contentHeight = $this.outerHeight();
				var contentWidth = $this.children(':first-child').outerWidth();
				var percentInView = paneHeight / contentHeight;
				var percentInViewH = paneWidth / contentWidth;
				
				if (percentInViewH < 1) {
					//alert('opa ' + percentInViewH);
					var $container = $this.parent();
					
					//alert($container.nodeName);
					
					$container.append(
						jQuery('<div>').attr({'className':'jScrollPaneTrackH'}).css({'height':settings.scrollbarWidth+'px'}).append(
							jQuery('<div>').attr({'className':'jScrollPaneDragH'}).css({'height':settings.scrollbarWidth+'px'}).append(
								jQuery('<div>').attr({'className':'jScrollPaneDragLeft'}).css({'height':settings.scrollbarWidth+'px'}),
								jQuery('<div>').attr({'className':'jScrollPaneDragRight'}).css({'height':settings.scrollbarWidth+'px'})
							)
						)
					);
					
					var $track = jQuery('>.jScrollPaneTrackH', $container);
					var $drag = jQuery('>.jScrollPaneTrackH .jScrollPaneDragH', $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);
							//console.log($(event.target));
							//currentArrowButton.parent().removeClass('jScrollArrowUpClicked jScrollArrowDownClicked');
						};
						var onArrowMouseDown = function() {
							//console.log(direction);
							//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 = jQuery(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 = jQuery(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();
							settings.arrowSize = topArrowHeight;
							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 maxX;
					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 = percentInViewH*paneWidth/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);
						currentOffset.left -= dragPosition;
						maxX = trackWidth - $drag[0].offsetWidth;
						mouseWheelMultiplier = 2 * settings.wheelSpeed * maxX / contentWidth;
					};
					
					var onStartDrag = function(event)
					{
						initDrag();
						dragMiddle = getPos(event, 'X') - dragPosition - currentOffset.left;
						jQuery('body').bind('mouseup', onStopDrag).bind('mousemove', updateScroll);
						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);
						dragMiddle = percentInViewH*paneWidth/2;
						if (jQuery.browser.msie) {
							jQuery('body').unbind('dragstart', ignoreNativeDrag).unbind('selectstart', ignoreNativeDrag);
						}
					};
					var positionDrag = function(destX)
					{
						destX = destX < 0 ? 0 : (destX > maxX ? maxX : destX);
						dragPosition = destX;
						$drag.css({'left':destX+'px'});
						var p = destX / maxX;
						$pane.css({'left':((paneWidth-contentWidth)*p) + 'px'});
						$this.trigger('scroll');
					};
					var updateScroll = function(e)
					{
						positionDrag(getPos(e, 'X') - currentOffset.left - dragMiddle);
					};
					
					var dragH = Math.max(Math.min(percentInViewH*(paneWidth-settings.arrowSize*2), settings.dragMaxWidth), settings.dragMinWidth);
					
					$drag.css(
						{'width':dragH+'px'}
					).bind('mousedown', onStartDrag);
					
					var trackScrollInterval;
					var trackScrollInc;
					var trackScrollMousePos;
					var doTrackScroll = function()
					{
						if (trackScrollInc > 8 || trackScrollInc%4==0) {
							positionDrag((dragPosition - ((dragPosition - trackScrollMousePos) / 2)));
						}
						trackScrollInc ++;
					};
					var onStopTrackClick = function()
					{
						clearInterval(trackScrollInterval);
						jQuery('body').unbind('mouseup', onStopTrackClick).unbind('mousemove', onTrackMouseMove);
					};
					var onTrackMouseMove = function(event)
					{
						trackScrollMousePos = getPos(event, 'X') - currentOffset.left - dragMiddle;
					};
					var onTrackClick = function(event)
					{
						initDrag();
						onTrackMouseMove(event);
						trackScrollInc = 0;
						jQuery('body').bind('mouseup', onStopTrackClick).bind('mousemove', onTrackMouseMove);
						trackScrollInterval = setInterval(doTrackScroll, 100);
						doTrackScroll();
					};
					
					$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) / settings.animateStep;
						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, preventAni)
					{
						if (typeof pos == "string") {
							$e = $(pos, this);
							if (!$e.length) return;
							pos = $e.offset({relativeTo:this}).left;
						}
						ceaseAnimation();
						var destDragPosition = -pos/(paneHeight-contentHeight) * maxX;
						if (!preventAni || settings.animateTo) {
							_animateToPosition = destDragPosition;
							_animateToInterval = setInterval(animateToPosition, settings.animateInterval);
						} else {
							positionDrag(destDragPosition);
						}
					};
					$this[0].scrollTo = scrollTo;
					
					$this[0].scrollBy = function(delta)
					{
						var currentPos = -parseInt($pane.css('left')) || 0;
						scrollTo(currentPos + delta);
					};
					
					initDrag();
					
					scrollTo(-currentScrollPosition, true);
					
					jQuery.jScrollPane.active.push($this[0]);
	
				}
				else {
					$this.css(
						{
							'width':paneWidth+'px',
							'height':paneHeight-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;
		}
	}
);