function fv_defaultMessages () {
	var m = new Array();

	m['required'] = 'The $FIELD field is required. You cannot leave it blank.';
	m['min-length'] = 'The $FIELD field must contain at least $CONSTRAINT characters. Currently it has $VALUE characters.';
	m['min-selected'] = 'The $FIELD field must have at least $CONSTRAINT items selected. Currently there are $VALUE selected.';
	m['max-length'] = 'The $FIELD field cannot contain more than $CONSTRAINT characters. Currently it contains $VALUE characters.';
	m['max-selected'] = 'The $FIELD field cannot have more than $CONSTRAINT items selected. Currently there are $VALUE selected.';
	m['invalid-date'] = 'The $FIELD field must contain a valid date.';
	m['invalid-number'] = 'The $FIELD field must be a number, without commas.';
	m['invalid-integer'] = 'The $FIELD field must be a whole number.  It cannot contain decimals or fractions.';
	m['invalid-url'] = 'The $FIELD field must contain a valid URL.';
	m['invalid-email'] = 'The $FIELD field must contain a valid e-mail address.';
	m['invalid-screenname']='The $FIELD field should not contain @ symbols';
	m['invalid-pattern'] = 'The $FIELD field is not formatted properly.';
	m['lower-bound'] = 'The number entered in $FIELD cannot be less than $CONSTRAINT.';
	m['upper-bound'] = 'The number entered in $FIELD cannot be greater than $CONSTRAINT.';
	m['confirm'] = 'The value entered in the confirmation field for $FIELD does not match the value entered in the $FIELD field.';
	m['submitted'] = 'This form is already being processed. It cannot be submitted again.';
	m['default'] = 'The value entered for $FIELD is not allowed.';

	return m;
}


function fv_getMessage (name) {
	var msg = null;

	if (this.messages != null) {
		msg = this.messages[name];
	}

	if (msg == null || msg == '') {
		msg = fv_defaultMessages()[name];
	}

	return msg;
}


function fv_trim (value) {
	if (value == null) {
		return '';
	}

	while (value.length > 0) {
		var c = value.charAt(0);
		if (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
			value = value.substring(1);
		} else {
			break;
		}
	}

	while (value.length > 0) {
		var c = value.charAt(value.length - 1);
		if (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
			value = value.substring(0, value.length - 1);
		} else {
			break;
		}
	}

	return value;
}



function fv_getFieldValues (theForm, fieldName) {
	var values = new Array();
	var index = 0;

	for (var i=0; i<theForm.length; i++) {
		with (theForm.elements[i]) {
			if (name == fieldName) {
				if ((type == 'text' || type == 'textarea' || type == 'password' || type == 'hidden' || type == 'file') && value != '') {
					values[index] = value;
					index++;
				} else if ((type == 'checkbox' || type == 'radio') && checked) {
					values[index] += value;
					index++;
				} else if (type == 'select' || type == 'select-one' || type == 'select-multiple') {
					if (selectedIndex >= 0 && options[selectedIndex].value !=  '') {
						values[index] = options[selectedIndex].value;
						index++;
					}
				}
			}
		}
	}

	return values;
}


function fv_isMultiSelect (theForm, fieldName) {
	for (var i=0; i<theForm.length; i++) {
		with (theForm.elements[i]) {
			if (name == fieldName) {
				if (type == 'checkbox' || type == 'select' || type == 'select-multiple') {
				    return true;
				}
			}
		}
	}

	return false;
}


function fv_isNumber (value) {
	value = fv_trim(value);
	if (value == '') {
		return true;
	}

	return !isNaN(value);
}


function fv_isInteger (value) {
	value = fv_trim(value);
	if (value == '') {
		return true;
	}

	if (value.indexOf('.') >= 0) {
		return false;
	} else {
		return !isNaN(value);
	}
}


function fv_monthNames (language) {
	if (language == 'es') {
		return new Array('Enero', 'Febrero', 'Marcha', 'Abril', 'Pueda', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre');
	} else {
		// Default is English
		return new Array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');
	}
}


function fv_monthAbbr (language) {
	if (language == 'es') {
		return new Array('Ene', 'Feb', 'Mar', 'Abr', 'Pue', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic');
	} else {
		// Default is English
		return new Array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
	}
}


function fv_daysInMonth (month, year) {
	var days = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

	if (month == 2 && (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))) {
		return days[1] + 1;
	} else {
		return days[month - 1];
	}
}


function fv_dayNames (language) {
	if (language == 'es') {
		return new Array('Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado');
	} else {
		// Default is English
		return new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday');
	}
}


function fv_dayAbbr (language) {
	if (language == 'es') {
		return new Array('Dom', 'Lun', 'Mar', 'Mie', 'Jue', 'Vie', 'Sab');
	} else {
		// Default is English
		return new Array('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
	}
}


function fv_substituteMonth (date, language) {
	// Substitute month names with numbers
	var months = fv_monthNames(language);
	for (var i=0; i<months.length; i++) {
		var j = date.indexOf(months[i].toUpperCase());
		if (j >= 0) {
			date = date.substring(0, j) + '(' + (i + 1) + ') ' + date.substring(j + months[i].length);
			return date;
		}
	}

	// Substitute month abbreviations with numbers
	months = fv_monthAbbr(language);
	for (var i=0; i<months.length; i++) {
		var j = date.indexOf(months[i].toUpperCase());
		if (j >= 0) {
			var length = months[i].length;
			if (date.charAt(j + length) == '.') {
				length += 1;
			}
			date = date.substring(0, j) + '(' + (i + 1) + ') ' + date.substring(j + length);
			return date;
		}
	}

	if (language != null && language != '' && language != 'en') {
		date = fv_substituteMonth(date);
	}

	return date;
}


function fv_removeDay (date, language) {
	// Remove day names from a date so they don't interfere with formatting.
	var days = fv_dayNames(language);
	for (var i=0; i<days.length; i++) {
		var j = date.indexOf(days[i].toUpperCase());
		if (j >= 0) {
			date = date.substring(0, j) + date.substring(j + days[i].length);
			return date;
		}
	}

	// Remove day abbreviations.
	days = fv_dayAbbr(language);
	for (var i=0; i<days.length; i++) {
		var j = date.indexOf(days[i].toUpperCase());
		if (j >= 0) {
			var length = days[i].length;
			if (date.charAt(j + length) == '.') {
				length += 1;
			}
			date = date.substring(0, j) + date.substring(j + length);
			return date;
		}
	}

	if (language != null && language != '' && language != 'en') {
		date = fv_removeDay(date);
	}

	return date;
}


function fv_removeNonDateChars (date) {
	var normalized = '';
	var lastChar = '';

	// Replace invalid characters
	for (var i=0; i<date.length; i++) {
		var c = date.charAt(i);
		if (c >= '0' && c <= '9') {
			if (lastChar >= 'A' && lastChar <= 'Z') {
				normalized += ' ';
			}
			normalized += c;
		} else if (c >= 'A' && c <= 'Z') {
			if (lastChar >= '0' && lastChar <= '9') {
				normalized += ' ';
			}
			normalized += c;
		} else if (c == ':' || c == '(' || c == ')' || c == '+') {
			normalized += c;
		} else if (c == '.') {
			if (lastChar >= '0' && lastChar <= '9' && lastChar != ' ') {
				c = ' ';
				normalized += c;
			}
		} else if (c == '-') {
			if (lastChar == 'T') {
				normalized += c;
			} else if (lastChar != ' ') {
				c = ' ';
				normalized += c;
			}
		} else if (c == ',' || c == '/' || c == ' ') {
			if (lastChar != ' ') {
				c = ' ';
				normalized += c;
			}
		} else {
			return null;
		}

		lastChar = c;
	}

	return normalized;
}


function fv_parseDate (normalizedDate, language, truncateTime) {
	var dateArray = normalizedDate.split(' ');

	var hour = null;
	var minute = null;
	var meridian = null;
	var timezone = null;
	var month = null;
	var day = null;
	var year = null;

	// Find time part
	for (var i=0; i<dateArray.length; i++) {
		var f = dateArray[i];

		if (f.indexOf(':') >= 0) {
			if (hour != null) {
				return null;
			}

			var time = f.split(':');
			hour = time[0];
			if (f.length > 1) {
				minute = time[1];
			} else {
				minute = '0';
			}

			dateArray[i] = '';
		} else if (f == 'a.m.' || f == 'p.m.') {
			if (meridian != null) {
				return null;
			}

			meridian = f;

			if (time == null && i > 0) {
				hour = dateArray[i-1];
				minute = '0';
				dateArray[i-1] = '';
			}

			dateArray[i] = '';
		} else if (f.charAt(0) >= 'A' && f.charAt(0) < 'Z') {
			if (timezone != null) {
				return null;
			}

			timezone = f;

			if (time == null && i > 0) {
				hour = dateArray[i-1];
				minute = '0';
				dateArray[i-1] = '';
			}

			dateArray[i] = '';
		}
	}

	if (hour != null) {
		var n = new Number(hour);
		if (n < 0 || n > 23) {
			return null;
		}
	}

	if (minute != null) {
		var n = new Number(minute);
		if (n < 0 || n > 59) {
			return null;
		}
		if (minute.length == 1) {
			minute = '0' + minute;
		} else if (minute.length != 2) {
			return null;
		}
	}

	// Find date part
	for (var i=0; i<dateArray.length; i++) {
		var f = dateArray[i];

		if (f.charAt(0) == '(' && f.charAt(f.length - 1) == ')') {
			if (month != null) {
				if (day == null) {
					day = month;
				} else {
					return null;
				}
			}

			month = f.substring(1, f.length - 1);
		} else if (f != '') {
			if (month == null) {
				month = f;
			} else if (day == null) {
				day = f;
			} else if (year == null) {
				year = f;
			} else {
				return null;
			}
		}
	}


	if (month == null || day == null) {
		return null;
	}

	// Default year to this year;
	if (year == null) {
		var y = new Date();
		year = y.getYear();
	} else {
		year = new Number(year);
	}

	if (year < 50) {
		year += 2000;
	} else if (year < 200) {
		year += 1900;
	}

	var m = new Number(month);
	if (m < 1 || m > 12) {
		return null;
	}
	var d = new Number(day);
	if (d < 1 || d > fv_daysInMonth(m, year)) {
		return null;
	}

	var time = '';
	if (hour != null) {
		time += ' ' + hour + ':' + minute;
		if (meridian != null) {
			time += ' ' + meridian;
		}
		if (timezone != null) {
			time += ' ' + timezone;
		}
	}

	var standardizedDate = day + ' ' + fv_monthAbbr()[m - 1] + ' ' + year + time;
	var validDate = new Date(standardizedDate);
	if (validDate = Number.NaN) {
		return null;
	}

	month = fv_monthNames()[m - 1];

	return month + ' ' + d + ', ' + year + (truncateTime ? '' : time);
}


function fv_normalizeDate (date, language, truncateTime) {
	date = date.toUpperCase();

	date = fv_substituteMonth(date, language);
	date = fv_removeDay(date, language);
	date = fv_removeNonDateChars(date);
	date = fv_parseDate(date, language, truncateTime);

	return date;
}


function fv_isDate (value, language) {
    var RegExPattern = /^(?=\d)(?:(?:(?:(?:(?:0?[13578]|1[02])(\/|\.)31)\1|(?:(?:0?[1,3-9]|1[0-2])(\/|\.)(?:29|30)\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})|(?:0?2(\/|\.)29\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))|(?:(?:0?[1-9])|(?:1[0-2]))(\/|\.)(?:0?[1-9]|1\d|2[0-8])\4(?:(?:1[6-9]|[2-9]\d)?\d{2}))($|\ (?=\d)))?(((0?[1-9]|1[012])(:[0-5]\d){0,2}(\ [AP]M))|([01]\d|2[0-3])(:[0-5]\d){1,2})?|(January|February|March|April|May|June|July|August|September|October|November|December)\s{1}\d{1,2}[,]\s{1}\d{4}$/;
    if (!(value.match(RegExPattern)) && (value!='')) {
    	return false;
    }

    value = fv_trim(value);
	if (value == '') {
		return true;
	}
	return (fv_normalizeDate(value, language, false) != null);
}

function fv_isURL (value) {
	value = fv_trim(value);
	if (value == '') {
		return true;
	}
	return (value.indexOf(':') > 0);
}

function fv_invalidScreenName (value) {
	value = fv_trim(value);
	if (value == '') {
		return true;
	}
	return (value.indexOf('@') > 0);
}

function fv_isEmail (value) {
	if (RegExp) {
	    var word = "([a-z0-9!#$%&'*+/=?^_`~{}|.-]+)";
	    var pattern = '^' + word + '@' + word + '\\.' + word + '$';
		var exp = new RegExp(pattern, 'i');
		if (exp.exec(value) == null) {
			return false;
		}

	}

	return true;
}


function fv_validationError (fieldValidator, formValidator, errorName, constraint, value) {
	var f = fieldValidator.form[fieldValidator.name];
	if (f.focus) {
		f.focus();
	}
	if (f.select) {
		f.select();
	}

	var fname = fieldValidator.displayname;

	alert(fv_errorMessage(formValidator, errorName, fname, constraint, value));
}


function fv_errorMessage (formValidator, type, field, constraint, value) {
	var msg = formValidator.getMessage(type);

	msg = fv_substituteErrorToken(msg, '$FIELD', field);
	msg = fv_substituteErrorToken(msg, '$CONSTRAINT', constraint);
	msg = fv_substituteErrorToken(msg, '$VALUE', value);

	return msg;
}


function fv_substituteErrorToken (message, token, value) {
	var i = 0;
	while (i < message.length) {
		i = message.indexOf(token);
		if (i < 0) {
			break;
		}
		message = message.substring(0, i) + value + message.substring(i + token.length);
		i += value.length;
	}
	return message;
}


function fv_isValidField (formValidator) {
	var values = fv_getFieldValues(this.form, this.name);

	if (values.length == 0) {
		if (this.required) {
			fv_validationError(this, formValidator, 'required');
			return false;
		} else {
			return true;
		}
	}

	if (this.minlength != null) {
		if (values.length == 1 && !fv_isMultiSelect(this.form, this.name)) {
			if (values[0].length < this.minlength) {
				fv_validationError(this, formValidator, 'min-length', this.minlength, values[0].length);
				return false;
			}
		} else if (values.length < this.minlength) {
			fv_validationError(this, formValidator, 'min-selected', this.minlength, values.length);
			return false;
		}
	}

	if (this.maxlength != null) {
	    if (values.length == 1 && !fv_isMultiSelect(this.form, this.name)) {
			if (values[0].length > this.maxlength) {
				fv_validationError(this, formValidator, 'max-length', this.maxlength, values[0].length);
				return false;
			}
		} else if (values.length > this.maxlength) {
			fv_validationError(this, formValidator, 'max-selected', this.maxlength, values.length);
			return false;
		}
	}

	if (this.confirmfield != null) {
		if (values.length > 0 && this.form[this.confirmfield]) {
			if (values[0] != this.form[this.confirmfield].value) {
				fv_validationError(this, formValidator, 'confirm', null, null);
				return false;
			}
		}
	}

	if (this.datatype != null) {
		for (var i=0; i<values.length; i++) {
			if (!this.checkDataType(formValidator, this.datatype, values[i])) {
				return false;
			}
		}
	}

	return true;
}


function fv_checkDataType (formValidator, datatype, val) {
	var type = fv_trim(datatype.toLowerCase());

	var range = null;
	var j = type.indexOf(' ');
	if (j > 0) {
		type = type.substring(0, j);
		if (type == 'number' || type == 'integer') {
			range = datatype.substring(j + 1).split('-');
		}
	}

	switch (type) {
		case 'date' :
			if (!fv_isDate(val, formValidator.language)) {
				fv_validationError(this, formValidator, 'invalid-date');
				return false;
			}
			break;
		case 'number' :
			if (!fv_isNumber(val)) {
				fv_validationError(this, formValidator, 'invalid-number');
				return false;
			}
			break;
		case 'integer' :
			if (!fv_isInteger(val)) {
				if (!fv_isNumber(val)) {
					fv_validationError(this, formValidator, 'invalid-number');
				} else {
					fv_validationError(this, formValidator, 'invalid-integer');
				}
				return false;
			}
			break;
		case 'url' :
			if (!fv_isURL(val)) {
				fv_validationError(this, formValidator, 'invalid-url');
				return false;
			}
			break;
		case 'email' :
			if (!fv_isEmail(val)) {
				fv_validationError(this, formValidator, 'invalid-email');
				return false;
			}
			break;
        case 'screenname' :
            if (fv_invalidScreenName(val)) {
                fv_validationError(this, formValidator, 'invalid-screenname');
                return false;
            }
            break;
		default :
			var k = datatype.lastIndexOf('/');
			if (type.charAt(0) == '/' && k > 0) {
				if (RegExp) {
					var pattern = datatype.substring(1, k);
					var modifiers = '';
					var extraDataType = null;
					if (k + 1 < datatype.length) {
						modifiers = datatype.substring(k + 1);
					}

					for (var c=0; c<modifiers.length; c++) {
						var c1 = modifiers.charAt(c);
						if (c1 == ' ') {
							extraDataType = modifiers.substring(c + 1);
							modifiers = modifiers.substring(0, c);
						} else if (c1 != 'i' && c1 != 'g') {
							alert('WARNING: illegal regexp modifiers on field ' + this.name + ': ' + modifiers);
							return false;
						}
					}

					var exp = new RegExp(pattern, modifiers);
					if (exp.exec(val) == null) {
						if (extraDataType != null) {
							return this.checkDataType(formValidator, extraDataType, val);
						} else {
							fv_validationError(this, formValidator, 'invalid-pattern');
							return false;
						}
					}
				}
			} else {
				alert('WARNING: cannot validate data type ' + datatype + ' on field ' + this.name + '.');
				return false;
			}
	}

	if (range != null) {
		var lower = range[0];
		if (lower == '') {
			lower = null;
		} else if (isNaN(lower)) {
			alert('WARNING: The value "' + lower + '" in the ' + this.name + ' field validator is not a valid number');
			return false
		}

		var upper = range[1];
		if (upper == '') {
			upper = null;
		} else if (isNaN(upper)) {
			alert('WARNING: The value "' + upper + '" in the ' + this.name + ' field validator is not a valid number');
			return false
		}

		val = new Number(val);
		if (lower != null && val < lower) {
			fv_validationError(this, formValidator, 'lower-bound', lower);
			return false;
		}

		if (upper != null && val > upper) {
			fv_validationError(this, formValidator, 'upper-bound', upper);
			return false;
		}
	}


	return true;
}


function fv_validateFields(name)
{
    if (name == null) {
        for (var i in this.validators) {
            //added check for null and method check for browser compatibility

            if (this.validators[i] && this.validators[i].valid) {
                var fieldObj = this.validators[i];
                var formField = fieldObj.form[i];
                //checking for disabled form fields and skipping them
                if (formField) {
                    if ('disabled' in formField && formField.disabled == true) {
                        return true;
                    }
                }

                if (!this.validators[i].valid(this)) {
                    return false;
                }
            } else {
                if (this.validators[i] && this.validators[i].valid) {
                    if (!this.validators[i].valid(this)) {
                        return false;
                    }
                }
            }
        }

        return true;
    } else {
        var validator = this.validators[name];
        if (validator != null) {
            return validator.valid(this);
        } else {
            if (this.form[name] != null) {
                alert('Field "' + name + '" cannot be validated because it does not exist on the form.');
                return false;
            } else {
                return true;
            }
        }
    }
}


function fv_addFieldValidator (name, required, minlength, maxlength, datatype, displayname, confirmfield) {
	var validator = this.validators[name];
	if (validator == null) {
		validator = new FieldValidator(name, this.form);
		this.validators[name] = validator;
	}

	if (required != null) {
		validator.required = required;
	}

	if (minlength != null) {
		validator.minlength = minlength;
	}

	if (maxlength != null) {
		validator.maxlength = maxlength;
	}

	if (datatype != null) {
		validator.datatype = datatype;
	}

	if (displayname != null) {
		validator.displayname = displayname;
	}

	if (confirmfield != null) {
		validator.confirmfield = confirmfield;
	}

	return validator;
}


function fv_getFieldValidator (field) {
	var name = field;
	if (typeof name == 'object') {
		name = name.name;
	}
	return this.validators[name];
}


function fv_execOnChange (field) {
	this.modified = true;

	if (this.onchange != null) {
		return eval(this.onchange + '(this.form, field)');
	}

}


function fv_execOnSubmit () {
	if (this.submitted) {
		alert(fv_getMessage('submitted'));
		return false;
	}

	if (this.validate()) {
		this.modified = false;
		this.submitted = true;
		return true;
	} else {
		return false;
	}
}


function fv_execOnNoSubmit () {
	if (this.modified && this.onnosubmit != null) {
		return eval(this.onnosubmit + '(this.form)');
	}

}


function fv_formatDate (value, truncateTime) {
	if (value == null) {
		return null;
	}

	if (typeof value == 'object' && value.toLocaleString) {
		value = value.toLocaleString();
	}

	return fv_normalizeDate(value, this.language, truncateTime);
}


function fv_getFieldTitle (name) {
	var field = this.validators[name];
	if (field != null) {
		return field.displayname;
	} else {
		return name;
	}
}

function FormValidator (form) {
	// properties
	this.form = form;
	this.validators = new Array();
	this.modified = false;
	this.onchange = null;
	this.onnosubmit = null;
	this.submitted = false;
	this.language = 'en';
	this.messages = null;

	// functions
	this.validate = fv_validateFields;
	this.add = fv_addFieldValidator;
	this.changed = fv_execOnChange;
	this.nosubmit = fv_execOnNoSubmit;
	this.field = fv_getFieldValidator;
	this.submit = fv_execOnSubmit;
	this.getMessage = fv_getMessage;
	this.formatDate = fv_formatDate;
	this.getFieldTitle = fv_getFieldTitle;
}


function FieldValidator (name, form) {
	// properties
	this.form = form;
	this.name = name;
	this.required = false;
	this.minlength = null;
	this.maxlength = null;
	this.datatype = null;
	this.confirmfield = null;
	this.displayname = name;

	// functions
	this.valid = fv_isValidField;
	this.checkDataType = fv_checkDataType;
}
