function Validator(prefix, delim, sep) {
   // params
   if (!delim) {
      delim = '_';
   }
   if (!sep) {
      sep = ':';
   }
   this.prefix = prefix;
   this.delim = delim;
   this.sep = sep;
   this.len = prefix.length;

   // methods
   this.process = validatorProcess;
   this._value = _validatorValue;
   this.__value = __validatorValue;
   this._confirm = _validatorConfirm;
   this._format = _validatorFormat;
   this._length = _validatorLength;
   this._encrypt = _validatorEncrypt;
   this._maxlength = _validatorMaxLength;
   this._minlength = _validatorMinLength;
   this._required = _validatorRequired;
   this._requiredif = _validatorRequiredIf;
   this._requiredone = _validatorRequiredOne;
   this._requiredall = _validatorRequiredAll;
   this._requiredgroup = _validatorRequiredGroup;

   //defaults
   this.message = "The following errors have occured. "+
                  "Please make corrections and resubmit the form.\n";
}

function validatorProcess(myform, errors) {
   var i;
   if (!errors) {
      errors = '';
   }
   for (i = 0;i < myform.elements.length;i++) {
      var e = myform.elements[i];
      var vname = e.name;
      var prefix = vname.substr(0, this.len);
      if (this.prefix == prefix) {
         var rule = e.value;
         var pos = vname.indexOf(this.delim, this.len+1);
         if (pos == -1) {
            alert('Bad validation function: ' + vname);
            return(false);
         }
         var fn = vname.substr(this.len+1, pos-this.len-1);
         var fname = vname.substr(pos+1);
         var r = eval('this._'+fn+'(myform, fname, rule)');
         if (r) {
            errors += "\n"+r;
         }
      }
   }
   if (errors != '') {
      alert(this.message+errors);
      return(false);
   }
   return(true);
}

function _validatorValue(myform, fname) {
   var field = myform[fname];
   if (!field) {
      alert('Unknown field: '+fname);
      return;
   }
   var type = field.type;
   var value = '';
   if (!type && field.length) {
      var k;
      for (k = 0;k < field.length;k++) {
         var v = this.__value(field[k]);
         if (v) {
            if (value) {
               value += this.sep;
            }
            value += v; 
         }
      }
   }
   else {
      value = this.__value(field);
   }
   return(value);
}

function __validatorValue(field) {
   var type = field.type;
   var value = '';
   if (type == 'file' || type == 'text' || type == 'textarea') {
      value = field.value;
   }
   else if (type == 'select-one') {
      value = field.options[field.selectedIndex].value;
   }
   else if (type == 'select-multiple') {
      var j;
      for (j = 0;j < field.options.length;j++) {
         if (field.options[j].selected) {
            if (value) {
               value += this.sep;
            }
            value += field.options[j].value;
         }
      }
   }
   else if (type == 'checkbox') {
      if (field.checked) {
         value = field.value;
      }
   }
   else if (type == 'radio') {
       if (field.checked) {
         value = field.value;
      }
   }
   else {
      value = field.value;
   }
   return(value);
}

function _validatorConfirm(myform, fname, rule) {
   var source = this._value(myform, fname);
   var a = rule.split('|');
   var target = this._value(myform, a[1]);
   if ((source != '') && (source != target)) {
      return(a[0]);
   }
}

function _validatorFormat(myform, fname, rule) {
   var value = this._value(myform, fname);
   if (value == '') {
      return;
   }
   var a = rule.split('|');
   var r = new RegExp(a[1]);
   if (!r.test(value)) {
      return(a[0]);
   }
}

function _validatorLength(myform, fname, rule) {
   var value = this._value(myform, fname);
   var a = rule.split('|');
   if (value && value.length != a[1]) {
      return(a[0]);
   }
}

function _validatorMaxLength(myform, fname, rule) {
   var value = this._value(myform, fname);
   var a = rule.split('|');
   if (value && value.length > a[1]) {
      return(a[0]);
   }
}

function _validatorMinLength(myform, fname, rule) {
   var value = this._value(myform, fname);
   var a = rule.split('|');
   if (value && value.length < a[1]) {
      return(a[0]);
   }
}

function _validatorRequired(myform, fname, rule) {
   var value = this._value(myform, fname);
   if (value == '') {
      return(rule);
   }
}

function _validatorRequiredIf(myform, fname, rule) {
   var source = this._value(myform, fname);
   var a = rule.split('|');
   var target = this._value(myform, a[1]);
   if (source == '' && (target == a[2])) {
      return(a[0]);
   }
}

function _validatorRequiredOne(myform, fname, rule) {
   var a = rule.split('|');
   var l = 0;
   for (var i=1;i < a.length;i++) {
      var value = this._value(myform, a[i]);
      if (value != '') {
         l++;
      }
   }
   if (l == 0) {
      return(a[0]);
   }
}

function _validatorRequiredAll(myform, fname, rule) {
   var a = rule.split('|');
   var l = 1;
   for (var i=1;i < a.length;i++) {
      var value = this._value(myform, a[i]);
      if (value != '') {
         l++;
      }
   }
   if (l < a.length) {
      return(a[0]);
   }
}

function _validatorRequiredGroup(myform, fname, rule) {
   var a = rule.split('|');
   var l = 1;
   var check = false;
   for (var i=1;i < a.length;i++) {
      var value = this._value(myform, a[i]);
      if (value != '') {
         l++;
         check = true;
      }
   }
   if (check && l < a.length) {
      return(a[0]);
   }
}

function _validatorEncrypt(myform, fname, rule) {
   var field = myform[fname];
   if (!field) {
      alert('Unknown field: '+fname);
      return;
   }
   field.value=rule+':'+hex_md5('53cr3+'+rule);
}

