UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

344 lines 47.5 kB
import { Injectable } from '@angular/core'; import { AlarmStatus, Severity, SEVERITY_LABELS } from '@c8y/client'; import { AlarmsViewService } from '@c8y/ngx-components/alarms'; import { ALARM_ORDER_VALUES, RECENT_ALARMS_WIDGET_ID } from './alarm-list-widget.model'; import * as i0 from "@angular/core"; import * as i1 from "@c8y/ngx-components/alarms"; export const DEFAULT_PAGE_SIZE = 20; export class AlarmWidgetService { constructor(alarmsViewService) { this.alarmsViewService = alarmsViewService; } /** * Checks if the provided data follows the LegacyAlarmConfig structure. * * This function determines if a given data object is an instance of LegacyAlarmConfig * by checking for the presence of the 'options' property. * * @param data - The data object to be checked. * @returns - Returns `true` if the data object is a LegacyAlarmConfig, otherwise `false`. */ isOldAlarmConfigStructure(data) { return data !== null && typeof data === 'object' && 'options' in data; } /** * Creates predefined widget configuration object. * * This method creates a new configuration object based on * a widgets ID (that determines if is a legacy Recent or Critical alarms widget). * * @param isIntervalRefresh - determines a type of a refresh. * @param widgetId - determines if a config should be done for Recent or Critical alarms widget. * @returns The new, predefined configuration object. */ getPredefinedConfiguration(isIntervalRefresh, widgetId) { return { order: ALARM_ORDER_VALUES.BY_ACTIVE, severities: widgetId === RECENT_ALARMS_WIDGET_ID ? { [Severity.CRITICAL]: true, [Severity.MAJOR]: true, [Severity.MINOR]: true, [Severity.WARNING]: true } : { [Severity.CRITICAL]: true, [Severity.MAJOR]: false, [Severity.MINOR]: false, [Severity.WARNING]: false }, status: widgetId === RECENT_ALARMS_WIDGET_ID ? { [AlarmStatus.ACKNOWLEDGED]: true, [AlarmStatus.ACTIVE]: true, [AlarmStatus.CLEARED]: true } : { [AlarmStatus.ACKNOWLEDGED]: false, [AlarmStatus.ACTIVE]: true, [AlarmStatus.CLEARED]: false }, types: [''], isRealtime: !isIntervalRefresh, isAutoRefreshEnabled: true, refreshInterval: isIntervalRefresh ? this.alarmsViewService.DEFAULT_INTERVAL_VALUE : undefined }; } /** * Transforms a LegacyAlarmConfig object into an AlarmListWidgetConfig object. * * This function maps the properties from an old configuration structure (LegacyAlarmConfig) * to a new configuration structure (AlarmListWidgetConfig). * * @param oldConfig - The old configuration object to be transformed. * @returns - The new configuration object mapped from the old one. */ mapToNewConfigStructure(oldConfig, isIntervalRefresh) { const order = oldConfig.options.orderMode === 'ACTIVE_FIRST' ? ALARM_ORDER_VALUES.BY_ACTIVE : ALARM_ORDER_VALUES.BY_SEVERITY; if (!this.isContainingAllSeverityTypes(oldConfig.options.severity)) { oldConfig.options.severity = this.addAllMissingSeverityTypes(oldConfig.options.severity); } return { order: order, isRealtime: oldConfig.realtime, device: oldConfig.device, showAlarmsForChildren: false, severities: oldConfig.options.severity, status: this.allValuesFalse(oldConfig.options.status) ? { [AlarmStatus.ACKNOWLEDGED]: true, [AlarmStatus.ACTIVE]: true, [AlarmStatus.CLEARED]: true } : oldConfig.options.status, types: oldConfig.options.types?.length ? oldConfig.options.types : [''], isAutoRefreshEnabled: true, refreshInterval: isIntervalRefresh ? this.alarmsViewService.DEFAULT_INTERVAL_VALUE : undefined }; } /** * Checks if the provided severity object contains all the predefined severity types. * * @param severity - A record object where keys are severity type strings and values are boolean. * - This object is checked against the predefined severity types. * @returns `true` if all predefined severity types are present in the severity object; otherwise, `false`. */ isContainingAllSeverityTypes(severity) { return Object.keys(SEVERITY_LABELS).every(severityType => severityType in severity); } /** * Adds any missing severity types to the provided severity object with a default value of `false`. * * @param severity - A record object where keys are severity type strings and values are boolean. * - Missing severity types will be added to this object. * @returns The modified severity object, which includes all predefined severity types, adding any * that were missing with a value of `false`. */ addAllMissingSeverityTypes(severity) { Object.keys(SEVERITY_LABELS).forEach(severityType => { if (!(severityType in severity)) { severity[severityType] = false; } }); return severity; } /** * Maps an AlarmListWidgetConfig object to an AlarmQueryFilter. * * This function converts the provided AlarmListWidgetConfig into a format suitable for querying alarms. * * @param config - The configuration object for the alarm list widget. * @param pageSize - Optional number specifying the size of the pages to be returned in the query. * @returns - The query filter object constructed from the provided configuration. */ mapConfigToQueryFilter(config, pageSize) { const filter = { pageSize: pageSize || DEFAULT_PAGE_SIZE, query: this.getOrderParameters(config.order), severity: this.extractFilterParams(config.severities || {}), status: this.extractFilterParams(config.status || {}), type: (config.types || []).join(','), withTotalPages: true }; if (config.dateFilter) { filter.lastUpdatedFrom = typeof config.dateFilter[0] === 'string' ? config.dateFilter[0] : config.dateFilter[0].toISOString(); filter.createdTo = typeof config.dateFilter[1] === 'string' ? config.dateFilter[1] : config.dateFilter[1].toISOString(); } if (config.device) { filter.source = config.device.id; filter.withSourceAssets = true; filter.withSourceDevices = true; filter.withSourceAssets = config.showAlarmsForChildren ?? true; filter.withSourceDevices = config.showAlarmsForChildren ?? true; } return filter; } /** * Extracts and concatenates filter parameters from a given object. * * This function takes an object containing filter settings (either SeverityFilter * or AlarmStatusSettings) and returns a string of all keys where the corresponding value is true. * If the object is empty or null, an empty string is returned. * * @param obj - The object containing filter settings. * @returns - A concatenated string of keys with true values, separated by commas. */ extractFilterParams(obj) { if (!obj) { return ''; } return Object.entries(obj) .filter(([, value]) => value) .map(([key]) => key) .join(','); } /** * Determines if an incoming real-time alarm has a different status than an existing alarm. * * This function checks if the provided incoming real-time alarm's status differs * from that of an existing alarm with the same ID in the given array of alarms. * * @param existingAlarms - The array of existing alarms. * @param incomingRealtimeAlarm - The incoming real-time alarm to check. * @returns - True if the existing alarm's status has changed, otherwise false. */ hasExistingAlarmChangedStatus(existingAlarms, incomingRealtimeAlarm) { const existingAlarm = existingAlarms.find(alarm => alarm.id === incomingRealtimeAlarm.id); return !!existingAlarm && existingAlarm.status !== incomingRealtimeAlarm.status; } /** * Filters alarms based on their status, severity, and type. * * This method determines if a given alarm, identified either by a numeric ID or an `IAlarm` object, * matches specific criteria defined in `alarms` and optionally `config`. * * @param alarm - The alarm to check, represented either by a numeric ID or an `IAlarm` object. * @param alarms - An array of `IAlarm` objects against which the given alarm is evaluated. * @param config - Optional. Configuration for the alarm list widget, used to define additional * filtering criteria. * * @returns `true` if the alarm matches the specified criteria; otherwise, `false`. * If `alarm` is a number, it always returns `false`. * If `config` is not provided, it uses a legacy filter for critical alarms. * * @remarks * - When `alarm` is a numeric ID, the function returns `false` as it cannot match against type and severity. * - If `config` is not provided, the function assumes a legacy scenario for filtering all critical alarms. */ filterAlarmsByStatusSeverityAndType(alarm, alarms, config) { if (typeof alarm === 'number') { return false; } return this.isAlarmMatchedByConfig(alarm, alarms, config); } /** * Determines if all values in the given object are false. * * This function checks every value in the provided object to see if they are all false. * * @param obj - An object with boolean values. * @returns - Returns `true` if all values in the object are false, otherwise `false`. */ allValuesFalse(obj) { return Object.values(obj).every(value => !value); } /** * Constructs a string of order parameters for a query based on the specified alarm order. * * This function takes an alarm order and maps it to a corresponding set of order parameters. * It supports different ordering types, such as BY_ACTIVE, BY_SEVERITY, and BY_DATE_ASCENDING. * The order parameters are used to construct a query string that determines the order * in which alarms are retrieved or displayed. * * @param order - The specified order for sorting alarms (e.g., BY_ACTIVE). * @returns - A string of order parameters to be used in a query, or an empty string if the order type is unrecognized. */ getOrderParameters(order) { let orderParams; switch (order) { case ALARM_ORDER_VALUES.BY_ACTIVE: orderParams = ['status asc', 'severity asc', 'time.date desc', 'text asc']; return this.buildOrderParameters(orderParams); case ALARM_ORDER_VALUES.BY_SEVERITY: orderParams = ['severity asc', 'time.date desc', 'text asc']; return this.buildOrderParameters(orderParams); case ALARM_ORDER_VALUES.BY_DATE_ASCENDING: orderParams = ['time.date asc', 'text asc']; return this.buildOrderParameters(orderParams); default: orderParams = ['time.date desc', 'text asc']; return this.buildOrderParameters(orderParams); } } /** * Determines if an alarm is matched by the specified widget configuration. * * This function evaluates whether a given alarm should be included based on the severity, * status, and type filters defined in the AlarmListWidgetConfig. It checks if the alarm's * severity and status match the configuration settings and if the alarm's type is included * in the configuration's types (if specified). * * @ignore * @param alarm - The alarm to evaluate. * @param alarms - An array of existing alarms, used for status matching. * @param config - The configuration settings to match against. * @returns - Returns `true` if the alarm matches the configuration criteria; otherwise, `false`. */ isAlarmMatchedByConfig(alarm, alarms, config) { const isSeverityMatched = this.isSeverityMatching(alarm.severity, config); const isStatusMatched = this.isStatusMatching(alarm, alarms, config); const isTypeMatched = this.isTypesMatching(config, alarm); return isSeverityMatched && isStatusMatched && isTypeMatched; } /** * Checks if the severity of an alarm matches the configuration setting. * * This function determines whether the severity of an alarm is included in the * severity settings defined in the AlarmListWidgetConfig. * * @ignore * @param severity - The severity of the alarm to check. * @param config - The configuration with severity settings. * @returns - Returns `true` if the alarm's severity matches the configuration; otherwise, `false`. */ isSeverityMatching(severity, config) { return !!config.severities[severity]; } /** * Evaluates if the status of an alarm matches the configuration setting or has changed. * * This function checks if the status of an alarm is included in the status settings defined in * the AlarmListWidgetConfig, or if the alarm's status has changed based on the existing alarms. * * @ignore * @param alarm - The alarm whose status is to be evaluated. * @param alarms - An array of existing alarms to compare against for status changes. * @param config - The configuration with status settings. * @returns - Returns `true` if the alarm's status matches or has changed as per the configuration; otherwise, `false`. */ isStatusMatching(alarm, alarms, config) { return !!config.status[alarm.status] || this.hasExistingAlarmChangedStatus(alarms, alarm); } /** * Checks if the configuration's types array contains only empty string or includes a specific alarm type. * * @param config - The configuration object with a `types` property. * @param alarm - The alarm object with a `type` property to check against the config's types. * @returns `true` if the config's types array contains only empty string or includes the alarm's type, otherwise `false`. */ isTypesMatching(config, alarm) { return Array.isArray(config.types) && config.types.length === 1 && config.types[0] === '' ? true : config.types?.includes(alarm.type) ?? false; } /** * Constructs a query string from an array of order parameters. * * This function takes an array of ordering parameters and constructs a query string * for use in alarm ordering queries. The parameters are concatenated into a single string, * prefixed with '$orderby='. * * @ignore * @private * @param orderParams - The order parameters to be included in the query. * @returns - A query string representing the order parameters. */ buildOrderParameters(orderParams) { return `$orderby=${orderParams.join(',')}`; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AlarmWidgetService, deps: [{ token: i1.AlarmsViewService }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AlarmWidgetService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AlarmWidgetService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i1.AlarmsViewService }] }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWxhcm0td2lkZ2V0LnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi93aWRnZXRzL2ltcGxlbWVudGF0aW9ucy9hbGFybXMvYWxhcm0td2lkZ2V0LnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMzQyxPQUFPLEVBR0wsV0FBVyxFQUVYLFFBQVEsRUFFUixlQUFlLEVBQ2hCLE1BQU0sYUFBYSxDQUFDO0FBR3JCLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQy9ELE9BQU8sRUFDTCxrQkFBa0IsRUFJbEIsdUJBQXVCLEVBQ3hCLE1BQU0sMkJBQTJCLENBQUM7OztBQUVuQyxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBRyxFQUFFLENBQUM7QUFHcEMsTUFBTSxPQUFPLGtCQUFrQjtJQUM3QixZQUFvQixpQkFBb0M7UUFBcEMsc0JBQWlCLEdBQWpCLGlCQUFpQixDQUFtQjtJQUFHLENBQUM7SUFFNUQ7Ozs7Ozs7O09BUUc7SUFDSCx5QkFBeUIsQ0FDdkIsSUFBbUQ7UUFFbkQsT0FBTyxJQUFJLEtBQUssSUFBSSxJQUFJLE9BQU8sSUFBSSxLQUFLLFFBQVEsSUFBSSxTQUFTLElBQUksSUFBSSxDQUFDO0lBQ3hFLENBQUM7SUFFRDs7Ozs7Ozs7O09BU0c7SUFDSCwwQkFBMEIsQ0FBQyxpQkFBMEIsRUFBRSxRQUFpQjtRQUN0RSxPQUFPO1lBQ0wsS0FBSyxFQUFFLGtCQUFrQixDQUFDLFNBQVM7WUFDbkMsVUFBVSxFQUNSLFFBQVEsS0FBSyx1QkFBdUI7Z0JBQ2xDLENBQUMsQ0FBRTtvQkFDQyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsRUFBRSxJQUFJO29CQUN6QixDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsRUFBRSxJQUFJO29CQUN0QixDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsRUFBRSxJQUFJO29CQUN0QixDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsRUFBRSxJQUFJO2lCQUNKO2dCQUN4QixDQUFDLENBQUU7b0JBQ0MsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLEVBQUUsSUFBSTtvQkFDekIsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLEVBQUUsS0FBSztvQkFDdkIsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLEVBQUUsS0FBSztvQkFDdkIsQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLEVBQUUsS0FBSztpQkFDTDtZQUM1QixNQUFNLEVBQ0osUUFBUSxLQUFLLHVCQUF1QjtnQkFDbEMsQ0FBQyxDQUFFO29CQUNDLENBQUMsV0FBVyxDQUFDLFlBQVksQ0FBQyxFQUFFLElBQUk7b0JBQ2hDLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxFQUFFLElBQUk7b0JBQzFCLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxFQUFFLElBQUk7aUJBQ0o7Z0JBQzNCLENBQUMsQ0FBRTtvQkFDQyxDQUFDLFdBQVcsQ0FBQyxZQUFZLENBQUMsRUFBRSxLQUFLO29CQUNqQyxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsRUFBRSxJQUFJO29CQUMxQixDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsRUFBRSxLQUFLO2lCQUNMO1lBQy9CLEtBQUssRUFBRSxDQUFDLEVBQUUsQ0FBQztZQUNYLFVBQVUsRUFBRSxDQUFDLGlCQUFpQjtZQUM5QixvQkFBb0IsRUFBRSxJQUFJO1lBQzFCLGVBQWUsRUFBRSxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLHNCQUFzQixDQUFDLENBQUMsQ0FBQyxTQUFTO1NBQy9GLENBQUM7SUFDSixDQUFDO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSCx1QkFBdUIsQ0FDckIsU0FBZ0MsRUFDaEMsaUJBQTBCO1FBRTFCLE1BQU0sS0FBSyxHQUNULFNBQVMsQ0FBQyxPQUFPLENBQUMsU0FBUyxLQUFLLGNBQWM7WUFDNUMsQ0FBQyxDQUFDLGtCQUFrQixDQUFDLFNBQVM7WUFDOUIsQ0FBQyxDQUFDLGtCQUFrQixDQUFDLFdBQVcsQ0FBQztRQUVyQyxJQUFJLENBQUMsSUFBSSxDQUFDLDRCQUE0QixDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztZQUNuRSxTQUFTLENBQUMsT0FBTyxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUMsMEJBQTBCLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUMzRixDQUFDO1FBRUQsT0FBTztZQUNMLEtBQUssRUFBRSxLQUFLO1lBQ1osVUFBVSxFQUFFLFNBQVMsQ0FBQyxRQUFRO1lBQzlCLE1BQU0sRUFBRSxTQUFTLENBQUMsTUFBTTtZQUN4QixxQkFBcUIsRUFBRSxLQUFLO1lBQzVCLFVBQVUsRUFBRSxTQUFTLENBQUMsT0FBTyxDQUFDLFFBQVE7WUFDdEMsTUFBTSxFQUFFLElBQUksQ0FBQyxjQUFjLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUM7Z0JBQ25ELENBQUMsQ0FBRTtvQkFDQyxDQUFDLFdBQVcsQ0FBQyxZQUFZLENBQUMsRUFBRSxJQUFJO29CQUNoQyxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsRUFBRSxJQUFJO29CQUMxQixDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsRUFBRSxJQUFJO2lCQUNKO2dCQUMzQixDQUFDLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxNQUFNO1lBQzVCLEtBQUssRUFBRSxTQUFTLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUN2RSxvQkFBb0IsRUFBRSxJQUFJO1lBQzFCLGVBQWUsRUFBRSxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLHNCQUFzQixDQUFDLENBQUMsQ0FBQyxTQUFTO1NBQy9GLENBQUM7SUFDSixDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0gsNEJBQTRCLENBQUMsUUFBd0I7UUFDbkQsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxZQUFZLENBQUMsRUFBRSxDQUFDLFlBQVksSUFBSSxRQUFRLENBQUMsQ0FBQztJQUN0RixDQUFDO0lBRUQ7Ozs7Ozs7T0FPRztJQUNILDBCQUEwQixDQUFDLFFBQXdCO1FBQ2pELE1BQU0sQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxFQUFFO1lBQ2xELElBQUksQ0FBQyxDQUFDLFlBQVksSUFBSSxRQUFRLENBQUMsRUFBRSxDQUFDO2dCQUNoQyxRQUFRLENBQUMsWUFBWSxDQUFDLEdBQUcsS0FBSyxDQUFDO1lBQ2pDLENBQUM7UUFDSCxDQUFDLENBQUMsQ0FBQztRQUNILE9BQU8sUUFBUSxDQUFDO0lBQ2xCLENBQUM7SUFFRDs7Ozs7Ozs7T0FRRztJQUNILHNCQUFzQixDQUFDLE1BQTZCLEVBQUUsUUFBaUI7UUFDckUsTUFBTSxNQUFNLEdBQXFCO1lBQy9CLFFBQVEsRUFBRSxRQUFRLElBQUksaUJBQWlCO1lBQ3ZDLEtBQUssRUFBRSxJQUFJLENBQUMsa0JBQWtCLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQztZQUM1QyxRQUFRLEVBQUUsSUFBSSxDQUFDLG1CQUFtQixDQUFDLE1BQU0sQ0FBQyxVQUFVLElBQUksRUFBRSxDQUFDO1lBQzNELE1BQU0sRUFBRSxJQUFJLENBQUMsbUJBQW1CLENBQUMsTUFBTSxDQUFDLE1BQU0sSUFBSSxFQUFFLENBQUM7WUFDckQsSUFBSSxFQUFFLENBQUMsTUFBTSxDQUFDLEtBQUssSUFBSSxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDO1lBQ3BDLGNBQWMsRUFBRSxJQUFJO1NBQ3JCLENBQUM7UUFDRixJQUFJLE1BQU0sQ0FBQyxVQUFVLEVBQUUsQ0FBQztZQUN0QixNQUFNLENBQUMsZUFBZTtnQkFDcEIsT0FBTyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxLQUFLLFFBQVE7b0JBQ3RDLENBQUMsQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQztvQkFDdEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDekMsTUFBTSxDQUFDLFNBQVM7Z0JBQ2QsT0FBTyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxLQUFLLFFBQVE7b0JBQ3RDLENBQUMsQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQztvQkFDdEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDM0MsQ0FBQztRQUNELElBQUksTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ2xCLE1BQU0sQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7WUFDakMsTUFBTSxDQUFDLGdCQUFnQixHQUFHLElBQUksQ0FBQztZQUMvQixNQUFNLENBQUMsaUJBQWlCLEdBQUcsSUFBSSxDQUFDO1lBQ2hDLE1BQU0sQ0FBQyxnQkFBZ0IsR0FBRyxNQUFNLENBQUMscUJBQXFCLElBQUksSUFBSSxDQUFDO1lBQy9ELE1BQU0sQ0FBQyxpQkFBaUIsR0FBRyxNQUFNLENBQUMscUJBQXFCLElBQUksSUFBSSxDQUFDO1FBQ2xFLENBQUM7UUFDRCxPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0lBRUQ7Ozs7Ozs7OztPQVNHO0lBQ0gsbUJBQW1CLENBQUMsR0FBeUM7UUFDM0QsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ1QsT0FBTyxFQUFFLENBQUM7UUFDWixDQUFDO1FBQ0QsT0FBTyxNQUFNLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQzthQUN2QixNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsS0FBSyxDQUFDLEVBQUUsRUFBRSxDQUFDLEtBQUssQ0FBQzthQUM1QixHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxHQUFHLENBQUM7YUFDbkIsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ2YsQ0FBQztJQUVEOzs7Ozs7Ozs7T0FTRztJQUNILDZCQUE2QixDQUFDLGNBQXdCLEVBQUUscUJBQTZCO1FBQ25GLE1BQU0sYUFBYSxHQUFHLGNBQWMsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsRUFBRSxLQUFLLHFCQUFxQixDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQzFGLE9BQU8sQ0FBQyxDQUFDLGFBQWEsSUFBSSxhQUFhLENBQUMsTUFBTSxLQUFLLHFCQUFxQixDQUFDLE1BQU0sQ0FBQztJQUNsRixDQUFDO0lBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7OztPQWtCRztJQUNILG1DQUFtQyxDQUNqQyxLQUFzQixFQUN0QixNQUFnQixFQUNoQixNQUE4QjtRQUU5QixJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQzlCLE9BQU8sS0FBSyxDQUFDO1FBQ2YsQ0FBQztRQUVELE9BQU8sSUFBSSxDQUFDLHNCQUFzQixDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDNUQsQ0FBQztJQUVEOzs7Ozs7O09BT0c7SUFDSCxjQUFjLENBQUMsR0FBK0I7UUFDNUMsT0FBTyxNQUFNLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDbkQsQ0FBQztJQUVEOzs7Ozs7Ozs7O09BVUc7SUFDSCxrQkFBa0IsQ0FBQyxLQUFxQjtRQUN0QyxJQUFJLFdBQXFCLENBQUM7UUFFMUIsUUFBUSxLQUFLLEVBQUUsQ0FBQztZQUNkLEtBQUssa0JBQWtCLENBQUMsU0FBUztnQkFDL0IsV0FBVyxHQUFHLENBQUMsWUFBWSxFQUFFLGNBQWMsRUFBRSxnQkFBZ0IsRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDM0UsT0FBTyxJQUFJLENBQUMsb0JBQW9CLENBQUMsV0FBVyxDQUFDLENBQUM7WUFDaEQsS0FBSyxrQkFBa0IsQ0FBQyxXQUFXO2dCQUNqQyxXQUFXLEdBQUcsQ0FBQyxjQUFjLEVBQUUsZ0JBQWdCLEVBQUUsVUFBVSxDQUFDLENBQUM7Z0JBQzdELE9BQU8sSUFBSSxDQUFDLG9CQUFvQixDQUFDLFdBQVcsQ0FBQyxDQUFDO1lBQ2hELEtBQUssa0JBQWtCLENBQUMsaUJBQWlCO2dCQUN2QyxXQUFXLEdBQUcsQ0FBQyxlQUFlLEVBQUUsVUFBVSxDQUFDLENBQUM7Z0JBQzVDLE9BQU8sSUFBSSxDQUFDLG9CQUFvQixDQUFDLFdBQVcsQ0FBQyxDQUFDO1lBQ2hEO2dCQUNFLFdBQVcsR0FBRyxDQUFDLGdCQUFnQixFQUFFLFVBQVUsQ0FBQyxDQUFDO2dCQUM3QyxPQUFPLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUNsRCxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7Ozs7Ozs7Ozs7O09BYUc7SUFDSCxzQkFBc0IsQ0FBQyxLQUFhLEVBQUUsTUFBZ0IsRUFBRSxNQUE2QjtRQUNuRixNQUFNLGlCQUFpQixHQUFHLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxLQUFLLENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQzFFLE1BQU0sZUFBZSxHQUFHLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ3JFLE1BQU0sYUFBYSxHQUFHLElBQUksQ0FBQyxlQUFlLENBQUMsTUFBTSxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBRTFELE9BQU8saUJBQWlCLElBQUksZUFBZSxJQUFJLGFBQWEsQ0FBQztJQUMvRCxDQUFDO0lBQ0Q7Ozs7Ozs7Ozs7T0FVRztJQUNILGtCQUFrQixDQUFDLFFBQXNCLEVBQUUsTUFBNkI7UUFDdEUsT0FBTyxDQUFDLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUN2QyxDQUFDO0lBRUQ7Ozs7Ozs7Ozs7O09BV0c7SUFDSCxnQkFBZ0IsQ0FBQyxLQUFhLEVBQUUsTUFBZ0IsRUFBRSxNQUE2QjtRQUM3RSxPQUFPLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsSUFBSSxJQUFJLENBQUMsNkJBQTZCLENBQUMsTUFBTSxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQzVGLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxlQUFlLENBQUMsTUFBNkIsRUFBRSxLQUFhO1FBQzFELE9BQU8sS0FBSyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksTUFBTSxDQUFDLEtBQUssQ0FBQyxNQUFNLEtBQUssQ0FBQyxJQUFJLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssRUFBRTtZQUN2RixDQUFDLENBQUMsSUFBSTtZQUNOLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLFFBQVEsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksS0FBSyxDQUFDO0lBQ2xELENBQUM7SUFFRDs7Ozs7Ozs7Ozs7T0FXRztJQUNLLG9CQUFvQixDQUFDLFdBQXFCO1FBQ2hELE9BQU8sWUFBWSxXQUFXLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7SUFDN0MsQ0FBQzsrR0F0V1Usa0JBQWtCO21IQUFsQixrQkFBa0IsY0FETCxNQUFNOzs0RkFDbkIsa0JBQWtCO2tCQUQ5QixVQUFVO21CQUFDLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7XG4gIEFsYXJtUXVlcnlGaWx0ZXIsXG4gIEFsYXJtU3RhdHVzU2V0dGluZ3MsXG4gIEFsYXJtU3RhdHVzLFxuICBJQWxhcm0sXG4gIFNldmVyaXR5LFxuICBTZXZlcml0eUZpbHRlcixcbiAgU0VWRVJJVFlfTEFCRUxTXG59IGZyb20gJ0BjOHkvY2xpZW50JztcblxuaW1wb3J0IHsgU2V2ZXJpdHlTZXR0aW5ncywgU2V2ZXJpdHlUeXBlIH0gZnJvbSAnQGM4eS9jbGllbnQnO1xuaW1wb3J0IHsgQWxhcm1zVmlld1NlcnZpY2UgfSBmcm9tICdAYzh5L25neC1jb21wb25lbnRzL2FsYXJtcyc7XG5pbXBvcnQge1xuICBBTEFSTV9PUkRFUl9WQUxVRVMsXG4gIEFsYXJtTGlzdFdpZGdldENvbmZpZyxcbiAgQWxhcm1PcmRlclR5cGUsXG4gIExlZ2FjeUFsYXJtTGlzdENvbmZpZyxcbiAgUkVDRU5UX0FMQVJNU19XSURHRVRfSURcbn0gZnJvbSAnLi9hbGFybS1saXN0LXdpZGdldC5tb2RlbCc7XG5cbmV4cG9ydCBjb25zdCBERUZBVUxUX1BBR0VfU0laRSA9IDIwO1xuXG5ASW5qZWN0YWJsZSh7IHByb3ZpZGVkSW46ICdyb290JyB9KVxuZXhwb3J0IGNsYXNzIEFsYXJtV2lkZ2V0U2VydmljZSB7XG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgYWxhcm1zVmlld1NlcnZpY2U6IEFsYXJtc1ZpZXdTZXJ2aWNlKSB7fVxuXG4gIC8qKlxuICAgKiBDaGVja3MgaWYgdGhlIHByb3ZpZGVkIGRhdGEgZm9sbG93cyB0aGUgTGVnYWN5QWxhcm1Db25maWcgc3RydWN0dXJlLlxuICAgKlxuICAgKiBUaGlzIGZ1bmN0aW9uIGRldGVybWluZXMgaWYgYSBnaXZlbiBkYXRhIG9iamVjdCBpcyBhbiBpbnN0YW5jZSBvZiBMZWdhY3lBbGFybUNvbmZpZ1xuICAgKiBieSBjaGVja2luZyBmb3IgdGhlIHByZXNlbmNlIG9mIHRoZSAnb3B0aW9ucycgcHJvcGVydHkuXG4gICAqXG4gICAqIEBwYXJhbSBkYXRhIC0gVGhlIGRhdGEgb2JqZWN0IHRvIGJlIGNoZWNrZWQuXG4gICAqIEByZXR1cm5zIC0gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIGRhdGEgb2JqZWN0IGlzIGEgTGVnYWN5QWxhcm1Db25maWcsIG90aGVyd2lzZSBgZmFsc2VgLlxuICAgKi9cbiAgaXNPbGRBbGFybUNvbmZpZ1N0cnVjdHVyZShcbiAgICBkYXRhOiBMZWdhY3lBbGFybUxpc3RDb25maWcgfCBBbGFybUxpc3RXaWRnZXRDb25maWdcbiAgKTogZGF0YSBpcyBMZWdhY3lBbGFybUxpc3RDb25maWcge1xuICAgIHJldHVybiBkYXRhICE9PSBudWxsICYmIHR5cGVvZiBkYXRhID09PSAnb2JqZWN0JyAmJiAnb3B0aW9ucycgaW4gZGF0YTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIHByZWRlZmluZWQgd2lkZ2V0IGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxuICAgKlxuICAgKiBUaGlzIG1ldGhvZCBjcmVhdGVzIGEgbmV3IGNvbmZpZ3VyYXRpb24gb2JqZWN0IGJhc2VkIG9uXG4gICAqIGEgd2lkZ2V0cyBJRCAodGhhdCBkZXRlcm1pbmVzIGlmIGlzIGEgbGVnYWN5IFJlY2VudCBvciBDcml0aWNhbCBhbGFybXMgd2lkZ2V0KS5cbiAgICpcbiAgICogQHBhcmFtIGlzSW50ZXJ2YWxSZWZyZXNoIC0gZGV0ZXJtaW5lcyBhIHR5cGUgb2YgYSByZWZyZXNoLlxuICAgKiBAcGFyYW0gd2lkZ2V0SWQgLSBkZXRlcm1pbmVzIGlmIGEgY29uZmlnIHNob3VsZCBiZSBkb25lIGZvciBSZWNlbnQgb3IgQ3JpdGljYWwgYWxhcm1zIHdpZGdldC5cbiAgICogQHJldHVybnMgVGhlIG5ldywgcHJlZGVmaW5lZCBjb25maWd1cmF0aW9uIG9iamVjdC5cbiAgICovXG4gIGdldFByZWRlZmluZWRDb25maWd1cmF0aW9uKGlzSW50ZXJ2YWxSZWZyZXNoOiBib29sZWFuLCB3aWRnZXRJZD86IHN0cmluZyk6IEFsYXJtTGlzdFdpZGdldENvbmZpZyB7XG4gICAgcmV0dXJuIHtcbiAgICAgIG9yZGVyOiBBTEFSTV9PUkRFUl9WQUxVRVMuQllfQUNUSVZFLFxuICAgICAgc2V2ZXJpdGllczpcbiAgICAgICAgd2lkZ2V0SWQgPT09IFJFQ0VOVF9BTEFSTVNfV0lER0VUX0lEXG4gICAgICAgICAgPyAoe1xuICAgICAgICAgICAgICBbU2V2ZXJpdHkuQ1JJVElDQUxdOiB0cnVlLFxuICAgICAgICAgICAgICBbU2V2ZXJpdHkuTUFKT1JdOiB0cnVlLFxuICAgICAgICAgICAgICBbU2V2ZXJpdHkuTUlOT1JdOiB0cnVlLFxuICAgICAgICAgICAgICBbU2V2ZXJpdHkuV0FSTklOR106IHRydWVcbiAgICAgICAgICAgIH0gYXMgU2V2ZXJpdHlTZXR0aW5ncylcbiAgICAgICAgICA6ICh7XG4gICAgICAgICAgICAgIFtTZXZlcml0eS5DUklUSUNBTF06IHRydWUsXG4gICAgICAgICAgICAgIFtTZXZlcml0eS5NQUpPUl06IGZhbHNlLFxuICAgICAgICAgICAgICBbU2V2ZXJpdHkuTUlOT1JdOiBmYWxzZSxcbiAgICAgICAgICAgICAgW1NldmVyaXR5LldBUk5JTkddOiBmYWxzZVxuICAgICAgICAgICAgfSBhcyBTZXZlcml0eVNldHRpbmdzKSxcbiAgICAgIHN0YXR1czpcbiAgICAgICAgd2lkZ2V0SWQgPT09IFJFQ0VOVF9BTEFSTVNfV0lER0VUX0lEXG4gICAgICAgICAgPyAoe1xuICAgICAgICAgICAgICBbQWxhcm1TdGF0dXMuQUNLTk9XTEVER0VEXTogdHJ1ZSxcbiAgICAgICAgICAgICAgW0FsYXJtU3RhdHVzLkFDVElWRV06IHRydWUsXG4gICAgICAgICAgICAgIFtBbGFybVN0YXR1cy5DTEVBUkVEXTogdHJ1ZVxuICAgICAgICAgICAgfSBhcyBBbGFybVN0YXR1c1NldHRpbmdzKVxuICAgICAgICAgIDogKHtcbiAgICAgICAgICAgICAgW0FsYXJtU3RhdHVzLkFDS05PV0xFREdFRF06IGZhbHNlLFxuICAgICAgICAgICAgICBbQWxhcm1TdGF0dXMuQUNUSVZFXTogdHJ1ZSxcbiAgICAgICAgICAgICAgW0FsYXJtU3RhdHVzLkNMRUFSRURdOiBmYWxzZVxuICAgICAgICAgICAgfSBhcyBBbGFybVN0YXR1c1NldHRpbmdzKSxcbiAgICAgIHR5cGVzOiBbJyddLFxuICAgICAgaXNSZWFsdGltZTogIWlzSW50ZXJ2YWxSZWZyZXNoLFxuICAgICAgaXNBdXRvUmVmcmVzaEVuYWJsZWQ6IHRydWUsXG4gICAgICByZWZyZXNoSW50ZXJ2YWw6IGlzSW50ZXJ2YWxSZWZyZXNoID8gdGhpcy5hbGFybXNWaWV3U2VydmljZS5ERUZBVUxUX0lOVEVSVkFMX1ZBTFVFIDogdW5kZWZpbmVkXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUcmFuc2Zvcm1zIGEgTGVnYWN5QWxhcm1Db25maWcgb2JqZWN0IGludG8gYW4gQWxhcm1MaXN0V2lkZ2V0Q29uZmlnIG9iamVjdC5cbiAgICpcbiAgICogVGhpcyBmdW5jdGlvbiBtYXBzIHRoZSBwcm9wZXJ0aWVzIGZyb20gYW4gb2xkIGNvbmZpZ3VyYXRpb24gc3RydWN0dXJlIChMZWdhY3lBbGFybUNvbmZpZylcbiAgICogdG8gYSBuZXcgY29uZmlndXJhdGlvbiBzdHJ1Y3R1cmUgKEFsYXJtTGlzdFdpZGdldENvbmZpZykuXG4gICAqXG4gICAqIEBwYXJhbSBvbGRDb25maWcgLSBUaGUgb2xkIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHRvIGJlIHRyYW5zZm9ybWVkLlxuICAgKiBAcmV0dXJucyAtIFRoZSBuZXcgY29uZmlndXJhdGlvbiBvYmplY3QgbWFwcGVkIGZyb20gdGhlIG9sZCBvbmUuXG4gICAqL1xuICBtYXBUb05ld0NvbmZpZ1N0cnVjdHVyZShcbiAgICBvbGRDb25maWc6IExlZ2FjeUFsYXJtTGlzdENvbmZpZyxcbiAgICBpc0ludGVydmFsUmVmcmVzaDogYm9vbGVhblxuICApOiBBbGFybUxpc3RXaWRnZXRDb25maWcge1xuICAgIGNvbnN0IG9yZGVyOiBBbGFybU9yZGVyVHlwZSA9XG4gICAgICBvbGRDb25maWcub3B0aW9ucy5vcmRlck1vZGUgPT09ICdBQ1RJVkVfRklSU1QnXG4gICAgICAgID8gQUxBUk1fT1JERVJfVkFMVUVTLkJZX0FDVElWRVxuICAgICAgICA6IEFMQVJNX09SREVSX1ZBTFVFUy5CWV9TRVZFUklUWTtcblxuICAgIGlmICghdGhpcy5pc0NvbnRhaW5pbmdBbGxTZXZlcml0eVR5cGVzKG9sZENvbmZpZy5vcHRpb25zLnNldmVyaXR5KSkge1xuICAgICAgb2xkQ29uZmlnLm9wdGlvbnMuc2V2ZXJpdHkgPSB0aGlzLmFkZEFsbE1pc3NpbmdTZXZlcml0eVR5cGVzKG9sZENvbmZpZy5vcHRpb25zLnNldmVyaXR5KTtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgb3JkZXI6IG9yZGVyLFxuICAgICAgaXNSZWFsdGltZTogb2xkQ29uZmlnLnJlYWx0aW1lLFxuICAgICAgZGV2aWNlOiBvbGRDb25maWcuZGV2aWNlLFxuICAgICAgc2hvd0FsYXJtc0ZvckNoaWxkcmVuOiBmYWxzZSxcbiAgICAgIHNldmVyaXRpZXM6IG9sZENvbmZpZy5vcHRpb25zLnNldmVyaXR5LFxuICAgICAgc3RhdHVzOiB0aGlzLmFsbFZhbHVlc0ZhbHNlKG9sZENvbmZpZy5vcHRpb25zLnN0YXR1cylcbiAgICAgICAgPyAoe1xuICAgICAgICAgICAgW0FsYXJtU3RhdHVzLkFDS05PV0xFREdFRF06IHRydWUsXG4gICAgICAgICAgICBbQWxhcm1TdGF0dXMuQUNUSVZFXTogdHJ1ZSxcbiAgICAgICAgICAgIFtBbGFybVN0YXR1cy5DTEVBUkVEXTogdHJ1ZVxuICAgICAgICAgIH0gYXMgQWxhcm1TdGF0dXNTZXR0aW5ncylcbiAgICAgICAgOiBvbGRDb25maWcub3B0aW9ucy5zdGF0dXMsXG4gICAgICB0eXBlczogb2xkQ29uZmlnLm9wdGlvbnMudHlwZXM/Lmxlbmd0aCA/IG9sZENvbmZpZy5vcHRpb25zLnR5cGVzIDogWycnXSxcbiAgICAgIGlzQXV0b1JlZnJlc2hFbmFibGVkOiB0cnVlLFxuICAgICAgcmVmcmVzaEludGVydmFsOiBpc0ludGVydmFsUmVmcmVzaCA/IHRoaXMuYWxhcm1zVmlld1NlcnZpY2UuREVGQVVMVF9JTlRFUlZBTF9WQUxVRSA6IHVuZGVmaW5lZFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2tzIGlmIHRoZSBwcm92aWRlZCBzZXZlcml0eSBvYmplY3QgY29udGFpbnMgYWxsIHRoZSBwcmVkZWZpbmVkIHNldmVyaXR5IHR5cGVzLlxuICAgKlxuICAgKiBAcGFyYW0gc2V2ZXJpdHkgLSBBIHJlY29yZCBvYmplY3Qgd2hlcmUga2V5cyBhcmUgc2V2ZXJpdHkgdHlwZSBzdHJpbmdzIGFuZCB2YWx1ZXMgYXJlIGJvb2xlYW4uXG4gICAqICAgICAgICAgICAgICAgICAtIFRoaXMgb2JqZWN0IGlzIGNoZWNrZWQgYWdhaW5zdCB0aGUgcHJlZGVmaW5lZCBzZXZlcml0eSB0eXBlcy5cbiAgICogQHJldHVybnMgYHRydWVgIGlmIGFsbCBwcmVkZWZpbmVkIHNldmVyaXR5IHR5cGVzIGFyZSBwcmVzZW50IGluIHRoZSBzZXZlcml0eSBvYmplY3Q7IG90aGVyd2lzZSwgYGZhbHNlYC5cbiAgICovXG4gIGlzQ29udGFpbmluZ0FsbFNldmVyaXR5VHlwZXMoc2V2ZXJpdHk6IFNldmVyaXR5RmlsdGVyKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIE9iamVjdC5rZXlzKFNFVkVSSVRZX0xBQkVMUykuZXZlcnkoc2V2ZXJpdHlUeXBlID0+IHNldmVyaXR5VHlwZSBpbiBzZXZlcml0eSk7XG4gIH1cblxuICAvKipcbiAgICogQWRkcyBhbnkgbWlzc2luZyBzZXZlcml0eSB0eXBlcyB0byB0aGUgcHJvdmlkZWQgc2V2ZXJpdHkgb2JqZWN0IHdpdGggYSBkZWZhdWx0IHZhbHVlIG9mIGBmYWxzZWAuXG4gICAqXG4gICAqIEBwYXJhbSBzZXZlcml0eSAtIEEgcmVjb3JkIG9iamVjdCB3aGVyZSBrZXlzIGFyZSBzZXZlcml0eSB0eXBlIHN0cmluZ3MgYW5kIHZhbHVlcyBhcmUgYm9vbGVhbi5cbiAgICogICAgICAgICAgICAgICAgIC0gTWlzc2luZyBzZXZlcml0eSB0eXBlcyB3aWxsIGJlIGFkZGVkIHRvIHRoaXMgb2JqZWN0LlxuICAgKiBAcmV0dXJucyBUaGUgbW9kaWZpZWQgc2V2ZXJpdHkgb2JqZWN0LCB3aGljaCBpbmNsdWRlcyBhbGwgcHJlZGVmaW5lZCBzZXZlcml0eSB0eXBlcywgYWRkaW5nIGFueVxuICAgKiAgICAgICAgICB0aGF0IHdlcmUgbWlzc2luZyB3aXRoIGEgdmFsdWUgb2YgYGZhbHNlYC5cbiAgICovXG4gIGFkZEFsbE1pc3NpbmdTZXZlcml0eVR5cGVzKHNldmVyaXR5OiBTZXZlcml0eUZpbHRlcik6IFNldmVyaXR5RmlsdGVyIHtcbiAgICBPYmplY3Qua2V5cyhTRVZFUklUWV9MQUJFTFMpLmZvckVhY2goc2V2ZXJpdHlUeXBlID0+IHtcbiAgICAgIGlmICghKHNldmVyaXR5VHlwZSBpbiBzZXZlcml0eSkpIHtcbiAgICAgICAgc2V2ZXJpdHlbc2V2ZXJpdHlUeXBlXSA9IGZhbHNlO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHJldHVybiBzZXZlcml0eTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNYXBzIGFuIEFsYXJtTGlzdFdpZGdldENvbmZpZyBvYmplY3QgdG8gYW4gQWxhcm1RdWVyeUZpbHRlci5cbiAgICpcbiAgICogVGhpcyBmdW5jdGlvbiBjb252ZXJ0cyB0aGUgcHJvdmlkZWQgQWxhcm1MaXN0V2lkZ2V0Q29uZmlnIGludG8gYSBmb3JtYXQgc3VpdGFibGUgZm9yIHF1ZXJ5aW5nIGFsYXJtcy5cbiAgICpcbiAgICogQHBhcmFtIGNvbmZpZyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBmb3IgdGhlIGFsYXJtIGxpc3Qgd2lkZ2V0LlxuICAgKiBAcGFyYW0gcGFnZVNpemUgLSBPcHRpb25hbCBudW1iZXIgc3BlY2lmeWluZyB0aGUgc2l6ZSBvZiB0aGUgcGFnZXMgdG8gYmUgcmV0dXJuZWQgaW4gdGhlIHF1ZXJ5LlxuICAgKiBAcmV0dXJucyAtIFRoZSBxdWVyeSBmaWx0ZXIgb2JqZWN0IGNvbnN0cnVjdGVkIGZyb20gdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24uXG4gICAqL1xuICBtYXBDb25maWdUb1F1ZXJ5RmlsdGVyKGNvbmZpZzogQWxhcm1MaXN0V2lkZ2V0Q29uZmlnLCBwYWdlU2l6ZT86IG51bWJlcik6IEFsYXJtUXVlcnlGaWx0ZXIge1xuICAgIGNvbnN0IGZpbHRlcjogQWxhcm1RdWVyeUZpbHRlciA9IHtcbiAgICAgIHBhZ2VTaXplOiBwYWdlU2l6ZSB8fCBERUZBVUxUX1BBR0VfU0laRSxcbiAgICAgIHF1ZXJ5OiB0aGlzLmdldE9yZGVyUGFyYW1ldGVycyhjb25maWcub3JkZXIpLFxuICAgICAgc2V2ZXJpdHk6IHRoaXMuZXh0cmFjdEZpbHRlclBhcmFtcyhjb25maWcuc2V2ZXJpdGllcyB8fCB7fSksXG4gICAgICBzdGF0dXM6IHRoaXMuZXh0cmFjdEZpbHRlclBhcmFtcyhjb25maWcuc3RhdHVzIHx8IHt9KSxcbiAgICAgIHR5cGU6IChjb25maWcudHlwZXMgfHwgW10pLmpvaW4oJywnKSxcbiAgICAgIHdpdGhUb3RhbFBhZ2VzOiB0cnVlXG4gICAgfTtcbiAgICBpZiAoY29uZmlnLmRhdGVGaWx0ZXIpIHtcbiAgICAgIGZpbHRlci5sYXN0VXBkYXRlZEZyb20gPVxuICAgICAgICB0eXBlb2YgY29uZmlnLmRhdGVGaWx0ZXJbMF0gPT09ICdzdHJpbmcnXG4gICAgICAgICAgPyBjb25maWcuZGF0ZUZpbHRlclswXVxuICAgICAgICAgIDogY29uZmlnLmRhdGVGaWx0ZXJbMF0udG9JU09TdHJpbmcoKTtcbiAgICAgIGZpbHRlci5jcmVhdGVkVG8gPVxuICAgICAgICB0eXBlb2YgY29uZmlnLmRhdGVGaWx0ZXJbMV0gPT09ICdzdHJpbmcnXG4gICAgICAgICAgPyBjb25maWcuZGF0ZUZpbHRlclsxXVxuICAgICAgICAgIDogY29uZmlnLmRhdGVGaWx0ZXJbMV0udG9JU09TdHJpbmcoKTtcbiAgICB9XG4gICAgaWYgKGNvbmZpZy5kZXZpY2UpIHtcbiAgICAgIGZpbHRlci5zb3VyY2UgPSBjb25maWcuZGV2aWNlLmlkO1xuICAgICAgZmlsdGVyLndpdGhTb3VyY2VBc3NldHMgPSB0cnVlO1xuICAgICAgZmlsdGVyLndpdGhTb3VyY2VEZXZpY2VzID0gdHJ1ZTtcbiAgICAgIGZpbHRlci53aXRoU291cmNlQXNzZXRzID0gY29uZmlnLnNob3dBbGFybXNGb3JDaGlsZHJlbiA/PyB0cnVlO1xuICAgICAgZmlsdGVyLndpdGhTb3VyY2VEZXZpY2VzID0gY29uZmlnLnNob3dBbGFybXNGb3JDaGlsZHJlbiA/PyB0cnVlO1xuICAgIH1cbiAgICByZXR1cm4gZmlsdGVyO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4dHJhY3RzIGFuZCBjb25jYXRlbmF0ZXMgZmlsdGVyIHBhcmFtZXRlcnMgZnJvbSBhIGdpdmVuIG9iamVjdC5cbiAgICpcbiAgICogVGhpcyBmdW5jdGlvbiB0YWtlcyBhbiBvYmplY3QgY29udGFpbmluZyBmaWx0ZXIgc2V0dGluZ3MgKGVpdGhlciBTZXZlcml0eUZpbHRlclxuICAgKiBvciBBbGFybVN0YXR1c1NldHRpbmdzKSBhbmQgcmV0dXJucyBhIHN0cmluZyBvZiBhbGwga2V5cyB3aGVyZSB0aGUgY29ycmVzcG9uZGluZyB2YWx1ZSBpcyB0cnVlLlxuICAgKiBJZiB0aGUgb2JqZWN0IGlzIGVtcHR5IG9yIG51bGwsIGFuIGVtcHR5IHN0cmluZyBpcyByZXR1cm5lZC5cbiAgICpcbiAgICogQHBhcmFtIG9iaiAtIFRoZSBvYmplY3QgY29udGFpbmluZyBmaWx0ZXIgc2V0dGluZ3MuXG4gICAqIEByZXR1cm5zIC0gQSBjb25jYXRlbmF0ZWQgc3RyaW5nIG9mIGtleXMgd2l0aCB0cnVlIHZhbHVlcywgc2VwYXJhdGVkIGJ5IGNvbW1hcy5cbiAgICovXG4gIGV4dHJhY3RGaWx0ZXJQYXJhbXMob2JqOiBTZXZlcml0eUZpbHRlciB8IEFsYXJtU3RhdHVzU2V0dGluZ3MpOiBzdHJpbmcge1xuICAgIGlmICghb2JqKSB7XG4gICAgICByZXR1cm4gJyc7XG4gICAgfVxuICAgIHJldHVybiBPYmplY3QuZW50cmllcyhvYmopXG4gICAgICAuZmlsdGVyKChbLCB2YWx1ZV0pID0+IHZhbHVlKVxuICAgICAgLm1hcCgoW2tleV0pID0+IGtleSlcbiAgICAgIC5qb2luKCcsJyk7XG4gIH1cblxuICAvKipcbiAgICogRGV0ZXJtaW5lcyBpZiBhbiBpbmNvbWluZyByZWFsLXRpbWUgYWxhcm0gaGFzIGEgZGlmZmVyZW50IHN0YXR1cyB0aGFuIGFuIGV4aXN0aW5nIGFsYXJtLlxuICAgKlxuICAgKiBUaGlzIGZ1bmN0aW9uIGNoZWNrcyBpZiB0aGUgcHJvdmlkZWQgaW5jb21pbmcgcmVhbC10aW1lIGFsYXJtJ3Mgc3RhdHVzIGRpZmZlcnNcbiAgICogZnJvbSB0aGF0IG9mIGFuIGV4aXN0aW5nIGFsYXJtIHdpdGggdGhlIHNhbWUgSUQgaW4gdGhlIGdpdmVuIGFycmF5IG9mIGFsYXJtcy5cbiAgICpcbiAgICogQHBhcmFtIGV4aXN0aW5nQWxhcm1zIC0gVGhlIGFycmF5IG9mIGV4aXN0aW5nIGFsYXJtcy5cbiAgICogQHBhcmFtIGluY29taW5nUmVhbHRpbWVBbGFybSAtIFRoZSBpbmNvbWluZyByZWFsLXRpbWUgYWxhcm0gdG8gY2hlY2suXG4gICAqIEByZXR1cm5zIC0gVHJ1ZSBpZiB0aGUgZXhpc3RpbmcgYWxhcm0ncyBzdGF0dXMgaGFzIGNoYW5nZWQsIG90aGVyd2lzZSBmYWxzZS5cbiAgICovXG4gIGhhc0V4aXN0aW5nQWxhcm1DaGFuZ2VkU3RhdHVzKGV4aXN0aW5nQWxhcm1zOiBJQWxhcm1bXSwgaW5jb21pbmdSZWFsdGltZUFsYXJtOiBJQWxhcm0pOiBib29sZWFuIHtcbiAgICBjb25zdCBleGlzdGluZ0FsYXJtID0gZXhpc3RpbmdBbGFybXMuZmluZChhbGFybSA9PiBhbGFybS5pZCA9PT0gaW5jb21pbmdSZWFsdGltZUFsYXJtLmlkKTtcbiAgICByZXR1cm4gISFleGlzdGluZ0FsYXJtICYmIGV4aXN0aW5nQWxhcm0uc3RhdHVzICE9PSBpbmNvbWluZ1JlYWx0aW1lQWxhcm0uc3RhdHVzO1xuICB9XG5cbiAgLyoqXG4gICAqIEZpbHRlcnMgYWxhcm1zIGJhc2VkIG9uIHRoZWlyIHN0YXR1cywgc2V2ZXJpdHksIGFuZCB0eXBlLlxuICAgKlxuICAgKiBUaGlzIG1ldGhvZCBkZXRlcm1pbmVzIGlmIGEgZ2l2ZW4gYWxhcm0sIGlkZW50aWZpZWQgZWl0aGVyIGJ5IGEgbnVtZXJpYyBJRCBvciBhbiBgSUFsYXJtYCBvYmplY3QsXG4gICAqIG1hdGNoZXMgc3BlY2lmaWMgY3JpdGVyaWEgZGVmaW5lZCBpbiBgYWxhcm1zYCBhbmQgb3B0aW9uYWxseSBgY29uZmlnYC5cbiAgICpcbiAgICogQHBhcmFtIGFsYXJtIC0gVGhlIGFsYXJtIHRvIGNoZWNrLCByZXByZXNlbnRlZCBlaXRoZXIgYnkgYSBudW1lcmljIElEIG9yIGFuIGBJQWxhcm1gIG9iamVjdC5cbiAgICogQHBhcmFtIGFsYXJtcyAtIEFuIGFycmF5IG9mIGBJQWxhcm1gIG9iamVjdHMgYWdhaW5zdCB3aGljaCB0aGUgZ2l2ZW4gYWxhcm0gaXMgZXZhbHVhdGVkLlxuICAgKiBAcGFyYW0gY29uZmlnIC0gT3B0aW9uYWwuIENvbmZpZ3VyYXRpb24gZm9yIHRoZSBhbGFybSBsaXN0IHdpZGdldCwgdXNlZCB0byBkZWZpbmUgYWRkaXRpb25hbFxuICAgKiAgICAgICAgICAgICAgICAgZmlsdGVyaW5nIGNyaXRlcmlhLlxuICAgKlxuICAgKiBAcmV0dXJucyBgdHJ1ZWAgaWYgdGhlIGFsYXJtIG1hdGNoZXMgdGhlIHNwZWNpZmllZCBjcml0ZXJpYTsgb3RoZXJ3aXNlLCBgZmFsc2VgLlxuICAgKiAgICAgICAgICBJZiBgYWxhcm1gIGlzIGEgbnVtYmVyLCBpdCBhbHdheXMgcmV0dXJucyBgZmFsc2VgLlxuICAgKiAgICAgICAgICBJZiBgY29uZmlnYCBpcyBub3QgcHJvdmlkZWQsIGl0IHVzZXMgYSBsZWdhY3kgZmlsdGVyIGZvciBjcml0aWNhbCBhbGFybXMuXG4gICAqXG4gICAqIEByZW1hcmtzXG4gICAqIC0gV2hlbiBgYWxhcm1gIGlzIGEgbnVtZXJpYyBJRCwgdGhlIGZ1bmN0aW9uIHJldHVybnMgYGZhbHNlYCBhcyBpdCBjYW5ub3QgbWF0Y2ggYWdhaW5zdCB0eXBlIGFuZCBzZXZlcml0eS5cbiAgICogLSBJZiBgY29uZmlnYCBpcyBub3QgcHJvdmlkZWQsIHRoZSBmdW5jdGlvbiBhc3N1bWVzIGEgbGVnYWN5IHNjZW5hcmlvIGZvciBmaWx0ZXJpbmcgYWxsIGNyaXRpY2FsIGFsYXJtcy5cbiAgICovXG4gIGZpbHRlckFsYXJtc0J5U3RhdHVzU2V2ZXJpdHlBbmRUeXBlKFxuICAgIGFsYXJtOiBudW1iZXIgfCBJQWxhcm0sXG4gICAgYWxhcm1zOiBJQWxhcm1bXSxcbiAgICBjb25maWc/OiBBbGFybUxpc3RXaWRnZXRDb25maWdcbiAgKTogYm9vbGVhbiB7XG4gICAgaWYgKHR5cGVvZiBhbGFybSA9PT0gJ251bWJlcicpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5pc0FsYXJtTWF0Y2hlZEJ5Q29uZmlnKGFsYXJtLCBhbGFybXMsIGNvbmZpZyk7XG4gIH1cblxuICAvKipcbiAgICogRGV0ZXJtaW5lcyBpZiBhbGwgdmFsdWVzIGluIHRoZSBnaXZlbiBvYmplY3QgYXJlIGZhbHNlLlxuICAgKlxuICAgKiBUaGlzIGZ1bmN0aW9uIGNoZWNrcyBldmVyeSB2YWx1ZSBpbiB0aGUgcHJvdmlkZWQgb2JqZWN0IHRvIHNlZSBpZiB0aGV5IGFyZSBhbGwgZmFsc2UuXG4gICAqXG4gICAqIEBwYXJhbSBvYmogLSBBbiBvYmplY3Qgd2l0aCBib29sZWFuIHZhbHVlcy5cbiAgICogQHJldHVybnMgLSBSZXR1cm5zIGB0cnVlYCBpZiBhbGwgdmFsdWVzIGluIHRoZSBvYmplY3QgYXJlIGZhbHNlLCBvdGhlcndpc2UgYGZhbHNlYC5cbiAgICovXG4gIGFsbFZhbHVlc0ZhbHNlKG9iajogeyBba2V5OiBzdHJpbmddOiBib29sZWFuIH0pOiBib29sZWFuIHtcbiAgICByZXR1cm4gT2JqZWN0LnZhbHVlcyhvYmopLmV2ZXJ5KHZhbHVlID0+ICF2YWx1ZSk7XG4gIH1cblxuICAvKipcbiAgICogQ29uc3RydWN0cyBhIHN0cmluZyBvZiBvcmRlciBwYXJhbWV0ZXJzIGZvciBhIHF1ZXJ5IGJhc2VkIG9uIHRoZSBzcGVjaWZpZWQgYWxhcm0gb3JkZXIuXG4gICAqXG4gICAqIFRoaXMgZnVuY3Rpb24gdGFrZXMgYW4gYWxhcm0gb3JkZXIgYW5kIG1hcHMgaXQgdG8gYSBjb3JyZXNwb25kaW5nIHNldCBvZiBvcmRlciBwYXJhbWV0ZXJzLlxuICAgKiBJdCBzdXBwb3J0cyBkaWZmZXJlbnQgb3JkZXJpbmcgdHlwZXMsIHN1Y2ggYXMgQllfQUNUSVZFLCBCWV9TRVZFUklUWSwgYW5kIEJZX0RBVEVfQVNDRU5ESU5HLlxuICAgKiBUaGUgb3JkZXIgcGFyYW1ldGVycyBhcmUgdXNlZCB0byBjb25zdHJ1Y3QgYSBxdWVyeSBzdHJpbmcgdGhhdCBkZXRlcm1pbmVzIHRoZSBvcmRlclxuICAgKiBpbiB3aGljaCBhbGFybXMgYXJlIHJldHJpZXZlZCBvciBkaXNwbGF5ZWQuXG4gICAqXG4gICAqIEBwYXJhbSBvcmRlciAtIFRoZSBzcGVjaWZpZWQgb3JkZXIgZm9yIHNvcnRpbmcgYWxhcm1zIChlLmcuLCBCWV9BQ1RJVkUpLlxuICAgKiBAcmV0dXJucyAtIEEgc3RyaW5nIG9mIG9yZGVyIHBhcmFtZXRlcnMgdG8gYmUgdXNlZCBpbiBhIHF1ZXJ5LCBvciBhbiBlbXB0eSBzdHJpbmcgaWYgdGhlIG9yZGVyIHR5cGUgaXMgdW5yZWNvZ25pemVkLlxuICAgKi9cbiAgZ2V0T3JkZXJQYXJhbWV0ZXJzKG9yZGVyOiBBbGFybU9yZGVyVHlwZSk6IHN0cmluZyB7XG4gICAgbGV0IG9yZGVyUGFyYW1zOiBzdHJpbmdbXTtcblxuICAgIHN3aXRjaCAob3JkZXIpIHtcbiAgICAgIGNhc2UgQUxBUk1fT1JERVJfVkFMVUVTLkJZX0FDVElWRTpcbiAgICAgICAgb3JkZXJQYXJhbXMgPSBbJ3N0YXR1cyBhc2MnLCAnc2V2ZXJpdHkgYXNjJywgJ3RpbWUuZGF0ZSBkZXNjJywgJ3RleHQgYXNjJ107XG4gICAgICAgIHJldHVybiB0aGlzLmJ1aWxkT3JkZXJQYXJhbWV0ZXJzKG9yZGVyUGFyYW1zKTtcbiAgICAgIGNhc2UgQUxBUk1fT1JERVJfVkFMVUVTLkJZX1NFVkVSSVRZOlxuICAgICAgICBvcmRlclBhcmFtcyA9IFsnc2V2ZXJpdHkgYXNjJywgJ3RpbWUuZGF0ZSBkZXNjJywgJ3RleHQgYXNjJ107XG4gICAgICAgIHJldHVybiB0aGlzLmJ1aWxkT3JkZXJQYXJhbWV0ZXJzKG9yZGVyUGFyYW1zKTtcbiAgICAgIGNhc2UgQUxBUk1fT1JERVJfVkFMVUVTLkJZX0RBVEVfQVNDRU5ESU5HOlxuICAgICAgICBvcmRlclBhcmFtcyA9IFsndGltZS5kYXRlIGFzYycsICd0ZXh0IGFzYyddO1xuICAgICAgICByZXR1cm4gdGhpcy5idWlsZE9yZGVyUGFyYW1ldGVycyhvcmRlclBhcmFtcyk7XG4gICAgICBkZWZhdWx0OlxuICAgICAgICBvcmRlclBhcmFtcyA9IFsndGltZS5kYXRlIGRlc2MnLCAndGV4dCBhc2MnXTtcbiAgICAgICAgcmV0dXJuIHRoaXMuYnVpbGRPcmRlclBhcmFtZXRlcnMob3JkZXJQYXJhbXMpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBEZXRlcm1pbmVzIGlmIGFuIGFsYXJtIGlzIG1hdGNoZWQgYnkgdGhlIHNwZWNpZmllZCB3aWRnZXQgY29uZmlndXJhdGlvbi5cbiAgICpcbiAgICogVGhpcyBmdW5jdGlvbiBldmFsdWF0ZXMgd2hldGhlciBhIGdpdmVuIGFsYXJtIHNob3VsZCBiZSBpbmNsdWRlZCBiYXNlZCBvbiB0aGUgc2V2ZXJpdHksXG4gICAqIHN0YXR1cywgYW5kIHR5cGUgZmlsdGVycyBkZWZpbmVkIGluIHRoZSBBbGFybUxpc3RXaWRnZXRDb25maWcuIEl0IGNoZWNrcyBpZiB0aGUgYWxhcm0nc1xuICAgKiBzZXZlcml0eSBhbmQgc3RhdHVzIG1hdGNoIHRoZSBjb25maWd1cmF0aW9uIHNldHRpbmdzIGFuZCBpZiB0aGUgYWxhcm0ncyB0eXBlIGlzIGluY2x1ZGVkXG4gICAqIGluIHRoZSBjb25maWd1cmF0aW9uJ3MgdHlwZXMgKGlmIHNwZWNpZmllZCkuXG4gICAqXG4gICAqIEBpZ25vcmVcbiAgICogQHBhcmFtIGFsYXJtIC0gVGhlIGFsYXJtIHRvIGV2YWx1YXRlLlxuICAgKiBAcGFyYW0gYWxhcm1zIC0gQW4gYXJyYXkgb2YgZXhpc3RpbmcgYWxhcm1zLCB1c2VkIGZvciBzdGF0dXMgbWF0Y2hpbmcuXG4gICAqIEBwYXJhbSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBzZXR0aW5ncyB0byBtYXRjaCBhZ2FpbnN0LlxuICAgKiBAcmV0dXJucyAtIFJldHVybnMgYHRydWVgIGlmIHRoZSBhbGFybSBtYXRjaGVzIHRoZSBjb25maWd1cmF0aW9uIGNyaXRlcmlhOyBvdGhlcndpc2UsIGBmYWxzZWAuXG4gICAqL1xuICBpc0FsYXJtTWF0Y2hlZEJ5Q29uZmlnKGFsYXJtOiBJQWxhcm0sIGFsYXJtczogSUFsYXJtW10sIGNvbmZpZzogQWxhcm1MaXN0V2lkZ2V0Q29uZmlnKTogYm9vbGVhbiB7XG4gICAgY29uc3QgaXNTZXZlcml0eU1hdGNoZWQgPSB0aGlzLmlzU2V2ZXJpdHlNYXRjaGluZyhhbGFybS5zZXZlcml0eSwgY29uZmlnKTtcbiAgICBjb25zdCBpc1N0YXR1c01hdGNoZWQgPSB0aGlzLmlzU3RhdHVzTWF0Y2hpbmcoYWxhcm0sIGFsYXJtcywgY29uZmlnKTtcbiAgICBjb25zdCBpc1R5cGVNYXRjaGVkID0gdGhpcy5pc1R5cGVzTWF0Y2hpbmcoY29uZmlnLCBhbGFybSk7XG5cbiAgICByZXR1cm4gaXNTZXZlcml0eU1hdGNoZWQgJiYgaXNTdGF0dXNNYXRjaGVkICYmIGlzVHlwZU1hdGNoZWQ7XG4gIH1cbiAgLyoqXG4gICAqIENoZWNrcyBpZiB0aGUgc2V2ZXJpdHkgb2YgYW4gYWxhcm0gbWF0Y2hlcyB0aGUgY29uZmlndXJhdGlvbiBzZXR0aW5nLlxuICAgKlxuICAgKiBUaGlzIGZ1bmN0aW9uIGRldGVybWluZXMgd2hldGhlciB0aGUgc2V2ZXJpdHkgb2YgYW4gYWxhcm0gaXMgaW5jbHVkZWQgaW4gdGhlXG4gICAqIHNldmVyaXR5IHNldHRpbmdzIGRlZmluZWQgaW4gdGhlIEFsYXJtTGlzdFdpZGdldENvbmZpZy5cbiAgICpcbiAgICogQGlnbm9yZVxuICAgKiBAcGFyYW0gc2V2ZXJpdHkgLSBUaGUgc2V2ZXJpdHkgb2YgdGhlIGFsYXJtIHRvIGNoZWNrLlxuICAgKiBAcGFyYW0gY29uZmlnIC0gVGhlIGNvbmZpZ3VyYXRpb24gd2l0aCBzZXZlcml0eSBzZXR0aW5ncy5cbiAgICogQHJldHVybnMgLSBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgYWxhcm0ncyBzZXZlcml0eSBtYXRjaGVzIHRoZSBjb25maWd1cmF0aW9uOyBvdGhlcndpc2UsIGBmYWxzZWAuXG4gICAqL1xuICBpc1NldmVyaXR5TWF0Y2hpbmcoc2V2ZXJpdHk6IFNldmVyaXR5VHlwZSwgY29uZmlnOiBBbGFybUxpc3RXaWRnZXRDb25maWcpOiBib29sZWFuIHtcbiAgICByZXR1cm4gISFjb25maWcuc2V2ZXJpdGllc1tzZXZlcml0eV07XG4gIH1cblxuICAvKipcbiAgICogRXZhbHVhdGVzIGlmIHRoZSBzdGF0dXMgb2YgYW4gYWxhcm0gbWF0Y2hlcyB0aGUgY29uZmlndXJhdGlvbiBzZXR0aW5nIG9yIGhhcyBjaGFuZ2VkLlxuICAgKlxuICAgKiBUaGlzIGZ1bmN0aW9uIGNoZWNrcyBpZiB0aGUgc3RhdHVzIG9mIGFuIGFsYXJtIGlzIGluY2x1ZGVkIGluIHRoZSBzdGF0dXMgc2V0dGluZ3MgZGVmaW5lZCBpblxuICAgKiB0aGUgQWxhcm1MaXN0V2lkZ2V0Q29uZmlnLCBvciBpZiB0aGUgYWxhcm0ncyBzdGF0dXMgaGFzIGNoYW5nZWQgYmFzZWQgb24gdGhlIGV4aXN0aW5nIGFsYXJtcy5cbiAgICpcbiAgICogQGlnbm9yZVxuICAgKiBAcGFyYW0gYWxhcm0gLSBUaGUgYWxhcm0gd2hvc2Ugc3RhdHVzIGlzIHRvIGJlIGV2YWx1YXRlZC5cbiAgICogQHBhcmFtIGFsYXJtcyAtIEFuIGFycmF5IG9mIGV4aXN0aW5nIGFsYXJtcyB0byBjb21wYXJlIGFnYWluc3QgZm9yIHN0YXR1cyBjaGFuZ2VzLlxuICAgKiBAcGFyYW0gY29uZmlnIC0gVGhlIGNvbmZpZ3VyYXRpb24gd2l0aCBzdGF0dXMgc2V0dGluZ3MuXG4gICAqIEByZXR1cm5zIC0gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIGFsYXJtJ3Mgc3RhdHVzIG1hdGNoZXMgb3IgaGFzIGNoYW5nZWQgYXMgcGVyIHRoZSBjb25maWd1cmF0aW9uOyBvdGhlcndpc2UsIGBmYWxzZWAuXG4gICAqL1xuICBpc1N0YXR1c01hdGNoaW5nKGFsYXJtOiBJQWxhcm0sIGFsYXJtczogSUFsYXJtW10sIGNvbmZpZzogQWxhcm1MaXN0V2lkZ2V0Q29uZmlnKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuICEhY29uZmlnLnN0YXR1c1thbGFybS5zdGF0dXNdIHx8IHRoaXMuaGFzRXhpc3RpbmdBbGFybUNoYW5nZWRTdGF0dXMoYWxhcm1zLCBhbGFybSk7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2tzIGlmIHRoZSBjb25maWd1cmF0aW9uJ3MgdHlwZXMgYXJyYXkgY29udGFpbnMgb25seSBlbXB0eSBzdHJpbmcgb3IgaW5jbHVkZXMgYSBzcGVjaWZpYyBhbGFybSB0eXBlLlxuICAgKlxuICAgKiBAcGFyYW0gY29uZmlnIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHdpdGggYSBgdHlwZXNgIHByb3BlcnR5LlxuICAgKiBAcGFyYW0gYWxhcm0gLSBUaGUgYWxhcm0gb2JqZWN0IHdpdGggYSBgdHlwZWAgcHJvcGVydHkgdG8gY2hlY2sgYWdhaW5zdCB0aGUgY29uZmlnJ3MgdHlwZXMuXG4gICAqIEByZXR1cm5zIGB0cnVlYCBpZiB0aGUgY29uZmlnJ3MgdHlwZXMgYXJyYXkgY29udGFpbnMgb25seSBlbXB0eSBzdHJpbmcgb3IgaW5jbHVkZXMgdGhlIGFsYXJtJ3MgdHlwZSwgb3RoZXJ3aXNlIGBmYWxzZWAuXG4gICAqL1xuICBpc1R5cGVzTWF0Y2hpbmcoY29uZmlnOiBBbGFybUxpc3RXaWRnZXRDb25maWcsIGFsYXJtOiBJQWxhcm0pOiBib29sZWFuIHtcbiAgICByZXR1cm4gQXJyYXkuaXNBcnJheShjb25maWcudHlwZXMpICYmIGNvbmZpZy50eXBlcy5sZW5ndGggPT09IDEgJiYgY29uZmlnLnR5cGVzWzBdID09PSAnJ1xuICAgICAgPyB0cnVlXG4gICAgICA6IGNvbmZpZy50eXBlcz8uaW5jbHVkZXMoYWxhcm0udHlwZSkgPz8gZmFsc2U7XG4gIH1cblxuICAvKipcbiAgICogQ29uc3RydWN0cyBhIHF1ZXJ5IHN0cmluZyBmcm9tIGFuIGFycmF5IG9mIG9yZGVyIHBhcmFtZXRlcnMuXG4gICAqXG4gICAqIFRoaXMgZnVuY3Rpb24gdGFrZXMgYW4gYXJyYXkgb2Ygb3JkZXJpbmcgcGFyYW1ldGVycyBhbmQgY29uc3RydWN0cyBhIHF1ZXJ5IHN0cmluZ1xuICAgKiBmb3IgdXNlIGluIGFsYXJtIG9yZGVyaW5nIHF1ZXJpZXMuIFRoZSBwYXJhbWV0ZXJzIGFyZSBjb25jYXRlbmF0ZWQgaW50byBhIHNpbmdsZSBzdHJpbmcsXG4gICAqIHByZWZpeGVkIHdpdGggJyRvcmRlcmJ5PScuXG4gICAqXG4gICAqIEBpZ25vcmVcbiAgICogQHByaXZhdGVcbiAgICogQHBhcmFtIG9yZGVyUGFyYW1zIC0gVGhlIG9yZGVyIHBhcmFtZXRlcnMgdG8gYmUgaW5jbHVkZWQgaW4gdGhlIHF1ZXJ5LlxuICAgKiBAcmV0dXJucyAtIEEgcXVlcnkgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgb3JkZXIgcGFyYW1ldGVycy5cbiAgICovXG4gIHByaXZhdGUgYnVpbGRPcmRlclBhcmFtZXRlcnMob3JkZXJQYXJhbXM6IHN0cmluZ1tdKTogc3RyaW5nIHtcbiAgICByZXR1cm4gYCRvcmRlcmJ5PSR7b3JkZXJQYXJhbXMuam9pbignLCcpfWA7XG4gIH1cbn1cbiJdfQ==