/*-----------------------------*/
/* Requires:	jquery.js				*/
/*					metadata.js			*/
/* Objects:								*/
/* -validationContainer				*/
/* -validationElement				*/
/* -validationForm					*/
/*	Version 2.0beta					*/
/*-----------------------------*/

jQuery.extend({
	fvjq_FvVersion: '2.0beta',
	fvjq_autoStart: true,
	fvjq_scriptPath: 'js/',
	fvjq_validationClassName: 'validated',
	fvjq_validationFunctions: new Object(),
	fvjq_valFuncCheck: new Object(),
	fvjq_validatedForms: new Object(),
	fvjq_vrEmpty: 1,
	fvjq_vrValid: 2,
	fvjq_vrInvalid: 3,
	addFormValidators: function(){
		$("form." + $.fvjq_validationClassName).addFormValidator();
		setVersionNumber();
	},
	addValidationFunction: function(domain,validationFunction){
		if ((typeof($.fvjq_validationFunctions[domain]) != 'function') && (typeof($.fvjq_valFuncCheck[domain]) == 'undefined')){
			if(validationFunction){
				$.registerValidationFunction(domain,validationFunction);
			}else{
			    $.fvjq_valFuncCheck[domain] = true;
				$.getScript($.fvjq_scriptPath+domain+'.js');
			}
		}
	},
	registerValidationFunction: function(domain,aFunction){
		if (typeof($.fvjq_validationFunctions[domain]) == 'undefined'){
			$.fvjq_validationFunctions[domain] = aFunction;
			$.fvjq_valFuncCheck[domain] = true;
			debugTrace('Function registered for domain <strong>'+domain+'</strong>');
		}
	},
	validateSection: function(formName, sectionName){
		return $.fvjq_validatedForms[formName].validateSection(sectionName);
	}
});

jQuery.fn.extend({
	addFormValidator: function(){
		this.each(function(i){
			if ((this.tagName.toUpperCase()=='FORM')&&(!$.fvjq_validatedForms[this.id])){
			    debugTrace('Added form ' + this.id);
			    var aMetaData = $(this).data();
				var aFrm = new validationForm(this,aMetaData,i);
				aFrm.debug = (aMetaData.debug) ? (typeof(debugObj) != 'undefined') : aMetaData.debug;
				$.fvjq_validatedForms[this.id] = aFrm;
				$(this).bind("submit",function(){
					return aFrm.validate();
				});
				return true;
			}else{
				return false;
			}
		});
	},
	addValidation: function(formName,dataObj,domain,validationFunction){
		if ((typeof(validationFunction) != 'undefined')&&(typeof(domain) != 'undefined')){
			$.addValidationFunction(domain,validationFunction);
		}
		var aFrm = $.fvjq_validatedForms[formName];
		if (aFrm){
			this.each(function(i){
				if (this.type){
					$(this).data(dataObj);
					aFrm.addContainer(this);
					return true;
				}else{
					return false;
				}
			});
			aFrm.setDependencies();
		}else{
			debugTrace(aFrm+' not found in validator list!',true);
		}
	}
});

if ($.fvjq_autoStart){
    $(document).ready($.addFormValidators);
}

function validationContainer(formInputElement, metaDataObj){
    this.enabled = true;
	this.name = formInputElement.name;
	this.formObject = metaDataObj.formObject;
	this.isCheckable = ((formInputElement.type == 'radio')||(formInputElement.type == 'checkbox'));
	this.validationElements = new Array();
	this.dependsOn = (metaDataObj.dependson) ? metaDataObj.dependson.split(',') : null;
	if ((this.dependsOn) && (!metaDataObj.dependencyconditions)){
	    this.dependencyConditions = new Array();
	    for(var i=0;i<this.dependsOn.length;i++){
	        this.dependencyConditions.push(true); 
        } 
	}else{
	    this.dependencyConditions = metaDataObj.dependencyconditions;
	}
	this.sortOrder = metaDataObj.sortorder;

	this.required = (metaDataObj.required) ? metaDataObj.required : false;
	this.requiredMinimum = (metaDataObj.requiredminimum) ? metaDataObj.requiredminimum : null;
	this.requiredMaximum = (metaDataObj.requiredmaximum) ? metaDataObj.requiredmaximum : null;
	if (metaDataObj.requiredrange){
		if (typeof(metaDataObj.requiredrange) != 'array'){
			metaDataObj.requiredrange = String(metaDataObj.requiredrange).split(',');
		}
		if (metaDataObj.requiredrange.length == 2){
			this.requiredMinimum = parseInt(metaDataObj.requiredrange[0]);
			this.requiredMaximum = parseInt(metaDataObj.requiredrange[1]);
		}
	}
	if (this.dependsOn == null){
		if ((this.requiredMinimum != null)  || (this.requiredMaximum != null)){
			this.required = true;	
		}
	}
};

validationContainer.prototype.addValidationElement = function(formInputElement){
	var allELms = this.validationElements;
	var aResult = $.grep(allELms, function(o,i){
		return (o.formInputElement == formInputElement);
	});
	if (aResult.length == 0){
		var aMetaData = $(formInputElement).data();
		aMetaData.required = this.required;
		aMetaData.formObject = this.formObject;
		this.validationElements.push(new validationElement(formInputElement,aMetaData, this));
		debugTrace('Added element to container <strong>'+this.name+'</strong>');
	}
};

validationContainer.prototype.validate = function(){
    if (!this.enabled){ return null; }
	if (this.dependsOn == null){
		return this.doValidate();
	}
	else{
		var dps = this.dependsOn;
		var dpCnds = this.dependencyConditions;
		if ($.grep(dps, function(o,i){
		    var aCondition = dpCnds[i];
		    if (typeof(aCondition) == 'boolean'){
			    return (o.isUsed() == aCondition);
			}else if (typeof(aCondition) == 'function'){
			    return aCondition(o);
			}
		}).length > 0){
			return this.doValidate();
		}else{
			return null;
		}
	}
};

validationContainer.prototype.doValidate = function(){
	var allElms = this.validationElements;
	var thisContainer = this;
	var useCount = 0;
	var InvalidElements = $.grep(allElms,function(o,i){
		var aRetval = o.validate();
		if (aRetval != $.fvjq_vrEmpty){
			useCount += o.lastUseCount ;
		}
		if ((o.required) || (o.parentContainer.dependsOn != null)){
			return ((aRetval == $.fvjq_vrEmpty) || (aRetval == $.fvjq_vrInvalid));
		}else{
			return (aRetval == $.fvjq_vrInvalid);
		}
	});
	if ((this.isCheckable) && (useCount > 0)){
		InvalidElements.length = 0;
	}
	if ((this.requiredMinimum != null) && (this.requiredMaximum != null)){
		if ((useCount >= this.requiredMinimum) && (useCount <= this.requiredMaximum)){
			return null;
		}else{
			return this.validationElements[0];
		}
	}else if (this.requiredMinimum != null){
		if (useCount >= this.requiredMinimum){
			return null;
		}else{
			return this.validationElements[0]; 
		}
	}else if (this.requiredMaximum != null){
		if (useCount <= this.requiredMaximum){
			return null;
		}else{
			return this.validationElements[0]; 
		}
	}else	if (InvalidElements.length > 0){
		return InvalidElements[0];
	}else{
		return null;
	}
};

validationContainer.prototype.isInSection = function(aSectionName)
{
    var aParentNode = this.validationElements[0].formInputElement.parentNode;
    while(aParentNode != null)
    {
        if (aParentNode.attributes)
        {
            if (($(aParentNode).attr('id')) && $(aParentNode).attr('id').toLowerCase() == aSectionName.toLowerCase())
            {
                return true;
            }
        }
        aParentNode = aParentNode.parentNode;
    }
    return false;
};

function validationElement(formInputElement, metaDataObj, parent){
    this.enabled = true;
	this.formInputElement = formInputElement;
	this.formObject = metaDataObj.formObject;
	if (formInputElement.id.length < 1){
	    formInputElement.id = this.formObject.form.name + this.formInputElement.name + this.formInputElement.tabIndex;
	}
	this.parentContainer = parent;
	this.domain = (metaDataObj.domain) ? metaDataObj.domain  : null;
	this.parameters = (metaDataObj.parameters) ? String(metaDataObj.parameters).split(',') : null;
	this.errorMessage = (metaDataObj.errormessage) ? metaDataObj.errormessage : null;
	this.requiredMessage = (metaDataObj.requiredmessage) ? metaDataObj.requiredmessage : null;
    this.onValidationError = metaDataObj.onerror;
    this.onRequiredError = metaDataObj.onrequired;
	this.defaultValue = (metaDataObj.defaultvalue) ? metaDataObj.defaultvalue : '';
	this.lastResult = null;
	this.lastUseCount  = 0;
	this.required = metaDataObj.required;

	var aValElm = this;
	$(formInputElement).bind('blur',(metaDataObj.onblur) ? function(){aValElm.validate();return metaDataObj.onblur(aValElm)} : new Function());
	$(formInputElement).bind('focus',(metaDataObj.onfocus) ? function(){metaDataObj.onfocus(aValElm)} : new Function());
	if (metaDataObj.firstfocus){
		formInputElement.focus();
	}
};

validationElement.prototype.validate = function(){
	this.lastResult = $.fvjq_vrValid;
	if (!this.enabled){ return this.lastResult; }
	if(((this.required)||(this.parentContainer.dependsOn != null)) && (!this.isUsed())){
		this.lastResult = $.fvjq_vrEmpty;
	}else if ((this.domain) && (this.isUsed())){
	    var aDomains = this.domain;
	    var aElm = this; 
	    this.lastResult = ($.grep(aDomains,function(o,i){
	         return (!$.fvjq_validationFunctions[o](aElm));
	    }).length > 0) ? $.fvjq_vrInvalid : $.fvjq_vrValid;
	}
	return this.lastResult;
};

validationElement.prototype.isUsed = function(){
	this.lastUseCount  = 0;
	switch(this.formInputElement.type)
	{
		case 'checkbox':
		case 'radio':
			var retVal = this.formInputElement.checked;
			this.lastUseCount  = (retVal) ? 1 : 0;
			return retVal;
			break;
		case 'file':
		case 'text':
		case 'password':
		case 'textarea':
			var retVal = ((this.trim(this.formInputElement.value).length > 0) && (!this.hasDefaultValue()));
			this.lastUseCount  = (retVal) ? 1 : 0;
			return retVal;
			break;
		case 'select':
		case 'select-one':
			var retVal = ((this.trim(this.formInputElement.value).length > 0) && (!this.hasDefaultValue()));
			this.lastUseCount  = (retVal) ? 1 : 0;
			break;
		case 'select-multiple':
			for(var i=0,aOption;aOption=this.formInputElement.options[i];i++){
				if ((aOption.selected == true) && (aOption.value != this.defaultValue)){
					this.lastUseCount ++;
				}
			}
			return (this.lastUseCount  > 0);
			break;
		default:
			return false;
			break;
	}
};

validationElement.prototype.hasDefaultValue = function(){
	if (this.defaultValue != null){
		switch(this.formInputElement.type)
		{
			case 'checkbox':
			case 'radio':
				return false;
				break;
			case 'file':
			case 'text':
			case 'password':
			case 'textarea':
				return (this.formInputElement.value == this.defaultValue);
				break;
			case 'select':
			case 'select-one':
			case 'select-multiple':
				var aValue = this.formInputElement.options[this.formInputElement.options.selectedIndex].value;
				return (aValue == this.defaultValue);
				break;
			default:
				return false;
				break;
		}
	}
	return false;
};

validationElement.prototype.hasValue = function(){
	switch(this.formInputElement.type)
	{
		case 'checkbox':
		case 'radio':
			return true;
			break;
		case 'file':
		case 'text':
		case 'textarea':
			return ((this.trim(this.formInputElement.value).length > 0) && (!this.hasDefaultValue()));
			break;
		case 'select':
		case 'select-one':
		case 'select-multiple':
			return ((this.formInputElement.options.selectedIndex > -1) && (!this.hasDefaultValue()));
			break;
		default:
			return false;
			break;
	}
};

validationElement.prototype.trim = function(str)
{
	return str.replace(/^\s*(.*)/, "$1").replace(/(.*)\s*$/, "$1");
};

function validationForm(form, metaDataObj, index){
	form.id = (form.id != '') ? form.id : form.name;
	this.enabled = true;
	this.form = form;
	this.debug = false;
	this.containers = new Array();
	
	$.fvjq_scriptPath = (metaDataObj.scriptpath) ? metaDataObj.scriptpath : $.fvjq_scriptPath;
	$.fvjq_scriptPath += ($.fvjq_scriptPath.substr(($.fvjq_scriptPath.length-1),1) != '/') ? '/' : '';
	this.validateAll = (metaDataObj.validateall) ? metaDataObj.validateall : false;
	this.onBlurFunction = (metaDataObj.onblur) ? metaDataObj.onblur : new Function();
	this.onFocusFunction = (metaDataObj.onfocus) ? metaDataObj.onfocus : new Function();
	this.onValidationError = (metaDataObj.onerror) ? metaDataObj.onerror : defaultValidationError;
	this.onRequiredError = (metaDataObj.onrequired) ? metaDataObj.onrequired : defaultRequiredError;
	this.onBeforeValidation =(metaDataObj.onbeforevalidation) ? metaDataObj.onbeforevalidation : function(){return true;};
	this.onAfterValidation =(metaDataObj.onaftervalidation) ? metaDataObj.onaftervalidation : function(isValid){return isValid;};
	this.onBeforeSectionValidation =(metaDataObj.onbeforesectionvalidation) ? metaDataObj.onbeforesectionvalidation : function(){return true;};
	this.onAfterSectionValidation =(metaDataObj.onaftersectionvalidation) ? metaDataObj.onaftersectionvalidation : function(isValid){return isValid;};
	
	this.initialise();
};

validationForm.prototype.addSubmitter = function(aElement){
    var aMetaObj = $(aElement).data();
    var aFrm = this; 
    if (!aMetaObj.section){
        debugTrace('Submitter added');
		$(aElement).bind('click',function(){
			if ((aFrm.validate()) && (!aFrm.debug)){
				aFrm.form.submit();
			}
			return false;
		});
    }else{
        debugTrace('Section submitter added for ' + aMetaObj.section);  
		$(aElement).bind('click',function(){
			aFrm.validateSection(aMetaObj.section);
			return false;
		});
    }   
};

validationForm.prototype.addContainer = function(formInputElement){
	var aMetaObj= $(formInputElement).data();
	aMetaObj.sortorder = formInputElement.tabIndex || 0;
	var aContainer = null;
	var aContainers = $.grep(this.containers, function(o,i){
		return (o.name == formInputElement.name);
	});
	if (aContainers.length < 1){
		aMetaObj.formObject = this;
		aContainer = new validationContainer(formInputElement,aMetaObj);
		this.containers.push(aContainer);
		debugTrace('Container <strong>'+formInputElement.name+'</strong> added');
	}else{
		aContainer = aContainers[0];
	}
	if (aMetaObj.domain)
	{
	    var aDomains = (typeof(aMetaObj.domain) == 'string') ? aMetaObj.domain.split(',') : aMetaObj.domain;
	    $(aDomains).each(function(i){
		    $.addValidationFunction(this);
		});
		aMetaObj.domain = aDomains;
	}
	aMetaObj.required = aContainer.required;
	if (!aMetaObj.onblur){
		aMetaObj.onblur = this.onBlurFunction;
	}
	if (!aMetaObj.onfocus){
		aMetaObj.onfocus = this.onFocusFunction;
	}
	if(!aMetaObj.onerror){
    	aMetaObj.onerror = this.onValidationError;
	}
	if(!aMetaObj.onrequired){
        aMetaObj.onrequired = this.onRequiredError;
	}
	aContainer.addValidationElement(formInputElement,aMetaObj);
};

validationForm.prototype.initialise = function(){
	var frm = this;
	$("#" + this.form.id + " ." + $.fvjq_validationClassName).each(function(i){
		if (this.tabIndex < 1){
			this.tabIndex = i+100;
		}
		if (!$(this).data().submitter){
		    frm.addContainer(this);
		}else{
		    frm.addSubmitter(this);
		}
	});
	this.setDependencies();
	this.sortContainers();
};

validationForm.prototype.sortContainers = function(){
	this.containers = this.containers.sort(function(c1,c2){
		return c1.sortOrder - c2.sortOrder;
	});
};

validationForm.prototype.setDependencies = function(){
	var allContainers = this.containers;
	$.grep($.grep(allContainers, function(o,i){
		return ((o.dependsOn != null) && (typeof(o.dependsOn[0])=='string'));
	}),function(o,i){
		var dps = o.dependsOn;
		var allElms = new Array();
		$.each(allContainers,function(o,i){
			$.each(this.validationElements,function(n){
				for(var i=0,aID;aID=dps[i];i++){
					if (this.formInputElement.id == aID){
						debugTrace('Found dependency <strong>'+aID+'</strong>');
						allElms.push(this);
					}
				}
			});
		});
		if (allElms.length == dps.length){
			o.dependsOn = allElms;
		}else{
			debugTrace('Not all dependencies were found for '+ o.name + ' ('+dps.join(', ')+')',true);
		}
	});
};

validationForm.prototype.validate = function(){
    if (!this.enabled){ return true; }
    var isValid = true;
	if (this.onBeforeValidation()){
		for(var i=0,aContainer;aContainer=this.containers[i];i++){
			var thisResult = this.validateContainer(aContainer);
			if (!thisResult){
				isValid = false;
				if  (!this.validateAll){
					break;
				}
			}
		}
	}else{
	    return false;
	}
	var aResult = this.onAfterValidation(isValid);
	return (this.debug) ? false : aResult;
};

validationForm.prototype.validateContainer = function(aContainer){
	var aElm = aContainer.validate();
	if (aElm != null){
		if (aElm.lastResult == $.fvjq_vrEmpty){
			aElm.onRequiredError(aElm);
		}else{
			aElm.onValidationError(aElm);
		}
		return false;
	}else{
		return true;
	}
};

validationForm.prototype.validateSection = function(aSectionName){
	if (!this.enabled){ return true; }
	var isValid = true;
	if (this.onBeforeSectionValidation(aSectionName)){
		debugTrace('Validating section: '+aSectionName);
		for (var i=0,aContainer;aContainer=this.containers[i];i++)
		{
		    if (aContainer.isInSection(aSectionName))
		    {
				var thisResult = this.validateContainer(aContainer);
			    if (!thisResult){
				    isValid = false;
				    if (!this.validateAll){
						break;
					}
			    }
			}
		}
	}else{
		return false;
	}
	var aResult = this.onAfterSectionValidation(isValid, aSectionName);
	return (this.debug) ? false : aResult;
};

function selectElement(aElm){
	if (aElm.select)
	{
		aElm.select();
	}
	else
	{
		aElm.focus();
	}
	if(aElm.scrollIntoView){
		aElm.scrollIntoView();
	}
};

function defaultValidationError(aValidationElement){
	alert(aValidationElement.errorMessage);
	selectElement(aValidationElement.formInputElement);
};

function defaultRequiredError(aValidationElement){
	alert(aValidationElement.requiredMessage);
	selectElement(aValidationElement.formInputElement);
};

function debugTrace(aMsg,isError)
{
	if (typeof(console) != 'undefined'){
        if (isError){
            console.error("FORM VALIDATOR ERROR:" + aMsg);
        }else{
            console.log(aMsg);
        }
	}else if (typeof(debugObj) != 'undefined')
	{
		debugObj.trace(aMsg,isError);
	}
};

function setVersionNumber(){
	if (typeof(debugObj) != 'undefined')
	{
		debugObj.setVersionNumber($.fvjq_FvVersion);
	}
};