(function($) {
$.fn.home = function(o, pics) {
    o = $.extend({
        wait_ms: 7000,
        fade_ms: 2000,
        caption_slide: 500,
        caption_wait: 3000
    }, o || {});
    
    var self = this;
    
    // generic utilities
    function format(str) {
        for (var i = 1; i < arguments.length; i++)
            str = str.replace(new RegExp('\\{' + (i-1) + '}', 'g'), arguments[i]);
        return str;
    }

    function abort() {
        arguments[0] = 'home: ' + arguments[0];
        throw format.apply(null, arguments);
    }
    
    // set container css
    self.empty().css({
        overflow: 'hidden',
        padding: 0
    });
    if (! self.css('position').match(/absolute|relative|fixed/)){
        self.css({ position: 'relative' });
    }
    if (! self.width() || ! self.height()){
        abort('container element does not have its own width and height');
    }
    
    // append the image (or anchor) element to the container
    for (var i = 0; i < pics.length; ++i) {
        var p = pics[i];
        var elm;
        elm = jQuery(format('<a href="{0}" title="{1}"><img src="{2}" alt="{1}"/></a>', p.href, p.desc, p.src));
        elm.appendTo(self);
    }
    // find images to animate and set initial css attributes
    var imgs = self.find('a').css({
        position: 'absolute',
        visibility: 'hidden',
        top: 0,
        left: 0,
        border: 0
    });
    
    // add caption 
    elm = jQuery('<div id="caption"></div>');
    elm.appendTo(self);
    var cap = self.find('#caption');
    cap.css({
        opacity: 0.7,
        visibility: 'visible'
    });
    
    var i_max = pics.length - 1;
    
    function hide_caption(){
        cap.animate({top: '350px'}, o.caption_slide, 'linear');
    }
    
    function show_caption(i){
        cap.html('<p>' + pics[i].desc + '</p>');
        cap.animate({top: '315px'}, o.caption_slide, 'linear', function(){
            setTimeout(hide_caption, o.caption_wait);
        });
    }
    
    // show last image
    imgs.eq(i_max).css({ visibility: 'visible' });
    show_caption(i_max);
    
    // start of fading
    var i_from = i_max;
    function fade(){
        //console.log('fade', i_from)
        i_to = i_from - 1;
        // wrap around special case
        if (i_to < 0){
            i_to = i_max;
            var img_to = imgs.eq(i_to);
            var to_anim = {
                opacity: 1
            };
            var to_init = {
                visibility: 'visible'
            }
            img_to.css(to_init);
            img_to.animate(to_anim, o.fade_ms, 'linear', function(){
                show_caption(i_to);
            });
            i_from = i_to;
        }
        else{
            var img_from = imgs.eq(i_from);
            var img_to = imgs.eq(i_to);
        
            var to_init = {
                visibility: 'visible',
                opacity: 1
            }
        
            var from_anim = {
                opacity: 0
            };
            img_to.css(to_init);
            img_from.animate(from_anim, o.fade_ms, 'linear', function(){
                img_from.css({visibility: 'hidden'});
                show_caption(i_to);
            });
        }
        i_from = i_to;
    }
    
    setInterval(fade, o.wait_ms);
    //fade();
};
})(jQuery);