UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

323 lines 58.6 kB
import { Component, Input, signal } from '@angular/core'; import { FormBuilder, FormControl, ReactiveFormsModule } from '@angular/forms'; import { AlertService, CommonModule, DismissAlertStrategy, DynamicComponentAlert, WidgetGlobalAutoRefreshService, gettext, globalAutoRefreshLoading } from '@c8y/ngx-components'; import { DataFetchingService, DatapointsExportSelectorComponent, dateRangeValidator } from '@c8y/ngx-components/datapoints-export-selector'; import { INTERVAL_VALUES } from '@c8y/ngx-components/interval-picker'; import { TranslateService } from '@ngx-translate/core'; import { BehaviorSubject, Subject, debounceTime, takeUntil } from 'rxjs'; import { DEFAULT_DPT_REFRESH_INTERVAL_VALUE } from '../datapoints-table-widget.model'; import { DatapointsTableService } from '../datapoints-table.service'; import { DateRangePickerComponent } from '../date-range-picker.component'; import { DatapointsReloadComponent } from './datapoints-reload/datapoints-reload.component'; import { DatapointsTableViewService } from './datapoints-table-view.service'; import { DatapointsTableComponent } from './datapoints-table/datapoints-table.component'; import * as i0 from "@angular/core"; import * as i1 from "@c8y/ngx-components"; import * as i2 from "@c8y/ngx-components/datapoints-export-selector"; import * as i3 from "../datapoints-table.service"; import * as i4 from "./datapoints-table-view.service"; import * as i5 from "@angular/forms"; import * as i6 from "@ngx-translate/core"; import * as i7 from "@angular/common"; export class DatapointsTableViewWidgetComponent { constructor(alertService, dataFetchingService, datapointsTableConfigService, datapointsTableViewService, formBuilder, translateService, widgetGlobalAutoRefresh) { this.alertService = alertService; this.dataFetchingService = dataFetchingService; this.datapointsTableConfigService = datapointsTableConfigService; this.datapointsTableViewService = datapointsTableViewService; this.formBuilder = formBuilder; this.translateService = translateService; this.widgetGlobalAutoRefresh = widgetGlobalAutoRefresh; /** * Represents the data points where __active property is set to true. */ this.activeDatapoints = []; /** * An array of `GroupedDatapointTableItem` objects representing the datapoints table items. * Used to populate the table with data. */ this.datapointsTableItems = []; /** * Indicates whether refreshing should be enabled or disabled. * It's 'true' when user is not allowed to view a measurements. */ this.isRefreshDisabled = false; /** * Indicates whether the component is in the initial request state * where data for a table structure is being prepared. */ this.isInitialRequest = true; /** * Current isLoading state. Indicates whether the data is being loaded. */ this.isLoading$ = new BehaviorSubject(true); this.isExportModalOpen = false; this.isScrolling = signal(false); this.TIMEOUT_ERROR_TEXT = gettext('The request is taking longer than usual. We apologize for the inconvenience.'); this.SERVER_ERROR_TEXT = gettext('Server error occurred.'); this.destroy$ = new Subject(); this.scrollingSubject$ = new Subject(); /** * Indicates if the alert has already been displayed and can be dismissed. * The message is only displayed when a component is initialized. */ this.isMissingAnyPermissionAlertShown = false; } async ngOnInit() { if (this.config.widgetInstanceGlobalAutoRefreshContext) { this.containerClass = 'a-i-center input-group p-t-4 p-b-4 max-width-fit m-l-auto'; } /** * Legacy widget used realtime so it must be converted to refresh approach * if it was enabled before migration. */ if (this.config.widgetInstanceGlobalTimeContext) { this.config.widgetInstanceGlobalAutoRefreshContext = true; this.config.widgetInstanceGlobalTimeContext = false; this.config.displaySettings.globalAutoRefreshContext = true; } if (this.config.widgetInstanceGlobalAutoRefreshContext) { this.handleGlobalRefreshLoading(); this.handleGlobalTimeContextSettings(); } this.setScrollingSubscription(); const isLegacyWidgetRealtimeActive = this.config.realtime && !this.config.hasOwnProperty('decimalPlaces') && !this.config.hasOwnProperty('refreshInterval') && !this.config.hasOwnProperty('isAutoRefreshEnabled'); if (isLegacyWidgetRealtimeActive) { this.setDefaultRefreshRelatedProperties(); } if (this.config.interval !== INTERVAL_VALUES.custom) { this.recalculateIntervalToMatchFromNowDate(); } if (this.config.displayDateSelection) { this.prepareDateRangeForm(); } await this.prepareTableData(); this.exportConfig = { aggregation: this.config.aggregation, dateFrom: this.config.dateFrom, dateTo: this.config.dateTo, datapointDetails: this.activeDatapoints.map(({ __target, fragment, series }) => ({ deviceName: __target.name, source: __target.id, valueFragmentSeries: series, valueFragmentType: fragment })) }; } ngOnDestroy() { if (this.subscription) { this.subscription.unsubscribe(); } this.destroy$.next(); this.destroy$.complete(); } onDateChange(data) { if (this.formGroup.invalid) { return; } if (data.dateFrom) { this.config.dateFrom = data.dateFrom; } if (data.dateTo) { this.config.dateTo = data.dateTo; } this.prepareTableData(); this.updateExportConfigDateRange(); } onExportModalOpen(isOpened) { this.isExportModalOpen = isOpened; } onScrolling(value) { this.scrollingSubject$.next(value); } async onCountdownEnded() { const dateRange = this.datapointsTableViewService.prepareTimeRange(this.config.interval, this.config.dateFrom, this.config.dateTo); this.config.dateFrom = dateRange.dateFrom; this.config.dateTo = dateRange.dateTo; this.updateExportConfigDateRange(); await this.prepareTableData(false); this.onScrolling(false); } handleGlobalTimeContextSettings() { this.config.dateFrom = this.dataFetchingService.adjustDate(this.config.date[0], 0, false); this.config.dateTo = this.dataFetchingService.adjustDate(this.config.date[1], 0, false); } /** * Sets up the scrolling subscription. * * Ensures similar UX as in the alarms countdown-pause logic. */ setScrollingSubscription() { this.subscription = this.scrollingSubject$ .pipe(debounceTime(300)) .subscribe(value => this.isScrolling.set(value)); } setDefaultRefreshRelatedProperties() { this.config.isAutoRefreshEnabled = true; this.config.refreshInterval = DEFAULT_DPT_REFRESH_INTERVAL_VALUE; } recalculateIntervalToMatchFromNowDate() { if (this.config.widgetInstanceGlobalAutoRefreshContext) { return; } const { dateFrom, dateTo } = this.datapointsTableConfigService.calculateDateRange(this.config.interval); this.config.dateFrom = dateFrom; this.config.dateTo = dateTo; } prepareDateRangeForm() { this.formGroup = this.createForm(); } createForm() { return this.formBuilder.group({ dateFrom: new FormControl(this.config.dateFrom), dateTo: new FormControl(this.config.dateTo) }, { validators: dateRangeValidator }); } /** * Prepares the table data by: * - filtering out inactive data points, * - checking if there are multiple devices as a source of data points, * - getting the column headers for devices, * - getting the series data for active data points (API call), * - preparing data points with values list, * - mapping data points with values to list items, * - grouping data points by date and device, * - sorting data by date descending. * * @param roundSeconds - Whether to round the seconds or not. * If true, the seconds will be rounded to 0. * If false, the seconds will be displayed as they are. */ async prepareTableData(roundSeconds = true) { this.isLoading$.next(true); if (this.isInitialRequest) { this.activeDatapoints = this.datapointsTableViewService.filterOutInactiveDatapoints(this.config.datapoints); this.hasMultipleDatapoints = this.datapointsTableViewService.hasMultipleDatapoints(this.activeDatapoints); this.devicesColumnHeaders = this.datapointsTableViewService.getColumnHeaders(this.activeDatapoints); this.isInitialRequest = false; } const activeDatapointsIdsWithSeries = this.datapointsTableViewService.groupSeriesByDeviceId(this.activeDatapoints); const activeDatapointsSeriesData = await this.getActiveDatapointsSeriesDataMap(activeDatapointsIdsWithSeries, this.config, roundSeconds); if (this.isFailedToFetchSeriesData) { return; } this.seriesWithoutPermissionToRead = this.datapointsTableViewService.getSeriesWithoutPermissionToRead(activeDatapointsSeriesData, activeDatapointsIdsWithSeries); if (!this.isMissingAnyPermissionAlertShown && this.seriesWithoutPermissionToRead?.length > 0 && activeDatapointsSeriesData?.size) { this.handleMissingAnyPermissionErrorMessage(); this.isMissingAnyPermissionAlertShown = true; } if (!activeDatapointsSeriesData) { return; } this.datapointsWithValues = this.datapointsTableViewService.getDatapointsWithValues(this.activeDatapoints, activeDatapointsSeriesData); const datapointsListItems = this.datapointsTableViewService.mapDatapointsWithValuesToList(this.datapointsWithValues); const groupedDatapointsListItems = this.datapointsTableViewService.groupByDateAndDevice(datapointsListItems, this.devicesColumnHeaders); this.datapointsTableItems = this.datapointsTableViewService.sortDataByDateDescending(groupedDatapointsListItems); this.isLoading$.next(false); } /** * Retrieves the active data points series data and returns it as a map. * * It's a wrapper method with try-catch block. * * @param datapointsIdsWithSeries - A map of data point IDs with their associated series. * @param config - The configuration of the data points table. * @param roundSeconds - Whether to round the seconds or not. * If true, the seconds will be rounded to 0. * If false, the seconds will be displayed as they are. * @returns A Promise that resolves to a Map object with data point IDs as keys and DataObject as values. */ async getActiveDatapointsSeriesDataMap(datapointsIdsWithSeries, config, roundSeconds) { try { this.isFailedToFetchSeriesData = false; return await this.datapointsTableViewService.getAllActiveSeriesDataMap(datapointsIdsWithSeries, config, roundSeconds); } catch (error) { this.isLoading$.next(false); this.handleFetchError(error); } } handleFetchError(error) { if (error?.message.includes('403')) { this.alerts.setAlertGroupDismissStrategy('warning', DismissAlertStrategy.NONE); this.isRefreshDisabled = true; return; } const isTimeoutError = error?.name === 'TimeoutError'; const errorMessage = isTimeoutError ? this.TIMEOUT_ERROR_TEXT : (error?.message ?? this.SERVER_ERROR_TEXT); this.alerts.setAlertGroupDismissStrategy('warning', DismissAlertStrategy.TEMPORARY_OR_PERMANENT); this.alerts.addAlerts(new DynamicComponentAlert({ type: 'warning', text: errorMessage })); this.isFailedToFetchSeriesData = true; this.alertService.addServerFailure(error); } handleMissingAnyPermissionErrorMessage() { this.alerts.setAlertGroupDismissStrategy('system', DismissAlertStrategy.TEMPORARY); this.alerts.addAlerts(new DynamicComponentAlert({ allowHtml: true, text: this.getMissingPermissionsMessage(), type: 'system' })); } getMissingPermissionsMessage() { const baseMessage = this.translateService.instant(gettext(`You don't have permissions to read the following series:`)); const formattedList = this.seriesWithoutPermissionToRead .map(datapoint => `<li>${datapoint.key.valueOf()}_${datapoint.value}</li>`) .join(''); return ` <div> <p>${baseMessage}</p> <ul> ${formattedList} </ul> <p><strong>Note:</strong> The name convention for each list item is: <code>[source]_[fragment.series]</code></p> </div> `; } updateExportConfigDateRange() { this.config.dateTo = this.adjustTargetDateBasedOnConditionTime(this.config.dateFrom, this.config.dateTo); this.config.dateFrom = this.adjustTargetDateBasedOnConditionTime(this.config.dateTo, this.config.dateFrom); this.syncExportConfigDates(); } adjustTargetDateBasedOnConditionTime(conditionDate, targetDate) { if (this.datapointsTableViewService.hasSecondsAndMillisecondsEqualZero(conditionDate)) { return this.dataFetchingService.adjustDate(targetDate, 0, true); } return targetDate; } syncExportConfigDates() { this.exportConfig.dateFrom = this.config.dateFrom; this.exportConfig.dateTo = this.config.dateTo; } handleGlobalRefreshLoading() { this.isLoading$ .pipe(globalAutoRefreshLoading(this.widgetGlobalAutoRefresh), takeUntil(this.destroy$)) .subscribe(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DatapointsTableViewWidgetComponent, deps: [{ token: i1.AlertService }, { token: i2.DataFetchingService }, { token: i3.DatapointsTableService }, { token: i4.DatapointsTableViewService }, { token: i5.FormBuilder }, { token: i6.TranslateService }, { token: i1.WidgetGlobalAutoRefreshService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: DatapointsTableViewWidgetComponent, isStandalone: true, selector: "c8y-datapoints-table-view", inputs: { config: "config" }, host: { classAttribute: "d-col fit-h" }, ngImport: i0, template: "<div class=\"d-flex gap-16 p-l-16 p-r-16 flex-wrap\">\n <ng-container *ngIf=\"config.displayDateSelection\">\n <form [formGroup]=\"formGroup\">\n <c8y-date-range-picker\n class=\"d-contents\"\n [isEmittingDateChange]=\"true\"\n [showLabel]=\"true\"\n (updatedDate)=\"onDateChange($event)\"\n ></c8y-date-range-picker>\n </form>\n </ng-container>\n <ng-container *ngIf=\"config.datapoints.length > 0\">\n <c8y-datapoints-reload\n class=\"d-contents\"\n [isAutoRefreshEnabled]=\"config.isAutoRefreshEnabled\"\n [refreshInterval]=\"config.refreshInterval\"\n [isRefreshDisabled]=\"isRefreshDisabled\"\n [isLoading]=\"isLoading$\"\n [isScrolling]=\"isScrolling()\"\n [isExportModalOpen]=\"isExportModalOpen\"\n [widgetInstanceGlobalAutoRefreshContext]=\"config.widgetInstanceGlobalAutoRefreshContext\"\n (onCountdownEnded)=\"onCountdownEnded()\"\n ></c8y-datapoints-reload>\n </ng-container>\n <c8y-datapoints-export-selector\n class=\"d-contents\"\n [exportConfig]=\"exportConfig\"\n (isOpen)=\"onExportModalOpen($event)\"\n ></c8y-datapoints-export-selector>\n</div>\n<ng-container *ngIf=\"devicesColumnHeaders\">\n <c8y-datapoints-table\n [aggregationType]=\"config.aggregation\"\n [datapointsTableItems]=\"datapointsTableItems\"\n [decimalPlaces]=\"config.decimalPlaces\"\n [devicesColumnHeaders]=\"devicesColumnHeaders\"\n [hasMultipleDatapoints]=\"hasMultipleDatapoints\"\n [isLoading]=\"isLoading$ | async\"\n [seriesWithoutPermissionToReadCount]=\"seriesWithoutPermissionToRead?.length\"\n (isScrolling)=\"onScrolling($event)\"\n ></c8y-datapoints-table>\n</ng-container>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i7.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i7.AsyncPipe, name: "async" }, { kind: "component", type: DatapointsExportSelectorComponent, selector: "c8y-datapoints-export-selector", inputs: ["containerClass", "exportConfig"], outputs: ["isOpen"] }, { kind: "component", type: DatapointsReloadComponent, selector: "c8y-datapoints-reload", inputs: ["isAutoRefreshEnabled", "isRefreshDisabled", "isLoading", "isScrolling", "isExportModalOpen", "refreshInterval", "widgetInstanceGlobalAutoRefreshContext"], outputs: ["onCountdownEnded"] }, { kind: "component", type: DatapointsTableComponent, selector: "c8y-datapoints-table", inputs: ["aggregationType", "datapointsTableItems", "devicesColumnHeaders", "decimalPlaces", "hasMultipleDatapoints", "isLoading", "seriesWithoutPermissionToReadCount"], outputs: ["isScrolling"] }, { kind: "component", type: DateRangePickerComponent, selector: "c8y-date-range-picker", inputs: ["isEmittingDateChange", "showLabel"], outputs: ["updatedDate"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i5.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i5.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i5.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DatapointsTableViewWidgetComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-datapoints-table-view', host: { class: 'd-col fit-h' }, standalone: true, imports: [ CommonModule, DatapointsExportSelectorComponent, DatapointsReloadComponent, DatapointsTableComponent, DateRangePickerComponent, ReactiveFormsModule ], template: "<div class=\"d-flex gap-16 p-l-16 p-r-16 flex-wrap\">\n <ng-container *ngIf=\"config.displayDateSelection\">\n <form [formGroup]=\"formGroup\">\n <c8y-date-range-picker\n class=\"d-contents\"\n [isEmittingDateChange]=\"true\"\n [showLabel]=\"true\"\n (updatedDate)=\"onDateChange($event)\"\n ></c8y-date-range-picker>\n </form>\n </ng-container>\n <ng-container *ngIf=\"config.datapoints.length > 0\">\n <c8y-datapoints-reload\n class=\"d-contents\"\n [isAutoRefreshEnabled]=\"config.isAutoRefreshEnabled\"\n [refreshInterval]=\"config.refreshInterval\"\n [isRefreshDisabled]=\"isRefreshDisabled\"\n [isLoading]=\"isLoading$\"\n [isScrolling]=\"isScrolling()\"\n [isExportModalOpen]=\"isExportModalOpen\"\n [widgetInstanceGlobalAutoRefreshContext]=\"config.widgetInstanceGlobalAutoRefreshContext\"\n (onCountdownEnded)=\"onCountdownEnded()\"\n ></c8y-datapoints-reload>\n </ng-container>\n <c8y-datapoints-export-selector\n class=\"d-contents\"\n [exportConfig]=\"exportConfig\"\n (isOpen)=\"onExportModalOpen($event)\"\n ></c8y-datapoints-export-selector>\n</div>\n<ng-container *ngIf=\"devicesColumnHeaders\">\n <c8y-datapoints-table\n [aggregationType]=\"config.aggregation\"\n [datapointsTableItems]=\"datapointsTableItems\"\n [decimalPlaces]=\"config.decimalPlaces\"\n [devicesColumnHeaders]=\"devicesColumnHeaders\"\n [hasMultipleDatapoints]=\"hasMultipleDatapoints\"\n [isLoading]=\"isLoading$ | async\"\n [seriesWithoutPermissionToReadCount]=\"seriesWithoutPermissionToRead?.length\"\n (isScrolling)=\"onScrolling($event)\"\n ></c8y-datapoints-table>\n</ng-container>\n" }] }], ctorParameters: () => [{ type: i1.AlertService }, { type: i2.DataFetchingService }, { type: i3.DatapointsTableService }, { type: i4.DatapointsTableViewService }, { type: i5.FormBuilder }, { type: i6.TranslateService }, { type: i1.WidgetGlobalAutoRefreshService }], propDecorators: { config: [{ type: Input }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YXBvaW50cy10YWJsZS12aWV3LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3dpZGdldHMvaW1wbGVtZW50YXRpb25zL2RhdGFwb2ludHMtdGFibGUvZGF0YXBvaW50cy10YWJsZS12aWV3L2RhdGFwb2ludHMtdGFibGUtdmlldy5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi93aWRnZXRzL2ltcGxlbWVudGF0aW9ucy9kYXRhcG9pbnRzLXRhYmxlL2RhdGFwb2ludHMtdGFibGUtdmlldy9kYXRhcG9pbnRzLXRhYmxlLXZpZXcuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQXFCLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUM1RSxPQUFPLEVBQUUsV0FBVyxFQUFFLFdBQVcsRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRS9FLE9BQU8sRUFDTCxZQUFZLEVBQ1osWUFBWSxFQUNaLG9CQUFvQixFQUNwQixxQkFBcUIsRUFFckIsOEJBQThCLEVBQzlCLE9BQU8sRUFDUCx3QkFBd0IsRUFDekIsTUFBTSxxQkFBcUIsQ0FBQztBQUU3QixPQUFPLEVBQ0wsbUJBQW1CLEVBQ25CLGlDQUFpQyxFQUlqQyxrQkFBa0IsRUFDbkIsTUFBTSxnREFBZ0QsQ0FBQztBQUN4RCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFDdEUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDdkQsT0FBTyxFQUFFLGVBQWUsRUFBRSxPQUFPLEVBQWdCLFlBQVksRUFBRSxTQUFTLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDdkYsT0FBTyxFQUNMLGtDQUFrQyxFQU1uQyxNQUFNLGtDQUFrQyxDQUFDO0FBQzFDLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLDZC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aUNBQWlDO3dCQUNqQyx5QkFBeUI7d0JBQ3pCLHdCQUF3Qjt3QkFDeEIsd0JBQXdCO3dCQUN4QixtQkFBbUI7cUJBQ3BCO3VTQU1RLE1BQU07c0JBQWQsS0FBSyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgSW5wdXQsIE9uRGVzdHJveSwgT25Jbml0LCBzaWduYWwgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEZvcm1CdWlsZGVyLCBGb3JtQ29udHJvbCwgUmVhY3RpdmVGb3Jtc01vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcbmltcG9ydCB7IElTZXJpZXMgfSBmcm9tICdAYzh5L2NsaWVudCc7XG5pbXBvcnQge1xuICBBbGVydFNlcnZpY2UsXG4gIENvbW1vbk1vZHVsZSxcbiAgRGlzbWlzc0FsZXJ0U3RyYXRlZ3ksXG4gIER5bmFtaWNDb21wb25lbnRBbGVydCxcbiAgRHluYW1pY0NvbXBvbmVudEFsZXJ0QWdncmVnYXRvcixcbiAgV2lkZ2V0R2xvYmFsQXV0b1JlZnJlc2hTZXJ2aWNlLFxuICBnZXR0ZXh0LFxuICBnbG9iYWxBdXRvUmVmcmVzaExvYWRpbmdcbn0gZnJvbSAnQGM4eS9uZ3gtY29tcG9uZW50cyc7XG5pbXBvcnQgeyBLUElEZXRhaWxzIH0gZnJvbSAnQGM4eS9uZ3gtY29tcG9uZW50cy9kYXRhcG9pbnQtc2VsZWN0b3InO1xuaW1wb3J0IHtcbiAgRGF0YUZldGNoaW5nU2VydmljZSxcbiAgRGF0YXBvaW50c0V4cG9ydFNlbGVjdG9yQ29tcG9uZW50LFxuICBEYXRhcG9pbnRzVmFsdWVzRGF0YU1hcCxcbiAgRXhwb3J0Q29uZmlnLFxuICBTb3VyY2VJZCxcbiAgZGF0ZVJhbmdlVmFsaWRhdG9yXG59IGZyb20gJ0BjOHkvbmd4LWNvbXBvbmVudHMvZGF0YXBvaW50cy1leHBvcnQtc2VsZWN0b3InO1xuaW1wb3J0IHsgSU5URVJWQUxfVkFMVUVTIH0gZnJvbSAnQGM4eS9uZ3gtY29tcG9uZW50cy9pbnRlcnZhbC1waWNrZXInO1xuaW1wb3J0IHsgVHJhbnNsYXRlU2VydmljZSB9IGZyb20gJ0BuZ3gtdHJhbnNsYXRlL2NvcmUnO1xuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0LCBTdWJqZWN0LCBTdWJzY3JpcHRpb24sIGRlYm91bmNlVGltZSwgdGFrZVVudGlsIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQge1xuICBERUZBVUxUX0RQVF9SRUZSRVNIX0lOVEVSVkFMX1ZBTFVFLFxuICBEYXRhcG9pbnRUYWJsZUl0ZW0sXG4gIERhdGFwb2ludFdpdGhWYWx1ZXMsXG4gIERhdGFwb2ludHNUYWJsZUNvbmZpZyxcbiAgR3JvdXBlZERhdGFwb2ludFRhYmxlSXRlbSxcbiAgVGFibGVDb2x1bW5IZWFkZXJcbn0gZnJvbSAnLi4vZGF0YXBvaW50cy10YWJsZS13aWRnZXQubW9kZWwnO1xuaW1wb3J0IHsgRGF0YXBvaW50c1RhYmxlU2VydmljZSB9IGZyb20gJy4uL2RhdGFwb2ludHMtdGFibGUuc2VydmljZSc7XG5pbXBvcnQgeyBEYXRlUmFuZ2VQaWNrZXJDb21wb25lbnQgfSBmcm9tICcuLi9kYXRlLXJhbmdlLXBpY2tlci5jb21wb25lbnQnO1xuaW1wb3J0IHsgRGF0YXBvaW50c1JlbG9hZENvbXBvbmVudCB9IGZyb20gJy4vZGF0YXBvaW50cy1yZWxvYWQvZGF0YXBvaW50cy1yZWxvYWQuY29tcG9uZW50JztcbmltcG9ydCB7IERhdGFwb2ludHNUYWJsZVZpZXdTZXJ2aWNlIH0gZnJvbSAnLi9kYXRhcG9pbnRzLXRhYmxlLXZpZXcuc2VydmljZSc7XG5pbXBvcnQgeyBEYXRhcG9pbnRzVGFibGVDb21wb25lbnQgfSBmcm9tICcuL2RhdGFwb2ludHMtdGFibGUvZGF0YXBvaW50cy10YWJsZS5jb21wb25lbnQnO1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdjOHktZGF0YXBvaW50cy10YWJsZS12aWV3JyxcbiAgdGVtcGxhdGVVcmw6ICcuL2RhdGFwb2ludHMtdGFibGUtdmlldy5jb21wb25lbnQuaHRtbCcsXG4gIGhvc3Q6IHsgY2xhc3M6ICdkLWNvbCBmaXQtaCcgfSxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW1xuICAgIENvbW1vbk1vZHVsZSxcbiAgICBEYXRhcG9pbnRzRXhwb3J0U2VsZWN0b3JDb21wb25lbnQsXG4gICAgRGF0YXBvaW50c1JlbG9hZENvbXBvbmVudCxcbiAgICBEYXRhcG9pbnRzVGFibGVDb21wb25lbnQsXG4gICAgRGF0ZVJhbmdlUGlja2VyQ29tcG9uZW50LFxuICAgIFJlYWN0aXZlRm9ybXNNb2R1bGVcbiAgXVxufSlcbmV4cG9ydCBjbGFzcyBEYXRhcG9pbnRzVGFibGVWaWV3V2lkZ2V0Q29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBPbkRlc3Ryb3kge1xuICAvKipcbiAgICogIERhdGEgcG9pbnRzIHRhYmxlIHdpZGdldCBjb25maWcuXG4gICAqL1xuICBASW5wdXQoKSBjb25maWc6IERhdGFwb2ludHNUYWJsZUNvbmZpZztcblxuICBhbGVydHM6IER5bmFtaWNDb21wb25lbnRBbGVydEFnZ3JlZ2F0b3I7XG5cbiAgLyoqXG4gICAqIFJlcHJlc2VudHMgdGhlIGRhdGEgcG9pbnRzIHdoZXJlIF9fYWN0aXZlIHByb3BlcnR5IGlzIHNldCB0byB0cnVlLlxuICAgKi9cbiAgYWN0aXZlRGF0YXBvaW50czogS1BJRGV0YWlsc1tdID0gW107XG4gIC8qKlxuICAgKiBSZXByZXNlbnRzIHRoZSBjdXN0b20gQ1NTIHN0eWxlIGZvciB0aGUgZXhwb3J0IHNlbGVjdG9yIGNvbXBvbmVudC5cbiAgICovXG4gIGNvbnRhaW5lckNsYXNzOiBzdHJpbmc7XG4gIC8qKlxuICAgKiBBbiBhcnJheSBvZiBvYmplY3RzIHJlcHJlc2VudGluZyBkYXRhcG9pbnRzIHdpdGggdGhlaXIgY29ycmVzcG9uZGluZyB2YWx1ZXMuXG4gICAqIFVzZWQgdG8gcG9wdWxhdGUgdGhlIENTVi9FeGNlbCBmaWxlIHdpdGggZGF0YS5cbiAgICovXG4gIGRhdGFwb2ludHNXaXRoVmFsdWVzOiBEYXRhcG9pbnRXaXRoVmFsdWVzW107XG4gIC8qKlxuICAgKiBBbiBhcnJheSBvZiBgR3JvdXBlZERhdGFwb2ludFRhYmxlSXRlbWAgb2JqZWN0cyByZXByZXNlbnRpbmcgdGhlIGRhdGFwb2ludHMgdGFibGUgaXRlbXMuXG4gICAqIFVzZWQgdG8gcG9wdWxhdGUgdGhlIHRhYmxlIHdpdGggZGF0YS5cbiAgICovXG4gIGRhdGFwb2ludHNUYWJsZUl0ZW1zOiBHcm91cGVkRGF0YXBvaW50VGFibGVJdGVtW10gPSBbXTtcblxuICBkZXZpY2VzQ29sdW1uSGVhZGVyczogVGFibGVDb2x1bW5IZWFkZXJbXTtcbiAgLyoqXG4gICAqIFJlcHJlc2VudHMgYSBjb25maWd1cmF0aW9uIG9wdGlvbnMgdXNlZCBieSBhIGM4eS1kYXRhcG9pbnRzLWV4cG9ydC1zZWxlY3Rvci5cbiAgICovXG4gIGV4cG9ydENvbmZpZzogRXhwb3J0Q29uZmlnO1xuXG4gIGZvcm1Hcm91cDogUmV0dXJuVHlwZTxEYXRhcG9pbnRzVGFibGVWaWV3V2lkZ2V0Q29tcG9uZW50WydjcmVhdGVGb3JtJ10+O1xuICAvKipcbiAgICogSW5kaWNhdGVzIHdoZXRoZXIgdGhlcmUgaXMgbW9yZSB0aGFuIG9uZSBkYXRhIHBvaW50LlxuICAgKiBJZiBpcyB0cnVlLCB0aGVuIGEgY29sdW1uICdEZXZpY2UnIHdpbGwgYmUgZGlzcGxheWVkIGluIHRoZSB0YWJsZS5cbiAgICovXG4gIGhhc011bHRpcGxlRGF0YXBvaW50czogYm9vbGVhbjtcbiAgLyoqXG4gICAqIEluZGljYXRlcyB3aGV0aGVyIHJlZnJlc2hpbmcgc2hvdWxkIGJlIGVuYWJsZWQgb3IgZGlzYWJsZWQuXG4gICAqIEl0J3MgJ3RydWUnIHdoZW4gdXNlciBpcyBub3QgYWxsb3dlZCB0byB2aWV3IGEgbWVhc3VyZW1lbnRzLlxuICAgKi9cbiAgaXNSZWZyZXNoRGlzYWJsZWQgPSBmYWxzZTtcbiAgLyoqXG4gICAqIEluZGljYXRlcyB3aGV0aGVyIHRoZSBjb21wb25lbnQgaXMgaW4gdGhlIGluaXRpYWwgcmVxdWVzdCBzdGF0ZVxuICAgKiB3aGVyZSBkYXRhIGZvciBhIHRhYmxlIHN0cnVjdHVyZSBpcyBiZWluZyBwcmVwYXJlZC5cbiAgICovXG4gIGlzSW5pdGlhbFJlcXVlc3QgPSB0cnVlO1xuICAvKipcbiAgICogQ3VycmVudCBpc0xvYWRpbmcgc3RhdGUuIEluZGljYXRlcyB3aGV0aGVyIHRoZSBkYXRhIGlzIGJlaW5nIGxvYWRlZC5cbiAgICovXG4gIGlzTG9hZGluZyQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+KHRydWUpO1xuXG4gIGlzRXhwb3J0TW9kYWxPcGVuID0gZmFsc2U7XG5cbiAgaXNTY3JvbGxpbmcgPSBzaWduYWwoZmFsc2UpO1xuXG4gIHNlcmllc1dpdGhvdXRQZXJtaXNzaW9uVG9SZWFkOiB7IGtleTogU291cmNlSWQ7IHZhbHVlOiBzdHJpbmdbXSB9W107XG5cbiAgcHJpdmF0ZSBUSU1FT1VUX0VSUk9SX1RFWFQgPSBnZXR0ZXh0KFxuICAgICdUaGUgcmVxdWVzdCBpcyB0YWtpbmcgbG9uZ2VyIHRoYW4gdXN1YWwuIFdlIGFwb2xvZ2l6ZSBmb3IgdGhlIGluY29udmVuaWVuY2UuJ1xuICApO1xuICBwcml2YXRlIFNFUlZFUl9FUlJPUl9URVhUID0gZ2V0dGV4dCgnU2VydmVyIGVycm9yIG9jY3VycmVkLicpO1xuXG4gIHByaXZhdGUgZGVzdHJveSQ6IFN1YmplY3Q8dm9pZD4gPSBuZXcgU3ViamVjdCgpO1xuICBwcml2YXRlIHNjcm9sbGluZ1N1YmplY3QkOiBTdWJqZWN0PGJvb2xlYW4+ID0gbmV3IFN1YmplY3Q8Ym9vbGVhbj4oKTtcbiAgcHJpdmF0ZSBzdWJzY3JpcHRpb246IFN1YnNjcmlwdGlvbjtcbiAgcHJpdmF0ZSBpc0ZhaWxlZFRvRmV0Y2hTZXJpZXNEYXRhOiBib29sZWFuO1xuICAvKipcbiAgICogSW5kaWNhdGVzIGlmIHRoZSBhbGVydCBoYXMgYWxyZWFkeSBiZWVuIGRpc3BsYXllZCBhbmQgY2FuIGJlIGRpc21pc3NlZC5cbiAgICogVGhlIG1lc3NhZ2UgaXMgb25seSBkaXNwbGF5ZWQgd2hlbiBhIGNvbXBvbmVudCBpcyBpbml0aWFsaXplZC5cbiAgICovXG4gIHByaXZhdGUgaXNNaXNzaW5nQW55UGVybWlzc2lvbkFsZXJ0U2hvd24gPSBmYWxzZTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIGFsZXJ0U2VydmljZTogQWxlcnRTZXJ2aWNlLFxuICAgIHByaXZhdGUgZGF0YUZldGNoaW5nU2VydmljZTogRGF0YUZldGNoaW5nU2VydmljZSxcbiAgICBwcml2YXRlIGRhdGFwb2ludHNUYWJsZUNvbmZpZ1NlcnZpY2U6IERhdGFwb2ludHNUYWJsZVNlcnZpY2UsXG4gICAgcHJpdmF0ZSBkYXRhcG9pbnRzVGFibGVWaWV3U2VydmljZTogRGF0YXBvaW50c1RhYmxlVmlld1NlcnZpY2UsXG4gICAgcHJpdmF0ZSBmb3JtQnVpbGRlcjogRm9ybUJ1aWxkZXIsXG4gICAgcHJpdmF0ZSB0cmFuc2xhdGVTZXJ2aWNlOiBUcmFuc2xhdGVTZXJ2aWNlLFxuICAgIHByaXZhdGUgd2lkZ2V0R2xvYmFsQXV0b1JlZnJlc2g6IFdpZGdldEdsb2JhbEF1dG9SZWZyZXNoU2VydmljZVxuICApIHt9XG5cbiAgYXN5bmMgbmdPbkluaXQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKHRoaXMuY29uZmlnLndpZGdldEluc3RhbmNlR2xvYmFsQXV0b1JlZnJlc2hDb250ZXh0KSB7XG4gICAgICB0aGlzLmNvbnRhaW5lckNsYXNzID0gJ2EtaS1jZW50ZXIgaW5wdXQtZ3JvdXAgcC10LTQgcC1iLTQgbWF4LXdpZHRoLWZpdCBtLWwtYXV0byc7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogTGVnYWN5IHdpZGdldCB1c2VkIHJlYWx0aW1lIHNvIGl0IG11c3QgYmUgY29udmVydGVkIHRvIHJlZnJlc2ggYXBwcm9hY2hcbiAgICAgKiBpZiBpdCB3YXMgZW5hYmxlZCBiZWZvcmUgbWlncmF0aW9uLlxuICAgICAqL1xuICAgIGlmICh0aGlzLmNvbmZpZy53aWRnZXRJbnN0YW5jZUdsb2JhbFRpbWVDb250ZXh0KSB7XG4gICAgICB0aGlzLmNvbmZpZy53aWRnZXRJbnN0YW5jZUdsb2JhbEF1dG9SZWZyZXNoQ29udGV4dCA9IHRydWU7XG4gICAgICB0aGlzLmNvbmZpZy53aWRnZXRJbnN0YW5jZUdsb2JhbFRpbWVDb250ZXh0ID0gZmFsc2U7XG4gICAgICB0aGlzLmNvbmZpZy5kaXNwbGF5U2V0dGluZ3MuZ2xvYmFsQXV0b1JlZnJlc2hDb250ZXh0ID0gdHJ1ZTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5jb25maWcud2lkZ2V0SW5zdGFuY2VHbG9iYWxBdXRvUmVmcmVzaENvbnRleHQpIHtcbiAgICAgIHRoaXMuaGFuZGxlR2xvYmFsUmVmcmVzaExvYWRpbmcoKTtcbiAgICAgIHRoaXMuaGFuZGxlR2xvYmFsVGltZUNvbnRleHRTZXR0aW5ncygpO1xuICAgIH1cblxuICAgIHRoaXMuc2V0U2Nyb2xsaW5nU3Vic2NyaXB0aW9uKCk7XG5cbiAgICBjb25zdCBpc0xlZ2FjeVdpZGdldFJlYWx0aW1lQWN0aXZlID1cbiAgICAgIHRoaXMuY29uZmlnLnJlYWx0aW1lICYmXG4gICAgICAhdGhpcy5jb25maWcuaGFzT3duUHJvcGVydHkoJ2RlY2ltYWxQbGFjZXMnKSAmJlxuICAgICAgIXRoaXMuY29uZmlnLmhhc093blByb3BlcnR5KCdyZWZyZXNoSW50ZXJ2YWwnKSAmJlxuICAgICAgIXRoaXMuY29uZmlnLmhhc093blByb3BlcnR5KCdpc0F1dG9SZWZyZXNoRW5hYmxlZCcpO1xuXG4gICAgaWYgKGlzTGVnYWN5V2lkZ2V0UmVhbHRpbWVBY3RpdmUpIHtcbiAgICAgIHRoaXMuc2V0RGVmYXVsdFJlZnJlc2hSZWxhdGVkUHJvcGVydGllcygpO1xuICAgIH1cblxuICAgIGlmICh0aGlzLmNvbmZpZy5pbnRlcnZhbCAhPT0gSU5URVJWQUxfVkFMVUVTLmN1c3RvbSkge1xuICAgICAgdGhpcy5yZWNhbGN1bGF0ZUludGVydmFsVG9NYXRjaEZyb21Ob3dEYXRlKCk7XG4gICAgfVxuXG4gICAgaWYgKHRoaXMuY29uZmlnLmRpc3BsYXlEYXRlU2VsZWN0aW9uKSB7XG4gICAgICB0aGlzLnByZXBhcmVEYXRlUmFuZ2VGb3JtKCk7XG4gICAgfVxuXG4gICAgYXdhaXQgdGhpcy5wcmVwYXJlVGFibGVEYXRhKCk7XG5cbiAgICB0aGlzLmV4cG9ydENvbmZpZyA9IHtcbiAgICAgIGFnZ3JlZ2F0aW9uOiB0aGlzLmNvbmZpZy5hZ2dyZWdhdGlvbixcbiAgICAgIGRhdGVGcm9tOiB0aGlzLmNvbmZpZy5kYXRlRnJvbSxcbiAgICAgIGRhdGVUbzogdGhpcy5jb25maWcuZGF0ZVRvLFxuICAgICAgZGF0YXBvaW50RGV0YWlsczogdGhpcy5hY3RpdmVEYXRhcG9pbnRzLm1hcCgoeyBfX3RhcmdldCwgZnJhZ21lbnQsIHNlcmllcyB9KSA9PiAoe1xuICAgICAgICBkZXZpY2VOYW1lOiBfX3RhcmdldC5uYW1lLFxuICAgICAgICBzb3VyY2U6IF9fdGFyZ2V0LmlkLFxuICAgICAgICB2YWx1ZUZyYWdtZW50U2VyaWVzOiBzZXJpZXMsXG4gICAgICAgIHZhbHVlRnJhZ21lbnRUeXBlOiBmcmFnbWVudFxuICAgICAgfSkpXG4gICAgfTtcbiAgfVxuXG4gIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgIGlmICh0aGlzLnN1YnNjcmlwdGlvbikge1xuICAgICAgdGhpcy5zdWJzY3JpcHRpb24udW5zdWJzY3JpYmUoKTtcbiAgICB9XG5cbiAgICB0aGlzLmRlc3Ryb3kkLm5leHQoKTtcbiAgICB0aGlzLmRlc3Ryb3kkLmNvbXBsZXRlKCk7XG4gIH1cblxuICBvbkRhdGVDaGFuZ2UoZGF0YTogeyBkYXRlRnJvbT86IHN0cmluZyB8IG51bGw7IGRhdGVUbz86IHN0cmluZyB8IG51bGwgfSk6IHZvaWQge1xuICAgIGlmICh0aGlzLmZvcm1Hcm91cC5pbnZhbGlkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKGRhdGEuZGF0ZUZyb20pIHtcbiAgICAgIHRoaXMuY29uZmlnLmRhdGVGcm9tID0gZGF0YS5kYXRlRnJvbTtcbiAgICB9XG4gICAgaWYgKGRhdGEuZGF0ZVRvKSB7XG4gICAgICB0aGlzLmNvbmZpZy5kYXRlVG8gPSBkYXRhLmRhdGVUbztcbiAgICB9XG4gICAgdGhpcy5wcmVwYXJlVGFibGVEYXRhKCk7XG5cbiAgICB0aGlzLnVwZGF0ZUV4cG9ydENvbmZpZ0RhdGVSYW5nZSgpO1xuICB9XG5cbiAgb25FeHBvcnRNb2RhbE9wZW4oaXNPcGVuZWQ6IGJvb2xlYW4pOiB2b2lkIHtcbiAgICB0aGlzLmlzRXhwb3J0TW9kYWxPcGVuID0gaXNPcGVuZWQ7XG4gIH1cblxuICBvblNjcm9sbGluZyh2YWx1ZTogYm9vbGVhbik6IHZvaWQge1xuICAgIHRoaXMuc2Nyb2xsaW5nU3ViamVjdCQubmV4dCh2YWx1ZSk7XG4gIH1cblxuICBhc3luYyBvbkNvdW50ZG93bkVuZGVkKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGRhdGVSYW5nZSA9IHRoaXMuZGF0YXBvaW50c1RhYmxlVmlld1NlcnZpY2UucHJlcGFyZVRpbWVSYW5nZShcbiAgICAgIHRoaXMuY29uZmlnLmludGVydmFsLFxuICAgICAgdGhpcy5jb25maWcuZGF0ZUZyb20sXG4gICAgICB0aGlzLmNvbmZpZy5kYXRlVG9cbiAgICApO1xuXG4gICAgdGhpcy5jb25maWcuZGF0ZUZyb20gPSBkYXRlUmFuZ2UuZGF0ZUZyb207XG4gICAgdGhpcy5jb25maWcuZGF0ZVRvID0gZGF0ZVJhbmdlLmRhdGVUbztcblxuICAgIHRoaXMudXBkYXRlRXhwb3J0Q29uZmlnRGF0ZVJhbmdlKCk7XG4gICAgYXdhaXQgdGhpcy5wcmVwYXJlVGFibGVEYXRhKGZhbHNlKTtcblxuICAgIHRoaXMub25TY3JvbGxpbmcoZmFsc2UpO1xuICB9XG5cbiAgcHJpdmF0ZSBoYW5kbGVHbG9iYWxUaW1lQ29udGV4dFNldHRpbmdzKCk6IHZvaWQge1xuICAgIHRoaXMuY29uZmlnLmRhdGVGcm9tID0gdGhpcy5kYXRhRmV0Y2hpbmdTZXJ2aWNlLmFkanVzdERhdGUodGhpcy5jb25maWcuZGF0ZVswXSwgMCwgZmFsc2UpO1xuICAgIHRoaXMuY29uZmlnLmRhdGVUbyA9IHRoaXMuZGF0YUZldGNoaW5nU2VydmljZS5hZGp1c3REYXRlKHRoaXMuY29uZmlnLmRhdGVbMV0sIDAsIGZhbHNlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXRzIHVwIHRoZSBzY3JvbGxpbmcgc3Vic2NyaXB0aW9uLlxuICAgKlxuICAgKiBFbnN1cmVzIHNpbWlsYXIgVVggYXMgaW4gdGhlIGFsYXJtcyBjb3VudGRvd24tcGF1c2UgbG9naWMuXG4gICAqL1xuICBwcml2YXRlIHNldFNjcm9sbGluZ1N1YnNjcmlwdGlvbigpOiB2b2lkIHtcbiAgICB0aGlzLnN1YnNjcmlwdGlvbiA9IHRoaXMuc2Nyb2xsaW5nU3ViamVjdCRcbiAgICAgIC5waXBlKGRlYm91bmNlVGltZSgzMDApKVxuICAgICAgLnN1YnNjcmliZSh2YWx1ZSA9PiB0aGlzLmlzU2Nyb2xsaW5nLnNldCh2YWx1ZSkpO1xuICB9XG5cbiAgcHJpdmF0ZSBzZXREZWZhdWx0UmVmcmVzaFJlbGF0ZWRQcm9wZXJ0aWVzKCk6IHZvaWQge1xuICAgIHRoaXMuY29uZmlnLmlzQXV0b1JlZnJlc2hFbmFibGVkID0gdHJ1ZTtcbiAgICB0aGlzLmNvbmZpZy5yZWZyZXNoSW50ZXJ2YWwgPSBERUZBVUxUX0RQVF9SRUZSRVNIX0lOVEVSVkFMX1ZBTFVFO1xuICB9XG5cbiAgcHJpdmF0ZSByZWNhbGN1bGF0ZUludGVydmFsVG9NYXRjaEZyb21Ob3dEYXRlKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmNvbmZpZy53aWRnZXRJbnN0YW5jZUdsb2JhbEF1dG9SZWZyZXNoQ29udGV4dCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHsgZGF0ZUZyb20sIGRhdGVUbyB9ID0gdGhpcy5kYXRhcG9pbnRzVGFibGVDb25maWdTZXJ2aWNlLmNhbGN1bGF0ZURhdGVSYW5nZShcbiAgICAgIHRoaXMuY29uZmlnLmludGVydmFsXG4gICAgKTtcbiAgICB0aGlzLmNvbmZpZy5kYXRlRnJvbSA9IGRhdGVGcm9tO1xuICAgIHRoaXMuY29uZmlnLmRhdGVUbyA9IGRhdGVUbztcbiAgfVxuXG4gIHByaXZhdGUgcHJlcGFyZURhdGVSYW5nZUZvcm0oKSB7XG4gICAgdGhpcy5mb3JtR3JvdXAgPSB0aGlzLmNyZWF0ZUZvcm0oKTtcbiAgfVxuXG4gIHByaXZhdGUgY3JlYXRlRm9ybSgpIHtcbiAgICByZXR1cm4gdGhpcy5mb3JtQnVpbGRlci5ncm91cChcbiAgICAgIHtcbiAgICAgICAgZGF0ZUZyb206IG5ldyBGb3JtQ29udHJvbCh0aGlzLmNvbmZpZy5kYXRlRnJvbSksXG4gICAgICAgIGRhdGVUbzogbmV3IEZvcm1Db250cm9sKHRoaXMuY29uZmlnLmRhdGVUbylcbiAgICAgIH0sXG4gICAgICB7IHZhbGlkYXRvcnM6IGRhdGVSYW5nZVZhbGlkYXRvciB9XG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQcmVwYXJlcyB0aGUgdGFibGUgZGF0YSBieTpcbiAgICogLSBmaWx0ZXJpbmcgb3V0IGluYWN0aXZlIGRhdGEgcG9pbnRzLFxuICAgKiAtIGNoZWNraW5nIGlmIHRoZXJlIGFyZSBtdWx0aXBsZSBkZXZpY2VzIGFzIGEgc291cmNlIG9mIGRhdGEgcG9pbnRzLFxuICAgKiAtIGdldHRpbmcgdGhlIGNvbHVtbiBoZWFkZXJzIGZvciBkZXZpY2VzLFxuICAgKiAtIGdldHRpbmcgdGhlIHNlcmllcyBkYXRhIGZvciBhY3RpdmUgZGF0YSBwb2ludHMgKEFQSSBjYWxsKSxcbiAgICogLSBwcmVwYXJpbmcgZGF0YSBwb2ludHMgd2l0aCB2YWx1ZXMgbGlzdCxcbiAgICogLSBtYXBwaW5nIGRhdGEgcG9pbnRzIHdpdGggdmFsdWVzIHRvIGxpc3QgaXRlbXMsXG4gICAqIC0gZ3JvdXBpbmcgZGF0YSBwb2ludHMgYnkgZGF0ZSBhbmQgZGV2aWNlLFxuICAgKiAtIHNvcnRpbmcgZGF0YSBieSBkYXRlIGRlc2NlbmRpbmcuXG4gICAqXG4gICAqIEBwYXJhbSByb3VuZFNlY29uZHMgLSBXaGV0aGVyIHRvIHJvdW5kIHRoZSBzZWNvbmRzIG9yIG5vdC5cbiAgICogICAgICAgICAgICAgICAgICAgICAgIElmIHRydWUsIHRoZSBzZWNvbmRzIHdpbGwgYmUgcm91bmRlZCB0byAwLlxuICAgKiAgICAgICAgICAgICAgICAgICAgICAgSWYgZmFsc2UsIHRoZSBzZWNvbmRzIHdpbGwgYmUgZGlzcGxheWVkIGFzIHRoZXkgYXJlLlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBwcmVwYXJlVGFibGVEYXRhKHJvdW5kU2Vjb25kcyA9IHRydWUpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmlzTG9hZGluZyQubmV4dCh0cnVlKTtcblxuICAgIGlmICh0aGlzLmlzSW5pdGlhbFJlcXVlc3QpIHtcbiAgICAgIHRoaXMuYWN0aXZlRGF0YXBvaW50cyA9IHRoaXMuZGF0YXBvaW50c1RhYmxlVmlld1NlcnZpY2UuZmlsdGVyT3V0SW5hY3RpdmVEYXRhcG9pbnRzKFxuICAgICAgICB0aGlzLmNvbmZpZy5kYXRhcG9pbnRzXG4gICAgICApO1xuXG4gICAgICB0aGlzLmhhc011bHRpcGxlRGF0YXBvaW50cyA9IHRoaXMuZGF0YXBvaW50c1RhYmxlVmlld1NlcnZpY2UuaGFzTXVsdGlwbGVEYXRhcG9pbnRzKFxuICAgICAgICB0aGlzLmFjdGl2ZURhdGFwb2ludHNcbiAgICAgICk7XG5cbiAgICAgIHRoaXMuZGV2aWNlc0NvbHVtbkhlYWRlcnMgPSB0aGlzLmRhdGFwb2ludHNUYWJsZVZpZXdTZXJ2aWNlLmdldENvbHVtbkhlYWRlcnMoXG4gICAgICAgIHRoaXMuYWN0aXZlRGF0YXBvaW50c1xuICAgICAgKTtcblxuICAgICAgdGhpcy5pc0luaXRpYWxSZXF1ZXN0ID0gZmFsc2U7XG4gICAgfVxuXG4gICAgY29uc3QgYWN0aXZlRGF0YXBvaW50c0lkc1dpdGhTZXJpZXM6IERhdGFwb2ludHNWYWx1ZXNEYXRhTWFwID1cbiAgICAgIHRoaXMuZGF0YXBvaW50c1RhYmxlVmlld1NlcnZpY2UuZ3JvdXBTZXJpZXNCeURldmljZUlkKHRoaXMuYWN0aXZlRGF0YXBvaW50cyk7XG5cbiAgICBjb25zdCBhY3RpdmVEYXRhcG9pbnRzU2VyaWVzRGF0YTogTWFwPHN0cmluZyB8IG51bWJlciwgSVNlcmllcz4gPVxuICAgICAgYXdhaXQgdGhpcy5nZXRBY3RpdmVEYXRhcG9pbnRzU2VyaWVzRGF0YU1hcChcbiAgICAgICAgYWN0aXZlRGF0YXBvaW50c0lkc1dpdGhTZXJpZXMsXG4gICAgICAgIHRoaXMuY29uZmlnLFxuICAgICAgICByb3VuZFNlY29uZHNcbiAgICAgICk7XG5cbiAgICBpZiAodGhpcy5pc0ZhaWxlZFRvRmV0Y2hTZXJpZXNEYXRhKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5zZXJpZXNXaXRob3V0UGVybWlzc2lvblRvUmVhZCA9XG4gICAgICB0aGlzLmRhdGFwb2ludHNUYWJsZVZpZXdTZXJ2aWNlLmdldFNlcmllc1dpdGhvdXRQZXJtaXNzaW9uVG9SZWFkKFxuICAgICAgICBhY3RpdmVEYXRhcG9pbnRzU2VyaWVzRGF0YSxcbiAgICAgICAgYWN0aXZlRGF0YXBvaW50c0lkc1dpdGhTZXJpZXNcbiAgICAgICk7XG5cbiAgICBpZiAoXG4gICAgICAhdGhpcy5pc01pc3NpbmdBbnlQZXJtaXNzaW9uQWxlcnRTaG93biAmJlxuICAgICAgdGhpcy5zZXJpZXNXaXRob3V0UGVybWlzc2lvblRvUmVhZD8ubGVuZ3RoID4gMCAmJlxuICAgICAgYWN0aXZlRGF0YXBvaW50c1Nlcmllc0RhdGE/LnNpemVcbiAgICApIHtcbiAgICAgIHRoaXMuaGFuZGxlTWlzc2luZ0FueVBlcm1pc3Npb25FcnJvck1lc3NhZ2UoKTtcbiAgICAgIHRoaXMuaXNNaXNzaW5nQW55UGVybWlzc2lvbkFsZXJ0U2hvd24gPSB0cnVlO1xuICAgIH1cblxuICAgIGlmICghYWN0aXZlRGF0YXBvaW50c1Nlcmll