/*
 * The Javascript file contains utility methods useful for JavaScript
 * programming.
 *
 * Copyright 2005 by Invisible Sun, Inc.
 * http://www.invsun.com, mailto:foss@invsun.com
 *
 * This file is part of the Invisible Sun, Inc. Javascript library, hereafter
 * referred to as the library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

// Variables.

jsutil_createNamespace("invsun.core");

var DATEUTIL_MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
var DATEUTIL_DAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];

var _JSUTIL_USER_AGENT = null
var _JSUTIL_USER_AGENT_VERSION = null;
var _JSUTIL_USER_AGENT_PLATFORM = null;

var LOG_LEVEL = ["SYS", "ERR", "WRN", "INF", "DBG"];
var LOG_COLORS = ["black", "red", "orange", "blue", "green"];
var LOG_DEFAULT_COLOR = "black";
var LOG_WINDOW_NAME = "log_logWindow";
var LOG_WINDOW_FEATURES = "width=640,height=480,menubar=no,scrollbars=yes,status=no,toolbar=no";

var STRINGUTIL_LEFT_JUSTIFY = 0;
var STRINGUTIL_CENTER_JUSTIFY = 1;
var STRINGUTIL_RIGHT_JUSTIFY = 2;
var STRINGUTIL_DEFAULT_VALUE_SEPARATOR = "=";
var STRINGUTIL_DEFAULT_LIST_SEPARATOR = "; ";

var VALIDATE_HAS_VALID_CHARACTERS = 0;
var VALIDATE_IS_CHECKED = 1;
var VALIDATE_IS_CORRECT_LENGTH = 2;
var VALIDATE_IS_DEFINED = 3;
var VALIDATE_IS_IDENTICAL = 4;
var VALIDATE_IS_VALID_DATE = 5;
var VALIDATE_MATCHES_PATTERN = 6;
var VALIDATE_EMAIL_REGEX = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z]{2,4})+$/;
var VALIDATE_PASSWORD_REGEX = /^([a-zA-Z0-9_\.\-])+/;

var WEBAPP_QUERY_STRING_MESSAGE_TYPE = 0;
var WEBAPP_XML_MESSAGE_TYPE = 1;

// The Log class is a singleton, the single instance of Log is referenced via
// _LOG_INSTANCE.

var _LOG_INSTANCE = null;

// The Session class is a singleton, the single instance of Session is
// referenced via _SESSION_INSTANCE.

var _SESSION_INSTANCE = null;

// APIs and class definitions.

/*
 * The APIs with the prefix dateutil_ are utility methods useful for
 * manipulating Date objects.
 */

function dateutil_format(date, datePattern) {
	var result = "";
	if (!jsutil_isDefined(date) || !jsutil_isDefined(datePattern)) {
		return (date);
	}
	var sourceDate = jsutil_asArray(date);
	var sourceDateIndex = 0;
	for (var i = 0; i < datePattern.length; ++i) {
		var ch = datePattern.charAt(i);
		var size = stringutil_countRepeatingCharacters(datePattern, ch, i);
		var bufr = "";
		switch (ch) {
			case "G" :
				break;
			case "y" :
				bufr = stringutil_format(sourceDate[sourceDateIndex].getFullYear(), size == 2 ? 2 : 4, STRINGUTIL_RIGHT_JUSTIFY, "0");
				break;
			case "M" :
				bufr = (size > 2) ? stringutil_format(DATEUTIL_MONTHS[sourceDate[sourceDateIndex].getMonth()], size < 4 ? 3 : -1) : stringutil_format(sourceDate[sourceDateIndex].getMonth() + 1, size, STRINGUTIL_RIGHT_JUSTIFY, "0");
				break;
			case "w" :
				break;
			case "W" :
				break;
			case "D" :
				break;
			case "d" :
				bufr = stringutil_format(sourceDate[sourceDateIndex].getDate(), size, STRINGUTIL_RIGHT_JUSTIFY, "0");
				break;
			case "F" :
				bufr = stringutil_format(sourceDate[sourceDateIndex].getDay(), size, STRINGUTIL_RIGHT_JUSTIFY, "0");
				break;
			case "E" :
			try {
				bufr = stringutil_format(DATEUTIL_DAYS[sourceDate[sourceDateIndex].getDay()], size == 2 ? 2 : (size < 4 ? 3 : -1));
			}
			catch (e) {
				new Log().error(e);
			}
				break;
			case "a" :
				bufr = sourceDate[sourceDateIndex].getHours() >= 12 ? "PM" : "AM";
				break;
			case "H" :
				bufr = stringutil_format(sourceDate[sourceDateIndex].getHours(), size, STRINGUTIL_RIGHT_JUSTIFY, "0");
				break;
			case "k" :
				bufr = stringutil_format(sourceDate[sourceDateIndex].getHours() + 1, size, STRINGUTIL_RIGHT_JUSTIFY, "0");
				break;
			case "K" :
				bufr = stringutil_format(sourceDate[sourceDateIndex].getHours() % 12, size, STRINGUTIL_RIGHT_JUSTIFY, "0");
				break;
			case "h" :
				bufr = stringutil_format(sourceDate[sourceDateIndex].getHours() == 0 ? 12 : (sourceDate[sourceDateIndex].getHours() < 13 ? sourceDate[sourceDateIndex].getHours() : sourceDate[sourceDateIndex].getHours() % 12), size, STRINGUTIL_RIGHT_JUSTIFY, "0");
				break;
			case "m" :
				bufr = stringutil_format(sourceDate[sourceDateIndex].getMinutes(), size, STRINGUTIL_RIGHT_JUSTIFY, "0");
				break;
			case "s" :
				bufr = stringutil_format(sourceDate[sourceDateIndex].getSeconds(), size, STRINGUTIL_RIGHT_JUSTIFY, "0");
				break;
			case "S" :
				bufr = stringutil_format(sourceDate[sourceDateIndex].getMilliseconds(), size, STRINGUTIL_RIGHT_JUSTIFY, "0");
				break;
			case "z" :
				break;
			case "Z" :
				bufr = sourceDate[sourceDateIndex].substring(sourceDate[sourceDateIndex].toString().indexOf("-"), 5);
				break;
			case "|" :
				sourceDateIndex += size;
				continue;
		}
		if (bufr.length == 0) {
			result += ch;
		}
		else {
			result += bufr;
			i += size - 1;
		}
	}

	return (result);
}

function dateutil_getBeginningOfWeekDate(date) {
	return (new Date(date.getFullYear(), date.getMonth(), date.getDate() - date.getDay()));
}

function dateutil_getEndOfWeekDate(date) {
	return (new Date(date.getFullYear(), date.getMonth(), date.getDate() - date.getDay() + 6));
}

function dateutil_parse(value, dateFormat) {
	if (!jsutil_isDefined(value) || !jsutil_isDefined(dateFormat)) {
		return (null);
	}
	value = value.toString();
	var valuePos = 0;
	var currentDate = new Date();
	var fullYear = currentDate.getFullYear();
	var month = 0;
	var date = 1;
	var hour = 0;
	var minute = 0;
	var second = 0;
	var millisecond = 0;
	var amPmFlag = null;
	for (var i = 0; i < dateFormat.length;) {
		var ch = dateFormat.charAt(i);
		var size = stringutil_countRepeatingCharacters(dateFormat, ch, i);
		switch (ch) {
			case "G" :
				break;
			case "y" :
				fullYear = new Number(value.substring(valuePos, valuePos + size));
				break;
			case "M" :
				if (size < 3) {
					month = new Number(value.substring(valuePos, valuePos + size)) - 1;
				}
				else {
					var monthName = value.substring(valuePos, valuePos + 3).toLowerCase();
					for (var j = 0; j < DATEUTIL_MONTHS.length; ++j) {
						if (monthName == DATEUTIL_MONTHS[j].substring(0, 3).toLowerCase()) {
							month = j;
							if (size > 3) {
								valuePos += DATEUTIL_MONTHS[j].length - size;
							}
							break;
						}
					}
				}
				break;
			case "w" :
				break;
			case "W" :
				break;
			case "D" :
				break;
			case "d" :
				date = new Number(value.substring(valuePos, valuePos + size));
				break;
			case "F" :
				break;
			case "E" :
				break;
			case "a" :
				var amPmFlag = value.substring(valuePos, valuePos + 2);
				valuePos++;
				break;
			case "H" :
				hour = new Number(value.substring(valuePos, valuePos + size));
				break;
			case "k" :
				break;
			case "K" :
				break;
			case "h" :
				hour = new Number(value.substring(valuePos, valuePos + size));
				break;
			case "m" :
				minute = new Number(value.substring(valuePos, valuePos + size));
				break;
			case "s" :
				second = new Number(value.substring(valuePos, valuePos + size));
				break;
			case "S" :
				millisecond = new Number(value.substring(valuePos, valuePos + size));
				break;
			case "z" :
				break;
			case "Z" :
				break;
		}
		valuePos += size;
		i += size;
	}
	if (amPmFlag != null && amPmFlag.toUpperCase() == "PM") {
		hour += 12;
	}
	return (new Date(fullYear, month, date, hour, minute, second, millisecond));
}

/*
 * The APIs with the prefix domcore_ are utility methods useful for manipulating
 * DOM core objects.
 */

function domcore_addChildNodes(node, childNodes) {
	if (domcore_isNode(node)) {
		childNodes = domcore_asNodes(node.ownerDocument, childNodes);
		for (var i = 0; i < childNodes.length; ++i) {
			node.appendChild(childNodes[i]);
		}
	}
}

function domcore_asElement(domDocument, elementOrId) {
	return (jsutil_isString(elementOrId) ? domDocument.getElementById(elementOrId) : elementOrId);
}

function domcore_asElements(domDocument, elementsOrIds) {
	var elements = [];
	for (var i = 0; i < elementsOrIds.length; ++i) {
		elements[i] = domcore_asElement(domDocument, elementsOrIds[i]);
	}
	return (elements);
}

function domcore_asNodes(domDocument, nodes) {
	var result = [];
	nodes = jsutil_asArray(nodes);
	for (var i = 0; i < nodes.length; ++i) {
		result[i] = domcore_isNode(nodes[i]) ? nodes[i] : domDocument.createTextNode(nodes[i]);
	}
	return (result);
}

function domcore_createElement(domDocument, tagName, childNodes, attributeNames, attributeValues) {
	var element = domDocument.createElement(tagName);
	domcore_addChildNodes(element, childNodes);
	jsutil_setAttributeValue(element, attributeNames, attributeValues);
	return (element);
}

function domcore_createId(domDocument, suffix) {
	var id = null;
	do {
		id = "__invsun_id_" + new Date().getTime() + (jsutil_isString(suffix) ? "_" + suffix : "");
	}
	while (domDocument.getElementById(id) != null);
	return (id);
}

function domcore_getChildElements(parentNode, nodeName, attributeNames, attributeValues) {
	var childNodes = [];
	if (parentNode != null && parentNode.hasChildNodes()) {
		nodeName = jsutil_isDefined(nodeName) ? nodeName : null;

		// Retrieve the specified nodes, parentNode must be either a Document or
		// Element, then filter the result so that only those attribute name/value
		// pairs matching the specified attribute name/value pairs are included
		// in the final result.

		childNodes = domcore_getMatchingNodes(parentNode.getElementsByTagName(nodeName), null, attributeNames, attributeValues);
	}
	return (childNodes);
}

function domcore_getChildNodes(parentNode, nodeName, attributeNames, attributeValues) {
	var childNodes = [];
	if (parentNode != null && parentNode.hasChildNodes()) {
		nodeName = jsutil_isDefined(nodeName) ? nodeName : null;
		childNodes = domcore_getMatchingNodes(parentNode.childNodes, nodeName, attributeNames, attributeValues);
	}
	return (childNodes);
}

function domcore_getMatchingNodes(nodeList, nodeName, attributeNames, attributeValues) {
	var matchingNodes = [];

	// Convert attributeNames and attributeValues to arrays.  If the sizes of
	// the resulting arrays do not match, then ignore the attributeNames and
	// attributeValues parameters.

	var names = jsutil_asArray(attributeNames);
	var values = jsutil_asArray(attributeValues);
	if (names.length != values.length) {
		names = [];
		values = [];
	}

	// Iterate over the collection of nodes, remove those that do not have
	// attributes that match those defined in attributeNames and
	// attributeValues.

	for (var i = 0; i < nodeList.length; ++i) {
		var node = nodeList.item(i);

		// If a node name was specified, ignore those nodes whose node name does
		// not match the specified node name.

		if (nodeName != null && node.nodeName.toLowerCase() != nodeName) {
			continue;
		}

		// If additional criteria was specified, add only those nodes that
		// match the criteria.

		for (var j = 0; j < names.length; ++j) {
			var value = eval("node." + names[j]);
			if (values[j] != value) {
				node = null;
				break;
			}
		}

		// If node is not null, add it to the result.

		if (node != null) {
			matchingNodes[matchingNodes.length] = node;
		}
	}
	return (matchingNodes);
}

function domcore_insertChildNodes(node, childNodes, index) {
	if (domcore_isNode(node)) {
		childNodes = domcore_asNodes(node.ownerDocument, childNodes);
		var referenceNode = node.childNodes.item(index);
		for (var i = 0; i < childNodes.length; ++i) {
			node.insertBefore(childNodes[i], referenceNode);
		}
	}
}

function domcore_isNode(node) {
	if (jsutil_isDefined(node)) {
		return (jsutil_isDefined(node.nodeName) && jsutil_isDefined(node.nodeType));
	}
	return (false);
}

function domcore_removeAllChildNodes(node, tagName) {
	var nodeList = node != null ? (jsutil_isString(tagName) ? node.getElementsByTagName(tagName) : node.childNodes) : [];
	for (var i = nodeList.length - 1; i >= 0; --i) {
		node.removeChild(nodeList[i]);
	}
}

function domcore_setChildNodes(node, childNodes) {
	domcore_removeAllChildNodes(node);
	domcore_addChildNodes(node, childNodes);
}

/*
 * The APIs with the prefix domhtml_ are utility methods useful for manipulating
 * DOM HTML objects.
 */

function domhtml_clearHTMLElementValues(elements) {
	elements = jsutil_asArray(elements);
	for (var i = 0; i < elements.length; ++i) {
		if (jsutil_isArray(elements[i])) {
			domhtml_clearHTMLElementValues(elements[i]);
		}
		else {
			if (elements[i].type == "checkbox") {
				elements[i].checked = false;
			}
			else {
				domhtml_setHTMLInputElementValue(elements[i], "");
			}
		}
	}
}

function domhtml_createHTMLAnchorElement(domDocument, childNodes, id, title, lang, dir, className, accessKey, charset, coords, href, hreflang, name, rel, rev, shape, tabIndex, target, type) {
	var a = domhtml_createHTMLElement(domDocument, "a", childNodes, id, title, lang, dir, className);
	jsutil_setAttributeValue(a, ["accessKey", "charset", "coords", "href", "hreflang", "name", "rel", "rev", "shape", "tabIndex", "target", "type"], [accessKey, charset, coords, href, hreflang, name, rel, rev, shape, tabIndex, target, type]);
	return (a);
}

function domhtml_createHTMLDivElement(domDocument, childNodes, id, title, lang, dir, className, align) {
	var div = domhtml_createHTMLElement(domDocument, "div", childNodes, id, title, lang, dir, className);
	jsutil_setAttributeValue(div, ["align"], [align]);
	return (div);
}

function domhtml_createHTMLElement(domDocument, tagName, childNodes, id, title, lang, dir, className) {
	return (domcore_createElement(domDocument, tagName, childNodes, ["id", "title", "lang", "dir", "className"], [id, title, lang, dir, className]));
}

function domhtml_createHTMLHeadingElement(domDocument, tagName, childNodes, id, title, lang, dir, className, align) {
	var heading = domhtml_createHTMLElement(domDocument, jsutil_arrayIndexOf(["h1", "h2", "h3", "h4", "h5", "h6"], tagName) != -1 ? tagName : "h1", childNodes, id, title, lang, dir, className);
	jsutil_setAttributeValue(heading, ["align"], [align]);
	return (heading);
}

function domhtml_createHTMLImageElement(domDocument, childNodes, id, title, lang, dir, className, name, align, alt, border, height, hspace, isMap, longDesc, src, useMap, vspace, width) {
	var img = domhtml_createHTMLElement(domDocument, "img", childNodes, id, title, lang, dir, className);
	jsutil_setAttributeValue(img, ["name", "align", "alt", "border", "height", "hspace", "isMap", "longDesc", "src", "useMap", "vspace", "width"], [name, align, alt, border, height, hspace, isMap, longDesc, src, useMap, vspace, width]);
	return (img);
}

function domhtml_createHTMLInputElement(domDocument, childNodes, id, title, lang, dir, className, defaultValue, defaultChecked, accept, accessKey, align, alt, checked, disabled, maxLength, name, readOnly, size, src, tabIndex, type, useMap, value) {
	var input = domhtml_createHTMLElement(domDocument, "input", childNodes, id, title, lang, dir, className);
	jsutil_setAttributeValue(input, ["defaultValue", "defaultChecked", "accept", "accessKey", "align", "alt", "checked", "disabled", "maxLength", "name", "readOnly", "size", "src", "tabIndex", "type", "useMap", "value"], [defaultValue, defaultChecked, accept, accessKey, align, alt, checked, disabled, maxLength, name, readOnly, size, src, tabIndex, type, useMap, value]);
	return (input);
}

function domhtml_createHTMLLabelElement(domDocument, childNodes, id, title, lang, dir, className, accessKey, htmlFor) {
	var label = domhtml_createHTMLElement(domDocument, "label", childNodes, id, title, lang, dir, className);
	jsutil_setAttributeValue(label, ["accessKey", "htmlFor"], [accessKey, htmlFor]);
	return (label);
}

function domhtml_createHTMLLIElement(domDocument, childNodes, id, title, lang, dir, className, type, value) {
	var li = domhtml_createHTMLElement(domDocument, "li", childNodes, id, title, lang, dir, className);
	jsutil_setAttributeValue(li, ["type", "value"], [type, value]);
	return (li);
}

function domhtml_createHTMLSelectElement(domDocument, childNodes, id, title, lang, dir, className, selectedIndex, value, length, disabled, multiple, name, size, tabIndex) {
	var select = domhtml_createHTMLElement(domDocument, "select", childNodes, id, title, lang, dir, className);
	jsutil_setAttributeValue(select, ["selectedIndex", "value", "length", "disabled", "multiple", "name", "size", "tabIndex"], [selectedIndex, value, length, disabled, multiple, name, size, tabIndex]);
	return (select);
}

function domhtml_createHTMLOptionElement(domDocument, childNodes, id, title, lang, dir, className, defaultSelected, disabled, label, selected, value) {
	var option = domhtml_createHTMLElement(domDocument, "option", childNodes, id, title, lang, dir, className);
	jsutil_setAttributeValue(option, ["defaultSelected", "disabled", "label", "selected", "value"], [defaultSelected, disabled, label, selected, value]);
	return (option);
}

function domhtml_createHTMLSpanElement(domDocument, childNodes, id, title, lang, dir, className) {
	var span = domhtml_createHTMLElement(domDocument, "span", childNodes, id, title, lang, dir, className);
	return (span);
}

function domhtml_createHTMLTableElement(domDocument, childNodes, id, title, lang, dir, className, caption, tHead, tFoot, align, bgColor, border, cellPadding, cellSpacing, frame, rules, summary, width) {
	var table = domhtml_createHTMLElement(domDocument, "table", childNodes, id, title, lang, dir, className);
	jsutil_setAttributeValue(table, ["caption", "tHead", "tFoot", "align", "bgColor", "border", "cellPadding", "cellSpacing", "frame", "rules", "summary", "width"], [caption, tHead, tFoot, align, bgColor, border, cellPadding, cellSpacing, frame, rules, summary, width]);
	return (table);
}

function domhtml_createHTMLTableCellElement(domDocument, tagName, childNodes, id, title, lang, dir, className, abbr, align, axis, bgColor, ch, chOff, colSpan, headers, height, noWrap, rowSpan, scope, vAlign, width) {
	var tableCell = domhtml_createHTMLElement(domDocument, jsutil_arrayIndexOf(["th", "td"], tagName) != -1 ? tagName : "td", childNodes, id, title, lang, dir, className);
	jsutil_setAttributeValue(tableCell, ["abbr", "align", "axis", "bgColor", "ch", "chOff", "colSpan", "headers", "height", "noWrap", "rowSpan", "scope", "vAlign", "width"], [abbr, align, axis, bgColor, ch, chOff, colSpan, headers, height, noWrap, rowSpan, scope, vAlign, width]);
	return (tableCell);
}

function domhtml_createHTMLTableRowElement(domDocument, childNodes, id, title, lang, dir, className, align, bgColor, ch, chOff, vAlign) {
	var tableRow = domhtml_createHTMLElement(domDocument, "tr", childNodes, id, title, lang, dir, className);
	jsutil_setAttributeValue(tableRow, ["align", "bgColor", "ch", "chOff", "vAlign"], [align, bgColor, ch, chOff, vAlign]);
	return (tableRow);
}

function domhtml_createHTMLTableSectionElement(domDocument, tagName, childNodes, id, title, lang, dir, className, align, ch, chOff, vAlign) {
	var tableSection = domhtml_createHTMLElement(domDocument, jsutil_arrayIndexOf(["thead", "tbody", "tfoot"], tagName) != -1 ? tagName : "tbody", childNodes, id, title, lang, dir, className);
	jsutil_setAttributeValue(tableSection, ["align", "ch", "chOff", "vAlign"], [align, ch, chOff, vAlign]);
	return (tableSection);
}

function domhtml_createHTMLUListElement(domDocument, childNodes, id, title, lang, dir, className, compact, type) {
	var ulist = domhtml_createHTMLElement(domDocument, "ul", childNodes, id, title, lang, dir, className);
	jsutil_setAttributeValue(ulist, ["compact", "type"], [compact, type]);
	return (ulist);
}

function domhtml_createJavaScriptHREF(javascriptCode) {
	return ("javascript:" + javascriptCode);
}

function domhtml_getHTMLDocumentPrefix(domDocument) {
	var delimiter = domDocument.URL.indexOf("\\") != -1 ? "\\" : "/";
	var source = domDocument.URL.substring(domDocument.URL.lastIndexOf(delimiter) + 1);
	source = source.substring(0, source.indexOf("."));
	return (source);
}

function domhtml_getHTMLDocumentQueryString(domDocument) {
	var result = null;
	var queryStringStartPos = domDocument.URL.indexOf("?");
	if (queryStringStartPos > -1) {
		result = [];
		var source = domDocument.URL.substring(queryStringStartPos + 1);

		// The following combinations of the query string delimiter characters
		// are handled by this algorithm: '', 'name', 'name&', 'name=',
		// 'name=&', 'name=value', and 'name=value&'.

		while (source != null && source.length > 0) {
			var equalsPos = source.indexOf("=");
			var ampersandPos = source.indexOf("&");
			var name = equalsPos != -1 ? source.substring(0, equalsPos) : (ampersandPos != -1 ? source.substring(0, ampersandPos) : source);
			var value = equalsPos != -1 && ampersandPos != -1 ? source.substring(equalsPos + 1, ampersandPos) : (equalsPos != -1 ? source.substring(equalsPos + 1) : null);
			result[result.length] = {name: name, value: value};
			source = ampersandPos == -1 ? null : source.substring(ampersandPos + 1);
		}
	}

	// The result is either null, an emtpy array, or an array of anonymous
	// objects, each containing name and value properties.

	return (result);
}

function domhtml_getHTMLDocumentSuffix(domDocument) {
	var delimiter = domDocument.URL.indexOf("\\") != -1 ? "\\" : "/";
	var source = domDocument.URL.substring(domDocument.URL.lastIndexOf(delimiter) + 1);
	source = source.substring(source.indexOf(".") + 1);
	var queryStringStartPos = source.indexOf("?");
	if (queryStringStartPos > -1) {
		source = source.substring(0, queryStringStartPos);
	}
	return (source);
}

function domhtml_getHTMLInputElementCursorPosition(inputElement) {
	var cursorStartPosition = -1;
	var cursorEndPosition = -1;
	if (jsutil_isUserAgent(["firefox", "mozilla", "netscape"])) {
		cursorStartPosition = inputElement.selectionStart;
		cursorEndPosition = inputElement.selectionEnd;
	}
	if (jsutil_isUserAgent("msie")) {
		var textRange = inputElement.ownerDocument.selection.createRange();
		inputElement.select();
		var inputElementTextRange = inputElement.ownerDocument.selection.createRange();
		cursorStartPosition = 0;
		while (inputElementTextRange.compareEndPoints("StartToStart", textRange) != 0) {
			inputElementTextRange.moveStart("character", 1);
			++cursorStartPosition;
		}
		cursorEndPosition = cursorStartPosition;
		while (inputElementTextRange.compareEndPoints("StartToEnd", textRange) != 0) {
			inputElementTextRange.moveStart("character", 1);
			++cursorEndPosition;
		}
		textRange.select();
	}
	return ({startPosition: cursorStartPosition, endPosition: cursorEndPosition});
}

function domhtml_getHTMLInputElementValue(inputElement) {
	return (stringutil_trim(inputElement.value));
}

function domhtml_getHTMLSelectElementContents(selectElement) {
	var contents = [];
	var childNodes = domcore_getChildNodes(selectElement);
	for (var i = 0; i < childNodes.length; ++i) {
		contents[i] = [childNodes[i].value, childNodes[i].text];
	}
	return (contents);
}

function domhtml_getHTMLTableCellElementValue(tableElement, row, column) {
	var element = tableElement.getElementsByTagName("tr").item(row).getElementsByTagName("td").item(column);
	return (element.innerHTML);
}

function domhtml_setChildElementsAttribute(parentElements, tagName, className, propertyName, propertyValue) {
	parentElements = jsutil_asArray(parentElements != null ? parentElements : []);
	for (var i = 0; i < parentElements.length; ++i) {
		var elements = domcore_getChildNodes(parentElements[i], tagName, "className", className);
		for (var j = 0; j < elements.length; ++j) {
			eval("elements[j]." + propertyName + " = " + jsutil_asJavaScriptLiteralValue(propertyValue));
		}
	}
}

function domhtml_setChildElementsStyle(parentElements, tagName, className, propertyName, propertyValue) {
	parentElements = jsutil_asArray(parentElements != null ? parentElements : []);
	for (var i = 0; i < parentElements.length; ++i) {
		var elements = domcore_getChildNodes(parentElements[i], tagName, "className", className);
		for (var j = 0; j < elements.length; ++j) {
			domhtml_setStyle(elements[j], propertyName, propertyValue);
		}
	}
}

function domhtml_setHTMLDocumentLocation(domDocument, documentName) {
	domDocument.location.href = documentName;
}

/*
 * Attaches a UI event handler to a DOM element.  Within MSIE, the event is
 * referenced by the variable named 'event', in all other browsers, the event is
 * referenced via the parameter named 'args'.
 */

function domhtml_setHTMLElementEventHandler(htmlElement, eventNames, eventHandler, component, eventData) {
	var fn = function(args) {
		return (eventHandler(typeof(args) != "undefined" ? args : event, component, eventData));
	}
	eventNames = jsutil_asArray(eventNames);
	for (var i = 0; i < eventNames.length; ++i) {
		htmlElement[eventNames[i]] = fn;
	}
}

function domhtml_setHTMLInputElementValue(inputElement, value) {
	inputElement.value = value;
}

function domhtml_setHTMLSelectElementContents(selectElement, contents) {
	var optionIds = [];
	var optionLabels = [];
	if (jsutil_isDefined(contents)) {
		for (var i = 0; i < contents.length; ++i) {
			optionIds[optionIds.length] = contents[i][0];
			optionLabels[optionLabels.length] = contents[i][1];
		}
	}
	domhtml_setHTMLSelectElementOptionsAndLabels(selectElement, optionIds, optionLabels);
}

function domhtml_setHTMLSelectElementOptionsAndLabels(selectElement, optionIds, optionLabels) {
	optionIds = jsutil_asArray(optionIds);
	optionLabels = jsutil_asArray(optionLabels);
	var childNodes = [];
	for (var i = 0; i < optionIds.length; ++i) {
		var value = optionIds[i];
		var label = i < optionLabels.length ? optionLabels[i] : value;
		childNodes[i] =
		domhtml_createHTMLOptionElement(selectElement.ownerDocument, domcore_asNodes(selectElement.ownerDocument, label), null, null, null, null, null, i == 0, null, null, null, value);
		//domhtml_createHTMLOptionElement(domDocument, childNodes, id, title, lang, dir, className, defaultSelected, disabled, label, selected, value)
//		domhtml_createHTMLOptionElement(selectElement.ownerDocument, domcore_asNodes(selectElement.ownerDocument, label), null, null, null, null, null, null, null, null, value);
	}
	domcore_setChildNodes(selectElement, childNodes);
}

function domhtml_setHTMLSelectElementValue(selectElement, value) {
	if (value == null) {
		selectElement.selectedIndex = -1;
	}
	else {
		for (var i = 0; i < selectElement.options.length; ++i) {
			if (selectElement.options[i].value == value) {
				selectElement.selectedIndex = i;
				return;
			}
		}
	}
}

function domhtml_setHTMLTableCellElementValue(tableElement, row, column, value) {
	var element = tableElement.getElementsByTagName("tr").item(row).getElementsByTagName("td").item(column);
	element.innerHTML = value;
}

function domhtml_setHTMLUListElementContents(ulistElement, contents) {
	var liElements = [];
	contents = domcore_asNodes(ulistElement.ownerDocument, contents);
	for (var i = 0; i < contents.length; ++i) {
		liElements[i] = domhtml_createHTMLLIElement(ulistElement.ownerDocument, contents[i]);
	}
	domcore_setChildNodes(ulistElement, liElements);
}

function domhtml_setStyle(element, propertyName, propertyValue) {
	if (element != null && jsutil_isString(propertyName) && jsutil_isString(propertyValue)) {
		eval("element.style." + propertyName + " = " + jsutil_asJavaScriptLiteralValue(propertyValue));
	}
}

/*
 * The APIs with the prefix jsutil_ are utility methods useful JavaScript
 * programming.
 */

function jsutil_arrayIndexOf(array, item) {
	if (jsutil_isArray(array)) {
		for (var i = 0; i < array.length; ++i) {
			if (array[i] == item) {
				return (i);
			}
		}
	}
	return (-1);
}

function jsutil_arrayCopy(array) {
	array = jsutil_asArray(array);
	var result = [];
	for (var i = 0; i < array.length; ++i) {
		result[i] = array[i];
	}
	return (result);
}

function jsutil_asArray(object) {
	return (jsutil_isArray(object) ? object : (jsutil_isDefined(object) ? [object] : []));
}

function jsutil_asJavaScriptLiteralValue(object) {
	return ((typeof(object) == "string") ? "'" + object + "'" : object);
}

function jsutil_createGlobalVariable() {
	var globalVariableName = null;
	do {
		globalVariableName = "__invsun_global_" + new Date().getTime();
	}
	while (eval("typeof(" + globalVariableName + ")") != "undefined");
	return (globalVariableName);
}

function jsutil_createJavaScriptFunctionCall(receiver, functionName, args) {
	var array = jsutil_asArray(receiver);
	receiver = "";
	for (var i = 0; i < array.length; ++i) {
		receiver += array[i] + ".";
	}
	return (receiver + functionName + "(" + jsutil_createParamString(args) + ")");
}

function jsutil_createNamespace(namespace) {
	var elements = namespace.toString().split(".");

	// Create the top-level, or global, namespace, if necessary.

	var currentNamespace = elements[0];
	try {
		typeof(eval(currentNamespace));
	}
	catch (e) {
		eval(currentNamespace + " = {};");
	}

	// Create the remaining part of the namespace, if necessary.

	for (var i = 1; i < elements.length; ++i) {
		currentNamespace += "." + elements[i];
		if (!jsutil_isDefined(eval(currentNamespace))) {
			eval(currentNamespace + " = {};");
		}
	}
}

function jsutil_createParamString(args) {
	args = jsutil_asArray(args);
	var paramStr = "";
	for (var i = 0; i < args.length; ++i) {
		if (i > 0) {
			paramStr += ", ";
		}
		if (jsutil_isArray(args[i])) {
			paramStr += "[" + jsutil_createParamString(args[i]) + "]";
		}
		else {
			paramStr += jsutil_asJavaScriptLiteralValue(args[i]);
		}
	}
	return (paramStr);
}

function jsutil_createSelfReference(object) {
	if (jsutil_isDefined(object)) {
		object.reference = jsutil_createGlobalVariable();
		eval(object.reference + " = object;");
	}
}

function jsutil_getFunctionReference(functionName) {
	return (jsutil_isFunctionDefined(functionName) ? eval(functionName) : null);
}

function jsutil_getObjectReference(objectName) {
	return (jsutil_isDefined(objectName) ? eval(objectName) : null);
}

function jsutil_getUserAgent() {
	jsutil_initializeUserAgentAndVersionAndPlatform();
	return (_JSUTIL_USER_AGENT);
}

function jsutil_getUserAgentPlatform() {
	jsutil_initializeUserAgentAndVersionAndPlatform();
	return (_JSUTIL_USER_AGENT_PLATFORM);
}

function jsutil_getUserAgentVersion() {
	jsutil_initializeUserAgentAndVersionAndPlatform();
	return (_JSUTIL_USER_AGENT_VERSION);
}

function jsutil_getVariableReference(objectName) {
	return (jsutil_isVariableDefined(objectName) ? eval(objectName) : null);
}

function jsutil_hasProperties(object) {
	for (var propertyName in object) {
		return (true);
	}
	return (false);
}

function jsutil_initializeUserAgentAndVersionAndPlatform() {
	if (_JSUTIL_USER_AGENT == null) {

		_JSUTIL_USER_AGENT_PLATFORM = navigator.platform;

		// Determine the user agent and version.

		if (navigator.userAgent.indexOf("Firefox") != -1) {
			_JSUTIL_USER_AGENT = "firefox";
			_JSUTIL_USER_AGENT_VERSION = navigator.userAgent.substring(navigator.userAgent.indexOf("Firefox/") + 8);
			return;
		}
		if (navigator.userAgent.indexOf("Mozilla") != -1 && navigator.userAgent.indexOf("Netscape") == -1 && navigator.userAgent.indexOf("Firefox") == -1 && navigator.userAgent.indexOf("MSIE") == -1) {
			_JSUTIL_USER_AGENT = "mozilla";
			_JSUTIL_USER_AGENT_VERSION = navigator.userAgent.substring(navigator.userAgent.indexOf("rv:") + 3, navigator.userAgent.indexOf(")", navigator.userAgent.indexOf("rv:")));
			return;
		}
		if (navigator.userAgent.indexOf("MSIE") != -1 && navigator.userAgent.indexOf("Opera") == -1) {
			_JSUTIL_USER_AGENT = "msie";
			_JSUTIL_USER_AGENT_VERSION = stringutil_trim(navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE") + 4, navigator.userAgent.indexOf(";", navigator.userAgent.indexOf("MSIE"))));
			return;
		}
		if (navigator.userAgent.indexOf("Netscape") != -1) {
			_JSUTIL_USER_AGENT = "netscape";
			_JSUTIL_USER_AGENT_VERSION = navigator.userAgent.substring(navigator.userAgent.indexOf("Netscape/") + 9, navigator.userAgent.indexOf("(", navigator.userAgent.indexOf("Netscape")));
			return;
		}
		if (navigator.userAgent.indexOf("Opera") != -1) {
			_JSUTIL_USER_AGENT = "opera";
			_JSUTIL_USER_AGENT_VERSION = navigator.userAgent.substring(navigator.userAgent.indexOf("Opera") + 5, navigator.userAgent.indexOf("[", navigator.userAgent.indexOf("Opera")));
			return;
		}
	}
}

function jsutil_integerDivision(value, divisor) {
	return ((value / divisor) - ((value % divisor) / divisor));
}

function jsutil_isArray(object) {
	return (jsutil_isDefined(object) ? object.constructor == Array : false);
}

function jsutil_isBoolean(object) {
	return (jsutil_isDefined(object) ? object.constructor == Boolean : false);
}

function jsutil_isDate(object) {
	return (jsutil_isDefined(object) ? object.constructor == Date : false);

}

function jsutil_isDefined(value) {
	return (typeof(value) != "undefined" && value != null);
}

function jsutil_isFunction(functionName) {
	var type = jsutil_isString(functionName) && functionName.length > 0 ? eval("type = typeof(" + functionName + ")") : typeof(functionName);
	return (type == "function");
}

function jsutil_isInstanceOf(object, type) {
//	return (jsutil_isDefined(object) && object.constructor != null ? new RegExp("^\\s*function\\s*" + type + "\\(.*\\)\\s*\\{").test(object.constructor.toString()) : false);
	if (jsutil_isDefined(object) && object.constructor != null) {

		// If type is an array, check all types referenced within it.

		if (jsutil_isArray(type)) {
			for (var i = 0; i < type.length; ++i) {
				if (object.constructor == type[i]) {
					return (true);
				}
			}
			return (false);
		}
		return (object.constructor == type);
	}
	return (false);
}

function jsutil_isNumeric(object) {
	return (jsutil_isDefined(object) ? object.constructor == Number : false);

}

function jsutil_isRegExp(object) {
	return (jsutil_isDefined(object) ? object.constructor == RegExp : false);
}

function jsutil_isString(object) {
	return (jsutil_isDefined(object) ? object.constructor == String : false);
}

function jsutil_isUserAgent(userAgent, version, platform) {
	var userAgentResult = true;
	var versionResult = true;
	var platformResult = true;

	jsutil_initializeUserAgentAndVersionAndPlatform();

	// If a user agent was specified, determine if the user agent matches the
	// actual user agent.

	if (jsutil_isDefined(userAgent)) {
		userAgent = jsutil_asArray(userAgent);
		userAgentResult = false;
		for (var i = 0; !userAgentResult && i < userAgent.length; ++i) {
			userAgentResult = userAgent[i] == _JSUTIL_USER_AGENT;
		}
	}

	// If a version was specified, ensure the version matches the actual version
	// or range of versions.

	if (jsutil_isDefined(version)) {
		version = jsutil_asArray(version);
		versionResult = false;
		for (var i = 0; !versionResult && i < version.length; ++i) {
			versionResult = version[i] == _JSUTIL_USER_AGENT_VERSION;
		}
	}

	// If a platform was specified, ensure that the platform matches the actual
	// platform or range of platforms.

	if (jsutil_isDefined(platform)) {
		platform = jsutil_asArray(platform);
		platformResult = false;
		for (var i = 0; !platformResult && i < platform.length; ++i) {
			platformResult = platform[i] == _JSUTIL_USER_AGENT_PLATFORM ;
		}
	}

	// The final result is the combination of the user agent, version, and
	// platform.

	return (userAgentResult && versionResult && platformResult);
}

function jsutil_isUserDefinedType(object) {
	return (!jsutil_isArray(object) && !jsutil_isBoolean(object) && !jsutil_isDate(object) && !jsutil_isNumeric(object) && !jsutil_isString(object));
}


function jsutil_resizeArray(array, newSize, filler) {
	if (array.length > newSize) {
		array.splice(newSize, array.length);
	}
	else {
		for (var i = array.length; i < newSize; ++i) {
			array[i] = filler;
		}
	}
	return (array);
}

function jsutil_selectFirstDefinedValue(values) {
	if (jsutil_isArray(values)) {
		for (var i = 0; i < values.length; ++i) {
			if (jsutil_isDefined(values[i])) {
				return (values[i]);
			}
		}
	}
	return (null);
}

function jsutil_setAttributeValue(object, attributeName, attributeValue) {
	if (object != null) {
		var names = jsutil_asArray(attributeName);
		var values = jsutil_asArray(attributeValue);
		for (var i = 0; i < names.length && i < values.length; ++i) {
			var value = jsutil_isString(values[i]) ? "\"" + values[i] + "\"" : values[i];
			if (jsutil_isDefined(value)) {
				eval("object." + names[i] + " = " + value);
			}
		}
	}
}


function jsutil_shallowCopy(source, destination) {
	for (i in source) {
		destination[i] = source[i];
	}
}

/*
 * The APIs with the prefix log_ define the Log class.
 */

function Log(level, colors, logWindowName, logWindowFeatures) {
	if (_LOG_INSTANCE == null) {

		// Methods.

		this.debug = log_debug;
		this.error = log_error;
		this.format = log_format;
		this.isLogLevel = log_isLogLevel;
		this.getLogWindowContents = log_getLogWindowContents;
		this.info = log_info;
		this.log = log_log;
		this.output = log_output;
		this.system = log_system;
		this.toString = log_toString;
		this.warning = log_warning;

		// Properties.

		this.level = jsutil_isDefined(level) ? (jsutil_isArray(level) ? level : [level]) : LOG_LEVEL;
		this.colors = jsutil_isDefined(colors) ? (jsutil_isArray(colors) ? colors : [colors]) : LOG_COLORS;
		this.logWindowName = jsutil_isDefined(logWindowName) ? logWindowName : LOG_WINDOW_NAME;
		this.logWindowFeatures = jsutil_isDefined(logWindowFeatures) ? logWindowFeatures : LOG_WINDOW_FEATURES;
		this.logWindow = null;
		this.leftJustify = 0;
		this.centerJustify = 1;
		this.rightJustify = 2;

		_LOG_INSTANCE = this;
	}
	return (_LOG_INSTANCE);
}


function log_debug(obj) {
	this.log("DBG", obj);
}

function log_error(obj) {
	this.log("ERR", obj);

}

function log_format(obj, width, filler, justification) {
	var log = new Log();
	var leftMargin = "";
	var rightMargin = "";
	width -= obj.toString().length;
	for (var i = 0; i < width; ++i) {
		leftMargin += justification != log.leftJustify ? filler : "";
		rightMargin += justification != log.rightJustify ? filler : "";
	}
	return (leftMargin + obj + rightMargin);
}

function log_getLogWindowContents() {
	var contents = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
	contents += "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
	contents += "<html>\n";
	contents += "\t<head><title>Log Window</title></head>\n";
	contents += "\t<body>\n";
	contents += "\t\t<script type=\"text/javascript\">\n";
	contents += "\t\t\tfunction clear() {\n\t\t\t\tdocument.getElementById(\"output\").innerHTML = \"\";\n\t\t\t}\n";
	contents += "\t\t</script>\n";
	contents += "\t\t<div style=\"background: white; position: fixed; top: 0px; width: 100%; z-index: 10;\"><a href=\"javascript:clear();\">clear</a><br/><hr/></div>\n";
	contents += "\t\t<div id=\"output\" style=\"font-family: courier; font-size: 10pt; position: relative; top: 2em; white-space: nowrap; z-index: 5;\"/>\n";
	contents += "\t</body>\n";
	contents += "</html>\n";
	return (contents);
}

function log_info(obj) {
	this.log("INF", obj);
}

function log_isLogLevel(level) {
	var log = new Log();
	level = level != null && level.constructor == Array ? level : [level];
	if (log.level != null && log.level.constructor == Array && log.level.length > 0) {
		for (var i = 0; i < log.level.length; ++i) {
			for (var j = 0; j < level.length; ++j) {
				if (log.level[i] == level[j]) {
					return (true);
				}
			}
		}
	}
	return (false);
}

function log_log(level, obj) {
	var log = new Log();
	if (log.isLogLevel(level)) {
		if (log.logWindow == null || log.logWindow.closed) {
			log.logWindow = window.open("", log.logWindowName, log.logWindowFeatures);

			// If the log window document body has nothing in it, then it was just
			// opened, so write the initial log window contents into the new window
			// document.

			if (log.logWindow.document.body.innerHTML.length == 0) {
				log.logWindow.document.writeln(this.getLogWindowContents());
			}
			log.logWindow.document.close();
		}
		var color = LOG_DEFAULT_COLOR;
		for (var i = 0; i < log.colors.length; ++i) {
			if (log.level[i] == level) {
				color = log.colors[i];
				break;
			}
		}
		var date = new Date();
		date = log.format(date.getFullYear(), 4, "0", log.rightJustify) + "-" + log.format(date.getMonth() + 1, 2, "0", log.rightJustify) + "-" + log.format(date.getDate(), 2, "0", log.rightJustify) + " " + log.format(date.getHours(), 2, "0", log.rightJustify) + ":" + log.format(date.getMinutes(), 2, "0", log.rightJustify) + ":" + log.format(date.getSeconds(), 2, "0", log.rightJustify) + ":" + log.format(date.getMilliseconds(), 3, "0", log.rightJustify);
		log.output("<div style=\"color: " + color + "\">" + date + " [" + level + "] " + log.toString(obj) + "</div>");
		log.logWindow.scrollBy(0, log.logWindow.innerHeight);
	}
}

function log_output(str) {
	var log = new Log();
	log.logWindow.document.getElementById("output").innerHTML = log.logWindow.document.getElementById("output").innerHTML + str;
}

function log_system(obj) {
	this.log("SYS", obj);
}

function log_toString(obj) {
	if (obj == null) {
		return (obj);
	}
	var constructorName = obj.constructor != null ? obj.constructor.name : obj.constructor;
	if (obj.constructor == Boolean || obj.constructor == Number || obj.constructor == String) {
		return (typeof(obj) + " (" + constructorName + "): " + obj);
	}
	var functions = "";
	var variables = "";
	for (i in obj) {
		try {
			var propertyConstructorName = obj[i].constructor != null ? obj[i].constructor.name : obj[i].constructor;
			var value = obj[i].toString();
			if (value.search(/\s*\S*function\s()\s*\S*/) == 0) {
				functions += "<span style=\"margin-left: 3em;\">" + i + " (" + propertyConstructorName + "): " + value + "</span><br/>";
			}
			else {
				if (value.indexOf("\n") != -1) {
					value = "<pre style=\"margin-left: 6em;\">" + value + "</pre>"
				}
				variables += "<span style=\"margin-left: 3em;\">" + i + " (" + propertyConstructorName + "): " + value + "</span><br/>";
			}
		}
		catch (exception) {
		}
	}
	return (typeof(obj) + " (" + constructorName + "):<br/>" + variables + functions);
}

function log_warning(obj) {
	this.log("WRN", obj);
}


/*
 * The APIs with the prefix stringutil_ are utility methods useful for
 * manipulating String objects.
 */

function stringutil_countRepeatingCharacters(str, ch, startPosition) {
	var result = 0;
	for (var i = startPosition; i < str.length; ++i) {
		if (str.charAt(i) == ch) {
			++result;
		}
		else {
			break;
		}
	}
	return (result);
}

function stringutil_format(obj, width, justification, filler) {
	if (!jsutil_isDefined(obj)) {
		return ("");
	}
	var str = obj.toString();
	var paddingSize = (width == -1) ? 0 : width - str.length;
	filler = jsutil_isDefined(filler) ? filler : " ";
	for (var i = 0; i < paddingSize; ++i) {
		str = (justification == STRINGUTIL_RIGHT_JUSTIFY) ? filler + str : str + filler;
	}
	var formattedString = str;
	if (width != -1 && !jsutil_isNumeric(obj) && formattedString.length > width) {
		formattedString = (!jsutil_isDefined(justification) || justification == STRINGUTIL_RIGHT_JUSTIFY) ? formattedString.substring(0, width) : formattedString.substring(formattedString.length - width);
	}
	return (formattedString);
}

function stringutil_indent(str, indentationLevel, lineDelimiter) {
	var result = "";
	var delimiter = jsutil_isDefined(lineDelimiter) ? lineDelimiter : "\n";
	var lines = str.split(delimiter);
	for (var i = 0; i < lines.length; ++i) {
		for (var j = 0; j < indentationLevel; ++j) {
			result += "\t";
		}
		result += lines[i] + delimiter;
	}
	return (result);
}

function stringutil_isWhitespace(ch) {
	return (ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t' || ch == '\f');
}

function stringutil_ltrim(str) {
	var i = -1;
	while (++i < str.length && stringutil_isWhitespace(str.charAt(i)));
	return (str.substring(i, str.length));
}

function stringutil_trim(str) {
	return (stringutil_ltrim(stringutil_rtrim(str)));
}

function stringutil_rtrim(str) {
	var i = str.length;
	while (--i > 0 && stringutil_isWhitespace(str.charAt(i)));
	return (str.substring(0, i + 1));
}

function stringutil_toString(object, valueSeparator, listSeparator) {
	var str = "";
	if (object == null) {
		return (str);
	}
	valueSeparator = jsutil_isDefined(valueSeparator) ? valueSeparator : STRINGUTIL_DEFAULT_VALUE_SEPARATOR;
	listSeparator = jsutil_isDefined(listSeparator) ? listSeparator : STRINGUTIL_DEFAULT_LIST_SEPARATOR;
	if (typeof object.length == "number") {
		for (var i = 0; i < object.length; ++i) {
			if (i > 0) {
				str += listSeparator;
			}
			str += object[i];
		}
	}
	else {
		for (i in object) {
			str += i + valueSeparator + object[i] + listSeparator;
		}
	}
	return (str);
}

/*
 * The APIs with the prefix validate_ are utility methods useful for
 * validating form data.
 */


function validate_hasValidCharacters(inputElement, invalidCharacters) {
	return (domhtml_getHTMLInputElementValue(inputElement).search(invalidCharacters) == -1);
}

function validate_isChecked(inputElement) {
	return (inputElement.checked == true);
}

function validate_isCorrectLength(inputElement, minimumSize, maximumSize) {
	var value = stringutil_trim(domhtml_getHTMLInputElementValue(inputElement));
	if (minimumSize > 0 && value.length < minimumSize) {
		return (false);
	}
	if (maximumSize > 0 && value.length > maximumSize) {
		return (false);
	}
	return (true);
}

function validate_isDefined(inputElement) {
	return (stringutil_trim(domhtml_getHTMLInputElementValue(inputElement)).length != 0);
}

function validate_isIdentical(inputElement1, inputElement2) {
	return (domhtml_getHTMLInputElementValue(inputElement1) == domhtml_getHTMLInputElementValue(inputElement2));
}

function validate_isValidDate(inputElement) {
	if (domhtml_getHTMLInputElementValue(inputElement).search(/(0[1-9]|1[012])[- \/\.](0[1-9]|[12][0-9]|3[01])[- \/\.](19|20)\d\d/) != 0) {
		return (false);
	}
	var delimiter = domhtml_getHTMLInputElementValue(inputElement).charAt(2);
	var date = domhtml_getHTMLInputElementValue(inputElement).split(delimiter);
	var month = date[0];
	var day = date[1];
	var year = date[3];
	if ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) {
		return (false);
	}
	if (month == 2 && day > 30) {
		return (false);
	}
	if (month == 2 && day == 29 && !(year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))) {
		return (false);
	}
	return (true);
}

function validate_matchesPattern(inputElement, sequence) {
	return (domhtml_getHTMLInputElementValue(inputElement).search(sequence) != -1);
}

invsun.core.AjaxTransaction = function(uri, userName, password, callback) {
	this.uri = uri;
	this.userName = userName;
	this.password = password;
	this.callback = callback;
	this.xmlHttpRequest = null;
	this.response = null;
	this.status = null;

	// Attempt to create a native (Mozilla/Safari, etc.) XMLHttpRequest object.

	if (window.XMLHttpRequest) {
		try {
			this.xmlHttpRequest = new XMLHttpRequest();
		}
		catch (e) {
		}
	}
	else {

		// Attempt to create an ActiveX/MSMXL(2) XMLHttpRequest object.

		if (window.ActiveXObject) {
			try {
				this.xmlHttpRequest = new ActiveXObject("Microsoft.XMLHTTP");
			}
			catch (e) {
				try {
					this.xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
				}
				catch (e) {
				}
			}
		}
	}

//	if (this.xmlHttpRequest) {
//		this.xmlHttpRequest.userData = this;
//	}
}

invsun.core.AjaxTransaction.DEFAULT_REQUEST_HEADERS = [
	["Content-Type", "text/xml"]
];

invsun.core.AjaxTransaction.prototype.getElementValue = function(elementName) {
	var domNode = this.response;
	var key = jsutil_isArray(elementName) ? elementName : elementName.split(".");
	var valueNode = null;
	for (var i = 0; i < key.length; ++i) {
		valueNode = null;
		for (var j = 0; j < domNode.childNodes.length; ++j) {
			var childNode = domNode.childNodes.item(j);
			if (childNode.nodeName == key[i]) {
				valueNode = childNode;
				domNode = childNode;
				break;
			}
		}
	}
	var value = null;
	if (valueNode != null) {
		if (valueNode.nodeValue != null) {
			value = valueNode.nodeValue;
		}
		for (var i = 0; i < valueNode.childNodes.length; ++i) {
			var childNode = valueNode.childNodes.item(i);
			if (childNode.nodeType == 3) {
				value = childNode.nodeValue;
			}
		}
	}
	return (value);
}

invsun.core.AjaxTransaction.prototype.send = function(content, requestHeaders) {

	// Open the connection and send the request only if the XMLHttpRequest object
	// was successfully created.

	if (this.xmlHttpRequest != null) {
		var asynchronousRequest = jsutil_isFunction(this.callback);
		this.xmlHttpRequest.open("POST", this.uri, asynchronousRequest, this.userName, this.password);
		for (var i = 0; i < invsun.core.AjaxTransaction.DEFAULT_REQUEST_HEADERS.length; ++i) {
			this.xmlHttpRequest.setRequestHeader(invsun.core.AjaxTransaction.DEFAULT_REQUEST_HEADERS[i][0], invsun.core.AjaxTransaction.DEFAULT_REQUEST_HEADERS[i][1]);
		}
		if (jsutil_isDefined(requestHeaders)) {
			for (var i = 0; i < requestHeaders.length; ++i) {
				this.xmlHttpRequest.setRequestHeader(requestHeaders[i][0], requestHeaders[i][1]);
			}
		}
		if (asynchronousRequest) {
			var ajaxTransaction = this;
			var xmlHttpRequest = this.xmlHttpRequest;
			var callback = this.callback;
			xmlHttpRequest.onreadystatechange = function() {
				if (xmlHttpRequest.readyState == 4) {
					ajaxTransaction.response = xmlHttpRequest.status == 200 ? (xmlHttpRequest.getResponseHeader("Content-Type") == "text/xml" ? xmlHttpRequest.responseXML : xmlHttpRequest.responseText) : xmlHttpRequest.statusText;
					ajaxTransaction.status = xmlHttpRequest.status;
					callback(ajaxTransaction.response);
				}
			};
			this.xmlHttpRequest.send(content);
			return;
		}
		this.xmlHttpRequest.send(content);
		this.response = this.xmlHttpRequest.getResponseHeader("Content-Type") == "text/xml" ? this.xmlHttpRequest.responseXML : this.xmlHttpRequest.responseText;
		this.status = this.xmlHttpRequest.status;
		return (this.response);
	}
	return (null);
}

invsun.core.WebApp = function(namespace, defaultEventHandlerPrefix, eventHandlerApiName, serverUrl, sessionIdName) {
	this.namespace = jsutil_isDefined(namespace) ? namespace : invsun.core.WebApp.DEFAULT_NAMESPACE;
	this.defaultEventHandlerPrefix = jsutil_isDefined(defaultEventHandlerPrefix) ? defaultEventHandlerPrefix : invsun.core.WebApp.DEFAULT_EVENT_HANDLER_PREFIX;
	this.eventHandlerApiName = jsutil_isDefined(eventHandlerApiName) ? eventHandlerApiName : invsun.core.WebApp.DEFAULT_EVENT_HANDLER_API_NAME;
	this.serverUrl = serverUrl;
	this.sessionIdName = jsutil_isDefined(sessionIdName) ? sessionIdName : invsun.core.WebApp.DEFAULT_SESSION_ID_NAME;
}

invsun.core.WebApp.DEFAULT_EVENT_HANDLER_API_NAME = "eventHandler";
invsun.core.WebApp.DEFAULT_EVENT_HANDLER_PREFIX = "default";
invsun.core.WebApp.DEFAULT_NAMESPACE = "webapp";
invsun.core.WebApp.DEFAULT_SESSION_ID_NAME = "webapp_session_id";

invsun.core.WebApp.prototype.asElement = function(elementId) {
	return (domcore_asElement(document, elementId));
}

invsun.core.WebApp.prototype.asElements = function(elementIds) {
	var result = [];
	elementIds = jsutil_asArray(elementIds);
	for (var i = 0; i < elementIds.length; ++i) {
		result[result.length] = domcore_asElement(document, elementIds[i]);
	}
	return (result);
}

invsun.core.WebApp.prototype.asImage = function(src, title) {
	return (domhtml_createHTMLImageElement(document, null, null, title, null, null, null, null, null, null, null, null, null, null, null, src, null, null, null));
}

invsun.core.WebApp.prototype.asNodes = function(nodes) {
	return (domcore_asNodes(document, nodes));
}

invsun.core.WebApp.prototype.authenticate = function(uri, credentials) {

	// If no server URL has been specified, then the website is in prototype
	// mode.

	if (this.serverUrl == null) {
		var date = new Date();
		date.setDate(date.getDate() + 1);
		this.setCookieValue(this.sessionIdName, "true", date, null, null, false);
	}
	else {

		// The website is in normal mode.  Invoke the server to authenticate.

		var ajaxTransaction = new invsun.core.AjaxTransaction(uri);
		var request = invsun.core.XmlObject.encode(new invsun.core.XmlObject(credentials).asDocument());
		var requestHeaders = [["Content-Type", "text/xml"], ["Content-Length", request.length]];
		ajaxTransaction.send(request, requestHeaders);
		if (ajaxTransaction.status != 200) {
			alert(ajaxTransaction.response);
		}
		return (ajaxTransaction.response);
	}
}

invsun.core.WebApp.prototype.createUrl = function(documentName, parameterNames, parameterValues) {
	var queryString = "";
	parameterNames = jsutil_asArray(parameterNames);
	parameterValues = jsutil_asArray(parameterValues);
	for (var i = 0; i < parameterNames.length; ++i) {
		queryString += queryString.length == 0 ? "?" : "&";
		queryString += parameterNames + "=" + (i < parameterValues.length ? parameterValues[i] : "");
	}
	return ((this.serverUrl != null ? this.serverUrl : "") + documentName + queryString);
}

invsun.core.WebApp.prototype.createMessage = function(messageType, id, attributeNames, attributeValues, section, fieldDescriptors) {
	var message = "";

	switch (messageType) {
		case WEBAPP_QUERY_STRING_MESSAGE_TYPE :
			break;
		case WEBAPP_XML_MESSAGE_TYPE :
			message = xmlutil_addStartTag(xmlutil_createXmlMessage(), 0, id, attributeNames, attributeValues);
			break;
	}

	var startSection = section == -1 ? 0 : section;
	var endSection = section == -1 ? fieldDescriptors.length : section + 1;
	for (var i = startSection; i < endSection; ++i) {
		for (var j = 0; j < fieldDescriptors[i].length; ++j) {
			var name = fieldDescriptors[i][j][0];
			var value = null;
			var element = this.asElement(name);
			if (element != null) {
				switch (element.nodeName.toLowerCase()) {
					case "input" :
						value = (element.type == "checkbox" || element.type == "radio") ? (element.checked ? "true" : "") : stringutil_trim(element.value);
						break;
					case "select" :
						value = stringutil_trim(element.value);
						break;
					case "textarea" :
						value = stringutil_trim(element.value);
						break;
					default :
						break;
				}
			}
			else {
				var obj = jsutil_getFunctionReference(name);
				if (obj != null) {
				}
			}

			if (value != null && value.length > 0) {
//new Log().debug(fieldDescriptors[i][j][0] + "-" + element.nodeName);
				switch (messageType) {
					case WEBAPP_QUERY_STRING_MESSAGE_TYPE :
						if (message.length > 0) {
							message += "&";
						}
						message += name + "=" + value;
						break;
					case WEBAPP_XML_MESSAGE_TYPE :
						message = xmlutil_addElement(message, 1, "property", [name], [value], null);
						break;
				}
			}
		}
	}

	switch (messageType) {
		case WEBAPP_QUERY_STRING_MESSAGE_TYPE :
			var prefix = "?id=" + id;
			if (jsutil_isArray(attributeNames)) {
				for (var i = 0; i < attributeNames.length; ++i) {
					prefix += "&" + attributeNames[i] + (jsutil_isArray(attributeValues) && i < attributeValues.length ? attributeValues[i] : "");
				}
			}
			message = prefix + (message.length > 0 ? "&" : "") + message;
			break;
		case WEBAPP_XML_MESSAGE_TYPE :
			message = xmlutil_addEndTag(message, 0, id);
			break;
	}

	return (message);
}
invsun.core.WebApp.prototype.deleteCookie = function(name, path, domain) {
//	var value = this.setCookieValue(document, name);
	if (this.getCookieValue(document, name) != null) {
		var date = new Date();
		date.setDate(date.getDate() - 1);
		this.setCookieValue(document, name, "", date, path, domain, false);
	}
}

invsun.core.WebApp.prototype.dispatchEvent = function(source, type, eventData) {

	// Create the name of the event handler function.

	var functionName = this.namespace + "." + (jsutil_isDefined(source) ? source : this.defaultEventHandlerPrefix) + "." + this.eventHandlerApiName;

	// If the function name is valid, then invoke it.

	if (jsutil_isFunction(functionName)) {
		new Log().system("invsun.core.WebApp.dispatchEvent: " + source + ": " + functionName + "(" + type + ", " + eventData + ")");
		try {
			eval(functionName)(type, eventData);
		}
		catch (exception) {
			new Log().error("invsun.core.WebApp.dispatchEvent: " + source + ": " + functionName + "(" + type + ", " + eventData + ")");
			new Log().error(exception);
		}
	}
}

invsun.core.WebApp.prototype.getCookieValue = function(name) {
	var array = document.cookie.split(";");
	var parsedCookies = new Array();
	for (var i = 0; i < array.length; ++i) {
		parsedCookies[i] = array[i].split("=");
	}
	for (var i = 0; i < parsedCookies.length; ++i) {
		if (stringutil_trim(parsedCookies[i][0]) == name && jsutil_isDefined(parsedCookies[i][1])) {
			return (stringutil_trim(parsedCookies[i][1]));
		}
	}
	return (null);
}

invsun.core.WebApp.prototype.getDefaultEventHandlerPrefix = function() {
	return (this.defaultEventHandlerPrefix);
}

invsun.core.WebApp.prototype.isAuthenticated = function() {
	var cookie = this.getCookieValue(this.sessionIdName);
	return (cookie != null && cookie.length > 0);
}

invsun.core.WebApp.prototype.setCookieValue = function(name, value, expires, path, domain, secure) {
	var value = name + "=" + escape(value) + (expires != null ? "; expires=" + expires.toGMTString() : "") + (path != null ? "; path=" + path : "") + (domain != null ? "; domain=" + domain : "") + (secure ? "; secure;" : "");
	document.cookie = value;
}

invsun.core.WebApp.prototype.setDocumentLocation = function(documentName, parameterNames, parameterValues) {
	domhtml_setHTMLDocumentLocation(document, this.createUrl(documentName, parameterNames, parameterValues));
}

invsun.core.WebApp.prototype.unauthenticate = function(uri, credentials) {

	// If no server URL has been specified, then the website is in prototype
	// mode.

	if (this.serverUrl == null) {
		this.deleteCookie(this.sessionIdName, null, null);
	}
	else {

		// The website is in normal mode.  Invoke the server to unauthenticate.

		var ajaxTransaction = new invsun.core.AjaxTransaction(uri);
		ajaxTransaction.send(invsun.core.XmlObject.encode(new invsun.core.XmlObject(credentials).asDocument()));
//		ajaxTransaction.send(new invsun.core.XmlObject(credentials).toString());
		if (ajaxTransaction.status != 200) {
			alert(ajaxTransaction.response);
		}
	}
}

invsun.core.WebApp.prototype.validateFields = function(fieldDescriptors) {
	fieldDescriptors = jsutil_asArray(fieldDescriptors);
	var errorMessages = [];
	var errorDetected = false;
	for (var i = 0; i < fieldDescriptors.length; ++i) {
		var element = this.asElement(fieldDescriptors[i][0]);
		for (var j = 1; j < fieldDescriptors[i].length; ++j) {
			var type = fieldDescriptors[i][j][0];
			var args = fieldDescriptors[i][j][1];
			var errorMessage = fieldDescriptors[i][j][2];
			switch (type) {
				case VALIDATE_HAS_VALID_CHARACTERS :
					if (!validate_hasValidCharacters(element, args[0])) {
						errorDetected = true;
					}
					break;
				case VALIDATE_IS_CHECKED :
					if (!validate_isChecked(element)) {
						errorDetected = true;
					}
					break;
				case VALIDATE_IS_CORRECT_LENGTH :
					if (!validate_isCorrectLength(element, args[0], args[1])) {
						errorDetected = true;
					}
					break;
				case VALIDATE_IS_DEFINED :
					if (!validate_isDefined(element)) {
						errorDetected = true;
					}
					break;
				case VALIDATE_IS_IDENTICAL :
					if (!validate_isIdentical(element, this.asElement(args))) {
						errorDetected = true;
					}
					break;
				case VALIDATE_IS_VALID_DATE :
					if (!validate_isValidDate(element)) {
						errorDetected = true;
					}
					break;
				case VALIDATE_MATCHES_PATTERN :
					if (!validate_matchesPattern(element, args[0])) {
						errorDetected = true;
					}
					break;
				default :
					break;
			}
			if (errorDetected) {
				errorMessages[errorMessages.length] = errorMessage;
				errorDetected = false;
			}
		}
	}
	return (errorMessages);
}

invsun.core.XmlObject = function(contents, isFormatted, indentationLevel) {
	this.contents = contents;
	this.xmlMessage = "";
	this.isFormatted = jsutil_isBoolean(isFormatted) ? isFormatted : false;
	this.indentationLevel = jsutil_isNumeric(indentationLevel) ? indentationLevel : 0;
}

invsun.core.XmlObject.XML_ELEMENT_ATTRIBUTES_NAME = "xmlAttrs";
invsun.core.XmlObject.XML_ELEMENT_VALUE_NAME = "xmlValue";

invsun.core.XmlObject.decode = function(object) {
	var result = jsutil_isDefined(object) ? object.toString() : "";
	result = result.replace(/&amp;/g, "&");
	result = result.replace(/&lt;/g, "<");
	result = result.replace(/&gt;/g, ">");
	result = result.replace(/&quot;/g, "\"");
	return (result);
}

invsun.core.XmlObject.encode = function(object) {
	var result = jsutil_isDefined(object) ? object.toString() : "";
	var result = result.replace(/&/g, "&amp;");
	result = result.replace(/</g, "&lt;");
	result = result.replace(/>/g, "&gt;");
	result = result.replace(/\"/g, "&quot;");
	return (result);
}

invsun.core.XmlObject.prototype.addCdataElement = function(elementName, attributeNames, attributeValues, cdataValue) {
	this.addElement(elementName, attributeNames, attributeValues, "<![CDATA[" + cdataValue + "]]>");
}

invsun.core.XmlObject.prototype.addComment = function(comment) {
	this.newLine();
	this.indent();
	this.xmlMessage += "<!-- " + comment + " -->";
}

invsun.core.XmlObject.prototype.addElement = function(elementName, attributeNames, attributeValues, value) {
	if (jsutil_isDefined(value)) {
		this.addStartTag(elementName, attributeNames, attributeValues);
		this.addFragment(value);
		this.addEndTag(elementName);
	}
	else {
		this.addEmptyElementTag(elementName, attributeNames, attributeValues);
	}
}

invsun.core.XmlObject.prototype.addEmptyElementTag = function(elementName, attributeNames, attributeValues) {
	this.newLine();
	this.indent();
	this.xmlMessage += "<" + elementName + this.buildAttributeList(attributeNames, attributeValues) + "/>";
}

invsun.core.XmlObject.prototype.addEndTag = function(elementName) {
	this.indentationLevel -= 1;
	this.indent();
	this.xmlMessage += "</" + elementName + ">";
}

invsun.core.XmlObject.prototype.addFragment = function(fragment) {
	 var isXmlObject = jsutil_isInstanceOf(fragment, invsun.core.XmlObject);
	 this.xmlMessage += isXmlObject ? fragment.asXmlMessage() : invsun.core.XmlObject.encode(fragment);
	 if (isXmlObject) {
		 this.newLine();
	 }
}

invsun.core.XmlObject.prototype.addStartTag = function(elementName, attributeNames, attributeValues) {
	this.newLine();
	this.indent();
	this.xmlMessage += "<" + elementName + this.buildAttributeList(attributeNames, attributeValues) + ">";
	this.indentationLevel += 1;
}

invsun.core.XmlObject.prototype.asDocument = function() {
	return ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + this.asXmlMessage());
}

invsun.core.XmlObject.prototype.asXmlMessage = function() {
	this.xmlMessage = "";

	// Iterate over each property name in the object referenced by contents.
	// Each property name corresponds to an element name in the resulting XML
	// message.

	for (var elementName in this.contents) {

		// Retrieve the value (element) associated with the current property
		// (element) name in the container object, then isolate the attribute and
		// value associated with this element.

		var element = this.contents[elementName];
		var attributes = invsun.core.XmlObject.getXmlElementAttributes(element);
		var value = invsun.core.XmlObject.getXmlElementValue(element);

		// If the value of the current element is an array, iterate over each
		// element in the array.  Each item in the array is treated as a unique
		// occurrence of the current element name.

		if (jsutil_isArray(value)) {
			for (var i = 0; i < value.length; ++i) {

				// Retrieve attributes associated with the current array-based
				// element and merge them with the attributes specified within the
				// containing element.

				var arrayValueAttributes = invsun.core.XmlObject.getXmlElementAttributes(value[i]);
				arrayValueAttributes.names = arrayValueAttributes.names.concat(attributes.names);
				arrayValueAttributes.values = arrayValueAttributes.values.concat(attributes.values);

				// If the value is an object, convert it to an XML fragment via a
				// new instance of XmlObject, otherwise, add a new element,
				// attributes, and value to the XML message.

				var elementValue = value[i];
				if (elementValue.constructor == Object) {
					var object = {};
					object[elementName] = elementValue;
					this.setObjectAttributes(elementValue, arrayValueAttributes);
					this.xmlMessage += new invsun.core.XmlObject(object, this.isFormatted, this.indentationLevel).asXmlMessage();
				}
				else {
					this.addElement(elementName, arrayValueAttributes.names, arrayValueAttributes.values, elementValue);
				}
			}
		}
		else {

			// The element value is not an array.  Add a new element, attributes,
			// and value to the XML message.

			if (elementName != invsun.core.XmlObject.XML_ELEMENT_ATTRIBUTES_NAME) {
				this.addElement(elementName, attributes.names, attributes.values, !jsutil_isUserDefinedType(value) ? value : new invsun.core.XmlObject(value, this.isFormatted, this.indentationLevel + 1));
			}
		}
	}
	return (this.xmlMessage);
}

invsun.core.XmlObject.prototype.buildAttributeList = function(attributeNames, attributeValues) {
	attributeNames = jsutil_asArray(attributeNames);
	attributeValues = jsutil_asArray(attributeValues);
	var attributeList = "";
	for (var i = 0; i < attributeNames.length; ++i) {
		attributeList += " " + attributeNames[i] + "=\"" + (jsutil_isDefined(attributeValues[i]) ? attributeValues[i] : "") + "\"";
	}
	return (attributeList);
}

invsun.core.XmlObject.prototype.getContents = function() {

	// If the contents of the XmlObject are already formatted as a Javascript
	// object, do nothing.

	if (!domcore_isNode(this.contents)) {
		return (this.contents);
	}
	var elementAttributesName = invsun.core.XmlObject.XML_ELEMENT_ATTRIBUTES_NAME;
	var elementValueName = invsun.core.XmlObject.XML_ELEMENT_VALUE_NAME;

	// Iterate over and descend into each of th the DOM nodes referenced via the
	// property named contents.

	var stack = [{nodes: this.contents.childNodes, index: 0, object: {}}];
	while (stack.length > 0) {
		var stackElement = stack[stack.length - 1];

		// If the current node has no more child elements, remove the current
		// stack element and proceed to the next peer node.

		if (stackElement.index >= stackElement.nodes.length) {
			var stackElement = stack.pop();

			// If the stack is empty, return the root Javascript object created by
			// this process.

			if (stack.length == 0) {
				return (stackElement.object);
			}
		}
		else {

			// Get the next node in the current node list.

			var node = stackElement.nodes[stackElement.index];
			stackElement.index = stackElement.index + 1;

			// If the node is an element node, create a new stack element for it.

			if (node.nodeType == 1) {

				// Initialize the object that will serve as a container for the
				// attributes, elements, and values contained within the current
				// node.

				var object = {};

				// If the name of the current node has occurred two or more times
				// within its peer group, place the container object at the end of
				// the array referenced by the node name; if the name of the current
				// node has occurred one other time within its peer group, convert
				// the value referenced by the node name to an array consisting of
				// the previous value and the current container object, otherwise,
				// initialize the value referenced by the node name to the container
				// object.

				if (jsutil_isArray(stackElement.object[node.nodeName])) {
					stackElement.object[node.nodeName][stackElement.object[node.nodeName].length] = object;
				}
				else {
					stackElement.object[node.nodeName] = jsutil_isDefined(stackElement.object[node.nodeName]) ? [stackElement.object[node.nodeName], object] : object;
				}

				// Copy the attribute name/value pairs to the container object.

				if (node.attributes.length > 0) {
					object[elementAttributesName] = {};
					for (var i = 0; i < node.attributes.length; ++i) {
						object[elementAttributesName][node.attributes[i].nodeName] = node.attributes[i].nodeValue;
					}
				}
				// Place a new element on the stack.

				stack.push({nodes: node.childNodes, index: 0, object: object});
				continue;
			}

			// If the node is a text node, assign its contents to the value of the
			// containing node.

			var nodeValue = jsutil_isDefined(node.nodeValue) ? stringutil_trim(node.nodeValue) : "";
			if (node.nodeType == 3 && nodeValue.length > 0) {
				var previousStackElement = stack[stack.length - 2];
				var propertyName = previousStackElement.nodes[previousStackElement.index - 1].nodeName;
				var propertyValue = previousStackElement.object[propertyName];

				// If the value referenced by the parent node name is an array,
				// add the node value to the end of the array.  If attributes were
				// specified in the parent node, assign the value to the property
				// specified by invsun.core.XmlObject.XML_ELEMENT_VALUE_NAME,
				// otherwise, add the value to the end of the array.

				if (jsutil_isArray(propertyValue)) {
					propertyValue = propertyValue[propertyValue.length - 1];
					if (jsutil_isDefined(propertyValue[elementAttributesName])) {
						propertyValue[elementValueName] = jsutil_isArray(propertyValue[elementValueName]) ? propertyValue[elementValueName].concat([nodeValue]) : nodeValue;
					}
					else {
						previousStackElement.object[propertyName][previousStackElement.object[propertyName].length - 1] = nodeValue;
					}
				}
				else {

					// The value referenced by the parent node is not an array; if
					// the property value references an empty object, replace it with
					// the node value.  If attributes were specified in the parent
					// node, assign the value to the property specified by
					// invsun.core.XmlObject.XML_ELEMENT_VALUE_NAME, otherwise,
					// convert the property value to an array consisting of the
					// previous value and the current node value.

					if (!jsutil_isDefined(propertyValue) || !jsutil_hasProperties(propertyValue)) {
						previousStackElement.object[propertyName] = nodeValue;
					}
					else {
						if (jsutil_isDefined(propertyValue[elementAttributesName])) {
							propertyValue[elementValueName] = jsutil_isArray(propertyValue[elementValueName]) ? propertyValue[elementValueName].concat([nodeValue]) : nodeValue;
						}
						else {
							propertyValue = [propertyValue, nodeValue];
						}
					}
				}
			}
		}
	}
}

invsun.core.XmlObject.getXmlElementAttributes = function(object) {
	var name = invsun.core.XmlObject.XML_ELEMENT_ATTRIBUTES_NAME;
	var attributes = jsutil_isDefined(object) && jsutil_isDefined(object[name]) ? object[name] : null;
	var result = {names: [], values: []};
	var i = -1;
	for (var attributeName in attributes) {
		result.names[++i] = attributeName;
		result.values[i] = attributes[attributeName];
	}
	return (result);
}

invsun.core.XmlObject.getXmlElementValue = function(object) {
	var name = invsun.core.XmlObject.XML_ELEMENT_VALUE_NAME;
	return (jsutil_isDefined(object) && jsutil_isDefined(object[name]) ? object[name] : object);
}

invsun.core.XmlObject.prototype.indent = function() {
	if (this.isFormatted && this.xmlMessage.charAt(this.xmlMessage.length - 1) == "\n") {
		for (var i = 0; i < this.indentationLevel; ++i) {
			this.xmlMessage += "   ";
		}
	}
}

invsun.core.XmlObject.prototype.newLine = function() {
	if (this.isFormatted && this.xmlMessage.charAt(this.xmlMessage.length - 1) != "\n") {
		this.xmlMessage +=  "\n";
	}
}

invsun.core.XmlObject.prototype.setObjectAttributes = function(object, attributes) {
	if (attributes.names.length > 0) {
		var attributesObject = {};
		for (var i = 0; i < attributes.names.length; ++i) {
			attributesObject[attributes.names[i]] = attributes.values[i];
		}
		object[invsun.core.XmlObject.XML_ELEMENT_ATTRIBUTES_NAME] = attributesObject;
	}
}

invsun.core.XmlObject.prototype.toString = function() {
	return (this.asXmlMessage());
}

