/////////////////////////////////////////////////////////////////////////////

/* Elastic Trail script
 *
 * (Originally by Philip Winston @ pwinston@yahoo.com,
 * URL: http://www.geocities.com/pwinston/)
 * Script featured on Dynamicdrive.com
 * For this and 100's more DHTML scripts, visit http://dynamicdrive.com
 */

var nDots = 7;

var Xpos = 0;
var Ypos = 0;

// fixed time step, no relation to real time
var DELTAT = .01;

// size of one spring in pixels
var SEGLEN = 10;

// spring constant, stiffness of springs
var SPRINGK = 10;

// all the physics is bogus, just picked stuff to
// make it look okay
var MASS = 1;

// Positive XGRAVITY pulls right, negative pulls left
// Positive YGRAVITY pulls down, negative up
var XGRAVITY = 0;
var YGRAVITY = 50;

// RESISTANCE determines a slowing force proportional to velocity
var RESISTANCE = 10;

// stopping criterea to prevent endless jittering
// doesn't work when sitting on bottom since floor
// doesn't push back so acceleration always as big as gravity
var STOPVEL = 0.1;
var STOPACC = 0.1;
var DOTSIZE = 40;

// BOUNCE is percent of velocity retained when
// bouncing off a wall
var BOUNCE = 0.75;

var interval = 20;

// always on for now, could be played with to
// let dots fall to botton, get thrown, etc.
var followmouse = true;

/////////////////////////////////////////////////////////////////////////////

function finit ()
{
   // reset positions
   for (i = 0; i < nDots; i++) {
      dots[i].X = 0;
      dots[i].Y = 0;
      dots[i].dx = 0;
      dots[i].dy = 0;
      dots[i].obj.left = 0;
      dots[i].obj.top = 0;
   }
}

function dot (i)
{
   this.X = 0;
   this.Y = 0;
   this.dx = 0;
   this.dy = 0;
   if (! document.getElementById) /* Netscape 4 */
      this.obj = eval ("document.dot" + i);
   else /* other browsers */
      this.obj = document.getElementById ("dot" + i);
   if (this.obj.style) /* newer browsers */
      this.obj = this.obj.style;
}

function init ()
{
   var i = 0;
   for (i = 0; i < nDots; i++) {
      dots[i] = new dot (i);
   }

   // set their positions
   for (i = 0; i < nDots; i++) {
      dots[i].obj.left = dots[i].X;
      dots[i].obj.top = dots[i].Y;
   }

   setInterval ("animate()", interval);
   document.onunload = finit; /* Opera */
   document.onUnload = finit;
}

function MouseMove (event)
{
   if (document.all) /* MSIE, Konqueror, Opera */ {
      Xpos = window.event.clientX;
      Ypos = window.event.clientY;
      if (document.body.scrollLeft)
         Xpos += document.body.scrollLeft;
      if (document.body.scrollTop)
         Ypos += document.body.scrollTop;
   } else /* Netscape, Mozilla */ {
      Xpos = event.pageX;
      Ypos = event.pageY;
   }
   Xpos -= DOTSIZE/2;
   Ypos -= DOTSIZE/2;
}

function MouseInit ()
{
   if (document.all) /* MSIE, Konqueror, Opera */
      document.onmousemove = MouseMove;

   else /* Netscape, Mozilla */ {
      if (typeof (document.addEventListener) == "function") /* Netscape6, Mozilla */
         document.addEventListener ("mousemove", MouseMove, true);

      else /* Netscape4 */ {
         window.captureEvents (Event.MOUSEMOVE);
         window.onmousemove = MouseMove;
      }
   }
}

function vec (X, Y)
{
   this.X = X;
   this.Y = Y;
}

// adds force in X and Y to spring for dot[i] on dot[j]
function springForce (i, j, spring)
{
   var dx = dots[i].X - dots[j].X;
   var dy = dots[i].Y - dots[j].Y;
   var len = Math.sqrt (dx*dx + dy*dy);
   if (len > SEGLEN) {
      var springF = SPRINGK * (len - SEGLEN);
      spring.X += (dx / len) * springF;
      spring.Y += (dy / len) * springF;
   }
}

function animate ()
{
   // dots[0] follows the mouse,
   // though no dot is drawn there
   var start = 0;
   if (followmouse) {
      dots[0].X = Xpos;
      dots[0].Y = Ypos;
      start = 1;
   }

   for (i = start; i < nDots; i++)
   {
      var spring = new vec (0, 0);
      if (i > 0) {
         springForce (i-1, i, spring);
      }
      if (i < nDots - 1) {
         springForce (i+1, i, spring);
      }

      // air resistance/friction
      var resist = new vec (
         -dots[i].dx * RESISTANCE,
         -dots[i].dy * RESISTANCE
      );

      // compute new accel, including gravity
      var accel = new vec (
         (spring.X + resist.X) / MASS + XGRAVITY,
         (spring.Y + resist.Y) / MASS + YGRAVITY
      );

      // compute new velocity
      dots[i].dx += DELTAT * accel.X;
      dots[i].dy += DELTAT * accel.Y;

      // stop dead so it doesn't jitter when nearly still
      if (
         Math.abs (dots[i].dx) < STOPVEL &&
         Math.abs (dots[i].dy) < STOPVEL &&
         Math.abs (accel.X) < STOPACC &&
         Math.abs (accel.Y) < STOPACC
      ) {
         dots[i].dx = 0;
         dots[i].dy = 0;
      }

      // move to new position
      dots[i].X += dots[i].dx;
      dots[i].Y += dots[i].dy;

      // get size of window
      var height, width;
      if (document.layers) {
         width = window.innerWidth + window.pageXOffset;
         height = window.innerHeight + window.pageYOffset;
      } else {
         width = document.body.clientWidth + document.body.scrollLeft;
         height = document.body.clientHeight + document.body.scrollTop;
      }

      // bounce off 3 walls (leave ceiling open)
      if (dots[i].Y >= height - DOTSIZE - 1) {
         if (dots[i].dy > 0) {
            dots[i].dy = BOUNCE * -dots[i].dy;
         }
         dots[i].Y = height - DOTSIZE - 1;
      }
      if (dots[i].X >= width - DOTSIZE - 1) {
         if (dots[i].dx > 0) {
            dots[i].dx = BOUNCE * -dots[i].dx;
         }
         dots[i].X = width - DOTSIZE - 1;
      }
      if (dots[i].X < 0) {
         if (dots[i].dx < 0) {
            dots[i].dx = BOUNCE * -dots[i].dx;
         }
         dots[i].X = 0;
      }

      // move img to new position
      dots[i].obj.left = dots[i].X;
      dots[i].obj.top = dots[i].Y;
   }
}

/////////////////////////////////////////////////////////////////////////////

var dots = new Array ();

init ();
MouseInit ();

/////////////////////////////////////////////////////////////////////////////

