// VALIDATOR.JS by frequency-decoder.com (http://www.frequency-decoder.com/form-validator/)
// Modified to add 'select' parsing, 'group' elements support
// and compatibility with Internet Explorer v5.x
// by Emanuele Rodriguez @ TangentDigital (http://www.tangent.es)

if(!Array.prototype.push) { // Array.push code by www.peterbailey.net
   Array.prototype.push = function() {
      for ( var i = 0; i < arguments.length; i++ ) { this[this.length] = arguments[i]; }
      return this.length;
   }
}

if(!String.prototype.trim) String.prototype.trim = function() { return this.replace(/^\s*/,'').replace(/\s*$/, ''); }

function formValidator(formid) {
   this.formid = formid;
   this.rules = new Array();
   this.styleLabelOnErr = true;
   this.invalidClass = "invalid";
   this.warningClass = "warn";
   this.validateAsErr = false;
   this.debug = false;

   this.init = function() {
      var nodes = new Array();
      // getElementsByTagName returns an object collection which doesn't have a .concat() method
      var tempnodes = document.getElementById(self.formid).getElementsByTagName('input');
      for(var i = 0; i < tempnodes.length; i++) if(tempnodes.item(i).type.toUpperCase() == 'TEXT' || tempnodes.item(i).type.toUpperCase() == 'PASSWORD') nodes.push(tempnodes.item(i));

      tempnodes = document.getElementById(self.formid).getElementsByTagName('textarea');
      for(i = 0; i < tempnodes.length; i++) nodes.push(tempnodes.item(i));

      tempnodes = document.getElementById(self.formid).getElementsByTagName('select');
      for(i = 0; i < tempnodes.length; i++) nodes.push(tempnodes.item(i));

      for(i = 0; i < nodes.length; i++) {
         var current = nodes[i];

         // Only if the INPUT/TEXTAREA/SELECT has an id;
         if(typeof current.id != 'undefined' && current.id != "") {
            while(current.previousSibling) {
               current = current.previousSibling;

               if(!(current.nodeName.toUpperCase().indexOf('TEXT') != -1 || current.nodeName.toUpperCase().indexOf('COMMENT') != -1 || current.nodeName.toUpperCase().indexOf('!') != -1)) { break; }

               // IE 5.x does not read correctly comment nodes widh nodeValue- use innerHTML instead
               var currNodeValue = '';
               if(current.nodeName.toUpperCase().indexOf('!') != -1) currNodeValue = current.innerHTML.replace("<!--","").replace("-->","");
               else if(current.nodeName.toUpperCase().indexOf('COMMENT') != -1) currNodeValue = current.nodeValue;

               if(currNodeValue.trim().indexOf(nodes[i].id) == 0) {
                  var argList = currNodeValue.trim().split(" ");
                  // Only if we can create a RegExp with the second argument
                  if(argList.length > 3 && typeof new RegExp(argList[1]) != 'undefined') {
                    //DEBUG:
                    if(this.debug) alert('AddRule ID ['+nodes[i].id+'] - RegExp ['+argList[1]+'] - UseBlur ['+argList[2]+'] - Required ['+argList[3]+'] - Group ['+argList[4]+'] - Equal['+argList[5]+']');

                    self.addRule(nodes[i], argList[1], (argList[2].trim() == "true" ? true : false), (argList[3].trim() == "true" ? true : false), (typeof(argList[4])!='undefined')?argList[4].trim().replace(/\/\/$/g,''):'', (typeof(argList[5])!='undefined')?argList[5].trim().replace(/\/\/$/g,''):'');
                  }
               }
            }
         }
      }
      // Call the user defined initialisation
      self.extendedInitialisation();
   }

   this.styleFields = function(which) {
      var label = null;
      if(self.styleLabelOnErr) {
         // Get the implicit label if any (f.y.i. depreciated as from HTML4)
         if(self.rules[which].element.parentNode.tagName.toUpperCase() == 'LABEL') {
            label = self.rules[which].element.parentNode;
         // No implicit label then search explicit label list
         } else {
            var labelList = document.getElementById(self.formid).getElementsByTagName('label');
            // loop through label array attempting to match each 'for' attribute to the id of the current element
            for(var lbl = 0; lbl < labelList.length; lbl++) {
               // Internet Explorer requires the htmlFor test
               if(labelList[lbl]['htmlFor'] && labelList[lbl]['htmlFor'] == self.rules[which].element.id) {
                  label = labelList[lbl];
               // All other compliant browsers
               } else if(labelList[lbl].getAttribute('for') == self.rules[which].element.id) {
                  label = labelList[lbl];
               }
            }
         }
      }

      if(self.rules[which].valid == false) {
         var classout;
         var classin;
         if(!self.validateAsErr || !self.rules[which].required) {
            classout = self.invalidClass;
            classin  = self.warningClass;
         } else {
            classout = self.warningClass;
            classin  = self.invalidClass;
         }
         self.rules[which].element.className = self.rules[which].element.className.replace(classout, "");
         if(self.rules[which].element.className.search(classin) == -1) { self.rules[which].element.className += " " + classin; }

         if(self.styleLabelOnErr && label != null) {
            label.className = label.className.replace(classout, "");
            if(label.className.search(classin) == -1) label.className += " " + classin;
         }
      } else {
         self.rules[which].element.className = self.rules[which].element.className.replace(self.invalidClass, "");
         self.rules[which].element.className = self.rules[which].element.className.replace(self.warningClass, "");
         if(self.styleLabelOnErr && label != null) {
            label.className = label.className.replace(self.invalidClass, "");
            label.className = label.className.replace(self.warningClass, "");
         }
      }
   }
   this.validateRegEx = function(which) {
      var isValidated = false;
      isValidated = !(self.rules[which].element.value.search(self.rules[which].regex) == -1);
      // Validate if empty and not a required field
      if(!self.rules[which].required && self.rules[which].element.value.trim() == '') isValidated = true;
      return isValidated;
   }
   this.validateSingleRule = function(which) {
      // If the element is part of a group, it must be checked together with the other elements of the same group
      if ((self.rules[which].group != '') && (self.rules[which].group != '.')) {
         var groupIsEmpty = self.isInEmptyGroup(which);
         var i=0;
         while(i<self.rules.length) {
            if (self.rules[i].group == self.rules[which].group) {
               // If all the elements in the group are empty, mark as invalid, otherwise validate as normally
               self.rules[i].valid = (groupIsEmpty)?false:self.validateRegEx(i);
               self.styleFields(i);
            }
            i++;
         }
      }
      else {
//alert(self.rules[which].equal);
         // Check if the element value must be the same as some other element
         if ((self.rules[which].equal != '') && (self.rules[which].equal != '.')) {
            var elemsDiffer = self.valuesDiffer(self.rules[which].element_id, self.rules[which].equal);
            var i=0;
            var i1 = 0;
            var i2 = 0;
            while (i<self.rules.length) {
               if (self.rules[which].element_id == self.rules[i].element_id)  i1 = i;  // current element
               if (self.rules[which].equal == self.rules[i].element_id)       i2 = i;  // element to compare
               i++;
            }
            self.rules[i1].valid = (elemsDiffer)?false:self.validateRegEx(i1);
            self.rules[i2].valid = (elemsDiffer)?false:self.validateRegEx(i2);
            self.styleFields(i1);
            self.styleFields(i2);
         }
         else {
            // Otherwise proceed normally
            self.rules[which].valid = self.validateRegEx(which);
         }
      }


      // Call the user defined validation routine
      self.extendedValidateSingleRule(self.rules[which].element_id, which);
      // Style the input and label
      self.styleFields(which);
      // Carry out any post validation processing
      self.postSingleRuleValidation(self.rules[which].element_id, which);
   }
   // event handler for individual input fields (activated onblur/onchange)
   this.validateSingle = function(e) {
      var i = 0;
      while(i<self.rules.length) {
         if(this.id == self.rules[i].element.id) {
            self.validateSingleRule(i);
         }
         i++;
      }

      return true;
   }
   // event handler for the submit button
   this.validateAll = function(e) {
      self.validateAsErr = true;
      for(var i = 0; i < self.rules.length; i++) {
         self.validateSingleRule(i);
      }
      self.postSubmitValidation();
		var isValidForm = (self.extendedValidate() && self.isValid());
		if (!(isValidForm)) alert(strFormErrorMsg);
      return isValidForm;
   }
   this.valuesDiffer = function(e1,e2){
      var v1 = document.getElementById(e1).value;
      var v2 = document.getElementById(e2).value;
      return (v1!=v2);
   }
   // chech if the element is in a group where no elements has a value defined
   this.isInEmptyGroup = function(which) {
      var g=self.rules[which].group;
      var allEmpty=true;
      var i=0;
      var ig=0;
      while(i<self.rules.length) {
         if (self.rules[i].group == g) {
            ig++;
            if (self.rules[i].element.value.trim() != '') allEmpty=false;
         }
         i++;
      }
      // If the element is the only one in the group, the condition is not verified
      if (ig<=1) allEmpty=false;
      return allEmpty;
   }

   // Are all (required) form elements valid
   this.isValid = function() {
         var valid = true;
         for(var i = 0; i < self.rules.length; i++) {
            //if(!self.rules[i].valid && self.rules[i].required) { valid = false; }
            if(!self.rules[i].valid) { valid = false; }
         }
         return valid;
   }

   // Create private variable 'self'
   var self = this;

   // Attach onsubmit event handler to the form
	var oForm = document.getElementById(this.formid);
	if (typeof(oForm.onsubmit)!='function') {
	   oForm.onsubmit = self.validateAll;
	}
   return this;
}
formValidator.prototype.addRule = function(element, regex, useblur, required, group, equal) {
   var obj     = new Object();
   obj.element = element
   obj.element_id = element.id;

   // Use onchange or onblur, it's up to you..
   if(useblur) obj.element.onblur   = this.validateSingle;
   else        obj.element.onchange = this.validateSingle;

   // Is this a required form field?
   obj.required = required;
   obj.group    = group;
   obj.equal    = equal;
   obj.regex    = regex.trim();
   obj.valid    = false;
   this.rules.push(obj);

   if(this.debug) alert('Pushed Rule ID ['+obj.element.id+'] - RegExp ['+obj.regex+'] - UseBlur ['+useblur+'] - Required ['+obj.required+'] - Group ['+obj.group+'] - Equal['+obj.equal+']');
}

// Override the following prototypes on a page by page basis if needs be
formValidator.prototype.extendedInitialisation = function() { }
formValidator.prototype.extendedValidate = function() { return true; }
formValidator.prototype.extendedValidateSingleRule = function(id, rule) { }
formValidator.prototype.postSubmitValidation = function() { }
formValidator.prototype.postSingleRuleValidation = function() { }

formValidator.validatorCollection = new Array();

function initiateFormValidation() {
   // Bail out if the browser can't handle the script
   if(!document.getElementById  || !document.getElementsByTagName) return;

   var validator;
   var formCollection = document.getElementsByTagName('form');

   // Iterate over the forms
   for(var i = 0; i < formCollection.length; i++) {
      // If the current form has an assigned id
      if(typeof formCollection[i].id != 'undefined' && formCollection[i].id != "") {
         // Create new validator object
         validator = new formValidator(formCollection[i].id);

         // Initialise the rule parsing
         validator.init();
         formValidator.validatorCollection.push(validator);
      }
   }
}

// Helper function: Retuns the validator object corresponding to the form id
function getValidatorObject(formid) {
   for(i = 0; i < formValidator.validatorCollection.length; i++) {
      if(formValidator.validatorCollection[i].formid == formid) {
         return formValidator.validatorCollection[i];
      }
   }
   return null;
}
