/*
 *	Validator.js
 *
 *	A JavaScript object for validating forms.
 *
 *	The form to be validated is passed as an argument to the constructor (and so must
 *	follow the form declaration in the HTML). The type of validation to do is recorded
 *	using the register function. Forms can be validated as strings, integers, positive
 *	integers, integers within a specified range, floating-point numbers (useful for
 *	monetary values), dates (as parseable by Date.parse()), email addresses or groups of
 *	radio buttons or check boxes.
 *
 *	When everything is registered call validate, this is usally done in a form's onSubmit
 *	handler. Validate will check the form and call either the valid function or error
 *	function passed to the constructor, allowing error reporting to be tailored to the
 *	application.
 *
 *	Example use:
 *
 *		function err(e) { alert("Error!"); }
 *		function err(e) { alert("Valid!"); }
 *
 *		v = new Validator(document.form, err, valid);
 *
 *	Note: the constructor must be called after the </form> tag as it is the the form
 *	object is passed to the function rather than just the name.
 *
 *	Afternoon <noon at aftnn.org>, 2002
 *
 */

// IE 5 Mac appears not to support Array.push()!
Array.prototype.push = function (el) { this[this.length] = el; }

// Constructor for Validator objects
function Validator(formToValidate, errorFunction, validFunction) {
	this.errors = 0;
	
	this.form = formToValidate;
	this.errFunc = errorFunction;
	this.validFunc = validFunction;

	this.elements = new Array();
}

// register names of elements to be validated, specify criteria
Validator.prototype.register = function (element, type, dependency) {
	switch (type) {
		case "int": case "posint": case "email": case "radiogroup": case "checkbox":
		case "select": case "multiselect": case "url":
			this.elements[this.elements.length] = new Array(element, type, dependency);
			break;
		default: this.elements[this.elements.length] = new Array(element, "string", dependency);
	}
}

Validator.prototype.bulk_register = function (elements) {
	if (elements.length > 0)
		for (i=0; i<elements.length; i++) {
			if (elements[i].length > 2) this.register(elements[i][0], elements[i][1], elements[i].slice(2));
			if (elements[i].length == 2) this.register(elements[i][0], elements[i][1]);
			if (elements[i].length == 1) this.register(elements[i][0]);
		}
}

// Work horse function, check on submit
Validator.prototype.validate = function () {
	for (e=0; e<this.elements.length; e++) {
		el = this.elements[e]; vl = this.form[el[0]].value; n = el[0]; d = el[2];
		switch (el[1]) {
			case "int":			this.check_int(n, vl, d);			break;
			case "posint":		this.check_posint(n, vl, d);		break;
			case "email":		this.check_email(n, vl, d);			break;
			case "url":			this.check_url(n, vl, d);			break;
			case "radiogroup":	this.check_radiogroup(n, vl, d);	break;
			case "checkbox":	this.check_checkbox(n, vl, d);		break;
			case "select":		this.check_select(n, vl, d);		break;
			case "multiselect":	this.check_multiselect(n, vl, d);	break;
			default:			this.check_string(n, vl, d);
		}
	}

	if (this.errors == 0) return true;
	else {
		this.errors = 0;
		alert("There are problems with the information you have entered.\nPlease check the elements marked with a red cross.");
		return false;
	}
}

// User should have registered a callback function to implement error displaying
Validator.prototype.flag_error = function (name) {
	this.errors++;
	this.errFunc(name);
}

Validator.prototype.flag_valid = function (name) { this.validFunc(name); }

Validator.prototype.required = function (dp) {
 	if (dp == null) return true;
	if (dp == false) return false;

	req = true;
	rgroups = new String();
	
	for (i=0; i<this.form.length; i++) if (this.form[i].type == "radio" && rgroups.indexOf(this.form[i].name + ",") == -1) rgroups += this.form[i].name + ",";

	for (i=0; i<dp.length; i+=2) {
		if (rgroups.indexOf(dp[i]) != -1) {
			for (j=0; j<this.form.length; j++)
				if (this.form[j].type == "radio" && this.form[j].value == dp[i+1] &&
					this.form[j].name == dp[0] && this.form[j].checked) req = false;
		}
		else {
			if ((dp[i+1] == "checked" && this.form[dp[i]].checked) ||
				(dp[i+1] == "unchecked" && this.form[dp[i]].checked == false) ||
				(dp[i+1] == "*" && this.form[dp[i]].value != "") ||
				(this.form[dp[i]].value == dp[i+1]))
				req = false;
		}
	}
	
	return req;
}

Validator.prototype.check_string = function (name, value, dependency) {
	if (this.required(dependency) && value == "") this.flag_error(name);
	else this.flag_valid(name);
}

Validator.prototype.check_int = function (name, value, dependency) {
	if (this.required(dependency) && isNaN(parseInt(value, 10))) this.flag_error(name);
	else this.flag_valid(name);
}

Validator.prototype.check_posint = function (name, value, dependency) {
	i = parseInt(value, 10);
	if (this.required(dependency) && (isNaN(i) || i < 0)) this.flag_error(name);
	else this.flag_valid(name);
}

Validator.prototype.check_email = function (name, value, dependency) {
	if (this.required(dependency) && new String(value).search(/^[\.\w-]+@[a-zA-Z0-9][\w-]+(\.[a-zA-Z0-9][\w-]+)+$/) == -1) this.flag_error(name);
	else this.flag_valid(name);
}

Validator.prototype.check_url = function (name, value, dependency) {
	if (this.required(dependency) && new String(value).search(/^http:\/\/([\.\w-\:]+@)?[a-zA-Z0-9][\w-]+(\.[a-zA-Z0-9][\w-]+)+(\/.+)?$/) == -1) this.flag_error(name);
	else this.flag_valid(name);
}

Validator.prototype.check_radiogroup = function (name, value, dependency) {
	valid = false;
	
	if (this.required(dependency)) {
		// find a radio button with the given name that is checked, otherwise group is invalid
		for (i=0; i<this.form.length; i++) if (this.form[i].type == "radio" && this.form[i].name == name && this.form[i].checked == true) valid = true;
	
		if (!valid) this.flag_error(name);
		else this.flag_valid(name);
	}
	else this.flag_valid(name);
}

Validator.prototype.check_checkbox = function (name, value, dependency) {
	if (this.required(dependency) && !this.form[name].checked) this.flag_error(name);
	else this.flag_valid(name);
}

Validator.prototype.check_select = function (name, value, dependency) {
	if (this.required(dependency) && this.form[name].options[this.form[name].selectedIndex].value == "") this.flag_error(name);
	else this.flag_valid(name);
}

Validator.prototype.check_multiselect = function (name, value, dependency) {
	if (this.required(dependency)) {
		valid = false;
		for (i=0; i<this.form[name].length; i++) if (this.form[name].options[i].selected == true) valid = true;
	
		if (this.form[name].value == "") this.flag_error(name);
		else this.flag_valid(name);
	}
	else this.flag_valid(name);
}


/*
 *	Function to gather the data from complex input controls (calendar, time drop-downs)
 *	and format it as a string suitable for passing back to the server.
 */
 
function mkdate(out, d, m, y) {
	if (d < 10) d = "0" + d;
	if (m < 10) m = "0" + m;
	document.forms[0].elements[out].value = d + "/" + m + "/" + y;
}

function mktime(out, prefix) {
	df = document.forms[0].elements;
	h = parseInt(df[prefix + "hours"].value, 10);
	m = parseInt(df[prefix + "mins"].value, 10);

	if (h >= 0 && h < 24 && m >= 0 && m < 60) {
		if (m < 10) m = "0" + m;
		if (h < 10) h = "0" + h;

		df[out].value = h + ":" + m;
	}
	else df[out].value = "";
}

function checkLength(el, len, name) {
	if (el.value.length > len) alert("The limit for the \"" + name + "\" field value is " + len + " characters. You have entered " + el.value.length + " characters.\n\nThe extra " + (len - el.value.length) + " characters will be removed unless you amend the value before submitting this form.");
}
