/**
 * Really easy field validation with Prototype
 * http://tetlaw.id.au/view/javascript/really-easy-field-validation
 * Andrew Tetlaw
 * Version 1.5.4.1 (2007-01-05)
 * Adapted and upgraded for StepStone V5 by JOBV, grochm01* 
 * 
 * Copyright (c) 2007 Andrew Tetlaw
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * Requirements:
 * prototype.js, version 1.6
 * effects.js, lightbox.js, feedback.css
 *
 **/

if(typeof(Prototype) == "undefined")
  throw "Validator requires Prototype to be loaded.";

var Validator = Class.create({
	initialize : function(className, error, test, options) {
		if(typeof test == 'function'){
			this.options = $H(options);
			this._test = test;
		} else {
			this.options = $H(test);
			this._test = function(){return true};
		}
		this.error = error || 'Validation failed.';
		this.className = className;

	},
	test : function(v, elm, form) {		
		var a = (this._test(v,elm) && this.options.all(function(p){		
			var b = Validator.methods[p.key] ? Validator.methods[p.key](v,elm,p.value, form) : true;
			return b;
		}));
		return a; 
	}
});

// tests to perform on the values
Validator.methods = {
	pattern : function(v,elm,opt) { return Validation.get('IsEmpty').test(v) || opt.test(v)},	// reg exp test
	minLength : function(v,elm,opt) {return v.length >= opt},
	maxLength : function(v,elm,opt) {return v.length <= opt},
	minimum : function(v,elm,opt) {return v >= parseFloat(opt)}, 
	minimumOrEmpty : function(v,elm,opt) {return Validation.get('IsEmpty').test(v) ||  v >= parseFloat(opt)},
	maximum : function(v,elm,opt) {return v <= parseFloat(opt)},
	maximumOrEmpty : function(v,elm,opt) {return Validation.get('IsEmpty').test(v) ||  v <= parseFloat(opt)},
	minChecked : function(v, elm, opt, form) {
		
		var elem_name = $(elm).readAttribute('name');		
		var ch = $A($(form).select("[name="+elem_name+"]"));		
		var vals = ch.pluck('checked').partition();
		return (vals[0].size() >= opt)  ;
	},
	maxChecked : function(v,elm,opt,form) {	
		var elem_name = $(elm).readAttribute('name');		
		var ch = $A($(form).select("[name="+elem_name+"]"));			
		var vals = ch.pluck('checked').partition();		
		return (vals[0].size() <= opt);	
	}, 
	maxCheckedUnique : function(v,elm,opt,form) {	
		var elem_name = $(elm).readAttribute('name');		
		var ch = $A($(form).select("[name="+elem_name+"]"));
		var uniqueMap = new Hash();
		ch.collect(function(chkbox) {
			if(chkbox.checked && chkbox.value != ''){
				uniqueMap.set(chkbox.value,'');				
			}
		});			
		var vals = uniqueMap.keys().size();		
		return (vals <= opt);	
	},
	maxOptions : function(v,elm,opt) {
		var elemNum = 0;
		$(elm).descendants().each(function(s){
			elemNum +=1;
		});
		return (elemNum <= opt);	
	},
	minOptions : function(v,elm,opt) {	
		var elemNum = 0;
		$(elm).descendants().each(function(s){
			elemNum +=1;
		});
		return (elemNum >= opt);	
	},		
	eqChecked : function(v,elm,opt) {	
		var elem_name = $(elm).readAttribute('name');		
		var ch = $A(document.getElementsByName(elem_name));		
		var vals = ch.pluck('checked').partition();		
		return  (vals[0].size() == opt);	
	},	
	notOneOf : function(v,elm,opt) {return $A(opt).all(function(value) {
		return v != value;
	})},
	oneOf : function(v,elm,opt) {return $A(opt).any(function(value) {
		return v == value;
	})},
	is : function(v,elm,opt) {return v == opt},
	isNot : function(v,elm,opt) {return v != opt},
	equalToField : function(v,elm,opt) {
			
			if ($(opt) != null)
			return v == $F(opt)
	},
	notEqualToFieldOrValue : function(v,elm,opt) {
		if ($(opt) != null)
			return ( v != $F(opt) || ($F(opt) == "done") )
	},
	notEqualToField : function(v,elm,opt) {
		
			if ($(opt) != null)
			return $A(opt).all(function(value) {
				return v != $F(value);
			})
	},	
	checkIfOneNotEmpty : function(v,elm,opt) {
			if (v == "") {		
				return $A(opt).all(function(value) {
					return (v != $F(value));
				});
			}
			return true;
	},	
	
	
	
	
	/**
	 *	Microsoft Dictionary based Password Strength Checker
	 *	Requires mspsc.js to be included!
	  *	@param {Object} v value to pass through to the checker
	 *	@param {Object} elm
	 *	@param {Integer} minimum strenght to test to
	 *	@return	{boolean} Return true when tested strength is higher or equal minimum strength
	 */
	checkPwdStrengthMSpsc : function(v,elm,minimum) { 
		// return true when MSpsc library is not available
		if(typeof MSpsc == 'undefined') return true;
		// get strength of v
		strength = MSpsc.EvalPwdStrength('',v);
		//console.log("EvalPwdStrength v :" + v + " : "  + strength + ' vs min : ' + minimum);
		return strength >= minimum;
	},
	/**
	 *	RegExp Password Strength Test
	 *	@author Douglas Karr http://www.douglaskarr.com/
	 *	@param {Object} v value to pass through to the checker
	 *	@param {Object} elm
	 *	@param {Integer} minimum strenght to test to
	 *	@return	{boolean} Return true when tested strength is higher or equal minimum strength
	 **/
	checkPwdStrength : function(v,elm,minimum) {
		var strongRegex = new RegExp("^(?=.{8,})(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*\\W).*$", "g");
		var mediumRegex = new RegExp("^(?=.{7,})(((?=.*[A-Z])(?=.*[a-z]))|((?=.*[A-Z])(?=.*[0-9]))|((?=.*[a-z])(?=.*[0-9]))).*$", "g");
		var enoughRegex = new RegExp("(?=.{6,}).*", "g");		
		if (v.length==0) {
			strength = 0; // none
		} else if (false == enoughRegex.test(v)) {
			strength = 1; // not enough chars
		} else if (strongRegex.test(v)) {
			strength = 4; // strong
		} else if (mediumRegex.test(v)) {
			strength = 3; // medium
		} else {
			strength = 2; //weak
		}
		//console.log("EvalPwdStrength " + v + " : "  + strength + ' vs ' + minimum);
		return strength >= minimum;
	},
	include : function(v,elm,opt) {
		return $A(opt).all(function(value) {
			return Validation.get(value).test(v,elm);
		});
	},
	isDate : function (v, elm) {		
		if( Validation.get('IsEmpty').test(v)) return true;
		var regex = /^(\d{1,2})[\/\.](\d{1,2})[\/\.](\d{4})$/;	
		if(!regex.test(v)) return false;		
		var d = new Date(v.replace(regex, '$2/$1/$3'));		
		return ( parseInt(RegExp.$2, 10) == ( 1 + d.getMonth()) ) && (parseInt(RegExp.$1, 10) == d.getDate()) && (parseInt(RegExp.$3, 10) == d.getFullYear() );
	},	
	isValidDateDMY : function(v,elm,opt,form){
		var aDate = $A(opt);
		var dates = [];
		
		for(var i = 0, len = aDate.size(); i < len; i++){
			dates[i] = Validation.getFormElement(aDate[i], form);
		}
				
		var valid_date = dates[0].value+'/'+dates[1].value+'/'+dates[2].value;		
		return this.isDate(valid_date,elm) ||( Validation.get('IsEmpty').test(dates[0].value) && Validation.get('IsEmpty').test(dates[1].value) && Validation.get('IsEmpty').test(dates[2].value) );
	},	
	isMoreThanXinThePast : function(v,elm,opt,form){		
		var aDate = $A(opt);
		var dates = [];
		
		for(var i = 0, len = aDate.size(); i < len;i++){
			dates[i] = Validation.getFormElement(aDate[i], form);
		}
				
		var valid_date = dates[0].value+'/'+dates[1].value+'/'+dates[2].value;				
		
		if(!this.isDate(valid_date, elm)){
			return 1;
		}	
		
		var myDate = new Date();		
		
		myDate.setFullYear(dates[2].value, dates[1].value-1, dates[0].value);		
		
		var today = new Date();
		
		today.setDate(today.getDate() - aDate[3]);		
		
		if(myDate > today){
			return 0;
		}else{
			return 1;
		}
	},	
	isLessThanXinThefuture : function(v,elm,opt,form){		
		var aDate = $A(opt);
		var dates = [];
		
		for(var i = 0, len = aDate.size(); i < len;i++){
			dates[i] = Validation.getFormElement(aDate[i], form);	
		}
				
		var valid_date = dates[0].value+'/'+dates[1].value+'/'+dates[2].value;		
		if(!this.isDate(valid_date,elm)){
			return 1;
		}	
		var myDate=new Date();		
		myDate.setFullYear(dates[2].value, dates[1].value-1,dates[0].value);		
		var today = new Date();
		today.setDate(today.getDate()+aDate[3]);
		if(myDate > today){
			return 0;
		}else{
			return 1;
		}
	},
	isLaterThan : function(v,elm,opt,form){		
		var aDate = $A(opt);
		var dates = [];
		
		for(var i = 0, len = aDate.size(); i < len;i++){
			dates[i] = Validation.getFormElement(aDate[i], form);	
		}
				
		var valid_date1 = dates[0].value+'/'+dates[1].value+'/'+dates[2].value;
		var valid_date2 = dates[3].value+'/'+dates[4].value+'/'+dates[5].value;		
		
		if(!this.isDate(valid_date1,elm) || !this.isDate(valid_date2,elm)){
			return 1;
		}	
		
		var myDate1=new Date();		
		myDate1.setFullYear(dates[2].value, dates[1].value - 1, dates[0].value);		
		
		var myDate2=new Date();		
		myDate2.setFullYear(dates[5].value, dates[4].value - 1, dates[3].value);		
		
		if(myDate1 < myDate2){
			return 0;
		}else{
			return 1;
		}
	},
	isMail : function (v, elm) {
		var regex = /^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9- ]+)*\.(([a-z]{2,4})|(travel|mobi|asia|jobs|aero|coop|info|museum|name))$/i;
		return Validation.get('IsEmpty').test(v) || regex.test(v.toLowerCase().trim());
	},
	
	isEmailsList: function(v,elem){		
		var aList = v.replace(/;/g,",");		
		var ok = true;
		aList  = aList.split(",");		
		for(i=0; i<aList.length;i++){			
			if(ok){
				ok = this.isMail(aList[i])
			}else{
				break;
			}			
		}		
		return ok;
	},
	minLengthOrEmpty : function(v,elm,opt) {return Validation.get('IsEmpty').test(v) || v.length >= opt}	
}

var Validation = Class.create({
	initialize : function(form, options){	
		
		this.form = $(form);
		this.options = Object.extend({
			onSubmit : true,								// do tests on form submit
			stopOnFirst : false,							// stop at the first error
			skipOnAction : '',							// stop at the first error
			immediate : true,								// validate field on blur			
			showInline : true,								// show error messages inline the form
			useErrorContainer: true,						// show the error messages at the top of the form, don't show the error messages inline
			useCustomAlert: false, 							// uses Lightbox.js to show the errorcontainer
			CustomAlertAnimate: true,						// show the custom alert animated or not
			CustomAlertMessage: '',							// What to show in the custom alert message; can a function
			CustomAlertMessageButtonText : "Please correct the errors in the form",	// button text of the custom alert message
			CustomAlertWidth: 500,							//custom alert width
			CustomAlertHeight: 400,							//custom alert height
			focusOnErrorContainer: true,					// will auto scroll the page to errorcontainer if Effect is available
			highlightErrorContainer : true,					// highlight errors in the container
			highlightErrorContainerEndColor : null,			// use background color of div; default: #fff
			focusOnError : true,							// set focus on the field with the error
			useTitles : false,								// use title attribute of field for error message
			useLabels : true,								// use the label with the for attribute same as field in the errorcontainer errors
			validateVisibleOnly : true,						// validate only visible HTML elements
			errorTextIntro: "Please fix the following error(s) and resubmit:",	//	Introduction to the errorcontainer errors
		  	errorJSItemBullet: "* ",						// used in the error container
			onCustomClick: '',								// custom elememt ID that trigers form valistaion
      		errorHTMLItemBullet: "&bull; ",					// used in the error container for each error message
			root : '/5/resources/',							// site resources root
      		onFormValidate : function(result, form) {},		// a function which will be executed when the form is valid
			onElementValidate : function(result, elm) {},	// a function which will be exectued when a field is valid
			showAdviceOnPossitiveValidation : false			// show advice on possitive validation   	
		}, options || {});
		
		// holds the error messages
		this.returnHash = [];
		
		if(this.options.useCustomAlert) {
			// the error container is used to place the error message in
			this.options.useErrorContainer = true;
			// don't place the focus on the field or errorcontainer; it will be shown in middle of screen
			this.options.focusOnErrorContainer = false;
			this.options.focusOnError = false;
		}
		
		if(this.options.useErrorContainer) { 
			this.errorDiv = Validation.createErrorContainer(this.form);
			var errorDiv = this.errorDiv;
		}else{
			// if we don't use an errorcontainer, show errors inline explicitly
			this.options.showInline = true;
			// if we don't use an errorContainer we stop at each error, one at a time
			this.options.stopOnFirst = true;
			this.options.focusOnErrorContainer = false;
		}
		
		// onsubmit handler
		if(this.options.onSubmit) Event.observe(this.form,'submit',this.onSubmit.bind(this),false);
		// onblur handler
		if(this.options.immediate) {
			var useTitles = this.options.useTitles;
			var callback = this.options.onElementValidate;
			var useErrorContainer = this.options.useErrorContainer;
			var showInlineError = this.options.showInline;
			var validateVisibleOnly = this.options.validateVisibleOnly;
			Form.getElements(this.form).each(function(input) { // Thanks Mike!
				Event.observe(input, 'blur', function(ev) { 
					test = Validation.validate(Event.element(ev),{useTitle : useTitles, onElementValidate : callback, showInlineError : showInlineError, validateVisibleOnly : validateVisibleOnly, form : this.form});
					// if validation passes, remove it from the container // todo: what about re-occuring errors?
					if(useErrorContainer) if(test) Validation.fadeErrorMessage(Event.element(ev), errorDiv);
				});
			});
		}
		//on custom element Click
		if(this.options.onCustomClick != '') Event.observe($(this.options.onCustomClick),'click',this.onSubmit.bind(this),false);

		// LIGHTBOX: customalert setup: prepare the lightbox for faster execution
		if(this.options.useCustomAlert && this.errorDiv) {
			// activate the lightbox for this.form to show validation.js errormessages on submit			
			this.lightbox = Validation.createCustomAlert(this.form, {ctrl: this.form, name:'validationLightbox', width: this.options.CustomAlertWidth, height: this.options.CustomAlertHeight, method: 'innerHTML', handler: 'submit', animate: this.options.CustomAlertAnimate, identifier: 'validation', position: 'center', root: this.options.root });			
			/* initialize Lightbox_instance e.g. for case when manullay function "validate" is called */
			Lightbox_instance = this.lightbox;	
		}
		
	},

	onSubmit :  function(ev){
		
		var str = this.form.action;
		var skipArr = this.options.skipOnAction;
		var boxcontent,buttontext,buttons,customMessage,lightboxcontent;
		
		if ($A(skipArr).any(
				function (element){if (str.search(element) > 0) return true;}
			)
		) return true;		

		if(!this.validate()){			

			// prevent real form onsubmit
			Event.stop(ev);
			
			// LIGHTBOX: create custom alert using lightbox.js
			

			if(this.errorDiv) {
				this.lightbox = Validation.createCustomAlert(this.form, {ctrl: this.form, name:'validationLightbox', width: this.options.CustomAlertWidth, height: this.options.CustomAlertHeight, method: 'innerHTML', handler: 'submit', animate: this.options.CustomAlertAnimate, identifier: 'validation', position: 'center', root: this.options.root });
				this.errorDiv.hide();				
				Lightbox_instance = this.lightbox;				
				var buttontext = this.options.CustomAlertMessageButtonText;
				var buttons = '<div class="row buttons"><div class="right" style="margin:20px;"><a href="#" class="UIButton Button_Blue" id="lightboxButton" rel="closeit" onclick="Lightbox_instance.closeit();return false;"><span>'+buttontext+'</span></a></div></div>';		
				var customMessage = (typeof this.options.CustomAlertMessage == 'function') ? this.options.CustomAlertMessage() : this.options.CustomAlertMessage;
				var lightboxcontent = (this.options.CustomAlertMessage) ? customMessage + buttons : this.errorDiv.innerHTML + buttons;				
				var boxcontent = '<div class="errorContainer">' + lightboxcontent + '</div>';
				//Lightbox_instance.getPageSize();
				Lightbox_instance.update(boxcontent);
				Lightbox_instance.activate();
			}
			
			// ERRORCONTAINER: scroll to div errorcontainer
			if(this.options.useErrorContainer && this.options.focusOnErrorContainer) {
				if(typeof Effect != 'undefined' && this.errorDiv) {
					new Effect.ScrollTo(this.errorDiv.id);
				}
			}
		}			

	},
	validate : function() {		
		var result = false;
		var useTitles = this.options.useTitles;
		var callback = this.options.onElementValidate;
		var useErrorContainer = this.options.useErrorContainer;		
		var validateVisibleOnly = this.options.validateVisibleOnly;	
		var showAdviceOnPossitiveValidation = this.options.showAdviceOnPossitiveValidation;
		var form = this.form;			
		
		if(this.options.stopOnFirst) {
			/*result = Form.getElements(this.form).all(function(elm) {
				return Validation.validate(elm, { useTitle : useTitles, onElementValidate : callback, showInlineError : !useErrorContainer, logError : true, validateVisibleOnly: validateVisibleOnly });
			});*/
			result = this.form.select(".validatethis").all(function(elm) {
				return Validation.validate(elm, { useTitle : useTitles, onElementValidate : callback, showInlineError : !useErrorContainer, logError : true, validateVisibleOnly: validateVisibleOnly, showAdviceOnPossitiveValidation:showAdviceOnPossitiveValidation, form : form });
			});
			
			
		} else {
			/*result = Form.getElements(this.form).collect(function(elm) {
					return Validation.validate(elm,{ useTitle : useTitles,onElementValidate : callback, showInlineError : !useErrorContainer, logError : true, validateVisibleOnly: validateVisibleOnly });
			}).all();*/
			
			result = this.form.select(".validatethis").collect(function(elm) {
					return Validation.validate(elm,{ useTitle : useTitles, onElementValidate : callback, showInlineError : !useErrorContainer, logError : true, validateVisibleOnly: validateVisibleOnly, showAdviceOnPossitiveValidation:showAdviceOnPossitiveValidation, form : form  });
			}).all();
		}		
		// set focus on first input field with class "validation-failed"
		if(!result && this.options.focusOnError) {
			Form.getElements(this.form).findAll(function(elm){return $(elm).hasClassName('validation-failed')}).first().focus();
		}		
		if(!result && useErrorContainer) {
			// reset the error messages	
			Validation.displayHTMLErrors(this.errorDiv, Validation.returnHash, this.options);
			Validation.resetErrors();
			
		}		
		this.options.onFormValidate(result, this.form);	
		
			
		return result;
	},
	reset : function() {
		Form.getElements(this.form).each(Validation.reset);
	},
	resetElement : function(element) {
		Validation.reset(element);
	},
	add : function(className, error, test, options) {
		Validation.add(className, error, test, options);
	},
	addAllThese : function(validators) {		
		Validation.addAllThese(validators);
	},
	setup : function (rules, formname) {
		Validation.setup(rules, formname);	
	},
	translate : function (translations) {
		options = this.options;
		Validation.translate(translations);	
	}
});

Object.extend(Validation, {
	returnHash: [],
	validate : function(elm, options){
		
		options = Object.extend({
			useTitle : false,
			showAdviceOnPossitiveValidation : false,
			onElementValidate : function(result, elm) {},
			speedappear : 0.2 // time to blur
		}, options || {});
		
		elm = $(elm);
		var form = options.form;
		if(elm == null) return false;
		
		var cn = $w(elm.className);	// depecrated: elm.classNames();
		return result = cn.all(function(value) {
			var test = Validation.test(value,elm,options,form);			
			options.onElementValidate(test, elm);
			return test;
		});	
	},	
	test : function (name, elm, options, form) {		
		
		var v = Validation.get(name);
		
		var theParent = Validation.checkParent(elm);
				
		try {
				
			var testIt = true;
			
			if(options.validateVisibleOnly){
				testIt = Validation.isVisible(elm);
			}
			
			if(testIt && !v.test($F(elm), elm, form)) {				
				
				// this prop is used to label already checked input fields
				var prop = '__advice'+name.camelize();		
				var label = [];
				var labeltext;				
				var useTitle = options.useTitle;
				var showInlineError = options.showInlineError;
				var logError = options.logError;			
				
				
							
				//if(!elm[prop]) {								
					
					var advice = Validation.getAdvice(name, elm);					
					var errorMsg = useTitle ? ((elm && elm.title) ? elm.title : v.error) : v.error;	
					
					var label = (elm.type != 'radio' && elm.type != 'checkbox') ? $$('label[for="' + Validation.getElmID(elm) + '"]') : $$('label[for="' + elm.name + '"]');					
					var labeltext = (label != null && typeof label != 'undefined' && label.length > 0) ? label[0].innerHTML : Validation.getElmID(elm);		
					
					if(logError) Validation.addToErrors([name, elm, errorMsg, labeltext]);					
									
					if(advice == null) {						
						// nested spans are used for layout CSS
						advice = '<span class="validation-advice error" id="advice-' + name + '-' + Validation.getElmID(elm) +'"><span>' + errorMsg + '</span></span>'						
						switch (elm.type.toLowerCase()) {
							case 'checkbox':
							case 'radio':
								var p = elm.parentNode;
								if(p) {
									new Insertion.Bottom(p, advice);
								} else {
									new Insertion.After(elm, advice);
								}
								break;
							default:
								new Insertion.After(elm, advice);
						}						
						advice = Validation.getAdvice(name, elm);						
					}
					if(options.showAdviceOnPossitiveValidation){
						Validation.getPossitiveAdvice(elm).hide();	
					}
									
					// show the inline errors only when allowed to					
					if(showInlineError == true && elm.type != 'checkbox' && elm.type != 'radio') {			
						
							
						// hide all existing errors, show only one error message on blur at a time
						var errormessages = $$('span[class="validation-advice error"]');						
						errormessages.each(function(error){
							$(error).hide();
						});						
						// use scriptaculous effect or just show
						if(typeof Effect == 'undefined') {
							advice.style.display = ''; // show is no display value; not block; this would break css					
						} else {
							new Effect.Appear(advice, {duration : options.speedappear });
						}						
						// set the error message on top of the others by changing the zindex
						// only for none IE 
						advice.setStyle({ zIndex: '100' });
					}else{
						advice.hide();					
					}
										
				//} // !elm[prop]

				//set property so we don't pass this elm again
				elm[prop] = true;
				
				elm.removeClassName('validation-passed');
				elm.addClassName('validation-failed');
				
				// show validation on the field label
				label.invoke("removeClassName","validation-passed");
				label.invoke("addClassName","validation-failed");
						
				// set the classname "error" to the parent DIV for CSS to work
				//if($(theparent) != 'null'){				
				Element.addClassName(theParent,'error');	
				//}
				
				//console.log('FOUT: ' + elm.inspect() + ' => test: ' + name + ' : ' + v.test($F(elm), elm));
				
				
				return false;
							
			} else {				
					var advice = Validation.getAdvice(name, elm);

					if(advice != null) advice.hide();

					elm[prop] = '';				
					
					elm.removeClassName('validation-failed');
					elm.addClassName('validation-passed');
					
					// show validation on the field label
					var label = (elm.type != 'radio' && elm.type != 'checkbox') ? $$('label[for="' + Validation.getElmID(elm) + '"]') : $$('label[for="' + elm.name + '"]');
					label.invoke("removeClassName","validation-failed");
					label.invoke("addClassName","validation-passed");
					
					//$(theParent).removeClassName('error');					
					Element.removeClassName(theParent,'error');
					
					//console.log('OK: ' + elm.inspect() + ' => test: ' + name);
					if(elm.type != 'checkbox' && elm.type != 'radio'){			
						if(options.showAdviceOnPossitiveValidation){

							Effect.Appear(Validation.getPossitiveAdvice(elm), {duration : 0.5});	
						}
					}
					return true;
			}		
		} catch(e) {
			throw(e)
		}
	},	
	isVisible : function(elm) {
		while(elm.tagName != 'BODY') {
			if(!$(elm).visible()) return false;
			elm = elm.parentNode;
		}
		return true;
	},	
	getAdvice : function(name, elm) {
		return $('advice-' + name + '-' + Validation.getElmID(elm)) || $('advice-' + Validation.getElmID(elm));
	},
	getPossitiveAdvice : function(elm) {
		elm = $(elm);
		var advice = $('possitive-advice-' + Validation.getElmID(elm));
		if(!advice){
			advice = new Element("span",{
				id:'possitive-advice-' + Validation.getElmID(elm),
				className : "validation-advice validation-passed",
				style : "display:none"
			});
			
			elm.insert({after:advice});
		}
		return advice; 
	},	
	getElmID : function(elm) {		
		return elm.id ? elm.id : elm.name;
	},	
	fadeErrorMessage : function(elm, errorDiv) {
		if(typeof Effect != 'undefined') {
			if($('error_' + elm.id) != null) $('error_' + elm.id).fade();
		}else{
			if($('error_' + elm.id) != null) $('error_' + elm.id).style.display = 'none';
		}
		// error has been solved, so substract one from error counter
		Validation.errorCount--;
		// if counter reaches zero, hide the container
		if(Validation.errorCount == 0) {
			if(typeof Effect != 'undefined') {	
				errorDiv.fade();
			} else {
				errorDiv.hide();	
			}
		}
	},	
	reset : function(elm) {
		elm = $(elm);
		var cn = $w(elm.className);	//depecrated : elm.classNames();
		cn.each(function(value) {
			var prop = '__advice'+value.camelize();
			if(elm[prop]) {
				var advice = Validation.getAdvice(value, elm);
				Validation.getPossitiveAdvice(elm).hide();
				advice.hide();
				elm[prop] = '';
			}
			elm.removeClassName('validation-failed');
			elm.removeClassName('error');
			elm.removeClassName('validation-passed');
			elm.parentNode.removeClassName('error');
		});		
	},	
	add : function(className, error, test, options) {
		var nv = {};
		nv[className] = new Validator(className, error, test, options);
		Object.extend(Validation.methods, nv);
	},	
	addAllThese : function(validators) {		
		var nv = {};		
		$A(validators).each(function(value) {
				// className, error, test, options				
				nv[value[0]] = new Validator(value[0], value[1], value[2], (value.length > 3 ? value[3] : {}));
			});		
		Object.extend(Validation.methods, nv);
	},	
	get : function(name) {
		return  Validation.methods[name] ? Validation.methods[name] : Validation.methods['_LikeNoIDIEverSaw_'];
	},
	
	getFormElement : function(name, form) {
		var elementData = {};
		
		var element = $(form).select("#"+name);	//	check if field with given id exists in form
		
		if(element.length){						//	there is field with id
			elementData.id = element[0].id;
		}else{
			element = $(form).select("[name="+name+"]");	//	check if field with given name exists in form

			if(element.length){	//	there is field with name get its id
				elementData.id = element[0].id;
			}else{
				elementData.id = null;				
			}
		}
		if(elementData.id !== null){
			elementData.value = $(elementData.id).getValue();
		}else{
			elementData.value = null
		}
		
		
		
		return elementData;
	},	
	/**
	 *	Translate the validation error messages
	 *	Uses the validator name for identification
	 *	@param {Object} the array of translations
	 */
	translate : function (translations) {
		$A(translations).any(function(value) { // each
			if(typeof value != 'undefined'){						   
				// translate validators
				if(typeof Validation.methods[value[0]] != 'undefined') {
					Validation.methods[value[0]].error = value[1];
				}
				// translate validation strings
				if(typeof options[value[0]] != 'undefined') {
					options[value[0]] = value[1];
				}
			}
		});
	},
	/**
	 *	@param {Object} the message to add to the returnHash
	 */
	addToErrors : function (message) {		
		Validation.returnHash.push(message);
		Validation.errorCount = Validation.returnHash.length;
	},	
	resetErrors : function () {
		Validation.returnHash = [];
	},	
	/**
	 *	check radio values
	 *	@param {Object} the id of the radio button to test
	 *	@return {String} the value of the radio button
	 */
	getRadioValue : function (idOrName) {
        var value = null;
        var element = document.getElementById(idOrName);
		
        var radioGroupName = null;         
        // if null, then the id must be the radio group name
        if (element == null) {
                radioGroupName = idOrName;
        } else {
                radioGroupName = element.name;     
        }
        if (radioGroupName == null) {
                return null;
        }
        var radios = document.getElementsByTagName('input');
		
        for (var i=0; i<radios.length; i++) {
                var input = radios[ i ];				
                if (input.type == 'radio' && input.name == radioGroupName && input.checked) { 						                 
                        value = input.value;
                        break;
                }
        }
        return value;
	},
	/*
	 *	loop recursively until we find a parentnode which is a div,
	 *	necessary for the css class "error" to work
	 *	@param {Object} the source element
	 *	@return {Object} first found parent container DIV
	 */
	checkParent : function (el) {
		return el.parentNode.nodeName === 'DIV' ? el.parentNode : this.checkParent(el.parentNode);
	},	
	
	/*
	 * function set a focus on elemtn
	 * @param el_id element id
	 * @return void
	 */
	setFocus: function(el_id){
		try{	
			var element = $(el_id);
			
			if(element.getStyle("display") != "none" && element.getStyle("visibility") != "hidden"){
				element.focus();
			}else{
				element.parentNode.scrollTo();
			}
		}catch(e){
		}
	},
	/**
	 *	Show all compiled errors in a container
	 *	Todo: create the contents of the error container through DOM <= doing this now
	 *
	 *	@param {Object} container The container ID
	 *	@param {Object} errorMessages The array containing the error messages
	 *	@param {Object} options
	 *	@return {Object} The error messages in a HTML structure
	 */
	displayHTMLErrors: function(container, errorMessages, options) {		
		//console.log(errorMessages.inspect());
		
		var container = $(container);
		
		if(container == null){
			return false;
		}
		
		// reset the contents
		container.update("");
		
		var intro = new Element('p', { id: 'intro_' + container.id, className: 'error-intro' }).update(options.errorTextIntro);
		var body = new Element('ol', { id: 'body_' + container.id, className: 'error-list' });		

		var fieldid, errormessage;

		var atts = '';
		var ahref = window.location.search;
		var bullet = options.errorHTMLItemBullet;
	

		$A(errorMessages).each(function(value, index) {	 
		
			// check if same error already exists
			if ($('error_' + fieldid)) {
				return false;
			};
			
			// setup current error
			fieldid = value[1].id;
			errormessage = value[2];
			fieldtype = value[1].type;
			
	
			atts = 'rel="closeit" onclick=\"Lightbox_instance.closeit(); Validation.setFocus(\''+fieldid+'\'); return false;  \"';
			
			var error = new Element('li', { id: 'error_' + fieldid, className: 'error-li' });
			try {
				if (options.useLabels) {
					// strip tags and asterix				
					var fieldlabel = value[3].replace(/(<([^>]+)>|\*|\:)/ig, "");
					
					error.update(bullet + '<strong><a href="#"' + atts + '>' + fieldlabel + '</a></strong>: ' + errormessage);
				}
				else {
					error.update(bullet + '<a href="#"' + atts + '>' + errormessage + '</a>');
				}
			}catch(e){
				
			}
		
			// add the error to the list
			Element.insert(body,error);
				
		});	//each
		
		if (errorMessages.length > 0) {
		  
		  Element.insert(container,intro);
		  Element.insert(container,body);		  
		  
		  container.style.display = "block";
		  
		  if(options.highlightErrorContainer && typeof Effect != 'undefined') {			
			theEndcolor = (options.highlightErrorContainerEndColor) ? options.highlightErrorContainerEndColor : '#fff'; 			 
			 container.highlight({ endcolor: theEndcolor });
		  }
		  
		}		
		
		return errorMessages;		
	},
	/**
	 *	Create a div to put the error messages 
	 *	@return {Object} the created div
	 */
	createErrorContainer : function(form) {
		var errorDivName = 'error_' + form.id;
			
		/*var errorDiv = document.createElement('div');
		errorDiv.setAttribute('id',errorDivName);
		errorDiv.setAttribute('class','errorContainer');*/
		
		var errorDiv = new Element('div', {
            id: errorDivName,
			className: 'errorContainer'
        });

		Element.insert($(form),{'top':errorDiv});
		return errorDiv;
	},
	/**
	 *	Create Lightbox creating the errormessages
	 *	@see lightbox.js
	 *	@return {Object} the lightbox object created by lightbox.js
	 */
	createCustomAlert : function(form, options) {
		if (typeof Lightbox == 'undefined') throw("createCustomAlert requires lightbox");
		return new Lightbox(options);
	},
	/**
	 * instantiates Validators
	 */
	methods : {
		'_LikeNoIDIEverSaw_' : new Validator('_LikeNoIDIEverSaw_','',{})
	},	
	/**
	 * Setup form rules
	 * @access public
	 * @param {Object} rules Set of field ID's and classname to identify the requirements of the field
	 */
	setup : function (rules, formname) {
		$A(rules).each(function(value) {
		
			if(typeof value != 'undefined'){								
				//field = $(value[0]);
				field = $A($(formname).select("#"+value[0]).collect());
						
				if(field.length){
					field[0].addClassName("validatethis");
					field[0].addClassName(value[1]);
				}else{
					var addValidateByName = $(formname).select("[name = "+ value[0] +"]").collect();					
					if(addValidateByName.size()>0) addValidateByName[0].addClassName(value[1]).addClassName("validatethis"); 
				}
			}
		});
	}	
});

// Default Validation Routine
Validation.add('IsEmpty', '', function(v) {
				return  ((v == null) || (v.length == 0) || /^\s+$/.test(v));
});

// Basic Validators
Validation.addAllThese([
	['required','This is a required field.', function(v) {
				return !Validation.get('IsEmpty').test(v);
			}],
	['validate_number','Please enter a valid number in this field.', function(v) {
				return Validation.get('IsEmpty').test(v) || (!isNaN(v) && !/^\s+$/.test(v));
			}],
	['validate_digits', 'Please use numbers only in this field. please avoid spaces or other characters such as dots or commas.', function(v) {
				return Validation.get('IsEmpty').test(v) || !/[^\d]/.test(v);
			}],
	['validate_alpha','Please use letters only (a-z) in this field.', function (v) {
				return Validation.get('IsEmpty').test(v) ||  /^[^0-9!"#$%&()\*\+,./:;\<\=\>?@\[\\\]^_`{|}~]+$/.test(v);
			}],
	['validate_alphanum','Please use only letters (a-z) or numbers (0-9) only in this field. No spaces or other characters are allowed.', function(v) {
				return Validation.get('IsEmpty').test(v) ||  !/\W/.test(v);
			}],	
	['validate_date','Please enter a valid date in the format: dd/mm/yyyy', { isDate : '' }],
	['validate_email','Please enter a valid email address. For example fred@domain.com .', { isMail : '' }],
	['validate_emails_list','Please enter a valid list of email address. For example fred@domain.com;fred2@domain.com .', { isEmailsList: ''}],
	['validate_url','Please enter a valid URL.', function (v) {
				return Validation.get('IsEmpty').test(v) || /^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i.test(v);
			}],
	['validate_website','Please enter a valid URL.', function (v) {
				return Validation.get('IsEmpty').test(v) || /^(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i.test(v);
			}],
	['validate_selection','Please make a selection', function(v,elm){				
				return elm.options ? elm.selectedIndex > 0 : !Validation.get('IsEmpty').test(v);
			}],
	['validate_one_required','Please select one of the options.', function (v,elm) {				
				radiovalue = Validation.getRadioValue(elm.id);				
				return radiovalue;						
			}],
	['validate_checkbox','Please make a checkbox', function(v,elm){				
				var p = elm.parentNode;
				var options = p.getElementsByTagName('INPUT');
				return $A(options).all(function(elm) {
					return $F(elm);
				});
			}]
]);	

// more validators
Validation.addAllThese([
	['required_legal','You cannot continue without agreeing to the legal statement.', function(v) {
				return !Validation.get('IsEmpty').test(v);
			}],
	['validate_password_confirm','Your confirmation password does not match your first password, please try again.', {
			equalToField : 'password'
			}],
	['validate_email_confirm','Your confirmation email does not match your first email, please try again.', {
			equalToField : 'email'
			}],	
	['validate_date_us','Please enter a valid date.', function(v) {
				var test = new Date(v); // US format: mm/dd/yyyy or yyyy/mm/dd
				//console.log('date',test);
				return Validation.get('IsEmpty').test(v) || !isNaN(test);
			}],
	['validate_date_iso','Please enter a valid date.', function(v) {
				return Validation.get('IsEmpty').test(v) ||  /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(v)
			}],	
	['validate_password','Your password must consist of a combination of 6 mixed-case letters and numbers and cannot be the same as your name or email address.', {
			minLength : 6,
			notEqualToField : ['email','firstname','lastname'],				
			checkPwdStrength : 3 // higher number means stronger password: 4 being the highest level
		}],
	['validate_password_high','Your password must consist of a combination of 6 mixed-case letters and numbers and cannot be the same as your name or email address.', {
			minLength : 6,
			notEqualToField : ['email','firstname','lastname'],						
			//pattern : new RegExp("^.{6,}$","ig"), //6 characters, letters and numbers in mixed case (^(.*\d)(.*[a-z])(.*[A-Z])\w{6,}$)
			checkPwdStrength : 4 // higher number means stronger password
		}],	
	['validate_address','A valid street adress consist of a plain street name and a number in digits.', function (v) {
				// regexp requires two strings with a space in between to be valid
				return Validation.get('IsEmpty').test(v) || /^(\d+)?([\w0-9 \.\'\-]+)+\,?\/? +([\w0-9 \.\'\-\/]+)+(\d+)?$/i.test(v)
			}],
	['validate_date_au','Please use this date format: dd/mm/yyyy. For example 17/03/2006 for the 17th of March, 2006.', function(v) {
				if(Validation.get('IsEmpty').test(v)) return true;
				var regex = /^(\d{2})\/(\d{2})\/(\d{4})$/;
				if(!regex.test(v)) return false;
				var d = new Date(v.replace(regex, '$2/$1/$3'));
				return ( parseInt(RegExp.$2, 10) == (1+d.getMonth()) ) && 
							(parseInt(RegExp.$1, 10) == d.getDate()) && 
							(parseInt(RegExp.$3, 10) == d.getFullYear() );
			}],
	['validate_currency_dollar','Please enter a valid $ amount. For example $100.00 .', function(v) {
				// [$]1[##][,###]+[.##]
				// [$]1###+[.##]
				// [$]0.##
				// [$].##
				return Validation.get('IsEmpty').test(v) ||  /^\$?\-?([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})?)$/.test(v)
			}],	
	['validate_creditcard','Please enter a valid creditcard number.', function(v) {	
				if ( Validation.get('IsEmpty').test(v) )
				return true;				
				var nCheck = 0,
					nDigit = 0,
					bEven = false;
				value = value.replace(/\D/g, "");	
				for (n = value.length - 1; n >= 0; n--) {
					var cDigit = value.charAt(n);
					var nDigit = parseInt(cDigit, 10);
					if (bEven) {
						if ((nDigit *= 2) > 9)
							nDigit -= 9;
					}
					nCheck += nDigit;
					bEven = !bEven;
				}	
				return (nCheck % 10) == 0;
			}],
	['validate_accept_image', 'The image file is not valid', function(v) {
				return Validation.get('IsEmpty').test(v) || /.(png|jpe?g|gif)$/.test(v);
			}]	
	
]);

