function getPosicaoElemento(elemID) {
	var offsetTrail = document.getElementById(elemID);
	var offsetLeft = 0;
	var offsetTop = 0;
	while (offsetTrail) {
		offsetLeft += offsetTrail.offsetLeft;
		offsetTop += offsetTrail.offsetTop;
		offsetTrail = offsetTrail.offsetParent;
	}
	if (navigator.userAgent.indexOf("Mac") != -1 && 
		typeof document.body.leftMargin != "undefined") {
		offsetLeft += document.body.leftMargin;
		offsetTop += document.body.topMargin;
	}
	return {left:offsetLeft, top:offsetTop};
}

function txtBoxFormat(strField, sMask, evtKeyPress) {
	var i, nCount, sValue, fldLen, mskLen,bolMask, sCod, nTecla;
	
	if (document.all) { // Internet Explorer
		 nTecla = evtKeyPress.keyCode;
	} else if (document.layers) { // Nestcape
		 nTecla = evtKeyPress.which;
	} else {
		 nTecla = evtKeyPress.which;
		 if (nTecla == 8) {
			  return true;
		 }
	}
	if (!nTecla) return true;

	sValue = strField.value;
	// Limpa todos os caracteres de formatação que
	// já estiverem no campo.
	while (sValue.indexOf("-")>=0){
		sValue = sValue.toString().replace( "-", "" );
	}
	while (sValue.indexOf(".")>=0){
		sValue = sValue.toString().replace( ".", "" );
	}
	while (sValue.indexOf(",")>=0){
		sValue = sValue.toString().replace( ",", "" );
	}
	while (sValue.indexOf("/")>=0){
		sValue = sValue.toString().replace( "/", "" );
	}
	while (sValue.indexOf("(")>=0){
		sValue = sValue.toString().replace( "(", "" );
	}
	while (sValue.indexOf(")")>=0){
		sValue = sValue.toString().replace( ")", "" );
	}
	while (sValue.indexOf(":")>=0){
		sValue = sValue.toString().replace( ":", "" );
	}
	//sValue = sValue.toString().replace( " ", "" );
	//sValue = sValue.toString().replace( " ", "" );
	fldLen = sValue.length;
	mskLen = sMask.length;
	
	i = 0;
	nCount = 0;
	sCod = "";
	mskLen = fldLen;
	
	while (i <= mskLen) {
		bolMask = ((sMask.charAt(i) == "-") || (sMask.charAt(i) == ":") || (sMask.charAt(i) == ".") || (sMask.charAt(i) == "/"));
		bolMask = bolMask || ((sMask.charAt(i) == "(") || (sMask.charAt(i) == ")") || (sMask.charAt(i) == " "));
		
		if (bolMask) {
			 sCod += sMask.charAt(i);
			 mskLen++;
		} else {
			 sCod += sValue.charAt(nCount);
			 nCount++;
		}
		i++;
	}
	
	strField.value = sCod;
	if(sMask.charAt(0) == "C"){
		return ((nTecla == 8) || (nTecla == 32) || (nTecla > 64) && (nTecla < 91) || (nTecla > 96) && (nTecla < 123) || (nTecla > 191) && (nTecla < 256));// apenas backspace, espaço, letras de a-z e A-Z com e sem acento
	} else {
		mask = sMask.charAt(i-1);
		switch(mask){
			case "9":
				return ((nTecla > 47) && (nTecla < 58));// números de 0 a 9
			break;
	
			default:
				return true;
			break;
		}
	}
}

/**
 * Gerenciador de formulários
 * 
 * @author {Eduardo Schmidt}
 * @param {Object/String} form Formulário que será gerenciado
 * @version {1.2}
 */
function FormValidator(form)
{
	var form = (typeof form == 'object') ? (form) : (document.forms[form]);
	form.formManager = this;
	
	this.frm = form;

	this.isValidated = false;
	this.invalidFields = new Array(0);

	this.IMAGE = 'styles/imagens/hint/bullet-cadastro.gif';
	this.IMAGE_POSITION = 'right';

	this.validators = {
		vtypes: [{
			validator: 'date',
			message: 'Preencha uma data válida no formato dd/mm/AAAA!',
			callback: function(value) {
				var regex = /^((([0][1-9]|[12][\d])|[3][01])[-/]([0][13578]|[1][02])[-/][1-9]\d\d\d)|((([0][1-9]|[12][\d])|[3][0])[-/]([0][13456789]|[1][012])[-/][1-9]\d\d\d)|(([0][1-9]|[12][\d])[-/][0][2][-/][1-9]\d([02468][048]|[13579][26]))|(([0][1-9]|[12][0-8])[-/][0][2][-/][1-9]\d\d\d)$/;
				return regex.test(value); 
			}
		}, {
			validator: 'email',
			message: 'Preencha um e-mail válido no formato usuario@dominio.com!',
			callback: function(value) {
				var regex = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/;
				return regex.test(value);
			}
		}, {
			validator: 'fone',
			message: 'Preencha corretamento o telefone no formato (DD)FFFF-FFFF!',
			callback: function(value) {
				var regex = /^\((\d{2})\)+(\d{4})+-(\d{4})$/;
				return regex.test(value);
			}
		}, {
			validator: 'cep',
			message: 'Preencha um CEP válido no formato XXXXX-XXX!',
			callback: function(value) {
				var regex = /^(\d{5})+-(\d{3})$/;
				return regex.test(value);
			}
		}, {
			/*validator: 'senha',
			message: 'A senha deve ter entre 5 e 15 dígitos, não conter caracteres especiais (*,.^_@+-) e conter pelo menos 1 número e 1 letra',
			callback: function(value) {
				var regex = /^(?!^[0-9]*$)(?!^[a-zA-Z]*$)^([a-zA-Z0-9]{5,15})$/;
				return regex.test(value);
			}*/
			validator: 'senha',
			message: 'A senha deve ter entre 6 e 15 dígitos',
			callback: function(value) {
				return (value.length >= 6 && value.length <= 15);
			}
		}, {
			validator: 'cpf',
			message: 'Preencha um CPF válido no formato XXX.XXX.XXX-XX',
			callback: function(value) {
				var regex = /^\d{3}.?\d{3}.?\d{3}-?\d{2}$/;
				return regex.test(value);
			}
		}, {
			validator: 'cnpj',
			message: 'Preencha um CNPJ válido no formato XX.XXX.XXX/XXXX-XX',
			callback: function(value) {
				var regex = /^\d{2}\.\d{3}\.\d{3}\/\d{4}\-\d{2}$/;
				return regex.test(value);
			}
		}],

		add: function(validator) {
			if (validator.validator && validator.message && validator.callback) {
				this.vtypes.push(validator);
			} else {
				window.alert('Invalid validator object! I need properties: validator, message and callback. See the doc for more.');
			}
		},
	
		get: function(validator) {
			for (var i=0; i<this.vtypes.length; i++) {
				if (this.vtypes[i].validator == validator) {
					return this.vtypes[i];
					break;
				}
			}
			return false;
		}
	};

	getValue = function(field) {
		switch (field.type.toLowerCase()) {
			case 'text':
			case 'textarea':
			case 'password':
			case 'hidden':
			case 'file':
			case 'select-one':
			case 'select-multiple':
				return field.value;
				break;
			case 'radio':
			var name = field.name;
				if (form.elements[name].length) {
					for (var i=0; i<form.elements[name].length; i++) {
						if (form.elements[name][i].checked) {
							return form.elements[name][i].value;
						}
					}
				} else {
					return form.elements[name].checked ? 'true' : '';
				}
				return '';
				break;
			case 'checkbox':
				return field.checked ? field.value : '';
				break;
			default: return ''; //alert(field.type);
		}
	};

	this.markField = function(field, image, message, pos) {
		field.className = field.className + ' fv-invalid-field';
		var alertEl = document.getElementById('alert_'+field.name); 
		if (alertEl === null) {
			if(image != '') {
				var img = document.createElement('img');
				img.setAttribute('id', 'alert_'+field.name);
				img.setAttribute('src', image);
				img.setAttribute('class', 'alert-validator');
				img.setAttribute('title', message);
				img.style.marginLeft = img.style.marginRight = '0px';

				img.onclick = function(event) { field.focus(); };
				img.onmouseover = function(event) {
					var hintEl = document.getElementById('hint_'+field.name);
					if (hintEl === null) {
						var hint = document.createElement('div');
						hint.setAttribute('id', 'hint_'+field.name);
						hint.innerHTML = '\
						<table border="0" cellpadding="0" cellspacing="0" class="alert">\
						<tbody>\
						<tr>\
							<td class="hint_ne"></td>\
							<td class="hint_n"></td>\
							<td class="hint_nw"></td>\
						</tr><tr>\
							<td class="hint_e"></td>\
							<td class="hint"><span>'+ img.getAttribute('title') +'</span></td>\
							<td class="hint_w"></td>\
						</tr><tr>\
							<td class="hint_se"></td>\
							<td class="hint_s"></td>\
							<td class="hint_sw"></td>\
						</tr><tr>\
							<td class="'+ (pos == 'left' ? 'hint_bl' : 'hint_br') +'" colspan="3"></td>\
						</tr>\
						</tbody>\
						</table>';
						hint.style.position = 'absolute';
						hint.style.zIndex = 10000;
						document.body.appendChild(hint);
						var imgPos = getPosicaoElemento(img.getAttribute('id'));
						hint.style.top = (imgPos.top - hint.offsetHeight - 5) +'px';
						if (pos == 'left') {
							hint.style.left = imgPos.left +'px';
						} else {
							hint.style.left = (imgPos.left - hint.offsetWidth + 20) +'px';
						}
						img.setAttribute('title', '');
					} else {
						hintEl.parentNode.removeChild(hintEl);
					}
				};
				img.onmouseout = function(event) {
					var hint = document.getElementById('hint_'+field.name);
					img.setAttribute('title', hint.getElementsByTagName('span')[0].innerHTML);
					if (hint) document.body.removeChild(hint);
				};
	
				if (pos == 'left') {
					field.parentNode.insertBefore(img, field);
				} else {
					field.parentNode.appendChild(img);
				}
			}
		} else {
			if (alertEl.title != message) alertEl.title = message;
		}
	};

	this.unmarkField = function(field, message) {
		var classes = field.className.split(' ');
		var nClasses = new Array();
		for (var i=0; i<classes.length; i++) {
			if (classes[i] != 'fv-invalid-field') nClasses.push(classes[i]);
		}
		field.className = nClasses.join(' ');
		if (document.getElementById('alert_'+field.name) !== null) {
			field.parentNode.removeChild(document.getElementById('alert_'+field.name));
			var hint = document.getElementById('hint_'+field.name);
			if (hint) document.body.removeChild(hint);
		}
	};

	/**
	 * Valida um campo específico do formulário
	 * 
	 * @param {Object/String} field Campo que se deseja validar
	 * @return {bool}
	 */
	this.validateField = function(field) {
		if (typeof field != 'object') field = document.getElementById(field);
		try {
			// preenchimento obrigatório
			if ((field.getAttribute('mandatory') == 'true') && !getValue(field).replace(/^\s*/, "").replace(/\s*$/, "").length) {
				throw 'Campo de preenchimento obrigatório';
			}
			// validador de conteúdo
			var valName = field.getAttribute('validator');
			if ((valName !== null) && getValue(field)) {
				var validator = this.validators.get(valName);
				if (!validator.callback(getValue(field))) throw validator.message;
			}
			this.unmarkField(field);
			return true;
		} catch (e) {
			this.markField(field, this.IMAGE, e, this.IMAGE_POSITION);
			return false;
		}
	};

	/**
	 * Valida o formulário em questão
	 * 
	 * @return {bool}
	 */
	this.validate = function() {
		this.isValidated = true;
		this.invalidFields = new Array(0);
		for (var i=0; i<form.length; i++) {
			var el  = form.elements[i];
			if (!this.validateField(el)) {
				this.invalidFields.push(el.name);
				this.isValidated = false;
			}
		}
		return this.isValidated;
	};

	/**
	 * Construtor
	 */
	 
	var that = this;
	
	var fn = function(event) {
		var obj = event.srcElement || event.target;
		that.frm.formManager.validateField(obj);
	}
	  
	for (var i=0; i<form.length; i++) {
		var el = form.elements[i];
		
		// validador
		if (el.getAttribute('mandatory') == 'true') {
			// IE
			if (document.all) {
				el.attachEvent('onchange', fn);
				el.attachEvent('onkeyup', fn);
				el.attachEvent('onblur', fn);
		
			// FF
			} else {
				el.addEventListener('change', fn, false);
				el.addEventListener('keyup', fn, false);
				el.addEventListener('blur', fn, false);				
			}
		}
		// máscara
		if (el.getAttribute('mask') !== null) {
			el.onkeypress = function(event) {
				if (document.all) event = window.event; // IE denovo, não repassa o evento
				return txtBoxFormat(this, this.getAttribute('mask'), event);
			}
		}
	}
	form.onsubmit = function(event) {
		var validated = this.formManager.validate();
		if (this.formManager.onsubmit) {
			var uResult = this.formManager.onsubmit(validated, this.formManager.invalidFields);
			return validated ? uResult : validated;
		} else {
			return validated;
		}
	};
};
