/*******
cbe_anim.js
CrossBrowserElement v3b15 (Animation)
Cross-Browser DHTML for IE 4+, NN 4+, Gecko, and Opera 4+
Download the latest version at cross-browser.com

Copyright (c) 2001 Michael Foster (mfoster@cybrtyme.com)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details, at www.gnu.org.
A copy of the GNU LGPL has been included with this distribution.
*******/

var cbeAnimJsLoaded = true;

function cbeSlideTo(x_cr, y_mar, inc, outside)
{
  var xTarget, yTarget;
  if (isFinite(x_cr)) {
    xTarget = x_cr;
    yTarget = y_mar;
  }
  else {
    this.relativePosition(x_cr, y_mar, outside);
    xTarget = this.x;
    yTarget = this.y;
  }
  //alert('this left/top: ' + this.left() + ', ' + this.top() + ' target x,y: ' + xTarget + ', ' + yTarget);
  if (this.linearPath(-1, this.left(), this.top(), xTarget, yTarget, inc)) {
    this.followPath(-1);
  }
}

function cbeArcTo(startAngle, stopAngle, incAngle, centerX, centerY, radius, radiusInc, useCenter, fn, fc)
{
  if (this.circularPath(-1, startAngle, stopAngle, incAngle, centerX, centerY, radius, radiusInc, useCenter, fn, fc)) {
    this.followPath(-1);
  }
}

function cbeLinearPath(pn, x1, y1, x2, y2, inc, fn, fc)
{
  var x, y, step, m, theta, d, incs, buf, q;
  if (pn == -1) {
    if (this.walk) {
      if (this.walk[0].active) return false;
      else delete this.walk;
    }  
  }
  else if (this.path[pn]) {
    if (this.path[pn].active) return false;
    else delete this.path[pn];
  }  
  inc = Math.abs(inc);
  m = cbeSlope(x1, y1, x2, y2);
  d = cbeDistance(x1, y1, x2, y2);
  theta = Math.atan(m);
  q = cbeQuadrant(x1, y1, x2, y2);
  if (q == 2 || q == 3) theta += cbeRadians(180);
  if (inc < 1) {
    var newD = 0;
    buf = new Array();
    step = 1; // init outside loop in case d==0
    for (; d; ++step) {
      delta = inc * d;
      if (delta < 1) d = 0;
      else {
        d -= delta;
        newD += delta;
        x = Math.round(newD * Math.cos(theta)) + x1;
        y = Math.round(newD * Math.sin(theta)) + y1;
        buf[step] = new cbeStep(x, y);
        if (fc && fn)
          buf[step].c = ((step % fc) == 0) ? true : false;
      }
    }               // end for
    incs = step - 1;
  }                 // end if(inc<1)
  else {
    incs = Math.ceil(d / Math.abs(inc));
    buf = new Array();
    //// buf = new Array(incs + 1);
    for (step = 1; step < incs; ++step) {
      x = Math.round(step * inc * Math.cos(theta)) + x1;
      y = Math.round(step * inc * Math.sin(theta)) + y1;
      buf[step] = new cbeStep(x, y);
      if (fc && fn)
        buf[step].c = ((step % fc) == 0) ? true : false;
    }
  }
  buf[incs] = new cbeStep(x2, y2);
  buf[incs].c = fn ? true : false;
  buf[0] = new cbePathHeader(1, fn);
  if (pn == -1) this.walk = buf;
  else this.path[pn] = buf;
  return true;
}

function cbeCircularPath(pn, startAngle, stopAngle, incAngle, centerX, centerY, radius, radiusInc, useCenter, fn, fc)
{
  var x, y, step, a, incs, buf;
  if (pn == -1) {
    if (this.walk) {
      if (this.walk[0].active) return false;
      else delete this.walk;
    }  
  }
  else if (this.path[pn]) {
    if (this.path[pn][0].active) return false;
    else delete this.path[pn];
  }  
  incAngle = Math.abs(incAngle);
  incs = Math.ceil(Math.abs((stopAngle - startAngle)/incAngle));
  buf = new Array(incs + 1);
  if (startAngle > stopAngle) incAngle = -incAngle;
  startAngle = cbeRadians(startAngle);
  stopAngle = cbeRadians(stopAngle);
  incAngle = cbeRadians(incAngle);
  for (step = 1, a = startAngle; step < incs; ++step, a += incAngle) {
    if (useCenter) {
      x = Math.round((Math.cos(a) * radius) + centerX - this.width()/2);
      y = Math.round((Math.sin(a) * radius) + centerY - this.height()/2);
    }
    else {
      x = Math.round((Math.cos(a) * radius) + centerX);
      y = Math.round((Math.sin(a) * radius) + centerY);
    }
    buf[step] = new cbeStep(x, y);
    radius += radiusInc;
    if (fc && fn)
      buf[step].c = ((step % fc) == 0) ? true : false;
  }
  if (useCenter) {
    x = Math.round((Math.cos(stopAngle) * radius) + centerX - this.width()/2);
    y = Math.round((Math.sin(stopAngle) * radius) + centerY - this.height()/2);
  }
  else {
    x = Math.round((Math.cos(stopAngle) * radius) + centerX);
    y = Math.round((Math.sin(stopAngle) * radius) + centerY);
  }  
  buf[incs] = new cbeStep(x, y);
  buf[incs].c = fn ? true : false;
  buf[0] = new cbePathHeader(1, fn);
  if (pn == -1) this.walk = buf;
  else this.path[pn] = buf;
  return true;
}

function cbeFollowPath(pn, iterating)
{
//alert('followpath: pn=' + pn +' ,iter= ' + iterating);
  var p;
  if (pn == -1) p = this.walk;
  else p = this.path[pn];
  //alert('p = ' + p);
  if (!p) return;
  if (arguments.length == 1 && this.moving) return;
  var step = p[0].step;
  if (this.stop && pn != -1) {
    this.moving = false;
    p[0].active = false;
    p[0].step = 1;
    return;
  }
  if (p.length > 1) {
    this.moveTo(p[step].x, p[step].y);
    ++step;
  }
  if (step < p.length) {
    this.moving = true;
    p[0].active = true;
    p[0].step = step;
	//alert("window.cbeAll["+this.index+"].followPath("+pn+",true)" + ", to= " + this.timeout);
    window.setTimeout("window.cbeAll["+this.index+"].followPath("+pn+",true)", this.timeout);
  }
  else {
    this.moving = false;
    p[0].active = false;
    p[0].step = 1;
    p[0].fn2(this, pn, (step-1), p.length - 1);
  }  
  if (p[step-1].c) {
    p[0].fn(this, pn, (step-1), p.length - 1);
  }  
}

function cbeCyclePaths()
{
  this.stop = false;
  this.followPath(0);
}

function cbeStopCycle()
{
  this.stop = true;
}


function cbeStep(x, y)            // Object constructor
{
  this.x = x;
  this.y = y;
  this.c = false;                 // call function indicator
}

function cbePathHeader(step, fn)  // Object constructor
{
  this.step = step;               // current step
  this.active = false;            // element is following this path
  this.fn2 = cbePathCycler;
  if (!fn) this.fn = null;        // function pointers
  else this.fn = fn;
}


function cbePathCycler(cbe, pn, step, steps)
{
  if (cbe.stop) {
    return;
  }
  if (++pn > cbe.path.length - 1) {
    pn = 0;
  }  
  cbe.followPath(pn);
}

function cbeQuadrant(x1, y1, x2, y2)
{
  var q = 0;
  if (x2 > x1 && y2 > y1) q = 1;
  else if (x2 < x1 && y2 >= y1) q = 2;
  else if (x2 < x1 && y2 < y1) q = 3;
  else if (x2 > x1 && y2 < y1) q = 4;
  return q;
}

function cbeDistance(x1, y1, x2, y2)
{
  return Math.sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
}

function cbeSlope(x1, y1, x2, y2)
{
  return (y2 - y1) / (x2 - x1);
}

function cbeRadians(deg)
{
  return deg * (Math.PI / 180);
}

function cbeDegrees(rad)
{
  return rad * (180 / Math.PI);
}

function cbeSlideToY(newYTarget, ySpeed, iterating)
{
  var delta, currentY;
  if (!this.moving) {
    this.yTarget = newYTarget;
    this.stop = false;
  }
  else if (!iterating) {
    this.yTarget = newYTarget;
    return;
  }
  currentY = this.top();
  this.moving = true;
  if (ySpeed < 1) {
    delta = ySpeed * Math.abs(Math.abs(currentY) - Math.abs(this.yTarget));
    if (delta < 1) delta = 1;
  }
  else
    delta = ySpeed;
  if (currentY < this.yTarget) {
    if (currentY + delta <= this.yTarget)
      this.top(currentY + delta);
    else
      this.top(this.yTarget);
  }
  else if (currentY > this.yTarget) {
    if (currentY - delta >= this.yTarget)
      this.top(currentY - delta);
    else
      this.top(this.yTarget);
  }
  else {
    this.moving = false;
    return;
  }
  if (!this.stop)
    setTimeout("window.cbeAll["+this.index+"].slideToY("+this.yTarget+","+ySpeed+","+true+")", this.timeout);
  else {
    this.stop = false;
    this.moving = false;
  }
}

var cbePathBuffer = null
var cbePathStep;
var cbePathCenterX, cbePathCenterY, cbePathFC;
var cbePathStepSpeed, cbePathStepTmp;

function cbeEllipticalPlot(x, y)
{
  --cbePathStepTmp;
  if (cbePathStepTmp)
    return;
  else
    cbePathStepTmp = cbePathStepSpeed;
  x += cbePathCenterX;
  y += cbePathCenterY;
  cbePathStep++;
  cbePathBuffer[cbePathStep] = new cbeStep(x, y);
  if (cbePathFC)
    cbePathBuffer[cbePathStep].c = ((cbePathStep % cbePathFC) == 0) ? true : false;
}

function cbeEllipticalPath(pn, startX, startY, endX, endY, centerX, centerY, radiusX, radiusY, speed, clockwise, fn_, fc_)
{
  var
    a = radiusX,
    b = radiusY;
  // this defines an elliptical conic
  var
    A = b*b,
    B = 0,
    C = a*a,
    D = 0,
    E = 0,
    F = -a*a*b*b;

  if (pn == -1) {
    if (this.walk) {
      if (this.walk[0].active) return false;
      else delete this.walk;
    }  
  }
  else if (this.path[pn]) {
    if (this.path[pn][0].active) return false;
    else delete this.path[pn];
  }  

  cbePathBuffer = new Array();
  cbePathBuffer.length = 0;
  cbePathStep = 0;
  cbePathCenterX = centerX;
  cbePathCenterY = centerY;
  cbePathFC = fc_;
  cbePathStepSpeed = speed ? speed : 3;
  cbePathStepTmp = cbePathStepSpeed;
  
  cbeConicPlotter(startX, startY, endX, endY, A,B,C,D,E,F, cbeEllipticalPlot);
  
  if (!clockwise) {
    cbePathBuffer.reverse();
    cbePathBuffer.length = cbePathStep; // dec length by 1
    --cbePathStep;
  }
  cbePathBuffer[cbePathStep].c = fn_ ? true : false;
  cbePathBuffer[0] = new cbePathHeader(1, fn_);
  if (pn == -1) this.walk = cbePathBuffer;
  else this.path[pn] = cbePathBuffer;
}

/***
2D Bresenham-like conic plotter.
conicPlotter(Sx,Sy, Ex,Ey, A,B,C,D,E,F, fn)
calls fn to plot the conic specified by
A x^2 + B x y + C y^2 + D x + E y + F = 0
from start point (Sx, Sy) to endpoint (Ex,Ey).
31-Mar-94: Originally written in C++ by Andrew W. Fitzgibbon <andrewfg@ed.ac.uk>
6-Oct-95: Version 2 bug fixes from Arne Steinarson <arst@ludd.luth.se>
22-May-01: Converted to Javascript by Mike Foster <mfoster@cybrtyme.com>
***/

function cbeOdd(n)
{
  return n & 1;
}

function cbeOctant(gx, gy)
{
  // Use gradient to identify octant.
  var upper = Math.abs(gx) > Math.abs(gy);
  if (gx >= 0) // Right-pointing
    if (gy >= 0) return 4 - upper; // Up
    else return 1 + upper; // Down
  else // Left
    if (gy > 0) return 5 + upper; // Up
    else return 8 - upper; // Down
}

function cbeConicPlotter(xs, ys, xe, ye, A, B, C, D, E, F, fnPlot)
{
  A *= 4, B *= 4, C *= 4, D *= 4, E *= 4, F *= 4;
  var
    DIAGx = new Array(999, 1,  1, -1, -1, -1, -1,  1,  1),
    DIAGy = new Array(999, 1,  1,  1,  1, -1, -1, -1, -1),
    SIDEx = new Array(999, 1,  0,  0, -1, -1,  0,  0,  1),
    SIDEy = new Array(999, 0,  1,  1,  0,  0, -1, -1,  0);
  // Translate start point to origin...
  F = A*xs*xs + B*xs*ys + C*ys*ys + D*xs + E*ys + F;
  D = D + 2 * A * xs + B * ys;
  E = E + B * xs + 2 * C * ys;
  // Work out starting octant
  var
    octant = cbeOctant(D,E),
    dxS = SIDEx[octant],
    dyS = SIDEy[octant],
    dxD = DIAGx[octant],
    dyD = DIAGy[octant],
    d,u,v;
  switch (octant) {
    case 1: d = A + B/2 + C/4 + D + E/2 + F; u = A + B/2 + D; v = u + E; break;
    case 2: d = A/4 + B/2 + C + D/2 + E + F; u = B/2 + C + E; v = u + D; break;
    case 3: d = A/4 - B/2 + C - D/2 + E + F; u = -B/2 + C + E; v = u - D; break;
    case 4: d = A - B/2 + C/4 - D + E/2 + F; u = A - B/2 - D; v = u + E; break;
    case 5: d = A + B/2 + C/4 - D - E/2 + F; u = A + B/2 - D; v = u - E; break;
    case 6: d = A/4 + B/2 + C - D/2 - E + F; u = B/2 + C - E; v = u - D; break;
    case 7: d = A/4 - B/2 + C + D/2 - E + F; u =  -B/2 + C - E; v = u + D; break;
    case 8: d = A - B/2 + C/4 + D - E/2 + F; u = A - B/2 + D; v = u - E; break;
    default: alert("CBE::conicPlotter error: invalid return from cbeOctant()"); return;
  }
  var
    k1sign = dyS*dyD,
    k1 = 2 * (A + k1sign * (C - A)),
    Bsign = dxD*dyD,
    k2 = k1 + Bsign * B,
    k3 = 2 * (A + C + Bsign * B);
  // Work out gradient at endpoint
  var
    gxe = xe - xs,
    gye = ye - ys ,
    gx = 2*A*gxe +   B*gye + D,
    gy =   B*gxe + 2*C*gye + E ,
    octantcount = cbeOctant(gx,gy) - octant;
  if (octantcount  <  0) octantcount = octantcount + 8;
  else if (octantcount==0)
    if((xs > xe && dxD > 0) || (ys > ye && dyD > 0) || (xs < xe && dxD < 0) || (ys < ye && dyD < 0)) octantcount +=8;
  var x = xs, y = ys;
  while (octantcount  >  0) {
    if (cbeOdd(octant)) {
      while (2*v  <= k2) {
        // Plot this point
        fnPlot(x,y);
        // Are we inside or outside?
        if (d  <  0) { x = x + dxS; y = y + dyS; u = u + k1; v = v + k2; d = d + u; } // Inside
        else { x = x + dxD; y = y + dyD; u = u + k2; v = v + k3; d = d + v; } // outside
      }
      d = d - u + v/2 - k2/2 + 3*k3/8; 
      // error (^) in Foley and van Dam p 959, "2nd ed, revised 5th printing"
      u = -u + v - k2/2 + k3/2;
      v = v - k2 + k3/2;
      k1 = k1 - 2*k2 + k3;
      k2 = k3 - k2;
      var tmp = dxS; dxS = -dyS; dyS = tmp;
    }
    else {                              // Octant is even
      while (2*u  <  k2) {
        // Plot this point
        fnPlot(x,y);
        // Are we inside or outside?
        if (d  >  0) { x = x + dxS; y = y + dyS; u = u + k1; v = v + k2; d = d + u; } // Outside
        else { x = x + dxD; y = y + dyD; u = u + k2; v = v + k3; d = d + v; } // Inside
      }
      var tmpdk = k1 - k2;
      d = d + u - v + tmpdk;
      v = 2*u - v + tmpdk;
      u = u + tmpdk;
      k3 = k3 + 4*tmpdk;
      k2 = k1 + tmpdk;
      var tmp = dxD; dxD = -dyD; dyD = tmp;
    }
    octant = (octant&7)+1;
    octantcount--;
  }
  // Draw final octant until we reach the endpoint
  if (cbeOdd(octant)) {
    while (2*v  <= k2) {
      // Plot this point
      fnPlot(x,y);
      if (x == xe && y == ye) break;
      // Are we inside or outside?
      if (d  <  0) { x = x + dxS; y = y + dyS; u = u + k1; v = v + k2; d = d + u; } // Inside
      else { x = x + dxD; y = y + dyD; u = u + k2; v = v + k3; d = d + v; } // outside
    }
  }
  else {                                // Octant is even
    while ((2*u  <  k2)) {
      // Plot this point
      fnPlot(x,y);
      if (x == xe && y == ye) break;
      // Are we inside or outside?
      if (d  >  0) { x = x + dxS; y = y + dyS; u = u + k1; v = v + k2; d = d + u; } // Outside
      else { x = x + dxD; y = y + dyD; u = u + k2; v = v + k3; d = d + v; } // Inside
    }
  }
  return 1;
}


// End cbe_anim.js
