// revealing module pattern
/**
 * vaildate.js
 * Author: Justen Robertson
 * Website: http://www.justenrobertson.com
 * License: see below
 * Info:
 * This is a simple drop-in javascript validator. It automatically attaches itself 
 * to forms and form elements with appropriate class names. To use, add the class
 * 'validateSubmit' to the submit buttons of any forms you wish to validate.
 * Add the class names corrosponding to the validation methods you require to each
 * input field you want to validate.
 *
 * You will need to provide styling for the following classes:
 * span.invalidMessage: the message inserted when a field is invalid, in the form of a span element directly after the invalid field in the DOM.
 * input.validateValid: additional styling for fields which have been verified
 * input.validateInvalid: additional styling for fields which have inavlid content
 * 
 * Licensing note: this is an ALPHA version. If you have it I probably gave it to you directly.
 * It will be available via my website under a GNU/GPL license sometime soon, but in the meanwhile
 * please do not redistribute publicly. You may otherwise modify and use at your discression
 * as long as you keep the contents of this header complete, unmodified, and in place.
 */
 
 
var Validate = function () {
/**
 * Each validation mode gets its own method in case it needs to do something more than just run a regexp
 * We store them here in the hash
 * Any field may be validated simply by adding the key for a validation function in this hash
 * as a class name in the form.
 * For instance, to validate a field that must contain only alphabetical characters, use
 * <input class='validateAlpha' [other attributes] />
 * For required fields, use the class name 'validateRequired'
 * The 'required' validator is run only on submit
 *
 * To add new validation methods, assign them here and implement them below
	*/
	var validators = {
		'validateAlpha' : 'checkAlpha', // alphabetical, no spaces
		'validateAlphaSpaces' : 'checkAlphaSpaces', // alphabetical, with spaces
		'validateAlphanumeric' : 'checkAlphanumeric', // alphabetical or numbers, no spaces
		'validateAlphanumSpaces' : 'checkAlphanumSpaces', // alphabetical or numbers, with spaces
		'validateStreetAddress' : 'checkStreetAddress', // alphabetical or numbers, with spaces, periods and hash marks
		'validateEmail' : 'checkEmail', // email
		'validateConfirmPassword' : 'checkConfirmPassword', // match password against previous 'validatePassword'
		'validateMin3' : 'checkMin3', // minimum of 3 characters
		'validateMin6' : 'checkMin6', // minimum of 6 characters
		'validateInt' : 'checkInt', // Integers only
		'validateFloat' : 'checkFloat', // Floating point numbers (decimals) allowed
		'validateIP' : 'checkIP', // validates IP addresses (decimal, does not support hex/octal and does not check ranges)
		'validateUSPhone' : 'checkUSPhone', // validates for standard US phone number formats only
		'validateUSCurrency' : 'checkUSCurrency', // validates for US currency
		'validateURL' : 'checkURL', // validates for US currency
		'validateNAPostal' : 'checkNAPostal' // validates North American (US/Canada) postal codes ONLY
		//'validateMemberRealName' : 'checkMemberName' // checks alphanumspaces & min3 -- Not enabled by default!
	}
	
	var lastCheckedName = ''; // for checking member names, we don't want to reproduce too much work
	var nameChecked = false;
	var nameGood = false;
	
	var lastCheckedEmail = ''; // for checking member names, we don't want to reproduce too much work
	var emailChecked = false;
	var emailGood = false;
	
	var bindObservers = function() {
		unbindObservers();
		if ($$('input')) $$('input').each(function(input) {
			// We bind everything but validateRequired, because we should only check if a field is empty
			// before final submission
			if (input.type == 'hidden') return; // Don't validate hidden attributes (the model will though)
			for(classname in validators) {
				if (input.hasClass(classname)) {
					input.removeEvents('blur'); // don't double up validation events for items with multiple validation types
					input.addEvent('blur', function(event) {Validate.check($(event.target))});
				}
			}
		});
		
		if ($$('.validateSubmit')) $$('.validateSubmit')[0].addEvent('click', function(event) {Validate.checkSubmit(event)});
	}
	
	
	
	
	var unbindObservers = function() {
		if ($$('input')) $$('input').each(function(input) { input.removeEvents('blur'); });
		if ($$('.validateSubmit')) $$('.validateSubmit')[0].removeEvents('click');
	}
	
	
	
	
	var check = function(item, text, invalid) {
		if (!item.hasClass('validateRequired') && item.value == '') return true;
		if(!text) text = false;
		if(!invalid) invalid = false;
		for(classname in validators) {
			var ret = false;
			if (item.hasClass(classname)) {
				var todo = validators[classname]+'(item)';
				var ret = eval(todo);
				if (ret) {
					invalid = true;
					if(text) text += "<br />\n"+ret;
					else text = ret;
				}
			}
		}
		
		if (invalid) markInvalid(item, text);
		else markValid(item);
		return true;
	}
	
	
	
	
	var checkSubmit = function(event) {
		event.stop();
		if ($$('input')) $$('input').each(function(input) { // Should always be inputs in a form, but what the hell
			var text = false;
			var invalid = false;
			if (input.type == 'hidden') return;// Don't validate hidden attributes (the model will though)
			if (input.type == 'button') return;
			if (input.type == 'checkbox') return;
			if (input.type == 'radio') return;
			if (input.hasClass('validateRequired')) {
				if (!input.value.match(/^.{1,}$/)) { // very simple check if the field isn't empty
					text = 'This field is required.';
					invalid = true;
				}
			}
			check(input, text, invalid);
		});
		
		if (!$$('.validateInvalid')[0]){
			//$(event.target).click();
			//$(event.target).getParent('form').submit();
			//this allows us to call the form's onsubmit event
			var form=$(event.target).getParent('form');
			if (typeof form.onsubmit == 'function') {
				if (form.onsubmit() !== false) {
					form.submit();
					return true;
				}
			}
			else{
				form.submit();
				return true;
			}
		}
		else{
			alert('You have submitted invalid information! Please review the form and try again.');
			return false;
		}
	}
	
	
	
	
	var checkAlpha = function(item) {
		var text = 'Must contain only alphabetical characters.';
		var value = $(item).value;
		if (!value.match(/^[a-zA-Z]*$/)) return text;
		return false;
	}
	
	
	
	
	var checkAlphaSpaces = function(item) {
		var text = 'Must contain only alphabetical characters and spaces.';
		var value = $(item).value;
		if (!value.match(/^[a-zA-Z ]*$/)) return text;
		return false;
	}


	
	
	var checkAlphanumeric = function(item) {
		var text = 'Must contain only alphabetical characters and numbers.';
		var value = $(item).value;
		if (!value.match(/^[a-zA-Z0-9]*$/)) return text;
		return false;
	}
	
	
	
	
	var checkConfirmPassword = function(item) {
		var text = 'Passwords do not match!';
		var value = $(item).value;
		var first = $$('.validatePassword')[0].value;
		if (value != first) return text;
		return false;
	}
	
	
	
	
	var checkAlphanumSpaces = function(item) {
		var text = 'Must contain only alphabetical characters, numbers, and spaces.';
		var value = $(item).value;
		if (!value.match(/^[a-zA-Z0-9 ]*$/)) return text;
		return false;
	}
	
	
	
	
	var checkInt = function(item) {
		var text = 'Must contain only integers (whole numbers, no decimals).';
		var value = $(item).value;
		if (!value.match(/^[0-9]*$/)) return text;
		return false;
	}
	
	
	
	
	var checkFloat = function(item) {
		var text = 'Must contain only numbers.';
		var value = $(item).value;
		if (!value.match(/^[0-9\.]*$/)) return text;
		return false;
	}
	

	

	var checkIP = function(item) {
		var text = 'Please enter a valid IP address.';
		var value = $(item).value;
		if (!value.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/)) return text;
		return false;
	}
	

	

	var checkUSPhone = function(item) {
		var text = 'Please enter a phone number in the format: (123) 456-7890.';
		var value = $(item).value;
		if (!value.match(/^(\+\d)*\s*(\(\d{3}\)\s*)*\d{3}(-{0,1}|\s{0,1})\d{2}(-{0,1}|\s{0,1})\d{2}$/)) return text;
		return false;
	}	
	
	
	
	
	var checkUSCurrency = function(item) {
		var text = 'Please enter a valid dollar amount.';
		var value = $(item).value;
		if (!value.match(/^\d+\.\d{2}$/)) return text;
		return false;
	}
	
	
	
	var checkURL = function(item) {
		var text = 'Please enter a valid URL';
		var value = $(item).value;
		if (!value.match(
			/^(http|https|ftp)\:\/\/([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&amp;%\$\-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|localhost|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(\:[0-9]+)*(\/($|[a-zA-Z0-9\.\,\?\'\\\+&amp;%\$#\=~_\-]+))*$/
		)) return text;
		return false;
	}	
	
	
	
	
	var checkStreetAddress = function(item) {
		var text = 'Please enter a valid street address.';
		var value = $(item).value;
		if (!value.match(
			/^[a-zA-Z\d]*(([\'\,\.\- #][a-zA-Z\d ])?[a-zA-Z\d]*[\.]*)*$/
		)) return text;
		return false;
	}

	
	

	var checkNAPostal = function(item) {
		var text = 'Please enter a valid US/Canada postal code.';
		var value = $(item).value;
		if (!value.match(
			/^((\d{5}-\d{4})|(\d{5})|([AaBbCcEeGgHhJjKkLlMmNnPpRrSsTtVvXxYy]\d[A-Za-z]\s?\d[A-Za-z]\d))$/
		)) return text;
		return false;
	}

	
	
		
/**
 * method checkEmail
 * Uses RFC 822 specification
 */
	
	var checkEmail = function(item) {
		var text = 'Please enter a valid email address.';
		var value = $(item).value;
		if (!value.match(
			/^((\"[^\"\f\n\r\t\v\b]+\")|([\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+(\.[\w\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+)*))@((\[(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))\])|(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))|((([A-Za-z0-9\-])+\.)+[A-Za-z\-]+))$/
		)) return text;
		else return false;
		/*
		 * The following code can work with an ajax responder to check for uniqueness.
		 * You must provide an implementation seperately according to your needs.
		 
		var text = "Checking to see if this email is in use...";
		
		var value = $(item).value;
		
		if (emailChecked && lastCheckedEmail == value) {
			if (emailGood) return false; 
			else return 'Sorry, this email address is already in use.';
		}
		emailChecked = false;
		emailGood = false;
		lastCheckedEmail = value;
		$(item).setProperty('disabled', true);
		
		var req = new Request.JSON({
			url: '', // your ajax resonder URL here
			data: 'email='+encodeURI(value),
			onSuccess: function(ret) {
				//alert(ret);
				if (ret.used) Validate.markInvalid(item, 'Sorry, this email address is already in use.');
				else {
					Validate.markValid(item);
					emailGood = true;
				}
				$(item).removeProperty('disabled');
				emailChecked = true;
			}
		}).send();*/
		return text;
	}
	
	
	
	
	var checkMin3 = function(item) {
		var text = 'Must be at least 3 characters long.';
		var value = $(item).value;
		if (!value.match(/^.{3,}$/)) return text;
		return false;
	}
	
	
	
	
	var checkMin6 = function(item) {
		var text = 'Must be at least 6 characters long.';
		var value = $(item).value;
		if (!value.match(/^.{6,}$/)) return text;
		return false;
	}
	
	
	
/**
 * method checkMemberName
 * This method works peculiarly in that it needs to send an ajax request to ask
 * if the member name is already in use. It always returns invalid to base validation
 * but it will handle marking once it's done.
 */
 
 /*
	var checkMemberName = function(item) {
	
		// First run basic checks: is it at least 6 characters long? is it alphanumeric with possible spaces?
		var text = false;
		var res = false;
		res = checkMin3(item);
		if (res) text = res;
		res = false;
		res = checkAlphanumSpaces(item);
		if (res) (text)? text += "<br />"+res : text = res;
		if (text) return text;
		
		var text = "Checking to see if this name is in use...";
		
		var value = $(item).value;
		
		if (nameChecked && lastCheckedName == value) {
			if (nameGood) return false; 
			else return 'Sorry, this member name is already in use.';
		}
		nameChecked = false;
		nameGood = false;
		lastCheckedName = value;
		$(item).setProperty('disabled', true);
		
		var req = new Request.JSON({
			url: '', // your ajax responder url here
			data: 'name='+encodeURI(value),
			onSuccess: function(ret) {
				//alert(ret);
				if (ret.used) Validate.markInvalid(item, 'Sorry, this member name is already in use.');
				else {
					Validate.markValid(item);
					nameGood = true;
				}
				$(item).removeProperty('disabled');
				nameChecked = true;
			}
		}).send();
		return text;
	}*/
	
	
	var markInvalid = function(item, text) {
		$(item).removeClass('validateValid');
		if ($(item).getPrevious('span.invalidMessage')) $(item).getPrevious('span.invalidMessage').dispose();
		message = new Element('span', {'html': text, 'class':'invalidMessage'});
		//message.inject($(item), 'before');
		$(item).addClass('validateInvalid');
	}
	
	
	
	
	var markValid = function(item) {
		$(item).removeClass('validateInvalid');
		if ($(item).getPrevious('span.invalidMessage')) $(item).getPrevious('span.invalidMessage').dispose();
		$(item).addClass('validateValid');
	}
	
	
	
	
	var init = function() {
		bindObservers();
	}
	
	
	
	
	return {
		init: init,
		unbindObservers: unbindObservers,
		bindObservers: bindObservers,
		markInvalid: markInvalid,
		markValid: markValid,
		check: check,
		checkSubmit: checkSubmit,
		
		// validator methods
		checkAlpha: checkAlpha
	}
	
}();

window.addEvent('load', Validate.init);