UNPKG

moh-common-lib

Version:

A library of Angular components, services, and styles for B.C. Government Ministry of Health (MoH).

511 lines 40.3 kB
/** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ import * as tslib_1 from "tslib"; import { Component, Input, ChangeDetectorRef, Output, EventEmitter, Optional, Self } from '@angular/core'; import { Subject, of, throwError } from 'rxjs'; import { debounceTime, distinctUntilChanged, switchMap, map, catchError } from 'rxjs/operators'; import { HttpClient, HttpParams } from '@angular/common/http'; import { NgControl } from '@angular/forms'; import { Address } from '../../models/address.model'; import { AbstractFormControl } from '../../models/abstract-form-control'; import { LabelReplacementTag } from '../../models/error-message.interface'; import { deburr } from '../../../helpers/deburr'; /** * For TemplateForms, pass in an Address and recieve an Address * \@example * <common-address-validator * label='Physical Address' * [(ngModel)]="myAddress"> * </common-address-validator> * * \@note * For ReactiveForms, pass in a string and recieve a string. If you need the * Address object you can use (select) in addition. * * \@example * <common-address-validator * label='Physical Address' * formControlName="address" * (select)="getAddressObject($event)"> * </common-address-validator> * @record */ export function AddressResult() { } if (false) { /** * String from the API that includes street, city, province, and country. * @type {?} */ AddressResult.prototype.AddressComplete; /** @type {?} */ AddressResult.prototype.HouseNumber; /** @type {?} */ AddressResult.prototype.SubBuilding; /** @type {?} */ AddressResult.prototype.Street; /** @type {?} */ AddressResult.prototype.Locality; /** @type {?} */ AddressResult.prototype.DeliveryAddressLines; /** @type {?} */ AddressResult.prototype.AddressLines; /** @type {?} */ AddressResult.prototype.Country; /** @type {?} */ AddressResult.prototype.Province; /** @type {?} */ AddressResult.prototype.PostalCode; } var AddressValidatorComponent = /** @class */ (function (_super) { tslib_1.__extends(AddressValidatorComponent, _super); function AddressValidatorComponent(controlDir, cd, http) { var _this = _super.call(this) || this; _this.controlDir = controlDir; _this.cd = cd; _this.http = http; _this.label = 'Address Lookup'; _this.populateAddressOnSelect = false; _this.addressChange = new EventEmitter(); _this.select = new EventEmitter(); _this.maxlength = '255'; _this._defaultErrMsg = { required: LabelReplacementTag + ' is required.', invalidChar: LabelReplacementTag + ' must contain letters and numbers, and may include special characters such as a hyphen, period, apostrophe, number sign, ampersand, forward slash, and blank characters.' }; /** * Is the request still in progress? */ _this.isTypeaheadLoading = false; /** * has returned and has no results, an empty array. */ _this.hasNoResults = false; _this.hasError = false; /** * Similar to this.address, but we can null it when user is searching for new addresses */ _this.selectedAddress = false; /** * The list of results, from API, that is passed to the typeahead list */ _this.typeaheadList$ = of([]); // Result from address lookup // Result from address lookup /** * The subject that triggers on user text input and gets typeaheadList$ to update. */ _this.searchText$ = new Subject(); _this._onChange = (/** * @param {?} _ * @return {?} */ function (_) { }); _this._onTouched = (/** * @param {?=} _ * @return {?} */ function (_) { }); if (controlDir) { controlDir.valueAccessor = _this; } return _this; } /** * @return {?} */ AddressValidatorComponent.prototype.ngOnInit = /** * @return {?} */ function () { var _this = this; _super.prototype.ngOnInit.call(this); this.typeaheadList$ = this.searchText$.pipe(debounceTime(500), distinctUntilChanged(), // Trigger the network request, get results switchMap((/** * @param {?} searchPhrase * @return {?} */ function (searchPhrase) { return _this.lookup(searchPhrase); })), catchError((/** * @param {?} err * @return {?} */ function (err) { return _this.onError(err); }))); }; /** * @param {?} err * @return {?} */ AddressValidatorComponent.prototype.onError = /** * @param {?} err * @return {?} */ function (err) { this.hasError = true; // Empty array simulates no result response, nothing for typeahead to iterate over return of([]); }; /** * @param {?} val * @return {?} */ AddressValidatorComponent.prototype.onLoading = /** * @param {?} val * @return {?} */ function (val) { this.isTypeaheadLoading = val; this.hasError = false; }; // Note - this will fire after an onError as well // Note - this will fire after an onError as well /** * @param {?} val * @return {?} */ AddressValidatorComponent.prototype.onNoResults = // Note - this will fire after an onError as well /** * @param {?} val * @return {?} */ function (val) { // If we have results, the error has resolved (e.g. network has re-connected) if (val === false) { this.hasError = false; } this.hasNoResults = val; }; /** * @param {?} event * @return {?} */ AddressValidatorComponent.prototype.onSelect = /** * @param {?} event * @return {?} */ function (event) { /** @type {?} */ var data = event.item; // Output string to FormControl. If street is more than the max length shorten /** @type {?} */ var stripped = data.AddressLines ? this.stripStringToMaxLength(deburr(data.AddressLines[0])) : null; /** @type {?} */ var addr = new Address(); addr.unitNumber = deburr(data.SubBuilding); addr.streetNumber = deburr(data.HouseNumber); addr.streetName = deburr(data.Street); addr.city = deburr(data.Locality); addr.country = data.Country; addr.province = data.Province; addr.street = stripped; addr.postal = deburr(data.PostalCode); addr.addressLine1 = data.AddressLines && data.AddressLines[0] ? deburr(data.AddressLines[0]) : null; addr.addressLine2 = data.AddressLines && data.AddressLines[1] ? deburr(data.AddressLines[1]) : null; addr.addressLine3 = data.AddressLines && data.AddressLines[2] ? deburr(data.AddressLines[2]) : null; // Save and emit Address for (select) this.selectedAddress = true; this.select.emit(addr); // For template forms, must explicitly set `search` value upon selecting an item. if (this.populateAddressOnSelect) { this.search = stripped; } this._onChange(stripped); }; /** * @param {?} event * @return {?} */ AddressValidatorComponent.prototype.onKeyUp = /** * @param {?} event * @return {?} */ function (event) { // Filter out 'enter' and other similar keyboard events that can trigger // when user is selecting a typeahead option instead of entering new text. // Without this filter, we do another HTTP request + force disiplay the UI // for now reason if (event.keyCode === 13 || event.keyCode === 9) { // enter & tab return; } // Clear out selection this.selectedAddress = false; this.searchText$.next(this.search); }; /** * @param {?} event * @return {?} */ AddressValidatorComponent.prototype.onBlur = /** * @param {?} event * @return {?} */ function (event) { this._onTouched(); if (this.search) { this._onChange(this.search); } }; /** * @param {?} value * @return {?} */ AddressValidatorComponent.prototype.writeValue = /** * @param {?} value * @return {?} */ function (value) { if (value !== undefined) { this.search = value; } }; /** * @param {?} value * @return {?} */ AddressValidatorComponent.prototype.setSearchValue = /** * @param {?} value * @return {?} */ function (value) { this._onChange(value); this._onTouched(value); }; // Register change function // Register change function /** * @param {?} fn * @return {?} */ AddressValidatorComponent.prototype.registerOnChange = // Register change function /** * @param {?} fn * @return {?} */ function (fn) { this._onChange = fn; }; // Register touched function // Register touched function /** * @param {?} fn * @return {?} */ AddressValidatorComponent.prototype.registerOnTouched = // Register touched function /** * @param {?} fn * @return {?} */ function (fn) { this._onTouched = fn; }; /** * @private * @param {?} str * @return {?} */ AddressValidatorComponent.prototype.stripStringToMaxLength = /** * @private * @param {?} str * @return {?} */ function (str) { /** @type {?} */ var maxlength = parseInt(this.maxlength, 10); return str.slice(0, maxlength); }; /** * @param {?} address * @return {?} */ AddressValidatorComponent.prototype.lookup = /** * @param {?} address * @return {?} */ function (address) { /** @type {?} */ var params = new HttpParams() .set('address', address); return this.http.get(this.serviceUrl, { params: params }).pipe(map(this.processResponse)); }; /** * Formats the response from ADDRESS_URL, trimming irrelevant fields. * * This works for other requests for the same API too, however it may error * out on some items if matchPrecisionNot is not set. * * @param obj The response from ADDRESS_URL */ /** * Formats the response from ADDRESS_URL, trimming irrelevant fields. * * This works for other requests for the same API too, however it may error * out on some items if matchPrecisionNot is not set. * * @protected * @param {?} obj The response from ADDRESS_URL * @return {?} */ AddressValidatorComponent.prototype.processResponse = /** * Formats the response from ADDRESS_URL, trimming irrelevant fields. * * This works for other requests for the same API too, however it may error * out on some items if matchPrecisionNot is not set. * * @protected * @param {?} obj The response from ADDRESS_URL * @return {?} */ function (obj) { return obj.Address.map((/** * @param {?} feature * @return {?} */ function (feature) { /** @type {?} */ var props = feature; /** @type {?} */ var Locality = props.Locality; /** @type {?} */ var AddressComplete = props.AddressComplete; /** @type {?} */ var AddressLines = props.AddressLines; /** @type {?} */ var DeliveryAddressLines = props.DeliveryAddressLines; /** @type {?} */ var Province = props.Province; /** @type {?} */ var Country = props.Country; /** @type {?} */ var PostalCode = props.PostalCode; /** @type {?} */ var SubBuilding = props.SubBuilding; /** @type {?} */ var Street = props.Street; /** @type {?} */ var HouseNumber = props.HouseNumber; return { AddressComplete: AddressComplete, AddressLines: AddressLines, SubBuilding: SubBuilding, Street: Street, HouseNumber: HouseNumber, Locality: Locality, DeliveryAddressLines: DeliveryAddressLines, Province: Province, Country: Country, PostalCode: PostalCode }; })); }; /** * @protected * @param {?} error * @return {?} */ AddressValidatorComponent.prototype.handleError = /** * @protected * @param {?} error * @return {?} */ function (error) { console.error('AddressValidator network error', { error: error }); return throwError('AddressValidator error'); }; AddressValidatorComponent.decorators = [ { type: Component, args: [{ selector: 'common-address-validator', template: "\r\n<label for=\"address-validator_{{label}}\" class='text-nowrap'>{{label}}\r\n <span class=\"address-validator-status\">\r\n <ng-container *ngIf=\"isTypeaheadLoading; else statusContainer\"> &mdash; Loading\r\n <i class=\"fa fa-spinner fa-pulse fa-fw\"></i>\r\n </ng-container>\r\n </span>\r\n\r\n <ng-template #statusContainer>\r\n <ng-template *ngIf=\"selectedAddress; then addressSelected; else error;\"></ng-template>\r\n </ng-template>\r\n</label>\r\n<input class=\"form-control\"\r\n type=\"text\"\r\n id=\"address-validator_{{label}}\"\r\n name=\"address-validator_{{label}}\"\r\n [(ngModel)]='search'\r\n (ngModelChange)='setSearchValue($event)'\r\n (keyup)='onKeyUp($event)'\r\n [typeahead]='typeaheadList$'\r\n [typeaheadIsFirstItemActive]=\"false\"\r\n [typeaheadSelectFirstItem]=\"false\"\r\n (typeaheadLoading)=\"onLoading($event)\"\r\n (typeaheadOnSelect)=\"onSelect($event)\"\r\n (typeaheadNoResults)=\"onNoResults($event)\"\r\n (blur)=\"onBlur($event)\"\r\n [attr.maxlength]='maxlength'\r\n typeaheadOptionField='AddressComplete'\r\n [typeaheadMinLength]=\"3\"\r\n autocomplete=\"off\"\r\n spellcheck=\"false\"\r\n />\r\n <!-- Intentionally using 'nope' for autocomplete as it is invalid and forces false - https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion -->\r\n<common-error-container\r\n [displayError]=\"controlDir && (controlDir?.touched || controlDir?.dirty) && controlDir?.errors\">\r\n <div *ngIf=\"controlDir?.errors?.required\">\r\n {{_defaultErrMsg.required}}\r\n </div>\r\n <div *ngIf=\"controlDir?.errors?.invalidChar\">\r\n {{_defaultErrMsg.invalidChar}}\r\n </div>\r\n</common-error-container>\r\n\r\n<ng-template #addressSelected>\r\n <span> &mdash; Selected\r\n <i class=\"fa fa-check fa-fw text-success\"></i>\r\n </span>\r\n</ng-template>\r\n\r\n<ng-template #noResults>\r\n <span *ngIf=\"search !== '' && hasNoResults\"> &mdash; No Results</span>\r\n</ng-template>\r\n\r\n\r\n<ng-template #error>\r\n <span *ngIf='hasError; else noResults'> &mdash; Error\r\n <i class=\"fa fa-exclamation-triangle fa-fw text-danger\"></i>\r\n </span>\r\n</ng-template>\r\n", styles: [""] }] } ]; /** @nocollapse */ AddressValidatorComponent.ctorParameters = function () { return [ { type: NgControl, decorators: [{ type: Optional }, { type: Self }] }, { type: ChangeDetectorRef }, { type: HttpClient } ]; }; AddressValidatorComponent.propDecorators = { label: [{ type: Input }], address: [{ type: Input }], serviceUrl: [{ type: Input }], populateAddressOnSelect: [{ type: Input }], addressChange: [{ type: Output }], select: [{ type: Output }], maxlength: [{ type: Input }] }; return AddressValidatorComponent; }(AbstractFormControl)); export { AddressValidatorComponent }; if (false) { /** @type {?} */ AddressValidatorComponent.prototype.label; /** @type {?} */ AddressValidatorComponent.prototype.address; /** @type {?} */ AddressValidatorComponent.prototype.serviceUrl; /** @type {?} */ AddressValidatorComponent.prototype.populateAddressOnSelect; /** @type {?} */ AddressValidatorComponent.prototype.addressChange; /** @type {?} */ AddressValidatorComponent.prototype.select; /** @type {?} */ AddressValidatorComponent.prototype.maxlength; /** @type {?} */ AddressValidatorComponent.prototype._defaultErrMsg; /** * The string in the box the user has typed * @type {?} */ AddressValidatorComponent.prototype.search; /** * Is the request still in progress? * @type {?} */ AddressValidatorComponent.prototype.isTypeaheadLoading; /** * has returned and has no results, an empty array. * @type {?} */ AddressValidatorComponent.prototype.hasNoResults; /** @type {?} */ AddressValidatorComponent.prototype.hasError; /** * Similar to this.address, but we can null it when user is searching for new addresses * @type {?} */ AddressValidatorComponent.prototype.selectedAddress; /** * The list of results, from API, that is passed to the typeahead list * @type {?} */ AddressValidatorComponent.prototype.typeaheadList$; /** * The subject that triggers on user text input and gets typeaheadList$ to update. * @type {?} * @private */ AddressValidatorComponent.prototype.searchText$; /** @type {?} */ AddressValidatorComponent.prototype._onChange; /** @type {?} */ AddressValidatorComponent.prototype._onTouched; /** @type {?} */ AddressValidatorComponent.prototype.controlDir; /** * @type {?} * @private */ AddressValidatorComponent.prototype.cd; /** * @type {?} * @protected */ AddressValidatorComponent.prototype.http; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWRkcmVzcy12YWxpZGF0b3IuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6Im5nOi8vbW9oLWNvbW1vbi1saWIvIiwic291cmNlcyI6WyJsaWIvY29tcG9uZW50cy9hZGRyZXNzLXZhbGlkYXRvci9hZGRyZXNzLXZhbGlkYXRvci5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFVLEtBQUssRUFBRSxpQkFBaUIsRUFBRSxNQUFNLEVBQUUsWUFBWSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDbEgsT0FBTyxFQUFFLE9BQU8sRUFBYyxFQUFFLEVBQUUsVUFBVSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQzNELE9BQU8sRUFBRSxZQUFZLEVBQUUsb0JBQW9CLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxVQUFVLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUVoRyxPQUFPLEVBQUUsVUFBVSxFQUFxQixVQUFVLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUNqRixPQUFPLEVBQUUsU0FBUyxFQUF3QixNQUFNLGdCQUFnQixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUNyRCxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUN6RSxPQUFPLEVBQWdCLG1CQUFtQixFQUFFLE1BQU0sc0NBQXNDLENBQUM7QUFDekYsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLHlCQUF5QixDQUFDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUF3QmpELG1DQWFDOzs7Ozs7SUFYQyx3Q0FBd0I7O0lBQ3hCLG9DQUFvQjs7SUFDcEIsb0NBQW9COztJQUNwQiwrQkFBZTs7SUFDZixpQ0FBaUI7O0lBQ2pCLDZDQUE2Qjs7SUFDN0IscUNBQTRCOztJQUU1QixnQ0FBZ0I7O0lBQ2hCLGlDQUFpQjs7SUFDakIsbUNBQW1COztBQUdyQjtJ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LElBQUksT0FBTyxFQUFFO1FBQzFCLElBQUksQ0FBQyxVQUFVLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUMzQyxJQUFJLENBQUMsWUFBWSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUM7UUFDN0MsSUFBSSxDQUFDLFVBQVUsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3RDLElBQUksQ0FBQyxJQUFJLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUNsQyxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUM7UUFDNUIsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDO1FBQzlCLElBQUksQ0FBQyxNQUFNLEdBQUcsUUFBUSxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUN0QyxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksQ0FBQyxZQUFZLElBQUksSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO1FBQ3BHLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDLFlBQVksSUFBSSxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7UUFDcEcsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLENBQUMsWUFBWSxJQUFJLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FB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Qjs7O0lBRXBDLDBDQUEwQzs7SUFDMUMsNENBQXlCOztJQUN6QiwrQ0FBNEI7O0lBQzVCLDREQUFrRDs7SUFDbEQsa0RBQTJFOztJQUMzRSwyQ0FBc0U7O0lBRXRFLDhDQUFtQzs7SUFFbkMsbURBR0U7Ozs7O0lBRUYsMkNBQXNCOzs7OztJQUV0Qix1REFBMkM7Ozs7O0lBRTNDLGlEQUFxQzs7SUFDckMsNkNBQWlDOzs7OztJQUdqQyxvREFBd0M7Ozs7O0lBRXhDLG1EQUE0RDs7Ozs7O0lBRTVELGdEQUE0Qzs7SUFFNUMsOENBQTJCOztJQUMzQiwrQ0FBNkI7O0lBRWpCLCtDQUFnRDs7Ozs7SUFDaEQsdUNBQTZCOzs7OztJQUM3Qix5Q0FBMEIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIE9uSW5pdCwgSW5wdXQsIENoYW5nZURldGVjdG9yUmVmLCBPdXRwdXQsIEV2ZW50RW1pdHRlciwgT3B0aW9uYWwsIFNlbGYgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHsgU3ViamVjdCwgT2JzZXJ2YWJsZSwgb2YsIHRocm93RXJyb3IgfSBmcm9tICdyeGpzJztcclxuaW1wb3J0IHsgZGVib3VuY2VUaW1lLCBkaXN0aW5jdFVudGlsQ2hhbmdlZCwgc3dpdGNoTWFwLCBtYXAsIGNhdGNoRXJyb3IgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XHJcbmltcG9ydCB7IFR5cGVhaGVhZE1hdGNoIH0gZnJvbSAnbmd4LWJvb3RzdHJhcC90eXBlYWhlYWQnO1xyXG5pbXBvcnQgeyBIdHRwQ2xpZW50LCBIdHRwRXJyb3JSZXNwb25zZSwgSHR0cFBhcmFtcyB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbi9odHRwJztcclxuaW1wb3J0IHsgTmdDb250cm9sLCBDb250cm9sVmFsdWVBY2Nlc3NvciB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcclxuaW1wb3J0IHsgQWRkcmVzcyB9IGZyb20gJy4uLy4uL21vZGVscy9hZGRyZXNzLm1vZGVsJztcclxuaW1wb3J0IHsgQWJzdHJhY3RGb3JtQ29udHJvbCB9IGZyb20gJy4uLy4uL21vZGVscy9hYnN0cmFjdC1mb3JtLWNvbnRyb2wnO1xyXG5pbXBvcnQgeyBFcnJvck1lc3NhZ2UsIExhYmVsUmVwbGFjZW1lbnRUYWcgfSBmcm9tICcuLi8uLi9tb2RlbHMvZXJyb3ItbWVzc2FnZS5pbnRlcmZhY2UnO1xyXG5pbXBvcnQgeyBkZWJ1cnIgfSBmcm9tICcuLi8uLi8uLi9oZWxwZXJzL2RlYnVycic7XHJcblxyXG5cclxuXHJcbi8qKlxyXG4gKiBGb3IgVGVtcGxhdGVGb3JtcywgcGFzcyBpbiBhbiBBZGRyZXNzIGFuZCByZWNpZXZlIGFuIEFkZHJlc3NcclxuICogQGV4YW1wbGVcclxuICogICAgICAgICAgIDxjb21tb24tYWRkcmVzcy12YWxpZGF0b3JcclxuICogICAgICAgICAgICAgICBsYWJlbD0nUGh5c2ljYWwgQWRkcmVzcydcclxuICogICAgICAgICAgICAgICBbKG5nTW9kZWwpXT1cIm15QWRkcmVzc1wiPlxyXG4gKiAgICAgICAgICAgPC9jb21tb24tYWRkcmVzcy12YWxpZGF0b3I+XHJcbiAqXHJcbiAqIEBub3RlXHJcbiAqIEZvciBSZWFjdGl2ZUZvcm1zLCBwYXNzIGluIGEgc3RyaW5nIGFuZCByZWNpZXZlIGEgc3RyaW5nLiAgSWYgeW91IG5lZWQgdGhlXHJcbiAqIEFkZHJlc3Mgb2JqZWN0IHlvdSBjYW4gdXNlIChzZWxlY3QpIGluIGFkZGl0aW9uLlxyXG4gKlxyXG4gKiBAZXhhbXBsZVxyXG4gKiAgICAgICAgICAgPGNvbW1vbi1hZGRyZXNzLXZhbGlkYXRvclxyXG4gKiAgICAgICAgICAgICAgbGFiZWw9J1BoeXNpY2FsIEFkZHJlc3MnXHJcbiAqICAgICAgICAgICAgICBmb3JtQ29udHJvbE5hbWU9XCJhZGRyZXNzXCJcclxuICogICAgICAgICAgICAgIChzZWxlY3QpPVwiZ2V0QWRkcmVzc09iamVjdCgkZXZlbnQpXCI+XHJcbiAqICAgICAgICAgIDwvY29tbW9uLWFkZHJlc3MtdmFsaWRhdG9yPlxyXG4gKi9cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgQWRkcmVzc1Jlc3VsdCB7XHJcbiAgLyoqIFN0cmluZyBmcm9tIHRoZSBBUEkgdGhhdCBpbmNsdWRlcyBzdHJlZXQsIGNpdHksIHByb3ZpbmNlLCBhbmQgY291bnRyeS4gKi9cclxuICBBZGRyZXNzQ29tcGxldGU6IHN0cmluZztcclxuICBIb3VzZU51bWJlcjogc3RyaW5nO1xyXG4gIFN1YkJ1aWxkaW5nOiBzdHJpbmc7XHJcbiAgU3RyZWV0OiBzdHJpbmc7XHJcbiAgTG9jYWxpdHk6IHN0cmluZztcclxuICBEZWxpdmVyeUFkZHJlc3NMaW5lczogc3RyaW5nO1xyXG4gIEFkZHJlc3NMaW5lczogQXJyYXk8c3RyaW5nPjtcclxuICAvLyBTZXQgdG8gZGVmYXVsdHMgaW4gcmVzcG9uc2VcclxuICBDb3VudHJ5OiBzdHJpbmc7XHJcbiAgUHJvdmluY2U6IHN0cmluZztcclxuICBQb3N0YWxDb2RlOiBzdHJpbmc7XHJcbn1cclxuXHJcbkBDb21wb25lbnQoe1xyXG4gIHNlbGVjdG9yOiAnY29tbW9uLWFkZHJlc3MtdmFsaWRhdG9yJyxcclxuICB0ZW1wbGF0ZVVybDogJy4vYWRkcmVzcy12YWxpZGF0b3IuY29tcG9uZW50Lmh0bWwnLFxyXG4gIHN0eWxlVXJsczogWycuL2FkZHJlc3MtdmFsaWRhdG9yLmNvbXBvbmVudC5zY3NzJ11cclxufSlcclxuZXhwb3J0IGNsYXNzIEFkZHJlc3NWYWxpZGF0b3JDb21wb25lbnQgZXh0ZW5kcyBBYnN0cmFjdEZvcm1Db250cm9sIGltcGxlbWVudHMgT25Jbml0LCBDb250cm9sVmFsdWVBY2Nlc3NvciB7XHJcblxyXG4gIEBJbnB1dCgpIGxhYmVsOiBzdHJpbmcgPSAnQWRkcmVzcyBMb29rdXAnO1xyXG4gIEBJbnB1dCgpIGFkZHJlc3M6IHN0cmluZztcclxuICBASW5wdXQoKSBzZXJ2aWNlVXJsOiBzdHJpbmc7XHJcbiAgQElucHV0KCkgcG9wdWxhdGVBZGRyZXNzT25TZWxlY3Q6IGJvb2xlYW4gPSBmYWxzZTtcclxuICBAT3V0cHV0KCkgYWRkcmVzc0NoYW5nZTogRXZlbnRFbWl0dGVyPHN0cmluZz4gPSBuZXcgRXZlbnRFbWl0dGVyPHN0cmluZz4oKTtcclxuICBAT3V0cHV0KCkgc2VsZWN0OiBFdmVudEVtaXR0ZXI8QWRkcmVzcz4gPSBuZXcgRXZlbnRFbWl0dGVyPEFkZHJlc3M+KCk7XHJcblxyXG4gIEBJbnB1dCgpIG1heGxlbmd0aDogc3RyaW5nID0gJzI1NSc7XHJcblxyXG4gIF9kZWZhdWx0RXJyTXNnOiBFcnJvck1lc3NhZ2UgPSB7XHJcbiAgICByZXF1aXJlZDogIExhYmVsUmVwbGFjZW1lbnRUYWcgKyAnIGlzIHJlcXVpcmVkLicsXHJcbiAgICBpbnZhbGlkQ2hhcjogTGFiZWxSZXBsYWNlbWVudFRhZyArICcgbXVzdCBjb250YWluIGxldHRlcnMgYW5kIG51bWJlcnMsIGFuZCBtYXkgaW5jbHVkZSBzcGVjaWFsIGNoYXJhY3RlcnMgc3VjaCBhcyBhIGh5cGhlbiwgcGVyaW9kLCBhcG9zdHJvcGhlLCBudW1iZXIgc2lnbiwgYW1wZXJzYW5kLCBmb3J3YXJkIHNsYXNoLCBhbmQgYmxhbmsgY2hhcmFjdGVycy4nXHJcbiAgfTtcclxuICAvKiogVGhlIHN0cmluZyBpbiB0aGUgYm94IHRoZSB1c2VyIGhhcyB0eXBlZCAqL1xyXG4gIHB1YmxpYyBzZWFyY2g6IHN0cmluZztcclxuICAvKiogSXMgdGhlIHJlcXVlc3Qgc3RpbGwgaW4gcHJvZ3Jlc3M/ICovXHJcbiAgcHVibGljIGlzVHlwZWFoZWFkTG9hZGluZzogYm9vbGVhbiA9IGZhbHNlO1xyXG4gIC8qKiBoYXMgcmV0dXJuZWQgYW5kIGhhcyBubyByZXN1bHRzLCBhbiBlbXB0eSBhcnJheS4gKi9cclxuICBwdWJsaWMgaGFzTm9SZXN1bHRzOiBib29sZWFuID0gZmFsc2U7XHJcbiAgcHVibGljIGhhc0Vycm9yOiBib29sZWFuID0gZmFsc2U7XHJcblxyXG4gIC8qKiBTaW1pbGFyIHRvIHRoaXMuYWRkcmVzcywgYnV0IHdlIGNhbiBudWxsIGl0IHdoZW4gdXNlciBpcyBzZWFyY2hpbmcgZm9yIG5ldyBhZGRyZXNzZXMgKi9cclxuICBwdWJsaWMgc2VsZWN0ZWRBZGRyZXNzOiBib29sZWFuID0gZmFsc2U7XHJcbiAgLyoqIFRoZSBsaXN0IG9mIHJlc3VsdHMsIGZyb20gQVBJLCB0aGF0IGlzIHBhc3NlZCB0byB0aGUgdHlwZWFoZWFkIGxpc3QgKi9cclxuICBwdWJsaWMgdHlwZWFoZWFkTGlzdCQ6IE9ic2VydmFibGU8QWRkcmVzc1Jlc3VsdFtdPiA9IG9mKFtdKTsgLy8gUmVzdWx0IGZyb20gYWRkcmVzcyBsb29rdXBcclxuICAvKiogVGhlIHN1YmplY3QgdGhhdCB0cmlnZ2VycyBvbiB1c2VyIHRleHQgaW5wdXQgYW5kIGdldHMgdHlwZWFoZWFkTGlzdCQgdG8gdXBkYXRlLiAgKi9cclxuICBwcml2YXRlIHNlYXJjaFRleHQkID0gbmV3IFN1YmplY3Q8c3RyaW5nPigpO1xyXG5cclxuICBfb25DaGFuZ2UgPSAoXzogYW55KSA9PiB7fTtcclxuICBfb25Ub3VjaGVkID0gKF8/OiBhbnkpID0+IHt9O1xyXG5cclxuICBjb25zdHJ1Y3RvcihAT3B0aW9uYWwoKSBAU2VsZigpIHB1YmxpYyBjb250cm9sRGlyOiBOZ0NvbnRyb2wsXHJcbiAgICAgICAgICAgICAgcHJpdmF0ZSBjZDogQ2hhbmdlRGV0ZWN0b3JSZWYsXHJcbiAgICAgICAgICAgICAgcHJvdGVjdGVkIGh0dHA6IEh0dHBDbGllbnQpIHtcclxuICAgIHN1cGVyKCk7XHJcbiAgICBpZiAoIGNvbnRyb2xEaXIgKSB7XHJcbiAgICAgIGNvbnRyb2xEaXIudmFsdWVBY2Nlc3NvciA9IHRoaXM7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBuZ09uSW5pdCgpIHtcclxuICAgIHN1cGVyLm5nT25Jbml0KCk7XHJcbiAgICBcclxuICAgIHRoaXMudHlwZWFoZWFkTGlzdCQgPSB0aGlzLnNlYXJjaFRleHQkLnBpcGUoXHJcbiAgICAgIGRlYm91bmNlVGltZSg1MDApLFxyXG4gICAgICBkaXN0aW5jdFVudGlsQ2hhbmdlZCgpLFxyXG4gICAgICAvLyBUcmlnZ2VyIHRoZSBuZXR3b3JrIHJlcXVlc3QsIGdldCByZXN1bHRzXHJcbiAgICAgIHN3aXRjaE1hcChzZWFyY2hQaHJhc2UgPT4gdGhpcy5sb29rdXAoc2VhcmNoUGhyYXNlKSksXHJcbiAgICAgIGNhdGNoRXJyb3IoZXJyID0+IHRoaXMub25FcnJvcihlcnIpKVxyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIG9uRXJyb3IoZXJyKTogT2JzZXJ2YWJsZTxBZGRyZXNzUmVzdWx0W10+IHtcclxuICAgIHRoaXMuaGFzRXJyb3IgPSB0cnVlO1xyXG4gICAgLy8gRW1wdHkgYXJyYXkgc2ltdWxhdGVzIG5vIHJlc3VsdCByZXNwb25zZSwgbm90aGluZyBmb3IgdHlwZWFoZWFkIHRvIGl0ZXJhdGUgb3ZlclxyXG4gICAgcmV0dXJuIG9mKFtdKTtcclxuICB9XHJcblxyXG4gIG9uTG9hZGluZyh2YWw6IGJvb2xlYW4pOiB2b2lkIHtcclxuICAgIHRoaXMuaXNUeXBlYWhlYWRMb2FkaW5nID0gdmFsO1xyXG4gICAgdGhpcy5oYXNFcnJvciA9IGZhbHNlO1xyXG4gIH1cclxuXHJcbiAgLy8gTm90ZSAtIHRoaXMgd2lsbCBmaXJlIGFmdGVyIGFuIG9uRXJyb3IgYXMgd2VsbFxyXG4gIG9uTm9SZXN1bHRzKHZhbDogYm9vbGVhbik6IHZvaWQge1xyXG4gICAgLy8gSWYgd2UgaGF2ZSByZXN1bHRzLCB0aGUgZXJyb3IgaGFzIHJlc29sdmVkIChlLmcuIG5ldHdvcmsgaGFzIHJlLWNvbm5lY3RlZClcclxuICAgIGlmICh2YWwgPT09IGZhbHNlKSB7XHJcbiAgICAgIHRoaXMuaGFzRXJyb3IgPSBmYWxzZTtcclxuICAgIH1cclxuXHJcbiAgICB0aGlzLmhhc05vUmVzdWx0cyA9IHZhbDtcclxuICB9XHJcblxyXG4gIG9uU2VsZWN0KGV2ZW50OiBUeXBlYWhlYWRNYXRjaCk6IHZvaWQge1xyXG5cclxuICAgIGNvbnN0IGRhdGE6IEFkZHJlc3NSZXN1bHQgPSBldmVudC5pdGVtO1xyXG5cclxuICAgIC8vIE91dHB1dCBzdHJpbmcgdG8gRm9ybUNvbnRyb2wuIElmIHN0cmVldCBpcyBtb3JlIHRoYW4gdGhlIG1heCBsZW5ndGggc2hvcnRlblxyXG4gICAgY29uc3Qgc3RyaXBwZWQgPSBkYXRhLkFkZHJlc3NMaW5lcyA/IHRoaXMuc3RyaXBTdHJpbmdUb01heExlbmd0aChkZWJ1cnIoZGF0YS5BZGRyZXNzTGluZXNbMF0pKSA6IG51bGw7XHJcblxyXG4gICAgY29uc3QgYWRkciA9IG5ldyBBZGRyZXNzKCk7XHJcbiAgICBhZGRyLnVuaXROdW1iZXIgPSBkZWJ1cnIoZGF0YS5TdWJCdWlsZGluZyk7XHJcbiAgICBhZGRyLnN0cmVldE51bWJlciA9IGRlYnVycihkYXRhLkhvdXNlTnVtYmVyKTtcclxuICAgIGFkZHIuc3RyZWV0TmFtZSA9IGRlYnVycihkYXRhLlN0cmVldCk7XHJcbiAgICBhZGRyLmNpdHkgPSBkZWJ1cnIoZGF0YS5Mb2NhbGl0eSk7XHJcbiAgICBhZGRyLmNvdW50cnkgPSBkYXRhLkNvdW50cnk7XHJcbiAgICBhZGRyLnByb3ZpbmNlID0gZGF0YS5Qcm92aW5jZTtcclxuICAgIGFkZHIuc3RyZWV0ID0gc3RyaXBwZWQ7XHJcbiAgICBhZGRyLnBvc3RhbCA9IGRlYnVycihkYXRhLlBvc3RhbENvZGUpO1xyXG4gICAgYWRkci5hZGRyZXNzTGluZTEgPSBkYXRhLkFkZHJlc3NMaW5lcyAmJiBkYXRhLkFkZHJlc3NMaW5lc1swXSA/IGRlYnVycihkYXRhLkFkZHJlc3NMaW5lc1swXSkgOiBudWxsO1xyXG4gICAgYWRkci5hZGRyZXNzTGluZTIgPSBkYXRhLkFkZHJlc3NMaW5lcyAmJiBkYXRhLkFkZHJlc3NMaW5lc1sxXSA/IGRlYnVycihkYXRhLkFkZHJlc3NMaW5lc1sxXSkgOiBudWxsO1xyXG4gICAgYWRkci5hZGRyZXNzTGluZTMgPSBkYXRhLkFkZHJlc3NMaW5lcyAmJiBkYXRhLkFkZHJlc3NMaW5lc1syXSA/IGRlYnVycihkYXRhLkFkZHJlc3NMaW5lc1syXSkgOiBudWxsO1xyXG4gICAgLy8gU2F2ZSBhbmQgZW1pdCBBZGRyZXNzIGZvciAoc2VsZWN0KVxyXG4gICAgdGhpcy5zZWxlY3RlZEFkZHJlc3MgPSB0cnVlO1xyXG4gICAgdGhpcy5zZWxlY3QuZW1pdChhZGRyKTtcclxuXHJcbiAgICAvLyBGb3IgdGVtcGxhdGUgZm9ybXMsIG11c3QgZXhwbGljaXRseSBzZXQgYHNlYXJjaGAgdmFsdWUgdXBvbiBzZWxlY3RpbmcgYW4gaXRlbS5cclxuICAgIGlmICh0aGlzLnBvcHVsYXRlQWRkcmVzc09uU2VsZWN0KSB7XHJcbiAgICAgIHRoaXMuc2VhcmNoID0gc3RyaXBwZWQ7XHJcbiAgICB9XHJcbiAgICB0aGlzLl9vbkNoYW5nZShzdHJpcHBlZCk7XHJcbiAgfVxyXG5cclxuICBvbktleVVwKGV2ZW50OiBLZXlib2FyZEV2ZW50KTogdm9pZCB7XHJcbiAgICAvLyBGaWx0ZXIgb3V0ICdlbnRlcicgYW5kIG90aGVyIHNpbWlsYXIga2V5Ym9hcmQgZXZlbnRzIHRoYXQgY2FuIHRyaWdnZXJcclxuICAgIC8vIHdoZW4gdXNlciBpcyBzZWxlY3RpbmcgYSB0eXBlYWhlYWQgb3B0aW9uIGluc3RlYWQgb2YgZW50ZXJpbmcgbmV3IHRleHQuXHJcbiAgICAvLyBXaXRob3V0IHRoaXMgZmlsdGVyLCB3ZSBkbyBhbm90aGVyIEhUVFAgcmVxdWVzdCArIGZvcmNlIGRpc2lwbGF5IHRoZSBVSVxyXG4gICAgLy8gZm9yIG5vdyByZWFzb25cclxuICAgIGlmIChldmVudC5rZXlDb2RlID09PSAxMyB8fCBldmVudC5rZXlDb2RlID09PSA5KSB7ICAvLyBlbnRlciAmIHRhYlxyXG4gICAgICByZXR1cm47XHJcbiAgICB9XHJcbiAgICAvLyBDbGVhciBvdXQgc2VsZWN0aW9uXHJcbiAgICB0aGlzLnNlbGVjdGVkQWRkcmVzcyA9IGZhbHNlO1xyXG4gICAgdGhpcy5zZWFyY2hUZXh0JC5uZXh0KHRoaXMuc2VhcmNoKTtcclxuICB9XHJcblxyXG4gIG9uQmx1cihldmVudCk6IHZvaWQge1xyXG4gICAgdGhpcy5fb25Ub3VjaGVkKCk7XHJcbiAgICBpZiAodGhpcy5zZWFyY2gpIHtcclxuICAgICAgdGhpcy5fb25DaGFuZ2UodGhpcy5zZWFyY2gpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgd3JpdGVWYWx1ZSggdmFsdWU6IGFueSApOiB2b2lkIHtcclxuICAgIGlmICggdmFsdWUgICE9PSB1bmRlZmluZWQgKSB7XHJcbiAgICAgIHRoaXMuc2VhcmNoID0gdmFsdWU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBzZXRTZWFyY2hWYWx1ZSh2YWx1ZTogYW55KSB7XHJcbiAgICB0aGlzLl9vbkNoYW5nZSh2YWx1ZSk7XHJcbiAgICB0aGlzLl9vblRvdWNoZWQodmFsdWUpO1xyXG4gIH1cclxuXHJcbiAgLy8gUmVnaXN0ZXIgY2hhbmdlIGZ1bmN0aW9uXHJcbiAgcmVnaXN0ZXJPbkNoYW5nZSggZm46IGFueSApOiB2b2lkIHtcclxuICAgIHRoaXMuX29uQ2hhbmdlID0gZm47XHJcbiAgfVxyXG5cclxuICAvLyBSZWdpc3RlciB0b3VjaGVkIGZ1bmN0aW9uXHJcbiAgcmVnaXN0ZXJPblRvdWNoZWQoIGZuOiBhbnkgKTogdm9pZCB7XHJcbiAgICB0aGlzLl9vblRvdWNoZWQgPSBmbjtcclxuICB9XHJcblxyXG4gIHByaXZhdGUgc3RyaXBTdHJpbmdUb01heExlbmd0aChzdHI6IHN0cmluZykge1xyXG4gICAgY29uc3QgbWF4bGVuZ3RoID0gcGFyc2VJbnQodGhpcy5tYXhsZW5ndGgsIDEwKTtcclxuICAgIHJldHVybiBzdHIuc2xpY2UoMCwgbWF4bGVuZ3RoKTtcclxuICB9XHJcblxyXG4gIGxvb2t1cChhZGRyZXNzOiBzdHJpbmcpOiBPYnNlcnZhYmxlPEFkZHJlc3NSZXN1bHRbXT4ge1xyXG4gICAgY29uc3QgcGFyYW1zID0gbmV3IEh0dHBQYXJhbXMoKVxyXG4gICAgICAgICAgICAgICAgICAgIC5zZXQoJ2FkZHJlc3MnLCBhZGRyZXNzKTtcclxuXHJcbiAgICByZXR1cm4gdGhpcy5odHRwLmdldCh0aGlzLnNlcnZpY2VVcmwsIHtcclxuICAgICAgcGFyYW1zOiBwYXJhbXNcclxuICAgIH0pLnBpcGUobWFwKHRoaXMucHJvY2Vzc1Jlc3BvbnNlKSk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBGb3JtYXRzIHRoZSByZXNwb25zZSBmcm9tIEFERFJFU1NfVVJMLCB0cmltbWluZyBpcnJlbGV2YW50IGZpZWxkcy5cclxuICAgKlxyXG4gICAqIFRoaXMgd29ya3MgZm9yIG90aGVyIHJlcXVlc3RzIGZvciB0aGUgc2FtZSBBUEkgdG9vLCBob3dldmVyIGl0IG1heSBlcnJvclxyXG4gICAqIG91dCBvbiBzb21lIGl0ZW1zIGlmIG1hdGNoUHJlY2lzaW9uTm90IGlzIG5vdCBzZXQuXHJcbiAgICpcclxuICAgKiBAcGFyYW0gb2JqIFRoZSByZXNwb25zZSBmcm9tIEFERFJFU1NfVVJMXHJcbiAgICovXHJcbiAgcHJvdGVjdGVkIHByb2Nlc3NSZXNwb25zZShvYmopOiBBZGRyZXNzUmVzdWx0W10ge1xyXG4gICAgcmV0dXJuIG9iai5BZGRyZXNzLm1hcChmZWF0dXJlID0+IHtcclxuICAgICAgY29uc3QgcHJvcHMgPSBmZWF0dXJlO1xyXG4gICAgICBjb25zdCBMb2NhbGl0eSA9IHByb3BzLkxvY2FsaXR5O1xyXG4gICAgICBjb25zdCBBZGRyZXNzQ29tcGxldGUgPSBwcm9wcy5BZGRyZXNzQ29tcGxldGU7XHJcbiAgICAgIGNvbnN0IEFkZHJlc3NMaW5lcyA9IHByb3BzLkFkZHJlc3NMaW5lcztcclxuICAgICAgY29uc3QgRGVsaXZlcnlBZGRyZXNzTGluZXMgPSBwcm9wcy5EZWxpdmVyeUFkZHJlc3NMaW5lcztcclxuICAgICAgY29uc3QgUHJvdmluY2UgPSBwcm9wcy5Qcm92aW5jZTtcclxuICAgICAgY29uc3QgQ291bnRyeSA9IHByb3BzLkNvdW50cnk7XHJcbiAgICAgIGNvbnN0IFBvc3RhbENvZGUgPSBwcm9wcy5Qb3N0YWxDb2RlO1xyXG4gICAgICBjb25zdCBTdWJCdWlsZGluZyA9IHByb3BzLlN1YkJ1aWxkaW5nO1xyXG4gICAgICBjb25zdCBTdHJlZXQgPSBwcm9wcy5TdHJlZXQ7XHJcbiAgICAgIGNvbnN0IEhvdXNlTnVtYmVyID0gcHJvcHMuSG91c2VOdW1iZXI7XHJcblxyXG4gICAgICByZXR1cm4ge1xyXG4gICAgICAgIEFkZHJlc3NDb21wbGV0ZSxcclxuICAgICAgICBBZGRyZXNzTGluZXMsXHJcbiAgICAgICAgU3ViQnVpbGRpbmcsXHJcbiAgICAgICAgU3RyZWV0LFxyXG4gICAgICAgIEhvdXNlTnVtYmVyLFxyXG4gICAgICAgIExvY2FsaXR5LFxyXG4gICAgICAgIERlbGl2ZXJ5QWRkcmVzc0xpbmVzLFxyXG4gICAgICAgIFByb3ZpbmNlLFxyXG4gICAgICAgIENvdW50cnksXHJcbiAgICAgICAgUG9zdGFsQ29kZVxyXG4gICAgICB9O1xyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICBwcm90ZWN0ZWQgaGFuZGxlRXJyb3IoZXJyb3I6IEh0dHBFcnJvclJlc3BvbnNlKSB7XHJcbiAgICBjb25zb2xlLmVycm9yKCdBZGRyZXNzVmFsaWRhdG9yIG5ldHdvcmsgZXJyb3InLCB7IGVycm9yIH0pO1xyXG4gICAgcmV0dXJuIHRocm93RXJyb3IoJ0FkZHJlc3NWYWxpZGF0b3IgZXJyb3InKTtcclxuICB9XHJcbn1cclxuIl19