UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

240 lines 43.9 kB
import { Component, Input, ViewChild } from '@angular/core'; import { AlarmService } from '@c8y/client'; import { AlarmRealtimeService, AlarmWithChildrenRealtimeService, AlertService, DashboardChildComponent, DismissAlertStrategy, DynamicComponentAlert, gettext, globalAutoRefreshLoading, WidgetGlobalAutoRefreshService } from '@c8y/ngx-components'; import { AlarmsViewService } from '@c8y/ngx-components/alarms'; import { isEmpty, isEqual } from 'lodash-es'; import { BehaviorSubject, Subject } from 'rxjs'; import { skip, takeUntil, tap } from 'rxjs/operators'; import { ASSET_ALARMS_WIDGET_ID, RECENT_ALARMS_WIDGET_ID } from '../alarm-list-widget.model'; import { AlarmWidgetService } from '../alarm-widget.service'; import { AlarmWidgetAlarmsReloadComponent } from '../alarm-widget-alarms-reload.component'; import * as i0 from "@angular/core"; import * as i1 from "../alarm-widget.service"; import * as i2 from "@c8y/ngx-components"; import * as i3 from "@c8y/client"; import * as i4 from "@c8y/ngx-components/alarms"; import * as i5 from "@angular/common"; import * as i6 from "../alarm-widget-alarms-reload.component"; /** * To ensure backward compatibility, legacy widgets will be converted to the new implementation. */ export class AlarmListWidgetComponent { constructor(alarmWidgetService, alarmRealtimeService, alarmService, alarmsViewService, alarmWithChildrenRealtimeService, dashboardChild, alertService, widgetGlobalAutoRefresh) { this.alarmWidgetService = alarmWidgetService; this.alarmRealtimeService = alarmRealtimeService; this.alarmService = alarmService; this.alarmsViewService = alarmsViewService; this.alarmWithChildrenRealtimeService = alarmWithChildrenRealtimeService; this.dashboardChild = dashboardChild; this.alertService = alertService; this.widgetGlobalAutoRefresh = widgetGlobalAutoRefresh; /** * Represents current alarms. */ this.alarms$ = new BehaviorSubject(null); /** * Current isLoading state. Based on it next countdown cycle is being started. */ this.isLoading$ = new BehaviorSubject(true); 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.unsubscribe$ = new Subject(); this.destroy$ = new Subject(); this.isDisabled = false; this.isIntervalRefresh = this.alarmsViewService.isIntervalRefresh(); } async ngOnInit() { this.alerts.setAlertGroupDismissStrategy('warning', DismissAlertStrategy.TEMPORARY_OR_PERMANENT); if (this.alarmWidgetService.isOldAlarmConfigStructure(this.config)) { const mappedConfig = this.alarmWidgetService.mapToNewConfigStructure(this.config, this.isIntervalRefresh); Object.assign(this.config, mappedConfig); } if (this.config.widgetInstanceGlobalAutoRefreshContext && !this.config.isRealtime) { this.handleGlobalRefreshLoading(); } const _config = this.dashboardChild['data']?.config; const isLegacyWidgetWithoutConfiguration = isEmpty(_config) || isEqual(_config, { displaySettings: { globalAutoRefreshContext: true } }); if (isLegacyWidgetWithoutConfiguration) { this.setLegacyRecentOrCriticalAlarmWidgetConfig(); } this.updateWidgetRefreshMode(); this.refreshInterval = !!this.config?.refreshInterval && this.config?.refreshInterval; await this.fetchAlarms(); } ngAfterViewInit() { if (!this.isIntervalRefresh && this.config.refreshOption === 'interval') { this.handleRealtimeAlarms(this.config, this.alarmWidgetAlarmsReloadComp.isRealtimeToggleOn); } } ngOnChanges(changes) { if (changes?.config?.currentValue?.date) { this.config.dateFilter = changes.config.currentValue.date; } } ngOnDestroy() { if (this.config.isRealtime) { this.unsubscribe$.next(); this.unsubscribe$.complete(); } this.destroy$.next(); this.destroy$.complete(); if (this.config.widgetInstanceGlobalAutoRefreshContext && this.isLoading$.value) { this.widgetGlobalAutoRefresh.decrementLoading(); } } async fetchAlarms() { this.updateDateFilter(); this.alarms$.next(await this.getAlarms()); } async onDateFilterChange(event) { this.config.dateFilter = event.selectedDates; this.config.interval = event.interval; await this.fetchAlarms(); } handleRealTimeToggleChange(isRealtimeToggleOn) { if (!isRealtimeToggleOn) { this.unsubscribe$.next(); this.unsubscribe$.complete(); } this.unsubscribe$ = new Subject(); this.handleRealtimeAlarms(this.config, isRealtimeToggleOn); } /** * Updates the widget's date filter based on the currently selected interval. * If the interval is set to last hour, we will configure the date filter to dynamically show alarms only from the last hour. */ updateDateFilter() { if (this.config.interval === 'custom' || this.config.widgetInstanceGlobalTimeContext) { return; } if (!this.config.interval) { this.config.interval = 'none'; } this.config.dateFilter = this.alarmsViewService.getDateTimeContextByInterval(this.config.interval); } /** * Updates the widget's refresh mode based on the current configuration and the interval refresh setting. * If the widget's 'isRealtime' configuration differs from the current global interval refresh setting (application options setting), * the widget's mode is updated to either 'realtime' or 'interval'. In 'realtime' mode, the refresh interval is null, * and in 'interval' mode, it is set to the default interval value. */ updateWidgetRefreshMode() { const isRefreshTypeSwitched = !!this.config.isRealtime !== !this.isIntervalRefresh; if (!isRefreshTypeSwitched) { return; } this.config.isRealtime = !this.isIntervalRefresh; if (this.config.isRealtime) { // Switching widget to realtime mode this.config.refreshInterval = null; this.config.refreshOption = null; this.config.widgetInstanceGlobalAutoRefreshContext = false; this.config.isAutoRefreshEnabled = true; } else { // Switching widget to interval mode this.config.refreshInterval = this.alarmsViewService.DEFAULT_INTERVAL_VALUE; this.config.refreshOption = this.alarmsViewService.DEFAULT_REFRESH_OPTION_VALUE; this.config.widgetInstanceGlobalAutoRefreshContext = false; } } async getAlarms() { try { this.isLoading$.next(true); return await this.alarmService.list(this.alarmWidgetService.mapConfigToQueryFilter(this.config)); } catch (error) { this.alerts.addAlerts(new DynamicComponentAlert({ type: 'warning', text: error?.name === 'TimeoutError' ? this.TIMEOUT_ERROR_TEXT : error?.message ?? this.SERVER_ERROR_TEXT })); if (error?.res?.status === 403) { this.isDisabled = true; return; } this.alertService.addServerFailure(error); } finally { this.isLoading$.next(false); } } /** * Handles the realtime updates of alarms based on the widget configuration. * If a specific device is provided in the widget configuration, all alarms * originating from that device and its children are filtered based on status, severity, and type. * Otherwise, it processes all incoming alarms and filters them according to status, severity, and type, * as specified in the widget configuration. If new alarms meet the specified criteria, it sets a flag * to indicate the presence of new alarms. * * @param config - The configuration settings of the alarm list widget. * @param isRealtimeToggleOn - A flag indicating whether the realtime toggle is on. If true, the alarm list will be refreshed. * If false, the presence of new alarms will be displayed as "New Alarms" entry on widget refresh button, but the list won't be refreshed automatically. */ handleRealtimeAlarms(config, isRealtimeToggleOn) { const callback = (data) => { if (this.alarmWidgetService.filterAlarmsByStatusSeverityAndType(data, this.alarms$.value.data, config)) { if (isRealtimeToggleOn) { this.fetchAlarms(); this.alarmWidgetAlarmsReloadComp.isNewAlarmMessageCleared = true; } else { this.alarmWidgetAlarmsReloadComp.isNewAlarmMessageCleared = false; } } }; if (this.config.device) { this.handleRealtime(this.alarmWithChildrenRealtimeService.onAll$.bind(this.alarmWithChildrenRealtimeService), callback); } else { this.handleRealtime(this.alarmRealtimeService.onAll$.bind(this.alarmRealtimeService), callback); } } /** * Sets up a subscription to a realtime data stream. This function takes a subscriber function, * which is used to subscribe to the data stream, and a callback function, which is called with each * data update received from the stream. * * @param subscriberFn - A function that returns an observable stream of alarm data. * @param callback - A function to be executed with each alarm data update. */ handleRealtime(subscriberFn, callback) { subscriberFn(this.config.device?.id) .pipe(tap(({ data }) => { callback(data); }), takeUntil(this.unsubscribe$)) .subscribe(); } setLegacyRecentOrCriticalAlarmWidgetConfig() { const widgetId = this.dashboardChild['data']?.componentId ? this.dashboardChild['data']?.componentId //property is used in cockpit dashboards : this.dashboardChild['data']?.name; //property is used in device management dashboards if (widgetId === RECENT_ALARMS_WIDGET_ID) { const predefinedConfig = this.alarmWidgetService.getPredefinedConfiguration(this.isIntervalRefresh, RECENT_ALARMS_WIDGET_ID); Object.assign(this.config, predefinedConfig); return; } if (widgetId === ASSET_ALARMS_WIDGET_ID) { const predefinedConfig = this.alarmWidgetService.getPredefinedConfiguration(this.isIntervalRefresh); Object.assign(this.config, predefinedConfig); } } handleGlobalRefreshLoading() { this.isLoading$ .pipe(skip(1), globalAutoRefreshLoading(this.widgetGlobalAutoRefresh), takeUntil(this.destroy$)) .subscribe(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AlarmListWidgetComponent, deps: [{ token: i1.AlarmWidgetService }, { token: i2.AlarmRealtimeService }, { token: i3.AlarmService }, { token: i4.AlarmsViewService }, { token: i2.AlarmWithChildrenRealtimeService }, { token: i2.DashboardChildComponent }, { token: i2.AlertService }, { token: i2.WidgetGlobalAutoRefreshService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: AlarmListWidgetComponent, selector: "c8y-alarm-list-widget", inputs: { config: "config" }, providers: [AlarmRealtimeService, AlarmWithChildrenRealtimeService], viewQueries: [{ propertyName: "alarmWidgetAlarmsReloadComp", first: true, predicate: AlarmWidgetAlarmsReloadComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<c8y-alarms-list\n #list\n [alarms]=\"alarms$ | async\"\n [navigationOptions]=\"{\n alwaysNavigateToAllAlarms: !config.device,\n allowNavigationToAlarmsView: true,\n includeClearedQueryParams: true,\n queryParamsHandling: ''\n }\"\n [isInitialLoading]=\"isLoading$ | async\"\n [hasPermissions]=\"!isDisabled\"\n>\n <c8y-alarms-date-filter\n *ngIf=\"config.displayDateSelection\"\n [updateQueryParams]=\"false\"\n [date]=\"this.config.dateFilter\"\n [DEFAULT_INTERVAL]=\"config.interval || 'none'\"\n (dateFilterChange)=\"onDateFilterChange($event)\"\n ></c8y-alarms-date-filter>\n <c8y-alarm-widget-alarms-reload\n class=\"d-contents\"\n [isIntervalRefresh]=\"isIntervalRefresh\"\n [refreshInterval]=\"refreshInterval\"\n [config]=\"config\"\n [isLoading]=\"isLoading$\"\n [isScrolling]=\"list.isScrolling\"\n [isRefreshDisabled]=\"isDisabled\"\n (onCountdownEnded)=\"fetchAlarms()\"\n (onRealTimeToggleChanged)=\"handleRealTimeToggleChange($event)\"\n ></c8y-alarm-widget-alarms-reload>\n</c8y-alarms-list>\n", dependencies: [{ kind: "component", type: i4.AlarmsListComponent, selector: "c8y-alarms-list", inputs: ["alarms", "hasPermissions", "typeFilters", "loadMoreMode", "navigationOptions", "isInitialLoading", "splitView"], outputs: ["onSelectedAlarm", "onScrollingStateChange"] }, { kind: "component", type: i4.AlarmsDateFilterComponent, selector: "c8y-alarms-date-filter", inputs: ["DEFAULT_INTERVAL", "updateQueryParams", "date"], outputs: ["dateFilterChange"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i6.AlarmWidgetAlarmsReloadComponent, selector: "c8y-alarm-widget-alarms-reload", inputs: ["isIntervalRefresh", "refreshInterval", "config", "isLoading", "isScrolling", "isRefreshDisabled"], outputs: ["onCountdownEnded", "onRealTimeToggleChanged"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AlarmListWidgetComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-alarm-list-widget', providers: [AlarmRealtimeService, AlarmWithChildrenRealtimeService], template: "<c8y-alarms-list\n #list\n [alarms]=\"alarms$ | async\"\n [navigationOptions]=\"{\n alwaysNavigateToAllAlarms: !config.device,\n allowNavigationToAlarmsView: true,\n includeClearedQueryParams: true,\n queryParamsHandling: ''\n }\"\n [isInitialLoading]=\"isLoading$ | async\"\n [hasPermissions]=\"!isDisabled\"\n>\n <c8y-alarms-date-filter\n *ngIf=\"config.displayDateSelection\"\n [updateQueryParams]=\"false\"\n [date]=\"this.config.dateFilter\"\n [DEFAULT_INTERVAL]=\"config.interval || 'none'\"\n (dateFilterChange)=\"onDateFilterChange($event)\"\n ></c8y-alarms-date-filter>\n <c8y-alarm-widget-alarms-reload\n class=\"d-contents\"\n [isIntervalRefresh]=\"isIntervalRefresh\"\n [refreshInterval]=\"refreshInterval\"\n [config]=\"config\"\n [isLoading]=\"isLoading$\"\n [isScrolling]=\"list.isScrolling\"\n [isRefreshDisabled]=\"isDisabled\"\n (onCountdownEnded)=\"fetchAlarms()\"\n (onRealTimeToggleChanged)=\"handleRealTimeToggleChange($event)\"\n ></c8y-alarm-widget-alarms-reload>\n</c8y-alarms-list>\n" }] }], ctorParameters: () => [{ type: i1.AlarmWidgetService }, { type: i2.AlarmRealtimeService }, { type: i3.AlarmService }, { type: i4.AlarmsViewService }, { type: i2.AlarmWithChildrenRealtimeService }, { type: i2.DashboardChildComponent }, { type: i2.AlertService }, { type: i2.WidgetGlobalAutoRefreshService }], propDecorators: { config: [{ type: Input }], alarmWidgetAlarmsReloadComp: [{ type: ViewChild, args: [AlarmWidgetAlarmsReloadComponent] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWxhcm0tbGlzdC13aWRnZXQuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vd2lkZ2V0cy9pbXBsZW1lbnRhdGlvbnMvYWxhcm1zL2FsYXJtLWxpc3Qtd2lkZ2V0LXZpZXcvYWxhcm0tbGlzdC13aWRnZXQuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vd2lkZ2V0cy9pbXBsZW1lbnRhdGlvbnMvYWxhcm1zL2FsYXJtLWxpc3Qtd2lkZ2V0LXZpZXcvYWxhcm0tbGlzdC13aWRnZXQuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUVMLFNBQVMsRUFDVCxLQUFLLEVBSUwsU0FBUyxFQUNWLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxZQUFZLEVBQWdDLE1BQU0sYUFBYSxDQUFDO0FBQ3pFLE9BQU8sRUFDTCxvQkFBb0IsRUFDcEIsZ0NBQWdDLEVBQ2hDLFlBQVksRUFDWix1QkFBdUIsRUFDdkIsb0JBQW9CLEVBRXBCLHFCQUFxQixFQUVyQixPQUFPLEVBQ1Asd0JBQXdCLEVBQ3hCLDhCQUE4QixFQUMvQixNQUFNLHFCQUFxQixDQUFDO0FBQzdCLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQy9ELE9BQU8sRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQzdDLE9BQU8sRUFBRSxlQUFlLEVBQWMsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQzVELE9BQU8sRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ3RELE9BQU8sRUFDTCxzQkFBc0IsRUFFdEIsdUJBQXVCLEVBQ3hCLE1BQU0sNEJBQTRCLENBQUM7QUFDcEMsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDN0QsT0FBTyxFQUFFLGdDQUFnQyxF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QztRQUVELElBQUksUUFBUSxLQUFLLHNCQUFzQixFQUFFLENBQUM7WUFDeEMsTUFBTSxnQkFBZ0IsR0FBRyxJQUFJLENBQUMsa0JBQWtCLENBQUMsMEJBQTBCLENBQ3pFLElBQUksQ0FBQyxpQkFBaUIsQ0FDdkIsQ0FBQztZQUNGLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxnQkFBZ0IsQ0FBQyxDQUFDO1FBQy9DLENBQUM7SUFDSCxDQUFDO0lBRU8sMEJBQTBCO1FBQ2hDLElBQUksQ0FBQyxVQUFVO2FBQ1osSUFBSSxDQUNILElBQUksQ0FBQyxDQUFDLENBQUMsRUFDUCx3QkFBd0IsQ0FBQyxJQUFJLENBQUMsdUJBQXVCLENBQUMsRUFDdEQsU0FBUyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FDekI7YUFDQSxTQUFTLEVBQUUsQ0FBQztJQUNqQixDQUFDOytHQXpTVSx3QkFBd0I7bUdBQXhCLHdCQUF3Qiw4RUFGeEIsQ0FBQyxvQkFBb0IsRUFBRSxnQ0FBZ0MsQ0FBQyx1RkEwQnhELGdDQUFnQyxxRUNwRTdDLG1qQ0ErQkE7OzRGRGFhLHdCQUF3QjtrQkFMcEMsU0FBUzsrQkFDRSx1QkFBdUIsYUFFdEIsQ0FBQyxvQkFBb0IsRUFBRSxnQ0FBZ0MsQ0FBQztrVkFRMUQsTUFBTTtzQkFBZCxLQUFLO2dCQW1CTiwyQkFBMkI7c0JBRDFCLFNBQVM7dUJBQUMsZ0NBQWdDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQWZ0ZXJWaWV3SW5pdCxcbiAgQ29tcG9uZW50LFxuICBJbnB1dCxcbiAgT25EZXN0cm95LFxuICBPbkluaXQsXG4gIFNpbXBsZUNoYW5nZXMsXG4gIFZpZXdDaGlsZFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEFsYXJtU2VydmljZSwgSUFsYXJtLCBJUmVzdWx0LCBJUmVzdWx0TGlzdCB9IGZyb20gJ0BjOHkvY2xpZW50JztcbmltcG9ydCB7XG4gIEFsYXJtUmVhbHRpbWVTZXJ2aWNlLFxuICBBbGFybVdpdGhDaGlsZHJlblJlYWx0aW1lU2VydmljZSxcbiAgQWxlcnRTZXJ2aWNlLFxuICBEYXNoYm9hcmRDaGlsZENvbXBvbmVudCxcbiAgRGlzbWlzc0FsZXJ0U3RyYXRlZ3ksXG4gIER5bmFtaWNDb21wb25lbnQsXG4gIER5bmFtaWNDb21wb25lbnRBbGVydCxcbiAgRHluYW1pY0NvbXBvbmVudEFsZXJ0QWdncmVnYXRvcixcbiAgZ2V0dGV4dCxcbiAgZ2xvYmFsQXV0b1JlZnJlc2hMb2FkaW5nLFxuICBXaWRnZXRHbG9iYWxBdXRvUmVmcmVzaFNlcnZpY2Vcbn0gZnJvbSAnQGM4eS9uZ3gtY29tcG9uZW50cyc7XG5pbXBvcnQgeyBBbGFybXNWaWV3U2VydmljZSB9IGZyb20gJ0BjOHkvbmd4LWNvbXBvbmVudHMvYWxhcm1zJztcbmltcG9ydCB7IGlzRW1wdHksIGlzRXF1YWwgfSBmcm9tICdsb2Rhc2gtZXMnO1xuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0LCBPYnNlcnZhYmxlLCBTdWJqZWN0IH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBza2lwLCB0YWtlVW50aWwsIHRhcCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7XG4gIEFTU0VUX0FMQVJNU19XSURHRVRfSUQsXG4gIEFsYXJtTGlzdFdpZGdldENvbmZpZyxcbiAgUkVDRU5UX0FMQVJNU19XSURHRVRfSURcbn0gZnJvbSAnLi4vYWxhcm0tbGlzdC13aWRnZXQubW9kZWwnO1xuaW1wb3J0IHsgQWxhcm1XaWRnZXRTZXJ2aWNlIH0gZnJvbSAnLi4vYWxhcm0td2lkZ2V0LnNlcnZpY2UnO1xuaW1wb3J0IHsgQWxhcm1XaWRnZXRBbGFybXNSZWxvYWRDb21wb25lbnQgfSBmcm9tICcuLi9hbGFybS13aWRnZXQtYWxhcm1zLXJlbG9hZC5jb21wb25lbnQnO1xuaW1wb3J0IHsgSW50ZXJ2YWwgfSBmcm9tICdAYzh5L25neC1jb21wb25lbnRzL2ludGVydmFsLXBpY2tlcic7XG5cbi8qKlxuICogVG8gZW5zdXJlIGJhY2t3YXJkIGNvbXBhdGliaWxpdHksIGxlZ2FjeSB3aWRnZXRzIHdpbGwgYmUgY29udmVydGVkIHRvIHRoZSBuZXcgaW1wbGVtZW50YXRpb24uXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2M4eS1hbGFybS1saXN0LXdpZGdldCcsXG4gIHRlbXBsYXRlVXJsOiAnLi9hbGFybS1saXN0LXdpZGdldC5jb21wb25lbnQuaHRtbCcsXG4gIHByb3ZpZGVyczogW0FsYXJtUmVhbHRpbWVTZXJ2aWNlLCBBbGFybVdpdGhDaGlsZHJlblJlYWx0aW1lU2VydmljZV1cbn0pXG5leHBvcnQgY2xhc3MgQWxhcm1MaXN0V2lkZ2V0Q29tcG9uZW50XG4gIGltcGxlbWVudHMgT25Jbml0LCBBZnRlclZpZXdJbml0LCBPbkRlc3Ryb3ksIER5bmFtaWNDb21wb25lbnRcbntcbiAgLyoqXG4gICAqICBBbGFybSB3aWRnZXQgY29uZmlnLlxuICAgKi9cbiAgQElucHV0KCkgY29uZmlnOiBBbGFybUxpc3RXaWRnZXRDb25maWc7XG4gIC8qKlxuICAgKiBSZXByZXNlbnRzIGN1cnJlbnQgYWxhcm1zLlxuICAgKi9cbiAgYWxhcm1zJDogQmVoYXZpb3JTdWJqZWN0PElSZXN1bHRMaXN0PElBbGFybT4gfCBudWxsPiA9IG5ldyBCZWhhdmlvclN1YmplY3QobnVsbCk7XG4gIC8qKlxuICAgKiBDdXJyZW50IGlzTG9hZGluZyBzdGF0ZS4gQmFzZWQgb24gaXQgbmV4dCBjb3VudGRvd24gY3ljbGUgaXMgYmVpbmcgc3RhcnRlZC5cbiAgICovXG4gIGlzTG9hZGluZyQ6IEJlaGF2aW9yU3ViamVjdDxib29sZWFuPiA9IG5ldyBCZWhhdmlvclN1YmplY3QodHJ1ZSk7XG4gIC8qKlxuICAgKiBTdGF0ZSBvZiB0aGlzIGJvb2xlYW4gZGVwZW5kcyBvbiBhbiBBcHBsaWNhdGlvbiBPcHRpb25zIFwiYWxhcm1zUmVmcmVzaFR5cGVcIiBzZXR0aW5nLlxuICAgKi9cbiAgaXNJbnRlcnZhbFJlZnJlc2g6IGJvb2xlYW47XG4gIC8qKlxuICAgKiBDdXJyZW50IHJlZnJlc2ggaW50ZXJ2YWwgc2V0IGluIGEgd2lkZ2V0IGNvbmZpZy5cbiAgICovXG4gIHJlZnJlc2hJbnRlcnZhbDogbnVtYmVyO1xuXG4gIEBWaWV3Q2hpbGQoQWxhcm1XaWRnZXRBbGFybXNSZWxvYWRDb21wb25lbnQpXG4gIGFsYXJtV2lkZ2V0QWxhcm1zUmVsb2FkQ29tcDogQWxhcm1XaWRnZXRBbGFybXNSZWxvYWRDb21wb25lbnQ7XG4gIGFsZXJ0czogRHluYW1pY0NvbXBvbmVudEFsZXJ0QWdncmVnYXRvcjtcblxuICBwcml2YXRlIFRJTUVPVVRfRVJST1JfVEVYVCA9IGdldHRleHQoXG4gICAgJ1RoZSByZXF1ZXN0IGlzIHRha2luZyBsb25nZXIgdGhhbiB1c3VhbC4gV2UgYXBvbG9naXplIGZvciB0aGUgaW5jb252ZW5pZW5jZS4nXG4gICk7XG4gIHByaXZhdGUgU0VSVkVSX0VSUk9SX1RFWFQgPSBnZXR0ZXh0KCdTZXJ2ZXIgZXJyb3Igb2NjdXJyZWQuJyk7XG4gIHByaXZhdGUgdW5zdWJzY3JpYmUkID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcbiAgcHJpdmF0ZSBkZXN0cm95JDogU3ViamVjdDx2b2lkPiA9IG5ldyBTdWJqZWN0KCk7XG4gIGlzRGlzYWJsZWQgPSBmYWxzZTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIGFsYXJtV2lkZ2V0U2VydmljZTogQWxhcm1XaWRnZXRTZXJ2aWNlLFxuICAgIHByaXZhdGUgYWxhcm1SZWFsdGltZVNlcnZpY2U6IEFsYXJtUmVhbHRpbWVTZXJ2aWNlLFxuICAgIHByaXZhdGUgYWxhcm1TZXJ2aWNlOiBBbGFybVNlcnZpY2UsXG4gICAgcHJpdmF0ZSBhbGFybXNWaWV3U2VydmljZTogQWxhcm1zVmlld1NlcnZpY2UsXG4gICAgcHJpdmF0ZSBhbGFybVdpdGhDaGlsZHJlblJlYWx0aW1lU2VydmljZTogQWxhcm1XaXRoQ2hpbGRyZW5SZWFsdGltZVNlcnZpY2UsXG4gICAgcHJpdmF0ZSBkYXNoYm9hcmRDaGlsZDogRGFzaGJvYXJkQ2hpbGRDb21wb25lbnQsXG4gICAgcHJpdmF0ZSBhbGVydFNlcnZpY2U6IEFsZXJ0U2VydmljZSxcbiAgICBwcml2YXRlIHdpZGdldEdsb2JhbEF1dG9SZWZyZXNoOiBXaWRnZXRHbG9iYWxBdXRvUmVmcmVzaFNlcnZpY2VcbiAgKSB7XG4gICAgdGhpcy5pc0ludGVydmFsUmVmcmVzaCA9IHRoaXMuYWxhcm1zVmlld1NlcnZpY2UuaXNJbnRlcnZhbFJlZnJlc2goKTtcbiAgfVxuXG4gIGFzeW5jIG5nT25Jbml0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuYWxlcnRzLnNldEFsZXJ0R3JvdXBEaXNtaXNzU3RyYXRlZ3koXG4gICAgICAnd2FybmluZycsXG4gICAgICBEaXNtaXNzQWxlcnRTdHJhdGVneS5URU1QT1JBUllfT1JfUEVSTUFORU5UXG4gICAgKTtcblxuICAgIGlmICh0aGlzLmFsYXJtV2lkZ2V0U2VydmljZS5pc09sZEFsYXJtQ29uZmlnU3RydWN0dXJlKHRoaXMuY29uZmlnKSkge1xuICAgICAgY29uc3QgbWFwcGVkQ29uZmlnID0gdGhpcy5hbGFybVdpZGdldFNlcnZpY2UubWFwVG9OZXdDb25maWdTdHJ1Y3R1cmUoXG4gICAgICAgIHRoaXMuY29uZmlnLFxuICAgICAgICB0aGlzLmlzSW50ZXJ2YWxSZWZyZXNoXG4gICAgICApO1xuICAgICAgT2JqZWN0LmFzc2lnbih0aGlzLmNvbmZpZywgbWFwcGVkQ29uZmlnKTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5jb25maWcud2lkZ2V0SW5zdGFuY2VHbG9iYWxBdXRvUmVmcmVzaENvbnRleHQgJiYgIXRoaXMuY29uZmlnLmlzUmVhbHRpbWUpIHtcbiAgICAgIHRoaXMuaGFuZGxlR2xvYmFsUmVmcmVzaExvYWRpbmcoKTtcbiAgICB9XG4gICAgY29uc3QgX2NvbmZpZyA9IHRoaXMuZGFzaGJvYXJkQ2hpbGRbJ2RhdGEnXT8uY29uZmlnO1xuICAgIGNvbnN0IGlzTGVnYWN5V2lkZ2V0V2l0aG91dENvbmZpZ3VyYXRpb24gPVxuICAgICAgaXNFbXB0eShfY29uZmlnKSB8fCBpc0VxdWFsKF9jb25maWcsIHsgZGlzcGxheVNldHRpbmdzOiB7IGdsb2JhbEF1dG9SZWZyZXNoQ29udGV4dDogdHJ1ZSB9IH0pO1xuXG4gICAgaWYgKGlzTGVnYWN5V2lkZ2V0V2l0aG91dENvbmZpZ3VyYXRpb24pIHtcbiAgICAgIHRoaXMuc2V0TGVnYWN5UmVjZW50T3JDcml0aWNhbEFsYXJtV2lkZ2V0Q29uZmlnKCk7XG4gICAgfVxuXG4gICAgdGhpcy51cGRhdGVXaWRnZXRSZWZyZXNoTW9kZSgpO1xuICAgIHRoaXMucmVmcmVzaEludGVydmFsID0gISF0aGlzLmNvbmZpZz8ucmVmcmVzaEludGVydmFsICYmIHRoaXMuY29uZmlnPy5yZWZyZXNoSW50ZXJ2YWw7XG4gICAgYXdhaXQgdGhpcy5mZXRjaEFsYXJtcygpO1xuICB9XG5cbiAgbmdBZnRlclZpZXdJbml0KCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5pc0ludGVydmFsUmVmcmVzaCAmJiB0aGlzLmNvbmZpZy5yZWZyZXNoT3B0aW9uID09PSAnaW50ZXJ2YWwnKSB7XG4gICAgICB0aGlzLmhhbmRsZVJlYWx0aW1lQWxhcm1zKHRoaXMuY29uZmlnLCB0aGlzLmFsYXJtV2lkZ2V0QWxhcm1zUmVsb2FkQ29tcC5pc1JlYWx0aW1lVG9nZ2xlT24pO1xuICAgIH1cbiAgfVxuXG4gIG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpOiB2b2lkIHtcbiAgICBpZiAoY2hhbmdlcz8uY29uZmlnPy5jdXJyZW50VmFsdWU/LmRhdGUpIHtcbiAgICAgIHRoaXMuY29uZmlnLmRhdGVGaWx0ZXIgPSBjaGFuZ2VzLmNvbmZpZy5jdXJyZW50VmFsdWUuZGF0ZTtcbiAgICB9XG4gIH1cblxuICBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5jb25maWcuaXNSZWFsdGltZSkge1xuICAgICAgdGhpcy51bnN1YnNjcmliZSQubmV4dCgpO1xuICAgICAgdGhpcy51bnN1YnNjcmliZSQuY29tcGxldGUoKTtcbiAgICB9XG5cbiAgICB0aGlzLmRlc3Ryb3kkLm5leHQoKTtcbiAgICB0aGlzLmRlc3Ryb3kkLmNvbXBsZXRlKCk7XG5cbiAgICBpZiAodGhpcy5jb25maWcud2lkZ2V0SW5zdGFuY2VHbG9iYWxBdXRvUmVmcmVzaENvbnRleHQgJiYgdGhpcy5pc0xvYWRpbmckLnZhbHVlKSB7XG4gICAgICB0aGlzLndpZGdldEdsb2JhbEF1dG9SZWZyZXNoLmRlY3JlbWVudExvYWRpbmcoKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBmZXRjaEFsYXJtcygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLnVwZGF0ZURhdGVGaWx0ZXIoKTtcbiAgICB0aGlzLmFsYXJtcyQubmV4dChhd2FpdCB0aGlzLmdldEFsYXJtcygpKTtcbiAgfVxuXG4gIGFzeW5jIG9uRGF0ZUZpbHRlckNoYW5nZShldmVudDoge1xuICAgIHNlbGVjdGVkRGF0ZXM6IFtEYXRlLCBEYXRlXTtcbiAgICBpbnRlcnZhbDogSW50ZXJ2YWxbJ2lkJ107XG4gIH0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmNvbmZpZy5kYXRlRmlsdGVyID0gZXZlbnQuc2VsZWN0ZWREYXRlcztcbiAgICB0aGlzLmNvbmZpZy5pbnRlcnZhbCA9IGV2ZW50LmludGVydmFsO1xuICAgIGF3YWl0IHRoaXMuZmV0Y2hBbGFybXMoKTtcbiAgfVxuXG4gIGhhbmRsZVJlYWxUaW1lVG9nZ2xlQ2hhbmdlKGlzUmVhbHRpbWVUb2dnbGVPbjogYm9vbGVhbik6IHZvaWQge1xuICAgIGlmICghaXNSZWFsdGltZVRvZ2dsZU9uKSB7XG4gICAgICB0aGlzLnVuc3Vic2NyaWJlJC5uZXh0KCk7XG4gICAgICB0aGlzLnVuc3Vic2NyaWJlJC5jb21wbGV0ZSgpO1xuICAgIH1cblxuICAgIHRoaXMudW5zdWJzY3JpYmUkID0gbmV3IFN1YmplY3QoKTtcbiAgICB0aGlzLmhhbmRsZVJlYWx0aW1lQWxhcm1zKHRoaXMuY29uZmlnLCBpc1JlYWx0aW1lVG9nZ2xlT24pO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZXMgdGhlIHdpZGdldCdzIGRhdGUgZmlsdGVyIGJhc2VkIG9uIHRoZSBjdXJyZW50bHkgc2VsZWN0ZWQgaW50ZXJ2YWwuXG4gICAqIElmIHRoZSBpbnRlcnZhbCBpcyBzZXQgdG8gbGFzdCBob3VyLCB3ZSB3aWxsIGNvbmZpZ3VyZSB0aGUgZGF0ZSBmaWx0ZXIgdG8gZHluYW1pY2FsbHkgc2hvdyBhbGFybXMgb25seSBmcm9tIHRoZSBsYXN0IGhvdXIuXG4gICAqL1xuICBwcml2YXRlIHVwZGF0ZURhdGVGaWx0ZXIoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuY29uZmlnLmludGVydmFsID09PSAnY3VzdG9tJyB8fCB0aGlzLmNvbmZpZy53aWRnZXRJbnN0YW5jZUdsb2JhbFRpbWVDb250ZXh0KSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKCF0aGlzLmNvbmZpZy5pbnRlcnZhbCkge1xuICAgICAgdGhpcy5jb25maWcuaW50ZXJ2YWwgPSAnbm9uZSc7XG4gICAgfVxuXG4gICAgdGhpcy5jb25maWcuZGF0ZUZpbHRlciA9IHRoaXMuYWxhcm1zVmlld1NlcnZpY2UuZ2V0RGF0ZVRpbWVDb250ZXh0QnlJbnRlcnZhbChcbiAgICAgIHRoaXMuY29uZmlnLmludGVydmFsXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBVcGRhdGVzIHRoZSB3aWRnZXQncyByZWZyZXNoIG1vZGUgYmFzZWQgb24gdGhlIGN1cnJlbnQgY29uZmlndXJhdGlvbiBhbmQgdGhlIGludGVydmFsIHJlZnJlc2ggc2V0dGluZy5cbiAgICogSWYgdGhlIHdpZGdldCdzICdpc1JlYWx0aW1lJyBjb25maWd1cmF0aW9uIGRpZmZlcnMgZnJvbSB0aGUgY3VycmVudCBnbG9iYWwgaW50ZXJ2YWwgcmVmcmVzaCBzZXR0aW5nIChhcHBsaWNhdGlvbiBvcHRpb25zIHNldHRpbmcpLFxuICAgKiB0aGUgd2lkZ2V0J3MgbW9kZSBpcyB1cGRhdGVkIHRvIGVpdGhlciAncmVhbHRpbWUnIG9yICdpbnRlcnZhbCcuIEluICdyZWFsdGltZScgbW9kZSwgdGhlIHJlZnJlc2ggaW50ZXJ2YWwgaXMgbnVsbCxcbiAgICogYW5kIGluICdpbnRlcnZhbCcgbW9kZSwgaXQgaXMgc2V0IHRvIHRoZSBkZWZhdWx0IGludGVydmFsIHZhbHVlLlxuICAgKi9cbiAgcHJpdmF0ZSB1cGRhdGVXaWRnZXRSZWZyZXNoTW9kZSgpOiB2b2lkIHtcbiAgICBjb25zdCBpc1JlZnJlc2hUeXBlU3dpdGNoZWQgPSAhIXRoaXMuY29uZmlnLmlzUmVhbHRpbWUgIT09ICF0aGlzLmlzSW50ZXJ2YWxSZWZyZXNoO1xuICAgIGlmICghaXNSZWZyZXNoVHlwZVN3aXRjaGVkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5jb25maWcuaXNSZWFsdGltZSA9ICF0aGlzLmlzSW50ZXJ2YWxSZWZyZXNoO1xuXG4gICAgaWYgKHRoaXMuY29uZmlnLmlzUmVhbHRpbWUpIHtcbiAgICAgIC8vIFN3aXRjaGluZyB3aWRnZXQgdG8gcmVhbHRpbWUgbW9kZVxuICAgICAgdGhpcy5jb25maWcucmVmcmVzaEludGVydmFsID0gbnVsbDtcbiAgICAgIHRoaXMuY29uZmlnLnJlZnJlc2hPcHRpb24gPSBudWxsO1xuICAgICAgdGhpcy5jb25maWcud2lkZ2V0SW5zdGFuY2VHbG9iYWxBdXRvUmVmcmVzaENvbnRleHQgPSBmYWxzZTtcbiAgICAgIHRoaXMuY29uZmlnLmlzQXV0b1JlZnJlc2hFbmFibGVkID0gdHJ1ZTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gU3dpdGNoaW5nIHdpZGdldCB0byBpbnRlcnZhbCBtb2RlXG4gICAgICB0aGlzLmNvbmZpZy5yZWZyZXNoSW50ZXJ2YWwgPSB0aGlzLmFsYXJtc1ZpZXdTZXJ2aWNlLkRFRkFVTFRfSU5URVJWQUxfVkFMVUU7XG4gICAgICB0aGlzLmNvbmZpZy5yZWZyZXNoT3B0aW9uID0gdGhpcy5hbGFybXNWaWV3U2VydmljZS5ERUZBVUxUX1JFRlJFU0hfT1BUSU9OX1ZBTFVFO1xuICAgICAgdGhpcy5jb25maWcud2lkZ2V0SW5zdGFuY2VHbG9iYWxBdXRvUmVmcmVzaENvbnRleHQgPSBmYWxzZTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGdldEFsYXJtcygpOiBQcm9taXNlPElSZXN1bHRMaXN0PElBbGFybT4+IHtcbiAgICB0cnkge1xuICAgICAgdGhpcy5pc0xvYWRpbmckLm5leHQodHJ1ZSk7XG4gICAgICByZXR1cm4gYXdhaXQgdGhpcy5hbGFybVNlcnZpY2UubGlzdChcbiAgICAgICAgdGhpcy5hbGFybVdpZGdldFNlcnZpY2UubWFwQ29uZmlnVG9RdWVyeUZpbHRlcih0aGlzLmNvbmZpZylcbiAgICAgICk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIHRoaXMuYWxlcnRzLmFkZEFsZXJ0cyhcbiAgICAgICAgbmV3IER5bmFtaWNDb21wb25lbnRBbGVydCh7XG4gICAgICAgICAgdHlwZTogJ3dhcm5pbmcnLFxuICAgICAgICAgIHRleHQ6XG4gICAgICAgICAgICBlcnJvcj8ubmFtZSA9PT0gJ1RpbWVvdXRFcnJvcidcbiAgICAgICAgICAgICAgPyB0aGlzLlRJTUVPVVRfRVJST1JfVEVYVFxuICAgICAgICAgICAgICA6IGVycm9yPy5tZXNzYWdlID8/IHRoaXMuU0VSVkVSX0VSUk9SX1RFWFRcbiAgICAgICAgfSlcbiAgICAgICk7XG4gICAgICBpZiAoZXJyb3I/LnJlcz8uc3RhdHVzID09PSA0MDMpIHtcbiAgICAgICAgdGhpcy5pc0Rpc2FibGVkID0gdHJ1ZTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdGhpcy5hbGVydFNlcnZpY2UuYWRkU2VydmVyRmFpbHVyZShlcnJvcik7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIHRoaXMuaXNMb2FkaW5nJC5uZXh0KGZhbHNlKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogSGFuZGxlcyB0aGUgcmVhbHRpbWUgdXBkYXRlcyBvZiBhbGFybXMgYmFzZWQgb24gdGhlIHdpZGdldCBjb25maWd1cmF0aW9uLlxuICAgKiBJZiBhIHNwZWNpZmljIGRldmljZSBpcyBwcm92aWRlZCBpbiB0aGUgd2lkZ2V0IGNvbmZpZ3VyYXRpb24sIGFsbCBhbGFybXNcbiAgICogb3JpZ2luYXRpbmcgZnJvbSB0aGF0IGRldmljZSBhbmQgaXRzIGNoaWxkcmVuIGFyZSBmaWx0ZXJlZCBiYXNlZCBvbiBzdGF0dXMsIHNldmVyaXR5LCBhbmQgdHlwZS5cbiAgICogT3RoZXJ3aXNlLCBpdCBwcm9jZXNzZXMgYWxsIGluY29taW5nIGFsYXJtcyBhbmQgZmlsdGVycyB0aGVtIGFjY29yZGluZyB0byBzdGF0dXMsIHNldmVyaXR5LCBhbmQgdHlwZSxcbiAgICogYXMgc3BlY2lmaWVkIGluIHRoZSB3aWRnZXQgY29uZmlndXJhdGlvbi4gSWYgbmV3IGFsYXJtcyBtZWV0IHRoZSBzcGVjaWZpZWQgY3JpdGVyaWEsIGl0IHNldHMgYSBmbGFnXG4gICAqIHRvIGluZGljYXRlIHRoZSBwcmVzZW5jZSBvZiBuZXcgYWxhcm1zLlxuICAgKlxuICAgKiBAcGFyYW0gY29uZmlnIC0gVGhlIGNvbmZpZ3VyYXRpb24gc2V0dGluZ3Mgb2YgdGhlIGFsYXJtIGxpc3Qgd2lkZ2V0LlxuICAgKiBAcGFyYW0gaXNSZWFsdGltZVRvZ2dsZU9uIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciB0aGUgcmVhbHRpbWUgdG9nZ2xlIGlzIG9uLiBJZiB0cnVlLCB0aGUgYWxhcm0gbGlzdCB3aWxsIGJlIHJlZnJlc2hlZC5cbiAgICogSWYgZmFsc2UsIHRoZSBwcmVzZW5jZSBvZiBuZXcgYWxhcm1zIHdpbGwgYmUgZGlzcGxheWVkIGFzIFwiTmV3IEFsYXJtc1wiIGVudHJ5IG9uIHdpZGdldCByZWZyZXNoIGJ1dHRvbiwgYnV0IHRoZSBsaXN0IHdvbid0IGJlIHJlZnJlc2hlZCBhdXRvbWF0aWNhbGx5LlxuICAgKi9cbiAgcHJpdmF0ZSBoYW5kbGVSZWFsdGltZUFsYXJtcyhjb25maWc6IEFsYXJtTGlzdFdpZGdldENvbmZpZywgaXNSZWFsdGltZVRvZ2dsZU9uOiBib29sZWFuKTogdm9pZCB7XG4gICAgY29uc3QgY2FsbGJhY2sgPSAoZGF0YTogbnVtYmVyIHwgSUFsYXJtKSA9PiB7XG4gICAgICBpZiAoXG4gICAgICAgIHRoaXMuYWxhcm1XaWRnZXRTZXJ2aWNlLmZpbHRlckFsYXJtc0J5U3RhdHVzU2V2ZXJpdHlBbmRUeXBlKFxuICAgICAgICAgIGRhdGEsXG4gICAgICAgICAgdGhpcy5hbGFybXMkLnZhbHVlLmRhdGEsXG4gICAgICAgICAgY29uZmlnXG4gICAgICAgIClcbiAgICAgICkge1xuICAgICAgICBpZiAoaXNSZWFsdGltZVRvZ2dsZU9uKSB7XG4gICAgICAgICAgdGhpcy5mZXRjaEFsYXJtcygpO1xuICAgICAgICAgIHRoaXMuYWxhcm1XaWRnZXRBbGFybXNSZWxvYWRDb21wLmlzTmV3QWxhcm1NZXNzYWdlQ2xlYXJlZCA9IHRydWU7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhpcy5hbGFybVdpZGdldEFsYXJtc1JlbG9hZENvbXAuaXNOZXdBbGFybU1lc3NhZ2VDbGVhcmVkID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9O1xuXG4gICAgaWYgKHRoaXMuY29uZmlnLmRldmljZSkge1xuICAgICAgdGhpcy5oYW5kbGVSZWFsdGltZShcbiAgICAgICAgdGhpcy5hbGFybVdpdGhDaGlsZHJlblJlYWx0aW1lU2VydmljZS5vbkFsbCQuYmluZCh0aGlzLmFsYXJtV2l0aENoaWxkcmVuUmVhbHRpbWVTZXJ2aWNlKSxcbiAgICAgICAgY2FsbGJhY2tcbiAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuaGFuZGxlUmVhbHRpbWUoXG4gICAgICAgIHRoaXMuYWxhcm1SZWFsdGltZVNlcnZpY2Uub25BbGwkLmJpbmQodGhpcy5hbGFybVJlYWx0aW1lU2VydmljZSksXG4gICAgICAgIGNhbGxiYWNrXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBTZXRzIHVwIGEgc3Vic2NyaXB0aW9uIHRvIGEgcmVhbHRpbWUgZGF0YSBzdHJlYW0uIFRoaXMgZnVuY3Rpb24gdGFrZXMgYSBzdWJzY3JpYmVyIGZ1bmN0aW9uLFxuICAgKiB3aGljaCBpcyB1c2VkIHRvIHN1YnNjcmliZSB0byB0aGUgZGF0YSBzdHJlYW0sIGFuZCBhIGNhbGxiYWNrIGZ1bmN0aW9uLCB3aGljaCBpcyBjYWxsZWQgd2l0aCBlYWNoXG4gICAqIGRhdGEgdXBkYXRlIHJlY2VpdmVkIGZyb20gdGhlIHN0cmVhbS5cbiAgICpcbiAgICogQHBhcmFtIHN1YnNjcmliZXJGbiAtIEEgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGFuIG9ic2VydmFibGUgc3RyZWFtIG9mIGFsYXJtIGRhdGEuXG4gICAqIEBwYXJhbSBjYWxsYmFjayAtIEEgZnVuY3Rpb24gdG8gYmUgZXhlY3V0ZWQgd2l0aCBlYWNoIGFsYXJtIGRhdGEgdXBkYXRlLlxuICAgKi9cbiAgcHJpdmF0ZSBoYW5kbGVSZWFsdGltZShcbiAgICBzdWJzY3JpYmVyRm46IChpZD86IHN0cmluZykgPT4gT2JzZXJ2YWJsZTxJUmVzdWx0PElBbGFybT4+LFxuICAgIGNhbGxiYWNrOiAoZGF0YTogSUFsYXJtKSA9PiB2b2lkXG4gICk6IHZvaWQge1xuICAgIHN1YnNjcmliZXJGbih0aGlzLmNvbmZpZy5kZXZpY2U/LmlkKVxuICAgICAgLnBpcGUoXG4gICAgICAgIHRhcCgoeyBkYXRhIH0pID0+IHtcbiAgICAgICAgICBjYWxsYmFjayhkYXRhKTtcbiAgICAgICAgfSksXG4gICAgICAgIHRha2VVbnRpbCh0aGlzLnVuc3Vic2NyaWJlJClcbiAgICAgIClcbiAgICAgIC5zdWJzY3JpYmUoKTtcbiAgfVxuXG4gIHByaXZhdGUgc2V0TGVnYWN5UmVjZW50T3JDcml0aWNhbEFsYXJtV2lkZ2V0Q29uZmlnKCk6IHZvaWQge1xuICAgIGNvbnN0IHdpZGdldElkID0gdGhpcy5kYXNoYm9hcmRDaGlsZFsnZGF0YSddPy5jb21wb25lbnRJZFxuICAgICAgPyB0aGlzLmRhc2hib2FyZENoaWxkWydkYXRhJ10/LmNvbXBvbmVudElkIC8vcHJvcGVydHkgaXMgdXNlZCBpbiBjb2NrcGl0IGRhc2hib2FyZHNcbiAgICAgIDogdGhpcy5kYXNoYm9hcmRDaGlsZFsnZGF0YSddPy5uYW1lOyAvL3Byb3BlcnR5IGlzIHVzZWQgaW4gZGV2aWNlIG1hbmFnZW1lbnQgZGFzaGJvYXJkc1xuXG4gICAgaWYgKHdpZGdldElkID09PSBSRUNFTlRfQUxBUk1TX1dJREdFVF9JRCkge1xuICAgICAgY29uc3QgcHJlZGVmaW5lZENvbmZpZyA9IHRoaXMuYWxhcm1XaWRnZXRTZXJ2aWNlLmdldFByZWRlZmluZWRDb25maWd1cmF0aW9uKFxuICAgICAgICB0aGlzLmlzSW50ZXJ2YWxSZWZyZXNoLFxuICAgICAgICBSRUNFTlRfQUxBUk1TX1dJREdFVF9JRFxuICAgICAgKTtcbiAgICAgIE9iamVjdC5hc3NpZ24odGhpcy5jb25maWcsIHByZWRlZmluZWRDb25maWcpO1xuXG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKHdpZGdldElkID09PSBBU1NFVF9BTEFSTVNfV0lER0VUX0lEKSB7XG4gICAgICBjb25zdCBwcmVkZWZpbmVkQ29uZmlnID0gdGhpcy5hbGFybVdpZGdldFNlcnZpY2UuZ2V0UHJlZGVmaW5lZENvbmZpZ3VyYXRpb24oXG4gICAgICAgIHRoaXMuaXNJbnRlcnZhbFJlZnJlc2hcbiAgICAgICk7XG4gICAgICBPYmplY3QuYXNzaWduKHRoaXMuY29uZmlnLCBwcmVkZWZpbmVkQ29uZmlnKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGhhbmRsZUdsb2JhbFJlZnJlc2hMb2FkaW5nKCk6IHZvaWQge1xuICAgIHRoaXMuaXNMb2FkaW5nJFxuICAgICAgLnBpcGUoXG4gICAgICAgIHNraXAoMSksXG4gICAgICAgIGdsb2JhbEF1dG9SZWZyZXNoTG9hZGluZyh0aGlzLndpZGdldEdsb2JhbEF1dG9SZWZyZXNoKSxcbiAgICAgICAgdGFrZVVudGlsKHRoaXMuZGVzdHJveSQpXG4gICAgICApXG4gICAgICAuc3Vic2NyaWJlKCk7XG4gIH1cbn1cbiIsIjxjOHktYWxhcm1zLWxpc3RcbiAgI2xpc3RcbiAgW2FsYXJtc109XCJhbGFybXMkIHwgYXN5bmNcIlxuICBbbmF2aWdhdGlvbk9wdGlvbnNdPVwie1xuICAgIGFsd2F5c05hdmlnYXRlVG9BbGxBbGFybXM6ICFjb25maWcuZGV2aWNlLFxuICAgIGFsbG93TmF2aWdhdGlvblRvQWxhcm1zVmlldzogdHJ1ZSxcbiAgICBpbmNsdWRlQ2xlYXJlZFF1ZXJ5UGFyYW1zOiB0cnVlLFxuICAgIHF1ZXJ5UGFyYW1zSGFuZGxpbmc6ICcnXG4gIH1cIlxuICBbaXNJbml0aWFsTG9hZGluZ109XCJpc0xvYWRpbmckIHwgYXN5bmNcIlxuICBbaGFzUGVybWlzc2lvbnNdPVwiIWlzRGlzYWJsZWRcIlxuPlxuICA8Yzh5LWFsYXJtcy1kYXRlLWZpbHRlclxuICAgICpuZ0lmPVwiY29uZmlnLmRpc3BsYXlEYXRlU2VsZWN0aW9uXCJcbiAgICBbdXBkYXRlUXVlcnlQYXJhbXNdPVwiZmFsc2VcIlxuICAgIFtkYXRlXT1cInRoaXMuY29uZmlnLmRhdGVGaWx0ZXJcIlxuICAgIFtERUZBVUxUX0lOVEVSVkFMXT1cImNvbmZpZy5pbnRlcnZhbCB8fCAnbm9uZSdcIlxuICAgIChkYXRlRmlsdGVyQ2hhbmdlKT1cIm9uRGF0ZUZpbHRlckNoYW5nZSgkZXZlbnQpXCJcbiAgPjwvYzh5LWFsYXJtcy1kYXRlLWZpbHRlcj5cbiAgPGM4eS1hbGFybS13aWRnZXQtYWxhcm1zLXJlbG9hZFxuICAgIGNsYXNzPVwiZC1jb250ZW50c1wiXG4gICAgW2lzSW50ZXJ2YWxSZWZyZXNoXT1cImlzSW50ZXJ2YWxSZWZyZXNoXCJcbiAgICBbcmVmcmVzaEludGVydmFsXT1cInJlZnJlc2hJbnRlcnZhbFwiXG4gICAgW2NvbmZpZ109XCJjb25maWdcIlxuICAgIFtpc0xvYWRpbmddPVwiaXNMb2FkaW5nJFwiXG4gICAgW2lzU2Nyb2xsaW5nXT1cImxpc3QuaXNTY3JvbGxpbmdcIlxuICAgIFtpc1JlZnJlc2hEaXNhYmxlZF09XCJpc0Rpc2FibGVkXCJcbiAgICAob25Db3VudGRvd25FbmRlZCk9XCJmZXRjaEFsYXJtcygpXCJcbiAgICAob25SZWFsVGltZVRvZ2dsZUNoYW5nZWQpPVwiaGFuZGxlUmVhbFRpbWVUb2dnbGVDaGFuZ2UoJGV2ZW50KVwiXG4gID48L2M4eS1hbGFybS13aWRnZXQtYWxhcm1zLXJlbG9hZD5cbjwvYzh5LWFsYXJtcy1saXN0PlxuIl19