From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- devtools/shared/storage/moz.build | 9 + devtools/shared/storage/utils.js | 161 + devtools/shared/storage/vendor/JSON5_LICENSE | 23 + devtools/shared/storage/vendor/JSON5_UPGRADING.md | 36 + devtools/shared/storage/vendor/json5.js | 1713 +++++++++ devtools/shared/storage/vendor/moz.build | 13 + .../storage/vendor/stringvalidator/UPDATING.md | 142 + .../storage/vendor/stringvalidator/moz.build | 15 + .../tests/xpcshell/head_stringvalidator.js | 15 + .../tests/xpcshell/test_sanitizers.js | 419 +++ .../tests/xpcshell/test_validators.js | 3762 ++++++++++++++++++++ .../stringvalidator/tests/xpcshell/xpcshell.ini | 8 + .../storage/vendor/stringvalidator/util/assert.js | 215 ++ .../storage/vendor/stringvalidator/util/moz.build | 9 + .../storage/vendor/stringvalidator/validator.js | 1489 ++++++++ 15 files changed, 8029 insertions(+) create mode 100644 devtools/shared/storage/moz.build create mode 100644 devtools/shared/storage/utils.js create mode 100644 devtools/shared/storage/vendor/JSON5_LICENSE create mode 100644 devtools/shared/storage/vendor/JSON5_UPGRADING.md create mode 100644 devtools/shared/storage/vendor/json5.js create mode 100644 devtools/shared/storage/vendor/moz.build create mode 100644 devtools/shared/storage/vendor/stringvalidator/UPDATING.md create mode 100644 devtools/shared/storage/vendor/stringvalidator/moz.build create mode 100644 devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/head_stringvalidator.js create mode 100644 devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/test_sanitizers.js create mode 100644 devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/test_validators.js create mode 100644 devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/xpcshell.ini create mode 100644 devtools/shared/storage/vendor/stringvalidator/util/assert.js create mode 100644 devtools/shared/storage/vendor/stringvalidator/util/moz.build create mode 100644 devtools/shared/storage/vendor/stringvalidator/validator.js (limited to 'devtools/shared/storage') 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 /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..4b2899ebef --- /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.ini'] + +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>', + + "": + '<script> alert('xss&fun'); </script>', + + 'Backtick: `': + 'Backtick: `', + + 'Backslash: \\': + 'Backslash: \', + }, + }); + }); + + it('should unescape HTML', function () { + test({ + sanitizer: 'unescape', + expect: { + '<script> alert("xss&fun"); </script>': + '', + + '<script> alert('xss&fun'); </script>': + "", + + 'Backtick: `': + '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 ', + 'Some Name ', + 'Some Name ', + 'Some Name ', + 'Some Name ', + 'Some Name ', + 'Some Name ', + 'Some Name ', + 'Some Name ', + 'Some Middle Name ', + 'Name ', + 'Name', + ], + invalid: [ + 'invalidemail@', + 'invalid.com', + '@invalid.com', + 'foo@bar.com.', + 'foo@bar.co.uk.', + 'Some Name ', + 'Some Name ', + 'Some Name <@invalid.com>', + 'Some Name ', + 'Some Name ', + 'Some Name foo@bar.co.uk.>', + 'Some Name ', + '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 ', + 'Some Name ', + 'Some Name ', + 'Some Name ', + 'Some Name ', + 'Some Name ', + 'Some Name ', + 'Some Name ', + 'Some Name ', + 'Some Middle Name ', + 'Name ', + 'Name', + ], + 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 ', + 'Some Name ', + 'Some Name <@invalid.com>', + 'Some Name ', + 'Some Name ', + 'Some Name foo@bar.co.uk.>', + 'Some Name ', + '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//', + ], + }); + }); + + 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.ini b/devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/xpcshell.ini new file mode 100644 index 0000000000..1c90bf2baa --- /dev/null +++ b/devtools/shared/storage/vendor/stringvalidator/tests/xpcshell/xpcshell.ini @@ -0,0 +1,8 @@ +[DEFAULT] +tags = devtools +head = head_stringvalidator.js +firefox-appdir = browser +skip-if = toolkit == '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 + * + * 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, '&').replace(/"/g, '"').replace(/'/g, ''').replace(//g, '>').replace(/\//g, '/').replace(/\\/g, '\').replace(/`/g, '`'); + } + + function unescape(str) { + assertString(str); + return str.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, "'").replace(/</g, '<').replace(/>/g, '>').replace(///g, '/').replace(/`/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; +})); -- cgit v1.2.3