From 85990afafa20cd7f131c26e4dc76d6c9ff0e2bb2 Mon Sep 17 00:00:00 2001 From: YangYang Yu Date: Mon, 9 Sep 2019 16:58:54 +0800 Subject: [PATCH 1/4] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index b852d9d..045b310 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +# Jobs + +![](https://gw.alipayobjects.com/mdn/prod_intro/afts/img/A*LishR5jCiHsAAAAAAAAAAABkARQnAQ) + # Description Angular2 custom validation, inspired by jQuery validation. From 1ea3e1806668e8645be0dbdf9b946a709eb3bcb3 Mon Sep 17 00:00:00 2001 From: YangYang Yu Date: Mon, 9 Sep 2019 17:02:04 +0800 Subject: [PATCH 2/4] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 045b310..38a9f07 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ ![](https://gw.alipayobjects.com/mdn/prod_intro/afts/img/A*LishR5jCiHsAAAAAAAAAAABkARQnAQ) +> Deprecated, you can fork and publish yours. + # Description Angular2 custom validation, inspired by jQuery validation. From 2e14cea0939a4dabe3038c232a63a56e3ccec3f9 Mon Sep 17 00:00:00 2001 From: YangYang Yu Date: Fri, 27 Sep 2019 11:32:29 +0800 Subject: [PATCH 3/4] Update README.md --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 38a9f07..2c323cb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,3 @@ -# Jobs - -![](https://gw.alipayobjects.com/mdn/prod_intro/afts/img/A*LishR5jCiHsAAAAAAAAAAABkARQnAQ) - > Deprecated, you can fork and publish yours. # Description From 2939b0ca2e4b35fd824285914aa5d2e7945d0ead Mon Sep 17 00:00:00 2001 From: Ilan Date: Sun, 23 May 2021 20:24:13 +0300 Subject: [PATCH 4/4] fix(url) Add localhost & fix IP validation - Split regex into vars for readability - Add support for localhost - Fix IP regex - Add tests --- src/url/validator.spec.ts | 43 ++++++++++++++++++++++++++++----------- src/url/validator.ts | 23 +++++++++++++++------ 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/src/url/validator.spec.ts b/src/url/validator.spec.ts index 62e07a9..06c3d8c 100644 --- a/src/url/validator.spec.ts +++ b/src/url/validator.spec.ts @@ -1,8 +1,9 @@ import { FormControl, ValidatorFn } from '@angular/forms'; -import { url } from './'; +import { url } from './validator'; describe('Url', () => { + const VALIDATOR_ERROR = {url: true}; let control: FormControl; let validator: ValidatorFn; @@ -10,18 +11,36 @@ describe('Url', () => { validator = url; }); - it('"http://www.test.com" should equal to "null"', () => { - control = new FormControl('http://www.test.com'); - expect(validator(control)).toBeNull() - }); + const validTestCases = [ + "http://test.com", + "http://www.test.com", + "https://www.test.com", + "http://localhost", + "http://localhost:4200", + "http://192.168.0.1:8000", + "http://172.0.0.1", + "http://my.domain.com:9000/auth/sso/acs", + ]; - it('"https://www.test.com" should equal to "null"', () => { - control = new FormControl('https://www.test.com'); - expect(validator(control)).toBeNull() - }); + const invalidTestCases = [ + "23a", + "test.com", + "1234://my.domain.com", + "http://my.domain.com/text with space", + ]; - it('"23a" should equal to "{url: true}"', () => { - control = new FormControl('23a'); - expect(validator(control)).toEqual({url: true}); + + validTestCases.forEach(validURL => { + it(`"${validURL}" URL should equal to "null"`, async () => { + control = new FormControl(validURL); + expect(validator(control)).toBeNull(); + }); }); + + invalidTestCases.forEach(invalidURL => { + it(`"${invalidURL}" should equal to "${VALIDATOR_ERROR}"`, async () => { + control = new FormControl(invalidURL); + expect(validator(control)).toEqual(VALIDATOR_ERROR); + }); + }) }); diff --git a/src/url/validator.ts b/src/url/validator.ts index 712c43d..1ebd14b 100644 --- a/src/url/validator.ts +++ b/src/url/validator.ts @@ -1,10 +1,21 @@ -import { AbstractControl, Validators, ValidatorFn } from '@angular/forms'; - +import { AbstractControl, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; import { isPresent } from '../util/lang'; -export const url: ValidatorFn = (control: AbstractControl): {[key: string]: boolean} => { - if (isPresent(Validators.required(control))) return null; +const PROTOCOL: RegExp = /(?:(?:(?:https?|ftp):)?\/\/)/; +const OPTIONAL_SUB_DOMAIN: RegExp = /(?:\S+(?::\S*)?@)?/; +const IPV4: RegExp = /(?:(?!(?: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})(?:[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])))/; +const DOMAIN: RegExp = /(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?/ +const LOCALHOST: RegExp = /localhost/; +const DOMAINS = new RegExp(`${OPTIONAL_SUB_DOMAIN.source}(?:${IPV4.source}|${DOMAIN.source}|${LOCALHOST.source})`); +const OPTIONAL_PORT: RegExp = /(?::\d{2,5})?/; +const OPTIONAL_PATH: RegExp = /(?:[/?#]\S*)?/; + +export const url: ValidatorFn = (control: AbstractControl): ValidationErrors => { + if (isPresent(Validators.required(control))) { + return null; + } - let v: string = control.value; - return /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?: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})(?:[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]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(v) ? null : {'url': true}; + const url: string = control.value; + const URL_RE: RegExp = new RegExp(`^${PROTOCOL.source}${DOMAINS.source}${OPTIONAL_PORT.source}${OPTIONAL_PATH.source}$`, 'i'); + return URL_RE.test(url) ? null : { 'url': true }; };