if(typeof(maxOpacity) == "undefined") {
  // Maximum permitted opacity
  // Going to 100% opacity in Firefox 1.5.03 switches the rendering mode in some way causing a
  // color change in OSX and blinking in XP. In Opera it causes the image to move.
  var maxOpacity = .99;
}

function rotate_divs(id, pause, initialPause) {
  var state = new Array();

  if(typeof(pause) != "undefined") {  // Length of time to display in milliseconds
    state["pause"] = pause;
  } else {
    if(typeof(visiblePause) != "undefined") {
      state["pause"] = visiblePause;
    } else {
      state["pause"] = 20000;      // 20 seconds
    }
  }
  if(typeof(initialPause) == "undefined") {
    initialPause = state["pause"]; // Length of time to display initially in milliseconds
  }

  state["elements"] = new Array();

  /**
   * Kintera is changing how they generate content listings. It seems to be changing, so
   * this just searches for the first element with more than one element child.
   */
  var parent = document.getElementById(id);
  do {
    var childCount = 0;
    var elementChild = undefined;
    for(var i = 0; i < parent.childNodes.length; i++) {
      var node = parent.childNodes[i];
      if(node.nodeType == Node.ELEMENT_NODE && node.nodeName.toLowerCase() != "link") {
        childCount++;
        elementChild = node;
      }
    }
    if(childCount == 1) {
      parent = elementChild;
    }
  } while(childCount == 1);

  var maxHeight = 0;
  for(var i = parent.childNodes.length - 1; i >= 0; i--) {
    var node = parent.childNodes[i];
    if(node.nodeType == Node.ELEMENT_NODE) {
      state["elements"].push(node);
      maxHeight = Math.max(maxHeight, node.offsetHeight);
      parent.removeChild(node);
    }
  }
  state["holder"] = document.createElement("div");
  state["holder"].className = "holder";
  state["holder"].style.height = maxHeight + "px";
  parent.appendChild(state["holder"]);
  state["elements"].randomize();
  state["currentIndex"] = 0;
  state["holder"].appendChild(state["elements"][state["currentIndex"]]);
  state["holder"].style.opacity = maxOpacity;
  var tId = state.threadId = 1;
  setTimeout(function() { hideElement(tId, state) }, initialPause);
  addListener(state["holder"], "mouseover", function() { holderHovered(state) });
  addListener(state["holder"], "mouseout", function() { holderLeft(state) });
}

function holderHovered(state) {
  state.hovered = true;
  if(state.state != "showing") {
    var tId = ++state.threadId;
    showElement(tId, state);
  }
}

function holderLeft(state) {
  state.hovered = false;
  var tId = ++state.threadId;
  hideElement(tId, state);
}

function hideElement(threadId, state) {
  if(state.state != "hiding" && !state.hovered) {
    state.state = "hiding";
    updateElement(threadId, state);
  }
}

function showElement(threadId, state) {
  if(state.state != "showing") {
    state.state = "showing";
    updateElement(threadId, state);
  }
}

/**
 * The threadId is needed because it is possible to spawn off a new timeout while one
 * is waiting. This guarantees that only the most recently spawned timeout will be
 * processed.
 */
function updateElement(threadId, state) {
  if(threadId == state.threadId) {
    var currentOpacity = parseFloat(state["holder"].style.opacity);
    var newOpacity = -1;
    if(currentOpacity > 0 && state.state == "hiding") {
      newOpacity = Math.max(currentOpacity - .05, 0);
    } else if(currentOpacity < maxOpacity && state.state == "showing") {
      newOpacity = Math.min(currentOpacity + .05, maxOpacity);
    }
    if(newOpacity >= 0) {
      state["holder"].style.opacity = newOpacity;
      state["holder"].style.filter = "alpha(opacity=" + (newOpacity * 100) + ")";
      var delay = 100;
      if(state.hovered) {
        delay = 5;
      } else if(state.state == "showing") {
        delay = 50;
      }
      var tId = ++state.threadId;
      setTimeout(function() { updateElement(tId, state) }, delay);
    } else if(!state.hovered) {
      if(state.state == "hiding") { // hidden
        state["holder"].removeChild(state["elements"][state["currentIndex"]]);
        state["currentIndex"] = (state["currentIndex"] + 1) % state["elements"].length;
        state["holder"].appendChild(state["elements"][state["currentIndex"]]);
        var tId = ++state.threadId;
        showElement(tId, state);
      } else if(state.state == "showing") { // shown
        var tId = ++state.threadId;
        setTimeout(function() { hideElement(tId, state) }, state["pause"]);
      }
    }
  }
}
