/*****************************************************************************************
Name: jquery.happyrotate.js
Description:
  Rotates images at fixed intervals, fading each out and replacing with random image that
  is not currently visible. Allows for multiple images to be displayed concurrently.
Author: Jeff Wintersinger
Created: 2008.07.01
Last modified: 2008.07.01

Usage:
  <div id="container">
    <div id="rotate-1"><img src="" alt="" /></div>
    <div id="rotate-2"><img src="" alt="" /></div>
  </div>

  <script type="text/javascript">
  $('#container').happyrotate({
    // Note that number of image paths passed must be greater than number of children of
    // #container -- that is, you must have more images to display than containers to
    // display them in, or script will be unable to find unused image to display on
    // next rotation.
    image_paths: ['dir/photo-1.jpg', 'dir/photo-2.jpg', 'dir/photo-3.jpg'], // Required.
    speed: 'normal',// 'slow', 'normal', 'fast', or time in millseconds; default = 'normal'
    delay: 400,     // Time in milliseconds before next rotation; default = 400
    fade_out_to: 0  // Float in interval [0, 1] indicating opacity to fade to
                    // (where 0 = invisible); default = 0
  });
  </script>
******************************************************************************************/

(function($) {
  $.fn.happyrotate = function(options) {
    var settings = {
      'image_paths': [],
      'speed': 'normal',
      'delay': 400,
      'fade_out_to': 0
    };
    if(options) $.extend(settings, options);
    var containers = $(this).children();
    if(settings.image_paths.length <= containers.length) {
      throw 'image_paths must contain more elements than you have containers to display in.'
    }
    if(containers.length > 0) {
      $.fn.happyrotate.rotate(containers, settings, 0);
    } else {
      throw 'No children containers to display in.'
    }
      
  }

  $.fn.happyrotate.rotate = function(containers, settings, last_slot_index) {
    var current_slot_index = containers.length > 1 ?
      random_between_except(0, containers.length, last_slot_index) : last_slot_index;
    $(containers.get(current_slot_index)).find('img').fadeTo(settings.speed, settings.fade_out_to, function() {
      $(this).attr('src', $.fn.happyrotate.select_image(containers, settings.image_paths));
      $(this).fadeTo(settings.speed, 1, function() {
        setTimeout(function() {
          $.fn.happyrotate.rotate(containers, settings, current_slot_index);
        }, settings.delay);
      });
    });
  }

  $.fn.happyrotate.select_image = function select_image(containers, image_paths) {
    var invisible_paths = [];
    var visible_paths = containers.find('img').map(function() { return $(this).attr('src'); });
    for(var i = 0; i < image_paths.length; i++) {
      if($(visible_paths).index(image_paths[i]) == -1) invisible_paths.push(image_paths[i]);
    }
    return random_element(invisible_paths);
  }
})(jQuery);


// Returns random integer in interval [lower, upper).
function random_between(lower, upper) {
  if(lower > upper) throw 'lower must be <= upper (lower = ' + lower + ', upper = ' + upper
  return Math.floor(Math.random() * (upper - lower)) + lower;
}

// Returns random integer in interval [lower, upper), while guaranteeing that it is not equal to except.
function random_between_except(lower, upper, except) {
  if(upper - lower <= 1 && (lower == except || upper == except)) {
    throw 'Impossible to find integer in range [' + lower + ', ' + upper + '), != ' + except
  }
  do { var num = random_between(lower, upper); } while(num == except);
  return num;
}

function random_element(arr) {
  if(arr.length == 0) throw 'Cannot retrieve element from empty array'
  return arr[random_between(0, arr.length)];
}
