/***************************************************************
*  Copyright notice
*
*  (c) 2009 Stephan Schuler <stephan.schuler@netlogix.de>
*  All rights reserved
*
*  This script is part of the TYPO3 project. The TYPO3 project is
*  free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  The GNU General Public License can be found at
*  http://www.gnu.org/copyleft/gpl.html.
*  A copy is found in the textfile GPL.txt and important notices to the license
*  from the author is found in LICENSE.txt distributed with these scripts.
*
*
*  This script is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/




/**
 * Formular Validator
 *
 * $Id$
 */
var nxformvalidation = {




	validationRules: [],




	groupingAttribute: '.nx-tabform-tab',




	init: function(_param) {

		var param = jQuery.extend({
			performAddingEvents: true,
			validateOnChange: true
		}, _param);

		jQuery(nxformvalidation.onInitFunction).each(function(initFunctionKey, initFunction) {
			initFunction();
		});

		jQuery('form.nx-formvalidation').each(function(formKey, form) {
			nxformvalidation.addValidationHandlerToFormInputs(jQuery(form), param);
		});
		jQuery('.nx-formvalidation-wrapper').find('form').each(function(formKey, form) {
			nxformvalidation.addValidationHandlerToFormInputs(jQuery(form), param);
		});

		if (param.performAddingEvents) {
			nxformvalidation.performAddingEvents();
		}

	},




	performAddingEvents: function() {

		while (addEventFunction = nxformvalidation.addEventStack.pop()) {
			if (typeof addEventFunction == 'function') {
				addEventFunction();
			}
		};

	},




	addValidationGroupHandlers: function() {

		jQuery('.nx-formvalidation-validategroupNew').each(function(i, Button) {
			var button = jQuery(Button);
			var groupId = '';
			var formId = '';
			jQuery.each(button.attr('class').split(/\s/), function(j, className) {
				if (className.substr(0, 'nx-formvalidation-validategroup-'.length) == 'nx-formvalidation-validategroup-') {
					groupId = className.substr('nx-formvalidation-validategroup-'.length);
				}
				if (className.substr(0, 'nx-formvalidation-validateform-'.length) == 'nx-formvalidation-validateform-') {
					formId = className.substr('nx-formvalidation-validateform-'.length);
				}
			});

			nxformvalidation.addEventStack.push(function() {
				button.click(function(event) {
					var counter = 0;
					var invalidResultsList = nxformvalidation.validateForms(formId, groupId);
					jQuery.each(invalidResultsList, function(group, invalidResults) {
						jQuery.each(invalidResults, function(t) {
							counter++;
						});
					});
					if (counter !== 0) {
						event.stopPropagation();
						event.stopImmediatePropagation();
						event.preventDefault();
					}
				});
			});

		});

	},




	onInit: function(onInitFunction) {
		nxformvalidation.onInitFunction.push(onInitFunction);
	},




	onValidate: function(onValidateFunction) {
		nxformvalidation.onValidateFunction.push(onValidateFunction);
	},




	addValidationHandlerToFormInputs: function(form, param) {

		if (form.hasClass('nx-formvalidation-validated')) {
			return false;
		}
		form.addClass('nx-formvalidation-validated');

		nxformvalidation.addVirtualId(jQuery(form));

		form.find('.nx-formvalidation-input').each(function(inputKey, input) {
			inputField = jQuery(input);
			nxformvalidation.addVirtualId(inputField);
			var validationRules = nxformvalidation.getValidationRules(inputField, form, param);
		});

		form.find(nxformvalidation.groupingAttribute).each(function(i, group) {
			nxformvalidation.addGroupValidationHandler(form, jQuery(group));
		});

		form.submit(function(e) {
			return nxformvalidation.formSubmit(e, form);
		});

	},




	addGroupValidationHandler: function(form, group) {
		group.find('.nx-formvalidation-validategroup').each(function(i, button) {
			nxformvalidation.addVirtualId(jQuery(button));
			jQuery(button).addClass('nx-formvalidation-validategroup-'+group.attr('id'));
			jQuery(button).addClass('nx-formvalidation-validateform-'+form.attr('id'));
			jQuery(button).addClass('nx-formvalidation-validategroupNew');
		});
	},




	addVirtualId: function(element) {

		if(!element.attr('id')) {
			nxformvalidation.virtualIdCounter++;
			element.attr('id', 'nx-formvalidation-virtualid-'+nxformvalidation.virtualIdCounter);
		}

	},




	formSubmit: function(e, form) {

		var invalidInputs = nxformvalidation.validateForms(form.attr('id'));

		var formInvalidInputs = [];
		if (invalidInputs[form.attr('id')]) {
			while (invalidInput = invalidInputs[form.attr('id')].pop()) {
				var formChild = form.find('*').each(function (formChildKey, formChild) {
					if(invalidInput[0] == formChild) {
						formInvalidInputs.push(invalidInput);
					}
				});
			};
		}

		if (formInvalidInputs.length == 0) {
			return true;
		}
		else {
			e.stopImmediatePropagation();
			e.stopPropagation();
			return false;
		}

		return false;

	},




	getValidationRules: function(inputField, containingForm, param) {

		var responsibleRules = [];
		jQuery((inputField.attr('class').split(' '))).each(function(classNameKey, className) {
			jQuery(nxformvalidation.validationRules).each(function(ruleObjectKey, ruleObject) {
				if (ruleObject.responsible(className)) {
					responsibleRules.push({
						className: className,
						ruleObject: ruleObject
					});
				}
			});
		});
		if(responsibleRules.length > 0) {
			nxformvalidation.validationJobs.push({
				inputField: inputField,
				containingForm: containingForm,
				responsibleRules: responsibleRules,
				overruled: false
			});
			if (param.validateOnChange) {
				inputField.change(function() {
					nxformvalidation.validateForms(containingForm.attr('id'));
				});
			}
		}

	},




	validateRules: function(inputField, responsibleRules) {
		/*
		if (inputField.hasClass('nx-formvalidation-empty') && inputField.val()=='') {
			return false;
		}
		*/
		var errorFound = false;
		jQuery(responsibleRules).each(function(ruleObjectKey, ruleObject) {

			if (!errorFound) {
				var className = ruleObject.className;
				var parameters = ruleObject.ruleObject.parameters(className);
				var currentValidationState = ruleObject.ruleObject.valid(inputField, parameters, className);
				if (!currentValidationState) {
					errorFound = ruleObject;
				}
			}

		});
		if(errorFound) {
			errorFound = false;
			var regExp = new RegExp('nx-formvalidation-error-(.+)');
			jQuery(inputField.attr('class').split(/\s/)).each(function(classNameKey, className) {
				var match = (className.match(regExp));
				if (match) {
					errorFound = 'nx-formvalidation-errorfield-'+match[1];
				}
			});
		}

		return errorFound;

	},




	validateForms: function(restrictToSingleForm, restrictToSingleGroup) {

		var errorBlockClassNames = {};
		var validationAnyGroups = {};

		jQuery(nxformvalidation.validationJobs).each(function(validationJobKey, validationJob) {
			var anyGroupName = nxformvalidation.getAnyGroupName(validationJob.inputField);
			var formId = validationJob.containingForm.attr('id');
			if (!validationAnyGroups[formId]) {
				validationAnyGroups[formId] = [];
			}
			if (!validationAnyGroups[formId][anyGroupName]) {
				validationAnyGroups[formId][anyGroupName] = [];
			}
			validationAnyGroups[formId][anyGroupName].push(validationJob);
		});

		var invalidInputs = {};
		jQuery(nxformvalidation.onValidateFunction).each(function(validationFunctionKey, validationFunction) {
			validationFunction(restrictToSingleForm, validationAnyGroups);
		});

		for (var formId in validationAnyGroups) {
			if (!restrictToSingleForm || restrictToSingleForm==formId) {

				invalidInputs[formId] = [];

				for (var anyGroupKey in validationAnyGroups[formId]) {

					if(typeof validationAnyGroups[formId][anyGroupKey] != 'object') {
						continue;
					}

					var anyGroup = validationAnyGroups[formId][anyGroupKey];
					var anyInputFalid = false;
					var anyErrorBlockClassNames = [];

					var invalidGroupInputs = [];
					jQuery(anyGroup).each(function(validationJobKey, validationJob) {

						if(!validationJob.overruled) {

							var errorBlockClassName = nxformvalidation.validateRules(validationJob.inputField, validationJob.responsibleRules);

							if(errorBlockClassName) {
								invalidGroupInputs.push(validationJob.inputField);
								anyErrorBlockClassNames.push(errorBlockClassName);
							}
							else {
								anyInputFalid = true;
							}

						}
						else {
							validationJob.overruled = false;
						}

					});
					if (!anyInputFalid) {
						while (invalidGroupInput = invalidGroupInputs.pop()) {
							invalidInputs[formId].push(invalidGroupInput);
						}
						while(errorBlockClassName = anyErrorBlockClassNames.pop()) {
							errorBlockClassNames[errorBlockClassName] = errorBlockClassName;
							nxformvalidation.errorBlockClassNames[errorBlockClassName] = errorBlockClassName;
						}
					}

				};

			};
		};

		var groupedInvalidInputs = nxformvalidation.groupInvalidInputs(invalidInputs, restrictToSingleForm, restrictToSingleGroup);

		nxformvalidation.changeCssClasses(errorBlockClassNames, groupedInvalidInputs, restrictToSingleForm);

		return groupedInvalidInputs;

	},




	groupInvalidInputs: function(invalidInputs, restrictToSingleForm, restrictToSingleGroup) {

		if (!restrictToSingleForm) {
			return invalidInputs;
		}

		if (!restrictToSingleGroup) {
			return invalidInputs;
		}

		if (typeof invalidInputs[restrictToSingleForm] == 'undefined') {
			return [];
		}

		var groupedInvalidInputs = {};

		jQuery.each(invalidInputs[restrictToSingleForm], function(i, input) {
			var targetGroup = '---';
			jQuery(nxformvalidation.groupingAttribute).each(function(j, groupBlock) {
				jQuery(groupBlock).find('#'+input.attr('id')).each(function() {
					targetGroup = jQuery(groupBlock).attr('id');
				});
			});
			if (typeof groupedInvalidInputs[targetGroup] == 'undefined') {
				groupedInvalidInputs[targetGroup] = [];
			}
			groupedInvalidInputs[targetGroup].push(input);
		});

		if (typeof groupedInvalidInputs[restrictToSingleGroup] == 'undefined') {
			return [];
		}
		else {
			var result = {};
			result[restrictToSingleForm] = groupedInvalidInputs[restrictToSingleGroup];
			return result;
		}

	},




	getAnyGroupName: function(inputField) {
		if (!nxformvalidation.anyGroupCounter) {
			nxformvalidation.anyGroupCounter = 0;
		}
		nxformvalidation.anyGroupCounter++;
		var anyGroupName = 'default-'+nxformvalidation.anyGroupCounter;
		jQuery(inputField.attr('class').split(' ')).each(function(classNameKey, className) {
			if(className.substr(0, 22) == 'nx-formvalidation-any-') {
				anyGroupName = className.substr(22);
			}
		});
		return anyGroupName;
	},




	changeCssClasses: function(errorBlockClassNames, invalidInputs, restrictToSingleForm) {

		for (var className in nxformvalidation.errorBlockClassNames) {
			if (typeof nxformvalidation.errorBlockClassNames[className] == 'string') {
				jQuery('.'+className)
					.addClass('nx-formvalidation-errorfield-errorblock-noerrorsfound')
					.removeClass('nx-formvalidation-errorfield-errorblock-errorsfound')
					;
			}
		};

		for (var className in errorBlockClassNames) {
			if (typeof errorBlockClassNames[className] == 'string') {
				jQuery('.'+className)
				.removeClass('nx-formvalidation-errorfield-errorblock-noerrorsfound')
				.addClass('nx-formvalidation-errorfield-errorblock-errorsfound')
					;
			}
		};

		var responsibleLabelFields = {};
		for (var validationJobKey in nxformvalidation.validationJobs) {
			var validationJob = nxformvalidation.validationJobs[validationJobKey];
			if (typeof validationJob == 'object') {
				if (!restrictToSingleForm || restrictToSingleForm == validationJob.containingForm.attr('id')) {
					validationJob.inputField
						.removeClass('nx-formvalidation-inputfield-errorsfound')
						.addClass('nx-formvalidation-inputfield-noerrorsfound')
					;
					if(validationJob.inputField.attr('id')) {
						var labelQuery = 'label[for='+validationJob.inputField.attr('id')+']';
						jQuery('form#'+validationJob.containingForm.attr('id')).find(labelQuery).each(function(labelKey, label) {
							responsibleLabelFields[validationJob.inputField.attr('id')] = {
								name: validationJob.inputField.attr('name'),
								id: validationJob.inputField.attr('id'),
								label: jQuery(label),
								validationJob: validationJob
							};
							jQuery(label)
								.removeClass('nx-formvalidation-inputlabel-errorsfound')
								.addClass('nx-formvalidation-inputlabel-noerrorsfound')
							;
						});
					}
				}
			}
		}

		for (var formKey in invalidInputs) {
			if(typeof invalidInputs[formKey] == 'object') {
				if (!restrictToSingleForm || restrictToSingleForm == formKey) {
					for (var invalidInputKey in invalidInputs[formKey]) {
						var invalidInput = invalidInputs[formKey][invalidInputKey];
						if (typeof invalidInput == 'object') {
							invalidInput
								.removeClass('nx-formvalidation-inputfield-noerrorsfound')
								.addClass('nx-formvalidation-inputfield-errorsfound')
							;
							if (invalidInput.attr('id')) {
								if (responsibleLabelFields[invalidInput.attr('id')]) {
									responsibleLabelFields[invalidInput.attr('id')].label
										.removeClass('nx-formvalidation-inputlabel-noerrorsfound')
										.addClass('nx-formvalidation-inputlabel-errorsfound')
									;
								}
							}
						}
					}
				}
			}
		}

	},




	validationJobs: [],




	errorBlockClassNames: {},




	onInitFunction: [],




	onValidateFunction: [],




	virtualIdCounter: 0,




	addEventStack: []




};




/**
 * Validation rule for min length
 */
nxformvalidation.validationRules.push({

	responsible: function(className) {
		if(className.substr(0, 28) == 'nx-formvalidation-minlength-')
			return true;
		return false;
	},

	parameters: function(className) {
		var returnValue = {
				minLength: 10
		};
		var minLength = (parseInt(className.substr(28)));
		if (minLength != 0) {
			returnValue.minLength = minLength;
		}
		return returnValue;

	},

	valid: function(inputObject, parameters, className) {
//		console.log(inputObject,  inputObject.val(), inputObject.val().length);
		if (inputObject.val().length < parameters.minLength) {
			return false;
		}
		else {
			return true;
		}
	}

});




/**
 * Validation rule for max length
 */
nxformvalidation.validationRules.push({

	responsible: function(className) {
		if(className.substr(0, 28) == 'nx-formvalidation-maxlength-')
			return true;
		return false;
	},

	parameters: function(className) {
		var returnValue = {
				maxLength: 10
		};
		var maxLength = (parseInt(className.substr(28)));
		if (maxLength != 0) {
			returnValue.maxLength = maxLength;
		}
		return returnValue;
	},

	valid: function(inputObject, parameters, className) {
		if (inputObject.val().length > parameters.maxLength) {
			return false;
		}
		else {
			return true;
		}
	}

});




/**
 * Validation rule for integers having a minimum value
 */
nxformvalidation.validationRules.push({

	responsible: function(className) {
		if(className.substr(0, 27) == 'nx-formvalidation-minvalue-')
			return true;
		return false;
	},

	parameters: function(className) {
		var returnValue = {
				minValue: 0
		};
		var minValue = (parseInt(className.substr(27)));
		if (minValue != 0) {
			returnValue.minValue = minValue;
		}
		return returnValue;
	},

	valid: function(inputObject, parameters, className) {
		if (parseInt(inputObject.val()) >= parameters.minValue) {
			return true;
		}
		else {
			return false;
		}
	}

});




/**
 * Validation rule for integers having a maximum value
 */
nxformvalidation.validationRules.push({

	responsible: function(className) {
		if(className.substr(0, 27) == 'nx-formvalidation-maxvalue-')
			return true;
		return false;
	},

	parameters: function(className) {
		var returnValue = {
				maxValue: 100000000
		};
		var maxValue = (parseInt(className.substr(27)));
		if (maxValue != 0) {
			returnValue.maxValue = maxValue;
		}
		return returnValue;
	},

	valid: function(inputObject, parameters, className) {
		if (parseInt(inputObject.val()) <= parameters.maxValue) {
			return true;
		}
		else {
			return false;
		}
	}

});




/**
 * Validation rule for email adresses
 */
nxformvalidation.validationRules.push({

	responsible: function(className) {
		if(className.substr(0, 23) == 'nx-formvalidation-email')
			return true;
		return false;
	},

	parameters: function(className) {
		return {};
	},

	valid: function(inputObject, parameters, className) {
//		var matchingExpression = new RegExp("^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$", 'i');
		var matchingExpression = new RegExp("^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+(?:[A-Z]{2}|com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum)$", 'i');
		if (!matchingExpression.test(inputObject.val())) {
			return false;
		}
		else {
			return true;
		}
	}

});




/**
 * Validation rule unchecked checkboxes
 */
nxformvalidation.validationRules.push({

	responsible: function(className) {
		if(className.substr(0, 25) == 'nx-formvalidation-checked')
			return true;
		return false;
	},

	parameters: function(className) {
		return {};
	},

	valid: function(inputObject, parameters, className) {

		return (inputObject.attr('checked'));

	}

});




/**
 * Validation rule for unchecked checkboxes
 */
nxformvalidation.validationRules.push({

	responsible: function(className) {
		if(className.substr(0, 27) == 'nx-formvalidation-unchecked')
			return true;
		return false;
	},

	parameters: function(className) {
		return {};
	},

	valid: function(inputObject, parameters, className) {

		return !(inputObject.attr('checked'));

	}

});




/**
 * Validation rule for integers
 */
nxformvalidation.validationRules.push({

	responsible: function(className) {
		if(className.substr(0, 25) == 'nx-formvalidation-integer')
			return true;
		return false;
	},

	parameters: function(className) {
		var returnValue = {
				intLength: 0
		};
		var intLength = (parseInt(className.substr(26)));
		if (intLength != 0) {
			returnValue.intLength = intLength;
		}
		if (returnValue.intLength == 0) {
			returnValue.minLength = 0;
			returnValue.maxLength = 10000;
		}
		else {
			returnValue.minLength = intLength;
			returnValue.maxLength = intLength;
		}
		return returnValue;
	},

	valid: function(inputObject, parameters, className) {
		var matchingExpression = new RegExp('^\\d{'+(parameters.minLength)+','+(parameters.maxLength)+'}$');
		if (!matchingExpression.test(inputObject.val())) {
			return false;
		}
		else {
			return true;
		}
	}

});




/**
 * Example form validation onInit hook
 */
nxformvalidation.onInit(function() {
	// noop();
});


if (typeof checkbox != 'function') {
	checkbox = function() {};
}

jQuery(document).ready(function () {
	nxformvalidation.init();
});



