/**
   This API has been written by Kovid Goyal. (aumerle@hotmail.com) and is distributed under
   The GNU General Public License.
   Visit my state-of-the-art website at www.kovidgoyal.net.

    The Visual Document API, an Application Programming Interface that exposes the
	properties of various browsers through a common interface and has added capabilities
	like the use of objects as event handlers.
    Copyright (C) 2000  Kovid Goyal.

    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.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    You can contact me at public@kovidgoyal.net
*/

var API_VERSION = "1.0.0";
var API_INFO = "The Visual Document API " + API_VERSION +
". Created by Kovid Goyal. <a href='http://kovidgoyal.cjb.net'>(kovidgoyal.cjb.net)</a>";

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////Utility Fuctions
//////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function arraycopy(src, dest, startIndex, endIndex, destIndex) {
		 if(src == null || dest == null || src.length < 1 || startIndex < 0 ||
		 endIndex > src.length - 1 || endIndex < startIndex ||
		 destIndex < 0) return;

		 if(destIndex == null) destIndex = 0;

		 for(var s = startIndex; s < endIndex + 1; s++) {
				 dest[destIndex] = src[s]; destIndex++;
		 }
		 return dest;
}

function arraysort(array) {
		 if(typeof array != "object") return;
		 for(var c = 0; c < array.length; c++) {
		 		 for(var i = c; i > 0; i--) {
				 		 var val = array[i].compareTo(array[i-1]);
						 if(isNaN(val)) return;
						 if(val > -1) {
						 		var temp = array[i-1];
								array[i-1] = array[i];
								array[i] = temp;
						}
				}
		 }
}



////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////Static Object Extensions
//////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Object.prototype.equals = function(obj) { return (obj == this); }
Object.prototype.compareTo = function(obj) { return Number.NaN; }

String.prototype.expungeChar = function(target) {
		if(typeof target != "string" || target.length != 1) return;
		var array = this.split(target); var string = "";
		for(var c = 0; c < array.length; c++) string += array[c];
		return string;
}

//Compares two strings alphabetically (Case Insensitive, Ignores Spaces)
//Returns 0 iff target == this
//Returns -1 iff target < this (target appears b4 this in a dictionary)
//Returns 1 iff target > this
//Returns NaN iff target is not a valid string
String.prototype.compareTo = function(target) {
		 if(target == null || typeof target != "string") return Number.NaN;
		 var tar = target.toLowerCase(); tar = tar.expungeChar(" ");
		 var src = this.toLowerCase(); src = src.expungeChar(" ");
		 if(src < tar) return 1;
		 if(src > tar) return-1;
		 return 0;
}

Array.prototype.insertElementAt = function(obj, index) {
					if(obj == null || index > this.length || index < 0) return;
					if(index == this.length) {this[index] = obj; return;}
					var temp = new Array();
					arraycopy(this, temp, index, this.length-1, 0);
					this[index] = obj;
					this.length = index+1;
					var c = index+1;
					for(var t = 0; t < temp.length; t++) {
							this[c] = temp[t];
							c++;
					}
}

Array.prototype.add = function(obj) {
					if(this.length == 0) this.insertElementAt(obj, 0);
					else this.insertElementAt(obj, this.length);
}

Array.prototype.removeElementAt = function(index) {
					if(index < 0 || index > this.length) return;
					var temp = new Array();
					arraycopy(this, temp, index+1, this.length-1, 0);
					var c = index;
					this.length = index;
					for(var t = 0; t < temp.length; t++) {
							this[c] = temp[t];
							c++;
					}
}

Array.prototype.indexOf = function(obj) {
					if(obj == null) return -1;
					for(var c = 0; c < this.length; c++) if(obj.equals(this[c])) return c;
					return -1;
}

Array.prototype.contains = function(obj) {
					if(this.indexOf(obj) == -1) return false;
					else return true;
}

Array.prototype.remove = function(obj) {
					var index = this.indexOf(obj);
					if(index == -1) return;
					else this.removeElementAt(index);
}

function Dimension(width, height) {
		 this.width = width; this.height = height;
		 this.toString = function() { return "Width: " + this.width + "\nHeight: " + this.height; }
		 this.equals = function(obj) { return (obj != null && this.width == obj.width && this.height == obj.height); }
}

function Point(left, top) {
		 this.left = left; this.top = top;
		 this.toString = function() { return "Left: " + this.left + "\nTop: " + this.top; }
		 this.equals = function(obj) { return (obj != null && this.left == obj.left && this.top == obj.top); }

}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////Debugging Extensions
///////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

var CurrentAPIError = null;

function fireError(msgSource) {
		 CurrentAPIError = new Object();
		 CurrentAPIError.src = msgSource;
		 CurrentAPIError.srcFunction = msgSource.substring(0, msgSource.indexOf("|"));
		 CurrentAPIError.msg = msgSource.substring(msgSource.indexOf("|") +1, msgSource.length);
		 //alert(msgSource);
		 throw "API Error in function " + CurrentAPIError.srcFunction;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////Client Configuration
///////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
screen.bufferDepth = -1;

function getPlatformInformation() {

    var agt=navigator.userAgent.toLowerCase();

    // *** BROWSER VERSION ***
    // Note: On IE5, these return 4, so use is.ie5up to detect IE5.
    this.major = parseInt(navigator.appVersion);
    this.minor = parseFloat(navigator.appVersion);

    this.nav  = ((agt.indexOf('mozilla')!=-1) && (agt.indexOf('spoofer')==-1)
                && (agt.indexOf('compatible') == -1) && (agt.indexOf('opera')==-1)
                && (agt.indexOf('webtv')==-1));
    this.nav2 = (this.nav && (this.major == 2));
    this.nav3 = (this.nav && (this.major == 3));
    this.nav4 = (this.nav && (this.major == 4));
    this.nav4up = (this.nav && (this.major >= 4));
    this.navonly      = (this.nav && ((agt.indexOf(";nav") != -1) ||
                          (agt.indexOf("; nav") != -1)) );
    this.nav5 = (this.nav && (this.major == 5));
    this.nav5up = (this.nav && (this.major >= 5));

    this.ie   = (agt.indexOf("msie") != -1);
    this.ie3  = (this.ie && (this.major < 4));
    this.ie4  = (this.ie && (this.major == 4) && (agt.indexOf("msie 5.0")==-1) && (agt.indexOf("msie 5.5")==-1) && (agt.indexOf("msie 6.0b")==-1) && (agt.indexOf("msie 6.0")==-1));
    this.ie55 = (this.ie && (this.major==4) && (agt.indexOf("msie 5.5")!=-1));
    this.ie6b = (this.ie && (this.major == 4) && (agt.indexOf("msie 6.0b") != -1));
    this.ie4up  = (this.ie  && (this.major >= 4));
    this.ie5  = (this.ie && (this.major == 4) && (agt.indexOf("msie 5.0")!=-1) );
    this.ie6 = (this.ie && (agt.indexOf("msie 6.0")!=-1) );
    this.ie5up  = (this.ie  && !this.ie3 && !this.ie4);

    // KNOWN BUG: On AOL4, returns false if IE3 is embedded browser
    // or if this is the first browser window opened.  Thus the
    // properties is.aol, is.aol3, and is.aol4 aren't 100% reliable.
    this.aol   = (agt.indexOf("aol") != -1);
    this.aol3  = (this.aol && this.ie3);
    this.aol4  = (this.aol && this.ie4);

    this.opera = (agt.indexOf("opera") != -1);
    this.webtv = (agt.indexOf("webtv") != -1);

    // *** JAVASCRIPT VERSION CHECK ***
    if (this.nav2 || this.ie3) this.js = 1.0
    else if (this.nav3 || this.opera) this.js = 1.1
    else if ((this.nav4 && (this.minor <= 4.05)) || this.ie4) this.js = 1.2
    else if ((this.nav4 && (this.minor > 4.05)) || this.ie5) this.js = 1.3
    else if (this.nav5) this.js = 1.4
    // NOTE: In the future, update this code when newer versions of JS
    // are released. For now, we try to provide some upward compatibility
    // so that future versions of Nav and IE will show they are at
    // *least* JS 1.x capable. Always check for JS version compatibility
    // with > or >=.
    else if (this.nav && (this.major > 5)) this.js = 1.5
    else if (this.ie && (this.major > 5)) this.js = 1.3
    // HACK: no idea for other browsers; always check for JS version with > or >=
    else this.js = 0.0;

    // *** PLATFORM ***
    this.win   = ( (agt.indexOf("win")!=-1) || (agt.indexOf("16bit")!=-1) );
    // NOTE: On Opera 3.0, the userAgent string includes "Windows 95/NT4" on all
    //        Win32, so you can't distinguish between Win95 and WinNT.
    this.win95 = ((agt.indexOf("win95")!=-1) || (agt.indexOf("windows 95")!=-1));

    // is this a 16 bit compiled version?
    this.win16 = ((agt.indexOf("win16")!=-1) ||
                  (agt.indexOf("16bit")!=-1) || (agt.indexOf("windows 3.1")!=-1) ||
                  (agt.indexOf("windows 16-bit")!=-1) );

    this.win31 = ((agt.indexOf("windows 3.1")!=-1) || (agt.indexOf("win16")!=-1) ||
                  (agt.indexOf("windows 16-bit")!=-1));

    // NOTE: Reliable detection of Win98 may not be possible. It appears that:
    //       - On Nav 4.x and before you'll get plain "Windows" in userAgent.
    //       - On Mercury client, the 32-bit version will return "Win98", but
    //         the 16-bit version running on Win98 will still return "Win95".
    this.win98 = ((agt.indexOf("win98")!=-1) || (agt.indexOf("windows 98")!=-1));
    this.winnt = ((agt.indexOf("winnt")!=-1) || (agt.indexOf("windows nt")!=-1));
    this.win32 = ( this.win95 || this.winnt || this.win98 ||
                   ((this.major >= 4) && (navigator.platform == "Win32")) ||
                   (agt.indexOf("win32")!=-1) || (agt.indexOf("32bit")!=-1) );

    this.os2   = ((agt.indexOf("os/2")!=-1) ||
                  (navigator.appVersion.indexOf("OS/2")!=-1) ||
                  (agt.indexOf("ibm-webexplorer")!=-1));

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

    this.sun   = (agt.indexOf("sunos")!=-1);
    this.sun4  = (agt.indexOf("sunos 4")!=-1);
    this.sun5  = (agt.indexOf("sunos 5")!=-1);
    this.suni86= (this.sun && (agt.indexOf("i86")!=-1));
    this.irix  = (agt.indexOf("irix") !=-1);    // SGI
    this.irix5 = (agt.indexOf("irix 5") !=-1);
    this.irix6 = ((agt.indexOf("irix 6") !=-1) || (agt.indexOf("irix6") !=-1));
    this.hpux  = (agt.indexOf("hp-ux")!=-1);
    this.hpux9 = (this.hpux && (agt.indexOf("09.")!=-1));
    this.hpux10= (this.hpux && (agt.indexOf("10.")!=-1));
    this.aix   = (agt.indexOf("aix") !=-1);      // IBM
    this.aix1  = (agt.indexOf("aix 1") !=-1);
    this.aix2  = (agt.indexOf("aix 2") !=-1);
    this.aix3  = (agt.indexOf("aix 3") !=-1);
    this.aix4  = (agt.indexOf("aix 4") !=-1);
    this.linux = (agt.indexOf("inux")!=-1);
    this.sco   = (agt.indexOf("sco")!=-1) || (agt.indexOf("unix_sv")!=-1);
    this.unixware = (agt.indexOf("unix_system_v")!=-1);
    this.mpras    = (agt.indexOf("ncr")!=-1);
    this.reliant  = (agt.indexOf("reliantunix")!=-1);
    this.dec   = ((agt.indexOf("dec")!=-1) || (agt.indexOf("osf1")!=-1) ||
         (agt.indexOf("dec_alpha")!=-1) || (agt.indexOf("alphaserver")!=-1) ||
         (agt.indexOf("ultrix")!=-1) || (agt.indexOf("alphastation")!=-1));
    this.sinix = (agt.indexOf("sinix")!=-1);
    this.freebsd = (agt.indexOf("freebsd")!=-1);
    this.bsd = (agt.indexOf("bsd")!=-1);
    this.unix  = ((agt.indexOf("x11")!=-1) || this.sun || this.irix || this.hpux ||
                 this.sco ||this.unixware || this.mpras || this.reliant ||
                 this.dec || this.sinix || this.aix || this.linux || this.bsd ||
                 this.freebsd);

    this.vms   = ((agt.indexOf("vax")!=-1) || (agt.indexOf("openvms")!=-1));
	this.ns = this.nav;

	this.correctBrowser = (this.nav || this.ie);
	this.correctVersion = (this.nav5up || this.ie5up);
	this.correctOS = (this.linux || this.win32);
	this.supported = (this.correctBrowser && this.correctVersion && this.correctOS);
}

var is = new getPlatformInformation();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////Utility Methods
/////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function getInsideWindowHeight() {
	if(document.body == null) {
					 fireError("getInsideWindowHeight()|The document hasn't finished loading yet. Please wait for the loading to complete before calling this method.");
	} else return (is.ns) ? window.innerHeight : document.body.offsetHeight;
}

function getInsideWindowWidth() {
	if(document.body == null) {
					 fireError("getInsideWindowWidth()|The document hasn't finished loading yet. Please wait for the loading to complete before calling this method.");
	} else return (is.ns) ? window.innerWidth : document.body.offsetWidth ;
}

function getReferenceToNode(handle, index) {
		 if(handle == null) {
		 		   fireError("getReferenceToNode()|You cannot pass a null parameter to this method.");
		 } else if(typeof handle == "object") {
		   		   if(handle.tagName == null) return null;
				   else return handle;
		 } else if(typeof handle == "string") {
		   		   var node = document.getElementById(handle);
				   if(node != null) return node;
				   node = document.getElementsByTagName(handle);
				   if(node == null) return null;
				   index = (typeof index != "number") ? 0 : index;
				   return node.item(index);
		 } return null;
}

function preloadImage(imageURL, loadingMonitor) {
		 if(imageURL == null) fireError("preloadImage()|Parameter: imageURL cannot be null.");
		 if(loadingMonitor != null && typeof loadingMonitor != "function") fireError("preloadImage()|Parameter: loadingMonitor must be a reference to a function.");
		 var img = new Image();
		 img.src = imageURL;
		 if(loadingMonitor != null) img.onload = loadingMonitor;
}

function swapDisplayedImage(handleToImageNode, newImageURL) {
	if(handleToImageNode == null || newImaeURL == null) fireError("swapDisplayedImage()|You cannot pass null parameters to this method");
	handleToImageNode = getReferenceToNode(handleToImageNode);
	if(handleToImageNode == null) fireError("swapDisplayedImage()|Parameter: handleToImageNode invalid. Does not point to an existing IMG Node");
	handleToImageNode.src = newImageURL;
}

//Used in NS6 up for outerHTML
function getAttributes(referenceToNode) {
		 if(is.nav5up) {
		 	  var attrs = referenceToNode.attributes;
			  var attrString = "";
			  for(var c = 0; c < attrs.length; c++)
			  	  attrString += attrs.item(c).name + "=\"" + attrs.item(c).value+"\" ";
			 return attrString;
		} else return null;
}

function getObjectProperties(obj, html) {
        var string = "";
        var lineBreak = (html) ? "<br/>" : "\n";
        try {
          for(prop in obj) string += prop + ": " + obj[prop] + lineBreak;
          return string;
        } catch(ex) {return "Unable to probe fields of " + obj;}
}

function stopDrag(e) { e.preventDefault(); }
function disableDragSelectionOn(obj) {
  obj = getReferenceToNode(obj);
  if(is.ie) {
    obj.ondragstart   = new Function("return false;");
    obj.onselectstart = new Function("return false;");
  } else {
    obj.addEventListener("mousedown", stopDrag, false);
  }
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////The  Visual Element Definition
/////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

VisualElement.prototype.numOfVisualElements = 0;

//Contstructor for a VisualElement Object that represents a HTMLElement (See DOM Spec).
function VisualElement(handleToNode, setAbsolute) {
		 if(handleToNode == null) fireError("VisualElement()|You cannot set handleToNode as null.");
		 this.node = getReferenceToNode(handleToNode);
		 if(this.node == null) fireError("VisualElement()|Parameter handleToNode does not point to an existing node. If you try to use the methods on this VisualElement, you may experience further errors.");
		 this.style = this.node.style;
		 setAbsolute = (setAbsolute == null) ? true : setAbsolute;
		 if(setAbsolute) this.setPositionType("absolute");
		 this.listenerCode = "VisualElement_" + ++VisualElement.prototype.numOfVisualElements;
		 this.animationStartListeners = new EventListenerList();
		 this.animationStopListeners = new EventListenerList();
		 this.animationChangeListeners = new EventListenerList();
}

VisualElement.prototype.getStyleDeclaration = function() {
		if(is.ie) return (this.node.currentStyle) ? this.node.currentStyle : this.style;		  
		return document.defaultView.getComputedStyle(this.node, null);
}

// -----------------Style group of methods---------------------------------
//Obtain style information using this group of methods
VisualElement.prototype.getPositionType = function() {
  return (is.ie) ? this.getStyleDeclaration().position :
    this.getStyleDeclaration().getPropertyValue("position");
}
VisualElement.prototype.isVisible = function() {
  var vis = (is.ie) ? this.getStyleDeclaration().visibility :
    this.getStyleDeclaration().getPropertyValue("visibility");
  if(vis == "hidden") return false;
  if(vis == "visible") return true;
  var parent = this.node;
  while(parent.tagName != "BODY") {
    vis = (is.ie) ? parent.currentStyle.visibility :
      document.defaultView.getComputedStyle(parent, null).getPropertyValue("visibility");
    if(vis == "hidden") return false;
    parent = parent.parentNode;
  }
  vis = (is.ie) ? (document.body.currentStyle.visibility == "inherit") :
    (document.defaultView.getComputedStyle(document.body, null).getPropertyValue("visibility") == "inherit");
  return vis;
}
VisualElement.prototype.getOpacity = function() {
  return parseInt((is.ie) ? this.node.filters.alpha.opacity : this.style.MozOpacity);
}
VisualElement.prototype.getZIndex = function() {
  var z = (is.ie) ? this.getStyleDeclaration().zIndex :
    this.getStyleDeclaration().getPropertyValue("z-index");
  if(isNaN(parseInt(z))) return 0;
  return parseInt(z);
}
VisualElement.prototype.getClip = function() {
  return (is.ie) ? this.style.clip : //this.getStyleDeclaration().clip :
    this.getStyleDeclaration().getPropertyValue("clip");
}
//Returns Number.NaN if clip value is auto or inherit
VisualElement.prototype.getClipTop = function() {
  if(is.ie) {
    return parseInt(this.getStyleDeclaration().clipTop);
  } else {//Assumes length unit is px
    var src = this.getStyleDeclaration().getPropertyValue("clip");
    if(src.length) {
      var top = src.match(/\d+px/);
      if(top) {
	 return parseInt(top[0]);
      } else return Number.NaN;
    } else return Number.NaN;
  }
}

VisualElement.prototype.getClipRight = function() {
  if(is.ie) {
    return parseInt(this.getStyleDeclaration().clipRight);
  } else {
    var src = this.getStyleDeclaration().getPropertyValue("clip");
    if(src.length) {
      var top = src.match(/\d+px/);
      if(top) {
	var right_src = src.substr(src.indexOf(top[0]) + top[0].length + 2);
	var right     = right_src.match(/\d+px/);
	return parseInt(right[0]);
      } else return Number.NaN;
    } else return Number.NaN;
  }
}

VisualElement.prototype.getClipBottom = function() {
  if(is.ie) {
  return parseInt(this.getStyleDeclaration().clipBottom);
  } else {
    var src = this.getStyleDeclaration().getPropertyValue("clip");
    if(src.length) {
      var top = src.match(/\d+px/);
      if(top) {
	var right_src  = src.substr(src.indexOf(top[0]) + top[0].length + 2);
	var right      = right_src.match(/\d+px/);
	var bottom_src = src.substr(src.indexOf(right[0]) + right[0].length + 2);
	var bottom     = bottom_src.match(/\d+px/);
	return parseInt(bottom[0]);
      } else return Number.NaN;
    } else return Number.NaN;
  }
}

VisualElement.prototype.getClipLeft = function() {
  if(is.ie) {
    return parseInt(this.getStyleDeclaration().clipLeft);
  } else {
    var src = this.getStyleDeclaration().getPropertyValue("clip");
    if(src.length) {
      var left = src.match(/\d+px\)/);
      if(left) {
	 return parseInt(left[0]);
      } else return Number.NaN;
    } else return Number.NaN;
  }
}

VisualElement.prototype.getOverflow = function() {
  return (is.ie) ? this.getStyleDeclaration().overflow :
    this.getStyleDeclaration().getPropertyValue("overflow");
}

VisualElement.prototype.getBackgroundColor = function() {
  return (is.ie) ? this.getStyleDeclaration().backgroundColor :
    this.getStyleDeclaration().getPropertyValue("background-color");
}

VisualElement.prototype.getBackgroundImage = function() {
  return (is.ie) ? this.getStyleDeclaration().backgroundImage :
    this.getStyleDeclaration().getPropertyValue("background-image");
}

VisualElement.prototype.getBackgroundAttachment = function() {
  return (is.ie) ? this.getStyleDeclaration().backgroundAttachment :
    this.getStyleDeclaration().getPropertyValue("background-attachment");
}

VisualElement.prototype.getBackgroundRepeat = function() {
  return (is.ie) ? this.getStyleDeclaration().backgroundRepeat :
    this.getStyleDeclaration().getPropertyValue("background-repeat");
}

VisualElement.prototype.getBackgroundPosition = function() {
  return (is.ie) ? this.getStyleDeclaration().backgroundPosition :
    this.getStyleDeclaration().getPropertyValue("background-position");
}

VisualElement.prototype.getForegroundColor = function() {
  return (is.ie) ? this.getStyleDeclaration().color :
    this.getStyleDeclaration().getPropertyValue("color");
}
VisualElement.prototype.getCursor = function() {
  return (is.ie) ? this.getStyleDeclaration().cursor :
    this.getStyleDeclaration().getPropertyValue("cursor");
}
VisualElement.prototype.getBorderRightWidth = function() {
  var width = (is.ie) ? parseInt(this.getStyleDeclaration().borderRightWidth) :
    parseInt(this.getStyleDeclaration().getPropertyValue("border-right-width"));
  return (isNaN(width)) ? 0 : width;
}
VisualElement.prototype.getBorderBottomWidth = function() {
  var width = (is.ie) ? parseInt(this.getStyleDeclaration().borderBottomWidth) :
    parseInt(this.getStyleDeclaration().getPropertyValue("border-bottom-width"));
  return (isNaN(width)) ? 0 : width;
}
VisualElement.prototype.getBorderLeftWidth = function() {
  var width = (is.ie) ? parseInt(this.getStyleDeclaration().borderLeftWidth) :
    parseInt(this.getStyleDeclaration().getPropertyValue("border-left-width"));
  return (isNaN(width)) ? 0 : width;
}
VisualElement.prototype.getBorderTopWidth = function() { 
  var width = (is.ie) ? parseInt(this.getStyleDeclaration().borderTopWidth) :
    parseInt(this.getStyleDeclaration().getPropertyValue("border-top-width"));
  return (isNaN(width)) ? 0 : width; 
}
VisualElement.prototype.getBorderRightColor = function() {
  return (is.ie) ? this.getStyleDeclaration().borderRightColor :
    this.getStyleDeclaration().getPropertyValue("border-right-color");
}
VisualElement.prototype.getBorderBottomColor = function() {
  return (is.ie) ? this.getStyleDeclaration().borderBottomColor :
    this.getStyleDeclaration().getPropertyValue("border-bottom-color");
}
VisualElement.prototype.getBorderLeftColor = function() {
  return (is.ie) ? this.getStyleDeclaration().borderLeftColor :
    this.getStyleDeclaration().getPropertyValue("border-left-color");
}
VisualElement.prototype.getBorderTopColor = function() {
  return (is.ie) ? this.getStyleDeclaration().borderTopColor :
    this.getStyleDeclaration().getPropertyValue("border-top-color");
}
VisualElement.prototype.getBorderRightStyle = function() {
  return (is.ie) ? this.getStyleDeclaration().borderRightStyle :
    this.getStyleDeclaration().getPropertyValue("border-right-style");
}
VisualElement.prototype.getBorderBottomStyle = function() {
  return (is.ie) ? this.getStyleDeclaration().borderBottomStyle :
    this.getStyleDeclaration().getPropertyValue("border-bottom-style");
}
VisualElement.prototype.getBorderLeftStyle = function() {
  return (is.ie) ? this.getStyleDeclaration().borderLeftStyle :
    this.getStyleDeclaration().getPropertyValue("border-left-style");
}
VisualElement.prototype.getBorderTopStyle = function() {
  return (is.ie) ? this.getStyleDeclaration().borderTopStyle :
    this.getStyleDeclaration().getPropertyValue("border-top-style");
}
VisualElement.prototype.getPaddingTop = function() {
  var width = (is.ie) ? parseInt(this.getStyleDeclaration().paddingTop) :
    parseInt(this.getStyleDeclaration().getPropertyValue("padding-top"));
  return (isNaN(width)) ? 0 : width;
}
VisualElement.prototype.getPaddingRight = function() {
  var width = (is.ie) ? parseInt(this.getStyleDeclaration().paddingRight) :
    parseInt(this.getStyleDeclaration().getPropertyValue("padding-right"));
  return (isNaN(width)) ? 0 : width;
}
VisualElement.prototype.getPaddingBottom = function() {
  var width = (is.ie) ? parseInt(this.getStyleDeclaration().paddingBottom) :
    parseInt(this.getStyleDeclaration().getPropertyValue("padding-bottom"));
  return (isNaN(width)) ? 0 : width;
}
VisualElement.prototype.getPaddingLeft = function() {
  var width = (is.ie) ? parseInt(this.getStyleDeclaration().paddingLeft) :
    parseInt(this.getStyleDeclaration().getPropertyValue("padding-left"));
  return (isNaN(width)) ? 0 : width;
}
VisualElement.prototype.getMarginTop = function() {
  var width = (is.ie) ? parseInt(this.getStyleDeclaration().marginTop) :
    parseInt(this.getStyleDeclaration().getPropertyValue("margin-top"));
  return (isNaN(width)) ? 0 : width;
}
VisualElement.prototype.getMarginRight = function() {
  var width = (is.ie) ? parseInt(this.getStyleDeclaration().marginRight) :
    parseInt(this.getStyleDeclaration().getPropertyValue("margin-right"));
  return (isNaN(width)) ? 0 : width;
}
VisualElement.prototype.getMarginBottom = function() {
  var width = (is.ie) ? parseInt(this.getStyleDeclaration().marginBottom) :
    parseInt(this.getStyleDeclaration().getPropertyValue("margin-bottom"));
  return (isNaN(width)) ? 0 : width;
}
VisualElement.prototype.getMarginLeft = function() {
  var width = (is.ie) ? parseInt(this.getStyleDeclaration().marginLeft) :
    parseInt(this.getStyleDeclaration().getPropertyValue("margin-left"));
  return (isNaN(width)) ? 0 : width;
}


//Set style parameters using this group of methods
VisualElement.prototype.setPositionType = function(type) {
  if(typeof type != "string") fireError("VisualElement.setPositionType()|Parameter 'type' must be a string");
  this.style.position = type;
}
VisualElement.prototype.setVisible = function(state) {
  if(state) {
    this.style.visibility = "visible";
  } else if(!state) {
    this.style.visibility = "hidden";
  }
}
VisualElement.prototype.setZIndex = function(index) {
  this.style.zIndex = index;
}
VisualElement.prototype.setOpacity = function(opacity) {
  if(is.ie) this.node.filters.alpha.opacity = opacity;
  else this.style.MozOpacity = opacity + "%";
}
VisualElement.prototype.setClip = function(top, right, bottom, left) {
  if(isNaN(parseInt(top))) top = ( 0 - screen.height * 1000) + "px";
  if(isNaN(parseInt(left))) left = ( 0 - screen.width * 1000) + "px";
  if(isNaN(parseInt(bottom))) bottom = (screen.height * 1000) + "px";
  if(isNaN(parseInt(right))) right = (screen.width * 1000) + "px";
  if(typeof top == "number") top = top + "px";
  if(typeof right == "number") right = right + "px";
  if(typeof bottom == "number") bottom = bottom + "px";
  if(typeof left == "number") left = left + "px";
  this.style.clip = "rect(" + top + "," + right + "," + bottom + "," +
    left + ")";
}
VisualElement.prototype.setClipTop = function(top) {
  if(typeof top != "number" && typeof top != "string") fireError("VisualElement.setClipTop()|Parameter 'top' must be a number or a string");
  this.setClip(top, this.geClipRight(), this.getClipBottom(), this.getClipLeft());
}
VisualElement.prototype.setClipRight = function(right) {
  if(typeof right != "number" && typeof right != "string") fireError("VisualElement.setClipRight()|Parameter 'right' must be a number or a string");
  this.setClip(this.getClipTop(), right, this.getClipBottom(), this.getClipLeft());
}
VisualElement.prototype.setClipBottom = function(bottom) {
  if(typeof bottom != "number" && typeof bottom != "string") fireError("VisualElement.setClipBottom()|Parameter 'bottom' must be a number or a string");
  this.setClip(this.getClipTop(), this.getClipRight(), bottom, this.getClipLeft());
}
VisualElement.prototype.setClipLeft = function(left) {
  if(typeof left != "number" && typeof left != "string") fireError("VisualElement.setClipLeft()|Parameter 'left' must be a number or a string");
  this.setClip(this.getClipTop(), this.geClipRight(), this.getClipBottom(), left);
}
VisualElement.prototype.setBackground = function(desc) {
  this.style.background = desc;
}
VisualElement.prototype.setBackgroundRepeat = function(value) {
  this.style.backgroundRepeat = value;
}
VisualElement.prototype.setBackgroundColor = function(color) {
  this.style.backgroundColor = color;
}
VisualElement.prototype.setBackgroundImage = function(URL) {
  this.style.backgroundImage = "url(" + URL + ")";
}
VisualElement.prototype.setBackgroundPosition = function(position) {
  this.style.backgroundPosition = position;
}
VisualElement.prototype.setBackgroundAttachment = function(attachment) {
  this.style.backgroundAttachment = attachment;
}

VisualElement.prototype.setBorder = function(desc) {
  if(typeof desc != "string") fireError("VisualElement.setBorder()|Parameter 'desc' must be a string.");
  this.style.border = desc;
}
VisualElement.prototype.setBorderTop = function(desc) {
  if(typeof desc != "string") fireError("VisualElement.setBorderTop()|Parameter 'desc' must be a string.");
  this.style.borderTop = desc;
}
VisualElement.prototype.setBorderRight = function(desc) {
  if(typeof desc != "string") fireError("VisualElement.setBorderRight()|Parameter 'desc' must be a string.");
  this.style.borderRight = desc;
}
VisualElement.prototype.setBorderBottom = function(desc) {
  if(typeof desc != "string") fireError("VisualElement.setBorderBottom()|Parameter 'desc' must be a string.");
  this.style.borderBottom = desc;
}
VisualElement.prototype.setBorderLeft = function(desc) {
  if(typeof desc != "string") fireError("VisualElement.setBorderLeft()|Parameter 'desc' must be a string.");
  this.style.borderLeft = desc;
}
VisualElement.prototype.setPadding = function(width) {
  if(typeof width != "string" && typeof width != "number") fireError("VisualElement.setPadding()|Parameter 'width' must be either a string or a number.");
  this.style.padding = width;
}
VisualElement.prototype.setPaddingTop = function(width) {
  if(typeof width != "string" && typeof width != "number") fireError("VisualElement.setPaddingTop()|Parameter 'width' must be either a string or a number.");
  this.style.paddingTop = width;
}
VisualElement.prototype.setPaddingBottom = function(width) {
  if(typeof width != "string" && typeof width != "number") fireError("VisualElement.setPaddingBottom()|Parameter 'width' must be either a string or a number.");
  this.style.paddingBottom = width;
}
VisualElement.prototype.setPaddingRight = function(width) {
  if(typeof width != "string" && typeof width != "number") fireError("VisualElement.setPaddingRight()|Parameter 'width' must be either a string or a number.");
  this.style.paddingRight = width;
}
VisualElement.prototype.setPaddingLeft = function(width) {
  if(typeof width != "string" && typeof width != "number") fireError("VisualElement.setPaddingLeft()|Parameter 'width' must be either a string or a number.");
  this.style.paddingLeft = width;
}
VisualElement.prototype.setForegroundColor = function(color) {
  if(typeof color != "string") fireError("VisualElement.setForegroundColor()|Parameter 'color' must be a string");
  this.style.color = color;
}
VisualElement.prototype.setOverflow = function(val) {
  if(typeof val != "string") fireError("VisualElement.setOverflow()|Parameter 'val' must be a string");
  this.style.overflow = val;
}
VisualElement.prototype.setCursor = function(type) {
  if(typeof type != "string") fireError("VisualElement.setCursor()|Parameter 'type' must be a string");
  this.style.cursor = type;
}

//-----------------FX Group of methods----------------------

//Perform common positioning actions
VisualElement.prototype.center = function() {
  this.centerX();
  this.centerY();
}

VisualElement.prototype.centerIn = function(obj) {
  this.centerXIn(obj);
  this.centerYIn(obj);
}

VisualElement.prototype.centerX = function() {
  this.setLeft(Math.round( (getInsideWindowWidth() - this.getWidth())/2 ));
}

VisualElement.prototype.centerXIn = function(obj) {
  if(obj == null) obj = this.node.offsetParent;
  var parent = (obj.node != null) ? obj : new VisualElement(obj);
  var left = Math.round((parent.getWidth() - this.getWidth())/2) + parent.getLeft();
  this.setLeft(left);
}

VisualElement.prototype.centerY = function(obj) {
  this.setTop(Math.round( (getInsideWindowHeight() - this.getHeight())/2 ));
}

VisualElement.prototype.centerYIn = function(obj) {
  if(obj == null) obj = this.node.offsetParent;
  var parent = (obj.node != null) ? obj : new VisualElement(obj);
  var top = Math.round((parent.getHeight() - this.getHeight())/2) + parent.getTop();
  this.setTop(top);
}

		 //Accelerated Animation
		 VisualElement.prototype.startAnimation = function(type, startPos, endPos,
		 										step, startSpeed, speedFactor, center, isClockwise) {
						 if(typeof step != "number" || typeof startPos.left != "number" || typeof endPos.left != "number" || typeof startPos.top != "number" || typeof endPos.top != "number" || typeof startSpeed != "number" || typeof speedFactor != "number")
						 			 fireError("VisualElement.startAnimation()|Incorrect Parameter set.");
						 this.isClockwise = isClockwise;
						 this.stopAnimation();
						 this.startPos = startPos; this.endPos = endPos;
						 this.animationType = type;
						 this.animateInterval = startSpeed;
						 this.animateIntervalFactor = speedFactor;
						 this.step = step;

						 if(type == "line") {
						 		 this.multiplier = ((this.endPos.left - this.startPos.left) > 0) ? 1 : -1;
								 this.linearFactor = (startPos.top - endPos.top)/(startPos.left - endPos.left);
						 }
						 else if(type == "arc") {
						 		 this.step = step * (Math.PI/180);
								 var xi = startPos.left - center.left;
								 var yi = -(startPos.top - center.top);
								 var xf = endPos.left - center.left;
								 var yf = -(endPos.top - center.top);
								 this.radiusOfArc = Math.sqrt( xi*xi + yi*yi );
								 if(this.radiusOfArc != Math.sqrt( xf*xf + yf*yf )) fireError("VisualElement.startAnimation()|Cannot animate along arc as the start and end positions do not both lie on the circle with center " + center);
								 this.centerOfArc = center;
								 this.initialAngle = Math.atan(yi/xi);
								 if(yi/xi < 0) this.initialAngle += 2*Math.PI;
								 this.finalAngle = Math.atan(yf/xf);
								 if(yf/xf < 0) this.finalAngle += 2*Math.PI;
								 if(this.initialAngle == 0) this.initialAngle = 2*Math.PI;
								 if(this.finalAngle == 0) this.finalAngle = 2*Math.PI;
						 }
						 if(animationDispatcher.getDispatcherFor(this.listenerCode) == null)
						 														  animationDispatcher.addDispatcher(this);
						 var listeners = this.animationStartListeners.getListeners();
						 for(var c = 0; c < listeners.length; c++) listeners[c].onAnimationStart(this);
						 this.setLeft(startPos.left); this.setTop(startPos.top);
						 this.isBeingAnimated = true;
						 this.animate();
		 }

var animationDispatcher = new EventDispatcherList();

		VisualElement.prototype.animate = function() {
						if(!this.isBeingAnimated) return;
						var cont = true;
						var newX = 0;
						var newY = 0;
						if(this.animationType == "line") {
											  newX = this.getLeft() + (this.multiplier * this.step);
											  if(this.multiplier < 0 && newX <= this.endPos.left) {
											  newX = this.endPos.left; cont = false;
											  } else if(this.multiplier > 0 && newX >= this.endPos.left) {
									  	      newX = this.endPos.left; cont = false;
											  }
											  newY = (cont) ? Math.floor(this.endPos.top + ((newX - this.endPos.left) * this.linearFactor)) : this.endPos.top;
											  this.animateInterval *= this.animateIntervalFactor;
											  if(this.animateInterval < 1) this.step++;
						}
						else if(this.animationType == "arc") {
							 var temp = -(this.getTop() - this.centerOfArc.top) / this.getLeft() - this.centerOfArc.left;
							 var theta = Math.atan(temp);
							 if(temp < 0) theta += 2*Math.PI;
							 if(theta == 0) theta =  2*Math.PI;
							 theta += (this.isClockwise) ? -this.step : this.step;
							 if(this.isClockwise) {
							 					  if(theta < this.finalAngle) { theta = this.finalAngle; cont = false; }
							 } else if(theta > this.finalAngle) { theta = this.finalAngle; cont = false; }
							 newX = (this.radiusOfArc * Math.cos(theta)) + this.centerOfArc.left;
							 newY = -(this.radiusOfArc * Math.sin(theta)) + this.centerOfArc.top;
							 this.animateInterval *= this.animateIntervalFactor;
							 if(this.animateInterval < 1) this.step += Math.PI/180;

						}

						this.setLeft(newX); this.setTop(newY);
						var listeners = this.animationChangeListeners.getListeners();
						for(var c = 0; c < listeners.length; c++) listeners[c].onPositionChange(newX, newY);
						if(cont) this.animationTimer = setTimeout("VisualElement.prototype.callAnimator('" + this.listenerCode + "')", this.animateInterval);
						else this.stopAnimation();
		}

		VisualElement.prototype.stopAnimation = function() {
						if(!this.isBeingAnimated) return;
						clearTimeout(this.animationTimer);
						this.isBeingAnimated = false;
						var listeners = this.animationStopListeners.getListeners();
						for(var c = 0; c < listeners.length; c++)
								listeners[c].onAnimationStop(this);
						animationDispatcher.removeDispatcher(this);
		}

		VisualElement.prototype.pauseAnimation = function() {
						clearTimeout(this.animationTimer);
		}

		VisualElement.restartAnimation = function() {
						if(this.isBeingAnimated)
												 this.animationTimer = setTimeout("VisualElement.prototype.callAnimator('" + this.listenerCode + "')", 1);
		}

		VisualElement.prototype.addAnimationStartListener = function(listener) {
						if(typeof listener != "object" || typeof listener.onAnimationStart != "function") return;
						this.animationStartListeners.addListener(listener);
		}

		VisualElement.prototype.addAnimationStopListener = function(listener) {
						if(typeof listener != "object" || typeof listener.onAnimationStop != "function") return;
						this.animationStopListeners.addListener(listener);
		}

		VisualElement.prototype.addAnimationChangeListener = function(listener) {
						if(typeof listener != "object" || typeof listener.onPositionChange != "function") return;
						this.animationChangeListeners.addListener(listener);
		}

		VisualElement.prototype.removeAnimationChangeListener = function(listener) {
						if(typeof listener != "object" || typeof listener.onPositionChange != "function") return;
						this.animationChangeListeners.removeListener(listener);
		}

		VisualElement.prototype.removeAnimationStartListener = function(listener) {
						if(typeof listener != "object" || typeof listener.onAnimationStart != "function") return;
						this.animationStartListeners.removeListener(listener);
		}

		VisualElement.prototype.removeAnimationStopListener = function(listener) {
						if(typeof listener != "object" || typeof listener.onAnimationStop != "function") return;
						this.animationStopListeners.removeListener(listener);
		}

		VisualElement.prototype.callAnimator = function(code) {
						var animator = animationDispatcher.getDispatcherFor(code);
						if(animator != null) animator.animate();
		}

//------------------------Positioning group of methods------------------------
VisualElement.prototype.getWidth = function() {
  return this.node.offsetWidth;
}
VisualElement.prototype.getHeight = function() {
  return this.node.offsetHeight;
}
VisualElement.prototype.getSize = function() {
  return new Dimension(this.getWidth(), this.getHeight());
}
//Returns left edge of left border in BODY system
VisualElement.prototype.getLeft = function() {
  var x = 0;
  var parent = this.node;
  while(parent && parent.tagName != "BODY" && parent.tagName != "HTML") {
    x += parent.offsetLeft;
    parent = parent.offsetParent;
  }
  return x;  
}
//Returns left edge of left border in parent's system
VisualElement.prototype.getOffsetLeft = function() {
  return this.node.offsetLeft;  
}

//Returns right edge of right border in BODY system
VisualElement.prototype.getRight = function() {
  return this.getLeft() + this.getWidth();
}
//Returns right edge of right border in parent's system
VisualElement.prototype.getOffsetRight = function() {
  return this.getOffsetLeft() + this.getWidth();
}

//Returns top edge of top border in BODY system
VisualElement.prototype.getTop = function() {
  var x = 0;
  var parent = this.node;
  while(parent && parent.tagName != "BODY" && parent.tagName != "HTML") {
    x += parent.offsetTop;
    parent = parent.offsetParent;
  }
  return x;  
}
//Returns top edge of top border in parent's system
VisualElement.prototype.getOffsetTop = function() {
  return this.node.offsetTop;
}
//Returns bottom edge of bottom border in BODY system
VisualElement.prototype.getBottom = function() {
  return this.getTop() + this.getHeight();
}
//Returns bottom edge of bottom border in parent's system
VisualElement.prototype.getOffsetBottom = function() {
  return this.getOffsetTop() + this.getHeight();
}

VisualElement.prototype.getLocation = function() {
  return new Point(this.getLeft(), this.getTop());
}
VisualElement.prototype.getOffsetLocation = function() {
  return new Point(this.getOffsetLeft(), this.getOffsetTop());
}

VisualElement.prototype.setWidth = function(width) {
  if(isNaN(parseInt(width))) fireError("VisualElement.setWidth()|Parameter 'width' must be a number or a string that evaluates to a number.");
  this.style.width = (parseInt(width) -
    ((is.ie)?0:this.getBorderLeftWidth()+this.getBorderRightWidth())) + "px";
}
VisualElement.prototype.setHeight = function(height) {
  if(isNaN(parseInt(height))) fireError("VisualElement.setHeight()|Parameter 'height' must be a number or a string that evaluates to a number.");
  this.style.height = (parseInt(height) -
		   ((is.ie)?0:this.getBorderTopWidth() + this.getBorderBottomWidth()))  + "px";
}
VisualElement.prototype.setSize = function(width, height) {
  this.setWidth(width); this.setHeight(height);
}

VisualElement.prototype.setOffsetLeft = function(left) {
  if(isNaN(parseInt(left))) fireError("VisualElement.setOffsetLeft()|Parameter 'left' must be a number or a string that evaluates to a number.");
  this.style.left = (parseInt(left) - this.getMarginLeft()) + "px";
}
VisualElement.prototype.setOffsetRight = function(right) {
  if(isNaN(parseInt(right))) fireError("VisualElement.setOffsetRight()|Parameter 'right' must be a number or a string that evaluates to a number.");
  this.setOffsetLeft(parseInt(right) - this.getWidth());
}
VisualElement.prototype.setOffsetBottom = function(bottom) {
  if(isNaN(parseInt(bottom))) fireError("VisualElement.setOffsetBottom()|Parameter 'bottom' must be a number or a string that evaluates to a number.");
  this.setOffsetTop(parseInt(bottom) - this.getHeight());
}
VisualElement.prototype.setOffsetTop = function(top) {
  if(isNaN(parseInt(top))) fireError("VisualElement.setOffsetTop()|Parameter 'top' must be a number or a string that evaluates to a number.");
  this.style.top = (parseInt(top) - this.getMarginTop()) + "px";
}
VisualElement.prototype.setLeft = function(left) {
  if(isNaN(parseInt(left))) fireError("VisualElement.setLeft()|Parameter 'left' must be a number or a string that evaluates to a number.");
  var offsetParent = (this.node.offsetParent) ? this.node.offsetParent : this.node.parentNode;
  var parent = new VisualElement(offsetParent, false);
  this.setOffsetLeft(parseInt(left) - parent.getLeft());
}
VisualElement.prototype.setRight = function(right) {
  if(isNaN(parseInt(right))) fireError("VisualElement.setRight()|Parameter 'right' must be a number or a string that evaluates to a number.");
  this.setLeft(parseInt(right) - this.getWidth());
}
VisualElement.prototype.setTop = function(top) {
  if(isNaN(parseInt(top))) fireError("VisualElement.setTop()|Parameter 'top' must be a number or a string that evaluates to a number.");
  var offsetParent = (this.node.offsetParent) ? this.node.offsetParent : this.node.parentNode;
  var parent = new VisualElement(offsetParent, false);
  this.setOffsetTop(parseInt(top) - parent.getTop());
}
VisualElement.prototype.setBottom = function(bottom) {
  if(isNaN(parseInt(bottom))) fireError("VisualElement.setBottom()|Parameter 'bottom' must be a number or a string that evaluates to a number.");
  this.setTop(parseInt(bottom) - this.getHeight());
}
VisualElement.prototype.setLocation = function(left, top) {
  this.setLeft(left); this.setTop(top);
}
//------------------Miscellanous Methods------------------------------------------------------
		VisualElement.prototype.setInnerHTML = function(html) {
		 				  this.node.innerHTML = html;
		}
		VisualElement.prototype.setOuterHTML = function(html) {
		 				  if(is.ie) this.node.outerHTML = html;
		 				  else if(is.ns) {
		 	  			  this.node.outerHTMLInput = html;
      		  			  var newText = this.node.ownerDocument.createRange();
      		  			  newText.setStartBefore(this.node);
      		  			  var docFrag = newText.createContextualFragment(html);
      		  			  this.node.parentNode.replaceChild(docFrag, this.node);
  						  }
		}
		VisualElement.prototype.getInnerHTML = function(node) {
		 				  return this.node.innerHTML;
		}
		VisualElement.prototype.getOuterHTML = function(node) {
		 				  if(is.ie4up) return this.node.outerHTML;
		 				  else if(is.nav5up) {
		 	  			  	   if(node == null) node = this.node;
		 	  				   var html = "<"+node.tagName+" "+getAttributes(node)+">";
			  				   html += this.getInnerHTML(node);
			  				   html += "</" + node.tagName + ">";
			  				   return html;
						 }
		}
		VisualElement.prototype.getInnerText = function(obj) {
		 				  var text = "";
		 				  if(is.ns) {
		 		   		  			var node = obj;
		 		   					if(node == null) node = this.node;
				   					if(node == null) return null;
				   					var children = getChildrenOfNode(node);
				   					for(var c = 0; c < children.length; c++) {
				   		   					var child = children.item(c);
						   						if(child.nodeType == Node.CDATA_SECTION_NODE ||
						   					 					  child.nodeType == Node.TEXT_NODE)
						   					 					  text += child.nodeValue;
						   						else if(child.nodeType == Node.ELEMENT_NODE)
						   							 text += this.getInnerText(child);
									}
		 					} else if(is.ie) text = this.node.innerText;
		 					return text;
		}
		VisualElement.prototype.refresh = function() {
					 this.setVisible(false);
					 this.setVisible(true);
		}

		VisualElement.prototype.focus = function() {
				if(this.node.focus) this.node.focus();
				else fireError("VisualElement.focus()|Unable to focus " + this + " as it does not support focusing.");
		}

		VisualElement.prototype.blur = function() {
				if(this.node.blur) this.node.blur();
				else fireError("VisualElement.blur()|Unable to blur " + this + " as it does not support blurring.");
		}

		VisualElement.prototype.getID = function() {
					 return this.node.id;
		}

		VisualElement.prototype.setID = function(id) {
					this.node.id = id;
		 }

		VisualElement.prototype.getParent = function() {
					return this.node.parentNode;
		}
		//Returns a NodeList (has method item() and field length())
		VisualElement.prototype.getChildren = function() {
					 return this.node.childNodes;
		}
		VisualElement.prototype.contains = function(node) {
					 node = getReferenceToNode(node);
					 if(node == null) return false;
					 var children = this.getChildren();
					 for(var c = 0; c < children.length; c++) {
					 		 if(children.item(c) == node) return true;
							 if((new VisualElement(children.item(c), false, false)).contains(node)) return true;
					 }
					 return false;
		}
		//Returns true iff visualElement == this (as two different VisualElements might be used for two simultaneous
		//Animation series on the same node.
		VisualElement.prototype.equals = function(visualElement) {
						   return (visualElement == this);
		}

		VisualElement.prototype.toString = function() {
										 return "[Visual Element: " + this.getID() + " " + this.node.tagName + "]";
		}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////The OO Event Dispatcher
//////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//In order to use this List, all EventListeners must have a listenerCode field defined and a
//proper equals method (it should return false for different objects)

function EventDispatcherList() {
		 this.listeners = new Array();
}

EventDispatcherList.prototype.size = function() { return this.listeners.length; }

EventDispatcherList.prototype.addDispatcher = function(listener) {
		 if(typeof listener != "object" || listener.listenerCode == null) return;
		 if(this.listeners.indexOf(listener) == -1) this.listeners.add(listener);
}

EventDispatcherList.prototype.removeDispatcher = function(listener) {
		if(typeof listener != "object") return;
		this.listeners.remove(listener);
}

EventDispatcherList.prototype.getDispatcherFor = function (listenerCode) {
		for(var c = 0; c < this.listeners.length; c++) {
				if(this.listeners[c].listenerCode == listenerCode) return this.listeners[c];
		}
		return null;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////The OO Event Listener
//////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function EventListenerList() {
		 this.listeners = new Array();
}

EventListenerList.prototype.addListener = function(listener) {
		if(typeof listener != "object") return;
		if(this.listeners.indexOf(listener) == -1) this.listeners.add(listener);
}

EventListenerList.prototype.getListeners = function() { return this.listeners; }

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////Event Handling
/////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function InterfaceEvent(evt) {
		 if(is.ns && (evt == null || typeof evt.type != "string")) fireError("InterfaceEvent Constructor|Parameter 'evt' must be a valid EventObject");
		 else if(is.ie && window.event == null) fireError("InterfaceEvent Constructor|There is no currently bubbling event.");
		 this.evt = (is.ie) ? window.event : evt;

		 //Returns the lowest non-text node in bubble tree
		 this.getSourceNode = function() {
							   if(is.ns) {
							   			 var parent = this.evt.target;
										 while(parent.nodeName == "#text") {
										 					   parent = parent.parentNode;
										 }
										 return parent;
							   }
							   if(is.ie) return this.evt.srcElement;
		 }
		//Y Offset in container elements space
		 this.getEventYOffset = function() {
			 				  	   var target = new VisualElement(this.getSourceNode(), false);
			 				  	   return this.getEventY() - target.getTop();
		 }
		//X Offset in container elements space
		this.getEventXOffset = function() {
			 				  	   var target = new VisualElement(this.getSourceNode(), false);
			 				  	   return this.getEventX() - target.getLeft();
		}

		this.getKeyCode = function() {
		 					  if(is.ns) return this.evt.which;
							  else if(is.ie) return this.evt.keyCode;
		}

		this.wasShiftPressed = function() {
		 					  return (is.ns) ? this.evt.modifiers & Event.SHIFT_MASK : this.evt.shiftKey;
		}

		this.wasCtrlPressed = function() {
		 					  return (is.ns) ? this.evt.modifiers & Event.CTRL_MASK : this.evt.ctrlKey;
		}

		this.wasAltPressed = function() {
		 					  return (is.ns) ? this.evt.modifiers & Event.ALT_MASK : this.evt.altKey;
		}
		//X co-ord in BODY space
		this.getEventX = function() {
		 					  return (is.ie) ? this.evt.clientX : this.evt.pageX;
		}
		//Y co-ord in BODY space
		this.getEventY = function() {
		 					  return (is.ie) ? this.evt.clientY : this.evt.pageY;
		}

		this.whichButton = function() {
							  return (is.ns) ? this.evt.which : this.evt.button;
		}

		this.wasLeftClick = function() {
							  return (parseInt(this.whichButton()) == 0);
		}

		this.wasRightClick = function() {
							  return ( (is.ie && this.whichButton() == -1) || (is.ns && this.whichButton() == 3) );
		}

		this.wasMiddleClick = function() {
							  return ( (is.ie && this.whichButton() == -1) || (is.ns && this.whichButton() == 2) );
		}

		this.stopBubble = function() {
		 		if(is.nav5up) this.evt.preventBubble();
				else if(is.ie4up) this.evt.cancelBubble = true;
		}

		this.getRelatedNode = function() {
					if(this.evt.type == "mouseout") {
									 if(is.ns) return this.evt.relatedNode;
									 if(is.ie) return this.evt.toElement;
					} else if(this.evt.type == "mouseover") {
					  	   			 if(is.ns) return this.evt.relatedNode;
									 if(is.ie) return this.evt.fromElement;
					}
					return null;
		}

		this.getType = function() { return this.evt.type; }

		this.toString = function() { return "[Interface Event: " + this.evt.type + "]";}
}
