@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
175 lines • 36.1 kB
JavaScript
import { moveItemInArray } from '@angular/cdk/drag-drop';
import { Component, forwardRef, Input, Optional, Output } from '@angular/core';
import { FormBuilder, NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms';
import { C8yValidators } from '@c8y/ngx-components';
import { WidgetConfigComponent } from '@c8y/ngx-components/context-dashboard';
import { from, Observable } from 'rxjs';
import { map, shareReplay, take, tap } from 'rxjs/operators';
import { DatapointLibraryService } from '../datapoint-library.service';
import { DatapointSelectorService } from '../datapoint-selector.service';
import { AddButtonTypes } from '../datapoint-selector-list-item/datapoint-selector-list-item.component';
import * as i0 from "@angular/core";
import * as i1 from "../datapoint-selector.service";
import * as i2 from "../datapoint-library.service";
import * as i3 from "@angular/forms";
import * as i4 from "@c8y/ngx-components/context-dashboard";
import * as i5 from "@c8y/ngx-components";
import * as i6 from "@angular/common";
import * as i7 from "@angular/cdk/drag-drop";
import * as i8 from "../datapoint-selector-list-item/datapoint-selector-list-item.component";
export class DatapointSelectionListComponent {
constructor(datapointSelector, datapointLibrary, formBuilder, widgetComponent) {
this.datapointSelector = datapointSelector;
this.datapointLibrary = datapointLibrary;
this.formBuilder = formBuilder;
this.widgetComponent = widgetComponent;
this.actions = [];
this.allowDragAndDrop = true;
this.config = {};
this.defaultFormOptions = {};
this.minActiveCount = 1;
this.resolveContext = true;
this.listTitle = '';
this.maxActiveCountReached = false;
this.AddButtonTypes = AddButtonTypes;
this.usedValidators = {};
this.formArray = this.formBuilder.array([]);
this.isValid = this.formArray.statusChanges.pipe(map(status => status === 'VALID'));
this.datapointLibraryEntries = from(this.datapointLibrary.getFirstDatapointLibraryPage()).pipe(shareReplay());
this.change = this.formArray.valueChanges.pipe(map(res => this.transformValue(res)));
}
ngOnChanges(changes) {
if (!changes.maxActiveCount && !changes.minActiveCount) {
return;
}
if (changes.maxActiveCount) {
this.usedValidators.maxActiveCount = C8yValidators.maxActiveCount(this.maxActiveCount);
}
if (changes.minActiveCount) {
this.usedValidators.minActiveCount = C8yValidators.minActiveCount(this.minActiveCount);
}
const validators = Object.values(this.usedValidators);
this.formArray.setValidators(validators);
}
registerOnTouched(fn) {
this.formArray.valueChanges.pipe(take(1)).subscribe(fn);
}
validate(_control) {
return this.formArray.valid ? null : { formInvalid: {} };
}
ngOnInit() {
const context = this.widgetComponent?.context;
if (context?.id && this.resolveContext) {
const { name, id, c8y_IsDevice } = context;
this.config.contextAsset = { name, id, c8y_IsDevice };
}
}
writeValue(obj) {
this.formArray.clear();
if (obj?.length) {
obj.forEach(val => {
const formgroup = this.formBuilder.group({ details: [] });
formgroup.patchValue({ details: val });
this.formArray.push(formgroup);
});
}
this.calculateMaxActiveCount();
}
registerOnChange(fn) {
this.formArray.valueChanges
.pipe(map(res => this.transformValue(res)),
// check maxActiveCount
tap(() => {
this.calculateMaxActiveCount();
}))
.subscribe(fn);
}
add() {
const allowChangingContext = !this.widgetComponent?.isDeviceTypeDashboard && this.config?.allowChangingContext !== false;
this.datapointSelector
.selectDataPoints({
...(this.config || {}),
selectedDatapoints: this.transformValue(this.formArray.value),
defaultActiveState: true,
allowChangingContext,
allowSearch: !this.config?.contextAsset
})
.then(result => {
this.writeValue(result);
}, () => {
// nothing to do, modal was closed
});
}
onItemRemoved(index) {
this.formArray.removeAt(index);
}
drop(event) {
const currentSorting = this.formArray.value;
moveItemInArray(currentSorting, event.previousIndex, event.currentIndex);
this.formArray.setValue(currentSorting);
}
transformValue(formArrayValue) {
if (!formArrayValue) {
return [];
}
return formArrayValue.map(tmp => Object.assign({}, ...Object.values(tmp)));
}
calculateMaxActiveCount() {
if (this.maxActiveCount) {
const currentlyActive = this.formArray.value.filter(tmp => tmp.details?.__active).length;
this.maxActiveCountReached = currentlyActive >= this.maxActiveCount;
}
this.maxActiveCountReached = false;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DatapointSelectionListComponent, deps: [{ token: i1.DatapointSelectorService }, { token: i2.DatapointLibraryService }, { token: i3.FormBuilder }, { token: i4.WidgetConfigComponent, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DatapointSelectionListComponent, selector: "c8y-datapoint-selection-list", inputs: { actions: "actions", allowDragAndDrop: "allowDragAndDrop", config: "config", defaultFormOptions: "defaultFormOptions", maxActiveCount: "maxActiveCount", minActiveCount: "minActiveCount", resolveContext: "resolveContext", listTitle: "listTitle" }, outputs: { isValid: "isValid", change: "change" }, providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => DatapointSelectionListComponent)
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => DatapointSelectionListComponent),
multi: true
}
], usesOnChanges: true, ngImport: i0, template: "<div class=\"card-header separator sticky-top bg-inherit\">\n <span\n class=\"card-title h4\"\n *ngIf=\"listTitle\"\n >\n {{ listTitle | translate }}\n </span>\n <span\n class=\"card-title h4\"\n *ngIf=\"!listTitle\"\n >\n {{ 'Data points' | translate }}\n </span>\n</div>\n\n<c8y-list-group\n class=\"flex-grow ff-scroll-fix cdk-droplist\"\n cdkDropList\n (cdkDropListDropped)=\"drop($event)\"\n [cdkDropListDisabled]=\"!allowDragAndDrop || formArray.controls?.length < 2\"\n>\n <div\n class=\"alert alert-warning m-t-8\"\n role=\"alert\"\n ngNonBindable\n *ngIf=\"formArray.errors?.minActiveCount\"\n translate\n [translateParams]=\"formArray.errors?.minActiveCount\"\n >\n At least {{ minActive }} active data points must be selected.\n </div>\n\n <div\n class=\"alert alert-warning m-t-8\"\n role=\"alert\"\n ngNonBindable\n *ngIf=\"formArray.errors?.maxActiveCount\"\n translate\n [translateParams]=\"formArray.errors?.maxActiveCount\"\n >\n At maximum {{ maxActive }} active data points are allowed to be selected.\n </div>\n\n <ng-content select=\".alert\"></ng-content>\n\n <div\n class=\"p-t-8\"\n *ngIf=\"!formArray.controls?.length\"\n >\n <c8y-ui-empty-state\n class=\"p-t-8\"\n [icon]=\"'c8y-data-points'\"\n [title]=\"'No data points to display.' | translate\"\n [subtitle]=\"'Add your first data point.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n <div\n [formGroup]=\"dpForm\"\n *ngFor=\"let dpForm of formArray.controls; let index = index\"\n >\n <c8y-datapoint-selector-list-item\n class=\"d-block\"\n [defaultFormOptions]=\"defaultFormOptions\"\n [activeToggleDisabled]=\"maxActiveCountReached\"\n [showActiveToggle]=\"true\"\n [addButtonType]=\"AddButtonTypes.none\"\n [showOptions]=\"true\"\n [editable]=\"true\"\n [colorPickerDisabled]=\"false\"\n [actions]=\"actions\"\n [optionToRemove]=\"true\"\n [datapointLibraryEntries]=\"datapointLibraryEntries\"\n [hasUnlinkTemplateOption]=\"true\"\n formControlName=\"details\"\n (removed)=\"onItemRemoved(index)\"\n cdkDrag\n >\n <c8y-li-drag-handle\n title=\"{{ 'Click and drag to reorder' | translate }}\"\n cdkDragHandle\n >\n <i c8yIcon=\"drag-reorder\"></i>\n </c8y-li-drag-handle>\n </c8y-datapoint-selector-list-item>\n </div>\n</c8y-list-group>\n\n<div class=\"card-footer bg-inherit\">\n <button\n class=\"btn btn-default btn-sm\"\n [title]=\"'Add data point' | translate\"\n type=\"button\"\n data-cy=\"c8y-datapoint-selection-list--add-datapoint-button\"\n (click)=\"add()\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add data point' | translate }}\n </button>\n</div>\n", dependencies: [{ kind: "component", type: i5.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i5.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i5.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i6.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i6.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i5.ListGroupComponent, selector: "c8y-list-group" }, { kind: "component", type: i5.ListItemDragHandleComponent, selector: "c8y-list-item-drag-handle, c8y-li-drag-handle" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i7.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i7.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i7.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: i8.DatapointSelectorListItemComponent, selector: "c8y-datapoint-selector-list-item", inputs: ["defaultFormOptions", "isSelected", "isCollapsed", "addButtonType", "editable", "showActiveToggle", "activeToggleDisabled", "showOptions", "datapointLibraryEntries", "actions", "optionToRemove", "hasUnlinkTemplateOption", "colorPickerDisabled", "disableTypeaheadIfSelected", "highlightText"], outputs: ["added", "removed"] }, { kind: "pipe", type: i5.C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DatapointSelectionListComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-datapoint-selection-list', providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => DatapointSelectionListComponent)
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => DatapointSelectionListComponent),
multi: true
}
], template: "<div class=\"card-header separator sticky-top bg-inherit\">\n <span\n class=\"card-title h4\"\n *ngIf=\"listTitle\"\n >\n {{ listTitle | translate }}\n </span>\n <span\n class=\"card-title h4\"\n *ngIf=\"!listTitle\"\n >\n {{ 'Data points' | translate }}\n </span>\n</div>\n\n<c8y-list-group\n class=\"flex-grow ff-scroll-fix cdk-droplist\"\n cdkDropList\n (cdkDropListDropped)=\"drop($event)\"\n [cdkDropListDisabled]=\"!allowDragAndDrop || formArray.controls?.length < 2\"\n>\n <div\n class=\"alert alert-warning m-t-8\"\n role=\"alert\"\n ngNonBindable\n *ngIf=\"formArray.errors?.minActiveCount\"\n translate\n [translateParams]=\"formArray.errors?.minActiveCount\"\n >\n At least {{ minActive }} active data points must be selected.\n </div>\n\n <div\n class=\"alert alert-warning m-t-8\"\n role=\"alert\"\n ngNonBindable\n *ngIf=\"formArray.errors?.maxActiveCount\"\n translate\n [translateParams]=\"formArray.errors?.maxActiveCount\"\n >\n At maximum {{ maxActive }} active data points are allowed to be selected.\n </div>\n\n <ng-content select=\".alert\"></ng-content>\n\n <div\n class=\"p-t-8\"\n *ngIf=\"!formArray.controls?.length\"\n >\n <c8y-ui-empty-state\n class=\"p-t-8\"\n [icon]=\"'c8y-data-points'\"\n [title]=\"'No data points to display.' | translate\"\n [subtitle]=\"'Add your first data point.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n <div\n [formGroup]=\"dpForm\"\n *ngFor=\"let dpForm of formArray.controls; let index = index\"\n >\n <c8y-datapoint-selector-list-item\n class=\"d-block\"\n [defaultFormOptions]=\"defaultFormOptions\"\n [activeToggleDisabled]=\"maxActiveCountReached\"\n [showActiveToggle]=\"true\"\n [addButtonType]=\"AddButtonTypes.none\"\n [showOptions]=\"true\"\n [editable]=\"true\"\n [colorPickerDisabled]=\"false\"\n [actions]=\"actions\"\n [optionToRemove]=\"true\"\n [datapointLibraryEntries]=\"datapointLibraryEntries\"\n [hasUnlinkTemplateOption]=\"true\"\n formControlName=\"details\"\n (removed)=\"onItemRemoved(index)\"\n cdkDrag\n >\n <c8y-li-drag-handle\n title=\"{{ 'Click and drag to reorder' | translate }}\"\n cdkDragHandle\n >\n <i c8yIcon=\"drag-reorder\"></i>\n </c8y-li-drag-handle>\n </c8y-datapoint-selector-list-item>\n </div>\n</c8y-list-group>\n\n<div class=\"card-footer bg-inherit\">\n <button\n class=\"btn btn-default btn-sm\"\n [title]=\"'Add data point' | translate\"\n type=\"button\"\n data-cy=\"c8y-datapoint-selection-list--add-datapoint-button\"\n (click)=\"add()\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add data point' | translate }}\n </button>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.DatapointSelectorService }, { type: i2.DatapointLibraryService }, { type: i3.FormBuilder }, { type: i4.WidgetConfigComponent, decorators: [{
type: Optional
}] }], propDecorators: { actions: [{
type: Input
}], allowDragAndDrop: [{
type: Input
}], config: [{
type: Input
}], defaultFormOptions: [{
type: Input
}], maxActiveCount: [{
type: Input
}], minActiveCount: [{
type: Input
}], resolveContext: [{
type: Input
}], listTitle: [{
type: Input
}], isValid: [{
type: Output
}], change: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YXBvaW50LXNlbGVjdGlvbi1saXN0LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2RhdGFwb2ludC1zZWxlY3Rvci9kYXRhcG9pbnQtc2VsZWN0aW9uLWxpc3QvZGF0YXBvaW50LXNlbGVjdGlvbi1saXN0LmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uL2RhdGFwb2ludC1zZWxlY3Rvci9kYXRhcG9pbnQtc2VsZWN0aW9uLWxpc3QvZGF0YXBvaW50LXNlbGVjdGlvbi1saXN0LmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBZSxlQUFlLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUN0RSxPQUFPLEVBQ0wsU0FBUyxFQUNULFVBQVUsRUFDVixLQUFLLEVBR0wsUUFBUSxFQUNSLE1BQU0sRUFFUCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBSUwsV0FBVyxFQUNYLGFBQWEsRUFDYixpQkFBaUIsRUFJbEIsTUFBTSxnQkFBZ0IsQ0FBQztBQUV4QixPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDcEQsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sdUNBQXVDLENBQUM7QUFDOUUsT0FBTyxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDeEMsT0FBTyxFQUFFLEdBQUcsRUFBRSxXQUFXLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzdELE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBUXZFLE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQ3pFLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSx3RUFBd0UsQ0FBQzs7Ozs7Ozs7OztBQWtCeEcsTUFBTSxPQUFPLCtCQUErQjtJQW9CMUMsWUFDVSxpQkFBMkMsRUFDM0MsZ0JB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QkFBZCxLQUFLO2dCQUNHLGtCQUFrQjtzQkFBMUIsS0FBSztnQkFDRyxjQUFjO3NCQUF0QixLQUFLO2dCQUNHLGNBQWM7c0JBQXRCLEtBQUs7Z0JBQ0csY0FBYztzQkFBdEIsS0FBSztnQkFDRyxTQUFTO3NCQUFqQixLQUFLO2dCQU1JLE9BQU87c0JBQWhCLE1BQU07Z0JBQ0csTUFBTTtzQkFBZixNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ2RrRHJhZ0Ryb3AsIG1vdmVJdGVtSW5BcnJheSB9IGZyb20gJ0Bhbmd1bGFyL2Nkay9kcmFnLWRyb3AnO1xuaW1wb3J0IHtcbiAgQ29tcG9uZW50LFxuICBmb3J3YXJkUmVmLFxuICBJbnB1dCxcbiAgT25DaGFuZ2VzLFxuICBPbkluaXQsXG4gIE9wdGlvbmFsLFxuICBPdXRwdXQsXG4gIFNpbXBsZUNoYW5nZXNcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQge1xuICBBYnN0cmFjdENvbnRyb2wsXG4gIENvbnRyb2xWYWx1ZUFjY2Vzc29yLFxuICBGb3JtQXJyYXksXG4gIEZvcm1CdWlsZGVyLFxuICBOR19WQUxJREFUT1JTLFxuICBOR19WQUxVRV9BQ0NFU1NPUixcbiAgVmFsaWRhdGlvbkVycm9ycyxcbiAgVmFsaWRhdG9yLFxuICBWYWxpZGF0b3JGblxufSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XG5pbXBvcnQgeyBJUmVzdWx0TGlzdCB9IGZyb20gJ0BjOHkvY2xpZW50JztcbmltcG9ydCB7IEM4eVZhbGlkYXRvcnMgfSBmcm9tICdAYzh5L25neC1jb21wb25lbnRzJztcbmltcG9ydCB7IFdpZGdldENvbmZpZ0NvbXBvbmVudCB9IGZyb20gJ0BjOHkvbmd4LWNvbXBvbmVudHMvY29udGV4dC1kYXNoYm9hcmQnO1xuaW1wb3J0IHsgZnJvbSwgT2JzZXJ2YWJsZSB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgbWFwLCBzaGFyZVJlcGxheSwgdGFrZSwgdGFwIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgRGF0YXBvaW50TGlicmFyeVNlcnZpY2UgfSBmcm9tICcuLi9kYXRhcG9pbnQtbGlicmFyeS5zZXJ2aWNlJztcbmltcG9ydCB7XG4gIERhdGFwb2ludEFjdGlvbixcbiAgRGF0YXBvaW50QXR0cmlidXRlc0Zvcm1Db25maWcsXG4gIEtQSURldGFpbHMsXG4gIE1hbmFnZWRPYmplY3RLUElcbn0gZnJvbSAnLi4vZGF0YXBvaW50LXNlbGVjdGlvbi5tb2RlbCc7XG5pbXBvcnQgeyBEYXRhcG9pbnRTZWxlY3Rvck1vZGFsT3B0aW9ucyB9IGZyb20gJy4uL2RhdGFwb2ludC1zZWxlY3Rvci1tb2RhbC9kYXRhcG9pbnQtc2VsZWN0b3ItbW9kYWwubW9kZWwnO1xuaW1wb3J0IHsgRGF0YXBvaW50U2VsZWN0b3JTZXJ2aWNlIH0gZnJvbSAnLi4vZGF0YXBvaW50LXNlbGVjdG9yLnNlcnZpY2UnO1xuaW1wb3J0IHsgQWRkQnV0dG9uVHlwZXMgfSBmcm9tICcuLi9kYXRhcG9pbnQtc2VsZWN0b3ItbGlzdC1pdGVtL2RhdGFwb2ludC1zZWxlY3Rvci1saXN0LWl0ZW0uY29tcG9uZW50JztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnYzh5LWRhdGFwb2ludC1zZWxlY3Rpb24tbGlzdCcsXG4gIHRlbXBsYXRlVXJsOiAnLi9kYXRhcG9pbnQtc2VsZWN0aW9uLWxpc3QuY29tcG9uZW50Lmh0bWwnLFxuICBwcm92aWRlcnM6IFtcbiAgICB7XG4gICAgICBwcm92aWRlOiBOR19WQUxVRV9BQ0NFU1NPUixcbiAgICAgIG11bHRpOiB0cnVlLFxuICAgICAgdXNlRXhpc3Rpbmc6IGZvcndhcmRSZWYoKCkgPT4gRGF0YXBvaW50U2VsZWN0aW9uTGlzdENvbXBvbmVudClcbiAgICB9LFxuICAgIHtcbiAgICAgIHByb3ZpZGU6IE5HX1ZBTElEQVRPUlMsXG4gICAgICB1c2VFeGlzdGluZzogZm9yd2FyZFJlZigoKSA9PiBEYXRhcG9pbnRTZWxlY3Rpb25MaXN0Q29tcG9uZW50KSxcbiAgICAgIG11bHRpOiB0cnVlXG4gICAgfVxuICBdXG59KVxuZXhwb3J0IGNsYXNzIERhdGFwb2ludFNlbGVjdGlvbkxpc3RDb21wb25lbnRcbiAgaW1wbGVtZW50cyBDb250cm9sVmFsdWVBY2Nlc3NvciwgVmFsaWRhdG9yLCBPbkluaXQsIE9uQ2hhbmdlc1xue1xuICBASW5wdXQoKSBhY3Rpb25zOiBEYXRhcG9pbnRBY3Rpb25bXSA9IFtdO1xuICBASW5wdXQoKSBhbGxvd0RyYWdBbmREcm9wID0gdHJ1ZTtcbiAgQElucHV0KCkgY29uZmlnOiBQYXJ0aWFsPERhdGFwb2ludFNlbGVjdG9yTW9kYWxPcHRpb25zPiA9IHt9O1xuICBASW5wdXQoKSBkZWZhdWx0Rm9ybU9wdGlvbnM6IFBhcnRpYWw8RGF0YXBvaW50QXR0cmlidXRlc0Zvcm1Db25maWc+ID0ge307XG4gIEBJbnB1dCgpIG1heEFjdGl2ZUNvdW50OiBudW1iZXI7XG4gIEBJbnB1dCgpIG1pbkFjdGl2ZUNvdW50ID0gMTtcbiAgQElucHV0KCkgcmVzb2x2ZUNvbnRleHQgPSB0cnVlO1xuICBASW5wdXQoKSBsaXN0VGl0bGUgPSAnJztcbiAgZm9ybUFycmF5OiBGb3JtQXJyYXk7XG4gIGRhdGFwb2ludExpYnJhcnlFbnRyaWVzOiBPYnNlcnZhYmxlPElSZXN1bHRMaXN0PE1hbmFnZWRPYmplY3RLUEk+PjtcbiAgbWF4QWN0aXZlQ291bnRSZWFjaGVkID0gZmFsc2U7XG4gIEFkZEJ1dHRvblR5cGVzID0gQWRkQnV0dG9uVHlwZXM7XG5cbiAgQE91dHB1dCgpIGlzVmFsaWQ6IE9ic2VydmFibGU8Ym9vbGVhbj47XG4gIEBPdXRwdXQoKSBjaGFuZ2U6IE9ic2VydmFibGU8YW55W10+O1xuICBwcml2YXRlIHVzZWRWYWxpZGF0b3JzOiB7IFtrZXk6IHN0cmluZ106IFZhbGlkYXRvckZuIH0gPSB7fTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIGRhdGFwb2ludFNlbGVjdG9yOiBEYXRhcG9pbnRTZWxlY3RvclNlcnZpY2UsXG4gICAgcHJpdmF0ZSBkYXRhcG9pbnRMaWJyYXJ5OiBEYXRhcG9pbnRMaWJyYXJ5U2VydmljZSxcbiAgICBwcml2YXRlIGZvcm1CdWlsZGVyOiBGb3JtQnVpbGRlcixcbiAgICBAT3B0aW9uYWwoKSBwcml2YXRlIHdpZGdldENvbXBvbmVudDogV2lkZ2V0Q29uZmlnQ29tcG9uZW50XG4gICkge1xuICAgIHRoaXMuZm9ybUFycmF5ID0gdGhpcy5mb3JtQnVpbGRlci5hcnJheShbXSk7XG4gICAgdGhpcy5pc1ZhbGlkID0gdGhpcy5mb3JtQXJyYXkuc3RhdHVzQ2hhbmdlcy5waXBlKG1hcChzdGF0dXMgPT4gc3RhdHVzID09PSAnVkFMSUQnKSk7XG4gICAgdGhpcy5kYXRhcG9pbnRMaWJyYXJ5RW50cmllcyA9IGZyb20odGhpcy5kYXRhcG9pbnRMaWJyYXJ5LmdldEZpcnN0RGF0YXBvaW50TGlicmFyeVBhZ2UoKSkucGlwZShcbiAgICAgIHNoYXJlUmVwbGF5KClcbiAgICApO1xuICAgIHRoaXMuY2hhbmdlID0gdGhpcy5mb3JtQXJyYXkudmFsdWVDaGFuZ2VzLnBpcGUobWFwKHJlcyA9PiB0aGlzLnRyYW5zZm9ybVZhbHVlKHJlcykpKTtcbiAgfVxuXG4gIG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpOiB2b2lkIHtcbiAgICBpZiAoIWNoYW5nZXMubWF4QWN0aXZlQ291bnQgJiYgIWNoYW5nZXMubWluQWN0aXZlQ291bnQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKGNoYW5nZXMubWF4QWN0aXZlQ291bnQpIHtcbiAgICAgIHRoaXMudXNlZFZhbGlkYXRvcnMubWF4QWN0aXZlQ291bnQgPSBDOHlWYWxpZGF0b3JzLm1heEFjdGl2ZUNvdW50KHRoaXMubWF4QWN0aXZlQ291bnQpO1xuICAgIH1cblxuICAgIGlmIChjaGFuZ2VzLm1pbkFjdGl2ZUNvdW50KSB7XG4gICAgICB0aGlzLnVzZWRWYWxpZGF0b3JzLm1pbkFjdGl2ZUNvdW50ID0gQzh5VmFsaWRhdG9ycy5taW5BY3RpdmVDb3VudCh0aGlzLm1pbkFjdGl2ZUNvdW50KTtcbiAgICB9XG4gICAgY29uc3QgdmFsaWRhdG9ycyA9IE9iamVjdC52YWx1ZXModGhpcy51c2VkVmFsaWRhdG9ycyk7XG4gICAgdGhpcy5mb3JtQXJyYXkuc2V0VmFsaWRhdG9ycyh2YWxpZGF0b3JzKTtcbiAgfVxuXG4gIHJlZ2lzdGVyT25Ub3VjaGVkKGZuOiBhbnkpOiB2b2lkIHtcbiAgICB0aGlzLmZvcm1BcnJheS52YWx1ZUNoYW5nZXMucGlwZSh0YWtlKDEpKS5zdWJzY3JpYmUoZm4pO1xuICB9XG5cbiAgdmFsaWRhdGUoX2NvbnRyb2w6IEFic3RyYWN0Q29udHJvbCk6IFZhbGlkYXRpb25FcnJvcnMge1xuICAgIHJldHVybiB0aGlzLmZvcm1BcnJheS52YWxpZCA/IG51bGwgOiB7IGZvcm1JbnZhbGlkOiB7fSB9O1xuICB9XG5cbiAgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgY29uc3QgY29udGV4dCA9IHRoaXMud2lkZ2V0Q29tcG9uZW50Py5jb250ZXh0O1xuICAgIGlmIChjb250ZXh0Py5pZCAmJiB0aGlzLnJlc29sdmVDb250ZXh0KSB7XG4gICAgICBjb25zdCB7IG5hbWUsIGlkLCBjOHlfSXNEZXZpY2UgfSA9IGNvbnRleHQ7XG4gICAgICB0aGlzLmNvbmZpZy5jb250ZXh0QXNzZXQgPSB7IG5hbWUsIGlkLCBjOHlfSXNEZXZpY2UgfTtcbiAgICB9XG4gIH1cblxuICB3cml0ZVZhbHVlKG9iajogS1BJRGV0YWlsc1tdKTogdm9pZCB7XG4gICAgdGhpcy5mb3JtQXJyYXkuY2xlYXIoKTtcbiAgICBpZiAob2JqPy5sZW5ndGgpIHtcbiAgICAgIG9iai5mb3JFYWNoKHZhbCA9PiB7XG4gICAgICAgIGNvbnN0IGZvcm1ncm91cCA9IHRoaXMuZm9ybUJ1aWxkZXIuZ3JvdXAoeyBkZXRhaWxzOiBbXSB9KTtcbiAgICAgICAgZm9ybWdyb3VwLnBhdGNoVmFsdWUoeyBkZXRhaWxzOiB2YWwgfSk7XG4gICAgICAgIHRoaXMuZm9ybUFycmF5LnB1c2goZm9ybWdyb3VwKTtcbiAgICAgIH0pO1xuICAgIH1cbiAgICB0aGlzLmNhbGN1bGF0ZU1heEFjdGl2ZUNvdW50KCk7XG4gIH1cblxuICByZWdpc3Rlck9uQ2hhbmdlKGZuOiBhbnkpOiB2b2lkIHtcbiAgICB0aGlzLmZvcm1BcnJheS52YWx1ZUNoYW5nZXNcbiAgICAgIC5waXBlKFxuICAgICAgICBtYXAocmVzID0+IHRoaXMudHJhbnNmb3JtVmFsdWUocmVzKSksXG4gICAgICAgIC8vIGNoZWNrIG1heEFjdGl2ZUNvdW50XG4gICAgICAgIHRhcCgoKSA9PiB7XG4gICAgICAgICAgdGhpcy5jYWxjdWxhdGVNYXhBY3RpdmVDb3VudCgpO1xuICAgICAgICB9KVxuICAgICAgKVxuICAgICAgLnN1YnNjcmliZShmbik7XG4gIH1cblxuICBhZGQoKSB7XG4gICAgY29uc3QgYWxsb3dDaGFuZ2luZ0NvbnRleHQgPVxuICAgICAgIXRoaXMud2lkZ2V0Q29tcG9uZW50Py5pc0RldmljZVR5cGVEYXNoYm9hcmQgJiYgdGhpcy5jb25maWc/LmFsbG93Q2hhbmdpbmdDb250ZXh0ICE9PSBmYWxzZTtcbiAgICB0aGlzLmRhdGFwb2ludFNlbGVjdG9yXG4gICAgICAuc2VsZWN0RGF0YVBvaW50cyh7XG4gICAgICAgIC4uLih0aGlzLmNvbmZpZyB8fCB7fSksXG4gICAgICAgIHNlbGVjdGVkRGF0YXBvaW50czogdGhpcy50cmFuc2Zvcm1WYWx1ZSh0aGlzLmZvcm1BcnJheS52YWx1ZSksXG4gICAgICAgIGRlZmF1bHRBY3RpdmVTdGF0ZTogdHJ1ZSxcbiAgICAgICAgYWxsb3dDaGFuZ2luZ0NvbnRleHQsXG4gICAgICAgIGFsbG93U2VhcmNoOiAhdGhpcy5jb25maWc/LmNvbnRleHRBc3NldFxuICAgICAgfSlcbiAgICAgIC50aGVuKFxuICAgICAgICByZXN1bHQgPT4ge1xuICAgICAgICAgIHRoaXMud3JpdGVWYWx1ZShyZXN1bHQpO1xuICAgICAgICB9LFxuICAgICAgICAoKSA9PiB7XG4gICAgICAgICAgLy8gbm90aGluZyB0byBkbywgbW9kYWwgd2FzIGNsb3NlZFxuICAgICAgICB9XG4gICAgICApO1xuICB9XG5cbiAgb25JdGVtUmVtb3ZlZChpbmRleDogbnVtYmVyKSB7XG4gICAgdGhpcy5mb3JtQXJyYXkucmVtb3ZlQXQoaW5kZXgpO1xuICB9XG5cbiAgZHJvcChldmVudDogQ2RrRHJhZ0Ryb3A8S1BJRGV0YWlsc1tdPikge1xuICAgIGNvbnN0IGN1cnJlbnRTb3J0aW5nID0gdGhpcy5mb3JtQXJyYXkudmFsdWU7XG4gICAgbW92ZUl0ZW1JbkFycmF5KGN1cnJlbnRTb3J0aW5nLCBldmVudC5wcmV2aW91c0luZGV4LCBldmVudC5jdXJyZW50SW5kZXgpO1xuICAgIHRoaXMuZm9ybUFycmF5LnNldFZhbHVlKGN1cnJlbnRTb3J0aW5nKTtcbiAgfVxuXG4gIHByaXZhdGUgdHJhbnNmb3JtVmFsdWUoZm9ybUFycmF5VmFsdWU6IGFueVtdKSB7XG4gICAgaWYgKCFmb3JtQXJyYXlWYWx1ZSkge1xuICAgICAgcmV0dXJuIFtdO1xuICAgIH1cbiAgICByZXR1cm4gZm9ybUFycmF5VmFsdWUubWFwKHRtcCA9PiBPYmplY3QuYXNzaWduKHt9LCAuLi5PYmplY3QudmFsdWVzKHRtcCkpKTtcbiAgfVxuXG4gIHByaXZhdGUgY2FsY3VsYXRlTWF4QWN0aXZlQ291bnQoKSB7XG4gICAgaWYgKHRoaXMubWF4QWN0aXZlQ291bnQpIHtcbiAgICAgIGNvbnN0IGN1cnJlbnRseUFjdGl2ZSA9IHRoaXMuZm9ybUFycmF5LnZhbHVlLmZpbHRlcih0bXAgPT4gdG1wLmRldGFpbHM/Ll9fYWN0aXZlKS5sZW5ndGg7XG4gICAgICB0aGlzLm1heEFjdGl2ZUNvdW50UmVhY2hlZCA9IGN1cnJlbnRseUFjdGl2ZSA+PSB0aGlzLm1heEFjdGl2ZUNvdW50O1xuICAgIH1cbiAgICB0aGlzLm1heEFjdGl2ZUNvdW50UmVhY2hlZCA9IGZhbHNlO1xuICB9XG59XG4iLCI8ZGl2IGNsYXNzPVwiY2FyZC1oZWFkZXIgc2VwYXJhdG9yIHN0aWNreS10b3AgYmctaW5oZXJpdFwiPlxuICA8c3BhblxuICAgIGNsYXNzPVwiY2FyZC10aXRsZSBoNFwiXG4gICAgKm5nSWY9XCJsaXN0VGl0bGVcIlxuICA+XG4gICAge3sgbGlzdFRpdGxlIHwgdHJhbnNsYXRlIH19XG4gIDwvc3Bhbj5cbiAgPHNwYW5cbiAgICBjbGFzcz1cImNhcmQtdGl0bGUgaDRcIlxuICAgICpuZ0lmPVwiIWxpc3RUaXRsZVwiXG4gID5cbiAgICB7eyAnRGF0YSBwb2ludHMnIHwgdHJhbnNsYXRlIH19XG4gIDwvc3Bhbj5cbjwvZGl2PlxuXG48Yzh5LWxpc3QtZ3JvdXBcbiAgY2xhc3M9XCJmbGV4LWdyb3cgZmYtc2Nyb2xsLWZpeCBjZGstZHJvcGxpc3RcIlxuICBjZGtEcm9wTGlzdFxuICAoY2RrRHJvcExpc3REcm9wcGVkKT1cImRyb3AoJGV2ZW50KVwiXG4gIFtjZGtEcm9wTGlzdERpc2FibGVkXT1cIiFhbGxvd0RyYWdBbmREcm9wIHx8IGZvcm1BcnJheS5jb250cm9scz8ubGVuZ3RoIDwgMlwiXG4+XG4gIDxkaXZcbiAgICBjbGFzcz1cImFsZXJ0IGFsZXJ0LXdhcm5pbmcgbS10LThcIlxuICAgIHJvbGU9XCJhbGVydFwiXG4gICAgbmdOb25CaW5kYWJsZVxuICAgICpuZ0lmPVwiZm9ybUFycmF5LmVycm9ycz8ubWluQWN0aXZlQ291bnRcIlxuICAgIHRyYW5zbGF0ZVxuICAgIFt0cmFuc2xhdGVQYXJhbXNdPVwiZm9ybUFycmF5LmVycm9ycz8ubWluQWN0aXZlQ291bnRcIlxuICA+XG4gICAgQXQgbGVhc3Qge3sgbWluQWN0aXZlIH19IGFjdGl2ZSBkYXRhIHBvaW50cyBtdXN0IGJlIHNlbGVjdGVkLlxuICA8L2Rpdj5cblxuICA8ZGl2XG4gICAgY2xhc3M9XCJhbGVydCBhbGVydC13YXJuaW5nIG0tdC04XCJcbiAgICByb2xlPVwiYWxlcnRcIlxuICAgIG5nTm9uQmluZGFibGVcbiAgICAqbmdJZj1cImZvcm1BcnJheS5lcnJvcnM/Lm1heEFjdGl2ZUNvdW50XCJcbiAgICB0cmFuc2xhdGVcbiAgICBbdHJhbnNsYXRlUGFyYW1zXT1cImZvcm1BcnJheS5lcnJvcnM/Lm1heEFjdGl2ZUNvdW50XCJcbiAgPlxuICAgIEF0IG1heGltdW0ge3sgbWF4QWN0aXZlIH19IGFjdGl2ZSBkYXRhIHBvaW50cyBhcmUgYWxsb3dlZCB0byBiZSBzZWxlY3RlZC5cbiAgPC9kaXY+XG5cbiAgPG5nLWNvbnRlbnQgc2VsZWN0PVwiLmFsZXJ0XCI+PC9uZy1jb250ZW50PlxuXG4gIDxkaXZcbiAgICBjbGFzcz1cInAtdC04XCJcbiAgICAqbmdJZj1cIiFmb3JtQXJyYXkuY29udHJvbHM/Lmxlbmd0aFwiXG4gID5cbiAgICA8Yzh5LXVpLWVtcHR5LXN0YXRlXG4gICAgICBjbGFzcz1cInAtdC04XCJcbiAgICAgIFtpY29uXT1cIidjOHktZGF0YS1wb2ludHMnXCJcbiAgICAgIFt0aXRsZV09XCInTm8gZGF0YSBwb2ludHMgdG8gZGlzcGxheS4nIHwgdHJhbnNsYXRlXCJcbiAgICAgIFtzdWJ0aXRsZV09XCInQWRkIHlvdXIgZmlyc3QgZGF0YSBwb2ludC4nIHwgdHJhbnNsYXRlXCJcbiAgICAgIFtob3Jpem9udGFsXT1cInRydWVcIlxuICAgID48L2M4eS11aS1lbXB0eS1zdGF0ZT5cbiAgPC9kaXY+XG4gIDxkaXZcbiAgICBbZm9ybUdyb3VwXT1cImRwRm9ybVwiXG4gICAgKm5nRm9yPVwibGV0IGRwRm9ybSBvZiBmb3JtQXJyYXkuY29udHJvbHM7IGxldCBpbmRleCA9IGluZGV4XCJcbiAgPlxuICAgIDxjOHktZGF0YXBvaW50LXNlbGVjdG9yLWxpc3QtaXRlbVxuICAgICAgY2xhc3M9XCJkLWJsb2NrXCJcbiAgICAgIFtkZWZhdWx0Rm9ybU9wdGlvbnNdPVwiZGVmYXVsdEZvcm1PcHRpb25zXCJcbiAgICAgIFthY3RpdmVUb2dnbGVEaXNhYmxlZF09XCJtYXhBY3RpdmVDb3VudFJlYWNoZWRcIlxuICAgICAgW3Nob3dBY3RpdmVUb2dnbGVdPVwidHJ1ZVwiXG4gICAgICBbYWRkQnV0dG9uVHlwZV09XCJBZGRCdXR0b25UeXBlcy5ub25lXCJcbiAgICAgIFtzaG93T3B0aW9uc109XCJ0cnVlXCJcbiAgICAgIFtlZGl0YWJsZV09XCJ0cnVlXCJcbiAgICAgIFtjb2xvclBpY2tlckRpc2FibGVkXT1cImZhbHNlXCJcbiAgICAgIFthY3Rpb25zXT1cImFjdGlvbnNcIlxuICAgICAgW29wdGlvblRvUmVtb3ZlXT1cInRydWVcIlxuICAgICAgW2RhdGFwb2ludExpYnJhcnlFbnRyaWVzXT1cImRhdGFwb2ludExpYnJhcnlFbnRyaWVzXCJcbiAgICAgIFtoYXNVbmxpbmtUZW1wbGF0ZU9wdGlvbl09XCJ0cnVlXCJcbiAgICAgIGZvcm1Db250cm9sTmFtZT1cImRldGFpbHNcIlxuICAgICAgKHJlbW92ZWQpPVwib25JdGVtUmVtb3ZlZChpbmRleClcIlxuICAgICAgY2RrRHJhZ1xuICAgID5cbiAgICAgIDxjOHktbGktZHJhZy1oYW5kbGVcbiAgICAgICAgdGl0bGU9XCJ7eyAnQ2xpY2sgYW5kIGRyYWcgdG8gcmVvcmRlcicgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgICBjZGtEcmFnSGFuZGxlXG4gICAgICA+XG4gICAgICAgIDxpIGM4eUljb249XCJkcmFnLXJlb3JkZXJcIj48L2k+XG4gICAgICA8L2M4eS1saS1kcmFnLWhhbmRsZT5cbiAgICA8L2M4eS1kYXRhcG9pbnQtc2VsZWN0b3ItbGlzdC1pdGVtPlxuICA8L2Rpdj5cbjwvYzh5LWxpc3QtZ3JvdXA+XG5cbjxkaXYgY2xhc3M9XCJjYXJkLWZvb3RlciBiZy1pbmhlcml0XCI+XG4gIDxidXR0b25cbiAgICBjbGFzcz1cImJ0biBidG4tZGVmYXVsdCBidG4tc21cIlxuICAgIFt0aXRsZV09XCInQWRkIGRhdGEgcG9pbnQnIHwgdHJhbnNsYXRlXCJcbiAgICB0eXBlPVwiYnV0dG9uXCJcbiAgICBkYXRhLWN5PVwiYzh5LWRhdGFwb2ludC1zZWxlY3Rpb24tbGlzdC0tYWRkLWRhdGFwb2ludC1idXR0b25cIlxuICAgIChjbGljayk9XCJhZGQoKVwiXG4gID5cbiAgICA8aSBjOHlJY29uPVwicGx1cy1jaXJjbGVcIj48L2k+XG4gICAge3sgJ0FkZCBkYXRhIHBvaW50JyB8IHRyYW5zbGF0ZSB9fVxuICA8L2J1dHRvbj5cbjwvZGl2PlxuIl19