/******************************************************************************
 qForm JavaScript API

 Author: Dan G. Switzer, II
 Date:   December 10, 2000
 Build:  124b
 
 Description:
 This library provides a API to forms on your page. This simplifies retrieval
 of field values by providing methods to retrieve the values from fields,
 without having to do complicate coding.

 GNU License
 ---------------------------------------------------------------------------
 This library provides common methods for interacting with HTML forms
 Copyright (C) 2001  Dan G. Switzer, II

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.

 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

******************************************************************************/

// find out which version of JavaScript the user has
var _jsver = 11;
for( var z=2; z < 10; z++ ) document.write("<SCRIPT LANGUAGE=\"JavaScript1." + z + "\"> _jsver = 1" + z + ";</SCRIPT>");

/******************************************************************************
 qForm API Initialization
******************************************************************************/
// define _a object
function _a(){
	this.objects = new Object();
	// the path where the external library components are found
	this.librarypath = "";
	// specifies whether the browser should autodetect the version of JavaScript being used
	this.autodetect = true;
	// this specifies the default modules to load when the wildcard ("*") is specified
	this.modules = new Array("field", "functions|12", "validation");
	// this is the name of the modules that have been loaded, libraries will not be loaded more then once
	this.packages = new Object();
	// this contains a list of the original contents of a container, when the setValue() method is used on a container, then the containers object is checked to see if the key exists
	this.containers = new Object();
	// this is a global namespace to use with custom libraries--this allows you to initialize global variables, etc
	this.libs = new Object();
	// this structure defines the version of JavaScript being used
	this.jsver = new Object();
	for( var z=1; z < 9; z++ ) this.jsver["1" + z] = "JavaScript1." + z;

	// this is background color style to use when a form field validation error has occurred
	this.errorColor = "red";
	// this specifies whether or not to use error color coding (by default browser that support it use it)
	this.useErrorColorCoding = (document.all || document.getElementById) ? true : false;
	// this checks to see if the WDDX JavaScript has been loaded
	this.usewddx = (!!window.WddxSerializer);
	// this specifies whether all qForm objects should be validated upon a form submission, or just the form being submitted. By default only the form being submitted is validated.
	this.validateAll = false;
	// this specifies whether or not a form can be submitted if validation errors occurred. If set to false, the user gets an alert box, if set to true, the user recieves a confirm box.
	this.allowSubmitOnError = false;
	// the place holder for the number of custom validators that have been initialized
	this.customValidators = 0;
	// specify whether the reset method should be run when the form object is initialized
	this.resetOnInit = true;
	return true;
}
qFormAPI = new _a();

// define _a setLibraryPath(); prototype
function _a_setLibraryPath(path){
	if( path.substring(path.length-1) != '/' ) path += '/';
	this.librarypath = path;
	return true;
}
_a.prototype.setLibraryPath = _a_setLibraryPath; 

// define _a include(); prototype
function _a_include(src, path, ver){
	var source = src;
	if( !source ) return true;
	if( !path ) var path = this.librarypath + "qforms/";
	if( !ver ) var ver = "";

	if( source.substring(source.length-3) != ".js" ) source += ".js";
	var thisPackage = source.substring(0,source.length-3);
	
	// if the package is already loaded, then kill method
	if( this.packages[thisPackage] ) return true;
	
	if( thisPackage == "*" ){
		for( var i=0; i < this.modules.length; i++ ){
			var source = this.modules[i];
			var ver = "99";
			if( source.indexOf("|") > -1 ){
				ver = source.substring(source.indexOf("|") + 1);
				source = source.substring(0, source.indexOf("|"));
			}
			if( _jsver > ver && this.autodetect ){
				document.write("<SCRIPT LANGUAGE=\"" + this.jsver[ver] + "\" SRC=\"" + path + source + "_js" + ver + ".js\"><\/SCRIPT>");
			} else {
				document.write("<SCRIPT LANGUAGE=\"JavaScript\" SRC=\"" + path + source + ".js\"><\/SCRIPT>");
			}
			this.packages[source] = true;
			this.libs[source] = new Object();
		}
	} else {
		if( !this.autodetect || _jsver < 12 ){
			document.write("<SCRIPT LANGUAGE=\"JavaScript\" SRC=\"" + path + source + "\"><\/SCRIPT>");
		} else if( this.autodetect && (parseInt(_jsver) >= parseInt(ver)) ){
			source = source.substring(0,source.length-3) + "_js" + ver + source.substring(source.length-3);
			document.write("<SCRIPT LANGUAGE=\"" + this.jsver[ver] + "\" SRC=\"" + path + source + "\"><\/SCRIPT>");
		} else {
			document.write("<SCRIPT LANGUAGE=\"JavaScript\" SRC=\"" + path + source + "\"><\/SCRIPT>");
		}
	}

	this.packages[thisPackage] = true;
	if( thisPackage != "*") this.libs[thisPackage] = new Object();
	return true;
}
_a.prototype.include = _a_include;

// define _a validate(); prototype
function _a_validate(qForm){
	// if just validate a single form, then validate now and exit
	if( !this.validateAll ) return qFormAPI.objects[qForm].validate();
	
	var aryErrors = new Array();
	
	// loop through all the forms
	for( obj in qFormAPI.objects ){
		// check the form for errors
		qFormAPI.objects[obj].checkForErrors();
		// add the errors from this form t adde queue
		for( var i=0; i < qFormAPI.objects[obj]._queue.errors.length; i++ ){
			aryErrors[aryErrors.length] = qFormAPI.objects[obj]._queue.errors[i];
		}
	}

	// if there are no errors then return true
	if( aryErrors.length == 0 ) return true;

	var strError = "The following error(s) occurred:\n";
	for( var i=0; i < aryErrors.length; i++ ){
		strError += " - " + aryErrors[i] + "\n";
	}

	// check to see if the user is allowed to submit the form even if an error occurred
	if( this._allowSubmitOnError ){
		strError += "\nAre you sure you want to continue?";
		var result = confirm(strError);
	} else {
		alert(strError);
		var result = false;
	}

	return result;
}
_a.prototype.validate = _a_validate;

function _a_reset(hardReset){
	// loop through all the forms and reset the properties
	for( obj in qFormAPI.objects ) qFormAPI.objects[obj].reset(hardReset);
	return true;
}
_a.prototype.reset = _a_reset;

// define _a getFields(); prototype
function _a_getFields(){
	stcAllData = new Object();
	
	// loop through all the forms
	for( obj in qFormAPI.objects ){
		// check the form for errors
		var tmpStruct = qFormAPI.objects[obj].getFields();
		// add the value from this form to the structure
		for( field in tmpStruct ){
			if( !stcAllData[field] ){
				stcAllData[field] = tmpStruct[field];
			} else {
				stcAllData[field] += "," + tmpStruct[field];
			}
		}
	}

	// return all the form data
	return stcAllData;
}
_a.prototype.getFields = _a_getFields;

// define _a setFields(); prototype
function _a_setFields(struct, resetDefault){
	// loop through each form and populate the fields
	for( obj in qFormAPI.objects ) qFormAPI.objects[obj].setFields(struct, resetDefault);
}
_a.prototype.setFields = _a_setFields;

function _a_serialize(exclude){
	if( !this.usewddx ) return alert("The WDDX JavaScript Library has not been loaded. Please \nload the library in order to use this function.");
	// if you need to reset the default values of the fields
	var lstExclude = (arguments.length > 0) ? "," + arguments[0] + "," : "";
	struct = new Object();
	stcAllFields = qFormAPI.getFields();
	// loop through form elements
	for( key in stcAllFields ){
		if( lstExclude.indexOf("," + key + ",") == -1 ) struct[key] = stcAllFields[key]; 
	}
	// create serializer object
	mySerializer = new WddxSerializer;
	return mySerializer.serialize(struct);
}
_a.prototype.serialize = _a_serialize;

// define _a dump(); prototype
function _a_dump(){
	var str = "";
	formData = this.getFields();
	for( field in formData ) str += field + " = " + formData[field] + "\n";
	alert(str);
}
_a.prototype.dump = _a_dump;


/******************************************************************************
 qForm Object
******************************************************************************/
// define qForm object
function qForm(name, parent, init){
	// check to see if the WDDX library is loaded
	qFormAPI.usewddx = (!!window.WddxSerializer);
	if( arguments.length > 2 ) return true;
	if(!name) return alert("No form specified.");
	this._name = name;
	this._parent = (parent) ? parent : null;
	this._status = null;
	this._queue = new Object();
	this._queue.errors = new Array();
	this._queue.validation = new Array();
	this._showAlerts = true;
	this._allowSubmitOnError = qFormAPI.allowSubmitOnError;
	this._locked = false;
	qFormAPI.objects[this._name] = this;
	// this is a string pointer to the qFormAPI object copy of this object
	this._pointer = "qFormAPI.objects['" + this._name + "']";
	this.init();
	return true;
}
// initialize dummy qForm object so that NS will initialize the prototype object
new qForm(null, null, true);

// define qForm init(); prototype
function _q_init(){
	if( !this._name ) return false;

	if( this._parent && document.layers ){
		this._form = this._parent + ".document." + this._name;
	} else {
		this._form = "document." + this._name;
	}
	// create a pointer to the form object	
	this.obj = eval(this._form);
	
	if( !this.obj ) return alert("The form \"" + this._name + "\" does not exist. This error \nwill occur if the Form object was initialized before the form \nhas been created or if it simply doesn't exist. Please make \nsure to initialize the Form object after page loads to avoid \npotential problems.");

	// reset the form	
	if( qFormAPI.resetOnInit ) this.obj.reset();

	// set the onSubmit method equal to whatever the current onSubmit is
	// this function is then run whenever the submitCheck determines it's ok to submit the form
	this.onSubmit = new Function(_functionToString(this.obj.onsubmit, ""));
	// replace the form's onSubmit event and just run the submitCheck() method
	this.obj.onsubmit = new Function("return " + this._pointer + ".submitCheck();");
	
	// loop through form elements
	this._fields = new Array();
	this._pointers = new Object();
	for( var j=0; j < this.obj.elements.length; j++ ){
		// current element's name
		var strFieldName = this.obj.elements[j].name;

		// check to make sure form field is unique
		if( (!this[strFieldName]) && (strFieldName.length > 0) ){
			// create an instance of the form field in the form object
			this[strFieldName] = new Field(this.obj, strFieldName, this._name);
			this._fields[this._fields.length] = strFieldName;
			this._pointers[strFieldName.toLowerCase()] = this[strFieldName];
		}
	} // end for loop (form elements)
	this._status = "initialized";
	return true;
}
qForm.prototype.init = _q_init;

// define qForm submitCheck prototype
function _q_submitCheck(){
	// make sure the form is submitted more then once
	if( this._status == "submitting" || this._status == "validating" ) return false; 
	this._status = "submitting";
	
	// validate the form
	var result = qFormAPI.validate(this._name);
	// if no errors occurred, run the onSubmit() method
	if( result ){
		// run the custom onSubmit method
		var x = this.onSubmit();
		// if a boolean value was passed back, then update the result value
		if( typeof x == "boolean" ) result = x;
	}

	// if the form shouldn't be submitted, then reset the form's status
	if( !result ){
		// if any validation errors occur or the form is not to be submitted because the
		// onSubmit() event return false, then set the reset the form's status
		this._status = "idle"; 
	// run any processing that should be done before submitting the form
	} else {
		// make sure to select all "container" objects so the values are included when submitted
		_setContainerValues(this);
	}
	return result;
}	
qForm.prototype.submitCheck = _q_submitCheck;

// define qForm onSubmit(); prototype
qForm.prototype.onSubmit = new Function("");


// define qForm addMethod(); prototype
function _q_addMethod(name, fn, type){
	if( arguments.length < 2 ) return alert("To create a new method, you must specify \nboth a name and function to run: \n  obj.addMethod(\"checkTime\", _isTime);");
	var type = _param(arguments[2], "from").toLowerCase();
	
	// set the object to attach the prototype method to
	if( type == "field" ) type = "Field";
	else type = "qForm";

 // if adding a predefined function, then add it now	
	if( typeof fn == "function" ){
		strFN = fn.toString();
		strFN = strFN.substring(strFN.indexOf(" "), strFN.indexOf("("));
		eval(type + ".prototype." + name + " = " + strFN);

 // if creating a new function, then add it now	
	} else {
		var fnTemp = new Function(fn);
		eval(type + ".prototype." + name + " = fnTemp;");
	}
	return true;
}
qForm.prototype.addMethod = _q_addMethod;

// define qForm addEvent(); prototype
function _q_addEvent(event, cmd, append){
	if( arguments.length < 2 ) return alert("Invalid arguments. Please use the format \naddEvent(event, command, [append]).");
	var append = _param(arguments[2], true, "boolean");
	_addEvent("qFormAPI.objects." + this._name + ".obj", arguments[0], arguments[1], append);
	return true;
}
qForm.prototype.addEvent = _q_addEvent;

// define qForm required(); prototype
function _q_required(fields, value){
	var value = _param(arguments[1], true, "boolean");
	// remove spaces from the fields
	while( fields.indexOf(" ") > -1 ) fields = fields.substring( 0, fields.indexOf(" ") ) + fields.substring( fields.indexOf(" ")+1 );
	aryField = fields.split(",");

	for( var i=0; i < aryField.length; i++ ){
		if( !this[aryField[i]] ) return alert("The form field \"" + aryField[i] + "\" does not exist.");
		this[aryField[i]].required = value;
	}
	return true;
}
qForm.prototype.required = _q_required;

// define qForm optional(); prototype
function _q_optional(fields){
	// turn the fields off
	this.required(fields, false);
	return true;
}
qForm.prototype.optional = _q_optional;


// define qForm forceValidation(); prototype
function _q_forceValidation(fields, value){
	var value = _param(arguments[1], true, "boolean");
	// remove spaces from the fields
	while( fields.indexOf(" ") > -1 ) fields = fields.substring( 0, fields.indexOf(" ") ) + fields.substring( fields.indexOf(" ")+1 );
	aryField = fields.split(",");

	for( var i=0; i < aryField.length; i++ ){
		if( !this[aryField[i]] ) return alert("The form field \"" + aryField[i] + "\" does not exist.");
		this[aryField[i]].validate = value;
	}
return true;
}
qForm.prototype.forceValidation = _q_forceValidation;


// define qForm submit(); prototype
function _q_submit(){
	// do not submit the form more then once
	if( this._status == "submitting" ) return false;
	if( this.obj.onsubmit() ) this.obj.submit();
	return true;
}
qForm.prototype.submit = _q_submit;

// define qForm disabled(); prototype
function _q_disabled(status){
	var objExists = (typeof this.obj.disabled == "boolean") ? true : false;
	if( arguments.length == 0 ) var status = (this.obj.disabled) ? false : true;
	// if the "disabled" var doesn't exist, then use the build in "locked" feature
	if( !objExists ){ 
		this._locked = (this._locked) ? false : true;
	// switch the status of the disabled property
	} else {
		this.obj.disabled = (this.obj.disabled) ? false : true;
	}
	return true;
}
qForm.prototype.disabled = _q_disabled;

// define qForm reset(); prototype
function _q_reset(hardReset){
	if( this._status == null ) return false;
	if( hardReset ){
		// loop through form elements
		for( var j=0; j < this._fields.length; j++ ){
			// reset the value for this field
			this[this._fields[j]].setValue(null, true, false); 
			// enforce any depencies of the current field
			if( this[this._fields[j]]._queue.dependencies.length > 0 ) this[this._fields[j]].enforceDependency(); 
		}
	} else {
		// loop through form elements
		for( var j=0; j < this._fields.length; j++ ){
			// reset the value for this field
			this[this._fields[j]].setValue(this[this._fields[j]].defaultValue, true, false); 
			// enforce any depencies of the current field
			if( this[this._fields[j]]._queue.dependencies.length > 0 ) this[this._fields[j]].enforceDependency(); 
		}
	}
	return true;
}
qForm.prototype.reset = _q_reset;

// define qForm getFields(); prototype
function _q_getFields(){
	if( this._status == null ) return false;
	struct = new Object();
	// loop through form elements
	for( var j=0; j < this._fields.length; j++ ){
		struct[this._fields[j]] = this[this._fields[j]].getValue();
	}
	return struct;
}
qForm.prototype.getFields = _q_getFields;

// define qForm setFields(); prototype
function _q_setFields(struct, resetDefault){
	if( this._status == null ) return false;
	// if you need to reset the default values of the fields
	var reset = _param(arguments[1], false, "boolean");
	// reset the form
	this.reset(reset);
	// loop through form elements
	for( key in struct ){
		lckey = key.toLowerCase();
		if( this._pointers[lckey] ){
			this._pointers[lckey].setValue(struct[key], true, false);
			if(reset) this._pointers[lckey].defaultValue = struct[key];
		}
	}
	return true;
}
qForm.prototype.setFields = _q_setFields;

// define qForm hasChanged(); prototype
function _q_hasChanged(){
	if( this._status == null ) return false;
	bool = false;
	// loop through form elements
	for( var j=0; j < this._fields.length; j++ ){
		if( this[this._fields[j]].getValue() != this[this._fields[j]].defaultValue ){
			bool = true;
			break;
		}
	}
	return bool;
}
qForm.prototype.hasChanged = _q_hasChanged;

// define qForm changedFields(); prototype
function _q_changedFields(){
	if( this._status == null ) return false;
	struct = new Object();
	// loop through form elements
	for( var j=0; j < this._fields.length; j++ ){
		if( this[this._fields[j]].getValue() != this[this._fields[j]].defaultValue ){
			struct[this._fields[j]] = this[this._fields[j]].getValue(); 
		}
	}
	return struct;
}
qForm.prototype.changedFields = _q_changedFields;

// define qForm serialize(); prototype
function _q_serialize(exclude){
	if( this._status == null ) return false;
	if( !qFormAPI.usewddx ) return alert("The WDDX JavaScript Library has not been loaded. Please \nload the library in order to use this function.");
	// if you need to reset the default values of the fields
	var lstExclude = (arguments.length > 0) ? "," + arguments[0] + "," : "";
	struct = new Object();
	// loop through form elements
	for( var j=0; j < this._fields.length; j++ ){
		if( lstExclude.indexOf("," + this._fields[j] + ",") == -1 ) struct[this._fields[j]] = this[this._fields[j]].getValue(); 
	}
	// create serializer object
	mySerializer = new WddxSerializer;
	return mySerializer.serialize(struct);
}
qForm.prototype.serialize = _q_serialize;

// define qForm dump(); prototype
function _q_dump(){
	var str = "";
	formData = this.getFields();
	for( field in formData ) str += field + " = " + formData[field] + "\n";
	alert(str);
}
qForm.prototype.dump = _q_dump;

/******************************************************************************
 Field Object
******************************************************************************/
// define Field object
function Field(form, field, formName, init){
	if( arguments.length > 3 ) return true;
	this._queue = new Object();
	this._queue.dependencies = new Array();
	this.qForm = qFormAPI.objects[formName];
	this.name = field;
	this.path = this.qForm._form + "['" + field + "']";
	this.pointer = "qFormAPI.objects." + formName + "['" + field + "']";
	this.obj = eval(this.path);
	this.locked = false;
	this.description = field.toLowerCase();
	this.required = false;
	this.validate = false;
	this.container = false;
	this.type = (!this.obj.type && !!this.obj[0]) ? this.obj[0].type : this.obj.type;

	var value = this.getValue();
	this.defaultValue = value;
	this.lastValue = value;

	// initialize the field object
	this.init();
	
	return true;
}
new Field(null, null, null, true);

// define Field init(); prototype
function _f_init(){
	if( qFormAPI.useErrorColorCoding && this.obj.style ) this.backgroundColor = this.obj.style.backgroundColor;

	if( document.layers && (this.type == "radio" || this.type == "checkbox") && !!this.obj[0] ){
		this.addEvent("onclick", "return " + this.pointer + ".allowFocus();");
	} else {
		this.addEvent("onfocus", "return " + this.pointer + ".allowFocus();");
	}
}
Field.prototype.init = _f_init;

// define Field allowFocus(); prototype
function _f_allowFocus(){
	// if the background color equals the error color, then reset the style to the original background
	if( qFormAPI.useErrorColorCoding && this.obj.style ){
		if( this.obj.style.backgroundColor == qFormAPI.errorColor) this.obj.style.backgroundColor = this.backgroundColor;
	}
	// store the current value in the lastValue property
	this.lastValue = this.getValue();
	// check to see if the field is locked
	var result = this.checkIfLocked();

	// if the field is locked, and we have a select box, we need to reset the value of the field
	// and call the onblur method to remove focus
	if( (this.type.indexOf("select") > -1) && !result ){
		this.resetLast();
		this.blur();
	}

	// if the field isn't locked, run the onFocus event
	if( !result ) this.onFocus();
	// return the result of the checkIfLocked() method
	return result;
}
Field.prototype.allowFocus = _f_allowFocus;

// define qForm onFocus(); prototype
Field.prototype.onFocus = new Function("");

// define Field addEvent(); prototype
function _f_addEvent(event, cmd, append){
	if( arguments.length < 2 ) return alert("Invalid arguments. Please use the format \naddEvent(event, command, [append]).");
	var append = _param(arguments[2], true, "boolean");

	// if the field is a multi-array element, then apply the event to all items in the array
	if( (this.type == "radio" || this.type == "checkbox") && !!this.obj[0] ){
		for( var i=0; i < this.obj.length; i++ ) _addEvent(this.path + "[" + i + "]", arguments[0], arguments[1], append);
	} else {
		_addEvent(this.path, arguments[0], arguments[1], append);
	}
	return true;
}
Field.prototype.addEvent = _f_addEvent;

// define Field disabled(); prototype
function _f_disabled(s){
	var status = arguments[0];
	var objExists = (typeof this.obj.disabled == "boolean") ? true : false;
	if( arguments.length == 0 ) var status = (this.obj.disabled) ? false : true;
	// if the "disabled" var doesn't exist, then use the build in "locked" feature
	if( !objExists ){ 
		this.locked = status;
	// switch the status of the disabled property
	} else {
		this.obj.disabled = status;
	}
	return true;
}
Field.prototype.disabled = _f_disabled;

// define Field checkIfLocked(); prototype
function _f_checkIfLocked(showMsg){
	var bShowMsg = _param(arguments[0], this.qForm._showAlerts);
	// if the value isn't equal to the key, then don't relocate the user
	if( this.isLocked() ){
		this.blur();
		if( bShowMsg ) alert("This field is disabled.");
		return false;
	}
	return true;
}
Field.prototype.checkIfLocked = _f_checkIfLocked;

// define Field isLocked(); prototype
function _f_isLocked(){
	var isLocked = this.locked;
	if( this.qForm._locked ) isLocked = true; // if the entire form is locked
	return isLocked;
}
Field.prototype.isLocked = _f_isLocked;

// define Field isDisabled(); prototype
function _f_isDisabled(){
	// if the disabled object exists, then get its status
	if( typeof this.obj.disabled == "boolean" ){
		var isDisabled = this.obj.disabled;
		if( this.qForm.obj.disabled ) isDisabled = true; // if the entire form is locked
		return isDisabled;
	// otherwise, return false (saying it's not disabled)
	} else {
		return false;
	}
}
Field.prototype.isDisabled = _f_isDisabled;

// define Field focus(); prototype
function _f_focus(){
	if( this.obj.focus ) this.obj.focus();
	return true;
}
Field.prototype.focus = _f_focus;

// define Field blur(); prototype
function _f_blur(){
	if( this.obj.blur ) this.obj.blur();
	return true;
}
Field.prototype.blur = _f_blur;

// define Field select(); prototype
function _f_select(){
	if( this.obj.select ) this.obj.select();
	return true;
}
Field.prototype.select = _f_select;

// define Field reset(); prototype
function _f_reset(){
	this.setValue(this.defaultValue, true, false);
	return true;
}
Field.prototype.reset = _f_reset;

// define Field getValue(); prototype
function _f_getValue(){
	type = (this.type.substring(0,6) == "select") ? "select" : this.type;
	
	if( type == "select" ){
		strValue = "";
		if( this.type == "select-one" && !this.container ){
			strValue = (this.obj.selectedIndex == -1) ? "" : this.obj[this.obj.selectedIndex].value;
		} else {
			// loop through all element in the array for this field
			for( var i=0; i < this.obj.length; i++ ){
				// if the element is selected, add get the value (unless it's a dummy container)
				if( (this.obj[i].selected || this.container) && (!this.dummyContainer) ){
					// if more then one element already exists, if so comma-delimit the list
					if( strValue.length > 0 ) strValue += ",";
					// append the selected value, if the value property doesn't exist, use the text
					strValue += (!!this.obj[i].value) ? this.obj[i].value : this.obj[i].text;
				}
			}
		}
	} else if( (type == "checkbox") || (type == "radio") ){
		strValue = "";
		// if more then one checkbox
		if( !!this.obj[0] ){ 
			// loop through all checkbox elements, and if a checkbox is checked, grab the value
			for( var i=0; i < this.obj.length; i++ ){
				if( this.obj[i].checked  ){
					// if more then one element already exists, if so comma-delimit the list
					if( strValue.length > 0 ) strValue += ",";
					// append the selected value
					strValue += this.obj[i].value;
				}
			}
		// otherwise, store the value of the field (if checkmarked) into the list
		} else if( this.obj.checked ){
			strValue = this.obj.value;
		}
	} else {
		strValue = this.obj.value;
	}

	return strValue;
}
Field.prototype.getValue = _f_getValue;

// define Field setValue(); prototype
function _f_setValue(value, bReset, doBlur){
	this.lastValue = this.getValue();
	var reset = _param(arguments[1], true, "boolean");
	var doBlur = _param(arguments[2], true, "boolean");
	var type = (this.type.substring(0,6) == "select") ? "select" : this.type;

	if( type == "select" ){
		var bSelectOne = (this.type == "select-one") ? true : false;
		value = "," + value + ",";
		bLookForFirst = true; // if select-one type, then only select the first value found
		// if the select box is not a container
		if( !this.container ){
			// loop through all element in the array for this field
			for( var i=0; i < this.obj.length; i++ ){
				bSelectItem = (value.indexOf("," + this.obj[i].value + ",") > -1) ? true : false;
				if( bSelectItem && (bLookForFirst || !bSelectOne) ) this.obj[i].selected = true;
				else if( reset || bSelectOne) this.obj[i].selected = false;
				if( bSelectItem && bLookForFirst ) bLookForFirst = false;
			}
		// if the select box is a container, then search through the container's original contents
		} else {
			newValues = new Object();
			for( var i=0; i < this.boundContainers.length; i++ ){
				// check to see if the container exists, if it does check for the value
				if( qFormAPI.containers[this.qForm._name + "_" + this.boundContainers[i]] ){
					// loop through all the container objects
					for( key in qFormAPI.containers[this.qForm._name + "_" + this.boundContainers[i]] ){
						// if the key is in the container, then make sure to add the value
						if( value.indexOf("," + key + ",") > -1 ){
							newValues[key] = qFormAPI.containers[this.qForm._name + "_" + this.boundContainers[i]][key];
						}
					}
				}
			}
			// populate the container values
			this.populate(newValues, reset)
		}

	} else if( (type == "checkbox") || (type == "radio") ){
		value = "," + value + ",";
		// if more then one checkbox
		if( !!this.obj[0] ){ 
			// loop through all checkbox elements, and if a checkbox is checked, grab the value
			for( var i=0; i < this.obj.length; i++ ){
				if( value.indexOf("," + this.obj[i].value + ",") > -1 ) this.obj[i].checked = true;
				else if( reset ) this.obj[i].checked = false;
			}
		// otherwise, store the value of the field (if checkmarked) into the list
		} else if( this.obj.value == value ){
			this.obj.checked = true;
		} else if( reset ){
			this.obj.checked = false;
		}
	
	} else {
		this.obj.value = (!value) ? "" : value;
	}
	
	// run the onblur event
	if( doBlur ){
		if( (type == "checkbox") || (type == "radio") && !!this.obj[0] ){
			for( var k=0; k < this.obj.length; k++ ){
				if( typeof this.obj[k].onblur == "function" ) this.obj[k].onblur();
			}
		} else if( typeof this.obj.onblur == "function" ){
			this.obj.onblur();
		}
	}
	return true;
}
Field.prototype.setValue = _f_setValue;

/******************************************************************************
 Validation Object
******************************************************************************/
// define qForm addValidator(); prototype
function _q_addValidator(name, fn){
	if( arguments.length < 2 ) return alert("To create a new validation object, you must specify \nboth a name and function to run: \n  obj.addValidator(\"isTime\", __isTime);");
	if( typeof fn == "string" ){
		var _func = new Function(fn);
		_addValidator(name, _func);
	} else {
		_addValidator(name, fn);
	}
	return true;
}
qForm.prototype.addValidator = _q_addValidator;


// define Field validateExp(); prototype
function _f_validateExp(expression, error, cmd){
	var expression = _param(arguments[0], "false");
	var error = _param(arguments[1], "An error occurred on the field '\" + this.description + \"'.");
	var cmd = _param(arguments[2]);
	
	var strFn = "if( " + expression + " ){ this.error = \"" + error + "\";}";
	if( cmd.length > 0 ) strFn += cmd;
	strValidateExp = "_validateExp" + qFormAPI.customValidators;
	_addValidator(strValidateExp, new Function(strFn));
	eval(this.pointer + ".validate" + strValidateExp + "();");
	qFormAPI.customValidators++;
}
Field.prototype.validateExp = _f_validateExp;

function _addValidator(name, fn){
	if( arguments.length < 2 ) return alert("To create a new validation object, you must specify \nboth a name and function to run: \n  _addValidator(\"isTime\", __isTime);");
	// strip "is" out of name if present
	if( name.substring(0,2).toLowerCase() == "is" ) name = name.substring(2);
	
	// if not registering a simple expression evaluator, then update the status bar
	if( name.substring(0,12) != "_validateExp" ){
		// update the status bar with the initialization request
		window.status = "Initializing the validate" + name + "() and is" + name + "() validation scripts...";
		// clear the status bar
		setTimeout("window.status = ''", 100);
	}
	
	var strFN = fn.toString();
	var strName = strFN.substring(strFN.indexOf(" "), strFN.indexOf("("));
	var strArguments = strFN.substring( strFN.indexOf("(")+1, strFN.indexOf(")") );
	// remove spaces from the arguments
	while( strArguments.indexOf(" ") > -1 ) strArguments = strArguments.substring( 0, strArguments.indexOf(" ") ) + strArguments.substring( strArguments.indexOf(" ")+1 );

	// add rountine to check to see if the validation method should be processed
	// if displaying errors, but the field is locked then return false immediately
	var strBody = "var display = (this.qForm._status == 'validating') ? false : true;\n";
	strBody += "if( (display && this.isLocked()) || this.qForm._status.substring(0,5) == 'error') return false;\n";
	strBody += "if( _ltrim(this.getValue()).length == 0 && !this.required ) return false;\n";
	strBody += "this.error = '';\n";

	// get the body of the custom function
	strBody += strFN.substring( strFN.indexOf("{")+1, strFN.lastIndexOf("}") ); 

	// if alerting the user to the error
	strBody += "if( display && this.error.length > 0 ){\n";
	strBody += "if( this.qForm._status.indexOf('_ShowError') > -1 ){\n";
	strBody += "this.qForm._status = 'error';\n";
	strBody += "alert(this.error);\n";
	strBody += "setTimeout(this.pointer + \".focus();\", 1);\n";
	strBody += "setTimeout(this.pointer + \".qForm._status = 'idle';\", 100);\n";
	strBody += "} return false;\n";
	strBody += "} else if ( display ){ return true; } return this.error;\n";
	
	// start build a string to create the new function
	var strNewFN = "new Function(";
	var aryArguments = strArguments.split(",");
	for( var i=0; i < aryArguments.length; i++ ){
		if(aryArguments[i] != "") strNewFN += "\"" + aryArguments[i] + "\",";
	}
	var strRuleFN = strNewFN;
	strNewFN += " strBody);";
	// create the Field prototype for validation
	eval("Field.prototype.is" + name + " = " + strNewFN);

	// create validation rule, the validation rule must loop through the arguments provided
	// and create a string to stick in the validation queue. This string will be eval() later
	// on to check for errors
	var strRule = "var cmd = this.pointer + '.is" + name + "';\n";
	strRule += "cmd += '( ';\n";
	strRule += "for( i=0; i < arguments.length; i++ ){ \n";
	strRule += "if( arguments[i] != '' ){\n";
	strRule += "if( typeof arguments[i] == 'string' ) cmd += '\"' + arguments[i] + '\",';\n";
	strRule += "else cmd += arguments[i] + ',';\n";
	strRule += "}}\n";
	strRule += "cmd = cmd.substring(0, cmd.length-1);\n";
	strRule += "cmd += ')';\n";
	strRule += "this.qForm._queue.validation[this.qForm._queue.validation.length] = cmd;\n";
	strRule += "if( !this.validate ) return true;\n";
	strRule += "cmd = 'qFormAPI.objects.' + this.qForm._name + '._status += \"_ShowError\";' + cmd;\n";
	strRule += "this.addEvent('onblur', cmd);";
	strRule += "return true;\n";

	strRuleFN += " strRule);";
	eval("Field.prototype.validate" + name + " = " + strRuleFN);
	
	return true;
}

// define qForm validate(); prototype
function _q_validate(){
	// check the form for errors
	this.checkForErrors();

	// if there are no errors then return true
	if( this._queue.errors.length == 0 ) return true;

	var strError = "The following error(s) occurred:\n";
	for( var i=0; i < this._queue.errors.length; i++ ){
		strError += " - " + this._queue.errors[i] + "\n";
	}

	// check to see if the user is allowed to submit the form even if an error occurred
	if( this._allowSubmitOnError ){
		strError += "\nAre you sure you want to continue?";
		var result = confirm(strError);
	} else {
		alert(strError);
		var result = false;
	}

	return result;
}
qForm.prototype.validate = _q_validate;

// define qForm checkForErrors(); prototype
function _q_checkForErrors(){
	// copy the current form's status
	var status = this._status;

	// set form's status to validating	
	this._status = "validating";
	
	// start by clearing the error queue
	this._queue.errors = new Array();

	aryLocalQueue = new Array();
	aryFieldsQueued = new Array();
	var lstRequiredFields = ",";
	
	// loop through form elements
	for( var j=0; j < this._fields.length; j++ ){
		// if the current field is required, then check to make sure it's value isn't blank
		if( this[this._fields[j]].required ){
			aryLocalQueue[aryLocalQueue.length] = "qFormAPI.objects." + this._name + "." + this._fields[j] + ".isNotEmpty();";
			aryFieldsQueued[aryFieldsQueued.length] = this._fields[j];
		}
		// reset the CSS settings on the field
		if( qFormAPI.useErrorColorCoding && this[this._fields[j]].obj.style ) this[this._fields[j]].obj.style.backgroundColor = this[this._fields[j]].backgroundColor;
	}
	
	// if there are no validation routines in queue, and no required fields, then break out
	if( this._queue.validation.length == 0 && aryLocalQueue.length == 0) return true;

	// loop through the required fields queue
	for( var i=0; i < aryLocalQueue.length; i++ ){
		var thisError = eval(aryLocalQueue[i]);
		// there's an error, add the error to the array
		if( thisError.length > 0 ){
			this._queue.errors[this._queue.errors.length] = thisError;
			// if the field has thrown an error, then don't validate it later
			lstRequiredFields += aryFieldsQueued[i] + ",";
			// change the background color of required fields to red
			if( qFormAPI.useErrorColorCoding && this[aryFieldsQueued[i]].obj.style ) this[aryFieldsQueued[i]].obj.style.backgroundColor = qFormAPI.errorColor;
		}
	}

	// loop through the validation queue
	for( var i=0; i < this._queue.validation.length; i++ ){
		// strip out to the method name
		var iPOS = this._queue.validation[i].indexOf(".is");
		var field = this._queue.validation[i].substring( this._queue.validation[i].lastIndexOf(".", iPOS-1)+1, iPOS);
		// get the field name in between the brackets
		field = field.substring( field.indexOf("['")+2, field.indexOf("']"));
		// if the field is required and has already thrown an error, then don't check the field for errors
		if( lstRequiredFields.indexOf("," + field + ",") == -1 ) var thisError = eval(this._queue.validation[i]);
		else var thisError = "";
		// if a boolean answer is return, blank the error message
		if( typeof thisError == "boolean" ) thisError = "";
		// there's an error, add the error to the array
		if( thisError.length > 0 ){
			this._queue.errors[this._queue.errors.length] = thisError;
			// change the background color of failed validation fields to red
			if( qFormAPI.useErrorColorCoding && this[field].obj.style ) this[field].obj.style.backgroundColor = qFormAPI.errorColor;
		}
	}

	// set form's status back to it's last status	
	this._status = status;
	
	return true;
}
qForm.prototype.checkForErrors = _q_checkForErrors;

/******************************************************************************
 Required Functions
******************************************************************************/
// define the addEvent() function
function _addEvent(obj, event, cmd, append){
	if( arguments.length < 3 ) return alert("Invalid arguments. Please use the format \n_addEvent(object, event, command, [append]).");
	var append = _param(arguments[3], true, "boolean");
	var event = arguments[0] + "." + arguments[1].toLowerCase();
	var objEvent = eval(event);
	var strEvent = (objEvent) ? objEvent.toString() : "";
	// strip out the body of the function
	strEvent = strEvent.substring(strEvent.indexOf("{")+1, strEvent.lastIndexOf("}"));
	strEvent = (append) ? (strEvent + cmd) : (cmd + strEvent);
	strEvent += "\n";
	eval(event + " = new Function(strEvent)");
	return true;
}

// define the _functionToString() function
function _functionToString(fn, cmd, append){
	if( arguments.length < 1 ) return alert("Invalid arguments. Please use the format \n_functionToString(function, [command], [append]).");
	var append = _param(arguments[2], true, "boolean");
	var strFunction = (!fn) ? "" : fn.toString();
	// strip out the body of the function
	strFunction = strFunction.substring(strFunction.indexOf("{")+1, strFunction.lastIndexOf("}"));
	if( cmd ) strFunction = (append) ? (strFunction + cmd + "\n") : (cmd + strFunction + "\n");
	return strFunction;
}

// define the _param(value, default, type) function
function _param(v, d, t){
	// if no default value is present, use an empty string
	if( typeof d == "undefined" ) d = "";
	// if no type value is present, use "string"
	if( typeof t == "undefined" ) t = "string";
	// get the value to return, if the v param is not equal to the type, use default value
	var value = (typeof v != "undefined" && typeof v == t.toLowerCase()) ? v : d;
	return value;
}

// defined the _setContainerValues(obj) function
function _setContainerValues(obj){
	// loop through form elements
	for( var i=0; i < obj._fields.length; i++ ){
		if( obj[obj._fields[i]].container && obj[obj._fields[i]].type.substring(0,6) == "select" ){
			for( var x=0; x < obj[obj._fields[i]].obj.length; x++ ){
				obj[obj._fields[i]].obj[x].selected = (!obj[obj._fields[i]].dummyContainer);
			}
		} 
	}
}

