
/* Form Checking Aid
 *
 * Autor:	Gunnar Zötl
 * Date:	12.03.2001
 *
 * Synopsis:
 *		bool form_i_check(event, field, typespec, ...)
 *		bool form_check_final(form)
 *
 * Usage:
 *		<form ... onsubmit="return form_check_final(this)">
 *			...
 * 			<input type="text" ... onkeypress="return form_i_check(...)">
 *			...
 *		</form>
 *
 * Description:
 *		Use these functions to check the contents of form fields. There are
 *		2 functions, one that checks the contents of a given field interactively
 *		while the user types its contents, and another ont that does the final
 *		checking for the entire form. The final check reuses the arguments given
 *		to the individual fields check routines, so you don't have to specify
 *		any additional conditions.
 *
 *		The function form_i_check() does the interactive check of the fields contents.
 *		The arguments are:
 *			event		the event object, should always be event.
 *			field		the field to check, usually this
 *			typespec	valid type and other resitictions for the fields contents.
 *						Values are:
 *							T_INT	field value is integer
 *							T_FLOAT	field value is a floating point number. The floating point
 *									checking routine also allows for ',' as a decimal delimiter. The
 *									final check converts this to '.', so that floats are always
 *									submitted with '.' as the decimal delimiter.
 *							T_SCI	field value is a floating point number in scientific notation,
 *									eg 123,45e17, with e meaning '*10^', so that the former term means
 *									123.45 * 10^17.
 *									All notes for T_FLOAT above also apply here.
 *							T_REGEX	field value is to match regular expression. Be careful, though,
 *									that partial valid values also match. If you want to provide
 *									different expressions for interactive and final check, then use
 *									a construct like this:
 *										FORM_FINAL ? /re for final check/ : /re for interactive check/
 *									The regular expression is passed in the first optional arg.
 *
 *						You may specify the following additional restrictions:
 *							T_MINL	fields length may not be less than arg
 *							T_MAXL	fields length may not exceed arg
 *							T_RANGEL both T_MINL and T_MAXL
 *							T_MINV	fields numerical value may not be less than arg
 *							T_MAXV	fields numerical value may not be more than arg
 *							T_RANGEV both T_MINV and T_MAXV
 *							T_OPT	value is optional
 *
 *						Extra arguments are specified in the following order:
 *							REGEX, MINL, MAXL, MINV, MAXV
 *						Non required arguments are not given. Thus, if your typespec looks like this:
 *							T_INT | T_MAXL
 *						then there is only one additional argument, the maximum field length.
 *
 *						Note that T_MINL, T_MINV and T_MAXV are only checked on the final check.
 *
 *		The function form_check_final() does the final check.
 *		The arguments are:
 *			form	the form to check, usually this
 *
 *		For the check, the function inspects all fields wether they have an onkeypress handler,
 *		and if so, calls it with the global variable "FORM_FINAL" set to true. This alters a few
 *		regular expressions for builtin tests, and also checks the min and max values for
 *		numerical fields. Fields that do not have a handler are not checked.
 *
 * NOTE:
 *		There is another file accompagnying this one, which holds the templates for the error
 *		messages. You can alter those to fit your locale or whatever.
 */

 // global variables, see above
var FORM_FINAL = false

// Type Codes
var T_MASK	= 15
var T_INT	= 1
var T_FLOAT	= 2
var T_SCI	= 3
var T_REGEX	= 4
var T_MINL	= 16
var T_MAXL	= 32
var T_MINV	= 64
var T_MAXV	= 128
var T_RANGEL = T_MINL | T_MAXL
var T_RANGEV = T_MINV | T_MAXV
var T_OPT	= 256

// Error Codes, internal use only.
var E_BASE	= 16
var E_INVAL	= E_BASE + 0
var E_MINL	= E_BASE + 1
var E_MAXL	= E_BASE + 2
var E_RANGEL = E_BASE + 3
var E_MINV	= E_BASE + 4
var E_MAXV	= E_BASE + 5
var E_RANGEV = E_BASE + 6

function form_i_check(evt, field, typespec)
{
	var val
	var res = true
	var checkre = /.*/
	var isnum = false
	var type = typespec & T_MASK
	var xargs = form_i_check.arguments;
	var xidx = 3
	var mini
	var maxi

	form_i_check.errmsg = ''

	if (!FORM_FINAL) {
		var ch = evt.keyCode || evt.which
		val = field.value + (FORM_FINAL ? '' : String.fromCharCode(ch))
	} else
		val = field.value

	// check fields types
	if 		  (type == T_INT) {
		isnum = true
		checkre = FORM_FINAL ? 	/^[+-]?\d+$/ :
								/^[+-]?\d*$/
	} else if (type == T_FLOAT) {
		isnum = true
		checkre = FORM_FINAL ?	/^[+-]?\d+([.,]\d+)?$/ :
								/^[+-]?\d*([.,]\d*)?$/
	} else if (type == T_SCI) {
		isnum = true
		checkre = FORM_FINAL ?	/^[+-]?\d+([.,]\d+)?([eE][+-]?\d+)?$/ :
								/^[+-]?\d*([.,]\d*)?([eE][+-]?\d*)?$/
	} else if (type == T_REGEX) {
		checkre = xargs[xidx++]
	} else {
		checkre = FORM_FINAL ? ((typespec & T_OPT) ?	/.*/ : /.+/) :
								/.*/;
	}
	res = checkre.test(val)
	if (!res && (typespec & T_OPT)) {
		res = !val
		typespec = 0	// don't perform additional checks on an empty value
	}
	
	if (!res) form_i_check.errmsg = form_error_message(E_INVAL, field.name, val)

	// check field  length
	if (res && (typespec & T_RANGEL)) {
		if (typespec & T_MINL) {
			mini = parseInt(xargs[xidx++])
			if (FORM_FINAL) {
				res = val.length >= mini
				if (!res)
					form_i_check.errmsg = form_error_message(E_MINL, field.name, val, mini)
			}
		}
		if (typespec & T_MAXL) {
			maxi = parseInt(xargs[xidx++])
			res = res && (val.length <= maxi)
			if (!res)
				form_i_check.errmsg = form_error_message(E_MAXL, field.name, val, mini)

		}
		if (!res && ((typespec & T_RANGEL) == T_RANGEL))
			form_i_check.errmsg = form_error_message(E_RANGEL, field.name, val, mini, maxi)
	}

	// check fields range, if applicable
	if (res && isnum && FORM_FINAL) {
		val = val.replace(',', '.')
		field.value = val

		num = parseFloat(val);
		if (typespec & T_MINV) {
			mini = parseFloat(xargs[xidx++])
			res = (num >= mini)
			if (!res)
				form_i_check.errmsg = form_error_message(E_MINV, field.name, val, mini)
		}

		if (typespec & T_MAXV) {
			maxi = parseFloat(xargs[xidx++])
			res = res && (num <= maxi)
			if (!res)
				form_i_check.errmsg = form_error_message(E_MAXV, field.name, val, 0, maxi)
		}

		if (!res && ((typespec & T_RANGEV) == T_RANGEV))
			form_i_check.errmsg = form_error_message(E_RANGEV, field.name, val, mini, maxi)
	}

	return res;
}
form_i_check.errmsg = ''

function form_final_check(frm)
{
	var num = frm.elements.length
	var res = true
	var inv = ''
	var errmsg = ''

	for (i = 0; i < num; ++i) {
		var el = frm.elements[i]
		if (el.onkeypress) {
			FORM_FINAL = true
			tmp = el.onkeypress()
			FORM_FINAL = false
			res = res && tmp
			if (!tmp) errmsg = errmsg + form_i_check.errmsg + "\n"
		}
	}

	if (!res) {
		alert(errmsg)
		errmsg = ''
	}

	return res
}

// Utility functions

// create an error message from actual data and a message template from fc_messages.js
function form_error_message(type, name, value, mini, maxi)
{
	var res = ''
	var messages = form_error_message_messages

	res = messages[type].replace('@field@', name)
			 			.replace('@value@', value)
			 			.replace('@min@', mini)
			 			.replace('@max@', maxi)

	return res
}
