/* 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 4765 2008-02-17 21:14:59Z kelvinluck $ *//** * 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}, 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();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;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.scrollbarWidth+'px'}).append(jQuery('<div></div>').attr({'className':'jScrollPaneDragTop'}).css({'width':settings.scrollbarWidth+'px'}),jQuery('<div></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('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':'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></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;}));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) {positionDrag(destDragPosition);} else {_animateToPosition = destDragPosition;_animateToInterval = setInterval(animateToPosition, settings.animateInterval);}};$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;}});
