// Form Validator
//   :: Purpose: validate form elements based on attributes
//   :: Developed by: Steve Gangwisch
//   :: Date: 3/17/2009
//   :: Version: 1.1

//   :: Requirements: Prototype & Script.acul.ous (Effects.js) Frameworks

/*
	Validator Options
		
		require
		validate-number
		validate-digits
		validate-alpha
		validate-alphanum
		validate-date
		validate-email
		validate-email-list
		validate-url
		validate-date-au
		validate-currency-dollar
		validate-regex
		validate-compare (Coming Soon)
		validate-required-one
		validate-not-first
		validate-not-empty

*/

var Validator = Class.create({
	
	initialize: function(settings){
		
		this.formId;
		this.elm;
		this.firstError;
		
		// Check for Custom Validator Settings
		this.settings=(settings)?settings:this.getDefaultSettings();
		
	},
	
	init: function(formId){
		
		this.formId = formId;
		
		var formElement = $(this.formId);
		
		formElement.select('div.' + this.settings.error_class).each(function(element){element.remove();});
		formElement.select('label.' + this.settings.validate_class).each(function(element){element.removeClassName('validation-error')});
		
		return this.checkForm(this.formId);
		
	},
	
	addListener: function(formId,callBack){
		
		this.formId = formId;
		
		Event.observe(formId,'submit',this.start.bindAsEventListener(this,callBack));
		
	},
	
	start: function(e,callback){
		
		try{
			var el = Event.element(e);
			
			var allValid = this.checkForm(el.identify());
			
			if(allValid){
			
				if(callback){
					callback(el.identify());
					Event.stop(e);
				}
			
			}else{
				
				Event.stop(e);
			
			}
			
		}catch(e){
			
			Event.stop(e);
		
		}
		
	},
	
	checkForm: function(formId){
		
		try{
			
			var elms = $(formId).getElements();
			var elmLen = elms.length;
			var elmNode = "";
			var type = "";
			var title = "";
			var radChk = [];
			var radCnt = [];
			var allValid = true;
			var showAlert = false;
			this.firstError = "";
			
			for(i=0;i<elmLen;i++){
				
				var valid = true;
				var applyClass = true;
				
				this.elm = elms[i];
				
				type = this.elm.type.toLowerCase();
				elmNode = this.elm.nodeName.toLowerCase();
				
				title=(this.elm.readAttribute('title') && this.settings.error_title)?this.elm.readAttribute('title'):this.settings.error_default;
				
				if(elmNode == "input"){
					if(type == "text" || type == "password"){
						valid = this.input();
					}else if(type == "radio" || type == "checkbox"){

						if(radCnt[this.elm.name] == 0 || !this.isDefined(radCnt,this.elm.name)){
							
							valid = this.radio($(formId));
							
							radChk[this.elm.name] = valid;
							
							radCnt[this.elm.name] = 1;

						}else{

							radCnt[this.elm.name] += 1;

							if(radChk[this.elm.name]){
								
								valid = true;
								
								applyClass = true;
								
							}else{
								
								if(radCnt[this.elm.name] > 1){
								
									valid = radChk[this.elm.name];
								
									applyClass = false;
									
								}
								
							}
							
						}
						
					}else{
						
						applyClass = false;
						
						valid = true;
						
					}

				}else if(elmNode == "select"){
					
					valid = this.selectEl();
					
				}else if(elmNode == "textarea"){
						
					valid = this.input();
					
				}else{
					
					applyClass = false;
					
				}

				
				if(!valid && applyClass && this.settings.validate_ignore.indexOf(this.type) == -1){
					
					var lblElm = $(formId).select('label[for="'+this.elm.name+'"]');
					
					this.updateLabel(lblElm,'add');
					
					// Box Method
					if(this.settings.error_show_method == "box"){
						
						var parentSize = lblElm[0].getDimensions(); // IE Relativize Fix
						lblElm[0]._originalHeight = parentSize.height+'px'; // IE Relativize Fix
						lblElm[0]._originalWidth = parentSize.width+'px'; // IE Relativize Fix
						lblElm[0].relativize();
						
						var chk = $('validateError_' + this.formId + '_' + this.elm.name);
						var chkErrBox =(chk)?chk.length:0;
												
						try{
							if(chkErrBox == 0){
								
								var lblHeight = this.elm.getHeight();
								var lblWidth=(type == "radio" || type == "checkbox")?this.elm.getWidth()+10:this.elm.getWidth()-5;
								
								if(this.settings.label_position == "left")lblWidth+=lblElm[0].getWidth();
								
								var divFrag = this.errorBox(title,lblWidth);
							
								lblElm[0].insert({top:divFrag});
								
								// Make sure event fires once by removing previous listener
								$('validateError_' + this.formId + '_' + this.elm.name + '_close').stopObserving('click');
								
								$('validateError_' + this.formId + '_' + this.elm.name + '_close').observe('click',this.removeBox.bindAsEventListener(this));
								
							}
						}catch(e){alert(e.message);}
					
					// Text under input element with an element wrapper
					}else if(this.settings.error_show_method == "text"){
						
						try{
							var chkErrBox = $('validateError_' + this.elm.name).length;
						}catch(err){ var chkErrBox = 0; }
						
						if(chkErrBox == 0){
						
							var divFrag = new Element(this.settings.error_element,{'id':'validateError_' + this.formId + '_' + this.elm.name,'class':this.settings.error_class}).update(title);
						
							this.elm.insert({after:divFrag});
						
						}
					
					// Simple javascript alert box
					}else if(this.settings.error_show_method == "alert"){
						
						showAlert = true;
						
					}
					
					allValid = false;
					if(this.firstError == "")this.firstError = 'validateError_' + this.elm.name;
					
				}else if(!valid){
					
					allValid = false;
					if(this.firstError == "")this.firstError = 'validateError_' + this.elm.name;
					
				}else if(valid && this.settings.validate_ignore.indexOf(this.type) == -1){
					
					this.cleanUpElm();
					
				}
				
			}
			
			if(showAlert){
				alert('Form could not validate.  Some required fields may have been left blank.');
			}
			
			return allValid;
		
		}catch(e){ alert(e.message); }
		
	},
	
	/*   Validator Methods   */
	
	input: function(){
		
		var e = this.elm;
		
		try{
			
			var txt = e.value.strip();
			
			// Input Validators
			if(e.hasClassName('require') && txt.length == 0){
				return false;
			}else if(e.hasClassName('require')){
				var m = e.getAttribute('minlength');
				if(m && Math.abs(m) > 0){
					if(txt.length < Math.abs(m)){
						return false;
					}
				}
			}
			
			// Check Special Validators
			if(e.hasClassName('validate-number') && isNaN(txt) && txt.match(/[^\d]/)){
				return false;
			}else if(e.hasClassName('validate-digits') && txt.replace(/ /,'').match(/[^\d]/)){
				return false;
			}else if(e.hasClassName('validate-alpha') && !txt.match(/^[a-zA-Z]+$/)){
				return false;
			}else if(e.hasClassName('validate-alphanum') && !txt.match(/^[a-zA-Z0-9]+$/)){
				return false;
			}else if(e.hasClassName('validate-date')){
				var d = new date(txt);
				if (isNaN(d)) {
					return false;
				}
			}else if(e.hasClassName('validate-email')){
				if(e.hasClassName('not-required') && e.value.length == 0){
					return true;
				}else{
					return this.validateEmail(txt);
				}
			}else if(e.hasClassName('validate-email-list')){
				if(!e.hasClassName('require') && e.value.length == 0){
					return true;
				}else{
					return this.validateEmailList(txt);
				}
			}else if(e.hasClassName('validate-url') && !txt.match(/^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i)){
				return false;
			}else if(e.hasClassName('validate-date-au') && !txt.match(/^(\d{2})\/(\d{2})\/(\d{4})$/)){
				return false;
			}else if(e.hasClassName('validate-currency-dollar') && !txt.match(/^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/)){
				return false;
			}else if(e.hasClassName('validate-regex')){
				var r = RegExp(e.getAttribute('regex'));
				if (r && ! txt.match(r)) {
					return false;
				}
			}else if(e.hasClassName('validate-compare') && e.getAttribute('compareto')){
				alert(e.getAttribute('compareto'));
			}
			
			return true;	
			
		}catch(e){ alert(e.message); return false; }
		
	},
	
	radio: function(f){
		
		var e = this.elm;
		
		var valid = true;
		
		if(e.hasClassName('validate-required-one')){

			valid = false;

			f.select('input[name="'+e.name+'"]').each(function(chk){
															   
				if(chk.checked){
					
					valid=true;
					
				}
				
			});
		}

		return valid;
		
	},
	
	selectEl: function(){
		
		var e = this.elm;
		
		if (e.hasClassName('validate-not-first') && e.selectedIndex == 0){
			
			return false;
			
		}else if(e.hasClassName('validate-not-empty') && e.options[e.selectedIndex].value.length == 0){
			
			return false;
			
		}
		
		return true;
		
	},
	
	/*   Getters   */
	getDefaultSettings: function(){
		
		var settings = {
			'validate_class'	:	'validation-error',
			'validate_ignore'	:	'hidden,submit',
			'error_show_method'	:	'box',
			'error_class'		:	'validation-error-box',
			'error_element'		:	'div',
			'error_default'		:	'This field is required.',
			'error_title'		:	'true',
			'label_position'	:	'left'
		};
		
		return settings;
		
	},
	
	/*   Helper Functions   */
	isVisible : function(elm) {
		
		while(elm.tagName != 'BODY') {
		
			if(!$(elm).visible()) return false;
		
			elm = elm.parentNode;
		
		}
	
		return true;
	},
	
	isDefined: function(object,variable){
		
		return (typeof(eval(object)[variable]) == "undefined")?false:true;
		
	},
	
	cleanUpElm: function(){ // Removes label class and error div
	
		try{
			
			this.updateLabel($(this.formId).select('label[for="'+this.elm.getAttribute('name')+'"][class="'+this.settings.validate_class+'"]'));
					
			$('validateError_' + this.formId + '_' + this.elm.name).remove();
			
		}catch(e){}
		
	},
	
	validateEmail: function(txt){
		
		if(!txt.match(/\w{1,}[@][\w\-]{1,}([.]([\w\-]{2,})){1,3}$/)){
			
			return false;
			
		}else{
			
			return true;
			
		}
		
	},
	
	validateEmailList: function(txt){
		
		var emails = txt.stripSpaces();
		
		var filter=/^[A-Z0-9\._%-]+@[A-Z0-9\.-]+\.[A-Z]{2,4}(?:[,;][A-Z0-9\._%-]+@[A-Z0-9\.-]+\.[A-Z]{2,4})*$/i;
		
		if((filter.test(emails))){
			return true;
		}
		
		return false;
		
	},

	updateLabel: function(elm,type){
		
		try{
			
			if(type == "add"){
				elm[0].addClassName(this.settings.validate_class);
			}else{
				elm[0].removeClassName(this.settings.validate_class);
			}
		
		}catch(err){}
		
	},

	errorBox: function(msg,elmWidth){
		
		var errMsg=(msg)?msg:this.settings.error_default;
		
		try{
			
			var errFrag = new Element('div',{'id':'validateError_' + this.formId + '_' + this.elm.name,'class':this.settings.error_class,'style':'top:-10px;left:'+elmWidth+'px'});
			
			var errPad = new Element('div',{'class':this.settings.error_class+'-padding'});
			
			var closeLink = new Element('a',{'href':'javascript:void(0);','id':'validateError_' + this.formId + '_' + this.elm.name + '_close','errbox':'validateError_' + this.formId + '_' + this.elm.name}).update('Close');
			
			errPad.appendChild(closeLink);
			
			errPad.appendChild(new Element('div').update(errMsg));
			
			errFrag.appendChild(errPad);
			
			return errFrag;
		
		}catch(e){
		
			return "";
			
		}
		
	},
	
	removeBox: function(e){
		
		try{
		
			var el = Event.element(e);
			
			$(el.getAttribute('errbox')).remove();
			
		}catch(e){}
		
	}
	
});

String.prototype.stripSpaces = function( ){ return this.replace( /\s/g, "" ); };

String.prototype.trim = function() {
	return this.replace(/^\s+|\s+$/g,"");
}
