//
// News/information scroller
//
// rjw 2/2008
// original code plus various pieces of other scrollers
//

//
// Scroller() - constructor
// arguments:
//   id            id of scrolling item container
//   itemclass     class name of scrolling item
//   pausetime     interval to pause on each item between scrolling (s)
//   pausetime0    first pause interval
//   scrollrate    scroll rate (pixels/s)
//   mousepause    pause on mouseover (true or false)
//
function Scroller(scrollerid, itemclass, pausetime, pausetime0, scrollrate, mousepause) {

  this.id = scrollerid;                // save container id and get container object
  this.obj = document.getElementById(scrollerid);

  this.y = 0;                  // vertical position

  this.numitems = 0;           // count of items - counted below
  this.curitem = 0;            // start with item 0
  this.targetitem = 0;         // scroll target item

  this.pausetime = Math.floor(pausetime*1000);  // pause time in ms
  this.scrollrate = scrollrate/1000;            // scroll rate in pixels/ms

  this.active = true;          // start out active (set to false on mouseover, if mousepause flag set)
  this.paused = true;          // start out in pause state

  this.lastupdatetime = new Date().getTime();  // counts for timers
  this.pausecount = pausetime0*1000;           // (pausecount counts down)

  // duplicate all of the item nodes, so that the top elements appear at the bottom
  //
  var childnodes = this.obj.childNodes;      // array of child nodes
  var numchildnodes = childnodes.length;
  for (i = 0; i < numchildnodes; i++) {
    this.obj.appendChild(childnodes[i].cloneNode(true));
  }

  // get vertical positions of items - used to stop scrolling at top of each item
  //
  this.itemoffsets = new Array();           // array of vertical positions
  // look for direct child nodes of container with class itemclass
  for (var i = 0; i < childnodes.length; i++) {
    var obj = childnodes.item(i);
    if (typeof obj.className != 'undefined' && obj.className == itemclass) {
      this.itemoffsets[this.itemoffsets.length] = obj.offsetTop;
      //alert("found item "+this.numitems+" at y = " + obj.offsetTop);
      ++this.numitems;
    }
  }
  // return if none found
  if (this.numitems == 0)
    return;

  // since the child nodes were doubled, we only scroll through half of them, plus one more
  //
  this.numitems = this.numitems/2 + 1;


  // set up mouse events and timer
  //
  var scrollerobj = this;
  if (mousepause) {
    this.obj.onmouseover = function() { scrollerobj.handleMouseOver() };
    this.obj.onmouseout = function(e) { scrollerobj.handleMouseOut(e) };
  }
  this.timer = window.setInterval(function(){scrollerobj.update();}, 15);
}

// update()
// called by interval timer
// updates counters and (if scrolling) position of container object
//
Scroller.prototype.update = function() {
  if (this.active) {  // check if active
    // get elapsed time, save new last update time
    var t = new Date().getTime();
    var dt = t - this.lastupdatetime;
    this.lastupdatetime = t;

    // handle pause condition
    if (this.paused) {
      // count down
      this.pausecount -= dt;
      // check if pause is over
      if (this.pausecount <= 0) {
        dt = -this.pausecount;        // save any leftover time, to apply to scrolling
        this.paused = false;
        // set up the next scroll
        this.targetitem = this.curitem + 1;
      }
    }
    // handle scroll condition
    if (!this.paused) {
      this.y += dt * this.scrollrate;
      // check if we've reached next item
      if (this.y >= this.itemoffsets[this.targetitem]) {
        // if pausing, set y to target item y
        if (this.pausetime > 0) {
          this.y = this.itemoffsets[this.targetitem];
        }
        // check if we've reached the last item
        if (this.targetitem == this.numitems - 1) {
          this.curitem = 0;
          this.y = 0;
        }
        // if not, increment current item and target item
        else {
          this.curitem = this.targetitem;
          this.targetitem = this.curitem + 1;
        }
        // if pausing, set up pause
        if (this.pausetime > 0) {
          this.paused = true;
          this.pausecount = this.pausetime;
        }
      }
      // no matter what happens, move container to current y position
      this.obj.style.top = "-" + Math.floor(this.y) + "px";
    }
  }
}

// mouseover handler
//
Scroller.prototype.handleMouseOver = function() {
  this.active = false;
}
// mouseout handler
//
Scroller.prototype.handleMouseOut = function(e) {
  e = e ? e : window.event;                                     // get event, so we know where mouse is going
  var toobj = e.relatedTarget ? e.relatedTarget : e.toElement;  // get object to which mouse is going
  if (!isObjInObj(toobj, this.obj)) {
    // going outside of container, so resume scroll
    this.lastupdatetime = new Date().getTime();
    this.active = true;
  }
}

// utility - check of obj1 is a descendant node of obj2 (or if they are the same node)
// returns true or false
// this is needed on mouseout, because sometimes a mouseout is not really a mouseout
//
function isObjInObj(obj1, obj2) {
  if (!obj1 || !obj2)
    return false;
  if (obj1 == obj2)
    return true;
  else
    return isObjInObj(obj1.parentNode, obj2);
}
