/* 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 3125 2007-09-06 20:39:42Z kelvin.luck $
*/
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
}, 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'
}
)
);
jQuery(document).bind(
'emchange', 
function(e, cur, prev)
{
$this.jScrollPane(settings);
}
);
}
var p = this.originalSidePaddingTotal;
$this.css(
{
'height':'auto',
'width':paneWidth - settings.scrollbarWidth - settings.scrollbarMargin - p + 'px',
'paddingRight':settings.scrollbarMargin + 'px'
}
);
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('body').unbind('mouseup', onArrowMouseUp);
currentArrowButton.removeClass('jScrollActiveArrowButton');
clearInterval(currentArrowInterval);
};
var onArrowMouseDown = function() {
jQuery('body').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;
})
);
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;
var dragPosition = 0;
var dragMiddle = percentInView*paneHeight/2;
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 ($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({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
}
);
}
}
)
};
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;
}
}
);
