jsbeans

jsbeans  1.0.0

jsbeans > jsbeans > Validator.js (source view)
Search:
 
Filters
/*!
 * Copyright (c) 2009 Francesco Mele jsbeans@francescomele.com
 *
 * This Software is licenced under the LGPL Licence (GNU Lesser General 
 * Public License).
 * In addition to the LGPL Licence the Software is subject to the 
 * following conditions:
 * 
 * 	i	every modification must be public and comunicated to the Author
 * 	ii	every "jsbean" added to this library must be self consistent 
 * 		except for the dependence from jsbeans-x.x.x.js
 * 	iii	copyright notice and this permission notice shall be included 
 * 		in all copies or substantial portions of the Software
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * A form's validator
 * @namespace jsbeans
 * @class Validator
 * @static
 * */
jsbeans.Validator = {
	/**
	 * Default messages for default asserts.
	 * @property messages
	 * @type JSON
	 * @static
	 * */
	messages: {
		prefix: "Errors occurred in the following fields:\n--------------------------\n",
		required: "is required",
		requiredByName: "at least one is required",
		integer: "must be an integer",
		email: "is not a valid email address",
		date: "is not a valid date",
		maxValue: "exceedes the maximum value allowed for the field",
		minValue: "is less than the minimum value allowed for the field",
		maxLen: "is too long",
		minLen: "is too short",
		numeric: "must be a number",
		invalid: "is not valid"
	},
	/**
	 * Default options are:
	 * <pre>
	 * errorClass: null, // the style class to apply in case of invalid value
	 * // default function invoked in case of at least one invalid value.
	 * // It get an &lt;Array&lt;JSON>> and this options as arguments
	 * callback: function(errors, options) {
	 * 		jsbeans.Validator.manageErrors(errors, options);
	 * },
	 * propagate: true, // if false validation will stop at first error
	 * focusOnFirst: true, // if true focus will be applied in first invalid input
	 * extraFunctions: null, // a list of functions for extra validation, useful for custom validation
	 * locale: "en" // default locale
	 * </pre>
	 * @property options
	 * @type JSON
	 * @static
	 * */
	options: {
		errorClass: null,
		callback: function(errors, options) {
			jsbeans.Validator.manageErrors(errors, options);
		},
		propagate: true,
		focusOnFirst: true,
		extraFunctions: null,
		locale: "en"
	},
	/**
	 * Loads a locale and sets it for error messages.
	 * @method setLocale
	 * @param locale {String} a string representing a locale. It loads <code>&lt;jsbeans_script_location>/Validator_&lt;locale>.js</code>.
	 * @static
	 * */
	setLocale: function(locale) {
		// there's no need to load the default
		if (locale != "en") {
			jsbeans.load("Validator_" + locale);
			jsbeans.Validator.options.locale = locale;
		}
	},
	/**
	 * @property _errorClassCache
	 * @type any
	 * @private
	 * @static
	 * */
	_errorClassCache: null,
	/**
	 * Use this method instead of overriding <code class="param">jsbeans.Validator.options</code> one by one or passing options on each <code class="methd">validate</code> invocation.
	 * @method init
	 * @param options {JSON} same as <code class="param">options</code>
	 * @static
	 * */
	init: function(options) {
		this.options.errorClass = options.errorClass || this.options.errorClass;
		this.options.callback = options.callback || this.options.callback;
		this.options.propagate = typeof(options.propagate) != "undefined" ? options.propagate : this.options.propagate;
		this.options.focusOnFirst = typeof(options.focusOnFirst) != "undefined" ? options.focusOnFirst : this.options.focusOnFirst;
		this.options.locale = options.locale || this.options.locale;
	},
	/**
	 * An Array of errors. Each error is a JSON in the form.
	 * <pre>
	 * object: &lt;DOM>, // the invalid input DOM Object
	 * label: &lt;String>, // the label for <code class="param">object</code> as returned by <code class="methd">getLabelFor</code>
	 * type: &lt;String>, // a string representing the name of the validation ('required', 'maxLen', ...)
	 * message: &lt;String> // the string containing the error message
	 * </pre>
	 * @property errors
	 * @type Array&lt;JSON>
	 * @static
	 * */
	errors: new Array(),
	/**
	 * Main method.<br/>
	 * Sample:
	 * <pre>
	 * &lt;form action="an_action" onsubmit="<strong>return validate();</strong>">
	 * 		&lt;label for="firstname">First Name&lt;label>: &lt;input type="text" id="firstname" />&lt;br/>
	 * 		&lt;label for="email">Email address&lt;label>: &lt;input type="text" id="email" />&lt;br/>
	 * 		&lt;label for="age">First Name&lt;label>: &lt;input type="text" id="age" />
	 * &lt;/form>
	 * &lt;script type="text/javascript">
	 * function <strong>validate()</strong> {
	 * 		return jsbeans.Validator.validate({
	 * 			firstname: ["required"],
	 * 			email: ["required", "email"],
	 * 			age: ["minValue=18"]
	 * 		});
	 * }
	 * &lt;/script>
	 * </pre>
	 * @method validate
	 * @param inputs {JSON} a JSON with the id of the input to validate as key and an Array of validation's asserts as value.
	 * @param [options] {JSON} override of default <code class="param">jsbeans.Validator.options</code>
	 * @return {boolean} true if there are no errors 
	 * @static
	 * */
	validate: function(inputs /*, options*/) {
		var options = arguments[1] || {};
		this.options.errorClass = options.errorClass || this.options.errorClass;
		this.options.callback = options.callback || this.options.callback;
		//
		this.options.propagate = typeof(options.propagate) != "undefined" ? options.propagate : this.options.propagate;
		var oPragt = this.options.propagate;
		this.options.focusOnFirst = typeof(options.focusOnFirst) != "undefined" ? options.focusOnFirst : this.options.focusOnFirst;
		//
		this.options.extraFunctions = typeof(options.extraFunctions) != "undefined" ? options.extraFunctions : null;
		var oExtras = this.options.extraFunctions;
		this.options.locale = options.locale || this.options.locale;
		
		this.errors = new Array();
		var obj, label, asserts;
		outer: for (var input in inputs) {
			obj = document.getElementById("" + input);
			label = this.getLabelFor("" + input);
			asserts = inputs[input];
			for (var i = 0, assert; assert = asserts[i]; i++) {
				var arg = null;
				if (assert.indexOf("=") != -1) {
					arg = assert.substring(assert.indexOf("=") + 1);
					assert = assert.substring(0, assert.indexOf("="));
				}
				// register the current number of errors
				var before = this.errors.length;
				if (!this._invoke(obj, assert, arg)) {
					// if current number of errors is different, the actual assert has registered an error, otherwise
					// this is the default
					if (before == this.errors.length) {
						this.errors.push({object: obj, label: label, type: assert, message: this._getMessage(assert)});
					}
					if (oPragt == false) {
						break outer;
					}
				} 
				else {
					this._clearErrorClass(obj);
				}
			}
		}
		if (oExtras != null) {
			var f;
			var isFunction = function(fn) {
				return !!fn && typeof fn != "string" && !fn.nodeName && fn.constructor != Array && /function/i.test( fn + "" );
			};
			var extrasLen = oExtras.length;
			for (var i = 0; i < extrasLen; i++) {
				f = oExtras[i];
				if (isFunction(f)) {
					try {
						f.apply();
					} 
					catch(e) {
						alert(e.message);
					}
				} 
				else {
					try {
						eval("(" + f + ")");
					} 
					catch(e) {
						alert(e.message);
					}
				}
			}
		}
		if (this.errors.length > 0) {
			options = this.options;
			eval("" + options.callback(this.errors, options));
			return false;
		}
		return true;
	},
	/**
	 * For mandatory fields.
	 * @method assertRequired
	 * @param input {DOM} the input to validate
	 * @return {boolean} false if input is empty (trimmed) and checkbox or radio are not checked
	 * @static
	 * */
	assertRequired: function(obj) {
		if (obj.type.toLowerCase() == "checkbox" || obj.type.toLowerCase() == "radio") {
			return obj.checked;
		}
		return ! (obj.value == null || ((obj.value || "").replace(/^\s+|\s+$/g, "")) == "");
	},
	/**
	 * For mandatory fields with same 'name' attribute of the given one. Each field is checked using {@method} <code class="methd">assertRequired</code> method.<br>
	 * Useful for input of type radio and checkbox.
	 * @method assertRequiredByName
	 * @param input {DOM} one of the input whose name is used to retrieve all the inputs to be validated
	 * @return {boolean} true if there is at least one valid field
	 * @static
	 * */
	assertRequiredByName: function(obj, arg) {
		var name = obj.getAttribute('name');
		if (typeof name != 'undefined' && name != null && name != '') {
			var inputs = document.getElementsByName(name);
			var areValid = inputs.length;
			for (var i = 0; i < inputs.length; i++) {
				if (this.assertRequired(inputs[i]) == false) {
					areValid--;
				}
			}
			if (areValid == 0) {
				var labelFor = arg || obj.id;
				this.errors.push({object: obj, label: this.getLabelFor(labelFor), type: "requiredByName", message: this._getMessage("requiredByName")});
			}
			return areValid != 0;
		}
		// no inputs found as no 'name' attribute exists
		return true;
	},
	/**
	 * Validation of email address
	 * @method assertEmail
	 * @param input {DOM} the input to validate
	 * @return {boolean} true for valid email address
	 * @static
	 * */
	assertEmail: function(obj) {
		var re = /^[^\s()<>@,;:\/]+@\w[\w\.-]+\.[a-z]{2,}$/i;
		return obj.value == "" || re.test(obj.value);
	},
	/**
	 * Validation for Date with optional check for format, min date (after) and max date (before). 
	 * @method assertDate
	 * @param input {DOM} the input to validate
	 * @param options {JSON}
	 * <pre>
	 * format: &lt;String>, // only dd/MM/yyyy and MM/dd/yyyy allowed, default dd/MM/yyyy if browser's language is "IT", MM/dd/yyyy otherwise
	 * min: &lt;String>, // a string representing a date in the same format of 'format' option, default 01/01/1970
	 * max: &lt;String>, // a string representing a date in the same format of 'format' option, default 01/01/3000
	 * </pre>
	 * For <code>min</code> and <code>max</code> options you can use the string <code>today</code> as special value or the name of a global function returning the time (Date object or milliseconds) to check against.
	 * @return {boolean} 
	 * @static
	 * */
	assertDate: function(obj, options) {
		var val = (obj.value || "").replace(/^\s+|\s+$/g, "");
		if (val == "") {
			return true;
		}
		var getDefaultFormat = function() {
		    var res = "dd/MM/yyyy";
		    var lang = null;
		    if (typeof navigator.userLanguage != "undefined") {
		        lang = navigator.userLanguage.toUpperCase();
		    } 
		    else if (typeof navigator.language != "undefined") {
		        lang = navigator.language.toUpperCase();
		    }
		    if (lang != null && lang.indexOf("IT") != -1) {
		    	res = "dd/MM/yyyy";
		    } 
		    else {
				res = "MM/dd/yyyy";
		    }
		    return res;
		};
		var options = arguments[1];
		if (typeof(options) == "undefined") {
			// defaults
			options = {
				format: getDefaultFormat()
				,min: "01/01/1970"
				,max: "01/01/3000"
			};
		} 
		else {
			options = eval("(" + options + ")");
			options.format = typeof(options.format) != "undefined" ? options.format : getDefaultFormat();
			options.min = typeof(options.min) != "undefined" ? options.min : "01/01/1970";
			options.max = typeof(options.max) != "undefined" ? options.max : "01/01/3000";
		}
		// try some optimizations
		var oFor = options.format;
		var oMin = options.min;
		var oMax = options.max;

		var separator = oFor.indexOf("/") != -1 ? "/" : "-";
		var sp = val.split(separator);
		if (sp.length != 3) {
			return false;
		}
		var dd, mm, yy;
		if (oFor.indexOf("d") == 0) {
			dd = sp[0];
			mm = sp[1];
		} 
		else {
			dd = sp[1];
			mm = sp[0];
		}
		yy = sp[2];
		if (yy.length != 4) {
			return false;
		}
		var _safeParseInt = function(v) {
			// workaround for a parseInt bug (or feature?): parseInt("0w") = 0
			// @see jsbeans.string.parseInt
			if (v == null || v == "") {
				return NaN;
			}
			var re = /[0-9]/;
			for (var i = 0; i < v.length; i++) {
				if (!re.test(v.charAt(i))) {
					return NaN;
				}
			}
			while (v.substring(0).indexOf("0") == 0) {
				v = v.substring(1);
			}
			return v == "" ? 0 : parseInt(v);
		};
		yy = _safeParseInt(yy);
		if (isNaN(yy)) {
			return false;
		}
		mm = _safeParseInt(mm);
		if (isNaN(mm) || mm > 12) {
			return false;
		}
		dd = _safeParseInt(dd);
		if (isNaN(dd) || dd > 31) {
			return false;
		}
		if (dd > 30 && (mm == 4 || mm == 6 || mm == 9 || mm == 11)) {
			return false;
		}
		var isLeap = false;
		if ((yy % 4 == 0) || (yy % 100 == 0) || (yy % 400 == 0)) {
			if (dd > 29 && mm == 2) {
				return false;
			}
		} 
		else {
			if (dd > 28 && mm == 2) {
				return false;
			}
		}
		var _floor = function(ms) {
			var res;
			if (ms) {
				// a Date has been passed
				if (ms.constructor && ("" + ms.constructor).indexOf("Date") != -1) {
					res = ms;
				} 
				// assuming ms are milliseconds
				else {
					res = new Date(ms);
				}
			}
			else {
				res = new Date();
			}
			res.setHours(0);
			res.setMinutes(0);
			res.setSeconds(0);
			return res;
		};
		// now check for min and max
		
		// from any type to integer yyyyMMdd
		var _getComparableFormat = function(v) {
			if (v == "today") {
				var today = _floor();
				var _yy = today.getFullYear();
				var _mm = today.getMonth() + 1;
				if (_mm < 10) {
					_mm = "0" + _mm;
				}
				var _dd = today.getDate();
				if (_dd < 10) {
					_dd = "0" + _dd;
				}
			} 
			else if (v.constructor && ("" + v.constructor).indexOf("Date") != -1) {
				var _yy = v.getFullYear();
				var _mm = v.getMonth() + 1;
				if (_mm < 10) {
					_mm = "0" + _mm;
				}
				var _dd = v.getDate();
				if (_dd < 10) {
					_dd = "0" + _dd;
				}
			}
			else {
				var vs = v.split(separator);
				if (oFor.indexOf("d") == 0) {
					_dd = vs[0];
					_mm = vs[1];
				} 
				else {
					_dd = vs[1];
					_mm = vs[0];
				}
				_yy = vs[2];
				_yy = _safeParseInt(_yy);
				_mm = _safeParseInt(_mm);
				_dd = _safeParseInt(_dd);
				if (_mm < 10) {
					_mm = "0" + _mm;
				}
				if (_dd < 10) {
					_dd = "0" + _dd;
				}
			}
			return parseInt("" + _yy + _mm + _dd);
		};
		var dateTime = _getComparableFormat(val);

		// if min or max are functions, we just use them to get time, otherwise they are strings representing dates in the current format
		var fMin = window[oMin];
		if (typeof fMin == "function") {
			var min = _getComparableFormat(_floor(fMin()));
		}
		else {
			var min = _getComparableFormat(oMin);
		}
		//
		var fMax = window[oMax];
		if (typeof fMax == "function") {
			var max = _getComparableFormat(_floor(fMax()));
		}
		else {
			var max = _getComparableFormat(oMax);
		}

		if (dateTime < min || dateTime > max) {
			return false;
		}
		return true;
	},
	/**
	 * Validation for integers.
	 * @method assertInteger
	 * @param input {DOM} the input to validate
	 * @return {boolean} false if <code class="param">input</code>'s value isn't a number (as returned by javascript's native <code>isNaN</code>) and it contains dots or commas.
	 * @static
	 * */
	assertInteger: function(obj) {
		if (obj.value.indexOf(".") != -1 || obj.value.indexOf(",") != -1) {
			return false;
		}
		return !isNaN(obj.value);
	},
	/**
	 * Validation for numeric, integers included, using javascript's native <code>isNaN</code> function.
	 * @method assertNumeric
	 * @param input {DOM}
	 * @return {boolean} 
	 * @static
	 * */
	assertNumeric: function(obj) {
		return !isNaN(obj.value);
	},
	/**
	 * Checks if {@param} <code class="param">input</code>'s value is less or equals than {@param} <code class="param">maxValue</code> using javascript's native <code>parseFloat</code> function.
	 * @method assertMaxValue
	 * @param input {DOM} the input to validate
	 * @param maxValue {Float}
	 * @return {boolean} 
	 * @static
	 * */
	assertMaxValue: function(obj, maxValue) {
		var val = (obj.value || "").replace(/^\s+|\s+$/g, "");
		if (val == "") {
			return true;
		}
		return parseFloat(val) <=  parseFloat(maxValue);
	},
	/**
	 * Checks if {@param} <code class="param">input</code>'s value is greater or equals than {@param} <code class="param">minValue</code> using javascript's native <code>parseFloat</code> function.
	 * @method assertMinValue
	 * @param input {DOM} the input to validate
	 * @param minValue {Float}
	 * @return {boolean} 
	 * @static
	 * */
	assertMinValue: function(obj, minValue) {
		var val = (obj.value || "").replace(/^\s+|\s+$/g, "");
		if (val == "") {
			return true;
		}
		return parseFloat(val) >=  parseFloat(minValue);
	},
	/**
	 * Checks if {@param} <code class="param">input</code>'s value length is less or equals than {@param} <code class="param">maxLen</code>.
	 * @method assertMaxLen
	 * @param input {DOM} the input to validate
	 * @param maxLen {Integer | String} Both Integers or Strings may be used as they will be parsed by javascript's native <code>parseInt</code> function.
	 * @return {boolean} 
	 * @static
	 * */
	assertMaxLen: function(obj, maxLen) {
		var val = (obj.value || "").replace(/^\s+|\s+$/g, "");
		if (val == "") {
			return true;
		}
		return val.length <= parseInt(maxLen);
	},
	/**
	 * Checks if {@param} <code class="param">input</code>'s value length is greater or equals than {@param} <code class="param">minLen</code>.
	 * @method assertMinLen
	 * @param input {DOM} the input to validate
	 * @param minLen {Integer | String} Both Integers or Strings may be used as they will be parsed by javascript's native <code>parseInt</code> function.
	 * @return {boolean} 
	 * @static
	 * */
	assertMinLen: function(obj, minLen) {
		var val = (obj.value || "").replace(/^\s+|\s+$/g, "");
		if (val == "") {
			return true;
		}
		return val.length >= parseInt(minLen);
	},
	/**
	 * Default function delegated to print errors. It uses an <code>alert</code>.
	 * @method manageErrors
	 * @param errors {Array&lt;JSON>} see <code class="param">jsbeans.Validator.errors</code> for details.
	 * @param options {JSON} see <code class="">jsbeans.Validator.options</code> for details.
	 * @static
	 * */
	manageErrors: function(errors, options) {
		if (errors != null && errors.length > 0) {
			var errorClass = options.errorClass || this.options.errorClass;
			var res = jsbeans.Validator.messages.prefix;
			var loc = jsbeans.Validator.messages[jsbeans.Validator.options.locale];
			if (typeof loc != "undefined" && typeof loc["prefix"] != "undefined") {
				res = loc["prefix"];
			}
			var focus1st = this.options.focusOnFirst == true;
			for (var i = 0, error; error = errors[i]; i++) {
				if (error.object == null || typeof error.object == "undefined") {
					// this should happen only in development stage or, for those with less experience, in runtime-generated environments
					var msg  = "[dev-warning] Validation error on field " + error.label;
					if (typeof JSON != "undefined" && typeof JSON.stringify != "undefined") {
						msg += " (" + JSON.stringify(error) + ")";
					}
					alert(msg);
					continue;
				}
				if (i == 0 && focus1st) {
					error.object.focus();
				}
				res += (typeof error.label == "undefined" ? error.message : ("\"" + error.label + "\" " + error.message)) + "\n";
				// adds errorClass if required
				// Note that it adds errorClass as many times as error is found, then clears them if input is valid
				if (errorClass != null) {
					error.object.className = (typeof error.object.className != "undefined") ? (error.object.className + " " + errorClass) : "" + errorClass;
				}
			}
			alert(res);
		}
	},
	/**
	 * Brings <code class="param">jsbeans.Validator.options</code> to defaults.
	 * @method reset
	 * @static
	 * */
	reset: function() {
		this._errorClassCache = this.options.errorClass;
		this.options.errorClass = null;
		this.options.callback = function(errors, options) {
			jsbeans.Validator.manageErrors(errors, options);
		};
		this.options.propagate = true;
		this.options.locale = "en";
	},
	/**
	 * @method _clearErrorClass
	 * @param errors {DOM}
	 * @private
	 * @static
	 * */
	_clearErrorClass: function(obj) {
		obj.className = obj.className ? obj.className.split(this.options.errorClass).join("") : "";
		if (this._errorClassCache != null) {
			obj.className = obj.className.split(this._errorClassCache).join("");
		}
	},
	/**
	 * A convenince method to get a human readble label for inputs. It always tries to return something in the following order:
	 * <ol>
	 * <li>looks in the page for a <code>label</code> tag with the 'for' attribute set to {@param} <code class="param">id</code></li>
	 * <li>if there's no DOM Object with given <code class="param">id</code>, the <code class="param">id</code> itself is returned</li>
	 * <li>looks for the 'title' attribute of the DOM Object as returned by a <code>document.getElementById</code> call</li>
	 * <li>looks for the 'name' attribute of the DOM Object as returned by a <code>document.getElementById</code> call</li>
	 * <ol>
	 * Result is trimmed to avoid unwanted characters such as spaces, newlines, ...
	 * @method getLabelFor
	 * @param id {String} the DOM Object id
	 * @return {String}
	 * @static
	 * */
	getLabelFor: function(id) {
		var trim = function(s) {
			return s.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
		};
		var labels = document.getElementsByTagName("label");
		for (var i = 0, l; l = labels[i]; i++) {
			try {
				var f = l.getAttributeNode("for").nodeValue;
				if (f != null && f == id) {
					return trim(l.innerHTML);
				}
			} 
			catch(e) {}
		}
		var o = document.getElementById(id);
		if (o == null) {
			return id;
		}
		var _val = function(obj, attrName) {
			var res = null;
			var node = o.getAttributeNode(attrName);
			if (node != null && typeof(node) != "undefined") {
				var v = node.nodeValue;
				if (v != null && typeof(v) != "undefined" && v != "") {
					res = v;
				}
			}
			return res;
		};
		var res = _val(o, "title");
		if (res == null) {
			res = _val(o, "name");
		}
		return res != null ? trim(res) : id;
	},
	/**
	 * @method _invoke
	 * @param object {DOM}
	 * @param type {String}
	 * @param arg {String}
	 * @private
	 * @static
	 * */
	_invoke: function(obj, type, arg) {
		try {
			if (obj == null) {
				throw {message: "Input to validate is null!"};
			}
			var _func = "assert" + type.charAt(0).toUpperCase() + type.substring(1);
			var res = false;
			if (typeof jsbeans.Validator[_func] != "undefined") {
				if (arg != null) {
					res = eval("jsbeans.Validator." + _func + "(obj,arg)");
				} 
				else {
					res = eval("jsbeans.Validator." + _func + "(obj)");
				}
			}
			else if (typeof jsbeans.Validator[type][_func] != "undefined") {
				if (arguments[2]) {
					var arg = arguments[2];
					res = eval("jsbeans.Validator." + type + "." + _func + "(obj,arg)");
				} 
				else {
					res = eval("jsbeans.Validator." + type + "." + _func + "(obj)");
				}
			}
			// no method found in jsbeans.Validator
			else {
				throw {message: "Missing method!"};
			}
		} 
		catch(e) {
			var m = "Invoke error: " + e.message;
			if (typeof console != "undefined") {
				console.log(m);
			}
			else {
				alert(m);
			}
		}
		return res;
	},
	/**
	 * @method _getMessage
	 * @param key {String}
	 * @return {String}
	 * @private
	 * @static
	 * */
	_getMessage: function(key) {
		var l = "";
		if (jsbeans.Validator.options.locale != null) {
			l = jsbeans.Validator.options.locale;
		}
		var msgs = jsbeans.Validator.messages;
		// current locale for default validation
		if (typeof(msgs[l]) != "undefined" && typeof(msgs[l][key]) != "undefined") {
			return msgs[l][key];
		}
		// current locale for extensions
		if (typeof(jsbeans.Validator[key]) != "undefined" && typeof(jsbeans.Validator[key].messages) != "undefined" && typeof(jsbeans.Validator[key].messages[l]) != "undefined") {
			return jsbeans.Validator[key].messages[l];
		}
		// default locale: en
		if (typeof(msgs.en) != "undefined" && typeof(msgs.en[key]) != "undefined") {
			return msgs.en[key];
		}
		// default locale for extensions
		if (typeof(jsbeans.Validator[key]) != "undefined" && typeof(jsbeans.Validator[key].messages) != "undefined" && typeof(jsbeans.Validator[key].messages.en) != "undefined") {
			return jsbeans.Validator[key].messages.en;
		}
		// straigth message
		if (typeof(msgs[key]) != "undefined") {
			return msgs[key];
		}
		/*
		try {
			return eval("jsbeans.Validator." + key + ".messages['" + key + "']");
		} catch(e) {
			try {
				return eval("jsbeans.Validator." + key + ".messages['" + key + "']");
			} catch(e1) {}
		}
		*/
		// when everything else fails
		return msgs["invalid"];
	}
};

Copyright © 2016 Francesco Mele. All rights reserved.