@iotize/ionic
Version:
Iotize specific building blocks on top of @ionic/angular.
419 lines • 63 kB
JavaScript
import { Injectable, NgZone } from '@angular/core';
import { Platform } from '@ionic/angular';
import { BLEScanner, getIoTizeBleCordovaPlugin, getScanRecordsFromBytes, } from '@iotize/device-com-ble.cordova';
import { ReplaySubject, defer, pipe, throwError } from 'rxjs';
import { filter, map, share, switchMap } from 'rxjs/operators';
import { runInZone } from '../../../lib/rx-utility/run-in-zone';
import { checkManufacturerDataMatch, parseScanRecords, } from './manufacturer-data-filter';
import { WebBluetoothScanner, PeripheralAdapter, } from '@iotize/device-com-web-bluetooth.js';
import * as i0 from "@angular/core";
import * as i1 from "@ionic/angular";
export function filterUndefined() {
return pipe(filter((value) => value !== undefined));
}
export class BleService {
constructor(platform, ngZone) {
this.platform = platform;
this.ngZone = ngZone;
this.webBluetoothPeripheralAdapters = new Map();
this._results$ = new ReplaySubject();
this.results = this._results$.pipe(filterUndefined(), runInZone(this.ngZone), share());
this.resultsWithRecords = this._results$.pipe(filterUndefined(), map((devices) => {
return devices?.map((device) => {
if (!this.platform.is('cordova') && device.name) {
//Web Bluetooth
let adapter = this.getDeviceFromPeripheralAdapter(device.name);
if (adapter == undefined) {
adapter = new PeripheralAdapter(device);
this.webBluetoothPeripheralAdapters.set(device.name, adapter);
// set deviceAdvertisement listener
this.setWatchAdvertisement(adapter);
}
return {
address: adapter.id,
name: adapter.name,
rssi: adapter.rssi,
txPower: adapter.txPower,
records: getScanRecordsFromBytes(new Uint8Array(adapter.scanRecord || [])),
};
}
else {
device = device;
return {
address: device.address,
name: device.name,
rssi: device.rssi,
txPower: device.txPower,
records: getScanRecordsFromBytes(new Uint8Array(device.scanRecord || [])),
};
}
});
}));
if (this.platform.is('cordova'))
this.plugin = getIoTizeBleCordovaPlugin();
}
createNewScanner(options) {
this.clearPreviousScanner();
if (this.platform.is('cordova')) {
if (options != undefined && options.filters != undefined) {
this.bleScanner = new BLEScanner(undefined, {
filters: options.filters,
});
}
else {
this.bleScanner = new BLEScanner(undefined);
}
}
else {
let webBLEOptions = {
acceptAllDevices: true,
};
const filters = options?.filters?.reduce((acc, current) => {
if (current.services && current.services.length != 0) {
acc.push({
services: current.services,
});
}
const manufacturerDataFilter = current.manufacturerData?.reduce((acc, current) => {
if (current.companyIdentifier != undefined) {
let newFilter = {
companyIdentifier: this.companyIdentifierToNumber(current.companyIdentifier),
// dataPrefix: undefined,
// mask: undefined
};
if (current.dataPrefix) {
newFilter.dataPrefix = new Uint8Array(current.dataPrefix);
}
if (current.mask) {
newFilter.mask = new Uint8Array(current.mask);
}
acc.push(newFilter);
}
return acc;
}, []);
if (manufacturerDataFilter && manufacturerDataFilter?.length != 0) {
acc.push({
manufacturerData: manufacturerDataFilter,
});
}
return acc;
}, []);
if (filters != undefined && filters.length != 0) {
webBLEOptions = {
filters,
};
}
this.bleScanner = new WebBluetoothScanner({
requestDeviceOptions: webBLEOptions,
});
}
this.scannerSubscription = this.bleScanner.results.subscribe((results) => {
if (options) {
const manufacturerDataFilters = options.filters?.reduce((filters, current) => {
if (current.manufacturerData) {
filters.push(...current.manufacturerData);
}
return filters;
}, []);
if (manufacturerDataFilters?.length) {
results = results.filter((result) => {
const scanRecord = result.scanRecord;
if (!scanRecord?.length || manufacturerDataFilters.length === 0) {
return false;
}
const scanRecords = parseScanRecords(scanRecord);
return checkManufacturerDataMatch(scanRecords, manufacturerDataFilters);
});
}
}
this.emitNexResults(results);
});
return this.bleScanner;
}
emitNexResults(results) {
this.lastResults = results;
this._results$.next(results);
}
clearPreviousScanner() {
if (this.bleScanner) {
this.scannerSubscription?.unsubscribe();
this.emitNexResults(undefined);
this.bleScanner = undefined;
}
}
async startScan(options) {
const newScanner = this.createNewScanner(options);
return newScanner.start();
}
async stopScan() {
if (this.platform.is('cordova')) {
if (this.bleScanner == undefined) {
throw new Error('BLE Scanner not defined');
}
return this.bleScanner.stop();
}
else {
// // stop watch advertisements
for (let [id, adapter] of this.webBluetoothPeripheralAdapters) {
this.unsetWatchAdvertisement(adapter);
}
return this.bleScanner?.stop();
}
}
connectByName(bleDeviceName) {
if (this.platform.is('cordova')) {
if (this.plugin == undefined) {
return throwError(() => new Error('BLE plugin is not available'));
}
return this.plugin.connect(bleDeviceName).pipe(share());
}
else {
return defer(async () => {
const device = this.getDeviceFromPeripheralAdapter(bleDeviceName);
if (device) {
device.connect().then(() => {
device._peripheral.watchAdvertisements();
});
return device.stateChange;
}
throwError(() => new Error(`ConnectionError, can not find device ${bleDeviceName}`));
}).pipe(filterUndefined(), switchMap((v) => v));
}
}
async disconnectByName(bleDeviceName) {
if (this.platform.is('cordova')) {
if (this.plugin == undefined) {
throw new Error('BLE plugin is not available');
}
return this.plugin.disConnect(bleDeviceName);
}
else {
const device = await this.getDeviceFromPeripheralAdapterOrThrow(bleDeviceName);
// stop watch advertisements
this.unsetWatchAdvertisement(device);
await device.disconnect();
return 'OK';
}
}
async closeByName(bleDeviceName) {
if (this.platform.is('cordova')) {
if (this.plugin == undefined) {
throw new Error('BLE plugin is not available');
}
return this.plugin.close(bleDeviceName);
}
else {
// Nothing to do on web browser
return 'OK';
}
}
async readCharacteristic(deviceName, serviceId, characteristicId) {
if (this.platform.is('cordova')) {
if (this.plugin == undefined) {
throw new Error('BLE plugin is not available');
}
return this.plugin.characteristicReadValue(deviceName, serviceId, characteristicId);
}
else {
const characteristic = await this.getCharacteristicFromPeripheralAdapterPromise(deviceName, serviceId, characteristicId);
return characteristic.read();
}
}
async writeCharacteristic(deviceName, serviceId, characteristicId, data, withResponse) {
if (this.platform.is('cordova')) {
if (this.plugin == undefined) {
throw new Error('BLE plugin is not available');
}
let writePromise;
if (withResponse) {
writePromise = this.plugin.characteristicWrite(deviceName, serviceId, characteristicId, data);
}
else {
writePromise = this.plugin.characteristicWriteWithoutResponse(deviceName, serviceId, characteristicId, data);
}
return writePromise.then((_) => true);
}
else {
const characteristic = await this.getCharacteristicFromPeripheralAdapterPromise(deviceName, serviceId, characteristicId);
await characteristic.write(data, !!withResponse);
return true;
}
}
listenCharacteristicChange(deviceName, serviceId, characteristicId) {
if (this.platform.is('cordova')) {
if (this.plugin == undefined) {
return throwError(new Error('BLE plugin is not available'));
}
return this.plugin
.characteristicChanged(deviceName, serviceId, characteristicId)
.pipe(runInZone(this.ngZone)
// share(),
);
}
else {
return this.getCharacteristicFromPeripheralAdapterObservable(deviceName, serviceId, characteristicId).pipe(switchMap((char) => {
return char.data;
}), map((data) => data.data), runInZone(this.ngZone));
}
}
async discoverServices(deviceName) {
if (this.platform.is('cordova')) {
if (this.plugin == undefined) {
throw new Error('BLE plugin is not available');
}
return this.plugin.discoverServices(deviceName);
}
else {
const device = await this.getDeviceFromPeripheralAdapterOrThrow(deviceName);
if (!device._peripheral.gatt?.connected) {
throw new Error(`device ${deviceName} is not connected`);
}
const servicesAdapters = await device.discoverServices();
const services = [];
for (let serviceName in servicesAdapters) {
const adapter = servicesAdapters[serviceName];
if (adapter) {
const description = {
uuid: adapter.uuid,
};
const characteristicDescriptions = [];
for (let characteristic of await adapter.getCharacteristics()) {
const descriptors = await characteristic.getDescriptors();
characteristicDescriptions.push({
uuid: characteristic.uuid,
properties: characteristic.properties,
descriptors: descriptors.map((desc) => {
return { uuid: desc.uuid, value: desc.value };
}),
});
}
if (characteristicDescriptions.length != 0) {
description.characteristics = characteristicDescriptions;
}
services.push(description);
}
}
return services;
}
}
async startNotification(deviceName, serviceId, characteristicId) {
if (this.platform.is('cordova')) {
if (this.plugin == undefined) {
throw new Error('BLE plugin is not available');
}
return this.plugin.characteristicStartNotification(deviceName, serviceId, characteristicId);
}
else {
const characteristic = await this.getCharacteristicFromPeripheralAdapterPromise(deviceName, serviceId, characteristicId);
await characteristic.enableNotifications(true);
return 'OK';
}
}
async stopNotification(deviceName, serviceId, characteristicId) {
if (this.platform.is('cordova')) {
if (this.plugin == undefined) {
throw new Error('BLE plugin is not available');
}
return this.plugin
.characteristicStopNotification(deviceName, serviceId, characteristicId)
.then((r) => true);
}
else {
const characteristic = await this.getCharacteristicFromPeripheralAdapterPromise(deviceName, serviceId, characteristicId);
await characteristic.enableNotifications(false);
return true;
}
}
getDeviceFromPeripheralAdapter(deviceName) {
const device = this.webBluetoothPeripheralAdapters.get(deviceName);
if (device != undefined) {
return device;
}
//try with id
for (let [id, adapter] of this.webBluetoothPeripheralAdapters) {
if (adapter.id == deviceName) {
return adapter;
}
}
return undefined;
}
async getDeviceFromPeripheralAdapterOrThrow(deviceName) {
const device = this.getDeviceFromPeripheralAdapter(deviceName);
if (device) {
return device;
}
throw new Error(`Can not find device ${device}`);
}
async getDeviceServiceFromPeripheralAdapter(deviceName, serviceId) {
const device = await this.getDeviceFromPeripheralAdapterOrThrow(deviceName);
const service = await device.getService(serviceId);
if (service == undefined) {
throw new Error(`Can not find service ${serviceId} on device ${device}`);
}
return service;
}
getCharacteristicFromPeripheralAdapterObservable(deviceName, serviceId, characteristicId) {
return defer(() => {
return this.getCharacteristicFromPeripheralAdapterPromise(deviceName, serviceId, characteristicId);
}).pipe(filterUndefined());
}
async getCharacteristicFromPeripheralAdapterPromise(deviceName, serviceId, characteristicId) {
const service = await this.getDeviceServiceFromPeripheralAdapter(deviceName, serviceId);
if (service) {
return await service.getCharacteristic(characteristicId);
}
throw new Error(`Can not find characteristic ${characteristicId} on service ${serviceId} on device ${deviceName}`);
}
setWatchAdvertisement(adapter) {
console.log('setWatchAdvertisement', adapter.name);
adapter._peripheral.addEventListener('advertisementreceived', (event) => {
console.log('UPDATE ADVERTISEMENT');
console.log(event);
this.updateAdvertisementData(event, adapter);
}, true);
adapter.watchAdvertisementsAbortController = new AbortController();
adapter._peripheral.watchAdvertisements({
signal: adapter.watchAdvertisementsAbortController.signal,
});
// (adapter._peripheral as any).onadvertisementreceived = (event: any) => {
// console.log("UPDATE ADVERTISEMENT");
// console.log(event);
// }
}
unsetWatchAdvertisement(adapter) {
// adapter._peripheral.removeEventListener('advertisementreceived', null);
adapter.watchAdvertisementsAbortController?.abort();
}
updateAdvertisementData(event, adapter) {
adapter.rssi = event.rssi;
adapter.txPower = event.txPower;
if (event.manufacturerData) {
adapter.scanRecord = [];
for (let [key, value] of event.manufacturerData) {
adapter.scanRecord.push(0xff, (key & 0xff00) >> 8, key & 0xff);
adapter.scanRecord.push(...new Uint8Array(value));
}
}
this.emitNexResults(this.lastResults);
}
companyIdentifierToNumber(companyIdentifier) {
if (typeof companyIdentifier == 'number') {
return companyIdentifier;
}
else {
return new Array(companyIdentifier).reduce((acc, next, i) => {
acc += next[i] << i;
return acc;
}, 0);
}
}
}
/** @nocollapse */ BleService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: BleService, deps: [{ token: i1.Platform }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
/** @nocollapse */ BleService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: BleService, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: BleService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: function () { return [{ type: i1.Platform }, { type: i0.NgZone }]; } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmxlLXNlcnZpY2Uuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2lvdGl6ZS1pb25pYy9zcmMvbGliL3RhcC1zY2FubmVyL3RhcC1zY2FubmVyLWJsZS9ibGUtc2VydmljZS5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ25ELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUMxQyxPQUFPLEVBQ0wsVUFBVSxFQUVWLHlCQUF5QixFQUN6Qix1QkFBdUIsR0FDeEIsTUFBTSxnQ0FBZ0MsQ0FBQztBQUd4QyxPQUFPLEVBR0wsYUFBYSxFQUdiLEtBQUssRUFDTCxJQUFJLEVBQ0osVUFBVSxFQUVYLE1BQU0sTUFBTSxDQUFDO0FBQ2QsT0FBTyxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQy9ELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQztBQUNoRSxPQUFPLEVBRUwsMEJBQTBCLEVBQzFCLGdCQUFnQixHQUNqQixNQUFNLDRCQUE0QixDQUFDO0FBQ3BDLE9BQU8sRUFDTCxtQkFBbUIsRUFDbkIsaUJBQWlCLEdBRWxCLE1BQU0scUNBQXFDLENBQUM7OztBQWtDN0MsTUFBTSxVQUFVLGVBQWU7SUFJN0IsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxLQUFLLEtBQUssU0FBUyxDQUFDLENBR2pELENBQUM7QUFDSixDQUFDO0FBS0QsTUFBTSxPQUFPLFVBQVU7SUEwRHJCLFlBQW9CLFFBQWtCLEVBQVUsTUFBYztRQUExQyxhQUFRLEdBQVIsUUFBUSxDQUFVO1FBQVUsV0FBTSxHQUFOLE1BQU0sQ0FBUTtRQXREdEQsbUNBQThC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QUMsQ0FBQzthQUNuRDtTQUNGO1FBQ0QsSUFBSSxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDeEMsQ0FBQztJQUVPLHlCQUF5QixDQUMvQixpQkFBaUQ7UUFFakQsSUFBSSxPQUFPLGlCQUFpQixJQUFJLFFBQVEsRUFBRTtZQUN4QyxPQUFPLGlCQUFpQixDQUFDO1NBQzFCO2FBQU07WUFDTCxPQUFPLElBQUksS0FBSyxDQUFDLGlCQUFpQixDQUFDLENBQUMsTUFBTSxDQUFTLENBQUMsR0FBRyxFQUFFLElBQUksRUFBRSxDQUFDLEVBQUUsRUFBRTtnQkFDbEUsR0FBRyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ3BCLE9BQU8sR0FBRyxDQUFDO1lBQ2IsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO1NBQ1A7SUFDSCxDQUFDOzsySEF6aUJVLFVBQVU7K0hBQVYsVUFBVSxjQUZULE1BQU07NEZBRVAsVUFBVTtrQkFIdEIsVUFBVTttQkFBQztvQkFDVixVQUFVLEVBQUUsTUFBTTtpQkFDbkIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlLCBOZ1pvbmUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IFBsYXRmb3JtIH0gZnJvbSAnQGlvbmljL2FuZ3VsYXInO1xuaW1wb3J0IHtcbiAgQkxFU2Nhbm5lcixcbiAgQ29yZG92YUJMRVNjYW5SZXN1bHQsXG4gIGdldElvVGl6ZUJsZUNvcmRvdmFQbHVnaW4sXG4gIGdldFNjYW5SZWNvcmRzRnJvbUJ5dGVzLFxufSBmcm9tICdAaW90aXplL2RldmljZS1jb20tYmxlLmNvcmRvdmEnO1xuaW1wb3J0IHsgSW9UaXplQmxlQ29yZG92YVBsdWdpbiB9IGZyb20gJ0Bpb3RpemUvZGV2aWNlLWNvbS1ibGUuY29yZG92YS9saWIvaW90aXplLWJsZS1jb3Jkb3ZhLXBsdWdpbic7XG5pbXBvcnQgeyBEZXZpY2VTY2FubmVyIH0gZnJvbSAnQGlvdGl6ZS90YXAvc2Nhbm5lci9hcGknO1xuaW1wb3J0IHtcbiAgT2JzZXJ2YWJsZSxcbiAgT3BlcmF0b3JGdW5jdGlvbixcbiAgUmVwbGF5U3ViamVjdCxcbiAgU3Vic2NyaXB0aW9uLFxuICBVbmFyeUZ1bmN0aW9uLFxuICBkZWZlcixcbiAgcGlwZSxcbiAgdGhyb3dFcnJvcixcbiAgdGltZW91dFxufSBmcm9tICdyeGpzJztcbmltcG9ydCB7IGZpbHRlciwgbWFwLCBzaGFyZSwgc3dpdGNoTWFwIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgcnVuSW5ab25lIH0gZnJvbSAnLi4vLi4vLi4vbGliL3J4LXV0aWxpdHkvcnVuLWluLXpvbmUnO1xuaW1wb3J0IHtcbiAgV2ViQmx1ZXRvb3RoTWFudWZhY3R1cmVyRGF0YUZpbHRlcixcbiAgY2hlY2tNYW51ZmFjdHVyZXJEYXRhTWF0Y2gsXG4gIHBhcnNlU2NhblJlY29yZHMsXG59IGZyb20gJy4vbWFudWZhY3R1cmVyLWRhdGEtZmlsdGVyJztcbmltcG9ydCB7XG4gIFdlYkJsdWV0b290aFNjYW5uZXIsXG4gIFBlcmlwaGVyYWxBZGFwdGVyLFxuICBXZWJCbHVldG9vdGhEZXZpY2UsXG59IGZyb20gJ0Bpb3RpemUvZGV2aWNlLWNvbS13ZWItYmx1ZXRvb3RoLmpzJztcbmltcG9ydCB7XG4gIENoYXJhY3RlcmlzdGljRGVzY3JpcHRpb24sXG4gIFNlcnZpY2VEZXNjcmlwdGlvbixcbiAgU2VydmljZUxpc3QsXG59IGZyb20gJ0Bpb3RpemUvZGV2aWNlLWNvbS1ibGUuY29yZG92YS9saWIvZGVmaW5pdGlvbnMnO1xuXG5leHBvcnQgdHlwZSBCbGVTY2FubmVyT3B0aW9ucyA9IHtcbiAgZmlsdGVycz86IHtcbiAgICBzZXJ2aWNlcz86IHN0cmluZ1tdO1xuICAgIG1hbnVmYWN0dXJlckRhdGE/OiBXZWJCbHVldG9vdGhNYW51ZmFjdHVyZXJEYXRhRmlsdGVyW107XG4gIH1bXTtcbn07XG5cbmV4cG9ydCB0eXBlIHNjYW5uZXJSZXN1bHQgPSBDb3Jkb3ZhQkxFU2NhblJlc3VsdCB8IFdlYkJsdWV0b290aERldmljZTtcblxuZXhwb3J0IHR5cGUgQkxFU2NhblJlc3VsdFdpdGhSZWNvcmRzID0ge1xuICBhZGRyZXNzOiBzdHJpbmc7XG4gIG5hbWU6IHN0cmluZztcbiAgcnNzaT86IG51bWJlcjtcbiAgdHhQb3dlcj86IG51bWJlcjtcbiAgcmVjb3Jkcz86IHtcbiAgICB0eXBlOiBudW1iZXI7XG4gICAgZGF0YTogVWludDhBcnJheTtcbiAgfVtdO1xufTtcblxuZXhwb3J0IHR5cGUgUGVyaXBoZXJhbEFkYXB0ZXJXaXRoUmVjb3JkcyA9IFBlcmlwaGVyYWxBZGFwdGVyICYge1xuICByc3NpPzogbnVtYmVyO1xuICB0eFBvd2VyPzogbnVtYmVyO1xuICBzY2FuUmVjb3JkPzogbnVtYmVyW107XG4gIHdhdGNoQWR2ZXJ0aXNlbWVudHNBYm9ydENvbnRyb2xsZXI/OiBBYm9ydENvbnRyb2xsZXI7XG59O1xuXG5leHBvcnQgZnVuY3Rpb24gZmlsdGVyVW5kZWZpbmVkPFQ+KCk6IFVuYXJ5RnVuY3Rpb248XG4gIE9ic2VydmFibGU8VCB8IHVuZGVmaW5lZD4sXG4gIE9ic2VydmFibGU8VD5cbj4ge1xuICByZXR1cm4gcGlwZShmaWx0ZXIoKHZhbHVlKSA9PiB2YWx1ZSAhPT0gdW5kZWZpbmVkKSkgYXMgT3BlcmF0b3JGdW5jdGlvbjxcbiAgICBUIHwgdW5kZWZpbmVkLFxuICAgIFRcbiAgPjtcbn1cblxuQEluamVjdGFibGUoe1xuICBwcm92aWRlZEluOiAncm9vdCcsXG59KVxuZXhwb3J0IGNsYXNzIEJsZVNlcnZpY2Uge1xuICBwcml2YXRlIGJsZVNjYW5uZXI/OiBEZXZpY2VTY2FubmVyPHNjYW5uZXJSZXN1bHQ+O1xuICBwcml2YXRlIHNjYW5uZXJTdWJzY3JpcHRpb24/OiBTdWJzY3JpcHRpb247XG4gIHByaXZhdGUgcGx1Z2luPzogSW9UaXplQmxlQ29yZG92YVBsdWdpbjtcbiAgcHJpdmF0ZSB3ZWJCbHVldG9vdGhQZXJpcGhlcmFsQWRhcHRlcnM6IE1hcDxcbiAgICBzdHJpbmcsXG4gICAgUGVyaXBoZXJhbEFkYXB0ZXJXaXRoUmVjb3Jkc1xuICA+ID0gbmV3IE1hcDxzdHJpbmcsIFBlcmlwaGVyYWxBZGFwdGVyV2l0aFJlY29yZHM+KCk7XG5cbiAgcHJpdmF0ZSBsYXN0UmVzdWx0cz86IHNjYW5uZXJSZXN1bHRbXTtcbiAgcHJpdmF0ZSBfcmVzdWx0cyQgPSBuZXcgUmVwbGF5U3ViamVjdDxzY2FubmVyUmVzdWx0W10gfCB1bmRlZmluZWQ+KCk7XG5cbiAgcHVibGljIHJlYWRvbmx5IHJlc3VsdHM6IE9ic2VydmFibGU8c2Nhbm5lclJlc3VsdFtdPiA9IHRoaXMuX3Jlc3VsdHMkLnBpcGUoXG4gICAgZmlsdGVyVW5kZWZpbmVkKCksXG4gICAgcnVuSW5ab25lKHRoaXMubmdab25lKSxcbiAgICBzaGFyZSgpXG4gICk7XG5cbiAgcHVibGljIHJlYWRvbmx5IHJlc3VsdHNXaXRoUmVjb3JkczogT2JzZXJ2YWJsZTxCTEVTY2FuUmVzdWx0V2l0aFJlY29yZHNbXT4gPVxuICAgIHRoaXMuX3Jlc3VsdHMkLnBpcGUoXG4gICAgICBmaWx0ZXJVbmRlZmluZWQoKSxcbiAgICAgIG1hcCgoZGV2aWNlcykgPT4ge1xuICAgICAgICByZXR1cm4gZGV2aWNlcz8ubWFwKChkZXZpY2UpID0+IHtcbiAgICAgICAgICBpZiAoIXRoaXMucGxhdGZvcm0uaXMoJ2NvcmRvdmEnKSAmJiBkZXZpY2UubmFtZSkge1xuICAgICAgICAgICAgLy9XZWIgQmx1ZXRvb3RoXG4gICAgICAgICAgICBsZXQgYWRhcHRlciA9IHRoaXMuZ2V0RGV2aWNlRnJvbVBlcmlwaGVyYWxBZGFwdGVyKGRldmljZS5uYW1lKTtcbiAgICAgICAgICAgIGlmIChhZGFwdGVyID09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICBhZGFwdGVyID0gbmV3IFBlcmlwaGVyYWxBZGFwdGVyKGRldmljZSBhcyBCbHVldG9vdGhEZXZpY2UpO1xuICAgICAgICAgICAgICB0aGlzLndlYkJsdWV0b290aFBlcmlwaGVyYWxBZGFwdGVycy5zZXQoZGV2aWNlLm5hbWUsIGFkYXB0ZXIpO1xuICAgICAgICAgICAgICAvLyBzZXQgZGV2aWNlQWR2ZXJ0aXNlbWVudCBsaXN0ZW5lclxuICAgICAgICAgICAgICB0aGlzLnNldFdhdGNoQWR2ZXJ0aXNlbWVudChhZGFwdGVyKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgYWRkcmVzczogYWRhcHRlci5pZCxcbiAgICAgICAgICAgICAgbmFtZTogYWRhcHRlci5uYW1lIGFzIHN0cmluZyxcbiAgICAgICAgICAgICAgcnNzaTogYWRhcHRlci5yc3NpLFxuICAgICAgICAgICAgICB0eFBvd2VyOiBhZGFwdGVyLnR4UG93ZXIsXG4gICAgICAgICAgICAgIHJlY29yZHM6IGdldFNjYW5SZWNvcmRzRnJvbUJ5dGVzKFxuICAgICAgICAgICAgICAgIG5ldyBVaW50OEFycmF5KGFkYXB0ZXIuc2NhblJlY29yZCB8fCBbXSlcbiAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGRldmljZSA9IGRldmljZSBhcyBDb3Jkb3ZhQkxFU2NhblJlc3VsdDtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgIGFkZHJlc3M6IGRldmljZS5hZGRyZXNzLFxuICAgICAgICAgICAgICBuYW1lOiBkZXZpY2UubmFtZSxcbiAgICAgICAgICAgICAgcnNzaTogZGV2aWNlLnJzc2ksXG4gICAgICAgICAgICAgIHR4UG93ZXI6IGRldmljZS50eFBvd2VyLFxuICAgICAgICAgICAgICByZWNvcmRzOiBnZXRTY2FuUmVjb3Jkc0Zyb21CeXRlcyhcbiAgICAgICAgICAgICAgICBuZXcgVWludDhBcnJheShkZXZpY2Uuc2NhblJlY29yZCB8fCBbXSlcbiAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgIH0pXG4gICAgKTtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHBsYXRmb3JtOiBQbGF0Zm9ybSwgcHJpdmF0ZSBuZ1pvbmU6IE5nWm9uZSkge1xuICAgIGlmICh0aGlzLnBsYXRmb3JtLmlzKCdjb3Jkb3ZhJykpIHRoaXMucGx1Z2luID0gZ2V0SW9UaXplQmxlQ29yZG92YVBsdWdpbigpO1xuICB9XG5cbiAgY3JlYXRlTmV3U2Nhbm5lcihvcHRpb25zPzogQmxlU2Nhbm5lck9wdGlvbnMpIHtcbiAgICB0aGlzLmNsZWFyUHJldmlvdXNTY2FubmVyKCk7XG4gICAgaWYgKHRoaXMucGxhdGZvcm0uaXMoJ2NvcmRvdmEnKSkge1xuICAgICAgaWYgKG9wdGlvbnMgIT0gdW5kZWZpbmVkICYmIG9wdGlvbnMuZmlsdGVycyAhPSB1bmRlZmluZWQpIHtcbiAgICAgICAgdGhpcy5ibGVTY2FubmVyID0gbmV3IEJMRVNjYW5uZXIodW5kZWZpbmVkLCB7XG4gICAgICAgICAgZmlsdGVyczogb3B0aW9ucy5maWx0ZXJzLFxuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuYmxlU2Nhbm5lciA9IG5ldyBCTEVTY2FubmVyKHVuZGVmaW5lZCk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGxldCB3ZWJCTEVPcHRpb25zOiBSZXF1ZXN0RGV2aWNlT3B0aW9ucyA9IHtcbiAgICAgICAgYWNjZXB0QWxsRGV2aWNlczogdHJ1ZSxcbiAgICAgIH07XG5cbiAgICAgIGNvbnN0IGZpbHRlcnM6IEJsdWV0b290aExFU2NhbkZpbHRlcltdIHwgdW5kZWZpbmVkID1cbiAgICAgICAgb3B0aW9ucz8uZmlsdGVycz8ucmVkdWNlPEJsdWV0b290aExFU2NhbkZpbHRlcltdPigoYWNjLCBjdXJyZW50KSA9PiB7XG4gICAgICAgICAgaWYgKGN1cnJlbnQuc2VydmljZXMgJiYgY3VycmVudC5zZXJ2aWNlcy5sZW5ndGggIT0gMCkge1xuICAgICAgICAgICAgYWNjLnB1c2goe1xuICAgICAgICAgICAgICBzZXJ2aWNlczogY3VycmVudC5zZXJ2aWNlcyxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zdCBtYW51ZmFjdHVyZXJEYXRhRmlsdGVyID0gY3VycmVudC5tYW51ZmFjdHVyZXJEYXRhPy5yZWR1Y2U8XG4gICAgICAgICAgICBCbHVldG9vdGhNYW51ZmFjdHVyZXJEYXRhRmlsdGVyW11cbiAgICAgICAgICA+KChhY2MsIGN1cnJlbnQpID0+IHtcbiAgICAgICAgICAgIGlmIChjdXJyZW50LmNvbXBhbnlJZGVudGlmaWVyICE9IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICBsZXQgbmV3RmlsdGVyOiBhbnkgPSB7XG4gICAgICAgICAgICAgICAgY29tcGFueUlkZW50aWZpZXI6IHRoaXMuY29tcGFueUlkZW50aWZpZXJUb051bWJlcihcbiAgICAgICAgICAgICAgICAgIGN1cnJlbnQuY29tcGFueUlkZW50aWZpZXJcbiAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAgIC8vIGRhdGFQcmVmaXg6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICAvLyBtYXNrOiB1bmRlZmluZWRcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgaWYgKGN1cnJlbnQuZGF0YVByZWZpeCkge1xuICAgICAgICAgICAgICAgIG5ld0ZpbHRlci5kYXRhUHJlZml4ID0gbmV3IFVpbnQ4QXJyYXkoY3VycmVudC5kYXRhUHJlZml4KTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBpZiAoY3VycmVudC5tYXNrKSB7XG4gICAgICAgICAgICAgICAgbmV3RmlsdGVyLm1hc2sgPSBuZXcgVWludDhBcnJheShjdXJyZW50Lm1hc2spO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGFjYy5wdXNoKG5ld0ZpbHRlciBhcyBCbHVldG9vdGhNYW51ZmFjdHVyZXJEYXRhRmlsdGVyKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBhY2M7XG4gICAgICAgICAgfSwgW10pO1xuICAgICAgICAgIGlmIChtYW51ZmFjdHVyZXJEYXRhRmlsdGVyICYmIG1hbnVmYWN0dXJlckRhdGFGaWx0ZXI/Lmxlbmd0aCAhPSAwKSB7XG4gICAgICAgICAgICBhY2MucHVzaCh7XG4gICAgICAgICAgICAgIG1hbnVmYWN0dXJlckRhdGE6IG1hbnVmYWN0dXJlckRhdGFGaWx0ZXIsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIGFjYztcbiAgICAgICAgfSwgW10pO1xuXG4gICAgICBpZiAoZmlsdGVycyAhPSB1bmRlZmluZWQgJiYgZmlsdGVycy5sZW5ndGggIT0gMCkge1xuICAgICAgICB3ZWJCTEVPcHRpb25zID0ge1xuICAgICAgICAgIGZpbHRlcnMsXG4gICAgICAgIH07XG4gICAgICB9XG5cbiAgICAgIHRoaXMuYmxlU2Nhbm5lciA9IG5ldyBXZWJCbHVldG9vdGhTY2FubmVyKHtcbiAgICAgICAgcmVxdWVzdERldmljZU9wdGlvbnM6IHdlYkJMRU9wdGlvbnMsXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICB0aGlzLnNjYW5uZXJTdWJzY3JpcHRpb24gPSB0aGlzLmJsZVNjYW5uZXIucmVzdWx0cy5zdWJzY3JpYmUoKHJlc3VsdHMpID0+IHtcbiAgICAgIGlmIChvcHRpb25zKSB7XG4gICAgICAgIGNvbnN0IG1hbnVmYWN0dXJlckRhdGFGaWx0ZXJzID0gb3B0aW9ucy5maWx0ZXJzPy5yZWR1Y2U8XG4gICAgICAgICAgV2ViQmx1ZXRvb3RoTWFudWZhY3R1cmVyRGF0YUZpbHRlcltdXG4gICAgICAgID4oKGZpbHRlcnMsIGN1cnJlbnQpID0+IHtcbiAgICAgICAgICBpZiAoY3VycmVudC5tYW51ZmFjdHVyZXJEYXRhKSB7XG4gICAgICAgICAgICBmaWx0ZXJzLnB1c2goLi4uY3VycmVudC5tYW51ZmFjdHVyZXJEYXRhKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIGZpbHRlcnM7XG4gICAgICAgIH0sIFtdKTtcbiAgICAgICAgaWYgKG1hbnVmYWN0dXJlckRhdGFGaWx0ZXJzPy5sZW5ndGgpIHtcbiAgICAgICAgICByZXN1bHRzID0gcmVzdWx0cy5maWx0ZXIoKHJlc3VsdCkgPT4ge1xuICAgICAgICAgICAgY29uc3Qgc2NhblJlY29yZCA9IChyZXN1bHQgYXMgYW55KS5zY2FuUmVjb3JkO1xuICAgICAgICAgICAgaWYgKCFzY2FuUmVjb3JkPy5sZW5ndGggfHwgbWFudWZhY3R1cmVyRGF0YUZpbHRlcnMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IHNjYW5SZWNvcmRzID0gcGFyc2VTY2FuUmVjb3JkcyhzY2FuUmVjb3JkKTtcblxuICAgICAgICAgICAgcmV0dXJuIGNoZWNrTWFudWZhY3R1cmVyRGF0YU1hdGNoKFxuICAgICAgICAgICAgICBzY2FuUmVjb3JkcyxcbiAgICAgICAgICAgICAgbWFudWZhY3R1cmVyRGF0YUZpbHRlcnNcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHRoaXMuZW1pdE5leFJlc3VsdHMocmVzdWx0cyk7XG4gICAgfSk7XG4gICAgcmV0dXJuIHRoaXMuYmxlU2Nhbm5lcjtcbiAgfVxuICBwcml2YXRlIGVtaXROZXhSZXN1bHRzKHJlc3VsdHM/OiBzY2FubmVyUmVzdWx0W10pIHtcbiAgICB0aGlzLmxhc3RSZXN1bHRzID0gcmVzdWx0cztcbiAgICB0aGlzLl9yZXN1bHRzJC5uZXh0KHJlc3VsdHMpO1xuICB9XG5cbiAgcHJpdmF0ZSBjbGVhclByZXZpb3VzU2Nhbm5lcigpIHtcbiAgICBpZiAodGhpcy5ibGVTY2FubmVyKSB7XG4gICAgICB0aGlzLnNjYW5uZXJTdWJzY3JpcHRpb24/LnVuc3Vic2NyaWJlKCk7XG4gICAgICB0aGlzLmVtaXROZXhSZXN1bHRzKHVuZGVmaW5lZCk7XG4gICAgICB0aGlzLmJsZVNjYW5uZXIgPSB1bmRlZmluZWQ7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgc3RhcnRTY2FuKG9wdGlvbnM/OiBCbGVTY2FubmVyT3B0aW9ucykge1xuICAgIGNvbnN0IG5ld1NjYW5uZXIgPSB0aGlzLmNyZWF0ZU5ld1NjYW5uZXIob3B0aW9ucyk7XG4gICAgcmV0dXJuIG5ld1NjYW5uZXIuc3RhcnQoKTtcbiAgfVxuXG4gIGFzeW5jIHN0b3BTY2FuKCkge1xuICAgIGlmICh0aGlzLnBsYXRmb3JtLmlzKCdjb3Jkb3ZhJykpIHtcbiAgICAgIGlmICh0aGlzLmJsZVNjYW5uZXIgPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignQkxFIFNjYW5uZXIgbm90IGRlZmluZWQnKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLmJsZVNjYW5uZXIuc3RvcCgpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyAvLyBzdG9wIHdhdGNoIGFkdmVydGlzZW1lbnRzXG4gICAgICBmb3IgKGxldCBbaWQsIGFkYXB0ZXJdIG9mIHRoaXMud2ViQmx1ZXRvb3RoUGVyaXBoZXJhbEFkYXB0ZXJzKSB7XG4gICAgICAgIHRoaXMudW5zZXRXYXRjaEFkdmVydGlzZW1lbnQoYWRhcHRlcik7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5ibGVTY2FubmVyPy5zdG9wKCk7XG4gICAgfVxuICB9XG5cbiAgY29ubmVjdEJ5TmFtZShibGVEZXZpY2VOYW1lOiBzdHJpbmcpIHtcbiAgICBpZiAodGhpcy5wbGF0Zm9ybS5pcygnY29yZG92YScpKSB7XG4gICAgICBpZiAodGhpcy5wbHVnaW4gPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHJldHVybiB0aHJvd0Vycm9yKCgpID0+IG5ldyBFcnJvcignQkxFIHBsdWdpbiBpcyBub3QgYXZhaWxhYmxlJykpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMucGx1Z2luLmNvbm5lY3QoYmxlRGV2aWNlTmFtZSkucGlwZShzaGFyZSgpKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIGRlZmVyKGFzeW5jICgpID0+IHtcbiAgICAgICAgY29uc3QgZGV2aWNlID0gdGhpcy5nZXREZXZpY2VGcm9tUGVyaXBoZXJhbEFkYXB0ZXIoYmxlRGV2aWNlTmFtZSk7XG4gICAgICAgIGlmIChkZXZpY2UpIHtcbiAgICAgICAgICBkZXZpY2UuY29ubmVjdCgpLnRoZW4oKCkgPT4ge1xuICAgICAgICAgICAgZGV2aWNlLl9wZXJpcGhlcmFsLndhdGNoQWR2ZXJ0aXNlbWVudHMoKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgICByZXR1cm4gZGV2aWNlLnN0YXRlQ2hhbmdlO1xuICAgICAgICB9XG4gICAgICAgIHRocm93RXJyb3IoXG4gICAgICAgICAgKCkgPT5cbiAgICAgICAgICAgIG5ldyBFcnJvcihgQ29ubmVjdGlvbkVycm9yLCBjYW4gbm90IGZpbmQgZGV2aWNlICR7YmxlRGV2aWNlTmFtZX1gKVxuICAgICAgICApO1xuICAgICAgfSkucGlwZShcbiAgICAgICAgZmlsdGVyVW5kZWZpbmVkKCksXG4gICAgICAgIHN3aXRjaE1hcCgodikgPT4gdilcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgZGlzY29ubmVjdEJ5TmFtZShibGVEZXZpY2VOYW1lOiBzdHJpbmcpIHtcbiAgICBpZiAodGhpcy5wbGF0Zm9ybS5pcygnY29yZG92YScpKSB7XG4gICAgICBpZiAodGhpcy5wbHVnaW4gPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignQkxFIHBsdWdpbiBpcyBub3QgYXZhaWxhYmxlJyk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5wbHVnaW4uZGlzQ29ubmVjdChibGVEZXZpY2VOYW1lKTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgZGV2aWNlID0gYXdhaXQgdGhpcy5nZXREZXZpY2VGcm9tUGVyaXBoZXJhbEFkYXB0ZXJPclRocm93KFxuICAgICAgICBibGVEZXZpY2VOYW1lXG4gICAgICApO1xuICAgICAgLy8gc3RvcCB3YXRjaCBhZHZlcnRpc2VtZW50c1xuICAgICAgdGhpcy51bnNldFdhdGNoQWR2ZXJ0aXNlbWVudChkZXZpY2UpO1xuICAgICAgYXdhaXQgZGV2aWNlLmRpc2Nvbm5lY3QoKTtcbiAgICAgIHJldHVybiAnT0snO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGNsb3NlQnlOYW1lKGJsZURldmljZU5hbWU6IHN0cmluZyk6IFByb21pc2U8J09LJz4ge1xuICAgIGlmICh0aGlzLnBsYXRmb3JtLmlzKCdjb3Jkb3ZhJykpIHtcbiAgICAgIGlmICh0aGlzLnBsdWdpbiA9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdCTEUgcGx1Z2luIGlzIG5vdCBhdmFpbGFibGUnKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLnBsdWdpbi5jbG9zZShibGVEZXZpY2VOYW1lKTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gTm90aGluZyB0byBkbyBvbiB3ZWIgYnJvd3NlclxuICAgICAgcmV0dXJuICdPSyc7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgcmVhZENoYXJhY3RlcmlzdGljKFxuICAgIGRldmljZU5hbWU6IHN0cmluZyxcbiAgICBzZXJ2aWNlSWQ6IHN0cmluZyxcbiAgICBjaGFyYWN0ZXJpc3RpY0lkOiBzdHJpbmdcbiAgKSB7XG4gICAgaWYgKHRoaXMucGxhdGZvcm0uaXMoJ2NvcmRvdmEnKSkge1xuICAgICAgaWYgKHRoaXMucGx1Z2luID09IHVuZGVmaW5lZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0JMRSBwbHVnaW4gaXMgbm90IGF2YWlsYWJsZScpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMucGx1Z2luLmNoYXJhY3RlcmlzdGljUmVhZFZhbHVlKFxuICAgICAgICBkZXZpY2VOYW1lLFxuICAgICAgICBzZXJ2aWNlSWQsXG4gICAgICAgIGNoYXJhY3RlcmlzdGljSWRcbiAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGNoYXJhY3RlcmlzdGljID1cbiAgICAgICAgYXdhaXQgdGhpcy5nZXRDaGFyYWN0ZXJpc3RpY0Zyb21QZXJpcGhlcmFsQWRhcHRlclByb21pc2UoXG4gICAgICAgICAgZGV2aWNlTmFtZSxcbiAgICAgICAgICBzZXJ2aWNlSWQsXG4gICAgICAgICAgY2hhcmFjdGVyaXN0aWNJZFxuICAgICAgICApO1xuICAgICAgcmV0dXJuIGNoYXJhY3RlcmlzdGljLnJlYWQoKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyB3cml0ZUNoYXJhY3RlcmlzdGljKFxuICAgIGRldmljZU5hbWU6IHN0cmluZyxcbiAgICBzZXJ2aWNlSWQ6IHN0cmluZyxcbiAgICBjaGFyYWN0ZXJpc3RpY0lkOiBzdHJpbmcsXG4gICAgZGF0YTogVWludDhBcnJheSxcbiAgICB3aXRoUmVzcG9uc2U/OiBib29sZWFuXG4gICk6IFByb21pc2U8dHJ1ZT4ge1xuICAgIGlmICh0aGlzLnBsYXRmb3JtLmlzKCdjb3Jkb3ZhJykpIHtcbiAgICAgIGlmICh0aGlzLnBsdWdpbiA9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdCTEUgcGx1Z2luIGlzIG5vdCBhdmFpbGFibGUnKTtcbiAgICAgIH1cbiAgICAgIGxldCB3cml0ZVByb21pc2U6IFByb21pc2U8J09LJz47XG4gICAgICBpZiAod2l0aFJlc3BvbnNlKSB7XG4gICAgICAgIHdyaXRlUHJvbWlzZSA9IHRoaXMucGx1Z2luLmNoYXJhY3RlcmlzdGljV3JpdGUoXG4gICAgICAgICAgZGV2aWNlTmFtZSxcbiAgICAgICAgICBzZXJ2aWNlSWQsXG4gICAgICAgICAgY2hhcmFjdGVyaXN0aWNJZCxcbiAgICAgICAgICBkYXRhXG4gICAgICAgICk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB3cml0ZVByb21pc2UgPSB0aGlzLnBsdWdpbi5jaGFyYWN0ZXJpc3RpY1dyaXRlV2l0aG91dFJlc3BvbnNlKFxuICAgICAgICAgIGRldmljZU5hbWUsXG4gICAgICAgICAgc2VydmljZUlkLFxuICAgICAgICAgIGNoYXJhY3RlcmlzdGljSWQsXG4gICAgICAgICAgZGF0YVxuICAgICAgICApO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHdyaXRlUHJvbWlzZS50aGVuKChfKSA9PiB0cnVlKTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgY2hhcmFjdGVyaXN0aWMgPVxuICAgICAgICBhd2FpdCB0aGlzLmdldENoYXJhY3RlcmlzdGljRnJvbVBlcmlwaGVyYWxBZGFwdGVyUHJvbWlzZShcbiAgICAgICAgICBkZXZpY2VOYW1lLFxuICAgICAgICAgIHNlcnZpY2VJZCxcbiAgICAgICAgICBjaGFyYWN0ZXJpc3RpY0lkXG4gICAgICAgICk7XG4gICAgICBhd2FpdCBjaGFyYWN0ZXJpc3RpYy53cml0ZShkYXRhLCAhIXdpdGhSZXNwb25zZSk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gIH1cblxuICBsaXN0ZW5DaGFyYWN0ZXJpc3RpY0NoYW5nZShcbiAgICBkZXZpY2VOYW1lOiBzdHJpbmcsXG4gICAgc2VydmljZUlkOiBzdHJpbmcsXG4gICAgY2hhcmFjdGVyaXN0aWNJZDogc3RyaW5nXG4gICk6IE9ic2VydmFibGU8VWludDhBcnJheT4ge1xuICAgIGlmICh0aGlzLnBsYXRmb3JtLmlzKCdjb3Jkb3ZhJykpIHtcbiAgICAgIGlmICh0aGlzLnBsdWdpbiA9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIHRocm93RXJyb3IobmV3IEVycm9yKCdCTEUgcGx1Z2luIGlzIG5vdCBhdmFpbGFibGUnKSk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5wbHVnaW5cbiAgICAgICAgLmNoYXJhY3RlcmlzdGljQ2hhbmdlZChkZXZpY2VOYW1lLCBzZXJ2aWNlSWQsIGNoYXJhY3RlcmlzdGljSWQpXG4gICAgICAgIC5waXBlKFxuICAgICAgICAgIHJ1bkluWm9uZSh0aGlzLm5nWm9uZSlcbiAgICAgICAgICAvLyBzaGFyZSgpLFxuICAgICAgICApO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gdGhpcy5nZXRDaGFyYWN0ZXJpc3RpY0Zyb21QZXJpcGhlcmFsQWRhcHRlck9ic2VydmFibGUoXG4gICAgICAgIGRldmljZU5hbWUsXG4gICAgICAgIHNlcnZpY2VJZCxcbiAgICAgICAgY2hhcmFjdGVyaXN0aWNJZFxuICAgICAgKS5waXBlKFxuICAgICAgICBzd2l0Y2hNYXAoKGNoYXIpID0+IHtcbiAgICAgICAgICByZXR1cm4gY2hhci5kYXRhO1xuICAgICAgICB9KSxcbiAgICAgICAgbWFwKChkYXRhKSA9PiBkYXRhLmRhdGEpLFxuICAgICAgICBydW5JblpvbmUodGhpcy5uZ1pvbmUpXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGRpc2NvdmVyU2VydmljZXMoZGV2aWNlTmFtZTogc3RyaW5nKTogUHJvbWlzZTxTZXJ2aWNlTGlzdD4ge1xuICAgIGlmICh0aGlzLnBsYXRmb3JtLmlzKCdjb3Jkb3ZhJykpIHtcbiAgICAgIGlmICh0aGlzLnBsdWdpbiA9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdCTEUgcGx1Z2luIGlzIG5vdCBhdmFpbGFibGUnKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLnBsdWdpbi5kaXNjb3ZlclNlcnZpY2VzKGRldmljZU5hbWUpO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCBkZXZpY2UgPSBhd2FpdCB0aGlzLmdldERldmljZUZyb21QZXJpcGhlcmFsQWRhcHRlck9yVGhyb3coXG4gICAgICAgIGRldmljZU5hbWVcbiAgICAgICk7XG4gICAgICBpZiAoIWRldmljZS5fcGVyaXBoZXJhbC5nYXR0Py5jb25uZWN0ZWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBkZXZpY2UgJHtkZXZpY2VOYW1lfSBpcyBub3QgY29ubmVjdGVkYCk7XG4gICAgICB9XG4gICAgICBjb25zdCBzZXJ2aWNlc0FkYXB0ZXJzID0gYXdhaXQgZGV2aWNlLmRpc2NvdmVyU2VydmljZXMoKTtcbiAgICAgIGNvbnN0IHNlcnZpY2VzOiBTZXJ2aWNlTGlzdCA9IFtdO1xuICAgICAgZm9yIChsZXQgc2VydmljZU5hbWUgaW4gc2VydmljZXNBZGFwdGVycykge1xuICAgICAgICBjb25zdCBhZGFwdGVyID0gc2VydmljZXNBZGFwdGVyc1tzZXJ2aWNlTmFtZV07XG4gICAgICAgIGlmIChhZGFwdGVyKSB7XG4gICAgICAgICAgY29uc3QgZGVzY3JpcHRpb246IFNlcnZpY2VEZXNjcmlwdGlvbiA9IHtcbiAgICAgICAgICAgIHV1aWQ6IGFkYXB0ZXIudXVpZCxcbiAgICAgICAgICB9O1xuICAgICAgICAgIGNvbnN0IGNoYXJhY3RlcmlzdGljRGVzY3JpcHRpb25zOiBDaGFyYWN0ZXJpc3RpY0Rlc2NyaXB0aW9uW10gPSBbXTtcbiAgICAgICAgICBmb3IgKGxldCBjaGFyYWN0ZXJpc3RpYyBvZiBhd2FpdCBhZGFwdGVyLmdldENoYXJhY3RlcmlzdGljcygpKSB7XG4gICAgICAgICAgICBjb25zdCBkZXNjcmlwdG9ycyA9IGF3YWl0IGNoYXJhY3RlcmlzdGljLmdldERlc2NyaXB0b3JzKCk7XG4gICAgICAgICAgICBjaGFyYWN0ZXJpc3RpY0Rlc2NyaXB0aW9ucy5wdXNoKHtcbiAgICAgICAgICAgICAgdXVpZDogY2hhcmFjdGVyaXN0aWMudXVpZCxcbiAgICAgICAgICAgICAgcHJvcGVydGllczogY2hhcmFjdGVyaXN0aWMucHJvcGVydGllcyxcbiAgICAgICAgICAgICAgZGVzY3JpcHRvcnM6IGRlc2NyaXB0b3JzLm1hcCgoZGVzYykgPT4ge1xuICAgICAgICAgICAgICAgIHJldHVybiB7IHV1aWQ6IGRlc2MudXVpZCwgdmFsdWU6IGRlc2MudmFsdWUgfTtcbiAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGNoYXJhY3RlcmlzdGljRGVzY3JpcHRpb25zLmxlbmd0aCAhPSAwKSB7XG4gICAgICAgICAgICBkZXNjcmlwdGlvbi5jaGFyYWN0ZXJpc3RpY3MgPSBjaGFyYWN0ZXJpc3RpY0Rlc2NyaXB0aW9ucztcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBzZXJ2aWNlcy5wdXNoKGRlc2NyaXB0aW9uKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICByZXR1cm4gc2VydmljZXM7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgc3RhcnROb3RpZmljYXRpb24oXG4gICAgZGV2aWNlTmFtZTogc3RyaW5nLFxuICAgIHNlcnZpY2VJZDogc3RyaW5nLFxuICAgIGNoYXJhY3RlcmlzdGljSWQ6IHN0cmluZ1xuICApOiBQcm9taXNlPCdPSyc+IHtcbiAgICBpZiAodGhpcy5wbGF0Zm9ybS5pcygnY29yZG92YScpKSB7XG4gICAgICBpZiAodGhpcy5wbHVnaW4gPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignQkxFIHBsdWdpbiBpcyBub3QgYXZhaWxhYmxlJyk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5wbHVnaW4uY2hhcmFjdGVyaXN0aWNTdGFydE5vdGlmaWNhdGlvbihcbiAgICAgICAgZGV2aWNlTmFtZSxcbiAgICAgICAgc2VydmljZUlkLFxuICAgICAgICBjaGFyYWN0ZXJpc3RpY0lkXG4gICAgICApO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCBjaGFyYWN0ZXJpc3RpYyA9XG4gICAgICAgIGF3YWl0IHRoaXMuZ2V0Q2hhcmFjdGVyaXN0aWNGcm9tUGVyaXBoZXJhbEFkYXB0ZXJQcm9taXNlKFxuICAgICAgICAgIGRldmljZU5hbWUsXG4gICAgICAgICAgc2VydmljZUlkLFxuICAgICAgICAgIGNoYXJhY3RlcmlzdGljSWRcbiAgICAgICAgKTtcbiAgICAgIGF3YWl0IGNoYXJhY3RlcmlzdGljLmVuYWJsZU5vdGlmaWNhdGlvbnModHJ1ZSk7XG4gICAgICByZXR1cm4gJ09LJztcbiAgICB9XG4gIH1cblxuICBhc3luYyBzdG9wTm90aWZpY2F0aW9uKFxuICAgIGRldmljZU5hbWU6IHN0cmluZyxcbiAgICBzZXJ2aWNlSWQ6IHN0cmluZyxcbiAgICBjaGFyYWN0ZXJpc3RpY0lkOiBzdHJpbmdcbiAgKTogUHJvbWlzZTx0cnVlPiB7XG4gICAgaWYgKHRoaXMucGxhdGZvcm0uaXMoJ2NvcmRvdmEnKSkge1xuICAgICAgaWYgKHRoaXMucGx1Z2luID09IHVuZGVmaW5lZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0JMRSBwbHVnaW4gaXMgbm90IGF2YWlsYWJsZScpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMucGx1Z2luXG4gICAgICAgIC5jaGFyYWN0ZXJpc3RpY1N0b3BOb3RpZmljYXRpb24oZGV2aWNlTmFtZSwgc2VydmljZUlkLCBjaGFyYWN0ZXJpc3RpY0lkKVxuICAgICAgICAudGhlbigocikgPT4gdHJ1ZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGNoYXJhY3RlcmlzdGljID1cbiAgICAgICAgYXdhaXQgdGhpcy5nZXRDaGFyYWN0ZXJpc3RpY0Zyb21QZXJpcGhlcmFsQWRhcHRlclByb21pc2UoXG4gICAgICAgICAgZGV2aWNlTmFtZSxcbiAgICAgICAgICBzZXJ2aWNlSWQsXG4gICAgICAgICAgY2hhcmFjdGVyaXN0aWNJZFxuICAgICAgICApO1xuICAgICAgYXdhaXQgY2hhcmFjdGVyaXN0aWMuZW5hYmxlTm90aWZpY2F0aW9ucyhmYWxzZSk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGdldERldmljZUZyb21QZXJpcGhlcmFsQWRhcHRlcihkZXZpY2VOYW1lOiBzdHJpbmcpIHtcbiAgICBjb25zdCBkZXZpY2UgPSB0aGlzLndlYkJsdWV0b290aFBlcmlwaGVyYWxBZGFwdGVycy5nZXQoZGV2aWNlTmFtZSk7XG4gICAgaWYgKGRldmljZSAhPSB1bmRlZmluZWQpIHtcbiAgICAgIHJldHVybiBkZXZpY2U7XG4gICAgfVxuICAgIC8vdHJ5IHdpdGggaWRcbiAgICBmb3IgKGxldCBbaWQsIGFkYXB0ZXJdIG9mIHRoaXMud2ViQmx1ZXRvb3RoUGVyaXBoZXJhbEFkYXB0ZXJzKSB7XG4gICAgICBpZiAoYWRhcHRlci5pZCA9PSBkZXZpY2VOYW1lKSB7XG4gICAgICAgIHJldHVybiBhZGFwdGVyO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBnZXREZXZpY2VGcm9tUGVyaXBoZXJhbEFkYXB0ZXJPclRocm93KGRldmljZU5hbWU6IHN0cmluZykge1xuICAgIGNvbnN0IGRldmljZSA9IHRoaXMuZ2V0RGV2aWNlRnJvbVBlcmlwaGVyYWxBZGFwdGVyKGRldmljZU5hbWUpO1xuICAgIGlmIChkZXZpY2UpIHtcbiAgICAgIHJldHVybiBkZXZpY2U7XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcihgQ2FuIG5vdCBmaW5kIGRldmljZSAke2RldmljZX1gKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZ2V0RGV2aWNlU2VydmljZUZyb21QZXJpcGhlcmFsQWRhcHRlcihcbiAgICBkZXZpY2VOYW1lOiBzdHJpbmcsXG4gICAgc2VydmljZUlkOiBzdHJpbmdcbiAgKSB7XG4gICAgY29uc3QgZGV2aWNlID0gYXdhaXQgdGhpcy5nZXREZXZpY2VGcm9tUGVyaXBoZXJhbEFkYXB0ZXJPclRocm93KGRldmljZU5hbWUpO1xuICAgIGNvbnN0IHNlcnZpY2UgPSBhd2FpdCBkZXZpY2UuZ2V0U2VydmljZShzZXJ2aWNlSWQpO1xuICAgIGlmIChzZXJ2aWNlID09IHVuZGVmaW5lZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBDYW4gbm90IGZpbmQgc2VydmljZSAke3NlcnZpY2VJZH0gb24gZGV2aWNlICR7ZGV2aWNlfWApO1xuICAgIH1cbiAgICByZXR1cm4gc2VydmljZTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0Q2hhcmFjdGVyaXN0aWNGcm9tUGVyaXBoZXJhbEFkYXB0ZXJPYnNlcnZhYmxlKFxuICAgIGRldmljZU5hbWU6IHN0cmluZyxcbiAgICBzZXJ2aWNlSWQ6IHN0cmluZyxcbiAgICBjaGFyYWN0ZXJpc3RpY0lkOiBzdHJpbmdcbiAgKSB7XG4gICAgcmV0dXJuIGRlZmVyKCgpID0+IHtcbiAgICAgIHJldHVybiB0aGlzLmdldENoYXJhY3RlcmlzdGljRnJvbVBlcmlwaGVyYWxBZGFwdGVyUHJvbWlzZShcbiAgICAgICAgZGV2aWNlTmFtZSxcbiAgICAgICAgc2VydmljZUlkLFxuICAgICAgICBjaGFyYWN0ZXJpc3RpY0lkXG4gICAgICApO1xuICAgIH0pLnBpcGUoZmlsdGVyVW5kZWZpbmVkKCkpO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBnZXRDaGFyYWN0ZXJpc3RpY0Zyb21QZXJpcGhlcmFsQWRhcHRlclByb21pc2UoXG4gICAgZGV2aWNlTmFtZTogc3RyaW5nLFxuICAgIHNlcnZpY2VJZDogc3RyaW5nLFxuICAgIGNoYXJhY3RlcmlzdGljSWQ6IHN0cmluZ1xuICApIHtcbiAgICBjb25zdCBzZXJ2aWNlID0gYXdhaXQgdGhpcy5nZXREZXZpY2VTZXJ2aWNlRnJvbVBlcmlwaGVyYWxBZGFwdGVyKFxuICAgICAgZGV2aWNlTmFtZSxcbiAgICAgIHNlcnZpY2VJZFxuICAgICk7XG4gICAgaWYgKHNlcnZpY2UpIHtcbiAgICAgIHJldHVybiBhd2FpdCBzZXJ2aWNlLmdldENoYXJhY3RlcmlzdGljKGNoYXJhY3RlcmlzdGljSWQpO1xuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgQ2FuIG5vdCBmaW5kIGNoYXJhY3RlcmlzdGljICR7Y2hhcmFjdGVyaXN0aWNJZH0gb24gc2VydmljZSAke3NlcnZpY2VJZH0gb24gZGV2aWNlICR7ZGV2aWNlTmFtZX1gXG4gICAgKTtcbiAgfVxuXG4gIHByaXZhdGUgc2V0V2F0Y2hBZHZlcnRpc2VtZW50KGFkYXB0ZXI6IFBlcmlwaGVyYWxBZGFwdGVyV2l0aFJlY29yZHMpIHtcbiAgICBjb25zb2xlLmxvZygnc2V0V2F0Y2hBZHZlcnRpc2VtZW50JywgYWRhcHRlci5uYW1lKTtcbiAgICBhZGFwdGVyLl9wZXJpcGhlcmFsLmFkZEV2ZW50TGlzdGVuZXIoXG4gICAgICAnYWR2ZXJ0aXNlbWVudHJlY2VpdmVkJyxcbiAgICAgIChldmVudCkgPT4ge1xuICAgICAgICBjb25zb2xlLmxvZygnVVBEQVRFIEFEVkVSVElTRU1FTlQnKTtcbiAgICAgICAgY29uc29sZS5sb2coZXZlbnQpO1xuICAgICAgICB0aGlzLnVwZGF0ZUFkdmVydGlzZW1lbnREYXRhKGV2ZW50LCBhZGFwdGVyKTtcbiAgICAgIH0sXG4gICAgICB0cnVlXG4gICAgKTtcblxuICAgIGFkYXB0ZXIud2F0Y2hBZHZlcnRpc2VtZW50c0Fib3J0Q29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcblxuICAgIGFkYXB0ZXIuX3BlcmlwaGVyYWwud2F0Y2hBZHZlcnRpc2VtZW50cyh7XG4gICAgICBzaWduYWw6IGFkYXB0ZXIud2F0Y2hBZHZlcnRpc2VtZW50c0Fib3J0Q29udHJvbGxlci5zaWduYWwsXG4gICAgfSk7XG5cbiAgICAvLyAoYWRhcHRlci5fcGVyaXBoZXJhbCBhcyBhbnkpLm9uYWR2ZXJ0aXNlbWVudHJlY2VpdmVkID0gKGV2ZW50OiBhbnkpID0+IHtcbiAgICAvLyAgIGNvbnNvbGUubG9nKFwiVVBEQVRFIEFEVkVSVElTRU1FTlRcIik7XG4gICAgLy8gICBjb25zb2xlLmxvZyhldmVudCk7XG4gICAgLy8gfVxuICB9XG4gIHByaXZhdGUgdW5zZXRXYXRjaEFkdmVydGlzZW1lbnQoYWRhcHRlcjogUGVyaXBoZXJhbEFkYXB0ZXJXaXRoUmVjb3Jkcykge1xuICAgIC8vIGFkYXB0ZXIuX3BlcmlwaGVyYWwucmVtb3ZlRXZlbnRMaXN0ZW5lcignYWR2ZXJ0aXNlbWVudHJlY2VpdmVkJywgbnVsbCk7XG4gICAgYWRhcHRlci53YXRjaEFkdmVydGlzZW1lbnRzQWJvcnRDb250cm9sbGVyPy5hYm9ydCgpO1xuICB9XG5cbiAgcHJpdmF0ZSB1cGRhdGVBZHZlcnRpc2VtZW50RGF0YShcbiAgICBldmVudDogYW55LFxuICAgIGFkYXB0ZXI6IFBlcmlwaGVyYWxBZGFwdGVyV2l0aFJlY29yZHNcbiAgKSB7XG4gICAgYWRhcHRlci5yc3NpID0gZXZlbnQucnNzaTtcbiAgICBhZGFwdGVyLnR4UG93ZXIgPSBldmVudC50eFBvd2VyO1xuICAgIGlmIChldmVudC5tYW51ZmFjdHVyZXJEYXRhKSB7XG4gICAgICBhZGFwdGVyLnNjYW5SZWNvcmQgPSBbXTtcbiAgICAgIGZvciAobGV0IFtrZXksIHZhbHVlXSBvZiBldmVudC5tYW51ZmFjdHVyZXJEYXRhIGFzIE1hcDxcbiAgICAgICAgbnVtYmVyLFxuICAgICAgICBBcnJheUJ1ZmZlclxuICAgICAgPikge1xuICAgICAgICBhZGFwdGVyLnNjYW5SZWNvcmQucHVzaCgweGZmLCAoa2V5ICYgMHhmZjAwKSA+PiA4LCBrZXkgJiAweGZmKTtcbiAgICAgICAgYWRhcHRlci5zY2FuUmVjb3JkLnB1c2goLi4ubmV3IFVpbnQ4QXJyYXkodmFsdWUpKTtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5lbWl0TmV4UmVzdWx0cyh0aGlzLmxhc3RSZXN1bHRzKTtcbiAgfVxuXG4gIHByaXZhdGUgY29tcGFueUlkZW50aWZpZXJUb051bWJlcihcbiAgICBjb21wYW55SWRlbnRpZmllcjogbnVtYmVyIHwgbnVtYmVyW10gfCBVaW50OEFycmF5XG4gICkge1xuICAgIGlmICh0eXBlb2YgY29tcGFueUlkZW50aWZpZXIgPT0gJ251bWJlcicpIHtcbiAgICAgIHJldHVybiBjb21wYW55SWRlbnRpZmllcjtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIG5ldyBBcnJheShjb21wYW55SWRlbnRpZmllcikucmVkdWNlPG51bWJlcj4oKGFjYywgbmV4dCwgaSkgPT4ge1xuICAgICAgICBhY2MgKz0gbmV4dFtpXSA8PCBpO1xuICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgfSwgMCk7XG4gICAgfVxuICB9XG59XG4iXX0=