

$(function(){

	// S�tter event-handlers p� felter:
	allFields().change(blurCallback);
	theForm().submit(submitCallback);

    /****************************************************************
    ** Callbacks til valideringer                                  **
    ****************************************************************/

	function submitCallback() {
		var fields = allFields();
		var errorsBefore = existErrors();

		clearAllErrors();

		fields.filter(nonEmptyOrMirror).each(matchAndProcessValidators);
		fields.filter(".required").each(requiredValidator);
		
		var errorsAfter = renderErrors(errorsBefore);
		
		if (errorsBefore && errorsAfter) {
			shakeErrors();
		}
		
		return !errorsAfter;
	}
	
	function blurCallback() {
		var field = $(this);
		var errorsBefore = existErrors();
		
		clearErrorsForField(field);

		field.filter(nonEmptyOrMirror).each(matchAndProcessValidators);
		field.filter(".required").each(requiredValidator);
		
		renderErrors(errorsBefore);
	}

    /****************************************************************
    ** Operationer til at matche og k�re valideringer              **
    ****************************************************************/
    
    function matchAndProcessValidators() {
    	var field = $(this);
		var classes = (field.attr("class") || "").split(/\s+/);

		for (var i = 0; i < classes.length; i++) {
			var className = classes[i];
			var validator = validators[className];

			if (typeof(validator)=='function') {
				field.each(validator);
			}
		}
    }
	
    /****************************************************************
    ** Operationer p� listen af valideringsfejl                    **
    ****************************************************************/

	var errorMap = {};
	
	// Constructor for ValidationError
	function ValidationError() {
		this.validationError = null;
		this.requiredError = null;
	}

	ValidationError.prototype.hasRequiredError = function() {
		return this.requiredError != null;
	};
	
	ValidationError.prototype.hasValidationError = function() {
		return this.validationError != null;
	};

	ValidationError.prototype.hasError = function() {
		return this.hasValidationError() || this.hasRequiredError();
	};	

	function clearErrorsForField(input) {
		input.each(clearErrorStyle);
		resetError(findId(input));
	}
	
	function removeRequiredMessage(input) {
		var error = fetchError(findId(input));
		
		var hasReqErr = error.hasRequiredError();

		if (hasReqErr) {
			error.requiredError = null;
		}
		
		return hasReqErr;
	}

	function clearAllErrors() {
		allFields().each(clearErrorStyle);
		errorMap = {};
	}
	
	function fetchError(errorId) {
		var error = errorMap[errorId];
			
		if (!isDefined(error)) {
			error = resetError(errorId);
		}
		
		return error;
	}
	
	function resetError(errorId) {
		var error = errorMap[errorId] = new ValidationError();
		return error;
	}

	function existErrors() {
		var fields = allFields();

		for (var i = 0; i < fields.length; i++) {
			var field = $(fields[i]);
			var error = fetchError(findId(field));
			
			if (error.hasError()) {
				return true;
			}
		}

		return false;
	}

    /****************************************************************
    ** Funktioner til at rendere valideringsfejl                   **
    ****************************************************************/
	
	function renderErrors(errorsBefore) {
		emptyErrorTable();
		allFields().each(fetchAndRenderError);
		return setErrorTableVisibility(errorsBefore);
	}
	
	function fetchAndRenderError(){
		var me = $(this);
		var id = me.attr("id");
		var error = fetchError(id);
		
		if (error.hasRequiredError()) {
			renderError(error.requiredError);
		} else if (error.hasValidationError()) {
			renderError(error.validationError);
		}
	}

	function renderErrorList(errorList) {
		if (!renderRequiredError(errorList.requiredError)) {
			renderErrorArray(errorList.errorArray);
		}
	}
	
	function renderRequiredError(requiredError) {
		if (requiredError) {
			renderError(requiredError);
			return true;
		}
		return false;
	}
	
    /****************************************************************
    ** Funktioner til at manipulere HTML'en for errors             **
    ****************************************************************/

	function allFields() {
		return $("table.kontaktformular input:text, table.kontaktformular textarea, table.kontaktformular select, table.kontaktformular input:checkbox");
	}
	
	function submitButton() {
		return $("table.kontaktformular input:submit");
	}

	function theForm() {
		return $("table.kontaktformular").closest("form");
	}
	
	function errorOuterWrapper() {
		return $("table.kontaktformular .errorouterwrapper");
	}

	function errorTable() {
		return $("table.kontaktformular .errorwrapper");
	}

	function emptyErrorTable() {
		errorTable().empty();
	}

	function setErrorTableVisibility(errorsBefore) {
		var errorsNow = existErrors();
		
		if (errorsNow) {
			if (!errorsBefore) {
				errorOuterWrapper().show("slow");
			} else {
				errorOuterWrapper().show();
			}
		} else {
			if (errorsBefore) {
				errorOuterWrapper().hide("fast");
			} else {
				errorOuterWrapper().hide();
			}
		}

		return errorsNow;
	}
	
	function shakeErrors() {
		errorOuterWrapper().effect("shake", {times : 2, distance: 7}, 60);
	}

	function showErrorTable() {
		errorTable().show("slow");
	}

	function hideErrorTable() {
		errorTable().hide("fast");
	}

	function emptyErrorTable() {
		$("table.kontaktformular .errorwrapper").empty();
	}
	
	function renderError(error) {
		errorTable().append('<div class="error">' + error.message + '</div>');
		error.inputs.addClass("error");
	}
	
	function clearErrorStyle() {
		$(this).removeClass("error");
	}

    /****************************************************************
    ** Valideringsbeskeder                                         **
    ****************************************************************/

	var validatorErrors = {
		'required'          : 'Du mangler at udfylde feltet ##labeltext##.',
		'cprnummer'         : 'Det oplyste CPR-nummer er ikke gyldigt.',
		'cvrnummer'         : 'Det oplyste CVR-nummer er ikke gyldigt.',
		'telefonnummer'     : 'Det oplyste telefonnummer er ikke gyldigt.',
		'email'             : 'Den oplyste e-mail-adresse er ikke gyldig.',
		'bilregnr'          : 'Det oplyste registreringsnummer er ikke gyldigt.',
		'regnr'             : 'Det oplyste registreringsnummer er ikke gyldigt.',
		'kontonummer'       : 'Det oplyste kontonummer er ikke gyldigt.',
		'kortnummer'        : 'Det oplyste kortnummer er ikke gyldigt.',
		'postnummer'        : 'Det oplyste postnummer er ikke gyldigt.',
		'mirror'            : '##labeltext## matcher ikke ##prevlabeltext##.'
	};

    /****************************************************************
    ** Valideringsfunktioner                                       **
    ****************************************************************/

	var validationFunctions = {
		'required' :
			function (value) {
				return typeof(value)=="string" && value.length > 0;
			},
		
		'checkboxRequired' :
			function (value) {
				return value==true;
			},
			
		'bilregnr' :
			function (value) {
				return value.length <= 7;
			},

		'regnr' :
			function (value) {
				return /^\d{1,4}$/.test(value);
			},

		'kontonummer' :
			function (value) {
				return /^\d{1,10}$/.test(value);
			},
			
		'kortnummer' : 
			function (value) {
				return /^\d{16}$/.test(value);
			},
			
		'cprdato' :
			function (value) {
				       /*   D  D  |  D  D |D  D   +  M  M  |M  M  + � �  */
				return /^(?:0[1-9]|[1-2]\d|3[0-1])(?:0[1-9]|1[0-2])\d{2}$/.test(value);
			},

		'cprserienr' :
			function (value) {
				return /^\d{4}$/.test(value);
			},

		'cvrnummer' :
			function(value) {
				if (!/^\d{8}$/.test(value)) {
					return false;
				}
					
				function charAtAsInt(str,charPos) {
					return parseInt(str[charPos]);
				}
		
				var valueAsString = valAsStr(value);
				var weightedSum = 0;
				
				weightedSum += charAtAsInt(valueAsString, 0) * 2; 
				weightedSum += charAtAsInt(valueAsString, 1) * 7;
				weightedSum += charAtAsInt(valueAsString, 2) * 6;
				weightedSum += charAtAsInt(valueAsString, 3) * 5;
				weightedSum += charAtAsInt(valueAsString, 4) * 4;
				weightedSum += charAtAsInt(valueAsString, 5) * 3;
				weightedSum += charAtAsInt(valueAsString, 6) * 2; 
				weightedSum += charAtAsInt(valueAsString, 7) * 1;
				
				var modulus11 = weightedSum % 11;
				
				return modulus11 == 0;
			},

		'telefonnummer' :
			function (value) {
				return /^\+?\d{8}\d*$/.test(value);
			},
			
		'email' :
			function (value) {
				return /^[^@\s]+@[^@\s]+\.\w{2,}$/.test(value);
			},

		'postnummer' :
			function (value) {
				return /^[1-9]\d{3}$/.test(value);
			}
	};

    /****************************************************************
    ** Validation-objekter                                         **
    ****************************************************************/

	/* Constructor for Validation */
	function Validation(validationFunction, validationErrorMessage) {
		this.validate = validationFunction;
		this.errorMessage = validationErrorMessage;
	}

	var validations = {
		'required'      	: new Validation(validationFunctions.required, validatorErrors.required),
		'checkboxRequired'  : new Validation(validationFunctions.checkboxRequired, validatorErrors.required),
		'bilregnr'      	: new Validation(validationFunctions.bilregnr, validatorErrors.bilregnr),
		'cprdato'       	: new Validation(validationFunctions.cprdato, validatorErrors.cprnummer),
		'cprserienr'    	: new Validation(validationFunctions.cprserienr, validatorErrors.cprnummer),
		'cvrnummer'    	 	: new Validation(validationFunctions.cvrnummer, validatorErrors.cvrnummer),
		'email'         	: new Validation(validationFunctions.email, validatorErrors.email),
		'telefonnummer' 	: new Validation(validationFunctions.telefonnummer, validatorErrors.telefonnummer),
		'postnummer'    	: new Validation(validationFunctions.postnummer, validatorErrors.postnummer),
		'regnr'         	: new Validation(validationFunctions.regnr, validatorErrors.regnr),
		'kontonummer'   	: new Validation(validationFunctions.kontonummer, validatorErrors.kontonummer),
		'kortnummer'    	: new Validation(validationFunctions.kortnummer, validatorErrors.kortnummer)
	};

    /****************************************************************
    ** Validatorer (matches mod klassenavne)                       **
    ****************************************************************/

	function requiredValidator() {
		var me = $(this);
		var id = findId(me);
		var value = me.val();
		
		if(me.attr('type')=='checkbox') {
			value = me.attr('checked');
			performValidation(validations.checkboxRequired, value, id, me);
		} else {
			performRequiredValidation(validations.required, value, id, me);
		}
	}
	
	var validators = {
		'bilregnr' : function () {
			var me = $(this);
			var id = findId(me)
			var value = removeWhiteSpace(me.val());
			
			performValidation(validations.bilregnr, value, id, me);
		},

		'cprdato' : function () {
			var me = $(this);
			var id = findId(me);
			var value = removeWhiteSpace(me.val());
			
			performValidation(validations.cprdato, value, id, me);
		},

		'cprserienr' : function () {
			var me = $(this);
			var id = findId(me)
			var value = removeWhiteSpace(me.val());
			
			performValidation(validations.cprserienr, value, id, me);
		},
		
		'cvrnummer' : function () {
			var me = $(this);
			var id = findId(me)
			var value = removeWhiteSpace(me.val());
			
			performValidation(validations.cvrnummer, value, id, me);
		},

		'email' : function () {
			var me = $(this);
			var id = findId(me)
			var value = me.val();
			
			performValidation(validations.email, value, id, me);
		},

		'telefonnummer' : function () {
			var me = $(this);
			var id = findId(me)
			var value = removeWhiteSpace(me.val());
			
			performValidation(validations.telefonnummer, value, id, me);
		},

		'postnummer' : function () {
			var me = $(this);
			var id = findId(me)
			var value = me.val();
			
			performValidation(validations.postnummer, value, id, me);
		},

		'regnr' : function () {
			var me = $(this);
			var id = findId(me)
			var value = me.val();
			
			performValidation(validations.regnr, value, id, me);
		},

		'kontonummer' : function () {
			var me = $(this);
			var id = findId(me)
			var value = removeWhiteSpace(me.val());
			
			performValidation(validations.kontonummer, value, id, me);
		},

		'kortnummer' : function () {
			var me = $(this);
			var id = findId(me)
			var value = removeWhiteSpace(me.val());
			
			performValidation(validations.kortnummer, value, id, me);
		},

		'mirror' : function () {
			var me = $(this);
			var id = findId(me)
			var value = removeWhiteSpace(me.val());
			
			performValidation(
				new Validation(function(val) {
					return val == me.closest("tr.formularrow").prev("tr.formularrow").find("input,textarea,select").val();
				}, validatorErrors.mirror),
				value, id, me);
		}
		
	};

    /****************************************************************
    ** Funktioner til validering                                   **
    ****************************************************************/

	function performValidation(validation, value, errorListId, inputs) {
		if (!validation.validate(value)) {
			var valErr = fetchError(errorListId);
			
			var affectedInputs = valErr.hasValidationError() ?
				inputs.add(valErr.validationError.inputs) :
				inputs;
			
			valErr.validationError = new Error(validation.errorMessage, affectedInputs);
		}
	}

	function performRequiredValidation(validation, value, errorListId, inputs) {
		if (!validation.validate(value)) {
			var valErr = fetchError(errorListId);
			
			var affectedInputs = valErr.hasRequiredError() ?
				inputs.add(valErr.requiredError.inputs) :
				inputs;
			
			valErr.requiredError = new Error(validation.errorMessage, affectedInputs);
		}
	}
	
	function Error(errorMessage, inputs) {
		this.message = formatErrorMessage(errorMessage, inputs);
		this.inputs = inputs;
	}
	
	function formatErrorMessage(errorMessage, inputs) {
		var mellemregning01 = errorMessage.replace("##labeltext##", inputs.closest("tr.formularrow").find("label").text());
		var mellemregning02 = mellemregning01.replace("##prevlabeltext##", inputs.closest("tr.formularrow").prev("tr.formularrow").find("label").text());
		return mellemregning02;
	}
	
    /****************************************************************
    ** Hj�lpefunktioner                                            **
    ****************************************************************/
    
	function isValidId(id) {
		return id != undefined && id.length > 0;
	}

    function findId(element) {
    	return element.closest("tr.formularrow").find("input,textarea,select").attr("id");
    }

	function isDefined(value) {
		return typeof(value) != 'undefined';
	}
	
	function valAsStr(value) {
		return $.trim(value.toString());
	}
	
	function removeWhiteSpace(strValue) {
		return strValue.replace(/\s/g, '');
	}
	
	function nonEmptyOrMirror() {
		var me = $(this);
		return me.hasClass("mirror") || me.val().length > 0;
	}

});
