top.GenericCheck = function GenericCheck() {

  this.successor = null;

  this.check = function(el) {
    if(this.successor != null) {
      this.successor.check(el);
    }
  }

  this.isEmpty = function(v) {
    return (v.search(/\S/) == -1);
  }

  this.isNumber = function(v, l) {
    if(!l) var l = (v.length > 0) ? v.length : 1;
    return (v.search(new RegExp("\\d{" + l + "}")) > -1) || (this.isEmpty(v));
  }
}
top.GenericCheck.instance = null;
top.GenericCheck.getInstance = function() {
  if(this.instance == null) {
    this.instance = new top.GenericCheck();
  }
  return this.instance;
}

top.IsCpfCheck = function IsCpfCheck() {

  this.successor = top.IsPasswordCheck.getInstance();

  this.check = function(el) {
    var error = "";
    if(el.getAttribute("validation").indexOf("cpf") > -1) {
      error += this.checkCpf(el);
    }

    if(this.successor != null) {
      return error + this.successor.check(el);
    }
    return error;
  }

  this.checkCpf = function(el) {
    if(this.isEmpty(el.value)) return "";
    var str = "";
    var sCpf = "";
    switch(parseInt(el.getAttribute("maxlength"))) {
      default: //one field, no format
        sCpf = el.value;
        break;
    }

    if(!this.isCpf(sCpf)) {
      str = "\nErro: Cpf inválido" + str;
    }

    return str;
  }

  this.isCpf = function(v) {
    var ok = false;
    var alg = 0;
    var soma1 = soma2 = 0;
    var var1 = var2 = var3 = var4 = 0;

    for(var i = 1 ; i < 11 ; i++) {
      if(v.charAt(0) != v.charAt(i)) {
        ok = true;
        break;
      }
    }

    if(!ok) {
      return false;
    }

    for(i = 0 ; i < 9 ; i++){	
      if (v.charAt(i) < '0' || v.charAt(i) > '9'){
        return false;
      }

      alg = parseInt(v.charAt(i));
      soma1 = soma1 + alg;
      soma2 = soma2 + alg * (9 - i);
    }

    var1 = 11 - ((soma1 * 1 + soma2 * 1) % 11);
    var3 = (var1 <= 9) ? var1 : 0;
    var2 = 11 - ((2 * (soma1 + var3) + soma2) % 11);
    var4 = (var2 <= 9) ? var2 : 0;

    dv = v.substring(v.length-2);
    if(dv != var3 * 10 + var4) {
      return false;
    }
    return true;
  }
}
top.IsCpfCheck.prototype.extendss("GenericCheck");
top.IsCpfCheck.instance = null;
top.IsCpfCheck.getInstance = function() {
  if(this.instance == null) {
    this.instance = new top.IsCpfCheck();
  }
  return this.instance;
}

top.IsPasswordCheck = function IsPasswordCheck() {

  this.successor = top.IsEmailCheck.getInstance();

  var MIN = 6;
  var NUMBERS = 1;      // -1 for undefined
  var CHARS = 1;        // -1 for undefined
  var UPPER_CASES = 0;  // Must be lower or equal to CHARS.
                        // -1 for undefined
  var LOWER_CASES = 1;  // Must be lower or equal to CHARS.
                        // -1 for undefined
  this.check = function(el) {
    var error = "";
    if(el.type == "password" || el.getAttribute("validation").indexOf("password") > -1) {
      error += this.isValidPassword(el.value);
    }
    if(el.getAttribute("validation").indexOf("passwordcheck") > -1) {
      error += this.passwordMatch(el);
    }

    if(this.successor != null) {
      return error + this.successor.check(el);
    }
    return error;
  }

  this.isValidPassword = function(v) {
    if(this.isEmpty(v)) return "";
    var str = "";
    var totalN = 0;
    var totalC = 0;
    var totalU = 0;
    var totalL = 0;
    
    for(var i = 0; i < v.length; i++) {
      if(v.charAt(i).search(/\d/) > -1) {
        totalN++;
      } else {
        totalC++;
        if(v.charAt(i).search(/[A-Z]/) > -1) {
          totalU++;
        } else if(v.charAt(i).search(/[a-z]/) > -1) {
          totalL++;
        }
      }
    }

    if(totalC + totalN < MIN) {
      str += "\nErro: Coloque no mínimo " + MIN + " caracteres";
    }
    if(totalN < NUMBERS) {
      str += "\nErro: Coloque no mínimo " + NUMBERS + " número(s)";
    }
    if(totalC < CHARS) {
      str += "\nErro: Coloque no mínimo " + CHARS + " letra(s)";
    }
    if(totalU < UPPER_CASES) {
      str += "\nErro: Coloque no mínimo " + UPPER_CASES + " letra(s) maiúscula(s)";
    }
    if(totalL < LOWER_CASES) {
      str += "\nErro: Coloque no mínimo " + LOWER_CASES + " letra(s) minúscula(s)";
    }

    return str;
  }

  this.passwordMatch = function(el) {
    var str = "";
    if(el.value != eval("el.form." + el.name.replace("check", "")).value && !this.isEmpty(el.value)) {
      str += "\nErro: Senhas não conferem";
    }
    return str;
  }
}
top.IsPasswordCheck.prototype.extendss("GenericCheck");
top.IsPasswordCheck.instance = null;
top.IsPasswordCheck.getInstance = function() {
  if(this.instance == null) {
    this.instance = new top.IsPasswordCheck();
  }
  return this.instance;
}

top.IsDateCheck = function IsDateCheck() {

  this.successor = top.IsTelephoneCheck.getInstance();

  this.daysInMonth = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  this.MIN_Y = 1900;
  this.MAX_Y = 2100;

  this.check = function(e) {
    var error = "";
    var val = e.getAttribute("validation");
    if(val.indexOf("dateddmmyyyy") > -1) {
      error += this.isDdMmYyyy(e);
    } else if(val.indexOf("datemmddyyyy") > -1) {
      error += this.isMmDdYyyy(e);
    } else if(val.indexOf("yearyyyy") > -1) {
      error += this.isDateYyyyMmDd(e.value, "01", "01");
    }

    if(this.successor != null) {
      return error + this.successor.check(e);
    }
    return error;
  }

  this.isDdMmYyyy = function(e) {
    var d, m, y;
    var str = "";
    if(e.getAttribute("maxlength") == 2) {
        d = e.value;
        m = e.form.elements[e.index + 1].value;
        y = e.form.elements[e.index + 2].value;
    } else {
      if(e.value.search(/\d{2}\/\d{2}\/\d{4}/) == -1 && !this.isEmpty(e.value)) {
        str = "\nErro: Formato de data inválido. O formato precisa ser: dd/mm/yyyy";
      }
      d = e.value.substring(0, e.value.indexOf("/"));
      m = e.value.substring(e.value.indexOf("/")+1, e.value.lastIndexOf("/"));
      y = e.value.substring(e.value.lastIndexOf("/")+1, e.value.length);
    }

    str = this.isDateYyyyMmDd(y, m, d) + str;

    return str;
  }

  this.isMmDdYyyy = function(e) {
    var d, m, y;
    var str = "";
    if(e.getAttribute("maxlength") == 2) {
        m = e.value;
        d = e.form.elements[e.index + 1].value;
        y = e.form.elements[e.index + 2].value;
    } else {
      if(e.value.search(/\d{2}\/\d{2}\/\d{4}/) == -1 && !this.isEmpty(e.value)) {
        str = "\nErro: Formato de data inválido. O formato precisa ser: mm/dd/yyyy";
      }
      m = e.value.substring(0, e.value.indexOf("/"));
      d = e.value.substring(e.value.indexOf("/")+1, e.value.lastIndexOf("/"));
      y = e.value.substring(e.value.lastIndexOf("/")+1, e.value.length);
    }

    str = this.isDateYyyyMmDd(y, m, d) + str;

    return str;
  }

  this.isDateYyyyMmDd = function(y, m, d) {
    var str = "";

    if(!this.isDayDd(y, m, d) && !this.isEmpty(d)) {
      str += "\nErro: Dia inválido para o mês informado. O Dia deve ter dois dígitos numéricos";
    }
    if(!this.isMonthMm(m) && !this.isEmpty(m)) {
      str += "\nErro: O Mês deve estar entre 01 e 12. O Mês deve ter dois dígitos numéricos";
    }
    if(!this.isYearYyyy(y) && !this.isEmpty(y)) {
      str += "\nErro: Os anos devem estar entre " + this.MIN_Y + " e " + this.MAX_Y + ". Anos devem ter quatro dígitos";
    }

    return str;
  }

  this.isDayDd = function(y, m, d) {
    return (this.isDay(y, parseInt(m), d) && d.length == 2);
  }

  this.isMonthMm = function(m) {
    return (this.isMonth(m) && m.length == 2);
  }

  this.isYearYyyy = function(y) {
    return (this.isYear(y) && y.length == 4);
  }

  this.isDay = function(y, m, d) {
    if(m == 2) {
      if(((y % 4 == 0) && ((!(y % 100 == 0)) || (y % 400 == 0))) && d <= 29) {
        return true;
      }
    }
    return (d >= 1 && d <= this.daysInMonth[m]);
  }

  this.isMonth = function(m) {
    return (m >= 1 && m <= 12);
  }

  this.isYear = function(y) {
    return (y >= this.MIN_Y && y <= this.MAX_Y);
  }
}
top.IsDateCheck.prototype.extendss("GenericCheck");
top.IsDateCheck.instance = null;
top.IsDateCheck.getInstance = function() {
  if(this.instance == null) {
    this.instance = new top.IsDateCheck();
  }
  return this.instance;
}

top.IsCepCheck = function IsCepCheck() {

  this.successor = top.IsDateCheck.getInstance();

  this.check = function(el) {
    var error = "";
    if(el.getAttribute("validation").indexOf("cep") > -1) {
      error += this.checkCep(el);
    }

    if(this.successor != null) {
      return error + this.successor.check(el);
    }
    return error;
  }

  this.checkCep = function(el) {
    if(this.isEmpty(el.value)) return "";
    var str = "";

    switch(parseInt(el.getAttribute("maxlength"))) {
      case 5:  //two separate fields
        if(!this.isNumber(el.value, 5) || !this.isNumber(el.form.elements[el.index + 1].value), 3) {
          str += "\nErro: O CEP deve conter apenas números"
        }
        break;
      case 9: //one field, format = 99999-999
        if(!this.isNumber(el.value.replace("-", ""), 8) || el.value.charAt(5) != "-") {
          str += "\nErro: O CEP deve estar no formato: \"99999-999\""
        }
        break
      default:  //one field no format
        if(!this.isNumber(el.value, 8) ) {
          str += "\nErro:  O CEP deve conter 8 números sem traço nem ponto"
        }
        break;
    }

    return str;
  }
}
top.IsCepCheck.prototype.extendss("GenericCheck");
top.IsCepCheck.instance = null;
top.IsCepCheck.getInstance = function() {
  if(this.instance == null) {
    this.instance = new top.IsCepCheck();
  }
  return this.instance;
}

top.IsEmailCheck = function IsEmailCheck() {

  this.successor = top.IsCepCheck.getInstance();

  this.check = function(el) {
    var error = "";
    if(el.getAttribute("validation").indexOf("email") > -1) {
      error += this.checkEmail(el.value);
    }
    if(el.getAttribute("validation").indexOf("emailcheck") > -1) {
      error += this.emailMatch(el);
    }

    if(this.successor != null) {
      return error + this.successor.check(el);
    }
    return error;
  }

  this.checkEmail = function(v) {
    var str = "";
    if(v.search(/^([a-z\d]+[\.]?[\_\-]*)*[a-z\d]+@(([a-z\d]+[\_\-]*)*[a-z\d]+\.)+[a-z]{2,3}$/i) == -1 && !this.isEmpty(v)) {
      str += "\nErro: Email inválido";
    }
    return str;
  }

  this.emailMatch = function(el) {
    var str = "";
    if(el.value != eval("el.form." + el.name.replace("check", "")).value && !this.isEmpty(el.value)) {
      str += "\nErro: Emails não conferem";
    }
    return str;
  }
}
top.IsEmailCheck.prototype.extendss("GenericCheck");
top.IsEmailCheck.instance = null;
top.IsEmailCheck.getInstance = function() {
  if(this.instance == null) {
    this.instance = new top.IsEmailCheck();
  }
  return this.instance;
}

top.IsTelephoneCheck = function IsTelephoneCheck() {

//  this.successor = top.IsCnpjCheck.getInstance();
  this.successor = null;

  this.check = function(e) {
    var error = "";
    if(e.getAttribute("validation").indexOf("telephone") > -1) {
      error += this.checkTelephone(e);
    }

    if(this.successor != null) {
      return error + this.successor.check(e);
    }
    return error;
  }

  this.checkTelephone = function(e) {
    if(this.isEmpty(e.value)) return "";
    var str = "";
    switch(parseInt(e.getAttribute("maxlength"))) {
      case 2:  //two separate fields
        if(!this.isNumber(e.value + "" + e.form.elements[e.index + 1].value, 10)) {
          str += "\nErro: Deve conter 2 números para o prefíxo do estado e 8 números para o telefone";
        }
        break;
      case 10: //one field with prefix, format = 0099999999
        if(!this.isNumber(e.value, 10)) {
          str += "\nErro: O campo deve conter 10 números, sendo os 2 primeiros o prefíxo do estado";
        }
        break;
      case 11: //one field, format = 00-99999999
        if(e.value.search(/\d{9}-\d{2}/) == -1) {
          str = "\nErro: O campo deve conter 10 números nesse formato: \"00-99999999\", sendo os dois zeros o prefíxo do estado";
        }
        break;
      default:  //one field no prefix
        if(!this.isNumber(e.value, 8)) {
          str += "\nErro: O campo deve conter 8 números";
        }
        break;
    }

    return str;
  }
}
top.IsTelephoneCheck.prototype.extendss("GenericCheck");
top.IsTelephoneCheck.instance = null;
top.IsTelephoneCheck.getInstance = function() {
  if(this.instance == null) {
    this.instance = new top.IsTelephoneCheck();
  }
  return this.instance;
}

top.IsCnpjCheck = function IsCnpjCheck() {

  this.successor = null;

  this.check = function(e) {
    var error = "";
    switch(e.getAttribute("datatype")) {
      case "cnpj":
        error += this.checkCnpj(e);
        break;
    }

    FormCheck.getInstance().addElementError(error);

    if(this.successor != null) {
      this.successor.check(e);
    }
  }

  this.checkCnpj = function(e) {
    var str = "";
    if(!IsEmptyCheck.getInstance().isEmpty(e.value)) {
      switch(parseInt(e.getAttribute("maxlength"))) {
        case 18:  //one field, format = 99.999.999/9999-99
        if(!this.isCnpj(e.value.replace(/-/g, "").replace(/\./g, "").replace(/\//g, ""))) {
          str += "\nErro: CNPJ inválido";
        }
        if(e.value.search(/\d{2}\.\d{3}\.\d{3}\/\d{4}-\d{2}/) == -1) {
          str += "\nErro: O CNPJ precisa estar nesse formato: \"99.999.999/9999-99\""
        }
        break;
      }
    } else if(e.getAttribute("r") == "true") {
      str += "\nErro: Campo vazio";
    }
    return str;
  }

  this.isCnpj = function(v) {
  	var alg1 = 0;
    var ok = false;
    var size = v.length;

    size--;

    alg1 = v.substr(1,1);
    for (i=2; i<size-1; ++i) {
      if (alg1 != v.charAt(i)) {
        ok = true;
        break;
      }
    }
	
    if (!ok) {
      return false;
    }
	
   	if(this.modulus(v.substring(0,v.length - 2)) + "" + this.modulus(v.substring(0,v.length - 1)) != v.substring(v.length - 2,v.length)) {
   		return false;
   	}

   	return true;
  }

  this.modulus = function(str) {
      var sum = 0;
      var ind = 2;

      for(pos=str.length-1;pos>-1;pos=pos-1) 
          {
        sum = sum + (parseInt(str.charAt(pos)) * ind);
        ind++;
        if(str.length>11) 
                  {
          if(ind>9) ind=2;
        }
    }

      rest = sum - (Math.floor(sum / 11) * 11);

      return (rest < 2)?0:(11 - rest);
  }
}
IsCnpjCheck.prototype.extendss("GenericCheck");

//implementation of the singleton design pattern
IsCnpjCheck.instance = 0;              //static "private" variable
IsCnpjCheck.getInstance = function() { //static method
  if(this.instance == 0) {
    this.instance = new IsCnpjCheck();
  }
  return this.instance;
}

top.IsCRCheck = function IsCRCheck() {

  this.successor = top.IsCpfCheck.getInstance();

  this.check = function(el) {
    var error = "";
    if(el.getAttribute("validation").indexOf("cr") > -1) {
      error += this.isCR(el);
    }

    if(this.successor != null) {
      return error + this.successor.check(el);
    }
    return error;
  }

  this.isCR = function(el) {
    var str = "";
    num = el.value;
    num = new Number(num.replace(",","."))
    if(isNaN(num)) {
      str = "\nErro: Campo só aceita números. Para separar a casa decimal utilize a vírgula";
    }else{
      if(num<0 || num>100){
        str = "\nErro: Campo deve ser preenchido apenas com números de 0 a 100 (com ou sem casas decimais separadas por vírgula)";
      }
    }
    return str;
  }
}
top.IsCRCheck.prototype.extendss("GenericCheck");
top.IsCRCheck.instance = null;
top.IsCRCheck.getInstance = function() {
  if(this.instance == null) {
    this.instance = new top.IsCRCheck();
  }
  return this.instance;
}

top.IsNumberCheck = function IsNumberCheck() {

  this.successor = top.IsCRCheck.getInstance();

  this.check = function(el) {
    var error = "";
    if(el.getAttribute("validation").indexOf("integer") > -1) {
      error += this.isInteger(el);
    } else if(el.getAttribute("validation").indexOf("float") > -1) {
      error += this.isFloat(el);
    }

    if(this.successor != null) {
      return error + this.successor.check(el);
    }
    return error;
  }

  this.isInteger = function(el) {
    var str = "";
    if(!this.isNumber(el.value)) {
      str = "\nErro: O campo deve ter um número inteiro, sem pontos ou vírgulas";
    }
    return str;
  }

  this.isFloat = function(el) {
    var str = "";
    if(!this.isNumber(el.value)) {
      str = "\nErro: Campo só aceita números. Para separar a casa decimal utilize a vírgula";
    }
    return str;
  }
}
top.IsNumberCheck.prototype.extendss("GenericCheck");
top.IsNumberCheck.instance = null;
top.IsNumberCheck.getInstance = function() {
  if(this.instance == null) {
    this.instance = new top.IsNumberCheck();
  }
  return this.instance;
}

top.IsInLengthCheck = function IsInLengthCheck() {

  this.successor = top.IsNumberCheck.getInstance();
  var max = 0;

  this.check = function(el) {
    var error = "";
    max = el.getAttribute("maxlength");
    if(max != null) {
      max = parseInt(max);
      switch(el.type) {
        case "file":
        case "password":
        case "text":
        case "textarea":
          error += this.isInLength(el);
          break;
        case "select-one":
        case "select-multiple":
          error += this.isInLengthSelect(el);
          break;
      }
    }

    if(this.successor != null) {
      return error + this.successor.check(el);
    }
    return error;
  }

  this.isInLength = function(el) {
    var str = "";
    if(el.value.length > max) {
      str += "\nErro: Campo possui " + el.value.length + " caracteres, " + (el.value.length - max) + " a mais do que o permitido. Coloque no máximo " + max + " caracteres";
    }
    return str;
  }

  this.isInLengthSelect = function(el) {
    var str = "";
    var selecteds = 0;
    for(var i = 0; i < el.length; i++) {
      if(el[i].selected) {
        selecteds++;
      }
    }

    if(selecteds > max) {
      str += "\nErro: Campo possui " + selecteds + " opções selecionadas, " + (selecteds - max) + " a mais do que o permitido. Selecione no máximo " + max + " opções";
    }
    return str;
  }
}
top.IsInLengthCheck.prototype.extendss("GenericCheck");
top.IsInLengthCheck.instance = null;
top.IsInLengthCheck.getInstance = function() {
  if(this.instance == null) {
    this.instance = new top.IsInLengthCheck();
  }
  return this.instance;
}

top.IsEmptyCheck = function IsEmptyCheck() {

  this.successor = top.IsInLengthCheck.getInstance();

  this.check = function(el) {
    var error = "";
    if(el.getAttribute("validation").indexOf("required") > -1) {
      if(!this.isDependencyEmpty(el)) {
        error = this.switchType(el);
      }
    }

    if(this.successor != null) {
      return error + this.successor.check(el);
    }
    return error;
  }

  this.switchType = function(el) {
    var error;
    switch(el.type) {
      case "file":
      case "password":
      case "text":
      case "textarea":
        error = this.isEmptyText(el);
        break;
      case "select-one":
        error = this.isEmptySelectOne(el);
        break;
      case "select-multiple":
        error = this.isEmptySelectMulti(el);
        break;
      default:
        error = this.isEmptyGroup(el);
    }
    return error;
  }

  this.isDependencyEmpty = function(el) {
    if(el.getAttribute("depends") == null) return false;
    var a = el.getAttribute("depends").split(",");
    for(var i = 0; i < a.length; i++) {
      if(this.switchType(eval("el.form." + a[i])) == "") {
        return false;
      }
    }
    return true;
  }

  this.isEmptyText = function(el) {
    var str = "";
    if(this.isEmpty(el.value)) {
      str += "\nErro: Campo está vazio";
    }
    return str;
  }

  this.isEmptyGroup = function(el) {
    var str = "";
    var group = eval("el.form." + el.name);
    if(group.length) {
      for(var i = 0; i < group.length; i++) {
        if(group[i].checked == true) {
          return str;
        }
      }
    } else {
      if(group.checked) {
        return str;
      }
    }
    return "\nErro: Nenhuma opção foi selecionada";
  }

  this.isEmptySelectOne = function(el) {
    var str = "";
    if(el.selectedIndex == 0) {
      str += "\nErro: Nenhuma opção foi selecionada";
    }
    return str;
  }

  this.isEmptySelectMulti = function(el) {
    var str = "";
    for(var i = 0; i < el.length; i++) {
      if(el[i].selected) {
        return str;
      }
    }
    str += "\nErro: Nenhuma opção foi selecionada";
    return str;
  }
}
top.IsEmptyCheck.prototype.extendss("GenericCheck");
top.IsEmptyCheck.instance = null;
top.IsEmptyCheck.getInstance = function() {
  if(this.instance == null) {
    this.instance = new top.IsEmptyCheck();
  }
  return this.instance;
}

top.FormCheck = function FormCheck() {

  var RETURN_ALL_ERRORS = 1;
  var RETURN_FIELD_ERRORS = 2;

  this.firstCheck = top.IsEmptyCheck.getInstance();
  this.elError = "";
  this.allErrors = "";
  this.returnErrors = RETURN_FIELD_ERRORS;

  this.checkForm = function(f, s, a, t) {
    var e, label;
    for(var i = 0; i < f.elements.length; i++) {
      this.elError = "";
      el = f.elements[i];
      if(el.disabled) {
        continue;
      } else if(el.getAttribute("validation") == undefined) {
        continue;
      }
      switch(el.type) {
        case "hidden":
        case "submit":
        case "button":
        case "reset":
        case "image":
          continue;
          break;
        default:
          this.elError = this.firstCheck.check(el);
      }

      if(this.elError == "") {
        continue;
      }

      label = el.name;
      if(el.getAttribute("label") != null) {
        label = el.getAttribute("label");
      }
      this.elError = "\nCampo: " + label + this.elError;
      this.allErrors += this.elError;
      if(this.returnErrors == RETURN_FIELD_ERRORS) {
        break;
      }
    }

    if(this.allErrors != "") {
      this.alertStrategy.alert("Erros encontrados:\n" + this.allErrors);
    } else {
      if(s) {
        if(a) f.action = a;
        if(t) f.target = t;
        f.submit();
      }
      return (true);
    }

    this.elError = "";
    this.allErrors = "";
    return (false);
  }

//default alert strategy
  var BasicAlert = function () {
    this.alert = function(s) {
      alert(s);
    }
  }
  this.alertStrategy = top.AlertBox.getInstance();
}
top.FormCheck.instance = null;
top.FormCheck.getInstance = function() {
  if(this.instance == null) {
    this.instance = new top.FormCheck();
  }
  return this.instance;
}

top.formcheck = function(f, s, a, t) {
  return (top.FormCheck.getInstance().checkForm(f, s, a, t));
}
