/*

Form constraints

Constraint functions accept one or two parameters: value and element name
	(optional).
Constraint fail functions accept two or three parameters: value, element alias
	and	element name (optional)

*/

function FormConstraint()
{
	this.constraints = new Array();
	this.constrainttypes = new Array();
	
	this.failcssclass = "needsinput";

	this.registerConstraintType("NOTEMPTY", constraint_notempty, constraint_notempty_fail);
	this.registerConstraintType("NUMERIC", constraint_isnumeric, constraint_isnumeric_fail);
	this.registerConstraintType("NUMERIC_IFNOTEMPTY", constraint_isnumeric_ifnotempty, constraint_isnumeric_ifnotempty_fail);
	this.registerConstraintType("INTEGER", constraint_isinteger, constraint_isinteger_fail);
	this.registerConstraintType("INTEGER_IFNOTEMPTY", constraint_isinteger_ifnotempty, constraint_isinteger_ifnotempty_fail);
	this.registerConstraintType("DATE_HUMAN8", constraint_validdate_human8, constraint_validdate_human8_fail);
}

Object.extend(FormConstraint.prototype,
{
	addConstraint: function(elementname, constrainttype, elementalias)
	{
		if (elementalias == null)
		{
			var elementalias = elementname;
		}
		
		var constraint = {
				elementname: elementname,
				elementalias: elementalias,
				type: constrainttype
			};
				
		this.constraints.push(constraint);
	},
	
	registerConstraintType: function(name, checkfunction, failfunction)
	{
		this.constrainttypes[name] = {
			checkfunction: checkfunction,
			failfunction: failfunction
		}
	},
	
	validate: function(form)
	{
		var constraints = this.constraints;
		
		for (var i=0; i < constraints.length; i++)
		{
			var elementname = constraints[i]['elementname'];
			var elementalias = constraints[i]['elementalias'];
			var element = form[elementname];
			
			if (typeof(element) == "undefined")
			{
				alert("No such form element '" + elementname + "'");

				return false;
			}
			
			var value = element.value;
			var newvalue = false;
			var constrainttype = constraints[i]['type'];
			
			if (typeof(this.constrainttypes[constrainttype]) == "undefined")
			{
				alert("No such constraint '" + constrainttype + "'");
				
				return false;
			}
			
			// it is no use checking elements that the user cannot fill in/change in the first place
			if (element.disabled == true || element.readOnly == true)
			{
				continue;
			}
			
			var checkfunction = this.constrainttypes[constrainttype]['checkfunction'];
			var failfunction = this.constrainttypes[constrainttype]['failfunction'];

			newvalue = checkfunction(value, element.name);
			
			if (newvalue === false)
			{
				// restore the old class, if set, so any set 'needsinput' class
				// will not end up as the new oldclass
				if (typeof(element.oldclass) != "undefined")
				{
					element.className = element.oldclass;
				}
				
				// we make this one up, store old class in it so we can
				// remove the presumably red warning colour etc.
				element.oldclass = element.className;
				element.className += " " + this.failcssclass;
				
				failfunction(value, elementalias, element.name);
				
				// IE won't focus on HIDDEN elements
				if (element.type.toLowerCase == "hidden")
				{
					return false;
				}
				// the same goes for DISABLED elements.
				// explicitly == true, we don't want to find out if the
				// attribute is present or not, we want to know if it's
				// disabled
				if (element.disabled == true)
				{
					return false;
				}
				
				// use MessageBox if available, otherwise use good old alert()
				if (typeof(MessageBox) != "undefined")
				{
					document.getElementById("MBmessagebox_okbutton").focus();
					MessageBox.elementfocusonok = element;
				} else
				{
					if (element.focus)
					{
						element.focus();
					}
					if (element.select)
					{
						element.select();
					}
				}
				
				element.onkeydown = function()
				{
					this.className = this.oldclass;
				}
				element.onclick= function()
				{
					this.className = this.oldclass;
				}
				
				return false;
			} else
			{
				element.value = newvalue;
			}
		}
		
		return true;
	}
});

function constraint_warn(warntext)
{
	// use MessageBox if available, fallback on alert
	if (typeof(MessageBox) != "undefined")
	{
		MessageBox.displayMessage(warntext);
	} else
	{
		alert(warntext);
	}
}

// several constraint functions from here on
function constraint_isnumeric(val)
{
	// also accept floating point numbers
	val = val.replace(/,/, ".");
	
	if (getal = parseFloat(val) && !isNaN(val))
	{
		return val;
	} else if (getal == 0 && !isNaN(val))
	{
		return 0;
	} else
	{
		return false;
	}
}

function constraint_isnumeric_fail(val, alias)
{
	if (typeof(texts) == "undefined" || texts["CONSTRAINT_NUMERIC"] == undefined)
	{
		var warntext = alias + " must be a number!";
	} else
	{
		var warntext = alias + " " + texts["CONSTRAINT_NUMERIC"] + "!";
	}
	
	constraint_warn(warntext);
}

function constraint_notempty(val)
{
	if (val == "")
	{
		return false;
	}
	
	return val;
}

function constraint_notempty_fail(val, alias)
{
	if (typeof(texts) == "undefined" || texts["CONSTRAINT_NOTEMPTY"] == undefined)
	{
		var warntext = alias + " must not be empty!";
	} else
	{
		var warntext = alias + " " + texts["CONSTRAINT_NOTEMPTY"] + "!";
	}
	
	constraint_warn(warntext);
}

function constraint_isnumeric_ifnotempty(val)
{
	if (val == "")
	{
		return "";
	}

	// also accept floating point numbers
	val = val.replace(/,/, ".");
	
	if (getal = parseFloat(val) && !isNaN(val))
	{
		return val;
	} else if (getal == 0)
	{
		return 0;
	} else
	{
		return false;
	}
}

function constraint_isnumeric_ifnotempty_fail(val, alias)
{
	if (typeof(texts) == "undefined" || texts["CONSTRAINT_NUMERICOREMPTY"] == undefined)
	{
		var warntext = alias + " must be numeric or empty!";
	} else
	{
		var warntext = alias + " " + texts["CONSTRAINT_NUMERICOREMPTY"] + "!";
	}
	
	constraint_warn(warntext);
}

function constraint_isinteger(val)
{
	if (parseInt(val, 10) == val)
	{
		return val;
	} else
	{
		return false;
	}
}

function constraint_isinteger_fail(val, alias)
{
	if (typeof(texts) == "undefined" || texts["CONSTRAINT_INTEGER"] == undefined)
	{
		var warntext = alias + " must be a whole number!";
	} else
	{
		var warntext = alias + " " + texts["CONSTRAINT_INTEGER"] + "!";
	}
	
	constraint_warn(warntext);
}

function constraint_isinteger_ifnotempty(val)
{
	if (val == "")
	{
		return "";
	}

	if (parseInt(val, 10) == val)
	{
		return val;
	} else
	{
		return false;
	}
}

function constraint_isinteger_ifnotempty_fail(val, alias)
{
	if (typeof(texts) == "undefined" || texts["CONSTRAINT_INTEGEROREMPTY"] == undefined)
	{
		var warntext = alias + " must be a whole number or empty!";
	} else
	{
		var warntext = alias + " " + texts["CONSTRAINT_INTEGEROREMPTY"] + "!";
	}
	
	constraint_warn(warntext);
}

function constraint_validdate_human8(val)
{
	// DDMMYY -> DDMMYYYY
	if (datepieces = /^(\d{4})(\d{2})$/.exec(val))
	{
		val = datepieces[1] + "20" + datepieces[2];
	}
	
	// DDMMYYYY -> DD-MM-YYYY
	if (datepieces = /^(\d{2})(\d{2})(\d{4})$/.exec(val))
	{
		val = datepieces[1] + "-" + datepieces[2] + "-" + datepieces[3];
	}
	
	if (datepieces = /^(\d{1,2})-(\d{1,2})-(\d{4})$/.exec(val))
	{
		// strings van maken
		var day = datepieces[1] + "";
		var month = datepieces[2] + "";
		var year = datepieces[3] + "";
	} else
	{
		return false;
	}

	var datestring = "";

	if (day.length < 2)
	{
		datestring += "0" + day + "-";
	} else
	{	
		datestring += day + "-";
	}
	
	if (month.length < 2)
	{
		datestring += "0" + month;
	} else
	{
		datestring += month;
	}
	
	datestring += "-" + year;
	
	if (date_valid_human8(datestring))
	{
		return datestring;
	} else
	{
		return false;
	}
}

function constraint_validdate_human8_fail(val, alias)
{
	if (typeof(texts) == "undefined" || texts["CONSTRAINT_HUMAN8"] == undefined)
	{
		var warntext = alias + " must be a valid date (DD-MM-YYYY)!";
	} else
	{
		var warntext = alias + " " + texts["CONSTRAINT_HUMAN8"] + "!";
	}
	
	constraint_warn(warntext);
}