var Cathmhaol = window.Cathmhaol || {};

/**
* Creates a modal window that will handle AJAX (XHR) requests. <p>The structure of the DOM Node added to the document is:</p><p>&lt;div id="{id}"&gt;&lt;div class="modal-window"&gt;&lt;div class="modal-window-control-buttons"&gt;&lt;span&gt;&lt;input class="button previous" type="button" value="&lt;"/&gt;&lt;input class="button next" type="button" value="&lt;"/&gt;&lt;/span&gt;&lt;span&gt;&lt;input class="button close" type="button" value="X"/&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="header"&gt;&lt;/div&gt;&lt;div class="body"&gt;&lt;/div&gt;&lt;div class="buttons"&gt;&lt;/div&gt;&lt;div class="footer"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</p><p>This allows you to style the modal window by inserting rules into the parent page such as #mymodalwindow div.modal-window { background-color: #ebcdef; } or #mymodalwindow div.modal-window div.buttons input,button {background: #A9A9A9 url(my-button-image.gif) repeat 0% 0%;}</p><p>In order to handle special response strings, modify the _xhrEvaluate method.</p>
*
* @author	Robert King (hrobertking@cathmhaol.com)
*
* @requires	http://www.cathmhaol.com/images/opaque.png
*
* @example	var oWindow = new Cathmhaol.XhrWindow(); oWindow.start(document.forms[0]);
*/
Cathmhaol.XhrWindow = function() {
	/**
	* @property	Enables the close button.
	* @type	{boolean}
	* @default	true
	*/
	this.allowClose = true;

	/**
	* @property	Enables the paging (back and forward) buttons.
	* @type	{boolean}
	* @default	true
	*/
	this.allowPaging = true;

	/**
	* @property	Use XmlHttpRequest in asynchronous mode.
	* @type	{boolean}
	* @default	true
	*/
	this.async = true;

	/**
	* @property	The background color of the modal window. Uses CSS format.
	* @type	{string}
	* @default	"#fff"
	*/
	this.backgroundColor = "#fff";

	/**
	* @property	The border to give to the modal window. Uses CSS border property shorthand.
	* @type	{string}
	* @default	"1px solid #000"
	*/
	this.border = "1px solid #000";

	/**
	* @property	Text color
	* @type	{string}
	* @default	"#000"
	*/
	this.color = "#000";

	/**
	* @property	The number of seconds to display the modal window when the XHR call fails.
	* @type	{number}
	* @default	5
	*/
	this.errorMessageTimeout = 5;

	/**
	* @property	Boolean to indicate whether the function to be executed when the processes is ended should be executed before the modal window is closed.
	* @type	{boolean}
	* @default	false
	*/
	this.executeFirst = false;

	/**
	* @property	Font family of the panel body
	* @type	{string}
	* @default	"Times New Roman"
	*/
	this.fontBody = "Times New Roman";

	/**
	* @property	Font family of the panel footer
	* @type	{string}
	* @default	"Arial"
	*/
	this.fontFooter = "Arial";

	/**
	* @property	Font family of the panel header
	* @type	{string}
	* @default	"Arial"
	*/
	this.fontHeader = "Arial";

	/**
	* @property	The height of the body of the panel. Height can be specified in pixels or as a percentage.
	* @type	{string}
	*/
	this.height = null;

	/**
	* @property	The id of the panel
	* @type	{string}
	*/
	this.id = null;

	/**
	* @property	The pixel position of the left border of the modal window
	* @type	{string}
	*/
	this.left = null;

	/**
	* @property	Event handler to call when canceling. 
	* @type	{function}
	*/
	this.onCancel = function() { return; };

	/**
	* @property	Event handler to call when ending.
	* @type	{function}
	*/
	this.onEnd = function() { return; };

	/**
	* @property	Event handler to call when when the XHR fails.
	* @type	{Function}
	*/
	this.onFail = function() { return; };

	/**
	* @property	Internal padding of the modal window. Uses CSS format.
	* @type	{string}
	* @default	"10px"
	*/
	this.padding = "10px";

	/**
	* @property	The number of milliseconds to wait for a response.
	* @type	{number}
	*/
	this.timeout = null;

	/**
	* @property	The pixel position of the top border of the modal window. Uses CSS format.
	* @type	{string}
	* @default	"100px"
	*/
	this.top = "100px";

	/**
	* @property	The width of the panel. Width can be specified in pixels or as a percentage.
	* @type	{string}
	* @default	"400px"
	*/
	this.width = "400px";

	/**
	* @method	Move back in the modal window history
	* @returns	{void}
	*/
	this.back = function() {
		// If there are pages before the current page in the pages array, go to the previous page.

		if (this._panelPageIndex > 0) {
			this.show(this._panelPageIndex - 1);
		}
		return;
	};

	/**
	* @method	Cancels the process and closes the modal window. If executeFirst is true, the event is called before the modal window is closed, otherwise, the event is triggered after the modal window is closed.
	* @returns	{void}
	* @argument	{XmlHttpRequest} xhr
	*/
	this.cancel = function(xhr) {
		// If executeFirst is true, the onCancel method is called before the modal window is closed;
		// otherwise, it's called after the modal window is closed.

		if (me.executeFirst) { me.onCancel.call(xhr); }
		me.close();
		if (!me.executeFirst) { me.onCancel.call(xhr); }
		return;
	};

	/**
	* @method	Destroys the DOM Node
	* @returns	{void}
	*/
	this.close = function() {
		// Reset the pages array and destroy the node that is being used as the panel container.

		_panelPages = new Array();
		if (_panelContainer) {
			var oDomParent = _panelContainer.parentNode;
			if (oDomParent) { oDomParent.removeChild(_panelContainer); }
		}
		return;
	};

	/**
	* @method	Ends the process and closes the modal window. If executeFirst is true, the event is called before the modal window is closed, otherwise, the event is triggered after the modal window is closed.
	* @returns	{void}
	* @argument	{XmlHttpRequest} xhr
	*/
	this.end = function(xhr) {
		// If executeFirst is true, the onEnd method is called before the modal window is closed;
		// otherwise, it's called after the modal window is closed.

		if (me.executeFirst) { me.onEnd.call(xhr); }
		me.close();
		if (!me.executeFirst) { me.onEnd.call(xhr); }
		return;
	};

	/**
	* @method	Move forward in the modal window history
	* @returns	{void}
	*/
	this.forward = function() {
		// If there are pages after the current page in the pages array, go to the next page.

		if (_panelPageIndex < (_panelPages.length - 1)) { me.show(_panelPageIndex + 1); }
		return;
	};

	/**
	* @method	Resets the content of the window to nothing
	* @returns	{void}
	*/
	this.reset = function() {
		// Set the content of the current page to empty containers and hide the primary container.

		me.setHeader("");
		me.setBody("");
		me.setButtons();
		me.setFooter();
		if (_panelContainer) { _panelContainer.style.display = "none"; }
		return;
	};

	/**
	* @method	Sets the content body.
	* @returns	{void}
 	* @argument	{string} html
	*/
	this.setBody = function(html) {
		// Set the content of the body of the current page to nothing.

		if (!_panelBody) { return; }
		if (html) { _panelBody.innerHTML = html; }
		return;
	};

	/**
	* @method	Sets the buttons.
	* @returns	{void}
	* @argument	{node[]} buttons
	*/
	this.setButtons = function(buttons) {
		// Set the button row of the current page to nothing.

		if (!_panelButtons) { return; }
		if (_panelButtons) {
			while (_panelButtons.childNodes[0]) { _panelButtons.removeChild(_panelButtons.childNodes[0]); }

			if (buttons) {
				for (var c = 0; c < buttons.length; c++) {
					var button = buttons[c];
					button.style.fontFamily = "inherit";
					_panelButtons.appendChild(button);
				}
			}
			
			_panelButtons.style.display = (_panelButtons.getElementsByTagName("input").length > 0 ? "block" : "none");
		}
		return;
	};

	/**
	* @method	Sets the content of the panel
	* @returns	{void}
	* @argument	{string} header
	* @argument	{string} body
	* @argument	{object[]} buttons
	* @argument	{string} footer
	*/
	this.setContent = function(header, body, buttons, footer) {
		// Set all the content of the current page to nothing.

		_panelAddPage(header, body, buttons, footer);
		return;
	};

	/**
	* @method	Sets the content of the footer.
	* @returns	{void}
 	* @argument	{string} html
	*/
	this.setFooter = function(html) {
		// Set the content of the footer of the current page to nothing.

		if (!_panelFooter) { return; }
		if (html) { _panelFooter.innerHTML = html; }
		return;
	};

	/**
	* @method	Sets the content of the header.
	* @returns	{void}
 	* @argument	{string} html
	*/
	this.setHeader = function(html) {
		// Set the content of the header of the current page to nothing.

		if (!_panelHeader) { return; }
		if (html) { _panelHeader.innerHTML = html; }
		return;
	};

	/**
	* @method	Shows the object
	* @returns	{void}
	* @argument	{integer} index
	*/
	this.show = function(index) {
		// Show the specified page from the pages array.

		var iPageFirst = 0;
		var iPageLast = (_panelPages.length - 1);
		_panelPageIndex = (index != null ? index : iPageLast);
		_panelPageIndex = (_panelPageIndex < iPageFirst ? iPageFirst : _panelPageIndex);
		_panelPageIndex = (_panelPageIndex > iPageLast ? iPageLast : _panelPageIndex);

		_panelCtrlClose();
		_panelCtrlPaging();

		var page = _panelPages[_panelPageIndex];
		me.setHeader(page.header);
		me.setBody(page.body);
		me.setButtons(page.buttons);
		me.setFooter(page.footer);

		if (_panelContainer) {
			if (page.header || page.body) {
				if (me.height) { _panelBody.style.maxHeight = me.height; }
				_panelContainer.style.display = "block";
			} else {
				_panelContainer.style.display = "none";
			}
		}
		_panelFormInitialize();
		return;
	};

	/**
	* @method	Starts the process using the specified form.
	* @returns	{void}
 	* @argument	{node} oForm
	*/
	this.start = function(oForm) {
		// Start the whole process by sending an HTTP request.

		_panelPages = new Array();
		_panelFormPost(oForm);
		return;
	};

	/**
	* @private
	* @method	Adds a page to the collection
	* @returns	{void}
	* @argument	{string} sHeader
	* @argument	{string} sBody
	* @argument	{object[]} aButtons
	* @argument	{string} sFooter
	*/
	function _panelAddPage(sHeader, sBody, aButtons, sFooter) {
		var page = {
			body: null,
			buttons: new Array(),
			footer: null,
			header: null, 

			/**
			* @method	Adds a button to the collection
			* @argument	{node|object} b
			* @argument	{function} h
			* @returns	{void}
			*/
			addButton: function(b, h) {
				var oNode = document.createElement("input");
				oNode.setAttribute("name", b.name);
				oNode.setAttribute("style", "width:auto !important;");
				oNode.setAttribute("type", b.type);
				oNode.setAttribute("value", ((b.tagName.toUpperCase() == "INPUT") ? b.value : b.innerHTML));
				if (h) { oNode.onclick = h; }
				this.buttons.push(oNode);
			},

			/**
			* Initializes the button array.
			* @returns	{void}
			*/
			clearButtons: function() {
				this.buttons = new Array();
				return;
			}
		};
		if (aButtons) {
			for (var c = 0; c < aButtons.length; c++) {
				page.addButton(aButtons[c].button, aButtons[c].handler);
			}
		}
		if (sBody) { page.body = sBody; }
		if (sFooter) { page.footer = sFooter; }
		if (sHeader) { page.header = sHeader; }
		_panelPages.push(page);
		return;
	}

	/**
	* @private
	* @method	Initializes the parent node.
	* @returns	{void}
	*/
	function _panelCreate() {
		// Create the panel container with a page control (navigtaion) container to hold the forward and back buttons and the modal window close (x)
		// button, a header container to hold the header content, a body container to hold the main content, a button row to group all the form submit buttons,
		// and a footer container to hold any footer content.

		var sId = me.id ? me.id : Math.random();
		_panelContainer = document.getElementById(sId);
		if (!_panelContainer) {
			_panelContainer = document.createElement("div");
			_panelContainer.id = sId;
			_panelContainer.style.display = "none";
			_panelContainer.style.backgroundImage = "url(http://www.cathmhaol.com/images/opaque.png)";
			_panelContainer.style.height = "100%";
			_panelContainer.style.left = "0px";
			_panelContainer.style.position = "absolute";
			_panelContainer.style.top = "0px";
			_panelContainer.style.width = "100%";
			_panelContainer.style.zIndex = 10;
			document.body.insertBefore(_panelContainer, document.body.firstChild);
		}

		var oControl = document.createElement("div");
		oControl.className = "modal-window-control-buttons";
		oControl.style.clear = "both";
		oControl.style.cssFloat = "none";
		oControl.style.margin = "0";
		oControl.style.padding = "0";
		oControl.style.styleFloat = "none";
		oControl.style.width = "100%";

		var oPageControl = document.createElement("span");
		oPageControl.style.display = "block";
		oPageControl.style.cssFloat = "left";
		oPageControl.style.margin = "0";
		oPageControl.style.padding = "0";
		oPageControl.style.styleFloat = "left";
		oPageControl.style.textAlign = "left";
		oPageControl.style.width = "45%";

		var oClose = document.createElement("span");
		oClose.style.display = "block";
		oClose.style.cssFloat = "right";
		oClose.style.margin = "0";
		oClose.style.padding = "0";
		oClose.style.styleFloat = "right";
		oClose.style.textAlign = "right";
		oClose.style.width = "45%";

		_panelBtnPrev = document.createElement("input");
		_panelBtnPrev.className = "button previous";
		_panelBtnPrev.style.background = "transparent";
		_panelBtnPrev.style.border = "1px solid #000";
		_panelBtnPrev.style.fontFamily = "Verdana"
		_panelBtnPrev.style.fontSize = ".7em"
		_panelBtnPrev.style.fontStyle = "normal";
		_panelBtnPrev.style.fontVariant = "small-caps";
		_panelBtnPrev.style.fontWeight = "bold";
		_panelBtnPrev.style.margin = "0";
		_panelBtnPrev.style.visibility = "hidden";
		_panelBtnPrev.style.width = "auto";
		_panelBtnPrev.type = "button";
		_panelBtnPrev.value = "<";
		oPageControl.appendChild(_panelBtnPrev);

		_panelBtnNext = document.createElement("input");
		_panelBtnNext.className = "button next";
		_panelBtnNext.style.background = "transparent";
		_panelBtnNext.style.border = "1px solid #000";
		_panelBtnNext.style.fontFamily = "Verdana"
		_panelBtnNext.style.fontSize = ".7em"
		_panelBtnNext.style.fontStyle = "normal";
		_panelBtnNext.style.fontVariant = "small-caps";
		_panelBtnNext.style.fontWeight = "bold";
		_panelBtnNext.style.margin = "0";
		_panelBtnNext.style.visibility = "hidden";
		_panelBtnNext.style.width = "auto";
		_panelBtnNext.type = "button";
		_panelBtnNext.value = ">";
		oPageControl.appendChild(_panelBtnNext);

		_panelBtnClose = document.createElement("input");
		_panelBtnClose.className = "button close";
		_panelBtnClose.style.background = "transparent";
		_panelBtnClose.style.border = "1px solid #000";
		_panelBtnClose.style.fontFamily = "Verdana";
		_panelBtnClose.style.fontSize = ".7em";
		_panelBtnClose.style.fontStyle = "normal";
		_panelBtnClose.style.fontVariant = "small-caps";
		_panelBtnClose.style.fontWeight = "bold";
		_panelBtnClose.style.margin = "0";
		_panelBtnClose.style.visibility = "hidden";
		_panelBtnClose.style.width = "1.5em";
		_panelBtnClose.type = "button";
		_panelBtnClose.value = "X";
		oClose.appendChild(_panelBtnClose);

		oControl.appendChild(oPageControl);
		oControl.appendChild(oClose);

		var oModalWindow = document.createElement("div");
		oModalWindow.className = "modal-window";
		if (me.backgroundColor) { oModalWindow.style.backgroundColor = me.backgroundColor; }
		if (me.border) { oModalWindow.style.border = me.border; }
		if (me.color) { oModalWindow.style.color = me.color; }
		if (me.padding) { oModalWindow.style.padding = me.padding; }
		if (me.top) { oModalWindow.style.top = me.top; }
		if (me.width) { oModalWindow.style.width = me.width; }

		oModalWindow.style.filter = "alpha(opacity=100)";
		oModalWindow.style.opacity = 1;
		oModalWindow.style.position = "absolute";
		oModalWindow.style.left = (me.left ? me.left : (me.width.indexOf("px") > -1) ? Math.floor((screen.availWidth - parseInt(me.width.replace(/px/, "")))/2)+"px" : Math.floor((100 - parseInt(me.width.replace(/%/, "")))/2)+"%");
		oModalWindow.style.zIndex = _panelContainer.style.zIndex + 1;

		_panelBody = document.createElement("div");
		_panelBody.className = "body";
		_panelBody.style.clear = "both";
		_panelBody.style.cssFloat = "none";
		_panelBody.style.fontFamily = me.fontBody;
		_panelBody.style.overflow = "auto";
		_panelBody.style.styleFloat = "none";
		_panelBody.style.width = "100%";

		_panelButtons = document.createElement("div");
		_panelButtons.className = "buttons";
		_panelButtons.style.borderTop = "1px solid #000";
		_panelButtons.style.clear = "both";
		_panelButtons.style.cssFloat = "none";
		_panelButtons.style.fontFamily = me.fontFooter
		_panelButtons.style.marginTop = "5px";
		_panelButtons.style.paddingTop = "3px";
		_panelButtons.style.styleFloat = "none";
		_panelButtons.style.textAlign = "right";
		_panelButtons.style.width = "100%";

		_panelFooter = document.createElement("div");
		_panelFooter.className = "footer";
		_panelFooter.style.clear = "both";
		_panelFooter.style.cssFloat = "none";
		_panelFooter.style.fontFamily = me.fontFooter;
		_panelFooter.style.fontSize = ".9em";
		_panelFooter.style.styleFloat = "none";
		_panelFooter.style.width = "100%";

		_panelHeader = document.createElement("div");
		_panelHeader.className = "header";
		_panelHeader.style.clear = "both";
		_panelHeader.style.cssFloat = "none";
		_panelHeader.style.fontFamily = me.fontHeader;
		_panelHeader.style.fontSize = "1.2em";
		_panelHeader.style.styleFloat = "none";
		_panelHeader.style.width = "100%";

		oModalWindow.appendChild(oControl);
		oModalWindow.appendChild(_panelHeader);
		oModalWindow.appendChild(_panelBody);
		oModalWindow.appendChild(_panelButtons);
		oModalWindow.appendChild(_panelFooter);

		var scope = me;
		_panelBtnClose.onclick = function() { scope.close.apply(scope); }
		_panelBtnNext.onclick = function() { scope.forward.apply(scope); }
		_panelBtnPrev.onclick = function() { scope.back.apply(scope); }

		_panelContainer.appendChild(oModalWindow);

		return;
	}

	/**
	* @private
	* @method	Controls the close button in the panel header
	* @returns	{void}
	*/
	function _panelCtrlClose() {
		// Set the visibility of the close (x) button based on the programmer's preference.

		_panelBtnClose.style.visibility = (me.allowClose ? "visible" : "hidden");
		return;
	}

	/**
	* @private
	* @method	Controls the paging buttons in the panel header
	* @returns	{void}
	*/
	function _panelCtrlPaging() {
		// Set the visibility of the page navigation buttons (forward and back) based on the programmer's preference
		// and the number of pages in the array.

		if (_panelPages.length > 1) {
			_panelBtnPrev.style.backgroundColor = (_panelPageIndex > 0 ? "transparent" : "#ccc");
			_panelBtnPrev.style.border = "1px solid " + (_panelPageIndex > 0 ? "#000" : "#999");
			_panelBtnPrev.style.color = (_panelPageIndex > 0 ? "#000" : "#999");
			_panelBtnPrev.style.visibility = (me.allowPaging ? "visible" : "hidden");

			_panelBtnNext.style.backgroundColor = (_panelPageIndex < (_panelPages.length - 1) ? "transparent" : "#ccc");
			_panelBtnNext.style.border = "1px solid " + (_panelPageIndex < (_panelPages.length - 1) ? "#000" : "#999");
			_panelBtnNext.style.color = (_panelPageIndex < (_panelPages.length - 1) ? "#000" : "#999");
			_panelBtnNext.style.visibility = (me.allowPaging ? "visible" : "hidden");
		}
		return;
	}

	/**
	* @private
	* @method	Focuses on the first non-hidden input in the form.
	* @returns	{void}
	*/
	function _panelFormInitialize() {
		// Loop through all the elements of the first form inside the panel container and if it's an element that can receive
		// focus (logically) and it's not hidden, then place focus there and exit the loop. If by the end of the loop we still
		// haven't set focus, set focus on the first button in the button row that's not hidden.

		var focused = false;
		var oForms = _panelBody.getElementsByTagName("form");
		if (oForms && oForms.length > 0) {
			var oForm = oForms.item(0);
			if (!oForm) { return;}
			var scope = me;
			for (var c = 0; c < oForm.elements.length; c++) {
				var oElement = oForm.elements[c];
				switch (oElement.tagName.toLowerCase()) {
					case "button":
						if (oElement.style.visibility != "hidden" && !focused) { oElement.focus(); focused = true; }
						break;
					case "fieldset":
						break;
					case "input":
						if (oElement.type != "hidden" && oElement.style.visibility != "hidden" && !focused) { oElement.focus(); focused = true; }
						oElement.onkeyup = function(e) { _panelFormInput_onKeyUp.apply(scope, [e]); }
						break;
					case "legend":
						break;
					case "optgroup":
						break;
					case "option":
						break;
					case "select":
						if (oElement.style.visibility != "hidden" && !focused) { oElement.focus(); focused = true; }
						break;
					case "textarea":
						if (oElement.style.visibility != "hidden" && !focused) { oElement.focus(); focused = true; }
						oElement.onkeyup = function(e) { _panelFormInput_onKeyUp.apply(scope, [e]); }
						break;
				}
			}
		}

		if (!focused && _panelButtons && _panelButtons.hasChildNodes) {
			var c = 0;
			while (!focused && c < _panelButtons.childNodes.length) {
				var oChild = _panelButtons.childNodes[c];
				if ((oChild.tagName.toLowerCase() == "input" || oChild.tagName.toLowerCase() == "button") && oChild.style.visibility != "hidden") { oChild.focus(); focused = true; }
				c++;
			}
		}
		return;
	}

	/**
	* @private
	* @method	Handle key press
	* @returns	{void}
	*/
	function _panelFormInput_onKeyUp(e, self) {
		// Handle key input by figuring out which key was hit and either returning or executing a bound function.

		e = e || window.event;
		var iKey = e.keyCode || e.which;
		switch (iKey) {
			case (008):	//Backspace
			case (009): //Tab
			case (013):	//Enter
				break;
			case (027):	//ESC
				_panelBtnClose.click();
				break;
			case (112):	//F1
			case (113):	//F2
			case (114):	//F3
			case (115):	//F4
			case (116):	//F5
			case (117):	//F6
			case (118):	//F7
			case (119):	//F8
			case (120):	//F9
			case (121):	//F10
			case (122):	//F11
			case (123):	//F12
				if (e.preventDefault) {
					e.preventDefault;
				} else if (window.event) {
					window.event.returnValue = null;
				}
				break;
			case (127):	//Delete
				break;
		}
	}

	/**
	* @private
	* @method	Event handler to process a form submit in the modal window. This method posts the passed form and, if present, the additional name/value pair passed in the oParam object. Data posted also includes a variable identified as "post_type" with the value "xhr_window" so the server can differentiate between posts from this JavaScript object and other posts of the form.
	* @returns	{void}
	* @argument	{Node|String} oForm	The form being processed.
	* @argument	{Object} oParam	An object with name and value properties.
	*/
	function _panelFormPost(oForm, oParam) {
		// Format the request and send the request through the XmlHttpRequest/MSXMLHTTP object, calling the failure
		// function if there's an error.

//		try {
			if (typeof oForm == "string") { oForm = document.getElementById(oForm); }
			if (!oForm || !oForm.action || !oForm.method) { return; }
			oForm.method = (oForm.method.toUpperCase() != "GET") ? "POST" : oForm.method.toUpperCase();
			var query = new Array();
			query.push(encodeURIComponent("post_type") + "=" + encodeURIComponent("xhr_window"));
			if (oParam && oParam.name && oParam.value) {
				query.push(encodeURIComponent(oParam.name) + "=" + encodeURIComponent(oParam.value));
			}
			for(var i = 0; i < oForm.elements.length; i++){
				oField = oForm.elements[i];
				if(oField.disabled || typeof(oField.type) == "undefined") { continue; }
				switch(oField.type.toLowerCase()){
					case "button":
						break;
					case "checkbox":
					case "radio":
						if (oField.checked) { query.push(encodeURIComponent(oField.name) + "=" + encodeURIComponent(oField.value || "on")); }
						break;
					case "reset":
						break;
					case "select-one":
						if (oField.selectedIndex > -1) { query.push(encodeURIComponent(oField.name) + "=" + encodeURIComponent(oField.options[oField.selectedIndex].value)); }
						break;
					case "select-multiple":
						for(var n = 0; n < oField.options.length; n++){ if (oField.options[n].selected) { query.push(encodeURIComponent(oField.name) + "=" + encodeURIComponent(oField.options[n].value)); } }
						break;
					default: //"hidden", "password", "submit", "text", "textarea"
						query.push(encodeURIComponent(oField.name) + "=" + encodeURIComponent(oField.value));
						break;
				}
			}
			var qs;
			if(oForm.method.toUpperCase() == "GET"){
				if (oForm.action.indexOf("?")) { oForm.action += "?"; }
				oForm.action += query.join("&");
			} else if (query.length > 0) {
				qs = query.join("&");
			}
			_xhrRequest.open(oForm.method, oForm.action, me.async);
			_xhrRequest.setRequestHeader("Cache-Control", "no-store, no-cache, must-revalidate");
			if (oForm.method.toUpperCase() == "POST") {
				_xhrRequest.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
				if (_xhrRequest.overrideMimeType) { _xhrRequest.overrideMimeType("text/xml"); }
			}
			_xhrMonitor(_xhrRequest, this);
			_xhrRequest.send(qs);
//		} catch(e) {
//			_xhrOnFailure();
//		}
		return;
	}

	/**
	* @private
	* @method	Event handler to process a form submit in the modal window.
	* @returns	{void}
	* @argument	{Event} e	The browser event that was triggered
	* @argument	{Node} oForm	The form being processed.
	*/
	function _panelSubmit(e, sFormId) {
		// Post the form, being sure to include the button clicked since those exist outside the form.

		e = e || window.event;
		var oButton = {};
		var oClicked = e.target || e.srcElement;
//		try {
			if (oClicked && 3 == oClicked.nodeType) {
				oClicked = oClicked.parentNode;
			}
//		} catch(e) {
			//
//		}
		if (oClicked) { oButton.name = oClicked.name; oButton.value = (oClicked.value && oClicked.value != "" ? oClicked.value : oClicked.name); }
		_panelFormPost(document.getElementById(sFormId), oButton);
		return;
	}

	/**
	* @private
	* @method	Method to terminate a XHR call.
	* @returns	{void}
	* @argument	{XmlHttpRequest} xhr	The connection object returned by asyncRequest.
	*/
	function _xhrAbort(xhr) {
		// Abort the pending request.

		if (xhr.readyState != 4 && xhr.readyState != 0) {
			window.clearInterval(_timerIdXhr);
			xhr.abort();
			me.cancel(xhr);
		}
		return;
	}

	/**
	* @private
	* @method	Customizable function used to evaluate the response stream.
	* @returns	{void}
	*/
	function _xhrEvaluate(xhr) {
		// Figure out how to handle the response when the request says that the response is complete.

		if (!xhr || !xhr.responseText) { _xhrOnFailure(); }

		// Here you would put code to handle a response stream prior to processing it into a modal window. For example,
		// you may choose to have your response stream close the modal window (at the end of a process). This can be done
		// by sending a response stream such as "{action: close}" or "{action: abort}" and then evaluating the stream by
		// using eval -- e.g. var o = eval(xhr.responseText) -- and using the response -- e.g. if (o.action == "close") {
		// me.close() } else if (o.action == "abort") { me.abort(); }

		return;
	}

	/**
	* @private
	* @method	Creates a timer that polls the XmlHttpRequest object every 50 milliseconds
	* @returns	{void}
	* @argument	{XmlHttpRequest} xhr	The XmlHttpRequest object
	* @argument	{Object} obj		This object. Used to maintain scope.
	*/
	function _xhrMonitor(xhr, obj) {
		// Set an interval timer that checks the request to see what the progress is. If it's complete (readyState == 4) then clear the timer and process the
		// response. If there is a set duration to wait, set a timeout and if, when the timeout expires, there is still a pending request, abort it and
		// consider the request a failure.

		var scope = me;
		_timerIdXhr = window.setInterval(function() { if (xhr && xhr.readyState == 4) { window.clearInterval(_timerIdXhr); _xhrOnReady(xhr, obj); } }, 50);
		if (obj.timeout) { window.setTimeout(function() { _xhrAbort(xhr); }, obj.timeout); }
		return;
	}

	/**
	* @private
	* @property	Event handler to handle a server error in the AJAX call, e.g. a 404 or a failure returned by the AJAX call.
	* @returns	{void}
	* @argument	{XmlHttpRequest} xhr	The XmlHttpRequest object
	*/
	function _xhrOnFailure(xhr) {
		// Call the function in the onFail property (if it exists), reset the modal window and show any failure content
		// returned in a response.

		if (me.onFail) { me.onEnd = me.onFail; }
		me.reset();
		if (xhr && xhr.status && xhr.statusText) {
			_panelAddPage(xhr.status, xhr.statusText);
			me.show();
			var scope = me;
			window.setTimeout(function(){ scope.end(); }, (me.errorMessageTimeout * 1000));
		} else {
			me.end(xhr);
		}
		return;
	}

	/**
	* @private
	* @method	Called when the readyState of the XHR object changes to "Complete".
	* @returns	{void}
	* @argument	{XmlHttpRequest} xhr	The XmlHttpRequest object
	* @argument	{Object} scope
	*/
	function _xhrOnReady(xhr, scope) {
		// Check to see if the HTTP Status Code is one that indicates the response is OK. If it's not (for example if it's a 30x, 40x, or 50x),
		// consider the request a failure and execute the failure function; otherwise, put the response in the modal window.

		if (xhr) {
			if (xhr.status == 200) {
				_xhrOnSuccess(xhr);
			} else {
				_xhrOnFailure(xhr);
			}
		}
		return;
	}

	/**
	* @private
	* @method	Event handler to process the AJAX Response object
	* @returns	{void}
	* @argument	{XmlHttpRequest} xhr	The XmlHttpRequest object
	*/
	function _xhrOnSuccess(xhr) {
		// Figure out if there is any special process that needs to be done before the response is put into the modal window,
		// then parse the response and put the various markup elements into their corresponding containers (header, body, button
		// row, and footer.

		_xhrEvaluate(xhr);
		var oBody = document.createElement("div");
		oBody.innerHTML = xhr.responseText;
		me.reset();
		_panelAddPage();
		var iLastIndex = _panelPages.length - 1;
		var oPage = _panelPages[iLastIndex];

		var oHeader = oBody.getElementsByTagName("h1").item(0);
		if (oHeader) { oBody.removeChild(oHeader); oPage.header = oHeader.innerHTML; }

		var oFooter;
		var oDivs = oBody.getElementsByTagName("div");
		for (var c = 0; c < oDivs.length; c++) {
			if ((/\bfooter\b/i).test(oDivs.item(c).className)) {
				oFooter = oDivs.item(c);
				oFooter.parentNode.removeChild(oFooter);
				oPage.footer = oFooter.innerHTML;
				break;
			}
		}

		var aButtons = new Array();
		var scope = me;
		var oForms = oBody.getElementsByTagName("form");
		for (var c = 0; c < oForms.length; c++) {
			var oForm = oForms.item(c);
			oForm.id = oForm.id != null && oForm.id != "" ? oForm.id : Math.random().toString();
			for (var c = 0; c < oForm.elements.length; c++) {
				var oHandler;
				var oElement = oForm.elements[c];
				if (oElement.type && (oElement.type.toLowerCase() == "button" || oElement.type.toLowerCase() == "submit")) {
					if ((/\bcancel\b/i).test(oElement.className)) {
						oHandler = function(e) { scope.cancel(); };
					} else if ((/\bend\b/i).test(oElement.className)) {
						oHandler = function(e) { scope.end(); };
					} else if (oElement.type.toLowerCase() == "submit") {
						oHandler = function(e) { _panelSubmit(e, oForm.id); };
					}
					if (oHandler) {
						oPage.addButton(oElement, oHandler);
						aButtons.push(oElement);
					}
				} else if (oElement.type && oElement.type.toLowerCase() == "reset") {
					oPage.addButton(oElement, function(e) { document.getElementById(oForm.id).reset(); });
					aButtons.push(oElement);
				}
			}
		}
		for (var c = 0; c < aButtons.length; c++) {
			var oElement = aButtons[c];
			oElement.parentNode.removeChild(oElement);
		}

		oPage.body = oBody.innerHTML;

		me.show(iLastIndex);
		return;
	}

	/**
	* _panelContainer	{node}	The DOM node that contains the body, buttons, footer, and header.
	* _panelBody	{node}	Body of the panel
	* _panelBtnClose	{node}	Closer
	* _panelBtnNext	{node}	Page control - next
	* _panelBtnPrev	{node}	Page control - previous
	* _panelFooter	{node}	Footer of the panel
	* _panelHeader	{node}	Header of the panel
	* _panelPageIndex	{integer}	Index of page currently displayed in the panel
	* _panelPages	{object[]}	Set of pages loaded into the modal window
	* _timerIdXhr	{integer}	The interval id of the XHR polling interval.
	* _xhrMsXmlHttp	{string[]}	Array of XmlHttpRequest object types for Internet Explorer
	* _xhrRequest	{XmlHttpRequest}	The XmlHttpRequest object used by the Panel
	*/
	var _panelContainer, _panelBody, _panelBtnClose, _panelBtnNext, _panelBtnPrev, _panelFooter, _panelHeader, _panelPageIndex = 0, _panelPages = new Array(), _timerIdXhr, _xhrRequest;

	/**
	* Constructor
	*/
	var me = this;
	if (window.XMLHttpRequest) {
		_xhrRequest = new XMLHttpRequest();
	} else if (typeof ActiveXObject != "undefined") {
		var _xhrMsXmlHttp = new Array("MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP");
		for (var c = 0; c < _xhrMsXmlHttp.length; ++c) {
//			try {
				_xhrRequest = new ActiveXObject(_xhrMsXmlHttp[c]);
				break;
//			} catch(e) {
//			}
		}
	}
	_panelCreate();
};

