// Metodus: Internet Productivo, Usabilidad y Desarrollo Web http://www.metodus.com
// License: http://www.opensource.org/licenses/mit-license.php
// Validate: v1.5 by Aeron Glemann http://www.electricprism.com/aeron

Metodus.validate = new Class({
	initialize: function(f, props) {
		this.props = {
			action: false,
			block: false,
			inline: 'last',
 			lang: 'en'
		}
		Object.extend(this.props, props || {});	

		this.f = $(f);		
		this.f.onsubmit = function() { return this.validate(); }.bind(this);  
		
		this.submit = $E('button[type="submit"], input[type="submit"]', this.f);
	},

	validate: function() {
		this.submit.disabled = false;

		errors = $ES('.error'); // clean up
		errors.each(function(e) {
			l = $E('label', e.parentNode);
			if (l) l.setStyle('display', 'block');
			
			if (e.getTag() == 'span') {
				spans = $ES('span', e.parentNode);
				for (s = 0; s < spans.length; s++) {
					if (spans[s].parentNode == e.parentNode && spans[s] != e) {
						spans[s].setStyle('display', 'inline');
						break;
					}
				}
			}

			if (e.getTag().test(/span|div/)) e.remove();
			else e.removeClass('error');
		});
		
		this.ul = new Element('ul'); // reset
		this.fx = new Array();
		this.passed = true;
		this.focused = false;
		
		$ES('input, select, textarea', this.f).each(function(el) {
			if (el.optional) return;
			
			t = el.type; 
			v = el.value;

		    n = (el.title) ? el.title : el.name;			
			n = n.clean().capitalize();

			if (t == 'text' || t == 'password' || t == 'hidden' || el.getTag() == 'textarea') {
				if (this.isEmpty(v)) { (this.props.lang == 'es') ? this.doError(el, n+' no puede estar vacio') : this.doError(el, n+' cannot be empty'); return; }
				
				// if (v == el.defaultValue) { this.doError(el, n+' cannot use the default value'); return; }
				
				if (el.isAlpha) {
					if(!this.isAlpha(v)) { (this.props.lang == 'es') ? this.doError(el, n+' solo puede tener caracteres A-Z a-z') : this.doError(el, n+' can only contain characters A-Z a-z'); return; }
				}
				
				if (el.isNumeric) {
					if(!this.isNumeric(v)) { (this.props.lang == 'es') ? this.doError(el, n+' solo puede tener caracteres 0-9') : this.doError(el, n+' can only contain characters 0-9'); return; }
				}
				
				if (el.isAlphaNumeric) {
					if(!this.isAlphaNumeric(v)) { (this.props.lang == 'es') ? this.doError(el, n+' solo puede tener caracteres A-Z a-z 0-9') : this.doError(el, n+' can only contain characters A-Z a-z 0-9'); return; }
				}
				
				if (el.isEmail) {
					if(!this.isEmail(v)) { (this.props.lang == 'es') ? this.doError(el, v+' no es un email v\xe1lido') : this.doError(el, v+' is not a valid email'); return; }
				}
				
				if (el.isLength != null) {
					var len = el.isLength;
					if (!this.isLength(v, len)) { (this.props.lang == 'es') ? this.doError(el, n+' debe ser solamente '+len+' caracteres') : this.doError(el, n+' must contain only '+len+' characters'); return; }
				}
				
				if (el.isLengthBetween != null) {
					var min = el.isLengthBetween[0];
					var max = el.isLengthBetween[1];
					if (!this.isLengthBetween(v, min, max)) { (this.props.lang == 'es') ? this.doError(el, n+' debe ser dentro '+min+' y '+max+' caracteres') : this.doError(el, n+' must contain between '+min+' and '+max+' characters'); return; }
				}
				
				if (el.isPhoneNumber) {
					if (!this.isPhoneNumber(v)) { (this.props.lang == 'es') ? this.doError(el, v+' no es un n\xfamero telefonico v\xe1lido') : this.doError(el, v+' is not a valid phone number'); return; }
				}
				
				if (el.isDate) {
					if (!this.isDate(v)) { (this.props.lang == 'es') ? this.doError(el, v+' no es una fecha v\xe1lida') : this.doError(el, v+' is not a valid date'); return; }
				}
				
				if (el.isMatch != null) {					
					if (!this.isMatch(v, el.isMatch.value)) { (this.props.lang == 'es') ? this.doError(el, n+' no son iguales') : this.doError(el, n+' does not match'); return; }
				} 
				
				if (el.isCreditCard != null) {	
					if (el.isCreditCard.nodeType == 1) {
						if (el.isCreditCard.getTag() == 'select') {
						  o = $E('option[value="' + el.isCreditCard.value + '"]', el.isCreditCard);
				          cc = (o) ? o.firstChild.nodeValue : '';				          
						}  
 						else cc = el.isCreditCard.value;  
						if (!cc) cc = ((this.props.lang == 'es') ? 'de tarjeta' : 'credit card');
					}
					else cc = (typeof(el.isCreditCard) == 'string') ? el.isCreditCard : ((this.props.lang == 'es') ? 'de tarjeta' : 'credit card');

					if (!this.isCreditCard(v, cc)) { (this.props.lang == 'es') ? this.doError(el, v+' no es un '+cc+' v\xe1lida') : this.doError(el, v+' is not a valid '+cc); return; }
				}				
			}
			
			if (t == 'checkbox') {
				if (!el.checked) { (this.props.lang == 'es') ? this.doError(el, n+' debe ser chequeado') : this.doError(el, n+' must be checked'); return; } 
			}

			if (el.getTag() == 'select') {
				if (this.isEmpty(el.options[el.selectedIndex].value)) { (this.props.lang == 'es') ? this.doError(el, n+' se necesita una opci\xf3n seleccionada') : this.doError(el, n+' needs an option selected'); return; }
			}
		}, this);

		if (!this.passed) {
			if (this.props.block != false && this.props.inline != 'block') {				
				div = new Element('div');
				div.addClass('error').setStyle('opacity', 0);	
				
				p = new Element('p');
				p.appendText('Please correct the errors indicated').injectInside(div);
				
				this.ul.clone().injectInside(div);	
								
				fx = new Fx.Style(div, 'opacity', { duration: 1000 });
				this.fx.push(fx);					

				if (this.props.block == 'outside') this.f.parentNode.insertBefore(div, this.f);
				else if (this.props.block == 'top') document.body.insertBefore(div, document.body.firstChild);
				else this.f.insertBefore(div, this.f.firstChild); // inside by default
			}

			this.fx.each(function(fx) { fx.start(0, 0.99); });
		}
		else if (this.props.action) {
			this.submit.disabled = true;
			
			this.f.action = this.props.action;
			this.f.submit();
		}
		
		return this.passed;
	},

	doError: function(el, err) {
		this.passed = false;

		if (!this.focused) { 
			this.focused = true;
			el.focus();
		}

		el.addClass('error');
		
		if (this.props.block != false && this.props.inline != 'block') {
			li = new Element('li');
			li.appendText(err).injectInside(this.ul);
		}
		
		if (this.props.inline != false) {
			if (this.props.inline == 'block') {
				div = new Element('div');
				div.addClass('error').setStyle('opacity', 0).injectAfter(el);		

				fx = new Fx.Style(div, 'opacity', { duration: 1000 });
				this.fx.push(fx);				

				p = new Element('p');
				p.appendText(err).injectInside(div);
				return;					
			}

			span = new Element('span');
			span.appendText(err).addClass('error').setStyle('opacity', 0);		
				
			fx = new Fx.Style(span, 'opacity', { duration: 1000 });
			this.fx.push(fx);					

			if (this.props.inline.test('label')) {
				l = $E('label', el.parentNode);
				if (l) {
					l.setStyle('display', 'none');
					span.injectBefore(l);
					return;
				}
			}
			else if (this.props.inline.test('span')) {
				spans = $ES('span', el.parentNode);
				for (s = 0; s < spans.length; s++) {
					if (spans[s].parentNode == el.parentNode) {
						spans[s].setStyle('display', 'none');
						span.injectBefore(spans[s]);
						return;
					}
				}
			}
			
			if (this.props.inline.test('first')) span.injectBefore(el.parentNode.firstChild);
			else if (this.props.inline.test('before')) span.injectBefore(el);
			else if (this.props.inline.test('after')) span.injectAfter(el);
			else span.injectInside(el.parentNode); // last by default
		}
	},

	// Regexp by Travis Beckham (http://www.squidfingers.com | http://www.podlob.com)

	isEmpty: function(str) { // returns true if the string is empty
		return (str == null) || (str.length == 0);
	},
	
	isEmail: function(str) { // returns true if the string is a valid email
		if (this.isEmpty(str)) return false;
		return str.test('\\w{1,}[@][\\w\\-]{1,}([.]([\\w\\-]{1,})){1,3}$');
	},

	isAlpha: function(str) { // returns true if the string only contains characters A-Z or a-z
		return str.test('^[a-zA-Z]+$');
	},
	
	isNumeric: function(str) { // returns true if the string only contains characters 0-9 
		return str.test('^[0-9]+$');
	},

	isAlphaNumeric: function(str) { // returns true if the string only contains characters A-Z, a-z or 0-9
		return str.test('^[a-zA-Z0-9]+$');
	},

	isLength: function(str, len) { // returns true if the string's length equals "len"
		return str.length == len;
	},
	
	isLengthBetween: function(str, min, max) { // returns true if the string's length is between "min" and "max"
		return (str.length >= min)&&(str.length <= max);
	},

	isPhoneNumber: function(str) { // returns true if the string is formatted as (000)000-0000, (000) 000-0000, 000-000-0000, 000.000.0000, 000 000 0000, 0000000000
		return str.test(/^\(?[2-9]\d{2}[\)\.-]?\s?\d{3}[\s\.-]?\d{4}$/);
	},

	isDate: function(str) { // returns true if the string is formatted equal to the format parameter
		if (!str.test('^(\d{1,2})[\s\.\/-](\d{1,2})[\s\.\/-](\d{4})$')) return false;
		result = str.match(re);
		m = parseInt(result[1]);
		d = parseInt(result[2]);
		y = parseInt(result[3]);
		if (m < 1 || m > 12 || y < 1900 || y > 2100) return false;
		if (m == 2) days = ((y % 4) == 0) ? 29 : 28;
		else if (m == 4 || m == 6 || m == 9 || m == 11) days = 30;
		else days = 31;
		return (d >= 1 && d <= days);
	}, 

	isMatch: function(str1, str2) { // returns true if "str1" is the same as the "str2"
		return str1 == str2;
	},
	
	// Credit Card validation from http://doc.async.com.br

	isCreditCard: function(str1, str2) { // returns true if "str1" is a valid credit card specified by "str2"
		re = /[ -]/g;
		cn = str1.replace(re, ''); // card number

		if (cn.length < 13 || cn.length >  19) return false;

		sum = 0; mul = 1; len = cn.length;

		for (j = 0; j < len; j++) {
			a = cn.substring((len - j - 1), (len - j));
			b = parseInt(a, 10) * mul;
			if (b >= 10) sum += (b % 10) + 1;
			else sum += b;
			if (mul == 1) mul++;
			else mul--;
		}

		if ((sum % 10) != 0) return false; // luhn mod-10 checksum 
		
		if (str2 === true) return true;

		ct = str2.replace(re, '').toUpperCase(); // card type

		switch (ct) {
			case 'AMERICAN': case 'AMERICANEXPRESS': case 'AMEX': 
				return this.isAmericanExpress(cn); 
			case 'CARTE': case 'CARTEBLANCHE': case 'DINERS': case 'DINERSCLUB': 
				return this.isDinersClub(cn); 
			case 'DISCOVER': 
				return this.isDiscover(cn);
			case 'JCB': 
				return this.isJCB(cn);
			case 'MASTER': case 'MASTERCARD': 
				return this.isMasterCard(cn); 
			case 'VISA': 
				return this.isVisa(cn);
			default:
				return true;
		}
	},

	isAmericanExpress: function(str) { // returns true if "str1" is a valid amex card
		a = str.substr(0, 1).toInt();
		b = str.substr(1, 1).toInt();
		return (str.length == 15 && a == 3 && (b == 4 || b == 7));
	},

	isDinersClub: function(str) { // returns true if "str1" is a valid diners club card
		a = str.substr(0, 1).toInt();
		b = str.substr(1, 1).toInt();
		return (str.length == 14 && a == 3 && (b == 0 || b == 6 || b == 8));
	},

	isDiscover: function(str) { // returns true if "str1" is a valid discover card
		a = str.substr(0, 4).toInt();
		return (str.length == 16 && a == 6011);
	},

	isJCB: function(str) { // returns true if "str1" is a valid jcb card
		a = str.substr(0, 4).toInt();
		return (str.length == 16 && (a == 3088 || a == 3096 ||  a == 3112 || a == 3158 || a == 3337 || a == 3528));
	},

	isMasterCard: function(str) { // returns true if "str1" is a valid master card
		a = str.substr(0, 1).toInt();
		b = str.substr(1, 1).toInt();
		return (str.length == 16 && a == 5 && (b >= 1 && b <= 5));
	},

	isVisa: function(str) { // returns true if "str1" is a valid visa card
		a = str.substr(0, 1).toInt();
		return ((str.length == 16 || str.length == 13) && a == 4);
	}
});
