if (!Array.prototype.copy) {
	Array.prototype.copy = function () {
		var a2 = [];
		for (var i = 0; i < this.length; i++) {
			a2.push(this[i]);
		}
		return a2;
	}
}

if (!Array.prototype.push) {
	Array.prototype.push = function () {
		var args = arguments;
		for (var i = 0; i < args.length; i++) this[this.length] = args[i];
		return this.length;
	}
}

if (!Array.prototype.pop) {
	Array.prototype.pop = function () {
		var li = null;
		if (this.length > 0) {
			li = this[this.length-1];
			this.length--;
		}
		return li;
	}
}

if (!Array.prototype.concat) {
	Array.prototype.concat = function () {
		var a1 = this.copy();
		var args = arguments;
		for (var i = 0; i < args.length; i++) {
			var a2 = args[i];
			for (var j = 0; j < a2; j++) a1.push(a2[j]);
		}
		return a1;
	}
}

if (!Array.prototype.slice) {
	Array.prototype.slice = function (start, end) {
		if (!end) end = this.length;
		else if (end < 0) end = this.length + end;
		else if (start > 0) end = this.length + start;
		if (start > end) {
			var tmp = start;
			start = end
			end = tmp;
		}
		var a2 = [];
		for (var i = 0; i < end-start; i++){
			a2.push(this.start+i);
		}
		return a2;
	}
}

if (!Array.prototype.splice) {
	Array.prototype.splice = function (start, c) {
		if (!c) c = this.length - start;
		var a2 = this.copy();
		for (var i = start; i < start + arguments.length - 2; i++) {
			this[i] = arguments[i - start + 2];
		}
		for (var i = start; i < start + arguments.length - 2; i++) {
			this[i] = a2[i + c - arguments.length + 2];
		}
		this.length = this.length - c + (arguments.length - 2);
		return a2.slice(start, start + c);
	}
}

Array.prototype.indexOf = function (obj) {
	for (var i = (this.length-1); i >= 0; i--) {
		if (obj == this[i]) return i;
	}
	return -1;
}


/**
 *	<code>Browser</code> is an object used to create the global 
 *	<code>is</code> variable. The <code>is</code> object allows
 *	us to discover the following information:
 *  - browser brand (is.op, is.ie, is.moz) 
 *	- dom support 
 *  - platform (is.win, is.mac)
 *	- quirk mode 
 *
 *	@attribute : op (Boolean) indicates whether the current browser is Opera
 *	@attribute : ie (Boolean) indicates whether the current browser is Internet Explorer
 *	@attribute : moz (Boolean) indicates whether the current browser is Mozilla
 *	@attribute : ie5 (Boolean) indicates whether the current browser is Internet Explorer 5.0
 *	@attribute : ie55 (Boolean) indicates whether the current browser is Internet Explorer 5.5
 *	@attribute : ie6 (Boolean) indicates whether the current browser is Internet Explorer 6.0
 *	@attribute : dom (Boolean) indicates whether the current browser is DOM compatible
 *	@attribute : win (Boolean) indicates whether the current platform is Windows
 *	@attribute : mac (Boolean) indicates whether the current platform is Macintosh
 *	@attribute : quirk (Boolean)indicates whether the browser is in "quirk mode"
 */

function Browser () {
    var ua = navigator.userAgent;
    this.op = (ua.indexOf("Opera") != -1);
    this.ie = (ua.indexOf("MSIE") != -1 && !this.op);
    this.moz = (ua.indexOf("Gecko") != -1 && !this.op);
    if (this.ie) {
        this.ie6 = (ua.indexOf("MSIE 6") != -1);
        this.ie55 = (ua.indexOf("MSIE 5.5") != -1);
        this.ie5 = (!this.ie55 && ua.indexOf("MSIE 5") != -1);
    }
    this.dom = (document.implementation && document.implementation.hasFeature)?true:false;
    ua = ua.toLowerCase();
    this.win = (ua.indexOf("win") != -1);
    this.mac = (ua.indexOf("mac") != -1);
    this.quirk = (document.compatMode == "BackCompat")?true:false;
    return this;
}

var is = new Browser();

/**
 *	<code>api</code> is a global object with various uses, it allow us to:
 *	- execute functions onLoad: api.addOnLoad(aFunction)
 *	- execute functions onUnLoad: api.addOnUnLoad(aFunction)
 *	- lookup elements: api.getElementById("someId"), api.getElementsByTagName("tagName"), api.getContainer(HTMLElement)
 *	- access the document: api.doc
 *  - include packages: api.include("lib.pkg");
 *	To include new packages the api.path variable must be set to the directory in which
 *	the dElement packages hav been copied.
 *
 *	@attribute : onLoadFunctions (Array) holds the list of functions to be called when the document has loaded.
 *	@attribute : onUnoadFunctions (Array) holds the list of functions to be called when the document is to be unloaded.
 *	@attribute : ids (Object) holds a list of all of the elements looked up using api.getElementById.
 *	@attribute : delements (Array) holds a list of all of the generated dElements. This list is used to dereference all of the dElements in the <code>document.unload</code> event, which is needed to clean up memory.
 *	@attribute : topZ (Number) holds the z-index to be used by elements that must come to the top of the z stack.
 *	@attribute : path (String) the path to dElements root folder.
 *	@attribute : loaded (Object) used to store the loaded state of a package.
 *	@attribute : packages (Object) registry for loadable packages and dependancies.
 *	@attribute : doc (dDocument) reference to the document. This property is created after the document has loaded, but is accesible from the onload functions.
 */	

api = {
    onLoadFunctions : [], onUnloadFunctions : [],
	addOnLoad : function (func) {
		api.onLoadFunctions.push(func);
	},
    addOnUnload : function (func) {
		api.onUnloadFunctions.push(func); 
	},	
    load : function () {
        if (document.readyState && document.readyState != "complete") return;
        api.doc = new dDocument(document.body);
        for (var i = 0; i < api.onLoadFunctions.length; i++) { api.onLoadFunctions[i]();}
        if (typeof(StateSaver) == "object") setTimeout("StateSaver.restoreState()",50);
    },
    unload : function () {
        for (var i = 0; i < api.onUnloadFunctions.length; i++) { api.onUnloadFunctions[i](); }
        if (typeof(StateSaver) == "object") StateSaver.saveState();
        for (var i = (api.delements.length-1); i > 0; i--) {
            for (var j in api.delements[i]) { api.delements[i][j] = null; }
            api.delements[i] = null;
        }
    },
    ids : {}, // keeps track off all the elements looked up with api.getElementById -> prevents more than one js object being created for each lookup
    delements : [], // keeps track off all of the created dElements
    getElementById : function (id) { if (api.ids[id]) return api.ids[id]; else if (elm = document.getElementById(id)) { api.ids[id] = new dElement(elm); return api.ids[id];}},
    getElementsByTagName : function (tag) { var elms = document.getElementsByTagName(tag); var delms = []; for (var i = 0; i < elms.length; i++) { delms.push((elms[i].dElement != null)?elms[i].dElement:new dElement(elms[i])); } return delms; },
    getContainer : function (elm) { if (elm) { while(elm.parentNode && !elm.dElement) { elm = elm.parentNode; } return elm.dElement; }},
    topZ : 10000,
    path : "",
	loaded : {},
    packages : {
        data : {
			tablemodel : []
		},
		gui : { 
			calendar : ["gui.imagebutton", "gui.table", "util.date"],
			datatable : ["data.tablemodel", "gui.table"],
			datepicker : ["gui.calendar", "gui.dropdown"], 
			dropdown : [], 
			imagebutton : [], 
			menu : [],
			monthcalendar : ["gui.imagebutton", "gui.table", "util.date"],
			table : [], 
			tree : ["util.file"], 
			xmlmenubar : ["gui.menu", "xml.xmlextras"],
			xmltree : ["gui.tree", "xml.xmlextras"] 
		},
		util : { date : [], file : [] },
		xml : { xmlextras : [] }
		
    },
    include : function (pkg) { if (api.loaded[pkg] == true) return; var tmp = pkg.split("."); var _pkg = tmp[0]; var _file = tmp[1]; if (_file == "*") { for (var i in api.packages[_pkg]) { api._includeRequired(_pkg, i); api._include(_pkg + "." + i); api.loaded[_pkg + "." + i] = true; }} else { api._includeRequired(_pkg, _file); api._include(pkg); api.loaded[pkg] = true; }},
    _includeRequired : function (pkg,file) { var required = api.packages[pkg][file]; for (var i = 0; i < required.length; i++) { api.include(required[i]);}},
    _include : function (pkg) { var url = pkg.replace(/\./g, "/"); if (url.match(/^gui\//) != null) document.write("<link rel=\"stylesheet\" type=\"text/css\" href=\"" + api.path + "styles/" + url + ".css\" media=\"screen\" />"); document.write("<script type=\"text/javascript\" src=\"" + api.path + "js/" + url + ".js\" /><\/\s\cr\i\pt\>"); }
}

/*
 *	These functions try to initialize the api using the documents readyState
 *	and if this is not possible the onload event is used.
 */

if( window.addEventListener ) window.addEventListener( "load", api.load, false ) ;
else if( typeof document.onreadystatechange != "undefined" ) document.onreadystatechange = api.load;
else { document.onload = api.load }
//onunload = api.unload;

/**
 *	<code>dElement</code> is the core object of the api. Usually to create 
 *	a new component you will extend the dElement.
 *	@param : obj (String, HTMLElement, dElement) a string, HTMLElement or dElement of which a new dElement will be created.
 *	@returns : dElement
 *	@attribute : elm
 *	@attribute : style (Object) reference to the elements style object.
 *	@attribute : elm (HTMLElement) reference to the element.
 *	@attribute : elm.dElement (dElement) reference to the dElement.
 */

function dElement (obj) {
    var arg = dElement.arguments[0];
    switch (typeof(obj)) {
        case "string" :
            this.elm = document.createElement(obj);
            break;
        case "object" :
            if (obj.constructor == dElement) obj = obj.elm;
            this.elm = obj;
            break;
        default :
            this.elm = document.createElement("div");
    }
    this.style = this.elm.style;
    this.elm.dElement = this;
    api.delements.push(this);
    return this;
}

/**
 *	Attaches an element to the object as a child.
 *	@argument : elm (HTMLElement | dElement) the element to append.
 */
dElement.prototype.appendChild = function (elm) {
    if (elm.constructor == dElement) elm = elm.elm;
    this.elm.appendChild(elm);
}

/**
 *	Removes the child from the object.
 *	@argument : elm (HTMLElement | dElement) the element to remove.
 */
dElement.prototype.removeChild = function (elm) {
    if (elm.constructor == dElement) this.elm.removeChild(elm.elm);
    else this.elm.removeChild(elm);
}

/**
 *	Inserts an element as a child before the reference node.
 *	@argument : elm (HTMLElement | dElement) the element to inset.
 *	@argument : ref (HTMLElement | dElement) the element before which the element should be inserted.
 */
dElement.prototype.insertBefore = function (elm, ref) {
    if (elm.constructor == dElement) elm = elm.elm;
    if (ref.constructor == dElement) ref = ref.elm;
    this.elm.insertBefore(elm,ref);
}

/**
 *	Sets an element as the parent of the object.
 *	@argument : par (HTMLElement | dElement) the elements the object should be appended to.
 */
dElement.prototype.setParent = function (par) {
    var elm = (par.constructor == dElement)?this:this.elm;
    par.appendChild(elm);
}

/**
 *	Returns the objects first parent that is a dElement.
 *	@returns : (dElement) the first parent dElement
 */
dElement.prototype.getParent = function () {
    var elm = this.elm;
    while (elm.parentNode != null) {
        elm = elm.parentNode;
        if (elm.dElement) return elm.dElement;
    }
}

/**
 *	Returns an array of dElements that are children of the object and have the tagname specified.
 *	@argument : tag (String) the tagname of the dElements you want.
 *	@returns : (Array) an array of dElements
 */
dElement.prototype.getElementsByTagName = function (tag) {
    var elms = this.elm.getElementsByTagName(tag);
    var delms = [];
    for (var i = 0; i < elms.length; i++) {
        delms.push((elms[i].dElement != null)?elms[i].dElement:new dElement(elms[i]));
    }
    return delms;
}

/**
 *	Returns an array of dElements that are childNodes of the object.
  *	@returns : (Array) an array of dElements
 */
dElement.prototype.getChildNodes = function () {
    var elms = this.elm.childNodes;
    var delms = [];
    for (var i = 0; i < elms.length; i++) {
        if (elms[i].nodeType == 1) delms.push((elms[i].dElement != null)?elms[i].dElement:new dElement(elms[i]));
    }
    return delms;
}

/**
 *	Returns the html of the object.
 *	@returns : (String) a string of html.
 */

dElement.prototype.getHTML = function () { return this.elm.innerHTML; }
/**
 *	Sets the html of the object.
 *	@argument : html (String) a string of html.
 */
dElement.prototype.setHTML = function (html) { this.elm.innerHTML = html; }

/**
 *	Returns the className (the html attribute <code>class</code>) of the element.
 *	@returns : (String) the className of the element.
 */ 
dElement.prototype.getClass = function () { return this.elm.className; }

/**
 *	Sets the className (the html attribute <code>class</code>) of the element.
 *	@argument : cn (String) the className of the element.
 */ 
dElement.prototype.setClass = function (cn) { this.elm.className = cn; }

dElement.prototype.addClass = function (cn) {
	var c = this.getClass();
	var cs = c.split(" ");
	if (cs.indexOf(cn) == -1) {
		cs.push(cn);
		this.setClass(cs.join(" "));
	}
}

dElement.prototype.removeClass = function (cn) {
	var c = this.getClass();
	var cs = c.split(" ");
	var i = cs.indexOf(cn);
	if (i > -1) {
		cs.splice(i,1);
		this.setClass(cs.join(" "));
	}
}

dElement.prototype.getAttribute = function (a) { return this.elm[a]; }
dElement.prototype.setAttribute = function (a, v) { this.elm[a] = v;}
dElement.prototype.removeAttribute = function (a) { this.elm[a] = null;}

dElement.prototype.invokeEvent = function (t) { if (this["on"+t]) { this["on"+t](); }}

dElement.prototype.addEventListener = function (t, f) {
    if (is.ie) {
        this.removeEventListener(t, f);
        this.elm.attachEvent("on" + t, f);
    }
    else if (is.dom) this.elm.addEventListener(t, f, false);
}

dElement.prototype.removeEventListener = function (t, f) {
    if (is.ie) this.elm.detachEvent("on" + t, f);
    else if (is.dom) this.elm.removeEventListener(t, f, false);
}

dElement.prototype.cancelBubble = function () {
    this.addEventListener("click", dElement.bubbleFunction);
    this.addEventListener("mousedown", dElement.bubbleFunction);
    this.addEventListener("mouseup", dElement.bubbleFunction);
    this.addEventListener("mouseover", dElement.bubbleFunction);
    this.addEventListener("mouseout", dElement.bubbleFunction);
}

dElement.prototype.allowBubble = function () {
    this.removeEventListener("click", dElement.bubbleFunction);
    this.removeEventListener("mousedown", dElement.bubbleFunction);
    this.removeEventListener("mouseup", dElement.bubbleFunction);
    this.removeEventListener("mouseover", dElement.bubbleFunction);
    this.removeEventListener("mouseout", dElement.bubbleFunction);
}

dElement.bubbleFunction = function (e) { var e = e || window.event; e.cancelBubble = true;}


dElement.prototype.getX = function () { return this.elm.offsetLeft; }
dElement.prototype.getPageX = function () { var x = 0, currentNode = this.elm; while (currentNode.offsetParent && currentNode != document.body) { x += currentNode.offsetLeft; currentNode = currentNode.offsetParent; } return x; }
dElement.prototype.setX = function (i) { this.moveTo(i,null); }
dElement.prototype.getY = function () { return this.elm.offsetTop; }
dElement.prototype.getPageY = function () { var y = 0, currentNode = this.elm; while (currentNode.offsetParent && currentNode != document.body) { y += currentNode.offsetTop; currentNode = currentNode.offsetParent; }	return y; }
dElement.prototype.setY = function (i) { this.moveTo(null,i); }
dElement.prototype.getW = function () { return this.elm.offsetWidth; }
dElement.prototype.setW = function (i) { this.setSize(i,null) }
dElement.prototype.getH = function () { return this.elm.offsetHeight; }
dElement.prototype.setH = function (i) { this.setSize(null,i); }
dElement.prototype.getZ = function () { return this.style.zIndex; }
dElement.prototype.setZ = function (i) { this.style.zIndex = i; }
dElement.prototype.moveTo = function (x,y) { if (x != null) this.style.left = x + "px"; if (y != null) this.style.top = y + "px"; this.invokeEvent("move");}
dElement.prototype.moveBy = function (x,y) { this.moveTo(this.getX() + x, this.getY() + y); }
dElement.prototype.setSize = function (w,h) { if (w && w >= 0) this.style.width = w + "px"; if (h && h >= 0) this.style.height = h + "px"; }
dElement.prototype.setSizeBy = function (w,h) { this.setSize(this.getW() + w, this.getH() + h); }

dElement.prototype.show = function () { this.style.visibility = "inherit"; }
dElement.prototype.hide = function () { this.style.visibility = "hidden"; }

/**
 *	<code>dDocument</code>
 */

function dDocument (doc) {
    this.parent = dElement;
    this.parent(doc);
    delete this.parent;

    return this;
}
dDocument.prototype = new dElement;

dDocument.prototype.getW = function () {
    if (is.moz || is.op) return window.innerWidth;
    else if (is.ie) return ((is.ie6 && !is.quirk)?document.documentElement.offsetWidth-16: document.body.clientWidth);
}

dDocument.prototype.getH = function () {
    if (is.moz || is.op) return window.innerHeight;
    else if (is.ie) return (is.ie6 && !is.quirk)?document.documentElement.offsetHeight-16:document.body.clientHeight;
}

dDocument.prototype.getScrollLeft = function () {
	if (is.moz || is.op) return window.pageXOffset;
    else if (is.ie) return (is.ie6 && !is.quirk)?document.documentElement.scrollLeft:document.body.scrollLeft;
}

dDocument.prototype.getScrollTop = function () {
    if (is.moz || is.op) return window.pageYOffset;
    else if (is.ie) return (is.ie6 && !is.quirk)?document.documentElement.scrollTop:document.body.scrollTop;
}

/**
 *	<code>dEvent</code>
 */


function dEvent (e) {
    this.e = window.event || e;
	this.e.dEvent = this;
    this.type = this.e.type;
    this.src = api.getContainer(this.e.target || this.e.srcElement);
    this.x = this.e.clientX + api.doc.getScrollLeft();
    this.y = this.e.clientY + api.doc.getScrollTop();
	this.offsetX = this.e.offsetX || this.e.layerX;
    this.offsetY = this.e.offsetY || this.e.layerY;
	if (typeof(this.offsetX) == "undefined") this.offsetX = 0;
	if (typeof(this.offsetY) == "undefined") this.offsetY = 0;
	
	return this;
}

util = {
	hideSelects : function () {
		var s = api.getElementsByTagName("select");
		for (var i = (s.length-1); i >= 0; i--) s.hide();
	},
	showSelects : function () {
		var s = api.getElementsByTagName("select");
		for (var i = (s.length-1); i >= 0; i--) s.show();
	}	
}

debug = {
	getProperties : function (obj) {
		var arr = [];
		for (var i in obj) arr[i] = obj;
		return arr;
	},
	writeToConsole : function (obj) {
		var console = api.getElementById("console");
		if (!console) {
			var cns = new dElement();
			cns.elm["id"] = "console";
			api.doc.appendChild(cns);
		}
		switch (typeof(obj)) {
			case "object" :
				var props = debug.getProperties(obj);
				for(var i in props) {
					var div = new dElement("div");
					div.setHTML(i + " : " + props[i]);
					console.appendChild(div);
				}
				break;
			default : 
				var div = new dElement("div");
				div.setHTML(obj);
				console.appendChild(div);
		}
	}
}


function Command (arg) {
	this.func = function () {};
	return this;
}

Command.prototype.execute = function (invoker) {
	this.func(invoker);
}

function MacroCommand (arg) {
	this.cmds = [];
}

MacroCommand.prototype.execute = function (invoker) {
	for (var i = 0; i < this.cmds; i++) {
		this.cmds.execute(invoker);
	}
}

MacroCommand.prototype.addCommand = function (arg) {
	this.cmds.push(arg.command);
}

function NavigateToCommand (arg) {
	this.func = function (invoker) { document.location = arg.href}
}
NavigateToCommand.prototype = new Command;

function ForwardToCommand (arg) {
	this.func = function () { document.location.replace(arg.href) }
}
ForwardToCommand.prototype = new Command;


