UNPKG

@synerty/vortexjs

Version:

Custom observable data serialisation and routing based on Angular 2+

440 lines 57 kB
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVHVwbGVEYXRhTG9hZGVyRGVsZWdhdGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvdm9ydGV4L2RhdGEtbG9hZGVyL1R1cGxlRGF0YUxvYWRlckRlbGVnYXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxlQUFlLEVBQUUsWUFBWSxFQUFjLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUcxRSxPQUFPLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUV6RCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFHckQsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUd0QyxNQUFNLE9BQU8sc0JBQXVCLFNBQVEsS0FBSztJQUM3QyxZQUFZLE9BQWdCO1FBQ3hCLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNmLElBQUksQ0FBQyxJQUFJLEdBQUcsd0JBQXdCLENBQUM7SUFDekMsQ0FBQztDQUNKO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7S0FnQks7QUFDTCxNQUFNLE9BQU8sdUJBQXVCO0lBbURwQjtJQUNBO0lBakRaLHdDQUF3QztJQUMvQixzQkFBc0IsR0FBRyxHQUFHLENBQUM7SUFFdEMsMEJBQTBCO0lBQ2pCLFFBQVEsR0FBRyxJQUFJLGVBQWUsQ0FBVSxLQUFLLENBQUMsQ0FBQztJQUMvQyxRQUFRLEdBQUcsSUFBSSxlQUFlLENBQVUsS0FBSyxDQUFDLENBQUM7SUFDL0MsU0FBUyxHQUFHLElBQUksZUFBZSxDQUFVLEtBQUssQ0FBQyxDQUFDO0lBRXpELG1DQUFtQztJQUMxQixNQUFNLEdBQUcsSUFBSSxlQUFlLENBQVUsSUFBSSxDQUFDLENBQUM7SUFFNUMsS0FBSyxHQUFHLElBQUksZUFBZSxDQUFtQixJQUFJLENBQUMsQ0FBQztJQUNwRCxTQUFTLEdBQUcsSUFBSSxlQUFlLENBQXVCLElBQUksQ0FBQyxDQUFDO0lBRXJELElBQUksR0FBRyxlQUFlLENBQUMsUUFBUSxFQUFFLENBQUM7SUFFekMsV0FBVyxHQUFHLElBQUksZUFBZSxDQUFVLEtBQUssQ0FBQyxDQUFDO0lBQ2xELFlBQVksR0FBRyxJQUFJLGVBQWUsQ0FBTyxJQUFJLENBQUMsQ0FBQztJQUV4RDs7Ozs7T0FLRztJQUNILElBQUksWUFBWTtRQUNaLE9BQU8sSUFBSSxDQUFDLG1CQUFtQixDQUFDLFlBQVksRUFBRSxDQUFDO0lBQ25ELENBQUM7SUFDTyxtQkFBbUIsR0FBRyxJQUFJLE9BQU8sRUFBUSxDQUFDO0lBRWxELGtFQUFrRTtJQUNsRSw2REFBNkQ7SUFDN0Qsd0VBQXdFO0lBQ3hFLGVBQWU7SUFDZixpRUFBaUU7SUFDakUsbUVBQW1FO0lBQ25FLDJDQUEyQztJQUMxQixlQUFlLEdBQUcsSUFBSSxlQUFlLENBQVUsS0FBSyxDQUFDLENBQUM7SUFDOUQsc0JBQXNCLEdBQUcsSUFBSSxlQUFlLENBQVUsS0FBSyxDQUFDLENBQUM7SUFFOUQsV0FBVyxHQUEyQixJQUFJLENBQUM7SUFFMUMsVUFBVSxHQUFHLElBQUksZUFBZSxDQUFtQixJQUFJLENBQUMsQ0FBQztJQUVsRSxvQ0FBb0M7SUFDNUIsY0FBYyxHQUFXLHNDQUFzQyxDQUFDO0lBRXhFLFlBQ1ksZUFBa0MsRUFDbEMsU0FBa0M7UUFEbEMsb0JBQWUsR0FBZixlQUFlLENBQW1CO1FBQ2xDLGNBQVMsR0FBVCxTQUFTLENBQXlCO1FBRTFDLDBEQUEwRDtRQUMxRCxJQUFJLENBQUMsZUFBZTthQUNmLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxjQUFjLENBQUMsQ0FBQzthQUNwRCxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQzthQUM5QixTQUFTLENBQUMsQ0FBQyxLQUFjLEVBQUUsRUFBRTtZQUMxQixPQUFPLENBQUMsR0FBRyxDQUNQLDRCQUE0QixJQUFJLENBQUMsY0FBYyxFQUFFO2dCQUM3QywrQkFBK0I7Z0JBQy9CLG9DQUFvQyxDQUMzQyxDQUFDO1lBQ0YsSUFBSSxDQUFDLHdCQUF3QixDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3pDLENBQUMsQ0FBQyxDQUFDO1FBRVAseUNBQXlDO1FBQ3pDLElBQUksQ0FBQyxlQUFlO2FBQ2YsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLGNBQWMsQ0FBQyxDQUFDO2FBQ3BELElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDO2FBQzlCLElBQUksQ0FBQyxZQUFZLENBQUMsR0FBRyxDQUFDLENBQUM7YUFDdkIsU0FBUyxDQUFDLEdBQUcsRUFBRTtZQUNaLE9BQU8sQ0FBQyxHQUFHLENBQ1AsNEJBQTRCLElBQUksQ0FBQyxjQUFjLEVBQUU7Z0JBQzdDLCtCQUErQjtnQkFDL0Isa0NBQWtDLENBQ3pDLENBQUM7WUFDRix3REFBd0Q7WUFDeEQsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFO2dCQUM5QixPQUFPLENBQUMsR0FBRyxDQUFDLFVBQVUsT0FBTyxFQUFFLG9CQUFvQixDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQzVELENBQUMsQ0FBQyxDQUFDO1FBQ1AsQ0FBQyxDQUFDLENBQUM7UUFFUCxJQUFJLENBQUMsS0FBSzthQUNMLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxjQUFjLENBQUMsQ0FBQzthQUNwRCxTQUFTLENBQUMsQ0FBQyxJQUE2QixFQUFFLEVBQUU7WUFDekMsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFO2dCQUNkLE9BQU8sQ0FBQyxHQUFHLENBQ1AsNEJBQTRCLElBQUksQ0FBQyxjQUFjLEVBQUU7b0JBQzdDLHVCQUF1QjtvQkFDdkIsVUFBVTtvQkFDVixnQkFBZ0IsQ0FDdkIsQ0FBQztnQkFDRixJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDM0IsT0FBTzthQUNWO1lBRUQsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDdkMsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1lBQ3pDLElBQUksU0FBUyxJQUFJLElBQUksRUFBRTtnQkFDbkIsT0FBTzthQUNWO1lBQ0QsSUFBSSxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUV0QyxJQUFJLElBQUksQ0FBQyxxQkFBcUIsRUFBRTtnQkFDNUIsU0FBUyxDQUFDLGdCQUFnQixFQUFFLENBQUM7YUFDaEM7WUFFRCxvRUFBb0U7WUFDcEUsUUFBUTtZQUNSLFNBQVMsQ0FBQyxZQUFZO2lCQUNqQixJQUFJLENBQUMsWUFBWSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsMEJBQTBCO2lCQUNsRCxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsY0FBYyxDQUFDLENBQUM7aUJBQ3BELElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7aUJBQ3RCLFNBQVMsQ0FBQyxHQUFHLEVBQUU7Z0JBQ1osSUFBSSxDQUFDLCtCQUErQixDQUFDLFNBQVMsQ0FBQyxDQUFDO1lBQ3BELENBQUMsQ0FBQyxDQUFDO1lBRVAsT0FBTyxDQUFDLEdBQUcsQ0FDUCw0QkFBNEIsSUFBSSxDQUFDLGNBQWMsRUFBRTtnQkFDN0MsOEJBQThCO2dCQUM5QixhQUFhLENBQ3BCLENBQUM7WUFDRixJQUFJLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztZQUMzQixJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUNwQyxDQUFDLENBQUMsQ0FBQztRQUVQLElBQUksQ0FBQyxzQkFBc0I7YUFDdEIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLGNBQWMsQ0FBQyxDQUFDO2FBQ3BELElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDO2FBQzlCLFNBQVMsQ0FBQyxHQUFHLEVBQUU7WUFDWixJQUFJLElBQUksQ0FBQyxTQUFTLElBQUksSUFBSSxFQUFFO2dCQUN4QixPQUFPLENBQUMsR0FBRyxDQUNQLDRCQUE0QixJQUFJLENBQUMsY0FBYyxFQUFFO29CQUM3QywwQkFBMEI7b0JBQzFCLDJCQUEyQixDQUNsQyxDQUFDO2dCQUNGLElBQUksQ0FBQyxTQUFTLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQzthQUNyQztRQUNMLENBQUMsQ0FBQyxDQUFDO1FBRVAsb0NBQW9DO1FBQ3BDLElBQUksQ0FBQyxlQUFlLENBQUMsY0FBYyxDQUFDLEVBQUU7YUFDakMsU0FBUyxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBQ3BELENBQUM7SUFFTywrQkFBK0IsQ0FBQyxTQUF5QjtRQUM3RCw2QkFBNkI7UUFDN0IsdURBQXVEO1FBQ3ZELE1BQU0sT0FBTyxHQUFHLFNBQVMsQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUN4QyxJQUFJLE9BQU8sSUFBSSxJQUFJLEVBQUU7WUFDakIsK0JBQStCO1lBQy9CLE9BQU87U0FDVjtRQUNELE9BQU8sQ0FBQyxHQUFHLENBQ1AsNEJBQTRCLElBQUksQ0FBQyxjQUFjLEVBQUU7WUFDN0MsaUJBQWlCO1lBQ2pCLDhDQUE4QztZQUM5QyxZQUFZLENBQ25CLENBQUM7UUFFRiwyQ0FBMkM7UUFDM0MsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUUzRCxxQkFBcUI7UUFDckIsSUFBSSxXQUFXLEVBQUU7WUFDYiw4Q0FBOEM7WUFDOUMsdUNBQXVDO1lBQ3ZDLElBQUksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDdEMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxDQUFDO1lBQzdCLE9BQU8sQ0FBQyxHQUFHLENBQ1AsNEJBQTRCLElBQUksQ0FBQyxjQUFjLEVBQUU7Z0JBQzdDLDJCQUEyQjtnQkFDM0IsMEJBQTBCLENBQ2pDLENBQUM7U0FDTDthQUFNO1lBQ0gsT0FBTyxDQUFDLEdBQUcsQ0FDUCw0QkFBNEIsSUFBSSxDQUFDLGNBQWMsRUFBRTtnQkFDN0MsMkJBQTJCO2dCQUMzQiwrQkFBK0IsQ0FDdEMsQ0FBQztTQUNMO0lBQ0wsQ0FBQztJQUVPLEtBQUssQ0FBQyxjQUFjLENBQ3hCLGtCQUEyQixFQUMzQixXQUFtQjtRQUVuQixJQUFJLENBQUMsa0JBQWtCLEVBQUU7WUFDckIsT0FBTztTQUNWO1FBRUQsTUFBTSxjQUFjLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQyxlQUFlLEVBQUUsR0FBRyxXQUFXLENBQUM7UUFDbEUsTUFBTSxJQUFJLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1lBQzFCLFVBQVUsQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLHNCQUFzQixHQUFHLGNBQWMsQ0FBQyxDQUFDO1FBQ3RFLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUVPLG1CQUFtQjtRQUN2QixJQUFJLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNyQyxDQUFDO0lBRU8saUJBQWlCLENBQUMsS0FBYztRQUNwQywrREFBK0Q7UUFDL0QsNEJBQTRCO1FBQzVCLElBQUksQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxjQUFjLElBQUksS0FBSyxDQUFDLENBQUM7SUFDNUQsQ0FBQztJQUVELElBQVksY0FBYztRQUN0QixPQUFPLElBQUksQ0FBQyxlQUFlLENBQUMsUUFBUSxFQUFFLENBQUM7SUFDM0MsQ0FBQztJQUVPLDBCQUEwQjtRQUM5QixJQUFJLENBQUMsc0JBQXNCLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzVDLENBQUM7SUFFTyx3QkFBd0IsQ0FBQyxLQUFjO1FBQzNDLDhEQUE4RDtRQUM5RCw0QkFBNEI7UUFDNUIsSUFBSSxDQUFDLHNCQUFzQixDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMscUJBQXFCLElBQUksS0FBSyxDQUFDLENBQUM7UUFFdEUsSUFBSSxJQUFJLENBQUMscUJBQXFCLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFO1lBQ2hELDRCQUE0QjtZQUM1QixvRUFBb0U7WUFDcEUsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7U0FDL0I7SUFDTCxDQUFDO0lBRUQsSUFBSSxVQUFVO1FBQ1YsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQ3ZDLENBQUM7SUFFRCxJQUFJLHFCQUFxQjtRQUNyQixPQUFPLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxRQUFRLEVBQUUsQ0FBQztJQUNsRCxDQUFDO0lBRUQsSUFBSSxRQUFRO1FBQ1IsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQ3JDLENBQUM7SUFFRCxJQUFZLFNBQVM7UUFDakIsT0FBTyxJQUFJLENBQUMsVUFBVSxFQUFFLFFBQVEsRUFBRSxDQUFDO0lBQ3ZDLENBQUM7SUFFRCxJQUFZLEtBQUssQ0FBQyxLQUFjO1FBQzVCLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzVCLENBQUM7SUFFRCxJQUFZLE9BQU8sQ0FBQyxLQUFjO1FBQzlCLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzlCLENBQUM7SUFFRCxJQUFZLE9BQU8sQ0FBQyxLQUFjO1FBQzlCLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzlCLENBQUM7SUFFRCxJQUFZLFFBQVEsQ0FBQyxLQUFjO1FBQy9CLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQy9CLENBQUM7SUFFRCxJQUFJLElBQUk7UUFDSixPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsUUFBUSxFQUFFLENBQUM7SUFDakMsQ0FBQztJQUVELElBQUksSUFBSSxDQUFDLEtBQWdCO1FBQ3JCLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzNCLENBQUM7SUFFRCxvQ0FBb0M7SUFDNUIsYUFBYSxDQUFDLE1BQXVCO1FBQ3pDLElBQUksQ0FBQyxXQUFXLEdBQUcsTUFBTSxDQUFDO0lBQzlCLENBQUM7SUFFRCxvQ0FBb0M7SUFDNUIsaUJBQWlCLENBQUMsSUFBZTtRQUNyQyxPQUFPLENBQUMsR0FBRyxDQUNQLDBCQUEwQjtZQUN0Qiw4Q0FBOEMsQ0FDckQsQ0FBQztRQUNGLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzFCLENBQUM7SUFFRCxvQ0FBb0M7SUFDNUIsaUJBQWlCLENBQ3JCLFVBQTJDO1FBRTNDLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUN6QyxJQUFJLFVBQVUsQ0FBQyxPQUFPLEVBQUU7WUFDcEIsT0FBTyxDQUFDLEdBQUcsQ0FDUCwwQkFBMEI7Z0JBQ3RCLGdEQUFnRCxDQUN2RCxDQUFDO1lBQ0YsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDdEIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDMUIsSUFBSSxDQUFDLDBCQUEwQixFQUFFLENBQUM7WUFDbEMsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDN0IsSUFBSSxDQUFDLG1CQUFtQixDQUFDLElBQUksRUFBRSxDQUFDO1NBQ25DO2FBQU0sSUFBSSxVQUFVLENBQUMsTUFBTSxFQUFFO1lBQzFCLE9BQU8sQ0FBQyxHQUFHLENBQ1AsMEJBQTBCO2dCQUN0Qiw4Q0FBOEMsQ0FDckQsQ0FBQztZQUNGLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDM0MsSUFBSSxDQUFDLHdCQUF3QixDQUN6QixVQUFVLENBQUMsZ0JBQWdCLElBQUksUUFBUSxDQUMxQyxDQUFDO1lBRUYsSUFBSSxVQUFVLENBQUMsOEJBQThCLElBQUksSUFBSSxDQUFDLElBQUksRUFBRTtnQkFDeEQsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLG1CQUFtQixDQUFDLENBQUM7YUFDbkQ7WUFDRCxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsa0JBQWtCLENBQUMsQ0FBQztTQUN6RDthQUFNO1lBQ0gsT0FBTyxDQUFDLEdBQUcsQ0FDUCwwQkFBMEI7Z0JBQ3RCLGdEQUFnRCxDQUN2RCxDQUFDO1lBQ0YsSUFBSSxDQUFDLDBCQUEwQixFQUFFLENBQUM7WUFDbEMsSUFBSSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7U0FDaEM7SUFDTCxDQUFDO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSCxZQUFZO1FBQ1IsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQztRQUNqQyxJQUFJLFNBQVMsSUFBSSxJQUFJLEVBQUU7WUFDbkIsT0FBTztTQUNWO1FBRUQscUVBQXFFO1FBQ3JFLFFBQVE7UUFDUixJQUFJLENBQUMsK0JBQStCLENBQUMsU0FBUyxDQUFDLENBQUM7UUFFaEQsSUFBSSxTQUFTLENBQUMsS0FBSyxFQUFFO1lBQ2pCLE9BQU87U0FDVjtRQUVELE1BQU0sQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1lBQ2xELElBQUksT0FBTyxDQUFDLE9BQU8sRUFBRTtnQkFDakIsT0FBTyxDQUFDLFdBQVcsRUFBRSxDQUFDO2dCQUN0QixPQUFPLENBQUMsc0JBQXNCLENBQUM7b0JBQzNCLFFBQVEsRUFBRSxJQUFJO2lCQUNqQixDQUFDLENBQUM7YUFDTjtRQUNMLENBQUMsQ0FBQyxDQUFDO1FBQ0gsTUFBTSxJQUFJLHNCQUFzQixDQUM1QixzREFBc0QsQ0FDekQsQ0FBQztJQUNOLENBQUM7SUFFRDs7U0FFSztJQUNMLEtBQUssQ0FBQyxNQUFNLENBQUMsa0JBQWtCLEdBQUcsSUFBSTtRQUNsQyxJQUFJLElBQUksQ0FBQyxRQUFRLElBQUksSUFBSSxFQUFFO1lBQ3ZCLE1BQU0sSUFBSSxLQUFLLENBQUMscUNBQXFDLENBQUMsQ0FBQztTQUMxRDtRQUVELElBQUksSUFBSSxDQUFDLFdBQVcsSUFBSSxJQUFJLEVBQUU7WUFDMUIsT0FBTyxDQUFDLEdBQUcsQ0FBQyw0QkFBNEIsQ0FBQyxDQUFDO1lBQzFDLE9BQU87U0FDVjtRQUVELE1BQU0sV0FBVyxHQUFHLElBQUksSUFBSSxFQUFFLENBQUMsZUFBZSxFQUFFLENBQUM7UUFFakQsSUFBSTtZQUNBLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO1lBQ25CLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDO1lBRXBCLCtEQUErRDtZQUMvRCxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7WUFFcEIsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUN2QztnQkFBUztZQUNOLE1BQU0sSUFBSSxDQUFDLGNBQWMsQ0FBQyxrQkFBa0IsRUFBRSxXQUFXLENBQUMsQ0FBQztZQUMzRCxJQUFJLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztZQUNyQixJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQztTQUNyQjtJQUNMLENBQUM7SUFFRDs7U0FFSztJQUNMLEtBQUssQ0FBQyxLQUFLLENBQUMsa0JBQWtCLEdBQUcsSUFBSTtRQUNqQyxJQUFJLElBQUksQ0FBQyxRQUFRLElBQUksSUFBSSxFQUFFO1lBQ3ZCLE1BQU0sSUFBSSxLQUFLLENBQUMsMkJBQTJCLENBQUMsQ0FBQztTQUNoRDtRQUVELElBQUksSUFBSSxDQUFDLFdBQVcsSUFBSSxJQUFJLEVBQUU7WUFDMUIsT0FBTyxDQUFDLEdBQUcsQ0FBQyw0QkFBNEIsQ0FBQyxDQUFDO1lBQzFDLE9BQU87U0FDVjtRQUVELE1BQU0sV0FBVyxHQUFHLElBQUksSUFBSSxFQUFFLENBQUMsZUFBZSxFQUFFLENBQUM7UUFFakQsSUFBSTtZQUNBLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO1lBQ25CLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDO1lBRXBCLCtEQUErRDtZQUMvRCxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7WUFFcEIsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUN0QztnQkFBUztZQUNOLE1BQU0sSUFBSSxDQUFDLGNBQWMsQ0FBQyxrQkFBa0IsRUFBRSxXQUFXLENBQUMsQ0FBQztZQUMzRCxJQUFJLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztZQUNyQixJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQztTQUNyQjtJQUNMLENBQUM7SUFFRDs7OztTQUlLO0lBQ0wsS0FBSyxDQUFDLElBQUksQ0FBQyxrQkFBa0IsR0FBRyxJQUFJO1FBQ2hDLElBQUksSUFBSSxDQUFDLFFBQVEsSUFBSSxJQUFJLEVBQUU7WUFDdkIsTUFBTSxJQUFJLEtBQUssQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO1NBQy9DO1FBRUQsSUFBSSxJQUFJLENBQUMsV0FBVyxJQUFJLElBQUksRUFBRTtZQUMxQixPQUFPLENBQUMsR0FBRyxDQUFDLDJCQUEyQixDQUFDLENBQUM7WUFDekMsT0FBTztTQUNWO1FBRUQsTUFBTSxXQUFXLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQyxlQUFlLEVBQUUsQ0FBQztRQUNqRCxJQUFJO1lBQ0EsSUFBSSxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUM7WUFDbkIsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztTQUNyQztnQkFBUztZQUNOLE1BQU0sSUFBSSxDQUFDLGNBQWMsQ0FBQyxrQkFBa0IsRUFBRSxXQUFXLENBQUMsQ0FBQztZQUMzRCxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQztTQUNyQjtJQUNMLENBQUM7SUFFRDs7Ozs7Ozs7U0FRSztJQUNMLEtBQUssQ0FBQyxNQUFNLENBQUMsa0JBQWtCLEdBQUcsSUFBSTtRQUNsQyxJQUFJO1lBQ0EsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUM7WUFDcEIsTUFBTSxJQUFJLENBQUMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLENBQUM7U0FDdkM7Z0JBQVM7WUFDTixJQUFJLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQztTQUN4QjtJQUNMLENBQUM7SUFFRDs7O1NBR0s7SUFDTCxLQUFLLENBQUMsTUFBTSxDQUFDLGtCQUFrQixHQUFHLElBQUk7UUFDbEMsSUFBSSxJQUFJLENBQUMsUUFBUSxJQUFJLElBQUksRUFBRTtZQUN2QixNQUFNLElBQUksS0FBSyxDQUFDLDRCQUE0QixDQUFDLENBQUM7U0FDakQ7UUFFRCxJQUFJLElBQUksQ0FBQyxXQUFXLElBQUksSUFBSSxFQUFFO1lBQzFCLE9BQU8sQ0FBQyxHQUFHLENBQUMsNkJBQTZCLENBQUMsQ0FBQztZQUMzQyxPQUFPO1NBQ1Y7UUFFRCxNQUFNLFdBQVcsR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDLGVBQWUsRUFBRSxDQUFDO1FBQ2pELElBQUk7WUFDQSxJQUFJLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztZQUNuQixJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQztZQUNyQixNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3BDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3RCLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1NBQzdCO2dCQUFTO1lBQ04sTUFBTSxJQUFJLENBQUMsY0FBYyxDQUFDLGtCQUFrQixFQUFFLFdBQVcsQ0FBQyxDQUFDO1lBQzNELElBQUksQ0FBQyxRQUFRLEdBQUcsS0FBSyxDQUFDO1lBQ3RCLElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDO1NBQ3JCO0lBQ0wsQ0FBQztJQUVEOzs7U0FHSztJQUNHLEtBQUssQ0FBQyxjQUFjO1FBQ3hCLElBQUksSUFBSSxDQUFDLFFBQVEsSUFBSSxJQUFJLEVBQUU7WUFDdkIsK0NBQStDO1lBQy9DLGlDQUFpQztZQUNqQyxPQUFPO1NBQ1Y7UUFFRCxJQUFJLElBQUksQ0FBQyxXQUFXLElBQUksSUFBSSxFQUFFO1lBQzFCLE9BQU8sQ0FBQyxHQUFHLENBQUMsbUNBQW1DLENBQUMsQ0FBQztZQUNqRCxPQUFPO1NBQ1Y7UUFFRCxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3RDLENBQUM7Q0FDSiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCwgZGVib3VuY2VUaW1lLCBPYnNlcnZhYmxlLCBTdWJqZWN0IH0gZnJvbSBcInJ4anNcIjtcbmltcG9ydCB7IFR1cGxlU2VsZWN0b3IgfSBmcm9tIFwiLi4vVHVwbGVTZWxlY3RvclwiO1xuaW1wb3J0IHsgVHVwbGVEYXRhTG9hZGVyIH0gZnJvbSBcIi4vVHVwbGVEYXRhTG9hZGVyXCI7XG5pbXBvcnQgeyBmaWx0ZXIsIHNraXAsIHRha2VVbnRpbCB9IGZyb20gXCJyeGpzL29wZXJhdG9yc1wiO1xuaW1wb3J0IHsgTmdPbkRlc3Ryb3lFdmVudEkgfSBmcm9tIFwiLi4vLi4vdXRpbC9OZ0xpZmVDeWNsZUV2ZW50c1wiO1xuaW1wb3J0IHsgVm9ydGV4Q2xpZW50QUJDIH0gZnJvbSBcIi4uL1ZvcnRleENsaWVudEFCQ1wiO1xuaW1wb3J0IHsgRm9ybUdyb3VwIH0gZnJvbSBcIkBhbmd1bGFyL2Zvcm1zXCI7XG5pbXBvcnQgeyBUdXBsZURhdGFMb2FkZXJUdXBsZUFCQyB9IGZyb20gXCIuL1R1cGxlRGF0YUxvYWRlclR1cGxlQUJDXCI7XG5pbXBvcnQgeyBkYXRlU3RyIH0gZnJvbSBcIi4uL1V0aWxNaXNjXCI7XG5pbXBvcnQgeyBfRGF0YUxvY2tTdGF0dXNUdXBsZSB9IGZyb20gXCIuL1R1cGxlRGF0YUxvYWRlclR1cGxlc1wiO1xuXG5leHBvcnQgY2xhc3MgVHVwbGVEYXRhTm90VmFsaWRFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgICBjb25zdHJ1Y3RvcihtZXNzYWdlPzogc3RyaW5nKSB7XG4gICAgICAgIHN1cGVyKG1lc3NhZ2UpO1xuICAgICAgICB0aGlzLm5hbWUgPSBcIlR1cGxlRGF0YU5vdFZhbGlkRXJyb3JcIjtcbiAgICB9XG59XG5cbi8qXG4gKiBFYWNoIHR1cGxlIChUdXBsZVR5cGUpIG9mIGRhdGEgdGhhdCBuZWVkcyB0byBiZSBsb2FkZWQgd2l0aCB0aGVcbiAqIGBUdXBsZURhdGFMb2FkZXJgIGlzIGFzc2lnbmVkIGEgZGVsZWdhdGUgb2YgdGhlIHR5cGUgYFR1cGxlRGF0YUxvYWRlckRlbGVnYXRlYFxuICpcbiAqIFRoZSBgVHVwbGVEYXRhTG9hZGVyRGVsZWdhdGVgIGNsYXNzIHByb3ZpZGVzIGFjY2VzcyB0byB0aGUgZGF0YSB0aGF0IGlzIGxvYWRlZFxuICogYW5kIHRvIGJlIHNlbnQgdG8gdGhlIGJhY2tlbmQgZGVsZWdhdGUsIGFjY2Vzc2libGUgdXNpbmcgdGhlIGBkYXRhJGAgYXR0cmlidXRlXG4gKlxuICogVGhlIGRhdGEgdGhhdCBpcyB0byBiZSBsb2FkZWQgYW5kIG1vZGlmaWVkIGlzIGRlc2NyaWJlZCB1c2luZyBhIGBUdXBsZVNlbGVjdG9yYFxuICogYWNjZXNzaWJsZSB1c2luZyB0aGUgYHNlbGVjdG9yJGAgYXR0cmlidXRlLiBVc2luZyBgLm5leHQoKWAgb24gdGhlIGBzZWxlY3RvciRgXG4gKiB3aWxsIHRyaWdnZXIgYSByZS1sb2FkIG9mIHRoZSBkYXRhLlxuICpcbiAqIElmIGEgbG9jayBpcyBoZWxkIGJ5IHRoZSBjdXJyZW50IHVzZXIgYW5kIGVkaXRpbmcgaXMgYWxsb3dlZCB0aGUgYGRpc2FibGVkYFxuICogYXR0cmlidXRlIHJldHVybnMgYGZhbHNlYC4gSWYgYSBsb2NrIGlzIG5vdCBoZWxkIG9yIHNvbWVvbmUgZWxzZSBob2xkcyB0aGUgbG9ja1xuICogdGhlIGBkaXNhYmxlZGAgYXR0cmlidXRlIHdpbGwgcmV0dXJuIGB0cnVlYC4gQW55IHVwZGF0ZXMgb3IgYXR0ZW1wdHMgdG8gbG9ja1xuICogd2lsbCBmYWlsIGFuZCB0aHJvdyBhbiBleGNlcHRpb24gaWYgYGRpc2FibGVkYCByZXR1cm5zIGBUcnVlYC4gRGF0YSBob3dldmVyXG4gKiBjYW4gc3RpbGwgYmUgbG9hZGVkIGFuZCB3YXRjaGVkIGZvciBsaXZlIHVwZGF0ZXMuXG4gKiAqL1xuZXhwb3J0IGNsYXNzIFR1cGxlRGF0YUxvYWRlckRlbGVnYXRlPFxuICAgIFR1cGxlVHlwZSBleHRlbmRzIFR1cGxlRGF0YUxvYWRlclR1cGxlQUJDLFxuPiB7XG4gICAgLy8gVGhpcyBhbGxvd3Mgc29tZSBVSSBhbmltYXRpb25zIHRvIHJ1blxuICAgIHJlYWRvbmx5IEFOSU1BVElPTl9XQUlUX1RJTUVfTVMgPSA0MDA7XG5cbiAgICAvKiogTG9hZGluZyBpbmRpY2F0b3JzICoqL1xuICAgIHJlYWRvbmx5IGxvYWRpbmckID0gbmV3IEJlaGF2aW9yU3ViamVjdDxib29sZWFuPihmYWxzZSk7XG4gICAgcmVhZG9ubHkgc3RvcmluZyQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+KGZhbHNlKTtcbiAgICByZWFkb25seSBkZWxldGluZyQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+KGZhbHNlKTtcblxuICAgIC8vIFNvbWV0aGluZyBpcyBoYXBwZW5pbmcgaW5kaWNhdG9yXG4gICAgcmVhZG9ubHkgcmVhZHkkID0gbmV3IEJlaGF2aW9yU3ViamVjdDxib29sZWFuPih0cnVlKTtcblxuICAgIHJlYWRvbmx5IGRhdGEkID0gbmV3IEJlaGF2aW9yU3ViamVjdDxUdXBsZVR5cGUgfCBudWxsPihudWxsKTtcbiAgICByZWFkb25seSBzZWxlY3RvciQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PFR1cGxlU2VsZWN0b3IgfCBudWxsPihudWxsKTtcblxuICAgIHB1YmxpYyByZWFkb25seSB1dWlkID0gVm9ydGV4Q2xpZW50QUJDLm1ha2VVdWlkKCk7XG5cbiAgICByZWFkb25seSBkYXRhTG9ja2VkJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8Ym9vbGVhbj4oZmFsc2UpO1xuICAgIHJlYWRvbmx5IGxvY2tFeHBpcmVzJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8RGF0ZT4obnVsbCk7XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIGFuIG9ic2VydmFibGUgb2YgYm9vbGVhbiBpbmRpY2F0aW5nIGlmIGRhdGEgaGFzIGJlZW4gZGVsZXRlZC5cbiAgICAgKlxuICAgICAqIEByZXR1cm4ge09ic2VydmFibGU8Ym9vbGVhbj59IEFuIG9ic2VydmFibGUgdGhhdCBlbWl0c1xuICAgICAqICBhIGJvb2xlYW4gdmFsdWUgaW5kaWNhdGluZyBpZiBkYXRhIGhhcyBiZWVuIGRlbGV0ZWQuXG4gICAgICovXG4gICAgZ2V0IGRhdGFEZWxldGVkJCgpOiBPYnNlcnZhYmxlPHZvaWQ+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2RhdGFEZWxldGVkU3ViamVjdC5hc09ic2VydmFibGUoKTtcbiAgICB9XG4gICAgcHJpdmF0ZSBfZGF0YURlbGV0ZWRTdWJqZWN0ID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcblxuICAgIC8vIGZvcm1Hcm91cERpcnR5IGlzIHByaXZhdGUsIGJlY2F1c2UgaXQncyBvbmx5IGxvY2FsIHRvIHRoaXMgZm9ybVxuICAgIC8vIGRhdGFEaXJ0eU9yV2VIYXZlTG9jayQgaXMgd2hhdCB0aGUgVUkgd2lsbCB1c2UsIGJlY2F1c2UuLi5cbiAgICAvLyBUaGUgZm9ybSBncm91cCB3aWxsIG5vdCBiZSBkaXJ0eSBoYXZlIHJlY2VpdmluZyBhIGxpdmUgdXBkYXRlIHZpYSB0aGVcbiAgICAvLyBsb2NrIHN0YXR1cy5cbiAgICAvLyBUaGUgZmFjdCB0aGF0IHdlIGhhdmUgYSBsb2NrIGluZGljYXRlcyB0aGF0IHRoZSBmb3JtIGlzIGRpcnR5LlxuICAgIC8vIEhvd2V2ZXIsIHRvIHJlc3BvbmQgcXVpY2tlciwgd2UgYWxzbyB3YW50IHRvIHVzZSB0aGUgZGlydHkgZmxhZy5cbiAgICAvLyBoZW5jZSwgdGhlIG5hbWUgXCJkaXJ0eSwgb3Igd2UgaGF2ZSBsb2NrXCJcbiAgICBwcml2YXRlIHJlYWRvbmx5IGZvcm1Hcm91cERpcnR5JCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8Ym9vbGVhbj4oZmFsc2UpO1xuICAgIHJlYWRvbmx5IGRhdGFEaXJ0eU9yV2VIYXZlTG9jayQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+KGZhbHNlKTtcblxuICAgIHByaXZhdGUgX2RhdGFMb2FkZXI6IFR1cGxlRGF0YUxvYWRlciB8IG51bGwgPSBudWxsO1xuXG4gICAgcmVhZG9ubHkgZm9ybUdyb3VwJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8Rm9ybUdyb3VwIHwgbnVsbD4obnVsbCk7XG5cbiAgICAvLyBub2luc3BlY3Rpb24gSlNVbnVzZWRMb2NhbFN5bWJvbHNcbiAgICBwcml2YXRlIHJlZ2lzdGVyZWROYW1lOiBzdHJpbmcgPSBcIlNFVCBGUk9NIFR1cGxlRGF0YUxvYWRlci5hZGREZWxlZ2F0ZVwiO1xuXG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIHByaXZhdGUgbGlmZUN5Y2xlRXZlbnRzOiBOZ09uRGVzdHJveUV2ZW50SSxcbiAgICAgICAgcHJpdmF0ZSB1c2VyVXVpZCQ6IEJlaGF2aW9yU3ViamVjdDxzdHJpbmc+LFxuICAgICkge1xuICAgICAgICAvLyBXaGVuIGRpcnR5IGNoYW5nZXMsIFNldCB0aGUgb2JzZXJ2YWJsZSB0aGUgVUlzIHdpbGwgdXNlXG4gICAgICAgIHRoaXMuZm9ybUdyb3VwRGlydHkkXG4gICAgICAgICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5saWZlQ3ljbGVFdmVudHMub25EZXN0cm95RXZlbnQpKVxuICAgICAgICAgICAgLnBpcGUoZmlsdGVyKChkaXJ0eSkgPT4gZGlydHkpKVxuICAgICAgICAgICAgLnN1YnNjcmliZSgoZGlydHk6IGJvb2xlYW4pID0+IHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICAgICAgYFR1cGxlRGF0YUxvYWRlckRlbGVnYXRlOiAke3RoaXMucmVnaXN0ZXJlZE5hbWV9YCArXG4gICAgICAgICAgICAgICAgICAgICAgICBcIiBmb3JtR3JvdXBEaXJ0eSQsIGRpcnR5PXRydWUsXCIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgXCIgY2FsbGluZyBzZXREYXRhRGlydHlPcldlSGF2ZUxvY2sgXCIsXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB0aGlzLnNldERhdGFEaXJ0eU9yV2VIYXZlTG9jayhkaXJ0eSk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAvLyBXaGVuIGRpcnR5IGNoYW5nZXMsIHVwZGF0ZSB0aGUgc2VydmVyLlxuICAgICAgICB0aGlzLmZvcm1Hcm91cERpcnR5JFxuICAgICAgICAgICAgLnBpcGUodGFrZVVudGlsKHRoaXMubGlmZUN5Y2xlRXZlbnRzLm9uRGVzdHJveUV2ZW50KSlcbiAgICAgICAgICAgIC5waXBlKGZpbHRlcigoZGlydHkpID0+IGRpcnR5KSlcbiAgICAgICAgICAgIC5waXBlKGRlYm91bmNlVGltZSg1MDApKVxuICAgICAgICAgICAgLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgICAgIGBUdXBsZURhdGFMb2FkZXJEZWxlZ2F0ZTogJHt0aGlzLnJlZ2lzdGVyZWROYW1lfWAgK1xuICAgICAgICAgICAgICAgICAgICAgICAgXCIgZm9ybUdyb3VwRGlydHkkLCBkaXJ0eT10cnVlLFwiICtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiIGNhbGxpbmcgc2VuZGluZyBsb2NrIHRvIHNlcnZlciBcIixcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIC8vIFNlbmQgYSBtZW1vcnkgb25seSBzbmFwc2hvdCBvZiB0aGUgZGF0YSB0byB0aGUgc2VydmVyXG4gICAgICAgICAgICAgICAgdGhpcy5sb2NrRm9yRWRpdGluZygpLmNhdGNoKChlKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGBFUlJPUjogJHtkYXRlU3RyKCl9OiB1cGRhdGVFZGl0aW5nOiAke2V9YCk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLmRhdGEkXG4gICAgICAgICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5saWZlQ3ljbGVFdmVudHMub25EZXN0cm95RXZlbnQpKVxuICAgICAgICAgICAgLnN1YnNjcmliZSgoZGF0YTogVHVwbGVEYXRhTG9hZGVyVHVwbGVBQkMpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAoZGF0YSA9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgICAgICAgICAgYFR1cGxlRGF0YUxvYWRlckRlbGVnYXRlOiAke3RoaXMucmVnaXN0ZXJlZE5hbWV9YCArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgXCIgZGF0YSQsT2JzZXJ2ZWQgZGF0YSRcIiArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgXCIgY2hhbmdlLFwiICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBgIHZhbHVlIGlzIG51bGxgLFxuICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmZvcm1Hcm91cCQubmV4dChudWxsKTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGNvbnN0IHVuc3ViID0gdGhpcy5kYXRhJC5waXBlKHNraXAoMSkpO1xuICAgICAgICAgICAgICAgIGNvbnN0IGZvcm1Hcm91cCA9IGRhdGEuY3JlYXRlRm9ybUdyb3VwKCk7XG4gICAgICAgICAgICAgICAgaWYgKGZvcm1Hcm91cCA9PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgdGhpcy5kYXRhLnVwZGF0ZVZhbGlkYXRpb24oZm9ybUdyb3VwKTtcblxuICAgICAgICAgICAgICAgIGlmICh0aGlzLmRhdGFEaXJ0eU9yV2VIYXZlTG9jaykge1xuICAgICAgICAgICAgICAgICAgICBmb3JtR3JvdXAubWFya0FsbEFzVG91Y2hlZCgpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIC8vIFRvIHN1YnNjcmliZSBmb3IgY2hhbmdlcyBpbiB0aGUgZm9ybSwgYW5kIGFzc2lnbiB0aGVtIGJhY2sgdG8gdGhlXG4gICAgICAgICAgICAgICAgLy8gdHVwbGVcbiAgICAgICAgICAgICAgICBmb3JtR3JvdXAudmFsdWVDaGFuZ2VzXG4gICAgICAgICAgICAgICAgICAgIC5waXBlKGRlYm91bmNlVGltZSgyMDApKSAvLyBEZWJvdW5jZSBhIExPVCBvZiBjYWxsc1xuICAgICAgICAgICAgICAgICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5saWZlQ3ljbGVFdmVudHMub25EZXN0cm95RXZlbnQpKVxuICAgICAgICAgICAgICAgICAgICAucGlwZSh0YWtlVW50aWwodW5zdWIpKVxuICAgICAgICAgICAgICAgICAgICAuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuc3luY2hyb25pc2VGb3JtR3JvdXBUb0RhdGFUdXBsZShmb3JtR3JvdXApO1xuICAgICAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgICAgICBgVHVwbGVEYXRhTG9hZGVyRGVsZWdhdGU6ICR7dGhpcy5yZWdpc3RlcmVkTmFtZX1gICtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiIGRhdGEkIEVtaXR0aW5nIG5ldyBwcmlzdGluZVwiICtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwiIGZvcm1Hcm91cCRcIixcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIHRoaXMucmVzZXRGb3JtR3JvdXBEaXJ0eSgpO1xuICAgICAgICAgICAgICAgIHRoaXMuZm9ybUdyb3VwJC5uZXh0KGZvcm1Hcm91cCk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLmRhdGFEaXJ0eU9yV2VIYXZlTG9jayRcbiAgICAgICAgICAgIC5waXBlKHRha2VVbnRpbCh0aGlzLmxpZmVDeWNsZUV2ZW50cy5vbkRlc3Ryb3lFdmVudCkpXG4gICAgICAgICAgICAucGlwZShmaWx0ZXIoKHZhbHVlKSA9PiB2YWx1ZSkpXG4gICAgICAgICAgICAuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5mb3JtR3JvdXAgIT0gbnVsbCkge1xuICAgICAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICAgICAgICAgIGBUdXBsZURhdGFMb2FkZXJEZWxlZ2F0ZTogJHt0aGlzLnJlZ2lzdGVyZWROYW1lfWAgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwiIGRhdGFEaXJ0eU9yV2VIYXZlTG9jayQsXCIgK1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwiIGNhbGxpbmcgbWFya0FsbEFzVG91Y2hlZFwiLFxuICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmZvcm1Hcm91cC5tYXJrQWxsQXNUb3VjaGVkKCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgLy8gVGVhciBkb3duIHRoZSBjaXJjdWxhciBkZXBlbmRlbmN5XG4gICAgICAgIHRoaXMubGlmZUN5Y2xlRXZlbnRzLm9uRGVzdHJveUV2ZW50IC8vXG4gICAgICAgICAgICAuc3Vic2NyaWJlKCgpID0+ICh0aGlzLl9kYXRhTG9hZGVyID0gbnVsbCkpO1xuICAgIH1cblxuICAgIHByaXZhdGUgc3luY2hyb25pc2VGb3JtR3JvdXBUb0RhdGFUdXBsZShmb3JtR3JvdXA6IEZvcm1Hcm91cDxhbnk+KSB7XG4gICAgICAgIC8vIHVzZSBmb3JtR3JvdXAgZnJvbSBjbG9zdXJlXG4gICAgICAgIC8vIFRoaXMgd2lsbCBjcmVhdGUgY2lyY3VsYXIgZGVwZW5kZW5jeSB1bnRpbCB0YWtlVW50aWxcbiAgICAgICAgY29uc3QgY2hhbmdlcyA9IGZvcm1Hcm91cC5nZXRSYXdWYWx1ZSgpO1xuICAgICAgICBpZiAoY2hhbmdlcyA9PSBudWxsKSB7XG4gICAgICAgICAgICAvLyBGb3JtR3JvdXAgaGFzIGJlZW4gZGVzdHJveWVkXG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICBgVHVwbGVEYXRhTG9hZGVyRGVsZWdhdGU6ICR7dGhpcy5yZWdpc3RlcmVkTmFtZX1gICtcbiAgICAgICAgICAgICAgICBcIiBkYXRhJCBPYnNlcnZlZFwiICtcbiAgICAgICAgICAgICAgICBcIiBmb3JtR3JvdXAkLnZhbHVlQ2hhbmdlcywgVXBkYXRpbmcgZGF0YSBmcm9tXCIgK1xuICAgICAgICAgICAgICAgIFwiIGZvcm1Hcm91cFwiLFxuICAgICAgICApO1xuXG4gICAgICAgIC8vIFVwZGF0ZSB0aGUgZGF0YSB0dXBsZSBmcm9tIHRoZSBGb3JtR3JvdXBcbiAgICAgICAgY29uc3QgZGlmZmVyZW5jZXMgPSB0aGlzLmRhdGEudXBkYXRlRnJvbUZvcm1Hcm91cChjaGFuZ2VzKTtcblxuICAgICAgICAvLyBTZXQgdGhlIGRpcnR5IGZsYWdcbiAgICAgICAgaWYgKGRpZmZlcmVuY2VzKSB7XG4gICAgICAgICAgICAvLyBPbmx5IHVwZGF0ZSB0aGUgdmFsaWRhdGlvbiBpZiBzb21ldGhpbmcgaGFzXG4gICAgICAgICAgICAvLyBjaGFuZ2VkLCBvciB3ZSBnZXQgYW4gaW5maW5pdGUgbG9vcC5cbiAgICAgICAgICAgIHRoaXMuZGF0YS51cGRhdGVWYWxpZGF0aW9uKGZvcm1Hcm91cCk7XG4gICAgICAgICAgICB0aGlzLnNldEZvcm1Hcm91cERpcnR5KHRydWUpO1xuICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgYFR1cGxlRGF0YUxvYWRlckRlbGVnYXRlOiAke3RoaXMucmVnaXN0ZXJlZE5hbWV9YCArXG4gICAgICAgICAgICAgICAgICAgIFwiIGRhdGEkIFVwZGF0aW5nIGRhdGEgZnJvbVwiICtcbiAgICAgICAgICAgICAgICAgICAgXCIgZm9ybUdyb3VwIGZvdW5kIGNoYW5nZXNcIixcbiAgICAgICAgICAgICk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgICAgICBgVHVwbGVEYXRhTG9hZGVyRGVsZWdhdGU6ICR7dGhpcy5yZWdpc3RlcmVkTmFtZX1gICtcbiAgICAgICAgICAgICAgICAgICAgXCIgZGF0YSQgVXBkYXRpbmcgZGF0YSBmcm9tXCIgK1xuICAgICAgICAgICAgICAgICAgICBcIiBmb3JtR3JvdXAsIGJvdGggbW9kZWxzIG1hdGNoXCIsXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBhbmltYXRpb25EZWxheShcbiAgICAgICAgd2l0aEFuaW1hdGlvbkRlbGF5OiBib29sZWFuLFxuICAgICAgICBzdGFydERhdGVNczogbnVtYmVyLFxuICAgICk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAoIXdpdGhBbmltYXRpb25EZWxheSkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgdGltZVRha2VuU29GYXIgPSBuZXcgRGF0ZSgpLmdldE1pbGxpc2Vjb25kcygpIC0gc3RhcnREYXRlTXM7XG4gICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgICBzZXRUaW1lb3V0KHJlc29sdmUsIHRoaXMuQU5JTUFUSU9OX1dBSVRfVElNRV9NUyAtIHRpbWVUYWtlblNvRmFyKTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSByZXNldEZvcm1Hcm91cERpcnR5KCk6IHZvaWQge1xuICAgICAgICB0aGlzLmZvcm1Hcm91cERpcnR5JC5uZXh0KGZhbHNlKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHNldEZvcm1Hcm91cERpcnR5KHZhbHVlOiBib29sZWFuKTogdm9pZCB7XG4gICAgICAgIC8vIFRoaW5ncyB1c2UgZGlydHkgdXBkYXRlcyB0byBrbm93IHdoZW4gdXBkYXRlcyBoYXZlIGhhcHBlbmVkLlxuICAgICAgICAvLyBFdmVuIGlmIGl0J3MgdHJ1ZSAtPiB0cnVlXG4gICAgICAgIHRoaXMuZm9ybUdyb3VwRGlydHkkLm5leHQodGhpcy5mb3JtR3JvdXBEaXJ0eSB8fCB2YWx1ZSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBnZXQgZm9ybUdyb3VwRGlydHkoKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiB0aGlzLmZvcm1Hcm91cERpcnR5JC5nZXRWYWx1ZSgpO1xuICAgIH1cblxuICAgIHByaXZhdGUgcmVzZXREYXRhRGlydHlPcldlSGF2ZUxvY2soKTogdm9pZCB7XG4gICAgICAgIHRoaXMuZGF0YURpcnR5T3JXZUhhdmVMb2NrJC5uZXh0KGZhbHNlKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHNldERhdGFEaXJ0eU9yV2VIYXZlTG9jayh2YWx1ZTogYm9vbGVhbik6IHZvaWQge1xuICAgICAgICAvLyBUaGluZ3MgdXNlIGxvY2sgdXBkYXRlcyB0byBrbm93IHdoZW4gdXBkYXRlcyBoYXZlIGhhcHBlbmVkLlxuICAgICAgICAvLyBFdmVuIGlmIGl0J3MgdHJ1ZSAtPiB0cnVlXG4gICAgICAgIHRoaXMuZGF0YURpcnR5T3JXZUhhdmVMb2NrJC5uZXh0KHRoaXMuZGF0YURpcnR5T3JXZUhhdmVMb2NrIHx8IHZhbHVlKTtcblxuICAgICAgICBpZiAodGhpcy5kYXRhRGlydHlPcldlSGF2ZUxvY2sgJiYgIXRoaXMuZGF0YUxvY2tlZCkge1xuICAgICAgICAgICAgLy8gUHJlZW1wdGl2ZWx5IHNldCB0aGUgbG9ja1xuICAgICAgICAgICAgLy8gY29uc29sZS5sb2coXCJQcmVlbXB0aXZlbHkgc2V0dGluZyB0aGUgbG9jayBmb3IgYmV0dGVyIHJlc3BvbnNlXCIpO1xuICAgICAgICAgICAgdGhpcy5kYXRhTG9ja2VkJC5uZXh0KHRydWUpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgZ2V0IGRhdGFMb2NrZWQoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmRhdGFMb2NrZWQkLmdldFZhbHVlKCk7XG4gICAgfVxuXG4gICAgZ2V0IGRhdGFEaXJ0eU9yV2VIYXZlTG9jaygpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZGF0YURpcnR5T3JXZUhhdmVMb2NrJC5nZXRWYWx1ZSgpO1xuICAgIH1cblxuICAgIGdldCBzZWxlY3RvcigpOiBUdXBsZVNlbGVjdG9yIHwgbnVsbCB7XG4gICAgICAgIHJldHVybiB0aGlzLnNlbGVjdG9yJC5nZXRWYWx1ZSgpO1xuICAgIH1cblxuICAgIHByaXZhdGUgZ2V0IGZvcm1Hcm91cCgpOiBGb3JtR3JvdXAgfCBudWxsIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZm9ybUdyb3VwJD8uZ2V0VmFsdWUoKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHNldCByZWFkeSh2YWx1ZTogYm9vbGVhbikge1xuICAgICAgICB0aGlzLnJlYWR5JC5uZXh0KHZhbHVlKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHNldCBzdG9yaW5nKHZhbHVlOiBib29sZWFuKSB7XG4gICAgICAgIHRoaXMuc3RvcmluZyQubmV4dCh2YWx1ZSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBzZXQgbG9hZGluZyh2YWx1ZTogYm9vbGVhbikge1xuICAgICAgICB0aGlzLmxvYWRpbmckLm5leHQodmFsdWUpO1xuICAgIH1cblxuICAgIHByaXZhdGUgc2V0IGRlbGV0aW5nKHZhbHVlOiBib29sZWFuKSB7XG4gICAgICAgIHRoaXMuZGVsZXRpbmckLm5leHQodmFsdWUpO1xuICAgIH1cblxuICAgIGdldCBkYXRhKCk6IFR1cGxlVHlwZSB7XG4gICAgICAgIHJldHVybiB0aGlzLmRhdGEkLmdldFZhbHVlKCk7XG4gICAgfVxuXG4gICAgc2V0IGRhdGEodmFsdWU6IFR1cGxlVHlwZSkge1xuICAgICAgICB0aGlzLmRhdGEkLm5leHQodmFsdWUpO1xuICAgIH1cblxuICAgIC8vIG5vaW5zcGVjdGlvbiBKU1VudXNlZExvY2FsU3ltYm9sc1xuICAgIHByaXZhdGUgc2V0RGF0YUxvYWRlcihsb2FkZXI6IFR1cGxlRGF0YUxvYWRlcikge1xuICAgICAgICB0aGlzLl9kYXRhTG9hZGVyID0gbG9hZGVyO1xuICAgIH1cblxuICAgIC8vIG5vaW5zcGVjdGlvbiBKU1VudXNlZExvY2FsU3ltYm9sc1xuICAgIHByaXZhdGUgcHJvY2Vzc0RhdGFVcGRhdGUoZGF0YTogVHVwbGVUeXBlKTogdm9pZCB7XG4gICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgXCJUdXBsZURhdGFMb2FkZXJEZWxlZ2F0ZTpcIiArXG4gICAgICAgICAgICAgICAgXCIgcHJvY2Vzc0RhdGFVcGRhdGUgcmVjZWl2ZWQgZGF0YSBmcm9tIHNlcnZlclwiLFxuICAgICAgICApO1xuICAgICAgICB0aGlzLmRhdGEkLm5leHQoZGF0YSk7XG4gICAgfVxuXG4gICAgLy8gbm9pbnNwZWN0aW9uIEpTVW51c2VkTG9jYWxTeW1ib2xzXG4gICAgcHJpdmF0ZSBwcm9jZXNzTG9ja1N0YXR1cyhcbiAgICAgICAgbG9ja1N0YXR1czogX0RhdGFMb2NrU3RhdHVzVHVwbGU8VHVwbGVUeXBlPixcbiAgICApOiB2b2lkIHtcbiAgICAgICAgdGhpcy5kYXRhTG9ja2VkJC5uZXh0KGxvY2tTdGF0dXMubG9ja2VkKTtcbiAgICAgICAgaWYgKGxvY2tTdGF0dXMuZGVsZXRlZCkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgXCJUdXBsZURhdGFMb2FkZXJEZWxlZ2F0ZTpcIiArXG4gICAgICAgICAgICAgICAgICAgIFwiIHByb2Nlc3NMb2NrU3RhdHVzIHJlY2VpdmVkIERFTEVURSBmcm9tIHNlcnZlclwiLFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIHRoaXMuZGF0YSQubmV4dChudWxsKTtcbiAgICAgICAgICAgIHRoaXMuc2VsZWN0b3IkLm5leHQobnVsbCk7XG4gICAgICAgICAgICB0aGlzLnJlc2V0RGF0YURpcnR5T3JXZUhhdmVMb2NrKCk7XG4gICAgICAgICAgICB0aGlzLmxvY2tFeHBpcmVzJC5uZXh0KG51bGwpO1xuICAgICAgICAgICAgdGhpcy5fZGF0YURlbGV0ZWRTdWJqZWN0Lm5leHQoKTtcbiAgICAgICAgfSBlbHNlIGlmIChsb2NrU3RhdHVzLmxvY2tlZCkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgXCJUdXBsZURhdGFMb2FkZXJEZWxlZ2F0ZTpcIiArXG4gICAgICAgICAgICAgICAgICAgIFwiIHByb2Nlc3NMb2NrU3RhdHVzIHJlY2VpdmVkIExPQ0sgZnJvbSBzZXJ2ZXJcIixcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBjb25zdCB1c2VyVXVpZCA9IHRoaXMudXNlclV1aWQkLmdldFZhbHVlKCk7XG4gICAgICAgICAgICB0aGlzLnNldERhdGFEaXJ0eU9yV2VIYXZlTG9jayhcbiAgICAgICAgICAgICAgICBsb2NrU3RhdHVzLmxvY2tlZEJ5VXNlclV1aWQgPT0gdXNlclV1aWQsXG4gICAgICAgICAgICApO1xuXG4gICAgICAgICAgICBpZiAobG9ja1N0YXR1cy5saXZlVXBkYXRlRGF0YUZyb21EZWxlZ2F0ZVV1aWQgIT0gdGhpcy51dWlkKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5kYXRhJC5uZXh0KGxvY2tTdGF0dXMubGl2ZVVwZGF0ZURhdGFUdXBsZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0aGlzLmxvY2tFeHBpcmVzJC5uZXh0KGxvY2tTdGF0dXMubG9ja0F1dG9FeHBpcmVEYXRlKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgIFwiVHVwbGVEYXRhTG9hZGVyRGVsZWdhdGU6XCIgK1xuICAgICAgICAgICAgICAgICAgICBcIiBwcm9jZXNzTG9ja1N0YXR1cyByZWNlaXZlZCBVTkxPQ0sgZnJvbSBzZXJ2ZXJcIixcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICB0aGlzLnJlc2V0RGF0YURpcnR5T3JXZUhhdmVMb2NrKCk7XG4gICAgICAgICAgICB0aGlzLmxvY2tFeHBpcmVzJC5uZXh0KG51bGwpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVmFsaWRhdGVzIHRoZSBmb3JtIGFuZCB0aHJvd3MgYW4gZXJyb3IgaWYgaXQgaXNcbiAgICAgKiBpbmNvbXBsZXRlIG9yIGNvbnRhaW5zIGVycm9ycy5cbiAgICAgKlxuICAgICAqIEB0aHJvd3Mge1R1cGxlRGF0YU5vdFZhbGlkRXJyb3J9IElmIHRoZSBmb3JtIGlzIGluY29tcGxldGVcbiAgICAgKiBvciBjb250YWlucyBlcnJvcnMuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHt2b2lkfVxuICAgICAqL1xuICAgIHZhbGlkYXRlRm9ybSgpOiB2b2lkIHtcbiAgICAgICAgY29uc3QgZm9ybUdyb3VwID0gdGhpcy5mb3JtR3JvdXA7XG4gICAgICAgIGlmIChmb3JtR3JvdXAgPT0gbnVsbCkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gVGhlc2UgdXBkYXRlcyBhcmUgZGVib3VuY2VkLCBlbnN1cmUgd2UgaGF2ZSB0aGUgbGF0ZXN0