////////////////////////////////////////
//class Format

function Format(name, regexp, adjust, parse, dscr, trailer)
{
	this.name = name;
	this.regexp = regexp;
	this.adjust = adjust;
	this.parse = parse;
	this.dscr = dscr;
	this.trailer = trailer;
}

var Formats = new Array();

////////////////////////////////////////
//class Validator

function Validator(warnAlong) {
	this.warnAlong = warnAlong;
	this.allAtOnce = false;
	this.errorList = null;
}

function Validator_submit(form)
{
	if( this.form(form) )
		form.submit();
}

function Validator_form(form)
{
	this.allAtOnce = true;
	this.errorList = new Array();

	var anyFalse = false;
	var temp;
	for(var i = 0; i < form.elements.length; i++) {
		if( form.elements[i].onchange ) {
			form.elements[i].onchange();
		}
		else if( form.elements[i].onblur ) {
			form.elements[i].onblur();
		}
	}

	if( this.errorList.length > 0 ) {
		var message = "The following problems prevent this form from being submitted:\n";

		for(var i = 0; i < this.errorList.length; i++) {
			message += "+  " + errorMessage(this.errorList[i][0], this.errorList[i][1], this.errorList[i][2], this.errorList[i][3])
				+ "\n";
		}
		alert(message);
		return false;
	}

	if( ! this.trailer(form) ) {
		return false;
	}

	this.errorList = null;
	this.allAtOnce = false;

	return true;
}

function Validator_field(field, name, min, max, format, param, delay)
{
	if( delay && ! this.allAtOnce )
		return;

	if( field.type == 'select-multiple' || field.type == 'select-one' ) {
		// for multi-select, min/max applies to 
		// number of items selected

		var length;
		if( format == 'select-basket' )
			length = field.options.length;
		else
			length = selectCount(field, param);

		if( min != null ) {
			//length = selectCount(field);
			if( length < min ) {
				this.minMaxAlert(field, name, min, max);
				return false;
			}
		}
		if( max != null ) {
			//if( length == null ) length = selectCount(field);
			if( length > max ) {
				this.minMaxAlert(field, name, min, max);
				return false;
			}
		}
	}
	//else if( field.type == 'select-one' ) {
		//if(format != null && format == 'free') {
			//param(this, field, name);
		//}
	//}
	else if( field.type == 'checkbox' ) {
		if(format != null && format == 'free') {
			param(this, field, name);
		}
	}
	else if( field.type == 'radio' ) {
		if(format != null && format == 'free') {
			param(this, field, name);
		}
	}
	else {
		if(format != null) {
			if(format == 'free') {
				param(this, field, name);
			}
			else {

				format = formatName2Object(format);

				if( format.parse ) {
					if( format.parse == 'int' || format.parse == 'real' ) {
						if( min != null && field.value.length == 0 ) {
							this.alert(field, name, 'format', format);
							return false;
						}
					}
				}

				if( ! min && field.value.length == 0 ) {
					return true;
				}

				// if regexp matching is defined
				if( format.regexp ) {
					if( ! format.regexp.exec(field.value) ) {
						this.alert(field, name, 'format', format);
						return false;
					}

					if( format.adjust ) {
						field.value = field.value.replace(
							format.regexp, format.adjust);
					}
				}

				// if there is a trailer
				if( format.trailer ) {
						alert('Trailers not implemented');
				}

				var minMaxCheck;

				// if there is a parsable type defined
				if( format.parse ) {
					var temp;
					if( format.parse == 'int' ) {
						temp = parseInt(field.value);
					}
					else if( format.parse == 'real' ) {
						temp = parseFloat(field.value);
					}

					if( isNaN(temp) ) {
						this.alert(field, name, 'format', format);
						return false;
					}

					field.value = temp;
					minMaxCheck = field.value;
				}
				else {
					minMaxCheck = field.value.length;
				}
			}
		}
		else {
			minMaxCheck = field.value.length;
		}

		if( min != null ) {
			if( minMaxCheck < min ) {
				this.minMaxAlert(field, name, min, max,
				  field.value.length == minMaxCheck && minMaxCheck == 0,
				  field.value.length == minMaxCheck);
				return false;
			}
		}
		if( max != null ) {
			if( minMaxCheck > max ) {
				this.minMaxAlert(field, name, min, max,
					false,
					field.value.length == minMaxCheck);
				return false;
			}
		}
	}

	return true;
}

function Validator_trailer(form)
{
	return true;
}

function Validator_minMaxAlert(field, name, min, max, blank, lengthWise)
{
	var message;
	var range;
	var units;

	if( field.type == 'select-one' ) {
		this.alert(field, name, 'free', '"' + name + '" must be selected');
		return;
	}

	if( lengthWise )
		units = ' characters long'
	else
		units = '';

	if( min != null ) {
		if( max != null )
			range = "between " + min + " and " + max + units;	
		else
			range = " no less than " + min + units;
			//range = " > " + min + units;
	}
	else if( max != null ) {
		range = " no more than " + max;
		//range = " < " + max;
	}

	if( blank ) {
		message = 'Must specify a value for "' + name + '"';
		if( (min != null && min > 1) || max != null )
			message += ' (' + range + ')';
	}
	else {
		if( field.type == 'select-multiple' )
			message = '"' + name + '" must have ' + range + ' options selected';
		else 
			message = '"' + name + '" must be ' + range;
	}

	this.alert(field, name, 'free', message);
}

function Validator_alert(field, name, msgType, msgParam)
{
	if(this.allAtOnce) {
		//record alert to display all at once
		this.errorList[this.errorList.length] =
			[field, name, msgType, msgParam];
	}
	else if(this.warnAlong) {
		alert(errorMessage(field, name, msgType, msgParam));
	}
}

function errorMessage(field, name, msgType, msgParam) {
	if( msgType == 'format' ) {
		return 'Please supply a value for "' + name + '" in the following format: ' + msgParam.dscr;
	}
	else if( msgType == 'free' ) {
		return msgParam;
	}
	else {
		return "";
	}
}

new Validator(null);
Validator.prototype.submit      = Validator_submit;
Validator.prototype.form        = Validator_form;
Validator.prototype.field       = Validator_field;
Validator.prototype.alert       = Validator_alert;
Validator.prototype.minMaxAlert = Validator_minMaxAlert;
Validator.prototype.trailer     = Validator_trailer;

////////////////////////////////////////
// Standard Formats

Formats[Formats.length] = new Format(
	'name', 
	/^\s*([ A-Za-z'-]*)\s*$/,
	"$1",
	null,
	'O\'Neal',
	null);
Formats[Formats.length] = new Format(
	'fullname', 
	/^\s*([ A-Za-z'-,]*)\s*$/,
	"$1",
	null,
	'O\'Neal',
	null);
Formats[Formats.length] = new Format(
	'int',
	/^\s*(\d*),?(\d{3})?,?(\d{3})?,?(\d{3})?,?(\d{3})?,?(\d{3})?,?(\d{3})?,?(\d{3})?(\.\d+)?\s*$/,
	"$1$2$3$4$5$6$7$8$9",
	'int',
	'123',
	null);
Formats[Formats.length] = new Format(
	'real',
	/^\s*(\d*),?(\d{3})?,?(\d{3})?,?(\d{3})?,?(\d{3})?,?(\d{3})?,?(\d{3})?,?(\d{3})?(\.\d+)?\s*$/,
	"$1$2$3$4$5$6$7$8$9",
	'real',
	'123.34',
	null);
Formats[Formats.length] = new Format(
	'domain_part',
// more official	/^([A-za-z0-9]([-A-Za-z0-9]*[A-Za-z0-9])?)(\.([A-za-z0-9]([-A-Za-z0-9]*[A-Za-z0-9])?))*$/,
	/^([A-za-z0-9]([-A-Za-z0-9]*[A-Za-z0-9])?)(\.[A-za-z0-9]([-A-Za-z0-9]*[A-Za-z0-9])?)+$/,
	"",
	null,
	'mydomain.com',
	null);
Formats[Formats.length] = new Format(
	'local_part',
	/^(([^\]\[()<>@,;:\"\\\. ]+)|(\"[^\"\\]+\"))(\.(([^\]\[()<>@,;:\"\\\.]+)|(\"[^\"\\]+\")))*$/,
	"",
	null,
	'my_name99',
	null);
Formats[Formats.length] = new Format(
	'email',
// more official	/^(([^\]\[()<>@,;:\"\\\. ]+)|(\"[^\"\\]+\"))(\.(([^\]\[()<>@,;:\"\\\.]+)|(\"[^\"\\]+\")))*@([A-za-z0-9]([-A-Za-z0-9]*[A-Za-z0-9])?)(\.([A-za-z0-9]([-A-Za-z0-9]*[A-Za-z0-9])?))*$/,
	/^(([^\]\[()<>@,;:\"\\\. ]+)|(\"[^\"\\]+\"))(\.(([^\]\[()<>@,;:\"\\\.]+)|(\"[^\"\\]+\")))*@([A-za-z0-9]([-A-Za-z0-9]*[A-Za-z0-9])?)(\.[A-za-z0-9]([-A-Za-z0-9]*[A-Za-z0-9])?)+$/,
	"",
	null,
	'my_name99@mydomain.com',
	null);
Formats[Formats.length] = new Format(
	'money',
	/^\s*\$?(\d*),?(\d{3})?,?(\d{3})?,?(\d{3})?,?(\d{3})?,?(\d{3})?,?(\d{3})?,?(\d{3})?(\.\d{1,2})?\s*$/,
	"$1$2$3$4$5$6$7$8$9",
	'real',
	'$123,123.00 ($ and , optional)',
	null);
Formats[Formats.length] = new Format(
	'httpurl',
	/^\s*(http:\/\/|)(.*)\s*/,
	"$2",
	null,
	'www.exampledomain.com',
	null);

////////////////////////////////////////
// functions

function selectCount(select, skip)
{
	var result = 0;
	for(var i = 0; i < select.length; i++) {
		if(select.options[i].selected &&
			( !skip || select.options[i].value != skip ) ) {

			result++;
		}
	}
	return result;
}

function formatName2Object(name)
{
	if(name == '[object Object]')
		return name;

	for(var i = 0; i < Formats.length; i++)
		if(Formats[i].name == name)
			return Formats[i];

	return null;
}

