(function() {
	var $package = jsPackage("epoint.ow.forms2");
	
	var Utils = $package.Utils = {

		/**
		 * Zwraca błąd walidacji walidatora JS.
		 * 
		 * @param errorCode kod błędu
		 * @param errorParameters mapa parametrów z walidatora, postaci: {klucz : wartość}
		 * @param mainFieldName nazwa głównego pola, którego błąd dotyczy lub <code>null</code>
		 * @param notValidFieldNames lista nazw pól, których ten błąd dotyczy - opcjonalna
		 * @return błąd walidacji walidatora JS.
		 */
		reportError : function(/*String*/ errorCode, /*key-value map*/ errorParameters, /*String*/ mainFieldName, /*array of String*/ notValidFieldNames) {
			return {
				errorCode : errorCode,
				errorParameters : errorParameters,
				mainFieldName : mainFieldName,
				notValidFieldNames : notValidFieldNames
			};
		},
	
		/**
		 * Zwraca domyślny błąd walidacji walidatora JS.
		 * 
		 * @param errorParameters mapa parametrów z walidatora, postaci: {klucz : wartość}.
		 * @param mainFieldName nazwa głównego pola, którego błąd dotyczy lub <code>null</code>
		 * @param notValidFieldNames lista nazw pól, których ten błąd dotyczy - opcjonalna
		 * @return błąd walidacji walidatora JS.
		 */
		reportDefaultError : function(/*key-value map*/ errorParameters, /*String*/ mainFieldName, /*array of String*/ notValidFieldNames) {
			return Utils.reportError('default', errorParameters, mainFieldName, notValidFieldNames);
		},
		
		/**
		 * Zwraca identyfikator pola o podanej nazwie.
		 * 
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 * @return identyfikator pola formularza
		 */
		getFieldId : function (formName, fieldName) {
			return formName + "_" + fieldName;
		},

		/**
		 * Zwraca identyfikator kontenera błędów pola o podanej nazwie.
		 * 
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 * @return identyfikator kontenera błędów pola formularza
		 */
		getFieldErrorContainerId : function (formName, fieldName) {
			return Utils.getFieldId(formName, fieldName) + "_error_container";
		},
		
		/**
		 * Zwraca identyfikator kontenera błędów formularza o podanej nazwie.
		 * 
		 * @param formName nazwa formularza
		 * @return identyfikator kontenera błędów formularza
		 */
		getFormErrorContainerId : function (formName) {
			return formName + "_errors";
		},
		
		/**
		 * Refejstruje domyślny handler zdarzenia <code>submit</code>.
		 * 
		 * @param formName nazwa formularza
		 */
		registerDefaultSubmitEventHandler : function(formName) {
			var form = epoint.ow.Utils.getForm(formName);
			
			$(form).submit(function(e, action) {
				var deferred = jQuery.Deferred();
			
				deferred.done(function () {
					Utils.runFormValidationSuccessActions(formName);
					if (action != null) {
						form.action = action;
					}
					form.submit();
				});
				
				deferred.fail(function () {
					Utils.runFormValidationFailedActions(formName);
				});
			
				Utils.validateForm(formName, deferred);
				e.preventDefault();
			});
		},
		
		/**
		 * Uruchamia akcje związane z negatywną walidacją formularza.
		 * 
		 * @param formName nazwa formularza
		 */
		runFormValidationFailedActions : function(formName) {
			var form = epoint.ow.Utils.getForm(formName);
			
			ErrorDisplayManager.showErrors(formName);
			ErrorDisplayManager.scrollToErrors(formName);
			ErrorManager.triggerErrorRelatedEvents(formName);
		},
		
		/**
		 * Uruchamia akcje związane z pozytywną walidacją formularza.
		 * 
		 * @param formName nazwa formularza
		 */
		runFormValidationSuccessActions : function(formName) {
			var form = epoint.ow.Utils.getForm(formName);
			
			ErrorDisplayManager.showErrors(formName);
			ErrorManager.triggerErrorRelatedEvents(formName);
		},
		
		/**
		 * Uruchamia akcje związane z pozytywną walidacją pola formularza.
		 * 
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 */
		runFieldValidationSuccessActions : function(formName, fieldName) {
			var field = epoint.ow.Utils.getField(formName, fieldName);
			
			ErrorDisplayManager.showErrors(formName);
			ErrorDisplayManager.scrollToErrors(formName);
			ErrorManager.triggerErrorRelatedEvents(formName);
		},
		
		/**
		 * Uruchamia akcje związane z negatywną walidacją pola formularza.
		 * 
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 */
		runFieldValidationFailedActions : function(formName, fieldName) {
			var field = epoint.ow.Utils.getField(formName, fieldName);
			
			ErrorDisplayManager.showErrors(formName);
			ErrorDisplayManager.scrollToErrors(formName);
			ErrorManager.triggerErrorRelatedEvents(formName);
		},
		
		/**
		 * Zwraca domyślny obiekt <code>deferred</code> zawierający akcje uruchamiane po walidacji formularza.
		 * 
		 * @param formName nazwa formularza
		 */
		getDefaultFormValidationDeferredObject : function(formName) {
			var deferred = jQuery.Deferred();
			deferred.done(function() { Utils.runFormValidationSuccessActions(formName);});
			deferred.fail(function() { Utils.runFormValidationFailedActions(formName);});
			return deferred;
		},
		
		/**
		 * Zwraca domyślny obiekt <code>deferred</code> zawierający akcje uruchamiane po walidacji pola formularza.
		 * 
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 */
		getDefaultFieldValidationDeferredObject : function(formName, fieldName) {
			var deferred = jQuery.Deferred();
			deferred.done(function() { Utils.runFieldValidationSuccessActions(formName, fieldName);});
			deferred.fail(function() { Utils.runFieldValidationFailedActions(formName, fieldName);});
			return deferred;
		},
		
		/**
		 * Waliduje formularz o podanej nazwie.
		 * 
		 * @param formName nazwa formularza
		 * @param deferred akcje wykonane w przypadku poprawnej/błędnej walidacji - opcjonalne
		 */
		validateForm : function(formName, deferred) {
			this.commonValidate(formName, null, deferred);
		},
		
		/**
		 * Waliduje grupę pól formularza powiązaną z danym polem.
		 * 
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola formularza
		 * @param deferred akcje wykonane w przypadku poprawnej/błędnej walidacji - opcjonalne
		 */
		validateRelatedFields : function(formName, fieldName, deferred) {
			this.commonValidate(formName, fieldName, deferred);
		},
		
		/**
		 * Wykonuje walidację pola na zdarzeniu <code>change</code>.
		 * 
		 * @param field nazwa pola formularza
		 */
		fieldValueChanged : function(field) {
			this.validateFieldOnChange(field);
		},
		
		commonValidate : function(formName, fieldName, deferred) {
			if (fieldName != null) {
				ErrorManager.clearFieldErrors(formName, fieldName);
				if (deferred == null) {
					deferred = Utils.getDefaultFieldValidationDeferredObject(formName, fieldName);
				}
			} else {
				ErrorManager.clearFormErrors(formName);
				if (deferred == null) {
					deferred = Utils.getDefaultFormValidationDeferredObject(formName);
				}
			}
			
			var fieldNames;
			if (fieldName != null)  {
				fieldNames = ConfigManager.getFieldRelatedFieldNamesForValidation(formName, fieldName, false);
			} else {
				fieldNames = ConfigManager.getAllFormFieldNames(formName);
			}
			
			for (var i = 0; i < fieldNames.length; i++) {
				var name = fieldNames[i];
				
				var field = epoint.ow.Utils.getField(formName, name);
				if (field.disabled) {
					continue;
				}
				
				var configs = ConfigManager.getFieldValidators(formName, name);
				for(var j = 0; j < configs.length; j++) {
					ValidatorFunctionManager.runConfigValidator(configs[j], formName, name);
				}
			}
			
			var configs = ConfigManager.getMultiFieldValidators(formName);
			for(var j = 0; j < configs.length; j++) {
				var config = configs[j];
				if (fieldName != null && epoint.ow.Utils.arraySearch(config.allFieldNames, fieldName) == -1) {
					continue;
				}
				ValidatorFunctionManager.runConfigValidator(configs[j], formName, null);
			}
			
			var valid =  ErrorManager.isFormValid(formName);
			if (!valid) {
				deferred.reject();
				return;
			}

			if (ConfigManager.isAjaxValidationEnabled(formName)) {
				if (fieldName != null) {
					AjaxManager.validateRelatedFields(formName, fieldName, deferred);
				} else {
					AjaxManager.validateForm(formName, deferred);
				}
			} else {
				deferred.resolve();
			}
			
		},
		
		/**
		 * Wykonuje walidację pola.
		 * 
		 * @param fieldName nazwa pola formularza
		 * @param deferred akcje wykonane w przypadku poprawnej/błędnej walidacji - opcjonalne
		 */
		validateFieldOnChange : function(field, deferred) {
			var formName = field.form.getAttribute('name');
			var fieldName = field.getAttribute('name');
			
			if (!ConfigManager.isOnChangeValidationEnabled(formName)) {
				return;
			}
			
			ErrorManager.clearFieldErrors(formName, fieldName);
			
			if (deferred == null) {
				deferred = Utils.getDefaultFieldValidationDeferredObject(formName, fieldName);
			}
			
			var configs = ConfigManager.getFieldValidators(formName, fieldName);
			
			var valid = true
			for(var i = 0; i < configs.length; i++) {
				var config = configs[i];
				if (config.isRunOnChangeEvent) {
					valid = ValidatorFunctionManager.runConfigValidator(configs[i], formName, fieldName) && valid;
				}
			}

			if (!valid) {
				deferred.reject();
				return;
			}
			
			if (ConfigManager.isAjaxValidationEnabled(formName)) {
				AjaxManager.validateField(formName, fieldName, deferred);
			} else {
				deferred.resolve();
			}
		},
		
		/**
		 * Submituje podany formularz na podany url.
		 * 
		 * @param formName nazwa formularza
		 * @param actionUrl - opcjonalnie
		 */
		submitForm : function (formName, actionUrl) {
			var form = epoint.ow.Utils.getForm(formName);
			if (actionUrl != null) {
				form.action = actionUrl;
			}
			
			form.submit();
		},
		
		/**
		 * Wyzwala zdarzenie form.submit.
		 * 
		 * @param formName nazwa formularza
		 * @param actionUrl - opcjonalnie
		 */
		submitFormJS : function (formName, action) {
			$(epoint.ow.Utils.getForm(formName)).trigger('submit', action);
		},
		
		/**
		 * Sprawdza, czy formularz o podanej nazwie zawiera niepuste pola typu file.
		 * 
		 * @param formName nazwa formularza
		 * @return <code>true</code> jeżeli formularz zawiera niepuste pola typu file.
		 */
		containsNotEmptyFileFields : function(formName) {
			var $files = $(epoint.ow.Utils.getForm(formName)).find(":file");
			
			for(var i = 0; i < $files.size(); i++) {
				var file = $files.get(i);
				if (file.value != null && file.value.length > 0) {
					return true;
				}
			}
			return false;
		}
	};
	
	var ErrorManager = $package.ErrorManager = {
		errorsMap : {},
		
		/**
		 * Zwraca wszystkie błędy walidacji formularza.
		 * 
		 * @param formName nazwa formularza
		 * @return tablica błędów
		 */
		getAllErrors : function(formName) {
			if (this.errorsMap[formName] == null) {
				this.errorsMap[formName] = new Array();
			}
			return this.errorsMap[formName];
		},
		
		/** 
		 * Dodaje błąd walidacji formularza.
		 * 
		 * @param formName nazwa formularza.
		 * @param fieldName nazwa pola lub <code>null</code> jeżeli jest to błąd walidatora MultiFieldValidator.
		 * @param validatorName unikalna nazwa walidatora.
		 * @param errorCode kod błędu walidacji.
		 * @param errorParameters mapa parametrów błędu walidacji.
		 * @param mainFieldName nazwa głównego pola, którego błąd dotyczy lub <code>null</code>
		 * @param notValidFieldNames lista nazw pól, których ten błąd dotyczy - opcjonalna
		 */
		addError : function (
				/*String*/ formName,
				/*String*/ fieldName,
				/*String*/ validatorName,
				/*String*/ errorCode,
				/*key-value map*/ errorParameters,
				/*String*/ mainFieldName,
				/*Array of String*/ notValidFieldNames) {
				
			var formErrors = this.getAllErrors(formName);
			var notValidFields = notValidFieldNames == null || !(notValidFieldNames instanceof Array) ? [] : notValidFieldNames;
			formErrors.push({
					fieldName : fieldName,
					validatorName : validatorName,
					errorCode : errorCode,
					errorParameters : errorParameters,
					mainFieldName : mainFieldName,
					notValidFieldNames : notValidFields
			});
		},
		
		/** 
		 * Dodaje błędy walidacji formularza.
		 * 
		 * @param formName nazwa formularza.
		 * @param errors tablica błędów walidacji formularza
		 */
		addErrors : function(/*String*/ formName, /*array of error*/ errors) {
			for(var i = 0; i < errors.length; i++) {
				var error = errors[i];
				this.addError(formName, error.fieldName, error.validatorName, error.errorCode, error.errorParameters,
						error.mainFieldName, error.notValidFieldNames);
			}
		},
		
		/** 
		 * Ustawia błędy walidacji formularza, kasuje poprzednie błędy.
		 * 
		 * @param formName nazwa formularza.
		 * @param errors tablica błędów walidacji formularza
		 */
		setErrors : function(/*String*/ formName, /*array of error*/ errors) {
			this.clearFormErrors(formName);
			this.addErrors(formName, errors);
		},
		
		/**
		 * Usuwa błędy walidacji danego pola.
		 * 
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 */
		clearFieldErrors : function(formName, fieldName) {
			var allErrors = this.getAllErrors(formName);
			var newErrors = new Array();
			for (var i = 0; i < allErrors.length; i++) {
				var error = allErrors[i];
				
				if (!this.isFieldRelatedError(formName, fieldName, error)) {
					newErrors.push(error);
				}
			}
			this.errorsMap[formName] = newErrors;
		},
		
		/**
		 * Sprawdza, czy błąd jest pochodzi z walidatora związanego z podanym polem.
		 * 
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 * @return <code>true</code> jeżeli błąd jest związany z podanym polem
		 */
		isFieldRelatedError : function(formName, fieldName, error) {
			var config = ConfigManager.getConfig(formName, error.fieldName, error.validatorName);
			var mainFieldName = ErrorManager.getMainFieldName(formName, error);
			
			if (mainFieldName == fieldName ||
					epoint.ow.Utils.arraySearch(config.allFieldNames, fieldName) != -1) {
				return true;
			}
			return false;
		},

		/**
		 * Usuwa błędy walidacji danego formularza.
		 * 
		 * @param formName nazwa formularza
		 */
		clearFormErrors : function(formName) {
			if (this.errorsMap[formName] != null) {
				this.errorsMap[formName] = new Array();
			}
		},
			
		/**
		 * Zwraca tablicę komunikatów błędów walidacji przypisanych do danego pola.
		 * 
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 */
		getFieldErrorMessages : function(formName, fieldName) {
			var allErrors = this.getAllErrors(formName);
			var fieldErrors = new Array();
			for (var i = 0; i < allErrors.length; i++) {
				var error = allErrors[i];
				var config = ConfigManager.getConfig(formName, error.fieldName, error.validatorName);
				var mainFieldName = ErrorManager.getMainFieldName(formName, error);
				if (mainFieldName == fieldName) {
					var msg = ValidatorFunctionManager.formatMessage(config, formName, fieldName, error.errorCode, error.errorParameters);
					fieldErrors.push(msg);
				}
			}
			return fieldErrors;
		},

		/**
		 * Zwraca tablicę komunikatów błędów walidacji nie przypisanych do żadnego pola.
		 * 
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 */
		getFormErrorMessages : function(formName) {
			var allErrors = this.getAllErrors(formName);
			var formErrors = new Array();
			for (var i = 0; i < allErrors.length; i++) {
				var error = allErrors[i];
				var config = ConfigManager.getConfig(formName, error.fieldName, error.validatorName);
				var mainFieldName = ErrorManager.getMainFieldName(formName, error);
				if (mainFieldName == null) {
					var msg = ValidatorFunctionManager.formatMessage(config, formName, null, error.errorCode, error.errorParameters);
					formErrors.push(msg);
				}
			}
			return formErrors;
		},
			
		/**
		 * Sprawdza, czy pole zawiera błędy walidacji.
		 * 
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 * @return <code>true</code> jeżeli pole nie zawiera błędów walidacji
		 */
		isFieldValid : function(formName, fieldName) {
			var allErrors = this.getAllErrors(formName);

			for (var i = 0; i < allErrors.length; i++) {
				var error = allErrors[i];
				var notValidFields = this.getNotValidFieldNames(formName, error);

				if (epoint.ow.Utils.arraySearch(notValidFields, fieldName) != -1) {
					return false;
				}
			}
			return true;
		},
		
		/**
		 * Zwraca nazwę głównego pola, którego dotyczy błąd.
		 * 
		 * @param formName nazwa formularza
		 * @param error błąd
		 */
		getMainFieldName : function(formName, error) {
			var config = ConfigManager.getConfig(formName, error.fieldName, error.validatorName);
			
			if (error.mainFieldName != null) {
				return error.mainFieldName;
			} else {
				return config.mainFieldName;
			}
		},
		
		/**
		 * Zwraca listę pól zawierających błąd dla podanego błędu walidacji.
		 * 
		 * @param formName nazwa formularza
		 * @param error błąd walidaji
		 * @return lista pól formularza, których dotyczy dany błąd
		 */
		getNotValidFieldNames : function(formName, error) {
			var config = ConfigManager.getConfig(formName, error.fieldName, error.validatorName);
			
			var notValidFields = config.notValidFieldNames;
			if ((error.notValidFieldNames != null) &&
					(error.notValidFieldNames instanceof Array)
					&& (error.notValidFieldNames.length > 0)) {
				notValidFields = error.notValidFieldNames;
			}
			
			return notValidFields;
		},
		
		/**
		 * Sprawdza, czy formularz zawiera błędy walidacji.
		 * 
		 * @param formName nazwa formularza
		 * @return <code>true</code> jeżeli formularz nie zawiera błędów walidacji
		 */
		isFormValid : function(formName) {
			return this.getAllErrors(formName).length == 0;
		},
		
		/**
		 * Wyzwala zdarzenia informujące o istnieniu lub braku błedów formularza i pól.
		 * 
		 * @param formName nazwa formularza
		 */
		triggerErrorRelatedEvents : function(formName) {
			var fieldNames = ConfigManager.getAllFormFieldNames(formName);
			for(var i = 0; i < fieldNames.length; i++) {
				var fieldName = fieldNames[i];
				var field = epoint.ow.Utils.getField(formName, fieldName);
				if (ErrorManager.isFieldValid(formName, fieldName)) {
					$(field).trigger('fieldIsValid');
				} else {
					$(field).trigger('fieldIsNotValid');
				}
			}
			
			var form = epoint.ow.Utils.getForm(formName);
			if (ErrorManager.isFormValid(formName)) {
				$(form).trigger('formIsValid');
			} else {
				$(form).trigger('formIsNotValid');
			}
		}
	};
	
	var ErrorDisplayManager = $package.ErrorDisplayManager = {
		displayHandlers : {},
		
		/**
		 * Ustawia funkcję wyświetlającą błędy walidacji formularza. Jeżeli <code>formName != null</code>,
		 * ustawia funkcję tylko dla tego formularza, w przeciwnym przypadku - ustawia funkcję globalną.
		 * 
		 * @param handlerFn funkcja
		 * @param formName nazwa formularza - opcjonaly
		 */
		setFormHandler : function(handlerFn, formName) {
			if (formName != null) {
				if (this.displayHandlers[formName] == null) {
					this.displayHandlers[formName] = {};
				}
				this.displayHandlers[formName].form = handlerFn;
			} else {
				this.displayHandlers.defaultForm = handlerFn;
			}
		},

		/**
		 * Ustawia funkcję wyświetlającą błędy walidacji pola. Jeżeli <code>formName != null</code> i <code>fieldName != null</code>,
		 * ustawia funkcję tylko dla tego pola. Jeżeli tylko <code>formName != null</code>, ustawia funkcję dla wszystkich pól
		 * tego formularza. Jeżeli <code>formName == null</code> i <code>fieldName == null</code> ustawia funkcję globalną.
		 * 
		 * @param handlerFn funkcja
		 * @param formName nazwa formularza - opcjonaly
		 * @param fieldName nazwa pola - opcjonaly
		 */
		setFieldHandler : function(handlerFn, formName, fieldName) {
			if (formName != null) {
				if (this.displayHandlers[formName] == null) {
					this.displayHandlers[formName] = {};
				}
				
				if (fieldName != null) {
					this.displayHandlers[formName][fieldName] = handlerFn;
				} else {
					this.displayHandlers[formName][''] = handlerFn;
				}
			} else {
				this.displayHandlers.defaultField = handlerFn;
			}
		},
		
		/**
		 * Zwraca funkcję wyświetlającą błędy walidacji formularza.
		 * 
		 * @param formName nazwa formularza
		 * @return funkcja wyświetlająca błędy walidacji formularza
		 */
		getFormHandler : function(formName) {
			if (this.displayHandlers[formName] && this.displayHandlers[formName].form) {
				return this.displayHandlers[formName].form;
			} else if (this.displayHandlers.defaultForm) {
				return this.displayHandlers.defaultForm;
			} else {
				return this.defaultFormHandler;
			}
		},

		/**
		 * Zwraca funkcję wyświetlającą błędy walidacji pola.
		 * 
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 * @return funkcja wyświetlająca błędy walidacji pola
		 */
		getFieldHandler : function(formName, fieldName) {
			if ( this.displayHandlers[formName] && this.displayHandlers[formName][fieldName]) {
				return this.displayHandlers[formName][fieldName];
			} else if (this.displayHandlers.defaultField) {
				return this.displayHandlers.defaultField;
			} else {
				return this.defaultFieldHandler;
			}
		},
		
		/**
		 * Domyślna funkcja wyświetlającą błędy walidacji formularza.
		 * 
		 * @param formName nazwa formularza
		 * @param messageArray komunikaty błędów walidacji
		 */
		defaultFormHandler : function(formName, messageArray) {
			var $c = $("#" + Utils.getFormErrorContainerId(formName));
			if (messageArray && messageArray.length > 0) {
				$c.show('slow');
				$c.find(".form_errors_list").html("<ul><li>" + messageArray.join("</li><li>") + "</li></ul>").end();
			} else {
				$c.hide('slow');
				$c.find(".form_errors_list").empty().end();
			}
		},

		/**
		 * Domyślna funkcja wyświetlającą błędy walidacji pola.
		 * 
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 * @param messageArray komunikaty błędów walidacji
		 */
		defaultFieldHandler : function(formName, fieldName, messageArray) {
			var form = epoint.ow.Utils.getForm(formName);
			var fieldContainerId = Utils.getFieldErrorContainerId(formName, fieldName);
			var $c = $('#' + fieldContainerId ).first();
			if($c.size() == 0 && messageArray.length > 0) {
				$c = $(form).find("[name=" + fieldName + "]").after("<span id='" + fieldContainerId  + "' class='field_error_container'></span>").nextAll(".field_error_container");
			}
			if ($c.size() > 0 && messageArray.length == 0) {
				$c.hide('slow');
				return;
			}
			var $innerC = $c.find(".field_error_inner");
			if ($innerC.size() == 0) {
				$innerC = $c.append("<span class='field_error_inner'></span>").find(".field_error_inner");
			}
			$innerC.html("<span>" + messageArray.join("</span> <span>") + "</span>");
			$c.show('slow');
		},

		/**
		 * Wyświetla aktualne błędy walidacji formularza.
		 * 
		 * @param formName nazwa formularza
		 */
		showErrors : function(formName) {
			this.getFormHandler(formName)(formName, ErrorManager.getFormErrorMessages(formName));
		    
            var fieldNames = ConfigManager.getAllFormFieldNames(formName);
			for (var i = 0; i < fieldNames.length; i++) {
				var fieldName = fieldNames[i];
				this.getFieldHandler(formName, fieldName)(formName, fieldName, ErrorManager.getFieldErrorMessages(formName, fieldName));
				
				if (ErrorManager.isFieldValid(formName, fieldName)) {
					$(epoint.ow.Utils.getForm(formName)).find("[name=" + fieldName + "]").removeClass("field_error");
					$("#" + formName + "_" + fieldName + "_label").removeClass("field_label_error");
				} else {
					$(epoint.ow.Utils.getForm(formName)).find("[name=" + fieldName + "]").addClass("field_error");
					$("#" + formName + "_" + fieldName + "_label").addClass("field_label_error");
				}
			}
			
			
		},
		
		/**
		 * Przewija ekran do pierwszego błędu walidacji formularza.
		 * 
		 * @param formName nazwa formularza
		 */
		scrollToErrors : function(formName) {
			var scrollPosition = null;
			
			var fieldNames = ConfigManager.getAllFormFieldNames(formName);
			for (var i = 0; i < fieldNames.length; i++) {
				var fieldName = fieldNames[i];
				if (ErrorManager.getFieldErrorMessages(formName, fieldName).length > 0) {
					var field = epoint.ow.Utils.getField(formName, fieldName);
					var cont = $('#' + Utils.getFieldErrorContainerId(formName, fieldName)).get(0);
					var position = null;
					if (cont == null) {
						position = $(field).offset().top;
					} else {
						position = $(cont).offset().top;
					}
					
					if (scrollPosition == null || position < scrollPosition) {
						scrollPosition = position;
					}
				}
			}

			if (scrollPosition == null && ErrorManager.getFormErrorMessages(formName).length > 0) {
				var container = $('#' + Utils.getFormErrorContainerId(formName)).get(0);
				if (container != null) {
					scrollPosition = $(container).offset().top;
				}
			}
			if (scrollPosition != null) {
				$(document).scrollTop(Math.max(0, scrollPosition - document.documentElement.clientHeight/2));
			}
		}
	};
	
	var ConfigManager = $package.ConfigManager = {
		
		/**
		 * Zarejestrowane konfiguracje walidatorów.
		 */
		validatorConfigs : {},
		
		ajaxValidationEnabled : {},
		
		onChangeValidationEnabled : {},
		
		/**
		 * Sprawdza, czy walidacja AJAX jest włączona.
		 * 
		 * @param formName nazwa formularza
		 * @return <code>true</code> jeżeli walidacja AJAX jest włączona 
		 */
		isAjaxValidationEnabled : function(formName) {
			return this.ajaxValidationEnabled[formName] == true;
		},

		/**
		 * Sprawdza, czy walidacja na zmianie wartości pola jest włączona.
		 * 
		 * @param formName nazwa formularza
		 * @return <code>true</code> jeżeli walidacja na zmianie wartości pola jest włączona 
		 */
		isOnChangeValidationEnabled : function(formName) {
			return this.onChangeValidationEnabled[formName] == true;
		},
		
		/**
		 * Włącza/wyłącza walidację AJAX.
		 * 
		 * @param formName nazwa formularza
		 * @param isEnabled <code>true</code> jeżeli walidacja AJAX ma być włączona
		 */
		setAjaxValidationEnabled : function(formName, isEnabled) {
			this.ajaxValidationEnabled[formName] = isEnabled == true;
		},

		/**
		 * Włącza/wyłącza walidację na zmianie wartości pola.
		 * 
		 * @param formName nazwa formularza
		 * @param isEnabled <code>true</code> jeżeli walidacja na zmianie wartości pola ma być włączona
		 */
		setOnChangeValidationEnabled : function(formName, isEnabled) {
			this.onChangeValidationEnabled[formName] = isEnabled == true;
		},
		
		/**
		 * Ustawia konfigurację walidatorów dla danego formularza.
		 * 
		 * @param formName nazwa formularza
		 * @param config konfiguracja walidatorów
		 */
		setFormConfig : function(formName, config) {
			this.validatorConfigs[formName] = config;
		},
		
		/**
		 * Zwraca tablicę konfiguracji walidatorów wielopolowych danego formularza.
		 * 
		 * @param formName nazwa formularza
		 * @return tablica konfiguracji walidatorów wielopolowych
		 */
		getMultiFieldValidators : function(formName) {
			var formConfig = this.validatorConfigs[formName];
			if (formConfig == null) {
				return new Array();
			}
			
			return formConfig['multiFieldValidatorConfigs'];
		},

		/**
		 * Zwraca tablicę konfiguracji walidatorów jednopolowych danego pola.
		 * 
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 * @return tablica konfiguracji walidatorów jednopolowych
		 */
		getFieldValidators : function(formName, fieldName) {
			var formConfig = this.validatorConfigs[formName];
			if (formConfig == null) {
				return new Array();
			}
			var fieldConfig = formConfig['fieldValidatorConfigs'][fieldName];
			if (fieldConfig == null) {
				return new Array();
			}
			return fieldConfig;
		},
		
		/**
		 * Zwraca nazwy wszystkich pól formularza.
		 * 
		 * @return nazwy wszystkich pól formularza
		 */
	    getAllFormFieldNames : function(formName) {
			var formConfig = this.validatorConfigs[formName];
            var fieldNames = new Array();
			if (formConfig && formConfig.fieldValidatorConfigs) {
				for(var name in formConfig.fieldValidatorConfigs) {
					fieldNames[fieldNames.length] = name;
				}
			}

            return fieldNames;
        },
        
        /**
         * Zwraca konfigurację walidatora dla danego pola.
         * 
         * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 * @param nazwa walidatora
		 * @return konfiguracja walidatora lub <code>null</code>
         */
        getConfig : function(formName, fieldName, validatorName) {
        	var configs = null;
        	if (fieldName != null) {
        		configs = this.getFieldValidators(formName, fieldName);
        	} else {
        		configs = this.getMultiFieldValidators(formName);
        	}
        	
        	for(var i = 0; i < configs.length; i++) {
        		if (validatorName == configs[i].validatorName) {
        			return configs[i];
        		}
        	}
        	
        	return null;
        },
        
        /**
		 * Zwraca listę nazw pól powiązanych z danym polem, których wartośc powinna być walidowana.
		 * 
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 * @param isAjaxValidation <code>true</code> jeżeli ma być walidowana AJAX, <code>false</code>
		 * jeżeli ma być walidowana JavaScript 
		 */
		getFieldRelatedFieldNamesForValidation : function(formName, fieldName, isAjaxValidation) {
			var allNames = new Array();
			
			configs = ConfigManager.getMultiFieldValidators(formName);
			for(var i = 0; i < configs.length; i++) {
				var config = configs[i];
				
				if (isAjaxValidation) {
					if (!config.isAjaxValidator || config.hasJSValidator) {
						continue;
					}
				} else {
					if (!config.hasJSValidator) {
						continue;
					}
				}
				
				if (epoint.ow.Utils.arraySearch(config.allFieldNames, fieldName) != -1 &&
						(config.runIfNotAllFieldsFilled || epoint.ow.Utils.isAllFieldsFilled(formName, config.allFieldNames))) {
					for(var j = 0; j < config.allFieldNames.length; j++) {
						allNames[allNames.length] = config.allFieldNames[j];
					}
				}
			}
			
            allNames[allNames.length] = fieldName;
			var uniqueNames = new Array();
			for(var i = 0; i < allNames.length; i++) {
				if (epoint.ow.Utils.arraySearch(uniqueNames, allNames[i]) == -1) {
					uniqueNames[uniqueNames.length] = allNames[i];
				}
			}
			
			return uniqueNames;
		}
	};
	
	var ValidatorFunctionManager = $package.ValidatorFunctionManager = {
		
		/**
		 * Zarejestrowane funkcje walidatorów JS.
		 */
		validatorFunctions : {},
		
		/**
		 * Ustawia walidator JS o podanej nazwie.
		 * 
		 * @param validatorId pełna nazwa walidatora
		 * @param validatorFn funkcja walidatora JS
		 */
		setValidator : function(validatorId, validatorFn) {
			this.validatorFunctions[validatorId] = validatorFn;
		},
		
		/**
		 * Zwraca komunikat błędu walidacji.
		 * 
		 * @param config konfiguracja walidatora
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 * @param errorCode kod błędu walidacji
		 * @param errorParameters parametry błędu walidacji
		 * @return komunikat błędu walidacji
		 */
		formatMessage : function (config, formName, fieldName, errorCode, errorParameters) {
			var msg = config.errorMessagesMap[errorCode];
			if (fieldName != null) {
				var values = epoint.ow.Utils.getFieldValues(formName, fieldName);
				if (values.length > 1) {
					msg = msg.replace('{field_value}', values);
				} else if (values.length == 1) {
					msg = msg.replace('{field_value}', values[0]);
				} else {
					msg = msg.replace('{field_value}', "");
				}
			}

			for (var parameterKey in errorParameters) {
				msg = msg.replace('{' + parameterKey + '}', errorParameters[parameterKey]);
			}
			return msg;
		},
		
		/**
		 * Uruchamia podany walidator jednopolowy (jeżeli <code>fieldName != null</code>) lub wielopolowy.
		 * 
		 * @param config konfiguracja walidatora
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola - <code>!= null</code> jeżeli walidator <code>config</code> jest walidatorem jednopolowym
		 * @return <code>false</code> jeżeli wystąpiły błędy walidacji
		 */
		runConfigValidator : function (config, formName, fieldName) {
			// nie waliduj, jeżeli nie ma walidatora JS
			if (!config.hasJSValidator) {
				return true;
			}

			// nie waliduj, jeżeli nie wszystkie pola wypełnione i "runIfNotAllFieldsFilled == false" 
			if ((config.runIfNotAllFieldsFilled == false) && (!epoint.ow.Utils.isAllFieldsFilled(formName, config.allFieldNames))) {
				return true;
			}
			
			// nie waliduj, jeżeli powiązane z walidatorem pola zawierają błędy
			for(var name in config.allFieldNames) {
				if (!ErrorManager.isFieldValid(formName, name)) {
					return true;
				}
			}

			var validationResult = null;
			if (fieldName != null) {
				validationResult = this.runFieldValidator(config.JSFunctionName, formName, fieldName, config.validatorParameters);
			} else {
				validationResult = this.runMultiFieldValidator(config.JSFunctionName, formName, config.validatorParameters);
			}
			
			if (validationResult == null) {
				return true;
			}
			
			ErrorManager.addError(formName, fieldName, config.validatorName, validationResult.errorCode,
					validationResult.errorParameters, validationResult.mainFieldName, validationResult.notValidFieldNames);
			return false;
		},
		
		/**
		 * Uruchamia podany walidator jednopolowy.
		 * 
		 * @param validatorId nazwa funkcji walidatora JS
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 * @param parameters parametry walidatora
		 * @return błąd walidacji formularza lub <code>null</code>
		 */
		runFieldValidator : function(validatorId, formName, fieldName, parameters) {
			if (this.validatorFunctions[validatorId]) {
				return this.validatorFunctions[validatorId].apply(this, [formName, fieldName].concat(parameters));
			} else {
				return null;
			}
		},

		/**
		 * Uruchamia podany walidator wielopolowy.
		 * 
		 * @param validatorId nazwa funkcji walidatora JS
		 * @param formName nazwa formularza
		 * @param parameters parametry walidatora
		 * @return błąd walidacji formularza lub <code>null</code>
		 */
		runMultiFieldValidator : function(validatorId, formName, parameters) {
			if (this.validatorFunctions[validatorId]) {
				return this.validatorFunctions[validatorId].apply(this, [formName].concat(parameters));
			} else {
				return null;
			}
		}
	};
	
	var AjaxManager = $package.AjaxManager = {
		
		/**
		 * URL akcji walidacji formularza.
		 */
		VALIDATE_FORM_URL : "?action=forms.validateForm",
		
		VALIDATE_FIELD_RELATED_FIELDS_URL : "?action=forms.validateFieldRelatedFields",
		
		VALIDATE_FIELD_ON_CHANGE_URL : "?action=forms.validateFieldOnChange",
		
		/**
		 * Waliduje podane pole i pola powiązane, wykonuje akcje z obiektu <code>deferred</code>.
		 * 
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 * @param isOnChangeValidation czy jest to walidacja na zdarzeniu <code>change</code>
		 * @param deferred akcje wykonywane po zakończeniu walidacji - opcjonaly
		 */
		validateField : function(formName, fieldName, deferred) {
			if (deferred == null) {
				deferred = Utils.getDefaultFieldValidationDeferredObject(formName, fieldName);
			}
			
			var fieldNames = [];
			if (this.hasFieldValidatorsToRun(formName, fieldName)) {
				fieldNames = [fieldName];
			}
			
			if (fieldNames.length > 0) {
				this.submitForm(formName, 
								function(errorsArray) {
									if (errorsArray.length > 0) {
										ErrorManager.clearFieldErrors(formName, fieldName);
										ErrorManager.addErrors(formName, errorsArray);
		
						                deferred.reject();
									} else {
										ErrorManager.clearFieldErrors(formName, fieldName);
										deferred.resolve();
									}
								},
								["form", formName, "field", fieldName],
								this.VALIDATE_FIELD_ON_CHANGE_URL,
								null,
								fieldNames);
			} else {
				ErrorManager.clearFieldErrors(formName, fieldName);
				deferred.resolve();
			}
		},
		
		/**
		 * Waliduje podane pole i pola powiązane, wykonuje akcje z obiektu <code>deferred</code>.
		 * 
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 * @param isOnChangeValidation czy jest to walidacja na zdarzeniu <code>change</code>
		 * @param deferred akcje wykonywane po zakończeniu walidacji - opcjonaly
		 */
		validateRelatedFields : function(formName, fieldName, deferred) {
			if (deferred == null) {
				deferred = Utils.getDefaultFieldValidationDeferredObject(formName, fieldName);
			}
			
			var fieldNames = ConfigManager.getFieldRelatedFieldNamesForValidation(formName, fieldName, true);
			
			if (fieldNames.length > 0) {
				this.submitForm(formName, 
								function(errorsArray) {
									if (errorsArray.length > 0) {
										ErrorManager.clearFieldErrors(formName, fieldName);
										ErrorManager.addErrors(formName, errorsArray);
		
						                deferred.reject();
									} else {
										ErrorManager.clearFieldErrors(formName, fieldName);
										deferred.resolve();
									}
								},
								["form", formName, "field", fieldName],
								this.VALIDATE_FIELD_RELATED_FIELDS_URL,
								null,
								fieldNames);
			} else {
				ErrorManager.clearFieldErrors(formName, fieldName);
				deferred.resolve();
			}
		},
		
		/**
		 * Waliduje formularz i wykonuje akcje z obiektu <code>deferred</code>.
		 * 
		 * @param formName nazwa formularza
		 * @param deferred akcje wykonywane po zakończeniu walidacji - opcjonaly
		 */
		validateForm : function(formName, deferred) {
			if (deferred == null) {
				deferred = Utils.getDefaultFormValidationDeferredObject(formName);
			}
			
			this.submitForm(formName,
							function(errorsArray) {
								if (errorsArray.length > 0) {
									ErrorManager.clearFormErrors(formName);
									ErrorManager.addErrors(formName, errorsArray);
									
									deferred.reject();
								} else {
									ErrorManager.clearFormErrors(formName);
									deferred.resolve();
								}
							},
							
							["form", formName],
							this.VALIDATE_FORM_URL);
		},
		
		/**
		 * Sprawdza, czy dane pole zawiera walidatory AJAX do uruchomienia.
		 * 
		 * @param formName nazwa formularza
		 * @param fieldName nazwa pola
		 */
		hasFieldValidatorsToRun : function(formName, fieldName) {
			
			configs = ConfigManager.getFieldValidators(formName, fieldName);
			for(var i = 0; i < configs.length; i++) {
				var config = configs[i];
				if (config.isAjaxValidator &&
						(!config.hasJSValidator) &&
						config.isRunOnChangeEvent &&
						(config.runIfNotAllFieldsFilled || epoint.ow.Utils.getFieldValue(formName, fieldName) != null)) {
					return true;
				}
			}
			
			return false;
		},
		
		/**
		 * Submituje formularz.
		 * 
		 * @param formName nazwa formularza
		 * @param callbackFn funkcja callback
		 * @param parametersArray tablica dodatkowych parametrów - opcjonaly
		 * @param submitUrl URL, na który wysłany zostanie request - opcjonaly
		 * @param dataType typ danych - opcjonaly
		 * @param submitOnlyFieldNamesArray tablica nazw pól do submitowania - opcjonaly
		 */
		submitForm : function(formName, callbackFn, parametersArray, submitUrl, dataType, submitOnlyFieldNamesArray) {
			var form = epoint.ow.Utils.getForm(formName);
			
			var url = submitUrl;
			if (url == null) {
				url = form.getAttribute('action');
			}
			
			var params;
			if (submitOnlyFieldNamesArray != null) {
				params = $(form).find("[name=" + submitOnlyFieldNamesArray.join("],[name=") + "]").serializeArray();
			} else {
				params = $(form).serializeArray();
			}
			
			if (parametersArray != null) {
				for(var i = 0; i < parametersArray.length; i+=2) {
					params.push({"name" : parametersArray[i], "value" : parametersArray[i+1]});	
				}
			}
			
			if (dataType == null) {
				dataType = "json";
			}

			$.post(url, params, callbackFn, dataType);
		},
		
		/**
		 * Dodaje dynamiczne pola typu <code>hidden</code> przenoszące wartości parametrów.
		 * 
		 * @param formName nazwa formularza
		 * @param parametersArray tablica parametrów
		 */
		addParametersAsFormFields : function(formName, parametersArray) {
			var form = epoint.ow.Utils.getForm(formName);
			
			if (form != null && parametersArray != null && parametersArray.length > 0) {
				for(var i = 0; i < parametersArray.length; i+=2) {
					var fieldId = parametersArray[i] + "_parameter_value_carrier";
					var p = $("#" + fieldId).remove();
					var p = document.createElement("input");
					p.setAttribute("type", "hidden");
					p.setAttribute("name", parametersArray[i]);
					p.setAttribute("id", parametersArray[i] + "_parameter_value_carrier");
					p.setAttribute("value", parametersArray[i+1]);
					$(form).append(p);
				}
			}
		},
		
		submitFormWithIFrame : function(formName, callbackFn, parametersArray, submitUrl, dataType) {
			var form = epoint.ow.Utils.getForm(formName);
			var IFRAME_ID = form.getAttribute('id') + "_iframe";
			var IFRAME_NAME = form.getAttribute('name') + "_iframe";
			
			if (submitUrl == null) {
				submitUrl = form.getAttribute('action');
			}
			
			if (document.getElementById(IFRAME_ID) != null) {
				throw "submitFormWithIFrame: other submit in progress, can not submit form '" + formName + "' to URL:" + submitUrl;
			}
			
			var iframe = document.createElement('iframe');
			iframe.setAttribute('id', IFRAME_ID);
			iframe.setAttribute('name', IFRAME_NAME);
			iframe.setAttribute('src', "about:blank");
			
			$(iframe).load(function() {
				iframe.contentWindow.name = iframe.name; 
				$(iframe).unbind('load');
				$(iframe).load(function() {
					var field = iframe.contentWindow.document.getElementById('carrierField');
					if (field != null) {
						var value = field.value;
						
						if (dataType == "json") {
							value = jQuery.parseJSON(value);
						}
						document.body.removeChild(document.getElementById(IFRAME_ID));
						callbackFn.apply(document, [value]);
					} else {
						var isErrorFromOW = $(iframe.contentWindow.document.head).find('meta:[http-equiv=OneWeb-error-handler-response]').attr('content') == "true";
						if (isErrorFromOW) {
							epoint.ow.JSErrorManager.handleAjaxError({}, {
								responseText : $(iframe.contentWindow.document.body).html(),
								getResponseHeader : function(x){return x == "OneWeb-error-handler-response" ? "true" : ""}
							},
							{
								url : epoint.ow.Utils.getForm(formName).getAttribute('action'),
								dataType : dataType
							},
							"Unknown error from IFrame");
						} else {
							throw "Unknown error from IFrame";
						}
					}
				});
				
				AjaxManager.addParametersAsFormFields(formName, parametersArray);

				form.setAttribute('action', submitUrl);
				form.setAttribute('target', IFRAME_NAME);
				form.submit();
			});
			$(document.body).append(iframe);
			$(iframe).hide();
		}
	}
	
	// register Validator JS functions
	ValidatorFunctionManager.setValidator(
		"pl.epoint.ow.forms2.validators.ConstantValueValidator",
		function (formName, fieldName, isFieldConstant, constantValueArray) {
			if (!isFieldConstant) {
				return null;
			}
			var fieldValues = epoint.ow.Utils.getFieldValues(formName, fieldName);
		
			if (epoint.ow.Utils.arrayEquals(fieldValues, constantValueArray)) {
				return null;
			}
			
			return Utils.reportDefaultError({"default_value" : constantValueArray[0], "default_values" : constantValueArray});
		});
	
	ValidatorFunctionManager.setValidator(
		"pl.epoint.ow.forms2.validators.ValueRequiredValidator",
		function (formName, fieldName, isFieldRequired) {
			if (!isFieldRequired) {
				return null;
			}
			var values = epoint.ow.Utils.getFieldValues(formName, fieldName);
			if (values.length == 0) {
				return Utils.reportDefaultError({});
			}
		
			return null;
	});
	
	ValidatorFunctionManager.setValidator(
		"pl.epoint.ow.forms2.validators.TwoFieldIdentityValidator",
		function (formName, field1Name, field2Name) {
			var field1Value = epoint.ow.Utils.getFieldValues(formName, field1Name);
			var field2Value = epoint.ow.Utils.getFieldValues(formName, field2Name);
			
			if(!epoint.ow.Utils.arrayEquals(field1Value, field2Value)) {
				return Utils.reportDefaultError({});
			}
			
			return null;
	});
	
	ValidatorFunctionManager.setValidator(
			"pl.epoint.ow.forms2.validators.FileRequiredValidator",
			function (formName, fieldName, isFieldRequired, isTemporaryStorageMode, storageFilesSize) {
				if (!isFieldRequired) {
					return null;
				}
				if (isTemporaryStorageMode) {
					if (storageFilesSize == 0) {
						return Utils.reportDefaultError({});
					} 
				} else {
					var fieldValue = epoint.ow.Utils.getFieldValues(formName, fieldName);
					
					if (fieldValue == null) {
						return Utils.reportDefaultError({});
					}
				}
				
				return null;
		});
	
	ValidatorFunctionManager.setValidator(
			"pl.epoint.ow.forms2.validators.IntegerValidator",
			function (formName, fieldName, minValue, maxValue) {
				var value = epoint.ow.Utils.getFieldValue(formName, fieldName);
				
				if (isNaN(value) || value % 1 != 0) {
					return Utils.reportDefaultError({});
				}
				
				var number = parseInt(value);
				if (minValue != null && number < minValue) {
					return Utils.reportError('MIN', {'min_value': minValue});
				}
				if (maxValue != null && number > maxValue) {
					return Utils.reportError('MAX', {'max_value': maxValue});
				}
				
				return null;

		});
})();

