Files
rpiRadio/ProjectNow/NodeServer/node_modules/midi-player-js/module/midiplayer.js
2018-05-19 02:20:19 +02:00

1238 lines
96 KiB
JavaScript

(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.MidiPlayer = f()}})(function(){var define,module,exports;return (function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}return e})()({1:[function(require,module,exports){
'use strict';
/**
* Constants used in player.
*/
var Constants = {
VERSION: '2.0.3',
NOTES: [],
CIRCLE_OF_FOURTHS: ['C', 'F', 'Bb', 'Eb', 'Ab', 'Db', 'Gb', 'Cb', 'Fb', 'Bbb', 'Ebb', 'Abb'],
CIRCLE_OF_FIFTHS: ['C', 'G', 'D', 'A', 'E', 'B', 'F#', 'C#', 'G#', 'D#', 'A#', 'E#']
};
// Builds notes object for reference against binary values.
var allNotes = [['C'], ['C#', 'Db'], ['D'], ['D#', 'Eb'], ['E'], ['F'], ['F#', 'Gb'], ['G'], ['G#', 'Ab'], ['A'], ['A#', 'Bb'], ['B']];
var counter = 0;
// All available octaves.
var _loop = function _loop(i) {
allNotes.forEach(function (noteGroup) {
noteGroup.forEach(function (note) {
return Constants.NOTES[counter] = note + i;
});
counter++;
});
};
for (var i = -1; i <= 9; i++) {
_loop(i);
}
exports.Constants = Constants;
},{}],2:[function(require,module,exports){
"use strict";
var Player = require("./player");
var Utils = require("./utils");
var Constants = require("./constants");
module.exports = {
Player: Player.Player,
Utils: Utils.Utils,
Constants: Constants.Constants
};
},{"./constants":1,"./player":3,"./utils":5}],3:[function(require,module,exports){
"use strict";
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Utils = require("./utils").Utils;
var Track = require("./track").Track;
// Polyfill Uint8Array.forEach: Doesn't exist on Safari <10
if (!Uint8Array.prototype.forEach) {
Object.defineProperty(Uint8Array.prototype, 'forEach', {
value: Array.prototype.forEach
});
}
/**
* Main player class. Contains methods to load files, start, stop.
* @param {function} - Callback to fire for each MIDI event. Can also be added with on('midiEvent', fn)
* @param {array} - Array buffer of MIDI file (optional).
*/
var Player = function () {
function Player(eventHandler, buffer) {
_classCallCheck(this, Player);
this.sampleRate = 5; // milliseconds
this.startTime = 0;
this.buffer = buffer || null;
this.division;
this.format;
this.setIntervalId = false;
this.tracks = [];
this.instruments = [];
this.defaultTempo = 120;
this.tempo = null;
this.startTick = 0;
this.tick = 0;
this.lastTick = null;
this.inLoop = false;
this.totalTicks = 0;
this.events = [];
this.totalEvents = 0;
this.eventListeners = {};
if (typeof eventHandler === 'function') this.on('midiEvent', eventHandler);
}
/**
* Load a file into the player (Node.js only).
* @param {string} path - Path of file.
* @return {Player}
*/
_createClass(Player, [{
key: "loadFile",
value: function loadFile(path) {
var fs = require('fs');
this.buffer = fs.readFileSync(path);
return this.fileLoaded();
}
/**
* Load an array buffer into the player.
* @param {array} arrayBuffer - Array buffer of file to be loaded.
* @return {Player}
*/
}, {
key: "loadArrayBuffer",
value: function loadArrayBuffer(arrayBuffer) {
this.buffer = new Uint8Array(arrayBuffer);
return this.fileLoaded();
}
/**
* Load a data URI into the player.
* @param {string} dataUri - Data URI to be loaded.
* @return {Player}
*/
}, {
key: "loadDataUri",
value: function loadDataUri(dataUri) {
// convert base64 to raw binary data held in a string.
// doesn't handle URLEncoded DataURIs - see SO answer #6850276 for code that does this
var byteString = Utils.atob(dataUri.split(',')[1]);
// write the bytes of the string to an ArrayBuffer
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
this.buffer = ia;
return this.fileLoaded();
}
/**
* Get filesize of loaded file in number of bytes.
* @return {number} - The filesize.
*/
}, {
key: "getFilesize",
value: function getFilesize() {
return this.buffer ? this.buffer.length : 0;
}
/**
* Sets default tempo, parses file for necessary information, and does a dry run to calculate total length.
* Populates this.events & this.totalTicks.
* @return {Player}
*/
}, {
key: "fileLoaded",
value: function fileLoaded() {
if (!this.validate()) throw 'Invalid MIDI file; should start with MThd';
return this.setTempo(this.defaultTempo).getDivision().getFormat().getTracks().dryRun();
}
/**
* Validates file using simple means - first four bytes should == MThd.
* @return {boolean}
*/
}, {
key: "validate",
value: function validate() {
return Utils.bytesToLetters(this.buffer.subarray(0, 4)) === 'MThd';
}
/**
* Gets MIDI file format for loaded file.
* @return {Player}
*/
}, {
key: "getFormat",
value: function getFormat() {
/*
MIDI files come in 3 variations:
Format 0 which contain a single track
Format 1 which contain one or more simultaneous tracks
(ie all tracks are to be played simultaneously).
Format 2 which contain one or more independant tracks
(ie each track is to be played independantly of the others).
return Utils.bytesToNumber(this.buffer.subarray(8, 10));
*/
this.format = Utils.bytesToNumber(this.buffer.subarray(8, 10));
return this;
}
/**
* Parses out tracks, places them in this.tracks and initializes this.pointers
* @return {Player}
*/
}, {
key: "getTracks",
value: function getTracks() {
this.tracks = [];
var trackOffset = 0;
while (trackOffset < this.buffer.length) {
if (Utils.bytesToLetters(this.buffer.subarray(trackOffset, trackOffset + 4)) == 'MTrk') {
var trackLength = Utils.bytesToNumber(this.buffer.subarray(trackOffset + 4, trackOffset + 8));
this.tracks.push(new Track(this.tracks.length, this.buffer.subarray(trackOffset + 8, trackOffset + 8 + trackLength)));
}
trackOffset += Utils.bytesToNumber(this.buffer.subarray(trackOffset + 4, trackOffset + 8)) + 8;
}
return this;
}
/**
* Enables a track for playing.
* @param {number} trackNumber - Track number
* @return {Player}
*/
}, {
key: "enableTrack",
value: function enableTrack(trackNumber) {
this.tracks[trackNumber - 1].enable();
return this;
}
/**
* Disables a track for playing.
* @param {number} - Track number
* @return {Player}
*/
}, {
key: "disableTrack",
value: function disableTrack(trackNumber) {
this.tracks[trackNumber - 1].disable();
return this;
}
/**
* Gets quarter note division of loaded MIDI file.
* @return {Player}
*/
}, {
key: "getDivision",
value: function getDivision() {
this.division = Utils.bytesToNumber(this.buffer.subarray(12, 14));
return this;
}
/**
* The main play loop.
* @param {boolean} - Indicates whether or not this is being called simply for parsing purposes. Disregards timing if so.
* @return {undefined}
*/
}, {
key: "playLoop",
value: function playLoop(dryRun) {
if (!this.inLoop) {
this.inLoop = true;
this.tick = this.getCurrentTick();
this.tracks.forEach(function (track) {
// Handle next event
if (!dryRun && this.endOfFile()) {
//console.log('end of file')
this.triggerPlayerEvent('endOfFile');
this.stop();
} else {
var event = track.handleEvent(this.tick, dryRun);
if (dryRun && event) {
if (event.hasOwnProperty('name') && event.name === 'Set Tempo') {
// Grab tempo if available.
this.setTempo(event.data);
}
if (event.hasOwnProperty('name') && event.name === 'Program Change') {
if (!this.instruments.includes(event.value)) {
this.instruments.push(event.value);
}
}
} else if (event) this.emitEvent(event);
}
}, this);
if (!dryRun) this.triggerPlayerEvent('playing', { tick: this.tick });
this.inLoop = false;
}
}
/**
* Setter for tempo.
* @param {number} - Tempo in bpm (defaults to 120)
*/
}, {
key: "setTempo",
value: function setTempo(tempo) {
this.tempo = tempo;
return this;
}
/**
* Setter for startTime.
* @param {number} - UTC timestamp
*/
}, {
key: "setStartTime",
value: function setStartTime(startTime) {
this.startTime = startTime;
}
/**
* Start playing loaded MIDI file if not already playing.
* @return {Player}
*/
}, {
key: "play",
value: function play() {
if (this.isPlaying()) throw 'Already playing...';
// Initialize
if (!this.startTime) this.startTime = new Date().getTime();
// Start play loop
//window.requestAnimationFrame(this.playLoop.bind(this));
this.setIntervalId = setInterval(this.playLoop.bind(this), this.sampleRate);
return this;
}
/**
* Pauses playback if playing.
* @return {Player}
*/
}, {
key: "pause",
value: function pause() {
clearInterval(this.setIntervalId);
this.setIntervalId = false;
this.startTick = this.tick;
this.startTime = 0;
return this;
}
/**
* Stops playback if playing.
* @return {Player}
*/
}, {
key: "stop",
value: function stop() {
clearInterval(this.setIntervalId);
this.setIntervalId = false;
this.startTick = 0;
this.startTime = 0;
this.resetTracks();
return this;
}
/**
* Skips player pointer to specified tick.
* @param {number} - Tick to skip to.
* @return {Player}
*/
}, {
key: "skipToTick",
value: function skipToTick(tick) {
this.stop();
this.startTick = tick;
// Need to set track event indexes to the nearest possible event to the specified tick.
this.tracks.forEach(function (track) {
track.setEventIndexByTick(tick);
});
return this;
}
/**
* Skips player pointer to specified percentage.
* @param {number} - Percent value in integer format.
* @return {Player}
*/
}, {
key: "skipToPercent",
value: function skipToPercent(percent) {
if (percent < 0 || percent > 100) throw "Percent must be number between 1 and 100.";
this.skipToTick(Math.round(percent / 100 * this.totalTicks));
return this;
}
/**
* Skips player pointer to specified seconds.
* @param {number} - Seconds to skip to.
* @return {Player}
*/
}, {
key: "skipToSeconds",
value: function skipToSeconds(seconds) {
var songTime = this.getSongTime();
if (seconds < 0 || seconds > songTime) throw seconds + " seconds not within song time of " + songTime;
this.skipToPercent(seconds / songTime * 100);
return this;
}
/**
* Checks if player is playing
* @return {boolean}
*/
}, {
key: "isPlaying",
value: function isPlaying() {
return this.setIntervalId > 0 || _typeof(this.setIntervalId) === 'object';
}
/**
* Plays the loaded MIDI file without regard for timing and saves events in this.events. Essentially used as a parser.
* @return {Player}
*/
}, {
key: "dryRun",
value: function dryRun() {
// Reset tracks first
this.resetTracks();
while (!this.endOfFile()) {
this.playLoop(true);
}this.events = this.getEvents();
this.totalEvents = this.getTotalEvents();
this.totalTicks = this.getTotalTicks();
this.startTick = 0;
this.startTime = 0;
// Leave tracks in pristine condish
this.resetTracks();
//console.log('Song time: ' + this.getSongTime() + ' seconds / ' + this.totalTicks + ' ticks.');
this.triggerPlayerEvent('fileLoaded', this);
return this;
}
/**
* Resets play pointers for all tracks.
* @return {Player}
*/
}, {
key: "resetTracks",
value: function resetTracks() {
this.tracks.forEach(function (track) {
return track.reset();
});
return this;
}
/**
* Gets an array of events grouped by track.
* @return {array}
*/
}, {
key: "getEvents",
value: function getEvents() {
return this.tracks.map(function (track) {
return track.events;
});
}
/**
* Gets total number of ticks in the loaded MIDI file.
* @return {number}
*/
}, {
key: "getTotalTicks",
value: function getTotalTicks() {
return Math.max.apply(null, this.tracks.map(function (track) {
return track.delta;
}));
}
/**
* Gets total number of events in the loaded MIDI file.
* @return {number}
*/
}, {
key: "getTotalEvents",
value: function getTotalEvents() {
return this.tracks.reduce(function (a, b) {
return { events: { length: a.events.length + b.events.length } };
}, { events: { length: 0 } }).events.length;
}
/**
* Gets song duration in seconds.
* @return {number}
*/
}, {
key: "getSongTime",
value: function getSongTime() {
return this.totalTicks / this.division / this.tempo * 60;
}
/**
* Gets remaining number of seconds in playback.
* @return {number}
*/
}, {
key: "getSongTimeRemaining",
value: function getSongTimeRemaining() {
return Math.round((this.totalTicks - this.tick) / this.division / this.tempo * 60);
}
/**
* Gets remaining percent of playback.
* @return {number}
*/
}, {
key: "getSongPercentRemaining",
value: function getSongPercentRemaining() {
return Math.round(this.getSongTimeRemaining() / this.getSongTime() * 100);
}
/**
* Number of bytes processed in the loaded MIDI file.
* @return {number}
*/
}, {
key: "bytesProcessed",
value: function bytesProcessed() {
// Currently assume header chunk is strictly 14 bytes
return 14 + this.tracks.length * 8 + this.tracks.reduce(function (a, b) {
return { pointer: a.pointer + b.pointer };
}, { pointer: 0 }).pointer;
}
/**
* Number of events played up to this point.
* @return {number}
*/
}, {
key: "eventsPlayed",
value: function eventsPlayed() {
return this.tracks.reduce(function (a, b) {
return { eventIndex: a.eventIndex + b.eventIndex };
}, { eventIndex: 0 }).eventIndex;
}
/**
* Determines if the player pointer has reached the end of the loaded MIDI file.
* Used in two ways:
* 1. If playing result is based on loaded JSON events.
* 2. If parsing (dryRun) it's based on the actual buffer length vs bytes processed.
* @return {boolean}
*/
}, {
key: "endOfFile",
value: function endOfFile() {
if (this.isPlaying()) {
return this.eventsPlayed() == this.totalEvents;
}
return this.bytesProcessed() == this.buffer.length;
}
/**
* Gets the current tick number in playback.
* @return {number}
*/
}, {
key: "getCurrentTick",
value: function getCurrentTick() {
return Math.round((new Date().getTime() - this.startTime) / 1000 * (this.division * (this.tempo / 60))) + this.startTick;
}
/**
* Sends MIDI event out to listener.
* @param {object}
* @return {Player}
*/
}, {
key: "emitEvent",
value: function emitEvent(event) {
this.triggerPlayerEvent('midiEvent', event);
return this;
}
/**
* Subscribes events to listeners
* @param {string} - Name of event to subscribe to.
* @param {function} - Callback to fire when event is broadcast.
* @return {Player}
*/
}, {
key: "on",
value: function on(playerEvent, fn) {
if (!this.eventListeners.hasOwnProperty(playerEvent)) this.eventListeners[playerEvent] = [];
this.eventListeners[playerEvent].push(fn);
return this;
}
/**
* Broadcasts event to trigger subscribed callbacks.
* @param {string} - Name of event.
* @param {object} - Data to be passed to subscriber callback.
* @return {Player}
*/
}, {
key: "triggerPlayerEvent",
value: function triggerPlayerEvent(playerEvent, data) {
if (this.eventListeners.hasOwnProperty(playerEvent)) this.eventListeners[playerEvent].forEach(function (fn) {
return fn(data || {});
});
return this;
}
}]);
return Player;
}();
exports.Player = Player;
},{"./track":4,"./utils":5,"fs":undefined}],4:[function(require,module,exports){
"use strict";
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Constants = require("./constants").Constants;
var Utils = require("./utils").Utils;
/**
* Class representing a track. Contains methods for parsing events and keeping track of pointer.
*/
var Track = function () {
function Track(index, data) {
_classCallCheck(this, Track);
this.enabled = true;
this.eventIndex = 0;
this.pointer = 0;
this.lastTick = 0;
this.lastStatus = null;
this.index = index;
this.data = data;
this.delta = 0;
this.runningDelta = 0;
this.events = [];
}
/**
* Resets all stateful track informaion used during playback.
* @return {Track}
*/
_createClass(Track, [{
key: "reset",
value: function reset() {
this.enabled = true;
this.eventIndex = 0;
this.pointer = 0;
this.lastTick = 0;
this.lastStatus = null;
this.delta = 0;
this.runningDelta = 0;
return this;
}
/**
* Sets this track to be enabled during playback.
* @return {Track}
*/
}, {
key: "enable",
value: function enable() {
this.enabled = true;
return this;
}
/**
* Sets this track to be disabled during playback.
* @return {Track}
*/
}, {
key: "disable",
value: function disable() {
this.enabled = false;
return this;
}
/**
* Sets the track event index to the nearest event to the given tick.
* @param {number} tick
* @return {Track}
*/
}, {
key: "setEventIndexByTick",
value: function setEventIndexByTick(tick) {
tick = tick || 0;
for (var i in this.events) {
if (this.events[i].tick >= tick) {
this.eventIndex = i;
return this;
}
}
}
/**
* Gets byte located at pointer position.
* @return {number}
*/
}, {
key: "getCurrentByte",
value: function getCurrentByte() {
return this.data[this.pointer];
}
/**
* Gets count of delta bytes and current pointer position.
* @return {number}
*/
}, {
key: "getDeltaByteCount",
value: function getDeltaByteCount() {
// Get byte count of delta VLV
// http://www.ccarh.org/courses/253/handout/vlv/
// If byte is greater or equal to 80h (128 decimal) then the next byte
// is also part of the VLV,
// else byte is the last byte in a VLV.
var currentByte = this.getCurrentByte();
var byteCount = 1;
while (currentByte >= 128) {
currentByte = this.data[this.pointer + byteCount];
byteCount++;
}
return byteCount;
}
/**
* Get delta value at current pointer position.
* @return {number}
*/
}, {
key: "getDelta",
value: function getDelta() {
return Utils.readVarInt(this.data.subarray(this.pointer, this.pointer + this.getDeltaByteCount()));
}
/**
* Handles event within a given track starting at specified index
* @param {number} currentTick
* @param {boolean} dryRun - If true events will be parsed and returned regardless of time.
*/
}, {
key: "handleEvent",
value: function handleEvent(currentTick, dryRun) {
dryRun = dryRun || false;
if (dryRun) {
var elapsedTicks = currentTick - this.lastTick;
var delta = this.getDelta();
var eventReady = elapsedTicks >= delta;
if (this.pointer < this.data.length && (dryRun || eventReady)) {
var _event = this.parseEvent();
if (this.enabled) return _event;
// Recursively call this function for each event ahead that has 0 delta time?
}
} else {
// Let's actually play the MIDI from the generated JSON events created by the dry run.
if (this.events[this.eventIndex] && this.events[this.eventIndex].tick <= currentTick) {
this.eventIndex++;
if (this.enabled) return this.events[this.eventIndex - 1];
}
}
return null;
}
/**
* Get string data from event.
* @param {number} eventStartIndex
* @return {string}
*/
}, {
key: "getStringData",
value: function getStringData(eventStartIndex) {
var currentByte = this.pointer;
var byteCount = 1;
var length = Utils.readVarInt(this.data.subarray(eventStartIndex + 2, eventStartIndex + 2 + byteCount));
var stringLength = length;
return Utils.bytesToLetters(this.data.subarray(eventStartIndex + byteCount + 2, eventStartIndex + byteCount + length + 2));
}
/**
* Parses event into JSON and advances pointer for the track
* @return {object}
*/
}, {
key: "parseEvent",
value: function parseEvent() {
var eventStartIndex = this.pointer + this.getDeltaByteCount();
var eventJson = {};
var deltaByteCount = this.getDeltaByteCount();
eventJson.track = this.index + 1;
eventJson.delta = this.getDelta();
this.lastTick = this.lastTick + eventJson.delta;
this.runningDelta += eventJson.delta;
eventJson.tick = this.runningDelta;
eventJson.byteIndex = this.pointer;
//eventJson.raw = event;
if (this.data[eventStartIndex] == 0xff) {
// Meta Event
// If this is a meta event we should emit the data and immediately move to the next event
// otherwise if we let it run through the next cycle a slight delay will accumulate if multiple tracks
// are being played simultaneously
switch (this.data[eventStartIndex + 1]) {
case 0x00:
// Sequence Number
eventJson.name = 'Sequence Number';
break;
case 0x01:
// Text Event
eventJson.name = 'Text Event';
eventJson.string = this.getStringData(eventStartIndex);
break;
case 0x02:
// Copyright Notice
eventJson.name = 'Copyright Notice';
break;
case 0x03:
// Sequence/Track Name
eventJson.name = 'Sequence/Track Name';
eventJson.string = this.getStringData(eventStartIndex);
break;
case 0x04:
// Instrument Name
eventJson.name = 'Instrument Name';
eventJson.string = this.getStringData(eventStartIndex);
break;
case 0x05:
// Lyric
eventJson.name = 'Lyric';
eventJson.string = this.getStringData(eventStartIndex);
break;
case 0x06:
// Marker
eventJson.name = 'Marker';
break;
case 0x07:
// Cue Point
eventJson.name = 'Cue Point';
eventJson.string = this.getStringData(eventStartIndex);
break;
case 0x09:
// Device Name
eventJson.name = 'Device Name';
eventJson.string = this.getStringData(eventStartIndex);
break;
case 0x20:
// MIDI Channel Prefix
eventJson.name = 'MIDI Channel Prefix';
break;
case 0x21:
// MIDI Port
eventJson.name = 'MIDI Port';
eventJson.data = Utils.bytesToNumber([this.data[eventStartIndex + 3]]);
break;
case 0x2F:
// End of Track
eventJson.name = 'End of Track';
break;
case 0x51:
// Set Tempo
eventJson.name = 'Set Tempo';
eventJson.data = Math.round(60000000 / Utils.bytesToNumber(this.data.subarray(eventStartIndex + 3, eventStartIndex + 6)));
this.tempo = eventJson.data;
break;
case 0x54:
// SMTPE Offset
eventJson.name = 'SMTPE Offset';
break;
case 0x58:
// Time Signature
// FF 58 04 nn dd cc bb
eventJson.name = 'Time Signature';
eventJson.data = this.data.subarray(eventStartIndex + 3, eventStartIndex + 7);
eventJson.timeSignature = "" + eventJson.data[0] + "/" + Math.pow(eventJson.data[1], 2);
break;
case 0x59:
// Key Signature
// FF 59 02 sf mi
eventJson.name = 'Key Signature';
eventJson.data = this.data.subarray(eventStartIndex + 3, eventStartIndex + 5);
if (eventJson.data[0] >= 0) {
eventJson.keySignature = Constants.CIRCLE_OF_FIFTHS[eventJson.data[0]];
} else if (eventJson.data[0] < 0) {
eventJson.keySignature = Constants.CIRCLE_OF_FOURTHS[Math.abs(eventJson.data[0])];
}
if (eventJson.data[1] == 0) {
eventJson.keySignature += " Major";
} else if (eventJson.data[1] == 1) {
eventJson.keySignature += " Minor";
}
break;
case 0x7F:
// Sequencer-Specific Meta-event
eventJson.name = 'Sequencer-Specific Meta-event';
break;
default:
eventJson.name = 'Unknown: ' + this.data[eventStartIndex + 1].toString(16);
break;
}
var length = this.data[this.pointer + deltaByteCount + 2];
// Some meta events will have vlv that needs to be handled
this.pointer += deltaByteCount + 3 + length;
} else if (this.data[eventStartIndex] == 0xf0) {
// Sysex
eventJson.name = 'Sysex';
var length = this.data[this.pointer + deltaByteCount + 1];
this.pointer += deltaByteCount + 2 + length;
} else {
// Voice event
if (this.data[eventStartIndex] < 0x80) {
// Running status
eventJson.running = true;
eventJson.noteNumber = this.data[eventStartIndex];
eventJson.noteName = Constants.NOTES[this.data[eventStartIndex]];
eventJson.velocity = this.data[eventStartIndex + 1];
if (this.lastStatus <= 0x8f) {
eventJson.name = 'Note off';
eventJson.channel = this.lastStatus - 0x80 + 1;
} else if (this.lastStatus <= 0x9f) {
eventJson.name = 'Note on';
eventJson.channel = this.lastStatus - 0x90 + 1;
}
this.pointer += deltaByteCount + 2;
} else {
this.lastStatus = this.data[eventStartIndex];
if (this.data[eventStartIndex] <= 0x8f) {
// Note off
eventJson.name = 'Note off';
eventJson.channel = this.lastStatus - 0x80 + 1;
eventJson.noteNumber = this.data[eventStartIndex + 1];
eventJson.noteName = Constants.NOTES[this.data[eventStartIndex + 1]];
eventJson.velocity = Math.round(this.data[eventStartIndex + 2] / 127 * 100);
this.pointer += deltaByteCount + 3;
} else if (this.data[eventStartIndex] <= 0x9f) {
// Note on
eventJson.name = 'Note on';
eventJson.channel = this.lastStatus - 0x90 + 1;
eventJson.noteNumber = this.data[eventStartIndex + 1];
eventJson.noteName = Constants.NOTES[this.data[eventStartIndex + 1]];
eventJson.velocity = Math.round(this.data[eventStartIndex + 2] / 127 * 100);
this.pointer += deltaByteCount + 3;
} else if (this.data[eventStartIndex] <= 0xaf) {
// Polyphonic Key Pressure
eventJson.name = 'Polyphonic Key Pressure';
eventJson.channel = this.lastStatus - 0xa0 + 1;
eventJson.note = Constants.NOTES[this.data[eventStartIndex + 1]];
eventJson.pressure = event[2];
this.pointer += deltaByteCount + 3;
} else if (this.data[eventStartIndex] <= 0xbf) {
// Controller Change
eventJson.name = 'Controller Change';
eventJson.channel = this.lastStatus - 0xb0 + 1;
eventJson.number = this.data[eventStartIndex + 1];
eventJson.value = this.data[eventStartIndex + 2];
this.pointer += deltaByteCount + 3;
} else if (this.data[eventStartIndex] <= 0xcf) {
// Program Change
eventJson.name = 'Program Change';
eventJson.channel = this.lastStatus - 0xc0 + 1;
eventJson.value = this.data[eventStartIndex + 1];
this.pointer += deltaByteCount + 2;
} else if (this.data[eventStartIndex] <= 0xdf) {
// Channel Key Pressure
eventJson.name = 'Channel Key Pressure';
eventJson.channel = this.lastStatus - 0xd0 + 1;
this.pointer += deltaByteCount + 2;
} else if (this.data[eventStartIndex] <= 0xef) {
// Pitch Bend
eventJson.name = 'Pitch Bend';
eventJson.channel = this.lastStatus - 0xe0 + 1;
this.pointer += deltaByteCount + 3;
} else {
eventJson.name = 'Unknown. Pointer: ' + this.pointer.toString() + ' ' + eventStartIndex.toString() + ' ' + this.data.length;
}
}
}
this.delta += eventJson.delta;
this.events.push(eventJson);
return eventJson;
}
/**
* Returns true if pointer has reached the end of the track.
* @param {boolean}
*/
}, {
key: "endOfTrack",
value: function endOfTrack() {
if (this.data[this.pointer + 1] == 0xff && this.data[this.pointer + 2] == 0x2f && this.data[this.pointer + 3] == 0x00) {
return true;
}
return false;
}
}]);
return Track;
}();
module.exports.Track = Track;
},{"./constants":1,"./utils":5}],5:[function(require,module,exports){
(function (Buffer){
'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* Contains misc static utility methods.
*/
var Utils = function () {
function Utils() {
_classCallCheck(this, Utils);
}
_createClass(Utils, null, [{
key: 'byteToHex',
/**
* Converts a single byte to a hex string.
* @param {number} byte
* @return {string}
*/
value: function byteToHex(byte) {
// Ensure hex string always has two chars
return ('0' + byte.toString(16)).slice(-2);
}
/**
* Converts an array of bytes to a hex string.
* @param {array} byteArray
* @return {string}
*/
}, {
key: 'bytesToHex',
value: function bytesToHex(byteArray) {
var hex = [];
byteArray.forEach(function (byte) {
return hex.push(Utils.byteToHex(byte));
});
return hex.join('');
}
/**
* Converts a hex string to a number.
* @param {string} hexString
* @return {number}
*/
}, {
key: 'hexToNumber',
value: function hexToNumber(hexString) {
return parseInt(hexString, 16);
}
/**
* Converts an array of bytes to a number.
* @param {array} byteArray
* @return {number}
*/
}, {
key: 'bytesToNumber',
value: function bytesToNumber(byteArray) {
return Utils.hexToNumber(Utils.bytesToHex(byteArray));
}
/**
* Converts an array of bytes to letters.
* @param {array} byteArray
* @return {string}
*/
}, {
key: 'bytesToLetters',
value: function bytesToLetters(byteArray) {
var letters = [];
byteArray.forEach(function (byte) {
return letters.push(String.fromCharCode(byte));
});
return letters.join('');
}
/**
* Converts a decimal to it's binary representation.
* @param {number} dec
* @return {string}
*/
}, {
key: 'decToBinary',
value: function decToBinary(dec) {
return (dec >>> 0).toString(2);
}
/**
* Reads a variable length value.
* @param {array} byteArray
* @return {number}
*/
}, {
key: 'readVarInt',
value: function readVarInt(byteArray) {
var result = 0;
byteArray.forEach(function (number) {
var b = number;
if (b & 0x80) {
result += b & 0x7f;
result <<= 7;
} else {
/* b is the last byte */
result += b;
}
});
return result;
}
/**
* Decodes base-64 encoded string
* @param {string} string
* @return {string}
*/
}, {
key: 'atob',
value: function (_atob) {
function atob(_x) {
return _atob.apply(this, arguments);
}
atob.toString = function () {
return _atob.toString();
};
return atob;
}(function (string) {
if (typeof atob === 'function') return atob(string);
return new Buffer(string, 'base64').toString('binary');
})
}]);
return Utils;
}();
exports.Utils = Utils;
}).call(this,require("buffer").Buffer)
},{"buffer":undefined}]},{},[2])(2)
});
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJzcmMvY29uc3RhbnRzLmpzIiwic3JjL2luZGV4LmpzIiwic3JjL3BsYXllci5qcyIsInNyYy90cmFjay5qcyIsInNyYy91dGlscy5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0FDQUE7OztBQUdBLElBQUksWUFBWTtBQUNmLFVBQVMsT0FETTtBQUVmLFFBQU8sRUFGUTtBQUdmLG9CQUFtQixDQUFDLEdBQUQsRUFBTSxHQUFOLEVBQVcsSUFBWCxFQUFpQixJQUFqQixFQUF1QixJQUF2QixFQUE2QixJQUE3QixFQUFtQyxJQUFuQyxFQUF5QyxJQUF6QyxFQUErQyxJQUEvQyxFQUFxRCxLQUFyRCxFQUE0RCxLQUE1RCxFQUFtRSxLQUFuRSxDQUhKO0FBSWYsbUJBQWtCLENBQUMsR0FBRCxFQUFNLEdBQU4sRUFBVyxHQUFYLEVBQWdCLEdBQWhCLEVBQXFCLEdBQXJCLEVBQTBCLEdBQTFCLEVBQStCLElBQS9CLEVBQXFDLElBQXJDLEVBQTJDLElBQTNDLEVBQWlELElBQWpELEVBQXVELElBQXZELEVBQTZELElBQTdEO0FBSkgsQ0FBaEI7O0FBT0E7QUFDQSxJQUFJLFdBQVcsQ0FBQyxDQUFDLEdBQUQsQ0FBRCxFQUFRLENBQUMsSUFBRCxFQUFNLElBQU4sQ0FBUixFQUFxQixDQUFDLEdBQUQsQ0FBckIsRUFBNEIsQ0FBQyxJQUFELEVBQU0sSUFBTixDQUE1QixFQUF5QyxDQUFDLEdBQUQsQ0FBekMsRUFBK0MsQ0FBQyxHQUFELENBQS9DLEVBQXNELENBQUMsSUFBRCxFQUFNLElBQU4sQ0FBdEQsRUFBbUUsQ0FBQyxHQUFELENBQW5FLEVBQTBFLENBQUMsSUFBRCxFQUFNLElBQU4sQ0FBMUUsRUFBdUYsQ0FBQyxHQUFELENBQXZGLEVBQThGLENBQUMsSUFBRCxFQUFNLElBQU4sQ0FBOUYsRUFBMkcsQ0FBQyxHQUFELENBQTNHLENBQWY7QUFDQSxJQUFJLFVBQVUsQ0FBZDs7QUFFQTs7MkJBQ1MsQztBQUNSLFVBQVMsT0FBVCxDQUFpQixxQkFBYTtBQUM3QixZQUFVLE9BQVYsQ0FBa0I7QUFBQSxVQUFRLFVBQVUsS0FBVixDQUFnQixPQUFoQixJQUEyQixPQUFPLENBQTFDO0FBQUEsR0FBbEI7QUFDQTtBQUNBLEVBSEQ7OztBQURELEtBQUssSUFBSSxJQUFJLENBQUMsQ0FBZCxFQUFpQixLQUFLLENBQXRCLEVBQXlCLEdBQXpCLEVBQThCO0FBQUEsT0FBckIsQ0FBcUI7QUFLN0I7O0FBRUQsUUFBUSxTQUFSLEdBQW9CLFNBQXBCOzs7OztBQ3RCQSxJQUFNLFNBQVMsUUFBUSxVQUFSLENBQWY7QUFDQSxJQUFNLFFBQVEsUUFBUSxTQUFSLENBQWQ7QUFDQSxJQUFNLFlBQVksUUFBUSxhQUFSLENBQWxCOztBQUVBLE9BQU8sT0FBUCxHQUFpQjtBQUNiLFlBQU8sT0FBTyxNQUREO0FBRWIsV0FBTSxNQUFNLEtBRkM7QUFHYixlQUFVLFVBQVU7QUFIUCxDQUFqQjs7Ozs7Ozs7Ozs7QUNKQSxJQUFNLFFBQVEsUUFBUSxTQUFSLEVBQW1CLEtBQWpDO0FBQ0EsSUFBTSxRQUFRLFFBQVEsU0FBUixFQUFtQixLQUFqQzs7QUFFQTtBQUNBLElBQUksQ0FBQyxXQUFXLFNBQVgsQ0FBcUIsT0FBMUIsRUFBbUM7QUFDbEMsUUFBTyxjQUFQLENBQXNCLFdBQVcsU0FBakMsRUFBNEMsU0FBNUMsRUFBdUQ7QUFDdEQsU0FBTyxNQUFNLFNBQU4sQ0FBZ0I7QUFEK0IsRUFBdkQ7QUFHQTs7QUFFRDs7Ozs7O0lBS00sTTtBQUNMLGlCQUFZLFlBQVosRUFBMEIsTUFBMUIsRUFBa0M7QUFBQTs7QUFDakMsT0FBSyxVQUFMLEdBQWtCLENBQWxCLENBRGlDLENBQ1o7QUFDckIsT0FBSyxTQUFMLEdBQWlCLENBQWpCO0FBQ0EsT0FBSyxNQUFMLEdBQWMsVUFBVSxJQUF4QjtBQUNBLE9BQUssUUFBTDtBQUNBLE9BQUssTUFBTDtBQUNBLE9BQUssYUFBTCxHQUFxQixLQUFyQjtBQUNBLE9BQUssTUFBTCxHQUFjLEVBQWQ7QUFDQSxPQUFLLFdBQUwsR0FBbUIsRUFBbkI7QUFDQSxPQUFLLFlBQUwsR0FBb0IsR0FBcEI7QUFDQSxPQUFLLEtBQUwsR0FBYSxJQUFiO0FBQ0EsT0FBSyxTQUFMLEdBQWlCLENBQWpCO0FBQ0EsT0FBSyxJQUFMLEdBQVksQ0FBWjtBQUNBLE9BQUssUUFBTCxHQUFnQixJQUFoQjtBQUNBLE9BQUssTUFBTCxHQUFjLEtBQWQ7QUFDQSxPQUFLLFVBQUwsR0FBa0IsQ0FBbEI7QUFDQSxPQUFLLE1BQUwsR0FBYyxFQUFkO0FBQ0EsT0FBSyxXQUFMLEdBQW1CLENBQW5CO0FBQ0EsT0FBSyxjQUFMLEdBQXNCLEVBQXRCOztBQUVBLE1BQUksT0FBTyxZQUFQLEtBQXlCLFVBQTdCLEVBQXlDLEtBQUssRUFBTCxDQUFRLFdBQVIsRUFBcUIsWUFBckI7QUFDekM7O0FBRUQ7Ozs7Ozs7OzsyQkFLUyxJLEVBQU07QUFDZCxPQUFJLEtBQUssUUFBUSxJQUFSLENBQVQ7QUFDQSxRQUFLLE1BQUwsR0FBYyxHQUFHLFlBQUgsQ0FBZ0IsSUFBaEIsQ0FBZDtBQUNBLFVBQU8sS0FBSyxVQUFMLEVBQVA7QUFDQTs7QUFFRDs7Ozs7Ozs7a0NBS2dCLFcsRUFBYTtBQUM1QixRQUFLLE1BQUwsR0FBYyxJQUFJLFVBQUosQ0FBZSxXQUFmLENBQWQ7QUFDQSxVQUFPLEtBQUssVUFBTCxFQUFQO0FBQ0E7O0FBRUQ7Ozs7Ozs7OzhCQUtZLE8sRUFBUztBQUNwQjtBQUNBO0FBQ0EsT0FBSSxhQUFhLE1BQU0sSUFBTixDQUFXLFFBQVEsS0FBUixDQUFjLEdBQWQsRUFBbUIsQ0FBbkIsQ0FBWCxDQUFqQjs7QUFFQTtBQUNBLE9BQUksS0FBSyxJQUFJLFVBQUosQ0FBZSxXQUFXLE1BQTFCLENBQVQ7QUFDQSxRQUFLLElBQUksSUFBSSxDQUFiLEVBQWdCLElBQUksV0FBVyxNQUEvQixFQUF1QyxHQUF2QyxFQUE0QztBQUMzQyxPQUFHLENBQUgsSUFBUSxXQUFXLFVBQVgsQ0FBc0IsQ0FBdEIsQ0FBUjtBQUNBOztBQUVELFFBQUssTUFBTCxHQUFjLEVBQWQ7QUFDQSxVQUFPLEtBQUssVUFBTCxFQUFQO0FBQ0E7O0FBRUQ7Ozs7Ozs7Z0NBSWM7QUFDYixVQUFPLEtBQUssTUFBTCxHQUFjLEtBQUssTUFBTCxDQUFZLE1BQTFCLEdBQW1DLENBQTFDO0FBQ0E7O0FBRUQ7Ozs7Ozs7OytCQUthO0FBQ1osT0FBSSxDQUFDLEtBQUssUUFBTCxFQUFMLEVBQXNCLE1BQU0sMkNBQU47QUFDdEIsVUFBTyxLQUFLLFFBQUwsQ0FBYyxLQUFLLFlBQW5CLEVBQWlDLFdBQWpDLEdBQStDLFNBQS9DLEdBQTJELFNBQTNELEdBQXVFLE1BQXZFLEVBQVA7QUFDQTs7QUFFRDs7Ozs7Ozs2QkFJVztBQUNWLFVBQU8sTUFBTSxjQUFOLENBQXFCLEtBQUssTUFBTCxDQUFZLFFBQVosQ0FBcUIsQ0FBckIsRUFBd0IsQ0FBeEIsQ0FBckIsTUFBcUQsTUFBNUQ7QUFDQTs7QUFFRDs7Ozs7Ozs4QkFJWTtBQUNYOzs7Ozs7Ozs7O0FBVUEsUUFBSyxNQUFMLEdBQWMsTUFBTSxhQUFOLENBQW9CLEtBQUssTUFBTCxDQUFZLFFBQVosQ0FBcUIsQ0FBckIsRUFBd0IsRUFBeEIsQ0FBcEIsQ0FBZDtBQUNBLFVBQU8sSUFBUDtBQUNBOztBQUVEOzs7Ozs7OzhCQUlZO0FBQ1gsUUFBSyxNQUFMLEdBQWMsRUFBZDtBQUNBLE9BQUksY0FBYyxDQUFsQjtBQUNBLFVBQU8sY0FBYyxLQUFLLE1BQUwsQ0FBWSxNQUFqQyxFQUF5QztBQUN4QyxRQUFJLE1BQU0sY0FBTixDQUFxQixLQUFLLE1BQUwsQ0FBWSxRQUFaLENBQXFCLFdBQXJCLEVBQWtDLGNBQWMsQ0FBaEQsQ0FBckIsS0FBNEUsTUFBaEYsRUFBd0Y7QUFDdkYsU0FBSSxjQUFjLE1BQU0sYUFBTixDQUFvQixLQUFLLE1BQUwsQ0FBWSxRQUFaLENBQXFCLGNBQWMsQ0FBbkMsRUFBc0MsY0FBYyxDQUFwRCxDQUFwQixDQUFsQjtBQUNBLFVBQUssTUFBTCxDQUFZLElBQVosQ0FBaUIsSUFBSSxLQUFKLENBQVUsS0FBSyxNQUFMLENBQVksTUFBdEIsRUFBOEIsS0FBSyxNQUFMLENBQVksUUFBWixDQUFxQixjQUFjLENBQW5DLEVBQXNDLGNBQWMsQ0FBZCxHQUFrQixXQUF4RCxDQUE5QixDQUFqQjtBQUNBOztBQUVELG1CQUFlLE1BQU0sYUFBTixDQUFvQixLQUFLLE1BQUwsQ0FBWSxRQUFaLENBQXFCLGNBQWMsQ0FBbkMsRUFBc0MsY0FBYyxDQUFwRCxDQUFwQixJQUE4RSxDQUE3RjtBQUNBO0FBQ0QsVUFBTyxJQUFQO0FBQ0E7O0FBRUQ7Ozs7Ozs7OzhCQUtZLFcsRUFBYTtBQUN4QixRQUFLLE1BQUwsQ0FBWSxjQUFjLENBQTFCLEVBQTZCLE1BQTdCO0FBQ0EsVUFBTyxJQUFQO0FBQ0E7O0FBRUQ7Ozs7Ozs7OytCQUthLFcsRUFBYTtBQUN6QixRQUFLLE1BQUwsQ0FBWSxjQUFjLENBQTFCLEVBQTZCLE9BQTdCO0FBQ0EsVUFBTyxJQUFQO0FBQ0E7O0FBRUQ7Ozs7Ozs7Z0NBSWM7QUFDYixRQUFLLFFBQUwsR0FBZ0IsTUFBTSxhQUFOLENBQW9CLEtBQUssTUFBTCxDQUFZLFFBQVosQ0FBcUIsRUFBckIsRUFBeUIsRUFBekIsQ0FBcEIsQ0FBaEI7QUFDQSxVQUFPLElBQVA7QUFDQTs7QUFFRDs7Ozs7Ozs7MkJBS1MsTSxFQUFRO0FBQ2hCLE9BQUksQ0FBQyxLQUFLLE1BQVYsRUFBa0I7QUFDakIsU0FBSyxNQUFMLEdBQWMsSUFBZDtBQUNBLFNBQUssSUFBTCxHQUFZLEtBQUssY0FBTCxFQUFaOztBQUVBLFNBQUssTUFBTCxDQUFZLE9BQVosQ0FBb0IsVUFBUyxLQUFULEVBQWdCO0FBQ25DO0FBQ0EsU0FBSSxDQUFDLE1BQUQsSUFBVyxLQUFLLFNBQUwsRUFBZixFQUFpQztBQUNoQztBQUNBLFdBQUssa0JBQUwsQ0FBd0IsV0FBeEI7QUFDQSxXQUFLLElBQUw7QUFDQSxNQUpELE1BSU87QUFDTixVQUFJLFFBQVEsTUFBTSxXQUFOLENBQWtCLEtBQUssSUFBdkIsRUFBNkIsTUFBN0IsQ0FBWjs7QUFFQSxVQUFJLFVBQVUsS0FBZCxFQUFxQjtBQUNwQixXQUFJLE1BQU0sY0FBTixDQUFxQixNQUFyQixLQUFnQyxNQUFNLElBQU4sS0FBZSxXQUFuRCxFQUFnRTtBQUMvRDtBQUNBLGFBQUssUUFBTCxDQUFjLE1BQU0sSUFBcEI7QUFDQTtBQUNELFdBQUksTUFBTSxjQUFOLENBQXFCLE1BQXJCLEtBQWdDLE1BQU0sSUFBTixLQUFlLGdCQUFuRCxFQUFxRTtBQUNwRSxZQUFJLENBQUMsS0FBSyxXQUFMLENBQWlCLFFBQWpCLENBQTBCLE1BQU0sS0FBaEMsQ0FBTCxFQUE2QztBQUM1QyxjQUFLLFdBQUwsQ0FBaUIsSUFBakIsQ0FBc0IsTUFBTSxLQUE1QjtBQUNBO0FBQ0Q7QUFDRCxPQVZELE1BVU8sSUFBSSxLQUFKLEVBQVcsS0FBSyxTQUFMLENBQWUsS0FBZjtBQUNsQjtBQUVELEtBdEJELEVBc0JHLElBdEJIOztBQXdCQSxRQUFJLENBQUMsTUFBTCxFQUFhLEtBQUssa0JBQUwsQ0FBd0IsU0FBeEIsRUFBbUMsRUFBQyxNQUFNLEtBQUssSUFBWixFQUFuQztBQUNiLFNBQUssTUFBTCxHQUFjLEtBQWQ7QUFDQTtBQUNEOztBQUVEOzs7Ozs7OzJCQUlTLEssRUFBTztBQUNmLFFBQUssS0FBTCxHQUFhLEtBQWI7QUFDQSxVQUFPLElBQVA7QUFDQTs7QUFFRDs7Ozs7OzsrQkFJYSxTLEVBQVc7QUFDdkIsUUFBSyxTQUFMLEdBQWlCLFNBQWpCO0FBQ0E7O0FBRUQ7Ozs7Ozs7eUJBSU87QUFDTixPQUFJLEtBQUssU0FBTCxFQUFKLEVBQXNCLE1BQU0sb0JBQU47O0FBRXRCO0FBQ0EsT0FBSSxDQUFDLEtBQUssU0FBVixFQUFxQixLQUFLLFNBQUwsR0FBa0IsSUFBSSxJQUFKLEVBQUQsQ0FBYSxPQUFiLEVBQWpCOztBQUVyQjtBQUNBO0FBQ0EsUUFBSyxhQUFMLEdBQXFCLFlBQVksS0FBSyxRQUFMLENBQWMsSUFBZCxDQUFtQixJQUFuQixDQUFaLEVBQXNDLEtBQUssVUFBM0MsQ0FBckI7O0FBRUEsVUFBTyxJQUFQO0FBQ0E7O0FBRUQ7Ozs7Ozs7MEJBSVE7QUFDUCxpQkFBYyxLQUFLLGFBQW5CO0FBQ0EsUUFBSyxhQUFMLEdBQXFCLEtBQXJCO0FBQ0EsUUFBSyxTQUFMLEdBQWlCLEtBQUssSUFBdEI7QUFDQSxRQUFLLFNBQUwsR0FBaUIsQ0FBakI7QUFDQSxVQUFPLElBQVA7QUFDQTs7QUFFRDs7Ozs7Ozt5QkFJTztBQUNOLGlCQUFjLEtBQUssYUFBbkI7QUFDQSxRQUFLLGFBQUwsR0FBcUIsS0FBckI7QUFDQSxRQUFLLFNBQUwsR0FBaUIsQ0FBakI7QUFDQSxRQUFLLFNBQUwsR0FBaUIsQ0FBakI7QUFDQSxRQUFLLFdBQUw7QUFDQSxVQUFPLElBQVA7QUFDQTs7QUFFRDs7Ozs7Ozs7NkJBS1csSSxFQUFNO0FBQ2hCLFFBQUssSUFBTDtBQUNBLFFBQUssU0FBTCxHQUFpQixJQUFqQjs7QUFFQTtBQUNBLFFBQUssTUFBTCxDQUFZLE9BQVosQ0FBb0IsVUFBUyxLQUFULEVBQWdCO0FBQ25DLFVBQU0sbUJBQU4sQ0FBMEIsSUFBMUI7QUFDQSxJQUZEO0FBR0EsVUFBTyxJQUFQO0FBQ0E7O0FBRUQ7Ozs7Ozs7O2dDQUtjLE8sRUFBUztBQUN0QixPQUFJLFVBQVUsQ0FBVixJQUFlLFVBQVUsR0FBN0IsRUFBa0MsTUFBTSwyQ0FBTjtBQUNsQyxRQUFLLFVBQUwsQ0FBZ0IsS0FBSyxLQUFMLENBQVcsVUFBVSxHQUFWLEdBQWdCLEtBQUssVUFBaEMsQ0FBaEI7QUFDQSxVQUFPLElBQVA7QUFDQTs7QUFFRDs7Ozs7Ozs7Z0NBS2MsTyxFQUFTO0FBQ3RCLE9BQUksV0FBVyxLQUFLLFdBQUwsRUFBZjtBQUNBLE9BQUksVUFBVSxDQUFWLElBQWUsVUFBVSxRQUE3QixFQUF1QyxNQUFNLFVBQVUsbUNBQVYsR0FBZ0QsUUFBdEQ7QUFDdkMsUUFBSyxhQUFMLENBQW1CLFVBQVUsUUFBVixHQUFxQixHQUF4QztBQUNBLFVBQU8sSUFBUDtBQUNBOztBQUVEOzs7Ozs7OzhCQUlZO0FBQ1gsVUFBTyxLQUFLLGFBQUwsR0FBcUIsQ0FBckIsSUFBMEIsUUFBTyxLQUFLLGFBQVosTUFBOEIsUUFBL0Q7QUFDQTs7QUFFRDs7Ozs7OzsyQkFJUztBQUNSO0FBQ0EsUUFBSyxXQUFMO0FBQ0EsVUFBTyxDQUFDLEtBQUssU0FBTCxFQUFSO0FBQTBCLFNBQUssUUFBTCxDQUFjLElBQWQ7QUFBMUIsSUFDQSxLQUFLLE1BQUwsR0FBYyxLQUFLLFNBQUwsRUFBZDtBQUNBLFFBQUssV0FBTCxHQUFtQixLQUFLLGNBQUwsRUFBbkI7QUFDQSxRQUFLLFVBQUwsR0FBa0IsS0FBSyxhQUFMLEVBQWxCO0FBQ0EsUUFBSyxTQUFMLEdBQWlCLENBQWpCO0FBQ0EsUUFBSyxTQUFMLEdBQWlCLENBQWpCOztBQUVBO0FBQ0EsUUFBSyxXQUFMOztBQUVBOztBQUVBLFFBQUssa0JBQUwsQ0FBd0IsWUFBeEIsRUFBc0MsSUFBdEM7QUFDQSxVQUFPLElBQVA7QUFDQTs7QUFFRDs7Ozs7OztnQ0FJYztBQUNiLFFBQUssTUFBTCxDQUFZLE9BQVosQ0FBb0I7QUFBQSxXQUFTLE1BQU0sS0FBTixFQUFUO0FBQUEsSUFBcEI7QUFDQSxVQUFPLElBQVA7QUFDQTs7QUFFRDs7Ozs7Ozs4QkFJWTtBQUNYLFVBQU8sS0FBSyxNQUFMLENBQVksR0FBWixDQUFnQjtBQUFBLFdBQVMsTUFBTSxNQUFmO0FBQUEsSUFBaEIsQ0FBUDtBQUNBOztBQUVEOzs7Ozs7O2tDQUlnQjtBQUNmLFVBQU8sS0FBSyxHQUFMLENBQVMsS0FBVCxDQUFlLElBQWYsRUFBcUIsS0FBSyxNQUFMLENBQVksR0FBWixDQUFnQjtBQUFBLFdBQVMsTUFBTSxLQUFmO0FBQUEsSUFBaEIsQ0FBckIsQ0FBUDtBQUNBOztBQUVEOzs7Ozs7O21DQUlpQjtBQUNoQixVQUFPLEtBQUssTUFBTCxDQUFZLE1BQVosQ0FBbUIsVUFBQyxDQUFELEVBQUksQ0FBSixFQUFVO0FBQUMsV0FBTyxFQUFDLFFBQVEsRUFBQyxRQUFRLEVBQUUsTUFBRixDQUFTLE1BQVQsR0FBa0IsRUFBRSxNQUFGLENBQVMsTUFBcEMsRUFBVCxFQUFQO0FBQTZELElBQTNGLEVBQTZGLEVBQUMsUUFBUSxFQUFDLFFBQVEsQ0FBVCxFQUFULEVBQTdGLEVBQW9ILE1BQXBILENBQTJILE1BQWxJO0FBQ0E7O0FBRUQ7Ozs7Ozs7Z0NBSWM7QUFDYixVQUFPLEtBQUssVUFBTCxHQUFrQixLQUFLLFFBQXZCLEdBQWtDLEtBQUssS0FBdkMsR0FBK0MsRUFBdEQ7QUFDQTs7QUFFRDs7Ozs7Ozt5Q0FJdUI7QUFDdEIsVUFBTyxLQUFLLEtBQUwsQ0FBVyxDQUFDLEtBQUssVUFBTCxHQUFrQixLQUFLLElBQXhCLElBQWdDLEtBQUssUUFBckMsR0FBZ0QsS0FBSyxLQUFyRCxHQUE2RCxFQUF4RSxDQUFQO0FBQ0E7O0FBRUQ7Ozs7Ozs7NENBSTBCO0FBQ3pCLFVBQU8sS0FBSyxLQUFMLENBQVcsS0FBSyxvQkFBTCxLQUE4QixLQUFLLFdBQUwsRUFBOUIsR0FBbUQsR0FBOUQsQ0FBUDtBQUNBOztBQUVEOzs7Ozs7O21DQUlpQjtBQUNoQjtBQUNBLFVBQU8sS0FBSyxLQUFLLE1BQUwsQ0FBWSxNQUFaLEdBQXFCLENBQTFCLEdBQThCLEtBQUssTUFBTCxDQUFZLE1BQVosQ0FBbUIsVUFBQyxDQUFELEVBQUksQ0FBSixFQUFVO0FBQUMsV0FBTyxFQUFDLFNBQVMsRUFBRSxPQUFGLEdBQVksRUFBRSxPQUF4QixFQUFQO0FBQXdDLElBQXRFLEVBQXdFLEVBQUMsU0FBUyxDQUFWLEVBQXhFLEVBQXNGLE9BQTNIO0FBQ0E7O0FBRUQ7Ozs7Ozs7aUNBSWU7QUFDZCxVQUFPLEtBQUssTUFBTCxDQUFZLE1BQVosQ0FBbUIsVUFBQyxDQUFELEVBQUksQ0FBSixFQUFVO0FBQUMsV0FBTyxFQUFDLFlBQVksRUFBRSxVQUFGLEdBQWUsRUFBRSxVQUE5QixFQUFQO0FBQWlELElBQS9FLEVBQWlGLEVBQUMsWUFBWSxDQUFiLEVBQWpGLEVBQWtHLFVBQXpHO0FBQ0E7O0FBRUQ7Ozs7Ozs7Ozs7OEJBT1k7QUFDWCxPQUFJLEtBQUssU0FBTCxFQUFKLEVBQXNCO0FBQ3JCLFdBQU8sS0FBSyxZQUFMLE1BQXVCLEtBQUssV0FBbkM7QUFDQTs7QUFFRCxVQUFPLEtBQUssY0FBTCxNQUF5QixLQUFLLE1BQUwsQ0FBWSxNQUE1QztBQUNBOztBQUVEOzs7Ozs7O21DQUlpQjtBQUNoQixVQUFPLEtBQUssS0FBTCxDQUFXLENBQUUsSUFBSSxJQUFKLEVBQUQsQ0FBYSxPQUFiLEtBQXlCLEtBQUssU0FBL0IsSUFBNEMsSUFBNUMsSUFBb0QsS0FBSyxRQUFMLElBQWlCLEtBQUssS0FBTCxHQUFhLEVBQTlCLENBQXBELENBQVgsSUFBcUcsS0FBSyxTQUFqSDtBQUNBOztBQUVEOzs7Ozs7Ozs0QkFLVSxLLEVBQU87QUFDaEIsUUFBSyxrQkFBTCxDQUF3QixXQUF4QixFQUFxQyxLQUFyQztBQUNBLFVBQU8sSUFBUDtBQUNBOztBQUVEOzs7Ozs7Ozs7cUJBTUcsVyxFQUFhLEUsRUFBSTtBQUNuQixPQUFJLENBQUMsS0FBSyxjQUFMLENBQW9CLGNBQXBCLENBQW1DLFdBQW5DLENBQUwsRUFBc0QsS0FBSyxjQUFMLENBQW9CLFdBQXBCLElBQW1DLEVBQW5DO0FBQ3RELFFBQUssY0FBTCxDQUFvQixXQUFwQixFQUFpQyxJQUFqQyxDQUFzQyxFQUF0QztBQUNBLFVBQU8sSUFBUDtBQUNBOztBQUVEOzs7Ozs7Ozs7cUNBTW1CLFcsRUFBYSxJLEVBQU07QUFDckMsT0FBSSxLQUFLLGNBQUwsQ0FBb0IsY0FBcEIsQ0FBbUMsV0FBbkMsQ0FBSixFQUFxRCxLQUFLLGNBQUwsQ0FBb0IsV0FBcEIsRUFBaUMsT0FBakMsQ0FBeUM7QUFBQSxXQUFNLEdBQUcsUUFBUSxFQUFYLENBQU47QUFBQSxJQUF6QztBQUNyRCxVQUFPLElBQVA7QUFDQTs7Ozs7O0FBSUYsUUFBUSxNQUFSLEdBQWlCLE1BQWpCOzs7Ozs7Ozs7QUN4ZEEsSUFBTSxZQUFZLFFBQVEsYUFBUixFQUF1QixTQUF6QztBQUNBLElBQU0sUUFBUSxRQUFRLFNBQVIsRUFBbUIsS0FBakM7O0FBRUE7Ozs7SUFHTSxLO0FBQ0wsZ0JBQVksS0FBWixFQUFtQixJQUFuQixFQUF5QjtBQUFBOztBQUN4QixPQUFLLE9BQUwsR0FBZSxJQUFmO0FBQ0EsT0FBSyxVQUFMLEdBQWtCLENBQWxCO0FBQ0EsT0FBSyxPQUFMLEdBQWUsQ0FBZjtBQUNBLE9BQUssUUFBTCxHQUFnQixDQUFoQjtBQUNBLE9BQUssVUFBTCxHQUFrQixJQUFsQjtBQUNBLE9BQUssS0FBTCxHQUFhLEtBQWI7QUFDQSxPQUFLLElBQUwsR0FBWSxJQUFaO0FBQ0EsT0FBSyxLQUFMLEdBQWEsQ0FBYjtBQUNBLE9BQUssWUFBTCxHQUFvQixDQUFwQjtBQUNBLE9BQUssTUFBTCxHQUFjLEVBQWQ7QUFDQTs7QUFFRDs7Ozs7Ozs7MEJBSVE7QUFDUCxRQUFLLE9BQUwsR0FBZSxJQUFmO0FBQ0EsUUFBSyxVQUFMLEdBQWtCLENBQWxCO0FBQ0EsUUFBSyxPQUFMLEdBQWUsQ0FBZjtBQUNBLFFBQUssUUFBTCxHQUFnQixDQUFoQjtBQUNBLFFBQUssVUFBTCxHQUFrQixJQUFsQjtBQUNBLFFBQUssS0FBTCxHQUFhLENBQWI7QUFDQSxRQUFLLFlBQUwsR0FBb0IsQ0FBcEI7QUFDQSxVQUFPLElBQVA7QUFDQTs7QUFFRDs7Ozs7OzsyQkFJUztBQUNSLFFBQUssT0FBTCxHQUFlLElBQWY7QUFDQSxVQUFPLElBQVA7QUFDQTs7QUFFRDs7Ozs7Ozs0QkFJVTtBQUNULFFBQUssT0FBTCxHQUFlLEtBQWY7QUFDQSxVQUFPLElBQVA7QUFDQTs7QUFFRDs7Ozs7Ozs7c0NBS29CLEksRUFBTTtBQUN6QixVQUFPLFFBQVEsQ0FBZjs7QUFFQSxRQUFLLElBQUksQ0FBVCxJQUFjLEtBQUssTUFBbkIsRUFBMkI7QUFDMUIsUUFBSSxLQUFLLE1BQUwsQ0FBWSxDQUFaLEVBQWUsSUFBZixJQUF1QixJQUEzQixFQUFpQztBQUNoQyxVQUFLLFVBQUwsR0FBa0IsQ0FBbEI7QUFDQSxZQUFPLElBQVA7QUFDQTtBQUNEO0FBQ0Q7O0FBRUQ7Ozs7Ozs7bUNBSWlCO0FBQ2hCLFVBQU8sS0FBSyxJQUFMLENBQVUsS0FBSyxPQUFmLENBQVA7QUFDQTs7QUFFRDs7Ozs7OztzQ0FJb0I7QUFDbkI7QUFDQTtBQUNBO0FBQ0c7QUFDQTtBQUNBLE9BQUksY0FBYyxLQUFLLGNBQUwsRUFBbEI7QUFDQSxPQUFJLFlBQVksQ0FBaEI7O0FBRUgsVUFBTyxlQUFlLEdBQXRCLEVBQTJCO0FBQzFCLGtCQUFjLEtBQUssSUFBTCxDQUFVLEtBQUssT0FBTCxHQUFlLFNBQXpCLENBQWQ7QUFDQTtBQUNBOztBQUVELFVBQU8sU0FBUDtBQUNBOztBQUVEOzs7Ozs7OzZCQUlXO0FBQ1YsVUFBTyxNQUFNLFVBQU4sQ0FBaUIsS0FBSyxJQUFMLENBQVUsUUFBVixDQUFtQixLQUFLLE9BQXhCLEVBQWlDLEtBQUssT0FBTCxHQUFlLEtBQUssaUJBQUwsRUFBaEQsQ0FBakIsQ0FBUDtBQUNBOztBQUVEOzs7Ozs7Ozs4QkFLWSxXLEVBQWEsTSxFQUFRO0FBQ2hDLFlBQVMsVUFBVSxLQUFuQjs7QUFFQSxPQUFJLE1BQUosRUFBWTtBQUNYLFFBQUksZUFBZSxjQUFjLEtBQUssUUFBdEM7QUFDQSxRQUFJLFFBQVEsS0FBSyxRQUFMLEVBQVo7QUFDQSxRQUFJLGFBQWEsZ0JBQWdCLEtBQWpDOztBQUVBLFFBQUksS0FBSyxPQUFMLEdBQWUsS0FBSyxJQUFMLENBQVUsTUFBekIsS0FBb0MsVUFBVSxVQUE5QyxDQUFKLEVBQStEO0FBQzlELFNBQUksU0FBUSxLQUFLLFVBQUwsRUFBWjtBQUNBLFNBQUksS0FBSyxPQUFULEVBQWtCLE9BQU8sTUFBUDtBQUNsQjtBQUNBO0FBRUQsSUFYRCxNQVdPO0FBQ047QUFDQSxRQUFJLEtBQUssTUFBTCxDQUFZLEtBQUssVUFBakIsS0FBZ0MsS0FBSyxNQUFMLENBQVksS0FBSyxVQUFqQixFQUE2QixJQUE3QixJQUFxQyxXQUF6RSxFQUFzRjtBQUNyRixVQUFLLFVBQUw7QUFDQSxTQUFJLEtBQUssT0FBVCxFQUFrQixPQUFPLEtBQUssTUFBTCxDQUFZLEtBQUssVUFBTCxHQUFrQixDQUE5QixDQUFQO0FBQ2xCO0FBQ0Q7O0FBRUQsVUFBTyxJQUFQO0FBQ0E7O0FBRUQ7Ozs7Ozs7O2dDQUtjLGUsRUFBaUI7QUFDOUIsT0FBSSxjQUFjLEtBQUssT0FBdkI7QUFDQSxPQUFJLFlBQVksQ0FBaEI7QUFDQSxPQUFJLFNBQVMsTUFBTSxVQUFOLENBQWlCLEtBQUssSUFBTCxDQUFVLFFBQVYsQ0FBbUIsa0JBQWtCLENBQXJDLEVBQXdDLGtCQUFrQixDQUFsQixHQUFzQixTQUE5RCxDQUFqQixDQUFiO0FBQ0EsT0FBSSxlQUFlLE1BQW5COztBQUVBLFVBQU8sTUFBTSxjQUFOLENBQXFCLEtBQUssSUFBTCxDQUFVLFFBQVYsQ0FBbUIsa0JBQWtCLFNBQWxCLEdBQThCLENBQWpELEVBQW9ELGtCQUFrQixTQUFsQixHQUE4QixNQUE5QixHQUF1QyxDQUEzRixDQUFyQixDQUFQO0FBQ0E7O0FBRUQ7Ozs7Ozs7K0JBSWE7QUFDWixPQUFJLGtCQUFrQixLQUFLLE9BQUwsR0FBZSxLQUFLLGlCQUFMLEVBQXJDO0FBQ0EsT0FBSSxZQUFZLEVBQWhCO0FBQ0EsT0FBSSxpQkFBaUIsS0FBSyxpQkFBTCxFQUFyQjtBQUNBLGFBQVUsS0FBVixHQUFrQixLQUFLLEtBQUwsR0FBYSxDQUEvQjtBQUNBLGFBQVUsS0FBVixHQUFrQixLQUFLLFFBQUwsRUFBbEI7QUFDQSxRQUFLLFFBQUwsR0FBZ0IsS0FBSyxRQUFMLEdBQWdCLFVBQVUsS0FBMUM7QUFDQSxRQUFLLFlBQUwsSUFBcUIsVUFBVSxLQUEvQjtBQUNBLGFBQVUsSUFBVixHQUFpQixLQUFLLFlBQXRCO0FBQ0EsYUFBVSxTQUFWLEdBQXNCLEtBQUssT0FBM0I7O0FBRUE7QUFDQSxPQUFJLEtBQUssSUFBTCxDQUFVLGVBQVYsS0FBOEIsSUFBbEMsRUFBd0M7QUFDdkM7O0FBRUE7QUFDQTtBQUNBOztBQUVBLFlBQU8sS0FBSyxJQUFMLENBQVUsa0JBQWtCLENBQTVCLENBQVA7QUFDQyxVQUFLLElBQUw7QUFBVztBQUNWLGdCQUFVLElBQVYsR0FBaUIsaUJBQWpCO0FBQ0E7QUFDRCxVQUFLLElBQUw7QUFBVztBQUNWLGdCQUFVLElBQVYsR0FBaUIsWUFBakI7QUFDQSxnQkFBVSxNQUFWLEdBQW1CLEtBQUssYUFBTCxDQUFtQixlQUFuQixDQUFuQjtBQUNBO0FBQ0QsVUFBSyxJQUFMO0FBQVc7QUFDVixnQkFBVSxJQUFWLEdBQWlCLGtCQUFqQjtBQUNBO0FBQ0QsVUFBSyxJQUFMO0FBQVc7QUFDVixnQkFBVSxJQUFWLEdBQWlCLHFCQUFqQjtBQUNBLGdCQUFVLE1BQVYsR0FBbUIsS0FBSyxhQUFMLENBQW1CLGVBQW5CLENBQW5CO0FBQ0E7QUFDRCxVQUFLLElBQUw7QUFBVztBQUNWLGdCQUFVLElBQVYsR0FBaUIsaUJBQWpCO0FBQ0EsZ0JBQVUsTUFBVixHQUFtQixLQUFLLGFBQUwsQ0FBbUIsZUFBbkIsQ0FBbkI7QUFDQTtBQUNELFVBQUssSUFBTDtBQUFXO0FBQ1YsZ0JBQVUsSUFBVixHQUFpQixPQUFqQjtBQUNBLGdCQUFVLE1BQVYsR0FBbUIsS0FBSyxhQUFMLENBQW1CLGVBQW5CLENBQW5CO0FBQ0E7QUFDRCxVQUFLLElBQUw7QUFBVztBQUNWLGdCQUFVLElBQVYsR0FBaUIsUUFBakI7QUFDQTtBQUNELFVBQUssSUFBTDtBQUFXO0FBQ1YsZ0JBQVUsSUFBVixHQUFpQixXQUFqQjtBQUNBLGdCQUFVLE1BQVYsR0FBbUIsS0FBSyxhQUFMLENBQW1CLGVBQW5CLENBQW5CO0FBQ0E7QUFDRCxVQUFLLElBQUw7QUFBVztBQUNWLGdCQUFVLElBQVYsR0FBaUIsYUFBakI7QUFDQSxnQkFBVSxNQUFWLEdBQW1CLEtBQUssYUFBTCxDQUFtQixlQUFuQixDQUFuQjtBQUNBO0FBQ0QsVUFBSyxJQUFMO0FBQVc7QUFDVixnQkFBVSxJQUFWLEdBQWlCLHFCQUFqQjtBQUNBO0FBQ0QsVUFBSyxJQUFMO0FBQVc7QUFDVixnQkFBVSxJQUFWLEdBQWlCLFdBQWpCO0FBQ0EsZ0JBQVUsSUFBVixHQUFpQixNQUFNLGFBQU4sQ0FBb0IsQ0FBQyxLQUFLLElBQUwsQ0FBVSxrQkFBa0IsQ0FBNUIsQ0FBRCxDQUFwQixDQUFqQjtBQUNBO0FBQ0QsVUFBSyxJQUFMO0FBQVc7QUFDVixnQkFBVSxJQUFWLEdBQWlCLGNBQWpCO0FBQ0E7QUFDRCxVQUFLLElBQUw7QUFBVztBQUNWLGdCQUFVLElBQVYsR0FBaUIsV0FBakI7QUFDQSxnQkFBVSxJQUFWLEdBQWlCLEtBQUssS0FBTCxDQUFXLFdBQVcsTUFBTSxhQUFOLENBQW9CLEtBQUssSUFBTCxDQUFVLFFBQVYsQ0FBbUIsa0JBQWtCLENBQXJDLEVBQXdDLGtCQUFrQixDQUExRCxDQUFwQixDQUF0QixDQUFqQjtBQUNBLFdBQUssS0FBTCxHQUFhLFVBQVUsSUFBdkI7QUFDQTtBQUNELFVBQUssSUFBTDtBQUFXO0FBQ1YsZ0JBQVUsSUFBVixHQUFpQixjQUFqQjtBQUNBO0FBQ0QsVUFBSyxJQUFMO0FBQVc7QUFDVjtBQUNBLGdCQUFVLElBQVYsR0FBaUIsZ0JBQWpCO0FBQ0EsZ0JBQVUsSUFBVixHQUFpQixLQUFLLElBQUwsQ0FBVSxRQUFWLENBQW1CLGtCQUFrQixDQUFyQyxFQUF3QyxrQkFBa0IsQ0FBMUQsQ0FBakI7QUFDQSxnQkFBVSxhQUFWLEdBQTBCLEtBQUssVUFBVSxJQUFWLENBQWUsQ0FBZixDQUFMLEdBQXlCLEdBQXpCLEdBQStCLEtBQUssR0FBTCxDQUFTLFVBQVUsSUFBVixDQUFlLENBQWYsQ0FBVCxFQUE0QixDQUE1QixDQUF6RDtBQUNBO0FBQ0QsVUFBSyxJQUFMO0FBQVc7QUFDVjtBQUNBLGdCQUFVLElBQVYsR0FBaUIsZUFBakI7QUFDQSxnQkFBVSxJQUFWLEdBQWlCLEtBQUssSUFBTCxDQUFVLFFBQVYsQ0FBbUIsa0JBQWtCLENBQXJDLEVBQXdDLGtCQUFrQixDQUExRCxDQUFqQjs7QUFFQSxVQUFJLFVBQVUsSUFBVixDQUFlLENBQWYsS0FBcUIsQ0FBekIsRUFBNEI7QUFDM0IsaUJBQVUsWUFBVixHQUF5QixVQUFVLGdCQUFWLENBQTJCLFVBQVUsSUFBVixDQUFlLENBQWYsQ0FBM0IsQ0FBekI7QUFFQSxPQUhELE1BR08sSUFBSSxVQUFVLElBQVYsQ0FBZSxDQUFmLElBQW9CLENBQXhCLEVBQTJCO0FBQ2pDLGlCQUFVLFlBQVYsR0FBeUIsVUFBVSxpQkFBVixDQUE0QixLQUFLLEdBQUwsQ0FBUyxVQUFVLElBQVYsQ0FBZSxDQUFmLENBQVQsQ0FBNUIsQ0FBekI7QUFDQTs7QUFFRCxVQUFJLFVBQVUsSUFBVixDQUFlLENBQWYsS0FBcUIsQ0FBekIsRUFBNEI7QUFDM0IsaUJBQVUsWUFBVixJQUEwQixRQUExQjtBQUVBLE9BSEQsTUFHTyxJQUFJLFVBQVUsSUFBVixDQUFlLENBQWYsS0FBcUIsQ0FBekIsRUFBNEI7QUFDbEMsaUJBQVUsWUFBVixJQUEwQixRQUExQjtBQUNBOztBQUVEO0FBQ0QsVUFBSyxJQUFMO0FBQVc7QUFDVixnQkFBVSxJQUFWLEdBQWlCLCtCQUFqQjtBQUNBO0FBQ0Q7QUFDQyxnQkFBVSxJQUFWLEdBQWlCLGNBQWMsS0FBSyxJQUFMLENBQVUsa0JBQWtCLENBQTVCLEVBQStCLFFBQS9CLENBQXdDLEVBQXhDLENBQS9CO0FBQ0E7QUFuRkY7O0FBc0ZBLFFBQUksU0FBUyxLQUFLLElBQUwsQ0FBVSxLQUFLLE9BQUwsR0FBZSxjQUFmLEdBQWdDLENBQTFDLENBQWI7QUFDQTs7QUFFQSxTQUFLLE9BQUwsSUFBZ0IsaUJBQWlCLENBQWpCLEdBQXFCLE1BQXJDO0FBRUEsSUFsR0QsTUFrR08sSUFBRyxLQUFLLElBQUwsQ0FBVSxlQUFWLEtBQThCLElBQWpDLEVBQXVDO0FBQzdDO0FBQ0EsY0FBVSxJQUFWLEdBQWlCLE9BQWpCO0FBQ0EsUUFBSSxTQUFTLEtBQUssSUFBTCxDQUFVLEtBQUssT0FBTCxHQUFlLGNBQWYsR0FBZ0MsQ0FBMUMsQ0FBYjtBQUNBLFNBQUssT0FBTCxJQUFnQixpQkFBaUIsQ0FBakIsR0FBcUIsTUFBckM7QUFFQSxJQU5NLE1BTUE7QUFDTjtBQUNBLFFBQUksS0FBSyxJQUFMLENBQVUsZUFBVixJQUE2QixJQUFqQyxFQUF1QztBQUN0QztBQUNBLGVBQVUsT0FBVixHQUFvQixJQUFwQjtBQUNBLGVBQVUsVUFBVixHQUF1QixLQUFLLElBQUwsQ0FBVSxlQUFWLENBQXZCO0FBQ0EsZUFBVSxRQUFWLEdBQXFCLFVBQVUsS0FBVixDQUFnQixLQUFLLElBQUwsQ0FBVSxlQUFWLENBQWhCLENBQXJCO0FBQ0EsZUFBVSxRQUFWLEdBQXFCLEtBQUssSUFBTCxDQUFVLGtCQUFrQixDQUE1QixDQUFyQjs7QUFFQSxTQUFJLEtBQUssVUFBTCxJQUFtQixJQUF2QixFQUE2QjtBQUM1QixnQkFBVSxJQUFWLEdBQWlCLFVBQWpCO0FBQ0EsZ0JBQVUsT0FBVixHQUFvQixLQUFLLFVBQUwsR0FBa0IsSUFBbEIsR0FBeUIsQ0FBN0M7QUFFQSxNQUpELE1BSU8sSUFBSSxLQUFLLFVBQUwsSUFBbUIsSUFBdkIsRUFBNkI7QUFDbkMsZ0JBQVUsSUFBVixHQUFpQixTQUFqQjtBQUNBLGdCQUFVLE9BQVYsR0FBb0IsS0FBSyxVQUFMLEdBQWtCLElBQWxCLEdBQXlCLENBQTdDO0FBQ0E7O0FBRUQsVUFBSyxPQUFMLElBQWdCLGlCQUFpQixDQUFqQztBQUVBLEtBbEJELE1Ba0JPO0FBQ04sVUFBSyxVQUFMLEdBQWtCLEtBQUssSUFBTCxDQUFVLGVBQVYsQ0FBbEI7O0FBRUEsU0FBSSxLQUFLLElBQUwsQ0FBVSxlQUFWLEtBQThCLElBQWxDLEVBQXdDO0FBQ3ZDO0FBQ0EsZ0JBQVUsSUFBVixHQUFpQixVQUFqQjtBQUNBLGdCQUFVLE9BQVYsR0FBb0IsS0FBSyxVQUFMLEdBQWtCLElBQWxCLEdBQXlCLENBQTdDO0FBQ0EsZ0JBQVUsVUFBVixHQUF1QixLQUFLLElBQUwsQ0FBVSxrQkFBa0IsQ0FBNUIsQ0FBdkI7QUFDQSxnQkFBVSxRQUFWLEdBQXFCLFVBQVUsS0FBVixDQUFnQixLQUFLLElBQUwsQ0FBVSxrQkFBa0IsQ0FBNUIsQ0FBaEIsQ0FBckI7QUFDQSxnQkFBVSxRQUFWLEdBQXFCLEtBQUssS0FBTCxDQUFXLEtBQUssSUFBTCxDQUFVLGtCQUFrQixDQUE1QixJQUFpQyxHQUFqQyxHQUF1QyxHQUFsRCxDQUFyQjtBQUNBLFdBQUssT0FBTCxJQUFnQixpQkFBaUIsQ0FBakM7QUFFQSxNQVRELE1BU08sSUFBSSxLQUFLLElBQUwsQ0FBVSxlQUFWLEtBQThCLElBQWxDLEVBQXdDO0FBQzlDO0FBQ0EsZ0JBQVUsSUFBVixHQUFpQixTQUFqQjtBQUNBLGdCQUFVLE9BQVYsR0FBb0IsS0FBSyxVQUFMLEdBQWtCLElBQWxCLEdBQXlCLENBQTdDO0FBQ0EsZ0JBQVUsVUFBVixHQUF1QixLQUFLLElBQUwsQ0FBVSxrQkFBa0IsQ0FBNUIsQ0FBdkI7QUFDQSxnQkFBVSxRQUFWLEdBQXFCLFVBQVUsS0FBVixDQUFnQixLQUFLLElBQUwsQ0FBVSxrQkFBa0IsQ0FBNUIsQ0FBaEIsQ0FBckI7QUFDQSxnQkFBVSxRQUFWLEdBQXFCLEtBQUssS0FBTCxDQUFXLEtBQUssSUFBTCxDQUFVLGtCQUFrQixDQUE1QixJQUFpQyxHQUFqQyxHQUF1QyxHQUFsRCxDQUFyQjtBQUNBLFdBQUssT0FBTCxJQUFnQixpQkFBaUIsQ0FBakM7QUFFQSxNQVRNLE1BU0EsSUFBSSxLQUFLLElBQUwsQ0FBVSxlQUFWLEtBQThCLElBQWxDLEVBQXdDO0FBQzlDO0FBQ0EsZ0JBQVUsSUFBVixHQUFpQix5QkFBakI7QUFDQSxnQkFBVSxPQUFWLEdBQW9CLEtBQUssVUFBTCxHQUFrQixJQUFsQixHQUF5QixDQUE3QztBQUNBLGdCQUFVLElBQVYsR0FBaUIsVUFBVSxLQUFWLENBQWdCLEtBQUssSUFBTCxDQUFVLGtCQUFrQixDQUE1QixDQUFoQixDQUFqQjtBQUNBLGdCQUFVLFFBQVYsR0FBcUIsTUFBTSxDQUFOLENBQXJCO0FBQ0EsV0FBSyxPQUFMLElBQWdCLGlCQUFpQixDQUFqQztBQUVBLE1BUk0sTUFRQSxJQUFJLEtBQUssSUFBTCxDQUFVLGVBQVYsS0FBOEIsSUFBbEMsRUFBd0M7QUFDOUM7QUFDQSxnQkFBVSxJQUFWLEdBQWlCLG1CQUFqQjtBQUNBLGdCQUFVLE9BQVYsR0FBb0IsS0FBSyxVQUFMLEdBQWtCLElBQWxCLEdBQXlCLENBQTdDO0FBQ0EsZ0JBQVUsTUFBVixHQUFtQixLQUFLLElBQUwsQ0FBVSxrQkFBa0IsQ0FBNUIsQ0FBbkI7QUFDQSxnQkFBVSxLQUFWLEdBQWtCLEtBQUssSUFBTCxDQUFVLGtCQUFrQixDQUE1QixDQUFsQjtBQUNBLFdBQUssT0FBTCxJQUFnQixpQkFBaUIsQ0FBakM7QUFFQSxNQVJNLE1BUUEsSUFBSSxLQUFLLElBQUwsQ0FBVSxlQUFWLEtBQThCLElBQWxDLEVBQXdDO0FBQzlDO0FBQ0EsZ0JBQVUsSUFBVixHQUFpQixnQkFBakI7QUFDQSxnQkFBVSxPQUFWLEdBQW9CLEtBQUssVUFBTCxHQUFrQixJQUFsQixHQUF5QixDQUE3QztBQUNBLGdCQUFVLEtBQVYsR0FBa0IsS0FBSyxJQUFMLENBQVUsa0JBQWtCLENBQTVCLENBQWxCO0FBQ0EsV0FBSyxPQUFMLElBQWdCLGlCQUFpQixDQUFqQztBQUVBLE1BUE0sTUFPQSxJQUFJLEtBQUssSUFBTCxDQUFVLGVBQVYsS0FBOEIsSUFBbEMsRUFBd0M7QUFDOUM7QUFDQSxnQkFBVSxJQUFWLEdBQWlCLHNCQUFqQjtBQUNBLGdCQUFVLE9BQVYsR0FBb0IsS0FBSyxVQUFMLEdBQWtCLElBQWxCLEdBQXlCLENBQTdDO0FBQ0EsV0FBSyxPQUFMLElBQWdCLGlCQUFpQixDQUFqQztBQUVBLE1BTk0sTUFNQSxJQUFJLEtBQUssSUFBTCxDQUFVLGVBQVYsS0FBOEIsSUFBbEMsRUFBd0M7QUFDOUM7QUFDQSxnQkFBVSxJQUFWLEdBQWlCLFlBQWpCO0FBQ0EsZ0JBQVUsT0FBVixHQUFvQixLQUFLLFVBQUwsR0FBa0IsSUFBbEIsR0FBeUIsQ0FBN0M7QUFDQSxXQUFLLE9BQUwsSUFBZ0IsaUJBQWlCLENBQWpDO0FBRUEsTUFOTSxNQU1BO0FBQ04sZ0JBQVUsSUFBVixHQUFpQix3QkFBd0IsS0FBSyxPQUFMLENBQWEsUUFBYixFQUF4QixHQUFrRCxHQUFsRCxHQUF5RCxnQkFBZ0IsUUFBaEIsRUFBekQsR0FBc0YsR0FBdEYsR0FBNEYsS0FBSyxJQUFMLENBQVUsTUFBdkg7QUFDQTtBQUNEO0FBQ0Q7O0FBRUQsUUFBSyxLQUFMLElBQWMsVUFBVSxLQUF4QjtBQUNBLFFBQUssTUFBTCxDQUFZLElBQVosQ0FBaUIsU0FBakI7O0FBRUEsVUFBTyxTQUFQO0FBQ0E7O0FBRUQ7Ozs7Ozs7K0JBSWE7QUFDWixPQUFJLEtBQUssSUFBTCxDQUFVLEtBQUssT0FBTCxHQUFlLENBQXpCLEtBQStCLElBQS9CLElBQXVDLEtBQUssSUFBTCxDQUFVLEtBQUssT0FBTCxHQUFlLENBQXpCLEtBQStCLElBQXRFLElBQThFLEtBQUssSUFBTCxDQUFVLEtBQUssT0FBTCxHQUFlLENBQXpCLEtBQStCLElBQWpILEVBQXVIO0FBQ3RILFdBQU8sSUFBUDtBQUNBOztBQUVELFVBQU8sS0FBUDtBQUNBOzs7Ozs7QUFHRixPQUFPLE9BQVAsQ0FBZSxLQUFmLEdBQXVCLEtBQXZCOzs7Ozs7Ozs7O0FDblhBOzs7SUFHTSxLOzs7Ozs7Ozs7QUFFTDs7Ozs7NEJBS2lCLEksRUFBTTtBQUN0QjtBQUNBLFVBQU8sQ0FBQyxNQUFNLEtBQUssUUFBTCxDQUFjLEVBQWQsQ0FBUCxFQUEwQixLQUExQixDQUFnQyxDQUFDLENBQWpDLENBQVA7QUFDQTs7QUFFRDs7Ozs7Ozs7NkJBS2tCLFMsRUFBVztBQUM1QixPQUFJLE1BQU0sRUFBVjtBQUNBLGFBQVUsT0FBVixDQUFrQjtBQUFBLFdBQVEsSUFBSSxJQUFKLENBQVMsTUFBTSxTQUFOLENBQWdCLElBQWhCLENBQVQsQ0FBUjtBQUFBLElBQWxCO0FBQ0EsVUFBTyxJQUFJLElBQUosQ0FBUyxFQUFULENBQVA7QUFDQTs7QUFFRDs7Ozs7Ozs7OEJBS21CLFMsRUFBVztBQUM3QixVQUFPLFNBQVMsU0FBVCxFQUFvQixFQUFwQixDQUFQO0FBQ0E7O0FBRUQ7Ozs7Ozs7O2dDQUtxQixTLEVBQVc7QUFDL0IsVUFBTyxNQUFNLFdBQU4sQ0FBa0IsTUFBTSxVQUFOLENBQWlCLFNBQWpCLENBQWxCLENBQVA7QUFDQTs7QUFFRDs7Ozs7Ozs7aUNBS3NCLFMsRUFBVztBQUNoQyxPQUFJLFVBQVUsRUFBZDtBQUNBLGFBQVUsT0FBVixDQUFrQjtBQUFBLFdBQVEsUUFBUSxJQUFSLENBQWEsT0FBTyxZQUFQLENBQW9CLElBQXBCLENBQWIsQ0FBUjtBQUFBLElBQWxCO0FBQ0EsVUFBTyxRQUFRLElBQVIsQ0FBYSxFQUFiLENBQVA7QUFDQTs7QUFFRDs7Ozs7Ozs7OEJBS21CLEcsRUFBSztBQUNwQixVQUFPLENBQUMsUUFBUSxDQUFULEVBQVksUUFBWixDQUFxQixDQUFyQixDQUFQO0FBQ0g7O0FBRUQ7Ozs7Ozs7OzZCQUtrQixTLEVBQVc7QUFDNUIsT0FBSSxTQUFTLENBQWI7QUFDQSxhQUFVLE9BQVYsQ0FBa0Isa0JBQVU7QUFDM0IsUUFBSSxJQUFJLE1BQVI7QUFDQSxRQUFJLElBQUksSUFBUixFQUFjO0FBQ2IsZUFBVyxJQUFJLElBQWY7QUFDQSxnQkFBVyxDQUFYO0FBQ0EsS0FIRCxNQUdPO0FBQ047QUFDQSxlQUFVLENBQVY7QUFDQTtBQUNELElBVEQ7O0FBV0EsVUFBTyxNQUFQO0FBQ0E7O0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7OztjQUtZLE0sRUFBUTtBQUNuQixPQUFJLE9BQU8sSUFBUCxLQUFnQixVQUFwQixFQUFnQyxPQUFPLEtBQUssTUFBTCxDQUFQO0FBQ2hDLFVBQU8sSUFBSSxNQUFKLENBQVcsTUFBWCxFQUFtQixRQUFuQixFQUE2QixRQUE3QixDQUFzQyxRQUF0QyxDQUFQO0FBQ0EsRzs7Ozs7O0FBR0YsUUFBUSxLQUFSLEdBQWdCLEtBQWhCIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24oKXtmdW5jdGlvbiBlKHQsbixyKXtmdW5jdGlvbiBzKG8sdSl7aWYoIW5bb10pe2lmKCF0W29dKXt2YXIgYT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2lmKCF1JiZhKXJldHVybiBhKG8sITApO2lmKGkpcmV0dXJuIGkobywhMCk7dmFyIGY9bmV3IEVycm9yKFwiQ2Fubm90IGZpbmQgbW9kdWxlICdcIitvK1wiJ1wiKTt0aHJvdyBmLmNvZGU9XCJNT0RVTEVfTk9UX0ZPVU5EXCIsZn12YXIgbD1uW29dPXtleHBvcnRzOnt9fTt0W29dWzBdLmNhbGwobC5leHBvcnRzLGZ1bmN0aW9uKGUpe3ZhciBuPXRbb11bMV1bZV07cmV0dXJuIHMobj9uOmUpfSxsLGwuZXhwb3J0cyxlLHQsbixyKX1yZXR1cm4gbltvXS5leHBvcnRzfXZhciBpPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7Zm9yKHZhciBvPTA7bzxyLmxlbmd0aDtvKyspcyhyW29dKTtyZXR1cm4gc31yZXR1cm4gZX0pKCkiLCIvKipcbiAqIENvbnN0YW50cyB1c2VkIGluIHBsYXllci5cbiAqL1xudmFyIENvbnN0YW50cyA9IHtcblx0VkVSU0lPTjogJzIuMC4zJyxcblx0Tk9URVM6IFtdLFxuXHRDSVJDTEVfT0ZfRk9VUlRIUzogWydDJywgJ0YnLCAnQmInLCAnRWInLCAnQWInLCAnRGInLCAnR2InLCAnQ2InLCAnRmInLCAnQmJiJywgJ0ViYicsICdBYmInXSxcblx0Q0lSQ0xFX09GX0ZJRlRIUzogWydDJywgJ0cnLCAnRCcsICdBJywgJ0UnLCAnQicsICdGIycsICdDIycsICdHIycsICdEIycsICdBIycsICdFIyddXG59O1xuXG4vLyBCdWlsZHMgbm90ZXMgb2JqZWN0IGZvciByZWZlcmVuY2UgYWdhaW5zdCBiaW5hcnkgdmFsdWVzLlxudmFyIGFsbE5vdGVzID0gW1snQyddLCBbJ0MjJywnRGInXSwgWydEJ10sIFsnRCMnLCdFYiddLCBbJ0UnXSxbJ0YnXSwgWydGIycsJ0diJ10sIFsnRyddLCBbJ0cjJywnQWInXSwgWydBJ10sIFsnQSMnLCdCYiddLCBbJ0InXV07XG52YXIgY291bnRlciA9IDA7XG5cbi8vIEFsbCBhdmFpbGFibGUgb2N0YXZlcy5cbmZvciAobGV0IGkgPSAtMTsgaSA8PSA5OyBpKyspIHtcblx0YWxsTm90ZXMuZm9yRWFjaChub3RlR3JvdXAgPT4ge1xuXHRcdG5vdGVHcm91cC5mb3JFYWNoKG5vdGUgPT4gQ29uc3RhbnRzLk5PVEVTW2NvdW50ZXJdID0gbm90ZSArIGkpO1xuXHRcdGNvdW50ZXIgKys7XG5cdH0pO1xufVxuXG5leHBvcnRzLkNvbnN0YW50cyA9IENvbnN0YW50czsiLCJjb25zdCBQbGF5ZXIgPSByZXF1aXJlKFwiLi9wbGF5ZXJcIik7XG5jb25zdCBVdGlscyA9IHJlcXVpcmUoXCIuL3V0aWxzXCIpO1xuY29uc3QgQ29uc3RhbnRzID0gcmVxdWlyZShcIi4vY29uc3RhbnRzXCIpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgICBQbGF5ZXI6UGxheWVyLlBsYXllcixcbiAgICBVdGlsczpVdGlscy5VdGlscyxcbiAgICBDb25zdGFudHM6Q29uc3RhbnRzLkNvbnN0YW50c1xufSIsImNvbnN0IFV0aWxzID0gcmVxdWlyZShcIi4vdXRpbHNcIikuVXRpbHM7XG5jb25zdCBUcmFjayA9IHJlcXVpcmUoXCIuL3RyYWNrXCIpLlRyYWNrO1xuXG4vLyBQb2x5ZmlsbCBVaW50OEFycmF5LmZvckVhY2g6IERvZXNuJ3QgZXhpc3Qgb24gU2FmYXJpIDwxMFxuaWYgKCFVaW50OEFycmF5LnByb3RvdHlwZS5mb3JFYWNoKSB7XG5cdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShVaW50OEFycmF5LnByb3RvdHlwZSwgJ2ZvckVhY2gnLCB7XG5cdFx0dmFsdWU6IEFycmF5LnByb3RvdHlwZS5mb3JFYWNoXG5cdH0pO1xufVxuXG4vKipcbiAqIE1haW4gcGxheWVyIGNsYXNzLiAgQ29udGFpbnMgbWV0aG9kcyB0byBsb2FkIGZpbGVzLCBzdGFydCwgc3RvcC5cbiAqIEBwYXJhbSB7ZnVuY3Rpb259IC0gQ2FsbGJhY2sgdG8gZmlyZSBmb3IgZWFjaCBNSURJIGV2ZW50LiAgQ2FuIGFsc28gYmUgYWRkZWQgd2l0aCBvbignbWlkaUV2ZW50JywgZm4pXG4gKiBAcGFyYW0ge2FycmF5fSAtIEFycmF5IGJ1ZmZlciBvZiBNSURJIGZpbGUgKG9wdGlvbmFsKS5cbiAqL1xuY2xhc3MgUGxheWVyIHtcblx0Y29uc3RydWN0b3IoZXZlbnRIYW5kbGVyLCBidWZmZXIpIHtcblx0XHR0aGlzLnNhbXBsZVJhdGUgPSA1OyAvLyBtaWxsaXNlY29uZHNcblx0XHR0aGlzLnN0YXJ0VGltZSA9IDA7XG5cdFx0dGhpcy5idWZmZXIgPSBidWZmZXIgfHwgbnVsbDtcblx0XHR0aGlzLmRpdmlzaW9uO1xuXHRcdHRoaXMuZm9ybWF0O1xuXHRcdHRoaXMuc2V0SW50ZXJ2YWxJZCA9IGZhbHNlO1xuXHRcdHRoaXMudHJhY2tzID0gW107XG5cdFx0dGhpcy5pbnN0cnVtZW50cyA9IFtdO1xuXHRcdHRoaXMuZGVmYXVsdFRlbXBvID0gMTIwO1xuXHRcdHRoaXMudGVtcG8gPSBudWxsO1xuXHRcdHRoaXMuc3RhcnRUaWNrID0gMDtcblx0XHR0aGlzLnRpY2sgPSAwO1xuXHRcdHRoaXMubGFzdFRpY2sgPSBudWxsO1xuXHRcdHRoaXMuaW5Mb29wID0gZmFsc2U7XG5cdFx0dGhpcy50b3RhbFRpY2tzID0gMDtcblx0XHR0aGlzLmV2ZW50cyA9IFtdO1xuXHRcdHRoaXMudG90YWxFdmVudHMgPSAwO1xuXHRcdHRoaXMuZXZlbnRMaXN0ZW5lcnMgPSB7fTtcblxuXHRcdGlmICh0eXBlb2YoZXZlbnRIYW5kbGVyKSA9PT0gJ2Z1bmN0aW9uJykgdGhpcy5vbignbWlkaUV2ZW50JywgZXZlbnRIYW5kbGVyKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBMb2FkIGEgZmlsZSBpbnRvIHRoZSBwbGF5ZXIgKE5vZGUuanMgb25seSkuXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gUGF0aCBvZiBmaWxlLlxuXHQgKiBAcmV0dXJuIHtQbGF5ZXJ9XG5cdCAqL1xuXHRsb2FkRmlsZShwYXRoKSB7XG5cdFx0dmFyIGZzID0gcmVxdWlyZSgnZnMnKTtcblx0XHR0aGlzLmJ1ZmZlciA9IGZzLnJlYWRGaWxlU3luYyhwYXRoKTtcblx0XHRyZXR1cm4gdGhpcy5maWxlTG9hZGVkKCk7XG5cdH1cblxuXHQvKipcblx0ICogTG9hZCBhbiBhcnJheSBidWZmZXIgaW50byB0aGUgcGxheWVyLlxuXHQgKiBAcGFyYW0ge2FycmF5fSBhcnJheUJ1ZmZlciAtIEFycmF5IGJ1ZmZlciBvZiBmaWxlIHRvIGJlIGxvYWRlZC5cblx0ICogQHJldHVybiB7UGxheWVyfVxuXHQgKi9cblx0bG9hZEFycmF5QnVmZmVyKGFycmF5QnVmZmVyKSB7XG5cdFx0dGhpcy5idWZmZXIgPSBuZXcgVWludDhBcnJheShhcnJheUJ1ZmZlcik7XG5cdFx0cmV0dXJuIHRoaXMuZmlsZUxvYWRlZCgpO1xuXHR9XG5cblx0LyoqXG5cdCAqIExvYWQgYSBkYXRhIFVSSSBpbnRvIHRoZSBwbGF5ZXIuXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSBkYXRhVXJpIC0gRGF0YSBVUkkgdG8gYmUgbG9hZGVkLlxuXHQgKiBAcmV0dXJuIHtQbGF5ZXJ9XG5cdCAqL1xuXHRsb2FkRGF0YVVyaShkYXRhVXJpKSB7XG5cdFx0Ly8gY29udmVydCBiYXNlNjQgdG8gcmF3IGJpbmFyeSBkYXRhIGhlbGQgaW4gYSBzdHJpbmcuXG5cdFx0Ly8gZG9lc24ndCBoYW5kbGUgVVJMRW5jb2RlZCBEYXRhVVJJcyAtIHNlZSBTTyBhbnN3ZXIgIzY4NTAyNzYgZm9yIGNvZGUgdGhhdCBkb2VzIHRoaXNcblx0XHR2YXIgYnl0ZVN0cmluZyA9IFV0aWxzLmF0b2IoZGF0YVVyaS5zcGxpdCgnLCcpWzFdKTtcblxuXHRcdC8vIHdyaXRlIHRoZSBieXRlcyBvZiB0aGUgc3RyaW5nIHRvIGFuIEFycmF5QnVmZmVyXG5cdFx0dmFyIGlhID0gbmV3IFVpbnQ4QXJyYXkoYnl0ZVN0cmluZy5sZW5ndGgpO1xuXHRcdGZvciAodmFyIGkgPSAwOyBpIDwgYnl0ZVN0cmluZy5sZW5ndGg7IGkrKykge1xuXHRcdFx0aWFbaV0gPSBieXRlU3RyaW5nLmNoYXJDb2RlQXQoaSk7XG5cdFx0fVxuXG5cdFx0dGhpcy5idWZmZXIgPSBpYTtcblx0XHRyZXR1cm4gdGhpcy5maWxlTG9hZGVkKCk7XG5cdH1cblxuXHQvKipcblx0ICogR2V0IGZpbGVzaXplIG9mIGxvYWRlZCBmaWxlIGluIG51bWJlciBvZiBieXRlcy5cblx0ICogQHJldHVybiB7bnVtYmVyfSAtIFRoZSBmaWxlc2l6ZS5cblx0ICovXG5cdGdldEZpbGVzaXplKCkge1xuXHRcdHJldHVybiB0aGlzLmJ1ZmZlciA/IHRoaXMuYnVmZmVyLmxlbmd0aCA6IDA7XG5cdH1cblxuXHQvKipcblx0ICogU2V0cyBkZWZhdWx0IHRlbXBvLCBwYXJzZXMgZmlsZSBmb3IgbmVjZXNzYXJ5IGluZm9ybWF0aW9uLCBhbmQgZG9lcyBhIGRyeSBydW4gdG8gY2FsY3VsYXRlIHRvdGFsIGxlbmd0aC5cblx0ICogUG9wdWxhdGVzIHRoaXMuZXZlbnRzICYgdGhpcy50b3RhbFRpY2tzLlxuXHQgKiBAcmV0dXJuIHtQbGF5ZXJ9XG5cdCAqL1xuXHRmaWxlTG9hZGVkKCkge1xuXHRcdGlmICghdGhpcy52YWxpZGF0ZSgpKSB0aHJvdyAnSW52YWxpZCBNSURJIGZpbGU7IHNob3VsZCBzdGFydCB3aXRoIE1UaGQnO1xuXHRcdHJldHVybiB0aGlzLnNldFRlbXBvKHRoaXMuZGVmYXVsdFRlbXBvKS5nZXREaXZpc2lvbigpLmdldEZvcm1hdCgpLmdldFRyYWNrcygpLmRyeVJ1bigpO1xuXHR9XG5cblx0LyoqXG5cdCAqIFZhbGlkYXRlcyBmaWxlIHVzaW5nIHNpbXBsZSBtZWFucyAtIGZpcnN0IGZvdXIgYnl0ZXMgc2hvdWxkID09IE1UaGQuXG5cdCAqIEByZXR1cm4ge2Jvb2xlYW59XG5cdCAqL1xuXHR2YWxpZGF0ZSgpIHtcblx0XHRyZXR1cm4gVXRpbHMuYnl0ZXNUb0xldHRlcnModGhpcy5idWZmZXIuc3ViYXJyYXkoMCwgNCkpID09PSAnTVRoZCc7XG5cdH1cblxuXHQvKipcblx0ICogR2V0cyBNSURJIGZpbGUgZm9ybWF0IGZvciBsb2FkZWQgZmlsZS5cblx0ICogQHJldHVybiB7UGxheWVyfVxuXHQgKi9cblx0Z2V0Rm9ybWF0KCkge1xuXHRcdC8qXG5cdFx0TUlESSBmaWxlcyBjb21lIGluIDMgdmFyaWF0aW9uczpcblx0XHRGb3JtYXQgMCB3aGljaCBjb250YWluIGEgc2luZ2xlIHRyYWNrXG5cdFx0Rm9ybWF0IDEgd2hpY2ggY29udGFpbiBvbmUgb3IgbW9yZSBzaW11bHRhbmVvdXMgdHJhY2tzXG5cdFx0KGllIGFsbCB0cmFja3MgYXJlIHRvIGJlIHBsYXllZCBzaW11bHRhbmVvdXNseSkuXG5cdFx0Rm9ybWF0IDIgd2hpY2ggY29udGFpbiBvbmUgb3IgbW9yZSBpbmRlcGVuZGFudCB0cmFja3Ncblx0XHQoaWUgZWFjaCB0cmFjayBpcyB0byBiZSBwbGF5ZWQgaW5kZXBlbmRhbnRseSBvZiB0aGUgb3RoZXJzKS5cblx0XHRyZXR1cm4gVXRpbHMuYnl0ZXNUb051bWJlcih0aGlzLmJ1ZmZlci5zdWJhcnJheSg4LCAxMCkpO1xuXHRcdCovXG5cblx0XHR0aGlzLmZvcm1hdCA9IFV0aWxzLmJ5dGVzVG9OdW1iZXIodGhpcy5idWZmZXIuc3ViYXJyYXkoOCwgMTApKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG5cdC8qKlxuXHQgKiBQYXJzZXMgb3V0IHRyYWNrcywgcGxhY2VzIHRoZW0gaW4gdGhpcy50cmFja3MgYW5kIGluaXRpYWxpemVzIHRoaXMucG9pbnRlcnNcblx0ICogQHJldHVybiB7UGxheWVyfVxuXHQgKi9cblx0Z2V0VHJhY2tzKCkge1xuXHRcdHRoaXMudHJhY2tzID0gW107XG5cdFx0bGV0IHRyYWNrT2Zmc2V0ID0gMDtcblx0XHR3aGlsZSAodHJhY2tPZmZzZXQgPCB0aGlzLmJ1ZmZlci5sZW5ndGgpIHtcblx0XHRcdGlmIChVdGlscy5ieXRlc1RvTGV0dGVycyh0aGlzLmJ1ZmZlci5zdWJhcnJheSh0cmFja09mZnNldCwgdHJhY2tPZmZzZXQgKyA0KSkgPT0gJ01UcmsnKSB7XG5cdFx0XHRcdGxldCB0cmFja0xlbmd0aCA9IFV0aWxzLmJ5dGVzVG9OdW1iZXIodGhpcy5idWZmZXIuc3ViYXJyYXkodHJhY2tPZmZzZXQgKyA0LCB0cmFja09mZnNldCArIDgpKTtcblx0XHRcdFx0dGhpcy50cmFja3MucHVzaChuZXcgVHJhY2sodGhpcy50cmFja3MubGVuZ3RoLCB0aGlzLmJ1ZmZlci5zdWJhcnJheSh0cmFja09mZnNldCArIDgsIHRyYWNrT2Zmc2V0ICsgOCArIHRyYWNrTGVuZ3RoKSkpO1xuXHRcdFx0fVxuXG5cdFx0XHR0cmFja09mZnNldCArPSBVdGlscy5ieXRlc1RvTnVtYmVyKHRoaXMuYnVmZmVyLnN1YmFycmF5KHRyYWNrT2Zmc2V0ICsgNCwgdHJhY2tPZmZzZXQgKyA4KSkgKyA4O1xuXHRcdH1cblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG5cdC8qKlxuXHQgKiBFbmFibGVzIGEgdHJhY2sgZm9yIHBsYXlpbmcuXG5cdCAqIEBwYXJhbSB7bnVtYmVyfSB0cmFja051bWJlciAtIFRyYWNrIG51bWJlclxuXHQgKiBAcmV0dXJuIHtQbGF5ZXJ9XG5cdCAqL1xuXHRlbmFibGVUcmFjayh0cmFja051bWJlcikge1xuXHRcdHRoaXMudHJhY2tzW3RyYWNrTnVtYmVyIC0gMV0uZW5hYmxlKCk7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH1cblxuXHQvKipcblx0ICogRGlzYWJsZXMgYSB0cmFjayBmb3IgcGxheWluZy5cblx0ICogQHBhcmFtIHtudW1iZXJ9IC0gVHJhY2sgbnVtYmVyXG5cdCAqIEByZXR1cm4ge1BsYXllcn1cblx0ICovXG5cdGRpc2FibGVUcmFjayh0cmFja051bWJlcikge1xuXHRcdHRoaXMudHJhY2tzW3RyYWNrTnVtYmVyIC0gMV0uZGlzYWJsZSgpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIEdldHMgcXVhcnRlciBub3RlIGRpdmlzaW9uIG9mIGxvYWRlZCBNSURJIGZpbGUuXG5cdCAqIEByZXR1cm4ge1BsYXllcn1cblx0ICovXG5cdGdldERpdmlzaW9uKCkge1xuXHRcdHRoaXMuZGl2aXNpb24gPSBVdGlscy5ieXRlc1RvTnVtYmVyKHRoaXMuYnVmZmVyLnN1YmFycmF5KDEyLCAxNCkpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIFRoZSBtYWluIHBsYXkgbG9vcC5cblx0ICogQHBhcmFtIHtib29sZWFufSAtIEluZGljYXRlcyB3aGV0aGVyIG9yIG5vdCB0aGlzIGlzIGJlaW5nIGNhbGxlZCBzaW1wbHkgZm9yIHBhcnNpbmcgcHVycG9zZXMuICBEaXNyZWdhcmRzIHRpbWluZyBpZiBzby5cblx0ICogQHJldHVybiB7dW5kZWZpbmVkfVxuXHQgKi9cblx0cGxheUxvb3AoZHJ5UnVuKSB7XG5cdFx0aWYgKCF0aGlzLmluTG9vcCkge1xuXHRcdFx0dGhpcy5pbkxvb3AgPSB0cnVlO1xuXHRcdFx0dGhpcy50aWNrID0gdGhpcy5nZXRDdXJyZW50VGljaygpO1xuXG5cdFx0XHR0aGlzLnRyYWNrcy5mb3JFYWNoKGZ1bmN0aW9uKHRyYWNrKSB7XG5cdFx0XHRcdC8vIEhhbmRsZSBuZXh0IGV2ZW50XG5cdFx0XHRcdGlmICghZHJ5UnVuICYmIHRoaXMuZW5kT2ZGaWxlKCkpIHtcblx0XHRcdFx0XHQvL2NvbnNvbGUubG9nKCdlbmQgb2YgZmlsZScpXG5cdFx0XHRcdFx0dGhpcy50cmlnZ2VyUGxheWVyRXZlbnQoJ2VuZE9mRmlsZScpO1xuXHRcdFx0XHRcdHRoaXMuc3RvcCgpO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdGxldCBldmVudCA9IHRyYWNrLmhhbmRsZUV2ZW50KHRoaXMudGljaywgZHJ5UnVuKTtcblxuXHRcdFx0XHRcdGlmIChkcnlSdW4gJiYgZXZlbnQpIHtcblx0XHRcdFx0XHRcdGlmIChldmVudC5oYXNPd25Qcm9wZXJ0eSgnbmFtZScpICYmIGV2ZW50Lm5hbWUgPT09ICdTZXQgVGVtcG8nKSB7XG5cdFx0XHRcdFx0XHRcdC8vIEdyYWIgdGVtcG8gaWYgYXZhaWxhYmxlLlxuXHRcdFx0XHRcdFx0XHR0aGlzLnNldFRlbXBvKGV2ZW50LmRhdGEpO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0aWYgKGV2ZW50Lmhhc093blByb3BlcnR5KCduYW1lJykgJiYgZXZlbnQubmFtZSA9PT0gJ1Byb2dyYW0gQ2hhbmdlJykge1xuXHRcdFx0XHRcdFx0XHRpZiAoIXRoaXMuaW5zdHJ1bWVudHMuaW5jbHVkZXMoZXZlbnQudmFsdWUpKSB7XG5cdFx0XHRcdFx0XHRcdFx0dGhpcy5pbnN0cnVtZW50cy5wdXNoKGV2ZW50LnZhbHVlKTtcblx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH0gZWxzZSBpZiAoZXZlbnQpIHRoaXMuZW1pdEV2ZW50KGV2ZW50KTtcblx0XHRcdFx0fVxuXG5cdFx0XHR9LCB0aGlzKTtcblxuXHRcdFx0aWYgKCFkcnlSdW4pIHRoaXMudHJpZ2dlclBsYXllckV2ZW50KCdwbGF5aW5nJywge3RpY2s6IHRoaXMudGlja30pO1xuXHRcdFx0dGhpcy5pbkxvb3AgPSBmYWxzZTtcblx0XHR9XG5cdH1cblxuXHQvKipcblx0ICogU2V0dGVyIGZvciB0ZW1wby5cblx0ICogQHBhcmFtIHtudW1iZXJ9IC0gVGVtcG8gaW4gYnBtIChkZWZhdWx0cyB0byAxMjApXG5cdCAqL1xuXHRzZXRUZW1wbyh0ZW1wbykge1xuXHRcdHRoaXMudGVtcG8gPSB0ZW1wbztcblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG5cdC8qKlxuXHQgKiBTZXR0ZXIgZm9yIHN0YXJ0VGltZS5cblx0ICogQHBhcmFtIHtudW1iZXJ9IC0gVVRDIHRpbWVzdGFtcFxuXHQgKi9cblx0c2V0U3RhcnRUaW1lKHN0YXJ0VGltZSkge1xuXHRcdHRoaXMuc3RhcnRUaW1lID0gc3RhcnRUaW1lO1xuXHR9XG5cblx0LyoqXG5cdCAqIFN0YXJ0IHBsYXlpbmcgbG9hZGVkIE1JREkgZmlsZSBpZiBub3QgYWxyZWFkeSBwbGF5aW5nLlxuXHQgKiBAcmV0dXJuIHtQbGF5ZXJ9XG5cdCAqL1xuXHRwbGF5KCkge1xuXHRcdGlmICh0aGlzLmlzUGxheWluZygpKSB0aHJvdyAnQWxyZWFkeSBwbGF5aW5nLi4uJztcblxuXHRcdC8vIEluaXRpYWxpemVcblx0XHRpZiAoIXRoaXMuc3RhcnRUaW1lKSB0aGlzLnN0YXJ0VGltZSA9IChuZXcgRGF0ZSgpKS5nZXRUaW1lKCk7XG5cblx0XHQvLyBTdGFydCBwbGF5IGxvb3Bcblx0XHQvL3dpbmRvdy5yZXF1ZXN0QW5pbWF0aW9uRnJhbWUodGhpcy5wbGF5TG9vcC5iaW5kKHRoaXMpKTtcblx0XHR0aGlzLnNldEludGVydmFsSWQgPSBzZXRJbnRlcnZhbCh0aGlzLnBsYXlMb29wLmJpbmQodGhpcyksIHRoaXMuc2FtcGxlUmF0ZSk7XG5cblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG5cdC8qKlxuXHQgKiBQYXVzZXMgcGxheWJhY2sgaWYgcGxheWluZy5cblx0ICogQHJldHVybiB7UGxheWVyfVxuXHQgKi9cblx0cGF1c2UoKSB7XG5cdFx0Y2xlYXJJbnRlcnZhbCh0aGlzLnNldEludGVydmFsSWQpO1xuXHRcdHRoaXMuc2V0SW50ZXJ2YWxJZCA9IGZhbHNlO1xuXHRcdHRoaXMuc3RhcnRUaWNrID0gdGhpcy50aWNrO1xuXHRcdHRoaXMuc3RhcnRUaW1lID0gMDtcblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG5cdC8qKlxuXHQgKiBTdG9wcyBwbGF5YmFjayBpZiBwbGF5aW5nLlxuXHQgKiBAcmV0dXJuIHtQbGF5ZXJ9XG5cdCAqL1xuXHRzdG9wKCkge1xuXHRcdGNsZWFySW50ZXJ2YWwodGhpcy5zZXRJbnRlcnZhbElkKTtcblx0XHR0aGlzLnNldEludGVydmFsSWQgPSBmYWxzZTtcblx0XHR0aGlzLnN0YXJ0VGljayA9IDA7XG5cdFx0dGhpcy5zdGFydFRpbWUgPSAwO1xuXHRcdHRoaXMucmVzZXRUcmFja3MoKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG5cdC8qKlxuXHQgKiBTa2lwcyBwbGF5ZXIgcG9pbnRlciB0byBzcGVjaWZpZWQgdGljay5cblx0ICogQHBhcmFtIHtudW1iZXJ9IC0gVGljayB0byBza2lwIHRvLlxuXHQgKiBAcmV0dXJuIHtQbGF5ZXJ9XG5cdCAqL1xuXHRza2lwVG9UaWNrKHRpY2spIHtcblx0XHR0aGlzLnN0b3AoKTtcblx0XHR0aGlzLnN0YXJ0VGljayA9IHRpY2s7XG5cblx0XHQvLyBOZWVkIHRvIHNldCB0cmFjayBldmVudCBpbmRleGVzIHRvIHRoZSBuZWFyZXN0IHBvc3NpYmxlIGV2ZW50IHRvIHRoZSBzcGVjaWZpZWQgdGljay5cblx0XHR0aGlzLnRyYWNrcy5mb3JFYWNoKGZ1bmN0aW9uKHRyYWNrKSB7XG5cdFx0XHR0cmFjay5zZXRFdmVudEluZGV4QnlUaWNrKHRpY2spO1xuXHRcdH0pO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIFNraXBzIHBsYXllciBwb2ludGVyIHRvIHNwZWNpZmllZCBwZXJjZW50YWdlLlxuXHQgKiBAcGFyYW0ge251bWJlcn0gLSBQZXJjZW50IHZhbHVlIGluIGludGVnZXIgZm9ybWF0LlxuXHQgKiBAcmV0dXJuIHtQbGF5ZXJ9XG5cdCAqL1xuXHRza2lwVG9QZXJjZW50KHBlcmNlbnQpIHtcblx0XHRpZiAocGVyY2VudCA8IDAgfHwgcGVyY2VudCA+IDEwMCkgdGhyb3cgXCJQZXJjZW50IG11c3QgYmUgbnVtYmVyIGJldHdlZW4gMSBhbmQgMTAwLlwiO1xuXHRcdHRoaXMuc2tpcFRvVGljayhNYXRoLnJvdW5kKHBlcmNlbnQgLyAxMDAgKiB0aGlzLnRvdGFsVGlja3MpKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG5cdC8qKlxuXHQgKiBTa2lwcyBwbGF5ZXIgcG9pbnRlciB0byBzcGVjaWZpZWQgc2Vjb25kcy5cblx0ICogQHBhcmFtIHtudW1iZXJ9IC0gU2Vjb25kcyB0byBza2lwIHRvLlxuXHQgKiBAcmV0dXJuIHtQbGF5ZXJ9XG5cdCAqL1xuXHRza2lwVG9TZWNvbmRzKHNlY29uZHMpIHtcblx0XHR2YXIgc29uZ1RpbWUgPSB0aGlzLmdldFNvbmdUaW1lKCk7XG5cdFx0aWYgKHNlY29uZHMgPCAwIHx8IHNlY29uZHMgPiBzb25nVGltZSkgdGhyb3cgc2Vjb25kcyArIFwiIHNlY29uZHMgbm90IHdpdGhpbiBzb25nIHRpbWUgb2YgXCIgKyBzb25nVGltZTtcblx0XHR0aGlzLnNraXBUb1BlcmNlbnQoc2Vjb25kcyAvIHNvbmdUaW1lICogMTAwKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG5cdC8qKlxuXHQgKiBDaGVja3MgaWYgcGxheWVyIGlzIHBsYXlpbmdcblx0ICogQHJldHVybiB7Ym9vbGVhbn1cblx0ICovXG5cdGlzUGxheWluZygpIHtcblx0XHRyZXR1cm4gdGhpcy5zZXRJbnRlcnZhbElkID4gMCB8fCB0eXBlb2YgdGhpcy5zZXRJbnRlcnZhbElkID09PSAnb2JqZWN0Jztcblx0fVxuXG5cdC8qKlxuXHQgKiBQbGF5cyB0aGUgbG9hZGVkIE1JREkgZmlsZSB3aXRob3V0IHJlZ2FyZCBmb3IgdGltaW5nIGFuZCBzYXZlcyBldmVudHMgaW4gdGhpcy5ldmVudHMuICBFc3NlbnRpYWxseSB1c2VkIGFzIGEgcGFyc2VyLlxuXHQgKiBAcmV0dXJuIHtQbGF5ZXJ9XG5cdCAqL1xuXHRkcnlSdW4oKSB7XG5cdFx0Ly8gUmVzZXQgdHJhY2tzIGZpcnN0XG5cdFx0dGhpcy5yZXNldFRyYWNrcygpO1xuXHRcdHdoaWxlICghdGhpcy5lbmRPZkZpbGUoKSkgdGhpcy5wbGF5TG9vcCh0cnVlKTtcblx0XHR0aGlzLmV2ZW50cyA9IHRoaXMuZ2V0RXZlbnRzKCk7XG5cdFx0dGhpcy50b3RhbEV2ZW50cyA9IHRoaXMuZ2V0VG90YWxFdmVudHMoKTtcblx0XHR0aGlzLnRvdGFsVGlja3MgPSB0aGlzLmdldFRvdGFsVGlja3MoKTtcblx0XHR0aGlzLnN0YXJ0VGljayA9IDA7XG5cdFx0dGhpcy5zdGFydFRpbWUgPSAwO1xuXG5cdFx0Ly8gTGVhdmUgdHJhY2tzIGluIHByaXN0aW5lIGNvbmRpc2hcblx0XHR0aGlzLnJlc2V0VHJhY2tzKCk7XG5cblx0XHQvL2NvbnNvbGUubG9nKCdTb25nIHRpbWU6ICcgKyB0aGlzLmdldFNvbmdUaW1lKCkgKyAnIHNlY29uZHMgLyAnICsgdGhpcy50b3RhbFRpY2tzICsgJyB0aWNrcy4nKTtcblxuXHRcdHRoaXMudHJpZ2dlclBsYXllckV2ZW50KCdmaWxlTG9hZGVkJywgdGhpcyk7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH1cblxuXHQvKipcblx0ICogUmVzZXRzIHBsYXkgcG9pbnRlcnMgZm9yIGFsbCB0cmFja3MuXG5cdCAqIEByZXR1cm4ge1BsYXllcn1cblx0ICovXG5cdHJlc2V0VHJhY2tzKCkge1xuXHRcdHRoaXMudHJhY2tzLmZvckVhY2godHJhY2sgPT4gdHJhY2sucmVzZXQoKSk7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH1cblxuXHQvKipcblx0ICogR2V0cyBhbiBhcnJheSBvZiBldmVudHMgZ3JvdXBlZCBieSB0cmFjay5cblx0ICogQHJldHVybiB7YXJyYXl9XG5cdCAqL1xuXHRnZXRFdmVudHMoKSB7XG5cdFx0cmV0dXJuIHRoaXMudHJhY2tzLm1hcCh0cmFjayA9PiB0cmFjay5ldmVudHMpO1xuXHR9XG5cblx0LyoqXG5cdCAqIEdldHMgdG90YWwgbnVtYmVyIG9mIHRpY2tzIGluIHRoZSBsb2FkZWQgTUlESSBmaWxlLlxuXHQgKiBAcmV0dXJuIHtudW1iZXJ9XG5cdCAqL1xuXHRnZXRUb3RhbFRpY2tzKCkge1xuXHRcdHJldHVybiBNYXRoLm1heC5hcHBseShudWxsLCB0aGlzLnRyYWNrcy5tYXAodHJhY2sgPT4gdHJhY2suZGVsdGEpKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBHZXRzIHRvdGFsIG51bWJlciBvZiBldmVudHMgaW4gdGhlIGxvYWRlZCBNSURJIGZpbGUuXG5cdCAqIEByZXR1cm4ge251bWJlcn1cblx0ICovXG5cdGdldFRvdGFsRXZlbnRzKCkge1xuXHRcdHJldHVybiB0aGlzLnRyYWNrcy5yZWR1Y2UoKGEsIGIpID0+IHtyZXR1cm4ge2V2ZW50czoge2xlbmd0aDogYS5ldmVudHMubGVuZ3RoICsgYi5ldmVudHMubGVuZ3RofX19LCB7ZXZlbnRzOiB7bGVuZ3RoOiAwfX0pLmV2ZW50cy5sZW5ndGg7XG5cdH1cblxuXHQvKipcblx0ICogR2V0cyBzb25nIGR1cmF0aW9uIGluIHNlY29uZHMuXG5cdCAqIEByZXR1cm4ge251bWJlcn1cblx0ICovXG5cdGdldFNvbmdUaW1lKCkge1xuXHRcdHJldHVybiB0aGlzLnRvdGFsVGlja3MgLyB0aGlzLmRpdmlzaW9uIC8gdGhpcy50ZW1wbyAqIDYwO1xuXHR9XG5cblx0LyoqXG5cdCAqIEdldHMgcmVtYWluaW5nIG51bWJlciBvZiBzZWNvbmRzIGluIHBsYXliYWNrLlxuXHQgKiBAcmV0dXJuIHtudW1iZXJ9XG5cdCAqL1xuXHRnZXRTb25nVGltZVJlbWFpbmluZygpIHtcblx0XHRyZXR1cm4gTWF0aC5yb3VuZCgodGhpcy50b3RhbFRpY2tzIC0gdGhpcy50aWNrKSAvIHRoaXMuZGl2aXNpb24gLyB0aGlzLnRlbXBvICogNjApO1xuXHR9XG5cblx0LyoqXG5cdCAqIEdldHMgcmVtYWluaW5nIHBlcmNlbnQgb2YgcGxheWJhY2suXG5cdCAqIEByZXR1cm4ge251bWJlcn1cblx0ICovXG5cdGdldFNvbmdQZXJjZW50UmVtYWluaW5nKCkge1xuXHRcdHJldHVybiBNYXRoLnJvdW5kKHRoaXMuZ2V0U29uZ1RpbWVSZW1haW5pbmcoKSAvIHRoaXMuZ2V0U29uZ1RpbWUoKSAqIDEwMCk7XG5cdH1cblxuXHQvKipcblx0ICogTnVtYmVyIG9mIGJ5dGVzIHByb2Nlc3NlZCBpbiB0aGUgbG9hZGVkIE1JREkgZmlsZS5cblx0ICogQHJldHVybiB7bnVtYmVyfVxuXHQgKi9cblx0Ynl0ZXNQcm9jZXNzZWQoKSB7XG5cdFx0Ly8gQ3VycmVudGx5IGFzc3VtZSBoZWFkZXIgY2h1bmsgaXMgc3RyaWN0bHkgMTQgYnl0ZXNcblx0XHRyZXR1cm4gMTQgKyB0aGlzLnRyYWNrcy5sZW5ndGggKiA4ICsgdGhpcy50cmFja3MucmVkdWNlKChhLCBiKSA9PiB7cmV0dXJuIHtwb2ludGVyOiBhLnBvaW50ZXIgKyBiLnBvaW50ZXJ9fSwge3BvaW50ZXI6IDB9KS5wb2ludGVyO1xuXHR9XG5cblx0LyoqXG5cdCAqIE51bWJlciBvZiBldmVudHMgcGxheWVkIHVwIHRvIHRoaXMgcG9pbnQuXG5cdCAqIEByZXR1cm4ge251bWJlcn1cblx0ICovXG5cdGV2ZW50c1BsYXllZCgpIHtcblx0XHRyZXR1cm4gdGhpcy50cmFja3MucmVkdWNlKChhLCBiKSA9PiB7cmV0dXJuIHtldmVudEluZGV4OiBhLmV2ZW50SW5kZXggKyBiLmV2ZW50SW5kZXh9fSwge2V2ZW50SW5kZXg6IDB9KS5ldmVudEluZGV4O1xuXHR9XG5cblx0LyoqXG5cdCAqIERldGVybWluZXMgaWYgdGhlIHBsYXllciBwb2ludGVyIGhhcyByZWFjaGVkIHRoZSBlbmQgb2YgdGhlIGxvYWRlZCBNSURJIGZpbGUuXG5cdCAqIFVzZWQgaW4gdHdvIHdheXM6XG5cdCAqIDEuIElmIHBsYXlpbmcgcmVzdWx0IGlzIGJhc2VkIG9uIGxvYWRlZCBKU09OIGV2ZW50cy5cblx0ICogMi4gSWYgcGFyc2luZyAoZHJ5UnVuKSBpdCdzIGJhc2VkIG9uIHRoZSBhY3R1YWwgYnVmZmVyIGxlbmd0aCB2cyBieXRlcyBwcm9jZXNzZWQuXG5cdCAqIEByZXR1cm4ge2Jvb2xlYW59XG5cdCAqL1xuXHRlbmRPZkZpbGUoKSB7XG5cdFx0aWYgKHRoaXMuaXNQbGF5aW5nKCkpIHtcblx0XHRcdHJldHVybiB0aGlzLmV2ZW50c1BsYXllZCgpID09IHRoaXMudG90YWxFdmVudHM7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIHRoaXMuYnl0ZXNQcm9jZXNzZWQoKSA9PSB0aGlzLmJ1ZmZlci5sZW5ndGg7XG5cdH1cblxuXHQvKipcblx0ICogR2V0cyB0aGUgY3VycmVudCB0aWNrIG51bWJlciBpbiBwbGF5YmFjay5cblx0ICogQHJldHVybiB7bnVtYmVyfVxuXHQgKi9cblx0Z2V0Q3VycmVudFRpY2soKSB7XG5cdFx0cmV0dXJuIE1hdGgucm91bmQoKChuZXcgRGF0ZSgpKS5nZXRUaW1lKCkgLSB0aGlzLnN0YXJ0VGltZSkgLyAxMDAwICogKHRoaXMuZGl2aXNpb24gKiAodGhpcy50ZW1wbyAvIDYwKSkpICsgdGhpcy5zdGFydFRpY2s7XG5cdH1cblxuXHQvKipcblx0ICogU2VuZHMgTUlESSBldmVudCBvdXQgdG8gbGlzdGVuZXIuXG5cdCAqIEBwYXJhbSB7b2JqZWN0fVxuXHQgKiBAcmV0dXJuIHtQbGF5ZXJ9XG5cdCAqL1xuXHRlbWl0RXZlbnQoZXZlbnQpIHtcblx0XHR0aGlzLnRyaWdnZXJQbGF5ZXJFdmVudCgnbWlkaUV2ZW50JywgZXZlbnQpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIFN1YnNjcmliZXMgZXZlbnRzIHRvIGxpc3RlbmVyc1xuXHQgKiBAcGFyYW0ge3N0cmluZ30gLSBOYW1lIG9mIGV2ZW50IHRvIHN1YnNjcmliZSB0by5cblx0ICogQHBhcmFtIHtmdW5jdGlvbn0gLSBDYWxsYmFjayB0byBmaXJlIHdoZW4gZXZlbnQgaXMgYnJvYWRjYXN0LlxuXHQgKiBAcmV0dXJuIHtQbGF5ZXJ9XG5cdCAqL1xuXHRvbihwbGF5ZXJFdmVudCwgZm4pIHtcblx0XHRpZiAoIXRoaXMuZXZlbnRMaXN0ZW5lcnMuaGFzT3duUHJvcGVydHkocGxheWVyRXZlbnQpKSB0aGlzLmV2ZW50TGlzdGVuZXJzW3BsYXllckV2ZW50XSA9IFtdO1xuXHRcdHRoaXMuZXZlbnRMaXN0ZW5lcnNbcGxheWVyRXZlbnRdLnB1c2goZm4pO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIEJyb2FkY2FzdHMgZXZlbnQgdG8gdHJpZ2dlciBzdWJzY3JpYmVkIGNhbGxiYWNrcy5cblx0ICogQHBhcmFtIHtzdHJpbmd9IC0gTmFtZSBvZiBldmVudC5cblx0ICogQHBhcmFtIHtvYmplY3R9IC0gRGF0YSB0byBiZSBwYXNzZWQgdG8gc3Vic2NyaWJlciBjYWxsYmFjay5cblx0ICogQHJldHVybiB7UGxheWVyfVxuXHQgKi9cblx0dHJpZ2dlclBsYXllckV2ZW50KHBsYXllckV2ZW50LCBkYXRhKSB7XG5cdFx0aWYgKHRoaXMuZXZlbnRMaXN0ZW5lcnMuaGFzT3duUHJvcGVydHkocGxheWVyRXZlbnQpKSB0aGlzLmV2ZW50TGlzdGVuZXJzW3BsYXllckV2ZW50XS5mb3JFYWNoKGZuID0+IGZuKGRhdGEgfHwge30pKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG59XG5cbmV4cG9ydHMuUGxheWVyID0gUGxheWVyO1xuIiwiY29uc3QgQ29uc3RhbnRzID0gcmVxdWlyZShcIi4vY29uc3RhbnRzXCIpLkNvbnN0YW50cztcbmNvbnN0IFV0aWxzID0gcmVxdWlyZShcIi4vdXRpbHNcIikuVXRpbHM7XG5cbi8qKlxuICogQ2xhc3MgcmVwcmVzZW50aW5nIGEgdHJhY2suICBDb250YWlucyBtZXRob2RzIGZvciBwYXJzaW5nIGV2ZW50cyBhbmQga2VlcGluZyB0cmFjayBvZiBwb2ludGVyLlxuICovXG5jbGFzcyBUcmFja1x0e1xuXHRjb25zdHJ1Y3RvcihpbmRleCwgZGF0YSkge1xuXHRcdHRoaXMuZW5hYmxlZCA9IHRydWU7XG5cdFx0dGhpcy5ldmVudEluZGV4ID0gMDtcblx0XHR0aGlzLnBvaW50ZXIgPSAwO1xuXHRcdHRoaXMubGFzdFRpY2sgPSAwO1xuXHRcdHRoaXMubGFzdFN0YXR1cyA9IG51bGw7XG5cdFx0dGhpcy5pbmRleCA9IGluZGV4O1xuXHRcdHRoaXMuZGF0YSA9IGRhdGE7XG5cdFx0dGhpcy5kZWx0YSA9IDA7XG5cdFx0dGhpcy5ydW5uaW5nRGVsdGEgPSAwO1xuXHRcdHRoaXMuZXZlbnRzID0gW107XG5cdH1cblxuXHQvKipcblx0ICogUmVzZXRzIGFsbCBzdGF0ZWZ1bCB0cmFjayBpbmZvcm1haW9uIHVzZWQgZHVyaW5nIHBsYXliYWNrLlxuXHQgKiBAcmV0dXJuIHtUcmFja31cblx0ICovXG5cdHJlc2V0KCkge1xuXHRcdHRoaXMuZW5hYmxlZCA9IHRydWU7XG5cdFx0dGhpcy5ldmVudEluZGV4ID0gMDtcblx0XHR0aGlzLnBvaW50ZXIgPSAwO1xuXHRcdHRoaXMubGFzdFRpY2sgPSAwO1xuXHRcdHRoaXMubGFzdFN0YXR1cyA9IG51bGw7XG5cdFx0dGhpcy5kZWx0YSA9IDA7XG5cdFx0dGhpcy5ydW5uaW5nRGVsdGEgPSAwO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIFNldHMgdGhpcyB0cmFjayB0byBiZSBlbmFibGVkIGR1cmluZyBwbGF5YmFjay5cblx0ICogQHJldHVybiB7VHJhY2t9XG5cdCAqL1xuXHRlbmFibGUoKSB7XG5cdFx0dGhpcy5lbmFibGVkID0gdHJ1ZTtcblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG5cdC8qKlxuXHQgKiBTZXRzIHRoaXMgdHJhY2sgdG8gYmUgZGlzYWJsZWQgZHVyaW5nIHBsYXliYWNrLlxuXHQgKiBAcmV0dXJuIHtUcmFja31cblx0ICovXG5cdGRpc2FibGUoKSB7XG5cdFx0dGhpcy5lbmFibGVkID0gZmFsc2U7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH1cblxuXHQvKipcblx0ICogU2V0cyB0aGUgdHJhY2sgZXZlbnQgaW5kZXggdG8gdGhlIG5lYXJlc3QgZXZlbnQgdG8gdGhlIGdpdmVuIHRpY2suXG5cdCAqIEBwYXJhbSB7bnVtYmVyfSB0aWNrXG5cdCAqIEByZXR1cm4ge1RyYWNrfVxuXHQgKi9cblx0c2V0RXZlbnRJbmRleEJ5VGljayh0aWNrKSB7XG5cdFx0dGljayA9IHRpY2sgfHwgMDtcblxuXHRcdGZvciAodmFyIGkgaW4gdGhpcy5ldmVudHMpIHtcblx0XHRcdGlmICh0aGlzLmV2ZW50c1tpXS50aWNrID49IHRpY2spIHtcblx0XHRcdFx0dGhpcy5ldmVudEluZGV4ID0gaTtcblx0XHRcdFx0cmV0dXJuIHRoaXM7XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cblx0LyoqXG5cdCAqIEdldHMgYnl0ZSBsb2NhdGVkIGF0IHBvaW50ZXIgcG9zaXRpb24uXG5cdCAqIEByZXR1cm4ge251bWJlcn1cblx0ICovXG5cdGdldEN1cnJlbnRCeXRlKCkge1xuXHRcdHJldHVybiB0aGlzLmRhdGFbdGhpcy5wb2ludGVyXTtcblx0fVxuXG5cdC8qKlxuXHQgKiBHZXRzIGNvdW50IG9mIGRlbHRhIGJ5dGVzIGFuZCBjdXJyZW50IHBvaW50ZXIgcG9zaXRpb24uXG5cdCAqIEByZXR1cm4ge251bWJlcn1cblx0ICovXG5cdGdldERlbHRhQnl0ZUNvdW50KCkge1xuXHRcdC8vIEdldCBieXRlIGNvdW50IG9mIGRlbHRhIFZMVlxuXHRcdC8vIGh0dHA6Ly93d3cuY2Nhcmgub3JnL2NvdXJzZXMvMjUzL2hhbmRvdXQvdmx2L1xuXHRcdC8vIElmIGJ5dGUgaXMgZ3JlYXRlciBvciBlcXVhbCB0byA4MGggKDEyOCBkZWNpbWFsKSB0aGVuIHRoZSBuZXh0IGJ5dGVcblx0ICAgIC8vIGlzIGFsc28gcGFydCBvZiB0aGUgVkxWLFxuXHQgICBcdC8vIGVsc2UgYnl0ZSBpcyB0aGUgbGFzdCBieXRlIGluIGEgVkxWLlxuXHQgICBcdHZhciBjdXJyZW50Qnl0ZSA9IHRoaXMuZ2V0Q3VycmVudEJ5dGUoKTtcblx0ICAgXHR2YXIgYnl0ZUNvdW50ID0gMTtcblxuXHRcdHdoaWxlIChjdXJyZW50Qnl0ZSA+PSAxMjgpIHtcblx0XHRcdGN1cnJlbnRCeXRlID0gdGhpcy5kYXRhW3RoaXMucG9pbnRlciArIGJ5dGVDb3VudF07XG5cdFx0XHRieXRlQ291bnQrKztcblx0XHR9XG5cblx0XHRyZXR1cm4gYnl0ZUNvdW50O1xuXHR9XG5cblx0LyoqXG5cdCAqIEdldCBkZWx0YSB2YWx1ZSBhdCBjdXJyZW50IHBvaW50ZXIgcG9zaXRpb24uXG5cdCAqIEByZXR1cm4ge251bWJlcn1cblx0ICovXG5cdGdldERlbHRhKCkge1xuXHRcdHJldHVybiBVdGlscy5yZWFkVmFySW50KHRoaXMuZGF0YS5zdWJhcnJheSh0aGlzLnBvaW50ZXIsIHRoaXMucG9pbnRlciArIHRoaXMuZ2V0RGVsdGFCeXRlQ291bnQoKSkpO1xuXHR9XG5cblx0LyoqXG5cdCAqIEhhbmRsZXMgZXZlbnQgd2l0aGluIGEgZ2l2ZW4gdHJhY2sgc3RhcnRpbmcgYXQgc3BlY2lmaWVkIGluZGV4XG5cdCAqIEBwYXJhbSB7bnVtYmVyfSBjdXJyZW50VGlja1xuXHQgKiBAcGFyYW0ge2Jvb2xlYW59IGRyeVJ1biAtIElmIHRydWUgZXZlbnRzIHdpbGwgYmUgcGFyc2VkIGFuZCByZXR1cm5lZCByZWdhcmRsZXNzIG9mIHRpbWUuXG5cdCAqL1xuXHRoYW5kbGVFdmVudChjdXJyZW50VGljaywgZHJ5UnVuKSB7XG5cdFx0ZHJ5UnVuID0gZHJ5UnVuIHx8IGZhbHNlO1xuXG5cdFx0aWYgKGRyeVJ1bikge1xuXHRcdFx0dmFyIGVsYXBzZWRUaWNrcyA9IGN1cnJlbnRUaWNrIC0gdGhpcy5sYXN0VGljaztcblx0XHRcdHZhciBkZWx0YSA9IHRoaXMuZ2V0RGVsdGEoKTtcblx0XHRcdHZhciBldmVudFJlYWR5ID0gZWxhcHNlZFRpY2tzID49IGRlbHRhO1xuXG5cdFx0XHRpZiAodGhpcy5wb2ludGVyIDwgdGhpcy5kYXRhLmxlbmd0aCAmJiAoZHJ5UnVuIHx8IGV2ZW50UmVhZHkpKSB7XG5cdFx0XHRcdGxldCBldmVudCA9IHRoaXMucGFyc2VFdmVudCgpO1xuXHRcdFx0XHRpZiAodGhpcy5lbmFibGVkKSByZXR1cm4gZXZlbnQ7XG5cdFx0XHRcdC8vIFJlY3Vyc2l2ZWx5IGNhbGwgdGhpcyBmdW5jdGlvbiBmb3IgZWFjaCBldmVudCBhaGVhZCB0aGF0IGhhcyAwIGRlbHRhIHRpbWU/XG5cdFx0XHR9XG5cblx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gTGV0J3MgYWN0dWFsbHkgcGxheSB0aGUgTUlESSBmcm9tIHRoZSBnZW5lcmF0ZWQgSlNPTiBldmVudHMgY3JlYXRlZCBieSB0aGUgZHJ5IHJ1bi5cblx0XHRcdGlmICh0aGlzLmV2ZW50c1t0aGlzLmV2ZW50SW5kZXhdICYmIHRoaXMuZXZlbnRzW3RoaXMuZXZlbnRJbmRleF0udGljayA8PSBjdXJyZW50VGljaykge1xuXHRcdFx0XHR0aGlzLmV2ZW50SW5kZXgrKztcblx0XHRcdFx0aWYgKHRoaXMuZW5hYmxlZCkgcmV0dXJuIHRoaXMuZXZlbnRzW3RoaXMuZXZlbnRJbmRleCAtIDFdO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHJldHVybiBudWxsO1xuXHR9XG5cblx0LyoqXG5cdCAqIEdldCBzdHJpbmcgZGF0YSBmcm9tIGV2ZW50LlxuXHQgKiBAcGFyYW0ge251bWJlcn0gZXZlbnRTdGFydEluZGV4XG5cdCAqIEByZXR1cm4ge3N0cmluZ31cblx0ICovXG5cdGdldFN0cmluZ0RhdGEoZXZlbnRTdGFydEluZGV4KSB7XG5cdFx0dmFyIGN1cnJlbnRCeXRlID0gdGhpcy5wb2ludGVyO1xuXHRcdHZhciBieXRlQ291bnQgPSAxO1xuXHRcdHZhciBsZW5ndGggPSBVdGlscy5yZWFkVmFySW50KHRoaXMuZGF0YS5zdWJhcnJheShldmVudFN0YXJ0SW5kZXggKyAyLCBldmVudFN0YXJ0SW5kZXggKyAyICsgYnl0ZUNvdW50KSk7XG5cdFx0dmFyIHN0cmluZ0xlbmd0aCA9IGxlbmd0aDtcblxuXHRcdHJldHVybiBVdGlscy5ieXRlc1RvTGV0dGVycyh0aGlzLmRhdGEuc3ViYXJyYXkoZXZlbnRTdGFydEluZGV4ICsgYnl0ZUNvdW50ICsgMiwgZXZlbnRTdGFydEluZGV4ICsgYnl0ZUNvdW50ICsgbGVuZ3RoICsgMikpO1xuXHR9XG5cblx0LyoqXG5cdCAqIFBhcnNlcyBldmVudCBpbnRvIEpTT04gYW5kIGFkdmFuY2VzIHBvaW50ZXIgZm9yIHRoZSB0cmFja1xuXHQgKiBAcmV0dXJuIHtvYmplY3R9XG5cdCAqL1xuXHRwYXJzZUV2ZW50KCkge1xuXHRcdHZhciBldmVudFN0YXJ0SW5kZXggPSB0aGlzLnBvaW50ZXIgKyB0aGlzLmdldERlbHRhQnl0ZUNvdW50KCk7XG5cdFx0dmFyIGV2ZW50SnNvbiA9IHt9O1xuXHRcdHZhciBkZWx0YUJ5dGVDb3VudCA9IHRoaXMuZ2V0RGVsdGFCeXRlQ291bnQoKTtcblx0XHRldmVudEpzb24udHJhY2sgPSB0aGlzLmluZGV4ICsgMTtcblx0XHRldmVudEpzb24uZGVsdGEgPSB0aGlzLmdldERlbHRhKCk7XG5cdFx0dGhpcy5sYXN0VGljayA9IHRoaXMubGFzdFRpY2sgKyBldmVudEpzb24uZGVsdGE7XG5cdFx0dGhpcy5ydW5uaW5nRGVsdGEgKz0gZXZlbnRKc29uLmRlbHRhO1xuXHRcdGV2ZW50SnNvbi50aWNrID0gdGhpcy5ydW5uaW5nRGVsdGE7XG5cdFx0ZXZlbnRKc29uLmJ5dGVJbmRleCA9IHRoaXMucG9pbnRlcjtcblxuXHRcdC8vZXZlbnRKc29uLnJhdyA9IGV2ZW50O1xuXHRcdGlmICh0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4XSA9PSAweGZmKSB7XG5cdFx0XHQvLyBNZXRhIEV2ZW50XG5cblx0XHRcdC8vIElmIHRoaXMgaXMgYSBtZXRhIGV2ZW50IHdlIHNob3VsZCBlbWl0IHRoZSBkYXRhIGFuZCBpbW1lZGlhdGVseSBtb3ZlIHRvIHRoZSBuZXh0IGV2ZW50XG5cdFx0XHQvLyBvdGhlcndpc2UgaWYgd2UgbGV0IGl0IHJ1biB0aHJvdWdoIHRoZSBuZXh0IGN5Y2xlIGEgc2xpZ2h0IGRlbGF5IHdpbGwgYWNjdW11bGF0ZSBpZiBtdWx0aXBsZSB0cmFja3Ncblx0XHRcdC8vIGFyZSBiZWluZyBwbGF5ZWQgc2ltdWx0YW5lb3VzbHlcblxuXHRcdFx0c3dpdGNoKHRoaXMuZGF0YVtldmVudFN0YXJ0SW5kZXggKyAxXSkge1xuXHRcdFx0XHRjYXNlIDB4MDA6IC8vIFNlcXVlbmNlIE51bWJlclxuXHRcdFx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ1NlcXVlbmNlIE51bWJlcic7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdGNhc2UgMHgwMTogLy8gVGV4dCBFdmVudFxuXHRcdFx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ1RleHQgRXZlbnQnO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5zdHJpbmcgPSB0aGlzLmdldFN0cmluZ0RhdGEoZXZlbnRTdGFydEluZGV4KTtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSAweDAyOiAvLyBDb3B5cmlnaHQgTm90aWNlXG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnQ29weXJpZ2h0IE5vdGljZSc7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdGNhc2UgMHgwMzogLy8gU2VxdWVuY2UvVHJhY2sgTmFtZVxuXHRcdFx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ1NlcXVlbmNlL1RyYWNrIE5hbWUnO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5zdHJpbmcgPSB0aGlzLmdldFN0cmluZ0RhdGEoZXZlbnRTdGFydEluZGV4KTtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSAweDA0OiAvLyBJbnN0cnVtZW50IE5hbWVcblx0XHRcdFx0XHRldmVudEpzb24ubmFtZSA9ICdJbnN0cnVtZW50IE5hbWUnO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5zdHJpbmcgPSB0aGlzLmdldFN0cmluZ0RhdGEoZXZlbnRTdGFydEluZGV4KTtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSAweDA1OiAvLyBMeXJpY1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ0x5cmljJztcblx0XHRcdFx0XHRldmVudEpzb24uc3RyaW5nID0gdGhpcy5nZXRTdHJpbmdEYXRhKGV2ZW50U3RhcnRJbmRleCk7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdGNhc2UgMHgwNjogLy8gTWFya2VyXG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnTWFya2VyJztcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSAweDA3OiAvLyBDdWUgUG9pbnRcblx0XHRcdFx0XHRldmVudEpzb24ubmFtZSA9ICdDdWUgUG9pbnQnO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5zdHJpbmcgPSB0aGlzLmdldFN0cmluZ0RhdGEoZXZlbnRTdGFydEluZGV4KTtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSAweDA5OiAvLyBEZXZpY2UgTmFtZVxuXHRcdFx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ0RldmljZSBOYW1lJztcblx0XHRcdFx0XHRldmVudEpzb24uc3RyaW5nID0gdGhpcy5nZXRTdHJpbmdEYXRhKGV2ZW50U3RhcnRJbmRleCk7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdGNhc2UgMHgyMDogLy8gTUlESSBDaGFubmVsIFByZWZpeFxuXHRcdFx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ01JREkgQ2hhbm5lbCBQcmVmaXgnO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlIDB4MjE6IC8vIE1JREkgUG9ydFxuXHRcdFx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ01JREkgUG9ydCc7XG5cdFx0XHRcdFx0ZXZlbnRKc29uLmRhdGEgPSBVdGlscy5ieXRlc1RvTnVtYmVyKFt0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4ICsgM11dKTtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSAweDJGOiAvLyBFbmQgb2YgVHJhY2tcblx0XHRcdFx0XHRldmVudEpzb24ubmFtZSA9ICdFbmQgb2YgVHJhY2snO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlIDB4NTE6IC8vIFNldCBUZW1wb1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ1NldCBUZW1wbyc7XG5cdFx0XHRcdFx0ZXZlbnRKc29uLmRhdGEgPSBNYXRoLnJvdW5kKDYwMDAwMDAwIC8gVXRpbHMuYnl0ZXNUb051bWJlcih0aGlzLmRhdGEuc3ViYXJyYXkoZXZlbnRTdGFydEluZGV4ICsgMywgZXZlbnRTdGFydEluZGV4ICsgNikpKTtcblx0XHRcdFx0XHR0aGlzLnRlbXBvID0gZXZlbnRKc29uLmRhdGE7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdGNhc2UgMHg1NDogLy8gU01UUEUgT2Zmc2V0XG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnU01UUEUgT2Zmc2V0Jztcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSAweDU4OiAvLyBUaW1lIFNpZ25hdHVyZVxuXHRcdFx0XHRcdC8vIEZGIDU4IDA0IG5uIGRkIGNjIGJiXG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnVGltZSBTaWduYXR1cmUnO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5kYXRhID0gdGhpcy5kYXRhLnN1YmFycmF5KGV2ZW50U3RhcnRJbmRleCArIDMsIGV2ZW50U3RhcnRJbmRleCArIDcpO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi50aW1lU2lnbmF0dXJlID0gXCJcIiArIGV2ZW50SnNvbi5kYXRhWzBdICsgXCIvXCIgKyBNYXRoLnBvdyhldmVudEpzb24uZGF0YVsxXSwgMik7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdGNhc2UgMHg1OTogLy8gS2V5IFNpZ25hdHVyZVxuXHRcdFx0XHRcdC8vIEZGIDU5IDAyIHNmIG1pXG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnS2V5IFNpZ25hdHVyZSc7XG5cdFx0XHRcdFx0ZXZlbnRKc29uLmRhdGEgPSB0aGlzLmRhdGEuc3ViYXJyYXkoZXZlbnRTdGFydEluZGV4ICsgMywgZXZlbnRTdGFydEluZGV4ICsgNSk7XG5cblx0XHRcdFx0XHRpZiAoZXZlbnRKc29uLmRhdGFbMF0gPj0gMCkge1xuXHRcdFx0XHRcdFx0ZXZlbnRKc29uLmtleVNpZ25hdHVyZSA9IENvbnN0YW50cy5DSVJDTEVfT0ZfRklGVEhTW2V2ZW50SnNvbi5kYXRhWzBdXTtcblxuXHRcdFx0XHRcdH0gZWxzZSBpZiAoZXZlbnRKc29uLmRhdGFbMF0gPCAwKSB7XG5cdFx0XHRcdFx0XHRldmVudEpzb24ua2V5U2lnbmF0dXJlID0gQ29uc3RhbnRzLkNJUkNMRV9PRl9GT1VSVEhTW01hdGguYWJzKGV2ZW50SnNvbi5kYXRhWzBdKV07XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0aWYgKGV2ZW50SnNvbi5kYXRhWzFdID09IDApIHtcblx0XHRcdFx0XHRcdGV2ZW50SnNvbi5rZXlTaWduYXR1cmUgKz0gXCIgTWFqb3JcIjtcblxuXHRcdFx0XHRcdH0gZWxzZSBpZiAoZXZlbnRKc29uLmRhdGFbMV0gPT0gMSkge1xuXHRcdFx0XHRcdFx0ZXZlbnRKc29uLmtleVNpZ25hdHVyZSArPSBcIiBNaW5vclwiO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlIDB4N0Y6IC8vIFNlcXVlbmNlci1TcGVjaWZpYyBNZXRhLWV2ZW50XG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnU2VxdWVuY2VyLVNwZWNpZmljIE1ldGEtZXZlbnQnO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRkZWZhdWx0OlxuXHRcdFx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ1Vua25vd246ICcgKyB0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4ICsgMV0udG9TdHJpbmcoMTYpO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXG5cdFx0XHR2YXIgbGVuZ3RoID0gdGhpcy5kYXRhW3RoaXMucG9pbnRlciArIGRlbHRhQnl0ZUNvdW50ICsgMl07XG5cdFx0XHQvLyBTb21lIG1ldGEgZXZlbnRzIHdpbGwgaGF2ZSB2bHYgdGhhdCBuZWVkcyB0byBiZSBoYW5kbGVkXG5cblx0XHRcdHRoaXMucG9pbnRlciArPSBkZWx0YUJ5dGVDb3VudCArIDMgKyBsZW5ndGg7XG5cblx0XHR9IGVsc2UgaWYodGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleF0gPT0gMHhmMCkge1xuXHRcdFx0Ly8gU3lzZXhcblx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ1N5c2V4Jztcblx0XHRcdHZhciBsZW5ndGggPSB0aGlzLmRhdGFbdGhpcy5wb2ludGVyICsgZGVsdGFCeXRlQ291bnQgKyAxXTtcblx0XHRcdHRoaXMucG9pbnRlciArPSBkZWx0YUJ5dGVDb3VudCArIDIgKyBsZW5ndGg7XG5cblx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gVm9pY2UgZXZlbnRcblx0XHRcdGlmICh0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4XSA8IDB4ODApIHtcblx0XHRcdFx0Ly8gUnVubmluZyBzdGF0dXNcblx0XHRcdFx0ZXZlbnRKc29uLnJ1bm5pbmcgPSB0cnVlO1xuXHRcdFx0XHRldmVudEpzb24ubm90ZU51bWJlciA9IHRoaXMuZGF0YVtldmVudFN0YXJ0SW5kZXhdO1xuXHRcdFx0XHRldmVudEpzb24ubm90ZU5hbWUgPSBDb25zdGFudHMuTk9URVNbdGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleF1dO1xuXHRcdFx0XHRldmVudEpzb24udmVsb2NpdHkgPSB0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4ICsgMV07XG5cblx0XHRcdFx0aWYgKHRoaXMubGFzdFN0YXR1cyA8PSAweDhmKSB7XG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnTm90ZSBvZmYnO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5jaGFubmVsID0gdGhpcy5sYXN0U3RhdHVzIC0gMHg4MCArIDE7XG5cblx0XHRcdFx0fSBlbHNlIGlmICh0aGlzLmxhc3RTdGF0dXMgPD0gMHg5Zikge1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ05vdGUgb24nO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5jaGFubmVsID0gdGhpcy5sYXN0U3RhdHVzIC0gMHg5MCArIDE7XG5cdFx0XHRcdH1cblxuXHRcdFx0XHR0aGlzLnBvaW50ZXIgKz0gZGVsdGFCeXRlQ291bnQgKyAyO1xuXG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHR0aGlzLmxhc3RTdGF0dXMgPSB0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4XTtcblxuXHRcdFx0XHRpZiAodGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleF0gPD0gMHg4Zikge1xuXHRcdFx0XHRcdC8vIE5vdGUgb2ZmXG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnTm90ZSBvZmYnO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5jaGFubmVsID0gdGhpcy5sYXN0U3RhdHVzIC0gMHg4MCArIDE7XG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5vdGVOdW1iZXIgPSB0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4ICsgMV07XG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5vdGVOYW1lID0gQ29uc3RhbnRzLk5PVEVTW3RoaXMuZGF0YVtldmVudFN0YXJ0SW5kZXggKyAxXV07XG5cdFx0XHRcdFx0ZXZlbnRKc29uLnZlbG9jaXR5ID0gTWF0aC5yb3VuZCh0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4ICsgMl0gLyAxMjcgKiAxMDApO1xuXHRcdFx0XHRcdHRoaXMucG9pbnRlciArPSBkZWx0YUJ5dGVDb3VudCArIDM7XG5cblx0XHRcdFx0fSBlbHNlIGlmICh0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4XSA8PSAweDlmKSB7XG5cdFx0XHRcdFx0Ly8gTm90ZSBvblxuXHRcdFx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ05vdGUgb24nO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5jaGFubmVsID0gdGhpcy5sYXN0U3RhdHVzIC0gMHg5MCArIDE7XG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5vdGVOdW1iZXIgPSB0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4ICsgMV07XG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5vdGVOYW1lID0gQ29uc3RhbnRzLk5PVEVTW3RoaXMuZGF0YVtldmVudFN0YXJ0SW5kZXggKyAxXV07XG5cdFx0XHRcdFx0ZXZlbnRKc29uLnZlbG9jaXR5ID0gTWF0aC5yb3VuZCh0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4ICsgMl0gLyAxMjcgKiAxMDApO1xuXHRcdFx0XHRcdHRoaXMucG9pbnRlciArPSBkZWx0YUJ5dGVDb3VudCArIDM7XG5cblx0XHRcdFx0fSBlbHNlIGlmICh0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4XSA8PSAweGFmKSB7XG5cdFx0XHRcdFx0Ly8gUG9seXBob25pYyBLZXkgUHJlc3N1cmVcblx0XHRcdFx0XHRldmVudEpzb24ubmFtZSA9ICdQb2x5cGhvbmljIEtleSBQcmVzc3VyZSc7XG5cdFx0XHRcdFx0ZXZlbnRKc29uLmNoYW5uZWwgPSB0aGlzLmxhc3RTdGF0dXMgLSAweGEwICsgMTtcblx0XHRcdFx0XHRldmVudEpzb24ubm90ZSA9IENvbnN0YW50cy5OT1RFU1t0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4ICsgMV1dO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5wcmVzc3VyZSA9IGV2ZW50WzJdO1xuXHRcdFx0XHRcdHRoaXMucG9pbnRlciArPSBkZWx0YUJ5dGVDb3VudCArIDM7XG5cblx0XHRcdFx0fSBlbHNlIGlmICh0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4XSA8PSAweGJmKSB7XG5cdFx0XHRcdFx0Ly8gQ29udHJvbGxlciBDaGFuZ2Vcblx0XHRcdFx0XHRldmVudEpzb24ubmFtZSA9ICdDb250cm9sbGVyIENoYW5nZSc7XG5cdFx0XHRcdFx0ZXZlbnRKc29uLmNoYW5uZWwgPSB0aGlzLmxhc3RTdGF0dXMgLSAweGIwICsgMTtcblx0XHRcdFx0XHRldmVudEpzb24ubnVtYmVyID0gdGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleCArIDFdO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi52YWx1ZSA9IHRoaXMuZGF0YVtldmVudFN0YXJ0SW5kZXggKyAyXTtcblx0XHRcdFx0XHR0aGlzLnBvaW50ZXIgKz0gZGVsdGFCeXRlQ291bnQgKyAzO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAodGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleF0gPD0gMHhjZikge1xuXHRcdFx0XHRcdC8vIFByb2dyYW0gQ2hhbmdlXG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnUHJvZ3JhbSBDaGFuZ2UnO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5jaGFubmVsID0gdGhpcy5sYXN0U3RhdHVzIC0gMHhjMCArIDE7XG5cdFx0XHRcdFx0ZXZlbnRKc29uLnZhbHVlID0gdGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleCArIDFdO1xuXHRcdFx0XHRcdHRoaXMucG9pbnRlciArPSBkZWx0YUJ5dGVDb3VudCArIDI7XG5cblx0XHRcdFx0fSBlbHNlIGlmICh0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4XSA8PSAweGRmKSB7XG5cdFx0XHRcdFx0Ly8gQ2hhbm5lbCBLZXkgUHJlc3N1cmVcblx0XHRcdFx0XHRldmVudEpzb24ubmFtZSA9ICdDaGFubmVsIEtleSBQcmVzc3VyZSc7XG5cdFx0XHRcdFx0ZXZlbnRKc29uLmNoYW5uZWwgPSB0aGlzLmxhc3RTdGF0dXMgLSAweGQwICsgMTtcblx0XHRcdFx0XHR0aGlzLnBvaW50ZXIgKz0gZGVsdGFCeXRlQ291bnQgKyAyO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAodGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleF0gPD0gMHhlZikge1xuXHRcdFx0XHRcdC8vIFBpdGNoIEJlbmRcblx0XHRcdFx0XHRldmVudEpzb24ubmFtZSA9ICdQaXRjaCBCZW5kJztcblx0XHRcdFx0XHRldmVudEpzb24uY2hhbm5lbCA9IHRoaXMubGFzdFN0YXR1cyAtIDB4ZTAgKyAxO1xuXHRcdFx0XHRcdHRoaXMucG9pbnRlciArPSBkZWx0YUJ5dGVDb3VudCArIDM7XG5cblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRldmVudEpzb24ubmFtZSA9ICdVbmtub3duLiAgUG9pbnRlcjogJyArIHRoaXMucG9pbnRlci50b1N0cmluZygpICsgJyAnICArIGV2ZW50U3RhcnRJbmRleC50b1N0cmluZygpICsgJyAnICsgdGhpcy5kYXRhLmxlbmd0aDtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH1cblxuXHRcdHRoaXMuZGVsdGEgKz0gZXZlbnRKc29uLmRlbHRhO1xuXHRcdHRoaXMuZXZlbnRzLnB1c2goZXZlbnRKc29uKTtcblxuXHRcdHJldHVybiBldmVudEpzb247XG5cdH1cblxuXHQvKipcblx0ICogUmV0dXJucyB0cnVlIGlmIHBvaW50ZXIgaGFzIHJlYWNoZWQgdGhlIGVuZCBvZiB0aGUgdHJhY2suXG5cdCAqIEBwYXJhbSB7Ym9vbGVhbn1cblx0ICovXG5cdGVuZE9mVHJhY2soKSB7XG5cdFx0aWYgKHRoaXMuZGF0YVt0aGlzLnBvaW50ZXIgKyAxXSA9PSAweGZmICYmIHRoaXMuZGF0YVt0aGlzLnBvaW50ZXIgKyAyXSA9PSAweDJmICYmIHRoaXMuZGF0YVt0aGlzLnBvaW50ZXIgKyAzXSA9PSAweDAwKSB7XG5cdFx0XHRyZXR1cm4gdHJ1ZTtcblx0XHR9XG5cblx0XHRyZXR1cm4gZmFsc2U7XG5cdH1cbn1cblxubW9kdWxlLmV4cG9ydHMuVHJhY2sgPSBUcmFjazsiLCIvKipcbiAqIENvbnRhaW5zIG1pc2Mgc3RhdGljIHV0aWxpdHkgbWV0aG9kcy5cbiAqL1xuY2xhc3MgVXRpbHMge1xuXG5cdC8qKlxuXHQgKiBDb252ZXJ0cyBhIHNpbmdsZSBieXRlIHRvIGEgaGV4IHN0cmluZy5cblx0ICogQHBhcmFtIHtudW1iZXJ9IGJ5dGVcblx0ICogQHJldHVybiB7c3RyaW5nfVxuXHQgKi9cblx0c3RhdGljIGJ5dGVUb0hleChieXRlKSB7XG5cdFx0Ly8gRW5zdXJlIGhleCBzdHJpbmcgYWx3YXlzIGhhcyB0d28gY2hhcnNcblx0XHRyZXR1cm4gKCcwJyArIGJ5dGUudG9TdHJpbmcoMTYpKS5zbGljZSgtMik7XG5cdH1cblxuXHQvKipcblx0ICogQ29udmVydHMgYW4gYXJyYXkgb2YgYnl0ZXMgdG8gYSBoZXggc3RyaW5nLlxuXHQgKiBAcGFyYW0ge2FycmF5fSBieXRlQXJyYXlcblx0ICogQHJldHVybiB7c3RyaW5nfVxuXHQgKi9cblx0c3RhdGljIGJ5dGVzVG9IZXgoYnl0ZUFycmF5KSB7XG5cdFx0dmFyIGhleCA9IFtdO1xuXHRcdGJ5dGVBcnJheS5mb3JFYWNoKGJ5dGUgPT4gaGV4LnB1c2goVXRpbHMuYnl0ZVRvSGV4KGJ5dGUpKSk7XG5cdFx0cmV0dXJuIGhleC5qb2luKCcnKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBDb252ZXJ0cyBhIGhleCBzdHJpbmcgdG8gYSBudW1iZXIuXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSBoZXhTdHJpbmdcblx0ICogQHJldHVybiB7bnVtYmVyfVxuXHQgKi9cblx0c3RhdGljIGhleFRvTnVtYmVyKGhleFN0cmluZykge1xuXHRcdHJldHVybiBwYXJzZUludChoZXhTdHJpbmcsIDE2KTtcblx0fVxuXG5cdC8qKlxuXHQgKiBDb252ZXJ0cyBhbiBhcnJheSBvZiBieXRlcyB0byBhIG51bWJlci5cblx0ICogQHBhcmFtIHthcnJheX0gYnl0ZUFycmF5XG5cdCAqIEByZXR1cm4ge251bWJlcn1cblx0ICovXG5cdHN0YXRpYyBieXRlc1RvTnVtYmVyKGJ5dGVBcnJheSkge1xuXHRcdHJldHVybiBVdGlscy5oZXhUb051bWJlcihVdGlscy5ieXRlc1RvSGV4KGJ5dGVBcnJheSkpO1xuXHR9XG5cblx0LyoqXG5cdCAqIENvbnZlcnRzIGFuIGFycmF5IG9mIGJ5dGVzIHRvIGxldHRlcnMuXG5cdCAqIEBwYXJhbSB7YXJyYXl9IGJ5dGVBcnJheVxuXHQgKiBAcmV0dXJuIHtzdHJpbmd9XG5cdCAqL1xuXHRzdGF0aWMgYnl0ZXNUb0xldHRlcnMoYnl0ZUFycmF5KSB7XG5cdFx0dmFyIGxldHRlcnMgPSBbXTtcblx0XHRieXRlQXJyYXkuZm9yRWFjaChieXRlID0+IGxldHRlcnMucHVzaChTdHJpbmcuZnJvbUNoYXJDb2RlKGJ5dGUpKSk7XG5cdFx0cmV0dXJuIGxldHRlcnMuam9pbignJyk7XG5cdH1cblxuXHQvKipcblx0ICogQ29udmVydHMgYSBkZWNpbWFsIHRvIGl0J3MgYmluYXJ5IHJlcHJlc2VudGF0aW9uLlxuXHQgKiBAcGFyYW0ge251bWJlcn0gZGVjXG5cdCAqIEByZXR1cm4ge3N0cmluZ31cblx0ICovXG5cdHN0YXRpYyBkZWNUb0JpbmFyeShkZWMpIHtcbiAgICBcdHJldHVybiAoZGVjID4+PiAwKS50b1N0cmluZygyKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBSZWFkcyBhIHZhcmlhYmxlIGxlbmd0aCB2YWx1ZS5cblx0ICogQHBhcmFtIHthcnJheX0gYnl0ZUFycmF5XG5cdCAqIEByZXR1cm4ge251bWJlcn1cblx0ICovXG5cdHN0YXRpYyByZWFkVmFySW50KGJ5dGVBcnJheSkge1xuXHRcdHZhciByZXN1bHQgPSAwO1xuXHRcdGJ5dGVBcnJheS5mb3JFYWNoKG51bWJlciA9PiB7XG5cdFx0XHR2YXIgYiA9IG51bWJlcjtcblx0XHRcdGlmIChiICYgMHg4MCkge1xuXHRcdFx0XHRyZXN1bHQgKz0gKGIgJiAweDdmKTtcblx0XHRcdFx0cmVzdWx0IDw8PSA3O1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0LyogYiBpcyB0aGUgbGFzdCBieXRlICovXG5cdFx0XHRcdHJlc3VsdCArPSBiO1xuXHRcdFx0fVxuXHRcdH0pO1xuXG5cdFx0cmV0dXJuIHJlc3VsdDtcblx0fVxuXG5cdC8qKlxuXHQgKiBEZWNvZGVzIGJhc2UtNjQgZW5jb2RlZCBzdHJpbmdcblx0ICogQHBhcmFtIHtzdHJpbmd9IHN0cmluZ1xuXHQgKiBAcmV0dXJuIHtzdHJpbmd9XG5cdCAqL1xuXHRzdGF0aWMgYXRvYihzdHJpbmcpIHtcblx0XHRpZiAodHlwZW9mIGF0b2IgPT09ICdmdW5jdGlvbicpIHJldHVybiBhdG9iKHN0cmluZyk7XG5cdFx0cmV0dXJuIG5ldyBCdWZmZXIoc3RyaW5nLCAnYmFzZTY0JykudG9TdHJpbmcoJ2JpbmFyeScpO1xuXHR9XG59XG5cbmV4cG9ydHMuVXRpbHMgPSBVdGlsczsiXX0=