angular6-json-schema-form
Version:
Angular JSON Schema Form builder
884 lines • 111 kB
JavaScript
import isEqual from 'lodash-es/isEqual';
import { _executeAsyncValidators, _executeValidators, _mergeErrors, _mergeObjects, getType, hasValue, isArray, isBoolean, isDefined, isEmpty, isNumber, isString, isType, toJavaScriptType, toObservable, xor } from './validator.functions';
import { forEachCopy } from './utility.functions';
import { forkJoin } from 'rxjs';
import { jsonSchemaFormatTests } from './format-regex.constants';
import { map } from 'rxjs/operators';
/**
* 'JsonValidators' class
*
* Provides an extended set of validators to be used by form controls,
* compatible with standard JSON Schema validation options.
* http://json-schema.org/latest/json-schema-validation.html
*
* Note: This library is designed as a drop-in replacement for the Angular
* Validators library, and except for one small breaking change to the 'pattern'
* validator (described below) it can even be imported as a substitute, like so:
*
* import { JsonValidators as Validators } from 'json-validators';
*
* and it should work with existing code as a complete replacement.
*
* The one exception is the 'pattern' validator, which has been changed to
* matche partial values by default (the standard 'pattern' validator wrapped
* all patterns in '^' and '$', forcing them to always match an entire value).
* However, the old behavior can be restored by simply adding '^' and '$'
* around your patterns, or by passing an optional second parameter of TRUE.
* This change is to make the 'pattern' validator match the behavior of a
* JSON Schema pattern, which allows partial matches, rather than the behavior
* of an HTML input control pattern, which does not.
*
* This library replaces Angular's validators and combination functions
* with the following validators and transformation functions:
*
* Validators:
* For all formControls: required (*), type, enum, const
* For text formControls: minLength (*), maxLength (*), pattern (*), format
* For numeric formControls: maximum, exclusiveMaximum,
* minimum, exclusiveMinimum, multipleOf
* For formGroup objects: minProperties, maxProperties, dependencies
* For formArray arrays: minItems, maxItems, uniqueItems, contains
* Not used by JSON Schema: min (*), max (*), requiredTrue (*), email (*)
* (Validators originally included with Angular are maked with (*).)
*
* NOTE / TODO: The dependencies validator is not complete.
* NOTE / TODO: The contains validator is not complete.
*
* Validators not used by JSON Schema (but included for compatibility)
* and their JSON Schema equivalents:
*
* Angular validator | JSON Schema equivalent
* ------------------|-----------------------
* min(number) | minimum(number)
* max(number) | maximum(number)
* requiredTrue() | const(true)
* email() | format('email')
*
* Validator transformation functions:
* composeAnyOf, composeOneOf, composeAllOf, composeNot
* (Angular's original combination funciton, 'compose', is also included for
* backward compatibility, though it is functionally equivalent to composeAllOf,
* asside from its more generic error message.)
*
* All validators have also been extended to accept an optional second argument
* which, if passed a TRUE value, causes the validator to perform the opposite
* of its original finction. (This is used internally to enable 'not' and
* 'composeOneOf' to function and return useful error messages.)
*
* The 'required' validator has also been overloaded so that if called with
* a boolean parameter (or no parameters) it returns the original validator
* function (rather than executing it). However, if it is called with an
* AbstractControl parameter (as was previously required), it behaves
* exactly as before.
*
* This enables all validators (including 'required') to be constructed in
* exactly the same way, so they can be automatically applied using the
* equivalent key names and values taken directly from a JSON Schema.
*
* This source code is partially derived from Angular,
* which is Copyright (c) 2014-2017 Google, Inc.
* Use of this source code is therefore governed by the same MIT-style license
* that can be found in the LICENSE file at https://angular.io/license
*
* Original Angular Validators:
* https://github.com/angular/angular/blob/master/packages/forms/src/validators.ts
*/
export class JsonValidators {
static required(input) {
if (input === undefined) {
input = true;
}
switch (input) {
case true: // Return required function (do not execute it yet)
return (control, invert = false) => {
if (invert) {
return null;
} // if not required, always return valid
return hasValue(control.value) ? null : { 'required': true };
};
case false: // Do nothing (if field is not required, it is always valid)
return JsonValidators.nullValidator;
default: // Execute required function
return hasValue(input.value) ? null : { 'required': true };
}
}
/**
* 'type' validator
*
* Requires a control to only accept values of a specified type,
* or one of an array of types.
*
* Note: SchemaPrimitiveType = 'string'|'number'|'integer'|'boolean'|'null'
*
* // {SchemaPrimitiveType|SchemaPrimitiveType[]} type - type(s) to accept
* // {IValidatorFn}
*/
static type(requiredType) {
if (!hasValue(requiredType)) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
if (isEmpty(control.value)) {
return null;
}
const currentValue = control.value;
const isValid = isArray(requiredType) ?
requiredType.some(type => isType(currentValue, type)) :
isType(currentValue, requiredType);
return xor(isValid, invert) ?
null : { 'type': { requiredType, currentValue } };
};
}
/**
* 'enum' validator
*
* Requires a control to have a value from an enumerated list of values.
*
* Converts types as needed to allow string inputs to still correctly
* match number, boolean, and null enum values.
*
* // {any[]} allowedValues - array of acceptable values
* // {IValidatorFn}
*/
static enum(allowedValues) {
if (!isArray(allowedValues)) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
if (isEmpty(control.value)) {
return null;
}
const currentValue = control.value;
const isEqualVal = (enumValue, inputValue) => enumValue === inputValue ||
(isNumber(enumValue) && +inputValue === +enumValue) ||
(isBoolean(enumValue, 'strict') &&
toJavaScriptType(inputValue, 'boolean') === enumValue) ||
(enumValue === null && !hasValue(inputValue)) ||
isEqual(enumValue, inputValue);
const isValid = isArray(currentValue) ?
currentValue.every(inputValue => allowedValues.some(enumValue => isEqualVal(enumValue, inputValue))) :
allowedValues.some(enumValue => isEqualVal(enumValue, currentValue));
return xor(isValid, invert) ?
null : { 'enum': { allowedValues, currentValue } };
};
}
/**
* 'const' validator
*
* Requires a control to have a specific value.
*
* Converts types as needed to allow string inputs to still correctly
* match number, boolean, and null values.
*
* TODO: modify to work with objects
*
* // {any[]} requiredValue - required value
* // {IValidatorFn}
*/
static const(requiredValue) {
if (!hasValue(requiredValue)) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
if (isEmpty(control.value)) {
return null;
}
const currentValue = control.value;
const isEqualVal = (constValue, inputValue) => constValue === inputValue ||
isNumber(constValue) && +inputValue === +constValue ||
isBoolean(constValue, 'strict') &&
toJavaScriptType(inputValue, 'boolean') === constValue ||
constValue === null && !hasValue(inputValue);
const isValid = isEqualVal(requiredValue, currentValue);
return xor(isValid, invert) ?
null : { 'const': { requiredValue, currentValue } };
};
}
/**
* 'minLength' validator
*
* Requires a control's text value to be greater than a specified length.
*
* // {number} minimumLength - minimum allowed string length
* // {boolean = false} invert - instead return error object only if valid
* // {IValidatorFn}
*/
static minLength(minimumLength) {
if (!hasValue(minimumLength)) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
if (isEmpty(control.value)) {
return null;
}
const currentLength = isString(control.value) ? control.value.length : 0;
const isValid = currentLength >= minimumLength;
return xor(isValid, invert) ?
null : { 'minLength': { minimumLength, currentLength } };
};
}
/**
* 'maxLength' validator
*
* Requires a control's text value to be less than a specified length.
*
* // {number} maximumLength - maximum allowed string length
* // {boolean = false} invert - instead return error object only if valid
* // {IValidatorFn}
*/
static maxLength(maximumLength) {
if (!hasValue(maximumLength)) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
const currentLength = isString(control.value) ? control.value.length : 0;
const isValid = currentLength <= maximumLength;
return xor(isValid, invert) ?
null : { 'maxLength': { maximumLength, currentLength } };
};
}
/**
* 'pattern' validator
*
* Note: NOT the same as Angular's default pattern validator.
*
* Requires a control's value to match a specified regular expression pattern.
*
* This validator changes the behavior of default pattern validator
* by replacing RegExp(`^${pattern}$`) with RegExp(`${pattern}`),
* which allows for partial matches.
*
* To return to the default funcitonality, and match the entire string,
* pass TRUE as the optional second parameter.
*
* // {string} pattern - regular expression pattern
* // {boolean = false} wholeString - match whole value string?
* // {IValidatorFn}
*/
static pattern(pattern, wholeString = false) {
if (!hasValue(pattern)) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
if (isEmpty(control.value)) {
return null;
}
let regex;
let requiredPattern;
if (typeof pattern === 'string') {
requiredPattern = (wholeString) ? `^${pattern}$` : pattern;
regex = new RegExp(requiredPattern);
}
else {
requiredPattern = pattern.toString();
regex = pattern;
}
const currentValue = control.value;
const isValid = isString(currentValue) ? regex.test(currentValue) : false;
return xor(isValid, invert) ?
null : { 'pattern': { requiredPattern, currentValue } };
};
}
/**
* 'format' validator
*
* Requires a control to have a value of a certain format.
*
* This validator currently checks the following formsts:
* date, time, date-time, email, hostname, ipv4, ipv6,
* uri, uri-reference, uri-template, url, uuid, color,
* json-pointer, relative-json-pointer, regex
*
* Fast format regular expressions copied from AJV:
* https://github.com/epoberezkin/ajv/blob/master/lib/compile/formats.js
*
* // {JsonSchemaFormatNames} requiredFormat - format to check
* // {IValidatorFn}
*/
static format(requiredFormat) {
if (!hasValue(requiredFormat)) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
if (isEmpty(control.value)) {
return null;
}
let isValid;
const currentValue = control.value;
if (isString(currentValue)) {
const formatTest = jsonSchemaFormatTests[requiredFormat];
if (typeof formatTest === 'object') {
isValid = formatTest.test(currentValue);
}
else if (typeof formatTest === 'function') {
isValid = formatTest(currentValue);
}
else {
console.error(`format validator error: "${requiredFormat}" is not a recognized format.`);
isValid = true;
}
}
else {
// Allow JavaScript Date objects
isValid = ['date', 'time', 'date-time'].includes(requiredFormat) &&
Object.prototype.toString.call(currentValue) === '[object Date]';
}
return xor(isValid, invert) ?
null : { 'format': { requiredFormat, currentValue } };
};
}
/**
* 'minimum' validator
*
* Requires a control's numeric value to be greater than or equal to
* a minimum amount.
*
* Any non-numeric value is also valid (according to the HTML forms spec,
* a non-numeric value doesn't have a minimum).
* https://www.w3.org/TR/html5/forms.html#attr-input-max
*
* // {number} minimum - minimum allowed value
* // {IValidatorFn}
*/
static minimum(minimumValue) {
if (!hasValue(minimumValue)) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
if (isEmpty(control.value)) {
return null;
}
const currentValue = control.value;
const isValid = !isNumber(currentValue) || currentValue >= minimumValue;
return xor(isValid, invert) ?
null : { 'minimum': { minimumValue, currentValue } };
};
}
/**
* 'exclusiveMinimum' validator
*
* Requires a control's numeric value to be less than a maximum amount.
*
* Any non-numeric value is also valid (according to the HTML forms spec,
* a non-numeric value doesn't have a maximum).
* https://www.w3.org/TR/html5/forms.html#attr-input-max
*
* // {number} exclusiveMinimumValue - maximum allowed value
* // {IValidatorFn}
*/
static exclusiveMinimum(exclusiveMinimumValue) {
if (!hasValue(exclusiveMinimumValue)) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
if (isEmpty(control.value)) {
return null;
}
const currentValue = control.value;
const isValid = !isNumber(currentValue) || +currentValue < exclusiveMinimumValue;
return xor(isValid, invert) ?
null : { 'exclusiveMinimum': { exclusiveMinimumValue, currentValue } };
};
}
/**
* 'maximum' validator
*
* Requires a control's numeric value to be less than or equal to
* a maximum amount.
*
* Any non-numeric value is also valid (according to the HTML forms spec,
* a non-numeric value doesn't have a maximum).
* https://www.w3.org/TR/html5/forms.html#attr-input-max
*
* // {number} maximumValue - maximum allowed value
* // {IValidatorFn}
*/
static maximum(maximumValue) {
if (!hasValue(maximumValue)) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
if (isEmpty(control.value)) {
return null;
}
const currentValue = control.value;
const isValid = !isNumber(currentValue) || +currentValue <= maximumValue;
return xor(isValid, invert) ?
null : { 'maximum': { maximumValue, currentValue } };
};
}
/**
* 'exclusiveMaximum' validator
*
* Requires a control's numeric value to be less than a maximum amount.
*
* Any non-numeric value is also valid (according to the HTML forms spec,
* a non-numeric value doesn't have a maximum).
* https://www.w3.org/TR/html5/forms.html#attr-input-max
*
* // {number} exclusiveMaximumValue - maximum allowed value
* // {IValidatorFn}
*/
static exclusiveMaximum(exclusiveMaximumValue) {
if (!hasValue(exclusiveMaximumValue)) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
if (isEmpty(control.value)) {
return null;
}
const currentValue = control.value;
const isValid = !isNumber(currentValue) || +currentValue < exclusiveMaximumValue;
return xor(isValid, invert) ?
null : { 'exclusiveMaximum': { exclusiveMaximumValue, currentValue } };
};
}
/**
* 'multipleOf' validator
*
* Requires a control to have a numeric value that is a multiple
* of a specified number.
*
* // {number} multipleOfValue - number value must be a multiple of
* // {IValidatorFn}
*/
static multipleOf(multipleOfValue) {
if (!hasValue(multipleOfValue)) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
if (isEmpty(control.value)) {
return null;
}
const currentValue = control.value;
const isValid = isNumber(currentValue) &&
currentValue % multipleOfValue === 0;
return xor(isValid, invert) ?
null : { 'multipleOf': { multipleOfValue, currentValue } };
};
}
/**
* 'minProperties' validator
*
* Requires a form group to have a minimum number of properties (i.e. have
* values entered in a minimum number of controls within the group).
*
* // {number} minimumProperties - minimum number of properties allowed
* // {IValidatorFn}
*/
static minProperties(minimumProperties) {
if (!hasValue(minimumProperties)) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
if (isEmpty(control.value)) {
return null;
}
const currentProperties = Object.keys(control.value).length || 0;
const isValid = currentProperties >= minimumProperties;
return xor(isValid, invert) ?
null : { 'minProperties': { minimumProperties, currentProperties } };
};
}
/**
* 'maxProperties' validator
*
* Requires a form group to have a maximum number of properties (i.e. have
* values entered in a maximum number of controls within the group).
*
* Note: Has no effect if the form group does not contain more than the
* maximum number of controls.
*
* // {number} maximumProperties - maximum number of properties allowed
* // {IValidatorFn}
*/
static maxProperties(maximumProperties) {
if (!hasValue(maximumProperties)) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
const currentProperties = Object.keys(control.value).length || 0;
const isValid = currentProperties <= maximumProperties;
return xor(isValid, invert) ?
null : { 'maxProperties': { maximumProperties, currentProperties } };
};
}
/**
* 'dependencies' validator
*
* Requires the controls in a form group to meet additional validation
* criteria, depending on the values of other controls in the group.
*
* Examples:
* https://spacetelescope.github.io/understanding-json-schema/reference/object.html#dependencies
*
* // {any} dependencies - required dependencies
* // {IValidatorFn}
*/
static dependencies(dependencies) {
if (getType(dependencies) !== 'object' || isEmpty(dependencies)) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
if (isEmpty(control.value)) {
return null;
}
const allErrors = _mergeObjects(forEachCopy(dependencies, (value, requiringField) => {
if (!hasValue(control.value[requiringField])) {
return null;
}
let requiringFieldErrors = {};
let requiredFields;
let properties = {};
if (getType(dependencies[requiringField]) === 'array') {
requiredFields = dependencies[requiringField];
}
else if (getType(dependencies[requiringField]) === 'object') {
requiredFields = dependencies[requiringField]['required'] || [];
properties = dependencies[requiringField]['properties'] || {};
}
// Validate property dependencies
for (const requiredField of requiredFields) {
if (xor(!hasValue(control.value[requiredField]), invert)) {
requiringFieldErrors[requiredField] = { 'required': true };
}
}
// Validate schema dependencies
requiringFieldErrors = _mergeObjects(requiringFieldErrors, forEachCopy(properties, (requirements, requiredField) => {
const requiredFieldErrors = _mergeObjects(forEachCopy(requirements, (requirement, parameter) => {
let validator = null;
if (requirement === 'maximum' || requirement === 'minimum') {
const exclusive = !!requirements['exclusiveM' + requirement.slice(1)];
validator = JsonValidators[requirement](parameter, exclusive);
}
else if (typeof JsonValidators[requirement] === 'function') {
validator = JsonValidators[requirement](parameter);
}
return !isDefined(validator) ?
null : validator(control.value[requiredField]);
}));
return isEmpty(requiredFieldErrors) ?
null : { [requiredField]: requiredFieldErrors };
}));
return isEmpty(requiringFieldErrors) ?
null : { [requiringField]: requiringFieldErrors };
}));
return isEmpty(allErrors) ? null : allErrors;
};
}
/**
* 'minItems' validator
*
* Requires a form array to have a minimum number of values.
*
* // {number} minimumItems - minimum number of items allowed
* // {IValidatorFn}
*/
static minItems(minimumItems) {
if (!hasValue(minimumItems)) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
if (isEmpty(control.value)) {
return null;
}
const currentItems = isArray(control.value) ? control.value.length : 0;
const isValid = currentItems >= minimumItems;
return xor(isValid, invert) ?
null : { 'minItems': { minimumItems, currentItems } };
};
}
/**
* 'maxItems' validator
*
* Requires a form array to have a maximum number of values.
*
* // {number} maximumItems - maximum number of items allowed
* // {IValidatorFn}
*/
static maxItems(maximumItems) {
if (!hasValue(maximumItems)) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
const currentItems = isArray(control.value) ? control.value.length : 0;
const isValid = currentItems <= maximumItems;
return xor(isValid, invert) ?
null : { 'maxItems': { maximumItems, currentItems } };
};
}
/**
* 'uniqueItems' validator
*
* Requires values in a form array to be unique.
*
* // {boolean = true} unique? - true to validate, false to disable
* // {IValidatorFn}
*/
static uniqueItems(unique = true) {
if (!unique) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
if (isEmpty(control.value)) {
return null;
}
const sorted = control.value.slice().sort();
const duplicateItems = [];
for (let i = 1; i < sorted.length; i++) {
if (sorted[i - 1] === sorted[i] && duplicateItems.includes(sorted[i])) {
duplicateItems.push(sorted[i]);
}
}
const isValid = !duplicateItems.length;
return xor(isValid, invert) ?
null : { 'uniqueItems': { duplicateItems } };
};
}
/**
* 'contains' validator
*
* TODO: Complete this validator
*
* Requires values in a form array to be unique.
*
* // {boolean = true} unique? - true to validate, false to disable
* // {IValidatorFn}
*/
static contains(requiredItem = true) {
if (!requiredItem) {
return JsonValidators.nullValidator;
}
return (control, invert = false) => {
if (isEmpty(control.value) || !isArray(control.value)) {
return null;
}
const currentItems = control.value;
// const isValid = currentItems.some(item =>
//
// );
const isValid = true;
return xor(isValid, invert) ?
null : { 'contains': { requiredItem, currentItems } };
};
}
/**
* No-op validator. Included for backward compatibility.
*/
static nullValidator(control) {
return null;
}
/**
* Validator transformation functions:
* composeAnyOf, composeOneOf, composeAllOf, composeNot,
* compose, composeAsync
*
* TODO: Add composeAnyOfAsync, composeOneOfAsync,
* composeAllOfAsync, composeNotAsync
*/
/**
* 'composeAnyOf' validator combination function
*
* Accepts an array of validators and returns a single validator that
* evaluates to valid if any one or more of the submitted validators are
* valid. If every validator is invalid, it returns combined errors from
* all validators.
*
* // {IValidatorFn[]} validators - array of validators to combine
* // {IValidatorFn} - single combined validator function
*/
static composeAnyOf(validators) {
if (!validators) {
return null;
}
const presentValidators = validators.filter(isDefined);
if (presentValidators.length === 0) {
return null;
}
return (control, invert = false) => {
const arrayOfErrors = _executeValidators(control, presentValidators, invert).filter(isDefined);
const isValid = validators.length > arrayOfErrors.length;
return xor(isValid, invert) ?
null : _mergeObjects(...arrayOfErrors, { 'anyOf': !invert });
};
}
/**
* 'composeOneOf' validator combination function
*
* Accepts an array of validators and returns a single validator that
* evaluates to valid only if exactly one of the submitted validators
* is valid. Otherwise returns combined information from all validators,
* both valid and invalid.
*
* // {IValidatorFn[]} validators - array of validators to combine
* // {IValidatorFn} - single combined validator function
*/
static composeOneOf(validators) {
if (!validators) {
return null;
}
const presentValidators = validators.filter(isDefined);
if (presentValidators.length === 0) {
return null;
}
return (control, invert = false) => {
const arrayOfErrors = _executeValidators(control, presentValidators);
const validControls = validators.length - arrayOfErrors.filter(isDefined).length;
const isValid = validControls === 1;
if (xor(isValid, invert)) {
return null;
}
const arrayOfValids = _executeValidators(control, presentValidators, invert);
return _mergeObjects(...arrayOfErrors, ...arrayOfValids, { 'oneOf': !invert });
};
}
/**
* 'composeAllOf' validator combination function
*
* Accepts an array of validators and returns a single validator that
* evaluates to valid only if all the submitted validators are individually
* valid. Otherwise it returns combined errors from all invalid validators.
*
* // {IValidatorFn[]} validators - array of validators to combine
* // {IValidatorFn} - single combined validator function
*/
static composeAllOf(validators) {
if (!validators) {
return null;
}
const presentValidators = validators.filter(isDefined);
if (presentValidators.length === 0) {
return null;
}
return (control, invert = false) => {
const combinedErrors = _mergeErrors(_executeValidators(control, presentValidators, invert));
const isValid = combinedErrors === null;
return (xor(isValid, invert)) ?
null : _mergeObjects(combinedErrors, { 'allOf': !invert });
};
}
/**
* 'composeNot' validator inversion function
*
* Accepts a single validator function and inverts its result.
* Returns valid if the submitted validator is invalid, and
* returns invalid if the submitted validator is valid.
* (Note: this function can itself be inverted
* - e.g. composeNot(composeNot(validator)) -
* but this can be confusing and is therefore not recommended.)
*
* // {IValidatorFn[]} validators - validator(s) to invert
* // {IValidatorFn} - new validator function that returns opposite result
*/
static composeNot(validator) {
if (!validator) {
return null;
}
return (control, invert = false) => {
if (isEmpty(control.value)) {
return null;
}
const error = validator(control, !invert);
const isValid = error === null;
return (xor(isValid, invert)) ?
null : _mergeObjects(error, { 'not': !invert });
};
}
/**
* 'compose' validator combination function
*
* // {IValidatorFn[]} validators - array of validators to combine
* // {IValidatorFn} - single combined validator function
*/
static compose(validators) {
if (!validators) {
return null;
}
const presentValidators = validators.filter(isDefined);
if (presentValidators.length === 0) {
return null;
}
return (control, invert = false) => _mergeErrors(_executeValidators(control, presentValidators, invert));
}
/**
* 'composeAsync' async validator combination function
*
* // {AsyncIValidatorFn[]} async validators - array of async validators
* // {AsyncIValidatorFn} - single combined async validator function
*/
static composeAsync(validators) {
if (!validators) {
return null;
}
const presentValidators = validators.filter(isDefined);
if (presentValidators.length === 0) {
return null;
}
return (control) => {
const observables = _executeAsyncValidators(control, presentValidators).map(toObservable);
return map.call(forkJoin(observables), _mergeErrors);
};
}
// Additional angular validators (not used by Angualr JSON Schema Form)
// From https://github.com/angular/angular/blob/master/packages/forms/src/validators.ts
/**
* Validator that requires controls to have a value greater than a number.
*/
static min(min) {
if (!hasValue(min)) {
return JsonValidators.nullValidator;
}
return (control) => {
// don't validate empty values to allow optional controls
if (isEmpty(control.value) || isEmpty(min)) {
return null;
}
const value = parseFloat(control.value);
const actual = control.value;
// Controls with NaN values after parsing should be treated as not having a
// minimum, per the HTML forms spec: https://www.w3.org/TR/html5/forms.html#attr-input-min
return isNaN(value) || value >= min ? null : { 'min': { min, actual } };
};
}
/**
* Validator that requires controls to have a value less than a number.
*/
static max(max) {
if (!hasValue(max)) {
return JsonValidators.nullValidator;
}
return (control) => {
// don't validate empty values to allow optional controls
if (isEmpty(control.value) || isEmpty(max)) {
return null;
}
const value = parseFloat(control.value);
const actual = control.value;
// Controls with NaN values after parsing should be treated as not having a
// maximum, per the HTML forms spec: https://www.w3.org/TR/html5/forms.html#attr-input-max
return isNaN(value) || value <= max ? null : { 'max': { max, actual } };
};
}
/**
* Validator that requires control value to be true.
*/
static requiredTrue(control) {
if (!control) {
return JsonValidators.nullValidator;
}
return control.value === true ? null : { 'required': true };
}
/**
* Validator that performs email validation.
*/
static email(control) {
if (!control) {
return JsonValidators.nullValidator;
}
const EMAIL_REGEXP =
// tslint:disable-next-line:max-line-length
/^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+(\.[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/;
return EMAIL_REGEXP.test(control.value) ? null : { 'email': true };
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoianNvbi52YWxpZGF0b3JzLmpzIiwic291cmNlUm9vdCI6Im5nOi8vYW5ndWxhcjYtanNvbi1zY2hlbWEtZm9ybS8iLCJzb3VyY2VzIjpbImxpYi9zaGFyZWQvanNvbi52YWxpZGF0b3JzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sT0FBTyxNQUFNLG1CQUFtQixDQUFDO0FBQ3hDLE9BQU8sRUFDTCx1QkFBdUIsRUFDdkIsa0JBQWtCLEVBQ2xCLFlBQVksRUFDWixhQUFhLEVBRWIsT0FBTyxFQUNQLFFBQVEsRUFDUixPQUFPLEVBQ1AsU0FBUyxFQUNULFNBQVMsRUFDVCxPQUFPLEVBQ1AsUUFBUSxFQUNSLFFBQVEsRUFDUixNQUFNLEVBR04sZ0JBQWdCLEVBQ2hCLFlBQVksRUFDWixHQUFHLEVBQ0YsTUFBTSx1QkFBdUIsQ0FBQztBQUVqQyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDbEQsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUNoQyxPQUFPLEVBQXlCLHFCQUFxQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDeEYsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBSXJDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0E4RUc7QUFDSCxNQUFNLE9BQU8sY0FBYztJQXNDekIsTUFBTSxDQUFDLFFBQVEsQ0FBQyxLQUErQjtRQUM3QyxJQUFJLEtBQUssS0FBSyxTQUFTLEVBQUU7WUFBRSxLQUFLLEdBQUcsSUFBSSxDQUFDO1NBQUU7UUFDMUMsUUFBUSxLQUFLLEVBQUU7WUFDYixLQUFLLElBQUksRUFBRSxtREFBbUQ7Z0JBQzVELE9BQU8sQ0FBQyxPQUF3QixFQUFFLE1BQU0sR0FBRyxLQUFLLEVBQXlCLEVBQUU7b0JBQ3pFLElBQUksTUFBTSxFQUFFO3dCQUFFLE9BQU8sSUFBSSxDQUFDO3FCQUFFLENBQUMsdUNBQXVDO29CQUNwRSxPQUFPLFFBQVEsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLENBQUM7Z0JBQy9ELENBQUMsQ0FBQztZQUNKLEtBQUssS0FBSyxFQUFFLDREQUE0RDtnQkFDdEUsT0FBTyxjQUFjLENBQUMsYUFBYSxDQUFDO1lBQ3RDLFNBQVMsNEJBQTRCO2dCQUNuQyxPQUFPLFFBQVEsQ0FBbUIsS0FBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxDQUFDO1NBQ2pGO0lBQ0gsQ0FBQztJQUVEOzs7Ozs7Ozs7O09BVUc7SUFDSCxNQUFNLENBQUMsSUFBSSxDQUFDLFlBQXVEO1FBQ2pFLElBQUksQ0FBQyxRQUFRLENBQUMsWUFBWSxDQUFDLEVBQUU7WUFBRSxPQUFPLGNBQWMsQ0FBQyxhQUFhLENBQUM7U0FBRTtRQUNyRSxPQUFPLENBQUMsT0FBd0IsRUFBRSxNQUFNLEdBQUcsS0FBSyxFQUF5QixFQUFFO1lBQ3pFLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRTtnQkFBRSxPQUFPLElBQUksQ0FBQzthQUFFO1lBQzVDLE1BQU0sWUFBWSxHQUFRLE9BQU8sQ0FBQyxLQUFLLENBQUM7WUFDeEMsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUM7Z0JBQ2IsWUFBYSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxZQUFZLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUNoRixNQUFNLENBQUMsWUFBWSxFQUF1QixZQUFZLENBQUMsQ0FBQztZQUMxRCxPQUFPLEdBQUcsQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztnQkFDM0IsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxFQUFFLFlBQVksRUFBRSxZQUFZLEVBQUUsRUFBRSxDQUFDO1FBQ3RELENBQUMsQ0FBQztJQUNKLENBQUM7SUFFRDs7Ozs7Ozs7OztPQVVHO0lBQ0gsTUFBTSxDQUFDLElBQUksQ0FBQyxhQUFvQjtRQUM5QixJQUFJLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxFQUFFO1lBQUUsT0FBTyxjQUFjLENBQUMsYUFBYSxDQUFDO1NBQUU7UUFDckUsT0FBTyxDQUFDLE9BQXdCLEVBQUUsTUFBTSxHQUFHLEtBQUssRUFBeUIsRUFBRTtZQUN6RSxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEVBQUU7Z0JBQUUsT0FBTyxJQUFJLENBQUM7YUFBRTtZQUM1QyxNQUFNLFlBQVksR0FBUSxPQUFPLENBQUMsS0FBSyxDQUFDO1lBQ3hDLE1BQU0sVUFBVSxHQUFHLENBQUMsU0FBUyxFQUFFLFVBQVUsRUFBRSxFQUFFLENBQzNDLFNBQVMsS0FBSyxVQUFVO2dCQUN4QixDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFVBQVUsS0FBSyxDQUFDLFNBQVMsQ0FBQztnQkFDbkQsQ0FBQyxTQUFTLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQztvQkFDN0IsZ0JBQWdCLENBQUMsVUFBVSxFQUFFLFNBQVMsQ0FBQyxLQUFLLFNBQVMsQ0FBQztnQkFDeEQsQ0FBQyxTQUFTLEtBQUssSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxDQUFDO2dCQUM3QyxPQUFPLENBQUMsU0FBUyxFQUFFLFVBQVUsQ0FBQyxDQUFDO1lBQ2pDLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDO2dCQUNyQyxZQUFZLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUM5RCxVQUFVLENBQUMsU0FBUyxFQUFFLFVBQVUsQ0FBQyxDQUNsQyxDQUFDLENBQUMsQ0FBQztnQkFDSixhQUFhLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLFNBQVMsRUFBRSxZQUFZLENBQUMsQ0FBQyxDQUFDO1lBQ3ZFLE9BQU8sR0FBRyxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO2dCQUMzQixJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsTUFBTSxFQUFFLEVBQUUsYUFBYSxFQUFFLFlBQVksRUFBRSxFQUFFLENBQUM7UUFDdkQsQ0FBQyxDQUFDO0lBQ0osQ0FBQztJQUVEOzs7Ozs7Ozs7Ozs7T0FZRztJQUNILE1BQU0sQ0FBQyxLQUFLLENBQUMsYUFBa0I7UUFDN0IsSUFBSSxDQUFDLFFBQVEsQ0FBQyxhQUFhLENBQUMsRUFBRTtZQUFFLE9BQU8sY0FBYyxDQUFDLGFBQWEsQ0FBQztTQUFFO1FBQ3RFLE9BQU8sQ0FBQyxPQUF3QixFQUFFLE1BQU0sR0FBRyxLQUFLLEVBQXlCLEVBQUU7WUFDekUsSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxFQUFFO2dCQUFFLE9BQU8sSUFBSSxDQUFDO2FBQUU7WUFDNUMsTUFBTSxZQUFZLEdBQVEsT0FBTyxDQUFDLEtBQUssQ0FBQztZQUN4QyxNQUFNLFVBQVUsR0FBRyxDQUFDLFVBQVUsRUFBRSxVQUFVLEVBQUUsRUFBRSxDQUM1QyxVQUFVLEtBQUssVUFBVTtnQkFDekIsUUFBUSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsVUFBVSxLQUFLLENBQUMsVUFBVTtnQkFDbkQsU0FBUyxDQUFDLFVBQVUsRUFBRSxRQUFRLENBQUM7b0JBQzdCLGdCQUFnQixDQUFDLFVBQVUsRUFBRSxTQUFTLENBQUMsS0FBSyxVQUFVO2dCQUN4RCxVQUFVLEtBQUssSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBQy9DLE1BQU0sT0FBTyxHQUFHLFVBQVUsQ0FBQyxhQUFhLEVBQUUsWUFBWSxDQUFDLENBQUM7WUFDeEQsT0FBTyxHQUFHLENBQUMsT0FBTyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7Z0JBQzNCLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxPQUFPLEVBQUUsRUFBRSxhQUFhLEVBQUUsWUFBWSxFQUFFLEVBQUUsQ0FBQztRQUN4RCxDQUFDLENBQUM7SUFDSixDQUFDO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSCxNQUFNLENBQUMsU0FBUyxDQUFDLGFBQXFCO1FBQ3BDLElBQUksQ0FBQyxRQUFRLENBQUMsYUFBYSxDQUFDLEVBQUU7WUFBRSxPQUFPLGNBQWMsQ0FBQyxhQUFhLENBQUM7U0FBRTtRQUN0RSxPQUFPLENBQUMsT0FBd0IsRUFBRSxNQUFNLEdBQUcsS0FBSyxFQUF5QixFQUFFO1lBQ3pFLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRTtnQkFBRSxPQUFPLElBQUksQ0FBQzthQUFFO1lBQzVDLE1BQU0sYUFBYSxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDekUsTUFBTSxPQUFPLEdBQUcsYUFBYSxJQUFJLGFBQWEsQ0FBQztZQUMvQyxPQUFPLEdBQUcsQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztnQkFDM0IsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLFdBQVcsRUFBRSxFQUFFLGFBQWEsRUFBRSxhQUFhLEVBQUUsRUFBRSxDQUFDO1FBQzdELENBQUMsQ0FBQztJQUNKLENBQUM7SUFFRDs7Ozs7Ozs7T0FRRztJQUNILE1BQU0sQ0FBQyxTQUFTLENBQUMsYUFBcUI7UUFDcEMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxhQUFhLENBQUMsRUFBRTtZQUFFLE9BQU8sY0FBYyxDQUFDLGFBQWEsQ0FBQztTQUFFO1FBQ3RFLE9BQU8sQ0FBQyxPQUF3QixFQUFFLE1BQU0sR0FBRyxLQUFLLEVBQXlCLEVBQUU7WUFDekUsTUFBTSxhQUFhLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUN6RSxNQUFNLE9BQU8sR0FBRyxhQUFhLElBQUksYUFBYSxDQUFDO1lBQy9DLE9BQU8sR0FBRyxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO2dCQUMzQixJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsV0FBVyxFQUFFLEVBQUUsYUFBYSxFQUFFLGFBQWEsRUFBRSxFQUFFLENBQUM7UUFDN0QsQ0FBQyxDQUFDO0lBQ0osQ0FBQztJQUVEOzs7Ozs7Ozs7Ozs7Ozs7OztPQWlCRztJQUNILE1BQU0sQ0FBQyxPQUFPLENBQUMsT0FBc0IsRUFBRSxXQUFXLEdBQUcsS0FBSztRQUN4RCxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxFQUFFO1lBQUUsT0FBTyxjQUFjLENBQUMsYUFBYSxDQUFDO1NBQUU7UUFDaEUsT0FBTyxDQUFDLE9BQXdCLEVBQUUsTUFBTSxHQUFHLEtBQUssRUFBeUIsRUFBRTtZQUN6RSxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEVBQUU7Z0JBQUUsT0FBTyxJQUFJLENBQUM7YUFBRTtZQUM1QyxJQUFJLEtBQWEsQ0FBQztZQUNsQixJQUFJLGVBQXVCLENBQUM7WUFDNUIsSUFBSSxPQUFPLE9BQU8sS0FBSyxRQUFRLEVBQUU7Z0JBQy9CLGVBQWUsR0FBRyxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLE9BQU8sR0FBRyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUM7Z0JBQzNELEtBQUssR0FBRyxJQUFJLE1BQU0sQ0FBQyxlQUFlLENBQUMsQ0FBQzthQUNyQztpQkFBTTtnQkFDTCxlQUFlLEdBQUcsT0FBTyxDQUFDLFFBQVEsRUFBRSxDQUFDO2dCQUNyQyxLQUFLLEdBQUcsT0FBTyxDQUFDO2FBQ2pCO1lBQ0QsTUFBTSxZQUFZLEdBQVcsT0FBTyxDQUFDLEtBQUssQ0FBQztZQUMzQyxNQUFNLE9BQU8sR0FBRyxRQUFRLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQztZQUMxRSxPQUFPLEdBQUcsQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztnQkFDM0IsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLFNBQVMsRUFBRSxFQUFFLGVBQWUsRUFBRSxZQUFZLEVBQUUsRUFBRSxDQUFDO1FBQzVELENBQUMsQ0FBQztJQUNKLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7Ozs7O09BZUc7SUFDSCxNQUFNLENBQUMsTUFBTSxDQUFDLGNBQXFDO1FBQ2pELElBQUksQ0FBQyxRQUFRLENBQUMsY0FBYyxDQUFDLEVBQUU7WUFBRSxPQUFPLGNBQWMsQ0FBQyxhQUFhLENBQUM7U0FBRTtRQUN2RSxPQUFPLENBQUMsT0FBd0IsRUFBRSxNQUFNLEdBQUcsS0FBSyxFQUF5QixFQUFFO1lBQ3pFLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRTtnQkFBRSxPQUFPLElBQUksQ0FBQzthQUFFO1lBQzVDLElBQUksT0FBZ0IsQ0FBQztZQUNyQixNQUFNLFlBQVksR0FBZ0IsT0FBTyxDQUFDLEtBQUssQ0FBQztZQUNoRCxJQUFJLFFBQVEsQ0FBQyxZQUFZLENBQUMsRUFBRTtnQkFDMUIsTUFBTSxVQUFVLEdBQW9CLHFCQUFxQixDQUFDLGNBQWMsQ0FBQyxDQUFDO2dCQUMxRSxJQUFJLE9BQU8sVUFBVSxLQUFLLFFBQVEsRUFBRTtvQkFDbEMsT0FBTyxHQUFZLFVBQVcsQ0FBQyxJQUFJLENBQVMsWUFBWSxDQUFDLENBQUM7aUJBQzNEO3FCQUFNLElBQUksT0FBTyxVQUFVLEtBQUssVUFBVSxFQUFFO29CQUMzQyxPQUFPLEdBQWMsVUFBVyxDQUFTLFlBQVksQ0FBQyxDQUFDO2lCQUN4RDtxQkFBTTtvQkFDTCxPQUFPLENBQUMsS0FBSyxDQUFDLDRCQUE0QixjQUFjLCtCQUErQixDQUFDLENBQUM7b0JBQ3pGLE9BQU8sR0FBRyxJQUFJLENBQUM7aUJBQ2hCO2FBQ0Y7aUJBQU07Z0JBQ0wsZ0NBQWdDO2dCQUNoQyxPQUFPLEdBQUcsQ0FBQyxNQUFNLEVBQUUsTUFBTSxFQUFFLFdBQVcsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxjQUFjLENBQUM7b0JBQzlELE1BQU0sQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsS0FBSyxlQUFlLENBQUM7YUFDcEU7WUFDRCxPQUFPLEdBQUcsQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztnQkFDM0IsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLFFBQVEsRUFBRSxFQUFFLGNBQWMsRUFBRSxZQUFZLEVBQUUsRUFBRSxDQUFDO1FBQzFELENBQUMsQ0FBQztJQUNKLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7O09BWUc7SUFDSCxNQUFNLENBQUMsT0FBTyxDQUFDLFlBQW9CO1FBQ2pDLElBQUksQ0FBQyxRQUFRLENBQUMsWUFBWSxDQUFDLEVBQUU7WUFBRSxPQUFPLGNBQWMsQ0FBQyxhQUFhLENBQUM7U0FBRTtRQUNyRSxPQUFPLENBQUMsT0FBd0IsRUFBRSxNQUFNLEdBQUcsS0FBSyxFQUF5QixFQUFFO1lBQ3pFLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRTtnQkFBRSxPQUFPLElBQUksQ0FBQzthQUFFO1lBQzVDLE1BQU0sWUFBWSxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUM7WUFDbkMsTUFBTSxPQUFPLEdBQUcsQ0FBQyxRQUFRLENBQUMsWUFBWSxDQUFDLElBQUksWUFBWSxJQUFJLFlBQVksQ0FBQztZQUN4RSxPQUFPLEdBQUcsQ0FBQyxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztnQkFDM0IsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLFNBQVMsRUFBRSxFQUFFLFlBQVksRUFBRSxZQUFZLEVBQUUsRUFBRSxDQUFDO1FBQ3pELENBQUMsQ0FBQztJQUNKLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7T0FXRztJQUNILE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxxQkFBNkI7UUFDbkQsSUFBSSxDQUFDLFFBQVEsQ0FBQyxxQkFBcUIsQ0FBQyxFQUFFO1lBQUUsT0FBTyxjQUFjLENBQUMsYUFBYSxDQUFDO1NBQUU7UUFDOUUsT0FBTyxDQUFDLE9BQXdCLEVBQUUsTUFBTSxHQUFHLEtBQUssRUFBeUIsRUFBRTtZQUN6RSxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEVBQUU7Z0JBQUUsT0FBTyxJQUFJLENBQUM7YUFBRTtZQUM1QyxNQUFNLFlBQVksR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDO1lBQ25DLE1BQU0sT0FBTyxHQUFHLENBQUMsUUFBUSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsWUFBWSxHQUFHLHFCQUFxQixDQUFDO1lBQ2pGLE9BQU8sR0FBRyxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO2dCQUMzQixJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsa0JBQWtCLEVBQUUsRUFBRSxxQkFBcUIsRUFBRSxZQUFZLEVBQUUsRUFBRSxDQUFDO1FBQzNFLENBQUMsQ0FBQztJQUNKLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7O09BWUc7SUFDSCxNQUFNLENBQUMsT0FBTyxDQUFDLFlBQW9CO1FBQ2pDLElBQUksQ0FBQyxRQUFRLENBQUMsWUFBWSxDQUFDLEVBQUU7WUFBRSxPQUFPLGNBQWMsQ0FBQyxhQUFhLENBQUM7U0FBRTtRQUNyRSxPQUFPLENBQUMsT0FBd0IsRUFBRSxNQUFNLEdBQUcsS0FBSyxFQUF5QixFQUFFO1lBQ3pFLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRTtnQkFBRSxPQUFPLElBQUksQ0FBQzthQUFFO1lBQzVDLE1BQU0sWUFBWSxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUM7WUFDbkMsTUFBTSxPQUFPLEdBQUcsQ0FBQyxRQUFRLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxZQUFZLElBQUksWUFBWSxDQUFDO1lBQ3pFLE9BQU8sR0FBRyxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO2dCQUMzQixJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsU0FBUyxFQUFFLEVBQUUsWUFBWSxFQUFFLFlBQVksRUFBRSxFQUFFLENBQUM7UUFDekQsQ0FBQyxDQUFDO0lBQ0osQ0FBQztJQUVEOzs7Ozs7Ozs7OztPQVdHO0lBQ0gsTUFBTSxDQUFDLGdCQUFnQixDQUFDLHFCQUE2QjtRQUNuRCxJQUFJLENBQUMsUUFBUSxDQUFDLHFCQUFxQixDQUFDLEVBQUU7WUFBRSxPQUFPLGNBQWMsQ0FBQyxhQUFhLENBQUM7U0FBRTtRQUM5RSxPQUFPLENBQUMsT0FBd0IsRUFBRSxNQUFNLEdBQUcsS0FBSyxFQUF5QixFQUFFO1lBQ3pFLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRTtnQkFBRSxPQUFPLElBQUksQ0FBQzthQUFFO1lBQzVDLE1BQU0sWUFBWSxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUM7WUFDbkMsTUFBTSxPQUFPLEdBQUcsQ0FBQyxRQUFRLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxZQUFZLEdBQUcscUJBQXFCLENBQUM7WUFDakYsT0FBTyxHQUFHLENBQUMsT0FBTyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7Z0JBQzNCLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxrQkFBa0IsRUFBRSxFQUFFLHFCQUFxQixFQUFFLFlBQVksRUFBRSxFQUFFLENBQUM7UUFDM0UsQ0FBQyxDQUFDO0lBQ0osQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0gsTUFBTSxDQUFDLFVBQVUsQ0FBQyxlQUF1QjtRQUN2QyxJQUFJLENBQUMsUUFBUSxDQUFDLGVBQWUsQ0FBQyxFQUFFO1lBQUUsT0FBTyxjQUFjLENBQUMsYUFBYSxDQUFDO1NBQUU7UUFDeEUsT0FBTyxDQUFDLE9BQXdCLEVBQUUsTUFBTSxHQUFHLEtBQUssRUFBeUIsRUFBRTtZQUN6RSxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEVBQUU7Z0JBQUUsT0FBTyxJQUFJLENBQUM7YUFBRTtZQUM1QyxNQUFNLFlBQVksR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDO1lBQ25DLE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxZQUFZLENBQUM7Z0JBQ3BDLFlBQVksR0FBRyxlQUFlLEtBQUssQ0FBQyxDQUFDO1lBQ3ZDLE9BQU8sR0FBRyxDQUFDLE9BQU8sRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO2dCQUMzQixJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsWUFBWSxFQUFFLEVBQUUsZUFBZSxFQUFFLFlBQVksRUFBRSxFQUFFLENBQUM7UUFDL0QsQ0FBQyxDQUFDO0lBQ0osQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0gsTUFBTSxDQUFDLGFBQWEsQ0FBQyxpQkFBeUI7UUFDNUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxFQUFFO1lBQUUsT0FBTyxjQUFjLENBQUMsYUFBYSxDQUFDO1NBQUU7UUFDMUUsT0FBTyxDQUFDLE9BQXdCLEVBQUUsTUFBTSxHQUFHLEtBQUssRUFBeUIsRUFBRTtZQUN6RSxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEVBQUU7Z0JBQUUsT0FBTyxJQUFJLENBQUM7YUFBRTtZQUM1QyxNQUFNLGlCQUFpQixHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLE1BQU0sSUFBSSxDQUFDLENBQUM7WUFDakUsTUFBTSxPQUFPLEdBQUcsaUJBQWlCLElBQUksaUJBQWlCLENBQUM7WUFDdkQsT0FBTyxHQUFHLENBQUMsT0FBTyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7Z0JBQzNCLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxlQUFlLEVBQUUsRUFBRSxpQkFBaUIsRUFBRSxpQkFBaUIsRUFBRSxFQUFFLENBQUM7UUFDekUsQ0FBQyxDQUFDO0lBQ0osQ0FBQztJQUVEOzs7Ozs7Ozs7OztPQVdHO0lBQ0gsTUFBTSxDQUFDLGFBQWEsQ0FBQyxpQkFBeUI7UUFDNUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxFQUFFO1lBQUUsT0FBTyxjQUFjLENBQUMsYUFBYSxDQUFDO1NBQUU7UUFDMUUsT0FBTyxDQUFDLE9BQXdCLEVBQUUsTUFBTSxHQUFHLEtBQUssRUFBeUIsRUFBRTtZQUN6RSxNQUFNLGlCQUFpQixHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLE1BQU0sSUFBSSxDQUFDLENBQUM7WUFDakUsTUFBTSxPQUFPLEdBQUcsaUJBQWlCLElBQUksaUJBQWlCLENBQUM7WUFDdkQsT0FBTyxHQUFHLENBQUMsT0FBTyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7Z0JBQzNCLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxlQUFlLEVBQUUsRUFBRSxpQkFBaUIsRUFBRSxpQkFBaUIsRUFBRSxFQUFFLENBQUM7UUFDekUsQ0FBQyxDQUFDO0lBQ0osQ0FBQztJQUVEOzs7Ozs7Ozs7OztPQVdHO0lBQ0gsTUFBTSxDQUFDLFlBQVksQ0FBQyxZQUFpQjtRQUNuQyxJQUFJLE9BQU8sQ0FBQyxZQUFZLENBQUMsS0FBSyxRQUFRLElBQUksT0FBTyxDQUFDLFlBQVksQ0FBQyxFQUFFO1lBQy9ELE9BQU8sY0FBYyxDQUFDLGFBQWEsQ0FBQztTQUNyQztRQUNELE9BQU8sQ0FBQyxPQUF3QixFQUFFLE1BQU0sR0FBRyxLQUFLLEVBQXlCLEVBQUU7WUFDekUsSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxFQUFFO2dCQUFFLE9BQU8sSUFBSSxDQUFDO2FBQUU7WUFDNUMsTUFBTSxTQUFTLEdBQUcsYUFBYSxDQUM3QixXQUFXLENBQUMsWUFBWSxFQUFFLENBQUMsS0FBSyxFQUFFLGNBQWMsRUFBRSxFQUFFO2dCQUNsRCxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUFDLENBQUMsRUFBRTtvQkFBRSxPQUFPLElBQUksQ0FBQztpQkFBRTtnQkFDOUQsSUFBSSxvQkFBb0IsR0FBcUIsRUFBRyxDQUFDO2dCQUNqRCxJQUFJLGNBQX