/*
 * Scroller.js
 * Scrolling selection control
 *
 * Version: 1.0. Initial release.
 * Version 1.1. Don't use root.children for IE as it includes comment nodes.
 *
 * Copyright (c) 2002 David C A Croft. All Rights Reserved.
 * E-mail: david at davidc dot net
 * http://davidc.net/dhtml/tut/scroller.html
 */

/* global array of all scrollers, for instance timeout */
var scrollers = new Array();

/* constructor */

function Scroller(root)
{
  this.scrollerId = scrollers.length;
  scrollers[this.scrollerId] = this;

  this.state = new Object();
  this.state.pendingClicks = 0; /* positive to move forwards, negative move back */

  this.state.moving = false;
  this.state.movingDir = 0;
  this.state.movingOut = null;
  this.state.movingIn = null;
  this.state.movingStep = 0;
  this.state.current = 0;

  this.config = new Object();
  this.config.movingSteps = 15;
  this.config.movingPeriod = 500; /* milliseconds to scroll left/right once */
  this.config.root = root;
  this.config.numElements = -1; /* defer calculation until document loaded */
}

Scroller.prototype.back = function ()
{
  this.maybeInit();
  if (this.config.numElements < 2) return;
  this.state.pendingClicks --;
  this.startMove();
}

Scroller.prototype.forward = function ()
{
  this.maybeInit();
  if (this.config.numElements < 2) return;
  this.state.pendingClicks ++;
  this.startMove();
}

Scroller.prototype.init = function ()
{
  /* count children of the root to get numElements */
  var root = document.getElementById(this.config.root);

  if (root.children && false) {
    /* IE - only HTML elements are children */
    this.config.numElements = root.children.length;
  }
  else {
    /* Non-IE. Construct 'children' using childNodes */
    root.scrollElements = new Array();
    var t = root.firstChild;
    this.config.numElements = 0;
    while (t) {
      if (t.nodeType == 1) { /* Element node */
	root.scrollElements[root.scrollElements.length] = t;
	this.config.numElements ++;
      }
      t = t.nextSibling;
    }
  }
}

Scroller.prototype.maybeInit = function ()
{
  if (this.config.numElements < 0) {
    this.init();
  }
}

Scroller.prototype.startMove = function ()
{
  if (this.state.moving || this.state.pendingClicks == 0) {
    return;
  }

  this.maybeInit();

  if (this.state.pendingClicks < 0) {
    this.state.movingDir = -1;
    this.state.pendingClicks ++;
  }
  else {
    this.state.movingDir = 1;
    this.state.pendingClicks --;
  }

  this.state.moving = true;
  this.state.movingStep = 0;
  this.state.movingOut = this.state.current;
  this.state.movingIn = this.state.current + this.state.movingDir;

  if (this.state.movingIn < 0) {
    this.state.movingIn = this.config.numElements-1;
  }
  else if (this.state.movingIn >= this.config.numElements) {
    this.state.movingIn = 0;
  }

  if (this.onScrollStart) {
    /* TODO this should only trigger on INITIAL start of scrolling */
    this.onScrollStart();
  }

  this.moveStep();
}

function clipTo(obj, t, r, b, l)
{
  obj.style.clip = "rect("+t+"px "+r+"px "+b+"px "+l+"px)";
}

Scroller.prototype.moveStep = function ()
{
  this.state.movingStep ++;

  var root = document.getElementById(this.config.root);
  var movingInDiv = root.scrollElements[this.state.movingIn];
  var movingOutDiv = root.scrollElements[this.state.movingOut];

  movingInDiv.style.display = 'block';
  movingOutDiv.style.display = 'block';

  divWidth = parseInt(root.offsetWidth);
  divHeight = parseInt(root.offsetHeight);

  var done = (this.state.movingStep/this.config.movingSteps);

  if (this.state.movingDir < 0) {
    /* moving left, so cur moves to the right, new moves in from the left. */
    clipTo(movingOutDiv, 0, divWidth-(divWidth*done), divHeight, 0);
    movingOutDiv.style.left = (divWidth*done);
    clipTo(movingInDiv, 0, divWidth, divHeight, divWidth-(divWidth*done));
    movingInDiv.style.left = -divWidth+(divWidth*done);
  }
  else {
    /* moving right, so cur moves to the left, new moves in from the right. */
    clipTo(movingOutDiv, 0, divWidth, divHeight, divWidth*done);
    movingOutDiv.style.left = -(divWidth*done);
    clipTo(movingInDiv, 0, (divWidth*done), divHeight, 0);
    movingInDiv.style.left = divWidth-(divWidth*done);
  }

  if (this.state.movingStep == this.config.movingSteps) {
    movingOutDiv.style.display = 'none';
    this.state.moving = false;
    this.state.current = this.state.movingIn;
    this.state.movingIn = null;
    this.state.movingOut = null;

    if (this.onChange) {
      this.onChange();
    }
    if (this.state.pendingClicks) {
      this.startMove();
    }
    else if (this.onScrollStop) {
      this.onScrollStop();
    }
  }
  else {
    window.setTimeout('scrollers['+this.scrollerId+'].moveStep()', this.config.movingPeriod/this.config.movingSteps);
  }
}

Scroller.prototype.scrollTo = function (element)
{
  if (this.config.numElements < 0) {
    this.init();
  }

  if (element < 0) {
    element = this.config.numElements + element;
  }

  if (this.state.current == element && !this.state.moving) {
    //    if (this.state.moving) {
      /* already moving, move back */
    //      this.state.pendingClicks = 0 - this.state.movingDir;
    //    }
    return;
  }
  else if (this.state.moving && this.state.movingIn == element) {
    pendingClicks = 0;
    return;
  }

  var current = this.state.current;

  if (this.state.moving) {
    current = this.state.movingIn;
  }

  if (current < element) {
    var forward = element - current;
    var back = 0 - (current + (this.config.numElements - element));
  }
  else {
    var forward = (this.config.numElements - current) + element;
    var back = element - current;
  }

  this.state.pendingClicks = (Math.abs(forward) > Math.abs(back) ? back : forward);

  this.startMove();
}

