@acrodata/gui
Version: 
JSON powered GUI for configurable panels.
139 lines • 25.3 kB
JavaScript
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, ViewChild, ViewEncapsulation, forwardRef, } from '@angular/core';
import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
import { MatIconButton } from '@angular/material/button';
import { MatFormField, MatHint, MatPrefix, MatSuffix } from '@angular/material/form-field';
import { MatIcon } from '@angular/material/icon';
import { MatInput } from '@angular/material/input';
import { finalize } from 'rxjs/operators';
import { GuiFieldLabel } from '../field-label/field-label';
import { GuiIconButtonWrapper } from '../icon-button-wrapper/icon-button-wrapper';
import * as i0 from "@angular/core";
import * as i1 from "./file-uploader-config";
import * as i2 from "../gui-icons";
import * as i3 from "@angular/forms";
export class GuiFileUploader {
    constructor(fileUploaderCfg, cdr, iconsRegistry) {
        this.fileUploaderCfg = fileUploaderCfg;
        this.cdr = cdr;
        this.config = {};
        this.disabled = false;
        this.type = '*';
        this.name = '';
        this.accept = '';
        this.fileChange = new EventEmitter();
        // file url that returned from the server
        this.url = '';
        this.onChange = () => { };
        this.onTouched = () => { };
        iconsRegistry.add('link', 'clear', 'file', 'upload');
    }
    ngOnChanges(changes) {
        if (changes['config'] || changes['accept'] || changes['type']) {
            this.accept = this.config.accept || this.accept || this.type + '/*';
        }
    }
    writeValue(value) {
        this.url = value;
        this.cdr.markForCheck();
    }
    registerOnChange(fn) {
        this.onChange = fn;
    }
    registerOnTouched(fn) {
        this.onTouched = fn;
    }
    setDisabledState(isDisabled) {
        this.disabled = isDisabled;
        this.cdr.markForCheck();
    }
    upload(fileUpload) {
        const formData = new FormData();
        formData.append('file', fileUpload.data);
        fileUpload.inProgress = true;
        this.fileUploaderCfg
            .upload(formData, this.config)
            .pipe(finalize(() => {
            fileUpload.inProgress = false;
        }))
            .subscribe(result => {
            if (result) {
                this.url = result;
                this.cdr.detectChanges();
                this.onChange(this.url);
                this.onTouched();
                this.fileChange.emit(this.url);
            }
        });
    }
    onUrlChange(e) {
        this.url = e.target.value;
        this.onChange(this.url);
        this.fileChange.emit(this.url);
    }
    onFileChange(e) {
        this.fileUpload = {
            data: e.target.files[0],
            inProgress: false,
            progress: 0,
        };
        this.upload(this.fileUpload);
        // reset input value
        e.target.value = '';
    }
    onBlur() {
        this.onTouched();
    }
    onClear() {
        this.url = '';
        this.onChange(this.url);
        this.onTouched();
        this.fileChange.emit(this.url);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: GuiFileUploader, deps: [{ token: i1.GuiFileUploaderConfig }, { token: i0.ChangeDetectorRef }, { token: i2.GuiIconsRegistry }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: GuiFileUploader, isStandalone: true, selector: "gui-file-uploader", inputs: { config: "config", disabled: "disabled", type: "type", name: "name", accept: "accept" }, outputs: { fileChange: "fileChange" }, host: { classAttribute: "gui-field gui-file-uploader" }, providers: [
            {
                provide: NG_VALUE_ACCESSOR,
                useExisting: forwardRef(() => GuiFileUploader),
                multi: true,
            },
        ], viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<mat-form-field>\n  <gui-icon-button-wrapper matPrefix>\n    <mat-icon svgIcon=\"link\" />\n  </gui-icon-button-wrapper>\n\n  <input\n    matInput\n    type=\"text\"\n    [ngModel]=\"url\"\n    [disabled]=\"disabled\"\n    [placeholder]=\"config.placeholder || ''\"\n    (change)=\"onUrlChange($event)\"\n    (blur)=\"onBlur()\"\n  />\n\n  @if (url) {\n    <gui-icon-button-wrapper matSuffix>\n      <button mat-icon-button type=\"button\" color=\"warn\" [disabled]=\"disabled\" (click)=\"onClear()\">\n        <mat-icon svgIcon=\"clear\" />\n      </button>\n    </gui-icon-button-wrapper>\n  }\n</mat-form-field>\n\n<figure class=\"gui-file-content\">\n  @if (url) {\n    @switch (type) {\n      @case ('image') {\n        <img [src]=\"url\" alt=\"\" />\n      }\n      @case ('video') {\n        <video [src]=\"url\"></video>\n      }\n      @case ('audio') {\n        <audio [src]=\"url\" controls></audio>\n      }\n      @default {\n        <mat-icon svgIcon=\"file\" />\n      }\n    }\n  } @else {\n    <div class=\"gui-file-placeholder\">\n      <mat-icon svgIcon=\"upload\" />\n    </div>\n  }\n\n  <input\n    #fileInput\n    type=\"file\"\n    [accept]=\"accept\"\n    [name]=\"name\"\n    [disabled]=\"disabled\"\n    (change)=\"onFileChange($event)\"\n  />\n</figure>\n\n@if (config.parentType === 'inline') {\n  <mat-hint>\n    <gui-field-label [config]=\"config\" />\n  </mat-hint>\n}\n", styles: [".gui-file-uploader{display:block;overflow:auto;line-height:1}.gui-file-uploader .mat-mdc-form-field{width:100%}.gui-file-uploader .gui-file-content{position:relative;display:flex;align-items:center;justify-content:center;height:7.5rem;margin:.25rem 0 0;padding:.25rem;background-color:var(--mdc-filled-text-field-container-color);border:1px solid transparent;border-radius:.25rem}.gui-file-uploader .gui-file-content:hover,.gui-file-uploader .gui-file-content:focus-within{border-color:currentColor}.gui-file-uploader .gui-file-content img,.gui-file-uploader .gui-file-content video{width:100%;height:100%;object-fit:contain}.gui-file-uploader .gui-file-content input[type=file]{position:absolute;top:0;left:0;width:100%;height:100%;opacity:0;cursor:pointer}.gui-file-uploader .gui-file-content .mat-icon svg{width:1.5rem;height:1.5rem}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.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: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl],      input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "directive", type: MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "component", type: GuiFieldLabel, selector: "gui-field-label", inputs: ["config", "index"] }, { kind: "component", type: GuiIconButtonWrapper, selector: "gui-icon-button-wrapper" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: GuiFileUploader, decorators: [{
            type: Component,
            args: [{ selector: 'gui-file-uploader', host: {
                        class: 'gui-field gui-file-uploader',
                    }, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
                        {
                            provide: NG_VALUE_ACCESSOR,
                            useExisting: forwardRef(() => GuiFileUploader),
                            multi: true,
                        },
                    ], standalone: true, imports: [
                        FormsModule,
                        MatFormField,
                        MatIcon,
                        MatPrefix,
                        MatInput,
                        MatIconButton,
                        MatSuffix,
                        MatHint,
                        GuiFieldLabel,
                        GuiIconButtonWrapper,
                    ], template: "<mat-form-field>\n  <gui-icon-button-wrapper matPrefix>\n    <mat-icon svgIcon=\"link\" />\n  </gui-icon-button-wrapper>\n\n  <input\n    matInput\n    type=\"text\"\n    [ngModel]=\"url\"\n    [disabled]=\"disabled\"\n    [placeholder]=\"config.placeholder || ''\"\n    (change)=\"onUrlChange($event)\"\n    (blur)=\"onBlur()\"\n  />\n\n  @if (url) {\n    <gui-icon-button-wrapper matSuffix>\n      <button mat-icon-button type=\"button\" color=\"warn\" [disabled]=\"disabled\" (click)=\"onClear()\">\n        <mat-icon svgIcon=\"clear\" />\n      </button>\n    </gui-icon-button-wrapper>\n  }\n</mat-form-field>\n\n<figure class=\"gui-file-content\">\n  @if (url) {\n    @switch (type) {\n      @case ('image') {\n        <img [src]=\"url\" alt=\"\" />\n      }\n      @case ('video') {\n        <video [src]=\"url\"></video>\n      }\n      @case ('audio') {\n        <audio [src]=\"url\" controls></audio>\n      }\n      @default {\n        <mat-icon svgIcon=\"file\" />\n      }\n    }\n  } @else {\n    <div class=\"gui-file-placeholder\">\n      <mat-icon svgIcon=\"upload\" />\n    </div>\n  }\n\n  <input\n    #fileInput\n    type=\"file\"\n    [accept]=\"accept\"\n    [name]=\"name\"\n    [disabled]=\"disabled\"\n    (change)=\"onFileChange($event)\"\n  />\n</figure>\n\n@if (config.parentType === 'inline') {\n  <mat-hint>\n    <gui-field-label [config]=\"config\" />\n  </mat-hint>\n}\n", styles: [".gui-file-uploader{display:block;overflow:auto;line-height:1}.gui-file-uploader .mat-mdc-form-field{width:100%}.gui-file-uploader .gui-file-content{position:relative;display:flex;align-items:center;justify-content:center;height:7.5rem;margin:.25rem 0 0;padding:.25rem;background-color:var(--mdc-filled-text-field-container-color);border:1px solid transparent;border-radius:.25rem}.gui-file-uploader .gui-file-content:hover,.gui-file-uploader .gui-file-content:focus-within{border-color:currentColor}.gui-file-uploader .gui-file-content img,.gui-file-uploader .gui-file-content video{width:100%;height:100%;object-fit:contain}.gui-file-uploader .gui-file-content input[type=file]{position:absolute;top:0;left:0;width:100%;height:100%;opacity:0;cursor:pointer}.gui-file-uploader .gui-file-content .mat-icon svg{width:1.5rem;height:1.5rem}\n"] }]
        }], ctorParameters: () => [{ type: i1.GuiFileUploaderConfig }, { type: i0.ChangeDetectorRef }, { type: i2.GuiIconsRegistry }], propDecorators: { fileInput: [{
                type: ViewChild,
                args: ['fileInput']
            }], config: [{
                type: Input
            }], disabled: [{
                type: Input
            }], type: [{
                type: Input
            }], name: [{
                type: Input
            }], accept: [{
                type: Input
            }], fileChange: [{
                type: Output
            }] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZS11cGxvYWRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL2d1aS9maWxlLXVwbG9hZGVyL2ZpbGUtdXBsb2FkZXIudHMiLCIuLi8uLi8uLi8uLi9wcm9qZWN0cy9ndWkvZmlsZS11cGxvYWRlci9maWxlLXVwbG9hZGVyLmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLHVCQUF1QixFQUV2QixTQUFTLEVBRVQsWUFBWSxFQUNaLEtBQUssRUFFTCxNQUFNLEVBRU4sU0FBUyxFQUNULGlCQUFpQixFQUNqQixVQUFVLEdBQ1gsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUF3QixXQUFXLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUN0RixPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDekQsT0FBTyxFQUFFLFlBQVksRUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQzNGLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNqRCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDbkQsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRTFDLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUUzRCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQzs7Ozs7QUEwQ2xGLE1BQU0sT0FBTyxlQUFlO0lBb0IxQixZQUNVLGVBQXNDLEVBQ3RDLEdBQXNCLEVBQzlCLGFBQStCO1FBRnZCLG9CQUFlLEdBQWYsZUFBZSxDQUF1QjtRQUN0QyxRQUFHLEdBQUgsR0FBRyxDQUFtQjtRQW5CdkIsV0FBTSxHQUF3QixFQUFFLENBQUM7UUFDakMsYUFBUSxHQUFHLEtBQUssQ0FBQztRQUNqQixTQUFJ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LFdBQVcsRUFBRSxVQUFVLENBQUMsR0FBRyxFQUFFLGdCQUFnQixDQUFDOzRCQUM5QyxLQUFLLEVBQUUsSUFBSTt5QkFDWjtxQkFDRixjQUNXLElBQUksV0FDUDt3QkFDUCxXQUFXO3dCQUNYLFlBQVk7d0JBQ1osT0FBTzt3QkFDUCxTQUFTO3dCQUNULFFBQVE7d0JBQ1IsYUFBYTt3QkFDYixTQUFTO3dCQUNULE9BQU87d0JBQ1AsYUFBYTt3QkFDYixvQkFBb0I7cUJBQ3JCO3lKQUd1QixTQUFTO3NCQUFoQyxTQUFTO3VCQUFDLFdBQVc7Z0JBRWIsTUFBTTtzQkFBZCxLQUFLO2dCQUNHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBQ0csSUFBSTtzQkFBWixLQUFLO2dCQUNHLElBQUk7c0JBQVosS0FBSztnQkFDRyxNQUFNO3NCQUFkLEtBQUs7Z0JBRUksVUFBVTtzQkFBbkIsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIENoYW5nZURldGVjdGlvblN0cmF0ZWd5LFxuICBDaGFuZ2VEZXRlY3RvclJlZixcbiAgQ29tcG9uZW50LFxuICBFbGVtZW50UmVmLFxuICBFdmVudEVtaXR0ZXIsXG4gIElucHV0LFxuICBPbkNoYW5nZXMsXG4gIE91dHB1dCxcbiAgU2ltcGxlQ2hhbmdlcyxcbiAgVmlld0NoaWxkLFxuICBWaWV3RW5jYXBzdWxhdGlvbixcbiAgZm9yd2FyZFJlZixcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBDb250cm9sVmFsdWVBY2Nlc3NvciwgRm9ybXNNb2R1bGUsIE5HX1ZBTFVFX0FDQ0VTU09SIH0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHsgTWF0SWNvbkJ1dHRvbiB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2J1dHRvbic7XG5pbXBvcnQgeyBNYXRGb3JtRmllbGQsIE1hdEhpbnQsIE1hdFByZWZpeCwgTWF0U3VmZml4IH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvZm9ybS1maWVsZCc7XG5pbXBvcnQgeyBNYXRJY29uIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvaWNvbic7XG5pbXBvcnQgeyBNYXRJbnB1dCB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2lucHV0JztcbmltcG9ydCB7IGZpbmFsaXplIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuXG5pbXBvcnQgeyBHdWlGaWVsZExhYmVsIH0gZnJvbSAnLi4vZmllbGQtbGFiZWwvZmllbGQtbGFiZWwnO1xuaW1wb3J0IHsgR3VpSWNvbnNSZWdpc3RyeSB9IGZyb20gJy4uL2d1aS1pY29ucyc7XG5pbXBvcnQgeyBHdWlJY29uQnV0dG9uV3JhcHBlciB9IGZyb20gJy4uL2ljb24tYnV0dG9uLXdyYXBwZXIvaWNvbi1idXR0b24td3JhcHBlcic7XG5pbXBvcnQgeyBHdWlDb250cm9sIH0gZnJvbSAnLi4vaW50ZXJmYWNlJztcbmltcG9ydCB7IEd1aUZpbGVVcGxvYWRlckNvbmZpZyB9IGZyb20gJy4vZmlsZS11cGxvYWRlci1jb25maWcnO1xuXG5leHBvcnQgdHlwZSBGaWxlVXBsb2FkVHlwZSA9ICdpbWFnZScgfCAndmlkZW8nIHwgJ2F1ZGlvJyB8ICcqJztcblxuZXhwb3J0IGludGVyZmFjZSBGaWxlVXBsb2FkQ29udGVudCB7XG4gIGRhdGE6IEZpbGU7XG4gIHByb2dyZXNzOiBudW1iZXI7XG4gIGluUHJvZ3Jlc3M6IGJvb2xlYW47XG59XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2d1aS1maWxlLXVwbG9hZGVyJyxcbiAgdGVtcGxhdGVVcmw6ICcuL2ZpbGUtdXBsb2FkZXIuaHRtbCcsXG4gIHN0eWxlVXJsOiAnLi9maWxlLXVwbG9hZGVyLnNjc3MnLFxuICBob3N0OiB7XG4gICAgY2xhc3M6ICdndWktZmllbGQgZ3VpLWZpbGUtdXBsb2FkZXInLFxuICB9LFxuICBlbmNhcHN1bGF0aW9uOiBWaWV3RW5jYXBzdWxhdGlvbi5Ob25lLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbiAgcHJvdmlkZXJzOiBbXG4gICAge1xuICAgICAgcHJvdmlkZTogTkdfVkFMVUVfQUNDRVNTT1IsXG4gICAgICB1c2VFeGlzdGluZzogZm9yd2FyZFJlZigoKSA9PiBHdWlGaWxlVXBsb2FkZXIpLFxuICAgICAgbXVsdGk6IHRydWUsXG4gICAgfSxcbiAgXSxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW1xuICAgIEZvcm1zTW9kdWxlLFxuICAgIE1hdEZvcm1GaWVsZCxcbiAgICBNYXRJY29uLFxuICAgIE1hdFByZWZpeCxcbiAgICBNYXRJbnB1dCxcbiAgICBNYXRJY29uQnV0dG9uLFxuICAgIE1hdFN1ZmZpeCxcbiAgICBNYXRIaW50LFxuICAgIEd1aUZpZWxkTGFiZWwsXG4gICAgR3VpSWNvbkJ1dHRvbldyYXBwZXIsXG4gIF0sXG59KVxuZXhwb3J0IGNsYXNzIEd1aUZpbGVVcGxvYWRlciBpbXBsZW1lbnRzIENvbnRyb2xWYWx1ZUFjY2Vzc29yLCBPbkNoYW5nZXMge1xuICBAVmlld0NoaWxkKCdmaWxlSW5wdXQnKSBmaWxlSW5wdXQhOiBFbGVtZW50UmVmPEhUTUxJbnB1dEVsZW1lbnQ+O1xuXG4gIEBJbnB1dCgpIGNvbmZpZzogUGFydGlhbDxHdWlDb250cm9sPiA9IHt9O1xuICBASW5wdXQoKSBkaXNhYmxlZCA9IGZhbHNlO1xuICBASW5wdXQoKSB0eXBlOiBGaWxlVXBsb2FkVHlwZSA9ICcqJztcbiAgQElucHV0KCkgbmFtZSA9ICcnO1xuICBASW5wdXQoKSBhY2NlcHQgPSAnJztcblxuICBAT3V0cHV0KCkgZmlsZUNoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8c3RyaW5nPigpO1xuXG4gIC8vIGZpbGUgdXJsIHRoYXQgcmV0dXJuZWQgZnJvbSB0aGUgc2VydmVyXG4gIHVybCA9ICcnO1xuXG4gIC8vIGZpbGUgdG8gdXBsb2FkXG4gIGZpbGVVcGxvYWQhOiBGaWxlVXBsb2FkQ29udGVudDtcblxuICBwcml2YXRlIG9uQ2hhbmdlOiAodmFsdWU6IHN0cmluZykgPT4gdm9pZCA9ICgpID0+IHt9O1xuICBwcml2YXRlIG9uVG91Y2hlZDogKCkgPT4gdm9pZCA9ICgpID0+IHt9O1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgZmlsZVVwbG9hZGVyQ2ZnOiBHdWlGaWxlVXBsb2FkZXJDb25maWcsXG4gICAgcHJpdmF0ZSBjZHI6IENoYW5nZURldGVjdG9yUmVmLFxuICAgIGljb25zUmVnaXN0cnk6IEd1aUljb25zUmVnaXN0cnlcbiAgKSB7XG4gICAgaWNvbnNSZWdpc3RyeS5hZGQoJ2xpbmsnLCAnY2xlYXInLCAnZmlsZScsICd1cGxvYWQnKTtcbiAgfVxuXG4gIG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpOiB2b2lkIHtcbiAgICBpZiAoY2hhbmdlc1snY29uZmlnJ10gfHwgY2hhbmdlc1snYWNjZXB0J10gfHwgY2hhbmdlc1sndHlwZSddKSB7XG4gICAgICB0aGlzLmFjY2VwdCA9IHRoaXMuY29uZmlnLmFjY2VwdCB8fCB0aGlzLmFjY2VwdCB8fCB0aGlzLnR5cGUgKyAnLyonO1xuICAgIH1cbiAgfVxuXG4gIHdyaXRlVmFsdWUodmFsdWU6IHN0cmluZykge1xuICAgIHRoaXMudXJsID0gdmFsdWU7XG4gICAgdGhpcy5jZHIubWFya0ZvckNoZWNrKCk7XG4gIH1cblxuICByZWdpc3Rlck9uQ2hhbmdlKGZuOiAodmFsdWU6IHN0cmluZykgPT4gdm9pZCkge1xuICAgIHRoaXMub25DaGFuZ2UgPSBmbjtcbiAgfVxuXG4gIHJlZ2lzdGVyT25Ub3VjaGVkKGZuOiAoKSA9PiB2b2lkKSB7XG4gICAgdGhpcy5vblRvdWNoZWQgPSBmbjtcbiAgfVxuXG4gIHNldERpc2FibGVkU3RhdGUoaXNEaXNhYmxlZDogYm9vbGVhbikge1xuICAgIHRoaXMuZGlzYWJsZWQgPSBpc0Rpc2FibGVkO1xuICAgIHRoaXMuY2RyLm1hcmtGb3JDaGVjaygpO1xuICB9XG5cbiAgdXBsb2FkKGZpbGVVcGxvYWQ6IEZpbGVVcGxvYWRDb250ZW50KSB7XG4gICAgY29uc3QgZm9ybURhdGEgPSBuZXcgRm9ybURhdGEoKTtcbiAgICBmb3JtRGF0YS5hcHBlbmQoJ2ZpbGUnLCBmaWxlVXBsb2FkLmRhdGEpO1xuXG4gICAgZmlsZVVwbG9hZC5pblByb2dyZXNzID0gdHJ1ZTtcblxuICAgIHRoaXMuZmlsZVVwbG9hZGVyQ2ZnXG4gICAgICAudXBsb2FkKGZvcm1EYXRhLCB0aGlzLmNvbmZpZylcbiAgICAgIC5waXBlKFxuICAgICAgICBmaW5hbGl6ZSgoKSA9PiB7XG4gICAgICAgICAgZmlsZVVwbG9hZC5pblByb2dyZXNzID0gZmFsc2U7XG4gICAgICAgIH0pXG4gICAgICApXG4gICAgICAuc3Vic2NyaWJlKHJlc3VsdCA9PiB7XG4gICAgICAgIGlmIChyZXN1bHQpIHtcbiAgICAgICAgICB0aGlzLnVybCA9IHJlc3VsdDtcbiAgICAgICAgICB0aGlzLmNkci5kZXRlY3RDaGFuZ2VzKCk7XG5cbiAgICAgICAgICB0aGlzLm9uQ2hhbmdlKHRoaXMudXJsKTtcbiAgICAgICAgICB0aGlzLm9uVG91Y2hlZCgpO1xuXG4gICAgICAgICAgdGhpcy5maWxlQ2hhbmdlLmVtaXQodGhpcy51cmwpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgfVxuXG4gIG9uVXJsQ2hhbmdlKGU6IEV2ZW50KSB7XG4gICAgdGhpcy51cmwgPSAoZS50YXJnZXQgYXMgSFRNTElucHV0RWxlbWVudCkudmFsdWU7XG5cbiAgICB0aGlzLm9uQ2hhbmdlKHRoaXMudXJsKTtcblxuICAgIHRoaXMuZmlsZUNoYW5nZS5lbWl0KHRoaXMudXJsKTtcbiAgfVxuXG4gIG9uRmlsZUNoYW5nZShlOiBFdmVudCkge1xuICAgIHRoaXMuZmlsZVVwbG9hZCA9IHtcbiAgICAgIGRhdGE6IChlLnRhcmdldCBhcyBIVE1MSW5wdXRFbGVtZW50KS5maWxlcyFbMF0sXG4gICAgICBpblByb2dyZXNzOiBmYWxzZSxcbiAgICAgIHByb2dyZXNzOiAwLFxuICAgIH07XG5cbiAgICB0aGlzLnVwbG9hZCh0aGlzLmZpbGVVcGxvYWQpO1xuXG4gICAgLy8gcmVzZXQgaW5wdXQgdmFsdWVcbiAgICAoZS50YXJnZXQgYXMgSFRNTElucHV0RWxlbWVudCkudmFsdWUgPSAnJztcbiAgfVxuXG4gIG9uQmx1cigpIHtcbiAgICB0aGlzLm9uVG91Y2hlZCgpO1xuICB9XG5cbiAgb25DbGVhcigpIHtcbiAgICB0aGlzLnVybCA9ICcnO1xuXG4gICAgdGhpcy5vbkNoYW5nZSh0aGlzLnVybCk7XG4gICAgdGhpcy5vblRvdWNoZWQoKTtcblxuICAgIHRoaXMuZmlsZUNoYW5nZS5lbWl0KHRoaXMudXJsKTtcbiAgfVxufVxuIiwiPG1hdC1mb3JtLWZpZWxkPlxuICA8Z3VpLWljb24tYnV0dG9uLXdyYXBwZXIgbWF0UHJlZml4PlxuICAgIDxtYXQtaWNvbiBzdmdJY29uPVwibGlua1wiIC8+XG4gIDwvZ3VpLWljb24tYnV0dG9uLXdyYXBwZXI+XG5cbiAgPGlucHV0XG4gICAgbWF0SW5wdXRcbiAgICB0eXBlPVwidGV4dFwiXG4gICAgW25nTW9kZWxdPVwidXJsXCJcbiAgICBbZGlzYWJsZWRdPVwiZGlzYWJsZWRcIlxuICAgIFtwbGFjZWhvbGRlcl09XCJjb25maWcucGxhY2Vob2xkZXIgfHwgJydcIlxuICAgIChjaGFuZ2UpPVwib25VcmxDaGFuZ2UoJGV2ZW50KVwiXG4gICAgKGJsdXIpPVwib25CbHVyKClcIlxuICAvPlxuXG4gIEBpZiAodXJsKSB7XG4gICAgPGd1aS1pY29uLWJ1dHRvbi13cmFwcGVyIG1hdFN1ZmZpeD5cbiAgICAgIDxidXR0b24gbWF0LWljb24tYnV0dG9uIHR5cGU9XCJidXR0b25cIiBjb2xvcj1cIndhcm5cIiBbZGlzYWJsZWRdPVwiZGlzYWJsZWRcIiAoY2xpY2spPVwib25DbGVhcigpXCI+XG4gICAgICAgIDxtYXQtaWNvbiBzdmdJY29uPVwiY2xlYXJcIiAvPlxuICAgICAgPC9idXR0b24+XG4gICAgPC9ndWktaWNvbi1idXR0b24td3JhcHBlcj5cbiAgfVxuPC9tYXQtZm9ybS1maWVsZD5cblxuPGZpZ3VyZSBjbGFzcz1cImd1aS1maWxlLWNvbnRlbnRcIj5cbiAgQGlmICh1cmwpIHtcbiAgICBAc3dpdGNoICh0eXBlKSB7XG4gICAgICBAY2FzZSAoJ2ltYWdlJykge1xuICAgICAgICA8aW1nIFtzcmNdPVwidXJsXCIgYWx0PVwiXCIgLz5cbiAgICAgIH1cbiAgICAgIEBjYXNlICgndmlkZW8nKSB7XG4gICAgICAgIDx2aWRlbyBbc3JjXT1cInVybFwiPjwvdmlkZW8+XG4gICAgICB9XG4gICAgICBAY2FzZSAoJ2F1ZGlvJykge1xuICAgICAgICA8YXVkaW8gW3NyY109XCJ1cmxcIiBjb250cm9scz48L2F1ZGlvPlxuICAgICAgfVxuICAgICAgQGRlZmF1bHQge1xuICAgICAgICA8bWF0LWljb24gc3ZnSWNvbj1cImZpbGVcIiAvPlxuICAgICAgfVxuICAgIH1cbiAgfSBAZWxzZSB7XG4gICAgPGRpdiBjbGFzcz1cImd1aS1maWxlLXBsYWNlaG9sZGVyXCI+XG4gICAgICA8bWF0LWljb24gc3ZnSWNvbj1cInVwbG9hZFwiIC8+XG4gICAgPC9kaXY+XG4gIH1cblxuICA8aW5wdXRcbiAgICAjZmlsZUlucHV0XG4gICAgdHlwZT1cImZpbGVcIlxuICAgIFthY2NlcHRdPVwiYWNjZXB0XCJcbiAgICBbbmFtZV09XCJuYW1lXCJcbiAgICBbZGlzYWJsZWRdPVwiZGlzYWJsZWRcIlxuICAgIChjaGFuZ2UpPVwib25GaWxlQ2hhbmdlKCRldmVudClcIlxuICAvPlxuPC9maWd1cmU+XG5cbkBpZiAoY29uZmlnLnBhcmVudFR5cGUgPT09ICdpbmxpbmUnKSB7XG4gIDxtYXQtaGludD5cbiAgICA8Z3VpLWZpZWxkLWxhYmVsIFtjb25maWddPVwiY29uZmlnXCIgLz5cbiAgPC9tYXQtaGludD5cbn1cbiJdfQ==