/* 
 * freshCarousel
 */

(function($) {

    $.fn.freshCarousel = function(settings) {

        if ($(this).size() == 0) {
            return;
        }

        var defaults = {
                // behavior
                trans:            'fade',
                duration:         300,
                interval:         6000, // ms
                delayAfterAction: 20000, // ms, delay of carousel start after user's action
                enableKeypressOn: function() { return false; }, // closure returning true whenever keypress is available

                // TODO:
                waitForAction:    false, // start carousel after user's action
                startAt:          null, // start at given index/ID

                // tags
                navEl:            '<span></span>', // tag of navigation item

                // selectors
                // ...

                // classes
                classes: {
                    carriage:     'carriage', // class of each of carousel carriages
                    wrapper:      'wrapper', // class of element that wraps all carriages
                    navWrapper:   'carousel-nav', // class of navigation wrapper
                    navPrev:      'nav-prev', // class of 'previous' element
                    navNext:      'nav-next', // class of 'next' element
                    activeNavEl:  'active' // class of active navigation element
                }
            },

            // Settings
            s = $.extend(true, {}, defaults, settings),

            // Carousel objects & params
            carousel = this, // carousel DOM object
            wrapper = $(_cls(s.classes.wrapper), carousel),
            carouselWidth = $(carousel).width(),
            size = $(_cls(s.classes.carriage), wrapper).size(),
            navWrapper = $(_cls(s.classes.navWrapper), carousel),
            //navTag = $(s.navEl).attr('tagName').toLowerCase(),
            navTag = $(s.navEl).get(0).tagName.toLowerCase(),

            _locked = false, // lock for any action while sliding

            // Carousel init.
            init = function() {
                $(_cls(s.classes.carriage), carousel).each(function(i) {
                    $(this).css({
                        display: (i > 0) ? 'none' : 'block',
                        position: 'absolute',
                        top: 0, left: 0
                     }).attr('rel', i + 1);
                });
            },

            // Initialization & updating carousel navigation
            nav = function(index) {
                if (!$(navTag + ':first-child', navWrapper).attr('rel')) {
                    // create navigation...
                    navWrapper.empty();
                    navWrapper.repeat(size, s.navEl, function(i) {return i;});
                }
                $(navTag, navWrapper).removeClass(s.classes.activeNavEl);
                if (!index) {
                    index = $(_cls(s.classes.carriage) + ':first-child', carousel).attr('rel');
                }
                $(navTag + '[rel=' + index + ']', navWrapper).addClass(s.classes.activeNavEl);
            },

            // Take care of sliding...
            slide = function(to) {
                if (_locked) { return; }
                _locked = true;
                var actual = parseInt($(_cls(s.classes.carriage) + ':first-child', carousel).attr('rel'));
                var navTo;
                if (actual == to || size < 2) { return _locked = false; }
                if (to == 'prev' || to == 'next') {
                    navTo = to;
                    to = ((to == 'next') ? (actual + 1) : ((to == 'prev') ? (actual - 1) : 1));
                } else {
                    to = parseInt((!to) ? (parseInt(actual) + 1) : to);
                }
                // sanitize limits
                to = (to > size) ? 1 : ((to < 1) ? size : to);
                navTo = (navTo) ? navTo : ((actual > to) ? 'prev' : 'next');
                var steps = (to < actual) ? (size - actual + to) : (to - actual);

                // update navigation
                nav(to);

                // slide transition...
                transition({
                    to: to,
                    steps: steps,
                    navTo: navTo
                }, s.trans, function() {
                    for (var i = 0; i < steps; i++) {
                        $(_cls(s.classes.carriage) + ':first-child', carousel).hide().appendTo(wrapper);
                    }
                    _locked = false;
                });
            },

            // Events binding...
            bind = function() {
                $(navTag, navWrapper)
                    .live('mouseenter', function() {$(this).addClass('hover');})
                    .live('mouseleave', function() {$(this).removeClass('hover');})
                    .live('click', function() {
                        stopTimer();
                        slide($(this).attr('rel'));
                        startTimer();
                    });
                $(_cls(s.classes.navPrev) + ', ' + _cls(s.classes.navNext), carousel)
                    .live('click', function() {
                        stopTimer();
                        slide($(this).hasClass(s.classes.navNext) ? 'next' : 'prev'); // prev or next?
                        startTimer();
                    });
                $(document).bind($.browser.webkit ? 'keyup' : 'keypress', function(event) {
                    var key = (event.which > 0) ? event.which : event.keyCode;
                    switch (event.target.tagName.toLowerCase()) {
                        case 'input':
                        case 'textarea':
                        case 'select':
                            return;
                        default:
                            if (key == '37' || key == '38' || key == '39' || key == '40' || key == '32') {
                                event.preventDefault();
                            }
                    }
                    if (!s.enableKeypressOn()) {
                        return;
                    }
                    var left = false;
                    if ((left = (key == '37')) || key == '39') {
                        stopTimer();
                        slide(left ? 'prev' : 'next'); // prev or next?
                        startTimer();
                    }
                });
            },

            // Carousel transitions
            transition = function(params, effect, callback) {
                var oldone = $(_cls(s.classes.carriage) + ':first-child', carousel),
                    newone = $(_cls(s.classes.carriage) + '[rel=' + params.to + ']');
                switch (effect) {
                    case 'slideleft':
                        oldone.animate({left: ((params.navTo == 'prev') ? '+' : '-') + '=' + carouselWidth}, s.duration, callback);
                        newone.css({left: ((params.navTo == 'prev') ? '-' : '') + carouselWidth, display: 'block'}).animate({left: 0}, s.duration);
                        break;
                    case 'fade':
                    default:
                        oldone.fadeOut(s.duration, callback);
                        newone.fadeIn(s.duration);
                        break;
                }
            },

            // Timing
            timer,
            startTimer = function() {
                if (s.interval == false) {return;}
                if (!timer) {
                    timer = setInterval(function() {slide('next');}, s.interval);
                    return;
                }
                clearInterval(timer);
                timer = null;
                setTimeout(function() {startTimer();}, s.delayAfterAction - s.interval);
            },
            stopTimer = function() {
                clearInterval(timer);
            };


        // Is there correct carousel structure in DOM?
        if ($(carousel).children(_cls(s.classes.wrapper)).size() == 0) {
            alert('freshCarousel: Incorrect DOM structure in the given element \'' + _getElSelector(carousel) + '\'. See the documentation.');
            return;
        }
        
        // Initialize carousel...
        init(); // init. carousel
        nav(); // init. navigation
        bind(); // bind events (clicking, key pressing...)
        startTimer();
    };


    // Helpers -----------------------------------------------------------------

    function _cls(name) {
        return '.' + name;
    }

    function _getElSelector(el) {
        var id, cls, selector = $(el).attr('tagName').toLowerCase();
        if (id = $(el).attr('id')) {selector += '#' + id;}
        if (cls = $(el).attr('class')) {selector += '.' + cls.replace(/[\s]+/, '.');}
        return selector;
    }

})(jQuery);



// Repeater Plugin
(function ($) {
$.fn.repeat = function(times, string, callback) {
    // For each item matched
    this.each(function(){
        var buff = '';
        for(var i = 1; i <= times; i++){
            var rel = callback(i);
            string = rel ? $('<div>').append($(string).clone().attr('rel', rel)).remove().html() : string;
            buff += string;
        }
        $(this).append(buff);
    });
    return this;
};
})(jQuery);

