// mOOlidator - a mootools Inline Validator
// v1.40 - 05.09.2011 - christianh
//
// All changes have to be documented in the mOOlidator Wiki entry. Also a new Version number
// has to be assigned, the date of change and the editor above updated. mOOlidator Updates can
// be accounted to task 0042626.
//
// Validation rules (validate['alpha','length[6,12]']):
//   required		- Set a Checkbox or Radio Buttons required if they are
//   alpha			- Alphabetical characters only including . _ -
//   alphanum		- Alphanumerical character only
//   digit			- Integer values only without prefix
//   nodigit		- All non Integer values
//   number			- Numbers inclusive floating point numbers and prefix
//   email			- eMail Adresses
//   image			- Images with the file extensions: jpg, jpeg, png, gif, bmp
//   phone			- Complete phone numbers inclusive (), . or -; use digit if you want a part of the phone number like tel_areacode
//   phone_inter	- International prefix for phone numbers
//   url			- URL's; possible protocols: http, https, ftp
//   length[]		- [x] = minimum length; [x,x] = a specific length; [x,y] = an interval, x,y inclusive
//   group[]		- Group several Elements together like Street & Street No. [street]
//   begins[]		- If the value begins with a specific character [z]
//   date[]			- Validates a part of a date [day], [month], [year]
// Exceptions rules:
//   noloading		- no loading state will be shown in the status element
//   noerror		- no error state will be shown in the status element
//   nosuccess		- no success state will be shown in the status element
var moo_parent = "";
var moolidator = new Class({
	Implements: Options,
	
	options: {
		trimValue: true,			// Boolean - Delete Whitespaces at the beginning and end of an value
		showErrors: true,			// Boolean - Show Error Messages
		showErrorStyles: true,		// Boolean - Apply the error styles to the input element
		errorClass: 'error',		// String - This class will be attached to the element if it has an error
		errorBigClass: 'big',
		errorMsgLength: 28,			// Integer - How many characters are allowed before a Error Big is used
		errorImage: '/wingame/global/images/error_warning.png',			// String - Path to the Error Image
		errorImageHeight: 22,		// Integer - Height of the Error Image
		errorBigImage: '/wingame/global/images/error_warning_big.png',	// String - Path to the Big Error Image
		errorBigImageHeight: 36,	// Integer - Height of the Big Error Image
		showSuccess: true,			// Boolean - Show Success Messages
		successClass: 'success',	// String - This class will be attached to the element if it is valid
		successImage: '/wingame/global/images/success.gif',				// String - Path to the Succes Image
		showLoading: true,			// Boolean - Show a loading Message
		loadingClass: 'loading',	// String - This class will be attached to the element if it beeing edited
		loadingImage: '/wingame/global/images/loader.gif',				// String - Path to the Loading Image
		loadingImageIE6: '',		// String - Path to the Loading Image without animation for IE6 performance issues
		minYear: 1900,				// Integer - The minimal possible Birthyear
		maxYear: 2011,				// Integer - The maximal possible Birthyear
		fullAge: false,				// Boolean - Test if the user is old enough
		fullAgeYears: 18,			// Integer - How many years a user has to be, if fullAge is true
		statusId: '_status',		// String - Second half of the Status Element ID (firstname_status)
		overrideMoolidatorStyles: false,	// Boolean - Allow mOOlidator to set Free-Choice Status-Styles
		overrideMoolidatorText: false,		// Boolean - Allow mOOlidator to set Error-Text
		getStatusStyles: false,		// Boolean - Does the mOOlidator automatically fetch the CSS Styles from the status element - not implemented yet
		getInputStyles: false,		// Boolean - Automatically fetch the standard CSS Styles from the input element - not implemented yet
		pngFixForIE6: true,			// Boolean - Applies a PNG fix for IE6
		submitButton: 'submit',		// String - Submit-Button ID
		debugConsole: false,		// Boolean - If true, there will be logs in the Firebug Concole
		onSubmitDo: "",				// String - This String will be "eval"ed before submitting
		onErrorSubmitDo: "",		// String - This String will be "eval"ed if errors are existing on submit
		onValidationDo: "",			// String - This String will be "eval"ed before the validation of an value
		afterValidationDo: "",		// String - This String will be "eval"ed after the validation of an value
		overrideSubmit: true,		// Boolean - Prevents the default submit action of the form
		preventSubmit: false,		// Boolean - Prevents submitting the form through mOOlidator
		agbErrorSubmit: false,		// Boolean - If true, mOOlidator will submit after agbErrorSubmitCount tries of submitting without the AGB's checked
		agbErrorSubmitCount: 3,		// Integer - Count for agbErrorSubmit
		
		regexp: {
			required: /[^.*]/,
			alpha: /^[a-zÀ-ÿ ._-]+$/i,				// without special characters: /^[a-z ._-]+$/i
			alphanum: /^[a-z\dÀ-ÿ ._-]+$/i,
			alphanum_pl: /^[a-z\dÀ-ÿĄĘŃŚŹŻÓŁĆąęńśźżół ._-]+$/i,	// PL specific characters
			digit: /^\d+$/,							// with prefix: /^[-+]?[0-9]+$/
			nodigit: /^[^\d]+$/,
			number: /^[-+]?\d*\.?\d+$/,
			email: /^([\da-zA-Z_]+[_\.\-+]*)*[\da-zA-Z_\-+]+\@([\da-zA-Z_\-+]+\.)+[\da-zA-Z_\.\-+]{2,4}$/, // <-- like the one in Perl - RFC: /^(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+@((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6}$/
			image: /.(jpg|jpeg|png|gif|bmp)$/i,
			phone: /^[\d\s ().-]+$/,				// alternate regex: /^[\d\s ().-]+$/,/^((\+\d{1,3}(-| )?\(?\d\)?(-| )?\d{1,5})|(\(?\d{2,6}\)?))(-| )?(\d{3,4})(-| )?(\d{4})(( x| ext)\d{1,5}){0,1}$/
			phone_inter: /^\+{0,1}[\d \(\)\.\-]+$/,
			url: /^(http|https|ftp)\:\/\/[a-z\d\-\.]+\.[a-z]{2,3}(:[a-z\d]*)?\/?([a-z\d\-\._\?\,\'\/\\\+&amp;%\$#\=~])*$/i,
			britishZIP: /^((([a-z]\d)([ _-]?)(\d[a-z][a-z]))|(([a-z]\d\d)([ _-]?)(\d[a-z][a-z]))|(([a-z]\d[a-z])([ _-]?)(\d[a-z][a-z]))|(([a-z][a-z]\d)([ _-]?)(\d[a-z][a-z]))|(([a-z][a-z]\d\d)([ _-]?)(\d[a-z][a-z]))|(([a-z][a-z]\d[a-z])([ _-]?)(\d[a-z][a-z])))$/i,
			wingame_names: /^\D*(\d\D*){1,2}$/i,	// wingame_* rules are checked inverted
			wingame_street: /^[\d\s]+$/,
			wingame_negate_street: /[a-z\d]{3,}/i,	// wingame_negage_* rules are treated like normal regexes
			wingame_negate_streetnr: /\w/,			// <== depricated, use 'alphanum'
			wingame_negate_city: /[a-z\d]{2,}/i,	// <== depricated, use 'alphanum' and 'length[x]'
			wingame_negate_zip: /^\d{4}$/,			// <== depricated, use 'digit' and 'length[x]'
			wingame_street: /^.{0,3}$/,				// <== DK specific
			wingame_street_com: /^\d+$/i,			// <== COM specific
			wingame_street_dk: /^.{0,3}$/,			// <== COM specific
			wingame_negate_street_dk: /[a-z\d]{2,}/i,	// <== DK specific
			wingame_negate_street: /[a-z\d]{2,}/i,	// <== DK specific
			wingame_negate_tel1: /^[1-9]\d{7}$/,	// <== DK specific
			wingame_negate_tel2: /^0\d{6,11}$/,		// <== DK specific
			wingame_negate_tel3: /^\d{8}$/,			// <== DK specific
			wingame_negate_tel4: /^\d{7,}$/,		// <== DK specific
			wingame_negate_tel5: /^[0-9\+]+$/,		// <== COM specific areacode
			wingame_negate_tel_sg: /^[689]\d{7}$/,	// <== SG specific
			wingame_negate_zip_kr: /^\d{3}[ -]?\d{3}$/,								// <== KR specific
			wingame_negate_email1_kr: /^([0-9a-z_]+[_\.\-+]*)*[0-9a-z_\-+]+$/i,		// <== KR specific
			wingame_negate_email2_kr: /^([0-9a-z_\-+]+\.)+[0-9a-z_\.\-+]{2,4}$/i,	// <== KR specific
			wingame_negate_street2_kr: /\d/i,		// <== KR / COM specific
			wingame_negate_street_no: /[a-z\d]{2,}/i,	// <== NO specific
			wingame_negate_streetnr_no: /\d/,		// <== NO specific
			wingame_negate_city_no: /[a-z]{2,}/i,	// <== NO specific
			wingame_negate_zip_12no: /^[0-9]{4}$/, 	// <== NO specific
			wingame_negate_zip_34no: /^[0-9]{5}$/, 	// <== NO specific
			wingame_negate_tel_sg: /^[689]\d{7}$/,	// <== SG specific
			wingame_negate_tel_1no: /^[2345679]\d{7}$/,	// <== NO specific
			wingame_negate_tel_2no: /^[1-9][0-9]{7}$/,	// <== NO specific
			wingame_negate_tel_3no: /^0[0-9]{6,11}$/,	// <== NO specific
			wingame_negate_tel_4no: /^[0-9]{7,}$/,	// <== NO specific
			wingame_negate_zip_nl: /^\d{4}\s?[a-zA-Z]{2}$/, // <== NL specific
			wingame_negate_tel_be: /^0(4\d{8}|[12356789]\d{7})$/ // <== BE specific
		},
		
		statusStyles: {						// Default styles
			visibility: 'visible',			// Show the Status Element
			background: 'none',				// Deletes all Backgrounds
			padding: '4px 5px 0 18px',		// Center the Error Text
			color: '#ffffff',				// Text Color
			width: '135px'					// Width of the element
		},
		
		errorInputStyles: {					// Styles for an Error of an Input Element - by default enabled
			border: '1px solid #ff0000',
			height: '18px'
		},
		
		inputStyles: {						// Styles for no Error of an Input Element - by default enabled
			border: '1px solid #808080',
			height: '18px'
		}
	},
	
	// Constructor
	//   Initializes the mOOlinator and bind the Events
	// Parameter
	//   form = String - id of the html form
	//   options = Array of options
	initialize: function(form, options){
		if(this.form = $(form)) {
			this.setOptions(options);
			this.debugThis("##### mOOlidator initializing...");
			moo_parent = this;
			this.regex = [];
			this.enteredDate = [];
			
			var regex = new Hash(this.options.regexp);
			regex.each(function(el, key) {
				this.regex.push(key);
			}, this);
			
			//if(this.options.getStatusStyles) this.options.statusStyles = this.form.getElements('[class~=status]')[1].getStyles();
			//if(this.options.getInputStyles) this.options.inputStyles = this.form.getElements('*[class*=validate]').filter('[type=text]')[0].getStyles();
			
			if(this.options.overrideSubmit) {
				$(form).addEvent('submit', function(event) {
					event.preventDefault();
					moo_parent.isFormValid();
				});
			}
			
			$(this.options.submitButton).addEvent('click', function() {
				if(moo_parent.isFormValid()) {
					if(moo_parent.options.onSubmitDo != "") eval(moo_parent.options.onSubmitDo);
					if(!moo_parent.options.preventSubmit) moo_parent.form.submit();
				} else {
					if(moo_parent.options.onErrorSubmitDo != "") eval(moo_parent.options.onErrorSubmitDo);
				}
				return false;
			});
			
			this.form.getElements('*[class*=validate]').each(function(el) {
				if(el.get('tag') == 'select' || el.get('tag') == 'input' || el.get('tag') == 'textarea') {
					try {
						// .textContent is needed due to Cross-Browser-Compability issues with .get('text') in Chrome
						if($$('.'+el.get('name')+this.options.statusId)[0].textContent) {
							el.errorMsg = $$('.'+el.get('name')+this.options.statusId)[0].textContent;
						} else {
							el.errorMsg = $$('.'+el.get('name')+this.options.statusId)[0].get('text');
						}
						$$('.'+el.get('name')+this.options.statusId)[0].errorMsg = el.errorMsg;
					} catch(er) {
						el.errorMsg = '';
						this.debugThis("----- Minor error: "+er+"\n----- Error message could not be read");
					}
					if(this.options.agbErrorSubmit && el.get('id') == 'agb') el.submitCount = 0;
					this.setRules(el);
					this.bindEvents(el);
					if($$('.'+el.get('name')+this.options.statusId)) this.debugThis("# Element registered: "+el.get('name')+" - Rules set: "+el.rules+" - Grouped with: "+el.group+" - Status found: true - Error Message: "+el.errorMsg);
					else this.debugThis("# Element registered: "+el.get('name')+" - Rules set: "+el.rules+" - Grouped with: "+el.group+" - Status found: false");
				}
			}, this);
			this.debugThis("##### mOOlidator initializing complete");
		}
	},
	
	// reinitialize - Caution: Overwrites events !!!
	//   If the html form - an input was added - or the rules changed,
	//   you need to trigger this function. This functions will reset
	//   all blur events and trigger the registration of the rules.
	reinitialize: function(){
		this.debugThis("##### mOOlidator reinitializing...");
		//if(this.options.getStatusStyles) this.options.statusStyles = this.form.getElements('[class~=status]')[1].getStyles();
		
		this.form.getElements("*[class*=validate]").each(function(el) {
			if(el.get('tag') == 'select' || el.get('tag') == 'input' || el.get('tag') == 'textarea') {
				try {
					el.removeEvents('blur');
					el.removeEvents('focus');
					el.removeEvents('click');
					el.removeEvents('change');
				} catch(er) {
					this.debugThis("# New element registered: "+el.get('name'));
				}
				try {
					if($$('.'+el.get('name')+this.options.statusId)[0].textContent) {
						el.errorMsg = $$('.'+el.get('name')+this.options.statusId)[0].textContent;
					} else {
						el.errorMsg = $$('.'+el.get('name')+this.options.statusId)[0].get('text');
					}
				} catch(er) {
					el.errorMsg = '';
					this.debugThis("----- Minor error: "+er+"\n----- Error message could not be read");
				}
				this.setRules(el);
				this.bindEvents(el);
				if($$('.'+el.get('name')+this.options.statusId)) this.debugThis("# Element registered: "+el.get('name')+" - Rules set: "+el.rules+" - Grouped with: "+el.group+" - Status found: true");
				else this.debugThis("# Element registered: "+el.get('name')+" - Rules set: "+el.rules+" - Grouped with: "+el.group+" - Status found: false");
			}
		}, this);
		this.debugThis("##### mOOlidator reinitializing complete");
	},
	
	// setRules
	//   The function setRules will register all validation rules for
	//   every element which is to validate.
	// Parameter
	//   el = an DOM element/object (input, textarea, etc.)
	setRules: function(el) {
		el.rules = [];
		el.useThis = false;
		el.getProperty("class").split(' ').each(function(classes) {
			if(classes.match(/^validate(\[.+\])$/)) {
				try {
					var detectedRules = eval(classes.match(/^validate(\[.+\])$/)[1]);
					if(detectedRules.length > 0) el.useThis = true;
					detectedRules.each(function(value, index) {
						if(value.contains("group")) {
							el.group = eval(value.match(/^.+(\[.+\])$/)[1].replace(/([a-z0-9\._-]+)/gi, "'$1'"));
						} else if(value.contains("noloading")) {
							el.noLoading = true;
						} else if(value.contains("nosuccess")) {
							el.noSuccess = true;
						} else if(value.contains("noerror")) {
							el.noError = true;
						} else if(value.contains("deletewhites")) {
							el.deleteWhites = true;
						} else el.rules.push(value);
					});
				} catch(er) {
					this.debugThis("----- Fatal Error: "+er+"\n----- Rules could not be set");
				}
			}
		}, this);
	},
	
	// bindEvents
	//   Bind events on an given element.
	// Parameter
	//   el = an DOM element/object (input, textarea, etc.)
	bindEvents: function(el) {
		this.eventGroupDecision = function() {
			if(el.useThis == true) {
				if(el.group) moo_parent.validateGroup(el);
				else moo_parent.validate(el);
			}
		};
		this.eventValidate = function() {
			if(el.useThis == true) {
				moo_parent.validate(el);
			}
		};
		this.eventLoading = function() {
			if(el.useThis == true) {
				moo_parent.showHighlight(el, "loading");
				moo_parent.showStatus(el, "loading");
			}
		};
		if(el.get('type') == 'text' || el.get('type') == 'password' || el.get('type') == 'hidden' || el.get('tag') == 'textarea') {
			el.addEvent('blur', this.eventGroupDecision);
			el.addEvent('focus', this.eventLoading);
		} else if(el.get('type') == 'radio' || el.get('type') == 'checkbox') {
			el.addEvent('click', this.eventValidate);
		} else if(el.get('tag') == 'select') {
			el.addEvent('change', this.eventGroupDecision);
		}
	},
	
	// setClass
	//   Adds or rewrites the validate class with the specific rules.
	// Parameter
	//   el = String - id of the element for the rules
	//   rules = Array - all rules for that element
	setClass: function(el, rules) {
		el = $(el);
		var newRules = "validate['"+rules.join("','")+"']";
		if(el.get('class').search(/validate(\[.+\])/i) > -1) el.set('class', el.get('class').replace(/validate(\[.+\])/i, newRules));
		else el.addClass(newRules);
		this.setRules(el);
		this.bindEvents(el);
		if(newRules.search(/group/i) > -1) {
			var group = el.group;
			group.push(el.get('name'));
			for(i=0; i<group.length; i++) {
				if($$('[name='+group[i]+']')[0].group) {
					group.combine($$('[name='+group[i]+']')[0].group);
				}
			}
			group.each(function(group_element) {
				var use_element = $$('[name='+group_element+']')[0];
				var group_element_class = use_element.get('class');
				var use_group = $unlink(group);
				use_element.group = use_group;
				use_group.erase(group_element);
				use_element.errorMsg = $$('.'+group_element+moo_parent.options.statusId)[0].errorMsg;
				var use_rules = "validate['"+use_element.rules.join("','")+"','group["+use_group.join(',')+"]']";
				use_element.set('class', use_element.get('class').replace(/validate(\[.+\])/i, use_rules));
			});
		}
		if(el.get('type') == 'text' || el.get('type') == 'password' || el.get('type') == 'hidden' || el.get('tag') == 'textarea') {
			if(el.group) moo_parent.validateGroup(el);
			else moo_parent.validate(el);
		} else if(el.get('type') == 'radio' || el.get('type') == 'checkbox' || el.get('tag') == 'select') {
			moo_parent.validate(el);
		}
		this.debugThis("# Rules changed: "+el.get('name')+" - new Rules: "+el.rules);
	},
	
	// removeClass - alpha status - not yet functioning correctly
	//   Removes the validate class and the rules from the element.
	// Parameter
	//   el = String - id of the element for the rules
	removeClass: function(el) {
		el = $(el);
		try {
			// doesn't remove the events :_(
			/*el.removeEvent('blur', this.eventGroupDecision);
			el.removeEvent('focus', this.eventLoading);
			el.removeEvent('click', this.eventValidate);
			el.removeEvent('change', this.eventValidate);*/
			this.hideStatus(el);
			this.hideHighlight(el);
			el.set('class', el.get('class').replace(/validate(\[.+\])/gi, ""));
			el.rules = [];
			el.isOk = false;
			el.useThis = false;
			if(el.group) el.group = [];
		} catch(er) {
			this.debugThis("----- Fatal Error: "+er+"\n----- Objects (rules, group, etc) could not be deleted");
		}
		this.debugThis("# Rules removed: "+el.get('name'));
	},
	
	// addRulesTo - deprecated; use setClass
	//   Adds one or more rules to an element.
	// Parameter
	//   el = String - id of the element for the rules
	//   rules = Array - all new rules
	addRulesTo: function(el, rules) {
		var result = false;
		try {
			el = $(el);
			rules.each(function(value, index) {
				if(!el.rules.contains(value)) {
					el.rules.push(value);
				}
			});
			this.printRules(el);
			result = true;
			this.debugThis("# Rules added to: "+el.get('name')+" - rules added: "+rules+" - rules: "+el.rules);
		} catch(er) {
			this.debugThis("----- Fatal Error: "+er+"\n----- Rules could not be applied");
		}
		return result;
	},
	
	// removeRulesFrom - deprecated; use setClass (or removeClass)
	//   Removes one or more rules from an element.
	// Parameter
	//   el = String - id of the element for the rules
	//   rules = Array - all rules to be removes
	removeRulesFrom: function(el, rules) {
		var result = false;
		try {
			el = $(el);
			rules.each(function(value, index) {
				if(el.rules.contains(value)) {
					el.rules.erase(value);
				}
			});
			this.printRules(el);
			result = true;
			this.debugThis("# Rules removed from: "+el.get('name')+" - rules removed: "+rules+" - rules: "+el.rules);
		} catch(er) {
			this.debugThis("----- Fatal Error: "+er+"\n----- Rules could not be removed");
		}
		return result;
	},
	
	// validateGroup
	//   If some elements are grouped, all elements have to be valid
	//   in order to get the group valid.
	// Parameter
	//   el = an DOM element/object (input, textarea, etc.)
	validateGroup: function(el) {
		var result = true;
		this.debugThis("### Group start - Elements: "+el.get('name')+","+el.group);
		
		result = this.validate(el);
		for(var i=0; i<el.group.length; i++) {
			if(this.validate(moo_parent.form.getElement('[name='+el.group[i]+']')) && result) result = true;
			else result = false;
		}
		el.group.isOk = result;
		for(var i=0; i<el.group.length; i++) {
			moo_parent.form.getElement('[name='+el.group[i]+']').group.isOk = result;
		}
		
		if(el.group) {
			el.group.each(function(name) {
				if(moo_parent.form.getElement('[name='+name+']').isOk) moo_parent.showHighlight(moo_parent.form.getElement('[name='+name+']'), "success");
				else moo_parent.showHighlight(moo_parent.form.getElement('[name='+name+']'), "error");
				if(moo_parent.form.getElement('[name='+name+']').group.isOk) moo_parent.showStatus(moo_parent.form.getElement('[name='+name+']'), "success");
				else moo_parent.showStatus(moo_parent.form.getElement('[name='+name+']'), "error");
			});
		}
		
		this.debugThis("### Group end - is valid: "+el.group.isOk);
		
		return result;
	},
	
	// validate
	//   validate decides how a value will be validated and delegates
	//   the value to the specific validation function.
	// Parameter
	//   el = an DOM element/object (input, textarea, etc.)
	validate: function(el) {
		try {
			el.errors = [];
			el.isOk = false;
			var result = false;
			
			if(this.options.trimValue && el.value) el.value = el.value.trim(); // trim whitespaces if the option is set
		
			if(el.rules.length == 0) result = true;
			else if(moo_parent.onValidationDo != "") eval(moo_parent.options.onValidationDo);
			el.rules.each(function(rule) {
				var ruleArgs = [];
				if(rule.match(/^.+\[/)) {
					var ruleMethod = rule.split('[')[0];
					ruleArgs = eval(rule.match(/^.+(\[.+\])$/)[1].replace(/([a-zA-Z0-9\._-]+)/g, "'$1'"));
				} else var ruleMethod = rule;
				
				if((el.get('type') == "radio" || el.type == "checkbox") && ruleMethod == "required") {
					if(this.validateRadioAndCheck(el)) result = true;
				} else if(el.get('tag') == "select" && ruleMethod == "required") {
					if(this.validateSelect(el)) result = true;
				} else if(el.get('type') == "text" || el.type == "password" || el.get('type') == 'hidden' || el.get('tag') == "textarea") {
					if(this.regex.contains(ruleMethod)) {
						if(this.validateWithRegex(el, ruleMethod)) result = true;
					} else if(ruleMethod == 'date') {
						if(this.validateDate(el, ruleArgs)) result = true;
					} else {
						if(this.validateOther(el, ruleMethod, ruleArgs)) result = true;
					}
				}
				this.debugThis("# Element validated: "+el.get('name')+" - Rules: "+ruleMethod+" - Rule Arguments: "+ruleArgs+" - Error Message: "+el.errorMsg+" - Rule passed: "+result);
			}, this);
			if(result && el.errors.length == 0) el.isOk = true;
			if(el.errors.length > 0 && moo_parent.afterValidationDo != "") eval(moo_parent.options.afterValidationDo);
			
			if(el.isOk && this.options.showSuccess) {
				this.showHighlight(el, "success");
				this.showStatus(el, "success");
			}
			if(!el.isOk && this.options.showErrors) {
				this.showHighlight(el, 'error');
				this.showStatus(el, "error");
			}
		} catch(er) {
			this.debugThis("----- Fatal Error: "+er+"\n----- No rules set");
		}
		
		return el.isOk;
	},
	
	// validateOther
	//   This is the function for alle other rules.
	// Parameter
	//   el = an DOM element/object (input, textarea, etc.)
	//   ruleMethod = String - name of the rule method ('alphanum')
	//   ruleArgs = Int - parameter for special validation rules ('length[1,6]');
	//              the parameter are both inclusive
	validateOther: function(el, ruleMethod, ruleArgs) {
		var result = false;
		
		if(ruleMethod == "length") {
			if(ruleArgs[1]) {
				try {
					ruleArgs[0] = parseInt(ruleArgs[0], 10);
					ruleArgs[1] = parseInt(ruleArgs[1], 10);
				} catch(er) {
					this.debugThis("----- Fatal Error: "+er+"\n----- Length arguments are no digits");
				}
				if(ruleArgs[0] == ruleArgs[1]) {
					if(el.value.length == ruleArgs[0]) result = true;
				} else if(ruleArgs[0] < ruleArgs[1]) {
					if(el.value.length >= ruleArgs[0] && el.value.length <= ruleArgs[1]) result = true;
				}
			} else if(ruleArgs[0]) {
				try {
					ruleArgs[0] = parseInt(ruleArgs[0], 10);
				} catch(er) {
					this.debugThis("----- Fatal Error: "+er+"\n----- Length arguments are no digits");
				}
				if(el.value.length >= ruleArgs[0]) result = true;
			}
		} else if(ruleMethod == 'begins') {
			if(el.value.charAt(0) == ruleArgs) result = true;
		}
		if(!result && (ruleMethod == "length" || ruleMethod == "begins")) el.errors.push(ruleMethod);
		
		return result;
	},
	
	// validateWithRegex
	//   This is the validation function for alle regex validations.
	// Parameter
	//   el = an DOM element/object (input, textarea, etc.)
	//   ruleMethod = String - name of the rule method ('alphanum')
	//   ruleArgs = Int - parameter for special validation rules ('length[1,6]')
	//              the parameter are both inclusive
	validateWithRegex: function(el, ruleMethod) {
		var result = false;
		
		if(ruleMethod.contains("wingame_") && !ruleMethod.contains("wingame_negate_")) {
			if(!this.options.regexp[ruleMethod].test(el.value) && el.value.length > 0) result = true;
		} else {
			if(this.options.regexp[ruleMethod].test(el.value)) result = true;
		}
		if(!result) el.errors.push(ruleMethod);
		
		return result;
	},
	
	// validateSelect
	//   If a selectbox validation is needed, this function will
	//   check for a value or a selected index != "".
	// Parameter
	//   el = an DOM element/object (select)
	validateSelect: function(el) {
		var result = false;
		
		//if(el.selectedIndex <= 0 || el.value == "") el.errors.push("select");
		if(el.value == "") el.errors.push("select");
		else result = true;
		
		return result;
	},
	
	// validateRadioButtons
	//   In the case of a radio button group, validateRadioButtons
	//   will check if one radio button is checked.
	// Parameter
	//   el = an DOM element/object (input type radio)
	validateRadioAndCheck: function(el) {
		var result = false;
		
		this.form.getElements('[name='+el.get("name")+']').each(function(element) {
			if(element.checked) result = true;
		});
		if(!result) el.errors.push(el.get('type'));
		
		return result;
	},
	
	// validateDate
	//   Decides if a day, month or year will be validated.
	// Parameter
	//   el = an DOM element/object (input, textarea, etc.)
	//   ruleArgs = String - Type of date validation (day,month,year)
	validateDate: function(el, ruleArgs) {
		var result = false;
		var today = new Date();
		
		try {
			if(ruleArgs == 'year') {
				this.enteredDate[2] = parseInt(el.value,10);
				if(this.isYearValid(today)) result = true;
				if(!result) el.errors.push("year");
			}
			if(ruleArgs == 'month') {
				this.enteredDate[1] = parseInt(el.value,10);
				if(this.isMonthValid(today)) result = true;
				if(!result) el.errors.push("month");
			}
			if(ruleArgs == 'day') {
				this.enteredDate[0] = parseInt(el.value,10);
				if(this.isDayValid(today)) result = true;
				if(!result) el.errors.push("day");
			}
		} catch(er) {
			this.debugThis("----- Fatal Error: "+er+"\n----- Entered values might not be digits");
		}
		
		return result;
	},
	
	// isDayValid
	//   Checks if the entered day is valid for the specific
	//   month. Even leap years and that the date has to be
	//   in the past is considered.
	// Parameter
	//   today = Date - a date object to determine the actual date
	isDayValid: function(today) {
		var result = false;
		if(this.enteredDate[0] >= 1 && this.enteredDate[0] <= 31) {
			if(this.enteredDate[1] == 1 || this.enteredDate[1] == 3 || this.enteredDate[1] == 5 || this.enteredDate[1] == 7 || this.enteredDate[1] == 8 || this.enteredDate[1] == 10 || this.enteredDate[1] == 12 ) result = true;
			if(this.enteredDate[0] <= 30 && (this.enteredDate[1] == 4 || this.enteredDate[1] == 6 || this.enteredDate[1] == 9 || this.enteredDate[1] == 11)) result = true;
			if(this.enteredDate[0] <= 28 && this.enteredDate[1] == 2) result = true;
			if(this.enteredDate[0] == 29 && (this.enteredDate[2] % 4 == 0 && (!(this.enteredDate[2] % 100 == 0) || this.enteredDate[2] % 400 == 0))) result = true;
		}
		if(this.options.fullAge) {
			if(this.enteredDate[2] == (today.getFullYear()-this.options.fullAgeYears) && this.enteredDate[1] == (today.getMonth()+1) && this.enteredDate[0] > today.getDate()) result = false;
		} else {
			if(this.enteredDate[2] == today.getFullYear() && this.enteredDate[1] == (today.getMonth()+1) && this.enteredDate[0] > today.getDate()) result = false;
		}
		
		return result;
	},
	
	// isMonthValid
	//   Checks if the entered month is valid and in the past
	//   or today.
	// Parameter
	//   today = Date - a date object to determine the actual date
	isMonthValid: function(today) {
		var result = false;
		if(this.enteredDate[1] >= 1 && this.enteredDate[1] <= 12) result = true;
		if(this.options.fullAge) {
			if(this.enteredDate[2] == (today.getFullYear()-this.options.fullAgeYears) && this.enteredDate[1] > (today.getMonth()+1)) result = false;
		} else {
			if(this.enteredDate[2] == today.getFullYear() && this.enteredDate[1] > (today.getMonth()+1)) result = false;
		}
		
		return result;
	},
	
	// isYearValid
	//   Checks if the entered Year is valid and in the past
	//   or today.
	// Parameter
	//   today = Date - a date object to determine the actual date
	isYearValid: function(today) {
		var result = false;
		if(this.enteredDate[2] >= this.options.minYear && this.enteredDate[2] <= this.options.maxYear && this.enteredDate[2] <= today.getFullYear()) {
			if(this.options.fullAge) {
				if(this.enteredDate[2] <= (today.getFullYear()-this.options.fullAgeYears)) result = true;
			} else result = true;
		}
		
		return result;
	},
	
	// isFormValid
	//   Triggered from the submit button, isFormValid will
	//   make a final check of all elements.
    isFormValid: function() {
		this.debugThis("##### isFormValid checking...");
		var error_count = 0;
		var result = false;
		
		this.form.getElements('*[class*=validate]').each(function(el) {
			if(el.get('tag') == 'select' || el.get('tag') == 'input' || el.get('tag') == 'textarea') {
				if(el.group) this.validateGroup(el);
				else this.validate(el);
				if(!el.isOk) {
					error_count += el.errors.length;
				}
			}
		}, this);
		
		if(error_count == 0) result = true;
		else result = false;
		
		// Form is Valid with an AGB Error for the agbErrorSubmitCount
		if(this.options.agbErrorSubmit && error_count == 1) {
			if($('agb').errors.length == 1) {
				$('agb').submitCount++;
				if($('agb').submitCount == this.options.agbErrorSubmitCount) result = true;
			}
		}
		
		this.debugThis("##### isFormValid checking complete - Errors occured: "+error_count+" - Form is valid: "+result);
		
		return result;
	},
	
	// printRules
	//   Changes the validate class of an element, if addRulesTo or
	//   removeRulesFrom is used.
	// Parameter
	//   el = an DOM element/object (input, textarea, etc.)
	printRules: function(el) {
		var result = false;
		try {
			var validateClasses = $(el).get('class');
			validateClasses = validateClasses.replace(/validate\['.*'\]/i, "validate['"+el.rules.join("','")+"']");
			$(el).set('class', validateClasses);
		} catch(er) {
			this.debugThis("----- Fatal Error: "+er+"\n----- The validate class couldn't be updated.");
		}
		return result;
	},
	
	// getNumberOfHeight
	//   Returns the height out of "18px" as a Integer.
	// Parameter
	//   height - String, "18px"
	getNumberOfHeight: function(height) {
		return parseInt(height.split("px"));
	},
	
	// showHighlight
	//   Show the Error-Styles on the element with an error or removes them.
	// Parameter
	//   el = an DOM element/object (input, textarea, etc.)
	//   type = String - the state of the Highlight, adds the specific class
	//          to the element (loading, success, error)
	showHighlight: function(el, type) {
		if(el.useThis) {
			if(!this.options.overrideMoolidatorStyles && el.get('type') != 'radio' && el.get('type') != 'checkbox') el.setStyles(this.options.inputStyles);
			if(!this.options.overrideMoolidatorStyles && el.get('type') == 'select-one') el.setStyle('height',this.getNumberOfHeight(this.options.inputStyles.height)+2);
			if(type == "loading") {
				el.removeClass(this.options.successClass);
				el.removeClass(this.options.errorClass);
				el.addClass(this.options.loadingClass);
			}
			if(type == "success") {
				el.removeClass(this.options.loadingClass);
				el.removeClass(this.options.errorClass);
				el.addClass(this.options.successClass);
			}
			if(type == "error") {
				if(!this.options.overrideMoolidatorStyles && this.options.showErrorStyles && el.get('type') != 'radio' && el.get('type') != 'checkbox' && !el.noError) el.setStyles(this.options.errorInputStyles);
				if(el.get('type') == 'select-one') el.setStyle('height',this.getNumberOfHeight(this.options.errorInputStyles.height)+2);
				el.removeClass(this.options.loadingClass);
				el.removeClass(this.options.successClass);
				el.addClass(this.options.errorClass);
			}
		}
	},
	
	// hideHighlight
	//   Removes all Error-Styles from the specific element.
	// Parameter
	//   el = an DOM element/object (input, textarea, etc.)
	hideHighlight: function(el) {
		if(el.useThis) {
			if(!this.options.overrideMoolidatorStyles && el.get('type') != 'radio' && el.get('type') != 'checkbox') el.setStyles(this.options.inputStyles);
			if(!this.options.overrideMoolidatorStyles && el.get('type') == 'select') el.setStyle('height',this.options.inputStyles.height+2);
			el.removeClass(this.options.loadingClass);
			el.removeClass(this.options.successClass);
			el.removeClass(this.options.errorClass);
		}
	},
	
	// showStatus
	//   Adds the state classes (loading, success, error by default) to the
	//   status element and shows the state action (loading icon etc.) if
	//   they're enabled.
	// Parameter
	//   el = an DOM element/object (input, textarea, etc.)
	//   type = String - the state of the Highlight, adds the specific class
	//          to the element (loading, success, error)
	showStatus: function(el, type) {
		try {
			if(el.useThis) {
				if(this.options.showErrors || this.options.showLoading || this.options.showSuccess) {
					if((type == "loading" && !el.noLoading) || (type == "success" && !el.noSuccess) || (type == "error" && !el.noError)) {
						if(!this.options.overrideMoolidatorStyles) {
							if(this.isIE6() && this.options.pngFixForIE6) {
								$$('.'+el.get('name')+this.options.statusId)[0].setStyles({
									filter: "none"
								});
							}
							$$('.'+el.get('name')+this.options.statusId)[0].setStyles(this.options.statusStyles);
							$$('.'+el.get('name')+this.options.statusId)[0].setStyle('height', this.options.errorImageHeight);
						}
						$$('.'+el.get('name')+this.options.statusId)[0].setStyle('visibility', 'visible');
					} else this.hideStatus(el);
				}
				if(type == "loading") {
					$$('.'+el.get('name')+this.options.statusId)[0].removeClass(this.options.successClass);
					$$('.'+el.get('name')+this.options.statusId)[0].removeClass(this.options.errorClass);
					$$('.'+el.get('name')+this.options.statusId)[0].removeClass(this.options.errorBigClass);
					$$('.'+el.get('name')+this.options.statusId)[0].addClass(this.options.loadingClass);
					if(this.options.showLoading) {
						if(!this.options.overrideMoolidatorText) $$('.'+el.get('name')+this.options.statusId)[0].set('html',"");
						if(!this.options.overrideMoolidatorStyles) {
							if(this.options.loadingImageIE6.length > 0 && this.isIE6()) $$('.'+el.get('name')+this.options.statusId)[0].setStyle('background','transparent url('+this.options.loadingImageIE6+') no-repeat left center');
							else $$('.'+el.get('name')+this.options.statusId)[0].setStyle('background','transparent url('+this.options.loadingImage+') no-repeat left center');
						}
					} else this.hideStatus(el);
				}
				if(type == "success") {
					$$('.'+el.get('name')+this.options.statusId)[0].removeClass(this.options.loadingClass);
					$$('.'+el.get('name')+this.options.statusId)[0].removeClass(this.options.errorClass);
					$$('.'+el.get('name')+this.options.statusId)[0].removeClass(this.options.errorBigClass);
					$$('.'+el.get('name')+this.options.statusId)[0].addClass(this.options.successClass);
					if(this.options.showSuccess) {
						if(!this.options.overrideMoolidatorText) $$('.'+el.get('name')+this.options.statusId)[0].set('html',"");
						if(!this.options.overrideMoolidatorStyles) $$('.'+el.get('name')+this.options.statusId)[0].setStyle('background','transparent url('+this.options.successImage+') no-repeat left center');
					} else this.hideStatus(el);
				}
				if(type == "error") {
					$$('.'+el.get('name')+this.options.statusId)[0].removeClass(this.options.loadingClass);
					$$('.'+el.get('name')+this.options.statusId)[0].removeClass(this.options.successClass);
					$$('.'+el.get('name')+this.options.statusId)[0].addClass(this.options.errorClass);
					if(el.errorMsg.length > this.options.errorMsgLength) $$('.'+el.get('name')+this.options.statusId)[0].addClass(this.options.errorBigClass);
					if(this.options.showErrors) {
						if(!this.options.overrideMoolidatorText) $$('.'+el.get('name')+this.options.statusId)[0].set('html',el.errorMsg);
						if(el.errorMsg.length > this.options.errorMsgLength) {
							if(!this.options.overrideMoolidatorStyles) {
								$$('.'+el.get('name')+this.options.statusId)[0].setStyles({
									background: 'transparent url('+this.options.errorBigImage+') no-repeat left center',
									height: this.options.errorBigImageHeight
								});
								if(this.isIE6() && this.options.pngFixForIE6) {
									$$('.'+el.get('name')+this.options.statusId)[0].setStyles({
										filter: "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+this.options.errorBigImage+"', sizingMethod='crop')",
										background: "none"
									});
								}
							}
						} else {
							if(!this.options.overrideMoolidatorStyles) {
								$$('.'+el.get('name')+this.options.statusId)[0].setStyles({
									background: 'transparent url('+this.options.errorImage+') no-repeat left center'
								});
								if(this.isIE6() && this.options.pngFixForIE6) {
									$$('.'+el.get('name')+this.options.statusId)[0].setStyles({
										filter: "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+this.options.errorImage+"', sizingMethod='crop')",
										background: "none"
									});
								}
							}
						}
					} else this.hideStatus(el);
				}
			}
		} catch(er) {
			this.debugThis("----- Minor error: "+er+"\n----- Status element does not exist");
		}
	},
	
	// hideStatus
	//   Removes the state classes (loading, success, error by default) from
	//   the status element.
	// Parameter
	//   el = an DOM element/object (input, textarea, etc.)
	hideStatus: function(el) {
		try {
			if(el.useThis) {
				$$('.'+el.get('name')+this.options.statusId)[0].removeClass('success');
				$$('.'+el.get('name')+this.options.statusId)[0].removeClass('error');
				$$('.'+el.get('name')+this.options.statusId)[0].removeClass('loading');
				$$('.'+el.get('name')+this.options.statusId)[0].setStyle('visibility','hidden');
			}
		} catch(er) {
			this.debugThis("----- Minor error: "+er+"\n----- Status element does not exist");
		}
	},
	
	// isIE6
	//   Detects the IE6 User Agent.
	isIE6: function() {
		if(navigator.userAgent.search(/MSIE 6\.0/) > -1) return true;
		else return false;
	},
	
	// debugThis
	//   Pushes error messages to the firebug console if enabled.
	// Parameter
	//   message = String - the complete error message
	debugThis: function(message) {
		try {
			if(this.options.debugConsole) {
				console.log(message);
			}
		} catch(er) {
			// there is no console, all error messages are lost for eternity...
		}
	}
});
