;(function (factory)
{
    if (typeof define === 'function' && define.amd)
    {
        // AMD. Register as an anonymous module.
        define(['jquery'], factory);
    } else if (typeof exports === 'object')
    {
        // Node/CommonJS
        factory(require('jquery'));
    } else
    {
        // Browser globals
        factory(jQuery);
    }
}(function ($)
{

    var pluginName = "tinyscrollbar"
    ,   defaults   =
        {
            axis           : 'y'           // vertical or horizontal scrollbar? ( x || y ).
        ,   wheel          : true          // enable or disable the mousewheel;
        ,   wheelSpeed     : 40            // how many pixels must the mouswheel scroll at a time.
        ,   wheelLock      : true          // return mouswheel to browser if there is no more content.
        ,   scrollInvert   : false         // Enable invert style scrolling
        ,   trackSize      : false         // set the size of the scrollbar to auto or a fixed number.
        ,   thumbSize      : false         // set the size of the thumb to auto or a fixed number.
        ,   thumbMinSize   : 15            // minimal size of the thumb, in pixel.
        }
    ;

    function Plugin($container, options)
    {
        this.options   = $.extend({}, defaults, options);
        this._defaults = defaults;
        this._name     = pluginName;

        var self        = this
        ,   $viewport   = $container.children(".viewport")
        ,   $scrollbar  = $container.children(".scrollbar")
        ,   $track      = $scrollbar.children(".track")
        ,   $thumb      = $track.children(".thumb")

        ,   mousePosition   = 0

        ,   isHorizontal   = this.options.axis === 'x'

        ,   sizeLabel = isHorizontal ? "width" : "height"
        ,   posiLabel = isHorizontal ? "left" : "top"
        ;

        this.contentPosition = 0;
        this.viewportSize    = 0;
        this.contentSize     = 0;
        this.contentRatio    = 0;
        this.trackSize       = 0;
        this.trackRatio      = 0;
        this.thumbSize       = this.options.thumbMinSize;
        this.thumbPosition   = 0;
        this.dragging        = false;

        function initialize()
        {
            self.update();
            setEvents();

            return self;
        }

        this.update = function(scrollTo)
        {
            var sizeLabelCap  = sizeLabel.charAt(0).toUpperCase() + sizeLabel.slice(1).toLowerCase();
            this.viewportSize = $viewport[0]['offset'+ sizeLabelCap];
            if ($viewport[0].contentWindow) // iframe
                this.contentSize  = $viewport[0].contentWindow.document.body['scroll'+ sizeLabelCap];
            else
                this.contentSize  = $viewport[0]['scroll'+ sizeLabelCap];
            this.contentRatio = this.viewportSize / this.contentSize;
            this.trackSize    = this.options.trackSize || this.viewportSize;
            this.thumbSize    = Math.min(this.trackSize, Math.max(this.options.thumbMinSize, (this.options.thumbSize || (this.trackSize * this.contentRatio))));
            this.trackRatio   = (this.contentSize - this.viewportSize) / (this.trackSize - this.thumbSize);

            $scrollbar.toggleClass("disable", this.contentRatio >= 1);

            switch (scrollTo)
            {
                case "top":
                    this.contentPosition = 0;
                    break;

                case "bottom":
                    this.contentPosition = this.contentSize - this.viewportSize;
                    break;

                case "relative":
                    this.contentPosition = Math.max(0, Math.min(this.contentSize - this.viewportSize, Math.max(0, this.contentPosition)));
                    break;

                case "lineup":
                    this.contentPosition = Math.max(0, this.contentPosition - this.options.wheelSpeed);
                    break;

                case "linedown":
                    this.contentPosition = Math.min(this.contentSize - this.viewportSize, this.contentPosition + this.options.wheelSpeed);
                    break;

                case "pageup":
                    this.contentPosition = Math.max(0, this.contentPosition - this.viewportSize);
                    break;

                case "pagedown":
                    this.contentPosition = Math.min(this.contentSize - this.viewportSize, this.contentPosition + this.viewportSize);
                    break;

                default:
                    this.contentPosition = parseInt(scrollTo, 10) || 0;
            }

            setSize();
        };

        function scroll()
        {
            var $vp = null;
            if ($viewport[0].contentWindow) // iframe
                $vp = $($viewport[0].contentWindow);
            else
                $vp = $viewport;
            if (isHorizontal) {
                $vp.scrollLeft(self.contentPosition);
            } else {
                $vp.scrollTop(self.contentPosition);
            }
        }

        function setSize()
        {
            $thumb.css(posiLabel, self.contentPosition / self.trackRatio);
            mousePosition = $thumb.offset()[posiLabel];

            $scrollbar.css(sizeLabel, self.trackSize);
            $track.css(sizeLabel, self.trackSize);
            $thumb.css(sizeLabel, self.thumbSize);

            scroll();
        }

        function setEvents()
        {
            $thumb.bind("mousedown", start);
            $track.bind("mouseup", drag);

            if (self.options.wheel)
            {
                if (window.addEventListener)
                    $container[0].addEventListener("wheel", wheel, false);
                else
                    $container[0].attachEvent('onmousewheel', wheel);
            }
        }

        function start(event)
        {
            mousePosition      = isHorizontal ? event.pageX : event.pageY;
            self.thumbPosition = parseInt($thumb.css(posiLabel), 10) || 0;

            $thumb.bind("mousemove", drag);
            $thumb.bind("mouseup", end);

            if (!window.captureEvents)
                $thumb[0].setCapture();
            else
                captureEvents();

            self.dragging = true;
        }

        function wheel(event)
        {
            if(self.contentRatio < 1)
            {
                var eventObject     = event || window.event;
                if (eventObject.wheelDelta) {
                    self.contentPosition -= eventObject.wheelDelta / 120 * self.options.wheelSpeed;
                } else {
                    var delta = isHorizontal ? eventObject.deltaX : eventObject.deltaY;
                    self.contentPosition +=
                        eventObject.deltaMode == 0 ? delta : delta * self.options.wheelSpeed;
                }

                self.contentPosition = Math.min((self.contentSize - self.viewportSize), Math.max(0, self.contentPosition));

                //$container.trigger("move");

                $thumb.css(posiLabel, self.contentPosition / self.trackRatio);
                scroll();

                if(self.options.wheelLock || (self.contentPosition !== (self.contentSize - self.viewportSize) && self.contentPosition !== 0))
                {
                    eventObject = $.event.fix(eventObject);
                    eventObject.preventDefault();
                }
            }
        }

        function drag( event )
        {
            if (self.contentRatio < 1)
            {
                var mousePositionNew   = isHorizontal ? event.pageX : event.pageY
                ,   thumbPositionDelta = mousePositionNew - mousePosition
                ;

                if(self.options.scrollInvert)
                {
                    thumbPositionDelta = mousePosition - mousePositionNew;
                }

                var thumbPositionNew = Math.min((self.trackSize - self.thumbSize), Math.max(0, self.thumbPosition + thumbPositionDelta));
                self.contentPosition = Math.round(thumbPositionNew * self.trackRatio);

                //$container.trigger("move");

                $thumb.css(posiLabel, thumbPositionNew);
                scroll();
            }
        }

        function end()
        {
            if (!window.releaseEvents)
                $thumb[0].releaseCapture();
            else
                releaseEvents();
            $thumb.unbind("mousemove", drag);
            $thumb.unbind("mouseup", end);

            self.dragging = false;
        }

        return initialize();
    }

    $.fn[pluginName] = function(options)
    {
        return this.each(function()
        {
            if(!$.data(this, "plugin_" + pluginName))
            {
                $.data(this, "plugin_" + pluginName, new Plugin($(this), options));
            }
        });
    };
}));
