@synerty/vortexjs
Version:
Custom observable data serialisation and routing based on Angular 2+
440 lines • 57 kB
JavaScript
import { BehaviorSubject, debounceTime, Subject } from "rxjs";
import { filter, skip, takeUntil } from "rxjs/operators";
import { VortexClientABC } from "../VortexClientABC";
import { dateStr } from "../UtilMisc";
export class TupleDataNotValidError extends Error {
constructor(message) {
super(message);
this.name = "TupleDataNotValidError";
}
}
/*
* Each tuple (TupleType) of data that needs to be loaded with the
* `TupleDataLoader` is assigned a delegate of the type `TupleDataLoaderDelegate`
*
* The `TupleDataLoaderDelegate` class provides access to the data that is loaded
* and to be sent to the backend delegate, accessible using the `data$` attribute
*
* The data that is to be loaded and modified is described using a `TupleSelector`
* accessible using the `selector$` attribute. Using `.next()` on the `selector$`
* will trigger a re-load of the data.
*
* If a lock is held by the current user and editing is allowed the `disabled`
* attribute returns `false`. If a lock is not held or someone else holds the lock
* the `disabled` attribute will return `true`. Any updates or attempts to lock
* will fail and throw an exception if `disabled` returns `True`. Data however
* can still be loaded and watched for live updates.
* */
export class TupleDataLoaderDelegate {
lifeCycleEvents;
userUuid$;
// This allows some UI animations to run
ANIMATION_WAIT_TIME_MS = 400;
/** Loading indicators **/
loading$ = new BehaviorSubject(false);
storing$ = new BehaviorSubject(false);
deleting$ = new BehaviorSubject(false);
// Something is happening indicator
ready$ = new BehaviorSubject(true);
data$ = new BehaviorSubject(null);
selector$ = new BehaviorSubject(null);
uuid = VortexClientABC.makeUuid();
dataLocked$ = new BehaviorSubject(false);
lockExpires$ = new BehaviorSubject(null);
/**
* Returns an observable of boolean indicating if data has been deleted.
*
* @return {Observable<boolean>} An observable that emits
* a boolean value indicating if data has been deleted.
*/
get dataDeleted$() {
return this._dataDeletedSubject.asObservable();
}
_dataDeletedSubject = new Subject();
// formGroupDirty is private, because it's only local to this form
// dataDirtyOrWeHaveLock$ is what the UI will use, because...
// The form group will not be dirty have receiving a live update via the
// lock status.
// The fact that we have a lock indicates that the form is dirty.
// However, to respond quicker, we also want to use the dirty flag.
// hence, the name "dirty, or we have lock"
formGroupDirty$ = new BehaviorSubject(false);
dataDirtyOrWeHaveLock$ = new BehaviorSubject(false);
_dataLoader = null;
formGroup$ = new BehaviorSubject(null);
// noinspection JSUnusedLocalSymbols
registeredName = "SET FROM TupleDataLoader.addDelegate";
constructor(lifeCycleEvents, userUuid$) {
this.lifeCycleEvents = lifeCycleEvents;
this.userUuid$ = userUuid$;
// When dirty changes, Set the observable the UIs will use
this.formGroupDirty$
.pipe(takeUntil(this.lifeCycleEvents.onDestroyEvent))
.pipe(filter((dirty) => dirty))
.subscribe((dirty) => {
console.log(`TupleDataLoaderDelegate: ${this.registeredName}` +
" formGroupDirty$, dirty=true," +
" calling setDataDirtyOrWeHaveLock ");
this.setDataDirtyOrWeHaveLock(dirty);
});
// When dirty changes, update the server.
this.formGroupDirty$
.pipe(takeUntil(this.lifeCycleEvents.onDestroyEvent))
.pipe(filter((dirty) => dirty))
.pipe(debounceTime(500))
.subscribe(() => {
console.log(`TupleDataLoaderDelegate: ${this.registeredName}` +
" formGroupDirty$, dirty=true," +
" calling sending lock to server ");
// Send a memory only snapshot of the data to the server
this.lockForEditing().catch((e) => {
console.log(`ERROR: ${dateStr()}: updateEditing: ${e}`);
});
});
this.data$
.pipe(takeUntil(this.lifeCycleEvents.onDestroyEvent))
.subscribe((data) => {
if (data == null) {
console.log(`TupleDataLoaderDelegate: ${this.registeredName}` +
" data$,Observed data$" +
" change," +
` value is null`);
this.formGroup$.next(null);
return;
}
const unsub = this.data$.pipe(skip(1));
const formGroup = data.createFormGroup();
if (formGroup == null) {
return;
}
this.data.updateValidation(formGroup);
if (this.dataDirtyOrWeHaveLock) {
formGroup.markAllAsTouched();
}
// To subscribe for changes in the form, and assign them back to the
// tuple
formGroup.valueChanges
.pipe(debounceTime(200)) // Debounce a LOT of calls
.pipe(takeUntil(this.lifeCycleEvents.onDestroyEvent))
.pipe(takeUntil(unsub))
.subscribe(() => {
this.synchroniseFormGroupToDataTuple(formGroup);
});
console.log(`TupleDataLoaderDelegate: ${this.registeredName}` +
" data$ Emitting new pristine" +
" formGroup$");
this.resetFormGroupDirty();
this.formGroup$.next(formGroup);
});
this.dataDirtyOrWeHaveLock$
.pipe(takeUntil(this.lifeCycleEvents.onDestroyEvent))
.pipe(filter((value) => value))
.subscribe(() => {
if (this.formGroup != null) {
console.log(`TupleDataLoaderDelegate: ${this.registeredName}` +
" dataDirtyOrWeHaveLock$," +
" calling markAllAsTouched");
this.formGroup.markAllAsTouched();
}
});
// Tear down the circular dependency
this.lifeCycleEvents.onDestroyEvent //
.subscribe(() => (this._dataLoader = null));
}
synchroniseFormGroupToDataTuple(formGroup) {
// use formGroup from closure
// This will create circular dependency until takeUntil
const changes = formGroup.getRawValue();
if (changes == null) {
// FormGroup has been destroyed
return;
}
console.log(`TupleDataLoaderDelegate: ${this.registeredName}` +
" data$ Observed" +
" formGroup$.valueChanges, Updating data from" +
" formGroup");
// Update the data tuple from the FormGroup
const differences = this.data.updateFromFormGroup(changes);
// Set the dirty flag
if (differences) {
// Only update the validation if something has
// changed, or we get an infinite loop.
this.data.updateValidation(formGroup);
this.setFormGroupDirty(true);
console.log(`TupleDataLoaderDelegate: ${this.registeredName}` +
" data$ Updating data from" +
" formGroup found changes");
}
else {
console.log(`TupleDataLoaderDelegate: ${this.registeredName}` +
" data$ Updating data from" +
" formGroup, both models match");
}
}
async animationDelay(withAnimationDelay, startDateMs) {
if (!withAnimationDelay) {
return;
}
const timeTakenSoFar = new Date().getMilliseconds() - startDateMs;
await new Promise((resolve) => {
setTimeout(resolve, this.ANIMATION_WAIT_TIME_MS - timeTakenSoFar);
});
}
resetFormGroupDirty() {
this.formGroupDirty$.next(false);
}
setFormGroupDirty(value) {
// Things use dirty updates to know when updates have happened.
// Even if it's true -> true
this.formGroupDirty$.next(this.formGroupDirty || value);
}
get formGroupDirty() {
return this.formGroupDirty$.getValue();
}
resetDataDirtyOrWeHaveLock() {
this.dataDirtyOrWeHaveLock$.next(false);
}
setDataDirtyOrWeHaveLock(value) {
// Things use lock updates to know when updates have happened.
// Even if it's true -> true
this.dataDirtyOrWeHaveLock$.next(this.dataDirtyOrWeHaveLock || value);
if (this.dataDirtyOrWeHaveLock && !this.dataLocked) {
// Preemptively set the lock
// console.log("Preemptively setting the lock for better response");
this.dataLocked$.next(true);
}
}
get dataLocked() {
return this.dataLocked$.getValue();
}
get dataDirtyOrWeHaveLock() {
return this.dataDirtyOrWeHaveLock$.getValue();
}
get selector() {
return this.selector$.getValue();
}
get formGroup() {
return this.formGroup$?.getValue();
}
set ready(value) {
this.ready$.next(value);
}
set storing(value) {
this.storing$.next(value);
}
set loading(value) {
this.loading$.next(value);
}
set deleting(value) {
this.deleting$.next(value);
}
get data() {
return this.data$.getValue();
}
set data(value) {
this.data$.next(value);
}
// noinspection JSUnusedLocalSymbols
setDataLoader(loader) {
this._dataLoader = loader;
}
// noinspection JSUnusedLocalSymbols
processDataUpdate(data) {
console.log("TupleDataLoaderDelegate:" +
" processDataUpdate received data from server");
this.data$.next(data);
}
// noinspection JSUnusedLocalSymbols
processLockStatus(lockStatus) {
this.dataLocked$.next(lockStatus.locked);
if (lockStatus.deleted) {
console.log("TupleDataLoaderDelegate:" +
" processLockStatus received DELETE from server");
this.data$.next(null);
this.selector$.next(null);
this.resetDataDirtyOrWeHaveLock();
this.lockExpires$.next(null);
this._dataDeletedSubject.next();
}
else if (lockStatus.locked) {
console.log("TupleDataLoaderDelegate:" +
" processLockStatus received LOCK from server");
const userUuid = this.userUuid$.getValue();
this.setDataDirtyOrWeHaveLock(lockStatus.lockedByUserUuid == userUuid);
if (lockStatus.liveUpdateDataFromDelegateUuid != this.uuid) {
this.data$.next(lockStatus.liveUpdateDataTuple);
}
this.lockExpires$.next(lockStatus.lockAutoExpireDate);
}
else {
console.log("TupleDataLoaderDelegate:" +
" processLockStatus received UNLOCK from server");
this.resetDataDirtyOrWeHaveLock();
this.lockExpires$.next(null);
}
}
/**
* Validates the form and throws an error if it is
* incomplete or contains errors.
*
* @throws {TupleDataNotValidError} If the form is incomplete
* or contains errors.
*
* @return {void}
*/
validateForm() {
const formGroup = this.formGroup;
if (formGroup == null) {
return;
}
// These updates are debounced, ensure we have the latest data in the
// tuple
this.synchroniseFormGroupToDataTuple(formGroup);
if (formGroup.valid) {
return;
}
Object.values(formGroup.controls).forEach((control) => {
if (control.invalid) {
control.markAsDirty();
control.updateValueAndValidity({
onlySelf: true,
});
}
});
throw new TupleDataNotValidError("Form Incomplete: Please fix the errors and resubmit.");
}
/*
* Store the data by calling the `storeData` method of the backend delegate
* */
async create(withAnimationDelay = true) {
if (this.selector != null) {
throw new Error("CREATE requires selector to be null");
}
if (this._dataLoader == null) {
console.log("store called after destroy");
return;
}
const startDateMs = new Date().getMilliseconds();
try {
this.ready = false;
this.storing = true;
// Ensure the form group is synchronised to the tuple and valid
this.validateForm();
await this._dataLoader.create(this);
}
finally {
await this.animationDelay(withAnimationDelay, startDateMs);
this.storing = false;
this.ready = true;
}
}
/*
* Store the data by calling the `storeData` method of the backend delegate
* */
async store(withAnimationDelay = true) {
if (this.selector == null) {
throw new Error("STORE requires a selector");
}
if (this._dataLoader == null) {
console.log("store called after destroy");
return;
}
const startDateMs = new Date().getMilliseconds();
try {
this.ready = false;
this.storing = true;
// Ensure the form group is synchronised to the tuple and valid
this.validateForm();
await this._dataLoader.store(this);
}
finally {
await this.animationDelay(withAnimationDelay, startDateMs);
this.storing = false;
this.ready = true;
}
}
/*
* Load the last saved data ignoring any live values using `loadData`
* Use this method for when the user hasn't clicked the revert button
* themselves.
* */
async load(withAnimationDelay = true) {
if (this.selector == null) {
throw new Error("LOAD requires a selector");
}
if (this._dataLoader == null) {
console.log("load called after destroy");
return;
}
const startDateMs = new Date().getMilliseconds();
try {
this.ready = false;
await this._dataLoader.load(this);
}
finally {
await this.animationDelay(withAnimationDelay, startDateMs);
this.ready = true;
}
}
/*
* Do the same as load, but also emit the loading$ indicator
* Use this method for calls from revert buttons.
*
* Why is this different?
* We want to see the ready$ indicator showing the data load with
* an animation delay, but it looks weird having the Revert button
* spinning while it's happening.
* */
async revert(withAnimationDelay = true) {
try {
this.loading = true;
await this.load(withAnimationDelay);
}
finally {
this.loading = false;
}
}
/*
* Delete the data referenced by the current `selector$.selector`. Will call
* the `deleteData` method of the backend delegate.
* */
async delete(withAnimationDelay = true) {
if (this.selector == null) {
throw new Error("DELETE requires a selector");
}
if (this._dataLoader == null) {
console.log("delete called after destroy");
return;
}
const startDateMs = new Date().getMilliseconds();
try {
this.ready = false;
this.deleting = true;
await this._dataLoader.delete(this);
this.data$.next(null);
this.selector$.next(null);
}
finally {
await this.animationDelay(withAnimationDelay, startDateMs);
this.deleting = false;
this.ready = true;
}
}
/*
* Notify the backend that the data has been started to be edited and should
* be locked
* */
async lockForEditing() {
if (this.selector == null) {
// If there is no selector, don't lock the data
// this is the state for creating
return;
}
if (this._dataLoader == null) {
console.log("startEditing called after destroy");
return;
}
await this._dataLoader.lock(this);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVHVwbGVEYXRhTG9hZGVyRGVsZWdhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvdm9ydGV4L2RhdGEtbG9hZGVyL1R1cGxlRGF0YUxvYWRlckRlbGVnYXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxlQUFlLEVBQUUsWUFBWSxFQUFjLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUcxRSxPQUFPLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUV6RCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFHckQsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUd0QyxNQUFNLE9BQU8sc0JBQXVCLFNBQVEsS0FBSztJQUM3QyxZQUFZLE9BQWdCO1FBQ3hCLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNmLElBQUksQ0FBQyxJQUFJLEdBQUcsd0JBQXdCLENBQUM7SUFDekMsQ0FBQztDQUNKO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7S0FnQks7QUFDTCxNQUFNLE9BQU8sdUJBQXVCO0lBbURwQjtJQUNBO0lBakRaLHdDQUF3QztJQUMvQixzQkFBc0IsR0FBRyxHQUFHLENBQUM7SUFFdEMsMEJBQTBCO0lBQ2pCLFFBQVEsR0FBRyxJQUFJLGVBQWUsQ0FBVSxLQUFLLENBQUMsQ0FBQztJQUMvQyxRQUFRLEdBQUcsSUFBSSxlQUFlLENBQVUsS0FBSyxDQUFDLENBQUM7SUFDL0MsU0FBUyxHQUFHLElBQUksZUFBZSxDQUFVLEtBQUssQ0FBQyxDQUFDO0lBRXpELG1DQUFtQztJQUMxQixNQUFNLEdBQUcsSUFBSSxlQUFlLENBQVUsSUFBSSxDQUFDLENBQUM7SUFFNUMsS0FBSyxHQUFHLElBQUksZUFBZSxDQUFtQixJQUFJLENBQUMsQ0FBQztJQUNwRCxTQUFTLEdBQUcsSUFBSSxlQUFlLENBQXVCLElB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Q0FBQyxRQUFRLEdBQUcsS0FBSyxDQUFDO1lBQ3RCLElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDO1NBQ3JCO0lBQ0wsQ0FBQztJQUVEOzs7U0FHSztJQUNHLEtBQUssQ0FBQyxjQUFjO1FBQ3hCLElBQUksSUFBSSxDQUFDLFFBQVEsSUFBSSxJQUFJLEVBQUU7WUFDdkIsK0NBQStDO1lBQy9DLGlDQUFpQztZQUNqQyxPQUFPO1NBQ1Y7UUFFRCxJQUFJLElBQUksQ0FBQyxXQUFXLElBQUksSUFBSSxFQUFFO1lBQzFCLE9BQU8sQ0FBQyxHQUFHLENBQUMsbUNBQW1DLENBQUMsQ0FBQztZQUNqRCxPQUFPO1NBQ1Y7UUFFRCxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3RDLENBQUM7Q0FDSiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCwgZGVib3VuY2VUaW1lLCBPYnNlcnZhYmxlLCBTdWJqZWN0IH0gZnJvbSBcInJ4anNcIjtcbmltcG9ydCB7IFR1cGxlU2VsZWN0b3IgfSBmcm9tIFwiLi4vVHVwbGVTZWxlY3RvclwiO1xuaW1wb3J0IHsgVHVwbGVEYXRhTG9hZGVyIH0gZnJvbSBcIi4vVHVwbGVEYXRhTG9hZGVyXCI7XG5pbXBvcnQgeyBmaWx0ZXIsIHNraXAsIHRha2VVbnRpbCB9IGZyb20gXCJyeGpzL29wZXJhdG9yc1wiO1xuaW1wb3J0IHsgTmdPbkRlc3Ryb3lFdmVudEkgfSBmcm9tIFwiLi4vLi4vdXRpbC9OZ0xpZmVDeWNsZUV2ZW50c1wiO1xuaW1wb3J0IHsgVm9ydGV4Q2xpZW50QUJDIH0gZnJvbSBcIi4uL1ZvcnRleENsaWVudEFCQ1wiO1xuaW1wb3J0IHsgRm9ybUdyb3VwIH0gZnJvbSBcIkBhbmd1bGFyL2Zvcm1zXCI7XG5pbXBvcnQgeyBUdXBsZURhdGFMb2FkZXJUdXBsZUFCQyB9IGZyb20gXCIuL1R1cGxlRGF0YUxvYWRlclR1cGxlQUJDXCI7XG5pbXBvcnQgeyBkYXRlU3RyIH0gZnJvbSBcIi4uL1V0aWxNaXNjXCI7XG5pbXBvcnQgeyBfRGF0YUxvY2tTdGF0dXNUdXBsZSB9IGZyb20gXCIuL1R1cGxlRGF0YUxvYWRlclR1cGxlc1wiO1xuXG5leHBvcnQgY2xhc3MgVHVwbGVEYXRhTm90VmFsaWRFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgICBjb25zdHJ1Y3RvcihtZXNzYWdlPzogc3RyaW5nKSB7XG4gICAgICAgIHN1cGVyKG1lc3NhZ2UpO1xuICAgICAgICB0aGlzLm5hbWUgPSBcIlR1cGxlRGF0YU5vdFZhbGlkRXJyb3JcIjtcbiAgICB9XG59XG5cbi8qXG4gKiBFYWNoIHR1cGxlIChUdXBsZVR5cGUpIG9mIGRhdGEgdGhhdCBuZWVkcyB0byBiZSBsb2FkZWQgd2l0aCB0aGVcbiAqIGBUdXBsZURhdGFMb2FkZXJgIGlzIGFzc2lnbmVkIGEgZGVsZWdhdGUgb2YgdGhlIHR5cGUgYFR1cGxlRGF0YUxvYWRlckRlbGVnYXRlYFxuICpcbiAqIFRoZSBgVHVwbGVEYXRhTG9hZGVyRGVsZWdhdGVgIGNsYXNzIHByb3ZpZGVzIGFjY2VzcyB0byB0aGUgZGF0YSB0aGF0IGlzIGxvYWRlZFxuICogYW5kIHRvIGJlIHNlbnQgdG8gdGhlIGJhY2tlbmQgZGVsZWdhdGUsIGFjY2Vzc2libGUgdXNpbmcgdGhlIGBkYXRhJGAgYXR0cmlidXRlXG4gKlxuICogVGhlIGRhdGEgdGhhdCBpcyB0byBiZSBsb2FkZWQgYW5kIG1vZGlmaWVkIGlzIGRlc2NyaWJlZCB1c2luZyBhIGBUdXBsZVNlbGVjdG9yYFxuICogYWNjZXNzaWJsZSB1c2luZyB0aGUgYHNlbGVjdG9yJGAgYXR0cmlidXRlLiBVc2luZyBgLm5leHQoKWAgb24gdGhlIGBzZWxlY3RvciRgXG4gKiB3aWxsIHRyaWdnZXIgYSByZS1sb2FkIG9mIHRoZSBkYXRhLlxuICpcbiAqIElmIGEgbG9jayBpcyBoZWxkIGJ5IHRoZSBjdXJyZW50IHVzZXIgYW5kIGVkaXRpbmcgaXMgYWxsb3dlZCB0aGUgYGRpc2FibGVkYFxuICogYXR0cmlidXRlIHJldHVybnMgYGZhbHNlYC4gSWYgYSBsb2NrIGlzIG5vdCBoZWxkIG9yIHNvbWVvbmUgZWxzZSBob2xkcyB0aGUgbG9ja1xuICogdGhlIGBkaXNhYmxlZGAgYXR0cmlidXRlIHdpbGwgcmV0dXJuIGB0cnVlYC4gQW55IHVwZGF0ZXMgb3IgYXR0ZW1wdHMgdG8gbG9ja1xuICogd2lsbCBmYWlsIGFuZCB0aHJvdyBhbiBleGNlcHRpb24gaWYgYGRpc2FibGVkYCByZXR1cm5zIGBUcnVlYC4gRGF0YSBob3dldmVyXG4gKiBjYW4gc3RpbGwgYmUgbG9hZGVkIGFuZCB3YXRjaGVkIGZvciBsaXZlIHVwZGF0ZXMuXG4gKiAqL1xuZXhwb3J0IGNsYXNzIFR1cGxlRGF0YUxvYWRlckRlbGVnYXRlPFxuICAgIFR1cGxlVHlwZSBleHRlbmRzIFR1cGxlRGF0YUxvYWRlclR1cGxlQUJDLFxuPiB7XG4gICAgLy8gVGhpcyBhbGxvd3Mgc29tZSBVSSBhbmltYXRpb25zIHRvIHJ1blxuICAgIHJlYWRvbmx5IEFOSU1BVElPTl9XQUlUX1RJTUVfTVMgPSA0MDA7XG5cbiAgICAvKiogTG9hZGluZyBpbmRpY2F0b3JzICoqL1xuICAgIHJlYWRvbmx5IGxvYWRpbmckID0gbmV3IEJlaGF2aW9yU3ViamVjdDxib29sZWFuPihmYWxzZSk7XG4gICAgcmVhZG9ubHkgc3RvcmluZyQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+KGZhbHNlKTtcbiAgICByZWFkb25seSBkZWxldGluZyQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+KGZhbHNlKTtcblxuICAgIC8vIFNvbWV0aGluZyBpcyBoYXBwZW5pbmcgaW5kaWNhdG9yXG4gICAgcmVhZG9ubHkgcmVhZHkkID0gbmV3IEJlaGF2aW9yU3ViamVjdDxib29sZWFuPih0cnVlKTtcblxuICAgIHJlYWRvbmx5IGRhdGEkID0gbmV3IEJlaGF2aW9yU3ViamVjdDxUdXBsZVR5cGUgfCBudWxsPihudWxsKTtcbiAgICByZWFkb25seSBzZWxlY3RvciQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PFR1cGxlU2VsZWN0b3IgfCBudWxsPihudWxsKTtcblxuICAgIHB1YmxpYyByZWFkb25seSB1dWlkID0gVm9ydGV4Q2xpZW50QUJDLm1ha2VVdWlkKCk7XG5cbiAgICByZWFkb25seSBkYXRhTG9ja2VkJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8Ym9vbGVhbj4oZmFsc2UpO1xuICAgIHJlYWRvbmx5IGxvY2tFeHBpcmVzJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8RGF0ZT4obnVsbCk7XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIGFuIG9ic2VydmFibGUgb2YgYm9vbGVhbiBpbmRpY2F0aW5nIGlmIGRhdGEgaGFzIGJlZW4gZGVsZXRlZC5cbiAgICAgKlxuICAgICAqIEByZXR1cm4ge09ic2VydmFibGU8Ym9vbGVhbj59IEFuIG9ic2VydmFibGUgdGhhdCBlbWl0c1xuICAgICAqICBhIGJvb2xlYW4gdmFsdWUgaW5kaWNhdGluZyBpZiBkYXRhIGhhcyBiZWVuIGRlbGV0ZWQuXG4gICAgICovXG4gICAgZ2V0IGRhdGFEZWxldGVkJCgpOiBPYnNlcnZhYmxlPHZvaWQ+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2RhdGFEZWxldGVkU3ViamVjdC5hc09ic2VydmFibGUoKTtcbiAgICB9XG4gICAgcHJpdmF0ZSBfZGF0YURlbGV0ZWRTdWJqZWN0ID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcblxuICAgIC8vIGZvcm1Hcm91cERpcnR5IGlzIHByaXZhdGUsIGJlY2F1c2UgaXQncyBvbmx5IGxvY2FsIHRvIHRoaXMgZm9ybVxuICAgIC8vIGRhdGFEaXJ0eU9yV2VIYXZlTG9jayQgaXMgd2hhdCB0aGUgVUkgd2lsbCB1c2UsIGJlY2F1c2UuLi5cbiAgICAvLyBUaGUgZm9ybSBncm91cCB3aWxsIG5vdCBiZSBkaXJ0eSBoYXZlIHJlY2VpdmluZyBhIGxpdmUgdXBkYXRlIHZpYSB0aGVcbiAgICAvLyBsb2NrIHN0YXR1cy5cbiAgICAvLyBUaGUgZmFjdCB0aGF0IHdlIGhhdmUgYSBsb2NrIGluZGljYXRlcyB0aGF0IHRoZSBmb3JtIGlzIGRpcnR5LlxuICAgIC8vIEhvd2V2ZXIsIHRvIHJlc3BvbmQgcXVpY2tlciwgd2UgYWxzbyB3YW50IHRvIHVzZSB0aGUgZGlydHkgZmxhZy5cbiAgICAvLyBoZW5jZSwgdGhlIG5hbWUgXCJkaXJ0eSwgb3Igd2UgaGF2ZSBsb2NrXCJcbiAgICBwcml2YXRlIHJlYWRvbmx5IGZvcm1Hcm91cERpcnR5JCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8Ym9vbGVhbj4oZmFsc2UpO1xuICAgIHJlYWRvbmx5IGRhdGFEaXJ0eU9yV2VIYXZlTG9jayQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+KGZhbHNlKTtcblxuICAgIHByaXZhdGUgX2RhdGFMb2FkZXI6IFR1cGxlRGF0YUxvYWRlciB8IG51bGwgPSBudWxsO1xuXG4gICAgcmVhZG9ubHkgZm9ybUdyb3VwJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8Rm9ybUdyb3VwIHwgbnVsbD4obnVsbCk7XG5cbiAgICAvLyBub2luc3BlY3Rpb24gSlNVbnVzZWRMb2NhbFN5bWJvbHNcbiAgICBwcml2YXRlIHJlZ2lzdGVyZWROYW1lOiBzdHJpbmcgPSBcIlNFVCBGUk9NIFR1cGxlRGF0YUxvYWRlci5hZGREZWxlZ2F0ZVwiO1xuXG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIHByaXZhdGUgbGlmZUN5Y2xlRXZlbnRzOiBOZ09uRGVzdHJveUV2ZW50SSxcbiAgICAgICAgcHJpdmF0ZSB1c2VyVXVpZCQ6IEJlaGF2aW9yU3ViamVjdDxzdHJpbmc+LFxuICAgICkge1xuICAgICAgICAvLyBXaGVuIGRpcnR5IGNoYW5nZXMsIFNldCB0aGUgb2JzZXJ2YWJsZSB0aGUgVUlzIHdpbGwgdXNlXG4gICAgICAgIHRoaXMuZm9ybUdyb3VwRGlydHkkXG4gICAgICAgICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5saWZlQ3ljbGVFdmVudHMub25EZXN0cm95RXZlbnQpKVxuICAgICAgICAgICAgLnBpcGUoZmlsdGVyKChkaXJ0eSkgPT4gZGlydHkpKVxuICAgICAgICAgICAgLnN1YnNjcmliZSgoZGlydHk6IGJvb2xlYW4pID0+IHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICAgICAgYFR1cGxlRGF0YUxvYWRlckRlbGVnYXRlOiAke3RoaXMucmVnaXN0ZXJlZE5hbWV9YCArXG4gICAgICAgICAgICAgICAgICAgICAgICBcIiBmb3JtR3JvdXBEaXJ0eSQsIGRpcnR5PXRydWUsXCIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgXCIgY2FsbGluZyBzZXREYXRhRGlydHlPcldlSGF2ZUxvY2sgXCIsXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB0aGlzLnNldERhdGFEaXJ0eU9yV2VIYXZlTG9jayhkaXJ0eSk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAvLyBXaGVuIGRpcnR5IGNoYW5nZXMsIHVwZGF0ZSB0aGUgc2VydmVyLlxuICAgICAgICB0aGlzLmZvcm1Hcm91cERpcnR5JFxuICAgICAgICAgICAgLnBpcGUodGFrZVVudGlsKHRoaXMubGlmZUN5Y2xlRXZlbnRzLm9uRGVzdHJveUV2ZW50KSlcbiAgICAgICAgICAgIC5waXBlKGZpbHRlcigoZGlydHkpID0+IGRpcnR5KSlcbiAgICAgICAgICAgIC5waXBlKGRlYm91bmNlVGltZSg1MDApKVxuICAgICAgICAgICAgLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgICAgIGBUdXBsZURhdGFMb2FkZXJEZWxlZ2F0ZTogJHt0aGlzLnJlZ2lzdGVyZWROYW1lfWAgK1xuICAgICAgICAgICAgICAgICAgICAgICAgXCIgZm9ybUdyb3VwRGlydHkkLCBkaXJ0eT10cnVlLFwiICtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiIGNhbGxpbmcgc2VuZGluZyBsb2NrIHRvIHNlcnZlciBcIixcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIC8vIFNlbmQgYSBtZW1vcnkgb25seSBzbmFwc2hvdCBvZiB0aGUgZGF0YSB0byB0aGUgc2VydmVyXG4gICAgICAgICAgICAgICAgdGhpcy5sb2NrRm9yRWRpdGluZygpLmNhdGNoKChlKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGBFUlJPUjogJHtkYXRlU3RyKCl9OiB1cGRhdGVFZGl0aW5nOiAke2V9YCk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLmRhdGEkXG4gICAgICAgICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5saWZlQ3ljbGVFdmVudHMub25EZXN0cm95RXZlbnQpKVxuICAgICAgICAgICAgLnN1YnNjcmliZSgoZGF0YTogVHVwbGVEYXRhTG9hZGVyVHVwbGVBQkMpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAoZGF0YSA9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgICAgICAgICAgYFR1cGxlRGF0YUxvYWRlckRlbGVnYXRlOiAke3RoaXMucmVnaXN0ZXJlZE5hbWV9YCArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgXCIgZGF0YSQsT2JzZXJ2ZWQgZGF0YSRcIiArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgXCIgY2hhbmdlLFwiICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBgIHZhbHVlIGlzIG51bGxgLFxuICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmZvcm1Hcm91cCQubmV4dChudWxsKTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGNvbnN0IHVuc3ViID0gdGhpcy5kYXRhJC5waXBlKHNraXAoMSkpO1xuICAgICAgICAgICAgICAgIGNvbnN0IGZvcm1Hcm91cCA9IGRhdGEuY3JlYXRlRm9ybUdyb3VwKCk7XG4gICAgICAgICAgICAgICAgaWYgKGZvcm1Hcm91cCA9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgdGhpcy5kYXRhLnVwZGF0ZVZhbGlkYXRpb24oZm9ybUdyb3VwKTtcblxuICAgICAgICAgICAgICAgIGlmICh0aGlzLmRhdGFEaXJ0eU9yV2VIYXZlTG9jaykge1xuICAgICAgICAgICAgICAgICAgICBmb3JtR3JvdXAubWFya0FsbEFzVG91Y2hlZCgpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIC8vIFRvIHN1YnNjcmliZSBmb3IgY2hhbmdlcyBpbiB0aGUgZm9ybSwgYW5kIGFzc2lnbiB0aGVtIGJhY2sgdG8gdGhlXG4gICAgICAgICAgICAgICAgLy8gdHVwbGVcbiAgICAgICAgICAgICAgICBmb3JtR3JvdXAudmFsdWVDaGFuZ2VzXG4gICAgICAgICAgICAgICAgICAgIC5waXBlKGRlYm91bmNlVGltZSgyMDApKSAvLyBEZWJvdW5jZSBhIExPVCBvZiBjYWxsc1xuICAgICAgICAgICAgICAgICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5saWZlQ3ljbGVFdmVudHMub25EZXN0cm95RXZlbnQpKVxuICAgICAgICAgICAgICAgICAgICAucGlwZSh0YWtlVW50aWwodW5zdWIpKVxuICAgICAgICAgICAgICAgICAgICAuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuc3luY2hyb25pc2VGb3JtR3JvdXBUb0RhdGFUdXBsZShmb3JtR3JvdXApO1xuICAgICAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgICAgICBgVHVwbGVEYXRhTG9hZGVyRGVsZWdhdGU6ICR7dGhpcy5yZWdpc3RlcmVkTmFtZX1gICtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiIGRhdGEkIEVtaXR0aW5nIG5ldyBwcmlzdGluZVwiICtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiIGZvcm1Hcm91cCRcIixcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIHRoaXMucmVzZXRGb3JtR3JvdXBEaXJ0eSgpO1xuICAgICAgICAgICAgICAgIHRoaXMuZm9ybUdyb3VwJC5uZXh0KGZvcm1Hcm91cCk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLmRhdGFEaXJ0eU9yV2VIYXZlTG9jayRcbiAgICAgICAgICAgIC5waXBlKHRha2VVbnRpbCh0aGlzLmxpZmVDeWNsZUV2ZW50cy5vbkRlc3Ryb3lFdmVudCkpXG4gICAgICAgICAgICAucGlwZShmaWx0ZXIoKHZhbHVlKSA9PiB2YWx1ZSkpXG4gICAgICAgICAgICAuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5mb3JtR3JvdXAgIT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICAgICAgICAgIGBUdXBsZURhdGFMb2FkZXJEZWxlZ2F0ZTogJHt0aGlzLnJlZ2lzdGVyZWROYW1lfWAgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwiIGRhdGFEaXJ0eU9yV2VIYXZlTG9jayQsXCIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwiIGNhbGxpbmcgbWFya0FsbEFzVG91Y2hlZFwiLFxuICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmZvcm1Hcm91cC5tYXJrQWxsQXNUb3VjaGVkKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgLy8gVGVhciBkb3duIHRoZSBjaXJjdWxhciBkZXBlbmRlbmN5XG4gICAgICAgIHRoaXMubGlmZUN5Y2xlRXZlbnRzLm9uRGVzdHJveUV2ZW50IC8vXG4gICAgICAgICAgICAuc3Vic2NyaWJlKCgpID0+ICh0aGlzLl9kYXRhTG9hZGVyID0gbnVsbCkpO1xuICAgIH1cblxuICAgIHByaXZhdGUgc3luY2hyb25pc2VGb3JtR3JvdXBUb0RhdGFUdXBsZShmb3JtR3JvdXA6IEZvcm1Hcm91cDxhbnk+KSB7XG4gICAgICAgIC8vIHVzZSBmb3JtR3JvdXAgZnJvbSBjbG9zdXJlXG4gICAgICAgIC8vIFRoaXMgd2lsbCBjcmVhdGUgY2lyY3VsYXIgZGVwZW5kZW5jeSB1bnRpbCB0YWtlVW50aWxcbiAgICAgICAgY29uc3QgY2hhbmdlcyA9IGZvcm1Hcm91cC5nZXRSYXdWYWx1ZSgpO1xuICAgICAgICBpZiAoY2hhbmdlcyA9PSBudWxsKSB7XG4gICAgICAgICAgICAvLyBGb3JtR3JvdXAgaGFzIGJlZW4gZGVzdHJveWVkXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICBgVHVwbGVEYXRhTG9hZGVyRGVsZWdhdGU6ICR7dGhpcy5yZWdpc3RlcmVkTmFtZX1gICtcbiAgICAgICAgICAgICAgICBcIiBkYXRhJCBPYnNlcnZlZFwiICtcbiAgICAgICAgICAgICAgICBcIiBmb3JtR3JvdXAkLnZhbHVlQ2hhbmdlcywgVXBkYXRpbmcgZGF0YSBmcm9tXCIgK1xuICAgICAgICAgICAgICAgIFwiIGZvcm1Hcm91cFwiLFxuICAgICAgICApO1xuXG4gICAgICAgIC8vIFVwZGF0ZSB0aGUgZGF0YSB0dXBsZSBmcm9tIHRoZSBGb3JtR3JvdXBcbiAgICAgICAgY29uc3QgZGlmZmVyZW5jZXMgPSB0aGlzLmRhdGEudXBkYXRlRnJvbUZvcm1Hcm91cChjaGFuZ2VzKTtcblxuICAgICAgICAvLyBTZXQgdGhlIGRpcnR5IGZsYWdcbiAgICAgICAgaWYgKGRpZmZlcmVuY2VzKSB7XG4gICAgICAgICAgICAvLyBPbmx5IHVwZGF0ZSB0aGUgdmFsaWRhdGlvbiBpZiBzb21ldGhpbmcgaGFzXG4gICAgICAgICAgICAvLyBjaGFuZ2VkLCBvciB3ZSBnZXQgYW4gaW5maW5pdGUgbG9vcC5cbiAgICAgICAgICAgIHRoaXMuZGF0YS51cGRhdGVWYWxpZGF0aW9uKGZvcm1Hcm91cCk7XG4gICAgICAgICAgICB0aGlzLnNldEZvcm1Hcm91cERpcnR5KHRydWUpO1xuICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgYFR1cGxlRGF0YUxvYWRlckRlbGVnYXRlOiAke3RoaXMucmVnaXN0ZXJlZE5hbWV9YCArXG4gICAgICAgICAgICAgICAgICAgIFwiIGRhdGEkIFVwZGF0aW5nIGRhdGEgZnJvbVwiICtcbiAgICAgICAgICAgICAgICAgICAgXCIgZm9ybUdyb3VwIGZvdW5kIGNoYW5nZXNcIixcbiAgICAgICAgICAgICk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICBgVHVwbGVEYXRhTG9hZGVyRGVsZWdhdGU6ICR7dGhpcy5yZWdpc3RlcmVkTmFtZX1gICtcbiAgICAgICAgICAgICAgICAgICAgXCIgZGF0YSQgVXBkYXRpbmcgZGF0YSBmcm9tXCIgK1xuICAgICAgICAgICAgICAgICAgICBcIiBmb3JtR3JvdXAsIGJvdGggbW9kZWxzIG1hdGNoXCIsXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBhbmltYXRpb25EZWxheShcbiAgICAgICAgd2l0aEFuaW1hdGlvbkRlbGF5OiBib29sZWFuLFxuICAgICAgICBzdGFydERhdGVNczogbnVtYmVyLFxuICAgICk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAoIXdpdGhBbmltYXRpb25EZWxheSkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgdGltZVRha2VuU29GYXIgPSBuZXcgRGF0ZSgpLmdldE1pbGxpc2Vjb25kcygpIC0gc3RhcnREYXRlTXM7XG4gICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICBzZXRUaW1lb3V0KHJlc29sdmUsIHRoaXMuQU5JTUFUSU9OX1dBSVRfVElNRV9NUyAtIHRpbWVUYWtlblNvRmFyKTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSByZXNldEZvcm1Hcm91cERpcnR5KCk6IHZvaWQge1xuICAgICAgICB0aGlzLmZvcm1Hcm91cERpcnR5JC5uZXh0KGZhbHNlKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHNldEZvcm1Hcm91cERpcnR5KHZhbHVlOiBib29sZWFuKTogdm9pZCB7XG4gICAgICAgIC8vIFRoaW5ncyB1c2UgZGlydHkgdXBkYXRlcyB0byBrbm93IHdoZW4gdXBkYXRlcyBoYXZlIGhhcHBlbmVkLlxuICAgICAgICAvLyBFdmVuIGlmIGl0J3MgdHJ1ZSAtPiB0cnVlXG4gICAgICAgIHRoaXMuZm9ybUdyb3VwRGlydHkkLm5leHQodGhpcy5mb3JtR3JvdXBEaXJ0eSB8fCB2YWx1ZSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBnZXQgZm9ybUdyb3VwRGlydHkoKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiB0aGlzLmZvcm1Hcm91cERpcnR5JC5nZXRWYWx1ZSgpO1xuICAgIH1cblxuICAgIHByaXZhdGUgcmVzZXREYXRhRGlydHlPcldlSGF2ZUxvY2soKTogdm9pZCB7XG4gICAgICAgIHRoaXMuZGF0YURpcnR5T3JXZUhhdmVMb2NrJC5uZXh0KGZhbHNlKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHNldERhdGFEaXJ0eU9yV2VIYXZlTG9jayh2YWx1ZTogYm9vbGVhbik6IHZvaWQge1xuICAgICAgICAvLyBUaGluZ3MgdXNlIGxvY2sgdXBkYXRlcyB0byBrbm93IHdoZW4gdXBkYXRlcyBoYXZlIGhhcHBlbmVkLlxuICAgICAgICAvLyBFdmVuIGlmIGl0J3MgdHJ1ZSAtPiB0cnVlXG4gICAgICAgIHRoaXMuZGF0YURpcnR5T3JXZUhhdmVMb2NrJC5uZXh0KHRoaXMuZGF0YURpcnR5T3JXZUhhdmVMb2NrIHx8IHZhbHVlKTtcblxuICAgICAgICBpZiAodGhpcy5kYXRhRGlydHlPcldlSGF2ZUxvY2sgJiYgIXRoaXMuZGF0YUxvY2tlZCkge1xuICAgICAgICAgICAgLy8gUHJlZW1wdGl2ZWx5IHNldCB0aGUgbG9ja1xuICAgICAgICAgICAgLy8gY29uc29sZS5sb2coXCJQcmVlbXB0aXZlbHkgc2V0dGluZyB0aGUgbG9jayBmb3IgYmV0dGVyIHJlc3BvbnNlXCIpO1xuICAgICAgICAgICAgdGhpcy5kYXRhTG9ja2VkJC5uZXh0KHRydWUpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgZ2V0IGRhdGFMb2NrZWQoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmRhdGFMb2NrZWQkLmdldFZhbHVlKCk7XG4gICAgfVxuXG4gICAgZ2V0IGRhdGFEaXJ0eU9yV2VIYXZlTG9jaygpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZGF0YURpcnR5T3JXZUhhdmVMb2NrJC5nZXRWYWx1ZSgpO1xuICAgIH1cblxuICAgIGdldCBzZWxlY3RvcigpOiBUdXBsZVNlbGVjdG9yIHwgbnVsbCB7XG4gICAgICAgIHJldHVybiB0aGlzLnNlbGVjdG9yJC5nZXRWYWx1ZSgpO1xuICAgIH1cblxuICAgIHByaXZhdGUgZ2V0IGZvcm1Hcm91cCgpOiBGb3JtR3JvdXAgfCBudWxsIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZm9ybUdyb3VwJD8uZ2V0VmFsdWUoKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHNldCByZWFkeSh2YWx1ZTogYm9vbGVhbikge1xuICAgICAgICB0aGlzLnJlYWR5JC5uZXh0KHZhbHVlKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHNldCBzdG9yaW5nKHZhbHVlOiBib29sZWFuKSB7XG4gICAgICAgIHRoaXMuc3RvcmluZyQubmV4dCh2YWx1ZSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBzZXQgbG9hZGluZyh2YWx1ZTogYm9vbGVhbikge1xuICAgICAgICB0aGlzLmxvYWRpbmckLm5leHQodmFsdWUpO1xuICAgIH1cblxuICAgIHByaXZhdGUgc2V0IGRlbGV0aW5nKHZhbHVlOiBib29sZWFuKSB7XG4gICAgICAgIHRoaXMuZGVsZXRpbmckLm5leHQodmFsdWUpO1xuICAgIH1cblxuICAgIGdldCBkYXRhKCk6IFR1cGxlVHlwZSB7XG4gICAgICAgIHJldHVybiB0aGlzLmRhdGEkLmdldFZhbHVlKCk7XG4gICAgfVxuXG4gICAgc2V0IGRhdGEodmFsdWU6IFR1cGxlVHlwZSkge1xuICAgICAgICB0aGlzLmRhdGEkLm5leHQodmFsdWUpO1xuICAgIH1cblxuICAgIC8vIG5vaW5zcGVjdGlvbiBKU1VudXNlZExvY2FsU3ltYm9sc1xuICAgIHByaXZhdGUgc2V0RGF0YUxvYWRlcihsb2FkZXI6IFR1cGxlRGF0YUxvYWRlcikge1xuICAgICAgICB0aGlzLl9kYXRhTG9hZGVyID0gbG9hZGVyO1xuICAgIH1cblxuICAgIC8vIG5vaW5zcGVjdGlvbiBKU1VudXNlZExvY2FsU3ltYm9sc1xuICAgIHByaXZhdGUgcHJvY2Vzc0RhdGFVcGRhdGUoZGF0YTogVHVwbGVUeXBlKTogdm9pZCB7XG4gICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgXCJUdXBsZURhdGFMb2FkZXJEZWxlZ2F0ZTpcIiArXG4gICAgICAgICAgICAgICAgXCIgcHJvY2Vzc0RhdGFVcGRhdGUgcmVjZWl2ZWQgZGF0YSBmcm9tIHNlcnZlclwiLFxuICAgICAgICApO1xuICAgICAgICB0aGlzLmRhdGEkLm5leHQoZGF0YSk7XG4gICAgfVxuXG4gICAgLy8gbm9pbnNwZWN0aW9uIEpTVW51c2VkTG9jYWxTeW1ib2xzXG4gICAgcHJpdmF0ZSBwcm9jZXNzTG9ja1N0YXR1cyhcbiAgICAgICAgbG9ja1N0YXR1czogX0RhdGFMb2NrU3RhdHVzVHVwbGU8VHVwbGVUeXBlPixcbiAgICApOiB2b2lkIHtcbiAgICAgICAgdGhpcy5kYXRhTG9ja2VkJC5uZXh0KGxvY2tTdGF0dXMubG9ja2VkKTtcbiAgICAgICAgaWYgKGxvY2tTdGF0dXMuZGVsZXRlZCkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgXCJUdXBsZURhdGFMb2FkZXJEZWxlZ2F0ZTpcIiArXG4gICAgICAgICAgICAgICAgICAgIFwiIHByb2Nlc3NMb2NrU3RhdHVzIHJlY2VpdmVkIERFTEVURSBmcm9tIHNlcnZlclwiLFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIHRoaXMuZGF0YSQubmV4dChudWxsKTtcbiAgICAgICAgICAgIHRoaXMuc2VsZWN0b3IkLm5leHQobnVsbCk7XG4gICAgICAgICAgICB0aGlzLnJlc2V0RGF0YURpcnR5T3JXZUhhdmVMb2NrKCk7XG4gICAgICAgICAgICB0aGlzLmxvY2tFeHBpcmVzJC5uZXh0KG51bGwpO1xuICAgICAgICAgICAgdGhpcy5fZGF0YURlbGV0ZWRTdWJqZWN0Lm5leHQoKTtcbiAgICAgICAgfSBlbHNlIGlmIChsb2NrU3RhdHVzLmxvY2tlZCkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgXCJUdXBsZURhdGFMb2FkZXJEZWxlZ2F0ZTpcIiArXG4gICAgICAgICAgICAgICAgICAgIFwiIHByb2Nlc3NMb2NrU3RhdHVzIHJlY2VpdmVkIExPQ0sgZnJvbSBzZXJ2ZXJcIixcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBjb25zdCB1c2VyVXVpZCA9IHRoaXMudXNlclV1aWQkLmdldFZhbHVlKCk7XG4gICAgICAgICAgICB0aGlzLnNldERhdGFEaXJ0eU9yV2VIYXZlTG9jayhcbiAgICAgICAgICAgICAgICBsb2NrU3RhdHVzLmxvY2tlZEJ5VXNlclV1aWQgPT0gdXNlclV1aWQsXG4gICAgICAgICAgICApO1xuXG4gICAgICAgICAgICBpZiAobG9ja1N0YXR1cy5saXZlVXBkYXRlRGF0YUZyb21EZWxlZ2F0ZVV1aWQgIT0gdGhpcy51dWlkKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5kYXRhJC5uZXh0KGxvY2tTdGF0dXMubGl2ZVVwZGF0ZURhdGFUdXBsZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLmxvY2tFeHBpcmVzJC5uZXh0KGxvY2tTdGF0dXMubG9ja0F1dG9FeHBpcmVEYXRlKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgIFwiVHVwbGVEYXRhTG9hZGVyRGVsZWdhdGU6XCIgK1xuICAgICAgICAgICAgICAgICAgICBcIiBwcm9jZXNzTG9ja1N0YXR1cyByZWNlaXZlZCBVTkxPQ0sgZnJvbSBzZXJ2ZXJcIixcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICB0aGlzLnJlc2V0RGF0YURpcnR5T3JXZUhhdmVMb2NrKCk7XG4gICAgICAgICAgICB0aGlzLmxvY2tFeHBpcmVzJC5uZXh0KG51bGwpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVmFsaWRhdGVzIHRoZSBmb3JtIGFuZCB0aHJvd3MgYW4gZXJyb3IgaWYgaXQgaXNcbiAgICAgKiBpbmNvbXBsZXRlIG9yIGNvbnRhaW5zIGVycm9ycy5cbiAgICAgKlxuICAgICAqIEB0aHJvd3Mge1R1cGxlRGF0YU5vdFZhbGlkRXJyb3J9IElmIHRoZSBmb3JtIGlzIGluY29tcGxldGVcbiAgICAgKiBvciBjb250YWlucyBlcnJvcnMuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHt2b2lkfVxuICAgICAqL1xuICAgIHZhbGlkYXRlRm9ybSgpOiB2b2lkIHtcbiAgICAgICAgY29uc3QgZm9ybUdyb3VwID0gdGhpcy5mb3JtR3JvdXA7XG4gICAgICAgIGlmIChmb3JtR3JvdXAgPT0gbnVsbCkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gVGhlc2UgdXBkYXRlcyBhcmUgZGVib3VuY2VkLCBlbnN1cmUgd2UgaGF2ZSB0aGUgbGF0ZXN0