· 8 years ago · Jul 30, 2018, 08:02 PM
1import {AbstractControl} from '@angular/forms';
2
3
4const default_email_options = {
5 allow_display_name: false,
6 require_display_name: false,
7 allow_utf8_local_part: true,
8 require_tld: true,
9};
10const default_fqdn_options = {
11 require_tld: true,
12 allow_underscores: false,
13 allow_trailing_dot: false,
14};
15/* tslint:disable:max-line-length */
16const displayName = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\.\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\.\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF\s]*<(.+)>$/i;
17const emailUserPart = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~]+$/i;
18const quotedEmailUser = /^([\s\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e]|(\\[\x01-\x09\x0b\x0c\x0d-\x7f]))*$/i;
19const emailUserUtf8Part = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+$/i;
20const quotedEmailUserUtf8 = /^([\s\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]|(\\[\x01-\x09\x0b\x0c\x0d-\x7f\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))*$/i;
21/* tslint:enable:max-line-length */
22
23export class EmailValidator {
24
25 public static isValidMailFormat(control: AbstractControl) {
26 if (!control.value || control.value == '') {
27 return {isValidMailFormat: true};
28 }
29
30 let str = control.value;
31
32 if (default_email_options.require_display_name || default_email_options.allow_display_name) {
33 const display_email = str.match(displayName);
34 if (display_email) {
35 str = display_email[1];
36 } else if (default_email_options.require_display_name) {
37 return {isValidMailFormat: true};
38 }
39 }
40 const parts = str.split('@');
41 const domain = parts.pop();
42 let user = parts.join('@');
43 const lower_domain = domain.toLowerCase();
44
45
46 if (lower_domain === 'gmail.com' || lower_domain === 'googlemail.com') {
47 user = user.replace(/\./g, '').toLowerCase();
48 }
49
50 if (!EmailValidator.isByteLength(user, {max: 64}) || !EmailValidator.isByteLength(domain, {max: 256})) {
51 return {isValidMailFormat: true};
52 }
53
54 if (!EmailValidator.isFdqn(domain, {require_tld: default_email_options.require_tld})) {
55 return {isValidMailFormat: true};
56 }
57
58 if (user[0] === '"') {
59 user = user.slice(1, user.length - 1);
60 if (default_email_options.allow_utf8_local_part) {
61 if (quotedEmailUserUtf8.test(user)) {
62 return null;
63 }
64 return {isValidMailFormat: true};
65 } else {
66 if (quotedEmailUser.test(user)) {
67 return null;
68 }
69 return {isValidMailFormat: true};
70 }
71 }
72
73 const pattern = default_email_options.allow_utf8_local_part ?
74 emailUserUtf8Part : emailUserPart;
75
76 const user_parts = user.split('.');
77 for (let i = 0; i < user_parts.length; i++) {
78 if (!pattern.test(user_parts[i])) {
79 return {isValidMailFormat: true};
80 }
81 }
82
83 return null;
84 }
85
86
87 private static isFdqn(str, options): boolean {
88 //assertString(str);
89 options = EmailValidator.merge(options, default_fqdn_options);
90
91 /* Remove the optional trailing dot before checking validity */
92 if (options.allow_trailing_dot && str[str.length - 1] === '.') {
93 str = str.substring(0, str.length - 1);
94 }
95 const parts = str.split('.');
96 if (options.require_tld) {
97 const tld = parts.pop();
98 if (!parts.length || !/^([a-z\u00a1-\uffff]{2,}|xn[a-z0-9-]{2,})$/i.test(tld)) {
99 return false;
100 }
101 }
102 for (let part, i = 0; i < parts.length; i++) {
103 part = parts[i];
104 if (options.allow_underscores) {
105 part = part.replace(/_/g, '');
106 }
107 if (!/^[a-z\u00a1-\uffff0-9-]+$/i.test(part)) {
108 return false;
109 }
110 if (/[\uff01-\uff5e]/.test(part)) {
111 // disallow full-width chars
112 return false;
113 }
114 if (part[0] === '-' || part[part.length - 1] === '-') {
115 return false;
116 }
117 }
118 return true;
119 }
120
121
122 private static isByteLength(str, options) {
123 //assertString(str);
124 let min;
125 let max;
126 if (typeof (options) === 'object') {
127 min = options.min || 0;
128 max = options.max;
129 } else { // backwards compatibility: isByteLength(str, min [, max])
130 min = arguments[1];
131 max = arguments[2];
132 }
133 const len = encodeURI(str).split(/%..|./).length - 1;
134 return len >= min && (typeof max === 'undefined' || len <= max);
135 }
136
137
138 private static merge(obj = {}, defaults) {
139 for (const key in defaults) {
140 if (typeof obj[key] === 'undefined') {
141 obj[key] = defaults[key];
142 }
143 }
144 return obj;
145 }
146
147}