UNPKG

@angular/forms

Version:

Angular - directives and services for creating forms

678 lines 75.5 kB
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import { InjectionToken, ɵisPromise as isPromise, ɵisSubscribable as isSubscribable, ɵRuntimeError as RuntimeError } from '@angular/core'; import { forkJoin, from } from 'rxjs'; import { map } from 'rxjs/operators'; function isEmptyInputValue(value) { /** * Check if the object is a string or array before evaluating the length attribute. * This avoids falsely rejecting objects that contain a custom length attribute. * For example, the object {id: 1, length: 0, width: 0} should not be returned as empty. */ return value == null || ((typeof value === 'string' || Array.isArray(value)) && value.length === 0); } function hasValidLength(value) { // non-strict comparison is intentional, to check for both `null` and `undefined` values return value != null && typeof value.length === 'number'; } /** * @description * An `InjectionToken` for registering additional synchronous validators used with * `AbstractControl`s. * * @see {@link NG_ASYNC_VALIDATORS} * * @usageNotes * * ### Providing a custom validator * * The following example registers a custom validator directive. Adding the validator to the * existing collection of validators requires the `multi: true` option. * * ```typescript * @Directive({ * selector: '[customValidator]', * providers: [{provide: NG_VALIDATORS, useExisting: CustomValidatorDirective, multi: true}] * }) * class CustomValidatorDirective implements Validator { * validate(control: AbstractControl): ValidationErrors | null { * return { 'custom': true }; * } * } * ``` * * @publicApi */ export const NG_VALIDATORS = new InjectionToken(ngDevMode ? 'NgValidators' : ''); /** * @description * An `InjectionToken` for registering additional asynchronous validators used with * `AbstractControl`s. * * @see {@link NG_VALIDATORS} * * @usageNotes * * ### Provide a custom async validator directive * * The following example implements the `AsyncValidator` interface to create an * async validator directive with a custom error key. * * ```typescript * @Directive({ * selector: '[customAsyncValidator]', * providers: [{provide: NG_ASYNC_VALIDATORS, useExisting: CustomAsyncValidatorDirective, multi: * true}] * }) * class CustomAsyncValidatorDirective implements AsyncValidator { * validate(control: AbstractControl): Promise<ValidationErrors|null> { * return Promise.resolve({'custom': true}); * } * } * ``` * * @publicApi */ export const NG_ASYNC_VALIDATORS = new InjectionToken(ngDevMode ? 'NgAsyncValidators' : ''); /** * A regular expression that matches valid e-mail addresses. * * At a high level, this regexp matches e-mail addresses of the format `local-part@tld`, where: * - `local-part` consists of one or more of the allowed characters (alphanumeric and some * punctuation symbols). * - `local-part` cannot begin or end with a period (`.`). * - `local-part` cannot be longer than 64 characters. * - `tld` consists of one or more `labels` separated by periods (`.`). For example `localhost` or * `foo.com`. * - A `label` consists of one or more of the allowed characters (alphanumeric, dashes (`-`) and * periods (`.`)). * - A `label` cannot begin or end with a dash (`-`) or a period (`.`). * - A `label` cannot be longer than 63 characters. * - The whole address cannot be longer than 254 characters. * * ## Implementation background * * This regexp was ported over from AngularJS (see there for git history): * https://github.com/angular/angular.js/blob/c133ef836/src/ng/directive/input.js#L27 * It is based on the * [WHATWG version](https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address) with * some enhancements to incorporate more RFC rules (such as rules related to domain names and the * lengths of different parts of the address). The main differences from the WHATWG version are: * - Disallow `local-part` to begin or end with a period (`.`). * - Disallow `local-part` length to exceed 64 characters. * - Disallow total address length to exceed 254 characters. * * See [this commit](https://github.com/angular/angular.js/commit/f3f5cf72e) for more details. */ const EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[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])?)*$/; /** * @description * Provides a set of built-in validators that can be used by form controls. * * A validator is a function that processes a `FormControl` or collection of * controls and returns an error map or null. A null map means that validation has passed. * * @see [Form Validation](/guide/form-validation) * * @publicApi */ export class Validators { /** * @description * Validator that requires the control's value to be greater than or equal to the provided number. * * @usageNotes * * ### Validate against a minimum of 3 * * ```typescript * const control = new FormControl(2, Validators.min(3)); * * console.log(control.errors); // {min: {min: 3, actual: 2}} * ``` * * @returns A validator function that returns an error map with the * `min` property if the validation check fails, otherwise `null`. * * @see {@link updateValueAndValidity()} * */ static min(min) { return minValidator(min); } /** * @description * Validator that requires the control's value to be less than or equal to the provided number. * * @usageNotes * * ### Validate against a maximum of 15 * * ```typescript * const control = new FormControl(16, Validators.max(15)); * * console.log(control.errors); // {max: {max: 15, actual: 16}} * ``` * * @returns A validator function that returns an error map with the * `max` property if the validation check fails, otherwise `null`. * * @see {@link updateValueAndValidity()} * */ static max(max) { return maxValidator(max); } /** * @description * Validator that requires the control have a non-empty value. * * @usageNotes * * ### Validate that the field is non-empty * * ```typescript * const control = new FormControl('', Validators.required); * * console.log(control.errors); // {required: true} * ``` * * @returns An error map with the `required` property * if the validation check fails, otherwise `null`. * * @see {@link updateValueAndValidity()} * */ static required(control) { return requiredValidator(control); } /** * @description * Validator that requires the control's value be true. This validator is commonly * used for required checkboxes. * * @usageNotes * * ### Validate that the field value is true * * ```typescript * const control = new FormControl('some value', Validators.requiredTrue); * * console.log(control.errors); // {required: true} * ``` * * @returns An error map that contains the `required` property * set to `true` if the validation check fails, otherwise `null`. * * @see {@link updateValueAndValidity()} * */ static requiredTrue(control) { return requiredTrueValidator(control); } /** * @description * Validator that requires the control's value pass an email validation test. * * Tests the value using a [regular * expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) * pattern suitable for common use cases. The pattern is based on the definition of a valid email * address in the [WHATWG HTML * specification](https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address) with * some enhancements to incorporate more RFC rules (such as rules related to domain names and the * lengths of different parts of the address). * * The differences from the WHATWG version include: * - Disallow `local-part` (the part before the `@` symbol) to begin or end with a period (`.`). * - Disallow `local-part` to be longer than 64 characters. * - Disallow the whole address to be longer than 254 characters. * * If this pattern does not satisfy your business needs, you can use `Validators.pattern()` to * validate the value against a different pattern. * * @usageNotes * * ### Validate that the field matches a valid email pattern * * ```typescript * const control = new FormControl('bad@', Validators.email); * * console.log(control.errors); // {email: true} * ``` * * @returns An error map with the `email` property * if the validation check fails, otherwise `null`. * * @see {@link updateValueAndValidity()} * */ static email(control) { return emailValidator(control); } /** * @description * Validator that requires the length of the control's value to be greater than or equal * to the provided minimum length. This validator is also provided by default if you use the * the HTML5 `minlength` attribute. Note that the `minLength` validator is intended to be used * only for types that have a numeric `length` property, such as strings or arrays. The * `minLength` validator logic is also not invoked for values when their `length` property is 0 * (for example in case of an empty string or an empty array), to support optional controls. You * can use the standard `required` validator if empty values should not be considered valid. * * @usageNotes * * ### Validate that the field has a minimum of 3 characters * * ```typescript * const control = new FormControl('ng', Validators.minLength(3)); * * console.log(control.errors); // {minlength: {requiredLength: 3, actualLength: 2}} * ``` * * ```html * <input minlength="5"> * ``` * * @returns A validator function that returns an error map with the * `minlength` property if the validation check fails, otherwise `null`. * * @see {@link updateValueAndValidity()} * */ static minLength(minLength) { return minLengthValidator(minLength); } /** * @description * Validator that requires the length of the control's value to be less than or equal * to the provided maximum length. This validator is also provided by default if you use the * the HTML5 `maxlength` attribute. Note that the `maxLength` validator is intended to be used * only for types that have a numeric `length` property, such as strings or arrays. * * @usageNotes * * ### Validate that the field has maximum of 5 characters * * ```typescript * const control = new FormControl('Angular', Validators.maxLength(5)); * * console.log(control.errors); // {maxlength: {requiredLength: 5, actualLength: 7}} * ``` * * ```html * <input maxlength="5"> * ``` * * @returns A validator function that returns an error map with the * `maxlength` property if the validation check fails, otherwise `null`. * * @see {@link updateValueAndValidity()} * */ static maxLength(maxLength) { return maxLengthValidator(maxLength); } /** * @description * Validator that requires the control's value to match a regex pattern. This validator is also * provided by default if you use the HTML5 `pattern` attribute. * * @usageNotes * * ### Validate that the field only contains letters or spaces * * ```typescript * const control = new FormControl('1', Validators.pattern('[a-zA-Z ]*')); * * console.log(control.errors); // {pattern: {requiredPattern: '^[a-zA-Z ]*$', actualValue: '1'}} * ``` * * ```html * <input pattern="[a-zA-Z ]*"> * ``` * * ### Pattern matching with the global or sticky flag * * `RegExp` objects created with the `g` or `y` flags that are passed into `Validators.pattern` * can produce different results on the same input when validations are run consecutively. This is * due to how the behavior of `RegExp.prototype.test` is * specified in [ECMA-262](https://tc39.es/ecma262/#sec-regexpbuiltinexec) * (`RegExp` preserves the index of the last match when the global or sticky flag is used). * Due to this behavior, it is recommended that when using * `Validators.pattern` you **do not** pass in a `RegExp` object with either the global or sticky * flag enabled. * * ```typescript * // Not recommended (since the `g` flag is used) * const controlOne = new FormControl('1', Validators.pattern(/foo/g)); * * // Good * const controlTwo = new FormControl('1', Validators.pattern(/foo/)); * ``` * * @param pattern A regular expression to be used as is to test the values, or a string. * If a string is passed, the `^` character is prepended and the `$` character is * appended to the provided string (if not already present), and the resulting regular * expression is used to test the values. * * @returns A validator function that returns an error map with the * `pattern` property if the validation check fails, otherwise `null`. * * @see {@link updateValueAndValidity()} * */ static pattern(pattern) { return patternValidator(pattern); } /** * @description * Validator that performs no operation. * * @see {@link updateValueAndValidity()} * */ static nullValidator(control) { return nullValidator(control); } static compose(validators) { return compose(validators); } /** * @description * Compose multiple async validators into a single function that returns the union * of the individual error objects for the provided control. * * @returns A validator function that returns an error map with the * merged error objects of the async validators if the validation check fails, otherwise `null`. * * @see {@link updateValueAndValidity()} * */ static composeAsync(validators) { return composeAsync(validators); } } /** * Validator that requires the control's value to be greater than or equal to the provided number. * See `Validators.min` for additional information. */ export function minValidator(min) { return (control) => { if (isEmptyInputValue(control.value) || isEmptyInputValue(min)) { return null; // don't validate empty values to allow optional controls } const value = parseFloat(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 ? { 'min': { 'min': min, 'actual': control.value } } : null; }; } /** * Validator that requires the control's value to be less than or equal to the provided number. * See `Validators.max` for additional information. */ export function maxValidator(max) { return (control) => { if (isEmptyInputValue(control.value) || isEmptyInputValue(max)) { return null; // don't validate empty values to allow optional controls } const value = parseFloat(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 ? { 'max': { 'max': max, 'actual': control.value } } : null; }; } /** * Validator that requires the control have a non-empty value. * See `Validators.required` for additional information. */ export function requiredValidator(control) { return isEmptyInputValue(control.value) ? { 'required': true } : null; } /** * Validator that requires the control's value be true. This validator is commonly * used for required checkboxes. * See `Validators.requiredTrue` for additional information. */ export function requiredTrueValidator(control) { return control.value === true ? null : { 'required': true }; } /** * Validator that requires the control's value pass an email validation test. * See `Validators.email` for additional information. */ export function emailValidator(control) { if (isEmptyInputValue(control.value)) { return null; // don't validate empty values to allow optional controls } return EMAIL_REGEXP.test(control.value) ? null : { 'email': true }; } /** * Validator that requires the length of the control's value to be greater than or equal * to the provided minimum length. See `Validators.minLength` for additional information. */ export function minLengthValidator(minLength) { return (control) => { if (isEmptyInputValue(control.value) || !hasValidLength(control.value)) { // don't validate empty values to allow optional controls // don't validate values without `length` property return null; } return control.value.length < minLength ? { 'minlength': { 'requiredLength': minLength, 'actualLength': control.value.length } } : null; }; } /** * Validator that requires the length of the control's value to be less than or equal * to the provided maximum length. See `Validators.maxLength` for additional information. */ export function maxLengthValidator(maxLength) { return (control) => { return hasValidLength(control.value) && control.value.length > maxLength ? { 'maxlength': { 'requiredLength': maxLength, 'actualLength': control.value.length } } : null; }; } /** * Validator that requires the control's value to match a regex pattern. * See `Validators.pattern` for additional information. */ export function patternValidator(pattern) { if (!pattern) return nullValidator; let regex; let regexStr; if (typeof pattern === 'string') { regexStr = ''; if (pattern.charAt(0) !== '^') regexStr += '^'; regexStr += pattern; if (pattern.charAt(pattern.length - 1) !== '$') regexStr += '$'; regex = new RegExp(regexStr); } else { regexStr = pattern.toString(); regex = pattern; } return (control) => { if (isEmptyInputValue(control.value)) { return null; // don't validate empty values to allow optional controls } const value = control.value; return regex.test(value) ? null : { 'pattern': { 'requiredPattern': regexStr, 'actualValue': value } }; }; } /** * Function that has `ValidatorFn` shape, but performs no operation. */ export function nullValidator(control) { return null; } function isPresent(o) { return o != null; } export function toObservable(value) { const obs = isPromise(value) ? from(value) : value; if ((typeof ngDevMode === 'undefined' || ngDevMode) && !(isSubscribable(obs))) { let errorMessage = `Expected async validator to return Promise or Observable.`; // A synchronous validator will return object or null. if (typeof value === 'object') { errorMessage += ' Are you using a synchronous validator where an async validator is expected?'; } throw new RuntimeError(-1101 /* RuntimeErrorCode.WRONG_VALIDATOR_RETURN_TYPE */, errorMessage); } return obs; } function mergeErrors(arrayOfErrors) { let res = {}; arrayOfErrors.forEach((errors) => { res = errors != null ? { ...res, ...errors } : res; }); return Object.keys(res).length === 0 ? null : res; } function executeValidators(control, validators) { return validators.map(validator => validator(control)); } function isValidatorFn(validator) { return !validator.validate; } /** * Given the list of validators that may contain both functions as well as classes, return the list * of validator functions (convert validator classes into validator functions). This is needed to * have consistent structure in validators list before composing them. * * @param validators The set of validators that may contain validators both in plain function form * as well as represented as a validator class. */ export function normalizeValidators(validators) { return validators.map(validator => { return isValidatorFn(validator) ? validator : ((c) => validator.validate(c)); }); } /** * Merges synchronous validators into a single validator function. * See `Validators.compose` for additional information. */ function compose(validators) { if (!validators) return null; const presentValidators = validators.filter(isPresent); if (presentValidators.length == 0) return null; return function (control) { return mergeErrors(executeValidators(control, presentValidators)); }; } /** * Accepts a list of validators of different possible shapes (`Validator` and `ValidatorFn`), * normalizes the list (converts everything to `ValidatorFn`) and merges them into a single * validator function. */ export function composeValidators(validators) { return validators != null ? compose(normalizeValidators(validators)) : null; } /** * Merges asynchronous validators into a single validator function. * See `Validators.composeAsync` for additional information. */ function composeAsync(validators) { if (!validators) return null; const presentValidators = validators.filter(isPresent); if (presentValidators.length == 0) return null; return function (control) { const observables = executeValidators(control, presentValidators).map(toObservable); return forkJoin(observables).pipe(map(mergeErrors)); }; } /** * Accepts a list of async validators of different possible shapes (`AsyncValidator` and * `AsyncValidatorFn`), normalizes the list (converts everything to `AsyncValidatorFn`) and merges * them into a single validator function. */ export function composeAsyncValidators(validators) { return validators != null ? composeAsync(normalizeValidators(validators)) : null; } /** * Merges raw control validators with a given directive validator and returns the combined list of * validators as an array. */ export function mergeValidators(controlValidators, dirValidator) { if (controlValidators === null) return [dirValidator]; return Array.isArray(controlValidators) ? [...controlValidators, dirValidator] : [controlValidators, dirValidator]; } /** * Retrieves the list of raw synchronous validators attached to a given control. */ export function getControlValidators(control) { return control._rawValidators; } /** * Retrieves the list of raw asynchronous validators attached to a given control. */ export function getControlAsyncValidators(control) { return control._rawAsyncValidators; } /** * Accepts a singleton validator, an array, or null, and returns an array type with the provided * validators. * * @param validators A validator, validators, or null. * @returns A validators array. */ export function makeValidatorsArray(validators) { if (!validators) return []; return Array.isArray(validators) ? validators : [validators]; } /** * Determines whether a validator or validators array has a given validator. * * @param validators The validator or validators to compare against. * @param validator The validator to check. * @returns Whether the validator is present. */ export function hasValidator(validators, validator) { return Array.isArray(validators) ? validators.includes(validator) : validators === validator; } /** * Combines two arrays of validators into one. If duplicates are provided, only one will be added. * * @param validators The new validators. * @param currentValidators The base array of current validators. * @returns An array of validators. */ export function addValidators(validators, currentValidators) { const current = makeValidatorsArray(currentValidators); const validatorsToAdd = makeValidatorsArray(validators); validatorsToAdd.forEach((v) => { // Note: if there are duplicate entries in the new validators array, // only the first one would be added to the current list of validators. // Duplicate ones would be ignored since `hasValidator` would detect // the presence of a validator function and we update the current list in place. if (!hasValidator(current, v)) { current.push(v); } }); return current; } export function removeValidators(validators, currentValidators) { return makeValidatorsArray(currentValidators).filter(v => !hasValidator(validators, v)); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdG9ycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3BhY2thZ2VzL2Zvcm1zL3NyYy92YWxpZGF0b3JzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7R0FNRztBQUVILE9BQU8sRUFBQyxjQUFjLEVBQUUsVUFBVSxJQUFJLFNBQVMsRUFBRSxlQUFlLElBQUksY0FBYyxFQUFFLGFBQWEsSUFBSSxZQUFZLEVBQUMsTUFBTSxlQUFlLENBQUM7QUFDeEksT0FBTyxFQUFDLFFBQVEsRUFBRSxJQUFJLEVBQWEsTUFBTSxNQUFNLENBQUM7QUFDaEQsT0FBTyxFQUFDLEdBQUcsRUFBQyxNQUFNLGdCQUFnQixDQUFDO0FBT25DLFNBQVMsaUJBQWlCLENBQUMsS0FBVTtJQUNuQzs7OztPQUlHO0lBQ0gsT0FBTyxLQUFLLElBQUksSUFBSTtRQUNoQixDQUFDLENBQUMsT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsSUFBSSxLQUFLLENBQUMsTUFBTSxLQUFLLENBQUMsQ0FBQyxDQUFDO0FBQ2xGLENBQUM7QUFFRCxTQUFTLGNBQWMsQ0FBQyxLQUFVO0lBQ2hDLHdGQUF3RjtJQUN4RixPQUFPLEtBQUssSUFBSSxJQUFJLElBQUksT0FBTyxLQUFLLENBQUMsTUFBTSxLQUFLLFFBQVEsQ0FBQztBQUMzRCxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTJCRztBQUNILE1BQU0sQ0FBQyxNQUFNLGFBQWEsR0FDdEIsSUFBSSxjQUFjLENBQW9DLFNBQVMsQ0FBQyxDQUFDLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQztBQUUzRjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTRCRztBQUNILE1BQU0sQ0FBQyxNQUFNLG1CQUFtQixHQUM1QixJQUFJLGNBQWMsQ0FBb0MsU0FBUyxDQUFDLENBQUMs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QyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDbEIsQ0FBQztJQUNILENBQUMsQ0FBQyxDQUFDO0lBQ0gsT0FBTyxPQUFPLENBQUM7QUFDakIsQ0FBQztBQUVELE1BQU0sVUFBVSxnQkFBZ0IsQ0FDNUIsVUFBaUIsRUFBRSxpQkFBNkI7SUFDbEQsT0FBTyxtQkFBbUIsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsWUFBWSxDQUFDLFVBQVUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQzFGLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBDb3B5cmlnaHQgR29vZ2xlIExMQyBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICpcbiAqIFVzZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGFuIE1JVC1zdHlsZSBsaWNlbnNlIHRoYXQgY2FuIGJlXG4gKiBmb3VuZCBpbiB0aGUgTElDRU5TRSBmaWxlIGF0IGh0dHBzOi8vYW5ndWxhci5pby9saWNlbnNlXG4gKi9cblxuaW1wb3J0IHtJbmplY3Rpb25Ub2tlbiwgybVpc1Byb21pc2UgYXMgaXNQcm9taXNlLCDJtWlzU3Vic2NyaWJhYmxlIGFzIGlzU3Vic2NyaWJhYmxlLCDJtVJ1bnRpbWVFcnJvciBhcyBSdW50aW1lRXJyb3J9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtmb3JrSm9pbiwgZnJvbSwgT2JzZXJ2YWJsZX0gZnJvbSAncnhqcyc7XG5pbXBvcnQge21hcH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuXG5pbXBvcnQge0FzeW5jVmFsaWRhdG9yLCBBc3luY1ZhbGlkYXRvckZuLCBWYWxpZGF0aW9uRXJyb3JzLCBWYWxpZGF0b3IsIFZhbGlkYXRvckZufSBmcm9tICcuL2RpcmVjdGl2ZXMvdmFsaWRhdG9ycyc7XG5pbXBvcnQge1J1bnRpbWVFcnJvckNvZGV9IGZyb20gJy4vZXJyb3JzJztcbmltcG9ydCB7QWJzdHJhY3RDb250cm9sfSBmcm9tICcuL21vZGVsL2Fic3RyYWN0X21vZGVsJztcblxuXG5mdW5jdGlvbiBpc0VtcHR5SW5wdXRWYWx1ZSh2YWx1ZTogYW55KTogYm9vbGVhbiB7XG4gIC8qKlxuICAgKiBDaGVjayBpZiB0aGUgb2JqZWN0IGlzIGEgc3RyaW5nIG9yIGFycmF5IGJlZm9yZSBldmFsdWF0aW5nIHRoZSBsZW5ndGggYXR0cmlidXRlLlxuICAgKiBUaGlzIGF2b2lkcyBmYWxzZWx5IHJlamVjdGluZyBvYmplY3RzIHRoYXQgY29udGFpbiBhIGN1c3RvbSBsZW5ndGggYXR0cmlidXRlLlxuICAgKiBGb3IgZXhhbXBsZSwgdGhlIG9iamVjdCB7aWQ6IDEsIGxlbmd0aDogMCwgd2lkdGg6IDB9IHNob3VsZCBub3QgYmUgcmV0dXJuZWQgYXMgZW1wdHkuXG4gICAqL1xuICByZXR1cm4gdmFsdWUgPT0gbnVsbCB8fFxuICAgICAgKCh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnIHx8IEFycmF5LmlzQXJyYXkodmFsdWUpKSAmJiB2YWx1ZS5sZW5ndGggPT09IDApO1xufVxuXG5mdW5jdGlvbiBoYXNWYWxpZExlbmd0aCh2YWx1ZTogYW55KTogYm9vbGVhbiB7XG4gIC8vIG5vbi1zdHJpY3QgY29tcGFyaXNvbiBpcyBpbnRlbnRpb25hbCwgdG8gY2hlY2sgZm9yIGJvdGggYG51bGxgIGFuZCBgdW5kZWZpbmVkYCB2YWx1ZXNcbiAgcmV0dXJuIHZhbHVlICE9IG51bGwgJiYgdHlwZW9mIHZhbHVlLmxlbmd0aCA9PT0gJ251bWJlcic7XG59XG5cbi8qKlxuICogQGRlc2NyaXB0aW9uXG4gKiBBbiBgSW5qZWN0aW9uVG9rZW5gIGZvciByZWdpc3RlcmluZyBhZGRpdGlvbmFsIHN5bmNocm9ub3VzIHZhbGlkYXRvcnMgdXNlZCB3aXRoXG4gKiBgQWJzdHJhY3RDb250cm9sYHMuXG4gKlxuICogQHNlZSB7QGxpbmsgTkdfQVNZTkNfVkFMSURBVE9SU31cbiAqXG4gKiBAdXNhZ2VOb3Rlc1xuICpcbiAqICMjIyBQcm92aWRpbmcgYSBjdXN0b20gdmFsaWRhdG9yXG4gKlxuICogVGhlIGZvbGxvd2luZyBleGFtcGxlIHJlZ2lzdGVycyBhIGN1c3RvbSB2YWxpZGF0b3IgZGlyZWN0aXZlLiBBZGRpbmcgdGhlIHZhbGlkYXRvciB0byB0aGVcbiAqIGV4aXN0aW5nIGNvbGxlY3Rpb24gb2YgdmFsaWRhdG9ycyByZXF1aXJlcyB0aGUgYG11bHRpOiB0cnVlYCBvcHRpb24uXG4gKlxuICogYGBgdHlwZXNjcmlwdFxuICogQERpcmVjdGl2ZSh7XG4gKiAgIHNlbGVjdG9yOiAnW2N1c3RvbVZhbGlkYXRvcl0nLFxuICogICBwcm92aWRlcnM6IFt7cHJvdmlkZTogTkdfVkFMSURBVE9SUywgdXNlRXhpc3Rpbmc6IEN1c3RvbVZhbGlkYXRvckRpcmVjdGl2ZSwgbXVsdGk6IHRydWV9XVxuICogfSlcbiAqIGNsYXNzIEN1c3RvbVZhbGlkYXRvckRpcmVjdGl2ZSBpbXBsZW1lbnRzIFZhbGlkYXRvciB7XG4gKiAgIHZhbGlkYXRlKGNvbnRyb2w6IEFic3RyYWN0Q29udHJvbCk6IFZhbGlkYXRpb25FcnJvcnMgfCBudWxsIHtcbiAqICAgICByZXR1cm4geyAnY3VzdG9tJzogdHJ1ZSB9O1xuICogICB9XG4gKiB9XG4gKiBgYGBcbiAqXG4gKiBAcHVibGljQXBpXG4gKi9cbmV4cG9ydCBjb25zdCBOR19WQUxJREFUT1JTID1cbiAgICBuZXcgSW5qZWN0aW9uVG9rZW48UmVhZG9ubHlBcnJheTxWYWxpZGF0b3J8RnVuY3Rpb24+PihuZ0Rldk1vZGUgPyAnTmdWYWxpZGF0b3JzJyA6ICcnKTtcblxuLyoqXG4gKiBAZGVzY3JpcHRpb25cbiAqIEFuIGBJbmplY3Rpb25Ub2tlbmAgZm9yIHJlZ2lzdGVyaW5nIGFkZGl0aW9uYWwgYXN5bmNocm9ub3VzIHZhbGlkYXRvcnMgdXNlZCB3aXRoXG4gKiBgQWJzdHJhY3RDb250cm9sYHMuXG4gKlxuICogQHNlZSB7QGxpbmsgTkdfVkFMSURBVE9SU31cbiAqXG4gKiBAdXNhZ2VOb3Rlc1xuICpcbiAqICMjIyBQcm92aWRlIGEgY3VzdG9tIGFzeW5jIHZhbGlkYXRvciBkaXJlY3RpdmVcbiAqXG4gKiBUaGUgZm9sbG93aW5nIGV4YW1wbGUgaW1wbGVtZW50cyB0aGUgYEFzeW5jVmFsaWRhdG9yYCBpbnRlcmZhY2UgdG8gY3JlYXRlIGFuXG4gKiBhc3luYyB2YWxpZGF0b3IgZGlyZWN0aXZlIHdpdGggYSBjdXN0b20gZXJyb3Iga2V5LlxuICpcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIEBEaXJlY3RpdmUoe1xuICogICBzZWxlY3RvcjogJ1tjdXN0b21Bc3luY1ZhbGlkYXRvcl0nLFxuICogICBwcm92aWRlcnM6IFt7cHJvdmlkZTogTkdfQVNZTkNfVkFMSURBVE9SUywgdXNlRXhpc3Rpbmc6IEN1c3RvbUFzeW5jVmFsaWRhdG9yRGlyZWN0aXZlLCBtdWx0aTpcbiAqIHRydWV9XVxuICogfSlcbiAqIGNsYXNzIEN1c3RvbUFzeW5jVmFsaWRhdG9yRGlyZWN0aXZlIGltcGxlbWVudHMgQXN5bmNWYWxpZGF0b3Ige1xuICogICB2YWxpZGF0ZShjb250cm9sOiBBYnN0cmFjdENvbnRyb2wpOiBQcm9taXNlPFZhbGlkYXRpb25FcnJvcnN8bnVsbD4ge1xuICogICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoeydjdXN0b20nOiB0cnVlfSk7XG4gKiAgIH1cbiAqIH1cbiAqIGBgYFxuICpcbiAqIEBwdWJsaWNBcGlcbiAqL1xuZXhwb3J0IGNvbnN0IE5HX0FTWU5DX1ZBTElEQVRPUlMgPVxuICAgIG5ldyBJbmplY3Rpb25Ub2tlbjxSZWFkb25seUFycmF5PFZhbGlkYXRvcnxGdW5jdGlvbj4+KG5nRGV2TW9kZSA/ICdOZ0FzeW5jVmFsaWRhdG9ycycgOiAnJyk7XG5cbi8qKlxuICogQSByZWd1bGFyIGV4cHJlc3Npb24gdGhhdCBtYXRjaGVzIHZhbGlkIGUtbWFpbCBhZGRyZXNzZXMuXG4gKlxuICogQXQgYSBoaWdoIGxldmVsLCB0aGlzIHJlZ2V4cCBtYXRjaGVzIGUtbWFpbCBhZGRyZXNzZXMgb2YgdGhlIGZvcm1hdCBgbG9jYWwtcGFydEB0bGRgLCB3aGVyZTpcbiAqIC0gYGxvY2FsLXBhcnRgIGNvbnNpc3RzIG9mIG9uZSBvciBtb3JlIG9mIHRoZSBhbGxvd2VkIGNoYXJhY3RlcnMgKGFscGhhbnVtZXJpYyBhbmQgc29tZVxuICogICBwdW5jdHVhdGlvbiBzeW1ib2xzKS5cbiAqIC0gYGxvY2FsLXBhcnRgIGNhbm5vdCBiZWdpbiBvciBlbmQgd2l0aCBhIHBlcmlvZCAoYC5gKS5cbiAqIC0gYGxvY2FsLXBhcnRgIGNhbm5vdCBiZSBsb25nZXIgdGhhbiA2NCBjaGFyYWN0ZXJzLlxuICogLSBgdGxkYCBjb25zaXN0cyBvZiBvbmUgb3IgbW9yZSBgbGFiZWxzYCBzZXBhcmF0ZWQgYnkgcGVyaW9kcyAoYC5gKS4gRm9yIGV4YW1wbGUgYGxvY2FsaG9zdGAgb3JcbiAqICAgYGZvby5jb21gLlxuICogLSBBIGBsYWJlbGAgY29uc2lzdHMgb2Ygb25lIG9yIG1vcmUgb2YgdGhlIGFsbG93ZWQgY2hhcmFjdGVycyAoYWxwaGFudW1lcmljLCBkYXNoZXMgKGAtYCkgYW5kXG4gKiAgIHBlcmlvZHMgKGAuYCkpLlxuICogLSBBIGBsYWJlbGAgY2Fubm90IGJlZ2luIG9yIGVuZCB3aXRoIGEgZGFzaCAoYC1gKSBvciBhIHBlcmlvZCAoYC5gKS5cbiAqIC0gQSBgbGFiZWxgIGNhbm5vdCBiZSBsb25nZXIgdGhhbiA2MyBjaGFyYWN0ZXJzLlxuICogLSBUaGUgd2hvbGUgYWRkcmVzcyBjYW5ub3QgYmUgbG9uZ2VyIHRoYW4gMjU0IGNoYXJhY3RlcnMuXG4gKlxuICogIyMgSW1wbGVtZW50YXRpb24gYmFja2dyb3VuZFxuICpcbiAqIFRoaXMgcmVnZXhwIHdhcyBwb3J0ZWQgb3ZlciBmcm9tIEFuZ3VsYXJKUyAoc2VlIHRoZXJlIGZvciBnaXQgaGlzdG9yeSk6XG4gKiBodHRwczovL2dpdGh1Yi5jb20vYW5ndWxhci9hbmd1bGFyLmpzL2Jsb2IvYzEzM2VmODM2L3NyYy9uZy9kaXJlY3RpdmUvaW5wdXQuanMjTDI3XG4gKiBJdCBpcyBiYXNlZCBvbiB0aGVcbiAqIFtXSEFUV0cgdmVyc2lvbl0oaHR0cHM6Ly9odG1sLnNwZWMud2hhdHdnLm9yZy9tdWx0aXBhZ2UvaW5wdXQuaHRtbCN2YWxpZC1lLW1haWwtYWRkcmVzcykgd2l0aFxuICogc29tZSBlbmhhbmNlbWVudHMgdG8gaW5jb3Jwb3JhdGUgbW9yZSBSRkMgcnVsZXMgKHN1Y2ggYXMgcnVsZXMgcmVsYXRlZCB0byBkb21haW4gbmFtZXMgYW5kIHRoZVxuICogbGVuZ3RocyBvZiBkaWZmZXJlbnQgcGFydHMgb2YgdGhlIGFkZHJlc3MpLiBUaGUgbWFpbiBkaWZmZXJlbmNlcyBmcm9tIHRoZSBXSEFUV0cgdmVyc2lvbiBhcmU6XG4gKiAgIC0gRGlzYWxsb3cgYGxvY2FsLXBhcnRgIHRvIGJlZ2luIG9yIGVuZCB3aXRoIGEgcGVyaW9kIChgLmApLlxuICogICAtIERpc2FsbG93IGBsb2NhbC1wYXJ0YCBsZW5ndGggdG8gZXhjZWVkIDY0IGNoYXJhY3RlcnMuXG4gKiAgIC0gRGlzYWxsb3cgdG90YWwgYWRkcmVzcyBsZW5ndGggdG8gZXhjZWVkIDI1NCBjaGFyYWN0ZXJzLlxuICpcbiAqIFNlZSBbdGhpcyBjb21taXRdKGh0dHBzOi8vZ2l0aHViLmNvbS9hbmd1bGFyL2FuZ3VsYXIuanMvY29tbWl0L2YzZjVjZjcyZSkgZm9yIG1vcmUgZGV0YWlscy5cbiAqL1xuY29uc3QgRU1BSUxfUkVHRVhQID1cbiAgICAvXig/PS57MSwyNTR9JCkoPz0uezEsNjR9QClbYS16QS1aMC05ISMkJSYnKisvPT9eX2B7fH1+LV0rKD86XFwuW2EtekEtWjAtOSEjJCUmJyorLz0/Xl9ge3x9fi1dKykqQFthLXpBLVowLTldKD86W2EtekEtWjAtOS1dezAsNjF9W2EtekEtWjAtOV0pPyg/OlxcLlthLXpBLVowLTldKD86W2EtekEtWjAtOS1dezAsNjF9W2EtekEtWjAtOV0pPykqJC87XG5cbi8qKlxuICogQGRlc2NyaXB0aW9uXG4gKiBQcm92aWRlcyBhIHNldCBvZiBidWlsdC1pbiB2YWxpZGF0b3JzIHRoYXQgY2FuIGJlIHVzZWQgYnkgZm9ybSBjb250cm9scy5cbiAqXG4gKiBBIHZhbGlkYXRvciBpcyBhIGZ1bmN0aW9uIHRoYXQgcHJvY2Vzc2VzIGEgYEZvcm1Db250cm9sYCBvciBjb2xsZWN0aW9uIG9mXG4gKiBjb250cm9scyBhbmQgcmV0dXJucyBhbiBlcnJvciBtYXAgb3IgbnVsbC4gQSBudWxsIG1hcCBtZWFucyB0aGF0IHZhbGlkYXRpb24gaGFzIHBhc3NlZC5cbiAqXG4gKiBAc2VlIFtGb3JtIFZhbGlkYXRpb25dKC9ndWlkZS9mb3JtLXZhbGlkYXRpb24pXG4gKlxuICogQHB1YmxpY0FwaVxuICovXG5leHBvcnQgY2xhc3MgVmFsaWRhdG9ycyB7XG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb25cbiAgICogVmFsaWRhdG9yIHRoYXQgcmVxdWlyZXMgdGhlIGNvbnRyb2wncyB2YWx1ZSB0byBiZSBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gdGhlIHByb3ZpZGVkIG51bWJlci5cbiAgICpcbiAgICogQHVzYWdlTm90ZXNcbiAgICpcbiAgICogIyMjIFZhbGlkYXRlIGFnYWluc3QgYSBtaW5pbXVtIG9mIDNcbiAgICpcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBjb25zdCBjb250cm9sID0gbmV3IEZvcm1Db250cm9sKDIsIFZhbGlkYXRvcnMubWluKDMpKTtcbiAgICpcbiAgICogY29uc29sZS5sb2coY29udHJvbC5lcnJvcnMpOyAvLyB7bWluOiB7bWluOiAzLCBhY3R1YWw6IDJ9fVxuICAgKiBgYGBcbiAgICpcbiAgICogQHJldHVybnMgQSB2YWxpZGF0b3IgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGFuIGVycm9yIG1hcCB3aXRoIHRoZVxuICAgKiBgbWluYCBwcm9wZXJ0eSBpZiB0aGUgdmFsaWRhdGlvbiBjaGVjayBmYWlscywgb3RoZXJ3aXNlIGBudWxsYC5cbiAgICpcbiAgICogQHNlZSB7QGxpbmsgdXBkYXRlVmFsdWVBbmRWYWxpZGl0eSgpfVxuICAgKlxuICAgKi9cbiAgc3RhdGljIG1pbihtaW46IG51bWJlcik6IFZhbGlkYXRvckZuIHtcbiAgICByZXR1cm4gbWluVmFsaWRhdG9yKG1pbik7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uXG4gICAqIFZhbGlkYXRvciB0aGF0IHJlcXVpcmVzIHRoZSBjb250cm9sJ3MgdmFsdWUgdG8gYmUgbGVzcyB0aGFuIG9yIGVxdWFsIHRvIHRoZSBwcm92aWRlZCBudW1iZXIuXG4gICAqXG4gICAqIEB1c2FnZU5vdGVzXG4gICAqXG4gICAqICMjIyBWYWxpZGF0ZSBhZ2FpbnN0IGEgbWF4aW11bSBvZiAxNVxuICAgKlxuICAgKiBgYGB0eXBlc2NyaXB0XG4gICAqIGNvbnN0IGNvbnRyb2wgPSBuZXcgRm9ybUNvbnRyb2woMTYsIFZhbGlkYXRvcnMubWF4KDE1KSk7XG4gICAqXG4gICAqIGNvbnNvbGUubG9nKGNvbnRyb2wuZXJyb3JzKTsgLy8ge21heDoge21heDogMTUsIGFjdHVhbDogMTZ9fVxuICAgKiBgYGBcbiAgICpcbiAgICogQHJldHVybnMgQSB2YWxpZGF0b3IgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGFuIGVycm9yIG1hcCB3aXRoIHRoZVxuICAgKiBgbWF4YCBwcm9wZXJ0eSBpZiB0aGUgdmFsaWRhdGlvbiBjaGVjayBmYWlscywgb3RoZXJ3aXNlIGBudWxsYC5cbiAgICpcbiAgICogQHNlZSB7QGxpbmsgdXBkYXRlVmFsdWVBbmRWYWxpZGl0eSgpfVxuICAgKlxuICAgKi9cbiAgc3RhdGljIG1heChtYXg6IG51bWJlcik6IFZhbGlkYXRvckZuIHtcbiAgICByZXR1cm4gbWF4VmFsaWRhdG9yKG1heCk7XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uXG4gICAqIFZhbGlkYXRvciB0aGF0IHJlcXVpcmVzIHRoZSBjb250cm9sIGhhdmUgYSBub24tZW1wdHkgdmFsdWUuXG4gICAqXG4gICAqIEB1c2FnZU5vdGVzXG4gICAqXG4gICAqICMjIyBWYWxpZGF0ZSB0aGF0IHRoZSBmaWVsZCBpcyBub24tZW1wdHlcbiAgICpcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBjb25zdCBjb250cm9sID0gbmV3IEZvcm1Db250cm9sKCcnLCBWYWxpZGF0b3JzLnJlcXVpcmVkKTtcbiAgICpcbiAgICogY29uc29sZS5sb2coY29udHJvbC5lcnJvcnMpOyAvLyB7cmVxdWlyZWQ6IHRydWV9XG4gICAqIGBgYFxuICAgKlxuICAgKiBAcmV0dXJucyBBbiBlcnJvciBtYXAgd2l0aCB0aGUgYHJlcXVpcmVkYCBwcm9wZXJ0eVxuICAgKiBpZiB0aGUgdmFsaWRhdGlvbiBjaGVjayBmYWlscywgb3RoZXJ3aXNlIGBudWxsYC5cbiAgICpcbiAgICogQHNlZSB7QGxpbmsgdXBkYXRlVmFsdWVBbmRWYWxpZGl0eSgpfVxuICAgKlxuICAgKi9cbiAgc3RhdGljIHJlcXVpcmVkKGNvbnRyb2w6IEFic3RyYWN0Q29udHJvbCk6IFZhbGlkYXRpb25FcnJvcnN8bnVsbCB7XG4gICAgcmV0dXJuIHJlcXVpcmVkVmFsaWRhdG9yKGNvbnRyb2wpO1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvblxuICAgKiBWYWxpZGF0b3IgdGhhdCByZXF1aXJlcyB0aGUgY29udHJvbCdzIHZhbHVlIGJlIHRydWUuIFRoaXMgdmFsaWRhdG9yIGlzIGNvbW1vbmx5XG4gICAqIHVzZWQgZm9yIHJlcXVpcmVkIGNoZWNrYm94ZXMuXG4gICAqXG4gICAqIEB1c2FnZU5vdGVzXG4gICAqXG4gICAqICMjIyBWYWxpZGF0ZSB0aGF0IHRoZSBmaWVsZCB2YWx1ZSBpcyB0cnVlXG4gICAqXG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogY29uc3QgY29udHJvbCA9IG5ldyBGb3JtQ29udHJvbCgnc29tZSB2YWx1ZScsIFZhbGlkYXRvcnMucmVxdWlyZWRUcnVlKTtcbiAgICpcbiAgICogY29uc29sZS5sb2coY29udHJvbC5lcnJvcnMpOyAvLyB7cmVxdWlyZWQ6IHRydWV9XG4gICAqIGBgYFxuICAgKlxuICAgKiBAcmV0dXJucyBBbiBlcnJvciBtYXAgdGhhdCBjb250YWlucyB0aGUgYHJlcXVpcmVkYCBwcm9wZXJ0eVxuICAgKiBzZXQgdG8gYHRydWVgIGlmIHRoZSB2YWxpZGF0aW9uIGNoZWNrIGZhaWxzLCBvdGhlcndpc2UgYG51bGxgLlxuICAgKlxuICAgKiBAc2VlIHtAbGluayB1cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KCl9XG4gICAqXG4gICAqL1xuICBzdGF0aWMgcmVxdWlyZWRUcnVlKGNvbnRyb2w6IEFic3RyYWN0Q29udHJvbCk6IFZhbGlkYXRpb25FcnJvcnN8bnVsbCB7XG4gICAgcmV0dXJuIHJlcXVpcmVkVHJ1ZVZhbGlkYXRvcihjb250cm9sKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb25cbiAgICogVmFsaWRhdG9yIHRoYXQgcmVxdWlyZXMgdGhlIGNvbnRyb2wncyB2YWx1ZSBwYXNzIGFuIGVtYWlsIHZhbGlkYXRpb24gdGVzdC5cbiAgICpcbiAgICogVGVzdHMgdGhlIHZhbHVlIHVzaW5nIGEgW3JlZ3VsYXJcbiAgICogZXhwcmVzc2lvbl0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvSmF2YVNjcmlwdC9HdWlkZS9SZWd1bGFyX0V4cHJlc3Npb25zKVxuICAgKiBwYXR0ZXJuIHN1aXRhYmxlIGZvciBjb21tb24gdXNlIGNhc2VzLiBUaGUgcGF0dGVybiBpcyBiYXNlZCBvbiB0aGUgZGVmaW5pdGlvbiBvZiBhIHZhbGlkIGVtYWlsXG4gICAqIGFkZHJlc3MgaW4gdGhlIFtXSEFUV0cgSFRNTFxuICAgKiBzcGVjaWZpY2F0aW9uXShodHRwczovL2h0bWwuc3BlYy53aGF0d2cub3JnL211bHRpcGFnZS9pbnB1dC5odG1sI3ZhbGlkLWUtbWFpbC1hZGRyZXNzKSB3aXRoXG4gICAqIHNvbWUgZW5oYW5jZW1lbnRzIHRvIGluY29ycG9yYXRlIG1vcmUgUkZDIHJ1bGVzIChzdWNoIGFzIHJ1bGVzIHJlbGF0ZWQgdG8gZG9tYWluIG5hbWVzIGFuZCB0aGVcbiAgICogbGVuZ3RocyBvZiBkaWZmZXJlbnQgcGFydHMgb2YgdGhlIGFkZHJlc3MpLlxuICAgKlxuICAgKiBUaGUgZGlmZmVyZW5jZXMgZnJvbSB0aGUgV0hBVFdHIHZlcnNpb24gaW5jbHVkZTpcbiAgICogLSBEaXNhbGxvdyBgbG9jYWwtcGFydGAgKHRoZSBwYXJ0IGJlZm9yZSB0aGUgYEBgIHN5bWJvbCkgdG8gYmVnaW4gb3IgZW5kIHdpdGggYSBwZXJpb2QgKGAuYCkuXG4gICAqIC0gRGlzYWxsb3cgYGxvY2FsLXBhcnRgIHRvIGJlIGxvbmdlciB0aGFuIDY0IGNoYXJhY3RlcnMuXG4gICAqIC0gRGlzYWxsb3cgdGhlIHdob2xlIGFkZHJlc3MgdG8gYmUgbG9uZ2VyIHRoYW4gMjU0IGNoYXJhY3RlcnMuXG4gICAqXG4gICAqIElmIHRoaXMgcGF0dGVybiBkb2VzIG5vdCBzYXRpc2Z5IHlvdXIgYnVzaW5lc3MgbmVlZHMsIHlvdSBjYW4gdXNlIGBWYWxpZGF0b3JzLnBhdHRlc