UNPKG

matrix-react-sdk

Version:
303 lines (288 loc) 45.9 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.voiceRecorderOptions = exports.highQualityRecorderOptions = exports.VoiceRecording = exports.SAMPLE_RATE = exports.RecordingState = exports.RECORDING_PLAYBACK_SAMPLES = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _recorderMin = _interopRequireDefault(require("opus-recorder/dist/recorder.min.js")); var _encoderWorkerMin = _interopRequireDefault(require("opus-recorder/dist/encoderWorker.min.js")); var _matrixWidgetApi = require("matrix-widget-api"); var _events = _interopRequireDefault(require("events")); var _logger = require("matrix-js-sdk/src/logger"); var _MediaDeviceHandler = _interopRequireDefault(require("../MediaDeviceHandler")); var _Singleflight = require("../utils/Singleflight"); var _consts = require("./consts"); var _AsyncStore = require("../stores/AsyncStore"); var _compat = require("./compat"); var _FixedRollingArray = require("../utils/FixedRollingArray"); var _numbers = require("../utils/numbers"); var _recorderWorkletFactory = _interopRequireDefault(require("./recorderWorkletFactory")); /* Copyright 2024 New Vector Ltd. Copyright 2021 The Matrix.org Foundation C.I.C. SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ const CHANNELS = 1; // stereo isn't important const SAMPLE_RATE = exports.SAMPLE_RATE = 48000; // 48khz is what WebRTC uses. 12khz is where we lose quality. const TARGET_MAX_LENGTH = 900; // 15 minutes in seconds. Somewhat arbitrary, though longer == larger files. const TARGET_WARN_TIME_LEFT = 10; // 10 seconds, also somewhat arbitrary. const RECORDING_PLAYBACK_SAMPLES = exports.RECORDING_PLAYBACK_SAMPLES = 44; const voiceRecorderOptions = exports.voiceRecorderOptions = { bitrate: 24000, // recommended Opus bitrate for high-quality VoIP encoderApplication: 2048 // voice }; const highQualityRecorderOptions = exports.highQualityRecorderOptions = { bitrate: 96000, // recommended Opus bitrate for high-quality music/audio streaming encoderApplication: 2049 // full band audio }; let RecordingState = exports.RecordingState = /*#__PURE__*/function (RecordingState) { RecordingState["Started"] = "started"; RecordingState["EndingSoon"] = "ending_soon"; RecordingState["Ended"] = "ended"; RecordingState["Uploading"] = "uploading"; RecordingState["Uploaded"] = "uploaded"; return RecordingState; }({}); class VoiceRecording extends _events.default { constructor(...args) { super(...args); (0, _defineProperty2.default)(this, "recorder", void 0); (0, _defineProperty2.default)(this, "recorderContext", void 0); (0, _defineProperty2.default)(this, "recorderSource", void 0); (0, _defineProperty2.default)(this, "recorderStream", void 0); (0, _defineProperty2.default)(this, "recorderWorklet", void 0); (0, _defineProperty2.default)(this, "recorderProcessor", void 0); (0, _defineProperty2.default)(this, "recording", false); (0, _defineProperty2.default)(this, "observable", void 0); (0, _defineProperty2.default)(this, "targetMaxLength", TARGET_MAX_LENGTH); (0, _defineProperty2.default)(this, "amplitudes", []); // at each second mark, generated (0, _defineProperty2.default)(this, "liveWaveform", new _FixedRollingArray.FixedRollingArray(RECORDING_PLAYBACK_SAMPLES, 0)); (0, _defineProperty2.default)(this, "onDataAvailable", void 0); (0, _defineProperty2.default)(this, "onAudioProcess", ev => { this.processAudioUpdate(ev.playbackTime); // We skip the functionality of the worklet regarding waveform calculations: we // should get that information pretty quick during the playback info. }); (0, _defineProperty2.default)(this, "processAudioUpdate", timeSeconds => { if (!this.recording) return; this.observable.update({ waveform: this.liveWaveform.value.map(v => (0, _numbers.clamp)(v, 0, 1)), timeSeconds: timeSeconds }); // Now that we've updated the data/waveform, let's do a time check. We don't want to // go horribly over the limit. We also emit a warning state if needed. // // We use the recorder's perspective of time to make sure we don't cut off the last // frame of audio, otherwise we end up with a 14:59 clip (899.68 seconds). This extra // safety can allow us to overshoot the target a bit, but at least when we say 15min // maximum we actually mean it. // // In testing, recorder time and worker time lag by about 400ms, which is roughly the // time needed to encode a sample/frame. // if (!this.targetMaxLength) { // skip time checks if max length has been disabled return; } const secondsLeft = TARGET_MAX_LENGTH - this.recorderSeconds; if (secondsLeft < 0) { // go over to make sure we definitely capture that last frame // noinspection JSIgnoredPromiseFromCall - we aren't concerned with it overlapping this.stop(); } else if (secondsLeft <= TARGET_WARN_TIME_LEFT) { _Singleflight.Singleflight.for(this, "ending_soon").do(() => { this.emit(RecordingState.EndingSoon, { secondsLeft }); return _Singleflight.Singleflight.Void; }); } }); } get contentType() { return "audio/ogg"; } get durationSeconds() { if (!this.recorder || !this.recorderContext) throw new Error("Duration not available without a recording"); return this.recorderContext.currentTime; } get isRecording() { return this.recording; } emit(event, ...args) { super.emit(event, ...args); super.emit(_AsyncStore.UPDATE_EVENT, event, ...args); return true; // we don't ever care if the event had listeners, so just return "yes" } disableMaxLength() { this.targetMaxLength = null; } shouldRecordInHighQuality() { // Non-voice use case is suspected when noise suppression is disabled by the user. // When recording complex audio, higher quality is required to avoid audio artifacts. // This is a really arbitrary decision, but it can be refined/replaced at any time. return !_MediaDeviceHandler.default.getAudioNoiseSuppression(); } async makeRecorder() { try { this.recorderStream = await navigator.mediaDevices.getUserMedia({ audio: { channelCount: CHANNELS, deviceId: _MediaDeviceHandler.default.getAudioInput(), autoGainControl: { ideal: _MediaDeviceHandler.default.getAudioAutoGainControl() }, echoCancellation: { ideal: _MediaDeviceHandler.default.getAudioEchoCancellation() }, noiseSuppression: { ideal: _MediaDeviceHandler.default.getAudioNoiseSuppression() } } }); this.recorderContext = (0, _compat.createAudioContext)({ // latencyHint: "interactive", // we don't want a latency hint (this causes data smoothing) }); this.recorderSource = this.recorderContext.createMediaStreamSource(this.recorderStream); // Connect our inputs and outputs if (this.recorderContext.audioWorklet) { // Set up our worklet. We use this for timing information and waveform analysis: the // web audio API prefers this be done async to avoid holding the main thread with math. await (0, _recorderWorkletFactory.default)(this.recorderContext); this.recorderWorklet = new AudioWorkletNode(this.recorderContext, _consts.WORKLET_NAME); this.recorderSource.connect(this.recorderWorklet); this.recorderWorklet.connect(this.recorderContext.destination); // Dev note: we can't use `addEventListener` for some reason. It just doesn't work. this.recorderWorklet.port.onmessage = ev => { switch (ev.data["ev"]) { case _consts.PayloadEvent.Timekeep: this.processAudioUpdate(ev.data["timeSeconds"]); break; case _consts.PayloadEvent.AmplitudeMark: // Sanity check to make sure we're adding about one sample per second if (ev.data["forIndex"] === this.amplitudes.length) { this.amplitudes.push(ev.data["amplitude"]); this.liveWaveform.pushValue(ev.data["amplitude"]); } break; } }; } else { // Safari fallback: use a processor node instead, buffered to 1024 bytes of data // like the worklet is. this.recorderProcessor = this.recorderContext.createScriptProcessor(1024, CHANNELS, CHANNELS); this.recorderSource.connect(this.recorderProcessor); this.recorderProcessor.connect(this.recorderContext.destination); this.recorderProcessor.addEventListener("audioprocess", this.onAudioProcess); } const recorderOptions = this.shouldRecordInHighQuality() ? highQualityRecorderOptions : voiceRecorderOptions; const { encoderApplication, bitrate } = recorderOptions; this.recorder = new _recorderMin.default({ encoderPath: _encoderWorkerMin.default, // magic from webpack encoderSampleRate: SAMPLE_RATE, encoderApplication: encoderApplication, streamPages: true, // this speeds up the encoding process by using CPU over time encoderFrameSize: 20, // ms, arbitrary frame size we send to the encoder numberOfChannels: CHANNELS, sourceNode: this.recorderSource, encoderBitRate: bitrate, // We use low values for the following to ease CPU usage - the resulting waveform // is indistinguishable for a voice message. Note that the underlying library will // pick defaults which prefer the highest possible quality, CPU be damned. encoderComplexity: 3, // 0-10, 10 is slow and high quality. resampleQuality: 3 // 0-10, 10 is slow and high quality }); // not using EventEmitter here because it leads to detached bufferes this.recorder.ondataavailable = data => this.onDataAvailable?.(data); } catch (e) { _logger.logger.error("Error starting recording: ", e); if (e instanceof DOMException) { // Unhelpful DOMExceptions are common - parse them sanely _logger.logger.error(`${e.name} (${e.code}): ${e.message}`); } // Clean up as best as possible if (this.recorderStream) this.recorderStream.getTracks().forEach(t => t.stop()); if (this.recorderSource) this.recorderSource.disconnect(); if (this.recorder) this.recorder.close(); if (this.recorderContext) { // noinspection ES6MissingAwait - not important that we wait this.recorderContext.close(); } throw e; // rethrow so upstream can handle it } } get liveData() { if (!this.recording || !this.observable) throw new Error("No observable when not recording"); return this.observable; } get isSupported() { return !!_recorderMin.default.isRecordingSupported(); } /** * {@link https://github.com/chris-rudmin/opus-recorder#instance-fields ref for recorderSeconds} */ get recorderSeconds() { if (!this.recorder) return undefined; return this.recorder.encodedSamplePosition / 48000; } async start() { if (this.recording) { throw new Error("Recording already in progress"); } if (this.observable) { this.observable.close(); } this.observable = new _matrixWidgetApi.SimpleObservable(); await this.makeRecorder(); await this.recorder?.start(); this.recording = true; this.emit(RecordingState.Started); } async stop() { return _Singleflight.Singleflight.for(this, "stop").do(async () => { if (!this.recording) { throw new Error("No recording to stop"); } // Disconnect the source early to start shutting down resources await this.recorder.stop(); // stop first to flush the last frame this.recorderSource.disconnect(); if (this.recorderWorklet) this.recorderWorklet.disconnect(); if (this.recorderProcessor) { this.recorderProcessor.disconnect(); this.recorderProcessor.removeEventListener("audioprocess", this.onAudioProcess); } // close the context after the recorder so the recorder doesn't try to // connect anything to the context (this would generate a warning) await this.recorderContext.close(); // Now stop all the media tracks so we can release them back to the user/OS this.recorderStream.getTracks().forEach(t => t.stop()); // Finally do our post-processing and clean up this.recording = false; await this.recorder.close(); this.emit(RecordingState.Ended); }); } destroy() { // noinspection JSIgnoredPromiseFromCall - not concerned about stop() being called async here this.stop(); this.removeAllListeners(); this.onDataAvailable = undefined; _Singleflight.Singleflight.forgetAllFor(this); // noinspection JSIgnoredPromiseFromCall - not concerned about being called async here this.observable?.close(); } } exports.VoiceRecording = VoiceRecording; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcmVjb3JkZXJNaW4iLCJfaW50ZXJvcFJlcXVpcmVEZWZhdWx0IiwicmVxdWlyZSIsIl9lbmNvZGVyV29ya2VyTWluIiwiX21hdHJpeFdpZGdldEFwaSIsIl9ldmVudHMiLCJfbG9nZ2VyIiwiX01lZGlhRGV2aWNlSGFuZGxlciIsIl9TaW5nbGVmbGlnaHQiLCJfY29uc3RzIiwiX0FzeW5jU3RvcmUiLCJfY29tcGF0IiwiX0ZpeGVkUm9sbGluZ0FycmF5IiwiX251bWJlcnMiLCJfcmVjb3JkZXJXb3JrbGV0RmFjdG9yeSIsIkNIQU5ORUxTIiwiU0FNUExFX1JBVEUiLCJleHBvcnRzIiwiVEFSR0VUX01BWF9MRU5HVEgiLCJUQVJHRVRfV0FSTl9USU1FX0xFRlQiLCJSRUNPUkRJTkdfUExBWUJBQ0tfU0FNUExFUyIsInZvaWNlUmVjb3JkZXJPcHRpb25zIiwiYml0cmF0ZSIsImVuY29kZXJBcHBsaWNhdGlvbiIsImhpZ2hRdWFsaXR5UmVjb3JkZXJPcHRpb25zIiwiUmVjb3JkaW5nU3RhdGUiLCJWb2ljZVJlY29yZGluZyIsIkV2ZW50RW1pdHRlciIsImNvbnN0cnVjdG9yIiwiYXJncyIsIl9kZWZpbmVQcm9wZXJ0eTIiLCJkZWZhdWx0IiwiRml4ZWRSb2xsaW5nQXJyYXkiLCJldiIsInByb2Nlc3NBdWRpb1VwZGF0ZSIsInBsYXliYWNrVGltZSIsInRpbWVTZWNvbmRzIiwicmVjb3JkaW5nIiwib2JzZXJ2YWJsZSIsInVwZGF0ZSIsIndhdmVmb3JtIiwibGl2ZVdhdmVmb3JtIiwidmFsdWUiLCJtYXAiLCJ2IiwiY2xhbXAiLCJ0YXJnZXRNYXhMZW5ndGgiLCJzZWNvbmRzTGVmdCIsInJlY29yZGVyU2Vjb25kcyIsInN0b3AiLCJTaW5nbGVmbGlnaHQiLCJmb3IiLCJkbyIsImVtaXQiLCJFbmRpbmdTb29uIiwiVm9pZCIsImNvbnRlbnRUeXBlIiwiZHVyYXRpb25TZWNvbmRzIiwicmVjb3JkZXIiLCJyZWNvcmRlckNvbnRleHQiLCJFcnJvciIsImN1cnJlbnRUaW1lIiwiaXNSZWNvcmRpbmciLCJldmVudCIsIlVQREFURV9FVkVOVCIsImRpc2FibGVNYXhMZW5ndGgiLCJzaG91bGRSZWNvcmRJbkhpZ2hRdWFsaXR5IiwiTWVkaWFEZXZpY2VIYW5kbGVyIiwiZ2V0QXVkaW9Ob2lzZVN1cHByZXNzaW9uIiwibWFrZVJlY29yZGVyIiwicmVjb3JkZXJTdHJlYW0iLCJuYXZpZ2F0b3IiLCJtZWRpYURldmljZXMiLCJnZXRVc2VyTWVkaWEiLCJhdWRpbyIsImNoYW5uZWxDb3VudCIsImRldmljZUlkIiwiZ2V0QXVkaW9JbnB1dCIsImF1dG9HYWluQ29udHJvbCIsImlkZWFsIiwiZ2V0QXVkaW9BdXRvR2FpbkNvbnRyb2wiLCJlY2hvQ2FuY2VsbGF0aW9uIiwiZ2V0QXVkaW9FY2hvQ2FuY2VsbGF0aW9uIiwibm9pc2VTdXBwcmVzc2lvbiIsImNyZWF0ZUF1ZGlvQ29udGV4dCIsInJlY29yZGVyU291cmNlIiwiY3JlYXRlTWVkaWFTdHJlYW1Tb3VyY2UiLCJhdWRpb1dvcmtsZXQiLCJyZWNvcmRlcldvcmtsZXRGYWN0b3J5IiwicmVjb3JkZXJXb3JrbGV0IiwiQXVkaW9Xb3JrbGV0Tm9kZSIsIldPUktMRVRfTkFNRSIsImNvbm5lY3QiLCJkZXN0aW5hdGlvbiIsInBvcnQiLCJvbm1lc3NhZ2UiLCJkYXRhIiwiUGF5bG9hZEV2ZW50IiwiVGltZWtlZXAiLCJBbXBsaXR1ZGVNYXJrIiwiYW1wbGl0dWRlcyIsImxlbmd0aCIsInB1c2giLCJwdXNoVmFsdWUiLCJyZWNvcmRlclByb2Nlc3NvciIsImNyZWF0ZVNjcmlwdFByb2Nlc3NvciIsImFkZEV2ZW50TGlzdGVuZXIiLCJvbkF1ZGlvUHJvY2VzcyIsInJlY29yZGVyT3B0aW9ucyIsIlJlY29yZGVyIiwiZW5jb2RlclBhdGgiLCJlbmNvZGVyU2FtcGxlUmF0ZSIsInN0cmVhbVBhZ2VzIiwiZW5jb2RlckZyYW1lU2l6ZSIsIm51bWJlck9mQ2hhbm5lbHMiLCJzb3VyY2VOb2RlIiwiZW5jb2RlckJpdFJhdGUiLCJlbmNvZGVyQ29tcGxleGl0eSIsInJlc2FtcGxlUXVhbGl0eSIsIm9uZGF0YWF2YWlsYWJsZSIsIm9uRGF0YUF2YWlsYWJsZSIsImUiLCJsb2dnZXIiLCJlcnJvciIsIkRPTUV4Y2VwdGlvbiIsIm5hbWUiLCJjb2RlIiwibWVzc2FnZSIsImdldFRyYWNrcyIsImZvckVhY2giLCJ0IiwiZGlzY29ubmVjdCIsImNsb3NlIiwibGl2ZURhdGEiLCJpc1N1cHBvcnRlZCIsImlzUmVjb3JkaW5nU3VwcG9ydGVkIiwidW5kZWZpbmVkIiwiZW5jb2RlZFNhbXBsZVBvc2l0aW9uIiwic3RhcnQiLCJTaW1wbGVPYnNlcnZhYmxlIiwiU3RhcnRlZCIsInJlbW92ZUV2ZW50TGlzdGVuZXIiLCJFbmRlZCIsImRlc3Ryb3kiLCJyZW1vdmVBbGxMaXN0ZW5lcnMiLCJmb3JnZXRBbGxGb3IiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvYXVkaW8vVm9pY2VSZWNvcmRpbmcudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLypcbkNvcHlyaWdodCAyMDI0IE5ldyBWZWN0b3IgTHRkLlxuQ29weXJpZ2h0IDIwMjEgVGhlIE1hdHJpeC5vcmcgRm91bmRhdGlvbiBDLkkuQy5cblxuU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFHUEwtMy4wLW9ubHkgT1IgR1BMLTMuMC1vbmx5XG5QbGVhc2Ugc2VlIExJQ0VOU0UgZmlsZXMgaW4gdGhlIHJlcG9zaXRvcnkgcm9vdCBmb3IgZnVsbCBkZXRhaWxzLlxuKi9cblxuaW1wb3J0IFJlY29yZGVyIGZyb20gXCJvcHVzLXJlY29yZGVyL2Rpc3QvcmVjb3JkZXIubWluLmpzXCI7XG5pbXBvcnQgZW5jb2RlclBhdGggZnJvbSBcIm9wdXMtcmVjb3JkZXIvZGlzdC9lbmNvZGVyV29ya2VyLm1pbi5qc1wiO1xuaW1wb3J0IHsgU2ltcGxlT2JzZXJ2YWJsZSB9IGZyb20gXCJtYXRyaXgtd2lkZ2V0LWFwaVwiO1xuaW1wb3J0IEV2ZW50RW1pdHRlciBmcm9tIFwiZXZlbnRzXCI7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tIFwibWF0cml4LWpzLXNkay9zcmMvbG9nZ2VyXCI7XG5cbmltcG9ydCBNZWRpYURldmljZUhhbmRsZXIgZnJvbSBcIi4uL01lZGlhRGV2aWNlSGFuZGxlclwiO1xuaW1wb3J0IHsgSURlc3Ryb3lhYmxlIH0gZnJvbSBcIi4uL3V0aWxzL0lEZXN0cm95YWJsZVwiO1xuaW1wb3J0IHsgU2luZ2xlZmxpZ2h0IH0gZnJvbSBcIi4uL3V0aWxzL1NpbmdsZWZsaWdodFwiO1xuaW1wb3J0IHsgUGF5bG9hZEV2ZW50LCBXT1JLTEVUX05BTUUgfSBmcm9tIFwiLi9jb25zdHNcIjtcbmltcG9ydCB7IFVQREFURV9FVkVOVCB9IGZyb20gXCIuLi9zdG9yZXMvQXN5bmNTdG9yZVwiO1xuaW1wb3J0IHsgY3JlYXRlQXVkaW9Db250ZXh0IH0gZnJvbSBcIi4vY29tcGF0XCI7XG5pbXBvcnQgeyBGaXhlZFJvbGxpbmdBcnJheSB9IGZyb20gXCIuLi91dGlscy9GaXhlZFJvbGxpbmdBcnJheVwiO1xuaW1wb3J0IHsgY2xhbXAgfSBmcm9tIFwiLi4vdXRpbHMvbnVtYmVyc1wiO1xuaW1wb3J0IHJlY29yZGVyV29ya2xldEZhY3RvcnkgZnJvbSBcIi4vcmVjb3JkZXJXb3JrbGV0RmFjdG9yeVwiO1xuXG5jb25zdCBDSEFOTkVMUyA9IDE7IC8vIHN0ZXJlbyBpc24ndCBpbXBvcnRhbnRcbmV4cG9ydCBjb25zdCBTQU1QTEVfUkFURSA9IDQ4MDAwOyAvLyA0OGtoeiBpcyB3aGF0IFdlYlJUQyB1c2VzLiAxMmtoeiBpcyB3aGVyZSB3ZSBsb3NlIHF1YWxpdHkuXG5jb25zdCBUQVJHRVRfTUFYX0xFTkdUSCA9IDkwMDsgLy8gMTUgbWludXRlcyBpbiBzZWNvbmRzLiBTb21ld2hhdCBhcmJpdHJhcnksIHRob3VnaCBsb25nZXIgPT0gbGFyZ2VyIGZpbGVzLlxuY29uc3QgVEFSR0VUX1dBUk5fVElNRV9MRUZUID0gMTA7IC8vIDEwIHNlY29uZHMsIGFsc28gc29tZXdoYXQgYXJiaXRyYXJ5LlxuXG5leHBvcnQgY29uc3QgUkVDT1JESU5HX1BMQVlCQUNLX1NBTVBMRVMgPSA0NDtcblxuaW50ZXJmYWNlIFJlY29yZGVyT3B0aW9ucyB7XG4gICAgYml0cmF0ZTogbnVtYmVyO1xuICAgIGVuY29kZXJBcHBsaWNhdGlvbjogbnVtYmVyO1xufVxuXG5leHBvcnQgY29uc3Qgdm9pY2VSZWNvcmRlck9wdGlvbnM6IFJlY29yZGVyT3B0aW9ucyA9IHtcbiAgICBiaXRyYXRlOiAyNDAwMCwgLy8gcmVjb21tZW5kZWQgT3B1cyBiaXRyYXRlIGZvciBoaWdoLXF1YWxpdHkgVm9JUFxuICAgIGVuY29kZXJBcHBsaWNhdGlvbjogMjA0OCwgLy8gdm9pY2Vcbn07XG5cbmV4cG9ydCBjb25zdCBoaWdoUXVhbGl0eVJlY29yZGVyT3B0aW9uczogUmVjb3JkZXJPcHRpb25zID0ge1xuICAgIGJpdHJhdGU6IDk2MDAwLCAvLyByZWNvbW1lbmRlZCBPcHVzIGJpdHJhdGUgZm9yIGhpZ2gtcXVhbGl0eSBtdXNpYy9hdWRpbyBzdHJlYW1pbmdcbiAgICBlbmNvZGVyQXBwbGljYXRpb246IDIwNDksIC8vIGZ1bGwgYmFuZCBhdWRpb1xufTtcblxuZXhwb3J0IGludGVyZmFjZSBJUmVjb3JkaW5nVXBkYXRlIHtcbiAgICB3YXZlZm9ybTogbnVtYmVyW107IC8vIGZsb2F0aW5nIHBvaW50cyBiZXR3ZWVuIDAgKGxvdykgYW5kIDEgKGhpZ2gpLlxuICAgIHRpbWVTZWNvbmRzOiBudW1iZXI7IC8vIGZsb2F0XG59XG5cbmV4cG9ydCBlbnVtIFJlY29yZGluZ1N0YXRlIHtcbiAgICBTdGFydGVkID0gXCJzdGFydGVkXCIsXG4gICAgRW5kaW5nU29vbiA9IFwiZW5kaW5nX3Nvb25cIiwgLy8gZW1pdHMgYW4gb2JqZWN0IHdpdGggYSBzaW5nbGUgbnVtZXJpY2FsIHZhbHVlOiBzZWNvbmRzTGVmdFxuICAgIEVuZGVkID0gXCJlbmRlZFwiLFxuICAgIFVwbG9hZGluZyA9IFwidXBsb2FkaW5nXCIsXG4gICAgVXBsb2FkZWQgPSBcInVwbG9hZGVkXCIsXG59XG5cbmV4cG9ydCBjbGFzcyBWb2ljZVJlY29yZGluZyBleHRlbmRzIEV2ZW50RW1pdHRlciBpbXBsZW1lbnRzIElEZXN0cm95YWJsZSB7XG4gICAgcHJpdmF0ZSByZWNvcmRlcj86IFJlY29yZGVyO1xuICAgIHByaXZhdGUgcmVjb3JkZXJDb250ZXh0PzogQXVkaW9Db250ZXh0O1xuICAgIHByaXZhdGUgcmVjb3JkZXJTb3VyY2U/OiBNZWRpYVN0cmVhbUF1ZGlvU291cmNlTm9kZTtcbiAgICBwcml2YXRlIHJlY29yZGVyU3RyZWFtPzogTWVkaWFTdHJlYW07XG4gICAgcHJpdmF0ZSByZWNvcmRlcldvcmtsZXQ/OiBBdWRpb1dvcmtsZXROb2RlO1xuICAgIHByaXZhdGUgcmVjb3JkZXJQcm9jZXNzb3I/OiBTY3JpcHRQcm9jZXNzb3JOb2RlO1xuICAgIHByaXZhdGUgcmVjb3JkaW5nID0gZmFsc2U7XG4gICAgcHJpdmF0ZSBvYnNlcnZhYmxlPzogU2ltcGxlT2JzZXJ2YWJsZTxJUmVjb3JkaW5nVXBkYXRlPjtcbiAgICBwcml2YXRlIHRhcmdldE1heExlbmd0aDogbnVtYmVyIHwgbnVsbCA9IFRBUkdFVF9NQVhfTEVOR1RIO1xuICAgIHB1YmxpYyBhbXBsaXR1ZGVzOiBudW1iZXJbXSA9IFtdOyAvLyBhdCBlYWNoIHNlY29uZCBtYXJrLCBnZW5lcmF0ZWRcbiAgICBwcml2YXRlIGxpdmVXYXZlZm9ybSA9IG5ldyBGaXhlZFJvbGxpbmdBcnJheShSRUNPUkRJTkdfUExBWUJBQ0tfU0FNUExFUywgMCk7XG4gICAgcHVibGljIG9uRGF0YUF2YWlsYWJsZT86IChkYXRhOiBBcnJheUJ1ZmZlcikgPT4gdm9pZDtcblxuICAgIHB1YmxpYyBnZXQgY29udGVudFR5cGUoKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIFwiYXVkaW8vb2dnXCI7XG4gICAgfVxuXG4gICAgcHVibGljIGdldCBkdXJhdGlvblNlY29uZHMoKTogbnVtYmVyIHtcbiAgICAgICAgaWYgKCF0aGlzLnJlY29yZGVyIHx8ICF0aGlzLnJlY29yZGVyQ29udGV4dCkgdGhyb3cgbmV3IEVycm9yKFwiRHVyYXRpb24gbm90IGF2YWlsYWJsZSB3aXRob3V0IGEgcmVjb3JkaW5nXCIpO1xuICAgICAgICByZXR1cm4gdGhpcy5yZWNvcmRlckNvbnRleHQuY3VycmVudFRpbWU7XG4gICAgfVxuXG4gICAgcHVibGljIGdldCBpc1JlY29yZGluZygpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucmVjb3JkaW5nO1xuICAgIH1cblxuICAgIHB1YmxpYyBlbWl0KGV2ZW50OiBzdHJpbmcsIC4uLmFyZ3M6IGFueVtdKTogYm9vbGVhbiB7XG4gICAgICAgIHN1cGVyLmVtaXQoZXZlbnQsIC4uLmFyZ3MpO1xuICAgICAgICBzdXBlci5lbWl0KFVQREFURV9FVkVOVCwgZXZlbnQsIC4uLmFyZ3MpO1xuICAgICAgICByZXR1cm4gdHJ1ZTsgLy8gd2UgZG9uJ3QgZXZlciBjYXJlIGlmIHRoZSBldmVudCBoYWQgbGlzdGVuZXJzLCBzbyBqdXN0IHJldHVybiBcInllc1wiXG4gICAgfVxuXG4gICAgcHVibGljIGRpc2FibGVNYXhMZW5ndGgoKTogdm9pZCB7XG4gICAgICAgIHRoaXMudGFyZ2V0TWF4TGVuZ3RoID0gbnVsbDtcbiAgICB9XG5cbiAgICBwcml2YXRlIHNob3VsZFJlY29yZEluSGlnaFF1YWxpdHkoKTogYm9vbGVhbiB7XG4gICAgICAgIC8vIE5vbi12b2ljZSB1c2UgY2FzZSBpcyBzdXNwZWN0ZWQgd2hlbiBub2lzZSBzdXBwcmVzc2lvbiBpcyBkaXNhYmxlZCBieSB0aGUgdXNlci5cbiAgICAgICAgLy8gV2hlbiByZWNvcmRpbmcgY29tcGxleCBhdWRpbywgaGlnaGVyIHF1YWxpdHkgaXMgcmVxdWlyZWQgdG8gYXZvaWQgYXVkaW8gYXJ0aWZhY3RzLlxuICAgICAgICAvLyBUaGlzIGlzIGEgcmVhbGx5IGFyYml0cmFyeSBkZWNpc2lvbiwgYnV0IGl0IGNhbiBiZSByZWZpbmVkL3JlcGxhY2VkIGF0IGFueSB0aW1lLlxuICAgICAgICByZXR1cm4gIU1lZGlhRGV2aWNlSGFuZGxlci5nZXRBdWRpb05vaXNlU3VwcHJlc3Npb24oKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIG1ha2VSZWNvcmRlcigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIHRoaXMucmVjb3JkZXJTdHJlYW0gPSBhd2FpdCBuYXZpZ2F0b3IubWVkaWFEZXZpY2VzLmdldFVzZXJNZWRpYSh7XG4gICAgICAgICAgICAgICAgYXVkaW86IHtcbiAgICAgICAgICAgICAgICAgICAgY2hhbm5lbENvdW50OiBDSEFOTkVMUyxcbiAgICAgICAgICAgICAgICAgICAgZGV2aWNlSWQ6IE1lZGlhRGV2aWNlSGFuZGxlci5nZXRBdWRpb0lucHV0KCksXG4gICAgICAgICAgICAgICAgICAgIGF1dG9HYWluQ29udHJvbDogeyBpZGVhbDogTWVkaWFEZXZpY2VIYW5kbGVyLmdldEF1ZGlvQXV0b0dhaW5Db250cm9sKCkgfSxcbiAgICAgICAgICAgICAgICAgICAgZWNob0NhbmNlbGxhdGlvbjogeyBpZGVhbDogTWVkaWFEZXZpY2VIYW5kbGVyLmdldEF1ZGlvRWNob0NhbmNlbGxhdGlvbigpIH0sXG4gICAgICAgICAgICAgICAgICAgIG5vaXNlU3VwcHJlc3Npb246IHsgaWRlYWw6IE1lZGlhRGV2aWNlSGFuZGxlci5nZXRBdWRpb05vaXNlU3VwcHJlc3Npb24oKSB9LFxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHRoaXMucmVjb3JkZXJDb250ZXh0ID0gY3JlYXRlQXVkaW9Db250ZXh0KHtcbiAgICAgICAgICAgICAgICAvLyBsYXRlbmN5SGludDogXCJpbnRlcmFjdGl2ZVwiLCAvLyB3ZSBkb24ndCB3YW50IGEgbGF0ZW5jeSBoaW50ICh0aGlzIGNhdXNlcyBkYXRhIHNtb290aGluZylcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgdGhpcy5yZWNvcmRlclNvdXJjZSA9IHRoaXMucmVjb3JkZXJDb250ZXh0LmNyZWF0ZU1lZGlhU3RyZWFtU291cmNlKHRoaXMucmVjb3JkZXJTdHJlYW0pO1xuXG4gICAgICAgICAgICAvLyBDb25uZWN0IG91ciBpbnB1dHMgYW5kIG91dHB1dHNcbiAgICAgICAgICAgIGlmICh0aGlzLnJlY29yZGVyQ29udGV4dC5hdWRpb1dvcmtsZXQpIHtcbiAgICAgICAgICAgICAgICAvLyBTZXQgdXAgb3VyIHdvcmtsZXQuIFdlIHVzZSB0aGlzIGZvciB0aW1pbmcgaW5mb3JtYXRpb24gYW5kIHdhdmVmb3JtIGFuYWx5c2lzOiB0aGVcbiAgICAgICAgICAgICAgICAvLyB3ZWIgYXVkaW8gQVBJIHByZWZlcnMgdGhpcyBiZSBkb25lIGFzeW5jIHRvIGF2b2lkIGhvbGRpbmcgdGhlIG1haW4gdGhyZWFkIHdpdGggbWF0aC5cbiAgICAgICAgICAgICAgICBhd2FpdCByZWNvcmRlcldvcmtsZXRGYWN0b3J5KHRoaXMucmVjb3JkZXJDb250ZXh0KTtcblxuICAgICAgICAgICAgICAgIHRoaXMucmVjb3JkZXJXb3JrbGV0ID0gbmV3IEF1ZGlvV29ya2xldE5vZGUodGhpcy5yZWNvcmRlckNvbnRleHQsIFdPUktMRVRfTkFNRSk7XG4gICAgICAgICAgICAgICAgdGhpcy5yZWNvcmRlclNvdXJjZS5jb25uZWN0KHRoaXMucmVjb3JkZXJXb3JrbGV0KTtcbiAgICAgICAgICAgICAgICB0aGlzLnJlY29yZGVyV29ya2xldC5jb25uZWN0KHRoaXMucmVjb3JkZXJDb250ZXh0LmRlc3RpbmF0aW9uKTtcblxuICAgICAgICAgICAgICAgIC8vIERldiBub3RlOiB3ZSBjYW4ndCB1c2UgYGFkZEV2ZW50TGlzdGVuZXJgIGZvciBzb21lIHJlYXNvbi4gSXQganVzdCBkb2Vzbid0IHdvcmsuXG4gICAgICAgICAgICAgICAgdGhpcy5yZWNvcmRlcldvcmtsZXQucG9ydC5vbm1lc3NhZ2UgPSAoZXYpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgc3dpdGNoIChldi5kYXRhW1wiZXZcIl0pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgUGF5bG9hZEV2ZW50LlRpbWVrZWVwOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMucHJvY2Vzc0F1ZGlvVXBkYXRlKGV2LmRhdGFbXCJ0aW1lU2Vjb25kc1wiXSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXNlIFBheWxvYWRFdmVudC5BbXBsaXR1ZGVNYXJrOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFNhbml0eSBjaGVjayB0byBtYWtlIHN1cmUgd2UncmUgYWRkaW5nIGFib3V0IG9uZSBzYW1wbGUgcGVyIHNlY29uZFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChldi5kYXRhW1wiZm9ySW5kZXhcIl0gPT09IHRoaXMuYW1wbGl0dWRlcy5sZW5ndGgpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5hbXBsaXR1ZGVzLnB1c2goZXYuZGF0YVtcImFtcGxpdHVkZVwiXSk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubGl2ZVdhdmVmb3JtLnB1c2hWYWx1ZShldi5kYXRhW1wiYW1wbGl0dWRlXCJdKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBTYWZhcmkgZmFsbGJhY2s6IHVzZSBhIHByb2Nlc3NvciBub2RlIGluc3RlYWQsIGJ1ZmZlcmVkIHRvIDEwMjQgYnl0ZXMgb2YgZGF0YVxuICAgICAgICAgICAgICAgIC8vIGxpa2UgdGhlIHdvcmtsZXQgaXMuXG4gICAgICAgICAgICAgICAgdGhpcy5yZWNvcmRlclByb2Nlc3NvciA9IHRoaXMucmVjb3JkZXJDb250ZXh0LmNyZWF0ZVNjcmlwdFByb2Nlc3NvcigxMDI0LCBDSEFOTkVMUywgQ0hBTk5FTFMpO1xuICAgICAgICAgICAgICAgIHRoaXMucmVjb3JkZXJTb3VyY2UuY29ubmVjdCh0aGlzLnJlY29yZGVyUHJvY2Vzc29yKTtcbiAgICAgICAgICAgICAgICB0aGlzLnJlY29yZGVyUHJvY2Vzc29yLmNvbm5lY3QodGhpcy5yZWNvcmRlckNvbnRleHQuZGVzdGluYXRpb24pO1xuICAgICAgICAgICAgICAgIHRoaXMucmVjb3JkZXJQcm9jZXNzb3IuYWRkRXZlbnRMaXN0ZW5lcihcImF1ZGlvcHJvY2Vzc1wiLCB0aGlzLm9uQXVkaW9Qcm9jZXNzKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3QgcmVjb3JkZXJPcHRpb25zID0gdGhpcy5zaG91bGRSZWNvcmRJbkhpZ2hRdWFsaXR5KClcbiAgICAgICAgICAgICAgICA/IGhpZ2hRdWFsaXR5UmVjb3JkZXJPcHRpb25zXG4gICAgICAgICAgICAgICAgOiB2b2ljZVJlY29yZGVyT3B0aW9ucztcbiAgICAgICAgICAgIGNvbnN0IHsgZW5jb2RlckFwcGxpY2F0aW9uLCBiaXRyYXRlIH0gPSByZWNvcmRlck9wdGlvbnM7XG5cbiAgICAgICAgICAgIHRoaXMucmVjb3JkZXIgPSBuZXcgUmVjb3JkZXIoe1xuICAgICAgICAgICAgICAgIGVuY29kZXJQYXRoLCAvLyBtYWdpYyBmcm9tIHdlYnBhY2tcbiAgICAgICAgICAgICAgICBlbmNvZGVyU2FtcGxlUmF0ZTogU0FNUExFX1JBVEUsXG4gICAgICAgICAgICAgICAgZW5jb2RlckFwcGxpY2F0aW9uOiBlbmNvZGVyQXBwbGljYXRpb24sXG4gICAgICAgICAgICAgICAgc3RyZWFtUGFnZXM6IHRydWUsIC8vIHRoaXMgc3BlZWRzIHVwIHRoZSBlbmNvZGluZyBwcm9jZXNzIGJ5IHVzaW5nIENQVSBvdmVyIHRpbWVcbiAgICAgICAgICAgICAgICBlbmNvZGVyRnJhbWVTaXplOiAyMCwgLy8gbXMsIGFyYml0cmFyeSBmcmFtZSBzaXplIHdlIHNlbmQgdG8gdGhlIGVuY29kZXJcbiAgICAgICAgICAgICAgICBudW1iZXJPZkNoYW5uZWxzOiBDSEFOTkVMUyxcbiAgICAgICAgICAgICAgICBzb3VyY2VOb2RlOiB0aGlzLnJlY29yZGVyU291cmNlLFxuICAgICAgICAgICAgICAgIGVuY29kZXJCaXRSYXRlOiBiaXRyYXRlLFxuXG4gICAgICAgICAgICAgICAgLy8gV2UgdXNlIGxvdyB2YWx1ZXMgZm9yIHRoZSBmb2xsb3dpbmcgdG8gZWFzZSBDUFUgdXNhZ2UgLSB0aGUgcmVzdWx0aW5nIHdhdmVmb3JtXG4gICAgICAgICAgICAgICAgLy8gaXMgaW5kaXN0aW5ndWlzaGFibGUgZm9yIGEgdm9pY2UgbWVzc2FnZS4gTm90ZSB0aGF0IHRoZSB1bmRlcmx5aW5nIGxpYnJhcnkgd2lsbFxuICAgICAgICAgICAgICAgIC8vIHBpY2sgZGVmYXVsdHMgd2hpY2ggcHJlZmVyIHRoZSBoaWdoZXN0IHBvc3NpYmxlIHF1YWxpdHksIENQVSBiZSBkYW1uZWQuXG4gICAgICAgICAgICAgICAgZW5jb2RlckNvbXBsZXhpdHk6IDMsIC8vIDAtMTAsIDEwIGlzIHNsb3cgYW5kIGhpZ2ggcXVhbGl0eS5cbiAgICAgICAgICAgICAgICByZXNhbXBsZVF1YWxpdHk6IDMsIC8vIDAtMTAsIDEwIGlzIHNsb3cgYW5kIGhpZ2ggcXVhbGl0eVxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIC8vIG5vdCB1c2luZyBFdmVudEVtaXR0ZXIgaGVyZSBiZWNhdXNlIGl0IGxlYWRzIHRvIGRldGFjaGVkIGJ1ZmZlcmVzXG4gICAgICAgICAgICB0aGlzLnJlY29yZGVyLm9uZGF0YWF2YWlsYWJsZSA9IChkYXRhOiBBcnJheUJ1ZmZlcikgPT4gdGhpcy5vbkRhdGFBdmFpbGFibGU/LihkYXRhKTtcbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgbG9nZ2VyLmVycm9yKFwiRXJyb3Igc3RhcnRpbmcgcmVjb3JkaW5nOiBcIiwgZSk7XG4gICAgICAgICAgICBpZiAoZSBpbnN0YW5jZW9mIERPTUV4Y2VwdGlvbikge1xuICAgICAgICAgICAgICAgIC8vIFVuaGVscGZ1bCBET01FeGNlcHRpb25zIGFyZSBjb21tb24gLSBwYXJzZSB0aGVtIHNhbmVseVxuICAgICAgICAgICAgICAgIGxvZ2dlci5lcnJvcihgJHtlLm5hbWV9ICgke2UuY29kZX0pOiAke2UubWVzc2FnZX1gKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gQ2xlYW4gdXAgYXMgYmVzdCBhcyBwb3NzaWJsZVxuICAgICAgICAgICAgaWYgKHRoaXMucmVjb3JkZXJTdHJlYW0pIHRoaXMucmVjb3JkZXJTdHJlYW0uZ2V0VHJhY2tzKCkuZm9yRWFjaCgodCkgPT4gdC5zdG9wKCkpO1xuICAgICAgICAgICAgaWYgKHRoaXMucmVjb3JkZXJTb3VyY2UpIHRoaXMucmVjb3JkZXJTb3VyY2UuZGlzY29ubmVjdCgpO1xuICAgICAgICAgICAgaWYgKHRoaXMucmVjb3JkZXIpIHRoaXMucmVjb3JkZXIuY2xvc2UoKTtcbiAgICAgICAgICAgIGlmICh0aGlzLnJlY29yZGVyQ29udGV4dCkge1xuICAgICAgICAgICAgICAgIC8vIG5vaW5zcGVjdGlvbiBFUzZNaXNzaW5nQXdhaXQgLSBub3QgaW1wb3J0YW50IHRoYXQgd2Ugd2FpdFxuICAgICAgICAgICAgICAgIHRoaXMucmVjb3JkZXJDb250ZXh0LmNsb3NlKCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHRocm93IGU7IC8vIHJldGhyb3cgc28gdXBzdHJlYW0gY2FuIGhhbmRsZSBpdFxuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIGdldCBsaXZlRGF0YSgpOiBTaW1wbGVPYnNlcnZhYmxlPElSZWNvcmRpbmdVcGRhdGU+IHtcbiAgICAgICAgaWYgKCF0aGlzLnJlY29yZGluZyB8fCAhdGhpcy5vYnNlcnZhYmxlKSB0aHJvdyBuZXcgRXJyb3IoXCJObyBvYnNlcnZhYmxlIHdoZW4gbm90IHJlY29yZGluZ1wiKTtcbiAgICAgICAgcmV0dXJuIHRoaXMub2JzZXJ2YWJsZTtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0IGlzU3VwcG9ydGVkKCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gISFSZWNvcmRlci5pc1JlY29yZGluZ1N1cHBvcnRlZCgpO1xuICAgIH1cblxuICAgIHByaXZhdGUgb25BdWRpb1Byb2Nlc3MgPSAoZXY6IEF1ZGlvUHJvY2Vzc2luZ0V2ZW50KTogdm9pZCA9PiB7XG4gICAgICAgIHRoaXMucHJvY2Vzc0F1ZGlvVXBkYXRlKGV2LnBsYXliYWNrVGltZSk7XG5cbiAgICAgICAgLy8gV2Ugc2tpcCB0aGUgZnVuY3Rpb25hbGl0eSBvZiB0aGUgd29ya2xldCByZWdhcmRpbmcgd2F2ZWZvcm0gY2FsY3VsYXRpb25zOiB3ZVxuICAgICAgICAvLyBzaG91bGQgZ2V0IHRoYXQgaW5mb3JtYXRpb24gcHJldHR5IHF1aWNrIGR1cmluZyB0aGUgcGxheWJhY2sgaW5mby5cbiAgICB9O1xuXG4gICAgcHJpdmF0ZSBwcm9jZXNzQXVkaW9VcGRhdGUgPSAodGltZVNlY29uZHM6IG51bWJlcik6IHZvaWQgPT4ge1xuICAgICAgICBpZiAoIXRoaXMucmVjb3JkaW5nKSByZXR1cm47XG5cbiAgICAgICAgdGhpcy5vYnNlcnZhYmxlIS51cGRhdGUoe1xuICAgICAgICAgICAgd2F2ZWZvcm06IHRoaXMubGl2ZVdhdmVmb3JtLnZhbHVlLm1hcCgodikgPT4gY2xhbXAodiwgMCwgMSkpLFxuICAgICAgICAgICAgdGltZVNlY29uZHM6IHRpbWVTZWNvbmRzLFxuICAgICAgICB9KTtcblxuICAgICAgICAvLyBOb3cgdGhhdCB3ZSd2ZSB1cGRhdGVkIHRoZSBkYXRhL3dhdmVmb3JtLCBsZXQncyBkbyBhIHRpbWUgY2hlY2suIFdlIGRvbid0IHdhbnQgdG9cbiAgICAgICAgLy8gZ28gaG9ycmlibHkgb3ZlciB0aGUgbGltaXQuIFdlIGFsc28gZW1pdCBhIHdhcm5pbmcgc3RhdGUgaWYgbmVlZGVkLlxuICAgICAgICAvL1xuICAgICAgICAvLyBXZSB1c2UgdGhlIHJlY29yZGVyJ3MgcGVyc3BlY3RpdmUgb2YgdGltZSB0byBtYWtlIHN1cmUgd2UgZG9uJ3QgY3V0IG9mZiB0aGUgbGFzdFxuICAgICAgICAvLyBmcmFtZSBvZiBhdWRpbywgb3RoZXJ3aXNlIHdlIGVuZCB1cCB3aXRoIGEgMTQ6NTkgY2xpcCAoODk5LjY4IHNlY29uZHMpLiBUaGlzIGV4dHJhXG4gICAgICAgIC8vIHNhZmV0eSBjYW4gYWxsb3cgdXMgdG8gb3ZlcnNob290IHRoZSB0YXJnZXQgYSBiaXQsIGJ1dCBhdCBsZWFzdCB3aGVuIHdlIHNheSAxNW1pblxuICAgICAgICAvLyBtYXhpbXVtIHdlIGFjdHVhbGx5IG1lYW4gaXQuXG4gICAgICAgIC8vXG4gICAgICAgIC8vIEluIHRlc3RpbmcsIHJlY29yZGVyIHRpbWUgYW5kIHdvcmtlciB0aW1lIGxhZyBieSBhYm91dCA0MDBtcywgd2hpY2ggaXMgcm91Z2hseSB0aGVcbiAgICAgICAgLy8gdGltZSBuZWVkZWQgdG8gZW5jb2RlIGEgc2FtcGxlL2ZyYW1lLlxuICAgICAgICAvL1xuXG4gICAgICAgIGlmICghdGhpcy50YXJnZXRNYXhMZW5ndGgpIHtcbiAgICAgICAgICAgIC8vIHNraXAgdGltZSBjaGVja3MgaWYgbWF4IGxlbmd0aCBoYXMgYmVlbiBkaXNhYmxlZFxuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3Qgc2Vjb25kc0xlZnQgPSBUQVJHRVRfTUFYX0xFTkdUSCAtIHRoaXMucmVjb3JkZXJTZWNvbmRzITtcbiAgICAgICAgaWYgKHNlY29uZHNMZWZ0IDwgMCkge1xuICAgICAgICAgICAgLy8gZ28gb3ZlciB0byBtYWtlIHN1cmUgd2UgZGVmaW5pdGVseSBjYXB0dXJlIHRoYXQgbGFzdCBmcmFtZVxuICAgICAgICAgICAgLy8gbm9pbnNwZWN0aW9uIEpTSWdub3JlZFByb21pc2VGcm9tQ2FsbCAtIHdlIGFyZW4ndCBjb25jZXJuZWQgd2l0aCBpdCBvdmVybGFwcGluZ1xuICAgICAgICAgICAgdGhpcy5zdG9wKCk7XG4gICAgICAgIH0gZWxzZSBpZiAoc2Vjb25kc0xlZnQgPD0gVEFSR0VUX1dBUk5fVElNRV9MRUZUKSB7XG4gICAgICAgICAgICBTaW5nbGVmbGlnaHQuZm9yKHRoaXMsIFwiZW5kaW5nX3Nvb25cIikuZG8oKCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuZW1pdChSZWNvcmRpbmdTdGF0ZS5FbmRpbmdTb29uLCB7IHNlY29uZHNMZWZ0IH0pO1xuICAgICAgICAgICAgICAgIHJldHVybiBTaW5nbGVmbGlnaHQuVm9pZDtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIHtAbGluayBodHRwczovL2dpdGh1Yi5jb20vY2hyaXMtcnVkbWluL29wdXMtcmVjb3JkZXIjaW5zdGFuY2UtZmllbGRzIHJlZiBmb3IgcmVjb3JkZXJTZWNvbmRzfVxuICAgICAqL1xuICAgIHB1YmxpYyBnZXQgcmVjb3JkZXJTZWNvbmRzKCk6IG51bWJlciB8IHVuZGVmaW5lZCB7XG4gICAgICAgIGlmICghdGhpcy5yZWNvcmRlcikgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgICAgcmV0dXJuIHRoaXMucmVjb3JkZXIuZW5jb2RlZFNhbXBsZVBvc2l0aW9uIC8gNDgwMDA7XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIHN0YXJ0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAodGhpcy5yZWNvcmRpbmcpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIlJlY29yZGluZyBhbHJlYWR5IGluIHByb2dyZXNzXCIpO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLm9ic2VydmFibGUpIHtcbiAgICAgICAgICAgIHRoaXMub2JzZXJ2YWJsZS5jbG9zZSgpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMub2JzZXJ2YWJsZSA9IG5ldyBTaW1wbGVPYnNlcnZhYmxlPElSZWNvcmRpbmdVcGRhdGU+KCk7XG4gICAgICAgIGF3YWl0IHRoaXMubWFrZVJlY29yZGVyKCk7XG4gICAgICAgIGF3YWl0IHRoaXMucmVjb3JkZXI/LnN0YXJ0KCk7XG4gICAgICAgIHRoaXMucmVjb3JkaW5nID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5lbWl0KFJlY29yZGluZ1N0YXRlLlN0YXJ0ZWQpO1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBzdG9wKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICByZXR1cm4gU2luZ2xlZmxpZ2h0LmZvcih0aGlzLCBcInN0b3BcIikuZG8oYXN5bmMgKCk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgICAgICAgICAgaWYgKCF0aGlzLnJlY29yZGluZykge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIk5vIHJlY29yZGluZyB0byBzdG9wXCIpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBEaXNjb25uZWN0IHRoZSBzb3VyY2UgZWFybHkgdG8gc3RhcnQgc2h1dHRpbmcgZG93biByZXNvdXJjZXNcbiAgICAgICAgICAgIGF3YWl0IHRoaXMucmVjb3JkZXIhLnN0b3AoKTsgLy8gc3RvcCBmaXJzdCB0byBmbHVzaCB0aGUgbGFzdCBmcmFtZVxuICAgICAgICAgICAgdGhpcy5yZWNvcmRlclNvdXJjZSEuZGlzY29ubmVjdCgpO1xuICAgICAgICAgICAgaWYgKHRoaXMucmVjb3JkZXJXb3JrbGV0KSB0aGlzLnJlY29yZGVyV29ya2xldC5kaXNjb25uZWN0KCk7XG4gICAgICAgICAgICBpZiAodGhpcy5yZWNvcmRlclByb2Nlc3Nvcikge1xuICAgICAgICAgICAgICAgIHRoaXMucmVjb3JkZXJQcm9jZXNzb3IuZGlzY29ubmVjdCgpO1xuICAgICAgICAgICAgICAgIHRoaXMucmVjb3JkZXJQcm9jZXNzb3IucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImF1ZGlvcHJvY2Vzc1wiLCB0aGlzLm9uQXVkaW9Qcm9jZXNzKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gY2xvc2UgdGhlIGNvbnRleHQgYWZ0ZXIgdGhlIHJlY29yZGVyIHNvIHRoZSByZWNvcmRlciBkb2Vzbid0IHRyeSB0b1xuICAgICAgICAgICAgLy8gY29ubmVjdCBhbnl0aGluZyB0byB0aGUgY29udGV4dCAodGhpcyB3b3VsZCBnZW5lcmF0ZSBhIHdhcm5pbmcpXG4gICAgICAgICAgICBhd2FpdCB0aGlzLnJlY29yZGVyQ29udGV4dCEuY2xvc2UoKTtcblxuICAgICAgICAgICAgLy8gTm93IHN0b3AgYWxsIHRoZSBtZWRpYSB0cmFja3Mgc28gd2UgY2FuIHJlbGVhc2UgdGhlbSBiYWNrIHRvIHRoZSB1c2VyL09TXG4gICAgICAgICAgICB0aGlzLnJlY29yZGVyU3RyZWFtIS5nZXRUcmFja3MoKS5mb3JFYWNoKCh0KSA9PiB0LnN0b3AoKSk7XG5cbiAgICAgICAgICAgIC8vIEZpbmFsbHkgZG8gb3VyIHBvc3QtcHJvY2Vzc2luZyBhbmQgY2xlYW4gdXBcbiAgICAgICAgICAgIHRoaXMucmVjb3JkaW5nID0gZmFsc2U7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLnJlY29yZGVyIS5jbG9zZSgpO1xuICAgICAgICAgICAgdGhpcy5lbWl0KFJlY29yZGluZ1N0YXRlLkVuZGVkKTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHVibGljIGRlc3Ryb3koKTogdm9pZCB7XG4gICAgICAgIC8vIG5vaW5zcGVjdGlvbiBKU0lnbm9yZWRQcm9taXNlRnJvbUNhbGwgLSBub3QgY29uY2VybmVkIGFib3V0IHN0b3AoKSBiZWluZyBjYWxsZWQgYXN5bmMgaGVyZVxuICAgICAgICB0aGlzLnN0b3AoKTtcbiAgICAgICAgdGhpcy5yZW1vdmVBbGxMaXN0ZW5lcnMoKTtcbiAgICAgICAgdGhpcy5vbkRhdGFBdmFpbGFibGUgPSB1bmRlZmluZWQ7XG4gICAgICAgIFNpbmdsZWZsaWdodC5mb3JnZXRBbGxGb3IodGhpcyk7XG4gICAgICAgIC8vIG5vaW5zcGVjdGlvbiBKU0lnbm9yZWRQcm9taXNlRnJvbUNhbGwgLSBub3QgY29uY2VybmVkIGFib3V0IGJlaW5nIGNhbGxlZCBhc3luYyBoZXJlXG4gICAgICAgIHRoaXMub2JzZXJ2YWJsZT8uY2xvc2UoKTtcbiAgICB9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBUUEsSUFBQUEsWUFBQSxHQUFBQyxzQkFBQSxDQUFBQyxPQUFBO0FBQ0EsSUFBQUMsaUJBQUEsR0FBQUYsc0JBQUEsQ0FBQUMsT0FBQTtBQUNBLElBQUFFLGdCQUFBLEdBQUFGLE9BQUE7QUFDQSxJQUFBRyxPQUFBLEdBQUFKLHNCQUFBLENBQUFDLE9BQUE7QUFDQSxJQUFBSSxPQUFBLEdBQUFKLE9BQUE7QUFFQSxJQUFBSyxtQkFBQSxHQUFBTixzQkFBQSxDQUFBQyxPQUFBO0FBRUEsSUFBQU0sYUFBQSxHQUFBTixPQUFBO0FBQ0EsSUFBQU8sT0FBQSxHQUFBUCxPQUFBO0FBQ0EsSUFBQVEsV0FBQSxHQUFBUixPQUFBO0FBQ0EsSUFBQVMsT0FBQSxHQUFBVCxPQUFBO0FBQ0EsSUFBQVUsa0JBQUEsR0FBQVYsT0FBQTtBQUNBLElBQUFXLFFBQUEsR0FBQVgsT0FBQTtBQUNBLElBQUFZLHVCQUFBLEdBQUFiLHNCQUFBLENBQUFDLE9BQUE7QUF0QkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBa0JBLE1BQU1hLFFBQVEsR0FBRyxDQUFDLENBQUMsQ0FBQztBQUNiLE1BQU1DLFdBQVcsR0FBQUMsT0FBQSxDQUFBRCxXQUFBLEdBQUcsS0FBSyxDQUFDLENBQUM7QUFDbEMsTUFBTUUsaUJBQWlCLEdBQUcsR0FBRyxDQUFDLENBQUM7QUFDL0IsTUFBTUMscUJBQXFCLEdBQUcsRUFBRSxDQUFDLENBQUM7O0FBRTNCLE1BQU1DLDBCQUEwQixHQUFBSCxPQUFBLENBQUFHLDBCQUFBLEdBQUcsRUFBRTtBQU9yQyxNQUFNQyxvQkFBcUMsR0FBQUosT0FBQSxDQUFBSSxvQkFBQSxHQUFHO0VBQ2pEQyxPQUFPLEVBQUUsS0FBSztFQUFFO0VBQ2hCQyxrQkFBa0Is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