@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
136 lines • 28.9 kB
JavaScript
import { Component, Input } from '@angular/core';
import { AlertService, C8yValidators, CoreModule, gettext } from '@c8y/ngx-components';
import { FormBuilder, NgForm, ReactiveFormsModule, Validators } from '@angular/forms';
import { MarkdownWidgetService } from '../markdown-widget.service';
import * as i0 from "@angular/core";
import * as i1 from "@angular/forms";
import * as i2 from "@c8y/ngx-components";
import * as i3 from "../markdown-widget.service";
import * as i4 from "@angular/common";
export class MarkdownWidgetConfigComponent {
constructor(formBuilder, form, alert, markdownService) {
this.formBuilder = formBuilder;
this.form = form;
this.alert = alert;
this.markdownService = markdownService;
this.uploadChoice = 'uploadUrl';
this.loading = false;
}
async onBeforeSave(config) {
if (this.formGroup.invalid) {
return false;
}
if (this.uploadChoice === 'uploadUrl') {
Object.assign(config, {
contentUrl: this.formGroup.value.contentUrl,
markdownBinaryId: null
});
return true;
}
const fileFromForm = this.getFileFromFormValue(this.formGroup.value);
if (fileFromForm && fileFromForm !== this.fileFromConfig) {
try {
const markdownBinaryId = await this.markdownService.uploadFile(fileFromForm);
Object.assign(config, { markdownBinaryId, contentUrl: null });
return true;
}
catch (e) {
this.alert.danger(gettext('Unable to upload Markdown file.'), e?.data);
return false;
}
}
if (!fileFromForm) {
Object.assign(config, { contentUrl: '/readme.md', markdownBinaryId: null });
}
return true;
}
async ngOnInit() {
this.initForm();
if (this.config.markdownBinaryId) {
this.uploadChoice = 'uploadBinary';
this.fileFromConfig = await this.markdownService.getFile(this.config.markdownBinaryId);
this.formGroup.patchValue({
droppedFile: [{ file: this.fileFromConfig, name: this.fileFromConfig.name }]
});
}
}
onChange(value) {
this.uploadChoice = value;
this.formGroup.controls['uploadChoice'].patchValue(value);
}
getFileFromFormValue(formValue) {
const binary = formValue?.droppedFile || [];
return binary[0]?.file || null;
}
initForm() {
this.formGroup = this.formBuilder.group({
contentUrl: ['', [Validators.maxLength(2000)]],
droppedFile: [
null,
[
Validators.minLength(1),
Validators.maxLength(1),
C8yValidators.filesValidator({ maximumFileSizeInKb: 1000 })
]
],
uploadChoice: [this.config.markdownBinaryId ? 'uploadBinary' : 'uploadUrl', []]
}, { validators: this.requireEitherBinaryOrUrl() });
this.form.form.addControl('config', this.formGroup);
this.formGroup.patchValue(this.config);
}
requireEitherBinaryOrUrl() {
return (control) => {
const url = control.get(`contentUrl`);
const uploadBinary = control.get(`droppedFile`);
const urlDefined = url && url.value !== undefined && url.value !== null;
const uploadBinaryDefined = uploadBinary && uploadBinary.value !== undefined && uploadBinary.value !== null;
const errors = {};
if (this.uploadChoice === 'uploadBinary' && !uploadBinaryDefined) {
// sets error
const error = { required: true };
uploadBinary.setErrors(Object.assign({}, uploadBinary.errors || {}, error));
Object.assign(errors, error);
}
else {
// remove previous error
this.removeErrors(uploadBinary, ['required']);
}
if (this.uploadChoice === 'uploadUrl' && (!urlDefined || url.value === '')) {
// sets error
const error = { required: true };
url.setErrors(Object.assign({}, url.errors || {}, error));
Object.assign(errors, error);
}
else {
// remove previous error
this.removeErrors(url, ['required']);
}
return Object.keys(errors).length ? errors : null;
};
}
removeErrors(control, errors) {
if (!control || !control.errors) {
return false;
}
let removedError = false;
for (const error of errors) {
if (control.errors[error]) {
removedError = true;
delete control.errors[error];
}
}
if (removedError) {
control.setErrors(Object.keys(control.errors).length ? Object.assign({}, control.errors) : null);
}
return removedError;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MarkdownWidgetConfigComponent, deps: [{ token: i1.FormBuilder }, { token: i1.NgForm }, { token: i2.AlertService }, { token: i3.MarkdownWidgetService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: MarkdownWidgetConfigComponent, isStandalone: true, selector: "c8y-markdown-widget-config", inputs: { config: "config" }, ngImport: i0, template: "<form [formGroup]=\"formGroup\" class=\"p-l-24 p-r-24 p-t-16\">\n <div class=\"form-group\">\n <label title=\"{{ 'Upload a binary' | translate }}\" class=\"c8y-radio radio-inline\">\n <input\n #radio\n formControlName=\"uploadChoice\"\n type=\"radio\"\n value=\"uploadBinary\"\n name=\"uploadChoice\"\n (change)=\"onChange($event.target.value)\"\n />\n <span></span>\n <span>{{ 'Upload a binary' | translate }}</span>\n </label>\n <label title=\"{{ 'Provide a file path' | translate }}\" class=\"c8y-radio radio-inline m-l-8\">\n <input\n #radio\n formControlName=\"uploadChoice\"\n type=\"radio\"\n value=\"uploadUrl\"\n name=\"uploadChoice\"\n (change)=\"onChange($event.target.value)\"\n />\n <span></span>\n <span>\n {{ 'Provide a file path' | translate }}\n </span>\n </label>\n </div>\n\n <ng-container [ngSwitch]=\"uploadChoice\">\n <div *ngSwitchCase=\"'uploadBinary'\">\n <c8y-form-group class=\"m-0\">\n <c8y-drop-area\n formControlName=\"droppedFile\"\n class=\"drop-area-sm\"\n [title]=\"'Drop file or click to browse' | translate\"\n [maxAllowedFiles]=\"1\"\n [accept]=\"'md'\"\n ></c8y-drop-area>\n </c8y-form-group>\n </div>\n <div *ngSwitchCase=\"'uploadUrl'\">\n <c8y-form-group class=\"m-0\">\n <div class=\"m-b-4 p-b-8\">\n <div class=\"input-group\">\n <span class=\"input-group-addon\">\n <i c8yIcon=\"globe\"></i>\n </span>\n <input\n type=\"text\"\n class=\"form-control\"\n formControlName=\"contentUrl\"\n placeholder=\"{{ 'e.g.' | translate }} http://example.com/binary.zip\"\n />\n </div>\n </div>\n </c8y-form-group>\n </div>\n </ng-container>\n</form>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i4.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i4.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "component", type: i2.DropAreaComponent, selector: "c8y-drop-area", inputs: ["formControl", "title", "message", "icon", "loadingMessage", "forceHideList", "alwaysShow", "clickToOpen", "loading", "progress", "maxAllowedFiles", "files", "maxFileSizeInMegaBytes", "accept"], outputs: ["dropped"] }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i2.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: ReactiveFormsModule }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MarkdownWidgetConfigComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-markdown-widget-config', standalone: true, imports: [CoreModule, ReactiveFormsModule], template: "<form [formGroup]=\"formGroup\" class=\"p-l-24 p-r-24 p-t-16\">\n <div class=\"form-group\">\n <label title=\"{{ 'Upload a binary' | translate }}\" class=\"c8y-radio radio-inline\">\n <input\n #radio\n formControlName=\"uploadChoice\"\n type=\"radio\"\n value=\"uploadBinary\"\n name=\"uploadChoice\"\n (change)=\"onChange($event.target.value)\"\n />\n <span></span>\n <span>{{ 'Upload a binary' | translate }}</span>\n </label>\n <label title=\"{{ 'Provide a file path' | translate }}\" class=\"c8y-radio radio-inline m-l-8\">\n <input\n #radio\n formControlName=\"uploadChoice\"\n type=\"radio\"\n value=\"uploadUrl\"\n name=\"uploadChoice\"\n (change)=\"onChange($event.target.value)\"\n />\n <span></span>\n <span>\n {{ 'Provide a file path' | translate }}\n </span>\n </label>\n </div>\n\n <ng-container [ngSwitch]=\"uploadChoice\">\n <div *ngSwitchCase=\"'uploadBinary'\">\n <c8y-form-group class=\"m-0\">\n <c8y-drop-area\n formControlName=\"droppedFile\"\n class=\"drop-area-sm\"\n [title]=\"'Drop file or click to browse' | translate\"\n [maxAllowedFiles]=\"1\"\n [accept]=\"'md'\"\n ></c8y-drop-area>\n </c8y-form-group>\n </div>\n <div *ngSwitchCase=\"'uploadUrl'\">\n <c8y-form-group class=\"m-0\">\n <div class=\"m-b-4 p-b-8\">\n <div class=\"input-group\">\n <span class=\"input-group-addon\">\n <i c8yIcon=\"globe\"></i>\n </span>\n <input\n type=\"text\"\n class=\"form-control\"\n formControlName=\"contentUrl\"\n placeholder=\"{{ 'e.g.' | translate }} http://example.com/binary.zip\"\n />\n </div>\n </div>\n </c8y-form-group>\n </div>\n </ng-container>\n</form>\n" }]
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i1.NgForm }, { type: i2.AlertService }, { type: i3.MarkdownWidgetService }], propDecorators: { config: [{
type: Input
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFya2Rvd24td2lkZ2V0LWNvbmZpZy5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi93aWRnZXRzL2ltcGxlbWVudGF0aW9ucy9tYXJrZG93bi9tYXJrZG93bi13aWRnZXQtY29uZmlnL21hcmtkb3duLXdpZGdldC1jb25maWcuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vd2lkZ2V0cy9pbXBsZW1lbnRhdGlvbnMvbWFya2Rvd24vbWFya2Rvd24td2lkZ2V0LWNvbmZpZy9tYXJrZG93bi13aWRnZXQtY29uZmlnLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFVLE1BQU0sZUFBZSxDQUFDO0FBQ3pELE9BQU8sRUFDTCxZQUFZLEVBQ1osYUFBYSxFQUNiLFVBQVUsRUFDVixPQUFPLEVBRVIsTUFBTSxxQkFBcUIsQ0FBQztBQUM3QixPQUFPLEVBRUwsV0FBVyxFQUVYLE1BQU0sRUFDTixtQkFBbUIsRUFHbkIsVUFBVSxFQUNYLE1BQU0sZ0JBQWdCLENBQUM7QUFFeEIsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7Ozs7OztBQVFuRSxNQUFNLE9BQU8sNkJBQTZCO0lBT3hDLFlBQ1UsV0FBd0IsRUFDeEIsSUFBWSxFQUNaLEtBQW1CLEVBQ25CLGVBQXNDO1FBSHRDLGdCQUFXLEdBQVgsV0FBVyxDQUFhO1FBQ3hCLFNBQUksR0FBSixJQUFJLENBQVE7UUFDWixVQUFLLEdBQUwsS0FBSyxDQUFjO1FBQ25CLG9CQUFlLEdBQWYsZUFBZSxDQUF1QjtRQVBoRCxpQkFBWSxHQUFpQyxXQUFXLENBQUM7UUFDekQsWUFBTyxHQUFHLEtBQUssQ0FBQztJQU9iLENBQUM7SUFFSixLQUFLLENBQUMsWUFBWSxDQUFDLE1BQTZCO1FBQzlDLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLEVBQUUs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QyxPQUFPLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDaEMsT0FBTyxLQUFLLENBQUM7UUFDZixDQUFDO1FBQ0QsSUFBSSxZQUFZLEdBQUcsS0FBSyxDQUFDO1FBQ3pCLEtBQUssTUFBTSxLQUFLLElBQUksTUFBTSxFQUFFLENBQUM7WUFDM0IsSUFBSSxPQUFPLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0JBQzFCLFlBQVksR0FBRyxJQUFJLENBQUM7Z0JBQ3BCLE9BQU8sT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUMvQixDQUFDO1FBQ0gsQ0FBQztRQUNELElBQUksWUFBWSxFQUFFLENBQUM7WUFDakIsT0FBTyxDQUFDLFNBQVMsQ0FDZixNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsRUFBRSxFQUFFLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUM5RSxDQUFDO1FBQ0osQ0FBQztRQUNELE9BQU8sWUFBWSxDQUFDO0lBQ3RCLENBQUM7K0dBdElVLDZCQUE2QjttR0FBN0IsNkJBQTZCLG9IQzNCMUMsZzdEQTZEQSwyQ0RwQ1ksVUFBVSwwakVBQUUsbUJBQW1COzs0RkFFOUIsNkJBQTZCO2tCQU56QyxTQUFTOytCQUNFLDRCQUE0QixjQUUxQixJQUFJLFdBQ1AsQ0FBQyxVQUFVLEVBQUUsbUJBQW1CLENBQUM7b0tBR2pDLE1BQU07c0JBQWQsS0FBSyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgSW5wdXQsIE9uSW5pdCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtcbiAgQWxlcnRTZXJ2aWNlLFxuICBDOHlWYWxpZGF0b3JzLFxuICBDb3JlTW9kdWxlLFxuICBnZXR0ZXh0LFxuICBPbkJlZm9yZVNhdmVcbn0gZnJvbSAnQGM4eS9uZ3gtY29tcG9uZW50cyc7XG5pbXBvcnQge1xuICBBYnN0cmFjdENvbnRyb2wsXG4gIEZvcm1CdWlsZGVyLFxuICBGb3JtR3JvdXAsXG4gIE5nRm9ybSxcbiAgUmVhY3RpdmVGb3Jtc01vZHVsZSxcbiAgVmFsaWRhdGlvbkVycm9ycyxcbiAgVmFsaWRhdG9yRm4sXG4gIFZhbGlkYXRvcnNcbn0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHsgTWFya2Rvd25XaWRnZXRDb25maWcgfSBmcm9tICcuLi9tYXJrZG93bi13aWRnZXQubW9kZWwnO1xuaW1wb3J0IHsgTWFya2Rvd25XaWRnZXRTZXJ2aWNlIH0gZnJvbSAnLi4vbWFya2Rvd24td2lkZ2V0LnNlcnZpY2UnO1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdjOHktbWFya2Rvd24td2lkZ2V0LWNvbmZpZycsXG4gIHRlbXBsYXRlVXJsOiAnLi9tYXJrZG93bi13aWRnZXQtY29uZmlnLmNvbXBvbmVudC5odG1sJyxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW0NvcmVNb2R1bGUsIFJlYWN0aXZlRm9ybXNNb2R1bGVdXG59KVxuZXhwb3J0IGNsYXNzIE1hcmtkb3duV2lkZ2V0Q29uZmlnQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBPbkJlZm9yZVNhdmUge1xuICBASW5wdXQoKSBjb25maWc6IE1hcmtkb3duV2lkZ2V0Q29uZmlnO1xuICBmb3JtR3JvdXA6IEZvcm1Hcm91cDtcbiAgZmlsZUZyb21Db25maWc6IEZpbGU7XG4gIHVwbG9hZENob2ljZTogJ3VwbG9hZEJpbmFyeScgfCAndXBsb2FkVXJsJyA9ICd1cGxvYWRVcmwnO1xuICBsb2FkaW5nID0gZmFsc2U7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBmb3JtQnVpbGRlcjogRm9ybUJ1aWxkZXIsXG4gICAgcHJpdmF0ZSBmb3JtOiBOZ0Zvcm0sXG4gICAgcHJpdmF0ZSBhbGVydDogQWxlcnRTZXJ2aWNlLFxuICAgIHByaXZhdGUgbWFya2Rvd25TZXJ2aWNlOiBNYXJrZG93bldpZGdldFNlcnZpY2VcbiAgKSB7fVxuXG4gIGFzeW5jIG9uQmVmb3JlU2F2ZShjb25maWc/OiBNYXJrZG93bldpZGdldENvbmZpZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGlmICh0aGlzLmZvcm1Hcm91cC5pbnZhbGlkKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGlmICh0aGlzLnVwbG9hZENob2ljZSA9PT0gJ3VwbG9hZFVybCcpIHtcbiAgICAgIE9iamVjdC5hc3NpZ24oY29uZmlnLCB7XG4gICAgICAgIGNvbnRlbnRVcmw6IHRoaXMuZm9ybUdyb3VwLnZhbHVlLmNvbnRlbnRVcmwsXG4gICAgICAgIG1hcmtkb3duQmluYXJ5SWQ6IG51bGxcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIGNvbnN0IGZpbGVGcm9tRm9ybSA9IHRoaXMuZ2V0RmlsZUZyb21Gb3JtVmFsdWUodGhpcy5mb3JtR3JvdXAudmFsdWUpO1xuICAgIGlmIChmaWxlRnJvbUZvcm0gJiYgZmlsZUZyb21Gb3JtICE9PSB0aGlzLmZpbGVGcm9tQ29uZmlnKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBtYXJrZG93bkJpbmFyeUlkID0gYXdhaXQgdGhpcy5tYXJrZG93blNlcnZpY2UudXBsb2FkRmlsZShmaWxlRnJvbUZvcm0pO1xuICAgICAgICBPYmplY3QuYXNzaWduKGNvbmZpZywgeyBtYXJrZG93bkJpbmFyeUlkLCBjb250ZW50VXJsOiBudWxsIH0pO1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgdGhpcy5hbGVydC5kYW5nZXIoZ2V0dGV4dCgnVW5hYmxlIHRvIHVwbG9hZCBNYXJrZG93biBmaWxlLicpLCBlPy5kYXRhKTtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAoIWZpbGVGcm9tRm9ybSkge1xuICAgICAgT2JqZWN0LmFzc2lnbihjb25maWcsIHsgY29udGVudFVybDogJy9yZWFkbWUubWQnLCBtYXJrZG93bkJpbmFyeUlkOiBudWxsIH0pO1xuICAgIH1cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIGFzeW5jIG5nT25Jbml0KCkge1xuICAgIHRoaXMuaW5pdEZvcm0oKTtcbiAgICBpZiAodGhpcy5jb25maWcubWFya2Rvd25CaW5hcnlJZCkge1xuICAgICAgdGhpcy51cGxvYWRDaG9pY2UgPSAndXBsb2FkQmluYXJ5JztcbiAgICAgIHRoaXMuZmlsZUZyb21Db25maWcgPSBhd2FpdCB0aGlzLm1hcmtkb3duU2VydmljZS5nZXRGaWxlKHRoaXMuY29uZmlnLm1hcmtkb3duQmluYXJ5SWQpO1xuICAgICAgdGhpcy5mb3JtR3JvdXAucGF0Y2hWYWx1ZSh7XG4gICAgICAgIGRyb3BwZWRGaWxlOiBbeyBmaWxlOiB0aGlzLmZpbGVGcm9tQ29uZmlnLCBuYW1lOiB0aGlzLmZpbGVGcm9tQ29uZmlnLm5hbWUgfV1cbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIG9uQ2hhbmdlKHZhbHVlOiAndXBsb2FkQmluYXJ5JyB8ICd1cGxvYWRVcmwnKSB7XG4gICAgdGhpcy51cGxvYWRDaG9pY2UgPSB2YWx1ZTtcbiAgICB0aGlzLmZvcm1Hcm91cC5jb250cm9sc1sndXBsb2FkQ2hvaWNlJ10ucGF0Y2hWYWx1ZSh2YWx1ZSk7XG4gIH1cblxuICBwcml2YXRlIGdldEZpbGVGcm9tRm9ybVZhbHVlKGZvcm1WYWx1ZTogYW55KTogRmlsZSB8IG51bGwge1xuICAgIGNvbnN0IGJpbmFyeTogYW55W10gPSBmb3JtVmFsdWU/LmRyb3BwZWRGaWxlIHx8IFtdO1xuICAgIHJldHVybiBiaW5hcnlbMF0/LmZpbGUgfHwgbnVsbDtcbiAgfVxuXG4gIHByaXZhdGUgaW5pdEZvcm0oKTogdm9pZCB7XG4gICAgdGhpcy5mb3JtR3JvdXAgPSB0aGlzLmZvcm1CdWlsZGVyLmdyb3VwKFxuICAgICAge1xuICAgICAgICBjb250ZW50VXJsOiBbJycsIFtWYWxpZGF0b3JzLm1heExlbmd0aCgyMDAwKV1dLFxuICAgICAgICBkcm9wcGVkRmlsZTogW1xuICAgICAgICAgIG51bGwsXG4gICAgICAgICAgW1xuICAgICAgICAgICAgVmFsaWRhdG9ycy5taW5MZW5ndGgoMSksXG4gICAgICAgICAgICBWYWxpZGF0b3JzLm1heExlbmd0aCgxKSxcbiAgICAgICAgICAgIEM4eVZhbGlkYXRvcnMuZmlsZXNWYWxpZGF0b3IoeyBtYXhpbXVtRmlsZVNpemVJbktiOiAxMDAwIH0pXG4gICAgICAgICAgXVxuICAgICAgICBdLFxuICAgICAgICB1cGxvYWRDaG9pY2U6IFt0aGlzLmNvbmZpZy5tYXJrZG93bkJpbmFyeUlkID8gJ3VwbG9hZEJpbmFyeScgOiAndXBsb2FkVXJsJywgW11dXG4gICAgICB9LFxuICAgICAgeyB2YWxpZGF0b3JzOiB0aGlzLnJlcXVpcmVFaXRoZXJCaW5hcnlPclVybCgpIH1cbiAgICApO1xuICAgIHRoaXMuZm9ybS5mb3JtLmFkZENvbnRyb2woJ2NvbmZpZycsIHRoaXMuZm9ybUdyb3VwKTtcbiAgICB0aGlzLmZvcm1Hcm91cC5wYXRjaFZhbHVlKHRoaXMuY29uZmlnKTtcbiAgfVxuXG4gIHByaXZhdGUgcmVxdWlyZUVpdGhlckJpbmFyeU9yVXJsKCk6IFZhbGlkYXRvckZuIHtcbiAgICByZXR1cm4gKGNvbnRyb2w6IEFic3RyYWN0Q29udHJvbCk6IFZhbGlkYXRpb25FcnJvcnMgfCBudWxsID0+IHtcbiAgICAgIGNvbnN0IHVybCA9IGNvbnRyb2wuZ2V0KGBjb250ZW50VXJsYCk7XG4gICAgICBjb25zdCB1cGxvYWRCaW5hcnkgPSBjb250cm9sLmdldChgZHJvcHBlZEZpbGVgKTtcblxuICAgICAgY29uc3QgdXJsRGVmaW5lZCA9IHVybCAmJiB1cmwudmFsdWUgIT09IHVuZGVmaW5lZCAmJiB1cmwudmFsdWUgIT09IG51bGw7XG4gICAgICBjb25zdCB1cGxvYWRCaW5hcnlEZWZpbmVkID1cbiAgICAgICAgdXBsb2FkQmluYXJ5ICYmIHVwbG9hZEJpbmFyeS52YWx1ZSAhPT0gdW5kZWZpbmVkICYmIHVwbG9hZEJpbmFyeS52YWx1ZSAhPT0gbnVsbDtcblxuICAgICAgY29uc3QgZXJyb3JzID0ge307XG4gICAgICBpZiAodGhpcy51cGxvYWRDaG9pY2UgPT09ICd1cGxvYWRCaW5hcnknICYmICF1cGxvYWRCaW5hcnlEZWZpbmVkKSB7XG4gICAgICAgIC8vIHNldHMgZXJyb3JcbiAgICAgICAgY29uc3QgZXJyb3IgPSB7IHJlcXVpcmVkOiB0cnVlIH07XG4gICAgICAgIHVwbG9hZEJpbmFyeS5zZXRFcnJvcnMoT2JqZWN0LmFzc2lnbih7fSwgdXBsb2FkQmluYXJ5LmVycm9ycyB8fCB7fSwgZXJyb3IpKTtcbiAgICAgICAgT2JqZWN0LmFzc2lnbihlcnJvcnMsIGVycm9yKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIHJlbW92ZSBwcmV2aW91cyBlcnJvclxuICAgICAgICB0aGlzLnJlbW92ZUVycm9ycyh1cGxvYWRCaW5hcnksIFsncmVxdWlyZWQnXSk7XG4gICAgICB9XG5cbiAgICAgIGlmICh0aGlzLnVwbG9hZENob2ljZSA9PT0gJ3VwbG9hZFVybCcgJiYgKCF1cmxEZWZpbmVkIHx8IHVybC52YWx1ZSA9PT0gJycpKSB7XG4gICAgICAgIC8vIHNldHMgZXJyb3JcbiAgICAgICAgY29uc3QgZXJyb3IgPSB7IHJlcXVpcmVkOiB0cnVlIH07XG4gICAgICAgIHVybC5zZXRFcnJvcnMoT2JqZWN0LmFzc2lnbih7fSwgdXJsLmVycm9ycyB8fCB7fSwgZXJyb3IpKTtcbiAgICAgICAgT2JqZWN0LmFzc2lnbihlcnJvcnMsIGVycm9yKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIHJlbW92ZSBwcmV2aW91cyBlcnJvclxuICAgICAgICB0aGlzLnJlbW92ZUVycm9ycyh1cmwsIFsncmVxdWlyZWQnXSk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBPYmplY3Qua2V5cyhlcnJvcnMpLmxlbmd0aCA/IGVycm9ycyA6IG51bGw7XG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgcmVtb3ZlRXJyb3JzKGNvbnRyb2w6IEFic3RyYWN0Q29udHJvbCwgZXJyb3JzOiBzdHJpbmdbXSk6IGJvb2xlYW4ge1xuICAgIGlmICghY29udHJvbCB8fCAhY29udHJvbC5lcnJvcnMpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgbGV0IHJlbW92ZWRFcnJvciA9IGZhbHNlO1xuICAgIGZvciAoY29uc3QgZXJyb3Igb2YgZXJyb3JzKSB7XG4gICAgICBpZiAoY29udHJvbC5lcnJvcnNbZXJyb3JdKSB7XG4gICAgICAgIHJlbW92ZWRFcnJvciA9IHRydWU7XG4gICAgICAgIGRlbGV0ZSBjb250cm9sLmVycm9yc1tlcnJvcl07XG4gICAgICB9XG4gICAgfVxuICAgIGlmIChyZW1vdmVkRXJyb3IpIHtcbiAgICAgIGNvbnRyb2wuc2V0RXJyb3JzKFxuICAgICAgICBPYmplY3Qua2V5cyhjb250cm9sLmVycm9ycykubGVuZ3RoID8gT2JqZWN0LmFzc2lnbih7fSwgY29udHJvbC5lcnJvcnMpIDogbnVsbFxuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIHJlbW92ZWRFcnJvcjtcbiAgfVxufVxuIiwiPGZvcm0gW2Zvcm1Hcm91cF09XCJmb3JtR3JvdXBcIiBjbGFzcz1cInAtbC0yNCBwLXItMjQgcC10LTE2XCI+XG4gIDxkaXYgY2xhc3M9XCJmb3JtLWdyb3VwXCI+XG4gICAgPGxhYmVsIHRpdGxlPVwie3sgJ1VwbG9hZCBhIGJpbmFyeScgfCB0cmFuc2xhdGUgfX1cIiBjbGFzcz1cImM4eS1yYWRpbyByYWRpby1pbmxpbmVcIj5cbiAgICAgIDxpbnB1dFxuICAgICAgICAjcmFkaW9cbiAgICAgICAgZm9ybUNvbnRyb2xOYW1lPVwidXBsb2FkQ2hvaWNlXCJcbiAgICAgICAgdHlwZT1cInJhZGlvXCJcbiAgICAgICAgdmFsdWU9XCJ1cGxvYWRCaW5hcnlcIlxuICAgICAgICBuYW1lPVwidXBsb2FkQ2hvaWNlXCJcbiAgICAgICAgKGNoYW5nZSk9XCJvbkNoYW5nZSgkZXZlbnQudGFyZ2V0LnZhbHVlKVwiXG4gICAgICAvPlxuICAgICAgPHNwYW4+PC9zcGFuPlxuICAgICAgPHNwYW4+e3sgJ1VwbG9hZCBhIGJpbmFyeScgfCB0cmFuc2xhdGUgfX08L3NwYW4+XG4gICAgPC9sYWJlbD5cbiAgICA8bGFiZWwgdGl0bGU9XCJ7eyAnUHJvdmlkZSBhIGZpbGUgcGF0aCcgfCB0cmFuc2xhdGUgfX1cIiBjbGFzcz1cImM4eS1yYWRpbyByYWRpby1pbmxpbmUgbS1sLThcIj5cbiAgICAgIDxpbnB1dFxuICAgICAgICAjcmFkaW9cbiAgICAgICAgZm9ybUNvbnRyb2xOYW1lPVwidXBsb2FkQ2hvaWNlXCJcbiAgICAgICAgdHlwZT1cInJhZGlvXCJcbiAgICAgICAgdmFsdWU9XCJ1cGxvYWRVcmxcIlxuICAgICAgICBuYW1lPVwidXBsb2FkQ2hvaWNlXCJcbiAgICAgICAgKGNoYW5nZSk9XCJvbkNoYW5nZSgkZXZlbnQudGFyZ2V0LnZhbHVlKVwiXG4gICAgICAvPlxuICAgICAgPHNwYW4+PC9zcGFuPlxuICAgICAgPHNwYW4+XG4gICAgICAgIHt7ICdQcm92aWRlIGEgZmlsZSBwYXRoJyB8IHRyYW5zbGF0ZSB9fVxuICAgICAgPC9zcGFuPlxuICAgIDwvbGFiZWw+XG4gIDwvZGl2PlxuXG4gIDxuZy1jb250YWluZXIgW25nU3dpdGNoXT1cInVwbG9hZENob2ljZVwiPlxuICAgIDxkaXYgKm5nU3dpdGNoQ2FzZT1cIid1cGxvYWRCaW5hcnknXCI+XG4gICAgICA8Yzh5LWZvcm0tZ3JvdXAgY2xhc3M9XCJtLTBcIj5cbiAgICAgICAgPGM4eS1kcm9wLWFyZWFcbiAgICAgICAgICBmb3JtQ29udHJvbE5hbWU9XCJkcm9wcGVkRmlsZVwiXG4gICAgICAgICAgY2xhc3M9XCJkcm9wLWFyZWEtc21cIlxuICAgICAgICAgIFt0aXRsZV09XCInRHJvcCBmaWxlIG9yIGNsaWNrIHRvIGJyb3dzZScgfCB0cmFuc2xhdGVcIlxuICAgICAgICAgIFttYXhBbGxvd2VkRmlsZXNdPVwiMVwiXG4gICAgICAgICAgW2FjY2VwdF09XCInbWQnXCJcbiAgICAgICAgPjwvYzh5LWRyb3AtYXJlYT5cbiAgICAgIDwvYzh5LWZvcm0tZ3JvdXA+XG4gICAgPC9kaXY+XG4gICAgPGRpdiAqbmdTd2l0Y2hDYXNlPVwiJ3VwbG9hZFVybCdcIj5cbiAgICAgIDxjOHktZm9ybS1ncm91cCBjbGFzcz1cIm0tMFwiPlxuICAgICAgICA8ZGl2IGNsYXNzPVwibS1iLTQgcC1iLThcIj5cbiAgICAgICAgICA8ZGl2IGNsYXNzPVwiaW5wdXQtZ3JvdXBcIj5cbiAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwiaW5wdXQtZ3JvdXAtYWRkb25cIj5cbiAgICAgICAgICAgICAgPGkgYzh5SWNvbj1cImdsb2JlXCI+PC9pPlxuICAgICAgICAgICAgPC9zcGFuPlxuICAgICAgICAgICAgPGlucHV0XG4gICAgICAgICAgICAgIHR5cGU9XCJ0ZXh0XCJcbiAgICAgICAgICAgICAgY2xhc3M9XCJmb3JtLWNvbnRyb2xcIlxuICAgICAgICAgICAgICBmb3JtQ29udHJvbE5hbWU9XCJjb250ZW50VXJsXCJcbiAgICAgICAgICAgICAgcGxhY2Vob2xkZXI9XCJ7eyAnZS5nLicgfCB0cmFuc2xhdGUgfX0gaHR0cDovL2V4YW1wbGUuY29tL2JpbmFyeS56aXBcIlxuICAgICAgICAgICAgLz5cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2M4eS1mb3JtLWdyb3VwPlxuICAgIDwvZGl2PlxuICA8L25nLWNvbnRhaW5lcj5cbjwvZm9ybT5cbiJdfQ==