/* 
 * Attribute:
 *
 * Trennzeichen bei mehreren Werten: "|" = Oder, "," = Und
 *
 * v-validate="true|false":
 * Legt fest ob das Feld validiert werden soll.
 *
 * v-required="true|false":
 * Eingabe obligatorisch (leerer Wert nicht erlaubt).
 *
 * v-match="alphabetic|name|numeric|email|url|/regex/":
 * Legt das Format der Eingabe fest.
 * 
 * v-purge="html|spaces|chars|numbers":
 * Entfernt die angegebenen Inhalte aus der Eingabe.
 *
 * v-classes="error:class[{selector}]|ok:class[{selector}]":
 * Legt die anzuzeigenden Klassen fuer den Fehler- und Ok-Fall fest.
 * "error" bzw. "ok" beschreiben den Anwendungsfall, "{selector}" ist ein
 * optionaler jQuery-kompatibler (also CSS-kompatibler) Selektor fuer das
 * Element, welches die durch "class" spezifizierte Klasse erhalten soll.
 * Wichtig: Der Selektor wird IMMER nach "aussen" angewandt, d.h. es wird in den
 * Elternelementen des angesprochenen Formularelements danach gesucht.
 *
 * v-message="message":
 * Legt die Fehlermeldung fest, die im Fehlerfall angezeigt werden soll.
 */

(function($){
	var options = {
		errorMessageClass : "v-error-message",
		animate           : true,
		animateFading     : true,
		animateSliding    : true,
		animationDuration : 250
	};

	$.fn.validator = function(_options) {
		options = $.extend(true, options, _options);

		return this.each(function () {
			var dataClassConf = $.data(this, "classConf");
			var classConf = null;

			if(dataClassConf) {
				classConf = dataClassConf;
			} else {
				classConf = parseClassesConfig(this);
				$.data(this, "classConf", classConf);
			}

			$(this).bind("blur", null, function() {
				if($(this).attr($.fn.validator.attributes.validate) == "true") {
					if(!validate(this)) {
						setState(this, "error", options);
					} else {
						setState(this, "ok", options);
					}
				}
			})
		});
	}

	$.fn.isValid = function() {
		var isValid = true;

		this.each(function () {
			var dataClassConf = $.data(this, "classConf");
			var classConf = null;

			if(dataClassConf) {
				classConf = dataClassConf;
			} else {
				classConf = parseClassesConfig(this);
				$.data(this, "classConf", classConf);
			}

			if($(this).attr($.fn.validator.attributes.validate) == "true") {
				if(!validate(this)) {
					setState(this, "error", options);
					isValid = false;
				}
			}
		});

		return isValid;
	}

	$.fn.validator.attributes = {
		validate : "v-validate",
		required : "v-required",
		match    : "v-match",
		purge    : "v-purge",
		classes  : "v-classes",
		message  : "v-message"
	}

	var domainChars = '[^-]{1,1}[àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿāăąćĉċčďđēĕėęěĝğ' +
	                  'ġģĥħĩīĭįıĵķĺļľŀłńņňŉŋōŏőœŕŗřśŝšťŧũūŭůųŵŷźżžșțΐάέήίΰαβγδεζ' +
	                  'ηθικλμνξοπρςστυφχψωϊϋόύώабвгдежзийклмнопрстуфхцчшщъыьэюяἀ' +
	                  'ἁἂἃἄἅἆἇἐἑἒἓἔἕἠἡἢἣἤἥἦἧἰἱἲἳἴἵἶἷὀὁὂὃὄὅὐὑὒὓὔὕὖὗὠὡὢὣὤὥὦὧὰάὲέὴή' +
	                  'ὶίὸόὺύὼώᾀᾁᾂᾃᾄᾅᾆᾇᾐᾑᾒᾓᾔᾕᾖᾗᾠᾡᾢᾣᾤᾥᾦᾧᾰᾱᾲᾳᾴᾶᾷῂῃῄῆῇῐῑῒΐῖῗῠῡῢΰῤῥῦ' +
	                  'ῧῲῳῴῶῷa-z0-9.-]+[^-]{1,1}';

	$.fn.validator.regex = {
		purge : {
			html    : /<[^>]*>/g,
			spaces  : /\s+/g,
			chars   : /[a-zA-Z]+/g,
			numbers : /[0-9]+/g
		},
		match : {
			alphabetic : '/^[a-z]+$/i',
			name       : '/^[a-z0-9._\s-]+$/i',
			numeric    : '/^[0-9]+$/',
			email      : '/^[A-Za-z0-9.!#$%&\'*+\/=?^_`{|}~-]+@' + domainChars + '\.[a-z]{2,10}$/i',
			url        : '/^(ftp|http|https):\/\/' + domainChars + '\.[a-z]{2,10}(\/.*)?$/i'
		}

		/*

		 */
	}

	function setState(obj, state, options) {
		var classConf = $.data(obj,"classConf");
		var $obj = $(obj);

		var $errorTarget = $obj;
		var $okTarget = $obj;

		if(classConf.error.selector != null) {
			$errorTarget = $errorTarget.closest(classConf.error.selector);
		}

		if(classConf.ok.selector != null) {
			$okTarget = $okTarget.closest(classConf.ok.selector);
		}

		var messageID = 'error_for_' + $obj.attr("name");

		if(state == "error") {
			if(classConf.error.clazz) $errorTarget.addClass(classConf.error.clazz);
			if(classConf.ok.clazz)    $okTarget.removeClass(classConf.ok.clazz);
			showErrorMessage(obj, messageID, options);
		} else {
			if(classConf.error.clazz) $errorTarget.removeClass(classConf.error.clazz);
			if(classConf.ok.clazz)    $okTarget.addClass(classConf.ok.clazz);
			hideErrorMessage(obj, messageID, options);
		}
	}

	function showErrorMessage(obj, ID, options) {
		var $obj = $(obj);

		if($('#' + ID).size() == 0) {
			$obj.after(
				$('<div style="display:none" id="' + ID + '" class="' + options.errorMessageClass + '">' +
					$obj.attr($.fn.validator.attributes.message) +
				'</div>')
			);
		}

		var $message = $('#' + ID);

		if(options.animate) {
			if(options.animateFading && options.animateSliding) {
				$message.fadeTo(0, 0.0).slideDown(options.animationDuration, function() {
					$message.fadeTo(options.animationDuration, 1.0);
				});
			} else if(options.animateFading && !options.animateSliding) {
				$message.fadeTo(0, 0.0).fadeTo(options.animationDuration, 1.0);
			} else if(options.animateSliding && !options.animateFading) {
				$message.slideDown(options.animationDuration);
			}
		} else {
			$message.show();
		}
	}

	function hideErrorMessage(obj, ID, options) {
		var $message = $('#' + ID);

		if(options.animate) {
			if(options.animateFading && options.animateSliding) {
				$message.fadeTo(options.animationDuration, 0.0, function() {
					$(this).slideUp(options.animationDuration, function() {
						$(this).remove();
					});
				});
			} else if(options.animateFading && !options.animateSliding) {
				$message.fadeTo(options.animationDuration, 0.0, function() {
					$(this).remove();
				});
			} else if(options.animateSliding && !options.animateFading) {
				$(this).slideUp(options.animationDuration, function() {
					$(this).remove();
				});
			}
		} else {
			$('#' + messageID).hide().remove();
		}
	}

	function parseClassesConfig(obj) {
		var config = {
			error : {clazz : null, selector : null},
			ok    : {clazz : null, selector : null}
		};

		var classes = $(obj).attr($.fn.validator.attributes.classes);

		if(classes != '') {
			var split = classes.split("|");
			var errorPart = "";
			var okPart = "";

			for(var key in split) {
				if(split[key].indexOf("error:") == 0) {
					errorPart = split[key].substr(6);
				} else if(split[key].indexOf("ok:") == 0) {
					okPart = split[key].substr(3);
				}
			}

			var matches;

			if(errorPart != "") {
				matches = /([^\{]+)({.*})?/.exec(errorPart);
				config.error.clazz    = matches[1];
				config.error.selector = matches[2] ? matches[2].replace(/[\{\}]/g,"") : null;
			}

			if(okPart != "") {
				matches = /([^\{]+)({.*})?/.exec(okPart);
				config.ok.clazz = matches[1];
				config.ok.selector = matches[2] ? matches[2].replace(/[\{\}]/g,"") : null;
			}
		}

		return config;
	}

	function validate(obj) {
		var $obj = $(obj);

		if($obj.attr($.fn.validator.attributes.purge)) {
			execPurge(obj, $obj.attr("v-purge"));
		}

		if($obj.attr($.fn.validator.attributes.required) == "true") {
			if(!validateRequired(obj)) return false;
		} else {
			// wenn nicht obligatorisch & der wert "leer", dann ist das ok...
			if($obj.val() == "") {
				return true;
			}
		}

		if($obj.attr($.fn.validator.attributes.match)) {
			if(!validateMatch(obj, $obj.attr("v-match"))) return false;
		}

		return true;
	}

	function execPurge(obj, rules) {
		rules = rules.indexOf("|") > 0 ? rules.split("|") : rules.split(",");
		var value = $(obj).val();
		
		for(var key in rules) {
			value = value.replace($.fn.validator.regex.purge[rules[key]], "");
		}

		$(obj).val(value);
	}

	function validateRequired(obj) {
		var value = $(obj).val();

		if($(obj).attr('type') == 'checkbox') {
			return $(obj).is(':checked');
		}

		if(
			( typeof value == "string" && value != "" ) ||
			( typeof value == "object" && value.length && value.length > 0 )
		) {
			return true;
		} else {
			return false;
		}
	}

	function validateMatch(obj, match) {
		// alphabetic|numeric|email|url|/regex/

		match = match.indexOf("|") > 0 ? match.split("|") : match.split(",");

		var value = $(obj).val();
		var regex = null;
		var regexString = '';

		for(var key in match) {
			if($.fn.validator.regex.match[match[key]]) {
				regexString = $.fn.validator.regex.match[match[key]];

				if(/(\/i)$/.exec(regexString)) {
					regexString = regexString.substr(1, regexString.length - 3);
					regex = new RegExp(regexString, "i");
				} else {
					regexString = regexString.substr(1, regexString.length - 2);
					regex = new RegExp(regexString);
				}

				if(!regex.exec(value)) {
					return false;
				}
			} else if(
				match[key].indexOf('/') == 0 &&
				match[key].lastIndexOf('/') == match[key].length - 1
			) {
				regex = new RegExp(match[key].replace("/", ""), "i");
				if(!regex.exec(value)) {
					return false;
				}
			}
		}

		return true;
	}
})(jQuery)
