Created
February 3, 2014 19:23
-
-
Save hunterc/8790547 to your computer and use it in GitHub Desktop.
Form field validator: currently requires a fieldmap with jquery element
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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