@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
285 lines • 42.8 kB
JavaScript
import { Injectable } from '@angular/core';
import { InventoryService } from '@c8y/client';
import { OptionsService, ServiceRegistry } from '@c8y/ngx-components';
import { latLng, latLngBounds } from 'leaflet';
import { get } from 'lodash-es';
import { combineLatest, defer, of } from 'rxjs';
import { map } from 'rxjs/operators';
import { ClusterSize, defaultLayer, defaultMapConfig, MapTenantOptionKeys } from './map.model';
import * as i0 from "@angular/core";
import * as i1 from "@c8y/client";
import * as i2 from "@c8y/ngx-components";
export class MapService {
/**
* Returns asset icon status for highest alarm severity found in device object.
* @param device Device that contains alarms information.
* @returns Status string according to alarm severity
*/
static getStatus(device) {
if (!device.c8y_ActiveAlarmsStatus) {
return 'text-muted';
}
if (device.c8y_ActiveAlarmsStatus.critical) {
return 'status critical';
}
if (device.c8y_ActiveAlarmsStatus.major) {
return 'status major';
}
if (device.c8y_ActiveAlarmsStatus.minor) {
return 'status minor';
}
if (device.c8y_ActiveAlarmsStatus.warning) {
return 'status warning';
}
return 'text-muted';
}
/**
* @ignore: Only DI.
*/
constructor(inventory, options, serviceRegistry) {
this.inventory = inventory;
this.options = options;
this.serviceRegistry = serviceRegistry;
/**
* The devices that are maximal displayed in one cluster.
*/
this.MAX_DEVICE_PER_CLUSTER = 200;
/**
* The count until the cluster is sized. There are a maximum of
* three clusters: 1, 4 or 16.
*/
this.CLUSTER_LEVEL_THRESHOLD = 500;
}
/**
* Returns the leaflet instance used by the cumulocity core.
*/
async getLeaflet() {
const originalLeflet = window.L;
const c8yLeafletInstance = (await import('leaflet')).default;
c8yLeafletInstance.noConflict();
window.L = originalLeflet;
return c8yLeafletInstance;
}
/**
* Verifies if a given managed object is a device with a position fragment.
* @param mo The given managed object.
*/
isPositionedDevice(mo) {
return !!(mo?.c8y_IsDevice && this.hasPosition(mo));
}
/**
* Verifies if a given managed object has a position fragment.
* @param mo The given managed object.
*/
hasPosition(mo) {
return mo?.c8y_Position;
}
getMapTileLayerProviders() {
const layerProviders = this.serviceRegistry.get('mapTileLayerHook');
return layerProviders;
}
getMapTileLayersFromHookedProviders$(layerProviders) {
if (!layerProviders.length) {
return of([]);
}
const layers = combineLatest(layerProviders.map(provider => provider.getMapTileLayers$()));
return layers.pipe(map(layers => layers.flat()));
}
/**
* Returns the layers available in this application.
* Layers are taken from plugins installed to this application.
* In case none of the plugins override the default layers, the default layers are also considered.
* @returns The layers.
*/
getMapTileLayers$() {
return defer(() => {
const layerProviders = this.getMapTileLayerProviders();
const overridesDefaultLayer = layerProviders.some(provider => provider.overridesDefaultLayer?.());
const layersFromProviders = defer(() => this.getMapTileLayersFromHookedProviders$(layerProviders));
if (overridesDefaultLayer) {
return layersFromProviders;
}
return combineLatest([this.getDefaultLayers(), layersFromProviders]).pipe(map(layers => {
return layers.flat();
}));
});
}
/**
* Returns the layers configured in the current platform via tenant options.
* @returns The layers. If not set in tenant options the default layers.
*/
getDefaultLayers() {
return this.getMapOption(MapTenantOptionKeys.LAYERS, this.options.mapLayers || [defaultLayer]);
}
/**
* Returns the map configuration configured on the tenant.
* @returns The configuration. If not set in tenant options the default configuration.
*/
getDefaultConfig() {
return this.getMapOption(MapTenantOptionKeys.CONFIG, this.options.mapConfig || defaultMapConfig);
}
/**
* Counts all managed objects in a given bound with a c8y_Position fragment.
* @param bound The lat lng bound to request the managed objects for.
* @param byGroupIdMO The group managed object of which direct children should be searched for.
* @returns The number of all position managed objects in the given bound (and group).
*/
async getPositionMOsFromBoundCount(bound, byGroupIdMO) {
return this.getPositionMOsFromBound(bound, byGroupIdMO, true);
}
async getPositionMOsFromBound(bound, byGroupIdMO, count = false) {
const { lat: latMin, lng: lngMinRaw } = bound.getSouthWest();
const { lat: latMax, lng: lngMaxRaw } = bound.getNorthEast();
const lngMin = lngMaxRaw - lngMinRaw > 360 ? -180 : this.normalizeLongitude(lngMinRaw);
const lngMax = lngMaxRaw - lngMinRaw > 360 ? 180 : this.normalizeLongitude(lngMaxRaw);
const byGroupIdFilter = byGroupIdMO
? `(bygroupid(${byGroupIdMO.id}) or id eq '${byGroupIdMO.id}') and `
: '';
let boundFilter = `$filter=${byGroupIdFilter}has(c8y_Position) and c8y_Position.lat gt ${latMin}d and c8y_Position.lat lt ${latMax}d`;
if (lngMin < lngMax) {
boundFilter = `${boundFilter} and c8y_Position.lng gt ${lngMin}d and c8y_Position.lng lt ${lngMax}d`;
}
else {
boundFilter = `${boundFilter} and (c8y_Position.lng gt ${lngMin}d or c8y_Position.lng lt ${lngMax}d)`;
}
const { paging, data } = await this.inventory.list({
pageSize: count ? 1 : this.MAX_DEVICE_PER_CLUSTER,
withTotalPages: count,
query: boundFilter
});
if (count) {
return paging.totalPages;
}
return data.map((pmo) => bound.contains(latLng(pmo.c8y_Position.lat, pmo.c8y_Position.lng))
? pmo
: this.denormalizePMO(pmo, bound));
}
/**
* Returns all devices with c8y_Position.
* @param pageSize The page size to return.
* @param count Switches to counting only.
* @returns All devices or the device count with a c8y_Position fragment.
*/
async getPositionDevices(pageSize = this.MAX_DEVICE_PER_CLUSTER, count) {
const { paging, data } = await this.inventory.list({
pageSize: count ? 1 : pageSize,
withTotalPages: !!count,
query: '$filter=has(c8y_Position) and has(c8y_IsDevice)'
});
if (count) {
return paging.totalPages;
}
return data;
}
/**
* Returns all managed object with a c8y_Position fragment.
* @param byGroupIdMO The group managed object of which direct children should be searched for.
* @param pageSize Defines how many results should be returned.
* @returns The managed objects with position.
*/
async getAllPositionMOs(byGroupIdMO, pageSize = 500) {
const filter = {
pageSize,
withTotalPages: true,
query: 'has(c8y_Position)'
};
if (byGroupIdMO) {
filter.query = `$filter=(bygroupid(${byGroupIdMO.id}) or id eq '${byGroupIdMO.id}') and has(c8y_Position)`;
}
const { paging, data, res } = await this.inventory.list(filter);
return {
res,
paging: paging,
data: data
};
}
/**
* Determines a rectangular geographical area based on the positions of all devices.
*
* @returns A [[LatLngBounds]] object fitting all devices' geo positions.
*/
async getAllDevicesBounds() {
const filter = (coord, order) => ({
pageSize: 1,
q: `$filter=has(c8y_Position) $orderby=c8y_Position.${coord} ${order}`
});
const filterReverse = (op, order) => ({
pageSize: 1,
q: `$filter=has(c8y_Position) and c8y_Position.lng ${op} 0d $orderby=c8y_Position.lng ${order}`
});
const [latMin, latMax, lngMin, lngMax, lngRevMin, lngRevMax] = await Promise.all([
this.inventory.list(filter('lat', 'asc')),
this.inventory.list(filter('lat', 'desc')),
this.inventory.list(filter('lng', 'asc')),
this.inventory.list(filter('lng', 'desc')),
this.inventory.list(filterReverse('gt', 'asc')),
this.inventory.list(filterReverse('lt', 'desc'))
]).then(result => result.map(r => get(r.data, '[0].c8y_Position')));
const shiftWorld = (lngRevMin?.lng ?? 0) - (lngRevMax?.lng ?? 0) > 180;
return latLngBounds(latLng(latMin?.lat, shiftWorld ? lngRevMin?.lng : lngMin?.lng), latLng(latMax?.lat, shiftWorld ? lngRevMax?.lng + 360 : lngMax?.lng));
}
/**
* Returns the cluster size for clustered maps. Counting the position MOs in a bounding
* and if it reach a threshold, returning a [[ClusterSize]].
* @param bound The bounding to check for cluster size.
* @returns The cluster size, can be NONE, FOUR or SIXTEEN.
*/
async getClusterSize(bound) {
const count = await this.getPositionMOsFromBoundCount(bound);
let clusterSize = ClusterSize.NONE;
if (count > this.CLUSTER_LEVEL_THRESHOLD) {
clusterSize = ClusterSize.SIXTEEN;
}
else if (count > this.MAX_DEVICE_PER_CLUSTER) {
clusterSize = ClusterSize.FOUR;
}
return clusterSize;
}
getMapOption(key, defaultValue) {
return defer(() => this.options.getTenantOption('configuration', key, defaultValue)).pipe(map(config => {
if (typeof config === 'string') {
console.error(`The tenant option for maps 'configuration.${key}' is not a valid JSON structure.`);
return defaultValue;
}
return config;
}));
}
/**
* Shifts longitudes received from Leaflet.js in the [-180 - k*360; 180 + k*360] rangewhen
* `noWrap` is enabled to the [-180; 180] range expected for values of the c8y_Position fragment.
*
* @param lng Longitude to shift.
* @returns Longitude value in the [-180; 180] range
*/
normalizeLongitude(lng) {
return ((((lng + 180) % 360) + 360) % 360) - 180;
}
/**
* Shifts longitudes in the [-180; 180] range expected for values of the c8y_Position fragment
* the the [-180 - k*360; 180 + k*360] range expected from Leaflet.js when `noWrap` is enabled.
*
* The method naively adds/subtracts 360 degrees to the original value until the position fits in the expected bounds.
*
* @param pmo A managed object with a `c8y_Position` fragment
* @param bounds The bounds where the position should fit
* @returns A managed object whose `c8y_Position`'s `lng` values has been shifted to fit in bounds
*/
denormalizePMO(pmo, bounds) {
let { lng } = pmo.c8y_Position;
const shiftFactor = lng > bounds.getEast() ? -1 : 1;
while (!bounds.contains(latLng(pmo.c8y_Position.lat, lng))) {
lng += shiftFactor * 360;
}
pmo.c8y_Position.lng = lng;
return pmo;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MapService, deps: [{ token: i1.InventoryService }, { token: i2.OptionsService }, { token: i2.ServiceRegistry }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MapService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MapService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}], ctorParameters: () => [{ type: i1.InventoryService }, { type: i2.OptionsService }, { type: i2.ServiceRegistry }] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFwLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9tYXAvbWFwLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMzQyxPQUFPLEVBQWtCLGdCQUFnQixFQUF1QixNQUFNLGFBQWEsQ0FBQztBQUNwRixPQUFPLEVBQUUsY0FBYyxFQUFFLGVBQWUsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBR3RFLE9BQU8sRUFBRSxNQUFNLEVBQUUsWUFBWSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQy9DLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDaEMsT0FBTyxFQUFFLGFBQWEsRUFBRSxLQUFLLEVBQWMsRUFBRSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQzVELE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNyQyxPQUFPLEVBQ0wsV0FBVyxFQUNYLFlBQVksRUFDWixnQkFBZ0IsRUFDaEIsbUJBQW1CLEVBRXBCLE1BQU0sYUFBYSxDQUFDOzs7O0FBS3JCLE1BQU0sT0FBTyxVQUFVO0lBQ3JCOzs7O09BSUc7SUFDSCxNQUFNLENBQUMsU0FBUyxDQUFDLE1BQTZCO1FBQzVDLElBQUksQ0FBQyxNQUFNLENBQUMsc0JBQXNCLEVBQUUsQ0FBQztZQUNuQyxPQUFPLFlBQVksQ0FBQztRQUN0QixDQUFDO1FBQ0QsSUFBSSxNQUFNLENBQUMsc0JBQXNCLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDM0MsT0FBTyxpQkFBaUIsQ0FBQztRQUMzQixDQUFDO1FBQ0QsSUFBSSxNQUFNLENBQUMsc0JBQXNCLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDeEMsT0FBTyxjQUFjLENBQUM7UUFDeEIsQ0FBQztRQUNELElBQUksTUFBTSxDQUFDLHNCQUFzQixDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ3hDLE9BQU8sY0FBYyxDQUFD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dXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IElNYW5hZ2VkT2JqZWN0LCBJbnZlbnRvcnlTZXJ2aWNlLCBJUmVzdWx0TGlzdCwgUGFnaW5nIH0gZnJvbSAnQGM4eS9jbGllbnQnO1xuaW1wb3J0IHsgT3B0aW9uc1NlcnZpY2UsIFNlcnZpY2VSZWdpc3RyeSB9IGZyb20gJ0BjOHkvbmd4LWNvbXBvbmVudHMnO1xuaW1wb3J0IHR5cGUgeyBNYXBEZWZhdWx0Q29uZmlnLCBNYXBUaWxlTGF5ZXIgfSBmcm9tICdAYzh5L29wdGlvbnMnO1xuaW1wb3J0IHR5cGUgKiBhcyBMIGZyb20gJ2xlYWZsZXQnO1xuaW1wb3J0IHsgbGF0TG5nLCBsYXRMbmdCb3VuZHMgfSBmcm9tICdsZWFmbGV0JztcbmltcG9ydCB7IGdldCB9IGZyb20gJ2xvZGFzaC1lcyc7XG5pbXBvcnQgeyBjb21iaW5lTGF0ZXN0LCBkZWZlciwgT2JzZXJ2YWJsZSwgb2YgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IG1hcCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7XG4gIENsdXN0ZXJTaXplLFxuICBkZWZhdWx0TGF5ZXIsXG4gIGRlZmF1bHRNYXBDb25maWcsXG4gIE1hcFRlbmFudE9wdGlvbktleXMsXG4gIFBvc2l0aW9uTWFuYWdlZE9iamVjdFxufSBmcm9tICcuL21hcC5tb2RlbCc7XG5cbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnXG59KVxuZXhwb3J0IGNsYXNzIE1hcFNlcnZpY2Uge1xuICAvKipcbiAgICogUmV0dXJucyBhc3NldCBpY29uIHN0YXR1cyBmb3IgaGlnaGVzdCBhbGFybSBzZXZlcml0eSBmb3VuZCBpbiBkZXZpY2Ugb2JqZWN0LlxuICAgKiBAcGFyYW0gZGV2aWNlIERldmljZSB0aGF0IGNvbnRhaW5zIGFsYXJtcyBpbmZvcm1hdGlvbi5cbiAgICogQHJldHVybnMgU3RhdHVzIHN0cmluZyBhY2NvcmRpbmcgdG8gYWxhcm0gc2V2ZXJpdHlcbiAgICovXG4gIHN0YXRpYyBnZXRTdGF0dXMoZGV2aWNlOiBQb3NpdGlvbk1hbmFnZWRPYmplY3QpIHtcbiAgICBpZiAoIWRldmljZS5jOHlfQWN0aXZlQWxhcm1zU3RhdHVzKSB7XG4gICAgICByZXR1cm4gJ3RleHQtbXV0ZWQnO1xuICAgIH1cbiAgICBpZiAoZGV2aWNlLmM4eV9BY3RpdmVBbGFybXNTdGF0dXMuY3JpdGljYWwpIHtcbiAgICAgIHJldHVybiAnc3RhdHVzIGNyaXRpY2FsJztcbiAgICB9XG4gICAgaWYgKGRldmljZS5jOHlfQWN0aXZlQWxhcm1zU3RhdHVzLm1ham9yKSB7XG4gICAgICByZXR1cm4gJ3N0YXR1cyBtYWpvcic7XG4gICAgfVxuICAgIGlmIChkZXZpY2UuYzh5X0FjdGl2ZUFsYXJtc1N0YXR1cy5taW5vcikge1xuICAgICAgcmV0dXJuICdzdGF0dXMgbWlub3InO1xuICAgIH1cbiAgICBpZiAoZGV2aWNlLmM4eV9BY3RpdmVBbGFybXNTdGF0dXMud2FybmluZykge1xuICAgICAgcmV0dXJuICdzdGF0dXMgd2FybmluZyc7XG4gICAgfVxuICAgIHJldHVybiAndGV4dC1tdXRlZCc7XG4gIH1cbiAgLyoqXG4gICAqIFRoZSBkZXZpY2VzIHRoYXQgYXJlIG1heGltYWwgZGlzcGxheWVkIGluIG9uZSBjbHVzdGVyLlxuICAgKi9cbiAgTUFYX0RFVklDRV9QRVJfQ0xVU1RFUiA9IDIwMDtcbiAgLyoqXG4gICAqIFRoZSBjb3VudCB1bnRpbCB0aGUgY2x1c3RlciBpcyBzaXplZC4gVGhlcmUgYXJlIGEgbWF4aW11bSBvZlxuICAgKiB0aHJlZSBjbHVzdGVyczogMSwgNCBvciAxNi5cbiAgICovXG4gIENMVVNURVJfTEVWRUxfVEhSRVNIT0xEID0gNTAwO1xuXG4gIC8qKlxuICAgKiBAaWdub3JlOiBPbmx5IERJLlxuICAgKi9cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBpbnZlbnRvcnk6IEludmVudG9yeVNlcnZpY2UsXG4gICAgcHJpdmF0ZSBvcHRpb25zOiBPcHRpb25zU2VydmljZSxcbiAgICBwcml2YXRlIHNlcnZpY2VSZWdpc3RyeTogU2VydmljZVJlZ2lzdHJ5XG4gICkge31cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgbGVhZmxldCBpbnN0YW5jZSB1c2VkIGJ5IHRoZSBjdW11bG9jaXR5IGNvcmUuXG4gICAqL1xuICBhc3luYyBnZXRMZWFmbGV0KCkge1xuICAgIGNvbnN0IG9yaWdpbmFsTGVmbGV0ID0gd2luZG93Lkw7XG4gICAgY29uc3QgYzh5TGVhZmxldEluc3RhbmNlID0gKGF3YWl0IGltcG9ydCgnbGVhZmxldCcpKS5kZWZhdWx0O1xuICAgIGM4eUxlYWZsZXRJbnN0YW5jZS5ub0NvbmZsaWN0KCk7XG4gICAgd2luZG93LkwgPSBvcmlnaW5hbExlZmxldDtcbiAgICByZXR1cm4gYzh5TGVhZmxldEluc3RhbmNlO1xuICB9XG5cbiAgLyoqXG4gICAqIFZlcmlmaWVzIGlmIGEgZ2l2ZW4gbWFuYWdlZCBvYmplY3QgaXMgYSBkZXZpY2Ugd2l0aCBhIHBvc2l0aW9uIGZyYWdtZW50LlxuICAgKiBAcGFyYW0gbW8gVGhlIGdpdmVuIG1hbmFnZWQgb2JqZWN0LlxuICAgKi9cbiAgaXNQb3NpdGlvbmVkRGV2aWNlKG1vOiBJTWFuYWdlZE9iamVjdCkge1xuICAgIHJldHVybiAhIShtbz8uYzh5X0lzRGV2aWNlICYmIHRoaXMuaGFzUG9zaXRpb24obW8pKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBWZXJpZmllcyBpZiBhIGdpdmVuIG1hbmFnZWQgb2JqZWN0IGhhcyBhIHBvc2l0aW9uIGZyYWdtZW50LlxuICAgKiBAcGFyYW0gbW8gVGhlIGdpdmVuIG1hbmFnZWQgb2JqZWN0LlxuICAgKi9cbiAgaGFzUG9zaXRpb24obW86IElNYW5hZ2VkT2JqZWN0KSB7XG4gICAgcmV0dXJuIG1vPy5jOHlfUG9zaXRpb247XG4gIH1cblxuICBnZXRNYXBUaWxlTGF5ZXJQcm92aWRlcnMoKTogQ3VtdWxvY2l0eVNlcnZpY2VSZWdpc3RyeS5NYXBUaWxlTGF5ZXJQcm92aWRlcltdIHtcbiAgICBjb25zdCBsYXllclByb3ZpZGVycyA9IHRoaXMuc2VydmljZVJlZ2lzdHJ5LmdldCgnbWFwVGlsZUxheWVySG9vaycpO1xuXG4gICAgcmV0dXJuIGxheWVyUHJvdmlkZXJzO1xuICB9XG5cbiAgZ2V0TWFwVGlsZUxheWVyc0Zyb21Ib29rZWRQcm92aWRlcnMkKFxuICAgIGxheWVyUHJvdmlkZXJzOiBDdW11bG9jaXR5U2VydmljZVJlZ2lzdHJ5Lk1hcFRpbGVMYXllclByb3ZpZGVyW11cbiAgKTogT2JzZXJ2YWJsZTxNYXBUaWxlTGF5ZXJbXT4ge1xuICAgIGlmICghbGF5ZXJQcm92aWRlcnMubGVuZ3RoKSB7XG4gICAgICByZXR1cm4gb2YoW10pO1xuICAgIH1cbiAgICBjb25zdCBsYXllcnMgPSBjb21iaW5lTGF0ZXN0KGxheWVyUHJvdmlkZXJzLm1hcChwcm92aWRlciA9PiBwcm92aWRlci5nZXRNYXBUaWxlTGF5ZXJzJCgpKSk7XG4gICAgcmV0dXJuIGxheWVycy5waXBlKG1hcChsYXllcnMgPT4gbGF5ZXJzLmZsYXQoKSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIGxheWVycyBhdmFpbGFibGUgaW4gdGhpcyBhcHBsaWNhdGlvbi5cbiAgICogTGF5ZXJzIGFyZSB0YWtlbiBmcm9tIHBsdWdpbnMgaW5zdGFsbGVkIHRvIHRoaXMgYXBwbGljYXRpb24uXG4gICAqIEluIGNhc2Ugbm9uZSBvZiB0aGUgcGx1Z2lucyBvdmVycmlkZSB0aGUgZGVmYXVsdCBsYXllcnMsIHRoZSBkZWZhdWx0IGxheWVycyBhcmUgYWxzbyBjb25zaWRlcmVkLlxuICAgKiBAcmV0dXJucyBUaGUgbGF5ZXJzLlxuICAgKi9cbiAgZ2V0TWFwVGlsZUxheWVycyQoKTogT2JzZXJ2YWJsZTxNYXBUaWxlTGF5ZXJbXT4ge1xuICAgIHJldHVybiBkZWZlcigoKSA9PiB7XG4gICAgICBjb25zdCBsYXllclByb3ZpZGVycyA9IHRoaXMuZ2V0TWFwVGlsZUxheWVyUHJvdmlkZXJzKCk7XG4gICAgICBjb25zdCBvdmVycmlkZXNEZWZhdWx0TGF5ZXIgPSBsYXllclByb3ZpZGVycy5zb21lKHByb3ZpZGVyID0+XG4gICAgICAgIHByb3ZpZGVyLm92ZXJyaWRlc0RlZmF1bHRMYXllcj8uKClcbiAgICAgICk7XG4gICAgICBjb25zdCBsYXllcnNGcm9tUHJvdmlkZXJzID0gZGVmZXIoKCkgPT5cbiAgICAgICAgdGhpcy5nZXRNYXBUaWxlTGF5ZXJzRnJvbUhvb2tlZFByb3ZpZGVycyQobGF5ZXJQcm92aWRlcnMpXG4gICAgICApO1xuICAgICAgaWYgKG92ZXJyaWRlc0RlZmF1bHRMYXllcikge1xuICAgICAgICByZXR1cm4gbGF5ZXJzRnJvbVByb3ZpZGVycztcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIGNvbWJpbmVMYXRlc3QoW3RoaXMuZ2V0RGVmYXVsdExheWVycygpLCBsYXllcnNGcm9tUHJvdmlkZXJzXSkucGlwZShcbiAgICAgICAgbWFwKGxheWVycyA9PiB7XG4gICAgICAgICAgcmV0dXJuIGxheWVycy5mbGF0KCk7XG4gICAgICAgIH0pXG4gICAgICApO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIGxheWVycyBjb25maWd1cmVkIGluIHRoZSBjdXJyZW50IHBsYXRmb3JtIHZpYSB0ZW5hbnQgb3B0aW9ucy5cbiAgICogQHJldHVybnMgVGhlIGxheWVycy4gSWYgbm90IHNldCBpbiB0ZW5hbnQgb3B0aW9ucyB0aGUgZGVmYXVsdCBsYXllcnMuXG4gICAqL1xuICBnZXREZWZhdWx0TGF5ZXJzKCk6IE9ic2VydmFibGU8TWFwVGlsZUxheWVyW10+IHtcbiAgICByZXR1cm4gdGhpcy5nZXRNYXBPcHRpb248TWFwVGlsZUxheWVyW10+KFxuICAgICAgTWFwVGVuYW50T3B0aW9uS2V5cy5MQVlFUlMsXG4gICAgICB0aGlzLm9wdGlvbnMubWFwTGF5ZXJzIHx8IFtkZWZhdWx0TGF5ZXJdXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBtYXAgY29uZmlndXJhdGlvbiBjb25maWd1cmVkIG9uIHRoZSB0ZW5hbnQuXG4gICAqIEByZXR1cm5zIFRoZSBjb25maWd1cmF0aW9uLiBJZiBub3Qgc2V0IGluIHRlbmFudCBvcHRpb25zIHRoZSBkZWZhdWx0IGNvbmZpZ3VyYXRpb24uXG4gICAqL1xuICBnZXREZWZhdWx0Q29uZmlnKCk6IE9ic2VydmFibGU8TWFwRGVmYXVsdENvbmZpZz4ge1xuICAgIHJldHVybiB0aGlzLmdldE1hcE9wdGlvbjxNYXBEZWZhdWx0Q29uZmlnPihcbiAgICAgIE1hcFRlbmFudE9wdGlvbktleXMuQ09ORklHLFxuICAgICAgdGhpcy5vcHRpb25zLm1hcENvbmZpZyB8fCBkZWZhdWx0TWFwQ29uZmlnXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb3VudHMgYWxsIG1hbmFnZWQgb2JqZWN0cyBpbiBhIGdpdmVuIGJvdW5kIHdpdGggYSBjOHlfUG9zaXRpb24gZnJhZ21lbnQuXG4gICAqIEBwYXJhbSBib3VuZCBUaGUgbGF0IGxuZyBib3VuZCB0byByZXF1ZXN0IHRoZSBtYW5hZ2VkIG9iamVjdHMgZm9yLlxuICAgKiBAcGFyYW0gYnlHcm91cElkTU8gVGhlIGdyb3VwIG1hbmFnZWQgb2JqZWN0IG9mIHdoaWNoIGRpcmVjdCBjaGlsZHJlbiBzaG91bGQgYmUgc2VhcmNoZWQgZm9yLlxuICAgKiBAcmV0dXJucyBUaGUgbnVtYmVyIG9mIGFsbCBwb3NpdGlvbiBtYW5hZ2VkIG9iamVjdHMgaW4gdGhlIGdpdmVuIGJvdW5kIChhbmQgZ3JvdXApLlxuICAgKi9cbiAgYXN5bmMgZ2V0UG9zaXRpb25NT3NGcm9tQm91bmRDb3VudChcbiAgICBib3VuZDogTC5MYXRMbmdCb3VuZHMsXG4gICAgYnlHcm91cElkTU8/OiBJTWFuYWdlZE9iamVjdFxuICApOiBQcm9taXNlPG51bWJlcj4ge1xuICAgIHJldHVybiB0aGlzLmdldFBvc2l0aW9uTU9zRnJvbUJvdW5kKGJvdW5kLCBieUdyb3VwSWRNTywgdHJ1ZSkgYXMgUHJvbWlzZTxudW1iZXI+O1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYWxsIG1hbmFnZWQgb2JqZWN0cyB3aXRoIGEgYzh5X1Bvc2l0aW9uIGZyYWdtZW50IGluIGEgY2VydGFpbiBib3VuZGFyeS5cbiAgICogQHBhcmFtIGJvdW5kIFRoZSBsYXQgbG5nIGJvdW5kIHRvIHJlcXVlc3QgdGhlIG1hbmFnZWQgb2JqZWN0cyBmb3IuXG4gICAqIEByZXR1cm5zIEFsbCBwb3NpdGlvbiBtYW5hZ2VkIG9iamVjdHMgaW4gdGhlIGdpdmVuIGJvdW5kLlxuICAgKi9cbiAgYXN5bmMgZ2V0UG9zaXRpb25NT3NGcm9tQm91bmQoYm91bmQ6IEwuTGF0TG5nQm91bmRzKTogUHJvbWlzZTxQb3NpdGlvbk1hbmFnZWRPYmplY3RbXT47XG4gIC8qKlxuICAgKiBSZXR1cm5zIGFsbCBtYW5hZ2VkIG9iamVjdHMgd2l0aCBhIGM4eV9Qb3NpdGlvbiBmcmFnbWVudCBpbiBhIGNlcnRhaW4gYm91bmRhcnkgdGhhdCBiZWxvbmdzIHRvIGEgY2VydGFpbiBncm91cC5cbiAgICogQHBhcmFtIGJvdW5kIFRoZSBsYXQgbG5nIGJvdW5kIHRvIHJlcXVlc3QgdGhlIG1hbmFnZWQgb2JqZWN0cyBmb3IuXG4gICAqIEBwYXJhbSBieUdyb3VwSWRNTyBUaGUgZ3JvdXAgbWFuYWdlZCBvYmplY3Qgb2Ygd2hpY2ggZGlyZWN0IGNoaWxkcmVuIHNob3VsZCBiZSBzZWFyY2hlZCBmb3IuXG4gICAqIEByZXR1cm5zIEFsbCBwb3NpdGlvbiBtYW5hZ2VkIG9iamVjdHMgaW4gdGhlIGdpdmVuIGJvdW5kIHRoYXQgYXJlIGNoaWxkcmVuIG9mIHRoZSBnaXZlbiBncm91cC5cbiAgICovXG4gIGFzeW5jIGdldFBvc2l0aW9uTU9zRnJvbUJvdW5kKFxuICAgIGJvdW5kOiBMLkxhdExuZ0JvdW5kcyxcbiAgICBieUdyb3VwSWRNTzogSU1hbmFnZWRPYmplY3RcbiAgKTogUHJvbWlzZTxQb3NpdGlvbk1hbmFnZWRPYmplY3RbXT47XG4gIC8qKlxuICAgKiBDb3VudHMgdGhlIG1hbmFnZWQgb2JqZWN0cyBpbiBhIGNlcnRhaW4gYm91bmRhcnkgYmVsb25naW5nIHRvIGEgZ3JvdXAuXG4gICAqIEBwYXJhbSBib3VuZCBUaGUgbGF0IGxuZyBib3VuZCB0byByZXF1ZXN0IHRoZSBtYW5hZ2VkIG9iamVjdHMgZm9yLlxuICAgKiBAcGFyYW0gYnlHcm91cElkTU8gVGhlIGdyb3VwIG1hbmFnZWQgb2JqZWN0IG9mIHdoaWNoIGRpcmVjdCBjaGlsZHJlbiBzaG91bGQgYmUgc2VhcmNoZWQgZm9yLlxuICAgKiBAcmV0dXJuIFRoZSBjb3VudCBvZiB0aGUgbWFuYWdlZCBvYmplY3RzLlxuICAgKi9cbiAgYXN5bmMgZ2V0UG9zaXRpb25NT3NGcm9tQm91bmQoXG4gICAgYm91bmQ6IEwuTGF0TG5nQm91bmRzLFxuICAgIGJ5R3JvdXBJZE1POiBJTWFuYWdlZE9iamVjdCxcbiAgICBjb3VudDogdHJ1ZVxuICApOiBQcm9taXNlPG51bWJlcj47XG4gIGFzeW5jIGdldFBvc2l0aW9uTU9zRnJvbUJvdW5kKFxuICAgIGJvdW5kOiBMLkxhdExuZ0JvdW5kcyxcbiAgICBieUdyb3VwSWRNTz86IElNYW5hZ2VkT2JqZWN0LFxuICAgIGNvdW50ID0gZmFsc2VcbiAgKTogUHJvbWlzZTxQb3NpdGlvbk1hbmFnZWRPYmplY3RbXSB8IG51bWJlcj4ge1xuICAgIGNvbnN0IHsgbGF0OiBsYXRNaW4sIGxuZzogbG5nTWluUmF3IH0gPSBib3VuZC5nZXRTb3V0aFdlc3QoKTtcbiAgICBjb25zdCB7IGxhdDogbGF0TWF4LCBsbmc6IGxuZ01heFJhdyB9ID0gYm91bmQuZ2V0Tm9ydGhFYXN0KCk7XG5cbiAgICBjb25zdCBsbmdNaW4gPSBsbmdNYXhSYXcgLSBsbmdNaW5SYXcgPiAzNjAgPyAtMTgwIDogdGhpcy5ub3JtYWxpemVMb25naXR1ZGUobG5nTWluUmF3KTtcbiAgICBjb25zdCBsbmdNYXggPSBsbmdNYXhSYXcgLSBsbmdNaW5SYXcgPiAzNjAgPyAxODAgOiB0aGlzLm5vcm1hbGl6ZUxvbmdpdHVkZShsbmdNYXhSYXcpO1xuXG4gICAgY29uc3QgYnlHcm91cElkRmlsdGVyID0gYnlHcm91cElkTU9cbiAgICAgID8gYChieWdyb3VwaWQoJHtieUdyb3VwSWRNTy5pZH0pIG9yIGlkIGVxICcke2J5R3JvdXBJZE1PLmlkfScpIGFuZCBgXG4gICAgICA6ICcnO1xuICAgIGxldCBib3VuZEZpbHRlciA9IGAkZmlsdGVyPSR7YnlHcm91cElkRmlsdGVyfWhhcyhjOHlfUG9zaXRpb24pIGFuZCBjOHlfUG9zaXRpb24ubGF0IGd0ICR7bGF0TWlufWQgYW5kIGM4eV9Qb3NpdGlvbi5sYXQgbHQgJHtsYXRNYXh9ZGA7XG5cbiAgICBpZiAobG5nTWluIDwgbG5nTWF4KSB7XG4gICAgICBib3VuZEZpbHRlciA9IGAke2JvdW5kRmlsdGVyfSBhbmQgYzh5X1Bvc2l0aW9uLmxuZyBndCAke2xuZ01pbn1kIGFuZCBjOHlfUG9zaXRpb24ubG5nIGx0ICR7bG5nTWF4fWRgO1xuICAgIH0gZWxzZSB7XG4gICAgICBib3VuZEZpbHRlciA9IGAke2JvdW5kRmlsdGVyfSBhbmQgKGM4eV9Qb3NpdGlvbi5sbmcgZ3QgJHtsbmdNaW59ZCBvciBjOHlfUG9zaXRpb24ubG5nIGx0ICR7bG5nTWF4fWQpYDtcbiAgICB9XG5cbiAgICBjb25zdCB7IHBhZ2luZywgZGF0YSB9ID0gYXdhaXQgdGhpcy5pbnZlbnRvcnkubGlzdCh7XG4gICAgICBwYWdlU2l6ZTogY291bnQgPyAxIDogdGhpcy5NQVhfREVWSUNFX1BFUl9DTFVTVEVSLFxuICAgICAgd2l0aFRvdGFsUGFnZXM6IGNvdW50LFxuICAgICAgcXVlcnk6IGJvdW5kRmlsdGVyXG4gICAgfSk7XG4gICAgaWYgKGNvdW50KSB7XG4gICAgICByZXR1cm4gcGFnaW5nLnRvdGFsUGFnZXM7XG4gICAgfVxuICAgIHJldHVybiBkYXRhLm1hcCgocG1vOiBQb3NpdGlvbk1hbmFnZWRPYmplY3QpID0+XG4gICAgICBib3VuZC5jb250YWlucyhsYXRMbmcocG1vLmM4eV9Qb3NpdGlvbi5sYXQsIHBtby5jOHlfUG9zaXRpb24ubG5nKSlcbiAgICAgICAgPyBwbW9cbiAgICAgICAgOiB0aGlzLmRlbm9ybWFsaXplUE1PKHBtbywgYm91bmQpXG4gICAgKSBhcyBQb3NpdGlvbk1hbmFnZWRPYmplY3RbXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGFsbCBkZXZpY2VzIHdpdGggYzh5X1Bvc2l0aW9uLlxuICAgKi9cbiAgYXN5bmMgZ2V0UG9zaXRpb25EZXZpY2VzKCk6IFByb21pc2U8UG9zaXRpb25NYW5hZ2VkT2JqZWN0W10+O1xuICAvKipcbiAgICogUmV0dXJucyBhbGwgZGV2aWNlcyB3aXRoIGM4eV9Qb3NpdGlvbi5cbiAgICogQHBhcmFtIHBhZ2VTaXplIFRoZSBwYWdlIHNpemUgdG8gcmV0dXJuLlxuICAgKi9cbiAgYXN5bmMgZ2V0UG9zaXRpb25EZXZpY2VzKHBhZ2VTaXplOiBudW1iZXIpOiBQcm9taXNlPFBvc2l0aW9uTWFuYWdlZE9iamVjdFtdPjtcbiAgLyoqXG4gICAqIFJldHVybnMgYWxsIGRldmljZXMgd2l0aCBjOHlfUG9zaXRpb24uXG4gICAqIEBwYXJhbSBwYWdlU2l6ZSBUaGUgcGFnZSBzaXplIHRvIHJldHVybi5cbiAgICogQHBhcmFtIGNvdW50IENvdW50aW5nIGlzIGRpc2FibGVkXG4gICAqL1xuICBhc3luYyBnZXRQb3NpdGlvbkRldmljZXMocGFnZVNpemU6IG51bWJlciwgY291bnQ6IGZhbHNlKTogUHJvbWlzZTxQb3NpdGlvbk1hbmFnZWRPYmplY3RbXT47XG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBudW1iZXIgb2YgYWxsIGRldmljZXMgd2l0aCBjOHlfUG9zaXRpb24uXG4gICAqIEBwYXJhbSBwYWdlU2l6ZSBUaGUgcGFnZSBzaXplIHRvIHJldHVybi5cbiAgICogQHBhcmFtIGNvdW50IENvdW50aW5nIGlzIGVuYWJsZWRcbiAgICovXG4gIGFzeW5jIGdldFBvc2l0aW9uRGV2aWNlcyhwYWdlU2l6ZTogbnVtYmVyLCBjb3VudDogdHJ1ZSk6IFByb21pc2U8bnVtYmVyPjtcbiAgLyoqXG4gICAqIFJldHVybnMgYWxsIGRldmljZXMgd2l0aCBjOHlfUG9zaXRpb24uXG4gICAqIEBwYXJhbSBwYWdlU2l6ZSBUaGUgcGFnZSBzaXplIHRvIHJldHVybi5cbiAgICogQHBhcmFtIGNvdW50IFN3aXRjaGVzIHRvIGNvdW50aW5nIG9ubHkuXG4gICAqIEByZXR1cm5zIEFsbCBkZXZpY2VzIG9yIHRoZSBkZXZpY2UgY291bnQgd2l0aCBhIGM4eV9Qb3NpdGlvbiBmcmFnbWVudC5cbiAgICovXG4gIGFzeW5jIGdldFBvc2l0aW9uRGV2aWNlcyhcbiAgICBwYWdlU2l6ZSA9IHRoaXMuTUFYX0RFVklDRV9QRVJfQ0xVU1RFUixcbiAgICBjb3VudD86IGJvb2xlYW5cbiAgKTogUHJvbWlzZTxudW1iZXIgfCBQb3NpdGlvbk1hbmFnZWRPYmplY3RbXT4ge1xuICAgIGNvbnN0IHsgcGFnaW5nLCBkYXRhIH0gPSBhd2FpdCB0aGlzLmludmVudG9yeS5saXN0KHtcbiAgICAgIHBhZ2VTaXplOiBjb3VudCA/IDEgOiBwYWdlU2l6ZSxcbiAgICAgIHdpdGhUb3RhbFBhZ2VzOiAhIWNvdW50LFxuICAgICAgcXVlcnk6ICckZmlsdGVyPWhhcyhjOHlfUG9zaXRpb24pIGFuZCBoYXMoYzh5X0lzRGV2aWNlKSdcbiAgICB9KTtcbiAgICBpZiAoY291bnQpIHtcbiAgICAgIHJldHVybiBwYWdpbmcudG90YWxQYWdlcztcbiAgICB9XG4gICAgcmV0dXJuIGRhdGEgYXMgUG9zaXRpb25NYW5hZ2VkT2JqZWN0W107XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBhbGwgbWFuYWdlZCBvYmplY3Qgd2l0aCBhIGM4eV9Qb3NpdGlvbiBmcmFnbWVudC5cbiAgICogQHBhcmFtIGJ5R3JvdXBJZE1PIFRoZSBncm91cCBtYW5hZ2VkIG9iamVjdCBvZiB3aGljaCBkaXJlY3QgY2hpbGRyZW4gc2hvdWxkIGJlIHNlYXJjaGVkIGZvci5cbiAgICogQHBhcmFtIHBhZ2VTaXplIERlZmluZXMgaG93IG1hbnkgcmVzdWx0cyBzaG91bGQgYmUgcmV0dXJuZWQuXG4gICAqIEByZXR1cm5zIFRoZSBtYW5hZ2VkIG9iamVjdHMgd2l0aCBwb3NpdGlvbi5cbiAgICovXG4gIGFzeW5jIGdldEFsbFBvc2l0aW9uTU9zKFxuICAgIGJ5R3JvdXBJZE1PPzogSU1hbmFnZWRPYmplY3QsXG4gICAgcGFnZVNpemUgPSA1MDBcbiAgKTogUHJvbWlzZTxJUmVzdWx0TGlzdDxQb3NpdGlvbk1hbmFnZWRPYmplY3Q+PiB7XG4gICAgY29uc3QgZmlsdGVyOiB7IHBhZ2VTaXplOiBudW1iZXI7IHdpdGhUb3RhbFBhZ2VzOiBib29sZWFuOyBxdWVyeTogc3RyaW5nIH0gPSB7XG4gICAgICBwYWdlU2l6ZSxcbiAgICAgIHdpdGhUb3RhbFBhZ2VzOiB0cnVlLFxuICAgICAgcXVlcnk6ICdoYXMoYzh5X1Bvc2l0aW9uKSdcbiAgICB9O1xuXG4gICAgaWYgKGJ5R3JvdXBJZE1PKSB7XG4gICAgICBmaWx0ZXIucXVlcnkgPSBgJGZpbHRlcj0oYnlncm91cGlkKCR7YnlHcm91cElkTU8uaWR9KSBvciBpZCBlcSAnJHtieUdyb3VwSWRNTy5pZH0nKSBhbmQgaGFzKGM4eV9Qb3NpdGlvbilgO1xuICAgIH1cblxuICAgIGNvbnN0IHsgcGFnaW5nLCBkYXRhLCByZXMgfSA9IGF3YWl0IHRoaXMuaW52ZW50b3J5Lmxpc3QoZmlsdGVyKTtcblxuICAgIHJldHVybiB7XG4gICAgICByZXMsXG4gICAgICBwYWdpbmc6IHBhZ2luZyBhcyBQYWdpbmc8UG9zaXRpb25NYW5hZ2VkT2JqZWN0PixcbiAgICAgIGRhdGE6IGRhdGEgYXMgUG9zaXRpb25NYW5hZ2VkT2JqZWN0W11cbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIERldGVybWluZXMgYSByZWN0YW5ndWxhciBnZW9ncmFwaGljYWwgYXJlYSBiYXNlZCBvbiB0aGUgcG9zaXRpb25zIG9mIGFsbCBkZXZpY2VzLlxuICAgKlxuICAgKiBAcmV0dXJucyBBIFtbTGF0TG5nQm91bmRzXV0gb2JqZWN0IGZpdHRpbmcgYWxsIGRldmljZXMnIGdlbyBwb3NpdGlvbnMuXG4gICAqL1xuICBhc3luYyBnZXRBbGxEZXZpY2VzQm91bmRzKCk6IFByb21pc2U8TC5MYXRMbmdCb3VuZHM+IHtcbiAgICBjb25zdCBmaWx0ZXIgPSAoY29vcmQ6ICdsYXQnIHwgJ2xuZycsIG9yZGVyOiAnYXNjJyB8ICdkZXNjJykgPT4gKHtcbiAgICAgIHBhZ2VTaXplOiAxLFxuICAgICAgcTogYCRmaWx0ZXI9aGFzKGM4eV9Qb3NpdGlvbikgJG9yZGVyYnk9Yzh5X1Bvc2l0aW9uLiR7Y29vcmR9ICR7b3JkZXJ9YFxuICAgIH0pO1xuXG4gICAgY29uc3QgZmlsdGVyUmV2ZXJzZSA9IChvcDogJ2x0JyB8ICdndCcsIG9yZGVyOiAnYXNjJyB8ICdkZXNjJykgPT4gKHtcbiAgICAgIHBhZ2VTaXplOiAxLFxuICAgICAgcTogYCRmaWx0ZXI9aGFzKGM4eV9Qb3NpdGlvbikgYW5kIGM4eV9Qb3NpdGlvbi5sbmcgJHtvcH0gMGQgJG9yZGVyYnk9Yzh5X1Bvc2l0aW9uLmxuZyAke29yZGVyfWBcbiAgICB9KTtcblxuICAgIGNvbnN0IFtsYXRNaW4sIGxhdE1heCwgbG5nTWluLCBsbmdNYXgsIGxuZ1Jldk1pbiwgbG5nUmV2TWF4XSA9IGF3YWl0IFByb21pc2UuYWxsKFtcbiAgICAgIHRoaXMuaW52ZW50b3J5Lmxpc3QoZmlsdGVyKCdsYXQnLCAnYXNjJykpLFxuICAgICAgdGhpcy5pbnZlbnRvcnkubGlzdChmaWx0ZXIoJ2xhdCcsICdkZXNjJykpLFxuICAgICAgdGhpcy5pbnZlbnRvcnkubGlzdChmaWx0ZXIoJ2xuZycsICdhc2MnKSksXG4gICAgICB0aGlzLmludmVudG9yeS5saXN0KGZpbHRlcignbG5nJywgJ2Rlc2MnKSksXG4gICAgICB0aGlzLmludmVudG9yeS5saXN0KGZpbHRlclJldmVyc2UoJ2d0JywgJ2FzYycpKSxcbiAgICAgIHRoaXMuaW52ZW50b3J5Lmxpc3QoZmlsdGVyUmV2ZXJzZSgnbHQnLCAnZGVzYycpKVxuICAgIF0pLnRoZW4ocmVzdWx0ID0+IHJlc3VsdC5tYXAociA9PiBnZXQoci5kYXRhLCAnWzBdLmM4eV9Qb3NpdGlvbicpKSk7XG5cbiAgICBjb25zdCBzaGlmdFdvcmxkID0gKGxuZ1Jldk1pbj8ubG5nID8/IDApIC0gKGxuZ1Jldk1heD8ubG5nID8/IDApID4gMTgwO1xuXG4gICAgcmV0dXJuIGxhdExuZ0JvdW5kcyhcbiAgICAgIGxhdExuZyhsYXRNaW4/LmxhdCwgc2hpZnRXb3JsZCA/IGxuZ1Jldk1pbj8ubG5nIDogbG5nTWluPy5sbmcpLFxuICAgICAgbGF0TG5nKGxhdE1heD8ubGF0LCBzaGlmdFdvcmxkID8gbG5nUmV2TWF4Py5sbmcgKyAzNjAgOiBsbmdNYXg/LmxuZylcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIGNsdXN0ZXIgc2l6ZSBmb3IgY2x1c3RlcmVkIG1hcHMuIENvdW50aW5nIHRoZSBwb3NpdGlvbiBNT3MgaW4gYSBib3VuZGluZ1xuICAgKiBhbmQgaWYgaXQgcmVhY2ggYSB0aHJlc2hvbGQsIHJldHVybmluZyBhIFtbQ2x1c3RlclNpemVdXS5cbiAgICogQHBhcmFtIGJvdW5kIFRoZSBib3VuZGluZyB0byBjaGVjayBmb3IgY2x1c3RlciBzaXplLlxuICAgKiBAcmV0dXJucyBUaGUgY2x1c3RlciBzaXplLCBjYW4gYmUgTk9ORSwgRk9VUiBvciBTSVhURUVOLlxuICAgKi9cbiAgYXN5bmMgZ2V0Q2x1c3RlclNpemUoYm91bmQ6IEwuTGF0TG5nQm91bmRzKSB7XG4gICAgY29uc3QgY291bnQgPSBhd2FpdCB0aGlzLmdldFBvc2l0aW9uTU9zRnJvbUJvdW5kQ291bnQoYm91bmQpO1xuICAgIGxldCBjbHVzdGVyU2l6ZSA9IENsdXN0ZXJTaXplLk5PTkU7XG4gICAgaWYgKGNvdW50ID4gdGhpcy5DTFVTVEVSX0xFVkVMX1RIUkVTSE9MRCkge1xuICAgICAgY2x1c3RlclNpemUgPSBDbHVzdGVyU2l6ZS5TSVhURUVOO1xuICAgIH0gZWxzZSBpZiAoY291bnQgPiB0aGlzLk1BWF9ERVZJQ0VfUEVSX0NMVVNURVIpIHtcbiAgICAgIGNsdXN0ZXJTaXplID0gQ2x1c3RlclNpemUuRk9VUjtcbiAgICB9XG4gICAgcmV0dXJuIGNsdXN0ZXJTaXplO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRNYXBPcHRpb248VD4oa2V5OiBNYXBUZW5hbnRPcHRpb25LZXlzLCBkZWZhdWx0VmFsdWU6IFQpIHtcbiAgICByZXR1cm4gZGVmZXIoKCkgPT5cbiAgICAgIHRoaXMub3B0aW9ucy5nZXRUZW5hbnRPcHRpb248VCB8IHN0cmluZz4oJ2NvbmZpZ3VyYXRpb24nLCBrZXksIGRlZmF1bHRWYWx1ZSlcbiAgICApLnBpcGUoXG4gICAgICBtYXAoY29uZmlnID0+IHtcbiAgICAgICAgaWYgKHR5cGVvZiBjb25maWcgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgY29uc29sZS5lcnJvcihcbiAgICAgICAgICAgIGBUaGUgdGVuYW50IG9wdGlvbiBmb3IgbWFwcyAnY29uZmlndXJhdGlvbi4ke2tleX0nIGlzIG5vdCBhIHZhbGlkIEpTT04gc3RydWN0dXJlLmBcbiAgICAgICAgICApO1xuICAgICAgICAgIHJldHVybiBkZWZhdWx0VmFsdWU7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGNvbmZpZztcbiAgICAgIH0pXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTaGlmdHMgbG9uZ2l0dWRlcyByZWNlaXZlZCBmcm9tIExlYWZsZXQuanMgaW4gdGhlIFstMTgwIC0gayozNjA7IDE4MCArIGsqMzYwXSByYW5nZXdoZW5cbiAgICogYG5vV3JhcGAgaXMgZW5hYmxlZCB0byB0aGUgWy0xODA7IDE4MF0gcmFuZ2UgZXhwZWN0ZWQgZm9yIHZhbHVlcyBvZiB0aGUgYzh5X1Bvc2l0aW9uIGZyYWdtZW50LlxuICAgKlxuICAgKiBAcGFyYW0gbG5nIExvbmdpdHVkZSB0byBzaGlmdC5cbiAgICogQHJldHVybnMgIExvbmdpdHVkZSB2YWx1ZSBpbiB0aGUgWy0xODA7IDE4MF0gcmFuZ2VcbiAgICovXG4gIHByaXZhdGUgbm9ybWFsaXplTG9uZ2l0dWRlKGxuZzogbnVtYmVyKTogbnVtYmVyIHtcbiAgICByZXR1cm4gKCgoKGxuZyArIDE4MCkgJSAzNjApICsgMzYwKSAlIDM2MCkgLSAxODA7XG4gIH1cblxuICAvKipcbiAgICogU2hpZnRzIGxvbmdpdHVkZXMgaW4gdGhlIFstMTgwOyAxODBdIHJhbmdlIGV4cGVjdGVkIGZvciB2YWx1ZXMgb2YgdGhlIGM4eV9Qb3NpdGlvbiBmcmFnbWVudFxuICAgKiB0aGUgdGhlIFstMTgwIC0gayozNjA7IDE4MCArIGsqMzYwXSByYW5nZSBleHBlY3RlZCBmcm9tIExlYWZsZXQuanMgd2hlbiBgbm9XcmFwYCBpcyBlbmFibGVkLlxuICAgKlxuICAgKiBUaGUgbWV0aG9kIG5haXZlbHkgYWRkcy9zdWJ0cmFjdHMgMzYwIGRlZ3JlZXMgdG8gdGhlIG9yaWdpbmFsIHZhbHVlIHVudGlsIHRoZSBwb3NpdGlvbiBmaXRzIGluIHRoZSBleHBlY3RlZCBib3VuZHMuXG4gICAqXG4gICAqIEBwYXJhbSBwbW8gQSBtYW5hZ2VkIG9iamVjdCB3aXRoIGEgYGM4eV9Qb3NpdGlvbmAgZnJhZ21lbnRcbiAgICogQHBhcmFtIGJvdW5kcyBUaGUgYm91bmRzIHdoZXJlIHRoZSBwb3NpdGlvbiBzaG91bGQgZml0XG4gICAqIEByZXR1cm5zIEEgbWFuYWdlZCBvYmplY3Qgd2hvc2UgYGM4eV9Qb3NpdGlvbmAncyBgbG5nYCB2YWx1ZXMgaGFzIGJlZW4gc2hpZnRlZCB0byBmaXQgaW4gYm91bmRzXG4gICAqL1xuICBwcml2YXRlIGRlbm9ybWFsaXplUE1PKFxuICAgIHBtbzogUG9zaXRpb25NYW5hZ2VkT2JqZWN0LFxuICAgIGJvdW5kczogTC5MYXRMbmdCb3VuZHNcbiAgKTogUG9zaXRpb25NYW5hZ2VkT2JqZWN0IHtcbiAgICBsZXQgeyBsbmcgfSA9IHBtby5jOHlfUG9zaXRpb247XG4gICAgY29uc3Qgc2hpZnRGYWN0b3IgPSBsbmcgPiBib3VuZHMuZ2V0RWFzdCgpID8gLTEgOiAxO1xuXG4gICAgd2hpbGUgKCFib3VuZHMuY29udGFpbnMobGF0TG5nKHBtby5jOHlfUG9zaXRpb24ubGF0LCBsbmcpKSkge1xuICAgICAgbG5nICs9IHNoaWZ0RmFjdG9yICogMzYwO1xuICAgIH1cblxuICAgIHBtby5jOHlfUG9zaXRpb24ubG5nID0gbG5nO1xuICAgIHJldHVybiBwbW87XG4gIH1cbn1cbiJdfQ==