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: 20,
        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'
            }));
            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);
                };
                var onArrowMouseDown = function () {
                    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;
            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('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 ($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
            });
        }
    })
};
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;
    }
});
