UNPKG

@iotize/ionic

Version:

Iotize specific building blocks on top of @ionic/angular.

243 lines 39.2 kB
import { ChangeDetectorRef, Component, EventEmitter, Input, NgZone, Output, } from '@angular/core'; import { AlertController, ModalController } from '@ionic/angular'; import { isCodeError } from '@iotize/common/error'; import { TapClientError } from '@iotize/tap/client/impl'; import { BehaviorSubject, merge, of, Subject } from 'rxjs'; import { filter, shareReplay, startWith, switchMap, tap } from 'rxjs/operators'; import { CurrentDeviceService } from '../current-device.service'; import { LibError } from '../errors'; import { isNfcTapRequiredError } from '../nfc/utility'; import { PendingCallManager } from '../pending-call-manager'; import { TapValueEditorModalComponent } from '../tap-value-editor-modal/tap-value-editor-modal.component'; import * as i0 from "@angular/core"; import * as i1 from "@ionic/angular"; import * as i2 from "../current-device.service"; import * as i3 from "@angular/common"; function isTapNotConnectedError(err) { return (isNfcTapRequiredError(err) || isCodeError(TapClientError.Code.NotConnectedError, err)); } export class TapValueEditorContainerComponent { set variable(v) { this.variableChange.next(v); } /** * Force value to display */ set value(v) { this.forceValueChange.next(v); } constructor(alertDialog, tapService, changeDetectorRef, ngZone, modalController) { this.alertDialog = alertDialog; this.tapService = tapService; this.changeDetectorRef = changeDetectorRef; this.ngZone = ngZone; this.modalController = modalController; this.button = false; this.inputOptions = { type: 'text', }; /** * Show refresh button */ this.showRefreshButton = false; /** * Show submit button */ this.showSubmitButton = false; /** * Show edit button */ this.showEditButton = false; /** * Enable edition mode */ this.editModeChange = new BehaviorSubject(false); this.loadingChange = new BehaviorSubject(false); this.lines = 'none'; this.modalEdition = false; this.submit = new EventEmitter(); this.refresh = new EventEmitter(); this.variableChange = new BehaviorSubject(undefined); this.variableValuesStream = this.variableChange.pipe(filter((v) => !!v), switchMap((v) => v.values), tap((v) => { this.lastReadValue = v; })); this.forceValueChange = new Subject(); this.valueToDisplay = this.editModeChange.pipe(switchMap((editMode) => { if (editMode || this.pendingSubmitValue !== undefined) { return of(this.valueToSubmit); } else { const lastValue = this.variableChange.value?.valueSnapshot; return merge(this.variableValuesStream.pipe(startWith(lastValue)), this.forceValueChange); } }), shareReplay(1)); this.destroyed = new Subject(); } ngOnInit() { this.pendingCallManager = PendingCallManager.create(this.tapService); this.pendingCallManager.pendingCallResult.subscribe((error) => { this.pendingSubmitValue = undefined; this.valueToSubmit = undefined; this.editModeChange.next(false); this.error = error; this.changeDetectorRef.detectChanges(); }); } // hasValueChanged(value: any) { // return this._lastValueFromDevice !== value; // } submitValue() { if (this.valueToSubmit === undefined) { console.warn('No value to submit yet'); return; } this._submitValue(this.valueToSubmit); } async notifyValueChange(newValue, forceWrite = false) { if (forceWrite || this.lastReadValue !== newValue) { await this._submitValue(newValue); } } async openErrorDialog(error) { const dialog = await this.alertDialog.create({ header: 'Error', message: error.message, buttons: ['Ok'], }); await dialog.present(); } async explainWaitForSubmit() { const alertDialog = await this.alertDialog.create({ header: 'Value not sent yet', message: 'New value will be sent to the device once you are reconnected', buttons: ['Ok'], }); await alertDialog.present(); } async _submitValue(newValue) { this.submit.emit(newValue); // OLD System const variable = this.variableChange.value; if (!variable) { this.error = LibError.componentArgumentRequired(this.constructor.name, 'variable'); return; } if (!this.pendingCallManager) { return; } console.log(`[${variable.id}]`, 'Submit value', newValue); await this.startAction(this.pendingCallManager .exec(async () => { const result = await variable.write(newValue); this.editModeChange.next(false); this.forceValueChange.next(newValue); this.error = undefined; this.pendingSubmitValue = undefined; return result; }) .catch((err) => { if (!isTapNotConnectedError(err)) { console.warn(`[${variable.id}]`, `onSubmit value "${newValue}" error`, err); this.error = err; } else { this.pendingSubmitValue = newValue; this.valueToSubmit = newValue; this.editModeChange.next(false); } })); } async refreshValue() { this.refresh.next(undefined); const variable = this.variableChange.value; if (variable) { await this.startAction(variable .read() .then((newValue) => { this.forceValueChange.next(newValue); }) .catch((err) => { console.log(err); })); } } async enableEditMode() { this.modalEdition ? this.showEditModal() : this.editModeChange.next(true); } async showEditModal() { const lastValue = this.variableChange.value?.valueSnapshot; const modal = await this.modalController.create({ component: TapValueEditorModalComponent, componentProps: { inputOptions: this.inputOptions, value: lastValue, }, }); await modal.present(); const result = await modal.onDidDismiss(); if (result) { if (result.role === 'submit') { const newValue = result.data.value; await this.notifyValueChange(newValue); } } } async startAction(actions) { this.ngZone.run(async () => { this.loadingChange.next(true); try { return await actions; } catch (err) { console.warn('action failed', err); } finally { this.loadingChange.next(false); } }); } ngOnDestroy() { this.destroyed.next(); this.destroyed.complete(); this.pendingCallManager?.destroy(); } cancelEdit() { this.pendingCallManager?.cancel(); this.error = undefined; this.pendingSubmitValue = undefined; this.valueToSubmit = undefined; this.editModeChange.next(false); } } /** @nocollapse */ TapValueEditorContainerComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TapValueEditorContainerComponent, deps: [{ token: i1.AlertController }, { token: i2.CurrentDeviceService }, { token: i0.ChangeDetectorRef }, { token: i0.NgZone }, { token: i1.ModalController }], target: i0.ɵɵFactoryTarget.Component }); /** @nocollapse */ TapValueEditorContainerComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: TapValueEditorContainerComponent, selector: "tap-value-editor-container", inputs: { button: "button", variable: "variable", inputOptions: "inputOptions", showRefreshButton: "showRefreshButton", showSubmitButton: "showSubmitButton", showEditButton: "showEditButton", error: "error", lines: "lines", modalEdition: "modalEdition", value: "value" }, outputs: { submit: "submit", refresh: "refresh" }, ngImport: i0, template: "<ion-item\n style=\"pointer-events: auto\"\n [lines]=\"lines\"\n [button]=\"button\"\n [ngClass]=\"{ 'item-input': editModeChange.value }\"\n>\n <ng-content></ng-content>\n <ion-buttons\n slot=\"end\"\n *ngIf=\"\n showEditButton ||\n showRefreshButton ||\n error ||\n pendingSubmitValue !== undefined\n \"\n >\n <ion-button\n (click)=\"explainWaitForSubmit(); $event.stopPropagation()\"\n *ngIf=\"pendingSubmitValue !== undefined\"\n [disabled]=\"false\"\n color=\"warning\"\n >\n <ion-icon name=\"alert-circle\"></ion-icon>\n </ion-button>\n <ion-button\n (click)=\"enableEditMode(); $event.stopPropagation()\"\n class=\"btn-enable-edit-mode\"\n *ngIf=\"\n pendingSubmitValue === undefined &&\n showEditButton &&\n !editModeChange.value\n \"\n >\n <ion-icon name=\"create\"></ion-icon>\n </ion-button>\n <ion-button\n (click)=\"submitValue(); $event.stopPropagation()\"\n *ngIf=\"\n showSubmitButton &&\n editModeChange.value &&\n pendingSubmitValue === undefined\n \"\n [disabled]=\"loadingChange | async\"\n class=\"btn-submit-value\"\n >\n <ion-icon name=\"send\"></ion-icon>\n </ion-button>\n <ion-button\n (click)=\"cancelEdit(); $event.stopPropagation()\"\n *ngIf=\"editModeChange.value || pendingSubmitValue !== undefined\"\n [disabled]=\"loadingChange | async\"\n class=\"btn-close-edit-mode\"\n >\n <ion-icon name=\"close\"></ion-icon>\n </ion-button>\n <ion-button\n *ngIf=\"showRefreshButton\"\n (click)=\"refreshValue(); $event.stopPropagation()\"\n [disabled]=\"loadingChange | async\"\n class=\"btn-refresh-value\"\n >\n <ion-icon name=\"sync\"></ion-icon>\n </ion-button>\n <ion-button\n *ngIf=\"error\"\n (click)=\"openErrorDialog(error); $event.stopPropagation()\"\n class=\"btn-show-error\"\n >\n <ion-icon slot=\"end\" name=\"alert-circle\" color=\"danger\"></ion-icon>\n </ion-button>\n </ion-buttons>\n <ion-spinner *ngIf=\"loadingChange | async\" slot=\"end\"></ion-spinner>\n</ion-item>\n", styles: ["ion-buttons[slot=end]{margin-inline-start:6px}ion-item{--ion-item-background: transparent;--color: initial}\n"], dependencies: [{ kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i1.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i1.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i1.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i1.IonItem, selector: "ion-item", inputs: ["button", "color", "counter", "counterFormatter", "detail", "detailIcon", "disabled", "download", "fill", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "shape", "target", "type"] }, { kind: "component", type: i1.IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TapValueEditorContainerComponent, decorators: [{ type: Component, args: [{ selector: 'tap-value-editor-container', template: "<ion-item\n style=\"pointer-events: auto\"\n [lines]=\"lines\"\n [button]=\"button\"\n [ngClass]=\"{ 'item-input': editModeChange.value }\"\n>\n <ng-content></ng-content>\n <ion-buttons\n slot=\"end\"\n *ngIf=\"\n showEditButton ||\n showRefreshButton ||\n error ||\n pendingSubmitValue !== undefined\n \"\n >\n <ion-button\n (click)=\"explainWaitForSubmit(); $event.stopPropagation()\"\n *ngIf=\"pendingSubmitValue !== undefined\"\n [disabled]=\"false\"\n color=\"warning\"\n >\n <ion-icon name=\"alert-circle\"></ion-icon>\n </ion-button>\n <ion-button\n (click)=\"enableEditMode(); $event.stopPropagation()\"\n class=\"btn-enable-edit-mode\"\n *ngIf=\"\n pendingSubmitValue === undefined &&\n showEditButton &&\n !editModeChange.value\n \"\n >\n <ion-icon name=\"create\"></ion-icon>\n </ion-button>\n <ion-button\n (click)=\"submitValue(); $event.stopPropagation()\"\n *ngIf=\"\n showSubmitButton &&\n editModeChange.value &&\n pendingSubmitValue === undefined\n \"\n [disabled]=\"loadingChange | async\"\n class=\"btn-submit-value\"\n >\n <ion-icon name=\"send\"></ion-icon>\n </ion-button>\n <ion-button\n (click)=\"cancelEdit(); $event.stopPropagation()\"\n *ngIf=\"editModeChange.value || pendingSubmitValue !== undefined\"\n [disabled]=\"loadingChange | async\"\n class=\"btn-close-edit-mode\"\n >\n <ion-icon name=\"close\"></ion-icon>\n </ion-button>\n <ion-button\n *ngIf=\"showRefreshButton\"\n (click)=\"refreshValue(); $event.stopPropagation()\"\n [disabled]=\"loadingChange | async\"\n class=\"btn-refresh-value\"\n >\n <ion-icon name=\"sync\"></ion-icon>\n </ion-button>\n <ion-button\n *ngIf=\"error\"\n (click)=\"openErrorDialog(error); $event.stopPropagation()\"\n class=\"btn-show-error\"\n >\n <ion-icon slot=\"end\" name=\"alert-circle\" color=\"danger\"></ion-icon>\n </ion-button>\n </ion-buttons>\n <ion-spinner *ngIf=\"loadingChange | async\" slot=\"end\"></ion-spinner>\n</ion-item>\n", styles: ["ion-buttons[slot=end]{margin-inline-start:6px}ion-item{--ion-item-background: transparent;--color: initial}\n"] }] }], ctorParameters: function () { return [{ type: i1.AlertController }, { type: i2.CurrentDeviceService }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }, { type: i1.ModalController }]; }, propDecorators: { button: [{ type: Input }], variable: [{ type: Input }], inputOptions: [{ type: Input }], showRefreshButton: [{ type: Input }], showSubmitButton: [{ type: Input }], showEditButton: [{ type: Input }], error: [{ type: Input }], lines: [{ type: Input }], modalEdition: [{ type: Input }], value: [{ type: Input }], submit: [{ type: Output }], refresh: [{ type: Output }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFwLXZhbHVlLWVkaXRvci1jb250YWluZXIuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvaW90aXplLWlvbmljL3NyYy9saWIvdGFwLXZhbHVlLWVkaXRvci1jb250YWluZXIvdGFwLXZhbHVlLWVkaXRvci1jb250YWluZXIuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvaW90aXplLWlvbmljL3NyYy9saWIvdGFwLXZhbHVlLWVkaXRvci1jb250YWluZXIvdGFwLXZhbHVlLWVkaXRvci1jb250YWluZXIuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLGlCQUFpQixFQUNqQixTQUFTLEVBQ1QsWUFBWSxFQUNaLEtBQUssRUFDTCxNQUFNLEVBR04sTUFBTSxHQUNQLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxlQUFlLEVBQUUsZUFBZSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDbEUsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ25ELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUV6RCxPQUFPLEVBQUUsZUFBZSxFQUFFLEtBQUssRUFBYyxFQUFFLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ3ZFLE9BQU8sRUFBRSxNQUFNLEVBQUUsV0FBVyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsR0FBRyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDaEYsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDakUsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUVyQyxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUN2RCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSx5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QkFBcEIsS0FBSztnQkFLTyxLQUFLO3NCQUFqQixLQUFLO2dCQUlJLE1BQU07c0JBQWYsTUFBTTtnQkFFRyxPQUFPO3NCQUFoQixNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gIENvbXBvbmVudCxcbiAgRXZlbnRFbWl0dGVyLFxuICBJbnB1dCxcbiAgTmdab25lLFxuICBPbkRlc3Ryb3ksXG4gIE9uSW5pdCxcbiAgT3V0cHV0LFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEFsZXJ0Q29udHJvbGxlciwgTW9kYWxDb250cm9sbGVyIH0gZnJvbSAnQGlvbmljL2FuZ3VsYXInO1xuaW1wb3J0IHsgaXNDb2RlRXJyb3IgfSBmcm9tICdAaW90aXplL2NvbW1vbi9lcnJvcic7XG5pbXBvcnQgeyBUYXBDbGllbnRFcnJvciB9IGZyb20gJ0Bpb3RpemUvdGFwL2NsaWVudC9pbXBsJztcbmltcG9ydCB7IEVkaXRhYmxlVmFsdWVEYXRhU3RyZWFtSW50ZXJmYWNlIH0gZnJvbSAnQGlvdGl6ZS90YXAvZGF0YSc7XG5pbXBvcnQgeyBCZWhhdmlvclN1YmplY3QsIG1lcmdlLCBPYnNlcnZhYmxlLCBvZiwgU3ViamVjdCB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgZmlsdGVyLCBzaGFyZVJlcGxheSwgc3RhcnRXaXRoLCBzd2l0Y2hNYXAsIHRhcCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IEN1cnJlbnREZXZpY2VTZXJ2aWNlIH0gZnJvbSAnLi4vY3VycmVudC1kZXZpY2Uuc2VydmljZSc7XG5pbXBvcnQgeyBMaWJFcnJvciB9IGZyb20gJy4uL2Vycm9ycyc7XG5pbXBvcnQgeyBJbmxpbmVFZGl0b3JDb21wb25lbnQgfSBmcm9tICcuLi9pbmxpbmUtZWRpdG9yL2lubGluZS1lZGl0b3IuY29tcG9uZW50JztcbmltcG9ydCB7IGlzTmZjVGFwUmVxdWlyZWRFcnJvciB9IGZyb20gJy4uL25mYy91dGlsaXR5JztcbmltcG9ydCB7IFBlbmRpbmdDYWxsTWFuYWdlciB9IGZyb20gJy4uL3BlbmRpbmctY2FsbC1tYW5hZ2VyJztcbmltcG9ydCB7IFRhcFZhbHVlRWRpdG9yTW9kYWxDb21wb25lbnQgfSBmcm9tICcuLi90YXAtdmFsdWUtZWRpdG9yLW1vZGFsL3RhcC12YWx1ZS1lZGl0b3ItbW9kYWwuY29tcG9uZW50JztcblxuZnVuY3Rpb24gaXNUYXBOb3RDb25uZWN0ZWRFcnJvcihlcnI6IEVycm9yKSB7XG4gIHJldHVybiAoXG4gICAgaXNOZmNUYXBSZXF1aXJlZEVycm9yKGVycikgfHxcbiAgICBpc0NvZGVFcnJvcihUYXBDbGllbnRFcnJvci5Db2RlLk5vdENvbm5lY3RlZEVycm9yLCBlcnIpXG4gICk7XG59XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ3RhcC12YWx1ZS1lZGl0b3ItY29udGFpbmVyJyxcbiAgdGVtcGxhdGVVcmw6ICcuL3RhcC12YWx1ZS1lZGl0b3ItY29udGFpbmVyLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vdGFwLXZhbHVlLWVkaXRvci1jb250YWluZXIuY29tcG9uZW50LnNjc3MnXSxcbn0pXG5leHBvcnQgY2xhc3MgVGFwVmFsdWVFZGl0b3JDb250YWluZXJDb21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQsIE9uRGVzdHJveSB7XG4gIEBJbnB1dCgpIGJ1dHRvbjogYm9vbGVhbiA9IGZhbHNlO1xuXG4gIEBJbnB1dCgpIHNldCB2YXJpYWJsZShcbiAgICB2OiBFZGl0YWJsZVZhbHVlRGF0YVN0cmVhbUludGVyZmFjZTx1bmtub3duLCBhbnk+IHwgdW5kZWZpbmVkXG4gICkge1xuICAgIHRoaXMudmFyaWFibGVDaGFuZ2UubmV4dCh2KTtcbiAgfVxuXG4gIEBJbnB1dCgpIGlucHV0T3B0aW9uczogSW5saW5lRWRpdG9yQ29tcG9uZW50LklucHV0T3B0aW9ucyB8IGFueSA9IHtcbiAgICB0eXBlOiAndGV4dCcsXG4gIH07XG5cbiAgLyoqXG4gICAqIFNob3cgcmVmcmVzaCBidXR0b25cbiAgICovXG4gIEBJbnB1dCgpIHNob3dSZWZyZXNoQnV0dG9uID0gZmFsc2U7XG5cbiAgLyoqXG4gICAqIFNob3cgc3VibWl0IGJ1dHRvblxuICAgKi9cbiAgQElucHV0KCkgc2hvd1N1Ym1pdEJ1dHRvbiA9IGZhbHNlO1xuXG4gIC8qKlxuICAgKiBTaG93IGVkaXQgYnV0dG9uXG4gICAqL1xuICBASW5wdXQoKSBzaG93RWRpdEJ1dHRvbiA9IGZhbHNlO1xuXG4gIC8qKlxuICAgKiBFbmFibGUgZWRpdGlvbiBtb2RlXG4gICAqL1xuICBlZGl0TW9kZUNoYW5nZSA9IG5ldyBCZWhhdmlvclN1YmplY3Q8Ym9vbGVhbj4oZmFsc2UpO1xuXG4gIGxvYWRpbmdDaGFuZ2UgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+KGZhbHNlKTtcblxuICAvKipcbiAgICogRXJyb3IgdG8gZGlzcGxheVxuICAgKi9cbiAgQElucHV0KCkgZXJyb3I/OiBFcnJvcjtcblxuICBASW5wdXQoKSBsaW5lcyA9ICdub25lJztcblxuICBASW5wdXQoKSBtb2RhbEVkaXRpb246IGJvb2xlYW4gPSBmYWxzZTtcblxuICAvKipcbiAgICogRm9yY2UgdmFsdWUgdG8gZGlzcGxheVxuICAgKi9cbiAgQElucHV0KCkgc2V0IHZhbHVlKHY6IGFueSkge1xuICAgIHRoaXMuZm9yY2VWYWx1ZUNoYW5nZS5uZXh0KHYpO1xuICB9XG5cbiAgQE91dHB1dCgpIHN1Ym1pdCA9IG5ldyBFdmVudEVtaXR0ZXI8YW55PigpO1xuXG4gIEBPdXRwdXQoKSByZWZyZXNoID0gbmV3IEV2ZW50RW1pdHRlcjxhbnk+KCk7XG5cbiAgcGVuZGluZ1N1Ym1pdFZhbHVlPzogYW55O1xuXG4gIHZhbHVlVG9TdWJtaXQ/OiBhbnk7XG5cbiAgbGFzdFJlYWRWYWx1ZT86IGFueTtcblxuICBwcml2YXRlIHZhcmlhYmxlQ2hhbmdlID0gbmV3IEJlaGF2aW9yU3ViamVjdDxcbiAgICBFZGl0YWJsZVZhbHVlRGF0YVN0cmVhbUludGVyZmFjZTx1bmtub3duLCBhbnk+IHwgdW5kZWZpbmVkXG4gID4odW5kZWZpbmVkKTtcblxuICBwdWJsaWMgdmFyaWFibGVWYWx1ZXNTdHJlYW06IE9ic2VydmFibGU8YW55PiA9IHRoaXMudmFyaWFibGVDaGFuZ2UucGlwZShcbiAgICBmaWx0ZXIoKHYpID0+ICEhdiksXG4gICAgc3dpdGNoTWFwKCh2KSA9PiB2IS52YWx1ZXMpLFxuICAgIHRhcCgodikgPT4ge1xuICAgICAgdGhpcy5sYXN0UmVhZFZhbHVlID0gdjtcbiAgICB9KVxuICApO1xuXG4gIGZvcmNlVmFsdWVDaGFuZ2UgPSBuZXcgU3ViamVjdDxhbnk+KCk7XG5cbiAgdmFsdWVUb0Rpc3BsYXkgPSB0aGlzLmVkaXRNb2RlQ2hhbmdlLnBpcGUoXG4gICAgc3dpdGNoTWFwKChlZGl0TW9kZSkgPT4ge1xuICAgICAgaWYgKGVkaXRNb2RlIHx8IHRoaXMucGVuZGluZ1N1Ym1pdFZhbHVlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIG9mKHRoaXMudmFsdWVUb1N1Ym1pdCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCBsYXN0VmFsdWUgPSB0aGlzLnZhcmlhYmxlQ2hhbmdlLnZhbHVlPy52YWx1ZVNuYXBzaG90O1xuICAgICAgICByZXR1cm4gbWVyZ2UoXG4gICAgICAgICAgdGhpcy52YXJpYWJsZVZhbHVlc1N0cmVhbS5waXBlKHN0YXJ0V2l0aChsYXN0VmFsdWUpKSxcbiAgICAgICAgICB0aGlzLmZvcmNlVmFsdWVDaGFuZ2VcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9KSxcbiAgICBzaGFyZVJlcGxheSgxKVxuICApO1xuXG4gIHByaXZhdGUgcGVuZGluZ0NhbGxNYW5hZ2VyPzogUGVuZGluZ0NhbGxNYW5hZ2VyO1xuXG4gIHByaXZhdGUgZGVzdHJveWVkID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIGFsZXJ0RGlhbG9nOiBBbGVydENvbnRyb2xsZXIsXG4gICAgcHJpdmF0ZSB0YXBTZXJ2aWNlOiBDdXJyZW50RGV2aWNlU2VydmljZSxcbiAgICBwcml2YXRlIGNoYW5nZURldGVjdG9yUmVmOiBDaGFuZ2VEZXRlY3RvclJlZixcbiAgICBwcml2YXRlIG5nWm9uZTogTmdab25lLFxuICAgIHByaXZhdGUgbW9kYWxDb250cm9sbGVyOiBNb2RhbENvbnRyb2xsZXJcbiAgKSB7fVxuXG4gIG5nT25Jbml0KCkge1xuICAgIHRoaXMucGVuZGluZ0NhbGxNYW5hZ2VyID0gUGVuZGluZ0NhbGxNYW5hZ2VyLmNyZWF0ZSh0aGlzLnRhcFNlcnZpY2UpO1xuXG4gICAgdGhpcy5wZW5kaW5nQ2FsbE1hbmFnZXIucGVuZGluZ0NhbGxSZXN1bHQuc3Vic2NyaWJlKChlcnJvcikgPT4ge1xuICAgICAgdGhpcy5wZW5kaW5nU3VibWl0VmFsdWUgPSB1bmRlZmluZWQ7XG4gICAgICB0aGlzLnZhbHVlVG9TdWJtaXQgPSB1bmRlZmluZWQ7XG4gICAgICB0aGlzLmVkaXRNb2RlQ2hhbmdlLm5leHQoZmFsc2UpO1xuICAgICAgdGhpcy5lcnJvciA9IGVycm9yO1xuICAgICAgdGhpcy5jaGFuZ2VEZXRlY3RvclJlZi5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgfSk7XG4gIH1cblxuICAvLyBoYXNWYWx1ZUNoYW5nZWQodmFsdWU6IGFueSkge1xuICAvLyAgIHJldHVybiB0aGlzLl9sYXN0VmFsdWVGcm9tRGV2aWNlICE9PSB2YWx1ZTtcbiAgLy8gfVxuXG4gIHN1Ym1pdFZhbHVlKCkge1xuICAgIGlmICh0aGlzLnZhbHVlVG9TdWJtaXQgPT09IHVuZGVmaW5lZCkge1xuICAgICAgY29uc29sZS53YXJuKCdObyB2YWx1ZSB0byBzdWJtaXQgeWV0Jyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuX3N1Ym1pdFZhbHVlKHRoaXMudmFsdWVUb1N1Ym1pdCk7XG4gIH1cblxuICBhc3luYyBub3RpZnlWYWx1ZUNoYW5nZShuZXdWYWx1ZTogYW55LCBmb3JjZVdyaXRlID0gZmFsc2UpIHtcbiAgICBpZiAoZm9yY2VXcml0ZSB8fCB0aGlzLmxhc3RSZWFkVmFsdWUgIT09IG5ld1ZhbHVlKSB7XG4gICAgICBhd2FpdCB0aGlzLl9zdWJtaXRWYWx1ZShuZXdWYWx1ZSk7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgb3BlbkVycm9yRGlhbG9nKGVycm9yOiBFcnJvcikge1xuICAgIGNvbnN0IGRpYWxvZyA9IGF3YWl0IHRoaXMuYWxlcnREaWFsb2cuY3JlYXRlKHtcbiAgICAgIGhlYWRlcjogJ0Vycm9yJyxcbiAgICAgIG1lc3NhZ2U6IGVycm9yLm1lc3NhZ2UsXG4gICAgICBidXR0b25zOiBbJ09rJ10sXG4gICAgfSk7XG4gICAgYXdhaXQgZGlhbG9nLnByZXNlbnQoKTtcbiAgfVxuXG4gIGFzeW5jIGV4cGxhaW5XYWl0Rm9yU3VibWl0KCkge1xuICAgIGNvbnN0IGFsZXJ0RGlhbG9nID0gYXdhaXQgdGhpcy5hbGVydERpYWxvZy5jcmVhdGUoe1xuICAgICAgaGVhZGVyOiAnVmFsdWUgbm90IHNlbnQgeWV0JyxcbiAgICAgIG1lc3NhZ2U6ICdOZXcgdmFsdWUgd2lsbCBiZSBzZW50IHRvIHRoZSBkZXZpY2Ugb25jZSB5b3UgYXJlIHJlY29ubmVjdGVkJyxcbiAgICAgIGJ1dHRvbnM6IFsnT2snXSxcbiAgICB9KTtcbiAgICBhd2FpdCBhbGVydERpYWxvZy5wcmVzZW50KCk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIF9zdWJtaXRWYWx1ZShuZXdWYWx1ZTogYW55KSB7XG4gICAgdGhpcy5zdWJtaXQuZW1pdChuZXdWYWx1ZSk7XG4gICAgLy8gT0xEIFN5c3RlbVxuICAgIGNvbnN0IHZhcmlhYmxlID0gdGhpcy52YXJpYWJsZUNoYW5nZS52YWx1ZTtcbiAgICBpZiAoIXZhcmlhYmxlKSB7XG4gICAgICB0aGlzLmVycm9yID0gTGliRXJyb3IuY29tcG9uZW50QXJndW1lbnRSZXF1aXJlZChcbiAgICAgICAgdGhpcy5jb25zdHJ1Y3Rvci5uYW1lLFxuICAgICAgICAndmFyaWFibGUnXG4gICAgICApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAoIXRoaXMucGVuZGluZ0NhbGxNYW5hZ2VyKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnNvbGUubG9nKGBbJHt2YXJpYWJsZS5pZH1dYCwgJ1N1Ym1pdCB2YWx1ZScsIG5ld1ZhbHVlKTtcbiAgICBhd2FpdCB0aGlzLnN0YXJ0QWN0aW9uKFxuICAgICAgdGhpcy5wZW5kaW5nQ2FsbE1hbmFnZXJcbiAgICAgICAgLmV4ZWMoYXN5bmMgKCkgPT4ge1xuICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHZhcmlhYmxlLndyaXRlKG5ld1ZhbHVlKTtcbiAgICAgICAgICB0aGlzLmVkaXRNb2RlQ2hhbmdlLm5leHQoZmFsc2UpO1xuICAgICAgICAgIHRoaXMuZm9yY2VWYWx1ZUNoYW5nZS5uZXh0KG5ld1ZhbHVlKTtcbiAgICAgICAgICB0aGlzLmVycm9yID0gdW5kZWZpbmVkO1xuICAgICAgICAgIHRoaXMucGVuZGluZ1N1Ym1pdFZhbHVlID0gdW5kZWZpbmVkO1xuICAgICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgICAgIH0pXG4gICAgICAgIC5jYXRjaCgoZXJyOiBhbnkpID0+IHtcbiAgICAgICAgICBpZiAoIWlzVGFwTm90Q29ubmVjdGVkRXJyb3IoZXJyKSkge1xuICAgICAgICAgICAgY29uc29sZS53YXJuKFxuICAgICAgICAgICAgICBgWyR7dmFyaWFibGUuaWR9XWAsXG4gICAgICAgICAgICAgIGBvblN1Ym1pdCB2YWx1ZSBcIiR7bmV3VmFsdWV9XCIgZXJyb3JgLFxuICAgICAgICAgICAgICBlcnJcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICB0aGlzLmVycm9yID0gZXJyO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLnBlbmRpbmdTdWJtaXRWYWx1ZSA9IG5ld1ZhbHVlO1xuICAgICAgICAgICAgdGhpcy52YWx1ZVRvU3VibWl0ID0gbmV3VmFsdWU7XG4gICAgICAgICAgICB0aGlzLmVkaXRNb2RlQ2hhbmdlLm5leHQoZmFsc2UpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICApO1xuICB9XG5cbiAgcHVibGljIGFzeW5jIHJlZnJlc2hWYWx1ZSgpIHtcbiAgICB0aGlzLnJlZnJlc2gubmV4dCh1bmRlZmluZWQpO1xuICAgIGNvbnN0IHZhcmlhYmxlID0gdGhpcy52YXJpYWJsZUNoYW5nZS52YWx1ZTtcbiAgICBpZiAodmFyaWFibGUpIHtcbiAgICAgIGF3YWl0IHRoaXMuc3RhcnRBY3Rpb24oXG4gICAgICAgIHZhcmlhYmxlXG4gICAgICAgICAgLnJlYWQoKVxuICAgICAgICAgIC50aGVuKChuZXdWYWx1ZTogYW55KSA9PiB7XG4gICAgICAgICAgICB0aGlzLmZvcmNlVmFsdWVDaGFuZ2UubmV4dChuZXdWYWx1ZSk7XG4gICAgICAgICAgfSlcbiAgICAgICAgICAuY2F0Y2goKGVycjogYW55KSA9PiB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZyhlcnIpO1xuICAgICAgICAgIH0pXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGVuYWJsZUVkaXRNb2RlKCkge1xuICAgIHRoaXMubW9kYWxFZGl0aW9uID8gdGhpcy5zaG93RWRpdE1vZGFsKCkgOiB0aGlzLmVkaXRNb2RlQ2hhbmdlLm5leHQodHJ1ZSk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHNob3dFZGl0TW9kYWwoKSB7XG4gICAgY29uc3QgbGFzdFZhbHVlID0gdGhpcy52YXJpYWJsZUNoYW5nZS52YWx1ZT8udmFsdWVTbmFwc2hvdDtcblxuICAgIGNvbnN0IG1vZGFsID0gYXdhaXQgdGhpcy5tb2RhbENvbnRyb2xsZXIuY3JlYXRlKHtcbiAgICAgIGNvbXBvbmVudDogVGFwVmFsdWVFZGl0b3JNb2RhbENvbXBvbmVudCxcbiAgICAgIGNvbXBvbmVudFByb3BzOiB7XG4gICAgICAgIGlucHV0T3B0aW9uczogdGhpcy5pbnB1dE9wdGlvbnMsXG4gICAgICAgIHZhbHVlOiBsYXN0VmFsdWUsXG4gICAgICB9LFxuICAgIH0pO1xuICAgIGF3YWl0IG1vZGFsLnByZXNlbnQoKTtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBtb2RhbC5vbkRpZERpc21pc3MoKTtcbiAgICBpZiAocmVzdWx0KSB7XG4gICAgICBpZiAocmVzdWx0LnJvbGUgPT09ICdzdWJtaXQnKSB7XG4gICAgICAgIGNvbnN0IG5ld1ZhbHVlID0gcmVzdWx0LmRhdGEudmFsdWU7XG4gICAgICAgIGF3YWl0IHRoaXMubm90aWZ5VmFsdWVDaGFuZ2UobmV3VmFsdWUpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgc3RhcnRBY3Rpb24oYWN0aW9uczogUHJvbWlzZTxhbnk+KTogUHJvbWlzZTxhbnk+IHtcbiAgICB0aGlzLm5nWm9uZS5ydW4oYXN5bmMgKCkgPT4ge1xuICAgICAgdGhpcy5sb2FkaW5nQ2hhbmdlLm5leHQodHJ1ZSk7XG4gICAgICB0cnkge1xuICAgICAgICByZXR1cm4gYXdhaXQgYWN0aW9ucztcbiAgICAgIH0gY2F0Y2ggKGVycjogYW55KSB7XG4gICAgICAgIGNvbnNvbGUud2FybignYWN0aW9uIGZhaWxlZCcsIGVycik7XG4gICAgICB9IGZpbmFsbHkge1xuICAgICAgICB0aGlzLmxvYWRpbmdDaGFuZ2UubmV4dChmYWxzZSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICB0aGlzLmRlc3Ryb3llZC5uZXh0KCk7XG4gICAgdGhpcy5kZXN0cm95ZWQuY29tcGxldGUoKTtcbiAgICB0aGlzLnBlbmRpbmdDYWxsTWFuYWdlcj8uZGVzdHJveSgpO1xuICB9XG5cbiAgY2FuY2VsRWRpdCgpIHtcbiAgICB0aGlzLnBlbmRpbmdDYWxsTWFuYWdlcj8uY2FuY2VsKCk7XG4gICAgdGhpcy5lcnJvciA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLnBlbmRpbmdTdWJtaXRWYWx1ZSA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLnZhbHVlVG9TdWJtaXQgPSB1bmRlZmluZWQ7XG4gICAgdGhpcy5lZGl0TW9kZUNoYW5nZS5uZXh0KGZhbHNlKTtcbiAgfVxufVxuIiwiPGlvbi1pdGVtXG4gIHN0eWxlPVwicG9pbnRlci1ldmVudHM6IGF1dG9cIlxuICBbbGluZXNdPVwibGluZXNcIlxuICBbYnV0dG9uXT1cImJ1dHRvblwiXG4gIFtuZ0NsYXNzXT1cInsgJ2l0ZW0taW5wdXQnOiBlZGl0TW9kZUNoYW5nZS52YWx1ZSB9XCJcbj5cbiAgPG5nLWNvbnRlbnQ+PC9uZy1jb250ZW50PlxuICA8aW9uLWJ1dHRvbnNcbiAgICBzbG90PVwiZW5kXCJcbiAgICAqbmdJZj1cIlxuICAgICAgc2hvd0VkaXRCdXR0b24gfHxcbiAgICAgIHNob3dSZWZyZXNoQnV0dG9uIHx8XG4gICAgICBlcnJvciB8fFxuICAgICAgcGVuZGluZ1N1Ym1pdFZhbHVlICE9PSB1bmRlZmluZWRcbiAgICBcIlxuICA+XG4gICAgPGlvbi1idXR0b25cbiAgICAgIChjbGljayk9XCJleHBsYWluV2FpdEZvclN1Ym1pdCgpOyAkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcIlxuICAgICAgKm5nSWY9XCJwZW5kaW5nU3VibWl0VmFsdWUgIT09IHVuZGVmaW5lZFwiXG4gICAgICBbZGlzYWJsZWRdPVwiZmFsc2VcIlxuICAgICAgY29sb3I9XCJ3YXJuaW5nXCJcbiAgICA+XG4gICAgICA8aW9uLWljb24gbmFtZT1cImFsZXJ0LWNpcmNsZVwiPjwvaW9uLWljb24+XG4gICAgPC9pb24tYnV0dG9uPlxuICAgIDxpb24tYnV0dG9uXG4gICAgICAoY2xpY2spPVwiZW5hYmxlRWRpdE1vZGUoKTsgJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCJcbiAgICAgIGNsYXNzPVwiYnRuLWVuYWJsZS1lZGl0LW1vZGVcIlxuICAgICAgKm5nSWY9XCJcbiAgICAgICAgcGVuZGluZ1N1Ym1pdFZhbHVlID09PSB1bmRlZmluZWQgJiZcbiAgICAgICAgc2hvd0VkaXRCdXR0b24gJiZcbiAgICAgICAgIWVkaXRNb2RlQ2hhbmdlLnZhbHVlXG4gICAgICBcIlxuICAgID5cbiAgICAgIDxpb24taWNvbiBuYW1lPVwiY3JlYXRlXCI+PC9pb24taWNvbj5cbiAgICA8L2lvbi1idXR0b24+XG4gICAgPGlvbi1idXR0b25cbiAgICAgIChjbGljayk9XCJzdWJtaXRWYWx1ZSgpOyAkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcIlxuICAgICAgKm5nSWY9XCJcbiAgICAgICAgc2hvd1N1Ym1pdEJ1dHRvbiAmJlxuICAgICAgICBlZGl0TW9kZUNoYW5nZS52YWx1ZSAmJlxuICAgICAgICBwZW5kaW5nU3VibWl0VmFsdWUgPT09IHVuZGVmaW5lZFxuICAgICAgXCJcbiAgICAgIFtkaXNhYmxlZF09XCJsb2FkaW5nQ2hhbmdlIHwgYXN5bmNcIlxuICAgICAgY2xhc3M9XCJidG4tc3VibWl0LXZhbHVlXCJcbiAgICA+XG4gICAgICA8aW9uLWljb24gbmFtZT1cInNlbmRcIj48L2lvbi1pY29uPlxuICAgIDwvaW9uLWJ1dHRvbj5cbiAgICA8aW9uLWJ1dHRvblxuICAgICAgKGNsaWNrKT1cImNhbmNlbEVkaXQoKTsgJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCJcbiAgICAgICpuZ0lmPVwiZWRpdE1vZGVDaGFuZ2UudmFsdWUgfHwgcGVuZGluZ1N1Ym1pdFZhbHVlICE9PSB1bmRlZmluZWRcIlxuICAgICAgW2Rpc2FibGVkXT1cImxvYWRpbmdDaGFuZ2UgfCBhc3luY1wiXG4gICAgICBjbGFzcz1cImJ0bi1jbG9zZS1lZGl0LW1vZGVcIlxuICAgID5cbiAgICAgIDxpb24taWNvbiBuYW1lPVwiY2xvc2VcIj48L2lvbi1pY29uPlxuICAgIDwvaW9uLWJ1dHRvbj5cbiAgICA8aW9uLWJ1dHRvblxuICAgICAgKm5nSWY9XCJzaG93UmVmcmVzaEJ1dHRvblwiXG4gICAgICAoY2xpY2spPVwicmVmcmVzaFZhbHVlKCk7ICRldmVudC5zdG9wUHJvcGFnYXRpb24oKVwiXG4gICAgICBbZGlzYWJsZWRdPVwibG9hZGluZ0NoYW5nZSB8IGFzeW5jXCJcbiAgICAgIGNsYXNzPVwiYnRuLXJlZnJlc2gtdmFsdWVcIlxuICAgID5cbiAgICAgIDxpb24taWNvbiBuYW1lPVwic3luY1wiPjwvaW9uLWljb24+XG4gICAgPC9pb24tYnV0dG9uPlxuICAgIDxpb24tYnV0dG9uXG4gICAgICAqbmdJZj1cImVycm9yXCJcbiAgICAgIChjbGljayk9XCJvcGVuRXJyb3JEaWFsb2coZXJyb3IpOyAkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcIlxuICAgICAgY2xhc3M9XCJidG4tc2hvdy1lcnJvclwiXG4gICAgPlxuICAgICAgPGlvbi1pY29uIHNsb3Q9XCJlbmRcIiBuYW1lPVwiYWxlcnQtY2lyY2xlXCIgY29sb3I9XCJkYW5nZXJcIj48L2lvbi1pY29uPlxuICAgIDwvaW9uLWJ1dHRvbj5cbiAgPC9pb24tYnV0dG9ucz5cbiAgPGlvbi1zcGlubmVyICpuZ0lmPVwibG9hZGluZ0NoYW5nZSB8IGFzeW5jXCIgc2xvdD1cImVuZFwiPjwvaW9uLXNwaW5uZXI+XG48L2lvbi1pdGVtPlxuIl19