/**
 * ClassFilter
 * @author Patrick S. de Palma
 * ULTIMA ATUALIZAÇÃO DE: $Author: patrick.palma $
 * ATUALIZAÇÃO: $Date: 2008-05-16 17:52:41 -0300 (sex, 16 mai 2008) $
 * REVISÃO: $Revision: 9359 $
 * @fileOverview Biblioteca de formatação de campos
 */
/**
 * Classe principal<br/>
 * Este objeto só serve para a verificação da versão do ClassFilter
 * @author Patrick S. de Palma
 * @type Object
 */
classfilter = {
  version: 1.68,

  rule: {
    numero: RegExp('^[-0-9\\.,+]+$')
  },

  /**
   * Funções executáveis do atributo required
   */
  requiredFunc: {
    // Vincula uma condição ao campo
    add: function(field, fName, params){
      var check = getLimit(field, check);
      if (!check) check = {};

      // Aplica as variáveis necessárias para validação ao campo
      this[fName].add.apply(field, params);

      // Aplica o método de validação ao campo
      check[fName] = this[fName].check;
      // ...e salva na variável de limites
      addLimit(field, 'check', check);
    },

    min: {
      add: function(n){
        addLimit(this, 'min', Number(n));
      },
      check: function(){
        var i = this;
        var min = getLimit(this, 'min');
        if (min !== false) {
          if (!isNaN(i.value.toNumber()) && i.value.toNumber() < min) {
            showMsg(nomeCampo + ': O valor mínimo permitido é ' + min + '.');
          } else if (isNaN(i.value.toNumber()) && i.value.length < min) {
            showMsg(nomeCampo + ': O campo deve ter no mínimo ' + min + ' caracteres.');
          };
        };
      }
    },
    max: {
      add: function(n){
        addLimit(this, 'max', Number(n));
      },
      check: function(){
        var i = this;
        var max =  getLimit(this, 'max');
        if (max !== false) {
          if (!isNaN(i.value.toNumber()) && i.value.toNumber() > max) {
            showMsg(nomeCampo + ': O valor máximo só pode ser até ' + max + '.');
          } else if (isNaN(i.value.toNumber()) && i.value.length > max) {
            showMsg(nomeCampo + ': É permitido somente ' + max + ' caracteres.');
          };
        };
      }
    },
    maxlen: {
      add: function(n) {
        addLimit(this, 'maxLen', Number(n));
      },
      check: function(){
        var i = this;
        var maxLen = getLimit(this, 'maxLen');
        if (maxLen != undefined) {
          if (i.value.length > maxLen) {
            showMsg(nomeCampo + ': O campo pode ter no máximo '+ maxLen + ' caracteres.');
          };
        };
      }
    },
    minlen: {
      add: function(n) {
        addLimit(this, 'minLen', Number(n));
      },
      check: function() {
       var i = this;
       var minLen = getLimit(this, 'minLen');
       if (minLen !== false) {
         if (i.value.length < minLen) {
           showMsg(nomeCampo + ': O campo deve ter no mínimo ' + minLen + ' caracteres.');
         };
       };
      }
    },
    minnum: {
      add: function(n){
        addLimit(this, 'minnum', Number(n));
      },
      check: function(){
        var i = this;
        var minnum = getLimit(this, 'minnum');
        var util = new pspUtils();
        if (minnum !== false) {
          var n = util.toNum(i.value);
          if (isNaN(n)) {
            showMsg(nomeCampo + ': Por favor, digite corretamente os algarismos numéricos.');
          } else if (n < minnum) {
            showMsg(nomeCampo + ': O valor mínimo permitido é ' + minnum + '. Você digitou ' + i.value + '.');
          };
        };
      }
    },
    maxnum: {
      add: function(n){
        addLimit(this, 'maxnum', Number(n));
      },
      check: function(){
        var i = this;
        var maxnum = getLimit(this, 'maxnum');
        var util = new pspUtils();
        if (maxnum !== false) {
          var n = util.toNum(i.value);
          if (isNaN(n)) {
            showMsg(nomeCampo + ': Por favor, digite corretamente os algarismos numéricos.');
          } else if (n > maxnum) {
            showMsg(nomeCampo + ': O valor máximo permitido é ' + maxnum + '. Você digitou ' + i.value + '.');
          };
        };
      }
    },
    between: {
      add: function(d1, d2){
        var data1 = d1.replace(RegExp('([0-9]{2})\\/([0-9]{2})\\/([0-9]{4})'), "$3$2$1");
        var data2 = d2.replace(RegExp('^(\\d+)\\/(\\d+)\\/(\\d+)$'), "$3$2$1");
        addLimit(this, 'data1', Math.min(data1, data2));
        addLimit(this, 'data2', Math.max(data1, data2));
      },
      check: function(){
        var i = this;
        var data1 = getLimit(this, 'data1');
        var data2 = getLimit(this, 'data2');
        if (data1 !== false) {
          var data = i.value.replace(RegExp('^([0-9]+)\\/([0-9]+)\\/([0-9]+)$'), "$3$2$1");
          if (!(data >= data1 && data <= data2)) {
            showMsg(nomeCampo + ': A data deve estar entre ' + classfilter.cnvDate(data1) + ' e ' + classfilter.cnvDate(data2) + '.');
          };
        };
      }
    },
    after: {
      add: function(data, consideraHoje){
        addLimit(this, 'dataAfter', data.replace(RegExp('^(\\d+)\\/(\\d+)\\/(\\d+)$'), "$3$2$1"));
        addLimit(this, 'checkTodayAndAfter', consideraHoje);
      },
      check: function(){
        var i = this;
        var dataAfter = getLimit(this, 'dataAfter');
        var today     = getLimit(this, 'checkTodayAndAfter');
        if (dataAfter !== false) {
          var data = i.value.replace(RegExp('^([0-9]+)\\/([0-9]+)\\/([0-9]+)$'), "$3$2$1");
          if ((!today && data <= dataAfter) || (today && data < dataAfter)) {
            showMsg(nomeCampo + ': A data deve ser posterior a ' + classfilter.cnvDate(dataAfter) + '.');
          };
        };
      }
    },
    before: {
      add: function(data, consideraHoje){
        addLimit(this, 'dataBefore', data.replace(RegExp('^(\\d+)\\/(\\d+)\\/(\\d+)$'), "$3$2$1"));
        addLimit(this, 'checkTodayAndBefore', consideraHoje);
      },
      check: function(){
        var i = this;
        var dataBefore = getLimit(this, 'dataBefore');
        var today      = getLimit(this, 'checkTodayAndBefore');
        if (dataBefore !== false) {
          var data = i.value.replace(RegExp('^([0-9]+)\\/([0-9]+)\\/([0-9]+)$'), "$3$2$1");
          if ((!today && data >= dataBefore) || (today && data > dataBefore)) {
            showMsg(nomeCampo + ': A data deve ser anterior a ' + classfilter.cnvDate(dataBefore) + '.');
          };
        };
      }
    }
  },

  /**
   * Converte uma data no formato YYYYMMDD para DD/MM/YYYY
   * @param {String} d Data no formato YYYYMMDD
   */
  cnvDate: function(d){
    return String(d).replace(RegExp('^([0-9]{4})([0-9]{2})([0-9]{2})$'), "$3/$2/$1");
  },

  /**
   * Retorna o objeto cujo id ou name é igual a nodeID ou false se não for achado
   * @param {Mixed} nodeID
   * @param {Int} index (Opcional) Quando o id for relativo ao atributo name de um ou mais campos se "index" for omitido retornará o array
   *                    de campos, caso contrário retornará o campo N
   * @return {Object}
   */
  getNode: function(nodeID, index){
    if (typeof nodeID == 'string') {
      if (document.getElementById(nodeID)) {
        return document.getElementById(nodeID);
      } else if (document.getElementsByName(nodeID)) {
        if (typeof index == 'undefined') {
          return document.getElementsByName(nodeID);
        } else {
          return document.getElementsByName(nodeID)[index];
        };
      } else {
        return false;
      };
    } else {
      // nodeID é um objeto
      return nodeID;
    };
  },

  /**
   * Retorna o tipo de nodo ("unknown", "id" ou "name")
   * @param {String} nodeID
   */
  nodeType: function(nodeID){
    var type = 'unknown';
    if (document.getElementById(nodeID)) {
      type = 'id';
    } else if (document.getElementsByName(nodeID)) {
      type = 'name';
    };

    return type;
  },

  /**
   * Remove o "Status" de requerido
   * @param {Mixed} nodeID
   */
  unsetRequired: function(nodeID){
    var node = classfilter.getNode(nodeID, 0);
    if (!node) {
      return false;
    };
    addFilter(node, 'active', false);
    updateFilter(node);
    return true;
  },

  /**
   * Impõe uma classe no campo eliminando todas as outras que já estavam definidas
   * TODO: Resolver a eliminação eficaz de eventos do objeto
   * OBS: Função instável
   * @param {Mixed} nodeID
   * @param {String} className
   */
  setClass: function(nodeID, className){
    var node = classfilter.getNode(nodeID);
    if (classfilter.nodeType(nodeID) == 'name' && node.length) {
      /******************************
       * HORA DA RECURSIVIDADE
       */
      for (var i = 0; i < node.length; i++) {
        if (!classfilter.setClass(node[i], className)) {
          return false;
        };
      };
      return true;
      /******************************/
    };
    if (node) {
      node.className = className;
      node.filter = {};
      var toDel = ['Colorized', '_onblur', '_onclick', '_onkeydown', '_onkeypress', '_onkeyup', 'pt', 'l', 'onclick', '_onfocus', 'onfocus', 'onkeydown', 'onkeypress', 'onkeyup'];
      for (var i=0; i<toDel.length; i++) {
        var v = toDel[i];
        try {
        	node[v] = undefined;
        } catch(e) {
          node[v] = null;
        };
      };
      updateFilter(node);
      colorFocus();
      return true;
    };
    return false;
  },

  /**
   * Remove a classe especificada de um campo
   * @param {Mixed} nodeID
   * @param {String} className
   */
  unsetClass: function(nodeID, className){
    var node = classfilter.getNode(nodeID);
    if (classfilter.nodeType == 'name' && node.length) {
      /***************************
       * Hora da recursividade
       */
      for (var i = 0; i < node.length; i++) {
        if (!classfilter.unsetClass(node[i], className)) {
          return false;
        };
      };
      return true;
      /***************************/
    };
    if (node) {
      node.className = node.className.replace(className, '');
      node.className = node.className.replace(RegExp('^\\s+|\\s+$', 'g'), ''); // Trim
      node.className = node.className.replace(RegExp('\\s+', 'g'), ' '); // Normalize spaces
      return true;
    };
    return false;
  },

  /**
   * Adiciona uma classe ao campo
   * @param {Mixed} nodeID
   * @param {String} className
   * @return {Boolean}
   */
  addClass: function(nodeID, className){
    var node = classfilter.getNode(nodeID);
    if (node) {
      classfilter.unsetClass(nodeID, className);
      node.className += ' ' + className;
      return true;
    };
    return false;
  },

  /**
   * Adiciona um asterisco ao label ou ao lado do campo
   * @param {Object} field
   */
  markField: function(field) {
    if (field.filter) {
      if (!field.filter.noLabel && field.filter.labelTag && field.filter.labelTag.innerHTML.indexOf('*') == -1) {
        field.filter.labelTag.innerHTML += '<span class="red">*</span>';
        field.filter.labelTag.style.fontWeight = "bold";
      };

      if (!field.filter.labelTag && !(field.nextSibling != null && field.nextSibling.nodeName == 'SPAN' && field.nextSibling.innerHTML == '*')) {
        var span = document.createElement('span');
        span.innerHTML = '*';
        span.className = "red";
        field.parentNode.insertBefore(span, field.nextSibling);
      };
    };
  },

  /**
   * Remove o asterisco
   * @param {Object} field
   */
  unmarkField: function(field) {
    if (field.filter) {
      // Remove o asterisco do label
      if (field.filter.labelTag && field.filter.labelTag.innerHTML.indexOf('*') >= 0) {
        field.filter.labelTag.innerHTML = String(field.filter.labelTag.innerHTML).replace(RegExp('\\<span.*span\\>', 'gi'), '');
        field.filter.labelTag.style.fontWeight = '';
      };

      // Remove possível asterisco
      if (field.nextSibling != null && field.nextSibling.nodeName == 'SPAN' && field.nextSibling.innerHTML == '*') {
        field.parentNode.removeChild(field.nextSibling);
      };
    };
  },

  /**
   * Verifica se o campo contém o atributo "required" e retorna, se for encontrado, o conteúdo do atributo,
   * caso contrário, retorna 0(zero)
   * @param {Object} field
   */
  isRequired: function(field){
    return (field && field.getAttribute && field.getAttribute('required')) ? field.getAttribute('required') : 0;
  },

  /**
   * Marca um campo como requerido
   * @param {Mixed} nodeID
   * @param {Boolean} fromLabel (Opcional) Utilizado somente pelos scaneadores de labels e titles
   * @return {null}
   */
  setRequired: function(nodeID, fromLabel){
    // Parâmetro especificado SOMENTE por scan.label e scan.title
    if (typeof fromLabel == 'undefined') fromLabel = false;

    var field = classfilter.getNode(nodeID, 0);
    requiredValue = classfilter.isRequired(field);

    if (!requiredValue && field.filter && field.filter.active) requiredValue = 1;
    addFilter(field, 'active', true);

    if (fromLabel) classfilter.markField(field);

    // Indica que o form tem pelo menos um campo requerido.
    if (field.form) field.form.hasRequired = true;

    if (isNaN(requiredValue)) {
      var r = String(requiredValue).toLowerCase();
      var cmd = r.split(';');
      if (field.filter.limits) field.filter.limits.check = {};
      for (var j = 0; j < cmd.length; j++) {
        try {
          var fnName = RegExp('^(\\w+)\\(').exec(cmd[j])[1];
          var fnAdd = cmd[j].replace(RegExp('^\\w+\\((.*)\\)'), '$1');
          var fnAdd = fnAdd.replace(/['"]/g, ''); // Remove aspas
          classfilter.conditions.add(field, fnName, fnAdd.split(','));
        } catch (e) {
          continue;
        };
      };
    };

    if (!fromLabel) {
      updateFilter(field);
    };
  },

  /**
   * Retorna um array de labels ou um label com o htmlFor igual ao _fieldName
   * @param {String} _fieldName Encontra um label cujo htmlFor é igual a _fieldName
   * @return {Array|Boolean} Array se o label for encontrado, se não FALSE
   */
  labels: function(_fieldName) {
    var found = false, _labels = document.getElementsByTagName('label');

    if (_fieldName && _labels) {
      for (var i=0; i<_labels.length; i++) {
        if (_labels[i].htmlFor == _fieldName) {
          _labels = [ _labels[i] ];
          found = true;
          break;
        };
      };
      if (!found) _labels = false;
    };

    return _labels;
  },

  /**
   * Scaneadores
   */
  scan: {
    /**
     * Scan de labels
     */
    label: function(_input) {
      var labels = classfilter.labels(_input);
      var hasRequired = false;

      /* Se não for encontrado, ou não houver labels, não continua. */
      if (labels === false) return false;

      for (var i = 0; i<labels.length; i++) {
        /* Pula para o próximo caso htmlFor esteja vazio */
        if (labels[i].htmlFor == '') continue;

        /* Pega o campo a partir do htmlFor do label */
        var field = labels[i].form[labels[i].htmlFor];
        /* mas se campo não existir, for de só leitura ou estiver inabilitado pula para o próximo campo */
        if (field == null || field.readOnly || field.disabled) continue;

        /* Se a variável field não está em um array de campos, cria uma variável array e adiciona o campo nessa variável */
        var fields = (field.length && field[0].nodeName == 'INPUT' && /^(?:checkbox|radio)$/.test(field[0].type)) ? field : [field];

        /* Trata todos os inputs quando estes são de tipo radio ou checkbox.
         * Ainda assim, se o campo não for desses dois tipos, aplica o filtro simplesmente.
         */
        for (var j = 0; j<fields.length; j++) {
          var f = fields[j], required = classfilter.isRequired(f) || getFilter(f, 'active') == true, filter = {}, fieldname;
          /* Atualiza o flag que indica que há pelo menos um campo requerido */
          if (required) hasRequired = true;
          fieldname = labels[i].innerHTML.replace(/(?:\:|<\w+.*\w+>)/g, '');

          /* Filtros */
          filter = {
            active: required && j == 0,
            noLabel: j > 0,
            label: fieldname,
            labelTag: labels[i]
          };

          /* Assigna os atributos ao filtro do campo */
          for (var attr in filter) addFilter(f, attr, filter[attr]);

          /* Marca o form como tendo pelo menos um campo a verificar */
          if (f.form && filter.active) f.form.hasLabel = true;

          /* Faz as alterações necessárias no campo quando este for ativo */
          if (filter.active)
            classfilter.setRequired(f, 1);
          else if (j == 0)
            classfilter.unmarkField(f);
        };
      };
      return hasRequired;
    },

    /**
     * Scan de campos com atributo "title"
     */
    title: function(input) {
      if (!document.forms) return false;
      var hasRequired = false;
      var tagCollection = [];

      if (typeof input != 'undefined') {
        tagCollection[0] = [input];
      } else {
        tagCollection = [
          document.getElementsByTagName('SELECT'),
          document.getElementsByTagName('INPUT'),
          document.getElementsByTagName('TEXTAREA')
        ];
      }

      for (var i = 0; i < tagCollection.length; i++) {
        for (var j = 0; j < tagCollection[i].length; j++) {
          var field = tagCollection[i][j];

          if (field.disabled || field.readOnly) continue;

          // Verifica se já tem label
          if (getFilter(field, 'label')) continue;

          if (classfilter.isRequired(field) && field.title.length > 0) {
            hasRequired = true;
            addFilter(field, 'label', field.title);
            classfilter.setRequired(field, 1);
          } else {
            addFilter(field, 'active', false);
            classfilter.unmarkField(field);
          };
        };
      };
      return hasRequired;
    }
  },

  debug: function(msg, clear) {
    var divDebug = document.getElementById("classfilterDebug");

    if (!divDebug) {
      divDebug = document.body.appendChild(document.createElement('div'));
      divDebug.id = 'classfilterDebug';
      divDebug.style.position = 'absolute';
      divDebug.style.top = '10px';
      divDebug.style.left = '10px';
      divDebug.style.width = '180px';
      divDebug.style.height = '400px';
      divDebug.style.overflow = 'auto';
      divDebug.style.padding = '5px 13px';
      divDebug.style.border = '1px solid black';
      divDebug.style.backgroundColor = '#EAEAEA';
    };

    if (clear) {
      divDebug.innerHTML = '';
    };

    divDebug.innerHTML += "<li>" + msg + "</li>";
  },

  debugStartTime: function() {
    this.debug.time = new Date();
  },

  debugEndTime: function() {
    var cTime = new Date();
    var timeDiff = cTime - this.debug.time;
    this.debug.time = null;
    this.debug("Tempo utilizado: " + timeDiff + "ms");
  },

  loadConfig: function() {
    var path, s = document.getElementsByTagName("script");

    for (var i=0; i<s.length; i++) {
      if (s[i].src.indexOf('classfilter') != -1) {
        path = s[i].src.replace(/classfilter(?:\.src|)\.js(\?.*)?$/,'');
      };
    };

    document.write('<script type="text/javascript" src="'+path+'classfilter.config.js"></script>');
  }
};

var _filtro = {
  add: function(fName, filtro, desc, mascaraFunc, maxlen) {
    _filtro[fName] = {};
    if (filtro) _filtro[fName].filtro = filtro;
    if (desc) _filtro[fName].desc = desc;

    if (typeof mascaraFunc == 'function') {
      _filtro[fName].funcao = mascaraFunc;
    } else if (typeof mascaraFunc == 'string') {
      _filtro[fName].mascara = mascaraFunc;
    };

    if (maxlen) _filtro[fName].maxlen = maxlen;
  }
};

function verifica_navegador() {
    // convert all characters to lowercase to simplify testing
    var agt=navigator.userAgent.toLowerCase();

    // *** BROWSER VERSION ***
    // Note: On IE5, these return 4, so use is_ie5up to detect IE5.
    var is_major = parseInt(navigator.appVersion);
    var is_minor = parseFloat(navigator.appVersion);

    var is_ie     = ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1));
    var is_ie3    = (is_ie && (is_major < 4));
    var is_ie4    = (is_ie && (is_major == 4) && (agt.indexOf("msie 4")!=-1) );
    var is_ie4up  = (is_ie && (is_major >= 4));
    var is_ie5    = (is_ie && (is_major == 4) && (agt.indexOf("msie 5.0")!=-1) );
    var is_ie5_5  = (is_ie && (is_major == 4) && (agt.indexOf("msie 5.5") !=-1));
    var is_ie5up  = (is_ie && !is_ie3 && !is_ie4);
    var is_ie5_5up =(is_ie && !is_ie3 && !is_ie4 && !is_ie5);
    var is_ie6    = (is_ie && (is_major == 4) && (agt.indexOf("msie 6.")!=-1) );
    var is_ie6up  = (is_ie && !is_ie3 && !is_ie4 && !is_ie5 && !is_ie5_5);

    verifica_navegador.is_ie6 = is_ie6;
    verifica_navegador.is_ie6up = is_ie6up;

    function showAlert() {
      var scrw = document.body.clientWidth;
      var scrh = document.body.clientHeight;
      var dw = scrw * 0.8;
      var dh = scrh * 0.8;
      var div = document.body.appendChild(document.createElement('div'));
      document.body.style.visibility = "hidden";
      div.style.position = "absolute";
      div.style.width = dw + "px";
      div.style.height = dh + "px";
      div.style.left = ((scrw - dw)/2) + "px";
      div.style.top = ((scrh - dh)/2) + "px";
      div.style.backgroundColor = 'white';
      div.style.border = "1px solid #999";
      div.style.paddingTop = "20px";//(dh / 2) + "px";
      div.style.fontSize = "1.5em";
      div.style.fontWeight = "bold";
      div.innerHTML = "<div style=\"font-size: 120%; color:#CC0000\">ATENÇÃO!</div>A sua versão de Internet Explorer (5.5 ou anterior) é muito antiga.<br/><br/>";
      div.innerHTML += "Para uma melhor experiência de navegação, aqui e em muitos outros sites,<br/> atualize a sua versão.<br/>";
      div.innerHTML += "Clique em uma das imagens a seguir para baixar a versão mais recente do<br/> Firefox (recomendado) ou Internet Explorer.<br/><br/>";
      div.innerHTML += '<a href="http://br.mozdev.org/" target="_blank"><img lowsrc="/fw/img/indicator.gif" src="http://www.spreadfirefox.com/community/images/affiliates/Buttons/180x60/rediscover.gif" width="180" height="60" style="border-style:none;" title="Firefox - Redescubra a web" alt="Firefox - Download" /></a>';
      div.innerHTML += '&nbsp;&nbsp;&nbsp;';
      div.innerHTML += '<a href="http://www.microsoft.com/downloads/browse.aspx?displaylang=pt-br&productID=5A8BB164-5FC3-4BE5-95BB-BA73EEED1CA6" target="_blank"><img lowsrc="/fw/img/indicator.gif" alt="Internet Explorer - Download" src="http://www.microsoft.com/genuine/offerimages/183a99d5-cb7d-4ec2-99a5-a72befbe42cc/ie7image_100.jpg"></a>';
    };

    if (is_ie) {
      // Ok. É IE.
      if (!is_ie6up) {
        window.onload = showAlert;
        return false;
      };
    };
    return true;
};

// Alias para requiredFunc
classfilter.conditions = classfilter.requiredFunc;


String.prototype.toNumber = function() {
  var n = this;

  if (n.indexOf(',') > 0) {
    n = n.replace(/\./g, '');
    n = n.replace(/,/g, '.');
  };

  if (isNaN(n)) {
    return Number.NaN;
  } else {
    return n;
  };
};

/**
 * String.isCPF Function v1.0
 * @author Carlos R. L. Rodrigues
 * @author Patrick S. de Palma (Modificação)
 * @addon
 */
String.prototype.isCPF = function() {
    var c = this;
    if((c = c.replace(RegExp('[^\\d]', 'g'),"").split("")).length != 11) return false;
    if(c.join("") == "12345678909") return false;
    if(c.join("") == "00000000191") return false;
    // Verifica se não há cpf inválido do tipo 000.000.000-00
    for(var i = 0, len=c.length, last = c[0], equal = true; i<len; equal = equal && (last == c[i]),last = c[i],i++);
    if (equal) return false;
    if(RegExp("^" + c[0] + "{11};$").test(c.join(""))) return false;
    for(var s = 10, n = 0, i = 0; s >= 2; n += c[i++] * s--);
    if(c[9] != (((n %= 11) < 2) ? 0 : 11 - n)) return false;
    for(var s = 11, n = 0, i = 0; s >= 2; n += c[i++] * s--);
    if(c[10] != (((n %= 11) < 2) ? 0 : 11 - n)) return false;
    return true;
};

/**
 * String.isCNPJ Function v1.0
 * @author Carlos R. L. Rodrigues
 * @author Patrick S. de Palma (Modificação)
 * @addon
 */
String.prototype.isCNPJ = function() {
    var b = [6,5,4,3,2,9,8,7,6,5,4,3,2], c = this;
    if((c = c.replace(RegExp('[^\\d]', 'g'),"").split("")).length != 14) return false;
    // Verifica se não há CNPJ inválido do tipo 00.000.000/0000-00
    for(var i = 0, len=c.length, last = c[0], equal = true; i<len; equal = equal && (last == c[i]),last = c[i],i++);
    if (equal) return false;
    for(var i = 0, n = 0; i < 12; n += c[i] * b[++i]);
    if(c[12] != (((n %= 11) < 2) ? 0 : 11 - n)) return false;
    for(var i = 0, n = 0; i <= 12; n += c[i] * b[i++]);
    if(c[13] != (((n %= 11) < 2) ? 0 : 11 - n)) return false;
    return true;
};

/**
 * formatCurrency Function v1.1
 * Formata o input de forma que ele assuma o comportamento de um campo monetário.
 * formatCurrency(field: HTMLInput, [floatPoint: Integer = 2], [decimalSep: String = ","], [thousandsSep: String = "."]): String
 * @author Carlos R. L. Rodrigues
 * @author Patrick S. de Palma (Modificação)
 *
 * @param {Object};  o   campo que receberá a formatação
 * @param {Integer}; n   número de casas decimais
 * @param {String};  dig string representando o separador decimal
 * @param {String};  dec string representando o separador de milhar
 */
function formatCurrency(o, n, dig, dec){
  var ev = new evUtils();
  o.c = !isNaN(n) ? Math.abs(n) : 2;
  o.dec = typeof dec != "string" ? "," : dec;
  o.dig = typeof dig != "string" ? "." : dig;

  if (o.filter && o.filter.currency) {
    return;
  } else {
    addFilter(o, 'currency', true);
  };

  ev.addEvent(o, "keypress", function(e){
    if (e.key > 47 && e.key < 58) {
      var o, s, l = (s = ((o = this).value.replace(RegExp('^0+', 'g'), "") + String.fromCharCode(e.key)).replace(RegExp('\\D', 'g'), "")).length, n;
      if (o.maxLength + 1 && l >= o.maxLength) return false;
      l <= (n = o.c) && (s = new Array(n - l + 2).join("0") + s);
      for (var i = (l = (s = s.split("")).length) - n; (i -= 3) > 0; s[i - 1] += o.dig);
      n && n < l && (s[l - ++n] += o.dec);
      o.value = s.join("");
    };
    e.key > 30 && e.preventDefault();
  });
};

/**
 * Restrict Class v1.0
 * @author Carlos R. L. Rodrigues
 * @author Patrick S. de Palma (Modificação)
 */
Restrict = function(form){ this.form = form, this.field = {}, this.input = {}, this.mask = {}; };

/**
 * @method
 */
Restrict.field = Restrict.inst = Restrict.c = null;
Restrict.prototype.start = function(){
  var ev = new evUtils();
  var $, form = this.form, s, x, j, c, sp, o = this, l;
  var p = {
    ".": new RegExp('.'),
    w: new RegExp('[a-zA-Z]'),
    W: new RegExp('\\W'),
    d: new RegExp('\\d'),
    D: new RegExp('\\D'),
    s: new RegExp('\\s'),
    a: new RegExp('[\\xc0-\\xff]'),
    A: new RegExp('[^\\xc0-\\xff]')
  };
  var _keyup = function(e){
        (Restrict.field = e.target).e = 0;
      };
  var _keypress = function(e){
        o.restrict(e) || e.preventDefault();
        var r = Restrict.field = e.target;
        if (!o.mask[r.name]) return;
        if (!r.e) {
          r.l = r.value.length, Restrict.inst = o, Restrict.c = e.key || 0;
          setTimeout(o.onchanged, 1);
        };
      };
  var _keydown = function(e){
        var r = Restrict.field = e.target;
        if (!o.mask[r.name]) return;
        r.l = r.value.length, Restrict.inst = o;
        Restrict.c = e.key;
        setTimeout(o.onchanged, r.e = 1);
      };
  Restrict.evFunc = {
    keyup: _keyup,
    keypress: _keypress,
    keydown: _keydown
  };
  for (var fieldname in $ = this.input) {
    var field = this.input[fieldname];
    if (RegExp('text|textarea|password', 'i').test(field.type)) {
      x = this.field[field.name].split(""), c = j = 0, sp, s = [[], []];
      for (var i = 0, l = x.length; i < l; i++)
        if (x[i] == "\\" || sp) {
          if (sp = !sp) continue;
          s[j][c++] = p[x[i]] || x[i];
        } else if (x[i] == "^")
          c = (j = 1) - 1;
        else
          s[j][c++] = x[i];
      o.mask[field.name] && (field.maxLength = o.mask[field.name].length);
      if (field.filter && !field.filter.maskActive) {
        field.pt = s, ev.addEvent(field, "keydown", _keydown);
        ev.addEvent(field, "keyup", _keyup);
        ev.addEvent(field, "keypress", _keypress);
        addFilter(field, 'maskActive', true);
      };
    };
  };
};
/**
 * @param {Object}; e Evento
 */
Restrict.prototype.restrict = function(e){
    var o, c = e.key, n = (o = e.target).name, r;
    var has = function(c, r){
        for(var i = r.length; i--;)
            if((r[i] instanceof RegExp && r[i].test(c)) || r[i] == c) return true;
        return false;
    };
    var inRange = function(c){
        return has(c, o.pt[0]) && !has(c, o.pt[1]);
    };
    return (c < 30 || inRange(String.fromCharCode(c))) ?
        (this.onKeyAccept && this.onKeyAccept(o, c), !0) :
        (this.onKeyRefuse && this.onKeyRefuse(o, c),  !1);
};
Restrict.prototype.onchanged = function(){
  var ob = Restrict, si, moz = false, o = ob.field, t, lt = (t = o.value).length, m = ob.inst.mask[o.name];
  if (o.l == o.value.length) return;
  try {
    if (si = o.selectionStart)
      moz = true;
    else if (o.createTextRange) {
      var obj = document.selection.createRange(), r = o.createTextRange();
      if (!r.setEndPoint) return false;
      r.setEndPoint("EndToStart", obj);
      si = r.text.length;
    } else
      return false;
  } catch (e) {
  };
  if (typeof m == 'undefined') return;
  for (var i in m = m.split(""))
    if (m[i] != "#") t = t.replace(m[i] == "\\" ? m[++i] : m[i], "");
  var j = 0, h = "", l = m.length, ini = si == 1, t = t.split("");
  for (i = 0; i < l; i++)
    if (m[i] != "#") {
      if (m[i] == "\\" && (h += m[++i])) continue;
      h += m[i], i + 1 == l && (t[j - 1] += h, h = "");
    } else {
      if (!t[j] && !(h = "")) break;
      (t[j] = h + t[j++]) && (h = "");
    };
  o.value = o.maxLength > -1 && o.maxLength < (t = t.join("")).length ? t.slice(0, o.maxLength) : t;
  if (ob.c && ob.c != 46 && ob.c != 8) {
    if (si != lt) {
      while (m[si] != "#" && m[si])
        si++;
      ini && m[0] != "#" && si++;
    } else
      si = o.value.length;
  };
  !moz ? (obj.move("character", si), obj.select()) : o.setSelectionRange(si, si);
};


/**
 * INICIO CALENDÁRIO
 */
/* --- Swazz Javascript Calendar ---
/* --- v 1.0 3rd November 2006
By Oliver Bryant
http://calendar.swazz.org

Modificado por Patrick
--- Lista de alterações:
:: Tradução dos meses para português.
:: Bugfix da posição do calendário no firefox.
:: Comentado código que desabilitava os dias que precediam a data atual.
:: Limitação do valor do mês (de 1 a 12) para não mostrar "undefined"
*/
swazzCalendar = {
  init: function() {
    var cal = swazzCalendar;
    // Calendar script
    cal.now    = new Date;
    cal.sccm   = cal.now.getMonth();
    cal.sccy   = cal.now.getFullYear();
    cal.ccm    = cal.now.getMonth();
    cal.ccy    = cal.now.getFullYear();
    cal.updobj = {};

    // TABLE
    var table = document.createElement('table');
    table.id = 'fc';
    table = document.body.appendChild(table);
    table.cellPadding = '2';
    // Evita que seja mostrada por alguns milisegundos a tabela enquanto é construída.
    table.style.display = 'none';

    // Row 1 : Month title and jump
    var row = table.insertRow(-1);

    var td = row.insertCell(-1);
    td.className = "csubm";
    td.innerHTML = "&nbsp;";
    td.onclick   = function(){cal.csubm()};

    td = row.insertCell(-1);
    td.id = "mns";
    td.colSpan = 5;

    td = row.insertCell(-1);
    td.className = "caddm";
    td.innerHTML = "&nbsp;";
    td.onclick = function(){cal.caddm()};

    // Row 2 : Weekdays
    row = table.insertRow(-1);
    row.className = "weekHeader";
    for(var i=0, w=['S','T','Q','Q','S','S','D'];i<7;i++) {
      row.insertCell(-1).innerHTML = w[i];
    }

    // Create month day
    for (var i=0; i<6; i++) {
      row = table.insertRow(-1);
      row.className = "monthDays";
      for (var j=0; j<7; j++ ) {
        var n = 7 * i + (j+1);
        td = row.insertCell(-1);
        td.id = "v" + n;
        td.innerHTML = "&nbsp;";
      }
    }

    swazzCalendar.addEvent(window, 'click', function(){cal.checkClick});
  },

  setEvent: function(obj) {
   if (obj.filter && obj.filter.calendar) {
     return;
   } else {
     addFilter(obj, 'calendar', true);
   };
   swazzCalendar.addEvent(obj, 'click', swazzCalendar.click);
   swazzCalendar.addEvent(obj, 'focus', swazzCalendar.focus);
   swazzCalendar.addEvent(obj, 'keyup', swazzCalendar.keyup);
   swazzCalendar.addEvent(obj, 'blur',  swazzCalendar.blur);
  },

  unsetEvent: function(obj) {
   if (obj.filter && obj.filter.calendar) {
     addFilter(obj, 'calendar', false);
   } else {
     return;
   };
   swazzCalendar.removeEvent(obj, 'click', swazzCalendar.click);
   swazzCalendar.removeEvent(obj, 'focus', swazzCalendar.focus);
   swazzCalendar.removeEvent(obj, 'keyup', swazzCalendar.keyup);
   swazzCalendar.removeEvent(obj, 'blur',  swazzCalendar.blur);
  },

  click: function(event){
    event.cancelBubble = true;
    this.select();
    swazzCalendar.lcs(this)
  },
  focus: function(event){
    if (!this.visible) {
      this.select();
      swazzCalendar.lcs(this);
    };
  },
  keyup:function(event) { if (!verifica_navegador.is_ie6) swazzCalendar.lcs(this) },
  blur:function(event) { window.calTimeout = setTimeout("swazzCalendar.cs_close()",500); },

  getObj: function(objID){
    if (document.getElementById) {
      return document.getElementById(objID)
    } else if (document.all) {
      return document.all[objID]
    } else if (document.layers) {
      return document.layers[objID]
    };
  },

  checkClick: function(e){
    evt = e ? e : event;
    CSE = evt.target ? evt.target : evt.srcElement;
    if (swazzCalendar.getObj('fc')) {
      if (!swazzCalendar.isChild(CSE, swazzCalendar.getObj('fc'))) {
        swazzCalendar.getObj('fc').style.display = 'none';
      };
    };
  },

  isChild: function(s, d){
    while (s) {
      if (s == d) return true;
      s = s.parentNode;
    };
    return false;
  },

  Left: function(obj){
    var curleft = 0;
    if (obj.offsetParent) {
      while (obj.offsetParent) {
        curleft += obj.offsetLeft;
        obj = obj.offsetParent;
      };
    } else if (obj.x) curleft += obj.x;
    return curleft;
  },

  Top: function(obj){
    var curtop = 0;
    if (obj.offsetParent) {
      while (obj.offsetParent) {
        curtop += obj.offsetTop;
        obj = obj.offsetParent;
      };
    } else if (obj.y) curtop += obj.y;
    return curtop;
  },

  lcs: function(ielem){
    window.p = new pspUtils();
    if (typeof window.calTimeout != 'undefined' && window.calTimeout) clearInterval(window.calTimeout);
    // Esconde todos os selects da página
    if (verifica_navegador.is_ie6) setTimeout("window.p.showSelects(false)",0);
    swazzCalendar.updobj = ielem;
    swazzCalendar.getObj('fc').style.left = swazzCalendar.Left(ielem) + "px";
    swazzCalendar.getObj('fc').style.top = swazzCalendar.Top(ielem) + ielem.offsetHeight + "px";
    swazzCalendar.getObj('fc').style.display = 'block';

    // First check date is valid
    var d = new Date();
    var curdt = ielem.value;
    var curdtarr = curdt.split('/');
    if (!curdtarr[0]) curdtarr[0] = d.getDate();
    if (!curdtarr[1] || (curdtarr[1] < 1 || curdtarr[1] > 12)) curdtarr[1] = d.getMonth() + 1;
    if (!curdtarr[2]) curdtarr[2] = d.getFullYear();

    var isdt = true;
    for (var k = 0; k < curdtarr.length; k++) {
      if (isNaN(curdtarr[k])) isdt = false;
    };
    if (isdt & (curdtarr.length == 3)) {
      swazzCalendar.ccm = curdtarr[1] - 1;
      swazzCalendar.ccy = curdtarr[2];
      swazzCalendar.prepcalendar(curdtarr[0], curdtarr[1] - 1, curdtarr[2]);
    };

  },
  cs_over: function(e){
    this.style.backgroundColor = '#FFCC66';
  },
  cs_out: function(e){
    this.style.backgroundColor = '';
  },
  cs_close: function(){
    swazzCalendar.getObj('fc').style.display = '';
    if (verifica_navegador.is_ie6) setTimeout("window.p.showSelects(true)",0);
  },
  cs_click: function(e){
    var d = swazzCalendar.calvalarr[this.id.substring(1, this.id.length)];
    d = d.split('/');
    if (String(d[0]).length < 2) d[0] = "0" + d[0];
    if (String(d[1]).length < 2) d[1] = "0" + d[1];
    d = d.join('/');
    swazzCalendar.updobj.value = d;
    swazzCalendar.getObj('fc').style.display = 'none';
  },
  visible: function() {
    return swazzCalendar.getObj('fc').style.display == 'block';
  },

  mn: new Array('JAN', 'FEV', 'MAR', 'ABR', 'MAI', 'JUN', 'JUL', 'AGO', 'SET', 'OUT', 'NOV', 'DEZ'),
  mnn: new Array('31', '28', '31', '30', '31', '30', '31', '31', '30', '31', '30', '31'),
  mnl: new Array('31', '29', '31', '30', '31', '30', '31', '31', '30', '31', '30', '31'),
  calvalarr: new Array(42),

  f_cps: function(obj){
    obj.className = "swazzNormal";
  },

  f_hds: function(obj){
    obj.className = "swazzToday";
  },

  /**
   *  This function is for Saturday and Sunday.
   *  @author Patrick S. de Palma
   */
  f_chds: function(obj){
    obj.className = "swazzWeekend";
  },

  // day selected
  prepcalendar: function(hd, cm, cy){
    var td = new Date();
    td.setDate(1);
    td.setFullYear(cy);
    td.setMonth(cm);
    var cd = td.getDay();

    // Cálculo para deixar como primeiro dia da semana a segunda-feira
    cd = (cd - 1) < 0 ? 6 : cd - 1;

    swazzCalendar.getObj('mns').innerHTML = swazzCalendar.mn[cm] + ' ' + cy;
    var marr = ((cy % 4) == 0) ? swazzCalendar.mnl : swazzCalendar.mnn;
    for (var d = 1; d <= 42; d++) {
      if ((d >= (cd - (-1))) && (d <= cd - (-marr[cm]))) {
        htd = ((hd != '') && (d - cd == hd));
        if (htd)
          swazzCalendar.f_hds(swazzCalendar.getObj('v' + d)); // Hoje
        else if (d % 7 == 0)
          swazzCalendar.f_chds(swazzCalendar.getObj('v' + d)); // Domingos "marcados"
        else
          swazzCalendar.f_cps(swazzCalendar.getObj('v' + d));

        swazzCalendar.getObj('v' + d).onmouseover = swazzCalendar.cs_over;
        swazzCalendar.getObj('v' + d).onmouseout = swazzCalendar.cs_out;
        swazzCalendar.getObj('v' + d).onclick = swazzCalendar.cs_click;

        swazzCalendar.getObj('v' + d).innerHTML = (d % 7 == 0 || (d + 1) % 7 == 0) ? String(d - cd).bold() : d - cd;
        swazzCalendar.calvalarr[d] = '' + (d - cd) + '/' + (cm - (-1)) + '/' + cy;
      } else {
        swazzCalendar.f_cps(swazzCalendar.getObj('v' + d));
        swazzCalendar.getObj('v' + d).innerHTML = '&nbsp;';
        swazzCalendar.getObj('v' + d).onmouseover = null;
        swazzCalendar.getObj('v' + d).onmouseout = null;
        swazzCalendar.getObj('v' + d).style.cursor = 'default';
      };
    };
  },

  caddm: function(){
    if (window.calTimeout) clearTimeout(window.calTimeout);
    window.calTimeout = null;

    var marr = ((swazzCalendar.ccy % 4) == 0) ? swazzCalendar.mnl : swazzCalendar.mnn;

    swazzCalendar.ccm += 1;
    if (swazzCalendar.ccm >= 12) {
      swazzCalendar.ccm = 0;
      swazzCalendar.ccy++;
    };
    //cdayf();
    var ccd = (swazzCalendar.sccm == swazzCalendar.ccm && swazzCalendar.sccy == swazzCalendar.ccy) ? swazzCalendar.cdayf() : '';
    swazzCalendar.prepcalendar(ccd, swazzCalendar.ccm, swazzCalendar.ccy);
  },

  csubm: function(){
    if (window.calTimeout) clearTimeout(window.calTimeout);
    window.calTimeout = null;

    var marr = ((swazzCalendar.ccy % 4) == 0) ? swazzCalendar.mnl : swazzCalendar.mnn;

    swazzCalendar.ccm -= 1;
    if (swazzCalendar.ccm < 0) {
      swazzCalendar.ccm = 11;
      swazzCalendar.ccy--;
    };
    //cdayf();
    var ccd = (swazzCalendar.sccm == swazzCalendar.ccm && swazzCalendar.sccy == swazzCalendar.ccy) ? swazzCalendar.cdayf() : '';
    swazzCalendar.prepcalendar(ccd, swazzCalendar.ccm, swazzCalendar.ccy);
  },

  cdayf: function(){
    var day = '';
    var re = /^([0-9]{2})/;
    if (re.test(swazzCalendar.updobj.value)) {
      var d = re.exec(swazzCalendar.updobj.value);
      day = d[1];
    } else {
      day = swazzCalendar.now.getDate();
    };

    return day;
  },

  /**
   * Função de associação de multiplos eventos a objetos
   * addEvent(object: Object, event: String, handler: Function(e: Event): Boolean, [scope: Object = object]): Boolean
   * Adiciona uma função que será disparada quando ocorrer determinado evento no objeto.
   * @param {Object}; o Objeto que receberá o listener
   * @param {String}; e Nome do evento sem o prefixo "on" (click, mouseover, ...)
   * @param {Object}; f Função que será chamada quando o evento ocorrer, será enviado como argumento
   *                   para esta função o objeto de evento, que além das propriedades normais, *sempre* irá conter:
   *                   - target: Objeto que gerou o evento
   *                   - key: Código do caractere em eventos de teclado
   *                   - stopPropagation: Método para evitar a propagação do evento
   *                   - preventDefault: Método para evitar que a ação default ocorra
   *                     O preventDefault pode ser emulado retornando "false" na função
   * @param {Object}; s Escopo (quem o "this" irá referenciar dentro do handler) que será usado quando a função for chamada, o default
   *                   é o objeto no primeiro argumento
   *
   */
  addEvent: function(o, e, f, s){
    var r = o[r = "_" + (e = "on" + e)] = o[r] || (o[e] ? [[o[e], o]] : []), a, c, d;
    r[r.length] = [f, s || o], o[e] = function(e){
      try{
        (e = e || event).preventDefault || (e.preventDefault = function(){e.returnValue = false;});
        e.stopPropagation || (e.stopPropagation = function(){e.cancelBubble = true;});
        e.target || (e.target = e.srcElement || null);
        e.key = (e.which + 1 || e.keyCode + 1) - 1 || 0;
      } catch(f){};
      for(d = 1, f = r.length; f; r[--f] && (a = r[f][0], o = r[f][1], a.call ? c = a.call(o, e) : (o._ = a, c = o._(e), o._ = null), d &= c !== false));
      return e = null, !!d;
      };
  },

  /**
   * Função de desassociação de múltiplos eventos a objetos
   * removeEvent(object: Object, event: String, handler: function(e: Event): Boolean, [scope: Object = object]): Boolean
   * Remove um listener previamente adicionado em um objeto e retorna true em caso de sucesso.
   * @param {Object}; o Objeto que recebeu o listener
   * @param {String}; e Nome do evento sem o prefixo "on" (click, mouseover, ...)
   * @param {Object}; f Mesma função que foi atribuida no addEvent
   * @param {Object}; s Escopo em que a função foi adicionada, caso você tenha fornecido um escopo diferente no addEvent, é necessário
   *                   que você passe como parâmetro o mesmo objeto, caso contrário a remoção do evento não será realizada.
   */
  removeEvent: function(o, e, f, s){
    for(var i = (e = o["_on" + e] || []).length; i;)
      if(e[--i] && e[i][0] == f && (s || o) == e[i][1])
        return delete e[i];
    return false;
  }
};

/**
 * FIM CALENDÁRIO
 */



/**
 * Classe de eventos
 * @type Object
 */
function evUtils() {
/**
 * Função de associação de multiplos eventos a objetos
 * addEvent(object: Object, event: String, handler: Function(e: Event): Boolean, [scope: Object = object]): Boolean
 * Adiciona uma função que será disparada quando ocorrer determinado evento no objeto.
 * @param {Object}; o Objeto que receberá o listener
 * @param {String}; e Nome do evento sem o prefixo "on" (click, mouseover, ...)
 * @param {Object}; f Função que será chamada quando o evento ocorrer, será enviado como argumento
 *                   para esta função o objeto de evento, que além das propriedades normais, *sempre* irá conter:
 *                   - target: Objeto que gerou o evento
 *                   - key: Código do caractere em eventos de teclado
 *                   - stopPropagation: Método para evitar a propagação do evento
 *                   - preventDefault: Método para evitar que a ação default ocorra
 *                     O preventDefault pode ser emulado retornando "false" na função
 * @param {Object}; s Escopo (quem o "this" irá referenciar dentro do handler) que será usado quando a função for chamada, o default
 *                   é o objeto no primeiro argumento
 *
 */
  this.addEvent = function(o, e, f, s){
    var r = o[r = "_" + (e = "on" + e)] = o[r] || (o[e] ? [[o[e], o]] : []), a, c, d;
    r[r.length] = [f, s || o], o[e] = function(e){
      try {
        (e = e || event).preventDefault || (e.preventDefault = function(){
          e.returnValue = false;
        });
        e.stopPropagation || (e.stopPropagation = function(){
          e.cancelBubble = true;
        });
        e.target || (e.target = e.srcElement || null);
        e.key = (e.which + 1 || e.keyCode + 1) - 1 || 0;
      } catch (f) {
      };
      for (d = 1, f = r.length; f; r[--f] && (a = r[f][0], o = r[f][1], a.call ? c = a.call(o, e) : (o._ = a, c = o._(e), o._ = null), d &= c !== false));
      return e = null, !!d;
    };
  };

/**
 * Função de desassociação de múltiplos eventos a objetos
 * removeEvent(object: Object, event: String, handler: function(e: Event): Boolean, [scope: Object = object]): Boolean
 * Remove um listener previamente adicionado em um objeto e retorna true em caso de sucesso.
 * @param {Object}; o Objeto que recebeu o listener
 * @param {String}; e Nome do evento sem o prefixo "on" (click, mouseover, ...)
 * @param {Object}; f Mesma função que foi atribuida no addEvent
 * @param {Object}; s Escopo em que a função foi adicionada, caso você tenha fornecido um escopo diferente no addEvent, é necessário
 *                   que você passe como parâmetro o mesmo objeto, caso contrário a remoção do evento não será realizada.
 */
  this.removeEvent = function(o, e, f, s){
    for(var i = (e = o["_on" + e] || []).length; i;)
      if(e[--i] && e[i][0] == f && (s || o) == e[i][1])
        return delete e[i];
    return false;
  };
};

/**
 * pspUtils Class
 * @version 0.1
 * @author Patrick S. de Palma
 * @type Object
 */
function pspUtils() {
  /**
   * Cria um id único
   * @author Patrick S. de Palma
   * @param {Integer}; len Número de caracteres no id. Padrão 6
   * @param {String}; prefix Prefixo para o id. Padrão ""
   * @return String
   */
  this.uniqueID = function(len, prefix) {
    if (len == null) len = 6;
    if (prefix == null) prefix = "";
    var ch = "abcdefghijklmnopqrstuvwxyz0123456789".split("");
    var id = "";
    for (var i=0; i<len; i++) {
      var n = parseInt(Math.random() * ch.length);
      id += ch[n];
    };
    return prefix + "" + id;
  };

  this.trim = function(str) {
    // Expressão regular para tirar espaços no início e no final de uma frase
    var reTRIM = new RegExp('(?:^\\s+|\\s+$)', 'g');
    // ExpReg para tirar espaços extras entre as palavras
    var reEXTRA = new RegExp('\\s{2,}', 'g');
    str = str.replace(reTRIM, '');
    return str.replace(reEXTRA, ' ');
  };

  this.toNum = function(str) {
    var n = 0;
    var s = String(str);
    if (s.indexOf(',') != -1) {
      s = s.replace(RegExp('\\.', 'g'),'');
      s = s.replace(RegExp(',', 'g'),'.');
      n = Number(s);
    } else {
      n = Number(s);
    }
    return n;
  };

/**
 * Verifica uma data<br/>
 * {@link http://jsfromhell.com/geral/is-date}<br/>
 * @version 1.0
 * @author Jonas Raoni Soares Silva
 * @author Patrick S. de Palma (Modificação)
 * @param String Data
 * @return Integer
 * Códigos de retorno<br/>
 * 0 = Data válida<br/>
 * 1 = Formato de data inválido (regular expression falhou ou quantidade de argumentos != 3)<br/>
 * 2 = Dia não está entre 1 e 31<br/>
 * 3 = Mês não está entre 1 e 12<br/>
 * 4 = Nos meses de abril, junho, setembro e novembro não existe o dia 31<br/>
 * 5 = O mês de fevereiro só tem 28 dias<br/>
 * 6 = Anos bissexto, o mês de fevereiro só tem 29 dias
 */
  this.checkdate = function(data){
          var dat = data.split("/");
          var y = parseFloat(dat[2]), m = parseFloat(dat[1]), d = parseFloat(dat[0]);
          if(typeof y == "string" && m instanceof RegExp && d){
              if(!m.test(y)) return 1;
              y = RegExp["$" + d.y], m = RegExp["$" + d.m], d = RegExp["$" + d.d];
          };
          d = Math.abs(d) || 0, m = Math.abs(m) || 0, y = Math.abs(y) || 0;
          return dat.length != 3 ? 1 : d < 1 || d > 31 ? 2 : m < 1 || m > 12 ? 3 : RegExp('4|6|9|11').test(m) && d == 31 ? 4
          : m == 2 && (d > ((y = !(y % 4) && (y % 1e2) || !(y % 4e2)) ? 29 : 28)) ? 5 + !!y : 0;
  };
  this.checkdateResult =  [
                      "",
                      "Formato de data inválido",
                      "Dia não está entre 1 e 31",
                      "Mês não está entre 1 e 12",
                      "Nos meses de abril, junho, setembro e novembro não existe o dia 31",
                      "O mês de fevereiro só tem 28 dias",
                      "Ano bissexto, o mês de fevereiro só tem 29 dias"
  ];

  /**
   * Esconde todos os selects de uma página
   * @version 1.0
   * @author Patrick S. de Palma
   * @param {Boolean}; flag Se FALSE, esconde todos os SELECTS da página
   */
  this.showSelects = function(flag) {
    if (typeof window.showSelectsFlag != 'undefined')
      if (flag == window.showSelectsFlag) return;

    window.showSelectsFlag = flag;

    var s = document.getElementsByTagName('select');
    if (!s) return;
    var len = s.length;

    if (flag) {
      for (var i=0; i<len; i++) {
        s[i].style.visibility = 'visible';
      }
    } else {
      for (var i=0; i<len; i++) {
        s[i].style.visibility = 'hidden';
      }
    }

  };
};

// Busca o submit de um determinado form
function findSubmitBtn(frm) {
  var i = frm.getElementsByTagName("input");
  for (var n=0, l=i.length; n<l; n++)
    if (i[n].type == "submit") return i[n];
  return false;
};
if (!String.trim) {
  String.prototype.trim = function(){
    return this.replace(RegExp('(?:^\\s*|\\s*$)', 'g'), '')
  };
};

/**
** Protótipo "pad"
** @return String
** Modo de uso: stringa.pad(<largura>, <caracter>[, {direcao=<left|right>};{default=left};])
** Exemplos:
** str = "55"; str.pad(7, "0"); saída => 0000055
** str.pad(5, "-", "right");
*/
if (!String.pad) String.prototype.pad = function(slen, ch, direction){
  var str = this;
  while (str.length < slen) {
    if (typeof direction == "undefined") direction = "left";
    str = (direction == "left" ? ch + "" + str : str + "" + ch);
  };
  return str;
};

function animationText(oForm, cancel) {
  if (typeof cancel == 'undefined') cancel = false;
  if (oForm.target != "") return;
  var o = oForm;
  var obj = findSubmitBtn(o);
  if (!cancel) {
    if (o.aniText == null) o.aniText = new String();
    o.aniText = o.aniText.trim(); // Remove espaços
    if (o.aniText.length<3) o.aniText += ".";
    else o.aniText = " ";

    if (obj == false) return; // ERRO, submit não encontrado.
    obj.value = obj.value.replace(RegExp('[\\.\s]+'),'').trim() + o.aniText.pad(3, " ", "right");

    // Encontra o index do form
    var idx = false;
    for (i=0;i<document.forms.length;i++) {
      if (document.forms[i] == o) idx = i;
    };
    // Verifica se o form foi encontrado
    if (idx === false) return;

    if (o.intervalID == null) {
      obj.oldValue = obj.value;
      obj.value = "Aguarde";
      obj.disabled = true;
      o.intervalID = setInterval('animationText(document.forms['+idx+'])',500);
    };
  } else {
    // CANCELA ANIMAÇÃO
    try {
      clearInterval(o.intervalID);
      obj.disabled = false;
      obj.value = obj.oldValue;
    } catch(e) {};
  };
};

/**
 * Retorna campo mais próximo
 * @param {Object}; field Nome do campo atual (atributo "name")
 * @return Object Retorna a referência ao próximo campo. Caso não haja mais campos próximos, retornar o própio campo
 */
function nextField(field){
  var tagFound = null;
  try {
    var tag = field.nextSibling;
    while(tag) {
      if (RegExp('^(?:SELECT|INPUT|TEXTAREA)$').test(tag.nodeName)) {
        tagFound = tag;
        break;
      } else {
        tag = tag.nextSibling;
      }
    }
  } catch(e) {

  }
  if (!tagFound) {
    return field;
  } else {
    return tagFound;
  }
  /*
  for (var i = 0; i < allTags.length; i++) {
    if (RegExp('^(?:SELECT|INPUT|TEXTAREA)$').test(allTags[i].nodeName)) {
      allInputs[allInputs.length] = allTags[i];
    };
  };

  for (var i = 0; i < allInputs.length; i++) {
    if (allInputs[i].name == field) {
      i++;
      if (i >= allInputs.length) i -= 1;
      while (i < allInputs.length) {
        if (!allInputs[i].disabled && !allInputs[i].readOnly && allInputs[i].type != 'hidden') {
          return allInputs[i];
        } else {
          i++;
        };
      };
    };
  };*/
  // Caso o próprio campo não tenha sido encontrado, retorna o próprio campo.
  //return document.getElementsByName(field)[0];
};

/**
 * Pula automáticamente para o próximo campo
 * @param {Object}; e Referência ao evento do objeto
 */
function autoTab(e){
  var key = document.all ? e.keyCode : e.which;
  if (this.filter && this.filter.maskLen > 0 && (key == 32 || (key >= 48 && key <= 105))) {
    if (this.value.length >= this.filter.maskLen) {
      // Se não tiver name assignado, assigna um
      if (this.name == '') {
        var util = new pspUtils();
        this.name = util.uniqueID();
      };
      var oncomplete = null;
      // Verifica se há alguma função definida para o evento oncomplete
      if (typeof this.oncomplete == 'function') {
        oncomplete = this.oncomplete;
      } else if (this.getAttribute('oncomplete')) {
        oncomplete = new Function(this.getAttribute('oncomplete'));
        this.oncomplete = oncomplete;
      };
      var result = true;
      if (oncomplete) result = oncomplete.apply(this);
      try {
        if (result) nextField(this).focus();
      } catch (er) {
        // Return in case of error
        return;
      };
    };
  };
};

/**
 * True se o campo está marcado como "required"
 * @param {Object}; field
 */
function issetRequired(field) {
  return field.filter && field.filter.active;
};

function addLimit(field, name, value){
  if (getFilter(field, 'limits') === false) {
    addFilter(field, 'limits', {});
  };

  field.filter.limits[name] = value;
};

function getLimit(field, name) {
  if (!field.filter || !field.filter.limits || !field.filter.limits[name]) {
    return false;
  } else {
    return field.filter.limits[name];
  }
};

function addFilter(field, _name, value) {
  if (!field.filter) field.filter = {};
  field.filter[_name] = value;
};

function getFilter(field, _name) {
  if (!field.filter || !field.filter[_name]) {
    return false;
  } else {
    return field.filter[_name];
  };
};

function getLabel(field){
  var labelTags = classfilter.labels();
  for (var i = 0; i < labelTags.length; i++) {
    if (labelTags[i].getAttribute('for') == field.name || (field.id && labelTags[i].getAttribute('for') == field.id)) {
      addFilter(field, 'label', labelTags[i].innerHTML.replace(/(?:\:|<\w+.*\w+>)/g, ''));
      addFilter(field, 'labelTag', labelTags[i]);
      return labelTags[i];
    };
  };
  return false;
};

/**
 * Atualiza parâmetros do campo
 * @param {Object} input ID ou objeto referente ao campo
 */
function updateFilter(input) {
  classfilter.scan.label(input);
  classfilter.scan.title(input);
  var p = new pspUtils();

  if (input.filter && input.filter.active == false) {
    classfilter.unmarkField(input);
  } else if (input.filter && input.filter.active == true) {
    classfilter.markField(input);
  };

  var blur = function(){
    eAccept(this)
  };

  var r = new Restrict(input.form);
  var classes = input.className.split(" ");
  if (classes.length == 0) return;

  for (var j = 0, l1 = classes.length; j < l1; j++) {
    var f = classes[j].toLowerCase();
    if (f in _filtro) {
      if (typeof _filtro[f].funcao == "function") {
        _filtro[f].funcao(input);
        if (_filtro[f].maxlen) {
          input.maxLength = _filtro[f].maxlen;
        };
      } else {
        // Cria um id automáticamente
        if (input.id == "") {
          input.id = p.uniqueID(6, input.type);
        }
        r.input[input.id] = input;
        r.field[input.name]     = _filtro[f].filtro;
        r.mask[input.name]      = _filtro[f].mascara;
        if (input.filter && !input.filter.blur) {
          window.evento.addEvent(input, "blur", blur);
          addFilter(input, 'blur', true);
        };
        if (input.filter && !input.filter.keyup) {
          window.evento.addEvent(input, "keyup", autoTab);
          addFilter(input, 'keyup', true);
        };
      };
      var desc;
      if (desc = _filtro[f].desc) input.title = desc;
    };
    if (f in _verifica) {
      addFilter(input, 're', _verifica[f].re);
      addFilter(input, 'msg', _verifica[f].msg);
      addFilter(input, 'funcao', _verifica[f].funcao);
      if (_verifica[f].evento) {
        for (var ev in _verifica[f].evento) {
          window.evento.addEvent(input, ev, _verifica[f].evento[ev]);
        };
      };
      if (_verifica[f].init) {
        for (var n = 0; n < _verifica[f].init.length; n++) {
          try {
            _verifica[f].init[n](input);
          } catch (e) {
          };
        };
      };
      addFilter(input, 'maskLen', typeof _filtro[f].mascara != 'undefined' ? _filtro[f].mascara.length : 0); // Ajuda a determinar o tamanho da máscara para fazer o autoTab
    };
    // Verificação adicional para atributo regex
    var attr_regex = input.getAttribute('regex');
    if (attr_regex) {
      try {
        var regex = eval(attr_regex);
        if ((regex instanceof RegExp) == false) throw "Erro na expressão regular do campo";
        addFilter(input, 're', regex);
        if (input.filter.msg == undefined) addFilter(input, 'msg', 'O campo ' + ((input.filter && input.filter.label) ? input.filter.label : input.name.replace(RegExp('(?:fields|[\\[\]]+)', 'g'), '')) + ' está incorreto.');
      } catch (e) {
        alert('Erro na expressão regular (' + attr_regex + ')\n\nO erro ocorreu no campo ' + (input.name ? input.name : input.id) + '.');
      };
    };
  };
  // FILETYPE
  var filetype = input.getAttribute('filetype');
  if (input.type == 'file' && filetype) {
    var extensions = filetype.split(',');
    addFilter(input, 'extensions', extensions);
  };
  r.onKeyRefuse = eRefuse;
  r.onKeyAccept = eAccept;
  r.start();
};

/**
 * Função de escaneio de campos requeridos
 */
function scanRequired() {
  var f = document.forms;
  var hasRequired = false;

  // LABEL SCAN
  classfilter.scan.label();

  // TITLE SCAN
  classfilter.scan.title();

  for (var i=0;i<f.length;i++) {
    if (!f[i].hasRequired) continue;
    if (f[i].RequiredScanned) {
      continue;
    } else {
      f[i].RequiredScanned = true;
    };

    // Campo obrigatório
    var div = document.createElement('div');
    div.innerHTML = "* Campo obrigatório";
    div.className = "required";
    f[i].insertBefore(div, f[i].firstChild);
  };
};

/**
 * Evidencia o campo com focus
 */
function colorFocus(){
  if (document.forms.length == 0) return;
  var ev = new evUtils();

  var tagsCollection = [
    document.getElementsByTagName('SELECT'),
    document.getElementsByTagName('INPUT'),
    document.getElementsByTagName('TEXTAREA')
  ];

  for (var j = 0; j < tagsCollection.length; j++) {
    var tags = tagsCollection[j];
    for (var i = 0; i < tags.length; i++) {
      var tag = tags[i];
      if (tag.Colorized || tag.disabled || tag.readOnly ||
      (tag.nodeName == 'INPUT' && !RegExp('^(?:text|password|file|checkbox|radio)$', 'i').test(tag.type))) {
        continue;
      } else {
        tag.Colorized = true;
      }

      var focusEvent = verifica_navegador.is_ie6up ? 'focusin' : 'focus';
      ev.addEvent(tag, focusEvent, new Function("this.className += ' focused'"));
      ev.addEvent(tag, 'blur', function(){
        this.className = this.className.replace(/\s*focused/g, '');
        setStyle(this, 'backgroundColor', '');
      });
    };
  };
};

/**
 * Verifica se o objeto está visivel ou escondido
 * @param {Object}; node ID do objeto ou referência ao objeto
 */
function isVisible(node){
  if (typeof node == 'string')
    node = (document.getElementById(node)) ? document.getElementById(node) : document.getElementsByName(node)[0];
  else if (typeof node != 'object') return false;

  var o = node;
  var v = true; // Visible?
  while (o.nodeName != 'BODY' && v == true) {
    if (o.style && (o.style.display == 'none' || o.style.visible == 'hidden')) v = false;
    o = o.parentNode;
  };

  return v;
};

function setStyle(node, attribute, value){
  if (RegExp('^(?:checkbox|radio)$').test(node.type)) {
    var f = node.form;
    if (typeof f != 'undefined') {
      var name = node.name;
    };
    if (typeof name != 'undefined') {
      var nodes = f[name];
    };
    if (nodes && nodes.length) {
      for (var i = 0; i < nodes.length; i++) {
        nodes[i].style[attribute] = value;
      };
    } else {
      node.style[attribute] = value;
    };
  } else {
    node.style[attribute] = value;
  };
};

var eRefuse = new Function("o", "k", "setStyle(o,'backgroundColor','#fdc')");
var eAccept = new Function("o", "k", "setStyle(o,'backgroundColor','')");

/**
 * Busca todos os inputs com a classe definida no array filtro e aplica o filtro correspondente
 */
function substituiClassPorFuncao(){
  // Funções úteis
  var $ = new Function("nodeId", "return document.getElementById(nodeId)");
  var $name = new Function("parent", "name", "return parent.getElementsByname(name)[0]");
  var $tag = new Function("tag", "return document.getElementsByTagName(tag)");
  // Verifica os campos antes de submeter o formulário
  var eventOnSubmit = function(){
    msg = "";
    var c = 0;
    var el = this.elements;
    var espaco = new RegExp('^\\s*$');
    var NUMERO = new RegExp('^[-+0-9\\.,]+$');
    var TEXTO = new RegExp('\\w+');
    var util = new pspUtils();
    showMsg = function(text){
      // Limpa texto
      text = text.replace(RegExp('\\<[\\/]*\\w+[^>]*\\>', 'g'), '');
      msg += (++c) + ") " + text + "\n";
      setStyle(i, "backgroundColor", "#fdc");
    };
    if (el) var lastCheckbox = "";
    // Início varredura de elementos dentro do form
    for (var k = 0, l = el.length; k < l; k++) {
      var fieldOk = true;
      // Atribui o elemento encontrado à variável "i" (i de input)
      var i = el[k];
      if (lastCheckbox == i.name) continue;

      // Caso esteja com propriedades css display:none ou visible:hidden, ou seja, não está visível, desativo ou sem filtro,
      // passa ao próximo elemento
      if (!isVisible(i) || i.disabled || !i.filter) continue;


      // Retira espaços extras do valor do elemento
      if (i && typeof i.value != "undefined" && (i.type != 'file' && i.nodeName != 'TEXTAREA')) i.value = i.value.trim();

      if (i.filter.label || i.name || i.alt) {
        if (i.filter && i.filter.label)
          nomeCampo = i.filter.label.toUpperCase();
        else if (i.alt)
          nomeCampo = i.alt.toUpperCase();
        else
          nomeCampo = i.name.replace(RegExp('(?:fields|[\\[\]])', 'g'), '').toUpperCase();
      } else {
          nomeCampo = "";
      };

      // Verifica a expressão regular ou função de validação ou ainda...se é requerido
      // Simplificação
      var requerido = issetRequired(i) && espaco.test(i.value);
      var noExpRegOk = i.filter.re && !espaco.test(i.value) && !i.filter.re.test(i.value);
      var noFuncOk = !espaco.test(i.value) && (i.filter.funcao && !i.filter.funcao(i.value, i));
      var isCheckbox = i.nodeName == 'INPUT' && RegExp('^(?:checkbox|radio)$').test(i.type);
      var noChecked = isCheckbox && !isChecked(i);

      if (requerido || noExpRegOk || noFuncOk || (issetRequired(i) && isCheckbox && noChecked)) {
        fieldOk = false;
        if (i.filter.msg) {
          showMsg(nomeCampo + ': ' + i.filter.msg);
        } else if (isCheckbox) {
          showMsg(nomeCampo + ': Nenhum item selecionado.');
        } else {
          showMsg(nomeCampo + ': Campo obrigatório.');
        };
      };
      // Memoriza o nome do último checkbox/rádio escaneado para não repetir as verificações no mesmo elemento.
      if (isCheckbox) {
        lastCheckbox = i.name;
        continue;
      };

      if (fieldOk && i.filter.active && i.filter.limits) {
        var limit = i.filter.limits;

        if (limit.check) {
          for(var j in limit.check) {
            limit.check[j].call(i);
          };
        };

        // FILETYPE
        try {
          if (limit.extensions != undefined) {
            var ext = RegExp('\\.([^\\.]+)$').exec(i.value);
            var ext_ok = false;
            if (ext) {
              for (var e = 0; e < limit.extensions.length; e++) {
                if (util.trim(limit.extensions[e]) == ext[1]) ext_ok = true;
              };
            };
            if (i.value != '' && !ext_ok) {
              showMsg('Formato de arquivo incorreto (' + ext[1] + '). Somente arquivos ' + limit.extensions + ' são aceitos.');
            };
          };
        } catch (e) {
        };
      };
    };
    if (msg) {
      alert("ATENÇÃO:\n\n" + msg);
      return false;
    } else if (this.lastOnSubmit != null) {
      var r = this.lastOnSubmit.apply(this);
      if (r) {
        animationText(this);
      };
      return r;
    } else {
      animationText(this);
      return true;
    };
  };

  /**
   * Verifica se um grupo de checkbox ou radio tem pelo menos uma seleção
   * @param {Object}; obj objeto input
   */
  function isChecked(obj){
    var f = obj.form;
    var name = obj.name;
    var nodes = f[name];
    if (typeof nodes == 'undefined') return;
    var checked = false;

    if (typeof nodes.checked != 'undefined' && nodes.checked && !nodes.disabled) {
      checked = true;

    } else {
      for (var i = 0; i < nodes.length; i++) {
        if (nodes[i].checked && !nodes[i].disabled) {
          checked = true;
          break;
        };
      };
    };

    return checked;
  };

  // Objetos
  window.evento = new evUtils(); // Classe de eventos
  var forms = document.forms;
  if (forms.length == 0) return;

  // Pega todos os forms da página
  for (var frm = 0, fl = forms.length; frm < fl; frm++) {
    var form = forms[frm];
    if (form.RequiredInstalled)
      continue;
    else
      form.RequiredInstalled = true;
    var inputs = form.elements;
    if (inputs.length == 0) return;
    //var r = new Restrict(form);

    // Passa por todos os inputs da página
    for (var i = 0, l = inputs.length; i < l; i++) {
      updateFilter(inputs[i]);
    };
    // Salva o evento onSubmit que tenha sido declarado por outro script
    if (form.onsubmit != null) {
      form.lastOnSubmit = form.onsubmit;
    };
    form.onsubmit = null;
    window.evento.addEvent(form, "submit", eventOnSubmit);
  };
};

/**
 * Inicia scripts no onload da página
 */
function __FILTRO_START() {
  var e = new evUtils();
  if (verifica_navegador()) {
    classfilter.loadConfig();
    // Inicializa o swazzCalendar
    e.addEvent(window, "load", swazzCalendar.init);
    //setTimeout("swazzCalendar.prepcalendar('',swazzCalendar.ccm,swazzCalendar.ccy)",0);
    e.addEvent(window, "load", scanRequired);
    e.addEvent(window, "load", substituiClassPorFuncao);
    e.addEvent(window, "load", colorFocus);

  };
};
__FILTRO_START();
