Skip to content

Instantly share code, notes, and snippets.

@MathRobin
Last active October 13, 2015 05:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MathRobin/4148467 to your computer and use it in GitHub Desktop.
Save MathRobin/4148467 to your computer and use it in GitHub Desktop.
Val Engine, a validation engine (sync and async validations allowed)
data = {};
data['a'] = '';
data['b'] = 'a';
data['c'] = 'a@';
data['d'] = 'a@a';
data['e'] = 'a@a.';
data['f'] = 'a@htmail.fr';
data['g'] = 'a@hotmail.fr';
valEngine = new ValEngine();
// init required
valEngine.addRuleToStore(data, 'a', 'required');
valEngine.addRuleToStore(data, 'b', 'required');
// init is_email
valEngine.addRuleToStore(data, 'a', 'is_email');
valEngine.addRuleToStore(data, 'b', 'is_email');
valEngine.addRuleToStore(data, 'c', 'is_email');
valEngine.addRuleToStore(data, 'd', 'is_email');
valEngine.addRuleToStore(data, 'e', 'is_email');
valEngine.addRuleToStore(data, 'f', 'is_email');
valEngine.addRuleToStore(data, 'g', 'is_email');
// valid required
valEngine.validStore(data, 'a', function (result) {console.log('success')}, function (result) {console.log('fail')}); // fail
valEngine.validStore(data, 'b', function (result) {console.log('success')}, function (result) {console.log('fail')}); // success
// valid is_email
valEngine.validStore(data, 'a', function (result) {console.log('success', result.message)}, function (result) {console.log('fail', result.message)}); // fail wrongFormat
valEngine.validStore(data, 'b', function (result) {console.log('success', result.message)}, function (result) {console.log('fail', result.message)}); // fail wrongFormat
valEngine.validStore(data, 'c', function (result) {console.log('success', result.message)}, function (result) {console.log('fail', result.message)}); // fail wrongFormat
valEngine.validStore(data, 'd', function (result) {console.log('success', result.message)}, function (result) {console.log('fail', result.message)}); // fail wrongFormat
valEngine.validStore(data, 'e', function (result) {console.log('success', result.message)}, function (result) {console.log('fail', result.message)}); // fail wrongFormat
valEngine.validStore(data, 'f', function (result) {console.log('success', result.message)}, function (result) {console.log('fail', result.message)}); // success a@hotmail.fr
valEngine.validStore(data, 'g', function (result) {console.log('success', result)}, function (result) {console.log('fail', result)}); // success undefined
/*jslint white: true, plusplus: true */
/*
* Mailcheck https://github.com/Kicksend/mailcheck
* Author
* Derrick Ko (@derrickko)
*
* Custom version by Mathieu ROBIN
*
* License
* Copyright (c) 2012 Receivd, Inc.
*
* Licensed under the MIT License.
*
* v 1.1
*/
var Kicksend = {
mailcheck : {
threshold : 3,
defaultDomains : [
"yahoo.com", "google.com", "hotmail.com", "gmail.com", "me.com", "aol.com", "mac.com", 'rocketmail.com',
"live.com", "comcast.net", "googlemail.com", "msn.com", "hotmail.co.uk", "yahoo.co.uk",
"facebook.com", "verizon.net", "sbcglobal.net", "att.net", "gmx.com", "mail.com"
],
defaultTopLevelDomains : ["co.uk", "com", "net", "org", "info", "edu", "gov", "mil"],
run : function (opts) {
'use strict';
var REGEX_MAIL = /^[a-zA-Z0-9._\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,4}$/,
result;
if (REGEX_MAIL.test(opts.email)) {
opts.domains = opts.domains || Kicksend.mailcheck.defaultDomains;
opts.topLevelDomains = opts.topLevelDomains || Kicksend.mailcheck.defaultTopLevelDomains;
opts.distanceFunction = opts.distanceFunction || Kicksend.sift3Distance;
result = Kicksend.mailcheck.suggest(encodeURI(opts.email), opts.domains, opts.topLevelDomains, opts.distanceFunction);
if (result) {
if (opts.suggested) {
opts.suggested(result);
}
} else {
if (opts.empty) {
opts.empty();
}
}
} else {
opts.wrongFormat();
}
},
suggest : function (email, domains, topLevelDomains, distanceFunction) {
'use strict';
var emailParts = this.splitEmail(email.toLowerCase()),
closestDomain,
closestTopLevelDomain,
domain;
closestDomain = this.findClosestDomain(emailParts.domain, domains, distanceFunction);
if (closestDomain) {
if (closestDomain !== emailParts.domain) {
// The email address closely matches one of the supplied domains; return a suggestion
return { address : emailParts.address, domain : closestDomain, full : emailParts.address + "@" + closestDomain };
}
} else {
// The email address does not closely match one of the supplied domains
closestTopLevelDomain = this.findClosestDomain(emailParts.topLevelDomain, topLevelDomains);
if (emailParts.domain && closestTopLevelDomain && closestTopLevelDomain !== emailParts.topLevelDomain) {
// The email address may have a mispelled top-level domain; return a suggestion
domain = emailParts.domain;
closestDomain = domain.substring(0, domain.lastIndexOf(emailParts.topLevelDomain)) + closestTopLevelDomain;
return { address : emailParts.address, domain : closestDomain, full : emailParts.address + "@" + closestDomain };
}
}
/* The email address exactly matches one of the supplied domains, does not closely
* match any domain and does not appear to simply have a mispelled top-level domain,
* or is an invalid email address; do not return a suggestion.
*/
return false;
},
findClosestDomain : function (domain, domains, distanceFunction) {
'use strict';
var dist,
minDist = 99,
closestDomain = null,
i,
exitStatus = false;
if (!domain || !domains) {
return false;
}
if (!distanceFunction) {
distanceFunction = this.sift3Distance;
}
for (i = 0; i < domains.length; i++) {
if (domain === domains[i]) {
return domain;
}
dist = distanceFunction(domain, domains[i]);
if (dist < minDist) {
minDist = dist;
closestDomain = domains[i];
}
}
if (minDist <= this.threshold && closestDomain !== null) {
exitStatus = closestDomain;
}
return exitStatus;
},
sift3Distance : function (s1, s2) {
'use strict';
var i,
c = 0,
offset1 = 0,
offset2 = 0,
lcs = 0,
maxOffset = 5;
// sift3: http://siderite.blogspot.com/2007/04/super-fast-and-accurate-string-distance.html
if (null === s1 || 0 === s1.length) {
if (s2 === null || 0 === s2.length) {
return 0;
} else {
return s2.length;
}
}
if (null === s2 || s2.length === 0) {
return s1.length;
}
while ((c + offset1 < s1.length) && (c + offset2 < s2.length)) {
if (s1.charAt(c + offset1) === s2.charAt(c + offset2)) {
lcs++;
} else {
offset1 = 0;
offset2 = 0;
for (i = 0; i < maxOffset; i++) {
if ((c + i < s1.length) && (s1.charAt(c + i) === s2.charAt(c))) {
offset1 = i;
break;
}
if ((c + i < s2.length) && (s1.charAt(c) === s2.charAt(c + i))) {
offset2 = i;
break;
}
}
}
c++;
}
return (s1.length + s2.length) / 2 - lcs;
},
splitEmail : function (email) {
'use strict';
var parts = email.split('@'),
i,
domain,
domainParts,
tld = '';
if (parts.length < 2) {
return false;
}
for (i = 0; i < parts.length; i++) {
if (parts[i] === '') {
return false;
}
}
domain = parts.pop();
domainParts = domain.split('.');
if (domainParts.length === 0) {
// The address does not have a top-level domain
return false;
} else if (domainParts.length === 1) {
// The address has only a top-level domain (valid under RFC)
tld = domainParts[0];
} else {
// The address has a domain and a top-level domain
for (i = 1; i < domainParts.length; i++) {
tld += domainParts[i] + '.';
}
if (domainParts.length >= 2) {
tld = tld.substring(0, tld.length - 1);
}
}
return {
topLevelDomain : tld,
domain : domain,
address : parts.join('@')
};
}
}
};
/*globals Kicksend, $ */
/**
* Storage validation system
* validate datastores by verifying customs set of rules
*
* @return {Object}
* @constructor
*/
function ValEngine() {
'use strict';
var stores = {},
rules = {
/**
* Required validation method
* Sync
*
* @param store
* @param store_id
* @return {Object}
*/
required : function (store, store_id) {
var retour;
if (undefined === store || undefined === store_id) {
throw 'ValEngine.required - Missed one (or more) parameter';
}
retour = ((undefined !== store[store_id]) && (null !== store[store_id]) && (('string' === typeof store[store_id]) || (('object' === typeof store[store_id]) && 0 < Object.keys(store[store_id]).length) || ('number' === typeof store[store_id])) && ('' !== store[store_id].toString().trim()));
return {
status : retour,
message : ''
};
},
/**
*
*
* @param store
* @param store_id
* @param success_callback
* @param failure_callback
*/
is_email : function (store, store_id, success_callback, failure_callback) {
if (undefined === store || undefined === store_id || undefined === success_callback || undefined === failure_callback) {
throw 'ValEngine.is_email - Missed one (or more) parameter';
}
Kicksend.mailcheck.run({
email : store[store_id],
domains : [
'yahoo.fr', 'yahoo.com', 'yahoo.de', 'google.com', 'hotmail.fr', 'hotmail.be', 'hotmail.com', 'gmail.com', 'me.com', 'aol.com', 'mac.com', 'rocketmail.com',
'live.fr', 'live.be', 'live.com', 'comcast.net', 'googlemail.com', 'msn.com', 'hotmail.co.uk', 'yahoo.co.uk', 'caramail.com',
'facebook.com', 'verizon.net', 'sbcglobal.net', 'att.net', 'gmx.com', 'gmx.fr', 'mail.com', 'rocketmail.com', 'laposte.net', 'skynet.be', 'wanadoo.fr', 'orange.fr', 'sfr.fr', 'neuf.fr', 'free.fr', 'club-internet.fr', 'aliceadsl.fr', 'tiscali.fr'
],
topLevelDomains : ['fr', 'co.uk', 'com', 'be', 'de', 'net', 'org', 'info', 'edu', 'gov', 'mil'],
suggested : function (suggestion) {
stores[store_id].success_counter = stores[store_id].success_counter + 1;
if (stores[store_id].rules.length === stores[store_id].success_counter) {
success_callback({
message : suggestion.full
});
}
},
empty : function () {
stores[store_id].success_counter = stores[store_id].success_counter + 1;
if (stores[store_id].rules.length === stores[store_id].success_counter) {
success_callback();
}
},
wrongFormat : function () {
failure_callback({
message : 'wrongFormat'
});
}
});
},
/**
* Sync
*
* @param store
* @param store_id
*/
is_url : function (store, store_id) {
var REGEX_URL = /^(https?):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i,
REGEX_URL_WITHOUT_PROTOCOL = /^((https?):\/\/)?(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i,
regex_used;
if (undefined === store || undefined === store_id) {
throw 'ValEngine.is_url - Missed one (or more) parameter';
}
if ((undefined !== stores[store_id].options) && (undefined !== stores[store_id].options.take_care_of_protocol) && (false === stores[store_id].options.take_care_of_protocol)) {
regex_used = REGEX_URL_WITHOUT_PROTOCOL;
} else {
regex_used = REGEX_URL;
}
return {
status : regex_used.test(store[store_id]),
message : ''
};
},
/**
* Verify if a string is only composed with letters
* Sync
*
* @param store
* @param store_id
*/
is_alpha_string : function (store, store_id) {
var REGEX_ALPHA_STRING = /^[a-z]+$/i;
if (undefined === store || undefined === store_id) {
throw 'ValEngine.is_alpha_string - Missed one (or more) parameter';
}
return {
status : REGEX_ALPHA_STRING.test(store[store_id]),
message : ''
};
},
/**
* Verify if a string is only composed with numbers
* can be configured for check if there is a comma, a dot or both
* Sync
*
* @param store
* @param store_id
*/
is_numeric : function (store, store_id) {
var REGEX_NUMBER = /^[0-9]+$/,
REGEX_NUMBER_WITH_COMMA = /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/,
REGEX_NUMBER_WITH_DOT = /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/,
REGEX_NUMBER_WITH_COMMA_AND_DOT = /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/,
regex_used;
if (undefined === store || undefined === store_id) {
throw 'ValEngine.is_numeric - Missed one (or more) parameter';
}
regex_used = REGEX_NUMBER;
if (undefined !== stores[store_id].options.comma_allowed && true === stores[store_id].options.comma_allowed) {
if (undefined !== stores[store_id].options.dot_allowed && true === stores[store_id].options.dot_allowed) {
regex_used = REGEX_NUMBER_WITH_COMMA;
} else {
regex_used = REGEX_NUMBER_WITH_COMMA_AND_DOT;
}
}
if (undefined !== stores[store_id].options.dot_allowed && true === stores[store_id].options.dot_allowed) {
regex_used = REGEX_NUMBER_WITH_DOT;
}
return {
status : regex_used.test(store[store_id]),
message : ''
};
},
/**
* Search in options.values if the gived value is not in
* Sync
*
* @param store
* @param store_id
* @return {Object}
*/
not_equal_to : function (store, store_id) {
var retour = {
status : false,
message : 'no rules defined'
};
if (undefined === store || undefined === store_id) {
throw 'ValEngine.not_equal_to - Missed one (or more) parameter';
}
if (undefined !== stores[store_id].options && $.isArray(stores[store_id].options.values)) {
if (-1 === stores[store_id].options.values.indexOf(store[store_id])) {
retour = {
status : true,
message : ''
};
}
}
return retour;
},
/**
* Verify if value matches with the mask provided
* Sync
*
* @param store
* @param store_id
* @return {Object}
*/
9999 : function (store, store_id) {
var regex = /^[0-9]{4}$/;
if (undefined === store || undefined === store_id) {
throw 'ValEngine.not_equal_to - Missed one (or more) parameter';
}
return {
status : regex.test(store[store_id]),
message : ''
};
},
/**
* Verify if value matches with the mask provided
* Sync
*
* @param store
* @param store_id
* @return {Object}
*/
99999 : function (store, store_id) {
var regex = /^[0-9]{4}$/;
if (undefined === store || undefined === store_id) {
throw 'ValEngine.not_equal_to - Missed one (or more) parameter';
}
return {
status : regex.test(store[store_id]),
message : ''
};
},
/**
* Verify if value matches with the mask provided
* Sync
*
* @param store
* @param store_id
* @return {Object}
*/
9999999999 : function (store, store_id) {
var regex = /^[0-9]{10}$/;
if (undefined === store || undefined === store_id) {
throw 'ValEngine.not_equal_to - Missed one (or more) parameter';
}
return {
status : regex.test(store[store_id]),
message : ''
};
},
/**
* Verify if value matches with the mask provided
* Sync
*
* @param store
* @param store_id
* @return {Object}
*/
'999 999 999 99999' : function (store, store_id) {
var regex = /^[0-9]{3} [0-9]{3} [0-9]{3} [0-9]{5}$/;
if (undefined === store || undefined === store_id) {
throw 'ValEngine.not_equal_to - Missed one (or more) parameter';
}
return {
status : regex.test(store[store_id]),
message : ''
};
},
/**
* Verify if value matches with the mask provided
* Sync
*
* @param store
* @param store_id
* @return {Object}
*/
'9999 aa' : function (store, store_id) {
var regex = /^[0-9]{4} [a-zA-Z]{2}$/;
if (undefined === store || undefined === store_id) {
throw 'ValEngine.not_equal_to - Missed one (or more) parameter';
}
return {
status : regex.test(store[store_id]),
message : ''
};
},
/**
* Verify if value matches with the mask provided
* Sync
*
* @param store
* @param store_id
* @return {Object}
*/
'99/99/9999' : function (store, store_id) {
var regex = /^[0-9]{2}[/][0-9]{2}[/][0-9]{4}$/;
if (undefined === store || undefined === store_id) {
throw 'ValEngine.not_equal_to - Missed one (or more) parameter';
}
return {
status : regex.test(store[store_id]),
message : ''
};
}
};
return {
/**
* Return all stores
*
* @return {Object}
*/
getStores : function () {
return stores;
},
/**
* Return specific store
*
* @return {Object}
*/
getStore : function (index) {
return stores[index];
},
/**
* Add a rule to a store
*
* @param store_id
* @param rule
* @param options
*/
addRuleToStore : function (store_id, rule, options) {
var retour = true;
if (undefined === store_id || undefined === rule || ('string' !== typeof store_id && 'number' !== typeof store_id) || undefined === rules[rule]) {
retour = false;
} else {
if (undefined === stores[store_id] || undefined === stores[store_id].rules || !$.isArray(stores[store_id].rules)) {
stores[store_id] = {
rules : [],
success_counter : 0
};
}
if (undefined !== options) {
stores[store_id].options = $.extend(options, stores[store_id].options);
}
stores[store_id].rules.push(rules[rule]);
}
return retour;
},
/**
* Validate a specific store by passing all rules
*
* @param global_store
* @param store_id
* @return {Boolean}
* @param success_callback
* @param failure_callback
*/
validStore : function (global_store, store_id, success_callback, failure_callback) {
var retour,
rule;
if (undefined === global_store || undefined === store_id || undefined === success_callback || undefined === failure_callback) {
throw 'ValEngine.validStore - Missed one (or more) parameter';
}
if (undefined !== stores[store_id]) {
if (undefined !== global_store[store_id]) {
stores[store_id].success_counter = 0;
if (!$.isArray(stores[store_id].rules)) {
stores[store_id].rules = [];
}
for (rule in stores[store_id].rules) {
if (stores[store_id].rules.hasOwnProperty(rule)) {
retour = stores[store_id].rules[rule](global_store, store_id, success_callback, failure_callback);
if (undefined !== retour && undefined !== retour.status) {
if (false === retour.status) {
if (undefined !== failure_callback) {
failure_callback();
}
} else {
if (true === retour.status) {
stores[store_id].success_counter = stores[store_id].success_counter + 1;
if (stores[store_id].rules.length === stores[store_id].success_counter) {
if (undefined !== success_callback) {
success_callback();
}
}
}
}
}
}
}
} else {
if (undefined !== failure_callback) {
failure_callback();
}
}
} else {
if (undefined !== success_callback) {
success_callback();
}
}
return undefined;
},
/**
* Reset the engine
*/
resetEngine : function () {
stores = {};
},
/**
* Reset the engine
*
* @param store
* @return {*}
*/
removeStore : function (store) {
var retour;
if (undefined !== stores[store]) {
delete stores[store];
retour = true;
}
return retour;
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment