moh-common-lib
Version:
A library of Angular components, services, and styles for B.C. Government Ministry of Health (MoH).
426 lines • 36.9 kB
JavaScript
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
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;
}
export class AddressValidatorComponent extends AbstractFormControl {
/**
* @param {?} controlDir
* @param {?} cd
* @param {?} http
*/
constructor(controlDir, cd, http) {
super();
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 {?}
*/
(_) => { });
this._onTouched = (/**
* @param {?=} _
* @return {?}
*/
(_) => { });
if (controlDir) {
controlDir.valueAccessor = this;
}
}
/**
* @return {?}
*/
ngOnInit() {
super.ngOnInit();
this.typeaheadList$ = this.searchText$.pipe(debounceTime(500), distinctUntilChanged(),
// Trigger the network request, get results
switchMap((/**
* @param {?} searchPhrase
* @return {?}
*/
searchPhrase => this.lookup(searchPhrase))), catchError((/**
* @param {?} err
* @return {?}
*/
err => this.onError(err))));
}
/**
* @param {?} err
* @return {?}
*/
onError(err) {
this.hasError = true;
// Empty array simulates no result response, nothing for typeahead to iterate over
return of([]);
}
/**
* @param {?} val
* @return {?}
*/
onLoading(val) {
this.isTypeaheadLoading = val;
this.hasError = false;
}
// Note - this will fire after an onError as well
/**
* @param {?} val
* @return {?}
*/
onNoResults(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 {?}
*/
onSelect(event) {
/** @type {?} */
const data = event.item;
// Output string to FormControl. If street is more than the max length shorten
/** @type {?} */
const stripped = data.AddressLines ? this.stripStringToMaxLength(deburr(data.AddressLines[0])) : null;
/** @type {?} */
const 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 {?}
*/
onKeyUp(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 {?}
*/
onBlur(event) {
this._onTouched();
if (this.search) {
this._onChange(this.search);
}
}
/**
* @param {?} value
* @return {?}
*/
writeValue(value) {
if (value !== undefined) {
this.search = value;
}
}
/**
* @param {?} value
* @return {?}
*/
setSearchValue(value) {
this._onChange(value);
this._onTouched(value);
}
// Register change function
/**
* @param {?} fn
* @return {?}
*/
registerOnChange(fn) {
this._onChange = fn;
}
// Register touched function
/**
* @param {?} fn
* @return {?}
*/
registerOnTouched(fn) {
this._onTouched = fn;
}
/**
* @private
* @param {?} str
* @return {?}
*/
stripStringToMaxLength(str) {
/** @type {?} */
const maxlength = parseInt(this.maxlength, 10);
return str.slice(0, maxlength);
}
/**
* @param {?} address
* @return {?}
*/
lookup(address) {
/** @type {?} */
const 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.
*
* @protected
* @param {?} obj The response from ADDRESS_URL
* @return {?}
*/
processResponse(obj) {
return obj.Address.map((/**
* @param {?} feature
* @return {?}
*/
feature => {
/** @type {?} */
const props = feature;
/** @type {?} */
const Locality = props.Locality;
/** @type {?} */
const AddressComplete = props.AddressComplete;
/** @type {?} */
const AddressLines = props.AddressLines;
/** @type {?} */
const DeliveryAddressLines = props.DeliveryAddressLines;
/** @type {?} */
const Province = props.Province;
/** @type {?} */
const Country = props.Country;
/** @type {?} */
const PostalCode = props.PostalCode;
/** @type {?} */
const SubBuilding = props.SubBuilding;
/** @type {?} */
const Street = props.Street;
/** @type {?} */
const HouseNumber = props.HouseNumber;
return {
AddressComplete,
AddressLines,
SubBuilding,
Street,
HouseNumber,
Locality,
DeliveryAddressLines,
Province,
Country,
PostalCode
};
}));
}
/**
* @protected
* @param {?} error
* @return {?}
*/
handleError(error) {
console.error('AddressValidator network 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\"> — 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> — 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\"> — No Results</span>\r\n</ng-template>\r\n\r\n\r\n<ng-template #error>\r\n <span *ngIf='hasError; else noResults'> — 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 = () => [
{ 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 }]
};
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWRkcmVzcy12YWxpZGF0b3IuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6Im5nOi8vbW9oLWNvbW1vbi1saWIvIiwic291cmNlcyI6WyJsaWIvY29tcG9uZW50cy9hZGRyZXNzLXZhbGlkYXRvci9hZGRyZXNzLXZhbGlkYXRvci5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7OztBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQVUsS0FBSyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sRUFBRSxZQUFZLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUNsSCxPQUFPLEVBQUUsT0FBTyxFQUFjLEVBQUUsRUFBRSxVQUFVLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDM0QsT0FBTyxFQUFFLFlBQVksRUFBRSxvQkFBb0IsRUFBRSxTQUFTLEVBQUUsR0FBRyxFQUFFLFVBQVUsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRWhHLE9BQU8sRUFBRSxVQUFVLEVBQXFCLFVBQVUsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ2pGLE9BQU8sRUFBRSxTQUFTLEVBQXdCLE1BQU0sZ0JBQWdCLENBQUM7QUFDakUsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3JELE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBQ3pFLE9BQU8sRUFBZ0IsbUJBQW1CLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUN6RixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0seUJBQXlCLENBQUM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXdCakQsbUNBYUM7Ozs7OztJQVhDLHdDQUF3Qjs7SUFDeEIsb0NBQW9COztJQUNwQixvQ0FBb0I7O0lBQ3BCLCtCQUFlOztJQUNmLGlDQUFpQjs7SUFDakIsNkNBQTZCOztJQUM3QixxQ0FBNEI7O0lBRTVCLGdDQUFnQjs7SUFDaEIsaUNBQWlCOztJQUNqQixtQ0FBbUI7O0FBUXJCLE1B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SSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQzNDLElBQUksQ0FBQyxZQUFZLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUM3QyxJQUFJLENBQUMsVUFBVSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDdEMsSUFBSSxDQUFDLElBQUksR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ2xDLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQztRQUM1QixJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUM7UUFDOUIsSUFBSSxDQUFDLE1BQU0sR0FBRyxRQUFRLENBQUM7UUFDdkIsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ3RDLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDLFlBQVksSUFBSSxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7UUFDcEcsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLENBQUMsWUFBWSxJQUFJLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztRQUNwRyxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksQ0FBQyxZQUFZLElBQUksSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO1FBQ3BHLHFDQUFxQztRQUNyQyxJQUFJLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQztRQUM1QixJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUV2QixpRkFBaUY7UUFDakYsSUFBSSxJQUFJLENBQUMsdUJBQXVCLEVBQUU7WUFDaEMsSUFBSSxDQUFDLE1BQU0sR0FBRyxRQUFRLENBQUM7U0FDeEI7UUFDRCxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQzNCLENBQUM7Ozs7O0lBRUQsT0FBTyxDQUFDLEtBQW9CO1FBQzFCLHdFQUF3RTtRQUN4RSwwRUFBMEU7UUFDMUUsMEVBQTBFO1FBQzFFLGlCQUFpQjtRQUNqQixJQUFJLEtBQUssQ0FBQyxPQUFPLEtBQUssRUFBRSxJQUFJLEtBQUssQ0FBQyxPQUFPLEtBQUssQ0FBQyxFQUFFLEVBQUcsY0FBYztZQUNoRSxPQUFPO1NBQ1I7UUFDRCxzQkFBc0I7UUFDdEIsSUFBSSxDQUFDLGVBQWUsR0FBRyxLQUFLLENBQUM7UUFDN0IsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ3JDLENBQUM7Ozs7O0lBRUQsTUFBTSxDQUFDLEtBQUs7UUFDVixJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7UUFDbEIsSUFBSSxJQUFJLENBQUMsTUFBTSxFQUFFO1lBQ2YsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7U0FDN0I7SUFDSCxDQUFDOzs7OztJQUVELFVBQVUsQ0FBRSxLQUFVO1FBQ3BCLElBQUssS0FBSyxLQUFNLFNBQVMsRUFBRztZQUMxQixJQUFJLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQztTQUNyQjtJQUNILENBQUM7Ozs7O0lBRUQsY0FBYyxDQUFDLEtBQVU7UUFDdkIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUN0QixJQUFJLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3pCLENBQUM7Ozs7OztJQUdELGdCQUFnQixDQUFFLEVBQU87UUFDdkIsSUFBSSxDQUFDLFNBQVMsR0FBRyxFQUFFLENBQUM7SUFDdEIsQ0FBQzs7Ozs7O0lBR0QsaUJBQWlCLENBQUUsRUFBTztRQUN4QixJQUFJLENBQUMsVUFBVSxHQUFHLEVBQUUsQ0FBQztJQUN2QixDQUFDOzs7Ozs7SUFFTyxzQkFBc0IsQ0FBQyxHQUFXOztjQUNsQyxTQUFTLEdBQUcsUUFBUSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsRUFBRSxDQUFDO1FBQzlDLE9BQU8sR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsU0FBUyxDQUFDLENBQUM7SUFDakMsQ0FBQzs7Ozs7SUFFRCxNQUFNLENBQUMsT0FBZTs7Y0FDZCxNQUFNLEdBQUcsSUFBSSxVQUFVLEVBQUU7YUFDZCxHQUFHLENBQUMsU0FBUyxFQUFFLE9BQU8sQ0FBQztRQUV4QyxPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUU7WUFDcEMsTUFBTSxFQUFFLE1BQU07U0FDZixDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUMsQ0FBQztJQUNyQyxDQUFDOzs7Ozs7Ozs7OztJQVVTLGVBQWUsQ0FBQyxHQUFHO1FBQzNCLE9BQU8sR0FBRyxDQUFDLE9BQU8sQ0FBQyxHQUFHOzs7O1FBQUMsT0FBTyxDQUFDLEVBQUU7O2tCQUN6QixLQUFLLEdBQUcsT0FBTzs7a0JBQ2YsUUFBUSxHQUFHLEtBQUssQ0FBQyxRQUFROztrQkFDekIsZUFBZSxHQUFHLEtBQUssQ0FBQyxlQUFlOztrQkFDdkMsWUFBWSxHQUFHLEtBQUssQ0FBQyxZQUFZOztrQkFDakMsb0JBQW9CLEdBQUcsS0FBSyxDQUFDLG9CQUFvQjs7a0JBQ2pELFFBQVEsR0FBRyxLQUFLLENBQUMsUUFBUTs7a0JBQ3pCLE9BQU8sR0FBRyxLQUFLLENBQUMsT0FBTzs7a0JBQ3ZCLFVBQVUsR0FBRyxLQUFLLENBQUMsVUFBVTs7a0JBQzdCLFdBQVcsR0FBRyxLQUFLLENBQUMsV0FBVzs7a0JBQy9CLE1BQU0sR0FBRyxLQUFLLENBQUMsTUFBTTs7a0JBQ3JCLFdBQVcsR0FBRyxLQUFLLENBQUMsV0FBVztZQUVyQyxPQUFPO2dCQUNMLGVBQWU7Z0JBQ2YsWUFBWTtnQkFDWixXQUFXO2dCQUNYLE1BQU07Z0JBQ04sV0FBVztnQkFDWCxRQUFRO2dCQUNSLG9CQUFvQjtnQkFDcEIsUUFBUTtnQkFDUixPQUFPO2dCQUNQLFVBQVU7YUFDWCxDQUFDO1FBQ0osQ0FBQyxFQUFDLENBQUM7SUFDTCxDQUFDOzs7Ozs7SUFFUyxXQUFXLENBQUMsS0FBd0I7UUFDNUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxnQ0FBZ0MsRUFBRSxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUM7UUFDM0QsT0FBTyxVQUFVLENBQUMsd0JBQXdCLENBQUMsQ0FBQztJQUM5QyxDQUFDOzs7WUE3TUYsU0FBUyxTQUFDO2dCQUNULFFBQVEsRUFBRSwwQkFBMEI7Z0JBQ3BDLHV6RUFBaUQ7O2FBRWxEOzs7O1lBL0NRLFNBQVMsdUJBaUZILFFBQVEsWUFBSSxJQUFJO1lBdEZJLGlCQUFpQjtZQUkzQyxVQUFVOzs7b0JBbURoQixLQUFLO3NCQUNMLEtBQUs7eUJBQ0wsS0FBSztzQ0FDTCxLQUFLOzRCQUNMLE1BQU07cUJBQ04sTUFBTTt3QkFFTixLQUFLOzs7O0lBUE4sMENBQTBDOztJQUMxQyw0Q0FBeUI7O0lBQ3pCLCtDQUE0Qjs7SUFDNUIsNERBQWtEOztJQUNsRCxrREFBMkU7O0lBQzNFLDJDQUFzRTs7SUFFdEUsOENBQW1DOztJQUVuQyxtREFHRTs7Ozs7SUFFRiwyQ0FBc0I7Ozs7O0lBRXRCLHVEQUEyQzs7Ozs7SUFFM0MsaURBQXFDOztJQUNyQyw2Q0FBaUM7Ozs7O0lBR2pDLG9EQUF3Qzs7Ozs7SUFFeEMsbURBQTREOzs7Ozs7SUFFNUQsZ0RBQTRDOztJQUU1Qyw4Q0FBMkI7O0lBQzNCLCtDQUE2Qjs7SUFFakIsK0NBQWdEOzs7OztJQUNoRCx1Q0FBNkI7Ozs7O0lBQzdCLHlDQUEwQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgT25Jbml0LCBJbnB1dCwgQ2hhbmdlRGV0ZWN0b3JSZWYsIE91dHB1dCwgRXZlbnRFbWl0dGVyLCBPcHRpb25hbCwgU2VsZiB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQgeyBTdWJqZWN0LCBPYnNlcnZhYmxlLCBvZiwgdGhyb3dFcnJvciB9IGZyb20gJ3J4anMnO1xyXG5pbXBvcnQgeyBkZWJvdW5jZVRpbWUsIGRpc3RpbmN0VW50aWxDaGFuZ2VkLCBzd2l0Y2hNYXAsIG1hcCwgY2F0Y2hFcnJvciB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcclxuaW1wb3J0IHsgVHlwZWFoZWFkTWF0Y2ggfSBmcm9tICduZ3gtYm9vdHN0cmFwL3R5cGVhaGVhZCc7XHJcbmltcG9ydCB7IEh0dHBDbGllbnQsIEh0dHBFcnJvclJlc3BvbnNlLCBIdHRwUGFyYW1zIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uL2h0dHAnO1xyXG5pbXBvcnQgeyBOZ0NvbnRyb2wsIENvbnRyb2xWYWx1ZUFjY2Vzc29yIH0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xyXG5pbXBvcnQgeyBBZGRyZXNzIH0gZnJvbSAnLi4vLi4vbW9kZWxzL2FkZHJlc3MubW9kZWwnO1xyXG5pbXBvcnQgeyBBYnN0cmFjdEZvcm1Db250cm9sIH0gZnJvbSAnLi4vLi4vbW9kZWxzL2Fic3RyYWN0LWZvcm0tY29udHJvbCc7XHJcbmltcG9ydCB7IEVycm9yTWVzc2FnZSwgTGFiZWxSZXBsYWNlbWVudFRhZyB9IGZyb20gJy4uLy4uL21vZGVscy9lcnJvci1tZXNzYWdlLmludGVyZmFjZSc7XHJcbmltcG9ydCB7IGRlYnVyciB9IGZyb20gJy4uLy4uLy4uL2hlbHBlcnMvZGVidXJyJztcclxuXHJcblxyXG5cclxuLyoqXHJcbiAqIEZvciBUZW1wbGF0ZUZvcm1zLCBwYXNzIGluIGFuIEFkZHJlc3MgYW5kIHJlY2lldmUgYW4gQWRkcmVzc1xyXG4gKiBAZXhhbXBsZVxyXG4gKiAgICAgICAgICAgPGNvbW1vbi1hZGRyZXNzLXZhbGlkYXRvclxyXG4gKiAgICAgICAgICAgICAgIGxhYmVsPSdQaHlzaWNhbCBBZGRyZXNzJ1xyXG4gKiAgICAgICAgICAgICAgIFsobmdNb2RlbCldPVwibXlBZGRyZXNzXCI+XHJcbiAqICAgICAgICAgICA8L2NvbW1vbi1hZGRyZXNzLXZhbGlkYXRvcj5cclxuICpcclxuICogQG5vdGVcclxuICogRm9yIFJlYWN0aXZlRm9ybXMsIHBhc3MgaW4gYSBzdHJpbmcgYW5kIHJlY2lldmUgYSBzdHJpbmcuICBJZiB5b3UgbmVlZCB0aGVcclxuICogQWRkcmVzcyBvYmplY3QgeW91IGNhbiB1c2UgKHNlbGVjdCkgaW4gYWRkaXRpb24uXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqICAgICAgICAgICA8Y29tbW9uLWFkZHJlc3MtdmFsaWRhdG9yXHJcbiAqICAgICAgICAgICAgICBsYWJlbD0nUGh5c2ljYWwgQWRkcmVzcydcclxuICogICAgICAgICAgICAgIGZvcm1Db250cm9sTmFtZT1cImFkZHJlc3NcIlxyXG4gKiAgICAgICAgICAgICAgKHNlbGVjdCk9XCJnZXRBZGRyZXNzT2JqZWN0KCRldmVudClcIj5cclxuICogICAgICAgICAgPC9jb21tb24tYWRkcmVzcy12YWxpZGF0b3I+XHJcbiAqL1xyXG5cclxuZXhwb3J0IGludGVyZmFjZSBBZGRyZXNzUmVzdWx0IHtcclxuICAvKiogU3RyaW5nIGZyb20gdGhlIEFQSSB0aGF0IGluY2x1ZGVzIHN0cmVldCwgY2l0eSwgcHJvdmluY2UsIGFuZCBjb3VudHJ5LiAqL1xyXG4gIEFkZHJlc3NDb21wbGV0ZTogc3RyaW5nO1xyXG4gIEhvdXNlTnVtYmVyOiBzdHJpbmc7XHJcbiAgU3ViQnVpbGRpbmc6IHN0cmluZztcclxuICBTdHJlZXQ6IHN0cmluZztcclxuICBMb2NhbGl0eTogc3RyaW5nO1xyXG4gIERlbGl2ZXJ5QWRkcmVzc0xpbmVzOiBzdHJpbmc7XHJcbiAgQWRkcmVzc0xpbmVzOiBBcnJheTxzdHJpbmc+O1xyXG4gIC8vIFNldCB0byBkZWZhdWx0cyBpbiByZXNwb25zZVxyXG4gIENvdW50cnk6IHN0cmluZztcclxuICBQcm92aW5jZTogc3RyaW5nO1xyXG4gIFBvc3RhbENvZGU6IHN0cmluZztcclxufVxyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgc2VsZWN0b3I6ICdjb21tb24tYWRkcmVzcy12YWxpZGF0b3InLFxyXG4gIHRlbXBsYXRlVXJsOiAnLi9hZGRyZXNzLXZhbGlkYXRvci5jb21wb25lbnQuaHRtbCcsXHJcbiAgc3R5bGVVcmxzOiBbJy4vYWRkcmVzcy12YWxpZGF0b3IuY29tcG9uZW50LnNjc3MnXVxyXG59KVxyXG5leHBvcnQgY2xhc3MgQWRkcmVzc1ZhbGlkYXRvckNvbXBvbmVudCBleHRlbmRzIEFic3RyYWN0Rm9ybUNvbnRyb2wgaW1wbGVtZW50cyBPbkluaXQsIENvbnRyb2xWYWx1ZUFjY2Vzc29yIHtcclxuXHJcbiAgQElucHV0KCkgbGFiZWw6IHN0cmluZyA9ICdBZGRyZXNzIExvb2t1cCc7XHJcbiAgQElucHV0KCkgYWRkcmVzczogc3RyaW5nO1xyXG4gIEBJbnB1dCgpIHNlcnZpY2VVcmw6IHN0cmluZztcclxuICBASW5wdXQoKSBwb3B1bGF0ZUFkZHJlc3NPblNlbGVjdDogYm9vbGVhbiA9IGZhbHNlO1xyXG4gIEBPdXRwdXQoKSBhZGRyZXNzQ2hhbmdlOiBFdmVudEVtaXR0ZXI8c3RyaW5nPiA9IG5ldyBFdmVudEVtaXR0ZXI8c3RyaW5nPigpO1xyXG4gIEBPdXRwdXQoKSBzZWxlY3Q6IEV2ZW50RW1pdHRlcjxBZGRyZXNzPiA9IG5ldyBFdmVudEVtaXR0ZXI8QWRkcmVzcz4oKTtcclxuXHJcbiAgQElucHV0KCkgbWF4bGVuZ3RoOiBzdHJpbmcgPSAnMjU1JztcclxuXHJcbiAgX2RlZmF1bHRFcnJNc2c6IEVycm9yTWVzc2FnZSA9IHtcclxuICAgIHJlcXVpcmVkOiAgTGFiZWxSZXBsYWNlbWVudFRhZyArICcgaXMgcmVxdWlyZWQuJyxcclxuICAgIGludmFsaWRDaGFyOiBMYWJlbFJlcGxhY2VtZW50VGFnICsgJyBtdXN0IGNvbnRhaW4gbGV0dGVycyBhbmQgbnVtYmVycywgYW5kIG1heSBpbmNsdWRlIHNwZWNpYWwgY2hhcmFjdGVycyBzdWNoIGFzIGEgaHlwaGVuLCBwZXJpb2QsIGFwb3N0cm9waGUsIG51bWJlciBzaWduLCBhbXBlcnNhbmQsIGZvcndhcmQgc2xhc2gsIGFuZCBibGFuayBjaGFyYWN0ZXJzLidcclxuICB9O1xyXG4gIC8qKiBUaGUgc3RyaW5nIGluIHRoZSBib3ggdGhlIHVzZXIgaGFzIHR5cGVkICovXHJcbiAgcHVibGljIHNlYXJjaDogc3RyaW5nO1xyXG4gIC8qKiBJcyB0aGUgcmVxdWVzdCBzdGlsbCBpbiBwcm9ncmVzcz8gKi9cclxuICBwdWJsaWMgaXNUeXBlYWhlYWRMb2FkaW5nOiBib29sZWFuID0gZmFsc2U7XHJcbiAgLyoqIGhhcyByZXR1cm5lZCBhbmQgaGFzIG5vIHJlc3VsdHMsIGFuIGVtcHR5IGFycmF5LiAqL1xyXG4gIHB1YmxpYyBoYXNOb1Jlc3VsdHM6IGJvb2xlYW4gPSBmYWxzZTtcclxuICBwdWJsaWMgaGFzRXJyb3I6IGJvb2xlYW4gPSBmYWxzZTtcclxuXHJcbiAgLyoqIFNpbWlsYXIgdG8gdGhpcy5hZGRyZXNzLCBidXQgd2UgY2FuIG51bGwgaXQgd2hlbiB1c2VyIGlzIHNlYXJjaGluZyBmb3IgbmV3IGFkZHJlc3NlcyAqL1xyXG4gIHB1YmxpYyBzZWxlY3RlZEFkZHJlc3M6IGJvb2xlYW4gPSBmYWxzZTtcclxuICAvKiogVGhlIGxpc3Qgb2YgcmVzdWx0cywgZnJvbSBBUEksIHRoYXQgaXMgcGFzc2VkIHRvIHRoZSB0eXBlYWhlYWQgbGlzdCAqL1xyXG4gIHB1YmxpYyB0eXBlYWhlYWRMaXN0JDogT2JzZXJ2YWJsZTxBZGRyZXNzUmVzdWx0W10+ID0gb2YoW10pOyAvLyBSZXN1bHQgZnJvbSBhZGRyZXNzIGxvb2t1cFxyXG4gIC8qKiBUaGUgc3ViamVjdCB0aGF0IHRyaWdnZXJzIG9uIHVzZXIgdGV4dCBpbnB1dCBhbmQgZ2V0cyB0eXBlYWhlYWRMaXN0JCB0byB1cGRhdGUuICAqL1xyXG4gIHByaXZhdGUgc2VhcmNoVGV4dCQgPSBuZXcgU3ViamVjdDxzdHJpbmc+KCk7XHJcblxyXG4gIF9vbkNoYW5nZSA9IChfOiBhbnkpID0+IHt9O1xyXG4gIF9vblRvdWNoZWQgPSAoXz86IGFueSkgPT4ge307XHJcblxyXG4gIGNvbnN0cnVjdG9yKEBPcHRpb25hbCgpIEBTZWxmKCkgcHVibGljIGNvbnRyb2xEaXI6IE5nQ29udHJvbCxcclxuICAgICAgICAgICAgICBwcml2YXRlIGNkOiBDaGFuZ2VEZXRlY3RvclJlZixcclxuICAgICAgICAgICAgICBwcm90ZWN0ZWQgaHR0cDogSHR0cENsaWVudCkge1xyXG4gICAgc3VwZXIoKTtcclxuICAgIGlmICggY29udHJvbERpciApIHtcclxuICAgICAgY29udHJvbERpci52YWx1ZUFjY2Vzc29yID0gdGhpcztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIG5nT25Jbml0KCkge1xyXG4gICAgc3VwZXIubmdPbkluaXQoKTtcclxuICAgIFxyXG4gICAgdGhpcy50eXBlYWhlYWRMaXN0JCA9IHRoaXMuc2VhcmNoVGV4dCQucGlwZShcclxuICAgICAgZGVib3VuY2VUaW1lKDUwMCksXHJcbiAgICAgIGRpc3RpbmN0VW50aWxDaGFuZ2VkKCksXHJcbiAgICAgIC8vIFRyaWdnZXIgdGhlIG5ldHdvcmsgcmVxdWVzdCwgZ2V0IHJlc3VsdHNcclxuICAgICAgc3dpdGNoTWFwKHNlYXJjaFBocmFzZSA9PiB0aGlzLmxvb2t1cChzZWFyY2hQaHJhc2UpKSxcclxuICAgICAgY2F0Y2hFcnJvcihlcnIgPT4gdGhpcy5vbkVycm9yKGVycikpXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgb25FcnJvcihlcnIpOiBPYnNlcnZhYmxlPEFkZHJlc3NSZXN1bHRbXT4ge1xyXG4gICAgdGhpcy5oYXNFcnJvciA9IHRydWU7XHJcbiAgICAvLyBFbXB0eSBhcnJheSBzaW11bGF0ZXMgbm8gcmVzdWx0IHJlc3BvbnNlLCBub3RoaW5nIGZvciB0eXBlYWhlYWQgdG8gaXRlcmF0ZSBvdmVyXHJcbiAgICByZXR1cm4gb2YoW10pO1xyXG4gIH1cclxuXHJcbiAgb25Mb2FkaW5nKHZhbDogYm9vbGVhbik6IHZvaWQge1xyXG4gICAgdGhpcy5pc1R5cGVhaGVhZExvYWRpbmcgPSB2YWw7XHJcbiAgICB0aGlzLmhhc0Vycm9yID0gZmFsc2U7XHJcbiAgfVxyXG5cclxuICAvLyBOb3RlIC0gdGhpcyB3aWxsIGZpcmUgYWZ0ZXIgYW4gb25FcnJvciBhcyB3ZWxsXHJcbiAgb25Ob1Jlc3VsdHModmFsOiBib29sZWFuKTogdm9pZCB7XHJcbiAgICAvLyBJZiB3ZSBoYXZlIHJlc3VsdHMsIHRoZSBlcnJvciBoYXMgcmVzb2x2ZWQgKGUuZy4gbmV0d29yayBoYXMgcmUtY29ubmVjdGVkKVxyXG4gICAgaWYgKHZhbCA9PT0gZmFsc2UpIHtcclxuICAgICAgdGhpcy5oYXNFcnJvciA9IGZhbHNlO1xyXG4gICAgfVxyXG5cclxuICAgIHRoaXMuaGFzTm9SZXN1bHRzID0gdmFsO1xyXG4gIH1cclxuXHJcbiAgb25TZWxlY3QoZXZlbnQ6IFR5cGVhaGVhZE1hdGNoKTogdm9pZCB7XHJcblxyXG4gICAgY29uc3QgZGF0YTogQWRkcmVzc1Jlc3VsdCA9IGV2ZW50Lml0ZW07XHJcblxyXG4gICAgLy8gT3V0cHV0IHN0cmluZyB0byBGb3JtQ29udHJvbC4gSWYgc3RyZWV0IGlzIG1vcmUgdGhhbiB0aGUgbWF4IGxlbmd0aCBzaG9ydGVuXHJcbiAgICBjb25zdCBzdHJpcHBlZCA9IGRhdGEuQWRkcmVzc0xpbmVzID8gdGhpcy5zdHJpcFN0cmluZ1RvTWF4TGVuZ3RoKGRlYnVycihkYXRhLkFkZHJlc3NMaW5lc1swXSkpIDogbnVsbDtcclxuXHJcbiAgICBjb25zdCBhZGRyID0gbmV3IEFkZHJlc3MoKTtcclxuICAgIGFkZHIudW5pdE51bWJlciA9IGRlYnVycihkYXRhLlN1YkJ1aWxkaW5nKTtcclxuICAgIGFkZHIuc3RyZWV0TnVtYmVyID0gZGVidXJyKGRhdGEuSG91c2VOdW1iZXIpO1xyXG4gICAgYWRkci5zdHJlZXROYW1lID0gZGVidXJyKGRhdGEuU3RyZWV0KTtcclxuICAgIGFkZHIuY2l0eSA9IGRlYnVycihkYXRhLkxvY2FsaXR5KTtcclxuICAgIGFkZHIuY291bnRyeSA9IGRhdGEuQ291bnRyeTtcclxuICAgIGFkZHIucHJvdmluY2UgPSBkYXRhLlByb3ZpbmNlO1xyXG4gICAgYWRkci5zdHJlZXQgPSBzdHJpcHBlZDtcclxuICAgIGFkZHIucG9zdGFsID0gZGVidXJyKGRhdGEuUG9zdGFsQ29kZSk7XHJcbiAgICBhZGRyLmFkZHJlc3NMaW5lMSA9IGRhdGEuQWRkcmVzc0xpbmVzICYmIGRhdGEuQWRkcmVzc0xpbmVzWzBdID8gZGVidXJyKGRhdGEuQWRkcmVzc0xpbmVzWzBdKSA6IG51bGw7XHJcbiAgICBhZGRyLmFkZHJlc3NMaW5lMiA9IGRhdGEuQWRkcmVzc0xpbmVzICYmIGRhdGEuQWRkcmVzc0xpbmVzWzFdID8gZGVidXJyKGRhdGEuQWRkcmVzc0xpbmVzWzFdKSA6IG51bGw7XHJcbiAgICBhZGRyLmFkZHJlc3NMaW5lMyA9IGRhdGEuQWRkcmVzc0xpbmVzICYmIGRhdGEuQWRkcmVzc0xpbmVzWzJdID8gZGVidXJyKGRhdGEuQWRkcmVzc0xpbmVzWzJdKSA6IG51bGw7XHJcbiAgICAvLyBTYXZlIGFuZCBlbWl0IEFkZHJlc3MgZm9yIChzZWxlY3QpXHJcbiAgICB0aGlzLnNlbGVjdGVkQWRkcmVzcyA9IHRydWU7XHJcbiAgICB0aGlzLnNlbGVjdC5lbWl0KGFkZHIpO1xyXG5cclxuICAgIC8vIEZvciB0ZW1wbGF0ZSBmb3JtcywgbXVzdCBleHBsaWNpdGx5IHNldCBgc2VhcmNoYCB2YWx1ZSB1cG9uIHNlbGVjdGluZyBhbiBpdGVtLlxyXG4gICAgaWYgKHRoaXMucG9wdWxhdGVBZGRyZXNzT25TZWxlY3QpIHtcclxuICAgICAgdGhpcy5zZWFyY2ggPSBzdHJpcHBlZDtcclxuICAgIH1cclxuICAgIHRoaXMuX29uQ2hhbmdlKHN0cmlwcGVkKTtcclxuICB9XHJcblxyXG4gIG9uS2V5VXAoZXZlbnQ6IEtleWJvYXJkRXZlbnQpOiB2b2lkIHtcclxuICAgIC8vIEZpbHRlciBvdXQgJ2VudGVyJyBhbmQgb3RoZXIgc2ltaWxhciBrZXlib2FyZCBldmVudHMgdGhhdCBjYW4gdHJpZ2dlclxyXG4gICAgLy8gd2hlbiB1c2VyIGlzIHNlbGVjdGluZyBhIHR5cGVhaGVhZCBvcHRpb24gaW5zdGVhZCBvZiBlbnRlcmluZyBuZXcgdGV4dC5cclxuICAgIC8vIFdpdGhvdXQgdGhpcyBmaWx0ZXIsIHdlIGRvIGFub3RoZXIgSFRUUCByZXF1ZXN0ICsgZm9yY2UgZGlzaXBsYXkgdGhlIFVJXHJcbiAgICAvLyBmb3Igbm93IHJlYXNvblxyXG4gICAgaWYgKGV2ZW50LmtleUNvZGUgPT09IDEzIHx8IGV2ZW50LmtleUNvZGUgPT09IDkpIHsgIC8vIGVudGVyICYgdGFiXHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuICAgIC8vIENsZWFyIG91dCBzZWxlY3Rpb25cclxuICAgIHRoaXMuc2VsZWN0ZWRBZGRyZXNzID0gZmFsc2U7XHJcbiAgICB0aGlzLnNlYXJjaFRleHQkLm5leHQodGhpcy5zZWFyY2gpO1xyXG4gIH1cclxuXHJcbiAgb25CbHVyKGV2ZW50KTogdm9pZCB7XHJcbiAgICB0aGlzLl9vblRvdWNoZWQoKTtcclxuICAgIGlmICh0aGlzLnNlYXJjaCkge1xyXG4gICAgICB0aGlzLl9vbkNoYW5nZSh0aGlzLnNlYXJjaCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICB3cml0ZVZhbHVlKCB2YWx1ZTogYW55ICk6IHZvaWQge1xyXG4gICAgaWYgKCB2YWx1ZSAgIT09IHVuZGVmaW5lZCApIHtcclxuICAgICAgdGhpcy5zZWFyY2ggPSB2YWx1ZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHNldFNlYXJjaFZhbHVlKHZhbHVlOiBhbnkpIHtcclxuICAgIHRoaXMuX29uQ2hhbmdlKHZhbHVlKTtcclxuICAgIHRoaXMuX29uVG91Y2hlZCh2YWx1ZSk7XHJcbiAgfVxyXG5cclxuICAvLyBSZWdpc3RlciBjaGFuZ2UgZnVuY3Rpb25cclxuICByZWdpc3Rlck9uQ2hhbmdlKCBmbjogYW55ICk6IHZvaWQge1xyXG4gICAgdGhpcy5fb25DaGFuZ2UgPSBmbjtcclxuICB9XHJcblxyXG4gIC8vIFJlZ2lzdGVyIHRvdWNoZWQgZnVuY3Rpb25cclxuICByZWdpc3Rlck9uVG91Y2hlZCggZm46IGFueSApOiB2b2lkIHtcclxuICAgIHRoaXMuX29uVG91Y2hlZCA9IGZuO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBzdHJpcFN0cmluZ1RvTWF4TGVuZ3RoKHN0cjogc3RyaW5nKSB7XHJcbiAgICBjb25zdCBtYXhsZW5ndGggPSBwYXJzZUludCh0aGlzLm1heGxlbmd0aCwgMTApO1xyXG4gICAgcmV0dXJuIHN0ci5zbGljZSgwLCBtYXhsZW5ndGgpO1xyXG4gIH1cclxuXHJcbiAgbG9va3VwKGFkZHJlc3M6IHN0cmluZyk6IE9ic2VydmFibGU8QWRkcmVzc1Jlc3VsdFtdPiB7XHJcbiAgICBjb25zdCBwYXJhbXMgPSBuZXcgSHR0cFBhcmFtcygpXHJcbiAgICAgICAgICAgICAgICAgICAgLnNldCgnYWRkcmVzcycsIGFkZHJlc3MpO1xyXG5cclxuICAgIHJldHVybiB0aGlzLmh0dHAuZ2V0KHRoaXMuc2VydmljZVVybCwge1xyXG4gICAgICBwYXJhbXM6IHBhcmFtc1xyXG4gICAgfSkucGlwZShtYXAodGhpcy5wcm9jZXNzUmVzcG9uc2UpKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEZvcm1hdHMgdGhlIHJlc3BvbnNlIGZyb20gQUREUkVTU19VUkwsIHRyaW1taW5nIGlycmVsZXZhbnQgZmllbGRzLlxyXG4gICAqXHJcbiAgICogVGhpcyB3b3JrcyBmb3Igb3RoZXIgcmVxdWVzdHMgZm9yIHRoZSBzYW1lIEFQSSB0b28sIGhvd2V2ZXIgaXQgbWF5IGVycm9yXHJcbiAgICogb3V0IG9uIHNvbWUgaXRlbXMgaWYgbWF0Y2hQcmVjaXNpb25Ob3QgaXMgbm90IHNldC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSBvYmogVGhlIHJlc3BvbnNlIGZyb20gQUREUkVTU19VUkxcclxuICAgKi9cclxuICBwcm90ZWN0ZWQgcHJvY2Vzc1Jlc3BvbnNlKG9iaik6IEFkZHJlc3NSZXN1bHRbXSB7XHJcbiAgICByZXR1cm4gb2JqLkFkZHJlc3MubWFwKGZlYXR1cmUgPT4ge1xyXG4gICAgICBjb25zdCBwcm9wcyA9IGZlYXR1cmU7XHJcbiAgICAgIGNvbnN0IExvY2FsaXR5ID0gcHJvcHMuTG9jYWxpdHk7XHJcbiAgICAgIGNvbnN0IEFkZHJlc3NDb21wbGV0ZSA9IHByb3BzLkFkZHJlc3NDb21wbGV0ZTtcclxuICAgICAgY29uc3QgQWRkcmVzc0xpbmVzID0gcHJvcHMuQWRkcmVzc0xpbmVzO1xyXG4gICAgICBjb25zdCBEZWxpdmVyeUFkZHJlc3NMaW5lcyA9IHByb3BzLkRlbGl2ZXJ5QWRkcmVzc0xpbmVzO1xyXG4gICAgICBjb25zdCBQcm92aW5jZSA9IHByb3BzLlByb3ZpbmNlO1xyXG4gICAgICBjb25zdCBDb3VudHJ5ID0gcHJvcHMuQ291bnRyeTtcclxuICAgICAgY29uc3QgUG9zdGFsQ29kZSA9IHByb3BzLlBvc3RhbENvZGU7XHJcbiAgICAgIGNvbnN0IFN1YkJ1aWxkaW5nID0gcHJvcHMuU3ViQnVpbGRpbmc7XHJcbiAgICAgIGNvbnN0IFN0cmVldCA9IHByb3BzLlN0cmVldDtcclxuICAgICAgY29uc3QgSG91c2VOdW1iZXIgPSBwcm9wcy5Ib3VzZU51bWJlcjtcclxuXHJcbiAgICAgIHJldHVybiB7XHJcbiAgICAgICAgQWRkcmVzc0NvbXBsZXRlLFxyXG4gICAgICAgIEFkZHJlc3NMaW5lcyxcclxuICAgICAgICBTdWJCdWlsZGluZyxcclxuICAgICAgICBTdHJlZXQsXHJcbiAgICAgICAgSG91c2VOdW1iZXIsXHJcbiAgICAgICAgTG9jYWxpdHksXHJcbiAgICAgICAgRGVsaXZlcnlBZGRyZXNzTGluZXMsXHJcbiAgICAgICAgUHJvdmluY2UsXHJcbiAgICAgICAgQ291bnRyeSxcclxuICAgICAgICBQb3N0YWxDb2RlXHJcbiAgICAgIH07XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIHByb3RlY3RlZCBoYW5kbGVFcnJvcihlcnJvcjogSHR0cEVycm9yUmVzcG9uc2UpIHtcclxuICAgIGNvbnNvbGUuZXJyb3IoJ0FkZHJlc3NWYWxpZGF0b3IgbmV0d29yayBlcnJvcicsIHsgZXJyb3IgfSk7XHJcbiAgICByZXR1cm4gdGhyb3dFcnJvcignQWRkcmVzc1ZhbGlkYXRvciBlcnJvcicpO1xyXG4gIH1cclxufVxyXG4iXX0=