validator.js (54384B)
1 /* 2 * Copyright (c) 2016 Chris O"Hara <cohara87@gmail.com> 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining 5 * a copy of this software and associated documentation files (the 6 * "Software"), to deal in the Software without restriction, including 7 * without limitation the rights to use, copy, modify, merge, publish, 8 * distribute, sublicense, and/or sell copies of the Software, and to 9 * permit persons to whom the Software is furnished to do so, subject to 10 * the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be 13 * included in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 * 23 * NOTE: This utility is derived from https://github.com/chriso/validator.js but it is 24 * **NOT** the same as the original. We have made the following changes: 25 * - Changed mocha tests to xpcshell based tests. 26 * - Merged the following pull requests: 27 * - [isMobileNumber] Added Lithuanian number pattern #667 28 * - Hongkong mobile number #665 29 * - Added option to validate any phone locale #663 30 * - Added validation for ISRC strings #660 31 * - Added isRFC5646 for rfc 5646 #572 32 * - Added isSemVer for version numbers. 33 * - Added isRGBColor for RGB colors. 34 * 35 * UPDATING: PLEASE FOLLOW THE INSTRUCTIONS INSIDE UPDATING.md 36 */ 37 38 "use strict"; 39 40 (function (global, factory) { 41 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 42 typeof define === 'function' && define.amd ? define(factory) : 43 (global.validator = factory()); 44 }(this, function () { 'use strict'; 45 46 function assertString(input) { 47 if (typeof input !== 'string') { 48 throw new TypeError('This library (validator.js) validates strings only'); 49 } 50 } 51 52 function toDate(date) { 53 assertString(date); 54 date = Date.parse(date); 55 return !isNaN(date) ? new Date(date) : null; 56 } 57 58 function toFloat(str) { 59 assertString(str); 60 return parseFloat(str); 61 } 62 63 function toInt(str, radix) { 64 assertString(str); 65 return parseInt(str, radix || 10); 66 } 67 68 function toBoolean(str, strict) { 69 assertString(str); 70 if (strict) { 71 return str === '1' || str === 'true'; 72 } 73 return str !== '0' && str !== 'false' && str !== ''; 74 } 75 76 function equals(str, comparison) { 77 assertString(str); 78 return str === comparison; 79 } 80 81 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { 82 return typeof obj; 83 } : function (obj) { 84 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; 85 }; 86 87 var asyncGenerator = function () { 88 function AwaitValue(value) { 89 this.value = value; 90 } 91 92 function AsyncGenerator(gen) { 93 var front, back; 94 95 function send(key, arg) { 96 return new Promise(function (resolve, reject) { 97 var request = { 98 key: key, 99 arg: arg, 100 resolve: resolve, 101 reject: reject, 102 next: null 103 }; 104 105 if (back) { 106 back = back.next = request; 107 } else { 108 front = back = request; 109 resume(key, arg); 110 } 111 }); 112 } 113 114 function resume(key, arg) { 115 try { 116 var result = gen[key](arg); 117 var value = result.value; 118 119 if (value instanceof AwaitValue) { 120 Promise.resolve(value.value).then(function (arg) { 121 resume("next", arg); 122 }, function (arg) { 123 resume("throw", arg); 124 }); 125 } else { 126 settle(result.done ? "return" : "normal", result.value); 127 } 128 } catch (err) { 129 settle("throw", err); 130 } 131 } 132 133 function settle(type, value) { 134 switch (type) { 135 case "return": 136 front.resolve({ 137 value: value, 138 done: true 139 }); 140 break; 141 142 case "throw": 143 front.reject(value); 144 break; 145 146 default: 147 front.resolve({ 148 value: value, 149 done: false 150 }); 151 break; 152 } 153 154 front = front.next; 155 156 if (front) { 157 resume(front.key, front.arg); 158 } else { 159 back = null; 160 } 161 } 162 163 this._invoke = send; 164 165 if (typeof gen.return !== "function") { 166 this.return = undefined; 167 } 168 } 169 170 if (typeof Symbol === "function" && Symbol.asyncIterator) { 171 AsyncGenerator.prototype[Symbol.asyncIterator] = function () { 172 return this; 173 }; 174 } 175 176 AsyncGenerator.prototype.next = function (arg) { 177 return this._invoke("next", arg); 178 }; 179 180 AsyncGenerator.prototype.throw = function (arg) { 181 return this._invoke("throw", arg); 182 }; 183 184 AsyncGenerator.prototype.return = function (arg) { 185 return this._invoke("return", arg); 186 }; 187 188 return { 189 wrap: function (fn) { 190 return function () { 191 return new AsyncGenerator(fn.apply(this, arguments)); 192 }; 193 }, 194 await: function (value) { 195 return new AwaitValue(value); 196 } 197 }; 198 }(); 199 200 function toString(input) { 201 if ((typeof input === 'undefined' ? 'undefined' : _typeof(input)) === 'object' && input !== null) { 202 if (typeof input.toString === 'function') { 203 input = input.toString(); 204 } else { 205 input = '[object Object]'; 206 } 207 } else if (input === null || typeof input === 'undefined' || isNaN(input) && !input.length) { 208 input = ''; 209 } 210 return String(input); 211 } 212 213 function contains(str, elem) { 214 assertString(str); 215 return str.indexOf(toString(elem)) >= 0; 216 } 217 218 function matches(str, pattern, modifiers) { 219 assertString(str); 220 if (Object.prototype.toString.call(pattern) !== '[object RegExp]') { 221 pattern = new RegExp(pattern, modifiers); 222 } 223 return pattern.test(str); 224 } 225 226 function merge() { 227 var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; 228 var defaults = arguments[1]; 229 230 for (var key in defaults) { 231 if (typeof obj[key] === 'undefined') { 232 obj[key] = defaults[key]; 233 } 234 } 235 return obj; 236 } 237 238 /* eslint-disable prefer-rest-params */ 239 function isByteLength(str, options) { 240 assertString(str); 241 var min = void 0; 242 var max = void 0; 243 if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') { 244 min = options.min || 0; 245 max = options.max; 246 } else { 247 // backwards compatibility: isByteLength(str, min [, max]) 248 min = arguments[1]; 249 max = arguments[2]; 250 } 251 var len = encodeURI(str).split(/%..|./).length - 1; 252 return len >= min && (typeof max === 'undefined' || len <= max); 253 } 254 255 var default_fqdn_options = { 256 require_tld: true, 257 allow_underscores: false, 258 allow_trailing_dot: false 259 }; 260 261 function isFDQN(str, options) { 262 assertString(str); 263 options = merge(options, default_fqdn_options); 264 265 /* Remove the optional trailing dot before checking validity */ 266 if (options.allow_trailing_dot && str[str.length - 1] === '.') { 267 str = str.substring(0, str.length - 1); 268 } 269 var parts = str.split('.'); 270 if (options.require_tld) { 271 var tld = parts.pop(); 272 if (!parts.length || !/^([a-z\u00a1-\uffff]{2,}|xn[a-z0-9-]{2,})$/i.test(tld)) { 273 return false; 274 } 275 } 276 for (var part, i = 0; i < parts.length; i++) { 277 part = parts[i]; 278 if (options.allow_underscores) { 279 part = part.replace(/_/g, ''); 280 } 281 if (!/^[a-z\u00a1-\uffff0-9-]+$/i.test(part)) { 282 return false; 283 } 284 if (/[\uff01-\uff5e]/.test(part)) { 285 // disallow full-width chars 286 return false; 287 } 288 if (part[0] === '-' || part[part.length - 1] === '-') { 289 return false; 290 } 291 } 292 return true; 293 } 294 295 var default_email_options = { 296 allow_display_name: false, 297 require_display_name: false, 298 allow_utf8_local_part: true, 299 require_tld: true 300 }; 301 302 /* eslint-disable max-len */ 303 /* eslint-disable no-control-regex */ 304 var displayName = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\.\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\.\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF\s]*<(.+)>$/i; 305 var emailUserPart = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~]+$/i; 306 var quotedEmailUser = /^([\s\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e]|(\\[\x01-\x09\x0b\x0c\x0d-\x7f]))*$/i; 307 var emailUserUtf8Part = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+$/i; 308 var 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; 309 /* eslint-enable max-len */ 310 /* eslint-enable no-control-regex */ 311 312 function isEmail(str, options) { 313 assertString(str); 314 options = merge(options, default_email_options); 315 316 if (options.require_display_name || options.allow_display_name) { 317 var display_email = str.match(displayName); 318 if (display_email) { 319 str = display_email[1]; 320 } else if (options.require_display_name) { 321 return false; 322 } 323 } 324 325 var parts = str.split('@'); 326 var domain = parts.pop(); 327 var user = parts.join('@'); 328 329 var lower_domain = domain.toLowerCase(); 330 if (lower_domain === 'gmail.com' || lower_domain === 'googlemail.com') { 331 user = user.replace(/\./g, '').toLowerCase(); 332 } 333 334 if (!isByteLength(user, { max: 64 }) || !isByteLength(domain, { max: 256 })) { 335 return false; 336 } 337 338 if (!isFDQN(domain, { require_tld: options.require_tld })) { 339 return false; 340 } 341 342 if (user[0] === '"') { 343 user = user.slice(1, user.length - 1); 344 return options.allow_utf8_local_part ? quotedEmailUserUtf8.test(user) : quotedEmailUser.test(user); 345 } 346 347 var pattern = options.allow_utf8_local_part ? emailUserUtf8Part : emailUserPart; 348 349 var user_parts = user.split('.'); 350 for (var i = 0; i < user_parts.length; i++) { 351 if (!pattern.test(user_parts[i])) { 352 return false; 353 } 354 } 355 356 return true; 357 } 358 359 var ipv4Maybe = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/; 360 var ipv6Block = /^[0-9A-F]{1,4}$/i; 361 362 function isIP(str) { 363 var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; 364 365 assertString(str); 366 version = String(version); 367 if (!version) { 368 return isIP(str, 4) || isIP(str, 6); 369 } else if (version === '4') { 370 if (!ipv4Maybe.test(str)) { 371 return false; 372 } 373 var parts = str.split('.').sort(function (a, b) { 374 return a - b; 375 }); 376 return parts[3] <= 255; 377 } else if (version === '6') { 378 var blocks = str.split(':'); 379 var foundOmissionBlock = false; // marker to indicate :: 380 381 // At least some OS accept the last 32 bits of an IPv6 address 382 // (i.e. 2 of the blocks) in IPv4 notation, and RFC 3493 says 383 // that '::ffff:a.b.c.d' is valid for IPv4-mapped IPv6 addresses, 384 // and '::a.b.c.d' is deprecated, but also valid. 385 var foundIPv4TransitionBlock = isIP(blocks[blocks.length - 1], 4); 386 var expectedNumberOfBlocks = foundIPv4TransitionBlock ? 7 : 8; 387 388 if (blocks.length > expectedNumberOfBlocks) { 389 return false; 390 } 391 // initial or final :: 392 if (str === '::') { 393 return true; 394 } else if (str.substr(0, 2) === '::') { 395 blocks.shift(); 396 blocks.shift(); 397 foundOmissionBlock = true; 398 } else if (str.substr(str.length - 2) === '::') { 399 blocks.pop(); 400 blocks.pop(); 401 foundOmissionBlock = true; 402 } 403 404 for (var i = 0; i < blocks.length; ++i) { 405 // test for a :: which can not be at the string start/end 406 // since those cases have been handled above 407 if (blocks[i] === '' && i > 0 && i < blocks.length - 1) { 408 if (foundOmissionBlock) { 409 return false; // multiple :: in address 410 } 411 foundOmissionBlock = true; 412 } else if (foundIPv4TransitionBlock && i === blocks.length - 1) { 413 // it has been checked before that the last 414 // block is a valid IPv4 address 415 } else if (!ipv6Block.test(blocks[i])) { 416 return false; 417 } 418 } 419 if (foundOmissionBlock) { 420 return blocks.length >= 1; 421 } 422 return blocks.length === expectedNumberOfBlocks; 423 } 424 return false; 425 } 426 427 var default_url_options = { 428 protocols: ['http', 'https', 'ftp'], 429 require_tld: true, 430 require_protocol: false, 431 require_host: true, 432 require_valid_protocol: true, 433 allow_underscores: false, 434 allow_trailing_dot: false, 435 allow_protocol_relative_urls: false 436 }; 437 438 var wrapped_ipv6 = /^\[([^\]]+)\](?::([0-9]+))?$/; 439 440 function isRegExp(obj) { 441 return Object.prototype.toString.call(obj) === '[object RegExp]'; 442 } 443 444 function checkHost(host, matches) { 445 for (var i = 0; i < matches.length; i++) { 446 var match = matches[i]; 447 if (host === match || isRegExp(match) && match.test(host)) { 448 return true; 449 } 450 } 451 return false; 452 } 453 454 function isURL(url, options) { 455 assertString(url); 456 if (!url || url.length >= 2083 || /[\s<>]/.test(url)) { 457 return false; 458 } 459 if (url.indexOf('mailto:') === 0) { 460 return false; 461 } 462 options = merge(options, default_url_options); 463 var protocol = void 0, 464 auth = void 0, 465 host = void 0, 466 hostname = void 0, 467 port = void 0, 468 port_str = void 0, 469 split = void 0, 470 ipv6 = void 0; 471 472 split = url.split('#'); 473 url = split.shift(); 474 475 split = url.split('?'); 476 url = split.shift(); 477 478 split = url.split('://'); 479 if (split.length > 1) { 480 protocol = split.shift(); 481 if (options.require_valid_protocol && options.protocols.indexOf(protocol) === -1) { 482 return false; 483 } 484 } else if (options.require_protocol) { 485 return false; 486 } else if (options.allow_protocol_relative_urls && url.substr(0, 2) === '//') { 487 split[0] = url.substr(2); 488 } 489 url = split.join('://'); 490 491 split = url.split('/'); 492 url = split.shift(); 493 494 if (url === '' && !options.require_host) { 495 return true; 496 } 497 498 split = url.split('@'); 499 if (split.length > 1) { 500 auth = split.shift(); 501 if (auth.indexOf(':') >= 0 && auth.split(':').length > 2) { 502 return false; 503 } 504 } 505 hostname = split.join('@'); 506 507 port_str = ipv6 = null; 508 var ipv6_match = hostname.match(wrapped_ipv6); 509 if (ipv6_match) { 510 host = ''; 511 ipv6 = ipv6_match[1]; 512 port_str = ipv6_match[2] || null; 513 } else { 514 split = hostname.split(':'); 515 host = split.shift(); 516 if (split.length) { 517 port_str = split.join(':'); 518 } 519 } 520 521 if (port_str !== null) { 522 port = parseInt(port_str, 10); 523 if (!/^[0-9]+$/.test(port_str) || port <= 0 || port > 65535) { 524 return false; 525 } 526 } 527 528 if (!isIP(host) && !isFDQN(host, options) && (!ipv6 || !isIP(ipv6, 6)) && host !== 'localhost') { 529 return false; 530 } 531 532 host = host || ipv6; 533 534 if (options.host_whitelist && !checkHost(host, options.host_whitelist)) { 535 return false; 536 } 537 if (options.host_blacklist && checkHost(host, options.host_blacklist)) { 538 return false; 539 } 540 541 return true; 542 } 543 544 var macAddress = /^([0-9a-fA-F][0-9a-fA-F]:){5}([0-9a-fA-F][0-9a-fA-F])$/; 545 546 function isMACAddress(str) { 547 assertString(str); 548 return macAddress.test(str); 549 } 550 551 function isBoolean(str) { 552 assertString(str); 553 return ['true', 'false', '1', '0'].indexOf(str) >= 0; 554 } 555 556 var alpha = { 557 'en-US': /^[A-Z]+$/i, 558 'cs-CZ': /^[A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i, 559 'da-DK': /^[A-ZÆØÅ]+$/i, 560 'de-DE': /^[A-ZÄÖÜß]+$/i, 561 'es-ES': /^[A-ZÁÉÍÑÓÚÜ]+$/i, 562 'fr-FR': /^[A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i, 563 'nl-NL': /^[A-ZÉËÏÓÖÜ]+$/i, 564 'hu-HU': /^[A-ZÁÉÍÓÖŐÚÜŰ]+$/i, 565 'pl-PL': /^[A-ZĄĆĘŚŁŃÓŻŹ]+$/i, 566 'pt-PT': /^[A-ZÃÁÀÂÇÉÊÍÕÓÔÚÜ]+$/i, 567 'ru-RU': /^[А-ЯЁ]+$/i, 568 'sr-RS@latin': /^[A-ZČĆŽŠĐ]+$/i, 569 'sr-RS': /^[А-ЯЂЈЉЊЋЏ]+$/i, 570 'tr-TR': /^[A-ZÇĞİıÖŞÜ]+$/i, 571 'uk-UA': /^[А-ЩЬЮЯЄIЇҐ]+$/i, 572 ar: /^[ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/ 573 }; 574 575 var alphanumeric = { 576 'en-US': /^[0-9A-Z]+$/i, 577 'cs-CZ': /^[0-9A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i, 578 'da-DK': /^[0-9A-ZÆØÅ]$/i, 579 'de-DE': /^[0-9A-ZÄÖÜß]+$/i, 580 'es-ES': /^[0-9A-ZÁÉÍÑÓÚÜ]+$/i, 581 'fr-FR': /^[0-9A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i, 582 'hu-HU': /^[0-9A-ZÁÉÍÓÖŐÚÜŰ]+$/i, 583 'nl-NL': /^[0-9A-ZÉËÏÓÖÜ]+$/i, 584 'pl-PL': /^[0-9A-ZĄĆĘŚŁŃÓŻŹ]+$/i, 585 'pt-PT': /^[0-9A-ZÃÁÀÂÇÉÊÍÕÓÔÚÜ]+$/i, 586 'ru-RU': /^[0-9А-ЯЁ]+$/i, 587 'sr-RS@latin': /^[0-9A-ZČĆŽŠĐ]+$/i, 588 'sr-RS': /^[0-9А-ЯЂЈЉЊЋЏ]+$/i, 589 'tr-TR': /^[0-9A-ZÇĞİıÖŞÜ]+$/i, 590 'uk-UA': /^[0-9А-ЩЬЮЯЄIЇҐ]+$/i, 591 ar: /^[٠١٢٣٤٥٦٧٨٩0-9ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/ 592 }; 593 594 var englishLocales = ['AU', 'GB', 'HK', 'IN', 'NZ', 'ZA', 'ZM']; 595 596 for (var locale, i = 0; i < englishLocales.length; i++) { 597 locale = 'en-' + englishLocales[i]; 598 alpha[locale] = alpha['en-US']; 599 alphanumeric[locale] = alphanumeric['en-US']; 600 } 601 602 alpha['pt-BR'] = alpha['pt-PT']; 603 alphanumeric['pt-BR'] = alphanumeric['pt-PT']; 604 605 // Source: http://www.localeplanet.com/java/ 606 var arabicLocales = ['AE', 'BH', 'DZ', 'EG', 'IQ', 'JO', 'KW', 'LB', 'LY', 'MA', 'QM', 'QA', 'SA', 'SD', 'SY', 'TN', 'YE']; 607 608 for (var _locale, _i = 0; _i < arabicLocales.length; _i++) { 609 _locale = 'ar-' + arabicLocales[_i]; 610 alpha[_locale] = alpha.ar; 611 alphanumeric[_locale] = alphanumeric.ar; 612 } 613 614 function isAlpha(str) { 615 var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'en-US'; 616 617 assertString(str); 618 if (locale in alpha) { 619 return alpha[locale].test(str); 620 } 621 throw new Error('Invalid locale \'' + locale + '\''); 622 } 623 624 function isAlphanumeric(str) { 625 var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'en-US'; 626 627 assertString(str); 628 if (locale in alphanumeric) { 629 return alphanumeric[locale].test(str); 630 } 631 throw new Error('Invalid locale \'' + locale + '\''); 632 } 633 634 var numeric = /^[-+]?[0-9]+$/; 635 636 function isNumeric(str) { 637 assertString(str); 638 return numeric.test(str); 639 } 640 641 function isLowercase(str) { 642 assertString(str); 643 return str === str.toLowerCase(); 644 } 645 646 function isUppercase(str) { 647 assertString(str); 648 return str === str.toUpperCase(); 649 } 650 651 /* eslint-disable no-control-regex */ 652 var ascii = /^[\x00-\x7F]+$/; 653 /* eslint-enable no-control-regex */ 654 655 function isAscii(str) { 656 assertString(str); 657 return ascii.test(str); 658 } 659 660 var fullWidth = /[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]/; 661 662 function isFullWidth(str) { 663 assertString(str); 664 return fullWidth.test(str); 665 } 666 667 var halfWidth = /[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]/; 668 669 function isHalfWidth(str) { 670 assertString(str); 671 return halfWidth.test(str); 672 } 673 674 function isVariableWidth(str) { 675 assertString(str); 676 return fullWidth.test(str) && halfWidth.test(str); 677 } 678 679 /* eslint-disable no-control-regex */ 680 var multibyte = /[^\x00-\x7F]/; 681 /* eslint-enable no-control-regex */ 682 683 function isMultibyte(str) { 684 assertString(str); 685 return multibyte.test(str); 686 } 687 688 var surrogatePair = /[\uD800-\uDBFF][\uDC00-\uDFFF]/; 689 690 function isSurrogatePair(str) { 691 assertString(str); 692 return surrogatePair.test(str); 693 } 694 695 var int = /^(?:[-+]?(?:0|[1-9][0-9]*))$/; 696 var intLeadingZeroes = /^[-+]?[0-9]+$/; 697 698 function isInt(str, options) { 699 assertString(str); 700 options = options || {}; 701 702 // Get the regex to use for testing, based on whether 703 // leading zeroes are allowed or not. 704 var regex = options.hasOwnProperty('allow_leading_zeroes') && !options.allow_leading_zeroes ? int : intLeadingZeroes; 705 706 // Check min/max/lt/gt 707 var minCheckPassed = !options.hasOwnProperty('min') || str >= options.min; 708 var maxCheckPassed = !options.hasOwnProperty('max') || str <= options.max; 709 var ltCheckPassed = !options.hasOwnProperty('lt') || str < options.lt; 710 var gtCheckPassed = !options.hasOwnProperty('gt') || str > options.gt; 711 712 return regex.test(str) && minCheckPassed && maxCheckPassed && ltCheckPassed && gtCheckPassed; 713 } 714 715 var float = /^(?:[-+])?(?:[0-9]+)?(?:\.[0-9]*)?(?:[eE][\+\-]?(?:[0-9]+))?$/; 716 717 function isFloat(str, options) { 718 assertString(str); 719 options = options || {}; 720 if (str === '' || str === '.') { 721 return false; 722 } 723 return float.test(str) && (!options.hasOwnProperty('min') || str >= options.min) && (!options.hasOwnProperty('max') || str <= options.max) && (!options.hasOwnProperty('lt') || str < options.lt) && (!options.hasOwnProperty('gt') || str > options.gt); 724 } 725 726 var decimal = /^[-+]?([0-9]+|\.[0-9]+|[0-9]+\.[0-9]+)$/; 727 728 function isDecimal(str) { 729 assertString(str); 730 return str !== '' && decimal.test(str); 731 } 732 733 var hexadecimal = /^[0-9A-F]+$/i; 734 735 function isHexadecimal(str) { 736 assertString(str); 737 return hexadecimal.test(str); 738 } 739 740 function isDivisibleBy(str, num) { 741 assertString(str); 742 return toFloat(str) % parseInt(num, 10) === 0; 743 } 744 745 var hexcolor = /^#?([0-9A-F]{3}|[0-9A-F]{6})$/i; 746 747 function isHexColor(str) { 748 assertString(str); 749 return hexcolor.test(str); 750 } 751 752 var md5 = /^[a-f0-9]{32}$/; 753 754 function isMD5(str) { 755 assertString(str); 756 return md5.test(str); 757 } 758 759 function isJSON(str) { 760 assertString(str); 761 try { 762 var obj = JSON.parse(str); 763 return !!obj && (typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object'; 764 } catch (e) {/* ignore */} 765 return false; 766 } 767 768 function isEmpty(str) { 769 assertString(str); 770 return str.length === 0; 771 } 772 773 /* eslint-disable prefer-rest-params */ 774 function isLength(str, options) { 775 assertString(str); 776 var min = void 0; 777 var max = void 0; 778 if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') { 779 min = options.min || 0; 780 max = options.max; 781 } else { 782 // backwards compatibility: isLength(str, min [, max]) 783 min = arguments[1]; 784 max = arguments[2]; 785 } 786 var surrogatePairs = str.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g) || []; 787 var len = str.length - surrogatePairs.length; 788 return len >= min && (typeof max === 'undefined' || len <= max); 789 } 790 791 var uuid = { 792 3: /^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i, 793 4: /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i, 794 5: /^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i, 795 all: /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i 796 }; 797 798 function isUUID(str) { 799 var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'all'; 800 801 assertString(str); 802 var pattern = uuid[version]; 803 return pattern && pattern.test(str); 804 } 805 806 function isMongoId(str) { 807 assertString(str); 808 return isHexadecimal(str) && str.length === 24; 809 } 810 811 function isAfter(str) { 812 var date = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : String(new Date()); 813 814 assertString(str); 815 var comparison = toDate(date); 816 var original = toDate(str); 817 return !!(original && comparison && original > comparison); 818 } 819 820 function isBefore(str) { 821 var date = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : String(new Date()); 822 823 assertString(str); 824 var comparison = toDate(date); 825 var original = toDate(str); 826 return !!(original && comparison && original < comparison); 827 } 828 829 function isIn(str, options) { 830 assertString(str); 831 var i = void 0; 832 if (Object.prototype.toString.call(options) === '[object Array]') { 833 var array = []; 834 for (i in options) { 835 if ({}.hasOwnProperty.call(options, i)) { 836 array[i] = toString(options[i]); 837 } 838 } 839 return array.indexOf(str) >= 0; 840 } else if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') { 841 return options.hasOwnProperty(str); 842 } else if (options && typeof options.indexOf === 'function') { 843 return options.indexOf(str) >= 0; 844 } 845 return false; 846 } 847 848 /* eslint-disable max-len */ 849 var creditCard = /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})|62[0-9]{14}$/; 850 /* eslint-enable max-len */ 851 852 function isCreditCard(str) { 853 assertString(str); 854 var sanitized = str.replace(/[^0-9]+/g, ''); 855 if (!creditCard.test(sanitized)) { 856 return false; 857 } 858 var sum = 0; 859 var digit = void 0; 860 var tmpNum = void 0; 861 var shouldDouble = void 0; 862 for (var i = sanitized.length - 1; i >= 0; i--) { 863 digit = sanitized.substring(i, i + 1); 864 tmpNum = parseInt(digit, 10); 865 if (shouldDouble) { 866 tmpNum *= 2; 867 if (tmpNum >= 10) { 868 sum += tmpNum % 10 + 1; 869 } else { 870 sum += tmpNum; 871 } 872 } else { 873 sum += tmpNum; 874 } 875 shouldDouble = !shouldDouble; 876 } 877 return !!(sum % 10 === 0 ? sanitized : false); 878 } 879 880 var isin = /^[A-Z]{2}[0-9A-Z]{9}[0-9]$/; 881 882 function isISIN(str) { 883 assertString(str); 884 if (!isin.test(str)) { 885 return false; 886 } 887 888 var checksumStr = str.replace(/[A-Z]/g, function (character) { 889 return parseInt(character, 36); 890 }); 891 892 var sum = 0; 893 var digit = void 0; 894 var tmpNum = void 0; 895 var shouldDouble = true; 896 for (var i = checksumStr.length - 2; i >= 0; i--) { 897 digit = checksumStr.substring(i, i + 1); 898 tmpNum = parseInt(digit, 10); 899 if (shouldDouble) { 900 tmpNum *= 2; 901 if (tmpNum >= 10) { 902 sum += tmpNum + 1; 903 } else { 904 sum += tmpNum; 905 } 906 } else { 907 sum += tmpNum; 908 } 909 shouldDouble = !shouldDouble; 910 } 911 912 return parseInt(str.substr(str.length - 1), 10) === (10000 - sum) % 10; 913 } 914 915 var isbn10Maybe = /^(?:[0-9]{9}X|[0-9]{10})$/; 916 var isbn13Maybe = /^(?:[0-9]{13})$/; 917 var factor = [1, 3]; 918 919 function isISBN(str) { 920 var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; 921 922 assertString(str); 923 version = String(version); 924 if (!version) { 925 return isISBN(str, 10) || isISBN(str, 13); 926 } 927 var sanitized = str.replace(/[\s-]+/g, ''); 928 var checksum = 0; 929 var i = void 0; 930 if (version === '10') { 931 if (!isbn10Maybe.test(sanitized)) { 932 return false; 933 } 934 for (i = 0; i < 9; i++) { 935 checksum += (i + 1) * sanitized.charAt(i); 936 } 937 if (sanitized.charAt(9) === 'X') { 938 checksum += 10 * 10; 939 } else { 940 checksum += 10 * sanitized.charAt(9); 941 } 942 if (checksum % 11 === 0) { 943 return !!sanitized; 944 } 945 } else if (version === '13') { 946 if (!isbn13Maybe.test(sanitized)) { 947 return false; 948 } 949 for (i = 0; i < 12; i++) { 950 checksum += factor[i % 2] * sanitized.charAt(i); 951 } 952 if (sanitized.charAt(12) - (10 - checksum % 10) % 10 === 0) { 953 return !!sanitized; 954 } 955 } 956 return false; 957 } 958 959 var issn = '^\\d{4}-?\\d{3}[\\dX]$'; 960 961 function isISSN(str) { 962 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 963 964 assertString(str); 965 var testIssn = issn; 966 testIssn = options.require_hyphen ? testIssn.replace('?', '') : testIssn; 967 testIssn = options.case_sensitive ? new RegExp(testIssn) : new RegExp(testIssn, 'i'); 968 if (!testIssn.test(str)) { 969 return false; 970 } 971 var issnDigits = str.replace('-', ''); 972 var position = 8; 973 var checksum = 0; 974 var _iteratorNormalCompletion = true; 975 var _didIteratorError = false; 976 var _iteratorError = undefined; 977 978 try { 979 for (var _iterator = issnDigits[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 980 var digit = _step.value; 981 982 var digitValue = digit.toUpperCase() === 'X' ? 10 : +digit; 983 checksum += digitValue * position; 984 --position; 985 } 986 } catch (err) { 987 _didIteratorError = true; 988 _iteratorError = err; 989 } finally { 990 try { 991 if (!_iteratorNormalCompletion && _iterator.return) { 992 _iterator.return(); 993 } 994 } finally { 995 if (_didIteratorError) { 996 throw _iteratorError; 997 } 998 } 999 } 1000 1001 return checksum % 11 === 0; 1002 } 1003 1004 /* eslint-disable max-len */ 1005 var phones = { 1006 'ar-DZ': /^(\+?213|0)(5|6|7)\d{8}$/, 1007 'ar-SY': /^(!?(\+?963)|0)?9\d{8}$/, 1008 'ar-SA': /^(!?(\+?966)|0)?5\d{8}$/, 1009 'en-US': /^(\+?1)?[2-9]\d{2}[2-9](?!11)\d{6}$/, 1010 'cs-CZ': /^(\+?420)? ?[1-9][0-9]{2} ?[0-9]{3} ?[0-9]{3}$/, 1011 'de-DE': /^(\+?49[ \.\-])?([\(]{1}[0-9]{1,6}[\)])?([0-9 \.\-\/]{3,20})((x|ext|extension)[ ]?[0-9]{1,4})?$/, 1012 'da-DK': /^(\+?45)?(\d{8})$/, 1013 'el-GR': /^(\+?30)?(69\d{8})$/, 1014 'en-AU': /^(\+?61|0)4\d{8}$/, 1015 'en-GB': /^(\+?44|0)7\d{9}$/, 1016 // According to http://www.ofca.gov.hk/filemanager/ofca/en/content_311/no_plan.pdf 1017 'en-HK': /^(\+?852-?)?((4(04[01]|06\d|09[3-9]|20\d|2[2-9]\d|3[3-9]\d|[467]\d{2}|5[1-9]\d|81\d|82[1-9]|8[69]\d|92[3-9]|95[2-9]|98\d)|5([1-79]\d{2})|6(0[1-9]\d|[1-9]\d{2})|7(0[1-9]\d|10[4-79]|11[458]|1[24578]\d|13[24-9]|16[0-8]|19[24579]|21[02-79]|2[456]\d|27[13-6]|3[456]\d|37[4578]|39[0146])|8(1[58]\d|2[45]\d|267|27[5-9]|2[89]\d|3[15-9]\d|32[5-8]|[46-9]\d{2}|5[013-9]\d)|9(0[1-9]\d|1[02-9]\d|[2-8]\d{2}))-?\d{4}|7130-?[0124-8]\d{3}|8167-?2\d{3})$/, 1018 'en-IN': /^(\+?91|0)?[789]\d{9}$/, 1019 'en-NG': /^(\+?234|0)?[789]\d{9}$/, 1020 'en-NZ': /^(\+?64|0)2\d{7,9}$/, 1021 'en-ZA': /^(\+?27|0)\d{9}$/, 1022 'en-ZM': /^(\+?26)?09[567]\d{7}$/, 1023 'es-ES': /^(\+?34)?(6\d{1}|7[1234])\d{7}$/, 1024 'fi-FI': /^(\+?358|0)\s?(4(0|1|2|4|5)?|50)\s?(\d\s?){4,8}\d$/, 1025 'fr-FR': /^(\+?33|0)[67]\d{8}$/, 1026 'he-IL': /^(\+972|0)([23489]|5[0248]|77)[1-9]\d{6}/, 1027 'hu-HU': /^(\+?36)(20|30|70)\d{7}$/, 1028 'lt-LT': /^(\+370|8)\d{8}$/, 1029 'id-ID': /^(\+?62|0[1-9])[\s|\d]+$/, 1030 'it-IT': /^(\+?39)?\s?3\d{2} ?\d{6,7}$/, 1031 'ko-KR': /^((\+?82)[ \-]?)?0?1([0|1|6|7|8|9]{1})[ \-]?\d{3,4}[ \-]?\d{4}$/, 1032 'ja-JP': /^(\+?81|0)\d{1,4}[ \-]?\d{1,4}[ \-]?\d{4}$/, 1033 'ms-MY': /^(\+?6?01){1}(([145]{1}(\-|\s)?\d{7,8})|([236789]{1}(\s|\-)?\d{7}))$/, 1034 'nb-NO': /^(\+?47)?[49]\d{7}$/, 1035 'nl-BE': /^(\+?32|0)4?\d{8}$/, 1036 'nn-NO': /^(\+?47)?[49]\d{7}$/, 1037 'pl-PL': /^(\+?48)? ?[5-8]\d ?\d{3} ?\d{2} ?\d{2}$/, 1038 'pt-BR': /^(\+?55|0)\-?[1-9]{2}\-?[2-9]{1}\d{3,4}\-?\d{4}$/, 1039 'pt-PT': /^(\+?351)?9[1236]\d{7}$/, 1040 'ro-RO': /^(\+?4?0)\s?7\d{2}(\/|\s|\.|\-)?\d{3}(\s|\.|\-)?\d{3}$/, 1041 'en-PK': /^((\+92)|(0092))-{0,1}\d{3}-{0,1}\d{7}$|^\d{11}$|^\d{4}-\d{7}$/, 1042 'ru-RU': /^(\+?7|8)?9\d{9}$/, 1043 'sr-RS': /^(\+3816|06)[- \d]{5,9}$/, 1044 'tr-TR': /^(\+?90|0)?5\d{9}$/, 1045 'vi-VN': /^(\+?84|0)?((1(2([0-9])|6([2-9])|88|99))|(9((?!5)[0-9])))([0-9]{7})$/, 1046 'zh-CN': /^(\+?0?86\-?)?1[345789]\d{9}$/, 1047 'zh-TW': /^(\+?886\-?|0)?9\d{8}$/ 1048 }; 1049 /* eslint-enable max-len */ 1050 1051 // aliases 1052 phones['en-CA'] = phones['en-US']; 1053 phones['fr-BE'] = phones['nl-BE']; 1054 phones['zh-HK'] = phones['en-HK']; 1055 1056 function isMobilePhone(str, locale) { 1057 assertString(str); 1058 if (locale in phones) { 1059 return phones[locale].test(str); 1060 } else if (locale === 'any') { 1061 return !!Object.values(phones).find(phone => phone.test(str)); 1062 } 1063 return false; 1064 } 1065 1066 function currencyRegex(options) { 1067 var symbol = '(\\' + options.symbol.replace(/\./g, '\\.') + ')' + (options.require_symbol ? '' : '?'), 1068 negative = '-?', 1069 whole_dollar_amount_without_sep = '[1-9]\\d*', 1070 whole_dollar_amount_with_sep = '[1-9]\\d{0,2}(\\' + options.thousands_separator + '\\d{3})*', 1071 valid_whole_dollar_amounts = ['0', whole_dollar_amount_without_sep, whole_dollar_amount_with_sep], 1072 whole_dollar_amount = '(' + valid_whole_dollar_amounts.join('|') + ')?', 1073 decimal_amount = '(\\' + options.decimal_separator + '\\d{2})?'; 1074 var pattern = whole_dollar_amount + decimal_amount; 1075 1076 // default is negative sign before symbol, but there are two other options (besides parens) 1077 if (options.allow_negatives && !options.parens_for_negatives) { 1078 if (options.negative_sign_after_digits) { 1079 pattern += negative; 1080 } else if (options.negative_sign_before_digits) { 1081 pattern = negative + pattern; 1082 } 1083 } 1084 1085 // South African Rand, for example, uses R 123 (space) and R-123 (no space) 1086 if (options.allow_negative_sign_placeholder) { 1087 pattern = '( (?!\\-))?' + pattern; 1088 } else if (options.allow_space_after_symbol) { 1089 pattern = ' ?' + pattern; 1090 } else if (options.allow_space_after_digits) { 1091 pattern += '( (?!$))?'; 1092 } 1093 1094 if (options.symbol_after_digits) { 1095 pattern += symbol; 1096 } else { 1097 pattern = symbol + pattern; 1098 } 1099 1100 if (options.allow_negatives) { 1101 if (options.parens_for_negatives) { 1102 pattern = '(\\(' + pattern + '\\)|' + pattern + ')'; 1103 } else if (!(options.negative_sign_before_digits || options.negative_sign_after_digits)) { 1104 pattern = negative + pattern; 1105 } 1106 } 1107 1108 /* eslint-disable prefer-template */ 1109 return new RegExp('^' + 1110 // ensure there's a dollar and/or decimal amount, and that 1111 // it doesn't start with a space or a negative sign followed by a space 1112 '(?!-? )(?=.*\\d)' + pattern + '$'); 1113 /* eslint-enable prefer-template */ 1114 } 1115 1116 var default_currency_options = { 1117 symbol: '$', 1118 require_symbol: false, 1119 allow_space_after_symbol: false, 1120 symbol_after_digits: false, 1121 allow_negatives: true, 1122 parens_for_negatives: false, 1123 negative_sign_before_digits: false, 1124 negative_sign_after_digits: false, 1125 allow_negative_sign_placeholder: false, 1126 thousands_separator: ',', 1127 decimal_separator: '.', 1128 allow_space_after_digits: false 1129 }; 1130 1131 function isCurrency(str, options) { 1132 assertString(str); 1133 options = merge(options, default_currency_options); 1134 return currencyRegex(options).test(str); 1135 } 1136 1137 /* eslint-disable max-len */ 1138 // from http://goo.gl/0ejHHW 1139 var iso8601 = /^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/; 1140 /* eslint-enable max-len */ 1141 1142 function isISO8601 (str) { 1143 assertString(str); 1144 return iso8601.test(str); 1145 } 1146 1147 function isBase64(str) { 1148 assertString(str); 1149 // Value length must be divisible by 4. 1150 var len = str.length; 1151 if (!len || len % 4 !== 0) { 1152 return false; 1153 } 1154 1155 try { 1156 if (atob(str)) { 1157 return true; 1158 } 1159 } catch (e) { 1160 return false; 1161 } 1162 } 1163 1164 var dataURI = /^\s*data:([a-z]+\/[a-z0-9\-\+]+(;[a-z\-]+=[a-z0-9\-]+)?)?(;base64)?,[a-z0-9!\$&',\(\)\*\+,;=\-\._~:@\/\?%\s]*\s*$/i; // eslint-disable-line max-len 1165 1166 function isDataURI(str) { 1167 assertString(str); 1168 return dataURI.test(str); 1169 } 1170 1171 function ltrim(str, chars) { 1172 assertString(str); 1173 var pattern = chars ? new RegExp('^[' + chars + ']+', 'g') : /^\s+/g; 1174 return str.replace(pattern, ''); 1175 } 1176 1177 function rtrim(str, chars) { 1178 assertString(str); 1179 var pattern = chars ? new RegExp('[' + chars + ']') : /\s/; 1180 1181 var idx = str.length - 1; 1182 while (idx >= 0 && pattern.test(str[idx])) { 1183 idx--; 1184 } 1185 1186 return idx < str.length ? str.substr(0, idx + 1) : str; 1187 } 1188 1189 function trim(str, chars) { 1190 return rtrim(ltrim(str, chars), chars); 1191 } 1192 1193 function escape(str) { 1194 assertString(str); 1195 return str.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>').replace(/\//g, '/').replace(/\\/g, '\').replace(/`/g, '`'); 1196 } 1197 1198 function unescape(str) { 1199 assertString(str); 1200 return str.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, "'").replace(/</g, '<').replace(/>/g, '>').replace(///g, '/').replace(/`/g, '`'); 1201 } 1202 1203 function blacklist(str, chars) { 1204 assertString(str); 1205 return str.replace(new RegExp('[' + chars + ']+', 'g'), ''); 1206 } 1207 1208 function stripLow(str, keep_new_lines) { 1209 assertString(str); 1210 var chars = keep_new_lines ? '\\x00-\\x09\\x0B\\x0C\\x0E-\\x1F\\x7F' : '\\x00-\\x1F\\x7F'; 1211 return blacklist(str, chars); 1212 } 1213 1214 function whitelist(str, chars) { 1215 assertString(str); 1216 return str.replace(new RegExp('[^' + chars + ']+', 'g'), ''); 1217 } 1218 1219 function isWhitelisted(str, chars) { 1220 assertString(str); 1221 for (var i = str.length - 1; i >= 0; i--) { 1222 if (chars.indexOf(str[i]) === -1) { 1223 return false; 1224 } 1225 } 1226 return true; 1227 } 1228 1229 var default_normalize_email_options = { 1230 // The following options apply to all email addresses 1231 // Lowercases the local part of the email address. 1232 // Please note this may violate RFC 5321 as per http://stackoverflow.com/a/9808332/192024). 1233 // The domain is always lowercased, as per RFC 1035 1234 all_lowercase: true, 1235 1236 // The following conversions are specific to GMail 1237 // Lowercases the local part of the GMail address (known to be case-insensitive) 1238 gmail_lowercase: true, 1239 // Removes dots from the local part of the email address, as that's ignored by GMail 1240 gmail_remove_dots: true, 1241 // Removes the subaddress (e.g. "+foo") from the email address 1242 gmail_remove_subaddress: true, 1243 // Conversts the googlemail.com domain to gmail.com 1244 gmail_convert_googlemaildotcom: true, 1245 1246 // The following conversions are specific to Outlook.com / Windows Live / Hotmail 1247 // Lowercases the local part of the Outlook.com address (known to be case-insensitive) 1248 outlookdotcom_lowercase: true, 1249 // Removes the subaddress (e.g. "+foo") from the email address 1250 outlookdotcom_remove_subaddress: true, 1251 1252 // The following conversions are specific to Yahoo 1253 // Lowercases the local part of the Yahoo address (known to be case-insensitive) 1254 yahoo_lowercase: true, 1255 // Removes the subaddress (e.g. "-foo") from the email address 1256 yahoo_remove_subaddress: true, 1257 1258 // The following conversions are specific to iCloud 1259 // Lowercases the local part of the iCloud address (known to be case-insensitive) 1260 icloud_lowercase: true, 1261 // Removes the subaddress (e.g. "+foo") from the email address 1262 icloud_remove_subaddress: true 1263 }; 1264 1265 // List of domains used by iCloud 1266 var icloud_domains = ['icloud.com', 'me.com']; 1267 1268 // List of domains used by Outlook.com and its predecessors 1269 // This list is likely incomplete. 1270 // Partial reference: 1271 // https://blogs.office.com/2013/04/17/outlook-com-gets-two-step-verification-sign-in-by-alias-and-new-international-domains/ 1272 var outlookdotcom_domains = ['hotmail.at', 'hotmail.be', 'hotmail.ca', 'hotmail.cl', 'hotmail.co.il', 'hotmail.co.nz', 'hotmail.co.th', 'hotmail.co.uk', 'hotmail.com', 'hotmail.com.ar', 'hotmail.com.au', 'hotmail.com.br', 'hotmail.com.gr', 'hotmail.com.mx', 'hotmail.com.pe', 'hotmail.com.tr', 'hotmail.com.vn', 'hotmail.cz', 'hotmail.de', 'hotmail.dk', 'hotmail.es', 'hotmail.fr', 'hotmail.hu', 'hotmail.id', 'hotmail.ie', 'hotmail.in', 'hotmail.it', 'hotmail.jp', 'hotmail.kr', 'hotmail.lv', 'hotmail.my', 'hotmail.ph', 'hotmail.pt', 'hotmail.sa', 'hotmail.sg', 'hotmail.sk', 'live.be', 'live.co.uk', 'live.com', 'live.com.ar', 'live.com.mx', 'live.de', 'live.es', 'live.eu', 'live.fr', 'live.it', 'live.nl', 'msn.com', 'outlook.at', 'outlook.be', 'outlook.cl', 'outlook.co.il', 'outlook.co.nz', 'outlook.co.th', 'outlook.com', 'outlook.com.ar', 'outlook.com.au', 'outlook.com.br', 'outlook.com.gr', 'outlook.com.pe', 'outlook.com.tr', 'outlook.com.vn', 'outlook.cz', 'outlook.de', 'outlook.dk', 'outlook.es', 'outlook.fr', 'outlook.hu', 'outlook.id', 'outlook.ie', 'outlook.in', 'outlook.it', 'outlook.jp', 'outlook.kr', 'outlook.lv', 'outlook.my', 'outlook.ph', 'outlook.pt', 'outlook.sa', 'outlook.sg', 'outlook.sk', 'passport.com']; 1273 1274 // List of domains used by Yahoo Mail 1275 // This list is likely incomplete 1276 var yahoo_domains = ['rocketmail.com', 'yahoo.ca', 'yahoo.co.uk', 'yahoo.com', 'yahoo.de', 'yahoo.fr', 'yahoo.in', 'yahoo.it', 'ymail.com']; 1277 1278 function normalizeEmail(email, options) { 1279 options = merge(options, default_normalize_email_options); 1280 1281 if (!isEmail(email)) { 1282 return false; 1283 } 1284 1285 var raw_parts = email.split('@'); 1286 var domain = raw_parts.pop(); 1287 var user = raw_parts.join('@'); 1288 var parts = [user, domain]; 1289 1290 // The domain is always lowercased, as it's case-insensitive per RFC 1035 1291 parts[1] = parts[1].toLowerCase(); 1292 1293 if (parts[1] === 'gmail.com' || parts[1] === 'googlemail.com') { 1294 // Address is GMail 1295 if (options.gmail_remove_subaddress) { 1296 parts[0] = parts[0].split('+')[0]; 1297 } 1298 if (options.gmail_remove_dots) { 1299 parts[0] = parts[0].replace(/\./g, ''); 1300 } 1301 if (!parts[0].length) { 1302 return false; 1303 } 1304 if (options.all_lowercase || options.gmail_lowercase) { 1305 parts[0] = parts[0].toLowerCase(); 1306 } 1307 parts[1] = options.gmail_convert_googlemaildotcom ? 'gmail.com' : parts[1]; 1308 } else if (~icloud_domains.indexOf(parts[1])) { 1309 // Address is iCloud 1310 if (options.icloud_remove_subaddress) { 1311 parts[0] = parts[0].split('+')[0]; 1312 } 1313 if (!parts[0].length) { 1314 return false; 1315 } 1316 if (options.all_lowercase || options.icloud_lowercase) { 1317 parts[0] = parts[0].toLowerCase(); 1318 } 1319 } else if (~outlookdotcom_domains.indexOf(parts[1])) { 1320 // Address is Outlook.com 1321 if (options.outlookdotcom_remove_subaddress) { 1322 parts[0] = parts[0].split('+')[0]; 1323 } 1324 if (!parts[0].length) { 1325 return false; 1326 } 1327 if (options.all_lowercase || options.outlookdotcom_lowercase) { 1328 parts[0] = parts[0].toLowerCase(); 1329 } 1330 } else if (~yahoo_domains.indexOf(parts[1])) { 1331 // Address is Yahoo 1332 if (options.yahoo_remove_subaddress) { 1333 var components = parts[0].split('-'); 1334 parts[0] = components.length > 1 ? components.slice(0, -1).join('-') : components[0]; 1335 } 1336 if (!parts[0].length) { 1337 return false; 1338 } 1339 if (options.all_lowercase || options.yahoo_lowercase) { 1340 parts[0] = parts[0].toLowerCase(); 1341 } 1342 } else if (options.all_lowercase) { 1343 // Any other address 1344 parts[0] = parts[0].toLowerCase(); 1345 } 1346 return parts.join('@'); 1347 } 1348 1349 // see http://isrc.ifpi.org/en/isrc-standard/code-syntax 1350 var isrc = /^[A-Z]{2}[0-9A-Z]{3}\d{2}\d{5}$/; 1351 1352 function isISRC(str) { 1353 assertString(str); 1354 return isrc.test(str); 1355 } 1356 1357 var cultureCodes = new Set(["ar", "bg", "ca", "zh-Hans", "cs", "da", "de", 1358 "el", "en", "es", "fi", "fr", "he", "hu", "is", "it", "ja", "ko", "nl", "no", 1359 "pl", "pt", "rm", "ro", "ru", "hr", "sk", "sq", "sv", "th", "tr", "ur", "id", 1360 "uk", "be", "sl", "et", "lv", "lt", "tg", "fa", "vi", "hy", "az", "eu", "hsb", 1361 "mk", "tn", "xh", "zu", "af", "ka", "fo", "hi", "mt", "se", "ga", "ms", "kk", 1362 "ky", "sw", "tk", "uz", "tt", "bn", "pa", "gu", "or", "ta", "te", "kn", "ml", 1363 "as", "mr", "sa", "mn", "bo", "cy", "km", "lo", "gl", "kok", "syr", "si", "iu", 1364 "am", "tzm", "ne", "fy", "ps", "fil", "dv", "ha", "yo", "quz", "nso", "ba", "lb", 1365 "kl", "ig", "ii", "arn", "moh", "br", "ug", "mi", "oc", "co", "gsw", "sah", 1366 "qut", "rw", "wo", "prs", "gd", "ar-SA", "bg-BG", "ca-ES", "zh-TW", "cs-CZ", 1367 "da-DK", "de-DE", "el-GR", "en-US", "fi-FI", "fr-FR", "he-IL", "hu-HU", "is-IS", 1368 "it-IT", "ja-JP", "ko-KR", "nl-NL", "nb-NO", "pl-PL", "pt-BR", "rm-CH", "ro-RO", 1369 "ru-RU", "hr-HR", "sk-SK", "sq-AL", "sv-SE", "th-TH", "tr-TR", "ur-PK", "id-ID", 1370 "uk-UA", "be-BY", "sl-SI", "et-EE", "lv-LV", "lt-LT", "tg-Cyrl-TJ", "fa-IR", 1371 "vi-VN", "hy-AM", "az-Latn-AZ", "eu-ES", "hsb-DE", "mk-MK", "tn-ZA", "xh-ZA", 1372 "zu-ZA", "af-ZA", "ka-GE", "fo-FO", "hi-IN", "mt-MT", "se-NO", "ms-MY", "kk-KZ", 1373 "ky-KG", "sw-KE", "tk-TM", "uz-Latn-UZ", "tt-RU", "bn-IN", "pa-IN", "gu-IN", 1374 "or-IN", "ta-IN", "te-IN", "kn-IN", "ml-IN", "as-IN", "mr-IN", "sa-IN", "mn-MN", 1375 "bo-CN", "cy-GB", "km-KH", "lo-LA", "gl-ES", "kok-IN", "syr-SY", "si-LK", 1376 "iu-Cans-CA", "am-ET", "ne-NP", "fy-NL", "ps-AF", "fil-PH", "dv-MV", 1377 "ha-Latn-NG", "yo-NG", "quz-BO", "nso-ZA", "ba-RU", "lb-LU", "kl-GL", "ig-NG", 1378 "ii-CN", "arn-CL", "moh-CA", "br-FR", "ug-CN", "mi-NZ", "oc-FR", "co-FR", 1379 "gsw-FR", "sah-RU", "qut-GT", "rw-RW", "wo-SN", "prs-AF", "gd-GB", "ar-IQ", 1380 "zh-CN", "de-CH", "en-GB", "es-MX", "fr-BE", "it-CH", "nl-BE", "nn-NO", "pt-PT", 1381 "sr-Latn-CS", "sv-FI", "az-Cyrl-AZ", "dsb-DE", "se-SE", "ga-IE", "ms-BN", 1382 "uz-Cyrl-UZ", "bn-BD", "mn-Mong-CN", "iu-Latn-CA", "tzm-Latn-DZ", "quz-EC", 1383 "ar-EG", "zh-HK", "de-AT", "en-AU", "es-ES", "fr-CA", "sr-Cyrl-CS", "se-FI", 1384 "quz-PE", "ar-LY", "zh-SG", "de-LU", "en-CA", "es-GT", "fr-CH", "hr-BA", 1385 "smj-NO", "ar-DZ", "zh-MO", "de-LI", "en-NZ", "es-CR", "fr-LU", "bs-Latn-BA", 1386 "smj-SE", "ar-MA", "en-IE", "es-PA", "fr-MC", "sr-Latn-BA", "sma-NO", "ar-TN", 1387 "en-ZA", "es-DO", "sr-Cyrl-BA", "sma-SE", "ar-OM", "en-JM", "es-VE", 1388 "bs-Cyrl-BA", "sms-FI", "ar-YE", "en-029", "es-CO", "sr-Latn-RS", "smn-FI", 1389 "ar-SY", "en-BZ", "es-PE", "sr-Cyrl-RS", "ar-JO", "en-TT", "es-AR", "sr-Latn-ME", 1390 "ar-LB", "en-ZW", "es-EC", "sr-Cyrl-ME", "ar-KW", "en-PH", "es-CL", "ar-AE", 1391 "es-UY", "ar-BH", "es-PY", "ar-QA", "en-IN", "es-BO", "en-MY", "es-SV", "en-SG", 1392 "es-HN", "es-NI", "es-PR", "es-US", "bs-Cyrl", "bs-Latn", "sr-Cyrl", "sr-Latn", 1393 "smn", "az-Cyrl", "sms", "zh", "nn", "bs", "az-Latn", "sma", "uz-Cyrl", 1394 "mn-Cyrl", "iu-Cans", "zh-Hant", "nb", "sr", "tg-Cyrl", "dsb", "smj", "uz-Latn", 1395 "mn-Mong", "iu-Latn", "tzm-Latn", "ha-Latn", "zh-CHS", "zh-CHT"]); 1396 1397 function isRFC5646(str) { 1398 assertString(str); 1399 // According to the spec these codes are case sensitive so we can check the 1400 // string directly. 1401 return cultureCodes.has(str); 1402 } 1403 1404 var semver = /^v?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(-(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$/i 1405 1406 function isSemVer(str) { 1407 assertString(str); 1408 return semver.test(str); 1409 } 1410 1411 var rgbcolor = /^rgb?\(\s*(0|[1-9]\d?|1\d\d?|2[0-4]\d|25[0-5])\s*,\s*(0|[1-9]\d?|1\d\d?|2[0-4]\d|25[0-5])\s*,\s*(0|[1-9]\d?|1\d\d?|2[0-4]\d|25[0-5])\s*\)$/i 1412 1413 function isRGBColor(str) { 1414 assertString(str); 1415 return rgbcolor.test(str); 1416 } 1417 1418 var version = '7.0.0'; 1419 1420 var validator = { 1421 blacklist: blacklist, 1422 contains: contains, 1423 equals: equals, 1424 escape: escape, 1425 isAfter: isAfter, 1426 isAlpha: isAlpha, 1427 isAlphanumeric: isAlphanumeric, 1428 isAscii: isAscii, 1429 isBase64: isBase64, 1430 isBefore: isBefore, 1431 isBoolean: isBoolean, 1432 isByteLength: isByteLength, 1433 isCreditCard: isCreditCard, 1434 isCurrency: isCurrency, 1435 isDataURI: isDataURI, 1436 isDecimal: isDecimal, 1437 isDivisibleBy: isDivisibleBy, 1438 isEmail: isEmail, 1439 isEmpty: isEmpty, 1440 isFloat: isFloat, 1441 isFQDN: isFDQN, 1442 isFullWidth: isFullWidth, 1443 isHalfWidth: isHalfWidth, 1444 isHexadecimal: isHexadecimal, 1445 isHexColor: isHexColor, 1446 isIn: isIn, 1447 isInt: isInt, 1448 isIP: isIP, 1449 isRFC5646: isRFC5646, 1450 isISBN: isISBN, 1451 isISIN: isISIN, 1452 isISO8601: isISO8601, 1453 isISRC: isISRC, 1454 isRGBColor: isRGBColor, 1455 isISSN: isISSN, 1456 isJSON: isJSON, 1457 isLength: isLength, 1458 isLowercase: isLowercase, 1459 isMACAddress: isMACAddress, 1460 isMD5: isMD5, 1461 isMobilePhone: isMobilePhone, 1462 isMongoId: isMongoId, 1463 isMultibyte: isMultibyte, 1464 isNumeric: isNumeric, 1465 isSemVer: isSemVer, 1466 isSurrogatePair: isSurrogatePair, 1467 isUppercase: isUppercase, 1468 isURL: isURL, 1469 isUUID: isUUID, 1470 isVariableWidth: isVariableWidth, 1471 isWhitelisted: isWhitelisted, 1472 ltrim: ltrim, 1473 matches: matches, 1474 normalizeEmail: normalizeEmail, 1475 rtrim: rtrim, 1476 stripLow: stripLow, 1477 toBoolean: toBoolean, 1478 toDate: toDate, 1479 toFloat: toFloat, 1480 toInt: toInt, 1481 toString: toString, 1482 trim: trim, 1483 unescape: unescape, 1484 version: version, 1485 whitelist: whitelist 1486 }; 1487 1488 return validator; 1489 }));