﻿
(function ($) {
    $.jScrollPane = { active: [] }; $.fn.jScrollPane = function (settings) {
        settings = $.extend({}, $.fn.jScrollPane.defaults, settings); var rf = function () { return false; }; return this.each(function () {
            var $this = $(this); var paneEle = this; var currentScrollPosition = 0; var paneWidth; var paneHeight; var trackHeight; var trackOffset = settings.topCapHeight; var $container; if ($(this).parent().is('.jScrollPaneContainer')) { $container = $(this).parent(); currentScrollPosition = settings.maintainPosition ? $this.position().top : 0; var $c = $(this).parent(); paneWidth = $c.innerWidth(); paneHeight = $c.outerHeight(); $('>.jScrollPaneTrack, >.jScrollArrowUp, >.jScrollArrowDown, >.jScrollCap', $c).remove(); $this.css({ 'top': 0 }); } else {
                $this.data('originalStyleTag', $this.attr('style')); $this.css('overflow', 'hidden'); 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); paneWidth = $this.innerWidth(); paneHeight = $this.innerHeight(); $container = $('<div></div>').attr({ 'className': 'jScrollPaneContainer' }).css({ 'height': paneHeight + 'px', 'width': paneWidth + 'px' }); if (settings.enableKeyboardNavigation) { $container.attr('tabindex', settings.tabIndex); }
                $this.wrap($container); $container = $this.parent(); $(document).bind('emchange', function (e, cur, prev) { $this.jScrollPane(settings); });
            }
            trackHeight = paneHeight; if (settings.reinitialiseOnImageLoad) { var $imagesToLoad = $.data(paneEle, 'jScrollPaneImagesToLoad') || $('img', $this); var loadedImages = []; if ($imagesToLoad.length) { $imagesToLoad.each(function (i, val) { $(this).bind('load readystatechange', function () { if ($.inArray(i, loadedImages) == -1) { loadedImages.push(val); $imagesToLoad = $.grep($imagesToLoad, function (n, i) { return n != val; }); $.data(paneEle, 'jScrollPaneImagesToLoad', $imagesToLoad); var s2 = $.extend(settings, { reinitialiseOnImageLoad: false }); $this.jScrollPane(s2); } }).each(function (i, val) { if (this.complete || this.complete === undefined) { this.src = this.src; } }); }); }; }
            var p = this.originalSidePaddingTotal; var realPaneWidth = paneWidth - settings.scrollbarWidth - settings.scrollbarMargin - p; var cssToApply = { 'height': 'auto', 'width': realPaneWidth + '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 isScrollable = percentInView < .99; $container[isScrollable ? 'addClass' : 'removeClass']('jScrollPaneScrollable'); if (isScrollable) {
                $container.append($('<div></div>').addClass('jScrollCap jScrollCapTop').css({ height: settings.topCapHeight }), $('<div></div>').attr({ 'className': 'jScrollPaneTrack' }).css({ 'width': settings.scrollbarWidth + 'px' }).append($('<div></div>').attr({ 'className': 'jScrollPaneDrag' }).css({ 'width': settings.scrollbarWidth + 'px' }).append($('<div></div>').attr({ 'className': 'jScrollPaneDragTop' }).css({ 'width': settings.scrollbarWidth + 'px' }), $('<div></div>').attr({ 'className': 'jScrollPaneDragBottom' }).css({ 'width': settings.scrollbarWidth + 'px' }))), $('<div></div>').addClass('jScrollCap jScrollCapBottom').css({ height: settings.bottomCapHeight })); var $track = $('>.jScrollPaneTrack', $container); var $drag = $('>.jScrollPaneTrack .jScrollPaneDrag', $container); var currentArrowDirection; var currentArrowTimerArr = []; var currentArrowInc; var whileArrowButtonDown = function () {
                    if (currentArrowInc > 4 || currentArrowInc % 4 == 0) { positionDrag(dragPosition + currentArrowDirection * mouseWheelMultiplier); }
                    currentArrowInc++;
                }; if (settings.enableKeyboardNavigation) {
                    $container.bind('keydown.jscrollpane', function (e) { switch (e.keyCode) { case 38: currentArrowDirection = -1; currentArrowInc = 0; whileArrowButtonDown(); currentArrowTimerArr[currentArrowTimerArr.length] = setInterval(whileArrowButtonDown, 100); return false; case 40: currentArrowDirection = 1; currentArrowInc = 0; whileArrowButtonDown(); currentArrowTimerArr[currentArrowTimerArr.length] = setInterval(whileArrowButtonDown, 100); return false; case 33: case 34: return false; default: } }).bind('keyup.jscrollpane', function (e) {
                        if (e.keyCode == 38 || e.keyCode == 40) {
                            for (var i = 0; i < currentArrowTimerArr.length; i++) { clearInterval(currentArrowTimerArr[i]); }
                            return false;
                        } 
                    });
                }
                if (settings.showArrows) { var currentArrowButton; var currentArrowInterval; var onArrowMouseUp = function (event) { $('html').unbind('mouseup', onArrowMouseUp); currentArrowButton.removeClass('jScrollActiveArrowButton'); clearInterval(currentArrowInterval); }; var onArrowMouseDown = function () { $('html').bind('mouseup', onArrowMouseUp); currentArrowButton.addClass('jScrollActiveArrowButton'); currentArrowInc = 0; whileArrowButtonDown(); currentArrowInterval = setInterval(whileArrowButtonDown, 100); }; $container.append($('<a></a>').attr({ 'href': 'javascript:;', 'className': 'jScrollArrowUp', 'tabindex': -1 }).css({ 'width': settings.scrollbarWidth + 'px', 'top': settings.topCapHeight + 'px' }).html('Scroll up').bind('mousedown', function () { currentArrowButton = $(this); currentArrowDirection = -1; onArrowMouseDown(); this.blur(); return false; }).bind('click', rf), $('<a></a>').attr({ 'href': 'javascript:;', 'className': 'jScrollArrowDown', 'tabindex': -1 }).css({ 'width': settings.scrollbarWidth + 'px', 'bottom': settings.bottomCapHeight + 'px' }).html('Scroll down').bind('mousedown', function () { currentArrowButton = $(this); currentArrowDirection = 1; onArrowMouseDown(); this.blur(); return false; }).bind('click', rf)); var $upArrow = $('>.jScrollArrowUp', $container); var $downArrow = $('>.jScrollArrowDown', $container); }
                if (settings.arrowSize) { trackHeight = paneHeight - settings.arrowSize - settings.arrowSize; trackOffset += settings.arrowSize; } else if ($upArrow) { var topArrowHeight = $upArrow.height(); settings.arrowSize = topArrowHeight; trackHeight = paneHeight - topArrowHeight - $downArrow.height(); trackOffset += topArrowHeight; }
                trackHeight -= settings.topCapHeight + settings.bottomCapHeight; $track.css({ 'height': trackHeight + 'px', top: trackOffset + 'px' })
                var $pane = $(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; $('html').bind('mouseup', onStopDrag).bind('mousemove', updateScroll).bind('mouseleave', onStopDrag)
                    if ($.browser.msie) { $('html').bind('dragstart', ignoreNativeDrag).bind('selectstart', ignoreNativeDrag); }
                    return false;
                }; var onStopDrag = function () { $('html').unbind('mouseup', onStopDrag).unbind('mousemove', updateScroll); dragMiddle = percentInView * paneHeight / 2; if ($.browser.msie) { $('html').unbind('dragstart', ignoreNativeDrag).unbind('selectstart', ignoreNativeDrag); } }; var positionDrag = function (destY) { $container.scrollTop(0); destY = destY < 0 ? 0 : (destY > maxY ? maxY : destY); dragPosition = destY; $drag.css({ 'top': destY + 'px' }); var p = destY / maxY; $this.data('jScrollPanePosition', (paneHeight - contentHeight) * -p); $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); $('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; $('html').bind('mouseup', onStopTrackClick).bind('mousemove', onTrackMouseMove); trackScrollInterval = setInterval(doTrackScroll, 100); doTrackScroll(); return false; }; $track.bind('mousedown', onTrackClick); $container.bind('mousewheel', function (event, delta) { delta = delta || (event.wheelDelta ? event.wheelDelta / 120 : (event.detail) ? -event.detail / 3 : 0); initDrag(); ceaseAnimation(); var d = dragPosition; positionDrag(dragPosition - delta * mouseWheelMultiplier); var dragOccured = d != dragPosition; return !dragOccured; }); 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") {
                        try { $e = $(pos, $this); } catch (err) { return; }
                        if (!$e.length) return; pos = $e.offset().top - $this.offset().top;
                    }
                    ceaseAnimation(); var maxScroll = contentHeight - paneHeight; pos = pos > maxScroll ? maxScroll : pos; $this.data('jScrollPaneMaxScroll', maxScroll); var destDragPosition = pos / maxScroll * maxY; if (preventAni || !settings.animateTo) { positionDrag(destDragPosition); } else { $container.scrollTop(0); _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); $('*', this).bind('focus', function (event) {
                    var $e = $(this); var eleTop = 0; var preventInfiniteLoop = 100; while ($e[0] != $this[0]) { eleTop += $e.position().top; $e = $e.offsetParent(); if (!preventInfiniteLoop--) { return; } }
                    var viewportTop = -parseInt($pane.css('top')) || 0; var maxVisibleEleTop = viewportTop + paneHeight; var eleInView = eleTop > viewportTop && eleTop < maxVisibleEleTop; if (!eleInView) {
                        var destPos = eleTop - settings.scrollbarMargin; if (eleTop > viewportTop) { destPos += $(this).height() + 15 + settings.scrollbarMargin - paneHeight; }
                        scrollTo(destPos);
                    } 
                })
                if (settings.observeHash) {
                    if (location.hash && location.hash.length > 1) { setTimeout(function () { scrollTo(location.hash); }, $.browser.safari ? 100 : 0); }
                    $(document).bind('click', function (e) { $target = $(e.target); if ($target.is('a')) { var h = $target.attr('href'); if (h && h.substr(0, 1) == '#' && h.length > 1) { setTimeout(function () { scrollTo(h, !settings.animateToInternalLinks); }, $.browser.safari ? 100 : 0); } } });
                }
                function onSelectScrollMouseDown(e) { $(document).bind('mousemove.jScrollPaneDragging', onTextSelectionScrollMouseMove); $(document).bind('mouseup.jScrollPaneDragging', onSelectScrollMouseUp); }
                var textDragDistanceAway; var textSelectionInterval; function onTextSelectionInterval() { direction = textDragDistanceAway < 0 ? -1 : 1; $this[0].scrollBy(textDragDistanceAway / 2); }
                function clearTextSelectionInterval() { if (textSelectionInterval) { clearInterval(textSelectionInterval); textSelectionInterval = undefined; } }
                function onTextSelectionScrollMouseMove(e) { var offset = $this.parent().offset().top; var maxOffset = offset + paneHeight; var mouseOffset = getPos(e, 'Y'); textDragDistanceAway = mouseOffset < offset ? mouseOffset - offset : (mouseOffset > maxOffset ? mouseOffset - maxOffset : 0); if (textDragDistanceAway == 0) { clearTextSelectionInterval(); } else { if (!textSelectionInterval) { textSelectionInterval = setInterval(onTextSelectionInterval, 100); } } }
                function onSelectScrollMouseUp(e) { $(document).unbind('mousemove.jScrollPaneDragging').unbind('mouseup.jScrollPaneDragging'); clearTextSelectionInterval(); }
                $container.bind('mousedown.jScrollPane', onSelectScrollMouseDown); $.jScrollPane.active.push($this[0]);
            } else { $this.css({ 'height': paneHeight + 'px', 'width': paneWidth - this.originalSidePaddingTotal + 'px', 'padding': this.originalPadding }); $this[0].scrollTo = $this[0].scrollBy = function () { }; $this.parent().unbind('mousewheel').unbind('mousedown.jScrollPane').unbind('keydown.jscrollpane').unbind('keyup.jscrollpane'); } 
        })
    }; $.fn.jScrollPaneRemove = function () { $(this).each(function () { $this = $(this); var $c = $this.parent(); if ($c.is('.jScrollPaneContainer')) { $this.css({ 'top': '', 'height': '', 'width': '', 'padding': '', 'overflow': '', 'position': '' }); $this.attr('style', $this.data('originalStyleTag')); $c.after($this).remove(); } }); }
    $.fn.jScrollPane.defaults = { scrollbarWidth: 10, scrollbarMargin: 5, wheelSpeed: 18, showArrows: false, arrowSize: 0, animateTo: false, dragMinHeight: 1, dragMaxHeight: 99999, animateInterval: 100, animateStep: 3, maintainPosition: true, scrollbarOnLeft: false, reinitialiseOnImageLoad: false, tabIndex: 0, enableKeyboardNavigation: true, animateToInternalLinks: false, topCapHeight: 0, bottomCapHeight: 0, observeHash: true }; $(window).bind('unload', function () { var els = $.jScrollPane.active; for (var i = 0; i < els.length; i++) { els[i].scrollTo = els[i].scrollBy = null; } });
})(jQuery);
