@firestone-hs/replay-parser
Version:
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.2.9.
246 lines • 39.2 kB
JavaScript
import { Injectable } from '@angular/core';
import { Map } from 'immutable';
import { Observable } from 'rxjs';
import { Game } from '../models/game/game';
import { ActionParserConfig, GameHistoryItem } from '../models/models';
import { XmlParserService } from './xml-parser.service';
import * as i0 from "@angular/core";
import * as i1 from "./all-cards.service";
import * as i2 from "./gamepipeline/action-parser.service";
import * as i3 from "./gamepipeline/turn-parser.service";
import * as i4 from "./image-preloader.service";
import * as i5 from "./entitiespipeline/game-population.service";
import * as i6 from "./entitiespipeline/game-state-parser.service";
import * as i7 from "./gamepipeline/game-initializer.service";
import * as i8 from "./gamepipeline/active-player-parser.service";
import * as i9 from "./gamepipeline/active-spell-parser.service";
import * as i10 from "./gamepipeline/targets-parser.service";
import * as i11 from "./gamepipeline/mulligan-parser.service";
import * as i12 from "./gamepipeline/end-game-parser.service";
import * as i13 from "./gamepipeline/narrator.service";
import * as i14 from "./state-processor.service";
const SMALL_PAUSE = 15;
export class GameParserService {
constructor(allCards, actionParser, turnParser, imagePreloader, gamePopulationService, gameStateParser, gameInitializer, activePlayerParser, activeSpellParser, targetsParser, mulliganParser, endGameParser, narrator, stateProcessor) {
this.allCards = allCards;
this.actionParser = actionParser;
this.turnParser = turnParser;
this.imagePreloader = imagePreloader;
this.gamePopulationService = gamePopulationService;
this.gameStateParser = gameStateParser;
this.gameInitializer = gameInitializer;
this.activePlayerParser = activePlayerParser;
this.activeSpellParser = activeSpellParser;
this.targetsParser = targetsParser;
this.mulliganParser = mulliganParser;
this.endGameParser = endGameParser;
this.narrator = narrator;
this.stateProcessor = stateProcessor;
}
async parse(replayAsString, options, config = new ActionParserConfig()) {
const start = Date.now();
this.cancelled = false;
if (this.processingTimeout) {
clearTimeout(this.processingTimeout);
this.processingTimeout = undefined;
}
if (!this.allCards.getCards()?.length) {
await this.allCards.initializeCardsDb();
this.logPerf('Retrieved cards DB, parsing replay', start);
}
const iterator = this.createGamePipeline(replayAsString, start, options, config);
return Observable.create(observer => {
this.buildObservableFunction(observer, iterator);
});
}
cancelProcessing() {
this.cancelled = true;
clearTimeout(this.processingTimeout);
}
buildObservableFunction(observer, iterator) {
// // console.log('calling next iteration');
try {
const itValue = iterator.next();
// // console.log('calling next obersable', itValue, itValue.value);
observer.next([itValue.value[0], itValue.value[2], itValue.done]);
if (!itValue.done && !this.cancelled) {
this.processingTimeout = setTimeout(() => this.buildObservableFunction(observer, iterator), itValue.value[1]);
}
}
catch (e) {
console.error('[game-parser] Exception in buildObservableFunction', e);
}
}
*createGamePipeline(replayAsString, start, options, config) {
if (!replayAsString || replayAsString.length == 0) {
return [null, SMALL_PAUSE, 'Invalid XML replay'];
}
// console.log('[game-parser] preparing entity / acrd ID mapping');
let entityCardId = Map([]);
const fullEntityIdCardIdMatcher = new RegExp(/id="(.*?)" cardID="(.*?)"/g);
const fullEntityMatchResult = replayAsString.match(fullEntityIdCardIdMatcher);
for (let match of fullEntityMatchResult) {
const result = new RegExp(/id="(.*?)" cardID="(.*?)"/g).exec(match);
if (result) {
entityCardId = entityCardId.set(parseInt(result[1]), result[2]);
}
}
const showEntityIdCardIdMatcher = new RegExp(/cardID="(.*?)" entity="(.*?)"/g);
const showEntityMatchResult = replayAsString.match(showEntityIdCardIdMatcher);
for (let match of showEntityMatchResult) {
// // console.log("updating with show', result", copy);
const result = new RegExp(/cardID="(.*?)" entity="(.*?)"/g).exec(match);
if (result) {
// // console.log('result', result);
entityCardId = entityCardId.set(parseInt(result[2]), result[1]);
}
}
// console.log('[game-parser] mapping done', entityCardId.size);
// Do the parsing turn by turn
// let history: readonly HistoryItem[];
const xmlParsingIterator = new XmlParserService().parseXml(replayAsString);
let game = Game.createGame({});
let counter = 0;
while (true) {
const itValue = xmlParsingIterator.next();
const history = itValue.value;
// console.debug('[game-parser] parsing for', counter, 'with history length', history.length);
if (!history || itValue.done) {
// console.debug('[game-parser] history parsing over', itValue);
break;
}
if (history[0] instanceof GameHistoryItem) {
const gameHistory = history[0];
game = Object.assign(game, {
buildNumber: gameHistory.buildNumber,
formatType: gameHistory.formatType,
gameType: gameHistory.gameType,
scenarioID: gameHistory.scenarioID,
});
// console.log('[game-parser] assign meta data to game', game);
}
// Battlegrounds tutorial
if (game.scenarioID === 3539) {
// console.log('[game-parser] Battlegrounds tutorial not supported, returning');
return [null, SMALL_PAUSE, 'Batllegrounds tutorial is not supported'];
}
// Preload the images we'll need early on
// const preloadIterator = this.imagePreloader.preloadImages(history);
// while (true) {
// const itValue = preloadIterator.next();
// if (itValue.done) {
// break;
// }
// }
// console.log('[game-parser] will initNewEntities', game, history, entityCardId.toJS());
let entities = this.gamePopulationService.initNewEntities(game, history, entityCardId);
// console.log('[game-parser] initNewEntities', entities.size);
if (game.turns.size === 0) {
game = this.gameInitializer.initializePlayers(game, entities);
game = this.gameStateParser.updateEntitiesUntilMulliganState(game, entities, history);
entities = game.entitiesBeforeMulligan;
// // console.log('game after populateEntitiesUntilMulliganState', game, game.turns.toJS());
}
game = this.turnParser.createTurns(game, history);
// // console.log('game after turn creation', game.turns.size);
game = this.actionParser.parseActions(game, entities, history, config);
// // console.log(
// 'entity 150 parseActions',
// game.getLatestParsedState().get(150) &&
// game
// .getLatestParsedState()
// .get(150)
// .tags.toJS(),
// );
// // console.log('game after action pasring', game.getLatestParsedState().toJS());
if (game.turns.size > 0) {
game = this.activePlayerParser.parseActivePlayerForLastTurn(game);
// // console.log(
// 'entity 150 parseActivePlayerForLastTurn',
// game.getLatestParsedState().get(150) &&
// game
// .getLatestParsedState()
// .get(150)
// .tags.toJS(),
// );
// // console.log('game after parseActivePlayer', game, game.turns.toJS());
game = this.activeSpellParser.parseActiveSpellForLastTurn(game);
// // console.log(
// 'entity 150 parseActiveSpellForLastTurn',
// game.getLatestParsedState().get(150) &&
// game
// .getLatestParsedState()
// .get(150)
// .tags.toJS(),
// );
// // console.log('game after parseActiveSpell', game, game.turns.toJS());
game = this.targetsParser.parseTargetsForLastTurn(game);
// // console.log(
// 'entity 150 parseTargetsForLastTurn',
// game.getLatestParsedState().get(150) &&
// game
// .getLatestParsedState()
// .get(150)
// .tags.toJS(),
// );
// // console.log('game after parseTargets', game, game.turns.toJS());
if (game.turns.size === 1) {
game = this.mulliganParser.affectMulligan(game);
}
// // console.log('game after affectMulligan', game, game.turns.toJS());
game = this.endGameParser.parseEndGame(game);
// // console.log('game after parseEndGame', game, game.turns.toJS());
game = this.narrator.populateActionTextForLastTurn(game);
// // console.log('game after populateActionText', game, game.turns.toJS());
game = this.narrator.createGameStoryForLastTurn(game);
// // console.log(
// 'entity 150 createGameStoryForLastTurn',
// game.getLatestParsedState().get(150) &&
// game
// .getLatestParsedState()
// .get(150)
// .tags.toJS(),
// );
// // console.log('game after createGameStory', game, game.turns.toJS());
// if (counter === 4) {
// counter++;
// // console.log('returning', counter);
// return [game, SMALL_PAUSE, 'Rendering game state'];
// }
// counter++;
// // console.log('moving on', counter);
// if (game.turns.size === 33) {
// // console.log(
// 'entities at end of turn',
// game.getLatestParsedState().toJS(),
// game.getLatestParsedState().get(507),
// );
// }
yield [game, SMALL_PAUSE, 'Parsed turn ' + counter++];
}
else {
// if (counter++ === 3) {
// counter++;
// // // console.log('returning', counter, game.entities.get(73), game.entities.get(74));
// return [game, SMALL_PAUSE, 'Rendering game state'];
// }
// counter++;
}
}
// console.log('parsing done, returning');
return [game, SMALL_PAUSE, 'Rendering game state'];
}
logPerf(what, start, result) {
// console.log('[perf] ', what, 'done after ', Date.now() - start, 'ms');
return result;
}
}
GameParserService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: GameParserService, deps: [{ token: i1.AllCardsService }, { token: i2.ActionParserService }, { token: i3.TurnParserService }, { token: i4.ImagePreloaderService }, { token: i5.GamePopulationService }, { token: i6.GameStateParserService }, { token: i7.GameInitializerService }, { token: i8.ActivePlayerParserService }, { token: i9.ActiveSpellParserService }, { token: i10.TargetsParserService }, { token: i11.MulliganParserService }, { token: i12.EndGameParserService }, { token: i13.NarratorService }, { token: i14.StateProcessorService }], target: i0.ɵɵFactoryTarget.Injectable });
GameParserService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: GameParserService, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: GameParserService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}], ctorParameters: function () { return [{ type: i1.AllCardsService }, { type: i2.ActionParserService }, { type: i3.TurnParserService }, { type: i4.ImagePreloaderService }, { type: i5.GamePopulationService }, { type: i6.GameStateParserService }, { type: i7.GameInitializerService }, { type: i8.ActivePlayerParserService }, { type: i9.ActiveSpellParserService }, { type: i10.TargetsParserService }, { type: i11.MulliganParserService }, { type: i12.EndGameParserService }, { type: i13.NarratorService }, { type: i14.StateProcessorService }]; } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2FtZS1wYXJzZXIuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3JlcGxheS1wYXJzZXIvc3JjL2xpYi9zZXJ2aWNlcy9nYW1lLXBhcnNlci5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDM0MsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUNoQyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ2xDLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUUzQyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsZUFBZSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFldkUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7Ozs7Ozs7Ozs7Ozs7Ozs7QUFFeEQsTUFBTSxXQUFXLEdBQUcsRUFBRSxDQUFDO0FBS3ZCLE1BQU0sT0FBTyxpQkFBaUI7SUFDN0IsWUFDUyxRQUF5QixFQUN6QixZQUFpQyxFQUNqQyxVQUE2QixFQUM3QixjQUFxQyxFQUNyQyxxQkFBNEMsRUFDNUMsZUFBdUMsRUFDdkMsZUFBdUMsRUFDdkMsa0JBQTZDLEVBQzdDLGlCQUEyQyxFQUMzQyxhQUFtQyxFQUNuQyxjQUFxQyxFQUNyQyxhQUFtQyxFQUNuQyxRQUF5QixFQUN6QixjQUFxQztRQWJyQyxhQUFRLEdBQVIsUUFBUSxDQUFpQjtRQUN6QixpQkFBWSxHQUFaLFlBQVksQ0FBcUI7UUFDakMsZUFBVSxHQUFWLFVBQVUsQ0FBbUI7UUFDN0IsbUJBQWMsR0FBZCxjQUFjLENBQXVCO1FBQ3JDLDBCQUFxQixHQUFyQixxQkFBcUIsQ0FBdUI7UUFDNUMsb0JBQWUsR0FBZixlQUFlLENBQXdCO1FBQ3ZDLG9CQUFlLEdBQWYsZUFBZSxDQUF3QjtRQUN2Qyx1QkFBa0IsR0FBbEIsa0JBQWtCLENBQTJCO1FBQzdDLHNCQUFpQixHQUFqQixpQkFBaUIsQ0FBMEI7UUFDM0Msa0JBQWEsR0FBYixhQUFhLENBQXNCO1FBQ25DLG1CQUFjLEdBQWQsY0FBYyxDQUF1QjtRQUNyQyxrQkFBYSxHQUFiLGFBQWEsQ0FBc0I7UUFDbkMsYUFBUSxHQUFSLFFBQVEsQ0FBaUI7UUFDekIsbUJBQWMsR0FBZCxjQUFjLENBQXVCO0lBQzNDLENBQUM7SUFJRyxLQUFLLENBQUMsS0FBSyxDQUNqQixjQUFzQixFQUN0QixPQUFpQyxFQUNqQyxTQUE2QixJQUFJLGtCQUFrQixFQUFFO1FBRXJELE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUN6QixJQUFJLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQztRQUN2QixJQUFJLElBQUksQ0FBQyxpQkFBaUIsRUFBRTtZQUMzQixZQUFZLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUM7WUFDckMsSUFBSSxDQUFDLGlCQUFpQixHQUFHLFNBQVMsQ0FBQztTQUNuQztRQUVELElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxFQUFFLE1BQU0sRUFBRTtZQUN0QyxNQUFNLElBQUksQ0FBQyxRQUFRLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztZQUN4QyxJQUFJLENBQUMsT0FBTyxDQUFDLG9DQUFvQyxFQUFFLEtBQUssQ0FBQyxDQUFDO1NBQzFEO1FBRUQsTUFBTSxRQUFRLEdBQTZDLElBQUksQ0FBQyxrQkFBa0IsQ0FDakYsY0FBYyxFQUNkLEtBQUssRUFDTCxPQUFPLEVBQ1AsTUFBTSxDQUNOLENBQUM7UUFDRixPQUFPLFVBQVUsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLEVBQUU7WUFDbkMsSUFBSSxDQUFDLHVCQUF1QixDQUFDLFFBQVEsRUFBRSxRQUFRLENBQUMsQ0FBQztRQUNsRCxDQUFDLENBQUMsQ0FBQztJQUNKLENBQUM7SUFFTSxnQkFBZ0I7UUFDdEIsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUM7UUFDdEIsWUFBWSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO0lBQ3RDLENBQUM7SUFFTyx1QkFBdUIsQ0FBQyxRQUFRLEVBQUUsUUFBa0Q7UUFDM0YsNENBQTRDO1FBQzVDLElBQUk7WUFDSCxNQUFNLE9BQU8sR0FBRyxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDaEMsb0VBQW9FO1lBQ3BFLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxFQUFFLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7WUFDbEUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFO2dCQUNyQyxJQUFJLENBQUMsaUJBQWlCLEdBQUcsVUFBVSxDQUNsQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsdUJBQXVCLENBQUMsUUFBUSxFQUFFLFFBQVEsQ0FBQyxFQUN0RCxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUNoQixDQUFDO2FBQ0Y7U0FDRDtRQUFDLE9BQU8sQ0FBQyxFQUFFO1lBQ1gsT0FBTyxDQUFDLEtBQUssQ0FBQyxvREFBb0QsRUFBRSxDQUFDLENBQUMsQ0FBQztTQUN2RTtJQUNGLENBQUM7SUFFTyxDQUFDLGtCQUFrQixDQUMxQixjQUFzQixFQUN0QixLQUFhLEVBQ2IsT0FBZ0MsRUFDaEMsTUFBMEI7UUFFMUIsSUFBSSxDQUFDLGNBQWMsSUFBSSxjQUFjLENBQUMsTUFBTSxJQUFJLENBQUMsRUFBRTtZQUNsRCxPQUFPLENBQUMsSUFBSSxFQUFFLFdBQVcsRUFBRSxvQkFBb0IsQ0FBQyxDQUFDO1NBQ2pEO1FBRUQsbUVBQW1FO1FBQ25FLElBQUksWUFBWSxHQUF3QixHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDaEQsTUFBTSx5QkFBeUIsR0FBRyxJQUFJLE1BQU0sQ0FBQyw0QkFBNEIsQ0FBQyxDQUFDO1FBQzNFLE1BQU0scUJBQXFCLEdBQUcsY0FBYyxDQUFDLEtBQUssQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO1FBQzlFLEtBQUssSUFBSSxLQUFLLElBQUkscUJBQXFCLEVBQUU7WUFDeEMsTUFBTSxNQUFNLEdBQUcsSUFBSSxNQUFNLENBQUMsNEJBQTRCLENBQUMsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDcEUsSUFBSSxNQUFNLEVBQUU7Z0JBQ1gsWUFBWSxHQUFHLFlBQVksQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO2FBQ2hFO1NBQ0Q7UUFDRCxNQUFNLHlCQUF5QixHQUFHLElBQUksTUFBTSxDQUFDLGdDQUFnQyxDQUFDLENBQUM7UUFDL0UsTUFBTSxxQkFBcUIsR0FBRyxjQUFjLENBQUMsS0FBSyxDQUFDLHlCQUF5QixDQUFDLENBQUM7UUFDOUUsS0FBSyxJQUFJLEtBQUssSUFBSSxxQkFBcUIsRUFBRTtZQUN4Qyx1REFBdUQ7WUFDdkQsTUFBTSxNQUFNLEdBQUcsSUFBSSxNQUFNLENBQUMsZ0NBQWdDLENBQUMsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDeEUsSUFBSSxNQUFNLEVBQUU7Z0JBQ1gsb0NBQW9DO2dCQUNwQyxZQUFZLEdBQUcsWUFBWSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7YUFDaEU7U0FDRDtRQUNELGdFQUFnRTtRQUVoRSw4QkFBOEI7UUFDOUIsdUNBQXVDO1FBQ3ZDLE1BQU0sa0JBQWtCLEdBQTZDLElBQUksZ0JBQWdCLEVBQUUsQ0FBQyxRQUFRLENBQ25HLGNBQWMsQ0FDZCxDQUFDO1FBQ0YsSUFBSSxJQUFJLEdBQVMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxFQUFVLENBQUMsQ0FBQztRQUM3QyxJQUFJLE9BQU8sR0FBRyxDQUFDLENBQUM7UUFDaEIsT0FBTyxJQUFJLEVBQUU7WUFDWixNQUFNLE9BQU8sR0FBRyxrQkFBa0IsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUMxQyxNQUFNLE9BQU8sR0FBMkIsT0FBTyxDQUFDLEtBQUssQ0FBQztZQUN0RCw4RkFBOEY7WUFFOUYsSUFBSSxDQUFDLE9BQU8sSUFBSSxPQUFPLENBQUMsSUFBSSxFQUFFO2dCQUM3QixnRUFBZ0U7Z0JBQ2hFLE1BQU07YUFDTjtZQUVELElBQUksT0FBTyxDQUFDLENBQUMsQ0FBQyxZQUFZLGVBQWUsRUFBRTtnQkFDMUMsTUFBTSxXQUFXLEdBQW9CLE9BQU8sQ0FBQyxDQUFDLENBQW9CLENBQUM7Z0JBQ25FLElBQUksR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksRUFBRTtvQkFDMUIsV0FBVyxFQUFFLFdBQVcsQ0FBQyxXQUFXO29CQUNwQyxVQUFVLEVBQUUsV0FBVyxDQUFDLFVBQVU7b0JBQ2xDLFFBQVEsRUFBRSxXQUFXLENBQUMsUUFBUTtvQkFDOUIsVUFBVSxFQUFFLFdBQVcsQ0FBQyxVQUFVO2lCQUMxQixDQUFDLENBQUM7Z0JBQ1gsK0RBQStEO2FBQy9EO1lBRUQseUJBQXlCO1lBQ3pCLElBQUksSUFBSSxDQUFDLFVBQVUsS0FBSyxJQUFJLEVBQUU7Z0JBQzdCLGdGQUFnRjtnQkFDaEYsT0FBTyxDQUFDLElBQUksRUFBRSxXQUFXLEVBQUUseUNBQXlDLENBQUMsQ0FBQzthQUN0RTtZQUVELHlDQUF5QztZQUN6QyxzRUFBc0U7WUFDdEUsaUJBQWlCO1lBQ2pCLDJDQUEyQztZQUMzQyx1QkFBdUI7WUFDdkIsV0FBVztZQUNYLEtBQUs7WUFDTCxJQUFJO1lBRUoseUZBQXlGO1lBQ3pGLElBQUksUUFBUSxHQUFHLElBQUksQ0FBQyxxQkFBcUIsQ0FBQyxlQUFlLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRSxZQUFZLENBQUMsQ0FBQztZQUN2RiwrREFBK0Q7WUFDL0QsSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxDQUFDLEVBQUU7Z0JBQzFCLElBQUksR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDLGlCQUFpQixDQUFDLElBQUksRUFBRSxRQUFRLENBQUMsQ0FBQztnQkFDOUQsSUFBSSxHQUFHLElBQUksQ0FBQyxlQUFlLENBQUMsZ0NBQWdDLENBQUMsSUFBSSxFQUFFLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQztnQkFDdEYsUUFBUSxHQUFHLElBQUksQ0FBQyxzQkFBc0IsQ0FBQztnQkFDdkMsNEZBQTRGO2FBQzVGO1lBRUQsSUFBSSxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsV0FBVyxDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQztZQUNsRCwrREFBK0Q7WUFDL0QsSUFBSSxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsWUFBWSxDQUFDLElBQUksRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1lBQ3ZFLGtCQUFrQjtZQUNsQiw4QkFBOEI7WUFDOUIsMkNBQTJDO1lBQzNDLFNBQVM7WUFDVCw2QkFBNkI7WUFDN0IsZUFBZTtZQUNmLG1CQUFtQjtZQUNuQixLQUFLO1lBQ0wsbUZBQW1GO1lBQ25GLElBQUksSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLEdBQUcsQ0FBQyxFQUFFO2dCQUN4QixJQUFJLEdBQUcsSUFBSSxDQUFDLGtCQUFrQixDQUFDLDRCQUE0QixDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUNsRSxrQkFBa0I7Z0JBQ2xCLDhDQUE4QztnQkFDOUMsMkNBQTJDO2dCQUMzQyxTQUFTO2dCQUNULDZCQUE2QjtnQkFDN0IsZUFBZTtnQkFDZixtQkFBbUI7Z0JBQ25CLEtBQUs7Z0JBQ0wsMkVBQTJFO2dCQUMzRSxJQUFJLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixDQUFDLDJCQUEyQixDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUNoRSxrQkFBa0I7Z0JBQ2xCLDZDQUE2QztnQkFDN0MsMkNBQTJDO2dCQUMzQyxTQUFTO2dCQUNULDZCQUE2QjtnQkFDN0IsZUFBZTtnQkFDZixtQkFBbUI7Z0JBQ25CLEtBQUs7Z0JBQ0wsMEVBQTBFO2dCQUMxRSxJQUFJLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyx1QkFBdUIsQ0FBQyxJQUFJLENBQUMsQ0FBQztnQkFDeEQsa0JBQWtCO2dCQUNsQix5Q0FBeUM7Z0JBQ3pDLDJDQUEyQztnQkFDM0MsU0FBUztnQkFDVCw2QkFBNkI7Z0JBQzdCLGVBQWU7Z0JBQ2YsbUJBQW1CO2dCQUNuQixLQUFLO2dCQUNMLHNFQUFzRTtnQkFDdEUsSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxDQUFDLEVBQUU7b0JBQzFCLElBQUksR0FBRyxJQUFJLENBQUMsY0FBYyxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsQ0FBQztpQkFDaEQ7Z0JBQ0Qsd0VBQXdFO2dCQUN4RSxJQUFJLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQzdDLHNFQUFzRTtnQkFDdEUsSUFBSSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsNkJBQTZCLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ3pELDRFQUE0RTtnQkFDNUUsSUFBSSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsMEJBQTBCLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ3RELGtCQUFrQjtnQkFDbEIsNENBQTRDO2dCQUM1QywyQ0FBMkM7Z0JBQzNDLFNBQVM7Z0JBQ1QsNkJBQTZCO2dCQUM3QixlQUFlO2dCQUNmLG1CQUFtQjtnQkFDbkIsS0FBSztnQkFDTCx5RUFBeUU7Z0JBQ3pFLHVCQUF1QjtnQkFDdkIsY0FBYztnQkFDZCx5Q0FBeUM7Z0JBQ3pDLHVEQUF1RDtnQkFDdkQsSUFBSTtnQkFDSixhQUFhO2dCQUNiLHdDQUF3QztnQkFDeEMsZ0NBQWdDO2dCQUNoQyxtQkFBbUI7Z0JBQ25CLCtCQUErQjtnQkFDL0Isd0NBQXdDO2dCQUN4QywwQ0FBMEM7Z0JBQzFDLE1BQU07Z0JBQ04sSUFBSTtnQkFFSixNQUFNLENBQUMsSUFBSSxFQUFFLFdBQVcsRUFBRSxjQUFjLEdBQUcsT0FBTyxFQUFFLENBQUMsQ0FBQzthQUN0RDtpQkFBTTtnQkFDTix5QkFBeUI7Z0JBQ3pCLGNBQWM7Z0JBQ2QsMEZBQTBGO2dCQUMxRix1REFBdUQ7Z0JBQ3ZELElBQUk7Z0JBQ0osYUFBYTthQUNiO1NBQ0Q7UUFDRCwwQ0FBMEM7UUFDMUMsT0FBTyxDQUFDLElBQUksRUFBRSxXQUFXLEVBQUUsc0JBQXNCLENBQUMsQ0FBQztJQUNwRCxDQUFDO0lBRU8sT0FBTyxDQUFJLElBQVksRUFBRSxLQUFhLEVBQUUsTUFBVTtRQUN6RCx5RUFBeUU7UUFDekUsT0FBTyxNQUFNLENBQUM7SUFDZixDQUFDOzs4R0F4UFcsaUJBQWlCO2tIQUFqQixpQkFBaUIsY0FGakIsTUFBTTsyRkFFTixpQkFBaUI7a0JBSDdCLFVBQVU7bUJBQUM7b0JBQ1gsVUFBVSxFQUFFLE1BQU07aUJBQ2xCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQgeyBNYXAgfSBmcm9tICdpbW11dGFibGUnO1xyXG5pbXBvcnQgeyBPYnNlcnZhYmxlIH0gZnJvbSAncnhqcyc7XHJcbmltcG9ydCB7IEdhbWUgfSBmcm9tICcuLi9tb2RlbHMvZ2FtZS9nYW1lJztcclxuaW1wb3J0IHsgSGlzdG9yeUl0ZW0gfSBmcm9tICcuLi9tb2RlbHMvaGlzdG9yeS9oaXN0b3J5LWl0ZW0nO1xyXG5pbXBvcnQgeyBBY3Rpb25QYXJzZXJDb25maWcsIEdhbWVIaXN0b3J5SXRlbSB9IGZyb20gJy4uL21vZGVscy9tb2RlbHMnO1xyXG5pbXBvcnQgeyBBbGxDYXJkc1NlcnZpY2UgfSBmcm9tICcuL2FsbC1jYXJkcy5zZXJ2aWNlJztcclxuaW1wb3J0IHsgR2FtZVBvcHVsYXRpb25TZXJ2aWNlIH0gZnJvbSAnLi9lbnRpdGllc3BpcGVsaW5lL2dhbWUtcG9wdWxhdGlvbi5zZXJ2aWNlJztcclxuaW1wb3J0IHsgR2FtZVN0YXRlUGFyc2VyU2VydmljZSB9IGZyb20gJy4vZW50aXRpZXNwaXBlbGluZS9nYW1lLXN0YXRlLXBhcnNlci5zZXJ2aWNlJztcclxuaW1wb3J0IHsgQWN0aW9uUGFyc2VyU2VydmljZSB9IGZyb20gJy4vZ2FtZXBpcGVsaW5lL2FjdGlvbi1wYXJzZXIuc2VydmljZSc7XHJcbmltcG9ydCB7IEFjdGl2ZVBsYXllclBhcnNlclNlcnZpY2UgfSBmcm9tICcuL2dhbWVwaXBlbGluZS9hY3RpdmUtcGxheWVyLXBhcnNlci5zZXJ2aWNlJztcclxuaW1wb3J0IHsgQWN0aXZlU3BlbGxQYXJzZXJTZXJ2aWNlIH0gZnJvbSAnLi9nYW1lcGlwZWxpbmUvYWN0aXZlLXNwZWxsLXBhcnNlci5zZXJ2aWNlJztcclxuaW1wb3J0IHsgRW5kR2FtZVBhcnNlclNlcnZpY2UgfSBmcm9tICcuL2dhbWVwaXBlbGluZS9lbmQtZ2FtZS1wYXJzZXIuc2VydmljZSc7XHJcbmltcG9ydCB7IEdhbWVJbml0aWFsaXplclNlcnZpY2UgfSBmcm9tICcuL2dhbWVwaXBlbGluZS9nYW1lLWluaXRpYWxpemVyLnNlcnZpY2UnO1xyXG5pbXBvcnQgeyBNdWxsaWdhblBhcnNlclNlcnZpY2UgfSBmcm9tICcuL2dhbWVwaXBlbGluZS9tdWxsaWdhbi1wYXJzZXIuc2VydmljZSc7XHJcbmltcG9ydCB7IE5hcnJhdG9yU2VydmljZSB9IGZyb20gJy4vZ2FtZXBpcGVsaW5lL25hcnJhdG9yLnNlcnZpY2UnO1xyXG5pbXBvcnQgeyBUYXJnZXRzUGFyc2VyU2VydmljZSB9IGZyb20gJy4vZ2FtZXBpcGVsaW5lL3RhcmdldHMtcGFyc2VyLnNlcnZpY2UnO1xyXG5pbXBvcnQgeyBUdXJuUGFyc2VyU2VydmljZSB9IGZyb20gJy4vZ2FtZXBpcGVsaW5lL3R1cm4tcGFyc2VyLnNlcnZpY2UnO1xyXG5pbXBvcnQgeyBJbWFnZVByZWxvYWRlclNlcnZpY2UgfSBmcm9tICcuL2ltYWdlLXByZWxvYWRlci5zZXJ2aWNlJztcclxuaW1wb3J0IHsgU3RhdGVQcm9jZXNzb3JTZXJ2aWNlIH0gZnJvbSAnLi9zdGF0ZS1wcm9jZXNzb3Iuc2VydmljZSc7XHJcbmltcG9ydCB7IFhtbFBhcnNlclNlcnZpY2UgfSBmcm9tICcuL3htbC1wYXJzZXIuc2VydmljZSc7XHJcblxyXG5jb25zdCBTTUFMTF9QQVVTRSA9IDE1O1xyXG5cclxuQEluamVjdGFibGUoe1xyXG5cdHByb3ZpZGVkSW46ICdyb290JyxcclxufSlcclxuZXhwb3J0IGNsYXNzIEdhbWVQYXJzZXJTZXJ2aWNlIHtcclxuXHRjb25zdHJ1Y3RvcihcclxuXHRcdHByaXZhdGUgYWxsQ2FyZHM6IEFsbENhcmRzU2VydmljZSxcclxuXHRcdHByaXZhdGUgYWN0aW9uUGFyc2VyOiBBY3Rpb25QYXJzZXJTZXJ2aWNlLFxyXG5cdFx0cHJpdmF0ZSB0dXJuUGFyc2VyOiBUdXJuUGFyc2VyU2VydmljZSxcclxuXHRcdHByaXZhdGUgaW1hZ2VQcmVsb2FkZXI6IEltYWdlUHJlbG9hZGVyU2VydmljZSxcclxuXHRcdHByaXZhdGUgZ2FtZVBvcHVsYXRpb25TZXJ2aWNlOiBHYW1lUG9wdWxhdGlvblNlcnZpY2UsXHJcblx0XHRwcml2YXRlIGdhbWVTdGF0ZVBhcnNlcjogR2FtZVN0YXRlUGFyc2VyU2VydmljZSxcclxuXHRcdHByaXZhdGUgZ2FtZUluaXRpYWxpemVyOiBHYW1lSW5pdGlhbGl6ZXJTZXJ2aWNlLFxyXG5cdFx0cHJpdmF0ZSBhY3RpdmVQbGF5ZXJQYXJzZXI6IEFjdGl2ZVBsYXllclBhcnNlclNlcnZpY2UsXHJcblx0XHRwcml2YXRlIGFjdGl2ZVNwZWxsUGFyc2VyOiBBY3RpdmVTcGVsbFBhcnNlclNlcnZpY2UsXHJcblx0XHRwcml2YXRlIHRhcmdldHNQYXJzZXI6IFRhcmdldHNQYXJzZXJTZXJ2aWNlLFxyXG5cdFx0cHJpdmF0ZSBtdWxsaWdhblBhcnNlcjogTXVsbGlnYW5QYXJzZXJTZXJ2aWNlLFxyXG5cdFx0cHJpdmF0ZSBlbmRHYW1lUGFyc2VyOiBFbmRHYW1lUGFyc2VyU2VydmljZSxcclxuXHRcdHByaXZhdGUgbmFycmF0b3I6IE5hcnJhdG9yU2VydmljZSxcclxuXHRcdHByaXZhdGUgc3RhdGVQcm9jZXNzb3I6IFN0YXRlUHJvY2Vzc29yU2VydmljZSxcclxuXHQpIHt9XHJcblx0cHJpdmF0ZSBjYW5jZWxsZWQ6IGJvb2xlYW47XHJcblx0cHJpdmF0ZSBwcm9jZXNzaW5nVGltZW91dDtcclxuXHJcblx0cHVibGljIGFzeW5jIHBhcnNlKFxyXG5cdFx0cmVwbGF5QXNTdHJpbmc6IHN0cmluZyxcclxuXHRcdG9wdGlvbnM/OiBUZWNobmljYWxQYXJzaW5nT3B0aW9ucyxcclxuXHRcdGNvbmZpZzogQWN0aW9uUGFyc2VyQ29uZmlnID0gbmV3IEFjdGlvblBhcnNlckNvbmZpZygpLFxyXG5cdCk6IFByb21pc2U8T2JzZXJ2YWJsZTxbR2FtZSwgc3RyaW5nLCBib29sZWFuXT4+IHtcclxuXHRcdGNvbnN0IHN0YXJ0ID0gRGF0ZS5ub3coKTtcclxuXHRcdHRoaXMuY2FuY2VsbGVkID0gZmFsc2U7XHJcblx0XHRpZiAodGhpcy5wcm9jZXNzaW5nVGltZW91dCkge1xyXG5cdFx0XHRjbGVhclRpbWVvdXQodGhpcy5wcm9jZXNzaW5nVGltZW91dCk7XHJcblx0XHRcdHRoaXMucHJvY2Vzc2luZ1RpbWVvdXQgPSB1bmRlZmluZWQ7XHJcblx0XHR9XHJcblxyXG5cdFx0aWYgKCF0aGlzLmFsbENhcmRzLmdldENhcmRzKCk/Lmxlbmd0aCkge1xyXG5cdFx0XHRhd2FpdCB0aGlzLmFsbENhcmRzLmluaXRpYWxpemVDYXJkc0RiKCk7XHJcblx0XHRcdHRoaXMubG9nUGVyZignUmV0cmlldmVkIGNhcmRzIERCLCBwYXJzaW5nIHJlcGxheScsIHN0YXJ0KTtcclxuXHRcdH1cclxuXHJcblx0XHRjb25zdCBpdGVyYXRvcjogSXRlcmFibGVJdGVyYXRvcjxbR2FtZSwgbnVtYmVyLCBzdHJpbmddPiA9IHRoaXMuY3JlYXRlR2FtZVBpcGVsaW5lKFxyXG5cdFx0XHRyZXBsYXlBc1N0cmluZyxcclxuXHRcdFx0c3RhcnQsXHJcblx0XHRcdG9wdGlvbnMsXHJcblx0XHRcdGNvbmZpZyxcclxuXHRcdCk7XHJcblx0XHRyZXR1cm4gT2JzZXJ2YWJsZS5jcmVhdGUob2JzZXJ2ZXIgPT4ge1xyXG5cdFx0XHR0aGlzLmJ1aWxkT2JzZXJ2YWJsZUZ1bmN0aW9uKG9ic2VydmVyLCBpdGVyYXRvcik7XHJcblx0XHR9KTtcclxuXHR9XHJcblxyXG5cdHB1YmxpYyBjYW5jZWxQcm9jZXNzaW5nKCk6IHZvaWQge1xyXG5cdFx0dGhpcy5jYW5jZWxsZWQgPSB0cnVlO1xyXG5cdFx0Y2xlYXJUaW1lb3V0KHRoaXMucHJvY2Vzc2luZ1RpbWVvdXQpO1xyXG5cdH1cclxuXHJcblx0cHJpdmF0ZSBidWlsZE9ic2VydmFibGVGdW5jdGlvbihvYnNlcnZlciwgaXRlcmF0b3I6IEl0ZXJhYmxlSXRlcmF0b3I8W0dhbWUsIG51bWJlciwgc3RyaW5nXT4pIHtcclxuXHRcdC8vIC8vIGNvbnNvbGUubG9nKCdjYWxsaW5nIG5leHQgaXRlcmF0aW9uJyk7XHJcblx0XHR0cnkge1xyXG5cdFx0XHRjb25zdCBpdFZhbHVlID0gaXRlcmF0b3IubmV4dCgpO1xyXG5cdFx0XHQvLyAvLyBjb25zb2xlLmxvZygnY2FsbGluZyBuZXh0IG9iZXJzYWJsZScsIGl0VmFsdWUsIGl0VmFsdWUudmFsdWUpO1xyXG5cdFx0XHRvYnNlcnZlci5uZXh0KFtpdFZhbHVlLnZhbHVlWzBdLCBpdFZhbHVlLnZhbHVlWzJdLCBpdFZhbHVlLmRvbmVdKTtcclxuXHRcdFx0aWYgKCFpdFZhbHVlLmRvbmUgJiYgIXRoaXMuY2FuY2VsbGVkKSB7XHJcblx0XHRcdFx0dGhpcy5wcm9jZXNzaW5nVGltZW91dCA9IHNldFRpbWVvdXQoXHJcblx0XHRcdFx0XHQoKSA9PiB0aGlzLmJ1aWxkT2JzZXJ2YWJsZUZ1bmN0aW9uKG9ic2VydmVyLCBpdGVyYXRvciksXHJcblx0XHRcdFx0XHRpdFZhbHVlLnZhbHVlWzFdLFxyXG5cdFx0XHRcdCk7XHJcblx0XHRcdH1cclxuXHRcdH0gY2F0Y2ggKGUpIHtcclxuXHRcdFx0Y29uc29sZS5lcnJvcignW2dhbWUtcGFyc2VyXSBFeGNlcHRpb24gaW4gYnVpbGRPYnNlcnZhYmxlRnVuY3Rpb24nLCBlKTtcclxuXHRcdH1cclxuXHR9XHJcblxyXG5cdHByaXZhdGUgKmNyZWF0ZUdhbWVQaXBlbGluZShcclxuXHRcdHJlcGxheUFzU3RyaW5nOiBzdHJpbmcsXHJcblx0XHRzdGFydDogbnVtYmVyLFxyXG5cdFx0b3B0aW9uczogVGVjaG5pY2FsUGFyc2luZ09wdGlvbnMsXHJcblx0XHRjb25maWc6IEFjdGlvblBhcnNlckNvbmZpZyxcclxuXHQpOiBJdGVyYWJsZUl0ZXJhdG9yPFtHYW1lLCBudW1iZXIsIHN0cmluZ10+IHtcclxuXHRcdGlmICghcmVwbGF5QXNTdHJpbmcgfHwgcmVwbGF5QXNTdHJpbmcubGVuZ3RoID09IDApIHtcclxuXHRcdFx0cmV0dXJuIFtudWxsLCBTTUFMTF9QQVVTRSwgJ0ludmFsaWQgWE1MIHJlcGxheSddO1xyXG5cdFx0fVxyXG5cclxuXHRcdC8vIGNvbnNvbGUubG9nKCdbZ2FtZS1wYXJzZXJdIHByZXBhcmluZyBlbnRpdHkgLyBhY3JkIElEIG1hcHBpbmcnKTtcclxuXHRcdGxldCBlbnRpdHlDYXJkSWQ6IE1hcDxudW1iZXIsIHN0cmluZz4gPSBNYXAoW10pO1xyXG5cdFx0Y29uc3QgZnVsbEVudGl0eUlkQ2FyZElkTWF0Y2hlciA9IG5ldyBSZWdFeHAoL2lkPVwiKC4qPylcIiBjYXJkSUQ9XCIoLio/KVwiL2cpO1xyXG5cdFx0Y29uc3QgZnVsbEVudGl0eU1hdGNoUmVzdWx0ID0gcmVwbGF5QXNTdHJpbmcubWF0Y2goZnVsbEVudGl0eUlkQ2FyZElkTWF0Y2hlcik7XHJcblx0XHRmb3IgKGxldCBtYXRjaCBvZiBmdWxsRW50aXR5TWF0Y2hSZXN1bHQpIHtcclxuXHRcdFx0Y29uc3QgcmVzdWx0ID0gbmV3IFJlZ0V4cCgvaWQ9XCIoLio/KVwiIGNhcmRJRD1cIiguKj8pXCIvZykuZXhlYyhtYXRjaCk7XHJcblx0XHRcdGlmIChyZXN1bHQpIHtcclxuXHRcdFx0XHRlbnRpdHlDYXJkSWQgPSBlbnRpdHlDYXJkSWQuc2V0KHBhcnNlSW50KHJlc3VsdFsxXSksIHJlc3VsdFsyXSk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHRcdGNvbnN0IHNob3dFbnRpdHlJZENhcmRJZE1hdGNoZXIgPSBuZXcgUmVnRXhwKC9jYXJkSUQ9XCIoLio/KVwiIGVudGl0eT1cIiguKj8pXCIvZyk7XHJcblx0XHRjb25zdCBzaG93RW50aXR5TWF0Y2hSZXN1bHQgPSByZXBsYXlBc1N0cmluZy5tYXRjaChzaG93RW50aXR5SWRDYXJkSWRNYXRjaGVyKTtcclxuXHRcdGZvciAobGV0IG1hdGNoIG9mIHNob3dFbnRpdHlNYXRjaFJlc3VsdCkge1xyXG5cdFx0XHQvLyAvLyBjb25zb2xlLmxvZyhcInVwZGF0aW5nIHdpdGggc2hvdycsIHJlc3VsdFwiLCBjb3B5KTtcclxuXHRcdFx0Y29uc3QgcmVzdWx0ID0gbmV3IFJlZ0V4cCgvY2FyZElEPVwiKC4qPylcIiBlbnRpdHk9XCIoLio/KVwiL2cpLmV4ZWMobWF0Y2gpO1xyXG5cdFx0XHRpZiAocmVzdWx0KSB7XHJcblx0XHRcdFx0Ly8gLy8gY29uc29sZS5sb2coJ3Jlc3VsdCcsIHJlc3VsdCk7XHJcblx0XHRcdFx0ZW50aXR5Q2FyZElkID0gZW50aXR5Q2FyZElkLnNldChwYXJzZUludChyZXN1bHRbMl0pLCByZXN1bHRbMV0pO1xyXG5cdFx0XHR9XHJcblx0XHR9XHJcblx0XHQvLyBjb25zb2xlLmxvZygnW2dhbWUtcGFyc2VyXSBtYXBwaW5nIGRvbmUnLCBlbnRpdHlDYXJkSWQuc2l6ZSk7XHJcblxyXG5cdFx0Ly8gRG8gdGhlIHBhcnNpbmcgdHVybiBieSB0dXJuXHJcblx0XHQvLyBsZXQgaGlzdG9yeTogcmVhZG9ubHkgSGlzdG9yeUl0ZW1bXTtcclxuXHRcdGNvbnN0IHhtbFBhcnNpbmdJdGVyYXRvcjogSXRlcmFibGVJdGVyYXRvcjxyZWFkb25seSBIaXN0b3J5SXRlbVtdPiA9IG5ldyBYbWxQYXJzZXJTZXJ2aWNlKCkucGFyc2VYbWwoXHJcblx0XHRcdHJlcGxheUFzU3RyaW5nLFxyXG5cdFx0KTtcclxuXHRcdGxldCBnYW1lOiBHYW1lID0gR2FtZS5jcmVhdGVHYW1lKHt9IGFzIEdhbWUpO1xyXG5cdFx0bGV0IGNvdW50ZXIgPSAwO1xyXG5cdFx0d2hpbGUgKHRydWUpIHtcclxuXHRcdFx0Y29uc3QgaXRWYWx1ZSA9IHhtbFBhcnNpbmdJdGVyYXRvci5uZXh0KCk7XHJcblx0XHRcdGNvbnN0IGhpc3Rvcnk6IHJlYWRvbmx5IEhpc3RvcnlJdGVtW10gPSBpdFZhbHVlLnZhbHVlO1xyXG5cdFx0XHQvLyBjb25zb2xlLmRlYnVnKCdbZ2FtZS1wYXJzZXJdIHBhcnNpbmcgZm9yJywgY291bnRlciwgJ3dpdGggaGlzdG9yeSBsZW5ndGgnLCBoaXN0b3J5Lmxlbmd0aCk7XHJcblxyXG5cdFx0XHRpZiAoIWhpc3RvcnkgfHwgaXRWYWx1ZS5kb25lKSB7XHJcblx0XHRcdFx0Ly8gY29uc29sZS5kZWJ1ZygnW2dhbWUtcGFyc2VyXSBoaXN0b3J5IHBhcnNpbmcgb3ZlcicsIGl0VmFsdWUpO1xyXG5cdFx0XHRcdGJyZWFrO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHRpZiAoaGlzdG9yeVswXSBpbnN0YW5jZW9mIEdhbWVIaXN0b3J5SXRlbSkge1xyXG5cdFx0XHRcdGNvbnN0IGdhbWVIaXN0b3J5OiBHYW1lSGlzdG9yeUl0ZW0gPSBoaXN0b3J5WzBdIGFzIEdhbWVIaXN0b3J5SXRlbTtcclxuXHRcdFx0XHRnYW1lID0gT2JqZWN0LmFzc2lnbihnYW1lLCB7XHJcblx0XHRcdFx0XHRidWlsZE51bWJlcjogZ2FtZUhpc3RvcnkuYnVpbGROdW1iZXIsXHJcblx0XHRcdFx0XHRmb3JtYXRUeXBlOiBnYW1lSGlzdG9yeS5mb3JtYXRUeXBlLFxyXG5cdFx0XHRcdFx0Z2FtZVR5cGU6IGdhbWVIaXN0b3J5LmdhbWVUeXBlLFxyXG5cdFx0XHRcdFx0c2NlbmFyaW9JRDogZ2FtZUhpc3Rvcnkuc2NlbmFyaW9JRCxcclxuXHRcdFx0XHR9IGFzIEdhbWUpO1xyXG5cdFx0XHRcdC8vIGNvbnNvbGUubG9nKCdbZ2FtZS1wYXJzZXJdIGFzc2lnbiBtZXRhIGRhdGEgdG8gZ2FtZScsIGdhbWUpO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyBCYXR0bGVncm91bmRzIHR1dG9yaWFsXHJcblx0XHRcdGlmIChnYW1lLnNjZW5hcmlvSUQgPT09IDM1MzkpIHtcclxuXHRcdFx0XHQvLyBjb25zb2xlLmxvZygnW2dhbWUtcGFyc2VyXSBCYXR0bGVncm91bmRzIHR1dG9yaWFsIG5vdCBzdXBwb3J0ZWQsIHJldHVybmluZycpO1xyXG5cdFx0XHRcdHJldHVybiBbbnVsbCwgU01BTExfUEFVU0UsICdCYXRsbGVncm91bmRzIHR1dG9yaWFsIGlzIG5vdCBzdXBwb3J0ZWQnXTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Ly8gUHJlbG9hZCB0aGUgaW1hZ2VzIHdlJ2xsIG5lZWQgZWFybHkgb25cclxuXHRcdFx0Ly8gY29uc3QgcHJlbG9hZEl0ZXJhdG9yID0gdGhpcy5pbWFnZVByZWxvYWRlci5wcmVsb2FkSW1hZ2VzKGhpc3RvcnkpO1xyXG5cdFx0XHQvLyB3aGlsZSAodHJ1ZSkge1xyXG5cdFx0XHQvLyBcdGNvbnN0IGl0VmFsdWUgPSBwcmVsb2FkSXRlcmF0b3IubmV4dCgpO1xyXG5cdFx0XHQvLyBcdGlmIChpdFZhbHVlLmRvbmUpIHtcclxuXHRcdFx0Ly8gXHRcdGJyZWFrO1xyXG5cdFx0XHQvLyBcdH1cclxuXHRcdFx0Ly8gfVxyXG5cclxuXHRcdFx0Ly8gY29uc29sZS5sb2coJ1tnYW1lLXBhcnNlcl0gd2lsbCBpbml0TmV3RW50aXRpZXMnLCBnYW1lLCBoaXN0b3J5LCBlbnRpdHlDYXJkSWQudG9KUygpKTtcclxuXHRcdFx0bGV0IGVudGl0aWVzID0gdGhpcy5nYW1lUG9wdWxhdGlvblNlcnZpY2UuaW5pdE5ld0VudGl0aWVzKGdhbWUsIGhpc3RvcnksIGVudGl0eUNhcmRJZCk7XHJcblx0XHRcdC8vIGNvbnNvbGUubG9nKCdbZ2FtZS1wYXJzZXJdIGluaXROZXdFbnRpdGllcycsIGVudGl0aWVzLnNpemUpO1xyXG5cdFx0XHRpZiAoZ2FtZS50dXJucy5zaXplID09PSAwKSB7XHJcblx0XHRcdFx0Z2FtZSA9IHRoaXMuZ2FtZUluaXRpYWxpemVyLmluaXRpYWxpemVQbGF5ZXJzKGdhbWUsIGVudGl0aWVzKTtcclxuXHRcdFx0XHRnYW1lID0gdGhpcy5nYW1lU3RhdGVQYXJzZXIudXBkYXRlRW50aXRpZXNVbnRpbE11bGxpZ2FuU3RhdGUoZ2FtZSwgZW50aXRpZXMsIGhpc3RvcnkpO1xyXG5cdFx0XHRcdGVudGl0aWVzID0gZ2FtZS5lbnRpdGllc0JlZm9yZU11bGxpZ2FuO1xyXG5cdFx0XHRcdC8vIC8vIGNvbnNvbGUubG9nKCdnYW1lIGFmdGVyIHBvcHVsYXRlRW50aXRpZXNVbnRpbE11bGxpZ2FuU3RhdGUnLCBnYW1lLCBnYW1lLnR1cm5zLnRvSlMoKSk7XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdGdhbWUgPSB0aGlzLnR1cm5QYXJzZXIuY3JlYXRlVHVybnMoZ2FtZSwgaGlzdG9yeSk7XHJcblx0XHRcdC8vIC8vIGNvbnNvbGUubG9nKCdnYW1lIGFmdGVyIHR1cm4gY3JlYXRpb24nLCBnYW1lLnR1cm5zLnNpemUpO1xyXG5cdFx0XHRnYW1lID0gdGhpcy5hY3Rpb25QYXJzZXIucGFyc2VBY3Rpb25zKGdhbWUsIGVudGl0aWVzLCBoaXN0b3J5LCBjb25maWcpO1xyXG5cdFx0XHQvLyAvLyBjb25zb2xlLmxvZyhcclxuXHRcdFx0Ly8gXHQnZW50aXR5IDE1MCBwYXJzZUFjdGlvbnMnLFxyXG5cdFx0XHQvLyBcdGdhbWUuZ2V0TGF0ZXN0UGFyc2VkU3RhdGUoKS5nZXQoMTUwKSAmJlxyXG5cdFx0XHQvLyBcdFx0Z2FtZVxyXG5cdFx0XHQvLyBcdFx0XHQuZ2V0TGF0ZXN0UGFyc2VkU3RhdGUoKVxyXG5cdFx0XHQvLyBcdFx0XHQuZ2V0KDE1MClcclxuXHRcdFx0Ly8gXHRcdFx0LnRhZ3MudG9KUygpLFxyXG5cdFx0XHQvLyApO1xyXG5cdFx0XHQvLyAvLyBjb25zb2xlLmxvZygnZ2FtZSBhZnRlciBhY3Rpb24gcGFzcmluZycsIGdhbWUuZ2V0TGF0ZXN0UGFyc2VkU3RhdGUoKS50b0pTKCkpO1xyXG5cdFx0XHRpZiAoZ2FtZS50dXJucy5zaXplID4gMCkge1xyXG5cdFx0XHRcdGdhbWUgPSB0aGlzLmFjdGl2ZVBsYXllclBhcnNlci5wYXJzZUFjdGl2ZVBsYXllckZvckxhc3RUdXJuKGdhbWUpO1xyXG5cdFx0XHRcdC8vIC8vIGNvbnNvbGUubG9nKFxyXG5cdFx0XHRcdC8vIFx0J2VudGl0eSAxNTAgcGFyc2VBY3RpdmVQbGF5ZXJGb3JMYXN0VHVybicsXHJcblx0XHRcdFx0Ly8gXHRnYW1lLmdldExhdGVzdFBhcnNlZFN0YXRlKCkuZ2V0KDE1MCkgJiZcclxuXHRcdFx0XHQvLyBcdFx0Z2FtZVxyXG5cdFx0XHRcdC8vIFx0XHRcdC5nZXRMYXRlc3RQYXJzZWRTdGF0ZSgpXHJcblx0XHRcdFx0Ly8gXHRcdFx0LmdldCgxNTApXHJcblx0XHRcdFx0Ly8gXHRcdFx0LnRhZ3MudG9KUygpLFxyXG5cdFx0XHRcdC8vICk7XHJcblx0XHRcdFx0Ly8gLy8gY29uc29sZS5sb2coJ2dhbWUgYWZ0ZXIgcGFyc2VBY3RpdmVQbGF5ZXInLCBnYW1lLCBnYW1lLnR1cm5zLnRvSlMoKSk7XHJcblx0XHRcdFx0Z2FtZSA9IHRoaXMuYWN0aXZlU3BlbGxQYXJzZXIucGFyc2VBY3RpdmVTcGVsbEZvckxhc3RUdXJuKGdhbWUpO1xyXG5cdFx0XHRcdC8vIC8vIGNvbnNvbGUubG9nKFxyXG5cdFx0XHRcdC8vIFx0J2VudGl0eSAxNTAgcGFyc2VBY3RpdmVTcGVsbEZvckxhc3RUdXJuJyxcclxuXHRcdFx0XHQvLyBcdGdhbWUuZ2V0TGF0ZXN0UGFyc2VkU3RhdGUoKS5nZXQoMTUwKSAmJlxyXG5cdFx0XHRcdC8vIFx0XHRnYW1lXHJcblx0XHRcdFx0Ly8gXHRcdFx0LmdldExhdGVzdFBhcnNlZFN0YXRlKClcclxuXHRcdFx0XHQvLyBcdFx0XHQuZ2V0KDE1MClcclxuXHRcdFx0XHQvLyBcdFx0XHQudGFncy50b0pTKCksXHJcblx0XHRcdFx0Ly8gKTtcclxuXHRcdFx0XHQvLyAvLyBjb25zb2xlLmxvZygnZ2FtZSBhZnRlciBwYXJzZUFjdGl2ZVNwZWxsJywgZ2FtZSwgZ2FtZS50dXJucy50b0pTKCkpO1xyXG5cdFx0XHRcdGdhbWUgPSB0aGlzLnRhcmdldHNQYXJzZXIucGFyc2VUYXJnZXRzRm9yTGFzdFR1cm4oZ2FtZSk7XHJcblx0XHRcdFx0Ly8gLy8gY29uc29sZS5sb2coXHJcblx0XHRcdFx0Ly8gXHQnZW50aXR5IDE1MCBwYXJzZVRhcmdldHNGb3JMYXN0VHVybicsXHJcblx0XHRcdFx0Ly8gXHRnYW1lLmdldExhdGVzdFBhcnNlZFN0YXRlKCkuZ2V0KDE1MCkgJiZcclxuXHRcdFx0XHQvLyBcdFx0Z2FtZVxyXG5cdFx0XHRcdC8vIFx0XHRcdC5nZXRMYXRlc3RQYXJzZWRTdGF0ZSgpXHJcblx0XHRcdFx0Ly8gXHRcdFx0LmdldCgxNTApXHJcblx0XHRcdFx0Ly8gXHRcdFx0LnRhZ3MudG9KUygpLFxyXG5cdFx0XHRcdC8vICk7XHJcblx0XHRcdFx0Ly8gLy8gY29uc29sZS5sb2coJ2dhbWUgYWZ0ZXIgcGFyc2VUYXJnZXRzJywgZ2FtZSwgZ2FtZS50dXJucy50b0pTKCkpO1xyXG5cdFx0XHRcdGlmIChnYW1lLnR1cm5zLnNpemUgPT09IDEpIHtcclxuXHRcdFx0XHRcdGdhbWUgPSB0aGlzLm11bGxpZ2FuUGFyc2VyLmFmZmVjdE11bGxpZ2FuKGdhbWUpO1xyXG5cdFx0XHRcdH1cclxuXHRcdFx0XHQvLyAvLyBjb25zb2xlLmxvZygnZ2FtZSBhZnRlciBhZmZlY3RNdWxsaWdhbicsIGdhbWUsIGdhbWUudHVybnMudG9KUygpKTtcclxuXHRcdFx0XHRnYW1lID0gdGhpcy5lbmRHYW1lUGFyc2VyLnBhcnNlRW5kR2FtZShnYW1lKTtcclxuXHRcdFx0XHQvLyAvLyBjb25zb2xlLmxvZygnZ2FtZSBhZnRlciBwYXJzZUVuZEdhbWUnLCBnYW1lLCBnYW1lLnR1cm5zLnRvSlMoKSk7XHJcblx0XHRcdFx0Z2FtZSA9IHRoaXMubmFycmF0b3IucG9wdWxhdGVBY3Rpb25UZXh0Rm9yTGFzdFR1cm4oZ2FtZSk7XHJcblx0XHRcdFx0Ly8gLy8gY29uc29sZS5sb2coJ2dhbWUgYWZ0ZXIgcG9wdWxhdGVBY3Rpb25UZXh0JywgZ2FtZSwgZ2FtZS50dXJucy50b0pTKCkpO1xyXG5cdFx0XHRcdGdhbWUgPSB0aGlzLm5hcnJhdG9yLmNyZWF0ZUdhbWVTdG9yeUZvckxhc3RUdXJuKGdhbWUpO1xyXG5cdFx0XHRcdC8vIC8vIGNvbnNvbGUubG9nKFxyXG5cdFx0XHRcdC8vIFx0J2VudGl0eSAxNTAgY3JlYXRlR2FtZVN0b3J5Rm9yTGFzdFR1cm4nLFxyXG5cdFx0XHRcdC8vIFx0Z2FtZS5nZXRMYXRlc3RQYXJzZWRTdGF0ZSgpLmdldCgxNTApICYmXHJcblx0XHRcdFx0Ly8gXHRcdGdhbWVcclxuXHRcdFx0XHQvLyBcdFx0XHQuZ2V0TGF0ZXN0UGFyc2VkU3RhdGUoKVxyXG5cdFx0XHRcdC8vIFx0XHRcdC5nZXQoMTUwKVxyXG5cdFx0XHRcdC8vIFx0XHRcdC50YWdzLnRvSlMoKSxcclxuXHRcdFx0XHQvLyApO1xyXG5cdFx0XHRcdC8vIC8vIGNvbnNvbGUubG9nKCdnYW1lIGFmdGVyIGNyZWF0ZUdhbWVTdG9yeScsIGdhbWUsIGdhbWUudHVybnMudG9KUygpKTtcclxuXHRcdFx0XHQvLyBpZiAoY291bnRlciA9PT0gNCkge1xyXG5cdFx0XHRcdC8vIFx0Y291bnRlcisrO1xyXG5cdFx0XHRcdC8vIFx0Ly8gY29uc29sZS5sb2coJ3JldHVybmluZycsIGNvdW50ZXIpO1xyXG5cdFx0XHRcdC8vIFx0cmV0dXJuIFtnYW1lLCBTTUFMTF9QQVVTRSwgJ1JlbmRlcmluZyBnYW1lIHN0YXRlJ107XHJcblx0XHRcdFx0Ly8gfVxyXG5cdFx0XHRcdC8vIGNvdW50ZXIrKztcclxuXHRcdFx0XHQvLyAvLyBjb25zb2xlLmxvZygnbW92aW5nIG9uJywgY291bnRlcik7XHJcblx0XHRcdFx0Ly8gaWYgKGdhbWUudHVybnMuc2l6ZSA9PT0gMzMpIHtcclxuXHRcdFx0XHQvLyBcdC8vIGNvbnNvbGUubG9nKFxyXG5cdFx0XHRcdC8vIFx0XHQnZW50aXRpZXMgYXQgZW5kIG9mIHR1cm4nLFxyXG5cdFx0XHRcdC8vIFx0XHRnYW1lLmdldExhdGVzdFBhcnNlZFN0YXRlKCkudG9KUygpLFxyXG5cdFx0XHRcdC8vIFx0XHRnYW1lLmdldExhdGVzdFBhcnNlZFN0YXRlKCkuZ2V0KDUwNyksXHJcblx0XHRcdFx0Ly8gXHQpO1xyXG5cdFx0XHRcdC8vIH1cclxuXHJcblx0XHRcdFx0eWllbGQgW2dhbWUsIFNNQUxMX1BBVVNFLCAnUGFyc2VkIHR1cm4gJyArIGNvdW50ZXIrK107XHJcblx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0Ly8gaWYgKGNvdW50ZXIrKyA9PT0gMykge1xyXG5cdFx0XHRcdC8vIFx0Y291bnRlcisrO1xyXG5cdFx0XHRcdC8vIFx0Ly8gLy8gY29uc29sZS5sb2coJ3JldHVybmluZycsIGNvdW50ZXIsIGdhbWUuZW50aXRpZXMuZ2V0KDczKSwgZ2FtZS5lbnRpdGllcy5nZXQoNzQpKTtcclxuXHRcdFx0XHQvLyBcdHJldHVybiBbZ2FtZSwgU01BTExfUEFVU0UsICdSZW5kZXJpbmcgZ2FtZSBzdGF0ZSddO1xyXG5cdFx0XHRcdC8vIH1cclxuXHRcdFx0XHQvLyBjb3VudGVyKys7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHRcdC8vIGNvbnNvbGUubG9nKCdwYXJzaW5nIGRvbmUsIHJldHVybmluZycpO1xyXG5cdFx0cmV0dXJuIFtnYW1lLCBTTUFMTF9QQVVTRSwgJ1JlbmRlcmluZyBnYW1lIHN0YXRlJ107XHJcblx0fVxyXG5cclxuXHRwcml2YXRlIGxvZ1BlcmY8VD4od2hhdDogc3RyaW5nLCBzdGFydDogbnVtYmVyLCByZXN1bHQ/OiBUKTogVCB7XHJcblx0XHQvLyBjb25zb2xlLmxvZygnW3BlcmZdICcsIHdoYXQsICdkb25lIGFmdGVyICcsIERhdGUubm93KCkgLSBzdGFydCwgJ21zJyk7XHJcblx0XHRyZXR1cm4gcmVzdWx0O1xyXG5cdH1cclxufVxyXG5cclxuZXhwb3J0IGludGVyZmFjZSBHYW1lUHJvY2Vzc2luZ1N0ZXAge1xyXG5cdGdhbWU6IEdhbWU7XHJcblx0c2hvdWxkQnViYmxlOiBib29sZWFuO1xyXG59XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIFRlY2huaWNhbFBhcnNpbmdPcHRpb25zIHtcclxuXHRyZWFkb25seSBzaG91bGRZaWVsZDogbnVtYmVyO1xyXG5cdHJlYWRvbmx5IHNraXBVaTogYm9vbGVhbjtcclxufVxyXG4iXX0=