├── doc
├── notes.txt
├── public
│ ├── fonts
│ │ ├── aller-bold.eot
│ │ ├── aller-bold.ttf
│ │ ├── aller-bold.woff
│ │ ├── aller-light.eot
│ │ ├── aller-light.ttf
│ │ ├── aller-light.woff
│ │ ├── roboto-black.eot
│ │ ├── roboto-black.ttf
│ │ ├── novecento-bold.eot
│ │ ├── novecento-bold.ttf
│ │ ├── roboto-black.woff
│ │ └── novecento-bold.woff
│ └── stylesheets
│ │ └── normalize.css
├── TODO.txt
├── examples
│ ├── simple.js
│ ├── http_request_options.js
│ └── range.js
└── docco.css
├── .travis.yml
├── test.sh
├── .gitignore
├── test
├── circle.js
├── jasmine-1.3.1
│ ├── MIT.LICENSE
│ ├── jasmine.css
│ └── jasmine-html.js
├── string-rulespec.js
├── jasmine.html
├── array.js
├── custom.js
├── default.js
├── ownparams.js
├── wildcard.js
├── format.js
├── run-jasmine.js
├── type.js
├── basic.js
├── parambulator-multi.spec.js
└── value.js
├── LICENSE.txt
├── package.json
├── parambulator-min.js
├── parambulator-min.map
├── README.md
└── parambulator.js
/doc/notes.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 6
4 | - 5
5 | - 4
6 | - 0.12
7 | - 0.10
8 |
--------------------------------------------------------------------------------
/doc/public/fonts/aller-bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjrodger/parambulator/HEAD/doc/public/fonts/aller-bold.eot
--------------------------------------------------------------------------------
/doc/public/fonts/aller-bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjrodger/parambulator/HEAD/doc/public/fonts/aller-bold.ttf
--------------------------------------------------------------------------------
/doc/public/fonts/aller-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjrodger/parambulator/HEAD/doc/public/fonts/aller-bold.woff
--------------------------------------------------------------------------------
/doc/public/fonts/aller-light.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjrodger/parambulator/HEAD/doc/public/fonts/aller-light.eot
--------------------------------------------------------------------------------
/doc/public/fonts/aller-light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjrodger/parambulator/HEAD/doc/public/fonts/aller-light.ttf
--------------------------------------------------------------------------------
/doc/public/fonts/aller-light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjrodger/parambulator/HEAD/doc/public/fonts/aller-light.woff
--------------------------------------------------------------------------------
/doc/public/fonts/roboto-black.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjrodger/parambulator/HEAD/doc/public/fonts/roboto-black.eot
--------------------------------------------------------------------------------
/doc/public/fonts/roboto-black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjrodger/parambulator/HEAD/doc/public/fonts/roboto-black.ttf
--------------------------------------------------------------------------------
/doc/public/fonts/novecento-bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjrodger/parambulator/HEAD/doc/public/fonts/novecento-bold.eot
--------------------------------------------------------------------------------
/doc/public/fonts/novecento-bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjrodger/parambulator/HEAD/doc/public/fonts/novecento-bold.ttf
--------------------------------------------------------------------------------
/doc/public/fonts/roboto-black.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjrodger/parambulator/HEAD/doc/public/fonts/roboto-black.woff
--------------------------------------------------------------------------------
/doc/public/fonts/novecento-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rjrodger/parambulator/HEAD/doc/public/fonts/novecento-bold.woff
--------------------------------------------------------------------------------
/test.sh:
--------------------------------------------------------------------------------
1 | ./node_modules/.bin/jasmine-node ./test
2 | ./node_modules/.bin/phantomjs test/run-jasmine.js test/jasmine.html
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | .DS_Store
3 |
4 | lib-cov
5 | *.seed
6 | *.log
7 | *.csv
8 | *.dat
9 | *.out
10 | *.pid
11 | *.gz
12 |
13 | pids
14 | logs
15 | results
16 |
17 | node_modules
18 | npm-debug.log
19 |
20 | README.html
21 |
--------------------------------------------------------------------------------
/test/circle.js:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2010-2013 Richard Rodger */
2 |
3 | "use strict";
4 |
5 |
6 | var assert = require('chai').assert
7 | var gex = require('gex')
8 |
9 | var parambulator = require('..')
10 |
11 |
12 | describe('circle', function() {
13 |
14 | var pb
15 |
16 | it('circle', function() {
17 | pb = parambulator({
18 | string$: ['foo']
19 | }, {msgs: {
20 | 'string$': 'circle: <%=json(point)%>'
21 | }})
22 |
23 |
24 | var a = {}
25 | a.foo = a
26 |
27 | var res = pb.validate(a)
28 | assert.equal('circle: {"foo":"[CIRCULAR-REFERENCE]"}',res.message)
29 | })
30 |
31 | })
32 |
33 |
34 |
--------------------------------------------------------------------------------
/doc/TODO.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 | -- post 0.1
4 |
5 | - validate call should be optional - most common use case is non-async - need to be able to throw an exception
6 |
7 | - ctxt.util.proplist not good name - actually gets single or list of anything
8 |
9 | - test/document validate return value if no async calls
10 | - tests for type child rules
11 | - test for only$ rule
12 | - how to support required$: true
13 | - default$ rule
14 | - function$ rule - applies function as rule
15 | - deeper self-vaidation
16 | - list$, prop$ validation, error handling
17 | - http request example - agent - type should accept array (OR semantics)
18 | - check for truth failures when gex returns a 0 or '' - wild$ rule
19 | - extend docs for custom rules
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2010 Richard Rodger
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/test/jasmine-1.3.1/MIT.LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2008-2011 Pivotal Labs
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "parambulator",
3 | "description": "A simple way to generate nice error messages for named parameters.",
4 | "keywords": [
5 | "json",
6 | "param",
7 | "arg",
8 | "parameter",
9 | "argument",
10 | "schema"
11 | ],
12 | "homepage": "https://github.com/rjrodger/parambulator",
13 | "author": "Richard Rodger (http://richardrodger.com/)",
14 | "contributors": [
15 | "Richard Rodger (http://richardrodger.com/)"
16 | ],
17 | "version": "1.5.2",
18 | "main": "parambulator.js",
19 | "repository": {
20 | "url": "https://github.com/rjrodger/parambulator.git"
21 | },
22 | "dependencies": {
23 | "gex": "0.2.2",
24 | "jsonic": "0.2.2",
25 | "lodash": "4.5.0"
26 | },
27 | "files": [
28 | "package.json",
29 | "LICENSE.txt",
30 | "README.md",
31 | "parambulator.js"
32 | ],
33 | "devDependencies": {
34 | "docco": "0.7.0",
35 | "jasmine-node": "1.14.5",
36 | "jshint": "2.8.0",
37 | "mocha": "~2.1.0",
38 | "phantomjs": "1.8.2-3",
39 | "uglify-js": "2.4.23"
40 | },
41 | "scripts": {
42 | "test": "./test.sh",
43 | "build": "./build.sh"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/test/string-rulespec.js:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2010-2013 Richard Rodger */
2 |
3 | "use strict";
4 |
5 |
6 | var assert = require('assert')
7 |
8 | var parambulator = require('..')
9 |
10 |
11 | describe('string-rulespec', function() {
12 |
13 | it('single', function() {
14 | var pm, res
15 |
16 | pm = parambulator({foo:'required$'})
17 | res = pm.validate({foo:'1'})
18 | assert.ok( null == res )
19 |
20 | res = pm.validate({bar:'1'})
21 | assert.ok( null != res )
22 | assert.equal('required$',res.parambulator.code)
23 | assert.ok(res.parambulator.point.bar)
24 | })
25 |
26 |
27 | it('multiple', function() {
28 | var pm, res
29 |
30 | pm = parambulator({foo:'required$,integer$'})
31 | res = pm.validate({foo:1})
32 | assert.ok( null == res )
33 |
34 | res = pm.validate({bar:1})
35 | assert.ok( null != res )
36 | assert.equal('required$',res.parambulator.code)
37 | assert.ok(res.parambulator.point.bar)
38 |
39 | res = pm.validate({foo:'zoo'})
40 | assert.ok( null != res )
41 | //console.log(res)
42 | assert.equal('integer$',res.parambulator.code)
43 | assert.ok(res.parambulator.point.foo)
44 | })
45 |
46 | })
47 |
--------------------------------------------------------------------------------
/doc/examples/simple.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Create a Parameter Check
4 | ========================
5 |
6 | Use parambulator to define a set of rules that input paramters must obey.
7 |
8 | */
9 |
10 | // use require to load the parambulator module
11 | // a single function, parambulator, is returned
12 | var parambulator = require('parambulator')
13 |
14 | // create a new instance, providing a "param spec" as the first argument
15 | // the param spec defined the properties you expect to see, and the rules that should apply to them
16 | // in this case, you want your input parameters to have a price property, the value of which is a number
17 | var paramcheck = parambulator({
18 | price: {type$:'number'}
19 | })
20 |
21 |
22 | // print out any errors
23 | function printresult(err,res) {
24 | if(err) console.log(err.message);
25 | }
26 |
27 |
28 |
29 | // this passes - price is a number
30 | paramcheck.validate( { price: 10.99 }, printresult )
31 |
32 |
33 | // this fails - price is a string
34 | paramcheck.validate( { price: 'free!' }, printresult )
35 | // output: The value 'free!' is not of type 'number' (parent: price).
36 |
37 |
38 | // this also fails - price is an object
39 | paramcheck.validate( { price: {foo:'bar'} }, printresult )
40 | // output: The value '{"foo":"bar"}' is not of type 'number' (parent: price).
41 |
42 |
43 |
--------------------------------------------------------------------------------
/test/jasmine.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | parambulator jasmine test
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/doc/examples/http_request_options.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Validate HTTP request options
4 | =============================
5 |
6 | Validate the options accepted by http.request.
7 | See http://nodejs.org/api/http.html#http_http_request_options_callback
8 |
9 |
10 | */
11 |
12 |
13 | var parambulator = require('parambulator')
14 |
15 | var paramcheck = parambulator({
16 | host: {type$:'string'},
17 | hostname: {type$:'string'},
18 | port: {type$:'integer'},
19 | localAddress: {type$:'string'},
20 | socketPath: {type$:'string'},
21 | method: {enum$:['DELETE','GET','HEAD','POST','PUT','CONNECT','OPTIONS','TRACE','COPY','LOCK','MKCOL','MOVE','PROPFIND','PROPPATCH','SEARCH','UNLOCK','REPORT','MKACTIVITY','CHECKOUT','MERGE','MSEARCH','M-SEARCH','NOTIFY','SUBSCRIBE','UNSUBSCRIBE','PATCH','PURGE']},
22 | path: {type$:'string'},
23 | headers: {type$:'object'},
24 | auth: {wild$:'*:*'},
25 | })
26 |
27 |
28 | // print out any errors
29 | function printresult(err,res) {
30 | if(err) console.log(err.message);
31 | }
32 |
33 |
34 |
35 | // these pass
36 | paramcheck.validate( {
37 | host: 'www.google.com',
38 | port: 80,
39 | path: '/upload',
40 | method: 'POST'
41 | }, printresult )
42 |
43 | paramcheck.validate( {
44 | auth: 'user1:pass1'
45 | }, printresult )
46 |
47 |
48 |
49 | // these fail
50 |
51 | paramcheck.validate( {
52 | port: '80',
53 | }, printresult )
54 |
55 | paramcheck.validate( {
56 | method: 'FOO'
57 | }, printresult )
58 |
59 | paramcheck.validate( {
60 | headers: true
61 | }, printresult )
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/test/array.js:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2010-2013 Richard Rodger */
2 |
3 | "use strict";
4 |
5 |
6 | var assert = require('chai').assert
7 | var gex = require('gex')
8 |
9 | var parambulator = require('..')
10 |
11 |
12 |
13 | describe('array', function() {
14 |
15 | var pb_array
16 |
17 | it('happy', function() {
18 | pb_array = new parambulator({
19 | z: 'required$',
20 | foo: {
21 |
22 | '__1': {
23 | bar: 'required$'
24 | },
25 |
26 | '__0': 'required$',
27 | },
28 | })
29 | })
30 |
31 |
32 | it('z', function() {
33 | pb_array.validate({z:1},function(err,res){
34 | assert.isNull(err)
35 | })
36 |
37 | pb_array.validate({},function(err,res){
38 | assert.isNotNull(err)
39 | assert.equal(err.parambulator.code,'required$')
40 | })
41 | })
42 |
43 |
44 | it('index0', function() {
45 | pb_array.validate({z:1},function(err,res){
46 | assert.isNull(err)
47 | })
48 |
49 | pb_array.validate({z:1,foo:[10]},function(err,res){
50 | assert.isNull(err)
51 | })
52 |
53 | pb_array.validate({z:1,foo:[]},function(err,res){
54 | assert.isNotNull(err)
55 | assert.equal(err.parambulator.code,'required$')
56 | })
57 | })
58 |
59 |
60 | it('index1', function() {
61 |
62 | pb_array.validate({z:1,foo:[10]},function(err,res){
63 | assert.isNull(err)
64 | })
65 |
66 | pb_array.validate({z:1,foo:[{},{bar:1}]},function(err,res){
67 | assert.isNull(err)
68 | })
69 |
70 | pb_array.validate({z:1,foo:[{},{}]},function(err,res){
71 | assert.isNotNull(err)
72 | assert.equal(err.parambulator.code,'required$')
73 | })
74 |
75 | pb_array.validate({z:1,foo:[{},{barx:1}]},function(err,res){
76 | assert.isNotNull(err)
77 | assert.equal(err.parambulator.code,'required$')
78 | })
79 | })
80 |
81 | })
82 |
83 |
--------------------------------------------------------------------------------
/test/custom.js:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2010-2013 Richard Rodger */
2 |
3 | "use strict";
4 |
5 |
6 | var assert = require('chai').assert
7 | var gex = require('gex')
8 |
9 | var parambulator = require('..')
10 |
11 |
12 |
13 | describe('custom', function() {
14 |
15 | var pb
16 |
17 | it('happy', function() {
18 | pb = parambulator({
19 | required$: 'req',
20 | equalsbar$: 'foo',
21 | exactlyone$: ['a','b']
22 | }, {
23 | rules: {
24 | equalsbar$: function(ctxt,cb) {
25 | var pn = ctxt.rule.spec
26 | var val = ctxt.point[pn]
27 | if( 'bar' == val ) {
28 | return cb(null)
29 | }
30 | else {
31 | return ctxt.util.fail(ctxt,cb)
32 | }
33 | }
34 | },
35 | msgs: {
36 | required$:'Property %s is required, yo! At %s.',
37 | equalsbar$:'Property %s is not equal to "bar", man... At %s.',
38 | exactlyone$: function(inserts) {
39 | return 'my custom error msg for '+inserts.rule.spec+' at location '+inserts.parentpath
40 | }
41 | }
42 | })
43 | })
44 |
45 |
46 | it('equalsbar', function() {
47 | pb.validate({req:1,a:1,foo:'bar'},function(err,res){
48 | assert.isNull(err)
49 | })
50 |
51 | pb.validate({req:1,a:1,foo:'foo'},function(err,res){
52 | assert.isNotNull(err)
53 | assert.equal(err.parambulator.code,'equalsbar$')
54 | })
55 |
56 | pb.validate({req:1,a:1},function(err,res){
57 | assert.isNotNull(err)
58 | assert.equal(err.parambulator.code,'equalsbar$')
59 | })
60 |
61 | pb.validate({a:1},function(err,res){
62 | assert.isNotNull(err)
63 | assert.equal(err.parambulator.code,'required$')
64 | })
65 |
66 | pb.validate({req:1,foo:'bar', a:1,b:1},function(err,res){
67 | assert.isNotNull(err)
68 | assert.equal(err.parambulator.code,'exactlyone$')
69 | assert.equal(err.message,'my custom error msg for a,b at location top level')
70 | })
71 | })
72 | })
73 |
74 |
--------------------------------------------------------------------------------
/test/default.js:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2010-2013 Richard Rodger */
2 |
3 | "use strict";
4 |
5 |
6 | var assert = require('chai').assert
7 | var gex = require('gex')
8 | var _ = require('underscore')
9 |
10 | var parambulator = require('..')
11 |
12 |
13 |
14 | describe('default', function() {
15 |
16 | var pb
17 |
18 | it('happy', function() {
19 | pb = parambulator({
20 | a: {default$:123, type$:'number'},
21 | b: {
22 | firstobj: {default$:23, type$:'number'},
23 | secondobj: {innerobj: {default$:'test'}},
24 | thirdobj: {type$:'array', __0: {default$:123}},
25 | },
26 | c: {default$:555, type$:'number'},
27 | d: {type$: 'array', __0: {default$:'arraytest0'}, __1: {default$:'arraytest1'}},
28 | e: {type$: 'array', default$:[]},
29 |
30 | // TODO: handle this case
31 | //f: {default$:'aa', type$:'number'}
32 | })
33 | })
34 |
35 |
36 | it('firsttest', function() {
37 | var obj = {c: 2222}
38 | pb.validate(obj)
39 |
40 | assert.isTrue(_.has(obj, 'a'))
41 | assert.equal(obj['a'], 123)
42 |
43 | assert.isTrue(_.has(obj, 'b'))
44 | assert.isTrue(_.has(obj['b'], 'firstobj'))
45 | assert.equal(obj['b']['firstobj'], 23)
46 |
47 | assert.isTrue(_.has(obj['b'], 'secondobj'))
48 | assert.isTrue(_.has(obj['b']['secondobj'], 'innerobj'))
49 | assert.equal(obj['b']['secondobj']['innerobj'], 'test')
50 |
51 | assert.isTrue(_.has(obj['b'], 'thirdobj'))
52 | assert.isTrue(_.isArray(obj['b']['thirdobj']))
53 | assert.equal(obj['b']['thirdobj'], '123')
54 |
55 | assert.isTrue(_.has(obj, 'c'))
56 | assert.equal(obj['c'], 2222)
57 |
58 | assert.isTrue(_.has(obj, 'd'))
59 | assert.isTrue(_.isArray(obj['d']))
60 | assert.equal('arraytest0', obj['d'][0])
61 | assert.equal('arraytest1', obj['d'][1])
62 |
63 | assert.isTrue(_.has(obj, 'e'))
64 | assert.isTrue(_.isArray(obj['e']))
65 | assert.equal(0, obj['e'].length)
66 | })
67 |
68 |
69 | it('nice', function() {
70 | parambulator({
71 | c: {default$:555, type$:'number'},
72 | d: {type$: 'number', __0: {default$:'arraytest0'}, __1: {default$:'arraytest1'}},
73 | })
74 | })
75 |
76 |
77 |
78 | })
79 |
80 |
81 |
--------------------------------------------------------------------------------
/test/ownparams.js:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2010-2013 Richard Rodger */
2 |
3 | "use strict";
4 |
5 |
6 | var assert = require('chai').assert
7 | var gex = require('gex')
8 | var _ = require('underscore')
9 |
10 | var parambulator = require('..')
11 |
12 |
13 |
14 | describe('ownparams', function() {
15 |
16 | var pb = parambulator.ownparams
17 |
18 |
19 |
20 | it('strings$', function() {
21 |
22 | for( var r in {required$:1,notempty$:1,atmostone$:1,exactlyone$:1,atleastone$:1} ) {
23 | var args = {}
24 |
25 |
26 | args[r]='foo'
27 | pb.validate(args,function(err,res){
28 | assert.isNull(err)
29 | })
30 |
31 |
32 | args[r]=['foo','bar']
33 | pb.validate(args,function(err,res){
34 | assert.isNull(err)
35 | })
36 |
37 | args[r]=1
38 | pb.validate(args,function(err,res){
39 | //console.dir(err)
40 | assert.isNotNull(err)
41 | assert.equal(err.parambulator.code,'strings$')
42 | })
43 |
44 |
45 | args = {}
46 |
47 | args['foo']={}
48 | args['foo'][r]='bar'
49 | pb.validate(args,function(err,res){
50 | assert.isNull(err)
51 | })
52 |
53 | args['foo']={}
54 | args['foo'][r]=1
55 | pb.validate(args,function(err,res){
56 | //console.dir(err)
57 | assert.isNotNull(err)
58 | assert.equal(err.parambulator.code,'strings$')
59 | })
60 | }
61 |
62 | })
63 |
64 |
65 | it('wild$', function() {
66 | pb.validate({a:{wild$:'b*'}},function(err,res){
67 | assert.isNull(err)
68 | })
69 |
70 | pb.validate({a:{wild$:1}},function(err,res){
71 | //console.dir(err)
72 | //console.dir(res)
73 | assert.isNotNull(err)
74 | assert.equal(err.parambulator.code,'type$')
75 | })
76 | })
77 |
78 |
79 | it('type$', function() {
80 | pb.validate({a:{type$:'string'}},function(err,res){
81 | assert.isNull(err)
82 | })
83 |
84 | pb.validate({a:{type$:1}},function(err,res){
85 | //console.dir(err)
86 | //console.dir(res)
87 | assert.isNotNull(err)
88 | assert.equal(err.parambulator.code,'type$')
89 | })
90 | })
91 |
92 |
93 | it('re$', function() {
94 | pb.validate({a:{re$:'/b/'}},function(err,res){
95 | assert.isNull(err)
96 | })
97 |
98 | pb.validate({a:{re$:1}},function(err,res){
99 | //console.dir(err)
100 | //console.dir(res)
101 | assert.isNotNull(err)
102 | assert.equal(err.parambulator.code,'type$')
103 | })
104 | })
105 |
106 |
107 | it('enum$', function() {
108 | pb.validate({a:{enum$:[11,22]}},function(err,res){
109 | assert.isNull(err)
110 | })
111 |
112 | pb.validate({a:{enum$:1}},function(err,res){
113 | //console.dir(err)
114 | //console.dir(res)
115 | assert.isNotNull(err)
116 | assert.equal(err.parambulator.code,'type$')
117 | })
118 | })
119 | })
120 |
121 |
122 |
--------------------------------------------------------------------------------
/test/wildcard.js:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2010-2013 Richard Rodger */
2 |
3 | "use strict";
4 |
5 |
6 | var assert = require('chai').assert
7 | var gex = require('gex')
8 |
9 | var parambulator = require('..')
10 |
11 |
12 |
13 | describe('array', function() {
14 |
15 | var pb = parambulator({
16 |
17 | atmostone$: 'a*',
18 |
19 | '*': {
20 | a:{type$:'integer'}
21 | },
22 |
23 | y: {
24 | '*': {
25 | a:{type$:'integer'}
26 | },
27 | },
28 |
29 | '**': {
30 | b:{type$:'integer'}
31 | },
32 |
33 | 'z*': 'required$'
34 | })
35 |
36 |
37 | //console.log(''+pb)
38 |
39 |
40 | it('a*', function() {
41 | pb.validate({z:1,},function(err,res){
42 | assert.isNull(err)
43 | })
44 |
45 | pb.validate({z:1,c:1},function(err,res){
46 | assert.isNull(err)
47 | })
48 |
49 |
50 | pb.validate({z:1,a:1},function(err,res){
51 | assert.isNull(err)
52 | })
53 |
54 | pb.validate({z:1,a:1,ax:1},function(err,res){
55 | //console.log(err)
56 | assert.isNotNull(err)
57 | assert.equal(err.parambulator.code,'atmostone$')
58 | })
59 | })
60 |
61 |
62 | it('star', function() {
63 | pb.validate({z:1,x:{a:1},y:{a:2}},function(err,res){
64 | assert.isNull(err)
65 | })
66 |
67 | pb.validate({z:1,x:{a:'b'}},function(err,res){
68 | assert.isNotNull(err)
69 | assert.equal(err.parambulator.code,'type$')
70 | })
71 |
72 | pb.validate({z:1,x:[{a:1},{a:2}]},function(err,res){
73 | assert.isNull(err)
74 | })
75 |
76 | pb.validate({z:1,y:[{a:1},{a:2}]},function(err,res){
77 | assert.isNull(err)
78 | })
79 |
80 |
81 | pb.validate({z:1,y:[{a:'b'}]},function(err,res){
82 | assert.isNotNull(err)
83 | assert.equal(err.parambulator.code,'type$')
84 | })
85 |
86 | })
87 |
88 |
89 |
90 | it('**', function() {
91 |
92 | pb.validate({z:1,b:1,x:{a:1,b:1,y:{b:1}}},function(err,res){
93 | assert.isNull(err)
94 | })
95 |
96 | pb.validate({z:1,b:'foo'},function(err,res){
97 | //console.log(err)
98 | assert.isNotNull(err)
99 | assert.equal(err.parambulator.code,'type$')
100 | })
101 |
102 |
103 | pb.validate({z:1,x:{b:'foo'}},function(err,res){
104 | //console.log(err)
105 | assert.isNotNull(err)
106 | assert.equal(err.parambulator.code,'type$')
107 | })
108 |
109 | pb.validate({z:1,x:{y:{b:'foo'}}},function(err,res){
110 | //console.log(err)
111 | assert.isNotNull(err)
112 | assert.equal(err.parambulator.code,'type$')
113 | })
114 | })
115 |
116 |
117 | it('z*', function() {
118 | pb.validate({z:1},function(err,res){
119 | assert.isNull(err)
120 | })
121 |
122 |
123 | pb.validate({za:1},function(err,res){
124 | assert.isNull(err)
125 | })
126 |
127 | pb.validate({},function(err,res){
128 | //console.log(err)
129 | assert.isNotNull(err)
130 | assert.equal(err.parambulator.code,'required$')
131 | })
132 | })
133 |
134 | })
135 |
136 |
--------------------------------------------------------------------------------
/test/format.js:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2010-2013 Richard Rodger */
2 |
3 | "use strict";
4 |
5 | var assert = require('chai').assert
6 | var gex = require('gex')
7 |
8 | var parambulator = require('..')
9 |
10 |
11 |
12 | describe('format', function() {
13 |
14 | var pb
15 |
16 | it('happy', function() {
17 | pb = parambulator({
18 | a: {format$:'datetime'},
19 | b: {format$:'date'},
20 | c: {format$:'time'},
21 | d: {format$:'utcmillisec'},
22 | e: {format$:'re'},
23 | f: {format$:['date', 'time']},
24 | })
25 | })
26 |
27 | it('datetime', function() {
28 | pb.validate({},function(err,res){
29 | assert.isNull(err)
30 | })
31 |
32 | pb.validate({a:'2012-02-02T11:12:13Z'},function(err,res){
33 | assert.isNull(err)
34 | })
35 |
36 | pb.validate({a:'2012-02-02'},function(err,res){
37 | assert.isNotNull(err)
38 | assert.equal(err.parambulator.code,'format$')
39 | })
40 | })
41 |
42 | it('date', function() {
43 | pb.validate({},function(err,res){
44 | assert.isNull(err)
45 | })
46 |
47 | pb.validate({b:'2012-02-02'},function(err,res){
48 | assert.isNull(err)
49 | })
50 |
51 | pb.validate({b:'2012-32-02'},function(err,res){
52 | assert.isNotNull(err)
53 | assert.equal(err.parambulator.code,'format$')
54 | })
55 | })
56 |
57 | it('time', function() {
58 | pb.validate({},function(err,res){
59 | assert.isNull(err)
60 | })
61 |
62 | pb.validate({c:'11:12:13Z'},function(err,res){
63 | assert.isNull(err)
64 | })
65 |
66 | pb.validate({c:'51:12:13Z'},function(err,res){
67 | assert.isNotNull(err)
68 | assert.equal(err.parambulator.code,'format$')
69 | })
70 | })
71 |
72 | it('utcmillisec', function() {
73 | pb.validate({},function(err,res){
74 | assert.isNull(err)
75 | })
76 |
77 | pb.validate({d:124578},function(err,res){
78 | assert.isNull(err)
79 | })
80 |
81 | pb.validate({d:'test'},function(err,res){
82 | assert.isNotNull(err)
83 | assert.equal(err.parambulator.code,'format$')
84 | })
85 | })
86 |
87 | it('re', function() {
88 | pb.validate({},function(err,res){
89 | assert.isNull(err)
90 | })
91 |
92 | pb.validate({e:/([0-1]\d|2[0-4]):[0-5]\d:[0-5]\dZ/},function(err,res){
93 | assert.isNull(err)
94 | })
95 |
96 | pb.validate({e:124578},function(err,res){
97 | assert.isNotNull(err)
98 | assert.equal(err.parambulator.code,'format$')
99 | })
100 | })
101 |
102 |
103 | it('date-time', function() {
104 | pb.validate({},function(err,res){
105 | assert.isNull(err)
106 | })
107 |
108 | pb.validate({f:'2012-02-02'},function(err,res){
109 | assert.isNull(err)
110 | })
111 |
112 | pb.validate({f:'2012-32-02'},function(err,res){
113 | assert.isNotNull(err)
114 | assert.equal(err.parambulator.code,'format$')
115 | })
116 |
117 | pb.validate({f:'11:12:13Z'},function(err,res){
118 | assert.isNull(err)
119 | })
120 |
121 | pb.validate({f:'51:12:13Z'},function(err,res){
122 | assert.isNotNull(err)
123 | assert.equal(err.parambulator.code,'format$')
124 | })
125 |
126 | pb.validate({f:124578},function(err,res){
127 | assert.isNotNull(err)
128 | assert.equal(err.parambulator.code,'format$')
129 | })
130 |
131 | pb.validate({f:/([0-1]\d|2[0-4]):[0-5]\d:[0-5]\dZ/},function(err,res){
132 | assert.isNotNull(err)
133 | assert.equal(err.parambulator.code,'format$')
134 | })
135 |
136 | pb.validate({f:124578},function(err,res){
137 | assert.isNotNull(err)
138 | assert.equal(err.parambulator.code,'format$')
139 | })
140 |
141 | pb.validate({f:'test'},function(err,res){
142 | assert.isNotNull(err)
143 | assert.equal(err.parambulator.code,'format$')
144 | })
145 | })
146 | })
147 |
148 |
--------------------------------------------------------------------------------
/test/run-jasmine.js:
--------------------------------------------------------------------------------
1 | var system = require('system');
2 |
3 | /**
4 | * Wait until the test condition is true or a timeout occurs. Useful for waiting
5 | * on a server response or for a ui change (fadeIn, etc.) to occur.
6 | *
7 | * @param testFx javascript condition that evaluates to a boolean,
8 | * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
9 | * as a callback function.
10 | * @param onReady what to do when testFx condition is fulfilled,
11 | * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
12 | * as a callback function.
13 | * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
14 | */
15 | function waitFor(testFx, onReady, timeOutMillis) {
16 | var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3001, //< Default Max Timeout is 3s
17 | start = new Date().getTime(),
18 | condition = false,
19 | interval = setInterval(function() {
20 | if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) {
21 | // If not time-out yet and condition not yet fulfilled
22 | condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code
23 | } else {
24 | if(!condition) {
25 | // If condition still not fulfilled (timeout but condition is 'false')
26 | console.log("'waitFor()' timeout");
27 | phantom.exit(1);
28 | } else {
29 | // Condition fulfilled (timeout and/or condition is 'true')
30 | console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms.");
31 | typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled
32 | clearInterval(interval); //< Stop this interval
33 | }
34 | }
35 | }, 100); //< repeat check every 100ms
36 | };
37 |
38 |
39 | if (system.args.length !== 2) {
40 | console.log('Usage: run-jasmine.js URL');
41 | phantom.exit(1);
42 | }
43 |
44 | var page = require('webpage').create();
45 |
46 | // Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
47 | page.onConsoleMessage = function(msg) {
48 | console.log(msg);
49 | };
50 |
51 | page.open(system.args[1], function(status){
52 | if (status !== "success") {
53 | console.log("Unable to access network");
54 | phantom.exit();
55 | } else {
56 | waitFor(function(){
57 | return page.evaluate(function(){
58 | return document.body.querySelector('.symbolSummary .pending') === null
59 | });
60 | }, function(){
61 | var exitCode = page.evaluate(function(){
62 | console.log('');
63 | console.log(document.body.querySelector('.description').innerText);
64 | var list = document.body.querySelectorAll('.results > #details > .specDetail.failed');
65 | if (list && list.length > 0) {
66 | console.log('');
67 | console.log(list.length + ' test(s) FAILED:');
68 | for (i = 0; i < list.length; ++i) {
69 | var el = list[i],
70 | desc = el.querySelector('.description'),
71 | msg = el.querySelector('.resultMessage.fail');
72 | console.log('');
73 | console.log(desc.innerText);
74 | console.log(msg.innerText);
75 | console.log('');
76 | }
77 | return 1;
78 | } else {
79 | console.log(document.body.querySelector('.alert > .passingAlert.bar').innerText);
80 | return 0;
81 | }
82 | });
83 | phantom.exit(exitCode);
84 | });
85 | }
86 | });
87 |
--------------------------------------------------------------------------------
/doc/examples/range.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Create a custom rule
4 | ====================
5 |
6 | The parambulator function takes a second argument ('pref') that lets you define
7 | custom rules:
8 |
9 | var paramcheck = parambulator( {...}, { rules:{ mycustomrule$: function(){...} } } )
10 |
11 | Here, a range$ rule is defined to check that property values fall
12 | within a given numerical range. A custom error message pattern is
13 | also defined - this is optional. And a validity check (also optional)
14 | is defined. The validity check lets you ensure that param specs
15 | specify the custom rule correctly.
16 |
17 | */
18 |
19 |
20 | var _ = require('underscore')
21 | var parambulator = require('parambulator')
22 |
23 |
24 | // this is the pref argument, defined as a separate object so that it can be reused.
25 | var customrules = {
26 |
27 | // define the rules by name (the $ suffix is required)
28 | rules: {
29 |
30 | // each rule is a function that takes the current context (ctxt), and a callback (cb)
31 | range$: function(ctxt,cb) {
32 |
33 | // ctxt.rule provides you with the rule options as used in the param spec
34 | // in this case, rule.spec is an array with two elements, a min and max number
35 | var min = ctxt.rule.spec[0]
36 | var max = ctxt.rule.spec[1]
37 |
38 | // ctxt.point is the current value to be verified, and can be a primitive
39 | // value (e.g. a string), or a complex value (e.g. an object)
40 | var val = ctxt.point
41 |
42 | // if the value is a number ... (remember to check for NaN!)
43 | if( _.isNumber(val) && !_.isNaN(val) ) {
44 |
45 | // ... but the value is outside the range
46 | if( val < min || max < val ) {
47 |
48 | // generate an Error object, using the message pattern below
49 | return ctxt.util.fail(ctxt,cb)
50 | }
51 | }
52 |
53 | // if you get to here, the rule passes
54 | return cb()
55 | }
56 | },
57 |
58 | // you can define custom error messages
59 | msgs: {
60 |
61 | // the template syntax is provided by the underscore library
62 | // see http://underscorejs.org/#template
63 | // available vars are value (the failing value), rule (.spec has the options),
64 | // and parentpath (dot-separated property path to the point)
65 | range$: 'The value <%=value%> is not within the range <%=rule.spec%> (property <%=parentpath%>).'
66 | },
67 |
68 | // this is a param spec that is applied to param specs to ensure they are valid
69 | // use this to define the valid options for your new rule
70 | // in this case, range$ rule spec must be an array of length 2, containing only numbers
71 | // also, note that the prop$ rule is used, as a literal 'range$' would trigger a rule check
72 | // this is how you escape $ chars at the end of property names
73 | valid: {
74 |
75 | // can't use range$ directly, as that would trigger an actual range check!
76 | prop$:{
77 | name:'range$',
78 |
79 | // these are the rules for range$
80 | rules:{
81 |
82 | // it must be an array - e.g. [10,20]
83 | type$:'array',
84 |
85 | // it must be an array of length 2 - [min, max]
86 | required$:['0','1'],
87 |
88 | // all property values must be numbers (array indexes are treated as properties: '0','1',...)
89 | '*':{type$:'number'}
90 | }
91 | }
92 | }
93 | }
94 |
95 |
96 | // create an instance to check the 'volume' parameter
97 | // http://www.youtube.com/watch?v=EbVKWCpNFhY
98 | var rangeparams = parambulator({ volume: {range$:[0,11]} }, customrules)
99 |
100 |
101 | // print out any errors
102 | function printresult(err,res) {
103 | if(err) console.log(err.message);
104 | }
105 |
106 |
107 |
108 | // this fails
109 | rangeparams.validate( { volume: -1 }, printresult )
110 | // output: The value -1 is not within the range 0,11 (property volume).
111 |
112 |
113 | // these pass
114 | rangeparams.validate( { volume: 0 }, printresult )
115 | rangeparams.validate( { volume: 5 }, printresult )
116 | rangeparams.validate( { volume: 11 }, printresult )
117 |
118 |
119 | // this fails
120 | rangeparams.validate( { volume: 12 }, printresult )
121 | // output: The value 12 is not within the range 0,11 (property volume).
122 |
123 |
124 |
125 | // these all fail - range$ rule spec is invalid in each case
126 | // note the parent path - array indexes are treated the same as any other properties
127 |
128 | try { parambulator({ volume: {range$:1} }, customrules) } catch(e) { console.log(e.message) }
129 | // output: The value '1' is not of type 'array' (parent: volume.range$).
130 |
131 | try { parambulator({ volume: {range$:[]} }, customrules) } catch(e) { console.log(e.message) }
132 | // output: The property 0 is missing and is always required (parent: volume.range$).
133 |
134 | try { parambulator({ volume: {range$:['a',1]} }, customrules) } catch(e) { console.log(e.message) }
135 | // output: The value 'a' is not of type 'number' (parent: volume.range$.0).
136 |
137 | // the above tests are using parambulator to check it's own input parameters
138 |
139 |
--------------------------------------------------------------------------------
/test/type.js:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2010-2013 Richard Rodger */
2 |
3 | "use strict";
4 |
5 |
6 | var assert = require('chai').assert
7 | var gex = require('gex')
8 |
9 | var parambulator = require('..')
10 |
11 |
12 |
13 | describe('type', function() {
14 |
15 | var pb = parambulator({
16 | a: {type$:'string'},
17 | b: {type$:'number'},
18 | c: {type$:'integer'},
19 | d: {type$:'boolean'},
20 | e: {type$:'date'},
21 | f: {type$:'array'},
22 | g: {type$:'object'},
23 | h: {type$:['date','string','array']},
24 | i: {type$:['object']},
25 | })
26 |
27 |
28 | it('multi-types-1', function() {
29 | pb.validate({},function(err,res){
30 | assert.isNull(err)
31 | })
32 |
33 | pb.validate({h:'foo'},function(err,res){
34 | assert.isNull(err)
35 | })
36 |
37 | pb.validate({h:new Date()},function(err,res){
38 | assert.isNull(err)
39 | })
40 |
41 | pb.validate({h:[1, 2, '3']},function(err,res){
42 | assert.isNull(err)
43 | })
44 |
45 | pb.validate({h:11.1},function(err,res){
46 | assert.isNotNull(err)
47 | assert.equal(err.parambulator.code,'type$')
48 | })
49 |
50 | pb.validate({h:1},function(err,res){
51 | assert.isNotNull(err)
52 | assert.equal(err.parambulator.code,'type$')
53 | })
54 |
55 | pb.validate({h:true},function(err,res){
56 | assert.isNotNull(err)
57 | assert.equal(err.parambulator.code,'type$')
58 | })
59 |
60 | pb.validate({h:{a:1}},function(err,res){
61 | assert.isNotNull(err)
62 | assert.equal(err.parambulator.code,'type$')
63 | })
64 | })
65 |
66 | it('multi-types-2', function() {
67 | pb.validate({},function(err,res){
68 | assert.isNull(err)
69 | })
70 |
71 | pb.validate({i:'foo'},function(err,res){
72 | assert.isNotNull(err)
73 | assert.equal(err.parambulator.code,'type$')
74 | })
75 |
76 | pb.validate({i:new Date()},function(err,res){
77 | assert.isNotNull(err)
78 | assert.equal(err.parambulator.code,'type$')
79 | })
80 |
81 | pb.validate({i:[1, 2, '3']},function(err,res){
82 | assert.isNotNull(err)
83 | assert.equal(err.parambulator.code,'type$')
84 | })
85 |
86 | pb.validate({i:11.1},function(err,res){
87 | assert.isNotNull(err)
88 | assert.equal(err.parambulator.code,'type$')
89 | })
90 |
91 | pb.validate({i:1},function(err,res){
92 | assert.isNotNull(err)
93 | assert.equal(err.parambulator.code,'type$')
94 | })
95 |
96 | pb.validate({i:true},function(err,res){
97 | assert.isNotNull(err)
98 | assert.equal(err.parambulator.code,'type$')
99 | })
100 |
101 | pb.validate({i:{a:1}},function(err,res){
102 | assert.isNull(err)
103 | })
104 | })
105 |
106 |
107 |
108 | it('string', function() {
109 | pb.validate({},function(err,res){
110 | assert.isNull(err)
111 | })
112 |
113 | pb.validate({a:'foo'},function(err,res){
114 | assert.isNull(err)
115 | })
116 |
117 | pb.validate({a:1},function(err,res){
118 | assert.isNotNull(err)
119 | assert.equal(err.parambulator.code,'type$')
120 | })
121 | })
122 |
123 |
124 |
125 | it('number', function() {
126 | pb.validate({b:1.1},function(err,res){
127 | assert.isNull(err)
128 | })
129 |
130 | pb.validate({b:'foo'},function(err,res){
131 | assert.isNotNull(err)
132 | assert.equal(err.parambulator.code,'type$')
133 | })
134 | })
135 |
136 |
137 | it('integer', function() {
138 | pb.validate({c:1},function(err,res){
139 | assert.isNull(err)
140 | })
141 |
142 | pb.validate({c:1.1},function(err,res){
143 | assert.isNotNull(err)
144 | assert.equal(err.parambulator.code,'type$')
145 | })
146 | })
147 |
148 |
149 | it('boolean', function() {
150 | pb.validate({d:true},function(err,res){
151 | assert.isNull(err)
152 | })
153 |
154 | pb.validate({d:false},function(err,res){
155 | assert.isNull(err)
156 | })
157 |
158 | pb.validate({d:'foo'},function(err,res){
159 | assert.isNotNull(err)
160 | assert.equal(err.parambulator.code,'type$')
161 | })
162 | })
163 |
164 |
165 | it('date', function() {
166 | pb.validate({e:new Date()},function(err,res){
167 | assert.isNull(err)
168 | })
169 |
170 | pb.validate({e:'foo'},function(err,res){
171 | assert.isNotNull(err)
172 | assert.equal(err.parambulator.code,'type$')
173 | })
174 |
175 | pb.validate({e:{a:1}},function(err,res){
176 | assert.isNotNull(err)
177 | assert.equal(err.parambulator.code,'type$')
178 | })
179 | })
180 |
181 |
182 | it('array', function() {
183 | pb.validate({f:[]},function(err,res){
184 | assert.isNull(err)
185 | })
186 |
187 | pb.validate({f:[11]},function(err,res){
188 | assert.isNull(err)
189 | })
190 |
191 | pb.validate({f:'foo'},function(err,res){
192 | assert.isNotNull(err)
193 | assert.equal(err.parambulator.code,'type$')
194 | })
195 |
196 | pb.validate({f:{a:1}},function(err,res){
197 | assert.isNotNull(err)
198 | assert.equal(err.parambulator.code,'type$')
199 | })
200 | })
201 |
202 |
203 | it('object', function() {
204 | pb.validate({g:null},function(err,res){
205 | assert.isNull(err)
206 | })
207 |
208 | pb.validate({g:{}},function(err,res){
209 | assert.isNull(err)
210 | })
211 |
212 | pb.validate({g:{a:1}},function(err,res){
213 | assert.isNull(err)
214 | })
215 |
216 | pb.validate({g:new Date()},function(err,res){
217 | assert.isNotNull(err)
218 | assert.equal(err.parambulator.code,'type$')
219 | })
220 |
221 | pb.validate({g:[]},function(err,res){
222 | assert.isNotNull(err)
223 | assert.equal(err.parambulator.code,'type$')
224 | })
225 | })
226 | })
227 |
--------------------------------------------------------------------------------
/test/jasmine-1.3.1/jasmine.css:
--------------------------------------------------------------------------------
1 | body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
2 |
3 | #HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
4 | #HTMLReporter a { text-decoration: none; }
5 | #HTMLReporter a:hover { text-decoration: underline; }
6 | #HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
7 | #HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
8 | #HTMLReporter #jasmine_content { position: fixed; right: 100%; }
9 | #HTMLReporter .version { color: #aaaaaa; }
10 | #HTMLReporter .banner { margin-top: 14px; }
11 | #HTMLReporter .duration { color: #aaaaaa; float: right; }
12 | #HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
13 | #HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
14 | #HTMLReporter .symbolSummary li.passed { font-size: 14px; }
15 | #HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
16 | #HTMLReporter .symbolSummary li.failed { line-height: 9px; }
17 | #HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
18 | #HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
19 | #HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
20 | #HTMLReporter .symbolSummary li.pending { line-height: 11px; }
21 | #HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
22 | #HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
23 | #HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
24 | #HTMLReporter .runningAlert { background-color: #666666; }
25 | #HTMLReporter .skippedAlert { background-color: #aaaaaa; }
26 | #HTMLReporter .skippedAlert:first-child { background-color: #333333; }
27 | #HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
28 | #HTMLReporter .passingAlert { background-color: #a6b779; }
29 | #HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
30 | #HTMLReporter .failingAlert { background-color: #cf867e; }
31 | #HTMLReporter .failingAlert:first-child { background-color: #b03911; }
32 | #HTMLReporter .results { margin-top: 14px; }
33 | #HTMLReporter #details { display: none; }
34 | #HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
35 | #HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
36 | #HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
37 | #HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
38 | #HTMLReporter.showDetails .summary { display: none; }
39 | #HTMLReporter.showDetails #details { display: block; }
40 | #HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
41 | #HTMLReporter .summary { margin-top: 14px; }
42 | #HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
43 | #HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
44 | #HTMLReporter .summary .specSummary.failed a { color: #b03911; }
45 | #HTMLReporter .description + .suite { margin-top: 0; }
46 | #HTMLReporter .suite { margin-top: 14px; }
47 | #HTMLReporter .suite a { color: #333333; }
48 | #HTMLReporter #details .specDetail { margin-bottom: 28px; }
49 | #HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
50 | #HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
51 | #HTMLReporter .resultMessage span.result { display: block; }
52 | #HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
53 |
54 | #TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
55 | #TrivialReporter a:visited, #TrivialReporter a { color: #303; }
56 | #TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
57 | #TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
58 | #TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
59 | #TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
60 | #TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
61 | #TrivialReporter .runner.running { background-color: yellow; }
62 | #TrivialReporter .options { text-align: right; font-size: .8em; }
63 | #TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
64 | #TrivialReporter .suite .suite { margin: 5px; }
65 | #TrivialReporter .suite.passed { background-color: #dfd; }
66 | #TrivialReporter .suite.failed { background-color: #fdd; }
67 | #TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
68 | #TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
69 | #TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
70 | #TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
71 | #TrivialReporter .spec.skipped { background-color: #bbb; }
72 | #TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
73 | #TrivialReporter .passed { background-color: #cfc; display: none; }
74 | #TrivialReporter .failed { background-color: #fbb; }
75 | #TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
76 | #TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
77 | #TrivialReporter .resultMessage .mismatch { color: black; }
78 | #TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
79 | #TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
80 | #TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
81 | #TrivialReporter #jasmine_content { position: fixed; right: 100%; }
82 | #TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }
83 |
--------------------------------------------------------------------------------
/test/basic.js:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2010-2013 Richard Rodger */
2 |
3 | "use strict";
4 |
5 |
6 | var assert = require('chai').assert
7 | var gex = require('gex')
8 |
9 | var parambulator = require('..')
10 |
11 |
12 | describe('basic', function() {
13 |
14 | var pb
15 |
16 |
17 |
18 | it('happy', function() {
19 | pb = parambulator({
20 | atmostone$: ['path','from'],
21 |
22 | search: {
23 | required$: ['find','replace']
24 | },
25 |
26 | exactlyone$: ['red','blue'],
27 | atleastone$: ['a','b'],
28 |
29 | sub: {
30 | dub: {
31 | exactlyone$: ['x','y','z'],
32 | }
33 | },
34 |
35 | required$: ['foo','bar'],
36 | notempty$: ['z'],
37 | })
38 |
39 |
40 | })
41 |
42 |
43 |
44 | it('required$', function() {
45 | pb.validate({a:1,z:1,red:1,foo:1,bar:1},function(err,res){
46 | assert.isNull(err)
47 | })
48 |
49 | pb.validate({a:1,z:1,red:1,foo:1},function(err,res){
50 | assert.isNotNull(err)
51 | assert.equal('required$',err.parambulator.code)
52 | })
53 |
54 | pb.validate({a:1,z:1,red:1,bar:1},function(err,res){
55 | assert.isNotNull(err)
56 | assert.equal('required$',err.parambulator.code)
57 | })
58 |
59 | pb.validate({a:1,z:1,red:1,},function(err,res){
60 | //console.log(err)
61 | assert.isNotNull(err)
62 | assert.equal('required$',err.parambulator.code)
63 | })
64 | })
65 |
66 |
67 | it('exactlyone$', function() {
68 | pb.validate({a:1,z:1,red:1, foo:1,bar:1},function(err,res){
69 | assert.isNull(err)
70 | })
71 |
72 | pb.validate({a:1,z:1,blue:1, foo:1,bar:1},function(err,res){
73 | assert.isNull(err)
74 | })
75 |
76 | pb.validate({a:1,z:1,foo:1,bar:1},function(err,res){
77 | //console.log(err)
78 | assert.isNotNull(err)
79 | assert.equal('exactlyone$',err.parambulator.code)
80 | })
81 |
82 | pb.validate({a:1,z:1,red:1,blue:1, foo:1,bar:1},function(err,res){
83 | assert.isNotNull(err)
84 | assert.equal('exactlyone$',err.parambulator.code)
85 | })
86 | })
87 |
88 |
89 | it('atmostone$', function() {
90 | pb.validate({a:1,z:1,red:1,foo:1,bar:1, from:1},function(err,res){
91 | assert.isNull(err)
92 | })
93 |
94 | pb.validate({a:1,z:1,red:1,foo:1,bar:1, path:1},function(err,res){
95 | assert.isNull(err)
96 | })
97 |
98 | pb.validate({a:1,z:1,red:1,foo:1,bar:1, path:1,from:1},function(err,res){
99 | //console.log(err)
100 | assert.isNotNull(err)
101 | assert.equal('atmostone$',err.parambulator.code)
102 | })
103 | })
104 |
105 |
106 | it('atleastone$', function() {
107 | pb.validate({z:1, red:1,foo:1,bar:1,from:1, a:1},function(err,res){
108 | assert.isNull(err)
109 | })
110 |
111 | pb.validate({z:1, red:1,foo:1,bar:1,from:1, b:1},function(err,res){
112 | assert.isNull(err)
113 | })
114 |
115 | pb.validate({red:1,foo:1,bar:1,from:1, a:1,z:1,b:1},function(err,res){
116 | assert.isNull(err)
117 | })
118 |
119 | pb.validate({z:1, red:1,foo:1,bar:1,from:1 },function(err,res){
120 | //console.log(err)
121 | assert.isNotNull(err)
122 | assert.equal('atleastone$',err.parambulator.code)
123 | })
124 | })
125 |
126 |
127 | it('search', function() {
128 | pb.validate({a:1,z:1,red:1,foo:1,bar:1, search:{find:1,replace:1}},function(err,res){
129 | assert.isNull(err)
130 | })
131 |
132 | pb.validate({a:1,z:1,red:1,foo:1,bar:1, search:{find:1}},function(err,res){
133 | assert.isNotNull(err)
134 | assert.equal('required$',err.parambulator.code)
135 | })
136 |
137 | pb.validate({a:1,z:1,red:1,foo:1,bar:1, search:{replace:1}},function(err,res){
138 | assert.isNotNull(err)
139 | assert.equal('required$',err.parambulator.code)
140 | })
141 | })
142 |
143 |
144 |
145 | it('sublevels', function() {
146 | pb.validate({a:1,z:1,red:1,foo:1,bar:1, sub:{dub:{x:1}}},function(err,res){
147 | assert.isNull(err)
148 | })
149 |
150 | pb.validate({a:1,z:1,red:1,foo:1,bar:1, sub:{dub:{y:1}}},function(err,res){
151 | assert.isNull(err)
152 | })
153 |
154 | pb.validate({a:1,z:1,red:1,foo:1,bar:1, sub:{dub:{z:1}}},function(err,res){
155 | assert.isNull(err)
156 | })
157 |
158 | pb.validate({a:1,z:1,red:1,foo:1,bar:1, sub:{dub:{}}},function(err,res){
159 | assert.isNotNull(err)
160 | assert.equal('exactlyone$',err.parambulator.code)
161 | })
162 |
163 | pb.validate({a:1,z:1,red:1,foo:1,bar:1, sub:{dub:{x:1,y:1}}},function(err,res){
164 | assert.isNotNull(err)
165 | assert.equal('exactlyone$',err.parambulator.code)
166 | })
167 |
168 | pb.validate({a:1,z:1,red:1,foo:1,bar:1, sub:{dub:{x:1,y:1,z:1}}},function(err,res){
169 | assert.isNotNull(err)
170 | assert.equal('exactlyone$',err.parambulator.code)
171 | })
172 | })
173 |
174 |
175 | it('notempty$', function() {
176 | pb.validate({a:1,z:'',red:1,foo:1,bar:1},function(err,res){
177 | //console.log(err)
178 | assert.isNotNull(err)
179 | assert.equal('notempty$',err.parambulator.code)
180 | })
181 |
182 | pb.validate({a:1,z:null,red:1,foo:1,bar:1},function(err,res){
183 | assert.isNotNull(err)
184 | assert.equal('notempty$',err.parambulator.code)
185 | })
186 |
187 | pb.validate({a:1,z:undefined,red:1,foo:1,bar:1},function(err,res){
188 | assert.isNotNull(err)
189 | assert.equal('notempty$',err.parambulator.code)
190 | })
191 | })
192 |
193 |
194 | it('pbeasy', function() {
195 | var pbeasy_one = parambulator({one:{string$:true,required$:true}})
196 |
197 | pbeasy_one.validate({one:'a'},function(err,res){
198 | assert.isNull(err)
199 | })
200 |
201 | pbeasy_one.validate({},function(err,res){
202 | assert.isNotNull(err)
203 | assert.equal('required$',err.parambulator.code)
204 | })
205 |
206 |
207 | var pbeasy_deep = parambulator({one:{required$:true,two:{string$:true,required$:true}}})
208 |
209 | pbeasy_deep.validate({one:{two:'a'}},function(err,res){
210 | assert.isNull(err)
211 | })
212 |
213 | pbeasy_deep.validate({one:{}},function(err,res){
214 | assert.isNotNull(err)
215 | assert.equal('required$',err.parambulator.code)
216 | })
217 |
218 |
219 | var pbeasy_two = parambulator({a:1,b:'q',c:{required$:true}})
220 |
221 | pbeasy_two.validate({a:1,b:'q',c:'w'},function(err,res){
222 | assert.isNull(err)
223 | })
224 |
225 | pbeasy_two.validate({a:1,b:'q'},function(err,res){
226 | //console.log(err)
227 | assert.isNotNull(err)
228 | assert.equal('required$',err.parambulator.code)
229 | })
230 |
231 |
232 | })
233 |
234 | })
235 |
236 |
237 |
--------------------------------------------------------------------------------
/doc/public/stylesheets/normalize.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v2.0.1 | MIT License | git.io/normalize */
2 |
3 | /* ==========================================================================
4 | HTML5 display definitions
5 | ========================================================================== */
6 |
7 | /*
8 | * Corrects `block` display not defined in IE 8/9.
9 | */
10 |
11 | article,
12 | aside,
13 | details,
14 | figcaption,
15 | figure,
16 | footer,
17 | header,
18 | hgroup,
19 | nav,
20 | section,
21 | summary {
22 | display: block;
23 | }
24 |
25 | /*
26 | * Corrects `inline-block` display not defined in IE 8/9.
27 | */
28 |
29 | audio,
30 | canvas,
31 | video {
32 | display: inline-block;
33 | }
34 |
35 | /*
36 | * Prevents modern browsers from displaying `audio` without controls.
37 | * Remove excess height in iOS 5 devices.
38 | */
39 |
40 | audio:not([controls]) {
41 | display: none;
42 | height: 0;
43 | }
44 |
45 | /*
46 | * Addresses styling for `hidden` attribute not present in IE 8/9.
47 | */
48 |
49 | [hidden] {
50 | display: none;
51 | }
52 |
53 | /* ==========================================================================
54 | Base
55 | ========================================================================== */
56 |
57 | /*
58 | * 1. Sets default font family to sans-serif.
59 | * 2. Prevents iOS text size adjust after orientation change, without disabling
60 | * user zoom.
61 | */
62 |
63 | html {
64 | font-family: sans-serif; /* 1 */
65 | -webkit-text-size-adjust: 100%; /* 2 */
66 | -ms-text-size-adjust: 100%; /* 2 */
67 | }
68 |
69 | /*
70 | * Removes default margin.
71 | */
72 |
73 | body {
74 | margin: 0;
75 | }
76 |
77 | /* ==========================================================================
78 | Links
79 | ========================================================================== */
80 |
81 | /*
82 | * Addresses `outline` inconsistency between Chrome and other browsers.
83 | */
84 |
85 | a:focus {
86 | outline: thin dotted;
87 | }
88 |
89 | /*
90 | * Improves readability when focused and also mouse hovered in all browsers.
91 | */
92 |
93 | a:active,
94 | a:hover {
95 | outline: 0;
96 | }
97 |
98 | /* ==========================================================================
99 | Typography
100 | ========================================================================== */
101 |
102 | /*
103 | * Addresses `h1` font sizes within `section` and `article` in Firefox 4+,
104 | * Safari 5, and Chrome.
105 | */
106 |
107 | h1 {
108 | font-size: 2em;
109 | }
110 |
111 | /*
112 | * Addresses styling not present in IE 8/9, Safari 5, and Chrome.
113 | */
114 |
115 | abbr[title] {
116 | border-bottom: 1px dotted;
117 | }
118 |
119 | /*
120 | * Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
121 | */
122 |
123 | b,
124 | strong {
125 | font-weight: bold;
126 | }
127 |
128 | /*
129 | * Addresses styling not present in Safari 5 and Chrome.
130 | */
131 |
132 | dfn {
133 | font-style: italic;
134 | }
135 |
136 | /*
137 | * Addresses styling not present in IE 8/9.
138 | */
139 |
140 | mark {
141 | background: #ff0;
142 | color: #000;
143 | }
144 |
145 |
146 | /*
147 | * Corrects font family set oddly in Safari 5 and Chrome.
148 | */
149 |
150 | code,
151 | kbd,
152 | pre,
153 | samp {
154 | font-family: monospace, serif;
155 | font-size: 1em;
156 | }
157 |
158 | /*
159 | * Improves readability of pre-formatted text in all browsers.
160 | */
161 |
162 | pre {
163 | white-space: pre;
164 | white-space: pre-wrap;
165 | word-wrap: break-word;
166 | }
167 |
168 | /*
169 | * Sets consistent quote types.
170 | */
171 |
172 | q {
173 | quotes: "\201C" "\201D" "\2018" "\2019";
174 | }
175 |
176 | /*
177 | * Addresses inconsistent and variable font size in all browsers.
178 | */
179 |
180 | small {
181 | font-size: 80%;
182 | }
183 |
184 | /*
185 | * Prevents `sub` and `sup` affecting `line-height` in all browsers.
186 | */
187 |
188 | sub,
189 | sup {
190 | font-size: 75%;
191 | line-height: 0;
192 | position: relative;
193 | vertical-align: baseline;
194 | }
195 |
196 | sup {
197 | top: -0.5em;
198 | }
199 |
200 | sub {
201 | bottom: -0.25em;
202 | }
203 |
204 | /* ==========================================================================
205 | Embedded content
206 | ========================================================================== */
207 |
208 | /*
209 | * Removes border when inside `a` element in IE 8/9.
210 | */
211 |
212 | img {
213 | border: 0;
214 | }
215 |
216 | /*
217 | * Corrects overflow displayed oddly in IE 9.
218 | */
219 |
220 | svg:not(:root) {
221 | overflow: hidden;
222 | }
223 |
224 | /* ==========================================================================
225 | Figures
226 | ========================================================================== */
227 |
228 | /*
229 | * Addresses margin not present in IE 8/9 and Safari 5.
230 | */
231 |
232 | figure {
233 | margin: 0;
234 | }
235 |
236 | /* ==========================================================================
237 | Forms
238 | ========================================================================== */
239 |
240 | /*
241 | * Define consistent border, margin, and padding.
242 | */
243 |
244 | fieldset {
245 | border: 1px solid #c0c0c0;
246 | margin: 0 2px;
247 | padding: 0.35em 0.625em 0.75em;
248 | }
249 |
250 | /*
251 | * 1. Corrects color not being inherited in IE 8/9.
252 | * 2. Remove padding so people aren't caught out if they zero out fieldsets.
253 | */
254 |
255 | legend {
256 | border: 0; /* 1 */
257 | padding: 0; /* 2 */
258 | }
259 |
260 | /*
261 | * 1. Corrects font family not being inherited in all browsers.
262 | * 2. Corrects font size not being inherited in all browsers.
263 | * 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome
264 | */
265 |
266 | button,
267 | input,
268 | select,
269 | textarea {
270 | font-family: inherit; /* 1 */
271 | font-size: 100%; /* 2 */
272 | margin: 0; /* 3 */
273 | }
274 |
275 | /*
276 | * Addresses Firefox 4+ setting `line-height` on `input` using `!important` in
277 | * the UA stylesheet.
278 | */
279 |
280 | button,
281 | input {
282 | line-height: normal;
283 | }
284 |
285 | /*
286 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
287 | * and `video` controls.
288 | * 2. Corrects inability to style clickable `input` types in iOS.
289 | * 3. Improves usability and consistency of cursor style between image-type
290 | * `input` and others.
291 | */
292 |
293 | button,
294 | html input[type="button"], /* 1 */
295 | input[type="reset"],
296 | input[type="submit"] {
297 | -webkit-appearance: button; /* 2 */
298 | cursor: pointer; /* 3 */
299 | }
300 |
301 | /*
302 | * Re-set default cursor for disabled elements.
303 | */
304 |
305 | button[disabled],
306 | input[disabled] {
307 | cursor: default;
308 | }
309 |
310 | /*
311 | * 1. Addresses box sizing set to `content-box` in IE 8/9.
312 | * 2. Removes excess padding in IE 8/9.
313 | */
314 |
315 | input[type="checkbox"],
316 | input[type="radio"] {
317 | box-sizing: border-box; /* 1 */
318 | padding: 0; /* 2 */
319 | }
320 |
321 | /*
322 | * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
323 | * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
324 | * (include `-moz` to future-proof).
325 | */
326 |
327 | input[type="search"] {
328 | -webkit-appearance: textfield; /* 1 */
329 | -moz-box-sizing: content-box;
330 | -webkit-box-sizing: content-box; /* 2 */
331 | box-sizing: content-box;
332 | }
333 |
334 | /*
335 | * Removes inner padding and search cancel button in Safari 5 and Chrome
336 | * on OS X.
337 | */
338 |
339 | input[type="search"]::-webkit-search-cancel-button,
340 | input[type="search"]::-webkit-search-decoration {
341 | -webkit-appearance: none;
342 | }
343 |
344 | /*
345 | * Removes inner padding and border in Firefox 4+.
346 | */
347 |
348 | button::-moz-focus-inner,
349 | input::-moz-focus-inner {
350 | border: 0;
351 | padding: 0;
352 | }
353 |
354 | /*
355 | * 1. Removes default vertical scrollbar in IE 8/9.
356 | * 2. Improves readability and alignment in all browsers.
357 | */
358 |
359 | textarea {
360 | overflow: auto; /* 1 */
361 | vertical-align: top; /* 2 */
362 | }
363 |
364 | /* ==========================================================================
365 | Tables
366 | ========================================================================== */
367 |
368 | /*
369 | * Remove most spacing between table cells.
370 | */
371 |
372 | table {
373 | border-collapse: collapse;
374 | border-spacing: 0;
375 | }
--------------------------------------------------------------------------------
/test/parambulator-multi.spec.js:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2014-2015 Richard Rodger, MIT License */
2 | "use strict";
3 |
4 |
5 | if( 'undefined' === typeof parambulator ) {
6 | var parambulator = require('..')
7 | }
8 |
9 |
10 | if( 'undefined' === typeof _ ) {
11 | var _ = require('lodash')
12 | }
13 |
14 |
15 | function s(obj){
16 | return JSON.stringify(obj)
17 | }
18 |
19 |
20 | var assert = {
21 | isNull: function(x){
22 | expect(x).toBe(null)
23 | },
24 | isNotNull: function(x){
25 | expect(x).toNotBe(null)
26 | },
27 | equal: function(x,y){
28 | expect(x).toBe(y)
29 | },
30 | isTrue: function(x){
31 | expect(!!x).toBe(true)
32 | },
33 | ok: function(x){
34 | expect(!!x).toBe(true)
35 | },
36 | }
37 |
38 |
39 | describe('parambulator-multi', function() {
40 |
41 | var pb_default = parambulator({
42 | a: {default$:123, type$:'number'},
43 | b: {
44 | firstobj: {default$:23, type$:'number'},
45 | secondobj: {innerobj: {default$:'test'}},
46 | thirdobj: {type$:'array', __0: {default$:123}},
47 | },
48 | c: {default$:555, type$:'number'},
49 | d: {type$: 'array', __0: {default$:'arraytest0'}, __1: {default$:'arraytest1'}},
50 | e: {type$: 'array', default$:[]},
51 |
52 | // TODO: handle this case
53 | //f: {default$:'aa', type$:'number'}
54 | }, {multiErrors: true})
55 |
56 |
57 | it('default-firsttest', function() {
58 | var obj = {c: 2222}
59 | pb_default.validate(obj)
60 |
61 | assert.isTrue(_.has(obj, 'a'))
62 | assert.equal(obj['a'], 123)
63 |
64 | assert.isTrue(_.has(obj, 'b'))
65 | assert.isTrue(_.has(obj['b'], 'firstobj'))
66 | assert.equal(obj['b']['firstobj'], 23)
67 |
68 | assert.isTrue(_.has(obj['b'], 'secondobj'))
69 | assert.isTrue(_.has(obj['b']['secondobj'], 'innerobj'))
70 | assert.equal(obj['b']['secondobj']['innerobj'], 'test')
71 |
72 | assert.isTrue(_.has(obj['b'], 'thirdobj'))
73 | assert.isTrue(_.isArray(obj['b']['thirdobj']))
74 | assert.equal(obj['b']['thirdobj'][0], 123)
75 |
76 | assert.isTrue(_.has(obj, 'c'))
77 | assert.equal(obj['c'], 2222)
78 |
79 | assert.isTrue(_.has(obj, 'd'))
80 | assert.isTrue(_.isArray(obj['d']))
81 | assert.equal('arraytest0', obj['d'][0])
82 | assert.equal('arraytest1', obj['d'][1])
83 |
84 | assert.isTrue(_.has(obj, 'e'))
85 | assert.isTrue(_.isArray(obj['e']))
86 | assert.equal(0, obj['e'].length)
87 | })
88 |
89 |
90 | var pb_format= parambulator({
91 | a: {format$:'datetime'},
92 | b: {format$:'date'},
93 | c: {format$:'time'},
94 | d: {format$:'utcmillisec'},
95 | e: {format$:'re'},
96 | f: {format$:['date', 'time']},
97 | }, {
98 | multiErrors: true
99 | })
100 |
101 |
102 | it('format-datetime', function() {
103 | var validationsCount = 0
104 |
105 | pb_format.validate({},function(err,res){
106 | assert.isNull(err)
107 | validationsCount++
108 | })
109 |
110 | pb_format.validate({a:'2012-02-02T11:12:13Z'},function(err,res){
111 | assert.isNull(err)
112 | validationsCount++
113 | })
114 |
115 | pb_format.validate({a:'2012-02-02'},function(err,res){
116 | assert.isNotNull(err)
117 | assert.equal(err.length, 1)
118 | assert.equal(err[0].parambulator.code,'format$')
119 | validationsCount++
120 | })
121 |
122 | // if there is a bug in parambulator where the callback is never called
123 | // tests will succeed without going through the asserts
124 | assert.equal(validationsCount, 3)
125 | })
126 |
127 | it('format-date', function() {
128 | pb_format.validate({},function(err,res){
129 | assert.isNull(err)
130 | })
131 |
132 | pb_format.validate({b:'2012-02-02'},function(err,res){
133 | assert.isNull(err)
134 | })
135 |
136 | pb_format.validate({b:'2012-32-02'},function(err,res){
137 | assert.isNotNull(err)
138 | assert.equal(err.length, 1)
139 | assert.equal(err[0].parambulator.code,'format$')
140 | })
141 | })
142 |
143 | it('format-time', function() {
144 | pb_format.validate({},function(err,res){
145 | assert.isNull(err)
146 | })
147 |
148 | pb_format.validate({c:'11:12:13Z'},function(err,res){
149 | assert.isNull(err)
150 | })
151 |
152 | pb_format.validate({c:'51:12:13Z'},function(err,res){
153 | assert.isNotNull(err)
154 | assert.equal(err.length, 1)
155 | assert.equal(err[0].parambulator.code,'format$')
156 | })
157 | })
158 |
159 | it('format-utcmillisec', function() {
160 | pb_format.validate({},function(err,res){
161 | assert.isNull(err)
162 | })
163 |
164 | pb_format.validate({d:124578},function(err,res){
165 | assert.isNull(err)
166 | })
167 |
168 | pb_format.validate({d:'test'},function(err,res){
169 | assert.isNotNull(err)
170 | assert.equal(err.length, 1)
171 | assert.equal(err[0].parambulator.code,'format$')
172 | })
173 | })
174 |
175 | it('format-re', function() {
176 | pb_format.validate({},function(err,res){
177 | assert.isNull(err)
178 | })
179 |
180 | pb_format.validate({e:/([0-1]\d|2[0-4]):[0-5]\d:[0-5]\dZ/},function(err,res){
181 | assert.isNull(err)
182 | })
183 |
184 | pb_format.validate({e:124578},function(err,res){
185 | assert.isNotNull(err)
186 | assert.equal(err.length, 1)
187 | assert.equal(err[0].parambulator.code,'format$')
188 | })
189 | })
190 |
191 |
192 | it('format-date-time', function() {
193 | pb_format.validate({},function(err,res){
194 | assert.isNull(err)
195 | })
196 |
197 | pb_format.validate({f:'2012-02-02'},function(err,res){
198 | assert.isNull(err)
199 | })
200 |
201 | pb_format.validate({f:'2012-32-02'},function(err,res){
202 | assert.isNotNull(err)
203 | assert.equal(err.length, 1)
204 | assert.equal(err[0].parambulator.code,'format$')
205 | })
206 |
207 | pb_format.validate({f:'11:12:13Z'},function(err,res){
208 | assert.isNull(err)
209 | })
210 |
211 | pb_format.validate({f:'51:12:13Z'},function(err,res){
212 | assert.isNotNull(err)
213 | assert.equal(err.length, 1)
214 | assert.equal(err[0].parambulator.code,'format$')
215 | })
216 |
217 | pb_format.validate({f:124578},function(err,res){
218 | assert.isNotNull(err)
219 | assert.equal(err.length, 1)
220 | assert.equal(err[0].parambulator.code,'format$')
221 | })
222 |
223 | pb_format.validate({f:/([0-1]\d|2[0-4]):[0-5]\d:[0-5]\dZ/},function(err,res){
224 | assert.isNotNull(err)
225 | assert.equal(err.length, 1)
226 | assert.equal(err[0].parambulator.code,'format$')
227 | })
228 |
229 | pb_format.validate({f:124578},function(err,res){
230 | assert.isNotNull(err)
231 | assert.equal(err.length, 1)
232 | assert.equal(err[0].parambulator.code,'format$')
233 | })
234 |
235 | pb_format.validate({f:'test'},function(err,res){
236 | assert.isNotNull(err)
237 | assert.equal(err.length, 1)
238 | assert.equal(err[0].parambulator.code,'format$')
239 | })
240 | })
241 |
242 | it('format-all', function(done) {
243 | var ent = {
244 | a:'2012-02-02',
245 | b:'2012-32-02',
246 | c:'51:12:13Z',
247 | d:'test',
248 | e:124578,
249 | f:'51:12:13Z'
250 | }
251 | pb_format.validate(ent,function(err,res) {
252 | assert.isNotNull(err)
253 | assert.equal(err.length, 6)
254 | var i = 0
255 | _.each(ent, function(val, prop) {
256 | assert.equal(err[i].parambulator.property, prop)
257 | assert.equal(err[i].parambulator.code, 'format$')
258 | i++
259 | })
260 | done()
261 | })
262 | })
263 |
264 | var pb_multi_require = parambulator({
265 | required$:['a', 'b'],
266 | c: 'required$'
267 | }, {
268 | multiErrors: true
269 | })
270 |
271 | it('required all missing', function(done) {
272 | var props = ['a', 'b', 'c']
273 | var ent = {}
274 | pb_multi_require.validate(ent,function(err,res) {
275 | assert.isNotNull(err)
276 | assert.equal(err.length, 3)
277 | var i = 0
278 | _.each(props, function(prop) {
279 | assert.equal(err[i].parambulator.property, prop)
280 | assert.equal(err[i].parambulator.code, 'required$')
281 | i++
282 | })
283 | done()
284 | })
285 | })
286 |
287 | it('required partially missing', function(done) {
288 | var props = ['b', 'c']
289 | var ent = {a: 1}
290 | pb_multi_require.validate(ent,function(err,res) {
291 | assert.isNotNull(err)
292 | assert.equal(err.length, 2)
293 | var i = 0
294 | _.each(props, function(prop) {
295 | assert.equal(err[i].parambulator.property, prop)
296 | assert.equal(err[i].parambulator.code, 'required$')
297 | i++
298 | })
299 | done()
300 | })
301 | })
302 |
303 | it('required partially missing', function(done) {
304 | var props = ['a', 'b', 'c']
305 | var ent = {a: 1, b: 2}
306 | pb_multi_require.validate(ent,function(err, res) {
307 | assert.isNotNull(err)
308 | assert.equal(err.length, 1)
309 | assert.equal(err[0].parambulator.property, 'c')
310 | assert.equal(err[0].parambulator.code, 'required$')
311 | done()
312 | })
313 | })
314 |
315 |
316 | })
317 |
--------------------------------------------------------------------------------
/test/value.js:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2010-2013 Richard Rodger */
2 |
3 | "use strict";
4 |
5 |
6 | var assert = require('chai').assert
7 | var gex = require('gex')
8 |
9 | var parambulator = require('..')
10 |
11 |
12 |
13 | describe('value', function() {
14 |
15 | var pb = parambulator({
16 | a: 'a',
17 | b: 'b*',
18 | c: {wild$:'*c'},
19 | d: {eq$:'d*'},
20 | e: {re$:'e[z]'},
21 | f: {re$:'/f[z]/i'},
22 | g: {enum$:['A','B','C']},
23 | h: {minlen$:2},
24 | i: {maxlen$:6},
25 | j: {lt$: 2},
26 | k: {lt$: new Date("2012-09-04")},
27 | l: {lte$: 2},
28 | m: {lte$: new Date("2012-09-04")},
29 | n: {gt$: 2},
30 | o: {gt$: new Date("2012-09-04")},
31 | p: {gte$: 2},
32 | q: {gte$: new Date("2012-09-04")},
33 | r: {min$: 2},
34 | s: {min$: new Date("2012-09-04")},
35 | t: {max$: 2},
36 | u: {max$: new Date("2012-09-04")},
37 | v: {uniq$: [true]},
38 | wild$:'top*', // does nothing
39 | })
40 |
41 |
42 | it('wild$', function() {
43 |
44 | pb.validate({},function(err,res){
45 | assert.isNull(err)
46 | })
47 |
48 | pb.validate({a:'a'},function(err,res){
49 | assert.isNull(err)
50 | })
51 |
52 | pb.validate({b:'bx'},function(err,res){
53 | assert.isNull(err)
54 | })
55 |
56 | pb.validate({c:'xc'},function(err,res){
57 | assert.isNull(err)
58 | })
59 |
60 | pb.validate({a:'b'},function(err,res){
61 | //console.log(err)
62 | assert.isNotNull(err)
63 | assert.equal(err.parambulator.code,'wild$')
64 | })
65 |
66 | })
67 |
68 |
69 | it('eq$', function() {
70 | pb.validate({d:'d*'},function(err,res){
71 | assert.isNull(err)
72 | })
73 |
74 | pb.validate({d:'dx'},function(err,res){
75 | //console.log(err)
76 | assert.isNotNull(err)
77 | assert.equal(err.parambulator.code,'eq$')
78 | })
79 | })
80 |
81 |
82 | it('minlen$', function() {
83 | // test for string values
84 | pb.validate({h:'abcde'},function(err,res){
85 | assert.isNull(err)
86 | })
87 |
88 | pb.validate({h:'a'},function(err,res){
89 | assert.isNotNull(err)
90 | assert.equal(err.parambulator.code,'minlen$')
91 | })
92 |
93 | // test arrays
94 | pb.validate({h:[1,2,3,4]},function(err,res){
95 | assert.isNull(err)
96 | })
97 |
98 | pb.validate({h:[1]},function(err,res){
99 | assert.isNotNull(err)
100 | assert.equal(err.parambulator.code,'minlen$')
101 | })
102 |
103 |
104 | // test objects
105 | pb.validate({h:{1:1, 2:2, 3:3, 4:4}},function(err,res){
106 | assert.isNull(err)
107 | })
108 |
109 | pb.validate({h:{1:1}},function(err,res){
110 | assert.isNotNull(err)
111 | assert.equal(err.parambulator.code,'minlen$')
112 | })
113 | })
114 |
115 |
116 | it('maxlen$', function() {
117 | // test string values
118 | pb.validate({i:'abcde'},function(err,res){
119 | assert.isNull(err)
120 | })
121 |
122 | pb.validate({i:'abcdefgh'},function(err,res){
123 | assert.isNotNull(err)
124 | assert.equal(err.parambulator.code,'maxlen$')
125 | })
126 |
127 | // test arrays
128 | pb.validate({i:[1,2,3,4,5]},function(err,res){
129 | assert.isNull(err)
130 | })
131 |
132 | pb.validate({i:[1,2,3,4,5,6,7]},function(err,res){
133 | assert.isNotNull(err)
134 | assert.equal(err.parambulator.code,'maxlen$')
135 | })
136 |
137 | // test objects
138 | pb.validate({i:{1:1, 2:2, 3:3, 4:4, 5:5}},function(err,res){
139 | assert.isNull(err)
140 | })
141 |
142 | pb.validate({i:{1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7}},function(err,res){
143 | assert.isNotNull(err)
144 | assert.equal(err.parambulator.code,'maxlen$')
145 | })
146 |
147 | })
148 |
149 |
150 | it('lt$', function() {
151 | pb.validate({j: 1},function(err,lt){
152 | assert.isNull(err)
153 | })
154 |
155 | pb.validate({j: 3}, function(err,res){
156 | assert.isNotNull(err)
157 | assert.equal(err.parambulator.code, 'lt$')
158 | })
159 |
160 | pb.validate({k: new Date("2012-09-03")}, function(err,res) {
161 | assert.isNull(err)
162 | })
163 |
164 | pb.validate({k: new Date("2012-09-04")}, function(err,res) {
165 | assert.isNotNull(err)
166 | assert.equal(err.parambulator.code, 'lt$')
167 | })
168 | })
169 |
170 |
171 | it('lte$', function() {
172 | pb.validate({l: 1},function(err,lt){
173 | assert.isNull(err)
174 | })
175 |
176 | pb.validate({l: 2},function(err,lt){
177 | assert.isNull(err)
178 | })
179 |
180 | pb.validate({l: 3}, function(err,res){
181 | assert.isNotNull(err)
182 | assert.equal(err.parambulator.code, 'lte$')
183 | })
184 |
185 | pb.validate({m: new Date("2012-09-04")}, function(err,res) {
186 | assert.isNull(err)
187 | })
188 |
189 | pb.validate({m: new Date("2012-09-05")}, function(err,res) {
190 | assert.isNotNull(err)
191 | assert.equal(err.parambulator.code, 'lte$')
192 | })
193 | })
194 |
195 |
196 | it('gt$', function() {
197 | pb.validate({n: 3},function(err,lt){
198 | assert.isNull(err)
199 | })
200 |
201 | pb.validate({n: 2}, function(err,res){
202 | assert.isNotNull(err)
203 | assert.equal(err.parambulator.code, 'gt$')
204 | })
205 |
206 | pb.validate({o: new Date("2012-09-05")}, function(err,res) {
207 | assert.isNull(err)
208 | })
209 |
210 | pb.validate({o: new Date("2012-09-04")}, function(err,res) {
211 | assert.isNotNull(err)
212 | assert.equal(err.parambulator.code, 'gt$')
213 | })
214 | })
215 |
216 |
217 | it('gte$', function() {
218 | pb.validate({p: 2},function(err,lt){
219 | assert.isNull(err)
220 | })
221 |
222 | pb.validate({p: 3},function(err,lt){
223 | assert.isNull(err)
224 | })
225 |
226 | pb.validate({p: 1}, function(err,res){
227 | assert.isNotNull(err)
228 | assert.equal(err.parambulator.code, 'gte$')
229 | })
230 |
231 | pb.validate({q: new Date("2012-09-04")},function(err,res) {
232 | assert.isNull(err)
233 | })
234 |
235 | pb.validate({q: new Date("2012-09-03")},function(err,res) {
236 | assert.isNotNull(err)
237 | assert.equal(err.parambulator.code, 'gte$')
238 | })
239 | })
240 |
241 |
242 | it('min$', function() {
243 | pb.validate({r: 2},function(err,lt){
244 | assert.isNull(err)
245 | })
246 |
247 | pb.validate({r: 3},function(err,lt){
248 | assert.isNull(err)
249 | })
250 |
251 | pb.validate({r: 1}, function(err,res){
252 | assert.isNotNull(err)
253 | assert.equal(err.parambulator.code, 'min$')
254 | })
255 |
256 | pb.validate({s: new Date("2012-09-04")},function(err,res) {
257 | assert.isNull(err)
258 | })
259 |
260 | pb.validate({s: new Date("2012-09-03")},function(err,res) {
261 | assert.isNotNull(err)
262 | assert.equal(err.parambulator.code, 'min$')
263 | })
264 | })
265 |
266 |
267 | it('max$', function() {
268 | pb.validate({t: 1},function(err,lt){
269 | assert.isNull(err)
270 | })
271 |
272 | pb.validate({t: 2},function(err,lt){
273 | assert.isNull(err)
274 | })
275 |
276 | pb.validate({t: 3},function(err,res){
277 | assert.isNotNull(err)
278 | assert.equal(err.parambulator.code, 'max$')
279 | })
280 |
281 | pb.validate({u: new Date("2012-09-04")},function(err,res){
282 | assert.isNull(err)
283 | })
284 |
285 | pb.validate({u: new Date("2012-09-05")},function(err,res){
286 | assert.isNotNull(err)
287 | assert.equal(err.parambulator.code, 'max$')
288 | })
289 | })
290 |
291 |
292 | it('uniq$', function() {
293 | pb.validate({v: [1,2,3]},function(err,res){
294 | assert.isNull(err)
295 | })
296 |
297 | pb.validate({v: [1,2,3,1]},function(err,res){
298 | assert.isNotNull(err)
299 | assert.equal(err.parambulator.code, 'uniq$')
300 | })
301 | })
302 |
303 |
304 | it('re$', function() {
305 | pb.validate({e:'ez'},function(err,res){
306 | assert.isNull(err)
307 | })
308 |
309 | pb.validate({e:'ex'},function(err,res){
310 | //console.log(err)
311 | assert.isNotNull(err)
312 | assert.equal(err.parambulator.code,'re$')
313 | })
314 |
315 | pb.validate({f:'FZ'},function(err,res){
316 | assert.isNull(err)
317 | })
318 |
319 | pb.validate({f:'fx'},function(err,res){
320 | assert.isNotNull(err)
321 | assert.equal(err.parambulator.code,'re$')
322 | })
323 | })
324 |
325 |
326 | it('enum$', function() {
327 | pb.validate({g:'A'},function(err,res){
328 | assert.isNull(err)
329 | })
330 |
331 | pb.validate({g:'X'},function(err,res){
332 | //console.log(err)
333 | assert.isNotNull(err)
334 | assert.equal(err.parambulator.code,'enum$')
335 | })
336 | })
337 |
338 |
339 | it('toplevel', function() {
340 |
341 | var pb = parambulator({
342 | type$:'object'
343 | })
344 |
345 | pb.validate({},function(err){
346 | assert.isNull(err)
347 | })
348 |
349 | pb.validate("foo",function(err){
350 | assert.isNotNull(err)
351 | })
352 | })
353 |
354 | })
355 |
356 |
357 |
358 |
--------------------------------------------------------------------------------
/doc/docco.css:
--------------------------------------------------------------------------------
1 | /*--------------------- Typography ----------------------------*/
2 |
3 | @font-face {
4 | font-family: 'aller-light';
5 | src: url('public/fonts/aller-light.eot');
6 | src: url('public/fonts/aller-light.eot?#iefix') format('embedded-opentype'),
7 | url('public/fonts/aller-light.woff') format('woff'),
8 | url('public/fonts/aller-light.ttf') format('truetype');
9 | font-weight: normal;
10 | font-style: normal;
11 | }
12 |
13 | @font-face {
14 | font-family: 'aller-bold';
15 | src: url('public/fonts/aller-bold.eot');
16 | src: url('public/fonts/aller-bold.eot?#iefix') format('embedded-opentype'),
17 | url('public/fonts/aller-bold.woff') format('woff'),
18 | url('public/fonts/aller-bold.ttf') format('truetype');
19 | font-weight: normal;
20 | font-style: normal;
21 | }
22 |
23 | @font-face {
24 | font-family: 'roboto-black';
25 | src: url('public/fonts/roboto-black.eot');
26 | src: url('public/fonts/roboto-black.eot?#iefix') format('embedded-opentype'),
27 | url('public/fonts/roboto-black.woff') format('woff'),
28 | url('public/fonts/roboto-black.ttf') format('truetype');
29 | font-weight: normal;
30 | font-style: normal;
31 | }
32 |
33 | /*--------------------- Layout ----------------------------*/
34 | html { height: 100%; }
35 | body {
36 | font-family: "aller-light";
37 | font-size: 14px;
38 | line-height: 18px;
39 | color: #30404f;
40 | margin: 0; padding: 0;
41 | height:100%;
42 | }
43 | #container { min-height: 100%; }
44 |
45 | a {
46 | color: #000;
47 | }
48 |
49 | b, strong {
50 | font-weight: normal;
51 | font-family: "aller-bold";
52 | }
53 |
54 | p {
55 | margin: 15px 0 0px;
56 | }
57 | .annotation ul, .annotation ol {
58 | margin: 25px 0;
59 | }
60 | .annotation ul li, .annotation ol li {
61 | font-size: 14px;
62 | line-height: 18px;
63 | margin: 10px 0;
64 | }
65 |
66 | h1, h2, h3, h4, h5, h6 {
67 | color: #112233;
68 | line-height: 1em;
69 | font-weight: normal;
70 | font-family: "roboto-black";
71 | text-transform: uppercase;
72 | margin: 30px 0 15px 0;
73 | }
74 |
75 | h1 {
76 | margin-top: 40px;
77 | }
78 | h2 {
79 | font-size: 1.26em;
80 | }
81 |
82 | hr {
83 | border: 0;
84 | background: 1px #ddd;
85 | height: 1px;
86 | margin: 20px 0;
87 | }
88 |
89 | pre, tt, code {
90 | font-size: 12px; line-height: 16px;
91 | font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace;
92 | margin: 0; padding: 0;
93 | }
94 | .annotation pre {
95 | display: block;
96 | margin: 0;
97 | padding: 7px 10px;
98 | background: #fcfcfc;
99 | -moz-box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
100 | -webkit-box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
101 | box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
102 | overflow-x: auto;
103 | }
104 | .annotation pre code {
105 | border: 0;
106 | padding: 0;
107 | background: transparent;
108 | }
109 |
110 |
111 | blockquote {
112 | border-left: 5px solid #ccc;
113 | margin: 0;
114 | padding: 1px 0 1px 1em;
115 | }
116 | .sections blockquote p {
117 | font-family: Menlo, Consolas, Monaco, monospace;
118 | font-size: 12px; line-height: 16px;
119 | color: #999;
120 | margin: 10px 0 0;
121 | white-space: pre-wrap;
122 | }
123 |
124 | ul.sections {
125 | list-style: none;
126 | padding:0 0 5px 0;;
127 | margin:0;
128 | }
129 |
130 | /*
131 | Force border-box so that % widths fit the parent
132 | container without overlap because of margin/padding.
133 |
134 | More Info : http://www.quirksmode.org/css/box.html
135 | */
136 | ul.sections > li > div {
137 | -moz-box-sizing: border-box; /* firefox */
138 | -ms-box-sizing: border-box; /* ie */
139 | -webkit-box-sizing: border-box; /* webkit */
140 | -khtml-box-sizing: border-box; /* konqueror */
141 | box-sizing: border-box; /* css3 */
142 | }
143 |
144 |
145 | /*---------------------- Jump Page -----------------------------*/
146 | #jump_to, #jump_page {
147 | margin: 0;
148 | background: white;
149 | -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
150 | -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
151 | font: 16px Arial;
152 | cursor: pointer;
153 | text-align: right;
154 | list-style: none;
155 | }
156 |
157 | #jump_to a {
158 | text-decoration: none;
159 | }
160 |
161 | #jump_to a.large {
162 | display: none;
163 | }
164 | #jump_to a.small {
165 | font-size: 22px;
166 | font-weight: bold;
167 | color: #676767;
168 | }
169 |
170 | #jump_to, #jump_wrapper {
171 | position: fixed;
172 | right: 0; top: 0;
173 | padding: 10px 15px;
174 | margin:0;
175 | }
176 |
177 | #jump_wrapper {
178 | display: none;
179 | padding:0;
180 | }
181 |
182 | #jump_to:hover #jump_wrapper {
183 | display: block;
184 | }
185 |
186 | #jump_page_wrapper{
187 | position: fixed;
188 | right: 0;
189 | top: 0;
190 | bottom: 0;
191 | }
192 |
193 | #jump_page {
194 | padding: 5px 0 3px;
195 | margin: 0 0 25px 25px;
196 | max-height: 100%;
197 | overflow: auto;
198 | }
199 |
200 | #jump_page .source {
201 | display: block;
202 | padding: 15px;
203 | text-decoration: none;
204 | border-top: 1px solid #eee;
205 | }
206 |
207 | #jump_page .source:hover {
208 | background: #f5f5ff;
209 | }
210 |
211 | #jump_page .source:first-child {
212 | }
213 |
214 | /*---------------------- Low resolutions (> 320px) ---------------------*/
215 | @media only screen and (min-width: 320px) {
216 | .pilwrap { display: none; }
217 |
218 | ul.sections > li > div {
219 | display: block;
220 | padding:5px 10px 0 10px;
221 | }
222 |
223 | ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol {
224 | padding-left: 30px;
225 | }
226 |
227 | ul.sections > li > div.content {
228 | overflow-x:auto;
229 | -webkit-box-shadow: inset 0 0 5px #e5e5ee;
230 | box-shadow: inset 0 0 5px #e5e5ee;
231 | border: 1px solid #dedede;
232 | margin:5px 10px 5px 10px;
233 | padding-bottom: 5px;
234 | }
235 |
236 | ul.sections > li > div.annotation pre {
237 | margin: 7px 0 7px;
238 | padding-left: 15px;
239 | }
240 |
241 | ul.sections > li > div.annotation p tt, .annotation code {
242 | background: #f8f8ff;
243 | border: 1px solid #dedede;
244 | font-size: 12px;
245 | padding: 0 0.2em;
246 | }
247 | }
248 |
249 | /*---------------------- (> 481px) ---------------------*/
250 | @media only screen and (min-width: 481px) {
251 | #container {
252 | position: relative;
253 | }
254 | body {
255 | background-color: #F5F5FF;
256 | font-size: 15px;
257 | line-height: 21px;
258 | }
259 | pre, tt, code {
260 | line-height: 18px;
261 | }
262 | p, ul, ol {
263 | margin: 0 0 15px;
264 | }
265 |
266 |
267 | #jump_to {
268 | padding: 5px 10px;
269 | }
270 | #jump_wrapper {
271 | padding: 0;
272 | }
273 | #jump_to, #jump_page {
274 | font: 10px Arial;
275 | text-transform: uppercase;
276 | }
277 | #jump_page .source {
278 | padding: 5px 10px;
279 | }
280 | #jump_to a.large {
281 | display: inline-block;
282 | }
283 | #jump_to a.small {
284 | display: none;
285 | }
286 |
287 |
288 |
289 | #background {
290 | position: absolute;
291 | top: 0; bottom: 0;
292 | width: 350px;
293 | background: #fff;
294 | border-right: 1px solid #e5e5ee;
295 | z-index: -1;
296 | }
297 |
298 | ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol {
299 | padding-left: 40px;
300 | }
301 |
302 | ul.sections > li {
303 | white-space: nowrap;
304 | }
305 |
306 | ul.sections > li > div {
307 | display: inline-block;
308 | }
309 |
310 | ul.sections > li > div.annotation {
311 | max-width: 350px;
312 | min-width: 350px;
313 | min-height: 5px;
314 | padding: 13px;
315 | overflow-x: hidden;
316 | white-space: normal;
317 | vertical-align: top;
318 | text-align: left;
319 | }
320 | ul.sections > li > div.annotation pre {
321 | margin: 15px 0 15px;
322 | padding-left: 15px;
323 | }
324 |
325 | ul.sections > li > div.content {
326 | padding: 13px;
327 | vertical-align: top;
328 | border: none;
329 | -webkit-box-shadow: none;
330 | box-shadow: none;
331 | }
332 |
333 | .pilwrap {
334 | position: relative;
335 | display: inline;
336 | }
337 |
338 | .pilcrow {
339 | font: 12px Arial;
340 | text-decoration: none;
341 | color: #454545;
342 | position: absolute;
343 | top: 3px; left: -20px;
344 | padding: 1px 2px;
345 | opacity: 0;
346 | -webkit-transition: opacity 0.2s linear;
347 | }
348 | .for-h1 .pilcrow {
349 | top: 47px;
350 | }
351 | .for-h2 .pilcrow, .for-h3 .pilcrow, .for-h4 .pilcrow {
352 | top: 35px;
353 | }
354 |
355 | ul.sections > li > div.annotation:hover .pilcrow {
356 | opacity: 1;
357 | }
358 | }
359 |
360 | /*---------------------- (> 1025px) ---------------------*/
361 | @media only screen and (min-width: 1025px) {
362 |
363 | body {
364 | font-size: 16px;
365 | line-height: 24px;
366 | }
367 |
368 | #background {
369 | width: 525px;
370 | }
371 | ul.sections > li > div.annotation {
372 | max-width: 525px;
373 | min-width: 525px;
374 | padding: 10px 25px 1px 50px;
375 | }
376 | ul.sections > li > div.content {
377 | padding: 9px 15px 16px 25px;
378 | }
379 | }
380 |
381 | /*---------------------- Syntax Highlighting -----------------------------*/
382 |
383 | td.linenos { background-color: #f0f0f0; padding-right: 10px; }
384 | span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
385 | /*
386 |
387 | github.com style (c) Vasily Polovnyov
388 |
389 | */
390 |
391 | pre code {
392 | display: block; padding: 0.5em;
393 | color: #000;
394 | background: #f8f8ff
395 | }
396 |
397 | pre .hljs-comment,
398 | pre .hljs-template_comment,
399 | pre .hljs-diff .hljs-header,
400 | pre .hljs-javadoc {
401 | color: #408080;
402 | font-style: italic
403 | }
404 |
405 | pre .hljs-keyword,
406 | pre .hljs-assignment,
407 | pre .hljs-literal,
408 | pre .hljs-css .hljs-rule .hljs-keyword,
409 | pre .hljs-winutils,
410 | pre .hljs-javascript .hljs-title,
411 | pre .hljs-lisp .hljs-title,
412 | pre .hljs-subst {
413 | color: #954121;
414 | /*font-weight: bold*/
415 | }
416 |
417 | pre .hljs-number,
418 | pre .hljs-hexcolor {
419 | color: #40a070
420 | }
421 |
422 | pre .hljs-string,
423 | pre .hljs-tag .hljs-value,
424 | pre .hljs-phpdoc,
425 | pre .hljs-tex .hljs-formula {
426 | color: #219161;
427 | }
428 |
429 | pre .hljs-title,
430 | pre .hljs-id {
431 | color: #19469D;
432 | }
433 | pre .hljs-params {
434 | color: #00F;
435 | }
436 |
437 | pre .hljs-javascript .hljs-title,
438 | pre .hljs-lisp .hljs-title,
439 | pre .hljs-subst {
440 | font-weight: normal
441 | }
442 |
443 | pre .hljs-class .hljs-title,
444 | pre .hljs-haskell .hljs-label,
445 | pre .hljs-tex .hljs-command {
446 | color: #458;
447 | font-weight: bold
448 | }
449 |
450 | pre .hljs-tag,
451 | pre .hljs-tag .hljs-title,
452 | pre .hljs-rules .hljs-property,
453 | pre .hljs-django .hljs-tag .hljs-keyword {
454 | color: #000080;
455 | font-weight: normal
456 | }
457 |
458 | pre .hljs-attribute,
459 | pre .hljs-variable,
460 | pre .hljs-instancevar,
461 | pre .hljs-lisp .hljs-body {
462 | color: #008080
463 | }
464 |
465 | pre .hljs-regexp {
466 | color: #B68
467 | }
468 |
469 | pre .hljs-class {
470 | color: #458;
471 | font-weight: bold
472 | }
473 |
474 | pre .hljs-symbol,
475 | pre .hljs-ruby .hljs-symbol .hljs-string,
476 | pre .hljs-ruby .hljs-symbol .hljs-keyword,
477 | pre .hljs-ruby .hljs-symbol .hljs-keymethods,
478 | pre .hljs-lisp .hljs-keyword,
479 | pre .hljs-tex .hljs-special,
480 | pre .hljs-input_number {
481 | color: #990073
482 | }
483 |
484 | pre .hljs-builtin,
485 | pre .hljs-constructor,
486 | pre .hljs-built_in,
487 | pre .hljs-lisp .hljs-title {
488 | color: #0086b3
489 | }
490 |
491 | pre .hljs-preprocessor,
492 | pre .hljs-pi,
493 | pre .hljs-doctype,
494 | pre .hljs-shebang,
495 | pre .hljs-cdata {
496 | color: #999;
497 | font-weight: bold
498 | }
499 |
500 | pre .hljs-deletion {
501 | background: #fdd
502 | }
503 |
504 | pre .hljs-addition {
505 | background: #dfd
506 | }
507 |
508 | pre .hljs-diff .hljs-change {
509 | background: #0086b3
510 | }
511 |
512 | pre .hljs-chunk {
513 | color: #aaa
514 | }
515 |
516 | pre .hljs-tex .hljs-formula {
517 | opacity: 0.5;
518 | }
519 |
--------------------------------------------------------------------------------
/parambulator-min.js:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2012-2015 Richard Rodger, MIT License */
2 | (function(){"use strict";function e(e){var r=e.rule.spec;h.isArray(r)||(r=[""+r]);var t=[];return h.each(r,function(r){r.match(/[*?]/)?t=t.concat(h.keys(m(r).on(e.point))):t.push(r)}),t}function r(){return!0}function t(){return!1}function n(e){return h.isUndefined(e)||h.isNull(e)}function a(e){var r={rules:e.rules,point:e.point,msgs:e.msgs,log:e.log,parents:e.parents,util:e.util};return r}function u(e,r){var t=r||"top level";return 00&&t.multiErrors)n(v,{log:e.log});else{var l=v.length>0?v[0]:null;n(l,{log:e.log})}}if(h.isUndefined(r))return o("no_input$",e,n);var u=e.rules;a(0)}function l(e,r){for(var t in f){var n=f[t],a=e.point;for(var u in n.pathnames){var i=n.pathnames[u];if(h.has(a,i))a=a[i];else if(u==n.pathnames.length-1)a[i]=n.defaultvalue;else{var o,p=n.pathtypes[u];if("object"==p)o={};else{if("array"!=p)return void e.util.fail("default$",e,r);o=[]}a[i]=o,a=o}}}r()}var s,c=t.callbackmaker?t.callbackmaker(n):n||function(){},m=function(e){s=e,c.apply(null,arguments)},v=[],y={rules:d,point:r,msgs:g,log:[],parents:[]};return y.util={formatparents:function(){var e=$(arguments);return e[1]=t&&t.topname&&!e[1]?t.topname:e[1],u.apply(null,e)},msgmods:function(e){return(t.msgprefix||"")+e+(t.msgsuffix||"")},fail:o,proplist:e,execrules:p,clone:a},l(y,function(e){e?m(e,{log:y.log}):p(y,function(e){m(e,{log:y.log})})}),s},c}var s=this,c=s.parambulator,f="undefined"!=typeof require,h=s._,m=s.gex,v=s.jsonic;if("undefined"==typeof h){if(!f)throw new Error("parambulator requires underscore, see http://underscorejs.org");h=require("lodash")}if("undefined"==typeof m){if(!f)throw new Error("parambulator requires gex, see http://github.com/rjrodger/gex");m=require("gex")}if("undefined"==typeof v){if(!f)throw new Error("parambulator requires jsonic, see http://github.com/rjrodger/jsonic");v=require("jsonic")}var $=function(){return Array.prototype.slice.call(arguments[0],arguments[1])},d=function(e,r){return r=r||"quantrule",function(r,t){r.prop=null;var n=r.util.proplist(r),a=0;return h.each(n,function(e){a+=r.point[e]?1:0}),e(a)?t():(r.value=""+n,r.util.fail(r,t))}},g=function(e){return function(r,t){var n=r.rule.spec,a=r.point;if(!h.isUndefined(a)){var u=h.isObject(a)?Object.keys(a).length:a.length;if(!h.isUndefined(u)&&!e(u,n))return r.util.fail(r,t)}return t()}},y=function(e,r,t){return t=t||"childrule",function(t,n){for(var a=t.util.proplist(t),u=0;u=e},"atmostone$"),exactlyone$:d(function(e){return 1==e},"exactlyone$"),atleastone$:d(function(e){return e>=1},"atleastone$"),required$:y(function(e,r,t){return!h.isUndefined(t)},t,"required$"),notempty$:y(function(e,r,t){return!h.isUndefined(t)&&!h.isNull(t)&&""!==t},r,"notempty$"),string$:y(function(e,r,t){return n(t)||h.isString(t)},r,"string$"),integer$:y(function(e,r,t){return n(t)||h.isNumber(t)&&t===(0|t)},r,"integer$"),number$:y(function(e,r,t){return n(t)||h.isNumber(t)},r,"number$"),boolean$:y(function(e,r,t){return n(t)||h.isBoolean(t)},r,"boolean$"),date$:y(function(e,r,t){return n(t)||h.isDate(t)},r,"date$"),array$:y(function(e,r,t){return n(t)||h.isArray(t)},r,"array$"),object$:y(function(e,r,t){return n(t)||h.isObject(t)&&!h.isArray(t)},r,"object$"),function$:y(function(e,r,t){return n(t)||h.isFunction(t)},r,"function$"),lt$:b(function(e,r){return r>e}),lte$:b(function(e,r){return r>=e}),gt$:b(function(e,r){return e>r}),gte$:b(function(e,r){return e>=r}),min$:b(function(e,r){return e>=r}),max$:b(function(e,r){return r>=e}),uniq$:function(e,r){for(var t=e.point,n={},a=0;a=r}),maxlen$:g(function(e,r){return r>=e}),re$:function(e,r){var t=e.point;if(!h.isUndefined(t)){t=""+t;var n=e.rule.spec,a=void 0,u=/^\/(.*)\/(\w*)$/.exec(e.rule.spec);u&&(n=u[1],a=u[2]);var i=new RegExp(n,a);if(!i.exec(t))return e.util.fail(e,r)}return r()},type$:function(e,r){var t=e.util.proplist(e),n={string:h.isString,number:h.isNumber,integer:function(e){return h.isNumber(e)&&e===(0|e)},"boolean":h.isBoolean,date:h.isDate,array:h.isArray,object:function(e){return h.isObject(e)&&!h.isArray(e)&&!h.isDate(e)},"function":function(e){return h.isFunction(e)}},a=0;return h.each(t,function(r){var t=n[r.toLowerCase()];t&&(a+=t(e.point))}),a?r():e.util.fail(e,r)},format$:function(e,r){var t=e.util.proplist(e),n={datetime:function(e){return/\d{4}-(0[1-9]|1[1-2])-([0-2]\d|3[0-1])T([0-1]\d|2[0-4]):[0-5]\d:[0-5]\dZ/.test(e)},date:function(e){return/\d{4}-[0-1][0-2]-[0-2]\d/.test(e)},time:function(e){return/([0-1]\d|2[0-4]):[0-5]\d:[0-5]\dZ/.test(e)},utcmillisec:h.isNumber,re:h.isRegExp},a=0;return h.each(t,function(r){var t=n[r.toLowerCase()];t&&(a+=t(e.point))}),a?r():e.util.fail(e,r)},default$:function(e,r){return r()},enum$:function(e,r){var t=e.point,n=e.rule.spec,a=[];h.isArray(t)?a=t:a[0]=t;var u=0;return a&&h.each(a,function(e){u+=-1==n.indexOf(e)}),u?e.util.fail(e,r):r()},iterate$:function(e,r){function t(u){if(u' (parent: <%=parentpath%>).",exactlyone$:"Exactly one of these properties must be used: '<%=value%>' (parent: <%=parentpath%>).",atleastone$:"At least one of these properties is required: '<%=value%>' (parent: <%=parentpath%>).",required$:"The property '<%=property%>' is missing and is always required (parent: <%=parentpath%>).",notempty$:"The property '<%=property%>' requires a value (parent: <%=parentpath%>).",string$:"The property '<%=property%>', with current value: '<%=value%>', must be a string (parent: <%=parentpath%>).",integer$:"The property '<%=property%>', with current value: '<%=value%>', must be a integer (parent: <%=parentpath%>).",number$:"The property '<%=property%>', with current value: '<%=value%>', must be a number (parent: <%=parentpath%>).",boolean$:"The property '<%=property%>', with current value: '<%=value%>', must be a boolean (parent: <%=parentpath%>).",date$:"The property '<%=property%>', with current value: '<%=value%>', must be a date (parent: <%=parentpath%>).",array$:"The property '<%=property%>', with current value: '<%=value%>', must be a array (parent: <%=parentpath%>).",object$:"The property '<%=property%>', with current value: '<%=value%>', must be a object (parent: <%=parentpath%>).",function$:"The property '<%=property%>', with current value: '<%=value%>', must be a function (parent: <%=parentpath%>).",only$:"The property '<%=property%>' is not recognised here. Recognised properties are: <%=rule.spec%> (parent: <%=parentpath%>).",wild$:"The value <%=value%> does not match the expression '<%=rule.spec%>' (parent: <%=parentpath%>).",re$:"The value <%=value%> does not match the regular expression <%=rule.spec%> (parent: <%=parentpath%>).",type$:"The value <%=value%> is not of type '<%=rule.spec%>' (parent: <%=parentpath%>).",format$:"The value <%=value%> is not of format '<%=rule.spec%>' (parent: <%=parentpath%>).",minlen$:"The property '<%=property%>', with current value: '<%=value%>', must have minimum length '<%=rule.spec%>' (parent: <%=parentpath%>).",maxlen$:"The property '<%=property%>', with current value: '<%=value%>', must have maximum length '<%=rule.spec%>' (parent: <%=parentpath%>).",eq$:"The value <%=value%> does not equal '<%=rule.spec%>' (parent: <%=parentpath%>).",lt$:"The value <%=value%> is not less than '<%=rule.spec%>' (parent: <%=parentpath%>).",lte$:"The value <%=value%> is not less than or equal with '<%=rule.spec%>' (parent: <%=parentpath%>).",gt$:"The value <%=value%> is not greater than '<%=rule.spec%>' (parent: <%=parentpath%>).",gte$:"The value <%=value%> is not not greater than or equal with '<%=rule.spec%>' (parent: <%=parentpath%>).",min$:"The value <%=value%> is not not greater than or equal with '<%=rule.spec%>' (parent: <%=parentpath%>).",max$:"The value <%=value%> is not less than or equal with '<%=rule.spec%>' (parent: <%=parentpath%>).",uniq$:"The value <%=value%> has duplicate elements.",enum$:"The value <%=value%> must be one of '<%=rule.spec%>' (parent: <%=parentpath%>)."},T=function(){var e=$(arguments);return l.apply(this,e)};T.ownparams=new l({"**":{strings$:["required$","notempty$","atmostone$","exactlyone$","atleastone$"],list$:[["prop$",{name:"wild$",rules:{type$:"string"}}],["prop$",{name:"type$",rules:{type$:["string","array"]}}],["prop$",{name:"format$",rules:{type$:["string","array"]}}],["prop$",{name:"re$",rules:{type$:"string"}}],["prop$",{name:"type$",rules:{enum$:["string","number","integer","boolean","date","array","object"]}}],["prop$",{name:"format$",rules:{enum$:["datetime","date","time","utcmillisec","re"]}}],["prop$",{name:"minlen$",rules:{type$:"number"}}],["prop$",{name:"maxlen$",rules:{type$:"number"}}],["prop$",{name:"enum$",rules:{type$:"array"}}],["prop$",{name:"list$",rules:{type$:"array"}}],["prop$",{name:"uniq$",rules:{type$:"array"}}]]}},{__ownparams__:!0,rules:{strings$:function(e,r){var t=e.rule.spec;h.isArray(t)||(t=[""+t]);for(var n=0;n rule needs a string or array of strings (property: <%=parentpath%>)."},topname:"spec"}),T.ownprefs=new l({object$:["valid","rules","msgs"],string$:["topname","msgprefix","msgsuffix"],boolean$:["multiErrors"],function$:["callbackmaker"],only$:["valid","rules","msgs","topname","msgprefix","msgsuffix","multiErrors","callbackmaker"]},{topname:"prefs"}),T.Parambulator=l,s.parambulator=T,T.noConflict=function(){return s.previous_parambulator=c,self},"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=T),exports.parambulator=T):s.parambulator=T}).call(this);
3 | //# sourceMappingURL=parambulator-min.map
--------------------------------------------------------------------------------
/parambulator-min.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"parambulator-min.js","sources":["parambulator.js"],"names":["proplist","ctxt","pn","rule","spec","_","isArray","all","each","n","match","concat","keys","gex","on","point","push","truefn","falsefn","noval","v","isUndefined","isNull","clone","newctxt","rules","msgs","log","parents","util","formatparents","topname","out","length","map","p","prop","join","killcircles","seen","k","contains","fail","code","arguments","cb","name","Error","inserts","property","value","JSON","stringify","parentpath","json","msg","isFunction","msgmods","template","err","parambulator","expected","proporder","obj","pc","indexOf","substring","Parambulator","pref","parsedefault","path","pathtypes","innerulenames","currentruletype","hasOwnProperty","defaultrule","pathnames","splice","defaultvalue","defaultrules","isObject","index","inner_rule","newpath","slice","newpathtypes","buildrule","rulespec","func","rulemap","parse","names","i","rs","subrules","multiErrors","item","item_rule","build_rule","starstar_subrules","starstar_rule","isBoolean","prop_subrules","prop_rule","isString","rulespecs","split","isNumber","self","jsonic","exp","ownprefs","validate","valid","prefparams","**","toString","extend","msgsmap","args","execrules","execrule","ruleI","specstr","errors","validatedefaults","ruleindex","location","has","newobj","type","reterr","callback","callbackmaker","wrapcb","apply","arrayify","msgprefix","msgsuffix","root","this","previous_parambulator","has_require","require","Array","prototype","call","quantrule","pass","rulename","found","lenrule","len","valuelen","Object","childrule","noneok","valrule","atmostone$","f","exactlyone$","atleastone$","required$","notempty$","string$","integer$","number$","boolean$","date$","isDate","array$","object$","function$","lt$","lte$","gt$","gte$","min$","max$","uniq$","o","only$","include","wild$","eq$","minlen$","conditionlen","maxlen$","re$","redef","reopt","m","exec","re","RegExp","type$","checkmap","string","number","integer","boolean","date","array","object","function","check","toLowerCase","format$","datetime","test","time","utcmillisec","isRegExp","default$","enum$","okvals","avalue","iserror","iterate$","eachprop","propI","psubctxt","subctxt","range","recurse$","recurse","eachpropctxt","recurctxt","$","descend$","no_input$","ownparams","strings$","list$","__ownparams__","pI","val","noConflict","exports","module"],"mappings":"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"}
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # parambulator - Node.js module
2 |
3 |
4 | A simple way to generate nice error messages for named parameters.
5 |
6 | This module is used by the Seneca framework for input validation.
7 |
8 | This module works on both Node.js and browsers.
9 |
10 | If you're using this module, feel free to contact me on twitter if you
11 | have any questions! :) [@rjrodger](http://twitter.com/rjrodger)
12 |
13 | [](https://gitter.im/rjrodger/parambulator)
14 |
15 | Current Version: 1.5.2
16 |
17 | [](https://travis-ci.org/rjrodger/parambulator)
18 |
19 | [Annotated Source](http://rjrodger.github.io/parambulator/doc/parambulator.html)
20 |
21 |
22 | # Usage
23 |
24 | Use this module to validate input or configuration parameters provided
25 | as JSON. You can ensure that the JSON structure and data types are
26 | what you need. You'll get friendly, useful error messages for your
27 | users, and that makes your API better!
28 |
29 | ```javascript
30 | var parambulator = require('parambulator')
31 |
32 | var paramcheck = parambulator({
33 | price: {type$:'number'}
34 | })
35 |
36 | // this passes
37 | paramcheck.validate( { price: 10.99 }, function(err) { console.log(err) } )
38 |
39 | // this fails - price should be a number
40 | paramcheck.validate( { price: 'free!' }, function(err) { console.log(err) } )
41 | // output: The value 'free!' is not of type 'number' (parent: price).
42 | ```
43 |
44 | _Why?_
45 |
46 | You're writing a module and you accept configuration as a structured
47 | JavaScript object. For example, opening a database connection:
48 | [MongoDB driver](http://mongodb.github.com/node-mongodb-native/api-generated/server.html). Or
49 | you want to have named parameters:
50 | [http.request](http://nodejs.org/api/http.html#http_http_request_options_callback).
51 |
52 | It's nice to be able to validate the input and provide useful error messages, without hand-coding the validation.
53 |
54 |
55 | _But What About JSONSchema!_
56 |
57 | Yes, [JSONSchema](http://json-schema.org) would be the proper way to do this. But the syntax is too hard, and the error messages aren't friendly. This is a [Worse is Better!](http://www.jwz.org/doc/worse-is-better.html) approach.
58 |
59 | There's also a philosophical difference. JSONSchema defines a formal structure, so you need to be fairly precise and complete. Parambulator defines a list of rules that are tested in the order you specify, and you can be vague and incomplete.
60 |
61 |
62 | Key Features:
63 |
64 | * Easy syntax, rules are tested in order
65 | * Add your own rules
66 | * Customize the error messages
67 |
68 | And yes Virginia, it does [validate its own input](http://en.wikipedia.org/wiki/Self-hosting).
69 |
70 |
71 | ## Installation
72 |
73 | npm install parambulator
74 |
75 | or
76 |
77 | bower install parambulator
78 |
79 |
80 | And in your code:
81 |
82 | var parambulator = require('parambulator')
83 |
84 | or
85 |
86 |
87 |
88 |
89 | ## Usage
90 |
91 | Import the module using the standard _require_ syntax:
92 |
93 | ```javascript
94 | var parambulator = require('parambulator')
95 | ```
96 |
97 | This is a function that creates _Parambulator_ object instances. This function accepts two arguments:
98 |
99 | * _spec_ - the rule specification to test against
100 | * _pref_ - your preferences, such as custom error messages and rules
101 |
102 | Example:
103 |
104 | ```javascript
105 | var paramcheck = parambulator({ price: {type$:'number'} })
106 | ```
107 |
108 |
109 | The _paramcheck_ variable is an instance of _Parambulator_. This object only has one method: _validate_, which accepts two arguments:
110 |
111 | * _args_: the object to validate
112 | * _cb_: a callback function, following the standard Node.js error convention (first arg is an Error)
113 |
114 | Example:
115 |
116 | ```javascript
117 | paramcheck.validate( { price: 10.99 }, function(err) { console.log(err) } )
118 | ```
119 |
120 |
121 | The callback function is called when the validation has completed. Processing of rules stops as soon as a rule fails. If validation fails, the first argument to the callback will be a standard JavaScript _Error_ object, with an error message in the _message_ property.
122 |
123 |
124 | ### Examples
125 |
126 | Heavily commented examples are provided in the _doc/examples_ folder: https://github.com/rjrodger/parambulator/tree/master/doc/examples
127 |
128 | You should probably read this rest of this first, though.
129 |
130 |
131 | ### Rules
132 |
133 | The validation rules are defined in the _spec_ argument to _parambulator_. The rules are specified as an object, the properties of which are the rule names, and the values the rule options, like so: `{required$:['foo','bar']}`. The rules are executed in the order that they appear (JavaScript preserves the order of object properties).
134 |
135 | Rule names always end with a `$` character. Properties that do not end with `$` are considered to be literal property names:
136 |
137 | ```javascript
138 | {
139 | required$: ['foo','bar'],
140 | foo: {
141 | type$: 'string'
142 | }
143 | }
144 | ```
145 |
146 | This specification requires the input object to have two properties, _foo_ and _bar_, and for the _foo_ property to have a string value. For example, this is valid:
147 |
148 | ```javascript
149 | { foo:'hello', bar:1 }
150 | ```
151 |
152 | But these are not:
153 | ```javascript
154 | { foo:1, bar:1 } // foo is not a string
155 | { foo:'hello' } // bar is missing
156 | ```
157 |
158 | The rules are evaluated in the order they appear:
159 |
160 | 1. at the current property (i.e. the top level), check for properties _foo_ and _bar_, as per `required$: ['foo','bar']`
161 | 2. descend into the _foo_ property, and check that it's value is of `type$: 'string'`
162 |
163 | You can nest rules within other rules. They will be evaluated in the order they appear, depth first.
164 |
165 | For each input property, the rules apply to the value or values within that property. This means that your rule specification mirrors the structure of the input object.
166 |
167 | For example, the specification:
168 |
169 | ```javascript
170 | {
171 | foo: {
172 | bar: { type$: 'integer' }
173 | }
174 | }
175 | ```
176 |
177 | matches
178 |
179 | ```javascript
180 | { foo: { bar: 1 } }
181 | ```
182 |
183 | but does not match
184 |
185 | ```javascript
186 | { bar: { foo: 1 } }
187 | ```
188 |
189 | In general, rules are permissive, in that they only apply if a given property is present. You need to use the _required$_ rule to require that a property is always present in the input.
190 |
191 | Each rule has a specific set of options relevant to that rule. For example, the _required$_ rule takes an array of property names. The type$ rule takes a string indicating the expected type: _string_, _number_, _boolean_, etc. For full details, see the rule descriptions below.
192 |
193 | Literal properties can also accept a wildcard string expression. For example:
194 |
195 | ```javascript
196 | { foo: "ba*" }
197 | ```
198 |
199 | This matches:
200 |
201 | ```javascript
202 | { foo: "ba" }
203 | { foo: "bar" }
204 | { foo: "barx" }
205 | ```
206 |
207 | but not
208 |
209 | ```javascript
210 | { foo: "b" }
211 | ```
212 |
213 |
214 |
215 | ### Wildcards
216 |
217 | Sometimes you don't know the property names in advance. To handle this case, you can also use wildcard expressions in literal properties:
218 |
219 | ```javascript
220 | { 'a*': { type$: 'boolean' } }
221 | ```
222 |
223 | This matches:
224 |
225 | ```javascript
226 | {
227 | a: true,
228 | ax: false,
229 | ayz: true
230 | }
231 | ```
232 |
233 | In particular, `'*'` on its own will match any property (at the same level). Wildcard expressions have the usual syntax: `*` means match anything, and `?` means match a single character.
234 |
235 |
236 | What about repeatedly nested rules? In this situation, you want to apply the same set of rules at any depth. You can use the special literal property `'**'` to achieve this:
237 |
238 | ```javascript
239 | { '**': { a: {type$: 'boolean' } } }
240 | ```
241 |
242 | This matches:
243 |
244 | ```javascript
245 | { a:true, x:{a:false, y:{a:true}}}
246 | ```
247 |
248 | ensuring that any properties called _a_ will be an integer. The recursive descent starts from the current level.
249 |
250 |
251 | ### Arrays
252 |
253 | Arrays are treated as if they were objects. The property names are simply the string values of the integer array indexes. For example:
254 |
255 | ```javascript
256 | { a: {'0':'first'} }
257 | ```
258 |
259 | This matches:
260 |
261 | ```javascript
262 | { a:['first'] }
263 | ```
264 |
265 | Due to a quirk in the Chrome V8 engine, the order of integer properties is not preserved. Use the special prefix `__` as a workaround:
266 |
267 | ```javascript
268 | { a: {'__1':'first', '__0':'second'} }
269 | ```
270 |
271 | This matches:
272 |
273 | ```javascript
274 | { a:['second','first'] }
275 | ```
276 |
277 | but the rules are tested in order:
278 |
279 | 1. `'__1':'first'`
280 | 2. `'__0':'second'`
281 |
282 |
283 |
284 | ### Custom Errors
285 |
286 | Each rule has an associated error message. By default these explain the reason why a rule failed, and give the property path (in standard JavaScript dot syntax: `foo.bar.baz`) of the offending value. You can customize these error messages, by providing your own string templates, or by providing a function that returns the error message text.
287 |
288 | Use the _msgs_ property of the _pref_ argument (the second argument to _parambulator_) to define custom error messages:
289 |
290 | ```javascript
291 | var pm = parambulator({...},{
292 | msgs: {
293 | required$: 'Property <%=property%> is required, yo!'
294 | }
295 | })
296 | ```
297 |
298 | The template syntax is provided by the _underscore_ module: http://underscorejs.org/#template
299 |
300 | The following properties are available:
301 |
302 | * _property_: the relevant property name
303 | * _value_: the string representation of the value that failed in some way
304 | * _point_: the actual value, which could be of any type, not just a string
305 | * _rule.name_: the name of the rule
306 | * _rule.spec_: the rule specification, e.g. `'boolean'` for rule `type$:'boolean'`
307 | * _parentpath_: a string locating the value in the input (properties in dot-syntax)
308 | * _json_: a reference to the JSON.stringify function, use like so: <%=json(rule.spec)%>
309 |
310 | The _parentpath_ will use the term _top level_ when the error concerns
311 | the top level of the input object. You can customize this term using the _topname_ option:
312 |
313 | ```javascript
314 | var pm = parambulator({...},{
315 | topname: 'name_of_param_in_my_function_definition'
316 | })
317 | ```
318 |
319 | You can also modify the error message using the _msgprefix_ and
320 | _msgsuffix_ options, which are prepended and appended to the message
321 | body, respectively, and also support the template syntax.
322 |
323 |
324 | You can also specify a custom error message using a function. This
325 | lets you customize on the specific failing conditions, such as the
326 | property name:
327 |
328 | ```javascript
329 | var pm = parambulator({...},{
330 | msgs: {
331 | required$: function(inserts){
332 | if( 'voodoo' == inserts.property ) {
333 | return "don't dare do: "+inserts.value
334 | }
335 | else {
336 | return 'Property '+inserts.property+' is required, yo!'
337 | }
338 | }
339 | }
340 | })
341 | ```
342 |
343 | The _inserts_ parameter is an object containing the properties as above.
344 |
345 |
346 | ## Rules
347 |
348 | The following rules are provided out-of-the-box. To define your own rules, see below.
349 |
350 | Each rule operates at the current _point_. This is the current property location inside the input object.
351 |
352 | For example, with input:
353 |
354 | ```javascript
355 | {
356 | foo: {
357 | bar: {
358 | baz: 'zzz'
359 | }
360 | }
361 | }
362 | ```
363 |
364 | the _point_ `foo.bar` is the object:
365 |
366 | ```javascript
367 | { baz: 'zzz'}
368 | ```
369 |
370 |
371 | ### literal property
372 |
373 | Match an input property. You can use wildcards. Accepts a set of sub rules, or a wildcard string to match against. The property names match against property names in the current point.
374 |
375 | ```javascript
376 | {
377 | a: { ... }
378 | 'b*': { ... }
379 | c: 'z*'
380 | }
381 | ```
382 |
383 |
384 | ### boolean rules
385 |
386 | As a convenience, rules that take a property name, such as _required$_, can be specified for a property using the form:
387 |
388 | ```javascript
389 | {
390 | foo: 'required$',
391 | bar: 'required$,string$'
392 | }
393 | ```
394 |
395 | To use a _$_ symbol literally, use the form:
396 |
397 | ```javascript
398 | {
399 | foo: { eq$:'text containing $' }
400 | }
401 | ```
402 |
403 |
404 | ### atmostone$
405 |
406 | Accept at most one of a list of properties. Accepts an array of property name strings. At most one of them can be present in the current point.
407 |
408 | ```javascript
409 | {
410 | atmostone$: ['foo','bar']
411 | }
412 | ```
413 |
414 |
415 | ### exactlyone$
416 |
417 | Accept exactly one of a list of properties. Accepts an array of property name strings. Exactly one of them must be present in the current point.
418 |
419 | ```javascript
420 | {
421 | exactlyone$: ['foo','bar']
422 | }
423 | ```
424 |
425 |
426 | ### atleastone$
427 |
428 | Accept at least one of a list of properties. Accepts an array of property name strings. At least one of them must be present in the current point.
429 |
430 | ```javascript
431 | {
432 | atleastone$: ['foo','bar']
433 | }
434 | ```
435 |
436 |
437 |
438 | ### required$
439 |
440 | Specify a set of required properties. Accepts an array of property name strings, or a single property name. Wildcards can be used. All properties must be present in the current point. Can also appear as a rule specification for literal properties.
441 |
442 |
443 | ```javascript
444 | { required$: ['foo','b*'] } // wildcards work too!
445 | { required$: 'bar' } // for convenience
446 | { bar: 'required$' } // for extra convenience
447 | { bar: {required$:true} } // for extra extra convenience
448 | { 'b*': 'required$' } // and that's just nice
449 | ```
450 |
451 |
452 | ### notempty$
453 |
454 | Specify a set of properties that cannot be empty, if they are present. Unlike _required$_, these properties can be absent altogether, so use _required$_ if they are also required! Accepts an array of property name strings, or a single property name. Wildcards can be used. All properties are relative to the current point. Can also appear as a rule specification for literal properties.
455 |
456 | ```javascript
457 | { notempty$: ['foo','b*'] } // wildcards work too!
458 | { notempty$: 'bar' } // for convenience
459 | { bar: 'notempty$' } // for extra convenience
460 | { 'b*': 'notempty$' } // and that's just nice, again
461 | ```
462 |
463 | ### wild$
464 |
465 | Specify a wildcard pattern that the property value must match. The property value does not need to be a string. See the [gex](https://github.com/rjrodger/gex) module documentation.
466 |
467 | ```javascript
468 | { foo: {wild$:'b*'} }
469 | ```
470 |
471 | ### re$
472 |
473 | Specify a regular expression that the property value must match. The property value is converted to a string. The regular epxression is given as-is, or can be in the format /.../X, where X is a modifier such as _i_.
474 |
475 | ```javascript
476 | {
477 | foo: {re$:'a.*'},
478 | bar: {re$:'/b/i'}
479 | }
480 | ```
481 |
482 |
483 | ### type$
484 |
485 | Check that a property value is of a given JavaScript type. Does not require the property to be present (use _required$_ for that). Can only be used as a subrule of a literal property.
486 |
487 | ```javascript
488 | {
489 | a: {type$:'string'},
490 | b: {type$:'number'},
491 | c: {type$:'integer'}, // can't be decimal!
492 | d: {type$:'boolean'},
493 | e: {type$:'date'},
494 | f: {type$:'array'},
495 | g: {type$:'object'},
496 | h: {type$:'function'}
497 | }
498 | ```
499 |
500 | As a convenience, the type rules can also be used in the form:
501 |
502 | ```javascript
503 | {
504 | $string: 'propname'
505 | }
506 | ```
507 |
508 |
509 | ### eq$
510 |
511 | Check that a property value is an exactly equal to the given value (must also match type).
512 |
513 | ```javascript
514 | {
515 | foo: {eq$:'bar'},
516 | }
517 | ```
518 |
519 | ### comparisons
520 |
521 | The following comparison rules can be used:
522 |
523 | * _lt$_: less than
524 | * _lte$_: less than or equal to (alias: max$)
525 | * _gt$_: greater than
526 | * _gte$_: greater than or equal to (alias: min$)
527 | * _gt$_: greater than
528 |
529 | For example:
530 |
531 | ```javascript
532 | {
533 | foo: {lt$:100},
534 | }
535 | ```
536 |
537 | Comparisons also work on alphabetically on strings.
538 |
539 |
540 |
541 | ### enum$
542 |
543 | Check that a property value is one of an enumerated list of values (can be of any type).
544 |
545 | ```javascript
546 | {
547 | color: {enum$:['red','green','blue']},
548 | }
549 | ```
550 |
551 |
552 | ### uniq$
553 |
554 | Check that a list contains only unique properties.
555 |
556 | ```javascript
557 | {
558 | rainbow: 'uniq$'
559 | }
560 | ```
561 |
562 | The above specification validates:
563 |
564 | ```javascript
565 | {
566 | rainbow: ['red','orange','yellow','green','blue','indigo','violet']
567 | }
568 | ```
569 |
570 | But does not validate:
571 |
572 | ```javascript
573 | {
574 | rainbow: ['red','red','red','red','red','red','red']
575 | }
576 | ```
577 |
578 |
579 | ### only$
580 |
581 | Check that a property _name_ is one of an enumerated list of names, at this point
582 |
583 | ```javascript
584 | {
585 | options: {only$:['folder','duration','limit']},
586 | }
587 | ```
588 |
589 |
590 |
591 | ### ** recursion
592 |
593 | Apply a set of subrules recursively to the current point and all it's children.
594 |
595 | ```javascript
596 | {
597 | a: {
598 | '**': {
599 | b: { type$:'integer' }
600 | },
601 | }
602 | }
603 | ```
604 |
605 |
606 | ## Custom Rules
607 |
608 | You can write your own rules if you need additional validation. The [range.js](https://github.com/rjrodger/parambulator/blob/master/doc/examples/range.js) example shows you how.
609 |
610 | Define your own rules inside the _rules_ property of the _prefs_ argument to _paramabulator_. Each rule is just a function, for example:
611 |
612 | ```javascript
613 | var pm = parambulator({...},{
614 | rules: {
615 | mynewrule$: function(ctxt,cb){
616 | ...
617 | }
618 | }
619 | })
620 | ```
621 |
622 | Dont forget the `$` suffix!
623 |
624 | The _ctxt_ parameter provides the same interface as the _inserts_ object for custom messages (as above). You can execute callback or evented code inside the rule function. Call the _cb_ callback without any arguments if the rule passes.
625 |
626 | If the rule fails, you can use a utility function to generate an error message:
627 |
628 | ```javascript
629 | return ctxt.util.fail(ctxt,cb)
630 | ```
631 |
632 | Just ensure you have a custom message with the same name as the rule!
633 |
634 | The built-in rule definitions in [parambulator.js](https://github.com/rjrodger/parambulator/blob/master/parambulator.js) are also a good resource.
635 |
636 | Tweet me [@rjrodger](http://twitter.com/rjrodger) if you get stuck.
637 |
638 | By the way, if you have a cool new rule and you thing it should be built-in, send me a pull request! Just follow the pattern for, say, _wild$_ in [parambulator.js](https://github.com/rjrodger/parambulator/blob/master/parambulator.js). You'll need entries in _rulemap_, _msgsmap_, and _ownparams_.
639 |
640 |
641 |
642 | ### Validation of Custom Rules
643 |
644 | When you define a custom rule, you'll want to ensure that rule specifications using it are valid. You can do this by adding validation rules to the optional _valid_ property of the _prefs_ argument.
645 |
646 | The [range.js](https://github.com/rjrodger/parambulator/blob/master/doc/examples/range.js) example also shows you how to do this.
647 |
648 | There is a gotcha. You need to escape the rule names, so that they are treated as literal properties, and not rules. To do this, use the `prop$` pseudo-rule:
649 |
650 | ```javascript
651 | { prop$: {name:'foo', rules:{type$:'string'}} }
652 | ```
653 |
654 | is equivalent to:
655 |
656 | ```javascript
657 | { foo: {type$:'string'} }
658 | ```
659 |
660 | The other pseudo-rule that may come in handy is the `list$` rule. This lets you specify rules using an array. Each element is a sub array with two elements, the first is the rule name, and the second the rule specification
661 |
662 | ```javascript
663 | {
664 | list$: [
665 | ['foo', {type$:'string'}],
666 | ['bar', {type$:'string'}],
667 | ]
668 | }
669 | ```
670 |
671 | Take a look at the definition of `ownparams` in [parambulator.js](https://github.com/rjrodger/parambulator/blob/master/parambulator.js) to see how _parambulator_ validates its own input.
672 |
673 |
674 | ## Multiple validation errors
675 |
676 | When configuring a parambulator instance, it is possible to pass an option so
677 | that parambulator runs all the validation rules and return multiple errors:
678 |
679 | ```javascript
680 | var pm = parambulator({...},{
681 | multiErrors: true
682 | })
683 | ```
684 |
685 | pm.validate({foo: 'bar}, function(errors) {
686 | // errors is null or errors.length > 0
687 | })
688 |
689 | ## Testing
690 |
691 | Tests run on the command line, in a headless browser, and in a normal browser:
692 |
693 | ```sh
694 | $ npm build
695 | $ npm test
696 | $ npm run browser
697 | $ open test/jasmine.html
698 | ```
699 |
700 | ## Releases
701 |
702 | Release numbers are strict [semver](http://semver.org/) as 1.x.x. All
703 | releases are tagged in github with release version.
704 |
705 |
--------------------------------------------------------------------------------
/test/jasmine-1.3.1/jasmine-html.js:
--------------------------------------------------------------------------------
1 | jasmine.HtmlReporterHelpers = {};
2 |
3 | jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
4 | var el = document.createElement(type);
5 |
6 | for (var i = 2; i < arguments.length; i++) {
7 | var child = arguments[i];
8 |
9 | if (typeof child === 'string') {
10 | el.appendChild(document.createTextNode(child));
11 | } else {
12 | if (child) {
13 | el.appendChild(child);
14 | }
15 | }
16 | }
17 |
18 | for (var attr in attrs) {
19 | if (attr == "className") {
20 | el[attr] = attrs[attr];
21 | } else {
22 | el.setAttribute(attr, attrs[attr]);
23 | }
24 | }
25 |
26 | return el;
27 | };
28 |
29 | jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
30 | var results = child.results();
31 | var status = results.passed() ? 'passed' : 'failed';
32 | if (results.skipped) {
33 | status = 'skipped';
34 | }
35 |
36 | return status;
37 | };
38 |
39 | jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
40 | var parentDiv = this.dom.summary;
41 | var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
42 | var parent = child[parentSuite];
43 |
44 | if (parent) {
45 | if (typeof this.views.suites[parent.id] == 'undefined') {
46 | this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
47 | }
48 | parentDiv = this.views.suites[parent.id].element;
49 | }
50 |
51 | parentDiv.appendChild(childElement);
52 | };
53 |
54 |
55 | jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
56 | for(var fn in jasmine.HtmlReporterHelpers) {
57 | ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
58 | }
59 | };
60 |
61 | jasmine.HtmlReporter = function(_doc) {
62 | var self = this;
63 | var doc = _doc || window.document;
64 |
65 | var reporterView;
66 |
67 | var dom = {};
68 |
69 | // Jasmine Reporter Public Interface
70 | self.logRunningSpecs = false;
71 |
72 | self.reportRunnerStarting = function(runner) {
73 | var specs = runner.specs() || [];
74 |
75 | if (specs.length == 0) {
76 | return;
77 | }
78 |
79 | createReporterDom(runner.env.versionString());
80 | doc.body.appendChild(dom.reporter);
81 | setExceptionHandling();
82 |
83 | reporterView = new jasmine.HtmlReporter.ReporterView(dom);
84 | reporterView.addSpecs(specs, self.specFilter);
85 | };
86 |
87 | self.reportRunnerResults = function(runner) {
88 | reporterView && reporterView.complete();
89 | };
90 |
91 | self.reportSuiteResults = function(suite) {
92 | reporterView.suiteComplete(suite);
93 | };
94 |
95 | self.reportSpecStarting = function(spec) {
96 | if (self.logRunningSpecs) {
97 | self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
98 | }
99 | };
100 |
101 | self.reportSpecResults = function(spec) {
102 | reporterView.specComplete(spec);
103 | };
104 |
105 | self.log = function() {
106 | var console = jasmine.getGlobal().console;
107 | if (console && console.log) {
108 | if (console.log.apply) {
109 | console.log.apply(console, arguments);
110 | } else {
111 | console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
112 | }
113 | }
114 | };
115 |
116 | self.specFilter = function(spec) {
117 | if (!focusedSpecName()) {
118 | return true;
119 | }
120 |
121 | return spec.getFullName().indexOf(focusedSpecName()) === 0;
122 | };
123 |
124 | return self;
125 |
126 | function focusedSpecName() {
127 | var specName;
128 |
129 | (function memoizeFocusedSpec() {
130 | if (specName) {
131 | return;
132 | }
133 |
134 | var paramMap = [];
135 | var params = jasmine.HtmlReporter.parameters(doc);
136 |
137 | for (var i = 0; i < params.length; i++) {
138 | var p = params[i].split('=');
139 | paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
140 | }
141 |
142 | specName = paramMap.spec;
143 | })();
144 |
145 | return specName;
146 | }
147 |
148 | function createReporterDom(version) {
149 | dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
150 | dom.banner = self.createDom('div', { className: 'banner' },
151 | self.createDom('span', { className: 'title' }, "Jasmine "),
152 | self.createDom('span', { className: 'version' }, version)),
153 |
154 | dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
155 | dom.alert = self.createDom('div', {className: 'alert'},
156 | self.createDom('span', { className: 'exceptions' },
157 | self.createDom('label', { className: 'label', 'for': 'no_try_catch' }, 'No try/catch'),
158 | self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))),
159 | dom.results = self.createDom('div', {className: 'results'},
160 | dom.summary = self.createDom('div', { className: 'summary' }),
161 | dom.details = self.createDom('div', { id: 'details' }))
162 | );
163 | }
164 |
165 | function noTryCatch() {
166 | return window.location.search.match(/catch=false/);
167 | }
168 |
169 | function searchWithCatch() {
170 | var params = jasmine.HtmlReporter.parameters(window.document);
171 | var removed = false;
172 | var i = 0;
173 |
174 | while (!removed && i < params.length) {
175 | if (params[i].match(/catch=/)) {
176 | params.splice(i, 1);
177 | removed = true;
178 | }
179 | i++;
180 | }
181 | if (jasmine.CATCH_EXCEPTIONS) {
182 | params.push("catch=false");
183 | }
184 |
185 | return params.join("&");
186 | }
187 |
188 | function setExceptionHandling() {
189 | var chxCatch = document.getElementById('no_try_catch');
190 |
191 | if (noTryCatch()) {
192 | chxCatch.setAttribute('checked', true);
193 | jasmine.CATCH_EXCEPTIONS = false;
194 | }
195 | chxCatch.onclick = function() {
196 | window.location.search = searchWithCatch();
197 | };
198 | }
199 | };
200 | jasmine.HtmlReporter.parameters = function(doc) {
201 | var paramStr = doc.location.search.substring(1);
202 | var params = [];
203 |
204 | if (paramStr.length > 0) {
205 | params = paramStr.split('&');
206 | }
207 | return params;
208 | }
209 | jasmine.HtmlReporter.sectionLink = function(sectionName) {
210 | var link = '?';
211 | var params = [];
212 |
213 | if (sectionName) {
214 | params.push('spec=' + encodeURIComponent(sectionName));
215 | }
216 | if (!jasmine.CATCH_EXCEPTIONS) {
217 | params.push("catch=false");
218 | }
219 | if (params.length > 0) {
220 | link += params.join("&");
221 | }
222 |
223 | return link;
224 | };
225 | jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);
226 | jasmine.HtmlReporter.ReporterView = function(dom) {
227 | this.startedAt = new Date();
228 | this.runningSpecCount = 0;
229 | this.completeSpecCount = 0;
230 | this.passedCount = 0;
231 | this.failedCount = 0;
232 | this.skippedCount = 0;
233 |
234 | this.createResultsMenu = function() {
235 | this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
236 | this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
237 | ' | ',
238 | this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
239 |
240 | this.summaryMenuItem.onclick = function() {
241 | dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
242 | };
243 |
244 | this.detailsMenuItem.onclick = function() {
245 | showDetails();
246 | };
247 | };
248 |
249 | this.addSpecs = function(specs, specFilter) {
250 | this.totalSpecCount = specs.length;
251 |
252 | this.views = {
253 | specs: {},
254 | suites: {}
255 | };
256 |
257 | for (var i = 0; i < specs.length; i++) {
258 | var spec = specs[i];
259 | this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
260 | if (specFilter(spec)) {
261 | this.runningSpecCount++;
262 | }
263 | }
264 | };
265 |
266 | this.specComplete = function(spec) {
267 | this.completeSpecCount++;
268 |
269 | if (isUndefined(this.views.specs[spec.id])) {
270 | this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
271 | }
272 |
273 | var specView = this.views.specs[spec.id];
274 |
275 | switch (specView.status()) {
276 | case 'passed':
277 | this.passedCount++;
278 | break;
279 |
280 | case 'failed':
281 | this.failedCount++;
282 | break;
283 |
284 | case 'skipped':
285 | this.skippedCount++;
286 | break;
287 | }
288 |
289 | specView.refresh();
290 | this.refresh();
291 | };
292 |
293 | this.suiteComplete = function(suite) {
294 | var suiteView = this.views.suites[suite.id];
295 | if (isUndefined(suiteView)) {
296 | return;
297 | }
298 | suiteView.refresh();
299 | };
300 |
301 | this.refresh = function() {
302 |
303 | if (isUndefined(this.resultsMenu)) {
304 | this.createResultsMenu();
305 | }
306 |
307 | // currently running UI
308 | if (isUndefined(this.runningAlert)) {
309 | this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" });
310 | dom.alert.appendChild(this.runningAlert);
311 | }
312 | this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
313 |
314 | // skipped specs UI
315 | if (isUndefined(this.skippedAlert)) {
316 | this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" });
317 | }
318 |
319 | this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
320 |
321 | if (this.skippedCount === 1 && isDefined(dom.alert)) {
322 | dom.alert.appendChild(this.skippedAlert);
323 | }
324 |
325 | // passing specs UI
326 | if (isUndefined(this.passedAlert)) {
327 | this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" });
328 | }
329 | this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
330 |
331 | // failing specs UI
332 | if (isUndefined(this.failedAlert)) {
333 | this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
334 | }
335 | this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
336 |
337 | if (this.failedCount === 1 && isDefined(dom.alert)) {
338 | dom.alert.appendChild(this.failedAlert);
339 | dom.alert.appendChild(this.resultsMenu);
340 | }
341 |
342 | // summary info
343 | this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
344 | this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
345 | };
346 |
347 | this.complete = function() {
348 | dom.alert.removeChild(this.runningAlert);
349 |
350 | this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
351 |
352 | if (this.failedCount === 0) {
353 | dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
354 | } else {
355 | showDetails();
356 | }
357 |
358 | dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
359 | };
360 |
361 | return this;
362 |
363 | function showDetails() {
364 | if (dom.reporter.className.search(/showDetails/) === -1) {
365 | dom.reporter.className += " showDetails";
366 | }
367 | }
368 |
369 | function isUndefined(obj) {
370 | return typeof obj === 'undefined';
371 | }
372 |
373 | function isDefined(obj) {
374 | return !isUndefined(obj);
375 | }
376 |
377 | function specPluralizedFor(count) {
378 | var str = count + " spec";
379 | if (count > 1) {
380 | str += "s"
381 | }
382 | return str;
383 | }
384 |
385 | };
386 |
387 | jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
388 |
389 |
390 | jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
391 | this.spec = spec;
392 | this.dom = dom;
393 | this.views = views;
394 |
395 | this.symbol = this.createDom('li', { className: 'pending' });
396 | this.dom.symbolSummary.appendChild(this.symbol);
397 |
398 | this.summary = this.createDom('div', { className: 'specSummary' },
399 | this.createDom('a', {
400 | className: 'description',
401 | href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()),
402 | title: this.spec.getFullName()
403 | }, this.spec.description)
404 | );
405 |
406 | this.detail = this.createDom('div', { className: 'specDetail' },
407 | this.createDom('a', {
408 | className: 'description',
409 | href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
410 | title: this.spec.getFullName()
411 | }, this.spec.getFullName())
412 | );
413 | };
414 |
415 | jasmine.HtmlReporter.SpecView.prototype.status = function() {
416 | return this.getSpecStatus(this.spec);
417 | };
418 |
419 | jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
420 | this.symbol.className = this.status();
421 |
422 | switch (this.status()) {
423 | case 'skipped':
424 | break;
425 |
426 | case 'passed':
427 | this.appendSummaryToSuiteDiv();
428 | break;
429 |
430 | case 'failed':
431 | this.appendSummaryToSuiteDiv();
432 | this.appendFailureDetail();
433 | break;
434 | }
435 | };
436 |
437 | jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
438 | this.summary.className += ' ' + this.status();
439 | this.appendToSummary(this.spec, this.summary);
440 | };
441 |
442 | jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
443 | this.detail.className += ' ' + this.status();
444 |
445 | var resultItems = this.spec.results().getItems();
446 | var messagesDiv = this.createDom('div', { className: 'messages' });
447 |
448 | for (var i = 0; i < resultItems.length; i++) {
449 | var result = resultItems[i];
450 |
451 | if (result.type == 'log') {
452 | messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
453 | } else if (result.type == 'expect' && result.passed && !result.passed()) {
454 | messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
455 |
456 | if (result.trace.stack) {
457 | messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
458 | }
459 | }
460 | }
461 |
462 | if (messagesDiv.childNodes.length > 0) {
463 | this.detail.appendChild(messagesDiv);
464 | this.dom.details.appendChild(this.detail);
465 | }
466 | };
467 |
468 | jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
469 | this.suite = suite;
470 | this.dom = dom;
471 | this.views = views;
472 |
473 | this.element = this.createDom('div', { className: 'suite' },
474 | this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description)
475 | );
476 |
477 | this.appendToSummary(this.suite, this.element);
478 | };
479 |
480 | jasmine.HtmlReporter.SuiteView.prototype.status = function() {
481 | return this.getSpecStatus(this.suite);
482 | };
483 |
484 | jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
485 | this.element.className += " " + this.status();
486 | };
487 |
488 | jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
489 |
490 | /* @deprecated Use jasmine.HtmlReporter instead
491 | */
492 | jasmine.TrivialReporter = function(doc) {
493 | this.document = doc || document;
494 | this.suiteDivs = {};
495 | this.logRunningSpecs = false;
496 | };
497 |
498 | jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
499 | var el = document.createElement(type);
500 |
501 | for (var i = 2; i < arguments.length; i++) {
502 | var child = arguments[i];
503 |
504 | if (typeof child === 'string') {
505 | el.appendChild(document.createTextNode(child));
506 | } else {
507 | if (child) { el.appendChild(child); }
508 | }
509 | }
510 |
511 | for (var attr in attrs) {
512 | if (attr == "className") {
513 | el[attr] = attrs[attr];
514 | } else {
515 | el.setAttribute(attr, attrs[attr]);
516 | }
517 | }
518 |
519 | return el;
520 | };
521 |
522 | jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
523 | var showPassed, showSkipped;
524 |
525 | this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
526 | this.createDom('div', { className: 'banner' },
527 | this.createDom('div', { className: 'logo' },
528 | this.createDom('span', { className: 'title' }, "Jasmine"),
529 | this.createDom('span', { className: 'version' }, runner.env.versionString())),
530 | this.createDom('div', { className: 'options' },
531 | "Show ",
532 | showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
533 | this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
534 | showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
535 | this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
536 | )
537 | ),
538 |
539 | this.runnerDiv = this.createDom('div', { className: 'runner running' },
540 | this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
541 | this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
542 | this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
543 | );
544 |
545 | this.document.body.appendChild(this.outerDiv);
546 |
547 | var suites = runner.suites();
548 | for (var i = 0; i < suites.length; i++) {
549 | var suite = suites[i];
550 | var suiteDiv = this.createDom('div', { className: 'suite' },
551 | this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
552 | this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
553 | this.suiteDivs[suite.id] = suiteDiv;
554 | var parentDiv = this.outerDiv;
555 | if (suite.parentSuite) {
556 | parentDiv = this.suiteDivs[suite.parentSuite.id];
557 | }
558 | parentDiv.appendChild(suiteDiv);
559 | }
560 |
561 | this.startedAt = new Date();
562 |
563 | var self = this;
564 | showPassed.onclick = function(evt) {
565 | if (showPassed.checked) {
566 | self.outerDiv.className += ' show-passed';
567 | } else {
568 | self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
569 | }
570 | };
571 |
572 | showSkipped.onclick = function(evt) {
573 | if (showSkipped.checked) {
574 | self.outerDiv.className += ' show-skipped';
575 | } else {
576 | self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
577 | }
578 | };
579 | };
580 |
581 | jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
582 | var results = runner.results();
583 | var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
584 | this.runnerDiv.setAttribute("class", className);
585 | //do it twice for IE
586 | this.runnerDiv.setAttribute("className", className);
587 | var specs = runner.specs();
588 | var specCount = 0;
589 | for (var i = 0; i < specs.length; i++) {
590 | if (this.specFilter(specs[i])) {
591 | specCount++;
592 | }
593 | }
594 | var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
595 | message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
596 | this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
597 |
598 | this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
599 | };
600 |
601 | jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
602 | var results = suite.results();
603 | var status = results.passed() ? 'passed' : 'failed';
604 | if (results.totalCount === 0) { // todo: change this to check results.skipped
605 | status = 'skipped';
606 | }
607 | this.suiteDivs[suite.id].className += " " + status;
608 | };
609 |
610 | jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
611 | if (this.logRunningSpecs) {
612 | this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
613 | }
614 | };
615 |
616 | jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
617 | var results = spec.results();
618 | var status = results.passed() ? 'passed' : 'failed';
619 | if (results.skipped) {
620 | status = 'skipped';
621 | }
622 | var specDiv = this.createDom('div', { className: 'spec ' + status },
623 | this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
624 | this.createDom('a', {
625 | className: 'description',
626 | href: '?spec=' + encodeURIComponent(spec.getFullName()),
627 | title: spec.getFullName()
628 | }, spec.description));
629 |
630 |
631 | var resultItems = results.getItems();
632 | var messagesDiv = this.createDom('div', { className: 'messages' });
633 | for (var i = 0; i < resultItems.length; i++) {
634 | var result = resultItems[i];
635 |
636 | if (result.type == 'log') {
637 | messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
638 | } else if (result.type == 'expect' && result.passed && !result.passed()) {
639 | messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
640 |
641 | if (result.trace.stack) {
642 | messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
643 | }
644 | }
645 | }
646 |
647 | if (messagesDiv.childNodes.length > 0) {
648 | specDiv.appendChild(messagesDiv);
649 | }
650 |
651 | this.suiteDivs[spec.suite.id].appendChild(specDiv);
652 | };
653 |
654 | jasmine.TrivialReporter.prototype.log = function() {
655 | var console = jasmine.getGlobal().console;
656 | if (console && console.log) {
657 | if (console.log.apply) {
658 | console.log.apply(console, arguments);
659 | } else {
660 | console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
661 | }
662 | }
663 | };
664 |
665 | jasmine.TrivialReporter.prototype.getLocation = function() {
666 | return this.document.location;
667 | };
668 |
669 | jasmine.TrivialReporter.prototype.specFilter = function(spec) {
670 | var paramMap = {};
671 | var params = this.getLocation().search.substring(1).split('&');
672 | for (var i = 0; i < params.length; i++) {
673 | var p = params[i].split('=');
674 | paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
675 | }
676 |
677 | if (!paramMap.spec) {
678 | return true;
679 | }
680 | return spec.getFullName().indexOf(paramMap.spec) === 0;
681 | };
682 |
--------------------------------------------------------------------------------
/parambulator.js:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2012-2015 Richard Rodger, MIT License */
2 | /* jshint node:true, asi:true, eqnull:true */
3 |
4 | (function() {
5 | "use strict";
6 |
7 | var root = this
8 | var previous_parambulator = root.parambulator
9 |
10 | var has_require = typeof require !== 'undefined'
11 |
12 |
13 | var _ = root._
14 | var gex = root.gex
15 | var jsonic = root.jsonic
16 |
17 |
18 | if( typeof _ === 'undefined' ) {
19 | if( has_require ) {
20 | _ = require('lodash')
21 | }
22 |
23 | // assume in web context, so still underscore, not lodash
24 | else throw new Error('parambulator requires underscore, see http://underscorejs.org');
25 | }
26 |
27 | if( typeof gex === 'undefined' ) {
28 | if( has_require ) {
29 | gex = require('gex')
30 | }
31 | else throw new Error('parambulator requires gex, see http://github.com/rjrodger/gex');
32 | }
33 |
34 | if( typeof jsonic === 'undefined' ) {
35 | if( has_require ) {
36 | jsonic = require('jsonic')
37 | }
38 | else throw new Error('parambulator requires jsonic, see http://github.com/rjrodger/jsonic');
39 | }
40 |
41 |
42 |
43 | var arrayify = function(){ return Array.prototype.slice.call(arguments[0],arguments[1]) }
44 |
45 |
46 | var quantrule = function( pass, rulename ) {
47 | rulename = rulename || 'quantrule'
48 |
49 | return function(ctxt,cb) {
50 | ctxt.prop = null
51 |
52 | var pn = ctxt.util.proplist(ctxt)
53 |
54 | var found = 0
55 | _.each(pn, function(p){
56 | found += ctxt.point[p]?1:0
57 | })
58 |
59 | if( !pass(found) ) {
60 | ctxt.value = ''+pn
61 | return ctxt.util.fail(ctxt,cb)
62 | }
63 | else return cb();
64 | }
65 | }
66 |
67 | var lenrule = function( pass ) {
68 | return function(ctxt,cb) {
69 | var len = ctxt.rule.spec
70 | var value = ctxt.point
71 |
72 | if( !_.isUndefined(value) ) {
73 | var valuelen = _.isObject(value) ? Object.keys(value).length : value.length
74 | if ( !_.isUndefined( valuelen ) ){
75 | if ( !pass( valuelen, len ) ) {
76 | return ctxt.util.fail(ctxt,cb)
77 | }
78 | }
79 | }
80 |
81 | return cb()
82 | }
83 | };
84 |
85 |
86 | var childrule = function( pass, noneok, rulename ) {
87 | rulename = rulename || 'childrule'
88 |
89 | return function(ctxt,cb) {
90 | var pn = ctxt.util.proplist(ctxt)
91 |
92 | for( var i = 0; i < pn.length; i++ ) {
93 | var p = pn[i]
94 | ctxt.prop = p
95 |
96 | var v = ctxt.point[p]
97 |
98 | if( !pass(ctxt,p,v) ) {
99 | ctxt.value = v
100 | return ctxt.util.fail(ctxt,cb)
101 | }
102 | }
103 |
104 | if( 0 === pn.length ) {
105 | if( !noneok() ) {
106 | // TODO needs a separate msg code
107 | ctxt.prop = JSON.stringify(ctxt.rule.spec,killcircles())
108 | return ctxt.util.fail(ctxt,cb)
109 | }
110 | }
111 | cb();
112 | }
113 | }
114 |
115 | function proplist(ctxt) {
116 | var pn = ctxt.rule.spec
117 |
118 | // TODO: handle comma separated strings
119 | // https://github.com/rjrodger/parambulator/issues/19
120 |
121 | if( !_.isArray(pn) ) {
122 | pn = [''+pn]
123 | }
124 |
125 | var all = []
126 | _.each(pn, function(n){
127 |
128 | if( n.match( /[*?]/ ) ) {
129 | all = all.concat( _.keys(gex(n).on(ctxt.point)) )
130 | }
131 | else all.push(n);
132 | })
133 |
134 | return all
135 | }
136 |
137 |
138 | function truefn(){return true}
139 | function falsefn(){return false}
140 | function noval(v){return _.isUndefined(v)||_.isNull(v)}
141 |
142 | var valrule = function( pass ) {
143 | return function(ctxt,cb) {
144 | var v = ctxt.point
145 | var p = ctxt.rule.spec
146 | if( !_.isUndefined(v) ) {
147 | if( !pass(v, p) ) {
148 | return ctxt.util.fail(ctxt,cb)
149 | }
150 | }
151 |
152 | return cb();
153 | }
154 | }
155 |
156 |
157 | var rulemap = {
158 |
159 | atmostone$: quantrule( function(f){return f<=1}, 'atmostone$'),
160 | exactlyone$: quantrule( function(f){return 1==f}, 'exactlyone$'),
161 | atleastone$: quantrule( function(f){return 1<=f}, 'atleastone$'),
162 |
163 |
164 | required$: childrule( function(ctxt,p,v){return !_.isUndefined(v)}, falsefn, 'required$' ),
165 |
166 | notempty$: childrule(
167 | function(ctxt,p,v){
168 | return !_.isUndefined(v) && !_.isNull(v) && '' !== v
169 | },
170 | truefn,
171 | 'notempty$'
172 | ),
173 |
174 | string$: childrule( function(ctxt,p,v){return noval(v) || _.isString(v)}, truefn, 'string$' ),
175 | integer$: childrule( function(ctxt,p,v){return noval(v) || _.isNumber(v) && v===(v|0)}, truefn, 'integer$' ),
176 | number$: childrule( function(ctxt,p,v){return noval(v) || _.isNumber(v)}, truefn, 'number$' ),
177 | boolean$: childrule( function(ctxt,p,v){return noval(v) || _.isBoolean(v)}, truefn, 'boolean$' ),
178 | date$: childrule( function(ctxt,p,v){return noval(v) || _.isDate(v)}, truefn, 'date$' ),
179 | array$: childrule( function(ctxt,p,v){return noval(v) || _.isArray(v)}, truefn, 'array$' ),
180 | object$: childrule( function(ctxt,p,v){return noval(v) || _.isObject(v) && !_.isArray(v)}, truefn, 'object$' ),
181 | function$: childrule( function(ctxt,p,v){return noval(v) || _.isFunction(v)}, truefn, 'function$' ),
182 |
183 | lt$: valrule( function(p,v){return p < v} ),
184 | lte$: valrule( function(p,v){return p <= v} ),
185 | gt$: valrule( function(p,v){return p > v} ),
186 | gte$: valrule( function(p,v){return p >= v} ),
187 |
188 | min$: valrule( function(p,v){return p >= v}),
189 | max$: valrule( function(p,v){return p <= v}),
190 |
191 | uniq$: function(ctxt,cb){
192 | var value = ctxt.point
193 | var o = {}
194 |
195 | for( var i=0; i= conditionlen} ),
246 | maxlen$: lenrule( function(valuelen, conditionlen){return valuelen <= conditionlen} ),
247 |
248 | re$: function(ctxt,cb) {
249 | var value = ctxt.point
250 |
251 | if( !_.isUndefined(value) ) {
252 | value = ''+value
253 | var redef = ctxt.rule.spec
254 | var reopt = void(0)
255 |
256 | var m = /^\/(.*)\/(\w*)$/.exec(ctxt.rule.spec)
257 | if( m ) {
258 | redef = m[1]
259 | reopt = m[2]
260 | }
261 |
262 | var re = new RegExp(redef,reopt)
263 |
264 | if( !re.exec(value) ) {
265 | return ctxt.util.fail(ctxt,cb)
266 | }
267 | }
268 |
269 | return cb();
270 | },
271 |
272 |
273 | type$: function(ctxt,cb) {
274 | var pn = ctxt.util.proplist(ctxt)
275 |
276 | var checkmap = {
277 | string:_.isString,
278 | number:_.isNumber,
279 | integer:function(v){return _.isNumber(v) && v===(v|0)},
280 | boolean:_.isBoolean,
281 | date:_.isDate,
282 | array:_.isArray,
283 | object:function(v){return _.isObject(v) && !_.isArray(v) && !_.isDate(v)},
284 | 'function':function(v){return _.isFunction(v)}
285 | }
286 |
287 | var found = 0;
288 | _.each(pn, function(p){
289 | var check = checkmap[p.toLowerCase()]
290 | if( check ) {
291 | found += check(ctxt.point)
292 | }
293 | })
294 |
295 | if( !found ) {
296 | return ctxt.util.fail(ctxt,cb)
297 | }
298 |
299 | return cb();
300 | },
301 |
302 | format$: function(ctxt,cb) {
303 | var pn = ctxt.util.proplist(ctxt)
304 |
305 | var checkmap = {
306 | datetime: function checkFormatRegex(v) { return /\d{4}-(0[1-9]|1[1-2])-([0-2]\d|3[0-1])T([0-1]\d|2[0-4]):[0-5]\d:[0-5]\dZ/.test(v) },
307 | date: function checkFormatRegex(v) { return /\d{4}-[0-1][0-2]-[0-2]\d/.test(v) },
308 | time: function checkFormatRegex(v) { return /([0-1]\d|2[0-4]):[0-5]\d:[0-5]\dZ/.test(v) },
309 | utcmillisec: _.isNumber,
310 | re: _.isRegExp
311 | }
312 |
313 | var found = 0;
314 | _.each(pn, function(p){
315 | var check = checkmap[p.toLowerCase()]
316 | if( check ) {
317 | found += check(ctxt.point)
318 | }
319 | })
320 |
321 | if( !found ) {
322 | return ctxt.util.fail(ctxt,cb)
323 | }
324 |
325 | return cb();
326 | },
327 |
328 | default$: function(ctxt,cb) {
329 | return cb();
330 | },
331 |
332 | enum$: function(ctxt,cb) {
333 | var value = ctxt.point
334 | var okvals = ctxt.rule.spec
335 |
336 | var avalue = []
337 |
338 | if ( _.isArray(value) ) {
339 | avalue = value
340 | }
341 | else {
342 | avalue[0] = value
343 | }
344 |
345 | var iserror = 0
346 | if( avalue ) {
347 | _.each(avalue, function(p){
348 | iserror += (-1 == okvals.indexOf(p) )
349 | })
350 | }
351 | if ( iserror ){
352 | return ctxt.util.fail(ctxt,cb)
353 | }
354 |
355 | return cb();
356 | },
357 |
358 |
359 |
360 |
361 |
362 | // internal rules
363 |
364 |
365 | iterate$: function(ctxt,cb) {
366 | var pn = [ctxt.rule.spec.prop]
367 |
368 | if( _.isObject(ctxt.point) ) {
369 | if( _.isArray(ctxt.point) ) {
370 | pn = gex( ctxt.rule.spec.prop ).on( _.range(ctxt.point.length))
371 | }
372 | else {
373 | pn = _.keys(gex( ctxt.rule.spec.prop ).on(ctxt.point))
374 | }
375 | }
376 |
377 | var subctxt = ctxt.util.clone(ctxt)
378 | subctxt.rules = ctxt.rule.spec.rules
379 |
380 | function eachprop(propI) {
381 | if( propI < pn.length ) {
382 | var p = pn[propI]
383 |
384 | var psubctxt = ctxt.util.clone(subctxt)
385 |
386 | psubctxt.parents = subctxt.parents.concat({prop:p,point:subctxt.point})
387 |
388 | psubctxt.prop = p
389 | psubctxt.point = subctxt.point ? subctxt.point[p] : null
390 |
391 | psubctxt.util.execrules(psubctxt,function(err){
392 | if( err ) return cb(err);
393 | eachprop(propI+1)
394 | })
395 | }
396 | else cb()
397 | }
398 | eachprop(0)
399 | },
400 |
401 |
402 | recurse$: function(ctxt,cb) {
403 |
404 | var recurctxt = ctxt.util.clone(ctxt)
405 | recurctxt.point = {$:ctxt.point}
406 | recurse('$',recurctxt.point,cb)
407 |
408 | function recurse(prop,point,cb) {
409 | if( !_.isObject(point) ) {
410 | return cb(null)
411 | }
412 |
413 | var pn = _.keys( point )
414 |
415 | var subctxt = ctxt.util.clone(ctxt)
416 | subctxt.rules = ctxt.rule.spec.rules
417 | subctxt.parents = '$'!=prop?subctxt.parents.concat({prop:subctxt.prop,point:subctxt.point}):subctxt.parents
418 |
419 | function eachprop(propI,cb) {
420 | if( propI < pn.length ) {
421 | var p = pn[propI]
422 | var eachpropctxt = subctxt.util.clone(subctxt)
423 | eachpropctxt.prop = p
424 | eachpropctxt.point = point[p]
425 | eachpropctxt.parents = '$'!=p?eachpropctxt.parents.concat({prop:subctxt.prop,point:subctxt.point}):eachpropctxt.parents
426 |
427 | eachpropctxt.util.execrules(eachpropctxt,function(err){
428 | if( err ) return cb(err);
429 |
430 | recurse(p,eachpropctxt.point,function(err){
431 | if( err ) return cb(err);
432 |
433 | eachprop(propI+1,cb)
434 | })
435 | })
436 | }
437 | else {
438 | return cb(null)
439 | }
440 | }
441 | eachprop(0,cb)
442 | }
443 | },
444 |
445 |
446 | descend$: function(ctxt,cb) {
447 | var subctxt = ctxt.util.clone(ctxt)
448 | var prop = ctxt.rule.spec.prop
449 |
450 | subctxt.rules = ctxt.rule.spec.rules
451 | subctxt.prop = prop
452 | subctxt.point = ctxt.point[prop]
453 | subctxt.parents = subctxt.parents.concat({prop:prop,point:ctxt.point})
454 |
455 | subctxt.util.execrules(subctxt,function(err){
456 | if( err ) return cb(err);
457 | cb(null)
458 | })
459 | }
460 | }
461 |
462 |
463 | var msgsmap = {
464 | no_input$: "There is no input parameter",
465 |
466 | atmostone$: "At most one of these properties can be used at a time: '<%=value%>' (parent: <%=parentpath%>).",
467 | exactlyone$: "Exactly one of these properties must be used: '<%=value%>' (parent: <%=parentpath%>).",
468 | atleastone$: "At least one of these properties is required: '<%=value%>' (parent: <%=parentpath%>).",
469 |
470 | required$: "The property '<%=property%>' is missing and is always required (parent: <%=parentpath%>).",
471 | notempty$: "The property '<%=property%>' requires a value (parent: <%=parentpath%>).",
472 |
473 | string$: "The property '<%=property%>', with current value: '<%=value%>', must be a string (parent: <%=parentpath%>).",
474 | integer$: "The property '<%=property%>', with current value: '<%=value%>', must be a integer (parent: <%=parentpath%>).",
475 | number$: "The property '<%=property%>', with current value: '<%=value%>', must be a number (parent: <%=parentpath%>).",
476 | boolean$: "The property '<%=property%>', with current value: '<%=value%>', must be a boolean (parent: <%=parentpath%>).",
477 | date$: "The property '<%=property%>', with current value: '<%=value%>', must be a date (parent: <%=parentpath%>).",
478 | array$: "The property '<%=property%>', with current value: '<%=value%>', must be a array (parent: <%=parentpath%>).",
479 | object$: "The property '<%=property%>', with current value: '<%=value%>', must be a object (parent: <%=parentpath%>).",
480 | function$: "The property '<%=property%>', with current value: '<%=value%>', must be a function (parent: <%=parentpath%>).",
481 |
482 | only$: "The property '<%=property%>' is not recognised here. Recognised properties are: <%=rule.spec%> (parent: <%=parentpath%>).",
483 |
484 | wild$: "The value <%=value%> does not match the expression '<%=rule.spec%>' (parent: <%=parentpath%>).",
485 | re$: "The value <%=value%> does not match the regular expression <%=rule.spec%> (parent: <%=parentpath%>).",
486 | type$: "The value <%=value%> is not of type '<%=rule.spec%>' (parent: <%=parentpath%>).",
487 | format$: "The value <%=value%> is not of format '<%=rule.spec%>' (parent: <%=parentpath%>).",
488 |
489 | minlen$: "The property '<%=property%>', with current value: '<%=value%>', must have minimum length '<%=rule.spec%>' (parent: <%=parentpath%>).",
490 | maxlen$: "The property '<%=property%>', with current value: '<%=value%>', must have maximum length '<%=rule.spec%>' (parent: <%=parentpath%>).",
491 |
492 | eq$: "The value <%=value%> does not equal '<%=rule.spec%>' (parent: <%=parentpath%>).",
493 | lt$: "The value <%=value%> is not less than '<%=rule.spec%>' (parent: <%=parentpath%>).",
494 | lte$: "The value <%=value%> is not less than or equal with '<%=rule.spec%>' (parent: <%=parentpath%>).",
495 | gt$: "The value <%=value%> is not greater than '<%=rule.spec%>' (parent: <%=parentpath%>).",
496 | gte$: "The value <%=value%> is not not greater than or equal with '<%=rule.spec%>' (parent: <%=parentpath%>).",
497 | min$: "The value <%=value%> is not not greater than or equal with '<%=rule.spec%>' (parent: <%=parentpath%>).",
498 | max$: "The value <%=value%> is not less than or equal with '<%=rule.spec%>' (parent: <%=parentpath%>).",
499 | uniq$: "The value <%=value%> has duplicate elements.",
500 | enum$: "The value <%=value%> must be one of '<%=rule.spec%>' (parent: <%=parentpath%>)."
501 |
502 | }
503 |
504 |
505 |
506 |
507 | function clone(ctxt) {
508 | var newctxt = {
509 | rules:ctxt.rules,
510 | point:ctxt.point,
511 | msgs:ctxt.msgs,
512 | log:ctxt.log,
513 | parents:ctxt.parents,
514 | util:ctxt.util
515 | }
516 | return newctxt;
517 | }
518 |
519 |
520 | function formatparents(parents,topname) {
521 | var out = topname || 'top level'
522 | if( 0 < parents.length ) {
523 | out = _.map(parents,function(p){return p.prop}).join('.')
524 | if( topname ) {
525 | out = topname+'.'+out
526 | }
527 | }
528 |
529 | return out
530 | }
531 |
532 |
533 | function killcircles() {
534 | var seen = []
535 | return function(k,v){
536 | if( (_.includes||_.contains)(seen,v) ) return '[CIRCULAR-REFERENCE]';
537 | seen.push(v)
538 | return v
539 | }
540 | }
541 |
542 |
543 |
544 | function fail() {
545 | var code = arguments[0]
546 | var ctxt = arguments[1]
547 | var cb = arguments[2]
548 |
549 | if( !cb ) {
550 | ctxt = arguments[0]
551 | cb = arguments[1]
552 | code = ctxt.rule.name
553 | }
554 |
555 | if(!cb) {
556 | throw new Error('Parambulator: ctxt.util.fail: callback undefined')
557 | }
558 |
559 | if(!ctxt) {
560 | return cb(new Error('Parambulator: ctxt.util.fail: ctxt undefined'))
561 | }
562 |
563 | var inserts = {
564 | property:ctxt.prop,
565 | value:ctxt.value||JSON.stringify(ctxt.point,killcircles()),
566 | point:ctxt.point,
567 | rule:ctxt.rule,
568 | parentpath:ctxt.util.formatparents(ctxt.parents),
569 | json:function(v){return JSON.stringify(v,killcircles())}
570 | }
571 |
572 | var msg = ctxt.msgs[code] || code
573 |
574 | if( _.isFunction(msg) ) {
575 | msg = msg(inserts,ctxt)
576 | }
577 | else {
578 | msg = ctxt.util.msgmods( msg )
579 | msg = _.template(msg)(inserts)
580 | }
581 |
582 | var err = new Error( msg )
583 |
584 | err.parambulator = {
585 | code: code,
586 | property: ctxt.prop,
587 | value: ctxt.point,
588 | expected: (ctxt.rule ? ctxt.rule.spec : void 0),
589 | parents: ctxt.parents,
590 | point: ctxt.point,
591 | rule: ctxt.rule}
592 | return cb(err)
593 | }
594 |
595 |
596 | // Return an ordered array of property names. The prefix __ is removed
597 | // from property names, both in the returned array, and the original
598 | // object.
599 | function proporder(obj) {
600 | var pn = []
601 | for( var p in obj ) {
602 | var pc = p
603 | if( 0 === p.indexOf('__') ) {
604 | pc = p.substring(2)
605 | obj[pc] = obj[p]
606 | delete obj[p]
607 | }
608 | pn.push(pc)
609 | }
610 | return pn
611 | }
612 |
613 |
614 | /*
615 |
616 | name$ -> rule name
617 | name -> property names
618 |
619 | */
620 | function Parambulator( spec, pref ) {
621 | var self = {}
622 | pref = pref || {}
623 | var defaultrules = []
624 |
625 | if( _.isString(spec) ) {
626 | spec = jsonic(spec)
627 | }
628 |
629 | if( !spec || !_.isObject(spec) || _.isArray(spec) ) {
630 | throw new Error('Parambulator: spec argument is not an object')
631 | }
632 |
633 | if( _.cloneDeep ) {
634 | spec = _.cloneDeep(spec)
635 | }
636 |
637 | if( pref ) {
638 | if( exp.ownprefs ) {
639 | exp.ownprefs.validate(pref,function(err){
640 | if( err ) throw err;
641 | })
642 | }
643 |
644 | if( pref.valid && exp ) {
645 | var prefparams = exp({'**':pref.valid})
646 | prefparams.validate(spec,function(err){
647 | if( err ) throw err;
648 | })
649 | }
650 | }
651 |
652 |
653 | //var rulenames = proporder(spec)
654 | var rules = parse(spec)
655 | parsedefault(spec, [], [])
656 |
657 |
658 | self.toString = function() {
659 | return JSON.stringify(rules)
660 | }
661 |
662 |
663 | /*
664 | * Example:
665 | * For a: {default$:123, type$:'number'}
666 | * creates {"pathnames":["a"],"pathtypes":[],"defaultvalue":123}
667 | *
668 | * for d: {type$: 'array', __0: {default$:'arraytest0'}}
669 | * creates {"pathnames":["d","0"],"pathtypes":["array"],"defaultvalue":"arraytest0"}
670 | */
671 | function parsedefault(spec, path, pathtypes){
672 | var innerulenames = []
673 | var currentruletype = 'object'
674 |
675 | for(var name in spec){
676 | if(spec.hasOwnProperty(name)) {
677 | var rule = spec[name]
678 | if ('default$' == name){
679 | var defaultrule = {}
680 | defaultrule.pathnames = path
681 |
682 | defaultrule.pathtypes = pathtypes.splice(1,pathtypes.length)
683 | defaultrule.defaultvalue = rule
684 | defaultrules.push(defaultrule)
685 | }
686 | if ('type$' == name){
687 | currentruletype = rule
688 | }
689 | if( _.isObject(rule) && !_.isArray(rule) ) {
690 | innerulenames.push(name)
691 | }
692 | }
693 | }
694 |
695 | for (var index in innerulenames){
696 | if(innerulenames.hasOwnProperty(index)) {
697 | var inner_rule = spec[innerulenames[index]]
698 |
699 | var newpath = path.slice()
700 | newpath.push(innerulenames[index])
701 |
702 | var newpathtypes = pathtypes.slice()
703 | newpathtypes.push(currentruletype)
704 | parsedefault(inner_rule, newpath, newpathtypes)
705 | }
706 | }
707 | }
708 |
709 |
710 | function buildrule(name,rulespec) {
711 | var func = (pref && pref.rules) ? pref.rules[name] : null
712 | if( !func ) {
713 | func = rulemap[name]
714 | }
715 |
716 | if( func ) {
717 | var rule = {
718 | func:func,
719 | name:name,
720 | spec:rulespec
721 | }
722 | return rule
723 | }
724 | else {
725 | throw new Error("Parambulator: Unknown rule: "+name)
726 | }
727 | }
728 |
729 |
730 | function parse(spec) {
731 | var rules = []
732 | var names = proporder(spec)
733 | _.each(names, function(name){
734 | var rulespec = spec[name]
735 |
736 |
737 | // enables multiple rules of same name
738 | if( 'list$' == name ) {
739 | for(var i = 0; i < rulespec.length; i++) {
740 | var rs = {}
741 | rs[rulespec[i][0]]=rulespec[i][1]
742 | rules.push(parse(rs)[0])
743 | }
744 | }
745 |
746 | // enables quoting of property names that end in $
747 | else if( 'prop$' == name ) {
748 | var subrules = parse( rulespec.rules )
749 | var rule = buildrule('descend$',{prop:rulespec.name,rules:subrules})
750 | rules.push(rule)
751 | }
752 |
753 |
754 | // it's a rule - name$ syntax
755 | else if( name.match(/\$$/) ) {
756 | if((name === 'required$' || name === 'notempty$') &&
757 | pref.multiErrors &&
758 | _.isArray(rulespec))
759 | {
760 | _.each(rulespec, function(item) {
761 | var item_rule = buildrule(name,[item],spec)
762 | rules.push(item_rule)
763 | })
764 | }
765 | else {
766 | var build_rule = buildrule(name,rulespec,spec)
767 | rules.push(build_rule)
768 | }
769 | }
770 |
771 |
772 | else if( '**' == name ) {
773 | var starstar_subrules = parse( rulespec )
774 | var starstar_rule =
775 | buildrule('recurse$',{prop:name,rules:starstar_subrules})
776 | rules.push(starstar_rule)
777 | }
778 |
779 |
780 | // it's a property
781 | else {
782 | if( _.isObject(rulespec) && !_.isArray(rulespec) ) {
783 |
784 | _.each( rulespec, function(v,p){
785 | if( p.match(/\$$/) && _.isBoolean(v) && v ) {
786 | var rule = buildrule(p,name,spec)
787 | rules.push(rule)
788 | delete rulespec[p]
789 | }
790 | })
791 |
792 | var prop_subrules = parse( rulespec )
793 | var prop_rule = buildrule('iterate$',{prop:name,rules:prop_subrules})
794 | rules.push(prop_rule)
795 | }
796 |
797 |
798 | else if( _.isString(rulespec) ) {
799 |
800 | // foo:'required$'
801 | if( rulespec.match(/\$/) ) {
802 | var rulespecs = rulespec.split(/\s*,\s*/)
803 | _.each( rulespecs, function( rulespec ) {
804 | rules.push( buildrule(rulespec,name) )
805 | })
806 | }
807 |
808 | // foo:'bar*'
809 | else {
810 | rules.push( buildrule('descend$',{
811 | prop:name,rules:[buildrule('wild$',rulespec)]
812 | }))
813 | }
814 | }
815 |
816 | // TODO: else check for other types, and use eq$ !!!
817 |
818 | else if( _.isNumber(rulespec) ) {
819 | rules.push( buildrule('descend$',{
820 | prop:name,rules:[buildrule('eq$',rulespec)]
821 | }))
822 | }
823 |
824 | else if( _.isBoolean(rulespec) ) {
825 | rules.push( buildrule('descend$',{
826 | prop:name,rules:[buildrule('eq$',rulespec)]
827 | }))
828 | }
829 |
830 | else {
831 | throw new Error("Parambulator: Unrecognized rule specification: "+rulespec)
832 | }
833 | }
834 |
835 | })
836 |
837 | return rules
838 | }
839 |
840 |
841 | var msgs = _.extend({},msgsmap,pref?pref.msgs:null)
842 |
843 |
844 | self.validate = function( args, cb ) {
845 | var reterr
846 | var callback = pref.callbackmaker ? pref.callbackmaker(cb) : (cb||function(){})
847 | var wrapcb = function(err){
848 | reterr=err
849 | // console.log('VALIDATE', JSON.stringify(arguments), arrayify(arguments))
850 | callback.apply(null,arguments)
851 | }
852 | var errors = []
853 |
854 |
855 | function execrules(ctxt,cb) {
856 | if( _.isUndefined(args) ) {
857 | return fail('no_input$',ctxt,cb)
858 | }
859 |
860 | var rules = ctxt.rules
861 |
862 | function execrule(ruleI) {
863 | if( ruleI < rules.length ) {
864 | var rule = rules[ruleI]
865 |
866 | if( !ctxt.point ) {
867 | return execrule(ruleI+1)
868 | }
869 |
870 | ctxt.rule = rule
871 |
872 | var specstr = JSON.stringify(rule.spec,killcircles())
873 |
874 | ctxt.log.push('rule:'+rule.name+':exec:'+specstr)
875 |
876 | rule.func(ctxt, function(err) {
877 | if( err ) {
878 | if(_.isArray(err)) {
879 | // huh !?
880 | // errors = errors.concat(err)
881 | } else {
882 | errors.push(err)
883 | ctxt.log.push('rule:'+rule.name+':fail:'+specstr)
884 | }
885 | execrule(ruleI+1)
886 | }
887 | else {
888 | ctxt.log.push('rule:'+rule.name+':pass:'+specstr)
889 | execrule(ruleI+1)
890 | }
891 | })
892 | }
893 | else {
894 | if(errors.length > 0 && pref.multiErrors) {
895 | cb(errors,{log:ctxt.log})
896 | } else {
897 | var err = errors.length > 0 ? errors[0] : null;
898 | cb(err,{log:ctxt.log})
899 | }
900 |
901 | }
902 | }
903 |
904 | execrule(0)
905 | }
906 |
907 | /*
908 | * Example:
909 | * For a: {"pathnames":["a"],"pathtypes":[],"defaultvalue":123}
910 | * creates {a:123}
911 | *
912 | * for d: {"pathnames":["d","0"],"pathtypes":["array"],"defaultvalue":"arraytest0"}
913 | * creates {d: ['arraytest0']}
914 | */
915 | function validatedefaults(ctxt, cb){
916 | for (var ruleindex in defaultrules){
917 | var rule = defaultrules[ruleindex]
918 | var obj = ctxt.point
919 |
920 | for (var index in rule.pathnames){
921 | var location = rule.pathnames[index]
922 | if ( !_.has(obj, location) ){
923 | // if is last in path then just add default value
924 | if (index == rule.pathnames.length - 1){
925 | obj[location] = rule.defaultvalue
926 | }
927 | // else create object or array and continue following path
928 | else{
929 | var type = rule.pathtypes[index]
930 | var newobj
931 | if ('object' == type){
932 | newobj = {}
933 | }
934 | else if ('array' == type){
935 | newobj = []
936 | }
937 | else{
938 | // cannot continue, call cb and return false;
939 | ctxt.util.fail('default$',ctxt,cb)
940 | return;
941 | }
942 | obj[location] = newobj
943 | obj = newobj
944 | }
945 | }
946 | else{
947 | obj = obj[location]
948 | }
949 | }
950 | }
951 | cb();
952 | }
953 |
954 | var ctxt = {rules:rules,point:args,msgs:msgs,log:[],parents:[]}
955 | ctxt.util = {
956 | formatparents:function(){
957 | var args = arrayify(arguments)
958 | args[1] = pref && pref.topname && !args[1] ? pref.topname : args[1]
959 | return formatparents.apply(null,args)
960 | },
961 | msgmods:function(msg) {
962 | return (pref.msgprefix||'') + msg + (pref.msgsuffix||'')
963 | },
964 | fail:fail,proplist:proplist,execrules:execrules,clone:clone}
965 |
966 | validatedefaults(ctxt,function(err){
967 | if (err){
968 | wrapcb(err,{log:ctxt.log})
969 | }
970 | else {
971 | execrules(ctxt,function(err){
972 | wrapcb(err,{log:ctxt.log})
973 | })
974 | }
975 | })
976 |
977 | // only works if no async calls inside rules
978 | return reterr
979 | }
980 |
981 |
982 | return self
983 | }
984 |
985 |
986 | var exp = function(){
987 | var args = arrayify(arguments)
988 | return Parambulator.apply(this,args)
989 | }
990 |
991 | // this is where Parambulator validates it's own input
992 | exp.ownparams = new Parambulator({
993 | '**':
994 | {
995 | strings$: ['required$','notempty$','atmostone$','exactlyone$','atleastone$'],
996 | list$:[
997 | ['prop$',{name:'wild$', rules:{type$:'string'}}],
998 | ['prop$',{name:'type$', rules:{type$:['string','array']}}],
999 | ['prop$',{name:'format$', rules:{type$:['string','array']}}],
1000 | ['prop$',{name:'re$', rules:{type$:'string'}}],
1001 |
1002 | ['prop$',{name:'type$', rules:{enum$:['string','number','integer','boolean','date','array','object',]}}],
1003 | ['prop$',{name:'format$', rules:{enum$:['datetime','date','time','utcmillisec', 're']}}],
1004 |
1005 | ['prop$',{name:'minlen$', rules:{type$:'number'}}],
1006 | ['prop$',{name:'maxlen$', rules:{type$:'number'}}],
1007 |
1008 | ['prop$',{name:'enum$', rules:{type$:'array'}}],
1009 | ['prop$',{name:'list$', rules:{type$:'array'}}],
1010 |
1011 | // This can work only after #1 is implemented
1012 | // these should also work for strings, right?
1013 | // ['prop$',{name:'lt$', rules:{type$:['number','date']}}],
1014 | // ['prop$',{name:'lte$', rules:{type$:['number','date']}}],
1015 | // ['prop$',{name:'gt$', rules:{type$:['number','date']}}],
1016 | // ['prop$',{name:'gte$', rules:{type$:['number','date']}}],
1017 | // ['prop$',{name:'min$', rules:{type$:['number','date']}}],
1018 | // ['prop$',{name:'max$', rules:{type$:['number','date']}}],
1019 |
1020 | ['prop$',{name:'uniq$', rules:{type$:'array'}}]
1021 | ]
1022 | }
1023 | }, {
1024 | __ownparams__:true,
1025 | rules: {
1026 | strings$: function(ctxt,cb){
1027 | var pn = ctxt.rule.spec
1028 |
1029 | if( !_.isArray(pn) ) {
1030 | pn = [''+pn]
1031 | }
1032 |
1033 | for( var pI = 0; pI < pn.length; pI++ ) {
1034 | var p = pn[pI]
1035 | var val = ctxt.point[p]
1036 |
1037 | if( !_.isUndefined(val) ) {
1038 | if( _.isString(val) ) {
1039 | // ok
1040 | }
1041 | else if( _.isArray(val) ) {
1042 | for(var i = 0; i < val.length; i++ ) {
1043 | if( !_.isString(val[i]) ) {
1044 | ctxt.prop = p
1045 | return ctxt.util.fail(ctxt,cb)
1046 | }
1047 | }
1048 | }
1049 | else {
1050 | ctxt.prop = p
1051 | return ctxt.util.fail(ctxt,cb)
1052 | }
1053 | }
1054 | }
1055 |
1056 | cb(null)
1057 | }
1058 | },
1059 | msgs: {
1060 | 'strings$': 'The <%=property%> rule needs a string or array of strings (property: <%=parentpath%>).'
1061 | },
1062 | topname:'spec'
1063 | })
1064 |
1065 |
1066 | // validate preferences
1067 | exp.ownprefs = new Parambulator({
1068 | object$:['valid','rules','msgs'],
1069 | string$:['topname','msgprefix','msgsuffix'],
1070 | boolean$:['multiErrors'],
1071 | function$:['callbackmaker'],
1072 | only$:['valid','rules','msgs', 'topname','msgprefix','msgsuffix', 'multiErrors', 'callbackmaker']
1073 | },{
1074 | topname:'prefs'
1075 | })
1076 |
1077 | exp.Parambulator = Parambulator
1078 |
1079 |
1080 | //module.exports = exp
1081 |
1082 |
1083 |
1084 | root.parambulator = exp
1085 |
1086 | exp.noConflict = function() {
1087 | root.parambulator = previous_parambulator;
1088 | return self;
1089 | }
1090 |
1091 |
1092 | if( typeof exports !== 'undefined' ) {
1093 | if( typeof module !== 'undefined' && module.exports ) {
1094 | exports = module.exports = exp
1095 | }
1096 | exports.parambulator = exp
1097 | }
1098 | else {
1099 | root.parambulator = exp
1100 | }
1101 |
1102 | }).call(this);
1103 |
--------------------------------------------------------------------------------