/***********************************************************************************

CROSS-PLATFORM JAVASCRIPT API FOR DYNAMIC POSITIONING AND LAYERING 
OF ELEMENTS IN A HTML DOCUMENT

This script is intended for linking or embedding into an HTML page, providing the page author
global objects for dynamic positioning and layering of elements. The script is cross platform
and will work in any HTML browser that reads Javascript. 

Author: Jim Dunn jim@brothersmedia.com
©  2001 All Rights Reserved

**********************************************************************************/

/***********************************************************************************
															GLOBAL VARIABLES

The following global variables are made available so that page authors can use them for browser 
detection if nessessary. Note that this API detects objects from the browser's DOM so that 
knowledge of specific browser names is not needed. The two variables
below are only created so that they are available to the developer, and for the few netscape 
specific issues in this API (Such as the resizing bug in Netscape 4).
**********************************************************************************/

var exp = navigator.appName.indexOf("Microsoft Internet Explorer") != -1;
var net = navigator.appName.indexOf("Netscape") != -1;

/**********************************************************************************
														UTILITY FUNCTIONS
														
The following functions provide utilities mostly for background use by the objects in this API.
**********************************************************************************/
 
/********************************************************************************
Handle the resizing bug in navigator 4. This bug affects DHTML pages loaded into the first release of
Netscape 4. The bug causes elements to lose their positioning when the window is resized. The following 
code forces a refresh under these circumstances.
********************************************************************************/

function handleResize(){
	location.reload();
	return false;
}
if(window.captureEvents){
	window.captureEvents(Event.RESIZE);
}
window.onresize=handleResize;
/***********************************************************************
Block errors. Developers may wish to comment out the following line during development.
***********************************************************************/

//self.onerror = function() { return true; }

/***********************************************************************
Return a style object meaningful to the browser's DOM
***********************************************************************/

function getObj(obj){

	var coll = "";
	var styleObj = "";
	if(document.all){
		coll = "all.";
		styleObj = ".style"
	}
	if(document.getElementById){		
		coll = "getElementById('";
		styleObj = "').style";
	}
	var theObj;
	if(typeof obj == "string"){
		theObj = eval("document."+coll+obj+styleObj);
	}else{theObj = obj}
	return theObj;
}

/***********************************************************************
Return an image object meaningful to the browser's DOM
***********************************************************************/

function getImgObj(lyr,img){
	var theObj,coll,doc;
	coll = "";
	doc = "";
	if(document.layers){
		if (lyr == ""){;}
		else{
			coll = "layers['"
			doc = "'].document.";
		}
	}
	if(document.all){
		lyr = "";
		coll = "all.";
	}
	if(document.getElementById){
		lyr = "";
		coll = "getElementById('";
		img += "')";
	}
	theObj = eval("document."+coll+lyr+doc+img);
	return theObj;
}

/*************************************************************************************
Return a psuedo random number. This is provided for developers who wish an easy
function for generating random numbers. Note that psuedo random numbers are not
true random numbers. This function should not be used for contests, lotteries, or games 
offering prizes to the general public.
*************************************************************************************/

function randNum(num){
	return(Math.round((num-1)*Math.random())+1);
}

/*************************************************************************************
Test for DHTML compatibility
*************************************************************************************/
function DHTMLcompliant(){
	var compliant = false;
	if(document.all||document.getElementById) {
		compliant= true;
	}
	return compliant;
}

/*************************************************************************************
																		OBJECTS
*************************************************************************************/

/*************************************************************************************
																	PositionableLayer

This object allows a document layer to be positioned on the fly.

Object constructor: var myObj = new PositionableLayer(xPosition,yPosition,layerID);

Arguments xPosition is the starting x coordinate of the layer, 
yPosition is the starting y coordinate of the layer, layerID is the value of the id attribute of 
the layer.

methods:
myObj.move(x,y); Move a layer by x and y pixels
position(xVal,yVal); Position a layer at the coordinates xVal, yVal
myObj.hide(); Hide a layer
myObj.show(); Show a layer
myObj.setIndex(num); Set the z-order of a layer to num
myObj.offsetXcenter(xO); Offset the x coordinate of the layer by x0 pixels from the horizontal center of the window.
myObj.offsetYcenter(yO); Offset the y coordinate of the layer by y0 pixels from the vertical center of the window.
myObj.centerX();  Returns the pixel value of the horizontal center of the window.
myObj.centerY();  Returns the pixel value of the vertical center of the window.

****************************************************************************************/

function PositionableLayer(xPosition,yPosition,layerID){
	this.x = xPosition;
	this.y = yPosition;
	this.isAnimating=false;
	this.layer = layerID;
	
	this.style = getStyle;	
	
	this.move = shiftLayer;
	
	this.position = moveTo;
	
	this.hide = hideLayer;
	
	this.show = showLayer;
	
	this.setIndex = zOrder;
	
	this.offsetXcenter= offsetHoriz;
	
	this.offsetYcenter= offsetVert;
	
	//The following functions are for internal use by the object to retrieve
	//the x and y coordinate of the centre of the window for use in the
	//two methods above.
	
	this.centerX = getXcenter;
	this.centerY = getYcenter;
}

/*************************************************************************************
Methods for PositionableLayer.
*************************************************************************************/
function getStyle(){
	return getObj(this.layer);
}

function shiftLayer(xVal,yVal){
	var theObj = getObj(this.layer);
	this.x += xVal;
	this.y += yVal;
	if(document.layers){
		theObj.moveTo(this.x,this.y);
	}
	if(document.all){
		theObj.pixelTop = this.y;
		theObj.pixelLeft = this.x;
	}
	if(document.getElementById){
		theObj.top = this.y;
		theObj.left = this.x;
	}
}

function moveTo(xVal,yVal){
	var theObj = getObj(this.layer);
	this.x = xVal;
	this.y = yVal;
	if(document.layers){
		theObj.moveTo(this.x,this.y);
	}
	if(document.all){
		theObj.pixelTop = this.y;
		theObj.pixelLeft = this.x;
	}
	if(document.getElementById){
		theObj.top = this.y;
		theObj.left = this.x;
	}
}

function hideLayer(){
	this.style().visibility = "hidden";
}

function showLayer(){
	this.style().visibility = "visible";
}

function zOrder(num){
	this.style().zIndex = num;
}

function offsetHoriz(xO){
	if(window.innerWidth){
		this.x = window.innerWidth;
	}
	else{
		this.x = document.body.clientWidth;
	}
	var theObj = getObj(this.layer);
	this.x /= 2;
	this.x += xO;
	if(document.layers){
		theObj.moveTo(this.x,this.y);
	}
	if(document.all){
		theObj.pixelTop = this.y;
		theObj.pixelLeft = this.x;
	}
	if(document.getElementById){
		theObj.left = this.x;
	}
}

function offsetVert(yO){
	if(window.innerHeight){
		this.y = window.innerHeight;
	}
	else{
		this.y = document.body.clientHeight;
	}
	var theObj = getObj(this.layer);
	this.y /= 2;
	this.y += yO;
	if(document.layers){
		theObj.moveTo(this.x,this.y);
	}
	if(document.all){
		theObj.pixelTop = this.y;
	}
	if(document.getElementById){
		theObj.top = this.y;
	}
}

function getXcenter(){
	var x;
	if(window.innerWidth){
		x = window.innerWidth / 2;
	}
	else{
		x = document.body.clientWidth / 2;
	}
	return x;
}

function getYcenter(){
	var y;
	if(window.innerHeight){
		y = window.innerHeight / 2;
	}
	else{
		y = document.body.clientHeight / 2;
	}
	return y;
}

/*********************************************************************************
End of positionableLayer
**********************************************************************************/

/********************************************************************
																AnimatedImage

Object constructor:

var x = new AnimatedImage(ID,name,Layer);

Arguments: ID is the id of the image, name is the name of the image.Note that
each image referenced by an AnimatedImage object must have both a name attribute
and an id. This is for compatibility between browsers. Layer is the id of the layer containing the
image. If the image is not in a layer, set Layer="".

Methods:

x.load(path,img1,img2, ...); 

The load method is overloaded. The first argument is the path to the directory
holding the image source files. Next are an optional number of strings representing 
the source files. 

x.replace(num); 

The num argument represents the number in the internal image array of the animatedImage 
object. When images are loaded using the load method, the first image loaded (img1 above) takes up the
first position in the array. The next takes position two and so on.

Example:
	
var Imap2 = new AnimatedImage("mp2ID","mp2","");
Imap2.load("pics/","map2a.gif","map2b.gif");
	
This would set up a rollover that could be called in the document as using the replace() method as follows:
	
<a href="url.htm" onmouseover="Imap2.replace(2)" onmouseout="Imap2.replace(1)">
<Img id="mp2ID" name="mp2" src="pics/map2a.gif">
</a>	

**********************************************************************/


function AnimatedImage(ID,name,Layer){
	this.imgAry;
	this.load = loadImages;
	if(document.getElementById){
		this.image = ID;
	}else{
		this.image = name;
	}
	this.layer = Layer;
	this.getImage = getImageObj;
	this.replace = replaceImage;
}

/***********************************************************************
Methods for AnimatedImage.
***********************************************************************/

function replaceImage(num){
	this.getImage().src = this.imgAry[num].src;
}

function getImageObj(){
	return getImgObj(this.layer,this.image);
}

function loadImages(){
	var path = loadImages.arguments[0];
	var num = loadImages.arguments.length-1;
	this.imgAry = new Array(num);
	for (var i =1; i<=loadImages.arguments.length; i++){
		var p = path;
		p += loadImages.arguments[i];
		this.imgAry[i] = new Image();
		this.imgAry[i].src = p;
	}
}

/***********************************************************************
End of AnimatedImage.
***********************************************************************/

/***********************************************************************
															Movie

The following shows a typical usage of the movie object. Note that the 
animatedImage and PositionableLayer objects are initialized and then loaded
into the movie. Any object can be loaded into a movie and then accessed
from the movie objects array.

First some animatedImage objects and PositionableLayer objects are initialized.

var img1 = new animatedImage("menu1ID","menu1","");
var img2 = new animatedImage("menu2ID","menu2","");
var img3 = new animatedImage("menu3ID","menu3","");
var img4 = new animatedImage("menu4ID","menu4","");
img1.load("pix/","menu01a.gif","menu01.gif");
img2.load("pix/","menu02a.gif","menu02.gif");
img3.load("pix/","menu03a.gif","menu03.gif");
img4.load("pix/","menu04a.gif","menu04.gif");
var l1 = new PositionableLayer(50,0,"lyr1");
var l2 = new PositionableLayer(130,30,"lyr2");
var l3 = new PositionableLayer(180,50,"lyr3");
var l4 = new PositionableLayer(230,60,"lyr4");
var l5 = new PositionableLayer(370,-100,"lyr5");

Now we initialize a Movie object using the objects above as arguments.

The first argument is the length of the movie, which is the number
of positions in the score array. Next come an optional number of 
arguments that are objects you can load into the movie objects
array for easy reference. In this case the optional arguments are the
PositionableLayer and  animatedImage objects above.

var mov1 = new Movie(100,l1,l2,l3,l4,l5,img1,img2,img3,img4);

After loading objects a score can be put together. The score is an array of 
stings. Some or all of the indices can contain javascript statements. Those
that don't are empty strings. When the movie plays, each position in 
the score is executed, and if it is executeble javascript, the script 
is executed. Any objects loaded into the movie can be referenced from the 
Movie.objects[ ] array, as is shown below. Note that whie the score here 
has 100 frames, the page author need not place script in each position in the array.
In the example only selected frames up to the 32 are used.

*Important: remeber that the javascript placed in the array mus be expressed as strings (inside " ")

mov1.score[5]="mov1.objects[6].replace(1)";
mov1.score[6]="mov1.objects[6].replace(2)";
mov1.score[7]="mov1.objects[7].replace(1)";
mov1.score[8]="mov1.objects[7].replace(2)";
mov1.score[9]="mov1.objects[8].replace(1)";
mov1.score[15]="mov1.objects[5].move(0,0)";
mov1.score[16]="mov1.objects[5].show()";
mov1.score[17]="mov1.objects[5].move(0,30)";
mov1.score[19]="mov1.objects[5].move(0,30)";
mov1.score[21]="mov1.objects[5].move(0,30)";
mov1.score[31]="mov1.objects[1].show()";
mov1.score[32]="mov1.objects[1].hide();mov1.objects[2].show()";

tID stores the id of the interval that plays the movie. The movie 
uses this id to clear the interval when the movie is finished. A 
function start() is defined to be called when the movie is to 
begin playing.

var tID;
function start(){
	tID = setInterval("mov1.play('tID')",300);
}

The start function can be called from the onload handler of the 
document: <body onload="start()">

***********************************************************************/

function Movie(){
	this.counter=1;
	this.score = new Array(Movie.arguments[0]);
	for (var i = 1; i <= Movie.arguments[0]; i++){this.score[i] = ";";}
	this.objects = new Array(Movie.arguments.length-1);
	for (var i=1; i<=Movie.arguments.length; i++){
		this.objects[i] = Movie.arguments[i];
	}
	this.play = playFrame;
}

function playFrame(ID){
	eval(this.score[this.counter]);
	this.counter++;
	if (this.counter > this.score.length){
		clearInterval(ID);	 
	}
}

/***********************************************************************
End of Movie
***********************************************************************/

/***********************************************************************
 															dragNdropper
 
This function takes a minimum of three arguments, and more in multiples 
of three. In each triplet the first value is the id of the layer, and 
the next two are the width and height respectively. Eg:

var dropper = new dragNdropper("lyr1",79,150);
dragDropSetUp();

Note that the above is all you need to set a layer up for dragging and dropping. 
The following sets up three layers

var dropper = new dragNdropper("lyr1",79,150,"lyr2",59,106,"lyr3",48,74);
dragDropSetUp();

Note that the object must be named dropper, so there can only be one 
dragNdropper object. However this one object can control many layers.
***********************************************************************/

function dragNdropper(){
	this.x=0;
	this.y=0;
	this.selected = false;
	this.objects = new Array(dragNdropper.arguments.length%3);
	for (var i=0;i< dragNdropper.arguments.length;i=3){
		this.objects[i+1] = dragNdropper.arguments[i];
		this.objects[i+1].width = dragNdropper.arguments[i+1];
		this.objects[i+1].height = dragNdropper.arguments[i+2];
		
	}	
	this.grab = grabIt;
	this.drag = dragIt;
	this.drop =dropIt;
}
/***********************************************************************
Methods for dragNdropper.
***********************************************************************/

function grabIt(e){
	if(document.captureEvents){
		this.x = e.pageX;
		this.y = e.pageY;
	}else{
		this.x = window.event.clientX;
		this.y = window.event.clientY; 
	}
	for (var i =1; i< this.objects.length; i++){
		if(((this.x>this.objects[i].x)&(this.x<this.objects[i].x+this.objects[i].width))&((this.y>this.objects[i].y)&(this.y<this.objects[i].y+this.objects[i].height))){
				this.selected = this.objects[i];
		}
	}
}
function dragIt(e){
	if (this.selected){			
	var x1,y1;
		if(document.captureEvents){
			x1 = e.pageX - this.x;
			y1 = e.pageY - this.y;
			this.selected.move(x1,y1);
			
		}else{ 
			x1 = window.event.clientX - this.x;
			y1 = window.event.clientY - this.y;
			this.selected.move(x1,y1);
			return false;
		} 
	}
}
function dropIt(e){
	this.selected = false;
}

function dragDropSetUp(){
	if(document.captureEvents){
		document.captureEvents(Event.MOUSEDOWN|Event.MOUSEUP|Event.MOUSEMOVE);
	}
	document.onmousedown = pickup;
	document.onmousemove = moveIt;
	document.onmouseup = dumpIt;
	
}
function pickup(e){
	dropper.grab(e);
}
function moveIt(e){
	dropper.drag(e);
}
function dumpIt(e){
	dropper.drop(e);
}	
