/* -------------------------------------------------------------------------
	FILE:		Panel.js
	PURPOSE:	Provides a client side form management
	DEPENDENCIES:	prototype.js		version 1.6.0.3

	Copyright 2007,2008,2009 (c) FlowTech Solutions, Inc.		(www.FlowTech-Solutions.com)

	Permission is hereby granted, free of charge, to any person obtaining
	a copy of this software and associated documentation files (the
	"Software"), to deal in the Software without restriction, including
	without limitation the rights to use, copy, modify, merge, publish,
	distribute, sublicense, and/or sell copies of the Software, and to
	permit persons to whom the Software is furnished to do so, subject to
	the following conditions:

	The above copyright notice and this permission notice shall be
	included in all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
	LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
	OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
	WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


	Attributes required:	id, name, maxlength
			recommended:	size, title

	ATTRIBUTE							DESCRIPTION
	---------------				------------------------------------------------
	validate
	required
	conditional
	hint

------------------------------------------------------------------------- */
var Panel = function()
{
	// Singleton class
	if (window.Panel) return window.Panel;

	/* -- Public  ------------------------------------------------------------- */
	var _public =
	{
		// Call during pageInitialize() to register ALL forms on the page
		initialize: function()
		{
			_private.loadStyleFile('script/css/Panel.css');
			 _private.registerForms();
			 return;
		},

		registerForm: function(form, force)
		{
			return(_private.registerForms(form, force));
		},

		// Public method to register a single element with form manager.
		registerElement: function(form, element)
		{
			_private.registerElement(form, element);
		},


		// -----   FORM-LEVEL ONLY METHODS	(hasChanged, getFormValues, setFocusToFirstFormField, submit, serializeForm)

			// Compares all form field values to original values to determine if changed
			hasChanged : function(formID)
			{
				if ( !(form = $(formID)) ) return null;
				var	elementValue, originalValue, element,
						elements = (arguments.length < 2) ? new Object(Form.getElements(form)) : $A(arguments).slice(1);
				for (var e = 0, l = elements.length; e < l; ++e)
				{
					element = $(elements[e]);
					if (element)
					{
						elementValue = (element.hasClassName('PanelFieldHint')) ? '' : _private.getValueById(element);
						if ( typeof(element.originalValue) == 'undefined' ) continue;
						originalValue = ( Object.isArray(element.originalValue) ) ? element.originalValue.join(',') : element.originalValue;
						elementValue = ( Object.isArray(elementValue) ) ? elementValue.join(',') : elementValue;
						if (originalValue != elementValue)
						{
							return (true);
						}
					}
				}
				return(false);
			},

			// This collects the values of all elements in a form and returns a string suitable for posting
			getFormValues : function(formID)
			{
				var form = $(formID);
				if (!form) return false;	// ignore request if invalid form
				return (_private.serializeForm(formID));
			},

			// Resets form values to initial values when first loaded
			resetForm :  function(formID)
			{
				return (Panel.reset(formID));
			},

			// Clear form values
			clearForm : function(formID)
			{
				return Panel.clear(formID);
			},

			// sets focus to the 1st non-hidden form field in the specified form
			setFocusToFirstFormField : function(formID)
			{
				var form = $(formID);
				if (!form) return(false);
				//form.focusFirstElement();	// Prototype does not account for readOnly
				var elements = new Object(Form.getElements(form));
				var firstElement = _private.nextEnterableElement(elements[elements.length-1]);
				if (!firstElement) return(false);
				window.scrollTo(0,document.body.scrollHeight);
				firstElement.focus();
				return true;
				/* TO BE REMOVED
					for(i=0; i < elements.length; i++)
					{
						if (elements[i].type != "hidden" && !elements[i].disabled )
						{
							window.scrollTo(0,document.body.scrollHeight);
							elements[i].focus();
							return true;
						}
					}
				*/
			},

			// Validate client side and do a normal HTML post on the form
			submit : function(formID, submitToUrl)
			{
				var form = formID ? $(formID) : document.forms.length > 0 ? document.forms[0] : null;
				if (!form) return(false);

				// Validate the form data entered
				if ( !_public.isValid(form) )			return(false);

				form.method = 'post';
				if (submitToUrl) form.action = submitToUrl;

				// HTML post on the form
				form.submit();
				return(true);
			},

			submitAjax : function(formID,transID,extendedProperties)
			{
				var form = formID ? $(formID) : document.forms.length > 0 ? document.forms[0] : null;
				if (!form) return(false);

				// Validate the form data entered
				if ( !_public.isValid(form) )			return(false);

				// Initiate the Ajax posting of the form
				_private._postForm(form,transID,extendedProperties);
				return (true);
			},

			// Retrieves a query string object of all the form elements (suitable for posting)
			serializeForm : function(form, returnObject)
			{
				return(_private.serializeForm(form, returnObject));
			},


		// -----   FORM OR FORM FIELD LEVEL METHODS	(enable, disable, reset, clear, isValid, show, hide, readOnly, enterable, resetOriginalValue)

			// Enables all fields in a form (if 1st argument is a form) or specified fields
			enable : function(formID)
			{
				var form = $(formID);
				if (!form) return false;
				var element, elements = (form.tagName.toLowerCase()=='form') ? new Object(Form.getElements(form)) : $A(arguments);
				for (i=0,l=elements.length; i<l; ++i)
				{
					element = $(elements[i]);
					if (element)
					{
						element.removeClassName('PanelFieldDisable');
						element.disabled = false;
					}
				}
				return true;
			},

			// Disables all fields in a form (if 1st argument is a form) or specified fields
			disable : function(formID)
			{
				var form = $(formID);
				if (!form) return false;
				var element, elements = (form.tagName.toLowerCase()=='form') ? new Object(Form.getElements(form)) : $A(arguments);
				for (i=0,l=elements.length; i<l; ++i)
				{
					element = $(elements[i]);
					if (element)
					{
						element.addClassName('PanelFieldDisable');
						element.disabled = true;
					}
				}
				return true;
			},

			// Resets all or specified form fields to their original values (arguments 2 to n s/b element IDs)
			reset : function(formID)
			{
				var form = $(formID);
				if (!form) return false;
				var element, elements = (form.tagName.toLowerCase()=='form') ? new Object(Form.getElements(form)) : $A(arguments);
				for (var i=elements.length-1; i>=0; i--)
				{
					element = $(elements[i]);
					if (element)
					{
						element.title = element.saveTitle;
						element.removeClassName('PanelFieldFormatError');
						element.removeClassName('PanelFieldRequiredError');
						element.removeClassName('PanelFieldHint');
						_private.setValueById(element,element.originalValue);

						// Check for HINT attribute
						if (element.hint!='' && !element.hasFocus()) _private.applyHint(element, element.hint);
					}
				}
				return true;
			},

			// Clears ALL/specified form fields and re-establishes any hint that may apply (arguments 2 to n s/b element IDs)
			clear : function(formID)
			{
				var form = $(formID);
				if (!form) return false;
				var element, elementType, elementTagName;
				try
				{
					var elements = (form.tagName.toLowerCase()=='form') ? new Object(Form.getElements(form)) : $A(arguments);
					for (var i=elements.length-1; i>=0; i--)
					{
						element = $(elements[i]);
						if (element)
						{
							element.title = element.saveTitle;
							element.removeClassName('PanelFieldFormatError');
							element.removeClassName('PanelFieldRequiredError');
							element.removeClassName('PanelFieldHint');
							elementTagName = element.tagName.toLowerCase();
							elementType = (elementTagName == 'select') ? ((element.type == 'select-one') ? 'selectOne' : 'selectMany') : element.type;
							if ( typeof(elementType) == 'undefined' || elementType == null ) continue;

							// Don't want to take away the button label, so don't clear a button!
							if ("button,reset,submit,file".indexOf(elementType.toLowerCase()) < 0) element.clear();
							// Check for HINT attribute
							if (element.hint!='' && !element.hasFocus())		_private.applyHint(element, element.hint);
						}
					}
				}
				catch(e)
				{
					_private.logError({name:'_private.clear()', error:e, form:form, element:element});
				}

				return true;
			},

			// Performs any form and field validations specified to determine if form is valid
			isValid : function (formID)
			{
				var valid = true;
				var form = formID ? $(formID) : document.forms.length > 0 ? document.forms[0] : null;
				if (!form || (form.tagName.toLowerCase()=='form' && !form.panel) || (form.form && !form.form.panel) ) return(false);

				var element, elementValue;
				var elements = (form.tagName.toLowerCase()=='form') ? new Object(Form.getElements(form)) : $A(arguments);
				for (var i=elements.length-1; i>=0; i--)
				{
					element = $(elements[i]);
					if (!element || element.disabled) continue;
					if ("button,reset,submit,file".indexOf(element.type.toLowerCase()) > 0) continue;
					if (!element.id || !element.name) continue;
					elementValue = _private.getValueById(element);
					element.removeClassName('PanelFieldFormatError');
					if ( element.validate && (typeof(elementValue)!='undefined' && elementValue!=null && elementValue!='')
						&& !_private.performValidate(element, element.validate, element.conditional, elementValue) )
						valid = false;
					if (element.required && !_private.performRequired(element, element.required, element.conditional, elementValue))
						valid = false;
				}
				return(valid);
			},

			// Makes the specified form or fields visible(true/false)
			show : function (formID)
			{
				var form = $(formID);
				if (!form) return false;
				if (arguments.length < 2)
				{
					form.show();
				}
				else
				{
					elms = $A(arguments).slice(1);
					for (i=0,l=elms.length; i<l; ++i)
					{
						if ($(elms[i])) $(elms[i]).show();
					}
				}
				return true;
			},

			// Makes the specified form or fields visible(true/false)
			hide : function (formID)
			{
				var form = $(formID);
				if (!form) return false;
				if (arguments.length < 2)
				{
					form.hide();
				}
				else
				{
					elms = $A(arguments).slice(1);
					for (i=0,l=elms.length; i<l; ++i)
					{
						if ($(elms[i])) $(elms[i]).hide();
					}
				}
				return true;
			},

			// Makes the form or select elements read-only via style & readOnly property
			//	Disabled fields are NOT changed to read only, they must be enabled first, then apply read only
			//	Elements specified are by ID ONLY, element names are NOT used.
			//		so for radio buttons, or groups of checkboxs, all element IDs must be provided
			readOnly : function(formID)
			{
				if ( !(form = $(formID)) ) return null;
				var element, elements = (arguments.length < 2) ? new Object(Form.getElements(form)) : $A(arguments).slice(1);
				for (var i=elements.length-1; i>=0; i--)
				{
					element = $(elements[i]);
					if (!element || element.disabled) continue; // || (!Object.isUndefined(element.readOnlyValue && element.readOnlyValue) continue;
					element.readOnlyValue = _private.getValueByName(element.name||element.ID)||'';
					element.addClassName('PanelFieldReadOnly');
					element.saveTabIndex = element.tabIndex;
					element.tabIndex = -1;
					element.readOnly = true;
					//if ($('debug')) $('debug').innerHTML += 'Event: readOnly('+(element.id||element.tagName)+')&nbsp;&nbsp;readOnlyValue='+element.readOnlyValue + '<br/>';
				}
				return true;
			},

			// opposite of readOnly, removes the style, makes a read-only element enterable
			enterable : function(formID)
			{
				if ( !(form = $(formID)) ) return null;
				var element, elements = (arguments.length < 2) ? new Object(Form.getElements(form)) : $A(arguments).slice(1);
				for (var i=elements.length-1; i>=0; i--)
				{
					element = $(elements[i]);
					if (element && element.readOnly && !element.disabled)
					{
						element = $(elements[i]);
						element.readOnlyValue = null;
						element.readOnly = false;
						element.removeClassName('PanelFieldReadOnly');
						element.tabIndex = (element.saveTabIndex && element.saveTabIndex >= 0) ? element.saveTabIndex : '';
					}
				}
				return true;
			},


			// Resets all or specified form fields to their original values (arguments 2 to n s/b element IDs)
			resetOriginalValue : function(formID)
			{
				var form = $(formID);
				if (!form) return false;
				var element, elements = (arguments.length < 2) ? new Object(Form.getElements(form)) : $A(arguments).slice(1);
				for (var i=elements.length-1; i>=0; i--)
				{
					element = $(elements[i]);
					if (element)
					{
						elementValue = _private.getValueById(element);
						if (element.hasClassName('PanelFieldHint'))
							elementValue = '';

						element.originalValue = elementValue;
						if (element.readOnlyValue != null) element.readOnlyValue = elementValue;
					}
				}
				return true;
			},



		// -----   FIELD ONLY LEVEL METHODS	(isRequired, isReadOnly, isEnterable, getValueById, setValueById ...
			//														getValueByName, setValueByName, getOriginalValue, hasFocus,
			//														readOnlyByName, enterableByName)

			isRequired : function(elementID)
			{
				var element = $(elementID);
				return ( (element && element.required) ? true : false );
			},

			isReadOnly : function(elementID)
			{
				var element = $(elementID);
				return ( (element && element.readOnly) ? true : false );
			},

			isEnterable : function(elementID)
			{
				return ( !Panel.isReadOnly(elementID) );
			},

			// Retrieves the value(s) associated with a group of elements by a given name
			//		Disabled fields are NOT retreived
			getValueByName : function(elementName,returnObject,subtypeSensitive)
			{
				return(_private.getValueByName(elementName,returnObject,subtypeSensitive));
			},

			// Retrieves a value of the specific element given it's ID
			//		Disabled fields ARE retrieved
			getValueById : function(elementID,returnObject)
			{
				return(_private.getValueById(elementID,returnObject));
			},

			// Sets the named group element(s) to the specified value
			//		Disabled fields are NOT set
			setValueByName : function(elementName,elementValue)
			{
				return(_private.setValueByName(elementName,elementValue));
			},

			// Sets the specific element(identified by ID) to the specified value
			//		Disabled fields ARE set
			setValueById : function(elementID,elementValue)
			{
				return(_private.setValueById(elementID,elementValue));
			},

			// retrieves the original value of the element prior to user interaction.
			getOriginalValue : function(elementID)
			{
				var element = $(elementID);
				return (element.originalValue);
			},

			hasFocus : function(elementID)
			{
				var element = $(elementID);
				return (element.hasFocus && element.hasFocus())
			},

			readOnlyByName : function(elementName,subtypeSensitive)
			{
				subtypeSensitive = (typeof(subtypeSensitive)=='undefined' || subtypeSensitive==null) ? true : false;
				var element, elements = document.getElementsByName(elementName);
				if (!elements || elements.length==0)
				{	// Could not find the group by the name give, so assume the name given was an ID
					// and attempt to use the name of the element assuming an ID was provided.
					if ( !(element = $(elementName)) )	return(false);
					elementName = element.name;
					elements = document.getElementsByName(elementName);
				}
				// Could not identify any elements by the name (or the element by ID's name)
				if (!elements || elements.length==0) return(false);

				// Collect some information about the element (tag name, type, initial value
				element = $(elements[0]);
				var elementTagName = element.tagName.toLowerCase();
				var elementType = (elementTagName == 'select') ? ((element.type == 'select-one') ? 'selectOne' : 'selectMany') : element.type;
				var elementSubtype = element.readAttribute('subtype') || '';

				// Cycle through each of the elements in the named group
				for (var e=0; e<elements.length; e++)
				{
					element = $(elements[e]);
					// ignore any elements not in the same subtype
					var subtype = element.readAttribute('subtype') || '';
					if ( subtypeSensitive && (elementSubtype != subtype) )	continue;
					_public.readOnly(element.form,element);
				}
				return(true);
			},

			// opposite of readOnly, removes the style, makes a read-only element enterable
			enterableByName : function(elementName,subtypeSensitive)
			{
				subtypeSensitive = (typeof(subtypeSensitive)=='undefined' || subtypeSensitive==null) ? true : false;
				var element, elements = document.getElementsByName(elementName);
				if (!elements || elements.length==0)
				{	// Could not find the group by the name give, so assume the name given was an ID
					// and attempt to use the name of the element assuming an ID was provided.
					if ( !(element = $(elementName)) )	return(false);
					elementName = element.name;
					elements = document.getElementsByName(elementName);
				}
				// Could not identify any elements by the name (or the element by ID's name)
				if (!elements || elements.length==0) return(false);

				// Collect some information about the element (tag name, type, initial value
				element = $(elements[0]);
				var elementTagName = element.tagName.toLowerCase();
				var elementType = (elementTagName == 'select') ? ((element.type == 'select-one') ? 'selectOne' : 'selectMany') : element.type;
				var elementSubtype = element.readAttribute('subtype') || '';

				// Cycle through each of the elements in the named group
				for (var e=0; e<elements.length; e++)
				{
					element = $(elements[e]);
					// ignore any elements not in the same subtype
					var subtype = element.readAttribute('subtype') || '';
					if ( subtypeSensitive && (elementSubtype != subtype) )	continue;
					_public.enterable(element.form,element);
				}
				return(true);
			},

			// Set the fields as invalid (any number of arguments, each argumnet is either an element or ID of an element)
			setValid : function()
			{
				var elements =  $A(arguments);
				elements.each(function(elmID,ndx){
					var elm = $(elmID);
					if (elm)
					{
						elm.title = elm.saveTitle || '';
						elm.removeClassName('PanelFieldFormatError');
						elm.removeClassName('PanelFieldRequiredError');
					}
				});
			},

			// Set the fields as invalid
			setInvalid : function(elementID,errmsg)
			{
				if ( !(element=$(elementID)) ) return;
				element.addClassName('PanelFieldRequiredError');
				//if (!element.saveTitle) element.saveTitle = element.title||'';
				element.title = 'Error: '+errmsg;
			},

			setHint : function(elementID, hintValue)
			{
				var element = $(elementID);
				if (!element) return (false);
				hintValue = hintValue || '';
				element.writeAttribute('hint',hintValue);
				element.hint = hintValue;
				if (hintValue != '')
					element.addClassName('PanelFieldHint');
				else
					element.removeClassName('PanelFieldHint');
				return ( _private.applyHint(element, hintValue) );
			}

	};


	/* -- Private ------------------------------------------------------------- */
	var _private =
	{
		// Validation: regular expressions and messages
		validation:	{	currency:			{token:/(^\d{0,9}\.\d{0,2}$)|(^\d{0,9}$)/, message:'Currency must be in the form 999999999.99 or 999999999. Must be positive.'},
							rate:					{token:/(^\d{0,9}\.\d{0,3}$)|(^\d{0,9}$)/, message:'Percentage must be in the form 999.999 or 999. Must be positive.'},
							adjustment:		{token:/(^[-+]?\d{0,9}\.\d{0,2}$)|(^\d{0,9}$)/, message:'Currency must be in the form +-999999999.99 or +-999999999.'},
							unitprice:			{token:/(^[-+]?\d{0,9}\.\d{0,4}$)|(^\d{0,9}$)/, message:'Unit Price must be in the form 999999999.9999 or 999999999. Must be positive.'},
							email:				{token:/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*\.(\w{2}|(com|net|org|edu|int|mil|gov|arpa|biz|aero|name|coop|info|pro|museum))$/i, message:'Email must be in the form  target@domain.scope'},
							email2:				{token:/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@[-a-z0-9\'_]+(\.[-a-z0-9\'_]+)*(\:[0-9]+)?$/i , message:'Email must be in the form  target@domain.scope'},
							hhmmss:			{token:/^((0?[1-9]|1[0-9]|2[0-3])[- :.]+[0-5]?[0-9]([- :.]+[0-5]?[0-9])?)$/, message:'Time must be in the format hh:mm or hh:mm:ss'},
							mmddyyyy:		{token:/^(((0?[1,3-9]|1[012])[- \/.](0?[1-9]|[12][0-9]|3[01]))|(0?[2][- \/.](0?[1-9]|[12][0-9])))[- \/.]((19|20)?[0-9]{2})$/, message:'Date must be in the format mm/dd/yyyy.'},
							phone:				{token:/^((\+\d{1,3}(-| )?\(?\d\)?(-| )?\d{1,3})|(\(?\d{2,3}\)?))(-| )?(\d{3,4})(-| )?(\d{4})(( x| ext)\d{1,5}){0,1}$/, message:'Phone must be in the form 999-999-9999 x999'},
							alpha:				{token:	/^[a-z A-Z]*$/, message:'Only alpha is accepted. a-z A-Z and space'},
							ALPHAnumeric:	{token:/^[A-Z0-9]*$/, message:'Only uppercase alphanumeric is accepted. A-Z 0-9'},
							alphanumeric:	{token:/^[a-z A-Z0-9]*$/, message:'Only alphanumeric is accepted. a-z A-Z 0-9 and space.'},
							integernumeric:	{token:	/^[-+]?[0-9]*$/, message:'Only integer numeric accepted. +-999999999'},
							floatnumeric:		{token:/^[-+]?[0-9]*\.?[0-9]+$/, message:'Only floating numeric is accepted. +-999999999.999999'},
							exponnumeric:	{token:/^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/, message:'Only floating numeric is accepted. +-999999999.999999'},
							positiveinteger:	{token:	/^[0-9]*$/, message:'Only positive integer numeric accepted. max 999999999'},
							positivefloat:		{token:/^[0-9]*\.?[0-9]+$/, message:'Only positive floating numeric is accepted. max 999999999.999999'},
							zip:					{token:/(^\d{5}$)|(^\d{5}-\d{4}$)/, message:'Zip code format 99999 or 99999-9999'},
							uri:					{token:/^(http:\/\/www.|https:\/\/www.|ftp:\/\/www.|www.|http:\/\/[\w]+.|https:\/\/[\w]+.|[\w]+.){1}([\w]+)(.[\w]+){1,2}$/, message:'Proper URL expected http, www, ftp, etc..'},
							creditcard:		{token:/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/, message:'Credit card number'},
							visa:					{token:/^4[0-9]{12}(?:[0-9]{3})?$/, message:'Visa card number expected'},
							mastercard:		{token:/^5[1-5][0-9]{14}$/, message:'Mastercard number expected'},
							americanexpress:{token:/^3[47][0-9]{13}$/, message:'American Express card number expected'},
							dinersclub:		{token:/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/, message:'Diners Club card number expected'},
							discover:			{token:/^6(?:011|5[0-9]{2})[0-9]{12}$/, message:'Discover card number expected'},
							jcb:					{token:/^(?:2131|1800|35\d{3})\d{11}$/, message:'JCB card number expected'},
							general:			{token:/^[a-zA-Z0-9 *\'_&?@!=+.,:;^#$%/\-\{\}\(\)\s\[\]]*$/, message:'Characters accepted include: a-z A-Z 0-9 space ! ? = + - _ : ; . , ^ $ & @ % / # ( ) { }'},
							code:				{token:/^[A-Z0-9 &+.:#\-]*$/, message:'Characters accepted include: A-Z 0-9 space +-.:#&'},
							partnbr:			{token:/^[A-Z0-9 &+.:#\-\(\)]*$/, message:'Characters accepted include: A-Z 0-9 space +-.:#&()'},
							partrev:			{token:/^[A-Z0-9]*$/, message:'Only alphanumeric is accepted. A-Z 0-9'},
							numericspace:	{token:	/^[0-9 ]*$/, message:'Only digits 0 - 9 and space may be used'},
							dimension:		{token:/^([.0-9]+\ *(IN|FT|CM|MM|FEET|DIA)?)$/i, message:'Dimension must be in the form 99999.9999um or 99999um where um is a unit of measure (in,ft,yd,mm,cm,m)  Inch is assumed.'},
							thickness:			{token:/^([.0-9]+\ *(IN|FT|CM|MM|GA|FEET|DIA)?)$/i, message:'Thickness must be in the form 999999999.9999um or 999999999um where um is a unit of measure (ga,in,mm,cm) Inch is assumed.'},
							filename:			{token:/(^(([\w]:)|(\\{2}[a-zA-Z0-9_!#$%&+\-\(\){}]+))((\\[^\\*?:]+)*|([^\\<*?:]+))[.]([a-zA-Z0-9_!#$%&+\-\(\){}]*)$)/, message:'Filename must be a full filename e.g. C:\\\\folder\\\\file.ext'},
							html:				{token:/<([A-Z][A-Z0-9]*)\b[^>]*>(.*?)<\/\1>/, message:''}
					},
		lastFocusElement: null,
		debugmode:		true,
		debugwindow:	null,
		jsLoaded:		'',


		// Register all forms on a page (with panel='true' attribute)
		registerForms : function(frms, force)
		{
			var form, f, l;
			try
			{
				frms = (frms) ? (Object.isArray(frms) ? frms : [frms]) : document.forms;
				for (f=0, l=frms.length; f<l; f++)
				{
					if ( !(form = $(frms[f])) ) return(false);
					if (force)	form.writeAttribute('panel','true');		// forces registration of all elements
					form.panel = (form.readAttribute('panel')||'').toLowerCase()=='true' ? true : false;
					if ( !form.panel )	continue;

					// Loop through elements in the form
					//var formElements = new Object(Form.getElements(form));
					var formElements = Form.getElements(form);
					for (i=0, il=formElements.length; i<il; ++i)
					{
						_private.registerElement(form, formElements[i]);
					}
				}
			}
			catch(err)
			{
				_private.logError({ name:'ERROR', description:'Panel._private.registerForms() method failure.', error:err });
			}

			// Fire a custom event to let the application know all the forms has been registered
			$(document).fire("Panel:initialized");
			return(true);
		},

		//
		registerElement: function(form, elm)
		{
			form = $(form);
			element = $(elm);
			var elementType = element.readAttribute('type') || '';
			var elementTagName = element.tagName.toLowerCase();
			element.removeClassName('PanelFieldHint');
			element.removeClassName('PanelFieldFormatError');
			element.removeClassName('PanelFieldRequiredError');

			// If element type is hidden, we dont need to load any UI behaviors on it.
			if (elementType == 'hidden') return;

			// Retrieve the actual value of the element (could be an array), radios are returned as a single value (value by name),
			// Hint value is never returned
			var elementValue = _private.getValueById(element);

			// Retain the element value as the original value
			element.originalValue = elementValue;
			element.readOnly = !!(element.readAttribute('readonly')) || false;
			element.required = !!(element.readAttribute('required'));
			element.validate = element.readAttribute('validate') || '';
			element.conditional = element.readAttribute('conditional') || '';
			element.saveTitle = element.saveTitle || element.title || '';

			// Load drop down Select element with options
			if (elementTagName == 'select')	_private.loadLovSelectElement(element);

			// Establish main event listeners
			element.focused = false;
			element.hasFocus = function() { return( this.focused || false ); };
			element.stopObserving('blur', _private.onBlur);
			element.stopObserving('focus', _private.onFocus);
			element.observe('blur', _private.onBlur);
			element.observe('focus', _private.onFocus);
			if (Prototype.Browser.IE )
			{
				if (elementType == 'radio' || elementType == 'checkbox')
				{
					element.stopObserving('click', _private.onClick);
					element.observe('click', _private.onClick);
				}
				if (elementTagName == 'select' )
				{
					element.stopObserving('change', _private.onChange);
					element.observe('change', _private.onChange);
				}
			}

			// Check for ReadOnly (via classname PanelFieldReadOnly) and
			if (element.readOnly || element.hasClassName('PanelFieldReadOnly'))	Panel.readOnly(element.form,element);

			// Apply the Hint if specified and it applies  (INPUT-TEXTBOX only)
			element.hint = element.readAttribute('hint') || '';
			if (element.hint!='' && !element.hasFocus())	_private.applyHint(element, element.hint);

			// Submit the form when a return is entered on this element
			element.onenter = element.readAttribute('onenter') || '';
			if (element.onenter != '' && elementType == 'text')
			{
				element.stopObserving('keydown', _private.onEnter);
				element.observe('keydown', _private.onEnter);
			}
		},

		// unregister element from form manager, well actually stop all form
		// manager event listeners.
		unregisterElement: function(argElement)
		{
			var element = $(argElement);
			element.stopObserving('blur', _private.onBlur);
			element.stopObserving('focus', _private.onFocus);
			element.stopObserving('click', _private.onClick);
			element.stopObserving('change', _private.onChange);
			element.stopObserving('keydown', _private.onReturn);
		},

		// special case Change listener for IE for select elements
		onChange: function(event)
		{
			return(_private.onFocus(event));
		},

		// special case Click listener for IE for checkbox and radio elements
		onClick: function(event)
		{
			return(_private.onFocus(event));
		},

		// Main listener routine for when a form field receives focus
		onFocus: function(evnt)
		{
			var element = evnt.element();
			if (!element || typeof(element.originalValue) == 'undefined') return(true);
			try
			{
				// If the element is read only force the value back to what it was when established as read-only
				if (element.readOnly)
				{
					Event.stop(evnt);
					element.focused = false;
					var elementType = element.readAttribute('type') || '';
					switch (elementType)
					{
						case 'checkbox':
							element.checked = (element.value == element.readOnlyValue) ? true : false;
							//_private.setValueById(element.id, element.readOnlyValue || '');
							break;
						case 'radio':
							_private.setValueByName(element.name, element.readOnlyValue || '');
							break;
					}
					//if (_private.lastFocusElement)	_private.lastFocusElement.focus();
					return(false);
				}
				element.focused = true;
				var elementValue = _private.getValueById(element);

				// If a Hint is currently in effect, remove it
				if (element.hasClassName('PanelFieldHint'))
				{
					_public.clear(element);
					element.removeClassName('PanelFieldHint');
				}
				if ( typeof(element.select) == 'function') element.select();

				// Remove any prior error indications, we will reestablish if need be
				element.title = element.saveTitle;
				element.removeClassName('PanelFieldFormatError');
				element.removeClassName('PanelFieldRequiredError');
				_private.lastFocusElement = null;
			}
			catch(e)
			{
				_private.logError({name:'_private.onFocus()', error:e, element:element});
			}
			return(true);
		},


		// Main listener routine for when a form field looses focus
		onBlur: function(evnt)
		{
			var element = evnt.element();
			if (!element || typeof(element.originalValue) == 'undefined') return(true);
			try
			{
				element.focused = false;
				_private.lastFocusElement = element;
				var elementValue = _private.getValueById(element);

				// If client validation is on and the element has a validation to apply
				if ( element.form.panel )
				{
					element.title = element.saveTitle;
					element.removeClassName('PanelFieldFormatError');
					element.removeClassName('PanelFieldRequiredError');
					if(element.validate!='' && (typeof(elementValue)!='undefined' && elementValue!=null && elementValue!='')
							&& !_private.performValidate(element, element.validate, element.conditional, elementValue))
						return(false);
					if ( element.required ) _private.performRequired(element, element.required, element.conditional, elementValue);
				}

				// Apply the Hint if specified and it applies
				if (element.hint!='') _private.applyHint(element, element.hint);

			}
			catch(e)
			{
				_private.logError({name:'_private.onBlur()', error:e, element:element});
			}
			return(true);
		},

		// Apply the blank value if specified and the element does not already have a value
		applyBlankValue: function(element, blankValue)
		{
			if (!blankValue) blankValue = (element.blankValue && element.blankValue!='') ? element.blankValue : (element.readAttribute('blankvalue')||'');
			if (!blankValue || blankValue == '') return(false);

			// Retrieve the current value(s) for the element
			// If the element already has a value do NOT apply the blank value
			var elementValue = _private.getValueById(element);
			if (!element || element.disabled || Object.isArray(elementValue) || elementValue!='')	return(false);

			// Set the element value to the blank value (default value)
			var newValue = (blankValue.indexOf(',')>=0) ? blankValue.split(',') : blankValue;
			_private.setValueById(element,newValue);
			return(true);
		},

		// Apply the blank value if specified and the element does not already have a value
		applyHint: function(element, hint)
		{
			if ( !(element=$(element)) ) return(false);
			var	elementTagName = element.tagName.toLowerCase(),
					elementType = element.type;
			if ( elementTagName != 'input' || typeof(elementType) == 'undefined' || elementType == null || elementType != 'text') return(true);

			hint = hint || element.hint;
			if (element.hasClassName('PanelFieldHint'))
			{
				element.clear();
				element.removeClassName('PanelFieldHint');
			}
			if (!hint || hint == '') return(true);

			// Retrieve the current value(s) for the element.  If the element already has a value,
			//	is a grouped element or is not an Input-Text element type do NOT apply any hint
			var elementValue = _private.getValueById(element);
			if (element.hasFocus() || Object.isArray(elementValue) || (elementValue!='' && elementValue!=hint)) return(true);

			// Set the element value to the hint
			element.addClassName("PanelFieldHint");
			element.setValue(hint);
			return(true);
		},

		// Determine if a conditional applies (required, verify) if so execute
		conditional: function(element,conditional,action,elementValue)
		{
			if (!conditional || conditional=='') return true;
			var functionReturn = true;
			conditional = conditional.removeWhiteSpace();
			var condition, conditions = conditional.split(' ');
			for (var c=0; c<conditions.length && functionReturn; c++)
			{
				condition = conditions[c];
				try
					{
					functionReturn = eval(condition+'(element,elementValue,action)');
					}
				catch(e)
					{
					_private.logError({name:'_private.conditional()', description:'conditional attribute function errored out. '+' Element id="'+element.id+'", condition="'+condition+'", action="'+action+'"',	error:e});
					functionReturn = true;
					}
			}
			return(functionReturn);
		},

		// Applies validation as appropriate (disabled elements are not validated), returns false if validation fails
		//	element must be a single element while elementValue might be an array for grouped elements
		performValidate : function(element, validate, conditional, elementValue)
		{
			// Don't check if element is disabled or no value exists
			if (element.disabled || validate==null || validate=='' || elementValue==null || (!Object.isArray(elementValue) && elementValue=='') || elementValue=='' ) return true;
			if (Object.isArray(elementValue))
			{	// collection of data values is empty, dont check it
				var c=0;
				for (var i=0; i<elementValue.length; i++)
					{c += (elementValue[i] && elementValue[i] != '') ? 1 : 0;}
				if (c==0) return true;
				elementValue = elementValue.join('');
			}

			// Conditionally apply the validation
			if ( !_private.conditional(element,element.conditional,'validate',elementValue) ) return(true);

			// verify the data meets the required format.
			var validateToken = validate.trim().toLowerCase();
			var verified = false;
			try
			{
				verified = elementValue.search( _private.validation[validateToken].token ) == 0;
			}
			catch (e)
			{
				_private.logError({name:'_private.performValidate()', description:'validate attribute expression errored out. '+' Element id="'+element.id+'", validate="'+validate+'", value="'+elementValue+'"',	error:e});
				return false;
			}
			if (!verified)
			{
				$(document).fire("Panel:CSValidationFailure",{form:element.form,element:element});
				element.addClassName('PanelFieldFormatError');
				//element.saveTitle = element.title;
				element.title = 'Format Error: '+_private.validation[validateToken].message;
			}
			return verified;
		},

		// Applies requiredness as appropriate (disabled elements are not checekd), returns false if required but empty
		//	element must be a single element (uniquely identified by ID), elementValue may be an array
		performRequired : function(element, required, conditional, elementValue)
		{
			if (element.disabled || !required) return(true);
			// may be an array for grouped/subtyped elements
			elementValue = (elementValue==null) ? _private.getValueById(element,true,true) : elementValue;

			// Conditionally apply the requiredness
			if ( !_private.conditional(element,element.conditional,'required',elementValue) ) return(true);

			// determined the number of characters or number of values (for arrays)
			elementValue = (Object.isArray(elementValue)) ? elementValue.join('') : elementValue||'';
			if (elementValue != '' ) return(true);

			// The element value(s) did not meat the requiredness criteria
			element.addClassName('PanelFieldRequiredError');
			$(document).fire("Panel:CSRequiredFailure",{form:element.form,element:element});
			return(false);
		},


		// Assembles a list of form values sutiable for a form submit (supports custom behavior)
		serializeForm: function(form)
		{
			form = $(form);
			var	elements = Form.getElements(form),
					element, key, subkey, fullkey, formvalues={};

			for (var e=0, l=elements.length; e<l; ++e)
			{
				element = $(elements[e]);
				if (!element || !element.name || element.disabled) continue;

		        key = element.name;
				subkey = element.readAttribute('subtype') || '';
				fullkey = key + (subkey && subkey!='' ?  '_'+subkey : '');

				if (typeof(formvalues[fullkey]) == 'undefined')
					formvalues[fullkey] = _private.getValueByName(key,null,true);
			}
			return ( Object.toQueryString(formvalues) );
		},

		// Retrieves the value of an element group (by name)
		// All elements in the named group MUST be of the same element type  e.g. INPUT-TEXT
		getValueByName : function(elementName,returnObject,subtypeSensitive)
		{
			try
			{
				subtypeSensitive = (typeof(subtypeSensitive)=='undefined' || subtypeSensitive==null) ? true : false;
				var element, elements = document.getElementsByName(elementName);
				if (!elements || elements.length==0)
				{	// Could not find the group by the name give, so assume the name given was an ID
					// and attempt to use the name of the element assuming an ID was provided.
					if ( !(element = $(elementName)) )	return(null);
					elementName = element.name;
					elements = document.getElementsByName(elementName);
				}
				// Could not identify any elements by the name (or the element by ID's name)
				if (!elements || elements.length==0) return(null);

				// Collect some information about the element (tag name, type, initial value
				element = $(elements[0]);
				var elementTagName = element.tagName.toLowerCase();
				var elementType = (elementTagName == 'select') ? ((element.type == 'select-one') ? 'selectOne' : 'selectMany') : element.type;
				var elementSubtype = element.readAttribute('subtype') || '';

				// Cycle through each of the elements in the named group
				var elementValue, nameValue = [];
				for (var e=0; e<elements.length; e++)
				{
					element = $(elements[e]);
					// ignore any elements not in the same subtype
					var subtype = element.readAttribute('subtype') || '';
					if ( subtypeSensitive && (elementSubtype != subtype) )	continue;

					elementValue = (element.hasClassName('PanelFieldHint')) ? '' : _private.getValueById(element)||'';	// Prototype returns an array for multi-select elements
					elementValue = (elementType == 'selectMany' && Object.isArray(elementValue)) ? elementValue.join(',') : elementValue;
					elementValue = (elementType == 'checkbox' && element.checked) ? element.value : elementValue;
					elementValue = (elementType == 'radio' && element.checked) ? element.value : elementValue;;

					//if (!Object.isArray(nameValue))	nameValue = [ nameValue ];
					switch (elementType)
					{
					case 'radio':
						nameValue = (nameValue=='') ? elementValue : nameValue;
						break;
					default:
						nameValue.push(elementValue);
					}
				}

				// Return the array of values (returnObject is true) or a string string
				return( (returnObject ? nameValue : (Object.isArray(nameValue) ? nameValue.join(',') : nameValue)) );
			}
			catch(e)
			{
				_private.logError({name:'_private.getValueByName()', error:e, elementName:elementName, subtypeSensitive:subtypeSensitive, returnObject:returnObject});
			}
			return('');
		},

		// Retrieves the value of an element by it's ID	(multi-selects returns a comma separated list of values)
		getValueById : function(element,returnObject)
		{
			if ( !(element=$(element)) ) return '';
			try
			{
				var elementTagName = element.tagName.toLowerCase();
				var elementType = (elementTagName == 'select') ? ((element.type == 'select-one') ? 'selectOne' : 'selectMany') : element.type;
				var elementValue = '';
				switch (elementType)
				{
				case 'file':
				case 'button':
				case 'reset':
				case 'submit':
					elementValue = (typeof(element.value) != 'undefined') ? element.value : '';
					break;

				case 'radio':
					var radioelements = document.getElementsByName(element.name);
					for (var r=0; r<radioelements.length && elementValue!=''; r++)
						elementValue = (element.checked) ? element.value||'' : '';
					break;

				case 'checkbox':
					elementValue = (element.checked) ? (element.value||true) : '';
					break;

				case 'text':
				case 'textarea':
				case 'hidden':
				case 'password':
					elementValue = (element.hasClassName('PanelFieldHint'))? '' : element.value;
					break;

				case 'selectOne':
					elementValue = (element.selectedIndex >= 0)? element.options[element.selectedIndex].value : '';
					break;

				case 'selectMany':
					elementValue = [];
					for (s=0; s<element.options.length; s++)
					{
						if (element.options[s].selected) elementValue.push( element.options[s].value );
					}
					break;
				default:
					elementValue = element.innerHTML || '';
					break;
				}
				return((returnObject) ? elementValue : (Object.isArray(elementValue)) ? elementValue.join(',') : elementValue);
			}
			catch(e)
			{
				_private.logError({name:'_private.getValueById()', error:e, element:element});
			}
			return('');
		},

		// Sets the named group element(s) to the specified value(s)
		//	For radio button group a single value is sufficient
		//	For named (checkbox, text, hidden, password, selectOne) groups a comma delimited string is expected
		//	selectMany elements are not expected to be in a named group (other than a single element group)
		setValueByName : function(elementName,newValue)
		{
			var element, elements = utils.getElementsByName(document,elementName);
			if (!elements || elements.length==0)
			{	// Could not find the group by the name given, so assume the name given was an ID
				// and attempt to use the name of the element assuming an ID was provided.
				if ( !(element = $(elementName)) )	return(null);
				elementName = element.name;
				//elements = document.getElementsByName(elementName);
				elements = utils.getElementsByName(document,elementName);
			}
			// Could not identify any elements by the name (or the element by ID's name)
			if (!elements || elements.length==0) return(null);

			// Collect some information about the element (tag name, type, initial value
			element = $(elements[0]);
			var elementTagName = element.tagName.toLowerCase();
			var elementType = (elementTagName == 'select') ? ((element.type == 'select-one') ? 'selectOne' : 'selectMany') : element.type;
			var elementSubtype = element.readAttribute('subtype') || '';

			// Cycle through each of the elements in the named group
			var newValues = (newValue||'').split(",");
			var elementValue, nameValue = [], n=0, s=0;
			for (var e=0; e<elements.length; e++)
			{
				element = $(elements[e]);
				// ignore any elements not in the same type
				elmType = (element.tagName.toLowerCase() == 'select') ? ((element.type == 'select-one') ? 'selectOne' : 'selectMany') : element.type;
				if (elementType != elmType) continue;
				// ignore any elements not in the same subtype
				var subtype = element.readAttribute('subtype') || '';
				if ( elementSubtype != subtype )	continue;
				element.title = element.saveTitle;
				element.removeClassName('PanelFieldFormatError');
				element.removeClassName('PanelFieldRequiredError');

				switch (elementType)
				{
					case 'radio':
						if (element.value == newValues[0])
						{
							element.checked = true;
							return(elements);
						}
						else
							element.checked = false;
						break;

					case 'checkbox':
						element.checked = (element.value == newValues[n]) ? true:false;
						break;

					case 'text':
					case 'hidden':
					case 'password':
						element.removeClassName('PanelFieldHint');
						element.value = (n<newValues.length) ? newValues[n] : '';
						// Apply the Hint if specified and it applies
						if (element.hint && element.hint!='' && !element.hasFocus()) _private.applyHint(element, element.hint);
						break;

					case 'selectOne':
						for (s=element.options.length-1; s>=0 && element.options[s].value!=newValues[n]; s--);
						if (s>=0 && element.options[s].value == newValues[n]) element.selectedIndex = s;
						break;

					case 'selectMany':
						for (s=0; s<element.options.length; s++)
						{
							element.options[s].selected = (element.options[s].value == newValues[n]) ? true : false;
						}
						break;
				}
				n++;
			}
			return(elements);
		},

		// Sets the specific element(identified by ID) to the specified value
		setValueById : function(elementID,elementValue)
		{
			var	element=$(elementID);
			if ( !element ) return(null);
			element.title = element.saveTitle;
			element.removeClassName('PanelFieldFormatError');
			element.removeClassName('PanelFieldRequiredError');
			var elementTagName = element.tagName.toLowerCase();
			var elementType = (elementTagName == 'select') ? ((element.type == 'select-one') ? 'selectOne' : 'selectMany') : element.type;
			elementValue = elementValue||'';
			switch (elementType)
			{
				case 'radio':
					var r, radioelements = document.getElementsByName(element.name);
					for (r=0; r<radioelements.length && radioelements[r].value!=elementValue; r++);
					if (r<radioelements.length && radioelements[r].value==elementValue) element.checked = true;
					break;
				case 'checkbox':
					element.checked = (element.value == elementValue) ? true : false;
					break;
				case 'text':
				case 'textarea':
				case 'hidden':
				case 'password':
					element.value = elementValue;
					element.removeClassName('PanelFieldHint');
					// Apply the Hint if specified and it applies
					if (element.hint && element.hint!='' && !element.hasFocus()) _private.applyHint(element, element.hint);
					break;
				case 'selectOne':
					var s = -1;
					for (s=element.options.length-1; s>=0 && element.options[s].value!=elementValue; s--);
					if (s>=0 && element.options[s].value == elementValue) element.selectedIndex = s;
					break;
				case 'selectMany':
					if (!Object.isArray(elementValue)) elementValue = [elementValue];
					elementValue = [];
					for (s=0; s<element.options.length; s++)
					{
						element.options[s].checked = false;
						for (v=0; v<elementValue.length; v++)
						{
							if (elementValue[v] == element.options[s].value)
							{
								element.options[s].checked = true;
							}
						}
					}
					break;
				default:
					element.innerHTML = elementValue;
					break;
			}

			return element;
		},


		//Loads the LOV select elements on page load.
		loadLovSelectElement : function(element, clearFirst)
		{
			var LovLoad, LovValue, LovDesc, LovDisable, LovSelected, LovArray;

			// Clear all select options if asked to first.
			if (clearFirst)
			{
				for (var l=element.options.length-1; l>=0; l--)	element.options[l] = null;
				element.options.length = 0;
			}

			// Process LOV-Select element specific attributes: 	lovload, lovvalue, lovdesc, lovdisable, lovselected
			LovLoad = element.readAttribute('lovload')||'';	// json formatted string with a list of properties
			LovValue = (element.readAttribute('lovvalue')||'');		// option.value, property name in the LovLoad to use
			LovDesc = (element.readAttribute('lovdesc')||'');			// option.text, property name in the LovLoad to use
			if (LovLoad != '' && LovValue != '' && LovDesc != '')
			{
				LovDisable = (element.readAttribute('lovdisable')||'');
				LovSelected = ',' + (element.readAttribute('lovselected')||'') + ',';
				try
				{
					LovLoad = eval(LovLoad);
					LovArray = eval('['+LovLoad.replace(/\+/g,' ').replace(/\|/g,',').replace(/\}\{/g,'},{').replace(/\}\s\{/g,'},{')+']');
					// Constructor: var newOpt = new Option("text", "value", isDefaultSelectedFlag, isSelectedFlag);
					for (var k=0; k<LovArray.length; k++)
					{
						idx = element.options.length;
						optionValue = WebService.decode(LovArray[k][LovValue]);
						element.options[idx] = new Option(WebService.decode(LovArray[k][LovDesc]), WebService.decode(LovArray[k][LovValue]), false, false);
						element.options[idx].extendedProperties = LovArray[k];
						element.options[idx].disabled = (LovDisable !='' ? LovArray[k][LovDisable] : false);
						element.options[idx].selected = (LovSelected !='' ? LovSelected.indexOf(','+optionValue+',')>=0 : false);
					}
				}
				catch (err)
				{
					_private.logError({ name:'_private.loadLovSelectElement()', description:'Failed to assemble LOV array.', error:err, element:element });
				}
			}
		},

		// Event handler to submit when a return is entered on the element is applied to
		onEnter : function(evnt)
		{
			evnt = evnt || window.event;
			var element = Event.element(evnt);
			var functionReturn=false;
			var keycode = (evnt.keyCode) ? evnt.keyCode : evnt.which ? evnt.which : 0;
			if (keycode == 13)
			{
				command = element.onenter;
				try
					{
					functionReturn = eval( command+'(element,element.value,\'onenter\',evnt)' );
					}
				catch(e)
					{
					_private.logError({name:'_private.onEnter()', description:'onenter attribute function errored out. '+' Element id="'+element.id+'", command="'+command+'", action="onenter"', error:e, event:evnt});
					}
			}
			return(functionReturn);
		},


		// -----	FORM POSTING		(uses WebService.js)

			// Posts a form via Ajax call
			//		Caller should listen for the following events:	WebService:AjaxSuccess, WebService:AjaxFailure, WebService:AjaxException
			_postForm : function (form,transID, extendedProperties)
			{
				var formValues = _private.serializeForm(form);	// customized method for collecting the data to send
				formValues += '&formID='+form.id;
				//_private.dumpObject(formValues.split("&"));

				// Assemble a list of fields and values to be submitted
				var	i, il, pair, pairs = formValues.split("&"),
						fields = "", values = "";
				for ( i=0, il=pairs.length; i<il; i++ )
				{
					pair = pairs[i].split("=");
					if ("__VIEWSTATE,__EVENTVALIDATION,PB_SAVE,PB_PRINT,PB_RETURN,PB_CANCEL".indexOf(pair[0].toUpperCase()) < 0)
					{
						fields += pair[0] + '|';
						values += pair[1] + '|';
					}
				}
				//fields = escape(fields);
				//values = escape(values);

				WebService.ExecuteTransaction(transID,
					{	parameters:			fields+'~'+values,
						method:				'post',
						section:				'AJAXGRID',
						argumentDelimiter:	'~',
						onSuccess:			_private._postFormSuccess,
						format:					'JSON',
						busyMessage:		'Saving panel data ...',
						extendedProperties:	extendedProperties
					});
				return true;
			},

			// Handles Ajax Request Success (status==200)
			_postFormSuccess : function (transport,optns,jsonObj)
			{
				var	ResultCode=-1, ResultMessage='', PrimaryKeyName, PrimaryKeyValue ;

				// Check for improper response
				if (!jsonObj || !jsonObj.rows || jsonObj.rows.length == 0)
				{
					alert("ERROR: Panel._postFormSuccess() No jsonObj");
					$(document).fire("Panel:SSValidationFailure", [transport,optns,jsonObj]);
					return;
				}

				// Check the transaction reponse for success or failure
				var rows = jsonObj.rows;					// array of rows
				var row = rows[0];							//
				if (row.ResultCode !== null && row.ResultCode == 0)
				{
					//		If success then flash a "Record Saved" message
					$(document).fire("Panel:SSValidationSuccess", [transport,optns,jsonObj]);
				}
				else
				{
					$(document).fire("Panel:SSValidationFailure", [transport,optns,jsonObj]);
				}
			},


		// -----	LOAD STYLE & SCRIPT

			//	Enable the inclusion of dependent library components
			loadJSFile: function(libraryPathName, scriptLoadedCallback)
			{
				var timerId, timeout;
				var headID = document.getElementsByTagName("head")[0];
				var newScript = document.createElement('script');
				newScript.type = 'text/javascript';
				newScript.src = libraryPathName;
				var libraryName = libraryPathName.replace('\/','').replace(' ','').replace('.','_').replace(':','_');
				newscript.id = libraryName;
				//if (typeof(scriptLoadedCallback) == "function")
				//{
				//newScript.onload = (typeof(scriptLoadedCallback) == "function") ? scriptLoadedCallback : _private.recordJSLoaded;
				//}
				headID.appendChild(newScript);

				timeout = 10;
				timerId = null;
				function isJSFileLoaded()
				{
					if ( !!( timerId ) )
					{
						if ( timeout == 0 ) return(false);
						if ( !!($(libraryName)) )
						{
							_private.jsLoaded += libraryName + ' ';
							return(true);
						}
						timeout--;
						timerId = setTimeout( "window.isJSFileLoaded()", 1000);
					}

				}
				window.isJSFileLoaded = isJSFileLoaded;
				timerId = setTimeout( "window.isJSFileLoaded()", 1000);

				//inserting via DOM fails in Safari 2.0, so brute force approach
				//document.write('<script type="text/javascript" src="'+libraryName+'"></script>');
			},


			//	Enable the inclusion of dependent library components
			loadStyleFile: function(StyleSheetName)
			{
				var headID = document.getElementsByTagName("head")[0];
				var cssNode = document.createElement('link');
				cssNode.type = 'text/css';
				cssNode.rel = 'stylesheet';
				cssNode.href = StyleSheetName;
				cssNode.media = 'screen';
				headID.appendChild(cssNode);
			},


		// -----	DEBUG TOOLS
			logError : function(err)
			{
				err.browser = navigator.userAgent;
				_private.dumpObject( err,2 );
				return err;
			},
			encodeToHtml : function (strng)
			{
				if ( !strng || strng=='' ) return strng;
				var encodedHtml = strng.toString();
				return encodedHtml.split("\x3C").join("&lt;").split("\x26").join("&amp;").split("\x3E").join("&gt;").split("\x22").join("&quot;");
			},
			inspect: function(obj, maxLevels, level)
			{
				var str = '', type, msg;

				// Start Input Validations
				// Don't touch, we start iterating at level zero
				if(level == null)  level = 0;

				// At least you want to show the first level
				if(maxLevels == null) maxLevels = 1;
				if(maxLevels < 1)
					return '<font color="red">Error: Levels number must be > 0</font>';

				// We start with a non null object
				if(obj == null)
				return '<font color="red">Error: Object <b>NULL</b></font>';
				// End Input Validations

				// Each Iteration must be indented
				str += '<ul>';

				// Start iterations for all objects in obj
				for(property in obj)
				{
				  try
				  {
					// Show "property" and "type property"
					type =  typeof(obj[property]);
					switch (type)
					{
					case "function":
						if ( obj[property] )		str += '<li>(' + type + ') ' + property + '()</li>';
						break;
					case "string":
						str += '<li>(' + type + ') ' + property + ( (obj[property]==null)?(': <b>null</b>'):(': <b>'+decodeURIComponent(obj[property]))) + '</b></li>';
						break;
					case "boolean":
						str += '<li>(' + type + ') ' + property + ( (obj[property]==null)?(': <b>null</b>'):(': <b>'+obj[property] ? 'true':'false')) + '</b></li>';
						break;
					case "number":
						str += '<li>(' + type + ') ' + property + ( (obj[property]==null)?(': <b>null</b>'):(': <b>'+obj[property])) + '</b></li>';
						break;
					case "object":
						// We keep iterating if this property is an Object, non null
						// and we are inside the required number of levels
						str += '<li>(' + type + ') ' + property + '</li>';
						if( (obj[property] != null) && (level+1 < maxLevels) )
							str += _private.inspect(obj[property], maxLevels, level+1);
						break;
					}
				  }
				  catch(err)
				  {
					// Is there some properties in obj we can't access? Print it red.
					if(typeof(err) == 'string') msg = err;
					else if(err.message)        msg = err.message;
					else if(err.description)    msg = err.description;
					else                        msg = 'Unknown';
					str += '<li><font color="red">(Error) ' + property + ': ' + msg +'</font></li>';
				  }
				}

				  // Close indent
				  str += '</ul>';
				return str;
			},
			dumpObject : function(obj,maxlevels)
			{
				if ( !_private.debugmode ) return;
				var str = _private.inspect(obj,maxlevels);
				if ( _private.debugwindow == null || (_private.debugwindow && _private.debugwindow.closed) )
				{
					_private.debugwindow = null;
					_private.debugwindow = window.open('about:blank','_blank','top=100,left=50,width=550,height=500,directories=no,location=no,scrollbars=yes,resizable=yes');
				}
				_private.debugwindow.document.write( str +'<br />-----------------------------------------------------------------------------------<br />');
				return;
			}
	};

	return _public;

}();

// When the DOM is constructed, make sure the Panel initialization is executed.
Event.observe(document, "dom:loaded", Panel.initialize);



/*
		// Returns next enterable elenment, if not found current element provided is returned
		_nextEnterableElement: function(element)
		{
			var e, elements = new Object(Form.getElements(element.form));
			for (e=0; e<elements.length && element.id != elements[e].id; e++);
			if (e>=elements.length) return(element);
			var ne = ((e+1) >= elements.length) ? 0 : e+1;
			while ( element.id!=elements[ne].id && (elements[ne].disabled || elements[ne].readOnly || !_private.isVisible(elements[ne])))
			{
				ne = ((ne+1) >= elements.length) ? 0 : ne+1;
			}
			return(elements[ne])
		},

		// determine if an element is visible by checking it's ancestry
		_isVisible: function(element)
		{
			var transientElement = $(element);
			if (!transientElement) return(null);
			var visble = true;
			do
			{
				if ((transientElement.style && transientElement.style.display && transientElement.style.display.toLowerCase() == 'none') ||	(transientElement.type == 'hidden') )
					visble = false;
				transientElement = transientElement.parentNode;
			} while(visble && transientElement!=null && transientElement.parentNode!=null && transientElement!=element.parentNode);
			return(visble);
		},



*/

