/*
 * Div object: an API library for cross-browser dynamic HTML
 * Copyright (C) 2001 Scott Martin (scott@coffeeblack.org)
 * $Revision: 1.16 $
 *
 * 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.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, it is available from the Free Software
 * Foundation, Inc. at http://www.gnu.org/copyleft/lesser.html or in writing at
 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/*
 * Constructs a Div object using the div's ID in the html document. The div whose
 * ID is supplied as the argument must be positioned-- it must have a style class
 * or ID defined for it which contains the attribute 'position,' either 'absolute'
 * or 'relative.' For Netscape 4.x browsers, whose document.layers object model
 * forms a hierarchical tree, the constructor traverses the object tree in order
 * to find the appropriate layer object.
 */
function Div(divId)
{	this.id = divId;
	this.alias = (document.getElementById) ? document.getElementById(this.id).id 
		: (document.layers) ? findLayerAlias(this.id) : new String("document.all." + this.id);
	this.styleAlias = (document.getElementById || document.all) ? this.alias + ".style" : this.alias;
	this.documentObject = (document.getElementById) ? document.getElementById(this.id) : eval(this.alias);
	this.styleObject = (document.getElementById) ? this.documentObject.style : eval(this.styleAlias);
		
	// for Netscape 4.x: finds a nested layer object		
	function findLayerAlias(name, docAlias) 
	{	var i, layer, docLayers, layerAlias;
		if(!docAlias) docAlias = "document";
		docLayers = eval(docAlias + ".layers");
		for(i = 0; i < docLayers.length; i++) 
		{	layerAlias = docAlias + ".layers." + docLayers[i].name;
			layer = eval(layerAlias);
			if(layer.name == name) return layerAlias;
			if(layer.document.layers.length > 0) 
			{	layerAlias = findLayerAlias(name, layerAlias + ".document");
				if(layerAlias != null) return layerAlias;
			}
		}
		return null;
	}
	
	return this;
}

/*
 * Gets this div's ID, as a string. This will be the same as the argument
 * passed into the constructor.
 */
Div.prototype.getId = function()
{	return this.id;
}

/*
 * Gets this Div's alias, as a string. A Div's alias is the name of the
 * object representing this html div element in the document. For example, 
 * a div named "myDiv" will have an alias "document.layers.myDiv" for NS 4
 * but an alias "document.all.myDiv" for IE 4.
 */
Div.prototype.getAlias = function()
{	return this.alias;
}

/*
 * Gets this Div's style alias, as a string. A Div's style alias is the name of
 * this html div element's style in the document. For example,
 * a div named "myDiv" will have an alias "document.layers.myDiv" for NS 4
 * but an alias "document.all.myDiv.style" for IE 4.
 */
Div.prototype.getStyleAlias = function()
{	return this.styleAlias;
}

/*
 * Gets the object in the html document which is referenced this Div's alias.
 */
Div.prototype.getDocumentObject = function()
{	return this.documentObject;
}

/*
 * Gets the object in the html document which is referenced this Div's style alias.
 */
Div.prototype.getStyleObject = function()
{	return this.styleObject;
}

/*
 * If this div is nested inside another div, gets this div's parent as an object
 * of type Div. If this div is a top-level element (that is, its parent is the 
 * element body), returns null.
 */
Div.prototype.getParentDiv = function()
{	pid = (document.getElementById) ? this.documentObject.parentNode.id 
		: (document.all) ? eval(this.alias + ".parentElement.id")
			: (document.layers) ? eval(this.alias + ".parentLayer.id")
				: null;
				
	return (pid && pid != null) ? new Div(pid) : null;
}

/*
 * Tests whether this div is visible.
 */
Div.prototype.isVisible = function()
{	if(this.styleObject.visibility)
	{	// default value for NS 4 is 'inherit', for DOM browsers, 
		// the default value is an empty string-- check if the parent is visible
		if(this.styleObject.visibility == "inherit" || this.styleObject.visibility.length == 0)
		{	pd = this.getParentDiv();
			return (pd != null) ? pd.isVisible() : true;
		}
		else return (this.styleObject.visibility == "visible" || this.styleObject.visibility == "show");
	}
	
	return true;
}

/*
 * Sets this div's visiblility to the value of the boolean argument.
 */
Div.prototype.setVisible = function(bool)
{	this.styleObject.visibility = (document.layers) ? ((bool) ? "show" : "hide") : ((bool) ? "visible" : "hidden");
}

/*
 * Gets the current pixel position of this div's left edge. If the div is nested within
 * another div, the number returned represents the distance relative to the position of
 * the div inside which it resides.
 */
Div.prototype.getLeft = function()
{	return (document.getElementById) ? (document.all || !this.documentObject.parentNode)
		? this.documentObject.offsetLeft : (this.documentObject.offsetLeft - this.documentObject.parentNode.offsetLeft) 
			: (document.all) ? this.styleObject.pixelLeft : this.styleObject.left;
}

/*
 * Sets the pixel position of this div's left edge.
 * The argument supplied must be a number.
 */
Div.prototype.setLeft = function(x)
{	if(!isNaN(x)) 
	{	if(document.getElementById)
			this.styleObject.left = new String(x + "px");
		else this.styleObject.left = x;
	}
}

/*
 * Gets the current pixel position of this div's top edge. If the div is nested within
 * another div, the number returned represents the distance relative to the position of
 * the div inside which it resides.
 */
Div.prototype.getTop = function()
{	return (document.getElementById) ? (document.all || !this.documentObject.parentNode)
		? this.documentObject.offsetTop : (this.documentObject.offsetTop - this.documentObject.parentNode.offsetTop) 
			: (document.all) ? this.styleObject.pixelTop : this.styleObject.top;
}

/*
 * Sets the pixel position of this div's top edge.
 * The argument supplied must be a number.
 */
Div.prototype.setTop = function(y)
{	if(!isNaN(y)) 
	{	if(document.getElementById)
			this.styleObject.top = new String(y + "px");
		else this.styleObject.top = y;
	}
}

/*
 * Gets the current pixel position of this div's right edge. If the div is nested within
 * another div, the number returned represents the distance relative to the position of
 * the div inside which it resides.
 */
Div.prototype.getRight = function()
{	return (this.getLeft() + this.getWidth());
}

/*
 * Sets the pixel position of this div's right edge.
 * The argument supplied must be a number.
 */
Div.prototype.setRight = function(x)
{	if(!isNaN(x)) this.setLeft(x - this.getWidth());
}

/*
 * Gets the current pixel position of this div's bottom edge. If the div is nested within
 * another div, the number returned represents the distance relative to the position of
 * the div inside which it resides.
 */
Div.prototype.getBottom = function()
{	return (this.getTop() + this.getHeight());
}

/*
 * Sets the pixel position of this div's bottom edge.
 * The argument supplied must be a number.
 */
Div.prototype.setBottom = function(y)
{	if(!isNaN(y)) this.setTop(y - this.getHeight());
}

/*
 * Gets the height of this div, in pixels.
 */
Div.prototype.getHeight = function()
{	return (document.getElementById) ? this.documentObject.offsetHeight
		: (document.all) ? (this.styleObject.pixelHeight) ? this.styleObject.pixelHeight
			: eval(this.alias + ".clientHeight") : this.styleObject.clip 
				? (this.styleObject.clip.bottom - this.styleObject.clip.top)
					: (this.documentObject.height) ? this.documentObject.height : -1;
}

/*
 * Sets the pixel height of this div. 
 * The argument supplied must be a number.
 */
Div.prototype.setHeight = function(newHeight)
{	if(!isNaN(newHeight))
	{	if(document.getElementById) this.styleObject.height = new String(newHeight + "px");
		else if(document.all) this.styleObject.pixelHeight = newHeight;
		else this.styleObject.clip.bottom += (newHeight - this.getHeight());
	}
}

/*
 * Gets the width of this div, in pixels.
 */
Div.prototype.getWidth = function()
{	return (document.getElementById) ? this.documentObject.offsetWidth
		: (document.all) ? (this.styleObject.pixelWidth) ? this.styleObject.pixelWidth
			: eval(this.alias + ".clientWidth") : this.styleObject.clip 
				? (this.styleObject.clip.right - this.styleObject.clip.left)
					: (this.documentObject.width) ? this.documentObject.width : -1;
}

/*
 * Sets the pixel width of this div. 
 * The argument supplied must be a number.
 */
Div.prototype.setWidth = function(newWidth)
{	if(!isNaN(newWidth))
	{	if(document.getElementById) this.styleObject.width = new String(newWidth + "px");
		else if(document.all) this.styleObject.pixelWidth = newWidth;
		else this.styleObject.clip.right = newWidth;
	}
}

/*
 * Gets this div's clipping region, as an object of type Clip.
 */
Div.prototype.getClip = function()
{	// clip could be defined but null in Mac IE 4.5
	if(this.styleObject.clip && this.styleObject.clip != null)
	{	if((document.getElementById || document.all) && (this.styleObject.clip.length > 0))
			return Clip.parseClip(this.styleObject.clip);
		else return new Clip(this.styleObject.clip.top, this.styleObject.clip.right, this.styleObject.clip.bottom, this.styleObject.clip.left);
	}
	else return new Clip(0, this.getWidth(), this.getHeight(), 0);
}

/*
 * Gets the value of the top edge of this div's clipping region, in number of pixels.
 */
Div.prototype.getClipTop = function()
{	cl = this.getClip();
	return cl.top;
}

/*
 * Gets the value of the right edge of this div's clipping region, in number of pixels.
 */
Div.prototype.getClipRight = function()
{	cl = this.getClip();
	return cl.right;
}

/*
 * Gets the value of the bottom edge of this div's clipping region, in number of pixels.
 */
Div.prototype.getClipBottom = function()
{	cl = this.getClip();
	return cl.bottom;
}

/*
 * Gets the value of the left edge of this div's clipping region, in number of pixels.
 */
Div.prototype.getClipLeft = function()
{	cl = this.getClip();
	return cl.left;
}

/*
 * Gets the value of the height of this div's clipping region, in number of pixels.
 */
Div.prototype.getClipHeight = function()
{	cl = this.getClip();
	return (cl.bottom - cl.top);
}

/*
 * Gets the value of the width of this div's clipping region, in number of pixels.
 */
Div.prototype.getClipWidth = function()
{	cl = this.getClip();
	return (cl.right - cl.left);
}

/*
 * Sets this div's clipping region. The argument supplied can either be an
 * object of type Clip or a string from which a Clip object will be parsed.
 * If a string is used, it must be in one of the following two formats:
 * 		rect(0 200 200 0)
 *		rect(20px 0px 9px 55px)
 */
Div.prototype.setClip = function(newClip)
{	clipObj = (typeof newClip == "string") ? Clip.parseClip(newClip) : newClip;
	
	if(document.getElementById || document.all)
		this.styleObject.clip = clipObj.toString();
	else
	{	this.documentObject.clip.top = clipObj.top;
		this.documentObject.clip.right = clipObj.right;
		this.documentObject.clip.bottom = clipObj.bottom;
		this.documentObject.clip.left = clipObj.left;
	}
}

/*
 * Moves this div to the supplied x and y pixel coordinates.
 */
Div.prototype.moveTo = function(x, y)
{	this.setLeft(x);
	this.setTop(y);
}

/*
 * Moves this div by the supplied change in x and y pixel coordinates.
 */
Div.prototype.moveBy = function(dx, dy)
{	this.moveTo((this.getLeft() + dx), (this.getTop() + dy));
}

/*
 * Gets the z-index of this div, if defined. If no z-index is defined,
 * returns -1.
 */
Div.prototype.getZIndex = function()
{	zi = parseInt(this.styleObject.zIndex);
	return (!isNaN(zi)) ? zi : -1;
}

/*
 * Sets this div's z-index.
 * The argument supplied must be a number.
 */
Div.prototype.setZIndex = function(z)
{	if(!isNaN(z)) this.styleObject.zIndex = z;
}

/*
 * Tests whether this div is above (has a higher z-index than) another div.
 * The argument supplied must be an object of type Div. 
 */
Div.prototype.isAbove = function(anotherDiv)
{	return (this.getZIndex() > anotherDiv.getZIndex());
}

/*
 * Tests whether this div is below (has a lower z-index than) another div.
 * The argument supplied must be an object of type Div. 
 */
Div.prototype.isBelow = function(anotherDiv)
{	return (this.getZIndex() < anotherDiv.getZIndex());
}

/*
 * Moves this div above another div. Gets the z-index of the other div, then
 * sets this div's z-index to that value plus one.
 * The argument supplied must be an object of type Div.
 */
Div.prototype.moveAbove = function(anotherDiv)
{	this.setZIndex(anotherDiv.getZIndex() + 1);
}

/*
 * Moves this div below another div. Gets the z-index of the other div, then
 * sets this div's z-index to that value minus one.
 * The argument supplied must be an object of type Div.
 */
Div.prototype.moveBelow = function(anotherDiv)
{	this.setZIndex(anotherDiv.getZIndex() - 1);
}

/*
 * Gets the textual content of this div. For Netscape 4, returns an empty string
 * until something is written to the div using setContent().
 */
Div.prototype.getContent = function()
{	return (document.all) ? eval(this.alias + ".innerHTML") 
		: (this.documentObject.innerHTML) ? this.documentObject.innerHTML : new String("");
}

/*
 * Sets the textual content of this div. For Netscape 4, all formatting information is
 * lost. A workaround is to use <span style= tags around the content.
 */
Div.prototype.setContent = function(str)
{	if(document.all)
		eval(this.alias + ".innerHTML = str;");
	else
	{	// even if NS 4, store the text as innerHTML for later retrieval by getContent()	
		this.documentObject.innerHTML = str;
			
		if(document.layers)
		{	with(this.documentObject.document)
			{	open();
				write(this.getContent());
				close();
			}
		}
	}
}

/*
 * Gets an object of type Form (identified by frmName) that resides inside this div in the document hierarchy.
 */
Div.prototype.getForm = function(frmName)
{	frmArray = (document.getElementById || document.all) ? document.forms : this.documentObject.document.forms;
	return (frmArray != null && frmArray.length > 0) ? frmArray[frmName] : null;
}

/*
 * Gets an object of type Image (identified by imgName) that resides inside this div in the document hierarchy.
 */
Div.prototype.getImage = function(imgName)
{	imgArray = (document.getElementById || document.all) ? document.images : this.documentObject.document.images;
	return (imgArray != null && imgArray.length > 0) ? imgArray[imgName] : null;
}

/*
 * Gets the color of this div, if any is defined. This value represents the foreground text color of any
 * textual content inside this div.
 */
Div.prototype.getColor = function()
{	return (document.getElementById || document.all) ? this.styleObject.color : this.documentObject.document.fgColor;
}

/*
 * Sets the color of this div. This value represents the foreground text color of any
 * textual content inside this div.
 */
Div.prototype.setColor = function(newColor)
{	if(document.getElementById || document.all) this.styleObject.color = newColor;
	else this.documentObject.document.fgColor = newColor;
}

/*
 * Gets the background color of this div, if any is defined.
 */
Div.prototype.getBackgroundColor = function()
{	return (document.getElementById || document.all) ? this.styleObject.backgroundColor 
		: (this.documentObject.document.bgColor) ? this.documentObject.document.bgColor : new String("");
}

/*
 * Sets the background color of this div.
 */
Div.prototype.setBackgroundColor = function(color)
{	if(typeof color == "string")
	{	if(document.getElementById || document.all) this.styleObject.backgroundColor = color;
		else this.documentObject.document.bgColor = color;
	}
}

/*
 * Gets this div's background image, if any is defined.
 */
Div.prototype.getBackgroundImage = function()
{	return (document.getElementById || document.all) ? this.styleObject.backgroundImage
		: this.documentObject.document.background ? this.documentObject.document.background.src : new String("");
}

/*
 * Sets this div's background image. The argument supplied can be either an object of type Image
 * or a string representing an image's source.
 */
Div.prototype.setBackgroundImage = function(newImage)
{	strImgName = (newImage.constructor == Image) ? newImage.src : newImage;
	
	if(typeof strImgName == "string")
	{	if(document.getElementById || document.all) this.styleObject.backgroundImage = "url(" + strImgName + ")";
		else 
		{	if(!this.documentObject.document.background || this.documentObject.document.background == null)
				this.documentObject.document.background = new Image();
			
			this.documentObject.document.background.src = strImgName;
		}
	}
}

/*
 * Gets a string representation of this Div.
 */
Div.prototype.toString = function()
{	return "[object Div {id:\"" + this.getId() + "\" alias:\"" + this.getAlias() + "\" styleAlias:\"" + this.getStyleAlias() + 
		"\" documentObject:{" + this.documentObject.toString() + "} styleObject:{" + this.styleObject.toString() + "}}]";
}

/*
 * Constructs a Clip object from 4 pixel distance values representing 
 * its top, right, bottom, and left edges.
 */
function Clip(top, right, bottom, left)
{	// calling parseInt() strips off the trailing "px", if any
	this.top = parseInt(top);
	this.right = parseInt(right);
	this.bottom = parseInt(bottom);
	this.left = parseInt(left);
	return this;
}

/*
 * Parses a clip object from a CSS clip string, like "rect(40 100 150 40)".
 */
Clip.parseClip = function(strClip)
{	if(strClip.indexOf("(") != -1 && strClip.indexOf(")") != -1)
	{	// store variable for compatability with Mac NS 4.06 object bug
		spl = new String(strClip.substring(strClip.indexOf("(") + 1, strClip.indexOf(")")));
		clipVals = spl.split(" ");
		if(clipVals.length == 4) return new Clip(clipVals[0], clipVals[1], clipVals[2], clipVals[3]);
	}
	
	return null;
}

/*
 * Gets a string representation of this Clip. This string is in the form of a 
 * CSS clip string. For all DOM2 and Netscape 4.x browsers, the pixel values will have
 * trailing "px" strings, for IE4 they will just have numeric values.
 */
Clip.prototype.toString = function()
{	strPx = (document.getElementById || document.layers) ? "px" : "";
	return "rect(" + this.top + strPx + " " + this.right + strPx + " " + this.bottom + strPx + " " + this.left + strPx + ")";
}