/** 
 * Copyright (c) 2010 Sylvain Gougouzian (sylvain@gougouzian.fr)
 * MIT (http://www.opensource.org/licenses/mit-license.php) licensed.
 * GNU GPL (http://www.gnu.org/licenses/gpl.html) licensed.
 *
 * jQuery moodular version: 2.1
 *
 * Requires: jQuery 1.3.2+ 	// http://www.jquery.com
 * Compatible : Internet Explorer 6+, Firefox 1.5+, Safari 3+, Opera 9+, Chrome 0.9+
 */
jQuery(function($){
    $.fn.moodular = function(options){
        var el = null;
        var opts = $.extend({}, $.fn.moodular.defaults, options);
        var ctrls = $.extend({}, $.fn.moodular.controls);
        var effects = $.extend({}, $.fn.moodular.effects);
        this.each(function(){
            el = new $moodular(this, opts, ctrls, effects);
            $(window).bind('resize', function () { el._resize(); });
        });
        return opts.api ? el : null;
    };
    $.moodular = function(e, opts, ctrls, effects){
        this.e = $(e);
        if (opts.continuous) $(e).html($(e).html() + $(e).html());
        this.aItems = null;
        this.nbItems = 0;
        this.current = 0;
        this.locked = false;
        this.dep = 0;
        this.timerMoving = null;
        this.opts = opts;
        this.controls = ctrls;
        this.effects = effects;
        this.direction = ((opts.direction == 'left') || (opts.direction == 'top') ? 'next' : 'prev');
        this.pos = ((opts.direction == 'left') || (opts.direction == 'right') ? 'left' : 'top');
        this.dir = ((opts.direction == 'right') || (opts.direction == 'bottom') ? -1 : 1);
        this.vertical = (this.pos == 'left' ? false : true);
        this._init();
    };
    var $moodular = $.moodular;
    $moodular.fn = $moodular.prototype = {
        moodular: '2.1'
    };
    $moodular.fn.extend = $moodular.extend = $.extend;
    $moodular.fn.extend({
        _init: function(){
            var self = this;
            this._resize();
            this.e.wrap('<div></div>');
            this.e.parent().css({
                'position' : 'relative',
                'overflow' : 'hidden',
                'width'	: this.e.width(),
                'height' : this.e.height()
            });
            this.e.css({
                'position' : 'absolute'
            });
            s = 0;
            $('> ' + this.opts.item, this.e).each(function() {
                if (this.vertical) {
                    s += parseInt($(this).outerHeight(true));
                }
                else {
                    s += parseInt($(this).outerWidth(true));
                }
            });
            this.e.css(this.vertical ? 'height' : 'width', s + 'px');
            this.aItems = $('> ' + this.opts.item, this.e);
            this.nbItems = this.aItems.length;
            if (!this.opts.continuous) this.nbItems = this.nbItems * 2;
            var control = this.opts.controls.split(' ');
            var i;
            for (i = 0; i < control.length; i++) {
                if ($.isFunction(this.controls[control[i]])) 
                    this.controls[control[i]](this);
            }
            var effect = this.opts.effects.split(' ');
			for (i = 0; i < effect.length; i++) {
                if ($.isFunction(this.effects.init[effect[i]])) 
					this.effects.init[effect[i]](this);
            }
            if (this.opts.startOn) {
				this.speed = this.opts.speed;
				this.opts.speed = 1;
            	this.moveTo(this.opts.startOn);
            }
            if (this.opts.auto) {
                setTimeout(function(){
                    self._animate('next');
                }, self.opts.dispTimeout);
            }
        },
        _animate: function(dir){
            dir = (dir == undefined ? this.direction : dir);
            if (!this.locked) {
                this.locked = true;
                clearTimeout(this.timerMoving);
                this.dep = this.dep == 0 ? this.opts.scroll : this.dep;
                if (this.dir == -1) {
                    if (dir == "next") {
                        dir = "prev";
                    }
                    else if (dir == "prev") {
                        dir = "next";
                    }
                }
                if (dir != 'next') {
                    this.dep *= -1;
                }
                var cont = true;
                if (!this.opts.continuous) {
                    if (dir == 'next') {
                        if (this.current >= ((this.nbItems / 2) - 1)) {
                            cont = false;
                            this.locked = false;
                        }
                    }
                    else {
                        if (this.current <= 0) {
                            cont = false;
                            this.locked = false;
                        }
                    }
                }
                if (cont) {
                    this._beforeMoving();
                }
            }
        },
        _beforeMoving: function(){
            var effect = this.opts.effects.split(' ');
            var i;
            for (i = 0; i < effect.length; i++) {
                if ($.isFunction(this.effects.before[effect[i]])) 
                    this.effects.before[effect[i]](this, (this.dep < 0 ? -1 : 1));
            }
            if (this.dep < 0 && this.opts.continuous) {
                var size = 0;
                for (i = 0; i < Math.abs(this.dep); i++) {
                    var item = $('> ' + this.opts.item + ':last', this.e);
                    size += parseInt(item.css(this.vertical ? 'height' : 'width'));
                    $('> ' + this.opts.item + ':last', this.e).remove();
                    this.e.prepend(item);
                }
                this.e.css(this.pos, -size);
            }
            this._move();
        },
        _move: function(){
            var self = this;
            if ($.isFunction(this.opts.move)) {
                this.opts.move(this, function(){
                    self._afterMoving();
                });
            }
            else {
                var size = 0;
                var i;
                if (this.dep > 0) {
                    for (i = 0; i < this.dep; i++) {
                        if (this.vertical) {
                            size += parseInt(this.aItems.eq(this._realpos(this.current) + i).outerHeight(true));
                        }
                        else {
                            size += parseInt(this.aItems.eq(this._realpos(this.current) + i).outerWidth(true));
                        }
                    }
                }
                else {
                    if (!this.opts.continuous && this.current <=0) {}
                    else {
                        for (i = 0; i < Math.abs(this.dep); i++) {
                            if (this.vertical) {
                                size += parseInt(this.aItems.eq(this._realpos(this.current) - i).outerHeight(true));
                            }
                            else {
                                size += parseInt(this.aItems.eq(this._realpos(this.current) - i).outerWidth(true));
                            }
                        }
                    }
                }
                if (this.e.css(this.pos) == 'auto') this.e.css(this.pos, 0);
                var dest = parseInt(this.e.css(this.pos)) + (this.dep > 0 ? -1 : 1) * size;
                if (!this.opts.continuous) {
                    if (dest > 0) dest = 0;
                    if (this.vertical) {
                        if ((parseInt(this.e.height()) + dest) < parseInt(this.e.parent().height())) {
                            dest = parseInt(this.e.parent().height()) - parseInt(this.e.height());
                        }
                    }
                    else {
                        if ((parseInt(this.e.width()) + dest) < parseInt(this.e.parent().width())) {
                            dest = parseInt(this.e.parent().width()) - parseInt(this.e.width()) ;
                        }
                    }
                }
                if (this.vertical) {
                    this.e.stop(true, true).animate({
                        top: parseInt(dest) + 'px'
                    }, this.opts.speed, this.opts.easing, function(){
                        self._afterMoving();
                    });
                }
                else {
                    this.e.stop(true, true).animate({
                        left: parseInt(dest) + 'px'
                    }, this.opts.speed, this.opts.easing, function(){
                        self._afterMoving();
                    });
                }
            }
        },
        _afterMoving: function(){
            var i;
            if (this.dep > 0 && this.opts.continuous) {
                for (i = 0; i < this.dep; i++) {
                    var item = $('> ' + this.opts.item + ':first', this.e);
                    $('> ' + this.opts.item + ':first', this.e).remove();
                    this.e.append(item);
                }
                this.e.css(this.pos, 0);
            }
            var self = this;
            this.current = this.current + this.dep;
            if (this.current == -1) {
                if (this.opts.continuous)
                    this.current = this.nbItems - 1;
                else
                    this.current = 0;
            }
            else {
                if (this.current == this.nbItems) {
                    this.current = 0;
                }
                else {
                    this.current = this._realpos(this.current);
                }
            }
            this.dep = 0;
            this.locked = false;
            var effect = this.opts.effects.split(' ');
            for (i = 0; i < effect.length; i++) {
                if ($.isFunction(this.effects.after[effect[i]])) 
                    this.effects.after[effect[i]](this);
            }
            for (i = 0; i < this.opts.callbacks.length; i++) {
                this.opts.callbacks[i](this);
            }
            var control = this.opts.controls.split(' ');
            for (i = 0; i < control.length; i++) {
                if ($.isFunction(this.controls.callback[control[i]])) 
                    this.controls.callback[control[i]](this);
            }
            if (this.opts.startOn) {
				this.opts.speed = this.speed;
            }
            if (this.opts.auto) {
                this.timerMoving = setTimeout(function(){
                    self._animate('next');
                }, this.opts.dispTimeout);
            }
        },
        _realpos: function(i){
            if (i < 0) i = (this.nbItems / 2) - i;
            return (i < (this.nbItems / 2) ? i : (i - (this.nbItems / 2)));
        },
        _resize: function () {
            var s = $('> ' + this.opts.item, this.e).css(this.vertical ? 'height' : 'width');
            if (s.indexOf('%') != -1) {
                $('> ' + this.opts.item, this.e).each(function() {
                    if (this.vertical) {
                        $(this).height($(this).outerHeight(false));
                    }
                    else {
                        $(this).width($(this).outerWidth(false));
                    }
                });
            }
        },
        reanimate: function() {
        	if (!this.opts.auto) {
                this.locked = false;
                this.opts.auto = true;
                var self = this;
                this.timerMoving = setTimeout(function(){
                	self._animate('next');
                }, this.opts.dispTimeout);
            }
        },
        start: function(){
            if (!this.opts.auto) {
                this.locked = false;
                this.opts.auto = true;
                this._animate('next');
            }
            return false;
        },
        stop: function(){
            clearTimeout(this.timerMoving);
            this.opts.auto = false;
            return false;
        },
        next: function(){
            this._animate('next');
            return false;
        },
        prev: function(){
            this._animate('prev');
            return false;
        },
        getCurrent: function(){
            return this._realpos(this.current);
        },
        moveTo: function(i){
            if (i > (this.nbItems / 2)) {
                i = (this.nbItems / 2) - 1;
            }
            this.dep = parseInt(i) - parseInt(this.current);
            if (this.dep != 0) {
                this._animate('next');
            }
            return false;
        }
    });
    $.fn.moodular.defaults = {
        item: 'li',
        controls: '',
        effects: '',
        easing: '',
        auto: true,
        continuous: true,
        speed: 2000,
        direction: 'left',
        scroll: 1,
        startOn: 0,
        dispTimeout: 1000,
        callbacks: [],
        api: false
    };
    $.fn.moodular.controls = {
        callback: {}
    };
    $.fn.moodular.effects = {
        init: {},
        before: {},
        after: {}
    };
});

