/**
 * jQuery Google Wave Scroll Pane
 * Version 0.1 2010-11-02
 * 
 * @requires jQuery v1.3+
 * @author Konr Ness http://konrness.com
 * 
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 */
(function($) {
$.fn.gWaveScrollPane = function(settings) {
    var config = {
        'foo': 'bar'
    };

    if (settings) $.extend(config, settings);

    return this.each(function() {
        
        var $this = $(this);
        var $scrollableWrapper, $scrollbarContainer, $scrollbarGrabber, $scrollbarIndicator;

        var waitToMoveGrabber = false;

        /**
         * Setup Scrollable Wrapper
         */
        // create gwave-scroll-wrapper
        $scrollableWrapper = $('<div class="gwave-scroll-wrapper"/>');

        // set width/height to be same as this
        $scrollableWrapper.height($this.height()).width($this.width());

        // add scrollable wrapper to DOM just before this
        $this.before($scrollableWrapper);

        // put this inside scrollable wrapper
        $this.appendTo($scrollableWrapper);

        /**
         * Create the scrollbar
         */
        $scrollbarContainer = $('<div class="scrollbar-container"><div class="scrollbar-bottom"/></div>');
        $scrollbarGrabber   = $('<div class="scrollbar-grabber"><div class="up"/><div class="down"/></div>');
        $scrollbarIndicator = $('<div class="scrollbar-indicator"/>');

        $scrollbarContainer.prepend($scrollbarIndicator);
        $scrollbarContainer.prepend($scrollbarGrabber);

        $scrollableWrapper.prepend($scrollbarContainer);

        /**
         * Setup initial layout
         */
        
        // hide indicator
        $scrollbarIndicator.hide();

        // grabber up/down button mouseovers
        $scrollbarGrabber.children().each(function(){
            $(this).mouseover(function(){
                $scrollbarGrabber.addClass(this.className + '-over');
            }).mouseleave(function(){
                $scrollbarGrabber.removeClass(this.className + '-over');
            });
        });

        // reset content's scroll top
        $this.scrollTop(0);
        $this.data('goal-scroll-top', $this.scrollTop());

        /**
         * Setup content scrolling events
         */
        
        // listen to scroll changes from box and update indicator position
        $this.bind('scroll', function(e){

            scrollPercentage = this.scrollTop / (this.scrollHeight - this.offsetHeight);

            newIndicatorTop = ($scrollbarContainer.height() - $scrollbarIndicator.height()) * scrollPercentage;
            newIndicatorTop = newIndicatorTop + 'px';

            $scrollbarIndicator.css('top', newIndicatorTop);

        });

        // setup mousewheel scrolling for content box
        $this.bind('mousewheel', function(event, delta) {
            $this.scrollTop($this.scrollTop() + (-delta*45));
            $this.data('goal-scroll-top', $this.scrollTop());
            updateGrabberPosition(true);
            return false;
        });

        /**
         * Setup grabber events
         */

        // grabber dragging
        $scrollbarGrabber.draggable({
            addClasses  : false,
            axis        : 'y',
            containment : 'parent',
            distance    : 2,
            cursor      : 'pointer',
            start       : function(){
                $scrollbarIndicator.show();
            },
            stop        : function(){
                $scrollbarIndicator.hide();
            },
            drag        : function(){
                scrollPercentage = $scrollbarGrabber.position().top / ($scrollbarContainer.height() - $scrollbarGrabber.height());
                scrollToPercentage(scrollPercentage);
            }
        }).mouseout(function(){
            waitToMoveGrabber = false;
            updateGrabberPosition(false);
        });

        // grabber arrow clicking
        $scrollbarGrabber.children('div').click(function(){
            direction = $(this).hasClass('up') ? -1 : 1;
            waitToMoveGrabber = true;
            $scrollbarIndicator.show();
            scrollByDelta(direction * 150);
        });

        /**
         * Functions
         */
        function updateGrabberPosition(immediate) {

            if(waitToMoveGrabber == true){
                return;
            }

            if($scrollbarGrabber.hasClass('ui-draggable-dragging')){
                return;
            }

            contentScrollPercentage = $this.get(0).scrollTop / ($this.get(0).scrollHeight - $this.get(0).offsetHeight);
            newGrabberTop = contentScrollPercentage * ($scrollbarContainer.height() - $scrollbarGrabber.height());

            if(immediate) {
                $scrollbarGrabber.css('top', newGrabberTop + 'px');
                $scrollbarIndicator.hide();
            } else {
                // animate
                $scrollbarGrabber.stop().animate(
                    { 
                        top : newGrabberTop + 'px'
                    },
                    250, 
                    'swing',
                    function(){
                        $scrollbarIndicator.hide();
                    }
                );
            }
        }

        function scrollToPercentage(percentage) {

            newContentTop = ($this.get(0).scrollHeight - $this.height()) * percentage;
            
            $this.data('goal-scroll-top', newContentTop);
            
            $this.stop().animate(
                { 
                    scrollTop: newContentTop + 'px'
                },
                500, 
                'swing'
            );
            
        }

        function scrollByDelta(delta) {

            newContentTop = $this.data('goal-scroll-top') + delta;
            
            $this.data('goal-scroll-top', newContentTop);
            
            $this.stop().animate(
                { 
                    scrollTop: newContentTop + 'px'
                },
                500, 
                'swing'
            );
            
        }

    });

};

})(jQuery);

