/*********************
 * Para validar os submits das forms.
 **********************/
var formValidators = new Array();

function beforeFormSubmit(index) {
    return formValidators[index].validate();
}



/*********************
 * Classe usada para validar formulários.
 * Adiciona-se validadores a este objecto para ele poder validar a form.
 **********************/
function FormValidator(id) {
    this.id = id;
    this.validators = new Array();
    formValidators[formValidators.length] = this;

    // adiciona a validação desta form ao evento onsubmit
    var index = formValidators.length - 1;
    var form = document.getElementById(this.id);
    var submitFunction = form.onsubmit;
    
    form.onsubmit = function() {
        var previousReturn = true;
        
        if (submitFunction != null && typeof(submitFunction) != 'undefined') {
            previousReturn = submitFunction(); // a função que anteriormente estava associada ao onsubmit
        }
        return previousReturn && beforeFormSubmit(index); // tem de ser passada uma variável (não pode ser passado o atributo da classe senão não dá);
    }
    this.focusFirst();
}

FormValidator.prototype.id;
FormValidator.prototype.validators;

FormValidator.prototype.add = function(validator) {
    this.validators[this.validators.length] = validator;
}

FormValidator.prototype.validate = function() {
    var focused = false;
    var validated = true;
    
    for (var i = 0; i < this.validators.length; i++) {
        var isValid = this.validators[i].isValid();
        
        if (!isValid) {
            if (!focused) {
                this.validators[i].focus();
                focused = true;
            }
            validated = false;
        }
        this.validators[i].displayErrorMessage(!isValid);
    }
    
    return validated;
}

FormValidator.prototype.focusFirst = function() {
    var form = document.getElementById(this.id);
    
    for (var i = 0; i < form.elements.length; i++) {
        var type = form.elements[i].type;
        
        if (type == null || type == 'undefined' || type == '') {
            continue;
        }
        type = type.toLowerCase();
        
        if (type == 'text' || type == 'radio' || type == 'checkbox'
                || type == 'password' || type == 'fileupload' || type == 'select-one'
                || type == 'textarea') {
            form.elements[i].focus();
            
            return;
        }
    }
}



/*********************
 * Classe principal dos validadores.
 * Todos os outros validadores são subclasse desta. Devem chamar o seu construtor.
 **********************/
function Validator(controlID, labelID, errorMessage, required) {
    this.init(controlID, labelID, errorMessage, required);
}

Validator.prototype.controlID;
Validator.prototype.labelID;
Validator.prototype.errorMessage;
Validator.prototype.required;

Validator.prototype.init = function(controlID, labelID, errorMessage, required) {
    this.controlID = controlID;
    this.labelID = labelID;
    this.errorMessage = errorMessage;
    this.required = required;
}

Validator.prototype.getValue = function() {
    return document.getElementById(this.controlID).value;
}

Validator.prototype.focus = function () {
    document.getElementById(this.controlID).focus();
}

Validator.prototype.displayErrorMessage = function (display) {
    var newInnerHTML = document.getElementById(this.labelID).innerHTML;
    newInnerHTML = newInnerHTML.replace(/<em>.*<\/em>/i, '');

    if (display) {
        newInnerHTML += '<em>' + this.errorMessage + '</em>';
    }
    document.getElementById(this.labelID).innerHTML = newInnerHTML;
}

Validator.prototype.isFilled = function () {
    return (this.getValue() != null && this.getValue() != '');
}

Validator.prototype.validates = function () {
    return true;
}

Validator.prototype.isValid = function () {
    if (!this.required && !this.isFilled()) {
        return true;
    }
    
    if (this.isFilled()) {
        return this.validates();
    }
    
    return false;
}



/*********************
 * Classe para validar números.
 **********************/
NumberValidator.prototype = new Validator();
NumberValidator.prototype.constructor = NumberValidator;
NumberValidator.superclass = Validator.prototype;

function NumberValidator(controlID, labelID, errorMessage, required, min, max) {
    NumberValidator.superclass.init.call(this, controlID, labelID, errorMessage, required);
    this.init(min, max);
}

NumberValidator.prototype.min;
NumberValidator.prototype.max;

NumberValidator.prototype.init = function(min, max) {
    this.min = min;
    this.max = max;
}

/* funções static */
NumberValidator.isNumber = function(n) {
    return (parseFloat(n) == n);
}

NumberValidator.isBetween = function(n, min, max) {
    return (NumberValidator.isNumber(n) && n >= min && n <= max);
}

/* funções instância */
NumberValidator.prototype.validates = function() {
    return NumberValidator.isBetween(this.getValue(), this.min, this.max);
}



/*********************
 * Classe para validar strings.
 **********************/
StringValidator.prototype = new Validator();
StringValidator.prototype.constructor = StringValidator;
StringValidator.superclass = Validator.prototype;

function StringValidator(controlID, labelID, errorMessage, required, regexp, minLength, maxLength) {
    StringValidator.superclass.init.call(this, controlID, labelID, errorMessage, required);
    this.init(regexp, minLength, maxLength);
}

StringValidator.prototype.regexp;
StringValidator.prototype.minLength;
StringValidator.prototype.maxLength;

StringValidator.prototype.init = function(regexp, minLength, maxLength) {
    this.regexp = regexp;
    this.minLength = minLength;
    this.maxLength = maxLength;
}

StringValidator.prototype.validates = function() {
    var s = this.getValue();
    var min = this.minLength;
    var max = this.maxLength;
    
    return NumberValidator.isBetween(s.length, min, max) && this.regexp.test(s);
}



/*********************
 * Classe para validar datas (baseadas em 3 selects).
 **********************/
DateValidator.prototype = new Validator();
DateValidator.prototype.constructor = DateValidator;
DateValidator.superclass = Validator.prototype;

function DateValidator(dayID, monthID, yearID, labelID, errorMessage, required) {
    DateValidator.superclass.init.call(this, dayID, labelID, errorMessage, required);
    this.init(dayID, monthID, yearID);
}

DateValidator.prototype.dayID;
DateValidator.prototype.monthID;
DateValidator.prototype.yearID;

DateValidator.prototype.init = function(dayID, monthID, yearID) {
    this.dayID = dayID;
    this.monthID = monthID;
    this.yearID = yearID;
}

/* static */
DateValidator.DAYS_IN_MONTH = new Array(-1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

DateValidator.daysInFebruary = function(year) {
    return ( (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? 29 : 28 );
}

/* instância */
DateValidator.prototype.getValue = function() {
    var daySelect = document.getElementById(this.dayID);
    var monthSelect = document.getElementById(this.monthID);
    var yearSelect = document.getElementById(this.yearID);
    
    var day = daySelect.options[daySelect.selectedIndex].value;
    var month = monthSelect.options[monthSelect.selectedIndex].value;
    var year = yearSelect.options[yearSelect.selectedIndex].value;
    
    return new Array(day, month, year);
}

DateValidator.prototype.validates = function() {
    var values = this.getValue();
    var day = values[0];
    var month = values[1];
    var year = values[2];
    
    return ( (month == 2 && day <= DateValidator.daysInFebruary(year))
        || day <= DateValidator.DAYS_IN_MONTH[month] );
}



/*********************
 * Classe para validar ficheiros.
 **********************/
FileValidator.prototype = new Validator();
FileValidator.prototype.constructor = FileValidator;
FileValidator.superclass = Validator.prototype;

function FileValidator(controlID, labelID, errorMessage, required, validExtensions) {
    FileValidator.superclass.init.call(this, controlID, labelID, errorMessage, required);
    this.init(validExtensions);
}

FileValidator.prototype.validExtensions;

FileValidator.prototype.init = function(validExtensions) {
    this.validExtensions = validExtensions;
}

FileValidator.prototype.validates = function() {
    for (var i = 0; i < this.validExtensions.length; i++) {
        var ext = this.validExtensions[i];
        var regStr = '\\.(' + ext.toLowerCase() + '|' + ext.toUpperCase() + ')';
        var regexp = new RegExp(regStr);
        
        if (regexp.test(this.getValue())) {
            return true;
        }
    }
    
    return false;
}
