jQuery.fn.imagesLoaded = function(callback){
  var elems = this.filter('img'),
      len = elems.length;
      
  elems.bind('load',function(){
      if (--len <= 0){ callback.call(elems,this); }
  }).each(function(){
     // cached images don't fire load sometimes, so we reset src.
     if (this.complete || this.complete === undefined){
        var src = this.src;
        // webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
        // data uri bypasses webkit log warning (thx doug jones)
        this.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
        this.src = src;
     }
  });

  return this;
};

/**
 * Fade and slide class.
 */
jQuery.fn.fadeSlider = function() {

  var args = arguments[0] || {};

  var slider_obj = $(this[0]);

  var slider_width  = slider_obj.width();
  var slider_height = slider_obj.height();
  var slider_treshold = 400;

  var slide_offset = 250;
  var slide_speed  = 1000;
  var slide_height = slider_height;
  var slide_width  = slider_width + (slide_offset * 2);

  var slides = $(slider_obj).find('.slides');
  var slides_count = slides.find('.slide').length;

  var buttons_speed = false;
  var buttons_distance = slider_obj.find('.buttons').height() / 2;

  var index_enabled = false;
  var index_rounded_corners = 4;
  var index_obj = slider_obj.find('.index');
  var index_speed = 500;

  var auto_enabled   = false;

  // We need at least two slides.
  if (slides_count < 2 && auto_enabled) { return false; }

  // Slides array.
  var slidesArray = new Array;
  var indexArray  = new Array;

  // Slide state.
  var curr_slide = 0;
  var next_slide = 1;
  var prev_slide = slides_count - 1;

  // Hide the buttons.
  if (buttons_speed) { slider_obj.find('.buttons').animate({'top' : '+='+ buttons_distance +'px', 'opacity' : 'toggle'}, 0); }

  // Set slide container and slides.
  slides.css({
    'width': slider_width +'px',
    'height': slider_height +'px',
    'position': 'relative',
    'overflow': 'hidden'
  }).find('.slide').each(function(count) {

    // Set image.
    var img = $(this).find('img');

    // Image dimensions.
    var img_w = img.width();
    var img_h = img.height();

    // Target dimensions / offset.
    if (img_w < slide_width) {
      img_w = slide_width + 20; var img_l = 10;
    } else {
      var img_l = Math.ceil((img_w - slide_width) / 2);
    }

    if (img_h < slide_height) {
      img_h = slide_height + 20; var img_t = 10;
    } else {
      var img_t = Math.ceil((img_h - slide_height) / 2);
    }

    // Set slide and image.
    $(this).attr('rel', count).css({
      'width': slide_width +'px',
      'height': slide_height +'px',
      'left': '-' + slide_offset +'px',
      'position': 'absolute',
      'overflow': 'hidden'
    }).find('img').css({
      'width': img_w,
      'height': img_h,
      'margin-left': '-'+ img_l +'px',
      'margin-top': '-'+ img_t +'px'
    });

    // Create index if enabled.
    if (index_enabled && index_obj.length > 0) {
      index_obj.append('<div class="item" rel="'+ count +'"><span>'+ (count + 1) +'</span></div>');

      // Then select it to add to the index array.
      indexArray[count] = index_obj.find('.item:last');
    }

    // Hide all except first.
    if (count != 0) { $(this).animate({'opacity' : 'toggle'}, 0); }

    // Save object into an array.
    slidesArray[count] = $(this);
  });

  // Reset the width due to scrollbars...
  slides.width(slider_obj.width()); $(window).bind('resize', function() { slides.width(slider_obj.width()); });

  // Setup the index.
  if (index_enabled && index_obj.length > 0) {

    // Select the first index.
    var first_index = index_obj.find('.item:first');

    var index_space = slides_count * first_index.outerWidth();
    var index_width = Math.floor((index_obj.width() - index_space - (2.5 * slides_count)) / slides_count);

    if (index_rounded_corners) { index_width -= 2 * index_rounded_corners; }

    var index_pos_change = Math.floor(index_width * 0.33);
    var index_neg_change = Math.floor(index_pos_change / slides_count);
    index_pos_change -= index_neg_change;

    var index_passive_width = index_width - index_neg_change;
    var index_active_width  = index_width + index_pos_change;

    index_obj.find('.item').width(index_passive_width);
    first_index.width(index_active_width);
  }

  // Automatic.
  if (auto_enabled) {

    // Boolean true uses calculation.
    if (auto_enabled === true) { auto_enabled = slide_speed * 3.5; }

    // Fix auto enabled time.
    if (auto_enabled < slide_speed) { auto_enabled = slide_speed; }

    // Set an interval.
    setInterval(function() { slide('right'); }, auto_enabled);

  // Buttons.
  } else {

    // Left button click.
    slider_obj.find('.buttons .button.left').bind('click', function() {
      slide('left'); return false;
    });

    // Right button click.
    slider_obj.find('.buttons .button.right').bind('click', function() {
      slide('right'); return false;
    });

    // Button hovering.
    slider_obj.find('.buttons .button').bind('mouseover mouseout', function() {
      $(this).toggleClass('active');
    });

    // Button mouseover.
    if (buttons_speed) { slider_obj.hover(function() {

      slider_obj.addClass('hover').find('.buttons').animate({'top' : '-='+ buttons_distance +'px', 'opacity' : 'toggle'}, buttons_speed);
    }, function() {

      slider_obj.removeClass('hover').find('.buttons').animate({'top' : '+='+ buttons_distance +'px', 'opacity' : 'toggle'}, buttons_speed);
    }); }
  }

  /**
   * Slide animation.
   */
  function slide(direction) {

    // Detect treshold height for slider.
    /*if (slider_treshold && slider_height != slider_treshold) {

      // Height becomes treshold.
      slider_height = slider_treshold;

      // Change the heights.
      slider_obj.height(slider_height);
      slides.height(slider_height);

      // Animate each image.
      for (x = 1; x < slidesArray.length; x++) {
        obj = slidesArray[x];
        //obj.animate({'top' : (obj.position.top + 100) +'px'}, 1000);
        obj.animate({'height' : '400px'}, 0);
      }
    }*/

    // Detect target slide.
    var targ_slide = (direction == 'left') ? prev_slide : next_slide;

    // Select targets.
    var current_slide = slidesArray[curr_slide];
    var target_slide  = slidesArray[targ_slide];

    // Exit during animation.
    if (slider_obj.find('div:animated').length != 0) { return false; }

    // Prepare slide.
    switch (direction) {
      case 'right':

        // Set base margin.
        target_slide.css('left', '0px');

        // Set slide offset.
        var current_offset = slide_offset * -2;
        var target_offset  = slide_offset * -1;

        break;

      case 'left':

        // Set base margin
        target_slide.css('left', (slide_offset * -2) +'px');

        // Set slide offset.
        var current_offset = 0;
        var target_offset  = slide_offset * -1;;

        break;
    }

    // Animate current slide out.
    if ($.browser.msie) {
      current_slide.animate({'opacity' : 'toggle'}, (slide_speed / 2));
      target_slide.animate({'opacity' : 'toggle'}, (slide_speed / 2));
    } else {
      current_slide.animate({'left' : current_offset +'px', 'opacity' : 'toggle'}, slide_speed);
      target_slide.animate({'left' : target_offset +'px', 'opacity' : 'toggle'}, slide_speed);
    }

    if (index_enabled) {

      var current_index = indexArray[curr_slide];
      var target_index  = indexArray[targ_slide];

      current_index.animate({'width' : index_passive_width +'px'}, index_speed);
      target_index.animate({'width' : index_active_width +'px'}, index_speed);
    }

    curr_slide = targ_slide;
    next_slide = (curr_slide + 1 == slides_count) ? 0 : curr_slide + 1;
    prev_slide = (curr_slide == 0) ? slides_count - 1 : curr_slide - 1;
  }
}
