// revealing module pattern
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
		'validateEmailBasic' : 'checkEmailBasic', // 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
		'validateSelect' : 'checkSelect' // Integers only
	}
	
	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 callbackSubmit = false; //this callback is executed before submit is called and submits only if the callback evaluates to true
	var callbackNoSubmit = false; //this callback is executed before submit is called and submit is not called;
	var valChain=new Chain;
	
	var bindObservers = function() {
		unbindObservers();
		if ($$('input', 'select')) $$('input', 'select').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', 'Checkout')) $$('.validateSubmit', 'Checkout').each(function(item) {
			item.removeEvents('click');																				   
			item.addEvent('click', function(event) {Validate.checkSubmit(event);});
		});
	}
	

	
	
	var unbindObservers = function() {
		if ($$('input')) $$('input').each(function(input) { input.removeEvents('blur'); });
		if ($$('.Checkout')) $$('.Checkout').each(function(item) {
			item.removeEvents('click');
		});
		if ($$('.validateSubmit')) $$('.validateSubmit').each(function(item) {
			item.removeEvents('click');
		});
	}
	
	
	
	
	var check = function(item, text, invalid) {
		if (item.disabled || (!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 runSubmit = function(event){
		if ($$('input','select')) $$('input','select').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.get('tag')!='select' && !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]){
			valChain.callChain();			
		}
		else{
			valChain.clearChain();
			alert('You have submitted invalid information! Please review the form and try again.');
		}
	}
	
	
	
	var checkSubmit = function(event) {
		event.stop;
		
		valChain.clearChain();
		
		valChain.chain(								 
							 function(){
								 runSubmit(event);
							 }
						);
		
				
		if(Validate.callbackNoSubmit){
			valChain.chain(
							function(){
									Validate.callbackNoSubmit(event);
							}
						)
		}
		else{			
			
			if(Validate.callbackSubmit){
				
				valChain.chain(
										function(){
											Validate.callbackSubmit(event);
										}
									);		
			}
			
			valChain.chain(
						  
						  function(){
								 $(event.target).getParent('form').submit();
							 }
						   
						  )
			
		}
		
		valChain.callChain();
		
	}
	
	
	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 checkSelect=function(item){
		
		var text = 'Please enter a valid select value.';
				
		var value ="";
		
		try{
				value=$(item).options[item.selectedIndex].value;
		}
		catch(e){}
		if(value=="") return text;
		return false;
		
	}
	
	
	
	var checkURL = function(item) {
		var text = 'Please enter a valid URL in the format: http://www.servername.com/path/to/page.html?querystringlookslike=this&...';
		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;
		
		
		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: '/views/ajax_checkEmail.php',
			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 checkEmailBasic = 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;
		return false;
		
	}	
	
	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: '/views/ajax_checkMemberName.php',
			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).getNext('span.validation_notice')) $(item).getNext('span.validation_notice').dispose();
		message = new Element('span', {'html': text, 'class':'validation_notice'});
		message.inject($(item), 'after');
		$(item).addClass('validateInvalid');
	}
	
	
	
	
	var markValid = function(item) {
		$(item).removeClass('validateInvalid');
		if ($(item).getNext('span.validation_notice')) $(item).getNext('span.validation_notice').dispose();
		$(item).addClass('validateValid');
	}
	
	
	
	
	var init = function() {
		bindObservers();
	}
	
	
	
	
	return {
		init: init,
		unbindObservers: unbindObservers,
		bindObservers: bindObservers,
		markInvalid: markInvalid,
		markValid: markValid,
		check: check,
		checkSubmit: checkSubmit,
		callbackSubmit: callbackSubmit, //this callback is executed before submit is called and submits only if the callback evaluates to true
	    callbackNoSubmit: callbackNoSubmit,
		valChain: valChain,   //this cannot be private as we need to have external methods call it;
		// validator methods
		checkAlpha: checkAlpha
	}
	
}();

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