/*!
 * jquery.customscroll 1.0
 *
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl-2.0.html
 */
;(function ($) {

    $.customscrollOptions = {
        always: false,
        show: {
            on: 'mouseenter scrollstart',
            effect: 'fadeIn',
            speed: 250,
            delay: 0
        },
        hide: {
            on: 'mouseleave scrollstop',
            effect: 'fadeOut',
            speed: 250,
            delay: 750
        },
        grow: {
            size: 3,
            speed: 100
        },
        pageUpnDown: {
            speed: 100
        }
    };

    $.fn.customscroll = function (options) {
        options = $.extend(true, {}, $.customscrollOptions, options || {});
        return this.each(function () {
            var object = $(this);

            // build template
            object.addClass('customscroll');
            object.html([
                '<div class="wrapper" style="height:' + object.height() + 'px">',
                '<div class="content" style="width:' + object.width() + 'px">',
                object.html(),
                '</div>',
                '</div>',
                '<div class="placeholder" style="top:' + object.css('paddingTop') + ';bottom:' + object.css('paddingBottom') + '">',
                '<div class="track">',
                '<div class="grip"></div>',
                '</div>',
                '</div>'
            ].join(''));

            // get elements
            var wrapper = object.children('.wrapper'),
                placeholder = object.children('.placeholder'),
                track = placeholder.children('.track'),
                grip = track.children('.grip');

            // object hover
            var showHideTimer,
                show = function (event) {
                    if (object.height() >= wrapper[0].scrollHeight) {
                        // we dont need scroll
                        return;
                    }
                    if (wrapper[0].scrollHeight < parseInt(object.css('max-height'))) {
                        // this is a fix for IE
                        return;
                    }
                    // show
                    clearInterval(showHideTimer);
                    showHideTimer = setTimeout(function () {
                        track[options.show.effect](options.show.speed);
                    }, options.show.delay);
                    if (!event || event.type !== 'scrollstart') {
                        // used for adjusting sizes
                        wrapper.trigger('scroll');
                    }
                };
            wrapper.on(options.show.on, show);
            wrapper.on(options.hide.on, function callback() {
                // don't hide if always is true
                if (options.always) {
                    return;
                }
                if (wrapper.hasClass('keep')) {
                    // someone says do not hide, try again later
                    return setTimeout(function () {
                        callback();
                    }, 250);
                }
                // hide
                clearInterval(showHideTimer);
                showHideTimer = setTimeout(function () {
                    track[options.hide.effect](options.hide.speed);
                }, options.hide.delay);
            });

            // bind scroll event
            var scrollingTimer;
            wrapper.on('scroll', function (event) {
                var height = object.height();
                grip.css({
                    // grip height is view height / scroll height * 200 (4 is for margins)
                    height: height / this.scrollHeight * height - parseInt(object.css('paddingTop')) - parseInt(object.css('paddingBottom')),
                    // scroll position
                    marginTop: this.scrollTop * 100 / this.scrollHeight * height / 100 + 'px'
                });
                // custom scroll start / end events
                if (!scrollingTimer) {
                    wrapper.trigger('scrollstart');
                }
                clearInterval(scrollingTimer);
                scrollingTimer = setTimeout(function () {
                    wrapper.trigger('scrollstop');
                    scrollingTimer = null;
                }, 200);
            });

            // placeholder and track hover
            wrapper.on('mouseover', function () {
                // do not hide track and show
                wrapper.addClass('keep');
                show();
            });

            // placeholder and track hover
            wrapper.on('mouseleave', function () {
                // do not hide track and show
                wrapper.removeClass('keep');
            });
            placeholder.on('mouseover', function () {
                // do not hide track and show
                wrapper.addClass('keep');
                show();
            });
            placeholder.on('mouseleave', function () {
                // hide track
                wrapper.removeClass('keep');
            });
            track.on('mouseenter', function () {
                track.addClass('hover');
                if (options.grow) {
                    // stop animation queue and animate
                    track.stop(true, true);
                    track.animate({width: '+=' + options.grow.size + 'px'}, options.grow.speed);
                }
            });
            track.on('mouseleave', function hover() {
                track.removeClass('hover');
                if (options.grow) {
                    // stop animation queue and animate
                    track.stop(true, true);
                    track.animate({width: '-=' + options.grow.size + 'px'}, options.grow.speed);
                }
            });

            // page up / down (track click)
            track.on('click', function (event) {
                if ($(event.target).hasClass('grip')) {
                    // do not trigger when clicking the egrip
                    return;
                }
                if (event.pageY - track.offset().top > parseInt(grip.css('marginTop'))) {
                    // clicked after grip
                    var operation = '+';
                } else {
                    // clicked before grip
                    var operation = '-';
                }
                // scroll
                wrapper.animate({
                    scrollTop: operation + '=' + object.height() + 'px'
                }, options.pageUpnDown.speed);
            });

            // show initially if always is true
            if (options.always) {
                show();
            }
            // grip drag
            var dragging = false;
            grip.on('mousedown', function (event) {
                // do not hide
                wrapper.addClass('keep');
                // mark as dragging
                dragging = true;
                // prevent text selection
                $('body').addClass('dragging');
            });
            $(document).on('mousemove', function (event) {
                if (dragging) {
                    // we have drag, move
                    wrapper.scrollTop(wrapper[0].scrollHeight * (event.pageY - track.offset().top - grip.height() / 2) / object.height());
                    event.preventDefault();
                }
            });
            $(document).on('mouseup', function () {
                if (dragging) {
                    // we have drag, remove
                    wrapper.removeClass('keep');
                    dragging = false;
                    $('body').removeClass('dragging');
                }
            });

        });
    };
})(jQuery);