Skip to content

Commit 1d1400e

Browse files
committed
add startsWith and url
1 parent f87e6b7 commit 1d1400e

File tree

6 files changed

+125
-2
lines changed

6 files changed

+125
-2
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ for details, see the [code](/index.es6). it's very clear i promise.
5959
- numeric
6060
- regex
6161
- required
62+
- startsWith
63+
- url
6264

6365
## aliases
6466

content/en-us/default.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,7 @@ module.exports = {
1111
minLength: '%1 must have a minimum length of %2',
1212
number: '%1 must be a number',
1313
numeric: '%1 must only contain numbers',
14-
required: '%1 is required'
14+
required: '%1 is required',
15+
startsWith: '%1 must start with %2',
16+
url: '%1 must be a url'
1517
};

index.es6

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,51 @@ export let library = {
4343
},
4444
required: function(value) {
4545
return typeof(value) === 'string' && value.trim() !== '';
46+
},
47+
startsWith: function(value, searchString) {
48+
return library.required(value) && library.exists(searchString) && value.trim().substr(0, searchString.length) === searchString;
49+
},
50+
url: function(value) {
51+
//
52+
// https://gist.github.com/dperini/729294
53+
//
54+
let re_weburl = new RegExp(
55+
"^" +
56+
// protocol identifier
57+
"(?:(?:https?|ftp)://)" +
58+
// user:pass authentication
59+
"(?:\\S+(?::\\S*)?@)?" +
60+
"(?:" +
61+
// IP address exclusion
62+
// private & local networks
63+
"(?!(?:10|127)(?:\\.\\d{1,3}){3})" +
64+
"(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" +
65+
"(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" +
66+
// IP address dotted notation octets
67+
// excludes loopback network 0.0.0.0
68+
// excludes reserved space >= 224.0.0.0
69+
// excludes network & broacast addresses
70+
// (first & last IP address of each class)
71+
"(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
72+
"(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
73+
"(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" +
74+
"|" +
75+
// host name
76+
"(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" +
77+
// domain name
78+
"(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" +
79+
// TLD identifier
80+
"(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))" +
81+
// TLD may end with dot
82+
"\\.?" +
83+
")" +
84+
// port number
85+
"(?::\\d{2,5})?" +
86+
// resource path
87+
"(?:[/?#]\\S*)?" +
88+
"$", "i"
89+
);
90+
return library.regex(value, re_weburl);
4691
}
4792
};
4893

index.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,41 @@ var library = exports.library = {
6262
},
6363
required: function required(value) {
6464
return typeof value === 'string' && value.trim() !== '';
65+
},
66+
startsWith: function startsWith(value, searchString) {
67+
return library.required(value) && library.exists(searchString) && value.trim().substr(0, searchString.length) === searchString;
68+
},
69+
url: function url(value) {
70+
//
71+
// https://gist.github.com/dperini/729294
72+
//
73+
var re_weburl = new RegExp("^" +
74+
// protocol identifier
75+
"(?:(?:https?|ftp)://)" +
76+
// user:pass authentication
77+
"(?:\\S+(?::\\S*)?@)?" + "(?:" +
78+
// IP address exclusion
79+
// private & local networks
80+
"(?!(?:10|127)(?:\\.\\d{1,3}){3})" + "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" + "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" +
81+
// IP address dotted notation octets
82+
// excludes loopback network 0.0.0.0
83+
// excludes reserved space >= 224.0.0.0
84+
// excludes network & broacast addresses
85+
// (first & last IP address of each class)
86+
"(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" + "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" + "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" + "|" +
87+
// host name
88+
'(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)' +
89+
// domain name
90+
'(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*' +
91+
// TLD identifier
92+
'(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))' +
93+
// TLD may end with dot
94+
"\\.?" + ")" +
95+
// port number
96+
"(?::\\d{2,5})?" +
97+
// resource path
98+
"(?:[/?#]\\S*)?" + "$", "i");
99+
return library.regex(value, re_weburl);
65100
}
66101
};
67102

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-formstate-validation",
3-
"version": "0.1.1",
3+
"version": "0.1.2",
44
"peerDependencies": {
55
"react-formstate": ">=0.3.0"
66
},

test/test.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ describe('validation library', function() {
1616
it('multiple dots', function() { assert.equal(true, lib.email('[email protected]')); });
1717
it('multiple @s', function() { assert.equal(true, lib.email('a@[email protected]')); });
1818
it('numbers', function() { assert.equal(true, lib.email('[email protected]')); });
19+
it('null', function() { assert.equal(false, lib.email(null)); });
1920
});
2021
describe('equals', function() {
2122
it('value', function() { assert.equal(true, lib.equals('3', '3')); });
@@ -150,6 +151,28 @@ describe('validation library', function() {
150151
it('{}', function() { assert.equal(false, lib.required({})); });
151152
it('boolean true', function() { assert.equal(false, lib.required({})); });
152153
});
154+
describe('startsWith', function() {
155+
it('normal case', function() { assert.equal(true, lib.startsWith('kilgore trout','kilg')); });
156+
it('no match', function() { assert.equal(false, lib.startsWith('kilgore trout','a')); });
157+
it('spaces', function() { assert.equal(true, lib.startsWith('kilgore trout','kilgore ')); });
158+
it('null', function() { assert.equal(false, lib.startsWith(null)); });
159+
it('null searchString', function() { assert.equal(false, lib.startsWith('kilgore trout', null)); });
160+
it('non-string searchString', function() { assert.equal(false, lib.startsWith('kilgore trout', 3)); });
161+
it('trims value', function() { assert.equal(true, lib.startsWith(' kilgore trout','kilg')); });
162+
it('does not trim searchString', function() { assert.equal(false, lib.startsWith('kilgore trout',' kilg')); });
163+
it('does not trim searchString2', function() { assert.equal(false, lib.startsWith(' kilgore trout',' kilg')); });
164+
it('empty string', function() { assert.equal(false, lib.startsWith('','kilgore ')); });
165+
});
166+
describe('url', function() {
167+
it('matches http', function() { assert.equal(true, lib.url('http://react-formstate-validation.test')); });
168+
it('matches https', function() { assert.equal(true, lib.url('https://react-formstate-validation.test')); });
169+
it('matches ftp', function() { assert.equal(true, lib.url('ftp://react-formstate-validation.test')); });
170+
it('does not match relative', function() { assert.equal(false, lib.url('/react-formstate-validation.test')); });
171+
it('does not match schema relative', function() { assert.equal(false, lib.url('//react-formstate-validation.test')); });
172+
it('does not match site relative', function() { assert.equal(false, lib.url('~/react-formstate-validation.test')); });
173+
it('does not match gopher', function() { assert.equal(false, lib.url('gopher://react-formstate-validation.test')); });
174+
it('does not crash', function() { assert.equal(false, lib.url(null)); });
175+
});
153176
});
154177

155178
function mock(validations) {
@@ -304,6 +327,22 @@ describe('Messages', function() {
304327
assert.equal(undefined, v['required']('46','Field'));
305328
});
306329
});
330+
describe('#startsWith', function() {
331+
it('has a message', function() {
332+
assert.equal('Field must start with f', v['startsWith']('','Field','f'));
333+
});
334+
it('might not return a message', function() {
335+
assert.equal(undefined, v['startsWith']('f','Field','f'));
336+
});
337+
});
338+
describe('#url', function() {
339+
it('has a message', function() {
340+
assert.equal('Field must be a url', v['url']('','Field'));
341+
});
342+
it('might not return a message', function() {
343+
assert.equal(undefined, v['url']('http://test.test','Field'));
344+
});
345+
});
307346
});
308347

309348

0 commit comments

Comments
 (0)