/** 
* Copyright 2005-2008 massimocorner.com
* @author      Massimo Foti (massimo@massimocorner.com)
* @version     2.0 alpha2, 2008-04-26
* @require     tmt_core.js
* @require     tmt_form.js
*/

if(typeof(tmt) == "undefined"){
	alert("Error: tmt.core JavaScript library missing");
}

tmt.validator = {};
tmt.validator.DEFAULT_DATE_PATTERN = "YYYY-MM-DD";

// Create all the validator objects required inside the document
tmt.validator.init = function(){
	var formNodes = document.getElementsByTagName("form");
	for(var i=0; i<formNodes.length; i++){
		if(formNodes[i].getAttribute("tmt:validate") == "true"){
			// Attach a validator object to each form that requires it
			formNodes[i].tmt_validator = tmt.validator.formValidatorFactory(formNodes[i]);
			// Set the form node's onsubmit event 
			// We use a gigantic hack to preserve exiting calls attached to the onsubmit event (most likely validation routines)
			if(typeof formNodes[i].onsubmit != "function"){
				formNodes[i].onsubmit = function(){
					return tmt.validator.validateForm(this);
				}
			}
			else{
				// Store a reference to the old function
				formNodes[i].tmt_oldSubmit = formNodes[i].onsubmit;
				formNodes[i].onsubmit = function(){
					// If the existing function return true, send the form
					if(this.tmt_oldSubmit()){
						return tmt.validator.validateForm(this);
					}
					return false;
				}
			}
		}
	}
}

// Perform the validation
tmt.validator.validateForm = function(formNode){
	var errorMsg = "";
	var formValidator = formNode.tmt_validator;
	// Be sure the form contains a validator object
	if(formValidator){
		var focusGiven = false;
		// This array will store all the field validators that contains errors
		// They may be required by the callback
		var invalidFields = [];
		// Validate all the fields
		for(var i=0; i<formValidator.validators.length; i++){
			if(formValidator.validators[i].validate()){
				// Append to the global error string
				errorMsg += formValidator.validators[i].message + "\n";
				invalidFields[invalidFields.length] = formValidator.validators[i];
				// Give focus to the first invalid text/textarea field
				if(!focusGiven && (formValidator.validators[i].type == "text")){
					formValidator.validators[i].getFocus();
					focusGiven = true;
				}
			}
		}
		if(errorMsg != ""){
			// We have errors, display them
			if(!formValidator.callback){
				// We don't have callbacks, just display an alert
				alert(errorMsg);
			}
			else{
				// Invoke the callbak, it will take care of displaying the errors
				eval(formValidator.callback + "(formNode, invalidFields)");
			}
		}
		else{
			// Everything is fine, disable form submission to avoid multiple submits
			formValidator.blockSubmit();
		}
	}
	return errorMsg.length == 0; 
}

/* Object factories */

// Create a form validator
tmt.validator.formValidatorFactory = function(formNode){
	var obj = {};
	// Store all the validator objects inside an array
	obj.validators = [];
	// Add the specified callback only if the function is currently defined
	if(formNode.getAttribute("tmt:callback")){
		obj.callback = formNode.getAttribute("tmt:callback");
	}
	var fieldsArray = tmt.form.getTextfieldNodes(formNode);
	for(var i=0; i<fieldsArray.length; i++){
		// Create a validator for each text field
		obj.validators[obj.validators.length] = tmt.validator.textValidatorFactory(fieldsArray[i]);
		if(fieldsArray[i].getAttribute("tmt:filters")){
			// Call the filters on the onkeyup and onblur events
			tmt.addEvent(fieldsArray[i], "keyup", function(){tmt.validator.filterField(this);});
			tmt.addEvent(fieldsArray[i], "blur", function(){tmt.validator.filterField(this);});
		}
	}
	var selectNodes = formNode.getElementsByTagName("select");
	for(var j=0; j<selectNodes.length; j++){
		// Create a validator for each select element
		obj.validators[obj.validators.length] = tmt.validator.selectValidatorFactory(selectNodes[j]);
	}
	var boxTable = tmt.validator.getGroupHash(formNode, "checkbox");
	for(var boxName in boxTable){
		// Create a validator for each group of checkboxes
		obj.validators[obj.validators.length] = tmt.validator.boxValidatorFactory(boxTable[boxName]);
	}
	var radioTable = tmt.validator.getGroupHash(formNode, "radio");
	for(var radioName in radioTable){
		// Create a validator for each group of radios
		obj.validators[obj.validators.length] = tmt.validator.radioValidatorFactory(radioTable[radioName]);
	}
	// Store all the submit buttons
	obj.buttons = tmt.form.getSubmitNodes(formNode);
	// Define a method that can block multiple submits
	obj.blockSubmit = function(){
		// Check to see if we want to disable submit buttons
		if(!formNode.getAttribute("tmt:blocksubmit") && !(formNode.getAttribute("tmt:blocksubmit") == "false")){
			// Disable each submit button
			for(var i=0; i<obj.buttons.length; i++){
				if(obj.buttons[i].getAttribute("tmt:waitmessage")){
					obj.buttons[i].value = obj.buttons[i].getAttribute("tmt:waitmessage");
				}
				obj.buttons[i].disabled = true;
			}
		}
	}
	
	return obj;
}

// Create an abstract field validator, to be extended/decorated for specific needs
tmt.validator.abstractValidatorFactory = function(fieldNode){
	var obj = {};
	obj.message = "";
	obj.name = fieldNode.name;
	obj.errorClass = "";
	if(fieldNode.getAttribute("tmt:message")){
		obj.message = fieldNode.getAttribute("tmt:message");
	}
	if(fieldNode.getAttribute("tmt:errorclass")){
		obj.errorClass = fieldNode.getAttribute("tmt:errorclass");
	}
	obj.flagInvalid = function(){
		// Append the CSS class to the existing one
		if(obj.errorClass){
			tmt.addClass(fieldNode, obj.errorClass);
		}
		// Set the title attribute in order to show a tootip
		fieldNode.setAttribute("title", obj.message);
	}
	obj.flagValid = function(){
		// Remove the CSS class
		if(obj.errorClass){
			tmt.removeClass(fieldNode, obj.errorClass);
		}
		fieldNode.removeAttribute("title");
	}
	obj.validate = function(){
		// If the field contains error, flag it as invalid and return the error message
		// Be careful, this method contains multiple exit points!!!
		if(fieldNode.disabled){
			// Disabled fields are always valid
			obj.flagValid();
			return false;
		}
		if(!obj.isValid()){
			obj.flagInvalid();
			return true;
		}
		else{
			obj.flagValid();
			return false;
		}
	}
	
	return obj;
}

// Create a validator for text and texarea fields
tmt.validator.textValidatorFactory = function(fieldNode){
	// Create a generic validator, than add specific properties and methods as needed
	var obj = tmt.validator.abstractValidatorFactory(fieldNode);
	obj.type = "text";
	var required = false;
	if(fieldNode.getAttribute("tmt:required")){
		required = fieldNode.getAttribute("tmt:required");
	}
	// Put focus and cursor inside the field
	obj.getFocus = function(){
		// This try block is required to solve an obscure issue with IE and hidden fields
		try{
			fieldNode.focus();
			fieldNode.select();
		}
		catch(exception){
		}
	}
	// Check if the field is conditionally required
	obj.isConditional = function(){
		return required == "conditional";
	}
	// Check if the field is empty
	obj.isEmpty = function(){
		return fieldNode.value == "";
	}
	// Check if the field is required
	obj.isRequired = function(){
		return required == "true";
	}
	// Check if the field satisfy the rules associated with it
	// Be careful, this method contains multiple exit points!!!
	obj.isValid = function(){
		// The tmt:required="conditional" attribute has a special meaning. 
		// The field isn't strictly required, so it may sometimes be empty, 
		// but before we let it go, we need to check any rule that may apply to it
		if(obj.isEmpty() && !obj.isConditional()){
			if(obj.isRequired()){
				return false;
			}
			else{
				return true;
			}
		}
		else{
			// It's empty, but conditionally required. Loop over all the available rules
			for(var rule in tmt.validator.rules){
				// Check if the current rule is required for the field
				if(fieldNode.getAttribute("tmt:" + rule)){
					// Invoke the rule
					if(!eval("tmt.validator.rules." + rule + "(fieldNode)")){
						return false;
					}
				}
			}
		}
		return true;
	}
	
	return obj;
}

// Create a validator for <select> fields
tmt.validator.selectValidatorFactory = function(selectNode){
	// Create a generic validator, than add specific properties and methods as needed
	var obj = tmt.validator.abstractValidatorFactory(selectNode);
	obj.type = "select";
	var required = false;
	var invalidIndex;
	if(selectNode.getAttribute("tmt:invalidindex")){
		invalidIndex = selectNode.getAttribute("tmt:invalidindex");
	}
	var invalidValue;
	if(selectNode.getAttribute("tmt:invalidvalue") != null){
		invalidValue = selectNode.getAttribute("tmt:invalidvalue");
	}
	// Check if the field is required
	obj.isRequired = function(){
		return required;
	}
	// Check if the field satisfy the rules associated with it
	// Be careful, this method contains multiple exit points!!!
	obj.isValid = function(){
		// Whenever a "size" attribute is available, the browser reports -1 as selectedIndex
		// Fix this weirdness
		if(selectNode.selectedIndex == -1){
			selectNode.selectedIndex = 0;
		}
		// Check for index
		if(selectNode.selectedIndex == invalidIndex){
			return false;
		}
		// Check for value
		if(selectNode.value == invalidValue){
			return false;
		}
		// Loop over all the available rules
		for(var rule in tmt.validator){
			// Check if the current rule is required for the field
			if(selectNode.getAttribute("tmt:" + rule)){
				// Invoke the rule
				if(!eval("tmt.validator." + rule + "(selectNode)")){
					return false;
				}
			}
		}
		return true;
	}
	
	return obj;
}

// Create generic validator for grouped fields (radio and checkboxes)
tmt.validator.groupValidatorFactory = function(buttonGroup){
	var obj = {};
	obj.name = buttonGroup.name;
	obj.message = "";
	obj.errorClass = "";
	// Since fields from the same group can have conflicting attribute values, the last one win
	for(var i=0; i<buttonGroup.elements.length; i++){
		if(buttonGroup.elements[i].getAttribute("tmt:message")){
			obj.message = buttonGroup.elements[i].getAttribute("tmt:message");
		}
		if(buttonGroup.elements[i].getAttribute("tmt:errorclass")){
			obj.errorClass = buttonGroup.elements[i].getAttribute("tmt:errorclass");
		}
	}
	obj.flagInvalid = function(){
		// Append the CSS class to the existing one
		if(obj.errorClass){
			for(var i=0; i<buttonGroup.elements.length; i++){
				tmt.addClass(buttonGroup.elements[i], obj.errorClass);
				buttonGroup.elements[i].setAttribute("title", obj.message);
			}
		}
	}
	obj.flagValid = function(){
		// Remove the CSS class
		if(obj.errorClass){
			for(var i=0; i<buttonGroup.elements.length; i++){
				tmt.removeClass(buttonGroup.elements[i], obj.errorClass);
				buttonGroup.elements[i].removeAttribute("title");
			}
		}
	}
	obj.validate = function(){
		var errorMsg = "";
		// If the field group contains error, flag it as invalid and return the error message
		if(!obj.isValid()){
			errorMsg += this.message;
			obj.flagInvalid();
		}
		else{
			obj.flagValid();
		}
		return errorMsg;
	}
	
	return obj;
}

// Create a checkbox validator (out of a group of boxes sharing the same name)
tmt.validator.boxValidatorFactory = function(boxGroup){
	var obj = tmt.validator.groupValidatorFactory(boxGroup);
	obj.type = "box";
	var minchecked = 0;
	var maxchecked = boxGroup.elements.length;
	// Since checkboxes from the same group can have conflicting attribute values, the last one win
	for(var i=0; i<boxGroup.elements.length; i++){
		if(boxGroup.elements[i].getAttribute("tmt:minchecked")){
			minchecked = boxGroup.elements[i].getAttribute("tmt:minchecked");
		}
		if(boxGroup.elements[i].getAttribute("tmt:maxchecked")){
			maxchecked = boxGroup.elements[i].getAttribute("tmt:maxchecked");
		}
	}
	// Check if the boxes validate
	obj.isValid = function(){
		var checkCounter = 0;
		for(var i=0; i<boxGroup.elements.length; i++){
		    // For each checked box, increase the counter
			if(boxGroup.elements[i].checked){
				checkCounter++;
			}
		}
		return (checkCounter >=  minchecked) && (checkCounter <= maxchecked);
	}
	
	return obj;
}

// Create a radio validator (out of a group of radios sharing the same name)
tmt.validator.radioValidatorFactory = function(radioGroup){
	var obj = tmt.validator.groupValidatorFactory(radioGroup);
	obj.type = "radio";
	
	obj.isRequired = function(){
		var requiredFlag = false;
		// Since radios from the same group can have conflicting attribute values, the last one win
		for(var i=0; i<radioGroup.elements.length; i++){
			if(radioGroup.elements[i].disabled == false){
				if(radioGroup.elements[i].getAttribute("tmt:required")){
					requiredFlag = radioGroup.elements[i].getAttribute("tmt:required");
				}
			}
		}
		return requiredFlag;
	}
	
	// Check if the radio validate
	obj.isValid = function(){
		if(obj.isRequired()){
			for(var i=0; i<radioGroup.elements.length; i++){
				// As soon as one is checked, we are fine
				if(radioGroup.elements[i].checked){
					return true;
				}
			}
			return false;
		}
		// It's not required, so it's fine
		else{
			return true;
		}	
	}
	
	return obj;
}

// This object stores all the validation rules
// Every rule is stored as a method that accepts the field node as argument and return a boolean
tmt.validator.rules = {};

tmt.validator.rules.datepattern = function(fieldNode){
	var datObj = tmt.validator.dateStrToObj(fieldNode.value, fieldNode.getAttribute("tmt:datepattern"));
	if(datObj){
		return true;
	}
	return false;
}

tmt.validator.rules.maxdate = function(fieldNode){
	// Default pattern
	var pattern = tmt.validator.DEFAULT_DATE_PATTERN;
	if(fieldNode.getAttribute("tmt:datepattern")){
		pattern = fieldNode.getAttribute("tmt:datepattern");
	}
	var valueDate = tmt.validator.dateStrToObj(fieldNode.value, pattern);
	var maxDate = tmt.validator.dateStrToObj(fieldNode.getAttribute("tmt:maxdate"), pattern);
	if(valueDate && maxDate){
		return valueDate <= maxDate;
	}
	return false;
}

tmt.validator.rules.mindate = function(fieldNode){
	// Default pattern
	var pattern = tmt.validator.DEFAULT_DATE_PATTERN;
	if(fieldNode.getAttribute("tmt:datepattern")){
		pattern = fieldNode.getAttribute("tmt:datepattern");
	}
	var valueDate = tmt.validator.dateStrToObj(fieldNode.value, pattern);	
	var minDate = tmt.validator.dateStrToObj(fieldNode.getAttribute("tmt:mindate"), pattern);
	if(valueDate && minDate){
		return valueDate >= minDate;
	}
	return false;
}

tmt.validator.rules.equalto = function(fieldNode){
	var twinNode = document.getElementById(fieldNode.getAttribute("tmt:equalto"));
	return twinNode.value == fieldNode.value;
}

tmt.validator.rules.maxlength = function(fieldNode){
	if(fieldNode.value.length > fieldNode.getAttribute("tmt:maxlength")){
		return false;
	}
	return true;
}

tmt.validator.rules.maxnumber = function(fieldNode){
	if(parseFloat(fieldNode.value) > fieldNode.getAttribute("tmt:maxnumber")){
		return false;
	}
	return true;
}

tmt.validator.rules.minlength = function(fieldNode){
	if(fieldNode.value.length < fieldNode.getAttribute("tmt:minlength")){
		return false;
	}
	return true;
}

tmt.validator.rules.minnumber = function(fieldNode){
	if(parseFloat(fieldNode.value) < fieldNode.getAttribute("tmt:minnumber")){
		return false;
	}
	return true;
}

tmt.validator.rules.pattern = function(fieldNode){
	var reg = tmt.validator.patterns[fieldNode.getAttribute("tmt:pattern")];
	if(reg){
		return reg.test(fieldNode.value);
	}
	else{
		// If the pattern is missing, skip it
		return true;	
	}
}

// This object stores all the RegExp patterns for strings
tmt.validator.patterns = {};
tmt.validator.patterns.email = new RegExp("^[\\w\\.=-]+@[\\w\\.-]+\\.[\\w\\.-]{2,4}$");
tmt.validator.patterns.lettersonly = new RegExp("^[a-zA-Z]*$");
tmt.validator.patterns.alphanumeric = new RegExp("^\\w*$");
tmt.validator.patterns.integer = new RegExp("^-?\\d\\d*$");
tmt.validator.patterns.positiveinteger = new RegExp("^\\d\\d*$");
tmt.validator.patterns.number = new RegExp("^-?(\\d\\d*\\.\\d*$)|(^-?\\d\\d*$)|(^-?\\.\\d\\d*$)");
tmt.validator.patterns.filepath_pdf = new RegExp("\\\\[\\w_]*\\.([pP][dD][fF])$");
tmt.validator.patterns.filepath_jpg_gif = new RegExp("\\\\[\\w_]*\\.([gG][iI][fF])|([jJ][pP][eE]?[gG])$");
tmt.validator.patterns.filepath_jpg = new RegExp("\\\\[\\w_]*\\.([jJ][pP][eE]?[gG])$");
tmt.validator.patterns.filepath_zip = new RegExp("\\\\[\\w_]*\\.([zZ][iI][pP])$");
tmt.validator.patterns.filepath = new RegExp("\\\\[\\w_]*\\.\\w{3}$");

// This objects stores all the info required for date validation
tmt.validator.datePatterns = {};

// Create an object that stores date validation's info
tmt.validator.createDatePattern = function(rex, year, month, day, separator){
	var infoObj = {};
	infoObj.rex = new RegExp(rex);
	infoObj.y = year;
	infoObj.m = month;
	infoObj.d = day;
	infoObj.s = separator;
	return infoObj;
}

tmt.validator.datePatterns["YYYY-MM-DD"] = tmt.validator.createDatePattern("^\([0-9]{4}\)\\-\([0-1][0-9]\)\\-\([0-3][0-9]\)$", 0, 1, 2, "-");
tmt.validator.datePatterns["YYYY-M-D"] = tmt.validator.createDatePattern("^\([0-9]{4}\)\\-\([0-1]?[0-9]\)\\-\([0-3]?[0-9]\)$", 0, 1, 2, "-");
tmt.validator.datePatterns["MM.DD.YYYY"] = tmt.validator.createDatePattern("^\([0-1][0-9]\)\\.\([0-3][0-9]\)\\.\([0-9]{4}\)$", 2, 0, 1, ".");
tmt.validator.datePatterns["M.D.YYYY"] = tmt.validator.createDatePattern("^\([0-1]?[0-9]\)\\.\([0-3]?[0-9]\)\\.\([0-9]{4}\)$", 2, 0, 1, ".");
tmt.validator.datePatterns["MM/DD/YYYY"] = tmt.validator.createDatePattern("^\([0-1][0-9]\)\/\([0-3][0-9]\)\/\([0-9]{4}\)$", 2, 0, 1, "/");
tmt.validator.datePatterns["M/D/YYYY"] = tmt.validator.createDatePattern("^\([0-1]?[0-9]\)\/\([0-3]?[0-9]\)\/\([0-9]{4}\)$", 2, 0, 1, "/");
tmt.validator.datePatterns["MM-DD-YYYY"] = tmt.validator.createDatePattern("^\([0-21][0-9]\)\\-\([0-3][0-9]\)\\-\([0-9]{4}\)$", 2, 0, 1, "-");
tmt.validator.datePatterns["M-D-YYYY"] = tmt.validator.createDatePattern("^\([0-1]?[0-9]\)\\-\([0-3]?[0-9]\)\\-\([0-9]{4}\)$", 2, 0, 1, "-");
tmt.validator.datePatterns["DD.MM.YYYY"] = tmt.validator.createDatePattern("^\([0-3][0-9]\)\\.\([0-1][0-9]\)\\.\([0-9]{4}\)$", 2, 1, 0, ".");
tmt.validator.datePatterns["D.M.YYYY"] = tmt.validator.createDatePattern("^\([0-3]?[0-9]\)\\.\([0-1]?[0-9]\)\\.\([0-9]{4}\)$", 2, 1, 0, ".");
tmt.validator.datePatterns["DD/MM/YYYY"] = tmt.validator.createDatePattern("^\([0-3][0-9]\)\/\([0-1][0-9]\)\/\([0-9]{4}\)$", 2, 1, 0, "/");
tmt.validator.datePatterns["D/M/YYYY"] = tmt.validator.createDatePattern("^\([0-3]?[0-9]\)\/\([0-1]?[0-9]\)\/\([0-9]{4}\)$", 2, 1, 0, "/");
tmt.validator.datePatterns["DD-MM-YYYY"] = tmt.validator.createDatePattern("^\([0-3][0-9]\)\\-\([0-1][0-9]\)\\-\([0-9]{4}\)$", 2, 1, 0, "-");
tmt.validator.datePatterns["D-M-YYYY"] = tmt.validator.createDatePattern("^\([0-3]?[0-9]\)\\-\([0-1]?[0-9]\)\\-\([0-9]{4}\)$", 2, 1, 0, "-");

// This object stores all the info required for filters
tmt.validator.filters = {};

// Create an object that stores filters's info
tmt.validator.createFilter = function(rex, replaceStr){
	var infoObj = {};
	infoObj.rex = new RegExp(rex, "g");
	infoObj.str = replaceStr;
	return infoObj;
}

tmt.validator.filters.ltrim = tmt.validator.createFilter ("^(\\s*)(\\b[\\w\\W]*)$", "$2");
tmt.validator.filters.rtrim = tmt.validator.createFilter ("^([\\w\\W]*)(\\b\\s*)$", "$1");
tmt.validator.filters.nospaces = tmt.validator.createFilter ("\\s*", "");
tmt.validator.filters.nocommas = tmt.validator.createFilter (",", "");
tmt.validator.filters.nodots = tmt.validator.createFilter ("\\.", "");
tmt.validator.filters.noquotes = tmt.validator.createFilter ("'", "");
tmt.validator.filters.nodoublequotes = tmt.validator.createFilter ('"', "");
tmt.validator.filters.nohtml = tmt.validator.createFilter ("<[^>]*>", "");
tmt.validator.filters.alphanumericonly = tmt.validator.createFilter ("[^\\w]", "");
tmt.validator.filters.numbersonly = tmt.validator.createFilter ("[^\\d]", "");
tmt.validator.filters.lettersonly = tmt.validator.createFilter ("[^a-zA-Z]", "");
tmt.validator.filters.commastodots = tmt.validator.createFilter (",", ".");
tmt.validator.filters.dotstocommas = tmt.validator.createFilter ("\\.", ",");
tmt.validator.filters.numberscommas = tmt.validator.createFilter ("[^\\d,]", "");
tmt.validator.filters.numbersdots = tmt.validator.createFilter ("[^\\d\\.]", "");

// Clean up the field based on filter's info
tmt.validator.filterField = function(fieldNode){
	var filtersArray = fieldNode.getAttribute("tmt:filters").split(",");
	for(var i=0; i<filtersArray.length; i++){
		var filtObj = tmt.validator.filters[filtersArray[i]];
		// Be sure we have the filter's data, then clean up
		if(filtObj){
			fieldNode.value = fieldNode.value.replace(filtObj.rex, filtObj.str)
		}
		// We handle demoroziner as a special case
		if(filtersArray[i] == "demoronizer"){
			fieldNode.value = tmt.form.stringDemoronizer(fieldNode.value);
		}
	}
}

/* Helper functions */

// Create a Date object out of a string, based on a given RegExp pattern
tmt.validator.dateStrToObj = function(dateStr, datePattern){
	var globalObj = tmt.validator.datePatterns[datePattern];
	if(globalObj){
		// Split the date into 3 different bits using the separator
		var dateBits = dateStr.split(globalObj.s);
		// First try to create a new date out of the bits
		var testDate = new Date(dateBits[globalObj.y], (dateBits[globalObj.m]-1), dateBits[globalObj.d]);
		// Make sure values match after conversion
		var isDate = (testDate.getFullYear() == dateBits[globalObj.y])
				 && (testDate.getMonth() == dateBits[globalObj.m]-1)
				 && (testDate.getDate() == dateBits[globalObj.d]);
		// If it's a date and it matches the RegExp, it's a go
		if(isDate && globalObj.rex.test(dateStr)){
			return testDate;
		}
		return null;
	}
	return null;
}

// Return an object (sort of an hashtable) containing checkboxes/radios data
// The returned object has two properties:
// name: the group name
// boxes: an array containing the DOM node of each checkbox/radio that share the same name
tmt.validator.getGroupHash = function(formNode, type){
	// This object will store data fields, just as an hash table
	var boxHolder = {};
	var boxNodes = formNode.getElementsByTagName("input");
	for(var i=0; i<boxNodes.length; i++){
		if(boxNodes[i].getAttribute("type") && (boxNodes[i].getAttribute("type").toLowerCase() == type)){
			// Store the reference to make it easier to read the code
			var boxName = boxNodes[i].name;
			if(boxHolder[boxName]){
				// We already have an entry with the same name
				// Append the DOM node to the relevant entry inside the object
				boxHolder[boxName].elements[boxHolder[boxName].elements.length] = boxNodes[i];
			}
			else{
				// Create a brand new entry inside the object
				boxHolder[boxName] = {};
				boxHolder[boxName].name = boxName;
				// Initialize the array that will store all the DOM nodes that share the same name
				boxHolder[boxName].elements = new Array;
				boxHolder[boxName].elements[0] = boxNodes[i];
			}
		}
	}
	return boxHolder;
}

// Display errors inside a box
tmt.validator.displayErrorBox = function(formNode, validators){
	var htmlStr = "<ul>";
	// Create a <ul> for each error
	for(var i=0;i<validators.length;i++){
		htmlStr += "<li><em>" + validators[i].name + "</em>"+ validators[i].message + "</li>";
	}
	htmlStr += "</ul>";
	tmt.form.displayErrorMessage(formNode, htmlStr);
}

tmt.addEvent(window, "load", tmt.validator.init);