summaryrefslogtreecommitdiffstats
path: root/devtools/shared/storage
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--devtools/shared/storage/moz.build9
-rw-r--r--devtools/shared/storage/utils.js161
-rw-r--r--devtools/shared/storage/vendor/JSON5_LICENSE23
-rw-r--r--devtools/shared/storage/vendor/JSON5_UPGRADING.md36
-rw-r--r--devtools/shared/storage/vendor/json5.js1713
-rw-r--r--devtools/shared/storage/vendor/moz.build13
-rw-r--r--devtools/shared/storage/vendor/stringvalidator/UPDATING.md142
-rw-r--r--devtools/shared/storage/vendor/stringvalidator/moz.build15
-rw-r--r--devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/head_stringvalidator.js15
-rw-r--r--devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/test_sanitizers.js419
-rw-r--r--devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/test_validators.js3762
-rw-r--r--devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/xpcshell.toml9
-rw-r--r--devtools/shared/storage/vendor/stringvalidator/util/assert.js215
-rw-r--r--devtools/shared/storage/vendor/stringvalidator/util/moz.build9
-rw-r--r--devtools/shared/storage/vendor/stringvalidator/validator.js1489
15 files changed, 8030 insertions, 0 deletions
diff --git a/devtools/shared/storage/moz.build b/devtools/shared/storage/moz.build
new file mode 100644
index 0000000000..95cf2857de
--- /dev/null
+++ b/devtools/shared/storage/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += ["vendor"]
+
+DevToolsModules("utils.js")
diff --git a/devtools/shared/storage/utils.js b/devtools/shared/storage/utils.js
new file mode 100644
index 0000000000..2c31a4ae88
--- /dev/null
+++ b/devtools/shared/storage/utils.js
@@ -0,0 +1,161 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+loader.lazyRequireGetter(
+ this,
+ "validator",
+ "resource://devtools/shared/storage/vendor/stringvalidator/validator.js"
+);
+loader.lazyRequireGetter(
+ this,
+ "JSON5",
+ "resource://devtools/shared/storage/vendor/json5.js"
+);
+
+const MATH_REGEX =
+ /(?:(?:^|[-+_*/])(?:\s*-?\d+(\.\d+)?(?:[eE][+-]?\d+)?\s*))+$/;
+
+/**
+ * Tries to parse a string into an object on the basis of key-value pairs,
+ * separated by various separators. If failed, tries to parse for single
+ * separator separated values to form an array.
+ *
+ * @param {string} value
+ * The string to be parsed into an object or array
+ */
+function _extractKeyValPairs(value) {
+ const makeObject = (keySep, pairSep) => {
+ const object = {};
+ for (const pair of value.split(pairSep)) {
+ const [key, val] = pair.split(keySep);
+ object[key] = val;
+ }
+ return object;
+ };
+
+ // Possible separators.
+ const separators = ["=", ":", "~", "#", "&", "\\*", ",", "\\."];
+ // Testing for object
+ for (let i = 0; i < separators.length; i++) {
+ const kv = separators[i];
+ for (let j = 0; j < separators.length; j++) {
+ if (i == j) {
+ continue;
+ }
+ const p = separators[j];
+ const word = `[^${kv}${p}]*`;
+ const keyValue = `${word}${kv}${word}`;
+ const keyValueList = `${keyValue}(${p}${keyValue})*`;
+ const regex = new RegExp(`^${keyValueList}$`);
+ if (
+ value.match &&
+ value.match(regex) &&
+ value.includes(kv) &&
+ (value.includes(p) || value.split(kv).length == 2)
+ ) {
+ return makeObject(kv, p);
+ }
+ }
+ }
+ // Testing for array
+ for (const p of separators) {
+ const word = `[^${p}]*`;
+ const wordList = `(${word}${p})+${word}`;
+ const regex = new RegExp(`^${wordList}$`);
+
+ if (regex.test(value)) {
+ const pNoBackslash = p.replace(/\\*/g, "");
+ return value.split(pNoBackslash);
+ }
+ }
+ return null;
+}
+
+/**
+ * Check whether the value string represents something that should be
+ * displayed as text. If so then it shouldn't be parsed into a tree.
+ *
+ * @param {String} value
+ * The value to be parsed.
+ */
+function _shouldParse(value) {
+ const validators = [
+ "isBase64",
+ "isBoolean",
+ "isCurrency",
+ "isDataURI",
+ "isEmail",
+ "isFQDN",
+ "isHexColor",
+ "isIP",
+ "isISO8601",
+ "isMACAddress",
+ "isSemVer",
+ "isURL",
+ ];
+
+ // Check for minus calculations e.g. 8-3 because otherwise 5 will be displayed.
+ if (MATH_REGEX.test(value)) {
+ return false;
+ }
+
+ // Check for any other types that shouldn't be parsed.
+ for (const test of validators) {
+ if (validator[test](value)) {
+ return false;
+ }
+ }
+
+ // Seems like this is data that should be parsed.
+ return true;
+}
+
+/**
+ * Tries to parse a string value into either a json or a key-value separated
+ * object. The value can also be a key separated array.
+ *
+ * @param {string} originalValue
+ * The string to be parsed into an object
+ */
+function parseItemValue(originalValue) {
+ // Find if value is URLEncoded ie
+ let decodedValue = "";
+ try {
+ decodedValue = decodeURIComponent(originalValue);
+ } catch (e) {
+ // Unable to decode, nothing to do
+ }
+ const value =
+ decodedValue && decodedValue !== originalValue
+ ? decodedValue
+ : originalValue;
+
+ if (!_shouldParse(value)) {
+ return value;
+ }
+
+ let obj = null;
+ try {
+ obj = JSON5.parse(value);
+ } catch (ex) {
+ obj = null;
+ }
+
+ if (!obj && value) {
+ obj = _extractKeyValPairs(value);
+ }
+
+ // return if obj is null, or same as value, or just a string.
+ if (!obj || obj === value || typeof obj === "string") {
+ return value;
+ }
+
+ // If we got this far, originalValue is an object literal or array,
+ // and we have successfully parsed it
+ return obj;
+}
+
+exports.parseItemValue = parseItemValue;
diff --git a/devtools/shared/storage/vendor/JSON5_LICENSE b/devtools/shared/storage/vendor/JSON5_LICENSE
new file mode 100644
index 0000000000..2171aca5a8
--- /dev/null
+++ b/devtools/shared/storage/vendor/JSON5_LICENSE
@@ -0,0 +1,23 @@
+MIT License
+
+Copyright (c) 2012-2018 Aseem Kishore, and [others].
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+[others]: https://github.com/json5/json5/contributors
diff --git a/devtools/shared/storage/vendor/JSON5_UPGRADING.md b/devtools/shared/storage/vendor/JSON5_UPGRADING.md
new file mode 100644
index 0000000000..631d73ee46
--- /dev/null
+++ b/devtools/shared/storage/vendor/JSON5_UPGRADING.md
@@ -0,0 +1,36 @@
+[//]: # (
+ This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+)
+
+# Upgrading json5
+
+## Getting the Source
+
+```bash
+git clone https://github.com/json5/json5
+cd json5
+git checkout v2.2.1 # checkout the right version tag
+```
+
+## Building
+
+```bash
+npm install
+npm run build
+cp dist/index.js <gecko-dev>/devtools/shared/storage/vendor/json5.js
+```
+
+## Patching json5
+
+- open `json5.js`
+- Add the version number to the top of the file:
+ ```
+ /**
+ * json5 v2.2.1
+ */
+ ```
+- Replace instances of `Function('return this')()` with `globalThis`. See Bug 1473549.
+
+## Update the version:
+
+The current version is 2.2.1. Update this version number everywhere in this file.
diff --git a/devtools/shared/storage/vendor/json5.js b/devtools/shared/storage/vendor/json5.js
new file mode 100644
index 0000000000..667d78ffdd
--- /dev/null
+++ b/devtools/shared/storage/vendor/json5.js
@@ -0,0 +1,1713 @@
+/**
+ * json5 v2.2.1
+ */
+
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global.JSON5 = factory());
+}(this, (function () { 'use strict';
+
+ function createCommonjsModule(fn, module) {
+ return module = { exports: {} }, fn(module, module.exports), module.exports;
+ }
+
+ var _global = createCommonjsModule(function (module) {
+ // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028
+ var global = module.exports = typeof window != 'undefined' && window.Math == Math
+ ? window : typeof self != 'undefined' && self.Math == Math ? self
+ : globalThis;
+ if (typeof __g == 'number') { __g = global; } // eslint-disable-line no-undef
+ });
+
+ var _core = createCommonjsModule(function (module) {
+ var core = module.exports = { version: '2.6.5' };
+ if (typeof __e == 'number') { __e = core; } // eslint-disable-line no-undef
+ });
+ var _core_1 = _core.version;
+
+ var _isObject = function (it) {
+ return typeof it === 'object' ? it !== null : typeof it === 'function';
+ };
+
+ var _anObject = function (it) {
+ if (!_isObject(it)) { throw TypeError(it + ' is not an object!'); }
+ return it;
+ };
+
+ var _fails = function (exec) {
+ try {
+ return !!exec();
+ } catch (e) {
+ return true;
+ }
+ };
+
+ // Thank's IE8 for his funny defineProperty
+ var _descriptors = !_fails(function () {
+ return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7;
+ });
+
+ var document = _global.document;
+ // typeof document.createElement is 'object' in old IE
+ var is = _isObject(document) && _isObject(document.createElement);
+ var _domCreate = function (it) {
+ return is ? document.createElement(it) : {};
+ };
+
+ var _ie8DomDefine = !_descriptors && !_fails(function () {
+ return Object.defineProperty(_domCreate('div'), 'a', { get: function () { return 7; } }).a != 7;
+ });
+
+ // 7.1.1 ToPrimitive(input [, PreferredType])
+
+ // instead of the ES6 spec version, we didn't implement @@toPrimitive case
+ // and the second argument - flag - preferred type is a string
+ var _toPrimitive = function (it, S) {
+ if (!_isObject(it)) { return it; }
+ var fn, val;
+ if (S && typeof (fn = it.toString) == 'function' && !_isObject(val = fn.call(it))) { return val; }
+ if (typeof (fn = it.valueOf) == 'function' && !_isObject(val = fn.call(it))) { return val; }
+ if (!S && typeof (fn = it.toString) == 'function' && !_isObject(val = fn.call(it))) { return val; }
+ throw TypeError("Can't convert object to primitive value");
+ };
+
+ var dP = Object.defineProperty;
+
+ var f = _descriptors ? Object.defineProperty : function defineProperty(O, P, Attributes) {
+ _anObject(O);
+ P = _toPrimitive(P, true);
+ _anObject(Attributes);
+ if (_ie8DomDefine) { try {
+ return dP(O, P, Attributes);
+ } catch (e) { /* empty */ } }
+ if ('get' in Attributes || 'set' in Attributes) { throw TypeError('Accessors not supported!'); }
+ if ('value' in Attributes) { O[P] = Attributes.value; }
+ return O;
+ };
+
+ var _objectDp = {
+ f: f
+ };
+
+ var _propertyDesc = function (bitmap, value) {
+ return {
+ enumerable: !(bitmap & 1),
+ configurable: !(bitmap & 2),
+ writable: !(bitmap & 4),
+ value: value
+ };
+ };
+
+ var _hide = _descriptors ? function (object, key, value) {
+ return _objectDp.f(object, key, _propertyDesc(1, value));
+ } : function (object, key, value) {
+ object[key] = value;
+ return object;
+ };
+
+ var hasOwnProperty = {}.hasOwnProperty;
+ var _has = function (it, key) {
+ return hasOwnProperty.call(it, key);
+ };
+
+ var id = 0;
+ var px = Math.random();
+ var _uid = function (key) {
+ return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36));
+ };
+
+ var _library = false;
+
+ var _shared = createCommonjsModule(function (module) {
+ var SHARED = '__core-js_shared__';
+ var store = _global[SHARED] || (_global[SHARED] = {});
+
+ (module.exports = function (key, value) {
+ return store[key] || (store[key] = value !== undefined ? value : {});
+ })('versions', []).push({
+ version: _core.version,
+ mode: _library ? 'pure' : 'global',
+ copyright: '© 2019 Denis Pushkarev (zloirock.ru)'
+ });
+ });
+
+ var _functionToString = _shared('native-function-to-string', Function.toString);
+
+ var _redefine = createCommonjsModule(function (module) {
+ var SRC = _uid('src');
+
+ var TO_STRING = 'toString';
+ var TPL = ('' + _functionToString).split(TO_STRING);
+
+ _core.inspectSource = function (it) {
+ return _functionToString.call(it);
+ };
+
+ (module.exports = function (O, key, val, safe) {
+ var isFunction = typeof val == 'function';
+ if (isFunction) { _has(val, 'name') || _hide(val, 'name', key); }
+ if (O[key] === val) { return; }
+ if (isFunction) { _has(val, SRC) || _hide(val, SRC, O[key] ? '' + O[key] : TPL.join(String(key))); }
+ if (O === _global) {
+ O[key] = val;
+ } else if (!safe) {
+ delete O[key];
+ _hide(O, key, val);
+ } else if (O[key]) {
+ O[key] = val;
+ } else {
+ _hide(O, key, val);
+ }
+ // add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative
+ })(Function.prototype, TO_STRING, function toString() {
+ return typeof this == 'function' && this[SRC] || _functionToString.call(this);
+ });
+ });
+
+ var _aFunction = function (it) {
+ if (typeof it != 'function') { throw TypeError(it + ' is not a function!'); }
+ return it;
+ };
+
+ // optional / simple context binding
+
+ var _ctx = function (fn, that, length) {
+ _aFunction(fn);
+ if (that === undefined) { return fn; }
+ switch (length) {
+ case 1: return function (a) {
+ return fn.call(that, a);
+ };
+ case 2: return function (a, b) {
+ return fn.call(that, a, b);
+ };
+ case 3: return function (a, b, c) {
+ return fn.call(that, a, b, c);
+ };
+ }
+ return function (/* ...args */) {
+ return fn.apply(that, arguments);
+ };
+ };
+
+ var PROTOTYPE = 'prototype';
+
+ var $export = function (type, name, source) {
+ var IS_FORCED = type & $export.F;
+ var IS_GLOBAL = type & $export.G;
+ var IS_STATIC = type & $export.S;
+ var IS_PROTO = type & $export.P;
+ var IS_BIND = type & $export.B;
+ var target = IS_GLOBAL ? _global : IS_STATIC ? _global[name] || (_global[name] = {}) : (_global[name] || {})[PROTOTYPE];
+ var exports = IS_GLOBAL ? _core : _core[name] || (_core[name] = {});
+ var expProto = exports[PROTOTYPE] || (exports[PROTOTYPE] = {});
+ var key, own, out, exp;
+ if (IS_GLOBAL) { source = name; }
+ for (key in source) {
+ // contains in native
+ own = !IS_FORCED && target && target[key] !== undefined;
+ // export native or passed
+ out = (own ? target : source)[key];
+ // bind timers to global for call from export context
+ exp = IS_BIND && own ? _ctx(out, _global) : IS_PROTO && typeof out == 'function' ? _ctx(Function.call, out) : out;
+ // extend global
+ if (target) { _redefine(target, key, out, type & $export.U); }
+ // export
+ if (exports[key] != out) { _hide(exports, key, exp); }
+ if (IS_PROTO && expProto[key] != out) { expProto[key] = out; }
+ }
+ };
+ _global.core = _core;
+ // type bitmap
+ $export.F = 1; // forced
+ $export.G = 2; // global
+ $export.S = 4; // static
+ $export.P = 8; // proto
+ $export.B = 16; // bind
+ $export.W = 32; // wrap
+ $export.U = 64; // safe
+ $export.R = 128; // real proto method for `library`
+ var _export = $export;
+
+ // 7.1.4 ToInteger
+ var ceil = Math.ceil;
+ var floor = Math.floor;
+ var _toInteger = function (it) {
+ return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it);
+ };
+
+ // 7.2.1 RequireObjectCoercible(argument)
+ var _defined = function (it) {
+ if (it == undefined) { throw TypeError("Can't call method on " + it); }
+ return it;
+ };
+
+ // true -> String#at
+ // false -> String#codePointAt
+ var _stringAt = function (TO_STRING) {
+ return function (that, pos) {
+ var s = String(_defined(that));
+ var i = _toInteger(pos);
+ var l = s.length;
+ var a, b;
+ if (i < 0 || i >= l) { return TO_STRING ? '' : undefined; }
+ a = s.charCodeAt(i);
+ return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff
+ ? TO_STRING ? s.charAt(i) : a
+ : TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000;
+ };
+ };
+
+ var $at = _stringAt(false);
+ _export(_export.P, 'String', {
+ // 21.1.3.3 String.prototype.codePointAt(pos)
+ codePointAt: function codePointAt(pos) {
+ return $at(this, pos);
+ }
+ });
+
+ var codePointAt = _core.String.codePointAt;
+
+ var max = Math.max;
+ var min = Math.min;
+ var _toAbsoluteIndex = function (index, length) {
+ index = _toInteger(index);
+ return index < 0 ? max(index + length, 0) : min(index, length);
+ };
+
+ var fromCharCode = String.fromCharCode;
+ var $fromCodePoint = String.fromCodePoint;
+
+ // length should be 1, old FF problem
+ _export(_export.S + _export.F * (!!$fromCodePoint && $fromCodePoint.length != 1), 'String', {
+ // 21.1.2.2 String.fromCodePoint(...codePoints)
+ fromCodePoint: function fromCodePoint(x) {
+ var arguments$1 = arguments;
+ // eslint-disable-line no-unused-vars
+ var res = [];
+ var aLen = arguments.length;
+ var i = 0;
+ var code;
+ while (aLen > i) {
+ code = +arguments$1[i++];
+ if (_toAbsoluteIndex(code, 0x10ffff) !== code) { throw RangeError(code + ' is not a valid code point'); }
+ res.push(code < 0x10000
+ ? fromCharCode(code)
+ : fromCharCode(((code -= 0x10000) >> 10) + 0xd800, code % 0x400 + 0xdc00)
+ );
+ } return res.join('');
+ }
+ });
+
+ var fromCodePoint = _core.String.fromCodePoint;
+
+ // This is a generated file. Do not edit.
+ var Space_Separator = /[\u1680\u2000-\u200A\u202F\u205F\u3000]/;
+ var ID_Start = /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF2D-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE83\uDE86-\uDE89\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F\uDFE0\uDFE1]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00-\uDD1E\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]/;
+ var ID_Continue = /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u08D4-\u08E1\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u09FC\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9-\u0AFF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C80-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D00-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D54-\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19D9\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1C80-\u1C88\u1CD0-\u1CD2\u1CD4-\u1CF9\u1D00-\u1DF9\u1DFB-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u2E2F\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099\u309A\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C5\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF2D-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE3E\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC00-\uDC4A\uDC50-\uDC59\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDE00-\uDE3E\uDE47\uDE50-\uDE83\uDE86-\uDE99\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC36\uDC38-\uDC40\uDC50-\uDC59\uDC72-\uDC8F\uDC92-\uDCA7\uDCA9-\uDCB6\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD36\uDD3A\uDD3C\uDD3D\uDD3F-\uDD47\uDD50-\uDD59]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F\uDFE0\uDFE1]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00-\uDD1E\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD838[\uDC00-\uDC06\uDC08-\uDC18\uDC1B-\uDC21\uDC23\uDC24\uDC26-\uDC2A]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6\uDD00-\uDD4A\uDD50-\uDD59]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/;
+
+ var unicode = {
+ Space_Separator: Space_Separator,
+ ID_Start: ID_Start,
+ ID_Continue: ID_Continue
+ };
+
+ var util = {
+ isSpaceSeparator: function isSpaceSeparator (c) {
+ return typeof c === 'string' && unicode.Space_Separator.test(c)
+ },
+
+ isIdStartChar: function isIdStartChar (c) {
+ return typeof c === 'string' && (
+ (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c === '$') || (c === '_') ||
+ unicode.ID_Start.test(c)
+ )
+ },
+
+ isIdContinueChar: function isIdContinueChar (c) {
+ return typeof c === 'string' && (
+ (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') ||
+ (c === '$') || (c === '_') ||
+ (c === '\u200C') || (c === '\u200D') ||
+ unicode.ID_Continue.test(c)
+ )
+ },
+
+ isDigit: function isDigit (c) {
+ return typeof c === 'string' && /[0-9]/.test(c)
+ },
+
+ isHexDigit: function isHexDigit (c) {
+ return typeof c === 'string' && /[0-9A-Fa-f]/.test(c)
+ },
+ };
+
+ var source;
+ var parseState;
+ var stack;
+ var pos;
+ var line;
+ var column;
+ var token;
+ var key;
+ var root;
+
+ var parse = function parse (text, reviver) {
+ source = String(text);
+ parseState = 'start';
+ stack = [];
+ pos = 0;
+ line = 1;
+ column = 0;
+ token = undefined;
+ key = undefined;
+ root = undefined;
+
+ do {
+ token = lex();
+
+ // This code is unreachable.
+ // if (!parseStates[parseState]) {
+ // throw invalidParseState()
+ // }
+
+ parseStates[parseState]();
+ } while (token.type !== 'eof')
+
+ if (typeof reviver === 'function') {
+ return internalize({'': root}, '', reviver)
+ }
+
+ return root
+ };
+
+ function internalize (holder, name, reviver) {
+ var value = holder[name];
+ if (value != null && typeof value === 'object') {
+ for (var key in value) {
+ var replacement = internalize(value, key, reviver);
+ if (replacement === undefined) {
+ delete value[key];
+ } else {
+ value[key] = replacement;
+ }
+ }
+ }
+
+ return reviver.call(holder, name, value)
+ }
+
+ var lexState;
+ var buffer;
+ var doubleQuote;
+ var sign;
+ var c;
+
+ function lex () {
+ lexState = 'default';
+ buffer = '';
+ doubleQuote = false;
+ sign = 1;
+
+ for (;;) {
+ c = peek();
+
+ // This code is unreachable.
+ // if (!lexStates[lexState]) {
+ // throw invalidLexState(lexState)
+ // }
+
+ var token = lexStates[lexState]();
+ if (token) {
+ return token
+ }
+ }
+ }
+
+ function peek () {
+ if (source[pos]) {
+ return String.fromCodePoint(source.codePointAt(pos))
+ }
+ }
+
+ function read () {
+ var c = peek();
+
+ if (c === '\n') {
+ line++;
+ column = 0;
+ } else if (c) {
+ column += c.length;
+ } else {
+ column++;
+ }
+
+ if (c) {
+ pos += c.length;
+ }
+
+ return c
+ }
+
+ var lexStates = {
+ default: function default$1 () {
+ switch (c) {
+ case '\t':
+ case '\v':
+ case '\f':
+ case ' ':
+ case '\u00A0':
+ case '\uFEFF':
+ case '\n':
+ case '\r':
+ case '\u2028':
+ case '\u2029':
+ read();
+ return
+
+ case '/':
+ read();
+ lexState = 'comment';
+ return
+
+ case undefined:
+ read();
+ return newToken('eof')
+ }
+
+ if (util.isSpaceSeparator(c)) {
+ read();
+ return
+ }
+
+ // This code is unreachable.
+ // if (!lexStates[parseState]) {
+ // throw invalidLexState(parseState)
+ // }
+
+ return lexStates[parseState]()
+ },
+
+ comment: function comment () {
+ switch (c) {
+ case '*':
+ read();
+ lexState = 'multiLineComment';
+ return
+
+ case '/':
+ read();
+ lexState = 'singleLineComment';
+ return
+ }
+
+ throw invalidChar(read())
+ },
+
+ multiLineComment: function multiLineComment () {
+ switch (c) {
+ case '*':
+ read();
+ lexState = 'multiLineCommentAsterisk';
+ return
+
+ case undefined:
+ throw invalidChar(read())
+ }
+
+ read();
+ },
+
+ multiLineCommentAsterisk: function multiLineCommentAsterisk () {
+ switch (c) {
+ case '*':
+ read();
+ return
+
+ case '/':
+ read();
+ lexState = 'default';
+ return
+
+ case undefined:
+ throw invalidChar(read())
+ }
+
+ read();
+ lexState = 'multiLineComment';
+ },
+
+ singleLineComment: function singleLineComment () {
+ switch (c) {
+ case '\n':
+ case '\r':
+ case '\u2028':
+ case '\u2029':
+ read();
+ lexState = 'default';
+ return
+
+ case undefined:
+ read();
+ return newToken('eof')
+ }
+
+ read();
+ },
+
+ value: function value () {
+ switch (c) {
+ case '{':
+ case '[':
+ return newToken('punctuator', read())
+
+ case 'n':
+ read();
+ literal('ull');
+ return newToken('null', null)
+
+ case 't':
+ read();
+ literal('rue');
+ return newToken('boolean', true)
+
+ case 'f':
+ read();
+ literal('alse');
+ return newToken('boolean', false)
+
+ case '-':
+ case '+':
+ if (read() === '-') {
+ sign = -1;
+ }
+
+ lexState = 'sign';
+ return
+
+ case '.':
+ buffer = read();
+ lexState = 'decimalPointLeading';
+ return
+
+ case '0':
+ buffer = read();
+ lexState = 'zero';
+ return
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ buffer = read();
+ lexState = 'decimalInteger';
+ return
+
+ case 'I':
+ read();
+ literal('nfinity');
+ return newToken('numeric', Infinity)
+
+ case 'N':
+ read();
+ literal('aN');
+ return newToken('numeric', NaN)
+
+ case '"':
+ case "'":
+ doubleQuote = (read() === '"');
+ buffer = '';
+ lexState = 'string';
+ return
+ }
+
+ throw invalidChar(read())
+ },
+
+ identifierNameStartEscape: function identifierNameStartEscape () {
+ if (c !== 'u') {
+ throw invalidChar(read())
+ }
+
+ read();
+ var u = unicodeEscape();
+ switch (u) {
+ case '$':
+ case '_':
+ break
+
+ default:
+ if (!util.isIdStartChar(u)) {
+ throw invalidIdentifier()
+ }
+
+ break
+ }
+
+ buffer += u;
+ lexState = 'identifierName';
+ },
+
+ identifierName: function identifierName () {
+ switch (c) {
+ case '$':
+ case '_':
+ case '\u200C':
+ case '\u200D':
+ buffer += read();
+ return
+
+ case '\\':
+ read();
+ lexState = 'identifierNameEscape';
+ return
+ }
+
+ if (util.isIdContinueChar(c)) {
+ buffer += read();
+ return
+ }
+
+ return newToken('identifier', buffer)
+ },
+
+ identifierNameEscape: function identifierNameEscape () {
+ if (c !== 'u') {
+ throw invalidChar(read())
+ }
+
+ read();
+ var u = unicodeEscape();
+ switch (u) {
+ case '$':
+ case '_':
+ case '\u200C':
+ case '\u200D':
+ break
+
+ default:
+ if (!util.isIdContinueChar(u)) {
+ throw invalidIdentifier()
+ }
+
+ break
+ }
+
+ buffer += u;
+ lexState = 'identifierName';
+ },
+
+ sign: function sign$1 () {
+ switch (c) {
+ case '.':
+ buffer = read();
+ lexState = 'decimalPointLeading';
+ return
+
+ case '0':
+ buffer = read();
+ lexState = 'zero';
+ return
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ buffer = read();
+ lexState = 'decimalInteger';
+ return
+
+ case 'I':
+ read();
+ literal('nfinity');
+ return newToken('numeric', sign * Infinity)
+
+ case 'N':
+ read();
+ literal('aN');
+ return newToken('numeric', NaN)
+ }
+
+ throw invalidChar(read())
+ },
+
+ zero: function zero () {
+ switch (c) {
+ case '.':
+ buffer += read();
+ lexState = 'decimalPoint';
+ return
+
+ case 'e':
+ case 'E':
+ buffer += read();
+ lexState = 'decimalExponent';
+ return
+
+ case 'x':
+ case 'X':
+ buffer += read();
+ lexState = 'hexadecimal';
+ return
+ }
+
+ return newToken('numeric', sign * 0)
+ },
+
+ decimalInteger: function decimalInteger () {
+ switch (c) {
+ case '.':
+ buffer += read();
+ lexState = 'decimalPoint';
+ return
+
+ case 'e':
+ case 'E':
+ buffer += read();
+ lexState = 'decimalExponent';
+ return
+ }
+
+ if (util.isDigit(c)) {
+ buffer += read();
+ return
+ }
+
+ return newToken('numeric', sign * Number(buffer))
+ },
+
+ decimalPointLeading: function decimalPointLeading () {
+ if (util.isDigit(c)) {
+ buffer += read();
+ lexState = 'decimalFraction';
+ return
+ }
+
+ throw invalidChar(read())
+ },
+
+ decimalPoint: function decimalPoint () {
+ switch (c) {
+ case 'e':
+ case 'E':
+ buffer += read();
+ lexState = 'decimalExponent';
+ return
+ }
+
+ if (util.isDigit(c)) {
+ buffer += read();
+ lexState = 'decimalFraction';
+ return
+ }
+
+ return newToken('numeric', sign * Number(buffer))
+ },
+
+ decimalFraction: function decimalFraction () {
+ switch (c) {
+ case 'e':
+ case 'E':
+ buffer += read();
+ lexState = 'decimalExponent';
+ return
+ }
+
+ if (util.isDigit(c)) {
+ buffer += read();
+ return
+ }
+
+ return newToken('numeric', sign * Number(buffer))
+ },
+
+ decimalExponent: function decimalExponent () {
+ switch (c) {
+ case '+':
+ case '-':
+ buffer += read();
+ lexState = 'decimalExponentSign';
+ return
+ }
+
+ if (util.isDigit(c)) {
+ buffer += read();
+ lexState = 'decimalExponentInteger';
+ return
+ }
+
+ throw invalidChar(read())
+ },
+
+ decimalExponentSign: function decimalExponentSign () {
+ if (util.isDigit(c)) {
+ buffer += read();
+ lexState = 'decimalExponentInteger';
+ return
+ }
+
+ throw invalidChar(read())
+ },
+
+ decimalExponentInteger: function decimalExponentInteger () {
+ if (util.isDigit(c)) {
+ buffer += read();
+ return
+ }
+
+ return newToken('numeric', sign * Number(buffer))
+ },
+
+ hexadecimal: function hexadecimal () {
+ if (util.isHexDigit(c)) {
+ buffer += read();
+ lexState = 'hexadecimalInteger';
+ return
+ }
+
+ throw invalidChar(read())
+ },
+
+ hexadecimalInteger: function hexadecimalInteger () {
+ if (util.isHexDigit(c)) {
+ buffer += read();
+ return
+ }
+
+ return newToken('numeric', sign * Number(buffer))
+ },
+
+ string: function string () {
+ switch (c) {
+ case '\\':
+ read();
+ buffer += escape();
+ return
+
+ case '"':
+ if (doubleQuote) {
+ read();
+ return newToken('string', buffer)
+ }
+
+ buffer += read();
+ return
+
+ case "'":
+ if (!doubleQuote) {
+ read();
+ return newToken('string', buffer)
+ }
+
+ buffer += read();
+ return
+
+ case '\n':
+ case '\r':
+ throw invalidChar(read())
+
+ case '\u2028':
+ case '\u2029':
+ separatorChar(c);
+ break
+
+ case undefined:
+ throw invalidChar(read())
+ }
+
+ buffer += read();
+ },
+
+ start: function start () {
+ switch (c) {
+ case '{':
+ case '[':
+ return newToken('punctuator', read())
+
+ // This code is unreachable since the default lexState handles eof.
+ // case undefined:
+ // return newToken('eof')
+ }
+
+ lexState = 'value';
+ },
+
+ beforePropertyName: function beforePropertyName () {
+ switch (c) {
+ case '$':
+ case '_':
+ buffer = read();
+ lexState = 'identifierName';
+ return
+
+ case '\\':
+ read();
+ lexState = 'identifierNameStartEscape';
+ return
+
+ case '}':
+ return newToken('punctuator', read())
+
+ case '"':
+ case "'":
+ doubleQuote = (read() === '"');
+ lexState = 'string';
+ return
+ }
+
+ if (util.isIdStartChar(c)) {
+ buffer += read();
+ lexState = 'identifierName';
+ return
+ }
+
+ throw invalidChar(read())
+ },
+
+ afterPropertyName: function afterPropertyName () {
+ if (c === ':') {
+ return newToken('punctuator', read())
+ }
+
+ throw invalidChar(read())
+ },
+
+ beforePropertyValue: function beforePropertyValue () {
+ lexState = 'value';
+ },
+
+ afterPropertyValue: function afterPropertyValue () {
+ switch (c) {
+ case ',':
+ case '}':
+ return newToken('punctuator', read())
+ }
+
+ throw invalidChar(read())
+ },
+
+ beforeArrayValue: function beforeArrayValue () {
+ if (c === ']') {
+ return newToken('punctuator', read())
+ }
+
+ lexState = 'value';
+ },
+
+ afterArrayValue: function afterArrayValue () {
+ switch (c) {
+ case ',':
+ case ']':
+ return newToken('punctuator', read())
+ }
+
+ throw invalidChar(read())
+ },
+
+ end: function end () {
+ // This code is unreachable since it's handled by the default lexState.
+ // if (c === undefined) {
+ // read()
+ // return newToken('eof')
+ // }
+
+ throw invalidChar(read())
+ },
+ };
+
+ function newToken (type, value) {
+ return {
+ type: type,
+ value: value,
+ line: line,
+ column: column,
+ }
+ }
+
+ function literal (s) {
+ for (var i = 0, list = s; i < list.length; i += 1) {
+ var c = list[i];
+
+ var p = peek();
+
+ if (p !== c) {
+ throw invalidChar(read())
+ }
+
+ read();
+ }
+ }
+
+ function escape () {
+ var c = peek();
+ switch (c) {
+ case 'b':
+ read();
+ return '\b'
+
+ case 'f':
+ read();
+ return '\f'
+
+ case 'n':
+ read();
+ return '\n'
+
+ case 'r':
+ read();
+ return '\r'
+
+ case 't':
+ read();
+ return '\t'
+
+ case 'v':
+ read();
+ return '\v'
+
+ case '0':
+ read();
+ if (util.isDigit(peek())) {
+ throw invalidChar(read())
+ }
+
+ return '\0'
+
+ case 'x':
+ read();
+ return hexEscape()
+
+ case 'u':
+ read();
+ return unicodeEscape()
+
+ case '\n':
+ case '\u2028':
+ case '\u2029':
+ read();
+ return ''
+
+ case '\r':
+ read();
+ if (peek() === '\n') {
+ read();
+ }
+
+ return ''
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ throw invalidChar(read())
+
+ case undefined:
+ throw invalidChar(read())
+ }
+
+ return read()
+ }
+
+ function hexEscape () {
+ var buffer = '';
+ var c = peek();
+
+ if (!util.isHexDigit(c)) {
+ throw invalidChar(read())
+ }
+
+ buffer += read();
+
+ c = peek();
+ if (!util.isHexDigit(c)) {
+ throw invalidChar(read())
+ }
+
+ buffer += read();
+
+ return String.fromCodePoint(parseInt(buffer, 16))
+ }
+
+ function unicodeEscape () {
+ var buffer = '';
+ var count = 4;
+
+ while (count-- > 0) {
+ var c = peek();
+ if (!util.isHexDigit(c)) {
+ throw invalidChar(read())
+ }
+
+ buffer += read();
+ }
+
+ return String.fromCodePoint(parseInt(buffer, 16))
+ }
+
+ var parseStates = {
+ start: function start () {
+ if (token.type === 'eof') {
+ throw invalidEOF()
+ }
+
+ push();
+ },
+
+ beforePropertyName: function beforePropertyName () {
+ switch (token.type) {
+ case 'identifier':
+ case 'string':
+ key = token.value;
+ parseState = 'afterPropertyName';
+ return
+
+ case 'punctuator':
+ // This code is unreachable since it's handled by the lexState.
+ // if (token.value !== '}') {
+ // throw invalidToken()
+ // }
+
+ pop();
+ return
+
+ case 'eof':
+ throw invalidEOF()
+ }
+
+ // This code is unreachable since it's handled by the lexState.
+ // throw invalidToken()
+ },
+
+ afterPropertyName: function afterPropertyName () {
+ // This code is unreachable since it's handled by the lexState.
+ // if (token.type !== 'punctuator' || token.value !== ':') {
+ // throw invalidToken()
+ // }
+
+ if (token.type === 'eof') {
+ throw invalidEOF()
+ }
+
+ parseState = 'beforePropertyValue';
+ },
+
+ beforePropertyValue: function beforePropertyValue () {
+ if (token.type === 'eof') {
+ throw invalidEOF()
+ }
+
+ push();
+ },
+
+ beforeArrayValue: function beforeArrayValue () {
+ if (token.type === 'eof') {
+ throw invalidEOF()
+ }
+
+ if (token.type === 'punctuator' && token.value === ']') {
+ pop();
+ return
+ }
+
+ push();
+ },
+
+ afterPropertyValue: function afterPropertyValue () {
+ // This code is unreachable since it's handled by the lexState.
+ // if (token.type !== 'punctuator') {
+ // throw invalidToken()
+ // }
+
+ if (token.type === 'eof') {
+ throw invalidEOF()
+ }
+
+ switch (token.value) {
+ case ',':
+ parseState = 'beforePropertyName';
+ return
+
+ case '}':
+ pop();
+ }
+
+ // This code is unreachable since it's handled by the lexState.
+ // throw invalidToken()
+ },
+
+ afterArrayValue: function afterArrayValue () {
+ // This code is unreachable since it's handled by the lexState.
+ // if (token.type !== 'punctuator') {
+ // throw invalidToken()
+ // }
+
+ if (token.type === 'eof') {
+ throw invalidEOF()
+ }
+
+ switch (token.value) {
+ case ',':
+ parseState = 'beforeArrayValue';
+ return
+
+ case ']':
+ pop();
+ }
+
+ // This code is unreachable since it's handled by the lexState.
+ // throw invalidToken()
+ },
+
+ end: function end () {
+ // This code is unreachable since it's handled by the lexState.
+ // if (token.type !== 'eof') {
+ // throw invalidToken()
+ // }
+ },
+ };
+
+ function push () {
+ var value;
+
+ switch (token.type) {
+ case 'punctuator':
+ switch (token.value) {
+ case '{':
+ value = {};
+ break
+
+ case '[':
+ value = [];
+ break
+ }
+
+ break
+
+ case 'null':
+ case 'boolean':
+ case 'numeric':
+ case 'string':
+ value = token.value;
+ break
+
+ // This code is unreachable.
+ // default:
+ // throw invalidToken()
+ }
+
+ if (root === undefined) {
+ root = value;
+ } else {
+ var parent = stack[stack.length - 1];
+ if (Array.isArray(parent)) {
+ parent.push(value);
+ } else {
+ parent[key] = value;
+ }
+ }
+
+ if (value !== null && typeof value === 'object') {
+ stack.push(value);
+
+ if (Array.isArray(value)) {
+ parseState = 'beforeArrayValue';
+ } else {
+ parseState = 'beforePropertyName';
+ }
+ } else {
+ var current = stack[stack.length - 1];
+ if (current == null) {
+ parseState = 'end';
+ } else if (Array.isArray(current)) {
+ parseState = 'afterArrayValue';
+ } else {
+ parseState = 'afterPropertyValue';
+ }
+ }
+ }
+
+ function pop () {
+ stack.pop();
+
+ var current = stack[stack.length - 1];
+ if (current == null) {
+ parseState = 'end';
+ } else if (Array.isArray(current)) {
+ parseState = 'afterArrayValue';
+ } else {
+ parseState = 'afterPropertyValue';
+ }
+ }
+
+ // This code is unreachable.
+ // function invalidParseState () {
+ // return new Error(`JSON5: invalid parse state '${parseState}'`)
+ // }
+
+ // This code is unreachable.
+ // function invalidLexState (state) {
+ // return new Error(`JSON5: invalid lex state '${state}'`)
+ // }
+
+ function invalidChar (c) {
+ if (c === undefined) {
+ return syntaxError(("JSON5: invalid end of input at " + line + ":" + column))
+ }
+
+ return syntaxError(("JSON5: invalid character '" + (formatChar(c)) + "' at " + line + ":" + column))
+ }
+
+ function invalidEOF () {
+ return syntaxError(("JSON5: invalid end of input at " + line + ":" + column))
+ }
+
+ // This code is unreachable.
+ // function invalidToken () {
+ // if (token.type === 'eof') {
+ // return syntaxError(`JSON5: invalid end of input at ${line}:${column}`)
+ // }
+
+ // const c = String.fromCodePoint(token.value.codePointAt(0))
+ // return syntaxError(`JSON5: invalid character '${formatChar(c)}' at ${line}:${column}`)
+ // }
+
+ function invalidIdentifier () {
+ column -= 5;
+ return syntaxError(("JSON5: invalid identifier character at " + line + ":" + column))
+ }
+
+ function separatorChar (c) {
+ console.warn(("JSON5: '" + (formatChar(c)) + "' in strings is not valid ECMAScript; consider escaping"));
+ }
+
+ function formatChar (c) {
+ var replacements = {
+ "'": "\\'",
+ '"': '\\"',
+ '\\': '\\\\',
+ '\b': '\\b',
+ '\f': '\\f',
+ '\n': '\\n',
+ '\r': '\\r',
+ '\t': '\\t',
+ '\v': '\\v',
+ '\0': '\\0',
+ '\u2028': '\\u2028',
+ '\u2029': '\\u2029',
+ };
+
+ if (replacements[c]) {
+ return replacements[c]
+ }
+
+ if (c < ' ') {
+ var hexString = c.charCodeAt(0).toString(16);
+ return '\\x' + ('00' + hexString).substring(hexString.length)
+ }
+
+ return c
+ }
+
+ function syntaxError (message) {
+ var err = new SyntaxError(message);
+ err.lineNumber = line;
+ err.columnNumber = column;
+ return err
+ }
+
+ var stringify = function stringify (value, replacer, space) {
+ var stack = [];
+ var indent = '';
+ var propertyList;
+ var replacerFunc;
+ var gap = '';
+ var quote;
+
+ if (
+ replacer != null &&
+ typeof replacer === 'object' &&
+ !Array.isArray(replacer)
+ ) {
+ space = replacer.space;
+ quote = replacer.quote;
+ replacer = replacer.replacer;
+ }
+
+ if (typeof replacer === 'function') {
+ replacerFunc = replacer;
+ } else if (Array.isArray(replacer)) {
+ propertyList = [];
+ for (var i = 0, list = replacer; i < list.length; i += 1) {
+ var v = list[i];
+
+ var item = (void 0);
+
+ if (typeof v === 'string') {
+ item = v;
+ } else if (
+ typeof v === 'number' ||
+ v instanceof String ||
+ v instanceof Number
+ ) {
+ item = String(v);
+ }
+
+ if (item !== undefined && propertyList.indexOf(item) < 0) {
+ propertyList.push(item);
+ }
+ }
+ }
+
+ if (space instanceof Number) {
+ space = Number(space);
+ } else if (space instanceof String) {
+ space = String(space);
+ }
+
+ if (typeof space === 'number') {
+ if (space > 0) {
+ space = Math.min(10, Math.floor(space));
+ gap = ' '.substr(0, space);
+ }
+ } else if (typeof space === 'string') {
+ gap = space.substr(0, 10);
+ }
+
+ return serializeProperty('', {'': value})
+
+ function serializeProperty (key, holder) {
+ var value = holder[key];
+ if (value != null) {
+ if (typeof value.toJSON5 === 'function') {
+ value = value.toJSON5(key);
+ } else if (typeof value.toJSON === 'function') {
+ value = value.toJSON(key);
+ }
+ }
+
+ if (replacerFunc) {
+ value = replacerFunc.call(holder, key, value);
+ }
+
+ if (value instanceof Number) {
+ value = Number(value);
+ } else if (value instanceof String) {
+ value = String(value);
+ } else if (value instanceof Boolean) {
+ value = value.valueOf();
+ }
+
+ switch (value) {
+ case null: return 'null'
+ case true: return 'true'
+ case false: return 'false'
+ }
+
+ if (typeof value === 'string') {
+ return quoteString(value, false)
+ }
+
+ if (typeof value === 'number') {
+ return String(value)
+ }
+
+ if (typeof value === 'object') {
+ return Array.isArray(value) ? serializeArray(value) : serializeObject(value)
+ }
+
+ return undefined
+ }
+
+ function quoteString (value) {
+ var quotes = {
+ "'": 0.1,
+ '"': 0.2,
+ };
+
+ var replacements = {
+ "'": "\\'",
+ '"': '\\"',
+ '\\': '\\\\',
+ '\b': '\\b',
+ '\f': '\\f',
+ '\n': '\\n',
+ '\r': '\\r',
+ '\t': '\\t',
+ '\v': '\\v',
+ '\0': '\\0',
+ '\u2028': '\\u2028',
+ '\u2029': '\\u2029',
+ };
+
+ var product = '';
+
+ for (var i = 0; i < value.length; i++) {
+ var c = value[i];
+ switch (c) {
+ case "'":
+ case '"':
+ quotes[c]++;
+ product += c;
+ continue
+
+ case '\0':
+ if (util.isDigit(value[i + 1])) {
+ product += '\\x00';
+ continue
+ }
+ }
+
+ if (replacements[c]) {
+ product += replacements[c];
+ continue
+ }
+
+ if (c < ' ') {
+ var hexString = c.charCodeAt(0).toString(16);
+ product += '\\x' + ('00' + hexString).substring(hexString.length);
+ continue
+ }
+
+ product += c;
+ }
+
+ var quoteChar = quote || Object.keys(quotes).reduce(function (a, b) { return (quotes[a] < quotes[b]) ? a : b; });
+
+ product = product.replace(new RegExp(quoteChar, 'g'), replacements[quoteChar]);
+
+ return quoteChar + product + quoteChar
+ }
+
+ function serializeObject (value) {
+ if (stack.indexOf(value) >= 0) {
+ throw TypeError('Converting circular structure to JSON5')
+ }
+
+ stack.push(value);
+
+ var stepback = indent;
+ indent = indent + gap;
+
+ var keys = propertyList || Object.keys(value);
+ var partial = [];
+ for (var i = 0, list = keys; i < list.length; i += 1) {
+ var key = list[i];
+
+ var propertyString = serializeProperty(key, value);
+ if (propertyString !== undefined) {
+ var member = serializeKey(key) + ':';
+ if (gap !== '') {
+ member += ' ';
+ }
+ member += propertyString;
+ partial.push(member);
+ }
+ }
+
+ var final;
+ if (partial.length === 0) {
+ final = '{}';
+ } else {
+ var properties;
+ if (gap === '') {
+ properties = partial.join(',');
+ final = '{' + properties + '}';
+ } else {
+ var separator = ',\n' + indent;
+ properties = partial.join(separator);
+ final = '{\n' + indent + properties + ',\n' + stepback + '}';
+ }
+ }
+
+ stack.pop();
+ indent = stepback;
+ return final
+ }
+
+ function serializeKey (key) {
+ if (key.length === 0) {
+ return quoteString(key, true)
+ }
+
+ var firstChar = String.fromCodePoint(key.codePointAt(0));
+ if (!util.isIdStartChar(firstChar)) {
+ return quoteString(key, true)
+ }
+
+ for (var i = firstChar.length; i < key.length; i++) {
+ if (!util.isIdContinueChar(String.fromCodePoint(key.codePointAt(i)))) {
+ return quoteString(key, true)
+ }
+ }
+
+ return key
+ }
+
+ function serializeArray (value) {
+ if (stack.indexOf(value) >= 0) {
+ throw TypeError('Converting circular structure to JSON5')
+ }
+
+ stack.push(value);
+
+ var stepback = indent;
+ indent = indent + gap;
+
+ var partial = [];
+ for (var i = 0; i < value.length; i++) {
+ var propertyString = serializeProperty(String(i), value);
+ partial.push((propertyString !== undefined) ? propertyString : 'null');
+ }
+
+ var final;
+ if (partial.length === 0) {
+ final = '[]';
+ } else {
+ if (gap === '') {
+ var properties = partial.join(',');
+ final = '[' + properties + ']';
+ } else {
+ var separator = ',\n' + indent;
+ var properties$1 = partial.join(separator);
+ final = '[\n' + indent + properties$1 + ',\n' + stepback + ']';
+ }
+ }
+
+ stack.pop();
+ indent = stepback;
+ return final
+ }
+ };
+
+ var JSON5 = {
+ parse: parse,
+ stringify: stringify,
+ };
+
+ var lib = JSON5;
+
+ var es5 = lib;
+
+ return es5;
+
+})));
diff --git a/devtools/shared/storage/vendor/moz.build b/devtools/shared/storage/vendor/moz.build
new file mode 100644
index 0000000000..5e8a82199a
--- /dev/null
+++ b/devtools/shared/storage/vendor/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+ 'stringvalidator',
+]
+
+DevToolsModules(
+ 'json5.js',
+) \ No newline at end of file
diff --git a/devtools/shared/storage/vendor/stringvalidator/UPDATING.md b/devtools/shared/storage/vendor/stringvalidator/UPDATING.md
new file mode 100644
index 0000000000..701715ed45
--- /dev/null
+++ b/devtools/shared/storage/vendor/stringvalidator/UPDATING.md
@@ -0,0 +1,142 @@
+# Updating this library
+
+1. Replace the contents of validator.js with the contents of https://github.com/chriso/validator.js/blob/master/validator.js.
+
+2. Add the following methods:
+ ```
+ // see http://isrc.ifpi.org/en/isrc-standard/code-syntax
+ var isrc = /^[A-Z]{2}[0-9A-Z]{3}\d{2}\d{5}$/;
+
+ function isISRC(str) {
+ assertString(str);
+ return isrc.test(str);
+ }
+ ```
+
+ ```
+ var cultureCodes = new Set(["ar", "bg", "ca", "zh-Hans", "cs", "da", "de",
+ "el", "en", "es", "fi", "fr", "he", "hu", "is", "it", "ja", "ko", "nl", "no",
+ "pl", "pt", "rm", "ro", "ru", "hr", "sk", "sq", "sv", "th", "tr", "ur", "id",
+ "uk", "be", "sl", "et", "lv", "lt", "tg", "fa", "vi", "hy", "az", "eu", "hsb",
+ "mk", "tn", "xh", "zu", "af", "ka", "fo", "hi", "mt", "se", "ga", "ms", "kk",
+ "ky", "sw", "tk", "uz", "tt", "bn", "pa", "gu", "or", "ta", "te", "kn", "ml",
+ "as", "mr", "sa", "mn", "bo", "cy", "km", "lo", "gl", "kok", "syr", "si", "iu",
+ "am", "tzm", "ne", "fy", "ps", "fil", "dv", "ha", "yo", "quz", "nso", "ba", "lb",
+ "kl", "ig", "ii", "arn", "moh", "br", "ug", "mi", "oc", "co", "gsw", "sah",
+ "qut", "rw", "wo", "prs", "gd", "ar-SA", "bg-BG", "ca-ES", "zh-TW", "cs-CZ",
+ "da-DK", "de-DE", "el-GR", "en-US", "fi-FI", "fr-FR", "he-IL", "hu-HU", "is-IS",
+ "it-IT", "ja-JP", "ko-KR", "nl-NL", "nb-NO", "pl-PL", "pt-BR", "rm-CH", "ro-RO",
+ "ru-RU", "hr-HR", "sk-SK", "sq-AL", "sv-SE", "th-TH", "tr-TR", "ur-PK", "id-ID",
+ "uk-UA", "be-BY", "sl-SI", "et-EE", "lv-LV", "lt-LT", "tg-Cyrl-TJ", "fa-IR",
+ "vi-VN", "hy-AM", "az-Latn-AZ", "eu-ES", "hsb-DE", "mk-MK", "tn-ZA", "xh-ZA",
+ "zu-ZA", "af-ZA", "ka-GE", "fo-FO", "hi-IN", "mt-MT", "se-NO", "ms-MY", "kk-KZ",
+ "ky-KG", "sw-KE", "tk-TM", "uz-Latn-UZ", "tt-RU", "bn-IN", "pa-IN", "gu-IN",
+ "or-IN", "ta-IN", "te-IN", "kn-IN", "ml-IN", "as-IN", "mr-IN", "sa-IN", "mn-MN",
+ "bo-CN", "cy-GB", "km-KH", "lo-LA", "gl-ES", "kok-IN", "syr-SY", "si-LK",
+ "iu-Cans-CA", "am-ET", "ne-NP", "fy-NL", "ps-AF", "fil-PH", "dv-MV",
+ "ha-Latn-NG", "yo-NG", "quz-BO", "nso-ZA", "ba-RU", "lb-LU", "kl-GL", "ig-NG",
+ "ii-CN", "arn-CL", "moh-CA", "br-FR", "ug-CN", "mi-NZ", "oc-FR", "co-FR",
+ "gsw-FR", "sah-RU", "qut-GT", "rw-RW", "wo-SN", "prs-AF", "gd-GB", "ar-IQ",
+ "zh-CN", "de-CH", "en-GB", "es-MX", "fr-BE", "it-CH", "nl-BE", "nn-NO", "pt-PT",
+ "sr-Latn-CS", "sv-FI", "az-Cyrl-AZ", "dsb-DE", "se-SE", "ga-IE", "ms-BN",
+ "uz-Cyrl-UZ", "bn-BD", "mn-Mong-CN", "iu-Latn-CA", "tzm-Latn-DZ", "quz-EC",
+ "ar-EG", "zh-HK", "de-AT", "en-AU", "es-ES", "fr-CA", "sr-Cyrl-CS", "se-FI",
+ "quz-PE", "ar-LY", "zh-SG", "de-LU", "en-CA", "es-GT", "fr-CH", "hr-BA",
+ "smj-NO", "ar-DZ", "zh-MO", "de-LI", "en-NZ", "es-CR", "fr-LU", "bs-Latn-BA",
+ "smj-SE", "ar-MA", "en-IE", "es-PA", "fr-MC", "sr-Latn-BA", "sma-NO", "ar-TN",
+ "en-ZA", "es-DO", "sr-Cyrl-BA", "sma-SE", "ar-OM", "en-JM", "es-VE",
+ "bs-Cyrl-BA", "sms-FI", "ar-YE", "en-029", "es-CO", "sr-Latn-RS", "smn-FI",
+ "ar-SY", "en-BZ", "es-PE", "sr-Cyrl-RS", "ar-JO", "en-TT", "es-AR", "sr-Latn-ME",
+ "ar-LB", "en-ZW", "es-EC", "sr-Cyrl-ME", "ar-KW", "en-PH", "es-CL", "ar-AE",
+ "es-UY", "ar-BH", "es-PY", "ar-QA", "en-IN", "es-BO", "en-MY", "es-SV", "en-SG",
+ "es-HN", "es-NI", "es-PR", "es-US", "bs-Cyrl", "bs-Latn", "sr-Cyrl", "sr-Latn",
+ "smn", "az-Cyrl", "sms", "zh", "nn", "bs", "az-Latn", "sma", "uz-Cyrl",
+ "mn-Cyrl", "iu-Cans", "zh-Hant", "nb", "sr", "tg-Cyrl", "dsb", "smj", "uz-Latn",
+ "mn-Mong", "iu-Latn", "tzm-Latn", "ha-Latn", "zh-CHS", "zh-CHT"]);
+
+ function isRFC5646(str) {
+ assertString(str);
+ // According to the spec these codes are case sensitive so we can check the
+ // string directly.
+ return cultureCodes.has(str);
+ }
+ ```
+
+ ```
+ 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
+
+ function isSemVer(str) {
+ assertString(str);
+ return semver.test(str);
+ }
+ ```
+
+ ```
+ 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
+
+ function isRGBColor(str) {
+ assertString(str);
+ return rgbcolor.test(str);
+ }
+ ```
+
+3. Add the following to the validator object towards the end of the file:
+ ```
+ isISRC: isISRC,
+ isRFC5646: isRFC5646,
+ isSemVer: isSemVer,
+ isRGBColor: isRGBColor,
+ ```
+
+4. Look for the phones array just above the isMobilePhone() method.
+
+ 1. Replace the en-HK regex with:
+ ```
+ // According to http://www.ofca.gov.hk/filemanager/ofca/en/content_311/no_plan.pdf
+ '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})$/,
+ ```
+ 2. Add:
+ ```
+ 'ko-KR': /^((\+?82)[ \-]?)?0?1([0|1|6|7|8|9]{1})[ \-]?\d{3,4}[ \-]?\d{4}$/,
+ 'lt-LT': /^(\+370|8)\d{8}$/,
+ ```
+
+5. Replace the isMobilePhone() method with:
+ ```
+ function isMobilePhone(str, locale) {
+ assertString(str);
+ if (locale in phones) {
+ return phones[locale].test(str);
+ } else if (locale === 'any') {
+ return !!Object.values(phones).find(phone => phone.test(str));
+ }
+ return false;
+ }
+ ```
+
+6. Delete the notBase64 regex and replace the isBase64 with:
+ ```
+ function isBase64(str) {
+ assertString(str);
+ // Value length must be divisible by 4.
+ var len = str.length;
+ if (!len || len % 4 !== 0) {
+ return false;
+ }
+
+ try {
+ if (atob(str)) {
+ return true;
+ }
+ } catch (e) {
+ return false;
+ }
+ }
+ ```
+
+7. Do not replace the test files as they have been converted to xpcshell tests. If there are new methods then add their tests to the `test_sanitizers.js` or `test_validators.js` files as appropriate.
+
+8. To test the library please run the following:
+ ```
+ ./mach xpcshell-test devtools/client/shared/vendor/stringvalidator/
+ ```
diff --git a/devtools/shared/storage/vendor/stringvalidator/moz.build b/devtools/shared/storage/vendor/stringvalidator/moz.build
new file mode 100644
index 0000000000..416242f722
--- /dev/null
+++ b/devtools/shared/storage/vendor/stringvalidator/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DIRS += [
+ 'util'
+]
+
+XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.toml']
+
+DevToolsModules(
+ 'validator.js',
+)
diff --git a/devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/head_stringvalidator.js b/devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/head_stringvalidator.js
new file mode 100644
index 0000000000..f1e25fe2ec
--- /dev/null
+++ b/devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/head_stringvalidator.js
@@ -0,0 +1,15 @@
+"use strict";
+
+const { require } = ChromeUtils.importESModule("resource://devtools/shared/loader/Loader.sys.mjs");
+
+this.validator = require("resource://devtools/shared/storage/vendor/stringvalidator/validator.js");
+
+function describe(suite, testFunc) {
+ info(`\n Test suite: ${suite}`.toUpperCase());
+ testFunc();
+}
+
+function it(description, testFunc) {
+ info(`\n - ${description}:\n`.toUpperCase());
+ testFunc();
+}
diff --git a/devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/test_sanitizers.js b/devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/test_sanitizers.js
new file mode 100644
index 0000000000..aa56d54c00
--- /dev/null
+++ b/devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/test_sanitizers.js
@@ -0,0 +1,419 @@
+/*
+ * Copyright 2013 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.md or:
+ * http://opensource.org/licenses/BSD-2-Clause
+ */
+
+ "use strict";
+
+function test(options) {
+ var args = options.args || [];
+
+ args.unshift(null);
+
+ Object.keys(options.expect).forEach(function (input) {
+ args[0] = input;
+
+ var result = validator[options.sanitizer](...args);
+ var expected = options.expect[input];
+ let argsString = args.join(', ');
+
+ if (isNaN(result) && !result.length && isNaN(expected)) {
+ ok(true, `validator.${options.sanitizer}(${argsString}) returned "${result}"`);
+ } else {
+ equal(result, expected, `validator.${options.sanitizer}("${argsString}") ` +
+ `returned "${result}"`);
+ }
+ });
+}
+
+function run_test() {
+ describe('Sanitizers', function () {
+ it('should sanitize boolean strings', function () {
+ test({
+ sanitizer: 'toBoolean',
+ expect: {
+ '0': false,
+ '': false,
+ '1': true,
+ 'true': true,
+ 'foobar': true,
+ ' ': true,
+ },
+ });
+ test({
+ sanitizer: 'toBoolean',
+ args: [true], // strict
+ expect: {
+ '0': false,
+ '': false,
+ '1': true,
+ 'true': true,
+ 'foobar': false,
+ ' ': false,
+ },
+ });
+ });
+
+ it('should trim whitespace', function () {
+ test({
+ sanitizer: 'trim',
+ expect: {
+ ' \r\n\tfoo \r\n\t ': 'foo',
+ ' \r': '',
+ },
+ });
+
+ test({
+ sanitizer: 'ltrim',
+ expect: {
+ ' \r\n\tfoo \r\n\t ': 'foo \r\n\t ',
+ ' \t \n': '',
+ },
+ });
+
+ test({
+ sanitizer: 'rtrim',
+ expect: {
+ ' \r\n\tfoo \r\n\t ': ' \r\n\tfoo',
+ ' \r\n \t': '',
+ },
+ });
+ });
+
+ it('should trim custom characters', function () {
+ test({
+ sanitizer: 'trim',
+ args: ['01'],
+ expect: { '010100201000': '2' },
+ });
+
+ test({
+ sanitizer: 'ltrim',
+ args: ['01'],
+ expect: { '010100201000': '201000' },
+ });
+
+ test({
+ sanitizer: 'rtrim',
+ args: ['01'],
+ expect: { '010100201000': '0101002' },
+ });
+ });
+
+ it('should convert strings to integers', function () {
+ test({
+ sanitizer: 'toInt',
+ expect: {
+ '3': 3,
+ ' 3 ': 3,
+ '2.4': 2,
+ 'foo': NaN,
+ },
+ });
+
+ test({
+ sanitizer: 'toInt',
+ args: [16],
+ expect: { 'ff': 255 },
+ });
+ });
+
+ it('should convert strings to floats', function () {
+ test({
+ sanitizer: 'toFloat',
+ expect: {
+ '2': 2.0,
+ '2.': 2.0,
+ '-2.5': -2.5,
+ '.5': 0.5,
+ 'foo': NaN,
+ },
+ });
+ });
+
+ it('should escape HTML', function () {
+ test({
+ sanitizer: 'escape',
+ expect: {
+ '<script> alert("xss&fun"); </script>':
+ '&lt;script&gt; alert(&quot;xss&amp;fun&quot;); &lt;&#x2F;script&gt;',
+
+ "<script> alert('xss&fun'); </script>":
+ '&lt;script&gt; alert(&#x27;xss&amp;fun&#x27;); &lt;&#x2F;script&gt;',
+
+ 'Backtick: `':
+ 'Backtick: &#96;',
+
+ 'Backslash: \\':
+ 'Backslash: &#x5C;',
+ },
+ });
+ });
+
+ it('should unescape HTML', function () {
+ test({
+ sanitizer: 'unescape',
+ expect: {
+ '&lt;script&gt; alert(&quot;xss&amp;fun&quot;); &lt;&#x2F;script&gt;':
+ '<script> alert("xss&fun"); </script>',
+
+ '&lt;script&gt; alert(&#x27;xss&amp;fun&#x27;); &lt;&#x2F;script&gt;':
+ "<script> alert('xss&fun'); </script>",
+
+ 'Backtick: &#96;':
+ 'Backtick: `',
+ },
+ });
+ });
+
+ it('should remove control characters (<32 and 127)', function () {
+ // Check basic functionality
+ test({
+ sanitizer: 'stripLow',
+ expect: {
+ 'foo\x00': 'foo',
+ '\x7Ffoo\x02': 'foo',
+ '\x01\x09': '',
+ 'foo\x0A\x0D': 'foo',
+ },
+ });
+ // Unicode safety
+ test({
+ sanitizer: 'stripLow',
+ expect: {
+ 'perch\u00e9': 'perch\u00e9',
+ '\u20ac': '\u20ac',
+ '\u2206\x0A': '\u2206',
+ '\ud83d\ude04': '\ud83d\ude04',
+ },
+ });
+ // Preserve newlines
+ test({
+ sanitizer: 'stripLow',
+ args: [true], // keep_new_lines
+ expect: {
+ 'foo\x0A\x0D': 'foo\x0A\x0D',
+ '\x03foo\x0A\x0D': 'foo\x0A\x0D',
+ },
+ });
+ });
+
+ it('should sanitize a string based on a whitelist', function () {
+ test({
+ sanitizer: 'whitelist',
+ args: ['abc'],
+ expect: {
+ 'abcdef': 'abc',
+ 'aaaaaaaaaabbbbbbbbbb': 'aaaaaaaaaabbbbbbbbbb',
+ 'a1b2c3': 'abc',
+ ' ': '',
+ },
+ });
+ });
+
+ it('should sanitize a string based on a blacklist', function () {
+ test({
+ sanitizer: 'blacklist',
+ args: ['abc'],
+ expect: {
+ 'abcdef': 'def',
+ 'aaaaaaaaaabbbbbbbbbb': '',
+ 'a1b2c3': '123',
+ ' ': ' ',
+ },
+ });
+ });
+
+ it('should normalize an email based on domain', function () {
+ test({
+ sanitizer: 'normalizeEmail',
+ expect: {
+ 'test@me.com': 'test@me.com',
+ 'some.name@gmail.com': 'somename@gmail.com',
+ 'some.name@googleMail.com': 'somename@gmail.com',
+ 'some.name+extension@gmail.com': 'somename@gmail.com',
+ 'some.Name+extension@GoogleMail.com': 'somename@gmail.com',
+ 'some.name.middleName+extension@gmail.com': 'somenamemiddlename@gmail.com',
+ 'some.name.middleName+extension@GoogleMail.com': 'somenamemiddlename@gmail.com',
+ 'some.name.midd.leNa.me.+extension@gmail.com': 'somenamemiddlename@gmail.com',
+ 'some.name.midd.leNa.me.+extension@GoogleMail.com': 'somenamemiddlename@gmail.com',
+ 'some.name+extension@unknown.com': 'some.name+extension@unknown.com',
+ 'hans@m端ller.com': 'hans@m端ller.com',
+ 'an invalid email address': false,
+ '': false,
+ '+extension@gmail.com': false,
+ '...@gmail.com': false,
+ '.+extension@googlemail.com': false,
+ '+a@icloud.com': false,
+ '+a@outlook.com': false,
+ '-a@yahoo.com': false,
+ 'some.name.midd..leNa...me...+extension@GoogleMail.com': 'somenamemiddlename@gmail.com',
+ '"foo@bar"@baz.com': '"foo@bar"@baz.com',
+ },
+ });
+
+ // Testing all_lowercase switch, should apply to domains not known to be case-insensitive
+ test({
+ sanitizer: 'normalizeEmail',
+ args: [{ all_lowercase: false }],
+ expect: {
+ 'test@foo.com': 'test@foo.com',
+ 'hans@m端ller.com': 'hans@m端ller.com',
+ 'test@FOO.COM': 'test@foo.com', // Hostname is always lowercased
+ 'blAH@x.com': 'blAH@x.com',
+ // In case of domains that are known to be case-insensitive, there's a separate switch
+ 'TEST@me.com': 'test@me.com',
+ 'TEST@ME.COM': 'test@me.com',
+ 'SOME.name@GMAIL.com': 'somename@gmail.com',
+ 'SOME.name.middleName+extension@GoogleMail.com': 'somenamemiddlename@gmail.com',
+ 'SOME.name.midd.leNa.me.+extension@gmail.com': 'somenamemiddlename@gmail.com',
+ 'SOME.name@gmail.com': 'somename@gmail.com',
+ 'SOME.name@yahoo.ca': 'some.name@yahoo.ca',
+ 'SOME.name@outlook.ie': 'some.name@outlook.ie',
+ 'SOME.name@me.com': 'some.name@me.com',
+ },
+ });
+
+ // Testing *_lowercase
+ test({
+ sanitizer: 'normalizeEmail',
+ args: [{
+ all_lowercase: false,
+ gmail_lowercase: false,
+ icloud_lowercase: false,
+ outlookdotcom_lowercase: false,
+ yahoo_lowercase: false,
+ }],
+ expect: {
+ 'TEST@FOO.COM': 'TEST@foo.com', // all_lowercase
+ 'ME@gMAil.com': 'ME@gmail.com', // gmail_lowercase
+ 'ME@me.COM': 'ME@me.com', // icloud_lowercase
+ 'ME@icloud.COM': 'ME@icloud.com', // icloud_lowercase
+ 'ME@outlook.COM': 'ME@outlook.com', // outlookdotcom_lowercase
+ 'JOHN@live.CA': 'JOHN@live.ca', // outlookdotcom_lowercase
+ 'ME@ymail.COM': 'ME@ymail.com', // yahoo_lowercase
+ },
+ });
+
+ // Testing all_lowercase
+ // Should overwrite all the *_lowercase options
+ test({
+ sanitizer: 'normalizeEmail',
+ args: [{
+ all_lowercase: true,
+ gmail_lowercase: false, // Overruled
+ icloud_lowercase: false, // Overruled
+ outlookdotcom_lowercase: false, // Overruled
+ yahoo_lowercase: false, // Overruled
+ }],
+ expect: {
+ 'TEST@FOO.COM': 'test@foo.com', // all_lowercase
+ 'ME@gMAil.com': 'me@gmail.com', // gmail_lowercase
+ 'ME@me.COM': 'me@me.com', // icloud_lowercase
+ 'ME@icloud.COM': 'me@icloud.com', // icloud_lowercase
+ 'ME@outlook.COM': 'me@outlook.com', // outlookdotcom_lowercase
+ 'JOHN@live.CA': 'john@live.ca', // outlookdotcom_lowercase
+ 'ME@ymail.COM': 'me@ymail.com', // yahoo_lowercase
+ },
+ });
+
+ // Testing *_remove_dots
+ test({
+ sanitizer: 'normalizeEmail',
+ args: [{
+ gmail_remove_dots: false,
+ }],
+ expect: {
+ 'SOME.name@GMAIL.com': 'some.name@gmail.com',
+ 'SOME.name+me@GMAIL.com': 'some.name@gmail.com',
+ 'my.self@foo.com': 'my.self@foo.com',
+ },
+ });
+
+ test({
+ sanitizer: 'normalizeEmail',
+ args: [{
+ gmail_remove_dots: true,
+ }],
+ expect: {
+ 'SOME.name@GMAIL.com': 'somename@gmail.com',
+ 'SOME.name+me@GMAIL.com': 'somename@gmail.com',
+ 'my.self@foo.com': 'my.self@foo.com',
+ },
+ });
+
+ // Testing *_remove_subaddress
+ test({
+ sanitizer: 'normalizeEmail',
+ args: [{
+ gmail_remove_subaddress: false,
+ icloud_remove_subaddress: false,
+ outlookdotcom_remove_subaddress: false,
+ yahoo_remove_subaddress: false, // Note Yahoo uses "-"
+ }],
+ expect: {
+ 'foo+bar@unknown.com': 'foo+bar@unknown.com',
+ 'foo+bar@gmail.com': 'foo+bar@gmail.com', // gmail_remove_subaddress
+ 'foo+bar@me.com': 'foo+bar@me.com', // icloud_remove_subaddress
+ 'foo+bar@icloud.com': 'foo+bar@icloud.com', // icloud_remove_subaddress
+ 'foo+bar@live.fr': 'foo+bar@live.fr', // outlookdotcom_remove_subaddress
+ 'foo+bar@hotmail.co.uk': 'foo+bar@hotmail.co.uk', // outlookdotcom_remove_subaddress
+ 'foo-bar@yahoo.com': 'foo-bar@yahoo.com', // yahoo_remove_subaddress
+ 'foo+bar@yahoo.com': 'foo+bar@yahoo.com', // yahoo_remove_subaddress
+ },
+ });
+
+ test({
+ sanitizer: 'normalizeEmail',
+ args: [{
+ gmail_remove_subaddress: true,
+ icloud_remove_subaddress: true,
+ outlookdotcom_remove_subaddress: true,
+ yahoo_remove_subaddress: true, // Note Yahoo uses "-"
+ }],
+ expect: {
+ 'foo+bar@unknown.com': 'foo+bar@unknown.com',
+ 'foo+bar@gmail.com': 'foo@gmail.com', // gmail_remove_subaddress
+ 'foo+bar@me.com': 'foo@me.com', // icloud_remove_subaddress
+ 'foo+bar@icloud.com': 'foo@icloud.com', // icloud_remove_subaddress
+ 'foo+bar@live.fr': 'foo@live.fr', // outlookdotcom_remove_subaddress
+ 'foo+bar@hotmail.co.uk': 'foo@hotmail.co.uk', // outlookdotcom_remove_subaddress
+ 'foo-bar@yahoo.com': 'foo@yahoo.com', // yahoo_remove_subaddress
+ 'foo+bar@yahoo.com': 'foo+bar@yahoo.com', // yahoo_remove_subaddress
+ },
+ });
+
+ // Testing gmail_convert_googlemaildotcom
+ test({
+ sanitizer: 'normalizeEmail',
+ args: [{
+ gmail_convert_googlemaildotcom: false,
+ }],
+ expect: {
+ 'SOME.name@GMAIL.com': 'somename@gmail.com',
+ 'SOME.name+me@GMAIL.com': 'somename@gmail.com',
+ 'SOME.name+me@googlemail.com': 'somename@googlemail.com',
+ 'SOME.name+me@googlemail.COM': 'somename@googlemail.com',
+ 'SOME.name+me@googlEmail.com': 'somename@googlemail.com',
+ 'my.self@foo.com': 'my.self@foo.com',
+ },
+ });
+
+ test({
+ sanitizer: 'normalizeEmail',
+ args: [{
+ gmail_convert_googlemaildotcom: true,
+ }],
+ expect: {
+ 'SOME.name@GMAIL.com': 'somename@gmail.com',
+ 'SOME.name+me@GMAIL.com': 'somename@gmail.com',
+ 'SOME.name+me@googlemail.com': 'somename@gmail.com',
+ 'SOME.name+me@googlemail.COM': 'somename@gmail.com',
+ 'SOME.name+me@googlEmail.com': 'somename@gmail.com',
+ 'my.self@foo.com': 'my.self@foo.com',
+ },
+ });
+ });
+ });
+}
diff --git a/devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/test_validators.js b/devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/test_validators.js
new file mode 100644
index 0000000000..eaf86bc7dd
--- /dev/null
+++ b/devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/test_validators.js
@@ -0,0 +1,3762 @@
+/*
+ * Copyright 2013 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.md or:
+ * http://opensource.org/licenses/BSD-2-Clause
+ */
+
+ "use strict";
+
+var assert = require("resource://devtools/shared/storage/vendor/stringvalidator/util/assert.js").assert;
+
+function test(options) {
+ var args = options.args || [];
+ args.unshift(null);
+ if (options.valid) {
+ options.valid.forEach(function (valid) {
+ args[0] = valid;
+
+ let argsString = args.join(', ');
+ ok(validator[options.validator](...args), `validator.${options.validator}(${argsString}) == true`);
+ });
+ }
+ if (options.invalid) {
+ options.invalid.forEach(function (invalid) {
+ args[0] = invalid;
+
+ let argsString = args.join(', ');
+ ok(!validator[options.validator](...args), `validator.${options.validator}(${argsString}) == false`);
+ });
+ }
+}
+
+function repeat(str, count) {
+ var result = '';
+ while (count--) {
+ result += str;
+ }
+ return result;
+}
+
+function random4digit() {
+ return Math.floor(1000 + (Math.random() * 9000));
+}
+
+function run_test() {
+ describe('Validators', function () {
+ it('should validate email addresses', function () {
+ test({
+ validator: 'isEmail',
+ valid: [
+ 'foo@bar.com',
+ 'x@x.au',
+ 'foo@bar.com.au',
+ 'foo+bar@bar.com',
+ 'hans.m端ller@test.com',
+ 'hans@m端ller.com',
+ 'test|123@m端ller.com',
+ 'test+ext@gmail.com',
+ 'some.name.midd.leNa.me.+extension@GoogleMail.com',
+ 'gmail...ignores...dots...@gmail.com',
+ '"foobar"@example.com',
+ '" foo m端ller "@example.com',
+ '"foo\\@bar"@example.com',
+ `${repeat('a', 64)}@${repeat('a', 252)}.com`,
+ ],
+ invalid: [
+ 'invalidemail@',
+ 'invalid.com',
+ '@invalid.com',
+ 'foo@bar.com.',
+ 'somename@gmail.com',
+ 'foo@bar.co.uk.',
+ 'z@co.c',
+ 'gmailgmailgmailgmailgmail@gmail.com',
+ `${repeat('a', 64)}@${repeat('a', 253)}.com`,
+ `${repeat('a', 65)}@${repeat('a', 252)}.com`,
+ ],
+ });
+ });
+
+ it('should validate email addresses without UTF8 characters in local part', function () {
+ test({
+ validator: 'isEmail',
+ args: [{ allow_utf8_local_part: false }],
+ valid: [
+ 'foo@bar.com',
+ 'x@x.au',
+ 'foo@bar.com.au',
+ 'foo+bar@bar.com',
+ 'hans@m端ller.com',
+ 'test|123@m端ller.com',
+ 'test+ext@gmail.com',
+ 'some.name.midd.leNa.me.+extension@GoogleMail.com',
+ '"foobar"@example.com',
+ '"foo\\@bar"@example.com',
+ '" foo bar "@example.com',
+ ],
+ invalid: [
+ 'invalidemail@',
+ 'invalid.com',
+ '@invalid.com',
+ 'foo@bar.com.',
+ 'foo@bar.co.uk.',
+ 'somename@gmail.com',
+ 'hans.m端ller@test.com',
+ 'z@co.c',
+ ],
+ });
+ });
+
+ it('should validate email addresses with display names', function () {
+ test({
+ validator: 'isEmail',
+ args: [{ allow_display_name: true }],
+ valid: [
+ 'foo@bar.com',
+ 'x@x.au',
+ 'foo@bar.com.au',
+ 'foo+bar@bar.com',
+ 'hans.m端ller@test.com',
+ 'hans@m端ller.com',
+ 'test|123@m端ller.com',
+ 'test+ext@gmail.com',
+ 'some.name.midd.leNa.me.+extension@GoogleMail.com',
+ 'Some Name <foo@bar.com>',
+ 'Some Name <x@x.au>',
+ 'Some Name <foo@bar.com.au>',
+ 'Some Name <foo+bar@bar.com>',
+ 'Some Name <hans.m端ller@test.com>',
+ 'Some Name <hans@m端ller.com>',
+ 'Some Name <test|123@m端ller.com>',
+ 'Some Name <test+ext@gmail.com>',
+ 'Some Name <some.name.midd.leNa.me.+extension@GoogleMail.com>',
+ 'Some Middle Name <some.name.midd.leNa.me.+extension@GoogleMail.com>',
+ 'Name <some.name.midd.leNa.me.+extension@GoogleMail.com>',
+ 'Name<some.name.midd.leNa.me.+extension@GoogleMail.com>',
+ ],
+ invalid: [
+ 'invalidemail@',
+ 'invalid.com',
+ '@invalid.com',
+ 'foo@bar.com.',
+ 'foo@bar.co.uk.',
+ 'Some Name <invalidemail@>',
+ 'Some Name <invalid.com>',
+ 'Some Name <@invalid.com>',
+ 'Some Name <foo@bar.com.>',
+ 'Some Name <foo@bar.co.uk.>',
+ 'Some Name foo@bar.co.uk.>',
+ 'Some Name <foo@bar.co.uk.',
+ 'Some Name < foo@bar.co.uk >',
+ 'Name foo@bar.co.uk',
+ ],
+ });
+ });
+
+ it('should validate email addresses with required display names', function () {
+ test({
+ validator: 'isEmail',
+ args: [{ require_display_name: true }],
+ valid: [
+ 'Some Name <foo@bar.com>',
+ 'Some Name <x@x.au>',
+ 'Some Name <foo@bar.com.au>',
+ 'Some Name <foo+bar@bar.com>',
+ 'Some Name <hans.m端ller@test.com>',
+ 'Some Name <hans@m端ller.com>',
+ 'Some Name <test|123@m端ller.com>',
+ 'Some Name <test+ext@gmail.com>',
+ 'Some Name <some.name.midd.leNa.me.+extension@GoogleMail.com>',
+ 'Some Middle Name <some.name.midd.leNa.me.+extension@GoogleMail.com>',
+ 'Name <some.name.midd.leNa.me.+extension@GoogleMail.com>',
+ 'Name<some.name.midd.leNa.me.+extension@GoogleMail.com>',
+ ],
+ invalid: [
+ 'some.name.midd.leNa.me.+extension@GoogleMail.com',
+ 'foo@bar.com',
+ 'x@x.au',
+ 'foo@bar.com.au',
+ 'foo+bar@bar.com',
+ 'hans.m端ller@test.com',
+ 'hans@m端ller.com',
+ 'test|123@m端ller.com',
+ 'test+ext@gmail.com',
+ 'invalidemail@',
+ 'invalid.com',
+ '@invalid.com',
+ 'foo@bar.com.',
+ 'foo@bar.co.uk.',
+ 'Some Name <invalidemail@>',
+ 'Some Name <invalid.com>',
+ 'Some Name <@invalid.com>',
+ 'Some Name <foo@bar.com.>',
+ 'Some Name <foo@bar.co.uk.>',
+ 'Some Name foo@bar.co.uk.>',
+ 'Some Name <foo@bar.co.uk.',
+ 'Some Name < foo@bar.co.uk >',
+ 'Name foo@bar.co.uk',
+ ],
+ });
+ });
+
+
+ it('should validate URLs', function () {
+ test({
+ validator: 'isURL',
+ valid: [
+ 'foobar.com',
+ 'www.foobar.com',
+ 'foobar.com/',
+ 'valid.au',
+ 'http://www.foobar.com/',
+ 'http://www.foobar.com:23/',
+ 'http://www.foobar.com:65535/',
+ 'http://www.foobar.com:5/',
+ 'https://www.foobar.com/',
+ 'ftp://www.foobar.com/',
+ 'http://www.foobar.com/~foobar',
+ 'http://user:pass@www.foobar.com/',
+ 'http://user:@www.foobar.com/',
+ 'http://127.0.0.1/',
+ 'http://10.0.0.0/',
+ 'http://189.123.14.13/',
+ 'http://duckduckgo.com/?q=%2F',
+ 'http://foobar.com/t$-_.+!*\'(),',
+ 'http://localhost:3000/',
+ 'http://foobar.com/?foo=bar#baz=qux',
+ 'http://foobar.com?foo=bar',
+ 'http://foobar.com#baz=qux',
+ 'http://www.xn--froschgrn-x9a.net/',
+ 'http://xn--froschgrn-x9a.com/',
+ 'http://foo--bar.com',
+ 'http://høyfjellet.no',
+ 'http://xn--j1aac5a4g.xn--j1amh',
+ 'http://xn------eddceddeftq7bvv7c4ke4c.xn--p1ai',
+ 'http://кулік.укр',
+ 'test.com?ref=http://test2.com',
+ 'http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html',
+ 'http://[1080:0:0:0:8:800:200C:417A]/index.html',
+ 'http://[3ffe:2a00:100:7031::1]',
+ 'http://[1080::8:800:200C:417A]/foo',
+ 'http://[::192.9.5.5]/ipng',
+ 'http://[::FFFF:129.144.52.38]:80/index.html',
+ 'http://[2010:836B:4179::836B:4179]',
+ ],
+ invalid: [
+ 'xyz://foobar.com',
+ 'invalid/',
+ 'invalid.x',
+ 'invalid.',
+ '.com',
+ 'http://com/',
+ 'http://',
+ 'http://300.0.0.1/',
+ 'mailto:foo@bar.com',
+ 'rtmp://foobar.com',
+ 'http://www.xn--.com/',
+ 'http://xn--.com/',
+ 'http://www.foobar.com:0/',
+ 'http://www.foobar.com:70000/',
+ 'http://www.foobar.com:99999/',
+ 'http://www.-foobar.com/',
+ 'http://www.foobar-.com/',
+ 'http://foobar/# lol',
+ 'http://foobar/? lol',
+ 'http://foobar/ lol/',
+ 'http://lol @foobar.com/',
+ 'http://lol:lol @foobar.com/',
+ 'http://lol:lol:lol@foobar.com/',
+ 'http://lol: @foobar.com/',
+ 'http://www.foo_bar.com/',
+ 'http://www.foobar.com/\t',
+ 'http://\n@www.foobar.com/',
+ '',
+ `http://foobar.com/${new Array(2083).join('f')}`,
+ 'http://*.foo.com',
+ '*.foo.com',
+ '!.foo.com',
+ 'http://example.com.',
+ 'http://localhost:61500this is an invalid url!!!!',
+ '////foobar.com',
+ 'http:////foobar.com',
+ 'https://',
+ 'https://example.com/foo/<script>alert(\'XSS\')</script>/',
+ ],
+ });
+ });
+
+ it('should validate URLs with custom protocols', function () {
+ test({
+ validator: 'isURL',
+ args: [{
+ protocols: ['rtmp'],
+ }],
+ valid: [
+ 'rtmp://foobar.com',
+ ],
+ invalid: [
+ 'http://foobar.com',
+ ],
+ });
+ });
+
+ it('should validate file URLs without a host', function () {
+ test({
+ validator: 'isURL',
+ args: [{
+ protocols: ['file'],
+ require_host: false,
+ }],
+ valid: [
+ 'file://localhost/foo.txt',
+ 'file:///foo.txt',
+ 'file:///',
+ ],
+ invalid: [
+ 'http://foobar.com',
+ ],
+ });
+ });
+
+ it('should validate URLs with any protocol', function () {
+ test({
+ validator: 'isURL',
+ args: [{
+ require_valid_protocol: false,
+ }],
+ valid: [
+ 'rtmp://foobar.com',
+ 'http://foobar.com',
+ 'test://foobar.com',
+ ],
+ invalid: [
+ 'mailto:test@example.com',
+ ],
+ });
+ });
+
+ it('should validate URLs with underscores', function () {
+ test({
+ validator: 'isURL',
+ args: [{
+ allow_underscores: true,
+ }],
+ valid: [
+ 'http://foo_bar.com',
+ 'http://pr.example_com.294.example.com/',
+ 'http://foo__bar.com',
+ ],
+ invalid: [],
+ });
+ });
+
+ it('should validate URLs that do not have a TLD', function () {
+ test({
+ validator: 'isURL',
+ args: [{
+ require_tld: false,
+ }],
+ valid: [
+ 'http://foobar.com/',
+ 'http://foobar/',
+ 'foobar/',
+ 'foobar',
+ ],
+ invalid: [],
+ });
+ });
+
+ it('should validate URLs with a trailing dot option', function () {
+ test({
+ validator: 'isURL',
+ args: [{
+ allow_trailing_dot: true,
+ require_tld: false,
+ }],
+ valid: [
+ 'http://example.com.',
+ 'foobar.',
+ ],
+ });
+ });
+
+ it('should validate protocol relative URLs', function () {
+ test({
+ validator: 'isURL',
+ args: [{
+ allow_protocol_relative_urls: true,
+ }],
+ valid: [
+ '//foobar.com',
+ 'http://foobar.com',
+ 'foobar.com',
+ ],
+ invalid: [
+ '://foobar.com',
+ '/foobar.com',
+ '////foobar.com',
+ 'http:////foobar.com',
+ ],
+ });
+ });
+
+ it('should not validate protocol relative URLs when require protocol is true', function () {
+ test({
+ validator: 'isURL',
+ args: [{
+ allow_protocol_relative_urls: true,
+ require_protocol: true,
+ }],
+ valid: [
+ 'http://foobar.com',
+ ],
+ invalid: [
+ '//foobar.com',
+ '://foobar.com',
+ '/foobar.com',
+ 'foobar.com',
+ ],
+ });
+ });
+
+ it('should let users specify whether URLs require a protocol', function () {
+ test({
+ validator: 'isURL',
+ args: [{
+ require_protocol: true,
+ }],
+ valid: [
+ 'http://foobar.com/',
+ 'http://localhost/',
+ ],
+ invalid: [
+ 'foobar.com',
+ 'foobar',
+ ],
+ });
+ });
+
+ it('should let users specify a host whitelist', function () {
+ test({
+ validator: 'isURL',
+ args: [{
+ host_whitelist: ['foo.com', 'bar.com'],
+ }],
+ valid: [
+ 'http://bar.com/',
+ 'http://foo.com/',
+ ],
+ invalid: [
+ 'http://foobar.com',
+ 'http://foo.bar.com/',
+ 'http://qux.com',
+ ],
+ });
+ });
+
+ it('should allow regular expressions in the host whitelist', function () {
+ test({
+ validator: 'isURL',
+ args: [{
+ host_whitelist: ['bar.com', 'foo.com', /\.foo\.com$/],
+ }],
+ valid: [
+ 'http://bar.com/',
+ 'http://foo.com/',
+ 'http://images.foo.com/',
+ 'http://cdn.foo.com/',
+ 'http://a.b.c.foo.com/',
+ ],
+ invalid: [
+ 'http://foobar.com',
+ 'http://foo.bar.com/',
+ 'http://qux.com',
+ ],
+ });
+ });
+
+ it('should let users specify a host blacklist', function () {
+ test({
+ validator: 'isURL',
+ args: [{
+ host_blacklist: ['foo.com', 'bar.com'],
+ }],
+ valid: [
+ 'http://foobar.com',
+ 'http://foo.bar.com/',
+ 'http://qux.com',
+ ],
+ invalid: [
+ 'http://bar.com/',
+ 'http://foo.com/',
+ ],
+ });
+ });
+
+ it('should allow regular expressions in the host blacklist', function () {
+ test({
+ validator: 'isURL',
+ args: [{
+ host_blacklist: ['bar.com', 'foo.com', /\.foo\.com$/],
+ }],
+ valid: [
+ 'http://foobar.com',
+ 'http://foo.bar.com/',
+ 'http://qux.com',
+ ],
+ invalid: [
+ 'http://bar.com/',
+ 'http://foo.com/',
+ 'http://images.foo.com/',
+ 'http://cdn.foo.com/',
+ 'http://a.b.c.foo.com/',
+ ],
+ });
+ });
+
+ it('should validate MAC addresses', function () {
+ test({
+ validator: 'isMACAddress',
+ valid: [
+ 'ab:ab:ab:ab:ab:ab',
+ 'FF:FF:FF:FF:FF:FF',
+ '01:02:03:04:05:ab',
+ '01:AB:03:04:05:06',
+ ],
+ invalid: [
+ 'abc',
+ '01:02:03:04:05',
+ '01:02:03:04::ab',
+ '1:2:3:4:5:6',
+ 'AB:CD:EF:GH:01:02',
+ ],
+ });
+ });
+
+ it('should validate IP addresses', function () {
+ test({
+ validator: 'isIP',
+ valid: [
+ '127.0.0.1',
+ '0.0.0.0',
+ '255.255.255.255',
+ '1.2.3.4',
+ '::1',
+ '2001:db8:0000:1:1:1:1:1',
+ '2001:41d0:2:a141::1',
+ '::ffff:127.0.0.1',
+ '::0000',
+ '0000::',
+ '1::',
+ '1111:1:1:1:1:1:1:1',
+ 'fe80::a6db:30ff:fe98:e946',
+ '::',
+ '::ffff:127.0.0.1',
+ '0:0:0:0:0:ffff:127.0.0.1',
+ ],
+ invalid: [
+ 'abc',
+ '256.0.0.0',
+ '0.0.0.256',
+ '26.0.0.256',
+ '0200.200.200.200',
+ '200.0200.200.200',
+ '200.200.0200.200',
+ '200.200.200.0200',
+ '::banana',
+ 'banana::',
+ '::1banana',
+ '::1::',
+ '1:',
+ ':1',
+ ':1:1:1::2',
+ '1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1',
+ '::11111',
+ '11111:1:1:1:1:1:1:1',
+ '2001:db8:0000:1:1:1:1::1',
+ '0:0:0:0:0:0:ffff:127.0.0.1',
+ '0:0:0:0:ffff:127.0.0.1',
+ ],
+ });
+ test({
+ validator: 'isIP',
+ args: [4],
+ valid: [
+ '127.0.0.1',
+ '0.0.0.0',
+ '255.255.255.255',
+ '1.2.3.4',
+ ],
+ invalid: [
+ '::1',
+ '2001:db8:0000:1:1:1:1:1',
+ '::ffff:127.0.0.1',
+ ],
+ });
+ test({
+ validator: 'isIP',
+ args: [6],
+ valid: [
+ '::1',
+ '2001:db8:0000:1:1:1:1:1',
+ '::ffff:127.0.0.1',
+ ],
+ invalid: [
+ '127.0.0.1',
+ '0.0.0.0',
+ '255.255.255.255',
+ '1.2.3.4',
+ '::ffff:287.0.0.1',
+ ],
+ });
+ test({
+ validator: 'isIP',
+ args: [10],
+ valid: [],
+ invalid: [
+ '127.0.0.1',
+ '0.0.0.0',
+ '255.255.255.255',
+ '1.2.3.4',
+ '::1',
+ '2001:db8:0000:1:1:1:1:1',
+ ],
+ });
+ });
+
+ it('should validate FQDN', function () {
+ test({
+ validator: 'isFQDN',
+ valid: [
+ 'domain.com',
+ 'dom.plato',
+ 'a.domain.co',
+ 'foo--bar.com',
+ 'xn--froschgrn-x9a.com',
+ 'rebecca.blackfriday',
+ ],
+ invalid: [
+ 'abc',
+ '256.0.0.0',
+ '_.com',
+ '*.some.com',
+ 's!ome.com',
+ 'domain.com/',
+ '/more.com',
+ ],
+ });
+ });
+ it('should validate FQDN with trailing dot option', function () {
+ test({
+ validator: 'isFQDN',
+ args: [
+ { allow_trailing_dot: true },
+ ],
+ valid: [
+ 'example.com.',
+ ],
+ });
+ });
+
+ it('should validate alpha strings', function () {
+ test({
+ validator: 'isAlpha',
+ valid: [
+ 'abc',
+ 'ABC',
+ 'FoObar',
+ ],
+ invalid: [
+ 'abc1',
+ ' foo ',
+ '',
+ 'ÄBC',
+ 'FÜübar',
+ 'Jön',
+ 'Heiß',
+ ],
+ });
+ });
+
+ it('should validate czech alpha strings', function () {
+ test({
+ validator: 'isAlpha',
+ args: ['cs-CZ'],
+ valid: [
+ 'žluťoučký',
+ 'KŮŇ',
+ 'Pěl',
+ 'Ďábelské',
+ 'ódy',
+ ],
+ invalid: [
+ 'ábc1',
+ ' fůj ',
+ '',
+ ],
+ });
+ });
+
+ it('should validate german alpha strings', function () {
+ test({
+ validator: 'isAlpha',
+ args: ['de-DE'],
+ valid: [
+ 'äbc',
+ 'ÄBC',
+ 'FöÖbär',
+ 'Heiß',
+ ],
+ invalid: [
+ 'äbc1',
+ ' föö ',
+ '',
+ ],
+ });
+ });
+
+ it('should validate hungarian alpha strings', function () {
+ test({
+ validator: 'isAlpha',
+ args: ['hu-HU'],
+ valid: [
+ 'árvíztűrőtükörfúrógép',
+ 'ÁRVÍZTŰRŐTÜKÖRFÚRÓGÉP',
+ ],
+ invalid: [
+ 'äbc1',
+ ' fäö ',
+ 'Heiß',
+ '',
+ ],
+ });
+ });
+
+ it('should validate arabic alpha strings', function () {
+ test({
+ validator: 'isAlpha',
+ args: ['ar'],
+ valid: [
+ 'أبت',
+ 'اَبِتَثّجً',
+ ],
+ invalid: [
+ '١٢٣أبت',
+ '١٢٣',
+ 'abc1',
+ ' foo ',
+ '',
+ 'ÄBC',
+ 'FÜübar',
+ 'Jön',
+ 'Heiß',
+ ],
+ });
+ });
+
+ it('should validate serbian cyrillic alpha strings', function () {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['sr-RS'],
+ valid: [
+ 'ШћжЂљЕ',
+ 'ЧПСТЋЏ',
+ ],
+ invalid: [
+ 'řiď ',
+ 'blé33!!',
+ 'föö!!',
+ ],
+ });
+ });
+
+ it('should validate serbian latin alpha strings', function () {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['sr-RS@latin'],
+ valid: [
+ 'ŠAabčšđćž',
+ 'ŠATROĆčđš',
+ ],
+ invalid: [
+ '12řiď ',
+ 'blé!!',
+ 'föö!2!',
+ ],
+ });
+ });
+
+
+ it('should validate defined arabic locales alpha strings', function () {
+ test({
+ validator: 'isAlpha',
+ args: ['ar-SY'],
+ valid: [
+ 'أبت',
+ 'اَبِتَثّجً',
+ ],
+ invalid: [
+ '١٢٣أبت',
+ '١٢٣',
+ 'abc1',
+ ' foo ',
+ '',
+ 'ÄBC',
+ 'FÜübar',
+ 'Jön',
+ 'Heiß',
+ ],
+ });
+ });
+
+ it('should validate turkish alpha strings', function () {
+ test({
+ validator: 'isAlpha',
+ args: ['tr-TR'],
+ valid: [
+ 'AİıÖöÇ窺ĞğÜüZ',
+ ],
+ invalid: [
+ '0AİıÖöÇ窺ĞğÜüZ1',
+ ' AİıÖöÇ窺ĞğÜüZ ',
+ 'abc1',
+ ' foo ',
+ '',
+ 'ÄBC',
+ 'Heiß',
+ ],
+ });
+ });
+
+ it('should validate urkrainian alpha strings', function () {
+ test({
+ validator: 'isAlpha',
+ args: ['uk-UA'],
+ valid: [
+ 'АБВГҐДЕЄЖЗИIЇЙКЛМНОПРСТУФХЦШЩЬЮЯ',
+ ],
+ invalid: [
+ '0AİıÖöÇ窺ĞğÜüZ1',
+ ' AİıÖöÇ窺ĞğÜüZ ',
+ 'abc1',
+ ' foo ',
+ '',
+ 'ÄBC',
+ 'Heiß',
+ 'ЫыЪъЭэ',
+ ],
+ });
+ });
+
+ it('should validate alphanumeric strings', function () {
+ test({
+ validator: 'isAlphanumeric',
+ valid: [
+ 'abc123',
+ 'ABC11',
+ ],
+ invalid: [
+ 'abc ',
+ 'foo!!',
+ 'ÄBC',
+ 'FÜübar',
+ 'Jön',
+ ],
+ });
+ });
+
+ it('should validate defined english aliases', function () {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['en-GB'],
+ valid: [
+ 'abc123',
+ 'ABC11',
+ ],
+ invalid: [
+ 'abc ',
+ 'foo!!',
+ 'ÄBC',
+ 'FÜübar',
+ 'Jön',
+ ],
+ });
+ });
+
+ it('should validate czech alphanumeric strings', function () {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['cs-CZ'],
+ valid: [
+ 'řiť123',
+ 'KŮŇ11',
+ ],
+ invalid: [
+ 'řiď ',
+ 'blé!!',
+ ],
+ });
+ });
+
+ it('should validate german alphanumeric strings', function () {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['de-DE'],
+ valid: [
+ 'äbc123',
+ 'ÄBC11',
+ ],
+ invalid: [
+ 'äca ',
+ 'föö!!',
+ ],
+ });
+ });
+
+ it('should validate hungarian alphanumeric strings', function () {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['hu-HU'],
+ valid: [
+ '0árvíztűrőtükörfúrógép123',
+ '0ÁRVÍZTŰRŐTÜKÖRFÚRÓGÉP123',
+ ],
+ invalid: [
+ '1időúr!',
+ 'äbc1',
+ ' fäö ',
+ 'Heiß!',
+ '',
+ ],
+ });
+ });
+
+ it('should validate spanish alphanumeric strings', function () {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['es-ES'],
+ valid: [
+ 'ábcó123',
+ 'ÁBCÓ11',
+ ],
+ invalid: [
+ 'äca ',
+ 'abcß',
+ 'föö!!',
+ ],
+ });
+ });
+
+ it('should validate arabic alphanumeric strings', function () {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['ar'],
+ valid: [
+ 'أبت123',
+ 'أبتَُِ١٢٣',
+ ],
+ invalid: [
+ 'äca ',
+ 'abcß',
+ 'föö!!',
+ ],
+ });
+ });
+
+ it('should validate defined arabic aliases', function () {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['ar-SY'],
+ valid: [
+ 'أبت123',
+ 'أبتَُِ١٢٣',
+ ],
+ invalid: [
+ 'abc ',
+ 'foo!!',
+ 'ÄBC',
+ 'FÜübar',
+ 'Jön',
+ ],
+ });
+ });
+
+ it('should validate serbian cyrillic alphanumeric strings', function () {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['sr-RS'],
+ valid: [
+ 'ШћжЂљЕ123',
+ 'ЧПСТ132ЋЏ',
+ ],
+ invalid: [
+ 'řiď ',
+ 'blé!!',
+ 'föö!!',
+ ],
+ });
+ });
+
+ it('should validate serbian latin alphanumeric strings', function () {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['sr-RS@latin'],
+ valid: [
+ 'ŠAabčšđćž123',
+ 'ŠATRO11Ćčđš',
+ ],
+ invalid: [
+ 'řiď ',
+ 'blé!!',
+ 'föö!!',
+ ],
+ });
+ });
+
+ it('should validate turkish alphanumeric strings', function () {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['tr-TR'],
+ valid: [
+ 'AİıÖöÇ窺ĞğÜüZ123',
+ ],
+ invalid: [
+ 'AİıÖöÇ窺ĞğÜüZ ',
+ 'foo!!',
+ 'ÄBC',
+ ],
+ });
+ });
+
+ it('should validate urkrainian alphanumeric strings', function () {
+ test({
+ validator: 'isAlphanumeric',
+ args: ['uk-UA'],
+ valid: [
+ 'АБВГҐДЕЄЖЗИIЇЙКЛМНОПРСТУФХЦШЩЬЮЯ123',
+ ],
+ invalid: [
+ 'éeoc ',
+ 'foo!!',
+ 'ÄBC',
+ 'ЫыЪъЭэ',
+ ],
+ });
+ });
+
+ it('should error on invalid locale', function () {
+ try {
+ validator.isAlphanumeric('abc123', 'in-INVALID');
+ assert(false);
+ } catch (err) {
+ assert(true);
+ }
+ });
+
+ it('should validate numeric strings', function () {
+ test({
+ validator: 'isNumeric',
+ valid: [
+ '123',
+ '00123',
+ '-00123',
+ '0',
+ '-0',
+ '+123',
+ ],
+ invalid: [
+ '123.123',
+ ' ',
+ '.',
+ ],
+ });
+ });
+
+ it('should validate RFC5646 strings', function () {
+ test({
+ validator: 'isRFC5646',
+ valid: [
+ 'en-US',
+ 'en-US',
+ ],
+ invalid: [
+ 'enus',
+ 'en-us',
+ 'xxx',
+ '',
+ ],
+ });
+ });
+
+ it('should validate RGBColor strings', function () {
+ test({
+ validator: 'isRGBColor',
+ valid: [
+ "rgb(0,31,255)",
+ "rgb(0, 31, 255)",
+ ],
+ invalid: [
+ "",
+ "rgb(1,349,275)",
+ "rgb(01,31,255)",
+ "rgb(0.6,31,255)",
+ "rgba(0,31,255)",
+ ],
+ });
+ });
+
+ it('should validate SemVer strings', function () {
+ test({
+ validator: 'isSemVer',
+ valid: [
+ "v1.0.0",
+ "1.0.0",
+ "1.0.0-alpha",
+ "1.0.0-alpha.1",
+ "1.0.0-0.3.7",
+ "1.0.0-x.7.z.92",
+ "1.0.0-alpha+001",
+ "1.0.0+20130313144700",
+ "1.0.0-beta+exp.sha.5114f85",
+ "1.0.0-beta+exp.sha.05114f85",
+ ],
+ invalid: [
+ "1.1.01",
+ "1.01.0",
+ "01.1.0",
+ "v1.1.01",
+ "v1.01.0",
+ "v01.1.0",
+ "1.0.0-0.03.7",
+ "1.0.0-00.3.7",
+ "1.0.0-+beta",
+ "1.0.0-b+-9+eta",
+ "v+1.8.0-b+-9+eta",
+ ],
+ });
+ });
+
+ it('should validate decimal numbers', function () {
+ test({
+ validator: 'isDecimal',
+ valid: [
+ '123',
+ '00123',
+ '-00123',
+ '0',
+ '-0',
+ '+123',
+ '0.01',
+ '.1',
+ '1.0',
+ '-.25',
+ '-0',
+ '0.0000000000001',
+ ],
+ invalid: [
+ '....',
+ ' ',
+ '',
+ '-',
+ '+',
+ '.',
+ '0.1a',
+ 'a',
+ '\n',
+ ],
+ });
+ });
+
+ it('should validate lowercase strings', function () {
+ test({
+ validator: 'isLowercase',
+ valid: [
+ 'abc',
+ 'abc123',
+ 'this is lowercase.',
+ 'tr竪s 端ber',
+ ],
+ invalid: [
+ 'fooBar',
+ '123A',
+ ],
+ });
+ });
+
+ it('should validate uppercase strings', function () {
+ test({
+ validator: 'isUppercase',
+ valid: [
+ 'ABC',
+ 'ABC123',
+ 'ALL CAPS IS FUN.',
+ ' .',
+ ],
+ invalid: [
+ 'fooBar',
+ '123abc',
+ ],
+ });
+ });
+
+ it('should validate integers', function () {
+ test({
+ validator: 'isInt',
+ valid: [
+ '13',
+ '123',
+ '0',
+ '123',
+ '-0',
+ '+1',
+ '01',
+ '-01',
+ '000',
+ ],
+ invalid: [
+ '100e10',
+ '123.123',
+ ' ',
+ '',
+ ],
+ });
+ test({
+ validator: 'isInt',
+ args: [{ allow_leading_zeroes: false }],
+ valid: [
+ '13',
+ '123',
+ '0',
+ '123',
+ '-0',
+ '+1',
+ ],
+ invalid: [
+ '01',
+ '-01',
+ '000',
+ '100e10',
+ '123.123',
+ ' ',
+ '',
+ ],
+ });
+ test({
+ validator: 'isInt',
+ args: [{ allow_leading_zeroes: true }],
+ valid: [
+ '13',
+ '123',
+ '0',
+ '123',
+ '-0',
+ '+1',
+ '01',
+ '-01',
+ '000',
+ '-000',
+ '+000',
+ ],
+ invalid: [
+ '100e10',
+ '123.123',
+ ' ',
+ '',
+ ],
+ });
+ test({
+ validator: 'isInt',
+ args: [{
+ min: 10,
+ }],
+ valid: [
+ '15',
+ '80',
+ '99',
+ ],
+ invalid: [
+ '9',
+ '6',
+ '3.2',
+ 'a',
+ ],
+ });
+ test({
+ validator: 'isInt',
+ args: [{
+ min: 10,
+ max: 15,
+ }],
+ valid: [
+ '15',
+ '11',
+ '13',
+ ],
+ invalid: [
+ '9',
+ '2',
+ '17',
+ '3.2',
+ '33',
+ 'a',
+ ],
+ });
+ test({
+ validator: 'isInt',
+ args: [{
+ gt: 10,
+ lt: 15,
+ }],
+ valid: [
+ '14',
+ '11',
+ '13',
+ ],
+ invalid: [
+ '10',
+ '15',
+ '17',
+ '3.2',
+ '33',
+ 'a',
+ ],
+ });
+ });
+
+ it('should validate floats', function () {
+ test({
+ validator: 'isFloat',
+ valid: [
+ '123',
+ '123.',
+ '123.123',
+ '-123.123',
+ '-0.123',
+ '+0.123',
+ '0.123',
+ '.0',
+ '-.123',
+ '+.123',
+ '01.123',
+ '-0.22250738585072011e-307',
+ ],
+ invalid: [
+ ' ',
+ '',
+ '.',
+ 'foo',
+ ],
+ });
+ test({
+ validator: 'isFloat',
+ args: [{
+ min: 3.7,
+ }],
+ valid: [
+ '3.888',
+ '3.92',
+ '4.5',
+ '50',
+ '3.7',
+ '3.71',
+ ],
+ invalid: [
+ '3.6',
+ '3.69',
+ '3',
+ '1.5',
+ 'a',
+ ],
+ });
+ test({
+ validator: 'isFloat',
+ args: [{
+ min: 0.1,
+ max: 1.0,
+ }],
+ valid: [
+ '0.1',
+ '1.0',
+ '0.15',
+ '0.33',
+ '0.57',
+ '0.7',
+ ],
+ invalid: [
+ '0',
+ '0.0',
+ 'a',
+ '1.3',
+ '0.05',
+ '5',
+ ],
+ });
+ test({
+ validator: 'isFloat',
+ args: [{
+ gt: -5.5,
+ lt: 10,
+ }],
+ valid: [
+ '9.9',
+ '1.0',
+ '0',
+ '-1',
+ '7',
+ '-5.4',
+ ],
+ invalid: [
+ '10',
+ '-5.5',
+ 'a',
+ '-20.3',
+ '20e3',
+ '10.00001',
+ ],
+ });
+ test({
+ validator: 'isFloat',
+ args: [{
+ min: -5.5,
+ max: 10,
+ gt: -5.5,
+ lt: 10,
+ }],
+ valid: [
+ '9.99999',
+ '-5.499999',
+ ],
+ invalid: [
+ '10',
+ '-5.5',
+ ],
+ });
+ });
+
+ it('should validate hexadecimal strings', function () {
+ test({
+ validator: 'isHexadecimal',
+ valid: [
+ 'deadBEEF',
+ 'ff0044',
+ ],
+ invalid: [
+ 'abcdefg',
+ '',
+ '..',
+ ],
+ });
+ });
+
+ it('should validate hexadecimal color strings', function () {
+ test({
+ validator: 'isHexColor',
+ valid: [
+ '#ff0034',
+ '#CCCCCC',
+ 'fff',
+ '#f00',
+ ],
+ invalid: [
+ '#ff',
+ 'fff0',
+ '#ff12FG',
+ ],
+ });
+ });
+
+ it('should validate ISRC code strings', function () {
+ test({
+ validator: 'isISRC',
+ valid: [
+ 'USAT29900609',
+ 'GBAYE6800011',
+ 'USRC15705223',
+ 'USCA29500702',
+ ],
+ invalid: [
+ 'USAT2990060',
+ 'SRC15705223',
+ 'US-CA29500702',
+ 'USARC15705223',
+ ],
+ });
+ });
+
+ it('should validate md5 strings', function () {
+ test({
+ validator: 'isMD5',
+ valid: [
+ 'd94f3f016ae679c3008de268209132f2',
+ '751adbc511ccbe8edf23d486fa4581cd',
+ '88dae00e614d8f24cfd5a8b3f8002e93',
+ '0bf1c35032a71a14c2f719e5a14c1e96',
+ ],
+ invalid: [
+ 'KYT0bf1c35032a71a14c2f719e5a14c1',
+ 'q94375dj93458w34',
+ '39485729348',
+ '%&FHKJFvk',
+ ],
+ });
+ });
+
+ it('should validate null strings', function () {
+ test({
+ validator: 'isEmpty',
+ valid: [
+ '',
+ ],
+ invalid: [
+ ' ',
+ 'foo',
+ '3',
+ ],
+ });
+ });
+
+ it('should validate strings against an expected value', function () {
+ test({ validator: 'equals', args: ['abc'], valid: ['abc'], invalid: ['Abc', '123'] });
+ });
+
+ it('should validate strings contain another string', function () {
+ test({
+ validator: 'contains',
+ args: ['foo'],
+ valid: ['foo', 'foobar', 'bazfoo'],
+ invalid: ['bar', 'fobar'],
+ });
+ });
+
+ it('should validate strings against a pattern', function () {
+ test({
+ validator: 'matches',
+ args: [/abc/],
+ valid: ['abc', 'abcdef', '123abc'],
+ invalid: ['acb', 'Abc'],
+ });
+ test({
+ validator: 'matches',
+ args: ['abc'],
+ valid: ['abc', 'abcdef', '123abc'],
+ invalid: ['acb', 'Abc'],
+ });
+ test({
+ validator: 'matches',
+ args: ['abc', 'i'],
+ valid: ['abc', 'abcdef', '123abc', 'AbC'],
+ invalid: ['acb'],
+ });
+ });
+
+ it('should validate strings by length (deprecated api)', function () {
+ test({
+ validator: 'isLength',
+ args: [2],
+ valid: ['abc', 'de', 'abcd'],
+ invalid: ['', 'a'],
+ });
+ test({
+ validator: 'isLength',
+ args: [2, 3],
+ valid: ['abc', 'de'],
+ invalid: ['', 'a', 'abcd'],
+ });
+ test({
+ validator: 'isLength',
+ args: [2, 3],
+ valid: ['干𩸽', '𠮷野家'],
+ invalid: ['', '𠀋', '千竈通り'],
+ });
+ test({
+ validator: 'isLength',
+ args: [0, 0],
+ valid: [''],
+ invalid: ['a', 'ab'],
+ });
+ });
+
+ it('should validate strings by byte length (deprecated api)', function () {
+ test({
+ validator: 'isByteLength',
+ args: [2],
+ valid: ['abc', 'de', 'abcd', 'gmail'],
+ invalid: ['', 'a'],
+ });
+ test({
+ validator: 'isByteLength',
+ args: [2, 3],
+ valid: ['abc', 'de', 'g'],
+ invalid: ['', 'a', 'abcd', 'gm'],
+ });
+ test({
+ validator: 'isByteLength',
+ args: [0, 0],
+ valid: [''],
+ invalid: ['g', 'a'],
+ });
+ });
+
+ it('should validate strings by length', function () {
+ test({
+ validator: 'isLength',
+ args: [{ min: 2 }],
+ valid: ['abc', 'de', 'abcd'],
+ invalid: ['', 'a'],
+ });
+ test({
+ validator: 'isLength',
+ args: [{ min: 2, max: 3 }],
+ valid: ['abc', 'de'],
+ invalid: ['', 'a', 'abcd'],
+ });
+ test({
+ validator: 'isLength',
+ args: [{ min: 2, max: 3 }],
+ valid: ['干𩸽', '𠮷野家'],
+ invalid: ['', '𠀋', '千竈通り'],
+ });
+ test({
+ validator: 'isLength',
+ args: [{ max: 3 }],
+ valid: ['abc', 'de', 'a', ''],
+ invalid: ['abcd'],
+ });
+ test({
+ validator: 'isLength',
+ args: [{ max: 0 }],
+ valid: [''],
+ invalid: ['a', 'ab'],
+ });
+ });
+
+ it('should validate strings by byte length', function () {
+ test({
+ validator: 'isByteLength',
+ args: [{ min: 2 }],
+ valid: ['abc', 'de', 'abcd', 'gmail'],
+ invalid: ['', 'a'],
+ });
+ test({
+ validator: 'isByteLength',
+ args: [{ min: 2, max: 3 }],
+ valid: ['abc', 'de', 'g'],
+ invalid: ['', 'a', 'abcd', 'gm'],
+ });
+ test({
+ validator: 'isByteLength',
+ args: [{ max: 3 }],
+ valid: ['abc', 'de', 'g', 'a', ''],
+ invalid: ['abcd', 'gm'],
+ });
+ test({
+ validator: 'isByteLength',
+ args: [{ max: 0 }],
+ valid: [''],
+ invalid: ['g', 'a'],
+ });
+ });
+
+ it('should validate UUIDs', function () {
+ test({
+ validator: 'isUUID',
+ valid: [
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A987FBC9-4BED-4078-8F07-9141BA07C9F3',
+ 'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+ ],
+ invalid: [
+ '',
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3xxx',
+ 'A987FBC94BED3078CF079141BA07C9F3',
+ '934859',
+ '987FBC9-4BED-3078-CF07A-9141BA07C9F3',
+ 'AAAAAAAA-1111-1111-AAAG-111111111111',
+ ],
+ });
+ test({
+ validator: 'isUUID',
+ args: [3],
+ valid: [
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ ],
+ invalid: [
+ '',
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ '934859',
+ 'AAAAAAAA-1111-1111-AAAG-111111111111',
+ 'A987FBC9-4BED-4078-8F07-9141BA07C9F3',
+ 'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+ ],
+ });
+ test({
+ validator: 'isUUID',
+ args: [4],
+ valid: [
+ '713ae7e3-cb32-45f9-adcb-7c4fa86b90c1',
+ '625e63f3-58f5-40b7-83a1-a72ad31acffb',
+ '57b73598-8764-4ad0-a76a-679bb6640eb1',
+ '9c858901-8a57-4791-81fe-4c455b099bc9',
+ ],
+ invalid: [
+ '',
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ '934859',
+ 'AAAAAAAA-1111-1111-AAAG-111111111111',
+ 'A987FBC9-4BED-5078-AF07-9141BA07C9F3',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ ],
+ });
+ test({
+ validator: 'isUUID',
+ args: [5],
+ valid: [
+ '987FBC97-4BED-5078-AF07-9141BA07C9F3',
+ '987FBC97-4BED-5078-BF07-9141BA07C9F3',
+ '987FBC97-4BED-5078-8F07-9141BA07C9F3',
+ '987FBC97-4BED-5078-9F07-9141BA07C9F3',
+ ],
+ invalid: [
+ '',
+ 'xxxA987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ '934859',
+ 'AAAAAAAA-1111-1111-AAAG-111111111111',
+ '9c858901-8a57-4791-81fe-4c455b099bc9',
+ 'A987FBC9-4BED-3078-CF07-9141BA07C9F3',
+ ],
+ });
+ });
+
+ it('should validate a string that is in another string or array', function () {
+ test({
+ validator: 'isIn',
+ args: ['foobar'],
+ valid: ['foo', 'bar', 'foobar', ''],
+ invalid: ['foobarbaz', 'barfoo'],
+ });
+ test({
+ validator: 'isIn',
+ args: [['foo', 'bar']],
+ valid: ['foo', 'bar'],
+ invalid: ['foobar', 'barfoo', ''],
+ });
+ test({
+ validator: 'isIn',
+ args: [['1', '2', '3']],
+ valid: ['1', '2', '3'],
+ invalid: ['4', ''],
+ });
+ test({ validator: 'isIn', invalid: ['foo', ''] });
+ });
+
+ it('should validate a string that is in another object', function () {
+ test({
+ validator: 'isIn',
+ args: [{ 'foo': 1, 'bar': 2, 'foobar': 3 }],
+ valid: ['foo', 'bar', 'foobar'],
+ invalid: ['foobarbaz', 'barfoo', ''],
+ });
+ test({
+ validator: 'isIn',
+ args: [{ 1: 3, 2: 0, 3: 1 }],
+ valid: ['1', '2', '3'],
+ invalid: ['4', ''],
+ });
+ });
+
+ it('should validate dates against a start date', function () {
+ test({
+ validator: 'isAfter',
+ args: ['2011-08-03'],
+ valid: ['2011-08-04', new Date(2011, 8, 10).toString()],
+ invalid: ['2010-07-02', '2011-08-03', new Date(0).toString(), 'foo'],
+ });
+ test({
+ validator: 'isAfter',
+ valid: ['2100-08-04', new Date(Date.now() + 86400000).toString()],
+ invalid: ['2010-07-02', new Date(0).toString()],
+ });
+ test({
+ validator: 'isAfter',
+ args: ['2011-08-03'],
+ valid: ['2015-09-17'],
+ invalid: ['invalid date'],
+ });
+ test({
+ validator: 'isAfter',
+ args: ['invalid date'],
+ invalid: ['invalid date', '2015-09-17'],
+ });
+ });
+
+ it('should validate dates against an end date', function () {
+ test({
+ validator: 'isBefore',
+ args: ['08/04/2011'],
+ valid: ['2010-07-02', '2010-08-04', new Date(0).toString()],
+ invalid: ['08/04/2011', new Date(2011, 9, 10).toString()],
+ });
+ test({
+ validator: 'isBefore',
+ args: [new Date(2011, 7, 4).toString()],
+ valid: ['2010-07-02', '2010-08-04', new Date(0).toString()],
+ invalid: ['08/04/2011', new Date(2011, 9, 10).toString()],
+ });
+ test({
+ validator: 'isBefore',
+ valid: [
+ '2000-08-04',
+ new Date(0).toString(),
+ new Date(Date.now() - 86400000).toString(),
+ ],
+ invalid: ['2100-07-02', new Date(2097, 10, 10).toString()],
+ });
+ test({
+ validator: 'isBefore',
+ args: ['2011-08-03'],
+ valid: ['1999-12-31'],
+ invalid: ['invalid date'],
+ });
+ test({
+ validator: 'isBefore',
+ args: ['invalid date'],
+ invalid: ['invalid date', '1999-12-31'],
+ });
+ });
+
+ it('should validate that integer strings are divisible by a number', function () {
+ test({
+ validator: 'isDivisibleBy',
+ args: [2],
+ valid: ['2', '4', '100', '1000'],
+ invalid: [
+ '1',
+ '2.5',
+ '101',
+ 'foo',
+ '',
+ ],
+ });
+ });
+
+ it('should validate credit cards', function () {
+ test({
+ validator: 'isCreditCard',
+ valid: [
+ '375556917985515',
+ '36050234196908',
+ '4716461583322103',
+ '4716-2210-5188-5662',
+ '4929 7226 5379 7141',
+ '5398228707871527',
+ '6283875070985593',
+ '6263892624162870',
+ '6234917882863855',
+ '6234698580215388',
+ '6226050967750613',
+ '6246281879460688',
+ '2222155765072228',
+ '2225855203075256',
+ '2720428011723762',
+ '2718760626256570',
+ ],
+ invalid: [
+ 'foo',
+ 'foo',
+ '5398228707871528',
+ '2718760626256571',
+ '2721465526338453',
+ '2220175103860763',
+ ],
+ });
+ });
+
+ it('should validate ISINs', function () {
+ test({
+ validator: 'isISIN',
+ valid: [
+ 'AU0000XVGZA3',
+ 'DE000BAY0017',
+ 'BE0003796134',
+ 'SG1G55870362',
+ 'GB0001411924',
+ 'DE000WCH8881',
+ 'PLLWBGD00016',
+ ],
+ invalid: [
+ 'DE000BAY0018',
+ 'PLLWBGD00019',
+ 'foo',
+ '5398228707871528',
+ ],
+ });
+ });
+
+ it('should validate ISBNs', function () {
+ test({
+ validator: 'isISBN',
+ args: [10],
+ valid: [
+ '3836221195', '3-8362-2119-5', '3 8362 2119 5',
+ '1617290858', '1-61729-085-8', '1 61729 085-8',
+ '0007269706', '0-00-726970-6', '0 00 726970 6',
+ '3423214120', '3-423-21412-0', '3 423 21412 0',
+ '340101319X', '3-401-01319-X', '3 401 01319 X',
+ ],
+ invalid: [
+ '3423214121', '3-423-21412-1', '3 423 21412 1',
+ '978-3836221191', '9783836221191',
+ '123456789a', 'foo', '',
+ ],
+ });
+ test({
+ validator: 'isISBN',
+ args: [13],
+ valid: [
+ '9783836221191', '978-3-8362-2119-1', '978 3 8362 2119 1',
+ '9783401013190', '978-3401013190', '978 3401013190',
+ '9784873113685', '978-4-87311-368-5', '978 4 87311 368 5',
+ ],
+ invalid: [
+ '9783836221190', '978-3-8362-2119-0', '978 3 8362 2119 0',
+ '3836221195', '3-8362-2119-5', '3 8362 2119 5',
+ '01234567890ab', 'foo', '',
+ ],
+ });
+ test({
+ validator: 'isISBN',
+ valid: [
+ '340101319X',
+ '9784873113685',
+ ],
+ invalid: [
+ '3423214121',
+ '9783836221190',
+ ],
+ });
+ test({
+ validator: 'isISBN',
+ args: ['foo'],
+ invalid: [
+ '340101319X',
+ '9784873113685',
+ ],
+ });
+ });
+
+ it('should validate ISSNs', function () {
+ test({
+ validator: 'isISSN',
+ valid: [
+ '0378-5955',
+ '0000-0000',
+ '2434-561X',
+ '2434-561x',
+ '01896016',
+ '20905076',
+ ],
+ invalid: [
+ '0378-5954',
+ '0000-0001',
+ '0378-123',
+ '037-1234',
+ '0',
+ '2434-561c',
+ '1684-5370',
+ '19960791',
+ '',
+ ],
+ });
+ test({
+ validator: 'isISSN',
+ args: [{ case_sensitive: true }],
+ valid: [
+ '2434-561X',
+ '2434561X',
+ '0378-5955',
+ '03785955',
+ ],
+ invalid: [
+ '2434-561x',
+ '2434561x',
+ ],
+ });
+ test({
+ validator: 'isISSN',
+ args: [{ require_hyphen: true }],
+ valid: [
+ '2434-561X',
+ '2434-561x',
+ '0378-5955',
+ ],
+ invalid: [
+ '2434561X',
+ '2434561x',
+ '03785955',
+ ],
+ });
+ test({
+ validator: 'isISSN',
+ args: [{ case_sensitive: true, require_hyphen: true }],
+ valid: [
+ '2434-561X',
+ '0378-5955',
+ ],
+ invalid: [
+ '2434-561x',
+ '2434561X',
+ '2434561x',
+ '03785955',
+ ],
+ });
+ });
+
+ it('should validate JSON', function () {
+ test({
+ validator: 'isJSON',
+ valid: [
+ '{ "key": "value" }',
+ '{}',
+ ],
+ invalid: [
+ '{ key: "value" }',
+ '{ \'key\': \'value\' }',
+ 'null',
+ '1234',
+ 'false',
+ '"nope"',
+ ],
+ });
+ });
+
+ it('should validate multibyte strings', function () {
+ test({
+ validator: 'isMultibyte',
+ valid: [
+ 'ひらがな・カタカナ、.漢字',
+ 'あいうえお foobar',
+ 'test@example.com',
+ '1234abcDExyz',
+ 'カタカナ',
+ '中文',
+ ],
+ invalid: [
+ 'abc',
+ 'abc123',
+ '<>@" *.',
+ ],
+ });
+ });
+
+ it('should validate ascii strings', function () {
+ test({
+ validator: 'isAscii',
+ valid: [
+ 'foobar',
+ '0987654321',
+ 'test@example.com',
+ '1234abcDEF',
+ ],
+ invalid: [
+ 'foobar',
+ 'xyz098',
+ '123456',
+ 'カタカナ',
+ ],
+ });
+ });
+
+ it('should validate full-width strings', function () {
+ test({
+ validator: 'isFullWidth',
+ valid: [
+ 'ひらがな・カタカナ、.漢字',
+ '3ー0 a@com',
+ 'Fカタカナ゙ᆲ',
+ 'Good=Parts',
+ ],
+ invalid: [
+ 'abc',
+ 'abc123',
+ '!"#$%&()<>/+=-_? ~^|.,@`{}[]',
+ ],
+ });
+ });
+
+ it('should validate half-width strings', function () {
+ test({
+ validator: 'isHalfWidth',
+ valid: [
+ '!"#$%&()<>/+=-_? ~^|.,@`{}[]',
+ 'l-btn_02--active',
+ 'abc123い',
+ 'カタカナ゙ᆲ←',
+ ],
+ invalid: [
+ 'あいうえお',
+ '0011',
+ ],
+ });
+ });
+
+ it('should validate variable-width strings', function () {
+ test({
+ validator: 'isVariableWidth',
+ valid: [
+ 'ひらがなカタカナ漢字ABCDE',
+ '3ー0123',
+ 'Fカタカナ゙ᆲ',
+ 'Good=Parts',
+ ],
+ invalid: [
+ 'abc',
+ 'abc123',
+ '!"#$%&()<>/+=-_? ~^|.,@`{}[]',
+ 'ひらがな・カタカナ、.漢字',
+ '123456',
+ 'カタカナ゙ᆲ',
+ ],
+ });
+ });
+
+ it('should validate surrogate pair strings', function () {
+ test({
+ validator: 'isSurrogatePair',
+ valid: [
+ '𠮷野𠮷',
+ '𩸽',
+ 'ABC千𥧄1-2-3',
+ ],
+ invalid: [
+ '吉野竈',
+ '鮪',
+ 'ABC1-2-3',
+ ],
+ });
+ });
+
+ it('should validate base64 strings', function () {
+ test({
+ validator: 'isBase64',
+ valid: [
+ 'Zg==',
+ 'Zm8=',
+ 'Zm9v',
+ 'Zm9vYg==',
+ 'Zm9vYmE=',
+ 'Zm9vYmFy',
+ 'TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4=',
+ 'Vml2YW11cyBmZXJtZW50dW0gc2VtcGVyIHBvcnRhLg==',
+ 'U3VzcGVuZGlzc2UgbGVjdHVzIGxlbw==',
+ 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuMPNS1Ufof9EW/M98FNw' +
+ 'UAKrwflsqVxaxQjBQnHQmiI7Vac40t8x7pIb8gLGV6wL7sBTJiPovJ0V7y7oc0Ye' +
+ 'rhKh0Rm4skP2z/jHwwZICgGzBvA0rH8xlhUiTvcwDCJ0kc+fh35hNt8srZQM4619' +
+ 'FTgB66Xmp4EtVyhpQV+t02g6NzK72oZI0vnAvqhpkxLeLiMCyrI416wHm5Tkukhx' +
+ 'QmcL2a6hNOyu0ixX/x2kSFXApEnVrJ+/IxGyfyw8kf4N2IZpW5nEP847lpfj0SZZ' +
+ 'Fwrd1mnfnDbYohX2zRptLy2ZUn06Qo9pkG5ntvFEPo9bfZeULtjYzIl6K8gJ2uGZ' +
+ 'HQIDAQAB',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ 'Zg=',
+ 'Z===',
+ 'Zm=8',
+ '=m9vYg==',
+ 'Zm9vYmFy====',
+ ],
+ });
+ });
+
+ it('should validate hex-encoded MongoDB ObjectId', function () {
+ test({
+ validator: 'isMongoId',
+ valid: [
+ '507f1f77bcf86cd799439011',
+ ],
+ invalid: [
+ '507f1f77bcf86cd7994390',
+ '507f1f77bcf86cd79943901z',
+ '',
+ '507f1f77bcf86cd799439011 ',
+ ],
+ });
+ });
+
+ it('should validate mobile phone number', function () {
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '0944549710',
+ '+963944549710',
+ '956654379',
+ '0944549710',
+ '0962655597',
+ ],
+ invalid: [
+ '12345',
+ '',
+ '+9639626626262',
+ '+963332210972',
+ '0114152198',
+ ],
+ args: ['ar-SY'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '0556578654',
+ '+966556578654',
+ '966556578654',
+ '596578654',
+ '572655597',
+ ],
+ invalid: [
+ '12345',
+ '',
+ '+9665626626262',
+ '+96633221097',
+ '0114152198',
+ ],
+ args: ['ar-SA'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '+420 123 456 789',
+ '+420 123456789',
+ '+420123456789',
+ '123 456 789',
+ '123456789',
+ ],
+ invalid: [
+ '',
+ '+42012345678',
+ ],
+ args: ['cs-CZ'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '+49 (0) 123 456 789',
+ '+49 (0) 123 456789',
+ '0123/4567890',
+ '+49 01234567890',
+ '01234567890',
+ ],
+ invalid: [
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ ],
+ args: ['de-DE'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '55-17-3332-2155',
+ '55-15-25661234',
+ '551223456789',
+ '01523456987',
+ '022995678947',
+ '+55-12-996551215',
+ ],
+ invalid: [
+ '+017-123456789',
+ '5501599623874',
+ '+55012962308',
+ '+55-015-1234-3214',
+ ],
+ args: ['pt-BR'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '15323456787',
+ '13523333233',
+ '13898728332',
+ '+086-13238234822',
+ '08613487234567',
+ '8617823492338',
+ '86-17823492338',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '010-38238383',
+ ],
+ args: ['zh-CN'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '0987123456',
+ '+886987123456',
+ '886987123456',
+ '+886-987123456',
+ '886-987123456',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '0-987123456',
+ ],
+ args: ['zh-TW'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ invalid: [
+ '15323456787',
+ '13523333233',
+ '13898728332',
+ '+086-13238234822',
+ '08613487234567',
+ '8617823492338',
+ '86-17823492338',
+ ],
+ args: ['en'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '0821231234',
+ '+27821231234',
+ '27821231234',
+ ],
+ invalid: [
+ '082123',
+ '08212312345',
+ '21821231234',
+ '+21821231234',
+ '+0821231234',
+ ],
+ args: ['en-ZA'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '61404111222',
+ '+61411222333',
+ '0417123456',
+ ],
+ invalid: [
+ '082123',
+ '08212312345',
+ '21821231234',
+ '+21821231234',
+ '+0821231234',
+ '04123456789',
+ ],
+ args: ['en-AU'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '91234567',
+ '9123-4567',
+ '61234567',
+ '51234567',
+ '+85291234567',
+ '+852-91234567',
+ '+852-9123-4567',
+ '852-91234567',
+ `8384-${random4digit()}`, // Spare - starting from 1.7.2017
+ `8580-${random4digit()}`, // Spare - starting from 1.7.2017
+ `7109-${random4digit()}`, // Spare - starting from 1.7.2017
+ `7114-${random4digit()}`, // Spare - starting from 1.7.2017
+ `6920-${random4digit()}`, // Spare - starting from 1.7.2017
+ `6280-${random4digit()}`, // Spare - starting from 1.7.2017
+ `5908-${random4digit()}`, // Spare - starting from 1.7.2017
+ `5152-${random4digit()}`, // Spare - starting from 1.7.2017
+ `4923-${random4digit()}`, // Spare - starting from 1.7.2017
+ ],
+ invalid: [
+ '999',
+ '+852-912345678',
+ '123456789',
+ '+852-1234-56789',
+ `9998${random4digit()}`, // Emergency Service (999)
+ `9111-${random4digit()}`, // Others (911)
+ `8521-${random4digit()}`, // Others (Conflict with country code)
+ `8009-${random4digit()}`, // 800 Fixed Services
+ `7211-${random4digit()}`, // Paging Service
+ `7119-${random4digit()}`, // Paging Service
+ `5850-${random4digit()}`, // Fixed Services
+ `5003${random4digit()}`, // Others (Special Services)
+ `4300-${random4digit()}`, // Others (Network Number)
+ `3830-${random4digit()}`, // Fixed Services
+ `2230-${random4digit()}`, // Fixed Services
+ `1833-${random4digit()}`, // Short Code (High traffic volume services)
+ `0060-${random4digit()}`, // Access Code (ETS, Prime IDD for Voice)
+ ],
+ args: ['en-HK'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '0612457898',
+ '+33612457898',
+ '33612457898',
+ '0712457898',
+ '+33712457898',
+ '33712457898',
+ ],
+ invalid: [
+ '061245789',
+ '06124578980',
+ '0112457898',
+ '0212457898',
+ '0312457898',
+ '0412457898',
+ '0512457898',
+ '0812457898',
+ '0912457898',
+ '+34612457898',
+ '+336124578980',
+ '+3361245789',
+ ],
+ args: ['fr-FR'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '+306944848966',
+ '6944848966',
+ '306944848966',
+ ],
+ invalid: [
+ '2102323234',
+ '+302646041461',
+ '120000000',
+ '20000000000',
+ '68129485729',
+ '6589394827',
+ '298RI89572',
+ ],
+ args: ['el-GR'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '447789345856',
+ '+447861235675',
+ '07888814488',
+ ],
+ invalid: [
+ '67699567',
+ '0773894868',
+ '077389f8688',
+ '+07888814488',
+ '0152456999',
+ '442073456754',
+ '+443003434751',
+ '05073456754',
+ '08001123123',
+ ],
+ args: ['en-GB'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '19876543210',
+ '8005552222',
+ '+15673628910',
+ ],
+ invalid: [
+ '564785',
+ '0123456789',
+ '1437439210',
+ '8009112340',
+ '+10345672645',
+ '11435213543',
+ '2436119753',
+ '16532116190',
+ ],
+ args: ['en-US'],
+ });
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '19876543210',
+ '8005552222',
+ '+15673628910',
+ ],
+ invalid: [
+ '564785',
+ '0123456789',
+ '1437439210',
+ '8009112340',
+ '+10345672645',
+ '11435213543',
+ '2436119753',
+ '16532116190',
+ ],
+ args: ['en-CA'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '0956684590',
+ '0966684590',
+ '0976684590',
+ '+260956684590',
+ '+260966684590',
+ '+260976684590',
+ '260976684590',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '010-38238383',
+ '966684590',
+ ],
+ args: ['en-ZM'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '+79676338855',
+ '79676338855',
+ '89676338855',
+ '9676338855',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '010-38238383',
+ '+9676338855',
+ '19676338855',
+ '6676338855',
+ '+99676338855',
+ ],
+ args: ['ru-RU'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '0640133338',
+ '063333133',
+ '0668888878',
+ '+381645678912',
+ '+381611314000',
+ '0655885010',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '010-38238383',
+ '+9676338855',
+ '19676338855',
+ '6676338855',
+ '+99676338855',
+ ],
+ args: ['sr-RS'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '+6427987035',
+ '642240512347',
+ '0293981646',
+ '029968425',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '+642956696123566',
+ '+02119620856',
+ '+9676338855',
+ '19676338855',
+ '6676338855',
+ '+99676338855',
+ ],
+ args: ['en-NZ'],
+ });
+
+ var norwegian = {
+ valid: [
+ '+4796338855',
+ '+4746338855',
+ '4796338855',
+ '4746338855',
+ '46338855',
+ '96338855',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '+4676338855',
+ '19676338855',
+ '+4726338855',
+ '4736338855',
+ '66338855',
+ ],
+ };
+ test({
+ validator: 'isMobilePhone',
+ valid: norwegian.valid,
+ invalid: norwegian.invalid,
+ args: ['nb-NO'],
+ });
+ test({
+ validator: 'isMobilePhone',
+ valid: norwegian.valid,
+ invalid: norwegian.invalid,
+ args: ['nn-NO'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '01636012403',
+ '+841636012403',
+ '1636012403',
+ '841636012403',
+ '+84999999999',
+ '84999999999',
+ '0999999999',
+ '999999999',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '010-38238383',
+ '260976684590',
+ ],
+ args: ['vi-VN'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '+34654789321',
+ '654789321',
+ '+34714789321',
+ '714789321',
+ '+34744789321',
+ '744789321',
+ ],
+ invalid: [
+ '12345',
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '+3465478932',
+ '65478932',
+ '+346547893210',
+ '6547893210',
+ '+34704789321',
+ '704789321',
+ '+34754789321',
+ '754789321',
+ ],
+ args: ['es-ES'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '+48512689767',
+ '+48 56 376 87 47',
+ '56 566 78 46',
+ '657562855',
+ '+48657562855',
+ '+48 887472765',
+ '+48 56 6572724',
+ '+48 67 621 5461',
+ '48 67 621 5461',
+ ],
+ invalid: [
+ '+48 67 621 5461',
+ '+55657562855',
+ '3454535',
+ 'teststring',
+ '',
+ '1800-88-8687',
+ '+6019-5830837',
+ '357562855',
+ ],
+ args: ['pl-PL'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '+358505557171',
+ '0455571',
+ '0505557171',
+ '358505557171',
+ '04412345',
+ '0457 123 45 67',
+ '+358457 123 45 67',
+ '+358 50 555 7171',
+ ],
+ invalid: [
+ '12345',
+ '',
+ '045557',
+ '045555717112312332423423421',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '010-38238383',
+ '+3-585-0555-7171',
+ '+9676338855',
+ '19676338855',
+ '6676338855',
+ '+99676338855',
+ '044123',
+ '019123456789012345678901',
+ ],
+ args: ['fi-FI'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '+60128228789',
+ '+60195830837',
+ '+6019-5830837',
+ '+6019-5830837',
+ '0128737867',
+ '01468987837',
+ '016-2838768',
+ '016 2838768',
+ ],
+ invalid: [
+ '12345',
+ '601238788657',
+ '088387675',
+ '16-2838768',
+ '032551433',
+ '6088-387888',
+ '088-261987',
+ '1800-88-8687',
+ '088-320000',
+ ],
+ args: ['ms-MY'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '0312345678',
+ '0721234567',
+ '09012345688',
+ '06 1234 5678',
+ '072 123 4567',
+ '0729 12 3456',
+ '07296 1 2345',
+ '072961 2345',
+ '090 1234 5678',
+ '03-1234-5678',
+ '+81312345678',
+ '+816-1234-5678',
+ '+8190-1234-5678',
+ ],
+ invalid: [
+ '12345',
+ '',
+ '045555717112312332423423421',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '+3-585-0555-7171',
+ '0 1234 5689',
+ '16 1234 5689',
+ '03_1234_5689',
+ ],
+ args: ['ja-JP'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '370 3175423',
+ '333202925',
+ '+39 310 7688449',
+ '+39 3339847632',
+ ],
+ invalid: [
+ '011 7387545',
+ '12345',
+ '+45 345 6782395',
+ ],
+ args: ['it-IT'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '0470123456',
+ '+32470123456',
+ '32470123456',
+ '021234567',
+ '+3221234567',
+ '3221234567',
+ ],
+ invalid: [
+ '12345',
+ '+3212345',
+ '3212345',
+ '04701234567',
+ '+3204701234567',
+ '3204701234567',
+ '0212345678',
+ '+320212345678',
+ '320212345678',
+ ],
+ args: ['fr-BE'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '0470123456',
+ '+32470123456',
+ '32470123456',
+ '021234567',
+ '+3221234567',
+ '3221234567',
+ ],
+ invalid: [
+ '12345',
+ '+3212345',
+ '3212345',
+ '04701234567',
+ '+3204701234567',
+ '3204701234567',
+ '0212345678',
+ '+320212345678',
+ '320212345678',
+ ],
+ args: ['nl-BE'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '+40740123456',
+ '+40 740123456',
+ '+40740 123 456',
+ '+40740.123.456',
+ '+40740-123-456',
+ '40740123456',
+ '40 740123456',
+ '40740 123 456',
+ '40740.123.456',
+ '40740-123-456',
+ '0740123456',
+ '0740/123456',
+ '0740 123 456',
+ '0740.123.456',
+ '0740-123-456',
+ ],
+ invalid: [
+ '',
+ 'Vml2YW11cyBmZXJtZtesting123',
+ '123456',
+ '740123456',
+ '+40640123456',
+ '+40210123456',
+ ],
+ args: ['ro-RO'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '0217123456',
+ '0811 778 998',
+ '089931236181900',
+ '622178878890',
+ '62811 778 998',
+ '62811778998',
+ '6289931236181900',
+ '6221 740123456',
+ '62899 740123456',
+ '62899 7401 2346',
+ '0341 8123456',
+ '0778 89800910',
+ '0741 123456',
+ '+6221740123456',
+ '+62811 778 998',
+ '+62811778998',
+ ],
+ invalid: [
+ '+65740 123 456',
+ '',
+ 'ASDFGJKLmZXJtZtesting123',
+ '123456',
+ '740123456',
+ '+65640123456',
+ '+64210123456',
+ ],
+ args: ['id-ID'],
+ });
+
+ test({
+ validator: 'isMobilePhone',
+ valid: [
+ '+37051234567',
+ '851234567',
+ ],
+ invalid: [
+ '+65740 123 456',
+ '',
+ 'ASDFGJKLmZXJtZtesting123',
+ '123456',
+ '740123456',
+ '+65640123456',
+ '+64210123456',
+ ],
+ args: ['lt-LT'],
+ });
+ });
+
+ it('should validate currency', function () {
+ test({
+ validator: 'isCurrency',
+ args: [
+ { },
+ '-$##,###.## (en-US, en-CA, en-AU, en-NZ, en-HK)',
+ ],
+ valid: [
+ '-$10,123.45',
+ '$10,123.45',
+ '$10123.45',
+ '10,123.45',
+ '10123.45',
+ '10,123',
+ '1,123,456',
+ '1123456',
+ '1.39',
+ '.03',
+ '0.10',
+ '$0.10',
+ '-$0.01',
+ '-$.99',
+ '$100,234,567.89',
+ '$10,123',
+ '10,123',
+ '-10123',
+ ],
+ invalid: [
+ '1.234',
+ '$1.1',
+ '$ 32.50',
+ '500$',
+ '.0001',
+ '$.001',
+ '$0.001',
+ '12,34.56',
+ '123456,123,123456',
+ '123,4',
+ ',123',
+ '$-,123',
+ '$',
+ '.',
+ ',',
+ '00',
+ '$-',
+ '$-,.',
+ '-',
+ '-$',
+ '',
+ '- $',
+ ],
+ });
+
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ require_symbol: true,
+ },
+ '-$##,###.## with $ required (en-US, en-CA, en-AU, en-NZ, en-HK)',
+ ],
+ valid: [
+ '-$10,123.45',
+ '$10,123.45',
+ '$10123.45',
+ '$10,123.45',
+ '$10,123',
+ '$1,123,456',
+ '$1123456',
+ '$1.39',
+ '$.03',
+ '$0.10',
+ '$0.10',
+ '-$0.01',
+ '-$.99',
+ '$100,234,567.89',
+ '$10,123',
+ '-$10123',
+ ],
+ invalid: [
+ '1.234',
+ '$1.234',
+ '1.1',
+ '$1.1',
+ '$ 32.50',
+ ' 32.50',
+ '500',
+ '10,123,456',
+ '.0001',
+ '$.001',
+ '$0.001',
+ '1,234.56',
+ '123456,123,123456',
+ '$123456,123,123456',
+ '123.4',
+ '$123.4',
+ ',123',
+ '$,123',
+ '$-,123',
+ '$',
+ '.',
+ '$.',
+ ',',
+ '$,',
+ '00',
+ '$00',
+ '$-',
+ '$-,.',
+ '-',
+ '-$',
+ '',
+ '$ ',
+ '- $',
+ ],
+ });
+
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ symbol: '¥',
+ negative_sign_before_digits: true,
+ },
+ '¥-##,###.## (zh-CN)',
+ ],
+ valid: [
+ '123,456.78',
+ '-123,456.78',
+ '¥6,954,231',
+ '¥-6,954,231',
+ '¥10.03',
+ '¥-10.03',
+ '10.03',
+ '1.39',
+ '.03',
+ '0.10',
+ '¥-10567.01',
+ '¥0.01',
+ '¥1,234,567.89',
+ '¥10,123',
+ '¥-10,123',
+ '¥-10,123.45',
+ '10,123',
+ '10123',
+ '¥-100',
+ ],
+ invalid: [
+ '1.234',
+ '¥1.1',
+ '5,00',
+ '.0001',
+ '¥.001',
+ '¥0.001',
+ '12,34.56',
+ '123456,123,123456',
+ '123 456',
+ ',123',
+ '¥-,123',
+ '',
+ ' ',
+ '¥',
+ '¥-',
+ '¥-,.',
+ '-',
+ '- ¥',
+ '-¥',
+ ],
+ });
+
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ symbol: '¥',
+ allow_negatives: false,
+ },
+ '¥##,###.## with no negatives (zh-CN)',
+ ],
+ valid: [
+ '123,456.78',
+ '¥6,954,231',
+ '¥10.03',
+ '10.03',
+ '1.39',
+ '.03',
+ '0.10',
+ '¥0.01',
+ '¥1,234,567.89',
+ '¥10,123',
+ '10,123',
+ '10123',
+ '¥100',
+ ],
+ invalid: [
+ '1.234',
+ '-123,456.78',
+ '¥-6,954,231',
+ '¥-10.03',
+ '¥-10567.01',
+ '¥1.1',
+ '¥-10,123',
+ '¥-10,123.45',
+ '5,00',
+ '¥-100',
+ '.0001',
+ '¥.001',
+ '¥-.001',
+ '¥0.001',
+ '12,34.56',
+ '123456,123,123456',
+ '123 456',
+ ',123',
+ '¥-,123',
+ '',
+ ' ',
+ '¥',
+ '¥-',
+ '¥-,.',
+ '-',
+ '- ¥',
+ '-¥',
+ ],
+ });
+
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ symbol: 'R',
+ negative_sign_before_digits: true,
+ thousands_separator: ' ',
+ decimal_separator: ',',
+ allow_negative_sign_placeholder: true,
+ },
+ 'R ## ###,## and R-10 123,25 (el-ZA)',
+ ],
+ valid: [
+ '123 456,78',
+ '-10 123',
+ 'R-10 123',
+ 'R 6 954 231',
+ 'R10,03',
+ '10,03',
+ '1,39',
+ ',03',
+ '0,10',
+ 'R10567,01',
+ 'R0,01',
+ 'R1 234 567,89',
+ 'R10 123',
+ 'R 10 123',
+ 'R 10123',
+ 'R-10123',
+ '10 123',
+ '10123',
+ ],
+ invalid: [
+ '1,234',
+ 'R -10123',
+ 'R- 10123',
+ 'R,1',
+ ',0001',
+ 'R,001',
+ 'R0,001',
+ '12 34,56',
+ '123456 123 123456',
+ ' 123',
+ '- 123',
+ '123 ',
+ '',
+ ' ',
+ 'R',
+ 'R- .1',
+ 'R-',
+ '-',
+ '-R 10123',
+ 'R00',
+ 'R -',
+ '-R',
+ ],
+ });
+
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ symbol: '€',
+ thousands_separator: '.',
+ decimal_separator: ',',
+ allow_space_after_symbol: true,
+ },
+ '-€ ##.###,## (it-IT)',
+ ],
+ valid: [
+ '123.456,78',
+ '-123.456,78',
+ '€6.954.231',
+ '-€6.954.231',
+ '€ 896.954.231',
+ '-€ 896.954.231',
+ '16.954.231',
+ '-16.954.231',
+ '€10,03',
+ '-€10,03',
+ '10,03',
+ '-10,03',
+ '-1,39',
+ ',03',
+ '0,10',
+ '-€10567,01',
+ '-€ 10567,01',
+ '€ 0,01',
+ '€1.234.567,89',
+ '€10.123',
+ '10.123',
+ '-€10.123',
+ '€ 10.123',
+ '€10.123',
+ '€ 10123',
+ '10.123',
+ '-10123',
+ ],
+ invalid: [
+ '1,234',
+ '€ 1,1',
+ '50#,50',
+ '123,@€ ',
+ '€€500',
+ ',0001',
+ '€ ,001',
+ '€0,001',
+ '12.34,56',
+ '123456.123.123456',
+ '€123€',
+ '',
+ ' ',
+ '€',
+ ' €',
+ '€ ',
+ '€€',
+ ' 123',
+ '- 123',
+ '.123',
+ '-€.123',
+ '123 ',
+ '€-',
+ '- €',
+ '€ - ',
+ '-',
+ '- ',
+ '-€',
+ ],
+ });
+
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ symbol: '€',
+ thousands_separator: '.',
+ symbol_after_digits: true,
+ decimal_separator: ',',
+ allow_space_after_digits: true,
+ },
+ '-##.###,## € (el-GR)',
+ ],
+ valid: [
+ '123.456,78',
+ '-123.456,78',
+ '6.954.231 €',
+ '-6.954.231 €',
+ '896.954.231',
+ '-896.954.231',
+ '16.954.231',
+ '-16.954.231',
+ '10,03€',
+ '-10,03€',
+ '10,03',
+ '-10,03',
+ '1,39',
+ ',03',
+ '-,03',
+ '-,03 €',
+ '-,03€',
+ '0,10',
+ '10567,01€',
+ '0,01 €',
+ '1.234.567,89€',
+ '10.123€',
+ '10.123',
+ '10.123€',
+ '10.123 €',
+ '10123 €',
+ '10.123',
+ '10123',
+ ],
+ invalid: [
+ '1,234',
+ '1,1 €',
+ ',0001',
+ ',001 €',
+ '0,001€',
+ '12.34,56',
+ '123456.123.123456',
+ '€123€',
+ '',
+ ' ',
+ '€',
+ ' €',
+ '€ ',
+ ' 123',
+ '- 123',
+ '.123',
+ '-.123€',
+ '-.123 €',
+ '123 ',
+ '-€',
+ '- €',
+ '-',
+ '- ',
+ ],
+ });
+
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ symbol: 'kr.',
+ negative_sign_before_digits: true,
+ thousands_separator: '.',
+ decimal_separator: ',',
+ allow_space_after_symbol: true,
+ },
+ 'kr. -##.###,## (da-DK)',
+ ],
+ valid: [
+ '123.456,78',
+ '-10.123',
+ 'kr. -10.123',
+ 'kr.-10.123',
+ 'kr. 6.954.231',
+ 'kr.10,03',
+ 'kr. -10,03',
+ '10,03',
+ '1,39',
+ ',03',
+ '0,10',
+ 'kr. 10567,01',
+ 'kr. 0,01',
+ 'kr. 1.234.567,89',
+ 'kr. -1.234.567,89',
+ '10.123',
+ 'kr. 10.123',
+ 'kr.10.123',
+ '10123',
+ '10.123',
+ 'kr.-10123',
+ ],
+ invalid: [
+ '1,234',
+ 'kr. -10123',
+ 'kr.,1',
+ ',0001',
+ 'kr. ,001',
+ 'kr.0,001',
+ '12.34,56',
+ '123456.123.123456',
+ '.123',
+ 'kr.-.123',
+ 'kr. -.123',
+ '- 123',
+ '123 ',
+ '',
+ ' ',
+ 'kr.',
+ ' kr.',
+ 'kr. ',
+ 'kr.-',
+ 'kr. -',
+ 'kr. - ',
+ ' - ',
+ '-',
+ '- kr.',
+ '-kr.',
+ ],
+ });
+
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ symbol: 'kr.',
+ allow_negatives: false,
+ negative_sign_before_digits: true,
+ thousands_separator: '.',
+ decimal_separator: ',',
+ allow_space_after_symbol: true,
+ },
+ 'kr. ##.###,## with no negatives (da-DK)',
+ ],
+ valid: [
+ '123.456,78',
+ '10.123',
+ 'kr. 10.123',
+ 'kr.10.123',
+ 'kr. 6.954.231',
+ 'kr.10,03',
+ 'kr. 10,03',
+ '10,03',
+ '1,39',
+ ',03',
+ '0,10',
+ 'kr. 10567,01',
+ 'kr. 0,01',
+ 'kr. 1.234.567,89',
+ 'kr.1.234.567,89',
+ '10.123',
+ 'kr. 10.123',
+ 'kr.10.123',
+ '10123',
+ '10.123',
+ 'kr.10123',
+ ],
+ invalid: [
+ '1,234',
+ '-10.123',
+ 'kr. -10.123',
+ 'kr. -1.234.567,89',
+ 'kr.-10123',
+ 'kr. -10123',
+ 'kr.-10.123',
+ 'kr. -10,03',
+ 'kr.,1',
+ ',0001',
+ 'kr. ,001',
+ 'kr.0,001',
+ '12.34,56',
+ '123456.123.123456',
+ '.123',
+ 'kr.-.123',
+ 'kr. -.123',
+ '- 123',
+ '123 ',
+ '',
+ ' ',
+ 'kr.',
+ ' kr.',
+ 'kr. ',
+ 'kr.-',
+ 'kr. -',
+ 'kr. - ',
+ ' - ',
+ '-',
+ '- kr.',
+ '-kr.',
+ ],
+ });
+
+ test({
+ validator: 'isCurrency',
+ args: [
+ {
+ parens_for_negatives: true,
+ },
+ '($##,###.##) (en-US, en-HK)',
+ ],
+ valid: [
+ '1,234',
+ '(1,234)',
+ '($6,954,231)',
+ '$10.03',
+ '(10.03)',
+ '($10.03)',
+ '1.39',
+ '.03',
+ '(.03)',
+ '($.03)',
+ '0.10',
+ '$10567.01',
+ '($0.01)',
+ '$1,234,567.89',
+ '$10,123',
+ '(10,123)',
+ '10123',
+ ],
+ invalid: [
+ '1.234',
+ '($1.1)',
+ '-$1.10',
+ '$ 32.50',
+ '500$',
+ '.0001',
+ '$.001',
+ '($0.001)',
+ '12,34.56',
+ '123456,123,123456',
+ '( 123)',
+ ',123',
+ '$-,123',
+ '',
+ ' ',
+ ' ',
+ ' ',
+ '$',
+ '$ ',
+ ' $',
+ ' 123',
+ '(123) ',
+ '.',
+ ',',
+ '00',
+ '$-',
+ '$ - ',
+ '$- ',
+ ' - ',
+ '-',
+ '- $',
+ '-$',
+ '()',
+ '( )',
+ '( -)',
+ '( - )',
+ '( - )',
+ '(-)',
+ '(-$)',
+ ],
+ });
+
+ test({
+ validator: 'isCurrency',
+ args: [
+ { allow_negatives: false },
+ '$##,###.## with no negatives (en-US, en-CA, en-AU, en-HK)',
+ ],
+ valid: [
+ '$10,123.45',
+ '$10123.45',
+ '10,123.45',
+ '10123.45',
+ '10,123',
+ '1,123,456',
+ '1123456',
+ '1.39',
+ '.03',
+ '0.10',
+ '$0.10',
+ '$100,234,567.89',
+ '$10,123',
+ '10,123',
+ ],
+ invalid: [
+ '1.234',
+ '-1.234',
+ '-10123',
+ '-$0.01',
+ '-$.99',
+ '$1.1',
+ '-$1.1',
+ '$ 32.50',
+ '500$',
+ '.0001',
+ '$.001',
+ '$0.001',
+ '12,34.56',
+ '123456,123,123456',
+ '-123456,123,123456',
+ '123,4',
+ ',123',
+ '$-,123',
+ '$',
+ '.',
+ ',',
+ '00',
+ '$-',
+ '$-,.',
+ '-',
+ '-$',
+ '',
+ '- $',
+ '-$10,123.45',
+ ],
+ });
+
+ test({
+ validator: 'isBoolean',
+ valid: [
+ 'true',
+ 'false',
+ '0',
+ '1',
+ ],
+ invalid: [
+ '1.0',
+ '0.0',
+ 'true ',
+ 'False',
+ 'True',
+ 'yes',
+ ],
+ });
+ });
+
+ it('should validate ISO 8601 dates', function () {
+ // from http://www.pelagodesign.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/
+ test({
+ validator: 'isISO8601',
+ valid: [
+ '2009-12T12:34',
+ '2009',
+ '2009-05-19',
+ '2009-05-19',
+ '20090519',
+ '2009123',
+ '2009-05',
+ '2009-123',
+ '2009-222',
+ '2009-001',
+ '2009-W01-1',
+ '2009-W51-1',
+ '2009-W511',
+ '2009-W33',
+ '2009W511',
+ '2009-05-19',
+ '2009-05-19 00:00',
+ '2009-05-19 14',
+ '2009-05-19 14:31',
+ '2009-05-19 14:39:22',
+ '2009-05-19T14:39Z',
+ '2009-W21-2',
+ '2009-W21-2T01:22',
+ '2009-139',
+ '2009-05-19 14:39:22-06:00',
+ '2009-05-19 14:39:22+0600',
+ '2009-05-19 14:39:22-01',
+ '20090621T0545Z',
+ '2007-04-06T00:00',
+ '2007-04-05T24:00',
+ '2010-02-18T16:23:48.5',
+ '2010-02-18T16:23:48,444',
+ '2010-02-18T16:23:48,3-06:00',
+ '2010-02-18T16:23.4',
+ '2010-02-18T16:23,25',
+ '2010-02-18T16:23.33+0600',
+ '2010-02-18T16.23334444',
+ '2010-02-18T16,2283',
+ '2009-05-19 143922.500',
+ '2009-05-19 1439,55',
+ ],
+ invalid: [
+ '200905',
+ '2009367',
+ '2009-',
+ '2007-04-05T24:50',
+ '2009-000',
+ '2009-M511',
+ '2009M511',
+ '2009-05-19T14a39r',
+ '2009-05-19T14:3924',
+ '2009-0519',
+ '2009-05-1914:39',
+ '2009-05-19 14:',
+ '2009-05-19r14:39',
+ '2009-05-19 14a39a22',
+ '200912-01',
+ '2009-05-19 14:39:22+06a00',
+ '2009-05-19 146922.500',
+ '2010-02-18T16.5:23.35:48',
+ '2010-02-18T16:23.35:48',
+ '2010-02-18T16:23.35:48.45',
+ '2009-05-19 14.5.44',
+ '2010-02-18T16:23.33.600',
+ '2010-02-18T16,25:23:48,444',
+ ],
+ });
+ });
+
+ it('should validate whitelisted characters', function () {
+ test({
+ validator: 'isWhitelisted',
+ args: ['abcdefghijklmnopqrstuvwxyz-'],
+ valid: ['foo', 'foobar', 'baz-foo'],
+ invalid: ['foo bar', 'fo.bar', 'türkçe'],
+ });
+ });
+
+ it('should error on non-string input', function () {
+ var empty = [undefined, null, [], NaN];
+ empty.forEach(function (item) {
+ assert.throws(validator.isEmpty.bind(null, item));
+ });
+ });
+
+ it('should validate dataURI', function () {
+ /* eslint-disable max-len */
+ test({
+ validator: 'isDataURI',
+ valid: [
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC',
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIBAMAAAA2IaO4AAAAFVBMVEXk5OTn5+ft7e319fX29vb5+fn///++GUmVAAAALUlEQVQIHWNICnYLZnALTgpmMGYIFWYIZTA2ZFAzTTFlSDFVMwVyQhmAwsYMAKDaBy0axX/iAAAAAElFTkSuQmCC',
+ ' data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIBAMAAAA2IaO4AAAAFVBMVEXk5OTn5+ft7e319fX29vb5+fn///++GUmVAAAALUlEQVQIHWNICnYLZnALTgpmMGYIFWYIZTA2ZFAzTTFlSDFVMwVyQhmAwsYMAKDaBy0axX/iAAAAAElFTkSuQmCC ',
+ 'data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%22%20height%3D%22100%22%3E%3Crect%20fill%3D%22%2300B1FF%22%20width%3D%22100%22%20height%3D%22100%22%2F%3E%3C%2Fsvg%3E',
+ 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIj48cmVjdCBmaWxsPSIjMDBCMUZGIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIvPjwvc3ZnPg==',
+ ' data:,Hello%2C%20World!',
+ ' data:,Hello World!',
+ ' data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D',
+ ' data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E',
+ 'data:,A%20brief%20note',
+ 'data:text/html;charset=US-ASCII,%3Ch1%3EHello!%3C%2Fh1%3E',
+ ],
+ invalid: [
+ 'dataxbase64',
+ 'data:HelloWorld',
+ 'data:text/html;charset=,%3Ch1%3EHello!%3C%2Fh1%3E',
+ 'data:text/html;charset,%3Ch1%3EHello!%3C%2Fh1%3E', 'data:base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC',
+ '',
+ 'http://wikipedia.org',
+ 'base64',
+ 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC',
+ ],
+ });
+ /* eslint-enable max-len */
+ });
+ });
+}
diff --git a/devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/xpcshell.toml b/devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/xpcshell.toml
new file mode 100644
index 0000000000..d877b73bff
--- /dev/null
+++ b/devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/xpcshell.toml
@@ -0,0 +1,9 @@
+[DEFAULT]
+tags = "devtools"
+head = "head_stringvalidator.js"
+firefox-appdir = "browser"
+skip-if = ["os == 'android'"]
+
+["test_sanitizers.js"]
+
+["test_validators.js"]
diff --git a/devtools/shared/storage/vendor/stringvalidator/util/assert.js b/devtools/shared/storage/vendor/stringvalidator/util/assert.js
new file mode 100644
index 0000000000..3bd796aebd
--- /dev/null
+++ b/devtools/shared/storage/vendor/stringvalidator/util/assert.js
@@ -0,0 +1,215 @@
+// // based on node assert, original notice:
+// // NB: The URL to the CommonJS spec is kept just for tradition.
+// // node-assert has evolved a lot since then, both in API and behavior.
+//
+// // http://wiki.commonjs.org/wiki/Unit_Testing/1.0
+// //
+// // THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
+// //
+// // Originally from narwhal.js (http://narwhaljs.org)
+// // Copyright (c) 2009 Thomas Robinson <280north.com>
+// //
+// // Permission is hereby granted, free of charge, to any person obtaining a copy
+// // of this software and associated documentation files (the 'Software'), to
+// // deal in the Software without restriction, including without limitation the
+// // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// // sell copies of the Software, and to permit persons to whom the Software is
+// // furnished to do so, subject to the following conditions:
+// //
+// // The above copyright notice and this permission notice shall be included in
+// // all copies or substantial portions of the Software.
+// //
+// // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// // AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+"use strict";
+
+var regex = /\s*function\s+([^\(\s]*)\s*/;
+
+var functionsHaveNames = (function () {
+ return function foo() {}.name === "foo";
+}());
+
+function assert(value, message) {
+ if (!value) {
+ fail(value, true, message, "==", assert.ok);
+ }
+}
+
+assert.equal = function equal(actual, expected, message) {
+ if (actual != expected) {
+ fail(actual, expected, message, "==", assert.equal);
+ }
+};
+
+assert.throws = function (block, error, message) {
+ _throws(true, block, error, message);
+};
+
+function _throws(shouldThrow, block, expected, message) {
+ var actual;
+
+ if (typeof block !== "function") {
+ throw new TypeError(`"block" argument must be a function`);
+ }
+
+ if (typeof expected === "string") {
+ message = expected;
+ expected = null;
+ }
+
+ actual = _tryBlock(block);
+
+ message = (expected?.name ? " (" + expected.name + ")." : ".") +
+ (message ? " " + message : ".");
+
+ if (shouldThrow && !actual) {
+ fail(actual, expected, "Missing expected exception" + message);
+ }
+
+ var userProvidedMessage = typeof message === "string";
+ var isUnwantedException = !shouldThrow && isError(actual);
+ var isUnexpectedException = !shouldThrow && actual && !expected;
+
+ if ((isUnwantedException &&
+ userProvidedMessage &&
+ expectedException(actual, expected)) ||
+ isUnexpectedException) {
+ fail(actual, expected, "Got unwanted exception" + message);
+ }
+
+ if ((shouldThrow && actual && expected &&
+ !expectedException(actual, expected)) || (!shouldThrow && actual)) {
+ throw actual;
+ }
+}
+
+function fail(actual, expected, message, operator, stackStartFunction) {
+ throw new assert.AssertionError({
+ message: message,
+ actual: actual,
+ expected: expected,
+ operator: operator,
+ stackStartFunction: stackStartFunction
+ });
+}
+
+assert.fail = fail;
+
+assert.AssertionError = function AssertionError(options) {
+ this.name = "AssertionError";
+ this.actual = options.actual;
+ this.expected = options.expected;
+ this.operator = options.operator;
+ if (options.message) {
+ this.message = options.message;
+ this.generatedMessage = false;
+ } else {
+ this.message = getMessage(this);
+ this.generatedMessage = true;
+ }
+ var stackStartFunction = options.stackStartFunction || fail;
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, stackStartFunction);
+ } else {
+ // non v8 browsers so we can have a stacktrace
+ var err = new Error();
+ if (err.stack) {
+ var out = err.stack;
+
+ // try to strip useless frames
+ var fn_name = getName(stackStartFunction);
+ var idx = out.indexOf("\n" + fn_name);
+ if (idx >= 0) {
+ // once we have located the function frame
+ // we need to strip out everything before it (and its line)
+ var next_line = out.indexOf("\n", idx + 1);
+ out = out.substring(next_line + 1);
+ }
+
+ this.stack = out;
+ }
+ }
+};
+
+function expectedException(actual, expected) {
+ if (!actual || !expected) {
+ return false;
+ }
+
+ if (Object.prototype.toString.call(expected) == "[object RegExp]") {
+ return expected.test(actual);
+ }
+
+ try {
+ if (actual instanceof expected) {
+ return true;
+ }
+ } catch (e) {
+ // Ignore. The instanceof check doesn"t work for arrow functions.
+ }
+
+ if (Error.isPrototypeOf(expected)) {
+ return false;
+ }
+
+ return expected.call({}, actual) === true;
+}
+
+function _tryBlock(block) {
+ var error;
+ try {
+ block();
+ } catch (e) {
+ error = e;
+ }
+ return error;
+}
+
+function isError(obj) {
+ return obj instanceof Error;
+}
+
+function isFunction(value) {
+ return typeof value === "function";
+}
+
+function getMessage(self) {
+ return truncate(inspect(self.actual), 128) + " " +
+ self.operator + " " +
+ truncate(inspect(self.expected), 128);
+}
+
+function getName(func) {
+ if (!isFunction(func)) {
+ return null;
+ }
+ if (functionsHaveNames) {
+ return func.name;
+ }
+ var str = func.toString();
+ var match = str.match(regex);
+ return match?.[1];
+}
+
+function truncate(s, n) {
+ if (typeof s === "string") {
+ return s.length < n ? s : s.slice(0, n);
+ }
+ return s;
+}
+
+function inspect(something) {
+ if (functionsHaveNames || !isFunction(something)) {
+ throw new Error(something);
+ }
+ var rawname = getName(something);
+ var name = rawname ? ": " + rawname : "";
+ return "[Function" + name + "]";
+}
+
+exports.assert = assert;
diff --git a/devtools/shared/storage/vendor/stringvalidator/util/moz.build b/devtools/shared/storage/vendor/stringvalidator/util/moz.build
new file mode 100644
index 0000000000..cc8fbcfab1
--- /dev/null
+++ b/devtools/shared/storage/vendor/stringvalidator/util/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+ 'assert.js',
+)
diff --git a/devtools/shared/storage/vendor/stringvalidator/validator.js b/devtools/shared/storage/vendor/stringvalidator/validator.js
new file mode 100644
index 0000000000..e11886599b
--- /dev/null
+++ b/devtools/shared/storage/vendor/stringvalidator/validator.js
@@ -0,0 +1,1489 @@
+/*
+ * Copyright (c) 2016 Chris O"Hara <cohara87@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * NOTE: This utility is derived from https://github.com/chriso/validator.js but it is
+ * **NOT** the same as the original. We have made the following changes:
+ * - Changed mocha tests to xpcshell based tests.
+ * - Merged the following pull requests:
+ * - [isMobileNumber] Added Lithuanian number pattern #667
+ * - Hongkong mobile number #665
+ * - Added option to validate any phone locale #663
+ * - Added validation for ISRC strings #660
+ * - Added isRFC5646 for rfc 5646 #572
+ * - Added isSemVer for version numbers.
+ * - Added isRGBColor for RGB colors.
+ *
+ * UPDATING: PLEASE FOLLOW THE INSTRUCTIONS INSIDE UPDATING.md
+ */
+
+"use strict";
+
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global.validator = factory());
+}(this, function () { 'use strict';
+
+ function assertString(input) {
+ if (typeof input !== 'string') {
+ throw new TypeError('This library (validator.js) validates strings only');
+ }
+ }
+
+ function toDate(date) {
+ assertString(date);
+ date = Date.parse(date);
+ return !isNaN(date) ? new Date(date) : null;
+ }
+
+ function toFloat(str) {
+ assertString(str);
+ return parseFloat(str);
+ }
+
+ function toInt(str, radix) {
+ assertString(str);
+ return parseInt(str, radix || 10);
+ }
+
+ function toBoolean(str, strict) {
+ assertString(str);
+ if (strict) {
+ return str === '1' || str === 'true';
+ }
+ return str !== '0' && str !== 'false' && str !== '';
+ }
+
+ function equals(str, comparison) {
+ assertString(str);
+ return str === comparison;
+ }
+
+ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
+ return typeof obj;
+ } : function (obj) {
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+ };
+
+ var asyncGenerator = function () {
+ function AwaitValue(value) {
+ this.value = value;
+ }
+
+ function AsyncGenerator(gen) {
+ var front, back;
+
+ function send(key, arg) {
+ return new Promise(function (resolve, reject) {
+ var request = {
+ key: key,
+ arg: arg,
+ resolve: resolve,
+ reject: reject,
+ next: null
+ };
+
+ if (back) {
+ back = back.next = request;
+ } else {
+ front = back = request;
+ resume(key, arg);
+ }
+ });
+ }
+
+ function resume(key, arg) {
+ try {
+ var result = gen[key](arg);
+ var value = result.value;
+
+ if (value instanceof AwaitValue) {
+ Promise.resolve(value.value).then(function (arg) {
+ resume("next", arg);
+ }, function (arg) {
+ resume("throw", arg);
+ });
+ } else {
+ settle(result.done ? "return" : "normal", result.value);
+ }
+ } catch (err) {
+ settle("throw", err);
+ }
+ }
+
+ function settle(type, value) {
+ switch (type) {
+ case "return":
+ front.resolve({
+ value: value,
+ done: true
+ });
+ break;
+
+ case "throw":
+ front.reject(value);
+ break;
+
+ default:
+ front.resolve({
+ value: value,
+ done: false
+ });
+ break;
+ }
+
+ front = front.next;
+
+ if (front) {
+ resume(front.key, front.arg);
+ } else {
+ back = null;
+ }
+ }
+
+ this._invoke = send;
+
+ if (typeof gen.return !== "function") {
+ this.return = undefined;
+ }
+ }
+
+ if (typeof Symbol === "function" && Symbol.asyncIterator) {
+ AsyncGenerator.prototype[Symbol.asyncIterator] = function () {
+ return this;
+ };
+ }
+
+ AsyncGenerator.prototype.next = function (arg) {
+ return this._invoke("next", arg);
+ };
+
+ AsyncGenerator.prototype.throw = function (arg) {
+ return this._invoke("throw", arg);
+ };
+
+ AsyncGenerator.prototype.return = function (arg) {
+ return this._invoke("return", arg);
+ };
+
+ return {
+ wrap: function (fn) {
+ return function () {
+ return new AsyncGenerator(fn.apply(this, arguments));
+ };
+ },
+ await: function (value) {
+ return new AwaitValue(value);
+ }
+ };
+ }();
+
+ function toString(input) {
+ if ((typeof input === 'undefined' ? 'undefined' : _typeof(input)) === 'object' && input !== null) {
+ if (typeof input.toString === 'function') {
+ input = input.toString();
+ } else {
+ input = '[object Object]';
+ }
+ } else if (input === null || typeof input === 'undefined' || isNaN(input) && !input.length) {
+ input = '';
+ }
+ return String(input);
+ }
+
+ function contains(str, elem) {
+ assertString(str);
+ return str.indexOf(toString(elem)) >= 0;
+ }
+
+ function matches(str, pattern, modifiers) {
+ assertString(str);
+ if (Object.prototype.toString.call(pattern) !== '[object RegExp]') {
+ pattern = new RegExp(pattern, modifiers);
+ }
+ return pattern.test(str);
+ }
+
+ function merge() {
+ var obj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var defaults = arguments[1];
+
+ for (var key in defaults) {
+ if (typeof obj[key] === 'undefined') {
+ obj[key] = defaults[key];
+ }
+ }
+ return obj;
+ }
+
+ /* eslint-disable prefer-rest-params */
+ function isByteLength(str, options) {
+ assertString(str);
+ var min = void 0;
+ var max = void 0;
+ if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') {
+ min = options.min || 0;
+ max = options.max;
+ } else {
+ // backwards compatibility: isByteLength(str, min [, max])
+ min = arguments[1];
+ max = arguments[2];
+ }
+ var len = encodeURI(str).split(/%..|./).length - 1;
+ return len >= min && (typeof max === 'undefined' || len <= max);
+ }
+
+ var default_fqdn_options = {
+ require_tld: true,
+ allow_underscores: false,
+ allow_trailing_dot: false
+ };
+
+ function isFDQN(str, options) {
+ assertString(str);
+ options = merge(options, default_fqdn_options);
+
+ /* Remove the optional trailing dot before checking validity */
+ if (options.allow_trailing_dot && str[str.length - 1] === '.') {
+ str = str.substring(0, str.length - 1);
+ }
+ var parts = str.split('.');
+ if (options.require_tld) {
+ var tld = parts.pop();
+ if (!parts.length || !/^([a-z\u00a1-\uffff]{2,}|xn[a-z0-9-]{2,})$/i.test(tld)) {
+ return false;
+ }
+ }
+ for (var part, i = 0; i < parts.length; i++) {
+ part = parts[i];
+ if (options.allow_underscores) {
+ part = part.replace(/_/g, '');
+ }
+ if (!/^[a-z\u00a1-\uffff0-9-]+$/i.test(part)) {
+ return false;
+ }
+ if (/[\uff01-\uff5e]/.test(part)) {
+ // disallow full-width chars
+ return false;
+ }
+ if (part[0] === '-' || part[part.length - 1] === '-') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ var default_email_options = {
+ allow_display_name: false,
+ require_display_name: false,
+ allow_utf8_local_part: true,
+ require_tld: true
+ };
+
+ /* eslint-disable max-len */
+ /* eslint-disable no-control-regex */
+ var displayName = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\.\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\.\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF\s]*<(.+)>$/i;
+ var emailUserPart = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~]+$/i;
+ var quotedEmailUser = /^([\s\x01-\x08\x0b\x0c\x0e-\x1f\x7f\x21\x23-\x5b\x5d-\x7e]|(\\[\x01-\x09\x0b\x0c\x0d-\x7f]))*$/i;
+ var emailUserUtf8Part = /^[a-z\d!#\$%&'\*\+\-\/=\?\^_`{\|}~\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+$/i;
+ 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;
+ /* eslint-enable max-len */
+ /* eslint-enable no-control-regex */
+
+ function isEmail(str, options) {
+ assertString(str);
+ options = merge(options, default_email_options);
+
+ if (options.require_display_name || options.allow_display_name) {
+ var display_email = str.match(displayName);
+ if (display_email) {
+ str = display_email[1];
+ } else if (options.require_display_name) {
+ return false;
+ }
+ }
+
+ var parts = str.split('@');
+ var domain = parts.pop();
+ var user = parts.join('@');
+
+ var lower_domain = domain.toLowerCase();
+ if (lower_domain === 'gmail.com' || lower_domain === 'googlemail.com') {
+ user = user.replace(/\./g, '').toLowerCase();
+ }
+
+ if (!isByteLength(user, { max: 64 }) || !isByteLength(domain, { max: 256 })) {
+ return false;
+ }
+
+ if (!isFDQN(domain, { require_tld: options.require_tld })) {
+ return false;
+ }
+
+ if (user[0] === '"') {
+ user = user.slice(1, user.length - 1);
+ return options.allow_utf8_local_part ? quotedEmailUserUtf8.test(user) : quotedEmailUser.test(user);
+ }
+
+ var pattern = options.allow_utf8_local_part ? emailUserUtf8Part : emailUserPart;
+
+ var user_parts = user.split('.');
+ for (var i = 0; i < user_parts.length; i++) {
+ if (!pattern.test(user_parts[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ var ipv4Maybe = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
+ var ipv6Block = /^[0-9A-F]{1,4}$/i;
+
+ function isIP(str) {
+ var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
+
+ assertString(str);
+ version = String(version);
+ if (!version) {
+ return isIP(str, 4) || isIP(str, 6);
+ } else if (version === '4') {
+ if (!ipv4Maybe.test(str)) {
+ return false;
+ }
+ var parts = str.split('.').sort(function (a, b) {
+ return a - b;
+ });
+ return parts[3] <= 255;
+ } else if (version === '6') {
+ var blocks = str.split(':');
+ var foundOmissionBlock = false; // marker to indicate ::
+
+ // At least some OS accept the last 32 bits of an IPv6 address
+ // (i.e. 2 of the blocks) in IPv4 notation, and RFC 3493 says
+ // that '::ffff:a.b.c.d' is valid for IPv4-mapped IPv6 addresses,
+ // and '::a.b.c.d' is deprecated, but also valid.
+ var foundIPv4TransitionBlock = isIP(blocks[blocks.length - 1], 4);
+ var expectedNumberOfBlocks = foundIPv4TransitionBlock ? 7 : 8;
+
+ if (blocks.length > expectedNumberOfBlocks) {
+ return false;
+ }
+ // initial or final ::
+ if (str === '::') {
+ return true;
+ } else if (str.substr(0, 2) === '::') {
+ blocks.shift();
+ blocks.shift();
+ foundOmissionBlock = true;
+ } else if (str.substr(str.length - 2) === '::') {
+ blocks.pop();
+ blocks.pop();
+ foundOmissionBlock = true;
+ }
+
+ for (var i = 0; i < blocks.length; ++i) {
+ // test for a :: which can not be at the string start/end
+ // since those cases have been handled above
+ if (blocks[i] === '' && i > 0 && i < blocks.length - 1) {
+ if (foundOmissionBlock) {
+ return false; // multiple :: in address
+ }
+ foundOmissionBlock = true;
+ } else if (foundIPv4TransitionBlock && i === blocks.length - 1) {
+ // it has been checked before that the last
+ // block is a valid IPv4 address
+ } else if (!ipv6Block.test(blocks[i])) {
+ return false;
+ }
+ }
+ if (foundOmissionBlock) {
+ return blocks.length >= 1;
+ }
+ return blocks.length === expectedNumberOfBlocks;
+ }
+ return false;
+ }
+
+ var default_url_options = {
+ protocols: ['http', 'https', 'ftp'],
+ require_tld: true,
+ require_protocol: false,
+ require_host: true,
+ require_valid_protocol: true,
+ allow_underscores: false,
+ allow_trailing_dot: false,
+ allow_protocol_relative_urls: false
+ };
+
+ var wrapped_ipv6 = /^\[([^\]]+)\](?::([0-9]+))?$/;
+
+ function isRegExp(obj) {
+ return Object.prototype.toString.call(obj) === '[object RegExp]';
+ }
+
+ function checkHost(host, matches) {
+ for (var i = 0; i < matches.length; i++) {
+ var match = matches[i];
+ if (host === match || isRegExp(match) && match.test(host)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function isURL(url, options) {
+ assertString(url);
+ if (!url || url.length >= 2083 || /[\s<>]/.test(url)) {
+ return false;
+ }
+ if (url.indexOf('mailto:') === 0) {
+ return false;
+ }
+ options = merge(options, default_url_options);
+ var protocol = void 0,
+ auth = void 0,
+ host = void 0,
+ hostname = void 0,
+ port = void 0,
+ port_str = void 0,
+ split = void 0,
+ ipv6 = void 0;
+
+ split = url.split('#');
+ url = split.shift();
+
+ split = url.split('?');
+ url = split.shift();
+
+ split = url.split('://');
+ if (split.length > 1) {
+ protocol = split.shift();
+ if (options.require_valid_protocol && options.protocols.indexOf(protocol) === -1) {
+ return false;
+ }
+ } else if (options.require_protocol) {
+ return false;
+ } else if (options.allow_protocol_relative_urls && url.substr(0, 2) === '//') {
+ split[0] = url.substr(2);
+ }
+ url = split.join('://');
+
+ split = url.split('/');
+ url = split.shift();
+
+ if (url === '' && !options.require_host) {
+ return true;
+ }
+
+ split = url.split('@');
+ if (split.length > 1) {
+ auth = split.shift();
+ if (auth.indexOf(':') >= 0 && auth.split(':').length > 2) {
+ return false;
+ }
+ }
+ hostname = split.join('@');
+
+ port_str = ipv6 = null;
+ var ipv6_match = hostname.match(wrapped_ipv6);
+ if (ipv6_match) {
+ host = '';
+ ipv6 = ipv6_match[1];
+ port_str = ipv6_match[2] || null;
+ } else {
+ split = hostname.split(':');
+ host = split.shift();
+ if (split.length) {
+ port_str = split.join(':');
+ }
+ }
+
+ if (port_str !== null) {
+ port = parseInt(port_str, 10);
+ if (!/^[0-9]+$/.test(port_str) || port <= 0 || port > 65535) {
+ return false;
+ }
+ }
+
+ if (!isIP(host) && !isFDQN(host, options) && (!ipv6 || !isIP(ipv6, 6)) && host !== 'localhost') {
+ return false;
+ }
+
+ host = host || ipv6;
+
+ if (options.host_whitelist && !checkHost(host, options.host_whitelist)) {
+ return false;
+ }
+ if (options.host_blacklist && checkHost(host, options.host_blacklist)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ var macAddress = /^([0-9a-fA-F][0-9a-fA-F]:){5}([0-9a-fA-F][0-9a-fA-F])$/;
+
+ function isMACAddress(str) {
+ assertString(str);
+ return macAddress.test(str);
+ }
+
+ function isBoolean(str) {
+ assertString(str);
+ return ['true', 'false', '1', '0'].indexOf(str) >= 0;
+ }
+
+ var alpha = {
+ 'en-US': /^[A-Z]+$/i,
+ 'cs-CZ': /^[A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i,
+ 'da-DK': /^[A-ZÆØÅ]+$/i,
+ 'de-DE': /^[A-ZÄÖÜß]+$/i,
+ 'es-ES': /^[A-ZÁÉÍÑÓÚÜ]+$/i,
+ 'fr-FR': /^[A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i,
+ 'nl-NL': /^[A-ZÉËÏÓÖÜ]+$/i,
+ 'hu-HU': /^[A-ZÁÉÍÓÖŐÚÜŰ]+$/i,
+ 'pl-PL': /^[A-ZĄĆĘŚŁŃÓŻŹ]+$/i,
+ 'pt-PT': /^[A-ZÃÁÀÂÇÉÊÍÕÓÔÚÜ]+$/i,
+ 'ru-RU': /^[А-ЯЁ]+$/i,
+ 'sr-RS@latin': /^[A-ZČĆŽŠĐ]+$/i,
+ 'sr-RS': /^[А-ЯЂЈЉЊЋЏ]+$/i,
+ 'tr-TR': /^[A-ZÇĞİıÖŞÜ]+$/i,
+ 'uk-UA': /^[А-ЩЬЮЯЄIЇҐ]+$/i,
+ ar: /^[ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/
+ };
+
+ var alphanumeric = {
+ 'en-US': /^[0-9A-Z]+$/i,
+ 'cs-CZ': /^[0-9A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i,
+ 'da-DK': /^[0-9A-ZÆØÅ]$/i,
+ 'de-DE': /^[0-9A-ZÄÖÜß]+$/i,
+ 'es-ES': /^[0-9A-ZÁÉÍÑÓÚÜ]+$/i,
+ 'fr-FR': /^[0-9A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i,
+ 'hu-HU': /^[0-9A-ZÁÉÍÓÖŐÚÜŰ]+$/i,
+ 'nl-NL': /^[0-9A-ZÉËÏÓÖÜ]+$/i,
+ 'pl-PL': /^[0-9A-ZĄĆĘŚŁŃÓŻŹ]+$/i,
+ 'pt-PT': /^[0-9A-ZÃÁÀÂÇÉÊÍÕÓÔÚÜ]+$/i,
+ 'ru-RU': /^[0-9А-ЯЁ]+$/i,
+ 'sr-RS@latin': /^[0-9A-ZČĆŽŠĐ]+$/i,
+ 'sr-RS': /^[0-9А-ЯЂЈЉЊЋЏ]+$/i,
+ 'tr-TR': /^[0-9A-ZÇĞİıÖŞÜ]+$/i,
+ 'uk-UA': /^[0-9А-ЩЬЮЯЄIЇҐ]+$/i,
+ ar: /^[٠١٢٣٤٥٦٧٨٩0-9ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/
+ };
+
+ var englishLocales = ['AU', 'GB', 'HK', 'IN', 'NZ', 'ZA', 'ZM'];
+
+ for (var locale, i = 0; i < englishLocales.length; i++) {
+ locale = 'en-' + englishLocales[i];
+ alpha[locale] = alpha['en-US'];
+ alphanumeric[locale] = alphanumeric['en-US'];
+ }
+
+ alpha['pt-BR'] = alpha['pt-PT'];
+ alphanumeric['pt-BR'] = alphanumeric['pt-PT'];
+
+ // Source: http://www.localeplanet.com/java/
+ var arabicLocales = ['AE', 'BH', 'DZ', 'EG', 'IQ', 'JO', 'KW', 'LB', 'LY', 'MA', 'QM', 'QA', 'SA', 'SD', 'SY', 'TN', 'YE'];
+
+ for (var _locale, _i = 0; _i < arabicLocales.length; _i++) {
+ _locale = 'ar-' + arabicLocales[_i];
+ alpha[_locale] = alpha.ar;
+ alphanumeric[_locale] = alphanumeric.ar;
+ }
+
+ function isAlpha(str) {
+ var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'en-US';
+
+ assertString(str);
+ if (locale in alpha) {
+ return alpha[locale].test(str);
+ }
+ throw new Error('Invalid locale \'' + locale + '\'');
+ }
+
+ function isAlphanumeric(str) {
+ var locale = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'en-US';
+
+ assertString(str);
+ if (locale in alphanumeric) {
+ return alphanumeric[locale].test(str);
+ }
+ throw new Error('Invalid locale \'' + locale + '\'');
+ }
+
+ var numeric = /^[-+]?[0-9]+$/;
+
+ function isNumeric(str) {
+ assertString(str);
+ return numeric.test(str);
+ }
+
+ function isLowercase(str) {
+ assertString(str);
+ return str === str.toLowerCase();
+ }
+
+ function isUppercase(str) {
+ assertString(str);
+ return str === str.toUpperCase();
+ }
+
+ /* eslint-disable no-control-regex */
+ var ascii = /^[\x00-\x7F]+$/;
+ /* eslint-enable no-control-regex */
+
+ function isAscii(str) {
+ assertString(str);
+ return ascii.test(str);
+ }
+
+ var fullWidth = /[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]/;
+
+ function isFullWidth(str) {
+ assertString(str);
+ return fullWidth.test(str);
+ }
+
+ var halfWidth = /[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]/;
+
+ function isHalfWidth(str) {
+ assertString(str);
+ return halfWidth.test(str);
+ }
+
+ function isVariableWidth(str) {
+ assertString(str);
+ return fullWidth.test(str) && halfWidth.test(str);
+ }
+
+ /* eslint-disable no-control-regex */
+ var multibyte = /[^\x00-\x7F]/;
+ /* eslint-enable no-control-regex */
+
+ function isMultibyte(str) {
+ assertString(str);
+ return multibyte.test(str);
+ }
+
+ var surrogatePair = /[\uD800-\uDBFF][\uDC00-\uDFFF]/;
+
+ function isSurrogatePair(str) {
+ assertString(str);
+ return surrogatePair.test(str);
+ }
+
+ var int = /^(?:[-+]?(?:0|[1-9][0-9]*))$/;
+ var intLeadingZeroes = /^[-+]?[0-9]+$/;
+
+ function isInt(str, options) {
+ assertString(str);
+ options = options || {};
+
+ // Get the regex to use for testing, based on whether
+ // leading zeroes are allowed or not.
+ var regex = options.hasOwnProperty('allow_leading_zeroes') && !options.allow_leading_zeroes ? int : intLeadingZeroes;
+
+ // Check min/max/lt/gt
+ var minCheckPassed = !options.hasOwnProperty('min') || str >= options.min;
+ var maxCheckPassed = !options.hasOwnProperty('max') || str <= options.max;
+ var ltCheckPassed = !options.hasOwnProperty('lt') || str < options.lt;
+ var gtCheckPassed = !options.hasOwnProperty('gt') || str > options.gt;
+
+ return regex.test(str) && minCheckPassed && maxCheckPassed && ltCheckPassed && gtCheckPassed;
+ }
+
+ var float = /^(?:[-+])?(?:[0-9]+)?(?:\.[0-9]*)?(?:[eE][\+\-]?(?:[0-9]+))?$/;
+
+ function isFloat(str, options) {
+ assertString(str);
+ options = options || {};
+ if (str === '' || str === '.') {
+ return false;
+ }
+ 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);
+ }
+
+ var decimal = /^[-+]?([0-9]+|\.[0-9]+|[0-9]+\.[0-9]+)$/;
+
+ function isDecimal(str) {
+ assertString(str);
+ return str !== '' && decimal.test(str);
+ }
+
+ var hexadecimal = /^[0-9A-F]+$/i;
+
+ function isHexadecimal(str) {
+ assertString(str);
+ return hexadecimal.test(str);
+ }
+
+ function isDivisibleBy(str, num) {
+ assertString(str);
+ return toFloat(str) % parseInt(num, 10) === 0;
+ }
+
+ var hexcolor = /^#?([0-9A-F]{3}|[0-9A-F]{6})$/i;
+
+ function isHexColor(str) {
+ assertString(str);
+ return hexcolor.test(str);
+ }
+
+ var md5 = /^[a-f0-9]{32}$/;
+
+ function isMD5(str) {
+ assertString(str);
+ return md5.test(str);
+ }
+
+ function isJSON(str) {
+ assertString(str);
+ try {
+ var obj = JSON.parse(str);
+ return !!obj && (typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object';
+ } catch (e) {/* ignore */}
+ return false;
+ }
+
+ function isEmpty(str) {
+ assertString(str);
+ return str.length === 0;
+ }
+
+ /* eslint-disable prefer-rest-params */
+ function isLength(str, options) {
+ assertString(str);
+ var min = void 0;
+ var max = void 0;
+ if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') {
+ min = options.min || 0;
+ max = options.max;
+ } else {
+ // backwards compatibility: isLength(str, min [, max])
+ min = arguments[1];
+ max = arguments[2];
+ }
+ var surrogatePairs = str.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g) || [];
+ var len = str.length - surrogatePairs.length;
+ return len >= min && (typeof max === 'undefined' || len <= max);
+ }
+
+ var uuid = {
+ 3: /^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
+ 4: /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
+ 5: /^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
+ all: /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i
+ };
+
+ function isUUID(str) {
+ var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'all';
+
+ assertString(str);
+ var pattern = uuid[version];
+ return pattern && pattern.test(str);
+ }
+
+ function isMongoId(str) {
+ assertString(str);
+ return isHexadecimal(str) && str.length === 24;
+ }
+
+ function isAfter(str) {
+ var date = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : String(new Date());
+
+ assertString(str);
+ var comparison = toDate(date);
+ var original = toDate(str);
+ return !!(original && comparison && original > comparison);
+ }
+
+ function isBefore(str) {
+ var date = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : String(new Date());
+
+ assertString(str);
+ var comparison = toDate(date);
+ var original = toDate(str);
+ return !!(original && comparison && original < comparison);
+ }
+
+ function isIn(str, options) {
+ assertString(str);
+ var i = void 0;
+ if (Object.prototype.toString.call(options) === '[object Array]') {
+ var array = [];
+ for (i in options) {
+ if ({}.hasOwnProperty.call(options, i)) {
+ array[i] = toString(options[i]);
+ }
+ }
+ return array.indexOf(str) >= 0;
+ } else if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') {
+ return options.hasOwnProperty(str);
+ } else if (options && typeof options.indexOf === 'function') {
+ return options.indexOf(str) >= 0;
+ }
+ return false;
+ }
+
+ /* eslint-disable max-len */
+ 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}$/;
+ /* eslint-enable max-len */
+
+ function isCreditCard(str) {
+ assertString(str);
+ var sanitized = str.replace(/[^0-9]+/g, '');
+ if (!creditCard.test(sanitized)) {
+ return false;
+ }
+ var sum = 0;
+ var digit = void 0;
+ var tmpNum = void 0;
+ var shouldDouble = void 0;
+ for (var i = sanitized.length - 1; i >= 0; i--) {
+ digit = sanitized.substring(i, i + 1);
+ tmpNum = parseInt(digit, 10);
+ if (shouldDouble) {
+ tmpNum *= 2;
+ if (tmpNum >= 10) {
+ sum += tmpNum % 10 + 1;
+ } else {
+ sum += tmpNum;
+ }
+ } else {
+ sum += tmpNum;
+ }
+ shouldDouble = !shouldDouble;
+ }
+ return !!(sum % 10 === 0 ? sanitized : false);
+ }
+
+ var isin = /^[A-Z]{2}[0-9A-Z]{9}[0-9]$/;
+
+ function isISIN(str) {
+ assertString(str);
+ if (!isin.test(str)) {
+ return false;
+ }
+
+ var checksumStr = str.replace(/[A-Z]/g, function (character) {
+ return parseInt(character, 36);
+ });
+
+ var sum = 0;
+ var digit = void 0;
+ var tmpNum = void 0;
+ var shouldDouble = true;
+ for (var i = checksumStr.length - 2; i >= 0; i--) {
+ digit = checksumStr.substring(i, i + 1);
+ tmpNum = parseInt(digit, 10);
+ if (shouldDouble) {
+ tmpNum *= 2;
+ if (tmpNum >= 10) {
+ sum += tmpNum + 1;
+ } else {
+ sum += tmpNum;
+ }
+ } else {
+ sum += tmpNum;
+ }
+ shouldDouble = !shouldDouble;
+ }
+
+ return parseInt(str.substr(str.length - 1), 10) === (10000 - sum) % 10;
+ }
+
+ var isbn10Maybe = /^(?:[0-9]{9}X|[0-9]{10})$/;
+ var isbn13Maybe = /^(?:[0-9]{13})$/;
+ var factor = [1, 3];
+
+ function isISBN(str) {
+ var version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
+
+ assertString(str);
+ version = String(version);
+ if (!version) {
+ return isISBN(str, 10) || isISBN(str, 13);
+ }
+ var sanitized = str.replace(/[\s-]+/g, '');
+ var checksum = 0;
+ var i = void 0;
+ if (version === '10') {
+ if (!isbn10Maybe.test(sanitized)) {
+ return false;
+ }
+ for (i = 0; i < 9; i++) {
+ checksum += (i + 1) * sanitized.charAt(i);
+ }
+ if (sanitized.charAt(9) === 'X') {
+ checksum += 10 * 10;
+ } else {
+ checksum += 10 * sanitized.charAt(9);
+ }
+ if (checksum % 11 === 0) {
+ return !!sanitized;
+ }
+ } else if (version === '13') {
+ if (!isbn13Maybe.test(sanitized)) {
+ return false;
+ }
+ for (i = 0; i < 12; i++) {
+ checksum += factor[i % 2] * sanitized.charAt(i);
+ }
+ if (sanitized.charAt(12) - (10 - checksum % 10) % 10 === 0) {
+ return !!sanitized;
+ }
+ }
+ return false;
+ }
+
+ var issn = '^\\d{4}-?\\d{3}[\\dX]$';
+
+ function isISSN(str) {
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+
+ assertString(str);
+ var testIssn = issn;
+ testIssn = options.require_hyphen ? testIssn.replace('?', '') : testIssn;
+ testIssn = options.case_sensitive ? new RegExp(testIssn) : new RegExp(testIssn, 'i');
+ if (!testIssn.test(str)) {
+ return false;
+ }
+ var issnDigits = str.replace('-', '');
+ var position = 8;
+ var checksum = 0;
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
+
+ try {
+ for (var _iterator = issnDigits[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var digit = _step.value;
+
+ var digitValue = digit.toUpperCase() === 'X' ? 10 : +digit;
+ checksum += digitValue * position;
+ --position;
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+
+ return checksum % 11 === 0;
+ }
+
+ /* eslint-disable max-len */
+ var phones = {
+ 'ar-DZ': /^(\+?213|0)(5|6|7)\d{8}$/,
+ 'ar-SY': /^(!?(\+?963)|0)?9\d{8}$/,
+ 'ar-SA': /^(!?(\+?966)|0)?5\d{8}$/,
+ 'en-US': /^(\+?1)?[2-9]\d{2}[2-9](?!11)\d{6}$/,
+ 'cs-CZ': /^(\+?420)? ?[1-9][0-9]{2} ?[0-9]{3} ?[0-9]{3}$/,
+ 'de-DE': /^(\+?49[ \.\-])?([\(]{1}[0-9]{1,6}[\)])?([0-9 \.\-\/]{3,20})((x|ext|extension)[ ]?[0-9]{1,4})?$/,
+ 'da-DK': /^(\+?45)?(\d{8})$/,
+ 'el-GR': /^(\+?30)?(69\d{8})$/,
+ 'en-AU': /^(\+?61|0)4\d{8}$/,
+ 'en-GB': /^(\+?44|0)7\d{9}$/,
+ // According to http://www.ofca.gov.hk/filemanager/ofca/en/content_311/no_plan.pdf
+ '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})$/,
+ 'en-IN': /^(\+?91|0)?[789]\d{9}$/,
+ 'en-NG': /^(\+?234|0)?[789]\d{9}$/,
+ 'en-NZ': /^(\+?64|0)2\d{7,9}$/,
+ 'en-ZA': /^(\+?27|0)\d{9}$/,
+ 'en-ZM': /^(\+?26)?09[567]\d{7}$/,
+ 'es-ES': /^(\+?34)?(6\d{1}|7[1234])\d{7}$/,
+ 'fi-FI': /^(\+?358|0)\s?(4(0|1|2|4|5)?|50)\s?(\d\s?){4,8}\d$/,
+ 'fr-FR': /^(\+?33|0)[67]\d{8}$/,
+ 'he-IL': /^(\+972|0)([23489]|5[0248]|77)[1-9]\d{6}/,
+ 'hu-HU': /^(\+?36)(20|30|70)\d{7}$/,
+ 'lt-LT': /^(\+370|8)\d{8}$/,
+ 'id-ID': /^(\+?62|0[1-9])[\s|\d]+$/,
+ 'it-IT': /^(\+?39)?\s?3\d{2} ?\d{6,7}$/,
+ 'ko-KR': /^((\+?82)[ \-]?)?0?1([0|1|6|7|8|9]{1})[ \-]?\d{3,4}[ \-]?\d{4}$/,
+ 'ja-JP': /^(\+?81|0)\d{1,4}[ \-]?\d{1,4}[ \-]?\d{4}$/,
+ 'ms-MY': /^(\+?6?01){1}(([145]{1}(\-|\s)?\d{7,8})|([236789]{1}(\s|\-)?\d{7}))$/,
+ 'nb-NO': /^(\+?47)?[49]\d{7}$/,
+ 'nl-BE': /^(\+?32|0)4?\d{8}$/,
+ 'nn-NO': /^(\+?47)?[49]\d{7}$/,
+ 'pl-PL': /^(\+?48)? ?[5-8]\d ?\d{3} ?\d{2} ?\d{2}$/,
+ 'pt-BR': /^(\+?55|0)\-?[1-9]{2}\-?[2-9]{1}\d{3,4}\-?\d{4}$/,
+ 'pt-PT': /^(\+?351)?9[1236]\d{7}$/,
+ 'ro-RO': /^(\+?4?0)\s?7\d{2}(\/|\s|\.|\-)?\d{3}(\s|\.|\-)?\d{3}$/,
+ 'en-PK': /^((\+92)|(0092))-{0,1}\d{3}-{0,1}\d{7}$|^\d{11}$|^\d{4}-\d{7}$/,
+ 'ru-RU': /^(\+?7|8)?9\d{9}$/,
+ 'sr-RS': /^(\+3816|06)[- \d]{5,9}$/,
+ 'tr-TR': /^(\+?90|0)?5\d{9}$/,
+ 'vi-VN': /^(\+?84|0)?((1(2([0-9])|6([2-9])|88|99))|(9((?!5)[0-9])))([0-9]{7})$/,
+ 'zh-CN': /^(\+?0?86\-?)?1[345789]\d{9}$/,
+ 'zh-TW': /^(\+?886\-?|0)?9\d{8}$/
+ };
+ /* eslint-enable max-len */
+
+ // aliases
+ phones['en-CA'] = phones['en-US'];
+ phones['fr-BE'] = phones['nl-BE'];
+ phones['zh-HK'] = phones['en-HK'];
+
+ function isMobilePhone(str, locale) {
+ assertString(str);
+ if (locale in phones) {
+ return phones[locale].test(str);
+ } else if (locale === 'any') {
+ return !!Object.values(phones).find(phone => phone.test(str));
+ }
+ return false;
+ }
+
+ function currencyRegex(options) {
+ var symbol = '(\\' + options.symbol.replace(/\./g, '\\.') + ')' + (options.require_symbol ? '' : '?'),
+ negative = '-?',
+ whole_dollar_amount_without_sep = '[1-9]\\d*',
+ whole_dollar_amount_with_sep = '[1-9]\\d{0,2}(\\' + options.thousands_separator + '\\d{3})*',
+ valid_whole_dollar_amounts = ['0', whole_dollar_amount_without_sep, whole_dollar_amount_with_sep],
+ whole_dollar_amount = '(' + valid_whole_dollar_amounts.join('|') + ')?',
+ decimal_amount = '(\\' + options.decimal_separator + '\\d{2})?';
+ var pattern = whole_dollar_amount + decimal_amount;
+
+ // default is negative sign before symbol, but there are two other options (besides parens)
+ if (options.allow_negatives && !options.parens_for_negatives) {
+ if (options.negative_sign_after_digits) {
+ pattern += negative;
+ } else if (options.negative_sign_before_digits) {
+ pattern = negative + pattern;
+ }
+ }
+
+ // South African Rand, for example, uses R 123 (space) and R-123 (no space)
+ if (options.allow_negative_sign_placeholder) {
+ pattern = '( (?!\\-))?' + pattern;
+ } else if (options.allow_space_after_symbol) {
+ pattern = ' ?' + pattern;
+ } else if (options.allow_space_after_digits) {
+ pattern += '( (?!$))?';
+ }
+
+ if (options.symbol_after_digits) {
+ pattern += symbol;
+ } else {
+ pattern = symbol + pattern;
+ }
+
+ if (options.allow_negatives) {
+ if (options.parens_for_negatives) {
+ pattern = '(\\(' + pattern + '\\)|' + pattern + ')';
+ } else if (!(options.negative_sign_before_digits || options.negative_sign_after_digits)) {
+ pattern = negative + pattern;
+ }
+ }
+
+ /* eslint-disable prefer-template */
+ return new RegExp('^' +
+ // ensure there's a dollar and/or decimal amount, and that
+ // it doesn't start with a space or a negative sign followed by a space
+ '(?!-? )(?=.*\\d)' + pattern + '$');
+ /* eslint-enable prefer-template */
+ }
+
+ var default_currency_options = {
+ symbol: '$',
+ require_symbol: false,
+ allow_space_after_symbol: false,
+ symbol_after_digits: false,
+ allow_negatives: true,
+ parens_for_negatives: false,
+ negative_sign_before_digits: false,
+ negative_sign_after_digits: false,
+ allow_negative_sign_placeholder: false,
+ thousands_separator: ',',
+ decimal_separator: '.',
+ allow_space_after_digits: false
+ };
+
+ function isCurrency(str, options) {
+ assertString(str);
+ options = merge(options, default_currency_options);
+ return currencyRegex(options).test(str);
+ }
+
+ /* eslint-disable max-len */
+ // from http://goo.gl/0ejHHW
+ 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)?)?)?)?$/;
+ /* eslint-enable max-len */
+
+ function isISO8601 (str) {
+ assertString(str);
+ return iso8601.test(str);
+ }
+
+ function isBase64(str) {
+ assertString(str);
+ // Value length must be divisible by 4.
+ var len = str.length;
+ if (!len || len % 4 !== 0) {
+ return false;
+ }
+
+ try {
+ if (atob(str)) {
+ return true;
+ }
+ } catch (e) {
+ return false;
+ }
+ }
+
+ 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
+
+ function isDataURI(str) {
+ assertString(str);
+ return dataURI.test(str);
+ }
+
+ function ltrim(str, chars) {
+ assertString(str);
+ var pattern = chars ? new RegExp('^[' + chars + ']+', 'g') : /^\s+/g;
+ return str.replace(pattern, '');
+ }
+
+ function rtrim(str, chars) {
+ assertString(str);
+ var pattern = chars ? new RegExp('[' + chars + ']') : /\s/;
+
+ var idx = str.length - 1;
+ while (idx >= 0 && pattern.test(str[idx])) {
+ idx--;
+ }
+
+ return idx < str.length ? str.substr(0, idx + 1) : str;
+ }
+
+ function trim(str, chars) {
+ return rtrim(ltrim(str, chars), chars);
+ }
+
+ function escape(str) {
+ assertString(str);
+ return str.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\//g, '&#x2F;').replace(/\\/g, '&#x5C;').replace(/`/g, '&#96;');
+ }
+
+ function unescape(str) {
+ assertString(str);
+ return str.replace(/&amp;/g, '&').replace(/&quot;/g, '"').replace(/&#x27;/g, "'").replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&#x2F;/g, '/').replace(/&#96;/g, '`');
+ }
+
+ function blacklist(str, chars) {
+ assertString(str);
+ return str.replace(new RegExp('[' + chars + ']+', 'g'), '');
+ }
+
+ function stripLow(str, keep_new_lines) {
+ assertString(str);
+ var chars = keep_new_lines ? '\\x00-\\x09\\x0B\\x0C\\x0E-\\x1F\\x7F' : '\\x00-\\x1F\\x7F';
+ return blacklist(str, chars);
+ }
+
+ function whitelist(str, chars) {
+ assertString(str);
+ return str.replace(new RegExp('[^' + chars + ']+', 'g'), '');
+ }
+
+ function isWhitelisted(str, chars) {
+ assertString(str);
+ for (var i = str.length - 1; i >= 0; i--) {
+ if (chars.indexOf(str[i]) === -1) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ var default_normalize_email_options = {
+ // The following options apply to all email addresses
+ // Lowercases the local part of the email address.
+ // Please note this may violate RFC 5321 as per http://stackoverflow.com/a/9808332/192024).
+ // The domain is always lowercased, as per RFC 1035
+ all_lowercase: true,
+
+ // The following conversions are specific to GMail
+ // Lowercases the local part of the GMail address (known to be case-insensitive)
+ gmail_lowercase: true,
+ // Removes dots from the local part of the email address, as that's ignored by GMail
+ gmail_remove_dots: true,
+ // Removes the subaddress (e.g. "+foo") from the email address
+ gmail_remove_subaddress: true,
+ // Conversts the googlemail.com domain to gmail.com
+ gmail_convert_googlemaildotcom: true,
+
+ // The following conversions are specific to Outlook.com / Windows Live / Hotmail
+ // Lowercases the local part of the Outlook.com address (known to be case-insensitive)
+ outlookdotcom_lowercase: true,
+ // Removes the subaddress (e.g. "+foo") from the email address
+ outlookdotcom_remove_subaddress: true,
+
+ // The following conversions are specific to Yahoo
+ // Lowercases the local part of the Yahoo address (known to be case-insensitive)
+ yahoo_lowercase: true,
+ // Removes the subaddress (e.g. "-foo") from the email address
+ yahoo_remove_subaddress: true,
+
+ // The following conversions are specific to iCloud
+ // Lowercases the local part of the iCloud address (known to be case-insensitive)
+ icloud_lowercase: true,
+ // Removes the subaddress (e.g. "+foo") from the email address
+ icloud_remove_subaddress: true
+ };
+
+ // List of domains used by iCloud
+ var icloud_domains = ['icloud.com', 'me.com'];
+
+ // List of domains used by Outlook.com and its predecessors
+ // This list is likely incomplete.
+ // Partial reference:
+ // https://blogs.office.com/2013/04/17/outlook-com-gets-two-step-verification-sign-in-by-alias-and-new-international-domains/
+ 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'];
+
+ // List of domains used by Yahoo Mail
+ // This list is likely incomplete
+ var yahoo_domains = ['rocketmail.com', 'yahoo.ca', 'yahoo.co.uk', 'yahoo.com', 'yahoo.de', 'yahoo.fr', 'yahoo.in', 'yahoo.it', 'ymail.com'];
+
+ function normalizeEmail(email, options) {
+ options = merge(options, default_normalize_email_options);
+
+ if (!isEmail(email)) {
+ return false;
+ }
+
+ var raw_parts = email.split('@');
+ var domain = raw_parts.pop();
+ var user = raw_parts.join('@');
+ var parts = [user, domain];
+
+ // The domain is always lowercased, as it's case-insensitive per RFC 1035
+ parts[1] = parts[1].toLowerCase();
+
+ if (parts[1] === 'gmail.com' || parts[1] === 'googlemail.com') {
+ // Address is GMail
+ if (options.gmail_remove_subaddress) {
+ parts[0] = parts[0].split('+')[0];
+ }
+ if (options.gmail_remove_dots) {
+ parts[0] = parts[0].replace(/\./g, '');
+ }
+ if (!parts[0].length) {
+ return false;
+ }
+ if (options.all_lowercase || options.gmail_lowercase) {
+ parts[0] = parts[0].toLowerCase();
+ }
+ parts[1] = options.gmail_convert_googlemaildotcom ? 'gmail.com' : parts[1];
+ } else if (~icloud_domains.indexOf(parts[1])) {
+ // Address is iCloud
+ if (options.icloud_remove_subaddress) {
+ parts[0] = parts[0].split('+')[0];
+ }
+ if (!parts[0].length) {
+ return false;
+ }
+ if (options.all_lowercase || options.icloud_lowercase) {
+ parts[0] = parts[0].toLowerCase();
+ }
+ } else if (~outlookdotcom_domains.indexOf(parts[1])) {
+ // Address is Outlook.com
+ if (options.outlookdotcom_remove_subaddress) {
+ parts[0] = parts[0].split('+')[0];
+ }
+ if (!parts[0].length) {
+ return false;
+ }
+ if (options.all_lowercase || options.outlookdotcom_lowercase) {
+ parts[0] = parts[0].toLowerCase();
+ }
+ } else if (~yahoo_domains.indexOf(parts[1])) {
+ // Address is Yahoo
+ if (options.yahoo_remove_subaddress) {
+ var components = parts[0].split('-');
+ parts[0] = components.length > 1 ? components.slice(0, -1).join('-') : components[0];
+ }
+ if (!parts[0].length) {
+ return false;
+ }
+ if (options.all_lowercase || options.yahoo_lowercase) {
+ parts[0] = parts[0].toLowerCase();
+ }
+ } else if (options.all_lowercase) {
+ // Any other address
+ parts[0] = parts[0].toLowerCase();
+ }
+ return parts.join('@');
+ }
+
+ // see http://isrc.ifpi.org/en/isrc-standard/code-syntax
+ var isrc = /^[A-Z]{2}[0-9A-Z]{3}\d{2}\d{5}$/;
+
+ function isISRC(str) {
+ assertString(str);
+ return isrc.test(str);
+ }
+
+ var cultureCodes = new Set(["ar", "bg", "ca", "zh-Hans", "cs", "da", "de",
+ "el", "en", "es", "fi", "fr", "he", "hu", "is", "it", "ja", "ko", "nl", "no",
+ "pl", "pt", "rm", "ro", "ru", "hr", "sk", "sq", "sv", "th", "tr", "ur", "id",
+ "uk", "be", "sl", "et", "lv", "lt", "tg", "fa", "vi", "hy", "az", "eu", "hsb",
+ "mk", "tn", "xh", "zu", "af", "ka", "fo", "hi", "mt", "se", "ga", "ms", "kk",
+ "ky", "sw", "tk", "uz", "tt", "bn", "pa", "gu", "or", "ta", "te", "kn", "ml",
+ "as", "mr", "sa", "mn", "bo", "cy", "km", "lo", "gl", "kok", "syr", "si", "iu",
+ "am", "tzm", "ne", "fy", "ps", "fil", "dv", "ha", "yo", "quz", "nso", "ba", "lb",
+ "kl", "ig", "ii", "arn", "moh", "br", "ug", "mi", "oc", "co", "gsw", "sah",
+ "qut", "rw", "wo", "prs", "gd", "ar-SA", "bg-BG", "ca-ES", "zh-TW", "cs-CZ",
+ "da-DK", "de-DE", "el-GR", "en-US", "fi-FI", "fr-FR", "he-IL", "hu-HU", "is-IS",
+ "it-IT", "ja-JP", "ko-KR", "nl-NL", "nb-NO", "pl-PL", "pt-BR", "rm-CH", "ro-RO",
+ "ru-RU", "hr-HR", "sk-SK", "sq-AL", "sv-SE", "th-TH", "tr-TR", "ur-PK", "id-ID",
+ "uk-UA", "be-BY", "sl-SI", "et-EE", "lv-LV", "lt-LT", "tg-Cyrl-TJ", "fa-IR",
+ "vi-VN", "hy-AM", "az-Latn-AZ", "eu-ES", "hsb-DE", "mk-MK", "tn-ZA", "xh-ZA",
+ "zu-ZA", "af-ZA", "ka-GE", "fo-FO", "hi-IN", "mt-MT", "se-NO", "ms-MY", "kk-KZ",
+ "ky-KG", "sw-KE", "tk-TM", "uz-Latn-UZ", "tt-RU", "bn-IN", "pa-IN", "gu-IN",
+ "or-IN", "ta-IN", "te-IN", "kn-IN", "ml-IN", "as-IN", "mr-IN", "sa-IN", "mn-MN",
+ "bo-CN", "cy-GB", "km-KH", "lo-LA", "gl-ES", "kok-IN", "syr-SY", "si-LK",
+ "iu-Cans-CA", "am-ET", "ne-NP", "fy-NL", "ps-AF", "fil-PH", "dv-MV",
+ "ha-Latn-NG", "yo-NG", "quz-BO", "nso-ZA", "ba-RU", "lb-LU", "kl-GL", "ig-NG",
+ "ii-CN", "arn-CL", "moh-CA", "br-FR", "ug-CN", "mi-NZ", "oc-FR", "co-FR",
+ "gsw-FR", "sah-RU", "qut-GT", "rw-RW", "wo-SN", "prs-AF", "gd-GB", "ar-IQ",
+ "zh-CN", "de-CH", "en-GB", "es-MX", "fr-BE", "it-CH", "nl-BE", "nn-NO", "pt-PT",
+ "sr-Latn-CS", "sv-FI", "az-Cyrl-AZ", "dsb-DE", "se-SE", "ga-IE", "ms-BN",
+ "uz-Cyrl-UZ", "bn-BD", "mn-Mong-CN", "iu-Latn-CA", "tzm-Latn-DZ", "quz-EC",
+ "ar-EG", "zh-HK", "de-AT", "en-AU", "es-ES", "fr-CA", "sr-Cyrl-CS", "se-FI",
+ "quz-PE", "ar-LY", "zh-SG", "de-LU", "en-CA", "es-GT", "fr-CH", "hr-BA",
+ "smj-NO", "ar-DZ", "zh-MO", "de-LI", "en-NZ", "es-CR", "fr-LU", "bs-Latn-BA",
+ "smj-SE", "ar-MA", "en-IE", "es-PA", "fr-MC", "sr-Latn-BA", "sma-NO", "ar-TN",
+ "en-ZA", "es-DO", "sr-Cyrl-BA", "sma-SE", "ar-OM", "en-JM", "es-VE",
+ "bs-Cyrl-BA", "sms-FI", "ar-YE", "en-029", "es-CO", "sr-Latn-RS", "smn-FI",
+ "ar-SY", "en-BZ", "es-PE", "sr-Cyrl-RS", "ar-JO", "en-TT", "es-AR", "sr-Latn-ME",
+ "ar-LB", "en-ZW", "es-EC", "sr-Cyrl-ME", "ar-KW", "en-PH", "es-CL", "ar-AE",
+ "es-UY", "ar-BH", "es-PY", "ar-QA", "en-IN", "es-BO", "en-MY", "es-SV", "en-SG",
+ "es-HN", "es-NI", "es-PR", "es-US", "bs-Cyrl", "bs-Latn", "sr-Cyrl", "sr-Latn",
+ "smn", "az-Cyrl", "sms", "zh", "nn", "bs", "az-Latn", "sma", "uz-Cyrl",
+ "mn-Cyrl", "iu-Cans", "zh-Hant", "nb", "sr", "tg-Cyrl", "dsb", "smj", "uz-Latn",
+ "mn-Mong", "iu-Latn", "tzm-Latn", "ha-Latn", "zh-CHS", "zh-CHT"]);
+
+ function isRFC5646(str) {
+ assertString(str);
+ // According to the spec these codes are case sensitive so we can check the
+ // string directly.
+ return cultureCodes.has(str);
+ }
+
+ 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
+
+ function isSemVer(str) {
+ assertString(str);
+ return semver.test(str);
+ }
+
+ 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
+
+ function isRGBColor(str) {
+ assertString(str);
+ return rgbcolor.test(str);
+ }
+
+ var version = '7.0.0';
+
+ var validator = {
+ blacklist: blacklist,
+ contains: contains,
+ equals: equals,
+ escape: escape,
+ isAfter: isAfter,
+ isAlpha: isAlpha,
+ isAlphanumeric: isAlphanumeric,
+ isAscii: isAscii,
+ isBase64: isBase64,
+ isBefore: isBefore,
+ isBoolean: isBoolean,
+ isByteLength: isByteLength,
+ isCreditCard: isCreditCard,
+ isCurrency: isCurrency,
+ isDataURI: isDataURI,
+ isDecimal: isDecimal,
+ isDivisibleBy: isDivisibleBy,
+ isEmail: isEmail,
+ isEmpty: isEmpty,
+ isFloat: isFloat,
+ isFQDN: isFDQN,
+ isFullWidth: isFullWidth,
+ isHalfWidth: isHalfWidth,
+ isHexadecimal: isHexadecimal,
+ isHexColor: isHexColor,
+ isIn: isIn,
+ isInt: isInt,
+ isIP: isIP,
+ isRFC5646: isRFC5646,
+ isISBN: isISBN,
+ isISIN: isISIN,
+ isISO8601: isISO8601,
+ isISRC: isISRC,
+ isRGBColor: isRGBColor,
+ isISSN: isISSN,
+ isJSON: isJSON,
+ isLength: isLength,
+ isLowercase: isLowercase,
+ isMACAddress: isMACAddress,
+ isMD5: isMD5,
+ isMobilePhone: isMobilePhone,
+ isMongoId: isMongoId,
+ isMultibyte: isMultibyte,
+ isNumeric: isNumeric,
+ isSemVer: isSemVer,
+ isSurrogatePair: isSurrogatePair,
+ isUppercase: isUppercase,
+ isURL: isURL,
+ isUUID: isUUID,
+ isVariableWidth: isVariableWidth,
+ isWhitelisted: isWhitelisted,
+ ltrim: ltrim,
+ matches: matches,
+ normalizeEmail: normalizeEmail,
+ rtrim: rtrim,
+ stripLow: stripLow,
+ toBoolean: toBoolean,
+ toDate: toDate,
+ toFloat: toFloat,
+ toInt: toInt,
+ toString: toString,
+ trim: trim,
+ unescape: unescape,
+ version: version,
+ whitelist: whitelist
+ };
+
+ return validator;
+}));