Comparing version 0.4.1 to 0.5.0
@@ -0,1 +1,6 @@ | ||
0.5.0 / 2014-05-01 | ||
================== | ||
* Added new form-level validatePastFirstErrorOption. When true, all fields will validate, instead of stopping at the first error. | ||
* Internal refactoring for improved HTML tag generation | ||
0.4.1 / 2014-04-24 | ||
@@ -2,0 +7,0 @@ ================== |
@@ -5,6 +5,6 @@ /*jslint node: true */ | ||
var forms = require('./forms'), | ||
tag = require('./tag'), | ||
is = require('is'), | ||
async = require('async'), | ||
validators = require('./validators'), | ||
htmlEscape = require('./htmlEscape'), | ||
coerceArray = function (arr) { | ||
@@ -72,3 +72,3 @@ return Array.isArray(arr) && arr.length > 0 ? arr : []; | ||
var classes = typeof this.cssClasses !== 'undefined' ? coerceArray(this.cssClasses.error) : []; | ||
return this.error ? '<p class="' + htmlEscape(['error_msg'].concat(classes).join(' ')) + '">' + this.error + '</p>' : ''; | ||
return this.error ? tag('p', { classes: ['error_msg'].concat(classes) }, this.error) : ''; | ||
}; | ||
@@ -75,0 +75,0 @@ f.labelText = function (name) { |
@@ -5,3 +5,3 @@ /*jslint node: true */ | ||
var async = require('async'), | ||
is = require('is'), | ||
is = require('is'), | ||
http = require('http'), | ||
@@ -18,3 +18,7 @@ querystring = require('qs'), | ||
exports.create = function (fields) { | ||
exports.create = function (fields, opts) { | ||
if (!opts) { opts = {}; } | ||
var validatePastFirstError = !!opts.validatePastFirstError; | ||
Object.keys(fields).forEach(function (k) { | ||
@@ -51,3 +55,3 @@ // if it's not a field object, create an object field. | ||
b.fields[k] = bound_field; | ||
callback(err); | ||
callback(validatePastFirstError ? null : err); | ||
}); | ||
@@ -82,3 +86,3 @@ }, function (err) { | ||
if (err) { throw err; } | ||
fields = querystring.parse(querystring.stringify(fields)); | ||
fields = querystring.parse(querystring.stringify(fields)); | ||
f.handle(fields, callbacks); | ||
@@ -85,0 +89,0 @@ }); |
/*jslint node: true */ | ||
'use strict'; | ||
var htmlEscape = require('./htmlEscape'); | ||
var wrapWith = function (tag) { | ||
var tag = require('./tag'); | ||
var wrapWith = function (tagName) { | ||
return function (name, field, opt) { | ||
if (!opt) { opt = {}; } | ||
var html = ['<' + tag + ' class="' + htmlEscape(field.classes().join(' ')) + '">'], | ||
errorHTML = field.errorHTML(); | ||
var wrappedContent = []; | ||
var errorHTML = field.errorHTML(); | ||
if (field.widget.type === 'multipleCheckbox' || field.widget.type === 'multipleRadio') { | ||
html = html.concat([ | ||
'<fieldset>', | ||
'<legend>', field.labelText(name), '</legend>', | ||
var fieldset = tag('fieldset', {}, [ | ||
tag('legend', {}, field.labelText(name)), | ||
opt.errorAfterField ? '' : errorHTML, | ||
field.widget.toHTML(name, field), | ||
opt.errorAfterField ? errorHTML : '', | ||
'</fieldset>' | ||
]); | ||
opt.errorAfterField ? errorHTML : '' | ||
].join('')); | ||
wrappedContent.push(fieldset); | ||
} else { | ||
html = html.concat([ | ||
opt.errorAfterField ? '' : errorHTML, | ||
field.labelHTML(name, field.id), | ||
field.widget.toHTML(name, field), | ||
opt.errorAfterField ? errorHTML : '', | ||
]); | ||
var fieldHTMLs = [field.labelHTML(name, field.id), field.widget.toHTML(name, field)]; | ||
if (opt.errorAfterField) { fieldHTMLs.push(errorHTML); } | ||
else { fieldHTMLs.unshift(errorHTML); } | ||
wrappedContent = wrappedContent.concat(fieldHTMLs); | ||
} | ||
return html.join('') + '</' + tag + '>'; | ||
return tag(tagName, { | ||
classes: field.classes() | ||
}, wrappedContent.join('')); | ||
}; | ||
@@ -36,14 +35,13 @@ }; | ||
if (!opt) { opt = {}; } | ||
var th = tag('th', {}, field.labelHTML(name, field.id)); | ||
var tdContent = field.widget.toHTML(name, field); | ||
var errorHTML = field.errorHTML(); | ||
return [ | ||
'<tr class="', htmlEscape(field.classes().join(' ')), '">', | ||
'<th>', field.labelHTML(name, field.id), '</th>', | ||
'<td>', | ||
opt.errorAfterField ? '' : errorHTML, | ||
field.widget.toHTML(name, field), | ||
opt.errorAfterField ? errorHTML : '', | ||
'</td>', | ||
'</tr>' | ||
].join(''); | ||
if (opt.errorAfterField) { tdContent += errorHTML; } | ||
else { tdContent = errorHTML + tdContent; } | ||
var td = tag('td', {}, tdContent); | ||
return tag('tr', { classes: field.classes() }, th + td); | ||
}; | ||
/*jslint node: true */ | ||
'use strict'; | ||
var htmlEscape = require('./htmlEscape'); | ||
var is = require('is'); | ||
var tag = require('./tag'); | ||
// generates a string for common widget attributes | ||
var attrs = function (a) { | ||
if (typeof a.id === 'boolean') { | ||
a.id = a.id ? 'id_' + a.name : null; | ||
} | ||
if (Array.isArray(a.classes) && a.classes.length > 0) { | ||
a['class'] = a.classes.join(' '); | ||
} | ||
a.classes = null; | ||
var pairs = []; | ||
Object.keys(a).map(function (field) { | ||
var value = a[field]; | ||
if (typeof value === 'boolean') { | ||
value = value ? field : null; | ||
} else if (typeof value === 'string' && value.length === 0) { | ||
value = null; | ||
} else if (typeof value === 'number' && isNaN(value)) { | ||
value = null; | ||
} | ||
if (typeof value !== 'undefined' && value !== null) { | ||
pairs.push(htmlEscape(field) + '="' + htmlEscape(value) + '"'); | ||
} | ||
}); | ||
return pairs.length > 0 ? ' ' + pairs.join(' ') : ''; | ||
}; | ||
var tag = function tag(tagName, attrsMap, content) { | ||
tagName = htmlEscape(tagName); | ||
var attrsHTML = !Array.isArray(attrsMap) ? attrs(attrsMap) : attrsMap.reduce(function (html, attrsMap) { | ||
return html + attrs(attrsMap); | ||
}, ''); | ||
return '<' + tagName + attrsHTML + '>' + content + '</' + tagName + '>'; | ||
}; | ||
var singleTag = function tag(tagName, attrsMap) { | ||
tagName = htmlEscape(tagName); | ||
var attrsHTML = !Array.isArray(attrsMap) ? attrs(attrsMap) : attrsMap.reduce(function (html, attrsMap) { | ||
return html + attrs(attrsMap); | ||
}, ''); | ||
return '<' + tagName + attrsHTML + ' />'; | ||
}; | ||
// used to generate different input elements varying only by type attribute | ||
var input = function (type) { | ||
var dataRegExp = /^data-[a-z]+$/, | ||
var dataRegExp = /^data-[a-z]+([-][a-z]+)*$/, | ||
ariaRegExp = /^aria-[a-z]+$/, | ||
@@ -74,3 +33,3 @@ legalAttrs = ['autocomplete', 'autocorrect', 'autofocus', 'autosuggest', 'checked', 'dirname', 'disabled', 'tabindex', 'list', 'max', 'maxlength', 'min', 'multiple', 'novalidate', 'pattern', 'placeholder', 'readonly', 'required', 'size', 'step'], | ||
if (!f) { f = {}; } | ||
return singleTag('input', [{ | ||
return tag('input', [{ | ||
type: type, | ||
@@ -135,3 +94,3 @@ name: name, | ||
if (!f) { f = {}; } | ||
return singleTag('input', [{ | ||
return tag('input', [{ | ||
type: 'checkbox', | ||
@@ -203,3 +162,3 @@ name: name, | ||
html += singleTag('input', [{ | ||
html += tag('input', [{ | ||
type: 'checkbox', | ||
@@ -249,3 +208,3 @@ name: name, | ||
html += singleTag('input', [{ | ||
html += tag('input', [{ | ||
type: 'radio', | ||
@@ -252,0 +211,0 @@ name: name, |
@@ -6,3 +6,3 @@ { | ||
"author": "Caolan McMahon", | ||
"version": "0.4.1", | ||
"version": "0.5.0", | ||
"repository": { | ||
@@ -22,3 +22,3 @@ "type": "git", | ||
"dependencies": { | ||
"async": "~0.7.0", | ||
"async": "~0.8.0", | ||
"qs": "~0.6.5", | ||
@@ -25,0 +25,0 @@ "formidable": "~1.0.14", |
@@ -210,3 +210,10 @@ # Forms <sup>[![Version Badge][9]][8]</sup> | ||
#### forms.create(fields, options) | ||
Forms can be created with an optional "options" object as well. | ||
#### Supported options: | ||
* `validatePastFirstError`: `true`, otherwise assumes `false` | ||
* If `false`, the first validation error will halt form validation. | ||
* If `true`, all fields will be validated. | ||
### Form object | ||
@@ -213,0 +220,0 @@ |
@@ -332,2 +332,40 @@ /*jslint node: true */ | ||
test('validation stops on first error', function (t) { | ||
t.plan(3); | ||
var f = forms.create({ | ||
field1: forms.fields.string({ required: true }), | ||
field2: forms.fields.string({ required: true }), | ||
field3: forms.fields.string({ required: true }) | ||
}); | ||
f.handle({ field1: 'test' }, { | ||
error: function(form) { | ||
t.equal(form.fields.field1.error, undefined); | ||
t.equal(form.fields.field2.error, 'field2 is required.'); | ||
t.equal(form.fields.field3.error, undefined); | ||
t.end(); | ||
} | ||
}); | ||
}); | ||
test('validates past first error with validatePastFirstError option', function (t) { | ||
t.plan(3); | ||
var f = forms.create({ | ||
field1: forms.fields.string({ required: true }), | ||
field2: forms.fields.string({ required: true }), | ||
field3: forms.fields.string({ required: true }) | ||
}, { | ||
validatePastFirstError: true | ||
}); | ||
f.handle({ field1: 'test' }, { | ||
error: function(form) { | ||
t.equal(form.fields.field1.error, undefined); | ||
t.equal(form.fields.field2.error, 'field2 is required.'); | ||
t.equal(form.fields.field3.error, 'field3 is required.'); | ||
t.end(); | ||
} | ||
}); | ||
}); | ||
test('handle ServerRequest POST with bodyDecoder', function (t) { | ||
@@ -334,0 +372,0 @@ t.plan(1); |
@@ -183,14 +183,14 @@ /*jslint node: true */ | ||
t.plan(4); | ||
validators.url(false, 'URL was invalid.')('form', {data: 'asdf.com'}, function (err) { | ||
t.equal(err, 'URL was invalid.'); | ||
validators.url()('form', {data: 'http://asdf.com'}, function (err) { | ||
t.equal(err, undefined); | ||
}); | ||
}); | ||
validators.url(true)('form', {data: 'localhost/test.html'}, function (err) { | ||
t.equal(err, 'Please enter a valid URL.'); | ||
validators.url(true)('form', {data: 'http://localhost/test.html'}, function (err) { | ||
t.equal(err, undefined); | ||
}); | ||
}); | ||
validators.url(false, 'URL was invalid.')('form', {data: 'asdf.com'}, function (err) { | ||
t.equal(err, 'URL was invalid.'); | ||
validators.url()('form', {data: 'http://asdf.com'}, function (err) { | ||
t.equal(err, undefined); | ||
}); | ||
}); | ||
validators.url(true)('form', {data: 'localhost/test.html'}, function (err) { | ||
t.equal(err, 'Please enter a valid URL.'); | ||
validators.url(true)('form', {data: 'http://localhost/test.html'}, function (err) { | ||
t.equal(err, undefined); | ||
}); | ||
}); | ||
t.end(); | ||
@@ -201,14 +201,14 @@ }); | ||
t.plan(4); | ||
validators.date('Date input must contain a valid date.')('form', {data: '02/28/2012'}, function (err) { | ||
t.equal(err, 'Date input must contain a valid date.'); | ||
validators.date()('form', {data: '2012-02-28'}, function (err) { | ||
t.equal(err, undefined); | ||
}); | ||
}); | ||
validators.date()('form', {data: '2012.02.30'}, function (err) { | ||
t.equal(err, 'Inputs of type "date" must be valid dates in the format "yyyy-mm-dd"'); | ||
validators.date()('form', {data: '2012-02-30'}, function (err) { | ||
t.equal(err, undefined); | ||
}); | ||
}); | ||
validators.date('Date input must contain a valid date.')('form', {data: '02/28/2012'}, function (err) { | ||
t.equal(err, 'Date input must contain a valid date.'); | ||
validators.date()('form', {data: '2012-02-28'}, function (err) { | ||
t.equal(err, undefined); | ||
}); | ||
}); | ||
validators.date()('form', {data: '2012.02.30'}, function (err) { | ||
t.equal(err, 'Inputs of type "date" must be valid dates in the format "yyyy-mm-dd"'); | ||
validators.date()('form', {data: '2012-02-30'}, function (err) { | ||
t.equal(err, undefined); | ||
}); | ||
}); | ||
t.end(); | ||
@@ -241,14 +241,14 @@ }); | ||
t.plan(4); | ||
validators.rangelength(2, 4, 'Enter between %s and %s characters.')('form', {data: '12345'}, function (err) { | ||
t.equal(err, 'Enter between 2 and 4 characters.'); | ||
}); | ||
validators.rangelength(2, 4)('form', {data: '1'}, function (err) { | ||
t.equal(err, 'Please enter a value between 2 and 4 characters long.'); | ||
}); | ||
validators.rangelength(2, 4)('form', {data: '12'}, function (err) { | ||
t.equal(err, undefined); | ||
}); | ||
validators.rangelength(2, 4)('form', {data: '1234'}, function (err) { | ||
t.equal(err, undefined); | ||
}); | ||
validators.rangelength(2, 4, 'Enter between %s and %s characters.')('form', {data: '12345'}, function (err) { | ||
t.equal(err, 'Enter between 2 and 4 characters.'); | ||
}); | ||
validators.rangelength(2, 4)('form', {data: '1'}, function (err) { | ||
t.equal(err, 'Please enter a value between 2 and 4 characters long.'); | ||
}); | ||
validators.rangelength(2, 4)('form', {data: '12'}, function (err) { | ||
t.equal(err, undefined); | ||
}); | ||
validators.rangelength(2, 4)('form', {data: '1234'}, function (err) { | ||
t.equal(err, undefined); | ||
}); | ||
t.end(); | ||
@@ -255,0 +255,0 @@ }); |
@@ -296,2 +296,4 @@ /*jslint node: true */ | ||
t.equal(re.test('data_input'), false); | ||
t.equal(re.test('data--'), false); | ||
t.equal(re.test('data-foo-bar'), true); | ||
t.end(); | ||
@@ -298,0 +300,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
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
141019
27
3454
366
+ Addedasync@0.8.0(transitive)
- Removedasync@0.7.0(transitive)
Updatedasync@~0.8.0