function prep_images(settings, el) {
    var $ul = $(el);
    var $firstimg = $ul.find('img:first-child');
    var width = $firstimg.width(); // assume all images are equally wide
    var height = $firstimg.height(); // this gets fine-tuned later
    
    var $lis = $ul.children();
    
    var num_images = $lis.length;
    var total_width = num_images * width;
    
    var titles = [];
    
    $lis.each(function(number) {
        /*
        We normalise the images here so that it is just a list of
        image tags.
        
        Possible contents are:
        
        1) <div class="frame"></div> with an image (possibly wrapped in a 
           link to the original) and a .info div with .title and possibly
           .description divs inside.
        
        2) an a.image linking to the original with the image inside it
        
        3) just an image            
        */
        
        var $li = $(this);
        
        var img_src = '';     
        var img_alt = '';
                    
        var img_link = '';            
        var img_title = '';
        var img_description = '';
        
        var $div_frame = $li.children('div.frame');
        var $a_img = $li.children('a.img');
        var $img = $li.children('img');
        
        if ($div_frame.length) {
            // div.frame
                        
            var $a_img = $div_frame.find('a.img')
            if ($a_img.length) {
                img_link = $a_img.attr('href');
            }
            
            var $img = $div_frame.find('img');
            img_src = $img.attr('src');
            img_alt = $img.attr('alt');
            
            img_title = $div_frame.find('.title').html();
            if (!img_title) {
                img_title = '';
            }
            titles[titles.length] = img_title;
            
            var $description = $div_frame.find('.description');
            if ($description.length) {
                img_description = $description.html();
            }
        
        } else if ($a_img.length) {
            // a.img
            
            img_link = $a_img.attr('href');
            
            var $img = $a_img.find('img');
            img_src = $img.attr('src');
            img_alt = $img.attr('alt');
            
            titles[titles.length] = '';
            
        } else if ($img.length) {
            //img
            
            img_src = $img.attr('src');
            img_alt = $img.attr('alt');
            
            titles[titles.length] = '';
            
        } else {
            titles[titles.length] = '';
            return; // rather just skip unknown ones
        }
        
        // set height so that it is at least as high as the shortest.
        var img_height = $img.height();
        if (img_height && (!height || img_height < height)) {
            height = img_height;
        }
        
        var html = '<img src="'+img_src+'" title="'+img_alt+'" />';
        $li.html(html);
        
    });
    
    // set the height so that absolute positioning works
    $lis.each(function() {
        $(this).css('height', height+'px');
    });
    
    $ul
        .css('width', total_width+'px')
        .css('height', height+'px')
        .wrap('<div class="'+settings.wrapclass+'"></div>');        
    
    var $wrapper = $ul.parent();
    
    $wrapper
        .css('width', width+'px')
        .css('height', height+'px');
    
    return {
        'width': width, 
        'height': height, 
        'num_images': num_images, 
        'titles': titles, 
        '$wrapper': $wrapper,
        '$ul': $ul
    }
}

jQuery.fn.igallery = function(options) {
    var settings = jQuery.extend({
        wrapclass: "igallerywrap"
    }, options);
    
    return this.each(function() {
        var info = prep_images(settings, this);
        
        var $ul = info['$ul'];
        var $wrapper = info['$wrapper'];
        var num_images = info['num_images'];
        var titles = info['titles'];
        var width = info['width'];
        var height = info['height'];
        
        // ======
            
        var next = '<a href="#next" class="next" title="next">Next</a>';
        var prev = '<a href="#prev" class="prev" title="previous">Previous</a>';
        var t_div = '<span class="title">'+titles[0]+'</span>'; 
        $wrapper.append('<div class="nav">'+t_div+' '+next+' '+prev+'</div>');
        
        $wrapper.append('<div class="total">1 / '+num_images+'</div>');
        
        var index = 0;
        $wrapper.find('.next').click(function() {
            if (index+1 < num_images) {                
                index++;
                $ul.animate({
                    marginLeft: -index*width+'px'                                               
                }, 100);
                if (index+1 >= num_images) {
                    $(this).css('display', 'none');                    
                }
                $wrapper.find('.prev').css('display', 'block');
                $wrapper.find('.total').text(index+1+' / '+num_images);
                $wrapper.find('.title').text(titles[index]);
            }
            
            return false;
        });
        
        $wrapper.find('.prev').click(function() {
            if (index > 0) {                
                index--;
                $ul.animate({
                    marginLeft: -index*width+'px'                                               
                }, 100);
                if (index <= 0) {
                    $(this).css('display', 'none');                    
                }
                $wrapper.find('.next').css('display', 'block');
                $wrapper.find('.total').text(index+1+' / '+num_images);
                $wrapper.find('.title').text(titles[index]);
            }
            return false;
        });
        
        $wrapper.mouseenter(function() {
            //$wrapper.find('.nav').css('display', 'block');
            $wrapper.addClass('mouseover');
            $wrapper.removeClass('mouseout');
        });
        
        $wrapper.mouseleave(function() {
            //$wrapper.find('.nav').css('display', 'none');
            $wrapper.addClass('mouseout');
            $wrapper.removeClass('mouseover');
        });
        
        $wrapper.addClass('mouseout');
    });
}

jQuery.fn.islideshow = function(options) {
    var settings = jQuery.extend({
        wrapclass: "islideshowwrap"
    }, options);
    
    return this.each(function() {
        if ($(this).find('li').length < 2) {        
            return;
        }
            
        var info = prep_images(settings, this);
        
        var $ul = info['$ul'];
        var $wrapper = info['$wrapper'];
        var num_images = info['num_images'];
        var titles = info['titles'];
        var width = info['width'];
        var height = info['height'];
        
        // ===
        
        var is_paused = false;
        
        if (is_paused) {
            klass = 'play';
        } else {
            klass = 'pause';
        }
        
        var playpause = '<a href="#" class="playpause '+klass+'">'+klass+'</a>';
        $wrapper.append(playpause);
        
        $playpause = $wrapper.find('.playpause');
        var pp_width = $playpause.width();
        var pp_height = $playpause.height();
        
        var pp_top = height/2-pp_height/2;
        var pp_left = width/2-pp_width/2;        
        $playpause.css('top', pp_top+'px');
        $playpause.css('left', pp_left+'px');
        
        var timeout_id = null; 
        
        $wrapper.find('.playpause').click(function() {
            if (is_paused) {
                is_paused = false;
                $(this).text('pause');
                clearTimeout(timeout_id);
                inc();
            } else{
                is_paused = true;
                $(this).text('play');
            }
            
            return false;
        });
        
        $wrapper.mouseenter(function() {
            //$wrapper.find('.playpause').css('display', 'block');
            $wrapper.addClass('mouseover');
            $wrapper.removeClass('mouseout');
        });
        
        $wrapper.mouseleave(function() {
            //$wrapper.find('.playpause').css('display', 'none');
            $wrapper.addClass('mouseout');
            $wrapper.removeClass('mouseover');
        });
        
        $wrapper.find('li:first').css('display', 'block');
        $wrapper.addClass('mouseout');
        
        var index = 0;
        
        function inc() {
            timeout_id = setTimeout(function() {
                //console.log('transition');
                if (is_paused) {
                    //console.log('pause');
                    return;
                }
                
                old_index = index;                
                index++;
                if (index >= num_images) {
                    index = 0;                    
                }
                
                $wrapper.find('li').css('display', 'none');
                
                $wrapper.find('li:eq('+old_index+')')
                    .css('display', 'block')
                    .css('z-index', 2)
                    .css('opacity', 1)
                    .animate({'opacity': 0}, 1000);
                
                $wrapper.find('li:eq('+index+')')
                    .css('display', 'block')
                    .css('z-index', 1)
                    .css('opacity', 1);
                
                inc();
            }, 4000)
        }
        
        inc();
        
    });
}
