﻿/** 
* Copyright 2005 massimocorner.com
* @author      Massimo Foti (massimo@massimocorner.com)
* @version     1.2.3, 2005-11-21
 */
// Create all the validator objects required inside the document
function tmt_validatorInit(){
 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 = new tmt_formValidator(formNodes[i]);
   // Set the form node's onsubmit event 
   formNodes[i].onsubmit = function(){
    return tmt_validateForm(this);
   }
  }
 }
}
// Perform the validation
function tmt_validateForm(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 = new Array();
  // 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 constructors */
// Form validator
function tmt_formValidator(formNode){
 // Store all the validator objects inside an array
 this.validators = new Array();
 // Add the specified callback only if the function is currently defined
 if(formNode.getAttribute("tmt:callback") && window[formNode.getAttribute("tmt:callback")]){
  this.callback = formNode.getAttribute("tmt:callback");
 }
 var fieldsArray = tmt_getTextfieldNodes(formNode);
 for(var i=0; i<fieldsArray.length; i++){
  // Create a validator for each text field
  this.validators[this.validators.length] = tmt_textValidatorFactory(fieldsArray[i]);
  
  if(fieldsArray[i].getAttribute("type")){
   // Set the onchange event for each image upload validation
   if((fieldsArray[i].getAttribute("type").toLowerCase() == "file") && (fieldsArray[i].getAttribute("tmt:image") == "true")){
    fieldsArray[i].onchange = function(){
     tmt_validateImg(this);
    }
   }
  }
  if(fieldsArray[i].getAttribute("tmt:filters")){
   // Call the filters on the onkeyup and onblur events
   fieldsArray[i].onkeyup = function(){
    tmt_filterField(this);
   }
   fieldsArray[i].onblur = function(){
    tmt_filterField(this);
   }
  }
 }
 var selectNodes = formNode.getElementsByTagName("select");
 for(var j=0; j<selectNodes.length; j++){
  // Create a validator for each select element
  this.validators[this.validators.length] = tmt_selectValidatorFactory(selectNodes[j]);
 }
 var boxTable = tmt_getNodesTable(formNode, "checkbox");
 for(var boxName in boxTable){
  // Create a validator for each group of checkboxes
  this.validators[this.validators.length] = tmt_boxValidatorFactory(boxTable[boxName]);
 }
 var radioTable = tmt_getNodesTable(formNode, "radio");
 for(var radioName in radioTable){
  // Create a validator for each group of radios
  this.validators[this.validators.length] = tmt_radioValidatorFactory(radioTable[radioName]);
 }
 // Store all the submit buttons
 this.buttons = tmt_getSubmitNodes(formNode);
 // Define a method that can block multiple submits
 this.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<this.buttons.length; i++){
    if(this.buttons[i].getAttribute("tmt:waitmessage")){
     this.buttons[i].value = this.buttons[i].getAttribute("tmt:waitmessage");
    }
    this.buttons[i].disabled = true;
   }
  }
 }
}
// Abstract field validator constructor
function tmt_abstractValidator(fieldNode){
 this.message = "";
 this.name = fieldNode.name;
 if(fieldNode.getAttribute("tmt:message")){
  this.message = fieldNode.getAttribute("tmt:message");
 }
 var errorClass = "";
 if(fieldNode.getAttribute("tmt:errorclass")){
  errorClass = fieldNode.getAttribute("tmt:errorclass");
 }
 this.flagInvalid = function(){
  // Append the CSS class to the existing one
  if(errorClass){
   // Flag only if it's not already flagged
   if(fieldNode.className.indexOf(errorClass) == -1){
    fieldNode.className = fieldNode.className + " " + errorClass;
   }
  }
  // Set the title attribute in order to show a tootip
  fieldNode.setAttribute("title", this.message);
 }
 this.flagValid = function(){
  // Remove the CSS class
  if(errorClass){
   var regClass = new RegExp("\\b" + errorClass);
   fieldNode.className = fieldNode.className.replace(regClass, "");
  }
  fieldNode.removeAttribute("title");
 }
 this.validate = function(){
  // If the field contains error, flag it as invalid and return the error message
  if(!this.isValid()){
   this.flagInvalid();
   return true;
  }
  else{
   this.flagValid();
   return false;
  }
 }
}
// Create a validator for text and texarea fields
function tmt_textValidatorFactory(fieldNode){
 // Create a generic validator, than add specific properties and methods as needed
 var obj = new tmt_abstractValidator(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 empty
 obj.isEmpty = function(){
  return fieldNode.value == "";
 }
 // 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(){
  // 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() && (required != "conditional")){
   if(obj.isRequired()){
    return false;
   }
   else{
    return true;
   }
  }
  else{
   // Loop over all the available rules
   for(var rule in tmt_globalRules){
    // Check if the current rule is required for the field
    if(fieldNode.getAttribute("tmt:" + rule)){
     // Invoke the rule
     if(!eval("tmt_globalRules." + rule + "(fieldNode)")){
      return false;
     }
    }
   }
  }
  return true;
 }
 return obj;
}
// Create a validator for <select> fields
function tmt_selectValidatorFactory(selectNode){
 // Create a generic validator, than add specific properties and methods as needed
 var obj = new tmt_abstractValidator(selectNode);
 obj.type = "select";
 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 select validate
 obj.isValid = function(){
  // Check for index
  if(selectNode.selectedIndex == invalidIndex){
   return false;
  }
  // Check for value
  if(selectNode.value == invalidValue){
   return false;
  }
  return true;
 }
 return obj;
}
// Generic validator for grouped fields (radio and checkboxes)
function tmt_groupValidatorFactory(buttonGroup){
 this.name = buttonGroup.name;
 this.message = "";
 this.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")){
   this.message = buttonGroup.elements[i].getAttribute("tmt:message");
  }
  if(buttonGroup.elements[i].getAttribute("tmt:errorclass")){
   this.errorClass = buttonGroup.elements[i].getAttribute("tmt:errorclass");
  }
 }
 this.flagInvalid = function(){
  // Append the CSS class to the existing one
  if(this.errorClass){
   for(var i=0; i<buttonGroup.elements.length; i++){
    // Flag only if it's not already flagged
    if(buttonGroup.elements[i].className.indexOf(this.errorClass) == -1){
     buttonGroup.elements[i].className = buttonGroup.elements[i].className + " " + this.errorClass;
    }
    buttonGroup.elements[i].setAttribute("title", this.message);
   }
  }
 }
 this.flagValid = function(){
  // Remove the CSS class
  if(this.errorClass){
   var regClass = new RegExp("\\b" + this.errorClass);
   for(var i=0; i<buttonGroup.elements.length; i++){
    buttonGroup.elements[i].className = buttonGroup.elements[i].className.replace(regClass, "");
    buttonGroup.elements[i].removeAttribute("title");
   }
  }
 }
 this.validate = function(){
  var errorMsg = "";
  // If the field group contains error, flag it as invalid and return the error message
  if(!this.isValid()){
   errorMsg += this.message;
   this.flagInvalid();
  }
  else{
   this.flagValid();
  }
  return errorMsg;
 }
}
// Checkbox validator (one for each group of boxes sharing the same name)
function tmt_boxValidatorFactory(boxGroup){
 var obj = new tmt_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;
}
// Radio validator (one for each group of radios sharing the same name)
function tmt_radioValidatorFactory(radioGroup){
 var obj = new tmt_groupValidatorFactory(radioGroup);
 obj.type = "radio";
 obj.required = 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].getAttribute("tmt:required")){
   obj.required = radioGroup.elements[i].getAttribute("tmt:required");
  }
 }
 // Check if the radio validate
 obj.isValid = function(){
  if(obj.required){
   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 global objects store all the validation rules
// Every rule is stored as a method that accepts the field node as argument and return a boolean
var tmt_globalRules = new Object;
tmt_globalRules.datepattern = function(fieldNode){
 var globalObj = tmt_globalDatePatterns[fieldNode.getAttribute("tmt:datepattern")];
 if(globalObj){
  // Split the date into 3 different bits using the separator
  var dateBits = fieldNode.value.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
  return isDate && globalObj.rex.test(fieldNode.value);
 }
}
tmt_globalRules.equalto = function(fieldNode){
 var twinNode = document.getElementById(fieldNode.getAttribute("tmt:equalto"));
 return twinNode.value == fieldNode.value;
}
tmt_globalRules.maxlength = function(fieldNode){
 if(fieldNode.value.length > fieldNode.getAttribute("tmt:maxlength")){
  return false;
 }
 return true;
}
tmt_globalRules.maxnumber = function(fieldNode){
 if(parseFloat(fieldNode.value) > fieldNode.getAttribute("tmt:maxnumber")){
  return false;
 }
 return true;
}
tmt_globalRules.minlength = function(fieldNode){
 if(fieldNode.value.length < fieldNode.getAttribute("tmt:minlength")){
  return false;
 }
 return true;
}
tmt_globalRules.minnumber = function(fieldNode){
 if(parseFloat(fieldNode.value) < fieldNode.getAttribute("tmt:minnumber")){
  return false;
 }
 return true;
}
tmt_globalRules.pattern = function(fieldNode){
 var reg = tmt_globalPatterns[fieldNode.getAttribute("tmt:pattern")];
 if(reg){
  return reg.test(fieldNode.value);
 }
 else{
  // If the pattern is missing, skip it
  return true; 
 }
}
/* Image upload validation */
tmt_globalRules.image = function(fieldNode){
 // If the flag isn't defined we assume things are fine
 if(!fieldNode.isValidImg){
  fieldNode.isValidImg = "true";
 }
 return fieldNode.isValidImg == "true";
}
// Check the currently selected image and set a validity flag
function tmt_validateImg(fieldNode){
 var imgURL = "file:///" + fieldNode.value;
 var img = new Image();
 img.maxSize =  fieldNode.getAttribute("tmt:imagemaxsize");
 img.maxWidth = fieldNode.getAttribute("tmt:imagemaxwidth");
 img.minWidth = fieldNode.getAttribute("tmt:imageminwidth");
 img.maxHeight = fieldNode.getAttribute("tmt:imagemaxheight");
 img.minHeight = fieldNode.getAttribute("tmt:imageminheight");
 // Store a reference to the input field
 img.fieldNode = fieldNode;
 // The image's data can be read only after loading. That's why we need a callback
 img.onload = tmt_validateImgCallback;
 img.src = imgURL;
}
function tmt_validateImgCallback(){
 var errorsCount = 0;
 // Check every constrain and increment the error counter accordingly
 if(this.fileSize && this.maxSize && (this.fileSize/1024) > this.maxSize){
  errorsCount ++;
 }
 if(this.maxWidth && (this.width > this.maxWidth)){
  errorsCount ++;
 }
 if(this.minWidth && (this.width < this.minWidth)){
  errorsCount ++;
 }
 if(this.maxHeight && (this.height > this.maxHeight)){
  errorsCount ++;
 }
 if(this.minHeight && (this.height < this.minHeight)){
  errorsCount ++;
 }
 // Store the valid flag inside the DOM node itself
 this.fieldNode.isValidImg = (errorsCount != 0) ? "false" : "true";
}
// This global objects store all the RegExp patterns for strings
var tmt_globalPatterns = new Object;
tmt_globalPatterns.email = new RegExp("^[\\w\\.=-]+@[\\w\\.-]+\\.[\\w\\.-]{2,4}$");
tmt_globalPatterns.lettersonly = new RegExp("^[a-zA-Z]*$");
tmt_globalPatterns.alphanumeric = new RegExp("^\\w*$");
tmt_globalPatterns.integer = new RegExp("^-?\\d\\d*$");
tmt_globalPatterns.positiveinteger = new RegExp("^\\d\\d*$");
tmt_globalPatterns.number = new RegExp("^-?(\\d\\d*\\.\\d*$)|(^-?\\d\\d*$)|(^-?\\.\\d\\d*$)");
tmt_globalPatterns.filepath_pdf = new RegExp("\\\\[\\w_]*\\.([pP][dD][fF])$");
tmt_globalPatterns.filepath_jpg_gif = new RegExp("\\\\[\\w_]*\\.([gG][iI][fF])|([jJ][pP][eE]?[gG])$");
tmt_globalPatterns.filepath_jpg = new RegExp("\\\\[\\w_]*\\.([jJ][pP][eE]?[gG])$");
tmt_globalPatterns.filepath_zip = new RegExp("\\\\[\\w_]*\\.([zZ][iI][pP])$");
tmt_globalPatterns.filepath = new RegExp("\\\\[\\w_]*\\.\\w{3}$");
// This global objects store all the info required for date validation
var tmt_globalDatePatterns = new Object;
tmt_globalDatePatterns["YYYY-MM-DD"] = tmt_dateInfo("^\([0-9]{4}\)\\-\([0-1][0-9]\)\\-\([0-3][0-9]\)$", 0, 1, 2, "-");
tmt_globalDatePatterns["YYYY-M-D"] = tmt_dateInfo("^\([0-9]{4}\)\\-\([0-1]?[0-9]\)\\-\([0-3]?[0-9]\)$", 0, 1, 2, "-");
tmt_globalDatePatterns["MM.DD.YYYY"] = tmt_dateInfo("^\([0-1][0-9]\)\\.\([0-3][0-9]\)\\.\([0-9]{4}\)$", 2, 0, 1, ".");
tmt_globalDatePatterns["M.D.YYYY"] = tmt_dateInfo("^\([0-1]?[0-9]\)\\.\([0-3]?[0-9]\)\\.\([0-9]{4}\)$", 2, 0, 1, ".");
tmt_globalDatePatterns["MM/DD/YYYY"] = tmt_dateInfo("^\([0-1][0-9]\)\/\([0-3][0-9]\)\/\([0-9]{4}\)$", 2, 0, 1, "/");
tmt_globalDatePatterns["M/D/YYYY"] = tmt_dateInfo("^\([0-1]?[0-9]\)\/\([0-3]?[0-9]\)\/\([0-9]{4}\)$", 2, 0, 1, "/");
tmt_globalDatePatterns["MM-DD-YYYY"] = tmt_dateInfo("^\([0-21][0-9]\)\\-\([0-3][0-9]\)\\-\([0-9]{4}\)$", 2, 0, 1, "-");
tmt_globalDatePatterns["M-D-YYYY"] = tmt_dateInfo("^\([0-1]?[0-9]\)\\-\([0-3]?[0-9]\)\\-\([0-9]{4}\)$", 2, 0, 1, "-");
tmt_globalDatePatterns["DD.MM.YYYY"] = tmt_dateInfo("^\([0-3][0-9]\)\\.\([0-1][0-9]\)\\.\([0-9]{4}\)$", 2, 1, 0, ".");
tmt_globalDatePatterns["D.M.YYYY"] = tmt_dateInfo("^\([0-3]?[0-9]\)\\.\([0-1]?[0-9]\)\\.\([0-9]{4}\)$", 2, 1, 0, ".");
tmt_globalDatePatterns["DD/MM/YYYY"] = tmt_dateInfo("^\([0-3][0-9]\)\/\([0-1][0-9]\)\/\([0-9]{4}\)$", 2, 1, 0, "/");
tmt_globalDatePatterns["D/M/YYYY"] = tmt_dateInfo("^\([0-3]?[0-9]\)\/\([0-1]?[0-9]\)\/\([0-9]{4}\)$", 2, 1, 0, "/");
tmt_globalDatePatterns["DD-MM-YYYY"] = tmt_dateInfo("^\([0-3][0-9]\)\\-\([0-1][0-9]\)\\-\([0-9]{4}\)$", 2, 1, 0, "-");
tmt_globalDatePatterns["D-M-YYYY"] = tmt_dateInfo("^\([0-3]?[0-9]\)\\-\([0-1]?[0-9]\)\\-\([0-9]{4}\)$", 2, 1, 0, "-");
// Create an object that stores date validation's info
function tmt_dateInfo(rex, year, month, day, separator){
 var infoObj = new Object;
 infoObj.rex = new RegExp(rex);
 infoObj.y = year;
 infoObj.m = month;
 infoObj.d = day;
 infoObj.s = separator;
 return infoObj;
}
/* Filters */
// This global objects store all the info required for filters
var tmt_globalFilters = new Object;
tmt_globalFilters.ltrim = tmt_filterInfo("^(\\s*)(\\b[\\w\\W]*)$", "$2");
tmt_globalFilters.rtrim = tmt_filterInfo("^([\\w\\W]*)(\\b\\s*)$", "$1");
tmt_globalFilters.nospaces = tmt_filterInfo("\\s*", "");
tmt_globalFilters.nocommas = tmt_filterInfo(",", "");
tmt_globalFilters.nodots = tmt_filterInfo("\\.", "");
tmt_globalFilters.noquotes = tmt_filterInfo("'", "");
tmt_globalFilters.nodoublequotes = tmt_filterInfo('"', "");
tmt_globalFilters.nohtml = tmt_filterInfo("<[^>]*>", "");
tmt_globalFilters.alphanumericonly = tmt_filterInfo("[^\\w]", "");
tmt_globalFilters.numbersonly = tmt_filterInfo("[^\\d]", "");
tmt_globalFilters.lettersonly = tmt_filterInfo("[^a-zA-Z]", "");
tmt_globalFilters.commastodots = tmt_filterInfo(",", ".");
tmt_globalFilters.dotstocommas = tmt_filterInfo("\\.", ",");
tmt_globalFilters.numberscommas = tmt_filterInfo("[^\\d,]", "");
tmt_globalFilters.numbersdots = tmt_filterInfo("[^\\d\\.]", "");
// Create an object that stores filters's info
function tmt_filterInfo(rex, replaceStr){
 var infoObj = new Object;
 infoObj.rex = new RegExp(rex, "g");
 infoObj.str = replaceStr;
 return infoObj;
}
// Clean up the field based on filter's info
function tmt_filterField(fieldNode){
 var filtersArray = fieldNode.getAttribute("tmt:filters").split(",");
 for(var i=0; i<filtersArray.length; i++){
  var filtObj = tmt_globalFilters[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_filterDemoronizer(fieldNode.value);
  }
 }
}
// Replace MS Word's non-ISO characters with plausible substitutes
function tmt_filterDemoronizer(str){
 str = str.replace(new RegExp(String.fromCharCode(710), "g"), "^");
 str = str.replace(new RegExp(String.fromCharCode(732), "g"), "~");
 // Evil "smarty" quotes
 str = str.replace(new RegExp(String.fromCharCode(8216), "g"), "'");
 str = str.replace(new RegExp(String.fromCharCode(8217), "g"), "'");
 str = str.replace(new RegExp(String.fromCharCode(8220), "g"), '"');
 str = str.replace(new RegExp(String.fromCharCode(8221), "g"), '"');
 // More MS Word's garbage
 str = str.replace(new RegExp(String.fromCharCode(8211), "g"), "-");
 str = str.replace(new RegExp(String.fromCharCode(8212), "g"), "--");
 str = str.replace(new RegExp(String.fromCharCode(8218), "g"), ",");
 str = str.replace(new RegExp(String.fromCharCode(8222), "g"), ",,");
 str = str.replace(new RegExp(String.fromCharCode(8226), "g"), "*");
 str = str.replace(new RegExp(String.fromCharCode(8230), "g"), "...");
 str = str.replace(new RegExp(String.fromCharCode(8364), "g"), "€");
 return str;
}
/* Helper functions */
// Get an array of submit button nodes contained inside a given node
function tmt_getSubmitNodes(startNode){
 var submitArray = new Array();
 var inputNodes = startNode.getElementsByTagName("input");
 // Get an array of submit nodes
 for(var i=0; i<inputNodes.length; i++){
  if(inputNodes[i].getAttribute("type").toLowerCase() == "submit"){
   submitArray[submitArray.length] = inputNodes[i];
  }
 }
 return submitArray;
}
// Get an array of input and textarea nodes contained inside a given node
function tmt_getTextfieldNodes(startNode){
 var inputsArray = new Array();
 var inputNodes = startNode.getElementsByTagName("input");
 var areaNodes = startNode.getElementsByTagName("textarea");
 // Get an array of text, password and file nodes
 for(var i=0; i<inputNodes.length; i++){
  if(!inputNodes[i].getAttribute("type")){
   inputNodes[i].setAttribute("type", "text");
  }
  var fieldType = inputNodes[i].getAttribute("type").toLowerCase();
  if((fieldType == "text") || (fieldType == "password") || (fieldType == "file") || (fieldType == "hidden")){
   inputsArray[inputsArray.length] = inputNodes[i];
  }
 }
 // Append textarea nodes too
 for(var j=0; j<areaNodes.length; j++){
     inputsArray[inputsArray.length] = areaNodes[j];
 }
 return inputsArray;
}
// 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
function tmt_getNodesTable(formNode, type){
 // This object will store data fields, just as an hash table
 var boxHolder = new Object;
 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] = new Object;
    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;
}
// This handy piece of code comes from:
// http://www.sitepoint.com/blog-post-view.php?id=171578
function addLoadEvent(func){
 var oldonload = window.onload;
 if(typeof window.onload != "function"){
  window.onload = func;
 } 
 else{
  window.onload = function(){
   oldonload();
   func();
  }
 }
}
addLoadEvent(tmt_validatorInit);
