var Cathmhaol = window.Cathmhaol || {};

/**
* @library     Cathmhaol.XhrWindow
* @description 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
* @requires    http://www.cathmhaol.com/images/opaque.png
*
* @example     var oWindow = new Cathmhaol.XhrWindow(); oWindow.start(document.forms[0]);
*/
Cathmhaol.XhrWindow = function() {
	this._initialize();
};

Cathmhaol.XhrWindow.prototype = {
	/**
	* @property    allowClose
	* @description Enables the close button.
	* @type {Boolean}
	* @default true
	*/
	allowClose: true,

	/**
	* @property    allowPaging
	* @description Enables the paging (back and forward) buttons.
	* @type {Boolean}
	* @default true
	*/
	allowPaging: true,

	/**
	* @property    async
	* @description Use XmlHttpRequest in asynchronous mode.
	* @type {Boolean}
	* @default true
	*/
	async: true,

	/**
	* @method      back
	* @description Move back in the modal window history
	* @returns {void}
	*/
	back: function() {
		if (this._panelPageIndex > 0) { this.show(this._panelPageIndex - 1); }
		return;
	},

	/**
	* @property    backgroundColor
	* @description The background color of the modal window. Uses CSS format.
	* @type {String}
	* @default "#fff"
	*/
	backgroundColor: "#fff",

	/**
	* @property    border
	* @description The border to give to the modal window. Uses CSS border property shorthand.
	* @type {String}
	* @default "1px solid #000"
	*/
	border: "1px solid #000",

	/**
	* @method      cancel
	* @description 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}
	* @param {XmlHttpRequest} xhr	The XmlHttpRequest object returned in the last call.
	*/
	cancel: function(xhr) {
		if (this.executeFirst) { this.onCancel.call(xhr); }
		this.close();
		if (!this.executeFirst) { this.onCancel.call(xhr); }
		return;
	},

	/**
	* @method      close
	* @description Destroys the DOM Node
	* @returns {void}
	*/
	close: function() {
		this._panelPages = new Array();
		if (this._panel) {
			var oDomParent = this._panel.parentNode;
			if (oDomParent) { oDomParent.removeChild(this._panel); }
		}
		return;
	},

	/**
	* @property    color
	* @description Text color
	* @type {String}
	* @default "#000"
	*/
	color: "#000",

	/**
	* @property    errorMessageTimeout
	* @description The number of seconds to display the modal window when the XHR call fails.
	* @type {Number}
	* @default 5
	*/
	errorMessageTimeout: 5,

	/**
	* @method      end
	* @description 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}
	* @param {XmlHttpRequest} xhr	The XmlHttpRequest object returned in the last call.
	*/
	end: function(xhr) {
		if (this.executeFirst) { this.onEnd.call(xhr); }
		this.close();
		if (!this.executeFirst) { this.onEnd.call(xhr); }
		return;
	},

	/**
	* @property    executeFirst
	* @description 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
	*/
	executeFirst: false,

	/**
	* @property    fontBody
	* @description Font family of the panel body
	* @type {string}
	* @default "Times New Roman"
	*/
	fontBody: "Times New Roman",

	/**
	* @property    fontFooter
	* @description Font family of the panel footer
	* @type {string}
	* @default "Arial"
	*/
	fontFooter: "Arial",

	/**
	* @property    fontHeader
	* @description Font family of the panel header
	* @type {string}
	* @default "Arial"
	*/
	fontHeader: "Arial",

	/**
	* @method      forward
	* @description Move forward in the modal window history
	* @returns {void}
	*/
	forward: function() {
		if (this._panelPageIndex < (this._panelPages.length - 1)) { this.show(this._panelPageIndex + 1); }
		return;
	},

	/**
	* @property    height
	* @description The height of the body of the panel. Height can be specified in pixels or as a percentage.
	* @type {String}
	*/
	height: null,

	/**
	* @property    id
	* @description The id of the panel
	* @type {String}
	*/
	id: null,

	/**
	* @property    left
	* @description The pixel position of the left border of the modal window
	* @type {String}
	*/
	left: null,

	/**
	* @property    onCancel
	* @description Event handler to call when canceling. 
	* @type {Function}
	*/
	onCancel: function() { return; },

	/**
	* @property    onEnd
	* @description Event handler to call when ending.
	* @type {Function}
	*/
	onEnd: function() { return; },

	/**
	* @property    onFail
	* @description Event handler to call when when the XHR fails.
	* @type {Function}
	*/
	onFail: function() { return; },

	/**
	* @property    padding
	* @description Internal padding of the modal window. Uses CSS format.
	* @type {String}
	* @default "10px"
	*/
	padding: "10px",

	/**
	* @method      reset
	* @description Resets the content of the window to nothing
	* @returns {void}
	*/
	reset: function() {
		this.setHeader("");
		this.setBody("");
		this.setButtons();
		this.setFooter();
		if (this._panel) { this._panel.style.display = "none"; }
		return;
	},

	/**
	* @method      setBody
	* @description Sets the content body.
	* @returns {void}
 	* @param {String} html	The HTML to be used as the content.
	*/
	setBody: function(html) {
		if (!this._panelBody) { return; }
		if (html) { this._panelBody.innerHTML = html; }
		return;
	},

	/**
	* @method      setButtons
	* @description Sets the buttons.
	* @returns {void}
	* @param {Node[]} buttons  Array of DOM Nodes that are input type="submit|reset|button"
	*/
	setButtons: function(buttons) {
		if (!this._panelButtons) { return; }
		if (this._panelButtons) {
			while (this._panelButtons.childNodes[0]) { this._panelButtons.removeChild(this._panelButtons.childNodes[0]); }

			if (buttons) {
				for (var c = 0; c < buttons.length; c++) {
					var button = buttons[c];
					button.style.fontFamily = "inherit";
					this._panelButtons.appendChild(button);
				}
			}
			
			this._panelButtons.style.display = (this._panelButtons.getElementsByTagName("input").length > 0 ? "block" : "none");
		}
		return;
	},

	/**
	* @method      setContent
	* @description Sets the content of the panel
	* @returns {void}
	* @param {string} header     Content of the header
	* @param {string} body       Content of the body
	* @param {Object[]} buttons  Array of buttons objects that contain a button property (which is a button or input DOM Node) and a handler property (which is a JavaScript function)
	* @param {string} footer     Content of the footer
	*/
	setContent: function(header, body, buttons, footer) {
		this._panelAddPage(header, body, buttons, footer);
		return;
	},

	/**
	* @method      setFooter
	* @description Sets the content of the footer.
	* @returns {void}
 	* @param {String} html	The HTML to be used as the content.
	*/
	setFooter: function(html) {
		if (!this._panelFooter) { return; }
		if (html) { this._panelFooter.innerHTML = html; }
		return;
	},

	/**
	* @method      setHeader
	* @description Sets the content of the header.
	* @returns {void}
 	* @param {String} html	The HTML to be used as the content.
	*/
	setHeader: function(html) {
		if (!this._panelHeader) { return; }
		if (html) { this._panelHeader.innerHTML = html; }
		return;
	},

	/**
	* @method      show
	* @description Shows the object
	* @returns {void}
	* @param {integer} index  The index of the page to show. Defaults to the last page in the collection.
	*/
	show: function(index) {
		var iPageFirst = 0;
		var iPageLast = (this._panelPages.length - 1);
		this._panelPageIndex = (index != null ? index : iPageLast);
		this._panelPageIndex = (this._panelPageIndex < iPageFirst ? iPageFirst : this._panelPageIndex);
		this._panelPageIndex = (this._panelPageIndex > iPageLast ? iPageLast : this._panelPageIndex);

		this._panelCtrlClose();
		this._panelCtrlPaging();

		var page = this._panelPages[this._panelPageIndex];
		this.setHeader(page.header);
		this.setBody(page.body);
		this.setButtons(page.buttons);
		this.setFooter(page.footer);

		if (this._panel) {
			if (page.header || page.body) {
				if (this.height) { this._panelBody.style.maxHeight = this.height; }
				this._panel.style.display = "block";
			} else {
				this._panel.style.display = "none";
			}
		}
		this._panelFormInitialize();
		return;
	},

	/**
	* @method      start
	* @description Starts the process using the specified form.
	* @returns {void}
 	* @param {Node} oForm	The form to be processed as a DOM Node.
	*/
	start: function(oForm) {
		this._panelPages = new Array();
		this._panelFormPost(oForm);
		return;
	},

	/**
	* @property    timeout
	* @description The number of milliseconds to wait for a response.
	* @type {Number}
	*/
	timeout: null,

	/**
	* @property    top
	* @description The pixel position of the top border of the modal window. Uses CSS format.
	* @type {String}
	* @default "100px"
	*/
	top: "100px",

	/**
	* @property    width
	* @description The width of the panel. Width can be specified in pixels or as a percentage.
	* @type {String}
	* @default "400px"
	*/
	width: "400px",

	/**
	* @private
	* @method      _initialize
	* @description Initializes the node that references the HTML body, the Panel object, and the XHR object.
	* @returns {Boolean}
	*/
	_initialize: function() {
		try {
			if (window.XMLHttpRequest) {
				this._xhrRequest = new XMLHttpRequest();
			} else if (typeof ActiveXObject != "undefined") {
				for (var c = 0; c < this._xhrMsXmlHttp.length; ++c) {
					try {
						this._xhrRequest = new ActiveXObject(this._xhrMsXmlHttp[c]);
						break;
					}
					catch(e) {
					}
				}
			}
		} catch (e) {
			return false;
		}
		this._panelCreate();
		return true;
	},

	/**
	* @private
	* @property    _panel
	* @description The DOM node that contains the body, buttons, footer, and header.
	* @type {Node}
	*/
	_panel: null,

	/**
	* @private
	* @method      _panelAddPage
	* @description Adds a page to the collection
	* @returns {void}
	* @param {string} sHeader     Text to show in the header of the panel
	* @param {string} sBody       Text to show in the body of the panel
	* @param {Object[]} aButtons  Array of objects that contain a DOM Node (input or button) and a handler function
	* @param {string} sFooter     Text to show in the footer
	*/
	_panelAddPage: function(sHeader, sBody, aButtons, sFooter) {
		var page = {
			body: null,
			buttons: new Array(),
			footer: null,
			header: null, 

			/**
			* Adds a button to the collection
					* @param {Node|Object} b  Button object that contains type="{submit|reset|button}", name, value
			* @param {Function} h     JavaScript handler function
			* @returns {void}
			*/
			addButton: function(b, h) {
				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; }
		this._panelPages.push(page);
		return;
	},

	/**
	* @private
	* @property    _panelBody
	* @description Body of the panel
	* @type {Node}
	*/
	_panelBody: null,

	/**
	* @private
	* @property    _panelBtnClose
	* @description Closer
	* @type {Node}
	*/
	_panelBtnClose: null,

	/**
	* @private
	* @property    _panelBtnNext
	* @description Page control - next
	* @type {Node}
	*/
	_panelBtnNext: null,

	/**
	* @private
	* @property    _panelBtnPrev
	* @description Page control - previous
	* @type {Node}
	*/
	_panelBtnPrev: null,

	/**
	* @private
	* @method      _panelCreate
	* @description Initializes the parent node.
	* @returns {void}
	*/
	_panelCreate: function() {
		var sId = this.id ? this.id : Math.random();
		this._panel = document.getElementById(sId);
		if (!this._panel) {
			this._panel = document.createElement("div");
			this._panel.id = sId;
			this._panel.style.display = "none";
			this._panel.style.backgroundImage = "url(http://www.cathmhaol.com/images/opaque.png)";
			this._panel.style.height = "100%";
			this._panel.style.left = "0px";
			this._panel.style.position = "absolute";
			this._panel.style.top = "0px";
			this._panel.style.width = "100%";
			this._panel.style.zIndex = (this._body && this._body.style && this._body.style.zIndex ? this._body.style.zIndex : 0) + 10;
			document.body.insertBefore(this._panel, 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%";

		this._panelBtnPrev = document.createElement("input");
		this._panelBtnPrev.className = "button previous";
		this._panelBtnPrev.style.background = "transparent";
		this._panelBtnPrev.style.border = "1px solid #000";
		this._panelBtnPrev.style.fontFamily = "Verdana"
		this._panelBtnPrev.style.fontSize = ".7em"
		this._panelBtnPrev.style.fontStyle = "normal";
		this._panelBtnPrev.style.fontVariant = "small-caps";
		this._panelBtnPrev.style.fontWeight = "bold";
		this._panelBtnPrev.style.margin = "0";
		this._panelBtnPrev.style.visibility = "hidden";
		this._panelBtnPrev.style.width = "auto";
		this._panelBtnPrev.type = "button";
		this._panelBtnPrev.value = "<";
		oPageControl.appendChild(this._panelBtnPrev);

		this._panelBtnNext = document.createElement("input");
		this._panelBtnNext.className = "button next";
		this._panelBtnNext.style.background = "transparent";
		this._panelBtnNext.style.border = "1px solid #000";
		this._panelBtnNext.style.fontFamily = "Verdana"
		this._panelBtnNext.style.fontSize = ".7em"
		this._panelBtnNext.style.fontStyle = "normal";
		this._panelBtnNext.style.fontVariant = "small-caps";
		this._panelBtnNext.style.fontWeight = "bold";
		this._panelBtnNext.style.margin = "0";
		this._panelBtnNext.style.visibility = "hidden";
		this._panelBtnNext.style.width = "auto";
		this._panelBtnNext.type = "button";
		this._panelBtnNext.value = ">";
		oPageControl.appendChild(this._panelBtnNext);

		this._panelBtnClose = document.createElement("input");
		this._panelBtnClose.className = "button close";
		this._panelBtnClose.style.background = "transparent";
		this._panelBtnClose.style.border = "1px solid #000";
		this._panelBtnClose.style.fontFamily = "Verdana";
		this._panelBtnClose.style.fontSize = ".7em";
		this._panelBtnClose.style.fontStyle = "normal";
		this._panelBtnClose.style.fontVariant = "small-caps";
		this._panelBtnClose.style.fontWeight = "bold";
		this._panelBtnClose.style.margin = "0";
		this._panelBtnClose.style.visibility = "hidden";
		this._panelBtnClose.style.width = "1.5em";
		this._panelBtnClose.type = "button";
		this._panelBtnClose.value = "X";
		oClose.appendChild(this._panelBtnClose);

		oControl.appendChild(oPageControl);
		oControl.appendChild(oClose);

		var oModalWindow = document.createElement("div");
		oModalWindow.className = "modal-window";
		if (this.backgroundColor) { oModalWindow.style.backgroundColor = this.backgroundColor; }
		if (this.border) { oModalWindow.style.border = this.border; }
		if (this.color) { oModalWindow.style.color = this.color; }
		if (this.padding) { oModalWindow.style.padding = this.padding; }
		if (this.top) { oModalWindow.style.top = this.top; }
		if (this.width) { oModalWindow.style.width = this.width; }

		oModalWindow.style.filter = "alpha(opacity=100)";
		oModalWindow.style.opacity = 1;
		oModalWindow.style.position = "absolute";
		oModalWindow.style.left = (this.left ? this.left : (this.width.indexOf("px") > -1) ? Math.floor((screen.availWidth - parseInt(this.width.replace(/px/, "")))/2)+"px" : Math.floor((100 - parseInt(this.width.replace(/%/, "")))/2)+"%");
		oModalWindow.style.zIndex = this._panel.style.zIndex + 1;

		this._panelBody = document.createElement("div");
		this._panelBody.className = "body";
		this._panelBody.style.clear = "both";
		this._panelBody.style.cssFloat = "none";
		this._panelBody.style.fontFamily = this.fontBody;
		this._panelBody.style.overflow = "auto";
		this._panelBody.style.styleFloat = "none";
		this._panelBody.style.width = "100%";

		this._panelButtons = document.createElement("div");
		this._panelButtons.className = "buttons";
		this._panelButtons.style.borderTop = "1px solid #000";
		this._panelButtons.style.clear = "both";
		this._panelButtons.style.cssFloat = "none";
		this._panelButtons.style.fontFamily = this.fontFooter
		this._panelButtons.style.marginTop = "5px";
		this._panelButtons.style.paddingTop = "3px";
		this._panelButtons.style.styleFloat = "none";
		this._panelButtons.style.textAlign = "right";
		this._panelButtons.style.width = "100%";

		this._panelFooter = document.createElement("div");
		this._panelFooter.className = "footer";
		this._panelFooter.style.clear = "both";
		this._panelFooter.style.cssFloat = "none";
		this._panelFooter.style.fontFamily = this.fontFooter;
		this._panelFooter.style.fontSize = ".9em";
		this._panelFooter.style.styleFloat = "none";
		this._panelFooter.style.width = "100%";

		this._panelHeader = document.createElement("div");
		this._panelHeader.className = "header";
		this._panelHeader.style.clear = "both";
		this._panelHeader.style.cssFloat = "none";
		this._panelHeader.style.fontFamily = this.fontHeader;
		this._panelHeader.style.fontSize = "1.2em";
		this._panelHeader.style.styleFloat = "none";
		this._panelHeader.style.width = "100%";

		oModalWindow.appendChild(oControl);
		oModalWindow.appendChild(this._panelHeader);
		oModalWindow.appendChild(this._panelBody);
		oModalWindow.appendChild(this._panelButtons);
		oModalWindow.appendChild(this._panelFooter);

		var scope = this;
		this._panelBtnClose.onclick = function() { scope.close.apply(scope); }
		this._panelBtnNext.onclick = function() { scope.forward.apply(scope); }
		this._panelBtnPrev.onclick = function() { scope.back.apply(scope); }

		this._panel.appendChild(oModalWindow);

		return;
	},

	/**
	* @private
	* @method      _panelCtrlClose
	* @description Controls the close button in the panel header
	* @returns {void}
	*/
	_panelCtrlClose: function() {
		this._panelBtnClose.style.visibility = (this.allowClose ? "visible" : "hidden");
		return;
	},

	/**
	* @private
	* @method      _panelCtrlPaging
	* @description Controls the paging buttons in the panel header
	* @returns {void}
	*/
	_panelCtrlPaging: function() {
		if (this._panelPages.length > 1) {
			this._panelBtnPrev.style.backgroundColor = (this._panelPageIndex > 0 ? "transparent" : "#ccc");
			this._panelBtnPrev.style.border = "1px solid " + (this._panelPageIndex > 0 ? "#000" : "#999");
			this._panelBtnPrev.style.color = (this._panelPageIndex > 0 ? "#000" : "#999");
			this._panelBtnPrev.style.visibility = (this.allowPaging ? "visible" : "hidden");

			this._panelBtnNext.style.backgroundColor = (this._panelPageIndex < (this._panelPages.length - 1) ? "transparent" : "#ccc");
			this._panelBtnNext.style.border = "1px solid " + (this._panelPageIndex < (this._panelPages.length - 1) ? "#000" : "#999");
			this._panelBtnNext.style.color = (this._panelPageIndex < (this._panelPages.length - 1) ? "#000" : "#999");
			this._panelBtnNext.style.visibility = (this.allowPaging ? "visible" : "hidden");
		}
		return;
	},

	/**
	* @private
	* @property    _panelFooter
	* @description Footer of the panel
	* @type {Node}
	*/
	_panelFooter: null,

	/**
	* @private
	* @method      _panelFormInitialize
	* @description Focuses on the first non-hidden input in the form.
	* @returns {void}
	*/
	_panelFormInitialize: function() {
		var focused = false;
		var oForms = this._panelBody.getElementsByTagName("form");
		if (oForms && oForms.length > 0) {
			var oForm = oForms.item(0);
			if (!oForm) { return;}
			var scope = this;
			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) { scope._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) { scope._panelFormInput_onKeyUp.apply(scope, [e]); }
						break;
				}
			}
		}

		if (!focused && this._panelButtons && this._panelButtons.hasChildNodes) {
			var c = 0;
			while (!focused && c < this._panelButtons.childNodes.length) {
				var oChild = this._panelButtons.childNodes[c];
				if ((oChild.tagName.toLowerCase() == "input" || oChild.tagName.toLowerCase() == "button") && oChild.style.visibility != "hidden") { oChild.focus(); focused = true; }
				c++;
			}
		}
		return;
	},

	/**
	* @private
	* @method      _panelFormInput_onKeyUp
	* @description Handle key press
	* @returns {void}
	*/
	_panelFormInput_onKeyUp: function(e, self) {
		e = e || window.event;
		var iKey = e.keyCode || e.which;
		switch (iKey) {
			case (8): break;											//Backspace
			case (9): break;											//Tab
			case (13): break;											//Enter
			case (27): this._panelBtnClose.click(); break;								//ESC
			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): if (window.event) { window.event.returnValue = null; } else { e.preventDefault(); } break;	//F12
			case (127): break;											//Delete
		}
	},

	/**
	* @private
	* @method      _panelFormPost
	* @description 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}
	* @param {Node|String} oForm	The form being processed.
	* @param {Object} oParam	An object with name and value properties.
	*/
	_panelFormPost: function(oForm, oParam) {
		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("&");
			}
			this._xhrRequest.open(oForm.method, oForm.action, this.async);
			this._xhrRequest.setRequestHeader("Cache-Control", "no-store, no-cache, must-revalidate");
			if (oForm.method.toUpperCase() == "POST") {
				this._xhrRequest.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
				if (this._xhrRequest.overrideMimeType) { this._xhrRequest.overrideMimeType("text/xml"); }
			}
			this._xhrMonitor(this._xhrRequest, this);
			this._xhrRequest.send(qs);
		} catch(e){
			this._xhrOnFailure();
		}
		return;
	},

	/**
	* @private
	* @property    _panelHeader
	* @description Header of the panel
	* @type {Node}
	*/
	_panelHeader: null,
	
	/**
	* @private
	* @property    _panelPageIndex
	* @description Index of page currently displayed in the panel
	* @type {integer}
	* @default 0
	*/
	_panelPageIndex: 0,

	/**
	* @private
	* @property    _panelPages
	* @description Set of pages loaded into the modal window
	* @type {Array}
	*/
	_panelPages: new Array(),

	/**
	* @private
	* @method      _panelSubmit
	* @description Event handler to process a form submit in the modal window.
	* @returns {void}
	* @param {Event} e	The browser event that was triggered
	* @param {Node} oForm	The form being processed.
	*/
	_panelSubmit: function(e, sFormId) {
		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); }
		this._panelFormPost(document.getElementById(sFormId), oButton);
		return;
	},

	/**
	* @private
	* @property    _timerIdXhr
	* @description The interval id of the XHR polling interval.
	* @type {Number}
	*/
	_timerIdXhr: null,

	/**
	* @private
	* @method      _xhrAbort
	* @description Method to terminate a XHR call.
	* @returns {void}
	* @param {XmlHttpRequest} xhr	The connection object returned by asyncRequest.
	*/
	_xhrAbort: function(xhr) {
		if (xhr.readyState != 4 && xhr.readyState != 0) {
			window.clearInterval(this._timerIdXhr);
			xhr.abort();
			this.cancel(xhr);
		}
		return;
	},

	/**
	* @private
	* @method      _xhrEvaluate
	* @description Customizable function used to evaluate the response stream.
	* @returns {void}
	*/
	_xhrEvaluate: function(xhr) {
		if (!xhr || !xhr.responseText) { this._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") {
		// this.close() } else if (o.action == "abort") { this.abort(); }
		return;
	},

	/**
	* @private
	* @method      _xhrMonitor
	* @description Creates a timer that polls the XmlHttpRequest object every 50 milliseconds
	* @returns {void}
	* @param {XmlHttpRequest} xhr	The XmlHttpRequest object
	* @param {Object} obj		This object. Used to maintain scope.
	*/
	_xhrMonitor: function(xhr, obj) {
		var scope = this;
		this._timerIdXhr = window.setInterval(function() { if (xhr && xhr.readyState == 4) { window.clearInterval(scope._timerIdXhr); scope._xhrOnReady(xhr, obj); } }, 50);
		if (obj.timeout) { window.setTimeout(function() { scope._xhrAbort(xhr); }, obj.timeout); }
		return;
	},

	/**
	* @private
	* @property    _xhrMsXmlHttp
	* @description Array of XmlHttpRequest object types for Internet Explorer
	* @type {Array}
	*/
	_xhrMsXmlHttp: new Array("MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"),

	/**
	* @private
	* @property    _xhrOnFailure
	* @description 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}
	* @param {XmlHttpRequest} xhr	The XmlHttpRequest object
	*/
	_xhrOnFailure: function(xhr) {
		if (this.onFail) { this.onEnd = this.onFail; }
		this.reset();
		if (xhr && xhr.status && xhr.statusText) {
			this._panelAddPage(xhr.status, xhr.statusText);
			this.show();
			var scope = this;
			window.setTimeout(function(){ scope.end(); }, (this.errorMessageTimeout * 1000));
		} else {
			this.end(xhr);
		}
		return;
	},

	/**
	* @private
	* @method      _xhrOnReady
	* @description Called when the readyState of the XHR object changes to "Complete".
	* @returns {void}
	* @param {XmlHttpRequest} xhr	The XmlHttpRequest object
	* @param {Object} scope
	*/
	_xhrOnReady: function(xhr, scope) {
		if (xhr) {
			if (xhr.status == 200) {
				scope._xhrOnSuccess(xhr);
			} else {
				scope._xhrOnFailure(xhr);
			}
		}
		return;
	},

	/**
	* @private
	* @method      _xhrOnSuccess
	* @description Event handler to process the AJAX Response object
	* @returns {void}
	* @param {XmlHttpRequest} xhr	The XmlHttpRequest object
	*/
	_xhrOnSuccess: function(xhr) {
		this._xhrEvaluate(xhr);
		var oBody = document.createElement("div");
		oBody.innerHTML = xhr.responseText;
		this.reset();
		this._panelAddPage();
		var iLastIndex = this._panelPages.length - 1;
		var oPage = this._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 = this;
		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) { scope._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;

		this.show(iLastIndex);
		return;
	},

	/**
	* @private
	* @property    _xhrRequest
	* @description The XmlHttpRequest object used by the Panel
	* @type {XmlHttpRequest}
	*/
	_xhrRequest: null
};
