UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

227 lines 57 kB
import { Component, Input, Optional, ViewChild } from '@angular/core';
import { ControlContainer, FormBuilder, NgForm, ReactiveFormsModule, Validators } from '@angular/forms';
import { DatapointSelectorModule } from '@c8y/ngx-components/datapoint-selector';
import { WidgetConfigComponent } from '@c8y/ngx-components/context-dashboard';
import { CommonModule, DynamicComponentComponent, FormGroupComponent, FormsModule, gettext, MeasurementRealtimeService, MessagesComponent } from '@c8y/ngx-components';
import { Subject, takeUntil } from 'rxjs';
import { defaultWidgetIds } from '@c8y/ngx-components/widgets/definitions';
import { RadialGaugeViewComponent } from '../radial-gauge/radial-gauge.component';
import { InfoGaugeCurrentMeasurementPipe } from '../current-measurement.pipe';
import { GAUGE_PRESET_NAMES, GAUGE_PRESETS } from './gauge.model';
import { EditorComponent, MonacoEditorMarkerValidatorDirective } from '@c8y/ngx-components/editor';
import { CollapseModule } from 'ngx-bootstrap/collapse';
import { PresetPreviewComponent } from './preset-preview/preset-preview.component';
import * as i0 from "@angular/core";
import * as i1 from "@angular/forms";
import * as i2 from "@c8y/ngx-components/context-dashboard";
import * as i3 from "@c8y/ngx-components";
import * as i4 from "@angular/common";
import * as i5 from "ngx-bootstrap/collapse";
import * as i6 from "@c8y/ngx-components/datapoint-selector";
function ensureAtLeastOneDatapointSelectedAndActive(datapointListAttributes) {
    return (control) => {
        const formValue = control.value;
        for (const listAttributeName of datapointListAttributes) {
            const valueForList = formValue[listAttributeName];
            if (!valueForList || !Array.isArray(valueForList)) {
                continue;
            }
            if (valueForList.find(dp => dp.__active)) {
                return null;
            }
        }
        return { noActiveDatapoint: true };
    };
}
export class InfoGaugeWidgetConfigComponent {
    constructor(formBuilder, form, widgetConfig, dynamicComponent) {
        this.formBuilder = formBuilder;
        this.form = form;
        this.widgetConfig = widgetConfig;
        this.dynamicComponent = dynamicComponent;
        this.datapointSelectionConfig = {};
        this.defaultFormOptions = {
            showRedRange: true,
            showYellowRange: true,
            showRange: true,
            selectableChartRenderTypes: []
        };
        this.showAdvancedOptions = false;
        this.gaugeOptionsString = '';
        this.showAdvancedOptionsLabel = gettext('Show advanced options');
        this.hideAdvancedOptionsLabel = gettext('Hide advanced options');
        this.ALL_GAUGE_PRESETS = [...GAUGE_PRESETS];
        this.limits = {
            numberOfDecimalPlacesMax: 10,
            numberOfDecimalPlacesMin: 0
        };
        this.GAUGE_PRESETS = GAUGE_PRESETS;
        this.destroy$ = new Subject();
    }
    onBeforeSave(config) {
        if (this.formGroup.valid) {
            Object.assign(config, this.formGroup.value);
            Object.assign(config, {
                gaugeOptions: this.gaugeOptions,
                selectedPresetId: this.isInfoGauge ? null : this.getPresetId(this.gaugeOptions)
            });
            return true;
        }
        return false;
    }
    ngOnInit() {
        if (this.widgetConfig.context?.id) {
            this.datapointSelectionConfig.contextAsset = this.widgetConfig?.context;
        }
        this.initForm();
        if (this.config?.datapointsLabels) {
            this.formGroup.patchValue({ datapointsLabels: this.config.datapointsLabels });
        }
        if (this.config?.datapointsGauge) {
            this.formGroup.patchValue({ datapointsGauge: this.config.datapointsGauge });
        }
        if (typeof this.config?.fractionSize === 'number' && !Number.isNaN(this.config?.fractionSize)) {
            this.formGroup.patchValue({ fractionSize: this.config.fractionSize });
        }
        this.isInfoGauge = this.dynamicComponent?.componentId === defaultWidgetIds.INFO_GAUGE;
        const isRadialGauge = !this.isInfoGauge;
        if (isRadialGauge) {
            this.setupRadialGauge();
        }
        this.formGroup.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(value => {
            if (value.datapointsGauge) {
                this.activeDatapointGauge = value.datapointsGauge.find(dp => dp.__active);
            }
        });
    }
    ngOnDestroy() {
        this.destroy$.next();
        this.destroy$.complete();
    }
    onRadioPresetChange(presetId) {
        // Find the selected preset
        const selectedPreset = this.ALL_GAUGE_PRESETS.find(preset => preset.id === presetId);
        if (selectedPreset) {
            this.gaugeOptions = { ...selectedPreset }; // Update gaugeOptions with the selected preset
            this.gaugeOptionsString = JSON.stringify(this.gaugeOptions, null, 2);
            // If the selected preset is not custom, hide advanced options
            if (selectedPreset.id !== 'custom') {
                this.showAdvancedOptions = false;
            }
            // Update the form control value
            this.formGroup.patchValue({ gaugePresetId: selectedPreset.id });
        }
    }
    async assignSchema() {
        const { schema } = await import('c8y-schema-loader?interfaceName=GaugeOptions!@c8y/ngx-components/widgets/implementations/info-gauge');
        this.editorComponent.monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
            validate: true,
            schemas: [
                {
                    uri: 'gauge-schema.json',
                    schema: schema,
                    fileMatch: ['*']
                }
            ],
            enableSchemaRequest: false,
            allowComments: false
        });
    }
    gaugeJSONChange(value) {
        try {
            const parsedOptions = JSON.parse(value);
            // Check if the parsed options match any existing preset
            const matchingPreset = this.GAUGE_PRESETS.find(preset => JSON.stringify(preset) === JSON.stringify(parsedOptions));
            if (matchingPreset) {
                this.formGroup.patchValue({ gaugePresetId: matchingPreset.id });
            }
            else {
                this.gaugeOptions = parsedOptions;
                this.gaugeOptions.name = GAUGE_PRESET_NAMES.CUSTOM;
                this.gaugeOptions.id = 'custom';
                const customPresetExists = this.ALL_GAUGE_PRESETS.some(preset => preset.id === 'custom');
                if (!customPresetExists) {
                    this.ALL_GAUGE_PRESETS = [...this.ALL_GAUGE_PRESETS, this.gaugeOptions];
                }
                else {
                    this.ALL_GAUGE_PRESETS = this.ALL_GAUGE_PRESETS.map(preset => preset.id === 'custom' ? this.gaugeOptions : preset);
                }
                this.formGroup.patchValue({ gaugePresetId: 'custom' });
            }
        }
        catch (error) {
            console.error('Invalid JSON:', error);
        }
    }
    trackByFn(_index, item) {
        return item.id;
    }
    getPresetId(gaugeOptions) {
        const matchingPreset = this.GAUGE_PRESETS.find(preset => preset.id === gaugeOptions.id);
        return matchingPreset ? matchingPreset.id : 'custom';
    }
    setupRadialGauge() {
        if (this.config.datapoints && !this.config.datapointsGauge) {
            this.config.datapointsGauge = this.config.datapoints;
        }
        this.gaugeOptions = this.config.gaugeOptions || { ...this.GAUGE_PRESETS[0] };
        if (this.config.selectedPresetId === 'custom') {
            this.gaugeOptions.name = GAUGE_PRESET_NAMES.CUSTOM;
            this.gaugeOptions.id = 'custom';
            this.ALL_GAUGE_PRESETS = [...this.GAUGE_PRESETS, this.config.gaugeOptions];
            this.formGroup.patchValue({ gaugePresetId: 'custom' });
        }
        this.gaugeOptionsString = JSON.stringify(this.gaugeOptions, null, 2);
    }
    initForm() {
        this.formGroup = this.createForm();
        this.form.form.addControl('config', this.formGroup);
        this.formGroup.patchValue(this.config);
    }
    createForm() {
        return this.formBuilder.group({
            fractionSize: [
                2,
                [
                    Validators.required,
                    Validators.min(this.limits.numberOfDecimalPlacesMin),
                    Validators.max(this.limits.numberOfDecimalPlacesMax)
                ]
            ],
            datapointsLabels: this.formBuilder.control(new Array(), []),
            datapointsGauge: this.formBuilder.control(new Array(), []),
            gaugePresetId: [this.config.selectedPresetId || 'default']
        }, {
            validators: [
                ensureAtLeastOneDatapointSelectedAndActive(['datapointsLabels', 'datapointsGauge'])
            ]
        });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: InfoGaugeWidgetConfigComponent, deps: [{ token: i1.FormBuilder }, { token: i1.NgForm }, { token: i2.WidgetConfigComponent }, { token: i3.DynamicComponentComponent, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: InfoGaugeWidgetConfigComponent, isStandalone: true, selector: "c8y-info-gauge-widget-config", inputs: { config: "config" }, providers: [MeasurementRealtimeService], viewQueries: [{ propertyName: "editorComponent", first: true, predicate: EditorComponent, descendants: true }], ngImport: i0, template: "<form\n  class=\"fit-h\"\n  [formGroup]=\"formGroup\"\n>\n  <div\n    class=\"m-l-16 m-r-16\"\n    *ngIf=\"isInfoGauge; else radialGauge\"\n  >\n    <div class=\"row\">\n      <div class=\"col-md-6\">\n        <c8y-form-group class=\"p-t-8\">\n          <label translate>Decimal places</label>\n          <input\n            class=\"form-control\"\n            name=\"fractionSize\"\n            type=\"number\"\n            formControlName=\"fractionSize\"\n            step=\"1\"\n          />\n          <c8y-messages [show]=\"formGroup.controls.fractionSize.errors\"></c8y-messages>\n        </c8y-form-group>\n      </div>\n      <div class=\"col-md-6 p-t-8\">\n        <div\n          class=\"alert alert-info\"\n          role=\"alert\"\n          *ngIf=\"formGroup.errors?.noActiveDatapoint\"\n          translate\n        >\n          At least one data point for the labels or the gauge needs to be selected.\n        </div>\n      </div>\n    </div>\n\n    <div class=\"row\">\n      <div class=\"col-md-6\">\n        <c8y-datapoint-selection-list\n          class=\"bg-inherit separator-top p-t-16 d-block\"\n          listTitle=\"{{ 'Multiple label and value pairs' | translate }}\"\n          name=\"datapoints\"\n          [defaultFormOptions]=\"defaultFormOptions\"\n          [config]=\"datapointSelectionConfig\"\n          [minActiveCount]=\"0\"\n          formControlName=\"datapointsLabels\"\n        ></c8y-datapoint-selection-list>\n      </div>\n      <div class=\"col-md-6\">\n        <c8y-datapoint-selection-list\n          class=\"bg-inherit separator-top p-t-16 d-block\"\n          listTitle=\"{{ 'Gauge`display`' | translate }}\"\n          name=\"datapoints\"\n          [defaultFormOptions]=\"defaultFormOptions\"\n          [config]=\"datapointSelectionConfig\"\n          [minActiveCount]=\"0\"\n          [maxActiveCount]=\"1\"\n          formControlName=\"datapointsGauge\"\n        ></c8y-datapoint-selection-list>\n      </div>\n    </div>\n  </div>\n\n  <ng-template #radialGauge>\n    <div class=\"d-flex-md fit-h\">\n      <div class=\"col-md-6 inner-scroll p-0 bg-component\">\n        <c8y-datapoint-selection-list\n          class=\"bg-inherit d-block\"\n          listTitle=\"{{ 'Data points`display`' | translate }}\"\n          name=\"datapoints\"\n          [defaultFormOptions]=\"defaultFormOptions\"\n          [config]=\"datapointSelectionConfig\"\n          [minActiveCount]=\"0\"\n          [maxActiveCount]=\"1\"\n          formControlName=\"datapointsGauge\"\n        ></c8y-datapoint-selection-list>\n\n        <c8y-form-group class=\"p-t-8 p-r-16 p-l-16\">\n          <label translate>Decimal places</label>\n          <input\n            class=\"form-control\"\n            name=\"fractionSize\"\n            type=\"number\"\n            formControlName=\"fractionSize\"\n            step=\"1\"\n          />\n          <c8y-messages [show]=\"formGroup.controls.fractionSize.errors\"></c8y-messages>\n        </c8y-form-group>\n\n        <div class=\"form-group p-r-16 p-l-16 m-b-0\">\n          <legend>\n            {{ 'Presets' | translate }}\n          </legend>\n          <c8y-preset-preview\n            formControlName=\"gaugePresetId\"\n            [ALL_GAUGE_PRESETS]=\"ALL_GAUGE_PRESETS\"\n            (onPresetChange)=\"onRadioPresetChange($event)\"\n          ></c8y-preset-preview>\n        </div>\n\n        <div class=\"p-16\">\n          <button\n            class=\"btn btn-default\"\n            aria-controls=\"advancedCollapse\"\n            [attr.aria-expanded]=\"showAdvancedOptions\"\n            type=\"button\"\n            (click)=\"showAdvancedOptions = !showAdvancedOptions\"\n          >\n            {{ showAdvancedOptions ? hideAdvancedOptionsLabel : showAdvancedOptionsLabel }}\n          </button>\n        </div>\n\n        <div\n          class=\"collapse\"\n          id=\"advancedCollapse\"\n          [collapse]=\"!showAdvancedOptions\"\n          [isAnimated]=\"true\"\n        >\n          <div\n            class=\"fit-h\"\n            style=\"min-height: 400px\"\n          >\n            <c8y-editor\n              [ngModel]=\"gaugeOptionsString\"\n              (ngModelChange)=\"gaugeJSONChange($event)\"\n              (editorInit)=\"assignSchema()\"\n              [ngModelOptions]=\"{ standalone: true }\"\n              monacoEditorMarkerValidator\n            ></c8y-editor>\n          </div>\n        </div>\n      </div>\n      <div\n        class=\"col-md-6\"\n        *ngIf=\"\n          this.formGroup.value.datapointsGauge.length > 0 && activeDatapointGauge;\n          else emptyState\n        \"\n      >\n        <!-- Radial Gauge -->\n        <c8y-radial-gauge\n          class=\"c8y-radial-gauge--square\"\n          *ngIf=\"activeDatapointGauge | infoGaugeCurrentMeasurement | async as measurement\"\n          [activeDatapointGauge]=\"activeDatapointGauge\"\n          [measurement]=\"measurement\"\n          [fractionSize]=\"formGroup.value.fractionSize\"\n          [gaugeOptions]=\"gaugeOptions\"\n          [selectedPresetId]=\"formGroup.value.gaugePresetId\"\n        ></c8y-radial-gauge>\n      </div>\n    </div>\n  </ng-template>\n</form>\n\n<ng-template #emptyState>\n  <div class=\"col-md-6 d-col a-i-start j-c-center\">\n    <c8y-ui-empty-state\n      [icon]=\"'c8y-data-points'\"\n      [title]=\"'Please select a data point first.' | translate\"\n      [horizontal]=\"false\"\n    ></c8y-ui-empty-state>\n  </div>\n</ng-template>\n", dependencies: [{ kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: i3.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "pipe", type: i3.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i3.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "ngmodule", type: FormsModule }, { 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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "ngmodule", type: CollapseModule }, { kind: "directive", type: i5.CollapseDirective, selector: "[collapse]", inputs: ["display", "isAnimated", "collapse"], outputs: ["collapsed", "collapses", "expanded", "expands"], exportAs: ["bs-collapse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { 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: "component", type: EditorComponent, selector: "c8y-editor", inputs: ["editorOptions"], outputs: ["editorInit"] }, { kind: "directive", type: MonacoEditorMarkerValidatorDirective, selector: "c8y-editor [monacoEditorMarkerValidator]" }, { kind: "ngmodule", type: DatapointSelectorModule }, { kind: "component", type: i6.DatapointSelectionListComponent, selector: "c8y-datapoint-selection-list", inputs: ["actions", "allowDragAndDrop", "config", "defaultFormOptions", "maxActiveCount", "minActiveCount", "resolveContext", "listTitle"], outputs: ["isValid", "change"] }, { kind: "component", type: RadialGaugeViewComponent, selector: "c8y-radial-gauge", inputs: ["activeDatapointGauge", "measurement", "fractionSize", "gaugeOptions", "selectedPresetId"] }, { kind: "pipe", type: InfoGaugeCurrentMeasurementPipe, name: "infoGaugeCurrentMeasurement" }, { kind: "component", type: PresetPreviewComponent, selector: "c8y-preset-preview", inputs: ["ALL_GAUGE_PRESETS"], outputs: ["onPresetChange"] }], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: InfoGaugeWidgetConfigComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-info-gauge-widget-config', viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], providers: [MeasurementRealtimeService], standalone: true, imports: [
                        FormGroupComponent,
                        MessagesComponent,
                        CommonModule,
                        FormsModule,
                        CollapseModule,
                        ReactiveFormsModule,
                        EditorComponent,
                        MonacoEditorMarkerValidatorDirective,
                        DatapointSelectorModule,
                        RadialGaugeViewComponent,
                        InfoGaugeCurrentMeasurementPipe,
                        PresetPreviewComponent
                    ], template: "<form\n  class=\"fit-h\"\n  [formGroup]=\"formGroup\"\n>\n  <div\n    class=\"m-l-16 m-r-16\"\n    *ngIf=\"isInfoGauge; else radialGauge\"\n  >\n    <div class=\"row\">\n      <div class=\"col-md-6\">\n        <c8y-form-group class=\"p-t-8\">\n          <label translate>Decimal places</label>\n          <input\n            class=\"form-control\"\n            name=\"fractionSize\"\n            type=\"number\"\n            formControlName=\"fractionSize\"\n            step=\"1\"\n          />\n          <c8y-messages [show]=\"formGroup.controls.fractionSize.errors\"></c8y-messages>\n        </c8y-form-group>\n      </div>\n      <div class=\"col-md-6 p-t-8\">\n        <div\n          class=\"alert alert-info\"\n          role=\"alert\"\n          *ngIf=\"formGroup.errors?.noActiveDatapoint\"\n          translate\n        >\n          At least one data point for the labels or the gauge needs to be selected.\n        </div>\n      </div>\n    </div>\n\n    <div class=\"row\">\n      <div class=\"col-md-6\">\n        <c8y-datapoint-selection-list\n          class=\"bg-inherit separator-top p-t-16 d-block\"\n          listTitle=\"{{ 'Multiple label and value pairs' | translate }}\"\n          name=\"datapoints\"\n          [defaultFormOptions]=\"defaultFormOptions\"\n          [config]=\"datapointSelectionConfig\"\n          [minActiveCount]=\"0\"\n          formControlName=\"datapointsLabels\"\n        ></c8y-datapoint-selection-list>\n      </div>\n      <div class=\"col-md-6\">\n        <c8y-datapoint-selection-list\n          class=\"bg-inherit separator-top p-t-16 d-block\"\n          listTitle=\"{{ 'Gauge`display`' | translate }}\"\n          name=\"datapoints\"\n          [defaultFormOptions]=\"defaultFormOptions\"\n          [config]=\"datapointSelectionConfig\"\n          [minActiveCount]=\"0\"\n          [maxActiveCount]=\"1\"\n          formControlName=\"datapointsGauge\"\n        ></c8y-datapoint-selection-list>\n      </div>\n    </div>\n  </div>\n\n  <ng-template #radialGauge>\n    <div class=\"d-flex-md fit-h\">\n      <div class=\"col-md-6 inner-scroll p-0 bg-component\">\n        <c8y-datapoint-selection-list\n          class=\"bg-inherit d-block\"\n          listTitle=\"{{ 'Data points`display`' | translate }}\"\n          name=\"datapoints\"\n          [defaultFormOptions]=\"defaultFormOptions\"\n          [config]=\"datapointSelectionConfig\"\n          [minActiveCount]=\"0\"\n          [maxActiveCount]=\"1\"\n          formControlName=\"datapointsGauge\"\n        ></c8y-datapoint-selection-list>\n\n        <c8y-form-group class=\"p-t-8 p-r-16 p-l-16\">\n          <label translate>Decimal places</label>\n          <input\n            class=\"form-control\"\n            name=\"fractionSize\"\n            type=\"number\"\n            formControlName=\"fractionSize\"\n            step=\"1\"\n          />\n          <c8y-messages [show]=\"formGroup.controls.fractionSize.errors\"></c8y-messages>\n        </c8y-form-group>\n\n        <div class=\"form-group p-r-16 p-l-16 m-b-0\">\n          <legend>\n            {{ 'Presets' | translate }}\n          </legend>\n          <c8y-preset-preview\n            formControlName=\"gaugePresetId\"\n            [ALL_GAUGE_PRESETS]=\"ALL_GAUGE_PRESETS\"\n            (onPresetChange)=\"onRadioPresetChange($event)\"\n          ></c8y-preset-preview>\n        </div>\n\n        <div class=\"p-16\">\n          <button\n            class=\"btn btn-default\"\n            aria-controls=\"advancedCollapse\"\n            [attr.aria-expanded]=\"showAdvancedOptions\"\n            type=\"button\"\n            (click)=\"showAdvancedOptions = !showAdvancedOptions\"\n          >\n            {{ showAdvancedOptions ? hideAdvancedOptionsLabel : showAdvancedOptionsLabel }}\n          </button>\n        </div>\n\n        <div\n          class=\"collapse\"\n          id=\"advancedCollapse\"\n          [collapse]=\"!showAdvancedOptions\"\n          [isAnimated]=\"true\"\n        >\n          <div\n            class=\"fit-h\"\n            style=\"min-height: 400px\"\n          >\n            <c8y-editor\n              [ngModel]=\"gaugeOptionsString\"\n              (ngModelChange)=\"gaugeJSONChange($event)\"\n              (editorInit)=\"assignSchema()\"\n              [ngModelOptions]=\"{ standalone: true }\"\n              monacoEditorMarkerValidator\n            ></c8y-editor>\n          </div>\n        </div>\n      </div>\n      <div\n        class=\"col-md-6\"\n        *ngIf=\"\n          this.formGroup.value.datapointsGauge.length > 0 && activeDatapointGauge;\n          else emptyState\n        \"\n      >\n        <!-- Radial Gauge -->\n        <c8y-radial-gauge\n          class=\"c8y-radial-gauge--square\"\n          *ngIf=\"activeDatapointGauge | infoGaugeCurrentMeasurement | async as measurement\"\n          [activeDatapointGauge]=\"activeDatapointGauge\"\n          [measurement]=\"measurement\"\n          [fractionSize]=\"formGroup.value.fractionSize\"\n          [gaugeOptions]=\"gaugeOptions\"\n          [selectedPresetId]=\"formGroup.value.gaugePresetId\"\n        ></c8y-radial-gauge>\n      </div>\n    </div>\n  </ng-template>\n</form>\n\n<ng-template #emptyState>\n  <div class=\"col-md-6 d-col a-i-start j-c-center\">\n    <c8y-ui-empty-state\n      [icon]=\"'c8y-data-points'\"\n      [title]=\"'Please select a data point first.' | translate\"\n      [horizontal]=\"false\"\n    ></c8y-ui-empty-state>\n  </div>\n</ng-template>\n" }]
        }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i1.NgForm }, { type: i2.WidgetConfigComponent }, { type: i3.DynamicComponentComponent, decorators: [{
                    type: Optional
                }] }], propDecorators: { config: [{
                type: Input
            }], editorComponent: [{
                type: ViewChild,
                args: [EditorComponent]
            }] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5mby1nYXVnZS13aWRnZXQtY29uZmlnLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3dpZGdldHMvaW1wbGVtZW50YXRpb25zL2luZm8tZ2F1Z2UvaW5mby1nYXVnZS13aWRnZXQtY29uZmlnL2luZm8tZ2F1Z2Utd2lkZ2V0LWNvbmZpZy5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi93aWRnZXRzL2ltcGxlbWVudGF0aW9ucy9pbmZvLWdhdWdlL2luZm8tZ2F1Z2Utd2lkZ2V0LWNvbmZpZy9pbmZvLWdhdWdlLXdpZGdldC1jb25maWcuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBVSxLQUFLLEVBQUUsUUFBUSxFQUFFLFNBQVMsRUFBYSxNQUFNLGVBQWUsQ0FBQztBQUN6RixPQUFPLEVBRUwsZ0JBQWdCLEVBQ2hCLFdBQVcsRUFDWCxNQUFNLEVBQ04sbUJBQW1CLEVBRW5CLFVBQVUsRUFDWCxNQUFNLGdCQUFnQixDQUFDO0FBQ3hCLE9BQU8sRUFDTCx1QkFBdUIsRUFJeEIsTUFBTSx3Q0FBd0MsQ0FBQztBQUNoRCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUM5RSxPQUFPLEVBQ0wsWUFBWSxFQUNaLHlCQUF5QixFQUN6QixrQkFBa0IsRUFDbEIsV0FBVyxFQUNYLE9BQU8sRUFDUCwwQkFBMEIsRUFDMUIsaUJBQWlCLEVBRWxCLE1BQU0scUJBQXFCLENBQUM7QUFDN0IsT0FBTyxFQUFjLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDdEQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0seUNBQXlDLENBQUM7QUFDM0UsT0FBTyxFQUFFLHdCQUF3QixFQUFFLE1BQU0sd0NBQXdDLENBQUM7QUFDbEYsT0FBTyxFQUFFLCtCQUErQixFQUFFLE1BQU0sNkJBQTZCLENBQUM7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QThCLGlCQUV6QixDQUFDLEVBQUUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLFdBQVcsRUFBRSxNQUFNLEVBQUUsQ0FBQyxhQUN4RCxDQUFDLDBCQUEwQixDQUFDLGNBQzNCLElBQUksV0FDUDt3QkFDUCxrQkFBa0I7d0JBQ2xCLGlCQUFpQjt3QkFDakIsWUFBWTt3QkFDWixXQUFXO3dCQUNYLGNBQWM7d0JBQ2QsbUJBQW1CO3dCQUNuQixlQUFlO3dCQUNmLG9DQUFvQzt3QkFDcEMsdUJBQXVCO3dCQUN2Qix3QkFBd0I7d0JBQ3hCLCtCQUErQjt3QkFDL0Isc0JBQXNCO3FCQUN2Qjs7MEJBaUNFLFFBQVE7eUNBOUJGLE1BQU07c0JBQWQsS0FBSztnQkFDc0IsZUFBZTtzQkFBMUMsU0FBUzt1QkFBQyxlQUFlIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBPbkluaXQsIElucHV0LCBPcHRpb25hbCwgVmlld0NoaWxkLCBPbkRlc3Ryb3kgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7XG4gIEFic3RyYWN0Q29udHJvbCxcbiAgQ29udHJvbENvbnRhaW5lcixcbiAgRm9ybUJ1aWxkZXIsXG4gIE5nRm9ybSxcbiAgUmVhY3RpdmVGb3Jtc01vZHVsZSxcbiAgVmFsaWRhdG9yRm4sXG4gIFZhbGlkYXRvcnNcbn0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHtcbiAgRGF0YXBvaW50U2VsZWN0b3JNb2R1bGUsXG4gIHR5cGUgRGF0YXBvaW50QXR0cmlidXRlc0Zvcm1Db25maWcsXG4gIHR5cGUgRGF0YXBvaW50U2VsZWN0b3JNb2RhbE9wdGlvbnMsXG4gIHR5cGUgS1BJRGV0YWlsc1xufSBmcm9tICdAYzh5L25neC1jb21wb25lbnRzL2RhdGFwb2ludC1zZWxlY3Rvcic7XG5pbXBvcnQgeyBXaWRnZXRDb25maWdDb21wb25lbnQgfSBmcm9tICdAYzh5L25neC1jb21wb25lbnRzL2NvbnRleHQtZGFzaGJvYXJkJztcbmltcG9ydCB7XG4gIENvbW1vbk1vZHVsZSxcbiAgRHluYW1pY0NvbXBvbmVudENvbXBvbmVudCxcbiAgRm9ybUdyb3VwQ29tcG9uZW50LFxuICBGb3Jtc01vZHVsZSxcbiAgZ2V0dGV4dCxcbiAgTWVhc3VyZW1lbnRSZWFsdGltZVNlcnZpY2UsXG4gIE1lc3NhZ2VzQ29tcG9uZW50LFxuICBPbkJlZm9yZVNhdmVcbn0gZnJvbSAnQGM4eS9uZ3gtY29tcG9uZW50cyc7XG5pbXBvcnQgeyBPYnNlcnZhYmxlLCBTdWJqZWN0LCB0YWtlVW50aWwgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IGRlZmF1bHRXaWRnZXRJZHMgfSBmcm9tICdAYzh5L25neC1jb21wb25lbnRzL3dpZGdldHMvZGVmaW5pdGlvbnMnO1xuaW1wb3J0IHsgUmFkaWFsR2F1Z2VWaWV3Q29tcG9uZW50IH0gZnJvbSAnLi4vcmFkaWFsLWdhdWdlL3JhZGlhbC1nYXVnZS5jb21wb25lbnQnO1xuaW1wb3J0IHsgSW5mb0dhdWdlQ3VycmVudE1lYXN1cmVtZW50UGlwZSB9IGZyb20gJy4uL2N1cnJlbnQtbWVhc3VyZW1lbnQucGlwZSc7XG5pbXBvcnQge1xuICBHQVVHRV9QUkVTRVRfTkFNRVMsXG4gIEdBVUdFX1BSRVNFVFMsXG4gIEdhdWdlT3B0aW9ucyxcbiAgSW5mb0dhdWdlV2lkZ2V0Q29uZmlnXG59IGZyb20gJy4vZ2F1Z2UubW9kZWwnO1xuaW1wb3J0IHsgRWRpdG9yQ29tcG9uZW50LCBNb25hY29FZGl0b3JNYXJrZXJWYWxpZGF0b3JEaXJlY3RpdmUgfSBmcm9tICdAYzh5L25neC1jb21wb25lbnRzL2VkaXRvcic7XG5pbXBvcnQgeyBDb2xsYXBzZU1vZHVsZSB9IGZyb20gJ25neC1ib290c3RyYXAvY29sbGFwc2UnO1xuaW1wb3J0IHsgUHJlc2V0UHJldmlld0NvbXBvbmVudCB9IGZyb20gJy4vcHJlc2V0LXByZXZpZXcvcHJlc2V0LXByZXZpZXcuY29tcG9uZW50JztcblxuZnVuY3Rpb24gZW5zdXJlQXRMZWFzdE9uZURhdGFwb2ludFNlbGVjdGVkQW5kQWN0aXZlKFxuICBkYXRhcG9pbnRMaXN0QXR0cmlidXRlczogc3RyaW5nW11cbik6IFZhbGlkYXRvckZuIHtcbiAgcmV0dXJuIChjb250cm9sOiBBYnN0cmFjdENvbnRyb2wpID0+IHtcbiAgICBjb25zdCBmb3JtVmFsdWUgPSBjb250cm9sLnZhbHVlO1xuICAgIGZvciAoY29uc3QgbGlzdEF0dHJpYnV0ZU5hbWUgb2YgZGF0YXBvaW50TGlzdEF0dHJpYnV0ZXMpIHtcbiAgICAgIGNvbnN0IHZhbHVlRm9yTGlzdDogS1BJRGV0YWlsc1tdID0gZm9ybVZhbHVlW2xpc3RBdHRyaWJ1dGVOYW1lXTtcbiAgICAgIGlmICghdmFsdWVGb3JMaXN0IHx8ICFBcnJheS5pc0FycmF5KHZhbHVlRm9yTGlzdCkpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIGlmICh2YWx1ZUZvckxpc3QuZmluZChkcCA9PiBkcC5fX2FjdGl2ZSkpIHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB7IG5vQWN0aXZlRGF0YXBvaW50OiB0cnVlIH07XG4gIH07XG59XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2M4eS1pbmZvLWdhdWdlLXdpZGdldC1jb25maWcnLFxuICB0ZW1wbGF0ZVVybDogJy4vaW5mby1nYXVnZS13aWRnZXQtY29uZmlnLmNvbXBvbmVudC5odG1sJyxcbiAgdmlld1Byb3ZpZGVyczogW3sgcHJvdmlkZTogQ29udHJvbENvbnRhaW5lciwgdXNlRXhpc3Rpbmc6IE5nRm9ybSB9XSxcbiAgcHJvdmlkZXJzOiBbTWVhc3VyZW1lbnRSZWFsdGltZVNlcnZpY2VdLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbXG4gICAgRm9ybUdyb3VwQ29tcG9uZW50LFxuICAgIE1lc3NhZ2VzQ29tcG9uZW50LFxuICAgIENvbW1vbk1vZHVsZSxcbiAgICBGb3Jtc01vZHVsZSxcbiAgICBDb2xsYXBzZU1vZHVsZSxcbiAgICBSZWFjdGl2ZUZvcm1zTW9kdWxlLFxuICAgIEVkaXRvckNvbXBvbmVudCxcbiAgICBNb25hY29FZGl0b3JNYXJrZXJWYWxpZGF0b3JEaXJlY3RpdmUsXG4gICAgRGF0YXBvaW50U2VsZWN0b3JNb2R1bGUsXG4gICAgUmFkaWFsR2F1Z2VWaWV3Q29tcG9uZW50LFxuICAgIEluZm9HYXVnZUN1cnJlbnRNZWFzdXJlbWVudFBpcGUsXG4gICAgUHJlc2V0UHJldmlld0NvbXBvbmVudFxuICBdXG59KVxuZXhwb3J0IGNsYXNzIEluZm9HYXVnZVdpZGdldENvbmZpZ0NvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCwgT25CZWZvcmVTYXZlLCBPbkRlc3Ryb3kge1xuICBASW5wdXQoKSBjb25maWc6IEluZm9HYXVnZVdpZGdldENvbmZpZztcbiAgQFZpZXdDaGlsZChFZGl0b3JDb21wb25lbnQpIGVkaXRvckNvbXBvbmVudCE6IEVkaXRvckNvbXBvbmVudDtcbiAgZm9ybUdyb3VwOiBSZXR1cm5UeXBlPEluZm9HYXVnZVdpZGdldENvbmZpZ0NvbXBvbmVudFsnY3JlYXRlRm9ybSddPjtcbiAgZGF0YXBvaW50U2VsZWN0aW9uQ29uZmlnOiBQYXJ0aWFsPERhdGFwb2ludFNlbGVjdG9yTW9kYWxPcHRpb25zPiA9IHt9O1xuICBkZWZhdWx0Rm9ybU9wdGlvbnM6IFBhcnRpYWw8RGF0YXBvaW50QXR0cmlidXRlc0Zvcm1Db25maWc+ID0ge1xuICAgIHNob3dSZWRSYW5nZTogdHJ1ZSxcbiAgICBzaG93WWVsbG93UmFuZ2U6IHRydWUsXG4gICAgc2hvd1JhbmdlOiB0cnVlLFxuICAgIHNlbGVjdGFibGVDaGFydFJlbmRlclR5cGVzOiBbXVxuICB9O1xuICBpc0luZm9HYXVnZTogYm9vbGVhbjtcbiAgYWN0aXZlRGF0YXBvaW50R2F1Z2U6IEtQSURldGFpbHM7XG4gIHNob3dBZHZhbmNlZE9wdGlvbnMgPSBmYWxzZTtcbiAgZ2F1Z2VPcHRpb25zU3RyaW5nID0gJyc7XG4gIGdhdWdlT3B0aW9uczogR2F1Z2VPcHRpb25zO1xuICBzaG93QWR2YW5jZWRPcHRpb25zTGFiZWwgPSBnZXR0ZXh0KCdTaG93IGFkdmFuY2VkIG9wdGlvbnMnKTtcbiAgaGlkZUFkdmFuY2VkT3B0aW9uc0xhYmVsID0gZ2V0dGV4dCgnSGlkZSBhZHZhbmNlZCBvcHRpb25zJyk7XG4gIEFMTF9HQVVHRV9QUkVTRVRTID0gWy4uLkdBVUdFX1BSRVNFVFNdO1xuICBwcml2YXRlIGxpbWl0cyA9IHtcbiAgICBudW1iZXJPZkRlY2ltYWxQbGFjZXNNYXg6IDEwLFxuICAgIG51bWJlck9mRGVjaW1hbFBsYWNlc01pbjogMFxuICB9IGFzIGNvbnN0O1xuXG4gIHJlYWRvbmx5IEdBVUdFX1BSRVNFVFMgPSBHQVVHRV9QUkVTRVRTO1xuICBwcml2YXRlIGRlc3Ryb3kkID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIGZvcm1CdWlsZGVyOiBGb3JtQnVpbGRlcixcbiAgICBwcml2YXRlIGZvcm06IE5nRm9ybSxcbiAgICBwcml2YXRlIHdpZGdldENvbmZpZzogV2lkZ2V0Q29uZmlnQ29tcG9uZW50LFxuICAgIEBPcHRpb25hbCgpIHByaXZhdGUgZHluYW1pY0NvbXBvbmVudD86IER5bmFtaWNDb21wb25lbnRDb21wb25lbnRcbiAgKSB7fVxuXG4gIG9uQmVmb3JlU2F2ZShcbiAgICBjb25maWc/OiBJbmZvR2F1Z2VXaWRnZXRDb25maWdDb21wb25lbnRbJ2NvbmZpZyddXG4gICk6IGJvb2xlYW4gfCBQcm9taXNlPGJvb2xlYW4+IHwgT2JzZXJ2YWJsZTxib29sZWFuPiB7XG4gICAgaWYgKHRoaXMuZm9ybUdyb3VwLnZhbGlkKSB7XG4gICAgICBPYmplY3QuYXNzaWduKGNvbmZpZywgdGhpcy5mb3JtR3JvdXAudmFsdWUpO1xuICAgICAgT2JqZWN0LmFzc2lnbihjb25maWcsIHtcbiAgICAgICAgZ2F1Z2VPcHRpb25zOiB0aGlzLmdhdWdlT3B0aW9ucyxcbiAgICAgICAgc2VsZWN0ZWRQcmVzZXRJZDogdGhpcy5pc0luZm9HYXVnZSA/IG51bGwgOiB0aGlzLmdldFByZXNldElkKHRoaXMuZ2F1Z2VPcHRpb25zKVxuICAgICAgfSk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMud2lkZ2V0Q29uZmlnLmNvbnRleHQ/LmlkKSB7XG4gICAgICB0aGlzLmRhdGFwb2ludFNlbGVjdGlvbkNvbmZpZy5jb250ZXh0QXNzZXQgPSB0aGlzLndpZGdldENvbmZpZz8uY29udGV4dDtcbiAgICB9XG4gICAgdGhpcy5pbml0Rm9ybSgpO1xuICAgIGlmICh0aGlzLmNvbmZpZz8uZGF0YXBvaW50c0xhYmVscykge1xuICAgICAgdGhpcy5mb3JtR3JvdXAucGF0Y2hWYWx1ZSh7IGRhdGFwb2ludHNMYWJlbHM6IHRoaXMuY29uZmlnLmRhdGFwb2ludHNMYWJlbHMgfSk7XG4gICAgfVxuICAgIGlmICh0aGlzLmNvbmZpZz8uZGF0YXBvaW50c0dhdWdlKSB7XG4gICAgICB0aGlzLmZvcm1Hcm91cC5wYXRjaFZhbHVlKHsgZGF0YXBvaW50c0dhdWdlOiB0aGlzLmNvbmZpZy5kYXRhcG9pbnRzR2F1Z2UgfSk7XG4gICAgfVxuICAgIGlmICh0eXBlb2YgdGhpcy5jb25maWc/LmZyYWN0aW9uU2l6ZSA9PT0gJ251bWJlcicgJiYgIU51bWJlci5pc05hTih0aGlzLmNvbmZpZz8uZnJhY3Rpb25TaXplKSkge1xuICAgICAgdGhpcy5mb3JtR3JvdXAucGF0Y2hWYWx1ZSh7IGZyYWN0aW9uU2l6ZTogdGhpcy5jb25maWcuZnJhY3Rpb25TaXplIH0pO1xuICAgIH1cbiAgICB0aGlzLmlzSW5mb0dhdWdlID0gdGhpcy5keW5hbWljQ29tcG9uZW50Py5jb21wb25lbnRJZCA9PT0gZGVmYXVsdFdpZGdldElkcy5JTkZPX0dBVUdFO1xuXG4gICAgY29uc3QgaXNSYWRpYWxHYXVnZSA9ICF0aGlzLmlzSW5mb0dhdWdlO1xuXG4gICAgaWYgKGlzUmFkaWFsR2F1Z2UpIHtcbiAgICAgIHRoaXMuc2V0dXBSYWRpYWxHYXVnZSgpO1xuICAgIH1cblxuICAgIHRoaXMuZm9ybUdyb3VwLnZhbHVlQ2hhbmdlcy5waXBlKHRha2VVbnRpbCh0aGlzLmRlc3Ryb3kkKSkuc3Vic2NyaWJlKHZhbHVlID0+IHtcbiAgICAgIGlmICh2YWx1ZS5kYXRhcG9pbnRzR2F1Z2UpIHtcbiAgICAgICAgdGhpcy5hY3RpdmVEYXRhcG9pbnRHYXVnZSA9IHZhbHVlLmRhdGFwb2ludHNHYXVnZS5maW5kKGRwID0+IGRwLl9fYWN0aXZlKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgIHRoaXMuZGVzdHJveSQubmV4dCgpO1xuICAgIHRoaXMuZGVzdHJveSQuY29tcGxldGUoKTtcbiAgfVxuXG4gIG9uUmFkaW9QcmVzZXRDaGFuZ2UocHJlc2V0SWQ6IHN0cmluZyk6IHZvaWQge1xuICAgIC8vIEZpbmQgdGhlIHNlbGVjdGVkIHByZXNldFxuICAgIGNvbnN0IHNlbGVjdGVkUHJlc2V0ID0gdGhpcy5BTExfR0FVR0VfUFJFU0VUUy5maW5kKHByZXNldCA9PiBwcmVzZXQuaWQgPT09IHByZXNldElkKTtcblxuICAgIGlmIChzZWxlY3RlZFByZXNldCkge1xuICAgICAgdGhpcy5nYXVnZU9wdGlvbnMgPSB7IC4uLnNlbGVjdGVkUHJlc2V0IH07IC8vIFVwZGF0ZSBnYXVnZU9wdGlvbnMgd2l0aCB0aGUgc2VsZWN0ZWQgcHJlc2V0XG4gICAgICB0aGlzLmdhdWdlT3B0aW9uc1N0cmluZyA9IEpTT04uc3RyaW5naWZ5KHRoaXMuZ2F1Z2VPcHRpb25zLCBudWxsLCAyKTtcblxuICAgICAgLy8gSWYgdGhlIHNlbGVjdGVkIHByZXNldCBpcyBub3QgY3VzdG9tLCBoaWRlIGFkdmFuY2VkIG9wdGlvbnNcbiAgICAgIGlmIChzZWxlY3RlZFByZXNldC5pZCAhPT0gJ2N1c3RvbScpIHtcbiAgICAgICAgdGhpcy5zaG93QWR2YW5jZWRPcHRpb25zID0gZmFsc2U7XG4gICAgICB9XG5cbiAgICAgIC8vIFVwZGF0ZSB0aGUgZm9ybSBjb250cm9sIHZhbHVlXG4gICAgICB0aGlzLmZvcm1Hcm91cC5wYXRjaFZhbHVlKHsgZ2F1Z2VQcmVzZXRJZDogc2VsZWN0ZWRQcmVzZXQuaWQgfSk7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgYXNzaWduU2NoZW1hKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHsgc2NoZW1hIH0gPSBhd2FpdCBpbXBvcnQoXG4gICAgICAnYzh5LXNjaGVtYS1sb2FkZXI/aW50ZXJmYWNlTmFtZT1HYXVnZU9wdGlvbnMhQGM4eS9uZ3gtY29tcG9uZW50cy93aWRnZXRzL2ltcGxlbWVudGF0aW9ucy9pbmZvLWdhdWdlJ1xuICAgICk7XG4gICAgdGhpcy5lZGl0b3JDb21wb25lbnQubW9uYWNvLmxhbmd1YWdlcy5qc29uLmpzb25EZWZhdWx0cy5zZXREaWFnbm9zdGljc09wdGlvbnMoe1xuICAgICAgdmFsaWRhdGU6IHRydWUsXG4gICAgICBzY2hlbWFzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICB1cmk6ICdnYXVnZS1zY2hlbWEuanNvbicsXG4gICAgICAgICAgc2NoZW1hOiBzY2hlbWEsXG4gICAgICAgICAgZmlsZU1hdGNoOiBbJyonXVxuICAgICAgICB9XG4gICAgICBdLFxuICAgICAgZW5hYmxlU2NoZW1hUmVxdWVzdDogZmFsc2UsXG4gICAgICBhbGxvd0NvbW1lbnRzOiBmYWxzZVxuICAgIH0pO1xuICB9XG5cbiAgZ2F1Z2VKU09OQ2hhbmdlKHZhbHVlOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcGFyc2VkT3B0aW9ucyA9IEpTT04ucGFyc2UodmFsdWUpO1xuXG4gICAgICAvLyBDaGVjayBpZiB0aGUgcGFyc2VkIG9wdGlvbnMgbWF0Y2ggYW55IGV4aXN0aW5nIHByZXNldFxuICAgICAgY29uc3QgbWF0Y2hpbmdQcmVzZXQgPSB0aGlzLkdBVUdFX1BSRVNFVFMuZmluZChcbiAgICAgICAgcHJlc2V0ID0+IEpTT04uc3RyaW5naWZ5KHByZXNldCkgPT09IEpTT04uc3RyaW5naWZ5KHBhcnNlZE9wdGlvbnMpXG4gICAgICApO1xuXG4gICAgICBpZiAobWF0Y2hpbmdQcmVzZXQpIHtcbiAgICAgICAgdGhpcy5mb3JtR3JvdXAucGF0Y2hWYWx1ZSh7IGdhdWdlUHJlc2V0SWQ6IG1hdGNoaW5nUHJlc2V0LmlkIH0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5nYXVnZU9wdGlvbnMgPSBwYXJzZWRPcHRpb25zO1xuICAgICAgICB0aGlzLmdhdWdlT3B0aW9ucy5uYW1lID0gR0FVR0VfUFJFU0VUX05BTUVTLkNVU1RPTTtcbiAgICAgICAgdGhpcy5nYXVnZU9wdGlvbnMuaWQgPSAnY3VzdG9tJztcblxuICAgICAgICBjb25zdCBjdXN0b21QcmVzZXRFeGlzdHMgPSB0aGlzLkFMTF9HQVVHRV9QUkVTRVRTLnNvbWUocHJlc2V0ID0+IHByZXNldC5pZCA9PT0gJ2N1c3RvbScpO1xuXG4gICAgICAgIGlmICghY3VzdG9tUHJlc2V0RXhpc3RzKSB7XG4gICAgICAgICAgdGhpcy5BTExfR0FVR0VfUFJFU0VUUyA9IFsuLi50aGlzLkFMTF9HQVVHRV9QUkVTRVRTLCB0aGlzLmdhdWdlT3B0aW9uc107XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhpcy5BTExfR0FVR0VfUFJFU0VUUyA9IHRoaXMuQUxMX0dBVUdFX1BSRVNFVFMubWFwKHByZXNldCA9PlxuICAgICAgICAgICAgcHJlc2V0LmlkID09PSAnY3VzdG9tJyA/IHRoaXMuZ2F1Z2VPcHRpb25zIDogcHJlc2V0XG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuZm9ybUdyb3VwLnBhdGNoVmFsdWUoeyBnYXVnZVByZXNldElkOiAnY3VzdG9tJyB9KTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY29uc29sZS5lcnJvcignSW52YWxpZCBKU09OOicsIGVycm9yKTtcbiAgICB9XG4gIH1cblxuICB0cmFja0J5Rm4oX2luZGV4OiBudW1iZXIsIGl0ZW06IEdhdWdlT3B0aW9ucyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGl0ZW0uaWQ7XG4gIH1cblxuICBwcml2YXRlIGdldFByZXNldElkKGdhdWdlT3B0aW9uczogR2F1Z2VPcHRpb25zKTogc3RyaW5nIHtcbiAgICBjb25zdCBtYXRjaGluZ1ByZXNldCA9IHRoaXMuR0FVR0VfUFJFU0VUUy5maW5kKHByZXNldCA9PiBwcmVzZXQuaWQgPT09IGdhdWdlT3B0aW9ucy5pZCk7XG5cbiAgICByZXR1cm4gbWF0Y2hpbmdQcmVzZXQgPyBtYXRjaGluZ1ByZXNldC5pZCA6ICdjdXN0b20nO1xuICB9XG5cbiAgcHJpdmF0ZSBzZXR1cFJhZGlhbEdhdWdlKCkge1xuICAgIGlmICh0aGlzLmNvbmZpZy5kYXRhcG9pbnRzICYmICF0aGlzLmNvbmZpZy5kYXRhcG9pbnRzR2F1Z2UpIHtcbiAgICAgIHRoaXMuY29uZmlnLmRhdGFwb2ludHNHYXVnZSA9IHRoaXMuY29uZmlnLmRhdGFwb2ludHM7XG4gICAgfVxuXG4gICAgdGhpcy5nYXVnZU9wdGlvbnMgPSB0aGlzLmNvbmZpZy5nYXVnZU9wdGlvbnMgfHwgeyAuLi50aGlzLkdBVUdFX1BSRVNFVFNbMF0gfTtcblxuICAgIGlmICh0aGlzLmNvbmZpZy5zZWxlY3RlZFByZXNldElkID09PSAnY3VzdG9tJykge1xuICAgICAgdGhpcy5nYXVnZU9wdGlvbnMubmFtZSA9IEdBVUdFX1BSRVNFVF9OQU1FUy5DVVNUT007XG4gICAgICB0aGlzLmdhdWdlT3B0aW9ucy5pZCA9ICdjdXN0b20nO1xuICAgICAgdGhpcy5BTExfR0FVR0VfUFJFU0VUUyA9IFsuLi50aGlzLkdBVUdFX1BSRVNFVFMsIHRoaXMuY29uZmlnLmdhdWdlT3B0aW9uc107XG4gICAgICB0aGlzLmZvcm1Hcm91cC5wYXRjaFZhbHVlKHsgZ2F1Z2VQcmVzZXRJZDogJ2N1c3RvbScgfSk7XG4gICAgfVxuXG4gICAgdGhpcy5nYXVnZU9wdGlvbnNTdHJpbmcgPSBKU09OLnN0cmluZ2lmeSh0aGlzLmdhdWdlT3B0aW9ucywgbnVsbCwgMik7XG4gIH1cblxuICBwcml2YXRlIGluaXRGb3JtKCk6IHZvaWQge1xuICAgIHRoaXMuZm9ybUdyb3VwID0gdGhpcy5jcmVhdGVGb3JtKCk7XG4gICAgdGhpcy5mb3JtLmZvcm0uYWRkQ29udHJvbCgnY29uZmlnJywgdGhpcy5mb3JtR3JvdXApO1xuICAgIHRoaXMuZm9ybUdyb3VwLnBhdGNoVmFsdWUodGhpcy5jb25maWcpO1xuICB9XG5cbiAgcHJpdmF0ZSBjcmVhdGVGb3JtKCkge1xuICAgIHJldHVybiB0aGlzLmZvcm1CdWlsZGVyLmdyb3VwKFxuICAgICAge1xuICAgICAgICBmcmFjdGlvblNpemU6IFtcbiAgICAgICAgICAyLFxuICAgICAgICAgIFtcbiAgICAgICAgICAgIFZhbGlkYXRvcnMucmVxdWlyZWQsXG4gICAgICAgICAgICBWYWxpZGF0b3JzLm1pbih0aGlzLmxpbWl0cy5udW1iZXJPZkRlY2ltYWxQbGFjZXNNaW4pLFxuICAgICAgICAgICAgVmFsaWRhdG9ycy5tYXgodGhpcy5saW1pdHMubnVtYmVyT2ZEZWNpbWFsUGxhY2VzTWF4KVxuICAgICAgICAgIF1cbiAgICAgICAgXSxcbiAgICAgICAgZGF0YXBvaW50c0xhYmVsczogdGhpcy5mb3JtQnVpbGRlci5jb250cm9sKG5ldyBBcnJheTxLUElEZXRhaWxzPigpLCBbXSksXG4gICAgICAgIGRhdGFwb2ludHNHYXVnZTogdGhpcy5mb3JtQnVpbGRlci5jb250cm9sKG5ldyBBcnJheTxLUElEZXRhaWxzPigpLCBbXSksXG4gICAgICAgIGdhdWdlUHJlc2V0SWQ6IFt0aGlzLmNvbmZpZy5zZWxlY3RlZFByZXNldElkIHx8ICdkZWZhdWx0J11cbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIHZhbGlkYXRvcnM6IFtcbiAgICAgICAgICBlbnN1cmVBdExlYXN0T25lRGF0YXBvaW50U2VsZWN0ZWRBbmRBY3RpdmUoWydkYXRhcG9pbnRzTGFiZWxzJywgJ2RhdGFwb2ludHNHYXVnZSddKVxuICAgICAgICBdXG4gICAgICB9XG4gICAgKTtcbiAgfVxufVxuIiwiPGZvcm1cbiAgY2xhc3M9XCJmaXQtaFwiXG4gIFtmb3JtR3JvdXBdPVwiZm9ybUdyb3VwXCJcbj5cbiAgPGRpdlxuICAgIGNsYXNzPVwibS1sLTE2IG0tci0xNlwiXG4gICAgKm5nSWY9XCJpc0luZm9HYXVnZTsgZWxzZSByYWRpYWxHYXVnZVwiXG4gID5cbiAgICA8ZGl2IGNsYXNzPVwicm93XCI+XG4gICAgICA8ZGl2IGNsYXNzPVwiY29sLW1kLTZcIj5cbiAgICAgICAgPGM4eS1mb3JtLWdyb3VwIGNsYXNzPVwicC10LThcIj5cbiAgICAgICAgICA8bGFiZWwgdHJhbnNsYXRlPkRlY2ltYWwgcGxhY2VzPC9sYWJlbD5cbiAgICAgICAgICA8aW5wdXRcbiAgICAgICAgICAgIGNsYXNzPVwiZm9ybS1jb250cm9sXCJcbiAgICAgICAgICAgIG5hbWU9XCJmcmFjdGlvblNpemVcIlxuICAgICAgICAgICAgdHlwZT1cIm51bWJlclwiXG4gICAgICAgICAgICBmb3JtQ29udHJvbE5hbWU9XCJmcmFjdGlvblNpemVcIlxuICAgICAgICAgICAgc3RlcD1cIjFcIlxuICAgICAgICAgIC8+XG4gICAgICAgICAgPGM4eS1tZXNzYWdlcyBbc2hvd109XCJmb3JtR3JvdXAuY29udHJvbHMuZnJhY3Rpb25TaXplLmVycm9yc1wiPjwvYzh5LW1lc3NhZ2VzPlxuICAgICAgICA8L2M4eS1mb3JtLWdyb3VwPlxuICAgICAgPC9kaXY+XG4gICAgICA8ZGl2IGNsYXNzPVwiY29sLW1kLTYgcC10LThcIj5cbiAgICAgICAgPGRpdlxuICAgICAgICAgIGNsYXNzPVwiYWxlcnQgYWxlcnQtaW5mb1wiXG4gICAgICAgICAgcm9sZT1cImFsZXJ0XCJcbiAgICAgICAgICAqbmdJZj1cImZvcm1Hcm91cC5lcnJvcnM/Lm5vQWN0aXZlRGF0YXBvaW50XCJcbiAgICAgICAgICB0cmFuc2xhdGVcbiAgICAgICAgPlxuICAgICAgICAgIEF0IGxlYXN0IG9uZSBkYXRhIHBvaW50IGZvciB0aGUgbGFiZWxzIG9yIHRoZSBnYXVnZSBuZWVkcyB0byBiZSBzZWxlY3RlZC5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cblxuICAgIDxkaXYgY2xhc3M9XCJyb3dcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJjb2wtbWQtNlwiPlxuICAgICAgICA8Yzh5LWRhdGFwb2ludC1zZWxlY3Rpb24tbGlzdFxuICAgICAgICAgIGNsYXNzPVwiYmctaW5oZXJpdCBzZXBhcmF0b3ItdG9wIHAtdC0xNiBkLWJsb2NrXCJcbiAgICAgICAgICBsaXN0VGl0bGU9XCJ7eyAnTXVsdGlwbGUgbGFiZWwgYW5kIHZhbHVlIHBhaXJzJyB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgICAgICAgbmFtZT1cImRhdGFwb2ludHNcIlxuICAgICAgICAgIFtkZWZhdWx0Rm9ybU9wdGlvbnNdPVwiZGVmYXVsdEZvcm1PcHRpb25zXCJcbiAgICAgICAgICBbY29uZmlnXT1cImRhdGFwb2ludFNlbGVjdGlvbkNvbmZpZ1wiXG4gICAgICAgICAgW21pbkFjdGl2ZUNvdW50XT1cIjBcIlxuICAgICAgICAgIGZvcm1Db250cm9sTmFtZT1cImRhdGFwb2ludHNMYWJlbHNcIlxuICAgICAgICA+PC9jOHktZGF0YXBvaW50LXNlbGVjdGlvbi1saXN0PlxuICAgICAgPC9kaXY+XG4gICAgICA8ZGl2IGNsYXNzPVwiY29sLW1kLTZcIj5cbiAgICAgICAgPGM4eS1kYXRhcG9pbnQtc2VsZWN0aW9uLWxpc3RcbiAgICAgICAgICBjbGFzcz1cImJnLWluaGVyaXQgc2VwYXJhdG9yLXRvcCBwLXQtMTYgZC1ibG9ja1wiXG4gICAgICAgICAgbGlzdFRpdGxlPVwie3sgJ0dhdWdlYGRpc3BsYXlgJyB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgICAgICAgbmFtZT1cImRhdGFwb2ludHNcIlxuICAgICAgICAgIFtkZWZhdWx0Rm9ybU9wdGlvbnNdPVwiZGVmYXVsdEZvcm1PcHRpb25zXCJcbiAgICAgICAgICBbY29uZmlnXT1cImRhdGFwb2ludFNlbGVjdGlvbkNvbmZpZ1wiXG4gICAgICAgICAgW21pbkFjdGl2ZUNvdW50XT1cIjBcIlxuICAgICAgICAgIFttYXhBY3RpdmVDb3VudF09XCIxXCJcbiAgICAgICAgICBmb3JtQ29udHJvbE5hbWU9XCJkYXRhcG9pbnRzR2F1Z2VcIlxuICAgICAgICA+PC9jOHktZGF0YXBvaW50LXNlbGVjdGlvbi1saXN0PlxuICAgICAgPC9kaXY+XG4gICAgPC9kaXY+XG4gIDwvZGl2PlxuXG4gIDxuZy10ZW1wbGF0ZSAjcmFkaWFsR2F1Z2U+XG4gICAgPGRpdiBjbGFzcz1cImQtZmxleC1tZCBmaXQtaFwiPlxuICAgICAgPGRpdiBjbGFzcz1cImNvbC1tZC02IGlubmVyLXNjcm9sbCBwLTAgYmctY29tcG9uZW50XCI+XG4gICAgICAgIDxjOHktZGF0YXBvaW50LXNlbGVjdGlvbi1saXN0XG4gICAgICAgICAgY2xhc3M9XCJiZy1pbmhlcml0IGQtYmxvY2tcIlxuICAgICAgICAgIGxpc3RUaXRsZT1cInt7ICdEYXRhIHBvaW50c2BkaXNwbGF5YCcgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgICAgIG5hbWU9XCJkYXRhcG9pbnRzXCJcbiAgICAgICAgICBbZGVmYXVsdEZvcm1PcHRpb25zXT1cImRlZmF1bHRGb3JtT3B0aW9uc1wiXG4gICAgICAgICAgW2NvbmZpZ109XCJkYXRhcG9pbnRTZWxlY3Rpb25Db25maWdcIlxuICAgICAgICAgIFttaW5BY3RpdmVDb3VudF09XCIwXCJcbiAgICAgICAgICBbbWF4QWN0aXZlQ291bnRdPVwiMVwiXG4gICAgICAgICAgZm9ybUNvbnRyb2xOYW1lPVwiZGF0YXBvaW50c0dhdWdlXCJcbiAgICAgICAgPjwvYzh5LWRhdGFwb2ludC1zZWxlY3Rpb24tbGlzdD5cblxuICAgICAgICA8Yzh5LWZvcm0tZ3JvdXAgY2xhc3M9XCJwLXQtOCBwLXItMTYgcC1sLTE2XCI+XG4gICAgICAgICAgPGxhYmVsIHRyYW5zbGF0ZT5EZWNpbWFsIHBsYWNlczwvbGFiZWw+XG4gICAgICAgICAgPGlucHV0XG4gICAgICAgICAgICBjbGFzcz1cImZvcm0tY29udHJvbFwiXG4gICAgICAgICAgICBuYW1lPVwiZnJhY3Rpb25TaXplXCJcbiAgICAgICAgICAgIHR5cGU9XCJudW1iZXJcIlxuICAgICAgICAgICAgZm9ybUNvbnRyb2xOYW1lPVwiZnJhY3Rpb25TaXplXCJcbiAgICAgICAgICAgIHN0ZXA9XCIxXCJcbiAgICAgICAgICAvPlxuICAgICAgICAgIDxjOHktbWVzc2FnZXMgW3Nob3ddPVwiZm9ybUdyb3VwLmNvbnRyb2xzLmZyYWN0aW9uU2l6ZS5lcnJvcnNcIj48L2M4eS1tZXNzYWdlcz5cbiAgICAgICAgPC9jOHktZm9ybS1ncm91cD5cblxuICAgICAgICA8ZGl2IGNsYXNzPVwiZm9ybS1ncm91cCBwLXItMTYgcC1sLTE2IG0tYi0wXCI+XG4gICAgICAgICAgPGxlZ2VuZD5cbiAgICAgICAgICAgIHt7ICdQcmVzZXRzJyB8IHRyYW5zbGF0ZSB9fVxuICAgICAgICAgIDwvbGVnZW5kPlxuICAgICAgICAgIDxjOHktcHJlc2V0LXByZXZpZXdcbiAgICAgICAgICAgIGZvcm1Db250cm9sTmFtZT1cImdhdWdlUHJlc2V0SWRcIlxuICAgICAgICAgICAgW0FMTF9HQVVHRV9QUkVTRVRTXT1cIkFMTF9HQVVHRV9QUkVTRVRTXCJcbiAgICAgICAgICAgIChvblByZXNldENoYW5nZSk9XCJvblJhZGlvUHJlc2V0Q2hhbmdlKCRldmVudClcIlxuICAgICAgICAgID48L2M4eS1wcmVzZXQtcHJldmlldz5cbiAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgPGRpdiBjbGFzcz1cInAtMTZcIj5cbiAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICBjbGFzcz1cImJ0biBidG4tZGVmYXVsdFwiXG4gICAgICAgICAgICBhcmlhLWNvbnRyb2xzPVwiYWR2YW5jZWRDb2xsYXBzZVwiXG4gICAgICAgICAgICBbYXR0ci5hcmlhLWV4cGFuZGVkXT1cInNob3dBZHZhbmNlZE9wdGlvbnNcIlxuICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICAoY2xpY2spPVwic2hvd0FkdmFuY2VkT3B0aW9ucyA9ICFzaG93QWR2YW5jZWRPcHRpb25zXCJcbiAgICAgICAgICA+XG4gICAgICAgICAgICB7eyBzaG93QWR2YW5jZWRPcHRpb25zID8gaGlkZUFkdmFuY2VkT3B0aW9uc0xhYmVsIDogc2hvd0FkdmFuY2VkT3B0aW9uc0xhYmVsIH19XG4gICAgICAgICAgPC9idXR0b24+XG4gICAgICAgIDwvZGl2PlxuXG4gICAgICAgIDxkaXZcbiAgICAgICAgICBjbGFzcz1cImNvbGxhcHNlXCJcbiAgICAgICAgICBpZD1cImFkdmFuY2VkQ29sbGFwc2VcIlxuICAgICAgICAgIFtjb2xsYXBzZV09XCIhc2hvd0FkdmFuY2VkT3B0aW9uc1wiXG4gICAgICAgICAgW2lzQW5pbWF0ZWRdPVwidHJ1ZVwiXG4gICAgICAgID5cbiAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICBjbGFzcz1cImZpdC1oXCJcbiAgICAgICAgICAgIHN0eWxlPVwibWluLWhlaWdodDogNDAwcHhcIlxuICAgICAgICAgID5cbiAgICAgICAgICAgIDxjOHktZWRpdG9yXG4gICAgICAgICAgICAgIFtuZ01vZGVsXT1cImdhdWdlT3B0aW9uc1N0cmluZ1wiXG4gICAgICAgICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cImdhdWdlSlNPTkNoYW5nZSgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgKGVkaXRvckluaXQpPVwiYXNzaWduU2NoZW1hKClcIlxuICAgICAgICAgICAgICBbbmdNb2RlbE9wdGlvbnNdPVwieyBzdGFuZGFsb25lOiB0cnVlIH1cIlxuICAgICAgICAgICAgICBtb25hY29FZGl0b3JNYXJrZXJWYWxpZGF0b3JcbiAgICAgICAgICAgID48L2M4eS1lZGl0b3I+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIDwvZGl2PlxuICAgICAgPC9kaXY+XG4gICAgICA8ZGl2XG4gICAgICAgIGNsYXNzPVwiY29sLW1kLTZcIlxuICAgICAgICAqbmdJZj1cIlxuICAgICAgICAgIHRoaXMuZm9ybUdyb3VwLnZhbHVlLmRhdGFwb2ludHNHYXVnZS5sZW5ndGggPiAwICYmIGFjdGl2ZURhdGFwb2ludEdhdWdlO1xuICAgICAgICAgIGVsc2UgZW1wdHlTdGF0ZVxuICAgICAgICBcIlxuICAgICAgPlxuICAgICAgICA8IS0tIFJhZGlhbCBHYXVnZSAtLT5cbiAgICAgICAgPGM4eS1yYWRpYWwtZ2F1Z2VcbiAgICAgICAgICBjbGFzcz1cImM4eS1yYWRpYWwtZ2F1Z2UtLXNxdWFyZVwiXG4gICAgICAgICAgKm5nSWY9XCJhY3RpdmVEYXRhcG9pbnRHYXVnZSB8IGluZm9HYXVnZUN1cnJlbnRNZWFzdXJlbWVudCB8IGFzeW5jIGFzIG1lYXN1cmVtZW50XCJcbiAgICAgICAgICBbYWN0aXZlRGF0YXBvaW50R2F1Z2VdPVwiYWN0aXZlRGF0YXBvaW50R2F1Z2VcIlxuICAgICAgICAgIFttZWFzdXJlbWVudF09XCJtZWFzdXJlbWVudFwiXG4gICAgICAgICAgW2ZyYWN0aW9uU2l6ZV09XCJmb3JtR3JvdXAudmFsdWUuZnJhY3Rpb25TaXplXCJcbiAgICAgICAgICBbZ2F1Z2VPcHRpb25zXT1cImdhdWdlT3B0aW9uc1wiXG4gICAgICAgICAgW3NlbGVjdGVkUHJlc2V0SWRdPVwiZm9ybUdyb3VwLnZhbHVlLmdhdWdlUHJlc2V0SWRcIlxuICAgICAgICA+PC9jOHktcmFkaWFsLWdhdWdlPlxuICAgICAgPC9kaXY+XG4gICAgPC9kaXY+XG4gIDwvbmctdGVtcGxhdGU+XG48L2Zvcm0+XG5cbjxuZy10ZW1wbGF0ZSAjZW1wdHlTdGF0ZT5cbiAgPGRpdiBjbGFzcz1cImNvbC1tZC02IGQtY29sIGEtaS1zdGFydCBqLWMtY2VudGVyXCI+XG4gICAgPGM4eS11aS1lbXB0eS1zdGF0ZVxuICAgICAgW2ljb25dPVwiJ2M4eS1kYXRhLXBvaW50cydcIlxuICAgICAgW3RpdGxlXT1cIidQbGVhc2Ugc2VsZWN0IGEgZGF0YSBwb2ludCBmaXJzdC4nIHwgdHJhbnNsYXRlXCJcbiAgICAgIFtob3Jpem9udGFsXT1cImZhbHNlXCJcbiAgICA+PC9jOHktdWktZW1wdHktc3RhdGU+XG4gIDwvZGl2PlxuPC9uZy10ZW1wbGF0ZT5cbiJdfQ==