UNPKG

ngx-custom-form-error

Version:

This library makes showing errors in angular forms easier.

143 lines 28.5 kB
import { ChangeDetectionStrategy, Component, ContentChild, ElementRef, Inject, Input, Optional } from '@angular/core'; import { FormControlName } from '@angular/forms'; import { combineLatest, Observable, of, Subject } from 'rxjs'; import { distinctUntilChanged, map, switchMap, tap, takeUntil } from 'rxjs/operators'; import { CustomFormControlLabelDirective } from '../custom-form-label.directive'; import { hasTwoObjectsSameProps } from '../helper'; import { CUSTOM_FORM_CONFIG } from '../injection-token'; import * as i0 from "@angular/core"; import * as i1 from "@angular/common"; export class NgxCustomFormErrorComponent { constructor(config) { this.config = config; this._destroy$ = new Subject(); this.defaultConfig = { errorClass: 'c-control-error', errorTextColor: '#ee3e3e', addErrorClassToElement: true, onTouchedOnly: false }; } ngAfterContentInit() { this.init(); this.errors$ = this.getErrors(); } init() { this.onTouchedOnly = this.onTouchedOnly ?? this.config?.onTouchedOnly ?? this.defaultConfig.onTouchedOnly; this.addErrorClassToElement = this.addErrorClassToElement ?? this.config?.addErrorClassToElement ?? this.defaultConfig.addErrorClassToElement; this.errorTextColor = this.errorTextColor ?? this.config?.errorTextColor ?? this.defaultConfig.errorTextColor; this.errorClass = this.config?.errorClass ?? this.defaultConfig.errorClass; this.label = this.label ?? this.labelRef?.el.nativeElement.innerText ?? undefined; this.initmessages(); } /*** We compose messages object from user inputs and global config here, which will use to show the error */ initmessages() { // We are checking for undefined because input property can be null if user don't want to show error that is configured // in the global config. this.messages = { required: this.required !== undefined ? this.required : this.config?.required, min: this.min !== undefined ? this.min : this.config?.min, max: this.max !== undefined ? this.max : this.config?.max, minlength: this.minlength !== undefined ? this.minlength : this.config?.minLength, maxlength: this.maxlength !== undefined ? this.maxlength : this.config?.maxLength, email: this.email !== undefined ? this.email : this.config?.email, pattern: this.pattern !== undefined ? this.pattern : this.config?.pattern }; } getErrors() { const touched$ = new Observable((subscribe => { // If onTouchedOnly is `true` the observable emits `true` only after the element is touched if (this.onTouchedOnly) { // Thsee are use to trigger if the input is marked as touched this.formControl.valueAccessor?.registerOnTouched(() => subscribe.next(true)); this.formControl.valueChanges?.pipe(takeUntil(this._destroy$)).subscribe(() => this.formControl.touched ? subscribe.next(true) : null); } else { // If onTouchedOnly is `false` the observable emits `true` so as to start looking for error rightaway subscribe.next(true); } })).pipe(distinctUntilChanged()); return combineLatest([touched$, this.formControl.statusChanges]).pipe(switchMap(() => of(this.formControl.errors)), tap(errors => { if (!this.addErrorClassToElement) return; // if config has addErrorClassToElement set to true, we set the errorClass to the element thas has `formControlName` directive. if (errors) { this.controlElement.nativeElement.classList.add(this.errorClass); } else { this.controlElement.nativeElement.classList.remove(this.errorClass); } }), // This observable will emit everytime we input on the element. And a simple distinctUntilChanged will not work // since it will emit either `null` or `error object` with same properties. But two objects cannot be same // so I have used `hasTwoObjectsSameProps` to stop emitting if the error object has same properties (meaning it is same ) as previous one. distinctUntilChanged((x, y) => hasTwoObjectsSameProps(x, y)), map((errorObj) => { if (!errorObj) return []; let errors = Object.keys(errorObj).map(key => { let errorKey = key; if (!this.messages[errorKey]) return; if (typeof this.messages[errorKey] == 'string') { return this.messages[errorKey]; } else { let errorFn = this.messages[errorKey]; return errorFn(this.label, errorObj[errorKey]); } }); // This to eliminate array of undefined and nulls return errors.filter(error => error); })); } ngOnDestroy() { this._destroy$.next(); } } NgxCustomFormErrorComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: NgxCustomFormErrorComponent, deps: [{ token: CUSTOM_FORM_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Component }); NgxCustomFormErrorComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.2.7", type: NgxCustomFormErrorComponent, selector: "c-form-error", inputs: { required: "required", maxlength: ["maxLength", "maxlength"], minlength: ["minLength", "minlength"], min: "min", max: "max", email: "email", pattern: "pattern", onTouchedOnly: "onTouchedOnly", addErrorClassToElement: "addErrorClassToElement", errorTextColor: "errorTextColor", maxLengthCount: "maxLengthCount", label: "label" }, queries: [{ propertyName: "formControl", first: true, predicate: FormControlName, descendants: true }, { propertyName: "controlElement", first: true, predicate: FormControlName, descendants: true, read: ElementRef }, { propertyName: "labelRef", first: true, predicate: CustomFormControlLabelDirective, descendants: true }], ngImport: i0, template: "<ng-content></ng-content>\r\n<div class=\"c-additions\">\r\n <div class=\"c-error-container\" *ngIf=\"(errors$ | async) as errors;else emptyDiv\">\r\n <ng-container *ngIf=\"errors.length\">\r\n <ng-container *ngFor=\"let error of errors\">\r\n <div [style.color]=\"errorTextColor\" class=\"c-error\">\r\n {{error}}\r\n </div>\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n <div *ngIf=\"formControl && maxLengthCount\" class=\"c-counter\">\r\n <span class=\"c-counter-left-bracket\">[</span>\r\n <span class=\"c-counter-first-elem\">\r\n {{(formControl.valueChanges | async)?.length || formControl.value?.length || 0}}\r\n </span>\r\n <span class=\"c-counter-divider\">\r\n /\r\n </span>\r\n <span class=\"c-counter-last-elem\">\r\n {{maxLengthCount}}\r\n </span>\r\n <span class=\"c-counter-right-bracket\">]</span>\r\n\r\n </div>\r\n</div>\r\n<ng-template #emptyDiv>\r\n <div></div>\r\n</ng-template>", styles: [".c-additions{display:flex;gap:1rem;justify-content:space-between}.c-additions .c-error-container{display:flex;flex-direction:column;gap:.5rem;padding:0 3px}.c-additions .c-counter{min-width:-moz-fit-content;min-width:fit-content;width:-moz-fit-content;width:fit-content;text-align:end;margin-top:.3rem}.c-additions .c-error:first-child{margin-top:.3rem}\n"], directives: [{ type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], pipes: { "async": i1.AsyncPipe }, changeDetection: i0.ChangeDetectionStrategy.OnPush }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.2.7", ngImport: i0, type: NgxCustomFormErrorComponent, decorators: [{ type: Component, args: [{ selector: 'c-form-error', changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>\r\n<div class=\"c-additions\">\r\n <div class=\"c-error-container\" *ngIf=\"(errors$ | async) as errors;else emptyDiv\">\r\n <ng-container *ngIf=\"errors.length\">\r\n <ng-container *ngFor=\"let error of errors\">\r\n <div [style.color]=\"errorTextColor\" class=\"c-error\">\r\n {{error}}\r\n </div>\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n <div *ngIf=\"formControl && maxLengthCount\" class=\"c-counter\">\r\n <span class=\"c-counter-left-bracket\">[</span>\r\n <span class=\"c-counter-first-elem\">\r\n {{(formControl.valueChanges | async)?.length || formControl.value?.length || 0}}\r\n </span>\r\n <span class=\"c-counter-divider\">\r\n /\r\n </span>\r\n <span class=\"c-counter-last-elem\">\r\n {{maxLengthCount}}\r\n </span>\r\n <span class=\"c-counter-right-bracket\">]</span>\r\n\r\n </div>\r\n</div>\r\n<ng-template #emptyDiv>\r\n <div></div>\r\n</ng-template>", styles: [".c-additions{display:flex;gap:1rem;justify-content:space-between}.c-additions .c-error-container{display:flex;flex-direction:column;gap:.5rem;padding:0 3px}.c-additions .c-counter{min-width:-moz-fit-content;min-width:fit-content;width:-moz-fit-content;width:fit-content;text-align:end;margin-top:.3rem}.c-additions .c-error:first-child{margin-top:.3rem}\n"] }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Inject, args: [CUSTOM_FORM_CONFIG] }, { type: Optional }] }]; }, propDecorators: { formControl: [{ type: ContentChild, args: [FormControlName] }], controlElement: [{ type: ContentChild, args: [FormControlName, { read: ElementRef }] }], labelRef: [{ type: ContentChild, args: [CustomFormControlLabelDirective] }], required: [{ type: Input }], maxlength: [{ type: Input, args: ['maxLength'] }], minlength: [{ type: Input, args: ['minLength'] }], min: [{ type: Input }], max: [{ type: Input }], email: [{ type: Input }], pattern: [{ type: Input }], onTouchedOnly: [{ type: Input }], addErrorClassToElement: [{ type: Input }], errorTextColor: [{ type: Input }], maxLengthCount: [{ type: Input }], label: [{ type: Input }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LWN1c3RvbS1mb3JtLWVycm9yLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL25neC1jdXN0b20tZm9ybS1lcnJvci9zcmMvbGliL2NvbXBvbmVudC9uZ3gtY3VzdG9tLWZvcm0tZXJyb3IuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LWN1c3RvbS1mb3JtLWVycm9yL3NyYy9saWIvY29tcG9uZW50L25neC1jdXN0b20tZm9ybS1lcnJvci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDdEgsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ2pELE9BQU8sRUFBRSxhQUFhLEVBQUUsVUFBVSxFQUFFLEVBQUUsRUFBRSxPQUFPLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDOUQsT0FBTyxFQUFFLG9CQUFvQixFQUFFLEdBQUcsRUFBRSxTQUFTLEVBQUUsR0FBRyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ3RGLE9BQU8sRUFBRSwrQkFBK0IsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ2pGLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUNuRCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQzs7O0FBUXhELE1BQU0sT0FBTywyQkFBMkI7SUFVdEMsWUFBMkQsTUFBb0I7UUFBcEIsV0FBTSxHQUFOLE1BQU0sQ0FBYztRQVR2RSxjQUFTLEdBQUcsSUFBSSxPQUFPLEVBQVEsQ0FBQztRQUVoQyxrQkFBYSxHQUFHO1lBQ3RCLFVBQVUsRUFBRSxpQkFBaUI7WUFDN0IsY0FB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QUEyQjtrQkFOdkMsU0FBUzsrQkFDRSxjQUFjLG1CQUdQLHVCQUF1QixDQUFDLE1BQU07OzBCQVlsQyxNQUFNOzJCQUFDLGtCQUFrQjs7MEJBQUcsUUFBUTs0Q0FFbEIsV0FBVztzQkFBekMsWUFBWTt1QkFBQyxlQUFlO2dCQUV3QixjQUFjO3NCQUFsRSxZQUFZO3VCQUFDLGVBQWUsRUFBRSxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUU7Z0JBRUosUUFBUTtzQkFBdEQsWUFBWTt1QkFBQywrQkFBK0I7Z0JBRXBDLFFBQVE7c0JBQWhCLEtBQUs7Z0JBQ2MsU0FBUztzQkFBNUIsS0FBSzt1QkFBQyxXQUFXO2dCQUNFLFNBQVM7c0JBQTVCLEtBQUs7dUJBQUMsV0FBVztnQkFDVCxHQUFHO3NCQUFYLEtBQUs7Z0JBQ0csR0FBRztzQkFBWCxLQUFLO2dCQUNHLEtBQUs7c0JBQWIsS0FBSztnQkFDRyxPQUFPO3NCQUFmLEtBQUs7Z0JBR0csYUFBYTtzQkFBckIsS0FBSztnQkFFRyxzQkFBc0I7c0JBQTlCLEtBQUs7Z0JBRUcsY0FBYztzQkFBdEIsS0FBSztnQkFFRyxjQUFjO3NCQUF0QixLQUFLO2dCQUVHLEtBQUs7c0JBQWIsS0FBSyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENoYW5nZURldGVjdGlvblN0cmF0ZWd5LCBDb21wb25lbnQsIENvbnRlbnRDaGlsZCwgRWxlbWVudFJlZiwgSW5qZWN0LCBJbnB1dCwgT3B0aW9uYWwgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEZvcm1Db250cm9sTmFtZSB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcbmltcG9ydCB7IGNvbWJpbmVMYXRlc3QsIE9ic2VydmFibGUsIG9mLCBTdWJqZWN0IH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBkaXN0aW5jdFVudGlsQ2hhbmdlZCwgbWFwLCBzd2l0Y2hNYXAsIHRhcCwgdGFrZVVudGlsIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgQ3VzdG9tRm9ybUNvbnRyb2xMYWJlbERpcmVjdGl2ZSB9IGZyb20gJy4uL2N1c3RvbS1mb3JtLWxhYmVsLmRpcmVjdGl2ZSc7XG5pbXBvcnQgeyBoYXNUd29PYmplY3RzU2FtZVByb3BzIH0gZnJvbSAnLi4vaGVscGVyJztcbmltcG9ydCB7IENVU1RPTV9GT1JNX0NPTkZJRyB9IGZyb20gJy4uL2luamVjdGlvbi10b2tlbic7XG5pbXBvcnQgeyBJRXJyb3IsIElFcnJvckNvbmZpZyB9IGZyb20gJy4uL25neC1jdXN0b20tZm9ybS1lcnJvci5tb2RlbCc7XG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdjLWZvcm0tZXJyb3InLFxuICB0ZW1wbGF0ZVVybDogJ25neC1jdXN0b20tZm9ybS1lcnJvci5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWyduZ3gtY3VzdG9tLWZvcm0tZXJyb3IuY29tcG9uZW50LnNjc3MnXSxcbiAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2hcbn0pXG5leHBvcnQgY2xhc3MgTmd4Q3VzdG9tRm9ybUVycm9yQ29tcG9uZW50IHtcbiAgcHJpdmF0ZSBfZGVzdHJveSQgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG4gIHByaXZhdGUgZGVmYXVsdENvbmZpZyA9IHtcbiAgICBlcnJvckNsYXNzOiAnYy1jb250cm9sLWVycm9yJyxcbiAgICBlcnJvclRleHRDb2xvcjogJyNlZTNlM2UnLFxuICAgIGFkZEVycm9yQ2xhc3NUb0VsZW1lbnQ6IHRydWUsXG4gICAgb25Ub3VjaGVkT25seTogZmFsc2VcbiAgfTtcblxuICBjb25zdHJ1Y3RvcihASW5qZWN0KENVU1RPTV9GT1JNX0NPTkZJRykgQE9wdGlvbmFsKCkgcHVibGljIGNvbmZpZzogSUVycm9yQ29uZmlnKSB7IH1cblxuICBAQ29udGVudENoaWxkKEZvcm1Db250cm9sTmFtZSkgZm9ybUNvbnRyb2whOiBGb3JtQ29udHJvbE5hbWU7XG4gIC8qKiBUaGlzIGlucHV0IGBjb250cm9sRWxlbWVudGAgaXMganVzdCBmb3IgYWRkaW5nIGVycm9yQ2xhc3MgdG8gdGhlIG5hdGl2ZSBlbGVtZW50IGlmIGNvbmZpZyBoYXMgYWRkRXJyb3JDbGFzc1RvRWxlbWVudCBzZXQgdG8gdHJ1ZSovXG4gIEBDb250ZW50Q2hpbGQoRm9ybUNvbnRyb2xOYW1lLCB7IHJlYWQ6IEVsZW1lbnRSZWYgfSkgY29udHJvbEVsZW1lbnQhOiBFbGVtZW50UmVmO1xuICAvKiogVGhpcyBpbnB1dCBsYWJlbFJlZiwgaXMgdXNlZCB0byBncmFiIGxhYmVsIGZyb20gaW5uZXJUZXh0IG9mIHRoZSBlbGVtZW50IHRoYXQgaGFzIGBjTGFiZWxgIGRpcmVjdGl2ZS4gKi9cbiAgQENvbnRlbnRDaGlsZChDdXN0b21Gb3JtQ29udHJvbExhYmVsRGlyZWN0aXZlKSBsYWJlbFJlZiE6IEN1c3RvbUZvcm1Db250cm9sTGFiZWxEaXJlY3RpdmU7XG5cbiAgQElucHV0KCkgcmVxdWlyZWQ/OiBzdHJpbmcgfCBudWxsO1xuICBASW5wdXQoJ21heExlbmd0aCcpIG1heGxlbmd0aD86IHN0cmluZyB8IG51bGw7XG4gIEBJbnB1dCgnbWluTGVuZ3RoJykgbWlubGVuZ3RoPzogc3RyaW5nIHwgbnVsbDtcbiAgQElucHV0KCkgbWluPzogc3RyaW5nIHwgbnVsbDtcbiAgQElucHV0KCkgbWF4Pzogc3RyaW5nIHwgbnVsbDtcbiAgQElucHV0KCkgZW1haWw/OiBzdHJpbmcgfCBudWxsO1xuICBASW5wdXQoKSBwYXR0ZXJuPzogc3RyaW5nIHwgbnVsbDtcblxuICAvKiogSWYgb25Ub3VjaGVkT25seSBmbGFnIGlzIG9uLCB3ZSBvbmx5IHNob3cgZXJyb3JzIGFmdGVyIHRoZSBmb3JtIGlzIHRvdWNoZWQgYW5kIGhhcyBlcnJvcnMgKi9cbiAgQElucHV0KCkgb25Ub3VjaGVkT25seSE6IGJvb2xlYW47XG4gIC8qKiBJdCBhZGRzIGVycm9yIGNsYXNzIHRvIHRoZSBmb3JtLWNvbnRyb2wgZWxlbWVudCB3aGljaCB5b3UgY2FuIHVzZSB0byBzdHlsZSB0aGUgZWxlbWVudCAqL1xuICBASW5wdXQoKSBhZGRFcnJvckNsYXNzVG9FbGVtZW50ITogYm9vbGVhbjtcbiAgLyoqIENvbG9yIG9mIHRoZSBlcnJvciBtZXNzYWdlICovXG4gIEBJbnB1dCgpIGVycm9yVGV4dENvbG9yITogc3RyaW5nO1xuICAvKipNYXggTGVuZ3RoIGNvdW50IGlzIHVzZWQgdG8gc2hvdyByZW1haW5pbmcgbGV0dGVycyBpbiByaWdodCBoYW5kIHNpZGUgb2YgZm9ybSBlcnJvciBhcmVhIGVnLiBbNSAvIDEwXSAqL1xuICBASW5wdXQoKSBtYXhMZW5ndGhDb3VudCE6IG51bWJlcjtcbiAgLyoqIGBsYWJlbGAgaW5wdXQgY2FuIGdpdmUgbGFiZWwgZm9yIHRoZSBmb3JtIGVycm9yIGFzIGlucHV0IHByb3BlcnR5IGluIGNhc2UgYGNMYWJlbGAgZGlyZWN0aXZlIGlzIG5vdCB1c2VkLiAgKi9cbiAgQElucHV0KCkgbGFiZWw/OiBzdHJpbmcgfCBudWxsO1xuXG4gIGVycm9ycyQhOiBPYnNlcnZhYmxlPG51bGwgfCBzdHJpbmdbXT47XG5cbiAgcHJpdmF0ZSBtZXNzYWdlcyE6IElFcnJvcjtcbiAgcHJpdmF0ZSBlcnJvckNsYXNzITogc3RyaW5nO1xuXG4gIG5nQWZ0ZXJDb250ZW50SW5pdCgpIHtcbiAgICB0aGlzLmluaXQoKTtcbiAgICB0aGlzLmVycm9ycyQgPSB0aGlzLmdldEVycm9ycygpO1xuICB9XG5cbiAgaW5pdCgpIHtcbiAgICB0aGlzLm9uVG91Y2hlZE9ubHkgPSB0aGlzLm9uVG91Y2hlZE9ubHkgPz8gdGhpcy5jb25maWc/Lm9uVG91Y2hlZE9ubHkgPz8gdGhpcy5kZWZhdWx0Q29uZmlnLm9uVG91Y2hlZE9ubHk7XG4gICAgdGhpcy5hZGRFcnJvckNsYXNzVG9FbGVtZW50ID0gdGhpcy5hZGRFcnJvckNsYXNzVG9FbGVtZW50ID8/IHRoaXMuY29uZmlnPy5hZGRFcnJvckNsYXNzVG9FbGVtZW50ID8/IHRoaXMuZGVmYXVsdENvbmZpZy5hZGRFcnJvckNsYXNzVG9FbGVtZW50O1xuICAgIHRoaXMuZXJyb3JUZXh0Q29sb3IgPSB0aGlzLmVycm9yVGV4dENvbG9yID8/IHRoaXMuY29uZmlnPy5lcnJvclRleHRDb2xvciA/PyB0aGlzLmRlZmF1bHRDb25maWcuZXJyb3JUZXh0Q29sb3I7XG4gICAgdGhpcy5lcnJvckNsYXNzID0gdGhpcy5jb25maWc/LmVycm9yQ2xhc3MgPz8gdGhpcy5kZWZhdWx0Q29uZmlnLmVycm9yQ2xhc3M7XG4gICAgdGhpcy5sYWJlbCA9IHRoaXMubGFiZWwgPz8gdGhpcy5sYWJlbFJlZj8uZWwubmF0aXZlRWxlbWVudC5pbm5lclRleHQgPz8gdW5kZWZpbmVkO1xuICAgIHRoaXMuaW5pdG1lc3NhZ2VzKCk7XG4gIH1cblxuICAvKioqIFdlIGNvbXBvc2UgbWVzc2FnZXMgb2JqZWN0IGZyb20gdXNlciBpbnB1dHMgYW5kIGdsb2JhbCBjb25maWcgaGVyZSwgd2hpY2ggd2lsbCB1c2UgdG8gc2hvdyB0aGUgZXJyb3IgKi9cbiAgaW5pdG1lc3NhZ2VzKCkge1xuICAgIC8vIFdlIGFyZSBjaGVja2luZyBmb3IgdW5kZWZpbmVkIGJlY2F1c2UgaW5wdXQgcHJvcGVydHkgY2FuIGJlIG51bGwgaWYgdXNlciBkb24ndCB3YW50IHRvIHNob3cgZXJyb3IgdGhhdCBpcyBjb25maWd1cmVkIFxuICAgIC8vIGluIHRoZSBnbG9iYWwgY29uZmlnLlxuICAgIHRoaXMubWVzc2FnZXMgPSB7XG4gICAgICByZXF1aXJlZDogdGhpcy5yZXF1aXJlZCAhPT0gdW5kZWZpbmVkID8gdGhpcy5yZXF1aXJlZCA6IHRoaXMuY29uZmlnPy5yZXF1aXJlZCxcbiAgICAgIG1pbjogdGhpcy5taW4gIT09IHVuZGVmaW5lZCA/IHRoaXMubWluIDogdGhpcy5jb25maWc/Lm1pbixcbiAgICAgIG1heDogdGhpcy5tYXggIT09IHVuZGVmaW5lZCA/IHRoaXMubWF4IDogdGhpcy5jb25maWc/Lm1heCxcbiAgICAgIG1pbmxlbmd0aDogdGhpcy5taW5sZW5ndGggIT09IHVuZGVmaW5lZCA/IHRoaXMubWlubGVuZ3RoIDogdGhpcy5jb25maWc/Lm1pbkxlbmd0aCxcbiAgICAgIG1heGxlbmd0aDogdGhpcy5tYXhsZW5ndGggIT09IHVuZGVmaW5lZCA/IHRoaXMubWF4bGVuZ3RoIDogdGhpcy5jb25maWc/Lm1heExlbmd0aCxcbiAgICAgIGVtYWlsOiB0aGlzLmVtYWlsICE9PSB1bmRlZmluZWQgPyB0aGlzLmVtYWlsIDogdGhpcy5jb25maWc/LmVtYWlsLFxuICAgICAgcGF0dGVybjogdGhpcy5wYXR0ZXJuICE9PSB1bmRlZmluZWQgPyB0aGlzLnBhdHRlcm4gOiB0aGlzLmNvbmZpZz8ucGF0dGVyblxuICAgIH07XG4gIH1cblxuICBnZXRFcnJvcnMoKSB7XG4gICAgY29uc3QgdG91Y2hlZCQgPSBuZXcgT2JzZXJ2YWJsZTxib29sZWFuPigoc3Vic2NyaWJlID0+IHtcbiAgICAgIC8vIElmIG9uVG91Y2hlZE9ubHkgaXMgYHRydWVgIHRoZSBvYnNlcnZhYmxlIGVtaXRzIGB0cnVlYCBvbmx5IGFmdGVyIHRoZSBlbGVtZW50IGlzIHRvdWNoZWRcbiAgICAgIGlmICh0aGlzLm9uVG91Y2hlZE9ubHkpIHtcbiAgICAgICAgLy8gVGhzZWUgYXJlIHVzZSB0byB0cmlnZ2VyIGlmIHRoZSBpbnB1dCBpcyBtYXJrZWQgYXMgdG91Y2hlZFxuICAgICAgICB0aGlzLmZvcm1Db250cm9sLnZhbHVlQWNjZXNzb3I/LnJlZ2lzdGVyT25Ub3VjaGVkKCgpID0+IHN1YnNjcmliZS5uZXh0KHRydWUpKTtcbiAgICAgICAgdGhpcy5mb3JtQ29udHJvbC52YWx1ZUNoYW5nZXM/LnBpcGUodGFrZVVudGlsKHRoaXMuX2Rlc3Ryb3kkKSkuc3Vic2NyaWJlKCgpID0+IHRoaXMuZm9ybUNvbnRyb2wudG91Y2hlZCA/IHN1YnNjcmliZS5uZXh0KHRydWUpIDogbnVsbCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBJZiBvblRvdWNoZWRPbmx5IGlzIGBmYWxzZWAgdGhlIG9ic2VydmFibGUgZW1pdHMgYHRydWVgIHNvIGFzIHRvIHN0YXJ0IGxvb2tpbmcgZm9yIGVycm9yIHJpZ2h0YXdheVxuICAgICAgICBzdWJzY3JpYmUubmV4dCh0cnVlKTtcbiAgICAgIH1cbiAgICB9KSkucGlwZShkaXN0aW5jdFVudGlsQ2hhbmdlZCgpKTtcblxuICAgIHJldHVybiBjb21iaW5lTGF0ZXN0KFt0b3VjaGVkJCwgdGhpcy5mb3JtQ29udHJvbC5zdGF0dXNDaGFuZ2VzIV0pLnBpcGUoXG4gICAgICBzd2l0Y2hNYXAoKCkgPT4gb2YodGhpcy5mb3JtQ29udHJvbC5lcnJvcnMpKSxcbiAgICAgIHRhcChlcnJvcnMgPT4ge1xuICAgICAgICBpZiAoIXRoaXMuYWRkRXJyb3JDbGFzc1RvRWxlbWVudCkgcmV0dXJuO1xuICAgICAgICAvLyBpZiBjb25maWcgaGFzIGFkZEVycm9yQ2xhc3NUb0VsZW1lbnQgc2V0IHRvIHRydWUsIHdlIHNldCB0aGUgZXJyb3JDbGFzcyB0byB0aGUgZWxlbWVudCB0aGFzIGhhcyBgZm9ybUNvbnRyb2xOYW1lYCBkaXJlY3RpdmUuXG4gICAgICAgIGlmIChlcnJvcnMpIHtcbiAgICAgICAgICB0aGlzLmNvbnRyb2xFbGVtZW50Lm5hdGl2ZUVsZW1lbnQuY2xhc3NMaXN0LmFkZCh0aGlzLmVycm9yQ2xhc3MpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRoaXMuY29udHJvbEVsZW1lbnQubmF0aXZlRWxlbWVudC5jbGFzc0xpc3QucmVtb3ZlKHRoaXMuZXJyb3JDbGFzcyk7XG4gICAgICAgIH1cbiAgICAgIH0pLFxuICAgICAgLy8gVGhpcyBvYnNlcnZhYmxlIHdpbGwgZW1pdCBldmVyeXRpbWUgd2UgaW5wdXQgb24gdGhlIGVsZW1lbnQuIEFuZCBhIHNpbXBsZSBkaXN0aW5jdFVudGlsQ2hhbmdlZCB3aWxsIG5vdCB3b3JrXG4gICAgICAvLyBzaW5jZSBpdCB3aWxsIGVtaXQgZWl0aGVyIGBudWxsYCBvciBgZXJyb3Igb2JqZWN0YCB3aXRoIHNhbWUgcHJvcGVydGllcy4gQnV0IHR3byBvYmplY3RzIGNhbm5vdCBiZSBzYW1lXG4gICAgICAvLyBzbyBJIGhhdmUgdXNlZCBgaGFzVHdvT2JqZWN0c1NhbWVQcm9wc2AgdG8gc3RvcCBlbWl0dGluZyBpZiB0aGUgZXJyb3Igb2JqZWN0IGhhcyBzYW1lIHByb3BlcnRpZXMgKG1lYW5pbmcgaXQgaXMgc2FtZSApIGFzIHByZXZpb3VzIG9uZS5cbiAgICAgIGRpc3RpbmN0VW50aWxDaGFuZ2VkKCh4LCB5KSA9PiBoYXNUd29PYmplY3RzU2FtZVByb3BzKHggYXMgT2JqZWN0LCB5IGFzIE9iamVjdCkpLFxuICAgICAgbWFwKChlcnJvck9iajogYW55KSA9PiB7XG5cbiAgICAgICAgaWYgKCFlcnJvck9iaikgcmV0dXJuIFtdO1xuXG4gICAgICAgIGxldCBlcnJvcnMgPSBPYmplY3Qua2V5cyhlcnJvck9iaikubWFwKGtleSA9PiB7XG5cbiAgICAgICAgICBsZXQgZXJyb3JLZXkgPSBrZXkgYXMga2V5b2YgSUVycm9yO1xuXG4gICAgICAgICAgaWYgKCF0aGlzLm1lc3NhZ2VzW2Vycm9yS2V5XSkgcmV0dXJuO1xuXG4gICAgICAgICAgaWYgKHR5cGVvZiB0aGlzLm1lc3NhZ2VzW2Vycm9yS2V5XSA9PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMubWVzc2FnZXNbZXJyb3JLZXldO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBsZXQgZXJyb3JGbiA9IHRoaXMubWVzc2FnZXNbZXJyb3JLZXldIGFzIEZ1bmN0aW9uO1xuICAgICAgICAgICAgcmV0dXJuIGVycm9yRm4odGhpcy5sYWJlbCwgZXJyb3JPYmpbZXJyb3JLZXldKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgfSk7XG4gICAgICAgIC8vIFRoaXMgdG8gZWxpbWluYXRlIGFycmF5IG9mIHVuZGVmaW5lZCBhbmQgbnVsbHNcbiAgICAgICAgcmV0dXJuIGVycm9ycy5maWx0ZXIoZXJyb3IgPT4gZXJyb3IpO1xuICAgICAgfSlcbiAgICApIGFzIE9ic2VydmFibGU8c3RyaW5nW10+O1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKSB7XG4gICAgdGhpcy5fZGVzdHJveSQubmV4dCgpO1xuICB9XG5cbn1cbiIsIjxuZy1jb250ZW50PjwvbmctY29udGVudD5cclxuPGRpdiBjbGFzcz1cImMtYWRkaXRpb25zXCI+XHJcbiAgICA8ZGl2IGNsYXNzPVwiYy1lcnJvci1jb250YWluZXJcIiAqbmdJZj1cIihlcnJvcnMkIHwgYXN5bmMpIGFzIGVycm9ycztlbHNlIGVtcHR5RGl2XCI+XHJcbiAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cImVycm9ycy5sZW5ndGhcIj5cclxuICAgICAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdGb3I9XCJsZXQgZXJyb3Igb2YgZXJyb3JzXCI+XHJcbiAgICAgICAgICAgICAgICA8ZGl2IFtzdHlsZS5jb2xvcl09XCJlcnJvclRleHRDb2xvclwiIGNsYXNzPVwiYy1lcnJvclwiPlxyXG4gICAgICAgICAgICAgICAgICAgIHt7ZXJyb3J9fVxyXG4gICAgICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICAgIDwvbmctY29udGFpbmVyPlxyXG4gICAgICAgIDwvbmctY29udGFpbmVyPlxyXG4gICAgPC9kaXY+XHJcbiAgICA8ZGl2ICpuZ0lmPVwiZm9ybUNvbnRyb2wgJiYgbWF4TGVuZ3RoQ291bnRcIiBjbGFzcz1cImMtY291bnRlclwiPlxyXG4gICAgICAgIDxzcGFuIGNsYXNzPVwiYy1jb3VudGVyLWxlZnQtYnJhY2tldFwiPls8L3NwYW4+XHJcbiAgICAgICAgPHNwYW4gY2xhc3M9XCJjLWNvdW50ZXItZmlyc3QtZWxlbVwiPlxyXG4gICAgICAgICAgICB7eyhmb3JtQ29udHJvbC52YWx1ZUNoYW5nZXMgfCBhc3luYyk/Lmxlbmd0aCB8fCBmb3JtQ29udHJvbC52YWx1ZT8ubGVuZ3RoIHx8IDB9fVxyXG4gICAgICAgIDwvc3Bhbj5cclxuICAgICAgICA8c3BhbiBjbGFzcz1cImMtY291bnRlci1kaXZpZGVyXCI+XHJcbiAgICAgICAgICAgIC9cclxuICAgICAgICA8L3NwYW4+XHJcbiAgICAgICAgPHNwYW4gY2xhc3M9XCJjLWNvdW50ZXItbGFzdC1lbGVtXCI+XHJcbiAgICAgICAgICAgIHt7bWF4TGVuZ3RoQ291bnR9fVxyXG4gICAgICAgIDwvc3Bhbj5cclxuICAgICAgICA8c3BhbiBjbGFzcz1cImMtY291bnRlci1yaWdodC1icmFja2V0XCI+XTwvc3Bhbj5cclxuXHJcbiAgICA8L2Rpdj5cclxuPC9kaXY+XHJcbjxuZy10ZW1wbGF0ZSAjZW1wdHlEaXY+XHJcbiAgICA8ZGl2PjwvZGl2PlxyXG48L25nLXRlbXBsYXRlPiJdfQ==