(function($) {
    
    /*
    Validation Singleton
    */
    var Validation = function() {

		messages={
				required: "{caption} is mandatory.",
				email: "Please enter valid email ID for {caption}",
				charonly: "Only characters are supported for {caption}",
				integer: "Please enter integer value for {caption}",
				floating: "Please enter numeric value for {caption}",
				username: "{caption} must start with a letter and can contain only alphanumeric characters (letters, _, ., digits). Length must be between 4 to 20 characters.",
				password: "{caption} should not contain whitespace characters. Length Must be between 4 to 20 characters.",
				user_regex: "Invalid value for {caption}.",
				lengthrange: "Length of {caption} must be between {minlength} and {maxlength}",
				range: "Value of {caption} must be between {minval} and {maxval}",
				comparewith_lt: "{caption} must be less than {comparefield}",
				comparewith_le: "{caption} must be less than or equal to {comparefield}",
				comparewith_gt: "{caption} must be greater than {comparefield}",
				comparewith_ge: "{caption} must be greater than or equal to {comparefield}",
				comparewith_eq: "{caption} must be same as {comparefield}",
				comparewith_ne: "{caption} should not be same as {comparefield}"
		};
        
        var rules = {
            
            email : {
               check: function(rval, value) {
                   
                   if(rval && value)
                       return testPattern(value,"^((?:(?:(?:[a-zA-Z0-9][\\.\\-\\+_]?)*)[a-zA-Z0-9])+)\\@((?:(?:(?:[a-zA-Z0-9][\\.\\-_]?){0,62})[a-zA-Z0-9])+)\\.([a-zA-Z0-9]{2,6})$");
                   return true;
               }
            },
            
            required : {
                
               check: function(rval, value) {
				  if(rval){	
	                   if(value){
	                       return true;
	                   }
	                   else{
	                       return false;
	                   }
				  }
				  else{
					  return true;
				  }
               }
            },

            charonly:{
                check: function(rval, value){
                	if(rval && value){
                    	return testPattern(value, '^[a-zA-Z\\s]*$');
                	}
                	return true;
            	}
            },

            integer:{
                check: function(rval, value){
                	if(rval && value){
                    	return testPattern(value, '^-?[\\d]+$');
                	}
                	return true;
            	}
            },

            floating:{
                check: function(rval, value){
                	if(rval && value){
                    	return testPattern(value, '^-?[\\d]*\\.?[\\d]+$');
                	}
                	return true;
            	}
            },

            username:{
                check: function(rval, value){
                	if(rval && value){
                    	return testPattern(value, '^[a-zA-Z][a-zA-Z_\\.0-9]{3,19}$');
                	}
                	return true;
            	}
            },

            password:{
                check: function(rval, value){
                	if(rval && value){
                    	return testPattern(value, '^.{4,20}$');
                	}
                	return true;
            	}
            },

            user_regex:{
                check: function(rval, value){
                	if(value){
                    	return testPattern(value, rval);
                	}
                	return true;
            	}
            },

            lengthrange:{
                check: function(rval, value){
                	if(value && $.isArray(rval) && rval.length==2){
                    	return (value.length>=rval[0] && value.length<=rval[1]);
                	}
                	return true;
            	}
            },

            range:{
                check: function(rval, value){
	            	/*if(value && $.isArray(rval) && rval.length==2){
		            	value=parseFloat(value);
	                	return (value>=rval[0] && value<=rval[1]);
	            	}*/
            		if(value){
            			var minval=rval.minval;
            			var maxval=rval.maxval;
            			if(rval.dateFormat){
            				minval=Date.parseDate(minval, rval.dateFormat).getTime();
            				maxval=Date.parseDate(maxval, rval.dateFormat).getTime();
            				value=Date.parseDate(value, rval.dateFormat).getTime();
            			}
            			if(rval.numeric){
            				minval=parseFloat(minval);
            				maxval=parseFloat(maxval);
            				value=parseFloat(value);
            			}
            			return(value >= minval && value <= maxval);
            		}
	            	return true;
            	}
            },

            comparewith:{
                check: function(rval, value, o){
            		
            		if(o.dateFormat){
            			rval=Date.parseDate(rval, o.dateFormat).getTime();
            			value=Date.parseDate(value, o.dateFormat).getTime();
            		}
            		if(o.numeric){
            			rval=parseFloat(rval);
            			value=parseFloat(value);
            		}
            		
            		switch (o.operator) {
					case 'lt':
						return(value<rval);
						break;
					case 'le':
						return(value<=rval);
						break;
					case 'gt':
						return(value>rval);
						break;
					case 'ge':
						return(value>=rval);
						break;
					case 'ne':
						return(value!=rval);
						break;
					default:
						return(value==rval);
						break;
					}
                }
            }
        }
        var testPattern = function(value, pattern) {

            var regExp = new RegExp(pattern,"");
            return regExp.test(value);
        }
        return {
            
            addRule : function(name, rule) {

                rules[name] = rule;
            },
            getRule : function(name) {
                return rules[name];
            },

            getMessage: function(name){
                return messages[name];
            },

            setMessages: function(obj){
                jQuery.extend(messages, obj);
            }
        }
    }
    
    /* 
    Form factory 
    */
    var Form = function(form, requirements, options) {
    	options=jQuery.extend({"errordisplay":"summary", "summaryElementId":"validation_default"}, options);
    	this.settings=options;
        var fields = [];
        form.find("input, textarea, select").each(function() {
            if($(this).attr('name')){
                var fldname=$(this).attr('name');
                if(fldname.substring(fldname.length-2)=='[]') fldname=fldname.substring(0, fldname.length-2);
                if(eval('requirements.'+fldname)){
                    var fldRequirement=(eval('requirements.'+fldname));
                    fields.push(new Field(this, fldRequirement, options));
                }
            }
        });
        this.fields = fields;
    }
    Form.prototype = {
        validate : function() {
            for(field in this.fields) {
                this.fields[field].validate();
                if(this.settings.errordisplay=="alertbox"){
                	if(!this.fields[field].valid) {
                        this.fields[field].field.focus();
                        break;
                    }
                }
            }
        },
        isValid : function() {
        	if(this.settings.errordisplay=='summary' && this.settings.summaryElementId=='validation_default') this.settings.summaryElementId=$(field).parents('form').attr('id');
            for(field in this.fields) {
                
                if(!this.fields[field].valid) {
                	if(this.settings.errordisplay!='summary') this.fields[field].field.focus();
                    return false;
                }
            }
            return true;
        }
    }
    
    /* 
    Field factory 
    */
    var Field = function(field, fldRequirement, options) {
    	this.settings=options;
        this.field = $(field);
        this.valid = false;
        if(this.settings.errordisplay!='alertbox') this.attach("change");
        this.fldRequirement=fldRequirement;
        if(this.settings.errordisplay=='summary' && this.settings.summaryElementId=='validation_default') this.settings.summaryElementId=$(field).parents('form').attr('id');
    }
    Field.prototype = {
        
        attach : function(event) {
        
            var obj = this;
            if(event == "change") {
                obj.field.bind("change",function() {
                    return obj.validate();
                });
            }
            if(event == "keyup") {
                obj.field.bind("keyup",function(e) {
                    return obj.validate();
                });
            }
        },
        validate : function() {
        	var clname='erlist_' + this.field.attr('name');
        	if(clname.substring(clname.length-2)=='[]') clname=clname.substring(0, clname.length-2);
        	$('.'+clname).remove();
            var obj = this,
                field = obj.field,
                errorClass = "errorlist",
                errorlist = $(document.createElement("ul")).addClass(errorClass).addClass(clname),
                types=this.fldRequirement,
                /*container = field.parent(),*/
                errors = []; 
            
            jQuery.each(types, function(rname, rval){
            	if(rname!='customMessage'){
            		var rule=$.Validation.getRule(rname);
            		if(rname=='comparewith'){
            			/*operator=rval.operator;
            			rval=$(field).parents('form').find('[name='+rval.fldname+']').val();*/
            			var validvalue=rule.check($(field).parents('form').find('[name='+rval.fldname+']').val(), field.val(), rval);
            		}
            		else{
            			var validvalue=rule.check(rval, field.val());
            		}
            		
            		
            		if(!validvalue){
            			field.addClass("error");
            			//var msg=(types.customMessage)?types.customMessage:$.Validation.getMessage(rname);
            			if(types.customMessage){
            				msg=types.customMessage;
            			}
            			else{
            				if(rname=='comparewith'){
            					msg=$.Validation.getMessage(rname+'_'+rval.operator);
            					msg=msg.replace("{comparefield}", $(field).parents('form').find('[name='+rval.fldname+']').attr('title'));
            				}
            				else{
            					msg=$.Validation.getMessage(rname);
            				}
            			}
            			msg=msg.replace("{caption}",$(field).attr('title'));
            			if(jQuery.isArray(rval) && rval.length==2){
            				msg=msg.replace("{minval}", rval[0]);
            				msg=msg.replace("{minlength}", rval[0]);
            				msg=msg.replace("{maxval}", rval[1]);
            				msg=msg.replace("{maxlength}", rval[1]);
            			}
            			if(rname=='range'){
            				msg=msg.replace("{minval}", rval.minval);
            				msg=msg.replace("{maxval}", rval.maxval);
            			}
            			
            			errors.push(msg);
            		}
            }});
            
            if(errors.length) {
            	if(this.settings.errordisplay!='alertbox' && this.settings.errordisplay!='summary'){
            		obj.field.unbind("keyup");
            		obj.attach("keyup");
            	}
                switch(this.settings.errordisplay){
                case 'summary':
                	$('#'+this.settings.summaryElementId).append(errorlist.empty()).scrollTo();
                	break;
                case 'beforefield':
                	field.before(errorlist.empty());
                	break;
                case 'afterfield':
                	$(field).parent().append(errorlist.empty());
                	break;
                case 'alertbox':
                	
                	break;
                }
                for(error in errors) {
                	if(this.settings.errordisplay=='alertbox'){
                		alert(errors[error]);
                	}
                	var li=$(document.createElement('li')).append($(document.createElement('a')).html(errors[error]).attr({'href':'javascript:void(0);'}).bind('click', function(){$(field).focus();}));
                    li.appendTo(errorlist);
                }
                obj.valid = false;
            } 
            else {
                errorlist.remove();
                field.removeClass("error");
                obj.valid = true;
            }
        }
    }
    
    /* 
    Validation extends jQuery prototype
    */
    $.extend($.fn, {
        
        validation : function(requirements, options) {
            
            var validator = new Form($(this), requirements, options);
            $.data($(this)[0], 'validator', validator);
            
            $(this).bind("submit", function(e) {
                validator.validate();
                if(!validator.isValid()) {
                    e.preventDefault();
                }
            });
            return validator;
        },
        validate : function() {
            var validator = $.data($(this)[0], 'validator');
            validator.validate();
            return validator.isValid();
        }
    });
    $.Validation = new Validation();
})(jQuery);

function updateFormRequirements(el, o){
	value=el.val();
	//if(eval("'"+value+"'" + o.operator + "'"+o.val+"'")==false) return;
	switch(o.operator){
	case 'eq':
		if(!(value==o.val)) return;
		break;
	case 'ne':
		if(!(value!=o.val)) return;
		break;
	case 'lt':
		if(!(value<o.val)) return;
		break;
	case 'le':
		if(!(value<=o.val)) return;
		break;
	case 'gt':
		if(!(value>o.val)) return;
		break;
	case 'ge':
		if(!(value>=o.val)) return;
		break;
	}
	eval(o.validator_requirements+'.'+o.fldname+'='+o.requirement);
	eval(o.validator_object + '=$("#' + o.form_id + '").validation(' + o.validator_requirements + ', ' + o.validator_formatting  + ');');
}
