/**
 * @fileDescriptor <p>Beinhaltet alle ben&ouml;tigten Klassen f&uuml;r 
 * 	Formularvalidierung.</p>
 * <p>
 * Die Validierung von Formularen kann flexibel erweitert bzw. 
 * zusammengestellt werden da regeln welche &uuml;ber RegEx gepr&uuml;ft werden 
 * k&ouml;nnen in einer Erweiterung der ValidatorRules implementiert werden 
 * k&ouml;nnen.
 * </p>
 * <p>
 *  
 * @requires Prototype JS(min Version 1.6.3)
 * @version $Id: FormValidator.js 11280 2010-06-22 07:33:31Z marlettas $
 * @namespace Form
 */
/**
 * Form.Validator , Klasse zum Validieren von Formularen.
 * 
 * @author hutterm & marlettas
 */
/**
 * Change Log:
 *
 * +------------+-------+-----------------------------------------------------+
 * | 09.11.2009 | DB 	| Prüfung um Selects und Textareas erweitert		  |
 * +------------+-------+-----------------------------------------------------+
 * | 10.12.2009 | SMA 	| Erweiterung der Textprüfung um äöüÄÖÜß			  |
 * +------------+-------+-----------------------------------------------------+
 * | 18.05.2010 | DB	| Bug, wenn nächstes Element nicht vorhanden,		  |
 * |			|		| entfernen											  |
 * +------------+-------+-----------------------------------------------------+
 */
Form.Validator = Class.create({
	/** @lends Form.Validator */
	/**
	 * Das Formular das gepr&uuml;ft werden soll.
	 * @type Form
	 * @access protected
	 */
	element: false,
	/**
	 * Das verwendete regelwerk, ggf von Form.ValidatorRules ableiten um eigene zu impl.
	 * @type Form.ValidatorRules
	 * @access protected
	 */
	rules: false,
	/**
	 * beinhaltet das Ergebnis des aktuellen Validierungsdurchlaufs.
	 * @type bool
	 * @access protected
	 */
	validationResult: null, 
	/**
	 * Wird vom Konstruktor automatisch aufgerufen.
	 * @param Form.ValidatorOptions validatorOptions Die zu verwendenden 
	 * Einstellungen
	 * @access public
	 */
	initialize: function(validatorOptions) {
		if(!validatorOptions)
			throw("NullException: validatorOptions could not be null.");
			
		if(!validatorOptions.element)
			throw("NullException: Element could not be null.");
			
		this.element = validatorOptions.element;
		this.beforeValidate = validatorOptions.beforeValidate;
		this.afterValidate = validatorOptions.afterValidate;
		
		this.element.observe("submit", this.submit.bind(this));
		
		this.rules = new Form.ValidatorRules();
	},
	/**
	 * Wird ausgel&ouml;st wenn das Formular versucht zu submitten.
	 * @access protected
	 * @event submit
	 */
	submit: function(submitEvent) {
		var valid = this.validate();
		
		if(!valid || this.afterValidate) {
			submitEvent.stop();
			valid = false;
		}
		
		return valid;
	},
	/**
	 * Wird von submit augfgerufen, wenn das Formular gepr&uuml;ft werden muss.
	 * @access protected
	 */
	validate: function() {
		if(this.beforeValidate)
			this.beforeValidate(this);
		
		this.validationResult = true;
		this.removeAllErrors();
		
		// Pflichtfeldprüfung(text, password, select & textarea)
		this.element.select(
			".mandatory[type=text], .mandatory[type=password], select[class~=mandatory], textarea[class~=mandatory]"
		).each(function(required) {
			var value = required.getValue();
			if(!value || value.length <= 0) {
				var errMessage = required.readAttribute("errorMessage");
				if(!errMessage || errMessage.empty())
					errMessage = "Dieses Feld muss ausgef&uuml;llt werden.";
				
				this.addErrorBox(required, errMessage);
				this.validationResult = false;
			}
		}.bind(this));
		
		// Checkboxprüfung
		this.element.select(".mandatory[type=checkbox]").each(function(required) {
			if(!required.checked) {
				var errMessage = required.readAttribute("errorMessage");
				if(!errMessage || errMessage.empty())
					errMessage = "Diese Box muss angehakt werden.";
				
				this.addErrorBox(required, errMessage);
				this.validationResult = false;
			}
		}.bind(this));
		
		// Prüfung anhand der Prüfregeln
		Object.keys(this.rules).each(function(ruleKey) {
			this.element.select("." + this.rules[ruleKey].className).each(function(ruleElement) {
				var value = ruleElement.getValue();
				if(value && !ruleElement.hasClassName(".mandatory")) {
					if(!value.match(this.rules[ruleKey].regExp)) {
						var errMessage = ruleElement.readAttribute("errorMessage");
						if(!errMessage || errMessage.empty())
							errMessage = this.rules[ruleKey].defaultMessage;
						
						this.addErrorBox(ruleElement, errMessage);
						this.validationResult = false;
					}
				}
			}.bind(this));
		}.bind(this));
		
		if(this.afterValidate)
			this.afterValidate(this);
		
		return this.validationResult;
	},
	/**
	 * Erlaubt es ein eigenes Regelwerk zu setzen um customized Regeln zu
	 * erlauben.
	 * @param Form.ValidatorRules rules das neue zu verwendende Regelwerk.
	 * @access public
	 */
	setRules: function(rules) {
		if(!rules)
			throw("NullException: Rules could not be null.");
			
		this.rules = rules;
	},
	/**
	 * F&uuml;gt eine Fehlermeldungsbox zum entsprechenden Element hinzu
	 * @access protected
	 * @param Element element das betroffene Element
	 * @param String message die anzuzeigende Nachricht
	 */
	addErrorBox: function(element, message) {
		var errorBox = new Element("li");
		errorBox.addClassName("errorBox");
		errorBox.update(message);
		
		element.addClassName("invalid");
		
		if(!element.next() || !element.next().hasClassName("errorList")) {
			var ul = new Element("ul");
			ul.addClassName("errorList");
			element.insert({
				"after": ul
			});
		}
		
		element.next().insert({
			"bottom": errorBox
		});
	},	
	/**
	 * Entfernt alle Fehlermeldungsboxen
	 * @access protected
	 */
	removeAllErrors: function() {
		this.element.select(".errorList").invoke("remove");
		this.element.select(".invalid").invoke("removeClassName", "invalid");
	},
	/**
	 * Liefert das Validierungsergebenis zurück.
	 * @return bool Ergensi der aktuellen Validierung.
	 * @access public
	 */
	getResult: function() {
		return this.validationResult;
	}
});

/**
 * ValidatorOptions Schnittstelle - legt fest welche Optionen f&uuml;r die 
 * Validierung.
 *
 * @author hutterm & marlettas
 */
Form.ValidatorOptions = Class.create({
	/** @lends Form.ValidatorOptions */
	/**
	 * Das Formularelement das validiert werden soll.
	 * @type Form
	 * @access public
	 */
	element: false,
	/**
	 * Wird aufgerufen bevor die Validierung anläuft.
	 * @param Form.Validator self
	 */
	beforeValidate: false,
	/**
	 * Wird aufgerufen nach dem die Validierung durchgeführt wurde.
	 * 
	 * Ergebnis der Validierung kann über getResult abgeholt werden.
	 *
	 * Sobald afterValidate iniziert wurde wird kein submit mehr ausgelöst.
	 * 
	 * @param Form.Validator self
	 */
	afterValidate: false
});

/**
 * Beinhaltet die Validierungsregeln, kann optional implementiert werden.
 * <p>
 * Von dieser Klasse k&ouml;nnen Sie eigene Ableitung schreiben und somit 
 * Projektspezifische Pr&uuml;fregeln innerhalb des entsprechenden Projekts
 * hinzufügen. Dies kann &uuml;ber zwei Wege 
 * </p>
 * @author hutterm & marlettas
 */
Form.ValidatorRules = Class.create({
	/** @lends Form.ValidatorRules */
	numeric: {
		className: "numeric",
		defaultMessage: "Dieses Feld darf nur Zahlen beinhalten. Mit Vorzeichen und Komma.",
		regExp: /^[-]?\d+(\,\d+)?$/
	},
	digits: {
		className: "digits",
		defaultMessage: "Dieses Feld darf nur Zahlen beinhalten.",
		regExp: /^[-]?\d+(\.\d+)?$/
	},
	email: {
		className: "email",
		defaultMessage: "Dieses Feld darf nur eine g&uuml;ltige E-Mail Adresse beinhalten.",
		regExp: /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/
	},
	alpha: {
		className: "alpha",
		defaultMessage: "Dieses Feld darf nur Buchstaben beinhalten.",
		regExp: /^[a-zA-Z\u00E4\u00C4\u00F6\u00D6\u00FC\u00DC\u00DF]{1,}$/
	},
	alphanumeric: {
		className: "alphanumeric",
		defaultMessage: "Dieses Feld darf nur Buchstaben und Zahlen beinhalten.",
		regExp: /^[a-zA-Z0-9]+$/
	}
});

/**
 * Schnittstelle zur Implementierung einer einzelnen Regel für ein 
 * Regelwerk.
 * <p>
 * Regeln k&ouml;nnen zur Laufzeit zu einem bestehenden Regelwerk hinzugefügt 
 * werden oder in einem eigen Regelwerk schon vorher definiert werden. Beispiele 
 * finden sie hier: {@link Form.ValidatorRules}.
 * </p>
 * @example
 * Der lange Weg:
 * <code>
 * var rule = new ValidatorRule();
 * rule.className = "alpha";
 * rule.defaultMessage = "Dieses Feld darf nur Buchstaben beinhalten..";
 * rule.regExp = /^[a-zA-z\s]+$/;
 * </code>
 * Oder die Inline-Abkürzung:
 * <code>
 * var rule = {
 * 		className: "alpha",
 * 		defaultMessage: "Dieses Feld darf nur Buchstaben beinhalten..",
 * 		regExp: /^[a-zA-z\s]+$/
 * };
 * </code>
 * @author marlettas
 */
Form.ValidatorRule = Class.create({
	/** @lends Form.ValidatorRule */
	/**
	 * Der CSS Klassenname innerhalb des Formulars auf welchen die
	 * Regel reagieren muss.
	 * @type String
	 * @access public
	 */
	className: false,
	/**
	 * Die Nachricht die angezeigt werden soll, wenn die Regel nicht
	 * auf das enstprechende feld angewendet werden kann. 
	 * @type String
	 * @access public
	 */
	defaultMessage: false,
	/**
	 * Der regul&auml;re Ausdruck mit welchem der feldinhalt gepr&uuml;ft werden
	 * soll.
	 * @type String
	 * @access public
	 */
	regExp: false
});