├── .gitignore ├── .jshintignore ├── .jshintrc ├── .travis.yml ├── README.md ├── component.json ├── package.json ├── test ├── domain.js ├── email.js ├── mocha.opts └── url.js └── validation.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /coverage/ 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | // JSHint Default Configuration File (as on JSHint website) 3 | // See http://jshint.com/docs/ for more details 4 | 5 | "maxerr" : 50, // {int} Maximum error before stopping 6 | 7 | // Enforcing 8 | "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) 9 | "camelcase" : false, // true: Identifiers must be in camelCase 10 | "curly" : true, // true: Require {} for every new block or scope 11 | "eqeqeq" : true, // true: Require triple equals (===) for comparison 12 | "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() 13 | "immed" : true, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` 14 | "indent" : 4, // {int} Number of spaces to use for indentation 15 | "latedef" : true, // true: Require variables/functions to be defined before being used 16 | "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()` 17 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` 18 | "noempty" : true, // true: Prohibit use of empty blocks 19 | "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) 20 | "plusplus" : true, // true: Prohibit use of `++` & `--` 21 | "quotmark" : "single", // Quotation mark consistency: 22 | // false : do nothing (default) 23 | // true : ensure whatever is used is consistent 24 | // "single" : require single quotes 25 | // "double" : require double quotes 26 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) 27 | "unused" : "vars", // true: Require all defined variables be used 28 | "strict" : false, // true: Requires all functions run in ES5 Strict Mode 29 | "trailing" : true, // true: Prohibit trailing whitespaces 30 | "maxparams" : false, // {int} Max number of formal params allowed per function 31 | "maxdepth" : false, // {int} Max depth of nested blocks (within functions) 32 | "maxstatements" : false, // {int} Max number statements per function 33 | "maxcomplexity" : false, // {int} Max cyclomatic complexity per function 34 | "maxlen" : false, // {int} Max number of characters per line 35 | 36 | // Relaxing 37 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) 38 | "boss" : false, // true: Tolerate assignments where comparisons would be expected 39 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. 40 | "eqnull" : false, // true: Tolerate use of `== null` 41 | "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) 42 | "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) 43 | // (ex: `for each`, multiple try/catch, function expression…) 44 | "evil" : false, // true: Tolerate use of `eval` and `new Function()` 45 | "expr" : false, // true: Tolerate `ExpressionStatement` as Programs 46 | "funcscope" : false, // true: Tolerate defining variables inside control statements" 47 | "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') 48 | "iterator" : false, // true: Tolerate using the `__iterator__` property 49 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block 50 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings 51 | "laxcomma" : false, // true: Tolerate comma-first style coding 52 | "loopfunc" : true, // true: Tolerate functions being defined in loops 53 | "multistr" : false, // true: Tolerate multi-line strings 54 | "proto" : false, // true: Tolerate using the `__proto__` property 55 | "scripturl" : false, // true: Tolerate script-targeted URLs 56 | "smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment 57 | "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` 58 | "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation 59 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` 60 | "validthis" : false, // true: Tolerate using this in a non-constructor function 61 | 62 | // Environments 63 | "browser" : false, // Web Browser (window, document, etc) 64 | "couch" : false, // CouchDB 65 | "devel" : true, // Development/debugging (alert, confirm, etc) 66 | "dojo" : false, // Dojo Toolkit 67 | "jquery" : true, // jQuery 68 | "mootools" : false, // MooTools 69 | "node" : true, // Node.js 70 | "nonstandard" : true, // Widely adopted globals (escape, unescape, etc) 71 | "prototypejs" : false, // Prototype and Scriptaculous 72 | "rhino" : false, // Rhino 73 | "worker" : false, // Web Workers 74 | "wsh" : false, // Windows Scripting Host 75 | "yui" : false, // Yahoo User Interface 76 | 77 | // Legacy 78 | "nomen" : false, // true: Prohibit dangling `_` in variables 79 | "onevar" : false, // true: Allow only one `var` statement per function 80 | "passfail" : false, // true: Stop on first error 81 | "white" : true, // true: Check against strict whitespace and indentation rules 82 | 83 | // Custom Globals 84 | "predef" : [ // additional predefined global variables 85 | "TR", 86 | "TRPAT", 87 | "GETTEXT", 88 | "GETSTATICURL", 89 | "INCLUDE", 90 | "define", 91 | "require" 92 | ] 93 | } 94 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8.11.1" 4 | before_install: "npm install -g npm && npm cache verify" 5 | script: "npm run-script travis" 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # one-validation 2 | 3 | [![NPM version](https://badge.fury.io/js/one-validation.png)](http://badge.fury.io/js/one-validation) 4 | [![Build Status](https://travis-ci.org/One-com/one-validation.png?branch=master)](https://travis-ci.org/One-com/one-validation) 5 | [![Coverage Status](https://coveralls.io/repos/One-com/one-validation/badge.png)](https://coveralls.io/r/One-com/one-validation) 6 | [![Dependency Status](https://david-dm.org/One-com/one-validation.png)](https://david-dm.org/One-com/one-validation) 7 | 8 | This is a collection of regular expressions for general validation purposes. 9 | The basic design concept is to split up the regexes into semantic parts of the pattern to match. 10 | As an example a url consists of many parts like scheme, optional userinfo, subdomain, domain, toplevel domain, path, query and fragment. 11 | It is a lot easier to write a maintainable and reusable regular expression by mathing each of these parts individually and write a regex that combines the individual later. 12 | 13 | The library includes a TLD whitelist that can be updated using make. 14 | 15 | This module works as a NodeJS CommonJS module, a require.js AMD module and falls back to exposing itself in the global scope on `one.validation` if included directly in the page. 16 | 17 | Package managers: 18 | * npm: `npm install one-validation` 19 | * bower: `bower install validation` 20 | 21 | ## Supported patterns 22 | 23 | * domain 24 | * email 25 | * url 26 | 27 | ## Examples 28 | 29 | ### domain and domainIdn 30 | 31 | ``` 32 | validation.domain.test('foo.co.uk'); 33 | return true; 34 | ``` 35 | 36 | ``` 37 | validation.domainIdn.test('hällo-test.de'); 38 | return true; 39 | ``` 40 | 41 | ### email and emailIdn 42 | 43 | ``` 44 | validation.email.test('test@foo.co.uk'); 45 | return true; 46 | ``` 47 | 48 | ``` 49 | validation.domainIdn.test('test@hällo-test.de'); 50 | return true; 51 | ``` 52 | 53 | ## Building 54 | 55 | ``` 56 | npm install 57 | make 58 | ``` 59 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "validation", 3 | "version": "1.2.0", 4 | "main": [ 5 | "validation.js" 6 | ], 7 | "dependencies": { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "maintainers": [ 3 | { 4 | "name": "Peter Müller", 5 | "email": "munter@fumle.dk" 6 | }, 7 | { 8 | "name": "Andreas Lind Petersen", 9 | "email": "andreas@one.com" 10 | } 11 | ], 12 | "name": "one-validation", 13 | "description": "Regexp based validation collection for common internet validation tasks", 14 | "version": "2.2.3", 15 | "repository": { 16 | "url": "git://github.com/One-com/one-validation.git" 17 | }, 18 | "engines": { 19 | "node": ">=0.4.0" 20 | }, 21 | "dependencies": {}, 22 | "devDependencies": { 23 | "coveralls": "=2.11.1", 24 | "istanbul": "=0.3.0", 25 | "jshint": "=2.5.2", 26 | "mocha": "^10.2.0", 27 | "punycode": ">=0.2.0", 28 | "unexpected": "=3.2.3" 29 | }, 30 | "scripts": { 31 | "test": "mocha", 32 | "prepublish": "npm test", 33 | "lint": "jshint .", 34 | "travis": "npm run lint && NODE_ENV=development ./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha -- --reporter dot && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js", 35 | "coverage": "NODE_ENV=development ./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha -- --reporter dot" 36 | }, 37 | "publishConfig": { 38 | "repository": "http://registry.npmjs.org/" 39 | }, 40 | "main": "./validation.js" 41 | } 42 | -------------------------------------------------------------------------------- /test/domain.js: -------------------------------------------------------------------------------- 1 | /*global beforeEach, describe, it*/ 2 | var unexpected = require('unexpected'), 3 | oneValidation = require('../validation'); 4 | 5 | describe('domain validation', function () { 6 | var regExp, 7 | expect = unexpected.clone().addAssertion('[not] to pass', function (expect, subject) { 8 | expect(subject, '[not] to match', regExp); 9 | }); 10 | 11 | describe('#domain', function () { 12 | beforeEach(function () { 13 | regExp = oneValidation.domain; 14 | }); 15 | 16 | it('should allow a two char tld', function () { 17 | expect('bar.ab', 'to pass'); 18 | }); 19 | 20 | it('should disallow a single char tld', function () { 21 | expect('bar.a', 'not to pass'); 22 | }); 23 | 24 | it('should disallow underscore in domain with no subdomain', function () { 25 | expect('_example.com', 'not to pass'); 26 | }); 27 | 28 | it('should disallow underscore in domain with valid subdomain', function () { 29 | expect('_sub._example.com', 'not to pass'); 30 | }); 31 | 32 | it('should allow underscore at start of subdomain', function () { 33 | expect('_sub.example.com', 'to pass'); 34 | }); 35 | 36 | it('should allow underscore in middle of subdomain', function () { 37 | expect('sub_domain.example.com', 'to pass'); 38 | }); 39 | 40 | it('should allow underscore in end of subdomain', function () { 41 | expect('sub_.example.com', 'to pass'); 42 | }); 43 | 44 | it('should allow underscore in multiple subdomains', function () { 45 | expect('_sub1.sub_2.sub3_.example.com', 'to pass'); 46 | }); 47 | 48 | it('should allow a punycode tld', function () { 49 | expect('bar.xn--fjq720a', 'to pass'); 50 | }); 51 | 52 | it('should disallow an invalid tld', function () { 53 | expect('bar.c7a', 'not to pass'); 54 | }); 55 | 56 | it('should disallow an invalid punycode tld', function () { 57 | expect('bar.xn--6ca', 'not to pass'); 58 | }); 59 | }); 60 | 61 | describe('#domainIdn', function () { 62 | beforeEach(function () { 63 | regExp = oneValidation.domainIdn; 64 | }); 65 | 66 | it('should allow a two char tld', function () { 67 | expect('bar.ab', 'to pass'); 68 | }); 69 | 70 | it('should disallow a single char tld', function () { 71 | expect('bar.a', 'not to pass'); 72 | }); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /test/email.js: -------------------------------------------------------------------------------- 1 | /*global beforeEach, describe, it*/ 2 | var unexpected = require('unexpected'), 3 | oneValidation = require('../validation'); 4 | 5 | describe('email validation', function () { 6 | var regExp, 7 | expect = unexpected.clone().addAssertion('[not] to pass', function (expect, subject) { 8 | expect(subject, '[not] to match', regExp); 9 | }); 10 | 11 | describe('#email', function () { 12 | beforeEach(function () { 13 | regExp = oneValidation.email; 14 | }); 15 | 16 | it('should accept valid input', function () { 17 | expect('foo@bar.dk', 'to pass'); 18 | expect('foo@bar.somenewtld', 'to pass'); 19 | expect('foo.@bar.dk', 'to pass'); 20 | }); 21 | 22 | it('should reject invalid input', function () { 23 | expect('foo@bar', 'not to pass'); 24 | expect('foo@dk', 'not to pass'); 25 | expect('foo@例子.测试', 'not to pass'); 26 | expect('foo@उदाहरण.परीक्षा', 'not to pass'); 27 | }); 28 | }); 29 | 30 | describe('#emailIdn', function () { 31 | beforeEach(function () { 32 | regExp = oneValidation.emailIdn; 33 | }); 34 | 35 | it('should accept valid input', function () { 36 | expect('foo@bar.dk', 'to pass'); 37 | expect('foo@bar.somenewtld', 'to pass'); 38 | expect('foo.@bar.dk', 'to pass'); 39 | expect('foo@例子.测试', 'to pass'); 40 | expect('foo.@उदाहरण.परीक्षा', 'to pass'); 41 | }); 42 | 43 | it('should reject invalid input', function () { 44 | expect('foo@bar', 'not to pass'); 45 | expect('foo@dk', 'not to pass'); 46 | }); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter spec 2 | --recursive 3 | --check-leaks 4 | -------------------------------------------------------------------------------- /test/url.js: -------------------------------------------------------------------------------- 1 | /*global beforeEach, describe, it*/ 2 | var unexpected = require('unexpected'), 3 | oneValidation = require('../validation'); 4 | 5 | describe('url validation', function () { 6 | var regExp, 7 | expect = unexpected.clone().addAssertion('[not] to pass', function (expect, subject) { 8 | expect(subject, '[not] to match', regExp); 9 | }); 10 | 11 | describe('#httpUrl', function () { 12 | beforeEach(function () { 13 | regExp = oneValidation.httpUrl; 14 | }); 15 | 16 | it('should accept valid input', function () { 17 | expect('http://www.foo.com/', 'to pass'); 18 | expect('http://www.foo.com?query=simple', 'to pass'); 19 | expect('http://www.foo.com?query=where&is=theTruth', 'to pass'); 20 | expect('http://www.foo.com?query=where&is=theTruth#in=the&world=true', 'to pass'); 21 | expect('http://www.foo.com/~username/', 'to pass'); 22 | expect('http://www.foo.com/?', 'to pass'); 23 | expect('http://www.foo.com/?#', 'to pass'); 24 | expect('http://example.com/foo/bar&something=blah', 'to pass'); 25 | expect('http://foo.com/blah_blah', 'to pass'); 26 | expect('http://foo.com/blah_blah/', 'to pass'); 27 | expect('http://foo.com/blah_blah_(wikipedia)', 'to pass'); 28 | expect('http://foo.com/blah_blah_(wikipedia)_(again)', 'to pass'); 29 | expect('http://www.example.com/wpstyle/?p=364', 'to pass'); 30 | expect('https://www.example.com/foo/?bar=baz&inga=42&quux', 'to pass'); 31 | expect('http://userid:password@example.com:8080', 'to pass'); 32 | expect('http://userid:password@example.com:8080/', 'to pass'); 33 | expect('http://userid@example.com', 'to pass'); 34 | expect('http://userid@example.com/', 'to pass'); 35 | expect('http://userid@example.com:8080', 'to pass'); 36 | expect('http://userid@example.com:8080/', 'to pass'); 37 | expect('http://userid:password@example.com', 'to pass'); 38 | expect('http://userid:password@example.com/', 'to pass'); 39 | expect('http://foo.com/blah_(wikipedia)#cite-1', 'to pass'); 40 | expect('http://foo.com/blah_(wikipedia)_blah#cite-1', 'to pass'); 41 | expect('http://foo.com/(something)?after=parens', 'to pass'); 42 | expect('http://code.google.com/events/#&product=browser', 'to pass'); 43 | expect('http://j.mp', 'to pass'); 44 | expect('http://foo.com/?q=Test%20URL-encoded%20stuff', 'to pass'); 45 | expect('http://-.~_!$&\'()*+,;=:%40:80%2f::::::@example.com', 'to pass'); 46 | expect('http://1337.net', 'to pass'); 47 | expect('http://a.b-c.de', 'to pass'); 48 | expect('http://www.hitta.se/karta#tool=coordinate&vkid=dWR_Xnqbbk¢er=6411726:1290882&zl=9&type=map&bounds=6411510:1290540,6411943:1291224', 'to pass'); 49 | // IP addresses for hostnames 50 | expect('http://142.42.1.1/', 'to pass'); 51 | expect('http://142.42.1.1:8080/', 'to pass'); 52 | expect('http://223.255.255.254', 'to pass'); 53 | // New TLDs 54 | expect('http://www.foo.cq/', 'to pass'); 55 | expect('http://www.foo.randomtld/', 'to pass'); 56 | // SLDs 57 | expect('http://foo.co.uk', 'to pass'); 58 | expect('http://foo.org.uk', 'to pass'); 59 | }); 60 | 61 | it('should reject invalid input', function () { 62 | expect('http://localhost/', 'not to pass'); 63 | expect('@.foo.com/?', 'not to pass'); 64 | expect('http://1.1.256.1', 'not to pass'); 65 | expect('http://1.1.1.300', 'not to pass'); 66 | }); 67 | }); 68 | 69 | describe('#httpUrlIdn', function () { 70 | beforeEach(function () { 71 | regExp = oneValidation.httpUrlIdn; 72 | }); 73 | 74 | it('should accept valid input', function () { 75 | expect('http://✪df.ws/123', 'to pass'); 76 | expect('http://⌘.ws', 'to pass'); 77 | expect('http://⌘.ws/', 'to pass'); 78 | expect('http://☺.damowmow.com/', 'to pass'); 79 | expect('http://مثال.إختبار', 'to pass'); 80 | expect('http://例子.测试', 'to pass'); 81 | expect('http://उदाहरण.परीक्षा', 'to pass'); 82 | }); 83 | 84 | it('should reject invalid input', function () { 85 | expect('http://foo.com/unicode_(✪)_in_parens', 'not to pass'); 86 | expect('http://➡.ws/䨹', 'not to pass'); 87 | expect('http://1.1.256.1', 'not to pass'); 88 | expect('http://1.1.1.300', 'not to pass'); 89 | }); 90 | }); 91 | 92 | describe('#domainIdn', function () { 93 | beforeEach(function () { 94 | regExp = oneValidation.domainIdn; 95 | }); 96 | 97 | it('should accept valid input'); 98 | 99 | it('should reject invalid input', function () { 100 | expect('foo:bar.com', 'not to pass'); 101 | expect('foo/bar.com', 'not to pass'); 102 | expect('foo%bar.com', 'not to pass'); 103 | }); 104 | }); 105 | 106 | describe('#rootRelativeUrl', function () { 107 | beforeEach(function () { 108 | regExp = oneValidation.rootRelativeUrl; 109 | }); 110 | 111 | it('should accept valid input', function () { 112 | expect('/', 'to pass'); 113 | expect('/foo.bar', 'to pass'); 114 | expect('/foo.bar/baz.quux?foo=bar&quux=zoo#pronk', 'to pass'); 115 | }); 116 | 117 | it('should reject invalid input', function () { 118 | expect('', 'not to pass'); 119 | }); 120 | }); 121 | }); 122 | -------------------------------------------------------------------------------- /validation.js: -------------------------------------------------------------------------------- 1 | /*global module, define*/ 2 | 3 | (function (root, factory) { 4 | if (typeof exports === 'object') { 5 | module.exports = factory(); 6 | } else if (typeof define === 'function' && define.amd) { 7 | define(factory); 8 | } else { 9 | root.one = root.one || {}; 10 | root.one.validation = factory(); 11 | } 12 | }(this, function () { 13 | 'use strict'; 14 | 15 | // Poor man's /x flag: 16 | // new RegExp(concatRegExps( 17 | // /blabla/, 18 | // /blablabla/ 19 | // ), 'i').test(string); 20 | function concatRegExps() { // ... 21 | var source = '', 22 | i = 0; 23 | for (; i < arguments.length; i += 1) { 24 | if (Object.prototype.toString.call(arguments[i]) === '[object RegExp]') { 25 | source += arguments[i].source; 26 | } else { 27 | source += arguments[i]; 28 | } 29 | } 30 | return source; 31 | } 32 | 33 | var ipv4DigitRegExpSource = /(?:[0-9]|1?[0-9][0-9]|2[0-4][0-9]|25[0-5])/.source, 34 | validation = { 35 | functions: {} 36 | }, 37 | fragments = { 38 | visibleNonAsciiChar: /[ªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͰ-ʹͶͷͺ-ͽΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԥԱ-Ֆՙա-ևא-תװ-ײء-يٮٯٱ-ۓەۥۦۮۯۺ-ۼۿܐܒ-ܯݍ-ޥޱߊ-ߪߴߵߺࠀ-ࠕࠚࠤࠨऄ-हऽॐक़-ॡॱॲॹ-ॿঅ-ঌএঐও-নপ-রলশ-হঽৎড়ঢ়য়-ৡৰৱਅ-ਊਏਐਓ-ਨਪ-ਰਲਲ਼ਵਸ਼ਸਹਖ਼-ੜਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલળવ-હઽૐૠૡଅ-ଌଏଐଓ-ନପ-ରଲଳଵ-ହଽଡ଼ଢ଼ୟ-ୡୱஃஅ-ஊஎ-ஐஒ-கஙசஜஞடணதந-பம-ஹௐఅ-ఌఎ-ఐఒ-నప-ళవ-హఽౘౙౠౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽೞೠೡഅ-ഌഎ-ഐഒ-നപ-ഹഽൠൡൺ-ൿඅ-ඖක-නඳ-රලව-ෆก-ะาำเ-ๆກຂຄງຈຊຍດ-ທນ-ຟມ-ຣລວສຫອ-ະາຳຽເ-ໄໆໜໝༀཀ-ཇཉ-ཬྈ-ྋက-ဪဿၐ-ၕၚ-ၝၡၥၦၮ-ၰၵ-ႁႎႠ-Ⴥა-ჺჼᄀ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏼᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗៜᠠ-ᡷᢀ-ᢨᢪᢰ-ᣵᤀ-ᤜᥐ-ᥭᥰ-ᥴᦀ-ᦫᧁ-ᧇᨀ-ᨖᨠ-ᩔᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮᮯᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₔℂℇℊ-ℓℕℙ-ℝℤΩℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎↃↄⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⴀ-ⴥⴰ-ⵥⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ々〆〱-〵〻〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆷㇰ-ㇿ㐀-䶵一-鿋ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪꘫꙀ-ꙟꙢ-ꙮꙿ-ꚗꚠ-ꛥꜗ-ꜟꜢ-ꞈꞋꞌꟻ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺꪀ-ꪯꪱꪵꪶꪹ-ꪽꫀꫂꫛ-ꫝꯀ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-鶴侮-舘並-龎ff-stﬓ-ﬗיִײַ-ﬨשׁ-זּטּ-לּמּנּסּףּפּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ̀-ͯ҃-҉֑-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٞۖ-ۜ۞-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࠭ऀ-ः़ा-ॎ॑-ॕॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఁ-ఃా-ౄె-ైొ-్ౕౖౢౣಂಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣംഃാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྐ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳា-៓៝᠋-᠍ᢩᤠ-ᤫᤰ-᤻ᦰ-ᧀᧈᧉᨗ-ᨛᩕ-ᩞ᩠-᩿᩼ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-᮪ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ᷀-᷽ᷦ-᷿⃐-⃰⳯-⳱ⷠ-〪ⷿ-゙゚〯꙯-꙲꙼꙽꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-꣄꣠-꣱ꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꨩ-ꨶꩃꩌꩍꩻꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︦²³¹¼-¾٠-٩۰-۹߀-߉०-९০-৯৴-৹੦-੯૦-૯୦-୯௦-௲౦-౯౸-౾೦-೯൦-൵๐-๙໐-໙༠-༳၀-၉႐-႙፩-፼ᛮ-ᛰ០-៩៰-៹᠐-᠙᥆-᥏᧐-᧚᪀-᪉᪐-᪙᭐-᭙᮰-᮹᱀-᱉᱐-᱙⁰⁴-⁹₀-₉⅐-ↂↅ-↉①-⒛⓪-⓿❶-➓⳽〇〡-〩〸-〺㆒-㆕㈠-㈩㉑-㉟㊀-㊉㊱-㊿꘠-꘩ꛦ-ꛯ꠰-꠵꣐-꣙꤀-꤉꧐-꧙꩐-꩙꯰-꯹0-9¡«·»¿;·՚-՟։֊־׀׃׆׳״؉؊،؍؛؞؟٪-٭۔܀-܍߷-߹࠰-࠾।॥॰෴๏๚๛༄-༒༺-༽྅࿐-࿔၊-၏჻፡-፨᐀᙭᙮᚛᚜᛫-᛭᜵᜶។-៖៘-៚᠀-᠊᥄᥅᧞᧟᨞᨟᪠-᪦᪨-᪭᭚-᭠᰻-᰿᱾᱿᳓‐-‧‰-⁃⁅-⁑⁓-⁞⁽⁾₍₎〈〉❨-❵⟅⟆⟦-⟯⦃-⦘⧘-⧛⧼⧽⳹-⳼⳾⳿⸀-⸮⸰⸱、-〃〈-】〔-〟〰〽゠・꓾꓿꘍-꘏꙳꙾꛲-꛷꡴-꡷꣎꣏꣸-꣺꤮꤯꥟꧁-꧍꧞꧟꩜-꩟꫞꫟꯫﴾﴿︐-︙︰-﹒﹔-﹡﹣﹨﹪﹫!-#%-*,-/:;?@[-]_{}⦅-・¢-©¬®-±´¶¸×÷˂-˅˒-˟˥-˫˭˯-˿͵΄΅϶҂؆-؈؋؎؏۩۽۾߶৲৳৺৻૱୰௳-௺౿ೱೲ൹฿༁-༃༓-༗༚-༟༴༶༸྾-࿅࿇-࿌࿎࿏࿕-࿘႞႟፠᎐-᎙៛᥀᧠-᧿᭡-᭪᭴-᭼᾽᾿-῁῍-῏῝-῟῭-`´῾⁄⁒⁺-⁼₊-₌₠-₸℀℁℃-℆℈℉℔№-℘℞-℣℥℧℩℮℺℻⅀-⅄⅊-⅍⅏←-⌨⌫-⏨␀-␦⑀-⑊⒜-ⓩ─-⛍⛏-⛡⛣⛨-⛿✁-✄✆-✉✌-✧✩-❋❍❏-❒❖-❞❡-❧➔➘-➯➱-➾⟀-⟄⟇-⟊⟌⟐-⟥⟰-⦂⦙-⧗⧜-⧻⧾-⭌⭐-⭙⳥-⳪⺀-⺙⺛-⻳⼀-⿕⿰-⿻〄〒〓〠〶〷〾〿゛゜㆐㆑㆖-㆟㇀-㇣㈀-㈞㈪-㉐㉠-㉿㊊-㊰㋀-㋾㌀-㏿䷀-䷿꒐-꓆꜀-꜖꜠꜡꞉꞊꠨-꠫꠶-꠹꩷-꩹﬩﷼﷽﹢﹤-﹦﹩$+<->^`|~¢-₩│-\uffee\ufffc\ufffd]/, 39 | printableAsciiChar: /[\x21-\x7e]*/, 40 | visibleAsciiChar: /[\x20-\x7e]*/, 41 | subDomainPart: /[_a-z0-9](?:[_\-a-z0-9]*[_a-z0-9])?/i, 42 | domainPart: /[a-z0-9](?:[\-a-z0-9]*[a-z0-9])?/i, 43 | punycodeTld: /xn\-\-[a-z0-9]{4,}/i, 44 | tld: /[a-z][\-a-z]*[a-z]/i, 45 | port: /\d{1,5}/, 46 | localpart: /[a-z0-9!#$%&'*+\/=?\^_`{|}~\-:][\.a-z0-9!#$%&'*+\/=?\^_`{|}~\-]*/i, // taken from: http://www.regular-expressions.info/email.html 47 | user: /[^:@\/]+/i, 48 | uuid: /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i, 49 | lowerCaseUuid: /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/, 50 | upperCaseUuid: /[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}/, 51 | password: /[^@\/]+?/i, 52 | pathname: /[\w\-\.~%!$&'\(\)*+,;=:@\/]*/, 53 | search: /[\w\-\.~%!$&'\(\)*+,;=:@\/?]*/, 54 | hash: /[\w\-\.~%!$&'\(\)*+,;=:@\/?#]*/, 55 | ipv4: new RegExp('(?:' + ipv4DigitRegExpSource + '\\.){3}' + ipv4DigitRegExpSource) 56 | }, 57 | name; 58 | 59 | // Highlevel regexes composed of regex fragments 60 | var alphanumericalPlusVisibleNonAsciiChars = new RegExp(fragments.visibleNonAsciiChar.source.replace(/^\[/, '[a-z0-9')), 61 | alphabeticalPlusVisibleNonAsciiChars = new RegExp(fragments.visibleNonAsciiChar.source.replace(/^\[/, '[a-z')); 62 | fragments.domainPartIdn = new RegExp(concatRegExps(alphanumericalPlusVisibleNonAsciiChars, 63 | '(?:', 64 | new RegExp(alphanumericalPlusVisibleNonAsciiChars.source.replace(/^\[/, '[\\-') + '*'), 65 | alphanumericalPlusVisibleNonAsciiChars, 66 | ')?')); 67 | fragments.tldIdn = new RegExp(concatRegExps(alphabeticalPlusVisibleNonAsciiChars, 68 | new RegExp(alphabeticalPlusVisibleNonAsciiChars.source.replace(/^\[/, '[\\-') + '*'), 69 | alphabeticalPlusVisibleNonAsciiChars)); 70 | 71 | // TODO: Consider disallowing invalid IDN domains, e.g. one-letter TLD "foo.æ" which has an ASCII representation of "foo.xn--6ca" 72 | 73 | // punycodetld source should come first than the normal tld because its a subset of normal tld. 74 | fragments.domain = fragments.domainName = new RegExp('(?:' + fragments.subDomainPart.source + '\\.)*' + '(?:' + fragments.domainPart.source + '\\.)+' + '(?:' + '(?:' + fragments.punycodeTld.source + ')' + '|(?:' + fragments.tld.source + '))', 'i'); 75 | fragments.domainIdn = fragments.domainNameIdn = new RegExp('(?:' + fragments.domainPartIdn.source + '\\.)+' + fragments.tldIdn.source, 'i'); 76 | 77 | fragments.email = fragments.emailAddress = new RegExp(fragments.localpart.source + '@' + fragments.domain.source, 'i'); 78 | fragments.emailIdn = fragments.emailAddressIdn = new RegExp(fragments.localpart.source + '@' + fragments.domainIdn.source, 'i'); 79 | fragments.mailtoUrl = new RegExp('mailto:' + fragments.email.source, 'i'); // TODO: This needs to be improved 80 | fragments.mailtoUrlIdn = new RegExp('mailto:' + fragments.emailIdn.source, 'i'); // TODO: This needs to be improved 81 | 82 | // Same as location.pathname + location.search + location.hash in the browser: 83 | fragments.searchHash = new RegExp(concatRegExps( 84 | '(?:\\?', fragments.search, ')?', 85 | '(?:#', fragments.hash, ')?' 86 | )); 87 | fragments.pathnameSearchHash = new RegExp(concatRegExps( 88 | '(?:', fragments.pathname, 89 | fragments.searchHash, 90 | ')?' // See http://www.ietf.org/rfc/rfc1738.txt 91 | )); 92 | 93 | // Root-relative URL. Same as pathnameSearchHash, except it can't be empty 94 | fragments.rootRelativeUrl = new RegExp(concatRegExps( 95 | '/', 96 | new RegExp(fragments.pathnameSearchHash.source.replace(/\?$/, '') 97 | ))); 98 | 99 | function createHttpishUrlRegExp(options) { 100 | // [protocol"://"[username[":"password]"@"]hostname[":"port]"/"?][path]["?"querystring]["#"fragment] 101 | options = options || {}; 102 | return new RegExp(concatRegExps( 103 | (options.scheme || 'https?'), '://', 104 | '(?:', 105 | fragments.user, 106 | '(?::', 107 | fragments.password, 108 | ')?@', 109 | ')?', 110 | '(?:', 111 | (options.localhost ? 'localhost|' : ''), 112 | (options.idn ? fragments.domainIdn : fragments.domain), 113 | '|', 114 | fragments.ipv4, 115 | ')', 116 | '(?::', fragments.port, ')?', 117 | '(?:/', fragments.pathnameSearchHash, '|', fragments.searchHash, ')' 118 | ), 'i'); 119 | } 120 | 121 | fragments.httpUrl = createHttpishUrlRegExp({scheme: /https?/}); 122 | fragments.httpUrlIdn = createHttpishUrlRegExp({scheme: /https?/, idn: true}); 123 | fragments.ftpUrl = createHttpishUrlRegExp({scheme: /ftp/}); 124 | fragments.ftpUrlIdn = createHttpishUrlRegExp({scheme: /ftp/, idn: true}); 125 | 126 | function getFlagsStringFromRegExp(regExp) { 127 | var flagsString = ''; 128 | if (regExp.ignoreCase) { 129 | flagsString += 'i'; 130 | } 131 | if (regExp.global) { 132 | flagsString += 'g'; 133 | } 134 | if (regExp.multiline) { 135 | flagsString += 'm'; 136 | } 137 | return flagsString; 138 | } 139 | 140 | // Add convenience regexes and functions 141 | for (name in fragments) { 142 | if (fragments.hasOwnProperty(name)) { 143 | validation[name] = new RegExp('^' + fragments[name].source + '$', getFlagsStringFromRegExp(fragments[name])); 144 | validation.functions[name] = (function (name) { 145 | return function (value) { 146 | return validation[name].test(value); 147 | }; 148 | }(name)); 149 | } 150 | } 151 | 152 | // Expose regex fragments for matching inside larger texts 153 | validation.fragments = fragments; 154 | 155 | validation.createHttpishUrlRegExp = createHttpishUrlRegExp; 156 | 157 | return validation; 158 | })); 159 | 160 | --------------------------------------------------------------------------------