mirror of
https://github.com/bvanroll/rpiRadio.git
synced 2025-08-30 04:22:50 +00:00
1220 lines
92 KiB
JavaScript
1220 lines
92 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 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})({1:[function(require,module,exports){
|
|
'use strict';
|
|
|
|
/**
|
|
* Constants used in player.
|
|
*/
|
|
var Constants = {
|
|
VERSION: '2.0.1',
|
|
NOTES: []
|
|
};
|
|
|
|
(function () {
|
|
// 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
|
|
eventJson.name = 'Time Signature';
|
|
break;
|
|
case 0x59:
|
|
// Key Signature
|
|
eventJson.name = 'Key Signature';
|
|
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,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJzcmNcXGNvbnN0YW50cy5qcyIsInNyY1xcaW5kZXguanMiLCJzcmNcXHBsYXllci5qcyIsInNyY1xcdHJhY2suanMiLCJzcmNcXHNyY1xcdXRpbHMuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztBQ0FBOzs7QUFHQSxJQUFJLFlBQVk7QUFDZixVQUFTLE9BRE07QUFFZixRQUFPO0FBRlEsQ0FBaEI7O0FBS0EsQ0FBQyxZQUFXO0FBQ1g7QUFDQSxLQUFJLFdBQVcsQ0FBQyxDQUFDLEdBQUQsQ0FBRCxFQUFRLENBQUMsSUFBRCxFQUFNLElBQU4sQ0FBUixFQUFxQixDQUFDLEdBQUQsQ0FBckIsRUFBNEIsQ0FBQyxJQUFELEVBQU0sSUFBTixDQUE1QixFQUF5QyxDQUFDLEdBQUQsQ0FBekMsRUFBK0MsQ0FBQyxHQUFELENBQS9DLEVBQXNELENBQUMsSUFBRCxFQUFNLElBQU4sQ0FBdEQsRUFBbUUsQ0FBQyxHQUFELENBQW5FLEVBQTBFLENBQUMsSUFBRCxFQUFNLElBQU4sQ0FBMUUsRUFBdUYsQ0FBQyxHQUFELENBQXZGLEVBQThGLENBQUMsSUFBRCxFQUFNLElBQU4sQ0FBOUYsRUFBMkcsQ0FBQyxHQUFELENBQTNHLENBQWY7QUFDQSxLQUFJLFVBQVUsQ0FBZDs7QUFFQTs7QUFMVyw0QkFNRixDQU5FO0FBT1YsV0FBUyxPQUFULENBQWlCLHFCQUFhO0FBQzdCLGFBQVUsT0FBVixDQUFrQjtBQUFBLFdBQVEsVUFBVSxLQUFWLENBQWdCLE9BQWhCLElBQTJCLE9BQU8sQ0FBMUM7QUFBQSxJQUFsQjtBQUNBO0FBQ0EsR0FIRDtBQVBVOztBQU1YLE1BQUssSUFBSSxJQUFJLENBQUMsQ0FBZCxFQUFpQixLQUFLLENBQXRCLEVBQXlCLEdBQXpCLEVBQThCO0FBQUEsUUFBckIsQ0FBcUI7QUFLN0I7QUFDRCxDQVpEOztBQWNBLFFBQVEsU0FBUixHQUFvQixTQUFwQjs7Ozs7QUN0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QUFxQixDQUFyQixDQUFQO0FBQ0g7O0FBRUQ7Ozs7Ozs7OzZCQUtrQixTLEVBQVc7QUFDNUIsT0FBSSxTQUFTLENBQWI7QUFDQSxhQUFVLE9BQVYsQ0FBa0Isa0JBQVU7QUFDM0IsUUFBSSxJQUFJLE1BQVI7QUFDQSxRQUFJLElBQUksSUFBUixFQUFjO0FBQ2IsZUFBVyxJQUFJLElBQWY7QUFDQSxnQkFBVyxDQUFYO0FBQ0EsS0FIRCxNQUdPO0FBQ047QUFDQSxlQUFVLENBQVY7QUFDQTtBQUNELElBVEQ7O0FBV0EsVUFBTyxNQUFQO0FBQ0E7O0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7OztjQUtZLE0sRUFBUTtBQUNuQixPQUFJLE9BQU8sSUFBUCxLQUFnQixVQUFwQixFQUFnQyxPQUFPLEtBQUssTUFBTCxDQUFQO0FBQ2hDLFVBQU8sSUFBSSxNQUFKLENBQVcsTUFBWCxFQUFtQixRQUFuQixFQUE2QixRQUE3QixDQUFzQyxRQUF0QyxDQUFQO0FBQ0EsRzs7Ozs7O0FBR0YsUUFBUSxLQUFSLEdBQWdCLEtBQWhCIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZighdFtvXSl7dmFyIGE9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtpZighdSYmYSlyZXR1cm4gYShvLCEwKTtpZihpKXJldHVybiBpKG8sITApO3ZhciBmPW5ldyBFcnJvcihcIkNhbm5vdCBmaW5kIG1vZHVsZSAnXCIrbytcIidcIik7dGhyb3cgZi5jb2RlPVwiTU9EVUxFX05PVF9GT1VORFwiLGZ9dmFyIGw9bltvXT17ZXhwb3J0czp7fX07dFtvXVswXS5jYWxsKGwuZXhwb3J0cyxmdW5jdGlvbihlKXt2YXIgbj10W29dWzFdW2VdO3JldHVybiBzKG4/bjplKX0sbCxsLmV4cG9ydHMsZSx0LG4scil9cmV0dXJuIG5bb10uZXhwb3J0c312YXIgaT10eXBlb2YgcmVxdWlyZT09XCJmdW5jdGlvblwiJiZyZXF1aXJlO2Zvcih2YXIgbz0wO288ci5sZW5ndGg7bysrKXMocltvXSk7cmV0dXJuIHN9KSIsIi8qKlxuICogQ29uc3RhbnRzIHVzZWQgaW4gcGxheWVyLlxuICovXG52YXIgQ29uc3RhbnRzID0ge1xuXHRWRVJTSU9OOiAnMi4wLjEnLFxuXHROT1RFUzogW11cbn07XG5cbihmdW5jdGlvbigpIHtcblx0Ly8gQnVpbGRzIG5vdGVzIG9iamVjdCBmb3IgcmVmZXJlbmNlIGFnYWluc3QgYmluYXJ5IHZhbHVlcy5cblx0dmFyIGFsbE5vdGVzID0gW1snQyddLCBbJ0MjJywnRGInXSwgWydEJ10sIFsnRCMnLCdFYiddLCBbJ0UnXSxbJ0YnXSwgWydGIycsJ0diJ10sIFsnRyddLCBbJ0cjJywnQWInXSwgWydBJ10sIFsnQSMnLCdCYiddLCBbJ0InXV07XG5cdHZhciBjb3VudGVyID0gMDtcblxuXHQvLyBBbGwgYXZhaWxhYmxlIG9jdGF2ZXMuXG5cdGZvciAobGV0IGkgPSAtMTsgaSA8PSA5OyBpKyspIHtcblx0XHRhbGxOb3Rlcy5mb3JFYWNoKG5vdGVHcm91cCA9PiB7XG5cdFx0XHRub3RlR3JvdXAuZm9yRWFjaChub3RlID0+IENvbnN0YW50cy5OT1RFU1tjb3VudGVyXSA9IG5vdGUgKyBpKTtcblx0XHRcdGNvdW50ZXIgKys7XG5cdFx0fSk7XG5cdH1cbn0pKCk7XG5cbmV4cG9ydHMuQ29uc3RhbnRzID0gQ29uc3RhbnRzOyIsImNvbnN0IFBsYXllciA9IHJlcXVpcmUoXCIuL3BsYXllclwiKTtcbmNvbnN0IFV0aWxzID0gcmVxdWlyZShcIi4vdXRpbHNcIik7XG5jb25zdCBDb25zdGFudHMgPSByZXF1aXJlKFwiLi9jb25zdGFudHNcIik7XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICAgIFBsYXllcjpQbGF5ZXIuUGxheWVyLFxuICAgIFV0aWxzOlV0aWxzLlV0aWxzLFxuICAgIENvbnN0YW50czpDb25zdGFudHMuQ29uc3RhbnRzXG59IiwiY29uc3QgVXRpbHMgPSByZXF1aXJlKFwiLi91dGlsc1wiKS5VdGlscztcbmNvbnN0IFRyYWNrID0gcmVxdWlyZShcIi4vdHJhY2tcIikuVHJhY2s7XG5cbi8vIFBvbHlmaWxsIFVpbnQ4QXJyYXkuZm9yRWFjaDogRG9lc24ndCBleGlzdCBvbiBTYWZhcmkgPDEwXG5pZiAoIVVpbnQ4QXJyYXkucHJvdG90eXBlLmZvckVhY2gpIHtcblx0T2JqZWN0LmRlZmluZVByb3BlcnR5KFVpbnQ4QXJyYXkucHJvdG90eXBlLCAnZm9yRWFjaCcsIHtcblx0XHR2YWx1ZTogQXJyYXkucHJvdG90eXBlLmZvckVhY2hcblx0fSk7XG59XG5cbi8qKlxuICogTWFpbiBwbGF5ZXIgY2xhc3MuICBDb250YWlucyBtZXRob2RzIHRvIGxvYWQgZmlsZXMsIHN0YXJ0LCBzdG9wLlxuICogQHBhcmFtIHtmdW5jdGlvbn0gLSBDYWxsYmFjayB0byBmaXJlIGZvciBlYWNoIE1JREkgZXZlbnQuICBDYW4gYWxzbyBiZSBhZGRlZCB3aXRoIG9uKCdtaWRpRXZlbnQnLCBmbilcbiAqIEBwYXJhbSB7YXJyYXl9IC0gQXJyYXkgYnVmZmVyIG9mIE1JREkgZmlsZSAob3B0aW9uYWwpLlxuICovXG5jbGFzcyBQbGF5ZXIge1xuXHRjb25zdHJ1Y3RvcihldmVudEhhbmRsZXIsIGJ1ZmZlcikge1xuXHRcdHRoaXMuc2FtcGxlUmF0ZSA9IDU7IC8vIG1pbGxpc2Vjb25kc1xuXHRcdHRoaXMuc3RhcnRUaW1lID0gMDtcblx0XHR0aGlzLmJ1ZmZlciA9IGJ1ZmZlciB8fCBudWxsO1xuXHRcdHRoaXMuZGl2aXNpb247XG5cdFx0dGhpcy5mb3JtYXQ7XG5cdFx0dGhpcy5zZXRJbnRlcnZhbElkID0gZmFsc2U7XG5cdFx0dGhpcy50cmFja3MgPSBbXTtcblx0XHR0aGlzLmluc3RydW1lbnRzID0gW107XG5cdFx0dGhpcy5kZWZhdWx0VGVtcG8gPSAxMjA7XG5cdFx0dGhpcy50ZW1wbyA9IG51bGw7XG5cdFx0dGhpcy5zdGFydFRpY2sgPSAwO1xuXHRcdHRoaXMudGljayA9IDA7XG5cdFx0dGhpcy5sYXN0VGljayA9IG51bGw7XG5cdFx0dGhpcy5pbkxvb3AgPSBmYWxzZTtcblx0XHR0aGlzLnRvdGFsVGlja3MgPSAwO1xuXHRcdHRoaXMuZXZlbnRzID0gW107XG5cdFx0dGhpcy50b3RhbEV2ZW50cyA9IDA7XG5cdFx0dGhpcy5ldmVudExpc3RlbmVycyA9IHt9O1xuXG5cdFx0aWYgKHR5cGVvZihldmVudEhhbmRsZXIpID09PSAnZnVuY3Rpb24nKSB0aGlzLm9uKCdtaWRpRXZlbnQnLCBldmVudEhhbmRsZXIpO1xuXHR9XG5cblx0LyoqXG5cdCAqIExvYWQgYSBmaWxlIGludG8gdGhlIHBsYXllciAoTm9kZS5qcyBvbmx5KS5cblx0ICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBQYXRoIG9mIGZpbGUuXG5cdCAqIEByZXR1cm4ge1BsYXllcn1cblx0ICovXG5cdGxvYWRGaWxlKHBhdGgpIHtcblx0XHR2YXIgZnMgPSByZXF1aXJlKCdmcycpO1xuXHRcdHRoaXMuYnVmZmVyID0gZnMucmVhZEZpbGVTeW5jKHBhdGgpO1xuXHRcdHJldHVybiB0aGlzLmZpbGVMb2FkZWQoKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBMb2FkIGFuIGFycmF5IGJ1ZmZlciBpbnRvIHRoZSBwbGF5ZXIuXG5cdCAqIEBwYXJhbSB7YXJyYXl9IGFycmF5QnVmZmVyIC0gQXJyYXkgYnVmZmVyIG9mIGZpbGUgdG8gYmUgbG9hZGVkLlxuXHQgKiBAcmV0dXJuIHtQbGF5ZXJ9XG5cdCAqL1xuXHRsb2FkQXJyYXlCdWZmZXIoYXJyYXlCdWZmZXIpIHtcblx0XHR0aGlzLmJ1ZmZlciA9IG5ldyBVaW50OEFycmF5KGFycmF5QnVmZmVyKTtcblx0XHRyZXR1cm4gdGhpcy5maWxlTG9hZGVkKCk7XG5cdH1cblxuXHQvKipcblx0ICogTG9hZCBhIGRhdGEgVVJJIGludG8gdGhlIHBsYXllci5cblx0ICogQHBhcmFtIHtzdHJpbmd9IGRhdGFVcmkgLSBEYXRhIFVSSSB0byBiZSBsb2FkZWQuXG5cdCAqIEByZXR1cm4ge1BsYXllcn1cblx0ICovXG5cdGxvYWREYXRhVXJpKGRhdGFVcmkpIHtcblx0XHQvLyBjb252ZXJ0IGJhc2U2NCB0byByYXcgYmluYXJ5IGRhdGEgaGVsZCBpbiBhIHN0cmluZy5cblx0XHQvLyBkb2Vzbid0IGhhbmRsZSBVUkxFbmNvZGVkIERhdGFVUklzIC0gc2VlIFNPIGFuc3dlciAjNjg1MDI3NiBmb3IgY29kZSB0aGF0IGRvZXMgdGhpc1xuXHRcdHZhciBieXRlU3RyaW5nID0gVXRpbHMuYXRvYihkYXRhVXJpLnNwbGl0KCcsJylbMV0pO1xuXG5cdFx0Ly8gd3JpdGUgdGhlIGJ5dGVzIG9mIHRoZSBzdHJpbmcgdG8gYW4gQXJyYXlCdWZmZXJcblx0XHR2YXIgaWEgPSBuZXcgVWludDhBcnJheShieXRlU3RyaW5nLmxlbmd0aCk7XG5cdFx0Zm9yICh2YXIgaSA9IDA7IGkgPCBieXRlU3RyaW5nLmxlbmd0aDsgaSsrKSB7XG5cdFx0XHRpYVtpXSA9IGJ5dGVTdHJpbmcuY2hhckNvZGVBdChpKTtcblx0XHR9XG5cblx0XHR0aGlzLmJ1ZmZlciA9IGlhO1xuXHRcdHJldHVybiB0aGlzLmZpbGVMb2FkZWQoKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBHZXQgZmlsZXNpemUgb2YgbG9hZGVkIGZpbGUgaW4gbnVtYmVyIG9mIGJ5dGVzLlxuXHQgKiBAcmV0dXJuIHtudW1iZXJ9IC0gVGhlIGZpbGVzaXplLlxuXHQgKi9cblx0Z2V0RmlsZXNpemUoKSB7XG5cdFx0cmV0dXJuIHRoaXMuYnVmZmVyID8gdGhpcy5idWZmZXIubGVuZ3RoIDogMDtcblx0fVxuXG5cdC8qKlxuXHQgKiBTZXRzIGRlZmF1bHQgdGVtcG8sIHBhcnNlcyBmaWxlIGZvciBuZWNlc3NhcnkgaW5mb3JtYXRpb24sIGFuZCBkb2VzIGEgZHJ5IHJ1biB0byBjYWxjdWxhdGUgdG90YWwgbGVuZ3RoLlxuXHQgKiBQb3B1bGF0ZXMgdGhpcy5ldmVudHMgJiB0aGlzLnRvdGFsVGlja3MuXG5cdCAqIEByZXR1cm4ge1BsYXllcn1cblx0ICovXG5cdGZpbGVMb2FkZWQoKSB7XG5cdFx0aWYgKCF0aGlzLnZhbGlkYXRlKCkpIHRocm93ICdJbnZhbGlkIE1JREkgZmlsZTsgc2hvdWxkIHN0YXJ0IHdpdGggTVRoZCc7XG5cdFx0cmV0dXJuIHRoaXMuc2V0VGVtcG8odGhpcy5kZWZhdWx0VGVtcG8pLmdldERpdmlzaW9uKCkuZ2V0Rm9ybWF0KCkuZ2V0VHJhY2tzKCkuZHJ5UnVuKCk7XG5cdH1cblxuXHQvKipcblx0ICogVmFsaWRhdGVzIGZpbGUgdXNpbmcgc2ltcGxlIG1lYW5zIC0gZmlyc3QgZm91ciBieXRlcyBzaG91bGQgPT0gTVRoZC5cblx0ICogQHJldHVybiB7Ym9vbGVhbn1cblx0ICovXG5cdHZhbGlkYXRlKCkge1xuXHRcdHJldHVybiBVdGlscy5ieXRlc1RvTGV0dGVycyh0aGlzLmJ1ZmZlci5zdWJhcnJheSgwLCA0KSkgPT09ICdNVGhkJztcblx0fVxuXG5cdC8qKlxuXHQgKiBHZXRzIE1JREkgZmlsZSBmb3JtYXQgZm9yIGxvYWRlZCBmaWxlLlxuXHQgKiBAcmV0dXJuIHtQbGF5ZXJ9XG5cdCAqL1xuXHRnZXRGb3JtYXQoKSB7XG5cdFx0Lypcblx0XHRNSURJIGZpbGVzIGNvbWUgaW4gMyB2YXJpYXRpb25zOlxuXHRcdEZvcm1hdCAwIHdoaWNoIGNvbnRhaW4gYSBzaW5nbGUgdHJhY2tcblx0XHRGb3JtYXQgMSB3aGljaCBjb250YWluIG9uZSBvciBtb3JlIHNpbXVsdGFuZW91cyB0cmFja3Ncblx0XHQoaWUgYWxsIHRyYWNrcyBhcmUgdG8gYmUgcGxheWVkIHNpbXVsdGFuZW91c2x5KS5cblx0XHRGb3JtYXQgMiB3aGljaCBjb250YWluIG9uZSBvciBtb3JlIGluZGVwZW5kYW50IHRyYWNrc1xuXHRcdChpZSBlYWNoIHRyYWNrIGlzIHRvIGJlIHBsYXllZCBpbmRlcGVuZGFudGx5IG9mIHRoZSBvdGhlcnMpLlxuXHRcdHJldHVybiBVdGlscy5ieXRlc1RvTnVtYmVyKHRoaXMuYnVmZmVyLnN1YmFycmF5KDgsIDEwKSk7XG5cdFx0Ki9cblxuXHRcdHRoaXMuZm9ybWF0ID0gVXRpbHMuYnl0ZXNUb051bWJlcih0aGlzLmJ1ZmZlci5zdWJhcnJheSg4LCAxMCkpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIFBhcnNlcyBvdXQgdHJhY2tzLCBwbGFjZXMgdGhlbSBpbiB0aGlzLnRyYWNrcyBhbmQgaW5pdGlhbGl6ZXMgdGhpcy5wb2ludGVyc1xuXHQgKiBAcmV0dXJuIHtQbGF5ZXJ9XG5cdCAqL1xuXHRnZXRUcmFja3MoKSB7XG5cdFx0dGhpcy50cmFja3MgPSBbXTtcblx0XHRsZXQgdHJhY2tPZmZzZXQgPSAwO1xuXHRcdHdoaWxlICh0cmFja09mZnNldCA8IHRoaXMuYnVmZmVyLmxlbmd0aCkge1xuXHRcdFx0aWYgKFV0aWxzLmJ5dGVzVG9MZXR0ZXJzKHRoaXMuYnVmZmVyLnN1YmFycmF5KHRyYWNrT2Zmc2V0LCB0cmFja09mZnNldCArIDQpKSA9PSAnTVRyaycpIHtcblx0XHRcdFx0bGV0IHRyYWNrTGVuZ3RoID0gVXRpbHMuYnl0ZXNUb051bWJlcih0aGlzLmJ1ZmZlci5zdWJhcnJheSh0cmFja09mZnNldCArIDQsIHRyYWNrT2Zmc2V0ICsgOCkpO1xuXHRcdFx0XHR0aGlzLnRyYWNrcy5wdXNoKG5ldyBUcmFjayh0aGlzLnRyYWNrcy5sZW5ndGgsIHRoaXMuYnVmZmVyLnN1YmFycmF5KHRyYWNrT2Zmc2V0ICsgOCwgdHJhY2tPZmZzZXQgKyA4ICsgdHJhY2tMZW5ndGgpKSk7XG5cdFx0XHR9XG5cblx0XHRcdHRyYWNrT2Zmc2V0ICs9IFV0aWxzLmJ5dGVzVG9OdW1iZXIodGhpcy5idWZmZXIuc3ViYXJyYXkodHJhY2tPZmZzZXQgKyA0LCB0cmFja09mZnNldCArIDgpKSArIDg7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIEVuYWJsZXMgYSB0cmFjayBmb3IgcGxheWluZy5cblx0ICogQHBhcmFtIHtudW1iZXJ9IHRyYWNrTnVtYmVyIC0gVHJhY2sgbnVtYmVyXG5cdCAqIEByZXR1cm4ge1BsYXllcn1cblx0ICovXG5cdGVuYWJsZVRyYWNrKHRyYWNrTnVtYmVyKSB7XG5cdFx0dGhpcy50cmFja3NbdHJhY2tOdW1iZXIgLSAxXS5lbmFibGUoKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG5cdC8qKlxuXHQgKiBEaXNhYmxlcyBhIHRyYWNrIGZvciBwbGF5aW5nLlxuXHQgKiBAcGFyYW0ge251bWJlcn0gLSBUcmFjayBudW1iZXJcblx0ICogQHJldHVybiB7UGxheWVyfVxuXHQgKi9cblx0ZGlzYWJsZVRyYWNrKHRyYWNrTnVtYmVyKSB7XG5cdFx0dGhpcy50cmFja3NbdHJhY2tOdW1iZXIgLSAxXS5kaXNhYmxlKCk7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH1cblxuXHQvKipcblx0ICogR2V0cyBxdWFydGVyIG5vdGUgZGl2aXNpb24gb2YgbG9hZGVkIE1JREkgZmlsZS5cblx0ICogQHJldHVybiB7UGxheWVyfVxuXHQgKi9cblx0Z2V0RGl2aXNpb24oKSB7XG5cdFx0dGhpcy5kaXZpc2lvbiA9IFV0aWxzLmJ5dGVzVG9OdW1iZXIodGhpcy5idWZmZXIuc3ViYXJyYXkoMTIsIDE0KSk7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH1cblxuXHQvKipcblx0ICogVGhlIG1haW4gcGxheSBsb29wLlxuXHQgKiBAcGFyYW0ge2Jvb2xlYW59IC0gSW5kaWNhdGVzIHdoZXRoZXIgb3Igbm90IHRoaXMgaXMgYmVpbmcgY2FsbGVkIHNpbXBseSBmb3IgcGFyc2luZyBwdXJwb3Nlcy4gIERpc3JlZ2FyZHMgdGltaW5nIGlmIHNvLlxuXHQgKiBAcmV0dXJuIHt1bmRlZmluZWR9XG5cdCAqL1xuXHRwbGF5TG9vcChkcnlSdW4pIHtcblx0XHRpZiAoIXRoaXMuaW5Mb29wKSB7XG5cdFx0XHR0aGlzLmluTG9vcCA9IHRydWU7XG5cdFx0XHR0aGlzLnRpY2sgPSB0aGlzLmdldEN1cnJlbnRUaWNrKCk7XG5cblx0XHRcdHRoaXMudHJhY2tzLmZvckVhY2goZnVuY3Rpb24odHJhY2spIHtcblx0XHRcdFx0Ly8gSGFuZGxlIG5leHQgZXZlbnRcblx0XHRcdFx0aWYgKCFkcnlSdW4gJiYgdGhpcy5lbmRPZkZpbGUoKSkge1xuXHRcdFx0XHRcdC8vY29uc29sZS5sb2coJ2VuZCBvZiBmaWxlJylcblx0XHRcdFx0XHR0aGlzLnRyaWdnZXJQbGF5ZXJFdmVudCgnZW5kT2ZGaWxlJyk7XG5cdFx0XHRcdFx0dGhpcy5zdG9wKCk7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0bGV0IGV2ZW50ID0gdHJhY2suaGFuZGxlRXZlbnQodGhpcy50aWNrLCBkcnlSdW4pO1xuXG5cdFx0XHRcdFx0aWYgKGRyeVJ1biAmJiBldmVudCkge1xuXHRcdFx0XHRcdFx0aWYgKGV2ZW50Lmhhc093blByb3BlcnR5KCduYW1lJykgJiYgZXZlbnQubmFtZSA9PT0gJ1NldCBUZW1wbycpIHtcblx0XHRcdFx0XHRcdFx0Ly8gR3JhYiB0ZW1wbyBpZiBhdmFpbGFibGUuXG5cdFx0XHRcdFx0XHRcdHRoaXMuc2V0VGVtcG8oZXZlbnQuZGF0YSk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRpZiAoZXZlbnQuaGFzT3duUHJvcGVydHkoJ25hbWUnKSAmJiBldmVudC5uYW1lID09PSAnUHJvZ3JhbSBDaGFuZ2UnKSB7XG5cdFx0XHRcdFx0XHRcdGlmICghdGhpcy5pbnN0cnVtZW50cy5pbmNsdWRlcyhldmVudC52YWx1ZSkpIHtcblx0XHRcdFx0XHRcdFx0XHR0aGlzLmluc3RydW1lbnRzLnB1c2goZXZlbnQudmFsdWUpO1xuXHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fSBlbHNlIGlmIChldmVudCkgdGhpcy5lbWl0RXZlbnQoZXZlbnQpO1xuXHRcdFx0XHR9XG5cblx0XHRcdH0sIHRoaXMpO1xuXG5cdFx0XHRpZiAoIWRyeVJ1bikgdGhpcy50cmlnZ2VyUGxheWVyRXZlbnQoJ3BsYXlpbmcnLCB7dGljazogdGhpcy50aWNrfSk7XG5cdFx0XHR0aGlzLmluTG9vcCA9IGZhbHNlO1xuXHRcdH1cblx0fVxuXG5cdC8qKlxuXHQgKiBTZXR0ZXIgZm9yIHRlbXBvLlxuXHQgKiBAcGFyYW0ge251bWJlcn0gLSBUZW1wbyBpbiBicG0gKGRlZmF1bHRzIHRvIDEyMClcblx0ICovXG5cdHNldFRlbXBvKHRlbXBvKSB7XG5cdFx0dGhpcy50ZW1wbyA9IHRlbXBvO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIFNldHRlciBmb3Igc3RhcnRUaW1lLlxuXHQgKiBAcGFyYW0ge251bWJlcn0gLSBVVEMgdGltZXN0YW1wXG5cdCAqL1xuXHRzZXRTdGFydFRpbWUoc3RhcnRUaW1lKSB7XG5cdFx0dGhpcy5zdGFydFRpbWUgPSBzdGFydFRpbWU7XG5cdH1cblxuXHQvKipcblx0ICogU3RhcnQgcGxheWluZyBsb2FkZWQgTUlESSBmaWxlIGlmIG5vdCBhbHJlYWR5IHBsYXlpbmcuXG5cdCAqIEByZXR1cm4ge1BsYXllcn1cblx0ICovXG5cdHBsYXkoKSB7XG5cdFx0aWYgKHRoaXMuaXNQbGF5aW5nKCkpIHRocm93ICdBbHJlYWR5IHBsYXlpbmcuLi4nO1xuXG5cdFx0Ly8gSW5pdGlhbGl6ZVxuXHRcdGlmICghdGhpcy5zdGFydFRpbWUpIHRoaXMuc3RhcnRUaW1lID0gKG5ldyBEYXRlKCkpLmdldFRpbWUoKTtcblxuXHRcdC8vIFN0YXJ0IHBsYXkgbG9vcFxuXHRcdC8vd2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZSh0aGlzLnBsYXlMb29wLmJpbmQodGhpcykpO1xuXHRcdHRoaXMuc2V0SW50ZXJ2YWxJZCA9IHNldEludGVydmFsKHRoaXMucGxheUxvb3AuYmluZCh0aGlzKSwgdGhpcy5zYW1wbGVSYXRlKTtcblxuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIFBhdXNlcyBwbGF5YmFjayBpZiBwbGF5aW5nLlxuXHQgKiBAcmV0dXJuIHtQbGF5ZXJ9XG5cdCAqL1xuXHRwYXVzZSgpIHtcblx0XHRjbGVhckludGVydmFsKHRoaXMuc2V0SW50ZXJ2YWxJZCk7XG5cdFx0dGhpcy5zZXRJbnRlcnZhbElkID0gZmFsc2U7XG5cdFx0dGhpcy5zdGFydFRpY2sgPSB0aGlzLnRpY2s7XG5cdFx0dGhpcy5zdGFydFRpbWUgPSAwO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIFN0b3BzIHBsYXliYWNrIGlmIHBsYXlpbmcuXG5cdCAqIEByZXR1cm4ge1BsYXllcn1cblx0ICovXG5cdHN0b3AoKSB7XG5cdFx0Y2xlYXJJbnRlcnZhbCh0aGlzLnNldEludGVydmFsSWQpO1xuXHRcdHRoaXMuc2V0SW50ZXJ2YWxJZCA9IGZhbHNlO1xuXHRcdHRoaXMuc3RhcnRUaWNrID0gMDtcblx0XHR0aGlzLnN0YXJ0VGltZSA9IDA7XG5cdFx0dGhpcy5yZXNldFRyYWNrcygpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIFNraXBzIHBsYXllciBwb2ludGVyIHRvIHNwZWNpZmllZCB0aWNrLlxuXHQgKiBAcGFyYW0ge251bWJlcn0gLSBUaWNrIHRvIHNraXAgdG8uXG5cdCAqIEByZXR1cm4ge1BsYXllcn1cblx0ICovXG5cdHNraXBUb1RpY2sodGljaykge1xuXHRcdHRoaXMuc3RvcCgpO1xuXHRcdHRoaXMuc3RhcnRUaWNrID0gdGljaztcblxuXHRcdC8vIE5lZWQgdG8gc2V0IHRyYWNrIGV2ZW50IGluZGV4ZXMgdG8gdGhlIG5lYXJlc3QgcG9zc2libGUgZXZlbnQgdG8gdGhlIHNwZWNpZmllZCB0aWNrLlxuXHRcdHRoaXMudHJhY2tzLmZvckVhY2goZnVuY3Rpb24odHJhY2spIHtcblx0XHRcdHRyYWNrLnNldEV2ZW50SW5kZXhCeVRpY2sodGljayk7XG5cdFx0fSk7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH1cblxuXHQvKipcblx0ICogU2tpcHMgcGxheWVyIHBvaW50ZXIgdG8gc3BlY2lmaWVkIHBlcmNlbnRhZ2UuXG5cdCAqIEBwYXJhbSB7bnVtYmVyfSAtIFBlcmNlbnQgdmFsdWUgaW4gaW50ZWdlciBmb3JtYXQuXG5cdCAqIEByZXR1cm4ge1BsYXllcn1cblx0ICovXG5cdHNraXBUb1BlcmNlbnQocGVyY2VudCkge1xuXHRcdGlmIChwZXJjZW50IDwgMCB8fCBwZXJjZW50ID4gMTAwKSB0aHJvdyBcIlBlcmNlbnQgbXVzdCBiZSBudW1iZXIgYmV0d2VlbiAxIGFuZCAxMDAuXCI7XG5cdFx0dGhpcy5za2lwVG9UaWNrKE1hdGgucm91bmQocGVyY2VudCAvIDEwMCAqIHRoaXMudG90YWxUaWNrcykpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIFNraXBzIHBsYXllciBwb2ludGVyIHRvIHNwZWNpZmllZCBzZWNvbmRzLlxuXHQgKiBAcGFyYW0ge251bWJlcn0gLSBTZWNvbmRzIHRvIHNraXAgdG8uXG5cdCAqIEByZXR1cm4ge1BsYXllcn1cblx0ICovXG5cdHNraXBUb1NlY29uZHMoc2Vjb25kcykge1xuXHRcdHZhciBzb25nVGltZSA9IHRoaXMuZ2V0U29uZ1RpbWUoKTtcblx0XHRpZiAoc2Vjb25kcyA8IDAgfHwgc2Vjb25kcyA+IHNvbmdUaW1lKSB0aHJvdyBzZWNvbmRzICsgXCIgc2Vjb25kcyBub3Qgd2l0aGluIHNvbmcgdGltZSBvZiBcIiArIHNvbmdUaW1lO1xuXHRcdHRoaXMuc2tpcFRvUGVyY2VudChzZWNvbmRzIC8gc29uZ1RpbWUgKiAxMDApO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIENoZWNrcyBpZiBwbGF5ZXIgaXMgcGxheWluZ1xuXHQgKiBAcmV0dXJuIHtib29sZWFufVxuXHQgKi9cblx0aXNQbGF5aW5nKCkge1xuXHRcdHJldHVybiB0aGlzLnNldEludGVydmFsSWQgPiAwIHx8IHR5cGVvZiB0aGlzLnNldEludGVydmFsSWQgPT09ICdvYmplY3QnO1xuXHR9XG5cblx0LyoqXG5cdCAqIFBsYXlzIHRoZSBsb2FkZWQgTUlESSBmaWxlIHdpdGhvdXQgcmVnYXJkIGZvciB0aW1pbmcgYW5kIHNhdmVzIGV2ZW50cyBpbiB0aGlzLmV2ZW50cy4gIEVzc2VudGlhbGx5IHVzZWQgYXMgYSBwYXJzZXIuXG5cdCAqIEByZXR1cm4ge1BsYXllcn1cblx0ICovXG5cdGRyeVJ1bigpIHtcblx0XHQvLyBSZXNldCB0cmFja3MgZmlyc3Rcblx0XHR0aGlzLnJlc2V0VHJhY2tzKCk7XG5cdFx0d2hpbGUgKCF0aGlzLmVuZE9mRmlsZSgpKSB0aGlzLnBsYXlMb29wKHRydWUpO1xuXHRcdHRoaXMuZXZlbnRzID0gdGhpcy5nZXRFdmVudHMoKTtcblx0XHR0aGlzLnRvdGFsRXZlbnRzID0gdGhpcy5nZXRUb3RhbEV2ZW50cygpO1xuXHRcdHRoaXMudG90YWxUaWNrcyA9IHRoaXMuZ2V0VG90YWxUaWNrcygpO1xuXHRcdHRoaXMuc3RhcnRUaWNrID0gMDtcblx0XHR0aGlzLnN0YXJ0VGltZSA9IDA7XG5cblx0XHQvLyBMZWF2ZSB0cmFja3MgaW4gcHJpc3RpbmUgY29uZGlzaFxuXHRcdHRoaXMucmVzZXRUcmFja3MoKTtcblxuXHRcdC8vY29uc29sZS5sb2coJ1NvbmcgdGltZTogJyArIHRoaXMuZ2V0U29uZ1RpbWUoKSArICcgc2Vjb25kcyAvICcgKyB0aGlzLnRvdGFsVGlja3MgKyAnIHRpY2tzLicpO1xuXG5cdFx0dGhpcy50cmlnZ2VyUGxheWVyRXZlbnQoJ2ZpbGVMb2FkZWQnLCB0aGlzKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG5cdC8qKlxuXHQgKiBSZXNldHMgcGxheSBwb2ludGVycyBmb3IgYWxsIHRyYWNrcy5cblx0ICogQHJldHVybiB7UGxheWVyfVxuXHQgKi9cblx0cmVzZXRUcmFja3MoKSB7XG5cdFx0dGhpcy50cmFja3MuZm9yRWFjaCh0cmFjayA9PiB0cmFjay5yZXNldCgpKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG5cdC8qKlxuXHQgKiBHZXRzIGFuIGFycmF5IG9mIGV2ZW50cyBncm91cGVkIGJ5IHRyYWNrLlxuXHQgKiBAcmV0dXJuIHthcnJheX1cblx0ICovXG5cdGdldEV2ZW50cygpIHtcblx0XHRyZXR1cm4gdGhpcy50cmFja3MubWFwKHRyYWNrID0+IHRyYWNrLmV2ZW50cyk7XG5cdH1cblxuXHQvKipcblx0ICogR2V0cyB0b3RhbCBudW1iZXIgb2YgdGlja3MgaW4gdGhlIGxvYWRlZCBNSURJIGZpbGUuXG5cdCAqIEByZXR1cm4ge251bWJlcn1cblx0ICovXG5cdGdldFRvdGFsVGlja3MoKSB7XG5cdFx0cmV0dXJuIE1hdGgubWF4LmFwcGx5KG51bGwsIHRoaXMudHJhY2tzLm1hcCh0cmFjayA9PiB0cmFjay5kZWx0YSkpO1xuXHR9XG5cblx0LyoqXG5cdCAqIEdldHMgdG90YWwgbnVtYmVyIG9mIGV2ZW50cyBpbiB0aGUgbG9hZGVkIE1JREkgZmlsZS5cblx0ICogQHJldHVybiB7bnVtYmVyfVxuXHQgKi9cblx0Z2V0VG90YWxFdmVudHMoKSB7XG5cdFx0cmV0dXJuIHRoaXMudHJhY2tzLnJlZHVjZSgoYSwgYikgPT4ge3JldHVybiB7ZXZlbnRzOiB7bGVuZ3RoOiBhLmV2ZW50cy5sZW5ndGggKyBiLmV2ZW50cy5sZW5ndGh9fX0sIHtldmVudHM6IHtsZW5ndGg6IDB9fSkuZXZlbnRzLmxlbmd0aDtcblx0fVxuXG5cdC8qKlxuXHQgKiBHZXRzIHNvbmcgZHVyYXRpb24gaW4gc2Vjb25kcy5cblx0ICogQHJldHVybiB7bnVtYmVyfVxuXHQgKi9cblx0Z2V0U29uZ1RpbWUoKSB7XG5cdFx0cmV0dXJuIHRoaXMudG90YWxUaWNrcyAvIHRoaXMuZGl2aXNpb24gLyB0aGlzLnRlbXBvICogNjA7XG5cdH1cblxuXHQvKipcblx0ICogR2V0cyByZW1haW5pbmcgbnVtYmVyIG9mIHNlY29uZHMgaW4gcGxheWJhY2suXG5cdCAqIEByZXR1cm4ge251bWJlcn1cblx0ICovXG5cdGdldFNvbmdUaW1lUmVtYWluaW5nKCkge1xuXHRcdHJldHVybiBNYXRoLnJvdW5kKCh0aGlzLnRvdGFsVGlja3MgLSB0aGlzLnRpY2spIC8gdGhpcy5kaXZpc2lvbiAvIHRoaXMudGVtcG8gKiA2MCk7XG5cdH1cblxuXHQvKipcblx0ICogR2V0cyByZW1haW5pbmcgcGVyY2VudCBvZiBwbGF5YmFjay5cblx0ICogQHJldHVybiB7bnVtYmVyfVxuXHQgKi9cblx0Z2V0U29uZ1BlcmNlbnRSZW1haW5pbmcoKSB7XG5cdFx0cmV0dXJuIE1hdGgucm91bmQodGhpcy5nZXRTb25nVGltZVJlbWFpbmluZygpIC8gdGhpcy5nZXRTb25nVGltZSgpICogMTAwKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBOdW1iZXIgb2YgYnl0ZXMgcHJvY2Vzc2VkIGluIHRoZSBsb2FkZWQgTUlESSBmaWxlLlxuXHQgKiBAcmV0dXJuIHtudW1iZXJ9XG5cdCAqL1xuXHRieXRlc1Byb2Nlc3NlZCgpIHtcblx0XHQvLyBDdXJyZW50bHkgYXNzdW1lIGhlYWRlciBjaHVuayBpcyBzdHJpY3RseSAxNCBieXRlc1xuXHRcdHJldHVybiAxNCArIHRoaXMudHJhY2tzLmxlbmd0aCAqIDggKyB0aGlzLnRyYWNrcy5yZWR1Y2UoKGEsIGIpID0+IHtyZXR1cm4ge3BvaW50ZXI6IGEucG9pbnRlciArIGIucG9pbnRlcn19LCB7cG9pbnRlcjogMH0pLnBvaW50ZXI7XG5cdH1cblxuXHQvKipcblx0ICogTnVtYmVyIG9mIGV2ZW50cyBwbGF5ZWQgdXAgdG8gdGhpcyBwb2ludC5cblx0ICogQHJldHVybiB7bnVtYmVyfVxuXHQgKi9cblx0ZXZlbnRzUGxheWVkKCkge1xuXHRcdHJldHVybiB0aGlzLnRyYWNrcy5yZWR1Y2UoKGEsIGIpID0+IHtyZXR1cm4ge2V2ZW50SW5kZXg6IGEuZXZlbnRJbmRleCArIGIuZXZlbnRJbmRleH19LCB7ZXZlbnRJbmRleDogMH0pLmV2ZW50SW5kZXg7XG5cdH1cblxuXHQvKipcblx0ICogRGV0ZXJtaW5lcyBpZiB0aGUgcGxheWVyIHBvaW50ZXIgaGFzIHJlYWNoZWQgdGhlIGVuZCBvZiB0aGUgbG9hZGVkIE1JREkgZmlsZS5cblx0ICogVXNlZCBpbiB0d28gd2F5czpcblx0ICogMS4gSWYgcGxheWluZyByZXN1bHQgaXMgYmFzZWQgb24gbG9hZGVkIEpTT04gZXZlbnRzLlxuXHQgKiAyLiBJZiBwYXJzaW5nIChkcnlSdW4pIGl0J3MgYmFzZWQgb24gdGhlIGFjdHVhbCBidWZmZXIgbGVuZ3RoIHZzIGJ5dGVzIHByb2Nlc3NlZC5cblx0ICogQHJldHVybiB7Ym9vbGVhbn1cblx0ICovXG5cdGVuZE9mRmlsZSgpIHtcblx0XHRpZiAodGhpcy5pc1BsYXlpbmcoKSkge1xuXHRcdFx0cmV0dXJuIHRoaXMuZXZlbnRzUGxheWVkKCkgPT0gdGhpcy50b3RhbEV2ZW50cztcblx0XHR9XG5cblx0XHRyZXR1cm4gdGhpcy5ieXRlc1Byb2Nlc3NlZCgpID09IHRoaXMuYnVmZmVyLmxlbmd0aDtcblx0fVxuXG5cdC8qKlxuXHQgKiBHZXRzIHRoZSBjdXJyZW50IHRpY2sgbnVtYmVyIGluIHBsYXliYWNrLlxuXHQgKiBAcmV0dXJuIHtudW1iZXJ9XG5cdCAqL1xuXHRnZXRDdXJyZW50VGljaygpIHtcblx0XHRyZXR1cm4gTWF0aC5yb3VuZCgoKG5ldyBEYXRlKCkpLmdldFRpbWUoKSAtIHRoaXMuc3RhcnRUaW1lKSAvIDEwMDAgKiAodGhpcy5kaXZpc2lvbiAqICh0aGlzLnRlbXBvIC8gNjApKSkgKyB0aGlzLnN0YXJ0VGljaztcblx0fVxuXG5cdC8qKlxuXHQgKiBTZW5kcyBNSURJIGV2ZW50IG91dCB0byBsaXN0ZW5lci5cblx0ICogQHBhcmFtIHtvYmplY3R9XG5cdCAqIEByZXR1cm4ge1BsYXllcn1cblx0ICovXG5cdGVtaXRFdmVudChldmVudCkge1xuXHRcdHRoaXMudHJpZ2dlclBsYXllckV2ZW50KCdtaWRpRXZlbnQnLCBldmVudCk7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH1cblxuXHQvKipcblx0ICogU3Vic2NyaWJlcyBldmVudHMgdG8gbGlzdGVuZXJzXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSAtIE5hbWUgb2YgZXZlbnQgdG8gc3Vic2NyaWJlIHRvLlxuXHQgKiBAcGFyYW0ge2Z1bmN0aW9ufSAtIENhbGxiYWNrIHRvIGZpcmUgd2hlbiBldmVudCBpcyBicm9hZGNhc3QuXG5cdCAqIEByZXR1cm4ge1BsYXllcn1cblx0ICovXG5cdG9uKHBsYXllckV2ZW50LCBmbikge1xuXHRcdGlmICghdGhpcy5ldmVudExpc3RlbmVycy5oYXNPd25Qcm9wZXJ0eShwbGF5ZXJFdmVudCkpIHRoaXMuZXZlbnRMaXN0ZW5lcnNbcGxheWVyRXZlbnRdID0gW107XG5cdFx0dGhpcy5ldmVudExpc3RlbmVyc1twbGF5ZXJFdmVudF0ucHVzaChmbik7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH1cblxuXHQvKipcblx0ICogQnJvYWRjYXN0cyBldmVudCB0byB0cmlnZ2VyIHN1YnNjcmliZWQgY2FsbGJhY2tzLlxuXHQgKiBAcGFyYW0ge3N0cmluZ30gLSBOYW1lIG9mIGV2ZW50LlxuXHQgKiBAcGFyYW0ge29iamVjdH0gLSBEYXRhIHRvIGJlIHBhc3NlZCB0byBzdWJzY3JpYmVyIGNhbGxiYWNrLlxuXHQgKiBAcmV0dXJuIHtQbGF5ZXJ9XG5cdCAqL1xuXHR0cmlnZ2VyUGxheWVyRXZlbnQocGxheWVyRXZlbnQsIGRhdGEpIHtcblx0XHRpZiAodGhpcy5ldmVudExpc3RlbmVycy5oYXNPd25Qcm9wZXJ0eShwbGF5ZXJFdmVudCkpIHRoaXMuZXZlbnRMaXN0ZW5lcnNbcGxheWVyRXZlbnRdLmZvckVhY2goZm4gPT4gZm4oZGF0YSB8fCB7fSkpO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cbn1cblxuZXhwb3J0cy5QbGF5ZXIgPSBQbGF5ZXI7XG4iLCJjb25zdCBDb25zdGFudHMgPSByZXF1aXJlKFwiLi9jb25zdGFudHNcIikuQ29uc3RhbnRzO1xuY29uc3QgVXRpbHMgPSByZXF1aXJlKFwiLi91dGlsc1wiKS5VdGlscztcblxuLyoqXG4gKiBDbGFzcyByZXByZXNlbnRpbmcgYSB0cmFjay4gIENvbnRhaW5zIG1ldGhvZHMgZm9yIHBhcnNpbmcgZXZlbnRzIGFuZCBrZWVwaW5nIHRyYWNrIG9mIHBvaW50ZXIuXG4gKi9cbmNsYXNzIFRyYWNrXHR7XG5cdGNvbnN0cnVjdG9yKGluZGV4LCBkYXRhKSB7XG5cdFx0dGhpcy5lbmFibGVkID0gdHJ1ZTtcblx0XHR0aGlzLmV2ZW50SW5kZXggPSAwO1xuXHRcdHRoaXMucG9pbnRlciA9IDA7XG5cdFx0dGhpcy5sYXN0VGljayA9IDA7XG5cdFx0dGhpcy5sYXN0U3RhdHVzID0gbnVsbDtcblx0XHR0aGlzLmluZGV4ID0gaW5kZXg7XG5cdFx0dGhpcy5kYXRhID0gZGF0YTtcblx0XHR0aGlzLmRlbHRhID0gMDtcblx0XHR0aGlzLnJ1bm5pbmdEZWx0YSA9IDA7XG5cdFx0dGhpcy5ldmVudHMgPSBbXTtcblx0fVxuXG5cdC8qKlxuXHQgKiBSZXNldHMgYWxsIHN0YXRlZnVsIHRyYWNrIGluZm9ybWFpb24gdXNlZCBkdXJpbmcgcGxheWJhY2suXG5cdCAqIEByZXR1cm4ge1RyYWNrfVxuXHQgKi9cblx0cmVzZXQoKSB7XG5cdFx0dGhpcy5lbmFibGVkID0gdHJ1ZTtcblx0XHR0aGlzLmV2ZW50SW5kZXggPSAwO1xuXHRcdHRoaXMucG9pbnRlciA9IDA7XG5cdFx0dGhpcy5sYXN0VGljayA9IDA7XG5cdFx0dGhpcy5sYXN0U3RhdHVzID0gbnVsbDtcblx0XHR0aGlzLmRlbHRhID0gMDtcblx0XHR0aGlzLnJ1bm5pbmdEZWx0YSA9IDA7XG5cdFx0cmV0dXJuIHRoaXM7XG5cdH1cblxuXHQvKipcblx0ICogU2V0cyB0aGlzIHRyYWNrIHRvIGJlIGVuYWJsZWQgZHVyaW5nIHBsYXliYWNrLlxuXHQgKiBAcmV0dXJuIHtUcmFja31cblx0ICovXG5cdGVuYWJsZSgpIHtcblx0XHR0aGlzLmVuYWJsZWQgPSB0cnVlO1xuXHRcdHJldHVybiB0aGlzO1xuXHR9XG5cblx0LyoqXG5cdCAqIFNldHMgdGhpcyB0cmFjayB0byBiZSBkaXNhYmxlZCBkdXJpbmcgcGxheWJhY2suXG5cdCAqIEByZXR1cm4ge1RyYWNrfVxuXHQgKi9cblx0ZGlzYWJsZSgpIHtcblx0XHR0aGlzLmVuYWJsZWQgPSBmYWxzZTtcblx0XHRyZXR1cm4gdGhpcztcblx0fVxuXG5cdC8qKlxuXHQgKiBTZXRzIHRoZSB0cmFjayBldmVudCBpbmRleCB0byB0aGUgbmVhcmVzdCBldmVudCB0byB0aGUgZ2l2ZW4gdGljay5cblx0ICogQHBhcmFtIHtudW1iZXJ9IHRpY2tcblx0ICogQHJldHVybiB7VHJhY2t9XG5cdCAqL1xuXHRzZXRFdmVudEluZGV4QnlUaWNrKHRpY2spIHtcblx0XHR0aWNrID0gdGljayB8fCAwO1xuXG5cdFx0Zm9yICh2YXIgaSBpbiB0aGlzLmV2ZW50cykge1xuXHRcdFx0aWYgKHRoaXMuZXZlbnRzW2ldLnRpY2sgPj0gdGljaykge1xuXHRcdFx0XHR0aGlzLmV2ZW50SW5kZXggPSBpO1xuXHRcdFx0XHRyZXR1cm4gdGhpcztcblx0XHRcdH1cblx0XHR9XG5cdH1cblxuXHQvKipcblx0ICogR2V0cyBieXRlIGxvY2F0ZWQgYXQgcG9pbnRlciBwb3NpdGlvbi5cblx0ICogQHJldHVybiB7bnVtYmVyfVxuXHQgKi9cblx0Z2V0Q3VycmVudEJ5dGUoKSB7XG5cdFx0cmV0dXJuIHRoaXMuZGF0YVt0aGlzLnBvaW50ZXJdO1xuXHR9XG5cblx0LyoqXG5cdCAqIEdldHMgY291bnQgb2YgZGVsdGEgYnl0ZXMgYW5kIGN1cnJlbnQgcG9pbnRlciBwb3NpdGlvbi5cblx0ICogQHJldHVybiB7bnVtYmVyfVxuXHQgKi9cblx0Z2V0RGVsdGFCeXRlQ291bnQoKSB7XG5cdFx0Ly8gR2V0IGJ5dGUgY291bnQgb2YgZGVsdGEgVkxWXG5cdFx0Ly8gaHR0cDovL3d3dy5jY2FyaC5vcmcvY291cnNlcy8yNTMvaGFuZG91dC92bHYvXG5cdFx0Ly8gSWYgYnl0ZSBpcyBncmVhdGVyIG9yIGVxdWFsIHRvIDgwaCAoMTI4IGRlY2ltYWwpIHRoZW4gdGhlIG5leHQgYnl0ZVxuXHQgICAgLy8gaXMgYWxzbyBwYXJ0IG9mIHRoZSBWTFYsXG5cdCAgIFx0Ly8gZWxzZSBieXRlIGlzIHRoZSBsYXN0IGJ5dGUgaW4gYSBWTFYuXG5cdCAgIFx0dmFyIGN1cnJlbnRCeXRlID0gdGhpcy5nZXRDdXJyZW50Qnl0ZSgpO1xuXHQgICBcdHZhciBieXRlQ291bnQgPSAxO1xuXG5cdFx0d2hpbGUgKGN1cnJlbnRCeXRlID49IDEyOCkge1xuXHRcdFx0Y3VycmVudEJ5dGUgPSB0aGlzLmRhdGFbdGhpcy5wb2ludGVyICsgYnl0ZUNvdW50XTtcblx0XHRcdGJ5dGVDb3VudCsrO1xuXHRcdH1cblxuXHRcdHJldHVybiBieXRlQ291bnQ7XG5cdH1cblxuXHQvKipcblx0ICogR2V0IGRlbHRhIHZhbHVlIGF0IGN1cnJlbnQgcG9pbnRlciBwb3NpdGlvbi5cblx0ICogQHJldHVybiB7bnVtYmVyfVxuXHQgKi9cblx0Z2V0RGVsdGEoKSB7XG5cdFx0cmV0dXJuIFV0aWxzLnJlYWRWYXJJbnQodGhpcy5kYXRhLnN1YmFycmF5KHRoaXMucG9pbnRlciwgdGhpcy5wb2ludGVyICsgdGhpcy5nZXREZWx0YUJ5dGVDb3VudCgpKSk7XG5cdH1cblxuXHQvKipcblx0ICogSGFuZGxlcyBldmVudCB3aXRoaW4gYSBnaXZlbiB0cmFjayBzdGFydGluZyBhdCBzcGVjaWZpZWQgaW5kZXhcblx0ICogQHBhcmFtIHtudW1iZXJ9IGN1cnJlbnRUaWNrXG5cdCAqIEBwYXJhbSB7Ym9vbGVhbn0gZHJ5UnVuIC0gSWYgdHJ1ZSBldmVudHMgd2lsbCBiZSBwYXJzZWQgYW5kIHJldHVybmVkIHJlZ2FyZGxlc3Mgb2YgdGltZS5cblx0ICovXG5cdGhhbmRsZUV2ZW50KGN1cnJlbnRUaWNrLCBkcnlSdW4pIHtcblx0XHRkcnlSdW4gPSBkcnlSdW4gfHwgZmFsc2U7XG5cblx0XHRpZiAoZHJ5UnVuKSB7XG5cdFx0XHR2YXIgZWxhcHNlZFRpY2tzID0gY3VycmVudFRpY2sgLSB0aGlzLmxhc3RUaWNrO1xuXHRcdFx0dmFyIGRlbHRhID0gdGhpcy5nZXREZWx0YSgpO1xuXHRcdFx0dmFyIGV2ZW50UmVhZHkgPSBlbGFwc2VkVGlja3MgPj0gZGVsdGE7XG5cblx0XHRcdGlmICh0aGlzLnBvaW50ZXIgPCB0aGlzLmRhdGEubGVuZ3RoICYmIChkcnlSdW4gfHwgZXZlbnRSZWFkeSkpIHtcblx0XHRcdFx0bGV0IGV2ZW50ID0gdGhpcy5wYXJzZUV2ZW50KCk7XG5cdFx0XHRcdGlmICh0aGlzLmVuYWJsZWQpIHJldHVybiBldmVudDtcblx0XHRcdFx0Ly8gUmVjdXJzaXZlbHkgY2FsbCB0aGlzIGZ1bmN0aW9uIGZvciBlYWNoIGV2ZW50IGFoZWFkIHRoYXQgaGFzIDAgZGVsdGEgdGltZT9cblx0XHRcdH1cblxuXHRcdH0gZWxzZSB7XG5cdFx0XHQvLyBMZXQncyBhY3R1YWxseSBwbGF5IHRoZSBNSURJIGZyb20gdGhlIGdlbmVyYXRlZCBKU09OIGV2ZW50cyBjcmVhdGVkIGJ5IHRoZSBkcnkgcnVuLlxuXHRcdFx0aWYgKHRoaXMuZXZlbnRzW3RoaXMuZXZlbnRJbmRleF0gJiYgdGhpcy5ldmVudHNbdGhpcy5ldmVudEluZGV4XS50aWNrIDw9IGN1cnJlbnRUaWNrKSB7XG5cdFx0XHRcdHRoaXMuZXZlbnRJbmRleCsrO1xuXHRcdFx0XHRpZiAodGhpcy5lbmFibGVkKSByZXR1cm4gdGhpcy5ldmVudHNbdGhpcy5ldmVudEluZGV4IC0gMV07XG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0cmV0dXJuIG51bGw7XG5cdH1cblxuXHQvKipcblx0ICogR2V0IHN0cmluZyBkYXRhIGZyb20gZXZlbnQuXG5cdCAqIEBwYXJhbSB7bnVtYmVyfSBldmVudFN0YXJ0SW5kZXhcblx0ICogQHJldHVybiB7c3RyaW5nfVxuXHQgKi9cblx0Z2V0U3RyaW5nRGF0YShldmVudFN0YXJ0SW5kZXgpIHtcblx0XHR2YXIgY3VycmVudEJ5dGUgPSB0aGlzLnBvaW50ZXI7XG5cdFx0dmFyIGJ5dGVDb3VudCA9IDE7XG5cdFx0dmFyIGxlbmd0aCA9IFV0aWxzLnJlYWRWYXJJbnQodGhpcy5kYXRhLnN1YmFycmF5KGV2ZW50U3RhcnRJbmRleCArIDIsIGV2ZW50U3RhcnRJbmRleCArIDIgKyBieXRlQ291bnQpKTtcblx0XHR2YXIgc3RyaW5nTGVuZ3RoID0gbGVuZ3RoO1xuXG5cdFx0cmV0dXJuIFV0aWxzLmJ5dGVzVG9MZXR0ZXJzKHRoaXMuZGF0YS5zdWJhcnJheShldmVudFN0YXJ0SW5kZXggKyBieXRlQ291bnQgKyAyLCBldmVudFN0YXJ0SW5kZXggKyBieXRlQ291bnQgKyBsZW5ndGggKyAyKSk7XG5cdH1cblxuXHQvKipcblx0ICogUGFyc2VzIGV2ZW50IGludG8gSlNPTiBhbmQgYWR2YW5jZXMgcG9pbnRlciBmb3IgdGhlIHRyYWNrXG5cdCAqIEByZXR1cm4ge29iamVjdH1cblx0ICovXG5cdHBhcnNlRXZlbnQoKSB7XG5cdFx0dmFyIGV2ZW50U3RhcnRJbmRleCA9IHRoaXMucG9pbnRlciArIHRoaXMuZ2V0RGVsdGFCeXRlQ291bnQoKTtcblx0XHR2YXIgZXZlbnRKc29uID0ge307XG5cdFx0dmFyIGRlbHRhQnl0ZUNvdW50ID0gdGhpcy5nZXREZWx0YUJ5dGVDb3VudCgpO1xuXHRcdGV2ZW50SnNvbi50cmFjayA9IHRoaXMuaW5kZXggKyAxO1xuXHRcdGV2ZW50SnNvbi5kZWx0YSA9IHRoaXMuZ2V0RGVsdGEoKTtcblx0XHR0aGlzLmxhc3RUaWNrID0gdGhpcy5sYXN0VGljayArIGV2ZW50SnNvbi5kZWx0YTtcblx0XHR0aGlzLnJ1bm5pbmdEZWx0YSArPSBldmVudEpzb24uZGVsdGE7XG5cdFx0ZXZlbnRKc29uLnRpY2sgPSB0aGlzLnJ1bm5pbmdEZWx0YTtcblx0XHRldmVudEpzb24uYnl0ZUluZGV4ID0gdGhpcy5wb2ludGVyO1xuXG5cdFx0Ly9ldmVudEpzb24ucmF3ID0gZXZlbnQ7XG5cdFx0aWYgKHRoaXMuZGF0YVtldmVudFN0YXJ0SW5kZXhdID09IDB4ZmYpIHtcblx0XHRcdC8vIE1ldGEgRXZlbnRcblxuXHRcdFx0Ly8gSWYgdGhpcyBpcyBhIG1ldGEgZXZlbnQgd2Ugc2hvdWxkIGVtaXQgdGhlIGRhdGEgYW5kIGltbWVkaWF0ZWx5IG1vdmUgdG8gdGhlIG5leHQgZXZlbnRcblx0XHRcdC8vIG90aGVyd2lzZSBpZiB3ZSBsZXQgaXQgcnVuIHRocm91Z2ggdGhlIG5leHQgY3ljbGUgYSBzbGlnaHQgZGVsYXkgd2lsbCBhY2N1bXVsYXRlIGlmIG11bHRpcGxlIHRyYWNrc1xuXHRcdFx0Ly8gYXJlIGJlaW5nIHBsYXllZCBzaW11bHRhbmVvdXNseVxuXG5cdFx0XHRzd2l0Y2godGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleCArIDFdKSB7XG5cdFx0XHRcdGNhc2UgMHgwMDogLy8gU2VxdWVuY2UgTnVtYmVyXG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnU2VxdWVuY2UgTnVtYmVyJztcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSAweDAxOiAvLyBUZXh0IEV2ZW50XG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnVGV4dCBFdmVudCc7XG5cdFx0XHRcdFx0ZXZlbnRKc29uLnN0cmluZyA9IHRoaXMuZ2V0U3RyaW5nRGF0YShldmVudFN0YXJ0SW5kZXgpO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlIDB4MDI6IC8vIENvcHlyaWdodCBOb3RpY2Vcblx0XHRcdFx0XHRldmVudEpzb24ubmFtZSA9ICdDb3B5cmlnaHQgTm90aWNlJztcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSAweDAzOiAvLyBTZXF1ZW5jZS9UcmFjayBOYW1lXG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnU2VxdWVuY2UvVHJhY2sgTmFtZSc7XG5cdFx0XHRcdFx0ZXZlbnRKc29uLnN0cmluZyA9IHRoaXMuZ2V0U3RyaW5nRGF0YShldmVudFN0YXJ0SW5kZXgpO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlIDB4MDQ6IC8vIEluc3RydW1lbnQgTmFtZVxuXHRcdFx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ0luc3RydW1lbnQgTmFtZSc7XG5cdFx0XHRcdFx0ZXZlbnRKc29uLnN0cmluZyA9IHRoaXMuZ2V0U3RyaW5nRGF0YShldmVudFN0YXJ0SW5kZXgpO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlIDB4MDU6IC8vIEx5cmljXG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnTHlyaWMnO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5zdHJpbmcgPSB0aGlzLmdldFN0cmluZ0RhdGEoZXZlbnRTdGFydEluZGV4KTtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSAweDA2OiAvLyBNYXJrZXJcblx0XHRcdFx0XHRldmVudEpzb24ubmFtZSA9ICdNYXJrZXInO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlIDB4MDc6IC8vIEN1ZSBQb2ludFxuXHRcdFx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ0N1ZSBQb2ludCc7XG5cdFx0XHRcdFx0ZXZlbnRKc29uLnN0cmluZyA9IHRoaXMuZ2V0U3RyaW5nRGF0YShldmVudFN0YXJ0SW5kZXgpO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlIDB4MDk6IC8vIERldmljZSBOYW1lXG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnRGV2aWNlIE5hbWUnO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5zdHJpbmcgPSB0aGlzLmdldFN0cmluZ0RhdGEoZXZlbnRTdGFydEluZGV4KTtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSAweDIwOiAvLyBNSURJIENoYW5uZWwgUHJlZml4XG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnTUlESSBDaGFubmVsIFByZWZpeCc7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdGNhc2UgMHgyMTogLy8gTUlESSBQb3J0XG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnTUlESSBQb3J0Jztcblx0XHRcdFx0XHRldmVudEpzb24uZGF0YSA9IFV0aWxzLmJ5dGVzVG9OdW1iZXIoW3RoaXMuZGF0YVtldmVudFN0YXJ0SW5kZXggKyAzXV0pO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlIDB4MkY6IC8vIEVuZCBvZiBUcmFja1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ0VuZCBvZiBUcmFjayc7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdGNhc2UgMHg1MTogLy8gU2V0IFRlbXBvXG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnU2V0IFRlbXBvJztcblx0XHRcdFx0XHRldmVudEpzb24uZGF0YSA9IE1hdGgucm91bmQoNjAwMDAwMDAgLyBVdGlscy5ieXRlc1RvTnVtYmVyKHRoaXMuZGF0YS5zdWJhcnJheShldmVudFN0YXJ0SW5kZXggKyAzLCBldmVudFN0YXJ0SW5kZXggKyA2KSkpO1xuXHRcdFx0XHRcdHRoaXMudGVtcG8gPSBldmVudEpzb24uZGF0YTtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSAweDU0OiAvLyBTTVRQRSBPZmZzZXRcblx0XHRcdFx0XHRldmVudEpzb24ubmFtZSA9ICdTTVRQRSBPZmZzZXQnO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlIDB4NTg6IC8vIFRpbWUgU2lnbmF0dXJlXG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnVGltZSBTaWduYXR1cmUnO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlIDB4NTk6IC8vIEtleSBTaWduYXR1cmVcblx0XHRcdFx0XHRldmVudEpzb24ubmFtZSA9ICdLZXkgU2lnbmF0dXJlJztcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0Y2FzZSAweDdGOiAvLyBTZXF1ZW5jZXItU3BlY2lmaWMgTWV0YS1ldmVudFxuXHRcdFx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ1NlcXVlbmNlci1TcGVjaWZpYyBNZXRhLWV2ZW50Jztcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0ZGVmYXVsdDpcblx0XHRcdFx0XHRldmVudEpzb24ubmFtZSA9ICdVbmtub3duOiAnICsgdGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleCArIDFdLnRvU3RyaW5nKDE2KTtcblx0XHRcdFx0XHRicmVhaztcblx0XHRcdH1cblxuXHRcdFx0dmFyIGxlbmd0aCA9IHRoaXMuZGF0YVt0aGlzLnBvaW50ZXIgKyBkZWx0YUJ5dGVDb3VudCArIDJdO1xuXHRcdFx0Ly8gU29tZSBtZXRhIGV2ZW50cyB3aWxsIGhhdmUgdmx2IHRoYXQgbmVlZHMgdG8gYmUgaGFuZGxlZFxuXG5cdFx0XHR0aGlzLnBvaW50ZXIgKz0gZGVsdGFCeXRlQ291bnQgKyAzICsgbGVuZ3RoO1xuXG5cdFx0fSBlbHNlIGlmKHRoaXMuZGF0YVtldmVudFN0YXJ0SW5kZXhdID09IDB4ZjApIHtcblx0XHRcdC8vIFN5c2V4XG5cdFx0XHRldmVudEpzb24ubmFtZSA9ICdTeXNleCc7XG5cdFx0XHR2YXIgbGVuZ3RoID0gdGhpcy5kYXRhW3RoaXMucG9pbnRlciArIGRlbHRhQnl0ZUNvdW50ICsgMV07XG5cdFx0XHR0aGlzLnBvaW50ZXIgKz0gZGVsdGFCeXRlQ291bnQgKyAyICsgbGVuZ3RoO1xuXG5cdFx0fSBlbHNlIHtcblx0XHRcdC8vIFZvaWNlIGV2ZW50XG5cdFx0XHRpZiAodGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleF0gPCAweDgwKSB7XG5cdFx0XHRcdC8vIFJ1bm5pbmcgc3RhdHVzXG5cdFx0XHRcdGV2ZW50SnNvbi5ydW5uaW5nID0gdHJ1ZTtcblx0XHRcdFx0ZXZlbnRKc29uLm5vdGVOdW1iZXIgPSB0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4XTtcblx0XHRcdFx0ZXZlbnRKc29uLm5vdGVOYW1lID0gQ29uc3RhbnRzLk5PVEVTW3RoaXMuZGF0YVtldmVudFN0YXJ0SW5kZXhdXTtcblx0XHRcdFx0ZXZlbnRKc29uLnZlbG9jaXR5ID0gdGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleCArIDFdO1xuXG5cdFx0XHRcdGlmICh0aGlzLmxhc3RTdGF0dXMgPD0gMHg4Zikge1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ05vdGUgb2ZmJztcblx0XHRcdFx0XHRldmVudEpzb24uY2hhbm5lbCA9IHRoaXMubGFzdFN0YXR1cyAtIDB4ODAgKyAxO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAodGhpcy5sYXN0U3RhdHVzIDw9IDB4OWYpIHtcblx0XHRcdFx0XHRldmVudEpzb24ubmFtZSA9ICdOb3RlIG9uJztcblx0XHRcdFx0XHRldmVudEpzb24uY2hhbm5lbCA9IHRoaXMubGFzdFN0YXR1cyAtIDB4OTAgKyAxO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0dGhpcy5wb2ludGVyICs9IGRlbHRhQnl0ZUNvdW50ICsgMjtcblxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0dGhpcy5sYXN0U3RhdHVzID0gdGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleF07XG5cblx0XHRcdFx0aWYgKHRoaXMuZGF0YVtldmVudFN0YXJ0SW5kZXhdIDw9IDB4OGYpIHtcblx0XHRcdFx0XHQvLyBOb3RlIG9mZlxuXHRcdFx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ05vdGUgb2ZmJztcblx0XHRcdFx0XHRldmVudEpzb24uY2hhbm5lbCA9IHRoaXMubGFzdFN0YXR1cyAtIDB4ODAgKyAxO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5ub3RlTnVtYmVyID0gdGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleCArIDFdO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5ub3RlTmFtZSA9IENvbnN0YW50cy5OT1RFU1t0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4ICsgMV1dO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi52ZWxvY2l0eSA9IE1hdGgucm91bmQodGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleCArIDJdIC8gMTI3ICogMTAwKTtcblx0XHRcdFx0XHR0aGlzLnBvaW50ZXIgKz0gZGVsdGFCeXRlQ291bnQgKyAzO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAodGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleF0gPD0gMHg5Zikge1xuXHRcdFx0XHRcdC8vIE5vdGUgb25cblx0XHRcdFx0XHRldmVudEpzb24ubmFtZSA9ICdOb3RlIG9uJztcblx0XHRcdFx0XHRldmVudEpzb24uY2hhbm5lbCA9IHRoaXMubGFzdFN0YXR1cyAtIDB4OTAgKyAxO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5ub3RlTnVtYmVyID0gdGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleCArIDFdO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5ub3RlTmFtZSA9IENvbnN0YW50cy5OT1RFU1t0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4ICsgMV1dO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi52ZWxvY2l0eSA9IE1hdGgucm91bmQodGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleCArIDJdIC8gMTI3ICogMTAwKTtcblx0XHRcdFx0XHR0aGlzLnBvaW50ZXIgKz0gZGVsdGFCeXRlQ291bnQgKyAzO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAodGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleF0gPD0gMHhhZikge1xuXHRcdFx0XHRcdC8vIFBvbHlwaG9uaWMgS2V5IFByZXNzdXJlXG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnUG9seXBob25pYyBLZXkgUHJlc3N1cmUnO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5jaGFubmVsID0gdGhpcy5sYXN0U3RhdHVzIC0gMHhhMCArIDE7XG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5vdGUgPSBDb25zdGFudHMuTk9URVNbdGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleCArIDFdXTtcblx0XHRcdFx0XHRldmVudEpzb24ucHJlc3N1cmUgPSBldmVudFsyXTtcblx0XHRcdFx0XHR0aGlzLnBvaW50ZXIgKz0gZGVsdGFCeXRlQ291bnQgKyAzO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAodGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleF0gPD0gMHhiZikge1xuXHRcdFx0XHRcdC8vIENvbnRyb2xsZXIgQ2hhbmdlXG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnQ29udHJvbGxlciBDaGFuZ2UnO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5jaGFubmVsID0gdGhpcy5sYXN0U3RhdHVzIC0gMHhiMCArIDE7XG5cdFx0XHRcdFx0ZXZlbnRKc29uLm51bWJlciA9IHRoaXMuZGF0YVtldmVudFN0YXJ0SW5kZXggKyAxXTtcblx0XHRcdFx0XHRldmVudEpzb24udmFsdWUgPSB0aGlzLmRhdGFbZXZlbnRTdGFydEluZGV4ICsgMl07XG5cdFx0XHRcdFx0dGhpcy5wb2ludGVyICs9IGRlbHRhQnl0ZUNvdW50ICsgMztcblxuXHRcdFx0XHR9IGVsc2UgaWYgKHRoaXMuZGF0YVtldmVudFN0YXJ0SW5kZXhdIDw9IDB4Y2YpIHtcblx0XHRcdFx0XHQvLyBQcm9ncmFtIENoYW5nZVxuXHRcdFx0XHRcdGV2ZW50SnNvbi5uYW1lID0gJ1Byb2dyYW0gQ2hhbmdlJztcblx0XHRcdFx0XHRldmVudEpzb24uY2hhbm5lbCA9IHRoaXMubGFzdFN0YXR1cyAtIDB4YzAgKyAxO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi52YWx1ZSA9IHRoaXMuZGF0YVtldmVudFN0YXJ0SW5kZXggKyAxXTtcblx0XHRcdFx0XHR0aGlzLnBvaW50ZXIgKz0gZGVsdGFCeXRlQ291bnQgKyAyO1xuXG5cdFx0XHRcdH0gZWxzZSBpZiAodGhpcy5kYXRhW2V2ZW50U3RhcnRJbmRleF0gPD0gMHhkZikge1xuXHRcdFx0XHRcdC8vIENoYW5uZWwgS2V5IFByZXNzdXJlXG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnQ2hhbm5lbCBLZXkgUHJlc3N1cmUnO1xuXHRcdFx0XHRcdGV2ZW50SnNvbi5jaGFubmVsID0gdGhpcy5sYXN0U3RhdHVzIC0gMHhkMCArIDE7XG5cdFx0XHRcdFx0dGhpcy5wb2ludGVyICs9IGRlbHRhQnl0ZUNvdW50ICsgMjtcblxuXHRcdFx0XHR9IGVsc2UgaWYgKHRoaXMuZGF0YVtldmVudFN0YXJ0SW5kZXhdIDw9IDB4ZWYpIHtcblx0XHRcdFx0XHQvLyBQaXRjaCBCZW5kXG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnUGl0Y2ggQmVuZCc7XG5cdFx0XHRcdFx0ZXZlbnRKc29uLmNoYW5uZWwgPSB0aGlzLmxhc3RTdGF0dXMgLSAweGUwICsgMTtcblx0XHRcdFx0XHR0aGlzLnBvaW50ZXIgKz0gZGVsdGFCeXRlQ291bnQgKyAzO1xuXG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0ZXZlbnRKc29uLm5hbWUgPSAnVW5rbm93bi4gIFBvaW50ZXI6ICcgKyB0aGlzLnBvaW50ZXIudG9TdHJpbmcoKSArICcgJyAgKyBldmVudFN0YXJ0SW5kZXgudG9TdHJpbmcoKSArICcgJyArIHRoaXMuZGF0YS5sZW5ndGg7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHR0aGlzLmRlbHRhICs9IGV2ZW50SnNvbi5kZWx0YTtcblx0XHR0aGlzLmV2ZW50cy5wdXNoKGV2ZW50SnNvbik7XG5cblx0XHRyZXR1cm4gZXZlbnRKc29uO1xuXHR9XG5cblx0LyoqXG5cdCAqIFJldHVybnMgdHJ1ZSBpZiBwb2ludGVyIGhhcyByZWFjaGVkIHRoZSBlbmQgb2YgdGhlIHRyYWNrLlxuXHQgKiBAcGFyYW0ge2Jvb2xlYW59XG5cdCAqL1xuXHRlbmRPZlRyYWNrKCkge1xuXHRcdGlmICh0aGlzLmRhdGFbdGhpcy5wb2ludGVyICsgMV0gPT0gMHhmZiAmJiB0aGlzLmRhdGFbdGhpcy5wb2ludGVyICsgMl0gPT0gMHgyZiAmJiB0aGlzLmRhdGFbdGhpcy5wb2ludGVyICsgM10gPT0gMHgwMCkge1xuXHRcdFx0cmV0dXJuIHRydWU7XG5cdFx0fVxuXG5cdFx0cmV0dXJuIGZhbHNlO1xuXHR9XG59XG5cbm1vZHVsZS5leHBvcnRzLlRyYWNrID0gVHJhY2s7IiwiLyoqXG4gKiBDb250YWlucyBtaXNjIHN0YXRpYyB1dGlsaXR5IG1ldGhvZHMuXG4gKi9cbmNsYXNzIFV0aWxzIHtcblxuXHQvKipcblx0ICogQ29udmVydHMgYSBzaW5nbGUgYnl0ZSB0byBhIGhleCBzdHJpbmcuXG5cdCAqIEBwYXJhbSB7bnVtYmVyfSBieXRlXG5cdCAqIEByZXR1cm4ge3N0cmluZ31cblx0ICovXG5cdHN0YXRpYyBieXRlVG9IZXgoYnl0ZSkge1xuXHRcdC8vIEVuc3VyZSBoZXggc3RyaW5nIGFsd2F5cyBoYXMgdHdvIGNoYXJzXG5cdFx0cmV0dXJuICgnMCcgKyBieXRlLnRvU3RyaW5nKDE2KSkuc2xpY2UoLTIpO1xuXHR9XG5cblx0LyoqXG5cdCAqIENvbnZlcnRzIGFuIGFycmF5IG9mIGJ5dGVzIHRvIGEgaGV4IHN0cmluZy5cblx0ICogQHBhcmFtIHthcnJheX0gYnl0ZUFycmF5XG5cdCAqIEByZXR1cm4ge3N0cmluZ31cblx0ICovXG5cdHN0YXRpYyBieXRlc1RvSGV4KGJ5dGVBcnJheSkge1xuXHRcdHZhciBoZXggPSBbXTtcblx0XHRieXRlQXJyYXkuZm9yRWFjaChieXRlID0+IGhleC5wdXNoKFV0aWxzLmJ5dGVUb0hleChieXRlKSkpO1xuXHRcdHJldHVybiBoZXguam9pbignJyk7XG5cdH1cblxuXHQvKipcblx0ICogQ29udmVydHMgYSBoZXggc3RyaW5nIHRvIGEgbnVtYmVyLlxuXHQgKiBAcGFyYW0ge3N0cmluZ30gaGV4U3RyaW5nXG5cdCAqIEByZXR1cm4ge251bWJlcn1cblx0ICovXG5cdHN0YXRpYyBoZXhUb051bWJlcihoZXhTdHJpbmcpIHtcblx0XHRyZXR1cm4gcGFyc2VJbnQoaGV4U3RyaW5nLCAxNik7XG5cdH1cblxuXHQvKipcblx0ICogQ29udmVydHMgYW4gYXJyYXkgb2YgYnl0ZXMgdG8gYSBudW1iZXIuXG5cdCAqIEBwYXJhbSB7YXJyYXl9IGJ5dGVBcnJheVxuXHQgKiBAcmV0dXJuIHtudW1iZXJ9XG5cdCAqL1xuXHRzdGF0aWMgYnl0ZXNUb051bWJlcihieXRlQXJyYXkpIHtcblx0XHRyZXR1cm4gVXRpbHMuaGV4VG9OdW1iZXIoVXRpbHMuYnl0ZXNUb0hleChieXRlQXJyYXkpKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBDb252ZXJ0cyBhbiBhcnJheSBvZiBieXRlcyB0byBsZXR0ZXJzLlxuXHQgKiBAcGFyYW0ge2FycmF5fSBieXRlQXJyYXlcblx0ICogQHJldHVybiB7c3RyaW5nfVxuXHQgKi9cblx0c3RhdGljIGJ5dGVzVG9MZXR0ZXJzKGJ5dGVBcnJheSkge1xuXHRcdHZhciBsZXR0ZXJzID0gW107XG5cdFx0Ynl0ZUFycmF5LmZvckVhY2goYnl0ZSA9PiBsZXR0ZXJzLnB1c2goU3RyaW5nLmZyb21DaGFyQ29kZShieXRlKSkpO1xuXHRcdHJldHVybiBsZXR0ZXJzLmpvaW4oJycpO1xuXHR9XG5cblx0LyoqXG5cdCAqIENvbnZlcnRzIGEgZGVjaW1hbCB0byBpdCdzIGJpbmFyeSByZXByZXNlbnRhdGlvbi5cblx0ICogQHBhcmFtIHtudW1iZXJ9IGRlY1xuXHQgKiBAcmV0dXJuIHtzdHJpbmd9XG5cdCAqL1xuXHRzdGF0aWMgZGVjVG9CaW5hcnkoZGVjKSB7XG4gICAgXHRyZXR1cm4gKGRlYyA+Pj4gMCkudG9TdHJpbmcoMik7XG5cdH1cblxuXHQvKipcblx0ICogUmVhZHMgYSB2YXJpYWJsZSBsZW5ndGggdmFsdWUuXG5cdCAqIEBwYXJhbSB7YXJyYXl9IGJ5dGVBcnJheVxuXHQgKiBAcmV0dXJuIHtudW1iZXJ9XG5cdCAqL1xuXHRzdGF0aWMgcmVhZFZhckludChieXRlQXJyYXkpIHtcblx0XHR2YXIgcmVzdWx0ID0gMDtcblx0XHRieXRlQXJyYXkuZm9yRWFjaChudW1iZXIgPT4ge1xuXHRcdFx0dmFyIGIgPSBudW1iZXI7XG5cdFx0XHRpZiAoYiAmIDB4ODApIHtcblx0XHRcdFx0cmVzdWx0ICs9IChiICYgMHg3Zik7XG5cdFx0XHRcdHJlc3VsdCA8PD0gNztcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdC8qIGIgaXMgdGhlIGxhc3QgYnl0ZSAqL1xuXHRcdFx0XHRyZXN1bHQgKz0gYjtcblx0XHRcdH1cblx0XHR9KTtcblxuXHRcdHJldHVybiByZXN1bHQ7XG5cdH1cblxuXHQvKipcblx0ICogRGVjb2RlcyBiYXNlLTY0IGVuY29kZWQgc3RyaW5nXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSBzdHJpbmdcblx0ICogQHJldHVybiB7c3RyaW5nfVxuXHQgKi9cblx0c3RhdGljIGF0b2Ioc3RyaW5nKSB7XG5cdFx0aWYgKHR5cGVvZiBhdG9iID09PSAnZnVuY3Rpb24nKSByZXR1cm4gYXRvYihzdHJpbmcpO1xuXHRcdHJldHVybiBuZXcgQnVmZmVyKHN0cmluZywgJ2Jhc2U2NCcpLnRvU3RyaW5nKCdiaW5hcnknKTtcblx0fVxufVxuXG5leHBvcnRzLlV0aWxzID0gVXRpbHM7Il19
|