express-form
Advanced tools
Comparing version 0.6.0 to 0.6.1
547
lib/field.js
@@ -10,66 +10,67 @@ var validator = require("validator") | ||
function Field(property, label) { | ||
var stack = [] | ||
, isArray = false | ||
, fieldLabel = property || label; | ||
var stack = [] | ||
, isArray = false | ||
, fieldLabel = property || label; | ||
this.name = property; | ||
this.__required = false; | ||
this.add = function(func) { | ||
stack.push(func); | ||
return this; | ||
}; | ||
this.array = function() { | ||
isArray = true; | ||
return this; | ||
}; | ||
this.run = function (source, form, options) { | ||
var self = this | ||
, errors = []; | ||
property = property.replace(/\[((.)*?)\]/g, ".$1"); // Convert square-bracket to dot notation. | ||
var value = utils.getProp(property, form) || utils.getProp(property, source); | ||
if (options.autoTrim) { | ||
stack.push(function (value) { | ||
if (object.isString(value)) { | ||
return FilterPrototype.trim.apply(externalFilter.sanitize(value)); | ||
} | ||
return value; | ||
}); | ||
} | ||
function runStack(foo) { | ||
stack.forEach(function (proc) { | ||
var result = proc(foo, source); // Pass source for "equals" proc. | ||
if (result.valid) return; | ||
if (result.error) { | ||
// If this field is not required and it doesn't have a value, ignore error. | ||
if (!utils.hasValue(value) && !self.__required) return; | ||
return errors.push(result.error.replace("%s", fieldLabel)); | ||
} | ||
foo = result; | ||
}); | ||
return foo; | ||
} | ||
if (isArray) { | ||
if (!utils.hasValue(value)) value = []; | ||
if (!Array.isArray(value)) value = [value]; | ||
value = value.map(runStack); | ||
} else { | ||
if (Array.isArray(value)) value = value[0]; | ||
value = runStack(value); | ||
} | ||
utils.setProp(property, form, value); | ||
if (errors.length) return errors; | ||
}; | ||
this.name = property; | ||
this.__required = false; | ||
this.add = function(func) { | ||
stack.push(func); | ||
return this; | ||
}; | ||
this.array = function() { | ||
isArray = true; | ||
return this; | ||
}; | ||
this.run = function (source, form, options) { | ||
var self = this | ||
, errors = []; | ||
property = property.replace(/\[((.)*?)\]/g, ".$1"); // Convert square-bracket to dot notation. | ||
var value = utils.getProp(property, form) || utils.getProp(property, source); | ||
if (options.autoTrim) { | ||
stack.push(function (value) { | ||
if (object.isString(value)) { | ||
return FilterPrototype.trim.apply(externalFilter.sanitize(value)); | ||
} | ||
return value; | ||
}); | ||
} | ||
function runStack(foo) { | ||
stack.forEach(function (proc) { | ||
var result = proc(foo, source); // Pass source for "equals" proc. | ||
if (result.valid) return; | ||
if (result.error) { | ||
// If this field is not required and it doesn't have a value, ignore error. | ||
if (!utils.hasValue(value) && !self.__required) return; | ||
return errors.push(result.error.replace("%s", fieldLabel)); | ||
} | ||
foo = result; | ||
}); | ||
return foo; | ||
} | ||
if (isArray) { | ||
if (!utils.hasValue(value)) value = []; | ||
if (!Array.isArray(value)) value = [value]; | ||
value = value.map(runStack); | ||
} else { | ||
if (Array.isArray(value)) value = value[0]; | ||
value = runStack(value); | ||
} | ||
utils.setProp(property, form, value); | ||
if (errors.length) return errors; | ||
}; | ||
} | ||
@@ -80,15 +81,15 @@ | ||
Field.prototype.array = function () { | ||
return this.array(); | ||
return this.array(); | ||
}; | ||
Field.prototype.arrLength = function (from, to) { | ||
return this.add(function (arr) { | ||
if (value.length < from) { | ||
return { error: message || e.message || "%s is too short" }; | ||
} | ||
if (value.length > to) { | ||
return { error: message || e.message || "%s is too long" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
return this.add(function (arr) { | ||
if (value.length < from) { | ||
return { error: message || e.message || "%s is too short" }; | ||
} | ||
if (value.length > to) { | ||
return { error: message || e.message || "%s is too long" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
} | ||
@@ -99,13 +100,13 @@ | ||
Field.prototype.custom = function(func, message) { | ||
return this.add(function (value) { | ||
try { | ||
var result = func(value); | ||
} catch (e) { | ||
return { error: message || e.message || "%s is invalid" }; | ||
} | ||
// Functions that return values are filters. | ||
if (result != null) return result; | ||
return { valid: true }; | ||
}); | ||
return this.add(function (value, source) { | ||
try { | ||
var result = func(value, source); | ||
} catch (e) { | ||
return { error: message || e.message || "%s is invalid" }; | ||
} | ||
// Functions that return values are filters. | ||
if (result != null) return result; | ||
return { valid: true }; | ||
}); | ||
}; | ||
@@ -116,52 +117,52 @@ | ||
Object.keys(FilterPrototype).forEach(function (name) { | ||
if (name.match(/^ifNull$/)) return; | ||
if (name.match(/^ifNull$/)) return; | ||
Field.prototype[name] = function () { | ||
var args = arguments; | ||
return this.add(function (value) { | ||
var a = FilterPrototype[name].apply(externalFilter.sanitize(value), args); | ||
return a; | ||
}); | ||
}; | ||
Field.prototype[name] = function () { | ||
var args = arguments; | ||
return this.add(function (value) { | ||
var a = FilterPrototype[name].apply(externalFilter.sanitize(value), args); | ||
return a; | ||
}); | ||
}; | ||
}); | ||
Field.prototype.ifNull = function (replacement) { | ||
return this.add(function (value) { | ||
if (object.isUndefined(value) || null === value || '' === value) { | ||
return replacement; | ||
} | ||
return value; | ||
}); | ||
return this.add(function (value) { | ||
if (object.isUndefined(value) || null === value || '' === value) { | ||
return replacement; | ||
} | ||
return value; | ||
}); | ||
}; | ||
Field.prototype.toUpper = Field.prototype.toUpperCase = function () { | ||
return this.add(function (value) { | ||
return value.toUpperCase(); | ||
}); | ||
return this.add(function (value) { | ||
return value.toUpperCase(); | ||
}); | ||
}; | ||
Field.prototype.toLower = Field.prototype.toLowerCase = function () { | ||
return this.add(function (value) { | ||
return value.toLowerCase(); | ||
}); | ||
return this.add(function (value) { | ||
return value.toLowerCase(); | ||
}); | ||
}; | ||
Field.prototype.truncate = function (length) { | ||
return this.add(function (value) { | ||
if (value.length <= length) { | ||
return value; | ||
} | ||
if (length <= 3) return "..."; | ||
if (value.length > length - 3) { | ||
return value.substr(0,length - 3) + "..."; | ||
} | ||
return value; | ||
}); | ||
return this.add(function (value) { | ||
if (value.length <= length) { | ||
return value; | ||
} | ||
if (length <= 3) return "..."; | ||
if (value.length > length - 3) { | ||
return value.substr(0,length - 3) + "..."; | ||
} | ||
return value; | ||
}); | ||
}; | ||
Field.prototype.customFilter = function (func) { | ||
return this.add(func); | ||
return this.add(func); | ||
}; | ||
@@ -172,54 +173,54 @@ | ||
var MESSAGES = { | ||
isEmail: "%s is not an email address", | ||
isUrl: "%s is not a URL", | ||
isIP: "%s is not an IP address", | ||
isAlpha: "%s contains non-letter characters", | ||
isAlphanumeric: "%s contains non alpha-numeric characters", | ||
isNumeric: "%s is not numeric", | ||
isLowercase: "%s contains uppercase letters", | ||
isUppercase: "%s contains lowercase letters", | ||
isInt: "%s is not an integer", | ||
notEmpty: "%s has no value or is only whitespace" | ||
isEmail: "%s is not an email address", | ||
isUrl: "%s is not a URL", | ||
isIP: "%s is not an IP address", | ||
isAlpha: "%s contains non-letter characters", | ||
isAlphanumeric: "%s contains non alpha-numeric characters", | ||
isNumeric: "%s is not numeric", | ||
isLowercase: "%s contains uppercase letters", | ||
isUppercase: "%s contains lowercase letters", | ||
isInt: "%s is not an integer", | ||
notEmpty: "%s has no value or is only whitespace" | ||
}; | ||
Object.keys(ValidatorPrototype).forEach(function (name) { | ||
if (name.match(/^(contains|notContains|equals|check|validate|assert|error|len|isNumeric|isDecimal|isFloat|regex|notRegex|is|not|notNull|isNull)$/)) { | ||
return; | ||
} | ||
if (name.match(/^(contains|notContains|equals|check|validate|assert|error|len|isNumeric|isDecimal|isFloat|regex|notRegex|is|not|notNull|isNull)$/)) { | ||
return; | ||
} | ||
Field.prototype[name] = function (message) { | ||
var args = arguments; | ||
message = message || MESSAGES[name]; | ||
Field.prototype[name] = function (message) { | ||
var args = arguments; | ||
message = message || MESSAGES[name]; | ||
return this.add(function(value) { | ||
try { | ||
ValidatorPrototype[name].apply(externalValidator.check(value, message), args); | ||
} catch (e) { | ||
return { error: e.message || e.toString() }; | ||
} | ||
return { valid: true }; | ||
}); | ||
}; | ||
return this.add(function(value) { | ||
try { | ||
ValidatorPrototype[name].apply(externalValidator.check(value, message), args); | ||
} catch (e) { | ||
return { error: e.message || e.toString() }; | ||
} | ||
return { valid: true }; | ||
}); | ||
}; | ||
}); | ||
Field.prototype.contains = function (test, message) { | ||
return this.add(function(value) { | ||
try { | ||
ValidatorPrototype.contains.call(externalValidator.check(value, message), test); | ||
} catch (e) { | ||
return { error: message || "%s does not contain required characters" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
return this.add(function(value) { | ||
try { | ||
ValidatorPrototype.contains.call(externalValidator.check(value, message), test); | ||
} catch (e) { | ||
return { error: message || "%s does not contain required characters" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
}; | ||
Field.prototype.notContains = function (test, message) { | ||
return this.add(function (value) { | ||
try { | ||
ValidatorPrototype.notContains.call(externalValidator.check(value, message), test); | ||
} catch (e) { | ||
return { error: message || "%s contains invalid characters" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
return this.add(function (value) { | ||
try { | ||
ValidatorPrototype.notContains.call(externalValidator.check(value, message), test); | ||
} catch (e) { | ||
return { error: message || "%s contains invalid characters" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
}; | ||
@@ -229,18 +230,18 @@ | ||
Field.prototype.equals = function (other, message) { | ||
if (object.isString(other) && other.match(/^field::/)) { | ||
this.__required = true; | ||
} | ||
return this.add(function (value, source) { | ||
// If other is a field token (field::fieldname), grab the value of fieldname | ||
// and use that as the OTHER value. | ||
var test = other; | ||
if (object.isString(other) && other.match(/^field::/)) { | ||
test = utils.getProp(other.replace(/^field::/, ""), source); | ||
} | ||
if (value != test) { | ||
return { error: message || "%s does not equal " + String(test) }; | ||
} | ||
return { valid: true }; | ||
}); | ||
if (object.isString(other) && other.match(/^field::/)) { | ||
this.__required = true; | ||
} | ||
return this.add(function (value, source) { | ||
// If other is a field token (field::fieldname), grab the value of fieldname | ||
// and use that as the OTHER value. | ||
var test = other; | ||
if (object.isString(other) && other.match(/^field::/)) { | ||
test = utils.getProp(other.replace(/^field::/, ""), source); | ||
} | ||
if (value != test) { | ||
return { error: message || "%s does not equal " + String(test) }; | ||
} | ||
return { valid: true }; | ||
}); | ||
}; | ||
@@ -250,9 +251,9 @@ | ||
Field.prototype.isNumeric = function (message) { | ||
return this.add(function (value) { | ||
if (object.isNumber(value) || (object.isString(value) && value.match(/^[-+]?[0-9]*\.?[0-9]+$/))) { | ||
} else { | ||
return { error: message || "%s is not a number" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
return this.add(function (value) { | ||
if (object.isNumber(value) || (object.isString(value) && value.match(/^[-+]?[0-9]*\.?[0-9]+$/))) { | ||
} else { | ||
return { error: message || "%s is not a number" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
}; | ||
@@ -262,116 +263,116 @@ | ||
Field.prototype.isFloat = Field.prototype.isDecimal = function (message) { | ||
return this.add(function (value) { | ||
if ((object.isNumber(value) && value % 1 == 0) || (object.isString(value) && value.match(/^[-+]?[0-9]*\.[0-9]+$/))) { | ||
} else { | ||
return { error: message || "%s is not a decimal" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
return this.add(function (value) { | ||
if ((object.isNumber(value) && value % 1 == 0) || (object.isString(value) && value.match(/^[-+]?[0-9]*\.[0-9]+$/))) { | ||
} else { | ||
return { error: message || "%s is not a decimal" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
}; | ||
Field.prototype.regex = Field.prototype.is = function (pattern, modifiers, message) { | ||
// regex(/pattern/) | ||
// regex(/pattern/, "message") | ||
// regex("pattern") | ||
// regex("pattern", "modifiers") | ||
// regex("pattern", "message") | ||
// regex("pattern", "modifiers", "message") | ||
if (pattern instanceof RegExp) { | ||
if (object.isString(modifiers) && modifiers.match(/^[gimy]+$/)) { | ||
throw new Error("Invalid arguments: `modifiers` can only be passed in if `pattern` is a string."); | ||
} | ||
// regex(/pattern/) | ||
// regex(/pattern/, "message") | ||
// regex("pattern") | ||
// regex("pattern", "modifiers") | ||
// regex("pattern", "message") | ||
// regex("pattern", "modifiers", "message") | ||
if (pattern instanceof RegExp) { | ||
if (object.isString(modifiers) && modifiers.match(/^[gimy]+$/)) { | ||
throw new Error("Invalid arguments: `modifiers` can only be passed in if `pattern` is a string."); | ||
} | ||
message = modifiers; | ||
modifiers = undefined; | ||
} else if (object.isString(pattern)) { | ||
if (arguments.length == 2 && !modifiers.match(/^[gimy]+$/)) { | ||
// 2nd arg doesn't look like modifier flags, it's the message (might also be undefined) | ||
message = modifiers; | ||
modifiers = undefined; | ||
} | ||
pattern = new RegExp(pattern, modifiers); | ||
} | ||
return this.add(function (value) { | ||
if (pattern.test(value) === false) { | ||
return { error: message || "%s has invalid characters" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
message = modifiers; | ||
modifiers = undefined; | ||
} else if (object.isString(pattern)) { | ||
if (arguments.length == 2 && !modifiers.match(/^[gimy]+$/)) { | ||
// 2nd arg doesn't look like modifier flags, it's the message (might also be undefined) | ||
message = modifiers; | ||
modifiers = undefined; | ||
} | ||
pattern = new RegExp(pattern, modifiers); | ||
} | ||
return this.add(function (value) { | ||
if (pattern.test(value) === false) { | ||
return { error: message || "%s has invalid characters" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
}; | ||
Field.prototype.notRegex = Field.prototype.not = function(pattern, modifiers, message) { | ||
// notRegex(/pattern/) | ||
// notRegex(/pattern/, "message") | ||
// notRegex("pattern") | ||
// notRegex("pattern", "modifiers") | ||
// notRegex("pattern", "message") | ||
// notRegex("pattern", "modifiers", "message") | ||
// notRegex(/pattern/) | ||
// notRegex(/pattern/, "message") | ||
// notRegex("pattern") | ||
// notRegex("pattern", "modifiers") | ||
// notRegex("pattern", "message") | ||
// notRegex("pattern", "modifiers", "message") | ||
if (pattern instanceof RegExp) { | ||
if (object.isString(modifiers) && modifiers.match(/^[gimy]+$/)) { | ||
throw new Error("Invalid arguments: `modifiers` can only be passed in if `pattern` is a string."); | ||
} | ||
message = modifiers; | ||
modifiers = undefined; | ||
} else if (object.isString(pattern)) { | ||
if (arguments.length == 2 && !modifiers.match(/^[gimy]+$/)) { | ||
// 2nd arg doesn't look like modifier flags, it's the message (might also be undefined) | ||
message = modifiers; | ||
modifiers = undefined; | ||
} | ||
pattern = new RegExp(pattern, modifiers); | ||
} | ||
if (pattern instanceof RegExp) { | ||
if (object.isString(modifiers) && modifiers.match(/^[gimy]+$/)) { | ||
throw new Error("Invalid arguments: `modifiers` can only be passed in if `pattern` is a string."); | ||
} | ||
message = modifiers; | ||
modifiers = undefined; | ||
} else if (object.isString(pattern)) { | ||
if (arguments.length == 2 && !modifiers.match(/^[gimy]+$/)) { | ||
// 2nd arg doesn't look like modifier flags, it's the message (might also be undefined) | ||
message = modifiers; | ||
modifiers = undefined; | ||
} | ||
pattern = new RegExp(pattern, modifiers); | ||
} | ||
return this.add(function(value) { | ||
if (pattern.test(value) === true) { | ||
return { error: message || "%s has invalid characters" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
return this.add(function(value) { | ||
if (pattern.test(value) === true) { | ||
return { error: message || "%s has invalid characters" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
}; | ||
Field.prototype.required = function (placeholderValue, message) { | ||
this.__required = true; | ||
return this.add(function (value) { | ||
if (!utils.hasValue(value) || value == placeholderValue) { | ||
return { error: message || "%s is required" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
this.__required = true; | ||
return this.add(function (value) { | ||
if (!utils.hasValue(value) || value == placeholderValue) { | ||
return { error: message || "%s is required" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
}; | ||
Field.prototype.minLength = function (length, message) { | ||
return this.add(function(value) { | ||
if (value.toString().length < length) { | ||
return { error: message || "%s is too short" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
return this.add(function(value) { | ||
if (value.toString().length < length) { | ||
return { error: message || "%s is too short" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
}; | ||
Field.prototype.maxLength = function (length, message) { | ||
return this.add(function(value) { | ||
if (value.toString().length > length) { | ||
return { error: message || "%s is too long" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
return this.add(function(value) { | ||
if (value.toString().length > length) { | ||
return { error: message || "%s is too long" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
}; | ||
Field.prototype.customValidator = function(func, message) { | ||
return this.add(function(value) { | ||
try { | ||
func(value); | ||
} catch (e) { | ||
return { error: message || e.message || "%s is invalid" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
return this.add(function(value, source) { | ||
try { | ||
func(value, source); | ||
} catch (e) { | ||
return { error: message || e.message || "%s is invalid" }; | ||
} | ||
return { valid: true }; | ||
}); | ||
}; | ||
module.exports = Field; |
190
lib/form.js
@@ -11,88 +11,88 @@ /*! | ||
function form() { | ||
var routines = Array.prototype.slice.call(arguments) | ||
, options = form._options; | ||
return function (req, res, next) { | ||
var map = {} | ||
, flashed = {} | ||
, mergedSource = {}; | ||
if (!req.form) req.form = {}; | ||
options.dataSources.forEach(function (source) { | ||
utils.merge(mergedSource, req[source]); | ||
}); | ||
if (options.passThrough) req.form = utils.clone(mergedSource); | ||
if (options.autoLocals) { | ||
for (var prop in req.body) { | ||
if (!req.body.hasOwnProperty(prop)) continue; | ||
if (typeof res.locals === "function") { // Express 2.0 Support | ||
res.local(utils.camelize(prop), req.body[prop]); | ||
} else { | ||
if (!res.locals) res.locals = {}; | ||
res.locals[utils.camelize(prop)] = req.body[prop]; | ||
} | ||
} | ||
} | ||
Object.defineProperties(req.form, { | ||
"errors": { | ||
value: [], | ||
enumerable: false | ||
}, | ||
"getErrors": { | ||
value: function (name) { | ||
if(!name) return map; | ||
return map[name] || []; | ||
}, | ||
enumerable: false | ||
}, | ||
"isValid": { | ||
get: function () { | ||
return this.errors.length === 0; | ||
}, | ||
enumerable: false | ||
}, | ||
"flashErrors": { | ||
value: function () { | ||
if (typeof req.flash !== "function") return; | ||
this.errors.forEach(function (error) { | ||
if (flashed[error]) return; | ||
flashed[error] = true; | ||
req.flash("error", error); | ||
}); | ||
}, | ||
enumerable: false | ||
} | ||
}); | ||
routines.forEach(function (routine) { | ||
var result = routine.run(mergedSource, req.form, options); | ||
if (!Array.isArray(result) || !result.length) return; | ||
var errors = req.form.errors = req.form.errors || [] | ||
, name = routine.name; | ||
map[name] = map[name] || []; | ||
result.forEach(function (error) { | ||
errors.push(error); | ||
map[name].push(error); | ||
}); | ||
}); | ||
if (options.flashErrors) req.form.flashErrors(); | ||
if (next) next(); | ||
} | ||
var routines = Array.prototype.slice.call(arguments) | ||
, options = form._options; | ||
return function (req, res, next) { | ||
var map = {} | ||
, flashed = {} | ||
, mergedSource = {}; | ||
if (!req.form) req.form = {}; | ||
options.dataSources.forEach(function (source) { | ||
utils.merge(mergedSource, req[source]); | ||
}); | ||
if (options.passThrough) req.form = utils.clone(mergedSource); | ||
if (options.autoLocals) { | ||
for (var prop in req.body) { | ||
if (!req.body.hasOwnProperty(prop)) continue; | ||
if (typeof res.locals === "function") { // Express 2.0 Support | ||
res.local(utils.camelize(prop), req.body[prop]); | ||
} else { | ||
if (!res.locals) res.locals = {}; | ||
res.locals[utils.camelize(prop)] = req.body[prop]; | ||
} | ||
} | ||
} | ||
Object.defineProperties(req.form, { | ||
"errors": { | ||
value: [], | ||
enumerable: false | ||
}, | ||
"getErrors": { | ||
value: function (name) { | ||
if(!name) return map; | ||
return map[name] || []; | ||
}, | ||
enumerable: false | ||
}, | ||
"isValid": { | ||
get: function () { | ||
return this.errors.length === 0; | ||
}, | ||
enumerable: false | ||
}, | ||
"flashErrors": { | ||
value: function () { | ||
if (typeof req.flash !== "function") return; | ||
this.errors.forEach(function (error) { | ||
if (flashed[error]) return; | ||
flashed[error] = true; | ||
req.flash("error", error); | ||
}); | ||
}, | ||
enumerable: false | ||
} | ||
}); | ||
routines.forEach(function (routine) { | ||
var result = routine.run(mergedSource, req.form, options); | ||
if (!Array.isArray(result) || !result.length) return; | ||
var errors = req.form.errors = req.form.errors || [] | ||
, name = routine.name; | ||
map[name] = map[name] || []; | ||
result.forEach(function (error) { | ||
errors.push(error); | ||
map[name].push(error); | ||
}); | ||
}); | ||
if (options.flashErrors) req.form.flashErrors(); | ||
if (next) next(); | ||
} | ||
} | ||
form.field = function (property, label) { | ||
return new Field(property, label); | ||
return new Field(property, label); | ||
}; | ||
@@ -103,19 +103,19 @@ | ||
form._options = { | ||
dataSources: ["body", "query", "params"], | ||
autoTrim: false, | ||
autoLocals: true, | ||
passThrough: false, | ||
flashErrors: true | ||
dataSources: ["body", "query", "params"], | ||
autoTrim: false, | ||
autoLocals: true, | ||
passThrough: false, | ||
flashErrors: true | ||
}; | ||
form.configure = function (options) { | ||
for (var p in options) { | ||
if (!Array.isArray(options[p]) && p === "dataSources") { | ||
options[p] = [options[p]]; | ||
} | ||
this._options[p] = options[p]; | ||
} | ||
return this; | ||
for (var p in options) { | ||
if (!Array.isArray(options[p]) && p === "dataSources") { | ||
options[p] = [options[p]]; | ||
} | ||
this._options[p] = options[p]; | ||
} | ||
return this; | ||
} | ||
module.exports = form; |
var getProp = exports.getProp = function (property, obj) { | ||
var levels = property.split("."); | ||
while (obj != null && levels[0]) { | ||
obj = obj[levels.shift()]; | ||
if (obj == null) obj = ""; | ||
} | ||
return obj; | ||
var levels = property.split("."); | ||
while (obj != null && levels[0]) { | ||
obj = obj[levels.shift()]; | ||
if (obj == null) obj = ""; | ||
} | ||
return obj; | ||
} | ||
var setProp = exports.setProp = function (property, obj, value) { | ||
var levels = property.split("."); | ||
while (levels[0]) { | ||
var p = levels.shift(); | ||
if (typeof obj[p] !== "object") obj[p] = {}; | ||
if (!levels.length) obj[p] = value; | ||
obj = obj[p]; | ||
} | ||
return obj; | ||
var levels = property.split("."); | ||
while (levels[0]) { | ||
var p = levels.shift(); | ||
if (typeof obj[p] !== "object") obj[p] = {}; | ||
if (!levels.length) obj[p] = value; | ||
obj = obj[p]; | ||
} | ||
return obj; | ||
} | ||
var clone = exports.clone = function (obj) { | ||
// Untested, probably better:-> return Object.create(obj).__proto__; | ||
return JSON.parse(JSON.stringify(obj)); | ||
// Untested, probably better:-> return Object.create(obj).__proto__; | ||
return JSON.parse(JSON.stringify(obj)); | ||
} | ||
@@ -40,5 +40,5 @@ | ||
var camelize = exports.camelize = function (str) { | ||
return (str || "").replace(/-+(.)?/g, function(match, chr) { | ||
return chr ? chr.toUpperCase() : ''; | ||
}); | ||
return (str || "").replace(/-+(.)?/g, function(match, chr) { | ||
return chr ? chr.toUpperCase() : ''; | ||
}); | ||
} | ||
@@ -51,20 +51,20 @@ | ||
var merge = exports.merge = function (obj1, obj2) { | ||
for (var p in obj2) { | ||
try { | ||
// Property in destination object set; update its value. | ||
if ( obj2[p].constructor==Object ) { | ||
obj1[p] = merge(obj1[p], obj2[p]); | ||
} else { | ||
obj1[p] = obj2[p]; | ||
} | ||
} catch (e) { | ||
// Property in destination object not set; create it and set its value. | ||
obj1[p] = obj2[p]; | ||
} | ||
} | ||
return obj1; | ||
for (var p in obj2) { | ||
try { | ||
// Property in destination object set; update its value. | ||
if ( obj2[p].constructor==Object ) { | ||
obj1[p] = merge(obj1[p], obj2[p]); | ||
} else { | ||
obj1[p] = obj2[p]; | ||
} | ||
} catch (e) { | ||
// Property in destination object not set; create it and set its value. | ||
obj1[p] = obj2[p]; | ||
} | ||
} | ||
return obj1; | ||
} | ||
var hasValue = exports.hasValue = function (value) { | ||
return !(undefined === value || null === value || "" === value); | ||
return !(undefined === value || null === value || "" === value); | ||
} |
@@ -5,3 +5,3 @@ { | ||
"description": "Form validation and data filtering for Express", | ||
"version": "0.6.0", | ||
"version": "0.6.1", | ||
"homepage": "http://dandean.github.com/express-form", | ||
@@ -8,0 +8,0 @@ "repository": { |
@@ -6,195 +6,195 @@ var assert = require("assert") | ||
module.exports = { | ||
"field : arrays": function () { | ||
// Array transformations. | ||
var request = { | ||
body: { | ||
field1: "", | ||
field2: "Hello!", | ||
field3: ["Alpacas?", "Llamas!!?", "Vicunas!", "Guanacos!!!"] | ||
} | ||
}; | ||
form( | ||
field("fieldx").array(), | ||
field("field1").array(), | ||
field("field2").array(), | ||
field("field3").array() | ||
)(request, {}); | ||
assert.strictEqual(Array.isArray(request.form.fieldx), true); | ||
assert.strictEqual(request.form.fieldx.length, 0); | ||
assert.strictEqual(Array.isArray(request.form.field1), true); | ||
assert.strictEqual(request.form.field1.length, 0); | ||
assert.strictEqual(request.form.field2[0], "Hello!"); | ||
assert.strictEqual(request.form.field2.length, 1); | ||
assert.strictEqual(request.form.field3[0], "Alpacas?"); | ||
assert.strictEqual(request.form.field3[1], "Llamas!!?"); | ||
assert.strictEqual(request.form.field3[2], "Vicunas!"); | ||
assert.strictEqual(request.form.field3[3], "Guanacos!!!"); | ||
assert.strictEqual(request.form.field3.length, 4); | ||
// No array flag! | ||
var request = { body: { field: ["red", "blue"] } }; | ||
form(field("field"))(request, {}); | ||
assert.strictEqual(request.form.field, "red"); | ||
// Iterate and filter array. | ||
var request = { body: { field: ["david", "stephen", "greg"] } }; | ||
form(field("field").array().toUpper())(request, {}); | ||
assert.strictEqual(request.form.field[0], "DAVID"); | ||
assert.strictEqual(request.form.field[1], "STEPHEN"); | ||
assert.strictEqual(request.form.field[2], "GREG"); | ||
assert.strictEqual(request.form.field.length, 3); | ||
// Iterate and validate array | ||
var request = { body: { field: [1, 2, "f"] } }; | ||
form(field("field").array().isInt())(request, {}); | ||
assert.equal(request.form.errors.length, 1); | ||
assert.equal(request.form.errors[0], "field is not an integer"); | ||
}, | ||
"field : nesting": function () { | ||
// Nesting with dot notation | ||
var request = { | ||
body: { | ||
field: { | ||
nest: "wow", | ||
child: "4", | ||
gb: { | ||
a: "a", | ||
b: "aaaa", | ||
c: { | ||
fruit: "deeper", | ||
must: { | ||
go: "deeperrrr" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
form( | ||
field("field.nest").toUpper(), | ||
field("field.child").toUpper(), | ||
field("field.gb.a").toUpper(), | ||
field("field.gb.b").toUpper(), | ||
field("field.gb.c.fruit").toUpper(), | ||
field("field.gb.c.must.go").toUpper() | ||
)(request, {}); | ||
assert.strictEqual(request.form.field.nest, "WOW"); | ||
assert.strictEqual(request.form.field.child, "4"); | ||
assert.strictEqual(request.form.field.gb.a, "A"); | ||
assert.strictEqual(request.form.field.gb.b, "AAAA"); | ||
assert.strictEqual(request.form.field.gb.c.fruit, "DEEPER"); | ||
assert.strictEqual(request.form.field.gb.c.must.go, "DEEPERRRR"); | ||
// Nesting with square-bracket notation | ||
var request = { | ||
body: { | ||
field: { | ||
nest: "wow", | ||
child: "4", | ||
gb: { | ||
a: "a", | ||
b: "aaaa", | ||
c: { | ||
fruit: "deeper", | ||
must: { | ||
go: "deeperrrr" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
form( | ||
field("field[nest]").toUpper(), | ||
field("field[child]").toUpper(), | ||
field("field[gb][a]").toUpper(), | ||
field("field[gb][b]").toUpper(), | ||
field("field[gb][c][fruit]").toUpper(), | ||
field("field[gb][c][must][go]").toUpper() | ||
)(request, {}); | ||
assert.strictEqual(request.form.field.nest, "WOW"); | ||
assert.strictEqual(request.form.field.child, "4"); | ||
assert.strictEqual(request.form.field.gb.a, "A"); | ||
assert.strictEqual(request.form.field.gb.b, "AAAA"); | ||
assert.strictEqual(request.form.field.gb.c.fruit, "DEEPER"); | ||
assert.strictEqual(request.form.field.gb.c.must.go, "DEEPERRRR"); | ||
}, | ||
"field : filter/validate combo ordering": function () { | ||
// Can arrange filter and validate procs in any order. | ||
var request = { | ||
body: { | ||
field1: " whatever ", | ||
field2: " some thing " | ||
} | ||
}; | ||
form( | ||
field("field1").trim().toUpper().maxLength(5), | ||
field("field2").minLength(12).trim() | ||
)(request, {}); | ||
assert.strictEqual(request.form.field1, "WHATEVER"); | ||
assert.strictEqual(request.form.field2, "some thing"); | ||
assert.equal(request.form.errors.length, 1); | ||
assert.equal(request.form.errors[0], "field1 is too long"); | ||
}, | ||
"field : autoTrim": function () { | ||
// Auto-trim declared fields. | ||
form.configure({ autoTrim: true }); | ||
var request = { body: { field: " whatever " } }; | ||
form(field("field"))(request, {}); | ||
assert.strictEqual(request.form.field, "whatever"); | ||
form.configure({ autoTrim: false }); | ||
}, | ||
"field : passThrough": function () { | ||
// request.form gets all values from sources. | ||
form.configure({ passThrough: true }); | ||
var request = { | ||
body: { | ||
field1: "fdsa", | ||
field2: "asdf" | ||
} | ||
}; | ||
form(field("field1"))(request, {}); | ||
assert.strictEqual(request.form.field1, "fdsa"); | ||
assert.strictEqual(request.form.field2, "asdf"); | ||
// request.form only gets declared fields. | ||
form.configure({ passThrough: false }); | ||
var request = { body: { | ||
field1: "fdsa", | ||
field2: "asdf" | ||
} }; | ||
form(field("field1"))(request, {}); | ||
assert.strictEqual(request.form.field1, "fdsa"); | ||
assert.strictEqual(typeof request.form.field2, "undefined"); | ||
}, | ||
"form : getErrors() gives full map": function() { | ||
var request = { | ||
body: { | ||
field0: "win", | ||
field1: "fail", | ||
field2: "fail", | ||
field3: "fail" | ||
} | ||
}; | ||
form( | ||
field("field0").equals("win"), | ||
field("field1").isEmail(), | ||
field("field2").isEmail().isUrl(), | ||
field("field3").isEmail().isUrl().isIP() | ||
)(request, {}); | ||
assert.equal(request.form.isValid, false); | ||
assert.equal(request.form.errors.length, 6); | ||
assert.equal(typeof request.form.getErrors().field0, "undefined"); | ||
assert.equal(request.form.getErrors().field1.length, 1); | ||
assert.equal(request.form.getErrors().field2.length, 2); | ||
assert.equal(request.form.getErrors().field3.length, 3); | ||
} | ||
"field : arrays": function () { | ||
// Array transformations. | ||
var request = { | ||
body: { | ||
field1: "", | ||
field2: "Hello!", | ||
field3: ["Alpacas?", "Llamas!!?", "Vicunas!", "Guanacos!!!"] | ||
} | ||
}; | ||
form( | ||
field("fieldx").array(), | ||
field("field1").array(), | ||
field("field2").array(), | ||
field("field3").array() | ||
)(request, {}); | ||
assert.strictEqual(Array.isArray(request.form.fieldx), true); | ||
assert.strictEqual(request.form.fieldx.length, 0); | ||
assert.strictEqual(Array.isArray(request.form.field1), true); | ||
assert.strictEqual(request.form.field1.length, 0); | ||
assert.strictEqual(request.form.field2[0], "Hello!"); | ||
assert.strictEqual(request.form.field2.length, 1); | ||
assert.strictEqual(request.form.field3[0], "Alpacas?"); | ||
assert.strictEqual(request.form.field3[1], "Llamas!!?"); | ||
assert.strictEqual(request.form.field3[2], "Vicunas!"); | ||
assert.strictEqual(request.form.field3[3], "Guanacos!!!"); | ||
assert.strictEqual(request.form.field3.length, 4); | ||
// No array flag! | ||
var request = { body: { field: ["red", "blue"] } }; | ||
form(field("field"))(request, {}); | ||
assert.strictEqual(request.form.field, "red"); | ||
// Iterate and filter array. | ||
var request = { body: { field: ["david", "stephen", "greg"] } }; | ||
form(field("field").array().toUpper())(request, {}); | ||
assert.strictEqual(request.form.field[0], "DAVID"); | ||
assert.strictEqual(request.form.field[1], "STEPHEN"); | ||
assert.strictEqual(request.form.field[2], "GREG"); | ||
assert.strictEqual(request.form.field.length, 3); | ||
// Iterate and validate array | ||
var request = { body: { field: [1, 2, "f"] } }; | ||
form(field("field").array().isInt())(request, {}); | ||
assert.equal(request.form.errors.length, 1); | ||
assert.equal(request.form.errors[0], "field is not an integer"); | ||
}, | ||
"field : nesting": function () { | ||
// Nesting with dot notation | ||
var request = { | ||
body: { | ||
field: { | ||
nest: "wow", | ||
child: "4", | ||
gb: { | ||
a: "a", | ||
b: "aaaa", | ||
c: { | ||
fruit: "deeper", | ||
must: { | ||
go: "deeperrrr" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
form( | ||
field("field.nest").toUpper(), | ||
field("field.child").toUpper(), | ||
field("field.gb.a").toUpper(), | ||
field("field.gb.b").toUpper(), | ||
field("field.gb.c.fruit").toUpper(), | ||
field("field.gb.c.must.go").toUpper() | ||
)(request, {}); | ||
assert.strictEqual(request.form.field.nest, "WOW"); | ||
assert.strictEqual(request.form.field.child, "4"); | ||
assert.strictEqual(request.form.field.gb.a, "A"); | ||
assert.strictEqual(request.form.field.gb.b, "AAAA"); | ||
assert.strictEqual(request.form.field.gb.c.fruit, "DEEPER"); | ||
assert.strictEqual(request.form.field.gb.c.must.go, "DEEPERRRR"); | ||
// Nesting with square-bracket notation | ||
var request = { | ||
body: { | ||
field: { | ||
nest: "wow", | ||
child: "4", | ||
gb: { | ||
a: "a", | ||
b: "aaaa", | ||
c: { | ||
fruit: "deeper", | ||
must: { | ||
go: "deeperrrr" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
form( | ||
field("field[nest]").toUpper(), | ||
field("field[child]").toUpper(), | ||
field("field[gb][a]").toUpper(), | ||
field("field[gb][b]").toUpper(), | ||
field("field[gb][c][fruit]").toUpper(), | ||
field("field[gb][c][must][go]").toUpper() | ||
)(request, {}); | ||
assert.strictEqual(request.form.field.nest, "WOW"); | ||
assert.strictEqual(request.form.field.child, "4"); | ||
assert.strictEqual(request.form.field.gb.a, "A"); | ||
assert.strictEqual(request.form.field.gb.b, "AAAA"); | ||
assert.strictEqual(request.form.field.gb.c.fruit, "DEEPER"); | ||
assert.strictEqual(request.form.field.gb.c.must.go, "DEEPERRRR"); | ||
}, | ||
"field : filter/validate combo ordering": function () { | ||
// Can arrange filter and validate procs in any order. | ||
var request = { | ||
body: { | ||
field1: " whatever ", | ||
field2: " some thing " | ||
} | ||
}; | ||
form( | ||
field("field1").trim().toUpper().maxLength(5), | ||
field("field2").minLength(12).trim() | ||
)(request, {}); | ||
assert.strictEqual(request.form.field1, "WHATEVER"); | ||
assert.strictEqual(request.form.field2, "some thing"); | ||
assert.equal(request.form.errors.length, 1); | ||
assert.equal(request.form.errors[0], "field1 is too long"); | ||
}, | ||
"field : autoTrim": function () { | ||
// Auto-trim declared fields. | ||
form.configure({ autoTrim: true }); | ||
var request = { body: { field: " whatever " } }; | ||
form(field("field"))(request, {}); | ||
assert.strictEqual(request.form.field, "whatever"); | ||
form.configure({ autoTrim: false }); | ||
}, | ||
"field : passThrough": function () { | ||
// request.form gets all values from sources. | ||
form.configure({ passThrough: true }); | ||
var request = { | ||
body: { | ||
field1: "fdsa", | ||
field2: "asdf" | ||
} | ||
}; | ||
form(field("field1"))(request, {}); | ||
assert.strictEqual(request.form.field1, "fdsa"); | ||
assert.strictEqual(request.form.field2, "asdf"); | ||
// request.form only gets declared fields. | ||
form.configure({ passThrough: false }); | ||
var request = { body: { | ||
field1: "fdsa", | ||
field2: "asdf" | ||
} }; | ||
form(field("field1"))(request, {}); | ||
assert.strictEqual(request.form.field1, "fdsa"); | ||
assert.strictEqual(typeof request.form.field2, "undefined"); | ||
}, | ||
"form : getErrors() gives full map": function() { | ||
var request = { | ||
body: { | ||
field0: "win", | ||
field1: "fail", | ||
field2: "fail", | ||
field3: "fail" | ||
} | ||
}; | ||
form( | ||
field("field0").equals("win"), | ||
field("field1").isEmail(), | ||
field("field2").isEmail().isUrl(), | ||
field("field3").isEmail().isUrl().isIP() | ||
)(request, {}); | ||
assert.equal(request.form.isValid, false); | ||
assert.equal(request.form.errors.length, 6); | ||
assert.equal(typeof request.form.getErrors().field0, "undefined"); | ||
assert.equal(request.form.getErrors().field1.length, 1); | ||
assert.equal(request.form.getErrors().field2.length, 2); | ||
assert.equal(request.form.getErrors().field3.length, 3); | ||
} | ||
} |
@@ -483,4 +483,6 @@ var assert = require("assert"), | ||
'validation : custom': function() { | ||
var request; | ||
// Failure. | ||
var request = { body: { field: "value" }}; | ||
request = { body: { field: "value" }}; | ||
form(validate("field").custom(function(value) { | ||
@@ -493,3 +495,3 @@ throw new Error(); | ||
// Failure w/ custom message. | ||
var request = { body: { field: "value" }}; | ||
request = { body: { field: "value" }}; | ||
form(validate("field").custom(function(value) { | ||
@@ -502,3 +504,3 @@ throw new Error(); | ||
// Failure w/ custom message from internal error. | ||
var request = { body: { field: "value" }}; | ||
request = { body: { field: "value" }}; | ||
form(validate("field").custom(function(value) { | ||
@@ -509,7 +511,16 @@ throw new Error("Radical %s"); | ||
assert.equal(request.form.errors[0], "Radical field"); | ||
// Success | ||
var request = { body: { field: "value" }}; | ||
form(validate("field").custom(function(validate) {}))(request, {}); | ||
request = { body: { field: "value" }}; | ||
form(validate("field").custom(function(value) {}))(request, {}); | ||
assert.equal(request.form.errors.length, 0); | ||
// Pass form data as 2nd argument to custom validators | ||
request = { body: { field1: "value1", field2: "value2" }}; | ||
form(validate("field1").custom(function(value, formData) { | ||
assert.equal("value1", value); | ||
assert.ok(formData); | ||
assert.equal("value1", formData.field1); | ||
assert.equal("value2", formData.field2); | ||
}))(request, {}); | ||
}, | ||
@@ -516,0 +527,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
170208
34
3930
4
4