Skip to content

Instantly share code, notes, and snippets.

@hunterc
Created February 3, 2014 19:23
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 hunterc/8790547 to your computer and use it in GitHub Desktop.
Save hunterc/8790547 to your computer and use it in GitHub Desktop.
Form field validator: currently requires a fieldmap with jquery element
(function (global) {
'use strict';
var Validator = function Validator() {
this.schemas = {
text: {
maxLength: function (val, prop) {
return {
success: val.length <= prop.maxLength,
msg: 'Value exceeds maximum length: ' + prop.maxLength + '.'
};
},
minLength: function (val, prop) {
return {
success: val.length >= prop.minLength,
msg: 'Value less than minimum length: ' + prop.minLength + '.'
};
},
pattern: function (val, prop) {
return {
success: prop.pattern.test(val),
msg: 'Value does not match pattern.'
};
},
invalid: function (val, prop) {
return {
success: prop.invalid.every(function (item, index, array) { return val !== item; }),
msg: 'Value is an invalid value.'
};
},
enum: function (val, prop, field) {
return {
success : prop.enum.indexOf(val) > -1 || field.custom,
msg: 'Value is not a valid choice.'
};
}
},
number: {
// required function. if !number then return false
number: function (val) {
return {
success: +val,
msg: 'Value not a number.'
};
},
multipleOf: function (val, prop) {
return {
success: val % prop.multipleOf === 0,
msg: 'Value not a multiple of ' + prop.multipleOf + '.'
};
},
maximum: function (val, prop) {
return {
success: val <= prop.maximum,
msg: 'Value exceeds maximum: ' + prop.maximum + '.'
};
},
minimum: function (val, prop) {
return {
success: val >= prop.minimum,
msg: 'Value less than minimum: ' + prop.minimum + '.'
};
}
},
date: {
date: function (val) {
return {
success: !isNaN(Date.parse(val)),
msg: 'Value is not a valid date.'
};
},
before: function (val, prop) {
var res = true;
// overloading
if (prop.before.element) {
if (Date.parse(prop.before.element.val())) {
res = Date.parse(val) < Date.parse(prop.before.element.val());
}
} else if (prop.before.value) {
if (Date.parse(prop.before.value)) {
res = Date.parse(val) < Date.parse(prop.before.value);
}
} else {
if (!prop.before.element && !prop.before.value && prop.before.value.length > 0) {
res = false;
}
}
return {
success: res,
msg: 'Date not before ' + prop.before.name + '.'
};
},
beforeEq: function (val, prop) {
var res = true;
// overloading
if (prop.beforeEq.element) {
if (Date.parse(prop.beforeEq.element.val())) {
res = Date.parse(val) <= Date.parse(prop.beforeEq.element.val());
}
} else if (prop.beforeEq.value) {
if (Date.parse(prop.beforeEq.value)) {
res = Date.parse(val) <= Date.parse(prop.beforeEq.value);
}
} else {
if (!prop.beforeEq.element && !prop.beforeEq.value && prop.beforeEq.value.length > 0) {
res = false;
}
}
return {
success: res,
msg: 'Date not before ' + prop.beforeEq.name + '.'
};
},
after: function (val, prop) {
var res = true;
// overloading
if (prop.after.element) {
if (Date.parse(prop.after.element.val())) {
res = Date.parse(val) > Date.parse(prop.after.element.val());
}
} else if (prop.after.value) {
if (Date.parse(prop.after.value)) {
res = Date.parse(val) > Date.parse(prop.after.value);
}
} else {
if (!prop.after.element && !prop.after.value && prop.after.value.length > 0) {
res = false;
}
}
return {
success: res,
msg: 'Date not after ' + prop.after.name + '.'
};
},
afterEq: function (val, prop) {
var res = false;
if (prop.afterEq.hasOwnProperty('element')) {
if (Date.parse(prop.afterEq.element.val())) {
res = Date.parse(val) >= Date.parse(prop.afterEq.element.val());
} else {
res = true;
}
} else if (prop.afterEq.hasOwnProperty('value')) {
if (Date.parse(prop.afterEq.value)) {
res = Date.parse(val) >= Date.parse(prop.afterEq.value);
} else {
res = true;
}
}
return {
success: res,
msg: 'Date not after ' + prop.afterEq.name + '.'
};
}
}
};
this.errors = [];
this.fieldErrors = [];
this.errorCount = 0;
this.skipped = [];
this.customSchemas = {};
};
Validator.prototype.addSchema = function (name, schema) {
this.customSchemas[name] = schema;
};
Validator.prototype.removeSchema = function (name) {
return delete this.customSchemas[name];
};
Validator.prototype.clearSchemas = function () {
this.customSchemas = {};
};
Validator.prototype.validate = function (fieldMap) {
var key,
rule,
field,
res;
// clear errors for new run
this.errors = [];
this.fieldErrors = [];
this.skipped = [];
this.errorCount = 0;
this.hideErrors();
for (key in fieldMap) {
if (fieldMap.hasOwnProperty(key)) {
field = fieldMap[key];
// unknown schema field type => error and return false;
if (this.schemas[field.type] === undefined) {
this.errors.push('Unknown schema field type: ' + key);
return false;
}
// check if 'element' key exist and is valid
if (field.hasOwnProperty('element') && field.element.length > 0) {
// if empty not required, skip
if (!field.required) {
if (field.element.val().trim().length === 0 || (field.properties.invalid && field.properties.invalid.indexOf(field.element.val().trim()) > -1) ) {
this.skipped.push({
element: field.element,
msg: 'Element was skipped.'
});
continue;
}
}
// has required base function
if (this.schemas[field.type][field.type]) {
res = this.schemas[field.type][field.type](field.element.val());
if (!res.success) {
this.errorCount++;
this.fieldErrors.push({
'element': field.element,
'msg': res.msg,
'required': field.required
});
continue;
}
}
// if valid is true then all rules passed for schema
for (rule in field.properties) {
if (field.properties.hasOwnProperty(rule)) {
// invalid rule for type => error and return false;
if (this.schemas[field.type][rule] === undefined) {
this.errors.push('Invalid rule for type: ' + field.type + '.');
return false;
}
if (field.custom && field.custom.is(':visible')) {
res = this.schemas[field.type][rule](field.custom.val(), field.properties, field);
} else {
res = this.schemas[field.type][rule](field.element.val(), field.properties, field);
}
if (!res.success) {
this.fieldErrors.push({
'element': field.element,
'msg': res.msg,
'required': field.required
});
this.errorCount++;
}
}
}
}
}
}
//if no errors => valid
return this.errorCount === 0;
};
Validator.prototype.showErrors = function() {
this.fieldErrors.forEach(function(item, index, array) {
item.element.addClass('error-field');
item.element.siblings('.error-label').length < 1
? item.element.parent().siblings('.error-label').html(item.msg).addClass('error-msg')
: item.element.siblings('.error-label').html(item.msg).addClass('error-msg');
});
};
Validator.prototype.hideErrors = function() {
$('p.error-label').removeClass('error-msg');
$('.error-field').removeClass('error-field');
};
global.fval = new Validator();
}(this));
/*
var fieldMap = {
field: {
element: $('#field'),
type: 'string',
name: 'Field Name',
required: true,
properties: {
minimum: 2,
maximum: 5
}
}
};
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment