if (typeof(console) == "undefined")
{
	var console =
	{
		log: function()
		{
			window.util.setStatus(arguments[0], 6000);
		},
		info: function()
		{
			window.util.setStatus(arguments[0], 6000);
		},
		group: function(){},
		groupEnd: function(){}
	};
}

/**
 * navigator.userAgent
 * @type String
 */
var agt = navigator.userAgent.toLowerCase();
/**
 * @type Boolean
 */
window.is_opera  = (agt.indexOf("opera") != -1);
/**
 * @type Boolean
 */
window.is_safari = (agt.indexOf('safari') != -1);
/**
 * @type Boolean
 */
window.is_ie  = !window.is_opera && agt.indexOf("msie") != -1;
/**
 * @type Boolean
 */
window.is_ie7 = window.is_ie && agt.indexOf("msie 7") != -1;
/**
 * @type Boolean
 */
window.is_ie6 = window.is_ie && !window.is_ie7 && agt.indexOf("msie 6") != -1;

/**
 * @type Boolean
 */
window.is_moz = (
	(agt.indexOf('mozilla/5')  != -1) && (agt.indexOf('spoofer') == -1) &&
	(agt.indexOf('compatible') == -1) && (agt.indexOf('opera')   == -1) &&
	(agt.indexOf('webtv')      == -1) && (agt.indexOf('hotjava') == -1));


/**
 * Dispatches custom events to event listeners.
 * All arguments to the constructor will be interpreted as event names
 * and these events will be created.
 * @constructor
 */
function Dispatcher()
{
	/**
	 * Object's defined event types
	 */
	this.events = new Array;
	/**
	 * Object listeners, by event type
	 */
	this.listeners = new Object;

	if (arguments.length != 0)
		for (var i = 0; i < arguments.length; i++)
			this.addEvent(arguments[i]);
}
/**
 * Adds an event type.
 * @param {String} eventType The type of the event.
 */
Dispatcher.prototype.addEvent = function (eventType)
{
	if (!this.hasEvent(eventType))
	{
		this.events.push(eventType);
		this.listeners[eventType] = new Array;
	}
}
/**
 * Returns true if the supplied eventType already exists.
 * @param {String} The type of the event.
 * @return {Boolean} True us the specified event type exists.
 */
Dispatcher.prototype.hasEvent = function (eventType)
{
	for (var i = 0; i < this.events.length; i++)
		if (this.events[i] == eventType)
			return true;

	return false;
}
/**
 * Adds an event listener for the specified event type.
 * The listener can be either an object whose method should be called
 * or just a function not related to an object. To use the second method,
 * pass a null value as the object argument.
 * @param {String} eventType The type of the event.
 * @param {String} object The listener object.
 * @param {String} method The listener method.
 */
Dispatcher.prototype.addListener = function (eventType, object, method)
{
	if (this.hasEvent(eventType) && !this.hasListener(eventType, object, method))
	{
		var listener = { object: object, method: method };
		this.listeners[eventType].push(listener);
	}
}
/**
 * Removes the specified event listener.
 * @param String eventType The event for which the listener should be removed.
 * @param Object The listener object
 * @param String The name of the object's method that handles the event.
 */
Dispatcher.prototype.removeListener = function (eventType, object, method)
{
	if (this.hasEvent(eventType))
	{
		for (var i = 0; i < this.listeners[eventType].length; i++)
		{
			if (method)
			{
				if (this.listeners[eventType][i].object == object && this.listeners[eventType][i].method == method)
				{
					this.listeners[eventType].splice(i, 1);
					return;
				}
			}
			else
			{
				if (this.listeners[eventType][i].object == object)
				{
					this.listeners[eventType].splice(i, 1);
					return;
				}
			}
		}
	}
}
/**
 * Returns true if the supplied eventType event listener exists.
 * @param {String} eventType The type of the event.
 * @param {String} object The listener object.
 * @param {String} method The listener method.
 * @return {Boolean} True us the specified listener exists.
 */
Dispatcher.prototype.hasListener = function (event, object, method)
{
	if (this.hasEvent(event))
	{
		for (var i = 0; i < this.listeners[event].length; i++)
		{
			if (method)
			{
				if (this.listeners[event][i].object == object && this.listeners[event][i].method == method)
					return true;
			}
			else
			{
				if (this.listeners[event][i].object == object)
					return true;
			}
		}
	}
	return false;
}
/**
 * Fires the event of the supplied event type.
 * @param {String} eventType The type of the event.
 */
Dispatcher.prototype.fireEvent = function (eventType, eventData)
{
	if (this.listeners[eventType] != null)
	{
		var eventObj = this.createEventObject(eventType, eventData);
		for (var i = 0; i < this.listeners[eventType].length; i++)
		{
			var listener = this.listeners[eventType][i];

			if (listener.object && listener.method)
				eventObj = listener.object[listener.method](eventObj);
			else if (listener.object)
				eventObj = listener.object(eventObj);
			else if (listener.method)
				eventObj = listener.method(eventObj);
		}
		return eventObj;
	}
	return null;
}
/**
 * Returns the event object for the fired event.
 * @param {String} eventType The type of the event.
 */
Dispatcher.prototype.createEventObject = function (eventType, eventData)
{
	var eventObj = { event: eventType, source: this };
	if (eventData != null)
		for (var prop in eventData)
			eventObj[prop] = eventData[prop];

	return eventObj;
}

/**
 * Represents an event dispatcher that depends on the presence of dom objects.
 * @constructor
 * @extends Dispatcher
 * @class Class from which all other exceptions should be extended.
 * <p>This superclass provides shared functionality for referencing
 * a specified list of dom elements. To work with this class, use the inheriting
 * function's name as the argument to the prototype constructor:<br/>
 * <code>CClass.prototype = new DomDependentObject(CClass)</code>, where <code>CClass</code>
 * is the name of the inheriting (child) class.</p>
 * <p> The list of dom elements that the child class uses should be specified as property
 * of the function itself as a static property of the constructor: <code>CClass.DomElements</code>,
 * where <code>CClass</code> is the name of the inheriting function, and <code>DomElements</code> is an object
 * where each property name is the id of a required dom element and property value
 * is a 2 element array where the first element is the tag name of the
 * required dom element, and the second a short description of this required element.
 * @param {Object} r_constructor Reference to the inheriting function.
 */
function DomDependentDispatcher(r_constructor)
{
	this.r_constructor = r_constructor;
}
DomDependentDispatcher.prototype = new Dispatcher;

/**
 * Creates references to the elements specified with DomElements property
 */
DomDependentDispatcher.prototype.initializeElements = function ()
{
	this.elementsMissing = new Array;
	for (elementID in this.r_constructor.DomElements)
	{
		var elementObj = document.getElementById(elementID);
		var elementSpec = this.r_constructor.DomElements[elementID];
		if (elementObj != null)
			this.r_constructor.DomElements[elementID] = elementObj;
		else
		{
			this.r_constructor.DomElements[elementID] = null;
			this.elementsMissing.push(elementID + ": " +
				(elementSpec.join ? elementSpec.join(" - ") : elementSpec));
		}
	}
	return this.elementsMissing.length;
}

/**
 * Removes references to the elements specified with DomElements property
 */
DomDependentDispatcher.prototype.cleanup = function ()
{
	for (elementID in this.r_constructor.DomElements)
	{
		this.r_constructor.DomElements[elementID] = null;
		delete this.r_constructor.DomElements[elementID];
	}
}

/**
 * Shows an alert with the list of dom elements that were not
 * found in the current document.
 */
DomDependentDispatcher.prototype.showMissingList = function ()
{
	alert("Current document is missing some required elements:\n\n" +
		this.elementsMissing.join("\n"));
}

/**
 * Returns a DOM element associated with the supplied ID
 */
DomDependentDispatcher.prototype.controls = function (controlID)
{
	return this.r_constructor.DomElements[controlID];
}

/**
 * Creates a function that can be used for callbacks.
 * @param {Object} object The object whose method should be called
 * @param {String} method The name of the object method to call
 * @param {Array} fxargs Optional array of arguments that should be passed to the method\
 * @return {Function} Function that will call the supplied method of the supplied object
 */
window.createCallback = function (object, method, fxargs)
{
	if (window.__callbacks == null)
		window.__callbacks = [];

	for (var i = 0; i < window.__callbacks.length; i++)
	{
		var cb = window.__callbacks[i];
		var fx = true;
		if (cb.fxargs && fxargs)
			for (var j = 0; j < cb.fxargs.length; j++)
				if (cb.fxargs[j] != fxargs[j])
					fx = false;

		if (cb.object == object && cb.method == method && fx == true)
			return cb;
	}

	var callbackFx = function () /**/
	{
		var object = arguments.callee.object;
		var method = arguments.callee.method;
		var fxargs = arguments.callee.fxargs;

		if (fxargs)
			return object[method].apply(object, fxargs);
		else if (arguments.length)
			return object[method].apply(object, arguments);
		else
			return object[method]();
	}
	callbackFx.object = object;
	callbackFx.method = method;
	callbackFx.fxargs = fxargs;

	window.__callbacks.push(callbackFx);

	return callbackFx;
}

window.cleanupCallbacks = function ()
{
	while (window.__callbacks && window.__callbacks.length)
	{
		var cbk = window.__callbacks.shift();
		cbk.object = null;
		cbk.method = null;
		cbk.fxargs = null;
		cbk = null;
	}
}

/**
 * Provides utility methods for working with xml
 * @type Object
 */
var xml = new Object;
xml.createHttpRequest = function (url, async, onload, onerror)
{
	var request;
	try
	{
		var request = window.XMLHttpRequest
			? new XMLHttpRequest()
			: new ActiveXObject("MSXML2.XMLHTTP");
	}
	catch (ex)
	{
		throw new Error("Your browser does not support XmlHttp objects");
	}

	if (onload)
	{
		request.onreadystatechange = function () /**/
		{
			if (request.readyState == 4)
			{
				if (request.status == 0 || request.status == 200)
					onload(request);
				else if (onerror)
					onerror(request);
				else
					util.setStatus("Error loading xml request: " + request.status);
			}
		}
	}

	if (url)
	{
		request.open("GET", url, async);
		request.send(null);
	}

	return request;
}
xml.createDocument = function ()
{
	try
	{
		// DOM2
		if (document.implementation && document.implementation.createDocument)
		{
			var doc = document.implementation.createDocument("", "", null);

			if (doc.readyState == null)
			{
				doc.readyState = 1;
				doc.addEventListener("load", function () /**/
				{
					doc.readyState = 4;
					if (typeof doc.onreadystatechange == "function")
						doc.onreadystatechange();

				}, false);
			}
			return doc;
		}
		if (window.ActiveXObject)
			return new ActiveXObject("MSXML2.DomDocument");
	}
	catch (ex)
	{
		throw new Error("Your browser does not support XmlDocument objects");
	}
}
xml.serializeNode = function (xmlNode)
{
	if (xmlNode == null)
		return null;

	if (window.is_moz)
		return (new XMLSerializer()).serializeToString(xmlNode);
	else if (window.is_ie)
		return xmlNode.xml;
	else
		return xml.__serializeNode(xmlNode);
}
xml.__serializeNode = function (xmlNode)
{
	switch (xmlNode.nodeType)
	{
		case 1: return xml.__serializeElem(xmlNode);
		case 2: return xml.__serializeAttr(xmlNode);
		case 3: return xml.__serializeText(xmlNode);
		case 4: return xml.__serializeData(xmlNode);
		case 9: return xml.__serializeDocu(xmlNode);
		default: return [];
	}
}
xml.__serializeDocu = function (xmlNode)
{
	return xml.__serializeElem(xmlNode.documentElement);
}
xml.__serializeElem = function (xmlNode)
{
	var result = new Array;
	result.push("<");
	result.push(xmlNode.nodeName);
	for (var i = 0; i < xmlNode.attributes.length; i++)
		result.push(" " + xml.__serializeAttr(xmlNode.attributes[i]));

	if (xmlNode.childNodes.length != 0)
	{
		result.push(">");

		for (var i = 0; i < xmlNode.childNodes.length; i++)
			result.push(xml.__serializeNode(xmlNode.childNodes[i]));

		result.push("</");
		result.push(xmlNode.nodeName);
		result.push(">");
	}
	else
	{
		result.push(" />");
	}
	return result.join("");
}
xml.__serializeAttr = function (xmlNode)
{
	return xmlNode.nodeName + "=\"" + xmlNode.nodeValue + "\"";
}
xml.__serializeText = function (xmlNode)
{
	return xmlNode.text;
}
xml.__serializeData = function (xmlNode)
{
	return xmlNode.data;
}

window.util = new Object;
/**
 * Copies all prototype members from and applies one or more functions to the elementObj.
 * arguments[1] to arguments[arguments.length-1] should be functions to copy from and apply.
 * @param {Object} elementObj The object to apply the interface to
 * @arguments function0,...,functionN The functions toarray.
 * @return {Object} The supplied elementObj argument.
 */
window.util.applyPrototypes = function (elementObj)
{
	var eventRxp = new RegExp("^on(?:" +
		"activate|beforedeactivate|beforeeditfocus|blur|change|click|contextmenu|dblclick|" +
		"deactivate|error|focus|keydown|keypress|keyup|load|mousedown|mousemove|mouseout|" +
		"mouseover|mouseup|mousewheel|move|resize|scroll|select|selectionchange|submit|unload)$", "i");

	if (elementObj != null)
	{
		for (var i = 1; i < arguments.length; i++)
		{
			var r_function = arguments[i];
			for (var memberName in r_function.prototype)
			{
				if (r_function.prototype[memberName] instanceof Function && memberName.match(eventRxp))
					evt.addHandler(elementObj, memberName, r_function.prototype[memberName]);

				else
					elementObj[memberName] = r_function.prototype[memberName];
			}

			r_function.apply(elementObj); /**/
		}
	}
	return elementObj;
}

window.util.argsToList = function ()
{
	var elems = arguments;
	if (arguments.length > 0)
	{
		if (arguments.length == 1 && isArray(arguments[0]))
			elems = arguments[0];
	}
	else
		elems = [];

	return elems;
}


window.util.makeUnselectable = function (element, recursive)
{
	if (element == null)
		return;

	if (window.is_ie)
		element.setAttribute("unselectable", "on");
	else if (!element.onmousedown)
		element.onmousedown = evt.preventDefault;

	if (recursive == true)
	{
		var childNodes = element.getElementsByTagName("*");
		for (var i = 0; i < childNodes.length; i++)
		{
			if (window.is_ie)
				childNodes[i].setAttribute("unselectable", "on");
			else if (!childNodes[i].onmousedown)
				childNodes[i].onmousedown = evt.preventDefault;
		}
	}
}
window.util.makeSelectable = function (element, recursive)
{
	if (!window.is_moz)
		element.removeAttribute("unselectable");

	if (window.is_moz)
	{
		var currElem = element;
		while (currElem && currElem.tagName)
		{
			if (currElem.onmousedown == evt.preventDefault)
				currElem.onmousedown = null;
			currElem = currElem.parentNode;
		}
	}
	if (recursive == true)
	{
		var childNodes = element.getElementsByTagName("*");
		for (var i = 0; i < childNodes.length; i++)
		{
			if (!window.is_moz)
				childNodes[i].removeAttribute("unselectable");
			if (window.is_moz && childNodes[i].onmousedown == evt.preventDefault)
				childNodes[i].onmousedown = null;
		}
	}
}

window.util.toggle = function (elementObj)
{
	if (elementObj == null || elementObj.style == null)
		return;

	if (elementObj.offsetHeight)
		css.undisplay(elementObj);
	else
		css.display(elementObj);
}

window.util.querystring = function (url)
{
	this.address = null;
	this.pagepath = null;
	this.pagehash = null;
	this.param = {};

	this.getItem = window.util.querystring.getItem;
	this.parse = window.util.querystring.parse;
	this.toString = window.util.querystring.toString;

	if (url != null)
		this.parse(url);
	else
		this.parse(location.href);
}
window.util.querystring.getItem = function (itemKey)
{
	return this.param[itemKey];
}
window.util.querystring.parse = function (url)
{
	if (url == null)
		return;

	this.url = new String(url);
	if (this.url.match(/^(((https?|ftp):\/\/([a-z-A-Z0-9.]+))(\/?[^\?]*)?)?(?:\?([^#]+))?(?:#(.*))?$/))
	{
		var m1 = RegExp.$1;
		var m2 = RegExp.$2;
		var m3 = RegExp.$3;
		var m4 = RegExp.$4;
		var m5 = RegExp.$5;
		var m6 = RegExp.$6;
		var m7 = RegExp.$7;

		this.scriptnamefull = m1;
		this.servernamefull = m2;
		this.protocol = m3;
		this.servername = m4;
		this.scriptname = m5;
		this.query = m6;
		this.hash = m7;
	}
	else
	{
		this.scriptnamefull = "";
		this.servernamefull = "";
		this.protocol = "";
		this.servername = "";
		this.scriptname = "";
		this.query = this.url;
		this.hash = "";
	}

	var param = this.query.split("&");
	for (var i = 0; i < param.length; i++)
	{
		if (param[i].length == 0)
			continue;

		var pair = param[i].split("=");
		var itemKey = pair[0];
		var itemValue = pair[1];

		if (this.param[itemKey] != null)
		{
			if (!isArray(this.param[itemKey]))
				this.param[itemKey] = [this.param[itemKey]];

			this.param[itemKey].push(itemValue);
		}
		else
			this.param[itemKey] = itemValue;
	}
}
window.util.querystring.toString = function (full)
{
	var param = new Array();
	for (var name in this.param)
	{
		if (this.param[name] && this.param[name].constructor == Array)
		{
			for (var i = 0; i < this.param[name].length; i++)
				param.push(escape(name) + "=" + escape(this.param[name][i]));
		}
		else
		{
			var itemValue = String(this.param[name]).match(/\{.*\}/) ? this.param[name] : escape(this.param[name]);
			param.push(escape(name) + "=" + itemValue);
		}
	}

	var query = param.join("&");
	if (full == true)
		var result = (param.length ? this.scriptnamefull + "?" + query : this.scriptnamefull) + this.hash;
	else
		var result = query;

	return result;
}

window.util.getEvent = function (e)
{
	if (e != null)
		return e;
	else
		return window.event;
}


window.util.setStatus = function (message, timeout)
{
	var statusObj = document.getElementById("status");

	if (statusObj)
		statusObj.innerText = message;
	else
		window.status = message;

	if (window._t1 != null)
		window.clearTimeout(window._t1);

	if (timeout)
		window._t1 = window.setTimeout(util.clearStatus, timeout);
}

window.util.clearStatus = function ()
{
	var statusObj = document.getElementById("status");

	if (statusObj)
		statusObj.innerText = "";
	else
		window.status = "";
}

window.util.trapError = function (errorText, errorFile, errorLine)
{
	var fileLocation = String(errorFile).replace(/.*\//, "");
	var errorMessage = errorText + " (" + "line " + errorLine + " in " + fileLocation + ")";
	util.setStatus(errorMessage, 10000);
	window.returnValue = true;
	return true;
}

window.log = new Object;
window.log.group = function (groupTitle)
{
	console.group(groupTitle);
}
window.log.groupEnd = function ()
{
	console.groupEnd();
}
window.log.message = function (messageText)
{
	if (arguments.length > 1)
	{
		var formats = new Array;
		if (isArray(arguments[1]))
			formats = arguments[1];
		else
		{
			for (var i = 1; i < arguments.length; i++)
				formats.push(arguments[i]);

			messageText = messageText.format(formats);
		}
	}
	if (window.console != null)
		console.log(messageText);
	else
		window.status = messageText;
}
window.log.error = function (messageText)
{
	if (arguments.length > 1)
	{
		var formats = new Array;
		if (isArray(arguments[1]))
			formats = arguments[1];
		else
		{
			for (var i = 1; i < arguments.length; i++)
				formats.push(arguments[i]);

			messageText = messageText.format(formats);
		}
	}
	if (window.console != null)
		console.log("Error: " + messageText);
	else
		window.status = "Error: " + messageText;
}

window.css = new Object;
window.css.containsClassName = function (elementObj)
{
	if (elementObj == null || elementObj.className == null || arguments.length < 2)
		return null;

	var contains = true;
	for (var i = 1; i < arguments.length; i++)
	{
		var rxp = new RegExp("(?:^|\\s)" + arguments[i] + "(?:$|\\s)");
		if (!elementObj.className.match(rxp))
			contains = false;

		if (!contains)
			break;
	}
	return contains;
}

window.css.addClassName = function (elementObj, className)
{
	if (elementObj == null || elementObj.className == null)
		return;
	else
	{
		if (!css.containsClassName(elementObj, className))
			elementObj.className += " " + className;
	}
}

window.css.removeClassName = function (elementObj, className)
{
	if (elementObj == null || elementObj.className == null)
		return;
	else
	{
		var rxp = new RegExp("(^|\\s)" + className + "($|\\s)", "g");
		elementObj.className = elementObj.className.replace(rxp, "$1$2");
	}
}

window.css.replaceClassName = function (elementObj, oldClass, newClass)
{
	css.removeClassName(elementObj, oldClass);
	css.addClassName(elementObj, newClass);
}

window.css.extractCss = function (selectorText)
{
	for (var i = 0; i < document.styleSheets.length; i++)
	{
		var rules = document.styleSheets[i].rules || document.styleSheets[i].cssRules;
		for (var j = 0; j < rules.length; j++)
			if (rules[j].selectorText && rules[j].selectorText.match(selectorText))
				return rules[j].style.cssText;
	}
	return "";
}

window.css.display = function (elem)
{
	var elems = util.argsToList.apply(this, arguments);

	for (var i = 0; i < elems.length; i++)
	{
		var elem = elems[i];
		if (elem == null || elem.style == null)
			continue;

		var displayValue;
		if (window.is_ie)
			displayValue = elem.tagName == "SPAN" ? "inline" : "block";
		else
		{
			var tagName = elem.tagName.toLowerCase();
			if (tagName.match(/^(a|b|code|em|i|img|input|label|span|textarea)$/))
				displayValue = "inline";
			else if (tagName == "tr")
				displayValue = "table-row";
			else if (tagName == "td")
				displayValue = "table-cell";
			else if (tagName == "table")
				displayValue = "table";
			else
				displayValue = "block";
		}
		elem.style.display = displayValue;
	}
}

window.css.undisplay = function (elementObj)
{
	var elems = util.argsToList.apply(this, arguments);

	for (var i = 0; i < elems.length; i++)
	{
		var elem = elems[i];
		if (elem == null || elem.style == null)
			continue;

		elem.style.display = "none";
	}
}

window.css.show = function (elementObj, inherit)
{
	if (elementObj == null || elementObj.style == null)
		return;

	elementObj.style.visibility = inherit == true ? "inherit" : "visible";
}

window.css.hide = function (elementObj)
{
	if (elementObj == null || elementObj.style == null)
		return;

	elementObj.style.visibility = "hidden";
}


window.css.pixelLeft = function (elem)
{
	if (elem == null || elem.style == null)
		return null;

	return parseInt(elem.style.left);
}

window.css.pixelTop = function (elem)
{
	if (elem == null || elem.style == null)
		return null;

	return parseInt(elem.style.top);
}

window.css.pixelWidth = function (elem)
{
	if (elem == null || elem.style == null)
		return null;

	return parseInt(elem.style.width);
}

window.css.pixelHeight = function (elem)
{
	if (elem == null || elem.style == null)
		return null;

	return parseInt(elem.style.height);
}

window.css.setPixelCoord = function (elem, top, left, bottom, right)
{
	if (elem == null || elem.style == null)
		return null;

	elem.style.top = top + "px";
	elem.style.left = left + "px";
	if (bottom != undefined)
		elem.style.bottom = bottom + "px";
	if (right != undefined)
		elem.style.right = right + "px";
}

window.css.setPixelLeft = function (elem, value)
{
	if (elem == null || elem.style == null)
		return null;

	elem.style.left = value + "px";
}

window.css.setPixelRight = function (elem, value)
{
	if (elem == null || elem.style == null)
		return null;

	elem.style.right = value + "px";
}

window.css.setPixelTop = function (elem, value)
{
	if (elem == null || elem.style == null)
		return null;

	elem.style.top = value + "px";
}

window.css.setPixelBottom = function (elem, value)
{
	if (elem == null || elem.style == null)
		return null;

	elem.style.bottom = value + "px";
}

window.css.setPixelWidth = function (elem, value)
{
	if (elem == null || elem.style == null)
		return null;

	elem.style.width = value + "px";
	return value;
}

window.css.setPixelHeight = function (elem, value)
{
	if (elem == null || elem.style == null)
		return null;

	elem.style.height = value + "px";
	return value;
}


window.dom = new Object;
window.dom.html = function ()
{
	return document.getElementsByTagName("html")[0];
}

window.dom.getLeft = function (elem)
{
	var node = elem;
	var left = 0;

	while (node && node.nodeName)
	{
		var bl = parseInt(dom.currentStyleProp(node, "borderLeftWidth")) || 0;
		var br = parseInt(dom.currentStyleProp(node, "borderRightWidth")) || 0;

		left += node.offsetLeft - node.scrollLeft;
		left += bl;
		if (node != elem)
			left += br;

		node = node.offsetParent;
	}
	return left;
}

window.dom.getTop = function (elem)
{
	var node = elem;
	var top = 0;

	while (node && node.nodeName)
	{
		var bt = parseInt(dom.currentStyleProp(node, "borderTopWidth")) || 0;
		var bt = parseInt(dom.currentStyleProp(node, "borderBottomWidth")) || 0;

		top += node.offsetTop - node.scrollTop;
		top += bt
		if (node != elem)
			top + bt;

		node = node.offsetParent;
	}
	return top;
}


window.dom.getElementsByTagNames = function (parentElement)
{
	var elems = [];
	if (parentElement == null || arguments.length < 2)
		return elems;

	for (var i = 1; i < arguments.length; i++)
	{
		var tags = parentElement.getElementsByTagName(arguments[i]);
		for (var j = 0; j < tags.length; j++)
			elems.push(tags[j]);
	}
	return elems;
}

window.dom.getElementsByClassName = function (parentElement, className, tagName)
{
	var elems = [];
	if (className == null || className == "")
		return elems;

	var parentObj = parentElement ? parentElement : document;

	var elemList = tagName ? parentObj.getElementsByTagName(tagName) : parentObj.getElementsByTagName("*");
	for (var i = 0; i < elemList.length; i++)
	{
		if (css.containsClassName(elemList[i], className))
			elems.push(elemList[i]);
	}
	return elems;
}

window.dom.getParentByTagName = function (refObject, tagName)
{
	if (refObject == null || tagName == null)
		return null;

	tagName = String(tagName).toUpperCase();
	var objParent = refObject;
	while(objParent = objParent.parentNode)
	{
		if (objParent.tagName == tagName)
			return objParent;
	}
	return null;
}

window.dom.getParentByClassName = function (refObject, className)
{
	if (refObject == null || className == null)
		return null;

	var objParent = refObject;
	while(objParent = objParent.parentNode)
	{
		if (css.containsClassName(objParent, "className"))
			return objParent;
	}
	return null;
}

window.dom.innerText = function (elementObj)
{
	if (elementObj == null)
		return null;

	if (window.is_ie)
		return elementObj.innerText;
	else
		return elementObj.textContent;
}

window.dom.setInnerText = function (elementObj, text)
{
	if (elementObj == null)
		return;

	if (window.is_ie)
		elementObj.innerText = text;
	else
		elementObj.textContent = text;
}

window.dom.currentStyle = function (elementObj)
{
	if (elementObj == null)
		return null;

	if (window.is_ie)
		return elementObj.currentStyle;
	else
		return getComputedStyle(elementObj, null);
}

window.dom.currentStyleProp = function (elementObj, propName)
{
	if (elementObj == null)
		return null;

	var cs = dom.currentStyle(elementObj);
	if (cs != null)
		return cs[propName];
	else
		return null;
}

window.dom.elementContains = function (parent, child)
{
	if (parent == null || child == null)
		return false;
	else
	{
		if (window.is_ie)
			return parent.contains(child);
		else
			return (parent == child ||
				dom.elementContains(parent, child.parentNode));
	}
}

window.dom.swapNode = function (newNode, oldNode)
{
	var tmpNode = newNode.cloneNode(1);
	var parent = newNode.parentNode;

	oldNode = parent.replaceChild(tmpNode, oldNode);
	parent.replaceChild(oldNode, newNode);
	parent.replaceChild(newNode, tmpNode);
	tmpNode = null;
}

window.dom.isOffScreen = function (elem)
{
	if (elem == null)
		return false;
	if (elem.offsetHeight == 0)
		return false;

	var x1 = dom.getLeft(elem);
	var y1 = dom.getTop(elem);
	var x2 = x1 + elem.offsetWidth;
	var y2 = y1 + elem.offsetHeight;

	var html = document.getElementsByTagName("html")[0];
	var body = document.body;
	var minx = (html.scrollLeft || body.scrollLeft);
	var maxx = minx + (html.offsetWidth || body.offsetWidth);
	var miny = (html.scrollTop || body.scrollTop);
	var maxy = miny + (html.offsetHeight || body.offsetHeight);

	if (x1 < minx || x2 > maxx || y1 < miny || y2 > maxy)
		return true;
	else
		return false;
}

window.evt = new Object;
window.evt.srcElement = function (evt)
{
	var event = util.getEvent(evt);
	if (event == null)
		return null;

	if (window.is_ie)
		return event.srcElement;
	else
	{
		if (event.target.nodeType == 1)
			return event.target;
		else
			return event.target.parentNode;
	}
}

window.evt.addListener = function (target, eventName, eventHandler, useCapture)
{
	if (target == null || eventName == null || eventHandler == null)
		return;
	if (useCapture != true && useCapture != false)
		useCapture = false;

	var ename1 = eventName.replace(/^on/, "");
	var ename2 = "on" + ename1;

	if (window.is_ie)
	{
		evt.addHandler(target, eventName, eventHandler);
	}
	else
	{
		target.addEventListener(ename1, eventHandler, useCapture);
	}
}

window.evt.addHandler = function (target, eventName, eventHandler, insertBefore)
{
	if (target == null)
		return;

	var elems = util.argsToList.call(this, target);
	for (var i = 0; i < elems.length; i++)
	{
		target = elems[i];

		if (target == window && eventName == "onready")
		{
			var enabled = evt.setup_onready();
			if (!enabled)
				eventName = "onload";
		}

		var handlerArrayName = "handlers__" + eventName;
		if (target[handlerArrayName] == null)
		{
			target[handlerArrayName] = new Array;
			if (target[eventName] != null)
				target[handlerArrayName].push(target[eventName]);
		}

		var handlerExists = false;
		for (var i = 0; i < target[handlerArrayName].length; i++)
			if (target[handlerArrayName][i] == eventHandler)
				handlerExists = true;

		// only assign an event handler once
		if (handlerExists == false)
		{
			if (insertBefore == true)
				target[handlerArrayName].unshift(eventHandler);
			else
				target[handlerArrayName].push(eventHandler);
		}

		target[eventName] = evt.onevent;
	}
}

window.evt.setup_onready = function ()
{
	var result = false;
	if (window.is_moz)
	{
		document.addEventListener("DOMContentLoaded", evt.onready, false);
		result = true;
	}
	/*@cc_on @*/
	/*@if (@_win32)
	document.write("<s" + "cript id=__ie_onload defer src=javascript:void(0)><\/s" + "cript>");
	var script = document.getElementById("__ie_onload");
	script.onreadystatechange = function()
	{
		if (this.readyState == "complete")
			evt.onready();
	};
	result = true;
	/*@end @*/
	return result;
}

window.evt.onready = function ()
{
	evt.fire_event(window, null, "ready");
}

window.evt.onevent = function (e)
{
	var eventObj = window.util.getEvent(e);
	if (eventObj)
		return evt.fire_event(this, eventObj);
}

window.evt.fire_event = function (target, eventObj, eventName)
{
	if (eventObj !=  null)
		eventName = eventObj.type;

	if (target != null && eventName != null)
	{
		var eventHandlers = "handlers__on" + eventName;

		var finalResult = null;
		var currentResult = null;

		for (var i = 0; i < target[eventHandlers].length; i++)
		{
			try
			{
				currentResult = target[eventHandlers][i].call(target, eventObj);
				if (currentResult != undefined)
					finalResult = currentResult;
			}
			catch(ex)
			{
				if (ex.number == -2147418113)
					continue;
				else
				{
					var debug = (target.tagName || target) + ".on" + eventName + ": " + ex.message;
					log.error(debug);
				}
			}
		}

		return finalResult;
	}
}

window.evt.removeListener = function (target, eventName, eventHandler, useCapture)
{
	if (target == null || eventName == null || eventHandler == null)
		return;
	if (useCapture != true && useCapture != false)
		useCapture = false;

	var ename1 = eventName.replace(/^on/, "");
	var ename2 = "on" + ename1;

	if (window.is_ie)
	{
		target.detachEvent(ename2, eventHandler);
		target.releaseCapture();
	}
	else
	{
		target.removeEventListener(ename1, eventHandler, true);
	}
}

window.evt.removeHandler = function (eventName, elementObj, eventHandler)
{
	var handlerArrayName = "handlers__" + eventName;
	if (elementObj[handlerArrayName] != null)
	{
		for (var i = 0; i < elementObj[handlerArrayName].length; i++)
			if (elementObj[handlerArrayName][i] == eventHandler)
				elementObj[handlerArrayName].splice(i, 1);
	}
}

window.evt.preventDefault = function (e)
{
	var event = util.getEvent(e);
	if (!window.is_ie)
		event.preventDefault();
}

window.evt.stopPropagation = function (e)
{
	var event = util.getEvent(e);
	if (window.is_ie)
		event.cancelBubble = true;
	else
		event.stopPropagation();
}

window.cookie = new Object;
/**
 * Sets/adds a cookie to the document.cookies collection
 * @param {String} name The name of the cookie. Required.
 * @param {String} value The value of the cookie. Required.
 * @param {String} expires An expression, signifying the expires property of the cookie. See <code>cookie.dateCalc</code>. Optional.
 * @param {String} domain The domain property of the cookie. Optional.
 * @param {Boolean} secure The secure property of the cookie. Optional.
 * @return {String} The value of the cookie that has just been set.
 */
window.cookie.set = function (name, value, expires, domain, path, secure)
{
	var kookie = name + "=" + escape(value);

	if (expires) kookie += "; expires=" + cookie.dateCalc(expires);
	if (domain)  kookie += "; domain="  + domain;
	if (path)    kookie += "; path="    + path;
	if (secure)  kookie += "; secure=true";

	document.cookie = kookie;

	return cookie.get(name);
}

/**
 * Returns a cookie with the specified name from the document.cookies collection, if present,
 * and a <code>null</code> if not.
 * @param {String} name The name of the cookie. Required.
 * @return {String|null} The value of the cookie, if found, and a <code>null</code> otherwise.
 */
window.cookie.get = function (name)
{
	var cookies = document.cookie.split("; ");
	for (var i = 0; i < cookies.length; i++)
	{
		var cookieName  = cookies[i].substring(0, cookies[i].indexOf("="));
		var cookieValue = cookies[i].substring(cookies[i].indexOf("=") + 1, cookies[i].length);
		if (cookieName == name)
		{
			if (cookieValue.indexOf("&") != -1)
			{
				var pairs  = strValue.split("&");
				var cookie = new Object();
				for (var i in pairs)
				{
					var arrTemp = pairs[i].split("=");
					cookie[arrTemp[0]] = arrTemp[1];
				}
				return cookie;
			}
			else
				return unescape(cookieValue);
		}
	}
	return null;
}

/**
 * Deletes a cookie with the specified name from the document.cookies collection, by setting its expires property
 * to the current date [expire now].
 * @param {String} name The name of the cookie. Required.
 */
window.cookie.remove = function (name)
{
	var dateObj = new Date();
	document.cookie = name + "=null; expires=" + dateObj.toGMTString();
}

window.cookie.dateCalc = function (offsetString)
{
	var dateObj = new Date();
	var multip = new Object();
	var offsetTime;
	multip['s'] = 1000*1;
	multip['m'] = 1000*60;
	multip['h'] = 1000*60*60;
	multip['d'] = 1000*60*60*24;
	multip['M'] = 1000*60*60*24*30;
	multip['y'] = 1000*60*60*24*365;

	if (!offsetString || (offsetString == '') || (offsetString.toLowerCase() == 'now'))
	{ // this will set the time calc to now
		offsetTime = 0;
	}
	else if ( matches = offsetString.match( /^([+-]?(\d+|\d*\.\d*))([mhdMy]?)/ ) )
	{ // perform calculation if offsetString matches specification
		offsetTime = multip[matches[3]] * matches[2];
	}
	else
	{ // otherwise assume that we're providing the date ourselves so just return original
		return offsetString;
	}
	dateObj.setTime(dateObj.getTime() + offsetTime);
	return (dateObj.toGMTString());
}


Number.random = function (randTop)
{
	var top = Math.abs(randTop);
	var random = Math.round((top) * Math.random());
	return random;
}

Date.today = function ()
{
	var now = new Date();
	var today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
	return today;
}

Date.parse = function (dateValue)
{
	if (dateValue == null)
		return null;

	else if (dateValue instanceof Date)
		return dateValue.getTime();

	else if (dateValue instanceof Array)
	{
		if (dateValue.length == 3)
		{
			var currentDate = new Date(dateValue[2], dateValue[1]-1, dateValue[0]);
			if (isNaN(currentDate))
				return null;
			else
				return currentDate.getTime();
		}
		else
			return null;
	}
	else
	{
		var dateString = new String(dateValue);
		if (dateString.length == 0)
			return null;

		var date = null;

		// xsl-short: YYYY-MM-DD or general date: YYYY/MM/DD
		if ((arrElem = dateString.match(/^(\d{4})(?:-|\/)(\d{1,2})(?:-|\/)(\d{1,2})$/)))
			date = new Date(arrElem[1], arrElem[2]-1, arrElem[3]);
		// xsl-long:  YYYY-MM-DDTHH:NN:SS
		else if ((arrElem = dateString.match(/^(\d{4})-(\d{1,2})-(\d{1,2})T(\d{1,2}):(\d{1,2}):(\d{1,2})/)))
			date = new Date(arrElem[1], arrElem[2]-1, arrElem[3], arrElem[4], arrElem[5], arrElem[6]);
		// js-string: www MMM dd hh:mm:ss ZON+dddd yyyy
		else if (dateString.match(/^\w{3}\s\w{2}\s\d{1,2}\s\d{2}:\d{2}:\d{2}\s\w{3}\+\d{4}\s\d{4}$/))
			date = new Date(dateString);
		// js-milliseconds date
		else if (dateString.match(/^-?\d+$/))
			date = new Date(parseInt(dateString));
		// lets generalize a bit: [dd-mm-yyyy] or [dd/mm/yyyy] or [dd.mm.yyyy]
		else if ((arrElem = dateString.match(/^(\d{1,2})[\.\/\-](\d{1,2})[\.\/\-](\d{4})$/)))
			date = new Date(arrElem[3], arrElem[2]-1, arrElem[1]);
		// more generalization:  [yyyy-mm-dd] or [yyyy.mm.dd] or [yyyy/mm/dd]
		else if ((arrElem = dateString.match(/^(\d{4})[\.\/\-](\d{1,2})[\.\/\-](\d{1,2})$/)))
			date = new Date(arrElem[1], arrElem[2]-1, arrElem[3]);
		// and some more: [dd-mm-yyyy hh:nn:ss] or [dd/mm/yyyy hh:nn:ss] or [dd.mm.yyyy hh:nn:ss]
		else if ((arrElem = dateString.match(/^(\d{1,2})[\.\/\-](\d{1,2})[\.\/\-](\d{4}) (\d{1,2}):(\d{1,2})(?::(\d{1,2}))?/)))
			date = new Date(arrElem[3], arrElem[2]-1, arrElem[1], arrElem[4], arrElem[5], arrElem[6] || 0);
		// and more:  [yyyy-mm-dd hh:nn:ss] or [yyyy.mm.dd hh:nn:ss] or [yyyy/mm/dd hh:nn:ss]
		else if ((arrElem = dateString.match(/^(\d{4})[\.\/\-](\d{1,2})[\.\/\-](\d{1,2}) (\d{1,2}):(\d{1,2})(?::(\d{1,2}))?$/)))
			date = new Date(arrElem[1], arrElem[2]-1, arrElem[3], arrElem[4], arrElem[5], arrElem[6] || 0);

		return date;
	}
}

Date.offset = function (dateInstance, timeFormat)
{
	var timeMultiplier =  { s: 1000, n: 1000*60, h: 1000*60*60, d: 1000*60*60*24, m: 1000*60*60*24*30, y: 1000*60*60*24*365 }
	var timeOffset = 0;

	if (timeFormat != undefined)
		if (String(timeFormat).match( /^([+-]?(?:\d+|\d*\.\d*))([mhdMy]?)$/ ))
			timeOffset = parseInt(timeMultiplier[RegExp.$2] * RegExp.$1);

	dateInstance.setTime(dateInstance.getTime() + timeOffset);
	return dateInstance;
}

Date.dayNames = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
Date.monthNames = ["January","February","March","April","May","June","July","August","September","October","November","December"];
Date.AM = "AM";
Date.PM = "PM";

Date.prototype.getWeekDay = function ()
{
	return Date.dayNames[this.getDay()];
}

Date.prototype.toString = function (format)
{
	if (isNaN(this))
		return "NaN";

	var f = {};
	f.s     = this.getSeconds();
	f.ss    = this.getSeconds().toString().padLeft(2, 0);
	f.h     = this.getHours();
	f.hh    = this.getHours().toString().padLeft(2, 0);
	f.n     = this.getMinutes();
	f.nn    = this.getMinutes().toString().padLeft(2, 0);
	f.d     = this.getDate();
	f.dd    = this.getDate().toString().padLeft(2, 0);
	f.www   = Date.dayNames[this.getDay()].substr(0, 3);
	f.wwww  = Date.dayNames[this.getDay()];
	f.m     = (this.getMonth() + 1);
	f.mm    = (this.getMonth() + 1).toString().padLeft(2, 0);
	f.mmm   = Date.monthNames[this.getMonth()].substr(0, 3);
	f.mmmm  = Date.monthNames[this.getMonth()];
	f.yyyy  = this.getFullYear();
	f.yy    = this.getFullYear().toString().substr(2, 4);
	f.y     = f.yy;

	f.date       = f.dd + "." + f.mm + "." + f.yyyy;
	f.time       = f.hh + ":" + f.nn + ":" + f.ss;
	f.dbdate     = f.yyyy + "/" + f.mm + "/" + f.dd;
	f.dbtime     = f.hh + ":" + f.nn + ":" + f.ss;
	f.dbdatetime = f.dbdate + " " + f.dbtime;
	f.xsl_short  = f.yyyy + "-" + f.mm + "-" + f.dd;
	f.xsl_long   = f.yyyy + "-" + f.mm + "-" + f.dd + "T" + f.hh + ":" + f.nn + ":" + f.ss;

	var offset = this.getTimezoneOffset();
	var offset_hours = Math.floor(Math.abs(offset) / 60);
	var offset_minutes = Math.abs(offset) - (offset_hours * 60);
	oh = offset_hours.toString().padLeft(2, 0);
	om = offset_minutes.toString().padLeft(2, 0);
	f.classic = [f.www, f.mmm, f.d, f.time, "UTC" + (offset > 0 ? "-" : "+") + oh + om, f.yyyy].join(" ");

	if (format)
	{
		var a = format.split(/\W/);
		for (var i in a)
		{
			if (f[a[i]])
			{
				format = format.replace(a[i], f[a[i]]);
			}
		}
	}
	else format = f.classic;
	return format;
}

Date.prototype.offset = function (timeFormat)
{
	return Date.offset(this, timeFormat);
}

Date.prototype.getUTCTime = function ()
{
	return this.getTime() + (this.getTimezoneOffset() * 60000)
}

Array.prototype.contains = function(value)
{
	for (var i = 0; i < this.length; i++)
	{
		if (this[i] == value)
			return true;
	}

	return false;
}

/**
 * Returns a new array with the supplied values removed from it
 * @param String value The values to remove (can be any number or arguments)
 * @return Array A new array with the value removed from it
 */
Array.prototype.remove = function()
{
	var list = new Array;
	var remove = false;
	for (var i = this.length - 1; i >= 0; i--)
	{
		remove = false;
		for (var j = 0; j < arguments.length; j++)
		{
			if (this[i] == arguments[j])
			{
				remove = true;
				break;
			}
		}
		if (remove == true)
			list.push(i);
	}
	for (var i = 0; i < list.length; i++)
	{
		this.splice(list[i], 1);
	}

	return this;
}

Function.prototype.getName = function (includeArguments, argumentsOnly)
{
	var functionBody = this.toString();

	functionBody = functionBody.replace(/^[\s\S]*?function[\s\n]+(([\w.$]*)[\s\n]*\((.*)\))\W[\s\S]*$/, function () /* rxp */
	{
		var fxName = arguments[2] ? arguments[2] : "anonymous";
		var functionDefinition = arguments[2] ? arguments[1] : [fxName, "(", arguments[3], ")"].join("");

		if (!includeArguments && !argumentsOnly)
			return fxName;

		if (includeArguments && !argumentsOnly)
			return functionDefinition;

		if (includeArguments && argumentsOnly)
			return arguments[3];
	});

	return functionBody;
}

Function.prototype.callStack = function (point, maxIterCount)
{
	var fxCaller = point || arguments.callee.caller;
	var callStack = new Array();
	var functions = new Array();

	var currentCount = 0;
	while (fxCaller != null && fxCaller.getName)
	{
		if (maxIterCount && currentCount >= maxIterCount)
			break;

		// unfortunatelly this script goes into an infinite loop
		// if there is a recursion going on in the function call stack :(
		else if (functions.contains(fxCaller)) /**/
			break;

		var fxName = fxCaller.getName();
		var fxArgs = new Array();
		var argValues = fxCaller.arguments;

		for (var i = 0; i < argValues.length; i++)
		{
			if (isAlien(argValues[i]))
				fxArgs.push("[foreign-object]");

			else if (isFunction(argValues[i])) /**/
				fxArgs.push("function: " + argValues[i].getName(true));

			else if (isArray(argValues[i]))
				fxArgs.push("array[" + argValues[i].length + "]");

			else if (isObject(argValues[i]))
				fxArgs.push("object{..}");

			else if (isNull(argValues[i]))
				fxArgs.push("null");

			else if (isString(argValues[i]))
				fxArgs.push((argValues[i].length < 200 ? "\"" + argValues[i] + "\"" : "[string(" + argValues[i].length + " chars)]"));

			else if (isUndefined(argValues[i]))
				fxArgs.push("undefined");

			else
				fxArgs.push(argValues[i]);
		}

		functions.push(fxCaller); /**/
		callStack.unshift(fxCaller.getName() + "(" + fxArgs.join(", ") + ")");
		fxCaller = fxCaller.caller;
		currentCount++;
	}
	callStack.toString = function (joinString) /**/
	{
		return this.join(joinString || "\n");
	}
	return callStack;
}

String.prototype.trim = function(expression)
{
	if (expression == null) expression = "\\s";
	var re = new RegExp("^[" + expression + "]*([\\s\\S]*?)[" + expression + "]*$");
	return this.replace(re, "$1");
}

String.prototype.format = function ()
{
	var arrArguments = (arguments.length == 1 && arguments[0] && isArray(arguments[0])) ? arguments[0] : arguments;

	function applyPadding(string, count, character, direction)
	{
		var string = '' + string;
		var diff = count - string.length;
		var output = new String();
		while (output.length < diff)
			output += character;

		return (direction == 2 ? string + output : output + string);
	}
	function applyFormat()
	{
		try
		{
			var strValue = new String(arrArguments[arguments[1]]);
		}
		catch(e) { strValue = ""; }

		if (arguments[2] != "" && arguments[3] != "")
		{
			var direction = arguments[4] == "+" ? 2 : 1;
			strValue = applyPadding(strValue, arguments[3], arguments[2] == "#" ? "0" : " ", direction);
		}
		return strValue;
	}
	return (this.replace(/\{(\d+)([$#])?(\d+)?([+-])?\}/g, applyFormat));
}
String.prototype.padLeft = function (length, padChar)
{
	if (padChar == undefined)
		padChar = " ";

	var string = this.toString();
	while(string.length < length)
		string = padChar + string;
	return string;
};
String.prototype.padRight = function (length, padChar)
{
	if (padChar == undefined)
		padChar = " ";

	var string = this.toString();
	while(string.length < length)
		string = string + padChar;
	return string;
};
function isArray(object)
{
	return isObject(object) && object.constructor == Array;
}
function isBoolean(object)
{
	return typeof object == 'boolean';
}
function isFunction(object)
{
	return typeof object == 'function';
}
function isAlien(object)
{
	return isObject(object) && typeof object.constructor != 'function';
}
function isNull(object)
{
	return typeof object == 'object' && !object;
}
function isNumber(object)
{
	return typeof object == 'number' && isFinite(object);
}
function isObject(object)
{
	return (object &&
		typeof object == 'object' &&
		object.constructor != String &&
		object.constructor != Number) || isFunction(object); /**/
}
function isString(object)
{
	return (typeof object == 'string') || (object != null && object.constructor == String);
}
function isUndefined(object)
{
	return typeof object == 'undefined';
}

evt.addHandler(window, "onunload", window.cleanupCallbacks);
