/* 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 4702 2008-02-09 10:19: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)
*								scrollbarOnLeft	-	Display the scrollbar on the left side?  (needs stylesheet changes, see examples.html)
* @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,
		    animateInterval: 100,
		    animateStep: 3,
		    maintainPosition: true,
		    scrollbarOnLeft: false,
		    scrollbarDragWidth: 0,
		    scrollbarDragBorder: 0,
		    forcePaneWidth: 0
		}, 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;
		        if ($c.unmousewheel) {
		            $c.unmousewheel();
		        }
		        jQuery('>.jScrollPaneTrack, >.jScrollArrowUp, >.jScrollArrowDown', $c).remove();
		        $this.css({ 'top': 0 });
		    } else {
		        var currentScrollPosition = 0;
		        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.forcePaneWidth > 0 && settings.forcePaneWidth > paneWidth)
		            paneWidth = settings.forcePaneWidth;
		        var paneHeight = $this.innerHeight();
		        var trackHeight = paneHeight;
		        $this.wrap(
					jQuery('<div></div>').attr(
						{ 'className': 'jScrollPaneContainer' }
    				).css(
						{
						    'height': paneHeight + 'px',
						    'width': paneWidth + 'px'
						}
					)
				);
		        // deal with text size changes (if the jquery.em plugin is included)
		        // and re-initialise the scrollPane so the track maintains the
		        // correct size
		        jQuery(document).bind(
					'emchange',
					function(e, cur, prev) {
					    $this.jScrollPane(settings);
					}
				);
		    }
		    var p = this.originalSidePaddingTotal;

		    var cssToApply = {
		        'height': 'auto',
		        'width': paneWidth - settings.scrollbarWidth - settings.scrollbarMargin - p + 'px'
		    }

		    if (settings.scrollbarOnLeft) {
		        cssToApply.paddingLeft = settings.scrollbarMargin + settings.scrollbarWidth + 'px';
		    } else {
		        cssToApply.paddingRight = settings.scrollbarMargin + 'px';
		    }

		    $this.css(cssToApply);

		    var contentHeight = $this.outerHeight();
		    var percentInView = paneHeight / contentHeight;
		    var marginLeftPaneDrag = 0;
		    if (settings.scrollbarDragWidth == 0)
		        settings.scrollbarDragWidth = settings.scrollbarWidth;
		    if (settings.scrollbarWidth > settings.scrollbarDragWidth)
		        marginLeftPaneDrag = Math.floor((settings.scrollbarWidth - settings.scrollbarDragWidth) / 2) - settings.scrollbarDragBorder;

		    if (percentInView < .99) {
		        var $container = $this.parent();
		        $container.append(
					jQuery('<div></div>').attr({ 'className': 'jScrollPaneTrack' }).css({ 'width': settings.scrollbarWidth + 'px' }).append(
						jQuery('<div></div>').attr({ 'className': 'jScrollPaneDrag' }).css({ 'width': settings.scrollbarDragWidth + 'px', 'margin-left': marginLeftPaneDrag + 'px' }).append(
							jQuery('<div></div>').attr({ 'className': 'jScrollPaneDragTop' }).css({ 'width': settings.scrollbarDragWidth + 'px', 'margin-left': marginLeftPaneDrag + 'px' }),
							jQuery('<div></div>').attr({ 'className': 'jScrollPaneDragBottom' }).css({ 'width': settings.scrollbarDragWidth + 'px', 'margin-left': marginLeftPaneDrag + '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('html').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('html').bind('mouseup', onArrowMouseUp);
		                currentArrowButton.addClass('jScrollActiveArrowButton');
		                currentArrowInc = 0;
		                whileArrowButtonDown();
		                currentArrowInterval = setInterval(whileArrowButtonDown, 100);
		            };
		            $container
						.append(
							jQuery('<a></a>')
								.attr({ 'href': '#', 'className': 'jScrollArrowUp' }) //cambiato javascript:; con #
								.css({ 'width': settings.scrollbarWidth + 'px' })
								.html('Scroll up')
								.bind('mousedown', function() {
								    currentArrowButton = jQuery(this);
								    currentArrowDirection = -1;
								    onArrowMouseDown();
								    this.blur();
								    return false;
								}),
							jQuery('<a></a>')
								.attr({ 'href': '#', 'className': 'jScrollArrowDown' }) //cambiato javascript:; con #
								.css({ 'width': settings.scrollbarWidth + 'px' })
								.html('Scroll down')
								.bind('mousedown', function() {
								    currentArrowButton = jQuery(this);
								    currentArrowDirection = 1;
								    onArrowMouseDown();
								    this.blur();
								    return false;
								})
						);
		            var $upArrow = jQuery('>.jScrollArrowUp', $container);
		            var $downArrow = jQuery('>.jScrollArrowDown', $container);
		            if (settings.arrowSize) {
		                trackHeight = paneHeight - settings.arrowSize - settings.arrowSize;
		                $track
							.css({ 'height': trackHeight + 'px', top: settings.arrowSize + 'px' })
		            } else {
		                var topArrowHeight = $upArrow.height();
		                settings.arrowSize = topArrowHeight;
		                trackHeight = paneHeight - topArrowHeight - $downArrow.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('html').bind('mouseup', onStopDrag).bind('mousemove', updateScroll);
		            if (jQuery.browser.msie) {
		                jQuery('html').bind('dragstart', ignoreNativeDrag).bind('selectstart', ignoreNativeDrag);
		            }
		            return false;
		        };
		        var onStopDrag = function() {
		            jQuery('html').unbind('mouseup', onStopDrag).unbind('mousemove', updateScroll);
		            dragMiddle = percentInView * paneHeight / 2;
		            if (jQuery.browser.msie) {
		                jQuery('html').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');
		            if (settings.showArrows) {
		                $upArrow[destY == 0 ? 'addClass' : 'removeClass']('disabled');
		                $downArrow[destY == maxY ? 'addClass' : 'removeClass']('disabled');
		            }
		        };
		        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('html').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('html').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 = jQuery(pos, this);
		                if (!$e.length) return;
		                pos = $e.offset().top - $this.offset().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?
		    }

		}
	)
};

// 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;
	    }
	}
);
