/*
 * jQuery Cycle Plugin for light-weight slideshows
 * Examples and documentation at: http://malsup.com/jquery/cycle/
 *
 * @author: M. Alsup
 * @version: 1.3 (8/09/2007)
 * @requires jQuery v1.1.3.1 or later
 *
 * Based on the work of:
 *  1) Matt Oakes (http://portfolio.gizone.co.uk/applications/slideshow/)
 *  2) Torsten Baldes (http://medienfreunde.com/lab/innerfade/)
 *  3) Benjamin Sterling (http://www.benjaminsterling.com/experiments/jqShuffle/)
 */
(function($) {

$.fn.cycle = function(options) {
    return this.each(function() {
        if (options && options.constructor == String) {
            if (options == 'stop') {
                if (this.cycleTimeout) clearTimeout(this.cycleTimeout);
                this.cycleTimeout = 0;
                return;
            }
            options = { fx: options };
        }
        var $this = $(this), $els = $this.children(), els = $els.get();
        if (els.length < 2) return; // don't bother

        var opts = $.extend({}, $.fn.cycle.defaults, options || {}, $.meta ? $this.data() : {});
        if (opts.autostop) opts.countdown = els.length;

        // allow shorthand overrides of width, height and timeout
        var cls = this.className;
        var w = parseInt((cls.match(/w:(\d+)/)||[])[1]) || opts.width
        var h = parseInt((cls.match(/h:(\d+)/)||[])[1]) || opts.height;
        opts.timeout = parseInt((cls.match(/t:(\d+)/)||[])[1]) || opts.timeout;

        if (opts.fx.indexOf('scroll') == 0) $this.css('overflow','hidden');
        if ($this.css('position') == 'static') $this.css('position', 'relative');
        if (w) $this.width(w);
        if (h && h != 'auto') $this.height(h);

        $els.each(function(i){$(this).css('z-index', els.length-i);}).css('position','absolute').hide();
        opts.fx == 'shuffle' ? $els.show() : $(els[0]).show();
        if (opts.fit && w) $els.width(w);
        if (opts.fit && h && h != 'auto') $els.height(h);
        if (opts.pause) $this.hover(function(){opts.paused=1;}, function(){opts.paused=0;});
        if (opts.fx == 'shuffle') {
            opts.random = 0;
            opts.shuffle = opts.shuffle || {left:-$this.width(), top:15};
            opts.els = [];
            for (var i=0; i < els.length; i++)
                opts.els.push(els[i]);
        }
        else if (opts.fx == 'zoom') {
            var $e0 = $(els[0]);
            var x = $e0.width(), y = $e0.height();
            $(els).not(':eq(0)').css({left:x/2,top:y/2,width:0,height:0});
        }
        else if (opts.fx == 'scrollLeft')
            $(els).not(':eq(0)').css({left:els[0].offsetWidth});
        else if (opts.fx == 'scrollRight')
            $(els).not(':eq(0)').css({left:-els[0].offsetWidth});
        else if (opts.fx == 'scrollUp')
            $(els).not(':eq(0)').css({top:els[0].offsetHeight});
        else if (opts.fx == 'scrollDown')
            $(els).not(':eq(0)').css({top:-els[0].offsetHeight});

        // ensure that timeout and speed settings are sane
        if (opts.speed.constructor == String)
            opts.speed = {slow: 600, fast: 200}[opts.speed] || 400;
        if (!opts.sync || opts.fx == 'shuffle')
            opts.speed = opts.speed / 2; // shuffle has 2 transitions
        while((opts.timeout - opts.speed) < 250)
            opts.timeout += opts.speed;

        if (opts.easing) opts.easeIn = opts.easeOut = opts.easing;
        if (!opts.speedIn) opts.speedIn = opts.speed
        if (!opts.speedOut) opts.speedOut = opts.speed;

        opts.curr = opts.random ? (Math.floor(Math.random() * (els.length-1)))+1 : 1;
        opts.last = 0;
        this.cycleTimeout = setTimeout(function(){$.fn.cycle.go(els, opts)}, opts.timeout + (opts.delay||0));
    });
};

$.fn.cycle.go = function (els, opts) {
    if (els[0].parentNode.cycleTimeout === 0) return;
    var last = els[opts.last], curr = els[opts.curr];

    if (opts.before) opts.before.apply(curr);
    var after = opts.after ? function(){opts.after.apply(curr)} : null;

    if (!opts.paused) {
        if (opts.autostop && --opts.countdown == 0) return;
        switch(opts.fx) {
        case 'scrollLeft':  $.fn.cycle.scroll(last, curr, 'left', 0, opts, after); break;
        case 'scrollRight': $.fn.cycle.scroll(last, curr, 'left', 1, opts, after); break;
        case 'scrollUp':    $.fn.cycle.scroll(last, curr, 'top',  0, opts, after); break;
        case 'scrollDown':  $.fn.cycle.scroll(last, curr, 'top',  1, opts, after); break;
        case 'slideX':      $.fn.cycle.slide(last, curr, 'width', opts, after);    break;
        case 'slideY':      $.fn.cycle.slide(last, curr, 'height', opts, after);   break;
        // this allows for plugable extensions
        default: $.fn.cycle[opts.fx](last,curr,opts,after);
        };

        if (opts.random) {
            opts.last = opts.curr;
            while (opts.curr == opts.last)
                opts.curr = Math.floor(Math.random() * els.length);
        }
        else { // sequence
            var roll = (opts.curr + 1) == els.length;
            opts.curr = roll ? 0 : opts.curr+1;
            opts.last = roll ? els.length-1 : opts.curr-1;
        }
    }
    setTimeout(function() { $.fn.cycle.go(els, opts) }, opts.timeout);
};

$.fn.cycle.fade = function(last, curr, opts, cb) {
    var fn = function() {$.fn.cycle.fadeToggle(curr, opts, cb)};
    $.fn.cycle.fadeToggle(last, opts, !opts.sync ? fn : null);
    if (opts.sync) fn();
};

// this fn is a bit more robust than the core fade method
$.fn.cycle.fadeToggle = function(el, opts, cb){
   var $el = $(el);
   var to = $el.is(':visible') && $el.css('opacity') > 0 ? 0 : 1;
   if (to) $el.css('opacity',0).show();
   $el.animate({opacity: to}, to ? opts.speedIn : opts.speedOut, to ? opts.easeIn : opts.easeOut, function() {
       to ? $el.show() : $el.hide();
       if (cb) cb();
   });
};

// hat tip to Benjamin Sterling for this bit of sweetness!
$.fn.cycle.shuffle = function(last, curr, opts, cb) {
    var $el = $(last);
    $el.animate(opts.shuffle, opts.speedIn, opts.easeIn, function() {
        opts.els.push(opts.els.shift());
        for (var i=0, len=opts.els.length; i < len; i++)
            $(opts.els[i]).css('z-index', len-i);
        $el.animate({left:0, top:0}, opts.speedOut, opts.easeOut, cb);
    });
};

$.fn.cycle.slide = function(last, curr, dir, opts, cb) {
    var ani1 = {}, ani2 = {};
    ani1[dir] = 'show'; ani2[dir] = 'hide';
    var fn = function() {$(curr).animate(ani1, opts.speedIn, opts.easeIn, cb)};
    $(last).animate(ani2, opts.speedOut, opts.easeOut, !opts.sync ? fn : null);
    if (opts.sync) fn();
};

$.fn.cycle.scroll = function(last, curr, dir, pos, opts, cb) {
    var ani1 = {}, ani2 = {}, $l = $(last), $c = $(curr);
    var dim1 = dir == 'left' ? last.offsetWidth : last.offsetHeight;
    var dim2 = dir == 'left' ? curr.offsetWidth : curr.offsetHeight;
    ani1[dir] = pos ? dim1 : -dim1;
    ani2[dir] = 0;
    var fn = function() { $c.animate(ani2, opts.speedIn, opts.easeIn, cb); };
    $l.animate(ani1, opts.speedOut, opts.easeOut, function() {
        $l.css(dir, pos ? -dim1 : dim1); 
        if (!opts.sync) fn();
    });
    if (opts.sync) fn();
};

$.fn.cycle.zoom = function(last, curr, opts, cb) {
    var $l = $(last), x = $l.width(), y = $l.height();
    var fn = function(){$(curr).animate({left:0,top:0,width:x,height:y}, opts.speedIn, opts.easeIn, cb)};
    $l.animate({left:x/2,top:y/2,width:0,height:0}, opts.speedOut, opts.easeOut, function() {
        $l.hide();
        if (!opts.sync) fn();
    });
    if (opts.sync) fn();
};

// override these globally if you like
$.fn.cycle.defaults = {
    fx:      'fade', // one of: fade, shuffle, zoom, slideX, slideY, scrollUp/Down/Left/Right
    timeout:  4000,  // ms duration for each slide
    speed:    1000,  // speed of the transition (any valid fx speed value)
    speedIn:  null,  // speed of the 'in' transition
    speedOut: null,  // speed of the 'out' transition
    before:   null,  // transition callback (scope set to element to be shown)
    after:    null,  // transition callback (scope set to element that was shown)
    easing:   null,  // easing method for both in and out transitions
    easeIn:   null,  // easing for "in" transition
    easeOut:  null,  // easing for "out" transition
    shuffle:  null,  // coords for shuffle animation, ie: { top:15, left: 200 }
    height:  'auto', // container height
    sync:     1,     // true if in/out transitions should occur simultaneously
    random:   0,     // true for random, false for sequence (not applicable to shuffle fx)
    fit:      0,     // force slides to fit container
    pause:    0,     // true to enable "pause on hover"
    autostop: 0,     // true to end slideshow after X transitions (where X == slide count)
    delay:    0      // additional delay (in ms) for first transition (hint: can be negative)
};

})(jQuery);
