/* $Id: bouncing_stars.js,v 1.6 2004/02/08 21:49:07 storem Exp $

Define an image that follows the cursor

Revisions:
  v1.0				Elastic Bullets
  v1.2	13/12/2000  Philip Winston (pwinston@yahoo.com)
  					"Scrolling Fix"
  					Troels Jakobsen <tjak@get2net.dk>
  v2.0  10/15/2001  Better documentation, more parameters
  					Pintér Gábor (propix@freemail.hu)
  v2.1  12/08/2001  NS6.1
  					Pintér Gábor (propix@freemail.hu)
  v2.2	08/10/2002	Bouncing Stars
  					Better browser detection, fix system freezes
					Add improved resistance code, fix resource hogging
  					Tim Dobbelaere (http://tim.dobbelaere.com)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program 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 General Public License for more details.

http://www.gnu.org/copyleft/gpl.html

Usage:
  1. Include this file in your page
  2. Define parameters or accept the defaults

This script requires Internet Explorer 5+ or a browser using a Gecko 5+ engine!
In other browsers it does nothing. On Mac OS, Gecko based browsers are ignored.

1. Include boucing_stars.js from the head of your page
Insert the following line into the head of your page:
  <script language="JavaScript" src="bouncing_stars.js" type="text/javascript"></script>

2. Define parameters
You can accept the defaults or assign new values to these variables:

bs_numdots=7
  Number of dots.
bs_img="star.gif"
  The image that follows the cursor.
bs_HTML=null
  Use this instead of bs_img if you want something else to follow the cursor.
bs_width=11; bs_height=11;
  Size of the image or the HTML object.
bs_seglength=10
  Length of the elastic band between two dots.
bs_springk=10
  Spring force constant.
bs_mass=1
  Mass of a bullet.
bs_gravity=50
  Gravity constant.
bs_resistance_min=8.5
  Resistance loss (constant when mouse moves).
bs_resistance_max=15
  Resistance at which the stars stop bouncing
bs_deltar=0.05
  Speed at which resistance is increased (and animation stopped when mouse doesn't move)
  # seconds = (bs_resistance_max-bs_resistance_min)/deltar*deltat
bs_bounce=0.85
  Bounce factor.
bs_zindex=5
  Define z order position of image.

Example: http://www.dobbelaere.com */

// Defaults
var bs_numdots= 7;
var bs_img= "star.gif";
var bs_HTML= null;
var bs_width= 11;
var bs_height= 11;
var bs_seglength= 10;
var bs_springk= 10;
var bs_mass= 1;
var bs_gravity= 50;
var bs_resistance_min= 8.5;
var bs_resistance_max= 15;
var bs_deltar= 0.05;
var bs_bounce= 0.85;
var bs_zindex= 5;

// Private variables
var bs_resistance=bs_resistance_min;
var bs_mousex=0, bs_mousey=0;
var bs_deltat=0.03;	// if smaller, Mozilla freezes the system
var bs_dots= new Array();

// Browser detection

// Global variable
var browsertype=0; // 0: unknown; 1:MSIE; 2:Gecko (NN, Mozilla)

// Return true if MSIE or Gecko detected
// detection by: Ultimate client-side JavaScript client sniff. Version 4.0
//               http://tim.dobbelaere.com/client_sniffer.html
function browserdetect() {
  var agt = navigator.userAgent.toLowerCase();

  var major = parseInt(navigator.appVersion);

  var ie      = ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1));
  var ie4     = (ie && (major == 4) && (agt.indexOf("msie 4") != -1) );
  var ie4up   = (ie && (major >= 4));
  var ie5up   = (ie4up && !ie4);

  var moz = ((agt.indexOf('mozilla') != -1) && (agt.indexOf('spoofer')==-1)
            && (agt.indexOf('compatible') == -1) && (agt.indexOf('opera')==-1)
            && (agt.indexOf('webtv')==-1) && (agt.indexOf('hotjava')==-1));
  var gecko = (agt.indexOf('gecko') != -1);
  var mozilla  = (moz && gecko);

  var mac68k = ((agt.indexOf("68k") != -1) || (agt.indexOf("68000") != -1));
  var macppc = ((agt.indexOf("ppc") != -1) || (agt.indexOf("powerpc") != -1));
  var mac = (mac68k || macppc || (agt.indexOf("mac") != -1));

  browsertype= ie5up ? 1 : mac ? 0 : mozilla ? 2 : 0;
  return(browsertype>0);
}

browserdetect();

// General utils

// Find object by name or id
function bs_obj(id) {
  var i, x;
  x= document[id];
  if (!x && document.all) x= document.all[id];
  for (i=0; !x && i<document.forms.length; i++) x= document.forms[i][id];
  if (!x && document.getElementById) x= document.getElementById(id);
  return(x);
}

// Move object
function bs_move(o, x, y) {
  if (!o) return;
  if (o.style) {
    o.style.left= x+"px";
    o.style.top= y+"px";
  } else {
    o.left= x;
    o.top= y;
  }
}

// Create dots
function bs_write() {
  var i, img;
  var x=bs_width/2, y=bs_height/2;

  if (browsertype==0) return;

  if (arguments.length==2) {
    x= arguments[0];
    y= arguments[1];
  }
  bs_mousex=x; bs_mousey=y;

  img= "";
  bs_dots[0]= new bs_dot(x, y);
  for (i=1; i<=bs_numdots; i++) {
    y+= bs_seglength;
    img+= "<div id='bs_dot"+i+"' style='position:absolute; left:"+x+"; top:"+y+"; "+
            "height:"+bs_height+"; width:"+bs_width+"; z-index:"+bs_zindex+"'>"+
	    (bs_HTML ? bs_HTML :
	      "<img src='"+bs_img+"' height='"+bs_height+"' width='"+bs_width+"' border='0'>")+
	  "</div>";
    bs_dots[i]= new bs_dot(x, y);
  }
  document.write(img);

  for (i=1; i<=bs_numdots; i++) {
    bs_dots[i].obj= bs_obj("bs_dot"+i);
  }

  switch (browsertype) {
    case 1:
      document.onmousemove=bs_mousemoveIE;
      break;
    case 2:
      document.captureEvents(Event.MOUSEMOVE);
      document.onmousemove=bs_mousemoveNS;
      break;
  }

  setInterval("bs_animate()", 1000*bs_deltat);
}

// Parameters of one dot
function bs_dot(x, y) {
  this.x= x;
  this.y= y;
  this.dx= 0;
  this.dy= 0;
  this.obj= null; // unknown
}

// Capture mouse position
function bs_mousemoveNS(e) {
  // Reset resistance increased in bs_animate()
  bs_resistance = bs_resistance_min;
  bs_mousex= e.pageX;
  bs_mousey= e.pageY;
  return(true);
}
function bs_mousemoveIE() {
  // Reset resistance increased in bs_animate()
  bs_resistance = bs_resistance_min;
  bs_mousex= window.event.clientX + document.body.scrollLeft;
  bs_mousey= window.event.clientY + document.body.scrollTop;
}

// Animation
function bs_animate() {

  // Vector object
  function bs_vec(X, Y) {
    this.x = X;
    this.y = Y;
  }

  // Add force in x and y direction to spring for bs_dot[i] on bs_dot[j]
  function bs_spring(i, j, spring) {
    var dx= bs_dots[i].x-bs_dots[j].x;
    var dy= bs_dots[i].y-bs_dots[j].y;
    var len= Math.sqrt(dx*dx+dy*dy);
    if (len>bs_seglength) {
      var springF= bs_springk*(len-bs_seglength);
      spring.x+= (dx/len)*springF;
      spring.y+= (dy/len)*springF;
    }
  }

  // Resistance at maximum indicates no mouse movements
  // Stop calculation to free system resources (primarily for Mozilla users)
  if (bs_resistance>bs_resistance_max) return;

  var winleft= browsertype==2 ? window.scrollX : document.body.scrollLeft;
  var wintop= browsertype==2 ? window.scrollY : document.body.scrollTop;
  var winwidth= browsertype==2 ? window.innerWidth-18 : document.body.clientWidth;
  var winheight= browsertype==2 ? window.innerHeight : document.body.clientHeight;

  bs_dots[0].x= bs_mousex-winleft;
  bs_dots[0].y= bs_mousey-wintop;
  for (i=1; i<=bs_numdots; i++) {
    var spring= new bs_vec(0, 0);
    bs_spring(i-1, i, spring);
    if (i<bs_numdots) {
      bs_spring(i+1, i, spring);
    }
    var resist= new bs_vec(-bs_dots[i].dx*bs_resistance, -bs_dots[i].dy*bs_resistance);
    var accel= new bs_vec((spring.x+resist.x)/bs_mass, (spring.y+resist.y)/bs_mass+bs_gravity);
    bs_dots[i].dx+= (bs_deltat*accel.x);
    bs_dots[i].dy+= (bs_deltat*accel.y);
    bs_dots[i].x+= bs_dots[i].dx;
    bs_dots[i].y+= bs_dots[i].dy;
    if (bs_dots[i].y>=winheight-bs_height/2-1) {
      if (bs_dots[i].dy>0) {
	bs_dots[i].dy= -bs_bounce*bs_dots[i].dy;
      }
      bs_dots[i].y= winheight-bs_height/2-1;
    }
    if (bs_dots[i].y<bs_height/2) {
      if (bs_dots[i].dy<0) {
	bs_dots[i].dy= -bs_bounce*bs_dots[i].dy;
      }
      bs_dots[i].y= bs_height/2;
    }
    if (bs_dots[i].x>=winwidth-bs_width/2) {
      if (bs_dots[i].dx>0) {
	bs_dots[i].dx= -bs_bounce*bs_dots[i].dx;
      }
      bs_dots[i].x= winwidth-bs_width/2-1;
    }
    if (bs_dots[i].x<bs_width/2) {
      if (bs_dots[i].dx<0) {
	bs_dots[i].dx= -bs_bounce*bs_dots[i].dx;
      }
      bs_dots[i].x= bs_width/2;
    }
    bs_move(bs_dots[i].obj, winleft+bs_dots[i].x-bs_width/2, wintop+bs_dots[i].y-bs_height/2);
  }

  // Increase the resistance, reset triggered by onMouseMove event
  bs_resistance += bs_deltar;
}

bs_write();
