(function($) {

jQuery.fn.maxHeight = function() {
  var max = 0;
  $(this).each(function() {
    if($(this).height() > max) max = $(this).height();
  });
  return max;
}

jQuery.fn.maxWidth = function() {
  var max = 0;
  $(this).each(function() {
    if($(this).width() > max) max = $(this).width();
  });
  return max;
}

jQuery.fn.marquee = function(options) {

  options = $.extend({
    bulletFunc: function(i) { $(this).find("span").text(i); }, /* For manipulating the bullets after creation. */
    effect: {
      delay: 0,         /* For 'fade', delay after fadeOut and before fadeIn */
      name: "slide",    /* just slide or fade at the moment. specifying anything else will do a simple blink */
      speed: 600        /* The speed of the animation */
    },
    height: null,       /* Marquee Height. default: use height of largest frame */
    interval: 5000,     /* Time interval between transitions. */
    togglePlayText: "Play/Pause",
    width: null         /* Marquee Width. default: use width of target */
  }, options);
  
  $(this).each(function() {
    // get all elements into vars, create controls
    var $frameWrapper = $(this).addClass("frame_wrapper");
    var $frames = $frameWrapper.children().addClass("frame");
    var $marqueeWrapper = $("<div class='marquee_wrapper'></div>");
    $frameWrapper.wrap($marqueeWrapper);
    var $controls = $("<div class='controls'></div>");
    $frameWrapper.after($controls);
    var $togglePlay = $("<a href='#' class='toggle_play'><span>" + options.togglePlayText + "</span></a>");
    $controls.append($togglePlay);
    var $controlBulletsWrapper = $("<ul class='bullets'></ul>");
    $controls.append($controlBulletsWrapper);
    $frames.each(function(i) {
      $controlBulletsWrapper.append("<li><a href='#'><span>&nbsp;</span></a></li>");
    });
    var $controlBullets = $controlBulletsWrapper.children().addClass("bullet");
    $controlBullets.each(options.bulletFunc);
    
    // initialize frame sizes
    $frameWrapper.css({ height: $frames.maxHeight(), width: $frameWrapper.width() });
    if(options.height) $frameWrapper.css("height", options.height);
    if(options.width) $frameWrapper.css("width", options.width);
    $frames.css("position", "absolute");
    
    // standard functions
    function play(timeout) {
      if(!timeout) timeout = options.interval;
      $togglePlay.removeClass("paused").attr("title", "Pause");
      clearTimeout(marqueeTimeout);
      marqueeTimeout = setTimeout(function() { nextFrame() }, timeout);
    }
    
    function pause() {
      $togglePlay.addClass("paused").attr("title", "Play");
      clearTimeout(marqueeTimeout);
      marqueeTimeout = null;
    }
    
    function nextFrame($next) {
      var $active = $frames.filter(".active");
      if(!$next) {
        if($active[0] != $frames.filter(":last")[0]) {
          $next = $active.next();
        }
        else {
          $next = $frames.filter(":first");
        }
      }
      var activeIndex = $frames.index($active);
      var nextIndex = $frames.index($next);
      
      if(options.effect.name == "slide") {
        $frames.stop();
        $next.css("left", $frameWrapper.width()).show();
        $active.css("left", 0).show();
        $frames.not($active[0]).not($next[0]).css({ left: -$frameWrapper.width() }).removeClass("active");
        $active.removeClass("active").animate({ left: -$frameWrapper.width() }, options.effect.speed, function() {
          $frames.not($active[0]).not($next[0]).css("left", $frameWrapper.width());
          $active.css("left", $frameWrapper.width());
          $active.hide();
        });
        $next.addClass("active").animate({ left: 0 }, options.effect.speed);
        $controlBullets.eq(activeIndex).removeClass("active");
        $controlBullets.eq(nextIndex).addClass("active");
        if(!$togglePlay.is(".paused")) play();
      }
      else if(options.effect.name == "fade") {
        $controlBullets.removeClass("active");
        $controlBullets.eq(nextIndex).addClass("active");
        if($active != null) {
          setTimeout(function() { $active.removeClass("active").fadeOut(options.effect.speed); }, 1);
        }
        setTimeout(function() {
          $next.addClass("active").fadeIn(options.effect.speed);
          if(!$togglePlay.is(".paused")) play();
        }, new String(options.effect.delay).match(/^\d+$/) ? Math.max(1, options.effect.delay) : options.effect.delay);
      }
      else {
        // no effect, just hide/show
        $controlBullets.removeClass("active");
        $frames.removeClass("active").hide();
        $controlBullets.eq(nextIndex).addClass("active");
        $next.addClass("active").show();
        if(!$togglePlay.is(".paused")) play();
      }
    }
    
    // jump to frames using the controls
    $controlBullets.each(function(index) {
      $(this).click(function() {
        if($(this).is(".active")) {
          pause();
          return false;
        }
        pause();
        nextFrame($frames.eq(index));
        return false;
      });
    });
    
    // play/pause toggling
    $togglePlay.click(function() {
      if($togglePlay.is(".paused")) {
        play(100);
      }
      else {
        pause();
      }
      return false;
    });
    
    // init to first frame on load
    var marqueeTimeout = null;
    var emptyFunc = function() { return false; };
    $controlBullets.removeClass("active").filter(":first").addClass("active");
    $frames.hide().removeClass("active").filter(":first").addClass("active").css("left", 0).show();
    $frames.find("a").attr("tabindex",-1);
    $frameWrapper.trigger("play", play);
    $frameWrapper.trigger("pause", pause);
    $frameWrapper.trigger("jump", function(index) { nextFrame($frames.eq(index)); });
    play();
  });
}
})(jQuery);
