(function($) {

    $.loader = function(options, keyword) {
        return $.loader.impl.init(options);
    };

    $.fn.loader = function(options) {
        return $.loader.impl.init(this, options);
    };

    /*
    * default options
    */
    $.loader.defaults = {
        imagePath: '/images/preloader.png',
        interval: 66,
        frameSize: 64,
        frameCount: 12
    };

    $.loader.impl = {

        /*
        * options
        */
        opts: null,

        /*
        * helper
        */
        helper: {},

        /*
        * interval
        */
        interval: 0,

        /*
        * frame
        */
        frame: 0,

        /*
        * loading
        */
        loading: false,

        /*
        * init
        */
        init: function(o, options) {
            
            var self = this;

            this.opts = $.extend({}, $.loader.defaults, options);

            self.helper.target = o;
            self.helper.container = $('<div id="preloader"></div>');
            self.helper.image = $(new Image());
            
            if (self.loading = false)
                return self;

            // load the image
            self.helper.image
                .load(function() {
                    
                    self.loading = true;
                    
                    self.helper.container
                        .css({
                            display: 'none',
                            left: '50%',
                            top: '50%',
                            marginLeft: -self.opts.frameSize / 2 + 'px',
                            marginTop: -self.opts.frameSize / 2 + 'px'
                        })
                        .appendTo(self.helper.target)
                        ;

                    // start the animation
                    self.interval = setInterval(self.animate, self.opts.interval);

                    // show the loader
                    self.helper.container
                        .fadeIn(250, function(){
                            if ($.isFunction(self.opts.onShow))
                                self.opts.onShow();
                        })
                        ;
                })
                .attr('src', self.opts.imagePath)
                .appendTo(self.helper.container)
                ;

            return self;
        },

        /*
        * animate
        */
        animate: function() {

            var self = $.loader.impl;

            self.helper.image
		        .css({
		            top: (self.frame * -self.opts.frameSize) + 'px'
		        })
		        ;

            self.frame = (self.frame + 1) % self.opts.frameCount;

            return;
        },

        /*
        * stop
        */
        stop: function(callback) {
            
            var self = $.loader.impl;

            self.loading = false;

            // hide the loader
            self.helper.container
                .queue('fx', [])
                .fadeOut(500, function() {

                    // clear the interval
                    clearInterval(self.interval);
                    
                    if ($.isFunction(callback))
                        callback();
                })
                ;

            return;
        }

    };
})(jQuery);
