/* Novusoft.Validators.js 
		requires: Novusoft, Novusoft.Settings, Novusoft.Utils 
*/

/**
 * @fileOverview The Novusoft.Validators namespace contains classes dealing with data validation.
 * @version 1.0
 */

Novusoft.registerNamespace("Novusoft.Validators");

/**
 * Novusoft.Validators.RequiredValidator validates whether a field is filled.
 * @class 
 */
Novusoft.Validators.RequiredValidator = Novusoft.Type({
    constructor: function(inp) {
        this.input = inp;
        this.valid = false;
        this.corrected = this.input;
        
        this._parseRequired();
    },
    
    _parseRequired: function() {
        if($.trim(this.input) != "") 
            this.valid = true;
    }
});

/**
 * Novusoft.Validators.EmailValidator validates whether a field is a valid email address.
 * @class 
 */
Novusoft.Validators.EmailValidator = Novusoft.Type({
    constructor: function(inp) {
        this.input = inp;
        this.valid = false;
        this.corrected = this.input;
        
        this._parseEmail();
    },
    
    _parseEmail: function() {
		this.valid = true;
		if ($.trim(this.input).length == 0) {
			return;
		}
		
		//this.valid = this.input.match(/^[A-Z0-9&._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,6}$/i);
		
		//from: http://www.mi-ange.net/blog/msg.php?id=79&lng=en
		this.valid = this.input.match(/^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.([a-z][a-z]+)|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i);
    }
});

/**
 * Novusoft.Validators.InitialsValidator validates formats a field such that 
 * @class 
 */
Novusoft.Validators.InitialsValidator = Novusoft.Type({
    constructor: function(inp) {
        this.input = inp;
        this.valid = false;
        this.corrected = "";
        
        this._parseInitials();
    },
    
    _parseInitials: function() {
		this.valid = true;
		if ($.trim(this.input).length == 0) {
			return;
		}

		var text = this.input.replace('\ ', '');

		if (text.match(/\./) == null) {
			for (var i=0; i < text.length; i++) { this.corrected += text.charAt(i).toUpperCase() + '.'; }
		} else {
			this.corrected += text.charAt(0).toUpperCase();
			for (var i=1; i < text.length; i++) {
				if (text.charAt(i-1) == '.') {
					this.corrected += text.charAt(i).toUpperCase();
				} else {
					this.corrected += text.charAt(i);
				}
			}
			if (text.charAt(text.length-1) != '.') { this.result += '.'; }
		}
	}
});

Novusoft.Validators.InitialsValidator.Validate = function(v) {
    var el = $('#' + v.controltovalidate);    
    var text = el.val();
    
    var inVal = new Novusoft.Validators.InitialsValidator(text + '');
    if(inVal.valid) {
        el.val(inVal.corrected);
    }
    return inVal.valid;
};



/*
var InitVal = new Novusoft.Validators.InitialsValidator("th.u.i");
console.log(InitVal.input);
console.log(InitVal.valid);
console.log(InitVal.corrected);
*/


Novusoft.Validators.BankNumberValidator = Novusoft.Type({
    constructor: function(inp, banktype) {
	    this.input = inp;
	    this.valid = false;
	    this.corrected = this.input;

	    this.check = Novusoft.Validators.BankNumberValidator.Checks[banktype] || Novusoft.Validators.BankNumberValidator.Checks.generic;
	    this._parseBankNumber();
    },
    
    _parseBankNumber: function() {
		if ($.trim(this.input).length == 0) {
			this.valid = true;
			return;
		}
		this.valid = this.check(this.input, this);			
	}
});

Novusoft.Validators.BankNumberValidator.Checks = {
	regular: function(inp, o) {
		var text = inp.replace(/[\. ]/g, '');
		if((text.length == 9 || text.length == 10) && text.match(/^\d+$/) != null) {
			var result=0, j=0;
			for(var i=text.length; i>0; i--) {
				result += text.charAt(j++)*i;
			}

			/* add dots if obj is passed along */
			if(o) {
				o.corrected = '';
				for(var i = 0; i < text.length; i++) {
					if (i==2||i==4||i==6) {
						o.corrected += '.';
					}
					o.corrected += text.charAt(i);
				}
			}
			/* -- */

			return result%11==0;
		}
		return false;
	},

	postbank: function(inp, o) {
		var text = inp.replace(/[\. ]/g, '');
		return text.match(/^[pP]?[0-9]{1,7}$/) != null;
	},
	
	generic: function(inp, o) {
	    if(inp.match(/[pP].*/)) { 
		   if (Novusoft.Validators.BankNumberValidator.Checks.postbank(inp, o)) return true;
		} else {
		    if (Novusoft.Validators.BankNumberValidator.Checks.regular(inp, o)) return true;
		}
		return false;
	}
};

/* As part of the C# Novusoft.Novucura.Web.UI.WebControls.Validators.RegularBankNumberValidator class 
    Not required when used outside Novucura.
*/
Novusoft.Validators.BankNumberValidator.Validate = function(v) {
    var el = $('#' + v.controltovalidate);    
    var text = el.val();
    
    var bankVal = new Novusoft.Validators.BankNumberValidator(text + '', v.banktype);
    
    if (bankVal.valid) {
        el.val(bankVal.corrected);
    }
    return bankVal.valid;
};


Novusoft.Validators.DateValidator = Novusoft.Type({
    constructor: function(inp) {
        this.input = inp;
        this.valid = false;
        this.corrected = this.input;
        this.written = "";

        this._parseDate();
    },

    getDatePos: function(dateComp) {
        var datePos = Novusoft.Settings.Locale.ShortDatePattern.split(Novusoft.Settings.Locale.DateSeparator);

        for (var i = 0; i < datePos.length; i++) {
            if (datePos[i].toLowerCase().substring(0, dateComp.length) == dateComp.toLowerCase()) {
                return i + 1;
            }
        }
        return -1;
    },

    _parseDate: function() {
        var text = $.trim(this.input);
        if (text.length == 0) {
            this.valid = true;
            return;
        }

        var dayPos = this.getDatePos("d");
        var monthPos = this.getDatePos("m");
        var yearPos = this.getDatePos("y");

        var dayRegex = "([0-3]?[0-9])";
        var monthRegex = "(0?[1-9]|1[0-2]|[aA-zZ]+)";
        var yearRegex = "([0-9]{4}|[0-9]{1,2})";
        var seperatorRegex = "[^0-9aA-zZ]*";

        var dateRegexArray = new Array();
        dateRegexArray[dayPos-1] = dayRegex;
        dateRegexArray[monthPos-1] = monthRegex;
        dateRegexArray[yearPos-1] = yearRegex;

        var regexp = new RegExp("^" + dateRegexArray.join(seperatorRegex) + "$");

        //var regexp = /^([0-3]?[0-9])[^0-9aA-zZ]*(0?[1-9]|1[0-2]|[aA-zZ]+)[^0-9aA-zZ]*([0-9]{4}|[0-9]{1,2})$/;
        var ar = regexp.exec(text);
        var months = Novusoft.Settings.Locale.months;

        if (ar != null) {
            var day = parseInt(ar[dayPos], 10);

            var month = 0;
            if (ar[monthPos].match(/^\d+$/)) {
                month = parseInt(ar[monthPos], 10) - 1;
            } else {
                month = Novusoft.Utils.findClosestString(ar[monthPos], Novusoft.Settings.Locale.months);
            }

            var year = parseInt(ar[yearPos], 10);
            if (year > 30 && year <= 99) {
                year = '19' + ar[yearPos];
            } else if (year <= 30) {
                if (year <= 9) {
                    year = '200' + year;
                } else {
                    year = '20' + year;
                }
            }

            var d = new Date(year, month, day);
            if (d.getMonth() != month || d.getDate() != day || d.getFullYear() != year) {
                this.valid = false;
            } else {
                this.valid = true;
                this.written = day + ' ' + months[month] + ' ' + year;
                this.corrected = d.format(Novusoft.Settings.Locale.ShortDatePattern);
            }
        }
    }

});

/* As part of the C# Novusoft.Novucura.Web.UI.WebControls.Validators.DateValidator class 
    Not required when used outside Novucura.
*/
Novusoft.Validators.DateValidator.Validate = function(v) {
    var el = $('#' + v.controltovalidate);    
    var text = el.val();
    
    
    var setWritten = function(va, t) {
        if (va.WrittenElement && $.trim(va.WrittenElement) != '') {
            var w = $('#' + va.WrittenElement);
            if (w) { w.text(t); }
        }    
    };    
    
    var dateVal = new Novusoft.Validators.DateValidator(text + '');
    
    if(dateVal.valid) {
        el.val(dateVal.corrected);
        setWritten(v,dateVal.written);
    } else {
        setWritten(v,'');
    }  
   
    return dateVal.valid;
};

/*
var DateVal = new Novusoft.Validators.DateValidator("10ja2009");
console.log(DateVal.input);
console.log(DateVal.valid);
console.log(DateVal.corrected);
console.log(DateVal.written);
*/

Novusoft.Validators.NetherlandsLicensePlateValidator = Novusoft.Type({
    constructor: function(inp) {
	    this.input = inp;
	    this.valid = false;
	    this.corrected = "";
	    this._parseLicensePlate();
	},

	_parseLicensePlate: function() {
		var text = $.trim(this.input);
		if (text.length == 0) {
			this.valid = true;
			return;
		}

		var regexp = /^(([0-9]{2})[\-]?([aA-zZ]{3})[\-]?([0-9])|([aA-zZ]{2}|[0-9]{2})[\-]?([aA-zZ]{2}|[0-9]{2})[\-]?([aA-zZ]{2}|[0-9]{2}))$/;
		var ar = regexp.exec(text);

		if (ar != null && text == ar[0]) {
			for(var i=2; i < ar.length; i++) {
				if (ar[i] != undefined && ar[i] != '') {
					this.corrected += ar[i].toUpperCase() + '-';
				}
			}
			this.corrected = this.corrected.substring(0, this.corrected.length-1);
			this.valid = true;
		}	
	}
});


Novusoft.Validators.NetherlandsLicensePlateValidator.Validate = function(v) {
    var el = $('#' + v.controltovalidate);    
    var text = el.val();
    
    var lpVal = new Novusoft.Validators.NetherlandsLicensePlateValidator(text + '');
    if(lpVal.valid) {
        el.val(lpVal.corrected);
    }
    return lpVal.valid;
};


/*
var LPV = new Novusoft.Validators.DutchLicensePlateValidator("01gbb1");
console.log(LPV.input);
console.log(LPV.valid);
console.log(LPV.corrected);
*/

Novusoft.Validators.NetherlandsZipCodeValidator = Novusoft.Type({
    constructor: function(inp) {
	    this.input = inp;
	    this.valid = false;
	    this.corrected = "";
	    this._parseZipCode();
    },
    
	_parseZipCode: function() {
		var text = $.trim(this.input);
		if (text.length == 0) {
			this.valid = true;
			return;
		}	

		var regexp = /^[0-9]{4}[\ ]*[aA-zZ]{2}$/;
		var ar = regexp.exec(text);

		if (ar != null && text == ar[0]) {
			this.corrected = text.replace(/\ +/, '').toUpperCase();
			this.valid = true;
			return;
		}
	}
});

Novusoft.Validators.NetherlandsZipCodeValidator.Validate = function(v) {
    var el = $('#' + v.controltovalidate);    
    var text = el.val();
    
    var zipVal = new Novusoft.Validators.NetherlandsZipCodeValidator(text + '');
    if(zipVal.valid) {
        el.val(zipVal.corrected);
    }
    return zipVal.valid;
};

/*
var ZCV = new Novusoft.Validators.NetherlandsZipCodeValidator("1016ab");
console.log(ZCV.input);
console.log(ZCV.valid);
console.log(ZCV.corrected);
*/

Novusoft.Validators.NumberValidator = Novusoft.Type({
    constructor: function(inp, scale) {
        this.input = inp;
        this.valid = false;
        this.corrected = "";

        this.scale = scale ? parseInt(scale) : null;
        this._parseNumber();
    },

    _parseNumber: function() {
        var text = $.trim(this.input);
        if (text.length == 0) {
            this.valid = true;
            return;
        }

        var splitted = this._splitNumber(text);
        this.corrected = this._dotNumber(splitted.integer);

        if (this.scale > 0)
            this.corrected += Novusoft.Settings.Locale.DecimalSeperator + this._setDecimal(splitted.fraction);        
        
        this.valid = true; //always valid?

    },

    _splitNumber: function(text) {
        text = text.replace(new RegExp('[^\\d\\' + Novusoft.Settings.Locale.DecimalSeperator + '-]', 'gi'), '');
        var num = text.split(Novusoft.Settings.Locale.DecimalSeperator);

        var res = {};
        if (num.length > 1) {
            res.fraction = num.pop();
            res.integer = num.join();
        } else {
            res.integer = num.pop();
        }

        return res;
    },

    _setDecimal: function(text) {
        text = text || "";

        if (this.scale) {
            if (text.length > this.scale) {
                text = text.substring(0, this.scale); //should round up
            }
            var l = text.length; //maybe a pad function?
            for (var i = 0; i < this.scale - l; i++) {
                text += '0';
            }
        }
        return text;
    },

    _dotNumber: function(text) {
        text = parseInt(text, 10) + '';
        var neg = text.charAt(0) == '-';
        text = text.replace(/[^\d]/g, '');
        for (var i = text.length - 1, s = ''; i >= 0; i--) {
            s = text.charAt(i) + s;
            if (Math.abs(i - text.length) % 3 == 0 && i != 0) {
                s = (Novusoft.Settings.Locale.NumberSeperator + s);
            }
        }
        return neg ? '-' + s : s;
    }
});



Novusoft.Validators.NumberValidator.Validate = function(v) {
    var el = $('#' + v.controltovalidate);    
    var text = el.val();
    
    var numberVal = new Novusoft.Validators.NumberValidator(text + '', v.scale);
    el.val(numberVal.corrected);
    return numberVal.valid;
}

/*
var NV = new Novusoft.Validators.NumberValidator("5.757,3646", 2);
console.log(NV.input);
console.log(NV.valid);
console.log(NV.corrected);
*/

/* Novusoft.Validators.CreditCardValidator
	requires: ccvalidate.js
*/

Novusoft.Validators.CreditCardValidator = function(inp) {
	this.input = inp;
	this.corrected = inp;
	this.valid = verifyCard(val.cardtype, text);
}


Novusoft.Validators.GenericReplace = function(v) {
    var el = $('#' + v.controltovalidate);    
    var text = el.val();
    	
    switch(v.action) {
        case 'lowercase':
            text = text.toLowerCase();
            break;
        case 'capitalize':
	        text = text.replace(/(^.| +.|-.)/g, function(x) { return x.toUpperCase() });
	        break;                        
        case 'uppercase':
            text = text.toUpperCase();
            break;
        default:
            text = text;
            break;
    }    
	el.val(text);
	return true;
}

Novusoft.Validators.Range = function(v) {   
    var el = $('#' + v.controltovalidate);    
    var text = el.val();
    if ($.trim(text).length == 0) {
        return true;
    }
    
    var n = parseFloat(text);    
    return (n <= parseFloat(v.minimumvalue) && n >= parseFloat(v.maximumvalue));
};


Novusoft.Validators.UrlPartValidator = Novusoft.Type({
    constructor: function(inp) {
	    this.input = inp;
	    this.valid = false;
	    this.corrected = "";

	    this._parseUrl();
    },
	
	_parseUrl: function() {
		this.valid = false;
		if($.trim(this.input).length == 0)
			return;
		
		this.corrected = $.trim(this.input).replace(/[\W]/g, this.urlReplace).replace(/_+/g, '_');
		
		//this.valid = !this.input.match(/[\w\_\])
	},
	
	urlReplace: function(v)
	{
		switch(v) {
			case '-':
				return '-';
			case '&':
				return '_';
			case ' ':
			case '_':
				return '_';
			default:
				return '';
		}
	}	
	
	
});


(function($) { 
	Novusoft.Validators.ValidatorEngine = Novusoft.Type({
	     constructor: function(bindings, funcs) {
			var funcs = funcs || {};
	         this.bindings = bindings; 
	
			this.c = Novusoft.Validators.ValidatorCollection; //needs to be settable
	
			var onPreValidation 	= funcs.onPreValidation 	|| Novusoft.Validators.ValidatorEngine.onPreValidation;
			var onValidation 		= funcs.onValidation 		|| Novusoft.Validators.ValidatorEngine.onValidation;
			var onPostValidation 	= funcs.onPostValidation 	|| Novusoft.Validators.ValidatorEngine.onPostValidation;
			var onValid 			= funcs.onValid 			|| Novusoft.Validators.ValidatorEngine.onValid;
			var onInvalid 			= funcs.onInvalid 			|| Novusoft.Validators.ValidatorEngine.onInvalid;			

	         for(var i in this.bindings.inputs) {
	             this.bindings.inputs[i] =  $.extend({
	                                             onPreValidation: onPreValidation,
	                                             onValidation: onValidation,
	                                             onPostValidation: onPostValidation,
												 onValid: onValid,
												 onInvalid: onInvalid	
	                                         }, this.bindings.inputs[i]);
	         }                              
	     },
	     
	     collection: function() {
	        return this.c;
	     },
	     
	     validate: function() {
	        var result = true;
	        for(var i in this.bindings.inputs) {
	            result = $(this.bindings.inputs[i].element).validate() && result;
	        }
	        return result;
	     },

	    _run: function(binding, funcName) {
	        var result = true;
			var el = $(binding.element);
			
	        for(var i in binding.validation) { 
				var r = binding[funcName](binding.validation[i], binding, this);
				
				if(funcName == "onValidation") {
					if (r) 
						el.data('validator_valid').push(binding.validation[i]);
					else
						el.data('validator_invalid').push(binding.validation[i]);
						
					result = r && result;
				}
	        }
	        return result;
	    },

	    /* does not stop running on false, unlike the binder */
	    _do: function(binding, a) {
	        var result = true;
	        for(var i = 1; i < arguments.length; ++i) { /* console.log("running %s for '%s'", arguments[i], binding.element); */
	            result = this._run(binding, arguments[i]) && result;
	        }
	        return result;       
	    },

	    install: function() {
			$.fn.extend({
				validate: function() {					        
				if(this.data('validator')) {
					return this.data('validator')();				
				}
				return true; //no validator == valid			
				}				
			});
			
	        
	        for(var i = 0; i < this.bindings.inputs.length; ++i) {
	            if(this.bindings.inputs[i].validation) { this._attachValidator(this.bindings.inputs[i]); }
	        }
	        
	        //var self = this;
	        //this.bindings.inputs.forEach(function(x) {
	        //    if(x.validation) { self._attachValidator(x); }
	        //});           
	    },

	    _attachValidator: function(binding) {
	        var el = $(binding.element);
	        if(el.length > 0) {
				el.addClass("validation");			
				el.data('validator', this._createValidator(binding));
	        }                      
	    },

	    _createValidator: function(binding) {
	        var self = this;
	        
	        return function() {
				var el = $(binding.element);
				
				el.data('validator_valid', []);
				el.data('validator_invalid', []);
						
	            var result =  self._do(binding, "onPreValidation", "onValidation", "onPostValidation");
	
				if(el.data('validator_invalid').length > 0)
					binding.onInvalid(el.data('validator_invalid'), binding, self);
	
				if(el.data('validator_valid').length > 0)
					binding.onValid(el.data('validator_valid'), binding, self);
							
	            return result;
	        }
	    }
	});
	
	Novusoft.Validators.ValidatorEngine.onPreValidation = function(validator, binding, engine) {
		var val = $(binding.element).novuvalue();		
		if(engine.c[validator] && engine.c[validator].preValidator) {
			return engine.c[validator].preValidator(val, $(binding.element));
		}		
		return true;
	};	
	
	Novusoft.Validators.ValidatorEngine.onValidation = function(validator, binding, engine) {
		var val = $(binding.element).novuvalue();		
		if(engine.c[validator] && engine.c[validator].validator) {
			return engine.c[validator].validator(val, $(binding.element));
		}		
		return true;
	};
	
	Novusoft.Validators.ValidatorEngine.onPostValidation = function(validator, binding, engine) {
		var val = $(binding.element).novuvalue();		
		if(engine.c[validator] && engine.c[validator].postValidator) {
			return engine.c[validator].postValidator(val, $(binding.element));
		}		
		return true;
	};	
	
	Novusoft.Validators.ValidatorEngine.onValid = function(validators, binding, engine) {
		
	};	
	
	Novusoft.Validators.ValidatorEngine.onInvalid = function(validators, binding, engine) {
		
	};	

})(jQuery);

Novusoft.Validators.ValidatorCollection = {
    required: {
        validator: function(value) {
            var val  = new Novusoft.Validators.RequiredValidator(value);
            //console.log('required', val);
            return val.valid;
        },
     	message: function() { return _("ValueRequired", "This field is required"); }
    },
    
    initials: {
        preValidator: function(value, el) { var v = new Novusoft.Validators.InitialsValidator(value); el.novuvalue(v.corrected); }    
    },        
    
    uppercase: {
        preValidator: function(value, el) { el.novuvalue(value.toUpperCase()); }
    },
    
    lowercase: {
        preValidator: function(value, el) { el.novuvalue(value.toLowerCase());  }
    },
    
	capitalize: {
		preValidator: function(value, el) { el.novuvalue(value.replace(/(^.| +.|-.)/g, function(x) { return x.toUpperCase() })); }
	},    
    
    nospaces: {
        preValidator: function(value, el) { el.novuvalue(value.replace(/ /g, '_') ); }
    },

	urlPart: {
		preValidator: function(value, el) { var v = new Novusoft.Validators.UrlPartValidator(value); el.novuvalue(v.corrected); }
	},	
	
	email: {
        validator: function(value) {
            var val  = new Novusoft.Validators.EmailValidator(value);
            //console.log('email', val);
            return val.valid;
        },
        message: function() { return _("ValueNoEmail", "This is not a valid email address"); }
	}
};