/*
Form validation, requires prototype
tested on Firefox 2.0.0.14 and IE 6.0.2900.2180
USAGE:
var formVal = new FormVal( $('person') ); //create object and initializes to a form
formVal.setSaveButton( $('saveBtn') ); //sets the element that performs save
formVal.submitFormOnEnter(); //called if you want to observe enter button

SUBSET USAGE:
var formVal2 = new FormVal( $('person'),'firstName,lastName' );
formVal2.setSaveButton( $('saveBtnSubset') );


attach classes to fields that need to be validated
classes are:
validate:notempty
validate:nonnumeric
validate:numeric
validate:notzero
validate:email
validate:checked
validate:maxlengthXXX where XXX is a int
validate:minlengthXXX where XXX is a int
validate:alphanumeric
validate:date

using preValidate to call a function BEFORE actual validation takes place
userd for WYSIWYG validation
USAGE:
var formVal = new FormVal( $('person') );

var customPreValidation = function(){
	//get content FORM WYSIWYG editor
	var content = FCKeditorAPI.GetInstance('description').GetXHTML();
	//populate actual field with content
	$('description').value = content;
	//proceed with normal validation
}

formVal.preValidate = customPreValidation;


NOTE:
if using "setSaveButton"
the default saving mechanism is encapsulated with validation checking before the original code is executed
IE has a issue when evaluating the original code, especially when 'this' is used. E.g. "this.form"
workaround is to code ur references to form via document.getElemetById OR prototypes $('formId')

if using "submitFormOnEnter" to listen to enter keypress
multiline fields will throw the event
*/
FormVal=function(form,fieldids)
{
	this.form;//current form being validated
	this.inputFields = new Array();//array of input fields
	this.errorMessage;
	this.saveButtonElement;
	this.preValidate;
	
	this.cnt = 0;
	this.maxcnt = 10;
	
	if(typeof(form)!='undefined')
	{
		this.form = $(form);
		this.inputFields = this.form.getElements();
		this.inputFields = this.removeDuplicateFields(this.inputFields);
		this.setReturnFalseOnFormSubmit(this.form);
	}
	
	if(typeof(fieldids)!='undefined')
	{
		//alert(fieldids)
		this.inputFields = new Array();
		var fieldidsArr = fieldids.split(',');
		
		for(var i=0;i<fieldidsArr.length;i++)
		{
			var field = $(fieldidsArr[i]);
			if( field )
			{
				this.inputFields[this.inputFields.length] = field; 
			}
		}
	}
}

//remove duplicates from array of fields
FormVal.prototype.removeDuplicateFields=function(inputFields)
{
	var results = new Array();
	
	for(var i=0;i<inputFields.length;i++)
	{
		for(var o=0;o<results.length;o++)
		{
			var found = false;
			
			if(results[o].name==inputFields[i].name)
			{
				found=true;
			}
		}
		if(!found){
			results[results.length] = inputFields[i]; 
		}
	}
	return results;
}

FormVal.prototype.setReturnFalseOnFormSubmit=function(frm)
{
	var eleOnSubmitFunc = this.getFunctionAsString(frm,'onsubmit');
	
	testFunc=function(e)
	{
		Event.stop(e);
		return false;
	}

	if(eleOnSubmitFunc==null)
	{
		//then its safe to attach on submit event
		Event.observe(frm,'submit', testFunc);
	}
}

//inits the class, sets any required variables with in this scope
FormVal.prototype.init=function()
{
	
}

//validates, returns false if validation fails 
FormVal.prototype.validate=function()
{
	if(this.preValidate)
	{
		this.preValidate();
	}
	var result = false;	
	this.errorMessage = '';//resets error messages on each validate
	this.processInputFields();
	if(this.errorMessage.trim()=="")//no errors? then all good;
	{
		result = true;
	}
	return result;
}

//processes input fields,
//checks if its can be validated, get the commands (class names - validate), or we can just ignore because it won't be in our switch case
//get the Actual label[name], defaults to fieldName 
//gets their classes and does checking according to classnames
FormVal.prototype.processInputFields=function()
{
	var result = false;//no need?
	
	//do general validation that does not involve specific fields
	this.doValidateGeneral();
	
	//tests that apply to all inputs
	for(var i=0;i<this.inputFields.length;i++)
	{
		var inputField = this.inputFields[i];
		//array of classnames i.e. commands split by whitespace
		var commands = $w(inputField.className);
		
		
		var validCommands = new Array();
		//clean up non-commands
		for(var a = 0;a<commands.length;a++)
		{
			if( commands[a].indexOf('validate:') > -1 && commands[a].trim()!='' )//found
			{
				validCommands[validCommands.length] = commands[a];
			}
		}
		commands = validCommands;
		
		if(commands.length>0)
		{
			var label;
			if(inputField.title && inputField.title!=""){
				label = inputField.title;
			}
			else{
				label = inputField.name;
			}
			 
			//label = this.getLabelFromClassNames(inputField);
			this.doValidate(inputField,commands,label);
		}
	}
	return result;
}

//given a string of class names, we extract out special 'name' class
//or any other method of getting the name
FormVal.prototype.getLabelFromClassNames=function(inputObj)
{
	//TODO:
	if(inputObj.title!='')
		return inputObj.title;
		
	var objectName = inputObj.name;
	var t = objectName.lastIndexOf(".")
	if(t>0)
	{
		return objectName.substring(t+1).toUpperCase();
	}
	else
	{
		return objectName.toUpperCase();
	}
	
}

//element: htmlinputelement
//commands: array
//label: string
FormVal.prototype.doValidate=function(element,commands,label)
{
	for(var i=0;i<commands.length;i++)
	{
		var command = commands[i].trim(); 
		if(command == "validate:notempty")
		{
			if( !this.validateNotEmpty(element.value) )//if test fails
			{
				this.errorMessage += label+" is required."+"\n";
			}
			//regardless if pass or fail we have handled it so we remove it form the list to be processed
		}
		
		if( command == "validate:nonnumeric")
		{
			if( !this.validateNonNumeric(element.value) )
			{
				this.errorMessage += label+" contains numeric values."+"\n";
			}
		}
		
		if( command == "validate:email" )
		{
			if( !this.validateEmail(element.value) )
			{
				this.errorMessage += label+" is a invalid email address."+"\n";
			}
		}
		
		if( command == "validate:checked" )
		{
			if( !this.validateChecked(element) )
			{
				this.errorMessage += label+" needs to have at least 1 checked item."+"\n";
			}
		}
		
		if( command.indexOf("validate:maxlength")>-1  )
		{
			var length = command.getNumeric();
			if( !this.validateMaxLength(element.value,length) )
			{
				this.errorMessage += label+" cannot be less than "+length+" characters."+"\n";
			}
		}
		
		if( command.indexOf("validate:minlength")>-1 )
		{
			var length = command.getNumeric();
			if( !this.validateMinLength(element.value,length) )
			{
				this.errorMessage += label+" must be more than "+length+" characters."+"\n";
			}
		}
		
		if( command == "validate:alphanumeric" )
		{
			if( !this.validateAlphaNumeric(element.value) )
			{
				this.errorMessage += label+' must be alphanumeric, alphabets OR numeric.'
			}
		}
		
		if( command == "validate:date" )
		{
			if( !this.validateDate(element.value) )
			{
				this.errorMessage += 'Please enter valid date as dd/mm/yyyy.\nThe date must be a real date. 30/2/2000 would not be accepted.\nFormay dd/mm/yyyy.'
			}
		}
		
		if( command == "validate:numeric" )
		{
			if( !this.validateNumeric(element.value) )
			{
				this.errorMessage += label+" contains non-numeric values."+"\n";
			}
		}
		
		if( command == "validate:notzero" )
		{
			if( !this.validateNotZero(element.value) )
			{
				this.errorMessage += label+" is required."+"\n";
			}
		}
	}
}

//outside loop for special cases
FormVal.prototype.doValidateGeneral=function()
{
	if( !this.isPasswordValid(this.inputFields) )//if test fails
	{
		this.errorMessage += "Passwords do not match."+"\n";
	}
}

//gets the function of a element on click as a string
//event is optional
FormVal.prototype.getFunctionAsString=function(element,event)
{
	var result = null;
	
	if( element.getAttribute(event)!=null )
	{
		result = element.getAttribute(event).toString().trim();
		result=result.replace(/javascript:/,'');
		//NOTE: firefox returns a string , ie returns a function ie6
		//so we have to do some additional processing for ie
		if(result.indexOf('function anonymous')!=-1)//is IE
		{
			result=result.replace('function anonymous()','');
			result=result.replace('{','');
			result=result.substring(0,result.length-1);
			result=result.trim();
		}
	}
	return result;
}

//attaches validation event to the given save button
//WITHOUT interfering with its own default saving mechanism
FormVal.prototype.setSaveButton=function(saveButtonElement)
{
	//set reference
	this.saveButtonElement = saveButtonElement;
	var eleOnClickFunc = this.getFunctionAsString(saveButtonElement,'onclick');
	//remove onclick event from element, i recall a different method for ie
	saveButtonElement.setAttribute('onclick','');
	
	//jons hack
	var obj = new Object();
	obj['this'] = this;
	
	//anonymous function, do not use 'new' because it will execute
	var newEleOnClickFunc = function(e){
		if(obj['this'].validate())//if ok then save
		{
			try
			{
				//alert(eleOnClickFunc);
				eval( eleOnClickFunc );
			}
			catch(e)
			{
				//alert('Failed to evaluate the function\n'+eleOnClickFunc+"\nThis may be because there are references to [this] within your function and the browser is IE. " + e);
			}
		}
		else
		{
			alert( obj['this'].errorMessage );
		}
	}
	//attach our own validation
	Event.observe( saveButtonElement, 'click' , newEleOnClickFunc );
}

//starts observing the document for enter
FormVal.prototype.submitFormOnEnter=function()
{
	var obj = new Object();
	obj['this']=this;
	//Event.observe(document,'keypress',function(e){
	Event.observe(this.form,'keypress',function(e){
		if(e.keyCode=="13")
		{
			//trigger button save
			if(obj['this'].saveButtonElement)//if there is a defined save button
			{
				//simulate mouse click on button
				//obj['this'].simulateMouseClickOnElement( obj['this'].saveButtonElement );
				//call click method, better supported in ie
				obj['this'].saveButtonElement.click();
				//var eleOnClickFunc = obj['this'].getFunctionAsString(obj['this'].saveButtonElement,'onclick');
				//alert(eleOnClickFunc)
				//eval(obj['this'].saveButtonElement.click)
				//alert(obj['this'].saveButtonElement.getAttribute('onclick'))
				
			}
		}
	});
}

//works in FF only
FormVal.prototype.simulateMouseClickOnElement=function(element)
{
	var evt = document.createEvent('MouseEvents');
	if(element && element.dispatchEvent && evt && evt.initMouseEvent) {
         evt.initMouseEvent(
           'click',
           false,     // Click events bubble
           true,     // and they can be cancelled
           document.defaultView,  // Use the default view
           1,        // Just a single click
           0,        // Don't bother with co-ordinates
           0,
           0,
           0,
           false,    // Don't apply any key modifiers
           false,
           false,
           false,
           0,        // 0 - left, 1 - middle, 2 - right
           null);    // Click events don't have any targets other than
                     // the recipient of the click
         element.dispatchEvent(evt);
       }
}

//TESTS TO PERFORM ARE HERE
FormVal.prototype.validateEmail=function(value)
{
	var result = false;
	if(value=='')
	{
		return true;// dont check for empty
	}
	if(value.search(/^[a-zA-Z0-9üöäßÄÖÜ\-_]+([\.-]?[a-zA-Z0-9üöäßÄÖÜ\-_]+)*@[a-zA-Z0-9üöäßÄÖÜ\-_]+([\.-]?[a-zA-ZüöäßÄÖÜ]+)*(\.\w{2,5})+$/) != -1)
	{
		return true;
	}
	return result;	
}

FormVal.prototype.validateNonNumeric=function(value)
{
	var result = false;
	if( value.search(/\d/) == -1 )
	{
		result = true;//test passed
	}
	return result;
}

FormVal.prototype.validateNumeric=function(value)
{
	var result = false;
	if( value.search(/[a-zA-Z-_]/) == -1 )
	{
		result = true;//test passed
	}
	return result;
}

FormVal.prototype.validateNotZero=function(value)
{
	var result = false;
	if(value.getNumeric() != "0")
	{
		result = true;
	}
	
	return result;
}

FormVal.prototype.validateAlphaNumeric=function(value)
{
	var result = false;
	if( value.search(/[^a-zA-Z0-9]/) == -1 )//did not find any non-alphanumeric
	{
		result = true;//test passed
	}
	return result;
}

FormVal.prototype.validateDate=function(value)
{
	var result = false;
	
	var RegExPattern = /^((((0?[1-9]|[12]\d|3[01])[\.\-\/](0?[13578]|1[02])[\.\-\/]((1[6-9]|[2-9]\d)?\d{2}))|((0?[1-9]|[12]\d|30)[\.\-\/](0?[13456789]|1[012])[\.\-\/]((1[6-9]|[2-9]\d)?\d{2}))|((0?[1-9]|1\d|2[0-8])[\.\-\/]0?2[\.\-\/]((1[6-9]|[2-9]\d)?\d{2}))|(29[\.\-\/]0?2[\.\-\/]((1[6-9]|[2-9]\d)?(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)|00)))|(((0[1-9]|[12]\d|3[01])(0[13578]|1[02])((1[6-9]|[2-9]\d)?\d{2}))|((0[1-9]|[12]\d|30)(0[13456789]|1[012])((1[6-9]|[2-9]\d)?\d{2}))|((0[1-9]|1\d|2[0-8])02((1[6-9]|[2-9]\d)?\d{2}))|(2902((1[6-9]|[2-9]\d)?(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)|00))))$/;
    if ((value.match(RegExPattern)) && (value!='')) 
    {
        result = true;//Date is OK
    }
	return result;
}

FormVal.prototype.validateNotEmpty=function(value)
{
	var result = false;
	if(value!='')
	{
		result = true; 
	}
	return result;
}

//used for radio and checkboxes
FormVal.prototype.validateChecked=function(ele)//if checked return true
{
	var result = false;
	var checkboxes = document.getElementsByName(ele.name);
	for(var i=0;i<checkboxes.length;i++)
	{
		var checkbox = checkboxes[i];
		if( checkbox.checked )
		{
			result = true;
			break;
		}
	}
	return result;
}

FormVal.prototype.validateMaxLength=function(value,length)
{
	var result = false;
	if(value.length<=length)
	{
		result = true;
	}
	return result;
}

FormVal.prototype.validateMinLength=function(value,length)
{
	var result = false;
	if(value.length>=length)
	{
		result = true;
	}
	return result;
}

//password validation logic here, if there is 1 password field
//else if there are >2 password fields
//: check if both match
FormVal.prototype.isPasswordValid=function(inputFields)
{
	// default: passwords are not valid
	var result = false;
	var passwordFields = new Array();
	//get password fields
	for(var i=0;i<inputFields.length;i++)
	{
		var inputField = inputFields[i];
		
		if(inputField.type && inputField.type=='password')
		{
			passwordFields[passwordFields.length] = inputField ; 
		}
	}
	
	if( passwordFields.length>=2 )//if there are 2 or more password fields they ALL must match
	{
		if( passwordFields[0].value==passwordFields[1].value && passwordFields[0].value!='')
		{
			// passwords are valid
			result = true;//test passes
		}
	}
	else
	{
		// there are no passwords fields in this form. form is valid
		result = true;//test passes
	}
	
	return result;
}

//Overloads to basic element
String.prototype.trim=function(){
	return this.replace(/^\s+|\s+$/, '');
}

String.prototype.getNumeric=function(){
	return this.replace(/[^0-9]*/, '');
}

String.prototype.reverse = function(){
	return this.split("").reverse().join("");
}

Array.prototype.remove=function(s){
  for(i=0;i<this .length;i++){
    if(s==this[i]) this.splice(i, 1);
  }
}