diff --git a/submodules/MediaPlayer/Sources/ChunkMediaPlayer.swift b/submodules/MediaPlayer/Sources/ChunkMediaPlayer.swift index fa578dd6c5..056243ded9 100644 --- a/submodules/MediaPlayer/Sources/ChunkMediaPlayer.swift +++ b/submodules/MediaPlayer/Sources/ChunkMediaPlayer.swift @@ -332,7 +332,7 @@ private final class ChunkMediaPlayerContext { audioRendererContext.start() self.tick() - let tickTimer = SwiftSignalKit.Timer(timeout: 1.0 / 60.0, repeat: true, completion: { [weak self] in + let tickTimer = SwiftSignalKit.Timer(timeout: 1.0 / 25.0, repeat: true, completion: { [weak self] in self?.tick() }, queue: self.queue) self.tickTimer = tickTimer @@ -608,15 +608,31 @@ private final class ChunkMediaPlayerContext { } } - /*if validParts.isEmpty, let initialSeekTimestamp = self.initialSeekTimestamp { + if validParts.isEmpty, let initialSeekTimestamp = self.initialSeekTimestamp { for part in self.partsState.parts { if initialSeekTimestamp >= part.startTime - 0.2 && initialSeekTimestamp < part.endTime { self.initialSeekTimestamp = nil - self.seek(timestamp: part.startTime + 0.05) + + self.videoRenderer.flush() + + if let audioRenderer = self.audioRenderer { + self.isSeeking = true + let queue = self.queue + audioRenderer.renderer.flushBuffers(at: CMTime(seconds: part.startTime + 0.1, preferredTimescale: 44100), completion: { [weak self] in + queue.async { + guard let self else { + return + } + self.isSeeking = false + self.tick() + } + }) + } + return } } - }*/ + } self.loadedState.partStates.removeAll(where: { partState in if !validParts.contains(where: { $0.id == partState.part.id }) { diff --git a/submodules/TelegramUniversalVideoContent/HlsBundle/index.bundle.js b/submodules/TelegramUniversalVideoContent/HlsBundle/index.bundle.js index d3214583ce..caa7b1a42c 100644 --- a/submodules/TelegramUniversalVideoContent/HlsBundle/index.bundle.js +++ b/submodules/TelegramUniversalVideoContent/HlsBundle/index.bundle.js @@ -1,29523 +1 @@ -"use strict"; -(self["webpackChunkmyhls"] = self["webpackChunkmyhls"] || []).push([["index"],{ - -/***/ "./src/MediaSourceStub.js": -/*!********************************!*\ - !*** ./src/MediaSourceStub.js ***! - \********************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ MediaSourceStub: () => (/* binding */ MediaSourceStub), -/* harmony export */ SourceBufferListStub: () => (/* binding */ SourceBufferListStub), -/* harmony export */ SourceBufferStub: () => (/* binding */ SourceBufferStub) -/* harmony export */ }); -/* harmony import */ var _TimeRangesStub_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./TimeRangesStub.js */ "./src/TimeRangesStub.js"); - - -function bytesToBase64(bytes) { - const binString = Array.from(bytes, (byte) => - String.fromCodePoint(byte), - ).join(""); - return btoa(binString); -} - -class SourceBufferListStub extends EventTarget { - constructor() { - super(); - this._buffers = []; - } - - _add(buffer) { - this._buffers.push(buffer); - this.dispatchEvent(new Event('addsourcebuffer')); - } - - _remove(buffer) { - const index = this._buffers.indexOf(buffer); - if (index === -1) { - return false; - } - this._buffers.splice(index, 1); - this.dispatchEvent(new Event('removesourcebuffer')); - return true; - } - - get length() { - return this._buffers.length; - } - - item(index) { - return this._buffers[index]; - } - - [Symbol.iterator]() { - return this._buffers[Symbol.iterator](); - } -} - -class SourceBufferStub extends EventTarget { - constructor(mediaSource, mimeType) { - super(); - this.mediaSource = mediaSource; - this.mimeType = mimeType; - this.updating = false; - this.buffered = new _TimeRangesStub_js__WEBPACK_IMPORTED_MODULE_0__.TimeRangesStub(); - this.timestampOffset = 0; - this.appendWindowStart = 0; - this.appendWindowEnd = Infinity; - - this.bridgeId = window.nextInternalId; - window.nextInternalId += 1; - window.bridgeObjectMap[this.bridgeId] = this; - - window.bridgeInvokeAsync(this.bridgeId, "SourceBuffer", "constructor", { - "mimeType": mimeType - }); - } - - appendBuffer(data) { - if (this.updating) { - throw new DOMException('SourceBuffer is updating', 'InvalidStateError'); - } - this.updating = true; - this.dispatchEvent(new Event('updatestart')); - - window.bridgeInvokeAsync(this.bridgeId, "SourceBuffer", "appendBuffer", { - "data": bytesToBase64(data) - }).then((result) => { - const updatedRanges = result["ranges"]; - var ranges = []; - for (var i = 0; i < updatedRanges.length; i += 2) { - ranges.push({ - start: updatedRanges[i], - end: updatedRanges[i + 1] - }); - } - this.buffered._ranges = ranges; - - this.mediaSource._reopen(); - - this.updating = false; - this.dispatchEvent(new Event('update')); - this.dispatchEvent(new Event('updateend')); - }); - } - - abort() { - if (this.updating) { - this.updating = false; - this.dispatchEvent(new Event('abort')); - - window.bridgeInvokeAsync(this.bridgeId, "SourceBuffer", "abort", {}).then((result) => { - }); - } - } - - remove(start, end) { - if (this.updating) { - throw new DOMException('SourceBuffer is updating', 'InvalidStateError'); - } - this.updating = true; - this.dispatchEvent(new Event('updatestart')); - - window.bridgeInvokeAsync(this.bridgeId, "SourceBuffer", "remove", { - "start": start, - "end": end - }).then((result) => { - const updatedRanges = result["ranges"]; - var ranges = []; - for (var i = 0; i < updatedRanges.length; i += 2) { - ranges.push({ - start: updatedRanges[i], - end: updatedRanges[i + 1] - }); - } - this.buffered._ranges = ranges; - - this.mediaSource._reopen(); - - this.updating = false; - this.dispatchEvent(new Event('update')); - this.dispatchEvent(new Event('updateend')); - }); - } -} - -class MediaSourceStub extends EventTarget { - constructor() { - super(); - - this.internalId = window.nextInternalId; - window.nextInternalId += 1; - - this.bridgeId = window.nextInternalId; - window.nextInternalId += 1; - window.bridgeObjectMap[this.bridgeId] = this; - - this.sourceBuffers = new SourceBufferListStub(); - this.activeSourceBuffers = new SourceBufferListStub(); - this.readyState = 'closed'; - this._duration = NaN; - - window.bridgeInvokeAsync(this.bridgeId, "MediaSource", "constructor", { - }); - - // Simulate asynchronous opening of MediaSource - setTimeout(() => { - this.readyState = 'open'; - this.dispatchEvent(new Event('sourceopen')); - }, 0); - } - - static isTypeSupported(mimeType) { - // Assume all MIME types are supported in this stub - return true; - } - - addSourceBuffer(mimeType) { - if (this.readyState !== 'open') { - throw new DOMException('MediaSource is not open', 'InvalidStateError'); - } - const sourceBuffer = new SourceBufferStub(this, mimeType); - this.sourceBuffers._add(sourceBuffer); - this.activeSourceBuffers._add(sourceBuffer); - return sourceBuffer; - } - - removeSourceBuffer(sourceBuffer) { - if (!this.sourceBuffers._remove(sourceBuffer)) { - throw new DOMException('SourceBuffer not found', 'NotFoundError'); - } - this.activeSourceBuffers._remove(sourceBuffer); - } - - endOfStream(error) { - if (this.readyState !== 'open') { - throw new DOMException('MediaSource is not open', 'InvalidStateError'); - } - this.readyState = 'ended'; - this.dispatchEvent(new Event('sourceended')); - } - - _reopen() { - if (this.readyState !== 'open') { - this.readyState = 'open'; - this.dispatchEvent(new Event('sourceopen')); - } - } - - set duration(value) { - if (this.readyState === 'closed') { - throw new DOMException('MediaSource is closed', 'InvalidStateError'); - } - this._duration = value; - - window.bridgeInvokeAsync(this.bridgeId, "MediaSource", "setDuration", { - "duration": value - }).then((result) => { - }) - } - - get duration() { - return this._duration; - } -} - - -/***/ }), - -/***/ "./src/TextTrackStub.js": -/*!******************************!*\ - !*** ./src/TextTrackStub.js ***! - \******************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ TextTrackCueListStub: () => (/* binding */ TextTrackCueListStub), -/* harmony export */ TextTrackListStub: () => (/* binding */ TextTrackListStub), -/* harmony export */ TextTrackStub: () => (/* binding */ TextTrackStub) -/* harmony export */ }); - -class TextTrackStub extends EventTarget { - constructor(kind = '', label = '', language = '') { - super(); - this.kind = kind; - this.label = label; - this.language = language; - this.mode = 'disabled'; // 'disabled', 'hidden', or 'showing' - this.cues = new TextTrackCueListStub(); - this.activeCues = new TextTrackCueListStub(); - } - - addCue(cue) { - this.cues._add(cue); - } - - removeCue(cue) { - this.cues._remove(cue); - } -} - -class TextTrackCueListStub { - constructor() { - this._cues = []; - } - - get length() { - return this._cues.length; - } - - item(index) { - return this._cues[index]; - } - - getCueById(id) { - return this._cues.find(cue => cue.id === id) || null; - } - - _add(cue) { - this._cues.push(cue); - } - - _remove(cue) { - const index = this._cues.indexOf(cue); - if (index !== -1) { - this._cues.splice(index, 1); - } - } - - [Symbol.iterator]() { - return this._cues[Symbol.iterator](); - } -} - -class TextTrackListStub extends EventTarget { - constructor() { - super(); - this._tracks = []; - } - - get length() { - return this._tracks.length; - } - - item(index) { - return this._tracks[index]; - } - - _add(track) { - this._tracks.push(track); - this.dispatchEvent(new Event('addtrack')); - } - - _remove(track) { - const index = this._tracks.indexOf(track); - if (index !== -1) { - this._tracks.splice(index, 1); - this.dispatchEvent(new Event('removetrack')); - } - } - - [Symbol.iterator]() { - return this._tracks[Symbol.iterator](); - } -} - - -/***/ }), - -/***/ "./src/TimeRangesStub.js": -/*!*******************************!*\ - !*** ./src/TimeRangesStub.js ***! - \*******************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ TimeRangesStub: () => (/* binding */ TimeRangesStub) -/* harmony export */ }); - -class TimeRangesStub { - constructor() { - this._ranges = []; - } - - get length() { - return this._ranges.length; - } - - start(index) { - if (index < 0 || index >= this._ranges.length) { - throw new DOMException('Invalid index', 'IndexSizeError'); - } - return this._ranges[index].start; - } - - end(index) { - if (index < 0 || index >= this._ranges.length) { - throw new DOMException('Invalid index', 'IndexSizeError'); - } - return this._ranges[index].end; - } - - // Helper method to add a range - _addRange(start, end) { - this._ranges.push({ start, end }); - this._normalizeRanges(); - } - - // Helper method to remove ranges that overlap with a given range - _removeRange(start, end) { - let updatedRanges = []; - for (let range of this._ranges) { - if (range.end <= start || range.start >= end) { - // No overlap, keep the range as is - updatedRanges.push(range); - } else if (range.start < start && range.end > end) { - // The range fully covers the removal range, split into two ranges - updatedRanges.push({ start: range.start, end: start }); - updatedRanges.push({ start: end, end: range.end }); - } else if (range.start >= start && range.end <= end) { - // The range is entirely within the removal range, remove it - // Do not add to updatedRanges - } else if (range.start < start && range.end > start && range.end <= end) { - // The range overlaps with the removal range on the left - updatedRanges.push({ start: range.start, end: start }); - } else if (range.start >= start && range.start < end && range.end > end) { - // The range overlaps with the removal range on the right - updatedRanges.push({ start: end, end: range.end }); - } - } - this._ranges = updatedRanges; - } - - // Normalize and merge overlapping ranges - _normalizeRanges() { - this._ranges.sort((a, b) => a.start - b.start); - let normalized = []; - for (let range of this._ranges) { - if (normalized.length === 0) { - normalized.push(range); - } else { - let last = normalized[normalized.length - 1]; - if (range.start <= last.end) { - last.end = Math.max(last.end, range.end); - } else { - normalized.push(range); - } - } - } - this._ranges = normalized; - } -} - -/***/ }), - -/***/ "./src/VideoElementStub.js": -/*!*********************************!*\ - !*** ./src/VideoElementStub.js ***! - \*********************************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ VideoElementStub: () => (/* binding */ VideoElementStub) -/* harmony export */ }); -/* harmony import */ var _TimeRangesStub_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./TimeRangesStub.js */ "./src/TimeRangesStub.js"); -/* harmony import */ var _TextTrackStub_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./TextTrackStub.js */ "./src/TextTrackStub.js"); - - - -class VideoElementStub extends EventTarget { - constructor() { - super(); - - this.bridgeId = window.nextInternalId; - window.nextInternalId += 1; - window.bridgeObjectMap[this.bridgeId] = this; - - this._currentTime = 0.0; - this.duration = NaN; - this.paused = true; - this.playbackRate = 1.0; - this.volume = 1.0; - this.muted = false; - this.readyState = 0; - this.networkState = 0; - this.buffered = new _TimeRangesStub_js__WEBPACK_IMPORTED_MODULE_0__.TimeRangesStub(); - this.seeking = false; - this.loop = false; - this.autoplay = false; - this.controls = false; - this.error = null; - this.src = ''; - this.videoWidth = 0; - this.videoHeight = 0; - this.textTracks = new _TextTrackStub_js__WEBPACK_IMPORTED_MODULE_1__.TextTrackListStub(); - this.isWaiting = false; - - window.bridgeInvokeAsync(this.bridgeId, "VideoElement", "constructor", { - }); - - setTimeout(() => { - this.readyState = 4; // HAVE_ENOUGH_DATA - this.dispatchEvent(new Event('loadedmetadata')); - this.dispatchEvent(new Event('loadeddata')); - this.dispatchEvent(new Event('canplay')); - this.dispatchEvent(new Event('canplaythrough')); - }, 0); - } - - get currentTime() { - return this._currentTime; - } - - set currentTime(value) { - if (this._currentTime != value) { - this._currentTime = value; - - this.dispatchEvent(new Event('seeking')); - - window.bridgeInvokeAsync(this.bridgeId, "VideoElement", "setCurrentTime", { - "currentTime": value - }).then((result) => { - this.dispatchEvent(new Event('seeked')); - }) - } - } - - bridgeUpdateBuffered(value) { - const updatedRanges = value; - var ranges = []; - for (var i = 0; i < updatedRanges.length; i += 2) { - ranges.push({ - start: updatedRanges[i], - end: updatedRanges[i + 1] - }); - } - this.buffered._ranges = ranges; - } - - bridgeUpdateStatus(dict) { - var paused = !dict["isPlaying"]; - var isWaiting = dict["isWaiting"]; - var currentTime = dict["currentTime"]; - - if (this.paused != paused) { - this.paused = paused; - - if (paused) { - this.dispatchEvent(new Event('pause')); - } else { - this.dispatchEvent(new Event('play')); - this.dispatchEvent(new Event('playing')); - } - } - - if (this.isWaiting != isWaiting) { - this.isWaiting = isWaiting; - if (isWaiting) { - this.dispatchEvent(new Event('waiting')); - } - } - - if (this._currentTime != currentTime) { - this._currentTime = currentTime; - this.dispatchEvent(new Event('timeupdate')); - } - } - - play() { - if (this.paused) { - return window.bridgeInvokeAsync(this.bridgeId, "VideoElement", "play", { - }).then((result) => { - this.dispatchEvent(new Event('play')); - this.dispatchEvent(new Event('playing')); - }) - } else { - return Promise.resolve(); - } - } - - pause() { - if (!this.paused) { - this.paused = true; - this.dispatchEvent(new Event('pause')); - - return window.bridgeInvokeAsync(this.bridgeId, "VideoElement", "pause", { - }).then((result) => { - }) - } - } - - canPlayType(type) { - return 'probably'; - } - - _getMedia() { - return window.mediaSourceMap[this.src]; - } - - /*_simulateTimeUpdate() { - if (this._isPlaying) { - // Simulate time progression - setTimeout(() => { - var bufferedEnd = 0.0; - - const media = this._getMedia(); - if (media) { - if (media.sourceBuffers.length != 0) { - this.buffered = media.sourceBuffers._buffers[0].buffered; - bufferedEnd = this.buffered.length == 0 ? 0 : this.buffered.end(this.buffered.length - 1); - } - } - - // Consume buffered data - if (this.currentTime < bufferedEnd) { - // Advance currentTime - this._currentTime += 0.1 * this.playbackRate; // Increment currentTime - this.dispatchEvent(new Event('timeupdate')); - - // Continue simulation - this._simulateTimeUpdate(); - } else { - console.log("Buffer underrun"); - // Buffer underrun - this._isPlaying = false; - this.paused = true; - this.dispatchEvent(new Event('waiting')); - // The player should react by buffering more data - } - }, 100); - } - }*/ - - addTextTrack(kind, label, language) { - const textTrack = new _TextTrackStub_js__WEBPACK_IMPORTED_MODULE_1__.TextTrackStub(kind, label, language); - this.textTracks._add(textTrack); - return textTrack; - } -} - - -/***/ }), - -/***/ "./src/index.js": -/*!**********************!*\ - !*** ./src/index.js ***! - \**********************/ -/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { - -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ bridgeInvokeCallback: () => (/* binding */ bridgeInvokeCallback), -/* harmony export */ playerInitialize: () => (/* binding */ playerInitialize), -/* harmony export */ playerLoad: () => (/* binding */ playerLoad), -/* harmony export */ playerPause: () => (/* binding */ playerPause), -/* harmony export */ playerPlay: () => (/* binding */ playerPlay), -/* harmony export */ playerSeek: () => (/* binding */ playerSeek), -/* harmony export */ playerSetBaseRate: () => (/* binding */ playerSetBaseRate), -/* harmony export */ playerSetIsMuted: () => (/* binding */ playerSetIsMuted), -/* harmony export */ playerSetLevel: () => (/* binding */ playerSetLevel) -/* harmony export */ }); -/* harmony import */ var hls_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! hls.js */ "./node_modules/hls.js/dist/hls.mjs"); -/* harmony import */ var _VideoElementStub_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./VideoElementStub.js */ "./src/VideoElementStub.js"); -/* harmony import */ var _MediaSourceStub_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./MediaSourceStub.js */ "./src/MediaSourceStub.js"); - - - - -window.bridgeObjectMap = {}; -window.bridgeCallbackMap = {}; - -function bridgeInvokeAsync(bridgeId, className, methodName, params) { - var promiseResolve; - var promiseReject; - var result = new Promise(function(resolve, reject) { - promiseResolve = resolve; - promiseReject = reject; - }); - const callbackId = window.nextInternalId; - window.nextInternalId += 1; - window.bridgeCallbackMap[callbackId] = promiseResolve; - - if (window.webkit.messageHandlers) { - window.webkit.messageHandlers.performAction.postMessage({ - 'event': 'bridgeInvoke', - 'data': { - 'bridgeId': bridgeId, - 'className': className, - 'methodName': methodName, - 'params': params, - 'callbackId': callbackId - } - }); - } - - return result; -} -window.bridgeInvokeAsync = bridgeInvokeAsync - -function bridgeInvokeCallback(callbackId, result) { - const callback = window.bridgeCallbackMap[callbackId]; - if (callback) { - callback(result); - } -} - -var useStubs = true; - -window.nextInternalId = 0; -window.mediaSourceMap = {}; - -// Replace the global MediaSource with our stub -if (useStubs && typeof window !== 'undefined') { - window.MediaSource = _MediaSourceStub_js__WEBPACK_IMPORTED_MODULE_2__.MediaSourceStub; - window.ManagedMediaSource = _MediaSourceStub_js__WEBPACK_IMPORTED_MODULE_2__.MediaSourceStub; - window.SourceBuffer = _MediaSourceStub_js__WEBPACK_IMPORTED_MODULE_2__.SourceBufferStub; - URL.createObjectURL = function(ms) { - const url = "blob:mock-media-source:" + ms.internalId; - window.mediaSourceMap[url] = ms; - return url; - }; -} - - -function postPlayerEvent(eventName, eventData) { - if (window.webkit && window.webkit.messageHandlers) { - window.webkit.messageHandlers.performAction.postMessage({'event': eventName, 'data': eventData}); - } -}; - -var video; -var hls; - -var isManifestParsed = false; -var isFirstFrameReady = false; - -var currentTimeUpdateTimeout = null; - -function playerInitialize(params) { - video.muted = false; - - video.addEventListener('loadeddata', (event) => { - if (!isFirstFrameReady) { - isFirstFrameReady = true; - refreshPlayerStatus(); - } - }); - video.addEventListener("playing", function() { - refreshPlayerStatus(); - }); - video.addEventListener("pause", function() { - refreshPlayerStatus(); - }); - video.addEventListener("seeking", function() { - refreshPlayerStatus(); - }); - video.addEventListener("waiting", function() { - refreshPlayerStatus(); - }); - - hls = new hls_js__WEBPACK_IMPORTED_MODULE_0__["default"]({ - startLevel: 0, - testBandwidth: false, - debug: params['debug'] || true, - autoStartLoad: false, - backBufferLength: 30, - maxBufferLength: 60, - maxMaxBufferLength: 60, - maxFragLookUpTolerance: 0.001, - nudgeMaxRetry: 10000 - }); - hls.on(hls_js__WEBPACK_IMPORTED_MODULE_0__["default"].Events.MANIFEST_PARSED, function() { - isManifestParsed = true; - refreshPlayerStatus(); - }); - - hls.on(hls_js__WEBPACK_IMPORTED_MODULE_0__["default"].Events.LEVEL_SWITCHED, function() { - refreshPlayerStatus(); - }); - hls.on(hls_js__WEBPACK_IMPORTED_MODULE_0__["default"].Events.LEVELS_UPDATED, function() { - refreshPlayerStatus(); - }); - - hls.loadSource('master.m3u8'); - hls.attachMedia(video); -} - -function playerLoad(initialLevelIndex) { - hls.startLevel = initialLevelIndex; - hls.startLoad(-1, false); -} - -function playerPlay() { - video.play(); -} - -function playerPause() { - video.pause(); -} - -function playerSetBaseRate(value) { - video.playbackRate = value; -} - -function playerSetLevel(level) { - if (level >= 0) { - hls.currentLevel = level; - } else { - hls.currentLevel = -1; - } -} - -function playerSeek(value) { - video.currentTime = value; -} - -function playerSetIsMuted(value) { - video.muted = value; -} - -function getLevels() { - var levels = []; - for (var i = 0; i < hls.levels.length; i++) { - var level = hls.levels[i]; - levels.push({ - 'index': i, - 'bitrate': level.bitrate || 0, - 'width': level.width || 0, - 'height': level.height || 0 - }); - } - return levels; -} - -function refreshPlayerStatus() { - var isPlaying = false; - if (!video.paused && !video.ended && video.readyState > 2) { - isPlaying = true; - } - - postPlayerEvent('playerStatus', { - 'isReady': isManifestParsed, - 'isFirstFrameReady': isFirstFrameReady, - 'isPlaying': !video.paused, - 'rate': isPlaying ? video.playbackRate : 0.0, - 'defaultRate': video.playbackRate, - 'levels': getLevels(), - 'currentLevel': hls.currentLevel - }); - - refreshPlayerCurrentTime(); - - if (isPlaying) { - if (currentTimeUpdateTimeout == null) { - currentTimeUpdateTimeout = setTimeout(() => { - refreshPlayerCurrentTime(); - }, 200); - } - } else { - if(currentTimeUpdateTimeout != null){ - clearTimeout(currentTimeUpdateTimeout); - currentTimeUpdateTimeout = null; - } - } -} - -function refreshPlayerCurrentTime() { - postPlayerEvent('playerCurrentTime', { - 'value': video.currentTime - }); - currentTimeUpdateTimeout = setTimeout(() => { - refreshPlayerCurrentTime() - }, 200); -} - -window.onload = () => { - if (useStubs) { - video = new _VideoElementStub_js__WEBPACK_IMPORTED_MODULE_1__.VideoElementStub(); - } else { - video = document.createElement('video'); - video.playsInline = true; - video.controls = true; - document.body.appendChild(video); - } - - postPlayerEvent('windowOnLoad', { - }); -}; - -window.playerInitialize = playerInitialize; -window.playerLoad = playerLoad; -window.playerPlay = playerPlay; -window.playerPause = playerPause; -window.playerSetBaseRate = playerSetBaseRate; -window.playerSetLevel = playerSetLevel; -window.playerSeek = playerSeek; -window.playerSetIsMuted = playerSetIsMuted; -window.bridgeInvokeCallback = bridgeInvokeCallback; - -/***/ }), - -/***/ "./node_modules/hls.js/dist/hls.mjs": -/*!******************************************!*\ - !*** ./node_modules/hls.js/dist/hls.mjs ***! - \******************************************/ -/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { - -__webpack_require__.r(__webpack_exports__); -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ AbrController: () => (/* binding */ AbrController), -/* harmony export */ AttrList: () => (/* binding */ AttrList), -/* harmony export */ AudioStreamController: () => (/* binding */ AudioStreamController), -/* harmony export */ AudioTrackController: () => (/* binding */ AudioTrackController), -/* harmony export */ BasePlaylistController: () => (/* binding */ BasePlaylistController), -/* harmony export */ BaseSegment: () => (/* binding */ BaseSegment), -/* harmony export */ BaseStreamController: () => (/* binding */ BaseStreamController), -/* harmony export */ BufferController: () => (/* binding */ BufferController), -/* harmony export */ CMCDController: () => (/* binding */ CMCDController), -/* harmony export */ CapLevelController: () => (/* binding */ CapLevelController), -/* harmony export */ ChunkMetadata: () => (/* binding */ ChunkMetadata), -/* harmony export */ ContentSteeringController: () => (/* binding */ ContentSteeringController), -/* harmony export */ DateRange: () => (/* binding */ DateRange), -/* harmony export */ EMEController: () => (/* binding */ EMEController), -/* harmony export */ ErrorActionFlags: () => (/* binding */ ErrorActionFlags), -/* harmony export */ ErrorController: () => (/* binding */ ErrorController), -/* harmony export */ ErrorDetails: () => (/* binding */ ErrorDetails), -/* harmony export */ ErrorTypes: () => (/* binding */ ErrorTypes), -/* harmony export */ Events: () => (/* binding */ Events), -/* harmony export */ FPSController: () => (/* binding */ FPSController), -/* harmony export */ Fragment: () => (/* binding */ Fragment), -/* harmony export */ Hls: () => (/* binding */ Hls), -/* harmony export */ HlsSkip: () => (/* binding */ HlsSkip), -/* harmony export */ HlsUrlParameters: () => (/* binding */ HlsUrlParameters), -/* harmony export */ KeySystemFormats: () => (/* binding */ KeySystemFormats), -/* harmony export */ KeySystems: () => (/* binding */ KeySystems), -/* harmony export */ Level: () => (/* binding */ Level), -/* harmony export */ LevelDetails: () => (/* binding */ LevelDetails), -/* harmony export */ LevelKey: () => (/* binding */ LevelKey), -/* harmony export */ LoadStats: () => (/* binding */ LoadStats), -/* harmony export */ MetadataSchema: () => (/* binding */ MetadataSchema), -/* harmony export */ NetworkErrorAction: () => (/* binding */ NetworkErrorAction), -/* harmony export */ Part: () => (/* binding */ Part), -/* harmony export */ PlaylistLevelType: () => (/* binding */ PlaylistLevelType), -/* harmony export */ SubtitleStreamController: () => (/* binding */ SubtitleStreamController), -/* harmony export */ SubtitleTrackController: () => (/* binding */ SubtitleTrackController), -/* harmony export */ TimelineController: () => (/* binding */ TimelineController), -/* harmony export */ "default": () => (/* binding */ Hls), -/* harmony export */ getMediaSource: () => (/* binding */ getMediaSource), -/* harmony export */ isMSESupported: () => (/* binding */ isMSESupported), -/* harmony export */ isSupported: () => (/* binding */ isSupported) -/* harmony export */ }); -function getDefaultExportFromCjs (x) { - return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; -} - -var urlToolkit = {exports: {}}; - -(function (module, exports) { - // see https://tools.ietf.org/html/rfc1808 - - (function (root) { - var URL_REGEX = - /^(?=((?:[a-zA-Z0-9+\-.]+:)?))\1(?=((?:\/\/[^\/?#]*)?))\2(?=((?:(?:[^?#\/]*\/)*[^;?#\/]*)?))\3((?:;[^?#]*)?)(\?[^#]*)?(#[^]*)?$/; - var FIRST_SEGMENT_REGEX = /^(?=([^\/?#]*))\1([^]*)$/; - var SLASH_DOT_REGEX = /(?:\/|^)\.(?=\/)/g; - var SLASH_DOT_DOT_REGEX = /(?:\/|^)\.\.\/(?!\.\.\/)[^\/]*(?=\/)/g; - - var URLToolkit = { - // If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or // - // E.g - // With opts.alwaysNormalize = false (default, spec compliant) - // http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g - // With opts.alwaysNormalize = true (not spec compliant) - // http://a.com/b/cd + /e/f/../g => http://a.com/e/g - buildAbsoluteURL: function (baseURL, relativeURL, opts) { - opts = opts || {}; - // remove any remaining space and CRLF - baseURL = baseURL.trim(); - relativeURL = relativeURL.trim(); - if (!relativeURL) { - // 2a) If the embedded URL is entirely empty, it inherits the - // entire base URL (i.e., is set equal to the base URL) - // and we are done. - if (!opts.alwaysNormalize) { - return baseURL; - } - var basePartsForNormalise = URLToolkit.parseURL(baseURL); - if (!basePartsForNormalise) { - throw new Error('Error trying to parse base URL.'); - } - basePartsForNormalise.path = URLToolkit.normalizePath( - basePartsForNormalise.path - ); - return URLToolkit.buildURLFromParts(basePartsForNormalise); - } - var relativeParts = URLToolkit.parseURL(relativeURL); - if (!relativeParts) { - throw new Error('Error trying to parse relative URL.'); - } - if (relativeParts.scheme) { - // 2b) If the embedded URL starts with a scheme name, it is - // interpreted as an absolute URL and we are done. - if (!opts.alwaysNormalize) { - return relativeURL; - } - relativeParts.path = URLToolkit.normalizePath(relativeParts.path); - return URLToolkit.buildURLFromParts(relativeParts); - } - var baseParts = URLToolkit.parseURL(baseURL); - if (!baseParts) { - throw new Error('Error trying to parse base URL.'); - } - if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') { - // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc - // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a' - var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path); - baseParts.netLoc = pathParts[1]; - baseParts.path = pathParts[2]; - } - if (baseParts.netLoc && !baseParts.path) { - baseParts.path = '/'; - } - var builtParts = { - // 2c) Otherwise, the embedded URL inherits the scheme of - // the base URL. - scheme: baseParts.scheme, - netLoc: relativeParts.netLoc, - path: null, - params: relativeParts.params, - query: relativeParts.query, - fragment: relativeParts.fragment, - }; - if (!relativeParts.netLoc) { - // 3) If the embedded URL's is non-empty, we skip to - // Step 7. Otherwise, the embedded URL inherits the - // (if any) of the base URL. - builtParts.netLoc = baseParts.netLoc; - // 4) If the embedded URL path is preceded by a slash "/", the - // path is not relative and we skip to Step 7. - if (relativeParts.path[0] !== '/') { - if (!relativeParts.path) { - // 5) If the embedded URL path is empty (and not preceded by a - // slash), then the embedded URL inherits the base URL path - builtParts.path = baseParts.path; - // 5a) if the embedded URL's is non-empty, we skip to - // step 7; otherwise, it inherits the of the base - // URL (if any) and - if (!relativeParts.params) { - builtParts.params = baseParts.params; - // 5b) if the embedded URL's is non-empty, we skip to - // step 7; otherwise, it inherits the of the base - // URL (if any) and we skip to step 7. - if (!relativeParts.query) { - builtParts.query = baseParts.query; - } - } - } else { - // 6) The last segment of the base URL's path (anything - // following the rightmost slash "/", or the entire path if no - // slash is present) is removed and the embedded URL's path is - // appended in its place. - var baseURLPath = baseParts.path; - var newPath = - baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) + - relativeParts.path; - builtParts.path = URLToolkit.normalizePath(newPath); - } - } - } - if (builtParts.path === null) { - builtParts.path = opts.alwaysNormalize - ? URLToolkit.normalizePath(relativeParts.path) - : relativeParts.path; - } - return URLToolkit.buildURLFromParts(builtParts); - }, - parseURL: function (url) { - var parts = URL_REGEX.exec(url); - if (!parts) { - return null; - } - return { - scheme: parts[1] || '', - netLoc: parts[2] || '', - path: parts[3] || '', - params: parts[4] || '', - query: parts[5] || '', - fragment: parts[6] || '', - }; - }, - normalizePath: function (path) { - // The following operations are - // then applied, in order, to the new path: - // 6a) All occurrences of "./", where "." is a complete path - // segment, are removed. - // 6b) If the path ends with "." as a complete path segment, - // that "." is removed. - path = path.split('').reverse().join('').replace(SLASH_DOT_REGEX, ''); - // 6c) All occurrences of "/../", where is a - // complete path segment not equal to "..", are removed. - // Removal of these path segments is performed iteratively, - // removing the leftmost matching pattern on each iteration, - // until no matching pattern remains. - // 6d) If the path ends with "/..", where is a - // complete path segment not equal to "..", that - // "/.." is removed. - while ( - path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, '')).length - ) {} - return path.split('').reverse().join(''); - }, - buildURLFromParts: function (parts) { - return ( - parts.scheme + - parts.netLoc + - parts.path + - parts.params + - parts.query + - parts.fragment - ); - }, - }; - - module.exports = URLToolkit; - })(); -} (urlToolkit)); - -var urlToolkitExports = urlToolkit.exports; - -function ownKeys(e, r) { - var t = Object.keys(e); - if (Object.getOwnPropertySymbols) { - var o = Object.getOwnPropertySymbols(e); - r && (o = o.filter(function (r) { - return Object.getOwnPropertyDescriptor(e, r).enumerable; - })), t.push.apply(t, o); - } - return t; -} -function _objectSpread2(e) { - for (var r = 1; r < arguments.length; r++) { - var t = null != arguments[r] ? arguments[r] : {}; - r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { - _defineProperty(e, r, t[r]); - }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { - Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); - }); - } - return e; -} -function _toPrimitive(t, r) { - if ("object" != typeof t || !t) return t; - var e = t[Symbol.toPrimitive]; - if (void 0 !== e) { - var i = e.call(t, r || "default"); - if ("object" != typeof i) return i; - throw new TypeError("@@toPrimitive must return a primitive value."); - } - return ("string" === r ? String : Number)(t); -} -function _toPropertyKey(t) { - var i = _toPrimitive(t, "string"); - return "symbol" == typeof i ? i : String(i); -} -function _defineProperty(obj, key, value) { - key = _toPropertyKey(key); - if (key in obj) { - Object.defineProperty(obj, key, { - value: value, - enumerable: true, - configurable: true, - writable: true - }); - } else { - obj[key] = value; - } - return obj; -} -function _extends() { - _extends = Object.assign ? Object.assign.bind() : function (target) { - for (var i = 1; i < arguments.length; i++) { - var source = arguments[i]; - for (var key in source) { - if (Object.prototype.hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } - } - } - return target; - }; - return _extends.apply(this, arguments); -} - -// https://caniuse.com/mdn-javascript_builtins_number_isfinite -const isFiniteNumber = Number.isFinite || function (value) { - return typeof value === 'number' && isFinite(value); -}; - -// https://caniuse.com/mdn-javascript_builtins_number_issafeinteger -const isSafeInteger = Number.isSafeInteger || function (value) { - return typeof value === 'number' && Math.abs(value) <= MAX_SAFE_INTEGER; -}; -const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; - -let Events = /*#__PURE__*/function (Events) { - Events["MEDIA_ATTACHING"] = "hlsMediaAttaching"; - Events["MEDIA_ATTACHED"] = "hlsMediaAttached"; - Events["MEDIA_DETACHING"] = "hlsMediaDetaching"; - Events["MEDIA_DETACHED"] = "hlsMediaDetached"; - Events["BUFFER_RESET"] = "hlsBufferReset"; - Events["BUFFER_CODECS"] = "hlsBufferCodecs"; - Events["BUFFER_CREATED"] = "hlsBufferCreated"; - Events["BUFFER_APPENDING"] = "hlsBufferAppending"; - Events["BUFFER_APPENDED"] = "hlsBufferAppended"; - Events["BUFFER_EOS"] = "hlsBufferEos"; - Events["BUFFER_FLUSHING"] = "hlsBufferFlushing"; - Events["BUFFER_FLUSHED"] = "hlsBufferFlushed"; - Events["MANIFEST_LOADING"] = "hlsManifestLoading"; - Events["MANIFEST_LOADED"] = "hlsManifestLoaded"; - Events["MANIFEST_PARSED"] = "hlsManifestParsed"; - Events["LEVEL_SWITCHING"] = "hlsLevelSwitching"; - Events["LEVEL_SWITCHED"] = "hlsLevelSwitched"; - Events["LEVEL_LOADING"] = "hlsLevelLoading"; - Events["LEVEL_LOADED"] = "hlsLevelLoaded"; - Events["LEVEL_UPDATED"] = "hlsLevelUpdated"; - Events["LEVEL_PTS_UPDATED"] = "hlsLevelPtsUpdated"; - Events["LEVELS_UPDATED"] = "hlsLevelsUpdated"; - Events["AUDIO_TRACKS_UPDATED"] = "hlsAudioTracksUpdated"; - Events["AUDIO_TRACK_SWITCHING"] = "hlsAudioTrackSwitching"; - Events["AUDIO_TRACK_SWITCHED"] = "hlsAudioTrackSwitched"; - Events["AUDIO_TRACK_LOADING"] = "hlsAudioTrackLoading"; - Events["AUDIO_TRACK_LOADED"] = "hlsAudioTrackLoaded"; - Events["SUBTITLE_TRACKS_UPDATED"] = "hlsSubtitleTracksUpdated"; - Events["SUBTITLE_TRACKS_CLEARED"] = "hlsSubtitleTracksCleared"; - Events["SUBTITLE_TRACK_SWITCH"] = "hlsSubtitleTrackSwitch"; - Events["SUBTITLE_TRACK_LOADING"] = "hlsSubtitleTrackLoading"; - Events["SUBTITLE_TRACK_LOADED"] = "hlsSubtitleTrackLoaded"; - Events["SUBTITLE_FRAG_PROCESSED"] = "hlsSubtitleFragProcessed"; - Events["CUES_PARSED"] = "hlsCuesParsed"; - Events["NON_NATIVE_TEXT_TRACKS_FOUND"] = "hlsNonNativeTextTracksFound"; - Events["INIT_PTS_FOUND"] = "hlsInitPtsFound"; - Events["FRAG_LOADING"] = "hlsFragLoading"; - Events["FRAG_LOAD_EMERGENCY_ABORTED"] = "hlsFragLoadEmergencyAborted"; - Events["FRAG_LOADED"] = "hlsFragLoaded"; - Events["FRAG_DECRYPTED"] = "hlsFragDecrypted"; - Events["FRAG_PARSING_INIT_SEGMENT"] = "hlsFragParsingInitSegment"; - Events["FRAG_PARSING_USERDATA"] = "hlsFragParsingUserdata"; - Events["FRAG_PARSING_METADATA"] = "hlsFragParsingMetadata"; - Events["FRAG_PARSED"] = "hlsFragParsed"; - Events["FRAG_BUFFERED"] = "hlsFragBuffered"; - Events["FRAG_CHANGED"] = "hlsFragChanged"; - Events["FPS_DROP"] = "hlsFpsDrop"; - Events["FPS_DROP_LEVEL_CAPPING"] = "hlsFpsDropLevelCapping"; - Events["MAX_AUTO_LEVEL_UPDATED"] = "hlsMaxAutoLevelUpdated"; - Events["ERROR"] = "hlsError"; - Events["DESTROYING"] = "hlsDestroying"; - Events["KEY_LOADING"] = "hlsKeyLoading"; - Events["KEY_LOADED"] = "hlsKeyLoaded"; - Events["LIVE_BACK_BUFFER_REACHED"] = "hlsLiveBackBufferReached"; - Events["BACK_BUFFER_REACHED"] = "hlsBackBufferReached"; - Events["STEERING_MANIFEST_LOADED"] = "hlsSteeringManifestLoaded"; - return Events; -}({}); - -/** - * Defines each Event type and payload by Event name. Used in {@link hls.js#HlsEventEmitter} to strongly type the event listener API. - */ - -let ErrorTypes = /*#__PURE__*/function (ErrorTypes) { - ErrorTypes["NETWORK_ERROR"] = "networkError"; - ErrorTypes["MEDIA_ERROR"] = "mediaError"; - ErrorTypes["KEY_SYSTEM_ERROR"] = "keySystemError"; - ErrorTypes["MUX_ERROR"] = "muxError"; - ErrorTypes["OTHER_ERROR"] = "otherError"; - return ErrorTypes; -}({}); -let ErrorDetails = /*#__PURE__*/function (ErrorDetails) { - ErrorDetails["KEY_SYSTEM_NO_KEYS"] = "keySystemNoKeys"; - ErrorDetails["KEY_SYSTEM_NO_ACCESS"] = "keySystemNoAccess"; - ErrorDetails["KEY_SYSTEM_NO_SESSION"] = "keySystemNoSession"; - ErrorDetails["KEY_SYSTEM_NO_CONFIGURED_LICENSE"] = "keySystemNoConfiguredLicense"; - ErrorDetails["KEY_SYSTEM_LICENSE_REQUEST_FAILED"] = "keySystemLicenseRequestFailed"; - ErrorDetails["KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED"] = "keySystemServerCertificateRequestFailed"; - ErrorDetails["KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED"] = "keySystemServerCertificateUpdateFailed"; - ErrorDetails["KEY_SYSTEM_SESSION_UPDATE_FAILED"] = "keySystemSessionUpdateFailed"; - ErrorDetails["KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED"] = "keySystemStatusOutputRestricted"; - ErrorDetails["KEY_SYSTEM_STATUS_INTERNAL_ERROR"] = "keySystemStatusInternalError"; - ErrorDetails["MANIFEST_LOAD_ERROR"] = "manifestLoadError"; - ErrorDetails["MANIFEST_LOAD_TIMEOUT"] = "manifestLoadTimeOut"; - ErrorDetails["MANIFEST_PARSING_ERROR"] = "manifestParsingError"; - ErrorDetails["MANIFEST_INCOMPATIBLE_CODECS_ERROR"] = "manifestIncompatibleCodecsError"; - ErrorDetails["LEVEL_EMPTY_ERROR"] = "levelEmptyError"; - ErrorDetails["LEVEL_LOAD_ERROR"] = "levelLoadError"; - ErrorDetails["LEVEL_LOAD_TIMEOUT"] = "levelLoadTimeOut"; - ErrorDetails["LEVEL_PARSING_ERROR"] = "levelParsingError"; - ErrorDetails["LEVEL_SWITCH_ERROR"] = "levelSwitchError"; - ErrorDetails["AUDIO_TRACK_LOAD_ERROR"] = "audioTrackLoadError"; - ErrorDetails["AUDIO_TRACK_LOAD_TIMEOUT"] = "audioTrackLoadTimeOut"; - ErrorDetails["SUBTITLE_LOAD_ERROR"] = "subtitleTrackLoadError"; - ErrorDetails["SUBTITLE_TRACK_LOAD_TIMEOUT"] = "subtitleTrackLoadTimeOut"; - ErrorDetails["FRAG_LOAD_ERROR"] = "fragLoadError"; - ErrorDetails["FRAG_LOAD_TIMEOUT"] = "fragLoadTimeOut"; - ErrorDetails["FRAG_DECRYPT_ERROR"] = "fragDecryptError"; - ErrorDetails["FRAG_PARSING_ERROR"] = "fragParsingError"; - ErrorDetails["FRAG_GAP"] = "fragGap"; - ErrorDetails["REMUX_ALLOC_ERROR"] = "remuxAllocError"; - ErrorDetails["KEY_LOAD_ERROR"] = "keyLoadError"; - ErrorDetails["KEY_LOAD_TIMEOUT"] = "keyLoadTimeOut"; - ErrorDetails["BUFFER_ADD_CODEC_ERROR"] = "bufferAddCodecError"; - ErrorDetails["BUFFER_INCOMPATIBLE_CODECS_ERROR"] = "bufferIncompatibleCodecsError"; - ErrorDetails["BUFFER_APPEND_ERROR"] = "bufferAppendError"; - ErrorDetails["BUFFER_APPENDING_ERROR"] = "bufferAppendingError"; - ErrorDetails["BUFFER_STALLED_ERROR"] = "bufferStalledError"; - ErrorDetails["BUFFER_FULL_ERROR"] = "bufferFullError"; - ErrorDetails["BUFFER_SEEK_OVER_HOLE"] = "bufferSeekOverHole"; - ErrorDetails["BUFFER_NUDGE_ON_STALL"] = "bufferNudgeOnStall"; - ErrorDetails["INTERNAL_EXCEPTION"] = "internalException"; - ErrorDetails["INTERNAL_ABORTED"] = "aborted"; - ErrorDetails["UNKNOWN"] = "unknown"; - return ErrorDetails; -}({}); - -const noop = function noop() {}; -const fakeLogger = { - trace: noop, - debug: noop, - log: noop, - warn: noop, - info: noop, - error: noop -}; -let exportedLogger = fakeLogger; - -// let lastCallTime; -// function formatMsgWithTimeInfo(type, msg) { -// const now = Date.now(); -// const diff = lastCallTime ? '+' + (now - lastCallTime) : '0'; -// lastCallTime = now; -// msg = (new Date(now)).toISOString() + ' | [' + type + '] > ' + msg + ' ( ' + diff + ' ms )'; -// return msg; -// } - -function consolePrintFn(type) { - const func = self.console[type]; - if (func) { - return func.bind(self.console, `[${type}] >`); - } - return noop; -} -function exportLoggerFunctions(debugConfig, ...functions) { - functions.forEach(function (type) { - exportedLogger[type] = debugConfig[type] ? debugConfig[type].bind(debugConfig) : consolePrintFn(type); - }); -} -function enableLogs(debugConfig, id) { - // check that console is available - if (typeof console === 'object' && debugConfig === true || typeof debugConfig === 'object') { - exportLoggerFunctions(debugConfig, - // Remove out from list here to hard-disable a log-level - // 'trace', - 'debug', 'log', 'info', 'warn', 'error'); - // Some browsers don't allow to use bind on console object anyway - // fallback to default if needed - try { - exportedLogger.log(`Debug logs enabled for "${id}" in hls.js version ${"1.5.15"}`); - } catch (e) { - exportedLogger = fakeLogger; - } - } else { - exportedLogger = fakeLogger; - } -} -const logger = exportedLogger; - -const DECIMAL_RESOLUTION_REGEX = /^(\d+)x(\d+)$/; -const ATTR_LIST_REGEX = /(.+?)=(".*?"|.*?)(?:,|$)/g; - -// adapted from https://github.com/kanongil/node-m3u8parse/blob/master/attrlist.js -class AttrList { - constructor(attrs) { - if (typeof attrs === 'string') { - attrs = AttrList.parseAttrList(attrs); - } - _extends(this, attrs); - } - get clientAttrs() { - return Object.keys(this).filter(attr => attr.substring(0, 2) === 'X-'); - } - decimalInteger(attrName) { - const intValue = parseInt(this[attrName], 10); - if (intValue > Number.MAX_SAFE_INTEGER) { - return Infinity; - } - return intValue; - } - hexadecimalInteger(attrName) { - if (this[attrName]) { - let stringValue = (this[attrName] || '0x').slice(2); - stringValue = (stringValue.length & 1 ? '0' : '') + stringValue; - const value = new Uint8Array(stringValue.length / 2); - for (let i = 0; i < stringValue.length / 2; i++) { - value[i] = parseInt(stringValue.slice(i * 2, i * 2 + 2), 16); - } - return value; - } else { - return null; - } - } - hexadecimalIntegerAsNumber(attrName) { - const intValue = parseInt(this[attrName], 16); - if (intValue > Number.MAX_SAFE_INTEGER) { - return Infinity; - } - return intValue; - } - decimalFloatingPoint(attrName) { - return parseFloat(this[attrName]); - } - optionalFloat(attrName, defaultValue) { - const value = this[attrName]; - return value ? parseFloat(value) : defaultValue; - } - enumeratedString(attrName) { - return this[attrName]; - } - bool(attrName) { - return this[attrName] === 'YES'; - } - decimalResolution(attrName) { - const res = DECIMAL_RESOLUTION_REGEX.exec(this[attrName]); - if (res === null) { - return undefined; - } - return { - width: parseInt(res[1], 10), - height: parseInt(res[2], 10) - }; - } - static parseAttrList(input) { - let match; - const attrs = {}; - const quote = '"'; - ATTR_LIST_REGEX.lastIndex = 0; - while ((match = ATTR_LIST_REGEX.exec(input)) !== null) { - let value = match[2]; - if (value.indexOf(quote) === 0 && value.lastIndexOf(quote) === value.length - 1) { - value = value.slice(1, -1); - } - const name = match[1].trim(); - attrs[name] = value; - } - return attrs; - } -} - -// Avoid exporting const enum so that these values can be inlined - -function isDateRangeCueAttribute(attrName) { - return attrName !== "ID" && attrName !== "CLASS" && attrName !== "START-DATE" && attrName !== "DURATION" && attrName !== "END-DATE" && attrName !== "END-ON-NEXT"; -} -function isSCTE35Attribute(attrName) { - return attrName === "SCTE35-OUT" || attrName === "SCTE35-IN"; -} -class DateRange { - constructor(dateRangeAttr, dateRangeWithSameId) { - this.attr = void 0; - this._startDate = void 0; - this._endDate = void 0; - this._badValueForSameId = void 0; - if (dateRangeWithSameId) { - const previousAttr = dateRangeWithSameId.attr; - for (const key in previousAttr) { - if (Object.prototype.hasOwnProperty.call(dateRangeAttr, key) && dateRangeAttr[key] !== previousAttr[key]) { - logger.warn(`DATERANGE tag attribute: "${key}" does not match for tags with ID: "${dateRangeAttr.ID}"`); - this._badValueForSameId = key; - break; - } - } - // Merge DateRange tags with the same ID - dateRangeAttr = _extends(new AttrList({}), previousAttr, dateRangeAttr); - } - this.attr = dateRangeAttr; - this._startDate = new Date(dateRangeAttr["START-DATE"]); - if ("END-DATE" in this.attr) { - const endDate = new Date(this.attr["END-DATE"]); - if (isFiniteNumber(endDate.getTime())) { - this._endDate = endDate; - } - } - } - get id() { - return this.attr.ID; - } - get class() { - return this.attr.CLASS; - } - get startDate() { - return this._startDate; - } - get endDate() { - if (this._endDate) { - return this._endDate; - } - const duration = this.duration; - if (duration !== null) { - return new Date(this._startDate.getTime() + duration * 1000); - } - return null; - } - get duration() { - if ("DURATION" in this.attr) { - const duration = this.attr.decimalFloatingPoint("DURATION"); - if (isFiniteNumber(duration)) { - return duration; - } - } else if (this._endDate) { - return (this._endDate.getTime() - this._startDate.getTime()) / 1000; - } - return null; - } - get plannedDuration() { - if ("PLANNED-DURATION" in this.attr) { - return this.attr.decimalFloatingPoint("PLANNED-DURATION"); - } - return null; - } - get endOnNext() { - return this.attr.bool("END-ON-NEXT"); - } - get isValid() { - return !!this.id && !this._badValueForSameId && isFiniteNumber(this.startDate.getTime()) && (this.duration === null || this.duration >= 0) && (!this.endOnNext || !!this.class); - } -} - -class LoadStats { - constructor() { - this.aborted = false; - this.loaded = 0; - this.retry = 0; - this.total = 0; - this.chunkCount = 0; - this.bwEstimate = 0; - this.loading = { - start: 0, - first: 0, - end: 0 - }; - this.parsing = { - start: 0, - end: 0 - }; - this.buffering = { - start: 0, - first: 0, - end: 0 - }; - } -} - -var ElementaryStreamTypes = { - AUDIO: "audio", - VIDEO: "video", - AUDIOVIDEO: "audiovideo" -}; -class BaseSegment { - constructor(baseurl) { - this._byteRange = null; - this._url = null; - // baseurl is the URL to the playlist - this.baseurl = void 0; - // relurl is the portion of the URL that comes from inside the playlist. - this.relurl = void 0; - // Holds the types of data this fragment supports - this.elementaryStreams = { - [ElementaryStreamTypes.AUDIO]: null, - [ElementaryStreamTypes.VIDEO]: null, - [ElementaryStreamTypes.AUDIOVIDEO]: null - }; - this.baseurl = baseurl; - } - - // setByteRange converts a EXT-X-BYTERANGE attribute into a two element array - setByteRange(value, previous) { - const params = value.split('@', 2); - let start; - if (params.length === 1) { - start = (previous == null ? void 0 : previous.byteRangeEndOffset) || 0; - } else { - start = parseInt(params[1]); - } - this._byteRange = [start, parseInt(params[0]) + start]; - } - get byteRange() { - if (!this._byteRange) { - return []; - } - return this._byteRange; - } - get byteRangeStartOffset() { - return this.byteRange[0]; - } - get byteRangeEndOffset() { - return this.byteRange[1]; - } - get url() { - if (!this._url && this.baseurl && this.relurl) { - this._url = urlToolkitExports.buildAbsoluteURL(this.baseurl, this.relurl, { - alwaysNormalize: true - }); - } - return this._url || ''; - } - set url(value) { - this._url = value; - } -} - -/** - * Object representing parsed data from an HLS Segment. Found in {@link hls.js#LevelDetails.fragments}. - */ -class Fragment extends BaseSegment { - constructor(type, baseurl) { - super(baseurl); - this._decryptdata = null; - this.rawProgramDateTime = null; - this.programDateTime = null; - this.tagList = []; - // EXTINF has to be present for a m3u8 to be considered valid - this.duration = 0; - // sn notates the sequence number for a segment, and if set to a string can be 'initSegment' - this.sn = 0; - // levelkeys are the EXT-X-KEY tags that apply to this segment for decryption - // core difference from the private field _decryptdata is the lack of the initialized IV - // _decryptdata will set the IV for this segment based on the segment number in the fragment - this.levelkeys = void 0; - // A string representing the fragment type - this.type = void 0; - // A reference to the loader. Set while the fragment is loading, and removed afterwards. Used to abort fragment loading - this.loader = null; - // A reference to the key loader. Set while the key is loading, and removed afterwards. Used to abort key loading - this.keyLoader = null; - // The level/track index to which the fragment belongs - this.level = -1; - // The continuity counter of the fragment - this.cc = 0; - // The starting Presentation Time Stamp (PTS) of the fragment. Set after transmux complete. - this.startPTS = void 0; - // The ending Presentation Time Stamp (PTS) of the fragment. Set after transmux complete. - this.endPTS = void 0; - // The starting Decode Time Stamp (DTS) of the fragment. Set after transmux complete. - this.startDTS = void 0; - // The ending Decode Time Stamp (DTS) of the fragment. Set after transmux complete. - this.endDTS = void 0; - // The start time of the fragment, as listed in the manifest. Updated after transmux complete. - this.start = 0; - // Set by `updateFragPTSDTS` in level-helper - this.deltaPTS = void 0; - // The maximum starting Presentation Time Stamp (audio/video PTS) of the fragment. Set after transmux complete. - this.maxStartPTS = void 0; - // The minimum ending Presentation Time Stamp (audio/video PTS) of the fragment. Set after transmux complete. - this.minEndPTS = void 0; - // Load/parse timing information - this.stats = new LoadStats(); - // Init Segment bytes (unset for media segments) - this.data = void 0; - // A flag indicating whether the segment was downloaded in order to test bitrate, and was not buffered - this.bitrateTest = false; - // #EXTINF segment title - this.title = null; - // The Media Initialization Section for this segment - this.initSegment = null; - // Fragment is the last fragment in the media playlist - this.endList = void 0; - // Fragment is marked by an EXT-X-GAP tag indicating that it does not contain media data and should not be loaded - this.gap = void 0; - // Deprecated - this.urlId = 0; - this.type = type; - } - get decryptdata() { - const { - levelkeys - } = this; - if (!levelkeys && !this._decryptdata) { - return null; - } - if (!this._decryptdata && this.levelkeys && !this.levelkeys.NONE) { - const key = this.levelkeys.identity; - if (key) { - this._decryptdata = key.getDecryptData(this.sn); - } else { - const keyFormats = Object.keys(this.levelkeys); - if (keyFormats.length === 1) { - return this._decryptdata = this.levelkeys[keyFormats[0]].getDecryptData(this.sn); - } - } - } - return this._decryptdata; - } - get end() { - return this.start + this.duration; - } - get endProgramDateTime() { - if (this.programDateTime === null) { - return null; - } - if (!isFiniteNumber(this.programDateTime)) { - return null; - } - const duration = !isFiniteNumber(this.duration) ? 0 : this.duration; - return this.programDateTime + duration * 1000; - } - get encrypted() { - var _this$_decryptdata; - // At the m3u8-parser level we need to add support for manifest signalled keyformats - // when we want the fragment to start reporting that it is encrypted. - // Currently, keyFormat will only be set for identity keys - if ((_this$_decryptdata = this._decryptdata) != null && _this$_decryptdata.encrypted) { - return true; - } else if (this.levelkeys) { - const keyFormats = Object.keys(this.levelkeys); - const len = keyFormats.length; - if (len > 1 || len === 1 && this.levelkeys[keyFormats[0]].encrypted) { - return true; - } - } - return false; - } - setKeyFormat(keyFormat) { - if (this.levelkeys) { - const key = this.levelkeys[keyFormat]; - if (key && !this._decryptdata) { - this._decryptdata = key.getDecryptData(this.sn); - } - } - } - abortRequests() { - var _this$loader, _this$keyLoader; - (_this$loader = this.loader) == null ? void 0 : _this$loader.abort(); - (_this$keyLoader = this.keyLoader) == null ? void 0 : _this$keyLoader.abort(); - } - setElementaryStreamInfo(type, startPTS, endPTS, startDTS, endDTS, partial = false) { - const { - elementaryStreams - } = this; - const info = elementaryStreams[type]; - if (!info) { - elementaryStreams[type] = { - startPTS, - endPTS, - startDTS, - endDTS, - partial - }; - return; - } - info.startPTS = Math.min(info.startPTS, startPTS); - info.endPTS = Math.max(info.endPTS, endPTS); - info.startDTS = Math.min(info.startDTS, startDTS); - info.endDTS = Math.max(info.endDTS, endDTS); - } - clearElementaryStreamInfo() { - const { - elementaryStreams - } = this; - elementaryStreams[ElementaryStreamTypes.AUDIO] = null; - elementaryStreams[ElementaryStreamTypes.VIDEO] = null; - elementaryStreams[ElementaryStreamTypes.AUDIOVIDEO] = null; - } -} - -/** - * Object representing parsed data from an HLS Partial Segment. Found in {@link hls.js#LevelDetails.partList}. - */ -class Part extends BaseSegment { - constructor(partAttrs, frag, baseurl, index, previous) { - super(baseurl); - this.fragOffset = 0; - this.duration = 0; - this.gap = false; - this.independent = false; - this.relurl = void 0; - this.fragment = void 0; - this.index = void 0; - this.stats = new LoadStats(); - this.duration = partAttrs.decimalFloatingPoint('DURATION'); - this.gap = partAttrs.bool('GAP'); - this.independent = partAttrs.bool('INDEPENDENT'); - this.relurl = partAttrs.enumeratedString('URI'); - this.fragment = frag; - this.index = index; - const byteRange = partAttrs.enumeratedString('BYTERANGE'); - if (byteRange) { - this.setByteRange(byteRange, previous); - } - if (previous) { - this.fragOffset = previous.fragOffset + previous.duration; - } - } - get start() { - return this.fragment.start + this.fragOffset; - } - get end() { - return this.start + this.duration; - } - get loaded() { - const { - elementaryStreams - } = this; - return !!(elementaryStreams.audio || elementaryStreams.video || elementaryStreams.audiovideo); - } -} - -const DEFAULT_TARGET_DURATION = 10; - -/** - * Object representing parsed data from an HLS Media Playlist. Found in {@link hls.js#Level.details}. - */ -class LevelDetails { - constructor(baseUrl) { - this.PTSKnown = false; - this.alignedSliding = false; - this.averagetargetduration = void 0; - this.endCC = 0; - this.endSN = 0; - this.fragments = void 0; - this.fragmentHint = void 0; - this.partList = null; - this.dateRanges = void 0; - this.live = true; - this.ageHeader = 0; - this.advancedDateTime = void 0; - this.updated = true; - this.advanced = true; - this.availabilityDelay = void 0; - // Manifest reload synchronization - this.misses = 0; - this.startCC = 0; - this.startSN = 0; - this.startTimeOffset = null; - this.targetduration = 0; - this.totalduration = 0; - this.type = null; - this.url = void 0; - this.m3u8 = ''; - this.version = null; - this.canBlockReload = false; - this.canSkipUntil = 0; - this.canSkipDateRanges = false; - this.skippedSegments = 0; - this.recentlyRemovedDateranges = void 0; - this.partHoldBack = 0; - this.holdBack = 0; - this.partTarget = 0; - this.preloadHint = void 0; - this.renditionReports = void 0; - this.tuneInGoal = 0; - this.deltaUpdateFailed = void 0; - this.driftStartTime = 0; - this.driftEndTime = 0; - this.driftStart = 0; - this.driftEnd = 0; - this.encryptedFragments = void 0; - this.playlistParsingError = null; - this.variableList = null; - this.hasVariableRefs = false; - this.fragments = []; - this.encryptedFragments = []; - this.dateRanges = {}; - this.url = baseUrl; - } - reloaded(previous) { - if (!previous) { - this.advanced = true; - this.updated = true; - return; - } - const partSnDiff = this.lastPartSn - previous.lastPartSn; - const partIndexDiff = this.lastPartIndex - previous.lastPartIndex; - this.updated = this.endSN !== previous.endSN || !!partIndexDiff || !!partSnDiff || !this.live; - this.advanced = this.endSN > previous.endSN || partSnDiff > 0 || partSnDiff === 0 && partIndexDiff > 0; - if (this.updated || this.advanced) { - this.misses = Math.floor(previous.misses * 0.6); - } else { - this.misses = previous.misses + 1; - } - this.availabilityDelay = previous.availabilityDelay; - } - get hasProgramDateTime() { - if (this.fragments.length) { - return isFiniteNumber(this.fragments[this.fragments.length - 1].programDateTime); - } - return false; - } - get levelTargetDuration() { - return this.averagetargetduration || this.targetduration || DEFAULT_TARGET_DURATION; - } - get drift() { - const runTime = this.driftEndTime - this.driftStartTime; - if (runTime > 0) { - const runDuration = this.driftEnd - this.driftStart; - return runDuration * 1000 / runTime; - } - return 1; - } - get edge() { - return this.partEnd || this.fragmentEnd; - } - get partEnd() { - var _this$partList; - if ((_this$partList = this.partList) != null && _this$partList.length) { - return this.partList[this.partList.length - 1].end; - } - return this.fragmentEnd; - } - get fragmentEnd() { - var _this$fragments; - if ((_this$fragments = this.fragments) != null && _this$fragments.length) { - return this.fragments[this.fragments.length - 1].end; - } - return 0; - } - get age() { - if (this.advancedDateTime) { - return Math.max(Date.now() - this.advancedDateTime, 0) / 1000; - } - return 0; - } - get lastPartIndex() { - var _this$partList2; - if ((_this$partList2 = this.partList) != null && _this$partList2.length) { - return this.partList[this.partList.length - 1].index; - } - return -1; - } - get lastPartSn() { - var _this$partList3; - if ((_this$partList3 = this.partList) != null && _this$partList3.length) { - return this.partList[this.partList.length - 1].fragment.sn; - } - return this.endSN; - } -} - -function base64Decode(base64encodedStr) { - return Uint8Array.from(atob(base64encodedStr), c => c.charCodeAt(0)); -} - -function getKeyIdBytes(str) { - const keyIdbytes = strToUtf8array(str).subarray(0, 16); - const paddedkeyIdbytes = new Uint8Array(16); - paddedkeyIdbytes.set(keyIdbytes, 16 - keyIdbytes.length); - return paddedkeyIdbytes; -} -function changeEndianness(keyId) { - const swap = function swap(array, from, to) { - const cur = array[from]; - array[from] = array[to]; - array[to] = cur; - }; - swap(keyId, 0, 3); - swap(keyId, 1, 2); - swap(keyId, 4, 5); - swap(keyId, 6, 7); -} -function convertDataUriToArrayBytes(uri) { - // data:[ - const colonsplit = uri.split(':'); - let keydata = null; - if (colonsplit[0] === 'data' && colonsplit.length === 2) { - const semicolonsplit = colonsplit[1].split(';'); - const commasplit = semicolonsplit[semicolonsplit.length - 1].split(','); - if (commasplit.length === 2) { - const isbase64 = commasplit[0] === 'base64'; - const data = commasplit[1]; - if (isbase64) { - semicolonsplit.splice(-1, 1); // remove from processing - keydata = base64Decode(data); - } else { - keydata = getKeyIdBytes(data); - } - } - } - return keydata; -} -function strToUtf8array(str) { - return Uint8Array.from(unescape(encodeURIComponent(str)), c => c.charCodeAt(0)); -} - -/** returns `undefined` is `self` is missing, e.g. in node */ -const optionalSelf = typeof self !== 'undefined' ? self : undefined; - -/** - * @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/requestMediaKeySystemAccess - */ -var KeySystems = { - CLEARKEY: "org.w3.clearkey", - FAIRPLAY: "com.apple.fps", - PLAYREADY: "com.microsoft.playready", - WIDEVINE: "com.widevine.alpha" -}; - -// Playlist #EXT-X-KEY KEYFORMAT values -var KeySystemFormats = { - CLEARKEY: "org.w3.clearkey", - FAIRPLAY: "com.apple.streamingkeydelivery", - PLAYREADY: "com.microsoft.playready", - WIDEVINE: "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed" -}; -function keySystemFormatToKeySystemDomain(format) { - switch (format) { - case KeySystemFormats.FAIRPLAY: - return KeySystems.FAIRPLAY; - case KeySystemFormats.PLAYREADY: - return KeySystems.PLAYREADY; - case KeySystemFormats.WIDEVINE: - return KeySystems.WIDEVINE; - case KeySystemFormats.CLEARKEY: - return KeySystems.CLEARKEY; - } -} - -// System IDs for which we can extract a key ID from "encrypted" event PSSH -var KeySystemIds = { - CENC: "1077efecc0b24d02ace33c1e52e2fb4b", - CLEARKEY: "e2719d58a985b3c9781ab030af78d30e", - FAIRPLAY: "94ce86fb07ff4f43adb893d2fa968ca2", - PLAYREADY: "9a04f07998404286ab92e65be0885f95", - WIDEVINE: "edef8ba979d64acea3c827dcd51d21ed" -}; -function keySystemIdToKeySystemDomain(systemId) { - if (systemId === KeySystemIds.WIDEVINE) { - return KeySystems.WIDEVINE; - } else if (systemId === KeySystemIds.PLAYREADY) { - return KeySystems.PLAYREADY; - } else if (systemId === KeySystemIds.CENC || systemId === KeySystemIds.CLEARKEY) { - return KeySystems.CLEARKEY; - } -} -function keySystemDomainToKeySystemFormat(keySystem) { - switch (keySystem) { - case KeySystems.FAIRPLAY: - return KeySystemFormats.FAIRPLAY; - case KeySystems.PLAYREADY: - return KeySystemFormats.PLAYREADY; - case KeySystems.WIDEVINE: - return KeySystemFormats.WIDEVINE; - case KeySystems.CLEARKEY: - return KeySystemFormats.CLEARKEY; - } -} -function getKeySystemsForConfig(config) { - const { - drmSystems, - widevineLicenseUrl - } = config; - const keySystemsToAttempt = drmSystems ? [KeySystems.FAIRPLAY, KeySystems.WIDEVINE, KeySystems.PLAYREADY, KeySystems.CLEARKEY].filter(keySystem => !!drmSystems[keySystem]) : []; - if (!keySystemsToAttempt[KeySystems.WIDEVINE] && widevineLicenseUrl) { - keySystemsToAttempt.push(KeySystems.WIDEVINE); - } - return keySystemsToAttempt; -} -const requestMediaKeySystemAccess = function (_optionalSelf$navigat) { - if (optionalSelf != null && (_optionalSelf$navigat = optionalSelf.navigator) != null && _optionalSelf$navigat.requestMediaKeySystemAccess) { - return self.navigator.requestMediaKeySystemAccess.bind(self.navigator); - } else { - return null; - } -}(); - -/** - * @see https://developer.mozilla.org/en-US/docs/Web/API/MediaKeySystemConfiguration - */ -function getSupportedMediaKeySystemConfigurations(keySystem, audioCodecs, videoCodecs, drmSystemOptions) { - let initDataTypes; - switch (keySystem) { - case KeySystems.FAIRPLAY: - initDataTypes = ['cenc', 'sinf']; - break; - case KeySystems.WIDEVINE: - case KeySystems.PLAYREADY: - initDataTypes = ['cenc']; - break; - case KeySystems.CLEARKEY: - initDataTypes = ['cenc', 'keyids']; - break; - default: - throw new Error(`Unknown key-system: ${keySystem}`); - } - return createMediaKeySystemConfigurations(initDataTypes, audioCodecs, videoCodecs, drmSystemOptions); -} -function createMediaKeySystemConfigurations(initDataTypes, audioCodecs, videoCodecs, drmSystemOptions) { - const baseConfig = { - initDataTypes: initDataTypes, - persistentState: drmSystemOptions.persistentState || 'optional', - distinctiveIdentifier: drmSystemOptions.distinctiveIdentifier || 'optional', - sessionTypes: drmSystemOptions.sessionTypes || [drmSystemOptions.sessionType || 'temporary'], - audioCapabilities: audioCodecs.map(codec => ({ - contentType: `audio/mp4; codecs="${codec}"`, - robustness: drmSystemOptions.audioRobustness || '', - encryptionScheme: drmSystemOptions.audioEncryptionScheme || null - })), - videoCapabilities: videoCodecs.map(codec => ({ - contentType: `video/mp4; codecs="${codec}"`, - robustness: drmSystemOptions.videoRobustness || '', - encryptionScheme: drmSystemOptions.videoEncryptionScheme || null - })) - }; - return [baseConfig]; -} - -function sliceUint8(array, start, end) { - // @ts-expect-error This polyfills IE11 usage of Uint8Array slice. - // It always exists in the TypeScript definition so fails, but it fails at runtime on IE11. - return Uint8Array.prototype.slice ? array.slice(start, end) : new Uint8Array(Array.prototype.slice.call(array, start, end)); -} - -// breaking up those two types in order to clarify what is happening in the decoding path. - -/** - * Returns true if an ID3 header can be found at offset in data - * @param data - The data to search - * @param offset - The offset at which to start searching - */ -const isHeader$2 = (data, offset) => { - /* - * http://id3.org/id3v2.3.0 - * [0] = 'I' - * [1] = 'D' - * [2] = '3' - * [3,4] = {Version} - * [5] = {Flags} - * [6-9] = {ID3 Size} - * - * An ID3v2 tag can be detected with the following pattern: - * $49 44 33 yy yy xx zz zz zz zz - * Where yy is less than $FF, xx is the 'flags' byte and zz is less than $80 - */ - if (offset + 10 <= data.length) { - // look for 'ID3' identifier - if (data[offset] === 0x49 && data[offset + 1] === 0x44 && data[offset + 2] === 0x33) { - // check version is within range - if (data[offset + 3] < 0xff && data[offset + 4] < 0xff) { - // check size is within range - if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) { - return true; - } - } - } - } - return false; -}; - -/** - * Returns true if an ID3 footer can be found at offset in data - * @param data - The data to search - * @param offset - The offset at which to start searching - */ -const isFooter = (data, offset) => { - /* - * The footer is a copy of the header, but with a different identifier - */ - if (offset + 10 <= data.length) { - // look for '3DI' identifier - if (data[offset] === 0x33 && data[offset + 1] === 0x44 && data[offset + 2] === 0x49) { - // check version is within range - if (data[offset + 3] < 0xff && data[offset + 4] < 0xff) { - // check size is within range - if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) { - return true; - } - } - } - } - return false; -}; - -/** - * Returns any adjacent ID3 tags found in data starting at offset, as one block of data - * @param data - The data to search in - * @param offset - The offset at which to start searching - * @returns the block of data containing any ID3 tags found - * or *undefined* if no header is found at the starting offset - */ -const getID3Data = (data, offset) => { - const front = offset; - let length = 0; - while (isHeader$2(data, offset)) { - // ID3 header is 10 bytes - length += 10; - const size = readSize(data, offset + 6); - length += size; - if (isFooter(data, offset + 10)) { - // ID3 footer is 10 bytes - length += 10; - } - offset += length; - } - if (length > 0) { - return data.subarray(front, front + length); - } - return undefined; -}; -const readSize = (data, offset) => { - let size = 0; - size = (data[offset] & 0x7f) << 21; - size |= (data[offset + 1] & 0x7f) << 14; - size |= (data[offset + 2] & 0x7f) << 7; - size |= data[offset + 3] & 0x7f; - return size; -}; -const canParse$2 = (data, offset) => { - return isHeader$2(data, offset) && readSize(data, offset + 6) + 10 <= data.length - offset; -}; - -/** - * Searches for the Elementary Stream timestamp found in the ID3 data chunk - * @param data - Block of data containing one or more ID3 tags - */ -const getTimeStamp = data => { - const frames = getID3Frames(data); - for (let i = 0; i < frames.length; i++) { - const frame = frames[i]; - if (isTimeStampFrame(frame)) { - return readTimeStamp(frame); - } - } - return undefined; -}; - -/** - * Returns true if the ID3 frame is an Elementary Stream timestamp frame - */ -const isTimeStampFrame = frame => { - return frame && frame.key === 'PRIV' && frame.info === 'com.apple.streaming.transportStreamTimestamp'; -}; -const getFrameData = data => { - /* - Frame ID $xx xx xx xx (four characters) - Size $xx xx xx xx - Flags $xx xx - */ - const type = String.fromCharCode(data[0], data[1], data[2], data[3]); - const size = readSize(data, 4); - - // skip frame id, size, and flags - const offset = 10; - return { - type, - size, - data: data.subarray(offset, offset + size) - }; -}; - -/** - * Returns an array of ID3 frames found in all the ID3 tags in the id3Data - * @param id3Data - The ID3 data containing one or more ID3 tags - */ -const getID3Frames = id3Data => { - let offset = 0; - const frames = []; - while (isHeader$2(id3Data, offset)) { - const size = readSize(id3Data, offset + 6); - // skip past ID3 header - offset += 10; - const end = offset + size; - // loop through frames in the ID3 tag - while (offset + 8 < end) { - const frameData = getFrameData(id3Data.subarray(offset)); - const frame = decodeFrame(frameData); - if (frame) { - frames.push(frame); - } - - // skip frame header and frame data - offset += frameData.size + 10; - } - if (isFooter(id3Data, offset)) { - offset += 10; - } - } - return frames; -}; -const decodeFrame = frame => { - if (frame.type === 'PRIV') { - return decodePrivFrame(frame); - } else if (frame.type[0] === 'W') { - return decodeURLFrame(frame); - } - return decodeTextFrame(frame); -}; -const decodePrivFrame = frame => { - /* - Format: \0 - */ - if (frame.size < 2) { - return undefined; - } - const owner = utf8ArrayToStr(frame.data, true); - const privateData = new Uint8Array(frame.data.subarray(owner.length + 1)); - return { - key: frame.type, - info: owner, - data: privateData.buffer - }; -}; -const decodeTextFrame = frame => { - if (frame.size < 2) { - return undefined; - } - if (frame.type === 'TXXX') { - /* - Format: - [0] = {Text Encoding} - [1-?] = {Description}\0{Value} - */ - let index = 1; - const description = utf8ArrayToStr(frame.data.subarray(index), true); - index += description.length + 1; - const value = utf8ArrayToStr(frame.data.subarray(index)); - return { - key: frame.type, - info: description, - data: value - }; - } - /* - Format: - [0] = {Text Encoding} - [1-?] = {Value} - */ - const text = utf8ArrayToStr(frame.data.subarray(1)); - return { - key: frame.type, - data: text - }; -}; -const decodeURLFrame = frame => { - if (frame.type === 'WXXX') { - /* - Format: - [0] = {Text Encoding} - [1-?] = {Description}\0{URL} - */ - if (frame.size < 2) { - return undefined; - } - let index = 1; - const description = utf8ArrayToStr(frame.data.subarray(index), true); - index += description.length + 1; - const value = utf8ArrayToStr(frame.data.subarray(index)); - return { - key: frame.type, - info: description, - data: value - }; - } - /* - Format: - [0-?] = {URL} - */ - const url = utf8ArrayToStr(frame.data); - return { - key: frame.type, - data: url - }; -}; -const readTimeStamp = timeStampFrame => { - if (timeStampFrame.data.byteLength === 8) { - const data = new Uint8Array(timeStampFrame.data); - // timestamp is 33 bit expressed as a big-endian eight-octet number, - // with the upper 31 bits set to zero. - const pts33Bit = data[3] & 0x1; - let timestamp = (data[4] << 23) + (data[5] << 15) + (data[6] << 7) + data[7]; - timestamp /= 45; - if (pts33Bit) { - timestamp += 47721858.84; - } // 2^32 / 90 - - return Math.round(timestamp); - } - return undefined; -}; - -// http://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript/22373197 -// http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt -/* utf.js - UTF-8 <=> UTF-16 convertion - * - * Copyright (C) 1999 Masanao Izumo - * Version: 1.0 - * LastModified: Dec 25 1999 - * This library is free. You can redistribute it and/or modify it. - */ -const utf8ArrayToStr = (array, exitOnNull = false) => { - const decoder = getTextDecoder(); - if (decoder) { - const decoded = decoder.decode(array); - if (exitOnNull) { - // grab up to the first null - const idx = decoded.indexOf('\0'); - return idx !== -1 ? decoded.substring(0, idx) : decoded; - } - - // remove any null characters - return decoded.replace(/\0/g, ''); - } - const len = array.length; - let c; - let char2; - let char3; - let out = ''; - let i = 0; - while (i < len) { - c = array[i++]; - if (c === 0x00 && exitOnNull) { - return out; - } else if (c === 0x00 || c === 0x03) { - // If the character is 3 (END_OF_TEXT) or 0 (NULL) then skip it - continue; - } - switch (c >> 4) { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - // 0xxxxxxx - out += String.fromCharCode(c); - break; - case 12: - case 13: - // 110x xxxx 10xx xxxx - char2 = array[i++]; - out += String.fromCharCode((c & 0x1f) << 6 | char2 & 0x3f); - break; - case 14: - // 1110 xxxx 10xx xxxx 10xx xxxx - char2 = array[i++]; - char3 = array[i++]; - out += String.fromCharCode((c & 0x0f) << 12 | (char2 & 0x3f) << 6 | (char3 & 0x3f) << 0); - break; - } - } - return out; -}; -let decoder; -function getTextDecoder() { - // On Play Station 4, TextDecoder is defined but partially implemented. - // Manual decoding option is preferable - if (navigator.userAgent.includes('PlayStation 4')) { - return; - } - if (!decoder && typeof self.TextDecoder !== 'undefined') { - decoder = new self.TextDecoder('utf-8'); - } - return decoder; -} - -/** - * hex dump helper class - */ - -const Hex = { - hexDump: function (array) { - let str = ''; - for (let i = 0; i < array.length; i++) { - let h = array[i].toString(16); - if (h.length < 2) { - h = '0' + h; - } - str += h; - } - return str; - } -}; - -const UINT32_MAX$1 = Math.pow(2, 32) - 1; -const push = [].push; - -// We are using fixed track IDs for driving the MP4 remuxer -// instead of following the TS PIDs. -// There is no reason not to do this and some browsers/SourceBuffer-demuxers -// may not like if there are TrackID "switches" -// See https://github.com/video-dev/hls.js/issues/1331 -// Here we are mapping our internal track types to constant MP4 track IDs -// With MSE currently one can only have one track of each, and we are muxing -// whatever video/audio rendition in them. -const RemuxerTrackIdConfig = { - video: 1, - audio: 2, - id3: 3, - text: 4 -}; -function bin2str(data) { - return String.fromCharCode.apply(null, data); -} -function readUint16(buffer, offset) { - const val = buffer[offset] << 8 | buffer[offset + 1]; - return val < 0 ? 65536 + val : val; -} -function readUint32(buffer, offset) { - const val = readSint32(buffer, offset); - return val < 0 ? 4294967296 + val : val; -} -function readUint64(buffer, offset) { - let result = readUint32(buffer, offset); - result *= Math.pow(2, 32); - result += readUint32(buffer, offset + 4); - return result; -} -function readSint32(buffer, offset) { - return buffer[offset] << 24 | buffer[offset + 1] << 16 | buffer[offset + 2] << 8 | buffer[offset + 3]; -} -function writeUint32(buffer, offset, value) { - buffer[offset] = value >> 24; - buffer[offset + 1] = value >> 16 & 0xff; - buffer[offset + 2] = value >> 8 & 0xff; - buffer[offset + 3] = value & 0xff; -} - -// Find "moof" box -function hasMoofData(data) { - const end = data.byteLength; - for (let i = 0; i < end;) { - const size = readUint32(data, i); - if (size > 8 && data[i + 4] === 0x6d && data[i + 5] === 0x6f && data[i + 6] === 0x6f && data[i + 7] === 0x66) { - return true; - } - i = size > 1 ? i + size : end; - } - return false; -} - -// Find the data for a box specified by its path -function findBox(data, path) { - const results = []; - if (!path.length) { - // short-circuit the search for empty paths - return results; - } - const end = data.byteLength; - for (let i = 0; i < end;) { - const size = readUint32(data, i); - const type = bin2str(data.subarray(i + 4, i + 8)); - const endbox = size > 1 ? i + size : end; - if (type === path[0]) { - if (path.length === 1) { - // this is the end of the path and we've found the box we were - // looking for - results.push(data.subarray(i + 8, endbox)); - } else { - // recursively search for the next box along the path - const subresults = findBox(data.subarray(i + 8, endbox), path.slice(1)); - if (subresults.length) { - push.apply(results, subresults); - } - } - } - i = endbox; - } - - // we've finished searching all of data - return results; -} -function parseSegmentIndex(sidx) { - const references = []; - const version = sidx[0]; - - // set initial offset, we skip the reference ID (not needed) - let index = 8; - const timescale = readUint32(sidx, index); - index += 4; - let earliestPresentationTime = 0; - let firstOffset = 0; - if (version === 0) { - earliestPresentationTime = readUint32(sidx, index); - firstOffset = readUint32(sidx, index + 4); - index += 8; - } else { - earliestPresentationTime = readUint64(sidx, index); - firstOffset = readUint64(sidx, index + 8); - index += 16; - } - - // skip reserved - index += 2; - let startByte = sidx.length + firstOffset; - const referencesCount = readUint16(sidx, index); - index += 2; - for (let i = 0; i < referencesCount; i++) { - let referenceIndex = index; - const referenceInfo = readUint32(sidx, referenceIndex); - referenceIndex += 4; - const referenceSize = referenceInfo & 0x7fffffff; - const referenceType = (referenceInfo & 0x80000000) >>> 31; - if (referenceType === 1) { - logger.warn('SIDX has hierarchical references (not supported)'); - return null; - } - const subsegmentDuration = readUint32(sidx, referenceIndex); - referenceIndex += 4; - references.push({ - referenceSize, - subsegmentDuration, - // unscaled - info: { - duration: subsegmentDuration / timescale, - start: startByte, - end: startByte + referenceSize - 1 - } - }); - startByte += referenceSize; - - // Skipping 1 bit for |startsWithSap|, 3 bits for |sapType|, and 28 bits - // for |sapDelta|. - referenceIndex += 4; - - // skip to next ref - index = referenceIndex; - } - return { - earliestPresentationTime, - timescale, - version, - referencesCount, - references - }; -} - -/** - * Parses an MP4 initialization segment and extracts stream type and - * timescale values for any declared tracks. Timescale values indicate the - * number of clock ticks per second to assume for time-based values - * elsewhere in the MP4. - * - * To determine the start time of an MP4, you need two pieces of - * information: the timescale unit and the earliest base media decode - * time. Multiple timescales can be specified within an MP4 but the - * base media decode time is always expressed in the timescale from - * the media header box for the track: - * ``` - * moov > trak > mdia > mdhd.timescale - * moov > trak > mdia > hdlr - * ``` - * @param initSegment the bytes of the init segment - * @returns a hash of track type to timescale values or null if - * the init segment is malformed. - */ - -function parseInitSegment(initSegment) { - const result = []; - const traks = findBox(initSegment, ['moov', 'trak']); - for (let i = 0; i < traks.length; i++) { - const trak = traks[i]; - const tkhd = findBox(trak, ['tkhd'])[0]; - if (tkhd) { - let version = tkhd[0]; - const trackId = readUint32(tkhd, version === 0 ? 12 : 20); - const mdhd = findBox(trak, ['mdia', 'mdhd'])[0]; - if (mdhd) { - version = mdhd[0]; - const timescale = readUint32(mdhd, version === 0 ? 12 : 20); - const hdlr = findBox(trak, ['mdia', 'hdlr'])[0]; - if (hdlr) { - const hdlrType = bin2str(hdlr.subarray(8, 12)); - const type = { - soun: ElementaryStreamTypes.AUDIO, - vide: ElementaryStreamTypes.VIDEO - }[hdlrType]; - if (type) { - // Parse codec details - const stsd = findBox(trak, ['mdia', 'minf', 'stbl', 'stsd'])[0]; - const stsdData = parseStsd(stsd); - result[trackId] = { - timescale, - type - }; - result[type] = _objectSpread2({ - timescale, - id: trackId - }, stsdData); - } - } - } - } - } - const trex = findBox(initSegment, ['moov', 'mvex', 'trex']); - trex.forEach(trex => { - const trackId = readUint32(trex, 4); - const track = result[trackId]; - if (track) { - track.default = { - duration: readUint32(trex, 12), - flags: readUint32(trex, 20) - }; - } - }); - return result; -} -function parseStsd(stsd) { - const sampleEntries = stsd.subarray(8); - const sampleEntriesEnd = sampleEntries.subarray(8 + 78); - const fourCC = bin2str(sampleEntries.subarray(4, 8)); - let codec = fourCC; - const encrypted = fourCC === 'enca' || fourCC === 'encv'; - if (encrypted) { - const encBox = findBox(sampleEntries, [fourCC])[0]; - const encBoxChildren = encBox.subarray(fourCC === 'enca' ? 28 : 78); - const sinfs = findBox(encBoxChildren, ['sinf']); - sinfs.forEach(sinf => { - const schm = findBox(sinf, ['schm'])[0]; - if (schm) { - const scheme = bin2str(schm.subarray(4, 8)); - if (scheme === 'cbcs' || scheme === 'cenc') { - const frma = findBox(sinf, ['frma'])[0]; - if (frma) { - // for encrypted content codec fourCC will be in frma - codec = bin2str(frma); - } - } - } - }); - } - switch (codec) { - case 'avc1': - case 'avc2': - case 'avc3': - case 'avc4': - { - // extract profile + compatibility + level out of avcC box - const avcCBox = findBox(sampleEntriesEnd, ['avcC'])[0]; - codec += '.' + toHex(avcCBox[1]) + toHex(avcCBox[2]) + toHex(avcCBox[3]); - break; - } - case 'mp4a': - { - const codecBox = findBox(sampleEntries, [fourCC])[0]; - const esdsBox = findBox(codecBox.subarray(28), ['esds'])[0]; - if (esdsBox && esdsBox.length > 12) { - let i = 4; - // ES Descriptor tag - if (esdsBox[i++] !== 0x03) { - break; - } - i = skipBERInteger(esdsBox, i); - i += 2; // skip es_id; - const flags = esdsBox[i++]; - if (flags & 0x80) { - i += 2; // skip dependency es_id - } - if (flags & 0x40) { - i += esdsBox[i++]; // skip URL - } - // Decoder config descriptor - if (esdsBox[i++] !== 0x04) { - break; - } - i = skipBERInteger(esdsBox, i); - const objectType = esdsBox[i++]; - if (objectType === 0x40) { - codec += '.' + toHex(objectType); - } else { - break; - } - i += 12; - // Decoder specific info - if (esdsBox[i++] !== 0x05) { - break; - } - i = skipBERInteger(esdsBox, i); - const firstByte = esdsBox[i++]; - let audioObjectType = (firstByte & 0xf8) >> 3; - if (audioObjectType === 31) { - audioObjectType += 1 + ((firstByte & 0x7) << 3) + ((esdsBox[i] & 0xe0) >> 5); - } - codec += '.' + audioObjectType; - } - break; - } - case 'hvc1': - case 'hev1': - { - const hvcCBox = findBox(sampleEntriesEnd, ['hvcC'])[0]; - const profileByte = hvcCBox[1]; - const profileSpace = ['', 'A', 'B', 'C'][profileByte >> 6]; - const generalProfileIdc = profileByte & 0x1f; - const profileCompat = readUint32(hvcCBox, 2); - const tierFlag = (profileByte & 0x20) >> 5 ? 'H' : 'L'; - const levelIDC = hvcCBox[12]; - const constraintIndicator = hvcCBox.subarray(6, 12); - codec += '.' + profileSpace + generalProfileIdc; - codec += '.' + profileCompat.toString(16).toUpperCase(); - codec += '.' + tierFlag + levelIDC; - let constraintString = ''; - for (let i = constraintIndicator.length; i--;) { - const byte = constraintIndicator[i]; - if (byte || constraintString) { - const encodedByte = byte.toString(16).toUpperCase(); - constraintString = '.' + encodedByte + constraintString; - } - } - codec += constraintString; - break; - } - case 'dvh1': - case 'dvhe': - { - const dvcCBox = findBox(sampleEntriesEnd, ['dvcC'])[0]; - const profile = dvcCBox[2] >> 1 & 0x7f; - const level = dvcCBox[2] << 5 & 0x20 | dvcCBox[3] >> 3 & 0x1f; - codec += '.' + addLeadingZero(profile) + '.' + addLeadingZero(level); - break; - } - case 'vp09': - { - const vpcCBox = findBox(sampleEntriesEnd, ['vpcC'])[0]; - const profile = vpcCBox[4]; - const level = vpcCBox[5]; - const bitDepth = vpcCBox[6] >> 4 & 0x0f; - codec += '.' + addLeadingZero(profile) + '.' + addLeadingZero(level) + '.' + addLeadingZero(bitDepth); - break; - } - case 'av01': - { - const av1CBox = findBox(sampleEntriesEnd, ['av1C'])[0]; - const profile = av1CBox[1] >>> 5; - const level = av1CBox[1] & 0x1f; - const tierFlag = av1CBox[2] >>> 7 ? 'H' : 'M'; - const highBitDepth = (av1CBox[2] & 0x40) >> 6; - const twelveBit = (av1CBox[2] & 0x20) >> 5; - const bitDepth = profile === 2 && highBitDepth ? twelveBit ? 12 : 10 : highBitDepth ? 10 : 8; - const monochrome = (av1CBox[2] & 0x10) >> 4; - const chromaSubsamplingX = (av1CBox[2] & 0x08) >> 3; - const chromaSubsamplingY = (av1CBox[2] & 0x04) >> 2; - const chromaSamplePosition = av1CBox[2] & 0x03; - // TODO: parse color_description_present_flag - // default it to BT.709/limited range for now - // more info https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox-syntax - const colorPrimaries = 1; - const transferCharacteristics = 1; - const matrixCoefficients = 1; - const videoFullRangeFlag = 0; - codec += '.' + profile + '.' + addLeadingZero(level) + tierFlag + '.' + addLeadingZero(bitDepth) + '.' + monochrome + '.' + chromaSubsamplingX + chromaSubsamplingY + chromaSamplePosition + '.' + addLeadingZero(colorPrimaries) + '.' + addLeadingZero(transferCharacteristics) + '.' + addLeadingZero(matrixCoefficients) + '.' + videoFullRangeFlag; - break; - } - } - return { - codec, - encrypted - }; -} -function skipBERInteger(bytes, i) { - const limit = i + 5; - while (bytes[i++] & 0x80 && i < limit) {} - return i; -} -function toHex(x) { - return ('0' + x.toString(16).toUpperCase()).slice(-2); -} -function addLeadingZero(num) { - return (num < 10 ? '0' : '') + num; -} -function patchEncyptionData(initSegment, decryptdata) { - if (!initSegment || !decryptdata) { - return initSegment; - } - const keyId = decryptdata.keyId; - if (keyId && decryptdata.isCommonEncryption) { - const traks = findBox(initSegment, ['moov', 'trak']); - traks.forEach(trak => { - const stsd = findBox(trak, ['mdia', 'minf', 'stbl', 'stsd'])[0]; - - // skip the sample entry count - const sampleEntries = stsd.subarray(8); - let encBoxes = findBox(sampleEntries, ['enca']); - const isAudio = encBoxes.length > 0; - if (!isAudio) { - encBoxes = findBox(sampleEntries, ['encv']); - } - encBoxes.forEach(enc => { - const encBoxChildren = isAudio ? enc.subarray(28) : enc.subarray(78); - const sinfBoxes = findBox(encBoxChildren, ['sinf']); - sinfBoxes.forEach(sinf => { - const tenc = parseSinf(sinf); - if (tenc) { - // Look for default key id (keyID offset is always 8 within the tenc box): - const tencKeyId = tenc.subarray(8, 24); - if (!tencKeyId.some(b => b !== 0)) { - logger.log(`[eme] Patching keyId in 'enc${isAudio ? 'a' : 'v'}>sinf>>tenc' box: ${Hex.hexDump(tencKeyId)} -> ${Hex.hexDump(keyId)}`); - tenc.set(keyId, 8); - } - } - }); - }); - }); - } - return initSegment; -} -function parseSinf(sinf) { - const schm = findBox(sinf, ['schm'])[0]; - if (schm) { - const scheme = bin2str(schm.subarray(4, 8)); - if (scheme === 'cbcs' || scheme === 'cenc') { - return findBox(sinf, ['schi', 'tenc'])[0]; - } - } - return null; -} - -/** - * Determine the base media decode start time, in seconds, for an MP4 - * fragment. If multiple fragments are specified, the earliest time is - * returned. - * - * The base media decode time can be parsed from track fragment - * metadata: - * ``` - * moof > traf > tfdt.baseMediaDecodeTime - * ``` - * It requires the timescale value from the mdhd to interpret. - * - * @param initData - a hash of track type to timescale values - * @param fmp4 - the bytes of the mp4 fragment - * @returns the earliest base media decode start time for the - * fragment, in seconds - */ -function getStartDTS(initData, fmp4) { - // we need info from two children of each track fragment box - return findBox(fmp4, ['moof', 'traf']).reduce((result, traf) => { - const tfdt = findBox(traf, ['tfdt'])[0]; - const version = tfdt[0]; - const start = findBox(traf, ['tfhd']).reduce((result, tfhd) => { - // get the track id from the tfhd - const id = readUint32(tfhd, 4); - const track = initData[id]; - if (track) { - let baseTime = readUint32(tfdt, 4); - if (version === 1) { - // If value is too large, assume signed 64-bit. Negative track fragment decode times are invalid, but they exist in the wild. - // This prevents large values from being used for initPTS, which can cause playlist sync issues. - // https://github.com/video-dev/hls.js/issues/5303 - if (baseTime === UINT32_MAX$1) { - logger.warn(`[mp4-demuxer]: Ignoring assumed invalid signed 64-bit track fragment decode time`); - return result; - } - baseTime *= UINT32_MAX$1 + 1; - baseTime += readUint32(tfdt, 8); - } - // assume a 90kHz clock if no timescale was specified - const scale = track.timescale || 90e3; - // convert base time to seconds - const startTime = baseTime / scale; - if (isFiniteNumber(startTime) && (result === null || startTime < result)) { - return startTime; - } - } - return result; - }, null); - if (start !== null && isFiniteNumber(start) && (result === null || start < result)) { - return start; - } - return result; - }, null); -} - -/* - For Reference: - aligned(8) class TrackFragmentHeaderBox - extends FullBox(‘tfhd’, 0, tf_flags){ - unsigned int(32) track_ID; - // all the following are optional fields - unsigned int(64) base_data_offset; - unsigned int(32) sample_description_index; - unsigned int(32) default_sample_duration; - unsigned int(32) default_sample_size; - unsigned int(32) default_sample_flags - } - */ -function getDuration(data, initData) { - let rawDuration = 0; - let videoDuration = 0; - let audioDuration = 0; - const trafs = findBox(data, ['moof', 'traf']); - for (let i = 0; i < trafs.length; i++) { - const traf = trafs[i]; - // There is only one tfhd & trun per traf - // This is true for CMAF style content, and we should perhaps check the ftyp - // and only look for a single trun then, but for ISOBMFF we should check - // for multiple track runs. - const tfhd = findBox(traf, ['tfhd'])[0]; - // get the track id from the tfhd - const id = readUint32(tfhd, 4); - const track = initData[id]; - if (!track) { - continue; - } - const trackDefault = track.default; - const tfhdFlags = readUint32(tfhd, 0) | (trackDefault == null ? void 0 : trackDefault.flags); - let sampleDuration = trackDefault == null ? void 0 : trackDefault.duration; - if (tfhdFlags & 0x000008) { - // 0x000008 indicates the presence of the default_sample_duration field - if (tfhdFlags & 0x000002) { - // 0x000002 indicates the presence of the sample_description_index field, which precedes default_sample_duration - // If present, the default_sample_duration exists at byte offset 12 - sampleDuration = readUint32(tfhd, 12); - } else { - // Otherwise, the duration is at byte offset 8 - sampleDuration = readUint32(tfhd, 8); - } - } - // assume a 90kHz clock if no timescale was specified - const timescale = track.timescale || 90e3; - const truns = findBox(traf, ['trun']); - for (let j = 0; j < truns.length; j++) { - rawDuration = computeRawDurationFromSamples(truns[j]); - if (!rawDuration && sampleDuration) { - const sampleCount = readUint32(truns[j], 4); - rawDuration = sampleDuration * sampleCount; - } - if (track.type === ElementaryStreamTypes.VIDEO) { - videoDuration += rawDuration / timescale; - } else if (track.type === ElementaryStreamTypes.AUDIO) { - audioDuration += rawDuration / timescale; - } - } - } - if (videoDuration === 0 && audioDuration === 0) { - // If duration samples are not available in the traf use sidx subsegment_duration - let sidxMinStart = Infinity; - let sidxMaxEnd = 0; - let sidxDuration = 0; - const sidxs = findBox(data, ['sidx']); - for (let i = 0; i < sidxs.length; i++) { - const sidx = parseSegmentIndex(sidxs[i]); - if (sidx != null && sidx.references) { - sidxMinStart = Math.min(sidxMinStart, sidx.earliestPresentationTime / sidx.timescale); - const subSegmentDuration = sidx.references.reduce((dur, ref) => dur + ref.info.duration || 0, 0); - sidxMaxEnd = Math.max(sidxMaxEnd, subSegmentDuration + sidx.earliestPresentationTime / sidx.timescale); - sidxDuration = sidxMaxEnd - sidxMinStart; - } - } - if (sidxDuration && isFiniteNumber(sidxDuration)) { - return sidxDuration; - } - } - if (videoDuration) { - return videoDuration; - } - return audioDuration; -} - -/* - For Reference: - aligned(8) class TrackRunBox - extends FullBox(‘trun’, version, tr_flags) { - unsigned int(32) sample_count; - // the following are optional fields - signed int(32) data_offset; - unsigned int(32) first_sample_flags; - // all fields in the following array are optional - { - unsigned int(32) sample_duration; - unsigned int(32) sample_size; - unsigned int(32) sample_flags - if (version == 0) - { unsigned int(32) - else - { signed int(32) - }[ sample_count ] - } - */ -function computeRawDurationFromSamples(trun) { - const flags = readUint32(trun, 0); - // Flags are at offset 0, non-optional sample_count is at offset 4. Therefore we start 8 bytes in. - // Each field is an int32, which is 4 bytes - let offset = 8; - // data-offset-present flag - if (flags & 0x000001) { - offset += 4; - } - // first-sample-flags-present flag - if (flags & 0x000004) { - offset += 4; - } - let duration = 0; - const sampleCount = readUint32(trun, 4); - for (let i = 0; i < sampleCount; i++) { - // sample-duration-present flag - if (flags & 0x000100) { - const sampleDuration = readUint32(trun, offset); - duration += sampleDuration; - offset += 4; - } - // sample-size-present flag - if (flags & 0x000200) { - offset += 4; - } - // sample-flags-present flag - if (flags & 0x000400) { - offset += 4; - } - // sample-composition-time-offsets-present flag - if (flags & 0x000800) { - offset += 4; - } - } - return duration; -} -function offsetStartDTS(initData, fmp4, timeOffset) { - findBox(fmp4, ['moof', 'traf']).forEach(traf => { - findBox(traf, ['tfhd']).forEach(tfhd => { - // get the track id from the tfhd - const id = readUint32(tfhd, 4); - const track = initData[id]; - if (!track) { - return; - } - // assume a 90kHz clock if no timescale was specified - const timescale = track.timescale || 90e3; - // get the base media decode time from the tfdt - findBox(traf, ['tfdt']).forEach(tfdt => { - const version = tfdt[0]; - const offset = timeOffset * timescale; - if (offset) { - let baseMediaDecodeTime = readUint32(tfdt, 4); - if (version === 0) { - baseMediaDecodeTime -= offset; - baseMediaDecodeTime = Math.max(baseMediaDecodeTime, 0); - writeUint32(tfdt, 4, baseMediaDecodeTime); - } else { - baseMediaDecodeTime *= Math.pow(2, 32); - baseMediaDecodeTime += readUint32(tfdt, 8); - baseMediaDecodeTime -= offset; - baseMediaDecodeTime = Math.max(baseMediaDecodeTime, 0); - const upper = Math.floor(baseMediaDecodeTime / (UINT32_MAX$1 + 1)); - const lower = Math.floor(baseMediaDecodeTime % (UINT32_MAX$1 + 1)); - writeUint32(tfdt, 4, upper); - writeUint32(tfdt, 8, lower); - } - } - }); - }); - }); -} - -// TODO: Check if the last moof+mdat pair is part of the valid range -function segmentValidRange(data) { - const segmentedRange = { - valid: null, - remainder: null - }; - const moofs = findBox(data, ['moof']); - if (moofs.length < 2) { - segmentedRange.remainder = data; - return segmentedRange; - } - const last = moofs[moofs.length - 1]; - // Offset by 8 bytes; findBox offsets the start by as much - segmentedRange.valid = sliceUint8(data, 0, last.byteOffset - 8); - segmentedRange.remainder = sliceUint8(data, last.byteOffset - 8); - return segmentedRange; -} -function appendUint8Array(data1, data2) { - const temp = new Uint8Array(data1.length + data2.length); - temp.set(data1); - temp.set(data2, data1.length); - return temp; -} -function parseSamples(timeOffset, track) { - const seiSamples = []; - const videoData = track.samples; - const timescale = track.timescale; - const trackId = track.id; - let isHEVCFlavor = false; - const moofs = findBox(videoData, ['moof']); - moofs.map(moof => { - const moofOffset = moof.byteOffset - 8; - const trafs = findBox(moof, ['traf']); - trafs.map(traf => { - // get the base media decode time from the tfdt - const baseTime = findBox(traf, ['tfdt']).map(tfdt => { - const version = tfdt[0]; - let result = readUint32(tfdt, 4); - if (version === 1) { - result *= Math.pow(2, 32); - result += readUint32(tfdt, 8); - } - return result / timescale; - })[0]; - if (baseTime !== undefined) { - timeOffset = baseTime; - } - return findBox(traf, ['tfhd']).map(tfhd => { - const id = readUint32(tfhd, 4); - const tfhdFlags = readUint32(tfhd, 0) & 0xffffff; - const baseDataOffsetPresent = (tfhdFlags & 0x000001) !== 0; - const sampleDescriptionIndexPresent = (tfhdFlags & 0x000002) !== 0; - const defaultSampleDurationPresent = (tfhdFlags & 0x000008) !== 0; - let defaultSampleDuration = 0; - const defaultSampleSizePresent = (tfhdFlags & 0x000010) !== 0; - let defaultSampleSize = 0; - const defaultSampleFlagsPresent = (tfhdFlags & 0x000020) !== 0; - let tfhdOffset = 8; - if (id === trackId) { - if (baseDataOffsetPresent) { - tfhdOffset += 8; - } - if (sampleDescriptionIndexPresent) { - tfhdOffset += 4; - } - if (defaultSampleDurationPresent) { - defaultSampleDuration = readUint32(tfhd, tfhdOffset); - tfhdOffset += 4; - } - if (defaultSampleSizePresent) { - defaultSampleSize = readUint32(tfhd, tfhdOffset); - tfhdOffset += 4; - } - if (defaultSampleFlagsPresent) { - tfhdOffset += 4; - } - if (track.type === 'video') { - isHEVCFlavor = isHEVC(track.codec); - } - findBox(traf, ['trun']).map(trun => { - const version = trun[0]; - const flags = readUint32(trun, 0) & 0xffffff; - const dataOffsetPresent = (flags & 0x000001) !== 0; - let dataOffset = 0; - const firstSampleFlagsPresent = (flags & 0x000004) !== 0; - const sampleDurationPresent = (flags & 0x000100) !== 0; - let sampleDuration = 0; - const sampleSizePresent = (flags & 0x000200) !== 0; - let sampleSize = 0; - const sampleFlagsPresent = (flags & 0x000400) !== 0; - const sampleCompositionOffsetsPresent = (flags & 0x000800) !== 0; - let compositionOffset = 0; - const sampleCount = readUint32(trun, 4); - let trunOffset = 8; // past version, flags, and sample count - - if (dataOffsetPresent) { - dataOffset = readUint32(trun, trunOffset); - trunOffset += 4; - } - if (firstSampleFlagsPresent) { - trunOffset += 4; - } - let sampleOffset = dataOffset + moofOffset; - for (let ix = 0; ix < sampleCount; ix++) { - if (sampleDurationPresent) { - sampleDuration = readUint32(trun, trunOffset); - trunOffset += 4; - } else { - sampleDuration = defaultSampleDuration; - } - if (sampleSizePresent) { - sampleSize = readUint32(trun, trunOffset); - trunOffset += 4; - } else { - sampleSize = defaultSampleSize; - } - if (sampleFlagsPresent) { - trunOffset += 4; - } - if (sampleCompositionOffsetsPresent) { - if (version === 0) { - compositionOffset = readUint32(trun, trunOffset); - } else { - compositionOffset = readSint32(trun, trunOffset); - } - trunOffset += 4; - } - if (track.type === ElementaryStreamTypes.VIDEO) { - let naluTotalSize = 0; - while (naluTotalSize < sampleSize) { - const naluSize = readUint32(videoData, sampleOffset); - sampleOffset += 4; - if (isSEIMessage(isHEVCFlavor, videoData[sampleOffset])) { - const data = videoData.subarray(sampleOffset, sampleOffset + naluSize); - parseSEIMessageFromNALu(data, isHEVCFlavor ? 2 : 1, timeOffset + compositionOffset / timescale, seiSamples); - } - sampleOffset += naluSize; - naluTotalSize += naluSize + 4; - } - } - timeOffset += sampleDuration / timescale; - } - }); - } - }); - }); - }); - return seiSamples; -} -function isHEVC(codec) { - if (!codec) { - return false; - } - const delimit = codec.indexOf('.'); - const baseCodec = delimit < 0 ? codec : codec.substring(0, delimit); - return baseCodec === 'hvc1' || baseCodec === 'hev1' || - // Dolby Vision - baseCodec === 'dvh1' || baseCodec === 'dvhe'; -} -function isSEIMessage(isHEVCFlavor, naluHeader) { - if (isHEVCFlavor) { - const naluType = naluHeader >> 1 & 0x3f; - return naluType === 39 || naluType === 40; - } else { - const naluType = naluHeader & 0x1f; - return naluType === 6; - } -} -function parseSEIMessageFromNALu(unescapedData, headerSize, pts, samples) { - const data = discardEPB(unescapedData); - let seiPtr = 0; - // skip nal header - seiPtr += headerSize; - let payloadType = 0; - let payloadSize = 0; - let b = 0; - while (seiPtr < data.length) { - payloadType = 0; - do { - if (seiPtr >= data.length) { - break; - } - b = data[seiPtr++]; - payloadType += b; - } while (b === 0xff); - - // Parse payload size. - payloadSize = 0; - do { - if (seiPtr >= data.length) { - break; - } - b = data[seiPtr++]; - payloadSize += b; - } while (b === 0xff); - const leftOver = data.length - seiPtr; - // Create a variable to process the payload - let payPtr = seiPtr; - - // Increment the seiPtr to the end of the payload - if (payloadSize < leftOver) { - seiPtr += payloadSize; - } else if (payloadSize > leftOver) { - // Some type of corruption has happened? - logger.error(`Malformed SEI payload. ${payloadSize} is too small, only ${leftOver} bytes left to parse.`); - // We might be able to parse some data, but let's be safe and ignore it. - break; - } - if (payloadType === 4) { - const countryCode = data[payPtr++]; - if (countryCode === 181) { - const providerCode = readUint16(data, payPtr); - payPtr += 2; - if (providerCode === 49) { - const userStructure = readUint32(data, payPtr); - payPtr += 4; - if (userStructure === 0x47413934) { - const userDataType = data[payPtr++]; - - // Raw CEA-608 bytes wrapped in CEA-708 packet - if (userDataType === 3) { - const firstByte = data[payPtr++]; - const totalCCs = 0x1f & firstByte; - const enabled = 0x40 & firstByte; - const totalBytes = enabled ? 2 + totalCCs * 3 : 0; - const byteArray = new Uint8Array(totalBytes); - if (enabled) { - byteArray[0] = firstByte; - for (let i = 1; i < totalBytes; i++) { - byteArray[i] = data[payPtr++]; - } - } - samples.push({ - type: userDataType, - payloadType, - pts, - bytes: byteArray - }); - } - } - } - } - } else if (payloadType === 5) { - if (payloadSize > 16) { - const uuidStrArray = []; - for (let i = 0; i < 16; i++) { - const _b = data[payPtr++].toString(16); - uuidStrArray.push(_b.length == 1 ? '0' + _b : _b); - if (i === 3 || i === 5 || i === 7 || i === 9) { - uuidStrArray.push('-'); - } - } - const length = payloadSize - 16; - const userDataBytes = new Uint8Array(length); - for (let i = 0; i < length; i++) { - userDataBytes[i] = data[payPtr++]; - } - samples.push({ - payloadType, - pts, - uuid: uuidStrArray.join(''), - userData: utf8ArrayToStr(userDataBytes), - userDataBytes - }); - } - } - } -} - -/** - * remove Emulation Prevention bytes from a RBSP - */ -function discardEPB(data) { - const length = data.byteLength; - const EPBPositions = []; - let i = 1; - - // Find all `Emulation Prevention Bytes` - while (i < length - 2) { - if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) { - EPBPositions.push(i + 2); - i += 2; - } else { - i++; - } - } - - // If no Emulation Prevention Bytes were found just return the original - // array - if (EPBPositions.length === 0) { - return data; - } - - // Create a new array to hold the NAL unit data - const newLength = length - EPBPositions.length; - const newData = new Uint8Array(newLength); - let sourceIndex = 0; - for (i = 0; i < newLength; sourceIndex++, i++) { - if (sourceIndex === EPBPositions[0]) { - // Skip this byte - sourceIndex++; - // Remove this position index - EPBPositions.shift(); - } - newData[i] = data[sourceIndex]; - } - return newData; -} -function parseEmsg(data) { - const version = data[0]; - let schemeIdUri = ''; - let value = ''; - let timeScale = 0; - let presentationTimeDelta = 0; - let presentationTime = 0; - let eventDuration = 0; - let id = 0; - let offset = 0; - if (version === 0) { - while (bin2str(data.subarray(offset, offset + 1)) !== '\0') { - schemeIdUri += bin2str(data.subarray(offset, offset + 1)); - offset += 1; - } - schemeIdUri += bin2str(data.subarray(offset, offset + 1)); - offset += 1; - while (bin2str(data.subarray(offset, offset + 1)) !== '\0') { - value += bin2str(data.subarray(offset, offset + 1)); - offset += 1; - } - value += bin2str(data.subarray(offset, offset + 1)); - offset += 1; - timeScale = readUint32(data, 12); - presentationTimeDelta = readUint32(data, 16); - eventDuration = readUint32(data, 20); - id = readUint32(data, 24); - offset = 28; - } else if (version === 1) { - offset += 4; - timeScale = readUint32(data, offset); - offset += 4; - const leftPresentationTime = readUint32(data, offset); - offset += 4; - const rightPresentationTime = readUint32(data, offset); - offset += 4; - presentationTime = 2 ** 32 * leftPresentationTime + rightPresentationTime; - if (!isSafeInteger(presentationTime)) { - presentationTime = Number.MAX_SAFE_INTEGER; - logger.warn('Presentation time exceeds safe integer limit and wrapped to max safe integer in parsing emsg box'); - } - eventDuration = readUint32(data, offset); - offset += 4; - id = readUint32(data, offset); - offset += 4; - while (bin2str(data.subarray(offset, offset + 1)) !== '\0') { - schemeIdUri += bin2str(data.subarray(offset, offset + 1)); - offset += 1; - } - schemeIdUri += bin2str(data.subarray(offset, offset + 1)); - offset += 1; - while (bin2str(data.subarray(offset, offset + 1)) !== '\0') { - value += bin2str(data.subarray(offset, offset + 1)); - offset += 1; - } - value += bin2str(data.subarray(offset, offset + 1)); - offset += 1; - } - const payload = data.subarray(offset, data.byteLength); - return { - schemeIdUri, - value, - timeScale, - presentationTime, - presentationTimeDelta, - eventDuration, - id, - payload - }; -} -function mp4Box(type, ...payload) { - const len = payload.length; - let size = 8; - let i = len; - while (i--) { - size += payload[i].byteLength; - } - const result = new Uint8Array(size); - result[0] = size >> 24 & 0xff; - result[1] = size >> 16 & 0xff; - result[2] = size >> 8 & 0xff; - result[3] = size & 0xff; - result.set(type, 4); - for (i = 0, size = 8; i < len; i++) { - result.set(payload[i], size); - size += payload[i].byteLength; - } - return result; -} -function mp4pssh(systemId, keyids, data) { - if (systemId.byteLength !== 16) { - throw new RangeError('Invalid system id'); - } - let version; - let kids; - if (keyids) { - version = 1; - kids = new Uint8Array(keyids.length * 16); - for (let ix = 0; ix < keyids.length; ix++) { - const k = keyids[ix]; // uint8array - if (k.byteLength !== 16) { - throw new RangeError('Invalid key'); - } - kids.set(k, ix * 16); - } - } else { - version = 0; - kids = new Uint8Array(); - } - let kidCount; - if (version > 0) { - kidCount = new Uint8Array(4); - if (keyids.length > 0) { - new DataView(kidCount.buffer).setUint32(0, keyids.length, false); - } - } else { - kidCount = new Uint8Array(); - } - const dataSize = new Uint8Array(4); - if (data && data.byteLength > 0) { - new DataView(dataSize.buffer).setUint32(0, data.byteLength, false); - } - return mp4Box([112, 115, 115, 104], new Uint8Array([version, 0x00, 0x00, 0x00 // Flags - ]), systemId, - // 16 bytes - kidCount, kids, dataSize, data || new Uint8Array()); -} -function parseMultiPssh(initData) { - const results = []; - if (initData instanceof ArrayBuffer) { - const length = initData.byteLength; - let offset = 0; - while (offset + 32 < length) { - const view = new DataView(initData, offset); - const pssh = parsePssh(view); - results.push(pssh); - offset += pssh.size; - } - } - return results; -} -function parsePssh(view) { - const size = view.getUint32(0); - const offset = view.byteOffset; - const length = view.byteLength; - if (length < size) { - return { - offset, - size: length - }; - } - const type = view.getUint32(4); - if (type !== 0x70737368) { - return { - offset, - size - }; - } - const version = view.getUint32(8) >>> 24; - if (version !== 0 && version !== 1) { - return { - offset, - size - }; - } - const buffer = view.buffer; - const systemId = Hex.hexDump(new Uint8Array(buffer, offset + 12, 16)); - const dataSizeOrKidCount = view.getUint32(28); - let kids = null; - let data = null; - if (version === 0) { - if (size - 32 < dataSizeOrKidCount || dataSizeOrKidCount < 22) { - return { - offset, - size - }; - } - data = new Uint8Array(buffer, offset + 32, dataSizeOrKidCount); - } else if (version === 1) { - if (!dataSizeOrKidCount || length < offset + 32 + dataSizeOrKidCount * 16 + 16) { - return { - offset, - size - }; - } - kids = []; - for (let i = 0; i < dataSizeOrKidCount; i++) { - kids.push(new Uint8Array(buffer, offset + 32 + i * 16, 16)); - } - } - return { - version, - systemId, - kids, - data, - offset, - size - }; -} - -let keyUriToKeyIdMap = {}; -class LevelKey { - static clearKeyUriToKeyIdMap() { - keyUriToKeyIdMap = {}; - } - constructor(method, uri, format, formatversions = [1], iv = null) { - this.uri = void 0; - this.method = void 0; - this.keyFormat = void 0; - this.keyFormatVersions = void 0; - this.encrypted = void 0; - this.isCommonEncryption = void 0; - this.iv = null; - this.key = null; - this.keyId = null; - this.pssh = null; - this.method = method; - this.uri = uri; - this.keyFormat = format; - this.keyFormatVersions = formatversions; - this.iv = iv; - this.encrypted = method ? method !== 'NONE' : false; - this.isCommonEncryption = this.encrypted && method !== 'AES-128'; - } - isSupported() { - // If it's Segment encryption or No encryption, just select that key system - if (this.method) { - if (this.method === 'AES-128' || this.method === 'NONE') { - return true; - } - if (this.keyFormat === 'identity') { - // Maintain support for clear SAMPLE-AES with MPEG-3 TS - return this.method === 'SAMPLE-AES'; - } else { - switch (this.keyFormat) { - case KeySystemFormats.FAIRPLAY: - case KeySystemFormats.WIDEVINE: - case KeySystemFormats.PLAYREADY: - case KeySystemFormats.CLEARKEY: - return ['ISO-23001-7', 'SAMPLE-AES', 'SAMPLE-AES-CENC', 'SAMPLE-AES-CTR'].indexOf(this.method) !== -1; - } - } - } - return false; - } - getDecryptData(sn) { - if (!this.encrypted || !this.uri) { - return null; - } - if (this.method === 'AES-128' && this.uri && !this.iv) { - if (typeof sn !== 'number') { - // We are fetching decryption data for a initialization segment - // If the segment was encrypted with AES-128 - // It must have an IV defined. We cannot substitute the Segment Number in. - if (this.method === 'AES-128' && !this.iv) { - logger.warn(`missing IV for initialization segment with method="${this.method}" - compliance issue`); - } - // Explicitly set sn to resulting value from implicit conversions 'initSegment' values for IV generation. - sn = 0; - } - const iv = createInitializationVector(sn); - const decryptdata = new LevelKey(this.method, this.uri, 'identity', this.keyFormatVersions, iv); - return decryptdata; - } - - // Initialize keyId if possible - const keyBytes = convertDataUriToArrayBytes(this.uri); - if (keyBytes) { - switch (this.keyFormat) { - case KeySystemFormats.WIDEVINE: - this.pssh = keyBytes; - // In case of widevine keyID is embedded in PSSH box. Read Key ID. - if (keyBytes.length >= 22) { - this.keyId = keyBytes.subarray(keyBytes.length - 22, keyBytes.length - 6); - } - break; - case KeySystemFormats.PLAYREADY: - { - const PlayReadyKeySystemUUID = new Uint8Array([0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42, 0x86, 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95]); - this.pssh = mp4pssh(PlayReadyKeySystemUUID, null, keyBytes); - const keyBytesUtf16 = new Uint16Array(keyBytes.buffer, keyBytes.byteOffset, keyBytes.byteLength / 2); - const keyByteStr = String.fromCharCode.apply(null, Array.from(keyBytesUtf16)); - - // Parse Playready WRMHeader XML - const xmlKeyBytes = keyByteStr.substring(keyByteStr.indexOf('<'), keyByteStr.length); - const parser = new DOMParser(); - const xmlDoc = parser.parseFromString(xmlKeyBytes, 'text/xml'); - const keyData = xmlDoc.getElementsByTagName('KID')[0]; - if (keyData) { - const keyId = keyData.childNodes[0] ? keyData.childNodes[0].nodeValue : keyData.getAttribute('VALUE'); - if (keyId) { - const keyIdArray = base64Decode(keyId).subarray(0, 16); - // KID value in PRO is a base64-encoded little endian GUID interpretation of UUID - // KID value in ‘tenc’ is a big endian UUID GUID interpretation of UUID - changeEndianness(keyIdArray); - this.keyId = keyIdArray; - } - } - break; - } - default: - { - let keydata = keyBytes.subarray(0, 16); - if (keydata.length !== 16) { - const padded = new Uint8Array(16); - padded.set(keydata, 16 - keydata.length); - keydata = padded; - } - this.keyId = keydata; - break; - } - } - } - - // Default behavior: assign a new keyId for each uri - if (!this.keyId || this.keyId.byteLength !== 16) { - let keyId = keyUriToKeyIdMap[this.uri]; - if (!keyId) { - const val = Object.keys(keyUriToKeyIdMap).length % Number.MAX_SAFE_INTEGER; - keyId = new Uint8Array(16); - const dv = new DataView(keyId.buffer, 12, 4); // Just set the last 4 bytes - dv.setUint32(0, val); - keyUriToKeyIdMap[this.uri] = keyId; - } - this.keyId = keyId; - } - return this; - } -} -function createInitializationVector(segmentNumber) { - const uint8View = new Uint8Array(16); - for (let i = 12; i < 16; i++) { - uint8View[i] = segmentNumber >> 8 * (15 - i) & 0xff; - } - return uint8View; -} - -const VARIABLE_REPLACEMENT_REGEX = /\{\$([a-zA-Z0-9-_]+)\}/g; -function hasVariableReferences(str) { - return VARIABLE_REPLACEMENT_REGEX.test(str); -} -function substituteVariablesInAttributes(parsed, attr, attributeNames) { - if (parsed.variableList !== null || parsed.hasVariableRefs) { - for (let i = attributeNames.length; i--;) { - const name = attributeNames[i]; - const value = attr[name]; - if (value) { - attr[name] = substituteVariables(parsed, value); - } - } - } -} -function substituteVariables(parsed, value) { - if (parsed.variableList !== null || parsed.hasVariableRefs) { - const variableList = parsed.variableList; - return value.replace(VARIABLE_REPLACEMENT_REGEX, variableReference => { - const variableName = variableReference.substring(2, variableReference.length - 1); - const variableValue = variableList == null ? void 0 : variableList[variableName]; - if (variableValue === undefined) { - parsed.playlistParsingError || (parsed.playlistParsingError = new Error(`Missing preceding EXT-X-DEFINE tag for Variable Reference: "${variableName}"`)); - return variableReference; - } - return variableValue; - }); - } - return value; -} -function addVariableDefinition(parsed, attr, parentUrl) { - let variableList = parsed.variableList; - if (!variableList) { - parsed.variableList = variableList = {}; - } - let NAME; - let VALUE; - if ('QUERYPARAM' in attr) { - NAME = attr.QUERYPARAM; - try { - const searchParams = new self.URL(parentUrl).searchParams; - if (searchParams.has(NAME)) { - VALUE = searchParams.get(NAME); - } else { - throw new Error(`"${NAME}" does not match any query parameter in URI: "${parentUrl}"`); - } - } catch (error) { - parsed.playlistParsingError || (parsed.playlistParsingError = new Error(`EXT-X-DEFINE QUERYPARAM: ${error.message}`)); - } - } else { - NAME = attr.NAME; - VALUE = attr.VALUE; - } - if (NAME in variableList) { - parsed.playlistParsingError || (parsed.playlistParsingError = new Error(`EXT-X-DEFINE duplicate Variable Name declarations: "${NAME}"`)); - } else { - variableList[NAME] = VALUE || ''; - } -} -function importVariableDefinition(parsed, attr, sourceVariableList) { - const IMPORT = attr.IMPORT; - if (sourceVariableList && IMPORT in sourceVariableList) { - let variableList = parsed.variableList; - if (!variableList) { - parsed.variableList = variableList = {}; - } - variableList[IMPORT] = sourceVariableList[IMPORT]; - } else { - parsed.playlistParsingError || (parsed.playlistParsingError = new Error(`EXT-X-DEFINE IMPORT attribute not found in Multivariant Playlist: "${IMPORT}"`)); - } -} - -/** - * MediaSource helper - */ - -function getMediaSource(preferManagedMediaSource = true) { - if (typeof self === 'undefined') return undefined; - const mms = (preferManagedMediaSource || !self.MediaSource) && self.ManagedMediaSource; - return mms || self.MediaSource || self.WebKitMediaSource; -} -function isManagedMediaSource(source) { - return typeof self !== 'undefined' && source === self.ManagedMediaSource; -} - -// from http://mp4ra.org/codecs.html -// values indicate codec selection preference (lower is higher priority) -const sampleEntryCodesISO = { - audio: { - a3ds: 1, - 'ac-3': 0.95, - 'ac-4': 1, - alac: 0.9, - alaw: 1, - dra1: 1, - 'dts+': 1, - 'dts-': 1, - dtsc: 1, - dtse: 1, - dtsh: 1, - 'ec-3': 0.9, - enca: 1, - fLaC: 0.9, - // MP4-RA listed codec entry for FLAC - flac: 0.9, - // legacy browser codec name for FLAC - FLAC: 0.9, - // some manifests may list "FLAC" with Apple's tools - g719: 1, - g726: 1, - m4ae: 1, - mha1: 1, - mha2: 1, - mhm1: 1, - mhm2: 1, - mlpa: 1, - mp4a: 1, - 'raw ': 1, - Opus: 1, - opus: 1, - // browsers expect this to be lowercase despite MP4RA says 'Opus' - samr: 1, - sawb: 1, - sawp: 1, - sevc: 1, - sqcp: 1, - ssmv: 1, - twos: 1, - ulaw: 1 - }, - video: { - avc1: 1, - avc2: 1, - avc3: 1, - avc4: 1, - avcp: 1, - av01: 0.8, - drac: 1, - dva1: 1, - dvav: 1, - dvh1: 0.7, - dvhe: 0.7, - encv: 1, - hev1: 0.75, - hvc1: 0.75, - mjp2: 1, - mp4v: 1, - mvc1: 1, - mvc2: 1, - mvc3: 1, - mvc4: 1, - resv: 1, - rv60: 1, - s263: 1, - svc1: 1, - svc2: 1, - 'vc-1': 1, - vp08: 1, - vp09: 0.9 - }, - text: { - stpp: 1, - wvtt: 1 - } -}; -function isCodecType(codec, type) { - const typeCodes = sampleEntryCodesISO[type]; - return !!typeCodes && !!typeCodes[codec.slice(0, 4)]; -} -function areCodecsMediaSourceSupported(codecs, type, preferManagedMediaSource = true) { - return !codecs.split(',').some(codec => !isCodecMediaSourceSupported(codec, type, preferManagedMediaSource)); -} -function isCodecMediaSourceSupported(codec, type, preferManagedMediaSource = true) { - var _MediaSource$isTypeSu; - const MediaSource = getMediaSource(preferManagedMediaSource); - return (_MediaSource$isTypeSu = MediaSource == null ? void 0 : MediaSource.isTypeSupported(mimeTypeForCodec(codec, type))) != null ? _MediaSource$isTypeSu : false; -} -function mimeTypeForCodec(codec, type) { - return `${type}/mp4;codecs="${codec}"`; -} -function videoCodecPreferenceValue(videoCodec) { - if (videoCodec) { - const fourCC = videoCodec.substring(0, 4); - return sampleEntryCodesISO.video[fourCC]; - } - return 2; -} -function codecsSetSelectionPreferenceValue(codecSet) { - return codecSet.split(',').reduce((num, fourCC) => { - const preferenceValue = sampleEntryCodesISO.video[fourCC]; - if (preferenceValue) { - return (preferenceValue * 2 + num) / (num ? 3 : 2); - } - return (sampleEntryCodesISO.audio[fourCC] + num) / (num ? 2 : 1); - }, 0); -} -const CODEC_COMPATIBLE_NAMES = {}; -function getCodecCompatibleNameLower(lowerCaseCodec, preferManagedMediaSource = true) { - if (CODEC_COMPATIBLE_NAMES[lowerCaseCodec]) { - return CODEC_COMPATIBLE_NAMES[lowerCaseCodec]; - } - - // Idealy fLaC and Opus would be first (spec-compliant) but - // some browsers will report that fLaC is supported then fail. - // see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728 - const codecsToCheck = { - flac: ['flac', 'fLaC', 'FLAC'], - opus: ['opus', 'Opus'] - }[lowerCaseCodec]; - for (let i = 0; i < codecsToCheck.length; i++) { - if (isCodecMediaSourceSupported(codecsToCheck[i], 'audio', preferManagedMediaSource)) { - CODEC_COMPATIBLE_NAMES[lowerCaseCodec] = codecsToCheck[i]; - return codecsToCheck[i]; - } - } - return lowerCaseCodec; -} -const AUDIO_CODEC_REGEXP = /flac|opus/i; -function getCodecCompatibleName(codec, preferManagedMediaSource = true) { - return codec.replace(AUDIO_CODEC_REGEXP, m => getCodecCompatibleNameLower(m.toLowerCase(), preferManagedMediaSource)); -} -function pickMostCompleteCodecName(parsedCodec, levelCodec) { - // Parsing of mp4a codecs strings in mp4-tools from media is incomplete as of d8c6c7a - // so use level codec is parsed codec is unavailable or incomplete - if (parsedCodec && parsedCodec !== 'mp4a') { - return parsedCodec; - } - return levelCodec ? levelCodec.split(',')[0] : levelCodec; -} -function convertAVC1ToAVCOTI(codec) { - // Convert avc1 codec string from RFC-4281 to RFC-6381 for MediaSource.isTypeSupported - // Examples: avc1.66.30 to avc1.42001e and avc1.77.30,avc1.66.30 to avc1.4d001e,avc1.42001e. - const codecs = codec.split(','); - for (let i = 0; i < codecs.length; i++) { - const avcdata = codecs[i].split('.'); - if (avcdata.length > 2) { - let result = avcdata.shift() + '.'; - result += parseInt(avcdata.shift()).toString(16); - result += ('000' + parseInt(avcdata.shift()).toString(16)).slice(-4); - codecs[i] = result; - } - } - return codecs.join(','); -} - -const MASTER_PLAYLIST_REGEX = /#EXT-X-STREAM-INF:([^\r\n]*)(?:[\r\n](?:#[^\r\n]*)?)*([^\r\n]+)|#EXT-X-(SESSION-DATA|SESSION-KEY|DEFINE|CONTENT-STEERING|START):([^\r\n]*)[\r\n]+/g; -const MASTER_PLAYLIST_MEDIA_REGEX = /#EXT-X-MEDIA:(.*)/g; -const IS_MEDIA_PLAYLIST = /^#EXT(?:INF|-X-TARGETDURATION):/m; // Handle empty Media Playlist (first EXTINF not signaled, but TARGETDURATION present) - -const LEVEL_PLAYLIST_REGEX_FAST = new RegExp([/#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source, -// duration (#EXTINF:,), group 1 => duration, group 2 => title -/(?!#) *(\S[^\r\n]*)/.source, -// segment URI, group 3 => the URI (note newline is not eaten) -/#EXT-X-BYTERANGE:*(.+)/.source, -// next segment's byterange, group 4 => range spec (x@y) -/#EXT-X-PROGRAM-DATE-TIME:(.+)/.source, -// next segment's program date/time group 5 => the datetime spec -/#.*/.source // All other non-segment oriented tags will match with all groups empty -].join('|'), 'g'); -const LEVEL_PLAYLIST_REGEX_SLOW = new RegExp([/#(EXTM3U)/.source, /#EXT-X-(DATERANGE|DEFINE|KEY|MAP|PART|PART-INF|PLAYLIST-TYPE|PRELOAD-HINT|RENDITION-REPORT|SERVER-CONTROL|SKIP|START):(.+)/.source, /#EXT-X-(BITRATE|DISCONTINUITY-SEQUENCE|MEDIA-SEQUENCE|TARGETDURATION|VERSION): *(\d+)/.source, /#EXT-X-(DISCONTINUITY|ENDLIST|GAP|INDEPENDENT-SEGMENTS)/.source, /(#)([^:]*):(.*)/.source, /(#)(.*)(?:.*)\r?\n?/.source].join('|')); -class M3U8Parser { - static findGroup(groups, mediaGroupId) { - for (let i = 0; i < groups.length; i++) { - const group = groups[i]; - if (group.id === mediaGroupId) { - return group; - } - } - } - static resolve(url, baseUrl) { - return urlToolkitExports.buildAbsoluteURL(baseUrl, url, { - alwaysNormalize: true - }); - } - static isMediaPlaylist(str) { - return IS_MEDIA_PLAYLIST.test(str); - } - static parseMasterPlaylist(string, baseurl) { - const hasVariableRefs = hasVariableReferences(string) ; - const parsed = { - contentSteering: null, - levels: [], - playlistParsingError: null, - sessionData: null, - sessionKeys: null, - startTimeOffset: null, - variableList: null, - hasVariableRefs - }; - const levelsWithKnownCodecs = []; - MASTER_PLAYLIST_REGEX.lastIndex = 0; - let result; - while ((result = MASTER_PLAYLIST_REGEX.exec(string)) != null) { - if (result[1]) { - var _level$unknownCodecs; - // '#EXT-X-STREAM-INF' is found, parse level tag in group 1 - const attrs = new AttrList(result[1]); - { - substituteVariablesInAttributes(parsed, attrs, ['CODECS', 'SUPPLEMENTAL-CODECS', 'ALLOWED-CPC', 'PATHWAY-ID', 'STABLE-VARIANT-ID', 'AUDIO', 'VIDEO', 'SUBTITLES', 'CLOSED-CAPTIONS', 'NAME']); - } - const uri = substituteVariables(parsed, result[2]) ; - const level = { - attrs, - bitrate: attrs.decimalInteger('BANDWIDTH') || attrs.decimalInteger('AVERAGE-BANDWIDTH'), - name: attrs.NAME, - url: M3U8Parser.resolve(uri, baseurl) - }; - const resolution = attrs.decimalResolution('RESOLUTION'); - if (resolution) { - level.width = resolution.width; - level.height = resolution.height; - } - setCodecs(attrs.CODECS, level); - if (!((_level$unknownCodecs = level.unknownCodecs) != null && _level$unknownCodecs.length)) { - levelsWithKnownCodecs.push(level); - } - parsed.levels.push(level); - } else if (result[3]) { - const tag = result[3]; - const attributes = result[4]; - switch (tag) { - case 'SESSION-DATA': - { - // #EXT-X-SESSION-DATA - const sessionAttrs = new AttrList(attributes); - { - substituteVariablesInAttributes(parsed, sessionAttrs, ['DATA-ID', 'LANGUAGE', 'VALUE', 'URI']); - } - const dataId = sessionAttrs['DATA-ID']; - if (dataId) { - if (parsed.sessionData === null) { - parsed.sessionData = {}; - } - parsed.sessionData[dataId] = sessionAttrs; - } - break; - } - case 'SESSION-KEY': - { - // #EXT-X-SESSION-KEY - const sessionKey = parseKey(attributes, baseurl, parsed); - if (sessionKey.encrypted && sessionKey.isSupported()) { - if (parsed.sessionKeys === null) { - parsed.sessionKeys = []; - } - parsed.sessionKeys.push(sessionKey); - } else { - logger.warn(`[Keys] Ignoring invalid EXT-X-SESSION-KEY tag: "${attributes}"`); - } - break; - } - case 'DEFINE': - { - // #EXT-X-DEFINE - { - const variableAttributes = new AttrList(attributes); - substituteVariablesInAttributes(parsed, variableAttributes, ['NAME', 'VALUE', 'QUERYPARAM']); - addVariableDefinition(parsed, variableAttributes, baseurl); - } - break; - } - case 'CONTENT-STEERING': - { - // #EXT-X-CONTENT-STEERING - const contentSteeringAttributes = new AttrList(attributes); - { - substituteVariablesInAttributes(parsed, contentSteeringAttributes, ['SERVER-URI', 'PATHWAY-ID']); - } - parsed.contentSteering = { - uri: M3U8Parser.resolve(contentSteeringAttributes['SERVER-URI'], baseurl), - pathwayId: contentSteeringAttributes['PATHWAY-ID'] || '.' - }; - break; - } - case 'START': - { - // #EXT-X-START - parsed.startTimeOffset = parseStartTimeOffset(attributes); - break; - } - } - } - } - // Filter out levels with unknown codecs if it does not remove all levels - const stripUnknownCodecLevels = levelsWithKnownCodecs.length > 0 && levelsWithKnownCodecs.length < parsed.levels.length; - parsed.levels = stripUnknownCodecLevels ? levelsWithKnownCodecs : parsed.levels; - if (parsed.levels.length === 0) { - parsed.playlistParsingError = new Error('no levels found in manifest'); - } - return parsed; - } - static parseMasterPlaylistMedia(string, baseurl, parsed) { - let result; - const results = {}; - const levels = parsed.levels; - const groupsByType = { - AUDIO: levels.map(level => ({ - id: level.attrs.AUDIO, - audioCodec: level.audioCodec - })), - SUBTITLES: levels.map(level => ({ - id: level.attrs.SUBTITLES, - textCodec: level.textCodec - })), - 'CLOSED-CAPTIONS': [] - }; - let id = 0; - MASTER_PLAYLIST_MEDIA_REGEX.lastIndex = 0; - while ((result = MASTER_PLAYLIST_MEDIA_REGEX.exec(string)) !== null) { - const attrs = new AttrList(result[1]); - const type = attrs.TYPE; - if (type) { - const groups = groupsByType[type]; - const medias = results[type] || []; - results[type] = medias; - { - substituteVariablesInAttributes(parsed, attrs, ['URI', 'GROUP-ID', 'LANGUAGE', 'ASSOC-LANGUAGE', 'STABLE-RENDITION-ID', 'NAME', 'INSTREAM-ID', 'CHARACTERISTICS', 'CHANNELS']); - } - const lang = attrs.LANGUAGE; - const assocLang = attrs['ASSOC-LANGUAGE']; - const channels = attrs.CHANNELS; - const characteristics = attrs.CHARACTERISTICS; - const instreamId = attrs['INSTREAM-ID']; - const media = { - attrs, - bitrate: 0, - id: id++, - groupId: attrs['GROUP-ID'] || '', - name: attrs.NAME || lang || '', - type, - default: attrs.bool('DEFAULT'), - autoselect: attrs.bool('AUTOSELECT'), - forced: attrs.bool('FORCED'), - lang, - url: attrs.URI ? M3U8Parser.resolve(attrs.URI, baseurl) : '' - }; - if (assocLang) { - media.assocLang = assocLang; - } - if (channels) { - media.channels = channels; - } - if (characteristics) { - media.characteristics = characteristics; - } - if (instreamId) { - media.instreamId = instreamId; - } - if (groups != null && groups.length) { - // If there are audio or text groups signalled in the manifest, let's look for a matching codec string for this track - // If we don't find the track signalled, lets use the first audio groups codec we have - // Acting as a best guess - const groupCodec = M3U8Parser.findGroup(groups, media.groupId) || groups[0]; - assignCodec(media, groupCodec, 'audioCodec'); - assignCodec(media, groupCodec, 'textCodec'); - } - medias.push(media); - } - } - return results; - } - static parseLevelPlaylist(string, baseurl, id, type, levelUrlId, multivariantVariableList) { - const level = new LevelDetails(baseurl); - const fragments = level.fragments; - // The most recent init segment seen (applies to all subsequent segments) - let currentInitSegment = null; - let currentSN = 0; - let currentPart = 0; - let totalduration = 0; - let discontinuityCounter = 0; - let prevFrag = null; - let frag = new Fragment(type, baseurl); - let result; - let i; - let levelkeys; - let firstPdtIndex = -1; - let createNextFrag = false; - let nextByteRange = null; - LEVEL_PLAYLIST_REGEX_FAST.lastIndex = 0; - level.m3u8 = string; - level.hasVariableRefs = hasVariableReferences(string) ; - while ((result = LEVEL_PLAYLIST_REGEX_FAST.exec(string)) !== null) { - if (createNextFrag) { - createNextFrag = false; - frag = new Fragment(type, baseurl); - // setup the next fragment for part loading - frag.start = totalduration; - frag.sn = currentSN; - frag.cc = discontinuityCounter; - frag.level = id; - if (currentInitSegment) { - frag.initSegment = currentInitSegment; - frag.rawProgramDateTime = currentInitSegment.rawProgramDateTime; - currentInitSegment.rawProgramDateTime = null; - if (nextByteRange) { - frag.setByteRange(nextByteRange); - nextByteRange = null; - } - } - } - const duration = result[1]; - if (duration) { - // INF - frag.duration = parseFloat(duration); - // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 - const title = (' ' + result[2]).slice(1); - frag.title = title || null; - frag.tagList.push(title ? ['INF', duration, title] : ['INF', duration]); - } else if (result[3]) { - // url - if (isFiniteNumber(frag.duration)) { - frag.start = totalduration; - if (levelkeys) { - setFragLevelKeys(frag, levelkeys, level); - } - frag.sn = currentSN; - frag.level = id; - frag.cc = discontinuityCounter; - fragments.push(frag); - // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 - const uri = (' ' + result[3]).slice(1); - frag.relurl = substituteVariables(level, uri) ; - assignProgramDateTime(frag, prevFrag); - prevFrag = frag; - totalduration += frag.duration; - currentSN++; - currentPart = 0; - createNextFrag = true; - } - } else if (result[4]) { - // X-BYTERANGE - const data = (' ' + result[4]).slice(1); - if (prevFrag) { - frag.setByteRange(data, prevFrag); - } else { - frag.setByteRange(data); - } - } else if (result[5]) { - // PROGRAM-DATE-TIME - // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 - frag.rawProgramDateTime = (' ' + result[5]).slice(1); - frag.tagList.push(['PROGRAM-DATE-TIME', frag.rawProgramDateTime]); - if (firstPdtIndex === -1) { - firstPdtIndex = fragments.length; - } - } else { - result = result[0].match(LEVEL_PLAYLIST_REGEX_SLOW); - if (!result) { - logger.warn('No matches on slow regex match for level playlist!'); - continue; - } - for (i = 1; i < result.length; i++) { - if (typeof result[i] !== 'undefined') { - break; - } - } - - // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 - const tag = (' ' + result[i]).slice(1); - const value1 = (' ' + result[i + 1]).slice(1); - const value2 = result[i + 2] ? (' ' + result[i + 2]).slice(1) : ''; - switch (tag) { - case 'PLAYLIST-TYPE': - level.type = value1.toUpperCase(); - break; - case 'MEDIA-SEQUENCE': - currentSN = level.startSN = parseInt(value1); - break; - case 'SKIP': - { - const skipAttrs = new AttrList(value1); - { - substituteVariablesInAttributes(level, skipAttrs, ['RECENTLY-REMOVED-DATERANGES']); - } - const skippedSegments = skipAttrs.decimalInteger('SKIPPED-SEGMENTS'); - if (isFiniteNumber(skippedSegments)) { - level.skippedSegments = skippedSegments; - // This will result in fragments[] containing undefined values, which we will fill in with `mergeDetails` - for (let _i = skippedSegments; _i--;) { - fragments.unshift(null); - } - currentSN += skippedSegments; - } - const recentlyRemovedDateranges = skipAttrs.enumeratedString('RECENTLY-REMOVED-DATERANGES'); - if (recentlyRemovedDateranges) { - level.recentlyRemovedDateranges = recentlyRemovedDateranges.split('\t'); - } - break; - } - case 'TARGETDURATION': - level.targetduration = Math.max(parseInt(value1), 1); - break; - case 'VERSION': - level.version = parseInt(value1); - break; - case 'INDEPENDENT-SEGMENTS': - case 'EXTM3U': - break; - case 'ENDLIST': - level.live = false; - break; - case '#': - if (value1 || value2) { - frag.tagList.push(value2 ? [value1, value2] : [value1]); - } - break; - case 'DISCONTINUITY': - discontinuityCounter++; - frag.tagList.push(['DIS']); - break; - case 'GAP': - frag.gap = true; - frag.tagList.push([tag]); - break; - case 'BITRATE': - frag.tagList.push([tag, value1]); - break; - case 'DATERANGE': - { - const dateRangeAttr = new AttrList(value1); - { - substituteVariablesInAttributes(level, dateRangeAttr, ['ID', 'CLASS', 'START-DATE', 'END-DATE', 'SCTE35-CMD', 'SCTE35-OUT', 'SCTE35-IN']); - substituteVariablesInAttributes(level, dateRangeAttr, dateRangeAttr.clientAttrs); - } - const dateRange = new DateRange(dateRangeAttr, level.dateRanges[dateRangeAttr.ID]); - if (dateRange.isValid || level.skippedSegments) { - level.dateRanges[dateRange.id] = dateRange; - } else { - logger.warn(`Ignoring invalid DATERANGE tag: "${value1}"`); - } - // Add to fragment tag list for backwards compatibility (< v1.2.0) - frag.tagList.push(['EXT-X-DATERANGE', value1]); - break; - } - case 'DEFINE': - { - { - const variableAttributes = new AttrList(value1); - substituteVariablesInAttributes(level, variableAttributes, ['NAME', 'VALUE', 'IMPORT', 'QUERYPARAM']); - if ('IMPORT' in variableAttributes) { - importVariableDefinition(level, variableAttributes, multivariantVariableList); - } else { - addVariableDefinition(level, variableAttributes, baseurl); - } - } - break; - } - case 'DISCONTINUITY-SEQUENCE': - discontinuityCounter = parseInt(value1); - break; - case 'KEY': - { - const levelKey = parseKey(value1, baseurl, level); - if (levelKey.isSupported()) { - if (levelKey.method === 'NONE') { - levelkeys = undefined; - break; - } - if (!levelkeys) { - levelkeys = {}; - } - if (levelkeys[levelKey.keyFormat]) { - levelkeys = _extends({}, levelkeys); - } - levelkeys[levelKey.keyFormat] = levelKey; - } else { - logger.warn(`[Keys] Ignoring invalid EXT-X-KEY tag: "${value1}"`); - } - break; - } - case 'START': - level.startTimeOffset = parseStartTimeOffset(value1); - break; - case 'MAP': - { - const mapAttrs = new AttrList(value1); - { - substituteVariablesInAttributes(level, mapAttrs, ['BYTERANGE', 'URI']); - } - if (frag.duration) { - // Initial segment tag is after segment duration tag. - // #EXTINF: 6.0 - // #EXT-X-MAP:URI="init.mp4 - const init = new Fragment(type, baseurl); - setInitSegment(init, mapAttrs, id, levelkeys); - currentInitSegment = init; - frag.initSegment = currentInitSegment; - if (currentInitSegment.rawProgramDateTime && !frag.rawProgramDateTime) { - frag.rawProgramDateTime = currentInitSegment.rawProgramDateTime; - } - } else { - // Initial segment tag is before segment duration tag - // Handle case where EXT-X-MAP is declared after EXT-X-BYTERANGE - const end = frag.byteRangeEndOffset; - if (end) { - const start = frag.byteRangeStartOffset; - nextByteRange = `${end - start}@${start}`; - } else { - nextByteRange = null; - } - setInitSegment(frag, mapAttrs, id, levelkeys); - currentInitSegment = frag; - createNextFrag = true; - } - break; - } - case 'SERVER-CONTROL': - { - const serverControlAttrs = new AttrList(value1); - level.canBlockReload = serverControlAttrs.bool('CAN-BLOCK-RELOAD'); - level.canSkipUntil = serverControlAttrs.optionalFloat('CAN-SKIP-UNTIL', 0); - level.canSkipDateRanges = level.canSkipUntil > 0 && serverControlAttrs.bool('CAN-SKIP-DATERANGES'); - level.partHoldBack = serverControlAttrs.optionalFloat('PART-HOLD-BACK', 0); - level.holdBack = serverControlAttrs.optionalFloat('HOLD-BACK', 0); - break; - } - case 'PART-INF': - { - const partInfAttrs = new AttrList(value1); - level.partTarget = partInfAttrs.decimalFloatingPoint('PART-TARGET'); - break; - } - case 'PART': - { - let partList = level.partList; - if (!partList) { - partList = level.partList = []; - } - const previousFragmentPart = currentPart > 0 ? partList[partList.length - 1] : undefined; - const index = currentPart++; - const partAttrs = new AttrList(value1); - { - substituteVariablesInAttributes(level, partAttrs, ['BYTERANGE', 'URI']); - } - const part = new Part(partAttrs, frag, baseurl, index, previousFragmentPart); - partList.push(part); - frag.duration += part.duration; - break; - } - case 'PRELOAD-HINT': - { - const preloadHintAttrs = new AttrList(value1); - { - substituteVariablesInAttributes(level, preloadHintAttrs, ['URI']); - } - level.preloadHint = preloadHintAttrs; - break; - } - case 'RENDITION-REPORT': - { - const renditionReportAttrs = new AttrList(value1); - { - substituteVariablesInAttributes(level, renditionReportAttrs, ['URI']); - } - level.renditionReports = level.renditionReports || []; - level.renditionReports.push(renditionReportAttrs); - break; - } - default: - logger.warn(`line parsed but not handled: ${result}`); - break; - } - } - } - if (prevFrag && !prevFrag.relurl) { - fragments.pop(); - totalduration -= prevFrag.duration; - if (level.partList) { - level.fragmentHint = prevFrag; - } - } else if (level.partList) { - assignProgramDateTime(frag, prevFrag); - frag.cc = discontinuityCounter; - level.fragmentHint = frag; - if (levelkeys) { - setFragLevelKeys(frag, levelkeys, level); - } - } - const fragmentLength = fragments.length; - const firstFragment = fragments[0]; - const lastFragment = fragments[fragmentLength - 1]; - totalduration += level.skippedSegments * level.targetduration; - if (totalduration > 0 && fragmentLength && lastFragment) { - level.averagetargetduration = totalduration / fragmentLength; - const lastSn = lastFragment.sn; - level.endSN = lastSn !== 'initSegment' ? lastSn : 0; - if (!level.live) { - lastFragment.endList = true; - } - if (firstFragment) { - level.startCC = firstFragment.cc; - } - } else { - level.endSN = 0; - level.startCC = 0; - } - if (level.fragmentHint) { - totalduration += level.fragmentHint.duration; - } - level.totalduration = totalduration; - level.endCC = discontinuityCounter; - - /** - * Backfill any missing PDT values - * "If the first EXT-X-PROGRAM-DATE-TIME tag in a Playlist appears after - * one or more Media Segment URIs, the client SHOULD extrapolate - * backward from that tag (using EXTINF durations and/or media - * timestamps) to associate dates with those segments." - * We have already extrapolated forward, but all fragments up to the first instance of PDT do not have their PDTs - * computed. - */ - if (firstPdtIndex > 0) { - backfillProgramDateTimes(fragments, firstPdtIndex); - } - return level; - } -} -function parseKey(keyTagAttributes, baseurl, parsed) { - var _keyAttrs$METHOD, _keyAttrs$KEYFORMAT; - // https://tools.ietf.org/html/rfc8216#section-4.3.2.4 - const keyAttrs = new AttrList(keyTagAttributes); - { - substituteVariablesInAttributes(parsed, keyAttrs, ['KEYFORMAT', 'KEYFORMATVERSIONS', 'URI', 'IV', 'URI']); - } - const decryptmethod = (_keyAttrs$METHOD = keyAttrs.METHOD) != null ? _keyAttrs$METHOD : ''; - const decrypturi = keyAttrs.URI; - const decryptiv = keyAttrs.hexadecimalInteger('IV'); - const decryptkeyformatversions = keyAttrs.KEYFORMATVERSIONS; - // From RFC: This attribute is OPTIONAL; its absence indicates an implicit value of "identity". - const decryptkeyformat = (_keyAttrs$KEYFORMAT = keyAttrs.KEYFORMAT) != null ? _keyAttrs$KEYFORMAT : 'identity'; - if (decrypturi && keyAttrs.IV && !decryptiv) { - logger.error(`Invalid IV: ${keyAttrs.IV}`); - } - // If decrypturi is a URI with a scheme, then baseurl will be ignored - // No uri is allowed when METHOD is NONE - const resolvedUri = decrypturi ? M3U8Parser.resolve(decrypturi, baseurl) : ''; - const keyFormatVersions = (decryptkeyformatversions ? decryptkeyformatversions : '1').split('/').map(Number).filter(Number.isFinite); - return new LevelKey(decryptmethod, resolvedUri, decryptkeyformat, keyFormatVersions, decryptiv); -} -function parseStartTimeOffset(startAttributes) { - const startAttrs = new AttrList(startAttributes); - const startTimeOffset = startAttrs.decimalFloatingPoint('TIME-OFFSET'); - if (isFiniteNumber(startTimeOffset)) { - return startTimeOffset; - } - return null; -} -function setCodecs(codecsAttributeValue, level) { - let codecs = (codecsAttributeValue || '').split(/[ ,]+/).filter(c => c); - ['video', 'audio', 'text'].forEach(type => { - const filtered = codecs.filter(codec => isCodecType(codec, type)); - if (filtered.length) { - // Comma separated list of all codecs for type - level[`${type}Codec`] = filtered.join(','); - // Remove known codecs so that only unknownCodecs are left after iterating through each type - codecs = codecs.filter(codec => filtered.indexOf(codec) === -1); - } - }); - level.unknownCodecs = codecs; -} -function assignCodec(media, groupItem, codecProperty) { - const codecValue = groupItem[codecProperty]; - if (codecValue) { - media[codecProperty] = codecValue; - } -} -function backfillProgramDateTimes(fragments, firstPdtIndex) { - let fragPrev = fragments[firstPdtIndex]; - for (let i = firstPdtIndex; i--;) { - const frag = fragments[i]; - // Exit on delta-playlist skipped segments - if (!frag) { - return; - } - frag.programDateTime = fragPrev.programDateTime - frag.duration * 1000; - fragPrev = frag; - } -} -function assignProgramDateTime(frag, prevFrag) { - if (frag.rawProgramDateTime) { - frag.programDateTime = Date.parse(frag.rawProgramDateTime); - } else if (prevFrag != null && prevFrag.programDateTime) { - frag.programDateTime = prevFrag.endProgramDateTime; - } - if (!isFiniteNumber(frag.programDateTime)) { - frag.programDateTime = null; - frag.rawProgramDateTime = null; - } -} -function setInitSegment(frag, mapAttrs, id, levelkeys) { - frag.relurl = mapAttrs.URI; - if (mapAttrs.BYTERANGE) { - frag.setByteRange(mapAttrs.BYTERANGE); - } - frag.level = id; - frag.sn = 'initSegment'; - if (levelkeys) { - frag.levelkeys = levelkeys; - } - frag.initSegment = null; -} -function setFragLevelKeys(frag, levelkeys, level) { - frag.levelkeys = levelkeys; - const { - encryptedFragments - } = level; - if ((!encryptedFragments.length || encryptedFragments[encryptedFragments.length - 1].levelkeys !== levelkeys) && Object.keys(levelkeys).some(format => levelkeys[format].isCommonEncryption)) { - encryptedFragments.push(frag); - } -} - -var PlaylistContextType = { - MANIFEST: "manifest", - LEVEL: "level", - AUDIO_TRACK: "audioTrack", - SUBTITLE_TRACK: "subtitleTrack" -}; -var PlaylistLevelType = { - MAIN: "main", - AUDIO: "audio", - SUBTITLE: "subtitle" -}; - -function mapContextToLevelType(context) { - const { - type - } = context; - switch (type) { - case PlaylistContextType.AUDIO_TRACK: - return PlaylistLevelType.AUDIO; - case PlaylistContextType.SUBTITLE_TRACK: - return PlaylistLevelType.SUBTITLE; - default: - return PlaylistLevelType.MAIN; - } -} -function getResponseUrl(response, context) { - let url = response.url; - // responseURL not supported on some browsers (it is used to detect URL redirection) - // data-uri mode also not supported (but no need to detect redirection) - if (url === undefined || url.indexOf('data:') === 0) { - // fallback to initial URL - url = context.url; - } - return url; -} -class PlaylistLoader { - constructor(hls) { - this.hls = void 0; - this.loaders = Object.create(null); - this.variableList = null; - this.hls = hls; - this.registerListeners(); - } - startLoad(startPosition) {} - stopLoad() { - this.destroyInternalLoaders(); - } - registerListeners() { - const { - hls - } = this; - hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this); - hls.on(Events.AUDIO_TRACK_LOADING, this.onAudioTrackLoading, this); - hls.on(Events.SUBTITLE_TRACK_LOADING, this.onSubtitleTrackLoading, this); - } - unregisterListeners() { - const { - hls - } = this; - hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.off(Events.LEVEL_LOADING, this.onLevelLoading, this); - hls.off(Events.AUDIO_TRACK_LOADING, this.onAudioTrackLoading, this); - hls.off(Events.SUBTITLE_TRACK_LOADING, this.onSubtitleTrackLoading, this); - } - - /** - * Returns defaults or configured loader-type overloads (pLoader and loader config params) - */ - createInternalLoader(context) { - const config = this.hls.config; - const PLoader = config.pLoader; - const Loader = config.loader; - const InternalLoader = PLoader || Loader; - const loader = new InternalLoader(config); - this.loaders[context.type] = loader; - return loader; - } - getInternalLoader(context) { - return this.loaders[context.type]; - } - resetInternalLoader(contextType) { - if (this.loaders[contextType]) { - delete this.loaders[contextType]; - } - } - - /** - * Call `destroy` on all internal loader instances mapped (one per context type) - */ - destroyInternalLoaders() { - for (const contextType in this.loaders) { - const loader = this.loaders[contextType]; - if (loader) { - loader.destroy(); - } - this.resetInternalLoader(contextType); - } - } - destroy() { - this.variableList = null; - this.unregisterListeners(); - this.destroyInternalLoaders(); - } - onManifestLoading(event, data) { - const { - url - } = data; - this.variableList = null; - this.load({ - id: null, - level: 0, - responseType: 'text', - type: PlaylistContextType.MANIFEST, - url, - deliveryDirectives: null - }); - } - onLevelLoading(event, data) { - const { - id, - level, - pathwayId, - url, - deliveryDirectives - } = data; - this.load({ - id, - level, - pathwayId, - responseType: 'text', - type: PlaylistContextType.LEVEL, - url, - deliveryDirectives - }); - } - onAudioTrackLoading(event, data) { - const { - id, - groupId, - url, - deliveryDirectives - } = data; - this.load({ - id, - groupId, - level: null, - responseType: 'text', - type: PlaylistContextType.AUDIO_TRACK, - url, - deliveryDirectives - }); - } - onSubtitleTrackLoading(event, data) { - const { - id, - groupId, - url, - deliveryDirectives - } = data; - this.load({ - id, - groupId, - level: null, - responseType: 'text', - type: PlaylistContextType.SUBTITLE_TRACK, - url, - deliveryDirectives - }); - } - load(context) { - var _context$deliveryDire; - const config = this.hls.config; - - // logger.debug(`[playlist-loader]: Loading playlist of type ${context.type}, level: ${context.level}, id: ${context.id}`); - - // Check if a loader for this context already exists - let loader = this.getInternalLoader(context); - if (loader) { - const loaderContext = loader.context; - if (loaderContext && loaderContext.url === context.url && loaderContext.level === context.level) { - // same URL can't overlap - logger.trace('[playlist-loader]: playlist request ongoing'); - return; - } - logger.log(`[playlist-loader]: aborting previous loader for type: ${context.type}`); - loader.abort(); - } - - // apply different configs for retries depending on - // context (manifest, level, audio/subs playlist) - let loadPolicy; - if (context.type === PlaylistContextType.MANIFEST) { - loadPolicy = config.manifestLoadPolicy.default; - } else { - loadPolicy = _extends({}, config.playlistLoadPolicy.default, { - timeoutRetry: null, - errorRetry: null - }); - } - loader = this.createInternalLoader(context); - - // Override level/track timeout for LL-HLS requests - // (the default of 10000ms is counter productive to blocking playlist reload requests) - if (isFiniteNumber((_context$deliveryDire = context.deliveryDirectives) == null ? void 0 : _context$deliveryDire.part)) { - let levelDetails; - if (context.type === PlaylistContextType.LEVEL && context.level !== null) { - levelDetails = this.hls.levels[context.level].details; - } else if (context.type === PlaylistContextType.AUDIO_TRACK && context.id !== null) { - levelDetails = this.hls.audioTracks[context.id].details; - } else if (context.type === PlaylistContextType.SUBTITLE_TRACK && context.id !== null) { - levelDetails = this.hls.subtitleTracks[context.id].details; - } - if (levelDetails) { - const partTarget = levelDetails.partTarget; - const targetDuration = levelDetails.targetduration; - if (partTarget && targetDuration) { - const maxLowLatencyPlaylistRefresh = Math.max(partTarget * 3, targetDuration * 0.8) * 1000; - loadPolicy = _extends({}, loadPolicy, { - maxTimeToFirstByteMs: Math.min(maxLowLatencyPlaylistRefresh, loadPolicy.maxTimeToFirstByteMs), - maxLoadTimeMs: Math.min(maxLowLatencyPlaylistRefresh, loadPolicy.maxTimeToFirstByteMs) - }); - } - } - } - const legacyRetryCompatibility = loadPolicy.errorRetry || loadPolicy.timeoutRetry || {}; - const loaderConfig = { - loadPolicy, - timeout: loadPolicy.maxLoadTimeMs, - maxRetry: legacyRetryCompatibility.maxNumRetry || 0, - retryDelay: legacyRetryCompatibility.retryDelayMs || 0, - maxRetryDelay: legacyRetryCompatibility.maxRetryDelayMs || 0 - }; - const loaderCallbacks = { - onSuccess: (response, stats, context, networkDetails) => { - const loader = this.getInternalLoader(context); - this.resetInternalLoader(context.type); - const string = response.data; - - // Validate if it is an M3U8 at all - if (string.indexOf('#EXTM3U') !== 0) { - this.handleManifestParsingError(response, context, new Error('no EXTM3U delimiter'), networkDetails || null, stats); - return; - } - stats.parsing.start = performance.now(); - if (M3U8Parser.isMediaPlaylist(string)) { - this.handleTrackOrLevelPlaylist(response, stats, context, networkDetails || null, loader); - } else { - this.handleMasterPlaylist(response, stats, context, networkDetails); - } - }, - onError: (response, context, networkDetails, stats) => { - this.handleNetworkError(context, networkDetails, false, response, stats); - }, - onTimeout: (stats, context, networkDetails) => { - this.handleNetworkError(context, networkDetails, true, undefined, stats); - } - }; - - // logger.debug(`[playlist-loader]: Calling internal loader delegate for URL: ${context.url}`); - - loader.load(context, loaderConfig, loaderCallbacks); - } - handleMasterPlaylist(response, stats, context, networkDetails) { - const hls = this.hls; - const string = response.data; - const url = getResponseUrl(response, context); - const parsedResult = M3U8Parser.parseMasterPlaylist(string, url); - if (parsedResult.playlistParsingError) { - this.handleManifestParsingError(response, context, parsedResult.playlistParsingError, networkDetails, stats); - return; - } - const { - contentSteering, - levels, - sessionData, - sessionKeys, - startTimeOffset, - variableList - } = parsedResult; - this.variableList = variableList; - const { - AUDIO: audioTracks = [], - SUBTITLES: subtitles, - 'CLOSED-CAPTIONS': captions - } = M3U8Parser.parseMasterPlaylistMedia(string, url, parsedResult); - if (audioTracks.length) { - // check if we have found an audio track embedded in main playlist (audio track without URI attribute) - const embeddedAudioFound = audioTracks.some(audioTrack => !audioTrack.url); - - // if no embedded audio track defined, but audio codec signaled in quality level, - // we need to signal this main audio track this could happen with playlists with - // alt audio rendition in which quality levels (main) - // contains both audio+video. but with mixed audio track not signaled - if (!embeddedAudioFound && levels[0].audioCodec && !levels[0].attrs.AUDIO) { - logger.log('[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one'); - audioTracks.unshift({ - type: 'main', - name: 'main', - groupId: 'main', - default: false, - autoselect: false, - forced: false, - id: -1, - attrs: new AttrList({}), - bitrate: 0, - url: '' - }); - } - } - hls.trigger(Events.MANIFEST_LOADED, { - levels, - audioTracks, - subtitles, - captions, - contentSteering, - url, - stats, - networkDetails, - sessionData, - sessionKeys, - startTimeOffset, - variableList - }); - } - handleTrackOrLevelPlaylist(response, stats, context, networkDetails, loader) { - const hls = this.hls; - const { - id, - level, - type - } = context; - const url = getResponseUrl(response, context); - const levelUrlId = 0; - const levelId = isFiniteNumber(level) ? level : isFiniteNumber(id) ? id : 0; - const levelType = mapContextToLevelType(context); - const levelDetails = M3U8Parser.parseLevelPlaylist(response.data, url, levelId, levelType, levelUrlId, this.variableList); - - // We have done our first request (Manifest-type) and receive - // not a master playlist but a chunk-list (track/level) - // We fire the manifest-loaded event anyway with the parsed level-details - // by creating a single-level structure for it. - if (type === PlaylistContextType.MANIFEST) { - const singleLevel = { - attrs: new AttrList({}), - bitrate: 0, - details: levelDetails, - name: '', - url - }; - hls.trigger(Events.MANIFEST_LOADED, { - levels: [singleLevel], - audioTracks: [], - url, - stats, - networkDetails, - sessionData: null, - sessionKeys: null, - contentSteering: null, - startTimeOffset: null, - variableList: null - }); - } - - // save parsing time - stats.parsing.end = performance.now(); - - // extend the context with the new levelDetails property - context.levelDetails = levelDetails; - this.handlePlaylistLoaded(levelDetails, response, stats, context, networkDetails, loader); - } - handleManifestParsingError(response, context, error, networkDetails, stats) { - this.hls.trigger(Events.ERROR, { - type: ErrorTypes.NETWORK_ERROR, - details: ErrorDetails.MANIFEST_PARSING_ERROR, - fatal: context.type === PlaylistContextType.MANIFEST, - url: response.url, - err: error, - error, - reason: error.message, - response, - context, - networkDetails, - stats - }); - } - handleNetworkError(context, networkDetails, timeout = false, response, stats) { - let message = `A network ${timeout ? 'timeout' : 'error' + (response ? ' (status ' + response.code + ')' : '')} occurred while loading ${context.type}`; - if (context.type === PlaylistContextType.LEVEL) { - message += `: ${context.level} id: ${context.id}`; - } else if (context.type === PlaylistContextType.AUDIO_TRACK || context.type === PlaylistContextType.SUBTITLE_TRACK) { - message += ` id: ${context.id} group-id: "${context.groupId}"`; - } - const error = new Error(message); - logger.warn(`[playlist-loader]: ${message}`); - let details = ErrorDetails.UNKNOWN; - let fatal = false; - const loader = this.getInternalLoader(context); - switch (context.type) { - case PlaylistContextType.MANIFEST: - details = timeout ? ErrorDetails.MANIFEST_LOAD_TIMEOUT : ErrorDetails.MANIFEST_LOAD_ERROR; - fatal = true; - break; - case PlaylistContextType.LEVEL: - details = timeout ? ErrorDetails.LEVEL_LOAD_TIMEOUT : ErrorDetails.LEVEL_LOAD_ERROR; - fatal = false; - break; - case PlaylistContextType.AUDIO_TRACK: - details = timeout ? ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT : ErrorDetails.AUDIO_TRACK_LOAD_ERROR; - fatal = false; - break; - case PlaylistContextType.SUBTITLE_TRACK: - details = timeout ? ErrorDetails.SUBTITLE_TRACK_LOAD_TIMEOUT : ErrorDetails.SUBTITLE_LOAD_ERROR; - fatal = false; - break; - } - if (loader) { - this.resetInternalLoader(context.type); - } - const errorData = { - type: ErrorTypes.NETWORK_ERROR, - details, - fatal, - url: context.url, - loader, - context, - error, - networkDetails, - stats - }; - if (response) { - const url = (networkDetails == null ? void 0 : networkDetails.url) || context.url; - errorData.response = _objectSpread2({ - url, - data: undefined - }, response); - } - this.hls.trigger(Events.ERROR, errorData); - } - handlePlaylistLoaded(levelDetails, response, stats, context, networkDetails, loader) { - const hls = this.hls; - const { - type, - level, - id, - groupId, - deliveryDirectives - } = context; - const url = getResponseUrl(response, context); - const parent = mapContextToLevelType(context); - const levelIndex = typeof context.level === 'number' && parent === PlaylistLevelType.MAIN ? level : undefined; - if (!levelDetails.fragments.length) { - const _error = new Error('No Segments found in Playlist'); - hls.trigger(Events.ERROR, { - type: ErrorTypes.NETWORK_ERROR, - details: ErrorDetails.LEVEL_EMPTY_ERROR, - fatal: false, - url, - error: _error, - reason: _error.message, - response, - context, - level: levelIndex, - parent, - networkDetails, - stats - }); - return; - } - if (!levelDetails.targetduration) { - levelDetails.playlistParsingError = new Error('Missing Target Duration'); - } - const error = levelDetails.playlistParsingError; - if (error) { - hls.trigger(Events.ERROR, { - type: ErrorTypes.NETWORK_ERROR, - details: ErrorDetails.LEVEL_PARSING_ERROR, - fatal: false, - url, - error, - reason: error.message, - response, - context, - level: levelIndex, - parent, - networkDetails, - stats - }); - return; - } - if (levelDetails.live && loader) { - if (loader.getCacheAge) { - levelDetails.ageHeader = loader.getCacheAge() || 0; - } - if (!loader.getCacheAge || isNaN(levelDetails.ageHeader)) { - levelDetails.ageHeader = 0; - } - } - switch (type) { - case PlaylistContextType.MANIFEST: - case PlaylistContextType.LEVEL: - hls.trigger(Events.LEVEL_LOADED, { - details: levelDetails, - level: levelIndex || 0, - id: id || 0, - stats, - networkDetails, - deliveryDirectives - }); - break; - case PlaylistContextType.AUDIO_TRACK: - hls.trigger(Events.AUDIO_TRACK_LOADED, { - details: levelDetails, - id: id || 0, - groupId: groupId || '', - stats, - networkDetails, - deliveryDirectives - }); - break; - case PlaylistContextType.SUBTITLE_TRACK: - hls.trigger(Events.SUBTITLE_TRACK_LOADED, { - details: levelDetails, - id: id || 0, - groupId: groupId || '', - stats, - networkDetails, - deliveryDirectives - }); - break; - } - } -} - -function sendAddTrackEvent(track, videoEl) { - let event; - try { - event = new Event('addtrack'); - } catch (err) { - // for IE11 - event = document.createEvent('Event'); - event.initEvent('addtrack', false, false); - } - event.track = track; - videoEl.dispatchEvent(event); -} -function addCueToTrack(track, cue) { - // Sometimes there are cue overlaps on segmented vtts so the same - // cue can appear more than once in different vtt files. - // This avoid showing duplicated cues with same timecode and text. - const mode = track.mode; - if (mode === 'disabled') { - track.mode = 'hidden'; - } - if (track.cues && !track.cues.getCueById(cue.id)) { - try { - track.addCue(cue); - if (!track.cues.getCueById(cue.id)) { - throw new Error(`addCue is failed for: ${cue}`); - } - } catch (err) { - logger.debug(`[texttrack-utils]: ${err}`); - try { - const textTrackCue = new self.TextTrackCue(cue.startTime, cue.endTime, cue.text); - textTrackCue.id = cue.id; - track.addCue(textTrackCue); - } catch (err2) { - logger.debug(`[texttrack-utils]: Legacy TextTrackCue fallback failed: ${err2}`); - } - } - } - if (mode === 'disabled') { - track.mode = mode; - } -} -function clearCurrentCues(track) { - // When track.mode is disabled, track.cues will be null. - // To guarantee the removal of cues, we need to temporarily - // change the mode to hidden - const mode = track.mode; - if (mode === 'disabled') { - track.mode = 'hidden'; - } - if (track.cues) { - for (let i = track.cues.length; i--;) { - track.removeCue(track.cues[i]); - } - } - if (mode === 'disabled') { - track.mode = mode; - } -} -function removeCuesInRange(track, start, end, predicate) { - const mode = track.mode; - if (mode === 'disabled') { - track.mode = 'hidden'; - } - if (track.cues && track.cues.length > 0) { - const cues = getCuesInRange(track.cues, start, end); - for (let i = 0; i < cues.length; i++) { - if (!predicate || predicate(cues[i])) { - track.removeCue(cues[i]); - } - } - } - if (mode === 'disabled') { - track.mode = mode; - } -} - -// Find first cue starting after given time. -// Modified version of binary search O(log(n)). -function getFirstCueIndexAfterTime(cues, time) { - // If first cue starts after time, start there - if (time < cues[0].startTime) { - return 0; - } - // If the last cue ends before time there is no overlap - const len = cues.length - 1; - if (time > cues[len].endTime) { - return -1; - } - let left = 0; - let right = len; - while (left <= right) { - const mid = Math.floor((right + left) / 2); - if (time < cues[mid].startTime) { - right = mid - 1; - } else if (time > cues[mid].startTime && left < len) { - left = mid + 1; - } else { - // If it's not lower or higher, it must be equal. - return mid; - } - } - // At this point, left and right have swapped. - // No direct match was found, left or right element must be the closest. Check which one has the smallest diff. - return cues[left].startTime - time < time - cues[right].startTime ? left : right; -} -function getCuesInRange(cues, start, end) { - const cuesFound = []; - const firstCueInRange = getFirstCueIndexAfterTime(cues, start); - if (firstCueInRange > -1) { - for (let i = firstCueInRange, len = cues.length; i < len; i++) { - const cue = cues[i]; - if (cue.startTime >= start && cue.endTime <= end) { - cuesFound.push(cue); - } else if (cue.startTime > end) { - return cuesFound; - } - } - } - return cuesFound; -} -function filterSubtitleTracks(textTrackList) { - const tracks = []; - for (let i = 0; i < textTrackList.length; i++) { - const track = textTrackList[i]; - // Edge adds a track without a label; we don't want to use it - if ((track.kind === 'subtitles' || track.kind === 'captions') && track.label) { - tracks.push(textTrackList[i]); - } - } - return tracks; -} - -var MetadataSchema = { - audioId3: "org.id3", - dateRange: "com.apple.quicktime.HLS", - emsg: "https://aomedia.org/emsg/ID3" -}; - -const MIN_CUE_DURATION = 0.25; -function getCueClass() { - if (typeof self === 'undefined') return undefined; - return self.VTTCue || self.TextTrackCue; -} -function createCueWithDataFields(Cue, startTime, endTime, data, type) { - let cue = new Cue(startTime, endTime, ''); - try { - cue.value = data; - if (type) { - cue.type = type; - } - } catch (e) { - cue = new Cue(startTime, endTime, JSON.stringify(type ? _objectSpread2({ - type - }, data) : data)); - } - return cue; -} - -// VTTCue latest draft allows an infinite duration, fallback -// to MAX_VALUE if necessary -const MAX_CUE_ENDTIME = (() => { - const Cue = getCueClass(); - try { - Cue && new Cue(0, Number.POSITIVE_INFINITY, ''); - } catch (e) { - return Number.MAX_VALUE; - } - return Number.POSITIVE_INFINITY; -})(); -function dateRangeDateToTimelineSeconds(date, offset) { - return date.getTime() / 1000 - offset; -} -function hexToArrayBuffer(str) { - return Uint8Array.from(str.replace(/^0x/, '').replace(/([\da-fA-F]{2}) ?/g, '0x$1 ').replace(/ +$/, '').split(' ')).buffer; -} -class ID3TrackController { - constructor(hls) { - this.hls = void 0; - this.id3Track = null; - this.media = null; - this.dateRangeCuesAppended = {}; - this.hls = hls; - this._registerListeners(); - } - destroy() { - this._unregisterListeners(); - this.id3Track = null; - this.media = null; - this.dateRangeCuesAppended = {}; - // @ts-ignore - this.hls = null; - } - _registerListeners() { - const { - hls - } = this; - hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.on(Events.FRAG_PARSING_METADATA, this.onFragParsingMetadata, this); - hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); - hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this); - } - _unregisterListeners() { - const { - hls - } = this; - hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.off(Events.FRAG_PARSING_METADATA, this.onFragParsingMetadata, this); - hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); - hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this); - } - - // Add ID3 metatadata text track. - onMediaAttached(event, data) { - this.media = data.media; - } - onMediaDetaching() { - if (!this.id3Track) { - return; - } - clearCurrentCues(this.id3Track); - this.id3Track = null; - this.media = null; - this.dateRangeCuesAppended = {}; - } - onManifestLoading() { - this.dateRangeCuesAppended = {}; - } - createTrack(media) { - const track = this.getID3Track(media.textTracks); - track.mode = 'hidden'; - return track; - } - getID3Track(textTracks) { - if (!this.media) { - return; - } - for (let i = 0; i < textTracks.length; i++) { - const textTrack = textTracks[i]; - if (textTrack.kind === 'metadata' && textTrack.label === 'id3') { - // send 'addtrack' when reusing the textTrack for metadata, - // same as what we do for captions - sendAddTrackEvent(textTrack, this.media); - return textTrack; - } - } - return this.media.addTextTrack('metadata', 'id3'); - } - onFragParsingMetadata(event, data) { - if (!this.media) { - return; - } - const { - hls: { - config: { - enableEmsgMetadataCues, - enableID3MetadataCues - } - } - } = this; - if (!enableEmsgMetadataCues && !enableID3MetadataCues) { - return; - } - const { - samples - } = data; - - // create track dynamically - if (!this.id3Track) { - this.id3Track = this.createTrack(this.media); - } - const Cue = getCueClass(); - if (!Cue) { - return; - } - for (let i = 0; i < samples.length; i++) { - const type = samples[i].type; - if (type === MetadataSchema.emsg && !enableEmsgMetadataCues || !enableID3MetadataCues) { - continue; - } - const frames = getID3Frames(samples[i].data); - if (frames) { - const startTime = samples[i].pts; - let endTime = startTime + samples[i].duration; - if (endTime > MAX_CUE_ENDTIME) { - endTime = MAX_CUE_ENDTIME; - } - const timeDiff = endTime - startTime; - if (timeDiff <= 0) { - endTime = startTime + MIN_CUE_DURATION; - } - for (let j = 0; j < frames.length; j++) { - const frame = frames[j]; - // Safari doesn't put the timestamp frame in the TextTrack - if (!isTimeStampFrame(frame)) { - // add a bounds to any unbounded cues - this.updateId3CueEnds(startTime, type); - const cue = createCueWithDataFields(Cue, startTime, endTime, frame, type); - if (cue) { - this.id3Track.addCue(cue); - } - } - } - } - } - } - updateId3CueEnds(startTime, type) { - var _this$id3Track; - const cues = (_this$id3Track = this.id3Track) == null ? void 0 : _this$id3Track.cues; - if (cues) { - for (let i = cues.length; i--;) { - const cue = cues[i]; - if (cue.type === type && cue.startTime < startTime && cue.endTime === MAX_CUE_ENDTIME) { - cue.endTime = startTime; - } - } - } - } - onBufferFlushing(event, { - startOffset, - endOffset, - type - }) { - const { - id3Track, - hls - } = this; - if (!hls) { - return; - } - const { - config: { - enableEmsgMetadataCues, - enableID3MetadataCues - } - } = hls; - if (id3Track && (enableEmsgMetadataCues || enableID3MetadataCues)) { - let predicate; - if (type === 'audio') { - predicate = cue => cue.type === MetadataSchema.audioId3 && enableID3MetadataCues; - } else if (type === 'video') { - predicate = cue => cue.type === MetadataSchema.emsg && enableEmsgMetadataCues; - } else { - predicate = cue => cue.type === MetadataSchema.audioId3 && enableID3MetadataCues || cue.type === MetadataSchema.emsg && enableEmsgMetadataCues; - } - removeCuesInRange(id3Track, startOffset, endOffset, predicate); - } - } - onLevelUpdated(event, { - details - }) { - if (!this.media || !details.hasProgramDateTime || !this.hls.config.enableDateRangeMetadataCues) { - return; - } - const { - dateRangeCuesAppended, - id3Track - } = this; - const { - dateRanges - } = details; - const ids = Object.keys(dateRanges); - // Remove cues from track not found in details.dateRanges - if (id3Track) { - const idsToRemove = Object.keys(dateRangeCuesAppended).filter(id => !ids.includes(id)); - for (let i = idsToRemove.length; i--;) { - const id = idsToRemove[i]; - Object.keys(dateRangeCuesAppended[id].cues).forEach(key => { - id3Track.removeCue(dateRangeCuesAppended[id].cues[key]); - }); - delete dateRangeCuesAppended[id]; - } - } - // Exit if the playlist does not have Date Ranges or does not have Program Date Time - const lastFragment = details.fragments[details.fragments.length - 1]; - if (ids.length === 0 || !isFiniteNumber(lastFragment == null ? void 0 : lastFragment.programDateTime)) { - return; - } - if (!this.id3Track) { - this.id3Track = this.createTrack(this.media); - } - const dateTimeOffset = lastFragment.programDateTime / 1000 - lastFragment.start; - const Cue = getCueClass(); - for (let i = 0; i < ids.length; i++) { - const id = ids[i]; - const dateRange = dateRanges[id]; - const startTime = dateRangeDateToTimelineSeconds(dateRange.startDate, dateTimeOffset); - - // Process DateRanges to determine end-time (known DURATION, END-DATE, or END-ON-NEXT) - const appendedDateRangeCues = dateRangeCuesAppended[id]; - const cues = (appendedDateRangeCues == null ? void 0 : appendedDateRangeCues.cues) || {}; - let durationKnown = (appendedDateRangeCues == null ? void 0 : appendedDateRangeCues.durationKnown) || false; - let endTime = MAX_CUE_ENDTIME; - const endDate = dateRange.endDate; - if (endDate) { - endTime = dateRangeDateToTimelineSeconds(endDate, dateTimeOffset); - durationKnown = true; - } else if (dateRange.endOnNext && !durationKnown) { - const nextDateRangeWithSameClass = ids.reduce((candidateDateRange, id) => { - if (id !== dateRange.id) { - const otherDateRange = dateRanges[id]; - if (otherDateRange.class === dateRange.class && otherDateRange.startDate > dateRange.startDate && (!candidateDateRange || dateRange.startDate < candidateDateRange.startDate)) { - return otherDateRange; - } - } - return candidateDateRange; - }, null); - if (nextDateRangeWithSameClass) { - endTime = dateRangeDateToTimelineSeconds(nextDateRangeWithSameClass.startDate, dateTimeOffset); - durationKnown = true; - } - } - - // Create TextTrack Cues for each MetadataGroup Item (select DateRange attribute) - // This is to emulate Safari HLS playback handling of DateRange tags - const attributes = Object.keys(dateRange.attr); - for (let j = 0; j < attributes.length; j++) { - const key = attributes[j]; - if (!isDateRangeCueAttribute(key)) { - continue; - } - const cue = cues[key]; - if (cue) { - if (durationKnown && !appendedDateRangeCues.durationKnown) { - cue.endTime = endTime; - } - } else if (Cue) { - let data = dateRange.attr[key]; - if (isSCTE35Attribute(key)) { - data = hexToArrayBuffer(data); - } - const _cue = createCueWithDataFields(Cue, startTime, endTime, { - key, - data - }, MetadataSchema.dateRange); - if (_cue) { - _cue.id = id; - this.id3Track.addCue(_cue); - cues[key] = _cue; - } - } - } - - // Keep track of processed DateRanges by ID for updating cues with new DateRange tag attributes - dateRangeCuesAppended[id] = { - cues, - dateRange, - durationKnown - }; - } - } -} - -class LatencyController { - constructor(hls) { - this.hls = void 0; - this.config = void 0; - this.media = null; - this.levelDetails = null; - this.currentTime = 0; - this.stallCount = 0; - this._latency = null; - this.timeupdateHandler = () => this.timeupdate(); - this.hls = hls; - this.config = hls.config; - this.registerListeners(); - } - get latency() { - return this._latency || 0; - } - get maxLatency() { - const { - config, - levelDetails - } = this; - if (config.liveMaxLatencyDuration !== undefined) { - return config.liveMaxLatencyDuration; - } - return levelDetails ? config.liveMaxLatencyDurationCount * levelDetails.targetduration : 0; - } - get targetLatency() { - const { - levelDetails - } = this; - if (levelDetails === null) { - return null; - } - const { - holdBack, - partHoldBack, - targetduration - } = levelDetails; - const { - liveSyncDuration, - liveSyncDurationCount, - lowLatencyMode - } = this.config; - const userConfig = this.hls.userConfig; - let targetLatency = lowLatencyMode ? partHoldBack || holdBack : holdBack; - if (userConfig.liveSyncDuration || userConfig.liveSyncDurationCount || targetLatency === 0) { - targetLatency = liveSyncDuration !== undefined ? liveSyncDuration : liveSyncDurationCount * targetduration; - } - const maxLiveSyncOnStallIncrease = targetduration; - const liveSyncOnStallIncrease = 1.0; - return targetLatency + Math.min(this.stallCount * liveSyncOnStallIncrease, maxLiveSyncOnStallIncrease); - } - get liveSyncPosition() { - const liveEdge = this.estimateLiveEdge(); - const targetLatency = this.targetLatency; - const levelDetails = this.levelDetails; - if (liveEdge === null || targetLatency === null || levelDetails === null) { - return null; - } - const edge = levelDetails.edge; - const syncPosition = liveEdge - targetLatency - this.edgeStalled; - const min = edge - levelDetails.totalduration; - const max = edge - (this.config.lowLatencyMode && levelDetails.partTarget || levelDetails.targetduration); - return Math.min(Math.max(min, syncPosition), max); - } - get drift() { - const { - levelDetails - } = this; - if (levelDetails === null) { - return 1; - } - return levelDetails.drift; - } - get edgeStalled() { - const { - levelDetails - } = this; - if (levelDetails === null) { - return 0; - } - const maxLevelUpdateAge = (this.config.lowLatencyMode && levelDetails.partTarget || levelDetails.targetduration) * 3; - return Math.max(levelDetails.age - maxLevelUpdateAge, 0); - } - get forwardBufferLength() { - const { - media, - levelDetails - } = this; - if (!media || !levelDetails) { - return 0; - } - const bufferedRanges = media.buffered.length; - return (bufferedRanges ? media.buffered.end(bufferedRanges - 1) : levelDetails.edge) - this.currentTime; - } - destroy() { - this.unregisterListeners(); - this.onMediaDetaching(); - this.levelDetails = null; - // @ts-ignore - this.hls = this.timeupdateHandler = null; - } - registerListeners() { - this.hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - this.hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - this.hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); - this.hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this); - this.hls.on(Events.ERROR, this.onError, this); - } - unregisterListeners() { - this.hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - this.hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - this.hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); - this.hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this); - this.hls.off(Events.ERROR, this.onError, this); - } - onMediaAttached(event, data) { - this.media = data.media; - this.media.addEventListener('timeupdate', this.timeupdateHandler); - } - onMediaDetaching() { - if (this.media) { - this.media.removeEventListener('timeupdate', this.timeupdateHandler); - this.media = null; - } - } - onManifestLoading() { - this.levelDetails = null; - this._latency = null; - this.stallCount = 0; - } - onLevelUpdated(event, { - details - }) { - this.levelDetails = details; - if (details.advanced) { - this.timeupdate(); - } - if (!details.live && this.media) { - this.media.removeEventListener('timeupdate', this.timeupdateHandler); - } - } - onError(event, data) { - var _this$levelDetails; - if (data.details !== ErrorDetails.BUFFER_STALLED_ERROR) { - return; - } - this.stallCount++; - if ((_this$levelDetails = this.levelDetails) != null && _this$levelDetails.live) { - logger.warn('[playback-rate-controller]: Stall detected, adjusting target latency'); - } - } - timeupdate() { - const { - media, - levelDetails - } = this; - if (!media || !levelDetails) { - return; - } - this.currentTime = media.currentTime; - const latency = this.computeLatency(); - if (latency === null) { - return; - } - this._latency = latency; - - // Adapt playbackRate to meet target latency in low-latency mode - const { - lowLatencyMode, - maxLiveSyncPlaybackRate - } = this.config; - if (!lowLatencyMode || maxLiveSyncPlaybackRate === 1 || !levelDetails.live) { - return; - } - const targetLatency = this.targetLatency; - if (targetLatency === null) { - return; - } - const distanceFromTarget = latency - targetLatency; - // Only adjust playbackRate when within one target duration of targetLatency - // and more than one second from under-buffering. - // Playback further than one target duration from target can be considered DVR playback. - const liveMinLatencyDuration = Math.min(this.maxLatency, targetLatency + levelDetails.targetduration); - const inLiveRange = distanceFromTarget < liveMinLatencyDuration; - if (inLiveRange && distanceFromTarget > 0.05 && this.forwardBufferLength > 1) { - const max = Math.min(2, Math.max(1.0, maxLiveSyncPlaybackRate)); - const rate = Math.round(2 / (1 + Math.exp(-0.75 * distanceFromTarget - this.edgeStalled)) * 20) / 20; - media.playbackRate = Math.min(max, Math.max(1, rate)); - } else if (media.playbackRate !== 1 && media.playbackRate !== 0) { - media.playbackRate = 1; - } - } - estimateLiveEdge() { - const { - levelDetails - } = this; - if (levelDetails === null) { - return null; - } - return levelDetails.edge + levelDetails.age; - } - computeLatency() { - const liveEdge = this.estimateLiveEdge(); - if (liveEdge === null) { - return null; - } - return liveEdge - this.currentTime; - } -} - -const HdcpLevels = ['NONE', 'TYPE-0', 'TYPE-1', null]; -function isHdcpLevel(value) { - return HdcpLevels.indexOf(value) > -1; -} -const VideoRangeValues = ['SDR', 'PQ', 'HLG']; -function isVideoRange(value) { - return !!value && VideoRangeValues.indexOf(value) > -1; -} -var HlsSkip = { - No: "", - Yes: "YES", - v2: "v2" -}; -function getSkipValue(details) { - const { - canSkipUntil, - canSkipDateRanges, - age - } = details; - // A Client SHOULD NOT request a Playlist Delta Update unless it already - // has a version of the Playlist that is no older than one-half of the Skip Boundary. - // @see: https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-6.3.7 - const playlistRecentEnough = age < canSkipUntil / 2; - if (canSkipUntil && playlistRecentEnough) { - if (canSkipDateRanges) { - return HlsSkip.v2; - } - return HlsSkip.Yes; - } - return HlsSkip.No; -} -class HlsUrlParameters { - constructor(msn, part, skip) { - this.msn = void 0; - this.part = void 0; - this.skip = void 0; - this.msn = msn; - this.part = part; - this.skip = skip; - } - addDirectives(uri) { - const url = new self.URL(uri); - if (this.msn !== undefined) { - url.searchParams.set('_HLS_msn', this.msn.toString()); - } - if (this.part !== undefined) { - url.searchParams.set('_HLS_part', this.part.toString()); - } - if (this.skip) { - url.searchParams.set('_HLS_skip', this.skip); - } - return url.href; - } -} -class Level { - constructor(data) { - this._attrs = void 0; - this.audioCodec = void 0; - this.bitrate = void 0; - this.codecSet = void 0; - this.url = void 0; - this.frameRate = void 0; - this.height = void 0; - this.id = void 0; - this.name = void 0; - this.videoCodec = void 0; - this.width = void 0; - this.details = void 0; - this.fragmentError = 0; - this.loadError = 0; - this.loaded = void 0; - this.realBitrate = 0; - this.supportedPromise = void 0; - this.supportedResult = void 0; - this._avgBitrate = 0; - this._audioGroups = void 0; - this._subtitleGroups = void 0; - // Deprecated (retained for backwards compatibility) - this._urlId = 0; - this.url = [data.url]; - this._attrs = [data.attrs]; - this.bitrate = data.bitrate; - if (data.details) { - this.details = data.details; - } - this.id = data.id || 0; - this.name = data.name; - this.width = data.width || 0; - this.height = data.height || 0; - this.frameRate = data.attrs.optionalFloat('FRAME-RATE', 0); - this._avgBitrate = data.attrs.decimalInteger('AVERAGE-BANDWIDTH'); - this.audioCodec = data.audioCodec; - this.videoCodec = data.videoCodec; - this.codecSet = [data.videoCodec, data.audioCodec].filter(c => !!c).map(s => s.substring(0, 4)).join(','); - this.addGroupId('audio', data.attrs.AUDIO); - this.addGroupId('text', data.attrs.SUBTITLES); - } - get maxBitrate() { - return Math.max(this.realBitrate, this.bitrate); - } - get averageBitrate() { - return this._avgBitrate || this.realBitrate || this.bitrate; - } - get attrs() { - return this._attrs[0]; - } - get codecs() { - return this.attrs.CODECS || ''; - } - get pathwayId() { - return this.attrs['PATHWAY-ID'] || '.'; - } - get videoRange() { - return this.attrs['VIDEO-RANGE'] || 'SDR'; - } - get score() { - return this.attrs.optionalFloat('SCORE', 0); - } - get uri() { - return this.url[0] || ''; - } - hasAudioGroup(groupId) { - return hasGroup(this._audioGroups, groupId); - } - hasSubtitleGroup(groupId) { - return hasGroup(this._subtitleGroups, groupId); - } - get audioGroups() { - return this._audioGroups; - } - get subtitleGroups() { - return this._subtitleGroups; - } - addGroupId(type, groupId) { - if (!groupId) { - return; - } - if (type === 'audio') { - let audioGroups = this._audioGroups; - if (!audioGroups) { - audioGroups = this._audioGroups = []; - } - if (audioGroups.indexOf(groupId) === -1) { - audioGroups.push(groupId); - } - } else if (type === 'text') { - let subtitleGroups = this._subtitleGroups; - if (!subtitleGroups) { - subtitleGroups = this._subtitleGroups = []; - } - if (subtitleGroups.indexOf(groupId) === -1) { - subtitleGroups.push(groupId); - } - } - } - - // Deprecated methods (retained for backwards compatibility) - get urlId() { - return 0; - } - set urlId(value) {} - get audioGroupIds() { - return this.audioGroups ? [this.audioGroupId] : undefined; - } - get textGroupIds() { - return this.subtitleGroups ? [this.textGroupId] : undefined; - } - get audioGroupId() { - var _this$audioGroups; - return (_this$audioGroups = this.audioGroups) == null ? void 0 : _this$audioGroups[0]; - } - get textGroupId() { - var _this$subtitleGroups; - return (_this$subtitleGroups = this.subtitleGroups) == null ? void 0 : _this$subtitleGroups[0]; - } - addFallback() {} -} -function hasGroup(groups, groupId) { - if (!groupId || !groups) { - return false; - } - return groups.indexOf(groupId) !== -1; -} - -function updateFromToPTS(fragFrom, fragTo) { - const fragToPTS = fragTo.startPTS; - // if we know startPTS[toIdx] - if (isFiniteNumber(fragToPTS)) { - // update fragment duration. - // it helps to fix drifts between playlist reported duration and fragment real duration - let duration = 0; - let frag; - if (fragTo.sn > fragFrom.sn) { - duration = fragToPTS - fragFrom.start; - frag = fragFrom; - } else { - duration = fragFrom.start - fragToPTS; - frag = fragTo; - } - if (frag.duration !== duration) { - frag.duration = duration; - } - // we dont know startPTS[toIdx] - } else if (fragTo.sn > fragFrom.sn) { - const contiguous = fragFrom.cc === fragTo.cc; - // TODO: With part-loading end/durations we need to confirm the whole fragment is loaded before using (or setting) minEndPTS - if (contiguous && fragFrom.minEndPTS) { - fragTo.start = fragFrom.start + (fragFrom.minEndPTS - fragFrom.start); - } else { - fragTo.start = fragFrom.start + fragFrom.duration; - } - } else { - fragTo.start = Math.max(fragFrom.start - fragTo.duration, 0); - } -} -function updateFragPTSDTS(details, frag, startPTS, endPTS, startDTS, endDTS) { - const parsedMediaDuration = endPTS - startPTS; - if (parsedMediaDuration <= 0) { - logger.warn('Fragment should have a positive duration', frag); - endPTS = startPTS + frag.duration; - endDTS = startDTS + frag.duration; - } - let maxStartPTS = startPTS; - let minEndPTS = endPTS; - const fragStartPts = frag.startPTS; - const fragEndPts = frag.endPTS; - if (isFiniteNumber(fragStartPts)) { - // delta PTS between audio and video - const deltaPTS = Math.abs(fragStartPts - startPTS); - if (!isFiniteNumber(frag.deltaPTS)) { - frag.deltaPTS = deltaPTS; - } else { - frag.deltaPTS = Math.max(deltaPTS, frag.deltaPTS); - } - maxStartPTS = Math.max(startPTS, fragStartPts); - startPTS = Math.min(startPTS, fragStartPts); - startDTS = Math.min(startDTS, frag.startDTS); - minEndPTS = Math.min(endPTS, fragEndPts); - endPTS = Math.max(endPTS, fragEndPts); - endDTS = Math.max(endDTS, frag.endDTS); - } - const drift = startPTS - frag.start; - if (frag.start !== 0) { - frag.start = startPTS; - } - frag.duration = endPTS - frag.start; - frag.startPTS = startPTS; - frag.maxStartPTS = maxStartPTS; - frag.startDTS = startDTS; - frag.endPTS = endPTS; - frag.minEndPTS = minEndPTS; - frag.endDTS = endDTS; - const sn = frag.sn; // 'initSegment' - // exit if sn out of range - if (!details || sn < details.startSN || sn > details.endSN) { - return 0; - } - let i; - const fragIdx = sn - details.startSN; - const fragments = details.fragments; - // update frag reference in fragments array - // rationale is that fragments array might not contain this frag object. - // this will happen if playlist has been refreshed between frag loading and call to updateFragPTSDTS() - // if we don't update frag, we won't be able to propagate PTS info on the playlist - // resulting in invalid sliding computation - fragments[fragIdx] = frag; - // adjust fragment PTS/duration from seqnum-1 to frag 0 - for (i = fragIdx; i > 0; i--) { - updateFromToPTS(fragments[i], fragments[i - 1]); - } - - // adjust fragment PTS/duration from seqnum to last frag - for (i = fragIdx; i < fragments.length - 1; i++) { - updateFromToPTS(fragments[i], fragments[i + 1]); - } - if (details.fragmentHint) { - updateFromToPTS(fragments[fragments.length - 1], details.fragmentHint); - } - details.PTSKnown = details.alignedSliding = true; - return drift; -} -function mergeDetails(oldDetails, newDetails) { - // Track the last initSegment processed. Initialize it to the last one on the timeline. - let currentInitSegment = null; - const oldFragments = oldDetails.fragments; - for (let i = oldFragments.length - 1; i >= 0; i--) { - const oldInit = oldFragments[i].initSegment; - if (oldInit) { - currentInitSegment = oldInit; - break; - } - } - if (oldDetails.fragmentHint) { - // prevent PTS and duration from being adjusted on the next hint - delete oldDetails.fragmentHint.endPTS; - } - // check if old/new playlists have fragments in common - // loop through overlapping SN and update startPTS , cc, and duration if any found - let ccOffset = 0; - let PTSFrag; - mapFragmentIntersection(oldDetails, newDetails, (oldFrag, newFrag) => { - if (oldFrag.relurl) { - // Do not compare CC if the old fragment has no url. This is a level.fragmentHint used by LL-HLS parts. - // It maybe be off by 1 if it was created before any parts or discontinuity tags were appended to the end - // of the playlist. - ccOffset = oldFrag.cc - newFrag.cc; - } - if (isFiniteNumber(oldFrag.startPTS) && isFiniteNumber(oldFrag.endPTS)) { - newFrag.start = newFrag.startPTS = oldFrag.startPTS; - newFrag.startDTS = oldFrag.startDTS; - newFrag.maxStartPTS = oldFrag.maxStartPTS; - newFrag.endPTS = oldFrag.endPTS; - newFrag.endDTS = oldFrag.endDTS; - newFrag.minEndPTS = oldFrag.minEndPTS; - newFrag.duration = oldFrag.endPTS - oldFrag.startPTS; - if (newFrag.duration) { - PTSFrag = newFrag; - } - - // PTS is known when any segment has startPTS and endPTS - newDetails.PTSKnown = newDetails.alignedSliding = true; - } - newFrag.elementaryStreams = oldFrag.elementaryStreams; - newFrag.loader = oldFrag.loader; - newFrag.stats = oldFrag.stats; - if (oldFrag.initSegment) { - newFrag.initSegment = oldFrag.initSegment; - currentInitSegment = oldFrag.initSegment; - } - }); - if (currentInitSegment) { - const fragmentsToCheck = newDetails.fragmentHint ? newDetails.fragments.concat(newDetails.fragmentHint) : newDetails.fragments; - fragmentsToCheck.forEach(frag => { - var _currentInitSegment; - if (frag && (!frag.initSegment || frag.initSegment.relurl === ((_currentInitSegment = currentInitSegment) == null ? void 0 : _currentInitSegment.relurl))) { - frag.initSegment = currentInitSegment; - } - }); - } - if (newDetails.skippedSegments) { - newDetails.deltaUpdateFailed = newDetails.fragments.some(frag => !frag); - if (newDetails.deltaUpdateFailed) { - logger.warn('[level-helper] Previous playlist missing segments skipped in delta playlist'); - for (let i = newDetails.skippedSegments; i--;) { - newDetails.fragments.shift(); - } - newDetails.startSN = newDetails.fragments[0].sn; - newDetails.startCC = newDetails.fragments[0].cc; - } else if (newDetails.canSkipDateRanges) { - newDetails.dateRanges = mergeDateRanges(oldDetails.dateRanges, newDetails.dateRanges, newDetails.recentlyRemovedDateranges); - } - } - const newFragments = newDetails.fragments; - if (ccOffset) { - logger.warn('discontinuity sliding from playlist, take drift into account'); - for (let i = 0; i < newFragments.length; i++) { - newFragments[i].cc += ccOffset; - } - } - if (newDetails.skippedSegments) { - newDetails.startCC = newDetails.fragments[0].cc; - } - - // Merge parts - mapPartIntersection(oldDetails.partList, newDetails.partList, (oldPart, newPart) => { - newPart.elementaryStreams = oldPart.elementaryStreams; - newPart.stats = oldPart.stats; - }); - - // if at least one fragment contains PTS info, recompute PTS information for all fragments - if (PTSFrag) { - updateFragPTSDTS(newDetails, PTSFrag, PTSFrag.startPTS, PTSFrag.endPTS, PTSFrag.startDTS, PTSFrag.endDTS); - } else { - // ensure that delta is within oldFragments range - // also adjust sliding in case delta is 0 (we could have old=[50-60] and new=old=[50-61]) - // in that case we also need to adjust start offset of all fragments - adjustSliding(oldDetails, newDetails); - } - if (newFragments.length) { - newDetails.totalduration = newDetails.edge - newFragments[0].start; - } - newDetails.driftStartTime = oldDetails.driftStartTime; - newDetails.driftStart = oldDetails.driftStart; - const advancedDateTime = newDetails.advancedDateTime; - if (newDetails.advanced && advancedDateTime) { - const edge = newDetails.edge; - if (!newDetails.driftStart) { - newDetails.driftStartTime = advancedDateTime; - newDetails.driftStart = edge; - } - newDetails.driftEndTime = advancedDateTime; - newDetails.driftEnd = edge; - } else { - newDetails.driftEndTime = oldDetails.driftEndTime; - newDetails.driftEnd = oldDetails.driftEnd; - newDetails.advancedDateTime = oldDetails.advancedDateTime; - } -} -function mergeDateRanges(oldDateRanges, deltaDateRanges, recentlyRemovedDateranges) { - const dateRanges = _extends({}, oldDateRanges); - if (recentlyRemovedDateranges) { - recentlyRemovedDateranges.forEach(id => { - delete dateRanges[id]; - }); - } - Object.keys(deltaDateRanges).forEach(id => { - const dateRange = new DateRange(deltaDateRanges[id].attr, dateRanges[id]); - if (dateRange.isValid) { - dateRanges[id] = dateRange; - } else { - logger.warn(`Ignoring invalid Playlist Delta Update DATERANGE tag: "${JSON.stringify(deltaDateRanges[id].attr)}"`); - } - }); - return dateRanges; -} -function mapPartIntersection(oldParts, newParts, intersectionFn) { - if (oldParts && newParts) { - let delta = 0; - for (let i = 0, len = oldParts.length; i <= len; i++) { - const oldPart = oldParts[i]; - const newPart = newParts[i + delta]; - if (oldPart && newPart && oldPart.index === newPart.index && oldPart.fragment.sn === newPart.fragment.sn) { - intersectionFn(oldPart, newPart); - } else { - delta--; - } - } - } -} -function mapFragmentIntersection(oldDetails, newDetails, intersectionFn) { - const skippedSegments = newDetails.skippedSegments; - const start = Math.max(oldDetails.startSN, newDetails.startSN) - newDetails.startSN; - const end = (oldDetails.fragmentHint ? 1 : 0) + (skippedSegments ? newDetails.endSN : Math.min(oldDetails.endSN, newDetails.endSN)) - newDetails.startSN; - const delta = newDetails.startSN - oldDetails.startSN; - const newFrags = newDetails.fragmentHint ? newDetails.fragments.concat(newDetails.fragmentHint) : newDetails.fragments; - const oldFrags = oldDetails.fragmentHint ? oldDetails.fragments.concat(oldDetails.fragmentHint) : oldDetails.fragments; - for (let i = start; i <= end; i++) { - const oldFrag = oldFrags[delta + i]; - let newFrag = newFrags[i]; - if (skippedSegments && !newFrag && i < skippedSegments) { - // Fill in skipped segments in delta playlist - newFrag = newDetails.fragments[i] = oldFrag; - } - if (oldFrag && newFrag) { - intersectionFn(oldFrag, newFrag); - } - } -} -function adjustSliding(oldDetails, newDetails) { - const delta = newDetails.startSN + newDetails.skippedSegments - oldDetails.startSN; - const oldFragments = oldDetails.fragments; - if (delta < 0 || delta >= oldFragments.length) { - return; - } - addSliding(newDetails, oldFragments[delta].start); -} -function addSliding(details, start) { - if (start) { - const fragments = details.fragments; - for (let i = details.skippedSegments; i < fragments.length; i++) { - fragments[i].start += start; - } - if (details.fragmentHint) { - details.fragmentHint.start += start; - } - } -} -function computeReloadInterval(newDetails, distanceToLiveEdgeMs = Infinity) { - let reloadInterval = 1000 * newDetails.targetduration; - if (newDetails.updated) { - // Use last segment duration when shorter than target duration and near live edge - const fragments = newDetails.fragments; - const liveEdgeMaxTargetDurations = 4; - if (fragments.length && reloadInterval * liveEdgeMaxTargetDurations > distanceToLiveEdgeMs) { - const lastSegmentDuration = fragments[fragments.length - 1].duration * 1000; - if (lastSegmentDuration < reloadInterval) { - reloadInterval = lastSegmentDuration; - } - } - } else { - // estimate = 'miss half average'; - // follow HLS Spec, If the client reloads a Playlist file and finds that it has not - // changed then it MUST wait for a period of one-half the target - // duration before retrying. - reloadInterval /= 2; - } - return Math.round(reloadInterval); -} -function getFragmentWithSN(level, sn, fragCurrent) { - if (!(level != null && level.details)) { - return null; - } - const levelDetails = level.details; - let fragment = levelDetails.fragments[sn - levelDetails.startSN]; - if (fragment) { - return fragment; - } - fragment = levelDetails.fragmentHint; - if (fragment && fragment.sn === sn) { - return fragment; - } - if (sn < levelDetails.startSN && fragCurrent && fragCurrent.sn === sn) { - return fragCurrent; - } - return null; -} -function getPartWith(level, sn, partIndex) { - var _level$details; - if (!(level != null && level.details)) { - return null; - } - return findPart((_level$details = level.details) == null ? void 0 : _level$details.partList, sn, partIndex); -} -function findPart(partList, sn, partIndex) { - if (partList) { - for (let i = partList.length; i--;) { - const part = partList[i]; - if (part.index === partIndex && part.fragment.sn === sn) { - return part; - } - } - } - return null; -} -function reassignFragmentLevelIndexes(levels) { - levels.forEach((level, index) => { - const { - details - } = level; - if (details != null && details.fragments) { - details.fragments.forEach(fragment => { - fragment.level = index; - }); - } - }); -} - -function isTimeoutError(error) { - switch (error.details) { - case ErrorDetails.FRAG_LOAD_TIMEOUT: - case ErrorDetails.KEY_LOAD_TIMEOUT: - case ErrorDetails.LEVEL_LOAD_TIMEOUT: - case ErrorDetails.MANIFEST_LOAD_TIMEOUT: - return true; - } - return false; -} -function getRetryConfig(loadPolicy, error) { - const isTimeout = isTimeoutError(error); - return loadPolicy.default[`${isTimeout ? 'timeout' : 'error'}Retry`]; -} -function getRetryDelay(retryConfig, retryCount) { - // exponential backoff capped to max retry delay - const backoffFactor = retryConfig.backoff === 'linear' ? 1 : Math.pow(2, retryCount); - return Math.min(backoffFactor * retryConfig.retryDelayMs, retryConfig.maxRetryDelayMs); -} -function getLoaderConfigWithoutReties(loderConfig) { - return _objectSpread2(_objectSpread2({}, loderConfig), { - errorRetry: null, - timeoutRetry: null - }); -} -function shouldRetry(retryConfig, retryCount, isTimeout, loaderResponse) { - if (!retryConfig) { - return false; - } - const httpStatus = loaderResponse == null ? void 0 : loaderResponse.code; - const retry = retryCount < retryConfig.maxNumRetry && (retryForHttpStatus(httpStatus) || !!isTimeout); - return retryConfig.shouldRetry ? retryConfig.shouldRetry(retryConfig, retryCount, isTimeout, loaderResponse, retry) : retry; -} -function retryForHttpStatus(httpStatus) { - // Do not retry on status 4xx, status 0 (CORS error), or undefined (decrypt/gap/parse error) - return httpStatus === 0 && navigator.onLine === false || !!httpStatus && (httpStatus < 400 || httpStatus > 499); -} - -const BinarySearch = { - /** - * Searches for an item in an array which matches a certain condition. - * This requires the condition to only match one item in the array, - * and for the array to be ordered. - * - * @param list The array to search. - * @param comparisonFn - * Called and provided a candidate item as the first argument. - * Should return: - * > -1 if the item should be located at a lower index than the provided item. - * > 1 if the item should be located at a higher index than the provided item. - * > 0 if the item is the item you're looking for. - * - * @returns the object if found, otherwise returns null - */ - search: function (list, comparisonFn) { - let minIndex = 0; - let maxIndex = list.length - 1; - let currentIndex = null; - let currentElement = null; - while (minIndex <= maxIndex) { - currentIndex = (minIndex + maxIndex) / 2 | 0; - currentElement = list[currentIndex]; - const comparisonResult = comparisonFn(currentElement); - if (comparisonResult > 0) { - minIndex = currentIndex + 1; - } else if (comparisonResult < 0) { - maxIndex = currentIndex - 1; - } else { - return currentElement; - } - } - return null; - } -}; - -/** - * Returns first fragment whose endPdt value exceeds the given PDT, or null. - * @param fragments - The array of candidate fragments - * @param PDTValue - The PDT value which must be exceeded - * @param maxFragLookUpTolerance - The amount of time that a fragment's start/end can be within in order to be considered contiguous - */ -function findFragmentByPDT(fragments, PDTValue, maxFragLookUpTolerance) { - if (PDTValue === null || !Array.isArray(fragments) || !fragments.length || !isFiniteNumber(PDTValue)) { - return null; - } - - // if less than start - const startPDT = fragments[0].programDateTime; - if (PDTValue < (startPDT || 0)) { - return null; - } - const endPDT = fragments[fragments.length - 1].endProgramDateTime; - if (PDTValue >= (endPDT || 0)) { - return null; - } - maxFragLookUpTolerance = maxFragLookUpTolerance || 0; - for (let seg = 0; seg < fragments.length; ++seg) { - const frag = fragments[seg]; - if (pdtWithinToleranceTest(PDTValue, maxFragLookUpTolerance, frag)) { - return frag; - } - } - return null; -} - -/** - * Finds a fragment based on the SN of the previous fragment; or based on the needs of the current buffer. - * This method compensates for small buffer gaps by applying a tolerance to the start of any candidate fragment, thus - * breaking any traps which would cause the same fragment to be continuously selected within a small range. - * @param fragPrevious - The last frag successfully appended - * @param fragments - The array of candidate fragments - * @param bufferEnd - The end of the contiguous buffered range the playhead is currently within - * @param maxFragLookUpTolerance - The amount of time that a fragment's start/end can be within in order to be considered contiguous - * @returns a matching fragment or null - */ -function findFragmentByPTS(fragPrevious, fragments, bufferEnd = 0, maxFragLookUpTolerance = 0, nextFragLookupTolerance = 0.005) { - let fragNext = null; - if (fragPrevious) { - fragNext = fragments[fragPrevious.sn - fragments[0].sn + 1] || null; - // check for buffer-end rounding error - const bufferEdgeError = fragPrevious.endDTS - bufferEnd; - if (bufferEdgeError > 0 && bufferEdgeError < 0.0000015) { - bufferEnd += 0.0000015; - } - } else if (bufferEnd === 0 && fragments[0].start === 0) { - fragNext = fragments[0]; - } - // Prefer the next fragment if it's within tolerance - if (fragNext && ((!fragPrevious || fragPrevious.level === fragNext.level) && fragmentWithinToleranceTest(bufferEnd, maxFragLookUpTolerance, fragNext) === 0 || fragmentWithinFastStartSwitch(fragNext, fragPrevious, Math.min(nextFragLookupTolerance, maxFragLookUpTolerance)))) { - return fragNext; - } - // We might be seeking past the tolerance so find the best match - const foundFragment = BinarySearch.search(fragments, fragmentWithinToleranceTest.bind(null, bufferEnd, maxFragLookUpTolerance)); - if (foundFragment && (foundFragment !== fragPrevious || !fragNext)) { - return foundFragment; - } - // If no match was found return the next fragment after fragPrevious, or null - return fragNext; -} -function fragmentWithinFastStartSwitch(fragNext, fragPrevious, nextFragLookupTolerance) { - if (fragPrevious && fragPrevious.start === 0 && fragPrevious.level < fragNext.level && (fragPrevious.endPTS || 0) > 0) { - const firstDuration = fragPrevious.tagList.reduce((duration, tag) => { - if (tag[0] === 'INF') { - duration += parseFloat(tag[1]); - } - return duration; - }, nextFragLookupTolerance); - return fragNext.start <= firstDuration; - } - return false; -} - -/** - * The test function used by the findFragmentBySn's BinarySearch to look for the best match to the current buffer conditions. - * @param candidate - The fragment to test - * @param bufferEnd - The end of the current buffered range the playhead is currently within - * @param maxFragLookUpTolerance - The amount of time that a fragment's start can be within in order to be considered contiguous - * @returns 0 if it matches, 1 if too low, -1 if too high - */ -function fragmentWithinToleranceTest(bufferEnd = 0, maxFragLookUpTolerance = 0, candidate) { - // eagerly accept an accurate match (no tolerance) - if (candidate.start <= bufferEnd && candidate.start + candidate.duration > bufferEnd) { - return 0; - } - // offset should be within fragment boundary - config.maxFragLookUpTolerance - // this is to cope with situations like - // bufferEnd = 9.991 - // frag[Ø] : [0,10] - // frag[1] : [10,20] - // bufferEnd is within frag[0] range ... although what we are expecting is to return frag[1] here - // frag start frag start+duration - // |-----------------------------| - // <---> <---> - // ...--------><-----------------------------><---------.... - // previous frag matching fragment next frag - // return -1 return 0 return 1 - // logger.log(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`); - // Set the lookup tolerance to be small enough to detect the current segment - ensures we don't skip over very small segments - const candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0)); - if (candidate.start + candidate.duration - candidateLookupTolerance <= bufferEnd) { - return 1; - } else if (candidate.start - candidateLookupTolerance > bufferEnd && candidate.start) { - // if maxFragLookUpTolerance will have negative value then don't return -1 for first element - return -1; - } - return 0; -} - -/** - * The test function used by the findFragmentByPdt's BinarySearch to look for the best match to the current buffer conditions. - * This function tests the candidate's program date time values, as represented in Unix time - * @param candidate - The fragment to test - * @param pdtBufferEnd - The Unix time representing the end of the current buffered range - * @param maxFragLookUpTolerance - The amount of time that a fragment's start can be within in order to be considered contiguous - * @returns true if contiguous, false otherwise - */ -function pdtWithinToleranceTest(pdtBufferEnd, maxFragLookUpTolerance, candidate) { - const candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0)) * 1000; - - // endProgramDateTime can be null, default to zero - const endProgramDateTime = candidate.endProgramDateTime || 0; - return endProgramDateTime - candidateLookupTolerance > pdtBufferEnd; -} -function findFragWithCC(fragments, cc) { - return BinarySearch.search(fragments, candidate => { - if (candidate.cc < cc) { - return 1; - } else if (candidate.cc > cc) { - return -1; - } else { - return 0; - } - }); -} - -var NetworkErrorAction = { - DoNothing: 0, - SendEndCallback: 1, - SendAlternateToPenaltyBox: 2, - RemoveAlternatePermanently: 3, - InsertDiscontinuity: 4, - RetryRequest: 5 -}; -var ErrorActionFlags = { - None: 0, - MoveAllAlternatesMatchingHost: 1, - MoveAllAlternatesMatchingHDCP: 2, - SwitchToSDR: 4 -}; // Reserved for future use -class ErrorController { - constructor(hls) { - this.hls = void 0; - this.playlistError = 0; - this.penalizedRenditions = {}; - this.log = void 0; - this.warn = void 0; - this.error = void 0; - this.hls = hls; - this.log = logger.log.bind(logger, `[info]:`); - this.warn = logger.warn.bind(logger, `[warning]:`); - this.error = logger.error.bind(logger, `[error]:`); - this.registerListeners(); - } - registerListeners() { - const hls = this.hls; - hls.on(Events.ERROR, this.onError, this); - hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this); - } - unregisterListeners() { - const hls = this.hls; - if (!hls) { - return; - } - hls.off(Events.ERROR, this.onError, this); - hls.off(Events.ERROR, this.onErrorOut, this); - hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this); - } - destroy() { - this.unregisterListeners(); - // @ts-ignore - this.hls = null; - this.penalizedRenditions = {}; - } - startLoad(startPosition) {} - stopLoad() { - this.playlistError = 0; - } - getVariantLevelIndex(frag) { - return (frag == null ? void 0 : frag.type) === PlaylistLevelType.MAIN ? frag.level : this.hls.loadLevel; - } - onManifestLoading() { - this.playlistError = 0; - this.penalizedRenditions = {}; - } - onLevelUpdated() { - this.playlistError = 0; - } - onError(event, data) { - var _data$frag, _data$level; - if (data.fatal) { - return; - } - const hls = this.hls; - const context = data.context; - switch (data.details) { - case ErrorDetails.FRAG_LOAD_ERROR: - case ErrorDetails.FRAG_LOAD_TIMEOUT: - case ErrorDetails.KEY_LOAD_ERROR: - case ErrorDetails.KEY_LOAD_TIMEOUT: - data.errorAction = this.getFragRetryOrSwitchAction(data); - return; - case ErrorDetails.FRAG_PARSING_ERROR: - // ignore empty segment errors marked as gap - if ((_data$frag = data.frag) != null && _data$frag.gap) { - data.errorAction = { - action: NetworkErrorAction.DoNothing, - flags: ErrorActionFlags.None - }; - return; - } - // falls through - case ErrorDetails.FRAG_GAP: - case ErrorDetails.FRAG_DECRYPT_ERROR: - { - // Switch level if possible, otherwise allow retry count to reach max error retries - data.errorAction = this.getFragRetryOrSwitchAction(data); - data.errorAction.action = NetworkErrorAction.SendAlternateToPenaltyBox; - return; - } - case ErrorDetails.LEVEL_EMPTY_ERROR: - case ErrorDetails.LEVEL_PARSING_ERROR: - { - var _data$context, _data$context$levelDe; - // Only retry when empty and live - const levelIndex = data.parent === PlaylistLevelType.MAIN ? data.level : hls.loadLevel; - if (data.details === ErrorDetails.LEVEL_EMPTY_ERROR && !!((_data$context = data.context) != null && (_data$context$levelDe = _data$context.levelDetails) != null && _data$context$levelDe.live)) { - data.errorAction = this.getPlaylistRetryOrSwitchAction(data, levelIndex); - } else { - // Escalate to fatal if not retrying or switching - data.levelRetry = false; - data.errorAction = this.getLevelSwitchAction(data, levelIndex); - } - } - return; - case ErrorDetails.LEVEL_LOAD_ERROR: - case ErrorDetails.LEVEL_LOAD_TIMEOUT: - if (typeof (context == null ? void 0 : context.level) === 'number') { - data.errorAction = this.getPlaylistRetryOrSwitchAction(data, context.level); - } - return; - case ErrorDetails.AUDIO_TRACK_LOAD_ERROR: - case ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT: - case ErrorDetails.SUBTITLE_LOAD_ERROR: - case ErrorDetails.SUBTITLE_TRACK_LOAD_TIMEOUT: - if (context) { - const level = hls.levels[hls.loadLevel]; - if (level && (context.type === PlaylistContextType.AUDIO_TRACK && level.hasAudioGroup(context.groupId) || context.type === PlaylistContextType.SUBTITLE_TRACK && level.hasSubtitleGroup(context.groupId))) { - // Perform Pathway switch or Redundant failover if possible for fastest recovery - // otherwise allow playlist retry count to reach max error retries - data.errorAction = this.getPlaylistRetryOrSwitchAction(data, hls.loadLevel); - data.errorAction.action = NetworkErrorAction.SendAlternateToPenaltyBox; - data.errorAction.flags = ErrorActionFlags.MoveAllAlternatesMatchingHost; - return; - } - } - return; - case ErrorDetails.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED: - { - const level = hls.levels[hls.loadLevel]; - const restrictedHdcpLevel = level == null ? void 0 : level.attrs['HDCP-LEVEL']; - if (restrictedHdcpLevel) { - data.errorAction = { - action: NetworkErrorAction.SendAlternateToPenaltyBox, - flags: ErrorActionFlags.MoveAllAlternatesMatchingHDCP, - hdcpLevel: restrictedHdcpLevel - }; - } else { - this.keySystemError(data); - } - } - return; - case ErrorDetails.BUFFER_ADD_CODEC_ERROR: - case ErrorDetails.REMUX_ALLOC_ERROR: - case ErrorDetails.BUFFER_APPEND_ERROR: - data.errorAction = this.getLevelSwitchAction(data, (_data$level = data.level) != null ? _data$level : hls.loadLevel); - return; - case ErrorDetails.INTERNAL_EXCEPTION: - case ErrorDetails.BUFFER_APPENDING_ERROR: - case ErrorDetails.BUFFER_FULL_ERROR: - case ErrorDetails.LEVEL_SWITCH_ERROR: - case ErrorDetails.BUFFER_STALLED_ERROR: - case ErrorDetails.BUFFER_SEEK_OVER_HOLE: - case ErrorDetails.BUFFER_NUDGE_ON_STALL: - data.errorAction = { - action: NetworkErrorAction.DoNothing, - flags: ErrorActionFlags.None - }; - return; - } - if (data.type === ErrorTypes.KEY_SYSTEM_ERROR) { - this.keySystemError(data); - } - } - keySystemError(data) { - const levelIndex = this.getVariantLevelIndex(data.frag); - // Do not retry level. Escalate to fatal if switching levels fails. - data.levelRetry = false; - data.errorAction = this.getLevelSwitchAction(data, levelIndex); - } - getPlaylistRetryOrSwitchAction(data, levelIndex) { - const hls = this.hls; - const retryConfig = getRetryConfig(hls.config.playlistLoadPolicy, data); - const retryCount = this.playlistError++; - const retry = shouldRetry(retryConfig, retryCount, isTimeoutError(data), data.response); - if (retry) { - return { - action: NetworkErrorAction.RetryRequest, - flags: ErrorActionFlags.None, - retryConfig, - retryCount - }; - } - const errorAction = this.getLevelSwitchAction(data, levelIndex); - if (retryConfig) { - errorAction.retryConfig = retryConfig; - errorAction.retryCount = retryCount; - } - return errorAction; - } - getFragRetryOrSwitchAction(data) { - const hls = this.hls; - // Share fragment error count accross media options (main, audio, subs) - // This allows for level based rendition switching when media option assets fail - const variantLevelIndex = this.getVariantLevelIndex(data.frag); - const level = hls.levels[variantLevelIndex]; - const { - fragLoadPolicy, - keyLoadPolicy - } = hls.config; - const retryConfig = getRetryConfig(data.details.startsWith('key') ? keyLoadPolicy : fragLoadPolicy, data); - const fragmentErrors = hls.levels.reduce((acc, level) => acc + level.fragmentError, 0); - // Switch levels when out of retried or level index out of bounds - if (level) { - if (data.details !== ErrorDetails.FRAG_GAP) { - level.fragmentError++; - } - const retry = shouldRetry(retryConfig, fragmentErrors, isTimeoutError(data), data.response); - if (retry) { - return { - action: NetworkErrorAction.RetryRequest, - flags: ErrorActionFlags.None, - retryConfig, - retryCount: fragmentErrors - }; - } - } - // Reach max retry count, or Missing level reference - // Switch to valid index - const errorAction = this.getLevelSwitchAction(data, variantLevelIndex); - // Add retry details to allow skipping of FRAG_PARSING_ERROR - if (retryConfig) { - errorAction.retryConfig = retryConfig; - errorAction.retryCount = fragmentErrors; - } - return errorAction; - } - getLevelSwitchAction(data, levelIndex) { - const hls = this.hls; - if (levelIndex === null || levelIndex === undefined) { - levelIndex = hls.loadLevel; - } - const level = this.hls.levels[levelIndex]; - if (level) { - var _data$frag2, _data$context2; - const errorDetails = data.details; - level.loadError++; - if (errorDetails === ErrorDetails.BUFFER_APPEND_ERROR) { - level.fragmentError++; - } - // Search for next level to retry - let nextLevel = -1; - const { - levels, - loadLevel, - minAutoLevel, - maxAutoLevel - } = hls; - if (!hls.autoLevelEnabled) { - hls.loadLevel = -1; - } - const fragErrorType = (_data$frag2 = data.frag) == null ? void 0 : _data$frag2.type; - // Find alternate audio codec if available on audio codec error - const isAudioCodecError = fragErrorType === PlaylistLevelType.AUDIO && errorDetails === ErrorDetails.FRAG_PARSING_ERROR || data.sourceBufferName === 'audio' && (errorDetails === ErrorDetails.BUFFER_ADD_CODEC_ERROR || errorDetails === ErrorDetails.BUFFER_APPEND_ERROR); - const findAudioCodecAlternate = isAudioCodecError && levels.some(({ - audioCodec - }) => level.audioCodec !== audioCodec); - // Find alternate video codec if available on video codec error - const isVideoCodecError = data.sourceBufferName === 'video' && (errorDetails === ErrorDetails.BUFFER_ADD_CODEC_ERROR || errorDetails === ErrorDetails.BUFFER_APPEND_ERROR); - const findVideoCodecAlternate = isVideoCodecError && levels.some(({ - codecSet, - audioCodec - }) => level.codecSet !== codecSet && level.audioCodec === audioCodec); - const { - type: playlistErrorType, - groupId: playlistErrorGroupId - } = (_data$context2 = data.context) != null ? _data$context2 : {}; - for (let i = levels.length; i--;) { - const candidate = (i + loadLevel) % levels.length; - if (candidate !== loadLevel && candidate >= minAutoLevel && candidate <= maxAutoLevel && levels[candidate].loadError === 0) { - var _level$audioGroups, _level$subtitleGroups; - const levelCandidate = levels[candidate]; - // Skip level switch if GAP tag is found in next level at same position - if (errorDetails === ErrorDetails.FRAG_GAP && fragErrorType === PlaylistLevelType.MAIN && data.frag) { - const levelDetails = levels[candidate].details; - if (levelDetails) { - const fragCandidate = findFragmentByPTS(data.frag, levelDetails.fragments, data.frag.start); - if (fragCandidate != null && fragCandidate.gap) { - continue; - } - } - } else if (playlistErrorType === PlaylistContextType.AUDIO_TRACK && levelCandidate.hasAudioGroup(playlistErrorGroupId) || playlistErrorType === PlaylistContextType.SUBTITLE_TRACK && levelCandidate.hasSubtitleGroup(playlistErrorGroupId)) { - // For audio/subs playlist errors find another group ID or fallthrough to redundant fail-over - continue; - } else if (fragErrorType === PlaylistLevelType.AUDIO && (_level$audioGroups = level.audioGroups) != null && _level$audioGroups.some(groupId => levelCandidate.hasAudioGroup(groupId)) || fragErrorType === PlaylistLevelType.SUBTITLE && (_level$subtitleGroups = level.subtitleGroups) != null && _level$subtitleGroups.some(groupId => levelCandidate.hasSubtitleGroup(groupId)) || findAudioCodecAlternate && level.audioCodec === levelCandidate.audioCodec || !findAudioCodecAlternate && level.audioCodec !== levelCandidate.audioCodec || findVideoCodecAlternate && level.codecSet === levelCandidate.codecSet) { - // For video/audio/subs frag errors find another group ID or fallthrough to redundant fail-over - continue; - } - nextLevel = candidate; - break; - } - } - if (nextLevel > -1 && hls.loadLevel !== nextLevel) { - data.levelRetry = true; - this.playlistError = 0; - return { - action: NetworkErrorAction.SendAlternateToPenaltyBox, - flags: ErrorActionFlags.None, - nextAutoLevel: nextLevel - }; - } - } - // No levels to switch / Manual level selection / Level not found - // Resolve with Pathway switch, Redundant fail-over, or stay on lowest Level - return { - action: NetworkErrorAction.SendAlternateToPenaltyBox, - flags: ErrorActionFlags.MoveAllAlternatesMatchingHost - }; - } - onErrorOut(event, data) { - var _data$errorAction; - switch ((_data$errorAction = data.errorAction) == null ? void 0 : _data$errorAction.action) { - case NetworkErrorAction.DoNothing: - break; - case NetworkErrorAction.SendAlternateToPenaltyBox: - this.sendAlternateToPenaltyBox(data); - if (!data.errorAction.resolved && data.details !== ErrorDetails.FRAG_GAP) { - data.fatal = true; - } else if (/MediaSource readyState: ended/.test(data.error.message)) { - this.warn(`MediaSource ended after "${data.sourceBufferName}" sourceBuffer append error. Attempting to recover from media error.`); - this.hls.recoverMediaError(); - } - break; - case NetworkErrorAction.RetryRequest: - // handled by stream and playlist/level controllers - break; - } - if (data.fatal) { - this.hls.stopLoad(); - return; - } - } - sendAlternateToPenaltyBox(data) { - const hls = this.hls; - const errorAction = data.errorAction; - if (!errorAction) { - return; - } - const { - flags, - hdcpLevel, - nextAutoLevel - } = errorAction; - switch (flags) { - case ErrorActionFlags.None: - this.switchLevel(data, nextAutoLevel); - break; - case ErrorActionFlags.MoveAllAlternatesMatchingHDCP: - if (hdcpLevel) { - hls.maxHdcpLevel = HdcpLevels[HdcpLevels.indexOf(hdcpLevel) - 1]; - errorAction.resolved = true; - } - this.warn(`Restricting playback to HDCP-LEVEL of "${hls.maxHdcpLevel}" or lower`); - break; - } - // If not resolved by previous actions try to switch to next level - if (!errorAction.resolved) { - this.switchLevel(data, nextAutoLevel); - } - } - switchLevel(data, levelIndex) { - if (levelIndex !== undefined && data.errorAction) { - this.warn(`switching to level ${levelIndex} after ${data.details}`); - this.hls.nextAutoLevel = levelIndex; - data.errorAction.resolved = true; - // Stream controller is responsible for this but won't switch on false start - this.hls.nextLoadLevel = this.hls.nextAutoLevel; - } - } -} - -class BasePlaylistController { - constructor(hls, logPrefix) { - this.hls = void 0; - this.timer = -1; - this.requestScheduled = -1; - this.canLoad = false; - this.log = void 0; - this.warn = void 0; - this.log = logger.log.bind(logger, `${logPrefix}:`); - this.warn = logger.warn.bind(logger, `${logPrefix}:`); - this.hls = hls; - } - destroy() { - this.clearTimer(); - // @ts-ignore - this.hls = this.log = this.warn = null; - } - clearTimer() { - if (this.timer !== -1) { - self.clearTimeout(this.timer); - this.timer = -1; - } - } - startLoad() { - this.canLoad = true; - this.requestScheduled = -1; - this.loadPlaylist(); - } - stopLoad() { - this.canLoad = false; - this.clearTimer(); - } - switchParams(playlistUri, previous, current) { - const renditionReports = previous == null ? void 0 : previous.renditionReports; - if (renditionReports) { - let foundIndex = -1; - for (let i = 0; i < renditionReports.length; i++) { - const attr = renditionReports[i]; - let uri; - try { - uri = new self.URL(attr.URI, previous.url).href; - } catch (error) { - logger.warn(`Could not construct new URL for Rendition Report: ${error}`); - uri = attr.URI || ''; - } - // Use exact match. Otherwise, the last partial match, if any, will be used - // (Playlist URI includes a query string that the Rendition Report does not) - if (uri === playlistUri) { - foundIndex = i; - break; - } else if (uri === playlistUri.substring(0, uri.length)) { - foundIndex = i; - } - } - if (foundIndex !== -1) { - const attr = renditionReports[foundIndex]; - const msn = parseInt(attr['LAST-MSN']) || (previous == null ? void 0 : previous.lastPartSn); - let part = parseInt(attr['LAST-PART']) || (previous == null ? void 0 : previous.lastPartIndex); - if (this.hls.config.lowLatencyMode) { - const currentGoal = Math.min(previous.age - previous.partTarget, previous.targetduration); - if (part >= 0 && currentGoal > previous.partTarget) { - part += 1; - } - } - const skip = current && getSkipValue(current); - return new HlsUrlParameters(msn, part >= 0 ? part : undefined, skip); - } - } - } - loadPlaylist(hlsUrlParameters) { - if (this.requestScheduled === -1) { - this.requestScheduled = self.performance.now(); - } - // Loading is handled by the subclasses - } - shouldLoadPlaylist(playlist) { - return this.canLoad && !!playlist && !!playlist.url && (!playlist.details || playlist.details.live); - } - shouldReloadPlaylist(playlist) { - return this.timer === -1 && this.requestScheduled === -1 && this.shouldLoadPlaylist(playlist); - } - playlistLoaded(index, data, previousDetails) { - const { - details, - stats - } = data; - - // Set last updated date-time - const now = self.performance.now(); - const elapsed = stats.loading.first ? Math.max(0, now - stats.loading.first) : 0; - details.advancedDateTime = Date.now() - elapsed; - - // if current playlist is a live playlist, arm a timer to reload it - if (details.live || previousDetails != null && previousDetails.live) { - details.reloaded(previousDetails); - if (previousDetails) { - this.log(`live playlist ${index} ${details.advanced ? 'REFRESHED ' + details.lastPartSn + '-' + details.lastPartIndex : details.updated ? 'UPDATED' : 'MISSED'}`); - } - // Merge live playlists to adjust fragment starts and fill in delta playlist skipped segments - if (previousDetails && details.fragments.length > 0) { - mergeDetails(previousDetails, details); - } - if (!this.canLoad || !details.live) { - return; - } - let deliveryDirectives; - let msn = undefined; - let part = undefined; - if (details.canBlockReload && details.endSN && details.advanced) { - // Load level with LL-HLS delivery directives - const lowLatencyMode = this.hls.config.lowLatencyMode; - const lastPartSn = details.lastPartSn; - const endSn = details.endSN; - const lastPartIndex = details.lastPartIndex; - const hasParts = lastPartIndex !== -1; - const lastPart = lastPartSn === endSn; - // When low latency mode is disabled, we'll skip part requests once the last part index is found - const nextSnStartIndex = lowLatencyMode ? 0 : lastPartIndex; - if (hasParts) { - msn = lastPart ? endSn + 1 : lastPartSn; - part = lastPart ? nextSnStartIndex : lastPartIndex + 1; - } else { - msn = endSn + 1; - } - // Low-Latency CDN Tune-in: "age" header and time since load indicates we're behind by more than one part - // Update directives to obtain the Playlist that has the estimated additional duration of media - const lastAdvanced = details.age; - const cdnAge = lastAdvanced + details.ageHeader; - let currentGoal = Math.min(cdnAge - details.partTarget, details.targetduration * 1.5); - if (currentGoal > 0) { - if (previousDetails && currentGoal > previousDetails.tuneInGoal) { - // If we attempted to get the next or latest playlist update, but currentGoal increased, - // then we either can't catchup, or the "age" header cannot be trusted. - this.warn(`CDN Tune-in goal increased from: ${previousDetails.tuneInGoal} to: ${currentGoal} with playlist age: ${details.age}`); - currentGoal = 0; - } else { - const segments = Math.floor(currentGoal / details.targetduration); - msn += segments; - if (part !== undefined) { - const parts = Math.round(currentGoal % details.targetduration / details.partTarget); - part += parts; - } - this.log(`CDN Tune-in age: ${details.ageHeader}s last advanced ${lastAdvanced.toFixed(2)}s goal: ${currentGoal} skip sn ${segments} to part ${part}`); - } - details.tuneInGoal = currentGoal; - } - deliveryDirectives = this.getDeliveryDirectives(details, data.deliveryDirectives, msn, part); - if (lowLatencyMode || !lastPart) { - this.loadPlaylist(deliveryDirectives); - return; - } - } else if (details.canBlockReload || details.canSkipUntil) { - deliveryDirectives = this.getDeliveryDirectives(details, data.deliveryDirectives, msn, part); - } - const bufferInfo = this.hls.mainForwardBufferInfo; - const position = bufferInfo ? bufferInfo.end - bufferInfo.len : 0; - const distanceToLiveEdgeMs = (details.edge - position) * 1000; - const reloadInterval = computeReloadInterval(details, distanceToLiveEdgeMs); - if (details.updated && now > this.requestScheduled + reloadInterval) { - this.requestScheduled = stats.loading.start; - } - if (msn !== undefined && details.canBlockReload) { - this.requestScheduled = stats.loading.first + reloadInterval - (details.partTarget * 1000 || 1000); - } else if (this.requestScheduled === -1 || this.requestScheduled + reloadInterval < now) { - this.requestScheduled = now; - } else if (this.requestScheduled - now <= 0) { - this.requestScheduled += reloadInterval; - } - let estimatedTimeUntilUpdate = this.requestScheduled - now; - estimatedTimeUntilUpdate = Math.max(0, estimatedTimeUntilUpdate); - this.log(`reload live playlist ${index} in ${Math.round(estimatedTimeUntilUpdate)} ms`); - // this.log( - // `live reload ${details.updated ? 'REFRESHED' : 'MISSED'} - // reload in ${estimatedTimeUntilUpdate / 1000} - // round trip ${(stats.loading.end - stats.loading.start) / 1000} - // diff ${ - // (reloadInterval - - // (estimatedTimeUntilUpdate + - // stats.loading.end - - // stats.loading.start)) / - // 1000 - // } - // reload interval ${reloadInterval / 1000} - // target duration ${details.targetduration} - // distance to edge ${distanceToLiveEdgeMs / 1000}` - // ); - - this.timer = self.setTimeout(() => this.loadPlaylist(deliveryDirectives), estimatedTimeUntilUpdate); - } else { - this.clearTimer(); - } - } - getDeliveryDirectives(details, previousDeliveryDirectives, msn, part) { - let skip = getSkipValue(details); - if (previousDeliveryDirectives != null && previousDeliveryDirectives.skip && details.deltaUpdateFailed) { - msn = previousDeliveryDirectives.msn; - part = previousDeliveryDirectives.part; - skip = HlsSkip.No; - } - return new HlsUrlParameters(msn, part, skip); - } - checkRetry(errorEvent) { - const errorDetails = errorEvent.details; - const isTimeout = isTimeoutError(errorEvent); - const errorAction = errorEvent.errorAction; - const { - action, - retryCount = 0, - retryConfig - } = errorAction || {}; - const retry = !!errorAction && !!retryConfig && (action === NetworkErrorAction.RetryRequest || !errorAction.resolved && action === NetworkErrorAction.SendAlternateToPenaltyBox); - if (retry) { - var _errorEvent$context; - this.requestScheduled = -1; - if (retryCount >= retryConfig.maxNumRetry) { - return false; - } - if (isTimeout && (_errorEvent$context = errorEvent.context) != null && _errorEvent$context.deliveryDirectives) { - // The LL-HLS request already timed out so retry immediately - this.warn(`Retrying playlist loading ${retryCount + 1}/${retryConfig.maxNumRetry} after "${errorDetails}" without delivery-directives`); - this.loadPlaylist(); - } else { - const delay = getRetryDelay(retryConfig, retryCount); - // Schedule level/track reload - this.timer = self.setTimeout(() => this.loadPlaylist(), delay); - this.warn(`Retrying playlist loading ${retryCount + 1}/${retryConfig.maxNumRetry} after "${errorDetails}" in ${delay}ms`); - } - // `levelRetry = true` used to inform other controllers that a retry is happening - errorEvent.levelRetry = true; - errorAction.resolved = true; - } - return retry; - } -} - -/* - * compute an Exponential Weighted moving average - * - https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average - * - heavily inspired from shaka-player - */ - -class EWMA { - // About half of the estimated value will be from the last |halfLife| samples by weight. - constructor(halfLife, estimate = 0, weight = 0) { - this.halfLife = void 0; - this.alpha_ = void 0; - this.estimate_ = void 0; - this.totalWeight_ = void 0; - this.halfLife = halfLife; - // Larger values of alpha expire historical data more slowly. - this.alpha_ = halfLife ? Math.exp(Math.log(0.5) / halfLife) : 0; - this.estimate_ = estimate; - this.totalWeight_ = weight; - } - sample(weight, value) { - const adjAlpha = Math.pow(this.alpha_, weight); - this.estimate_ = value * (1 - adjAlpha) + adjAlpha * this.estimate_; - this.totalWeight_ += weight; - } - getTotalWeight() { - return this.totalWeight_; - } - getEstimate() { - if (this.alpha_) { - const zeroFactor = 1 - Math.pow(this.alpha_, this.totalWeight_); - if (zeroFactor) { - return this.estimate_ / zeroFactor; - } - } - return this.estimate_; - } -} - -/* - * EWMA Bandwidth Estimator - * - heavily inspired from shaka-player - * Tracks bandwidth samples and estimates available bandwidth. - * Based on the minimum of two exponentially-weighted moving averages with - * different half-lives. - */ - -class EwmaBandWidthEstimator { - constructor(slow, fast, defaultEstimate, defaultTTFB = 100) { - this.defaultEstimate_ = void 0; - this.minWeight_ = void 0; - this.minDelayMs_ = void 0; - this.slow_ = void 0; - this.fast_ = void 0; - this.defaultTTFB_ = void 0; - this.ttfb_ = void 0; - this.defaultEstimate_ = defaultEstimate; - this.minWeight_ = 0.001; - this.minDelayMs_ = 50; - this.slow_ = new EWMA(slow); - this.fast_ = new EWMA(fast); - this.defaultTTFB_ = defaultTTFB; - this.ttfb_ = new EWMA(slow); - } - update(slow, fast) { - const { - slow_, - fast_, - ttfb_ - } = this; - if (slow_.halfLife !== slow) { - this.slow_ = new EWMA(slow, slow_.getEstimate(), slow_.getTotalWeight()); - } - if (fast_.halfLife !== fast) { - this.fast_ = new EWMA(fast, fast_.getEstimate(), fast_.getTotalWeight()); - } - if (ttfb_.halfLife !== slow) { - this.ttfb_ = new EWMA(slow, ttfb_.getEstimate(), ttfb_.getTotalWeight()); - } - } - sample(durationMs, numBytes) { - durationMs = Math.max(durationMs, this.minDelayMs_); - const numBits = 8 * numBytes; - // weight is duration in seconds - const durationS = durationMs / 1000; - // value is bandwidth in bits/s - const bandwidthInBps = numBits / durationS; - this.fast_.sample(durationS, bandwidthInBps); - this.slow_.sample(durationS, bandwidthInBps); - } - sampleTTFB(ttfb) { - // weight is frequency curve applied to TTFB in seconds - // (longer times have less weight with expected input under 1 second) - const seconds = ttfb / 1000; - const weight = Math.sqrt(2) * Math.exp(-Math.pow(seconds, 2) / 2); - this.ttfb_.sample(weight, Math.max(ttfb, 5)); - } - canEstimate() { - return this.fast_.getTotalWeight() >= this.minWeight_; - } - getEstimate() { - if (this.canEstimate()) { - // console.log('slow estimate:'+ Math.round(this.slow_.getEstimate())); - // console.log('fast estimate:'+ Math.round(this.fast_.getEstimate())); - // Take the minimum of these two estimates. This should have the effect of - // adapting down quickly, but up more slowly. - return Math.min(this.fast_.getEstimate(), this.slow_.getEstimate()); - } else { - return this.defaultEstimate_; - } - } - getEstimateTTFB() { - if (this.ttfb_.getTotalWeight() >= this.minWeight_) { - return this.ttfb_.getEstimate(); - } else { - return this.defaultTTFB_; - } - } - destroy() {} -} - -const SUPPORTED_INFO_DEFAULT = { - supported: true, - configurations: [], - decodingInfoResults: [{ - supported: true, - powerEfficient: true, - smooth: true - }] -}; -const SUPPORTED_INFO_CACHE = {}; -function requiresMediaCapabilitiesDecodingInfo(level, audioTracksByGroup, currentVideoRange, currentFrameRate, currentBw, audioPreference) { - // Only test support when configuration is exceeds minimum options - const audioGroups = level.audioCodec ? level.audioGroups : null; - const audioCodecPreference = audioPreference == null ? void 0 : audioPreference.audioCodec; - const channelsPreference = audioPreference == null ? void 0 : audioPreference.channels; - const maxChannels = channelsPreference ? parseInt(channelsPreference) : audioCodecPreference ? Infinity : 2; - let audioChannels = null; - if (audioGroups != null && audioGroups.length) { - try { - if (audioGroups.length === 1 && audioGroups[0]) { - audioChannels = audioTracksByGroup.groups[audioGroups[0]].channels; - } else { - audioChannels = audioGroups.reduce((acc, groupId) => { - if (groupId) { - const audioTrackGroup = audioTracksByGroup.groups[groupId]; - if (!audioTrackGroup) { - throw new Error(`Audio track group ${groupId} not found`); - } - // Sum all channel key values - Object.keys(audioTrackGroup.channels).forEach(key => { - acc[key] = (acc[key] || 0) + audioTrackGroup.channels[key]; - }); - } - return acc; - }, { - 2: 0 - }); - } - } catch (error) { - return true; - } - } - return level.videoCodec !== undefined && (level.width > 1920 && level.height > 1088 || level.height > 1920 && level.width > 1088 || level.frameRate > Math.max(currentFrameRate, 30) || level.videoRange !== 'SDR' && level.videoRange !== currentVideoRange || level.bitrate > Math.max(currentBw, 8e6)) || !!audioChannels && isFiniteNumber(maxChannels) && Object.keys(audioChannels).some(channels => parseInt(channels) > maxChannels); -} -function getMediaDecodingInfoPromise(level, audioTracksByGroup, mediaCapabilities) { - const videoCodecs = level.videoCodec; - const audioCodecs = level.audioCodec; - if (!videoCodecs || !audioCodecs || !mediaCapabilities) { - return Promise.resolve(SUPPORTED_INFO_DEFAULT); - } - const baseVideoConfiguration = { - width: level.width, - height: level.height, - bitrate: Math.ceil(Math.max(level.bitrate * 0.9, level.averageBitrate)), - // Assume a framerate of 30fps since MediaCapabilities will not accept Level default of 0. - framerate: level.frameRate || 30 - }; - const videoRange = level.videoRange; - if (videoRange !== 'SDR') { - baseVideoConfiguration.transferFunction = videoRange.toLowerCase(); - } - const configurations = videoCodecs.split(',').map(videoCodec => ({ - type: 'media-source', - video: _objectSpread2(_objectSpread2({}, baseVideoConfiguration), {}, { - contentType: mimeTypeForCodec(videoCodec, 'video') - }) - })); - if (audioCodecs && level.audioGroups) { - level.audioGroups.forEach(audioGroupId => { - var _audioTracksByGroup$g; - if (!audioGroupId) { - return; - } - (_audioTracksByGroup$g = audioTracksByGroup.groups[audioGroupId]) == null ? void 0 : _audioTracksByGroup$g.tracks.forEach(audioTrack => { - if (audioTrack.groupId === audioGroupId) { - const channels = audioTrack.channels || ''; - const channelsNumber = parseFloat(channels); - if (isFiniteNumber(channelsNumber) && channelsNumber > 2) { - configurations.push.apply(configurations, audioCodecs.split(',').map(audioCodec => ({ - type: 'media-source', - audio: { - contentType: mimeTypeForCodec(audioCodec, 'audio'), - channels: '' + channelsNumber - // spatialRendering: - // audioCodec === 'ec-3' && channels.indexOf('JOC'), - } - }))); - } - } - }); - }); - } - return Promise.all(configurations.map(configuration => { - // Cache MediaCapabilities promises - const decodingInfoKey = getMediaDecodingInfoKey(configuration); - return SUPPORTED_INFO_CACHE[decodingInfoKey] || (SUPPORTED_INFO_CACHE[decodingInfoKey] = mediaCapabilities.decodingInfo(configuration)); - })).then(decodingInfoResults => ({ - supported: !decodingInfoResults.some(info => !info.supported), - configurations, - decodingInfoResults - })).catch(error => ({ - supported: false, - configurations, - decodingInfoResults: [], - error - })); -} -function getMediaDecodingInfoKey(config) { - const { - audio, - video - } = config; - const mediaConfig = video || audio; - if (mediaConfig) { - const codec = mediaConfig.contentType.split('"')[1]; - if (video) { - return `r${video.height}x${video.width}f${Math.ceil(video.framerate)}${video.transferFunction || 'sd'}_${codec}_${Math.ceil(video.bitrate / 1e5)}`; - } - if (audio) { - return `c${audio.channels}${audio.spatialRendering ? 's' : 'n'}_${codec}`; - } - } - return ''; -} - -/** - * @returns Whether we can detect and validate HDR capability within the window context - */ -function isHdrSupported() { - if (typeof matchMedia === 'function') { - const mediaQueryList = matchMedia('(dynamic-range: high)'); - const badQuery = matchMedia('bad query'); - if (mediaQueryList.media !== badQuery.media) { - return mediaQueryList.matches === true; - } - } - return false; -} - -/** - * Sanitizes inputs to return the active video selection options for HDR/SDR. - * When both inputs are null: - * - * `{ preferHDR: false, allowedVideoRanges: [] }` - * - * When `currentVideoRange` non-null, maintain the active range: - * - * `{ preferHDR: currentVideoRange !== 'SDR', allowedVideoRanges: [currentVideoRange] }` - * - * When VideoSelectionOption non-null: - * - * - Allow all video ranges if `allowedVideoRanges` unspecified. - * - If `preferHDR` is non-null use the value to filter `allowedVideoRanges`. - * - Else check window for HDR support and set `preferHDR` to the result. - * - * @param currentVideoRange - * @param videoPreference - */ -function getVideoSelectionOptions(currentVideoRange, videoPreference) { - let preferHDR = false; - let allowedVideoRanges = []; - if (currentVideoRange) { - preferHDR = currentVideoRange !== 'SDR'; - allowedVideoRanges = [currentVideoRange]; - } - if (videoPreference) { - allowedVideoRanges = videoPreference.allowedVideoRanges || VideoRangeValues.slice(0); - preferHDR = videoPreference.preferHDR !== undefined ? videoPreference.preferHDR : isHdrSupported(); - if (preferHDR) { - allowedVideoRanges = allowedVideoRanges.filter(range => range !== 'SDR'); - } else { - allowedVideoRanges = ['SDR']; - } - } - return { - preferHDR, - allowedVideoRanges - }; -} - -function getStartCodecTier(codecTiers, currentVideoRange, currentBw, audioPreference, videoPreference) { - const codecSets = Object.keys(codecTiers); - const channelsPreference = audioPreference == null ? void 0 : audioPreference.channels; - const audioCodecPreference = audioPreference == null ? void 0 : audioPreference.audioCodec; - const preferStereo = channelsPreference && parseInt(channelsPreference) === 2; - // Use first level set to determine stereo, and minimum resolution and framerate - let hasStereo = true; - let hasCurrentVideoRange = false; - let minHeight = Infinity; - let minFramerate = Infinity; - let minBitrate = Infinity; - let selectedScore = 0; - let videoRanges = []; - const { - preferHDR, - allowedVideoRanges - } = getVideoSelectionOptions(currentVideoRange, videoPreference); - for (let i = codecSets.length; i--;) { - const tier = codecTiers[codecSets[i]]; - hasStereo = tier.channels[2] > 0; - minHeight = Math.min(minHeight, tier.minHeight); - minFramerate = Math.min(minFramerate, tier.minFramerate); - minBitrate = Math.min(minBitrate, tier.minBitrate); - const matchingVideoRanges = allowedVideoRanges.filter(range => tier.videoRanges[range] > 0); - if (matchingVideoRanges.length > 0) { - hasCurrentVideoRange = true; - videoRanges = matchingVideoRanges; - } - } - minHeight = isFiniteNumber(minHeight) ? minHeight : 0; - minFramerate = isFiniteNumber(minFramerate) ? minFramerate : 0; - const maxHeight = Math.max(1080, minHeight); - const maxFramerate = Math.max(30, minFramerate); - minBitrate = isFiniteNumber(minBitrate) ? minBitrate : currentBw; - currentBw = Math.max(minBitrate, currentBw); - // If there are no variants with matching preference, set currentVideoRange to undefined - if (!hasCurrentVideoRange) { - currentVideoRange = undefined; - videoRanges = []; - } - const codecSet = codecSets.reduce((selected, candidate) => { - // Remove candiates which do not meet bitrate, default audio, stereo or channels preference, 1080p or lower, 30fps or lower, or SDR/HDR selection if present - const candidateTier = codecTiers[candidate]; - if (candidate === selected) { - return selected; - } - if (candidateTier.minBitrate > currentBw) { - logStartCodecCandidateIgnored(candidate, `min bitrate of ${candidateTier.minBitrate} > current estimate of ${currentBw}`); - return selected; - } - if (!candidateTier.hasDefaultAudio) { - logStartCodecCandidateIgnored(candidate, `no renditions with default or auto-select sound found`); - return selected; - } - if (audioCodecPreference && candidate.indexOf(audioCodecPreference.substring(0, 4)) % 5 !== 0) { - logStartCodecCandidateIgnored(candidate, `audio codec preference "${audioCodecPreference}" not found`); - return selected; - } - if (channelsPreference && !preferStereo) { - if (!candidateTier.channels[channelsPreference]) { - logStartCodecCandidateIgnored(candidate, `no renditions with ${channelsPreference} channel sound found (channels options: ${Object.keys(candidateTier.channels)})`); - return selected; - } - } else if ((!audioCodecPreference || preferStereo) && hasStereo && candidateTier.channels['2'] === 0) { - logStartCodecCandidateIgnored(candidate, `no renditions with stereo sound found`); - return selected; - } - if (candidateTier.minHeight > maxHeight) { - logStartCodecCandidateIgnored(candidate, `min resolution of ${candidateTier.minHeight} > maximum of ${maxHeight}`); - return selected; - } - if (candidateTier.minFramerate > maxFramerate) { - logStartCodecCandidateIgnored(candidate, `min framerate of ${candidateTier.minFramerate} > maximum of ${maxFramerate}`); - return selected; - } - if (!videoRanges.some(range => candidateTier.videoRanges[range] > 0)) { - logStartCodecCandidateIgnored(candidate, `no variants with VIDEO-RANGE of ${JSON.stringify(videoRanges)} found`); - return selected; - } - if (candidateTier.maxScore < selectedScore) { - logStartCodecCandidateIgnored(candidate, `max score of ${candidateTier.maxScore} < selected max of ${selectedScore}`); - return selected; - } - // Remove candiates with less preferred codecs or more errors - if (selected && (codecsSetSelectionPreferenceValue(candidate) >= codecsSetSelectionPreferenceValue(selected) || candidateTier.fragmentError > codecTiers[selected].fragmentError)) { - return selected; - } - selectedScore = candidateTier.maxScore; - return candidate; - }, undefined); - return { - codecSet, - videoRanges, - preferHDR, - minFramerate, - minBitrate - }; -} -function logStartCodecCandidateIgnored(codeSet, reason) { - logger.log(`[abr] start candidates with "${codeSet}" ignored because ${reason}`); -} -function getAudioTracksByGroup(allAudioTracks) { - return allAudioTracks.reduce((audioTracksByGroup, track) => { - let trackGroup = audioTracksByGroup.groups[track.groupId]; - if (!trackGroup) { - trackGroup = audioTracksByGroup.groups[track.groupId] = { - tracks: [], - channels: { - 2: 0 - }, - hasDefault: false, - hasAutoSelect: false - }; - } - trackGroup.tracks.push(track); - const channelsKey = track.channels || '2'; - trackGroup.channels[channelsKey] = (trackGroup.channels[channelsKey] || 0) + 1; - trackGroup.hasDefault = trackGroup.hasDefault || track.default; - trackGroup.hasAutoSelect = trackGroup.hasAutoSelect || track.autoselect; - if (trackGroup.hasDefault) { - audioTracksByGroup.hasDefaultAudio = true; - } - if (trackGroup.hasAutoSelect) { - audioTracksByGroup.hasAutoSelectAudio = true; - } - return audioTracksByGroup; - }, { - hasDefaultAudio: false, - hasAutoSelectAudio: false, - groups: {} - }); -} -function getCodecTiers(levels, audioTracksByGroup, minAutoLevel, maxAutoLevel) { - return levels.slice(minAutoLevel, maxAutoLevel + 1).reduce((tiers, level) => { - if (!level.codecSet) { - return tiers; - } - const audioGroups = level.audioGroups; - let tier = tiers[level.codecSet]; - if (!tier) { - tiers[level.codecSet] = tier = { - minBitrate: Infinity, - minHeight: Infinity, - minFramerate: Infinity, - maxScore: 0, - videoRanges: { - SDR: 0 - }, - channels: { - '2': 0 - }, - hasDefaultAudio: !audioGroups, - fragmentError: 0 - }; - } - tier.minBitrate = Math.min(tier.minBitrate, level.bitrate); - const lesserWidthOrHeight = Math.min(level.height, level.width); - tier.minHeight = Math.min(tier.minHeight, lesserWidthOrHeight); - tier.minFramerate = Math.min(tier.minFramerate, level.frameRate); - tier.maxScore = Math.max(tier.maxScore, level.score); - tier.fragmentError += level.fragmentError; - tier.videoRanges[level.videoRange] = (tier.videoRanges[level.videoRange] || 0) + 1; - if (audioGroups) { - audioGroups.forEach(audioGroupId => { - if (!audioGroupId) { - return; - } - const audioGroup = audioTracksByGroup.groups[audioGroupId]; - if (!audioGroup) { - return; - } - // Default audio is any group with DEFAULT=YES, or if missing then any group with AUTOSELECT=YES, or all variants - tier.hasDefaultAudio = tier.hasDefaultAudio || audioTracksByGroup.hasDefaultAudio ? audioGroup.hasDefault : audioGroup.hasAutoSelect || !audioTracksByGroup.hasDefaultAudio && !audioTracksByGroup.hasAutoSelectAudio; - Object.keys(audioGroup.channels).forEach(channels => { - tier.channels[channels] = (tier.channels[channels] || 0) + audioGroup.channels[channels]; - }); - }); - } - return tiers; - }, {}); -} -function findMatchingOption(option, tracks, matchPredicate) { - if ('attrs' in option) { - const index = tracks.indexOf(option); - if (index !== -1) { - return index; - } - } - for (let i = 0; i < tracks.length; i++) { - const track = tracks[i]; - if (matchesOption(option, track, matchPredicate)) { - return i; - } - } - return -1; -} -function matchesOption(option, track, matchPredicate) { - const { - groupId, - name, - lang, - assocLang, - characteristics, - default: isDefault - } = option; - const forced = option.forced; - return (groupId === undefined || track.groupId === groupId) && (name === undefined || track.name === name) && (lang === undefined || track.lang === lang) && (lang === undefined || track.assocLang === assocLang) && (isDefault === undefined || track.default === isDefault) && (forced === undefined || track.forced === forced) && (characteristics === undefined || characteristicsMatch(characteristics, track.characteristics)) && (matchPredicate === undefined || matchPredicate(option, track)); -} -function characteristicsMatch(characteristicsA, characteristicsB = '') { - const arrA = characteristicsA.split(','); - const arrB = characteristicsB.split(','); - // Expects each item to be unique: - return arrA.length === arrB.length && !arrA.some(el => arrB.indexOf(el) === -1); -} -function audioMatchPredicate(option, track) { - const { - audioCodec, - channels - } = option; - return (audioCodec === undefined || (track.audioCodec || '').substring(0, 4) === audioCodec.substring(0, 4)) && (channels === undefined || channels === (track.channels || '2')); -} -function findClosestLevelWithAudioGroup(option, levels, allAudioTracks, searchIndex, matchPredicate) { - const currentLevel = levels[searchIndex]; - // Are there variants with same URI as current level? - // If so, find a match that does not require any level URI change - const variants = levels.reduce((variantMap, level, index) => { - const uri = level.uri; - const renditions = variantMap[uri] || (variantMap[uri] = []); - renditions.push(index); - return variantMap; - }, {}); - const renditions = variants[currentLevel.uri]; - if (renditions.length > 1) { - searchIndex = Math.max.apply(Math, renditions); - } - // Find best match - const currentVideoRange = currentLevel.videoRange; - const currentFrameRate = currentLevel.frameRate; - const currentVideoCodec = currentLevel.codecSet.substring(0, 4); - const matchingVideo = searchDownAndUpList(levels, searchIndex, level => { - if (level.videoRange !== currentVideoRange || level.frameRate !== currentFrameRate || level.codecSet.substring(0, 4) !== currentVideoCodec) { - return false; - } - const audioGroups = level.audioGroups; - const tracks = allAudioTracks.filter(track => !audioGroups || audioGroups.indexOf(track.groupId) !== -1); - return findMatchingOption(option, tracks, matchPredicate) > -1; - }); - if (matchingVideo > -1) { - return matchingVideo; - } - return searchDownAndUpList(levels, searchIndex, level => { - const audioGroups = level.audioGroups; - const tracks = allAudioTracks.filter(track => !audioGroups || audioGroups.indexOf(track.groupId) !== -1); - return findMatchingOption(option, tracks, matchPredicate) > -1; - }); -} -function searchDownAndUpList(arr, searchIndex, predicate) { - for (let i = searchIndex; i; i--) { - if (predicate(arr[i])) { - return i; - } - } - for (let i = searchIndex + 1; i < arr.length; i++) { - if (predicate(arr[i])) { - return i; - } - } - return -1; -} - -class AbrController { - constructor(_hls) { - this.hls = void 0; - this.lastLevelLoadSec = 0; - this.lastLoadedFragLevel = -1; - this.firstSelection = -1; - this._nextAutoLevel = -1; - this.nextAutoLevelKey = ''; - this.audioTracksByGroup = null; - this.codecTiers = null; - this.timer = -1; - this.fragCurrent = null; - this.partCurrent = null; - this.bitrateTestDelay = 0; - this.bwEstimator = void 0; - /* - This method monitors the download rate of the current fragment, and will downswitch if that fragment will not load - quickly enough to prevent underbuffering - */ - this._abandonRulesCheck = () => { - const { - fragCurrent: frag, - partCurrent: part, - hls - } = this; - const { - autoLevelEnabled, - media - } = hls; - if (!frag || !media) { - return; - } - const now = performance.now(); - const stats = part ? part.stats : frag.stats; - const duration = part ? part.duration : frag.duration; - const timeLoading = now - stats.loading.start; - const minAutoLevel = hls.minAutoLevel; - // If frag loading is aborted, complete, or from lowest level, stop timer and return - if (stats.aborted || stats.loaded && stats.loaded === stats.total || frag.level <= minAutoLevel) { - this.clearTimer(); - // reset forced auto level value so that next level will be selected - this._nextAutoLevel = -1; - return; - } - - // This check only runs if we're in ABR mode and actually playing - if (!autoLevelEnabled || media.paused || !media.playbackRate || !media.readyState) { - return; - } - const bufferInfo = hls.mainForwardBufferInfo; - if (bufferInfo === null) { - return; - } - const ttfbEstimate = this.bwEstimator.getEstimateTTFB(); - const playbackRate = Math.abs(media.playbackRate); - // To maintain stable adaptive playback, only begin monitoring frag loading after half or more of its playback duration has passed - if (timeLoading <= Math.max(ttfbEstimate, 1000 * (duration / (playbackRate * 2)))) { - return; - } - - // bufferStarvationDelay is an estimate of the amount time (in seconds) it will take to exhaust the buffer - const bufferStarvationDelay = bufferInfo.len / playbackRate; - const ttfb = stats.loading.first ? stats.loading.first - stats.loading.start : -1; - const loadedFirstByte = stats.loaded && ttfb > -1; - const bwEstimate = this.getBwEstimate(); - const levels = hls.levels; - const level = levels[frag.level]; - const expectedLen = stats.total || Math.max(stats.loaded, Math.round(duration * level.averageBitrate / 8)); - let timeStreaming = loadedFirstByte ? timeLoading - ttfb : timeLoading; - if (timeStreaming < 1 && loadedFirstByte) { - timeStreaming = Math.min(timeLoading, stats.loaded * 8 / bwEstimate); - } - const loadRate = loadedFirstByte ? stats.loaded * 1000 / timeStreaming : 0; - // fragLoadDelay is an estimate of the time (in seconds) it will take to buffer the remainder of the fragment - const fragLoadedDelay = loadRate ? (expectedLen - stats.loaded) / loadRate : expectedLen * 8 / bwEstimate + ttfbEstimate / 1000; - // Only downswitch if the time to finish loading the current fragment is greater than the amount of buffer left - if (fragLoadedDelay <= bufferStarvationDelay) { - return; - } - const bwe = loadRate ? loadRate * 8 : bwEstimate; - let fragLevelNextLoadedDelay = Number.POSITIVE_INFINITY; - let nextLoadLevel; - // Iterate through lower level and try to find the largest one that avoids rebuffering - for (nextLoadLevel = frag.level - 1; nextLoadLevel > minAutoLevel; nextLoadLevel--) { - // compute time to load next fragment at lower level - // 8 = bits per byte (bps/Bps) - const levelNextBitrate = levels[nextLoadLevel].maxBitrate; - fragLevelNextLoadedDelay = this.getTimeToLoadFrag(ttfbEstimate / 1000, bwe, duration * levelNextBitrate, !levels[nextLoadLevel].details); - if (fragLevelNextLoadedDelay < bufferStarvationDelay) { - break; - } - } - // Only emergency switch down if it takes less time to load a new fragment at lowest level instead of continuing - // to load the current one - if (fragLevelNextLoadedDelay >= fragLoadedDelay) { - return; - } - - // if estimated load time of new segment is completely unreasonable, ignore and do not emergency switch down - if (fragLevelNextLoadedDelay > duration * 10) { - return; - } - hls.nextLoadLevel = hls.nextAutoLevel = nextLoadLevel; - if (loadedFirstByte) { - // If there has been loading progress, sample bandwidth using loading time offset by minimum TTFB time - this.bwEstimator.sample(timeLoading - Math.min(ttfbEstimate, ttfb), stats.loaded); - } else { - // If there has been no loading progress, sample TTFB - this.bwEstimator.sampleTTFB(timeLoading); - } - const nextLoadLevelBitrate = levels[nextLoadLevel].maxBitrate; - if (this.getBwEstimate() * this.hls.config.abrBandWidthUpFactor > nextLoadLevelBitrate) { - this.resetEstimator(nextLoadLevelBitrate); - } - this.clearTimer(); - logger.warn(`[abr] Fragment ${frag.sn}${part ? ' part ' + part.index : ''} of level ${frag.level} is loading too slowly; - Time to underbuffer: ${bufferStarvationDelay.toFixed(3)} s - Estimated load time for current fragment: ${fragLoadedDelay.toFixed(3)} s - Estimated load time for down switch fragment: ${fragLevelNextLoadedDelay.toFixed(3)} s - TTFB estimate: ${ttfb | 0} ms - Current BW estimate: ${isFiniteNumber(bwEstimate) ? bwEstimate | 0 : 'Unknown'} bps - New BW estimate: ${this.getBwEstimate() | 0} bps - Switching to level ${nextLoadLevel} @ ${nextLoadLevelBitrate | 0} bps`); - hls.trigger(Events.FRAG_LOAD_EMERGENCY_ABORTED, { - frag, - part, - stats - }); - }; - this.hls = _hls; - this.bwEstimator = this.initEstimator(); - this.registerListeners(); - } - resetEstimator(abrEwmaDefaultEstimate) { - if (abrEwmaDefaultEstimate) { - logger.log(`setting initial bwe to ${abrEwmaDefaultEstimate}`); - this.hls.config.abrEwmaDefaultEstimate = abrEwmaDefaultEstimate; - } - this.firstSelection = -1; - this.bwEstimator = this.initEstimator(); - } - initEstimator() { - const config = this.hls.config; - return new EwmaBandWidthEstimator(config.abrEwmaSlowVoD, config.abrEwmaFastVoD, config.abrEwmaDefaultEstimate); - } - registerListeners() { - const { - hls - } = this; - hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.on(Events.FRAG_LOADING, this.onFragLoading, this); - hls.on(Events.FRAG_LOADED, this.onFragLoaded, this); - hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this); - hls.on(Events.LEVEL_SWITCHING, this.onLevelSwitching, this); - hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this); - hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); - hls.on(Events.MAX_AUTO_LEVEL_UPDATED, this.onMaxAutoLevelUpdated, this); - hls.on(Events.ERROR, this.onError, this); - } - unregisterListeners() { - const { - hls - } = this; - if (!hls) { - return; - } - hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.off(Events.FRAG_LOADING, this.onFragLoading, this); - hls.off(Events.FRAG_LOADED, this.onFragLoaded, this); - hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this); - hls.off(Events.LEVEL_SWITCHING, this.onLevelSwitching, this); - hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this); - hls.off(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); - hls.off(Events.MAX_AUTO_LEVEL_UPDATED, this.onMaxAutoLevelUpdated, this); - hls.off(Events.ERROR, this.onError, this); - } - destroy() { - this.unregisterListeners(); - this.clearTimer(); - // @ts-ignore - this.hls = this._abandonRulesCheck = null; - this.fragCurrent = this.partCurrent = null; - } - onManifestLoading(event, data) { - this.lastLoadedFragLevel = -1; - this.firstSelection = -1; - this.lastLevelLoadSec = 0; - this.fragCurrent = this.partCurrent = null; - this.onLevelsUpdated(); - this.clearTimer(); - } - onLevelsUpdated() { - if (this.lastLoadedFragLevel > -1 && this.fragCurrent) { - this.lastLoadedFragLevel = this.fragCurrent.level; - } - this._nextAutoLevel = -1; - this.onMaxAutoLevelUpdated(); - this.codecTiers = null; - this.audioTracksByGroup = null; - } - onMaxAutoLevelUpdated() { - this.firstSelection = -1; - this.nextAutoLevelKey = ''; - } - onFragLoading(event, data) { - const frag = data.frag; - if (this.ignoreFragment(frag)) { - return; - } - if (!frag.bitrateTest) { - var _data$part; - this.fragCurrent = frag; - this.partCurrent = (_data$part = data.part) != null ? _data$part : null; - } - this.clearTimer(); - this.timer = self.setInterval(this._abandonRulesCheck, 100); - } - onLevelSwitching(event, data) { - this.clearTimer(); - } - onError(event, data) { - if (data.fatal) { - return; - } - switch (data.details) { - case ErrorDetails.BUFFER_ADD_CODEC_ERROR: - case ErrorDetails.BUFFER_APPEND_ERROR: - // Reset last loaded level so that a new selection can be made after calling recoverMediaError - this.lastLoadedFragLevel = -1; - this.firstSelection = -1; - break; - case ErrorDetails.FRAG_LOAD_TIMEOUT: - { - const frag = data.frag; - const { - fragCurrent, - partCurrent: part - } = this; - if (frag && fragCurrent && frag.sn === fragCurrent.sn && frag.level === fragCurrent.level) { - const now = performance.now(); - const stats = part ? part.stats : frag.stats; - const timeLoading = now - stats.loading.start; - const ttfb = stats.loading.first ? stats.loading.first - stats.loading.start : -1; - const loadedFirstByte = stats.loaded && ttfb > -1; - if (loadedFirstByte) { - const ttfbEstimate = this.bwEstimator.getEstimateTTFB(); - this.bwEstimator.sample(timeLoading - Math.min(ttfbEstimate, ttfb), stats.loaded); - } else { - this.bwEstimator.sampleTTFB(timeLoading); - } - } - break; - } - } - } - getTimeToLoadFrag(timeToFirstByteSec, bandwidth, fragSizeBits, isSwitch) { - const fragLoadSec = timeToFirstByteSec + fragSizeBits / bandwidth; - const playlistLoadSec = isSwitch ? this.lastLevelLoadSec : 0; - return fragLoadSec + playlistLoadSec; - } - onLevelLoaded(event, data) { - const config = this.hls.config; - const { - loading - } = data.stats; - const timeLoadingMs = loading.end - loading.start; - if (isFiniteNumber(timeLoadingMs)) { - this.lastLevelLoadSec = timeLoadingMs / 1000; - } - if (data.details.live) { - this.bwEstimator.update(config.abrEwmaSlowLive, config.abrEwmaFastLive); - } else { - this.bwEstimator.update(config.abrEwmaSlowVoD, config.abrEwmaFastVoD); - } - } - onFragLoaded(event, { - frag, - part - }) { - const stats = part ? part.stats : frag.stats; - if (frag.type === PlaylistLevelType.MAIN) { - this.bwEstimator.sampleTTFB(stats.loading.first - stats.loading.start); - } - if (this.ignoreFragment(frag)) { - return; - } - // stop monitoring bw once frag loaded - this.clearTimer(); - // reset forced auto level value so that next level will be selected - if (frag.level === this._nextAutoLevel) { - this._nextAutoLevel = -1; - } - this.firstSelection = -1; - - // compute level average bitrate - if (this.hls.config.abrMaxWithRealBitrate) { - const duration = part ? part.duration : frag.duration; - const level = this.hls.levels[frag.level]; - const loadedBytes = (level.loaded ? level.loaded.bytes : 0) + stats.loaded; - const loadedDuration = (level.loaded ? level.loaded.duration : 0) + duration; - level.loaded = { - bytes: loadedBytes, - duration: loadedDuration - }; - level.realBitrate = Math.round(8 * loadedBytes / loadedDuration); - } - if (frag.bitrateTest) { - const fragBufferedData = { - stats, - frag, - part, - id: frag.type - }; - this.onFragBuffered(Events.FRAG_BUFFERED, fragBufferedData); - frag.bitrateTest = false; - } else { - // store level id after successful fragment load for playback - this.lastLoadedFragLevel = frag.level; - } - } - onFragBuffered(event, data) { - const { - frag, - part - } = data; - const stats = part != null && part.stats.loaded ? part.stats : frag.stats; - if (stats.aborted) { - return; - } - if (this.ignoreFragment(frag)) { - return; - } - // Use the difference between parsing and request instead of buffering and request to compute fragLoadingProcessing; - // rationale is that buffer appending only happens once media is attached. This can happen when config.startFragPrefetch - // is used. If we used buffering in that case, our BW estimate sample will be very large. - const processingMs = stats.parsing.end - stats.loading.start - Math.min(stats.loading.first - stats.loading.start, this.bwEstimator.getEstimateTTFB()); - this.bwEstimator.sample(processingMs, stats.loaded); - stats.bwEstimate = this.getBwEstimate(); - if (frag.bitrateTest) { - this.bitrateTestDelay = processingMs / 1000; - } else { - this.bitrateTestDelay = 0; - } - } - ignoreFragment(frag) { - // Only count non-alt-audio frags which were actually buffered in our BW calculations - return frag.type !== PlaylistLevelType.MAIN || frag.sn === 'initSegment'; - } - clearTimer() { - if (this.timer > -1) { - self.clearInterval(this.timer); - this.timer = -1; - } - } - get firstAutoLevel() { - const { - maxAutoLevel, - minAutoLevel - } = this.hls; - const bwEstimate = this.getBwEstimate(); - const maxStartDelay = this.hls.config.maxStarvationDelay; - const abrAutoLevel = this.findBestLevel(bwEstimate, minAutoLevel, maxAutoLevel, 0, maxStartDelay, 1, 1); - if (abrAutoLevel > -1) { - return abrAutoLevel; - } - const firstLevel = this.hls.firstLevel; - const clamped = Math.min(Math.max(firstLevel, minAutoLevel), maxAutoLevel); - logger.warn(`[abr] Could not find best starting auto level. Defaulting to first in playlist ${firstLevel} clamped to ${clamped}`); - return clamped; - } - get forcedAutoLevel() { - if (this.nextAutoLevelKey) { - return -1; - } - return this._nextAutoLevel; - } - - // return next auto level - get nextAutoLevel() { - const forcedAutoLevel = this.forcedAutoLevel; - const bwEstimator = this.bwEstimator; - const useEstimate = bwEstimator.canEstimate(); - const loadedFirstFrag = this.lastLoadedFragLevel > -1; - // in case next auto level has been forced, and bw not available or not reliable, return forced value - if (forcedAutoLevel !== -1 && (!useEstimate || !loadedFirstFrag || this.nextAutoLevelKey === this.getAutoLevelKey())) { - return forcedAutoLevel; - } - - // compute next level using ABR logic - const nextABRAutoLevel = useEstimate && loadedFirstFrag ? this.getNextABRAutoLevel() : this.firstAutoLevel; - - // use forced auto level while it hasn't errored more than ABR selection - if (forcedAutoLevel !== -1) { - const levels = this.hls.levels; - if (levels.length > Math.max(forcedAutoLevel, nextABRAutoLevel) && levels[forcedAutoLevel].loadError <= levels[nextABRAutoLevel].loadError) { - return forcedAutoLevel; - } - } - - // save result until state has changed - this._nextAutoLevel = nextABRAutoLevel; - this.nextAutoLevelKey = this.getAutoLevelKey(); - return nextABRAutoLevel; - } - getAutoLevelKey() { - return `${this.getBwEstimate()}_${this.getStarvationDelay().toFixed(2)}`; - } - getNextABRAutoLevel() { - const { - fragCurrent, - partCurrent, - hls - } = this; - const { - maxAutoLevel, - config, - minAutoLevel - } = hls; - const currentFragDuration = partCurrent ? partCurrent.duration : fragCurrent ? fragCurrent.duration : 0; - const avgbw = this.getBwEstimate(); - // bufferStarvationDelay is the wall-clock time left until the playback buffer is exhausted. - const bufferStarvationDelay = this.getStarvationDelay(); - let bwFactor = config.abrBandWidthFactor; - let bwUpFactor = config.abrBandWidthUpFactor; - - // First, look to see if we can find a level matching with our avg bandwidth AND that could also guarantee no rebuffering at all - if (bufferStarvationDelay) { - const _bestLevel = this.findBestLevel(avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, 0, bwFactor, bwUpFactor); - if (_bestLevel >= 0) { - return _bestLevel; - } - } - // not possible to get rid of rebuffering... try to find level that will guarantee less than maxStarvationDelay of rebuffering - let maxStarvationDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxStarvationDelay) : config.maxStarvationDelay; - if (!bufferStarvationDelay) { - // in case buffer is empty, let's check if previous fragment was loaded to perform a bitrate test - const bitrateTestDelay = this.bitrateTestDelay; - if (bitrateTestDelay) { - // if it is the case, then we need to adjust our max starvation delay using maxLoadingDelay config value - // max video loading delay used in automatic start level selection : - // in that mode ABR controller will ensure that video loading time (ie the time to fetch the first fragment at lowest quality level + - // the time to fetch the fragment at the appropriate quality level is less than ```maxLoadingDelay``` ) - // cap maxLoadingDelay and ensure it is not bigger 'than bitrate test' frag duration - const maxLoadingDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxLoadingDelay) : config.maxLoadingDelay; - maxStarvationDelay = maxLoadingDelay - bitrateTestDelay; - logger.info(`[abr] bitrate test took ${Math.round(1000 * bitrateTestDelay)}ms, set first fragment max fetchDuration to ${Math.round(1000 * maxStarvationDelay)} ms`); - // don't use conservative factor on bitrate test - bwFactor = bwUpFactor = 1; - } - } - const bestLevel = this.findBestLevel(avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, maxStarvationDelay, bwFactor, bwUpFactor); - logger.info(`[abr] ${bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty'}, optimal quality level ${bestLevel}`); - if (bestLevel > -1) { - return bestLevel; - } - // If no matching level found, see if min auto level would be a better option - const minLevel = hls.levels[minAutoLevel]; - const autoLevel = hls.levels[hls.loadLevel]; - if ((minLevel == null ? void 0 : minLevel.bitrate) < (autoLevel == null ? void 0 : autoLevel.bitrate)) { - return minAutoLevel; - } - // or if bitrate is not lower, continue to use loadLevel - return hls.loadLevel; - } - getStarvationDelay() { - const hls = this.hls; - const media = hls.media; - if (!media) { - return Infinity; - } - // playbackRate is the absolute value of the playback rate; if media.playbackRate is 0, we use 1 to load as - // if we're playing back at the normal rate. - const playbackRate = media && media.playbackRate !== 0 ? Math.abs(media.playbackRate) : 1.0; - const bufferInfo = hls.mainForwardBufferInfo; - return (bufferInfo ? bufferInfo.len : 0) / playbackRate; - } - getBwEstimate() { - return this.bwEstimator.canEstimate() ? this.bwEstimator.getEstimate() : this.hls.config.abrEwmaDefaultEstimate; - } - findBestLevel(currentBw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, maxStarvationDelay, bwFactor, bwUpFactor) { - var _level$details; - const maxFetchDuration = bufferStarvationDelay + maxStarvationDelay; - const lastLoadedFragLevel = this.lastLoadedFragLevel; - const selectionBaseLevel = lastLoadedFragLevel === -1 ? this.hls.firstLevel : lastLoadedFragLevel; - const { - fragCurrent, - partCurrent - } = this; - const { - levels, - allAudioTracks, - loadLevel, - config - } = this.hls; - if (levels.length === 1) { - return 0; - } - const level = levels[selectionBaseLevel]; - const live = !!(level != null && (_level$details = level.details) != null && _level$details.live); - const firstSelection = loadLevel === -1 || lastLoadedFragLevel === -1; - let currentCodecSet; - let currentVideoRange = 'SDR'; - let currentFrameRate = (level == null ? void 0 : level.frameRate) || 0; - const { - audioPreference, - videoPreference - } = config; - const audioTracksByGroup = this.audioTracksByGroup || (this.audioTracksByGroup = getAudioTracksByGroup(allAudioTracks)); - if (firstSelection) { - if (this.firstSelection !== -1) { - return this.firstSelection; - } - const codecTiers = this.codecTiers || (this.codecTiers = getCodecTiers(levels, audioTracksByGroup, minAutoLevel, maxAutoLevel)); - const startTier = getStartCodecTier(codecTiers, currentVideoRange, currentBw, audioPreference, videoPreference); - const { - codecSet, - videoRanges, - minFramerate, - minBitrate, - preferHDR - } = startTier; - currentCodecSet = codecSet; - currentVideoRange = preferHDR ? videoRanges[videoRanges.length - 1] : videoRanges[0]; - currentFrameRate = minFramerate; - currentBw = Math.max(currentBw, minBitrate); - logger.log(`[abr] picked start tier ${JSON.stringify(startTier)}`); - } else { - currentCodecSet = level == null ? void 0 : level.codecSet; - currentVideoRange = level == null ? void 0 : level.videoRange; - } - const currentFragDuration = partCurrent ? partCurrent.duration : fragCurrent ? fragCurrent.duration : 0; - const ttfbEstimateSec = this.bwEstimator.getEstimateTTFB() / 1000; - const levelsSkipped = []; - for (let i = maxAutoLevel; i >= minAutoLevel; i--) { - var _levelInfo$supportedR; - const levelInfo = levels[i]; - const upSwitch = i > selectionBaseLevel; - if (!levelInfo) { - continue; - } - if (config.useMediaCapabilities && !levelInfo.supportedResult && !levelInfo.supportedPromise) { - const mediaCapabilities = navigator.mediaCapabilities; - if (typeof (mediaCapabilities == null ? void 0 : mediaCapabilities.decodingInfo) === 'function' && requiresMediaCapabilitiesDecodingInfo(levelInfo, audioTracksByGroup, currentVideoRange, currentFrameRate, currentBw, audioPreference)) { - levelInfo.supportedPromise = getMediaDecodingInfoPromise(levelInfo, audioTracksByGroup, mediaCapabilities); - levelInfo.supportedPromise.then(decodingInfo => { - if (!this.hls) { - return; - } - levelInfo.supportedResult = decodingInfo; - const levels = this.hls.levels; - const index = levels.indexOf(levelInfo); - if (decodingInfo.error) { - logger.warn(`[abr] MediaCapabilities decodingInfo error: "${decodingInfo.error}" for level ${index} ${JSON.stringify(decodingInfo)}`); - } else if (!decodingInfo.supported) { - logger.warn(`[abr] Unsupported MediaCapabilities decodingInfo result for level ${index} ${JSON.stringify(decodingInfo)}`); - if (index > -1 && levels.length > 1) { - logger.log(`[abr] Removing unsupported level ${index}`); - this.hls.removeLevel(index); - } - } - }); - } else { - levelInfo.supportedResult = SUPPORTED_INFO_DEFAULT; - } - } - - // skip candidates which change codec-family or video-range, - // and which decrease or increase frame-rate for up and down-switch respectfully - if (currentCodecSet && levelInfo.codecSet !== currentCodecSet || currentVideoRange && levelInfo.videoRange !== currentVideoRange || upSwitch && currentFrameRate > levelInfo.frameRate || !upSwitch && currentFrameRate > 0 && currentFrameRate < levelInfo.frameRate || levelInfo.supportedResult && !((_levelInfo$supportedR = levelInfo.supportedResult.decodingInfoResults) != null && _levelInfo$supportedR[0].smooth)) { - levelsSkipped.push(i); - continue; - } - const levelDetails = levelInfo.details; - const avgDuration = (partCurrent ? levelDetails == null ? void 0 : levelDetails.partTarget : levelDetails == null ? void 0 : levelDetails.averagetargetduration) || currentFragDuration; - let adjustedbw; - // follow algorithm captured from stagefright : - // https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/httplive/LiveSession.cpp - // Pick the highest bandwidth stream below or equal to estimated bandwidth. - // consider only 80% of the available bandwidth, but if we are switching up, - // be even more conservative (70%) to avoid overestimating and immediately - // switching back. - if (!upSwitch) { - adjustedbw = bwFactor * currentBw; - } else { - adjustedbw = bwUpFactor * currentBw; - } - - // Use average bitrate when starvation delay (buffer length) is gt or eq two segment durations and rebuffering is not expected (maxStarvationDelay > 0) - const bitrate = currentFragDuration && bufferStarvationDelay >= currentFragDuration * 2 && maxStarvationDelay === 0 ? levels[i].averageBitrate : levels[i].maxBitrate; - const fetchDuration = this.getTimeToLoadFrag(ttfbEstimateSec, adjustedbw, bitrate * avgDuration, levelDetails === undefined); - const canSwitchWithinTolerance = - // if adjusted bw is greater than level bitrate AND - adjustedbw >= bitrate && ( - // no level change, or new level has no error history - i === lastLoadedFragLevel || levelInfo.loadError === 0 && levelInfo.fragmentError === 0) && ( - // fragment fetchDuration unknown OR live stream OR fragment fetchDuration less than max allowed fetch duration, then this level matches - // we don't account for max Fetch Duration for live streams, this is to avoid switching down when near the edge of live sliding window ... - // special case to support startLevel = -1 (bitrateTest) on live streams : in that case we should not exit loop so that findBestLevel will return -1 - fetchDuration <= ttfbEstimateSec || !isFiniteNumber(fetchDuration) || live && !this.bitrateTestDelay || fetchDuration < maxFetchDuration); - if (canSwitchWithinTolerance) { - const forcedAutoLevel = this.forcedAutoLevel; - if (i !== loadLevel && (forcedAutoLevel === -1 || forcedAutoLevel !== loadLevel)) { - if (levelsSkipped.length) { - logger.trace(`[abr] Skipped level(s) ${levelsSkipped.join(',')} of ${maxAutoLevel} max with CODECS and VIDEO-RANGE:"${levels[levelsSkipped[0]].codecs}" ${levels[levelsSkipped[0]].videoRange}; not compatible with "${level.codecs}" ${currentVideoRange}`); - } - logger.info(`[abr] switch candidate:${selectionBaseLevel}->${i} adjustedbw(${Math.round(adjustedbw)})-bitrate=${Math.round(adjustedbw - bitrate)} ttfb:${ttfbEstimateSec.toFixed(1)} avgDuration:${avgDuration.toFixed(1)} maxFetchDuration:${maxFetchDuration.toFixed(1)} fetchDuration:${fetchDuration.toFixed(1)} firstSelection:${firstSelection} codecSet:${currentCodecSet} videoRange:${currentVideoRange} hls.loadLevel:${loadLevel}`); - } - if (firstSelection) { - this.firstSelection = i; - } - // as we are looping from highest to lowest, this will return the best achievable quality level - return i; - } - } - // not enough time budget even with quality level 0 ... rebuffering might happen - return -1; - } - set nextAutoLevel(nextLevel) { - const { - maxAutoLevel, - minAutoLevel - } = this.hls; - const value = Math.min(Math.max(nextLevel, minAutoLevel), maxAutoLevel); - if (this._nextAutoLevel !== value) { - this.nextAutoLevelKey = ''; - this._nextAutoLevel = value; - } - } -} - -/** - * @ignore - * Sub-class specialization of EventHandler base class. - * - * TaskLoop allows to schedule a task function being called (optionnaly repeatedly) on the main loop, - * scheduled asynchroneously, avoiding recursive calls in the same tick. - * - * The task itself is implemented in `doTick`. It can be requested and called for single execution - * using the `tick` method. - * - * It will be assured that the task execution method (`tick`) only gets called once per main loop "tick", - * no matter how often it gets requested for execution. Execution in further ticks will be scheduled accordingly. - * - * If further execution requests have already been scheduled on the next tick, it can be checked with `hasNextTick`, - * and cancelled with `clearNextTick`. - * - * The task can be scheduled as an interval repeatedly with a period as parameter (see `setInterval`, `clearInterval`). - * - * Sub-classes need to implement the `doTick` method which will effectively have the task execution routine. - * - * Further explanations: - * - * The baseclass has a `tick` method that will schedule the doTick call. It may be called synchroneously - * only for a stack-depth of one. On re-entrant calls, sub-sequent calls are scheduled for next main loop ticks. - * - * When the task execution (`tick` method) is called in re-entrant way this is detected and - * we are limiting the task execution per call stack to exactly one, but scheduling/post-poning further - * task processing on the next main loop iteration (also known as "next tick" in the Node/JS runtime lingo). - */ -class TaskLoop { - constructor() { - this._boundTick = void 0; - this._tickTimer = null; - this._tickInterval = null; - this._tickCallCount = 0; - this._boundTick = this.tick.bind(this); - } - destroy() { - this.onHandlerDestroying(); - this.onHandlerDestroyed(); - } - onHandlerDestroying() { - // clear all timers before unregistering from event bus - this.clearNextTick(); - this.clearInterval(); - } - onHandlerDestroyed() {} - hasInterval() { - return !!this._tickInterval; - } - hasNextTick() { - return !!this._tickTimer; - } - - /** - * @param millis - Interval time (ms) - * @eturns True when interval has been scheduled, false when already scheduled (no effect) - */ - setInterval(millis) { - if (!this._tickInterval) { - this._tickCallCount = 0; - this._tickInterval = self.setInterval(this._boundTick, millis); - return true; - } - return false; - } - - /** - * @returns True when interval was cleared, false when none was set (no effect) - */ - clearInterval() { - if (this._tickInterval) { - self.clearInterval(this._tickInterval); - this._tickInterval = null; - return true; - } - return false; - } - - /** - * @returns True when timeout was cleared, false when none was set (no effect) - */ - clearNextTick() { - if (this._tickTimer) { - self.clearTimeout(this._tickTimer); - this._tickTimer = null; - return true; - } - return false; - } - - /** - * Will call the subclass doTick implementation in this main loop tick - * or in the next one (via setTimeout(,0)) in case it has already been called - * in this tick (in case this is a re-entrant call). - */ - tick() { - this._tickCallCount++; - if (this._tickCallCount === 1) { - this.doTick(); - // re-entrant call to tick from previous doTick call stack - // -> schedule a call on the next main loop iteration to process this task processing request - if (this._tickCallCount > 1) { - // make sure only one timer exists at any time at max - this.tickImmediate(); - } - this._tickCallCount = 0; - } - } - tickImmediate() { - this.clearNextTick(); - this._tickTimer = self.setTimeout(this._boundTick, 0); - } - - /** - * For subclass to implement task logic - * @abstract - */ - doTick() {} -} - -var FragmentState = { - NOT_LOADED: "NOT_LOADED", - APPENDING: "APPENDING", - PARTIAL: "PARTIAL", - OK: "OK" -}; -class FragmentTracker { - constructor(hls) { - this.activePartLists = Object.create(null); - this.endListFragments = Object.create(null); - this.fragments = Object.create(null); - this.timeRanges = Object.create(null); - this.bufferPadding = 0.2; - this.hls = void 0; - this.hasGaps = false; - this.hls = hls; - this._registerListeners(); - } - _registerListeners() { - const { - hls - } = this; - hls.on(Events.BUFFER_APPENDED, this.onBufferAppended, this); - hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this); - hls.on(Events.FRAG_LOADED, this.onFragLoaded, this); - } - _unregisterListeners() { - const { - hls - } = this; - hls.off(Events.BUFFER_APPENDED, this.onBufferAppended, this); - hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this); - hls.off(Events.FRAG_LOADED, this.onFragLoaded, this); - } - destroy() { - this._unregisterListeners(); - // @ts-ignore - this.fragments = - // @ts-ignore - this.activePartLists = - // @ts-ignore - this.endListFragments = this.timeRanges = null; - } - - /** - * Return a Fragment or Part with an appended range that matches the position and levelType - * Otherwise, return null - */ - getAppendedFrag(position, levelType) { - const activeParts = this.activePartLists[levelType]; - if (activeParts) { - for (let i = activeParts.length; i--;) { - const activePart = activeParts[i]; - if (!activePart) { - break; - } - const appendedPTS = activePart.end; - if (activePart.start <= position && appendedPTS !== null && position <= appendedPTS) { - return activePart; - } - } - } - return this.getBufferedFrag(position, levelType); - } - - /** - * Return a buffered Fragment that matches the position and levelType. - * A buffered Fragment is one whose loading, parsing and appending is done (completed or "partial" meaning aborted). - * If not found any Fragment, return null - */ - getBufferedFrag(position, levelType) { - const { - fragments - } = this; - const keys = Object.keys(fragments); - for (let i = keys.length; i--;) { - const fragmentEntity = fragments[keys[i]]; - if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === levelType && fragmentEntity.buffered) { - const frag = fragmentEntity.body; - if (frag.start <= position && position <= frag.end) { - return frag; - } - } - } - return null; - } - - /** - * Partial fragments effected by coded frame eviction will be removed - * The browser will unload parts of the buffer to free up memory for new buffer data - * Fragments will need to be reloaded when the buffer is freed up, removing partial fragments will allow them to reload(since there might be parts that are still playable) - */ - detectEvictedFragments(elementaryStream, timeRange, playlistType, appendedPart) { - if (this.timeRanges) { - this.timeRanges[elementaryStream] = timeRange; - } - // Check if any flagged fragments have been unloaded - // excluding anything newer than appendedPartSn - const appendedPartSn = (appendedPart == null ? void 0 : appendedPart.fragment.sn) || -1; - Object.keys(this.fragments).forEach(key => { - const fragmentEntity = this.fragments[key]; - if (!fragmentEntity) { - return; - } - if (appendedPartSn >= fragmentEntity.body.sn) { - return; - } - if (!fragmentEntity.buffered && !fragmentEntity.loaded) { - if (fragmentEntity.body.type === playlistType) { - this.removeFragment(fragmentEntity.body); - } - return; - } - const esData = fragmentEntity.range[elementaryStream]; - if (!esData) { - return; - } - esData.time.some(time => { - const isNotBuffered = !this.isTimeBuffered(time.startPTS, time.endPTS, timeRange); - if (isNotBuffered) { - // Unregister partial fragment as it needs to load again to be reused - this.removeFragment(fragmentEntity.body); - } - return isNotBuffered; - }); - }); - } - - /** - * Checks if the fragment passed in is loaded in the buffer properly - * Partially loaded fragments will be registered as a partial fragment - */ - detectPartialFragments(data) { - const timeRanges = this.timeRanges; - const { - frag, - part - } = data; - if (!timeRanges || frag.sn === 'initSegment') { - return; - } - const fragKey = getFragmentKey(frag); - const fragmentEntity = this.fragments[fragKey]; - if (!fragmentEntity || fragmentEntity.buffered && frag.gap) { - return; - } - const isFragHint = !frag.relurl; - Object.keys(timeRanges).forEach(elementaryStream => { - const streamInfo = frag.elementaryStreams[elementaryStream]; - if (!streamInfo) { - return; - } - const timeRange = timeRanges[elementaryStream]; - const partial = isFragHint || streamInfo.partial === true; - fragmentEntity.range[elementaryStream] = this.getBufferedTimes(frag, part, partial, timeRange); - }); - fragmentEntity.loaded = null; - if (Object.keys(fragmentEntity.range).length) { - fragmentEntity.buffered = true; - const endList = fragmentEntity.body.endList = frag.endList || fragmentEntity.body.endList; - if (endList) { - this.endListFragments[fragmentEntity.body.type] = fragmentEntity; - } - if (!isPartial(fragmentEntity)) { - // Remove older fragment parts from lookup after frag is tracked as buffered - this.removeParts(frag.sn - 1, frag.type); - } - } else { - // remove fragment if nothing was appended - this.removeFragment(fragmentEntity.body); - } - } - removeParts(snToKeep, levelType) { - const activeParts = this.activePartLists[levelType]; - if (!activeParts) { - return; - } - this.activePartLists[levelType] = activeParts.filter(part => part.fragment.sn >= snToKeep); - } - fragBuffered(frag, force) { - const fragKey = getFragmentKey(frag); - let fragmentEntity = this.fragments[fragKey]; - if (!fragmentEntity && force) { - fragmentEntity = this.fragments[fragKey] = { - body: frag, - appendedPTS: null, - loaded: null, - buffered: false, - range: Object.create(null) - }; - if (frag.gap) { - this.hasGaps = true; - } - } - if (fragmentEntity) { - fragmentEntity.loaded = null; - fragmentEntity.buffered = true; - } - } - getBufferedTimes(fragment, part, partial, timeRange) { - const buffered = { - time: [], - partial - }; - const startPTS = fragment.start; - const endPTS = fragment.end; - const minEndPTS = fragment.minEndPTS || endPTS; - const maxStartPTS = fragment.maxStartPTS || startPTS; - for (let i = 0; i < timeRange.length; i++) { - const startTime = timeRange.start(i) - this.bufferPadding; - const endTime = timeRange.end(i) + this.bufferPadding; - if (maxStartPTS >= startTime && minEndPTS <= endTime) { - // Fragment is entirely contained in buffer - // No need to check the other timeRange times since it's completely playable - buffered.time.push({ - startPTS: Math.max(startPTS, timeRange.start(i)), - endPTS: Math.min(endPTS, timeRange.end(i)) - }); - break; - } else if (startPTS < endTime && endPTS > startTime) { - const start = Math.max(startPTS, timeRange.start(i)); - const end = Math.min(endPTS, timeRange.end(i)); - if (end > start) { - buffered.partial = true; - // Check for intersection with buffer - // Get playable sections of the fragment - buffered.time.push({ - startPTS: start, - endPTS: end - }); - } - } else if (endPTS <= startTime) { - // No need to check the rest of the timeRange as it is in order - break; - } - } - return buffered; - } - - /** - * Gets the partial fragment for a certain time - */ - getPartialFragment(time) { - let bestFragment = null; - let timePadding; - let startTime; - let endTime; - let bestOverlap = 0; - const { - bufferPadding, - fragments - } = this; - Object.keys(fragments).forEach(key => { - const fragmentEntity = fragments[key]; - if (!fragmentEntity) { - return; - } - if (isPartial(fragmentEntity)) { - startTime = fragmentEntity.body.start - bufferPadding; - endTime = fragmentEntity.body.end + bufferPadding; - if (time >= startTime && time <= endTime) { - // Use the fragment that has the most padding from start and end time - timePadding = Math.min(time - startTime, endTime - time); - if (bestOverlap <= timePadding) { - bestFragment = fragmentEntity.body; - bestOverlap = timePadding; - } - } - } - }); - return bestFragment; - } - isEndListAppended(type) { - const lastFragmentEntity = this.endListFragments[type]; - return lastFragmentEntity !== undefined && (lastFragmentEntity.buffered || isPartial(lastFragmentEntity)); - } - getState(fragment) { - const fragKey = getFragmentKey(fragment); - const fragmentEntity = this.fragments[fragKey]; - if (fragmentEntity) { - if (!fragmentEntity.buffered) { - return FragmentState.APPENDING; - } else if (isPartial(fragmentEntity)) { - return FragmentState.PARTIAL; - } else { - return FragmentState.OK; - } - } - return FragmentState.NOT_LOADED; - } - isTimeBuffered(startPTS, endPTS, timeRange) { - let startTime; - let endTime; - for (let i = 0; i < timeRange.length; i++) { - startTime = timeRange.start(i) - this.bufferPadding; - endTime = timeRange.end(i) + this.bufferPadding; - if (startPTS >= startTime && endPTS <= endTime) { - return true; - } - if (endPTS <= startTime) { - // No need to check the rest of the timeRange as it is in order - return false; - } - } - return false; - } - onFragLoaded(event, data) { - const { - frag, - part - } = data; - // don't track initsegment (for which sn is not a number) - // don't track frags used for bitrateTest, they're irrelevant. - if (frag.sn === 'initSegment' || frag.bitrateTest) { - return; - } - - // Fragment entity `loaded` FragLoadedData is null when loading parts - const loaded = part ? null : data; - const fragKey = getFragmentKey(frag); - this.fragments[fragKey] = { - body: frag, - appendedPTS: null, - loaded, - buffered: false, - range: Object.create(null) - }; - } - onBufferAppended(event, data) { - const { - frag, - part, - timeRanges - } = data; - if (frag.sn === 'initSegment') { - return; - } - const playlistType = frag.type; - if (part) { - let activeParts = this.activePartLists[playlistType]; - if (!activeParts) { - this.activePartLists[playlistType] = activeParts = []; - } - activeParts.push(part); - } - // Store the latest timeRanges loaded in the buffer - this.timeRanges = timeRanges; - Object.keys(timeRanges).forEach(elementaryStream => { - const timeRange = timeRanges[elementaryStream]; - this.detectEvictedFragments(elementaryStream, timeRange, playlistType, part); - }); - } - onFragBuffered(event, data) { - this.detectPartialFragments(data); - } - hasFragment(fragment) { - const fragKey = getFragmentKey(fragment); - return !!this.fragments[fragKey]; - } - hasParts(type) { - var _this$activePartLists; - return !!((_this$activePartLists = this.activePartLists[type]) != null && _this$activePartLists.length); - } - removeFragmentsInRange(start, end, playlistType, withGapOnly, unbufferedOnly) { - if (withGapOnly && !this.hasGaps) { - return; - } - Object.keys(this.fragments).forEach(key => { - const fragmentEntity = this.fragments[key]; - if (!fragmentEntity) { - return; - } - const frag = fragmentEntity.body; - if (frag.type !== playlistType || withGapOnly && !frag.gap) { - return; - } - if (frag.start < end && frag.end > start && (fragmentEntity.buffered || unbufferedOnly)) { - this.removeFragment(frag); - } - }); - } - removeFragment(fragment) { - const fragKey = getFragmentKey(fragment); - fragment.stats.loaded = 0; - fragment.clearElementaryStreamInfo(); - const activeParts = this.activePartLists[fragment.type]; - if (activeParts) { - const snToRemove = fragment.sn; - this.activePartLists[fragment.type] = activeParts.filter(part => part.fragment.sn !== snToRemove); - } - delete this.fragments[fragKey]; - if (fragment.endList) { - delete this.endListFragments[fragment.type]; - } - } - removeAllFragments() { - this.fragments = Object.create(null); - this.endListFragments = Object.create(null); - this.activePartLists = Object.create(null); - this.hasGaps = false; - } -} -function isPartial(fragmentEntity) { - var _fragmentEntity$range, _fragmentEntity$range2, _fragmentEntity$range3; - return fragmentEntity.buffered && (fragmentEntity.body.gap || ((_fragmentEntity$range = fragmentEntity.range.video) == null ? void 0 : _fragmentEntity$range.partial) || ((_fragmentEntity$range2 = fragmentEntity.range.audio) == null ? void 0 : _fragmentEntity$range2.partial) || ((_fragmentEntity$range3 = fragmentEntity.range.audiovideo) == null ? void 0 : _fragmentEntity$range3.partial)); -} -function getFragmentKey(fragment) { - return `${fragment.type}_${fragment.level}_${fragment.sn}`; -} - -/** - * Provides methods dealing with buffer length retrieval for example. - * - * In general, a helper around HTML5 MediaElement TimeRanges gathered from `buffered` property. - * - * Also @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/buffered - */ - -const noopBuffered = { - length: 0, - start: () => 0, - end: () => 0 -}; -class BufferHelper { - /** - * Return true if `media`'s buffered include `position` - */ - static isBuffered(media, position) { - try { - if (media) { - const buffered = BufferHelper.getBuffered(media); - for (let i = 0; i < buffered.length; i++) { - if (position >= buffered.start(i) && position <= buffered.end(i)) { - return true; - } - } - } - } catch (error) { - // this is to catch - // InvalidStateError: Failed to read the 'buffered' property from 'SourceBuffer': - // This SourceBuffer has been removed from the parent media source - } - return false; - } - static bufferInfo(media, pos, maxHoleDuration) { - try { - if (media) { - const vbuffered = BufferHelper.getBuffered(media); - const buffered = []; - let i; - for (i = 0; i < vbuffered.length; i++) { - buffered.push({ - start: vbuffered.start(i), - end: vbuffered.end(i) - }); - } - return this.bufferedInfo(buffered, pos, maxHoleDuration); - } - } catch (error) { - // this is to catch - // InvalidStateError: Failed to read the 'buffered' property from 'SourceBuffer': - // This SourceBuffer has been removed from the parent media source - } - return { - len: 0, - start: pos, - end: pos, - nextStart: undefined - }; - } - static bufferedInfo(buffered, pos, maxHoleDuration) { - pos = Math.max(0, pos); - // sort on buffer.start/smaller end (IE does not always return sorted buffered range) - buffered.sort(function (a, b) { - const diff = a.start - b.start; - if (diff) { - return diff; - } else { - return b.end - a.end; - } - }); - let buffered2 = []; - if (maxHoleDuration) { - // there might be some small holes between buffer time range - // consider that holes smaller than maxHoleDuration are irrelevant and build another - // buffer time range representations that discards those holes - for (let i = 0; i < buffered.length; i++) { - const buf2len = buffered2.length; - if (buf2len) { - const buf2end = buffered2[buf2len - 1].end; - // if small hole (value between 0 or maxHoleDuration ) or overlapping (negative) - if (buffered[i].start - buf2end < maxHoleDuration) { - // merge overlapping time ranges - // update lastRange.end only if smaller than item.end - // e.g. [ 1, 15] with [ 2,8] => [ 1,15] (no need to modify lastRange.end) - // whereas [ 1, 8] with [ 2,15] => [ 1,15] ( lastRange should switch from [1,8] to [1,15]) - if (buffered[i].end > buf2end) { - buffered2[buf2len - 1].end = buffered[i].end; - } - } else { - // big hole - buffered2.push(buffered[i]); - } - } else { - // first value - buffered2.push(buffered[i]); - } - } - } else { - buffered2 = buffered; - } - let bufferLen = 0; - - // bufferStartNext can possibly be undefined based on the conditional logic below - let bufferStartNext; - - // bufferStart and bufferEnd are buffer boundaries around current video position - let bufferStart = pos; - let bufferEnd = pos; - for (let i = 0; i < buffered2.length; i++) { - const start = buffered2[i].start; - const end = buffered2[i].end; - // logger.log('buf start/end:' + buffered.start(i) + '/' + buffered.end(i)); - if (pos + maxHoleDuration >= start && pos < end) { - // play position is inside this buffer TimeRange, retrieve end of buffer position and buffer length - bufferStart = start; - bufferEnd = end; - bufferLen = bufferEnd - pos; - } else if (pos + maxHoleDuration < start) { - bufferStartNext = start; - break; - } - } - return { - len: bufferLen, - start: bufferStart || 0, - end: bufferEnd || 0, - nextStart: bufferStartNext - }; - } - - /** - * Safe method to get buffered property. - * SourceBuffer.buffered may throw if SourceBuffer is removed from it's MediaSource - */ - static getBuffered(media) { - try { - return media.buffered; - } catch (e) { - logger.log('failed to get media.buffered', e); - return noopBuffered; - } - } -} - -class ChunkMetadata { - constructor(level, sn, id, size = 0, part = -1, partial = false) { - this.level = void 0; - this.sn = void 0; - this.part = void 0; - this.id = void 0; - this.size = void 0; - this.partial = void 0; - this.transmuxing = getNewPerformanceTiming(); - this.buffering = { - audio: getNewPerformanceTiming(), - video: getNewPerformanceTiming(), - audiovideo: getNewPerformanceTiming() - }; - this.level = level; - this.sn = sn; - this.id = id; - this.size = size; - this.part = part; - this.partial = partial; - } -} -function getNewPerformanceTiming() { - return { - start: 0, - executeStart: 0, - executeEnd: 0, - end: 0 - }; -} - -function findFirstFragWithCC(fragments, cc) { - for (let i = 0, len = fragments.length; i < len; i++) { - var _fragments$i; - if (((_fragments$i = fragments[i]) == null ? void 0 : _fragments$i.cc) === cc) { - return fragments[i]; - } - } - return null; -} -function shouldAlignOnDiscontinuities(lastFrag, switchDetails, details) { - if (switchDetails) { - if (details.endCC > details.startCC || lastFrag && lastFrag.cc < details.startCC) { - return true; - } - } - return false; -} - -// Find the first frag in the previous level which matches the CC of the first frag of the new level -function findDiscontinuousReferenceFrag(prevDetails, curDetails) { - const prevFrags = prevDetails.fragments; - const curFrags = curDetails.fragments; - if (!curFrags.length || !prevFrags.length) { - logger.log('No fragments to align'); - return; - } - const prevStartFrag = findFirstFragWithCC(prevFrags, curFrags[0].cc); - if (!prevStartFrag || prevStartFrag && !prevStartFrag.startPTS) { - logger.log('No frag in previous level to align on'); - return; - } - return prevStartFrag; -} -function adjustFragmentStart(frag, sliding) { - if (frag) { - const start = frag.start + sliding; - frag.start = frag.startPTS = start; - frag.endPTS = start + frag.duration; - } -} -function adjustSlidingStart(sliding, details) { - // Update segments - const fragments = details.fragments; - for (let i = 0, len = fragments.length; i < len; i++) { - adjustFragmentStart(fragments[i], sliding); - } - // Update LL-HLS parts at the end of the playlist - if (details.fragmentHint) { - adjustFragmentStart(details.fragmentHint, sliding); - } - details.alignedSliding = true; -} - -/** - * Using the parameters of the last level, this function computes PTS' of the new fragments so that they form a - * contiguous stream with the last fragments. - * The PTS of a fragment lets Hls.js know where it fits into a stream - by knowing every PTS, we know which fragment to - * download at any given time. PTS is normally computed when the fragment is demuxed, so taking this step saves us time - * and an extra download. - * @param lastFrag - * @param lastLevel - * @param details - */ -function alignStream(lastFrag, switchDetails, details) { - if (!switchDetails) { - return; - } - alignDiscontinuities(lastFrag, details, switchDetails); - if (!details.alignedSliding && switchDetails) { - // If the PTS wasn't figured out via discontinuity sequence that means there was no CC increase within the level. - // Aligning via Program Date Time should therefore be reliable, since PDT should be the same within the same - // discontinuity sequence. - alignMediaPlaylistByPDT(details, switchDetails); - } - if (!details.alignedSliding && switchDetails && !details.skippedSegments) { - // Try to align on sn so that we pick a better start fragment. - // Do not perform this on playlists with delta updates as this is only to align levels on switch - // and adjustSliding only adjusts fragments after skippedSegments. - adjustSliding(switchDetails, details); - } -} - -/** - * Computes the PTS if a new level's fragments using the PTS of a fragment in the last level which shares the same - * discontinuity sequence. - * @param lastFrag - The last Fragment which shares the same discontinuity sequence - * @param lastLevel - The details of the last loaded level - * @param details - The details of the new level - */ -function alignDiscontinuities(lastFrag, details, switchDetails) { - if (shouldAlignOnDiscontinuities(lastFrag, switchDetails, details)) { - const referenceFrag = findDiscontinuousReferenceFrag(switchDetails, details); - if (referenceFrag && isFiniteNumber(referenceFrag.start)) { - logger.log(`Adjusting PTS using last level due to CC increase within current level ${details.url}`); - adjustSlidingStart(referenceFrag.start, details); - } - } -} - -/** - * Ensures appropriate time-alignment between renditions based on PDT. - * This function assumes the timelines represented in `refDetails` are accurate, including the PDTs - * for the last discontinuity sequence number shared by both playlists when present, - * and uses the "wallclock"/PDT timeline as a cross-reference to `details`, adjusting the presentation - * times/timelines of `details` accordingly. - * Given the asynchronous nature of fetches and initial loads of live `main` and audio/subtitle tracks, - * the primary purpose of this function is to ensure the "local timelines" of audio/subtitle tracks - * are aligned to the main/video timeline, using PDT as the cross-reference/"anchor" that should - * be consistent across playlists, per the HLS spec. - * @param details - The details of the rendition you'd like to time-align (e.g. an audio rendition). - * @param refDetails - The details of the reference rendition with start and PDT times for alignment. - */ -function alignMediaPlaylistByPDT(details, refDetails) { - if (!details.hasProgramDateTime || !refDetails.hasProgramDateTime) { - return; - } - const fragments = details.fragments; - const refFragments = refDetails.fragments; - if (!fragments.length || !refFragments.length) { - return; - } - - // Calculate a delta to apply to all fragments according to the delta in PDT times and start times - // of a fragment in the reference details, and a fragment in the target details of the same discontinuity. - // If a fragment of the same discontinuity was not found use the middle fragment of both. - let refFrag; - let frag; - const targetCC = Math.min(refDetails.endCC, details.endCC); - if (refDetails.startCC < targetCC && details.startCC < targetCC) { - refFrag = findFirstFragWithCC(refFragments, targetCC); - frag = findFirstFragWithCC(fragments, targetCC); - } - if (!refFrag || !frag) { - refFrag = refFragments[Math.floor(refFragments.length / 2)]; - frag = findFirstFragWithCC(fragments, refFrag.cc) || fragments[Math.floor(fragments.length / 2)]; - } - const refPDT = refFrag.programDateTime; - const targetPDT = frag.programDateTime; - if (!refPDT || !targetPDT) { - return; - } - const delta = (targetPDT - refPDT) / 1000 - (frag.start - refFrag.start); - adjustSlidingStart(delta, details); -} - -const MIN_CHUNK_SIZE = Math.pow(2, 17); // 128kb - -class FragmentLoader { - constructor(config) { - this.config = void 0; - this.loader = null; - this.partLoadTimeout = -1; - this.config = config; - } - destroy() { - if (this.loader) { - this.loader.destroy(); - this.loader = null; - } - } - abort() { - if (this.loader) { - // Abort the loader for current fragment. Only one may load at any given time - this.loader.abort(); - } - } - load(frag, onProgress) { - const url = frag.url; - if (!url) { - return Promise.reject(new LoadError({ - type: ErrorTypes.NETWORK_ERROR, - details: ErrorDetails.FRAG_LOAD_ERROR, - fatal: false, - frag, - error: new Error(`Fragment does not have a ${url ? 'part list' : 'url'}`), - networkDetails: null - })); - } - this.abort(); - const config = this.config; - const FragmentILoader = config.fLoader; - const DefaultILoader = config.loader; - return new Promise((resolve, reject) => { - if (this.loader) { - this.loader.destroy(); - } - if (frag.gap) { - if (frag.tagList.some(tags => tags[0] === 'GAP')) { - reject(createGapLoadError(frag)); - return; - } else { - // Reset temporary treatment as GAP tag - frag.gap = false; - } - } - const loader = this.loader = frag.loader = FragmentILoader ? new FragmentILoader(config) : new DefaultILoader(config); - const loaderContext = createLoaderContext(frag); - const loadPolicy = getLoaderConfigWithoutReties(config.fragLoadPolicy.default); - const loaderConfig = { - loadPolicy, - timeout: loadPolicy.maxLoadTimeMs, - maxRetry: 0, - retryDelay: 0, - maxRetryDelay: 0, - highWaterMark: frag.sn === 'initSegment' ? Infinity : MIN_CHUNK_SIZE - }; - // Assign frag stats to the loader's stats reference - frag.stats = loader.stats; - loader.load(loaderContext, loaderConfig, { - onSuccess: (response, stats, context, networkDetails) => { - this.resetLoader(frag, loader); - let payload = response.data; - if (context.resetIV && frag.decryptdata) { - frag.decryptdata.iv = new Uint8Array(payload.slice(0, 16)); - payload = payload.slice(16); - } - resolve({ - frag, - part: null, - payload, - networkDetails - }); - }, - onError: (response, context, networkDetails, stats) => { - this.resetLoader(frag, loader); - reject(new LoadError({ - type: ErrorTypes.NETWORK_ERROR, - details: ErrorDetails.FRAG_LOAD_ERROR, - fatal: false, - frag, - response: _objectSpread2({ - url, - data: undefined - }, response), - error: new Error(`HTTP Error ${response.code} ${response.text}`), - networkDetails, - stats - })); - }, - onAbort: (stats, context, networkDetails) => { - this.resetLoader(frag, loader); - reject(new LoadError({ - type: ErrorTypes.NETWORK_ERROR, - details: ErrorDetails.INTERNAL_ABORTED, - fatal: false, - frag, - error: new Error('Aborted'), - networkDetails, - stats - })); - }, - onTimeout: (stats, context, networkDetails) => { - this.resetLoader(frag, loader); - reject(new LoadError({ - type: ErrorTypes.NETWORK_ERROR, - details: ErrorDetails.FRAG_LOAD_TIMEOUT, - fatal: false, - frag, - error: new Error(`Timeout after ${loaderConfig.timeout}ms`), - networkDetails, - stats - })); - }, - onProgress: (stats, context, data, networkDetails) => { - if (onProgress) { - onProgress({ - frag, - part: null, - payload: data, - networkDetails - }); - } - } - }); - }); - } - loadPart(frag, part, onProgress) { - this.abort(); - const config = this.config; - const FragmentILoader = config.fLoader; - const DefaultILoader = config.loader; - return new Promise((resolve, reject) => { - if (this.loader) { - this.loader.destroy(); - } - if (frag.gap || part.gap) { - reject(createGapLoadError(frag, part)); - return; - } - const loader = this.loader = frag.loader = FragmentILoader ? new FragmentILoader(config) : new DefaultILoader(config); - const loaderContext = createLoaderContext(frag, part); - // Should we define another load policy for parts? - const loadPolicy = getLoaderConfigWithoutReties(config.fragLoadPolicy.default); - const loaderConfig = { - loadPolicy, - timeout: loadPolicy.maxLoadTimeMs, - maxRetry: 0, - retryDelay: 0, - maxRetryDelay: 0, - highWaterMark: MIN_CHUNK_SIZE - }; - // Assign part stats to the loader's stats reference - part.stats = loader.stats; - loader.load(loaderContext, loaderConfig, { - onSuccess: (response, stats, context, networkDetails) => { - this.resetLoader(frag, loader); - this.updateStatsFromPart(frag, part); - const partLoadedData = { - frag, - part, - payload: response.data, - networkDetails - }; - onProgress(partLoadedData); - resolve(partLoadedData); - }, - onError: (response, context, networkDetails, stats) => { - this.resetLoader(frag, loader); - reject(new LoadError({ - type: ErrorTypes.NETWORK_ERROR, - details: ErrorDetails.FRAG_LOAD_ERROR, - fatal: false, - frag, - part, - response: _objectSpread2({ - url: loaderContext.url, - data: undefined - }, response), - error: new Error(`HTTP Error ${response.code} ${response.text}`), - networkDetails, - stats - })); - }, - onAbort: (stats, context, networkDetails) => { - frag.stats.aborted = part.stats.aborted; - this.resetLoader(frag, loader); - reject(new LoadError({ - type: ErrorTypes.NETWORK_ERROR, - details: ErrorDetails.INTERNAL_ABORTED, - fatal: false, - frag, - part, - error: new Error('Aborted'), - networkDetails, - stats - })); - }, - onTimeout: (stats, context, networkDetails) => { - this.resetLoader(frag, loader); - reject(new LoadError({ - type: ErrorTypes.NETWORK_ERROR, - details: ErrorDetails.FRAG_LOAD_TIMEOUT, - fatal: false, - frag, - part, - error: new Error(`Timeout after ${loaderConfig.timeout}ms`), - networkDetails, - stats - })); - } - }); - }); - } - updateStatsFromPart(frag, part) { - const fragStats = frag.stats; - const partStats = part.stats; - const partTotal = partStats.total; - fragStats.loaded += partStats.loaded; - if (partTotal) { - const estTotalParts = Math.round(frag.duration / part.duration); - const estLoadedParts = Math.min(Math.round(fragStats.loaded / partTotal), estTotalParts); - const estRemainingParts = estTotalParts - estLoadedParts; - const estRemainingBytes = estRemainingParts * Math.round(fragStats.loaded / estLoadedParts); - fragStats.total = fragStats.loaded + estRemainingBytes; - } else { - fragStats.total = Math.max(fragStats.loaded, fragStats.total); - } - const fragLoading = fragStats.loading; - const partLoading = partStats.loading; - if (fragLoading.start) { - // add to fragment loader latency - fragLoading.first += partLoading.first - partLoading.start; - } else { - fragLoading.start = partLoading.start; - fragLoading.first = partLoading.first; - } - fragLoading.end = partLoading.end; - } - resetLoader(frag, loader) { - frag.loader = null; - if (this.loader === loader) { - self.clearTimeout(this.partLoadTimeout); - this.loader = null; - } - loader.destroy(); - } -} -function createLoaderContext(frag, part = null) { - const segment = part || frag; - const loaderContext = { - frag, - part, - responseType: 'arraybuffer', - url: segment.url, - headers: {}, - rangeStart: 0, - rangeEnd: 0 - }; - const start = segment.byteRangeStartOffset; - const end = segment.byteRangeEndOffset; - if (isFiniteNumber(start) && isFiniteNumber(end)) { - var _frag$decryptdata; - let byteRangeStart = start; - let byteRangeEnd = end; - if (frag.sn === 'initSegment' && ((_frag$decryptdata = frag.decryptdata) == null ? void 0 : _frag$decryptdata.method) === 'AES-128') { - // MAP segment encrypted with method 'AES-128', when served with HTTP Range, - // has the unencrypted size specified in the range. - // Ref: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6 - const fragmentLen = end - start; - if (fragmentLen % 16) { - byteRangeEnd = end + (16 - fragmentLen % 16); - } - if (start !== 0) { - loaderContext.resetIV = true; - byteRangeStart = start - 16; - } - } - loaderContext.rangeStart = byteRangeStart; - loaderContext.rangeEnd = byteRangeEnd; - } - return loaderContext; -} -function createGapLoadError(frag, part) { - const error = new Error(`GAP ${frag.gap ? 'tag' : 'attribute'} found`); - const errorData = { - type: ErrorTypes.MEDIA_ERROR, - details: ErrorDetails.FRAG_GAP, - fatal: false, - frag, - error, - networkDetails: null - }; - if (part) { - errorData.part = part; - } - (part ? part : frag).stats.aborted = true; - return new LoadError(errorData); -} -class LoadError extends Error { - constructor(data) { - super(data.error.message); - this.data = void 0; - this.data = data; - } -} - -class AESCrypto { - constructor(subtle, iv) { - this.subtle = void 0; - this.aesIV = void 0; - this.subtle = subtle; - this.aesIV = iv; - } - decrypt(data, key) { - return this.subtle.decrypt({ - name: 'AES-CBC', - iv: this.aesIV - }, key, data); - } -} - -class FastAESKey { - constructor(subtle, key) { - this.subtle = void 0; - this.key = void 0; - this.subtle = subtle; - this.key = key; - } - expandKey() { - return this.subtle.importKey('raw', this.key, { - name: 'AES-CBC' - }, false, ['encrypt', 'decrypt']); - } -} - -// PKCS7 -function removePadding(array) { - const outputBytes = array.byteLength; - const paddingBytes = outputBytes && new DataView(array.buffer).getUint8(outputBytes - 1); - if (paddingBytes) { - return sliceUint8(array, 0, outputBytes - paddingBytes); - } - return array; -} -class AESDecryptor { - constructor() { - this.rcon = [0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]; - this.subMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)]; - this.invSubMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)]; - this.sBox = new Uint32Array(256); - this.invSBox = new Uint32Array(256); - this.key = new Uint32Array(0); - this.ksRows = 0; - this.keySize = 0; - this.keySchedule = void 0; - this.invKeySchedule = void 0; - this.initTable(); - } - - // Using view.getUint32() also swaps the byte order. - uint8ArrayToUint32Array_(arrayBuffer) { - const view = new DataView(arrayBuffer); - const newArray = new Uint32Array(4); - for (let i = 0; i < 4; i++) { - newArray[i] = view.getUint32(i * 4); - } - return newArray; - } - initTable() { - const sBox = this.sBox; - const invSBox = this.invSBox; - const subMix = this.subMix; - const subMix0 = subMix[0]; - const subMix1 = subMix[1]; - const subMix2 = subMix[2]; - const subMix3 = subMix[3]; - const invSubMix = this.invSubMix; - const invSubMix0 = invSubMix[0]; - const invSubMix1 = invSubMix[1]; - const invSubMix2 = invSubMix[2]; - const invSubMix3 = invSubMix[3]; - const d = new Uint32Array(256); - let x = 0; - let xi = 0; - let i = 0; - for (i = 0; i < 256; i++) { - if (i < 128) { - d[i] = i << 1; - } else { - d[i] = i << 1 ^ 0x11b; - } - } - for (i = 0; i < 256; i++) { - let sx = xi ^ xi << 1 ^ xi << 2 ^ xi << 3 ^ xi << 4; - sx = sx >>> 8 ^ sx & 0xff ^ 0x63; - sBox[x] = sx; - invSBox[sx] = x; - - // Compute multiplication - const x2 = d[x]; - const x4 = d[x2]; - const x8 = d[x4]; - - // Compute sub/invSub bytes, mix columns tables - let t = d[sx] * 0x101 ^ sx * 0x1010100; - subMix0[x] = t << 24 | t >>> 8; - subMix1[x] = t << 16 | t >>> 16; - subMix2[x] = t << 8 | t >>> 24; - subMix3[x] = t; - - // Compute inv sub bytes, inv mix columns tables - t = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100; - invSubMix0[sx] = t << 24 | t >>> 8; - invSubMix1[sx] = t << 16 | t >>> 16; - invSubMix2[sx] = t << 8 | t >>> 24; - invSubMix3[sx] = t; - - // Compute next counter - if (!x) { - x = xi = 1; - } else { - x = x2 ^ d[d[d[x8 ^ x2]]]; - xi ^= d[d[xi]]; - } - } - } - expandKey(keyBuffer) { - // convert keyBuffer to Uint32Array - const key = this.uint8ArrayToUint32Array_(keyBuffer); - let sameKey = true; - let offset = 0; - while (offset < key.length && sameKey) { - sameKey = key[offset] === this.key[offset]; - offset++; - } - if (sameKey) { - return; - } - this.key = key; - const keySize = this.keySize = key.length; - if (keySize !== 4 && keySize !== 6 && keySize !== 8) { - throw new Error('Invalid aes key size=' + keySize); - } - const ksRows = this.ksRows = (keySize + 6 + 1) * 4; - let ksRow; - let invKsRow; - const keySchedule = this.keySchedule = new Uint32Array(ksRows); - const invKeySchedule = this.invKeySchedule = new Uint32Array(ksRows); - const sbox = this.sBox; - const rcon = this.rcon; - const invSubMix = this.invSubMix; - const invSubMix0 = invSubMix[0]; - const invSubMix1 = invSubMix[1]; - const invSubMix2 = invSubMix[2]; - const invSubMix3 = invSubMix[3]; - let prev; - let t; - for (ksRow = 0; ksRow < ksRows; ksRow++) { - if (ksRow < keySize) { - prev = keySchedule[ksRow] = key[ksRow]; - continue; - } - t = prev; - if (ksRow % keySize === 0) { - // Rot word - t = t << 8 | t >>> 24; - - // Sub word - t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 0xff] << 16 | sbox[t >>> 8 & 0xff] << 8 | sbox[t & 0xff]; - - // Mix Rcon - t ^= rcon[ksRow / keySize | 0] << 24; - } else if (keySize > 6 && ksRow % keySize === 4) { - // Sub word - t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 0xff] << 16 | sbox[t >>> 8 & 0xff] << 8 | sbox[t & 0xff]; - } - keySchedule[ksRow] = prev = (keySchedule[ksRow - keySize] ^ t) >>> 0; - } - for (invKsRow = 0; invKsRow < ksRows; invKsRow++) { - ksRow = ksRows - invKsRow; - if (invKsRow & 3) { - t = keySchedule[ksRow]; - } else { - t = keySchedule[ksRow - 4]; - } - if (invKsRow < 4 || ksRow <= 4) { - invKeySchedule[invKsRow] = t; - } else { - invKeySchedule[invKsRow] = invSubMix0[sbox[t >>> 24]] ^ invSubMix1[sbox[t >>> 16 & 0xff]] ^ invSubMix2[sbox[t >>> 8 & 0xff]] ^ invSubMix3[sbox[t & 0xff]]; - } - invKeySchedule[invKsRow] = invKeySchedule[invKsRow] >>> 0; - } - } - - // Adding this as a method greatly improves performance. - networkToHostOrderSwap(word) { - return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24; - } - decrypt(inputArrayBuffer, offset, aesIV) { - const nRounds = this.keySize + 6; - const invKeySchedule = this.invKeySchedule; - const invSBOX = this.invSBox; - const invSubMix = this.invSubMix; - const invSubMix0 = invSubMix[0]; - const invSubMix1 = invSubMix[1]; - const invSubMix2 = invSubMix[2]; - const invSubMix3 = invSubMix[3]; - const initVector = this.uint8ArrayToUint32Array_(aesIV); - let initVector0 = initVector[0]; - let initVector1 = initVector[1]; - let initVector2 = initVector[2]; - let initVector3 = initVector[3]; - const inputInt32 = new Int32Array(inputArrayBuffer); - const outputInt32 = new Int32Array(inputInt32.length); - let t0, t1, t2, t3; - let s0, s1, s2, s3; - let inputWords0, inputWords1, inputWords2, inputWords3; - let ksRow, i; - const swapWord = this.networkToHostOrderSwap; - while (offset < inputInt32.length) { - inputWords0 = swapWord(inputInt32[offset]); - inputWords1 = swapWord(inputInt32[offset + 1]); - inputWords2 = swapWord(inputInt32[offset + 2]); - inputWords3 = swapWord(inputInt32[offset + 3]); - s0 = inputWords0 ^ invKeySchedule[0]; - s1 = inputWords3 ^ invKeySchedule[1]; - s2 = inputWords2 ^ invKeySchedule[2]; - s3 = inputWords1 ^ invKeySchedule[3]; - ksRow = 4; - - // Iterate through the rounds of decryption - for (i = 1; i < nRounds; i++) { - t0 = invSubMix0[s0 >>> 24] ^ invSubMix1[s1 >> 16 & 0xff] ^ invSubMix2[s2 >> 8 & 0xff] ^ invSubMix3[s3 & 0xff] ^ invKeySchedule[ksRow]; - t1 = invSubMix0[s1 >>> 24] ^ invSubMix1[s2 >> 16 & 0xff] ^ invSubMix2[s3 >> 8 & 0xff] ^ invSubMix3[s0 & 0xff] ^ invKeySchedule[ksRow + 1]; - t2 = invSubMix0[s2 >>> 24] ^ invSubMix1[s3 >> 16 & 0xff] ^ invSubMix2[s0 >> 8 & 0xff] ^ invSubMix3[s1 & 0xff] ^ invKeySchedule[ksRow + 2]; - t3 = invSubMix0[s3 >>> 24] ^ invSubMix1[s0 >> 16 & 0xff] ^ invSubMix2[s1 >> 8 & 0xff] ^ invSubMix3[s2 & 0xff] ^ invKeySchedule[ksRow + 3]; - // Update state - s0 = t0; - s1 = t1; - s2 = t2; - s3 = t3; - ksRow = ksRow + 4; - } - - // Shift rows, sub bytes, add round key - t0 = invSBOX[s0 >>> 24] << 24 ^ invSBOX[s1 >> 16 & 0xff] << 16 ^ invSBOX[s2 >> 8 & 0xff] << 8 ^ invSBOX[s3 & 0xff] ^ invKeySchedule[ksRow]; - t1 = invSBOX[s1 >>> 24] << 24 ^ invSBOX[s2 >> 16 & 0xff] << 16 ^ invSBOX[s3 >> 8 & 0xff] << 8 ^ invSBOX[s0 & 0xff] ^ invKeySchedule[ksRow + 1]; - t2 = invSBOX[s2 >>> 24] << 24 ^ invSBOX[s3 >> 16 & 0xff] << 16 ^ invSBOX[s0 >> 8 & 0xff] << 8 ^ invSBOX[s1 & 0xff] ^ invKeySchedule[ksRow + 2]; - t3 = invSBOX[s3 >>> 24] << 24 ^ invSBOX[s0 >> 16 & 0xff] << 16 ^ invSBOX[s1 >> 8 & 0xff] << 8 ^ invSBOX[s2 & 0xff] ^ invKeySchedule[ksRow + 3]; - - // Write - outputInt32[offset] = swapWord(t0 ^ initVector0); - outputInt32[offset + 1] = swapWord(t3 ^ initVector1); - outputInt32[offset + 2] = swapWord(t2 ^ initVector2); - outputInt32[offset + 3] = swapWord(t1 ^ initVector3); - - // reset initVector to last 4 unsigned int - initVector0 = inputWords0; - initVector1 = inputWords1; - initVector2 = inputWords2; - initVector3 = inputWords3; - offset = offset + 4; - } - return outputInt32.buffer; - } -} - -const CHUNK_SIZE = 16; // 16 bytes, 128 bits - -class Decrypter { - constructor(config, { - removePKCS7Padding = true - } = {}) { - this.logEnabled = true; - this.removePKCS7Padding = void 0; - this.subtle = null; - this.softwareDecrypter = null; - this.key = null; - this.fastAesKey = null; - this.remainderData = null; - this.currentIV = null; - this.currentResult = null; - this.useSoftware = void 0; - this.useSoftware = config.enableSoftwareAES; - this.removePKCS7Padding = removePKCS7Padding; - // built in decryptor expects PKCS7 padding - if (removePKCS7Padding) { - try { - const browserCrypto = self.crypto; - if (browserCrypto) { - this.subtle = browserCrypto.subtle || browserCrypto.webkitSubtle; - } - } catch (e) { - /* no-op */ - } - } - this.useSoftware = !this.subtle; - } - destroy() { - this.subtle = null; - this.softwareDecrypter = null; - this.key = null; - this.fastAesKey = null; - this.remainderData = null; - this.currentIV = null; - this.currentResult = null; - } - isSync() { - return this.useSoftware; - } - flush() { - const { - currentResult, - remainderData - } = this; - if (!currentResult || remainderData) { - this.reset(); - return null; - } - const data = new Uint8Array(currentResult); - this.reset(); - if (this.removePKCS7Padding) { - return removePadding(data); - } - return data; - } - reset() { - this.currentResult = null; - this.currentIV = null; - this.remainderData = null; - if (this.softwareDecrypter) { - this.softwareDecrypter = null; - } - } - decrypt(data, key, iv) { - if (this.useSoftware) { - return new Promise((resolve, reject) => { - this.softwareDecrypt(new Uint8Array(data), key, iv); - const decryptResult = this.flush(); - if (decryptResult) { - resolve(decryptResult.buffer); - } else { - reject(new Error('[softwareDecrypt] Failed to decrypt data')); - } - }); - } - return this.webCryptoDecrypt(new Uint8Array(data), key, iv); - } - - // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached - // data is handled in the flush() call - softwareDecrypt(data, key, iv) { - const { - currentIV, - currentResult, - remainderData - } = this; - this.logOnce('JS AES decrypt'); - // The output is staggered during progressive parsing - the current result is cached, and emitted on the next call - // This is done in order to strip PKCS7 padding, which is found at the end of each segment. We only know we've reached - // the end on flush(), but by that time we have already received all bytes for the segment. - // Progressive decryption does not work with WebCrypto - - if (remainderData) { - data = appendUint8Array(remainderData, data); - this.remainderData = null; - } - - // Byte length must be a multiple of 16 (AES-128 = 128 bit blocks = 16 bytes) - const currentChunk = this.getValidChunk(data); - if (!currentChunk.length) { - return null; - } - if (currentIV) { - iv = currentIV; - } - let softwareDecrypter = this.softwareDecrypter; - if (!softwareDecrypter) { - softwareDecrypter = this.softwareDecrypter = new AESDecryptor(); - } - softwareDecrypter.expandKey(key); - const result = currentResult; - this.currentResult = softwareDecrypter.decrypt(currentChunk.buffer, 0, iv); - this.currentIV = sliceUint8(currentChunk, -16).buffer; - if (!result) { - return null; - } - return result; - } - webCryptoDecrypt(data, key, iv) { - if (this.key !== key || !this.fastAesKey) { - if (!this.subtle) { - return Promise.resolve(this.onWebCryptoError(data, key, iv)); - } - this.key = key; - this.fastAesKey = new FastAESKey(this.subtle, key); - } - return this.fastAesKey.expandKey().then(aesKey => { - // decrypt using web crypto - if (!this.subtle) { - return Promise.reject(new Error('web crypto not initialized')); - } - this.logOnce('WebCrypto AES decrypt'); - const crypto = new AESCrypto(this.subtle, new Uint8Array(iv)); - return crypto.decrypt(data.buffer, aesKey); - }).catch(err => { - logger.warn(`[decrypter]: WebCrypto Error, disable WebCrypto API, ${err.name}: ${err.message}`); - return this.onWebCryptoError(data, key, iv); - }); - } - onWebCryptoError(data, key, iv) { - this.useSoftware = true; - this.logEnabled = true; - this.softwareDecrypt(data, key, iv); - const decryptResult = this.flush(); - if (decryptResult) { - return decryptResult.buffer; - } - throw new Error('WebCrypto and softwareDecrypt: failed to decrypt data'); - } - getValidChunk(data) { - let currentChunk = data; - const splitPoint = data.length - data.length % CHUNK_SIZE; - if (splitPoint !== data.length) { - currentChunk = sliceUint8(data, 0, splitPoint); - this.remainderData = sliceUint8(data, splitPoint); - } - return currentChunk; - } - logOnce(msg) { - if (!this.logEnabled) { - return; - } - logger.log(`[decrypter]: ${msg}`); - this.logEnabled = false; - } -} - -/** - * TimeRanges to string helper - */ - -const TimeRanges = { - toString: function (r) { - let log = ''; - const len = r.length; - for (let i = 0; i < len; i++) { - log += `[${r.start(i).toFixed(3)}-${r.end(i).toFixed(3)}]`; - } - return log; - } -}; - -const State = { - STOPPED: 'STOPPED', - IDLE: 'IDLE', - KEY_LOADING: 'KEY_LOADING', - FRAG_LOADING: 'FRAG_LOADING', - FRAG_LOADING_WAITING_RETRY: 'FRAG_LOADING_WAITING_RETRY', - WAITING_TRACK: 'WAITING_TRACK', - PARSING: 'PARSING', - PARSED: 'PARSED', - ENDED: 'ENDED', - ERROR: 'ERROR', - WAITING_INIT_PTS: 'WAITING_INIT_PTS', - WAITING_LEVEL: 'WAITING_LEVEL' -}; -class BaseStreamController extends TaskLoop { - constructor(hls, fragmentTracker, keyLoader, logPrefix, playlistType) { - super(); - this.hls = void 0; - this.fragPrevious = null; - this.fragCurrent = null; - this.fragmentTracker = void 0; - this.transmuxer = null; - this._state = State.STOPPED; - this.playlistType = void 0; - this.media = null; - this.mediaBuffer = null; - this.config = void 0; - this.bitrateTest = false; - this.lastCurrentTime = 0; - this.nextLoadPosition = 0; - this.startPosition = 0; - this.startTimeOffset = null; - this.loadedmetadata = false; - this.retryDate = 0; - this.levels = null; - this.fragmentLoader = void 0; - this.keyLoader = void 0; - this.levelLastLoaded = null; - this.startFragRequested = false; - this.decrypter = void 0; - this.initPTS = []; - this.onvseeking = null; - this.onvended = null; - this.logPrefix = ''; - this.log = void 0; - this.warn = void 0; - this.playlistType = playlistType; - this.logPrefix = logPrefix; - this.log = logger.log.bind(logger, `${logPrefix}:`); - this.warn = logger.warn.bind(logger, `${logPrefix}:`); - this.hls = hls; - this.fragmentLoader = new FragmentLoader(hls.config); - this.keyLoader = keyLoader; - this.fragmentTracker = fragmentTracker; - this.config = hls.config; - this.decrypter = new Decrypter(hls.config); - hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this); - } - doTick() { - this.onTickEnd(); - } - onTickEnd() {} - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - startLoad(startPosition) {} - stopLoad() { - this.fragmentLoader.abort(); - this.keyLoader.abort(this.playlistType); - const frag = this.fragCurrent; - if (frag != null && frag.loader) { - frag.abortRequests(); - this.fragmentTracker.removeFragment(frag); - } - this.resetTransmuxer(); - this.fragCurrent = null; - this.fragPrevious = null; - this.clearInterval(); - this.clearNextTick(); - this.state = State.STOPPED; - } - _streamEnded(bufferInfo, levelDetails) { - // If playlist is live, there is another buffered range after the current range, nothing buffered, media is detached, - // of nothing loading/loaded return false - if (levelDetails.live || bufferInfo.nextStart || !bufferInfo.end || !this.media) { - return false; - } - const partList = levelDetails.partList; - // Since the last part isn't guaranteed to correspond to the last playlist segment for Low-Latency HLS, - // check instead if the last part is buffered. - if (partList != null && partList.length) { - const lastPart = partList[partList.length - 1]; - - // Checking the midpoint of the part for potential margin of error and related issues. - // NOTE: Technically I believe parts could yield content that is < the computed duration (including potential a duration of 0) - // and still be spec-compliant, so there may still be edge cases here. Likewise, there could be issues in end of stream - // part mismatches for independent audio and video playlists/segments. - const lastPartBuffered = BufferHelper.isBuffered(this.media, lastPart.start + lastPart.duration / 2); - return lastPartBuffered; - } - const playlistType = levelDetails.fragments[levelDetails.fragments.length - 1].type; - return this.fragmentTracker.isEndListAppended(playlistType); - } - getLevelDetails() { - if (this.levels && this.levelLastLoaded !== null) { - var _this$levelLastLoaded; - return (_this$levelLastLoaded = this.levelLastLoaded) == null ? void 0 : _this$levelLastLoaded.details; - } - } - onMediaAttached(event, data) { - const media = this.media = this.mediaBuffer = data.media; - this.onvseeking = this.onMediaSeeking.bind(this); - this.onvended = this.onMediaEnded.bind(this); - media.addEventListener('seeking', this.onvseeking); - media.addEventListener('ended', this.onvended); - const config = this.config; - if (this.levels && config.autoStartLoad && this.state === State.STOPPED) { - this.startLoad(config.startPosition); - } - } - onMediaDetaching() { - const media = this.media; - if (media != null && media.ended) { - this.log('MSE detaching and video ended, reset startPosition'); - this.startPosition = this.lastCurrentTime = 0; - } - - // remove video listeners - if (media && this.onvseeking && this.onvended) { - media.removeEventListener('seeking', this.onvseeking); - media.removeEventListener('ended', this.onvended); - this.onvseeking = this.onvended = null; - } - if (this.keyLoader) { - this.keyLoader.detach(); - } - this.media = this.mediaBuffer = null; - this.loadedmetadata = false; - this.fragmentTracker.removeAllFragments(); - this.stopLoad(); - } - onMediaSeeking() { - const { - config, - fragCurrent, - media, - mediaBuffer, - state - } = this; - const currentTime = media ? media.currentTime : 0; - const bufferInfo = BufferHelper.bufferInfo(mediaBuffer ? mediaBuffer : media, currentTime, config.maxBufferHole); - this.log(`media seeking to ${isFiniteNumber(currentTime) ? currentTime.toFixed(3) : currentTime}, state: ${state}`); - if (this.state === State.ENDED) { - this.resetLoadingState(); - } else if (fragCurrent) { - // Seeking while frag load is in progress - const tolerance = config.maxFragLookUpTolerance; - const fragStartOffset = fragCurrent.start - tolerance; - const fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance; - // if seeking out of buffered range or into new one - if (!bufferInfo.len || fragEndOffset < bufferInfo.start || fragStartOffset > bufferInfo.end) { - const pastFragment = currentTime > fragEndOffset; - // if the seek position is outside the current fragment range - if (currentTime < fragStartOffset || pastFragment) { - if (pastFragment && fragCurrent.loader) { - this.log('seeking outside of buffer while fragment load in progress, cancel fragment load'); - fragCurrent.abortRequests(); - this.resetLoadingState(); - } - this.fragPrevious = null; - } - } - } - if (media) { - // Remove gap fragments - this.fragmentTracker.removeFragmentsInRange(currentTime, Infinity, this.playlistType, true); - this.lastCurrentTime = currentTime; - } - - // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target - if (!this.loadedmetadata && !bufferInfo.len) { - this.nextLoadPosition = this.startPosition = currentTime; - } - - // Async tick to speed up processing - this.tickImmediate(); - } - onMediaEnded() { - // reset startPosition and lastCurrentTime to restart playback @ stream beginning - this.startPosition = this.lastCurrentTime = 0; - } - onManifestLoaded(event, data) { - this.startTimeOffset = data.startTimeOffset; - this.initPTS = []; - } - onHandlerDestroying() { - this.hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this); - this.stopLoad(); - super.onHandlerDestroying(); - // @ts-ignore - this.hls = null; - } - onHandlerDestroyed() { - this.state = State.STOPPED; - if (this.fragmentLoader) { - this.fragmentLoader.destroy(); - } - if (this.keyLoader) { - this.keyLoader.destroy(); - } - if (this.decrypter) { - this.decrypter.destroy(); - } - this.hls = this.log = this.warn = this.decrypter = this.keyLoader = this.fragmentLoader = this.fragmentTracker = null; - super.onHandlerDestroyed(); - } - loadFragment(frag, level, targetBufferTime) { - this._loadFragForPlayback(frag, level, targetBufferTime); - } - _loadFragForPlayback(frag, level, targetBufferTime) { - const progressCallback = data => { - if (this.fragContextChanged(frag)) { - this.warn(`Fragment ${frag.sn}${data.part ? ' p: ' + data.part.index : ''} of level ${frag.level} was dropped during download.`); - this.fragmentTracker.removeFragment(frag); - return; - } - frag.stats.chunkCount++; - this._handleFragmentLoadProgress(data); - }; - this._doFragLoad(frag, level, targetBufferTime, progressCallback).then(data => { - if (!data) { - // if we're here we probably needed to backtrack or are waiting for more parts - return; - } - const state = this.state; - if (this.fragContextChanged(frag)) { - if (state === State.FRAG_LOADING || !this.fragCurrent && state === State.PARSING) { - this.fragmentTracker.removeFragment(frag); - this.state = State.IDLE; - } - return; - } - if ('payload' in data) { - this.log(`Loaded fragment ${frag.sn} of level ${frag.level}`); - this.hls.trigger(Events.FRAG_LOADED, data); - } - - // Pass through the whole payload; controllers not implementing progressive loading receive data from this callback - this._handleFragmentLoadComplete(data); - }).catch(reason => { - if (this.state === State.STOPPED || this.state === State.ERROR) { - return; - } - this.warn(`Frag error: ${(reason == null ? void 0 : reason.message) || reason}`); - this.resetFragmentLoading(frag); - }); - } - clearTrackerIfNeeded(frag) { - var _this$mediaBuffer; - const { - fragmentTracker - } = this; - const fragState = fragmentTracker.getState(frag); - if (fragState === FragmentState.APPENDING) { - // Lower the max buffer length and try again - const playlistType = frag.type; - const bufferedInfo = this.getFwdBufferInfo(this.mediaBuffer, playlistType); - const minForwardBufferLength = Math.max(frag.duration, bufferedInfo ? bufferedInfo.len : this.config.maxBufferLength); - // If backtracking, always remove from the tracker without reducing max buffer length - const backtrackFragment = this.backtrackFragment; - const backtracked = backtrackFragment ? frag.sn - backtrackFragment.sn : 0; - if (backtracked === 1 || this.reduceMaxBufferLength(minForwardBufferLength, frag.duration)) { - fragmentTracker.removeFragment(frag); - } - } else if (((_this$mediaBuffer = this.mediaBuffer) == null ? void 0 : _this$mediaBuffer.buffered.length) === 0) { - // Stop gap for bad tracker / buffer flush behavior - fragmentTracker.removeAllFragments(); - } else if (fragmentTracker.hasParts(frag.type)) { - // In low latency mode, remove fragments for which only some parts were buffered - fragmentTracker.detectPartialFragments({ - frag, - part: null, - stats: frag.stats, - id: frag.type - }); - if (fragmentTracker.getState(frag) === FragmentState.PARTIAL) { - fragmentTracker.removeFragment(frag); - } - } - } - checkLiveUpdate(details) { - if (details.updated && !details.live) { - // Live stream ended, update fragment tracker - const lastFragment = details.fragments[details.fragments.length - 1]; - this.fragmentTracker.detectPartialFragments({ - frag: lastFragment, - part: null, - stats: lastFragment.stats, - id: lastFragment.type - }); - } - if (!details.fragments[0]) { - details.deltaUpdateFailed = true; - } - } - flushMainBuffer(startOffset, endOffset, type = null) { - if (!(startOffset - endOffset)) { - return; - } - // When alternate audio is playing, the audio-stream-controller is responsible for the audio buffer. Otherwise, - // passing a null type flushes both buffers - const flushScope = { - startOffset, - endOffset, - type - }; - this.hls.trigger(Events.BUFFER_FLUSHING, flushScope); - } - _loadInitSegment(frag, level) { - this._doFragLoad(frag, level).then(data => { - if (!data || this.fragContextChanged(frag) || !this.levels) { - throw new Error('init load aborted'); - } - return data; - }).then(data => { - const { - hls - } = this; - const { - payload - } = data; - const decryptData = frag.decryptdata; - - // check to see if the payload needs to be decrypted - if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && decryptData.method === 'AES-128') { - const startTime = self.performance.now(); - // decrypt init segment data - return this.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer).catch(err => { - hls.trigger(Events.ERROR, { - type: ErrorTypes.MEDIA_ERROR, - details: ErrorDetails.FRAG_DECRYPT_ERROR, - fatal: false, - error: err, - reason: err.message, - frag - }); - throw err; - }).then(decryptedData => { - const endTime = self.performance.now(); - hls.trigger(Events.FRAG_DECRYPTED, { - frag, - payload: decryptedData, - stats: { - tstart: startTime, - tdecrypt: endTime - } - }); - data.payload = decryptedData; - return this.completeInitSegmentLoad(data); - }); - } - return this.completeInitSegmentLoad(data); - }).catch(reason => { - if (this.state === State.STOPPED || this.state === State.ERROR) { - return; - } - this.warn(reason); - this.resetFragmentLoading(frag); - }); - } - completeInitSegmentLoad(data) { - const { - levels - } = this; - if (!levels) { - throw new Error('init load aborted, missing levels'); - } - const stats = data.frag.stats; - this.state = State.IDLE; - data.frag.data = new Uint8Array(data.payload); - stats.parsing.start = stats.buffering.start = self.performance.now(); - stats.parsing.end = stats.buffering.end = self.performance.now(); - this.tick(); - } - fragContextChanged(frag) { - const { - fragCurrent - } = this; - return !frag || !fragCurrent || frag.sn !== fragCurrent.sn || frag.level !== fragCurrent.level; - } - fragBufferedComplete(frag, part) { - var _frag$startPTS, _frag$endPTS, _this$fragCurrent, _this$fragPrevious; - const media = this.mediaBuffer ? this.mediaBuffer : this.media; - this.log(`Buffered ${frag.type} sn: ${frag.sn}${part ? ' part: ' + part.index : ''} of ${this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'} ${frag.level} (frag:[${((_frag$startPTS = frag.startPTS) != null ? _frag$startPTS : NaN).toFixed(3)}-${((_frag$endPTS = frag.endPTS) != null ? _frag$endPTS : NaN).toFixed(3)}] > buffer:${media ? TimeRanges.toString(BufferHelper.getBuffered(media)) : '(detached)'})`); - if (frag.sn !== 'initSegment') { - var _this$levels; - if (frag.type !== PlaylistLevelType.SUBTITLE) { - const el = frag.elementaryStreams; - if (!Object.keys(el).some(type => !!el[type])) { - // empty segment - this.state = State.IDLE; - return; - } - } - const level = (_this$levels = this.levels) == null ? void 0 : _this$levels[frag.level]; - if (level != null && level.fragmentError) { - this.log(`Resetting level fragment error count of ${level.fragmentError} on frag buffered`); - level.fragmentError = 0; - } - } - this.state = State.IDLE; - if (!media) { - return; - } - if (!this.loadedmetadata && frag.type == PlaylistLevelType.MAIN && media.buffered.length && ((_this$fragCurrent = this.fragCurrent) == null ? void 0 : _this$fragCurrent.sn) === ((_this$fragPrevious = this.fragPrevious) == null ? void 0 : _this$fragPrevious.sn)) { - this.loadedmetadata = true; - this.seekToStartPos(); - } - this.tick(); - } - seekToStartPos() {} - _handleFragmentLoadComplete(fragLoadedEndData) { - const { - transmuxer - } = this; - if (!transmuxer) { - return; - } - const { - frag, - part, - partsLoaded - } = fragLoadedEndData; - // If we did not load parts, or loaded all parts, we have complete (not partial) fragment data - const complete = !partsLoaded || partsLoaded.length === 0 || partsLoaded.some(fragLoaded => !fragLoaded); - const chunkMeta = new ChunkMetadata(frag.level, frag.sn, frag.stats.chunkCount + 1, 0, part ? part.index : -1, !complete); - transmuxer.flush(chunkMeta); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _handleFragmentLoadProgress(frag) {} - _doFragLoad(frag, level, targetBufferTime = null, progressCallback) { - var _frag$decryptdata; - const details = level == null ? void 0 : level.details; - if (!this.levels || !details) { - throw new Error(`frag load aborted, missing level${details ? '' : ' detail'}s`); - } - let keyLoadingPromise = null; - if (frag.encrypted && !((_frag$decryptdata = frag.decryptdata) != null && _frag$decryptdata.key)) { - this.log(`Loading key for ${frag.sn} of [${details.startSN}-${details.endSN}], ${this.logPrefix === '[stream-controller]' ? 'level' : 'track'} ${frag.level}`); - this.state = State.KEY_LOADING; - this.fragCurrent = frag; - keyLoadingPromise = this.keyLoader.load(frag).then(keyLoadedData => { - if (!this.fragContextChanged(keyLoadedData.frag)) { - this.hls.trigger(Events.KEY_LOADED, keyLoadedData); - if (this.state === State.KEY_LOADING) { - this.state = State.IDLE; - } - return keyLoadedData; - } - }); - this.hls.trigger(Events.KEY_LOADING, { - frag - }); - if (this.fragCurrent === null) { - keyLoadingPromise = Promise.reject(new Error(`frag load aborted, context changed in KEY_LOADING`)); - } - } else if (!frag.encrypted && details.encryptedFragments.length) { - this.keyLoader.loadClear(frag, details.encryptedFragments); - } - targetBufferTime = Math.max(frag.start, targetBufferTime || 0); - if (this.config.lowLatencyMode && frag.sn !== 'initSegment') { - const partList = details.partList; - if (partList && progressCallback) { - if (targetBufferTime > frag.end && details.fragmentHint) { - frag = details.fragmentHint; - } - const partIndex = this.getNextPart(partList, frag, targetBufferTime); - if (partIndex > -1) { - const part = partList[partIndex]; - this.log(`Loading part sn: ${frag.sn} p: ${part.index} cc: ${frag.cc} of playlist [${details.startSN}-${details.endSN}] parts [0-${partIndex}-${partList.length - 1}] ${this.logPrefix === '[stream-controller]' ? 'level' : 'track'}: ${frag.level}, target: ${parseFloat(targetBufferTime.toFixed(3))}`); - this.nextLoadPosition = part.start + part.duration; - this.state = State.FRAG_LOADING; - let _result; - if (keyLoadingPromise) { - _result = keyLoadingPromise.then(keyLoadedData => { - if (!keyLoadedData || this.fragContextChanged(keyLoadedData.frag)) { - return null; - } - return this.doFragPartsLoad(frag, part, level, progressCallback); - }).catch(error => this.handleFragLoadError(error)); - } else { - _result = this.doFragPartsLoad(frag, part, level, progressCallback).catch(error => this.handleFragLoadError(error)); - } - this.hls.trigger(Events.FRAG_LOADING, { - frag, - part, - targetBufferTime - }); - if (this.fragCurrent === null) { - return Promise.reject(new Error(`frag load aborted, context changed in FRAG_LOADING parts`)); - } - return _result; - } else if (!frag.url || this.loadedEndOfParts(partList, targetBufferTime)) { - // Fragment hint has no parts - return Promise.resolve(null); - } - } - } - this.log(`Loading fragment ${frag.sn} cc: ${frag.cc} ${details ? 'of [' + details.startSN + '-' + details.endSN + '] ' : ''}${this.logPrefix === '[stream-controller]' ? 'level' : 'track'}: ${frag.level}, target: ${parseFloat(targetBufferTime.toFixed(3))}`); - // Don't update nextLoadPosition for fragments which are not buffered - if (isFiniteNumber(frag.sn) && !this.bitrateTest) { - this.nextLoadPosition = frag.start + frag.duration; - } - this.state = State.FRAG_LOADING; - - // Load key before streaming fragment data - const dataOnProgress = this.config.progressive; - let result; - if (dataOnProgress && keyLoadingPromise) { - result = keyLoadingPromise.then(keyLoadedData => { - if (!keyLoadedData || this.fragContextChanged(keyLoadedData == null ? void 0 : keyLoadedData.frag)) { - return null; - } - return this.fragmentLoader.load(frag, progressCallback); - }).catch(error => this.handleFragLoadError(error)); - } else { - // load unencrypted fragment data with progress event, - // or handle fragment result after key and fragment are finished loading - result = Promise.all([this.fragmentLoader.load(frag, dataOnProgress ? progressCallback : undefined), keyLoadingPromise]).then(([fragLoadedData]) => { - if (!dataOnProgress && fragLoadedData && progressCallback) { - progressCallback(fragLoadedData); - } - return fragLoadedData; - }).catch(error => this.handleFragLoadError(error)); - } - this.hls.trigger(Events.FRAG_LOADING, { - frag, - targetBufferTime - }); - if (this.fragCurrent === null) { - return Promise.reject(new Error(`frag load aborted, context changed in FRAG_LOADING`)); - } - return result; - } - doFragPartsLoad(frag, fromPart, level, progressCallback) { - return new Promise((resolve, reject) => { - var _level$details; - const partsLoaded = []; - const initialPartList = (_level$details = level.details) == null ? void 0 : _level$details.partList; - const loadPart = part => { - this.fragmentLoader.loadPart(frag, part, progressCallback).then(partLoadedData => { - partsLoaded[part.index] = partLoadedData; - const loadedPart = partLoadedData.part; - this.hls.trigger(Events.FRAG_LOADED, partLoadedData); - const nextPart = getPartWith(level, frag.sn, part.index + 1) || findPart(initialPartList, frag.sn, part.index + 1); - if (nextPart) { - loadPart(nextPart); - } else { - return resolve({ - frag, - part: loadedPart, - partsLoaded - }); - } - }).catch(reject); - }; - loadPart(fromPart); - }); - } - handleFragLoadError(error) { - if ('data' in error) { - const data = error.data; - if (error.data && data.details === ErrorDetails.INTERNAL_ABORTED) { - this.handleFragLoadAborted(data.frag, data.part); - } else { - this.hls.trigger(Events.ERROR, data); - } - } else { - this.hls.trigger(Events.ERROR, { - type: ErrorTypes.OTHER_ERROR, - details: ErrorDetails.INTERNAL_EXCEPTION, - err: error, - error, - fatal: true - }); - } - return null; - } - _handleTransmuxerFlush(chunkMeta) { - const context = this.getCurrentContext(chunkMeta); - if (!context || this.state !== State.PARSING) { - if (!this.fragCurrent && this.state !== State.STOPPED && this.state !== State.ERROR) { - this.state = State.IDLE; - } - return; - } - const { - frag, - part, - level - } = context; - const now = self.performance.now(); - frag.stats.parsing.end = now; - if (part) { - part.stats.parsing.end = now; - } - this.updateLevelTiming(frag, part, level, chunkMeta.partial); - } - getCurrentContext(chunkMeta) { - const { - levels, - fragCurrent - } = this; - const { - level: levelIndex, - sn, - part: partIndex - } = chunkMeta; - if (!(levels != null && levels[levelIndex])) { - this.warn(`Levels object was unset while buffering fragment ${sn} of level ${levelIndex}. The current chunk will not be buffered.`); - return null; - } - const level = levels[levelIndex]; - const part = partIndex > -1 ? getPartWith(level, sn, partIndex) : null; - const frag = part ? part.fragment : getFragmentWithSN(level, sn, fragCurrent); - if (!frag) { - return null; - } - if (fragCurrent && fragCurrent !== frag) { - frag.stats = fragCurrent.stats; - } - return { - frag, - part, - level - }; - } - bufferFragmentData(data, frag, part, chunkMeta, noBacktracking) { - var _buffer; - if (!data || this.state !== State.PARSING) { - return; - } - const { - data1, - data2 - } = data; - let buffer = data1; - if (data1 && data2) { - // Combine the moof + mdat so that we buffer with a single append - buffer = appendUint8Array(data1, data2); - } - if (!((_buffer = buffer) != null && _buffer.length)) { - return; - } - const segment = { - type: data.type, - frag, - part, - chunkMeta, - parent: frag.type, - data: buffer - }; - this.hls.trigger(Events.BUFFER_APPENDING, segment); - if (data.dropped && data.independent && !part) { - if (noBacktracking) { - return; - } - // Clear buffer so that we reload previous segments sequentially if required - this.flushBufferGap(frag); - } - } - flushBufferGap(frag) { - const media = this.media; - if (!media) { - return; - } - // If currentTime is not buffered, clear the back buffer so that we can backtrack as much as needed - if (!BufferHelper.isBuffered(media, media.currentTime)) { - this.flushMainBuffer(0, frag.start); - return; - } - // Remove back-buffer without interrupting playback to allow back tracking - const currentTime = media.currentTime; - const bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0); - const fragDuration = frag.duration; - const segmentFraction = Math.min(this.config.maxFragLookUpTolerance * 2, fragDuration * 0.25); - const start = Math.max(Math.min(frag.start - segmentFraction, bufferInfo.end - segmentFraction), currentTime + segmentFraction); - if (frag.start - start > segmentFraction) { - this.flushMainBuffer(start, frag.start); - } - } - getFwdBufferInfo(bufferable, type) { - const pos = this.getLoadPosition(); - if (!isFiniteNumber(pos)) { - return null; - } - return this.getFwdBufferInfoAtPos(bufferable, pos, type); - } - getFwdBufferInfoAtPos(bufferable, pos, type) { - const { - config: { - maxBufferHole - } - } = this; - const bufferInfo = BufferHelper.bufferInfo(bufferable, pos, maxBufferHole); - // Workaround flaw in getting forward buffer when maxBufferHole is smaller than gap at current pos - if (bufferInfo.len === 0 && bufferInfo.nextStart !== undefined) { - const bufferedFragAtPos = this.fragmentTracker.getBufferedFrag(pos, type); - if (bufferedFragAtPos && bufferInfo.nextStart < bufferedFragAtPos.end) { - return BufferHelper.bufferInfo(bufferable, pos, Math.max(bufferInfo.nextStart, maxBufferHole)); - } - } - return bufferInfo; - } - getMaxBufferLength(levelBitrate) { - const { - config - } = this; - let maxBufLen; - if (levelBitrate) { - maxBufLen = Math.max(8 * config.maxBufferSize / levelBitrate, config.maxBufferLength); - } else { - maxBufLen = config.maxBufferLength; - } - return Math.min(maxBufLen, config.maxMaxBufferLength); - } - reduceMaxBufferLength(threshold, fragDuration) { - const config = this.config; - const minLength = Math.max(Math.min(threshold - fragDuration, config.maxBufferLength), fragDuration); - const reducedLength = Math.max(threshold - fragDuration * 3, config.maxMaxBufferLength / 2, minLength); - if (reducedLength >= minLength) { - // reduce max buffer length as it might be too high. we do this to avoid loop flushing ... - config.maxMaxBufferLength = reducedLength; - this.warn(`Reduce max buffer length to ${reducedLength}s`); - return true; - } - return false; - } - getAppendedFrag(position, playlistType = PlaylistLevelType.MAIN) { - const fragOrPart = this.fragmentTracker.getAppendedFrag(position, PlaylistLevelType.MAIN); - if (fragOrPart && 'fragment' in fragOrPart) { - return fragOrPart.fragment; - } - return fragOrPart; - } - getNextFragment(pos, levelDetails) { - const fragments = levelDetails.fragments; - const fragLen = fragments.length; - if (!fragLen) { - return null; - } - - // find fragment index, contiguous with end of buffer position - const { - config - } = this; - const start = fragments[0].start; - let frag; - if (levelDetails.live) { - const initialLiveManifestSize = config.initialLiveManifestSize; - if (fragLen < initialLiveManifestSize) { - this.warn(`Not enough fragments to start playback (have: ${fragLen}, need: ${initialLiveManifestSize})`); - return null; - } - // The real fragment start times for a live stream are only known after the PTS range for that level is known. - // In order to discover the range, we load the best matching fragment for that level and demux it. - // Do not load using live logic if the starting frag is requested - we want to use getFragmentAtPosition() so that - // we get the fragment matching that start time - if (!levelDetails.PTSKnown && !this.startFragRequested && this.startPosition === -1 || pos < start) { - frag = this.getInitialLiveFragment(levelDetails, fragments); - this.startPosition = this.nextLoadPosition = frag ? this.hls.liveSyncPosition || frag.start : pos; - } - } else if (pos <= start) { - // VoD playlist: if loadPosition before start of playlist, load first fragment - frag = fragments[0]; - } - - // If we haven't run into any special cases already, just load the fragment most closely matching the requested position - if (!frag) { - const end = config.lowLatencyMode ? levelDetails.partEnd : levelDetails.fragmentEnd; - frag = this.getFragmentAtPosition(pos, end, levelDetails); - } - return this.mapToInitFragWhenRequired(frag); - } - isLoopLoading(frag, targetBufferTime) { - const trackerState = this.fragmentTracker.getState(frag); - return (trackerState === FragmentState.OK || trackerState === FragmentState.PARTIAL && !!frag.gap) && this.nextLoadPosition > targetBufferTime; - } - getNextFragmentLoopLoading(frag, levelDetails, bufferInfo, playlistType, maxBufLen) { - const gapStart = frag.gap; - const nextFragment = this.getNextFragment(this.nextLoadPosition, levelDetails); - if (nextFragment === null) { - return nextFragment; - } - frag = nextFragment; - if (gapStart && frag && !frag.gap && bufferInfo.nextStart) { - // Media buffered after GAP tags should not make the next buffer timerange exceed forward buffer length - const nextbufferInfo = this.getFwdBufferInfoAtPos(this.mediaBuffer ? this.mediaBuffer : this.media, bufferInfo.nextStart, playlistType); - if (nextbufferInfo !== null && bufferInfo.len + nextbufferInfo.len >= maxBufLen) { - // Returning here might result in not finding an audio and video candiate to skip to - this.log(`buffer full after gaps in "${playlistType}" playlist starting at sn: ${frag.sn}`); - return null; - } - } - return frag; - } - mapToInitFragWhenRequired(frag) { - // If an initSegment is present, it must be buffered first - if (frag != null && frag.initSegment && !(frag != null && frag.initSegment.data) && !this.bitrateTest) { - return frag.initSegment; - } - return frag; - } - getNextPart(partList, frag, targetBufferTime) { - let nextPart = -1; - let contiguous = false; - let independentAttrOmitted = true; - for (let i = 0, len = partList.length; i < len; i++) { - const part = partList[i]; - independentAttrOmitted = independentAttrOmitted && !part.independent; - if (nextPart > -1 && targetBufferTime < part.start) { - break; - } - const loaded = part.loaded; - if (loaded) { - nextPart = -1; - } else if ((contiguous || part.independent || independentAttrOmitted) && part.fragment === frag) { - nextPart = i; - } - contiguous = loaded; - } - return nextPart; - } - loadedEndOfParts(partList, targetBufferTime) { - const lastPart = partList[partList.length - 1]; - return lastPart && targetBufferTime > lastPart.start && lastPart.loaded; - } - - /* - This method is used find the best matching first fragment for a live playlist. This fragment is used to calculate the - "sliding" of the playlist, which is its offset from the start of playback. After sliding we can compute the real - start and end times for each fragment in the playlist (after which this method will not need to be called). - */ - getInitialLiveFragment(levelDetails, fragments) { - const fragPrevious = this.fragPrevious; - let frag = null; - if (fragPrevious) { - if (levelDetails.hasProgramDateTime) { - // Prefer using PDT, because it can be accurate enough to choose the correct fragment without knowing the level sliding - this.log(`Live playlist, switching playlist, load frag with same PDT: ${fragPrevious.programDateTime}`); - frag = findFragmentByPDT(fragments, fragPrevious.endProgramDateTime, this.config.maxFragLookUpTolerance); - } - if (!frag) { - // SN does not need to be accurate between renditions, but depending on the packaging it may be so. - const targetSN = fragPrevious.sn + 1; - if (targetSN >= levelDetails.startSN && targetSN <= levelDetails.endSN) { - const fragNext = fragments[targetSN - levelDetails.startSN]; - // Ensure that we're staying within the continuity range, since PTS resets upon a new range - if (fragPrevious.cc === fragNext.cc) { - frag = fragNext; - this.log(`Live playlist, switching playlist, load frag with next SN: ${frag.sn}`); - } - } - // It's important to stay within the continuity range if available; otherwise the fragments in the playlist - // will have the wrong start times - if (!frag) { - frag = findFragWithCC(fragments, fragPrevious.cc); - if (frag) { - this.log(`Live playlist, switching playlist, load frag with same CC: ${frag.sn}`); - } - } - } - } else { - // Find a new start fragment when fragPrevious is null - const liveStart = this.hls.liveSyncPosition; - if (liveStart !== null) { - frag = this.getFragmentAtPosition(liveStart, this.bitrateTest ? levelDetails.fragmentEnd : levelDetails.edge, levelDetails); - } - } - return frag; - } - - /* - This method finds the best matching fragment given the provided position. - */ - getFragmentAtPosition(bufferEnd, end, levelDetails) { - const { - config - } = this; - let { - fragPrevious - } = this; - let { - fragments, - endSN - } = levelDetails; - const { - fragmentHint - } = levelDetails; - const { - maxFragLookUpTolerance - } = config; - const partList = levelDetails.partList; - const loadingParts = !!(config.lowLatencyMode && partList != null && partList.length && fragmentHint); - if (loadingParts && fragmentHint && !this.bitrateTest) { - // Include incomplete fragment with parts at end - fragments = fragments.concat(fragmentHint); - endSN = fragmentHint.sn; - } - let frag; - if (bufferEnd < end) { - const lookupTolerance = bufferEnd > end - maxFragLookUpTolerance ? 0 : maxFragLookUpTolerance; - // Remove the tolerance if it would put the bufferEnd past the actual end of stream - // Uses buffer and sequence number to calculate switch segment (required if using EXT-X-DISCONTINUITY-SEQUENCE) - frag = findFragmentByPTS(fragPrevious, fragments, bufferEnd, lookupTolerance); - } else { - // reach end of playlist - frag = fragments[fragments.length - 1]; - } - if (frag) { - const curSNIdx = frag.sn - levelDetails.startSN; - // Move fragPrevious forward to support forcing the next fragment to load - // when the buffer catches up to a previously buffered range. - const fragState = this.fragmentTracker.getState(frag); - if (fragState === FragmentState.OK || fragState === FragmentState.PARTIAL && frag.gap) { - fragPrevious = frag; - } - if (fragPrevious && frag.sn === fragPrevious.sn && (!loadingParts || partList[0].fragment.sn > frag.sn)) { - // Force the next fragment to load if the previous one was already selected. This can occasionally happen with - // non-uniform fragment durations - const sameLevel = fragPrevious && frag.level === fragPrevious.level; - if (sameLevel) { - const nextFrag = fragments[curSNIdx + 1]; - if (frag.sn < endSN && this.fragmentTracker.getState(nextFrag) !== FragmentState.OK) { - frag = nextFrag; - } else { - frag = null; - } - } - } - } - return frag; - } - synchronizeToLiveEdge(levelDetails) { - const { - config, - media - } = this; - if (!media) { - return; - } - const liveSyncPosition = this.hls.liveSyncPosition; - const currentTime = media.currentTime; - const start = levelDetails.fragments[0].start; - const end = levelDetails.edge; - const withinSlidingWindow = currentTime >= start - config.maxFragLookUpTolerance && currentTime <= end; - // Continue if we can seek forward to sync position or if current time is outside of sliding window - if (liveSyncPosition !== null && media.duration > liveSyncPosition && (currentTime < liveSyncPosition || !withinSlidingWindow)) { - // Continue if buffer is starving or if current time is behind max latency - const maxLatency = config.liveMaxLatencyDuration !== undefined ? config.liveMaxLatencyDuration : config.liveMaxLatencyDurationCount * levelDetails.targetduration; - if (!withinSlidingWindow && media.readyState < 4 || currentTime < end - maxLatency) { - if (!this.loadedmetadata) { - this.nextLoadPosition = liveSyncPosition; - } - // Only seek if ready and there is not a significant forward buffer available for playback - if (media.readyState) { - this.warn(`Playback: ${currentTime.toFixed(3)} is located too far from the end of live sliding playlist: ${end}, reset currentTime to : ${liveSyncPosition.toFixed(3)}`); - media.currentTime = liveSyncPosition; - } - } - } - } - alignPlaylists(details, previousDetails, switchDetails) { - // FIXME: If not for `shouldAlignOnDiscontinuities` requiring fragPrevious.cc, - // this could all go in level-helper mergeDetails() - const length = details.fragments.length; - if (!length) { - this.warn(`No fragments in live playlist`); - return 0; - } - const slidingStart = details.fragments[0].start; - const firstLevelLoad = !previousDetails; - const aligned = details.alignedSliding && isFiniteNumber(slidingStart); - if (firstLevelLoad || !aligned && !slidingStart) { - const { - fragPrevious - } = this; - alignStream(fragPrevious, switchDetails, details); - const alignedSlidingStart = details.fragments[0].start; - this.log(`Live playlist sliding: ${alignedSlidingStart.toFixed(2)} start-sn: ${previousDetails ? previousDetails.startSN : 'na'}->${details.startSN} prev-sn: ${fragPrevious ? fragPrevious.sn : 'na'} fragments: ${length}`); - return alignedSlidingStart; - } - return slidingStart; - } - waitForCdnTuneIn(details) { - // Wait for Low-Latency CDN Tune-in to get an updated playlist - const advancePartLimit = 3; - return details.live && details.canBlockReload && details.partTarget && details.tuneInGoal > Math.max(details.partHoldBack, details.partTarget * advancePartLimit); - } - setStartPosition(details, sliding) { - // compute start position if set to -1. use it straight away if value is defined - let startPosition = this.startPosition; - if (startPosition < sliding) { - startPosition = -1; - } - if (startPosition === -1 || this.lastCurrentTime === -1) { - // Use Playlist EXT-X-START:TIME-OFFSET when set - // Prioritize Multivariant Playlist offset so that main, audio, and subtitle stream-controller start times match - const offsetInMultivariantPlaylist = this.startTimeOffset !== null; - const startTimeOffset = offsetInMultivariantPlaylist ? this.startTimeOffset : details.startTimeOffset; - if (startTimeOffset !== null && isFiniteNumber(startTimeOffset)) { - startPosition = sliding + startTimeOffset; - if (startTimeOffset < 0) { - startPosition += details.totalduration; - } - startPosition = Math.min(Math.max(sliding, startPosition), sliding + details.totalduration); - this.log(`Start time offset ${startTimeOffset} found in ${offsetInMultivariantPlaylist ? 'multivariant' : 'media'} playlist, adjust startPosition to ${startPosition}`); - this.startPosition = startPosition; - } else if (details.live) { - // Leave this.startPosition at -1, so that we can use `getInitialLiveFragment` logic when startPosition has - // not been specified via the config or an as an argument to startLoad (#3736). - startPosition = this.hls.liveSyncPosition || sliding; - } else { - this.startPosition = startPosition = 0; - } - this.lastCurrentTime = startPosition; - } - this.nextLoadPosition = startPosition; - } - getLoadPosition() { - const { - media - } = this; - // if we have not yet loaded any fragment, start loading from start position - let pos = 0; - if (this.loadedmetadata && media) { - pos = media.currentTime; - } else if (this.nextLoadPosition) { - pos = this.nextLoadPosition; - } - return pos; - } - handleFragLoadAborted(frag, part) { - if (this.transmuxer && frag.sn !== 'initSegment' && frag.stats.aborted) { - this.warn(`Fragment ${frag.sn}${part ? ' part ' + part.index : ''} of level ${frag.level} was aborted`); - this.resetFragmentLoading(frag); - } - } - resetFragmentLoading(frag) { - if (!this.fragCurrent || !this.fragContextChanged(frag) && this.state !== State.FRAG_LOADING_WAITING_RETRY) { - this.state = State.IDLE; - } - } - onFragmentOrKeyLoadError(filterType, data) { - if (data.chunkMeta && !data.frag) { - const context = this.getCurrentContext(data.chunkMeta); - if (context) { - data.frag = context.frag; - } - } - const frag = data.frag; - // Handle frag error related to caller's filterType - if (!frag || frag.type !== filterType || !this.levels) { - return; - } - if (this.fragContextChanged(frag)) { - var _this$fragCurrent2; - this.warn(`Frag load error must match current frag to retry ${frag.url} > ${(_this$fragCurrent2 = this.fragCurrent) == null ? void 0 : _this$fragCurrent2.url}`); - return; - } - const gapTagEncountered = data.details === ErrorDetails.FRAG_GAP; - if (gapTagEncountered) { - this.fragmentTracker.fragBuffered(frag, true); - } - // keep retrying until the limit will be reached - const errorAction = data.errorAction; - const { - action, - retryCount = 0, - retryConfig - } = errorAction || {}; - if (errorAction && action === NetworkErrorAction.RetryRequest && retryConfig) { - this.resetStartWhenNotLoaded(this.levelLastLoaded); - const delay = getRetryDelay(retryConfig, retryCount); - this.warn(`Fragment ${frag.sn} of ${filterType} ${frag.level} errored with ${data.details}, retrying loading ${retryCount + 1}/${retryConfig.maxNumRetry} in ${delay}ms`); - errorAction.resolved = true; - this.retryDate = self.performance.now() + delay; - this.state = State.FRAG_LOADING_WAITING_RETRY; - } else if (retryConfig && errorAction) { - this.resetFragmentErrors(filterType); - if (retryCount < retryConfig.maxNumRetry) { - // Network retry is skipped when level switch is preferred - if (!gapTagEncountered && action !== NetworkErrorAction.RemoveAlternatePermanently) { - errorAction.resolved = true; - } - } else { - logger.warn(`${data.details} reached or exceeded max retry (${retryCount})`); - return; - } - } else if ((errorAction == null ? void 0 : errorAction.action) === NetworkErrorAction.SendAlternateToPenaltyBox) { - this.state = State.WAITING_LEVEL; - } else { - this.state = State.ERROR; - } - // Perform next async tick sooner to speed up error action resolution - this.tickImmediate(); - } - reduceLengthAndFlushBuffer(data) { - // if in appending state - if (this.state === State.PARSING || this.state === State.PARSED) { - const frag = data.frag; - const playlistType = data.parent; - const bufferedInfo = this.getFwdBufferInfo(this.mediaBuffer, playlistType); - // 0.5 : tolerance needed as some browsers stalls playback before reaching buffered end - // reduce max buf len if current position is buffered - const buffered = bufferedInfo && bufferedInfo.len > 0.5; - if (buffered) { - this.reduceMaxBufferLength(bufferedInfo.len, (frag == null ? void 0 : frag.duration) || 10); - } - const flushBuffer = !buffered; - if (flushBuffer) { - // current position is not buffered, but browser is still complaining about buffer full error - // this happens on IE/Edge, refer to https://github.com/video-dev/hls.js/pull/708 - // in that case flush the whole audio buffer to recover - this.warn(`Buffer full error while media.currentTime is not buffered, flush ${playlistType} buffer`); - } - if (frag) { - this.fragmentTracker.removeFragment(frag); - this.nextLoadPosition = frag.start; - } - this.resetLoadingState(); - return flushBuffer; - } - return false; - } - resetFragmentErrors(filterType) { - if (filterType === PlaylistLevelType.AUDIO) { - // Reset current fragment since audio track audio is essential and may not have a fail-over track - this.fragCurrent = null; - } - // Fragment errors that result in a level switch or redundant fail-over - // should reset the stream controller state to idle - if (!this.loadedmetadata) { - this.startFragRequested = false; - } - if (this.state !== State.STOPPED) { - this.state = State.IDLE; - } - } - afterBufferFlushed(media, bufferType, playlistType) { - if (!media) { - return; - } - // After successful buffer flushing, filter flushed fragments from bufferedFrags use mediaBuffered instead of media - // (so that we will check against video.buffered ranges in case of alt audio track) - const bufferedTimeRanges = BufferHelper.getBuffered(media); - this.fragmentTracker.detectEvictedFragments(bufferType, bufferedTimeRanges, playlistType); - if (this.state === State.ENDED) { - this.resetLoadingState(); - } - } - resetLoadingState() { - this.log('Reset loading state'); - this.fragCurrent = null; - this.fragPrevious = null; - this.state = State.IDLE; - } - resetStartWhenNotLoaded(level) { - // if loadedmetadata is not set, it means that first frag request failed - // in that case, reset startFragRequested flag - if (!this.loadedmetadata) { - this.startFragRequested = false; - const details = level ? level.details : null; - if (details != null && details.live) { - // Update the start position and return to IDLE to recover live start - this.startPosition = -1; - this.setStartPosition(details, 0); - this.resetLoadingState(); - } else { - this.nextLoadPosition = this.startPosition; - } - } - } - resetWhenMissingContext(chunkMeta) { - this.warn(`The loading context changed while buffering fragment ${chunkMeta.sn} of level ${chunkMeta.level}. This chunk will not be buffered.`); - this.removeUnbufferedFrags(); - this.resetStartWhenNotLoaded(this.levelLastLoaded); - this.resetLoadingState(); - } - removeUnbufferedFrags(start = 0) { - this.fragmentTracker.removeFragmentsInRange(start, Infinity, this.playlistType, false, true); - } - updateLevelTiming(frag, part, level, partial) { - var _this$transmuxer; - const details = level.details; - if (!details) { - this.warn('level.details undefined'); - return; - } - const parsed = Object.keys(frag.elementaryStreams).reduce((result, type) => { - const info = frag.elementaryStreams[type]; - if (info) { - const parsedDuration = info.endPTS - info.startPTS; - if (parsedDuration <= 0) { - // Destroy the transmuxer after it's next time offset failed to advance because duration was <= 0. - // The new transmuxer will be configured with a time offset matching the next fragment start, - // preventing the timeline from shifting. - this.warn(`Could not parse fragment ${frag.sn} ${type} duration reliably (${parsedDuration})`); - return result || false; - } - const drift = partial ? 0 : updateFragPTSDTS(details, frag, info.startPTS, info.endPTS, info.startDTS, info.endDTS); - this.hls.trigger(Events.LEVEL_PTS_UPDATED, { - details, - level, - drift, - type, - frag, - start: info.startPTS, - end: info.endPTS - }); - return true; - } - return result; - }, false); - if (!parsed && ((_this$transmuxer = this.transmuxer) == null ? void 0 : _this$transmuxer.error) === null) { - const error = new Error(`Found no media in fragment ${frag.sn} of level ${frag.level} resetting transmuxer to fallback to playlist timing`); - if (level.fragmentError === 0) { - // Mark and track the odd empty segment as a gap to avoid reloading - level.fragmentError++; - frag.gap = true; - this.fragmentTracker.removeFragment(frag); - this.fragmentTracker.fragBuffered(frag, true); - } - this.warn(error.message); - this.hls.trigger(Events.ERROR, { - type: ErrorTypes.MEDIA_ERROR, - details: ErrorDetails.FRAG_PARSING_ERROR, - fatal: false, - error, - frag, - reason: `Found no media in msn ${frag.sn} of level "${level.url}"` - }); - if (!this.hls) { - return; - } - this.resetTransmuxer(); - // For this error fallthrough. Marking parsed will allow advancing to next fragment. - } - this.state = State.PARSED; - this.hls.trigger(Events.FRAG_PARSED, { - frag, - part - }); - } - resetTransmuxer() { - if (this.transmuxer) { - this.transmuxer.destroy(); - this.transmuxer = null; - } - } - recoverWorkerError(data) { - if (data.event === 'demuxerWorker') { - this.fragmentTracker.removeAllFragments(); - this.resetTransmuxer(); - this.resetStartWhenNotLoaded(this.levelLastLoaded); - this.resetLoadingState(); - } - } - set state(nextState) { - const previousState = this._state; - if (previousState !== nextState) { - this._state = nextState; - this.log(`${previousState}->${nextState}`); - } - } - get state() { - return this._state; - } -} - -class ChunkCache { - constructor() { - this.chunks = []; - this.dataLength = 0; - } - push(chunk) { - this.chunks.push(chunk); - this.dataLength += chunk.length; - } - flush() { - const { - chunks, - dataLength - } = this; - let result; - if (!chunks.length) { - return new Uint8Array(0); - } else if (chunks.length === 1) { - result = chunks[0]; - } else { - result = concatUint8Arrays(chunks, dataLength); - } - this.reset(); - return result; - } - reset() { - this.chunks.length = 0; - this.dataLength = 0; - } -} -function concatUint8Arrays(chunks, dataLength) { - const result = new Uint8Array(dataLength); - let offset = 0; - for (let i = 0; i < chunks.length; i++) { - const chunk = chunks[i]; - result.set(chunk, offset); - offset += chunk.length; - } - return result; -} - -// ensure the worker ends up in the bundle -// If the worker should not be included this gets aliased to empty.js -function hasUMDWorker() { - return typeof __HLS_WORKER_BUNDLE__ === 'function'; -} -function injectWorker() { - const blob = new self.Blob([`var exports={};var module={exports:exports};function define(f){f()};define.amd=true;(${__HLS_WORKER_BUNDLE__.toString()})(true);`], { - type: 'text/javascript' - }); - const objectURL = self.URL.createObjectURL(blob); - const worker = new self.Worker(objectURL); - return { - worker, - objectURL - }; -} -function loadWorker(path) { - const scriptURL = new self.URL(path, self.location.href).href; - const worker = new self.Worker(scriptURL); - return { - worker, - scriptURL - }; -} - -function dummyTrack(type = '', inputTimeScale = 90000) { - return { - type, - id: -1, - pid: -1, - inputTimeScale, - sequenceNumber: -1, - samples: [], - dropped: 0 - }; -} - -class BaseAudioDemuxer { - constructor() { - this._audioTrack = void 0; - this._id3Track = void 0; - this.frameIndex = 0; - this.cachedData = null; - this.basePTS = null; - this.initPTS = null; - this.lastPTS = null; - } - resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) { - this._id3Track = { - type: 'id3', - id: 3, - pid: -1, - inputTimeScale: 90000, - sequenceNumber: 0, - samples: [], - dropped: 0 - }; - } - resetTimeStamp(deaultTimestamp) { - this.initPTS = deaultTimestamp; - this.resetContiguity(); - } - resetContiguity() { - this.basePTS = null; - this.lastPTS = null; - this.frameIndex = 0; - } - canParse(data, offset) { - return false; - } - appendFrame(track, data, offset) {} - - // feed incoming data to the front of the parsing pipeline - demux(data, timeOffset) { - if (this.cachedData) { - data = appendUint8Array(this.cachedData, data); - this.cachedData = null; - } - let id3Data = getID3Data(data, 0); - let offset = id3Data ? id3Data.length : 0; - let lastDataIndex; - const track = this._audioTrack; - const id3Track = this._id3Track; - const timestamp = id3Data ? getTimeStamp(id3Data) : undefined; - const length = data.length; - if (this.basePTS === null || this.frameIndex === 0 && isFiniteNumber(timestamp)) { - this.basePTS = initPTSFn(timestamp, timeOffset, this.initPTS); - this.lastPTS = this.basePTS; - } - if (this.lastPTS === null) { - this.lastPTS = this.basePTS; - } - - // more expressive than alternative: id3Data?.length - if (id3Data && id3Data.length > 0) { - id3Track.samples.push({ - pts: this.lastPTS, - dts: this.lastPTS, - data: id3Data, - type: MetadataSchema.audioId3, - duration: Number.POSITIVE_INFINITY - }); - } - while (offset < length) { - if (this.canParse(data, offset)) { - const frame = this.appendFrame(track, data, offset); - if (frame) { - this.frameIndex++; - this.lastPTS = frame.sample.pts; - offset += frame.length; - lastDataIndex = offset; - } else { - offset = length; - } - } else if (canParse$2(data, offset)) { - // after a ID3.canParse, a call to ID3.getID3Data *should* always returns some data - id3Data = getID3Data(data, offset); - id3Track.samples.push({ - pts: this.lastPTS, - dts: this.lastPTS, - data: id3Data, - type: MetadataSchema.audioId3, - duration: Number.POSITIVE_INFINITY - }); - offset += id3Data.length; - lastDataIndex = offset; - } else { - offset++; - } - if (offset === length && lastDataIndex !== length) { - const partialData = sliceUint8(data, lastDataIndex); - if (this.cachedData) { - this.cachedData = appendUint8Array(this.cachedData, partialData); - } else { - this.cachedData = partialData; - } - } - } - return { - audioTrack: track, - videoTrack: dummyTrack(), - id3Track, - textTrack: dummyTrack() - }; - } - demuxSampleAes(data, keyData, timeOffset) { - return Promise.reject(new Error(`[${this}] This demuxer does not support Sample-AES decryption`)); - } - flush(timeOffset) { - // Parse cache in case of remaining frames. - const cachedData = this.cachedData; - if (cachedData) { - this.cachedData = null; - this.demux(cachedData, 0); - } - return { - audioTrack: this._audioTrack, - videoTrack: dummyTrack(), - id3Track: this._id3Track, - textTrack: dummyTrack() - }; - } - destroy() {} -} - -/** - * Initialize PTS - * <p> - * use timestamp unless it is undefined, NaN or Infinity - * </p> - */ -const initPTSFn = (timestamp, timeOffset, initPTS) => { - if (isFiniteNumber(timestamp)) { - return timestamp * 90; - } - const init90kHz = initPTS ? initPTS.baseTime * 90000 / initPTS.timescale : 0; - return timeOffset * 90000 + init90kHz; -}; - -/** - * ADTS parser helper - * @link https://wiki.multimedia.cx/index.php?title=ADTS - */ -function getAudioConfig(observer, data, offset, audioCodec) { - let adtsObjectType; - let adtsExtensionSamplingIndex; - let adtsChannelConfig; - let config; - const userAgent = navigator.userAgent.toLowerCase(); - const manifestCodec = audioCodec; - const adtsSamplingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350]; - // byte 2 - adtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1; - const adtsSamplingIndex = (data[offset + 2] & 0x3c) >>> 2; - if (adtsSamplingIndex > adtsSamplingRates.length - 1) { - const error = new Error(`invalid ADTS sampling index:${adtsSamplingIndex}`); - observer.emit(Events.ERROR, Events.ERROR, { - type: ErrorTypes.MEDIA_ERROR, - details: ErrorDetails.FRAG_PARSING_ERROR, - fatal: true, - error, - reason: error.message - }); - return; - } - adtsChannelConfig = (data[offset + 2] & 0x01) << 2; - // byte 3 - adtsChannelConfig |= (data[offset + 3] & 0xc0) >>> 6; - logger.log(`manifest codec:${audioCodec}, ADTS type:${adtsObjectType}, samplingIndex:${adtsSamplingIndex}`); - // firefox: freq less than 24kHz = AAC SBR (HE-AAC) - if (/firefox/i.test(userAgent)) { - if (adtsSamplingIndex >= 6) { - adtsObjectType = 5; - config = new Array(4); - // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies - // there is a factor 2 between frame sample rate and output sample rate - // multiply frequency by 2 (see table below, equivalent to substract 3) - adtsExtensionSamplingIndex = adtsSamplingIndex - 3; - } else { - adtsObjectType = 2; - config = new Array(2); - adtsExtensionSamplingIndex = adtsSamplingIndex; - } - // Android : always use AAC - } else if (userAgent.indexOf('android') !== -1) { - adtsObjectType = 2; - config = new Array(2); - adtsExtensionSamplingIndex = adtsSamplingIndex; - } else { - /* for other browsers (Chrome/Vivaldi/Opera ...) - always force audio type to be HE-AAC SBR, as some browsers do not support audio codec switch properly (like Chrome ...) - */ - adtsObjectType = 5; - config = new Array(4); - // if (manifest codec is HE-AAC or HE-AACv2) OR (manifest codec not specified AND frequency less than 24kHz) - if (audioCodec && (audioCodec.indexOf('mp4a.40.29') !== -1 || audioCodec.indexOf('mp4a.40.5') !== -1) || !audioCodec && adtsSamplingIndex >= 6) { - // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies - // there is a factor 2 between frame sample rate and output sample rate - // multiply frequency by 2 (see table below, equivalent to substract 3) - adtsExtensionSamplingIndex = adtsSamplingIndex - 3; - } else { - // if (manifest codec is AAC) AND (frequency less than 24kHz AND nb channel is 1) OR (manifest codec not specified and mono audio) - // Chrome fails to play back with low frequency AAC LC mono when initialized with HE-AAC. This is not a problem with stereo. - if (audioCodec && audioCodec.indexOf('mp4a.40.2') !== -1 && (adtsSamplingIndex >= 6 && adtsChannelConfig === 1 || /vivaldi/i.test(userAgent)) || !audioCodec && adtsChannelConfig === 1) { - adtsObjectType = 2; - config = new Array(2); - } - adtsExtensionSamplingIndex = adtsSamplingIndex; - } - } - /* refer to http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config - ISO 14496-3 (AAC).pdf - Table 1.13 — Syntax of AudioSpecificConfig() - Audio Profile / Audio Object Type - 0: Null - 1: AAC Main - 2: AAC LC (Low Complexity) - 3: AAC SSR (Scalable Sample Rate) - 4: AAC LTP (Long Term Prediction) - 5: SBR (Spectral Band Replication) - 6: AAC Scalable - sampling freq - 0: 96000 Hz - 1: 88200 Hz - 2: 64000 Hz - 3: 48000 Hz - 4: 44100 Hz - 5: 32000 Hz - 6: 24000 Hz - 7: 22050 Hz - 8: 16000 Hz - 9: 12000 Hz - 10: 11025 Hz - 11: 8000 Hz - 12: 7350 Hz - 13: Reserved - 14: Reserved - 15: frequency is written explictly - Channel Configurations - These are the channel configurations: - 0: Defined in AOT Specifc Config - 1: 1 channel: front-center - 2: 2 channels: front-left, front-right - */ - // audioObjectType = profile => profile, the MPEG-4 Audio Object Type minus 1 - config[0] = adtsObjectType << 3; - // samplingFrequencyIndex - config[0] |= (adtsSamplingIndex & 0x0e) >> 1; - config[1] |= (adtsSamplingIndex & 0x01) << 7; - // channelConfiguration - config[1] |= adtsChannelConfig << 3; - if (adtsObjectType === 5) { - // adtsExtensionSamplingIndex - config[1] |= (adtsExtensionSamplingIndex & 0x0e) >> 1; - config[2] = (adtsExtensionSamplingIndex & 0x01) << 7; - // adtsObjectType (force to 2, chrome is checking that object type is less than 5 ??? - // https://chromium.googlesource.com/chromium/src.git/+/master/media/formats/mp4/aac.cc - config[2] |= 2 << 2; - config[3] = 0; - } - return { - config, - samplerate: adtsSamplingRates[adtsSamplingIndex], - channelCount: adtsChannelConfig, - codec: 'mp4a.40.' + adtsObjectType, - manifestCodec - }; -} -function isHeaderPattern$1(data, offset) { - return data[offset] === 0xff && (data[offset + 1] & 0xf6) === 0xf0; -} -function getHeaderLength(data, offset) { - return data[offset + 1] & 0x01 ? 7 : 9; -} -function getFullFrameLength(data, offset) { - return (data[offset + 3] & 0x03) << 11 | data[offset + 4] << 3 | (data[offset + 5] & 0xe0) >>> 5; -} -function canGetFrameLength(data, offset) { - return offset + 5 < data.length; -} -function isHeader$1(data, offset) { - // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1 - // Layer bits (position 14 and 15) in header should be always 0 for ADTS - // More info https://wiki.multimedia.cx/index.php?title=ADTS - return offset + 1 < data.length && isHeaderPattern$1(data, offset); -} -function canParse$1(data, offset) { - return canGetFrameLength(data, offset) && isHeaderPattern$1(data, offset) && getFullFrameLength(data, offset) <= data.length - offset; -} -function probe$1(data, offset) { - // same as isHeader but we also check that ADTS frame follows last ADTS frame - // or end of data is reached - if (isHeader$1(data, offset)) { - // ADTS header Length - const headerLength = getHeaderLength(data, offset); - if (offset + headerLength >= data.length) { - return false; - } - // ADTS frame Length - const frameLength = getFullFrameLength(data, offset); - if (frameLength <= headerLength) { - return false; - } - const newOffset = offset + frameLength; - return newOffset === data.length || isHeader$1(data, newOffset); - } - return false; -} -function initTrackConfig(track, observer, data, offset, audioCodec) { - if (!track.samplerate) { - const config = getAudioConfig(observer, data, offset, audioCodec); - if (!config) { - return; - } - track.config = config.config; - track.samplerate = config.samplerate; - track.channelCount = config.channelCount; - track.codec = config.codec; - track.manifestCodec = config.manifestCodec; - logger.log(`parsed codec:${track.codec}, rate:${config.samplerate}, channels:${config.channelCount}`); - } -} -function getFrameDuration(samplerate) { - return 1024 * 90000 / samplerate; -} -function parseFrameHeader(data, offset) { - // The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header - const headerLength = getHeaderLength(data, offset); - if (offset + headerLength <= data.length) { - // retrieve frame size - const frameLength = getFullFrameLength(data, offset) - headerLength; - if (frameLength > 0) { - // logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}`); - return { - headerLength, - frameLength - }; - } - } -} -function appendFrame$2(track, data, offset, pts, frameIndex) { - const frameDuration = getFrameDuration(track.samplerate); - const stamp = pts + frameIndex * frameDuration; - const header = parseFrameHeader(data, offset); - let unit; - if (header) { - const { - frameLength, - headerLength - } = header; - const _length = headerLength + frameLength; - const missing = Math.max(0, offset + _length - data.length); - // logger.log(`AAC frame ${frameIndex}, pts:${stamp} length@offset/total: ${frameLength}@${offset+headerLength}/${data.byteLength} missing: ${missing}`); - if (missing) { - unit = new Uint8Array(_length - headerLength); - unit.set(data.subarray(offset + headerLength, data.length), 0); - } else { - unit = data.subarray(offset + headerLength, offset + _length); - } - const _sample = { - unit, - pts: stamp - }; - if (!missing) { - track.samples.push(_sample); - } - return { - sample: _sample, - length: _length, - missing - }; - } - // overflow incomplete header - const length = data.length - offset; - unit = new Uint8Array(length); - unit.set(data.subarray(offset, data.length), 0); - const sample = { - unit, - pts: stamp - }; - return { - sample, - length, - missing: -1 - }; -} - -/** - * MPEG parser helper - */ - -let chromeVersion$1 = null; -const BitratesMap = [32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160]; -const SamplingRateMap = [44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000]; -const SamplesCoefficients = [ -// MPEG 2.5 -[0, -// Reserved -72, -// Layer3 -144, -// Layer2 -12 // Layer1 -], -// Reserved -[0, -// Reserved -0, -// Layer3 -0, -// Layer2 -0 // Layer1 -], -// MPEG 2 -[0, -// Reserved -72, -// Layer3 -144, -// Layer2 -12 // Layer1 -], -// MPEG 1 -[0, -// Reserved -144, -// Layer3 -144, -// Layer2 -12 // Layer1 -]]; -const BytesInSlot = [0, -// Reserved -1, -// Layer3 -1, -// Layer2 -4 // Layer1 -]; -function appendFrame$1(track, data, offset, pts, frameIndex) { - // Using http://www.datavoyage.com/mpgscript/mpeghdr.htm as a reference - if (offset + 24 > data.length) { - return; - } - const header = parseHeader(data, offset); - if (header && offset + header.frameLength <= data.length) { - const frameDuration = header.samplesPerFrame * 90000 / header.sampleRate; - const stamp = pts + frameIndex * frameDuration; - const sample = { - unit: data.subarray(offset, offset + header.frameLength), - pts: stamp, - dts: stamp - }; - track.config = []; - track.channelCount = header.channelCount; - track.samplerate = header.sampleRate; - track.samples.push(sample); - return { - sample, - length: header.frameLength, - missing: 0 - }; - } -} -function parseHeader(data, offset) { - const mpegVersion = data[offset + 1] >> 3 & 3; - const mpegLayer = data[offset + 1] >> 1 & 3; - const bitRateIndex = data[offset + 2] >> 4 & 15; - const sampleRateIndex = data[offset + 2] >> 2 & 3; - if (mpegVersion !== 1 && bitRateIndex !== 0 && bitRateIndex !== 15 && sampleRateIndex !== 3) { - const paddingBit = data[offset + 2] >> 1 & 1; - const channelMode = data[offset + 3] >> 6; - const columnInBitrates = mpegVersion === 3 ? 3 - mpegLayer : mpegLayer === 3 ? 3 : 4; - const bitRate = BitratesMap[columnInBitrates * 14 + bitRateIndex - 1] * 1000; - const columnInSampleRates = mpegVersion === 3 ? 0 : mpegVersion === 2 ? 1 : 2; - const sampleRate = SamplingRateMap[columnInSampleRates * 3 + sampleRateIndex]; - const channelCount = channelMode === 3 ? 1 : 2; // If bits of channel mode are `11` then it is a single channel (Mono) - const sampleCoefficient = SamplesCoefficients[mpegVersion][mpegLayer]; - const bytesInSlot = BytesInSlot[mpegLayer]; - const samplesPerFrame = sampleCoefficient * 8 * bytesInSlot; - const frameLength = Math.floor(sampleCoefficient * bitRate / sampleRate + paddingBit) * bytesInSlot; - if (chromeVersion$1 === null) { - const userAgent = navigator.userAgent || ''; - const result = userAgent.match(/Chrome\/(\d+)/i); - chromeVersion$1 = result ? parseInt(result[1]) : 0; - } - const needChromeFix = !!chromeVersion$1 && chromeVersion$1 <= 87; - if (needChromeFix && mpegLayer === 2 && bitRate >= 224000 && channelMode === 0) { - // Work around bug in Chromium by setting channelMode to dual-channel (01) instead of stereo (00) - data[offset + 3] = data[offset + 3] | 0x80; - } - return { - sampleRate, - channelCount, - frameLength, - samplesPerFrame - }; - } -} -function isHeaderPattern(data, offset) { - return data[offset] === 0xff && (data[offset + 1] & 0xe0) === 0xe0 && (data[offset + 1] & 0x06) !== 0x00; -} -function isHeader(data, offset) { - // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1 - // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III) - // More info http://www.mp3-tech.org/programmer/frame_header.html - return offset + 1 < data.length && isHeaderPattern(data, offset); -} -function canParse(data, offset) { - const headerSize = 4; - return isHeaderPattern(data, offset) && headerSize <= data.length - offset; -} -function probe(data, offset) { - // same as isHeader but we also check that MPEG frame follows last MPEG frame - // or end of data is reached - if (offset + 1 < data.length && isHeaderPattern(data, offset)) { - // MPEG header Length - const headerLength = 4; - // MPEG frame Length - const header = parseHeader(data, offset); - let frameLength = headerLength; - if (header != null && header.frameLength) { - frameLength = header.frameLength; - } - const newOffset = offset + frameLength; - return newOffset === data.length || isHeader(data, newOffset); - } - return false; -} - -/** - * AAC demuxer - */ -class AACDemuxer extends BaseAudioDemuxer { - constructor(observer, config) { - super(); - this.observer = void 0; - this.config = void 0; - this.observer = observer; - this.config = config; - } - resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) { - super.resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration); - this._audioTrack = { - container: 'audio/adts', - type: 'audio', - id: 2, - pid: -1, - sequenceNumber: 0, - segmentCodec: 'aac', - samples: [], - manifestCodec: audioCodec, - duration: trackDuration, - inputTimeScale: 90000, - dropped: 0 - }; - } - - // Source for probe info - https://wiki.multimedia.cx/index.php?title=ADTS - static probe(data) { - if (!data) { - return false; - } - - // Check for the ADTS sync word - // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1 - // Layer bits (position 14 and 15) in header should be always 0 for ADTS - // More info https://wiki.multimedia.cx/index.php?title=ADTS - const id3Data = getID3Data(data, 0); - let offset = (id3Data == null ? void 0 : id3Data.length) || 0; - if (probe(data, offset)) { - return false; - } - for (let length = data.length; offset < length; offset++) { - if (probe$1(data, offset)) { - logger.log('ADTS sync word found !'); - return true; - } - } - return false; - } - canParse(data, offset) { - return canParse$1(data, offset); - } - appendFrame(track, data, offset) { - initTrackConfig(track, this.observer, data, offset, track.manifestCodec); - const frame = appendFrame$2(track, data, offset, this.basePTS, this.frameIndex); - if (frame && frame.missing === 0) { - return frame; - } - } -} - -const emsgSchemePattern = /\/emsg[-/]ID3/i; -class MP4Demuxer { - constructor(observer, config) { - this.remainderData = null; - this.timeOffset = 0; - this.config = void 0; - this.videoTrack = void 0; - this.audioTrack = void 0; - this.id3Track = void 0; - this.txtTrack = void 0; - this.config = config; - } - resetTimeStamp() {} - resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) { - const videoTrack = this.videoTrack = dummyTrack('video', 1); - const audioTrack = this.audioTrack = dummyTrack('audio', 1); - const captionTrack = this.txtTrack = dummyTrack('text', 1); - this.id3Track = dummyTrack('id3', 1); - this.timeOffset = 0; - if (!(initSegment != null && initSegment.byteLength)) { - return; - } - const initData = parseInitSegment(initSegment); - if (initData.video) { - const { - id, - timescale, - codec - } = initData.video; - videoTrack.id = id; - videoTrack.timescale = captionTrack.timescale = timescale; - videoTrack.codec = codec; - } - if (initData.audio) { - const { - id, - timescale, - codec - } = initData.audio; - audioTrack.id = id; - audioTrack.timescale = timescale; - audioTrack.codec = codec; - } - captionTrack.id = RemuxerTrackIdConfig.text; - videoTrack.sampleDuration = 0; - videoTrack.duration = audioTrack.duration = trackDuration; - } - resetContiguity() { - this.remainderData = null; - } - static probe(data) { - return hasMoofData(data); - } - demux(data, timeOffset) { - this.timeOffset = timeOffset; - // Load all data into the avc track. The CMAF remuxer will look for the data in the samples object; the rest of the fields do not matter - let videoSamples = data; - const videoTrack = this.videoTrack; - const textTrack = this.txtTrack; - if (this.config.progressive) { - // Split the bytestream into two ranges: one encompassing all data up until the start of the last moof, and everything else. - // This is done to guarantee that we're sending valid data to MSE - when demuxing progressively, we have no guarantee - // that the fetch loader gives us flush moof+mdat pairs. If we push jagged data to MSE, it will throw an exception. - if (this.remainderData) { - videoSamples = appendUint8Array(this.remainderData, data); - } - const segmentedData = segmentValidRange(videoSamples); - this.remainderData = segmentedData.remainder; - videoTrack.samples = segmentedData.valid || new Uint8Array(); - } else { - videoTrack.samples = videoSamples; - } - const id3Track = this.extractID3Track(videoTrack, timeOffset); - textTrack.samples = parseSamples(timeOffset, videoTrack); - return { - videoTrack, - audioTrack: this.audioTrack, - id3Track, - textTrack: this.txtTrack - }; - } - flush() { - const timeOffset = this.timeOffset; - const videoTrack = this.videoTrack; - const textTrack = this.txtTrack; - videoTrack.samples = this.remainderData || new Uint8Array(); - this.remainderData = null; - const id3Track = this.extractID3Track(videoTrack, this.timeOffset); - textTrack.samples = parseSamples(timeOffset, videoTrack); - return { - videoTrack, - audioTrack: dummyTrack(), - id3Track, - textTrack: dummyTrack() - }; - } - extractID3Track(videoTrack, timeOffset) { - const id3Track = this.id3Track; - if (videoTrack.samples.length) { - const emsgs = findBox(videoTrack.samples, ['emsg']); - if (emsgs) { - emsgs.forEach(data => { - const emsgInfo = parseEmsg(data); - if (emsgSchemePattern.test(emsgInfo.schemeIdUri)) { - const pts = isFiniteNumber(emsgInfo.presentationTime) ? emsgInfo.presentationTime / emsgInfo.timeScale : timeOffset + emsgInfo.presentationTimeDelta / emsgInfo.timeScale; - let duration = emsgInfo.eventDuration === 0xffffffff ? Number.POSITIVE_INFINITY : emsgInfo.eventDuration / emsgInfo.timeScale; - // Safari takes anything <= 0.001 seconds and maps it to Infinity - if (duration <= 0.001) { - duration = Number.POSITIVE_INFINITY; - } - const payload = emsgInfo.payload; - id3Track.samples.push({ - data: payload, - len: payload.byteLength, - dts: pts, - pts: pts, - type: MetadataSchema.emsg, - duration: duration - }); - } - }); - } - } - return id3Track; - } - demuxSampleAes(data, keyData, timeOffset) { - return Promise.reject(new Error('The MP4 demuxer does not support SAMPLE-AES decryption')); - } - destroy() {} -} - -const getAudioBSID = (data, offset) => { - // check the bsid to confirm ac-3 | ec-3 - let bsid = 0; - let numBits = 5; - offset += numBits; - const temp = new Uint32Array(1); // unsigned 32 bit for temporary storage - const mask = new Uint32Array(1); // unsigned 32 bit mask value - const byte = new Uint8Array(1); // unsigned 8 bit for temporary storage - while (numBits > 0) { - byte[0] = data[offset]; - // read remaining bits, upto 8 bits at a time - const bits = Math.min(numBits, 8); - const shift = 8 - bits; - mask[0] = 0xff000000 >>> 24 + shift << shift; - temp[0] = (byte[0] & mask[0]) >> shift; - bsid = !bsid ? temp[0] : bsid << bits | temp[0]; - offset += 1; - numBits -= bits; - } - return bsid; -}; - -class AC3Demuxer extends BaseAudioDemuxer { - constructor(observer) { - super(); - this.observer = void 0; - this.observer = observer; - } - resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) { - super.resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration); - this._audioTrack = { - container: 'audio/ac-3', - type: 'audio', - id: 2, - pid: -1, - sequenceNumber: 0, - segmentCodec: 'ac3', - samples: [], - manifestCodec: audioCodec, - duration: trackDuration, - inputTimeScale: 90000, - dropped: 0 - }; - } - canParse(data, offset) { - return offset + 64 < data.length; - } - appendFrame(track, data, offset) { - const frameLength = appendFrame(track, data, offset, this.basePTS, this.frameIndex); - if (frameLength !== -1) { - const sample = track.samples[track.samples.length - 1]; - return { - sample, - length: frameLength, - missing: 0 - }; - } - } - static probe(data) { - if (!data) { - return false; - } - const id3Data = getID3Data(data, 0); - if (!id3Data) { - return false; - } - - // look for the ac-3 sync bytes - const offset = id3Data.length; - if (data[offset] === 0x0b && data[offset + 1] === 0x77 && getTimeStamp(id3Data) !== undefined && - // check the bsid to confirm ac-3 - getAudioBSID(data, offset) < 16) { - return true; - } - return false; - } -} -function appendFrame(track, data, start, pts, frameIndex) { - if (start + 8 > data.length) { - return -1; // not enough bytes left - } - if (data[start] !== 0x0b || data[start + 1] !== 0x77) { - return -1; // invalid magic - } - - // get sample rate - const samplingRateCode = data[start + 4] >> 6; - if (samplingRateCode >= 3) { - return -1; // invalid sampling rate - } - const samplingRateMap = [48000, 44100, 32000]; - const sampleRate = samplingRateMap[samplingRateCode]; - - // get frame size - const frameSizeCode = data[start + 4] & 0x3f; - const frameSizeMap = [64, 69, 96, 64, 70, 96, 80, 87, 120, 80, 88, 120, 96, 104, 144, 96, 105, 144, 112, 121, 168, 112, 122, 168, 128, 139, 192, 128, 140, 192, 160, 174, 240, 160, 175, 240, 192, 208, 288, 192, 209, 288, 224, 243, 336, 224, 244, 336, 256, 278, 384, 256, 279, 384, 320, 348, 480, 320, 349, 480, 384, 417, 576, 384, 418, 576, 448, 487, 672, 448, 488, 672, 512, 557, 768, 512, 558, 768, 640, 696, 960, 640, 697, 960, 768, 835, 1152, 768, 836, 1152, 896, 975, 1344, 896, 976, 1344, 1024, 1114, 1536, 1024, 1115, 1536, 1152, 1253, 1728, 1152, 1254, 1728, 1280, 1393, 1920, 1280, 1394, 1920]; - const frameLength = frameSizeMap[frameSizeCode * 3 + samplingRateCode] * 2; - if (start + frameLength > data.length) { - return -1; - } - - // get channel count - const channelMode = data[start + 6] >> 5; - let skipCount = 0; - if (channelMode === 2) { - skipCount += 2; - } else { - if (channelMode & 1 && channelMode !== 1) { - skipCount += 2; - } - if (channelMode & 4) { - skipCount += 2; - } - } - const lfeon = (data[start + 6] << 8 | data[start + 7]) >> 12 - skipCount & 1; - const channelsMap = [2, 1, 2, 3, 3, 4, 4, 5]; - const channelCount = channelsMap[channelMode] + lfeon; - - // build dac3 box - const bsid = data[start + 5] >> 3; - const bsmod = data[start + 5] & 7; - const config = new Uint8Array([samplingRateCode << 6 | bsid << 1 | bsmod >> 2, (bsmod & 3) << 6 | channelMode << 3 | lfeon << 2 | frameSizeCode >> 4, frameSizeCode << 4 & 0xe0]); - const frameDuration = 1536 / sampleRate * 90000; - const stamp = pts + frameIndex * frameDuration; - const unit = data.subarray(start, start + frameLength); - track.config = config; - track.channelCount = channelCount; - track.samplerate = sampleRate; - track.samples.push({ - unit, - pts: stamp - }); - return frameLength; -} - -class BaseVideoParser { - constructor() { - this.VideoSample = null; - } - createVideoSample(key, pts, dts, debug) { - return { - key, - frame: false, - pts, - dts, - units: [], - debug, - length: 0 - }; - } - getLastNalUnit(samples) { - var _VideoSample; - let VideoSample = this.VideoSample; - let lastUnit; - // try to fallback to previous sample if current one is empty - if (!VideoSample || VideoSample.units.length === 0) { - VideoSample = samples[samples.length - 1]; - } - if ((_VideoSample = VideoSample) != null && _VideoSample.units) { - const units = VideoSample.units; - lastUnit = units[units.length - 1]; - } - return lastUnit; - } - pushAccessUnit(VideoSample, videoTrack) { - if (VideoSample.units.length && VideoSample.frame) { - // if sample does not have PTS/DTS, patch with last sample PTS/DTS - if (VideoSample.pts === undefined) { - const samples = videoTrack.samples; - const nbSamples = samples.length; - if (nbSamples) { - const lastSample = samples[nbSamples - 1]; - VideoSample.pts = lastSample.pts; - VideoSample.dts = lastSample.dts; - } else { - // dropping samples, no timestamp found - videoTrack.dropped++; - return; - } - } - videoTrack.samples.push(VideoSample); - } - if (VideoSample.debug.length) { - logger.log(VideoSample.pts + '/' + VideoSample.dts + ':' + VideoSample.debug); - } - } -} - -/** - * Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264. - */ - -class ExpGolomb { - constructor(data) { - this.data = void 0; - this.bytesAvailable = void 0; - this.word = void 0; - this.bitsAvailable = void 0; - this.data = data; - // the number of bytes left to examine in this.data - this.bytesAvailable = data.byteLength; - // the current word being examined - this.word = 0; // :uint - // the number of bits left to examine in the current word - this.bitsAvailable = 0; // :uint - } - - // ():void - loadWord() { - const data = this.data; - const bytesAvailable = this.bytesAvailable; - const position = data.byteLength - bytesAvailable; - const workingBytes = new Uint8Array(4); - const availableBytes = Math.min(4, bytesAvailable); - if (availableBytes === 0) { - throw new Error('no bytes available'); - } - workingBytes.set(data.subarray(position, position + availableBytes)); - this.word = new DataView(workingBytes.buffer).getUint32(0); - // track the amount of this.data that has been processed - this.bitsAvailable = availableBytes * 8; - this.bytesAvailable -= availableBytes; - } - - // (count:int):void - skipBits(count) { - let skipBytes; // :int - count = Math.min(count, this.bytesAvailable * 8 + this.bitsAvailable); - if (this.bitsAvailable > count) { - this.word <<= count; - this.bitsAvailable -= count; - } else { - count -= this.bitsAvailable; - skipBytes = count >> 3; - count -= skipBytes << 3; - this.bytesAvailable -= skipBytes; - this.loadWord(); - this.word <<= count; - this.bitsAvailable -= count; - } - } - - // (size:int):uint - readBits(size) { - let bits = Math.min(this.bitsAvailable, size); // :uint - const valu = this.word >>> 32 - bits; // :uint - if (size > 32) { - logger.error('Cannot read more than 32 bits at a time'); - } - this.bitsAvailable -= bits; - if (this.bitsAvailable > 0) { - this.word <<= bits; - } else if (this.bytesAvailable > 0) { - this.loadWord(); - } else { - throw new Error('no bits available'); - } - bits = size - bits; - if (bits > 0 && this.bitsAvailable) { - return valu << bits | this.readBits(bits); - } else { - return valu; - } - } - - // ():uint - skipLZ() { - let leadingZeroCount; // :uint - for (leadingZeroCount = 0; leadingZeroCount < this.bitsAvailable; ++leadingZeroCount) { - if ((this.word & 0x80000000 >>> leadingZeroCount) !== 0) { - // the first bit of working word is 1 - this.word <<= leadingZeroCount; - this.bitsAvailable -= leadingZeroCount; - return leadingZeroCount; - } - } - // we exhausted word and still have not found a 1 - this.loadWord(); - return leadingZeroCount + this.skipLZ(); - } - - // ():void - skipUEG() { - this.skipBits(1 + this.skipLZ()); - } - - // ():void - skipEG() { - this.skipBits(1 + this.skipLZ()); - } - - // ():uint - readUEG() { - const clz = this.skipLZ(); // :uint - return this.readBits(clz + 1) - 1; - } - - // ():int - readEG() { - const valu = this.readUEG(); // :int - if (0x01 & valu) { - // the number is odd if the low order bit is set - return 1 + valu >>> 1; // add 1 to make it even, and divide by 2 - } else { - return -1 * (valu >>> 1); // divide by two then make it negative - } - } - - // Some convenience functions - // :Boolean - readBoolean() { - return this.readBits(1) === 1; - } - - // ():int - readUByte() { - return this.readBits(8); - } - - // ():int - readUShort() { - return this.readBits(16); - } - - // ():int - readUInt() { - return this.readBits(32); - } - - /** - * Advance the ExpGolomb decoder past a scaling list. The scaling - * list is optionally transmitted as part of a sequence parameter - * set and is not relevant to transmuxing. - * @param count the number of entries in this scaling list - * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1 - */ - skipScalingList(count) { - let lastScale = 8; - let nextScale = 8; - let deltaScale; - for (let j = 0; j < count; j++) { - if (nextScale !== 0) { - deltaScale = this.readEG(); - nextScale = (lastScale + deltaScale + 256) % 256; - } - lastScale = nextScale === 0 ? lastScale : nextScale; - } - } - - /** - * Read a sequence parameter set and return some interesting video - * properties. A sequence parameter set is the H264 metadata that - * describes the properties of upcoming video frames. - * @returns an object with configuration parsed from the - * sequence parameter set, including the dimensions of the - * associated video frames. - */ - readSPS() { - let frameCropLeftOffset = 0; - let frameCropRightOffset = 0; - let frameCropTopOffset = 0; - let frameCropBottomOffset = 0; - let numRefFramesInPicOrderCntCycle; - let scalingListCount; - let i; - const readUByte = this.readUByte.bind(this); - const readBits = this.readBits.bind(this); - const readUEG = this.readUEG.bind(this); - const readBoolean = this.readBoolean.bind(this); - const skipBits = this.skipBits.bind(this); - const skipEG = this.skipEG.bind(this); - const skipUEG = this.skipUEG.bind(this); - const skipScalingList = this.skipScalingList.bind(this); - readUByte(); - const profileIdc = readUByte(); // profile_idc - readBits(5); // profileCompat constraint_set[0-4]_flag, u(5) - skipBits(3); // reserved_zero_3bits u(3), - readUByte(); // level_idc u(8) - skipUEG(); // seq_parameter_set_id - // some profiles have more optional data we don't need - if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) { - const chromaFormatIdc = readUEG(); - if (chromaFormatIdc === 3) { - skipBits(1); - } // separate_colour_plane_flag - - skipUEG(); // bit_depth_luma_minus8 - skipUEG(); // bit_depth_chroma_minus8 - skipBits(1); // qpprime_y_zero_transform_bypass_flag - if (readBoolean()) { - // seq_scaling_matrix_present_flag - scalingListCount = chromaFormatIdc !== 3 ? 8 : 12; - for (i = 0; i < scalingListCount; i++) { - if (readBoolean()) { - // seq_scaling_list_present_flag[ i ] - if (i < 6) { - skipScalingList(16); - } else { - skipScalingList(64); - } - } - } - } - } - skipUEG(); // log2_max_frame_num_minus4 - const picOrderCntType = readUEG(); - if (picOrderCntType === 0) { - readUEG(); // log2_max_pic_order_cnt_lsb_minus4 - } else if (picOrderCntType === 1) { - skipBits(1); // delta_pic_order_always_zero_flag - skipEG(); // offset_for_non_ref_pic - skipEG(); // offset_for_top_to_bottom_field - numRefFramesInPicOrderCntCycle = readUEG(); - for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) { - skipEG(); - } // offset_for_ref_frame[ i ] - } - skipUEG(); // max_num_ref_frames - skipBits(1); // gaps_in_frame_num_value_allowed_flag - const picWidthInMbsMinus1 = readUEG(); - const picHeightInMapUnitsMinus1 = readUEG(); - const frameMbsOnlyFlag = readBits(1); - if (frameMbsOnlyFlag === 0) { - skipBits(1); - } // mb_adaptive_frame_field_flag - - skipBits(1); // direct_8x8_inference_flag - if (readBoolean()) { - // frame_cropping_flag - frameCropLeftOffset = readUEG(); - frameCropRightOffset = readUEG(); - frameCropTopOffset = readUEG(); - frameCropBottomOffset = readUEG(); - } - let pixelRatio = [1, 1]; - if (readBoolean()) { - // vui_parameters_present_flag - if (readBoolean()) { - // aspect_ratio_info_present_flag - const aspectRatioIdc = readUByte(); - switch (aspectRatioIdc) { - case 1: - pixelRatio = [1, 1]; - break; - case 2: - pixelRatio = [12, 11]; - break; - case 3: - pixelRatio = [10, 11]; - break; - case 4: - pixelRatio = [16, 11]; - break; - case 5: - pixelRatio = [40, 33]; - break; - case 6: - pixelRatio = [24, 11]; - break; - case 7: - pixelRatio = [20, 11]; - break; - case 8: - pixelRatio = [32, 11]; - break; - case 9: - pixelRatio = [80, 33]; - break; - case 10: - pixelRatio = [18, 11]; - break; - case 11: - pixelRatio = [15, 11]; - break; - case 12: - pixelRatio = [64, 33]; - break; - case 13: - pixelRatio = [160, 99]; - break; - case 14: - pixelRatio = [4, 3]; - break; - case 15: - pixelRatio = [3, 2]; - break; - case 16: - pixelRatio = [2, 1]; - break; - case 255: - { - pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()]; - break; - } - } - } - } - return { - width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2), - height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset), - pixelRatio: pixelRatio - }; - } - readSliceType() { - // skip NALu type - this.readUByte(); - // discard first_mb_in_slice - this.readUEG(); - // return slice_type - return this.readUEG(); - } -} - -class AvcVideoParser extends BaseVideoParser { - parseAVCPES(track, textTrack, pes, last, duration) { - const units = this.parseAVCNALu(track, pes.data); - let VideoSample = this.VideoSample; - let push; - let spsfound = false; - // free pes.data to save up some memory - pes.data = null; - - // if new NAL units found and last sample still there, let's push ... - // this helps parsing streams with missing AUD (only do this if AUD never found) - if (VideoSample && units.length && !track.audFound) { - this.pushAccessUnit(VideoSample, track); - VideoSample = this.VideoSample = this.createVideoSample(false, pes.pts, pes.dts, ''); - } - units.forEach(unit => { - var _VideoSample2; - switch (unit.type) { - // NDR - case 1: - { - let iskey = false; - push = true; - const data = unit.data; - // only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...) - if (spsfound && data.length > 4) { - // retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR - const sliceType = new ExpGolomb(data).readSliceType(); - // 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice - // SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples. - // An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice. - // I slice: A slice that is not an SI slice that is decoded using intra prediction only. - // if (sliceType === 2 || sliceType === 7) { - if (sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9) { - iskey = true; - } - } - if (iskey) { - var _VideoSample; - // if we have non-keyframe data already, that cannot belong to the same frame as a keyframe, so force a push - if ((_VideoSample = VideoSample) != null && _VideoSample.frame && !VideoSample.key) { - this.pushAccessUnit(VideoSample, track); - VideoSample = this.VideoSample = null; - } - } - if (!VideoSample) { - VideoSample = this.VideoSample = this.createVideoSample(true, pes.pts, pes.dts, ''); - } - VideoSample.frame = true; - VideoSample.key = iskey; - break; - // IDR - } - case 5: - push = true; - // handle PES not starting with AUD - // if we have frame data already, that cannot belong to the same frame, so force a push - if ((_VideoSample2 = VideoSample) != null && _VideoSample2.frame && !VideoSample.key) { - this.pushAccessUnit(VideoSample, track); - VideoSample = this.VideoSample = null; - } - if (!VideoSample) { - VideoSample = this.VideoSample = this.createVideoSample(true, pes.pts, pes.dts, ''); - } - VideoSample.key = true; - VideoSample.frame = true; - break; - // SEI - case 6: - { - push = true; - parseSEIMessageFromNALu(unit.data, 1, pes.pts, textTrack.samples); - break; - // SPS - } - case 7: - { - var _track$pixelRatio, _track$pixelRatio2; - push = true; - spsfound = true; - const sps = unit.data; - const expGolombDecoder = new ExpGolomb(sps); - const config = expGolombDecoder.readSPS(); - if (!track.sps || track.width !== config.width || track.height !== config.height || ((_track$pixelRatio = track.pixelRatio) == null ? void 0 : _track$pixelRatio[0]) !== config.pixelRatio[0] || ((_track$pixelRatio2 = track.pixelRatio) == null ? void 0 : _track$pixelRatio2[1]) !== config.pixelRatio[1]) { - track.width = config.width; - track.height = config.height; - track.pixelRatio = config.pixelRatio; - track.sps = [sps]; - track.duration = duration; - const codecarray = sps.subarray(1, 4); - let codecstring = 'avc1.'; - for (let i = 0; i < 3; i++) { - let h = codecarray[i].toString(16); - if (h.length < 2) { - h = '0' + h; - } - codecstring += h; - } - track.codec = codecstring; - } - break; - } - // PPS - case 8: - push = true; - track.pps = [unit.data]; - break; - // AUD - case 9: - push = true; - track.audFound = true; - if (VideoSample) { - this.pushAccessUnit(VideoSample, track); - } - VideoSample = this.VideoSample = this.createVideoSample(false, pes.pts, pes.dts, ''); - break; - // Filler Data - case 12: - push = true; - break; - default: - push = false; - if (VideoSample) { - VideoSample.debug += 'unknown NAL ' + unit.type + ' '; - } - break; - } - if (VideoSample && push) { - const units = VideoSample.units; - units.push(unit); - } - }); - // if last PES packet, push samples - if (last && VideoSample) { - this.pushAccessUnit(VideoSample, track); - this.VideoSample = null; - } - } - parseAVCNALu(track, array) { - const len = array.byteLength; - let state = track.naluState || 0; - const lastState = state; - const units = []; - let i = 0; - let value; - let overflow; - let unitType; - let lastUnitStart = -1; - let lastUnitType = 0; - // logger.log('PES:' + Hex.hexDump(array)); - - if (state === -1) { - // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet - lastUnitStart = 0; - // NALu type is value read from offset 0 - lastUnitType = array[0] & 0x1f; - state = 0; - i = 1; - } - while (i < len) { - value = array[i++]; - // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case - if (!state) { - state = value ? 0 : 1; - continue; - } - if (state === 1) { - state = value ? 0 : 2; - continue; - } - // here we have state either equal to 2 or 3 - if (!value) { - state = 3; - } else if (value === 1) { - overflow = i - state - 1; - if (lastUnitStart >= 0) { - const unit = { - data: array.subarray(lastUnitStart, overflow), - type: lastUnitType - }; - // logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength); - units.push(unit); - } else { - // lastUnitStart is undefined => this is the first start code found in this PES packet - // first check if start code delimiter is overlapping between 2 PES packets, - // ie it started in last packet (lastState not zero) - // and ended at the beginning of this PES packet (i <= 4 - lastState) - const lastUnit = this.getLastNalUnit(track.samples); - if (lastUnit) { - if (lastState && i <= 4 - lastState) { - // start delimiter overlapping between PES packets - // strip start delimiter bytes from the end of last NAL unit - // check if lastUnit had a state different from zero - if (lastUnit.state) { - // strip last bytes - lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState); - } - } - // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit. - - if (overflow > 0) { - // logger.log('first NALU found with overflow:' + overflow); - lastUnit.data = appendUint8Array(lastUnit.data, array.subarray(0, overflow)); - lastUnit.state = 0; - } - } - } - // check if we can read unit type - if (i < len) { - unitType = array[i] & 0x1f; - // logger.log('find NALU @ offset:' + i + ',type:' + unitType); - lastUnitStart = i; - lastUnitType = unitType; - state = 0; - } else { - // not enough byte to read unit type. let's read it on next PES parsing - state = -1; - } - } else { - state = 0; - } - } - if (lastUnitStart >= 0 && state >= 0) { - const unit = { - data: array.subarray(lastUnitStart, len), - type: lastUnitType, - state: state - }; - units.push(unit); - // logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state); - } - // no NALu found - if (units.length === 0) { - // append pes.data to previous NAL unit - const lastUnit = this.getLastNalUnit(track.samples); - if (lastUnit) { - lastUnit.data = appendUint8Array(lastUnit.data, array); - } - } - track.naluState = state; - return units; - } -} - -/** - * SAMPLE-AES decrypter - */ - -class SampleAesDecrypter { - constructor(observer, config, keyData) { - this.keyData = void 0; - this.decrypter = void 0; - this.keyData = keyData; - this.decrypter = new Decrypter(config, { - removePKCS7Padding: false - }); - } - decryptBuffer(encryptedData) { - return this.decrypter.decrypt(encryptedData, this.keyData.key.buffer, this.keyData.iv.buffer); - } - - // AAC - encrypt all full 16 bytes blocks starting from offset 16 - decryptAacSample(samples, sampleIndex, callback) { - const curUnit = samples[sampleIndex].unit; - if (curUnit.length <= 16) { - // No encrypted portion in this sample (first 16 bytes is not - // encrypted, see https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption/Encryption/Encryption.html), - return; - } - const encryptedData = curUnit.subarray(16, curUnit.length - curUnit.length % 16); - const encryptedBuffer = encryptedData.buffer.slice(encryptedData.byteOffset, encryptedData.byteOffset + encryptedData.length); - this.decryptBuffer(encryptedBuffer).then(decryptedBuffer => { - const decryptedData = new Uint8Array(decryptedBuffer); - curUnit.set(decryptedData, 16); - if (!this.decrypter.isSync()) { - this.decryptAacSamples(samples, sampleIndex + 1, callback); - } - }); - } - decryptAacSamples(samples, sampleIndex, callback) { - for (;; sampleIndex++) { - if (sampleIndex >= samples.length) { - callback(); - return; - } - if (samples[sampleIndex].unit.length < 32) { - continue; - } - this.decryptAacSample(samples, sampleIndex, callback); - if (!this.decrypter.isSync()) { - return; - } - } - } - - // AVC - encrypt one 16 bytes block out of ten, starting from offset 32 - getAvcEncryptedData(decodedData) { - const encryptedDataLen = Math.floor((decodedData.length - 48) / 160) * 16 + 16; - const encryptedData = new Int8Array(encryptedDataLen); - let outputPos = 0; - for (let inputPos = 32; inputPos < decodedData.length - 16; inputPos += 160, outputPos += 16) { - encryptedData.set(decodedData.subarray(inputPos, inputPos + 16), outputPos); - } - return encryptedData; - } - getAvcDecryptedUnit(decodedData, decryptedData) { - const uint8DecryptedData = new Uint8Array(decryptedData); - let inputPos = 0; - for (let outputPos = 32; outputPos < decodedData.length - 16; outputPos += 160, inputPos += 16) { - decodedData.set(uint8DecryptedData.subarray(inputPos, inputPos + 16), outputPos); - } - return decodedData; - } - decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit) { - const decodedData = discardEPB(curUnit.data); - const encryptedData = this.getAvcEncryptedData(decodedData); - this.decryptBuffer(encryptedData.buffer).then(decryptedBuffer => { - curUnit.data = this.getAvcDecryptedUnit(decodedData, decryptedBuffer); - if (!this.decrypter.isSync()) { - this.decryptAvcSamples(samples, sampleIndex, unitIndex + 1, callback); - } - }); - } - decryptAvcSamples(samples, sampleIndex, unitIndex, callback) { - if (samples instanceof Uint8Array) { - throw new Error('Cannot decrypt samples of type Uint8Array'); - } - for (;; sampleIndex++, unitIndex = 0) { - if (sampleIndex >= samples.length) { - callback(); - return; - } - const curUnits = samples[sampleIndex].units; - for (;; unitIndex++) { - if (unitIndex >= curUnits.length) { - break; - } - const curUnit = curUnits[unitIndex]; - if (curUnit.data.length <= 48 || curUnit.type !== 1 && curUnit.type !== 5) { - continue; - } - this.decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit); - if (!this.decrypter.isSync()) { - return; - } - } - } - } -} - -const PACKET_LENGTH = 188; -class TSDemuxer { - constructor(observer, config, typeSupported) { - this.observer = void 0; - this.config = void 0; - this.typeSupported = void 0; - this.sampleAes = null; - this.pmtParsed = false; - this.audioCodec = void 0; - this.videoCodec = void 0; - this._duration = 0; - this._pmtId = -1; - this._videoTrack = void 0; - this._audioTrack = void 0; - this._id3Track = void 0; - this._txtTrack = void 0; - this.aacOverFlow = null; - this.remainderData = null; - this.videoParser = void 0; - this.observer = observer; - this.config = config; - this.typeSupported = typeSupported; - this.videoParser = new AvcVideoParser(); - } - static probe(data) { - const syncOffset = TSDemuxer.syncOffset(data); - if (syncOffset > 0) { - logger.warn(`MPEG2-TS detected but first sync word found @ offset ${syncOffset}`); - } - return syncOffset !== -1; - } - static syncOffset(data) { - const length = data.length; - let scanwindow = Math.min(PACKET_LENGTH * 5, length - PACKET_LENGTH) + 1; - let i = 0; - while (i < scanwindow) { - // a TS init segment should contain at least 2 TS packets: PAT and PMT, each starting with 0x47 - let foundPat = false; - let packetStart = -1; - let tsPackets = 0; - for (let j = i; j < length; j += PACKET_LENGTH) { - if (data[j] === 0x47 && (length - j === PACKET_LENGTH || data[j + PACKET_LENGTH] === 0x47)) { - tsPackets++; - if (packetStart === -1) { - packetStart = j; - // First sync word found at offset, increase scan length (#5251) - if (packetStart !== 0) { - scanwindow = Math.min(packetStart + PACKET_LENGTH * 99, data.length - PACKET_LENGTH) + 1; - } - } - if (!foundPat) { - foundPat = parsePID(data, j) === 0; - } - // Sync word found at 0 with 3 packets, or found at offset least 2 packets up to scanwindow (#5501) - if (foundPat && tsPackets > 1 && (packetStart === 0 && tsPackets > 2 || j + PACKET_LENGTH > scanwindow)) { - return packetStart; - } - } else if (tsPackets) { - // Exit if sync word found, but does not contain contiguous packets - return -1; - } else { - break; - } - } - i++; - } - return -1; - } - - /** - * Creates a track model internal to demuxer used to drive remuxing input - */ - static createTrack(type, duration) { - return { - container: type === 'video' || type === 'audio' ? 'video/mp2t' : undefined, - type, - id: RemuxerTrackIdConfig[type], - pid: -1, - inputTimeScale: 90000, - sequenceNumber: 0, - samples: [], - dropped: 0, - duration: type === 'audio' ? duration : undefined - }; - } - - /** - * Initializes a new init segment on the demuxer/remuxer interface. Needed for discontinuities/track-switches (or at stream start) - * Resets all internal track instances of the demuxer. - */ - resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) { - this.pmtParsed = false; - this._pmtId = -1; - this._videoTrack = TSDemuxer.createTrack('video'); - this._audioTrack = TSDemuxer.createTrack('audio', trackDuration); - this._id3Track = TSDemuxer.createTrack('id3'); - this._txtTrack = TSDemuxer.createTrack('text'); - this._audioTrack.segmentCodec = 'aac'; - - // flush any partial content - this.aacOverFlow = null; - this.remainderData = null; - this.audioCodec = audioCodec; - this.videoCodec = videoCodec; - this._duration = trackDuration; - } - resetTimeStamp() {} - resetContiguity() { - const { - _audioTrack, - _videoTrack, - _id3Track - } = this; - if (_audioTrack) { - _audioTrack.pesData = null; - } - if (_videoTrack) { - _videoTrack.pesData = null; - } - if (_id3Track) { - _id3Track.pesData = null; - } - this.aacOverFlow = null; - this.remainderData = null; - } - demux(data, timeOffset, isSampleAes = false, flush = false) { - if (!isSampleAes) { - this.sampleAes = null; - } - let pes; - const videoTrack = this._videoTrack; - const audioTrack = this._audioTrack; - const id3Track = this._id3Track; - const textTrack = this._txtTrack; - let videoPid = videoTrack.pid; - let videoData = videoTrack.pesData; - let audioPid = audioTrack.pid; - let id3Pid = id3Track.pid; - let audioData = audioTrack.pesData; - let id3Data = id3Track.pesData; - let unknownPID = null; - let pmtParsed = this.pmtParsed; - let pmtId = this._pmtId; - let len = data.length; - if (this.remainderData) { - data = appendUint8Array(this.remainderData, data); - len = data.length; - this.remainderData = null; - } - if (len < PACKET_LENGTH && !flush) { - this.remainderData = data; - return { - audioTrack, - videoTrack, - id3Track, - textTrack - }; - } - const syncOffset = Math.max(0, TSDemuxer.syncOffset(data)); - len -= (len - syncOffset) % PACKET_LENGTH; - if (len < data.byteLength && !flush) { - this.remainderData = new Uint8Array(data.buffer, len, data.buffer.byteLength - len); - } - - // loop through TS packets - let tsPacketErrors = 0; - for (let start = syncOffset; start < len; start += PACKET_LENGTH) { - if (data[start] === 0x47) { - const stt = !!(data[start + 1] & 0x40); - const pid = parsePID(data, start); - const atf = (data[start + 3] & 0x30) >> 4; - - // if an adaption field is present, its length is specified by the fifth byte of the TS packet header. - let offset; - if (atf > 1) { - offset = start + 5 + data[start + 4]; - // continue if there is only adaptation field - if (offset === start + PACKET_LENGTH) { - continue; - } - } else { - offset = start + 4; - } - switch (pid) { - case videoPid: - if (stt) { - if (videoData && (pes = parsePES(videoData))) { - this.videoParser.parseAVCPES(videoTrack, textTrack, pes, false, this._duration); - } - videoData = { - data: [], - size: 0 - }; - } - if (videoData) { - videoData.data.push(data.subarray(offset, start + PACKET_LENGTH)); - videoData.size += start + PACKET_LENGTH - offset; - } - break; - case audioPid: - if (stt) { - if (audioData && (pes = parsePES(audioData))) { - switch (audioTrack.segmentCodec) { - case 'aac': - this.parseAACPES(audioTrack, pes); - break; - case 'mp3': - this.parseMPEGPES(audioTrack, pes); - break; - case 'ac3': - { - this.parseAC3PES(audioTrack, pes); - } - break; - } - } - audioData = { - data: [], - size: 0 - }; - } - if (audioData) { - audioData.data.push(data.subarray(offset, start + PACKET_LENGTH)); - audioData.size += start + PACKET_LENGTH - offset; - } - break; - case id3Pid: - if (stt) { - if (id3Data && (pes = parsePES(id3Data))) { - this.parseID3PES(id3Track, pes); - } - id3Data = { - data: [], - size: 0 - }; - } - if (id3Data) { - id3Data.data.push(data.subarray(offset, start + PACKET_LENGTH)); - id3Data.size += start + PACKET_LENGTH - offset; - } - break; - case 0: - if (stt) { - offset += data[offset] + 1; - } - pmtId = this._pmtId = parsePAT(data, offset); - // logger.log('PMT PID:' + this._pmtId); - break; - case pmtId: - { - if (stt) { - offset += data[offset] + 1; - } - const parsedPIDs = parsePMT(data, offset, this.typeSupported, isSampleAes, this.observer); - - // only update track id if track PID found while parsing PMT - // this is to avoid resetting the PID to -1 in case - // track PID transiently disappears from the stream - // this could happen in case of transient missing audio samples for example - // NOTE this is only the PID of the track as found in TS, - // but we are not using this for MP4 track IDs. - videoPid = parsedPIDs.videoPid; - if (videoPid > 0) { - videoTrack.pid = videoPid; - videoTrack.segmentCodec = parsedPIDs.segmentVideoCodec; - } - audioPid = parsedPIDs.audioPid; - if (audioPid > 0) { - audioTrack.pid = audioPid; - audioTrack.segmentCodec = parsedPIDs.segmentAudioCodec; - } - id3Pid = parsedPIDs.id3Pid; - if (id3Pid > 0) { - id3Track.pid = id3Pid; - } - if (unknownPID !== null && !pmtParsed) { - logger.warn(`MPEG-TS PMT found at ${start} after unknown PID '${unknownPID}'. Backtracking to sync byte @${syncOffset} to parse all TS packets.`); - unknownPID = null; - // we set it to -188, the += 188 in the for loop will reset start to 0 - start = syncOffset - 188; - } - pmtParsed = this.pmtParsed = true; - break; - } - case 0x11: - case 0x1fff: - break; - default: - unknownPID = pid; - break; - } - } else { - tsPacketErrors++; - } - } - if (tsPacketErrors > 0) { - emitParsingError(this.observer, new Error(`Found ${tsPacketErrors} TS packet/s that do not start with 0x47`)); - } - videoTrack.pesData = videoData; - audioTrack.pesData = audioData; - id3Track.pesData = id3Data; - const demuxResult = { - audioTrack, - videoTrack, - id3Track, - textTrack - }; - if (flush) { - this.extractRemainingSamples(demuxResult); - } - return demuxResult; - } - flush() { - const { - remainderData - } = this; - this.remainderData = null; - let result; - if (remainderData) { - result = this.demux(remainderData, -1, false, true); - } else { - result = { - videoTrack: this._videoTrack, - audioTrack: this._audioTrack, - id3Track: this._id3Track, - textTrack: this._txtTrack - }; - } - this.extractRemainingSamples(result); - if (this.sampleAes) { - return this.decrypt(result, this.sampleAes); - } - return result; - } - extractRemainingSamples(demuxResult) { - const { - audioTrack, - videoTrack, - id3Track, - textTrack - } = demuxResult; - const videoData = videoTrack.pesData; - const audioData = audioTrack.pesData; - const id3Data = id3Track.pesData; - // try to parse last PES packets - let pes; - if (videoData && (pes = parsePES(videoData))) { - this.videoParser.parseAVCPES(videoTrack, textTrack, pes, true, this._duration); - videoTrack.pesData = null; - } else { - // either avcData null or PES truncated, keep it for next frag parsing - videoTrack.pesData = videoData; - } - if (audioData && (pes = parsePES(audioData))) { - switch (audioTrack.segmentCodec) { - case 'aac': - this.parseAACPES(audioTrack, pes); - break; - case 'mp3': - this.parseMPEGPES(audioTrack, pes); - break; - case 'ac3': - { - this.parseAC3PES(audioTrack, pes); - } - break; - } - audioTrack.pesData = null; - } else { - if (audioData != null && audioData.size) { - logger.log('last AAC PES packet truncated,might overlap between fragments'); - } - - // either audioData null or PES truncated, keep it for next frag parsing - audioTrack.pesData = audioData; - } - if (id3Data && (pes = parsePES(id3Data))) { - this.parseID3PES(id3Track, pes); - id3Track.pesData = null; - } else { - // either id3Data null or PES truncated, keep it for next frag parsing - id3Track.pesData = id3Data; - } - } - demuxSampleAes(data, keyData, timeOffset) { - const demuxResult = this.demux(data, timeOffset, true, !this.config.progressive); - const sampleAes = this.sampleAes = new SampleAesDecrypter(this.observer, this.config, keyData); - return this.decrypt(demuxResult, sampleAes); - } - decrypt(demuxResult, sampleAes) { - return new Promise(resolve => { - const { - audioTrack, - videoTrack - } = demuxResult; - if (audioTrack.samples && audioTrack.segmentCodec === 'aac') { - sampleAes.decryptAacSamples(audioTrack.samples, 0, () => { - if (videoTrack.samples) { - sampleAes.decryptAvcSamples(videoTrack.samples, 0, 0, () => { - resolve(demuxResult); - }); - } else { - resolve(demuxResult); - } - }); - } else if (videoTrack.samples) { - sampleAes.decryptAvcSamples(videoTrack.samples, 0, 0, () => { - resolve(demuxResult); - }); - } - }); - } - destroy() { - this._duration = 0; - } - parseAACPES(track, pes) { - let startOffset = 0; - const aacOverFlow = this.aacOverFlow; - let data = pes.data; - if (aacOverFlow) { - this.aacOverFlow = null; - const frameMissingBytes = aacOverFlow.missing; - const sampleLength = aacOverFlow.sample.unit.byteLength; - // logger.log(`AAC: append overflowing ${sampleLength} bytes to beginning of new PES`); - if (frameMissingBytes === -1) { - data = appendUint8Array(aacOverFlow.sample.unit, data); - } else { - const frameOverflowBytes = sampleLength - frameMissingBytes; - aacOverFlow.sample.unit.set(data.subarray(0, frameMissingBytes), frameOverflowBytes); - track.samples.push(aacOverFlow.sample); - startOffset = aacOverFlow.missing; - } - } - // look for ADTS header (0xFFFx) - let offset; - let len; - for (offset = startOffset, len = data.length; offset < len - 1; offset++) { - if (isHeader$1(data, offset)) { - break; - } - } - // if ADTS header does not start straight from the beginning of the PES payload, raise an error - if (offset !== startOffset) { - let reason; - const recoverable = offset < len - 1; - if (recoverable) { - reason = `AAC PES did not start with ADTS header,offset:${offset}`; - } else { - reason = 'No ADTS header found in AAC PES'; - } - emitParsingError(this.observer, new Error(reason), recoverable); - if (!recoverable) { - return; - } - } - initTrackConfig(track, this.observer, data, offset, this.audioCodec); - let pts; - if (pes.pts !== undefined) { - pts = pes.pts; - } else if (aacOverFlow) { - // if last AAC frame is overflowing, we should ensure timestamps are contiguous: - // first sample PTS should be equal to last sample PTS + frameDuration - const frameDuration = getFrameDuration(track.samplerate); - pts = aacOverFlow.sample.pts + frameDuration; - } else { - logger.warn('[tsdemuxer]: AAC PES unknown PTS'); - return; - } - - // scan for aac samples - let frameIndex = 0; - let frame; - while (offset < len) { - frame = appendFrame$2(track, data, offset, pts, frameIndex); - offset += frame.length; - if (!frame.missing) { - frameIndex++; - for (; offset < len - 1; offset++) { - if (isHeader$1(data, offset)) { - break; - } - } - } else { - this.aacOverFlow = frame; - break; - } - } - } - parseMPEGPES(track, pes) { - const data = pes.data; - const length = data.length; - let frameIndex = 0; - let offset = 0; - const pts = pes.pts; - if (pts === undefined) { - logger.warn('[tsdemuxer]: MPEG PES unknown PTS'); - return; - } - while (offset < length) { - if (isHeader(data, offset)) { - const frame = appendFrame$1(track, data, offset, pts, frameIndex); - if (frame) { - offset += frame.length; - frameIndex++; - } else { - // logger.log('Unable to parse Mpeg audio frame'); - break; - } - } else { - // nothing found, keep looking - offset++; - } - } - } - parseAC3PES(track, pes) { - { - const data = pes.data; - const pts = pes.pts; - if (pts === undefined) { - logger.warn('[tsdemuxer]: AC3 PES unknown PTS'); - return; - } - const length = data.length; - let frameIndex = 0; - let offset = 0; - let parsed; - while (offset < length && (parsed = appendFrame(track, data, offset, pts, frameIndex++)) > 0) { - offset += parsed; - } - } - } - parseID3PES(id3Track, pes) { - if (pes.pts === undefined) { - logger.warn('[tsdemuxer]: ID3 PES unknown PTS'); - return; - } - const id3Sample = _extends({}, pes, { - type: this._videoTrack ? MetadataSchema.emsg : MetadataSchema.audioId3, - duration: Number.POSITIVE_INFINITY - }); - id3Track.samples.push(id3Sample); - } -} -function parsePID(data, offset) { - // pid is a 13-bit field starting at the last bit of TS[1] - return ((data[offset + 1] & 0x1f) << 8) + data[offset + 2]; -} -function parsePAT(data, offset) { - // skip the PSI header and parse the first PMT entry - return (data[offset + 10] & 0x1f) << 8 | data[offset + 11]; -} -function parsePMT(data, offset, typeSupported, isSampleAes, observer) { - const result = { - audioPid: -1, - videoPid: -1, - id3Pid: -1, - segmentVideoCodec: 'avc', - segmentAudioCodec: 'aac' - }; - const sectionLength = (data[offset + 1] & 0x0f) << 8 | data[offset + 2]; - const tableEnd = offset + 3 + sectionLength - 4; - // to determine where the table is, we have to figure out how - // long the program info descriptors are - const programInfoLength = (data[offset + 10] & 0x0f) << 8 | data[offset + 11]; - // advance the offset to the first entry in the mapping table - offset += 12 + programInfoLength; - while (offset < tableEnd) { - const pid = parsePID(data, offset); - const esInfoLength = (data[offset + 3] & 0x0f) << 8 | data[offset + 4]; - switch (data[offset]) { - case 0xcf: - // SAMPLE-AES AAC - if (!isSampleAes) { - logEncryptedSamplesFoundInUnencryptedStream('ADTS AAC'); - break; - } - /* falls through */ - case 0x0f: - // ISO/IEC 13818-7 ADTS AAC (MPEG-2 lower bit-rate audio) - // logger.log('AAC PID:' + pid); - if (result.audioPid === -1) { - result.audioPid = pid; - } - break; - - // Packetized metadata (ID3) - case 0x15: - // logger.log('ID3 PID:' + pid); - if (result.id3Pid === -1) { - result.id3Pid = pid; - } - break; - case 0xdb: - // SAMPLE-AES AVC - if (!isSampleAes) { - logEncryptedSamplesFoundInUnencryptedStream('H.264'); - break; - } - /* falls through */ - case 0x1b: - // ITU-T Rec. H.264 and ISO/IEC 14496-10 (lower bit-rate video) - // logger.log('AVC PID:' + pid); - if (result.videoPid === -1) { - result.videoPid = pid; - result.segmentVideoCodec = 'avc'; - } - break; - - // ISO/IEC 11172-3 (MPEG-1 audio) - // or ISO/IEC 13818-3 (MPEG-2 halved sample rate audio) - case 0x03: - case 0x04: - // logger.log('MPEG PID:' + pid); - if (!typeSupported.mpeg && !typeSupported.mp3) { - logger.log('MPEG audio found, not supported in this browser'); - } else if (result.audioPid === -1) { - result.audioPid = pid; - result.segmentAudioCodec = 'mp3'; - } - break; - case 0xc1: - // SAMPLE-AES AC3 - if (!isSampleAes) { - logEncryptedSamplesFoundInUnencryptedStream('AC-3'); - break; - } - /* falls through */ - case 0x81: - { - if (!typeSupported.ac3) { - logger.log('AC-3 audio found, not supported in this browser'); - } else if (result.audioPid === -1) { - result.audioPid = pid; - result.segmentAudioCodec = 'ac3'; - } - } - break; - case 0x06: - // stream_type 6 can mean a lot of different things in case of DVB. - // We need to look at the descriptors. Right now, we're only interested - // in AC-3 audio, so we do the descriptor parsing only when we don't have - // an audio PID yet. - if (result.audioPid === -1 && esInfoLength > 0) { - let parsePos = offset + 5; - let remaining = esInfoLength; - while (remaining > 2) { - const descriptorId = data[parsePos]; - switch (descriptorId) { - case 0x6a: - // DVB Descriptor for AC-3 - { - if (typeSupported.ac3 !== true) { - logger.log('AC-3 audio found, not supported in this browser for now'); - } else { - result.audioPid = pid; - result.segmentAudioCodec = 'ac3'; - } - } - break; - } - const descriptorLen = data[parsePos + 1] + 2; - parsePos += descriptorLen; - remaining -= descriptorLen; - } - } - break; - case 0xc2: // SAMPLE-AES EC3 - /* falls through */ - case 0x87: - emitParsingError(observer, new Error('Unsupported EC-3 in M2TS found')); - return result; - case 0x24: - emitParsingError(observer, new Error('Unsupported HEVC in M2TS found')); - return result; - } - // move to the next table entry - // skip past the elementary stream descriptors, if present - offset += esInfoLength + 5; - } - return result; -} -function emitParsingError(observer, error, levelRetry) { - logger.warn(`parsing error: ${error.message}`); - observer.emit(Events.ERROR, Events.ERROR, { - type: ErrorTypes.MEDIA_ERROR, - details: ErrorDetails.FRAG_PARSING_ERROR, - fatal: false, - levelRetry, - error, - reason: error.message - }); -} -function logEncryptedSamplesFoundInUnencryptedStream(type) { - logger.log(`${type} with AES-128-CBC encryption found in unencrypted stream`); -} -function parsePES(stream) { - let i = 0; - let frag; - let pesLen; - let pesHdrLen; - let pesPts; - let pesDts; - const data = stream.data; - // safety check - if (!stream || stream.size === 0) { - return null; - } - - // we might need up to 19 bytes to read PES header - // if first chunk of data is less than 19 bytes, let's merge it with following ones until we get 19 bytes - // usually only one merge is needed (and this is rare ...) - while (data[0].length < 19 && data.length > 1) { - data[0] = appendUint8Array(data[0], data[1]); - data.splice(1, 1); - } - // retrieve PTS/DTS from first fragment - frag = data[0]; - const pesPrefix = (frag[0] << 16) + (frag[1] << 8) + frag[2]; - if (pesPrefix === 1) { - pesLen = (frag[4] << 8) + frag[5]; - // if PES parsed length is not zero and greater than total received length, stop parsing. PES might be truncated - // minus 6 : PES header size - if (pesLen && pesLen > stream.size - 6) { - return null; - } - const pesFlags = frag[7]; - if (pesFlags & 0xc0) { - /* PES header described here : http://dvd.sourceforge.net/dvdinfo/pes-hdr.html - as PTS / DTS is 33 bit we cannot use bitwise operator in JS, - as Bitwise operators treat their operands as a sequence of 32 bits */ - pesPts = (frag[9] & 0x0e) * 536870912 + - // 1 << 29 - (frag[10] & 0xff) * 4194304 + - // 1 << 22 - (frag[11] & 0xfe) * 16384 + - // 1 << 14 - (frag[12] & 0xff) * 128 + - // 1 << 7 - (frag[13] & 0xfe) / 2; - if (pesFlags & 0x40) { - pesDts = (frag[14] & 0x0e) * 536870912 + - // 1 << 29 - (frag[15] & 0xff) * 4194304 + - // 1 << 22 - (frag[16] & 0xfe) * 16384 + - // 1 << 14 - (frag[17] & 0xff) * 128 + - // 1 << 7 - (frag[18] & 0xfe) / 2; - if (pesPts - pesDts > 60 * 90000) { - logger.warn(`${Math.round((pesPts - pesDts) / 90000)}s delta between PTS and DTS, align them`); - pesPts = pesDts; - } - } else { - pesDts = pesPts; - } - } - pesHdrLen = frag[8]; - // 9 bytes : 6 bytes for PES header + 3 bytes for PES extension - let payloadStartOffset = pesHdrLen + 9; - if (stream.size <= payloadStartOffset) { - return null; - } - stream.size -= payloadStartOffset; - // reassemble PES packet - const pesData = new Uint8Array(stream.size); - for (let j = 0, dataLen = data.length; j < dataLen; j++) { - frag = data[j]; - let len = frag.byteLength; - if (payloadStartOffset) { - if (payloadStartOffset > len) { - // trim full frag if PES header bigger than frag - payloadStartOffset -= len; - continue; - } else { - // trim partial frag if PES header smaller than frag - frag = frag.subarray(payloadStartOffset); - len -= payloadStartOffset; - payloadStartOffset = 0; - } - } - pesData.set(frag, i); - i += len; - } - if (pesLen) { - // payload size : remove PES header + PES extension - pesLen -= pesHdrLen + 3; - } - return { - data: pesData, - pts: pesPts, - dts: pesDts, - len: pesLen - }; - } - return null; -} - -/** - * MP3 demuxer - */ -class MP3Demuxer extends BaseAudioDemuxer { - resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) { - super.resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration); - this._audioTrack = { - container: 'audio/mpeg', - type: 'audio', - id: 2, - pid: -1, - sequenceNumber: 0, - segmentCodec: 'mp3', - samples: [], - manifestCodec: audioCodec, - duration: trackDuration, - inputTimeScale: 90000, - dropped: 0 - }; - } - static probe(data) { - if (!data) { - return false; - } - - // check if data contains ID3 timestamp and MPEG sync word - // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1 - // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III) - // More info http://www.mp3-tech.org/programmer/frame_header.html - const id3Data = getID3Data(data, 0); - let offset = (id3Data == null ? void 0 : id3Data.length) || 0; - - // Check for ac-3|ec-3 sync bytes and return false if present - if (id3Data && data[offset] === 0x0b && data[offset + 1] === 0x77 && getTimeStamp(id3Data) !== undefined && - // check the bsid to confirm ac-3 or ec-3 (not mp3) - getAudioBSID(data, offset) <= 16) { - return false; - } - for (let length = data.length; offset < length; offset++) { - if (probe(data, offset)) { - logger.log('MPEG Audio sync word found !'); - return true; - } - } - return false; - } - canParse(data, offset) { - return canParse(data, offset); - } - appendFrame(track, data, offset) { - if (this.basePTS === null) { - return; - } - return appendFrame$1(track, data, offset, this.basePTS, this.frameIndex); - } -} - -/** - * AAC helper - */ - -class AAC { - static getSilentFrame(codec, channelCount) { - switch (codec) { - case 'mp4a.40.2': - if (channelCount === 1) { - return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x23, 0x80]); - } else if (channelCount === 2) { - return new Uint8Array([0x21, 0x00, 0x49, 0x90, 0x02, 0x19, 0x00, 0x23, 0x80]); - } else if (channelCount === 3) { - return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x8e]); - } else if (channelCount === 4) { - return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x80, 0x2c, 0x80, 0x08, 0x02, 0x38]); - } else if (channelCount === 5) { - return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x38]); - } else if (channelCount === 6) { - return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x00, 0xb2, 0x00, 0x20, 0x08, 0xe0]); - } - break; - // handle HE-AAC below (mp4a.40.5 / mp4a.40.29) - default: - if (channelCount === 1) { - // ffmpeg -y -f lavfi -i "aevalsrc=0:d=0.05" -c:a libfdk_aac -profile:a aac_he -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac - return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x4e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x1c, 0x6, 0xf1, 0xc1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]); - } else if (channelCount === 2) { - // ffmpeg -y -f lavfi -i "aevalsrc=0|0:d=0.05" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac - return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x0, 0x95, 0x0, 0x6, 0xf1, 0xa1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]); - } else if (channelCount === 3) { - // ffmpeg -y -f lavfi -i "aevalsrc=0|0|0:d=0.05" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac - return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x0, 0x95, 0x0, 0x6, 0xf1, 0xa1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]); - } - break; - } - return undefined; - } -} - -/** - * Generate MP4 Box - */ - -const UINT32_MAX = Math.pow(2, 32) - 1; -class MP4 { - static init() { - MP4.types = { - avc1: [], - // codingname - avcC: [], - btrt: [], - dinf: [], - dref: [], - esds: [], - ftyp: [], - hdlr: [], - mdat: [], - mdhd: [], - mdia: [], - mfhd: [], - minf: [], - moof: [], - moov: [], - mp4a: [], - '.mp3': [], - dac3: [], - 'ac-3': [], - mvex: [], - mvhd: [], - pasp: [], - sdtp: [], - stbl: [], - stco: [], - stsc: [], - stsd: [], - stsz: [], - stts: [], - tfdt: [], - tfhd: [], - traf: [], - trak: [], - trun: [], - trex: [], - tkhd: [], - vmhd: [], - smhd: [] - }; - let i; - for (i in MP4.types) { - if (MP4.types.hasOwnProperty(i)) { - MP4.types[i] = [i.charCodeAt(0), i.charCodeAt(1), i.charCodeAt(2), i.charCodeAt(3)]; - } - } - const videoHdlr = new Uint8Array([0x00, - // version 0 - 0x00, 0x00, 0x00, - // flags - 0x00, 0x00, 0x00, 0x00, - // pre_defined - 0x76, 0x69, 0x64, 0x65, - // handler_type: 'vide' - 0x00, 0x00, 0x00, 0x00, - // reserved - 0x00, 0x00, 0x00, 0x00, - // reserved - 0x00, 0x00, 0x00, 0x00, - // reserved - 0x56, 0x69, 0x64, 0x65, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler' - ]); - const audioHdlr = new Uint8Array([0x00, - // version 0 - 0x00, 0x00, 0x00, - // flags - 0x00, 0x00, 0x00, 0x00, - // pre_defined - 0x73, 0x6f, 0x75, 0x6e, - // handler_type: 'soun' - 0x00, 0x00, 0x00, 0x00, - // reserved - 0x00, 0x00, 0x00, 0x00, - // reserved - 0x00, 0x00, 0x00, 0x00, - // reserved - 0x53, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler' - ]); - MP4.HDLR_TYPES = { - video: videoHdlr, - audio: audioHdlr - }; - const dref = new Uint8Array([0x00, - // version 0 - 0x00, 0x00, 0x00, - // flags - 0x00, 0x00, 0x00, 0x01, - // entry_count - 0x00, 0x00, 0x00, 0x0c, - // entry_size - 0x75, 0x72, 0x6c, 0x20, - // 'url' type - 0x00, - // version 0 - 0x00, 0x00, 0x01 // entry_flags - ]); - const stco = new Uint8Array([0x00, - // version - 0x00, 0x00, 0x00, - // flags - 0x00, 0x00, 0x00, 0x00 // entry_count - ]); - MP4.STTS = MP4.STSC = MP4.STCO = stco; - MP4.STSZ = new Uint8Array([0x00, - // version - 0x00, 0x00, 0x00, - // flags - 0x00, 0x00, 0x00, 0x00, - // sample_size - 0x00, 0x00, 0x00, 0x00 // sample_count - ]); - MP4.VMHD = new Uint8Array([0x00, - // version - 0x00, 0x00, 0x01, - // flags - 0x00, 0x00, - // graphicsmode - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // opcolor - ]); - MP4.SMHD = new Uint8Array([0x00, - // version - 0x00, 0x00, 0x00, - // flags - 0x00, 0x00, - // balance - 0x00, 0x00 // reserved - ]); - MP4.STSD = new Uint8Array([0x00, - // version 0 - 0x00, 0x00, 0x00, - // flags - 0x00, 0x00, 0x00, 0x01]); // entry_count - - const majorBrand = new Uint8Array([105, 115, 111, 109]); // isom - const avc1Brand = new Uint8Array([97, 118, 99, 49]); // avc1 - const minorVersion = new Uint8Array([0, 0, 0, 1]); - MP4.FTYP = MP4.box(MP4.types.ftyp, majorBrand, minorVersion, majorBrand, avc1Brand); - MP4.DINF = MP4.box(MP4.types.dinf, MP4.box(MP4.types.dref, dref)); - } - static box(type, ...payload) { - let size = 8; - let i = payload.length; - const len = i; - // calculate the total size we need to allocate - while (i--) { - size += payload[i].byteLength; - } - const result = new Uint8Array(size); - result[0] = size >> 24 & 0xff; - result[1] = size >> 16 & 0xff; - result[2] = size >> 8 & 0xff; - result[3] = size & 0xff; - result.set(type, 4); - // copy the payload into the result - for (i = 0, size = 8; i < len; i++) { - // copy payload[i] array @ offset size - result.set(payload[i], size); - size += payload[i].byteLength; - } - return result; - } - static hdlr(type) { - return MP4.box(MP4.types.hdlr, MP4.HDLR_TYPES[type]); - } - static mdat(data) { - return MP4.box(MP4.types.mdat, data); - } - static mdhd(timescale, duration) { - duration *= timescale; - const upperWordDuration = Math.floor(duration / (UINT32_MAX + 1)); - const lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1)); - return MP4.box(MP4.types.mdhd, new Uint8Array([0x01, - // version 1 - 0x00, 0x00, 0x00, - // flags - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - // creation_time - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, - // modification_time - timescale >> 24 & 0xff, timescale >> 16 & 0xff, timescale >> 8 & 0xff, timescale & 0xff, - // timescale - upperWordDuration >> 24, upperWordDuration >> 16 & 0xff, upperWordDuration >> 8 & 0xff, upperWordDuration & 0xff, lowerWordDuration >> 24, lowerWordDuration >> 16 & 0xff, lowerWordDuration >> 8 & 0xff, lowerWordDuration & 0xff, 0x55, 0xc4, - // 'und' language (undetermined) - 0x00, 0x00])); - } - static mdia(track) { - return MP4.box(MP4.types.mdia, MP4.mdhd(track.timescale, track.duration), MP4.hdlr(track.type), MP4.minf(track)); - } - static mfhd(sequenceNumber) { - return MP4.box(MP4.types.mfhd, new Uint8Array([0x00, 0x00, 0x00, 0x00, - // flags - sequenceNumber >> 24, sequenceNumber >> 16 & 0xff, sequenceNumber >> 8 & 0xff, sequenceNumber & 0xff // sequence_number - ])); - } - static minf(track) { - if (track.type === 'audio') { - return MP4.box(MP4.types.minf, MP4.box(MP4.types.smhd, MP4.SMHD), MP4.DINF, MP4.stbl(track)); - } else { - return MP4.box(MP4.types.minf, MP4.box(MP4.types.vmhd, MP4.VMHD), MP4.DINF, MP4.stbl(track)); - } - } - static moof(sn, baseMediaDecodeTime, track) { - return MP4.box(MP4.types.moof, MP4.mfhd(sn), MP4.traf(track, baseMediaDecodeTime)); - } - static moov(tracks) { - let i = tracks.length; - const boxes = []; - while (i--) { - boxes[i] = MP4.trak(tracks[i]); - } - return MP4.box.apply(null, [MP4.types.moov, MP4.mvhd(tracks[0].timescale, tracks[0].duration)].concat(boxes).concat(MP4.mvex(tracks))); - } - static mvex(tracks) { - let i = tracks.length; - const boxes = []; - while (i--) { - boxes[i] = MP4.trex(tracks[i]); - } - return MP4.box.apply(null, [MP4.types.mvex, ...boxes]); - } - static mvhd(timescale, duration) { - duration *= timescale; - const upperWordDuration = Math.floor(duration / (UINT32_MAX + 1)); - const lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1)); - const bytes = new Uint8Array([0x01, - // version 1 - 0x00, 0x00, 0x00, - // flags - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - // creation_time - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, - // modification_time - timescale >> 24 & 0xff, timescale >> 16 & 0xff, timescale >> 8 & 0xff, timescale & 0xff, - // timescale - upperWordDuration >> 24, upperWordDuration >> 16 & 0xff, upperWordDuration >> 8 & 0xff, upperWordDuration & 0xff, lowerWordDuration >> 24, lowerWordDuration >> 16 & 0xff, lowerWordDuration >> 8 & 0xff, lowerWordDuration & 0xff, 0x00, 0x01, 0x00, 0x00, - // 1.0 rate - 0x01, 0x00, - // 1.0 volume - 0x00, 0x00, - // reserved - 0x00, 0x00, 0x00, 0x00, - // reserved - 0x00, 0x00, 0x00, 0x00, - // reserved - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - // transformation: unity matrix - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // pre_defined - 0xff, 0xff, 0xff, 0xff // next_track_ID - ]); - return MP4.box(MP4.types.mvhd, bytes); - } - static sdtp(track) { - const samples = track.samples || []; - const bytes = new Uint8Array(4 + samples.length); - let i; - let flags; - // leave the full box header (4 bytes) all zero - // write the sample table - for (i = 0; i < samples.length; i++) { - flags = samples[i].flags; - bytes[i + 4] = flags.dependsOn << 4 | flags.isDependedOn << 2 | flags.hasRedundancy; - } - return MP4.box(MP4.types.sdtp, bytes); - } - static stbl(track) { - return MP4.box(MP4.types.stbl, MP4.stsd(track), MP4.box(MP4.types.stts, MP4.STTS), MP4.box(MP4.types.stsc, MP4.STSC), MP4.box(MP4.types.stsz, MP4.STSZ), MP4.box(MP4.types.stco, MP4.STCO)); - } - static avc1(track) { - let sps = []; - let pps = []; - let i; - let data; - let len; - // assemble the SPSs - - for (i = 0; i < track.sps.length; i++) { - data = track.sps[i]; - len = data.byteLength; - sps.push(len >>> 8 & 0xff); - sps.push(len & 0xff); - - // SPS - sps = sps.concat(Array.prototype.slice.call(data)); - } - - // assemble the PPSs - for (i = 0; i < track.pps.length; i++) { - data = track.pps[i]; - len = data.byteLength; - pps.push(len >>> 8 & 0xff); - pps.push(len & 0xff); - pps = pps.concat(Array.prototype.slice.call(data)); - } - const avcc = MP4.box(MP4.types.avcC, new Uint8Array([0x01, - // version - sps[3], - // profile - sps[4], - // profile compat - sps[5], - // level - 0xfc | 3, - // lengthSizeMinusOne, hard-coded to 4 bytes - 0xe0 | track.sps.length // 3bit reserved (111) + numOfSequenceParameterSets - ].concat(sps).concat([track.pps.length // numOfPictureParameterSets - ]).concat(pps))); // "PPS" - const width = track.width; - const height = track.height; - const hSpacing = track.pixelRatio[0]; - const vSpacing = track.pixelRatio[1]; - return MP4.box(MP4.types.avc1, new Uint8Array([0x00, 0x00, 0x00, - // reserved - 0x00, 0x00, 0x00, - // reserved - 0x00, 0x01, - // data_reference_index - 0x00, 0x00, - // pre_defined - 0x00, 0x00, - // reserved - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // pre_defined - width >> 8 & 0xff, width & 0xff, - // width - height >> 8 & 0xff, height & 0xff, - // height - 0x00, 0x48, 0x00, 0x00, - // horizresolution - 0x00, 0x48, 0x00, 0x00, - // vertresolution - 0x00, 0x00, 0x00, 0x00, - // reserved - 0x00, 0x01, - // frame_count - 0x12, 0x64, 0x61, 0x69, 0x6c, - // dailymotion/hls.js - 0x79, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x68, 0x6c, 0x73, 0x2e, 0x6a, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // compressorname - 0x00, 0x18, - // depth = 24 - 0x11, 0x11]), - // pre_defined = -1 - avcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80, - // bufferSizeDB - 0x00, 0x2d, 0xc6, 0xc0, - // maxBitrate - 0x00, 0x2d, 0xc6, 0xc0])), - // avgBitrate - MP4.box(MP4.types.pasp, new Uint8Array([hSpacing >> 24, - // hSpacing - hSpacing >> 16 & 0xff, hSpacing >> 8 & 0xff, hSpacing & 0xff, vSpacing >> 24, - // vSpacing - vSpacing >> 16 & 0xff, vSpacing >> 8 & 0xff, vSpacing & 0xff]))); - } - static esds(track) { - const configlen = track.config.length; - return new Uint8Array([0x00, - // version 0 - 0x00, 0x00, 0x00, - // flags - - 0x03, - // descriptor_type - 0x17 + configlen, - // length - 0x00, 0x01, - // es_id - 0x00, - // stream_priority - - 0x04, - // descriptor_type - 0x0f + configlen, - // length - 0x40, - // codec : mpeg4_audio - 0x15, - // stream_type - 0x00, 0x00, 0x00, - // buffer_size - 0x00, 0x00, 0x00, 0x00, - // maxBitrate - 0x00, 0x00, 0x00, 0x00, - // avgBitrate - - 0x05 // descriptor_type - ].concat([configlen]).concat(track.config).concat([0x06, 0x01, 0x02])); // GASpecificConfig)); // length + audio config descriptor - } - static audioStsd(track) { - const samplerate = track.samplerate; - return new Uint8Array([0x00, 0x00, 0x00, - // reserved - 0x00, 0x00, 0x00, - // reserved - 0x00, 0x01, - // data_reference_index - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // reserved - 0x00, track.channelCount, - // channelcount - 0x00, 0x10, - // sampleSize:16bits - 0x00, 0x00, 0x00, 0x00, - // reserved2 - samplerate >> 8 & 0xff, samplerate & 0xff, - // - 0x00, 0x00]); - } - static mp4a(track) { - return MP4.box(MP4.types.mp4a, MP4.audioStsd(track), MP4.box(MP4.types.esds, MP4.esds(track))); - } - static mp3(track) { - return MP4.box(MP4.types['.mp3'], MP4.audioStsd(track)); - } - static ac3(track) { - return MP4.box(MP4.types['ac-3'], MP4.audioStsd(track), MP4.box(MP4.types.dac3, track.config)); - } - static stsd(track) { - if (track.type === 'audio') { - if (track.segmentCodec === 'mp3' && track.codec === 'mp3') { - return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp3(track)); - } - if (track.segmentCodec === 'ac3') { - return MP4.box(MP4.types.stsd, MP4.STSD, MP4.ac3(track)); - } - return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track)); - } else { - return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track)); - } - } - static tkhd(track) { - const id = track.id; - const duration = track.duration * track.timescale; - const width = track.width; - const height = track.height; - const upperWordDuration = Math.floor(duration / (UINT32_MAX + 1)); - const lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1)); - return MP4.box(MP4.types.tkhd, new Uint8Array([0x01, - // version 1 - 0x00, 0x00, 0x07, - // flags - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - // creation_time - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, - // modification_time - id >> 24 & 0xff, id >> 16 & 0xff, id >> 8 & 0xff, id & 0xff, - // track_ID - 0x00, 0x00, 0x00, 0x00, - // reserved - upperWordDuration >> 24, upperWordDuration >> 16 & 0xff, upperWordDuration >> 8 & 0xff, upperWordDuration & 0xff, lowerWordDuration >> 24, lowerWordDuration >> 16 & 0xff, lowerWordDuration >> 8 & 0xff, lowerWordDuration & 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // reserved - 0x00, 0x00, - // layer - 0x00, 0x00, - // alternate_group - 0x00, 0x00, - // non-audio track volume - 0x00, 0x00, - // reserved - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - // transformation: unity matrix - width >> 8 & 0xff, width & 0xff, 0x00, 0x00, - // width - height >> 8 & 0xff, height & 0xff, 0x00, 0x00 // height - ])); - } - static traf(track, baseMediaDecodeTime) { - const sampleDependencyTable = MP4.sdtp(track); - const id = track.id; - const upperWordBaseMediaDecodeTime = Math.floor(baseMediaDecodeTime / (UINT32_MAX + 1)); - const lowerWordBaseMediaDecodeTime = Math.floor(baseMediaDecodeTime % (UINT32_MAX + 1)); - return MP4.box(MP4.types.traf, MP4.box(MP4.types.tfhd, new Uint8Array([0x00, - // version 0 - 0x00, 0x00, 0x00, - // flags - id >> 24, id >> 16 & 0xff, id >> 8 & 0xff, id & 0xff // track_ID - ])), MP4.box(MP4.types.tfdt, new Uint8Array([0x01, - // version 1 - 0x00, 0x00, 0x00, - // flags - upperWordBaseMediaDecodeTime >> 24, upperWordBaseMediaDecodeTime >> 16 & 0xff, upperWordBaseMediaDecodeTime >> 8 & 0xff, upperWordBaseMediaDecodeTime & 0xff, lowerWordBaseMediaDecodeTime >> 24, lowerWordBaseMediaDecodeTime >> 16 & 0xff, lowerWordBaseMediaDecodeTime >> 8 & 0xff, lowerWordBaseMediaDecodeTime & 0xff])), MP4.trun(track, sampleDependencyTable.length + 16 + - // tfhd - 20 + - // tfdt - 8 + - // traf header - 16 + - // mfhd - 8 + - // moof header - 8), - // mdat header - sampleDependencyTable); - } - - /** - * Generate a track box. - * @param track a track definition - */ - static trak(track) { - track.duration = track.duration || 0xffffffff; - return MP4.box(MP4.types.trak, MP4.tkhd(track), MP4.mdia(track)); - } - static trex(track) { - const id = track.id; - return MP4.box(MP4.types.trex, new Uint8Array([0x00, - // version 0 - 0x00, 0x00, 0x00, - // flags - id >> 24, id >> 16 & 0xff, id >> 8 & 0xff, id & 0xff, - // track_ID - 0x00, 0x00, 0x00, 0x01, - // default_sample_description_index - 0x00, 0x00, 0x00, 0x00, - // default_sample_duration - 0x00, 0x00, 0x00, 0x00, - // default_sample_size - 0x00, 0x01, 0x00, 0x01 // default_sample_flags - ])); - } - static trun(track, offset) { - const samples = track.samples || []; - const len = samples.length; - const arraylen = 12 + 16 * len; - const array = new Uint8Array(arraylen); - let i; - let sample; - let duration; - let size; - let flags; - let cts; - offset += 8 + arraylen; - array.set([track.type === 'video' ? 0x01 : 0x00, - // version 1 for video with signed-int sample_composition_time_offset - 0x00, 0x0f, 0x01, - // flags - len >>> 24 & 0xff, len >>> 16 & 0xff, len >>> 8 & 0xff, len & 0xff, - // sample_count - offset >>> 24 & 0xff, offset >>> 16 & 0xff, offset >>> 8 & 0xff, offset & 0xff // data_offset - ], 0); - for (i = 0; i < len; i++) { - sample = samples[i]; - duration = sample.duration; - size = sample.size; - flags = sample.flags; - cts = sample.cts; - array.set([duration >>> 24 & 0xff, duration >>> 16 & 0xff, duration >>> 8 & 0xff, duration & 0xff, - // sample_duration - size >>> 24 & 0xff, size >>> 16 & 0xff, size >>> 8 & 0xff, size & 0xff, - // sample_size - flags.isLeading << 2 | flags.dependsOn, flags.isDependedOn << 6 | flags.hasRedundancy << 4 | flags.paddingValue << 1 | flags.isNonSync, flags.degradPrio & 0xf0 << 8, flags.degradPrio & 0x0f, - // sample_flags - cts >>> 24 & 0xff, cts >>> 16 & 0xff, cts >>> 8 & 0xff, cts & 0xff // sample_composition_time_offset - ], 12 + 16 * i); - } - return MP4.box(MP4.types.trun, array); - } - static initSegment(tracks) { - if (!MP4.types) { - MP4.init(); - } - const movie = MP4.moov(tracks); - const result = appendUint8Array(MP4.FTYP, movie); - return result; - } -} -MP4.types = void 0; -MP4.HDLR_TYPES = void 0; -MP4.STTS = void 0; -MP4.STSC = void 0; -MP4.STCO = void 0; -MP4.STSZ = void 0; -MP4.VMHD = void 0; -MP4.SMHD = void 0; -MP4.STSD = void 0; -MP4.FTYP = void 0; -MP4.DINF = void 0; - -const MPEG_TS_CLOCK_FREQ_HZ = 90000; -function toTimescaleFromBase(baseTime, destScale, srcBase = 1, round = false) { - const result = baseTime * destScale * srcBase; // equivalent to `(value * scale) / (1 / base)` - return round ? Math.round(result) : result; -} -function toTimescaleFromScale(baseTime, destScale, srcScale = 1, round = false) { - return toTimescaleFromBase(baseTime, destScale, 1 / srcScale, round); -} -function toMsFromMpegTsClock(baseTime, round = false) { - return toTimescaleFromBase(baseTime, 1000, 1 / MPEG_TS_CLOCK_FREQ_HZ, round); -} -function toMpegTsClockFromTimescale(baseTime, srcScale = 1) { - return toTimescaleFromBase(baseTime, MPEG_TS_CLOCK_FREQ_HZ, 1 / srcScale); -} - -const MAX_SILENT_FRAME_DURATION = 10 * 1000; // 10 seconds -const AAC_SAMPLES_PER_FRAME = 1024; -const MPEG_AUDIO_SAMPLE_PER_FRAME = 1152; -const AC3_SAMPLES_PER_FRAME = 1536; -let chromeVersion = null; -let safariWebkitVersion = null; -class MP4Remuxer { - constructor(observer, config, typeSupported, vendor = '') { - this.observer = void 0; - this.config = void 0; - this.typeSupported = void 0; - this.ISGenerated = false; - this._initPTS = null; - this._initDTS = null; - this.nextAvcDts = null; - this.nextAudioPts = null; - this.videoSampleDuration = null; - this.isAudioContiguous = false; - this.isVideoContiguous = false; - this.videoTrackConfig = void 0; - this.observer = observer; - this.config = config; - this.typeSupported = typeSupported; - this.ISGenerated = false; - if (chromeVersion === null) { - const userAgent = navigator.userAgent || ''; - const result = userAgent.match(/Chrome\/(\d+)/i); - chromeVersion = result ? parseInt(result[1]) : 0; - } - if (safariWebkitVersion === null) { - const result = navigator.userAgent.match(/Safari\/(\d+)/i); - safariWebkitVersion = result ? parseInt(result[1]) : 0; - } - } - destroy() { - // @ts-ignore - this.config = this.videoTrackConfig = this._initPTS = this._initDTS = null; - } - resetTimeStamp(defaultTimeStamp) { - logger.log('[mp4-remuxer]: initPTS & initDTS reset'); - this._initPTS = this._initDTS = defaultTimeStamp; - } - resetNextTimestamp() { - logger.log('[mp4-remuxer]: reset next timestamp'); - this.isVideoContiguous = false; - this.isAudioContiguous = false; - } - resetInitSegment() { - logger.log('[mp4-remuxer]: ISGenerated flag reset'); - this.ISGenerated = false; - this.videoTrackConfig = undefined; - } - getVideoStartPts(videoSamples) { - let rolloverDetected = false; - const startPTS = videoSamples.reduce((minPTS, sample) => { - const delta = sample.pts - minPTS; - if (delta < -4294967296) { - // 2^32, see PTSNormalize for reasoning, but we're hitting a rollover here, and we don't want that to impact the timeOffset calculation - rolloverDetected = true; - return normalizePts(minPTS, sample.pts); - } else if (delta > 0) { - return minPTS; - } else { - return sample.pts; - } - }, videoSamples[0].pts); - if (rolloverDetected) { - logger.debug('PTS rollover detected'); - } - return startPTS; - } - remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, accurateTimeOffset, flush, playlistType) { - let video; - let audio; - let initSegment; - let text; - let id3; - let independent; - let audioTimeOffset = timeOffset; - let videoTimeOffset = timeOffset; - - // If we're remuxing audio and video progressively, wait until we've received enough samples for each track before proceeding. - // This is done to synchronize the audio and video streams. We know if the current segment will have samples if the "pid" - // parameter is greater than -1. The pid is set when the PMT is parsed, which contains the tracks list. - // However, if the initSegment has already been generated, or we've reached the end of a segment (flush), - // then we can remux one track without waiting for the other. - const hasAudio = audioTrack.pid > -1; - const hasVideo = videoTrack.pid > -1; - const length = videoTrack.samples.length; - const enoughAudioSamples = audioTrack.samples.length > 0; - const enoughVideoSamples = flush && length > 0 || length > 1; - const canRemuxAvc = (!hasAudio || enoughAudioSamples) && (!hasVideo || enoughVideoSamples) || this.ISGenerated || flush; - if (canRemuxAvc) { - if (this.ISGenerated) { - var _videoTrack$pixelRati, _config$pixelRatio, _videoTrack$pixelRati2, _config$pixelRatio2; - const config = this.videoTrackConfig; - if (config && (videoTrack.width !== config.width || videoTrack.height !== config.height || ((_videoTrack$pixelRati = videoTrack.pixelRatio) == null ? void 0 : _videoTrack$pixelRati[0]) !== ((_config$pixelRatio = config.pixelRatio) == null ? void 0 : _config$pixelRatio[0]) || ((_videoTrack$pixelRati2 = videoTrack.pixelRatio) == null ? void 0 : _videoTrack$pixelRati2[1]) !== ((_config$pixelRatio2 = config.pixelRatio) == null ? void 0 : _config$pixelRatio2[1]))) { - this.resetInitSegment(); - } - } else { - initSegment = this.generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset); - } - const isVideoContiguous = this.isVideoContiguous; - let firstKeyFrameIndex = -1; - let firstKeyFramePTS; - if (enoughVideoSamples) { - firstKeyFrameIndex = findKeyframeIndex(videoTrack.samples); - if (!isVideoContiguous && this.config.forceKeyFrameOnDiscontinuity) { - independent = true; - if (firstKeyFrameIndex > 0) { - logger.warn(`[mp4-remuxer]: Dropped ${firstKeyFrameIndex} out of ${length} video samples due to a missing keyframe`); - const startPTS = this.getVideoStartPts(videoTrack.samples); - videoTrack.samples = videoTrack.samples.slice(firstKeyFrameIndex); - videoTrack.dropped += firstKeyFrameIndex; - videoTimeOffset += (videoTrack.samples[0].pts - startPTS) / videoTrack.inputTimeScale; - firstKeyFramePTS = videoTimeOffset; - } else if (firstKeyFrameIndex === -1) { - logger.warn(`[mp4-remuxer]: No keyframe found out of ${length} video samples`); - independent = false; - } - } - } - if (this.ISGenerated) { - if (enoughAudioSamples && enoughVideoSamples) { - // timeOffset is expected to be the offset of the first timestamp of this fragment (first DTS) - // if first audio DTS is not aligned with first video DTS then we need to take that into account - // when providing timeOffset to remuxAudio / remuxVideo. if we don't do that, there might be a permanent / small - // drift between audio and video streams - const startPTS = this.getVideoStartPts(videoTrack.samples); - const tsDelta = normalizePts(audioTrack.samples[0].pts, startPTS) - startPTS; - const audiovideoTimestampDelta = tsDelta / videoTrack.inputTimeScale; - audioTimeOffset += Math.max(0, audiovideoTimestampDelta); - videoTimeOffset += Math.max(0, -audiovideoTimestampDelta); - } - - // Purposefully remuxing audio before video, so that remuxVideo can use nextAudioPts, which is calculated in remuxAudio. - if (enoughAudioSamples) { - // if initSegment was generated without audio samples, regenerate it again - if (!audioTrack.samplerate) { - logger.warn('[mp4-remuxer]: regenerate InitSegment as audio detected'); - initSegment = this.generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset); - } - audio = this.remuxAudio(audioTrack, audioTimeOffset, this.isAudioContiguous, accurateTimeOffset, hasVideo || enoughVideoSamples || playlistType === PlaylistLevelType.AUDIO ? videoTimeOffset : undefined); - if (enoughVideoSamples) { - const audioTrackLength = audio ? audio.endPTS - audio.startPTS : 0; - // if initSegment was generated without video samples, regenerate it again - if (!videoTrack.inputTimeScale) { - logger.warn('[mp4-remuxer]: regenerate InitSegment as video detected'); - initSegment = this.generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset); - } - video = this.remuxVideo(videoTrack, videoTimeOffset, isVideoContiguous, audioTrackLength); - } - } else if (enoughVideoSamples) { - video = this.remuxVideo(videoTrack, videoTimeOffset, isVideoContiguous, 0); - } - if (video) { - video.firstKeyFrame = firstKeyFrameIndex; - video.independent = firstKeyFrameIndex !== -1; - video.firstKeyFramePTS = firstKeyFramePTS; - } - } - } - - // Allow ID3 and text to remux, even if more audio/video samples are required - if (this.ISGenerated && this._initPTS && this._initDTS) { - if (id3Track.samples.length) { - id3 = flushTextTrackMetadataCueSamples(id3Track, timeOffset, this._initPTS, this._initDTS); - } - if (textTrack.samples.length) { - text = flushTextTrackUserdataCueSamples(textTrack, timeOffset, this._initPTS); - } - } - return { - audio, - video, - initSegment, - independent, - text, - id3 - }; - } - generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset) { - const audioSamples = audioTrack.samples; - const videoSamples = videoTrack.samples; - const typeSupported = this.typeSupported; - const tracks = {}; - const _initPTS = this._initPTS; - let computePTSDTS = !_initPTS || accurateTimeOffset; - let container = 'audio/mp4'; - let initPTS; - let initDTS; - let timescale; - if (computePTSDTS) { - initPTS = initDTS = Infinity; - } - if (audioTrack.config && audioSamples.length) { - // let's use audio sampling rate as MP4 time scale. - // rationale is that there is a integer nb of audio frames per audio sample (1024 for AAC) - // using audio sampling rate here helps having an integer MP4 frame duration - // this avoids potential rounding issue and AV sync issue - audioTrack.timescale = audioTrack.samplerate; - switch (audioTrack.segmentCodec) { - case 'mp3': - if (typeSupported.mpeg) { - // Chrome and Safari - container = 'audio/mpeg'; - audioTrack.codec = ''; - } else if (typeSupported.mp3) { - // Firefox - audioTrack.codec = 'mp3'; - } - break; - case 'ac3': - audioTrack.codec = 'ac-3'; - break; - } - tracks.audio = { - id: 'audio', - container: container, - codec: audioTrack.codec, - initSegment: audioTrack.segmentCodec === 'mp3' && typeSupported.mpeg ? new Uint8Array(0) : MP4.initSegment([audioTrack]), - metadata: { - channelCount: audioTrack.channelCount - } - }; - if (computePTSDTS) { - timescale = audioTrack.inputTimeScale; - if (!_initPTS || timescale !== _initPTS.timescale) { - // remember first PTS of this demuxing context. for audio, PTS = DTS - initPTS = initDTS = audioSamples[0].pts - Math.round(timescale * timeOffset); - } else { - computePTSDTS = false; - } - } - } - if (videoTrack.sps && videoTrack.pps && videoSamples.length) { - // let's use input time scale as MP4 video timescale - // we use input time scale straight away to avoid rounding issues on frame duration / cts computation - videoTrack.timescale = videoTrack.inputTimeScale; - tracks.video = { - id: 'main', - container: 'video/mp4', - codec: videoTrack.codec, - initSegment: MP4.initSegment([videoTrack]), - metadata: { - width: videoTrack.width, - height: videoTrack.height - } - }; - if (computePTSDTS) { - timescale = videoTrack.inputTimeScale; - if (!_initPTS || timescale !== _initPTS.timescale) { - const startPTS = this.getVideoStartPts(videoSamples); - const startOffset = Math.round(timescale * timeOffset); - initDTS = Math.min(initDTS, normalizePts(videoSamples[0].dts, startPTS) - startOffset); - initPTS = Math.min(initPTS, startPTS - startOffset); - } else { - computePTSDTS = false; - } - } - this.videoTrackConfig = { - width: videoTrack.width, - height: videoTrack.height, - pixelRatio: videoTrack.pixelRatio - }; - } - if (Object.keys(tracks).length) { - this.ISGenerated = true; - if (computePTSDTS) { - this._initPTS = { - baseTime: initPTS, - timescale: timescale - }; - this._initDTS = { - baseTime: initDTS, - timescale: timescale - }; - } else { - initPTS = timescale = undefined; - } - return { - tracks, - initPTS, - timescale - }; - } - } - remuxVideo(track, timeOffset, contiguous, audioTrackLength) { - const timeScale = track.inputTimeScale; - const inputSamples = track.samples; - const outputSamples = []; - const nbSamples = inputSamples.length; - const initPTS = this._initPTS; - let nextAvcDts = this.nextAvcDts; - let offset = 8; - let mp4SampleDuration = this.videoSampleDuration; - let firstDTS; - let lastDTS; - let minPTS = Number.POSITIVE_INFINITY; - let maxPTS = Number.NEGATIVE_INFINITY; - let sortSamples = false; - - // if parsed fragment is contiguous with last one, let's use last DTS value as reference - if (!contiguous || nextAvcDts === null) { - const pts = timeOffset * timeScale; - const cts = inputSamples[0].pts - normalizePts(inputSamples[0].dts, inputSamples[0].pts); - if (chromeVersion && nextAvcDts !== null && Math.abs(pts - cts - nextAvcDts) < 15000) { - // treat as contigous to adjust samples that would otherwise produce video buffer gaps in Chrome - contiguous = true; - } else { - // if not contiguous, let's use target timeOffset - nextAvcDts = pts - cts; - } - } - - // PTS is coded on 33bits, and can loop from -2^32 to 2^32 - // PTSNormalize will make PTS/DTS value monotonic, we use last known DTS value as reference value - const initTime = initPTS.baseTime * timeScale / initPTS.timescale; - for (let i = 0; i < nbSamples; i++) { - const sample = inputSamples[i]; - sample.pts = normalizePts(sample.pts - initTime, nextAvcDts); - sample.dts = normalizePts(sample.dts - initTime, nextAvcDts); - if (sample.dts < inputSamples[i > 0 ? i - 1 : i].dts) { - sortSamples = true; - } - } - - // sort video samples by DTS then PTS then demux id order - if (sortSamples) { - inputSamples.sort(function (a, b) { - const deltadts = a.dts - b.dts; - const deltapts = a.pts - b.pts; - return deltadts || deltapts; - }); - } - - // Get first/last DTS - firstDTS = inputSamples[0].dts; - lastDTS = inputSamples[inputSamples.length - 1].dts; - - // Sample duration (as expected by trun MP4 boxes), should be the delta between sample DTS - // set this constant duration as being the avg delta between consecutive DTS. - const inputDuration = lastDTS - firstDTS; - const averageSampleDuration = inputDuration ? Math.round(inputDuration / (nbSamples - 1)) : mp4SampleDuration || track.inputTimeScale / 30; - - // if fragment are contiguous, detect hole/overlapping between fragments - if (contiguous) { - // check timestamp continuity across consecutive fragments (this is to remove inter-fragment gap/hole) - const delta = firstDTS - nextAvcDts; - const foundHole = delta > averageSampleDuration; - const foundOverlap = delta < -1; - if (foundHole || foundOverlap) { - if (foundHole) { - logger.warn(`AVC: ${toMsFromMpegTsClock(delta, true)} ms (${delta}dts) hole between fragments detected at ${timeOffset.toFixed(3)}`); - } else { - logger.warn(`AVC: ${toMsFromMpegTsClock(-delta, true)} ms (${delta}dts) overlapping between fragments detected at ${timeOffset.toFixed(3)}`); - } - if (!foundOverlap || nextAvcDts >= inputSamples[0].pts || chromeVersion) { - firstDTS = nextAvcDts; - const firstPTS = inputSamples[0].pts - delta; - if (foundHole) { - inputSamples[0].dts = firstDTS; - inputSamples[0].pts = firstPTS; - } else { - for (let i = 0; i < inputSamples.length; i++) { - if (inputSamples[i].dts > firstPTS) { - break; - } - inputSamples[i].dts -= delta; - inputSamples[i].pts -= delta; - } - } - logger.log(`Video: Initial PTS/DTS adjusted: ${toMsFromMpegTsClock(firstPTS, true)}/${toMsFromMpegTsClock(firstDTS, true)}, delta: ${toMsFromMpegTsClock(delta, true)} ms`); - } - } - } - firstDTS = Math.max(0, firstDTS); - let nbNalu = 0; - let naluLen = 0; - let dtsStep = firstDTS; - for (let i = 0; i < nbSamples; i++) { - // compute total/avc sample length and nb of NAL units - const sample = inputSamples[i]; - const units = sample.units; - const nbUnits = units.length; - let sampleLen = 0; - for (let j = 0; j < nbUnits; j++) { - sampleLen += units[j].data.length; - } - naluLen += sampleLen; - nbNalu += nbUnits; - sample.length = sampleLen; - - // ensure sample monotonic DTS - if (sample.dts < dtsStep) { - sample.dts = dtsStep; - dtsStep += averageSampleDuration / 4 | 0 || 1; - } else { - dtsStep = sample.dts; - } - minPTS = Math.min(sample.pts, minPTS); - maxPTS = Math.max(sample.pts, maxPTS); - } - lastDTS = inputSamples[nbSamples - 1].dts; - - /* concatenate the video data and construct the mdat in place - (need 8 more bytes to fill length and mpdat type) */ - const mdatSize = naluLen + 4 * nbNalu + 8; - let mdat; - try { - mdat = new Uint8Array(mdatSize); - } catch (err) { - this.observer.emit(Events.ERROR, Events.ERROR, { - type: ErrorTypes.MUX_ERROR, - details: ErrorDetails.REMUX_ALLOC_ERROR, - fatal: false, - error: err, - bytes: mdatSize, - reason: `fail allocating video mdat ${mdatSize}` - }); - return; - } - const view = new DataView(mdat.buffer); - view.setUint32(0, mdatSize); - mdat.set(MP4.types.mdat, 4); - let stretchedLastFrame = false; - let minDtsDelta = Number.POSITIVE_INFINITY; - let minPtsDelta = Number.POSITIVE_INFINITY; - let maxDtsDelta = Number.NEGATIVE_INFINITY; - let maxPtsDelta = Number.NEGATIVE_INFINITY; - for (let i = 0; i < nbSamples; i++) { - const VideoSample = inputSamples[i]; - const VideoSampleUnits = VideoSample.units; - let mp4SampleLength = 0; - // convert NALU bitstream to MP4 format (prepend NALU with size field) - for (let j = 0, nbUnits = VideoSampleUnits.length; j < nbUnits; j++) { - const unit = VideoSampleUnits[j]; - const unitData = unit.data; - const unitDataLen = unit.data.byteLength; - view.setUint32(offset, unitDataLen); - offset += 4; - mdat.set(unitData, offset); - offset += unitDataLen; - mp4SampleLength += 4 + unitDataLen; - } - - // expected sample duration is the Decoding Timestamp diff of consecutive samples - let ptsDelta; - if (i < nbSamples - 1) { - mp4SampleDuration = inputSamples[i + 1].dts - VideoSample.dts; - ptsDelta = inputSamples[i + 1].pts - VideoSample.pts; - } else { - const config = this.config; - const lastFrameDuration = i > 0 ? VideoSample.dts - inputSamples[i - 1].dts : averageSampleDuration; - ptsDelta = i > 0 ? VideoSample.pts - inputSamples[i - 1].pts : averageSampleDuration; - if (config.stretchShortVideoTrack && this.nextAudioPts !== null) { - // In some cases, a segment's audio track duration may exceed the video track duration. - // Since we've already remuxed audio, and we know how long the audio track is, we look to - // see if the delta to the next segment is longer than maxBufferHole. - // If so, playback would potentially get stuck, so we artificially inflate - // the duration of the last frame to minimize any potential gap between segments. - const gapTolerance = Math.floor(config.maxBufferHole * timeScale); - const deltaToFrameEnd = (audioTrackLength ? minPTS + audioTrackLength * timeScale : this.nextAudioPts) - VideoSample.pts; - if (deltaToFrameEnd > gapTolerance) { - // We subtract lastFrameDuration from deltaToFrameEnd to try to prevent any video - // frame overlap. maxBufferHole should be >> lastFrameDuration anyway. - mp4SampleDuration = deltaToFrameEnd - lastFrameDuration; - if (mp4SampleDuration < 0) { - mp4SampleDuration = lastFrameDuration; - } else { - stretchedLastFrame = true; - } - logger.log(`[mp4-remuxer]: It is approximately ${deltaToFrameEnd / 90} ms to the next segment; using duration ${mp4SampleDuration / 90} ms for the last video frame.`); - } else { - mp4SampleDuration = lastFrameDuration; - } - } else { - mp4SampleDuration = lastFrameDuration; - } - } - const compositionTimeOffset = Math.round(VideoSample.pts - VideoSample.dts); - minDtsDelta = Math.min(minDtsDelta, mp4SampleDuration); - maxDtsDelta = Math.max(maxDtsDelta, mp4SampleDuration); - minPtsDelta = Math.min(minPtsDelta, ptsDelta); - maxPtsDelta = Math.max(maxPtsDelta, ptsDelta); - outputSamples.push(new Mp4Sample(VideoSample.key, mp4SampleDuration, mp4SampleLength, compositionTimeOffset)); - } - if (outputSamples.length) { - if (chromeVersion) { - if (chromeVersion < 70) { - // Chrome workaround, mark first sample as being a Random Access Point (keyframe) to avoid sourcebuffer append issue - // https://code.google.com/p/chromium/issues/detail?id=229412 - const flags = outputSamples[0].flags; - flags.dependsOn = 2; - flags.isNonSync = 0; - } - } else if (safariWebkitVersion) { - // Fix for "CNN special report, with CC" in test-streams (Safari browser only) - // Ignore DTS when frame durations are irregular. Safari MSE does not handle this leading to gaps. - if (maxPtsDelta - minPtsDelta < maxDtsDelta - minDtsDelta && averageSampleDuration / maxDtsDelta < 0.025 && outputSamples[0].cts === 0) { - logger.warn('Found irregular gaps in sample duration. Using PTS instead of DTS to determine MP4 sample duration.'); - let dts = firstDTS; - for (let i = 0, len = outputSamples.length; i < len; i++) { - const nextDts = dts + outputSamples[i].duration; - const pts = dts + outputSamples[i].cts; - if (i < len - 1) { - const nextPts = nextDts + outputSamples[i + 1].cts; - outputSamples[i].duration = nextPts - pts; - } else { - outputSamples[i].duration = i ? outputSamples[i - 1].duration : averageSampleDuration; - } - outputSamples[i].cts = 0; - dts = nextDts; - } - } - } - } - // next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale) - mp4SampleDuration = stretchedLastFrame || !mp4SampleDuration ? averageSampleDuration : mp4SampleDuration; - this.nextAvcDts = nextAvcDts = lastDTS + mp4SampleDuration; - this.videoSampleDuration = mp4SampleDuration; - this.isVideoContiguous = true; - const moof = MP4.moof(track.sequenceNumber++, firstDTS, _extends({}, track, { - samples: outputSamples - })); - const type = 'video'; - const data = { - data1: moof, - data2: mdat, - startPTS: minPTS / timeScale, - endPTS: (maxPTS + mp4SampleDuration) / timeScale, - startDTS: firstDTS / timeScale, - endDTS: nextAvcDts / timeScale, - type, - hasAudio: false, - hasVideo: true, - nb: outputSamples.length, - dropped: track.dropped - }; - track.samples = []; - track.dropped = 0; - return data; - } - getSamplesPerFrame(track) { - switch (track.segmentCodec) { - case 'mp3': - return MPEG_AUDIO_SAMPLE_PER_FRAME; - case 'ac3': - return AC3_SAMPLES_PER_FRAME; - default: - return AAC_SAMPLES_PER_FRAME; - } - } - remuxAudio(track, timeOffset, contiguous, accurateTimeOffset, videoTimeOffset) { - const inputTimeScale = track.inputTimeScale; - const mp4timeScale = track.samplerate ? track.samplerate : inputTimeScale; - const scaleFactor = inputTimeScale / mp4timeScale; - const mp4SampleDuration = this.getSamplesPerFrame(track); - const inputSampleDuration = mp4SampleDuration * scaleFactor; - const initPTS = this._initPTS; - const rawMPEG = track.segmentCodec === 'mp3' && this.typeSupported.mpeg; - const outputSamples = []; - const alignedWithVideo = videoTimeOffset !== undefined; - let inputSamples = track.samples; - let offset = rawMPEG ? 0 : 8; - let nextAudioPts = this.nextAudioPts || -1; - - // window.audioSamples ? window.audioSamples.push(inputSamples.map(s => s.pts)) : (window.audioSamples = [inputSamples.map(s => s.pts)]); - - // for audio samples, also consider consecutive fragments as being contiguous (even if a level switch occurs), - // for sake of clarity: - // consecutive fragments are frags with - // - less than 100ms gaps between new time offset (if accurate) and next expected PTS OR - // - less than 20 audio frames distance - // contiguous fragments are consecutive fragments from same quality level (same level, new SN = old SN + 1) - // this helps ensuring audio continuity - // and this also avoids audio glitches/cut when switching quality, or reporting wrong duration on first audio frame - const timeOffsetMpegTS = timeOffset * inputTimeScale; - const initTime = initPTS.baseTime * inputTimeScale / initPTS.timescale; - this.isAudioContiguous = contiguous = contiguous || inputSamples.length && nextAudioPts > 0 && (accurateTimeOffset && Math.abs(timeOffsetMpegTS - nextAudioPts) < 9000 || Math.abs(normalizePts(inputSamples[0].pts - initTime, timeOffsetMpegTS) - nextAudioPts) < 20 * inputSampleDuration); - - // compute normalized PTS - inputSamples.forEach(function (sample) { - sample.pts = normalizePts(sample.pts - initTime, timeOffsetMpegTS); - }); - if (!contiguous || nextAudioPts < 0) { - // filter out sample with negative PTS that are not playable anyway - // if we don't remove these negative samples, they will shift all audio samples forward. - // leading to audio overlap between current / next fragment - inputSamples = inputSamples.filter(sample => sample.pts >= 0); - - // in case all samples have negative PTS, and have been filtered out, return now - if (!inputSamples.length) { - return; - } - if (videoTimeOffset === 0) { - // Set the start to 0 to match video so that start gaps larger than inputSampleDuration are filled with silence - nextAudioPts = 0; - } else if (accurateTimeOffset && !alignedWithVideo) { - // When not seeking, not live, and LevelDetails.PTSKnown, use fragment start as predicted next audio PTS - nextAudioPts = Math.max(0, timeOffsetMpegTS); - } else { - // if frags are not contiguous and if we cant trust time offset, let's use first sample PTS as next audio PTS - nextAudioPts = inputSamples[0].pts; - } - } - - // If the audio track is missing samples, the frames seem to get "left-shifted" within the - // resulting mp4 segment, causing sync issues and leaving gaps at the end of the audio segment. - // In an effort to prevent this from happening, we inject frames here where there are gaps. - // When possible, we inject a silent frame; when that's not possible, we duplicate the last - // frame. - - if (track.segmentCodec === 'aac') { - const maxAudioFramesDrift = this.config.maxAudioFramesDrift; - for (let i = 0, nextPts = nextAudioPts; i < inputSamples.length; i++) { - // First, let's see how far off this frame is from where we expect it to be - const sample = inputSamples[i]; - const pts = sample.pts; - const delta = pts - nextPts; - const duration = Math.abs(1000 * delta / inputTimeScale); - - // When remuxing with video, if we're overlapping by more than a duration, drop this sample to stay in sync - if (delta <= -maxAudioFramesDrift * inputSampleDuration && alignedWithVideo) { - if (i === 0) { - logger.warn(`Audio frame @ ${(pts / inputTimeScale).toFixed(3)}s overlaps nextAudioPts by ${Math.round(1000 * delta / inputTimeScale)} ms.`); - this.nextAudioPts = nextAudioPts = nextPts = pts; - } - } // eslint-disable-line brace-style - - // Insert missing frames if: - // 1: We're more than maxAudioFramesDrift frame away - // 2: Not more than MAX_SILENT_FRAME_DURATION away - // 3: currentTime (aka nextPtsNorm) is not 0 - // 4: remuxing with video (videoTimeOffset !== undefined) - else if (delta >= maxAudioFramesDrift * inputSampleDuration && duration < MAX_SILENT_FRAME_DURATION && alignedWithVideo) { - let missing = Math.round(delta / inputSampleDuration); - // Adjust nextPts so that silent samples are aligned with media pts. This will prevent media samples from - // later being shifted if nextPts is based on timeOffset and delta is not a multiple of inputSampleDuration. - nextPts = pts - missing * inputSampleDuration; - if (nextPts < 0) { - missing--; - nextPts += inputSampleDuration; - } - if (i === 0) { - this.nextAudioPts = nextAudioPts = nextPts; - } - logger.warn(`[mp4-remuxer]: Injecting ${missing} audio frame @ ${(nextPts / inputTimeScale).toFixed(3)}s due to ${Math.round(1000 * delta / inputTimeScale)} ms gap.`); - for (let j = 0; j < missing; j++) { - const newStamp = Math.max(nextPts, 0); - let fillFrame = AAC.getSilentFrame(track.manifestCodec || track.codec, track.channelCount); - if (!fillFrame) { - logger.log('[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead.'); - fillFrame = sample.unit.subarray(); - } - inputSamples.splice(i, 0, { - unit: fillFrame, - pts: newStamp - }); - nextPts += inputSampleDuration; - i++; - } - } - sample.pts = nextPts; - nextPts += inputSampleDuration; - } - } - let firstPTS = null; - let lastPTS = null; - let mdat; - let mdatSize = 0; - let sampleLength = inputSamples.length; - while (sampleLength--) { - mdatSize += inputSamples[sampleLength].unit.byteLength; - } - for (let j = 0, _nbSamples = inputSamples.length; j < _nbSamples; j++) { - const audioSample = inputSamples[j]; - const unit = audioSample.unit; - let pts = audioSample.pts; - if (lastPTS !== null) { - // If we have more than one sample, set the duration of the sample to the "real" duration; the PTS diff with - // the previous sample - const prevSample = outputSamples[j - 1]; - prevSample.duration = Math.round((pts - lastPTS) / scaleFactor); - } else { - if (contiguous && track.segmentCodec === 'aac') { - // set PTS/DTS to expected PTS/DTS - pts = nextAudioPts; - } - // remember first PTS of our audioSamples - firstPTS = pts; - if (mdatSize > 0) { - /* concatenate the audio data and construct the mdat in place - (need 8 more bytes to fill length and mdat type) */ - mdatSize += offset; - try { - mdat = new Uint8Array(mdatSize); - } catch (err) { - this.observer.emit(Events.ERROR, Events.ERROR, { - type: ErrorTypes.MUX_ERROR, - details: ErrorDetails.REMUX_ALLOC_ERROR, - fatal: false, - error: err, - bytes: mdatSize, - reason: `fail allocating audio mdat ${mdatSize}` - }); - return; - } - if (!rawMPEG) { - const view = new DataView(mdat.buffer); - view.setUint32(0, mdatSize); - mdat.set(MP4.types.mdat, 4); - } - } else { - // no audio samples - return; - } - } - mdat.set(unit, offset); - const unitLen = unit.byteLength; - offset += unitLen; - // Default the sample's duration to the computed mp4SampleDuration, which will either be 1024 for AAC or 1152 for MPEG - // In the case that we have 1 sample, this will be the duration. If we have more than one sample, the duration - // becomes the PTS diff with the previous sample - outputSamples.push(new Mp4Sample(true, mp4SampleDuration, unitLen, 0)); - lastPTS = pts; - } - - // We could end up with no audio samples if all input samples were overlapping with the previously remuxed ones - const nbSamples = outputSamples.length; - if (!nbSamples) { - return; - } - - // The next audio sample PTS should be equal to last sample PTS + duration - const lastSample = outputSamples[outputSamples.length - 1]; - this.nextAudioPts = nextAudioPts = lastPTS + scaleFactor * lastSample.duration; - - // Set the track samples from inputSamples to outputSamples before remuxing - const moof = rawMPEG ? new Uint8Array(0) : MP4.moof(track.sequenceNumber++, firstPTS / scaleFactor, _extends({}, track, { - samples: outputSamples - })); - - // Clear the track samples. This also clears the samples array in the demuxer, since the reference is shared - track.samples = []; - const start = firstPTS / inputTimeScale; - const end = nextAudioPts / inputTimeScale; - const type = 'audio'; - const audioData = { - data1: moof, - data2: mdat, - startPTS: start, - endPTS: end, - startDTS: start, - endDTS: end, - type, - hasAudio: true, - hasVideo: false, - nb: nbSamples - }; - this.isAudioContiguous = true; - return audioData; - } - remuxEmptyAudio(track, timeOffset, contiguous, videoData) { - const inputTimeScale = track.inputTimeScale; - const mp4timeScale = track.samplerate ? track.samplerate : inputTimeScale; - const scaleFactor = inputTimeScale / mp4timeScale; - const nextAudioPts = this.nextAudioPts; - // sync with video's timestamp - const initDTS = this._initDTS; - const init90kHz = initDTS.baseTime * 90000 / initDTS.timescale; - const startDTS = (nextAudioPts !== null ? nextAudioPts : videoData.startDTS * inputTimeScale) + init90kHz; - const endDTS = videoData.endDTS * inputTimeScale + init90kHz; - // one sample's duration value - const frameDuration = scaleFactor * AAC_SAMPLES_PER_FRAME; - // samples count of this segment's duration - const nbSamples = Math.ceil((endDTS - startDTS) / frameDuration); - // silent frame - const silentFrame = AAC.getSilentFrame(track.manifestCodec || track.codec, track.channelCount); - logger.warn('[mp4-remuxer]: remux empty Audio'); - // Can't remux if we can't generate a silent frame... - if (!silentFrame) { - logger.trace('[mp4-remuxer]: Unable to remuxEmptyAudio since we were unable to get a silent frame for given audio codec'); - return; - } - const samples = []; - for (let i = 0; i < nbSamples; i++) { - const stamp = startDTS + i * frameDuration; - samples.push({ - unit: silentFrame, - pts: stamp, - dts: stamp - }); - } - track.samples = samples; - return this.remuxAudio(track, timeOffset, contiguous, false); - } -} -function normalizePts(value, reference) { - let offset; - if (reference === null) { - return value; - } - if (reference < value) { - // - 2^33 - offset = -8589934592; - } else { - // + 2^33 - offset = 8589934592; - } - /* PTS is 33bit (from 0 to 2^33 -1) - if diff between value and reference is bigger than half of the amplitude (2^32) then it means that - PTS looping occured. fill the gap */ - while (Math.abs(value - reference) > 4294967296) { - value += offset; - } - return value; -} -function findKeyframeIndex(samples) { - for (let i = 0; i < samples.length; i++) { - if (samples[i].key) { - return i; - } - } - return -1; -} -function flushTextTrackMetadataCueSamples(track, timeOffset, initPTS, initDTS) { - const length = track.samples.length; - if (!length) { - return; - } - const inputTimeScale = track.inputTimeScale; - for (let index = 0; index < length; index++) { - const sample = track.samples[index]; - // setting id3 pts, dts to relative time - // using this._initPTS and this._initDTS to calculate relative time - sample.pts = normalizePts(sample.pts - initPTS.baseTime * inputTimeScale / initPTS.timescale, timeOffset * inputTimeScale) / inputTimeScale; - sample.dts = normalizePts(sample.dts - initDTS.baseTime * inputTimeScale / initDTS.timescale, timeOffset * inputTimeScale) / inputTimeScale; - } - const samples = track.samples; - track.samples = []; - return { - samples - }; -} -function flushTextTrackUserdataCueSamples(track, timeOffset, initPTS) { - const length = track.samples.length; - if (!length) { - return; - } - const inputTimeScale = track.inputTimeScale; - for (let index = 0; index < length; index++) { - const sample = track.samples[index]; - // setting text pts, dts to relative time - // using this._initPTS and this._initDTS to calculate relative time - sample.pts = normalizePts(sample.pts - initPTS.baseTime * inputTimeScale / initPTS.timescale, timeOffset * inputTimeScale) / inputTimeScale; - } - track.samples.sort((a, b) => a.pts - b.pts); - const samples = track.samples; - track.samples = []; - return { - samples - }; -} -class Mp4Sample { - constructor(isKeyframe, duration, size, cts) { - this.size = void 0; - this.duration = void 0; - this.cts = void 0; - this.flags = void 0; - this.duration = duration; - this.size = size; - this.cts = cts; - this.flags = { - isLeading: 0, - isDependedOn: 0, - hasRedundancy: 0, - degradPrio: 0, - dependsOn: isKeyframe ? 2 : 1, - isNonSync: isKeyframe ? 0 : 1 - }; - } -} - -class PassThroughRemuxer { - constructor() { - this.emitInitSegment = false; - this.audioCodec = void 0; - this.videoCodec = void 0; - this.initData = void 0; - this.initPTS = null; - this.initTracks = void 0; - this.lastEndTime = null; - } - destroy() {} - resetTimeStamp(defaultInitPTS) { - this.initPTS = defaultInitPTS; - this.lastEndTime = null; - } - resetNextTimestamp() { - this.lastEndTime = null; - } - resetInitSegment(initSegment, audioCodec, videoCodec, decryptdata) { - this.audioCodec = audioCodec; - this.videoCodec = videoCodec; - this.generateInitSegment(patchEncyptionData(initSegment, decryptdata)); - this.emitInitSegment = true; - } - generateInitSegment(initSegment) { - let { - audioCodec, - videoCodec - } = this; - if (!(initSegment != null && initSegment.byteLength)) { - this.initTracks = undefined; - this.initData = undefined; - return; - } - const initData = this.initData = parseInitSegment(initSegment); - - // Get codec from initSegment or fallback to default - if (initData.audio) { - audioCodec = getParsedTrackCodec(initData.audio, ElementaryStreamTypes.AUDIO); - } - if (initData.video) { - videoCodec = getParsedTrackCodec(initData.video, ElementaryStreamTypes.VIDEO); - } - const tracks = {}; - if (initData.audio && initData.video) { - tracks.audiovideo = { - container: 'video/mp4', - codec: audioCodec + ',' + videoCodec, - initSegment, - id: 'main' - }; - } else if (initData.audio) { - tracks.audio = { - container: 'audio/mp4', - codec: audioCodec, - initSegment, - id: 'audio' - }; - } else if (initData.video) { - tracks.video = { - container: 'video/mp4', - codec: videoCodec, - initSegment, - id: 'main' - }; - } else { - logger.warn('[passthrough-remuxer.ts]: initSegment does not contain moov or trak boxes.'); - } - this.initTracks = tracks; - } - remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, accurateTimeOffset) { - var _initData, _initData2; - let { - initPTS, - lastEndTime - } = this; - const result = { - audio: undefined, - video: undefined, - text: textTrack, - id3: id3Track, - initSegment: undefined - }; - - // If we haven't yet set a lastEndDTS, or it was reset, set it to the provided timeOffset. We want to use the - // lastEndDTS over timeOffset whenever possible; during progressive playback, the media source will not update - // the media duration (which is what timeOffset is provided as) before we need to process the next chunk. - if (!isFiniteNumber(lastEndTime)) { - lastEndTime = this.lastEndTime = timeOffset || 0; - } - - // The binary segment data is added to the videoTrack in the mp4demuxer. We don't check to see if the data is only - // audio or video (or both); adding it to video was an arbitrary choice. - const data = videoTrack.samples; - if (!(data != null && data.length)) { - return result; - } - const initSegment = { - initPTS: undefined, - timescale: 1 - }; - let initData = this.initData; - if (!((_initData = initData) != null && _initData.length)) { - this.generateInitSegment(data); - initData = this.initData; - } - if (!((_initData2 = initData) != null && _initData2.length)) { - // We can't remux if the initSegment could not be generated - logger.warn('[passthrough-remuxer.ts]: Failed to generate initSegment.'); - return result; - } - if (this.emitInitSegment) { - initSegment.tracks = this.initTracks; - this.emitInitSegment = false; - } - const duration = getDuration(data, initData); - const startDTS = getStartDTS(initData, data); - const decodeTime = startDTS === null ? timeOffset : startDTS; - if (isInvalidInitPts(initPTS, decodeTime, timeOffset, duration) || initSegment.timescale !== initPTS.timescale && accurateTimeOffset) { - initSegment.initPTS = decodeTime - timeOffset; - if (initPTS && initPTS.timescale === 1) { - logger.warn(`Adjusting initPTS by ${initSegment.initPTS - initPTS.baseTime}`); - } - this.initPTS = initPTS = { - baseTime: initSegment.initPTS, - timescale: 1 - }; - } - const startTime = audioTrack ? decodeTime - initPTS.baseTime / initPTS.timescale : lastEndTime; - const endTime = startTime + duration; - offsetStartDTS(initData, data, initPTS.baseTime / initPTS.timescale); - if (duration > 0) { - this.lastEndTime = endTime; - } else { - logger.warn('Duration parsed from mp4 should be greater than zero'); - this.resetNextTimestamp(); - } - const hasAudio = !!initData.audio; - const hasVideo = !!initData.video; - let type = ''; - if (hasAudio) { - type += 'audio'; - } - if (hasVideo) { - type += 'video'; - } - const track = { - data1: data, - startPTS: startTime, - startDTS: startTime, - endPTS: endTime, - endDTS: endTime, - type, - hasAudio, - hasVideo, - nb: 1, - dropped: 0 - }; - result.audio = track.type === 'audio' ? track : undefined; - result.video = track.type !== 'audio' ? track : undefined; - result.initSegment = initSegment; - result.id3 = flushTextTrackMetadataCueSamples(id3Track, timeOffset, initPTS, initPTS); - if (textTrack.samples.length) { - result.text = flushTextTrackUserdataCueSamples(textTrack, timeOffset, initPTS); - } - return result; - } -} -function isInvalidInitPts(initPTS, startDTS, timeOffset, duration) { - if (initPTS === null) { - return true; - } - // InitPTS is invalid when distance from program would be more than segment duration or a minimum of one second - const minDuration = Math.max(duration, 1); - const startTime = startDTS - initPTS.baseTime / initPTS.timescale; - return Math.abs(startTime - timeOffset) > minDuration; -} -function getParsedTrackCodec(track, type) { - const parsedCodec = track == null ? void 0 : track.codec; - if (parsedCodec && parsedCodec.length > 4) { - return parsedCodec; - } - if (type === ElementaryStreamTypes.AUDIO) { - if (parsedCodec === 'ec-3' || parsedCodec === 'ac-3' || parsedCodec === 'alac') { - return parsedCodec; - } - if (parsedCodec === 'fLaC' || parsedCodec === 'Opus') { - // Opting not to get `preferManagedMediaSource` from player config for isSupported() check for simplicity - const preferManagedMediaSource = false; - return getCodecCompatibleName(parsedCodec, preferManagedMediaSource); - } - const result = 'mp4a.40.5'; - logger.info(`Parsed audio codec "${parsedCodec}" or audio object type not handled. Using "${result}"`); - return result; - } - // Provide defaults based on codec type - // This allows for some playback of some fmp4 playlists without CODECS defined in manifest - logger.warn(`Unhandled video codec "${parsedCodec}"`); - if (parsedCodec === 'hvc1' || parsedCodec === 'hev1') { - return 'hvc1.1.6.L120.90'; - } - if (parsedCodec === 'av01') { - return 'av01.0.04M.08'; - } - return 'avc1.42e01e'; -} - -let now; -// performance.now() not available on WebWorker, at least on Safari Desktop -try { - now = self.performance.now.bind(self.performance); -} catch (err) { - logger.debug('Unable to use Performance API on this environment'); - now = optionalSelf == null ? void 0 : optionalSelf.Date.now; -} -const muxConfig = [{ - demux: MP4Demuxer, - remux: PassThroughRemuxer -}, { - demux: TSDemuxer, - remux: MP4Remuxer -}, { - demux: AACDemuxer, - remux: MP4Remuxer -}, { - demux: MP3Demuxer, - remux: MP4Remuxer -}]; -{ - muxConfig.splice(2, 0, { - demux: AC3Demuxer, - remux: MP4Remuxer - }); -} -class Transmuxer { - constructor(observer, typeSupported, config, vendor, id) { - this.async = false; - this.observer = void 0; - this.typeSupported = void 0; - this.config = void 0; - this.vendor = void 0; - this.id = void 0; - this.demuxer = void 0; - this.remuxer = void 0; - this.decrypter = void 0; - this.probe = void 0; - this.decryptionPromise = null; - this.transmuxConfig = void 0; - this.currentTransmuxState = void 0; - this.observer = observer; - this.typeSupported = typeSupported; - this.config = config; - this.vendor = vendor; - this.id = id; - } - configure(transmuxConfig) { - this.transmuxConfig = transmuxConfig; - if (this.decrypter) { - this.decrypter.reset(); - } - } - push(data, decryptdata, chunkMeta, state) { - const stats = chunkMeta.transmuxing; - stats.executeStart = now(); - let uintData = new Uint8Array(data); - const { - currentTransmuxState, - transmuxConfig - } = this; - if (state) { - this.currentTransmuxState = state; - } - const { - contiguous, - discontinuity, - trackSwitch, - accurateTimeOffset, - timeOffset, - initSegmentChange - } = state || currentTransmuxState; - const { - audioCodec, - videoCodec, - defaultInitPts, - duration, - initSegmentData - } = transmuxConfig; - const keyData = getEncryptionType(uintData, decryptdata); - if (keyData && keyData.method === 'AES-128') { - const decrypter = this.getDecrypter(); - // Software decryption is synchronous; webCrypto is not - if (decrypter.isSync()) { - // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached - // data is handled in the flush() call - let decryptedData = decrypter.softwareDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer); - // For Low-Latency HLS Parts, decrypt in place, since part parsing is expected on push progress - const loadingParts = chunkMeta.part > -1; - if (loadingParts) { - decryptedData = decrypter.flush(); - } - if (!decryptedData) { - stats.executeEnd = now(); - return emptyResult(chunkMeta); - } - uintData = new Uint8Array(decryptedData); - } else { - this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer).then(decryptedData => { - // Calling push here is important; if flush() is called while this is still resolving, this ensures that - // the decrypted data has been transmuxed - const result = this.push(decryptedData, null, chunkMeta); - this.decryptionPromise = null; - return result; - }); - return this.decryptionPromise; - } - } - const resetMuxers = this.needsProbing(discontinuity, trackSwitch); - if (resetMuxers) { - const error = this.configureTransmuxer(uintData); - if (error) { - logger.warn(`[transmuxer] ${error.message}`); - this.observer.emit(Events.ERROR, Events.ERROR, { - type: ErrorTypes.MEDIA_ERROR, - details: ErrorDetails.FRAG_PARSING_ERROR, - fatal: false, - error, - reason: error.message - }); - stats.executeEnd = now(); - return emptyResult(chunkMeta); - } - } - if (discontinuity || trackSwitch || initSegmentChange || resetMuxers) { - this.resetInitSegment(initSegmentData, audioCodec, videoCodec, duration, decryptdata); - } - if (discontinuity || initSegmentChange || resetMuxers) { - this.resetInitialTimestamp(defaultInitPts); - } - if (!contiguous) { - this.resetContiguity(); - } - const result = this.transmux(uintData, keyData, timeOffset, accurateTimeOffset, chunkMeta); - const currentState = this.currentTransmuxState; - currentState.contiguous = true; - currentState.discontinuity = false; - currentState.trackSwitch = false; - stats.executeEnd = now(); - return result; - } - - // Due to data caching, flush calls can produce more than one TransmuxerResult (hence the Array type) - flush(chunkMeta) { - const stats = chunkMeta.transmuxing; - stats.executeStart = now(); - const { - decrypter, - currentTransmuxState, - decryptionPromise - } = this; - if (decryptionPromise) { - // Upon resolution, the decryption promise calls push() and returns its TransmuxerResult up the stack. Therefore - // only flushing is required for async decryption - return decryptionPromise.then(() => { - return this.flush(chunkMeta); - }); - } - const transmuxResults = []; - const { - timeOffset - } = currentTransmuxState; - if (decrypter) { - // The decrypter may have data cached, which needs to be demuxed. In this case we'll have two TransmuxResults - // This happens in the case that we receive only 1 push call for a segment (either for non-progressive downloads, - // or for progressive downloads with small segments) - const decryptedData = decrypter.flush(); - if (decryptedData) { - // Push always returns a TransmuxerResult if decryptdata is null - transmuxResults.push(this.push(decryptedData, null, chunkMeta)); - } - } - const { - demuxer, - remuxer - } = this; - if (!demuxer || !remuxer) { - // If probing failed, then Hls.js has been given content its not able to handle - stats.executeEnd = now(); - return [emptyResult(chunkMeta)]; - } - const demuxResultOrPromise = demuxer.flush(timeOffset); - if (isPromise(demuxResultOrPromise)) { - // Decrypt final SAMPLE-AES samples - return demuxResultOrPromise.then(demuxResult => { - this.flushRemux(transmuxResults, demuxResult, chunkMeta); - return transmuxResults; - }); - } - this.flushRemux(transmuxResults, demuxResultOrPromise, chunkMeta); - return transmuxResults; - } - flushRemux(transmuxResults, demuxResult, chunkMeta) { - const { - audioTrack, - videoTrack, - id3Track, - textTrack - } = demuxResult; - const { - accurateTimeOffset, - timeOffset - } = this.currentTransmuxState; - logger.log(`[transmuxer.ts]: Flushed fragment ${chunkMeta.sn}${chunkMeta.part > -1 ? ' p: ' + chunkMeta.part : ''} of level ${chunkMeta.level}`); - const remuxResult = this.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, accurateTimeOffset, true, this.id); - transmuxResults.push({ - remuxResult, - chunkMeta - }); - chunkMeta.transmuxing.executeEnd = now(); - } - resetInitialTimestamp(defaultInitPts) { - const { - demuxer, - remuxer - } = this; - if (!demuxer || !remuxer) { - return; - } - demuxer.resetTimeStamp(defaultInitPts); - remuxer.resetTimeStamp(defaultInitPts); - } - resetContiguity() { - const { - demuxer, - remuxer - } = this; - if (!demuxer || !remuxer) { - return; - } - demuxer.resetContiguity(); - remuxer.resetNextTimestamp(); - } - resetInitSegment(initSegmentData, audioCodec, videoCodec, trackDuration, decryptdata) { - const { - demuxer, - remuxer - } = this; - if (!demuxer || !remuxer) { - return; - } - demuxer.resetInitSegment(initSegmentData, audioCodec, videoCodec, trackDuration); - remuxer.resetInitSegment(initSegmentData, audioCodec, videoCodec, decryptdata); - } - destroy() { - if (this.demuxer) { - this.demuxer.destroy(); - this.demuxer = undefined; - } - if (this.remuxer) { - this.remuxer.destroy(); - this.remuxer = undefined; - } - } - transmux(data, keyData, timeOffset, accurateTimeOffset, chunkMeta) { - let result; - if (keyData && keyData.method === 'SAMPLE-AES') { - result = this.transmuxSampleAes(data, keyData, timeOffset, accurateTimeOffset, chunkMeta); - } else { - result = this.transmuxUnencrypted(data, timeOffset, accurateTimeOffset, chunkMeta); - } - return result; - } - transmuxUnencrypted(data, timeOffset, accurateTimeOffset, chunkMeta) { - const { - audioTrack, - videoTrack, - id3Track, - textTrack - } = this.demuxer.demux(data, timeOffset, false, !this.config.progressive); - const remuxResult = this.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, accurateTimeOffset, false, this.id); - return { - remuxResult, - chunkMeta - }; - } - transmuxSampleAes(data, decryptData, timeOffset, accurateTimeOffset, chunkMeta) { - return this.demuxer.demuxSampleAes(data, decryptData, timeOffset).then(demuxResult => { - const remuxResult = this.remuxer.remux(demuxResult.audioTrack, demuxResult.videoTrack, demuxResult.id3Track, demuxResult.textTrack, timeOffset, accurateTimeOffset, false, this.id); - return { - remuxResult, - chunkMeta - }; - }); - } - configureTransmuxer(data) { - const { - config, - observer, - typeSupported, - vendor - } = this; - // probe for content type - let mux; - for (let i = 0, len = muxConfig.length; i < len; i++) { - var _muxConfig$i$demux; - if ((_muxConfig$i$demux = muxConfig[i].demux) != null && _muxConfig$i$demux.probe(data)) { - mux = muxConfig[i]; - break; - } - } - if (!mux) { - return new Error('Failed to find demuxer by probing fragment data'); - } - // so let's check that current remuxer and demuxer are still valid - const demuxer = this.demuxer; - const remuxer = this.remuxer; - const Remuxer = mux.remux; - const Demuxer = mux.demux; - if (!remuxer || !(remuxer instanceof Remuxer)) { - this.remuxer = new Remuxer(observer, config, typeSupported, vendor); - } - if (!demuxer || !(demuxer instanceof Demuxer)) { - this.demuxer = new Demuxer(observer, config, typeSupported); - this.probe = Demuxer.probe; - } - } - needsProbing(discontinuity, trackSwitch) { - // in case of continuity change, or track switch - // we might switch from content type (AAC container to TS container, or TS to fmp4 for example) - return !this.demuxer || !this.remuxer || discontinuity || trackSwitch; - } - getDecrypter() { - let decrypter = this.decrypter; - if (!decrypter) { - decrypter = this.decrypter = new Decrypter(this.config); - } - return decrypter; - } -} -function getEncryptionType(data, decryptData) { - let encryptionType = null; - if (data.byteLength > 0 && (decryptData == null ? void 0 : decryptData.key) != null && decryptData.iv !== null && decryptData.method != null) { - encryptionType = decryptData; - } - return encryptionType; -} -const emptyResult = chunkMeta => ({ - remuxResult: {}, - chunkMeta -}); -function isPromise(p) { - return 'then' in p && p.then instanceof Function; -} -class TransmuxConfig { - constructor(audioCodec, videoCodec, initSegmentData, duration, defaultInitPts) { - this.audioCodec = void 0; - this.videoCodec = void 0; - this.initSegmentData = void 0; - this.duration = void 0; - this.defaultInitPts = void 0; - this.audioCodec = audioCodec; - this.videoCodec = videoCodec; - this.initSegmentData = initSegmentData; - this.duration = duration; - this.defaultInitPts = defaultInitPts || null; - } -} -class TransmuxState { - constructor(discontinuity, contiguous, accurateTimeOffset, trackSwitch, timeOffset, initSegmentChange) { - this.discontinuity = void 0; - this.contiguous = void 0; - this.accurateTimeOffset = void 0; - this.trackSwitch = void 0; - this.timeOffset = void 0; - this.initSegmentChange = void 0; - this.discontinuity = discontinuity; - this.contiguous = contiguous; - this.accurateTimeOffset = accurateTimeOffset; - this.trackSwitch = trackSwitch; - this.timeOffset = timeOffset; - this.initSegmentChange = initSegmentChange; - } -} - -var eventemitter3 = {exports: {}}; - -(function (module) { - - var has = Object.prototype.hasOwnProperty - , prefix = '~'; - - /** - * Constructor to create a storage for our `EE` objects. - * An `Events` instance is a plain object whose properties are event names. - * - * @constructor - * @private - */ - function Events() {} - - // - // We try to not inherit from `Object.prototype`. In some engines creating an - // instance in this way is faster than calling `Object.create(null)` directly. - // If `Object.create(null)` is not supported we prefix the event names with a - // character to make sure that the built-in object properties are not - // overridden or used as an attack vector. - // - if (Object.create) { - Events.prototype = Object.create(null); - - // - // This hack is needed because the `__proto__` property is still inherited in - // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5. - // - if (!new Events().__proto__) prefix = false; - } - - /** - * Representation of a single event listener. - * - * @param {Function} fn The listener function. - * @param {*} context The context to invoke the listener with. - * @param {Boolean} [once=false] Specify if the listener is a one-time listener. - * @constructor - * @private - */ - function EE(fn, context, once) { - this.fn = fn; - this.context = context; - this.once = once || false; - } - - /** - * Add a listener for a given event. - * - * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. - * @param {(String|Symbol)} event The event name. - * @param {Function} fn The listener function. - * @param {*} context The context to invoke the listener with. - * @param {Boolean} once Specify if the listener is a one-time listener. - * @returns {EventEmitter} - * @private - */ - function addListener(emitter, event, fn, context, once) { - if (typeof fn !== 'function') { - throw new TypeError('The listener must be a function'); - } - - var listener = new EE(fn, context || emitter, once) - , evt = prefix ? prefix + event : event; - - if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++; - else if (!emitter._events[evt].fn) emitter._events[evt].push(listener); - else emitter._events[evt] = [emitter._events[evt], listener]; - - return emitter; - } - - /** - * Clear event by name. - * - * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. - * @param {(String|Symbol)} evt The Event name. - * @private - */ - function clearEvent(emitter, evt) { - if (--emitter._eventsCount === 0) emitter._events = new Events(); - else delete emitter._events[evt]; - } - - /** - * Minimal `EventEmitter` interface that is molded against the Node.js - * `EventEmitter` interface. - * - * @constructor - * @public - */ - function EventEmitter() { - this._events = new Events(); - this._eventsCount = 0; - } - - /** - * Return an array listing the events for which the emitter has registered - * listeners. - * - * @returns {Array} - * @public - */ - EventEmitter.prototype.eventNames = function eventNames() { - var names = [] - , events - , name; - - if (this._eventsCount === 0) return names; - - for (name in (events = this._events)) { - if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); - } - - if (Object.getOwnPropertySymbols) { - return names.concat(Object.getOwnPropertySymbols(events)); - } - - return names; - }; - - /** - * Return the listeners registered for a given event. - * - * @param {(String|Symbol)} event The event name. - * @returns {Array} The registered listeners. - * @public - */ - EventEmitter.prototype.listeners = function listeners(event) { - var evt = prefix ? prefix + event : event - , handlers = this._events[evt]; - - if (!handlers) return []; - if (handlers.fn) return [handlers.fn]; - - for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) { - ee[i] = handlers[i].fn; - } - - return ee; - }; - - /** - * Return the number of listeners listening to a given event. - * - * @param {(String|Symbol)} event The event name. - * @returns {Number} The number of listeners. - * @public - */ - EventEmitter.prototype.listenerCount = function listenerCount(event) { - var evt = prefix ? prefix + event : event - , listeners = this._events[evt]; - - if (!listeners) return 0; - if (listeners.fn) return 1; - return listeners.length; - }; - - /** - * Calls each of the listeners registered for a given event. - * - * @param {(String|Symbol)} event The event name. - * @returns {Boolean} `true` if the event had listeners, else `false`. - * @public - */ - EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { - var evt = prefix ? prefix + event : event; - - if (!this._events[evt]) return false; - - var listeners = this._events[evt] - , len = arguments.length - , args - , i; - - if (listeners.fn) { - if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); - - switch (len) { - case 1: return listeners.fn.call(listeners.context), true; - case 2: return listeners.fn.call(listeners.context, a1), true; - case 3: return listeners.fn.call(listeners.context, a1, a2), true; - case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; - case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; - case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; - } - - for (i = 1, args = new Array(len -1); i < len; i++) { - args[i - 1] = arguments[i]; - } - - listeners.fn.apply(listeners.context, args); - } else { - var length = listeners.length - , j; - - for (i = 0; i < length; i++) { - if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); - - switch (len) { - case 1: listeners[i].fn.call(listeners[i].context); break; - case 2: listeners[i].fn.call(listeners[i].context, a1); break; - case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; - case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; - default: - if (!args) for (j = 1, args = new Array(len -1); j < len; j++) { - args[j - 1] = arguments[j]; - } - - listeners[i].fn.apply(listeners[i].context, args); - } - } - } - - return true; - }; - - /** - * Add a listener for a given event. - * - * @param {(String|Symbol)} event The event name. - * @param {Function} fn The listener function. - * @param {*} [context=this] The context to invoke the listener with. - * @returns {EventEmitter} `this`. - * @public - */ - EventEmitter.prototype.on = function on(event, fn, context) { - return addListener(this, event, fn, context, false); - }; - - /** - * Add a one-time listener for a given event. - * - * @param {(String|Symbol)} event The event name. - * @param {Function} fn The listener function. - * @param {*} [context=this] The context to invoke the listener with. - * @returns {EventEmitter} `this`. - * @public - */ - EventEmitter.prototype.once = function once(event, fn, context) { - return addListener(this, event, fn, context, true); - }; - - /** - * Remove the listeners of a given event. - * - * @param {(String|Symbol)} event The event name. - * @param {Function} fn Only remove the listeners that match this function. - * @param {*} context Only remove the listeners that have this context. - * @param {Boolean} once Only remove one-time listeners. - * @returns {EventEmitter} `this`. - * @public - */ - EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) { - var evt = prefix ? prefix + event : event; - - if (!this._events[evt]) return this; - if (!fn) { - clearEvent(this, evt); - return this; - } - - var listeners = this._events[evt]; - - if (listeners.fn) { - if ( - listeners.fn === fn && - (!once || listeners.once) && - (!context || listeners.context === context) - ) { - clearEvent(this, evt); - } - } else { - for (var i = 0, events = [], length = listeners.length; i < length; i++) { - if ( - listeners[i].fn !== fn || - (once && !listeners[i].once) || - (context && listeners[i].context !== context) - ) { - events.push(listeners[i]); - } - } - - // - // Reset the array, or remove it completely if we have no more listeners. - // - if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; - else clearEvent(this, evt); - } - - return this; - }; - - /** - * Remove all listeners, or those of the specified event. - * - * @param {(String|Symbol)} [event] The event name. - * @returns {EventEmitter} `this`. - * @public - */ - EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) { - var evt; - - if (event) { - evt = prefix ? prefix + event : event; - if (this._events[evt]) clearEvent(this, evt); - } else { - this._events = new Events(); - this._eventsCount = 0; - } - - return this; - }; - - // - // Alias methods names because people roll like that. - // - EventEmitter.prototype.off = EventEmitter.prototype.removeListener; - EventEmitter.prototype.addListener = EventEmitter.prototype.on; - - // - // Expose the prefix. - // - EventEmitter.prefixed = prefix; - - // - // Allow `EventEmitter` to be imported as module namespace. - // - EventEmitter.EventEmitter = EventEmitter; - - // - // Expose the module. - // - { - module.exports = EventEmitter; - } -} (eventemitter3)); - -var eventemitter3Exports = eventemitter3.exports; -var EventEmitter = /*@__PURE__*/getDefaultExportFromCjs(eventemitter3Exports); - -class TransmuxerInterface { - constructor(hls, id, onTransmuxComplete, onFlush) { - this.error = null; - this.hls = void 0; - this.id = void 0; - this.observer = void 0; - this.frag = null; - this.part = null; - this.useWorker = void 0; - this.workerContext = null; - this.onwmsg = void 0; - this.transmuxer = null; - this.onTransmuxComplete = void 0; - this.onFlush = void 0; - const config = hls.config; - this.hls = hls; - this.id = id; - this.useWorker = !!config.enableWorker; - this.onTransmuxComplete = onTransmuxComplete; - this.onFlush = onFlush; - const forwardMessage = (ev, data) => { - data = data || {}; - data.frag = this.frag; - data.id = this.id; - if (ev === Events.ERROR) { - this.error = data.error; - } - this.hls.trigger(ev, data); - }; - - // forward events to main thread - this.observer = new EventEmitter(); - this.observer.on(Events.FRAG_DECRYPTED, forwardMessage); - this.observer.on(Events.ERROR, forwardMessage); - const MediaSource = getMediaSource(config.preferManagedMediaSource) || { - isTypeSupported: () => false - }; - const m2tsTypeSupported = { - mpeg: MediaSource.isTypeSupported('audio/mpeg'), - mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'), - ac3: MediaSource.isTypeSupported('audio/mp4; codecs="ac-3"') - }; - if (this.useWorker && typeof Worker !== 'undefined') { - const canCreateWorker = config.workerPath || hasUMDWorker(); - if (canCreateWorker) { - try { - if (config.workerPath) { - logger.log(`loading Web Worker ${config.workerPath} for "${id}"`); - this.workerContext = loadWorker(config.workerPath); - } else { - logger.log(`injecting Web Worker for "${id}"`); - this.workerContext = injectWorker(); - } - this.onwmsg = event => this.onWorkerMessage(event); - const { - worker - } = this.workerContext; - worker.addEventListener('message', this.onwmsg); - worker.onerror = event => { - const error = new Error(`${event.message} (${event.filename}:${event.lineno})`); - config.enableWorker = false; - logger.warn(`Error in "${id}" Web Worker, fallback to inline`); - this.hls.trigger(Events.ERROR, { - type: ErrorTypes.OTHER_ERROR, - details: ErrorDetails.INTERNAL_EXCEPTION, - fatal: false, - event: 'demuxerWorker', - error - }); - }; - worker.postMessage({ - cmd: 'init', - typeSupported: m2tsTypeSupported, - vendor: '', - id: id, - config: JSON.stringify(config) - }); - } catch (err) { - logger.warn(`Error setting up "${id}" Web Worker, fallback to inline`, err); - this.resetWorker(); - this.error = null; - this.transmuxer = new Transmuxer(this.observer, m2tsTypeSupported, config, '', id); - } - return; - } - } - this.transmuxer = new Transmuxer(this.observer, m2tsTypeSupported, config, '', id); - } - resetWorker() { - if (this.workerContext) { - const { - worker, - objectURL - } = this.workerContext; - if (objectURL) { - // revoke the Object URL that was used to create transmuxer worker, so as not to leak it - self.URL.revokeObjectURL(objectURL); - } - worker.removeEventListener('message', this.onwmsg); - worker.onerror = null; - worker.terminate(); - this.workerContext = null; - } - } - destroy() { - if (this.workerContext) { - this.resetWorker(); - this.onwmsg = undefined; - } else { - const transmuxer = this.transmuxer; - if (transmuxer) { - transmuxer.destroy(); - this.transmuxer = null; - } - } - const observer = this.observer; - if (observer) { - observer.removeAllListeners(); - } - this.frag = null; - // @ts-ignore - this.observer = null; - // @ts-ignore - this.hls = null; - } - push(data, initSegmentData, audioCodec, videoCodec, frag, part, duration, accurateTimeOffset, chunkMeta, defaultInitPTS) { - var _frag$initSegment, _lastFrag$initSegment; - chunkMeta.transmuxing.start = self.performance.now(); - const { - transmuxer - } = this; - const timeOffset = part ? part.start : frag.start; - // TODO: push "clear-lead" decrypt data for unencrypted fragments in streams with encrypted ones - const decryptdata = frag.decryptdata; - const lastFrag = this.frag; - const discontinuity = !(lastFrag && frag.cc === lastFrag.cc); - const trackSwitch = !(lastFrag && chunkMeta.level === lastFrag.level); - const snDiff = lastFrag ? chunkMeta.sn - lastFrag.sn : -1; - const partDiff = this.part ? chunkMeta.part - this.part.index : -1; - const progressive = snDiff === 0 && chunkMeta.id > 1 && chunkMeta.id === (lastFrag == null ? void 0 : lastFrag.stats.chunkCount); - const contiguous = !trackSwitch && (snDiff === 1 || snDiff === 0 && (partDiff === 1 || progressive && partDiff <= 0)); - const now = self.performance.now(); - if (trackSwitch || snDiff || frag.stats.parsing.start === 0) { - frag.stats.parsing.start = now; - } - if (part && (partDiff || !contiguous)) { - part.stats.parsing.start = now; - } - const initSegmentChange = !(lastFrag && ((_frag$initSegment = frag.initSegment) == null ? void 0 : _frag$initSegment.url) === ((_lastFrag$initSegment = lastFrag.initSegment) == null ? void 0 : _lastFrag$initSegment.url)); - const state = new TransmuxState(discontinuity, contiguous, accurateTimeOffset, trackSwitch, timeOffset, initSegmentChange); - if (!contiguous || discontinuity || initSegmentChange) { - logger.log(`[transmuxer-interface, ${frag.type}]: Starting new transmux session for sn: ${chunkMeta.sn} p: ${chunkMeta.part} level: ${chunkMeta.level} id: ${chunkMeta.id} - discontinuity: ${discontinuity} - trackSwitch: ${trackSwitch} - contiguous: ${contiguous} - accurateTimeOffset: ${accurateTimeOffset} - timeOffset: ${timeOffset} - initSegmentChange: ${initSegmentChange}`); - const config = new TransmuxConfig(audioCodec, videoCodec, initSegmentData, duration, defaultInitPTS); - this.configureTransmuxer(config); - } - this.frag = frag; - this.part = part; - - // Frags with sn of 'initSegment' are not transmuxed - if (this.workerContext) { - // post fragment payload as transferable objects for ArrayBuffer (no copy) - this.workerContext.worker.postMessage({ - cmd: 'demux', - data, - decryptdata, - chunkMeta, - state - }, data instanceof ArrayBuffer ? [data] : []); - } else if (transmuxer) { - const transmuxResult = transmuxer.push(data, decryptdata, chunkMeta, state); - if (isPromise(transmuxResult)) { - transmuxer.async = true; - transmuxResult.then(data => { - this.handleTransmuxComplete(data); - }).catch(error => { - this.transmuxerError(error, chunkMeta, 'transmuxer-interface push error'); - }); - } else { - transmuxer.async = false; - this.handleTransmuxComplete(transmuxResult); - } - } - } - flush(chunkMeta) { - chunkMeta.transmuxing.start = self.performance.now(); - const { - transmuxer - } = this; - if (this.workerContext) { - this.workerContext.worker.postMessage({ - cmd: 'flush', - chunkMeta - }); - } else if (transmuxer) { - let transmuxResult = transmuxer.flush(chunkMeta); - const asyncFlush = isPromise(transmuxResult); - if (asyncFlush || transmuxer.async) { - if (!isPromise(transmuxResult)) { - transmuxResult = Promise.resolve(transmuxResult); - } - transmuxResult.then(data => { - this.handleFlushResult(data, chunkMeta); - }).catch(error => { - this.transmuxerError(error, chunkMeta, 'transmuxer-interface flush error'); - }); - } else { - this.handleFlushResult(transmuxResult, chunkMeta); - } - } - } - transmuxerError(error, chunkMeta, reason) { - if (!this.hls) { - return; - } - this.error = error; - this.hls.trigger(Events.ERROR, { - type: ErrorTypes.MEDIA_ERROR, - details: ErrorDetails.FRAG_PARSING_ERROR, - chunkMeta, - frag: this.frag || undefined, - fatal: false, - error, - err: error, - reason - }); - } - handleFlushResult(results, chunkMeta) { - results.forEach(result => { - this.handleTransmuxComplete(result); - }); - this.onFlush(chunkMeta); - } - onWorkerMessage(event) { - const data = event.data; - if (!(data != null && data.event)) { - logger.warn(`worker message received with no ${data ? 'event name' : 'data'}`); - return; - } - const hls = this.hls; - if (!this.hls) { - return; - } - switch (data.event) { - case 'init': - { - var _this$workerContext; - const objectURL = (_this$workerContext = this.workerContext) == null ? void 0 : _this$workerContext.objectURL; - if (objectURL) { - // revoke the Object URL that was used to create transmuxer worker, so as not to leak it - self.URL.revokeObjectURL(objectURL); - } - break; - } - case 'transmuxComplete': - { - this.handleTransmuxComplete(data.data); - break; - } - case 'flush': - { - this.onFlush(data.data); - break; - } - - // pass logs from the worker thread to the main logger - case 'workerLog': - if (logger[data.data.logType]) { - logger[data.data.logType](data.data.message); - } - break; - default: - { - data.data = data.data || {}; - data.data.frag = this.frag; - data.data.id = this.id; - hls.trigger(data.event, data.data); - break; - } - } - } - configureTransmuxer(config) { - const { - transmuxer - } = this; - if (this.workerContext) { - this.workerContext.worker.postMessage({ - cmd: 'configure', - config - }); - } else if (transmuxer) { - transmuxer.configure(config); - } - } - handleTransmuxComplete(result) { - result.chunkMeta.transmuxing.end = self.performance.now(); - this.onTransmuxComplete(result); - } -} - -function subtitleOptionsIdentical(trackList1, trackList2) { - if (trackList1.length !== trackList2.length) { - return false; - } - for (let i = 0; i < trackList1.length; i++) { - if (!mediaAttributesIdentical(trackList1[i].attrs, trackList2[i].attrs)) { - return false; - } - } - return true; -} -function mediaAttributesIdentical(attrs1, attrs2, customAttributes) { - // Media options with the same rendition ID must be bit identical - const stableRenditionId = attrs1['STABLE-RENDITION-ID']; - if (stableRenditionId && !customAttributes) { - return stableRenditionId === attrs2['STABLE-RENDITION-ID']; - } - // When rendition ID is not present, compare attributes - return !(customAttributes || ['LANGUAGE', 'NAME', 'CHARACTERISTICS', 'AUTOSELECT', 'DEFAULT', 'FORCED', 'ASSOC-LANGUAGE']).some(subtitleAttribute => attrs1[subtitleAttribute] !== attrs2[subtitleAttribute]); -} -function subtitleTrackMatchesTextTrack(subtitleTrack, textTrack) { - return textTrack.label.toLowerCase() === subtitleTrack.name.toLowerCase() && (!textTrack.language || textTrack.language.toLowerCase() === (subtitleTrack.lang || '').toLowerCase()); -} - -const TICK_INTERVAL$2 = 100; // how often to tick in ms - -class AudioStreamController extends BaseStreamController { - constructor(hls, fragmentTracker, keyLoader) { - super(hls, fragmentTracker, keyLoader, '[audio-stream-controller]', PlaylistLevelType.AUDIO); - this.videoBuffer = null; - this.videoTrackCC = -1; - this.waitingVideoCC = -1; - this.bufferedTrack = null; - this.switchingTrack = null; - this.trackId = -1; - this.waitingData = null; - this.mainDetails = null; - this.flushing = false; - this.bufferFlushed = false; - this.cachedTrackLoadedData = null; - this._registerListeners(); - } - onHandlerDestroying() { - this._unregisterListeners(); - super.onHandlerDestroying(); - this.mainDetails = null; - this.bufferedTrack = null; - this.switchingTrack = null; - } - _registerListeners() { - const { - hls - } = this; - hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this); - hls.on(Events.AUDIO_TRACKS_UPDATED, this.onAudioTracksUpdated, this); - hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this); - hls.on(Events.AUDIO_TRACK_LOADED, this.onAudioTrackLoaded, this); - hls.on(Events.ERROR, this.onError, this); - hls.on(Events.BUFFER_RESET, this.onBufferReset, this); - hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this); - hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); - hls.on(Events.BUFFER_FLUSHED, this.onBufferFlushed, this); - hls.on(Events.INIT_PTS_FOUND, this.onInitPtsFound, this); - hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this); - } - _unregisterListeners() { - const { - hls - } = this; - hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this); - hls.off(Events.AUDIO_TRACKS_UPDATED, this.onAudioTracksUpdated, this); - hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this); - hls.off(Events.AUDIO_TRACK_LOADED, this.onAudioTrackLoaded, this); - hls.off(Events.ERROR, this.onError, this); - hls.off(Events.BUFFER_RESET, this.onBufferReset, this); - hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this); - hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); - hls.off(Events.BUFFER_FLUSHED, this.onBufferFlushed, this); - hls.off(Events.INIT_PTS_FOUND, this.onInitPtsFound, this); - hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this); - } - - // INIT_PTS_FOUND is triggered when the video track parsed in the stream-controller has a new PTS value - onInitPtsFound(event, { - frag, - id, - initPTS, - timescale - }) { - // Always update the new INIT PTS - // Can change due level switch - if (id === 'main') { - const cc = frag.cc; - this.initPTS[frag.cc] = { - baseTime: initPTS, - timescale - }; - this.log(`InitPTS for cc: ${cc} found from main: ${initPTS}`); - this.videoTrackCC = cc; - // If we are waiting, tick immediately to unblock audio fragment transmuxing - if (this.state === State.WAITING_INIT_PTS) { - this.tick(); - } - } - } - startLoad(startPosition) { - if (!this.levels) { - this.startPosition = startPosition; - this.state = State.STOPPED; - return; - } - const lastCurrentTime = this.lastCurrentTime; - this.stopLoad(); - this.setInterval(TICK_INTERVAL$2); - if (lastCurrentTime > 0 && startPosition === -1) { - this.log(`Override startPosition with lastCurrentTime @${lastCurrentTime.toFixed(3)}`); - startPosition = lastCurrentTime; - this.state = State.IDLE; - } else { - this.loadedmetadata = false; - this.state = State.WAITING_TRACK; - } - this.nextLoadPosition = this.startPosition = this.lastCurrentTime = startPosition; - this.tick(); - } - doTick() { - switch (this.state) { - case State.IDLE: - this.doTickIdle(); - break; - case State.WAITING_TRACK: - { - var _levels$trackId; - const { - levels, - trackId - } = this; - const details = levels == null ? void 0 : (_levels$trackId = levels[trackId]) == null ? void 0 : _levels$trackId.details; - if (details) { - if (this.waitForCdnTuneIn(details)) { - break; - } - this.state = State.WAITING_INIT_PTS; - } - break; - } - case State.FRAG_LOADING_WAITING_RETRY: - { - var _this$media; - const now = performance.now(); - const retryDate = this.retryDate; - // if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading - if (!retryDate || now >= retryDate || (_this$media = this.media) != null && _this$media.seeking) { - const { - levels, - trackId - } = this; - this.log('RetryDate reached, switch back to IDLE state'); - this.resetStartWhenNotLoaded((levels == null ? void 0 : levels[trackId]) || null); - this.state = State.IDLE; - } - break; - } - case State.WAITING_INIT_PTS: - { - // Ensure we don't get stuck in the WAITING_INIT_PTS state if the waiting frag CC doesn't match any initPTS - const waitingData = this.waitingData; - if (waitingData) { - const { - frag, - part, - cache, - complete - } = waitingData; - if (this.initPTS[frag.cc] !== undefined) { - this.waitingData = null; - this.waitingVideoCC = -1; - this.state = State.FRAG_LOADING; - const payload = cache.flush(); - const data = { - frag, - part, - payload, - networkDetails: null - }; - this._handleFragmentLoadProgress(data); - if (complete) { - super._handleFragmentLoadComplete(data); - } - } else if (this.videoTrackCC !== this.waitingVideoCC) { - // Drop waiting fragment if videoTrackCC has changed since waitingFragment was set and initPTS was not found - this.log(`Waiting fragment cc (${frag.cc}) cancelled because video is at cc ${this.videoTrackCC}`); - this.clearWaitingFragment(); - } else { - // Drop waiting fragment if an earlier fragment is needed - const pos = this.getLoadPosition(); - const bufferInfo = BufferHelper.bufferInfo(this.mediaBuffer, pos, this.config.maxBufferHole); - const waitingFragmentAtPosition = fragmentWithinToleranceTest(bufferInfo.end, this.config.maxFragLookUpTolerance, frag); - if (waitingFragmentAtPosition < 0) { - this.log(`Waiting fragment cc (${frag.cc}) @ ${frag.start} cancelled because another fragment at ${bufferInfo.end} is needed`); - this.clearWaitingFragment(); - } - } - } else { - this.state = State.IDLE; - } - } - } - this.onTickEnd(); - } - clearWaitingFragment() { - const waitingData = this.waitingData; - if (waitingData) { - this.fragmentTracker.removeFragment(waitingData.frag); - this.waitingData = null; - this.waitingVideoCC = -1; - this.state = State.IDLE; - } - } - resetLoadingState() { - this.clearWaitingFragment(); - super.resetLoadingState(); - } - onTickEnd() { - const { - media - } = this; - if (!(media != null && media.readyState)) { - // Exit early if we don't have media or if the media hasn't buffered anything yet (readyState 0) - return; - } - this.lastCurrentTime = media.currentTime; - } - doTickIdle() { - const { - hls, - levels, - media, - trackId - } = this; - const config = hls.config; - - // 1. if video not attached AND - // start fragment already requested OR start frag prefetch not enabled - // 2. if tracks or track not loaded and selected - // then exit loop - // => if media not attached but start frag prefetch is enabled and start frag not requested yet, we will not exit loop - if (!media && (this.startFragRequested || !config.startFragPrefetch) || !(levels != null && levels[trackId])) { - return; - } - const levelInfo = levels[trackId]; - const trackDetails = levelInfo.details; - if (!trackDetails || trackDetails.live && this.levelLastLoaded !== levelInfo || this.waitForCdnTuneIn(trackDetails)) { - this.state = State.WAITING_TRACK; - return; - } - const bufferable = this.mediaBuffer ? this.mediaBuffer : this.media; - if (this.bufferFlushed && bufferable) { - this.bufferFlushed = false; - this.afterBufferFlushed(bufferable, ElementaryStreamTypes.AUDIO, PlaylistLevelType.AUDIO); - } - const bufferInfo = this.getFwdBufferInfo(bufferable, PlaylistLevelType.AUDIO); - if (bufferInfo === null) { - return; - } - const { - bufferedTrack, - switchingTrack - } = this; - if (!switchingTrack && this._streamEnded(bufferInfo, trackDetails)) { - hls.trigger(Events.BUFFER_EOS, { - type: 'audio' - }); - this.state = State.ENDED; - return; - } - const mainBufferInfo = this.getFwdBufferInfo(this.videoBuffer ? this.videoBuffer : this.media, PlaylistLevelType.MAIN); - const bufferLen = bufferInfo.len; - const maxBufLen = this.getMaxBufferLength(mainBufferInfo == null ? void 0 : mainBufferInfo.len); - const fragments = trackDetails.fragments; - const start = fragments[0].start; - let targetBufferTime = this.flushing ? this.getLoadPosition() : bufferInfo.end; - if (switchingTrack && media) { - const pos = this.getLoadPosition(); - // STABLE - if (bufferedTrack && !mediaAttributesIdentical(switchingTrack.attrs, bufferedTrack.attrs)) { - targetBufferTime = pos; - } - // if currentTime (pos) is less than alt audio playlist start time, it means that alt audio is ahead of currentTime - if (trackDetails.PTSKnown && pos < start) { - // if everything is buffered from pos to start or if audio buffer upfront, let's seek to start - if (bufferInfo.end > start || bufferInfo.nextStart) { - this.log('Alt audio track ahead of main track, seek to start of alt audio track'); - media.currentTime = start + 0.05; - } - } - } - - // if buffer length is less than maxBufLen, or near the end, find a fragment to load - if (bufferLen >= maxBufLen && !switchingTrack && targetBufferTime < fragments[fragments.length - 1].start) { - return; - } - let frag = this.getNextFragment(targetBufferTime, trackDetails); - let atGap = false; - // Avoid loop loading by using nextLoadPosition set for backtracking and skipping consecutive GAP tags - if (frag && this.isLoopLoading(frag, targetBufferTime)) { - atGap = !!frag.gap; - frag = this.getNextFragmentLoopLoading(frag, trackDetails, bufferInfo, PlaylistLevelType.MAIN, maxBufLen); - } - if (!frag) { - this.bufferFlushed = true; - return; - } - - // Buffer audio up to one target duration ahead of main buffer - const atBufferSyncLimit = mainBufferInfo && frag.start > mainBufferInfo.end + trackDetails.targetduration; - if (atBufferSyncLimit || - // Or wait for main buffer after buffing some audio - !(mainBufferInfo != null && mainBufferInfo.len) && bufferInfo.len) { - // Check fragment-tracker for main fragments since GAP segments do not show up in bufferInfo - const mainFrag = this.getAppendedFrag(frag.start, PlaylistLevelType.MAIN); - if (mainFrag === null) { - return; - } - // Bridge gaps in main buffer - atGap || (atGap = !!mainFrag.gap || !!atBufferSyncLimit && mainBufferInfo.len === 0); - if (atBufferSyncLimit && !atGap || atGap && bufferInfo.nextStart && bufferInfo.nextStart < mainFrag.end) { - return; - } - } - this.loadFragment(frag, levelInfo, targetBufferTime); - } - getMaxBufferLength(mainBufferLength) { - const maxConfigBuffer = super.getMaxBufferLength(); - if (!mainBufferLength) { - return maxConfigBuffer; - } - return Math.min(Math.max(maxConfigBuffer, mainBufferLength), this.config.maxMaxBufferLength); - } - onMediaDetaching() { - this.videoBuffer = null; - this.bufferFlushed = this.flushing = false; - super.onMediaDetaching(); - } - onAudioTracksUpdated(event, { - audioTracks - }) { - // Reset tranxmuxer is essential for large context switches (Content Steering) - this.resetTransmuxer(); - this.levels = audioTracks.map(mediaPlaylist => new Level(mediaPlaylist)); - } - onAudioTrackSwitching(event, data) { - // if any URL found on new audio track, it is an alternate audio track - const altAudio = !!data.url; - this.trackId = data.id; - const { - fragCurrent - } = this; - if (fragCurrent) { - fragCurrent.abortRequests(); - this.removeUnbufferedFrags(fragCurrent.start); - } - this.resetLoadingState(); - // destroy useless transmuxer when switching audio to main - if (!altAudio) { - this.resetTransmuxer(); - } else { - // switching to audio track, start timer if not already started - this.setInterval(TICK_INTERVAL$2); - } - - // should we switch tracks ? - if (altAudio) { - this.switchingTrack = data; - // main audio track are handled by stream-controller, just do something if switching to alt audio track - this.state = State.IDLE; - this.flushAudioIfNeeded(data); - } else { - this.switchingTrack = null; - this.bufferedTrack = data; - this.state = State.STOPPED; - } - this.tick(); - } - onManifestLoading() { - this.fragmentTracker.removeAllFragments(); - this.startPosition = this.lastCurrentTime = 0; - this.bufferFlushed = this.flushing = false; - this.levels = this.mainDetails = this.waitingData = this.bufferedTrack = this.cachedTrackLoadedData = this.switchingTrack = null; - this.startFragRequested = false; - this.trackId = this.videoTrackCC = this.waitingVideoCC = -1; - } - onLevelLoaded(event, data) { - this.mainDetails = data.details; - if (this.cachedTrackLoadedData !== null) { - this.hls.trigger(Events.AUDIO_TRACK_LOADED, this.cachedTrackLoadedData); - this.cachedTrackLoadedData = null; - } - } - onAudioTrackLoaded(event, data) { - var _track$details; - if (this.mainDetails == null) { - this.cachedTrackLoadedData = data; - return; - } - const { - levels - } = this; - const { - details: newDetails, - id: trackId - } = data; - if (!levels) { - this.warn(`Audio tracks were reset while loading level ${trackId}`); - return; - } - this.log(`Audio track ${trackId} loaded [${newDetails.startSN},${newDetails.endSN}]${newDetails.lastPartSn ? `[part-${newDetails.lastPartSn}-${newDetails.lastPartIndex}]` : ''},duration:${newDetails.totalduration}`); - const track = levels[trackId]; - let sliding = 0; - if (newDetails.live || (_track$details = track.details) != null && _track$details.live) { - this.checkLiveUpdate(newDetails); - const mainDetails = this.mainDetails; - if (newDetails.deltaUpdateFailed || !mainDetails) { - return; - } - if (!track.details && newDetails.hasProgramDateTime && mainDetails.hasProgramDateTime) { - // Make sure our audio rendition is aligned with the "main" rendition, using - // pdt as our reference times. - alignMediaPlaylistByPDT(newDetails, mainDetails); - sliding = newDetails.fragments[0].start; - } else { - var _this$levelLastLoaded; - sliding = this.alignPlaylists(newDetails, track.details, (_this$levelLastLoaded = this.levelLastLoaded) == null ? void 0 : _this$levelLastLoaded.details); - } - } - track.details = newDetails; - this.levelLastLoaded = track; - - // compute start position if we are aligned with the main playlist - if (!this.startFragRequested && (this.mainDetails || !newDetails.live)) { - this.setStartPosition(this.mainDetails || newDetails, sliding); - } - // only switch back to IDLE state if we were waiting for track to start downloading a new fragment - if (this.state === State.WAITING_TRACK && !this.waitForCdnTuneIn(newDetails)) { - this.state = State.IDLE; - } - - // trigger handler right now - this.tick(); - } - _handleFragmentLoadProgress(data) { - var _frag$initSegment; - const { - frag, - part, - payload - } = data; - const { - config, - trackId, - levels - } = this; - if (!levels) { - this.warn(`Audio tracks were reset while fragment load was in progress. Fragment ${frag.sn} of level ${frag.level} will not be buffered`); - return; - } - const track = levels[trackId]; - if (!track) { - this.warn('Audio track is undefined on fragment load progress'); - return; - } - const details = track.details; - if (!details) { - this.warn('Audio track details undefined on fragment load progress'); - this.removeUnbufferedFrags(frag.start); - return; - } - const audioCodec = config.defaultAudioCodec || track.audioCodec || 'mp4a.40.2'; - let transmuxer = this.transmuxer; - if (!transmuxer) { - transmuxer = this.transmuxer = new TransmuxerInterface(this.hls, PlaylistLevelType.AUDIO, this._handleTransmuxComplete.bind(this), this._handleTransmuxerFlush.bind(this)); - } - - // Check if we have video initPTS - // If not we need to wait for it - const initPTS = this.initPTS[frag.cc]; - const initSegmentData = (_frag$initSegment = frag.initSegment) == null ? void 0 : _frag$initSegment.data; - if (initPTS !== undefined) { - // this.log(`Transmuxing ${sn} of [${details.startSN} ,${details.endSN}],track ${trackId}`); - // time Offset is accurate if level PTS is known, or if playlist is not sliding (not live) - const accurateTimeOffset = false; // details.PTSKnown || !details.live; - const partIndex = part ? part.index : -1; - const partial = partIndex !== -1; - const chunkMeta = new ChunkMetadata(frag.level, frag.sn, frag.stats.chunkCount, payload.byteLength, partIndex, partial); - transmuxer.push(payload, initSegmentData, audioCodec, '', frag, part, details.totalduration, accurateTimeOffset, chunkMeta, initPTS); - } else { - this.log(`Unknown video PTS for cc ${frag.cc}, waiting for video PTS before demuxing audio frag ${frag.sn} of [${details.startSN} ,${details.endSN}],track ${trackId}`); - const { - cache - } = this.waitingData = this.waitingData || { - frag, - part, - cache: new ChunkCache(), - complete: false - }; - cache.push(new Uint8Array(payload)); - this.waitingVideoCC = this.videoTrackCC; - this.state = State.WAITING_INIT_PTS; - } - } - _handleFragmentLoadComplete(fragLoadedData) { - if (this.waitingData) { - this.waitingData.complete = true; - return; - } - super._handleFragmentLoadComplete(fragLoadedData); - } - onBufferReset( /* event: Events.BUFFER_RESET */ - ) { - // reset reference to sourcebuffers - this.mediaBuffer = this.videoBuffer = null; - this.loadedmetadata = false; - } - onBufferCreated(event, data) { - const audioTrack = data.tracks.audio; - if (audioTrack) { - this.mediaBuffer = audioTrack.buffer || null; - } - if (data.tracks.video) { - this.videoBuffer = data.tracks.video.buffer || null; - } - } - onFragBuffered(event, data) { - const { - frag, - part - } = data; - if (frag.type !== PlaylistLevelType.AUDIO) { - if (!this.loadedmetadata && frag.type === PlaylistLevelType.MAIN) { - const bufferable = this.videoBuffer || this.media; - if (bufferable) { - const bufferedTimeRanges = BufferHelper.getBuffered(bufferable); - if (bufferedTimeRanges.length) { - this.loadedmetadata = true; - } - } - } - return; - } - if (this.fragContextChanged(frag)) { - // If a level switch was requested while a fragment was buffering, it will emit the FRAG_BUFFERED event upon completion - // Avoid setting state back to IDLE or concluding the audio switch; otherwise, the switched-to track will not buffer - this.warn(`Fragment ${frag.sn}${part ? ' p: ' + part.index : ''} of level ${frag.level} finished buffering, but was aborted. state: ${this.state}, audioSwitch: ${this.switchingTrack ? this.switchingTrack.name : 'false'}`); - return; - } - if (frag.sn !== 'initSegment') { - this.fragPrevious = frag; - const track = this.switchingTrack; - if (track) { - this.bufferedTrack = track; - this.switchingTrack = null; - this.hls.trigger(Events.AUDIO_TRACK_SWITCHED, _objectSpread2({}, track)); - } - } - this.fragBufferedComplete(frag, part); - } - onError(event, data) { - var _data$context; - if (data.fatal) { - this.state = State.ERROR; - return; - } - switch (data.details) { - case ErrorDetails.FRAG_GAP: - case ErrorDetails.FRAG_PARSING_ERROR: - case ErrorDetails.FRAG_DECRYPT_ERROR: - case ErrorDetails.FRAG_LOAD_ERROR: - case ErrorDetails.FRAG_LOAD_TIMEOUT: - case ErrorDetails.KEY_LOAD_ERROR: - case ErrorDetails.KEY_LOAD_TIMEOUT: - this.onFragmentOrKeyLoadError(PlaylistLevelType.AUDIO, data); - break; - case ErrorDetails.AUDIO_TRACK_LOAD_ERROR: - case ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT: - case ErrorDetails.LEVEL_PARSING_ERROR: - // in case of non fatal error while loading track, if not retrying to load track, switch back to IDLE - if (!data.levelRetry && this.state === State.WAITING_TRACK && ((_data$context = data.context) == null ? void 0 : _data$context.type) === PlaylistContextType.AUDIO_TRACK) { - this.state = State.IDLE; - } - break; - case ErrorDetails.BUFFER_APPEND_ERROR: - case ErrorDetails.BUFFER_FULL_ERROR: - if (!data.parent || data.parent !== 'audio') { - return; - } - if (data.details === ErrorDetails.BUFFER_APPEND_ERROR) { - this.resetLoadingState(); - return; - } - if (this.reduceLengthAndFlushBuffer(data)) { - this.bufferedTrack = null; - super.flushMainBuffer(0, Number.POSITIVE_INFINITY, 'audio'); - } - break; - case ErrorDetails.INTERNAL_EXCEPTION: - this.recoverWorkerError(data); - break; - } - } - onBufferFlushing(event, { - type - }) { - if (type !== ElementaryStreamTypes.VIDEO) { - this.flushing = true; - } - } - onBufferFlushed(event, { - type - }) { - if (type !== ElementaryStreamTypes.VIDEO) { - this.flushing = false; - this.bufferFlushed = true; - if (this.state === State.ENDED) { - this.state = State.IDLE; - } - const mediaBuffer = this.mediaBuffer || this.media; - if (mediaBuffer) { - this.afterBufferFlushed(mediaBuffer, type, PlaylistLevelType.AUDIO); - this.tick(); - } - } - } - _handleTransmuxComplete(transmuxResult) { - var _id3$samples; - const id = 'audio'; - const { - hls - } = this; - const { - remuxResult, - chunkMeta - } = transmuxResult; - const context = this.getCurrentContext(chunkMeta); - if (!context) { - this.resetWhenMissingContext(chunkMeta); - return; - } - const { - frag, - part, - level - } = context; - const { - details - } = level; - const { - audio, - text, - id3, - initSegment - } = remuxResult; - - // Check if the current fragment has been aborted. We check this by first seeing if we're still playing the current level. - // If we are, subsequently check if the currently loading fragment (fragCurrent) has changed. - if (this.fragContextChanged(frag) || !details) { - this.fragmentTracker.removeFragment(frag); - return; - } - this.state = State.PARSING; - if (this.switchingTrack && audio) { - this.completeAudioSwitch(this.switchingTrack); - } - if (initSegment != null && initSegment.tracks) { - const mapFragment = frag.initSegment || frag; - this._bufferInitSegment(level, initSegment.tracks, mapFragment, chunkMeta); - hls.trigger(Events.FRAG_PARSING_INIT_SEGMENT, { - frag: mapFragment, - id, - tracks: initSegment.tracks - }); - // Only flush audio from old audio tracks when PTS is known on new audio track - } - if (audio) { - const { - startPTS, - endPTS, - startDTS, - endDTS - } = audio; - if (part) { - part.elementaryStreams[ElementaryStreamTypes.AUDIO] = { - startPTS, - endPTS, - startDTS, - endDTS - }; - } - frag.setElementaryStreamInfo(ElementaryStreamTypes.AUDIO, startPTS, endPTS, startDTS, endDTS); - this.bufferFragmentData(audio, frag, part, chunkMeta); - } - if (id3 != null && (_id3$samples = id3.samples) != null && _id3$samples.length) { - const emittedID3 = _extends({ - id, - frag, - details - }, id3); - hls.trigger(Events.FRAG_PARSING_METADATA, emittedID3); - } - if (text) { - const emittedText = _extends({ - id, - frag, - details - }, text); - hls.trigger(Events.FRAG_PARSING_USERDATA, emittedText); - } - } - _bufferInitSegment(currentLevel, tracks, frag, chunkMeta) { - if (this.state !== State.PARSING) { - return; - } - // delete any video track found on audio transmuxer - if (tracks.video) { - delete tracks.video; - } - - // include levelCodec in audio and video tracks - const track = tracks.audio; - if (!track) { - return; - } - track.id = 'audio'; - const variantAudioCodecs = currentLevel.audioCodec; - this.log(`Init audio buffer, container:${track.container}, codecs[level/parsed]=[${variantAudioCodecs}/${track.codec}]`); - // SourceBuffer will use track.levelCodec if defined - if (variantAudioCodecs && variantAudioCodecs.split(',').length === 1) { - track.levelCodec = variantAudioCodecs; - } - this.hls.trigger(Events.BUFFER_CODECS, tracks); - const initSegment = track.initSegment; - if (initSegment != null && initSegment.byteLength) { - const segment = { - type: 'audio', - frag, - part: null, - chunkMeta, - parent: frag.type, - data: initSegment - }; - this.hls.trigger(Events.BUFFER_APPENDING, segment); - } - // trigger handler right now - this.tickImmediate(); - } - loadFragment(frag, track, targetBufferTime) { - // only load if fragment is not loaded or if in audio switch - const fragState = this.fragmentTracker.getState(frag); - this.fragCurrent = frag; - - // we force a frag loading in audio switch as fragment tracker might not have evicted previous frags in case of quick audio switch - if (this.switchingTrack || fragState === FragmentState.NOT_LOADED || fragState === FragmentState.PARTIAL) { - var _track$details2; - if (frag.sn === 'initSegment') { - this._loadInitSegment(frag, track); - } else if ((_track$details2 = track.details) != null && _track$details2.live && !this.initPTS[frag.cc]) { - this.log(`Waiting for video PTS in continuity counter ${frag.cc} of live stream before loading audio fragment ${frag.sn} of level ${this.trackId}`); - this.state = State.WAITING_INIT_PTS; - const mainDetails = this.mainDetails; - if (mainDetails && mainDetails.fragments[0].start !== track.details.fragments[0].start) { - alignMediaPlaylistByPDT(track.details, mainDetails); - } - } else { - this.startFragRequested = true; - super.loadFragment(frag, track, targetBufferTime); - } - } else { - this.clearTrackerIfNeeded(frag); - } - } - flushAudioIfNeeded(switchingTrack) { - const { - media, - bufferedTrack - } = this; - const bufferedAttributes = bufferedTrack == null ? void 0 : bufferedTrack.attrs; - const switchAttributes = switchingTrack.attrs; - if (media && bufferedAttributes && (bufferedAttributes.CHANNELS !== switchAttributes.CHANNELS || bufferedTrack.name !== switchingTrack.name || bufferedTrack.lang !== switchingTrack.lang)) { - this.log('Switching audio track : flushing all audio'); - super.flushMainBuffer(0, Number.POSITIVE_INFINITY, 'audio'); - this.bufferedTrack = null; - } - } - completeAudioSwitch(switchingTrack) { - const { - hls - } = this; - this.flushAudioIfNeeded(switchingTrack); - this.bufferedTrack = switchingTrack; - this.switchingTrack = null; - hls.trigger(Events.AUDIO_TRACK_SWITCHED, _objectSpread2({}, switchingTrack)); - } -} - -class AudioTrackController extends BasePlaylistController { - constructor(hls) { - super(hls, '[audio-track-controller]'); - this.tracks = []; - this.groupIds = null; - this.tracksInGroup = []; - this.trackId = -1; - this.currentTrack = null; - this.selectDefaultTrack = true; - this.registerListeners(); - } - registerListeners() { - const { - hls - } = this; - hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this); - hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this); - hls.on(Events.LEVEL_SWITCHING, this.onLevelSwitching, this); - hls.on(Events.AUDIO_TRACK_LOADED, this.onAudioTrackLoaded, this); - hls.on(Events.ERROR, this.onError, this); - } - unregisterListeners() { - const { - hls - } = this; - hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this); - hls.off(Events.LEVEL_LOADING, this.onLevelLoading, this); - hls.off(Events.LEVEL_SWITCHING, this.onLevelSwitching, this); - hls.off(Events.AUDIO_TRACK_LOADED, this.onAudioTrackLoaded, this); - hls.off(Events.ERROR, this.onError, this); - } - destroy() { - this.unregisterListeners(); - this.tracks.length = 0; - this.tracksInGroup.length = 0; - this.currentTrack = null; - super.destroy(); - } - onManifestLoading() { - this.tracks = []; - this.tracksInGroup = []; - this.groupIds = null; - this.currentTrack = null; - this.trackId = -1; - this.selectDefaultTrack = true; - } - onManifestParsed(event, data) { - this.tracks = data.audioTracks || []; - } - onAudioTrackLoaded(event, data) { - const { - id, - groupId, - details - } = data; - const trackInActiveGroup = this.tracksInGroup[id]; - if (!trackInActiveGroup || trackInActiveGroup.groupId !== groupId) { - this.warn(`Audio track with id:${id} and group:${groupId} not found in active group ${trackInActiveGroup == null ? void 0 : trackInActiveGroup.groupId}`); - return; - } - const curDetails = trackInActiveGroup.details; - trackInActiveGroup.details = data.details; - this.log(`Audio track ${id} "${trackInActiveGroup.name}" lang:${trackInActiveGroup.lang} group:${groupId} loaded [${details.startSN}-${details.endSN}]`); - if (id === this.trackId) { - this.playlistLoaded(id, data, curDetails); - } - } - onLevelLoading(event, data) { - this.switchLevel(data.level); - } - onLevelSwitching(event, data) { - this.switchLevel(data.level); - } - switchLevel(levelIndex) { - const levelInfo = this.hls.levels[levelIndex]; - if (!levelInfo) { - return; - } - const audioGroups = levelInfo.audioGroups || null; - const currentGroups = this.groupIds; - let currentTrack = this.currentTrack; - if (!audioGroups || (currentGroups == null ? void 0 : currentGroups.length) !== (audioGroups == null ? void 0 : audioGroups.length) || audioGroups != null && audioGroups.some(groupId => (currentGroups == null ? void 0 : currentGroups.indexOf(groupId)) === -1)) { - this.groupIds = audioGroups; - this.trackId = -1; - this.currentTrack = null; - const audioTracks = this.tracks.filter(track => !audioGroups || audioGroups.indexOf(track.groupId) !== -1); - if (audioTracks.length) { - // Disable selectDefaultTrack if there are no default tracks - if (this.selectDefaultTrack && !audioTracks.some(track => track.default)) { - this.selectDefaultTrack = false; - } - // track.id should match hls.audioTracks index - audioTracks.forEach((track, i) => { - track.id = i; - }); - } else if (!currentTrack && !this.tracksInGroup.length) { - // Do not dispatch AUDIO_TRACKS_UPDATED when there were and are no tracks - return; - } - this.tracksInGroup = audioTracks; - - // Find preferred track - const audioPreference = this.hls.config.audioPreference; - if (!currentTrack && audioPreference) { - const groupIndex = findMatchingOption(audioPreference, audioTracks, audioMatchPredicate); - if (groupIndex > -1) { - currentTrack = audioTracks[groupIndex]; - } else { - const allIndex = findMatchingOption(audioPreference, this.tracks); - currentTrack = this.tracks[allIndex]; - } - } - - // Select initial track - let trackId = this.findTrackId(currentTrack); - if (trackId === -1 && currentTrack) { - trackId = this.findTrackId(null); - } - - // Dispatch events and load track if needed - const audioTracksUpdated = { - audioTracks - }; - this.log(`Updating audio tracks, ${audioTracks.length} track(s) found in group(s): ${audioGroups == null ? void 0 : audioGroups.join(',')}`); - this.hls.trigger(Events.AUDIO_TRACKS_UPDATED, audioTracksUpdated); - const selectedTrackId = this.trackId; - if (trackId !== -1 && selectedTrackId === -1) { - this.setAudioTrack(trackId); - } else if (audioTracks.length && selectedTrackId === -1) { - var _this$groupIds; - const error = new Error(`No audio track selected for current audio group-ID(s): ${(_this$groupIds = this.groupIds) == null ? void 0 : _this$groupIds.join(',')} track count: ${audioTracks.length}`); - this.warn(error.message); - this.hls.trigger(Events.ERROR, { - type: ErrorTypes.MEDIA_ERROR, - details: ErrorDetails.AUDIO_TRACK_LOAD_ERROR, - fatal: true, - error - }); - } - } else if (this.shouldReloadPlaylist(currentTrack)) { - // Retry playlist loading if no playlist is or has been loaded yet - this.setAudioTrack(this.trackId); - } - } - onError(event, data) { - if (data.fatal || !data.context) { - return; - } - if (data.context.type === PlaylistContextType.AUDIO_TRACK && data.context.id === this.trackId && (!this.groupIds || this.groupIds.indexOf(data.context.groupId) !== -1)) { - this.requestScheduled = -1; - this.checkRetry(data); - } - } - get allAudioTracks() { - return this.tracks; - } - get audioTracks() { - return this.tracksInGroup; - } - get audioTrack() { - return this.trackId; - } - set audioTrack(newId) { - // If audio track is selected from API then don't choose from the manifest default track - this.selectDefaultTrack = false; - this.setAudioTrack(newId); - } - setAudioOption(audioOption) { - const hls = this.hls; - hls.config.audioPreference = audioOption; - if (audioOption) { - const allAudioTracks = this.allAudioTracks; - this.selectDefaultTrack = false; - if (allAudioTracks.length) { - // First see if current option matches (no switch op) - const currentTrack = this.currentTrack; - if (currentTrack && matchesOption(audioOption, currentTrack, audioMatchPredicate)) { - return currentTrack; - } - // Find option in available tracks (tracksInGroup) - const groupIndex = findMatchingOption(audioOption, this.tracksInGroup, audioMatchPredicate); - if (groupIndex > -1) { - const track = this.tracksInGroup[groupIndex]; - this.setAudioTrack(groupIndex); - return track; - } else if (currentTrack) { - // Find option in nearest level audio group - let searchIndex = hls.loadLevel; - if (searchIndex === -1) { - searchIndex = hls.firstAutoLevel; - } - const switchIndex = findClosestLevelWithAudioGroup(audioOption, hls.levels, allAudioTracks, searchIndex, audioMatchPredicate); - if (switchIndex === -1) { - // could not find matching variant - return null; - } - // and switch level to acheive the audio group switch - hls.nextLoadLevel = switchIndex; - } - if (audioOption.channels || audioOption.audioCodec) { - // Could not find a match with codec / channels predicate - // Find a match without channels or codec - const withoutCodecAndChannelsMatch = findMatchingOption(audioOption, allAudioTracks); - if (withoutCodecAndChannelsMatch > -1) { - return allAudioTracks[withoutCodecAndChannelsMatch]; - } - } - } - } - return null; - } - setAudioTrack(newId) { - const tracks = this.tracksInGroup; - - // check if level idx is valid - if (newId < 0 || newId >= tracks.length) { - this.warn(`Invalid audio track id: ${newId}`); - return; - } - - // stopping live reloading timer if any - this.clearTimer(); - this.selectDefaultTrack = false; - const lastTrack = this.currentTrack; - const track = tracks[newId]; - const trackLoaded = track.details && !track.details.live; - if (newId === this.trackId && track === lastTrack && trackLoaded) { - return; - } - this.log(`Switching to audio-track ${newId} "${track.name}" lang:${track.lang} group:${track.groupId} channels:${track.channels}`); - this.trackId = newId; - this.currentTrack = track; - this.hls.trigger(Events.AUDIO_TRACK_SWITCHING, _objectSpread2({}, track)); - // Do not reload track unless live - if (trackLoaded) { - return; - } - const hlsUrlParameters = this.switchParams(track.url, lastTrack == null ? void 0 : lastTrack.details, track.details); - this.loadPlaylist(hlsUrlParameters); - } - findTrackId(currentTrack) { - const audioTracks = this.tracksInGroup; - for (let i = 0; i < audioTracks.length; i++) { - const track = audioTracks[i]; - if (this.selectDefaultTrack && !track.default) { - continue; - } - if (!currentTrack || matchesOption(currentTrack, track, audioMatchPredicate)) { - return i; - } - } - if (currentTrack) { - const { - name, - lang, - assocLang, - characteristics, - audioCodec, - channels - } = currentTrack; - for (let i = 0; i < audioTracks.length; i++) { - const track = audioTracks[i]; - if (matchesOption({ - name, - lang, - assocLang, - characteristics, - audioCodec, - channels - }, track, audioMatchPredicate)) { - return i; - } - } - for (let i = 0; i < audioTracks.length; i++) { - const track = audioTracks[i]; - if (mediaAttributesIdentical(currentTrack.attrs, track.attrs, ['LANGUAGE', 'ASSOC-LANGUAGE', 'CHARACTERISTICS'])) { - return i; - } - } - for (let i = 0; i < audioTracks.length; i++) { - const track = audioTracks[i]; - if (mediaAttributesIdentical(currentTrack.attrs, track.attrs, ['LANGUAGE'])) { - return i; - } - } - } - return -1; - } - loadPlaylist(hlsUrlParameters) { - const audioTrack = this.currentTrack; - if (this.shouldLoadPlaylist(audioTrack) && audioTrack) { - super.loadPlaylist(); - const id = audioTrack.id; - const groupId = audioTrack.groupId; - let url = audioTrack.url; - if (hlsUrlParameters) { - try { - url = hlsUrlParameters.addDirectives(url); - } catch (error) { - this.warn(`Could not construct new URL with HLS Delivery Directives: ${error}`); - } - } - // track not retrieved yet, or live playlist we need to (re)load it - this.log(`loading audio-track playlist ${id} "${audioTrack.name}" lang:${audioTrack.lang} group:${groupId}`); - this.clearTimer(); - this.hls.trigger(Events.AUDIO_TRACK_LOADING, { - url, - id, - groupId, - deliveryDirectives: hlsUrlParameters || null - }); - } - } -} - -const TICK_INTERVAL$1 = 500; // how often to tick in ms - -class SubtitleStreamController extends BaseStreamController { - constructor(hls, fragmentTracker, keyLoader) { - super(hls, fragmentTracker, keyLoader, '[subtitle-stream-controller]', PlaylistLevelType.SUBTITLE); - this.currentTrackId = -1; - this.tracksBuffered = []; - this.mainDetails = null; - this._registerListeners(); - } - onHandlerDestroying() { - this._unregisterListeners(); - super.onHandlerDestroying(); - this.mainDetails = null; - } - _registerListeners() { - const { - hls - } = this; - hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this); - hls.on(Events.ERROR, this.onError, this); - hls.on(Events.SUBTITLE_TRACKS_UPDATED, this.onSubtitleTracksUpdated, this); - hls.on(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this); - hls.on(Events.SUBTITLE_TRACK_LOADED, this.onSubtitleTrackLoaded, this); - hls.on(Events.SUBTITLE_FRAG_PROCESSED, this.onSubtitleFragProcessed, this); - hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); - hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this); - } - _unregisterListeners() { - const { - hls - } = this; - hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this); - hls.off(Events.ERROR, this.onError, this); - hls.off(Events.SUBTITLE_TRACKS_UPDATED, this.onSubtitleTracksUpdated, this); - hls.off(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this); - hls.off(Events.SUBTITLE_TRACK_LOADED, this.onSubtitleTrackLoaded, this); - hls.off(Events.SUBTITLE_FRAG_PROCESSED, this.onSubtitleFragProcessed, this); - hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); - hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this); - } - startLoad(startPosition) { - this.stopLoad(); - this.state = State.IDLE; - this.setInterval(TICK_INTERVAL$1); - this.nextLoadPosition = this.startPosition = this.lastCurrentTime = startPosition; - this.tick(); - } - onManifestLoading() { - this.mainDetails = null; - this.fragmentTracker.removeAllFragments(); - } - onMediaDetaching() { - this.tracksBuffered = []; - super.onMediaDetaching(); - } - onLevelLoaded(event, data) { - this.mainDetails = data.details; - } - onSubtitleFragProcessed(event, data) { - const { - frag, - success - } = data; - this.fragPrevious = frag; - this.state = State.IDLE; - if (!success) { - return; - } - const buffered = this.tracksBuffered[this.currentTrackId]; - if (!buffered) { - return; - } - - // Create/update a buffered array matching the interface used by BufferHelper.bufferedInfo - // so we can re-use the logic used to detect how much has been buffered - let timeRange; - const fragStart = frag.start; - for (let i = 0; i < buffered.length; i++) { - if (fragStart >= buffered[i].start && fragStart <= buffered[i].end) { - timeRange = buffered[i]; - break; - } - } - const fragEnd = frag.start + frag.duration; - if (timeRange) { - timeRange.end = fragEnd; - } else { - timeRange = { - start: fragStart, - end: fragEnd - }; - buffered.push(timeRange); - } - this.fragmentTracker.fragBuffered(frag); - this.fragBufferedComplete(frag, null); - } - onBufferFlushing(event, data) { - const { - startOffset, - endOffset - } = data; - if (startOffset === 0 && endOffset !== Number.POSITIVE_INFINITY) { - const endOffsetSubtitles = endOffset - 1; - if (endOffsetSubtitles <= 0) { - return; - } - data.endOffsetSubtitles = Math.max(0, endOffsetSubtitles); - this.tracksBuffered.forEach(buffered => { - for (let i = 0; i < buffered.length;) { - if (buffered[i].end <= endOffsetSubtitles) { - buffered.shift(); - continue; - } else if (buffered[i].start < endOffsetSubtitles) { - buffered[i].start = endOffsetSubtitles; - } else { - break; - } - i++; - } - }); - this.fragmentTracker.removeFragmentsInRange(startOffset, endOffsetSubtitles, PlaylistLevelType.SUBTITLE); - } - } - onFragBuffered(event, data) { - if (!this.loadedmetadata && data.frag.type === PlaylistLevelType.MAIN) { - var _this$media; - if ((_this$media = this.media) != null && _this$media.buffered.length) { - this.loadedmetadata = true; - } - } - } - - // If something goes wrong, proceed to next frag, if we were processing one. - onError(event, data) { - const frag = data.frag; - if ((frag == null ? void 0 : frag.type) === PlaylistLevelType.SUBTITLE) { - if (data.details === ErrorDetails.FRAG_GAP) { - this.fragmentTracker.fragBuffered(frag, true); - } - if (this.fragCurrent) { - this.fragCurrent.abortRequests(); - } - if (this.state !== State.STOPPED) { - this.state = State.IDLE; - } - } - } - - // Got all new subtitle levels. - onSubtitleTracksUpdated(event, { - subtitleTracks - }) { - if (this.levels && subtitleOptionsIdentical(this.levels, subtitleTracks)) { - this.levels = subtitleTracks.map(mediaPlaylist => new Level(mediaPlaylist)); - return; - } - this.tracksBuffered = []; - this.levels = subtitleTracks.map(mediaPlaylist => { - const level = new Level(mediaPlaylist); - this.tracksBuffered[level.id] = []; - return level; - }); - this.fragmentTracker.removeFragmentsInRange(0, Number.POSITIVE_INFINITY, PlaylistLevelType.SUBTITLE); - this.fragPrevious = null; - this.mediaBuffer = null; - } - onSubtitleTrackSwitch(event, data) { - var _this$levels; - this.currentTrackId = data.id; - if (!((_this$levels = this.levels) != null && _this$levels.length) || this.currentTrackId === -1) { - this.clearInterval(); - return; - } - - // Check if track has the necessary details to load fragments - const currentTrack = this.levels[this.currentTrackId]; - if (currentTrack != null && currentTrack.details) { - this.mediaBuffer = this.mediaBufferTimeRanges; - } else { - this.mediaBuffer = null; - } - if (currentTrack) { - this.setInterval(TICK_INTERVAL$1); - } - } - - // Got a new set of subtitle fragments. - onSubtitleTrackLoaded(event, data) { - var _track$details; - const { - currentTrackId, - levels - } = this; - const { - details: newDetails, - id: trackId - } = data; - if (!levels) { - this.warn(`Subtitle tracks were reset while loading level ${trackId}`); - return; - } - const track = levels[trackId]; - if (trackId >= levels.length || !track) { - return; - } - this.log(`Subtitle track ${trackId} loaded [${newDetails.startSN},${newDetails.endSN}]${newDetails.lastPartSn ? `[part-${newDetails.lastPartSn}-${newDetails.lastPartIndex}]` : ''},duration:${newDetails.totalduration}`); - this.mediaBuffer = this.mediaBufferTimeRanges; - let sliding = 0; - if (newDetails.live || (_track$details = track.details) != null && _track$details.live) { - const mainDetails = this.mainDetails; - if (newDetails.deltaUpdateFailed || !mainDetails) { - return; - } - const mainSlidingStartFragment = mainDetails.fragments[0]; - if (!track.details) { - if (newDetails.hasProgramDateTime && mainDetails.hasProgramDateTime) { - alignMediaPlaylistByPDT(newDetails, mainDetails); - sliding = newDetails.fragments[0].start; - } else if (mainSlidingStartFragment) { - // line up live playlist with main so that fragments in range are loaded - sliding = mainSlidingStartFragment.start; - addSliding(newDetails, sliding); - } - } else { - var _this$levelLastLoaded; - sliding = this.alignPlaylists(newDetails, track.details, (_this$levelLastLoaded = this.levelLastLoaded) == null ? void 0 : _this$levelLastLoaded.details); - if (sliding === 0 && mainSlidingStartFragment) { - // realign with main when there is no overlap with last refresh - sliding = mainSlidingStartFragment.start; - addSliding(newDetails, sliding); - } - } - } - track.details = newDetails; - this.levelLastLoaded = track; - if (trackId !== currentTrackId) { - return; - } - if (!this.startFragRequested && (this.mainDetails || !newDetails.live)) { - this.setStartPosition(this.mainDetails || newDetails, sliding); - } - - // trigger handler right now - this.tick(); - - // If playlist is misaligned because of bad PDT or drift, delete details to resync with main on reload - if (newDetails.live && !this.fragCurrent && this.media && this.state === State.IDLE) { - const foundFrag = findFragmentByPTS(null, newDetails.fragments, this.media.currentTime, 0); - if (!foundFrag) { - this.warn('Subtitle playlist not aligned with playback'); - track.details = undefined; - } - } - } - _handleFragmentLoadComplete(fragLoadedData) { - const { - frag, - payload - } = fragLoadedData; - const decryptData = frag.decryptdata; - const hls = this.hls; - if (this.fragContextChanged(frag)) { - return; - } - // check to see if the payload needs to be decrypted - if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && decryptData.method === 'AES-128') { - const startTime = performance.now(); - // decrypt the subtitles - this.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer).catch(err => { - hls.trigger(Events.ERROR, { - type: ErrorTypes.MEDIA_ERROR, - details: ErrorDetails.FRAG_DECRYPT_ERROR, - fatal: false, - error: err, - reason: err.message, - frag - }); - throw err; - }).then(decryptedData => { - const endTime = performance.now(); - hls.trigger(Events.FRAG_DECRYPTED, { - frag, - payload: decryptedData, - stats: { - tstart: startTime, - tdecrypt: endTime - } - }); - }).catch(err => { - this.warn(`${err.name}: ${err.message}`); - this.state = State.IDLE; - }); - } - } - doTick() { - if (!this.media) { - this.state = State.IDLE; - return; - } - if (this.state === State.IDLE) { - const { - currentTrackId, - levels - } = this; - const track = levels == null ? void 0 : levels[currentTrackId]; - if (!track || !levels.length || !track.details) { - return; - } - const { - config - } = this; - const currentTime = this.getLoadPosition(); - const bufferedInfo = BufferHelper.bufferedInfo(this.tracksBuffered[this.currentTrackId] || [], currentTime, config.maxBufferHole); - const { - end: targetBufferTime, - len: bufferLen - } = bufferedInfo; - const mainBufferInfo = this.getFwdBufferInfo(this.media, PlaylistLevelType.MAIN); - const trackDetails = track.details; - const maxBufLen = this.getMaxBufferLength(mainBufferInfo == null ? void 0 : mainBufferInfo.len) + trackDetails.levelTargetDuration; - if (bufferLen > maxBufLen) { - return; - } - const fragments = trackDetails.fragments; - const fragLen = fragments.length; - const end = trackDetails.edge; - let foundFrag = null; - const fragPrevious = this.fragPrevious; - if (targetBufferTime < end) { - const tolerance = config.maxFragLookUpTolerance; - const lookupTolerance = targetBufferTime > end - tolerance ? 0 : tolerance; - foundFrag = findFragmentByPTS(fragPrevious, fragments, Math.max(fragments[0].start, targetBufferTime), lookupTolerance); - if (!foundFrag && fragPrevious && fragPrevious.start < fragments[0].start) { - foundFrag = fragments[0]; - } - } else { - foundFrag = fragments[fragLen - 1]; - } - if (!foundFrag) { - return; - } - foundFrag = this.mapToInitFragWhenRequired(foundFrag); - if (foundFrag.sn !== 'initSegment') { - // Load earlier fragment in same discontinuity to make up for misaligned playlists and cues that extend beyond end of segment - const curSNIdx = foundFrag.sn - trackDetails.startSN; - const prevFrag = fragments[curSNIdx - 1]; - if (prevFrag && prevFrag.cc === foundFrag.cc && this.fragmentTracker.getState(prevFrag) === FragmentState.NOT_LOADED) { - foundFrag = prevFrag; - } - } - if (this.fragmentTracker.getState(foundFrag) === FragmentState.NOT_LOADED) { - // only load if fragment is not loaded - this.loadFragment(foundFrag, track, targetBufferTime); - } - } - } - getMaxBufferLength(mainBufferLength) { - const maxConfigBuffer = super.getMaxBufferLength(); - if (!mainBufferLength) { - return maxConfigBuffer; - } - return Math.max(maxConfigBuffer, mainBufferLength); - } - loadFragment(frag, level, targetBufferTime) { - this.fragCurrent = frag; - if (frag.sn === 'initSegment') { - this._loadInitSegment(frag, level); - } else { - this.startFragRequested = true; - super.loadFragment(frag, level, targetBufferTime); - } - } - get mediaBufferTimeRanges() { - return new BufferableInstance(this.tracksBuffered[this.currentTrackId] || []); - } -} -class BufferableInstance { - constructor(timeranges) { - this.buffered = void 0; - const getRange = (name, index, length) => { - index = index >>> 0; - if (index > length - 1) { - throw new DOMException(`Failed to execute '${name}' on 'TimeRanges': The index provided (${index}) is greater than the maximum bound (${length})`); - } - return timeranges[index][name]; - }; - this.buffered = { - get length() { - return timeranges.length; - }, - end(index) { - return getRange('end', index, timeranges.length); - }, - start(index) { - return getRange('start', index, timeranges.length); - } - }; - } -} - -class SubtitleTrackController extends BasePlaylistController { - constructor(hls) { - super(hls, '[subtitle-track-controller]'); - this.media = null; - this.tracks = []; - this.groupIds = null; - this.tracksInGroup = []; - this.trackId = -1; - this.currentTrack = null; - this.selectDefaultTrack = true; - this.queuedDefaultTrack = -1; - this.asyncPollTrackChange = () => this.pollTrackChange(0); - this.useTextTrackPolling = false; - this.subtitlePollingInterval = -1; - this._subtitleDisplay = true; - this.onTextTracksChanged = () => { - if (!this.useTextTrackPolling) { - self.clearInterval(this.subtitlePollingInterval); - } - // Media is undefined when switching streams via loadSource() - if (!this.media || !this.hls.config.renderTextTracksNatively) { - return; - } - let textTrack = null; - const tracks = filterSubtitleTracks(this.media.textTracks); - for (let i = 0; i < tracks.length; i++) { - if (tracks[i].mode === 'hidden') { - // Do not break in case there is a following track with showing. - textTrack = tracks[i]; - } else if (tracks[i].mode === 'showing') { - textTrack = tracks[i]; - break; - } - } - - // Find internal track index for TextTrack - const trackId = this.findTrackForTextTrack(textTrack); - if (this.subtitleTrack !== trackId) { - this.setSubtitleTrack(trackId); - } - }; - this.registerListeners(); - } - destroy() { - this.unregisterListeners(); - this.tracks.length = 0; - this.tracksInGroup.length = 0; - this.currentTrack = null; - this.onTextTracksChanged = this.asyncPollTrackChange = null; - super.destroy(); - } - get subtitleDisplay() { - return this._subtitleDisplay; - } - set subtitleDisplay(value) { - this._subtitleDisplay = value; - if (this.trackId > -1) { - this.toggleTrackModes(); - } - } - registerListeners() { - const { - hls - } = this; - hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this); - hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this); - hls.on(Events.LEVEL_SWITCHING, this.onLevelSwitching, this); - hls.on(Events.SUBTITLE_TRACK_LOADED, this.onSubtitleTrackLoaded, this); - hls.on(Events.ERROR, this.onError, this); - } - unregisterListeners() { - const { - hls - } = this; - hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this); - hls.off(Events.LEVEL_LOADING, this.onLevelLoading, this); - hls.off(Events.LEVEL_SWITCHING, this.onLevelSwitching, this); - hls.off(Events.SUBTITLE_TRACK_LOADED, this.onSubtitleTrackLoaded, this); - hls.off(Events.ERROR, this.onError, this); - } - - // Listen for subtitle track change, then extract the current track ID. - onMediaAttached(event, data) { - this.media = data.media; - if (!this.media) { - return; - } - if (this.queuedDefaultTrack > -1) { - this.subtitleTrack = this.queuedDefaultTrack; - this.queuedDefaultTrack = -1; - } - this.useTextTrackPolling = !(this.media.textTracks && 'onchange' in this.media.textTracks); - if (this.useTextTrackPolling) { - this.pollTrackChange(500); - } else { - this.media.textTracks.addEventListener('change', this.asyncPollTrackChange); - } - } - pollTrackChange(timeout) { - self.clearInterval(this.subtitlePollingInterval); - this.subtitlePollingInterval = self.setInterval(this.onTextTracksChanged, timeout); - } - onMediaDetaching() { - if (!this.media) { - return; - } - self.clearInterval(this.subtitlePollingInterval); - if (!this.useTextTrackPolling) { - this.media.textTracks.removeEventListener('change', this.asyncPollTrackChange); - } - if (this.trackId > -1) { - this.queuedDefaultTrack = this.trackId; - } - const textTracks = filterSubtitleTracks(this.media.textTracks); - // Clear loaded cues on media detachment from tracks - textTracks.forEach(track => { - clearCurrentCues(track); - }); - // Disable all subtitle tracks before detachment so when reattached only tracks in that content are enabled. - this.subtitleTrack = -1; - this.media = null; - } - onManifestLoading() { - this.tracks = []; - this.groupIds = null; - this.tracksInGroup = []; - this.trackId = -1; - this.currentTrack = null; - this.selectDefaultTrack = true; - } - - // Fired whenever a new manifest is loaded. - onManifestParsed(event, data) { - this.tracks = data.subtitleTracks; - } - onSubtitleTrackLoaded(event, data) { - const { - id, - groupId, - details - } = data; - const trackInActiveGroup = this.tracksInGroup[id]; - if (!trackInActiveGroup || trackInActiveGroup.groupId !== groupId) { - this.warn(`Subtitle track with id:${id} and group:${groupId} not found in active group ${trackInActiveGroup == null ? void 0 : trackInActiveGroup.groupId}`); - return; - } - const curDetails = trackInActiveGroup.details; - trackInActiveGroup.details = data.details; - this.log(`Subtitle track ${id} "${trackInActiveGroup.name}" lang:${trackInActiveGroup.lang} group:${groupId} loaded [${details.startSN}-${details.endSN}]`); - if (id === this.trackId) { - this.playlistLoaded(id, data, curDetails); - } - } - onLevelLoading(event, data) { - this.switchLevel(data.level); - } - onLevelSwitching(event, data) { - this.switchLevel(data.level); - } - switchLevel(levelIndex) { - const levelInfo = this.hls.levels[levelIndex]; - if (!levelInfo) { - return; - } - const subtitleGroups = levelInfo.subtitleGroups || null; - const currentGroups = this.groupIds; - let currentTrack = this.currentTrack; - if (!subtitleGroups || (currentGroups == null ? void 0 : currentGroups.length) !== (subtitleGroups == null ? void 0 : subtitleGroups.length) || subtitleGroups != null && subtitleGroups.some(groupId => (currentGroups == null ? void 0 : currentGroups.indexOf(groupId)) === -1)) { - this.groupIds = subtitleGroups; - this.trackId = -1; - this.currentTrack = null; - const subtitleTracks = this.tracks.filter(track => !subtitleGroups || subtitleGroups.indexOf(track.groupId) !== -1); - if (subtitleTracks.length) { - // Disable selectDefaultTrack if there are no default tracks - if (this.selectDefaultTrack && !subtitleTracks.some(track => track.default)) { - this.selectDefaultTrack = false; - } - // track.id should match hls.audioTracks index - subtitleTracks.forEach((track, i) => { - track.id = i; - }); - } else if (!currentTrack && !this.tracksInGroup.length) { - // Do not dispatch SUBTITLE_TRACKS_UPDATED when there were and are no tracks - return; - } - this.tracksInGroup = subtitleTracks; - - // Find preferred track - const subtitlePreference = this.hls.config.subtitlePreference; - if (!currentTrack && subtitlePreference) { - this.selectDefaultTrack = false; - const groupIndex = findMatchingOption(subtitlePreference, subtitleTracks); - if (groupIndex > -1) { - currentTrack = subtitleTracks[groupIndex]; - } else { - const allIndex = findMatchingOption(subtitlePreference, this.tracks); - currentTrack = this.tracks[allIndex]; - } - } - - // Select initial track - let trackId = this.findTrackId(currentTrack); - if (trackId === -1 && currentTrack) { - trackId = this.findTrackId(null); - } - - // Dispatch events and load track if needed - const subtitleTracksUpdated = { - subtitleTracks - }; - this.log(`Updating subtitle tracks, ${subtitleTracks.length} track(s) found in "${subtitleGroups == null ? void 0 : subtitleGroups.join(',')}" group-id`); - this.hls.trigger(Events.SUBTITLE_TRACKS_UPDATED, subtitleTracksUpdated); - if (trackId !== -1 && this.trackId === -1) { - this.setSubtitleTrack(trackId); - } - } else if (this.shouldReloadPlaylist(currentTrack)) { - // Retry playlist loading if no playlist is or has been loaded yet - this.setSubtitleTrack(this.trackId); - } - } - findTrackId(currentTrack) { - const tracks = this.tracksInGroup; - const selectDefault = this.selectDefaultTrack; - for (let i = 0; i < tracks.length; i++) { - const track = tracks[i]; - if (selectDefault && !track.default || !selectDefault && !currentTrack) { - continue; - } - if (!currentTrack || matchesOption(track, currentTrack)) { - return i; - } - } - if (currentTrack) { - for (let i = 0; i < tracks.length; i++) { - const track = tracks[i]; - if (mediaAttributesIdentical(currentTrack.attrs, track.attrs, ['LANGUAGE', 'ASSOC-LANGUAGE', 'CHARACTERISTICS'])) { - return i; - } - } - for (let i = 0; i < tracks.length; i++) { - const track = tracks[i]; - if (mediaAttributesIdentical(currentTrack.attrs, track.attrs, ['LANGUAGE'])) { - return i; - } - } - } - return -1; - } - findTrackForTextTrack(textTrack) { - if (textTrack) { - const tracks = this.tracksInGroup; - for (let i = 0; i < tracks.length; i++) { - const track = tracks[i]; - if (subtitleTrackMatchesTextTrack(track, textTrack)) { - return i; - } - } - } - return -1; - } - onError(event, data) { - if (data.fatal || !data.context) { - return; - } - if (data.context.type === PlaylistContextType.SUBTITLE_TRACK && data.context.id === this.trackId && (!this.groupIds || this.groupIds.indexOf(data.context.groupId) !== -1)) { - this.checkRetry(data); - } - } - get allSubtitleTracks() { - return this.tracks; - } - - /** get alternate subtitle tracks list from playlist **/ - get subtitleTracks() { - return this.tracksInGroup; - } - - /** get/set index of the selected subtitle track (based on index in subtitle track lists) **/ - get subtitleTrack() { - return this.trackId; - } - set subtitleTrack(newId) { - this.selectDefaultTrack = false; - this.setSubtitleTrack(newId); - } - setSubtitleOption(subtitleOption) { - this.hls.config.subtitlePreference = subtitleOption; - if (subtitleOption) { - const allSubtitleTracks = this.allSubtitleTracks; - this.selectDefaultTrack = false; - if (allSubtitleTracks.length) { - // First see if current option matches (no switch op) - const currentTrack = this.currentTrack; - if (currentTrack && matchesOption(subtitleOption, currentTrack)) { - return currentTrack; - } - // Find option in current group - const groupIndex = findMatchingOption(subtitleOption, this.tracksInGroup); - if (groupIndex > -1) { - const track = this.tracksInGroup[groupIndex]; - this.setSubtitleTrack(groupIndex); - return track; - } else if (currentTrack) { - // If this is not the initial selection return null - // option should have matched one in active group - return null; - } else { - // Find the option in all tracks for initial selection - const allIndex = findMatchingOption(subtitleOption, allSubtitleTracks); - if (allIndex > -1) { - return allSubtitleTracks[allIndex]; - } - } - } - } - return null; - } - loadPlaylist(hlsUrlParameters) { - super.loadPlaylist(); - const currentTrack = this.currentTrack; - if (this.shouldLoadPlaylist(currentTrack) && currentTrack) { - const id = currentTrack.id; - const groupId = currentTrack.groupId; - let url = currentTrack.url; - if (hlsUrlParameters) { - try { - url = hlsUrlParameters.addDirectives(url); - } catch (error) { - this.warn(`Could not construct new URL with HLS Delivery Directives: ${error}`); - } - } - this.log(`Loading subtitle playlist for id ${id}`); - this.hls.trigger(Events.SUBTITLE_TRACK_LOADING, { - url, - id, - groupId, - deliveryDirectives: hlsUrlParameters || null - }); - } - } - - /** - * Disables the old subtitleTrack and sets current mode on the next subtitleTrack. - * This operates on the DOM textTracks. - * A value of -1 will disable all subtitle tracks. - */ - toggleTrackModes() { - const { - media - } = this; - if (!media) { - return; - } - const textTracks = filterSubtitleTracks(media.textTracks); - const currentTrack = this.currentTrack; - let nextTrack; - if (currentTrack) { - nextTrack = textTracks.filter(textTrack => subtitleTrackMatchesTextTrack(currentTrack, textTrack))[0]; - if (!nextTrack) { - this.warn(`Unable to find subtitle TextTrack with name "${currentTrack.name}" and language "${currentTrack.lang}"`); - } - } - [].slice.call(textTracks).forEach(track => { - if (track.mode !== 'disabled' && track !== nextTrack) { - track.mode = 'disabled'; - } - }); - if (nextTrack) { - const mode = this.subtitleDisplay ? 'showing' : 'hidden'; - if (nextTrack.mode !== mode) { - nextTrack.mode = mode; - } - } - } - - /** - * This method is responsible for validating the subtitle index and periodically reloading if live. - * Dispatches the SUBTITLE_TRACK_SWITCH event, which instructs the subtitle-stream-controller to load the selected track. - */ - setSubtitleTrack(newId) { - const tracks = this.tracksInGroup; - - // setting this.subtitleTrack will trigger internal logic - // if media has not been attached yet, it will fail - // we keep a reference to the default track id - // and we'll set subtitleTrack when onMediaAttached is triggered - if (!this.media) { - this.queuedDefaultTrack = newId; - return; - } - - // exit if track id as already set or invalid - if (newId < -1 || newId >= tracks.length || !isFiniteNumber(newId)) { - this.warn(`Invalid subtitle track id: ${newId}`); - return; - } - - // stopping live reloading timer if any - this.clearTimer(); - this.selectDefaultTrack = false; - const lastTrack = this.currentTrack; - const track = tracks[newId] || null; - this.trackId = newId; - this.currentTrack = track; - this.toggleTrackModes(); - if (!track) { - // switch to -1 - this.hls.trigger(Events.SUBTITLE_TRACK_SWITCH, { - id: newId - }); - return; - } - const trackLoaded = !!track.details && !track.details.live; - if (newId === this.trackId && track === lastTrack && trackLoaded) { - return; - } - this.log(`Switching to subtitle-track ${newId}` + (track ? ` "${track.name}" lang:${track.lang} group:${track.groupId}` : '')); - const { - id, - groupId = '', - name, - type, - url - } = track; - this.hls.trigger(Events.SUBTITLE_TRACK_SWITCH, { - id, - groupId, - name, - type, - url - }); - const hlsUrlParameters = this.switchParams(track.url, lastTrack == null ? void 0 : lastTrack.details, track.details); - this.loadPlaylist(hlsUrlParameters); - } -} - -class BufferOperationQueue { - constructor(sourceBufferReference) { - this.buffers = void 0; - this.queues = { - video: [], - audio: [], - audiovideo: [] - }; - this.buffers = sourceBufferReference; - } - append(operation, type, pending) { - const queue = this.queues[type]; - queue.push(operation); - if (queue.length === 1 && !pending) { - this.executeNext(type); - } - } - insertAbort(operation, type) { - const queue = this.queues[type]; - queue.unshift(operation); - this.executeNext(type); - } - appendBlocker(type) { - let execute; - const promise = new Promise(resolve => { - execute = resolve; - }); - const operation = { - execute, - onStart: () => {}, - onComplete: () => {}, - onError: () => {} - }; - this.append(operation, type); - return promise; - } - executeNext(type) { - const queue = this.queues[type]; - if (queue.length) { - const operation = queue[0]; - try { - // Operations are expected to result in an 'updateend' event being fired. If not, the queue will lock. Operations - // which do not end with this event must call _onSBUpdateEnd manually - operation.execute(); - } catch (error) { - logger.warn(`[buffer-operation-queue]: Exception executing "${type}" SourceBuffer operation: ${error}`); - operation.onError(error); - - // Only shift the current operation off, otherwise the updateend handler will do this for us - const sb = this.buffers[type]; - if (!(sb != null && sb.updating)) { - this.shiftAndExecuteNext(type); - } - } - } - } - shiftAndExecuteNext(type) { - this.queues[type].shift(); - this.executeNext(type); - } - current(type) { - return this.queues[type][0]; - } -} - -const VIDEO_CODEC_PROFILE_REPLACE = /(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/; -class BufferController { - constructor(hls) { - // The level details used to determine duration, target-duration and live - this.details = null; - // cache the self generated object url to detect hijack of video tag - this._objectUrl = null; - // A queue of buffer operations which require the SourceBuffer to not be updating upon execution - this.operationQueue = void 0; - // References to event listeners for each SourceBuffer, so that they can be referenced for event removal - this.listeners = void 0; - this.hls = void 0; - // The number of BUFFER_CODEC events received before any sourceBuffers are created - this.bufferCodecEventsExpected = 0; - // The total number of BUFFER_CODEC events received - this._bufferCodecEventsTotal = 0; - // A reference to the attached media element - this.media = null; - // A reference to the active media source - this.mediaSource = null; - // Last MP3 audio chunk appended - this.lastMpegAudioChunk = null; - this.appendSource = void 0; - // counters - this.appendErrors = { - audio: 0, - video: 0, - audiovideo: 0 - }; - this.tracks = {}; - this.pendingTracks = {}; - this.sourceBuffer = void 0; - this.log = void 0; - this.warn = void 0; - this.error = void 0; - this._onEndStreaming = event => { - if (!this.hls) { - return; - } - this.hls.pauseBuffering(); - }; - this._onStartStreaming = event => { - if (!this.hls) { - return; - } - this.hls.resumeBuffering(); - }; - // Keep as arrow functions so that we can directly reference these functions directly as event listeners - this._onMediaSourceOpen = () => { - const { - media, - mediaSource - } = this; - this.log('Media source opened'); - if (media) { - media.removeEventListener('emptied', this._onMediaEmptied); - this.updateMediaElementDuration(); - this.hls.trigger(Events.MEDIA_ATTACHED, { - media, - mediaSource: mediaSource - }); - } - if (mediaSource) { - // once received, don't listen anymore to sourceopen event - mediaSource.removeEventListener('sourceopen', this._onMediaSourceOpen); - } - this.checkPendingTracks(); - }; - this._onMediaSourceClose = () => { - this.log('Media source closed'); - }; - this._onMediaSourceEnded = () => { - this.log('Media source ended'); - }; - this._onMediaEmptied = () => { - const { - mediaSrc, - _objectUrl - } = this; - if (mediaSrc !== _objectUrl) { - logger.error(`Media element src was set while attaching MediaSource (${_objectUrl} > ${mediaSrc})`); - } - }; - this.hls = hls; - const logPrefix = '[buffer-controller]'; - this.appendSource = isManagedMediaSource(getMediaSource(hls.config.preferManagedMediaSource)); - this.log = logger.log.bind(logger, logPrefix); - this.warn = logger.warn.bind(logger, logPrefix); - this.error = logger.error.bind(logger, logPrefix); - this._initSourceBuffer(); - this.registerListeners(); - } - hasSourceTypes() { - return this.getSourceBufferTypes().length > 0 || Object.keys(this.pendingTracks).length > 0; - } - destroy() { - this.unregisterListeners(); - this.details = null; - this.lastMpegAudioChunk = null; - // @ts-ignore - this.hls = null; - } - registerListeners() { - const { - hls - } = this; - hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); - hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this); - hls.on(Events.BUFFER_RESET, this.onBufferReset, this); - hls.on(Events.BUFFER_APPENDING, this.onBufferAppending, this); - hls.on(Events.BUFFER_CODECS, this.onBufferCodecs, this); - hls.on(Events.BUFFER_EOS, this.onBufferEos, this); - hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); - hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this); - hls.on(Events.FRAG_PARSED, this.onFragParsed, this); - hls.on(Events.FRAG_CHANGED, this.onFragChanged, this); - } - unregisterListeners() { - const { - hls - } = this; - hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); - hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this); - hls.off(Events.BUFFER_RESET, this.onBufferReset, this); - hls.off(Events.BUFFER_APPENDING, this.onBufferAppending, this); - hls.off(Events.BUFFER_CODECS, this.onBufferCodecs, this); - hls.off(Events.BUFFER_EOS, this.onBufferEos, this); - hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); - hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this); - hls.off(Events.FRAG_PARSED, this.onFragParsed, this); - hls.off(Events.FRAG_CHANGED, this.onFragChanged, this); - } - _initSourceBuffer() { - this.sourceBuffer = {}; - this.operationQueue = new BufferOperationQueue(this.sourceBuffer); - this.listeners = { - audio: [], - video: [], - audiovideo: [] - }; - this.appendErrors = { - audio: 0, - video: 0, - audiovideo: 0 - }; - this.lastMpegAudioChunk = null; - } - onManifestLoading() { - this.bufferCodecEventsExpected = this._bufferCodecEventsTotal = 0; - this.details = null; - } - onManifestParsed(event, data) { - // in case of alt audio 2 BUFFER_CODECS events will be triggered, one per stream controller - // sourcebuffers will be created all at once when the expected nb of tracks will be reached - // in case alt audio is not used, only one BUFFER_CODEC event will be fired from main stream controller - // it will contain the expected nb of source buffers, no need to compute it - let codecEvents = 2; - if (data.audio && !data.video || !data.altAudio || !true) { - codecEvents = 1; - } - this.bufferCodecEventsExpected = this._bufferCodecEventsTotal = codecEvents; - this.log(`${this.bufferCodecEventsExpected} bufferCodec event(s) expected`); - } - onMediaAttaching(event, data) { - const media = this.media = data.media; - const MediaSource = getMediaSource(this.appendSource); - if (media && MediaSource) { - var _ms$constructor; - const ms = this.mediaSource = new MediaSource(); - this.log(`created media source: ${(_ms$constructor = ms.constructor) == null ? void 0 : _ms$constructor.name}`); - // MediaSource listeners are arrow functions with a lexical scope, and do not need to be bound - ms.addEventListener('sourceopen', this._onMediaSourceOpen); - ms.addEventListener('sourceended', this._onMediaSourceEnded); - ms.addEventListener('sourceclose', this._onMediaSourceClose); - if (this.appendSource) { - ms.addEventListener('startstreaming', this._onStartStreaming); - ms.addEventListener('endstreaming', this._onEndStreaming); - } - - // cache the locally generated object url - const objectUrl = this._objectUrl = self.URL.createObjectURL(ms); - // link video and media Source - if (this.appendSource) { - try { - media.removeAttribute('src'); - // ManagedMediaSource will not open without disableRemotePlayback set to false or source alternatives - const MMS = self.ManagedMediaSource; - media.disableRemotePlayback = media.disableRemotePlayback || MMS && ms instanceof MMS; - removeSourceChildren(media); - addSource(media, objectUrl); - media.load(); - } catch (error) { - media.src = objectUrl; - } - } else { - media.src = objectUrl; - } - media.addEventListener('emptied', this._onMediaEmptied); - } - } - onMediaDetaching() { - const { - media, - mediaSource, - _objectUrl - } = this; - if (mediaSource) { - this.log('media source detaching'); - if (mediaSource.readyState === 'open') { - try { - // endOfStream could trigger exception if any sourcebuffer is in updating state - // we don't really care about checking sourcebuffer state here, - // as we are anyway detaching the MediaSource - // let's just avoid this exception to propagate - mediaSource.endOfStream(); - } catch (err) { - this.warn(`onMediaDetaching: ${err.message} while calling endOfStream`); - } - } - // Clean up the SourceBuffers by invoking onBufferReset - this.onBufferReset(); - mediaSource.removeEventListener('sourceopen', this._onMediaSourceOpen); - mediaSource.removeEventListener('sourceended', this._onMediaSourceEnded); - mediaSource.removeEventListener('sourceclose', this._onMediaSourceClose); - if (this.appendSource) { - mediaSource.removeEventListener('startstreaming', this._onStartStreaming); - mediaSource.removeEventListener('endstreaming', this._onEndStreaming); - } - - // Detach properly the MediaSource from the HTMLMediaElement as - // suggested in https://github.com/w3c/media-source/issues/53. - if (media) { - media.removeEventListener('emptied', this._onMediaEmptied); - if (_objectUrl) { - self.URL.revokeObjectURL(_objectUrl); - } - - // clean up video tag src only if it's our own url. some external libraries might - // hijack the video tag and change its 'src' without destroying the Hls instance first - if (this.mediaSrc === _objectUrl) { - media.removeAttribute('src'); - if (this.appendSource) { - removeSourceChildren(media); - } - media.load(); - } else { - this.warn('media|source.src was changed by a third party - skip cleanup'); - } - } - this.mediaSource = null; - this.media = null; - this._objectUrl = null; - this.bufferCodecEventsExpected = this._bufferCodecEventsTotal; - this.pendingTracks = {}; - this.tracks = {}; - } - this.hls.trigger(Events.MEDIA_DETACHED, undefined); - } - onBufferReset() { - this.getSourceBufferTypes().forEach(type => { - this.resetBuffer(type); - }); - this._initSourceBuffer(); - } - resetBuffer(type) { - const sb = this.sourceBuffer[type]; - try { - if (sb) { - var _this$mediaSource; - this.removeBufferListeners(type); - // Synchronously remove the SB from the map before the next call in order to prevent an async function from - // accessing it - this.sourceBuffer[type] = undefined; - if ((_this$mediaSource = this.mediaSource) != null && _this$mediaSource.sourceBuffers.length) { - this.mediaSource.removeSourceBuffer(sb); - } - } - } catch (err) { - this.warn(`onBufferReset ${type}`, err); - } - } - onBufferCodecs(event, data) { - const sourceBufferCount = this.getSourceBufferTypes().length; - const trackNames = Object.keys(data); - trackNames.forEach(trackName => { - if (sourceBufferCount) { - // check if SourceBuffer codec needs to change - const track = this.tracks[trackName]; - if (track && typeof track.buffer.changeType === 'function') { - var _trackCodec; - const { - id, - codec, - levelCodec, - container, - metadata - } = data[trackName]; - const currentCodecFull = pickMostCompleteCodecName(track.codec, track.levelCodec); - const currentCodec = currentCodecFull == null ? void 0 : currentCodecFull.replace(VIDEO_CODEC_PROFILE_REPLACE, '$1'); - let trackCodec = pickMostCompleteCodecName(codec, levelCodec); - const nextCodec = (_trackCodec = trackCodec) == null ? void 0 : _trackCodec.replace(VIDEO_CODEC_PROFILE_REPLACE, '$1'); - if (trackCodec && currentCodec !== nextCodec) { - if (trackName.slice(0, 5) === 'audio') { - trackCodec = getCodecCompatibleName(trackCodec, this.appendSource); - } - const mimeType = `${container};codecs=${trackCodec}`; - this.appendChangeType(trackName, mimeType); - this.log(`switching codec ${currentCodecFull} to ${trackCodec}`); - this.tracks[trackName] = { - buffer: track.buffer, - codec, - container, - levelCodec, - metadata, - id - }; - } - } - } else { - // if source buffer(s) not created yet, appended buffer tracks in this.pendingTracks - this.pendingTracks[trackName] = data[trackName]; - } - }); - - // if sourcebuffers already created, do nothing ... - if (sourceBufferCount) { - return; - } - const bufferCodecEventsExpected = Math.max(this.bufferCodecEventsExpected - 1, 0); - if (this.bufferCodecEventsExpected !== bufferCodecEventsExpected) { - this.log(`${bufferCodecEventsExpected} bufferCodec event(s) expected ${trackNames.join(',')}`); - this.bufferCodecEventsExpected = bufferCodecEventsExpected; - } - if (this.mediaSource && this.mediaSource.readyState === 'open') { - this.checkPendingTracks(); - } - } - appendChangeType(type, mimeType) { - const { - operationQueue - } = this; - const operation = { - execute: () => { - const sb = this.sourceBuffer[type]; - if (sb) { - this.log(`changing ${type} sourceBuffer type to ${mimeType}`); - sb.changeType(mimeType); - } - operationQueue.shiftAndExecuteNext(type); - }, - onStart: () => {}, - onComplete: () => {}, - onError: error => { - this.warn(`Failed to change ${type} SourceBuffer type`, error); - } - }; - operationQueue.append(operation, type, !!this.pendingTracks[type]); - } - onBufferAppending(event, eventData) { - const { - hls, - operationQueue, - tracks - } = this; - const { - data, - type, - frag, - part, - chunkMeta - } = eventData; - const chunkStats = chunkMeta.buffering[type]; - const bufferAppendingStart = self.performance.now(); - chunkStats.start = bufferAppendingStart; - const fragBuffering = frag.stats.buffering; - const partBuffering = part ? part.stats.buffering : null; - if (fragBuffering.start === 0) { - fragBuffering.start = bufferAppendingStart; - } - if (partBuffering && partBuffering.start === 0) { - partBuffering.start = bufferAppendingStart; - } - - // TODO: Only update timestampOffset when audio/mpeg fragment or part is not contiguous with previously appended - // Adjusting `SourceBuffer.timestampOffset` (desired point in the timeline where the next frames should be appended) - // in Chrome browser when we detect MPEG audio container and time delta between level PTS and `SourceBuffer.timestampOffset` - // is greater than 100ms (this is enough to handle seek for VOD or level change for LIVE videos). - // More info here: https://github.com/video-dev/hls.js/issues/332#issuecomment-257986486 - const audioTrack = tracks.audio; - let checkTimestampOffset = false; - if (type === 'audio' && (audioTrack == null ? void 0 : audioTrack.container) === 'audio/mpeg') { - checkTimestampOffset = !this.lastMpegAudioChunk || chunkMeta.id === 1 || this.lastMpegAudioChunk.sn !== chunkMeta.sn; - this.lastMpegAudioChunk = chunkMeta; - } - const fragStart = frag.start; - const operation = { - execute: () => { - chunkStats.executeStart = self.performance.now(); - if (checkTimestampOffset) { - const sb = this.sourceBuffer[type]; - if (sb) { - const delta = fragStart - sb.timestampOffset; - if (Math.abs(delta) >= 0.1) { - this.log(`Updating audio SourceBuffer timestampOffset to ${fragStart} (delta: ${delta}) sn: ${frag.sn})`); - sb.timestampOffset = fragStart; - } - } - } - this.appendExecutor(data, type); - }, - onStart: () => { - // logger.debug(`[buffer-controller]: ${type} SourceBuffer updatestart`); - }, - onComplete: () => { - // logger.debug(`[buffer-controller]: ${type} SourceBuffer updateend`); - const end = self.performance.now(); - chunkStats.executeEnd = chunkStats.end = end; - if (fragBuffering.first === 0) { - fragBuffering.first = end; - } - if (partBuffering && partBuffering.first === 0) { - partBuffering.first = end; - } - const { - sourceBuffer - } = this; - const timeRanges = {}; - for (const type in sourceBuffer) { - timeRanges[type] = BufferHelper.getBuffered(sourceBuffer[type]); - } - this.appendErrors[type] = 0; - if (type === 'audio' || type === 'video') { - this.appendErrors.audiovideo = 0; - } else { - this.appendErrors.audio = 0; - this.appendErrors.video = 0; - } - this.hls.trigger(Events.BUFFER_APPENDED, { - type, - frag, - part, - chunkMeta, - parent: frag.type, - timeRanges - }); - }, - onError: error => { - // in case any error occured while appending, put back segment in segments table - const event = { - type: ErrorTypes.MEDIA_ERROR, - parent: frag.type, - details: ErrorDetails.BUFFER_APPEND_ERROR, - sourceBufferName: type, - frag, - part, - chunkMeta, - error, - err: error, - fatal: false - }; - if (error.code === DOMException.QUOTA_EXCEEDED_ERR) { - // QuotaExceededError: http://www.w3.org/TR/html5/infrastructure.html#quotaexceedederror - // let's stop appending any segments, and report BUFFER_FULL_ERROR error - event.details = ErrorDetails.BUFFER_FULL_ERROR; - } else { - const appendErrorCount = ++this.appendErrors[type]; - event.details = ErrorDetails.BUFFER_APPEND_ERROR; - /* with UHD content, we could get loop of quota exceeded error until - browser is able to evict some data from sourcebuffer. Retrying can help recover. - */ - this.warn(`Failed ${appendErrorCount}/${hls.config.appendErrorMaxRetry} times to append segment in "${type}" sourceBuffer`); - if (appendErrorCount >= hls.config.appendErrorMaxRetry) { - event.fatal = true; - } - } - hls.trigger(Events.ERROR, event); - } - }; - operationQueue.append(operation, type, !!this.pendingTracks[type]); - } - onBufferFlushing(event, data) { - const { - operationQueue - } = this; - const flushOperation = type => ({ - execute: this.removeExecutor.bind(this, type, data.startOffset, data.endOffset), - onStart: () => { - // logger.debug(`[buffer-controller]: Started flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`); - }, - onComplete: () => { - // logger.debug(`[buffer-controller]: Finished flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`); - this.hls.trigger(Events.BUFFER_FLUSHED, { - type - }); - }, - onError: error => { - this.warn(`Failed to remove from ${type} SourceBuffer`, error); - } - }); - if (data.type) { - operationQueue.append(flushOperation(data.type), data.type); - } else { - this.getSourceBufferTypes().forEach(type => { - operationQueue.append(flushOperation(type), type); - }); - } - } - onFragParsed(event, data) { - const { - frag, - part - } = data; - const buffersAppendedTo = []; - const elementaryStreams = part ? part.elementaryStreams : frag.elementaryStreams; - if (elementaryStreams[ElementaryStreamTypes.AUDIOVIDEO]) { - buffersAppendedTo.push('audiovideo'); - } else { - if (elementaryStreams[ElementaryStreamTypes.AUDIO]) { - buffersAppendedTo.push('audio'); - } - if (elementaryStreams[ElementaryStreamTypes.VIDEO]) { - buffersAppendedTo.push('video'); - } - } - const onUnblocked = () => { - const now = self.performance.now(); - frag.stats.buffering.end = now; - if (part) { - part.stats.buffering.end = now; - } - const stats = part ? part.stats : frag.stats; - this.hls.trigger(Events.FRAG_BUFFERED, { - frag, - part, - stats, - id: frag.type - }); - }; - if (buffersAppendedTo.length === 0) { - this.warn(`Fragments must have at least one ElementaryStreamType set. type: ${frag.type} level: ${frag.level} sn: ${frag.sn}`); - } - this.blockBuffers(onUnblocked, buffersAppendedTo); - } - onFragChanged(event, data) { - this.trimBuffers(); - } - - // on BUFFER_EOS mark matching sourcebuffer(s) as ended and trigger checkEos() - // an undefined data.type will mark all buffers as EOS. - onBufferEos(event, data) { - const ended = this.getSourceBufferTypes().reduce((acc, type) => { - const sb = this.sourceBuffer[type]; - if (sb && (!data.type || data.type === type)) { - sb.ending = true; - if (!sb.ended) { - sb.ended = true; - this.log(`${type} sourceBuffer now EOS`); - } - } - return acc && !!(!sb || sb.ended); - }, true); - if (ended) { - this.log(`Queueing mediaSource.endOfStream()`); - this.blockBuffers(() => { - this.getSourceBufferTypes().forEach(type => { - const sb = this.sourceBuffer[type]; - if (sb) { - sb.ending = false; - } - }); - const { - mediaSource - } = this; - if (!mediaSource || mediaSource.readyState !== 'open') { - if (mediaSource) { - this.log(`Could not call mediaSource.endOfStream(). mediaSource.readyState: ${mediaSource.readyState}`); - } - return; - } - this.log(`Calling mediaSource.endOfStream()`); - // Allow this to throw and be caught by the enqueueing function - mediaSource.endOfStream(); - }); - } - } - onLevelUpdated(event, { - details - }) { - if (!details.fragments.length) { - return; - } - this.details = details; - if (this.getSourceBufferTypes().length) { - this.blockBuffers(this.updateMediaElementDuration.bind(this)); - } else { - this.updateMediaElementDuration(); - } - } - trimBuffers() { - const { - hls, - details, - media - } = this; - if (!media || details === null) { - return; - } - const sourceBufferTypes = this.getSourceBufferTypes(); - if (!sourceBufferTypes.length) { - return; - } - const config = hls.config; - const currentTime = media.currentTime; - const targetDuration = details.levelTargetDuration; - - // Support for deprecated liveBackBufferLength - const backBufferLength = details.live && config.liveBackBufferLength !== null ? config.liveBackBufferLength : config.backBufferLength; - if (isFiniteNumber(backBufferLength) && backBufferLength > 0) { - const maxBackBufferLength = Math.max(backBufferLength, targetDuration); - const targetBackBufferPosition = Math.floor(currentTime / targetDuration) * targetDuration - maxBackBufferLength; - this.flushBackBuffer(currentTime, targetDuration, targetBackBufferPosition); - } - if (isFiniteNumber(config.frontBufferFlushThreshold) && config.frontBufferFlushThreshold > 0) { - const frontBufferLength = Math.max(config.maxBufferLength, config.frontBufferFlushThreshold); - const maxFrontBufferLength = Math.max(frontBufferLength, targetDuration); - const targetFrontBufferPosition = Math.floor(currentTime / targetDuration) * targetDuration + maxFrontBufferLength; - this.flushFrontBuffer(currentTime, targetDuration, targetFrontBufferPosition); - } - } - flushBackBuffer(currentTime, targetDuration, targetBackBufferPosition) { - const { - details, - sourceBuffer - } = this; - const sourceBufferTypes = this.getSourceBufferTypes(); - sourceBufferTypes.forEach(type => { - const sb = sourceBuffer[type]; - if (sb) { - const buffered = BufferHelper.getBuffered(sb); - // when target buffer start exceeds actual buffer start - if (buffered.length > 0 && targetBackBufferPosition > buffered.start(0)) { - this.hls.trigger(Events.BACK_BUFFER_REACHED, { - bufferEnd: targetBackBufferPosition - }); - - // Support for deprecated event: - if (details != null && details.live) { - this.hls.trigger(Events.LIVE_BACK_BUFFER_REACHED, { - bufferEnd: targetBackBufferPosition - }); - } else if (sb.ended && buffered.end(buffered.length - 1) - currentTime < targetDuration * 2) { - this.log(`Cannot flush ${type} back buffer while SourceBuffer is in ended state`); - return; - } - this.hls.trigger(Events.BUFFER_FLUSHING, { - startOffset: 0, - endOffset: targetBackBufferPosition, - type - }); - } - } - }); - } - flushFrontBuffer(currentTime, targetDuration, targetFrontBufferPosition) { - const { - sourceBuffer - } = this; - const sourceBufferTypes = this.getSourceBufferTypes(); - sourceBufferTypes.forEach(type => { - const sb = sourceBuffer[type]; - if (sb) { - const buffered = BufferHelper.getBuffered(sb); - const numBufferedRanges = buffered.length; - // The buffer is either empty or contiguous - if (numBufferedRanges < 2) { - return; - } - const bufferStart = buffered.start(numBufferedRanges - 1); - const bufferEnd = buffered.end(numBufferedRanges - 1); - // No flush if we can tolerate the current buffer length or the current buffer range we would flush is contiguous with current position - if (targetFrontBufferPosition > bufferStart || currentTime >= bufferStart && currentTime <= bufferEnd) { - return; - } else if (sb.ended && currentTime - bufferEnd < 2 * targetDuration) { - this.log(`Cannot flush ${type} front buffer while SourceBuffer is in ended state`); - return; - } - this.hls.trigger(Events.BUFFER_FLUSHING, { - startOffset: bufferStart, - endOffset: Infinity, - type - }); - } - }); - } - - /** - * Update Media Source duration to current level duration or override to Infinity if configuration parameter - * 'liveDurationInfinity` is set to `true` - * More details: https://github.com/video-dev/hls.js/issues/355 - */ - updateMediaElementDuration() { - if (!this.details || !this.media || !this.mediaSource || this.mediaSource.readyState !== 'open') { - return; - } - const { - details, - hls, - media, - mediaSource - } = this; - const levelDuration = details.fragments[0].start + details.totalduration; - const mediaDuration = media.duration; - const msDuration = isFiniteNumber(mediaSource.duration) ? mediaSource.duration : 0; - if (details.live && hls.config.liveDurationInfinity) { - // Override duration to Infinity - mediaSource.duration = Infinity; - this.updateSeekableRange(details); - } else if (levelDuration > msDuration && levelDuration > mediaDuration || !isFiniteNumber(mediaDuration)) { - // levelDuration was the last value we set. - // not using mediaSource.duration as the browser may tweak this value - // only update Media Source duration if its value increase, this is to avoid - // flushing already buffered portion when switching between quality level - this.log(`Updating Media Source duration to ${levelDuration.toFixed(3)}`); - mediaSource.duration = levelDuration; - } - } - updateSeekableRange(levelDetails) { - const mediaSource = this.mediaSource; - const fragments = levelDetails.fragments; - const len = fragments.length; - if (len && levelDetails.live && mediaSource != null && mediaSource.setLiveSeekableRange) { - const start = Math.max(0, fragments[0].start); - const end = Math.max(start, start + levelDetails.totalduration); - this.log(`Media Source duration is set to ${mediaSource.duration}. Setting seekable range to ${start}-${end}.`); - mediaSource.setLiveSeekableRange(start, end); - } - } - checkPendingTracks() { - const { - bufferCodecEventsExpected, - operationQueue, - pendingTracks - } = this; - - // Check if we've received all of the expected bufferCodec events. When none remain, create all the sourceBuffers at once. - // This is important because the MSE spec allows implementations to throw QuotaExceededErrors if creating new sourceBuffers after - // data has been appended to existing ones. - // 2 tracks is the max (one for audio, one for video). If we've reach this max go ahead and create the buffers. - const pendingTracksCount = Object.keys(pendingTracks).length; - if (pendingTracksCount && (!bufferCodecEventsExpected || pendingTracksCount === 2 || 'audiovideo' in pendingTracks)) { - // ok, let's create them now ! - this.createSourceBuffers(pendingTracks); - this.pendingTracks = {}; - // append any pending segments now ! - const buffers = this.getSourceBufferTypes(); - if (buffers.length) { - this.hls.trigger(Events.BUFFER_CREATED, { - tracks: this.tracks - }); - buffers.forEach(type => { - operationQueue.executeNext(type); - }); - } else { - const error = new Error('could not create source buffer for media codec(s)'); - this.hls.trigger(Events.ERROR, { - type: ErrorTypes.MEDIA_ERROR, - details: ErrorDetails.BUFFER_INCOMPATIBLE_CODECS_ERROR, - fatal: true, - error, - reason: error.message - }); - } - } - } - createSourceBuffers(tracks) { - const { - sourceBuffer, - mediaSource - } = this; - if (!mediaSource) { - throw Error('createSourceBuffers called when mediaSource was null'); - } - for (const trackName in tracks) { - if (!sourceBuffer[trackName]) { - var _track$levelCodec; - const track = tracks[trackName]; - if (!track) { - throw Error(`source buffer exists for track ${trackName}, however track does not`); - } - // use levelCodec as first priority unless it contains multiple comma-separated codec values - let codec = ((_track$levelCodec = track.levelCodec) == null ? void 0 : _track$levelCodec.indexOf(',')) === -1 ? track.levelCodec : track.codec; - if (codec) { - if (trackName.slice(0, 5) === 'audio') { - codec = getCodecCompatibleName(codec, this.appendSource); - } - } - const mimeType = `${track.container};codecs=${codec}`; - this.log(`creating sourceBuffer(${mimeType})`); - try { - const sb = sourceBuffer[trackName] = mediaSource.addSourceBuffer(mimeType); - const sbName = trackName; - this.addBufferListener(sbName, 'updatestart', this._onSBUpdateStart); - this.addBufferListener(sbName, 'updateend', this._onSBUpdateEnd); - this.addBufferListener(sbName, 'error', this._onSBUpdateError); - // ManagedSourceBuffer bufferedchange event - if (this.appendSource) { - this.addBufferListener(sbName, 'bufferedchange', (type, event) => { - // If media was ejected check for a change. Added ranges are redundant with changes on 'updateend' event. - const removedRanges = event.removedRanges; - if (removedRanges != null && removedRanges.length) { - this.hls.trigger(Events.BUFFER_FLUSHED, { - type: trackName - }); - } - }); - } - this.tracks[trackName] = { - buffer: sb, - codec: codec, - container: track.container, - levelCodec: track.levelCodec, - metadata: track.metadata, - id: track.id - }; - } catch (err) { - this.error(`error while trying to add sourceBuffer: ${err.message}`); - this.hls.trigger(Events.ERROR, { - type: ErrorTypes.MEDIA_ERROR, - details: ErrorDetails.BUFFER_ADD_CODEC_ERROR, - fatal: false, - error: err, - sourceBufferName: trackName, - mimeType: mimeType - }); - } - } - } - } - get mediaSrc() { - var _this$media, _this$media$querySele; - const media = ((_this$media = this.media) == null ? void 0 : (_this$media$querySele = _this$media.querySelector) == null ? void 0 : _this$media$querySele.call(_this$media, 'source')) || this.media; - return media == null ? void 0 : media.src; - } - _onSBUpdateStart(type) { - const { - operationQueue - } = this; - const operation = operationQueue.current(type); - operation.onStart(); - } - _onSBUpdateEnd(type) { - var _this$mediaSource2; - if (((_this$mediaSource2 = this.mediaSource) == null ? void 0 : _this$mediaSource2.readyState) === 'closed') { - this.resetBuffer(type); - return; - } - const { - operationQueue - } = this; - const operation = operationQueue.current(type); - operation.onComplete(); - operationQueue.shiftAndExecuteNext(type); - } - _onSBUpdateError(type, event) { - var _this$mediaSource3; - const error = new Error(`${type} SourceBuffer error. MediaSource readyState: ${(_this$mediaSource3 = this.mediaSource) == null ? void 0 : _this$mediaSource3.readyState}`); - this.error(`${error}`, event); - // according to http://www.w3.org/TR/media-source/#sourcebuffer-append-error - // SourceBuffer errors are not necessarily fatal; if so, the HTMLMediaElement will fire an error event - this.hls.trigger(Events.ERROR, { - type: ErrorTypes.MEDIA_ERROR, - details: ErrorDetails.BUFFER_APPENDING_ERROR, - sourceBufferName: type, - error, - fatal: false - }); - // updateend is always fired after error, so we'll allow that to shift the current operation off of the queue - const operation = this.operationQueue.current(type); - if (operation) { - operation.onError(error); - } - } - - // This method must result in an updateend event; if remove is not called, _onSBUpdateEnd must be called manually - removeExecutor(type, startOffset, endOffset) { - const { - media, - mediaSource, - operationQueue, - sourceBuffer - } = this; - const sb = sourceBuffer[type]; - if (!media || !mediaSource || !sb) { - this.warn(`Attempting to remove from the ${type} SourceBuffer, but it does not exist`); - operationQueue.shiftAndExecuteNext(type); - return; - } - const mediaDuration = isFiniteNumber(media.duration) ? media.duration : Infinity; - const msDuration = isFiniteNumber(mediaSource.duration) ? mediaSource.duration : Infinity; - const removeStart = Math.max(0, startOffset); - const removeEnd = Math.min(endOffset, mediaDuration, msDuration); - if (removeEnd > removeStart && (!sb.ending || sb.ended)) { - sb.ended = false; - this.log(`Removing [${removeStart},${removeEnd}] from the ${type} SourceBuffer`); - sb.remove(removeStart, removeEnd); - } else { - // Cycle the queue - operationQueue.shiftAndExecuteNext(type); - } - } - - // This method must result in an updateend event; if append is not called, _onSBUpdateEnd must be called manually - appendExecutor(data, type) { - const sb = this.sourceBuffer[type]; - if (!sb) { - if (!this.pendingTracks[type]) { - throw new Error(`Attempting to append to the ${type} SourceBuffer, but it does not exist`); - } - return; - } - sb.ended = false; - sb.appendBuffer(data); - } - - // Enqueues an operation to each SourceBuffer queue which, upon execution, resolves a promise. When all promises - // resolve, the onUnblocked function is executed. Functions calling this method do not need to unblock the queue - // upon completion, since we already do it here - blockBuffers(onUnblocked, buffers = this.getSourceBufferTypes()) { - if (!buffers.length) { - this.log('Blocking operation requested, but no SourceBuffers exist'); - Promise.resolve().then(onUnblocked); - return; - } - const { - operationQueue - } = this; - - // logger.debug(`[buffer-controller]: Blocking ${buffers} SourceBuffer`); - const blockingOperations = buffers.map(type => operationQueue.appendBlocker(type)); - Promise.all(blockingOperations).then(() => { - // logger.debug(`[buffer-controller]: Blocking operation resolved; unblocking ${buffers} SourceBuffer`); - onUnblocked(); - buffers.forEach(type => { - const sb = this.sourceBuffer[type]; - // Only cycle the queue if the SB is not updating. There's a bug in Chrome which sets the SB updating flag to - // true when changing the MediaSource duration (https://bugs.chromium.org/p/chromium/issues/detail?id=959359&can=2&q=mediasource%20duration) - // While this is a workaround, it's probably useful to have around - if (!(sb != null && sb.updating)) { - operationQueue.shiftAndExecuteNext(type); - } - }); - }); - } - getSourceBufferTypes() { - return Object.keys(this.sourceBuffer); - } - addBufferListener(type, event, fn) { - const buffer = this.sourceBuffer[type]; - if (!buffer) { - return; - } - const listener = fn.bind(this, type); - this.listeners[type].push({ - event, - listener - }); - buffer.addEventListener(event, listener); - } - removeBufferListeners(type) { - const buffer = this.sourceBuffer[type]; - if (!buffer) { - return; - } - this.listeners[type].forEach(l => { - buffer.removeEventListener(l.event, l.listener); - }); - } -} -function removeSourceChildren(node) { - const sourceChildren = node.querySelectorAll('source'); - [].slice.call(sourceChildren).forEach(source => { - node.removeChild(source); - }); -} -function addSource(media, url) { - const source = self.document.createElement('source'); - source.type = 'video/mp4'; - source.src = url; - media.appendChild(source); -} - -/** - * - * This code was ported from the dash.js project at: - * https://github.com/Dash-Industry-Forum/dash.js/blob/development/externals/cea608-parser.js - * https://github.com/Dash-Industry-Forum/dash.js/commit/8269b26a761e0853bb21d78780ed945144ecdd4d#diff-71bc295a2d6b6b7093a1d3290d53a4b2 - * - * The original copyright appears below: - * - * The copyright in this software is being made available under the BSD License, - * included below. This software may be subject to other third party and contributor - * rights, including patent rights, and no such rights are granted under this license. - * - * Copyright (c) 2015-2016, DASH Industry Forum. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * 2. Neither the name of Dash Industry Forum nor the names of its - * contributors may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -/** - * Exceptions from regular ASCII. CodePoints are mapped to UTF-16 codes - */ - -const specialCea608CharsCodes = { - 0x2a: 0xe1, - // lowercase a, acute accent - 0x5c: 0xe9, - // lowercase e, acute accent - 0x5e: 0xed, - // lowercase i, acute accent - 0x5f: 0xf3, - // lowercase o, acute accent - 0x60: 0xfa, - // lowercase u, acute accent - 0x7b: 0xe7, - // lowercase c with cedilla - 0x7c: 0xf7, - // division symbol - 0x7d: 0xd1, - // uppercase N tilde - 0x7e: 0xf1, - // lowercase n tilde - 0x7f: 0x2588, - // Full block - // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS - // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F - // THIS MEANS THAT \x50 MUST BE ADDED TO THE VALUES - 0x80: 0xae, - // Registered symbol (R) - 0x81: 0xb0, - // degree sign - 0x82: 0xbd, - // 1/2 symbol - 0x83: 0xbf, - // Inverted (open) question mark - 0x84: 0x2122, - // Trademark symbol (TM) - 0x85: 0xa2, - // Cents symbol - 0x86: 0xa3, - // Pounds sterling - 0x87: 0x266a, - // Music 8'th note - 0x88: 0xe0, - // lowercase a, grave accent - 0x89: 0x20, - // transparent space (regular) - 0x8a: 0xe8, - // lowercase e, grave accent - 0x8b: 0xe2, - // lowercase a, circumflex accent - 0x8c: 0xea, - // lowercase e, circumflex accent - 0x8d: 0xee, - // lowercase i, circumflex accent - 0x8e: 0xf4, - // lowercase o, circumflex accent - 0x8f: 0xfb, - // lowercase u, circumflex accent - // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS - // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F - 0x90: 0xc1, - // capital letter A with acute - 0x91: 0xc9, - // capital letter E with acute - 0x92: 0xd3, - // capital letter O with acute - 0x93: 0xda, - // capital letter U with acute - 0x94: 0xdc, - // capital letter U with diaresis - 0x95: 0xfc, - // lowercase letter U with diaeresis - 0x96: 0x2018, - // opening single quote - 0x97: 0xa1, - // inverted exclamation mark - 0x98: 0x2a, - // asterisk - 0x99: 0x2019, - // closing single quote - 0x9a: 0x2501, - // box drawings heavy horizontal - 0x9b: 0xa9, - // copyright sign - 0x9c: 0x2120, - // Service mark - 0x9d: 0x2022, - // (round) bullet - 0x9e: 0x201c, - // Left double quotation mark - 0x9f: 0x201d, - // Right double quotation mark - 0xa0: 0xc0, - // uppercase A, grave accent - 0xa1: 0xc2, - // uppercase A, circumflex - 0xa2: 0xc7, - // uppercase C with cedilla - 0xa3: 0xc8, - // uppercase E, grave accent - 0xa4: 0xca, - // uppercase E, circumflex - 0xa5: 0xcb, - // capital letter E with diaresis - 0xa6: 0xeb, - // lowercase letter e with diaresis - 0xa7: 0xce, - // uppercase I, circumflex - 0xa8: 0xcf, - // uppercase I, with diaresis - 0xa9: 0xef, - // lowercase i, with diaresis - 0xaa: 0xd4, - // uppercase O, circumflex - 0xab: 0xd9, - // uppercase U, grave accent - 0xac: 0xf9, - // lowercase u, grave accent - 0xad: 0xdb, - // uppercase U, circumflex - 0xae: 0xab, - // left-pointing double angle quotation mark - 0xaf: 0xbb, - // right-pointing double angle quotation mark - // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS - // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F - 0xb0: 0xc3, - // Uppercase A, tilde - 0xb1: 0xe3, - // Lowercase a, tilde - 0xb2: 0xcd, - // Uppercase I, acute accent - 0xb3: 0xcc, - // Uppercase I, grave accent - 0xb4: 0xec, - // Lowercase i, grave accent - 0xb5: 0xd2, - // Uppercase O, grave accent - 0xb6: 0xf2, - // Lowercase o, grave accent - 0xb7: 0xd5, - // Uppercase O, tilde - 0xb8: 0xf5, - // Lowercase o, tilde - 0xb9: 0x7b, - // Open curly brace - 0xba: 0x7d, - // Closing curly brace - 0xbb: 0x5c, - // Backslash - 0xbc: 0x5e, - // Caret - 0xbd: 0x5f, - // Underscore - 0xbe: 0x7c, - // Pipe (vertical line) - 0xbf: 0x223c, - // Tilde operator - 0xc0: 0xc4, - // Uppercase A, umlaut - 0xc1: 0xe4, - // Lowercase A, umlaut - 0xc2: 0xd6, - // Uppercase O, umlaut - 0xc3: 0xf6, - // Lowercase o, umlaut - 0xc4: 0xdf, - // Esszett (sharp S) - 0xc5: 0xa5, - // Yen symbol - 0xc6: 0xa4, - // Generic currency sign - 0xc7: 0x2503, - // Box drawings heavy vertical - 0xc8: 0xc5, - // Uppercase A, ring - 0xc9: 0xe5, - // Lowercase A, ring - 0xca: 0xd8, - // Uppercase O, stroke - 0xcb: 0xf8, - // Lowercase o, strok - 0xcc: 0x250f, - // Box drawings heavy down and right - 0xcd: 0x2513, - // Box drawings heavy down and left - 0xce: 0x2517, - // Box drawings heavy up and right - 0xcf: 0x251b // Box drawings heavy up and left -}; - -/** - * Utils - */ -const getCharForByte = byte => String.fromCharCode(specialCea608CharsCodes[byte] || byte); -const NR_ROWS = 15; -const NR_COLS = 100; -// Tables to look up row from PAC data -const rowsLowCh1 = { - 0x11: 1, - 0x12: 3, - 0x15: 5, - 0x16: 7, - 0x17: 9, - 0x10: 11, - 0x13: 12, - 0x14: 14 -}; -const rowsHighCh1 = { - 0x11: 2, - 0x12: 4, - 0x15: 6, - 0x16: 8, - 0x17: 10, - 0x13: 13, - 0x14: 15 -}; -const rowsLowCh2 = { - 0x19: 1, - 0x1a: 3, - 0x1d: 5, - 0x1e: 7, - 0x1f: 9, - 0x18: 11, - 0x1b: 12, - 0x1c: 14 -}; -const rowsHighCh2 = { - 0x19: 2, - 0x1a: 4, - 0x1d: 6, - 0x1e: 8, - 0x1f: 10, - 0x1b: 13, - 0x1c: 15 -}; -const backgroundColors = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta', 'black', 'transparent']; -class CaptionsLogger { - constructor() { - this.time = null; - this.verboseLevel = 0; - } - log(severity, msg) { - if (this.verboseLevel >= severity) { - const m = typeof msg === 'function' ? msg() : msg; - logger.log(`${this.time} [${severity}] ${m}`); - } - } -} -const numArrayToHexArray = function numArrayToHexArray(numArray) { - const hexArray = []; - for (let j = 0; j < numArray.length; j++) { - hexArray.push(numArray[j].toString(16)); - } - return hexArray; -}; -class PenState { - constructor() { - this.foreground = 'white'; - this.underline = false; - this.italics = false; - this.background = 'black'; - this.flash = false; - } - reset() { - this.foreground = 'white'; - this.underline = false; - this.italics = false; - this.background = 'black'; - this.flash = false; - } - setStyles(styles) { - const attribs = ['foreground', 'underline', 'italics', 'background', 'flash']; - for (let i = 0; i < attribs.length; i++) { - const style = attribs[i]; - if (styles.hasOwnProperty(style)) { - this[style] = styles[style]; - } - } - } - isDefault() { - return this.foreground === 'white' && !this.underline && !this.italics && this.background === 'black' && !this.flash; - } - equals(other) { - return this.foreground === other.foreground && this.underline === other.underline && this.italics === other.italics && this.background === other.background && this.flash === other.flash; - } - copy(newPenState) { - this.foreground = newPenState.foreground; - this.underline = newPenState.underline; - this.italics = newPenState.italics; - this.background = newPenState.background; - this.flash = newPenState.flash; - } - toString() { - return 'color=' + this.foreground + ', underline=' + this.underline + ', italics=' + this.italics + ', background=' + this.background + ', flash=' + this.flash; - } -} - -/** - * Unicode character with styling and background. - * @constructor - */ -class StyledUnicodeChar { - constructor() { - this.uchar = ' '; - this.penState = new PenState(); - } - reset() { - this.uchar = ' '; - this.penState.reset(); - } - setChar(uchar, newPenState) { - this.uchar = uchar; - this.penState.copy(newPenState); - } - setPenState(newPenState) { - this.penState.copy(newPenState); - } - equals(other) { - return this.uchar === other.uchar && this.penState.equals(other.penState); - } - copy(newChar) { - this.uchar = newChar.uchar; - this.penState.copy(newChar.penState); - } - isEmpty() { - return this.uchar === ' ' && this.penState.isDefault(); - } -} - -/** - * CEA-608 row consisting of NR_COLS instances of StyledUnicodeChar. - * @constructor - */ -class Row { - constructor(logger) { - this.chars = []; - this.pos = 0; - this.currPenState = new PenState(); - this.cueStartTime = null; - this.logger = void 0; - for (let i = 0; i < NR_COLS; i++) { - this.chars.push(new StyledUnicodeChar()); - } - this.logger = logger; - } - equals(other) { - for (let i = 0; i < NR_COLS; i++) { - if (!this.chars[i].equals(other.chars[i])) { - return false; - } - } - return true; - } - copy(other) { - for (let i = 0; i < NR_COLS; i++) { - this.chars[i].copy(other.chars[i]); - } - } - isEmpty() { - let empty = true; - for (let i = 0; i < NR_COLS; i++) { - if (!this.chars[i].isEmpty()) { - empty = false; - break; - } - } - return empty; - } - - /** - * Set the cursor to a valid column. - */ - setCursor(absPos) { - if (this.pos !== absPos) { - this.pos = absPos; - } - if (this.pos < 0) { - this.logger.log(3, 'Negative cursor position ' + this.pos); - this.pos = 0; - } else if (this.pos > NR_COLS) { - this.logger.log(3, 'Too large cursor position ' + this.pos); - this.pos = NR_COLS; - } - } - - /** - * Move the cursor relative to current position. - */ - moveCursor(relPos) { - const newPos = this.pos + relPos; - if (relPos > 1) { - for (let i = this.pos + 1; i < newPos + 1; i++) { - this.chars[i].setPenState(this.currPenState); - } - } - this.setCursor(newPos); - } - - /** - * Backspace, move one step back and clear character. - */ - backSpace() { - this.moveCursor(-1); - this.chars[this.pos].setChar(' ', this.currPenState); - } - insertChar(byte) { - if (byte >= 0x90) { - // Extended char - this.backSpace(); - } - const char = getCharForByte(byte); - if (this.pos >= NR_COLS) { - this.logger.log(0, () => 'Cannot insert ' + byte.toString(16) + ' (' + char + ') at position ' + this.pos + '. Skipping it!'); - return; - } - this.chars[this.pos].setChar(char, this.currPenState); - this.moveCursor(1); - } - clearFromPos(startPos) { - let i; - for (i = startPos; i < NR_COLS; i++) { - this.chars[i].reset(); - } - } - clear() { - this.clearFromPos(0); - this.pos = 0; - this.currPenState.reset(); - } - clearToEndOfRow() { - this.clearFromPos(this.pos); - } - getTextString() { - const chars = []; - let empty = true; - for (let i = 0; i < NR_COLS; i++) { - const char = this.chars[i].uchar; - if (char !== ' ') { - empty = false; - } - chars.push(char); - } - if (empty) { - return ''; - } else { - return chars.join(''); - } - } - setPenStyles(styles) { - this.currPenState.setStyles(styles); - const currChar = this.chars[this.pos]; - currChar.setPenState(this.currPenState); - } -} - -/** - * Keep a CEA-608 screen of 32x15 styled characters - * @constructor - */ -class CaptionScreen { - constructor(logger) { - this.rows = []; - this.currRow = NR_ROWS - 1; - this.nrRollUpRows = null; - this.lastOutputScreen = null; - this.logger = void 0; - for (let i = 0; i < NR_ROWS; i++) { - this.rows.push(new Row(logger)); - } - this.logger = logger; - } - reset() { - for (let i = 0; i < NR_ROWS; i++) { - this.rows[i].clear(); - } - this.currRow = NR_ROWS - 1; - } - equals(other) { - let equal = true; - for (let i = 0; i < NR_ROWS; i++) { - if (!this.rows[i].equals(other.rows[i])) { - equal = false; - break; - } - } - return equal; - } - copy(other) { - for (let i = 0; i < NR_ROWS; i++) { - this.rows[i].copy(other.rows[i]); - } - } - isEmpty() { - let empty = true; - for (let i = 0; i < NR_ROWS; i++) { - if (!this.rows[i].isEmpty()) { - empty = false; - break; - } - } - return empty; - } - backSpace() { - const row = this.rows[this.currRow]; - row.backSpace(); - } - clearToEndOfRow() { - const row = this.rows[this.currRow]; - row.clearToEndOfRow(); - } - - /** - * Insert a character (without styling) in the current row. - */ - insertChar(char) { - const row = this.rows[this.currRow]; - row.insertChar(char); - } - setPen(styles) { - const row = this.rows[this.currRow]; - row.setPenStyles(styles); - } - moveCursor(relPos) { - const row = this.rows[this.currRow]; - row.moveCursor(relPos); - } - setCursor(absPos) { - this.logger.log(2, 'setCursor: ' + absPos); - const row = this.rows[this.currRow]; - row.setCursor(absPos); - } - setPAC(pacData) { - this.logger.log(2, () => 'pacData = ' + JSON.stringify(pacData)); - let newRow = pacData.row - 1; - if (this.nrRollUpRows && newRow < this.nrRollUpRows - 1) { - newRow = this.nrRollUpRows - 1; - } - - // Make sure this only affects Roll-up Captions by checking this.nrRollUpRows - if (this.nrRollUpRows && this.currRow !== newRow) { - // clear all rows first - for (let i = 0; i < NR_ROWS; i++) { - this.rows[i].clear(); - } - - // Copy this.nrRollUpRows rows from lastOutputScreen and place it in the newRow location - // topRowIndex - the start of rows to copy (inclusive index) - const topRowIndex = this.currRow + 1 - this.nrRollUpRows; - // We only copy if the last position was already shown. - // We use the cueStartTime value to check this. - const lastOutputScreen = this.lastOutputScreen; - if (lastOutputScreen) { - const prevLineTime = lastOutputScreen.rows[topRowIndex].cueStartTime; - const time = this.logger.time; - if (prevLineTime !== null && time !== null && prevLineTime < time) { - for (let i = 0; i < this.nrRollUpRows; i++) { - this.rows[newRow - this.nrRollUpRows + i + 1].copy(lastOutputScreen.rows[topRowIndex + i]); - } - } - } - } - this.currRow = newRow; - const row = this.rows[this.currRow]; - if (pacData.indent !== null) { - const indent = pacData.indent; - const prevPos = Math.max(indent - 1, 0); - row.setCursor(pacData.indent); - pacData.color = row.chars[prevPos].penState.foreground; - } - const styles = { - foreground: pacData.color, - underline: pacData.underline, - italics: pacData.italics, - background: 'black', - flash: false - }; - this.setPen(styles); - } - - /** - * Set background/extra foreground, but first do back_space, and then insert space (backwards compatibility). - */ - setBkgData(bkgData) { - this.logger.log(2, () => 'bkgData = ' + JSON.stringify(bkgData)); - this.backSpace(); - this.setPen(bkgData); - this.insertChar(0x20); // Space - } - setRollUpRows(nrRows) { - this.nrRollUpRows = nrRows; - } - rollUp() { - if (this.nrRollUpRows === null) { - this.logger.log(3, 'roll_up but nrRollUpRows not set yet'); - return; // Not properly setup - } - this.logger.log(1, () => this.getDisplayText()); - const topRowIndex = this.currRow + 1 - this.nrRollUpRows; - const topRow = this.rows.splice(topRowIndex, 1)[0]; - topRow.clear(); - this.rows.splice(this.currRow, 0, topRow); - this.logger.log(2, 'Rolling up'); - // this.logger.log(VerboseLevel.TEXT, this.get_display_text()) - } - - /** - * Get all non-empty rows with as unicode text. - */ - getDisplayText(asOneRow) { - asOneRow = asOneRow || false; - const displayText = []; - let text = ''; - let rowNr = -1; - for (let i = 0; i < NR_ROWS; i++) { - const rowText = this.rows[i].getTextString(); - if (rowText) { - rowNr = i + 1; - if (asOneRow) { - displayText.push('Row ' + rowNr + ": '" + rowText + "'"); - } else { - displayText.push(rowText.trim()); - } - } - } - if (displayText.length > 0) { - if (asOneRow) { - text = '[' + displayText.join(' | ') + ']'; - } else { - text = displayText.join('\n'); - } - } - return text; - } - getTextAndFormat() { - return this.rows; - } -} - -// var modes = ['MODE_ROLL-UP', 'MODE_POP-ON', 'MODE_PAINT-ON', 'MODE_TEXT']; - -class Cea608Channel { - constructor(channelNumber, outputFilter, logger) { - this.chNr = void 0; - this.outputFilter = void 0; - this.mode = void 0; - this.verbose = void 0; - this.displayedMemory = void 0; - this.nonDisplayedMemory = void 0; - this.lastOutputScreen = void 0; - this.currRollUpRow = void 0; - this.writeScreen = void 0; - this.cueStartTime = void 0; - this.logger = void 0; - this.chNr = channelNumber; - this.outputFilter = outputFilter; - this.mode = null; - this.verbose = 0; - this.displayedMemory = new CaptionScreen(logger); - this.nonDisplayedMemory = new CaptionScreen(logger); - this.lastOutputScreen = new CaptionScreen(logger); - this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1]; - this.writeScreen = this.displayedMemory; - this.mode = null; - this.cueStartTime = null; // Keeps track of where a cue started. - this.logger = logger; - } - reset() { - this.mode = null; - this.displayedMemory.reset(); - this.nonDisplayedMemory.reset(); - this.lastOutputScreen.reset(); - this.outputFilter.reset(); - this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1]; - this.writeScreen = this.displayedMemory; - this.mode = null; - this.cueStartTime = null; - } - getHandler() { - return this.outputFilter; - } - setHandler(newHandler) { - this.outputFilter = newHandler; - } - setPAC(pacData) { - this.writeScreen.setPAC(pacData); - } - setBkgData(bkgData) { - this.writeScreen.setBkgData(bkgData); - } - setMode(newMode) { - if (newMode === this.mode) { - return; - } - this.mode = newMode; - this.logger.log(2, () => 'MODE=' + newMode); - if (this.mode === 'MODE_POP-ON') { - this.writeScreen = this.nonDisplayedMemory; - } else { - this.writeScreen = this.displayedMemory; - this.writeScreen.reset(); - } - if (this.mode !== 'MODE_ROLL-UP') { - this.displayedMemory.nrRollUpRows = null; - this.nonDisplayedMemory.nrRollUpRows = null; - } - this.mode = newMode; - } - insertChars(chars) { - for (let i = 0; i < chars.length; i++) { - this.writeScreen.insertChar(chars[i]); - } - const screen = this.writeScreen === this.displayedMemory ? 'DISP' : 'NON_DISP'; - this.logger.log(2, () => screen + ': ' + this.writeScreen.getDisplayText(true)); - if (this.mode === 'MODE_PAINT-ON' || this.mode === 'MODE_ROLL-UP') { - this.logger.log(1, () => 'DISPLAYED: ' + this.displayedMemory.getDisplayText(true)); - this.outputDataUpdate(); - } - } - ccRCL() { - // Resume Caption Loading (switch mode to Pop On) - this.logger.log(2, 'RCL - Resume Caption Loading'); - this.setMode('MODE_POP-ON'); - } - ccBS() { - // BackSpace - this.logger.log(2, 'BS - BackSpace'); - if (this.mode === 'MODE_TEXT') { - return; - } - this.writeScreen.backSpace(); - if (this.writeScreen === this.displayedMemory) { - this.outputDataUpdate(); - } - } - ccAOF() { - // Reserved (formerly Alarm Off) - } - ccAON() { - // Reserved (formerly Alarm On) - } - ccDER() { - // Delete to End of Row - this.logger.log(2, 'DER- Delete to End of Row'); - this.writeScreen.clearToEndOfRow(); - this.outputDataUpdate(); - } - ccRU(nrRows) { - // Roll-Up Captions-2,3,or 4 Rows - this.logger.log(2, 'RU(' + nrRows + ') - Roll Up'); - this.writeScreen = this.displayedMemory; - this.setMode('MODE_ROLL-UP'); - this.writeScreen.setRollUpRows(nrRows); - } - ccFON() { - // Flash On - this.logger.log(2, 'FON - Flash On'); - this.writeScreen.setPen({ - flash: true - }); - } - ccRDC() { - // Resume Direct Captioning (switch mode to PaintOn) - this.logger.log(2, 'RDC - Resume Direct Captioning'); - this.setMode('MODE_PAINT-ON'); - } - ccTR() { - // Text Restart in text mode (not supported, however) - this.logger.log(2, 'TR'); - this.setMode('MODE_TEXT'); - } - ccRTD() { - // Resume Text Display in Text mode (not supported, however) - this.logger.log(2, 'RTD'); - this.setMode('MODE_TEXT'); - } - ccEDM() { - // Erase Displayed Memory - this.logger.log(2, 'EDM - Erase Displayed Memory'); - this.displayedMemory.reset(); - this.outputDataUpdate(true); - } - ccCR() { - // Carriage Return - this.logger.log(2, 'CR - Carriage Return'); - this.writeScreen.rollUp(); - this.outputDataUpdate(true); - } - ccENM() { - // Erase Non-Displayed Memory - this.logger.log(2, 'ENM - Erase Non-displayed Memory'); - this.nonDisplayedMemory.reset(); - } - ccEOC() { - // End of Caption (Flip Memories) - this.logger.log(2, 'EOC - End Of Caption'); - if (this.mode === 'MODE_POP-ON') { - const tmp = this.displayedMemory; - this.displayedMemory = this.nonDisplayedMemory; - this.nonDisplayedMemory = tmp; - this.writeScreen = this.nonDisplayedMemory; - this.logger.log(1, () => 'DISP: ' + this.displayedMemory.getDisplayText()); - } - this.outputDataUpdate(true); - } - ccTO(nrCols) { - // Tab Offset 1,2, or 3 columns - this.logger.log(2, 'TO(' + nrCols + ') - Tab Offset'); - this.writeScreen.moveCursor(nrCols); - } - ccMIDROW(secondByte) { - // Parse MIDROW command - const styles = { - flash: false - }; - styles.underline = secondByte % 2 === 1; - styles.italics = secondByte >= 0x2e; - if (!styles.italics) { - const colorIndex = Math.floor(secondByte / 2) - 0x10; - const colors = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta']; - styles.foreground = colors[colorIndex]; - } else { - styles.foreground = 'white'; - } - this.logger.log(2, 'MIDROW: ' + JSON.stringify(styles)); - this.writeScreen.setPen(styles); - } - outputDataUpdate(dispatch = false) { - const time = this.logger.time; - if (time === null) { - return; - } - if (this.outputFilter) { - if (this.cueStartTime === null && !this.displayedMemory.isEmpty()) { - // Start of a new cue - this.cueStartTime = time; - } else { - if (!this.displayedMemory.equals(this.lastOutputScreen)) { - this.outputFilter.newCue(this.cueStartTime, time, this.lastOutputScreen); - if (dispatch && this.outputFilter.dispatchCue) { - this.outputFilter.dispatchCue(); - } - this.cueStartTime = this.displayedMemory.isEmpty() ? null : time; - } - } - this.lastOutputScreen.copy(this.displayedMemory); - } - } - cueSplitAtTime(t) { - if (this.outputFilter) { - if (!this.displayedMemory.isEmpty()) { - if (this.outputFilter.newCue) { - this.outputFilter.newCue(this.cueStartTime, t, this.displayedMemory); - } - this.cueStartTime = t; - } - } - } -} - -// Will be 1 or 2 when parsing captions - -class Cea608Parser { - constructor(field, out1, out2) { - this.channels = void 0; - this.currentChannel = 0; - this.cmdHistory = createCmdHistory(); - this.logger = void 0; - const logger = this.logger = new CaptionsLogger(); - this.channels = [null, new Cea608Channel(field, out1, logger), new Cea608Channel(field + 1, out2, logger)]; - } - getHandler(channel) { - return this.channels[channel].getHandler(); - } - setHandler(channel, newHandler) { - this.channels[channel].setHandler(newHandler); - } - - /** - * Add data for time t in forms of list of bytes (unsigned ints). The bytes are treated as pairs. - */ - addData(time, byteList) { - this.logger.time = time; - for (let i = 0; i < byteList.length; i += 2) { - const a = byteList[i] & 0x7f; - const b = byteList[i + 1] & 0x7f; - let cmdFound = false; - let charsFound = null; - if (a === 0 && b === 0) { - continue; - } else { - this.logger.log(3, () => '[' + numArrayToHexArray([byteList[i], byteList[i + 1]]) + '] -> (' + numArrayToHexArray([a, b]) + ')'); - } - const cmdHistory = this.cmdHistory; - const isControlCode = a >= 0x10 && a <= 0x1f; - if (isControlCode) { - // Skip redundant control codes - if (hasCmdRepeated(a, b, cmdHistory)) { - setLastCmd(null, null, cmdHistory); - this.logger.log(3, () => 'Repeated command (' + numArrayToHexArray([a, b]) + ') is dropped'); - continue; - } - setLastCmd(a, b, this.cmdHistory); - cmdFound = this.parseCmd(a, b); - if (!cmdFound) { - cmdFound = this.parseMidrow(a, b); - } - if (!cmdFound) { - cmdFound = this.parsePAC(a, b); - } - if (!cmdFound) { - cmdFound = this.parseBackgroundAttributes(a, b); - } - } else { - setLastCmd(null, null, cmdHistory); - } - if (!cmdFound) { - charsFound = this.parseChars(a, b); - if (charsFound) { - const currChNr = this.currentChannel; - if (currChNr && currChNr > 0) { - const channel = this.channels[currChNr]; - channel.insertChars(charsFound); - } else { - this.logger.log(2, 'No channel found yet. TEXT-MODE?'); - } - } - } - if (!cmdFound && !charsFound) { - this.logger.log(2, () => "Couldn't parse cleaned data " + numArrayToHexArray([a, b]) + ' orig: ' + numArrayToHexArray([byteList[i], byteList[i + 1]])); - } - } - } - - /** - * Parse Command. - * @returns True if a command was found - */ - parseCmd(a, b) { - const cond1 = (a === 0x14 || a === 0x1c || a === 0x15 || a === 0x1d) && b >= 0x20 && b <= 0x2f; - const cond2 = (a === 0x17 || a === 0x1f) && b >= 0x21 && b <= 0x23; - if (!(cond1 || cond2)) { - return false; - } - const chNr = a === 0x14 || a === 0x15 || a === 0x17 ? 1 : 2; - const channel = this.channels[chNr]; - if (a === 0x14 || a === 0x15 || a === 0x1c || a === 0x1d) { - if (b === 0x20) { - channel.ccRCL(); - } else if (b === 0x21) { - channel.ccBS(); - } else if (b === 0x22) { - channel.ccAOF(); - } else if (b === 0x23) { - channel.ccAON(); - } else if (b === 0x24) { - channel.ccDER(); - } else if (b === 0x25) { - channel.ccRU(2); - } else if (b === 0x26) { - channel.ccRU(3); - } else if (b === 0x27) { - channel.ccRU(4); - } else if (b === 0x28) { - channel.ccFON(); - } else if (b === 0x29) { - channel.ccRDC(); - } else if (b === 0x2a) { - channel.ccTR(); - } else if (b === 0x2b) { - channel.ccRTD(); - } else if (b === 0x2c) { - channel.ccEDM(); - } else if (b === 0x2d) { - channel.ccCR(); - } else if (b === 0x2e) { - channel.ccENM(); - } else if (b === 0x2f) { - channel.ccEOC(); - } - } else { - // a == 0x17 || a == 0x1F - channel.ccTO(b - 0x20); - } - this.currentChannel = chNr; - return true; - } - - /** - * Parse midrow styling command - */ - parseMidrow(a, b) { - let chNr = 0; - if ((a === 0x11 || a === 0x19) && b >= 0x20 && b <= 0x2f) { - if (a === 0x11) { - chNr = 1; - } else { - chNr = 2; - } - if (chNr !== this.currentChannel) { - this.logger.log(0, 'Mismatch channel in midrow parsing'); - return false; - } - const channel = this.channels[chNr]; - if (!channel) { - return false; - } - channel.ccMIDROW(b); - this.logger.log(3, () => 'MIDROW (' + numArrayToHexArray([a, b]) + ')'); - return true; - } - return false; - } - - /** - * Parse Preable Access Codes (Table 53). - * @returns {Boolean} Tells if PAC found - */ - parsePAC(a, b) { - let row; - const case1 = (a >= 0x11 && a <= 0x17 || a >= 0x19 && a <= 0x1f) && b >= 0x40 && b <= 0x7f; - const case2 = (a === 0x10 || a === 0x18) && b >= 0x40 && b <= 0x5f; - if (!(case1 || case2)) { - return false; - } - const chNr = a <= 0x17 ? 1 : 2; - if (b >= 0x40 && b <= 0x5f) { - row = chNr === 1 ? rowsLowCh1[a] : rowsLowCh2[a]; - } else { - // 0x60 <= b <= 0x7F - row = chNr === 1 ? rowsHighCh1[a] : rowsHighCh2[a]; - } - const channel = this.channels[chNr]; - if (!channel) { - return false; - } - channel.setPAC(this.interpretPAC(row, b)); - this.currentChannel = chNr; - return true; - } - - /** - * Interpret the second byte of the pac, and return the information. - * @returns pacData with style parameters - */ - interpretPAC(row, byte) { - let pacIndex; - const pacData = { - color: null, - italics: false, - indent: null, - underline: false, - row: row - }; - if (byte > 0x5f) { - pacIndex = byte - 0x60; - } else { - pacIndex = byte - 0x40; - } - pacData.underline = (pacIndex & 1) === 1; - if (pacIndex <= 0xd) { - pacData.color = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta', 'white'][Math.floor(pacIndex / 2)]; - } else if (pacIndex <= 0xf) { - pacData.italics = true; - pacData.color = 'white'; - } else { - pacData.indent = Math.floor((pacIndex - 0x10) / 2) * 4; - } - return pacData; // Note that row has zero offset. The spec uses 1. - } - - /** - * Parse characters. - * @returns An array with 1 to 2 codes corresponding to chars, if found. null otherwise. - */ - parseChars(a, b) { - let channelNr; - let charCodes = null; - let charCode1 = null; - if (a >= 0x19) { - channelNr = 2; - charCode1 = a - 8; - } else { - channelNr = 1; - charCode1 = a; - } - if (charCode1 >= 0x11 && charCode1 <= 0x13) { - // Special character - let oneCode; - if (charCode1 === 0x11) { - oneCode = b + 0x50; - } else if (charCode1 === 0x12) { - oneCode = b + 0x70; - } else { - oneCode = b + 0x90; - } - this.logger.log(2, () => "Special char '" + getCharForByte(oneCode) + "' in channel " + channelNr); - charCodes = [oneCode]; - } else if (a >= 0x20 && a <= 0x7f) { - charCodes = b === 0 ? [a] : [a, b]; - } - if (charCodes) { - this.logger.log(3, () => 'Char codes = ' + numArrayToHexArray(charCodes).join(',')); - } - return charCodes; - } - - /** - * Parse extended background attributes as well as new foreground color black. - * @returns True if background attributes are found - */ - parseBackgroundAttributes(a, b) { - const case1 = (a === 0x10 || a === 0x18) && b >= 0x20 && b <= 0x2f; - const case2 = (a === 0x17 || a === 0x1f) && b >= 0x2d && b <= 0x2f; - if (!(case1 || case2)) { - return false; - } - let index; - const bkgData = {}; - if (a === 0x10 || a === 0x18) { - index = Math.floor((b - 0x20) / 2); - bkgData.background = backgroundColors[index]; - if (b % 2 === 1) { - bkgData.background = bkgData.background + '_semi'; - } - } else if (b === 0x2d) { - bkgData.background = 'transparent'; - } else { - bkgData.foreground = 'black'; - if (b === 0x2f) { - bkgData.underline = true; - } - } - const chNr = a <= 0x17 ? 1 : 2; - const channel = this.channels[chNr]; - channel.setBkgData(bkgData); - return true; - } - - /** - * Reset state of parser and its channels. - */ - reset() { - for (let i = 0; i < Object.keys(this.channels).length; i++) { - const channel = this.channels[i]; - if (channel) { - channel.reset(); - } - } - setLastCmd(null, null, this.cmdHistory); - } - - /** - * Trigger the generation of a cue, and the start of a new one if displayScreens are not empty. - */ - cueSplitAtTime(t) { - for (let i = 0; i < this.channels.length; i++) { - const channel = this.channels[i]; - if (channel) { - channel.cueSplitAtTime(t); - } - } - } -} -function setLastCmd(a, b, cmdHistory) { - cmdHistory.a = a; - cmdHistory.b = b; -} -function hasCmdRepeated(a, b, cmdHistory) { - return cmdHistory.a === a && cmdHistory.b === b; -} -function createCmdHistory() { - return { - a: null, - b: null - }; -} - -class OutputFilter { - constructor(timelineController, trackName) { - this.timelineController = void 0; - this.cueRanges = []; - this.trackName = void 0; - this.startTime = null; - this.endTime = null; - this.screen = null; - this.timelineController = timelineController; - this.trackName = trackName; - } - dispatchCue() { - if (this.startTime === null) { - return; - } - this.timelineController.addCues(this.trackName, this.startTime, this.endTime, this.screen, this.cueRanges); - this.startTime = null; - } - newCue(startTime, endTime, screen) { - if (this.startTime === null || this.startTime > startTime) { - this.startTime = startTime; - } - this.endTime = endTime; - this.screen = screen; - this.timelineController.createCaptionsTrack(this.trackName); - } - reset() { - this.cueRanges = []; - this.startTime = null; - } -} - -/** - * Copyright 2013 vtt.js Contributors - * - * Licensed under the Apache License, Version 2.0 (the 'License'); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an 'AS IS' BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var VTTCue = (function () { - if (optionalSelf != null && optionalSelf.VTTCue) { - return self.VTTCue; - } - const AllowedDirections = ['', 'lr', 'rl']; - const AllowedAlignments = ['start', 'middle', 'end', 'left', 'right']; - function isAllowedValue(allowed, value) { - if (typeof value !== 'string') { - return false; - } - // necessary for assuring the generic conforms to the Array interface - if (!Array.isArray(allowed)) { - return false; - } - // reset the type so that the next narrowing works well - const lcValue = value.toLowerCase(); - // use the allow list to narrow the type to a specific subset of strings - if (~allowed.indexOf(lcValue)) { - return lcValue; - } - return false; - } - function findDirectionSetting(value) { - return isAllowedValue(AllowedDirections, value); - } - function findAlignSetting(value) { - return isAllowedValue(AllowedAlignments, value); - } - function extend(obj, ...rest) { - let i = 1; - for (; i < arguments.length; i++) { - const cobj = arguments[i]; - for (const p in cobj) { - obj[p] = cobj[p]; - } - } - return obj; - } - function VTTCue(startTime, endTime, text) { - const cue = this; - const baseObj = { - enumerable: true - }; - /** - * Shim implementation specific properties. These properties are not in - * the spec. - */ - - // Lets us know when the VTTCue's data has changed in such a way that we need - // to recompute its display state. This lets us compute its display state - // lazily. - cue.hasBeenReset = false; - - /** - * VTTCue and TextTrackCue properties - * http://dev.w3.org/html5/webvtt/#vttcue-interface - */ - - let _id = ''; - let _pauseOnExit = false; - let _startTime = startTime; - let _endTime = endTime; - let _text = text; - let _region = null; - let _vertical = ''; - let _snapToLines = true; - let _line = 'auto'; - let _lineAlign = 'start'; - let _position = 50; - let _positionAlign = 'middle'; - let _size = 50; - let _align = 'middle'; - Object.defineProperty(cue, 'id', extend({}, baseObj, { - get: function () { - return _id; - }, - set: function (value) { - _id = '' + value; - } - })); - Object.defineProperty(cue, 'pauseOnExit', extend({}, baseObj, { - get: function () { - return _pauseOnExit; - }, - set: function (value) { - _pauseOnExit = !!value; - } - })); - Object.defineProperty(cue, 'startTime', extend({}, baseObj, { - get: function () { - return _startTime; - }, - set: function (value) { - if (typeof value !== 'number') { - throw new TypeError('Start time must be set to a number.'); - } - _startTime = value; - this.hasBeenReset = true; - } - })); - Object.defineProperty(cue, 'endTime', extend({}, baseObj, { - get: function () { - return _endTime; - }, - set: function (value) { - if (typeof value !== 'number') { - throw new TypeError('End time must be set to a number.'); - } - _endTime = value; - this.hasBeenReset = true; - } - })); - Object.defineProperty(cue, 'text', extend({}, baseObj, { - get: function () { - return _text; - }, - set: function (value) { - _text = '' + value; - this.hasBeenReset = true; - } - })); - - // todo: implement VTTRegion polyfill? - Object.defineProperty(cue, 'region', extend({}, baseObj, { - get: function () { - return _region; - }, - set: function (value) { - _region = value; - this.hasBeenReset = true; - } - })); - Object.defineProperty(cue, 'vertical', extend({}, baseObj, { - get: function () { - return _vertical; - }, - set: function (value) { - const setting = findDirectionSetting(value); - // Have to check for false because the setting an be an empty string. - if (setting === false) { - throw new SyntaxError('An invalid or illegal string was specified.'); - } - _vertical = setting; - this.hasBeenReset = true; - } - })); - Object.defineProperty(cue, 'snapToLines', extend({}, baseObj, { - get: function () { - return _snapToLines; - }, - set: function (value) { - _snapToLines = !!value; - this.hasBeenReset = true; - } - })); - Object.defineProperty(cue, 'line', extend({}, baseObj, { - get: function () { - return _line; - }, - set: function (value) { - if (typeof value !== 'number' && value !== 'auto') { - throw new SyntaxError('An invalid number or illegal string was specified.'); - } - _line = value; - this.hasBeenReset = true; - } - })); - Object.defineProperty(cue, 'lineAlign', extend({}, baseObj, { - get: function () { - return _lineAlign; - }, - set: function (value) { - const setting = findAlignSetting(value); - if (!setting) { - throw new SyntaxError('An invalid or illegal string was specified.'); - } - _lineAlign = setting; - this.hasBeenReset = true; - } - })); - Object.defineProperty(cue, 'position', extend({}, baseObj, { - get: function () { - return _position; - }, - set: function (value) { - if (value < 0 || value > 100) { - throw new Error('Position must be between 0 and 100.'); - } - _position = value; - this.hasBeenReset = true; - } - })); - Object.defineProperty(cue, 'positionAlign', extend({}, baseObj, { - get: function () { - return _positionAlign; - }, - set: function (value) { - const setting = findAlignSetting(value); - if (!setting) { - throw new SyntaxError('An invalid or illegal string was specified.'); - } - _positionAlign = setting; - this.hasBeenReset = true; - } - })); - Object.defineProperty(cue, 'size', extend({}, baseObj, { - get: function () { - return _size; - }, - set: function (value) { - if (value < 0 || value > 100) { - throw new Error('Size must be between 0 and 100.'); - } - _size = value; - this.hasBeenReset = true; - } - })); - Object.defineProperty(cue, 'align', extend({}, baseObj, { - get: function () { - return _align; - }, - set: function (value) { - const setting = findAlignSetting(value); - if (!setting) { - throw new SyntaxError('An invalid or illegal string was specified.'); - } - _align = setting; - this.hasBeenReset = true; - } - })); - - /** - * Other <track> spec defined properties - */ - - // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state - cue.displayState = undefined; - } - - /** - * VTTCue methods - */ - - VTTCue.prototype.getCueAsHTML = function () { - // Assume WebVTT.convertCueToDOMTree is on the global. - const WebVTT = self.WebVTT; - return WebVTT.convertCueToDOMTree(self, this.text); - }; - // this is a polyfill hack - return VTTCue; -})(); - -/* - * Source: https://github.com/mozilla/vtt.js/blob/master/dist/vtt.js - */ - -class StringDecoder { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - decode(data, options) { - if (!data) { - return ''; - } - if (typeof data !== 'string') { - throw new Error('Error - expected string data.'); - } - return decodeURIComponent(encodeURIComponent(data)); - } -} - -// Try to parse input as a time stamp. -function parseTimeStamp(input) { - function computeSeconds(h, m, s, f) { - return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + parseFloat(f || 0); - } - const m = input.match(/^(?:(\d+):)?(\d{2}):(\d{2})(\.\d+)?/); - if (!m) { - return null; - } - if (parseFloat(m[2]) > 59) { - // Timestamp takes the form of [hours]:[minutes].[milliseconds] - // First position is hours as it's over 59. - return computeSeconds(m[2], m[3], 0, m[4]); - } - // Timestamp takes the form of [hours (optional)]:[minutes]:[seconds].[milliseconds] - return computeSeconds(m[1], m[2], m[3], m[4]); -} - -// A settings object holds key/value pairs and will ignore anything but the first -// assignment to a specific key. -class Settings { - constructor() { - this.values = Object.create(null); - } - // Only accept the first assignment to any key. - set(k, v) { - if (!this.get(k) && v !== '') { - this.values[k] = v; - } - } - // Return the value for a key, or a default value. - // If 'defaultKey' is passed then 'dflt' is assumed to be an object with - // a number of possible default values as properties where 'defaultKey' is - // the key of the property that will be chosen; otherwise it's assumed to be - // a single value. - get(k, dflt, defaultKey) { - if (defaultKey) { - return this.has(k) ? this.values[k] : dflt[defaultKey]; - } - return this.has(k) ? this.values[k] : dflt; - } - // Check whether we have a value for a key. - has(k) { - return k in this.values; - } - // Accept a setting if its one of the given alternatives. - alt(k, v, a) { - for (let n = 0; n < a.length; ++n) { - if (v === a[n]) { - this.set(k, v); - break; - } - } - } - // Accept a setting if its a valid (signed) integer. - integer(k, v) { - if (/^-?\d+$/.test(v)) { - // integer - this.set(k, parseInt(v, 10)); - } - } - // Accept a setting if its a valid percentage. - percent(k, v) { - if (/^([\d]{1,3})(\.[\d]*)?%$/.test(v)) { - const percent = parseFloat(v); - if (percent >= 0 && percent <= 100) { - this.set(k, percent); - return true; - } - } - return false; - } -} - -// Helper function to parse input into groups separated by 'groupDelim', and -// interpret each group as a key/value pair separated by 'keyValueDelim'. -function parseOptions(input, callback, keyValueDelim, groupDelim) { - const groups = groupDelim ? input.split(groupDelim) : [input]; - for (const i in groups) { - if (typeof groups[i] !== 'string') { - continue; - } - const kv = groups[i].split(keyValueDelim); - if (kv.length !== 2) { - continue; - } - const k = kv[0]; - const v = kv[1]; - callback(k, v); - } -} -const defaults = new VTTCue(0, 0, ''); -// 'middle' was changed to 'center' in the spec: https://github.com/w3c/webvtt/pull/244 -// Safari doesn't yet support this change, but FF and Chrome do. -const center = defaults.align === 'middle' ? 'middle' : 'center'; -function parseCue(input, cue, regionList) { - // Remember the original input if we need to throw an error. - const oInput = input; - // 4.1 WebVTT timestamp - function consumeTimeStamp() { - const ts = parseTimeStamp(input); - if (ts === null) { - throw new Error('Malformed timestamp: ' + oInput); - } - - // Remove time stamp from input. - input = input.replace(/^[^\sa-zA-Z-]+/, ''); - return ts; - } - - // 4.4.2 WebVTT cue settings - function consumeCueSettings(input, cue) { - const settings = new Settings(); - parseOptions(input, function (k, v) { - let vals; - switch (k) { - case 'region': - // Find the last region we parsed with the same region id. - for (let i = regionList.length - 1; i >= 0; i--) { - if (regionList[i].id === v) { - settings.set(k, regionList[i].region); - break; - } - } - break; - case 'vertical': - settings.alt(k, v, ['rl', 'lr']); - break; - case 'line': - vals = v.split(','); - settings.integer(k, vals[0]); - if (settings.percent(k, vals[0])) { - settings.set('snapToLines', false); - } - settings.alt(k, vals[0], ['auto']); - if (vals.length === 2) { - settings.alt('lineAlign', vals[1], ['start', center, 'end']); - } - break; - case 'position': - vals = v.split(','); - settings.percent(k, vals[0]); - if (vals.length === 2) { - settings.alt('positionAlign', vals[1], ['start', center, 'end', 'line-left', 'line-right', 'auto']); - } - break; - case 'size': - settings.percent(k, v); - break; - case 'align': - settings.alt(k, v, ['start', center, 'end', 'left', 'right']); - break; - } - }, /:/, /\s/); - - // Apply default values for any missing fields. - cue.region = settings.get('region', null); - cue.vertical = settings.get('vertical', ''); - let line = settings.get('line', 'auto'); - if (line === 'auto' && defaults.line === -1) { - // set numeric line number for Safari - line = -1; - } - cue.line = line; - cue.lineAlign = settings.get('lineAlign', 'start'); - cue.snapToLines = settings.get('snapToLines', true); - cue.size = settings.get('size', 100); - cue.align = settings.get('align', center); - let position = settings.get('position', 'auto'); - if (position === 'auto' && defaults.position === 50) { - // set numeric position for Safari - position = cue.align === 'start' || cue.align === 'left' ? 0 : cue.align === 'end' || cue.align === 'right' ? 100 : 50; - } - cue.position = position; - } - function skipWhitespace() { - input = input.replace(/^\s+/, ''); - } - - // 4.1 WebVTT cue timings. - skipWhitespace(); - cue.startTime = consumeTimeStamp(); // (1) collect cue start time - skipWhitespace(); - if (input.slice(0, 3) !== '-->') { - // (3) next characters must match '-->' - throw new Error("Malformed time stamp (time stamps must be separated by '-->'): " + oInput); - } - input = input.slice(3); - skipWhitespace(); - cue.endTime = consumeTimeStamp(); // (5) collect cue end time - - // 4.1 WebVTT cue settings list. - skipWhitespace(); - consumeCueSettings(input, cue); -} -function fixLineBreaks(input) { - return input.replace(/<br(?: \/)?>/gi, '\n'); -} -class VTTParser { - constructor() { - this.state = 'INITIAL'; - this.buffer = ''; - this.decoder = new StringDecoder(); - this.regionList = []; - this.cue = null; - this.oncue = void 0; - this.onparsingerror = void 0; - this.onflush = void 0; - } - parse(data) { - const _this = this; - - // If there is no data then we won't decode it, but will just try to parse - // whatever is in buffer already. This may occur in circumstances, for - // example when flush() is called. - if (data) { - // Try to decode the data that we received. - _this.buffer += _this.decoder.decode(data, { - stream: true - }); - } - function collectNextLine() { - let buffer = _this.buffer; - let pos = 0; - buffer = fixLineBreaks(buffer); - while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') { - ++pos; - } - const line = buffer.slice(0, pos); - // Advance the buffer early in case we fail below. - if (buffer[pos] === '\r') { - ++pos; - } - if (buffer[pos] === '\n') { - ++pos; - } - _this.buffer = buffer.slice(pos); - return line; - } - - // 3.2 WebVTT metadata header syntax - function parseHeader(input) { - parseOptions(input, function (k, v) { - // switch (k) { - // case 'region': - // 3.3 WebVTT region metadata header syntax - // console.log('parse region', v); - // parseRegion(v); - // break; - // } - }, /:/); - } - - // 5.1 WebVTT file parsing. - try { - let line = ''; - if (_this.state === 'INITIAL') { - // We can't start parsing until we have the first line. - if (!/\r\n|\n/.test(_this.buffer)) { - return this; - } - line = collectNextLine(); - // strip of UTF-8 BOM if any - // https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8 - const m = line.match(/^()?WEBVTT([ \t].*)?$/); - if (!(m != null && m[0])) { - throw new Error('Malformed WebVTT signature.'); - } - _this.state = 'HEADER'; - } - let alreadyCollectedLine = false; - while (_this.buffer) { - // We can't parse a line until we have the full line. - if (!/\r\n|\n/.test(_this.buffer)) { - return this; - } - if (!alreadyCollectedLine) { - line = collectNextLine(); - } else { - alreadyCollectedLine = false; - } - switch (_this.state) { - case 'HEADER': - // 13-18 - Allow a header (metadata) under the WEBVTT line. - if (/:/.test(line)) { - parseHeader(line); - } else if (!line) { - // An empty line terminates the header and starts the body (cues). - _this.state = 'ID'; - } - continue; - case 'NOTE': - // Ignore NOTE blocks. - if (!line) { - _this.state = 'ID'; - } - continue; - case 'ID': - // Check for the start of NOTE blocks. - if (/^NOTE($|[ \t])/.test(line)) { - _this.state = 'NOTE'; - break; - } - // 19-29 - Allow any number of line terminators, then initialize new cue values. - if (!line) { - continue; - } - _this.cue = new VTTCue(0, 0, ''); - _this.state = 'CUE'; - // 30-39 - Check if self line contains an optional identifier or timing data. - if (line.indexOf('-->') === -1) { - _this.cue.id = line; - continue; - } - // Process line as start of a cue. - /* falls through */ - case 'CUE': - // 40 - Collect cue timings and settings. - if (!_this.cue) { - _this.state = 'BADCUE'; - continue; - } - try { - parseCue(line, _this.cue, _this.regionList); - } catch (e) { - // In case of an error ignore rest of the cue. - _this.cue = null; - _this.state = 'BADCUE'; - continue; - } - _this.state = 'CUETEXT'; - continue; - case 'CUETEXT': - { - const hasSubstring = line.indexOf('-->') !== -1; - // 34 - If we have an empty line then report the cue. - // 35 - If we have the special substring '-->' then report the cue, - // but do not collect the line as we need to process the current - // one as a new cue. - if (!line || hasSubstring && (alreadyCollectedLine = true)) { - // We are done parsing self cue. - if (_this.oncue && _this.cue) { - _this.oncue(_this.cue); - } - _this.cue = null; - _this.state = 'ID'; - continue; - } - if (_this.cue === null) { - continue; - } - if (_this.cue.text) { - _this.cue.text += '\n'; - } - _this.cue.text += line; - } - continue; - case 'BADCUE': - // 54-62 - Collect and discard the remaining cue. - if (!line) { - _this.state = 'ID'; - } - } - } - } catch (e) { - // If we are currently parsing a cue, report what we have. - if (_this.state === 'CUETEXT' && _this.cue && _this.oncue) { - _this.oncue(_this.cue); - } - _this.cue = null; - // Enter BADWEBVTT state if header was not parsed correctly otherwise - // another exception occurred so enter BADCUE state. - _this.state = _this.state === 'INITIAL' ? 'BADWEBVTT' : 'BADCUE'; - } - return this; - } - flush() { - const _this = this; - try { - // Finish decoding the stream. - // _this.buffer += _this.decoder.decode(); - // Synthesize the end of the current cue or region. - if (_this.cue || _this.state === 'HEADER') { - _this.buffer += '\n\n'; - _this.parse(); - } - // If we've flushed, parsed, and we're still on the INITIAL state then - // that means we don't have enough of the stream to parse the first - // line. - if (_this.state === 'INITIAL' || _this.state === 'BADWEBVTT') { - throw new Error('Malformed WebVTT signature.'); - } - } catch (e) { - if (_this.onparsingerror) { - _this.onparsingerror(e); - } - } - if (_this.onflush) { - _this.onflush(); - } - return this; - } -} - -const LINEBREAKS = /\r\n|\n\r|\n|\r/g; - -// String.prototype.startsWith is not supported in IE11 -const startsWith = function startsWith(inputString, searchString, position = 0) { - return inputString.slice(position, position + searchString.length) === searchString; -}; -const cueString2millis = function cueString2millis(timeString) { - let ts = parseInt(timeString.slice(-3)); - const secs = parseInt(timeString.slice(-6, -4)); - const mins = parseInt(timeString.slice(-9, -7)); - const hours = timeString.length > 9 ? parseInt(timeString.substring(0, timeString.indexOf(':'))) : 0; - if (!isFiniteNumber(ts) || !isFiniteNumber(secs) || !isFiniteNumber(mins) || !isFiniteNumber(hours)) { - throw Error(`Malformed X-TIMESTAMP-MAP: Local:${timeString}`); - } - ts += 1000 * secs; - ts += 60 * 1000 * mins; - ts += 60 * 60 * 1000 * hours; - return ts; -}; - -// From https://github.com/darkskyapp/string-hash -const hash = function hash(text) { - let _hash = 5381; - let i = text.length; - while (i) { - _hash = _hash * 33 ^ text.charCodeAt(--i); - } - return (_hash >>> 0).toString(); -}; - -// Create a unique hash id for a cue based on start/end times and text. -// This helps timeline-controller to avoid showing repeated captions. -function generateCueId(startTime, endTime, text) { - return hash(startTime.toString()) + hash(endTime.toString()) + hash(text); -} -const calculateOffset = function calculateOffset(vttCCs, cc, presentationTime) { - let currCC = vttCCs[cc]; - let prevCC = vttCCs[currCC.prevCC]; - - // This is the first discontinuity or cues have been processed since the last discontinuity - // Offset = current discontinuity time - if (!prevCC || !prevCC.new && currCC.new) { - vttCCs.ccOffset = vttCCs.presentationOffset = currCC.start; - currCC.new = false; - return; - } - - // There have been discontinuities since cues were last parsed. - // Offset = time elapsed - while ((_prevCC = prevCC) != null && _prevCC.new) { - var _prevCC; - vttCCs.ccOffset += currCC.start - prevCC.start; - currCC.new = false; - currCC = prevCC; - prevCC = vttCCs[currCC.prevCC]; - } - vttCCs.presentationOffset = presentationTime; -}; -function parseWebVTT(vttByteArray, initPTS, vttCCs, cc, timeOffset, callBack, errorCallBack) { - const parser = new VTTParser(); - // Convert byteArray into string, replacing any somewhat exotic linefeeds with "\n", then split on that character. - // Uint8Array.prototype.reduce is not implemented in IE11 - const vttLines = utf8ArrayToStr(new Uint8Array(vttByteArray)).trim().replace(LINEBREAKS, '\n').split('\n'); - const cues = []; - const init90kHz = initPTS ? toMpegTsClockFromTimescale(initPTS.baseTime, initPTS.timescale) : 0; - let cueTime = '00:00.000'; - let timestampMapMPEGTS = 0; - let timestampMapLOCAL = 0; - let parsingError; - let inHeader = true; - parser.oncue = function (cue) { - // Adjust cue timing; clamp cues to start no earlier than - and drop cues that don't end after - 0 on timeline. - const currCC = vttCCs[cc]; - let cueOffset = vttCCs.ccOffset; - - // Calculate subtitle PTS offset - const webVttMpegTsMapOffset = (timestampMapMPEGTS - init90kHz) / 90000; - - // Update offsets for new discontinuities - if (currCC != null && currCC.new) { - if (timestampMapLOCAL !== undefined) { - // When local time is provided, offset = discontinuity start time - local time - cueOffset = vttCCs.ccOffset = currCC.start; - } else { - calculateOffset(vttCCs, cc, webVttMpegTsMapOffset); - } - } - if (webVttMpegTsMapOffset) { - if (!initPTS) { - parsingError = new Error('Missing initPTS for VTT MPEGTS'); - return; - } - // If we have MPEGTS, offset = presentation time + discontinuity offset - cueOffset = webVttMpegTsMapOffset - vttCCs.presentationOffset; - } - const duration = cue.endTime - cue.startTime; - const startTime = normalizePts((cue.startTime + cueOffset - timestampMapLOCAL) * 90000, timeOffset * 90000) / 90000; - cue.startTime = Math.max(startTime, 0); - cue.endTime = Math.max(startTime + duration, 0); - - //trim trailing webvtt block whitespaces - const text = cue.text.trim(); - - // Fix encoding of special characters - cue.text = decodeURIComponent(encodeURIComponent(text)); - - // If the cue was not assigned an id from the VTT file (line above the content), create one. - if (!cue.id) { - cue.id = generateCueId(cue.startTime, cue.endTime, text); - } - if (cue.endTime > 0) { - cues.push(cue); - } - }; - parser.onparsingerror = function (error) { - parsingError = error; - }; - parser.onflush = function () { - if (parsingError) { - errorCallBack(parsingError); - return; - } - callBack(cues); - }; - - // Go through contents line by line. - vttLines.forEach(line => { - if (inHeader) { - // Look for X-TIMESTAMP-MAP in header. - if (startsWith(line, 'X-TIMESTAMP-MAP=')) { - // Once found, no more are allowed anyway, so stop searching. - inHeader = false; - // Extract LOCAL and MPEGTS. - line.slice(16).split(',').forEach(timestamp => { - if (startsWith(timestamp, 'LOCAL:')) { - cueTime = timestamp.slice(6); - } else if (startsWith(timestamp, 'MPEGTS:')) { - timestampMapMPEGTS = parseInt(timestamp.slice(7)); - } - }); - try { - // Convert cue time to seconds - timestampMapLOCAL = cueString2millis(cueTime) / 1000; - } catch (error) { - parsingError = error; - } - // Return without parsing X-TIMESTAMP-MAP line. - return; - } else if (line === '') { - inHeader = false; - } - } - // Parse line by default. - parser.parse(line + '\n'); - }); - parser.flush(); -} - -const IMSC1_CODEC = 'stpp.ttml.im1t'; - -// Time format: h:m:s:frames(.subframes) -const HMSF_REGEX = /^(\d{2,}):(\d{2}):(\d{2}):(\d{2})\.?(\d+)?$/; - -// Time format: hours, minutes, seconds, milliseconds, frames, ticks -const TIME_UNIT_REGEX = /^(\d*(?:\.\d*)?)(h|m|s|ms|f|t)$/; -const textAlignToLineAlign = { - left: 'start', - center: 'center', - right: 'end', - start: 'start', - end: 'end' -}; -function parseIMSC1(payload, initPTS, callBack, errorCallBack) { - const results = findBox(new Uint8Array(payload), ['mdat']); - if (results.length === 0) { - errorCallBack(new Error('Could not parse IMSC1 mdat')); - return; - } - const ttmlList = results.map(mdat => utf8ArrayToStr(mdat)); - const syncTime = toTimescaleFromScale(initPTS.baseTime, 1, initPTS.timescale); - try { - ttmlList.forEach(ttml => callBack(parseTTML(ttml, syncTime))); - } catch (error) { - errorCallBack(error); - } -} -function parseTTML(ttml, syncTime) { - const parser = new DOMParser(); - const xmlDoc = parser.parseFromString(ttml, 'text/xml'); - const tt = xmlDoc.getElementsByTagName('tt')[0]; - if (!tt) { - throw new Error('Invalid ttml'); - } - const defaultRateInfo = { - frameRate: 30, - subFrameRate: 1, - frameRateMultiplier: 0, - tickRate: 0 - }; - const rateInfo = Object.keys(defaultRateInfo).reduce((result, key) => { - result[key] = tt.getAttribute(`ttp:${key}`) || defaultRateInfo[key]; - return result; - }, {}); - const trim = tt.getAttribute('xml:space') !== 'preserve'; - const styleElements = collectionToDictionary(getElementCollection(tt, 'styling', 'style')); - const regionElements = collectionToDictionary(getElementCollection(tt, 'layout', 'region')); - const cueElements = getElementCollection(tt, 'body', '[begin]'); - return [].map.call(cueElements, cueElement => { - const cueText = getTextContent(cueElement, trim); - if (!cueText || !cueElement.hasAttribute('begin')) { - return null; - } - const startTime = parseTtmlTime(cueElement.getAttribute('begin'), rateInfo); - const duration = parseTtmlTime(cueElement.getAttribute('dur'), rateInfo); - let endTime = parseTtmlTime(cueElement.getAttribute('end'), rateInfo); - if (startTime === null) { - throw timestampParsingError(cueElement); - } - if (endTime === null) { - if (duration === null) { - throw timestampParsingError(cueElement); - } - endTime = startTime + duration; - } - const cue = new VTTCue(startTime - syncTime, endTime - syncTime, cueText); - cue.id = generateCueId(cue.startTime, cue.endTime, cue.text); - const region = regionElements[cueElement.getAttribute('region')]; - const style = styleElements[cueElement.getAttribute('style')]; - - // Apply styles to cue - const styles = getTtmlStyles(region, style, styleElements); - const { - textAlign - } = styles; - if (textAlign) { - // cue.positionAlign not settable in FF~2016 - const lineAlign = textAlignToLineAlign[textAlign]; - if (lineAlign) { - cue.lineAlign = lineAlign; - } - cue.align = textAlign; - } - _extends(cue, styles); - return cue; - }).filter(cue => cue !== null); -} -function getElementCollection(fromElement, parentName, childName) { - const parent = fromElement.getElementsByTagName(parentName)[0]; - if (parent) { - return [].slice.call(parent.querySelectorAll(childName)); - } - return []; -} -function collectionToDictionary(elementsWithId) { - return elementsWithId.reduce((dict, element) => { - const id = element.getAttribute('xml:id'); - if (id) { - dict[id] = element; - } - return dict; - }, {}); -} -function getTextContent(element, trim) { - return [].slice.call(element.childNodes).reduce((str, node, i) => { - var _node$childNodes; - if (node.nodeName === 'br' && i) { - return str + '\n'; - } - if ((_node$childNodes = node.childNodes) != null && _node$childNodes.length) { - return getTextContent(node, trim); - } else if (trim) { - return str + node.textContent.trim().replace(/\s+/g, ' '); - } - return str + node.textContent; - }, ''); -} -function getTtmlStyles(region, style, styleElements) { - const ttsNs = 'http://www.w3.org/ns/ttml#styling'; - let regionStyle = null; - const styleAttributes = ['displayAlign', 'textAlign', 'color', 'backgroundColor', 'fontSize', 'fontFamily' - // 'fontWeight', - // 'lineHeight', - // 'wrapOption', - // 'fontStyle', - // 'direction', - // 'writingMode' - ]; - const regionStyleName = region != null && region.hasAttribute('style') ? region.getAttribute('style') : null; - if (regionStyleName && styleElements.hasOwnProperty(regionStyleName)) { - regionStyle = styleElements[regionStyleName]; - } - return styleAttributes.reduce((styles, name) => { - const value = getAttributeNS(style, ttsNs, name) || getAttributeNS(region, ttsNs, name) || getAttributeNS(regionStyle, ttsNs, name); - if (value) { - styles[name] = value; - } - return styles; - }, {}); -} -function getAttributeNS(element, ns, name) { - if (!element) { - return null; - } - return element.hasAttributeNS(ns, name) ? element.getAttributeNS(ns, name) : null; -} -function timestampParsingError(node) { - return new Error(`Could not parse ttml timestamp ${node}`); -} -function parseTtmlTime(timeAttributeValue, rateInfo) { - if (!timeAttributeValue) { - return null; - } - let seconds = parseTimeStamp(timeAttributeValue); - if (seconds === null) { - if (HMSF_REGEX.test(timeAttributeValue)) { - seconds = parseHoursMinutesSecondsFrames(timeAttributeValue, rateInfo); - } else if (TIME_UNIT_REGEX.test(timeAttributeValue)) { - seconds = parseTimeUnits(timeAttributeValue, rateInfo); - } - } - return seconds; -} -function parseHoursMinutesSecondsFrames(timeAttributeValue, rateInfo) { - const m = HMSF_REGEX.exec(timeAttributeValue); - const frames = (m[4] | 0) + (m[5] | 0) / rateInfo.subFrameRate; - return (m[1] | 0) * 3600 + (m[2] | 0) * 60 + (m[3] | 0) + frames / rateInfo.frameRate; -} -function parseTimeUnits(timeAttributeValue, rateInfo) { - const m = TIME_UNIT_REGEX.exec(timeAttributeValue); - const value = Number(m[1]); - const unit = m[2]; - switch (unit) { - case 'h': - return value * 3600; - case 'm': - return value * 60; - case 'ms': - return value * 1000; - case 'f': - return value / rateInfo.frameRate; - case 't': - return value / rateInfo.tickRate; - } - return value; -} - -class TimelineController { - constructor(hls) { - this.hls = void 0; - this.media = null; - this.config = void 0; - this.enabled = true; - this.Cues = void 0; - this.textTracks = []; - this.tracks = []; - this.initPTS = []; - this.unparsedVttFrags = []; - this.captionsTracks = {}; - this.nonNativeCaptionsTracks = {}; - this.cea608Parser1 = void 0; - this.cea608Parser2 = void 0; - this.lastCc = -1; - // Last video (CEA-608) fragment CC - this.lastSn = -1; - // Last video (CEA-608) fragment MSN - this.lastPartIndex = -1; - // Last video (CEA-608) fragment Part Index - this.prevCC = -1; - // Last subtitle fragment CC - this.vttCCs = newVTTCCs(); - this.captionsProperties = void 0; - this.hls = hls; - this.config = hls.config; - this.Cues = hls.config.cueHandler; - this.captionsProperties = { - textTrack1: { - label: this.config.captionsTextTrack1Label, - languageCode: this.config.captionsTextTrack1LanguageCode - }, - textTrack2: { - label: this.config.captionsTextTrack2Label, - languageCode: this.config.captionsTextTrack2LanguageCode - }, - textTrack3: { - label: this.config.captionsTextTrack3Label, - languageCode: this.config.captionsTextTrack3LanguageCode - }, - textTrack4: { - label: this.config.captionsTextTrack4Label, - languageCode: this.config.captionsTextTrack4LanguageCode - } - }; - hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); - hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this); - hls.on(Events.SUBTITLE_TRACKS_UPDATED, this.onSubtitleTracksUpdated, this); - hls.on(Events.FRAG_LOADING, this.onFragLoading, this); - hls.on(Events.FRAG_LOADED, this.onFragLoaded, this); - hls.on(Events.FRAG_PARSING_USERDATA, this.onFragParsingUserdata, this); - hls.on(Events.FRAG_DECRYPTED, this.onFragDecrypted, this); - hls.on(Events.INIT_PTS_FOUND, this.onInitPtsFound, this); - hls.on(Events.SUBTITLE_TRACKS_CLEARED, this.onSubtitleTracksCleared, this); - hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); - } - destroy() { - const { - hls - } = this; - hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); - hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this); - hls.off(Events.SUBTITLE_TRACKS_UPDATED, this.onSubtitleTracksUpdated, this); - hls.off(Events.FRAG_LOADING, this.onFragLoading, this); - hls.off(Events.FRAG_LOADED, this.onFragLoaded, this); - hls.off(Events.FRAG_PARSING_USERDATA, this.onFragParsingUserdata, this); - hls.off(Events.FRAG_DECRYPTED, this.onFragDecrypted, this); - hls.off(Events.INIT_PTS_FOUND, this.onInitPtsFound, this); - hls.off(Events.SUBTITLE_TRACKS_CLEARED, this.onSubtitleTracksCleared, this); - hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); - // @ts-ignore - this.hls = this.config = null; - this.cea608Parser1 = this.cea608Parser2 = undefined; - } - initCea608Parsers() { - if (this.config.enableCEA708Captions && (!this.cea608Parser1 || !this.cea608Parser2)) { - const channel1 = new OutputFilter(this, 'textTrack1'); - const channel2 = new OutputFilter(this, 'textTrack2'); - const channel3 = new OutputFilter(this, 'textTrack3'); - const channel4 = new OutputFilter(this, 'textTrack4'); - this.cea608Parser1 = new Cea608Parser(1, channel1, channel2); - this.cea608Parser2 = new Cea608Parser(3, channel3, channel4); - } - } - addCues(trackName, startTime, endTime, screen, cueRanges) { - // skip cues which overlap more than 50% with previously parsed time ranges - let merged = false; - for (let i = cueRanges.length; i--;) { - const cueRange = cueRanges[i]; - const overlap = intersection(cueRange[0], cueRange[1], startTime, endTime); - if (overlap >= 0) { - cueRange[0] = Math.min(cueRange[0], startTime); - cueRange[1] = Math.max(cueRange[1], endTime); - merged = true; - if (overlap / (endTime - startTime) > 0.5) { - return; - } - } - } - if (!merged) { - cueRanges.push([startTime, endTime]); - } - if (this.config.renderTextTracksNatively) { - const track = this.captionsTracks[trackName]; - this.Cues.newCue(track, startTime, endTime, screen); - } else { - const cues = this.Cues.newCue(null, startTime, endTime, screen); - this.hls.trigger(Events.CUES_PARSED, { - type: 'captions', - cues, - track: trackName - }); - } - } - - // Triggered when an initial PTS is found; used for synchronisation of WebVTT. - onInitPtsFound(event, { - frag, - id, - initPTS, - timescale - }) { - const { - unparsedVttFrags - } = this; - if (id === 'main') { - this.initPTS[frag.cc] = { - baseTime: initPTS, - timescale - }; - } - - // Due to asynchronous processing, initial PTS may arrive later than the first VTT fragments are loaded. - // Parse any unparsed fragments upon receiving the initial PTS. - if (unparsedVttFrags.length) { - this.unparsedVttFrags = []; - unparsedVttFrags.forEach(frag => { - this.onFragLoaded(Events.FRAG_LOADED, frag); - }); - } - } - getExistingTrack(label, language) { - const { - media - } = this; - if (media) { - for (let i = 0; i < media.textTracks.length; i++) { - const textTrack = media.textTracks[i]; - if (canReuseVttTextTrack(textTrack, { - name: label, - lang: language, - attrs: {} - })) { - return textTrack; - } - } - } - return null; - } - createCaptionsTrack(trackName) { - if (this.config.renderTextTracksNatively) { - this.createNativeTrack(trackName); - } else { - this.createNonNativeTrack(trackName); - } - } - createNativeTrack(trackName) { - if (this.captionsTracks[trackName]) { - return; - } - const { - captionsProperties, - captionsTracks, - media - } = this; - const { - label, - languageCode - } = captionsProperties[trackName]; - // Enable reuse of existing text track. - const existingTrack = this.getExistingTrack(label, languageCode); - if (!existingTrack) { - const textTrack = this.createTextTrack('captions', label, languageCode); - if (textTrack) { - // Set a special property on the track so we know it's managed by Hls.js - textTrack[trackName] = true; - captionsTracks[trackName] = textTrack; - } - } else { - captionsTracks[trackName] = existingTrack; - clearCurrentCues(captionsTracks[trackName]); - sendAddTrackEvent(captionsTracks[trackName], media); - } - } - createNonNativeTrack(trackName) { - if (this.nonNativeCaptionsTracks[trackName]) { - return; - } - // Create a list of a single track for the provider to consume - const trackProperties = this.captionsProperties[trackName]; - if (!trackProperties) { - return; - } - const label = trackProperties.label; - const track = { - _id: trackName, - label, - kind: 'captions', - default: trackProperties.media ? !!trackProperties.media.default : false, - closedCaptions: trackProperties.media - }; - this.nonNativeCaptionsTracks[trackName] = track; - this.hls.trigger(Events.NON_NATIVE_TEXT_TRACKS_FOUND, { - tracks: [track] - }); - } - createTextTrack(kind, label, lang) { - const media = this.media; - if (!media) { - return; - } - return media.addTextTrack(kind, label, lang); - } - onMediaAttaching(event, data) { - this.media = data.media; - this._cleanTracks(); - } - onMediaDetaching() { - const { - captionsTracks - } = this; - Object.keys(captionsTracks).forEach(trackName => { - clearCurrentCues(captionsTracks[trackName]); - delete captionsTracks[trackName]; - }); - this.nonNativeCaptionsTracks = {}; - } - onManifestLoading() { - // Detect discontinuity in video fragment (CEA-608) parsing - this.lastCc = -1; - this.lastSn = -1; - this.lastPartIndex = -1; - // Detect discontinuity in subtitle manifests - this.prevCC = -1; - this.vttCCs = newVTTCCs(); - // Reset tracks - this._cleanTracks(); - this.tracks = []; - this.captionsTracks = {}; - this.nonNativeCaptionsTracks = {}; - this.textTracks = []; - this.unparsedVttFrags = []; - this.initPTS = []; - if (this.cea608Parser1 && this.cea608Parser2) { - this.cea608Parser1.reset(); - this.cea608Parser2.reset(); - } - } - _cleanTracks() { - // clear outdated subtitles - const { - media - } = this; - if (!media) { - return; - } - const textTracks = media.textTracks; - if (textTracks) { - for (let i = 0; i < textTracks.length; i++) { - clearCurrentCues(textTracks[i]); - } - } - } - onSubtitleTracksUpdated(event, data) { - const tracks = data.subtitleTracks || []; - const hasIMSC1 = tracks.some(track => track.textCodec === IMSC1_CODEC); - if (this.config.enableWebVTT || hasIMSC1 && this.config.enableIMSC1) { - const listIsIdentical = subtitleOptionsIdentical(this.tracks, tracks); - if (listIsIdentical) { - this.tracks = tracks; - return; - } - this.textTracks = []; - this.tracks = tracks; - if (this.config.renderTextTracksNatively) { - const media = this.media; - const inUseTracks = media ? filterSubtitleTracks(media.textTracks) : null; - this.tracks.forEach((track, index) => { - // Reuse tracks with the same label and lang, but do not reuse 608/708 tracks - let textTrack; - if (inUseTracks) { - let inUseTrack = null; - for (let i = 0; i < inUseTracks.length; i++) { - if (inUseTracks[i] && canReuseVttTextTrack(inUseTracks[i], track)) { - inUseTrack = inUseTracks[i]; - inUseTracks[i] = null; - break; - } - } - if (inUseTrack) { - textTrack = inUseTrack; - } - } - if (textTrack) { - clearCurrentCues(textTrack); - } else { - const textTrackKind = captionsOrSubtitlesFromCharacteristics(track); - textTrack = this.createTextTrack(textTrackKind, track.name, track.lang); - if (textTrack) { - textTrack.mode = 'disabled'; - } - } - if (textTrack) { - this.textTracks.push(textTrack); - } - }); - // Warn when video element has captions or subtitle TextTracks carried over from another source - if (inUseTracks != null && inUseTracks.length) { - const unusedTextTracks = inUseTracks.filter(t => t !== null).map(t => t.label); - if (unusedTextTracks.length) { - logger.warn(`Media element contains unused subtitle tracks: ${unusedTextTracks.join(', ')}. Replace media element for each source to clear TextTracks and captions menu.`); - } - } - } else if (this.tracks.length) { - // Create a list of tracks for the provider to consume - const tracksList = this.tracks.map(track => { - return { - label: track.name, - kind: track.type.toLowerCase(), - default: track.default, - subtitleTrack: track - }; - }); - this.hls.trigger(Events.NON_NATIVE_TEXT_TRACKS_FOUND, { - tracks: tracksList - }); - } - } - } - onManifestLoaded(event, data) { - if (this.config.enableCEA708Captions && data.captions) { - data.captions.forEach(captionsTrack => { - const instreamIdMatch = /(?:CC|SERVICE)([1-4])/.exec(captionsTrack.instreamId); - if (!instreamIdMatch) { - return; - } - const trackName = `textTrack${instreamIdMatch[1]}`; - const trackProperties = this.captionsProperties[trackName]; - if (!trackProperties) { - return; - } - trackProperties.label = captionsTrack.name; - if (captionsTrack.lang) { - // optional attribute - trackProperties.languageCode = captionsTrack.lang; - } - trackProperties.media = captionsTrack; - }); - } - } - closedCaptionsForLevel(frag) { - const level = this.hls.levels[frag.level]; - return level == null ? void 0 : level.attrs['CLOSED-CAPTIONS']; - } - onFragLoading(event, data) { - // if this frag isn't contiguous, clear the parser so cues with bad start/end times aren't added to the textTrack - if (this.enabled && data.frag.type === PlaylistLevelType.MAIN) { - var _data$part$index, _data$part; - const { - cea608Parser1, - cea608Parser2, - lastSn - } = this; - const { - cc, - sn - } = data.frag; - const partIndex = (_data$part$index = (_data$part = data.part) == null ? void 0 : _data$part.index) != null ? _data$part$index : -1; - if (cea608Parser1 && cea608Parser2) { - if (sn !== lastSn + 1 || sn === lastSn && partIndex !== this.lastPartIndex + 1 || cc !== this.lastCc) { - cea608Parser1.reset(); - cea608Parser2.reset(); - } - } - this.lastCc = cc; - this.lastSn = sn; - this.lastPartIndex = partIndex; - } - } - onFragLoaded(event, data) { - const { - frag, - payload - } = data; - if (frag.type === PlaylistLevelType.SUBTITLE) { - // If fragment is subtitle type, parse as WebVTT. - if (payload.byteLength) { - const decryptData = frag.decryptdata; - // fragment after decryption has a stats object - const decrypted = ('stats' in data); - // If the subtitles are not encrypted, parse VTTs now. Otherwise, we need to wait. - if (decryptData == null || !decryptData.encrypted || decrypted) { - const trackPlaylistMedia = this.tracks[frag.level]; - const vttCCs = this.vttCCs; - if (!vttCCs[frag.cc]) { - vttCCs[frag.cc] = { - start: frag.start, - prevCC: this.prevCC, - new: true - }; - this.prevCC = frag.cc; - } - if (trackPlaylistMedia && trackPlaylistMedia.textCodec === IMSC1_CODEC) { - this._parseIMSC1(frag, payload); - } else { - this._parseVTTs(data); - } - } - } else { - // In case there is no payload, finish unsuccessfully. - this.hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, { - success: false, - frag, - error: new Error('Empty subtitle payload') - }); - } - } - } - _parseIMSC1(frag, payload) { - const hls = this.hls; - parseIMSC1(payload, this.initPTS[frag.cc], cues => { - this._appendCues(cues, frag.level); - hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, { - success: true, - frag: frag - }); - }, error => { - logger.log(`Failed to parse IMSC1: ${error}`); - hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, { - success: false, - frag: frag, - error - }); - }); - } - _parseVTTs(data) { - var _frag$initSegment; - const { - frag, - payload - } = data; - // We need an initial synchronisation PTS. Store fragments as long as none has arrived - const { - initPTS, - unparsedVttFrags - } = this; - const maxAvCC = initPTS.length - 1; - if (!initPTS[frag.cc] && maxAvCC === -1) { - unparsedVttFrags.push(data); - return; - } - const hls = this.hls; - // Parse the WebVTT file contents. - const payloadWebVTT = (_frag$initSegment = frag.initSegment) != null && _frag$initSegment.data ? appendUint8Array(frag.initSegment.data, new Uint8Array(payload)) : payload; - parseWebVTT(payloadWebVTT, this.initPTS[frag.cc], this.vttCCs, frag.cc, frag.start, cues => { - this._appendCues(cues, frag.level); - hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, { - success: true, - frag: frag - }); - }, error => { - const missingInitPTS = error.message === 'Missing initPTS for VTT MPEGTS'; - if (missingInitPTS) { - unparsedVttFrags.push(data); - } else { - this._fallbackToIMSC1(frag, payload); - } - // Something went wrong while parsing. Trigger event with success false. - logger.log(`Failed to parse VTT cue: ${error}`); - if (missingInitPTS && maxAvCC > frag.cc) { - return; - } - hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, { - success: false, - frag: frag, - error - }); - }); - } - _fallbackToIMSC1(frag, payload) { - // If textCodec is unknown, try parsing as IMSC1. Set textCodec based on the result - const trackPlaylistMedia = this.tracks[frag.level]; - if (!trackPlaylistMedia.textCodec) { - parseIMSC1(payload, this.initPTS[frag.cc], () => { - trackPlaylistMedia.textCodec = IMSC1_CODEC; - this._parseIMSC1(frag, payload); - }, () => { - trackPlaylistMedia.textCodec = 'wvtt'; - }); - } - } - _appendCues(cues, fragLevel) { - const hls = this.hls; - if (this.config.renderTextTracksNatively) { - const textTrack = this.textTracks[fragLevel]; - // WebVTTParser.parse is an async method and if the currently selected text track mode is set to "disabled" - // before parsing is done then don't try to access currentTrack.cues.getCueById as cues will be null - // and trying to access getCueById method of cues will throw an exception - // Because we check if the mode is disabled, we can force check `cues` below. They can't be null. - if (!textTrack || textTrack.mode === 'disabled') { - return; - } - cues.forEach(cue => addCueToTrack(textTrack, cue)); - } else { - const currentTrack = this.tracks[fragLevel]; - if (!currentTrack) { - return; - } - const track = currentTrack.default ? 'default' : 'subtitles' + fragLevel; - hls.trigger(Events.CUES_PARSED, { - type: 'subtitles', - cues, - track - }); - } - } - onFragDecrypted(event, data) { - const { - frag - } = data; - if (frag.type === PlaylistLevelType.SUBTITLE) { - this.onFragLoaded(Events.FRAG_LOADED, data); - } - } - onSubtitleTracksCleared() { - this.tracks = []; - this.captionsTracks = {}; - } - onFragParsingUserdata(event, data) { - this.initCea608Parsers(); - const { - cea608Parser1, - cea608Parser2 - } = this; - if (!this.enabled || !cea608Parser1 || !cea608Parser2) { - return; - } - const { - frag, - samples - } = data; - if (frag.type === PlaylistLevelType.MAIN && this.closedCaptionsForLevel(frag) === 'NONE') { - return; - } - // If the event contains captions (found in the bytes property), push all bytes into the parser immediately - // It will create the proper timestamps based on the PTS value - for (let i = 0; i < samples.length; i++) { - const ccBytes = samples[i].bytes; - if (ccBytes) { - const ccdatas = this.extractCea608Data(ccBytes); - cea608Parser1.addData(samples[i].pts, ccdatas[0]); - cea608Parser2.addData(samples[i].pts, ccdatas[1]); - } - } - } - onBufferFlushing(event, { - startOffset, - endOffset, - endOffsetSubtitles, - type - }) { - const { - media - } = this; - if (!media || media.currentTime < endOffset) { - return; - } - // Clear 608 caption cues from the captions TextTracks when the video back buffer is flushed - // Forward cues are never removed because we can loose streamed 608 content from recent fragments - if (!type || type === 'video') { - const { - captionsTracks - } = this; - Object.keys(captionsTracks).forEach(trackName => removeCuesInRange(captionsTracks[trackName], startOffset, endOffset)); - } - if (this.config.renderTextTracksNatively) { - // Clear VTT/IMSC1 subtitle cues from the subtitle TextTracks when the back buffer is flushed - if (startOffset === 0 && endOffsetSubtitles !== undefined) { - const { - textTracks - } = this; - Object.keys(textTracks).forEach(trackName => removeCuesInRange(textTracks[trackName], startOffset, endOffsetSubtitles)); - } - } - } - extractCea608Data(byteArray) { - const actualCCBytes = [[], []]; - const count = byteArray[0] & 0x1f; - let position = 2; - for (let j = 0; j < count; j++) { - const tmpByte = byteArray[position++]; - const ccbyte1 = 0x7f & byteArray[position++]; - const ccbyte2 = 0x7f & byteArray[position++]; - if (ccbyte1 === 0 && ccbyte2 === 0) { - continue; - } - const ccValid = (0x04 & tmpByte) !== 0; // Support all four channels - if (ccValid) { - const ccType = 0x03 & tmpByte; - if (0x00 /* CEA608 field1*/ === ccType || 0x01 /* CEA608 field2*/ === ccType) { - // Exclude CEA708 CC data. - actualCCBytes[ccType].push(ccbyte1); - actualCCBytes[ccType].push(ccbyte2); - } - } - } - return actualCCBytes; - } -} -function captionsOrSubtitlesFromCharacteristics(track) { - if (track.characteristics) { - if (/transcribes-spoken-dialog/gi.test(track.characteristics) && /describes-music-and-sound/gi.test(track.characteristics)) { - return 'captions'; - } - } - return 'subtitles'; -} -function canReuseVttTextTrack(inUseTrack, manifestTrack) { - return !!inUseTrack && inUseTrack.kind === captionsOrSubtitlesFromCharacteristics(manifestTrack) && subtitleTrackMatchesTextTrack(manifestTrack, inUseTrack); -} -function intersection(x1, x2, y1, y2) { - return Math.min(x2, y2) - Math.max(x1, y1); -} -function newVTTCCs() { - return { - ccOffset: 0, - presentationOffset: 0, - 0: { - start: 0, - prevCC: -1, - new: true - } - }; -} - -class CapLevelController { - constructor(hls) { - this.hls = void 0; - this.autoLevelCapping = void 0; - this.firstLevel = void 0; - this.media = void 0; - this.restrictedLevels = void 0; - this.timer = void 0; - this.clientRect = void 0; - this.streamController = void 0; - this.hls = hls; - this.autoLevelCapping = Number.POSITIVE_INFINITY; - this.firstLevel = -1; - this.media = null; - this.restrictedLevels = []; - this.timer = undefined; - this.clientRect = null; - this.registerListeners(); - } - setStreamController(streamController) { - this.streamController = streamController; - } - destroy() { - if (this.hls) { - this.unregisterListener(); - } - if (this.timer) { - this.stopCapping(); - } - this.media = null; - this.clientRect = null; - // @ts-ignore - this.hls = this.streamController = null; - } - registerListeners() { - const { - hls - } = this; - hls.on(Events.FPS_DROP_LEVEL_CAPPING, this.onFpsDropLevelCapping, this); - hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); - hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this); - hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); - hls.on(Events.BUFFER_CODECS, this.onBufferCodecs, this); - hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - } - unregisterListener() { - const { - hls - } = this; - hls.off(Events.FPS_DROP_LEVEL_CAPPING, this.onFpsDropLevelCapping, this); - hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); - hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this); - hls.off(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); - hls.off(Events.BUFFER_CODECS, this.onBufferCodecs, this); - hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - } - onFpsDropLevelCapping(event, data) { - // Don't add a restricted level more than once - const level = this.hls.levels[data.droppedLevel]; - if (this.isLevelAllowed(level)) { - this.restrictedLevels.push({ - bitrate: level.bitrate, - height: level.height, - width: level.width - }); - } - } - onMediaAttaching(event, data) { - this.media = data.media instanceof HTMLVideoElement ? data.media : null; - this.clientRect = null; - if (this.timer && this.hls.levels.length) { - this.detectPlayerSize(); - } - } - onManifestParsed(event, data) { - const hls = this.hls; - this.restrictedLevels = []; - this.firstLevel = data.firstLevel; - if (hls.config.capLevelToPlayerSize && data.video) { - // Start capping immediately if the manifest has signaled video codecs - this.startCapping(); - } - } - onLevelsUpdated(event, data) { - if (this.timer && isFiniteNumber(this.autoLevelCapping)) { - this.detectPlayerSize(); - } - } - - // Only activate capping when playing a video stream; otherwise, multi-bitrate audio-only streams will be restricted - // to the first level - onBufferCodecs(event, data) { - const hls = this.hls; - if (hls.config.capLevelToPlayerSize && data.video) { - // If the manifest did not signal a video codec capping has been deferred until we're certain video is present - this.startCapping(); - } - } - onMediaDetaching() { - this.stopCapping(); - } - detectPlayerSize() { - if (this.media) { - if (this.mediaHeight <= 0 || this.mediaWidth <= 0) { - this.clientRect = null; - return; - } - const levels = this.hls.levels; - if (levels.length) { - const hls = this.hls; - const maxLevel = this.getMaxLevel(levels.length - 1); - if (maxLevel !== this.autoLevelCapping) { - logger.log(`Setting autoLevelCapping to ${maxLevel}: ${levels[maxLevel].height}p@${levels[maxLevel].bitrate} for media ${this.mediaWidth}x${this.mediaHeight}`); - } - hls.autoLevelCapping = maxLevel; - if (hls.autoLevelCapping > this.autoLevelCapping && this.streamController) { - // if auto level capping has a higher value for the previous one, flush the buffer using nextLevelSwitch - // usually happen when the user go to the fullscreen mode. - this.streamController.nextLevelSwitch(); - } - this.autoLevelCapping = hls.autoLevelCapping; - } - } - } - - /* - * returns level should be the one with the dimensions equal or greater than the media (player) dimensions (so the video will be downscaled) - */ - getMaxLevel(capLevelIndex) { - const levels = this.hls.levels; - if (!levels.length) { - return -1; - } - const validLevels = levels.filter((level, index) => this.isLevelAllowed(level) && index <= capLevelIndex); - this.clientRect = null; - return CapLevelController.getMaxLevelByMediaSize(validLevels, this.mediaWidth, this.mediaHeight); - } - startCapping() { - if (this.timer) { - // Don't reset capping if started twice; this can happen if the manifest signals a video codec - return; - } - this.autoLevelCapping = Number.POSITIVE_INFINITY; - self.clearInterval(this.timer); - this.timer = self.setInterval(this.detectPlayerSize.bind(this), 1000); - this.detectPlayerSize(); - } - stopCapping() { - this.restrictedLevels = []; - this.firstLevel = -1; - this.autoLevelCapping = Number.POSITIVE_INFINITY; - if (this.timer) { - self.clearInterval(this.timer); - this.timer = undefined; - } - } - getDimensions() { - if (this.clientRect) { - return this.clientRect; - } - const media = this.media; - const boundsRect = { - width: 0, - height: 0 - }; - if (media) { - const clientRect = media.getBoundingClientRect(); - boundsRect.width = clientRect.width; - boundsRect.height = clientRect.height; - if (!boundsRect.width && !boundsRect.height) { - // When the media element has no width or height (equivalent to not being in the DOM), - // then use its width and height attributes (media.width, media.height) - boundsRect.width = clientRect.right - clientRect.left || media.width || 0; - boundsRect.height = clientRect.bottom - clientRect.top || media.height || 0; - } - } - this.clientRect = boundsRect; - return boundsRect; - } - get mediaWidth() { - return this.getDimensions().width * this.contentScaleFactor; - } - get mediaHeight() { - return this.getDimensions().height * this.contentScaleFactor; - } - get contentScaleFactor() { - let pixelRatio = 1; - if (!this.hls.config.ignoreDevicePixelRatio) { - try { - pixelRatio = self.devicePixelRatio; - } catch (e) { - /* no-op */ - } - } - return pixelRatio; - } - isLevelAllowed(level) { - const restrictedLevels = this.restrictedLevels; - return !restrictedLevels.some(restrictedLevel => { - return level.bitrate === restrictedLevel.bitrate && level.width === restrictedLevel.width && level.height === restrictedLevel.height; - }); - } - static getMaxLevelByMediaSize(levels, width, height) { - if (!(levels != null && levels.length)) { - return -1; - } - - // Levels can have the same dimensions but differing bandwidths - since levels are ordered, we can look to the next - // to determine whether we've chosen the greatest bandwidth for the media's dimensions - const atGreatestBandwidth = (curLevel, nextLevel) => { - if (!nextLevel) { - return true; - } - return curLevel.width !== nextLevel.width || curLevel.height !== nextLevel.height; - }; - - // If we run through the loop without breaking, the media's dimensions are greater than every level, so default to - // the max level - let maxLevelIndex = levels.length - 1; - // Prevent changes in aspect-ratio from causing capping to toggle back and forth - const squareSize = Math.max(width, height); - for (let i = 0; i < levels.length; i += 1) { - const level = levels[i]; - if ((level.width >= squareSize || level.height >= squareSize) && atGreatestBandwidth(level, levels[i + 1])) { - maxLevelIndex = i; - break; - } - } - return maxLevelIndex; - } -} - -class FPSController { - constructor(hls) { - this.hls = void 0; - this.isVideoPlaybackQualityAvailable = false; - this.timer = void 0; - this.media = null; - this.lastTime = void 0; - this.lastDroppedFrames = 0; - this.lastDecodedFrames = 0; - // stream controller must be provided as a dependency! - this.streamController = void 0; - this.hls = hls; - this.registerListeners(); - } - setStreamController(streamController) { - this.streamController = streamController; - } - registerListeners() { - this.hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); - } - unregisterListeners() { - this.hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); - } - destroy() { - if (this.timer) { - clearInterval(this.timer); - } - this.unregisterListeners(); - this.isVideoPlaybackQualityAvailable = false; - this.media = null; - } - onMediaAttaching(event, data) { - const config = this.hls.config; - if (config.capLevelOnFPSDrop) { - const media = data.media instanceof self.HTMLVideoElement ? data.media : null; - this.media = media; - if (media && typeof media.getVideoPlaybackQuality === 'function') { - this.isVideoPlaybackQualityAvailable = true; - } - self.clearInterval(this.timer); - this.timer = self.setInterval(this.checkFPSInterval.bind(this), config.fpsDroppedMonitoringPeriod); - } - } - checkFPS(video, decodedFrames, droppedFrames) { - const currentTime = performance.now(); - if (decodedFrames) { - if (this.lastTime) { - const currentPeriod = currentTime - this.lastTime; - const currentDropped = droppedFrames - this.lastDroppedFrames; - const currentDecoded = decodedFrames - this.lastDecodedFrames; - const droppedFPS = 1000 * currentDropped / currentPeriod; - const hls = this.hls; - hls.trigger(Events.FPS_DROP, { - currentDropped: currentDropped, - currentDecoded: currentDecoded, - totalDroppedFrames: droppedFrames - }); - if (droppedFPS > 0) { - // logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod)); - if (currentDropped > hls.config.fpsDroppedMonitoringThreshold * currentDecoded) { - let currentLevel = hls.currentLevel; - logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel); - if (currentLevel > 0 && (hls.autoLevelCapping === -1 || hls.autoLevelCapping >= currentLevel)) { - currentLevel = currentLevel - 1; - hls.trigger(Events.FPS_DROP_LEVEL_CAPPING, { - level: currentLevel, - droppedLevel: hls.currentLevel - }); - hls.autoLevelCapping = currentLevel; - this.streamController.nextLevelSwitch(); - } - } - } - } - this.lastTime = currentTime; - this.lastDroppedFrames = droppedFrames; - this.lastDecodedFrames = decodedFrames; - } - } - checkFPSInterval() { - const video = this.media; - if (video) { - if (this.isVideoPlaybackQualityAvailable) { - const videoPlaybackQuality = video.getVideoPlaybackQuality(); - this.checkFPS(video, videoPlaybackQuality.totalVideoFrames, videoPlaybackQuality.droppedVideoFrames); - } else { - // HTMLVideoElement doesn't include the webkit types - this.checkFPS(video, video.webkitDecodedFrameCount, video.webkitDroppedFrameCount); - } - } - } -} - -const LOGGER_PREFIX = '[eme]'; -/** - * Controller to deal with encrypted media extensions (EME) - * @see https://developer.mozilla.org/en-US/docs/Web/API/Encrypted_Media_Extensions_API - * - * @class - * @constructor - */ -class EMEController { - constructor(hls) { - this.hls = void 0; - this.config = void 0; - this.media = null; - this.keyFormatPromise = null; - this.keySystemAccessPromises = {}; - this._requestLicenseFailureCount = 0; - this.mediaKeySessions = []; - this.keyIdToKeySessionPromise = {}; - this.setMediaKeysQueue = EMEController.CDMCleanupPromise ? [EMEController.CDMCleanupPromise] : []; - this.onMediaEncrypted = this._onMediaEncrypted.bind(this); - this.onWaitingForKey = this._onWaitingForKey.bind(this); - this.debug = logger.debug.bind(logger, LOGGER_PREFIX); - this.log = logger.log.bind(logger, LOGGER_PREFIX); - this.warn = logger.warn.bind(logger, LOGGER_PREFIX); - this.error = logger.error.bind(logger, LOGGER_PREFIX); - this.hls = hls; - this.config = hls.config; - this.registerListeners(); - } - destroy() { - this.unregisterListeners(); - this.onMediaDetached(); - // Remove any references that could be held in config options or callbacks - const config = this.config; - config.requestMediaKeySystemAccessFunc = null; - config.licenseXhrSetup = config.licenseResponseCallback = undefined; - config.drmSystems = config.drmSystemOptions = {}; - // @ts-ignore - this.hls = this.onMediaEncrypted = this.onWaitingForKey = this.keyIdToKeySessionPromise = null; - // @ts-ignore - this.config = null; - } - registerListeners() { - this.hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - this.hls.on(Events.MEDIA_DETACHED, this.onMediaDetached, this); - this.hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); - this.hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this); - } - unregisterListeners() { - this.hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - this.hls.off(Events.MEDIA_DETACHED, this.onMediaDetached, this); - this.hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); - this.hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this); - } - getLicenseServerUrl(keySystem) { - const { - drmSystems, - widevineLicenseUrl - } = this.config; - const keySystemConfiguration = drmSystems[keySystem]; - if (keySystemConfiguration) { - return keySystemConfiguration.licenseUrl; - } - - // For backward compatibility - if (keySystem === KeySystems.WIDEVINE && widevineLicenseUrl) { - return widevineLicenseUrl; - } - throw new Error(`no license server URL configured for key-system "${keySystem}"`); - } - getServerCertificateUrl(keySystem) { - const { - drmSystems - } = this.config; - const keySystemConfiguration = drmSystems[keySystem]; - if (keySystemConfiguration) { - return keySystemConfiguration.serverCertificateUrl; - } else { - this.log(`No Server Certificate in config.drmSystems["${keySystem}"]`); - } - } - attemptKeySystemAccess(keySystemsToAttempt) { - const levels = this.hls.levels; - const uniqueCodec = (value, i, a) => !!value && a.indexOf(value) === i; - const audioCodecs = levels.map(level => level.audioCodec).filter(uniqueCodec); - const videoCodecs = levels.map(level => level.videoCodec).filter(uniqueCodec); - if (audioCodecs.length + videoCodecs.length === 0) { - videoCodecs.push('avc1.42e01e'); - } - return new Promise((resolve, reject) => { - const attempt = keySystems => { - const keySystem = keySystems.shift(); - this.getMediaKeysPromise(keySystem, audioCodecs, videoCodecs).then(mediaKeys => resolve({ - keySystem, - mediaKeys - })).catch(error => { - if (keySystems.length) { - attempt(keySystems); - } else if (error instanceof EMEKeyError) { - reject(error); - } else { - reject(new EMEKeyError({ - type: ErrorTypes.KEY_SYSTEM_ERROR, - details: ErrorDetails.KEY_SYSTEM_NO_ACCESS, - error, - fatal: true - }, error.message)); - } - }); - }; - attempt(keySystemsToAttempt); - }); - } - requestMediaKeySystemAccess(keySystem, supportedConfigurations) { - const { - requestMediaKeySystemAccessFunc - } = this.config; - if (!(typeof requestMediaKeySystemAccessFunc === 'function')) { - let errMessage = `Configured requestMediaKeySystemAccess is not a function ${requestMediaKeySystemAccessFunc}`; - if (requestMediaKeySystemAccess === null && self.location.protocol === 'http:') { - errMessage = `navigator.requestMediaKeySystemAccess is not available over insecure protocol ${location.protocol}`; - } - return Promise.reject(new Error(errMessage)); - } - return requestMediaKeySystemAccessFunc(keySystem, supportedConfigurations); - } - getMediaKeysPromise(keySystem, audioCodecs, videoCodecs) { - // This can throw, but is caught in event handler callpath - const mediaKeySystemConfigs = getSupportedMediaKeySystemConfigurations(keySystem, audioCodecs, videoCodecs, this.config.drmSystemOptions); - const keySystemAccessPromises = this.keySystemAccessPromises[keySystem]; - let keySystemAccess = keySystemAccessPromises == null ? void 0 : keySystemAccessPromises.keySystemAccess; - if (!keySystemAccess) { - this.log(`Requesting encrypted media "${keySystem}" key-system access with config: ${JSON.stringify(mediaKeySystemConfigs)}`); - keySystemAccess = this.requestMediaKeySystemAccess(keySystem, mediaKeySystemConfigs); - const _keySystemAccessPromises = this.keySystemAccessPromises[keySystem] = { - keySystemAccess - }; - keySystemAccess.catch(error => { - this.log(`Failed to obtain access to key-system "${keySystem}": ${error}`); - }); - return keySystemAccess.then(mediaKeySystemAccess => { - this.log(`Access for key-system "${mediaKeySystemAccess.keySystem}" obtained`); - const certificateRequest = this.fetchServerCertificate(keySystem); - this.log(`Create media-keys for "${keySystem}"`); - _keySystemAccessPromises.mediaKeys = mediaKeySystemAccess.createMediaKeys().then(mediaKeys => { - this.log(`Media-keys created for "${keySystem}"`); - return certificateRequest.then(certificate => { - if (certificate) { - return this.setMediaKeysServerCertificate(mediaKeys, keySystem, certificate); - } - return mediaKeys; - }); - }); - _keySystemAccessPromises.mediaKeys.catch(error => { - this.error(`Failed to create media-keys for "${keySystem}"}: ${error}`); - }); - return _keySystemAccessPromises.mediaKeys; - }); - } - return keySystemAccess.then(() => keySystemAccessPromises.mediaKeys); - } - createMediaKeySessionContext({ - decryptdata, - keySystem, - mediaKeys - }) { - this.log(`Creating key-system session "${keySystem}" keyId: ${Hex.hexDump(decryptdata.keyId || [])}`); - const mediaKeysSession = mediaKeys.createSession(); - const mediaKeySessionContext = { - decryptdata, - keySystem, - mediaKeys, - mediaKeysSession, - keyStatus: 'status-pending' - }; - this.mediaKeySessions.push(mediaKeySessionContext); - return mediaKeySessionContext; - } - renewKeySession(mediaKeySessionContext) { - const decryptdata = mediaKeySessionContext.decryptdata; - if (decryptdata.pssh) { - const keySessionContext = this.createMediaKeySessionContext(mediaKeySessionContext); - const keyId = this.getKeyIdString(decryptdata); - const scheme = 'cenc'; - this.keyIdToKeySessionPromise[keyId] = this.generateRequestWithPreferredKeySession(keySessionContext, scheme, decryptdata.pssh, 'expired'); - } else { - this.warn(`Could not renew expired session. Missing pssh initData.`); - } - this.removeSession(mediaKeySessionContext); - } - getKeyIdString(decryptdata) { - if (!decryptdata) { - throw new Error('Could not read keyId of undefined decryptdata'); - } - if (decryptdata.keyId === null) { - throw new Error('keyId is null'); - } - return Hex.hexDump(decryptdata.keyId); - } - updateKeySession(mediaKeySessionContext, data) { - var _mediaKeySessionConte; - const keySession = mediaKeySessionContext.mediaKeysSession; - this.log(`Updating key-session "${keySession.sessionId}" for keyID ${Hex.hexDump(((_mediaKeySessionConte = mediaKeySessionContext.decryptdata) == null ? void 0 : _mediaKeySessionConte.keyId) || [])} - } (data length: ${data ? data.byteLength : data})`); - return keySession.update(data); - } - selectKeySystemFormat(frag) { - const keyFormats = Object.keys(frag.levelkeys || {}); - if (!this.keyFormatPromise) { - this.log(`Selecting key-system from fragment (sn: ${frag.sn} ${frag.type}: ${frag.level}) key formats ${keyFormats.join(', ')}`); - this.keyFormatPromise = this.getKeyFormatPromise(keyFormats); - } - return this.keyFormatPromise; - } - getKeyFormatPromise(keyFormats) { - return new Promise((resolve, reject) => { - const keySystemsInConfig = getKeySystemsForConfig(this.config); - const keySystemsToAttempt = keyFormats.map(keySystemFormatToKeySystemDomain).filter(value => !!value && keySystemsInConfig.indexOf(value) !== -1); - return this.getKeySystemSelectionPromise(keySystemsToAttempt).then(({ - keySystem - }) => { - const keySystemFormat = keySystemDomainToKeySystemFormat(keySystem); - if (keySystemFormat) { - resolve(keySystemFormat); - } else { - reject(new Error(`Unable to find format for key-system "${keySystem}"`)); - } - }).catch(reject); - }); - } - loadKey(data) { - const decryptdata = data.keyInfo.decryptdata; - const keyId = this.getKeyIdString(decryptdata); - const keyDetails = `(keyId: ${keyId} format: "${decryptdata.keyFormat}" method: ${decryptdata.method} uri: ${decryptdata.uri})`; - this.log(`Starting session for key ${keyDetails}`); - let keySessionContextPromise = this.keyIdToKeySessionPromise[keyId]; - if (!keySessionContextPromise) { - keySessionContextPromise = this.keyIdToKeySessionPromise[keyId] = this.getKeySystemForKeyPromise(decryptdata).then(({ - keySystem, - mediaKeys - }) => { - this.throwIfDestroyed(); - this.log(`Handle encrypted media sn: ${data.frag.sn} ${data.frag.type}: ${data.frag.level} using key ${keyDetails}`); - return this.attemptSetMediaKeys(keySystem, mediaKeys).then(() => { - this.throwIfDestroyed(); - const keySessionContext = this.createMediaKeySessionContext({ - keySystem, - mediaKeys, - decryptdata - }); - const scheme = 'cenc'; - return this.generateRequestWithPreferredKeySession(keySessionContext, scheme, decryptdata.pssh, 'playlist-key'); - }); - }); - keySessionContextPromise.catch(error => this.handleError(error)); - } - return keySessionContextPromise; - } - throwIfDestroyed(message = 'Invalid state') { - if (!this.hls) { - throw new Error('invalid state'); - } - } - handleError(error) { - if (!this.hls) { - return; - } - this.error(error.message); - if (error instanceof EMEKeyError) { - this.hls.trigger(Events.ERROR, error.data); - } else { - this.hls.trigger(Events.ERROR, { - type: ErrorTypes.KEY_SYSTEM_ERROR, - details: ErrorDetails.KEY_SYSTEM_NO_KEYS, - error, - fatal: true - }); - } - } - getKeySystemForKeyPromise(decryptdata) { - const keyId = this.getKeyIdString(decryptdata); - const mediaKeySessionContext = this.keyIdToKeySessionPromise[keyId]; - if (!mediaKeySessionContext) { - const keySystem = keySystemFormatToKeySystemDomain(decryptdata.keyFormat); - const keySystemsToAttempt = keySystem ? [keySystem] : getKeySystemsForConfig(this.config); - return this.attemptKeySystemAccess(keySystemsToAttempt); - } - return mediaKeySessionContext; - } - getKeySystemSelectionPromise(keySystemsToAttempt) { - if (!keySystemsToAttempt.length) { - keySystemsToAttempt = getKeySystemsForConfig(this.config); - } - if (keySystemsToAttempt.length === 0) { - throw new EMEKeyError({ - type: ErrorTypes.KEY_SYSTEM_ERROR, - details: ErrorDetails.KEY_SYSTEM_NO_CONFIGURED_LICENSE, - fatal: true - }, `Missing key-system license configuration options ${JSON.stringify({ - drmSystems: this.config.drmSystems - })}`); - } - return this.attemptKeySystemAccess(keySystemsToAttempt); - } - _onMediaEncrypted(event) { - const { - initDataType, - initData - } = event; - const logMessage = `"${event.type}" event: init data type: "${initDataType}"`; - this.debug(logMessage); - - // Ignore event when initData is null - if (initData === null) { - return; - } - let keyId; - let keySystemDomain; - if (initDataType === 'sinf' && this.config.drmSystems[KeySystems.FAIRPLAY]) { - // Match sinf keyId to playlist skd://keyId= - const json = bin2str(new Uint8Array(initData)); - try { - const sinf = base64Decode(JSON.parse(json).sinf); - const tenc = parseSinf(new Uint8Array(sinf)); - if (!tenc) { - throw new Error(`'schm' box missing or not cbcs/cenc with schi > tenc`); - } - keyId = tenc.subarray(8, 24); - keySystemDomain = KeySystems.FAIRPLAY; - } catch (error) { - this.warn(`${logMessage} Failed to parse sinf: ${error}`); - return; - } - } else { - // Support Widevine clear-lead key-session creation (otherwise depend on playlist keys) - const psshResults = parseMultiPssh(initData); - const psshInfo = psshResults.filter(pssh => pssh.systemId === KeySystemIds.WIDEVINE)[0]; - if (!psshInfo) { - if (psshResults.length === 0 || psshResults.some(pssh => !pssh.systemId)) { - this.warn(`${logMessage} contains incomplete or invalid pssh data`); - } else { - this.log(`ignoring ${logMessage} for ${psshResults.map(pssh => keySystemIdToKeySystemDomain(pssh.systemId)).join(',')} pssh data in favor of playlist keys`); - } - return; - } - keySystemDomain = keySystemIdToKeySystemDomain(psshInfo.systemId); - if (psshInfo.version === 0 && psshInfo.data) { - const offset = psshInfo.data.length - 22; - keyId = psshInfo.data.subarray(offset, offset + 16); - } - } - if (!keySystemDomain || !keyId) { - return; - } - const keyIdHex = Hex.hexDump(keyId); - const { - keyIdToKeySessionPromise, - mediaKeySessions - } = this; - let keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex]; - for (let i = 0; i < mediaKeySessions.length; i++) { - // Match playlist key - const keyContext = mediaKeySessions[i]; - const decryptdata = keyContext.decryptdata; - if (!decryptdata.keyId) { - continue; - } - const oldKeyIdHex = Hex.hexDump(decryptdata.keyId); - if (keyIdHex === oldKeyIdHex || decryptdata.uri.replace(/-/g, '').indexOf(keyIdHex) !== -1) { - keySessionContextPromise = keyIdToKeySessionPromise[oldKeyIdHex]; - if (decryptdata.pssh) { - break; - } - delete keyIdToKeySessionPromise[oldKeyIdHex]; - decryptdata.pssh = new Uint8Array(initData); - decryptdata.keyId = keyId; - keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex] = keySessionContextPromise.then(() => { - return this.generateRequestWithPreferredKeySession(keyContext, initDataType, initData, 'encrypted-event-key-match'); - }); - break; - } - } - if (!keySessionContextPromise) { - // Clear-lead key (not encountered in playlist) - keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex] = this.getKeySystemSelectionPromise([keySystemDomain]).then(({ - keySystem, - mediaKeys - }) => { - var _keySystemToKeySystem; - this.throwIfDestroyed(); - const decryptdata = new LevelKey('ISO-23001-7', keyIdHex, (_keySystemToKeySystem = keySystemDomainToKeySystemFormat(keySystem)) != null ? _keySystemToKeySystem : ''); - decryptdata.pssh = new Uint8Array(initData); - decryptdata.keyId = keyId; - return this.attemptSetMediaKeys(keySystem, mediaKeys).then(() => { - this.throwIfDestroyed(); - const keySessionContext = this.createMediaKeySessionContext({ - decryptdata, - keySystem, - mediaKeys - }); - return this.generateRequestWithPreferredKeySession(keySessionContext, initDataType, initData, 'encrypted-event-no-match'); - }); - }); - } - keySessionContextPromise.catch(error => this.handleError(error)); - } - _onWaitingForKey(event) { - this.log(`"${event.type}" event`); - } - attemptSetMediaKeys(keySystem, mediaKeys) { - const queue = this.setMediaKeysQueue.slice(); - this.log(`Setting media-keys for "${keySystem}"`); - // Only one setMediaKeys() can run at one time, and multiple setMediaKeys() operations - // can be queued for execution for multiple key sessions. - const setMediaKeysPromise = Promise.all(queue).then(() => { - if (!this.media) { - throw new Error('Attempted to set mediaKeys without media element attached'); - } - return this.media.setMediaKeys(mediaKeys); - }); - this.setMediaKeysQueue.push(setMediaKeysPromise); - return setMediaKeysPromise.then(() => { - this.log(`Media-keys set for "${keySystem}"`); - queue.push(setMediaKeysPromise); - this.setMediaKeysQueue = this.setMediaKeysQueue.filter(p => queue.indexOf(p) === -1); - }); - } - generateRequestWithPreferredKeySession(context, initDataType, initData, reason) { - var _this$config$drmSyste, _this$config$drmSyste2; - const generateRequestFilter = (_this$config$drmSyste = this.config.drmSystems) == null ? void 0 : (_this$config$drmSyste2 = _this$config$drmSyste[context.keySystem]) == null ? void 0 : _this$config$drmSyste2.generateRequest; - if (generateRequestFilter) { - try { - const mappedInitData = generateRequestFilter.call(this.hls, initDataType, initData, context); - if (!mappedInitData) { - throw new Error('Invalid response from configured generateRequest filter'); - } - initDataType = mappedInitData.initDataType; - initData = context.decryptdata.pssh = mappedInitData.initData ? new Uint8Array(mappedInitData.initData) : null; - } catch (error) { - var _this$hls; - this.warn(error.message); - if ((_this$hls = this.hls) != null && _this$hls.config.debug) { - throw error; - } - } - } - if (initData === null) { - this.log(`Skipping key-session request for "${reason}" (no initData)`); - return Promise.resolve(context); - } - const keyId = this.getKeyIdString(context.decryptdata); - this.log(`Generating key-session request for "${reason}": ${keyId} (init data type: ${initDataType} length: ${initData ? initData.byteLength : null})`); - const licenseStatus = new EventEmitter(); - const onmessage = context._onmessage = event => { - const keySession = context.mediaKeysSession; - if (!keySession) { - licenseStatus.emit('error', new Error('invalid state')); - return; - } - const { - messageType, - message - } = event; - this.log(`"${messageType}" message event for session "${keySession.sessionId}" message size: ${message.byteLength}`); - if (messageType === 'license-request' || messageType === 'license-renewal') { - this.renewLicense(context, message).catch(error => { - this.handleError(error); - licenseStatus.emit('error', error); - }); - } else if (messageType === 'license-release') { - if (context.keySystem === KeySystems.FAIRPLAY) { - this.updateKeySession(context, strToUtf8array('acknowledged')); - this.removeSession(context); - } - } else { - this.warn(`unhandled media key message type "${messageType}"`); - } - }; - const onkeystatuseschange = context._onkeystatuseschange = event => { - const keySession = context.mediaKeysSession; - if (!keySession) { - licenseStatus.emit('error', new Error('invalid state')); - return; - } - this.onKeyStatusChange(context); - const keyStatus = context.keyStatus; - licenseStatus.emit('keyStatus', keyStatus); - if (keyStatus === 'expired') { - this.warn(`${context.keySystem} expired for key ${keyId}`); - this.renewKeySession(context); - } - }; - context.mediaKeysSession.addEventListener('message', onmessage); - context.mediaKeysSession.addEventListener('keystatuseschange', onkeystatuseschange); - const keyUsablePromise = new Promise((resolve, reject) => { - licenseStatus.on('error', reject); - licenseStatus.on('keyStatus', keyStatus => { - if (keyStatus.startsWith('usable')) { - resolve(); - } else if (keyStatus === 'output-restricted') { - reject(new EMEKeyError({ - type: ErrorTypes.KEY_SYSTEM_ERROR, - details: ErrorDetails.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED, - fatal: false - }, 'HDCP level output restricted')); - } else if (keyStatus === 'internal-error') { - reject(new EMEKeyError({ - type: ErrorTypes.KEY_SYSTEM_ERROR, - details: ErrorDetails.KEY_SYSTEM_STATUS_INTERNAL_ERROR, - fatal: true - }, `key status changed to "${keyStatus}"`)); - } else if (keyStatus === 'expired') { - reject(new Error('key expired while generating request')); - } else { - this.warn(`unhandled key status change "${keyStatus}"`); - } - }); - }); - return context.mediaKeysSession.generateRequest(initDataType, initData).then(() => { - var _context$mediaKeysSes; - this.log(`Request generated for key-session "${(_context$mediaKeysSes = context.mediaKeysSession) == null ? void 0 : _context$mediaKeysSes.sessionId}" keyId: ${keyId}`); - }).catch(error => { - throw new EMEKeyError({ - type: ErrorTypes.KEY_SYSTEM_ERROR, - details: ErrorDetails.KEY_SYSTEM_NO_SESSION, - error, - fatal: false - }, `Error generating key-session request: ${error}`); - }).then(() => keyUsablePromise).catch(error => { - licenseStatus.removeAllListeners(); - this.removeSession(context); - throw error; - }).then(() => { - licenseStatus.removeAllListeners(); - return context; - }); - } - onKeyStatusChange(mediaKeySessionContext) { - mediaKeySessionContext.mediaKeysSession.keyStatuses.forEach((status, keyId) => { - this.log(`key status change "${status}" for keyStatuses keyId: ${Hex.hexDump('buffer' in keyId ? new Uint8Array(keyId.buffer, keyId.byteOffset, keyId.byteLength) : new Uint8Array(keyId))} session keyId: ${Hex.hexDump(new Uint8Array(mediaKeySessionContext.decryptdata.keyId || []))} uri: ${mediaKeySessionContext.decryptdata.uri}`); - mediaKeySessionContext.keyStatus = status; - }); - } - fetchServerCertificate(keySystem) { - const config = this.config; - const Loader = config.loader; - const certLoader = new Loader(config); - const url = this.getServerCertificateUrl(keySystem); - if (!url) { - return Promise.resolve(); - } - this.log(`Fetching server certificate for "${keySystem}"`); - return new Promise((resolve, reject) => { - const loaderContext = { - responseType: 'arraybuffer', - url - }; - const loadPolicy = config.certLoadPolicy.default; - const loaderConfig = { - loadPolicy, - timeout: loadPolicy.maxLoadTimeMs, - maxRetry: 0, - retryDelay: 0, - maxRetryDelay: 0 - }; - const loaderCallbacks = { - onSuccess: (response, stats, context, networkDetails) => { - resolve(response.data); - }, - onError: (response, contex, networkDetails, stats) => { - reject(new EMEKeyError({ - type: ErrorTypes.KEY_SYSTEM_ERROR, - details: ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED, - fatal: true, - networkDetails, - response: _objectSpread2({ - url: loaderContext.url, - data: undefined - }, response) - }, `"${keySystem}" certificate request failed (${url}). Status: ${response.code} (${response.text})`)); - }, - onTimeout: (stats, context, networkDetails) => { - reject(new EMEKeyError({ - type: ErrorTypes.KEY_SYSTEM_ERROR, - details: ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED, - fatal: true, - networkDetails, - response: { - url: loaderContext.url, - data: undefined - } - }, `"${keySystem}" certificate request timed out (${url})`)); - }, - onAbort: (stats, context, networkDetails) => { - reject(new Error('aborted')); - } - }; - certLoader.load(loaderContext, loaderConfig, loaderCallbacks); - }); - } - setMediaKeysServerCertificate(mediaKeys, keySystem, cert) { - return new Promise((resolve, reject) => { - mediaKeys.setServerCertificate(cert).then(success => { - this.log(`setServerCertificate ${success ? 'success' : 'not supported by CDM'} (${cert == null ? void 0 : cert.byteLength}) on "${keySystem}"`); - resolve(mediaKeys); - }).catch(error => { - reject(new EMEKeyError({ - type: ErrorTypes.KEY_SYSTEM_ERROR, - details: ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED, - error, - fatal: true - }, error.message)); - }); - }); - } - renewLicense(context, keyMessage) { - return this.requestLicense(context, new Uint8Array(keyMessage)).then(data => { - return this.updateKeySession(context, new Uint8Array(data)).catch(error => { - throw new EMEKeyError({ - type: ErrorTypes.KEY_SYSTEM_ERROR, - details: ErrorDetails.KEY_SYSTEM_SESSION_UPDATE_FAILED, - error, - fatal: true - }, error.message); - }); - }); - } - unpackPlayReadyKeyMessage(xhr, licenseChallenge) { - // On Edge, the raw license message is UTF-16-encoded XML. We need - // to unpack the Challenge element (base64-encoded string containing the - // actual license request) and any HttpHeader elements (sent as request - // headers). - // For PlayReady CDMs, we need to dig the Challenge out of the XML. - const xmlString = String.fromCharCode.apply(null, new Uint16Array(licenseChallenge.buffer)); - if (!xmlString.includes('PlayReadyKeyMessage')) { - // This does not appear to be a wrapped message as on Edge. Some - // clients do not need this unwrapping, so we will assume this is one of - // them. Note that "xml" at this point probably looks like random - // garbage, since we interpreted UTF-8 as UTF-16. - xhr.setRequestHeader('Content-Type', 'text/xml; charset=utf-8'); - return licenseChallenge; - } - const keyMessageXml = new DOMParser().parseFromString(xmlString, 'application/xml'); - // Set request headers. - const headers = keyMessageXml.querySelectorAll('HttpHeader'); - if (headers.length > 0) { - let header; - for (let i = 0, len = headers.length; i < len; i++) { - var _header$querySelector, _header$querySelector2; - header = headers[i]; - const name = (_header$querySelector = header.querySelector('name')) == null ? void 0 : _header$querySelector.textContent; - const value = (_header$querySelector2 = header.querySelector('value')) == null ? void 0 : _header$querySelector2.textContent; - if (name && value) { - xhr.setRequestHeader(name, value); - } - } - } - const challengeElement = keyMessageXml.querySelector('Challenge'); - const challengeText = challengeElement == null ? void 0 : challengeElement.textContent; - if (!challengeText) { - throw new Error(`Cannot find <Challenge> in key message`); - } - return strToUtf8array(atob(challengeText)); - } - setupLicenseXHR(xhr, url, keysListItem, licenseChallenge) { - const licenseXhrSetup = this.config.licenseXhrSetup; - if (!licenseXhrSetup) { - xhr.open('POST', url, true); - return Promise.resolve({ - xhr, - licenseChallenge - }); - } - return Promise.resolve().then(() => { - if (!keysListItem.decryptdata) { - throw new Error('Key removed'); - } - return licenseXhrSetup.call(this.hls, xhr, url, keysListItem, licenseChallenge); - }).catch(error => { - if (!keysListItem.decryptdata) { - // Key session removed. Cancel license request. - throw error; - } - // let's try to open before running setup - xhr.open('POST', url, true); - return licenseXhrSetup.call(this.hls, xhr, url, keysListItem, licenseChallenge); - }).then(licenseXhrSetupResult => { - // if licenseXhrSetup did not yet call open, let's do it now - if (!xhr.readyState) { - xhr.open('POST', url, true); - } - const finalLicenseChallenge = licenseXhrSetupResult ? licenseXhrSetupResult : licenseChallenge; - return { - xhr, - licenseChallenge: finalLicenseChallenge - }; - }); - } - requestLicense(keySessionContext, licenseChallenge) { - const keyLoadPolicy = this.config.keyLoadPolicy.default; - return new Promise((resolve, reject) => { - const url = this.getLicenseServerUrl(keySessionContext.keySystem); - this.log(`Sending license request to URL: ${url}`); - const xhr = new XMLHttpRequest(); - xhr.responseType = 'arraybuffer'; - xhr.onreadystatechange = () => { - if (!this.hls || !keySessionContext.mediaKeysSession) { - return reject(new Error('invalid state')); - } - if (xhr.readyState === 4) { - if (xhr.status === 200) { - this._requestLicenseFailureCount = 0; - let data = xhr.response; - this.log(`License received ${data instanceof ArrayBuffer ? data.byteLength : data}`); - const licenseResponseCallback = this.config.licenseResponseCallback; - if (licenseResponseCallback) { - try { - data = licenseResponseCallback.call(this.hls, xhr, url, keySessionContext); - } catch (error) { - this.error(error); - } - } - resolve(data); - } else { - const retryConfig = keyLoadPolicy.errorRetry; - const maxNumRetry = retryConfig ? retryConfig.maxNumRetry : 0; - this._requestLicenseFailureCount++; - if (this._requestLicenseFailureCount > maxNumRetry || xhr.status >= 400 && xhr.status < 500) { - reject(new EMEKeyError({ - type: ErrorTypes.KEY_SYSTEM_ERROR, - details: ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED, - fatal: true, - networkDetails: xhr, - response: { - url, - data: undefined, - code: xhr.status, - text: xhr.statusText - } - }, `License Request XHR failed (${url}). Status: ${xhr.status} (${xhr.statusText})`)); - } else { - const attemptsLeft = maxNumRetry - this._requestLicenseFailureCount + 1; - this.warn(`Retrying license request, ${attemptsLeft} attempts left`); - this.requestLicense(keySessionContext, licenseChallenge).then(resolve, reject); - } - } - } - }; - if (keySessionContext.licenseXhr && keySessionContext.licenseXhr.readyState !== XMLHttpRequest.DONE) { - keySessionContext.licenseXhr.abort(); - } - keySessionContext.licenseXhr = xhr; - this.setupLicenseXHR(xhr, url, keySessionContext, licenseChallenge).then(({ - xhr, - licenseChallenge - }) => { - if (keySessionContext.keySystem == KeySystems.PLAYREADY) { - licenseChallenge = this.unpackPlayReadyKeyMessage(xhr, licenseChallenge); - } - xhr.send(licenseChallenge); - }); - }); - } - onMediaAttached(event, data) { - if (!this.config.emeEnabled) { - return; - } - const media = data.media; - - // keep reference of media - this.media = media; - media.addEventListener('encrypted', this.onMediaEncrypted); - media.addEventListener('waitingforkey', this.onWaitingForKey); - } - onMediaDetached() { - const media = this.media; - const mediaKeysList = this.mediaKeySessions; - if (media) { - media.removeEventListener('encrypted', this.onMediaEncrypted); - media.removeEventListener('waitingforkey', this.onWaitingForKey); - this.media = null; - } - this._requestLicenseFailureCount = 0; - this.setMediaKeysQueue = []; - this.mediaKeySessions = []; - this.keyIdToKeySessionPromise = {}; - LevelKey.clearKeyUriToKeyIdMap(); - - // Close all sessions and remove media keys from the video element. - const keySessionCount = mediaKeysList.length; - EMEController.CDMCleanupPromise = Promise.all(mediaKeysList.map(mediaKeySessionContext => this.removeSession(mediaKeySessionContext)).concat(media == null ? void 0 : media.setMediaKeys(null).catch(error => { - this.log(`Could not clear media keys: ${error}`); - }))).then(() => { - if (keySessionCount) { - this.log('finished closing key sessions and clearing media keys'); - mediaKeysList.length = 0; - } - }).catch(error => { - this.log(`Could not close sessions and clear media keys: ${error}`); - }); - } - onManifestLoading() { - this.keyFormatPromise = null; - } - onManifestLoaded(event, { - sessionKeys - }) { - if (!sessionKeys || !this.config.emeEnabled) { - return; - } - if (!this.keyFormatPromise) { - const keyFormats = sessionKeys.reduce((formats, sessionKey) => { - if (formats.indexOf(sessionKey.keyFormat) === -1) { - formats.push(sessionKey.keyFormat); - } - return formats; - }, []); - this.log(`Selecting key-system from session-keys ${keyFormats.join(', ')}`); - this.keyFormatPromise = this.getKeyFormatPromise(keyFormats); - } - } - removeSession(mediaKeySessionContext) { - const { - mediaKeysSession, - licenseXhr - } = mediaKeySessionContext; - if (mediaKeysSession) { - this.log(`Remove licenses and keys and close session ${mediaKeysSession.sessionId}`); - if (mediaKeySessionContext._onmessage) { - mediaKeysSession.removeEventListener('message', mediaKeySessionContext._onmessage); - mediaKeySessionContext._onmessage = undefined; - } - if (mediaKeySessionContext._onkeystatuseschange) { - mediaKeysSession.removeEventListener('keystatuseschange', mediaKeySessionContext._onkeystatuseschange); - mediaKeySessionContext._onkeystatuseschange = undefined; - } - if (licenseXhr && licenseXhr.readyState !== XMLHttpRequest.DONE) { - licenseXhr.abort(); - } - mediaKeySessionContext.mediaKeysSession = mediaKeySessionContext.decryptdata = mediaKeySessionContext.licenseXhr = undefined; - const index = this.mediaKeySessions.indexOf(mediaKeySessionContext); - if (index > -1) { - this.mediaKeySessions.splice(index, 1); - } - return mediaKeysSession.remove().catch(error => { - this.log(`Could not remove session: ${error}`); - }).then(() => { - return mediaKeysSession.close(); - }).catch(error => { - this.log(`Could not close session: ${error}`); - }); - } - } -} -EMEController.CDMCleanupPromise = void 0; -class EMEKeyError extends Error { - constructor(data, message) { - super(message); - this.data = void 0; - data.error || (data.error = new Error(message)); - this.data = data; - data.err = data.error; - } -} - -/** - * Common Media Object Type - * - * @group CMCD - * @group CMSD - * - * @beta - */ -var CmObjectType; -(function (CmObjectType) { - /** - * text file, such as a manifest or playlist - */ - CmObjectType["MANIFEST"] = "m"; - /** - * audio only - */ - CmObjectType["AUDIO"] = "a"; - /** - * video only - */ - CmObjectType["VIDEO"] = "v"; - /** - * muxed audio and video - */ - CmObjectType["MUXED"] = "av"; - /** - * init segment - */ - CmObjectType["INIT"] = "i"; - /** - * caption or subtitle - */ - CmObjectType["CAPTION"] = "c"; - /** - * ISOBMFF timed text track - */ - CmObjectType["TIMED_TEXT"] = "tt"; - /** - * cryptographic key, license or certificate. - */ - CmObjectType["KEY"] = "k"; - /** - * other - */ - CmObjectType["OTHER"] = "o"; -})(CmObjectType || (CmObjectType = {})); - -/** - * Common Media Streaming Format - * - * @group CMCD - * @group CMSD - * - * @beta - */ -var CmStreamingFormat; -(function (CmStreamingFormat) { - /** - * MPEG DASH - */ - CmStreamingFormat["DASH"] = "d"; - /** - * HTTP Live Streaming (HLS) - */ - CmStreamingFormat["HLS"] = "h"; - /** - * Smooth Streaming - */ - CmStreamingFormat["SMOOTH"] = "s"; - /** - * Other - */ - CmStreamingFormat["OTHER"] = "o"; -})(CmStreamingFormat || (CmStreamingFormat = {})); - -/** - * CMCD header fields. - * - * @group CMCD - * - * @beta - */ -var CmcdHeaderField; -(function (CmcdHeaderField) { - /** - * keys whose values vary with the object being requested. - */ - CmcdHeaderField["OBJECT"] = "CMCD-Object"; - /** - * keys whose values vary with each request. - */ - CmcdHeaderField["REQUEST"] = "CMCD-Request"; - /** - * keys whose values are expected to be invariant over the life of the session. - */ - CmcdHeaderField["SESSION"] = "CMCD-Session"; - /** - * keys whose values do not vary with every request or object. - */ - CmcdHeaderField["STATUS"] = "CMCD-Status"; -})(CmcdHeaderField || (CmcdHeaderField = {})); - -/** - * The map of CMCD header fields to official CMCD keys. - * - * @internal - * - * @group CMCD - */ -const CmcdHeaderMap = { - [CmcdHeaderField.OBJECT]: ['br', 'd', 'ot', 'tb'], - [CmcdHeaderField.REQUEST]: ['bl', 'dl', 'mtp', 'nor', 'nrr', 'su'], - [CmcdHeaderField.SESSION]: ['cid', 'pr', 'sf', 'sid', 'st', 'v'], - [CmcdHeaderField.STATUS]: ['bs', 'rtp'] -}; - -/** - * Structured Field Item - * - * @group Structured Field - * - * @beta - */ -class SfItem { - constructor(value, params) { - this.value = void 0; - this.params = void 0; - if (Array.isArray(value)) { - value = value.map(v => v instanceof SfItem ? v : new SfItem(v)); - } - this.value = value; - this.params = params; - } -} - -/** - * A class to represent structured field tokens when `Symbol` is not available. - * - * @group Structured Field - * - * @beta - */ -class SfToken { - constructor(description) { - this.description = void 0; - this.description = description; - } -} - -const DICT = 'Dict'; - -function format(value) { - if (Array.isArray(value)) { - return JSON.stringify(value); - } - if (value instanceof Map) { - return 'Map{}'; - } - if (value instanceof Set) { - return 'Set{}'; - } - if (typeof value === 'object') { - return JSON.stringify(value); - } - return String(value); -} -function throwError(action, src, type, cause) { - return new Error(`failed to ${action} "${format(src)}" as ${type}`, { - cause - }); -} - -const BARE_ITEM = 'Bare Item'; - -const BOOLEAN = 'Boolean'; - -const BYTES = 'Byte Sequence'; - -const DECIMAL = 'Decimal'; - -const INTEGER = 'Integer'; - -function isInvalidInt(value) { - return value < -999999999999999 || 999999999999999 < value; -} - -const STRING_REGEX = /[\x00-\x1f\x7f]+/; // eslint-disable-line no-control-regex - -const TOKEN = 'Token'; - -const KEY = 'Key'; - -function serializeError(src, type, cause) { - return throwError('serialize', src, type, cause); -} - -// 4.1.9. Serializing a Boolean -// -// Given a Boolean as input_boolean, return an ASCII string suitable for -// use in a HTTP field value. -// -// 1. If input_boolean is not a boolean, fail serialization. -// -// 2. Let output be an empty string. -// -// 3. Append "?" to output. -// -// 4. If input_boolean is true, append "1" to output. -// -// 5. If input_boolean is false, append "0" to output. -// -// 6. Return output. -function serializeBoolean(value) { - if (typeof value !== 'boolean') { - throw serializeError(value, BOOLEAN); - } - return value ? '?1' : '?0'; -} - -/** - * Encodes binary data to base64 - * - * @param binary - The binary data to encode - * @returns The base64 encoded string - * - * @group Utils - * - * @beta - */ -function base64encode(binary) { - return btoa(String.fromCharCode(...binary)); -} - -// 4.1.8. Serializing a Byte Sequence -// -// Given a Byte Sequence as input_bytes, return an ASCII string suitable -// for use in a HTTP field value. -// -// 1. If input_bytes is not a sequence of bytes, fail serialization. -// -// 2. Let output be an empty string. -// -// 3. Append ":" to output. -// -// 4. Append the result of base64-encoding input_bytes as per -// [RFC4648], Section 4, taking account of the requirements below. -// -// 5. Append ":" to output. -// -// 6. Return output. -// -// The encoded data is required to be padded with "=", as per [RFC4648], -// Section 3.2. -// -// Likewise, encoded data SHOULD have pad bits set to zero, as per -// [RFC4648], Section 3.5, unless it is not possible to do so due to -// implementation constraints. -function serializeByteSequence(value) { - if (ArrayBuffer.isView(value) === false) { - throw serializeError(value, BYTES); - } - return `:${base64encode(value)}:`; -} - -// 4.1.4. Serializing an Integer -// -// Given an Integer as input_integer, return an ASCII string suitable -// for use in a HTTP field value. -// -// 1. If input_integer is not an integer in the range of -// -999,999,999,999,999 to 999,999,999,999,999 inclusive, fail -// serialization. -// -// 2. Let output be an empty string. -// -// 3. If input_integer is less than (but not equal to) 0, append "-" to -// output. -// -// 4. Append input_integer's numeric value represented in base 10 using -// only decimal digits to output. -// -// 5. Return output. -function serializeInteger(value) { - if (isInvalidInt(value)) { - throw serializeError(value, INTEGER); - } - return value.toString(); -} - -// 4.1.10. Serializing a Date -// -// Given a Date as input_integer, return an ASCII string suitable for -// use in an HTTP field value. -// 1. Let output be "@". -// 2. Append to output the result of running Serializing an Integer -// with input_date (Section 4.1.4). -// 3. Return output. -function serializeDate(value) { - return `@${serializeInteger(value.getTime() / 1000)}`; -} - -/** - * This implements the rounding procedure described in step 2 of the "Serializing a Decimal" specification. - * This rounding style is known as "even rounding", "banker's rounding", or "commercial rounding". - * - * @param value - The value to round - * @param precision - The number of decimal places to round to - * @returns The rounded value - * - * @group Utils - * - * @beta - */ -function roundToEven(value, precision) { - if (value < 0) { - return -roundToEven(-value, precision); - } - const decimalShift = Math.pow(10, precision); - const isEquidistant = Math.abs(value * decimalShift % 1 - 0.5) < Number.EPSILON; - if (isEquidistant) { - // If the tail of the decimal place is 'equidistant' we round to the nearest even value - const flooredValue = Math.floor(value * decimalShift); - return (flooredValue % 2 === 0 ? flooredValue : flooredValue + 1) / decimalShift; - } else { - // Otherwise, proceed as normal - return Math.round(value * decimalShift) / decimalShift; - } -} - -// 4.1.5. Serializing a Decimal -// -// Given a decimal number as input_decimal, return an ASCII string -// suitable for use in a HTTP field value. -// -// 1. If input_decimal is not a decimal number, fail serialization. -// -// 2. If input_decimal has more than three significant digits to the -// right of the decimal point, round it to three decimal places, -// rounding the final digit to the nearest value, or to the even -// value if it is equidistant. -// -// 3. If input_decimal has more than 12 significant digits to the left -// of the decimal point after rounding, fail serialization. -// -// 4. Let output be an empty string. -// -// 5. If input_decimal is less than (but not equal to) 0, append "-" -// to output. -// -// 6. Append input_decimal's integer component represented in base 10 -// (using only decimal digits) to output; if it is zero, append -// "0". -// -// 7. Append "." to output. -// -// 8. If input_decimal's fractional component is zero, append "0" to -// output. -// -// 9. Otherwise, append the significant digits of input_decimal's -// fractional component represented in base 10 (using only decimal -// digits) to output. -// -// 10. Return output. -function serializeDecimal(value) { - const roundedValue = roundToEven(value, 3); // round to 3 decimal places - if (Math.floor(Math.abs(roundedValue)).toString().length > 12) { - throw serializeError(value, DECIMAL); - } - const stringValue = roundedValue.toString(); - return stringValue.includes('.') ? stringValue : `${stringValue}.0`; -} - -const STRING = 'String'; - -// 4.1.6. Serializing a String -// -// Given a String as input_string, return an ASCII string suitable for -// use in a HTTP field value. -// -// 1. Convert input_string into a sequence of ASCII characters; if -// conversion fails, fail serialization. -// -// 2. If input_string contains characters in the range %x00-1f or %x7f -// (i.e., not in VCHAR or SP), fail serialization. -// -// 3. Let output be the string DQUOTE. -// -// 4. For each character char in input_string: -// -// 1. If char is "\" or DQUOTE: -// -// 1. Append "\" to output. -// -// 2. Append char to output. -// -// 5. Append DQUOTE to output. -// -// 6. Return output. -function serializeString(value) { - if (STRING_REGEX.test(value)) { - throw serializeError(value, STRING); - } - return `"${value.replace(/\\/g, `\\\\`).replace(/"/g, `\\"`)}"`; -} - -function symbolToStr(symbol) { - return symbol.description || symbol.toString().slice(7, -1); -} - -function serializeToken(token) { - const value = symbolToStr(token); - if (/^([a-zA-Z*])([!#$%&'*+\-.^_`|~\w:/]*)$/.test(value) === false) { - throw serializeError(value, TOKEN); - } - return value; -} - -// 4.1.3.1. Serializing a Bare Item -// -// Given an Item as input_item, return an ASCII string suitable for use -// in a HTTP field value. -// -// 1. If input_item is an Integer, return the result of running -// Serializing an Integer (Section 4.1.4) with input_item. -// -// 2. If input_item is a Decimal, return the result of running -// Serializing a Decimal (Section 4.1.5) with input_item. -// -// 3. If input_item is a String, return the result of running -// Serializing a String (Section 4.1.6) with input_item. -// -// 4. If input_item is a Token, return the result of running -// Serializing a Token (Section 4.1.7) with input_item. -// -// 5. If input_item is a Boolean, return the result of running -// Serializing a Boolean (Section 4.1.9) with input_item. -// -// 6. If input_item is a Byte Sequence, return the result of running -// Serializing a Byte Sequence (Section 4.1.8) with input_item. -// -// 7. If input_item is a Date, return the result of running Serializing -// a Date (Section 4.1.10) with input_item. -// -// 8. Otherwise, fail serialization. -function serializeBareItem(value) { - switch (typeof value) { - case 'number': - if (!isFiniteNumber(value)) { - throw serializeError(value, BARE_ITEM); - } - if (Number.isInteger(value)) { - return serializeInteger(value); - } - return serializeDecimal(value); - case 'string': - return serializeString(value); - case 'symbol': - return serializeToken(value); - case 'boolean': - return serializeBoolean(value); - case 'object': - if (value instanceof Date) { - return serializeDate(value); - } - if (value instanceof Uint8Array) { - return serializeByteSequence(value); - } - if (value instanceof SfToken) { - return serializeToken(value); - } - default: - // fail - throw serializeError(value, BARE_ITEM); - } -} - -// 4.1.1.3. Serializing a Key -// -// Given a key as input_key, return an ASCII string suitable for use in -// a HTTP field value. -// -// 1. Convert input_key into a sequence of ASCII characters; if -// conversion fails, fail serialization. -// -// 2. If input_key contains characters not in lcalpha, DIGIT, "_", "-", -// ".", or "*" fail serialization. -// -// 3. If the first character of input_key is not lcalpha or "*", fail -// serialization. -// -// 4. Let output be an empty string. -// -// 5. Append input_key to output. -// -// 6. Return output. -function serializeKey(value) { - if (/^[a-z*][a-z0-9\-_.*]*$/.test(value) === false) { - throw serializeError(value, KEY); - } - return value; -} - -// 4.1.1.2. Serializing Parameters -// -// Given an ordered Dictionary as input_parameters (each member having a -// param_name and a param_value), return an ASCII string suitable for -// use in a HTTP field value. -// -// 1. Let output be an empty string. -// -// 2. For each param_name with a value of param_value in -// input_parameters: -// -// 1. Append ";" to output. -// -// 2. Append the result of running Serializing a Key -// (Section 4.1.1.3) with param_name to output. -// -// 3. If param_value is not Boolean true: -// -// 1. Append "=" to output. -// -// 2. Append the result of running Serializing a bare Item -// (Section 4.1.3.1) with param_value to output. -// -// 3. Return output. -function serializeParams(params) { - if (params == null) { - return ''; - } - return Object.entries(params).map(([key, value]) => { - if (value === true) { - return `;${serializeKey(key)}`; // omit true - } - return `;${serializeKey(key)}=${serializeBareItem(value)}`; - }).join(''); -} - -// 4.1.3. Serializing an Item -// -// Given an Item as bare_item and Parameters as item_parameters, return -// an ASCII string suitable for use in a HTTP field value. -// -// 1. Let output be an empty string. -// -// 2. Append the result of running Serializing a Bare Item -// Section 4.1.3.1 with bare_item to output. -// -// 3. Append the result of running Serializing Parameters -// Section 4.1.1.2 with item_parameters to output. -// -// 4. Return output. -function serializeItem(value) { - if (value instanceof SfItem) { - return `${serializeBareItem(value.value)}${serializeParams(value.params)}`; - } else { - return serializeBareItem(value); - } -} - -// 4.1.1.1. Serializing an Inner List -// -// Given an array of (member_value, parameters) tuples as inner_list, -// and parameters as list_parameters, return an ASCII string suitable -// for use in a HTTP field value. -// -// 1. Let output be the string "(". -// -// 2. For each (member_value, parameters) of inner_list: -// -// 1. Append the result of running Serializing an Item -// (Section 4.1.3) with (member_value, parameters) to output. -// -// 2. If more values remain in inner_list, append a single SP to -// output. -// -// 3. Append ")" to output. -// -// 4. Append the result of running Serializing Parameters -// (Section 4.1.1.2) with list_parameters to output. -// -// 5. Return output. -function serializeInnerList(value) { - return `(${value.value.map(serializeItem).join(' ')})${serializeParams(value.params)}`; -} - -// 4.1.2. Serializing a Dictionary -// -// Given an ordered Dictionary as input_dictionary (each member having a -// member_name and a tuple value of (member_value, parameters)), return -// an ASCII string suitable for use in a HTTP field value. -// -// 1. Let output be an empty string. -// -// 2. For each member_name with a value of (member_value, parameters) -// in input_dictionary: -// -// 1. Append the result of running Serializing a Key -// (Section 4.1.1.3) with member's member_name to output. -// -// 2. If member_value is Boolean true: -// -// 1. Append the result of running Serializing Parameters -// (Section 4.1.1.2) with parameters to output. -// -// 3. Otherwise: -// -// 1. Append "=" to output. -// -// 2. If member_value is an array, append the result of running -// Serializing an Inner List (Section 4.1.1.1) with -// (member_value, parameters) to output. -// -// 3. Otherwise, append the result of running Serializing an -// Item (Section 4.1.3) with (member_value, parameters) to -// output. -// -// 4. If more members remain in input_dictionary: -// -// 1. Append "," to output. -// -// 2. Append a single SP to output. -// -// 3. Return output. -function serializeDict(dict, options = { - whitespace: true -}) { - if (typeof dict !== 'object') { - throw serializeError(dict, DICT); - } - const entries = dict instanceof Map ? dict.entries() : Object.entries(dict); - const optionalWhiteSpace = options != null && options.whitespace ? ' ' : ''; - return Array.from(entries).map(([key, item]) => { - if (item instanceof SfItem === false) { - item = new SfItem(item); - } - let output = serializeKey(key); - if (item.value === true) { - output += serializeParams(item.params); - } else { - output += '='; - if (Array.isArray(item.value)) { - output += serializeInnerList(item); - } else { - output += serializeItem(item); - } - } - return output; - }).join(`,${optionalWhiteSpace}`); -} - -/** - * Encode an object into a structured field dictionary - * - * @param input - The structured field dictionary to encode - * @returns The structured field string - * - * @group Structured Field - * - * @beta - */ -function encodeSfDict(value, options) { - return serializeDict(value, options); -} - -/** - * Checks if the given key is a token field. - * - * @param key - The key to check. - * - * @returns `true` if the key is a token field. - * - * @internal - * - * @group CMCD - */ -const isTokenField = key => key === 'ot' || key === 'sf' || key === 'st'; - -const isValid = value => { - if (typeof value === 'number') { - return isFiniteNumber(value); - } - return value != null && value !== '' && value !== false; -}; - -/** - * Constructs a relative path from a URL. - * - * @param url - The destination URL - * @param base - The base URL - * @returns The relative path - * - * @group Utils - * - * @beta - */ -function urlToRelativePath(url, base) { - const to = new URL(url); - const from = new URL(base); - if (to.origin !== from.origin) { - return url; - } - const toPath = to.pathname.split('/').slice(1); - const fromPath = from.pathname.split('/').slice(1, -1); - // remove common parents - while (toPath[0] === fromPath[0]) { - toPath.shift(); - fromPath.shift(); - } - // add back paths - while (fromPath.length) { - fromPath.shift(); - toPath.unshift('..'); - } - return toPath.join('/'); -} - -/** - * Generate a random v4 UUID - * - * @returns A random v4 UUID - * - * @group Utils - * - * @beta - */ -function uuid() { - try { - return crypto.randomUUID(); - } catch (error) { - try { - const url = URL.createObjectURL(new Blob()); - const uuid = url.toString(); - URL.revokeObjectURL(url); - return uuid.slice(uuid.lastIndexOf('/') + 1); - } catch (error) { - let dt = new Date().getTime(); - const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { - const r = (dt + Math.random() * 16) % 16 | 0; - dt = Math.floor(dt / 16); - return (c == 'x' ? r : r & 0x3 | 0x8).toString(16); - }); - return uuid; - } - } -} - -const toRounded = value => Math.round(value); -const toUrlSafe = (value, options) => { - if (options != null && options.baseUrl) { - value = urlToRelativePath(value, options.baseUrl); - } - return encodeURIComponent(value); -}; -const toHundred = value => toRounded(value / 100) * 100; -/** - * The default formatters for CMCD values. - * - * @group CMCD - * - * @beta - */ -const CmcdFormatters = { - /** - * Bitrate (kbps) rounded integer - */ - br: toRounded, - /** - * Duration (milliseconds) rounded integer - */ - d: toRounded, - /** - * Buffer Length (milliseconds) rounded nearest 100ms - */ - bl: toHundred, - /** - * Deadline (milliseconds) rounded nearest 100ms - */ - dl: toHundred, - /** - * Measured Throughput (kbps) rounded nearest 100kbps - */ - mtp: toHundred, - /** - * Next Object Request URL encoded - */ - nor: toUrlSafe, - /** - * Requested maximum throughput (kbps) rounded nearest 100kbps - */ - rtp: toHundred, - /** - * Top Bitrate (kbps) rounded integer - */ - tb: toRounded -}; - -/** - * Internal CMCD processing function. - * - * @param obj - The CMCD object to process. - * @param map - The mapping function to use. - * @param options - Options for encoding. - * - * @internal - * - * @group CMCD - */ -function processCmcd(obj, options) { - const results = {}; - if (obj == null || typeof obj !== 'object') { - return results; - } - const keys = Object.keys(obj).sort(); - const formatters = _extends({}, CmcdFormatters, options == null ? void 0 : options.formatters); - const filter = options == null ? void 0 : options.filter; - keys.forEach(key => { - if (filter != null && filter(key)) { - return; - } - let value = obj[key]; - const formatter = formatters[key]; - if (formatter) { - value = formatter(value, options); - } - // Version should only be reported if not equal to 1. - if (key === 'v' && value === 1) { - return; - } - // Playback rate should only be sent if not equal to 1. - if (key == 'pr' && value === 1) { - return; - } - // ignore invalid values - if (!isValid(value)) { - return; - } - if (isTokenField(key) && typeof value === 'string') { - value = new SfToken(value); - } - results[key] = value; - }); - return results; -} - -/** - * Encode a CMCD object to a string. - * - * @param cmcd - The CMCD object to encode. - * @param options - Options for encoding. - * - * @returns The encoded CMCD string. - * - * @group CMCD - * - * @beta - */ -function encodeCmcd(cmcd, options = {}) { - if (!cmcd) { - return ''; - } - return encodeSfDict(processCmcd(cmcd, options), _extends({ - whitespace: false - }, options)); -} - -/** - * Convert a CMCD data object to request headers - * - * @param cmcd - The CMCD data object to convert. - * @param options - Options for encoding the CMCD object. - * - * @returns The CMCD header shards. - * - * @group CMCD - * - * @beta - */ -function toCmcdHeaders(cmcd, options = {}) { - if (!cmcd) { - return {}; - } - const entries = Object.entries(cmcd); - const headerMap = Object.entries(CmcdHeaderMap).concat(Object.entries((options == null ? void 0 : options.customHeaderMap) || {})); - const shards = entries.reduce((acc, entry) => { - var _headerMap$find, _acc$field; - const [key, value] = entry; - const field = ((_headerMap$find = headerMap.find(entry => entry[1].includes(key))) == null ? void 0 : _headerMap$find[0]) || CmcdHeaderField.REQUEST; - (_acc$field = acc[field]) != null ? _acc$field : acc[field] = {}; - acc[field][key] = value; - return acc; - }, {}); - return Object.entries(shards).reduce((acc, [field, value]) => { - acc[field] = encodeCmcd(value, options); - return acc; - }, {}); -} - -/** - * Append CMCD query args to a header object. - * - * @param headers - The headers to append to. - * @param cmcd - The CMCD object to append. - * @param customHeaderMap - A map of custom CMCD keys to header fields. - * - * @returns The headers with the CMCD header shards appended. - * - * @group CMCD - * - * @beta - */ -function appendCmcdHeaders(headers, cmcd, options) { - return _extends(headers, toCmcdHeaders(cmcd, options)); -} - -/** - * CMCD parameter name. - * - * @group CMCD - * - * @beta - */ -const CMCD_PARAM = 'CMCD'; - -/** - * Convert a CMCD data object to a query arg. - * - * @param cmcd - The CMCD object to convert. - * @param options - Options for encoding the CMCD object. - * - * @returns The CMCD query arg. - * - * @group CMCD - * - * @beta - */ -function toCmcdQuery(cmcd, options = {}) { - if (!cmcd) { - return ''; - } - const params = encodeCmcd(cmcd, options); - return `${CMCD_PARAM}=${encodeURIComponent(params)}`; -} - -const REGEX = /CMCD=[^&#]+/; -/** - * Append CMCD query args to a URL. - * - * @param url - The URL to append to. - * @param cmcd - The CMCD object to append. - * @param options - Options for encoding the CMCD object. - * - * @returns The URL with the CMCD query args appended. - * - * @group CMCD - * - * @beta - */ -function appendCmcdQuery(url, cmcd, options) { - // TODO: Replace with URLSearchParams once we drop Safari < 10.1 & Chrome < 49 support. - // https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams - const query = toCmcdQuery(cmcd, options); - if (!query) { - return url; - } - if (REGEX.test(url)) { - return url.replace(REGEX, query); - } - const separator = url.includes('?') ? '&' : '?'; - return `${url}${separator}${query}`; -} - -/** - * Controller to deal with Common Media Client Data (CMCD) - * @see https://cdn.cta.tech/cta/media/media/resources/standards/pdfs/cta-5004-final.pdf - */ -class CMCDController { - // eslint-disable-line no-restricted-globals - - constructor(hls) { - this.hls = void 0; - this.config = void 0; - this.media = void 0; - this.sid = void 0; - this.cid = void 0; - this.useHeaders = false; - this.includeKeys = void 0; - this.initialized = false; - this.starved = false; - this.buffering = true; - this.audioBuffer = void 0; - // eslint-disable-line no-restricted-globals - this.videoBuffer = void 0; - this.onWaiting = () => { - if (this.initialized) { - this.starved = true; - } - this.buffering = true; - }; - this.onPlaying = () => { - if (!this.initialized) { - this.initialized = true; - } - this.buffering = false; - }; - /** - * Apply CMCD data to a manifest request. - */ - this.applyPlaylistData = context => { - try { - this.apply(context, { - ot: CmObjectType.MANIFEST, - su: !this.initialized - }); - } catch (error) { - logger.warn('Could not generate manifest CMCD data.', error); - } - }; - /** - * Apply CMCD data to a segment request - */ - this.applyFragmentData = context => { - try { - const fragment = context.frag; - const level = this.hls.levels[fragment.level]; - const ot = this.getObjectType(fragment); - const data = { - d: fragment.duration * 1000, - ot - }; - if (ot === CmObjectType.VIDEO || ot === CmObjectType.AUDIO || ot == CmObjectType.MUXED) { - data.br = level.bitrate / 1000; - data.tb = this.getTopBandwidth(ot) / 1000; - data.bl = this.getBufferLength(ot); - } - this.apply(context, data); - } catch (error) { - logger.warn('Could not generate segment CMCD data.', error); - } - }; - this.hls = hls; - const config = this.config = hls.config; - const { - cmcd - } = config; - if (cmcd != null) { - config.pLoader = this.createPlaylistLoader(); - config.fLoader = this.createFragmentLoader(); - this.sid = cmcd.sessionId || uuid(); - this.cid = cmcd.contentId; - this.useHeaders = cmcd.useHeaders === true; - this.includeKeys = cmcd.includeKeys; - this.registerListeners(); - } - } - registerListeners() { - const hls = this.hls; - hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - hls.on(Events.MEDIA_DETACHED, this.onMediaDetached, this); - hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this); - } - unregisterListeners() { - const hls = this.hls; - hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - hls.off(Events.MEDIA_DETACHED, this.onMediaDetached, this); - hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this); - } - destroy() { - this.unregisterListeners(); - this.onMediaDetached(); - - // @ts-ignore - this.hls = this.config = this.audioBuffer = this.videoBuffer = null; - // @ts-ignore - this.onWaiting = this.onPlaying = null; - } - onMediaAttached(event, data) { - this.media = data.media; - this.media.addEventListener('waiting', this.onWaiting); - this.media.addEventListener('playing', this.onPlaying); - } - onMediaDetached() { - if (!this.media) { - return; - } - this.media.removeEventListener('waiting', this.onWaiting); - this.media.removeEventListener('playing', this.onPlaying); - - // @ts-ignore - this.media = null; - } - onBufferCreated(event, data) { - var _data$tracks$audio, _data$tracks$video; - this.audioBuffer = (_data$tracks$audio = data.tracks.audio) == null ? void 0 : _data$tracks$audio.buffer; - this.videoBuffer = (_data$tracks$video = data.tracks.video) == null ? void 0 : _data$tracks$video.buffer; - } - /** - * Create baseline CMCD data - */ - createData() { - var _this$media; - return { - v: 1, - sf: CmStreamingFormat.HLS, - sid: this.sid, - cid: this.cid, - pr: (_this$media = this.media) == null ? void 0 : _this$media.playbackRate, - mtp: this.hls.bandwidthEstimate / 1000 - }; - } - - /** - * Apply CMCD data to a request. - */ - apply(context, data = {}) { - // apply baseline data - _extends(data, this.createData()); - const isVideo = data.ot === CmObjectType.INIT || data.ot === CmObjectType.VIDEO || data.ot === CmObjectType.MUXED; - if (this.starved && isVideo) { - data.bs = true; - data.su = true; - this.starved = false; - } - if (data.su == null) { - data.su = this.buffering; - } - - // TODO: Implement rtp, nrr, nor, dl - - const { - includeKeys - } = this; - if (includeKeys) { - data = Object.keys(data).reduce((acc, key) => { - includeKeys.includes(key) && (acc[key] = data[key]); - return acc; - }, {}); - } - if (this.useHeaders) { - if (!context.headers) { - context.headers = {}; - } - appendCmcdHeaders(context.headers, data); - } else { - context.url = appendCmcdQuery(context.url, data); - } - } - /** - * The CMCD object type. - */ - getObjectType(fragment) { - const { - type - } = fragment; - if (type === 'subtitle') { - return CmObjectType.TIMED_TEXT; - } - if (fragment.sn === 'initSegment') { - return CmObjectType.INIT; - } - if (type === 'audio') { - return CmObjectType.AUDIO; - } - if (type === 'main') { - if (!this.hls.audioTracks.length) { - return CmObjectType.MUXED; - } - return CmObjectType.VIDEO; - } - return undefined; - } - - /** - * Get the highest bitrate. - */ - getTopBandwidth(type) { - let bitrate = 0; - let levels; - const hls = this.hls; - if (type === CmObjectType.AUDIO) { - levels = hls.audioTracks; - } else { - const max = hls.maxAutoLevel; - const len = max > -1 ? max + 1 : hls.levels.length; - levels = hls.levels.slice(0, len); - } - for (const level of levels) { - if (level.bitrate > bitrate) { - bitrate = level.bitrate; - } - } - return bitrate > 0 ? bitrate : NaN; - } - - /** - * Get the buffer length for a media type in milliseconds - */ - getBufferLength(type) { - const media = this.hls.media; - const buffer = type === CmObjectType.AUDIO ? this.audioBuffer : this.videoBuffer; - if (!buffer || !media) { - return NaN; - } - const info = BufferHelper.bufferInfo(buffer, media.currentTime, this.config.maxBufferHole); - return info.len * 1000; - } - - /** - * Create a playlist loader - */ - createPlaylistLoader() { - const { - pLoader - } = this.config; - const apply = this.applyPlaylistData; - const Ctor = pLoader || this.config.loader; - return class CmcdPlaylistLoader { - constructor(config) { - this.loader = void 0; - this.loader = new Ctor(config); - } - get stats() { - return this.loader.stats; - } - get context() { - return this.loader.context; - } - destroy() { - this.loader.destroy(); - } - abort() { - this.loader.abort(); - } - load(context, config, callbacks) { - apply(context); - this.loader.load(context, config, callbacks); - } - }; - } - - /** - * Create a playlist loader - */ - createFragmentLoader() { - const { - fLoader - } = this.config; - const apply = this.applyFragmentData; - const Ctor = fLoader || this.config.loader; - return class CmcdFragmentLoader { - constructor(config) { - this.loader = void 0; - this.loader = new Ctor(config); - } - get stats() { - return this.loader.stats; - } - get context() { - return this.loader.context; - } - destroy() { - this.loader.destroy(); - } - abort() { - this.loader.abort(); - } - load(context, config, callbacks) { - apply(context); - this.loader.load(context, config, callbacks); - } - }; - } -} - -const PATHWAY_PENALTY_DURATION_MS = 300000; -class ContentSteeringController { - constructor(hls) { - this.hls = void 0; - this.log = void 0; - this.loader = null; - this.uri = null; - this.pathwayId = '.'; - this.pathwayPriority = null; - this.timeToLoad = 300; - this.reloadTimer = -1; - this.updated = 0; - this.started = false; - this.enabled = true; - this.levels = null; - this.audioTracks = null; - this.subtitleTracks = null; - this.penalizedPathways = {}; - this.hls = hls; - this.log = logger.log.bind(logger, `[content-steering]:`); - this.registerListeners(); - } - registerListeners() { - const hls = this.hls; - hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this); - hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this); - hls.on(Events.ERROR, this.onError, this); - } - unregisterListeners() { - const hls = this.hls; - if (!hls) { - return; - } - hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this); - hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this); - hls.off(Events.ERROR, this.onError, this); - } - startLoad() { - this.started = true; - this.clearTimeout(); - if (this.enabled && this.uri) { - if (this.updated) { - const ttl = this.timeToLoad * 1000 - (performance.now() - this.updated); - if (ttl > 0) { - this.scheduleRefresh(this.uri, ttl); - return; - } - } - this.loadSteeringManifest(this.uri); - } - } - stopLoad() { - this.started = false; - if (this.loader) { - this.loader.destroy(); - this.loader = null; - } - this.clearTimeout(); - } - clearTimeout() { - if (this.reloadTimer !== -1) { - self.clearTimeout(this.reloadTimer); - this.reloadTimer = -1; - } - } - destroy() { - this.unregisterListeners(); - this.stopLoad(); - // @ts-ignore - this.hls = null; - this.levels = this.audioTracks = this.subtitleTracks = null; - } - removeLevel(levelToRemove) { - const levels = this.levels; - if (levels) { - this.levels = levels.filter(level => level !== levelToRemove); - } - } - onManifestLoading() { - this.stopLoad(); - this.enabled = true; - this.timeToLoad = 300; - this.updated = 0; - this.uri = null; - this.pathwayId = '.'; - this.levels = this.audioTracks = this.subtitleTracks = null; - } - onManifestLoaded(event, data) { - const { - contentSteering - } = data; - if (contentSteering === null) { - return; - } - this.pathwayId = contentSteering.pathwayId; - this.uri = contentSteering.uri; - if (this.started) { - this.startLoad(); - } - } - onManifestParsed(event, data) { - this.audioTracks = data.audioTracks; - this.subtitleTracks = data.subtitleTracks; - } - onError(event, data) { - const { - errorAction - } = data; - if ((errorAction == null ? void 0 : errorAction.action) === NetworkErrorAction.SendAlternateToPenaltyBox && errorAction.flags === ErrorActionFlags.MoveAllAlternatesMatchingHost) { - const levels = this.levels; - let pathwayPriority = this.pathwayPriority; - let errorPathway = this.pathwayId; - if (data.context) { - const { - groupId, - pathwayId, - type - } = data.context; - if (groupId && levels) { - errorPathway = this.getPathwayForGroupId(groupId, type, errorPathway); - } else if (pathwayId) { - errorPathway = pathwayId; - } - } - if (!(errorPathway in this.penalizedPathways)) { - this.penalizedPathways[errorPathway] = performance.now(); - } - if (!pathwayPriority && levels) { - // If PATHWAY-PRIORITY was not provided, list pathways for error handling - pathwayPriority = levels.reduce((pathways, level) => { - if (pathways.indexOf(level.pathwayId) === -1) { - pathways.push(level.pathwayId); - } - return pathways; - }, []); - } - if (pathwayPriority && pathwayPriority.length > 1) { - this.updatePathwayPriority(pathwayPriority); - errorAction.resolved = this.pathwayId !== errorPathway; - } - if (!errorAction.resolved) { - logger.warn(`Could not resolve ${data.details} ("${data.error.message}") with content-steering for Pathway: ${errorPathway} levels: ${levels ? levels.length : levels} priorities: ${JSON.stringify(pathwayPriority)} penalized: ${JSON.stringify(this.penalizedPathways)}`); - } - } - } - filterParsedLevels(levels) { - // Filter levels to only include those that are in the initial pathway - this.levels = levels; - let pathwayLevels = this.getLevelsForPathway(this.pathwayId); - if (pathwayLevels.length === 0) { - const pathwayId = levels[0].pathwayId; - this.log(`No levels found in Pathway ${this.pathwayId}. Setting initial Pathway to "${pathwayId}"`); - pathwayLevels = this.getLevelsForPathway(pathwayId); - this.pathwayId = pathwayId; - } - if (pathwayLevels.length !== levels.length) { - this.log(`Found ${pathwayLevels.length}/${levels.length} levels in Pathway "${this.pathwayId}"`); - return pathwayLevels; - } - return levels; - } - getLevelsForPathway(pathwayId) { - if (this.levels === null) { - return []; - } - return this.levels.filter(level => pathwayId === level.pathwayId); - } - updatePathwayPriority(pathwayPriority) { - this.pathwayPriority = pathwayPriority; - let levels; - - // Evaluate if we should remove the pathway from the penalized list - const penalizedPathways = this.penalizedPathways; - const now = performance.now(); - Object.keys(penalizedPathways).forEach(pathwayId => { - if (now - penalizedPathways[pathwayId] > PATHWAY_PENALTY_DURATION_MS) { - delete penalizedPathways[pathwayId]; - } - }); - for (let i = 0; i < pathwayPriority.length; i++) { - const pathwayId = pathwayPriority[i]; - if (pathwayId in penalizedPathways) { - continue; - } - if (pathwayId === this.pathwayId) { - return; - } - const selectedIndex = this.hls.nextLoadLevel; - const selectedLevel = this.hls.levels[selectedIndex]; - levels = this.getLevelsForPathway(pathwayId); - if (levels.length > 0) { - this.log(`Setting Pathway to "${pathwayId}"`); - this.pathwayId = pathwayId; - reassignFragmentLevelIndexes(levels); - this.hls.trigger(Events.LEVELS_UPDATED, { - levels - }); - // Set LevelController's level to trigger LEVEL_SWITCHING which loads playlist if needed - const levelAfterChange = this.hls.levels[selectedIndex]; - if (selectedLevel && levelAfterChange && this.levels) { - if (levelAfterChange.attrs['STABLE-VARIANT-ID'] !== selectedLevel.attrs['STABLE-VARIANT-ID'] && levelAfterChange.bitrate !== selectedLevel.bitrate) { - this.log(`Unstable Pathways change from bitrate ${selectedLevel.bitrate} to ${levelAfterChange.bitrate}`); - } - this.hls.nextLoadLevel = selectedIndex; - } - break; - } - } - } - getPathwayForGroupId(groupId, type, defaultPathway) { - const levels = this.getLevelsForPathway(defaultPathway).concat(this.levels || []); - for (let i = 0; i < levels.length; i++) { - if (type === PlaylistContextType.AUDIO_TRACK && levels[i].hasAudioGroup(groupId) || type === PlaylistContextType.SUBTITLE_TRACK && levels[i].hasSubtitleGroup(groupId)) { - return levels[i].pathwayId; - } - } - return defaultPathway; - } - clonePathways(pathwayClones) { - const levels = this.levels; - if (!levels) { - return; - } - const audioGroupCloneMap = {}; - const subtitleGroupCloneMap = {}; - pathwayClones.forEach(pathwayClone => { - const { - ID: cloneId, - 'BASE-ID': baseId, - 'URI-REPLACEMENT': uriReplacement - } = pathwayClone; - if (levels.some(level => level.pathwayId === cloneId)) { - return; - } - const clonedVariants = this.getLevelsForPathway(baseId).map(baseLevel => { - const attributes = new AttrList(baseLevel.attrs); - attributes['PATHWAY-ID'] = cloneId; - const clonedAudioGroupId = attributes.AUDIO && `${attributes.AUDIO}_clone_${cloneId}`; - const clonedSubtitleGroupId = attributes.SUBTITLES && `${attributes.SUBTITLES}_clone_${cloneId}`; - if (clonedAudioGroupId) { - audioGroupCloneMap[attributes.AUDIO] = clonedAudioGroupId; - attributes.AUDIO = clonedAudioGroupId; - } - if (clonedSubtitleGroupId) { - subtitleGroupCloneMap[attributes.SUBTITLES] = clonedSubtitleGroupId; - attributes.SUBTITLES = clonedSubtitleGroupId; - } - const url = performUriReplacement(baseLevel.uri, attributes['STABLE-VARIANT-ID'], 'PER-VARIANT-URIS', uriReplacement); - const clonedLevel = new Level({ - attrs: attributes, - audioCodec: baseLevel.audioCodec, - bitrate: baseLevel.bitrate, - height: baseLevel.height, - name: baseLevel.name, - url, - videoCodec: baseLevel.videoCodec, - width: baseLevel.width - }); - if (baseLevel.audioGroups) { - for (let i = 1; i < baseLevel.audioGroups.length; i++) { - clonedLevel.addGroupId('audio', `${baseLevel.audioGroups[i]}_clone_${cloneId}`); - } - } - if (baseLevel.subtitleGroups) { - for (let i = 1; i < baseLevel.subtitleGroups.length; i++) { - clonedLevel.addGroupId('text', `${baseLevel.subtitleGroups[i]}_clone_${cloneId}`); - } - } - return clonedLevel; - }); - levels.push(...clonedVariants); - cloneRenditionGroups(this.audioTracks, audioGroupCloneMap, uriReplacement, cloneId); - cloneRenditionGroups(this.subtitleTracks, subtitleGroupCloneMap, uriReplacement, cloneId); - }); - } - loadSteeringManifest(uri) { - const config = this.hls.config; - const Loader = config.loader; - if (this.loader) { - this.loader.destroy(); - } - this.loader = new Loader(config); - let url; - try { - url = new self.URL(uri); - } catch (error) { - this.enabled = false; - this.log(`Failed to parse Steering Manifest URI: ${uri}`); - return; - } - if (url.protocol !== 'data:') { - const throughput = (this.hls.bandwidthEstimate || config.abrEwmaDefaultEstimate) | 0; - url.searchParams.set('_HLS_pathway', this.pathwayId); - url.searchParams.set('_HLS_throughput', '' + throughput); - } - const context = { - responseType: 'json', - url: url.href - }; - const loadPolicy = config.steeringManifestLoadPolicy.default; - const legacyRetryCompatibility = loadPolicy.errorRetry || loadPolicy.timeoutRetry || {}; - const loaderConfig = { - loadPolicy, - timeout: loadPolicy.maxLoadTimeMs, - maxRetry: legacyRetryCompatibility.maxNumRetry || 0, - retryDelay: legacyRetryCompatibility.retryDelayMs || 0, - maxRetryDelay: legacyRetryCompatibility.maxRetryDelayMs || 0 - }; - const callbacks = { - onSuccess: (response, stats, context, networkDetails) => { - this.log(`Loaded steering manifest: "${url}"`); - const steeringData = response.data; - if (steeringData.VERSION !== 1) { - this.log(`Steering VERSION ${steeringData.VERSION} not supported!`); - return; - } - this.updated = performance.now(); - this.timeToLoad = steeringData.TTL; - const { - 'RELOAD-URI': reloadUri, - 'PATHWAY-CLONES': pathwayClones, - 'PATHWAY-PRIORITY': pathwayPriority - } = steeringData; - if (reloadUri) { - try { - this.uri = new self.URL(reloadUri, url).href; - } catch (error) { - this.enabled = false; - this.log(`Failed to parse Steering Manifest RELOAD-URI: ${reloadUri}`); - return; - } - } - this.scheduleRefresh(this.uri || context.url); - if (pathwayClones) { - this.clonePathways(pathwayClones); - } - const loadedSteeringData = { - steeringManifest: steeringData, - url: url.toString() - }; - this.hls.trigger(Events.STEERING_MANIFEST_LOADED, loadedSteeringData); - if (pathwayPriority) { - this.updatePathwayPriority(pathwayPriority); - } - }, - onError: (error, context, networkDetails, stats) => { - this.log(`Error loading steering manifest: ${error.code} ${error.text} (${context.url})`); - this.stopLoad(); - if (error.code === 410) { - this.enabled = false; - this.log(`Steering manifest ${context.url} no longer available`); - return; - } - let ttl = this.timeToLoad * 1000; - if (error.code === 429) { - const loader = this.loader; - if (typeof (loader == null ? void 0 : loader.getResponseHeader) === 'function') { - const retryAfter = loader.getResponseHeader('Retry-After'); - if (retryAfter) { - ttl = parseFloat(retryAfter) * 1000; - } - } - this.log(`Steering manifest ${context.url} rate limited`); - return; - } - this.scheduleRefresh(this.uri || context.url, ttl); - }, - onTimeout: (stats, context, networkDetails) => { - this.log(`Timeout loading steering manifest (${context.url})`); - this.scheduleRefresh(this.uri || context.url); - } - }; - this.log(`Requesting steering manifest: ${url}`); - this.loader.load(context, loaderConfig, callbacks); - } - scheduleRefresh(uri, ttlMs = this.timeToLoad * 1000) { - this.clearTimeout(); - this.reloadTimer = self.setTimeout(() => { - var _this$hls; - const media = (_this$hls = this.hls) == null ? void 0 : _this$hls.media; - if (media && !media.ended) { - this.loadSteeringManifest(uri); - return; - } - this.scheduleRefresh(uri, this.timeToLoad * 1000); - }, ttlMs); - } -} -function cloneRenditionGroups(tracks, groupCloneMap, uriReplacement, cloneId) { - if (!tracks) { - return; - } - Object.keys(groupCloneMap).forEach(audioGroupId => { - const clonedTracks = tracks.filter(track => track.groupId === audioGroupId).map(track => { - const clonedTrack = _extends({}, track); - clonedTrack.details = undefined; - clonedTrack.attrs = new AttrList(clonedTrack.attrs); - clonedTrack.url = clonedTrack.attrs.URI = performUriReplacement(track.url, track.attrs['STABLE-RENDITION-ID'], 'PER-RENDITION-URIS', uriReplacement); - clonedTrack.groupId = clonedTrack.attrs['GROUP-ID'] = groupCloneMap[audioGroupId]; - clonedTrack.attrs['PATHWAY-ID'] = cloneId; - return clonedTrack; - }); - tracks.push(...clonedTracks); - }); -} -function performUriReplacement(uri, stableId, perOptionKey, uriReplacement) { - const { - HOST: host, - PARAMS: params, - [perOptionKey]: perOptionUris - } = uriReplacement; - let perVariantUri; - if (stableId) { - perVariantUri = perOptionUris == null ? void 0 : perOptionUris[stableId]; - if (perVariantUri) { - uri = perVariantUri; - } - } - const url = new self.URL(uri); - if (host && !perVariantUri) { - url.host = host; - } - if (params) { - Object.keys(params).sort().forEach(key => { - if (key) { - url.searchParams.set(key, params[key]); - } - }); - } - return url.href; -} - -const AGE_HEADER_LINE_REGEX = /^age:\s*[\d.]+\s*$/im; -class XhrLoader { - constructor(config) { - this.xhrSetup = void 0; - this.requestTimeout = void 0; - this.retryTimeout = void 0; - this.retryDelay = void 0; - this.config = null; - this.callbacks = null; - this.context = null; - this.loader = null; - this.stats = void 0; - this.xhrSetup = config ? config.xhrSetup || null : null; - this.stats = new LoadStats(); - this.retryDelay = 0; - } - destroy() { - this.callbacks = null; - this.abortInternal(); - this.loader = null; - this.config = null; - this.context = null; - this.xhrSetup = null; - } - abortInternal() { - const loader = this.loader; - self.clearTimeout(this.requestTimeout); - self.clearTimeout(this.retryTimeout); - if (loader) { - loader.onreadystatechange = null; - loader.onprogress = null; - if (loader.readyState !== 4) { - this.stats.aborted = true; - loader.abort(); - } - } - } - abort() { - var _this$callbacks; - this.abortInternal(); - if ((_this$callbacks = this.callbacks) != null && _this$callbacks.onAbort) { - this.callbacks.onAbort(this.stats, this.context, this.loader); - } - } - load(context, config, callbacks) { - if (this.stats.loading.start) { - throw new Error('Loader can only be used once.'); - } - this.stats.loading.start = self.performance.now(); - this.context = context; - this.config = config; - this.callbacks = callbacks; - this.loadInternal(); - } - loadInternal() { - const { - config, - context - } = this; - if (!config || !context) { - return; - } - const xhr = this.loader = new self.XMLHttpRequest(); - const stats = this.stats; - stats.loading.first = 0; - stats.loaded = 0; - stats.aborted = false; - const xhrSetup = this.xhrSetup; - if (xhrSetup) { - Promise.resolve().then(() => { - if (this.loader !== xhr || this.stats.aborted) return; - return xhrSetup(xhr, context.url); - }).catch(error => { - if (this.loader !== xhr || this.stats.aborted) return; - xhr.open('GET', context.url, true); - return xhrSetup(xhr, context.url); - }).then(() => { - if (this.loader !== xhr || this.stats.aborted) return; - this.openAndSendXhr(xhr, context, config); - }).catch(error => { - // IE11 throws an exception on xhr.open if attempting to access an HTTP resource over HTTPS - this.callbacks.onError({ - code: xhr.status, - text: error.message - }, context, xhr, stats); - return; - }); - } else { - this.openAndSendXhr(xhr, context, config); - } - } - openAndSendXhr(xhr, context, config) { - if (!xhr.readyState) { - xhr.open('GET', context.url, true); - } - const headers = context.headers; - const { - maxTimeToFirstByteMs, - maxLoadTimeMs - } = config.loadPolicy; - if (headers) { - for (const header in headers) { - xhr.setRequestHeader(header, headers[header]); - } - } - if (context.rangeEnd) { - xhr.setRequestHeader('Range', 'bytes=' + context.rangeStart + '-' + (context.rangeEnd - 1)); - } - xhr.onreadystatechange = this.readystatechange.bind(this); - xhr.onprogress = this.loadprogress.bind(this); - xhr.responseType = context.responseType; - // setup timeout before we perform request - self.clearTimeout(this.requestTimeout); - config.timeout = maxTimeToFirstByteMs && isFiniteNumber(maxTimeToFirstByteMs) ? maxTimeToFirstByteMs : maxLoadTimeMs; - this.requestTimeout = self.setTimeout(this.loadtimeout.bind(this), config.timeout); - xhr.send(); - } - readystatechange() { - const { - context, - loader: xhr, - stats - } = this; - if (!context || !xhr) { - return; - } - const readyState = xhr.readyState; - const config = this.config; - - // don't proceed if xhr has been aborted - if (stats.aborted) { - return; - } - - // >= HEADERS_RECEIVED - if (readyState >= 2) { - if (stats.loading.first === 0) { - stats.loading.first = Math.max(self.performance.now(), stats.loading.start); - // readyState >= 2 AND readyState !==4 (readyState = HEADERS_RECEIVED || LOADING) rearm timeout as xhr not finished yet - if (config.timeout !== config.loadPolicy.maxLoadTimeMs) { - self.clearTimeout(this.requestTimeout); - config.timeout = config.loadPolicy.maxLoadTimeMs; - this.requestTimeout = self.setTimeout(this.loadtimeout.bind(this), config.loadPolicy.maxLoadTimeMs - (stats.loading.first - stats.loading.start)); - } - } - if (readyState === 4) { - self.clearTimeout(this.requestTimeout); - xhr.onreadystatechange = null; - xhr.onprogress = null; - const status = xhr.status; - // http status between 200 to 299 are all successful - const useResponse = xhr.responseType !== 'text'; - if (status >= 200 && status < 300 && (useResponse && xhr.response || xhr.responseText !== null)) { - stats.loading.end = Math.max(self.performance.now(), stats.loading.first); - const data = useResponse ? xhr.response : xhr.responseText; - const len = xhr.responseType === 'arraybuffer' ? data.byteLength : data.length; - stats.loaded = stats.total = len; - stats.bwEstimate = stats.total * 8000 / (stats.loading.end - stats.loading.first); - if (!this.callbacks) { - return; - } - const onProgress = this.callbacks.onProgress; - if (onProgress) { - onProgress(stats, context, data, xhr); - } - if (!this.callbacks) { - return; - } - const response = { - url: xhr.responseURL, - data: data, - code: status - }; - this.callbacks.onSuccess(response, stats, context, xhr); - } else { - const retryConfig = config.loadPolicy.errorRetry; - const retryCount = stats.retry; - // if max nb of retries reached or if http status between 400 and 499 (such error cannot be recovered, retrying is useless), return error - const response = { - url: context.url, - data: undefined, - code: status - }; - if (shouldRetry(retryConfig, retryCount, false, response)) { - this.retry(retryConfig); - } else { - logger.error(`${status} while loading ${context.url}`); - this.callbacks.onError({ - code: status, - text: xhr.statusText - }, context, xhr, stats); - } - } - } - } - } - loadtimeout() { - if (!this.config) return; - const retryConfig = this.config.loadPolicy.timeoutRetry; - const retryCount = this.stats.retry; - if (shouldRetry(retryConfig, retryCount, true)) { - this.retry(retryConfig); - } else { - var _this$context; - logger.warn(`timeout while loading ${(_this$context = this.context) == null ? void 0 : _this$context.url}`); - const callbacks = this.callbacks; - if (callbacks) { - this.abortInternal(); - callbacks.onTimeout(this.stats, this.context, this.loader); - } - } - } - retry(retryConfig) { - const { - context, - stats - } = this; - this.retryDelay = getRetryDelay(retryConfig, stats.retry); - stats.retry++; - logger.warn(`${status ? 'HTTP Status ' + status : 'Timeout'} while loading ${context == null ? void 0 : context.url}, retrying ${stats.retry}/${retryConfig.maxNumRetry} in ${this.retryDelay}ms`); - // abort and reset internal state - this.abortInternal(); - this.loader = null; - // schedule retry - self.clearTimeout(this.retryTimeout); - this.retryTimeout = self.setTimeout(this.loadInternal.bind(this), this.retryDelay); - } - loadprogress(event) { - const stats = this.stats; - stats.loaded = event.loaded; - if (event.lengthComputable) { - stats.total = event.total; - } - } - getCacheAge() { - let result = null; - if (this.loader && AGE_HEADER_LINE_REGEX.test(this.loader.getAllResponseHeaders())) { - const ageHeader = this.loader.getResponseHeader('age'); - result = ageHeader ? parseFloat(ageHeader) : null; - } - return result; - } - getResponseHeader(name) { - if (this.loader && new RegExp(`^${name}:\\s*[\\d.]+\\s*$`, 'im').test(this.loader.getAllResponseHeaders())) { - return this.loader.getResponseHeader(name); - } - return null; - } -} - -function fetchSupported() { - if ( - // @ts-ignore - self.fetch && self.AbortController && self.ReadableStream && self.Request) { - try { - new self.ReadableStream({}); // eslint-disable-line no-new - return true; - } catch (e) { - /* noop */ - } - } - return false; -} -const BYTERANGE = /(\d+)-(\d+)\/(\d+)/; -class FetchLoader { - constructor(config /* HlsConfig */) { - this.fetchSetup = void 0; - this.requestTimeout = void 0; - this.request = null; - this.response = null; - this.controller = void 0; - this.context = null; - this.config = null; - this.callbacks = null; - this.stats = void 0; - this.loader = null; - this.fetchSetup = config.fetchSetup || getRequest; - this.controller = new self.AbortController(); - this.stats = new LoadStats(); - } - destroy() { - this.loader = this.callbacks = this.context = this.config = this.request = null; - this.abortInternal(); - this.response = null; - // @ts-ignore - this.fetchSetup = this.controller = this.stats = null; - } - abortInternal() { - if (this.controller && !this.stats.loading.end) { - this.stats.aborted = true; - this.controller.abort(); - } - } - abort() { - var _this$callbacks; - this.abortInternal(); - if ((_this$callbacks = this.callbacks) != null && _this$callbacks.onAbort) { - this.callbacks.onAbort(this.stats, this.context, this.response); - } - } - load(context, config, callbacks) { - const stats = this.stats; - if (stats.loading.start) { - throw new Error('Loader can only be used once.'); - } - stats.loading.start = self.performance.now(); - const initParams = getRequestParameters(context, this.controller.signal); - const onProgress = callbacks.onProgress; - const isArrayBuffer = context.responseType === 'arraybuffer'; - const LENGTH = isArrayBuffer ? 'byteLength' : 'length'; - const { - maxTimeToFirstByteMs, - maxLoadTimeMs - } = config.loadPolicy; - this.context = context; - this.config = config; - this.callbacks = callbacks; - this.request = this.fetchSetup(context, initParams); - self.clearTimeout(this.requestTimeout); - config.timeout = maxTimeToFirstByteMs && isFiniteNumber(maxTimeToFirstByteMs) ? maxTimeToFirstByteMs : maxLoadTimeMs; - this.requestTimeout = self.setTimeout(() => { - this.abortInternal(); - callbacks.onTimeout(stats, context, this.response); - }, config.timeout); - self.fetch(this.request).then(response => { - this.response = this.loader = response; - const first = Math.max(self.performance.now(), stats.loading.start); - self.clearTimeout(this.requestTimeout); - config.timeout = maxLoadTimeMs; - this.requestTimeout = self.setTimeout(() => { - this.abortInternal(); - callbacks.onTimeout(stats, context, this.response); - }, maxLoadTimeMs - (first - stats.loading.start)); - if (!response.ok) { - const { - status, - statusText - } = response; - throw new FetchError(statusText || 'fetch, bad network response', status, response); - } - stats.loading.first = first; - stats.total = getContentLength(response.headers) || stats.total; - if (onProgress && isFiniteNumber(config.highWaterMark)) { - return this.loadProgressively(response, stats, context, config.highWaterMark, onProgress); - } - if (isArrayBuffer) { - return response.arrayBuffer(); - } - if (context.responseType === 'json') { - return response.json(); - } - return response.text(); - }).then(responseData => { - const response = this.response; - if (!response) { - throw new Error('loader destroyed'); - } - self.clearTimeout(this.requestTimeout); - stats.loading.end = Math.max(self.performance.now(), stats.loading.first); - const total = responseData[LENGTH]; - if (total) { - stats.loaded = stats.total = total; - } - const loaderResponse = { - url: response.url, - data: responseData, - code: response.status - }; - if (onProgress && !isFiniteNumber(config.highWaterMark)) { - onProgress(stats, context, responseData, response); - } - callbacks.onSuccess(loaderResponse, stats, context, response); - }).catch(error => { - self.clearTimeout(this.requestTimeout); - if (stats.aborted) { - return; - } - // CORS errors result in an undefined code. Set it to 0 here to align with XHR's behavior - // when destroying, 'error' itself can be undefined - const code = !error ? 0 : error.code || 0; - const text = !error ? null : error.message; - callbacks.onError({ - code, - text - }, context, error ? error.details : null, stats); - }); - } - getCacheAge() { - let result = null; - if (this.response) { - const ageHeader = this.response.headers.get('age'); - result = ageHeader ? parseFloat(ageHeader) : null; - } - return result; - } - getResponseHeader(name) { - return this.response ? this.response.headers.get(name) : null; - } - loadProgressively(response, stats, context, highWaterMark = 0, onProgress) { - const chunkCache = new ChunkCache(); - const reader = response.body.getReader(); - const pump = () => { - return reader.read().then(data => { - if (data.done) { - if (chunkCache.dataLength) { - onProgress(stats, context, chunkCache.flush(), response); - } - return Promise.resolve(new ArrayBuffer(0)); - } - const chunk = data.value; - const len = chunk.length; - stats.loaded += len; - if (len < highWaterMark || chunkCache.dataLength) { - // The current chunk is too small to to be emitted or the cache already has data - // Push it to the cache - chunkCache.push(chunk); - if (chunkCache.dataLength >= highWaterMark) { - // flush in order to join the typed arrays - onProgress(stats, context, chunkCache.flush(), response); - } - } else { - // If there's nothing cached already, and the chache is large enough - // just emit the progress event - onProgress(stats, context, chunk, response); - } - return pump(); - }).catch(() => { - /* aborted */ - return Promise.reject(); - }); - }; - return pump(); - } -} -function getRequestParameters(context, signal) { - const initParams = { - method: 'GET', - mode: 'cors', - credentials: 'same-origin', - signal, - headers: new self.Headers(_extends({}, context.headers)) - }; - if (context.rangeEnd) { - initParams.headers.set('Range', 'bytes=' + context.rangeStart + '-' + String(context.rangeEnd - 1)); - } - return initParams; -} -function getByteRangeLength(byteRangeHeader) { - const result = BYTERANGE.exec(byteRangeHeader); - if (result) { - return parseInt(result[2]) - parseInt(result[1]) + 1; - } -} -function getContentLength(headers) { - const contentRange = headers.get('Content-Range'); - if (contentRange) { - const byteRangeLength = getByteRangeLength(contentRange); - if (isFiniteNumber(byteRangeLength)) { - return byteRangeLength; - } - } - const contentLength = headers.get('Content-Length'); - if (contentLength) { - return parseInt(contentLength); - } -} -function getRequest(context, initParams) { - return new self.Request(context.url, initParams); -} -class FetchError extends Error { - constructor(message, code, details) { - super(message); - this.code = void 0; - this.details = void 0; - this.code = code; - this.details = details; - } -} - -const WHITESPACE_CHAR = /\s/; -const Cues = { - newCue(track, startTime, endTime, captionScreen) { - const result = []; - let row; - // the type data states this is VTTCue, but it can potentially be a TextTrackCue on old browsers - let cue; - let indenting; - let indent; - let text; - const Cue = self.VTTCue || self.TextTrackCue; - for (let r = 0; r < captionScreen.rows.length; r++) { - row = captionScreen.rows[r]; - indenting = true; - indent = 0; - text = ''; - if (!row.isEmpty()) { - var _track$cues; - for (let c = 0; c < row.chars.length; c++) { - if (WHITESPACE_CHAR.test(row.chars[c].uchar) && indenting) { - indent++; - } else { - text += row.chars[c].uchar; - indenting = false; - } - } - // To be used for cleaning-up orphaned roll-up captions - row.cueStartTime = startTime; - - // Give a slight bump to the endTime if it's equal to startTime to avoid a SyntaxError in IE - if (startTime === endTime) { - endTime += 0.0001; - } - if (indent >= 16) { - indent--; - } else { - indent++; - } - const cueText = fixLineBreaks(text.trim()); - const id = generateCueId(startTime, endTime, cueText); - - // If this cue already exists in the track do not push it - if (!(track != null && (_track$cues = track.cues) != null && _track$cues.getCueById(id))) { - cue = new Cue(startTime, endTime, cueText); - cue.id = id; - cue.line = r + 1; - cue.align = 'left'; - // Clamp the position between 10 and 80 percent (CEA-608 PAC indent code) - // https://dvcs.w3.org/hg/text-tracks/raw-file/default/608toVTT/608toVTT.html#positioning-in-cea-608 - // Firefox throws an exception and captions break with out of bounds 0-100 values - cue.position = 10 + Math.min(80, Math.floor(indent * 8 / 32) * 10); - result.push(cue); - } - } - } - if (track && result.length) { - // Sort bottom cues in reverse order so that they render in line order when overlapping in Chrome - result.sort((cueA, cueB) => { - if (cueA.line === 'auto' || cueB.line === 'auto') { - return 0; - } - if (cueA.line > 8 && cueB.line > 8) { - return cueB.line - cueA.line; - } - return cueA.line - cueB.line; - }); - result.forEach(cue => addCueToTrack(track, cue)); - } - return result; - } -}; - -/** - * @deprecated use fragLoadPolicy.default - */ - -/** - * @deprecated use manifestLoadPolicy.default and playlistLoadPolicy.default - */ - -const defaultLoadPolicy = { - maxTimeToFirstByteMs: 8000, - maxLoadTimeMs: 20000, - timeoutRetry: null, - errorRetry: null -}; - -/** - * @ignore - * If possible, keep hlsDefaultConfig shallow - * It is cloned whenever a new Hls instance is created, by keeping the config - * shallow the properties are cloned, and we don't end up manipulating the default - */ -const hlsDefaultConfig = _objectSpread2(_objectSpread2({ - autoStartLoad: true, - // used by stream-controller - startPosition: -1, - // used by stream-controller - defaultAudioCodec: undefined, - // used by stream-controller - debug: false, - // used by logger - capLevelOnFPSDrop: false, - // used by fps-controller - capLevelToPlayerSize: false, - // used by cap-level-controller - ignoreDevicePixelRatio: false, - // used by cap-level-controller - preferManagedMediaSource: true, - initialLiveManifestSize: 1, - // used by stream-controller - maxBufferLength: 30, - // used by stream-controller - backBufferLength: Infinity, - // used by buffer-controller - frontBufferFlushThreshold: Infinity, - maxBufferSize: 60 * 1000 * 1000, - // used by stream-controller - maxBufferHole: 0.1, - // used by stream-controller - highBufferWatchdogPeriod: 2, - // used by stream-controller - nudgeOffset: 0.1, - // used by stream-controller - nudgeMaxRetry: 3, - // used by stream-controller - maxFragLookUpTolerance: 0.25, - // used by stream-controller - liveSyncDurationCount: 3, - // used by latency-controller - liveMaxLatencyDurationCount: Infinity, - // used by latency-controller - liveSyncDuration: undefined, - // used by latency-controller - liveMaxLatencyDuration: undefined, - // used by latency-controller - maxLiveSyncPlaybackRate: 1, - // used by latency-controller - liveDurationInfinity: false, - // used by buffer-controller - /** - * @deprecated use backBufferLength - */ - liveBackBufferLength: null, - // used by buffer-controller - maxMaxBufferLength: 600, - // used by stream-controller - enableWorker: true, - // used by transmuxer - workerPath: null, - // used by transmuxer - enableSoftwareAES: true, - // used by decrypter - startLevel: undefined, - // used by level-controller - startFragPrefetch: false, - // used by stream-controller - fpsDroppedMonitoringPeriod: 5000, - // used by fps-controller - fpsDroppedMonitoringThreshold: 0.2, - // used by fps-controller - appendErrorMaxRetry: 3, - // used by buffer-controller - loader: XhrLoader, - // loader: FetchLoader, - fLoader: undefined, - // used by fragment-loader - pLoader: undefined, - // used by playlist-loader - xhrSetup: undefined, - // used by xhr-loader - licenseXhrSetup: undefined, - // used by eme-controller - licenseResponseCallback: undefined, - // used by eme-controller - abrController: AbrController, - bufferController: BufferController, - capLevelController: CapLevelController, - errorController: ErrorController, - fpsController: FPSController, - stretchShortVideoTrack: false, - // used by mp4-remuxer - maxAudioFramesDrift: 1, - // used by mp4-remuxer - forceKeyFrameOnDiscontinuity: true, - // used by ts-demuxer - abrEwmaFastLive: 3, - // used by abr-controller - abrEwmaSlowLive: 9, - // used by abr-controller - abrEwmaFastVoD: 3, - // used by abr-controller - abrEwmaSlowVoD: 9, - // used by abr-controller - abrEwmaDefaultEstimate: 5e5, - // 500 kbps // used by abr-controller - abrEwmaDefaultEstimateMax: 5e6, - // 5 mbps - abrBandWidthFactor: 0.95, - // used by abr-controller - abrBandWidthUpFactor: 0.7, - // used by abr-controller - abrMaxWithRealBitrate: false, - // used by abr-controller - maxStarvationDelay: 4, - // used by abr-controller - maxLoadingDelay: 4, - // used by abr-controller - minAutoBitrate: 0, - // used by hls - emeEnabled: false, - // used by eme-controller - widevineLicenseUrl: undefined, - // used by eme-controller - drmSystems: {}, - // used by eme-controller - drmSystemOptions: {}, - // used by eme-controller - requestMediaKeySystemAccessFunc: requestMediaKeySystemAccess , - // used by eme-controller - testBandwidth: true, - progressive: false, - lowLatencyMode: true, - cmcd: undefined, - enableDateRangeMetadataCues: true, - enableEmsgMetadataCues: true, - enableID3MetadataCues: true, - useMediaCapabilities: true, - certLoadPolicy: { - default: defaultLoadPolicy - }, - keyLoadPolicy: { - default: { - maxTimeToFirstByteMs: 8000, - maxLoadTimeMs: 20000, - timeoutRetry: { - maxNumRetry: 1, - retryDelayMs: 1000, - maxRetryDelayMs: 20000, - backoff: 'linear' - }, - errorRetry: { - maxNumRetry: 8, - retryDelayMs: 1000, - maxRetryDelayMs: 20000, - backoff: 'linear' - } - } - }, - manifestLoadPolicy: { - default: { - maxTimeToFirstByteMs: Infinity, - maxLoadTimeMs: 20000, - timeoutRetry: { - maxNumRetry: 2, - retryDelayMs: 0, - maxRetryDelayMs: 0 - }, - errorRetry: { - maxNumRetry: 1, - retryDelayMs: 1000, - maxRetryDelayMs: 8000 - } - } - }, - playlistLoadPolicy: { - default: { - maxTimeToFirstByteMs: 10000, - maxLoadTimeMs: 20000, - timeoutRetry: { - maxNumRetry: 2, - retryDelayMs: 0, - maxRetryDelayMs: 0 - }, - errorRetry: { - maxNumRetry: 2, - retryDelayMs: 1000, - maxRetryDelayMs: 8000 - } - } - }, - fragLoadPolicy: { - default: { - maxTimeToFirstByteMs: 10000, - maxLoadTimeMs: 120000, - timeoutRetry: { - maxNumRetry: 4, - retryDelayMs: 0, - maxRetryDelayMs: 0 - }, - errorRetry: { - maxNumRetry: 6, - retryDelayMs: 1000, - maxRetryDelayMs: 8000 - } - } - }, - steeringManifestLoadPolicy: { - default: { - maxTimeToFirstByteMs: 10000, - maxLoadTimeMs: 20000, - timeoutRetry: { - maxNumRetry: 2, - retryDelayMs: 0, - maxRetryDelayMs: 0 - }, - errorRetry: { - maxNumRetry: 1, - retryDelayMs: 1000, - maxRetryDelayMs: 8000 - } - } - }, - // These default settings are deprecated in favor of the above policies - // and are maintained for backwards compatibility - manifestLoadingTimeOut: 10000, - manifestLoadingMaxRetry: 1, - manifestLoadingRetryDelay: 1000, - manifestLoadingMaxRetryTimeout: 64000, - levelLoadingTimeOut: 10000, - levelLoadingMaxRetry: 4, - levelLoadingRetryDelay: 1000, - levelLoadingMaxRetryTimeout: 64000, - fragLoadingTimeOut: 20000, - fragLoadingMaxRetry: 6, - fragLoadingRetryDelay: 1000, - fragLoadingMaxRetryTimeout: 64000 -}, timelineConfig()), {}, { - subtitleStreamController: SubtitleStreamController , - subtitleTrackController: SubtitleTrackController , - timelineController: TimelineController , - audioStreamController: AudioStreamController , - audioTrackController: AudioTrackController , - emeController: EMEController , - cmcdController: CMCDController , - contentSteeringController: ContentSteeringController -}); -function timelineConfig() { - return { - cueHandler: Cues, - // used by timeline-controller - enableWebVTT: true, - // used by timeline-controller - enableIMSC1: true, - // used by timeline-controller - enableCEA708Captions: true, - // used by timeline-controller - captionsTextTrack1Label: 'English', - // used by timeline-controller - captionsTextTrack1LanguageCode: 'en', - // used by timeline-controller - captionsTextTrack2Label: 'Spanish', - // used by timeline-controller - captionsTextTrack2LanguageCode: 'es', - // used by timeline-controller - captionsTextTrack3Label: 'Unknown CC', - // used by timeline-controller - captionsTextTrack3LanguageCode: '', - // used by timeline-controller - captionsTextTrack4Label: 'Unknown CC', - // used by timeline-controller - captionsTextTrack4LanguageCode: '', - // used by timeline-controller - renderTextTracksNatively: true - }; -} - -/** - * @ignore - */ -function mergeConfig(defaultConfig, userConfig) { - if ((userConfig.liveSyncDurationCount || userConfig.liveMaxLatencyDurationCount) && (userConfig.liveSyncDuration || userConfig.liveMaxLatencyDuration)) { - throw new Error("Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration"); - } - if (userConfig.liveMaxLatencyDurationCount !== undefined && (userConfig.liveSyncDurationCount === undefined || userConfig.liveMaxLatencyDurationCount <= userConfig.liveSyncDurationCount)) { - throw new Error('Illegal hls.js config: "liveMaxLatencyDurationCount" must be greater than "liveSyncDurationCount"'); - } - if (userConfig.liveMaxLatencyDuration !== undefined && (userConfig.liveSyncDuration === undefined || userConfig.liveMaxLatencyDuration <= userConfig.liveSyncDuration)) { - throw new Error('Illegal hls.js config: "liveMaxLatencyDuration" must be greater than "liveSyncDuration"'); - } - const defaultsCopy = deepCpy(defaultConfig); - - // Backwards compatibility with deprecated config values - const deprecatedSettingTypes = ['manifest', 'level', 'frag']; - const deprecatedSettings = ['TimeOut', 'MaxRetry', 'RetryDelay', 'MaxRetryTimeout']; - deprecatedSettingTypes.forEach(type => { - const policyName = `${type === 'level' ? 'playlist' : type}LoadPolicy`; - const policyNotSet = userConfig[policyName] === undefined; - const report = []; - deprecatedSettings.forEach(setting => { - const deprecatedSetting = `${type}Loading${setting}`; - const value = userConfig[deprecatedSetting]; - if (value !== undefined && policyNotSet) { - report.push(deprecatedSetting); - const settings = defaultsCopy[policyName].default; - userConfig[policyName] = { - default: settings - }; - switch (setting) { - case 'TimeOut': - settings.maxLoadTimeMs = value; - settings.maxTimeToFirstByteMs = value; - break; - case 'MaxRetry': - settings.errorRetry.maxNumRetry = value; - settings.timeoutRetry.maxNumRetry = value; - break; - case 'RetryDelay': - settings.errorRetry.retryDelayMs = value; - settings.timeoutRetry.retryDelayMs = value; - break; - case 'MaxRetryTimeout': - settings.errorRetry.maxRetryDelayMs = value; - settings.timeoutRetry.maxRetryDelayMs = value; - break; - } - } - }); - if (report.length) { - logger.warn(`hls.js config: "${report.join('", "')}" setting(s) are deprecated, use "${policyName}": ${JSON.stringify(userConfig[policyName])}`); - } - }); - return _objectSpread2(_objectSpread2({}, defaultsCopy), userConfig); -} -function deepCpy(obj) { - if (obj && typeof obj === 'object') { - if (Array.isArray(obj)) { - return obj.map(deepCpy); - } - return Object.keys(obj).reduce((result, key) => { - result[key] = deepCpy(obj[key]); - return result; - }, {}); - } - return obj; -} - -/** - * @ignore - */ -function enableStreamingMode(config) { - const currentLoader = config.loader; - if (currentLoader !== FetchLoader && currentLoader !== XhrLoader) { - // If a developer has configured their own loader, respect that choice - logger.log('[config]: Custom loader detected, cannot enable progressive streaming'); - config.progressive = false; - } else { - const canStreamProgressively = fetchSupported(); - if (canStreamProgressively) { - config.loader = FetchLoader; - config.progressive = true; - config.enableSoftwareAES = true; - logger.log('[config]: Progressive streaming enabled, using FetchLoader'); - } - } -} - -let chromeOrFirefox; -class LevelController extends BasePlaylistController { - constructor(hls, contentSteeringController) { - super(hls, '[level-controller]'); - this._levels = []; - this._firstLevel = -1; - this._maxAutoLevel = -1; - this._startLevel = void 0; - this.currentLevel = null; - this.currentLevelIndex = -1; - this.manualLevelIndex = -1; - this.steering = void 0; - this.onParsedComplete = void 0; - this.steering = contentSteeringController; - this._registerListeners(); - } - _registerListeners() { - const { - hls - } = this; - hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this); - hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this); - hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); - hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this); - hls.on(Events.ERROR, this.onError, this); - } - _unregisterListeners() { - const { - hls - } = this; - hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this); - hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this); - hls.off(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); - hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this); - hls.off(Events.ERROR, this.onError, this); - } - destroy() { - this._unregisterListeners(); - this.steering = null; - this.resetLevels(); - super.destroy(); - } - stopLoad() { - const levels = this._levels; - - // clean up live level details to force reload them, and reset load errors - levels.forEach(level => { - level.loadError = 0; - level.fragmentError = 0; - }); - super.stopLoad(); - } - resetLevels() { - this._startLevel = undefined; - this.manualLevelIndex = -1; - this.currentLevelIndex = -1; - this.currentLevel = null; - this._levels = []; - this._maxAutoLevel = -1; - } - onManifestLoading(event, data) { - this.resetLevels(); - } - onManifestLoaded(event, data) { - const preferManagedMediaSource = this.hls.config.preferManagedMediaSource; - const levels = []; - const redundantSet = {}; - const generatePathwaySet = {}; - let resolutionFound = false; - let videoCodecFound = false; - let audioCodecFound = false; - data.levels.forEach(levelParsed => { - var _audioCodec, _videoCodec; - const attributes = levelParsed.attrs; - - // erase audio codec info if browser does not support mp4a.40.34. - // demuxer will autodetect codec and fallback to mpeg/audio - let { - audioCodec, - videoCodec - } = levelParsed; - if (((_audioCodec = audioCodec) == null ? void 0 : _audioCodec.indexOf('mp4a.40.34')) !== -1) { - chromeOrFirefox || (chromeOrFirefox = /chrome|firefox/i.test(navigator.userAgent)); - if (chromeOrFirefox) { - levelParsed.audioCodec = audioCodec = undefined; - } - } - if (audioCodec) { - levelParsed.audioCodec = audioCodec = getCodecCompatibleName(audioCodec, preferManagedMediaSource); - } - if (((_videoCodec = videoCodec) == null ? void 0 : _videoCodec.indexOf('avc1')) === 0) { - videoCodec = levelParsed.videoCodec = convertAVC1ToAVCOTI(videoCodec); - } - - // only keep levels with supported audio/video codecs - const { - width, - height, - unknownCodecs - } = levelParsed; - resolutionFound || (resolutionFound = !!(width && height)); - videoCodecFound || (videoCodecFound = !!videoCodec); - audioCodecFound || (audioCodecFound = !!audioCodec); - if (unknownCodecs != null && unknownCodecs.length || audioCodec && !areCodecsMediaSourceSupported(audioCodec, 'audio', preferManagedMediaSource) || videoCodec && !areCodecsMediaSourceSupported(videoCodec, 'video', preferManagedMediaSource)) { - return; - } - const { - CODECS, - 'FRAME-RATE': FRAMERATE, - 'HDCP-LEVEL': HDCP, - 'PATHWAY-ID': PATHWAY, - RESOLUTION, - 'VIDEO-RANGE': VIDEO_RANGE - } = attributes; - const contentSteeringPrefix = `${PATHWAY || '.'}-`; - const levelKey = `${contentSteeringPrefix}${levelParsed.bitrate}-${RESOLUTION}-${FRAMERATE}-${CODECS}-${VIDEO_RANGE}-${HDCP}`; - if (!redundantSet[levelKey]) { - const level = new Level(levelParsed); - redundantSet[levelKey] = level; - generatePathwaySet[levelKey] = 1; - levels.push(level); - } else if (redundantSet[levelKey].uri !== levelParsed.url && !levelParsed.attrs['PATHWAY-ID']) { - // Assign Pathway IDs to Redundant Streams (default Pathways is ".". Redundant Streams "..", "...", and so on.) - // Content Steering controller to handles Pathway fallback on error - const pathwayCount = generatePathwaySet[levelKey] += 1; - levelParsed.attrs['PATHWAY-ID'] = new Array(pathwayCount + 1).join('.'); - const level = new Level(levelParsed); - redundantSet[levelKey] = level; - levels.push(level); - } else { - redundantSet[levelKey].addGroupId('audio', attributes.AUDIO); - redundantSet[levelKey].addGroupId('text', attributes.SUBTITLES); - } - }); - this.filterAndSortMediaOptions(levels, data, resolutionFound, videoCodecFound, audioCodecFound); - } - filterAndSortMediaOptions(filteredLevels, data, resolutionFound, videoCodecFound, audioCodecFound) { - let audioTracks = []; - let subtitleTracks = []; - let levels = filteredLevels; - - // remove audio-only and invalid video-range levels if we also have levels with video codecs or RESOLUTION signalled - if ((resolutionFound || videoCodecFound) && audioCodecFound) { - levels = levels.filter(({ - videoCodec, - videoRange, - width, - height - }) => (!!videoCodec || !!(width && height)) && isVideoRange(videoRange)); - } - if (levels.length === 0) { - // Dispatch error after MANIFEST_LOADED is done propagating - Promise.resolve().then(() => { - if (this.hls) { - if (data.levels.length) { - this.warn(`One or more CODECS in variant not supported: ${JSON.stringify(data.levels[0].attrs)}`); - } - const error = new Error('no level with compatible codecs found in manifest'); - this.hls.trigger(Events.ERROR, { - type: ErrorTypes.MEDIA_ERROR, - details: ErrorDetails.MANIFEST_INCOMPATIBLE_CODECS_ERROR, - fatal: true, - url: data.url, - error, - reason: error.message - }); - } - }); - return; - } - if (data.audioTracks) { - const { - preferManagedMediaSource - } = this.hls.config; - audioTracks = data.audioTracks.filter(track => !track.audioCodec || areCodecsMediaSourceSupported(track.audioCodec, 'audio', preferManagedMediaSource)); - // Assign ids after filtering as array indices by group-id - assignTrackIdsByGroup(audioTracks); - } - if (data.subtitles) { - subtitleTracks = data.subtitles; - assignTrackIdsByGroup(subtitleTracks); - } - // start bitrate is the first bitrate of the manifest - const unsortedLevels = levels.slice(0); - // sort levels from lowest to highest - levels.sort((a, b) => { - if (a.attrs['HDCP-LEVEL'] !== b.attrs['HDCP-LEVEL']) { - return (a.attrs['HDCP-LEVEL'] || '') > (b.attrs['HDCP-LEVEL'] || '') ? 1 : -1; - } - // sort on height before bitrate for cap-level-controller - if (resolutionFound && a.height !== b.height) { - return a.height - b.height; - } - if (a.frameRate !== b.frameRate) { - return a.frameRate - b.frameRate; - } - if (a.videoRange !== b.videoRange) { - return VideoRangeValues.indexOf(a.videoRange) - VideoRangeValues.indexOf(b.videoRange); - } - if (a.videoCodec !== b.videoCodec) { - const valueA = videoCodecPreferenceValue(a.videoCodec); - const valueB = videoCodecPreferenceValue(b.videoCodec); - if (valueA !== valueB) { - return valueB - valueA; - } - } - if (a.uri === b.uri && a.codecSet !== b.codecSet) { - const valueA = codecsSetSelectionPreferenceValue(a.codecSet); - const valueB = codecsSetSelectionPreferenceValue(b.codecSet); - if (valueA !== valueB) { - return valueB - valueA; - } - } - if (a.averageBitrate !== b.averageBitrate) { - return a.averageBitrate - b.averageBitrate; - } - return 0; - }); - let firstLevelInPlaylist = unsortedLevels[0]; - if (this.steering) { - levels = this.steering.filterParsedLevels(levels); - if (levels.length !== unsortedLevels.length) { - for (let i = 0; i < unsortedLevels.length; i++) { - if (unsortedLevels[i].pathwayId === levels[0].pathwayId) { - firstLevelInPlaylist = unsortedLevels[i]; - break; - } - } - } - } - this._levels = levels; - - // find index of first level in sorted levels - for (let i = 0; i < levels.length; i++) { - if (levels[i] === firstLevelInPlaylist) { - var _this$hls$userConfig; - this._firstLevel = i; - const firstLevelBitrate = firstLevelInPlaylist.bitrate; - const bandwidthEstimate = this.hls.bandwidthEstimate; - this.log(`manifest loaded, ${levels.length} level(s) found, first bitrate: ${firstLevelBitrate}`); - // Update default bwe to first variant bitrate as long it has not been configured or set - if (((_this$hls$userConfig = this.hls.userConfig) == null ? void 0 : _this$hls$userConfig.abrEwmaDefaultEstimate) === undefined) { - const startingBwEstimate = Math.min(firstLevelBitrate, this.hls.config.abrEwmaDefaultEstimateMax); - if (startingBwEstimate > bandwidthEstimate && bandwidthEstimate === hlsDefaultConfig.abrEwmaDefaultEstimate) { - this.hls.bandwidthEstimate = startingBwEstimate; - } - } - break; - } - } - - // Audio is only alternate if manifest include a URI along with the audio group tag, - // and this is not an audio-only stream where levels contain audio-only - const audioOnly = audioCodecFound && !videoCodecFound; - const edata = { - levels, - audioTracks, - subtitleTracks, - sessionData: data.sessionData, - sessionKeys: data.sessionKeys, - firstLevel: this._firstLevel, - stats: data.stats, - audio: audioCodecFound, - video: videoCodecFound, - altAudio: !audioOnly && audioTracks.some(t => !!t.url) - }; - this.hls.trigger(Events.MANIFEST_PARSED, edata); - - // Initiate loading after all controllers have received MANIFEST_PARSED - if (this.hls.config.autoStartLoad || this.hls.forceStartLoad) { - this.hls.startLoad(this.hls.config.startPosition); - } - } - get levels() { - if (this._levels.length === 0) { - return null; - } - return this._levels; - } - get level() { - return this.currentLevelIndex; - } - set level(newLevel) { - const levels = this._levels; - if (levels.length === 0) { - return; - } - // check if level idx is valid - if (newLevel < 0 || newLevel >= levels.length) { - // invalid level id given, trigger error - const error = new Error('invalid level idx'); - const fatal = newLevel < 0; - this.hls.trigger(Events.ERROR, { - type: ErrorTypes.OTHER_ERROR, - details: ErrorDetails.LEVEL_SWITCH_ERROR, - level: newLevel, - fatal, - error, - reason: error.message - }); - if (fatal) { - return; - } - newLevel = Math.min(newLevel, levels.length - 1); - } - const lastLevelIndex = this.currentLevelIndex; - const lastLevel = this.currentLevel; - const lastPathwayId = lastLevel ? lastLevel.attrs['PATHWAY-ID'] : undefined; - const level = levels[newLevel]; - const pathwayId = level.attrs['PATHWAY-ID']; - this.currentLevelIndex = newLevel; - this.currentLevel = level; - if (lastLevelIndex === newLevel && level.details && lastLevel && lastPathwayId === pathwayId) { - return; - } - this.log(`Switching to level ${newLevel} (${level.height ? level.height + 'p ' : ''}${level.videoRange ? level.videoRange + ' ' : ''}${level.codecSet ? level.codecSet + ' ' : ''}@${level.bitrate})${pathwayId ? ' with Pathway ' + pathwayId : ''} from level ${lastLevelIndex}${lastPathwayId ? ' with Pathway ' + lastPathwayId : ''}`); - const levelSwitchingData = { - level: newLevel, - attrs: level.attrs, - details: level.details, - bitrate: level.bitrate, - averageBitrate: level.averageBitrate, - maxBitrate: level.maxBitrate, - realBitrate: level.realBitrate, - width: level.width, - height: level.height, - codecSet: level.codecSet, - audioCodec: level.audioCodec, - videoCodec: level.videoCodec, - audioGroups: level.audioGroups, - subtitleGroups: level.subtitleGroups, - loaded: level.loaded, - loadError: level.loadError, - fragmentError: level.fragmentError, - name: level.name, - id: level.id, - uri: level.uri, - url: level.url, - urlId: 0, - audioGroupIds: level.audioGroupIds, - textGroupIds: level.textGroupIds - }; - this.hls.trigger(Events.LEVEL_SWITCHING, levelSwitchingData); - // check if we need to load playlist for this level - const levelDetails = level.details; - if (!levelDetails || levelDetails.live) { - // level not retrieved yet, or live playlist we need to (re)load it - const hlsUrlParameters = this.switchParams(level.uri, lastLevel == null ? void 0 : lastLevel.details, levelDetails); - this.loadPlaylist(hlsUrlParameters); - } - } - get manualLevel() { - return this.manualLevelIndex; - } - set manualLevel(newLevel) { - this.manualLevelIndex = newLevel; - if (this._startLevel === undefined) { - this._startLevel = newLevel; - } - if (newLevel !== -1) { - this.level = newLevel; - } - } - get firstLevel() { - return this._firstLevel; - } - set firstLevel(newLevel) { - this._firstLevel = newLevel; - } - get startLevel() { - // Setting hls.startLevel (this._startLevel) overrides config.startLevel - if (this._startLevel === undefined) { - const configStartLevel = this.hls.config.startLevel; - if (configStartLevel !== undefined) { - return configStartLevel; - } - return this.hls.firstAutoLevel; - } - return this._startLevel; - } - set startLevel(newLevel) { - this._startLevel = newLevel; - } - onError(event, data) { - if (data.fatal || !data.context) { - return; - } - if (data.context.type === PlaylistContextType.LEVEL && data.context.level === this.level) { - this.checkRetry(data); - } - } - - // reset errors on the successful load of a fragment - onFragBuffered(event, { - frag - }) { - if (frag !== undefined && frag.type === PlaylistLevelType.MAIN) { - const el = frag.elementaryStreams; - if (!Object.keys(el).some(type => !!el[type])) { - return; - } - const level = this._levels[frag.level]; - if (level != null && level.loadError) { - this.log(`Resetting level error count of ${level.loadError} on frag buffered`); - level.loadError = 0; - } - } - } - onLevelLoaded(event, data) { - var _data$deliveryDirecti2; - const { - level, - details - } = data; - const curLevel = this._levels[level]; - if (!curLevel) { - var _data$deliveryDirecti; - this.warn(`Invalid level index ${level}`); - if ((_data$deliveryDirecti = data.deliveryDirectives) != null && _data$deliveryDirecti.skip) { - details.deltaUpdateFailed = true; - } - return; - } - - // only process level loaded events matching with expected level - if (level === this.currentLevelIndex) { - // reset level load error counter on successful level loaded only if there is no issues with fragments - if (curLevel.fragmentError === 0) { - curLevel.loadError = 0; - } - this.playlistLoaded(level, data, curLevel.details); - } else if ((_data$deliveryDirecti2 = data.deliveryDirectives) != null && _data$deliveryDirecti2.skip) { - // received a delta playlist update that cannot be merged - details.deltaUpdateFailed = true; - } - } - loadPlaylist(hlsUrlParameters) { - super.loadPlaylist(); - const currentLevelIndex = this.currentLevelIndex; - const currentLevel = this.currentLevel; - if (currentLevel && this.shouldLoadPlaylist(currentLevel)) { - let url = currentLevel.uri; - if (hlsUrlParameters) { - try { - url = hlsUrlParameters.addDirectives(url); - } catch (error) { - this.warn(`Could not construct new URL with HLS Delivery Directives: ${error}`); - } - } - const pathwayId = currentLevel.attrs['PATHWAY-ID']; - this.log(`Loading level index ${currentLevelIndex}${(hlsUrlParameters == null ? void 0 : hlsUrlParameters.msn) !== undefined ? ' at sn ' + hlsUrlParameters.msn + ' part ' + hlsUrlParameters.part : ''} with${pathwayId ? ' Pathway ' + pathwayId : ''} ${url}`); - - // console.log('Current audio track group ID:', this.hls.audioTracks[this.hls.audioTrack].groupId); - // console.log('New video quality level audio group id:', levelObject.attrs.AUDIO, level); - this.clearTimer(); - this.hls.trigger(Events.LEVEL_LOADING, { - url, - level: currentLevelIndex, - pathwayId: currentLevel.attrs['PATHWAY-ID'], - id: 0, - // Deprecated Level urlId - deliveryDirectives: hlsUrlParameters || null - }); - } - } - get nextLoadLevel() { - if (this.manualLevelIndex !== -1) { - return this.manualLevelIndex; - } else { - return this.hls.nextAutoLevel; - } - } - set nextLoadLevel(nextLevel) { - this.level = nextLevel; - if (this.manualLevelIndex === -1) { - this.hls.nextAutoLevel = nextLevel; - } - } - removeLevel(levelIndex) { - var _this$currentLevel; - const levels = this._levels.filter((level, index) => { - if (index !== levelIndex) { - return true; - } - if (this.steering) { - this.steering.removeLevel(level); - } - if (level === this.currentLevel) { - this.currentLevel = null; - this.currentLevelIndex = -1; - if (level.details) { - level.details.fragments.forEach(f => f.level = -1); - } - } - return false; - }); - reassignFragmentLevelIndexes(levels); - this._levels = levels; - if (this.currentLevelIndex > -1 && (_this$currentLevel = this.currentLevel) != null && _this$currentLevel.details) { - this.currentLevelIndex = this.currentLevel.details.fragments[0].level; - } - this.hls.trigger(Events.LEVELS_UPDATED, { - levels - }); - } - onLevelsUpdated(event, { - levels - }) { - this._levels = levels; - } - checkMaxAutoUpdated() { - const { - autoLevelCapping, - maxAutoLevel, - maxHdcpLevel - } = this.hls; - if (this._maxAutoLevel !== maxAutoLevel) { - this._maxAutoLevel = maxAutoLevel; - this.hls.trigger(Events.MAX_AUTO_LEVEL_UPDATED, { - autoLevelCapping, - levels: this.levels, - maxAutoLevel, - minAutoLevel: this.hls.minAutoLevel, - maxHdcpLevel - }); - } - } -} -function assignTrackIdsByGroup(tracks) { - const groups = {}; - tracks.forEach(track => { - const groupId = track.groupId || ''; - track.id = groups[groupId] = groups[groupId] || 0; - groups[groupId]++; - }); -} - -class KeyLoader { - constructor(config) { - this.config = void 0; - this.keyUriToKeyInfo = {}; - this.emeController = null; - this.config = config; - } - abort(type) { - for (const uri in this.keyUriToKeyInfo) { - const loader = this.keyUriToKeyInfo[uri].loader; - if (loader) { - var _loader$context; - if (type && type !== ((_loader$context = loader.context) == null ? void 0 : _loader$context.frag.type)) { - return; - } - loader.abort(); - } - } - } - detach() { - for (const uri in this.keyUriToKeyInfo) { - const keyInfo = this.keyUriToKeyInfo[uri]; - // Remove cached EME keys on detach - if (keyInfo.mediaKeySessionContext || keyInfo.decryptdata.isCommonEncryption) { - delete this.keyUriToKeyInfo[uri]; - } - } - } - destroy() { - this.detach(); - for (const uri in this.keyUriToKeyInfo) { - const loader = this.keyUriToKeyInfo[uri].loader; - if (loader) { - loader.destroy(); - } - } - this.keyUriToKeyInfo = {}; - } - createKeyLoadError(frag, details = ErrorDetails.KEY_LOAD_ERROR, error, networkDetails, response) { - return new LoadError({ - type: ErrorTypes.NETWORK_ERROR, - details, - fatal: false, - frag, - response, - error, - networkDetails - }); - } - loadClear(loadingFrag, encryptedFragments) { - if (this.emeController && this.config.emeEnabled) { - // access key-system with nearest key on start (loaidng frag is unencrypted) - const { - sn, - cc - } = loadingFrag; - for (let i = 0; i < encryptedFragments.length; i++) { - const frag = encryptedFragments[i]; - if (cc <= frag.cc && (sn === 'initSegment' || frag.sn === 'initSegment' || sn < frag.sn)) { - this.emeController.selectKeySystemFormat(frag).then(keySystemFormat => { - frag.setKeyFormat(keySystemFormat); - }); - break; - } - } - } - } - load(frag) { - if (!frag.decryptdata && frag.encrypted && this.emeController) { - // Multiple keys, but none selected, resolve in eme-controller - return this.emeController.selectKeySystemFormat(frag).then(keySystemFormat => { - return this.loadInternal(frag, keySystemFormat); - }); - } - return this.loadInternal(frag); - } - loadInternal(frag, keySystemFormat) { - var _keyInfo, _keyInfo2; - if (keySystemFormat) { - frag.setKeyFormat(keySystemFormat); - } - const decryptdata = frag.decryptdata; - if (!decryptdata) { - const error = new Error(keySystemFormat ? `Expected frag.decryptdata to be defined after setting format ${keySystemFormat}` : 'Missing decryption data on fragment in onKeyLoading'); - return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, error)); - } - const uri = decryptdata.uri; - if (!uri) { - return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error(`Invalid key URI: "${uri}"`))); - } - let keyInfo = this.keyUriToKeyInfo[uri]; - if ((_keyInfo = keyInfo) != null && _keyInfo.decryptdata.key) { - decryptdata.key = keyInfo.decryptdata.key; - return Promise.resolve({ - frag, - keyInfo - }); - } - // Return key load promise as long as it does not have a mediakey session with an unusable key status - if ((_keyInfo2 = keyInfo) != null && _keyInfo2.keyLoadPromise) { - var _keyInfo$mediaKeySess; - switch ((_keyInfo$mediaKeySess = keyInfo.mediaKeySessionContext) == null ? void 0 : _keyInfo$mediaKeySess.keyStatus) { - case undefined: - case 'status-pending': - case 'usable': - case 'usable-in-future': - return keyInfo.keyLoadPromise.then(keyLoadedData => { - // Return the correct fragment with updated decryptdata key and loaded keyInfo - decryptdata.key = keyLoadedData.keyInfo.decryptdata.key; - return { - frag, - keyInfo - }; - }); - } - // If we have a key session and status and it is not pending or usable, continue - // This will go back to the eme-controller for expired keys to get a new keyLoadPromise - } - - // Load the key or return the loading promise - keyInfo = this.keyUriToKeyInfo[uri] = { - decryptdata, - keyLoadPromise: null, - loader: null, - mediaKeySessionContext: null - }; - switch (decryptdata.method) { - case 'ISO-23001-7': - case 'SAMPLE-AES': - case 'SAMPLE-AES-CENC': - case 'SAMPLE-AES-CTR': - if (decryptdata.keyFormat === 'identity') { - // loadKeyHTTP handles http(s) and data URLs - return this.loadKeyHTTP(keyInfo, frag); - } - return this.loadKeyEME(keyInfo, frag); - case 'AES-128': - return this.loadKeyHTTP(keyInfo, frag); - default: - return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error(`Key supplied with unsupported METHOD: "${decryptdata.method}"`))); - } - } - loadKeyEME(keyInfo, frag) { - const keyLoadedData = { - frag, - keyInfo - }; - if (this.emeController && this.config.emeEnabled) { - const keySessionContextPromise = this.emeController.loadKey(keyLoadedData); - if (keySessionContextPromise) { - return (keyInfo.keyLoadPromise = keySessionContextPromise.then(keySessionContext => { - keyInfo.mediaKeySessionContext = keySessionContext; - return keyLoadedData; - })).catch(error => { - // Remove promise for license renewal or retry - keyInfo.keyLoadPromise = null; - throw error; - }); - } - } - return Promise.resolve(keyLoadedData); - } - loadKeyHTTP(keyInfo, frag) { - const config = this.config; - const Loader = config.loader; - const keyLoader = new Loader(config); - frag.keyLoader = keyInfo.loader = keyLoader; - return keyInfo.keyLoadPromise = new Promise((resolve, reject) => { - const loaderContext = { - keyInfo, - frag, - responseType: 'arraybuffer', - url: keyInfo.decryptdata.uri - }; - - // maxRetry is 0 so that instead of retrying the same key on the same variant multiple times, - // key-loader will trigger an error and rely on stream-controller to handle retry logic. - // this will also align retry logic with fragment-loader - const loadPolicy = config.keyLoadPolicy.default; - const loaderConfig = { - loadPolicy, - timeout: loadPolicy.maxLoadTimeMs, - maxRetry: 0, - retryDelay: 0, - maxRetryDelay: 0 - }; - const loaderCallbacks = { - onSuccess: (response, stats, context, networkDetails) => { - const { - frag, - keyInfo, - url: uri - } = context; - if (!frag.decryptdata || keyInfo !== this.keyUriToKeyInfo[uri]) { - return reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error('after key load, decryptdata unset or changed'), networkDetails)); - } - keyInfo.decryptdata.key = frag.decryptdata.key = new Uint8Array(response.data); - - // detach fragment key loader on load success - frag.keyLoader = null; - keyInfo.loader = null; - resolve({ - frag, - keyInfo - }); - }, - onError: (response, context, networkDetails, stats) => { - this.resetLoader(context); - reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error(`HTTP Error ${response.code} loading key ${response.text}`), networkDetails, _objectSpread2({ - url: loaderContext.url, - data: undefined - }, response))); - }, - onTimeout: (stats, context, networkDetails) => { - this.resetLoader(context); - reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_TIMEOUT, new Error('key loading timed out'), networkDetails)); - }, - onAbort: (stats, context, networkDetails) => { - this.resetLoader(context); - reject(this.createKeyLoadError(frag, ErrorDetails.INTERNAL_ABORTED, new Error('key loading aborted'), networkDetails)); - } - }; - keyLoader.load(loaderContext, loaderConfig, loaderCallbacks); - }); - } - resetLoader(context) { - const { - frag, - keyInfo, - url: uri - } = context; - const loader = keyInfo.loader; - if (frag.keyLoader === loader) { - frag.keyLoader = null; - keyInfo.loader = null; - } - delete this.keyUriToKeyInfo[uri]; - if (loader) { - loader.destroy(); - } - } -} - -function getSourceBuffer() { - return self.SourceBuffer || self.WebKitSourceBuffer; -} -function isMSESupported() { - const mediaSource = getMediaSource(); - if (!mediaSource) { - return false; - } - - // if SourceBuffer is exposed ensure its API is valid - // Older browsers do not expose SourceBuffer globally so checking SourceBuffer.prototype is impossible - const sourceBuffer = getSourceBuffer(); - return !sourceBuffer || sourceBuffer.prototype && typeof sourceBuffer.prototype.appendBuffer === 'function' && typeof sourceBuffer.prototype.remove === 'function'; -} -function isSupported() { - if (!isMSESupported()) { - return false; - } - const mediaSource = getMediaSource(); - return typeof (mediaSource == null ? void 0 : mediaSource.isTypeSupported) === 'function' && (['avc1.42E01E,mp4a.40.2', 'av01.0.01M.08', 'vp09.00.50.08'].some(codecsForVideoContainer => mediaSource.isTypeSupported(mimeTypeForCodec(codecsForVideoContainer, 'video'))) || ['mp4a.40.2', 'fLaC'].some(codecForAudioContainer => mediaSource.isTypeSupported(mimeTypeForCodec(codecForAudioContainer, 'audio')))); -} -function changeTypeSupported() { - var _sourceBuffer$prototy; - const sourceBuffer = getSourceBuffer(); - return typeof (sourceBuffer == null ? void 0 : (_sourceBuffer$prototy = sourceBuffer.prototype) == null ? void 0 : _sourceBuffer$prototy.changeType) === 'function'; -} - -const STALL_MINIMUM_DURATION_MS = 250; -const MAX_START_GAP_JUMP = 2.0; -const SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1; -const SKIP_BUFFER_RANGE_START = 0.05; -class GapController { - constructor(config, media, fragmentTracker, hls) { - this.config = void 0; - this.media = null; - this.fragmentTracker = void 0; - this.hls = void 0; - this.nudgeRetry = 0; - this.stallReported = false; - this.stalled = null; - this.moved = false; - this.seeking = false; - this.config = config; - this.media = media; - this.fragmentTracker = fragmentTracker; - this.hls = hls; - } - destroy() { - this.media = null; - // @ts-ignore - this.hls = this.fragmentTracker = null; - } - - /** - * Checks if the playhead is stuck within a gap, and if so, attempts to free it. - * A gap is an unbuffered range between two buffered ranges (or the start and the first buffered range). - * - * @param lastCurrentTime - Previously read playhead position - */ - poll(lastCurrentTime, activeFrag) { - const { - config, - media, - stalled - } = this; - if (media === null) { - return; - } - const { - currentTime, - seeking - } = media; - const seeked = this.seeking && !seeking; - const beginSeek = !this.seeking && seeking; - this.seeking = seeking; - - // The playhead is moving, no-op - if (currentTime !== lastCurrentTime) { - this.moved = true; - if (!seeking) { - this.nudgeRetry = 0; - } - if (stalled !== null) { - // The playhead is now moving, but was previously stalled - if (this.stallReported) { - const _stalledDuration = self.performance.now() - stalled; - logger.warn(`playback not stuck anymore @${currentTime}, after ${Math.round(_stalledDuration)}ms`); - this.stallReported = false; - } - this.stalled = null; - } - return; - } - - // Clear stalled state when beginning or finishing seeking so that we don't report stalls coming out of a seek - if (beginSeek || seeked) { - this.stalled = null; - return; - } - - // The playhead should not be moving - if (media.paused && !seeking || media.ended || media.playbackRate === 0 || !BufferHelper.getBuffered(media).length) { - this.nudgeRetry = 0; - return; - } - const bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0); - const nextStart = bufferInfo.nextStart || 0; - if (seeking) { - // Waiting for seeking in a buffered range to complete - const hasEnoughBuffer = bufferInfo.len > MAX_START_GAP_JUMP; - // Next buffered range is too far ahead to jump to while still seeking - const noBufferGap = !nextStart || activeFrag && activeFrag.start <= currentTime || nextStart - currentTime > MAX_START_GAP_JUMP && !this.fragmentTracker.getPartialFragment(currentTime); - if (hasEnoughBuffer || noBufferGap) { - return; - } - // Reset moved state when seeking to a point in or before a gap - this.moved = false; - } - - // Skip start gaps if we haven't played, but the last poll detected the start of a stall - // The addition poll gives the browser a chance to jump the gap for us - if (!this.moved && this.stalled !== null) { - var _level$details; - // There is no playable buffer (seeked, waiting for buffer) - const isBuffered = bufferInfo.len > 0; - if (!isBuffered && !nextStart) { - return; - } - // Jump start gaps within jump threshold - const startJump = Math.max(nextStart, bufferInfo.start || 0) - currentTime; - - // When joining a live stream with audio tracks, account for live playlist window sliding by allowing - // a larger jump over start gaps caused by the audio-stream-controller buffering a start fragment - // that begins over 1 target duration after the video start position. - const level = this.hls.levels ? this.hls.levels[this.hls.currentLevel] : null; - const isLive = level == null ? void 0 : (_level$details = level.details) == null ? void 0 : _level$details.live; - const maxStartGapJump = isLive ? level.details.targetduration * 2 : MAX_START_GAP_JUMP; - const partialOrGap = this.fragmentTracker.getPartialFragment(currentTime); - if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) { - if (!media.paused) { - this._trySkipBufferHole(partialOrGap); - } - return; - } - } - - // Start tracking stall time - const tnow = self.performance.now(); - if (stalled === null) { - this.stalled = tnow; - return; - } - const stalledDuration = tnow - stalled; - if (!seeking && stalledDuration >= STALL_MINIMUM_DURATION_MS) { - // Report stalling after trying to fix - this._reportStall(bufferInfo); - if (!this.media) { - return; - } - } - const bufferedWithHoles = BufferHelper.bufferInfo(media, currentTime, config.maxBufferHole); - this._tryFixBufferStall(bufferedWithHoles, stalledDuration); - } - - /** - * Detects and attempts to fix known buffer stalling issues. - * @param bufferInfo - The properties of the current buffer. - * @param stalledDurationMs - The amount of time Hls.js has been stalling for. - * @private - */ - _tryFixBufferStall(bufferInfo, stalledDurationMs) { - const { - config, - fragmentTracker, - media - } = this; - if (media === null) { - return; - } - const currentTime = media.currentTime; - const partial = fragmentTracker.getPartialFragment(currentTime); - if (partial) { - // Try to skip over the buffer hole caused by a partial fragment - // This method isn't limited by the size of the gap between buffered ranges - const targetTime = this._trySkipBufferHole(partial); - // we return here in this case, meaning - // the branch below only executes when we haven't seeked to a new position - if (targetTime || !this.media) { - return; - } - } - - // if we haven't had to skip over a buffer hole of a partial fragment - // we may just have to "nudge" the playlist as the browser decoding/rendering engine - // needs to cross some sort of threshold covering all source-buffers content - // to start playing properly. - if ((bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) { - logger.warn('Trying to nudge playhead over buffer-hole'); - // Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds - // We only try to jump the hole if it's under the configured size - // Reset stalled so to rearm watchdog timer - this.stalled = null; - this._tryNudgeBuffer(); - } - } - - /** - * Triggers a BUFFER_STALLED_ERROR event, but only once per stall period. - * @param bufferLen - The playhead distance from the end of the current buffer segment. - * @private - */ - _reportStall(bufferInfo) { - const { - hls, - media, - stallReported - } = this; - if (!stallReported && media) { - // Report stalled error once - this.stallReported = true; - const error = new Error(`Playback stalling at @${media.currentTime} due to low buffer (${JSON.stringify(bufferInfo)})`); - logger.warn(error.message); - hls.trigger(Events.ERROR, { - type: ErrorTypes.MEDIA_ERROR, - details: ErrorDetails.BUFFER_STALLED_ERROR, - fatal: false, - error, - buffer: bufferInfo.len - }); - } - } - - /** - * Attempts to fix buffer stalls by jumping over known gaps caused by partial fragments - * @param partial - The partial fragment found at the current time (where playback is stalling). - * @private - */ - _trySkipBufferHole(partial) { - const { - config, - hls, - media - } = this; - if (media === null) { - return 0; - } - - // Check if currentTime is between unbuffered regions of partial fragments - const currentTime = media.currentTime; - const bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0); - const startTime = currentTime < bufferInfo.start ? bufferInfo.start : bufferInfo.nextStart; - if (startTime) { - const bufferStarved = bufferInfo.len <= config.maxBufferHole; - const waiting = bufferInfo.len > 0 && bufferInfo.len < 1 && media.readyState < 3; - const gapLength = startTime - currentTime; - if (gapLength > 0 && (bufferStarved || waiting)) { - // Only allow large gaps to be skipped if it is a start gap, or all fragments in skip range are partial - if (gapLength > config.maxBufferHole) { - const { - fragmentTracker - } = this; - let startGap = false; - if (currentTime === 0) { - const startFrag = fragmentTracker.getAppendedFrag(0, PlaylistLevelType.MAIN); - if (startFrag && startTime < startFrag.end) { - startGap = true; - } - } - if (!startGap) { - const startProvisioned = partial || fragmentTracker.getAppendedFrag(currentTime, PlaylistLevelType.MAIN); - if (startProvisioned) { - let moreToLoad = false; - let pos = startProvisioned.end; - while (pos < startTime) { - const provisioned = fragmentTracker.getPartialFragment(pos); - if (provisioned) { - pos += provisioned.duration; - } else { - moreToLoad = true; - break; - } - } - if (moreToLoad) { - return 0; - } - } - } - } - const targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS); - logger.warn(`skipping hole, adjusting currentTime from ${currentTime} to ${targetTime}`); - this.moved = true; - this.stalled = null; - media.currentTime = targetTime; - if (partial && !partial.gap) { - const error = new Error(`fragment loaded with buffer holes, seeking from ${currentTime} to ${targetTime}`); - hls.trigger(Events.ERROR, { - type: ErrorTypes.MEDIA_ERROR, - details: ErrorDetails.BUFFER_SEEK_OVER_HOLE, - fatal: false, - error, - reason: error.message, - frag: partial - }); - } - return targetTime; - } - } - return 0; - } - - /** - * Attempts to fix buffer stalls by advancing the mediaElement's current time by a small amount. - * @private - */ - _tryNudgeBuffer() { - const { - config, - hls, - media, - nudgeRetry - } = this; - if (media === null) { - return; - } - const currentTime = media.currentTime; - this.nudgeRetry++; - if (nudgeRetry < config.nudgeMaxRetry) { - const targetTime = currentTime + (nudgeRetry + 1) * config.nudgeOffset; - // playback stalled in buffered area ... let's nudge currentTime to try to overcome this - const error = new Error(`Nudging 'currentTime' from ${currentTime} to ${targetTime}`); - logger.warn(error.message); - media.currentTime = targetTime; - hls.trigger(Events.ERROR, { - type: ErrorTypes.MEDIA_ERROR, - details: ErrorDetails.BUFFER_NUDGE_ON_STALL, - error, - fatal: false - }); - } else { - const error = new Error(`Playhead still not moving while enough data buffered @${currentTime} after ${config.nudgeMaxRetry} nudges`); - logger.error(error.message); - hls.trigger(Events.ERROR, { - type: ErrorTypes.MEDIA_ERROR, - details: ErrorDetails.BUFFER_STALLED_ERROR, - error, - fatal: true - }); - } - } -} - -const TICK_INTERVAL = 100; // how often to tick in ms - -class StreamController extends BaseStreamController { - constructor(hls, fragmentTracker, keyLoader) { - super(hls, fragmentTracker, keyLoader, '[stream-controller]', PlaylistLevelType.MAIN); - this.audioCodecSwap = false; - this.gapController = null; - this.level = -1; - this._forceStartLoad = false; - this.altAudio = false; - this.audioOnly = false; - this.fragPlaying = null; - this.onvplaying = null; - this.onvseeked = null; - this.fragLastKbps = 0; - this.couldBacktrack = false; - this.backtrackFragment = null; - this.audioCodecSwitch = false; - this.videoBuffer = null; - this._registerListeners(); - } - _registerListeners() { - const { - hls - } = this; - hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this); - hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this); - hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this); - hls.on(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this); - hls.on(Events.ERROR, this.onError, this); - hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this); - hls.on(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this); - hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this); - hls.on(Events.BUFFER_FLUSHED, this.onBufferFlushed, this); - hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); - hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this); - } - _unregisterListeners() { - const { - hls - } = this; - hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this); - hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this); - hls.off(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this); - hls.off(Events.ERROR, this.onError, this); - hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this); - hls.off(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this); - hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this); - hls.off(Events.BUFFER_FLUSHED, this.onBufferFlushed, this); - hls.off(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); - hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this); - } - onHandlerDestroying() { - this._unregisterListeners(); - super.onHandlerDestroying(); - } - startLoad(startPosition) { - if (this.levels) { - const { - lastCurrentTime, - hls - } = this; - this.stopLoad(); - this.setInterval(TICK_INTERVAL); - this.level = -1; - if (!this.startFragRequested) { - // determine load level - let startLevel = hls.startLevel; - if (startLevel === -1) { - if (hls.config.testBandwidth && this.levels.length > 1) { - // -1 : guess start Level by doing a bitrate test by loading first fragment of lowest quality level - startLevel = 0; - this.bitrateTest = true; - } else { - startLevel = hls.firstAutoLevel; - } - } - // set new level to playlist loader : this will trigger start level load - // hls.nextLoadLevel remains until it is set to a new value or until a new frag is successfully loaded - hls.nextLoadLevel = startLevel; - this.level = hls.loadLevel; - this.loadedmetadata = false; - } - // if startPosition undefined but lastCurrentTime set, set startPosition to last currentTime - if (lastCurrentTime > 0 && startPosition === -1) { - this.log(`Override startPosition with lastCurrentTime @${lastCurrentTime.toFixed(3)}`); - startPosition = lastCurrentTime; - } - this.state = State.IDLE; - this.nextLoadPosition = this.startPosition = this.lastCurrentTime = startPosition; - this.tick(); - } else { - this._forceStartLoad = true; - this.state = State.STOPPED; - } - } - stopLoad() { - this._forceStartLoad = false; - super.stopLoad(); - } - doTick() { - switch (this.state) { - case State.WAITING_LEVEL: - { - const { - levels, - level - } = this; - const currentLevel = levels == null ? void 0 : levels[level]; - const details = currentLevel == null ? void 0 : currentLevel.details; - if (details && (!details.live || this.levelLastLoaded === currentLevel)) { - if (this.waitForCdnTuneIn(details)) { - break; - } - this.state = State.IDLE; - break; - } else if (this.hls.nextLoadLevel !== this.level) { - this.state = State.IDLE; - break; - } - break; - } - case State.FRAG_LOADING_WAITING_RETRY: - { - var _this$media; - const now = self.performance.now(); - const retryDate = this.retryDate; - // if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading - if (!retryDate || now >= retryDate || (_this$media = this.media) != null && _this$media.seeking) { - const { - levels, - level - } = this; - const currentLevel = levels == null ? void 0 : levels[level]; - this.resetStartWhenNotLoaded(currentLevel || null); - this.state = State.IDLE; - } - } - break; - } - if (this.state === State.IDLE) { - this.doTickIdle(); - } - this.onTickEnd(); - } - onTickEnd() { - super.onTickEnd(); - this.checkBuffer(); - this.checkFragmentChanged(); - } - doTickIdle() { - const { - hls, - levelLastLoaded, - levels, - media - } = this; - - // if start level not parsed yet OR - // if video not attached AND start fragment already requested OR start frag prefetch not enabled - // exit loop, as we either need more info (level not parsed) or we need media to be attached to load new fragment - if (levelLastLoaded === null || !media && (this.startFragRequested || !hls.config.startFragPrefetch)) { - return; - } - - // If the "main" level is audio-only but we are loading an alternate track in the same group, do not load anything - if (this.altAudio && this.audioOnly) { - return; - } - const level = hls.nextLoadLevel; - if (!(levels != null && levels[level])) { - return; - } - const levelInfo = levels[level]; - - // if buffer length is less than maxBufLen try to load a new fragment - - const bufferInfo = this.getMainFwdBufferInfo(); - if (bufferInfo === null) { - return; - } - const lastDetails = this.getLevelDetails(); - if (lastDetails && this._streamEnded(bufferInfo, lastDetails)) { - const data = {}; - if (this.altAudio) { - data.type = 'video'; - } - this.hls.trigger(Events.BUFFER_EOS, data); - this.state = State.ENDED; - return; - } - - // set next load level : this will trigger a playlist load if needed - if (hls.loadLevel !== level && hls.manualLevel === -1) { - this.log(`Adapting to level ${level} from level ${this.level}`); - } - this.level = hls.nextLoadLevel = level; - const levelDetails = levelInfo.details; - // if level info not retrieved yet, switch state and wait for level retrieval - // if live playlist, ensure that new playlist has been refreshed to avoid loading/try to load - // a useless and outdated fragment (that might even introduce load error if it is already out of the live playlist) - if (!levelDetails || this.state === State.WAITING_LEVEL || levelDetails.live && this.levelLastLoaded !== levelInfo) { - this.level = level; - this.state = State.WAITING_LEVEL; - return; - } - const bufferLen = bufferInfo.len; - - // compute max Buffer Length that we could get from this load level, based on level bitrate. don't buffer more than 60 MB and more than 30s - const maxBufLen = this.getMaxBufferLength(levelInfo.maxBitrate); - - // Stay idle if we are still with buffer margins - if (bufferLen >= maxBufLen) { - return; - } - if (this.backtrackFragment && this.backtrackFragment.start > bufferInfo.end) { - this.backtrackFragment = null; - } - const targetBufferTime = this.backtrackFragment ? this.backtrackFragment.start : bufferInfo.end; - let frag = this.getNextFragment(targetBufferTime, levelDetails); - // Avoid backtracking by loading an earlier segment in streams with segments that do not start with a key frame (flagged by `couldBacktrack`) - if (this.couldBacktrack && !this.fragPrevious && frag && frag.sn !== 'initSegment' && this.fragmentTracker.getState(frag) !== FragmentState.OK) { - var _this$backtrackFragme; - const backtrackSn = ((_this$backtrackFragme = this.backtrackFragment) != null ? _this$backtrackFragme : frag).sn; - const fragIdx = backtrackSn - levelDetails.startSN; - const backtrackFrag = levelDetails.fragments[fragIdx - 1]; - if (backtrackFrag && frag.cc === backtrackFrag.cc) { - frag = backtrackFrag; - this.fragmentTracker.removeFragment(backtrackFrag); - } - } else if (this.backtrackFragment && bufferInfo.len) { - this.backtrackFragment = null; - } - // Avoid loop loading by using nextLoadPosition set for backtracking and skipping consecutive GAP tags - if (frag && this.isLoopLoading(frag, targetBufferTime)) { - const gapStart = frag.gap; - if (!gapStart) { - // Cleanup the fragment tracker before trying to find the next unbuffered fragment - const type = this.audioOnly && !this.altAudio ? ElementaryStreamTypes.AUDIO : ElementaryStreamTypes.VIDEO; - const mediaBuffer = (type === ElementaryStreamTypes.VIDEO ? this.videoBuffer : this.mediaBuffer) || this.media; - if (mediaBuffer) { - this.afterBufferFlushed(mediaBuffer, type, PlaylistLevelType.MAIN); - } - } - frag = this.getNextFragmentLoopLoading(frag, levelDetails, bufferInfo, PlaylistLevelType.MAIN, maxBufLen); - } - if (!frag) { - return; - } - if (frag.initSegment && !frag.initSegment.data && !this.bitrateTest) { - frag = frag.initSegment; - } - this.loadFragment(frag, levelInfo, targetBufferTime); - } - loadFragment(frag, level, targetBufferTime) { - // Check if fragment is not loaded - const fragState = this.fragmentTracker.getState(frag); - this.fragCurrent = frag; - if (fragState === FragmentState.NOT_LOADED || fragState === FragmentState.PARTIAL) { - if (frag.sn === 'initSegment') { - this._loadInitSegment(frag, level); - } else if (this.bitrateTest) { - this.log(`Fragment ${frag.sn} of level ${frag.level} is being downloaded to test bitrate and will not be buffered`); - this._loadBitrateTestFrag(frag, level); - } else { - this.startFragRequested = true; - super.loadFragment(frag, level, targetBufferTime); - } - } else { - this.clearTrackerIfNeeded(frag); - } - } - getBufferedFrag(position) { - return this.fragmentTracker.getBufferedFrag(position, PlaylistLevelType.MAIN); - } - followingBufferedFrag(frag) { - if (frag) { - // try to get range of next fragment (500ms after this range) - return this.getBufferedFrag(frag.end + 0.5); - } - return null; - } - - /* - on immediate level switch : - - pause playback if playing - - cancel any pending load request - - and trigger a buffer flush - */ - immediateLevelSwitch() { - this.abortCurrentFrag(); - this.flushMainBuffer(0, Number.POSITIVE_INFINITY); - } - - /** - * try to switch ASAP without breaking video playback: - * in order to ensure smooth but quick level switching, - * we need to find the next flushable buffer range - * we should take into account new segment fetch time - */ - nextLevelSwitch() { - const { - levels, - media - } = this; - // ensure that media is defined and that metadata are available (to retrieve currentTime) - if (media != null && media.readyState) { - let fetchdelay; - const fragPlayingCurrent = this.getAppendedFrag(media.currentTime); - if (fragPlayingCurrent && fragPlayingCurrent.start > 1) { - // flush buffer preceding current fragment (flush until current fragment start offset) - // minus 1s to avoid video freezing, that could happen if we flush keyframe of current video ... - this.flushMainBuffer(0, fragPlayingCurrent.start - 1); - } - const levelDetails = this.getLevelDetails(); - if (levelDetails != null && levelDetails.live) { - const bufferInfo = this.getMainFwdBufferInfo(); - // Do not flush in live stream with low buffer - if (!bufferInfo || bufferInfo.len < levelDetails.targetduration * 2) { - return; - } - } - if (!media.paused && levels) { - // add a safety delay of 1s - const nextLevelId = this.hls.nextLoadLevel; - const nextLevel = levels[nextLevelId]; - const fragLastKbps = this.fragLastKbps; - if (fragLastKbps && this.fragCurrent) { - fetchdelay = this.fragCurrent.duration * nextLevel.maxBitrate / (1000 * fragLastKbps) + 1; - } else { - fetchdelay = 0; - } - } else { - fetchdelay = 0; - } - // this.log('fetchdelay:'+fetchdelay); - // find buffer range that will be reached once new fragment will be fetched - const bufferedFrag = this.getBufferedFrag(media.currentTime + fetchdelay); - if (bufferedFrag) { - // we can flush buffer range following this one without stalling playback - const nextBufferedFrag = this.followingBufferedFrag(bufferedFrag); - if (nextBufferedFrag) { - // if we are here, we can also cancel any loading/demuxing in progress, as they are useless - this.abortCurrentFrag(); - // start flush position is in next buffered frag. Leave some padding for non-independent segments and smoother playback. - const maxStart = nextBufferedFrag.maxStartPTS ? nextBufferedFrag.maxStartPTS : nextBufferedFrag.start; - const fragDuration = nextBufferedFrag.duration; - const startPts = Math.max(bufferedFrag.end, maxStart + Math.min(Math.max(fragDuration - this.config.maxFragLookUpTolerance, fragDuration * (this.couldBacktrack ? 0.5 : 0.125)), fragDuration * (this.couldBacktrack ? 0.75 : 0.25))); - this.flushMainBuffer(startPts, Number.POSITIVE_INFINITY); - } - } - } - } - abortCurrentFrag() { - const fragCurrent = this.fragCurrent; - this.fragCurrent = null; - this.backtrackFragment = null; - if (fragCurrent) { - fragCurrent.abortRequests(); - this.fragmentTracker.removeFragment(fragCurrent); - } - switch (this.state) { - case State.KEY_LOADING: - case State.FRAG_LOADING: - case State.FRAG_LOADING_WAITING_RETRY: - case State.PARSING: - case State.PARSED: - this.state = State.IDLE; - break; - } - this.nextLoadPosition = this.getLoadPosition(); - } - flushMainBuffer(startOffset, endOffset) { - super.flushMainBuffer(startOffset, endOffset, this.altAudio ? 'video' : null); - } - onMediaAttached(event, data) { - super.onMediaAttached(event, data); - const media = data.media; - this.onvplaying = this.onMediaPlaying.bind(this); - this.onvseeked = this.onMediaSeeked.bind(this); - media.addEventListener('playing', this.onvplaying); - media.addEventListener('seeked', this.onvseeked); - this.gapController = new GapController(this.config, media, this.fragmentTracker, this.hls); - } - onMediaDetaching() { - const { - media - } = this; - if (media && this.onvplaying && this.onvseeked) { - media.removeEventListener('playing', this.onvplaying); - media.removeEventListener('seeked', this.onvseeked); - this.onvplaying = this.onvseeked = null; - this.videoBuffer = null; - } - this.fragPlaying = null; - if (this.gapController) { - this.gapController.destroy(); - this.gapController = null; - } - super.onMediaDetaching(); - } - onMediaPlaying() { - // tick to speed up FRAG_CHANGED triggering - this.tick(); - } - onMediaSeeked() { - const media = this.media; - const currentTime = media ? media.currentTime : null; - if (isFiniteNumber(currentTime)) { - this.log(`Media seeked to ${currentTime.toFixed(3)}`); - } - - // If seeked was issued before buffer was appended do not tick immediately - const bufferInfo = this.getMainFwdBufferInfo(); - if (bufferInfo === null || bufferInfo.len === 0) { - this.warn(`Main forward buffer length on "seeked" event ${bufferInfo ? bufferInfo.len : 'empty'})`); - return; - } - - // tick to speed up FRAG_CHANGED triggering - this.tick(); - } - onManifestLoading() { - // reset buffer on manifest loading - this.log('Trigger BUFFER_RESET'); - this.hls.trigger(Events.BUFFER_RESET, undefined); - this.fragmentTracker.removeAllFragments(); - this.couldBacktrack = false; - this.startPosition = this.lastCurrentTime = this.fragLastKbps = 0; - this.levels = this.fragPlaying = this.backtrackFragment = this.levelLastLoaded = null; - this.altAudio = this.audioOnly = this.startFragRequested = false; - } - onManifestParsed(event, data) { - // detect if we have different kind of audio codecs used amongst playlists - let aac = false; - let heaac = false; - data.levels.forEach(level => { - const codec = level.audioCodec; - if (codec) { - aac = aac || codec.indexOf('mp4a.40.2') !== -1; - heaac = heaac || codec.indexOf('mp4a.40.5') !== -1; - } - }); - this.audioCodecSwitch = aac && heaac && !changeTypeSupported(); - if (this.audioCodecSwitch) { - this.log('Both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC'); - } - this.levels = data.levels; - this.startFragRequested = false; - } - onLevelLoading(event, data) { - const { - levels - } = this; - if (!levels || this.state !== State.IDLE) { - return; - } - const level = levels[data.level]; - if (!level.details || level.details.live && this.levelLastLoaded !== level || this.waitForCdnTuneIn(level.details)) { - this.state = State.WAITING_LEVEL; - } - } - onLevelLoaded(event, data) { - var _curLevel$details; - const { - levels - } = this; - const newLevelId = data.level; - const newDetails = data.details; - const duration = newDetails.totalduration; - if (!levels) { - this.warn(`Levels were reset while loading level ${newLevelId}`); - return; - } - this.log(`Level ${newLevelId} loaded [${newDetails.startSN},${newDetails.endSN}]${newDetails.lastPartSn ? `[part-${newDetails.lastPartSn}-${newDetails.lastPartIndex}]` : ''}, cc [${newDetails.startCC}, ${newDetails.endCC}] duration:${duration}`); - const curLevel = levels[newLevelId]; - const fragCurrent = this.fragCurrent; - if (fragCurrent && (this.state === State.FRAG_LOADING || this.state === State.FRAG_LOADING_WAITING_RETRY)) { - if (fragCurrent.level !== data.level && fragCurrent.loader) { - this.abortCurrentFrag(); - } - } - let sliding = 0; - if (newDetails.live || (_curLevel$details = curLevel.details) != null && _curLevel$details.live) { - var _this$levelLastLoaded; - this.checkLiveUpdate(newDetails); - if (newDetails.deltaUpdateFailed) { - return; - } - sliding = this.alignPlaylists(newDetails, curLevel.details, (_this$levelLastLoaded = this.levelLastLoaded) == null ? void 0 : _this$levelLastLoaded.details); - } - // override level info - curLevel.details = newDetails; - this.levelLastLoaded = curLevel; - this.hls.trigger(Events.LEVEL_UPDATED, { - details: newDetails, - level: newLevelId - }); - - // only switch back to IDLE state if we were waiting for level to start downloading a new fragment - if (this.state === State.WAITING_LEVEL) { - if (this.waitForCdnTuneIn(newDetails)) { - // Wait for Low-Latency CDN Tune-in - return; - } - this.state = State.IDLE; - } - if (!this.startFragRequested) { - this.setStartPosition(newDetails, sliding); - } else if (newDetails.live) { - this.synchronizeToLiveEdge(newDetails); - } - - // trigger handler right now - this.tick(); - } - _handleFragmentLoadProgress(data) { - var _frag$initSegment; - const { - frag, - part, - payload - } = data; - const { - levels - } = this; - if (!levels) { - this.warn(`Levels were reset while fragment load was in progress. Fragment ${frag.sn} of level ${frag.level} will not be buffered`); - return; - } - const currentLevel = levels[frag.level]; - const details = currentLevel.details; - if (!details) { - this.warn(`Dropping fragment ${frag.sn} of level ${frag.level} after level details were reset`); - this.fragmentTracker.removeFragment(frag); - return; - } - const videoCodec = currentLevel.videoCodec; - - // time Offset is accurate if level PTS is known, or if playlist is not sliding (not live) - const accurateTimeOffset = details.PTSKnown || !details.live; - const initSegmentData = (_frag$initSegment = frag.initSegment) == null ? void 0 : _frag$initSegment.data; - const audioCodec = this._getAudioCodec(currentLevel); - - // transmux the MPEG-TS data to ISO-BMFF segments - // this.log(`Transmuxing ${frag.sn} of [${details.startSN} ,${details.endSN}],level ${frag.level}, cc ${frag.cc}`); - const transmuxer = this.transmuxer = this.transmuxer || new TransmuxerInterface(this.hls, PlaylistLevelType.MAIN, this._handleTransmuxComplete.bind(this), this._handleTransmuxerFlush.bind(this)); - const partIndex = part ? part.index : -1; - const partial = partIndex !== -1; - const chunkMeta = new ChunkMetadata(frag.level, frag.sn, frag.stats.chunkCount, payload.byteLength, partIndex, partial); - const initPTS = this.initPTS[frag.cc]; - transmuxer.push(payload, initSegmentData, audioCodec, videoCodec, frag, part, details.totalduration, accurateTimeOffset, chunkMeta, initPTS); - } - onAudioTrackSwitching(event, data) { - // if any URL found on new audio track, it is an alternate audio track - const fromAltAudio = this.altAudio; - const altAudio = !!data.url; - // if we switch on main audio, ensure that main fragment scheduling is synced with media.buffered - // don't do anything if we switch to alt audio: audio stream controller is handling it. - // we will just have to change buffer scheduling on audioTrackSwitched - if (!altAudio) { - if (this.mediaBuffer !== this.media) { - this.log('Switching on main audio, use media.buffered to schedule main fragment loading'); - this.mediaBuffer = this.media; - const fragCurrent = this.fragCurrent; - // we need to refill audio buffer from main: cancel any frag loading to speed up audio switch - if (fragCurrent) { - this.log('Switching to main audio track, cancel main fragment load'); - fragCurrent.abortRequests(); - this.fragmentTracker.removeFragment(fragCurrent); - } - // destroy transmuxer to force init segment generation (following audio switch) - this.resetTransmuxer(); - // switch to IDLE state to load new fragment - this.resetLoadingState(); - } else if (this.audioOnly) { - // Reset audio transmuxer so when switching back to main audio we're not still appending where we left off - this.resetTransmuxer(); - } - const hls = this.hls; - // If switching from alt to main audio, flush all audio and trigger track switched - if (fromAltAudio) { - hls.trigger(Events.BUFFER_FLUSHING, { - startOffset: 0, - endOffset: Number.POSITIVE_INFINITY, - type: null - }); - this.fragmentTracker.removeAllFragments(); - } - hls.trigger(Events.AUDIO_TRACK_SWITCHED, data); - } - } - onAudioTrackSwitched(event, data) { - const trackId = data.id; - const altAudio = !!this.hls.audioTracks[trackId].url; - if (altAudio) { - const videoBuffer = this.videoBuffer; - // if we switched on alternate audio, ensure that main fragment scheduling is synced with video sourcebuffer buffered - if (videoBuffer && this.mediaBuffer !== videoBuffer) { - this.log('Switching on alternate audio, use video.buffered to schedule main fragment loading'); - this.mediaBuffer = videoBuffer; - } - } - this.altAudio = altAudio; - this.tick(); - } - onBufferCreated(event, data) { - const tracks = data.tracks; - let mediaTrack; - let name; - let alternate = false; - for (const type in tracks) { - const track = tracks[type]; - if (track.id === 'main') { - name = type; - mediaTrack = track; - // keep video source buffer reference - if (type === 'video') { - const videoTrack = tracks[type]; - if (videoTrack) { - this.videoBuffer = videoTrack.buffer; - } - } - } else { - alternate = true; - } - } - if (alternate && mediaTrack) { - this.log(`Alternate track found, use ${name}.buffered to schedule main fragment loading`); - this.mediaBuffer = mediaTrack.buffer; - } else { - this.mediaBuffer = this.media; - } - } - onFragBuffered(event, data) { - const { - frag, - part - } = data; - if (frag && frag.type !== PlaylistLevelType.MAIN) { - return; - } - if (this.fragContextChanged(frag)) { - // If a level switch was requested while a fragment was buffering, it will emit the FRAG_BUFFERED event upon completion - // Avoid setting state back to IDLE, since that will interfere with a level switch - this.warn(`Fragment ${frag.sn}${part ? ' p: ' + part.index : ''} of level ${frag.level} finished buffering, but was aborted. state: ${this.state}`); - if (this.state === State.PARSED) { - this.state = State.IDLE; - } - return; - } - const stats = part ? part.stats : frag.stats; - this.fragLastKbps = Math.round(8 * stats.total / (stats.buffering.end - stats.loading.first)); - if (frag.sn !== 'initSegment') { - this.fragPrevious = frag; - } - this.fragBufferedComplete(frag, part); - } - onError(event, data) { - var _data$context; - if (data.fatal) { - this.state = State.ERROR; - return; - } - switch (data.details) { - case ErrorDetails.FRAG_GAP: - case ErrorDetails.FRAG_PARSING_ERROR: - case ErrorDetails.FRAG_DECRYPT_ERROR: - case ErrorDetails.FRAG_LOAD_ERROR: - case ErrorDetails.FRAG_LOAD_TIMEOUT: - case ErrorDetails.KEY_LOAD_ERROR: - case ErrorDetails.KEY_LOAD_TIMEOUT: - this.onFragmentOrKeyLoadError(PlaylistLevelType.MAIN, data); - break; - case ErrorDetails.LEVEL_LOAD_ERROR: - case ErrorDetails.LEVEL_LOAD_TIMEOUT: - case ErrorDetails.LEVEL_PARSING_ERROR: - // in case of non fatal error while loading level, if level controller is not retrying to load level, switch back to IDLE - if (!data.levelRetry && this.state === State.WAITING_LEVEL && ((_data$context = data.context) == null ? void 0 : _data$context.type) === PlaylistContextType.LEVEL) { - this.state = State.IDLE; - } - break; - case ErrorDetails.BUFFER_APPEND_ERROR: - case ErrorDetails.BUFFER_FULL_ERROR: - if (!data.parent || data.parent !== 'main') { - return; - } - if (data.details === ErrorDetails.BUFFER_APPEND_ERROR) { - this.resetLoadingState(); - return; - } - if (this.reduceLengthAndFlushBuffer(data)) { - this.flushMainBuffer(0, Number.POSITIVE_INFINITY); - } - break; - case ErrorDetails.INTERNAL_EXCEPTION: - this.recoverWorkerError(data); - break; - } - } - - // Checks the health of the buffer and attempts to resolve playback stalls. - checkBuffer() { - const { - media, - gapController - } = this; - if (!media || !gapController || !media.readyState) { - // Exit early if we don't have media or if the media hasn't buffered anything yet (readyState 0) - return; - } - if (this.loadedmetadata || !BufferHelper.getBuffered(media).length) { - // Resolve gaps using the main buffer, whose ranges are the intersections of the A/V sourcebuffers - const activeFrag = this.state !== State.IDLE ? this.fragCurrent : null; - gapController.poll(this.lastCurrentTime, activeFrag); - } - this.lastCurrentTime = media.currentTime; - } - onFragLoadEmergencyAborted() { - this.state = State.IDLE; - // if loadedmetadata is not set, it means that we are emergency switch down on first frag - // in that case, reset startFragRequested flag - if (!this.loadedmetadata) { - this.startFragRequested = false; - this.nextLoadPosition = this.startPosition; - } - this.tickImmediate(); - } - onBufferFlushed(event, { - type - }) { - if (type !== ElementaryStreamTypes.AUDIO || this.audioOnly && !this.altAudio) { - const mediaBuffer = (type === ElementaryStreamTypes.VIDEO ? this.videoBuffer : this.mediaBuffer) || this.media; - this.afterBufferFlushed(mediaBuffer, type, PlaylistLevelType.MAIN); - this.tick(); - } - } - onLevelsUpdated(event, data) { - if (this.level > -1 && this.fragCurrent) { - this.level = this.fragCurrent.level; - } - this.levels = data.levels; - } - swapAudioCodec() { - this.audioCodecSwap = !this.audioCodecSwap; - } - - /** - * Seeks to the set startPosition if not equal to the mediaElement's current time. - */ - seekToStartPos() { - const { - media - } = this; - if (!media) { - return; - } - const currentTime = media.currentTime; - let startPosition = this.startPosition; - // only adjust currentTime if different from startPosition or if startPosition not buffered - // at that stage, there should be only one buffered range, as we reach that code after first fragment has been buffered - if (startPosition >= 0 && currentTime < startPosition) { - if (media.seeking) { - this.log(`could not seek to ${startPosition}, already seeking at ${currentTime}`); - return; - } - const buffered = BufferHelper.getBuffered(media); - const bufferStart = buffered.length ? buffered.start(0) : 0; - const delta = bufferStart - startPosition; - if (delta > 0 && (delta < this.config.maxBufferHole || delta < this.config.maxFragLookUpTolerance)) { - this.log(`adjusting start position by ${delta} to match buffer start`); - startPosition += delta; - this.startPosition = startPosition; - } - this.log(`seek to target start position ${startPosition} from current time ${currentTime}`); - media.currentTime = startPosition; - } - } - _getAudioCodec(currentLevel) { - let audioCodec = this.config.defaultAudioCodec || currentLevel.audioCodec; - if (this.audioCodecSwap && audioCodec) { - this.log('Swapping audio codec'); - if (audioCodec.indexOf('mp4a.40.5') !== -1) { - audioCodec = 'mp4a.40.2'; - } else { - audioCodec = 'mp4a.40.5'; - } - } - return audioCodec; - } - _loadBitrateTestFrag(frag, level) { - frag.bitrateTest = true; - this._doFragLoad(frag, level).then(data => { - const { - hls - } = this; - if (!data || this.fragContextChanged(frag)) { - return; - } - level.fragmentError = 0; - this.state = State.IDLE; - this.startFragRequested = false; - this.bitrateTest = false; - const stats = frag.stats; - // Bitrate tests fragments are neither parsed nor buffered - stats.parsing.start = stats.parsing.end = stats.buffering.start = stats.buffering.end = self.performance.now(); - hls.trigger(Events.FRAG_LOADED, data); - frag.bitrateTest = false; - }); - } - _handleTransmuxComplete(transmuxResult) { - var _id3$samples; - const id = 'main'; - const { - hls - } = this; - const { - remuxResult, - chunkMeta - } = transmuxResult; - const context = this.getCurrentContext(chunkMeta); - if (!context) { - this.resetWhenMissingContext(chunkMeta); - return; - } - const { - frag, - part, - level - } = context; - const { - video, - text, - id3, - initSegment - } = remuxResult; - const { - details - } = level; - // The audio-stream-controller handles audio buffering if Hls.js is playing an alternate audio track - const audio = this.altAudio ? undefined : remuxResult.audio; - - // Check if the current fragment has been aborted. We check this by first seeing if we're still playing the current level. - // If we are, subsequently check if the currently loading fragment (fragCurrent) has changed. - if (this.fragContextChanged(frag)) { - this.fragmentTracker.removeFragment(frag); - return; - } - this.state = State.PARSING; - if (initSegment) { - if (initSegment != null && initSegment.tracks) { - const mapFragment = frag.initSegment || frag; - this._bufferInitSegment(level, initSegment.tracks, mapFragment, chunkMeta); - hls.trigger(Events.FRAG_PARSING_INIT_SEGMENT, { - frag: mapFragment, - id, - tracks: initSegment.tracks - }); - } - - // This would be nice if Number.isFinite acted as a typeguard, but it doesn't. See: https://github.com/Microsoft/TypeScript/issues/10038 - const initPTS = initSegment.initPTS; - const timescale = initSegment.timescale; - if (isFiniteNumber(initPTS)) { - this.initPTS[frag.cc] = { - baseTime: initPTS, - timescale - }; - hls.trigger(Events.INIT_PTS_FOUND, { - frag, - id, - initPTS, - timescale - }); - } - } - - // Avoid buffering if backtracking this fragment - if (video && details && frag.sn !== 'initSegment') { - const prevFrag = details.fragments[frag.sn - 1 - details.startSN]; - const isFirstFragment = frag.sn === details.startSN; - const isFirstInDiscontinuity = !prevFrag || frag.cc > prevFrag.cc; - if (remuxResult.independent !== false) { - const { - startPTS, - endPTS, - startDTS, - endDTS - } = video; - if (part) { - part.elementaryStreams[video.type] = { - startPTS, - endPTS, - startDTS, - endDTS - }; - } else { - if (video.firstKeyFrame && video.independent && chunkMeta.id === 1 && !isFirstInDiscontinuity) { - this.couldBacktrack = true; - } - if (video.dropped && video.independent) { - // Backtrack if dropped frames create a gap after currentTime - - const bufferInfo = this.getMainFwdBufferInfo(); - const targetBufferTime = (bufferInfo ? bufferInfo.end : this.getLoadPosition()) + this.config.maxBufferHole; - const startTime = video.firstKeyFramePTS ? video.firstKeyFramePTS : startPTS; - if (!isFirstFragment && targetBufferTime < startTime - this.config.maxBufferHole && !isFirstInDiscontinuity) { - this.backtrack(frag); - return; - } else if (isFirstInDiscontinuity) { - // Mark segment with a gap to avoid loop loading - frag.gap = true; - } - // Set video stream start to fragment start so that truncated samples do not distort the timeline, and mark it partial - frag.setElementaryStreamInfo(video.type, frag.start, endPTS, frag.start, endDTS, true); - } else if (isFirstFragment && startPTS > MAX_START_GAP_JUMP) { - // Mark segment with a gap to skip large start gap - frag.gap = true; - } - } - frag.setElementaryStreamInfo(video.type, startPTS, endPTS, startDTS, endDTS); - if (this.backtrackFragment) { - this.backtrackFragment = frag; - } - this.bufferFragmentData(video, frag, part, chunkMeta, isFirstFragment || isFirstInDiscontinuity); - } else if (isFirstFragment || isFirstInDiscontinuity) { - // Mark segment with a gap to avoid loop loading - frag.gap = true; - } else { - this.backtrack(frag); - return; - } - } - if (audio) { - const { - startPTS, - endPTS, - startDTS, - endDTS - } = audio; - if (part) { - part.elementaryStreams[ElementaryStreamTypes.AUDIO] = { - startPTS, - endPTS, - startDTS, - endDTS - }; - } - frag.setElementaryStreamInfo(ElementaryStreamTypes.AUDIO, startPTS, endPTS, startDTS, endDTS); - this.bufferFragmentData(audio, frag, part, chunkMeta); - } - if (details && id3 != null && (_id3$samples = id3.samples) != null && _id3$samples.length) { - const emittedID3 = { - id, - frag, - details, - samples: id3.samples - }; - hls.trigger(Events.FRAG_PARSING_METADATA, emittedID3); - } - if (details && text) { - const emittedText = { - id, - frag, - details, - samples: text.samples - }; - hls.trigger(Events.FRAG_PARSING_USERDATA, emittedText); - } - } - _bufferInitSegment(currentLevel, tracks, frag, chunkMeta) { - if (this.state !== State.PARSING) { - return; - } - this.audioOnly = !!tracks.audio && !tracks.video; - - // if audio track is expected to come from audio stream controller, discard any coming from main - if (this.altAudio && !this.audioOnly) { - delete tracks.audio; - } - // include levelCodec in audio and video tracks - const { - audio, - video, - audiovideo - } = tracks; - if (audio) { - let audioCodec = currentLevel.audioCodec; - const ua = navigator.userAgent.toLowerCase(); - if (this.audioCodecSwitch) { - if (audioCodec) { - if (audioCodec.indexOf('mp4a.40.5') !== -1) { - audioCodec = 'mp4a.40.2'; - } else { - audioCodec = 'mp4a.40.5'; - } - } - // In the case that AAC and HE-AAC audio codecs are signalled in manifest, - // force HE-AAC, as it seems that most browsers prefers it. - // don't force HE-AAC if mono stream, or in Firefox - const audioMetadata = audio.metadata; - if (audioMetadata && 'channelCount' in audioMetadata && (audioMetadata.channelCount || 1) !== 1 && ua.indexOf('firefox') === -1) { - audioCodec = 'mp4a.40.5'; - } - } - // HE-AAC is broken on Android, always signal audio codec as AAC even if variant manifest states otherwise - if (audioCodec && audioCodec.indexOf('mp4a.40.5') !== -1 && ua.indexOf('android') !== -1 && audio.container !== 'audio/mpeg') { - // Exclude mpeg audio - audioCodec = 'mp4a.40.2'; - this.log(`Android: force audio codec to ${audioCodec}`); - } - if (currentLevel.audioCodec && currentLevel.audioCodec !== audioCodec) { - this.log(`Swapping manifest audio codec "${currentLevel.audioCodec}" for "${audioCodec}"`); - } - audio.levelCodec = audioCodec; - audio.id = 'main'; - this.log(`Init audio buffer, container:${audio.container}, codecs[selected/level/parsed]=[${audioCodec || ''}/${currentLevel.audioCodec || ''}/${audio.codec}]`); - } - if (video) { - video.levelCodec = currentLevel.videoCodec; - video.id = 'main'; - this.log(`Init video buffer, container:${video.container}, codecs[level/parsed]=[${currentLevel.videoCodec || ''}/${video.codec}]`); - } - if (audiovideo) { - this.log(`Init audiovideo buffer, container:${audiovideo.container}, codecs[level/parsed]=[${currentLevel.codecs}/${audiovideo.codec}]`); - } - this.hls.trigger(Events.BUFFER_CODECS, tracks); - // loop through tracks that are going to be provided to bufferController - Object.keys(tracks).forEach(trackName => { - const track = tracks[trackName]; - const initSegment = track.initSegment; - if (initSegment != null && initSegment.byteLength) { - this.hls.trigger(Events.BUFFER_APPENDING, { - type: trackName, - data: initSegment, - frag, - part: null, - chunkMeta, - parent: frag.type - }); - } - }); - // trigger handler right now - this.tickImmediate(); - } - getMainFwdBufferInfo() { - return this.getFwdBufferInfo(this.mediaBuffer ? this.mediaBuffer : this.media, PlaylistLevelType.MAIN); - } - backtrack(frag) { - this.couldBacktrack = true; - // Causes findFragments to backtrack through fragments to find the keyframe - this.backtrackFragment = frag; - this.resetTransmuxer(); - this.flushBufferGap(frag); - this.fragmentTracker.removeFragment(frag); - this.fragPrevious = null; - this.nextLoadPosition = frag.start; - this.state = State.IDLE; - } - checkFragmentChanged() { - const video = this.media; - let fragPlayingCurrent = null; - if (video && video.readyState > 1 && video.seeking === false) { - const currentTime = video.currentTime; - /* if video element is in seeked state, currentTime can only increase. - (assuming that playback rate is positive ...) - As sometimes currentTime jumps back to zero after a - media decode error, check this, to avoid seeking back to - wrong position after a media decode error - */ - - if (BufferHelper.isBuffered(video, currentTime)) { - fragPlayingCurrent = this.getAppendedFrag(currentTime); - } else if (BufferHelper.isBuffered(video, currentTime + 0.1)) { - /* ensure that FRAG_CHANGED event is triggered at startup, - when first video frame is displayed and playback is paused. - add a tolerance of 100ms, in case current position is not buffered, - check if current pos+100ms is buffered and use that buffer range - for FRAG_CHANGED event reporting */ - fragPlayingCurrent = this.getAppendedFrag(currentTime + 0.1); - } - if (fragPlayingCurrent) { - this.backtrackFragment = null; - const fragPlaying = this.fragPlaying; - const fragCurrentLevel = fragPlayingCurrent.level; - if (!fragPlaying || fragPlayingCurrent.sn !== fragPlaying.sn || fragPlaying.level !== fragCurrentLevel) { - this.fragPlaying = fragPlayingCurrent; - this.hls.trigger(Events.FRAG_CHANGED, { - frag: fragPlayingCurrent - }); - if (!fragPlaying || fragPlaying.level !== fragCurrentLevel) { - this.hls.trigger(Events.LEVEL_SWITCHED, { - level: fragCurrentLevel - }); - } - } - } - } - } - get nextLevel() { - const frag = this.nextBufferedFrag; - if (frag) { - return frag.level; - } - return -1; - } - get currentFrag() { - const media = this.media; - if (media) { - return this.fragPlaying || this.getAppendedFrag(media.currentTime); - } - return null; - } - get currentProgramDateTime() { - const media = this.media; - if (media) { - const currentTime = media.currentTime; - const frag = this.currentFrag; - if (frag && isFiniteNumber(currentTime) && isFiniteNumber(frag.programDateTime)) { - const epocMs = frag.programDateTime + (currentTime - frag.start) * 1000; - return new Date(epocMs); - } - } - return null; - } - get currentLevel() { - const frag = this.currentFrag; - if (frag) { - return frag.level; - } - return -1; - } - get nextBufferedFrag() { - const frag = this.currentFrag; - if (frag) { - return this.followingBufferedFrag(frag); - } - return null; - } - get forceStartLoad() { - return this._forceStartLoad; - } -} - -/** - * The `Hls` class is the core of the HLS.js library used to instantiate player instances. - * @public - */ -class Hls { - /** - * Get the video-dev/hls.js package version. - */ - static get version() { - return "1.5.15"; - } - - /** - * Check if the required MediaSource Extensions are available. - */ - static isMSESupported() { - return isMSESupported(); - } - - /** - * Check if MediaSource Extensions are available and isTypeSupported checks pass for any baseline codecs. - */ - static isSupported() { - return isSupported(); - } - - /** - * Get the MediaSource global used for MSE playback (ManagedMediaSource, MediaSource, or WebKitMediaSource). - */ - static getMediaSource() { - return getMediaSource(); - } - static get Events() { - return Events; - } - static get ErrorTypes() { - return ErrorTypes; - } - static get ErrorDetails() { - return ErrorDetails; - } - - /** - * Get the default configuration applied to new instances. - */ - static get DefaultConfig() { - if (!Hls.defaultConfig) { - return hlsDefaultConfig; - } - return Hls.defaultConfig; - } - - /** - * Replace the default configuration applied to new instances. - */ - static set DefaultConfig(defaultConfig) { - Hls.defaultConfig = defaultConfig; - } - - /** - * Creates an instance of an HLS client that can attach to exactly one `HTMLMediaElement`. - * @param userConfig - Configuration options applied over `Hls.DefaultConfig` - */ - constructor(userConfig = {}) { - /** - * The runtime configuration used by the player. At instantiation this is combination of `hls.userConfig` merged over `Hls.DefaultConfig`. - */ - this.config = void 0; - /** - * The configuration object provided on player instantiation. - */ - this.userConfig = void 0; - this.coreComponents = void 0; - this.networkControllers = void 0; - this.started = false; - this._emitter = new EventEmitter(); - this._autoLevelCapping = -1; - this._maxHdcpLevel = null; - this.abrController = void 0; - this.bufferController = void 0; - this.capLevelController = void 0; - this.latencyController = void 0; - this.levelController = void 0; - this.streamController = void 0; - this.audioTrackController = void 0; - this.subtitleTrackController = void 0; - this.emeController = void 0; - this.cmcdController = void 0; - this._media = null; - this.url = null; - this.triggeringException = void 0; - enableLogs(userConfig.debug || false, 'Hls instance'); - const config = this.config = mergeConfig(Hls.DefaultConfig, userConfig); - this.userConfig = userConfig; - if (config.progressive) { - enableStreamingMode(config); - } - - // core controllers and network loaders - const { - abrController: ConfigAbrController, - bufferController: ConfigBufferController, - capLevelController: ConfigCapLevelController, - errorController: ConfigErrorController, - fpsController: ConfigFpsController - } = config; - const errorController = new ConfigErrorController(this); - const abrController = this.abrController = new ConfigAbrController(this); - const bufferController = this.bufferController = new ConfigBufferController(this); - const capLevelController = this.capLevelController = new ConfigCapLevelController(this); - const fpsController = new ConfigFpsController(this); - const playListLoader = new PlaylistLoader(this); - const id3TrackController = new ID3TrackController(this); - const ConfigContentSteeringController = config.contentSteeringController; - // ConentSteeringController is defined before LevelController to receive Multivariant Playlist events first - const contentSteering = ConfigContentSteeringController ? new ConfigContentSteeringController(this) : null; - const levelController = this.levelController = new LevelController(this, contentSteering); - // FragmentTracker must be defined before StreamController because the order of event handling is important - const fragmentTracker = new FragmentTracker(this); - const keyLoader = new KeyLoader(this.config); - const streamController = this.streamController = new StreamController(this, fragmentTracker, keyLoader); - - // Cap level controller uses streamController to flush the buffer - capLevelController.setStreamController(streamController); - // fpsController uses streamController to switch when frames are being dropped - fpsController.setStreamController(streamController); - const networkControllers = [playListLoader, levelController, streamController]; - if (contentSteering) { - networkControllers.splice(1, 0, contentSteering); - } - this.networkControllers = networkControllers; - const coreComponents = [abrController, bufferController, capLevelController, fpsController, id3TrackController, fragmentTracker]; - this.audioTrackController = this.createController(config.audioTrackController, networkControllers); - const AudioStreamControllerClass = config.audioStreamController; - if (AudioStreamControllerClass) { - networkControllers.push(new AudioStreamControllerClass(this, fragmentTracker, keyLoader)); - } - // subtitleTrackController must be defined before subtitleStreamController because the order of event handling is important - this.subtitleTrackController = this.createController(config.subtitleTrackController, networkControllers); - const SubtitleStreamControllerClass = config.subtitleStreamController; - if (SubtitleStreamControllerClass) { - networkControllers.push(new SubtitleStreamControllerClass(this, fragmentTracker, keyLoader)); - } - this.createController(config.timelineController, coreComponents); - keyLoader.emeController = this.emeController = this.createController(config.emeController, coreComponents); - this.cmcdController = this.createController(config.cmcdController, coreComponents); - this.latencyController = this.createController(LatencyController, coreComponents); - this.coreComponents = coreComponents; - - // Error controller handles errors before and after all other controllers - // This listener will be invoked after all other controllers error listeners - networkControllers.push(errorController); - const onErrorOut = errorController.onErrorOut; - if (typeof onErrorOut === 'function') { - this.on(Events.ERROR, onErrorOut, errorController); - } - } - createController(ControllerClass, components) { - if (ControllerClass) { - const controllerInstance = new ControllerClass(this); - if (components) { - components.push(controllerInstance); - } - return controllerInstance; - } - return null; - } - - // Delegate the EventEmitter through the public API of Hls.js - on(event, listener, context = this) { - this._emitter.on(event, listener, context); - } - once(event, listener, context = this) { - this._emitter.once(event, listener, context); - } - removeAllListeners(event) { - this._emitter.removeAllListeners(event); - } - off(event, listener, context = this, once) { - this._emitter.off(event, listener, context, once); - } - listeners(event) { - return this._emitter.listeners(event); - } - emit(event, name, eventObject) { - return this._emitter.emit(event, name, eventObject); - } - trigger(event, eventObject) { - if (this.config.debug) { - return this.emit(event, event, eventObject); - } else { - try { - return this.emit(event, event, eventObject); - } catch (error) { - logger.error('An internal error happened while handling event ' + event + '. Error message: "' + error.message + '". Here is a stacktrace:', error); - // Prevent recursion in error event handlers that throw #5497 - if (!this.triggeringException) { - this.triggeringException = true; - const fatal = event === Events.ERROR; - this.trigger(Events.ERROR, { - type: ErrorTypes.OTHER_ERROR, - details: ErrorDetails.INTERNAL_EXCEPTION, - fatal, - event, - error - }); - this.triggeringException = false; - } - } - } - return false; - } - listenerCount(event) { - return this._emitter.listenerCount(event); - } - - /** - * Dispose of the instance - */ - destroy() { - logger.log('destroy'); - this.trigger(Events.DESTROYING, undefined); - this.detachMedia(); - this.removeAllListeners(); - this._autoLevelCapping = -1; - this.url = null; - this.networkControllers.forEach(component => component.destroy()); - this.networkControllers.length = 0; - this.coreComponents.forEach(component => component.destroy()); - this.coreComponents.length = 0; - // Remove any references that could be held in config options or callbacks - const config = this.config; - config.xhrSetup = config.fetchSetup = undefined; - // @ts-ignore - this.userConfig = null; - } - - /** - * Attaches Hls.js to a media element - */ - attachMedia(media) { - logger.log('attachMedia'); - this._media = media; - this.trigger(Events.MEDIA_ATTACHING, { - media: media - }); - } - - /** - * Detach Hls.js from the media - */ - detachMedia() { - logger.log('detachMedia'); - this.trigger(Events.MEDIA_DETACHING, undefined); - this._media = null; - } - - /** - * Set the source URL. Can be relative or absolute. - */ - loadSource(url) { - this.stopLoad(); - const media = this.media; - const loadedSource = this.url; - const loadingSource = this.url = urlToolkitExports.buildAbsoluteURL(self.location.href, url, { - alwaysNormalize: true - }); - this._autoLevelCapping = -1; - this._maxHdcpLevel = null; - logger.log(`loadSource:${loadingSource}`); - if (media && loadedSource && (loadedSource !== loadingSource || this.bufferController.hasSourceTypes())) { - this.detachMedia(); - this.attachMedia(media); - } - // when attaching to a source URL, trigger a playlist load - this.trigger(Events.MANIFEST_LOADING, { - url: url - }); - } - - /** - * Start loading data from the stream source. - * Depending on default config, client starts loading automatically when a source is set. - * - * @param startPosition - Set the start position to stream from. - * Defaults to -1 (None: starts from earliest point) - */ - startLoad(startPosition = -1) { - logger.log(`startLoad(${startPosition})`); - this.started = true; - this.networkControllers.forEach(controller => { - controller.startLoad(startPosition); - }); - } - - /** - * Stop loading of any stream data. - */ - stopLoad() { - logger.log('stopLoad'); - this.started = false; - this.networkControllers.forEach(controller => { - controller.stopLoad(); - }); - } - - /** - * Resumes stream controller segment loading if previously started. - */ - resumeBuffering() { - if (this.started) { - this.networkControllers.forEach(controller => { - if ('fragmentLoader' in controller) { - controller.startLoad(-1); - } - }); - } - } - - /** - * Stops stream controller segment loading without changing 'started' state like stopLoad(). - * This allows for media buffering to be paused without interupting playlist loading. - */ - pauseBuffering() { - this.networkControllers.forEach(controller => { - if ('fragmentLoader' in controller) { - controller.stopLoad(); - } - }); - } - - /** - * Swap through possible audio codecs in the stream (for example to switch from stereo to 5.1) - */ - swapAudioCodec() { - logger.log('swapAudioCodec'); - this.streamController.swapAudioCodec(); - } - - /** - * When the media-element fails, this allows to detach and then re-attach it - * as one call (convenience method). - * - * Automatic recovery of media-errors by this process is configurable. - */ - recoverMediaError() { - logger.log('recoverMediaError'); - const media = this._media; - this.detachMedia(); - if (media) { - this.attachMedia(media); - } - } - removeLevel(levelIndex) { - this.levelController.removeLevel(levelIndex); - } - - /** - * @returns an array of levels (variants) sorted by HDCP-LEVEL, RESOLUTION (height), FRAME-RATE, CODECS, VIDEO-RANGE, and BANDWIDTH - */ - get levels() { - const levels = this.levelController.levels; - return levels ? levels : []; - } - - /** - * Index of quality level (variant) currently played - */ - get currentLevel() { - return this.streamController.currentLevel; - } - - /** - * Set quality level index immediately. This will flush the current buffer to replace the quality asap. That means playback will interrupt at least shortly to re-buffer and re-sync eventually. Set to -1 for automatic level selection. - */ - set currentLevel(newLevel) { - logger.log(`set currentLevel:${newLevel}`); - this.levelController.manualLevel = newLevel; - this.streamController.immediateLevelSwitch(); - } - - /** - * Index of next quality level loaded as scheduled by stream controller. - */ - get nextLevel() { - return this.streamController.nextLevel; - } - - /** - * Set quality level index for next loaded data. - * This will switch the video quality asap, without interrupting playback. - * May abort current loading of data, and flush parts of buffer (outside currently played fragment region). - * @param newLevel - Pass -1 for automatic level selection - */ - set nextLevel(newLevel) { - logger.log(`set nextLevel:${newLevel}`); - this.levelController.manualLevel = newLevel; - this.streamController.nextLevelSwitch(); - } - - /** - * Return the quality level of the currently or last (of none is loaded currently) segment - */ - get loadLevel() { - return this.levelController.level; - } - - /** - * Set quality level index for next loaded data in a conservative way. - * This will switch the quality without flushing, but interrupt current loading. - * Thus the moment when the quality switch will appear in effect will only be after the already existing buffer. - * @param newLevel - Pass -1 for automatic level selection - */ - set loadLevel(newLevel) { - logger.log(`set loadLevel:${newLevel}`); - this.levelController.manualLevel = newLevel; - } - - /** - * get next quality level loaded - */ - get nextLoadLevel() { - return this.levelController.nextLoadLevel; - } - - /** - * Set quality level of next loaded segment in a fully "non-destructive" way. - * Same as `loadLevel` but will wait for next switch (until current loading is done). - */ - set nextLoadLevel(level) { - this.levelController.nextLoadLevel = level; - } - - /** - * Return "first level": like a default level, if not set, - * falls back to index of first level referenced in manifest - */ - get firstLevel() { - return Math.max(this.levelController.firstLevel, this.minAutoLevel); - } - - /** - * Sets "first-level", see getter. - */ - set firstLevel(newLevel) { - logger.log(`set firstLevel:${newLevel}`); - this.levelController.firstLevel = newLevel; - } - - /** - * Return the desired start level for the first fragment that will be loaded. - * The default value of -1 indicates automatic start level selection. - * Setting hls.nextAutoLevel without setting a startLevel will result in - * the nextAutoLevel value being used for one fragment load. - */ - get startLevel() { - const startLevel = this.levelController.startLevel; - if (startLevel === -1 && this.abrController.forcedAutoLevel > -1) { - return this.abrController.forcedAutoLevel; - } - return startLevel; - } - - /** - * set start level (level of first fragment that will be played back) - * if not overrided by user, first level appearing in manifest will be used as start level - * if -1 : automatic start level selection, playback will start from level matching download bandwidth - * (determined from download of first segment) - */ - set startLevel(newLevel) { - logger.log(`set startLevel:${newLevel}`); - // if not in automatic start level detection, ensure startLevel is greater than minAutoLevel - if (newLevel !== -1) { - newLevel = Math.max(newLevel, this.minAutoLevel); - } - this.levelController.startLevel = newLevel; - } - - /** - * Whether level capping is enabled. - * Default value is set via `config.capLevelToPlayerSize`. - */ - get capLevelToPlayerSize() { - return this.config.capLevelToPlayerSize; - } - - /** - * Enables or disables level capping. If disabled after previously enabled, `nextLevelSwitch` will be immediately called. - */ - set capLevelToPlayerSize(shouldStartCapping) { - const newCapLevelToPlayerSize = !!shouldStartCapping; - if (newCapLevelToPlayerSize !== this.config.capLevelToPlayerSize) { - if (newCapLevelToPlayerSize) { - this.capLevelController.startCapping(); // If capping occurs, nextLevelSwitch will happen based on size. - } else { - this.capLevelController.stopCapping(); - this.autoLevelCapping = -1; - this.streamController.nextLevelSwitch(); // Now we're uncapped, get the next level asap. - } - this.config.capLevelToPlayerSize = newCapLevelToPlayerSize; - } - } - - /** - * Capping/max level value that should be used by automatic level selection algorithm (`ABRController`) - */ - get autoLevelCapping() { - return this._autoLevelCapping; - } - - /** - * Returns the current bandwidth estimate in bits per second, when available. Otherwise, `NaN` is returned. - */ - get bandwidthEstimate() { - const { - bwEstimator - } = this.abrController; - if (!bwEstimator) { - return NaN; - } - return bwEstimator.getEstimate(); - } - set bandwidthEstimate(abrEwmaDefaultEstimate) { - this.abrController.resetEstimator(abrEwmaDefaultEstimate); - } - - /** - * get time to first byte estimate - * @type {number} - */ - get ttfbEstimate() { - const { - bwEstimator - } = this.abrController; - if (!bwEstimator) { - return NaN; - } - return bwEstimator.getEstimateTTFB(); - } - - /** - * Capping/max level value that should be used by automatic level selection algorithm (`ABRController`) - */ - set autoLevelCapping(newLevel) { - if (this._autoLevelCapping !== newLevel) { - logger.log(`set autoLevelCapping:${newLevel}`); - this._autoLevelCapping = newLevel; - this.levelController.checkMaxAutoUpdated(); - } - } - get maxHdcpLevel() { - return this._maxHdcpLevel; - } - set maxHdcpLevel(value) { - if (isHdcpLevel(value) && this._maxHdcpLevel !== value) { - this._maxHdcpLevel = value; - this.levelController.checkMaxAutoUpdated(); - } - } - - /** - * True when automatic level selection enabled - */ - get autoLevelEnabled() { - return this.levelController.manualLevel === -1; - } - - /** - * Level set manually (if any) - */ - get manualLevel() { - return this.levelController.manualLevel; - } - - /** - * min level selectable in auto mode according to config.minAutoBitrate - */ - get minAutoLevel() { - const { - levels, - config: { - minAutoBitrate - } - } = this; - if (!levels) return 0; - const len = levels.length; - for (let i = 0; i < len; i++) { - if (levels[i].maxBitrate >= minAutoBitrate) { - return i; - } - } - return 0; - } - - /** - * max level selectable in auto mode according to autoLevelCapping - */ - get maxAutoLevel() { - const { - levels, - autoLevelCapping, - maxHdcpLevel - } = this; - let maxAutoLevel; - if (autoLevelCapping === -1 && levels != null && levels.length) { - maxAutoLevel = levels.length - 1; - } else { - maxAutoLevel = autoLevelCapping; - } - if (maxHdcpLevel) { - for (let i = maxAutoLevel; i--;) { - const hdcpLevel = levels[i].attrs['HDCP-LEVEL']; - if (hdcpLevel && hdcpLevel <= maxHdcpLevel) { - return i; - } - } - } - return maxAutoLevel; - } - get firstAutoLevel() { - return this.abrController.firstAutoLevel; - } - - /** - * next automatically selected quality level - */ - get nextAutoLevel() { - return this.abrController.nextAutoLevel; - } - - /** - * this setter is used to force next auto level. - * this is useful to force a switch down in auto mode: - * in case of load error on level N, hls.js can set nextAutoLevel to N-1 for example) - * forced value is valid for one fragment. upon successful frag loading at forced level, - * this value will be resetted to -1 by ABR controller. - */ - set nextAutoLevel(nextLevel) { - this.abrController.nextAutoLevel = nextLevel; - } - - /** - * get the datetime value relative to media.currentTime for the active level Program Date Time if present - */ - get playingDate() { - return this.streamController.currentProgramDateTime; - } - get mainForwardBufferInfo() { - return this.streamController.getMainFwdBufferInfo(); - } - - /** - * Find and select the best matching audio track, making a level switch when a Group change is necessary. - * Updates `hls.config.audioPreference`. Returns the selected track, or null when no matching track is found. - */ - setAudioOption(audioOption) { - var _this$audioTrackContr; - return (_this$audioTrackContr = this.audioTrackController) == null ? void 0 : _this$audioTrackContr.setAudioOption(audioOption); - } - /** - * Find and select the best matching subtitle track, making a level switch when a Group change is necessary. - * Updates `hls.config.subtitlePreference`. Returns the selected track, or null when no matching track is found. - */ - setSubtitleOption(subtitleOption) { - var _this$subtitleTrackCo; - (_this$subtitleTrackCo = this.subtitleTrackController) == null ? void 0 : _this$subtitleTrackCo.setSubtitleOption(subtitleOption); - return null; - } - - /** - * Get the complete list of audio tracks across all media groups - */ - get allAudioTracks() { - const audioTrackController = this.audioTrackController; - return audioTrackController ? audioTrackController.allAudioTracks : []; - } - - /** - * Get the list of selectable audio tracks - */ - get audioTracks() { - const audioTrackController = this.audioTrackController; - return audioTrackController ? audioTrackController.audioTracks : []; - } - - /** - * index of the selected audio track (index in audio track lists) - */ - get audioTrack() { - const audioTrackController = this.audioTrackController; - return audioTrackController ? audioTrackController.audioTrack : -1; - } - - /** - * selects an audio track, based on its index in audio track lists - */ - set audioTrack(audioTrackId) { - const audioTrackController = this.audioTrackController; - if (audioTrackController) { - audioTrackController.audioTrack = audioTrackId; - } - } - - /** - * get the complete list of subtitle tracks across all media groups - */ - get allSubtitleTracks() { - const subtitleTrackController = this.subtitleTrackController; - return subtitleTrackController ? subtitleTrackController.allSubtitleTracks : []; - } - - /** - * get alternate subtitle tracks list from playlist - */ - get subtitleTracks() { - const subtitleTrackController = this.subtitleTrackController; - return subtitleTrackController ? subtitleTrackController.subtitleTracks : []; - } - - /** - * index of the selected subtitle track (index in subtitle track lists) - */ - get subtitleTrack() { - const subtitleTrackController = this.subtitleTrackController; - return subtitleTrackController ? subtitleTrackController.subtitleTrack : -1; - } - get media() { - return this._media; - } - - /** - * select an subtitle track, based on its index in subtitle track lists - */ - set subtitleTrack(subtitleTrackId) { - const subtitleTrackController = this.subtitleTrackController; - if (subtitleTrackController) { - subtitleTrackController.subtitleTrack = subtitleTrackId; - } - } - - /** - * Whether subtitle display is enabled or not - */ - get subtitleDisplay() { - const subtitleTrackController = this.subtitleTrackController; - return subtitleTrackController ? subtitleTrackController.subtitleDisplay : false; - } - - /** - * Enable/disable subtitle display rendering - */ - set subtitleDisplay(value) { - const subtitleTrackController = this.subtitleTrackController; - if (subtitleTrackController) { - subtitleTrackController.subtitleDisplay = value; - } - } - - /** - * get mode for Low-Latency HLS loading - */ - get lowLatencyMode() { - return this.config.lowLatencyMode; - } - - /** - * Enable/disable Low-Latency HLS part playlist and segment loading, and start live streams at playlist PART-HOLD-BACK rather than HOLD-BACK. - */ - set lowLatencyMode(mode) { - this.config.lowLatencyMode = mode; - } - - /** - * Position (in seconds) of live sync point (ie edge of live position minus safety delay defined by ```hls.config.liveSyncDuration```) - * @returns null prior to loading live Playlist - */ - get liveSyncPosition() { - return this.latencyController.liveSyncPosition; - } - - /** - * Estimated position (in seconds) of live edge (ie edge of live playlist plus time sync playlist advanced) - * @returns 0 before first playlist is loaded - */ - get latency() { - return this.latencyController.latency; - } - - /** - * maximum distance from the edge before the player seeks forward to ```hls.liveSyncPosition``` - * configured using ```liveMaxLatencyDurationCount``` (multiple of target duration) or ```liveMaxLatencyDuration``` - * @returns 0 before first playlist is loaded - */ - get maxLatency() { - return this.latencyController.maxLatency; - } - - /** - * target distance from the edge as calculated by the latency controller - */ - get targetLatency() { - return this.latencyController.targetLatency; - } - - /** - * the rate at which the edge of the current live playlist is advancing or 1 if there is none - */ - get drift() { - return this.latencyController.drift; - } - - /** - * set to true when startLoad is called before MANIFEST_PARSED event - */ - get forceStartLoad() { - return this.streamController.forceStartLoad; - } -} -Hls.defaultConfig = void 0; - - -//# sourceMappingURL=hls.mjs.map - - -/***/ }) - -}, -/******/ __webpack_require__ => { // webpackRuntimeModules -/******/ var __webpack_exec__ = (moduleId) => (__webpack_require__(__webpack_require__.s = moduleId)) -/******/ var __webpack_exports__ = (__webpack_exec__("./src/index.js")); -/******/ } -]); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguYnVuZGxlLmpzIiwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBb0Q7O0FBRXBEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFTztBQUNQO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDRCQUE0Qiw4REFBYztBQUMxQztBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBLDRCQUE0QiwwQkFBMEI7QUFDdEQ7QUFDQTtBQUNBO0FBQ0EsaUJBQWlCO0FBQ2pCO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLCtFQUErRTtBQUMvRSxhQUFhO0FBQ2I7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQSw0QkFBNEIsMEJBQTBCO0FBQ3REO0FBQ0E7QUFDQTtBQUNBLGlCQUFpQjtBQUNqQjtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBOztBQUVPO0FBQ1A7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLFNBQVM7O0FBRVQ7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsU0FBUztBQUNULFNBQVM7QUFDVDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FDaE5PO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGdDQUFnQztBQUNoQztBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVPO0FBQ1A7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRU87QUFDUDtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7O0FDbkZPO0FBQ1A7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSw0QkFBNEIsWUFBWTtBQUN4QztBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsY0FBYztBQUNkO0FBQ0EscUNBQXFDLGdDQUFnQztBQUNyRSxxQ0FBcUMsNEJBQTRCO0FBQ2pFLGNBQWM7QUFDZDtBQUNBO0FBQ0EsY0FBYztBQUNkO0FBQ0EscUNBQXFDLGdDQUFnQztBQUNyRSxjQUFjO0FBQ2Q7QUFDQSxxQ0FBcUMsNEJBQTRCO0FBQ2pFO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsY0FBYztBQUNkO0FBQ0E7QUFDQTtBQUNBLGtCQUFrQjtBQUNsQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7OztBQ3pFb0Q7QUFDaUI7O0FBRTlEO0FBQ1A7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDRCQUE0Qiw4REFBYztBQUMxQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsOEJBQThCLGdFQUFpQjtBQUMvQzs7QUFFQTtBQUNBLFNBQVM7O0FBRVQ7QUFDQSxpQ0FBaUM7QUFDakM7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0EsYUFBYTtBQUNiO0FBQ0EsYUFBYTtBQUNiO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0Esd0JBQXdCLDBCQUEwQjtBQUNsRDtBQUNBO0FBQ0E7QUFDQSxhQUFhO0FBQ2I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxjQUFjO0FBQ2Q7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsYUFBYTtBQUNiO0FBQ0E7QUFDQSxhQUFhO0FBQ2IsVUFBVTtBQUNWO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLGFBQWE7QUFDYixhQUFhO0FBQ2I7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLGtFQUFrRTtBQUNsRTs7QUFFQTtBQUNBO0FBQ0Esa0JBQWtCO0FBQ2xCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYTtBQUNiO0FBQ0EsS0FBSzs7QUFFTDtBQUNBLDhCQUE4Qiw0REFBYTtBQUMzQztBQUNBO0FBQ0E7QUFDQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUM1S3lCO0FBQytCO0FBQ2dCOztBQUV4RTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7O0FBRUE7QUFDQTtBQUNBOztBQUVPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSx5QkFBeUIsZ0VBQWU7QUFDeEMsZ0NBQWdDLGdFQUFlO0FBQy9DLDBCQUEwQixpRUFBZ0I7QUFDMUM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7QUFHQTtBQUNBO0FBQ0EsaUVBQWlFLHNDQUFzQztBQUN2RztBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTs7QUFFTztBQUNQOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBLEtBQUs7O0FBRUwsY0FBYyw4Q0FBRztBQUNqQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0wsV0FBVyxxREFBVTtBQUNyQjtBQUNBO0FBQ0EsS0FBSzs7QUFFTCxXQUFXLHFEQUFVO0FBQ3JCO0FBQ0EsS0FBSztBQUNMLFdBQVcscURBQVU7QUFDckI7QUFDQSxLQUFLOztBQUVMO0FBQ0E7QUFDQTs7QUFFTztBQUNQO0FBQ0E7QUFDQTs7QUFFTztBQUNQO0FBQ0E7O0FBRU87QUFDUDtBQUNBOztBQUVPO0FBQ1A7QUFDQTs7QUFFTztBQUNQO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBOztBQUVPO0FBQ1A7QUFDQTs7QUFFTztBQUNQO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLG9CQUFvQix1QkFBdUI7QUFDM0M7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLOztBQUVMOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYTtBQUNiO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBLEtBQUs7QUFDTDs7QUFFQTtBQUNBO0FBQ0Esb0JBQW9CLGtFQUFnQjtBQUNwQyxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEtBQUs7QUFDTDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQ3pPQTtBQUNBO0FBQ0E7O0FBRUEsa0JBQWtCOztBQUVsQjtBQUNBOztBQUVBO0FBQ0E7QUFDQSx1RkFBdUYsaUJBQWlCO0FBQ3hHO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx3QkFBd0I7QUFDeEI7QUFDQTtBQUNBO0FBQ0E7QUFDQSwwQkFBMEI7QUFDMUI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQWE7QUFDYjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047O0FBRUE7QUFDQSxFQUFFO0FBQ0YsRUFBRTs7QUFFRjs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQSxrQkFBa0Isc0JBQXNCO0FBQ3hDO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTCxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CLHNCQUFzQjtBQUMxQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDLEdBQUc7O0FBRUo7QUFDQSwrREFBK0QsOEJBQThCO0FBQzdGOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsQ0FBQyxHQUFHO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUMsR0FBRzs7QUFFSjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLHVDQUF1QyxLQUFLO0FBQzVDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0RBQW9ELEdBQUcsc0JBQXNCLFNBQVM7QUFDdEYsTUFBTTtBQUNOO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esc0JBQXNCLDRCQUE0QjtBQUNsRDtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxtREFBbUQsSUFBSSxzQ0FBc0MsaUJBQWlCO0FBQzlHO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw4Q0FBOEM7QUFDOUM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxrRUFBa0Usb0NBQW9DO0FBQ3RHO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLDBFQUEwRSxtQ0FBbUM7QUFDN0c7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQSx5RUFBeUUsMkJBQTJCO0FBQ3BHO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx5QkFBeUIsa0JBQWtCO0FBQzNDO0FBQ0E7QUFDQTtBQUNBLGlEQUFpRDtBQUNqRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esc0NBQXNDO0FBQ3RDO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0EsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsNkNBQTZDLFVBQVU7QUFDdkQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwrQkFBK0IsVUFBVSxNQUFNO0FBQy9DO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQSwrQkFBK0IsVUFBVSxNQUFNO0FBQy9DO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGdCQUFnQjtBQUNoQixnQkFBZ0I7QUFDaEIsZ0JBQWdCO0FBQ2hCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0JBQWtCLG1CQUFtQjtBQUNyQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFhO0FBQ2IsYUFBYSxZQUFZLEdBQUc7QUFDNUI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXO0FBQ1gsV0FBVztBQUNYO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFhO0FBQ2IsYUFBYSxZQUFZLEdBQUc7QUFDNUI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXO0FBQ1g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07O0FBRU47QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0Isa0JBQWtCO0FBQ3RDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLGtCQUFrQixRQUFRO0FBQzFCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtCQUFrQixRQUFRO0FBQzFCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0JBQWtCLHFCQUFxQjtBQUN2QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLGtCQUFrQixrQkFBa0I7QUFDcEM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVztBQUNYO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFhO0FBQ2I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0JBQWtCO0FBQ2xCO0FBQ0E7QUFDQSxvQkFBb0I7QUFDcEI7QUFDQTtBQUNBLCtCQUErQjtBQUMvQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGlEQUFpRCxJQUFJO0FBQ3JEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esd0RBQXdELG9CQUFvQixvQkFBb0Isd0JBQXdCLEtBQUssbUJBQW1CO0FBQ2hKO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVCxPQUFPO0FBQ1AsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxrQkFBa0Isa0JBQWtCO0FBQ3BDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9CQUFvQixrQkFBa0I7QUFDdEM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0Isa0JBQWtCO0FBQ3RDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQWE7QUFDYjtBQUNBLGFBQWE7QUFDYixNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtCQUFrQixpQkFBaUI7QUFDbkM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsWUFBWTtBQUNaO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQLEtBQUs7QUFDTCxHQUFHO0FBQ0g7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esd0JBQXdCO0FBQ3hCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGdDQUFnQzs7QUFFaEM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDZCQUE2QixrQkFBa0I7QUFDL0M7QUFDQTtBQUNBO0FBQ0EsZ0JBQWdCO0FBQ2hCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxnQkFBZ0I7QUFDaEI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtCQUFrQjtBQUNsQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVc7QUFDWDtBQUNBLE9BQU87QUFDUCxLQUFLO0FBQ0wsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNOztBQUVOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBLDZDQUE2QyxhQUFhLHFCQUFxQixVQUFVO0FBQ3pGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGdDQUFnQyxnQkFBZ0I7QUFDaEQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGVBQWU7QUFDZjtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0Esd0JBQXdCLFFBQVE7QUFDaEM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHdCQUF3QixZQUFZO0FBQ3BDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGNBQWMsZUFBZTtBQUM3QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHdCQUF3QixTQUFTO0FBQ2pDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxxQkFBcUIsb0JBQW9CO0FBQ3pDLDRCQUE0QjtBQUM1QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CLHdCQUF3QjtBQUM1QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDRFQUE0RSxZQUFZO0FBQ3hGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esc0RBQXNEO0FBQ3REO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsbUJBQW1CLFFBQVE7QUFDM0I7QUFDQTtBQUNBO0FBQ0E7O0FBRUEsc0NBQXNDLG9CQUFvQjtBQUMxRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esd0NBQXdDLElBQUk7QUFDNUM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsK0lBQStJLGFBQWE7QUFDNUo7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSLDRCQUE0QixLQUFLLGdEQUFnRCxVQUFVO0FBQzNGO0FBQ0EsTUFBTTtBQUNOLDBHQUEwRyxjQUFjO0FBQ3hIO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsbUlBQW1JLEtBQUs7QUFDeEksSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0osa0pBQWtKLE9BQU87QUFDeko7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsWUFBWSxLQUFLLEtBQUssVUFBVSxNQUFNO0FBQ3RDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNILGtCQUFrQiwwQkFBMEI7QUFDNUM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtCQUFrQixtQkFBbUI7QUFDckM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLDhEQUE4RDs7QUFFOUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0IsbUJBQW1CO0FBQ3ZDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsZ0JBQWdCO0FBQ2hCLCtFQUErRSxXQUFXO0FBQzFGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9CQUFvQixtQkFBbUI7QUFDdkM7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsK0NBQStDLEtBQUs7QUFDcEQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsZ0JBQWdCO0FBQ2hCLGdFQUFnRSxPQUFPO0FBQ3ZFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtCQUFrQjtBQUNsQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EseUNBQXlDO0FBQ3pDO0FBQ0E7QUFDQSxnQkFBZ0I7QUFDaEIsdUVBQXVFLE9BQU87QUFDOUU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGdCQUFnQjtBQUNoQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EscUNBQXFDLFlBQVksR0FBRyxNQUFNO0FBQzFELGtCQUFrQjtBQUNsQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHdEQUF3RCxPQUFPO0FBQy9EO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwyQ0FBMkM7QUFDM0M7QUFDQTtBQUNBLGdDQUFnQyxZQUFZO0FBQzVDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGVBQWUsS0FBSztBQUNwQjtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw4QkFBOEIsSUFBSTtBQUNsQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTs7QUFFQSxtRUFBbUUsYUFBYSxXQUFXLGNBQWMsUUFBUSxXQUFXOztBQUU1SDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwwRUFBMEUsYUFBYTtBQUN2RjtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ04sOEJBQThCO0FBQzlCO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxrQ0FBa0M7QUFDbEM7QUFDQTtBQUNBLFdBQVc7QUFDWDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTs7QUFFQSxvRkFBb0YsWUFBWTs7QUFFaEc7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxnQ0FBZ0M7QUFDaEM7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw4QkFBOEI7QUFDOUI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQSwrQkFBK0IscUZBQXFGLHlCQUF5QixhQUFhO0FBQzFKO0FBQ0Esc0JBQXNCLGVBQWUsTUFBTSxXQUFXO0FBQ3RELE1BQU07QUFDTix5QkFBeUIsWUFBWSxhQUFhLGdCQUFnQjtBQUNsRTtBQUNBO0FBQ0Esc0NBQXNDLFFBQVE7QUFDOUM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxpREFBaUQsSUFBSTtBQUNyRDtBQUNBLE1BQU07QUFDTix5Q0FBeUMsSUFBSTtBQUM3QztBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUixnRkFBZ0YsS0FBSztBQUNyRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9DQUFvQyxJQUFJO0FBQ3hDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0IsaUJBQWlCO0FBQ3JDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EscURBQXFELFNBQVM7QUFDOUQ7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxrQkFBa0IsMEJBQTBCO0FBQzVDO0FBQ0EsMENBQTBDO0FBQzFDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0EsQ0FBQztBQUNEO0FBQ0E7QUFDQTtBQUNBO0FBQ0EscUVBQXFFLEVBQUU7QUFDdkU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CLHVCQUF1QjtBQUMzQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07O0FBRU47QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9CQUFvQixvQkFBb0I7QUFDeEM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esd0JBQXdCLG1CQUFtQjtBQUMzQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxnQ0FBZ0MsSUFBSTtBQUNwQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsdUNBQXVDLElBQUk7QUFDM0M7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CLGdCQUFnQjtBQUNwQztBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxzQkFBc0IsdUJBQXVCO0FBQzdDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVc7QUFDWDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxzQkFBc0I7QUFDdEI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9CQUFvQixPQUFPO0FBQzNCO0FBQ0E7O0FBRUE7QUFDQSxvQkFBb0IsMEJBQTBCO0FBQzlDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHdDQUF3QyxRQUFRO0FBQ2hEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLCtDQUErQyxJQUFJO0FBQ25EO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9CQUFvQix5QkFBeUI7QUFDN0M7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsZ0NBQWdDO0FBQ2hDO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOLDRFQUE0RSx5Q0FBeUM7QUFDckg7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDJDQUEyQyxVQUFVO0FBQ3JEO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHNCQUFzQixVQUFVO0FBQ2hDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsMENBQTBDLHNCQUFzQjtBQUNoRTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0NBQWtDLElBQUk7QUFDdEM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBLEdBQUc7QUFDSDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwrQkFBK0IsZ0NBQWdDO0FBQy9EO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EseUNBQXlDO0FBQ3pDO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9CQUFvQix3QkFBd0I7QUFDNUM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSw4REFBOEQ7QUFDOUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsNkNBQTZDLE1BQU0sR0FBRyxhQUFhLEdBQUcsZ0JBQWdCLEdBQUcscUNBQXFDLEdBQUcsVUFBVTtBQUMzSTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBLEdBQUc7QUFDSDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFlBQVk7QUFDWjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1Isa0NBQWtDLElBQUk7QUFDdEM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBLFlBQVk7QUFDWjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1YsZ0RBQWdELHNCQUFzQjtBQUN0RTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw0REFBNEQsaUJBQWlCO0FBQzdFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHNDQUFzQyxZQUFZLFFBQVEsYUFBYTtBQUN2RTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsMENBQTBDLFVBQVU7QUFDcEQsNENBQTRDLFVBQVU7QUFDdEQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHNCQUFzQiw2QkFBNkI7QUFDbkQ7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1YsMkVBQTJFLE1BQU07QUFDakY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNOztBQUVOO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0NBQWtDLE9BQU8sRUFBRSw0SEFBNEg7QUFDdks7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwwREFBMEQsNEJBQTRCLE1BQU0sYUFBYSxxQkFBcUIsWUFBWTtBQUMxSTtBQUNBLFlBQVk7QUFDWjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx5Q0FBeUMsa0JBQWtCLGtCQUFrQix3QkFBd0IsVUFBVSxhQUFhLFVBQVUsVUFBVSxVQUFVLEtBQUs7QUFDL0o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsdUNBQXVDLE9BQU8sS0FBSyxzQ0FBc0M7QUFDekY7QUFDQSwwQkFBMEI7QUFDMUIscUJBQXFCO0FBQ3JCLHNCQUFzQjtBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDJCQUEyQjtBQUMzQiwyQkFBMkI7QUFDM0IsNEJBQTRCLDRCQUE0QjtBQUN4RDs7QUFFQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLCtDQUErQyxlQUFlLEdBQUcseUJBQXlCLFNBQVMsYUFBYTtBQUNoSDtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQSwrQ0FBK0MsZUFBZSxHQUFHLHlCQUF5QixTQUFTLGFBQWEsT0FBTyxNQUFNO0FBQzdIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBLG1EQUFtRCxTQUFTO0FBQzVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYTtBQUNiO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQSxTQUFTO0FBQ1Q7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsMkNBQTJDLDZCQUE2QjtBQUN4RTtBQUNBLEtBQUs7QUFDTCxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFhO0FBQ2I7QUFDQTtBQUNBLE9BQU87QUFDUCxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBLGlCQUFpQixhQUFhLEdBQUcsWUFBWSxHQUFHLDJCQUEyQixFQUFFLCtCQUErQixHQUFHLE1BQU0sR0FBRywrQkFBK0I7QUFDdko7QUFDQTtBQUNBLGlCQUFpQixlQUFlLEVBQUUsbUNBQW1DLEdBQUcsTUFBTTtBQUM5RTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVMsMENBQTBDO0FBQ25EO0FBQ0E7QUFDQTtBQUNBLFNBQVMsaUZBQWlGO0FBQzFGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0osaUNBQWlDLElBQUk7QUFDckM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGlFQUFpRSwwQkFBMEIsd0JBQXdCLFVBQVU7QUFDN0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwwRUFBMEUscUJBQXFCO0FBQy9GO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsdUVBQXVFLG9CQUFvQix5Q0FBeUMsb0NBQW9DO0FBQ3hLO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQSxvRUFBb0UseUJBQXlCLGVBQWUsVUFBVTtBQUN0SDtBQUNBO0FBQ0E7QUFDQSxtRUFBbUUsNEJBQTRCLGVBQWUsYUFBYTtBQUMzSDtBQUNBO0FBQ0E7QUFDQSxrRkFBa0YsNkJBQTZCO0FBQy9HO0FBQ0E7QUFDQTtBQUNBLCtEQUErRCx3QkFBd0Isb0JBQW9CLGNBQWM7QUFDekg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw2Q0FBNkMsUUFBUSxvQkFBb0IsT0FBTztBQUNoRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNULE9BQU87QUFDUDtBQUNBO0FBQ0EsR0FBRyxJQUFJO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtCQUFrQixtQkFBbUI7QUFDckM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHLElBQUk7QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBLDRCQUE0QixHQUFHO0FBQy9CO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsZ0NBQWdDLGdCQUFnQjtBQUNoRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsMkNBQTJDLDhCQUE4QjtBQUN6RTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQ0FBb0MsUUFBUSxFQUFFLG1DQUFtQyxXQUFXLFlBQVk7QUFDeEcsNkJBQTZCLGtDQUFrQztBQUMvRCxrREFBa0QsNEJBQTRCO0FBQzlFLHNEQUFzRCxxQ0FBcUM7QUFDM0YsdUJBQXVCLFVBQVU7QUFDakMsNkJBQTZCLHlEQUF5RDtBQUN0Rix5QkFBeUIsMEJBQTBCO0FBQ25ELDJCQUEyQixlQUFlLElBQUksMEJBQTBCO0FBQ3hFO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsMkNBQTJDLHVCQUF1QjtBQUNsRTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFlBQVk7QUFDWjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxjQUFjO0FBQ2Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxrR0FBa0csWUFBWSxhQUFhLFFBQVE7QUFDbkk7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGNBQWMscUJBQXFCLEdBQUcscUNBQXFDO0FBQzNFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLCtDQUErQyxvQ0FBb0MsOENBQThDLHVDQUF1QztBQUN4SztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EseUJBQXlCLG1FQUFtRSwwQkFBMEIsVUFBVTtBQUNoSTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxnRUFBZ0U7QUFDaEU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQSw0Q0FBNEMsMEJBQTBCO0FBQ3RFLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwrQkFBK0IsbUJBQW1CO0FBQ2xEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDBFQUEwRSxtQkFBbUIsY0FBYyxPQUFPLEVBQUUsNkJBQTZCO0FBQ2pKLGNBQWM7QUFDZCwrRkFBK0YsT0FBTyxFQUFFLDZCQUE2QjtBQUNySTtBQUNBLCtEQUErRCxNQUFNO0FBQ3JFO0FBQ0E7QUFDQTtBQUNBLFdBQVc7QUFDWCxVQUFVO0FBQ1Y7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG1EQUFtRCx5QkFBeUIsS0FBSyxjQUFjLG1DQUFtQyxnQ0FBZ0MsSUFBSSxzQ0FBc0MsdUJBQXVCLGFBQWEsSUFBSSxrQkFBa0I7QUFDdFE7QUFDQSxnREFBZ0QsbUJBQW1CLElBQUksR0FBRyxhQUFhLHVCQUF1QixZQUFZLGtDQUFrQyxPQUFPLDRCQUE0QixjQUFjLHdCQUF3QixtQkFBbUIsNkJBQTZCLGdCQUFnQiwwQkFBMEIsaUJBQWlCLGdCQUFnQixXQUFXLGlCQUFpQixhQUFhLG1CQUFtQixnQkFBZ0IsVUFBVTtBQUN0YjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx1Q0FBdUMsSUFBSTtBQUMzQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBLDhCQUE4QixJQUFJO0FBQ2xDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQLEtBQUs7QUFDTDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CLHNCQUFzQjtBQUMxQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVc7QUFDWDtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0Isc0JBQXNCO0FBQzFDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsWUFBWSxjQUFjLEdBQUcsZUFBZSxHQUFHLFlBQVk7QUFDM0Q7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx3QkFBd0IscUJBQXFCO0FBQzdDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CLHNCQUFzQjtBQUMxQztBQUNBO0FBQ0E7QUFDQSxXQUFXO0FBQ1g7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxzQkFBc0IscUJBQXFCO0FBQzNDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFlBQVk7QUFDWjtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CLHNCQUFzQjtBQUMxQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSwwQ0FBMEMsU0FBUztBQUNuRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwwQ0FBMEMsU0FBUztBQUNuRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwyRkFBMkYsWUFBWTtBQUN2RztBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQSx3Q0FBd0M7O0FBRXhDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHFEQUFxRCwwQkFBMEI7QUFDL0U7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVztBQUNYLFNBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQWE7QUFDYiwyQ0FBMkMsZUFBZSxFQUFFLGNBQWM7QUFDMUU7QUFDQTtBQUNBLFdBQVc7QUFDWCxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXO0FBQ1gsU0FBUztBQUNUO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsOENBQThDLHFCQUFxQjtBQUNuRTtBQUNBO0FBQ0EsV0FBVztBQUNYLFNBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQWE7QUFDYjtBQUNBO0FBQ0EsT0FBTztBQUNQLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYTtBQUNiLDJDQUEyQyxlQUFlLEVBQUUsY0FBYztBQUMxRTtBQUNBO0FBQ0EsV0FBVztBQUNYLFNBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXO0FBQ1gsU0FBUztBQUNUO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw4Q0FBOEMscUJBQXFCO0FBQ25FO0FBQ0E7QUFDQSxXQUFXO0FBQ1g7QUFDQSxPQUFPO0FBQ1AsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxlQUFlO0FBQ2Y7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxpQ0FBaUMsZ0NBQWdDO0FBQ2pFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CLE9BQU87QUFDM0I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsZ0JBQWdCLFNBQVM7QUFDekI7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQSxnQkFBZ0IsU0FBUztBQUN6QjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0IsZ0JBQWdCO0FBQ3BDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx1QkFBdUIsbUJBQW1CO0FBQzFDO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxrQkFBa0IsYUFBYTtBQUMvQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQSx1QkFBdUI7O0FBRXZCO0FBQ0E7QUFDQTtBQUNBLElBQUksSUFBSTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0wsMEVBQTBFLFNBQVMsSUFBSSxZQUFZO0FBQ25HO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLCtCQUErQixJQUFJO0FBQ25DO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0IsU0FBUztBQUM3QixpQkFBaUIsc0JBQXNCLEdBQUcsb0JBQW9CO0FBQzlEO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDBDQUEwQyxVQUFVO0FBQ3BELDRDQUE0QyxVQUFVO0FBQ3REO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQSxpQ0FBaUMsbUVBQW1FLFdBQVcsTUFBTTtBQUNySDtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDhCQUE4QixRQUFRLEVBQUUsMkNBQTJDLFdBQVcsWUFBWTtBQUMxRztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0NBQW9DLFNBQVMsV0FBVyxXQUFXO0FBQ25FO0FBQ0E7O0FBRUEseUNBQXlDO0FBQ3pDO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBLCtCQUErQixxREFBcUQ7QUFDcEY7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0EsUUFBUTtBQUNSOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVc7QUFDWDtBQUNBLFNBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVztBQUNYO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx5QkFBeUIsV0FBVyxNQUFNLFFBQVEsRUFBRSxvQ0FBb0MsS0FBSyxrRUFBa0UsRUFBRSxZQUFZLFNBQVMsNkVBQTZFLEdBQUcsdUVBQXVFLGFBQWEsNEVBQTRFO0FBQ3RhO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDREQUE0RCxxQkFBcUI7QUFDakY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHlEQUF5RCx5QkFBeUI7QUFDbEY7QUFDQTtBQUNBO0FBQ0Esa0NBQWtDLFNBQVMsTUFBTSxnQkFBZ0IsR0FBRyxjQUFjLEtBQUssOERBQThELEVBQUUsV0FBVztBQUNsSztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx1Q0FBdUMsU0FBUyxLQUFLLFlBQVksTUFBTSxTQUFTLGVBQWUsZ0JBQWdCLEdBQUcsY0FBYyxhQUFhLFVBQVUsR0FBRyxvQkFBb0IsSUFBSSw2REFBNkQsSUFBSSxXQUFXLFlBQVksd0NBQXdDO0FBQ2xUO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQWE7QUFDYixZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVztBQUNYO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxpQ0FBaUMsU0FBUyxNQUFNLFNBQVMsRUFBRSxxRUFBcUUsRUFBRSw2REFBNkQsSUFBSSxXQUFXLFlBQVksd0NBQXdDO0FBQ2xRO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFhO0FBQ2I7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQSxvRUFBb0UsSUFBSSxXQUFXLFdBQVc7QUFDOUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsK0NBQStDLGNBQWM7QUFDN0Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG1FQUFtRSxRQUFRLFVBQVUsd0JBQXdCO0FBQzdHO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsK0NBQStDLGFBQWEsNkJBQTZCLFFBQVE7QUFDakc7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwyQ0FBMkMsU0FBUztBQUNwRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxnRkFBZ0YsNkJBQTZCO0FBQzdHO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsbUZBQW1GLFFBQVE7QUFDM0Y7QUFDQTtBQUNBLDRFQUE0RTtBQUM1RTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG1GQUFtRixRQUFRO0FBQzNGO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxpQ0FBaUMsd0JBQXdCLDREQUE0RCxJQUFJLDJCQUEyQiw0QkFBNEI7QUFDaEw7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBLHlDQUF5QyxnQ0FBZ0MsWUFBWSxpREFBaUQsSUFBSSxpQkFBaUIsV0FBVyx1Q0FBdUMsYUFBYSxPQUFPO0FBQ2pPO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxzQ0FBc0MsaUJBQWlCLFdBQVcseURBQXlELG9DQUFvQyxjQUFjO0FBQzdLO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsNEJBQTRCLFFBQVEsRUFBRSxtQ0FBbUMsV0FBVyxZQUFZO0FBQ2hHO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0VBQW9FLFVBQVUsSUFBSSxrRkFBa0Y7QUFDcEs7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBLDRCQUE0QixTQUFTLEtBQUssWUFBWSxFQUFFLFlBQVksZUFBZSxhQUFhLHFCQUFxQixlQUFlLEdBQUcseUJBQXlCLEtBQUssTUFBTTtBQUMzSztBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSLHVCQUF1QixjQUFjLGlDQUFpQyxXQUFXO0FBQ2pGO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxzRkFBc0YsY0FBYztBQUNwRztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxzRUFBc0UsY0FBYyxXQUFXLGdCQUFnQjtBQUMvRztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGdEQUFnRCxTQUFTLEVBQUUsTUFBTSxxQkFBcUIsZUFBZTtBQUNyRztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBLDREQUE0RCxTQUFTLFdBQVcsWUFBWTtBQUM1RjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EseUNBQXlDLFNBQVMsWUFBWSxVQUFVO0FBQ3hFLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtCQUFrQixjQUFjLElBQUksVUFBVTtBQUM5QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0JBQWtCLG1CQUFtQjtBQUNyQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsOENBQThDLFlBQVksaUJBQWlCLG1CQUFtQixLQUFLLGdCQUFnQixHQUFHLGlDQUFpQyxRQUFRO0FBQy9KO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHdDQUF3QyxLQUFLO0FBQzdDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwyREFBMkQsa0JBQWtCO0FBQzdFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsK0JBQStCLFdBQVcsY0FBYyxlQUFlLGtCQUFrQixrQkFBa0I7QUFDM0c7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsK0JBQStCLFlBQVksU0FBUyxrQkFBa0IsYUFBYSxvQkFBb0I7QUFDdkc7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsMERBQTBELG9CQUFvQixHQUFHLFlBQVksR0FBRyxnQkFBZ0I7QUFDaEg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBLCtCQUErQixXQUFXLFFBQVEsT0FBTyx1QkFBdUIsWUFBWSxHQUFHLG9CQUFvQixHQUFHLGlCQUFpQixXQUFXLFFBQVE7QUFDMUo7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0RBQW9EO0FBQ3BEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG1DQUFtQyxpQkFBaUI7QUFDcEQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHdHQUF3RztBQUN4RztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFhO0FBQ2I7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBbUM7QUFDbkMsbUNBQW1DO0FBQ25DLGtDQUFrQztBQUNsQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGVBQWU7QUFDZjtBQUNBO0FBQ0EsZUFBZTtBQUNmOztBQUVBO0FBQ0E7QUFDQTtBQUNBLGVBQWU7QUFDZjtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsbUJBQW1CO0FBQ25CO0FBQ0EsNEJBQTRCO0FBQzVCOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxtQkFBbUI7QUFDbkI7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxtREFBbUQ7QUFDbkQsMENBQTBDO0FBQzFDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsMEJBQTBCO0FBQzFCLCtCQUErQix1Q0FBdUM7QUFDdEU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsK0JBQStCO0FBQy9CO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLGlDQUFpQztBQUNqQztBQUNBO0FBQ0EsNkJBQTZCO0FBQzdCLE1BQU07QUFDTixnQ0FBZ0M7QUFDaEM7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0IsV0FBVztBQUMvQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9DQUFvQztBQUNwQyxpQkFBaUI7QUFDakIsaUJBQWlCO0FBQ2pCLGlCQUFpQjtBQUNqQixlQUFlO0FBQ2Y7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7O0FBRVIsaUJBQWlCO0FBQ2pCLGlCQUFpQjtBQUNqQixtQkFBbUI7QUFDbkI7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CLHNCQUFzQjtBQUMxQztBQUNBO0FBQ0E7QUFDQTtBQUNBLGNBQWM7QUFDZDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxlQUFlO0FBQ2Y7QUFDQTtBQUNBLGlCQUFpQjtBQUNqQixNQUFNO0FBQ04sbUJBQW1CO0FBQ25CLGdCQUFnQjtBQUNoQixnQkFBZ0I7QUFDaEI7QUFDQSxrQkFBa0Isb0NBQW9DO0FBQ3REO0FBQ0EsUUFBUTtBQUNSO0FBQ0EsZUFBZTtBQUNmLGlCQUFpQjtBQUNqQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTs7QUFFTixpQkFBaUI7QUFDakI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDhCQUE4QixPQUFPO0FBQ3JDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDRCQUE0QixvQ0FBb0M7QUFDaEU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw2QkFBNkIscUNBQXFDO0FBQ2xFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsWUFBWTtBQUNaO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxjQUFjO0FBQ2Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDBFQUEwRSxXQUFXO0FBQ3JGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHNCQUFzQixZQUFZO0FBQ2xDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLGlDQUFpQyxhQUFhO0FBQzlDO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0RBQW9ELE9BQU8scUJBQXFCLFdBQVcsZ0NBQWdDLFlBQVk7QUFDdkk7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBLHlEQUF5RCxnQkFBZ0I7QUFDekU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYTtBQUNiLFlBQVk7QUFDWjtBQUNBO0FBQ0EsU0FBUztBQUNULFFBQVE7QUFDUjtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLCtDQUErQyxjQUFjO0FBQzdEO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0RBQWtELGtCQUFrQjtBQUNwRTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxrRUFBa0UsT0FBTztBQUN6RSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGVBQWUsa0JBQWtCO0FBQ2pDO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsaUNBQWlDO0FBQ2pDO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsZ0NBQWdDLGNBQWM7QUFDOUM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBLGdCQUFnQixNQUFNO0FBQ3RCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHlCQUF5QixzQ0FBc0M7QUFDL0Q7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwyQ0FBMkMsYUFBYTtBQUN4RDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG1DQUFtQyxpQkFBaUI7QUFDcEQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0EsVUFBVTtBQUNWO0FBQ0EsVUFBVTtBQUNWO0FBQ0EsVUFBVTtBQUNWO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw4QkFBOEI7O0FBRTlCLDZEQUE2RDtBQUM3RCx5REFBeUQ7QUFDekQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwwQkFBMEIsU0FBUztBQUNuQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxnQkFBZ0Isb0JBQW9CO0FBQ3BDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQSxnQkFBZ0Isc0JBQXNCO0FBQ3RDO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLGdCQUFnQixzQkFBc0I7QUFDdEM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esc0JBQXNCO0FBQ3RCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSw0RUFBNEUsdUJBQXVCO0FBQ25HO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxnQkFBZ0IsU0FBUztBQUN6QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLGlEQUFpRDtBQUNqRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLDZDQUE2QztBQUM3QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxrREFBa0Qsb0JBQW9CLFNBQVMsUUFBUTtBQUN2RjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsWUFBWTtBQUNaLG1FQUFtRSxRQUFRO0FBQzNFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLG9CQUFvQixlQUFlO0FBQ25DO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDhCQUE4QixrQ0FBa0MsTUFBTSxNQUFNLDBDQUEwQyxzQkFBc0I7QUFDNUksVUFBVTtBQUNWLDhCQUE4QixtQ0FBbUMsTUFBTSxNQUFNLGlEQUFpRCxzQkFBc0I7QUFDcEo7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1osNEJBQTRCLHlCQUF5QjtBQUNyRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHlEQUF5RCxvQ0FBb0MsR0FBRyxvQ0FBb0MsV0FBVyxrQ0FBa0M7QUFDakw7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0IsZUFBZTtBQUNuQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esc0JBQXNCLGFBQWE7QUFDbkM7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDhDQUE4QyxTQUFTO0FBQ3ZELE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9CQUFvQixlQUFlO0FBQ25DO0FBQ0E7QUFDQTtBQUNBO0FBQ0EseURBQXlELGFBQWE7QUFDdEU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGNBQWM7QUFDZDtBQUNBO0FBQ0EsNkRBQTZELHNCQUFzQix3QkFBd0IsaUJBQWlCLHdCQUF3QjtBQUNwSixZQUFZO0FBQ1o7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esc0RBQXNELFNBQVM7QUFDL0Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGNBQWM7QUFDZDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHVFQUF1RTtBQUN2RTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxnREFBZ0Q7QUFDaEQ7O0FBRUE7QUFDQTtBQUNBLDhDQUE4Qyx5QkFBeUI7QUFDdkU7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSx5Q0FBeUMsa0NBQWtDLDZCQUE2QiwyQ0FBMkM7QUFDbko7QUFDQTtBQUNBLFVBQVU7O0FBRVY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtEQUFrRCxTQUFTLGdCQUFnQixzQ0FBc0MsV0FBVywyQ0FBMkM7QUFDdkssMEJBQTBCLGFBQWE7QUFDdkM7QUFDQTtBQUNBO0FBQ0EsMkZBQTJGO0FBQzNGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFhO0FBQ2I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxzREFBc0QsZ0JBQWdCO0FBQ3RFO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsbUdBQW1HO0FBQ25HO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0RBQW9ELFNBQVM7QUFDN0QsYUFBYTtBQUNiO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0EsbUhBQW1IO0FBQ25IO0FBQ0EsS0FBSzs7QUFFTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9CQUFvQixlQUFlO0FBQ25DO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0JBQWtCLG9CQUFvQjtBQUN0QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxzQkFBc0IsZ0JBQWdCO0FBQ3RDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHNCQUFzQixnQkFBZ0I7QUFDdEM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLHFEQUFxRDtBQUNyRDtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLGlDQUFpQztBQUNqQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDRDQUE0Qyx1Q0FBdUM7QUFDbkY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHVDQUF1QyxZQUFZLDZDQUE2QyxPQUFPO0FBQ3ZHO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esd0NBQXdDLFlBQVk7QUFDcEQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7QUFDRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDO0FBQ0Q7QUFDQTtBQUNBLENBQUM7QUFDRDtBQUNBO0FBQ0EsQ0FBQztBQUNEO0FBQ0E7QUFDQSxDQUFDO0FBQ0Q7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0EsNkNBQTZDO0FBQzdDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQSw2Q0FBNkM7QUFDN0M7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQ0FBb0MsY0FBYztBQUNsRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTixvREFBb0QsYUFBYSxFQUFFLG9EQUFvRCxXQUFXLGdCQUFnQjtBQUNsSjtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0EsNENBQTRDLFNBQVM7QUFDckQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxpQkFBaUI7QUFDakI7QUFDQSxDQUFDO0FBQ0Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLHFCQUFxQjs7QUFFckI7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsWUFBWSxVQUFVO0FBQ3RCLFlBQVksR0FBRztBQUNmLFlBQVksU0FBUztBQUNyQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLFlBQVksY0FBYztBQUMxQixZQUFZLGlCQUFpQjtBQUM3QixZQUFZLFVBQVU7QUFDdEIsWUFBWSxHQUFHO0FBQ2YsWUFBWSxTQUFTO0FBQ3JCLGNBQWM7QUFDZDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxZQUFZLGNBQWM7QUFDMUIsWUFBWSxpQkFBaUI7QUFDN0I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxjQUFjO0FBQ2Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxZQUFZLGlCQUFpQjtBQUM3QixjQUFjLE9BQU87QUFDckI7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBLDJEQUEyRCxPQUFPO0FBQ2xFO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxZQUFZLGlCQUFpQjtBQUM3QixjQUFjLFFBQVE7QUFDdEI7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxZQUFZLGlCQUFpQjtBQUM3QixjQUFjLFNBQVM7QUFDdkI7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLDJDQUEyQyxTQUFTO0FBQ3BEO0FBQ0E7O0FBRUE7QUFDQSxLQUFLO0FBQ0w7QUFDQTs7QUFFQSxpQkFBaUIsWUFBWTtBQUM3Qjs7QUFFQTtBQUNBLDZEQUE2RDtBQUM3RCxpRUFBaUU7QUFDakUscUVBQXFFO0FBQ3JFLHlFQUF5RTtBQUN6RTtBQUNBLDREQUE0RCxTQUFTO0FBQ3JFO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxZQUFZLGlCQUFpQjtBQUM3QixZQUFZLFVBQVU7QUFDdEIsWUFBWSxHQUFHO0FBQ2YsY0FBYyxjQUFjO0FBQzVCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsWUFBWSxpQkFBaUI7QUFDN0IsWUFBWSxVQUFVO0FBQ3RCLFlBQVksR0FBRztBQUNmLGNBQWMsY0FBYztBQUM1QjtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLFlBQVksaUJBQWlCO0FBQzdCLFlBQVksVUFBVTtBQUN0QixZQUFZLEdBQUc7QUFDZixZQUFZLFNBQVM7QUFDckIsY0FBYyxjQUFjO0FBQzVCO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTCw2REFBNkQsWUFBWTtBQUN6RTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLFlBQVksaUJBQWlCO0FBQzdCLGNBQWMsY0FBYztBQUM1QjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxFQUFFOztBQUVGO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxtREFBbUQ7QUFDbkQsbURBQW1EO0FBQ25EO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDZDQUE2QyxtQkFBbUIsT0FBTyxHQUFHO0FBQzFFO0FBQ0EsWUFBWTtBQUNaLG9EQUFvRCxHQUFHO0FBQ3ZEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBLHVDQUF1QyxnQkFBZ0IsR0FBRyxlQUFlLEdBQUcsYUFBYTtBQUN6RjtBQUNBLHFDQUFxQyxHQUFHO0FBQ3hDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQWE7QUFDYjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVc7QUFDWCxVQUFVO0FBQ1YsMkNBQTJDLEdBQUc7QUFDOUM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsMkNBQTJDLFVBQVUsMkNBQTJDLGNBQWMsS0FBSyxnQkFBZ0IsU0FBUyxpQkFBaUIsTUFBTTtBQUNuSyx5QkFBeUI7QUFDekIsdUJBQXVCO0FBQ3ZCLHNCQUFzQjtBQUN0Qiw4QkFBOEI7QUFDOUIsc0JBQXNCO0FBQ3RCLDZCQUE2QixrQkFBa0I7QUFDL0M7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1AsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQSxTQUFTO0FBQ1QsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUCxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBLFNBQVM7QUFDVCxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxxREFBcUQsNkJBQTZCO0FBQ2xGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUCxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtCQUFrQix1QkFBdUI7QUFDekM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLDZCQUE2Qjs7QUFFN0I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0NBQWtDLElBQUksbUJBQW1CLFFBQVE7QUFDakU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLCtEQUErRCwyQkFBMkI7QUFDMUY7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsWUFBWTtBQUNaO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsY0FBYztBQUNkO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsY0FBYztBQUNkO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGNBQWM7QUFDZDtBQUNBLCtDQUErQyxRQUFRLHFDQUFxQyxrQkFBa0I7QUFDOUc7QUFDQSxjQUFjO0FBQ2Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGlEQUFpRCxRQUFRLE1BQU0sWUFBWSx3Q0FBd0MsZ0JBQWdCO0FBQ25JO0FBQ0E7QUFDQTtBQUNBLFlBQVk7QUFDWjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBLCtEQUErRCxRQUFRO0FBQ3ZFO0FBQ0E7QUFDQSw0QkFBNEIsU0FBUyxVQUFVLG1CQUFtQixHQUFHLGlCQUFpQixHQUFHLGlDQUFpQyxzQkFBc0IsR0FBRyx5QkFBeUIsUUFBUSxZQUFZLHlCQUF5QjtBQUN6TjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQSx5RkFBeUYsU0FBUyxXQUFXLFlBQVk7QUFDekg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxpQ0FBaUMsSUFBSSxNQUFNLGlCQUFpQixHQUFHLGNBQWMsVUFBVSxRQUFRO0FBQy9GO0FBQ0Esd0NBQXdDO0FBQ3hDO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOLDJDQUEyQyxRQUFRLHFEQUFxRCxTQUFTLE1BQU0saUJBQWlCLEdBQUcsY0FBYyxVQUFVLFFBQVE7QUFDM0s7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDBFQUEwRTtBQUMxRSw0QkFBNEIsUUFBUSxFQUFFLGlDQUFpQyxXQUFXLFlBQVksOENBQThDLFdBQVcsaUJBQWlCLHlEQUF5RDtBQUNqTztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsdUVBQXVFO0FBQ3ZFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNOztBQUVOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsNkNBQTZDLGdCQUFnQiwwQkFBMEIsbUJBQW1CLEdBQUcsWUFBWTtBQUN6SDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUixnRUFBZ0UsU0FBUywrQ0FBK0MsU0FBUyxXQUFXLGFBQWE7QUFDeko7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQSw4REFBOEQ7QUFDOUQ7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0EsdUNBQXVDLElBQUksWUFBWSxTQUFTLDRCQUE0QixpRUFBaUU7QUFDN0o7QUFDQTtBQUNBO0FBQ0E7QUFDQSw0QkFBNEIsSUFBSSxHQUFHLHdCQUF3QixTQUFTLHlCQUF5QixRQUFRLFNBQVMsVUFBVSxnQkFBZ0IsR0FBRyxjQUFjO0FBQ3pKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNULFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHlDQUF5QyxvQkFBb0IsOEJBQThCLHFEQUFxRDtBQUNoSjtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBLDBGQUEwRiw4RUFBOEUsZUFBZSxtQkFBbUI7QUFDMU07QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLDJDQUEyQyxNQUFNO0FBQ2pEO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EseUNBQXlDLE9BQU8sR0FBRyxXQUFXLFNBQVMsWUFBWSxRQUFRLGVBQWUsV0FBVyxlQUFlO0FBQ3BJO0FBQ0E7QUFDQSxvRUFBb0U7QUFDcEU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CLHdCQUF3QjtBQUM1QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUixzQkFBc0Isd0JBQXdCO0FBQzlDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0Esc0JBQXNCLHdCQUF3QjtBQUM5QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esc0JBQXNCLHdCQUF3QjtBQUM5QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1YsaUZBQWlGLE1BQU07QUFDdkY7QUFDQTtBQUNBO0FBQ0EsK0NBQStDLElBQUksR0FBRyxnQkFBZ0IsU0FBUyxpQkFBaUIsUUFBUSxRQUFRO0FBQ2hIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7O0FBRUEsNkJBQTZCOztBQUU3QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CLHFCQUFxQjtBQUN6QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esd0JBQXdCLG9CQUFvQjtBQUM1QztBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0Esa0VBQWtFLFFBQVE7QUFDMUU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsK0JBQStCLFNBQVMsVUFBVSxtQkFBbUIsR0FBRyxpQkFBaUIsR0FBRyxpQ0FBaUMsc0JBQXNCLEdBQUcseUJBQXlCLFFBQVEsWUFBWSx5QkFBeUI7QUFDNU47QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1QsT0FBTztBQUNQLHFCQUFxQixTQUFTLElBQUksWUFBWTtBQUM5QztBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EscURBQXFELEtBQUsseUNBQXlDLE1BQU0sdUNBQXVDLE9BQU87QUFDdko7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esc0JBQXNCLG1CQUFtQjtBQUN6QztBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQSwwQ0FBMEMsSUFBSSxZQUFZLFNBQVMsNEJBQTRCLGlFQUFpRTtBQUNoSztBQUNBO0FBQ0E7QUFDQTtBQUNBLCtCQUErQixJQUFJLEdBQUcsd0JBQXdCLFNBQVMseUJBQXlCLFFBQVEsU0FBUyxVQUFVLGdCQUFnQixHQUFHLGNBQWM7QUFDNUo7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1QsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSw0Q0FBNEMsdUJBQXVCLHFCQUFxQiwyREFBMkQ7QUFDbko7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0IsbUJBQW1CO0FBQ3ZDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHNCQUFzQixtQkFBbUI7QUFDekM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHNCQUFzQixtQkFBbUI7QUFDekM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHNCQUFzQixtQkFBbUI7QUFDekM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1YsaUZBQWlGLE1BQU07QUFDdkY7QUFDQTtBQUNBLG1EQUFtRCxHQUFHO0FBQ3REO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtFQUFrRSxrQkFBa0Isa0JBQWtCLGtCQUFrQjtBQUN4SDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLDhDQUE4QyxNQUFNO0FBQ3BEO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDRDQUE0QyxNQUFNLGtCQUFrQixXQUFXLFNBQVMsWUFBWSxRQUFRLGNBQWM7QUFDMUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQSx1QkFBdUI7QUFDdkIsMEJBQTBCO0FBQzFCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSLHNFQUFzRSxLQUFLLDRCQUE0QixNQUFNO0FBQzdHOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQSwrRUFBK0UsWUFBWSxJQUFJLFNBQVM7QUFDeEc7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGdCQUFnQixnQ0FBZ0M7QUFDaEQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx3Q0FBd0MsMkVBQTJFO0FBQ25IO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWLHlDQUF5QyxhQUFhO0FBQ3REO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOLGlDQUFpQyxLQUFLO0FBQ3RDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGdDQUFnQyxXQUFXLFNBQVMsV0FBVztBQUMvRDtBQUNBLHdDQUF3QyxrQkFBa0IsS0FBSyxXQUFXO0FBQzFFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBLEtBQUs7O0FBRUw7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0JBQWtCLDJCQUEyQixnQ0FBZ0MscUJBQXFCO0FBQ2xHO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBLCtCQUErQixNQUFNLHVCQUF1QixTQUFTO0FBQ3JFO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUCx1QkFBdUI7QUFDdkIsMEJBQTBCO0FBQzFCO0FBQ0Esc0NBQXNDLE1BQU07QUFDNUM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx5RUFBeUUsV0FBVyxVQUFVLE1BQU0sUUFBUSxRQUFRO0FBQ3BIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQSxnREFBZ0QsTUFBTTtBQUN0RCxPQUFPO0FBQ1A7QUFDQSxnREFBZ0QsTUFBTTtBQUN0RDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVCxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw4QkFBOEIsaUJBQWlCLEdBQUcsZ0NBQWdDLDhCQUE4QixLQUFLO0FBQ3JIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQSxpRUFBaUUsa0JBQWtCLEtBQUssZ0JBQWdCLE1BQU0sTUFBTTtBQUNwSCxPQUFPO0FBQ1A7QUFDQSxrRUFBa0Usa0JBQWtCLEtBQUssZ0JBQWdCLE1BQU0sTUFBTTtBQUNySDtBQUNBO0FBQ0EsU0FBUztBQUNULE9BQU87QUFDUDtBQUNBLDJDQUEyQyxNQUFNO0FBQ2pEO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBLG9GQUFvRixXQUFXLFNBQVMsWUFBWSxNQUFNLFFBQVE7QUFDbEk7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHNCQUFzQixNQUFNO0FBQzVCO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBLDBGQUEwRix1QkFBdUI7QUFDakg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXOztBQUVYO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYTtBQUNiLFlBQVk7QUFDWixxQ0FBcUMsTUFBTTtBQUMzQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXO0FBQ1g7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1YsbUNBQW1DLE1BQU07QUFDekM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0EsS0FBSztBQUNMOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQSxvREFBb0QseUJBQXlCO0FBQzdFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0RBQWtELHFCQUFxQiw4QkFBOEIsTUFBTSxHQUFHLElBQUk7QUFDbEg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07O0FBRU47QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBO0FBQ0EsU0FBUztBQUNULFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esd0RBQXdELFVBQVU7QUFDbEU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDRCQUE0QixpQkFBaUIsU0FBUyxNQUFNO0FBQzVELDBDQUEwQyxTQUFTO0FBQ25EO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxpQkFBaUI7QUFDakI7QUFDQSxhQUFhO0FBQ2I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWLGdFQUFnRSxZQUFZO0FBQzVFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVztBQUNYO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLCtCQUErQixNQUFNLDhDQUE4Qyx5RkFBeUY7QUFDNUssa0JBQWtCLE1BQU07QUFDeEI7QUFDQSxzREFBc0Q7QUFDdEQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQSxvREFBb0Q7QUFDcEQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQSxpREFBaUQsTUFBTTtBQUN2RDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw0QkFBNEIsWUFBWSxHQUFHLFVBQVUsYUFBYSxNQUFNO0FBQ3hFO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBOztBQUVBLG9EQUFvRDtBQUNwRDtBQUNBO0FBQ0E7QUFDQTtBQUNBLHVEQUF1RCxNQUFNO0FBQzdEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTs7QUFFTixxREFBcUQsU0FBUztBQUM5RDtBQUNBO0FBQ0EseUVBQXlFLGFBQWEsU0FBUztBQUMvRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1AsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxpRUFBaUU7QUFDakUsYUFBYTtBQUNiO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CLFdBQVcsR0FBRyxTQUFTLElBQUksRUFBRTtBQUNqRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0JBQWtCLHFCQUFxQjtBQUN2QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0Isb0JBQW9CO0FBQ3hDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9CQUFvQixhQUFhO0FBQ2pDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0IsYUFBYTtBQUNqQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9CQUFvQixhQUFhO0FBQ2pDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0IsYUFBYTtBQUNqQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxpQ0FBaUMsZ0JBQWdCO0FBQ2pEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx1QkFBdUIsYUFBYTtBQUNwQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CLGFBQWE7QUFDakM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9CQUFvQixhQUFhO0FBQ2pDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0IsYUFBYTtBQUNqQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0IsYUFBYTtBQUNqQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CLGFBQWE7QUFDakM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9CQUFvQixhQUFhO0FBQ2pDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLHNCQUFzQixhQUFhO0FBQ25DO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwwQkFBMEIsdUJBQXVCO0FBQ2pEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsMkJBQTJCO0FBQzNCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsY0FBYztBQUNkO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CLGFBQWE7QUFDakM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDhCQUE4QjtBQUM5QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0Isa0JBQWtCO0FBQ3RDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0IscUJBQXFCO0FBQ3pDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBLFFBQVE7QUFDUjtBQUNBLFFBQVE7QUFDUjtBQUNBLFFBQVE7QUFDUjtBQUNBLFFBQVE7QUFDUjtBQUNBLFFBQVE7QUFDUjtBQUNBLFFBQVE7QUFDUjtBQUNBLFFBQVE7QUFDUjtBQUNBLFFBQVE7QUFDUjtBQUNBLFFBQVE7QUFDUjtBQUNBLFFBQVE7QUFDUjtBQUNBLFFBQVE7QUFDUjtBQUNBLFFBQVE7QUFDUjtBQUNBLFFBQVE7QUFDUjtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxlQUFlLFNBQVM7QUFDeEI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBLG9CQUFvQjtBQUNwQjs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CLHVDQUF1QztBQUMzRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9CQUFvQiwwQkFBMEI7QUFDOUM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVyxzQkFBc0I7QUFDakM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDhDQUE4QztBQUM5QztBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTCx1REFBdUQ7QUFDdkQ7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0wscURBQXFEO0FBQ3JEO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0wsbURBQW1EO0FBQ25EO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0wsZ0RBQWdEO0FBQ2hEO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLOztBQUVMO0FBQ0Esa0RBQWtEO0FBQ2xEO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0wsb0RBQW9EO0FBQ3BEO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMLHVEQUF1RDtBQUN2RDtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMLGdEQUFnRDtBQUNoRDtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMLHFEQUFxRDtBQUNyRDtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0wsb0RBQW9EO0FBQ3BEO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0wseURBQXlEO0FBQ3pEO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTCxnREFBZ0Q7QUFDaEQ7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTCxpREFBaUQ7QUFDakQ7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSzs7QUFFTDtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EseUNBQXlDLEVBQUUsTUFBTSxFQUFFO0FBQ25EO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0RBQWtEO0FBQ2xEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CLGNBQWM7QUFDbEM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsZ0JBQWdCLElBQUk7QUFDcEI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsOENBQThDLFFBQVE7QUFDdEQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLOztBQUVMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLHNDQUFzQztBQUN0QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9DQUFvQzs7QUFFcEM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGNBQWM7QUFDZDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGNBQWM7QUFDZDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0RBQW9ELFdBQVc7QUFDL0Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDBCQUEwQjtBQUMxQjtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsWUFBWTtBQUNaO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTs7QUFFQTs7QUFFQTtBQUNBLHlCQUF5QixHQUFHLE1BQU0sRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFOztBQUVwRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EseUNBQXlDLElBQUk7QUFDN0M7QUFDQSxHQUFHLElBQUk7QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHLElBQUk7QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRyxJQUFJO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHFEQUFxRCxLQUFLO0FBQzFEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBbUMsSUFBSTtBQUN2QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTs7QUFFQSw2Q0FBNkM7QUFDN0M7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBLHNCQUFzQiw2QkFBNkI7QUFDbkQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxzQkFBc0IsdUJBQXVCO0FBQzdDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw0QkFBNEIsd0JBQXdCO0FBQ3BEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFlBQVk7QUFDWjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7QUFDQSwwRUFBMEUsNEJBQTRCO0FBQ3RHO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esc0NBQXNDLG1CQUFtQjtBQUN6RDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsWUFBWTtBQUNaO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQLEtBQUs7QUFDTCwyQ0FBMkMsTUFBTTtBQUNqRDtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUCxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0EsNkNBQTZDLE1BQU07QUFDbkQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1AsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9CQUFvQixvQkFBb0I7QUFDeEM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0IsV0FBVztBQUMvQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw4Q0FBOEM7QUFDOUM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUEsd0RBQXdEO0FBQ3hEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0RBQW9ELFNBQVMsSUFBSSx3QkFBd0IsSUFBSSwwQkFBMEIsWUFBWSxnQkFBZ0IsR0FBRyxpQkFBaUI7QUFDdks7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsK0NBQStDO0FBQy9DO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0JBQW9CLG1CQUFtQjtBQUN2QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGVBQWU7QUFDZjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHdFQUF3RSxVQUFVO0FBQ2xGO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTiw4REFBOEQsVUFBVTtBQUN4RTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBO0FBQ0EsWUFBWTtBQUNaO0FBQ0EsWUFBWTtBQUNaO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFhO0FBQ2I7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBLG1GQUFtRixnQ0FBZ0M7QUFDbkg7QUFDQSxzR0FBc0csa0JBQWtCO0FBQ3hIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw4Q0FBOEMsVUFBVSxtQ0FBbUMsc0NBQXNDO0FBQ2pJO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwyREFBMkQsVUFBVSxLQUFLLE1BQU07QUFDaEYsT0FBTztBQUNQO0FBQ0EsMkNBQTJDLCtCQUErQjtBQUMxRTtBQUNBLDJDQUEyQyxVQUFVO0FBQ3JEO0FBQ0EsOENBQThDLFVBQVU7QUFDeEQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVc7QUFDWCxTQUFTO0FBQ1Q7QUFDQSx5REFBeUQsVUFBVSxFQUFFLElBQUksTUFBTTtBQUMvRSxTQUFTO0FBQ1Q7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0gsNkNBQTZDLFVBQVUsV0FBVyxxQ0FBcUM7QUFDdkc7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esc0NBQXNDLHFCQUFxQixjQUFjO0FBQ3pFLFFBQVEsZ0JBQWdCLDhCQUE4QjtBQUN0RDtBQUNBO0FBQ0E7QUFDQSx1REFBdUQ7QUFDdkQ7QUFDQSwwREFBMEQsU0FBUyxFQUFFLFVBQVUsSUFBSSxXQUFXLGdCQUFnQixzQkFBc0I7QUFDcEk7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWLG9FQUFvRSxVQUFVO0FBQzlFO0FBQ0EsT0FBTztBQUNQLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtDQUFrQyxPQUFPLFdBQVcsc0JBQXNCLFlBQVksb0JBQW9CLE9BQU8sZ0JBQWdCO0FBQ2pJLHlDQUF5QyxXQUFXO0FBQ3BEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQSwrQ0FBK0MsY0FBYyxFQUFFLGVBQWUsSUFBSSxpQkFBaUIsWUFBWSxXQUFXO0FBQzFIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVc7QUFDWDtBQUNBO0FBQ0EsU0FBUztBQUNULE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU8sc0RBQXNEO0FBQzdEO0FBQ0EsT0FBTyxFQUFFO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ04sMkJBQTJCLFdBQVcsNEJBQTRCLGFBQWE7QUFDL0U7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUixxQkFBcUIsWUFBWSx3QkFBd0IsTUFBTTtBQUMvRDtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx1QkFBdUIsWUFBWTtBQUNuQyxVQUFVO0FBQ1YsK0JBQStCLFlBQVksTUFBTSxnRkFBZ0Y7QUFDakk7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQSxvQkFBb0IsNkJBQTZCO0FBQ2pEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVztBQUNYO0FBQ0EsU0FBUztBQUNULE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBLGlCQUFpQixXQUFXO0FBQzVCO0FBQ0E7QUFDQTtBQUNBLHdDQUF3QyxVQUFVO0FBQ2xEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQSxzQ0FBc0MsVUFBVTtBQUNoRDtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esb0RBQW9ELE9BQU87QUFDM0Q7QUFDQTtBQUNBO0FBQ0Esb0RBQW9ELE9BQU8sS0FBSyxPQUFPLG1CQUFtQixjQUFjLFVBQVUsc0NBQXNDO0FBQ3hKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSLG1CQUFtQixZQUFZLCtCQUErQixxQkFBcUIsa0JBQWtCLG1CQUFtQjtBQUN4SDtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVCxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1IsdURBQXVELFlBQVk7QUFDbkU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EscUJBQXFCLG1CQUFtQixrQkFBa0IsTUFBTTtBQUNoRTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVc7QUFDWCxVQUFVO0FBQ1Y7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLDRCQUE0QixVQUFVO0FBQ2pELFVBQVU7QUFDVjtBQUNBLFVBQVU7QUFDVixvREFBb0QsVUFBVTtBQUM5RDtBQUNBLE9BQU87QUFDUCxLQUFLO0FBQ0w7QUFDQTtBQUNBLHFEQUFxRCxzR0FBc0csV0FBVyxNQUFNO0FBQzVLLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTywyQ0FBMkMsTUFBTTtBQUN4RCxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0EscUNBQXFDLE9BQU8sMkJBQTJCLDJIQUEySCxpQkFBaUIsNkVBQTZFLE9BQU8sdUNBQXVDO0FBQzlVO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGlEQUFpRCxVQUFVO0FBQzNEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQWE7QUFDYixXQUFXLE1BQU0sVUFBVSxnQ0FBZ0MsSUFBSSxhQUFhLGVBQWUsR0FBRyxjQUFjO0FBQzVHLFNBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVcsTUFBTSxVQUFVLG1DQUFtQyxJQUFJO0FBQ2xFLFNBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0EseUNBQXlDLDhDQUE4QyxHQUFHLHdDQUF3QyxRQUFRLFVBQVU7QUFDcEo7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVCxPQUFPO0FBQ1AsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVCxPQUFPO0FBQ1AsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esc0RBQXNEO0FBQ3REO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsNENBQTRDLFNBQVM7QUFDckQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxrREFBa0QsSUFBSTtBQUN0RDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHlDQUF5QyxxREFBcUQ7QUFDOUY7QUFDQTtBQUNBO0FBQ0E7QUFDQSxnQkFBZ0I7QUFDaEI7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsZUFBZSxpQ0FBaUMsSUFBSSxhQUFhLFlBQVksR0FBRyxlQUFlO0FBQy9GLGNBQWM7QUFDZDtBQUNBLHFEQUFxRCxjQUFjO0FBQ25FO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUCxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSw4Q0FBOEMsTUFBTTtBQUNwRCxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0wsaUVBQWlFLE1BQU07QUFDdkUsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1AseURBQXlELHNCQUFzQjtBQUMvRTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBLDZEQUE2RCwyQkFBMkI7QUFDeEY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDhDQUE4QyxNQUFNO0FBQ3BELE9BQU87QUFDUDtBQUNBLE9BQU87QUFDUCw2Q0FBNkMsTUFBTTtBQUNuRCxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxDQUFDLG9DQUFvQzs7QUFFckM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUMsOENBQThDOztBQUUvQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUMsMENBQTBDOztBQUUzQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGlCQUFpQjtBQUNqQjtBQUNBO0FBQ0EsaUJBQWlCO0FBQ2pCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsZ0NBQWdDLFFBQVEsR0FBRyxZQUFZLE9BQU8sS0FBSztBQUNuRTtBQUNBLEdBQUc7QUFDSDs7QUFFQTs7QUFFQTs7QUFFQTs7QUFFQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUEseUNBQXlDOztBQUV6Qzs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFhLG9CQUFvQjtBQUNqQzs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYSx5Q0FBeUM7QUFDdEQ7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsK0NBQStDO0FBQy9DO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsOENBQThDO0FBQzlDO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esc0RBQXNELFlBQVk7QUFDbEU7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGlFQUFpRTtBQUNqRTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQWEsa0RBQWtEO0FBQy9EOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsOERBQThEO0FBQzlEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0I7QUFDcEI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsZUFBZSxFQUFFLGtCQUFrQixHQUFHO0FBQ3RDO0FBQ0EsYUFBYSxFQUFFLGtCQUFrQixHQUFHLHlCQUF5QjtBQUM3RCxHQUFHO0FBQ0g7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxjQUFjLCtCQUErQixFQUFFLDhCQUE4QjtBQUM3RSxJQUFJO0FBQ0o7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxhQUFhLHlDQUF5QyxHQUFHLDhCQUE4QjtBQUN2Rjs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUM7QUFDRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRyxXQUFXLG1CQUFtQjtBQUNqQzs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGdDQUFnQztBQUNoQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esc0NBQXNDO0FBQ3RDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EseUNBQXlDO0FBQ3pDO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0lBQWtJO0FBQ2xJO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRyxJQUFJO0FBQ1A7QUFDQTtBQUNBO0FBQ0EsR0FBRyxJQUFJO0FBQ1A7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx1Q0FBdUM7QUFDdkM7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZLFdBQVcsR0FBRywyQkFBMkI7QUFDckQ7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZLElBQUksRUFBRSxVQUFVLEVBQUUsTUFBTTtBQUNwQzs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1QsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSwwQkFBMEI7QUFDMUI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTyxJQUFJO0FBQ1g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBLFVBQVU7QUFDVjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx5Q0FBeUMsY0FBYyxJQUFJLG1CQUFtQix3Q0FBd0MsY0FBYyxVQUFVLGlDQUFpQyxjQUFjLGlDQUFpQyxhQUFhLHVDQUF1QztBQUNsUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw2Q0FBNkMsZUFBZSxnQ0FBZ0MsVUFBVTtBQUN0RztBQUNBO0FBQ0E7QUFDQTtBQUNBLHdCQUF3QixxQkFBcUIsR0FBRyxlQUFlLHFCQUFxQixlQUFlO0FBQ25HO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMLG9CQUFvQiw0QkFBNEI7QUFDaEQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHdDQUF3QyxVQUFVO0FBQ2xEO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsOERBQThELHVCQUF1QixLQUFLLHlCQUF5QjtBQUNuSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0IsbUJBQW1CO0FBQ3ZDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwwREFBMEQsaUJBQWlCLFNBQVMsUUFBUTtBQUM1RixpRUFBaUUscUJBQXFCLFNBQVMsUUFBUTtBQUN2RztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQSwwQkFBMEIsa0NBQWtDO0FBQzVELCtDQUErQyx5QkFBeUIsU0FBUyxRQUFRO0FBQ3pGO0FBQ0E7QUFDQTtBQUNBLDBCQUEwQixxQ0FBcUM7QUFDL0QsOENBQThDLDRCQUE0QixTQUFTLFFBQVE7QUFDM0Y7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0EseURBQXlELElBQUk7QUFDN0Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwrQ0FBK0MsSUFBSTtBQUNuRDtBQUNBO0FBQ0EsdUNBQXVDLHNCQUFzQjtBQUM3RDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBLFlBQVk7QUFDWjtBQUNBLHNFQUFzRSxVQUFVO0FBQ2hGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBLHFEQUFxRCxZQUFZLEVBQUUsWUFBWSxHQUFHLFlBQVk7QUFDOUY7QUFDQTtBQUNBO0FBQ0Esd0NBQXdDLGFBQWE7QUFDckQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHdDQUF3QyxhQUFhO0FBQ3JEO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBLHVEQUF1RCxZQUFZO0FBQ25FO0FBQ0E7QUFDQTtBQUNBLDhDQUE4QyxJQUFJO0FBQ2xEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EscUNBQXFDO0FBQ3JDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0EsT0FBTztBQUNQLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1osNEJBQTRCLFFBQVEsZ0JBQWdCLFlBQVk7QUFDaEU7QUFDQTtBQUNBO0FBQ0EsYUFBYTtBQUNiO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQSwyQ0FBMkMsb0VBQW9FO0FBQy9HO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBLG1CQUFtQiw4Q0FBOEMsZ0JBQWdCLHVDQUF1QyxhQUFhLFlBQVksR0FBRyx5QkFBeUIsS0FBSyxnQkFBZ0I7QUFDbE07QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHNDQUFzQyxLQUFLO0FBQzNDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGdDQUFnQyxHQUFHO0FBQ25DO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUCxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EseUNBQXlDO0FBQ3pDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxvQkFBb0IsK0JBQStCO0FBQ25EO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHdCQUF3QixzQkFBc0I7QUFDOUM7QUFDQTtBQUNBLFlBQVk7QUFDWjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsZ0JBQWdCO0FBQ2hCO0FBQ0Esc0JBQXNCO0FBQ3RCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsQ0FBQyx1QkFBdUI7QUFDeEI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLENBQUM7QUFDRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSwwQkFBMEIscUNBQXFDO0FBQy9EO0FBQ0E7QUFDQTtBQUNBLG1DQUFtQyxLQUFLLFNBQVMsUUFBUTtBQUN6RDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0EscUNBQXFDLG9CQUFvQixvQ0FBb0MsV0FBVyxLQUFLLHVDQUF1QztBQUNwSjtBQUNBLEdBQUc7QUFDSCx5Q0FBeUM7QUFDekM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSyxJQUFJO0FBQ1Q7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSLHVDQUF1QyxlQUFlO0FBQ3RELDBCQUEwQixzQkFBc0IsRUFBRSxvQkFBb0IsR0FBRyxXQUFXLEdBQUcsVUFBVSxHQUFHLE9BQU8sR0FBRyxZQUFZLEdBQUcsS0FBSztBQUNsSTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esc0VBQXNFLHFDQUFxQztBQUMzRztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXO0FBQ1g7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBLHdCQUF3QiwyQkFBMkI7QUFDbkQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLG9CQUFvQixtQkFBbUI7QUFDdkM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHFDQUFxQyxlQUFlLGlDQUFpQyxrQkFBa0I7QUFDdkc7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsbUNBQW1DLFVBQVUsR0FBRyx3Q0FBd0MsRUFBRSwrQ0FBK0MsRUFBRSwyQ0FBMkMsR0FBRyxjQUFjLEdBQUcsK0NBQStDLGFBQWEsZUFBZSxFQUFFLHNEQUFzRDtBQUM3VTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsbURBQW1ELGlCQUFpQjtBQUNwRTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0EsdUNBQXVDLE1BQU07QUFDN0M7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFVBQVU7QUFDVixpRkFBaUYsTUFBTTtBQUN2RjtBQUNBO0FBQ0E7QUFDQSxzQ0FBc0Msa0JBQWtCLEVBQUUscUpBQXFKLE1BQU0sMENBQTBDLEVBQUUsSUFBSTs7QUFFclE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSLHNCQUFzQiwrQkFBK0I7QUFDckQ7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXO0FBQ1g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxnSEFBZ0gsZ0JBQWdCO0FBQ2hJO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esc0hBQXNILElBQUk7QUFDMUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXO0FBQ1g7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDZJQUE2SSxtQkFBbUI7QUFDaEs7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVztBQUNYLFNBQVM7QUFDVDtBQUNBO0FBQ0Esb0dBQW9HLGVBQWUsY0FBYyxjQUFjO0FBQy9JO0FBQ0E7QUFDQSxXQUFXO0FBQ1gsU0FBUztBQUNUO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHFEQUFxRCxZQUFZLFVBQVUsNkJBQTZCO0FBQ3hHO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQSx1REFBdUQsbUJBQW1CLHFCQUFxQiwyQkFBMkI7QUFDMUg7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxrQkFBa0I7QUFDbEI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGlFQUFpRSxhQUFhLEtBQUssV0FBVztBQUM5RjtBQUNBO0FBQ0E7QUFDQTtBQUNBLHFGQUFxRixhQUFhLEtBQUssV0FBVztBQUNsSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVc7QUFDWDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDREQUE0RCxhQUFhLEtBQUssV0FBVztBQUN6RjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUCxNQUFNO0FBQ04sdUZBQXVGLGFBQWEsUUFBUSxzQkFBc0I7QUFDbEk7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTs7QUFFQSwyQkFBMkI7O0FBRTNCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGlFQUFpRSwyQkFBMkI7QUFDNUY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFlBQVk7QUFDWjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxjQUFjO0FBQ2Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07O0FBRU47QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxvQ0FBb0MsT0FBTyxhQUFhLFdBQVc7QUFDbkU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSLDZCQUE2QixTQUFTLFdBQVcsWUFBWTtBQUM3RDtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtDQUFrQyx1QkFBdUI7QUFDekQ7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsZ0VBQWdFLHNDQUFzQztBQUN0RztBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBLHVEQUF1RDtBQUN2RDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQSx5REFBeUQsV0FBVztBQUNwRTtBQUNBO0FBQ0Esc0JBQXNCLFlBQVksVUFBVSxtQkFBbUIsR0FBRyxpQkFBaUIsR0FBRyxpQ0FBaUMsc0JBQXNCLEdBQUcseUJBQXlCLFFBQVEsUUFBUSxtQkFBbUIsSUFBSSxpQkFBaUIsYUFBYSxTQUFTO0FBQ3ZQO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSzs7QUFFTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0EsbUZBQW1GLFNBQVMsV0FBVyxZQUFZO0FBQ25IO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxxQ0FBcUMsU0FBUyxXQUFXLFlBQVk7QUFDckU7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSwrQkFBK0IsU0FBUyxNQUFNLGlCQUFpQixHQUFHLGNBQWMsVUFBVSxXQUFXLE9BQU8sUUFBUTtBQUNwSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBLDZDQUE2QyxLQUFLO0FBQ2xEO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDRCQUE0QixRQUFRLEVBQUUsaUNBQWlDLFdBQVcsWUFBWSw4Q0FBOEMsV0FBVztBQUN2SjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esc0NBQXNDLGNBQWMsdUJBQXVCLFlBQVk7QUFDdkY7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsZ0RBQWdELE9BQU87QUFDdkQ7QUFDQTtBQUNBO0FBQ0EsZ0RBQWdELGVBQWUsb0JBQW9CLFlBQVk7QUFDL0Y7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsY0FBYztBQUNkO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0RBQWtELFdBQVc7QUFDN0Q7QUFDQTtBQUNBLG1EQUFtRCx3QkFBd0IsU0FBUyxXQUFXO0FBQy9GO0FBQ0E7QUFDQTtBQUNBLCtDQUErQyxnQkFBZ0IsbUNBQW1DLGlCQUFpQixHQUFHLDhCQUE4QixHQUFHLFlBQVk7QUFDbks7QUFDQTtBQUNBO0FBQ0E7QUFDQSwrQ0FBK0MsZ0JBQWdCLDBCQUEwQiw4QkFBOEIsR0FBRyxZQUFZO0FBQ3RJO0FBQ0E7QUFDQSxvREFBb0QscUJBQXFCLDBCQUEwQixvQkFBb0IsR0FBRyxpQkFBaUI7QUFDM0k7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVc7QUFDWDtBQUNBO0FBQ0E7QUFDQSxhQUFhO0FBQ2I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsNkJBQTZCO0FBQzdCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBLFFBQVE7QUFDUjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVztBQUNYO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBLDZCQUE2QixjQUFjO0FBQzNDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSw0QkFBNEIsY0FBYztBQUMxQztBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBbUMsU0FBUztBQUM1QztBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsZ0NBQWdDLFNBQVM7QUFDekM7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGdDQUFnQyxTQUFTO0FBQ3pDO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsaUNBQWlDLFNBQVM7QUFDMUM7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGlDQUFpQyxTQUFTO0FBQzFDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGdEQUFnRDtBQUNoRCxRQUFRO0FBQ1I7QUFDQTtBQUNBLGlEQUFpRDtBQUNqRDtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLFlBQVk7QUFDWjtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx5Q0FBeUMsU0FBUztBQUNsRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0Esb0JBQW9CLFNBQVM7QUFDN0I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBLGlDQUFpQyxJQUFJO0FBQ3JDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRTRvQjtBQUM1b0IiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9teWhscy8uL3NyYy9NZWRpYVNvdXJjZVN0dWIuanMiLCJ3ZWJwYWNrOi8vbXlobHMvLi9zcmMvVGV4dFRyYWNrU3R1Yi5qcyIsIndlYnBhY2s6Ly9teWhscy8uL3NyYy9UaW1lUmFuZ2VzU3R1Yi5qcyIsIndlYnBhY2s6Ly9teWhscy8uL3NyYy9WaWRlb0VsZW1lbnRTdHViLmpzIiwid2VicGFjazovL215aGxzLy4vc3JjL2luZGV4LmpzIiwid2VicGFjazovL215aGxzLy4vbm9kZV9tb2R1bGVzL2hscy5qcy9kaXN0L2hscy5tanMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgVGltZVJhbmdlc1N0dWIgfSBmcm9tIFwiLi9UaW1lUmFuZ2VzU3R1Yi5qc1wiXG5cbmZ1bmN0aW9uIGJ5dGVzVG9CYXNlNjQoYnl0ZXMpIHtcbiAgICBjb25zdCBiaW5TdHJpbmcgPSBBcnJheS5mcm9tKGJ5dGVzLCAoYnl0ZSkgPT5cbiAgICAgICAgU3RyaW5nLmZyb21Db2RlUG9pbnQoYnl0ZSksXG4gICAgKS5qb2luKFwiXCIpO1xuICAgIHJldHVybiBidG9hKGJpblN0cmluZyk7XG59XG5cbmV4cG9ydCBjbGFzcyBTb3VyY2VCdWZmZXJMaXN0U3R1YiBleHRlbmRzIEV2ZW50VGFyZ2V0IHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgc3VwZXIoKTtcbiAgICAgICAgdGhpcy5fYnVmZmVycyA9IFtdO1xuICAgIH1cblxuICAgIF9hZGQoYnVmZmVyKSB7XG4gICAgICAgIHRoaXMuX2J1ZmZlcnMucHVzaChidWZmZXIpO1xuICAgICAgICB0aGlzLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCdhZGRzb3VyY2VidWZmZXInKSk7XG4gICAgfVxuXG4gICAgX3JlbW92ZShidWZmZXIpIHtcbiAgICAgICAgY29uc3QgaW5kZXggPSB0aGlzLl9idWZmZXJzLmluZGV4T2YoYnVmZmVyKTtcbiAgICAgICAgaWYgKGluZGV4ID09PSAtMSkge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuX2J1ZmZlcnMuc3BsaWNlKGluZGV4LCAxKTtcbiAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgncmVtb3Zlc291cmNlYnVmZmVyJykpO1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICBnZXQgbGVuZ3RoKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fYnVmZmVycy5sZW5ndGg7XG4gICAgfVxuXG4gICAgaXRlbShpbmRleCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fYnVmZmVyc1tpbmRleF07XG4gICAgfVxuXG4gICAgW1N5bWJvbC5pdGVyYXRvcl0oKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9idWZmZXJzW1N5bWJvbC5pdGVyYXRvcl0oKTtcbiAgICB9XG59XG5cbmV4cG9ydCBjbGFzcyBTb3VyY2VCdWZmZXJTdHViIGV4dGVuZHMgRXZlbnRUYXJnZXQge1xuICAgIGNvbnN0cnVjdG9yKG1lZGlhU291cmNlLCBtaW1lVHlwZSkge1xuICAgICAgICBzdXBlcigpO1xuICAgICAgICB0aGlzLm1lZGlhU291cmNlID0gbWVkaWFTb3VyY2U7XG4gICAgICAgIHRoaXMubWltZVR5cGUgPSBtaW1lVHlwZTtcbiAgICAgICAgdGhpcy51cGRhdGluZyA9IGZhbHNlO1xuICAgICAgICB0aGlzLmJ1ZmZlcmVkID0gbmV3IFRpbWVSYW5nZXNTdHViKCk7XG4gICAgICAgIHRoaXMudGltZXN0YW1wT2Zmc2V0ID0gMDtcbiAgICAgICAgdGhpcy5hcHBlbmRXaW5kb3dTdGFydCA9IDA7XG4gICAgICAgIHRoaXMuYXBwZW5kV2luZG93RW5kID0gSW5maW5pdHk7XG5cbiAgICAgICAgdGhpcy5icmlkZ2VJZCA9IHdpbmRvdy5uZXh0SW50ZXJuYWxJZDtcbiAgICAgICAgd2luZG93Lm5leHRJbnRlcm5hbElkICs9IDE7XG4gICAgICAgIHdpbmRvdy5icmlkZ2VPYmplY3RNYXBbdGhpcy5icmlkZ2VJZF0gPSB0aGlzO1xuXG4gICAgICAgIHdpbmRvdy5icmlkZ2VJbnZva2VBc3luYyh0aGlzLmJyaWRnZUlkLCBcIlNvdXJjZUJ1ZmZlclwiLCBcImNvbnN0cnVjdG9yXCIsIHtcbiAgICAgICAgICAgIFwibWltZVR5cGVcIjogbWltZVR5cGVcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgYXBwZW5kQnVmZmVyKGRhdGEpIHtcbiAgICAgICAgaWYgKHRoaXMudXBkYXRpbmcpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBET01FeGNlcHRpb24oJ1NvdXJjZUJ1ZmZlciBpcyB1cGRhdGluZycsICdJbnZhbGlkU3RhdGVFcnJvcicpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMudXBkYXRpbmcgPSB0cnVlO1xuICAgICAgICB0aGlzLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCd1cGRhdGVzdGFydCcpKTtcblxuICAgICAgICB3aW5kb3cuYnJpZGdlSW52b2tlQXN5bmModGhpcy5icmlkZ2VJZCwgXCJTb3VyY2VCdWZmZXJcIiwgXCJhcHBlbmRCdWZmZXJcIiwge1xuICAgICAgICAgICAgXCJkYXRhXCI6IGJ5dGVzVG9CYXNlNjQoZGF0YSlcbiAgICAgICAgfSkudGhlbigocmVzdWx0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCB1cGRhdGVkUmFuZ2VzID0gcmVzdWx0W1wicmFuZ2VzXCJdO1xuICAgICAgICAgICAgdmFyIHJhbmdlcyA9IFtdO1xuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB1cGRhdGVkUmFuZ2VzLmxlbmd0aDsgaSArPSAyKSB7XG4gICAgICAgICAgICAgICAgcmFuZ2VzLnB1c2goe1xuICAgICAgICAgICAgICAgICAgICBzdGFydDogdXBkYXRlZFJhbmdlc1tpXSxcbiAgICAgICAgICAgICAgICAgICAgZW5kOiB1cGRhdGVkUmFuZ2VzW2kgKyAxXVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5idWZmZXJlZC5fcmFuZ2VzID0gcmFuZ2VzO1xuXG4gICAgICAgICAgICB0aGlzLm1lZGlhU291cmNlLl9yZW9wZW4oKTtcblxuICAgICAgICAgICAgdGhpcy51cGRhdGluZyA9IGZhbHNlO1xuICAgICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgndXBkYXRlJykpO1xuICAgICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgndXBkYXRlZW5kJykpO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBhYm9ydCgpIHtcbiAgICAgICAgaWYgKHRoaXMudXBkYXRpbmcpIHtcbiAgICAgICAgICAgIHRoaXMudXBkYXRpbmcgPSBmYWxzZTtcbiAgICAgICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2Fib3J0JykpO1xuXG4gICAgICAgICAgICB3aW5kb3cuYnJpZGdlSW52b2tlQXN5bmModGhpcy5icmlkZ2VJZCwgXCJTb3VyY2VCdWZmZXJcIiwgXCJhYm9ydFwiLCB7fSkudGhlbigocmVzdWx0KSA9PiB7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHJlbW92ZShzdGFydCwgZW5kKSB7XG4gICAgICAgIGlmICh0aGlzLnVwZGF0aW5nKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRE9NRXhjZXB0aW9uKCdTb3VyY2VCdWZmZXIgaXMgdXBkYXRpbmcnLCAnSW52YWxpZFN0YXRlRXJyb3InKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnVwZGF0aW5nID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgndXBkYXRlc3RhcnQnKSk7XG5cbiAgICAgICAgd2luZG93LmJyaWRnZUludm9rZUFzeW5jKHRoaXMuYnJpZGdlSWQsIFwiU291cmNlQnVmZmVyXCIsIFwicmVtb3ZlXCIsIHtcbiAgICAgICAgICAgIFwic3RhcnRcIjogc3RhcnQsXG4gICAgICAgICAgICBcImVuZFwiOiBlbmRcbiAgICAgICAgfSkudGhlbigocmVzdWx0KSA9PiB7XG4gICAgICAgICAgICBjb25zdCB1cGRhdGVkUmFuZ2VzID0gcmVzdWx0W1wicmFuZ2VzXCJdO1xuICAgICAgICAgICAgdmFyIHJhbmdlcyA9IFtdO1xuICAgICAgICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB1cGRhdGVkUmFuZ2VzLmxlbmd0aDsgaSArPSAyKSB7XG4gICAgICAgICAgICAgICAgcmFuZ2VzLnB1c2goe1xuICAgICAgICAgICAgICAgICAgICBzdGFydDogdXBkYXRlZFJhbmdlc1tpXSxcbiAgICAgICAgICAgICAgICAgICAgZW5kOiB1cGRhdGVkUmFuZ2VzW2kgKyAxXVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5idWZmZXJlZC5fcmFuZ2VzID0gcmFuZ2VzO1xuXG4gICAgICAgICAgICB0aGlzLm1lZGlhU291cmNlLl9yZW9wZW4oKTtcblxuICAgICAgICAgICAgdGhpcy51cGRhdGluZyA9IGZhbHNlO1xuICAgICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgndXBkYXRlJykpO1xuICAgICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgndXBkYXRlZW5kJykpO1xuICAgICAgICB9KTtcbiAgICB9XG59XG5cbmV4cG9ydCBjbGFzcyBNZWRpYVNvdXJjZVN0dWIgZXh0ZW5kcyBFdmVudFRhcmdldCB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHN1cGVyKCk7XG5cbiAgICAgICAgdGhpcy5pbnRlcm5hbElkID0gd2luZG93Lm5leHRJbnRlcm5hbElkO1xuICAgICAgICB3aW5kb3cubmV4dEludGVybmFsSWQgKz0gMTtcblxuICAgICAgICB0aGlzLmJyaWRnZUlkID0gd2luZG93Lm5leHRJbnRlcm5hbElkO1xuICAgICAgICB3aW5kb3cubmV4dEludGVybmFsSWQgKz0gMTtcbiAgICAgICAgd2luZG93LmJyaWRnZU9iamVjdE1hcFt0aGlzLmJyaWRnZUlkXSA9IHRoaXM7XG5cbiAgICAgICAgdGhpcy5zb3VyY2VCdWZmZXJzID0gbmV3IFNvdXJjZUJ1ZmZlckxpc3RTdHViKCk7XG4gICAgICAgIHRoaXMuYWN0aXZlU291cmNlQnVmZmVycyA9IG5ldyBTb3VyY2VCdWZmZXJMaXN0U3R1YigpO1xuICAgICAgICB0aGlzLnJlYWR5U3RhdGUgPSAnY2xvc2VkJztcbiAgICAgICAgdGhpcy5fZHVyYXRpb24gPSBOYU47XG5cbiAgICAgICAgd2luZG93LmJyaWRnZUludm9rZUFzeW5jKHRoaXMuYnJpZGdlSWQsIFwiTWVkaWFTb3VyY2VcIiwgXCJjb25zdHJ1Y3RvclwiLCB7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vIFNpbXVsYXRlIGFzeW5jaHJvbm91cyBvcGVuaW5nIG9mIE1lZGlhU291cmNlXG4gICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5yZWFkeVN0YXRlID0gJ29wZW4nO1xuICAgICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgnc291cmNlb3BlbicpKTtcbiAgICAgICAgfSwgMCk7XG4gICAgfVxuXG4gICAgc3RhdGljIGlzVHlwZVN1cHBvcnRlZChtaW1lVHlwZSkge1xuICAgICAgICAvLyBBc3N1bWUgYWxsIE1JTUUgdHlwZXMgYXJlIHN1cHBvcnRlZCBpbiB0aGlzIHN0dWJcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgYWRkU291cmNlQnVmZmVyKG1pbWVUeXBlKSB7XG4gICAgICAgIGlmICh0aGlzLnJlYWR5U3RhdGUgIT09ICdvcGVuJykge1xuICAgICAgICAgICAgdGhyb3cgbmV3IERPTUV4Y2VwdGlvbignTWVkaWFTb3VyY2UgaXMgbm90IG9wZW4nLCAnSW52YWxpZFN0YXRlRXJyb3InKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBzb3VyY2VCdWZmZXIgPSBuZXcgU291cmNlQnVmZmVyU3R1Yih0aGlzLCBtaW1lVHlwZSk7XG4gICAgICAgIHRoaXMuc291cmNlQnVmZmVycy5fYWRkKHNvdXJjZUJ1ZmZlcik7XG4gICAgICAgIHRoaXMuYWN0aXZlU291cmNlQnVmZmVycy5fYWRkKHNvdXJjZUJ1ZmZlcik7XG4gICAgICAgIHJldHVybiBzb3VyY2VCdWZmZXI7XG4gICAgfVxuXG4gICAgcmVtb3ZlU291cmNlQnVmZmVyKHNvdXJjZUJ1ZmZlcikge1xuICAgICAgICBpZiAoIXRoaXMuc291cmNlQnVmZmVycy5fcmVtb3ZlKHNvdXJjZUJ1ZmZlcikpIHtcbiAgICAgICAgdGhyb3cgbmV3IERPTUV4Y2VwdGlvbignU291cmNlQnVmZmVyIG5vdCBmb3VuZCcsICdOb3RGb3VuZEVycm9yJyk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5hY3RpdmVTb3VyY2VCdWZmZXJzLl9yZW1vdmUoc291cmNlQnVmZmVyKTtcbiAgICB9XG5cbiAgICBlbmRPZlN0cmVhbShlcnJvcikge1xuICAgICAgICBpZiAodGhpcy5yZWFkeVN0YXRlICE9PSAnb3BlbicpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBET01FeGNlcHRpb24oJ01lZGlhU291cmNlIGlzIG5vdCBvcGVuJywgJ0ludmFsaWRTdGF0ZUVycm9yJyk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5yZWFkeVN0YXRlID0gJ2VuZGVkJztcbiAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgnc291cmNlZW5kZWQnKSk7XG4gICAgfVxuXG4gICAgX3Jlb3BlbigpIHtcbiAgICAgICAgaWYgKHRoaXMucmVhZHlTdGF0ZSAhPT0gJ29wZW4nKSB7XG4gICAgICAgICAgICB0aGlzLnJlYWR5U3RhdGUgPSAnb3Blbic7XG4gICAgICAgICAgICB0aGlzLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCdzb3VyY2VvcGVuJykpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgc2V0IGR1cmF0aW9uKHZhbHVlKSB7XG4gICAgICAgIGlmICh0aGlzLnJlYWR5U3RhdGUgPT09ICdjbG9zZWQnKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRE9NRXhjZXB0aW9uKCdNZWRpYVNvdXJjZSBpcyBjbG9zZWQnLCAnSW52YWxpZFN0YXRlRXJyb3InKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLl9kdXJhdGlvbiA9IHZhbHVlO1xuXG4gICAgICAgIHdpbmRvdy5icmlkZ2VJbnZva2VBc3luYyh0aGlzLmJyaWRnZUlkLCBcIk1lZGlhU291cmNlXCIsIFwic2V0RHVyYXRpb25cIiwge1xuICAgICAgICAgICAgXCJkdXJhdGlvblwiOiB2YWx1ZVxuICAgICAgICB9KS50aGVuKChyZXN1bHQpID0+IHtcbiAgICAgICAgfSlcbiAgICB9XG5cbiAgICBnZXQgZHVyYXRpb24oKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9kdXJhdGlvbjtcbiAgICB9XG59XG4iLCJcbmV4cG9ydCBjbGFzcyBUZXh0VHJhY2tTdHViIGV4dGVuZHMgRXZlbnRUYXJnZXQge1xuICAgIGNvbnN0cnVjdG9yKGtpbmQgPSAnJywgbGFiZWwgPSAnJywgbGFuZ3VhZ2UgPSAnJykge1xuICAgICAgICBzdXBlcigpO1xuICAgICAgICB0aGlzLmtpbmQgPSBraW5kO1xuICAgICAgICB0aGlzLmxhYmVsID0gbGFiZWw7XG4gICAgICAgIHRoaXMubGFuZ3VhZ2UgPSBsYW5ndWFnZTtcbiAgICAgICAgdGhpcy5tb2RlID0gJ2Rpc2FibGVkJzsgLy8gJ2Rpc2FibGVkJywgJ2hpZGRlbicsIG9yICdzaG93aW5nJ1xuICAgICAgICB0aGlzLmN1ZXMgPSBuZXcgVGV4dFRyYWNrQ3VlTGlzdFN0dWIoKTtcbiAgICAgICAgdGhpcy5hY3RpdmVDdWVzID0gbmV3IFRleHRUcmFja0N1ZUxpc3RTdHViKCk7XG4gICAgfVxuXG4gICAgYWRkQ3VlKGN1ZSkge1xuICAgICAgICB0aGlzLmN1ZXMuX2FkZChjdWUpO1xuICAgIH1cblxuICAgIHJlbW92ZUN1ZShjdWUpIHtcbiAgICAgICAgdGhpcy5jdWVzLl9yZW1vdmUoY3VlKTtcbiAgICB9XG59XG5cbmV4cG9ydCBjbGFzcyBUZXh0VHJhY2tDdWVMaXN0U3R1YiB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHRoaXMuX2N1ZXMgPSBbXTtcbiAgICB9XG5cbiAgICBnZXQgbGVuZ3RoKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fY3Vlcy5sZW5ndGg7XG4gICAgfVxuXG4gICAgaXRlbShpbmRleCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fY3Vlc1tpbmRleF07XG4gICAgfVxuXG4gICAgZ2V0Q3VlQnlJZChpZCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fY3Vlcy5maW5kKGN1ZSA9PiBjdWUuaWQgPT09IGlkKSB8fCBudWxsO1xuICAgIH1cblxuICAgIF9hZGQoY3VlKSB7XG4gICAgICAgIHRoaXMuX2N1ZXMucHVzaChjdWUpO1xuICAgIH1cblxuICAgIF9yZW1vdmUoY3VlKSB7XG4gICAgICAgIGNvbnN0IGluZGV4ID0gdGhpcy5fY3Vlcy5pbmRleE9mKGN1ZSk7XG4gICAgICAgIGlmIChpbmRleCAhPT0gLTEpIHtcbiAgICAgICAgdGhpcy5fY3Vlcy5zcGxpY2UoaW5kZXgsIDEpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgW1N5bWJvbC5pdGVyYXRvcl0oKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9jdWVzW1N5bWJvbC5pdGVyYXRvcl0oKTtcbiAgICB9XG59XG5cbmV4cG9ydCBjbGFzcyBUZXh0VHJhY2tMaXN0U3R1YiBleHRlbmRzIEV2ZW50VGFyZ2V0IHtcbiAgICBjb25zdHJ1Y3RvcigpIHtcbiAgICAgICAgc3VwZXIoKTtcbiAgICAgICAgdGhpcy5fdHJhY2tzID0gW107XG4gICAgfVxuXG4gICAgZ2V0IGxlbmd0aCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX3RyYWNrcy5sZW5ndGg7XG4gICAgfVxuXG4gICAgaXRlbShpbmRleCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fdHJhY2tzW2luZGV4XTtcbiAgICB9XG5cbiAgICBfYWRkKHRyYWNrKSB7XG4gICAgICAgIHRoaXMuX3RyYWNrcy5wdXNoKHRyYWNrKTtcbiAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgnYWRkdHJhY2snKSk7XG4gICAgfVxuXG4gICAgX3JlbW92ZSh0cmFjaykge1xuICAgICAgICBjb25zdCBpbmRleCA9IHRoaXMuX3RyYWNrcy5pbmRleE9mKHRyYWNrKTtcbiAgICAgICAgaWYgKGluZGV4ICE9PSAtMSkge1xuICAgICAgICB0aGlzLl90cmFja3Muc3BsaWNlKGluZGV4LCAxKTtcbiAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgncmVtb3ZldHJhY2snKSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBbU3ltYm9sLml0ZXJhdG9yXSgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX3RyYWNrc1tTeW1ib2wuaXRlcmF0b3JdKCk7XG4gICAgfVxufVxuIiwiXG5leHBvcnQgY2xhc3MgVGltZVJhbmdlc1N0dWIge1xuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLl9yYW5nZXMgPSBbXTtcbiAgICB9XG5cbiAgICBnZXQgbGVuZ3RoKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5fcmFuZ2VzLmxlbmd0aDtcbiAgICB9XG5cbiAgICBzdGFydChpbmRleCkge1xuICAgICAgICBpZiAoaW5kZXggPCAwIHx8IGluZGV4ID49IHRoaXMuX3Jhbmdlcy5sZW5ndGgpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBET01FeGNlcHRpb24oJ0ludmFsaWQgaW5kZXgnLCAnSW5kZXhTaXplRXJyb3InKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcy5fcmFuZ2VzW2luZGV4XS5zdGFydDtcbiAgICB9XG5cbiAgICBlbmQoaW5kZXgpIHtcbiAgICAgICAgaWYgKGluZGV4IDwgMCB8fCBpbmRleCA+PSB0aGlzLl9yYW5nZXMubGVuZ3RoKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRE9NRXhjZXB0aW9uKCdJbnZhbGlkIGluZGV4JywgJ0luZGV4U2l6ZUVycm9yJyk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXMuX3Jhbmdlc1tpbmRleF0uZW5kO1xuICAgIH1cblxuICAgIC8vIEhlbHBlciBtZXRob2QgdG8gYWRkIGEgcmFuZ2VcbiAgICBfYWRkUmFuZ2Uoc3RhcnQsIGVuZCkge1xuICAgICAgICB0aGlzLl9yYW5nZXMucHVzaCh7IHN0YXJ0LCBlbmQgfSk7XG4gICAgICAgIHRoaXMuX25vcm1hbGl6ZVJhbmdlcygpO1xuICAgIH1cblxuICAgIC8vIEhlbHBlciBtZXRob2QgdG8gcmVtb3ZlIHJhbmdlcyB0aGF0IG92ZXJsYXAgd2l0aCBhIGdpdmVuIHJhbmdlXG4gICAgX3JlbW92ZVJhbmdlKHN0YXJ0LCBlbmQpIHtcbiAgICAgICAgbGV0IHVwZGF0ZWRSYW5nZXMgPSBbXTtcbiAgICAgICAgZm9yIChsZXQgcmFuZ2Ugb2YgdGhpcy5fcmFuZ2VzKSB7XG4gICAgICAgICAgICBpZiAocmFuZ2UuZW5kIDw9IHN0YXJ0IHx8IHJhbmdlLnN0YXJ0ID49IGVuZCkge1xuICAgICAgICAgICAgICAgIC8vIE5vIG92ZXJsYXAsIGtlZXAgdGhlIHJhbmdlIGFzIGlzXG4gICAgICAgICAgICAgICAgdXBkYXRlZFJhbmdlcy5wdXNoKHJhbmdlKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAocmFuZ2Uuc3RhcnQgPCBzdGFydCAmJiByYW5nZS5lbmQgPiBlbmQpIHtcbiAgICAgICAgICAgICAgICAvLyBUaGUgcmFuZ2UgZnVsbHkgY292ZXJzIHRoZSByZW1vdmFsIHJhbmdlLCBzcGxpdCBpbnRvIHR3byByYW5nZXNcbiAgICAgICAgICAgICAgICB1cGRhdGVkUmFuZ2VzLnB1c2goeyBzdGFydDogcmFuZ2Uuc3RhcnQsIGVuZDogc3RhcnQgfSk7XG4gICAgICAgICAgICAgICAgdXBkYXRlZFJhbmdlcy5wdXNoKHsgc3RhcnQ6IGVuZCwgZW5kOiByYW5nZS5lbmQgfSk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHJhbmdlLnN0YXJ0ID49IHN0YXJ0ICYmIHJhbmdlLmVuZCA8PSBlbmQpIHtcbiAgICAgICAgICAgICAgICAvLyBUaGUgcmFuZ2UgaXMgZW50aXJlbHkgd2l0aGluIHRoZSByZW1vdmFsIHJhbmdlLCByZW1vdmUgaXRcbiAgICAgICAgICAgICAgICAvLyBEbyBub3QgYWRkIHRvIHVwZGF0ZWRSYW5nZXNcbiAgICAgICAgICAgIH0gZWxzZSBpZiAocmFuZ2Uuc3RhcnQgPCBzdGFydCAmJiByYW5nZS5lbmQgPiBzdGFydCAmJiByYW5nZS5lbmQgPD0gZW5kKSB7XG4gICAgICAgICAgICAgICAgLy8gVGhlIHJhbmdlIG92ZXJsYXBzIHdpdGggdGhlIHJlbW92YWwgcmFuZ2Ugb24gdGhlIGxlZnRcbiAgICAgICAgICAgICAgICB1cGRhdGVkUmFuZ2VzLnB1c2goeyBzdGFydDogcmFuZ2Uuc3RhcnQsIGVuZDogc3RhcnQgfSk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHJhbmdlLnN0YXJ0ID49IHN0YXJ0ICYmIHJhbmdlLnN0YXJ0IDwgZW5kICYmIHJhbmdlLmVuZCA+IGVuZCkge1xuICAgICAgICAgICAgICAgIC8vIFRoZSByYW5nZSBvdmVybGFwcyB3aXRoIHRoZSByZW1vdmFsIHJhbmdlIG9uIHRoZSByaWdodFxuICAgICAgICAgICAgICAgIHVwZGF0ZWRSYW5nZXMucHVzaCh7IHN0YXJ0OiBlbmQsIGVuZDogcmFuZ2UuZW5kIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHRoaXMuX3JhbmdlcyA9IHVwZGF0ZWRSYW5nZXM7XG4gICAgfVxuXG4gICAgLy8gTm9ybWFsaXplIGFuZCBtZXJnZSBvdmVybGFwcGluZyByYW5nZXNcbiAgICBfbm9ybWFsaXplUmFuZ2VzKCkge1xuICAgICAgICB0aGlzLl9yYW5nZXMuc29ydCgoYSwgYikgPT4gYS5zdGFydCAtIGIuc3RhcnQpO1xuICAgICAgICBsZXQgbm9ybWFsaXplZCA9IFtdO1xuICAgICAgICBmb3IgKGxldCByYW5nZSBvZiB0aGlzLl9yYW5nZXMpIHtcbiAgICAgICAgICAgIGlmIChub3JtYWxpemVkLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgICAgIG5vcm1hbGl6ZWQucHVzaChyYW5nZSk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGxldCBsYXN0ID0gbm9ybWFsaXplZFtub3JtYWxpemVkLmxlbmd0aCAtIDFdO1xuICAgICAgICAgICAgICAgIGlmIChyYW5nZS5zdGFydCA8PSBsYXN0LmVuZCkge1xuICAgICAgICAgICAgICAgICAgICBsYXN0LmVuZCA9IE1hdGgubWF4KGxhc3QuZW5kLCByYW5nZS5lbmQpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIG5vcm1hbGl6ZWQucHVzaChyYW5nZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHRoaXMuX3JhbmdlcyA9IG5vcm1hbGl6ZWQ7XG4gICAgfVxufSIsImltcG9ydCB7IFRpbWVSYW5nZXNTdHViIH0gZnJvbSBcIi4vVGltZVJhbmdlc1N0dWIuanNcIlxuaW1wb3J0IHsgVGV4dFRyYWNrU3R1YiwgVGV4dFRyYWNrTGlzdFN0dWIgfSBmcm9tIFwiLi9UZXh0VHJhY2tTdHViLmpzXCJcblxuZXhwb3J0IGNsYXNzIFZpZGVvRWxlbWVudFN0dWIgZXh0ZW5kcyBFdmVudFRhcmdldCB7XG4gICAgY29uc3RydWN0b3IoKSB7XG4gICAgICAgIHN1cGVyKCk7XG5cbiAgICAgICAgdGhpcy5icmlkZ2VJZCA9IHdpbmRvdy5uZXh0SW50ZXJuYWxJZDtcbiAgICAgICAgd2luZG93Lm5leHRJbnRlcm5hbElkICs9IDE7XG4gICAgICAgIHdpbmRvdy5icmlkZ2VPYmplY3RNYXBbdGhpcy5icmlkZ2VJZF0gPSB0aGlzO1xuXG4gICAgICAgIHRoaXMuX2N1cnJlbnRUaW1lID0gMC4wO1xuICAgICAgICB0aGlzLmR1cmF0aW9uID0gTmFOO1xuICAgICAgICB0aGlzLnBhdXNlZCA9IHRydWU7XG4gICAgICAgIHRoaXMucGxheWJhY2tSYXRlID0gMS4wO1xuICAgICAgICB0aGlzLnZvbHVtZSA9IDEuMDtcbiAgICAgICAgdGhpcy5tdXRlZCA9IGZhbHNlO1xuICAgICAgICB0aGlzLnJlYWR5U3RhdGUgPSAwO1xuICAgICAgICB0aGlzLm5ldHdvcmtTdGF0ZSA9IDA7XG4gICAgICAgIHRoaXMuYnVmZmVyZWQgPSBuZXcgVGltZVJhbmdlc1N0dWIoKTtcbiAgICAgICAgdGhpcy5zZWVraW5nID0gZmFsc2U7XG4gICAgICAgIHRoaXMubG9vcCA9IGZhbHNlO1xuICAgICAgICB0aGlzLmF1dG9wbGF5ID0gZmFsc2U7XG4gICAgICAgIHRoaXMuY29udHJvbHMgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5lcnJvciA9IG51bGw7XG4gICAgICAgIHRoaXMuc3JjID0gJyc7XG4gICAgICAgIHRoaXMudmlkZW9XaWR0aCA9IDA7XG4gICAgICAgIHRoaXMudmlkZW9IZWlnaHQgPSAwO1xuICAgICAgICB0aGlzLnRleHRUcmFja3MgPSBuZXcgVGV4dFRyYWNrTGlzdFN0dWIoKTtcbiAgICAgICAgdGhpcy5pc1dhaXRpbmcgPSBmYWxzZTtcblxuICAgICAgICB3aW5kb3cuYnJpZGdlSW52b2tlQXN5bmModGhpcy5icmlkZ2VJZCwgXCJWaWRlb0VsZW1lbnRcIiwgXCJjb25zdHJ1Y3RvclwiLCB7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5yZWFkeVN0YXRlID0gNDsgLy8gSEFWRV9FTk9VR0hfREFUQVxuICAgICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgnbG9hZGVkbWV0YWRhdGEnKSk7XG4gICAgICAgICAgICB0aGlzLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCdsb2FkZWRkYXRhJykpO1xuICAgICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgnY2FucGxheScpKTtcbiAgICAgICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ2NhbnBsYXl0aHJvdWdoJykpO1xuICAgICAgICB9LCAwKTtcbiAgICB9XG5cbiAgICBnZXQgY3VycmVudFRpbWUoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9jdXJyZW50VGltZTtcbiAgICB9XG5cbiAgICBzZXQgY3VycmVudFRpbWUodmFsdWUpIHtcbiAgICAgICAgaWYgKHRoaXMuX2N1cnJlbnRUaW1lICE9IHZhbHVlKSB7XG4gICAgICAgICAgICB0aGlzLl9jdXJyZW50VGltZSA9IHZhbHVlO1xuXG4gICAgICAgICAgICB0aGlzLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCdzZWVraW5nJykpO1xuXG4gICAgICAgICAgICB3aW5kb3cuYnJpZGdlSW52b2tlQXN5bmModGhpcy5icmlkZ2VJZCwgXCJWaWRlb0VsZW1lbnRcIiwgXCJzZXRDdXJyZW50VGltZVwiLCB7XG4gICAgICAgICAgICAgICAgXCJjdXJyZW50VGltZVwiOiB2YWx1ZVxuICAgICAgICAgICAgfSkudGhlbigocmVzdWx0KSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgnc2Vla2VkJykpO1xuICAgICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGJyaWRnZVVwZGF0ZUJ1ZmZlcmVkKHZhbHVlKSB7XG4gICAgICAgIGNvbnN0IHVwZGF0ZWRSYW5nZXMgPSB2YWx1ZTtcbiAgICAgICAgdmFyIHJhbmdlcyA9IFtdO1xuICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IHVwZGF0ZWRSYW5nZXMubGVuZ3RoOyBpICs9IDIpIHtcbiAgICAgICAgICAgIHJhbmdlcy5wdXNoKHtcbiAgICAgICAgICAgICAgICBzdGFydDogdXBkYXRlZFJhbmdlc1tpXSxcbiAgICAgICAgICAgICAgICBlbmQ6IHVwZGF0ZWRSYW5nZXNbaSArIDFdXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmJ1ZmZlcmVkLl9yYW5nZXMgPSByYW5nZXM7XG4gICAgfVxuICAgIFxuICAgIGJyaWRnZVVwZGF0ZVN0YXR1cyhkaWN0KSB7XG4gICAgICAgIHZhciBwYXVzZWQgPSAhZGljdFtcImlzUGxheWluZ1wiXTtcbiAgICAgICAgdmFyIGlzV2FpdGluZyA9IGRpY3RbXCJpc1dhaXRpbmdcIl07XG4gICAgICAgIHZhciBjdXJyZW50VGltZSA9IGRpY3RbXCJjdXJyZW50VGltZVwiXTtcblxuICAgICAgICBpZiAodGhpcy5wYXVzZWQgIT0gcGF1c2VkKSB7XG4gICAgICAgICAgICB0aGlzLnBhdXNlZCA9IHBhdXNlZDtcblxuICAgICAgICAgICAgaWYgKHBhdXNlZCkge1xuICAgICAgICAgICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ3BhdXNlJykpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCdwbGF5JykpO1xuICAgICAgICAgICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ3BsYXlpbmcnKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5pc1dhaXRpbmcgIT0gaXNXYWl0aW5nKSB7XG4gICAgICAgICAgICB0aGlzLmlzV2FpdGluZyA9IGlzV2FpdGluZztcbiAgICAgICAgICAgIGlmIChpc1dhaXRpbmcpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCd3YWl0aW5nJykpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMuX2N1cnJlbnRUaW1lICE9IGN1cnJlbnRUaW1lKSB7XG4gICAgICAgICAgICB0aGlzLl9jdXJyZW50VGltZSA9IGN1cnJlbnRUaW1lO1xuICAgICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgndGltZXVwZGF0ZScpKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHBsYXkoKSB7XG4gICAgICAgIGlmICh0aGlzLnBhdXNlZCkge1xuICAgICAgICAgICAgcmV0dXJuIHdpbmRvdy5icmlkZ2VJbnZva2VBc3luYyh0aGlzLmJyaWRnZUlkLCBcIlZpZGVvRWxlbWVudFwiLCBcInBsYXlcIiwge1xuICAgICAgICAgICAgfSkudGhlbigocmVzdWx0KSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgncGxheScpKTtcbiAgICAgICAgICAgICAgICB0aGlzLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCdwbGF5aW5nJykpO1xuICAgICAgICAgICAgfSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHBhdXNlKCkge1xuICAgICAgICBpZiAoIXRoaXMucGF1c2VkKSB7XG4gICAgICAgICAgICB0aGlzLnBhdXNlZCA9IHRydWU7XG4gICAgICAgICAgICB0aGlzLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCdwYXVzZScpKTtcblxuICAgICAgICAgICAgcmV0dXJuIHdpbmRvdy5icmlkZ2VJbnZva2VBc3luYyh0aGlzLmJyaWRnZUlkLCBcIlZpZGVvRWxlbWVudFwiLCBcInBhdXNlXCIsIHtcbiAgICAgICAgICAgIH0pLnRoZW4oKHJlc3VsdCkgPT4ge1xuICAgICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGNhblBsYXlUeXBlKHR5cGUpIHtcbiAgICAgICAgcmV0dXJuICdwcm9iYWJseSc7XG4gICAgfVxuXG4gICAgX2dldE1lZGlhKCkge1xuICAgICAgICByZXR1cm4gd2luZG93Lm1lZGlhU291cmNlTWFwW3RoaXMuc3JjXTtcbiAgICB9XG5cbiAgICAvKl9zaW11bGF0ZVRpbWVVcGRhdGUoKSB7XG4gICAgICAgIGlmICh0aGlzLl9pc1BsYXlpbmcpIHtcbiAgICAgICAgICAgIC8vIFNpbXVsYXRlIHRpbWUgcHJvZ3Jlc3Npb25cbiAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgICAgIHZhciBidWZmZXJlZEVuZCA9IDAuMDtcbiAgICAgICAgICAgICAgICBcbiAgICAgICAgICAgICAgICBjb25zdCBtZWRpYSA9IHRoaXMuX2dldE1lZGlhKCk7XG4gICAgICAgICAgICAgICAgaWYgKG1lZGlhKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChtZWRpYS5zb3VyY2VCdWZmZXJzLmxlbmd0aCAhPSAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmJ1ZmZlcmVkID0gbWVkaWEuc291cmNlQnVmZmVycy5fYnVmZmVyc1swXS5idWZmZXJlZDtcbiAgICAgICAgICAgICAgICAgICAgICAgIGJ1ZmZlcmVkRW5kID0gdGhpcy5idWZmZXJlZC5sZW5ndGggPT0gMCA/IDAgOiB0aGlzLmJ1ZmZlcmVkLmVuZCh0aGlzLmJ1ZmZlcmVkLmxlbmd0aCAtIDEpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gQ29uc3VtZSBidWZmZXJlZCBkYXRhXG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuY3VycmVudFRpbWUgPCBidWZmZXJlZEVuZCkge1xuICAgICAgICAgICAgICAgICAgICAvLyBBZHZhbmNlIGN1cnJlbnRUaW1lXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2N1cnJlbnRUaW1lICs9IDAuMSAqIHRoaXMucGxheWJhY2tSYXRlOyAvLyBJbmNyZW1lbnQgY3VycmVudFRpbWVcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgndGltZXVwZGF0ZScpKTtcblxuICAgICAgICAgICAgICAgICAgICAvLyBDb250aW51ZSBzaW11bGF0aW9uXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX3NpbXVsYXRlVGltZVVwZGF0ZSgpO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKFwiQnVmZmVyIHVuZGVycnVuXCIpO1xuICAgICAgICAgICAgICAgICAgICAvLyBCdWZmZXIgdW5kZXJydW5cbiAgICAgICAgICAgICAgICAgICAgdGhpcy5faXNQbGF5aW5nID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMucGF1c2VkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KG5ldyBFdmVudCgnd2FpdGluZycpKTtcbiAgICAgICAgICAgICAgICAgICAgLy8gVGhlIHBsYXllciBzaG91bGQgcmVhY3QgYnkgYnVmZmVyaW5nIG1vcmUgZGF0YVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sIDEwMCk7XG4gICAgICAgIH1cbiAgICB9Ki9cblxuICAgIGFkZFRleHRUcmFjayhraW5kLCBsYWJlbCwgbGFuZ3VhZ2UpIHtcbiAgICAgICAgY29uc3QgdGV4dFRyYWNrID0gbmV3IFRleHRUcmFja1N0dWIoa2luZCwgbGFiZWwsIGxhbmd1YWdlKTtcbiAgICAgICAgdGhpcy50ZXh0VHJhY2tzLl9hZGQodGV4dFRyYWNrKTtcbiAgICAgICAgcmV0dXJuIHRleHRUcmFjaztcbiAgICB9XG59XG4iLCJpbXBvcnQgSGxzIGZyb20gXCJobHMuanNcIjtcbmltcG9ydCB7IFZpZGVvRWxlbWVudFN0dWIgfSBmcm9tIFwiLi9WaWRlb0VsZW1lbnRTdHViLmpzXCJcbmltcG9ydCB7IE1lZGlhU291cmNlU3R1YiwgU291cmNlQnVmZmVyU3R1YiB9IGZyb20gXCIuL01lZGlhU291cmNlU3R1Yi5qc1wiXG5cbndpbmRvdy5icmlkZ2VPYmplY3RNYXAgPSB7fTtcbndpbmRvdy5icmlkZ2VDYWxsYmFja01hcCA9IHt9O1xuXG5mdW5jdGlvbiBicmlkZ2VJbnZva2VBc3luYyhicmlkZ2VJZCwgY2xhc3NOYW1lLCBtZXRob2ROYW1lLCBwYXJhbXMpIHtcbiAgICB2YXIgcHJvbWlzZVJlc29sdmU7XG4gICAgdmFyIHByb21pc2VSZWplY3Q7XG4gICAgdmFyIHJlc3VsdCA9IG5ldyBQcm9taXNlKGZ1bmN0aW9uKHJlc29sdmUsIHJlamVjdCkge1xuICAgICAgICBwcm9taXNlUmVzb2x2ZSA9IHJlc29sdmU7XG4gICAgICAgIHByb21pc2VSZWplY3QgPSByZWplY3Q7XG4gICAgfSk7XG4gICAgY29uc3QgY2FsbGJhY2tJZCA9IHdpbmRvdy5uZXh0SW50ZXJuYWxJZDtcbiAgICB3aW5kb3cubmV4dEludGVybmFsSWQgKz0gMTtcbiAgICB3aW5kb3cuYnJpZGdlQ2FsbGJhY2tNYXBbY2FsbGJhY2tJZF0gPSBwcm9taXNlUmVzb2x2ZTtcblxuICAgIGlmICh3aW5kb3cud2Via2l0Lm1lc3NhZ2VIYW5kbGVycykge1xuICAgICAgICB3aW5kb3cud2Via2l0Lm1lc3NhZ2VIYW5kbGVycy5wZXJmb3JtQWN0aW9uLnBvc3RNZXNzYWdlKHtcbiAgICAgICAgICAgICdldmVudCc6ICdicmlkZ2VJbnZva2UnLFxuICAgICAgICAgICAgJ2RhdGEnOiB7XG4gICAgICAgICAgICAgICAgJ2JyaWRnZUlkJzogYnJpZGdlSWQsXG4gICAgICAgICAgICAgICAgJ2NsYXNzTmFtZSc6IGNsYXNzTmFtZSxcbiAgICAgICAgICAgICAgICAnbWV0aG9kTmFtZSc6IG1ldGhvZE5hbWUsXG4gICAgICAgICAgICAgICAgJ3BhcmFtcyc6IHBhcmFtcyxcbiAgICAgICAgICAgICAgICAnY2FsbGJhY2tJZCc6IGNhbGxiYWNrSWRcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3VsdDtcbn1cbndpbmRvdy5icmlkZ2VJbnZva2VBc3luYyA9IGJyaWRnZUludm9rZUFzeW5jXG5cbmV4cG9ydCBmdW5jdGlvbiBicmlkZ2VJbnZva2VDYWxsYmFjayhjYWxsYmFja0lkLCByZXN1bHQpIHtcbiAgICBjb25zdCBjYWxsYmFjayA9IHdpbmRvdy5icmlkZ2VDYWxsYmFja01hcFtjYWxsYmFja0lkXTtcbiAgICBpZiAoY2FsbGJhY2spIHtcbiAgICAgICAgY2FsbGJhY2socmVzdWx0KTtcbiAgICB9XG59XG5cbnZhciB1c2VTdHVicyA9IHRydWU7XG5cbndpbmRvdy5uZXh0SW50ZXJuYWxJZCA9IDA7XG53aW5kb3cubWVkaWFTb3VyY2VNYXAgPSB7fTtcblxuLy8gUmVwbGFjZSB0aGUgZ2xvYmFsIE1lZGlhU291cmNlIHdpdGggb3VyIHN0dWJcbmlmICh1c2VTdHVicyAmJiB0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJykge1xuICAgIHdpbmRvdy5NZWRpYVNvdXJjZSA9IE1lZGlhU291cmNlU3R1YjtcbiAgICB3aW5kb3cuTWFuYWdlZE1lZGlhU291cmNlID0gTWVkaWFTb3VyY2VTdHViO1xuICAgIHdpbmRvdy5Tb3VyY2VCdWZmZXIgPSBTb3VyY2VCdWZmZXJTdHViO1xuICAgIFVSTC5jcmVhdGVPYmplY3RVUkwgPSBmdW5jdGlvbihtcykge1xuICAgICAgICBjb25zdCB1cmwgPSBcImJsb2I6bW9jay1tZWRpYS1zb3VyY2U6XCIgKyBtcy5pbnRlcm5hbElkO1xuICAgICAgICB3aW5kb3cubWVkaWFTb3VyY2VNYXBbdXJsXSA9IG1zO1xuICAgICAgICByZXR1cm4gdXJsO1xuICAgIH07XG59XG5cblxuZnVuY3Rpb24gcG9zdFBsYXllckV2ZW50KGV2ZW50TmFtZSwgZXZlbnREYXRhKSB7XG4gICAgaWYgKHdpbmRvdy53ZWJraXQgJiYgd2luZG93LndlYmtpdC5tZXNzYWdlSGFuZGxlcnMpIHtcbiAgICAgICAgd2luZG93LndlYmtpdC5tZXNzYWdlSGFuZGxlcnMucGVyZm9ybUFjdGlvbi5wb3N0TWVzc2FnZSh7J2V2ZW50JzogZXZlbnROYW1lLCAnZGF0YSc6IGV2ZW50RGF0YX0pO1xuICAgIH1cbn07XG5cbnZhciB2aWRlbztcbnZhciBobHM7XG5cbnZhciBpc01hbmlmZXN0UGFyc2VkID0gZmFsc2U7XG52YXIgaXNGaXJzdEZyYW1lUmVhZHkgPSBmYWxzZTtcblxudmFyIGN1cnJlbnRUaW1lVXBkYXRlVGltZW91dCA9IG51bGw7XG5cbmV4cG9ydCBmdW5jdGlvbiBwbGF5ZXJJbml0aWFsaXplKHBhcmFtcykge1xuICAgIHZpZGVvLm11dGVkID0gZmFsc2U7XG5cbiAgICB2aWRlby5hZGRFdmVudExpc3RlbmVyKCdsb2FkZWRkYXRhJywgKGV2ZW50KSA9PiB7XG4gICAgICAgIGlmICghaXNGaXJzdEZyYW1lUmVhZHkpIHtcbiAgICAgICAgICAgIGlzRmlyc3RGcmFtZVJlYWR5ID0gdHJ1ZTtcbiAgICAgICAgICAgIHJlZnJlc2hQbGF5ZXJTdGF0dXMoKTtcbiAgICAgICAgfVxuICAgIH0pO1xuICAgIHZpZGVvLmFkZEV2ZW50TGlzdGVuZXIoXCJwbGF5aW5nXCIsIGZ1bmN0aW9uKCkge1xuICAgICAgICByZWZyZXNoUGxheWVyU3RhdHVzKCk7XG4gICAgfSk7XG4gICAgdmlkZW8uYWRkRXZlbnRMaXN0ZW5lcihcInBhdXNlXCIsIGZ1bmN0aW9uKCkgeyBcbiAgICAgICAgcmVmcmVzaFBsYXllclN0YXR1cygpO1xuICAgIH0pO1xuICAgIHZpZGVvLmFkZEV2ZW50TGlzdGVuZXIoXCJzZWVraW5nXCIsIGZ1bmN0aW9uKCkgeyBcbiAgICAgICAgcmVmcmVzaFBsYXllclN0YXR1cygpO1xuICAgIH0pO1xuICAgIHZpZGVvLmFkZEV2ZW50TGlzdGVuZXIoXCJ3YWl0aW5nXCIsIGZ1bmN0aW9uKCkgeyBcbiAgICAgICAgcmVmcmVzaFBsYXllclN0YXR1cygpO1xuICAgIH0pO1xuXG4gICAgaGxzID0gbmV3IEhscyh7XG4gICAgICAgIHN0YXJ0TGV2ZWw6IDAsXG4gICAgICAgIHRlc3RCYW5kd2lkdGg6IGZhbHNlLFxuICAgICAgICBkZWJ1ZzogcGFyYW1zWydkZWJ1ZyddIHx8IHRydWUsXG4gICAgICAgIGF1dG9TdGFydExvYWQ6IGZhbHNlLFxuICAgICAgICBiYWNrQnVmZmVyTGVuZ3RoOiAzMCxcbiAgICAgICAgbWF4QnVmZmVyTGVuZ3RoOiA2MCxcbiAgICAgICAgbWF4TWF4QnVmZmVyTGVuZ3RoOiA2MCxcbiAgICAgICAgbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZTogMC4wMDEsXG4gICAgICAgIG51ZGdlTWF4UmV0cnk6IDEwMDAwXG4gICAgfSk7XG4gICAgaGxzLm9uKEhscy5FdmVudHMuTUFOSUZFU1RfUEFSU0VELCBmdW5jdGlvbigpIHtcbiAgICAgICAgaXNNYW5pZmVzdFBhcnNlZCA9IHRydWU7XG4gICAgICAgIHJlZnJlc2hQbGF5ZXJTdGF0dXMoKTtcbiAgICB9KTtcblxuICAgIGhscy5vbihIbHMuRXZlbnRzLkxFVkVMX1NXSVRDSEVELCBmdW5jdGlvbigpIHtcbiAgICAgICAgcmVmcmVzaFBsYXllclN0YXR1cygpO1xuICAgIH0pO1xuICAgIGhscy5vbihIbHMuRXZlbnRzLkxFVkVMU19VUERBVEVELCBmdW5jdGlvbigpIHtcbiAgICAgICAgcmVmcmVzaFBsYXllclN0YXR1cygpO1xuICAgIH0pO1xuXG4gICAgaGxzLmxvYWRTb3VyY2UoJ21hc3Rlci5tM3U4Jyk7XG4gICAgaGxzLmF0dGFjaE1lZGlhKHZpZGVvKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBsYXllckxvYWQoaW5pdGlhbExldmVsSW5kZXgpIHtcbiAgICBobHMuc3RhcnRMZXZlbCA9IGluaXRpYWxMZXZlbEluZGV4O1xuICAgIGhscy5zdGFydExvYWQoLTEsIGZhbHNlKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBsYXllclBsYXkoKSB7XG4gICAgdmlkZW8ucGxheSgpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcGxheWVyUGF1c2UoKSB7XG4gICAgdmlkZW8ucGF1c2UoKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHBsYXllclNldEJhc2VSYXRlKHZhbHVlKSB7XG4gICAgdmlkZW8ucGxheWJhY2tSYXRlID0gdmFsdWU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBwbGF5ZXJTZXRMZXZlbChsZXZlbCkge1xuICAgIGlmIChsZXZlbCA+PSAwKSB7XG4gICAgICAgIGhscy5jdXJyZW50TGV2ZWwgPSBsZXZlbDtcbiAgICB9IGVsc2Uge1xuICAgICAgICBobHMuY3VycmVudExldmVsID0gLTE7XG4gICAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gcGxheWVyU2Vlayh2YWx1ZSkge1xuICAgIHZpZGVvLmN1cnJlbnRUaW1lID0gdmFsdWU7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBwbGF5ZXJTZXRJc011dGVkKHZhbHVlKSB7XG4gICAgdmlkZW8ubXV0ZWQgPSB2YWx1ZTtcbn1cblxuZnVuY3Rpb24gZ2V0TGV2ZWxzKCkge1xuICAgIHZhciBsZXZlbHMgPSBbXTtcbiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGhscy5sZXZlbHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgdmFyIGxldmVsID0gaGxzLmxldmVsc1tpXTtcbiAgICAgICAgbGV2ZWxzLnB1c2goe1xuICAgICAgICAgICAgJ2luZGV4JzogaSxcbiAgICAgICAgICAgICdiaXRyYXRlJzogbGV2ZWwuYml0cmF0ZSB8fCAwLFxuICAgICAgICAgICAgJ3dpZHRoJzogbGV2ZWwud2lkdGggfHwgMCxcbiAgICAgICAgICAgICdoZWlnaHQnOiBsZXZlbC5oZWlnaHQgfHwgMFxuICAgICAgICB9KTtcbiAgICB9XG4gICAgcmV0dXJuIGxldmVscztcbn1cblxuZnVuY3Rpb24gcmVmcmVzaFBsYXllclN0YXR1cygpIHtcbiAgICB2YXIgaXNQbGF5aW5nID0gZmFsc2U7XG4gICAgaWYgKCF2aWRlby5wYXVzZWQgJiYgIXZpZGVvLmVuZGVkICYmIHZpZGVvLnJlYWR5U3RhdGUgPiAyKSB7XG4gICAgICAgIGlzUGxheWluZyA9IHRydWU7XG4gICAgfVxuXG4gICAgcG9zdFBsYXllckV2ZW50KCdwbGF5ZXJTdGF0dXMnLCB7XG4gICAgICAgICdpc1JlYWR5JzogaXNNYW5pZmVzdFBhcnNlZCxcbiAgICAgICAgJ2lzRmlyc3RGcmFtZVJlYWR5JzogaXNGaXJzdEZyYW1lUmVhZHksXG4gICAgICAgICdpc1BsYXlpbmcnOiAhdmlkZW8ucGF1c2VkLFxuICAgICAgICAncmF0ZSc6IGlzUGxheWluZyA/IHZpZGVvLnBsYXliYWNrUmF0ZSA6IDAuMCxcbiAgICAgICAgJ2RlZmF1bHRSYXRlJzogdmlkZW8ucGxheWJhY2tSYXRlLFxuICAgICAgICAnbGV2ZWxzJzogZ2V0TGV2ZWxzKCksXG4gICAgICAgICdjdXJyZW50TGV2ZWwnOiBobHMuY3VycmVudExldmVsXG4gICAgfSk7XG5cbiAgICByZWZyZXNoUGxheWVyQ3VycmVudFRpbWUoKTtcblxuICAgIGlmIChpc1BsYXlpbmcpIHtcbiAgICAgICAgaWYgKGN1cnJlbnRUaW1lVXBkYXRlVGltZW91dCA9PSBudWxsKSB7XG4gICAgICAgICAgICBjdXJyZW50VGltZVVwZGF0ZVRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgICAgICByZWZyZXNoUGxheWVyQ3VycmVudFRpbWUoKTtcbiAgICAgICAgICAgIH0sIDIwMCk7XG4gICAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgICBpZihjdXJyZW50VGltZVVwZGF0ZVRpbWVvdXQgIT0gbnVsbCl7XG4gICAgICAgICAgICBjbGVhclRpbWVvdXQoY3VycmVudFRpbWVVcGRhdGVUaW1lb3V0KTtcbiAgICAgICAgICAgIGN1cnJlbnRUaW1lVXBkYXRlVGltZW91dCA9IG51bGw7XG4gICAgICAgIH1cbiAgICB9XG59XG5cbmZ1bmN0aW9uIHJlZnJlc2hQbGF5ZXJDdXJyZW50VGltZSgpIHtcbiAgICBwb3N0UGxheWVyRXZlbnQoJ3BsYXllckN1cnJlbnRUaW1lJywge1xuICAgICAgICAndmFsdWUnOiB2aWRlby5jdXJyZW50VGltZVxuICAgIH0pO1xuICAgIGN1cnJlbnRUaW1lVXBkYXRlVGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICByZWZyZXNoUGxheWVyQ3VycmVudFRpbWUoKVxuICAgIH0sIDIwMCk7XG59XG5cbndpbmRvdy5vbmxvYWQgPSAoKSA9PiB7XG4gICAgaWYgKHVzZVN0dWJzKSB7XG4gICAgICAgIHZpZGVvID0gbmV3IFZpZGVvRWxlbWVudFN0dWIoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICB2aWRlbyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3ZpZGVvJyk7XG4gICAgICAgIHZpZGVvLnBsYXlzSW5saW5lID0gdHJ1ZTtcbiAgICAgICAgdmlkZW8uY29udHJvbHMgPSB0cnVlO1xuICAgICAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHZpZGVvKTtcbiAgICB9XG5cbiAgICBwb3N0UGxheWVyRXZlbnQoJ3dpbmRvd09uTG9hZCcsIHtcbiAgICB9KTtcbn07XG5cbndpbmRvdy5wbGF5ZXJJbml0aWFsaXplID0gcGxheWVySW5pdGlhbGl6ZTtcbndpbmRvdy5wbGF5ZXJMb2FkID0gcGxheWVyTG9hZDtcbndpbmRvdy5wbGF5ZXJQbGF5ID0gcGxheWVyUGxheTtcbndpbmRvdy5wbGF5ZXJQYXVzZSA9IHBsYXllclBhdXNlO1xud2luZG93LnBsYXllclNldEJhc2VSYXRlID0gcGxheWVyU2V0QmFzZVJhdGU7XG53aW5kb3cucGxheWVyU2V0TGV2ZWwgPSBwbGF5ZXJTZXRMZXZlbDtcbndpbmRvdy5wbGF5ZXJTZWVrID0gcGxheWVyU2VlaztcbndpbmRvdy5wbGF5ZXJTZXRJc011dGVkID0gcGxheWVyU2V0SXNNdXRlZDtcbndpbmRvdy5icmlkZ2VJbnZva2VDYWxsYmFjayA9IGJyaWRnZUludm9rZUNhbGxiYWNrOyIsImZ1bmN0aW9uIGdldERlZmF1bHRFeHBvcnRGcm9tQ2pzICh4KSB7XG5cdHJldHVybiB4ICYmIHguX19lc01vZHVsZSAmJiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoeCwgJ2RlZmF1bHQnKSA/IHhbJ2RlZmF1bHQnXSA6IHg7XG59XG5cbnZhciB1cmxUb29sa2l0ID0ge2V4cG9ydHM6IHt9fTtcblxuKGZ1bmN0aW9uIChtb2R1bGUsIGV4cG9ydHMpIHtcblx0Ly8gc2VlIGh0dHBzOi8vdG9vbHMuaWV0Zi5vcmcvaHRtbC9yZmMxODA4XG5cblx0KGZ1bmN0aW9uIChyb290KSB7XG5cdCAgdmFyIFVSTF9SRUdFWCA9XG5cdCAgICAvXig/PSgoPzpbYS16QS1aMC05K1xcLS5dKzopPykpXFwxKD89KCg/OlxcL1xcL1teXFwvPyNdKik/KSlcXDIoPz0oKD86KD86W14/I1xcL10qXFwvKSpbXjs/I1xcL10qKT8pKVxcMygoPzo7W14/I10qKT8pKFxcP1teI10qKT8oI1teXSopPyQvO1xuXHQgIHZhciBGSVJTVF9TRUdNRU5UX1JFR0VYID0gL14oPz0oW15cXC8/I10qKSlcXDEoW15dKikkLztcblx0ICB2YXIgU0xBU0hfRE9UX1JFR0VYID0gLyg/OlxcL3xeKVxcLig/PVxcLykvZztcblx0ICB2YXIgU0xBU0hfRE9UX0RPVF9SRUdFWCA9IC8oPzpcXC98XilcXC5cXC5cXC8oPyFcXC5cXC5cXC8pW15cXC9dKig/PVxcLykvZztcblxuXHQgIHZhciBVUkxUb29sa2l0ID0ge1xuXHQgICAgLy8gSWYgb3B0cy5hbHdheXNOb3JtYWxpemUgaXMgdHJ1ZSB0aGVuIHRoZSBwYXRoIHdpbGwgYWx3YXlzIGJlIG5vcm1hbGl6ZWQgZXZlbiB3aGVuIGl0IHN0YXJ0cyB3aXRoIC8gb3IgLy9cblx0ICAgIC8vIEUuZ1xuXHQgICAgLy8gV2l0aCBvcHRzLmFsd2F5c05vcm1hbGl6ZSA9IGZhbHNlIChkZWZhdWx0LCBzcGVjIGNvbXBsaWFudClcblx0ICAgIC8vIGh0dHA6Ly9hLmNvbS9iL2NkICsgL2UvZi8uLi9nID0+IGh0dHA6Ly9hLmNvbS9lL2YvLi4vZ1xuXHQgICAgLy8gV2l0aCBvcHRzLmFsd2F5c05vcm1hbGl6ZSA9IHRydWUgKG5vdCBzcGVjIGNvbXBsaWFudClcblx0ICAgIC8vIGh0dHA6Ly9hLmNvbS9iL2NkICsgL2UvZi8uLi9nID0+IGh0dHA6Ly9hLmNvbS9lL2dcblx0ICAgIGJ1aWxkQWJzb2x1dGVVUkw6IGZ1bmN0aW9uIChiYXNlVVJMLCByZWxhdGl2ZVVSTCwgb3B0cykge1xuXHQgICAgICBvcHRzID0gb3B0cyB8fCB7fTtcblx0ICAgICAgLy8gcmVtb3ZlIGFueSByZW1haW5pbmcgc3BhY2UgYW5kIENSTEZcblx0ICAgICAgYmFzZVVSTCA9IGJhc2VVUkwudHJpbSgpO1xuXHQgICAgICByZWxhdGl2ZVVSTCA9IHJlbGF0aXZlVVJMLnRyaW0oKTtcblx0ICAgICAgaWYgKCFyZWxhdGl2ZVVSTCkge1xuXHQgICAgICAgIC8vIDJhKSBJZiB0aGUgZW1iZWRkZWQgVVJMIGlzIGVudGlyZWx5IGVtcHR5LCBpdCBpbmhlcml0cyB0aGVcblx0ICAgICAgICAvLyBlbnRpcmUgYmFzZSBVUkwgKGkuZS4sIGlzIHNldCBlcXVhbCB0byB0aGUgYmFzZSBVUkwpXG5cdCAgICAgICAgLy8gYW5kIHdlIGFyZSBkb25lLlxuXHQgICAgICAgIGlmICghb3B0cy5hbHdheXNOb3JtYWxpemUpIHtcblx0ICAgICAgICAgIHJldHVybiBiYXNlVVJMO1xuXHQgICAgICAgIH1cblx0ICAgICAgICB2YXIgYmFzZVBhcnRzRm9yTm9ybWFsaXNlID0gVVJMVG9vbGtpdC5wYXJzZVVSTChiYXNlVVJMKTtcblx0ICAgICAgICBpZiAoIWJhc2VQYXJ0c0Zvck5vcm1hbGlzZSkge1xuXHQgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdFcnJvciB0cnlpbmcgdG8gcGFyc2UgYmFzZSBVUkwuJyk7XG5cdCAgICAgICAgfVxuXHQgICAgICAgIGJhc2VQYXJ0c0Zvck5vcm1hbGlzZS5wYXRoID0gVVJMVG9vbGtpdC5ub3JtYWxpemVQYXRoKFxuXHQgICAgICAgICAgYmFzZVBhcnRzRm9yTm9ybWFsaXNlLnBhdGhcblx0ICAgICAgICApO1xuXHQgICAgICAgIHJldHVybiBVUkxUb29sa2l0LmJ1aWxkVVJMRnJvbVBhcnRzKGJhc2VQYXJ0c0Zvck5vcm1hbGlzZSk7XG5cdCAgICAgIH1cblx0ICAgICAgdmFyIHJlbGF0aXZlUGFydHMgPSBVUkxUb29sa2l0LnBhcnNlVVJMKHJlbGF0aXZlVVJMKTtcblx0ICAgICAgaWYgKCFyZWxhdGl2ZVBhcnRzKSB7XG5cdCAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdFcnJvciB0cnlpbmcgdG8gcGFyc2UgcmVsYXRpdmUgVVJMLicpO1xuXHQgICAgICB9XG5cdCAgICAgIGlmIChyZWxhdGl2ZVBhcnRzLnNjaGVtZSkge1xuXHQgICAgICAgIC8vIDJiKSBJZiB0aGUgZW1iZWRkZWQgVVJMIHN0YXJ0cyB3aXRoIGEgc2NoZW1lIG5hbWUsIGl0IGlzXG5cdCAgICAgICAgLy8gaW50ZXJwcmV0ZWQgYXMgYW4gYWJzb2x1dGUgVVJMIGFuZCB3ZSBhcmUgZG9uZS5cblx0ICAgICAgICBpZiAoIW9wdHMuYWx3YXlzTm9ybWFsaXplKSB7XG5cdCAgICAgICAgICByZXR1cm4gcmVsYXRpdmVVUkw7XG5cdCAgICAgICAgfVxuXHQgICAgICAgIHJlbGF0aXZlUGFydHMucGF0aCA9IFVSTFRvb2xraXQubm9ybWFsaXplUGF0aChyZWxhdGl2ZVBhcnRzLnBhdGgpO1xuXHQgICAgICAgIHJldHVybiBVUkxUb29sa2l0LmJ1aWxkVVJMRnJvbVBhcnRzKHJlbGF0aXZlUGFydHMpO1xuXHQgICAgICB9XG5cdCAgICAgIHZhciBiYXNlUGFydHMgPSBVUkxUb29sa2l0LnBhcnNlVVJMKGJhc2VVUkwpO1xuXHQgICAgICBpZiAoIWJhc2VQYXJ0cykge1xuXHQgICAgICAgIHRocm93IG5ldyBFcnJvcignRXJyb3IgdHJ5aW5nIHRvIHBhcnNlIGJhc2UgVVJMLicpO1xuXHQgICAgICB9XG5cdCAgICAgIGlmICghYmFzZVBhcnRzLm5ldExvYyAmJiBiYXNlUGFydHMucGF0aCAmJiBiYXNlUGFydHMucGF0aFswXSAhPT0gJy8nKSB7XG5cdCAgICAgICAgLy8gSWYgbmV0TG9jIG1pc3NpbmcgYW5kIHBhdGggZG9lc24ndCBzdGFydCB3aXRoICcvJywgYXNzdW1lIGV2ZXJ0aGluZyBiZWZvcmUgdGhlIGZpcnN0ICcvJyBpcyB0aGUgbmV0TG9jXG5cdCAgICAgICAgLy8gVGhpcyBjYXVzZXMgJ2V4YW1wbGUuY29tL2EnIHRvIGJlIGhhbmRsZWQgYXMgJy8vZXhhbXBsZS5jb20vYScgaW5zdGVhZCBvZiAnL2V4YW1wbGUuY29tL2EnXG5cdCAgICAgICAgdmFyIHBhdGhQYXJ0cyA9IEZJUlNUX1NFR01FTlRfUkVHRVguZXhlYyhiYXNlUGFydHMucGF0aCk7XG5cdCAgICAgICAgYmFzZVBhcnRzLm5ldExvYyA9IHBhdGhQYXJ0c1sxXTtcblx0ICAgICAgICBiYXNlUGFydHMucGF0aCA9IHBhdGhQYXJ0c1syXTtcblx0ICAgICAgfVxuXHQgICAgICBpZiAoYmFzZVBhcnRzLm5ldExvYyAmJiAhYmFzZVBhcnRzLnBhdGgpIHtcblx0ICAgICAgICBiYXNlUGFydHMucGF0aCA9ICcvJztcblx0ICAgICAgfVxuXHQgICAgICB2YXIgYnVpbHRQYXJ0cyA9IHtcblx0ICAgICAgICAvLyAyYykgT3RoZXJ3aXNlLCB0aGUgZW1iZWRkZWQgVVJMIGluaGVyaXRzIHRoZSBzY2hlbWUgb2Zcblx0ICAgICAgICAvLyB0aGUgYmFzZSBVUkwuXG5cdCAgICAgICAgc2NoZW1lOiBiYXNlUGFydHMuc2NoZW1lLFxuXHQgICAgICAgIG5ldExvYzogcmVsYXRpdmVQYXJ0cy5uZXRMb2MsXG5cdCAgICAgICAgcGF0aDogbnVsbCxcblx0ICAgICAgICBwYXJhbXM6IHJlbGF0aXZlUGFydHMucGFyYW1zLFxuXHQgICAgICAgIHF1ZXJ5OiByZWxhdGl2ZVBhcnRzLnF1ZXJ5LFxuXHQgICAgICAgIGZyYWdtZW50OiByZWxhdGl2ZVBhcnRzLmZyYWdtZW50LFxuXHQgICAgICB9O1xuXHQgICAgICBpZiAoIXJlbGF0aXZlUGFydHMubmV0TG9jKSB7XG5cdCAgICAgICAgLy8gMykgSWYgdGhlIGVtYmVkZGVkIFVSTCdzIDxuZXRfbG9jPiBpcyBub24tZW1wdHksIHdlIHNraXAgdG9cblx0ICAgICAgICAvLyBTdGVwIDcuICBPdGhlcndpc2UsIHRoZSBlbWJlZGRlZCBVUkwgaW5oZXJpdHMgdGhlIDxuZXRfbG9jPlxuXHQgICAgICAgIC8vIChpZiBhbnkpIG9mIHRoZSBiYXNlIFVSTC5cblx0ICAgICAgICBidWlsdFBhcnRzLm5ldExvYyA9IGJhc2VQYXJ0cy5uZXRMb2M7XG5cdCAgICAgICAgLy8gNCkgSWYgdGhlIGVtYmVkZGVkIFVSTCBwYXRoIGlzIHByZWNlZGVkIGJ5IGEgc2xhc2ggXCIvXCIsIHRoZVxuXHQgICAgICAgIC8vIHBhdGggaXMgbm90IHJlbGF0aXZlIGFuZCB3ZSBza2lwIHRvIFN0ZXAgNy5cblx0ICAgICAgICBpZiAocmVsYXRpdmVQYXJ0cy5wYXRoWzBdICE9PSAnLycpIHtcblx0ICAgICAgICAgIGlmICghcmVsYXRpdmVQYXJ0cy5wYXRoKSB7XG5cdCAgICAgICAgICAgIC8vIDUpIElmIHRoZSBlbWJlZGRlZCBVUkwgcGF0aCBpcyBlbXB0eSAoYW5kIG5vdCBwcmVjZWRlZCBieSBhXG5cdCAgICAgICAgICAgIC8vIHNsYXNoKSwgdGhlbiB0aGUgZW1iZWRkZWQgVVJMIGluaGVyaXRzIHRoZSBiYXNlIFVSTCBwYXRoXG5cdCAgICAgICAgICAgIGJ1aWx0UGFydHMucGF0aCA9IGJhc2VQYXJ0cy5wYXRoO1xuXHQgICAgICAgICAgICAvLyA1YSkgaWYgdGhlIGVtYmVkZGVkIFVSTCdzIDxwYXJhbXM+IGlzIG5vbi1lbXB0eSwgd2Ugc2tpcCB0b1xuXHQgICAgICAgICAgICAvLyBzdGVwIDc7IG90aGVyd2lzZSwgaXQgaW5oZXJpdHMgdGhlIDxwYXJhbXM+IG9mIHRoZSBiYXNlXG5cdCAgICAgICAgICAgIC8vIFVSTCAoaWYgYW55KSBhbmRcblx0ICAgICAgICAgICAgaWYgKCFyZWxhdGl2ZVBhcnRzLnBhcmFtcykge1xuXHQgICAgICAgICAgICAgIGJ1aWx0UGFydHMucGFyYW1zID0gYmFzZVBhcnRzLnBhcmFtcztcblx0ICAgICAgICAgICAgICAvLyA1YikgaWYgdGhlIGVtYmVkZGVkIFVSTCdzIDxxdWVyeT4gaXMgbm9uLWVtcHR5LCB3ZSBza2lwIHRvXG5cdCAgICAgICAgICAgICAgLy8gc3RlcCA3OyBvdGhlcndpc2UsIGl0IGluaGVyaXRzIHRoZSA8cXVlcnk+IG9mIHRoZSBiYXNlXG5cdCAgICAgICAgICAgICAgLy8gVVJMIChpZiBhbnkpIGFuZCB3ZSBza2lwIHRvIHN0ZXAgNy5cblx0ICAgICAgICAgICAgICBpZiAoIXJlbGF0aXZlUGFydHMucXVlcnkpIHtcblx0ICAgICAgICAgICAgICAgIGJ1aWx0UGFydHMucXVlcnkgPSBiYXNlUGFydHMucXVlcnk7XG5cdCAgICAgICAgICAgICAgfVxuXHQgICAgICAgICAgICB9XG5cdCAgICAgICAgICB9IGVsc2Uge1xuXHQgICAgICAgICAgICAvLyA2KSBUaGUgbGFzdCBzZWdtZW50IG9mIHRoZSBiYXNlIFVSTCdzIHBhdGggKGFueXRoaW5nXG5cdCAgICAgICAgICAgIC8vIGZvbGxvd2luZyB0aGUgcmlnaHRtb3N0IHNsYXNoIFwiL1wiLCBvciB0aGUgZW50aXJlIHBhdGggaWYgbm9cblx0ICAgICAgICAgICAgLy8gc2xhc2ggaXMgcHJlc2VudCkgaXMgcmVtb3ZlZCBhbmQgdGhlIGVtYmVkZGVkIFVSTCdzIHBhdGggaXNcblx0ICAgICAgICAgICAgLy8gYXBwZW5kZWQgaW4gaXRzIHBsYWNlLlxuXHQgICAgICAgICAgICB2YXIgYmFzZVVSTFBhdGggPSBiYXNlUGFydHMucGF0aDtcblx0ICAgICAgICAgICAgdmFyIG5ld1BhdGggPVxuXHQgICAgICAgICAgICAgIGJhc2VVUkxQYXRoLnN1YnN0cmluZygwLCBiYXNlVVJMUGF0aC5sYXN0SW5kZXhPZignLycpICsgMSkgK1xuXHQgICAgICAgICAgICAgIHJlbGF0aXZlUGFydHMucGF0aDtcblx0ICAgICAgICAgICAgYnVpbHRQYXJ0cy5wYXRoID0gVVJMVG9vbGtpdC5ub3JtYWxpemVQYXRoKG5ld1BhdGgpO1xuXHQgICAgICAgICAgfVxuXHQgICAgICAgIH1cblx0ICAgICAgfVxuXHQgICAgICBpZiAoYnVpbHRQYXJ0cy5wYXRoID09PSBudWxsKSB7XG5cdCAgICAgICAgYnVpbHRQYXJ0cy5wYXRoID0gb3B0cy5hbHdheXNOb3JtYWxpemVcblx0ICAgICAgICAgID8gVVJMVG9vbGtpdC5ub3JtYWxpemVQYXRoKHJlbGF0aXZlUGFydHMucGF0aClcblx0ICAgICAgICAgIDogcmVsYXRpdmVQYXJ0cy5wYXRoO1xuXHQgICAgICB9XG5cdCAgICAgIHJldHVybiBVUkxUb29sa2l0LmJ1aWxkVVJMRnJvbVBhcnRzKGJ1aWx0UGFydHMpO1xuXHQgICAgfSxcblx0ICAgIHBhcnNlVVJMOiBmdW5jdGlvbiAodXJsKSB7XG5cdCAgICAgIHZhciBwYXJ0cyA9IFVSTF9SRUdFWC5leGVjKHVybCk7XG5cdCAgICAgIGlmICghcGFydHMpIHtcblx0ICAgICAgICByZXR1cm4gbnVsbDtcblx0ICAgICAgfVxuXHQgICAgICByZXR1cm4ge1xuXHQgICAgICAgIHNjaGVtZTogcGFydHNbMV0gfHwgJycsXG5cdCAgICAgICAgbmV0TG9jOiBwYXJ0c1syXSB8fCAnJyxcblx0ICAgICAgICBwYXRoOiBwYXJ0c1szXSB8fCAnJyxcblx0ICAgICAgICBwYXJhbXM6IHBhcnRzWzRdIHx8ICcnLFxuXHQgICAgICAgIHF1ZXJ5OiBwYXJ0c1s1XSB8fCAnJyxcblx0ICAgICAgICBmcmFnbWVudDogcGFydHNbNl0gfHwgJycsXG5cdCAgICAgIH07XG5cdCAgICB9LFxuXHQgICAgbm9ybWFsaXplUGF0aDogZnVuY3Rpb24gKHBhdGgpIHtcblx0ICAgICAgLy8gVGhlIGZvbGxvd2luZyBvcGVyYXRpb25zIGFyZVxuXHQgICAgICAvLyB0aGVuIGFwcGxpZWQsIGluIG9yZGVyLCB0byB0aGUgbmV3IHBhdGg6XG5cdCAgICAgIC8vIDZhKSBBbGwgb2NjdXJyZW5jZXMgb2YgXCIuL1wiLCB3aGVyZSBcIi5cIiBpcyBhIGNvbXBsZXRlIHBhdGhcblx0ICAgICAgLy8gc2VnbWVudCwgYXJlIHJlbW92ZWQuXG5cdCAgICAgIC8vIDZiKSBJZiB0aGUgcGF0aCBlbmRzIHdpdGggXCIuXCIgYXMgYSBjb21wbGV0ZSBwYXRoIHNlZ21lbnQsXG5cdCAgICAgIC8vIHRoYXQgXCIuXCIgaXMgcmVtb3ZlZC5cblx0ICAgICAgcGF0aCA9IHBhdGguc3BsaXQoJycpLnJldmVyc2UoKS5qb2luKCcnKS5yZXBsYWNlKFNMQVNIX0RPVF9SRUdFWCwgJycpO1xuXHQgICAgICAvLyA2YykgQWxsIG9jY3VycmVuY2VzIG9mIFwiPHNlZ21lbnQ+Ly4uL1wiLCB3aGVyZSA8c2VnbWVudD4gaXMgYVxuXHQgICAgICAvLyBjb21wbGV0ZSBwYXRoIHNlZ21lbnQgbm90IGVxdWFsIHRvIFwiLi5cIiwgYXJlIHJlbW92ZWQuXG5cdCAgICAgIC8vIFJlbW92YWwgb2YgdGhlc2UgcGF0aCBzZWdtZW50cyBpcyBwZXJmb3JtZWQgaXRlcmF0aXZlbHksXG5cdCAgICAgIC8vIHJlbW92aW5nIHRoZSBsZWZ0bW9zdCBtYXRjaGluZyBwYXR0ZXJuIG9uIGVhY2ggaXRlcmF0aW9uLFxuXHQgICAgICAvLyB1bnRpbCBubyBtYXRjaGluZyBwYXR0ZXJuIHJlbWFpbnMuXG5cdCAgICAgIC8vIDZkKSBJZiB0aGUgcGF0aCBlbmRzIHdpdGggXCI8c2VnbWVudD4vLi5cIiwgd2hlcmUgPHNlZ21lbnQ+IGlzIGFcblx0ICAgICAgLy8gY29tcGxldGUgcGF0aCBzZWdtZW50IG5vdCBlcXVhbCB0byBcIi4uXCIsIHRoYXRcblx0ICAgICAgLy8gXCI8c2VnbWVudD4vLi5cIiBpcyByZW1vdmVkLlxuXHQgICAgICB3aGlsZSAoXG5cdCAgICAgICAgcGF0aC5sZW5ndGggIT09IChwYXRoID0gcGF0aC5yZXBsYWNlKFNMQVNIX0RPVF9ET1RfUkVHRVgsICcnKSkubGVuZ3RoXG5cdCAgICAgICkge31cblx0ICAgICAgcmV0dXJuIHBhdGguc3BsaXQoJycpLnJldmVyc2UoKS5qb2luKCcnKTtcblx0ICAgIH0sXG5cdCAgICBidWlsZFVSTEZyb21QYXJ0czogZnVuY3Rpb24gKHBhcnRzKSB7XG5cdCAgICAgIHJldHVybiAoXG5cdCAgICAgICAgcGFydHMuc2NoZW1lICtcblx0ICAgICAgICBwYXJ0cy5uZXRMb2MgK1xuXHQgICAgICAgIHBhcnRzLnBhdGggK1xuXHQgICAgICAgIHBhcnRzLnBhcmFtcyArXG5cdCAgICAgICAgcGFydHMucXVlcnkgK1xuXHQgICAgICAgIHBhcnRzLmZyYWdtZW50XG5cdCAgICAgICk7XG5cdCAgICB9LFxuXHQgIH07XG5cblx0ICBtb2R1bGUuZXhwb3J0cyA9IFVSTFRvb2xraXQ7XG5cdH0pKCk7IFxufSAodXJsVG9vbGtpdCkpO1xuXG52YXIgdXJsVG9vbGtpdEV4cG9ydHMgPSB1cmxUb29sa2l0LmV4cG9ydHM7XG5cbmZ1bmN0aW9uIG93bktleXMoZSwgcikge1xuICB2YXIgdCA9IE9iamVjdC5rZXlzKGUpO1xuICBpZiAoT2JqZWN0LmdldE93blByb3BlcnR5U3ltYm9scykge1xuICAgIHZhciBvID0gT2JqZWN0LmdldE93blByb3BlcnR5U3ltYm9scyhlKTtcbiAgICByICYmIChvID0gby5maWx0ZXIoZnVuY3Rpb24gKHIpIHtcbiAgICAgIHJldHVybiBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKGUsIHIpLmVudW1lcmFibGU7XG4gICAgfSkpLCB0LnB1c2guYXBwbHkodCwgbyk7XG4gIH1cbiAgcmV0dXJuIHQ7XG59XG5mdW5jdGlvbiBfb2JqZWN0U3ByZWFkMihlKSB7XG4gIGZvciAodmFyIHIgPSAxOyByIDwgYXJndW1lbnRzLmxlbmd0aDsgcisrKSB7XG4gICAgdmFyIHQgPSBudWxsICE9IGFyZ3VtZW50c1tyXSA/IGFyZ3VtZW50c1tyXSA6IHt9O1xuICAgIHIgJSAyID8gb3duS2V5cyhPYmplY3QodCksICEwKS5mb3JFYWNoKGZ1bmN0aW9uIChyKSB7XG4gICAgICBfZGVmaW5lUHJvcGVydHkoZSwgciwgdFtyXSk7XG4gICAgfSkgOiBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9ycyA/IE9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKGUsIE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3JzKHQpKSA6IG93bktleXMoT2JqZWN0KHQpKS5mb3JFYWNoKGZ1bmN0aW9uIChyKSB7XG4gICAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkoZSwgciwgT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0LCByKSk7XG4gICAgfSk7XG4gIH1cbiAgcmV0dXJuIGU7XG59XG5mdW5jdGlvbiBfdG9QcmltaXRpdmUodCwgcikge1xuICBpZiAoXCJvYmplY3RcIiAhPSB0eXBlb2YgdCB8fCAhdCkgcmV0dXJuIHQ7XG4gIHZhciBlID0gdFtTeW1ib2wudG9QcmltaXRpdmVdO1xuICBpZiAodm9pZCAwICE9PSBlKSB7XG4gICAgdmFyIGkgPSBlLmNhbGwodCwgciB8fCBcImRlZmF1bHRcIik7XG4gICAgaWYgKFwib2JqZWN0XCIgIT0gdHlwZW9mIGkpIHJldHVybiBpO1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoXCJAQHRvUHJpbWl0aXZlIG11c3QgcmV0dXJuIGEgcHJpbWl0aXZlIHZhbHVlLlwiKTtcbiAgfVxuICByZXR1cm4gKFwic3RyaW5nXCIgPT09IHIgPyBTdHJpbmcgOiBOdW1iZXIpKHQpO1xufVxuZnVuY3Rpb24gX3RvUHJvcGVydHlLZXkodCkge1xuICB2YXIgaSA9IF90b1ByaW1pdGl2ZSh0LCBcInN0cmluZ1wiKTtcbiAgcmV0dXJuIFwic3ltYm9sXCIgPT0gdHlwZW9mIGkgPyBpIDogU3RyaW5nKGkpO1xufVxuZnVuY3Rpb24gX2RlZmluZVByb3BlcnR5KG9iaiwga2V5LCB2YWx1ZSkge1xuICBrZXkgPSBfdG9Qcm9wZXJ0eUtleShrZXkpO1xuICBpZiAoa2V5IGluIG9iaikge1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShvYmosIGtleSwge1xuICAgICAgdmFsdWU6IHZhbHVlLFxuICAgICAgZW51bWVyYWJsZTogdHJ1ZSxcbiAgICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZSxcbiAgICAgIHdyaXRhYmxlOiB0cnVlXG4gICAgfSk7XG4gIH0gZWxzZSB7XG4gICAgb2JqW2tleV0gPSB2YWx1ZTtcbiAgfVxuICByZXR1cm4gb2JqO1xufVxuZnVuY3Rpb24gX2V4dGVuZHMoKSB7XG4gIF9leHRlbmRzID0gT2JqZWN0LmFzc2lnbiA/IE9iamVjdC5hc3NpZ24uYmluZCgpIDogZnVuY3Rpb24gKHRhcmdldCkge1xuICAgIGZvciAodmFyIGkgPSAxOyBpIDwgYXJndW1lbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICB2YXIgc291cmNlID0gYXJndW1lbnRzW2ldO1xuICAgICAgZm9yICh2YXIga2V5IGluIHNvdXJjZSkge1xuICAgICAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHNvdXJjZSwga2V5KSkge1xuICAgICAgICAgIHRhcmdldFtrZXldID0gc291cmNlW2tleV07XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHRhcmdldDtcbiAgfTtcbiAgcmV0dXJuIF9leHRlbmRzLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG59XG5cbi8vIGh0dHBzOi8vY2FuaXVzZS5jb20vbWRuLWphdmFzY3JpcHRfYnVpbHRpbnNfbnVtYmVyX2lzZmluaXRlXG5jb25zdCBpc0Zpbml0ZU51bWJlciA9IE51bWJlci5pc0Zpbml0ZSB8fCBmdW5jdGlvbiAodmFsdWUpIHtcbiAgcmV0dXJuIHR5cGVvZiB2YWx1ZSA9PT0gJ251bWJlcicgJiYgaXNGaW5pdGUodmFsdWUpO1xufTtcblxuLy8gaHR0cHM6Ly9jYW5pdXNlLmNvbS9tZG4tamF2YXNjcmlwdF9idWlsdGluc19udW1iZXJfaXNzYWZlaW50ZWdlclxuY29uc3QgaXNTYWZlSW50ZWdlciA9IE51bWJlci5pc1NhZmVJbnRlZ2VyIHx8IGZ1bmN0aW9uICh2YWx1ZSkge1xuICByZXR1cm4gdHlwZW9mIHZhbHVlID09PSAnbnVtYmVyJyAmJiBNYXRoLmFicyh2YWx1ZSkgPD0gTUFYX1NBRkVfSU5URUdFUjtcbn07XG5jb25zdCBNQVhfU0FGRV9JTlRFR0VSID0gTnVtYmVyLk1BWF9TQUZFX0lOVEVHRVIgfHwgOTAwNzE5OTI1NDc0MDk5MTtcblxubGV0IEV2ZW50cyA9IC8qI19fUFVSRV9fKi9mdW5jdGlvbiAoRXZlbnRzKSB7XG4gIEV2ZW50c1tcIk1FRElBX0FUVEFDSElOR1wiXSA9IFwiaGxzTWVkaWFBdHRhY2hpbmdcIjtcbiAgRXZlbnRzW1wiTUVESUFfQVRUQUNIRURcIl0gPSBcImhsc01lZGlhQXR0YWNoZWRcIjtcbiAgRXZlbnRzW1wiTUVESUFfREVUQUNISU5HXCJdID0gXCJobHNNZWRpYURldGFjaGluZ1wiO1xuICBFdmVudHNbXCJNRURJQV9ERVRBQ0hFRFwiXSA9IFwiaGxzTWVkaWFEZXRhY2hlZFwiO1xuICBFdmVudHNbXCJCVUZGRVJfUkVTRVRcIl0gPSBcImhsc0J1ZmZlclJlc2V0XCI7XG4gIEV2ZW50c1tcIkJVRkZFUl9DT0RFQ1NcIl0gPSBcImhsc0J1ZmZlckNvZGVjc1wiO1xuICBFdmVudHNbXCJCVUZGRVJfQ1JFQVRFRFwiXSA9IFwiaGxzQnVmZmVyQ3JlYXRlZFwiO1xuICBFdmVudHNbXCJCVUZGRVJfQVBQRU5ESU5HXCJdID0gXCJobHNCdWZmZXJBcHBlbmRpbmdcIjtcbiAgRXZlbnRzW1wiQlVGRkVSX0FQUEVOREVEXCJdID0gXCJobHNCdWZmZXJBcHBlbmRlZFwiO1xuICBFdmVudHNbXCJCVUZGRVJfRU9TXCJdID0gXCJobHNCdWZmZXJFb3NcIjtcbiAgRXZlbnRzW1wiQlVGRkVSX0ZMVVNISU5HXCJdID0gXCJobHNCdWZmZXJGbHVzaGluZ1wiO1xuICBFdmVudHNbXCJCVUZGRVJfRkxVU0hFRFwiXSA9IFwiaGxzQnVmZmVyRmx1c2hlZFwiO1xuICBFdmVudHNbXCJNQU5JRkVTVF9MT0FESU5HXCJdID0gXCJobHNNYW5pZmVzdExvYWRpbmdcIjtcbiAgRXZlbnRzW1wiTUFOSUZFU1RfTE9BREVEXCJdID0gXCJobHNNYW5pZmVzdExvYWRlZFwiO1xuICBFdmVudHNbXCJNQU5JRkVTVF9QQVJTRURcIl0gPSBcImhsc01hbmlmZXN0UGFyc2VkXCI7XG4gIEV2ZW50c1tcIkxFVkVMX1NXSVRDSElOR1wiXSA9IFwiaGxzTGV2ZWxTd2l0Y2hpbmdcIjtcbiAgRXZlbnRzW1wiTEVWRUxfU1dJVENIRURcIl0gPSBcImhsc0xldmVsU3dpdGNoZWRcIjtcbiAgRXZlbnRzW1wiTEVWRUxfTE9BRElOR1wiXSA9IFwiaGxzTGV2ZWxMb2FkaW5nXCI7XG4gIEV2ZW50c1tcIkxFVkVMX0xPQURFRFwiXSA9IFwiaGxzTGV2ZWxMb2FkZWRcIjtcbiAgRXZlbnRzW1wiTEVWRUxfVVBEQVRFRFwiXSA9IFwiaGxzTGV2ZWxVcGRhdGVkXCI7XG4gIEV2ZW50c1tcIkxFVkVMX1BUU19VUERBVEVEXCJdID0gXCJobHNMZXZlbFB0c1VwZGF0ZWRcIjtcbiAgRXZlbnRzW1wiTEVWRUxTX1VQREFURURcIl0gPSBcImhsc0xldmVsc1VwZGF0ZWRcIjtcbiAgRXZlbnRzW1wiQVVESU9fVFJBQ0tTX1VQREFURURcIl0gPSBcImhsc0F1ZGlvVHJhY2tzVXBkYXRlZFwiO1xuICBFdmVudHNbXCJBVURJT19UUkFDS19TV0lUQ0hJTkdcIl0gPSBcImhsc0F1ZGlvVHJhY2tTd2l0Y2hpbmdcIjtcbiAgRXZlbnRzW1wiQVVESU9fVFJBQ0tfU1dJVENIRURcIl0gPSBcImhsc0F1ZGlvVHJhY2tTd2l0Y2hlZFwiO1xuICBFdmVudHNbXCJBVURJT19UUkFDS19MT0FESU5HXCJdID0gXCJobHNBdWRpb1RyYWNrTG9hZGluZ1wiO1xuICBFdmVudHNbXCJBVURJT19UUkFDS19MT0FERURcIl0gPSBcImhsc0F1ZGlvVHJhY2tMb2FkZWRcIjtcbiAgRXZlbnRzW1wiU1VCVElUTEVfVFJBQ0tTX1VQREFURURcIl0gPSBcImhsc1N1YnRpdGxlVHJhY2tzVXBkYXRlZFwiO1xuICBFdmVudHNbXCJTVUJUSVRMRV9UUkFDS1NfQ0xFQVJFRFwiXSA9IFwiaGxzU3VidGl0bGVUcmFja3NDbGVhcmVkXCI7XG4gIEV2ZW50c1tcIlNVQlRJVExFX1RSQUNLX1NXSVRDSFwiXSA9IFwiaGxzU3VidGl0bGVUcmFja1N3aXRjaFwiO1xuICBFdmVudHNbXCJTVUJUSVRMRV9UUkFDS19MT0FESU5HXCJdID0gXCJobHNTdWJ0aXRsZVRyYWNrTG9hZGluZ1wiO1xuICBFdmVudHNbXCJTVUJUSVRMRV9UUkFDS19MT0FERURcIl0gPSBcImhsc1N1YnRpdGxlVHJhY2tMb2FkZWRcIjtcbiAgRXZlbnRzW1wiU1VCVElUTEVfRlJBR19QUk9DRVNTRURcIl0gPSBcImhsc1N1YnRpdGxlRnJhZ1Byb2Nlc3NlZFwiO1xuICBFdmVudHNbXCJDVUVTX1BBUlNFRFwiXSA9IFwiaGxzQ3Vlc1BhcnNlZFwiO1xuICBFdmVudHNbXCJOT05fTkFUSVZFX1RFWFRfVFJBQ0tTX0ZPVU5EXCJdID0gXCJobHNOb25OYXRpdmVUZXh0VHJhY2tzRm91bmRcIjtcbiAgRXZlbnRzW1wiSU5JVF9QVFNfRk9VTkRcIl0gPSBcImhsc0luaXRQdHNGb3VuZFwiO1xuICBFdmVudHNbXCJGUkFHX0xPQURJTkdcIl0gPSBcImhsc0ZyYWdMb2FkaW5nXCI7XG4gIEV2ZW50c1tcIkZSQUdfTE9BRF9FTUVSR0VOQ1lfQUJPUlRFRFwiXSA9IFwiaGxzRnJhZ0xvYWRFbWVyZ2VuY3lBYm9ydGVkXCI7XG4gIEV2ZW50c1tcIkZSQUdfTE9BREVEXCJdID0gXCJobHNGcmFnTG9hZGVkXCI7XG4gIEV2ZW50c1tcIkZSQUdfREVDUllQVEVEXCJdID0gXCJobHNGcmFnRGVjcnlwdGVkXCI7XG4gIEV2ZW50c1tcIkZSQUdfUEFSU0lOR19JTklUX1NFR01FTlRcIl0gPSBcImhsc0ZyYWdQYXJzaW5nSW5pdFNlZ21lbnRcIjtcbiAgRXZlbnRzW1wiRlJBR19QQVJTSU5HX1VTRVJEQVRBXCJdID0gXCJobHNGcmFnUGFyc2luZ1VzZXJkYXRhXCI7XG4gIEV2ZW50c1tcIkZSQUdfUEFSU0lOR19NRVRBREFUQVwiXSA9IFwiaGxzRnJhZ1BhcnNpbmdNZXRhZGF0YVwiO1xuICBFdmVudHNbXCJGUkFHX1BBUlNFRFwiXSA9IFwiaGxzRnJhZ1BhcnNlZFwiO1xuICBFdmVudHNbXCJGUkFHX0JVRkZFUkVEXCJdID0gXCJobHNGcmFnQnVmZmVyZWRcIjtcbiAgRXZlbnRzW1wiRlJBR19DSEFOR0VEXCJdID0gXCJobHNGcmFnQ2hhbmdlZFwiO1xuICBFdmVudHNbXCJGUFNfRFJPUFwiXSA9IFwiaGxzRnBzRHJvcFwiO1xuICBFdmVudHNbXCJGUFNfRFJPUF9MRVZFTF9DQVBQSU5HXCJdID0gXCJobHNGcHNEcm9wTGV2ZWxDYXBwaW5nXCI7XG4gIEV2ZW50c1tcIk1BWF9BVVRPX0xFVkVMX1VQREFURURcIl0gPSBcImhsc01heEF1dG9MZXZlbFVwZGF0ZWRcIjtcbiAgRXZlbnRzW1wiRVJST1JcIl0gPSBcImhsc0Vycm9yXCI7XG4gIEV2ZW50c1tcIkRFU1RST1lJTkdcIl0gPSBcImhsc0Rlc3Ryb3lpbmdcIjtcbiAgRXZlbnRzW1wiS0VZX0xPQURJTkdcIl0gPSBcImhsc0tleUxvYWRpbmdcIjtcbiAgRXZlbnRzW1wiS0VZX0xPQURFRFwiXSA9IFwiaGxzS2V5TG9hZGVkXCI7XG4gIEV2ZW50c1tcIkxJVkVfQkFDS19CVUZGRVJfUkVBQ0hFRFwiXSA9IFwiaGxzTGl2ZUJhY2tCdWZmZXJSZWFjaGVkXCI7XG4gIEV2ZW50c1tcIkJBQ0tfQlVGRkVSX1JFQUNIRURcIl0gPSBcImhsc0JhY2tCdWZmZXJSZWFjaGVkXCI7XG4gIEV2ZW50c1tcIlNURUVSSU5HX01BTklGRVNUX0xPQURFRFwiXSA9IFwiaGxzU3RlZXJpbmdNYW5pZmVzdExvYWRlZFwiO1xuICByZXR1cm4gRXZlbnRzO1xufSh7fSk7XG5cbi8qKlxuICogRGVmaW5lcyBlYWNoIEV2ZW50IHR5cGUgYW5kIHBheWxvYWQgYnkgRXZlbnQgbmFtZS4gVXNlZCBpbiB7QGxpbmsgaGxzLmpzI0hsc0V2ZW50RW1pdHRlcn0gdG8gc3Ryb25nbHkgdHlwZSB0aGUgZXZlbnQgbGlzdGVuZXIgQVBJLlxuICovXG5cbmxldCBFcnJvclR5cGVzID0gLyojX19QVVJFX18qL2Z1bmN0aW9uIChFcnJvclR5cGVzKSB7XG4gIEVycm9yVHlwZXNbXCJORVRXT1JLX0VSUk9SXCJdID0gXCJuZXR3b3JrRXJyb3JcIjtcbiAgRXJyb3JUeXBlc1tcIk1FRElBX0VSUk9SXCJdID0gXCJtZWRpYUVycm9yXCI7XG4gIEVycm9yVHlwZXNbXCJLRVlfU1lTVEVNX0VSUk9SXCJdID0gXCJrZXlTeXN0ZW1FcnJvclwiO1xuICBFcnJvclR5cGVzW1wiTVVYX0VSUk9SXCJdID0gXCJtdXhFcnJvclwiO1xuICBFcnJvclR5cGVzW1wiT1RIRVJfRVJST1JcIl0gPSBcIm90aGVyRXJyb3JcIjtcbiAgcmV0dXJuIEVycm9yVHlwZXM7XG59KHt9KTtcbmxldCBFcnJvckRldGFpbHMgPSAvKiNfX1BVUkVfXyovZnVuY3Rpb24gKEVycm9yRGV0YWlscykge1xuICBFcnJvckRldGFpbHNbXCJLRVlfU1lTVEVNX05PX0tFWVNcIl0gPSBcImtleVN5c3RlbU5vS2V5c1wiO1xuICBFcnJvckRldGFpbHNbXCJLRVlfU1lTVEVNX05PX0FDQ0VTU1wiXSA9IFwia2V5U3lzdGVtTm9BY2Nlc3NcIjtcbiAgRXJyb3JEZXRhaWxzW1wiS0VZX1NZU1RFTV9OT19TRVNTSU9OXCJdID0gXCJrZXlTeXN0ZW1Ob1Nlc3Npb25cIjtcbiAgRXJyb3JEZXRhaWxzW1wiS0VZX1NZU1RFTV9OT19DT05GSUdVUkVEX0xJQ0VOU0VcIl0gPSBcImtleVN5c3RlbU5vQ29uZmlndXJlZExpY2Vuc2VcIjtcbiAgRXJyb3JEZXRhaWxzW1wiS0VZX1NZU1RFTV9MSUNFTlNFX1JFUVVFU1RfRkFJTEVEXCJdID0gXCJrZXlTeXN0ZW1MaWNlbnNlUmVxdWVzdEZhaWxlZFwiO1xuICBFcnJvckRldGFpbHNbXCJLRVlfU1lTVEVNX1NFUlZFUl9DRVJUSUZJQ0FURV9SRVFVRVNUX0ZBSUxFRFwiXSA9IFwia2V5U3lzdGVtU2VydmVyQ2VydGlmaWNhdGVSZXF1ZXN0RmFpbGVkXCI7XG4gIEVycm9yRGV0YWlsc1tcIktFWV9TWVNURU1fU0VSVkVSX0NFUlRJRklDQVRFX1VQREFURV9GQUlMRURcIl0gPSBcImtleVN5c3RlbVNlcnZlckNlcnRpZmljYXRlVXBkYXRlRmFpbGVkXCI7XG4gIEVycm9yRGV0YWlsc1tcIktFWV9TWVNURU1fU0VTU0lPTl9VUERBVEVfRkFJTEVEXCJdID0gXCJrZXlTeXN0ZW1TZXNzaW9uVXBkYXRlRmFpbGVkXCI7XG4gIEVycm9yRGV0YWlsc1tcIktFWV9TWVNURU1fU1RBVFVTX09VVFBVVF9SRVNUUklDVEVEXCJdID0gXCJrZXlTeXN0ZW1TdGF0dXNPdXRwdXRSZXN0cmljdGVkXCI7XG4gIEVycm9yRGV0YWlsc1tcIktFWV9TWVNURU1fU1RBVFVTX0lOVEVSTkFMX0VSUk9SXCJdID0gXCJrZXlTeXN0ZW1TdGF0dXNJbnRlcm5hbEVycm9yXCI7XG4gIEVycm9yRGV0YWlsc1tcIk1BTklGRVNUX0xPQURfRVJST1JcIl0gPSBcIm1hbmlmZXN0TG9hZEVycm9yXCI7XG4gIEVycm9yRGV0YWlsc1tcIk1BTklGRVNUX0xPQURfVElNRU9VVFwiXSA9IFwibWFuaWZlc3RMb2FkVGltZU91dFwiO1xuICBFcnJvckRldGFpbHNbXCJNQU5JRkVTVF9QQVJTSU5HX0VSUk9SXCJdID0gXCJtYW5pZmVzdFBhcnNpbmdFcnJvclwiO1xuICBFcnJvckRldGFpbHNbXCJNQU5JRkVTVF9JTkNPTVBBVElCTEVfQ09ERUNTX0VSUk9SXCJdID0gXCJtYW5pZmVzdEluY29tcGF0aWJsZUNvZGVjc0Vycm9yXCI7XG4gIEVycm9yRGV0YWlsc1tcIkxFVkVMX0VNUFRZX0VSUk9SXCJdID0gXCJsZXZlbEVtcHR5RXJyb3JcIjtcbiAgRXJyb3JEZXRhaWxzW1wiTEVWRUxfTE9BRF9FUlJPUlwiXSA9IFwibGV2ZWxMb2FkRXJyb3JcIjtcbiAgRXJyb3JEZXRhaWxzW1wiTEVWRUxfTE9BRF9USU1FT1VUXCJdID0gXCJsZXZlbExvYWRUaW1lT3V0XCI7XG4gIEVycm9yRGV0YWlsc1tcIkxFVkVMX1BBUlNJTkdfRVJST1JcIl0gPSBcImxldmVsUGFyc2luZ0Vycm9yXCI7XG4gIEVycm9yRGV0YWlsc1tcIkxFVkVMX1NXSVRDSF9FUlJPUlwiXSA9IFwibGV2ZWxTd2l0Y2hFcnJvclwiO1xuICBFcnJvckRldGFpbHNbXCJBVURJT19UUkFDS19MT0FEX0VSUk9SXCJdID0gXCJhdWRpb1RyYWNrTG9hZEVycm9yXCI7XG4gIEVycm9yRGV0YWlsc1tcIkFVRElPX1RSQUNLX0xPQURfVElNRU9VVFwiXSA9IFwiYXVkaW9UcmFja0xvYWRUaW1lT3V0XCI7XG4gIEVycm9yRGV0YWlsc1tcIlNVQlRJVExFX0xPQURfRVJST1JcIl0gPSBcInN1YnRpdGxlVHJhY2tMb2FkRXJyb3JcIjtcbiAgRXJyb3JEZXRhaWxzW1wiU1VCVElUTEVfVFJBQ0tfTE9BRF9USU1FT1VUXCJdID0gXCJzdWJ0aXRsZVRyYWNrTG9hZFRpbWVPdXRcIjtcbiAgRXJyb3JEZXRhaWxzW1wiRlJBR19MT0FEX0VSUk9SXCJdID0gXCJmcmFnTG9hZEVycm9yXCI7XG4gIEVycm9yRGV0YWlsc1tcIkZSQUdfTE9BRF9USU1FT1VUXCJdID0gXCJmcmFnTG9hZFRpbWVPdXRcIjtcbiAgRXJyb3JEZXRhaWxzW1wiRlJBR19ERUNSWVBUX0VSUk9SXCJdID0gXCJmcmFnRGVjcnlwdEVycm9yXCI7XG4gIEVycm9yRGV0YWlsc1tcIkZSQUdfUEFSU0lOR19FUlJPUlwiXSA9IFwiZnJhZ1BhcnNpbmdFcnJvclwiO1xuICBFcnJvckRldGFpbHNbXCJGUkFHX0dBUFwiXSA9IFwiZnJhZ0dhcFwiO1xuICBFcnJvckRldGFpbHNbXCJSRU1VWF9BTExPQ19FUlJPUlwiXSA9IFwicmVtdXhBbGxvY0Vycm9yXCI7XG4gIEVycm9yRGV0YWlsc1tcIktFWV9MT0FEX0VSUk9SXCJdID0gXCJrZXlMb2FkRXJyb3JcIjtcbiAgRXJyb3JEZXRhaWxzW1wiS0VZX0xPQURfVElNRU9VVFwiXSA9IFwia2V5TG9hZFRpbWVPdXRcIjtcbiAgRXJyb3JEZXRhaWxzW1wiQlVGRkVSX0FERF9DT0RFQ19FUlJPUlwiXSA9IFwiYnVmZmVyQWRkQ29kZWNFcnJvclwiO1xuICBFcnJvckRldGFpbHNbXCJCVUZGRVJfSU5DT01QQVRJQkxFX0NPREVDU19FUlJPUlwiXSA9IFwiYnVmZmVySW5jb21wYXRpYmxlQ29kZWNzRXJyb3JcIjtcbiAgRXJyb3JEZXRhaWxzW1wiQlVGRkVSX0FQUEVORF9FUlJPUlwiXSA9IFwiYnVmZmVyQXBwZW5kRXJyb3JcIjtcbiAgRXJyb3JEZXRhaWxzW1wiQlVGRkVSX0FQUEVORElOR19FUlJPUlwiXSA9IFwiYnVmZmVyQXBwZW5kaW5nRXJyb3JcIjtcbiAgRXJyb3JEZXRhaWxzW1wiQlVGRkVSX1NUQUxMRURfRVJST1JcIl0gPSBcImJ1ZmZlclN0YWxsZWRFcnJvclwiO1xuICBFcnJvckRldGFpbHNbXCJCVUZGRVJfRlVMTF9FUlJPUlwiXSA9IFwiYnVmZmVyRnVsbEVycm9yXCI7XG4gIEVycm9yRGV0YWlsc1tcIkJVRkZFUl9TRUVLX09WRVJfSE9MRVwiXSA9IFwiYnVmZmVyU2Vla092ZXJIb2xlXCI7XG4gIEVycm9yRGV0YWlsc1tcIkJVRkZFUl9OVURHRV9PTl9TVEFMTFwiXSA9IFwiYnVmZmVyTnVkZ2VPblN0YWxsXCI7XG4gIEVycm9yRGV0YWlsc1tcIklOVEVSTkFMX0VYQ0VQVElPTlwiXSA9IFwiaW50ZXJuYWxFeGNlcHRpb25cIjtcbiAgRXJyb3JEZXRhaWxzW1wiSU5URVJOQUxfQUJPUlRFRFwiXSA9IFwiYWJvcnRlZFwiO1xuICBFcnJvckRldGFpbHNbXCJVTktOT1dOXCJdID0gXCJ1bmtub3duXCI7XG4gIHJldHVybiBFcnJvckRldGFpbHM7XG59KHt9KTtcblxuY29uc3Qgbm9vcCA9IGZ1bmN0aW9uIG5vb3AoKSB7fTtcbmNvbnN0IGZha2VMb2dnZXIgPSB7XG4gIHRyYWNlOiBub29wLFxuICBkZWJ1Zzogbm9vcCxcbiAgbG9nOiBub29wLFxuICB3YXJuOiBub29wLFxuICBpbmZvOiBub29wLFxuICBlcnJvcjogbm9vcFxufTtcbmxldCBleHBvcnRlZExvZ2dlciA9IGZha2VMb2dnZXI7XG5cbi8vIGxldCBsYXN0Q2FsbFRpbWU7XG4vLyBmdW5jdGlvbiBmb3JtYXRNc2dXaXRoVGltZUluZm8odHlwZSwgbXNnKSB7XG4vLyAgIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4vLyAgIGNvbnN0IGRpZmYgPSBsYXN0Q2FsbFRpbWUgPyAnKycgKyAobm93IC0gbGFzdENhbGxUaW1lKSA6ICcwJztcbi8vICAgbGFzdENhbGxUaW1lID0gbm93O1xuLy8gICBtc2cgPSAobmV3IERhdGUobm93KSkudG9JU09TdHJpbmcoKSArICcgfCBbJyArICB0eXBlICsgJ10gPiAnICsgbXNnICsgJyAoICcgKyBkaWZmICsgJyBtcyApJztcbi8vICAgcmV0dXJuIG1zZztcbi8vIH1cblxuZnVuY3Rpb24gY29uc29sZVByaW50Rm4odHlwZSkge1xuICBjb25zdCBmdW5jID0gc2VsZi5jb25zb2xlW3R5cGVdO1xuICBpZiAoZnVuYykge1xuICAgIHJldHVybiBmdW5jLmJpbmQoc2VsZi5jb25zb2xlLCBgWyR7dHlwZX1dID5gKTtcbiAgfVxuICByZXR1cm4gbm9vcDtcbn1cbmZ1bmN0aW9uIGV4cG9ydExvZ2dlckZ1bmN0aW9ucyhkZWJ1Z0NvbmZpZywgLi4uZnVuY3Rpb25zKSB7XG4gIGZ1bmN0aW9ucy5mb3JFYWNoKGZ1bmN0aW9uICh0eXBlKSB7XG4gICAgZXhwb3J0ZWRMb2dnZXJbdHlwZV0gPSBkZWJ1Z0NvbmZpZ1t0eXBlXSA/IGRlYnVnQ29uZmlnW3R5cGVdLmJpbmQoZGVidWdDb25maWcpIDogY29uc29sZVByaW50Rm4odHlwZSk7XG4gIH0pO1xufVxuZnVuY3Rpb24gZW5hYmxlTG9ncyhkZWJ1Z0NvbmZpZywgaWQpIHtcbiAgLy8gY2hlY2sgdGhhdCBjb25zb2xlIGlzIGF2YWlsYWJsZVxuICBpZiAodHlwZW9mIGNvbnNvbGUgPT09ICdvYmplY3QnICYmIGRlYnVnQ29uZmlnID09PSB0cnVlIHx8IHR5cGVvZiBkZWJ1Z0NvbmZpZyA9PT0gJ29iamVjdCcpIHtcbiAgICBleHBvcnRMb2dnZXJGdW5jdGlvbnMoZGVidWdDb25maWcsXG4gICAgLy8gUmVtb3ZlIG91dCBmcm9tIGxpc3QgaGVyZSB0byBoYXJkLWRpc2FibGUgYSBsb2ctbGV2ZWxcbiAgICAvLyAndHJhY2UnLFxuICAgICdkZWJ1ZycsICdsb2cnLCAnaW5mbycsICd3YXJuJywgJ2Vycm9yJyk7XG4gICAgLy8gU29tZSBicm93c2VycyBkb24ndCBhbGxvdyB0byB1c2UgYmluZCBvbiBjb25zb2xlIG9iamVjdCBhbnl3YXlcbiAgICAvLyBmYWxsYmFjayB0byBkZWZhdWx0IGlmIG5lZWRlZFxuICAgIHRyeSB7XG4gICAgICBleHBvcnRlZExvZ2dlci5sb2coYERlYnVnIGxvZ3MgZW5hYmxlZCBmb3IgXCIke2lkfVwiIGluIGhscy5qcyB2ZXJzaW9uICR7XCIxLjUuMTVcIn1gKTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBleHBvcnRlZExvZ2dlciA9IGZha2VMb2dnZXI7XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIGV4cG9ydGVkTG9nZ2VyID0gZmFrZUxvZ2dlcjtcbiAgfVxufVxuY29uc3QgbG9nZ2VyID0gZXhwb3J0ZWRMb2dnZXI7XG5cbmNvbnN0IERFQ0lNQUxfUkVTT0xVVElPTl9SRUdFWCA9IC9eKFxcZCspeChcXGQrKSQvO1xuY29uc3QgQVRUUl9MSVNUX1JFR0VYID0gLyguKz8pPShcIi4qP1wifC4qPykoPzosfCQpL2c7XG5cbi8vIGFkYXB0ZWQgZnJvbSBodHRwczovL2dpdGh1Yi5jb20va2Fub25naWwvbm9kZS1tM3U4cGFyc2UvYmxvYi9tYXN0ZXIvYXR0cmxpc3QuanNcbmNsYXNzIEF0dHJMaXN0IHtcbiAgY29uc3RydWN0b3IoYXR0cnMpIHtcbiAgICBpZiAodHlwZW9mIGF0dHJzID09PSAnc3RyaW5nJykge1xuICAgICAgYXR0cnMgPSBBdHRyTGlzdC5wYXJzZUF0dHJMaXN0KGF0dHJzKTtcbiAgICB9XG4gICAgX2V4dGVuZHModGhpcywgYXR0cnMpO1xuICB9XG4gIGdldCBjbGllbnRBdHRycygpIHtcbiAgICByZXR1cm4gT2JqZWN0LmtleXModGhpcykuZmlsdGVyKGF0dHIgPT4gYXR0ci5zdWJzdHJpbmcoMCwgMikgPT09ICdYLScpO1xuICB9XG4gIGRlY2ltYWxJbnRlZ2VyKGF0dHJOYW1lKSB7XG4gICAgY29uc3QgaW50VmFsdWUgPSBwYXJzZUludCh0aGlzW2F0dHJOYW1lXSwgMTApO1xuICAgIGlmIChpbnRWYWx1ZSA+IE51bWJlci5NQVhfU0FGRV9JTlRFR0VSKSB7XG4gICAgICByZXR1cm4gSW5maW5pdHk7XG4gICAgfVxuICAgIHJldHVybiBpbnRWYWx1ZTtcbiAgfVxuICBoZXhhZGVjaW1hbEludGVnZXIoYXR0ck5hbWUpIHtcbiAgICBpZiAodGhpc1thdHRyTmFtZV0pIHtcbiAgICAgIGxldCBzdHJpbmdWYWx1ZSA9ICh0aGlzW2F0dHJOYW1lXSB8fCAnMHgnKS5zbGljZSgyKTtcbiAgICAgIHN0cmluZ1ZhbHVlID0gKHN0cmluZ1ZhbHVlLmxlbmd0aCAmIDEgPyAnMCcgOiAnJykgKyBzdHJpbmdWYWx1ZTtcbiAgICAgIGNvbnN0IHZhbHVlID0gbmV3IFVpbnQ4QXJyYXkoc3RyaW5nVmFsdWUubGVuZ3RoIC8gMik7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHN0cmluZ1ZhbHVlLmxlbmd0aCAvIDI7IGkrKykge1xuICAgICAgICB2YWx1ZVtpXSA9IHBhcnNlSW50KHN0cmluZ1ZhbHVlLnNsaWNlKGkgKiAyLCBpICogMiArIDIpLCAxNik7XG4gICAgICB9XG4gICAgICByZXR1cm4gdmFsdWU7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuICBoZXhhZGVjaW1hbEludGVnZXJBc051bWJlcihhdHRyTmFtZSkge1xuICAgIGNvbnN0IGludFZhbHVlID0gcGFyc2VJbnQodGhpc1thdHRyTmFtZV0sIDE2KTtcbiAgICBpZiAoaW50VmFsdWUgPiBOdW1iZXIuTUFYX1NBRkVfSU5URUdFUikge1xuICAgICAgcmV0dXJuIEluZmluaXR5O1xuICAgIH1cbiAgICByZXR1cm4gaW50VmFsdWU7XG4gIH1cbiAgZGVjaW1hbEZsb2F0aW5nUG9pbnQoYXR0ck5hbWUpIHtcbiAgICByZXR1cm4gcGFyc2VGbG9hdCh0aGlzW2F0dHJOYW1lXSk7XG4gIH1cbiAgb3B0aW9uYWxGbG9hdChhdHRyTmFtZSwgZGVmYXVsdFZhbHVlKSB7XG4gICAgY29uc3QgdmFsdWUgPSB0aGlzW2F0dHJOYW1lXTtcbiAgICByZXR1cm4gdmFsdWUgPyBwYXJzZUZsb2F0KHZhbHVlKSA6IGRlZmF1bHRWYWx1ZTtcbiAgfVxuICBlbnVtZXJhdGVkU3RyaW5nKGF0dHJOYW1lKSB7XG4gICAgcmV0dXJuIHRoaXNbYXR0ck5hbWVdO1xuICB9XG4gIGJvb2woYXR0ck5hbWUpIHtcbiAgICByZXR1cm4gdGhpc1thdHRyTmFtZV0gPT09ICdZRVMnO1xuICB9XG4gIGRlY2ltYWxSZXNvbHV0aW9uKGF0dHJOYW1lKSB7XG4gICAgY29uc3QgcmVzID0gREVDSU1BTF9SRVNPTFVUSU9OX1JFR0VYLmV4ZWModGhpc1thdHRyTmFtZV0pO1xuICAgIGlmIChyZXMgPT09IG51bGwpIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICAgIHJldHVybiB7XG4gICAgICB3aWR0aDogcGFyc2VJbnQocmVzWzFdLCAxMCksXG4gICAgICBoZWlnaHQ6IHBhcnNlSW50KHJlc1syXSwgMTApXG4gICAgfTtcbiAgfVxuICBzdGF0aWMgcGFyc2VBdHRyTGlzdChpbnB1dCkge1xuICAgIGxldCBtYXRjaDtcbiAgICBjb25zdCBhdHRycyA9IHt9O1xuICAgIGNvbnN0IHF1b3RlID0gJ1wiJztcbiAgICBBVFRSX0xJU1RfUkVHRVgubGFzdEluZGV4ID0gMDtcbiAgICB3aGlsZSAoKG1hdGNoID0gQVRUUl9MSVNUX1JFR0VYLmV4ZWMoaW5wdXQpKSAhPT0gbnVsbCkge1xuICAgICAgbGV0IHZhbHVlID0gbWF0Y2hbMl07XG4gICAgICBpZiAodmFsdWUuaW5kZXhPZihxdW90ZSkgPT09IDAgJiYgdmFsdWUubGFzdEluZGV4T2YocXVvdGUpID09PSB2YWx1ZS5sZW5ndGggLSAxKSB7XG4gICAgICAgIHZhbHVlID0gdmFsdWUuc2xpY2UoMSwgLTEpO1xuICAgICAgfVxuICAgICAgY29uc3QgbmFtZSA9IG1hdGNoWzFdLnRyaW0oKTtcbiAgICAgIGF0dHJzW25hbWVdID0gdmFsdWU7XG4gICAgfVxuICAgIHJldHVybiBhdHRycztcbiAgfVxufVxuXG4vLyBBdm9pZCBleHBvcnRpbmcgY29uc3QgZW51bSBzbyB0aGF0IHRoZXNlIHZhbHVlcyBjYW4gYmUgaW5saW5lZFxuXG5mdW5jdGlvbiBpc0RhdGVSYW5nZUN1ZUF0dHJpYnV0ZShhdHRyTmFtZSkge1xuICByZXR1cm4gYXR0ck5hbWUgIT09IFwiSURcIiAmJiBhdHRyTmFtZSAhPT0gXCJDTEFTU1wiICYmIGF0dHJOYW1lICE9PSBcIlNUQVJULURBVEVcIiAmJiBhdHRyTmFtZSAhPT0gXCJEVVJBVElPTlwiICYmIGF0dHJOYW1lICE9PSBcIkVORC1EQVRFXCIgJiYgYXR0ck5hbWUgIT09IFwiRU5ELU9OLU5FWFRcIjtcbn1cbmZ1bmN0aW9uIGlzU0NURTM1QXR0cmlidXRlKGF0dHJOYW1lKSB7XG4gIHJldHVybiBhdHRyTmFtZSA9PT0gXCJTQ1RFMzUtT1VUXCIgfHwgYXR0ck5hbWUgPT09IFwiU0NURTM1LUlOXCI7XG59XG5jbGFzcyBEYXRlUmFuZ2Uge1xuICBjb25zdHJ1Y3RvcihkYXRlUmFuZ2VBdHRyLCBkYXRlUmFuZ2VXaXRoU2FtZUlkKSB7XG4gICAgdGhpcy5hdHRyID0gdm9pZCAwO1xuICAgIHRoaXMuX3N0YXJ0RGF0ZSA9IHZvaWQgMDtcbiAgICB0aGlzLl9lbmREYXRlID0gdm9pZCAwO1xuICAgIHRoaXMuX2JhZFZhbHVlRm9yU2FtZUlkID0gdm9pZCAwO1xuICAgIGlmIChkYXRlUmFuZ2VXaXRoU2FtZUlkKSB7XG4gICAgICBjb25zdCBwcmV2aW91c0F0dHIgPSBkYXRlUmFuZ2VXaXRoU2FtZUlkLmF0dHI7XG4gICAgICBmb3IgKGNvbnN0IGtleSBpbiBwcmV2aW91c0F0dHIpIHtcbiAgICAgICAgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChkYXRlUmFuZ2VBdHRyLCBrZXkpICYmIGRhdGVSYW5nZUF0dHJba2V5XSAhPT0gcHJldmlvdXNBdHRyW2tleV0pIHtcbiAgICAgICAgICBsb2dnZXIud2FybihgREFURVJBTkdFIHRhZyBhdHRyaWJ1dGU6IFwiJHtrZXl9XCIgZG9lcyBub3QgbWF0Y2ggZm9yIHRhZ3Mgd2l0aCBJRDogXCIke2RhdGVSYW5nZUF0dHIuSUR9XCJgKTtcbiAgICAgICAgICB0aGlzLl9iYWRWYWx1ZUZvclNhbWVJZCA9IGtleTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgLy8gTWVyZ2UgRGF0ZVJhbmdlIHRhZ3Mgd2l0aCB0aGUgc2FtZSBJRFxuICAgICAgZGF0ZVJhbmdlQXR0ciA9IF9leHRlbmRzKG5ldyBBdHRyTGlzdCh7fSksIHByZXZpb3VzQXR0ciwgZGF0ZVJhbmdlQXR0cik7XG4gICAgfVxuICAgIHRoaXMuYXR0ciA9IGRhdGVSYW5nZUF0dHI7XG4gICAgdGhpcy5fc3RhcnREYXRlID0gbmV3IERhdGUoZGF0ZVJhbmdlQXR0cltcIlNUQVJULURBVEVcIl0pO1xuICAgIGlmIChcIkVORC1EQVRFXCIgaW4gdGhpcy5hdHRyKSB7XG4gICAgICBjb25zdCBlbmREYXRlID0gbmV3IERhdGUodGhpcy5hdHRyW1wiRU5ELURBVEVcIl0pO1xuICAgICAgaWYgKGlzRmluaXRlTnVtYmVyKGVuZERhdGUuZ2V0VGltZSgpKSkge1xuICAgICAgICB0aGlzLl9lbmREYXRlID0gZW5kRGF0ZTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgZ2V0IGlkKCkge1xuICAgIHJldHVybiB0aGlzLmF0dHIuSUQ7XG4gIH1cbiAgZ2V0IGNsYXNzKCkge1xuICAgIHJldHVybiB0aGlzLmF0dHIuQ0xBU1M7XG4gIH1cbiAgZ2V0IHN0YXJ0RGF0ZSgpIHtcbiAgICByZXR1cm4gdGhpcy5fc3RhcnREYXRlO1xuICB9XG4gIGdldCBlbmREYXRlKCkge1xuICAgIGlmICh0aGlzLl9lbmREYXRlKSB7XG4gICAgICByZXR1cm4gdGhpcy5fZW5kRGF0ZTtcbiAgICB9XG4gICAgY29uc3QgZHVyYXRpb24gPSB0aGlzLmR1cmF0aW9uO1xuICAgIGlmIChkdXJhdGlvbiAhPT0gbnVsbCkge1xuICAgICAgcmV0dXJuIG5ldyBEYXRlKHRoaXMuX3N0YXJ0RGF0ZS5nZXRUaW1lKCkgKyBkdXJhdGlvbiAqIDEwMDApO1xuICAgIH1cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBnZXQgZHVyYXRpb24oKSB7XG4gICAgaWYgKFwiRFVSQVRJT05cIiBpbiB0aGlzLmF0dHIpIHtcbiAgICAgIGNvbnN0IGR1cmF0aW9uID0gdGhpcy5hdHRyLmRlY2ltYWxGbG9hdGluZ1BvaW50KFwiRFVSQVRJT05cIik7XG4gICAgICBpZiAoaXNGaW5pdGVOdW1iZXIoZHVyYXRpb24pKSB7XG4gICAgICAgIHJldHVybiBkdXJhdGlvbjtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKHRoaXMuX2VuZERhdGUpIHtcbiAgICAgIHJldHVybiAodGhpcy5fZW5kRGF0ZS5nZXRUaW1lKCkgLSB0aGlzLl9zdGFydERhdGUuZ2V0VGltZSgpKSAvIDEwMDA7XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG4gIGdldCBwbGFubmVkRHVyYXRpb24oKSB7XG4gICAgaWYgKFwiUExBTk5FRC1EVVJBVElPTlwiIGluIHRoaXMuYXR0cikge1xuICAgICAgcmV0dXJuIHRoaXMuYXR0ci5kZWNpbWFsRmxvYXRpbmdQb2ludChcIlBMQU5ORUQtRFVSQVRJT05cIik7XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG4gIGdldCBlbmRPbk5leHQoKSB7XG4gICAgcmV0dXJuIHRoaXMuYXR0ci5ib29sKFwiRU5ELU9OLU5FWFRcIik7XG4gIH1cbiAgZ2V0IGlzVmFsaWQoKSB7XG4gICAgcmV0dXJuICEhdGhpcy5pZCAmJiAhdGhpcy5fYmFkVmFsdWVGb3JTYW1lSWQgJiYgaXNGaW5pdGVOdW1iZXIodGhpcy5zdGFydERhdGUuZ2V0VGltZSgpKSAmJiAodGhpcy5kdXJhdGlvbiA9PT0gbnVsbCB8fCB0aGlzLmR1cmF0aW9uID49IDApICYmICghdGhpcy5lbmRPbk5leHQgfHwgISF0aGlzLmNsYXNzKTtcbiAgfVxufVxuXG5jbGFzcyBMb2FkU3RhdHMge1xuICBjb25zdHJ1Y3RvcigpIHtcbiAgICB0aGlzLmFib3J0ZWQgPSBmYWxzZTtcbiAgICB0aGlzLmxvYWRlZCA9IDA7XG4gICAgdGhpcy5yZXRyeSA9IDA7XG4gICAgdGhpcy50b3RhbCA9IDA7XG4gICAgdGhpcy5jaHVua0NvdW50ID0gMDtcbiAgICB0aGlzLmJ3RXN0aW1hdGUgPSAwO1xuICAgIHRoaXMubG9hZGluZyA9IHtcbiAgICAgIHN0YXJ0OiAwLFxuICAgICAgZmlyc3Q6IDAsXG4gICAgICBlbmQ6IDBcbiAgICB9O1xuICAgIHRoaXMucGFyc2luZyA9IHtcbiAgICAgIHN0YXJ0OiAwLFxuICAgICAgZW5kOiAwXG4gICAgfTtcbiAgICB0aGlzLmJ1ZmZlcmluZyA9IHtcbiAgICAgIHN0YXJ0OiAwLFxuICAgICAgZmlyc3Q6IDAsXG4gICAgICBlbmQ6IDBcbiAgICB9O1xuICB9XG59XG5cbnZhciBFbGVtZW50YXJ5U3RyZWFtVHlwZXMgPSB7XG4gIEFVRElPOiBcImF1ZGlvXCIsXG4gIFZJREVPOiBcInZpZGVvXCIsXG4gIEFVRElPVklERU86IFwiYXVkaW92aWRlb1wiXG59O1xuY2xhc3MgQmFzZVNlZ21lbnQge1xuICBjb25zdHJ1Y3RvcihiYXNldXJsKSB7XG4gICAgdGhpcy5fYnl0ZVJhbmdlID0gbnVsbDtcbiAgICB0aGlzLl91cmwgPSBudWxsO1xuICAgIC8vIGJhc2V1cmwgaXMgdGhlIFVSTCB0byB0aGUgcGxheWxpc3RcbiAgICB0aGlzLmJhc2V1cmwgPSB2b2lkIDA7XG4gICAgLy8gcmVsdXJsIGlzIHRoZSBwb3J0aW9uIG9mIHRoZSBVUkwgdGhhdCBjb21lcyBmcm9tIGluc2lkZSB0aGUgcGxheWxpc3QuXG4gICAgdGhpcy5yZWx1cmwgPSB2b2lkIDA7XG4gICAgLy8gSG9sZHMgdGhlIHR5cGVzIG9mIGRhdGEgdGhpcyBmcmFnbWVudCBzdXBwb3J0c1xuICAgIHRoaXMuZWxlbWVudGFyeVN0cmVhbXMgPSB7XG4gICAgICBbRWxlbWVudGFyeVN0cmVhbVR5cGVzLkFVRElPXTogbnVsbCxcbiAgICAgIFtFbGVtZW50YXJ5U3RyZWFtVHlwZXMuVklERU9dOiBudWxsLFxuICAgICAgW0VsZW1lbnRhcnlTdHJlYW1UeXBlcy5BVURJT1ZJREVPXTogbnVsbFxuICAgIH07XG4gICAgdGhpcy5iYXNldXJsID0gYmFzZXVybDtcbiAgfVxuXG4gIC8vIHNldEJ5dGVSYW5nZSBjb252ZXJ0cyBhIEVYVC1YLUJZVEVSQU5HRSBhdHRyaWJ1dGUgaW50byBhIHR3byBlbGVtZW50IGFycmF5XG4gIHNldEJ5dGVSYW5nZSh2YWx1ZSwgcHJldmlvdXMpIHtcbiAgICBjb25zdCBwYXJhbXMgPSB2YWx1ZS5zcGxpdCgnQCcsIDIpO1xuICAgIGxldCBzdGFydDtcbiAgICBpZiAocGFyYW1zLmxlbmd0aCA9PT0gMSkge1xuICAgICAgc3RhcnQgPSAocHJldmlvdXMgPT0gbnVsbCA/IHZvaWQgMCA6IHByZXZpb3VzLmJ5dGVSYW5nZUVuZE9mZnNldCkgfHwgMDtcbiAgICB9IGVsc2Uge1xuICAgICAgc3RhcnQgPSBwYXJzZUludChwYXJhbXNbMV0pO1xuICAgIH1cbiAgICB0aGlzLl9ieXRlUmFuZ2UgPSBbc3RhcnQsIHBhcnNlSW50KHBhcmFtc1swXSkgKyBzdGFydF07XG4gIH1cbiAgZ2V0IGJ5dGVSYW5nZSgpIHtcbiAgICBpZiAoIXRoaXMuX2J5dGVSYW5nZSkge1xuICAgICAgcmV0dXJuIFtdO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fYnl0ZVJhbmdlO1xuICB9XG4gIGdldCBieXRlUmFuZ2VTdGFydE9mZnNldCgpIHtcbiAgICByZXR1cm4gdGhpcy5ieXRlUmFuZ2VbMF07XG4gIH1cbiAgZ2V0IGJ5dGVSYW5nZUVuZE9mZnNldCgpIHtcbiAgICByZXR1cm4gdGhpcy5ieXRlUmFuZ2VbMV07XG4gIH1cbiAgZ2V0IHVybCgpIHtcbiAgICBpZiAoIXRoaXMuX3VybCAmJiB0aGlzLmJhc2V1cmwgJiYgdGhpcy5yZWx1cmwpIHtcbiAgICAgIHRoaXMuX3VybCA9IHVybFRvb2xraXRFeHBvcnRzLmJ1aWxkQWJzb2x1dGVVUkwodGhpcy5iYXNldXJsLCB0aGlzLnJlbHVybCwge1xuICAgICAgICBhbHdheXNOb3JtYWxpemU6IHRydWVcbiAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fdXJsIHx8ICcnO1xuICB9XG4gIHNldCB1cmwodmFsdWUpIHtcbiAgICB0aGlzLl91cmwgPSB2YWx1ZTtcbiAgfVxufVxuXG4vKipcbiAqIE9iamVjdCByZXByZXNlbnRpbmcgcGFyc2VkIGRhdGEgZnJvbSBhbiBITFMgU2VnbWVudC4gRm91bmQgaW4ge0BsaW5rIGhscy5qcyNMZXZlbERldGFpbHMuZnJhZ21lbnRzfS5cbiAqL1xuY2xhc3MgRnJhZ21lbnQgZXh0ZW5kcyBCYXNlU2VnbWVudCB7XG4gIGNvbnN0cnVjdG9yKHR5cGUsIGJhc2V1cmwpIHtcbiAgICBzdXBlcihiYXNldXJsKTtcbiAgICB0aGlzLl9kZWNyeXB0ZGF0YSA9IG51bGw7XG4gICAgdGhpcy5yYXdQcm9ncmFtRGF0ZVRpbWUgPSBudWxsO1xuICAgIHRoaXMucHJvZ3JhbURhdGVUaW1lID0gbnVsbDtcbiAgICB0aGlzLnRhZ0xpc3QgPSBbXTtcbiAgICAvLyBFWFRJTkYgaGFzIHRvIGJlIHByZXNlbnQgZm9yIGEgbTN1OCB0byBiZSBjb25zaWRlcmVkIHZhbGlkXG4gICAgdGhpcy5kdXJhdGlvbiA9IDA7XG4gICAgLy8gc24gbm90YXRlcyB0aGUgc2VxdWVuY2UgbnVtYmVyIGZvciBhIHNlZ21lbnQsIGFuZCBpZiBzZXQgdG8gYSBzdHJpbmcgY2FuIGJlICdpbml0U2VnbWVudCdcbiAgICB0aGlzLnNuID0gMDtcbiAgICAvLyBsZXZlbGtleXMgYXJlIHRoZSBFWFQtWC1LRVkgdGFncyB0aGF0IGFwcGx5IHRvIHRoaXMgc2VnbWVudCBmb3IgZGVjcnlwdGlvblxuICAgIC8vIGNvcmUgZGlmZmVyZW5jZSBmcm9tIHRoZSBwcml2YXRlIGZpZWxkIF9kZWNyeXB0ZGF0YSBpcyB0aGUgbGFjayBvZiB0aGUgaW5pdGlhbGl6ZWQgSVZcbiAgICAvLyBfZGVjcnlwdGRhdGEgd2lsbCBzZXQgdGhlIElWIGZvciB0aGlzIHNlZ21lbnQgYmFzZWQgb24gdGhlIHNlZ21lbnQgbnVtYmVyIGluIHRoZSBmcmFnbWVudFxuICAgIHRoaXMubGV2ZWxrZXlzID0gdm9pZCAwO1xuICAgIC8vIEEgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgZnJhZ21lbnQgdHlwZVxuICAgIHRoaXMudHlwZSA9IHZvaWQgMDtcbiAgICAvLyBBIHJlZmVyZW5jZSB0byB0aGUgbG9hZGVyLiBTZXQgd2hpbGUgdGhlIGZyYWdtZW50IGlzIGxvYWRpbmcsIGFuZCByZW1vdmVkIGFmdGVyd2FyZHMuIFVzZWQgdG8gYWJvcnQgZnJhZ21lbnQgbG9hZGluZ1xuICAgIHRoaXMubG9hZGVyID0gbnVsbDtcbiAgICAvLyBBIHJlZmVyZW5jZSB0byB0aGUga2V5IGxvYWRlci4gU2V0IHdoaWxlIHRoZSBrZXkgaXMgbG9hZGluZywgYW5kIHJlbW92ZWQgYWZ0ZXJ3YXJkcy4gVXNlZCB0byBhYm9ydCBrZXkgbG9hZGluZ1xuICAgIHRoaXMua2V5TG9hZGVyID0gbnVsbDtcbiAgICAvLyBUaGUgbGV2ZWwvdHJhY2sgaW5kZXggdG8gd2hpY2ggdGhlIGZyYWdtZW50IGJlbG9uZ3NcbiAgICB0aGlzLmxldmVsID0gLTE7XG4gICAgLy8gVGhlIGNvbnRpbnVpdHkgY291bnRlciBvZiB0aGUgZnJhZ21lbnRcbiAgICB0aGlzLmNjID0gMDtcbiAgICAvLyBUaGUgc3RhcnRpbmcgUHJlc2VudGF0aW9uIFRpbWUgU3RhbXAgKFBUUykgb2YgdGhlIGZyYWdtZW50LiBTZXQgYWZ0ZXIgdHJhbnNtdXggY29tcGxldGUuXG4gICAgdGhpcy5zdGFydFBUUyA9IHZvaWQgMDtcbiAgICAvLyBUaGUgZW5kaW5nIFByZXNlbnRhdGlvbiBUaW1lIFN0YW1wIChQVFMpIG9mIHRoZSBmcmFnbWVudC4gU2V0IGFmdGVyIHRyYW5zbXV4IGNvbXBsZXRlLlxuICAgIHRoaXMuZW5kUFRTID0gdm9pZCAwO1xuICAgIC8vIFRoZSBzdGFydGluZyBEZWNvZGUgVGltZSBTdGFtcCAoRFRTKSBvZiB0aGUgZnJhZ21lbnQuIFNldCBhZnRlciB0cmFuc211eCBjb21wbGV0ZS5cbiAgICB0aGlzLnN0YXJ0RFRTID0gdm9pZCAwO1xuICAgIC8vIFRoZSBlbmRpbmcgRGVjb2RlIFRpbWUgU3RhbXAgKERUUykgb2YgdGhlIGZyYWdtZW50LiBTZXQgYWZ0ZXIgdHJhbnNtdXggY29tcGxldGUuXG4gICAgdGhpcy5lbmREVFMgPSB2b2lkIDA7XG4gICAgLy8gVGhlIHN0YXJ0IHRpbWUgb2YgdGhlIGZyYWdtZW50LCBhcyBsaXN0ZWQgaW4gdGhlIG1hbmlmZXN0LiBVcGRhdGVkIGFmdGVyIHRyYW5zbXV4IGNvbXBsZXRlLlxuICAgIHRoaXMuc3RhcnQgPSAwO1xuICAgIC8vIFNldCBieSBgdXBkYXRlRnJhZ1BUU0RUU2AgaW4gbGV2ZWwtaGVscGVyXG4gICAgdGhpcy5kZWx0YVBUUyA9IHZvaWQgMDtcbiAgICAvLyBUaGUgbWF4aW11bSBzdGFydGluZyBQcmVzZW50YXRpb24gVGltZSBTdGFtcCAoYXVkaW8vdmlkZW8gUFRTKSBvZiB0aGUgZnJhZ21lbnQuIFNldCBhZnRlciB0cmFuc211eCBjb21wbGV0ZS5cbiAgICB0aGlzLm1heFN0YXJ0UFRTID0gdm9pZCAwO1xuICAgIC8vIFRoZSBtaW5pbXVtIGVuZGluZyBQcmVzZW50YXRpb24gVGltZSBTdGFtcCAoYXVkaW8vdmlkZW8gUFRTKSBvZiB0aGUgZnJhZ21lbnQuIFNldCBhZnRlciB0cmFuc211eCBjb21wbGV0ZS5cbiAgICB0aGlzLm1pbkVuZFBUUyA9IHZvaWQgMDtcbiAgICAvLyBMb2FkL3BhcnNlIHRpbWluZyBpbmZvcm1hdGlvblxuICAgIHRoaXMuc3RhdHMgPSBuZXcgTG9hZFN0YXRzKCk7XG4gICAgLy8gSW5pdCBTZWdtZW50IGJ5dGVzICh1bnNldCBmb3IgbWVkaWEgc2VnbWVudHMpXG4gICAgdGhpcy5kYXRhID0gdm9pZCAwO1xuICAgIC8vIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgdGhlIHNlZ21lbnQgd2FzIGRvd25sb2FkZWQgaW4gb3JkZXIgdG8gdGVzdCBiaXRyYXRlLCBhbmQgd2FzIG5vdCBidWZmZXJlZFxuICAgIHRoaXMuYml0cmF0ZVRlc3QgPSBmYWxzZTtcbiAgICAvLyAjRVhUSU5GICBzZWdtZW50IHRpdGxlXG4gICAgdGhpcy50aXRsZSA9IG51bGw7XG4gICAgLy8gVGhlIE1lZGlhIEluaXRpYWxpemF0aW9uIFNlY3Rpb24gZm9yIHRoaXMgc2VnbWVudFxuICAgIHRoaXMuaW5pdFNlZ21lbnQgPSBudWxsO1xuICAgIC8vIEZyYWdtZW50IGlzIHRoZSBsYXN0IGZyYWdtZW50IGluIHRoZSBtZWRpYSBwbGF5bGlzdFxuICAgIHRoaXMuZW5kTGlzdCA9IHZvaWQgMDtcbiAgICAvLyBGcmFnbWVudCBpcyBtYXJrZWQgYnkgYW4gRVhULVgtR0FQIHRhZyBpbmRpY2F0aW5nIHRoYXQgaXQgZG9lcyBub3QgY29udGFpbiBtZWRpYSBkYXRhIGFuZCBzaG91bGQgbm90IGJlIGxvYWRlZFxuICAgIHRoaXMuZ2FwID0gdm9pZCAwO1xuICAgIC8vIERlcHJlY2F0ZWRcbiAgICB0aGlzLnVybElkID0gMDtcbiAgICB0aGlzLnR5cGUgPSB0eXBlO1xuICB9XG4gIGdldCBkZWNyeXB0ZGF0YSgpIHtcbiAgICBjb25zdCB7XG4gICAgICBsZXZlbGtleXNcbiAgICB9ID0gdGhpcztcbiAgICBpZiAoIWxldmVsa2V5cyAmJiAhdGhpcy5fZGVjcnlwdGRhdGEpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICBpZiAoIXRoaXMuX2RlY3J5cHRkYXRhICYmIHRoaXMubGV2ZWxrZXlzICYmICF0aGlzLmxldmVsa2V5cy5OT05FKSB7XG4gICAgICBjb25zdCBrZXkgPSB0aGlzLmxldmVsa2V5cy5pZGVudGl0eTtcbiAgICAgIGlmIChrZXkpIHtcbiAgICAgICAgdGhpcy5fZGVjcnlwdGRhdGEgPSBrZXkuZ2V0RGVjcnlwdERhdGEodGhpcy5zbik7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCBrZXlGb3JtYXRzID0gT2JqZWN0LmtleXModGhpcy5sZXZlbGtleXMpO1xuICAgICAgICBpZiAoa2V5Rm9ybWF0cy5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgICByZXR1cm4gdGhpcy5fZGVjcnlwdGRhdGEgPSB0aGlzLmxldmVsa2V5c1trZXlGb3JtYXRzWzBdXS5nZXREZWNyeXB0RGF0YSh0aGlzLnNuKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fZGVjcnlwdGRhdGE7XG4gIH1cbiAgZ2V0IGVuZCgpIHtcbiAgICByZXR1cm4gdGhpcy5zdGFydCArIHRoaXMuZHVyYXRpb247XG4gIH1cbiAgZ2V0IGVuZFByb2dyYW1EYXRlVGltZSgpIHtcbiAgICBpZiAodGhpcy5wcm9ncmFtRGF0ZVRpbWUgPT09IG51bGwpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICBpZiAoIWlzRmluaXRlTnVtYmVyKHRoaXMucHJvZ3JhbURhdGVUaW1lKSkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIGNvbnN0IGR1cmF0aW9uID0gIWlzRmluaXRlTnVtYmVyKHRoaXMuZHVyYXRpb24pID8gMCA6IHRoaXMuZHVyYXRpb247XG4gICAgcmV0dXJuIHRoaXMucHJvZ3JhbURhdGVUaW1lICsgZHVyYXRpb24gKiAxMDAwO1xuICB9XG4gIGdldCBlbmNyeXB0ZWQoKSB7XG4gICAgdmFyIF90aGlzJF9kZWNyeXB0ZGF0YTtcbiAgICAvLyBBdCB0aGUgbTN1OC1wYXJzZXIgbGV2ZWwgd2UgbmVlZCB0byBhZGQgc3VwcG9ydCBmb3IgbWFuaWZlc3Qgc2lnbmFsbGVkIGtleWZvcm1hdHNcbiAgICAvLyB3aGVuIHdlIHdhbnQgdGhlIGZyYWdtZW50IHRvIHN0YXJ0IHJlcG9ydGluZyB0aGF0IGl0IGlzIGVuY3J5cHRlZC5cbiAgICAvLyBDdXJyZW50bHksIGtleUZvcm1hdCB3aWxsIG9ubHkgYmUgc2V0IGZvciBpZGVudGl0eSBrZXlzXG4gICAgaWYgKChfdGhpcyRfZGVjcnlwdGRhdGEgPSB0aGlzLl9kZWNyeXB0ZGF0YSkgIT0gbnVsbCAmJiBfdGhpcyRfZGVjcnlwdGRhdGEuZW5jcnlwdGVkKSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9IGVsc2UgaWYgKHRoaXMubGV2ZWxrZXlzKSB7XG4gICAgICBjb25zdCBrZXlGb3JtYXRzID0gT2JqZWN0LmtleXModGhpcy5sZXZlbGtleXMpO1xuICAgICAgY29uc3QgbGVuID0ga2V5Rm9ybWF0cy5sZW5ndGg7XG4gICAgICBpZiAobGVuID4gMSB8fCBsZW4gPT09IDEgJiYgdGhpcy5sZXZlbGtleXNba2V5Rm9ybWF0c1swXV0uZW5jcnlwdGVkKSB7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgc2V0S2V5Rm9ybWF0KGtleUZvcm1hdCkge1xuICAgIGlmICh0aGlzLmxldmVsa2V5cykge1xuICAgICAgY29uc3Qga2V5ID0gdGhpcy5sZXZlbGtleXNba2V5Rm9ybWF0XTtcbiAgICAgIGlmIChrZXkgJiYgIXRoaXMuX2RlY3J5cHRkYXRhKSB7XG4gICAgICAgIHRoaXMuX2RlY3J5cHRkYXRhID0ga2V5LmdldERlY3J5cHREYXRhKHRoaXMuc24pO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBhYm9ydFJlcXVlc3RzKCkge1xuICAgIHZhciBfdGhpcyRsb2FkZXIsIF90aGlzJGtleUxvYWRlcjtcbiAgICAoX3RoaXMkbG9hZGVyID0gdGhpcy5sb2FkZXIpID09IG51bGwgPyB2b2lkIDAgOiBfdGhpcyRsb2FkZXIuYWJvcnQoKTtcbiAgICAoX3RoaXMka2V5TG9hZGVyID0gdGhpcy5rZXlMb2FkZXIpID09IG51bGwgPyB2b2lkIDAgOiBfdGhpcyRrZXlMb2FkZXIuYWJvcnQoKTtcbiAgfVxuICBzZXRFbGVtZW50YXJ5U3RyZWFtSW5mbyh0eXBlLCBzdGFydFBUUywgZW5kUFRTLCBzdGFydERUUywgZW5kRFRTLCBwYXJ0aWFsID0gZmFsc2UpIHtcbiAgICBjb25zdCB7XG4gICAgICBlbGVtZW50YXJ5U3RyZWFtc1xuICAgIH0gPSB0aGlzO1xuICAgIGNvbnN0IGluZm8gPSBlbGVtZW50YXJ5U3RyZWFtc1t0eXBlXTtcbiAgICBpZiAoIWluZm8pIHtcbiAgICAgIGVsZW1lbnRhcnlTdHJlYW1zW3R5cGVdID0ge1xuICAgICAgICBzdGFydFBUUyxcbiAgICAgICAgZW5kUFRTLFxuICAgICAgICBzdGFydERUUyxcbiAgICAgICAgZW5kRFRTLFxuICAgICAgICBwYXJ0aWFsXG4gICAgICB9O1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpbmZvLnN0YXJ0UFRTID0gTWF0aC5taW4oaW5mby5zdGFydFBUUywgc3RhcnRQVFMpO1xuICAgIGluZm8uZW5kUFRTID0gTWF0aC5tYXgoaW5mby5lbmRQVFMsIGVuZFBUUyk7XG4gICAgaW5mby5zdGFydERUUyA9IE1hdGgubWluKGluZm8uc3RhcnREVFMsIHN0YXJ0RFRTKTtcbiAgICBpbmZvLmVuZERUUyA9IE1hdGgubWF4KGluZm8uZW5kRFRTLCBlbmREVFMpO1xuICB9XG4gIGNsZWFyRWxlbWVudGFyeVN0cmVhbUluZm8oKSB7XG4gICAgY29uc3Qge1xuICAgICAgZWxlbWVudGFyeVN0cmVhbXNcbiAgICB9ID0gdGhpcztcbiAgICBlbGVtZW50YXJ5U3RyZWFtc1tFbGVtZW50YXJ5U3RyZWFtVHlwZXMuQVVESU9dID0gbnVsbDtcbiAgICBlbGVtZW50YXJ5U3RyZWFtc1tFbGVtZW50YXJ5U3RyZWFtVHlwZXMuVklERU9dID0gbnVsbDtcbiAgICBlbGVtZW50YXJ5U3RyZWFtc1tFbGVtZW50YXJ5U3RyZWFtVHlwZXMuQVVESU9WSURFT10gPSBudWxsO1xuICB9XG59XG5cbi8qKlxuICogT2JqZWN0IHJlcHJlc2VudGluZyBwYXJzZWQgZGF0YSBmcm9tIGFuIEhMUyBQYXJ0aWFsIFNlZ21lbnQuIEZvdW5kIGluIHtAbGluayBobHMuanMjTGV2ZWxEZXRhaWxzLnBhcnRMaXN0fS5cbiAqL1xuY2xhc3MgUGFydCBleHRlbmRzIEJhc2VTZWdtZW50IHtcbiAgY29uc3RydWN0b3IocGFydEF0dHJzLCBmcmFnLCBiYXNldXJsLCBpbmRleCwgcHJldmlvdXMpIHtcbiAgICBzdXBlcihiYXNldXJsKTtcbiAgICB0aGlzLmZyYWdPZmZzZXQgPSAwO1xuICAgIHRoaXMuZHVyYXRpb24gPSAwO1xuICAgIHRoaXMuZ2FwID0gZmFsc2U7XG4gICAgdGhpcy5pbmRlcGVuZGVudCA9IGZhbHNlO1xuICAgIHRoaXMucmVsdXJsID0gdm9pZCAwO1xuICAgIHRoaXMuZnJhZ21lbnQgPSB2b2lkIDA7XG4gICAgdGhpcy5pbmRleCA9IHZvaWQgMDtcbiAgICB0aGlzLnN0YXRzID0gbmV3IExvYWRTdGF0cygpO1xuICAgIHRoaXMuZHVyYXRpb24gPSBwYXJ0QXR0cnMuZGVjaW1hbEZsb2F0aW5nUG9pbnQoJ0RVUkFUSU9OJyk7XG4gICAgdGhpcy5nYXAgPSBwYXJ0QXR0cnMuYm9vbCgnR0FQJyk7XG4gICAgdGhpcy5pbmRlcGVuZGVudCA9IHBhcnRBdHRycy5ib29sKCdJTkRFUEVOREVOVCcpO1xuICAgIHRoaXMucmVsdXJsID0gcGFydEF0dHJzLmVudW1lcmF0ZWRTdHJpbmcoJ1VSSScpO1xuICAgIHRoaXMuZnJhZ21lbnQgPSBmcmFnO1xuICAgIHRoaXMuaW5kZXggPSBpbmRleDtcbiAgICBjb25zdCBieXRlUmFuZ2UgPSBwYXJ0QXR0cnMuZW51bWVyYXRlZFN0cmluZygnQllURVJBTkdFJyk7XG4gICAgaWYgKGJ5dGVSYW5nZSkge1xuICAgICAgdGhpcy5zZXRCeXRlUmFuZ2UoYnl0ZVJhbmdlLCBwcmV2aW91cyk7XG4gICAgfVxuICAgIGlmIChwcmV2aW91cykge1xuICAgICAgdGhpcy5mcmFnT2Zmc2V0ID0gcHJldmlvdXMuZnJhZ09mZnNldCArIHByZXZpb3VzLmR1cmF0aW9uO1xuICAgIH1cbiAgfVxuICBnZXQgc3RhcnQoKSB7XG4gICAgcmV0dXJuIHRoaXMuZnJhZ21lbnQuc3RhcnQgKyB0aGlzLmZyYWdPZmZzZXQ7XG4gIH1cbiAgZ2V0IGVuZCgpIHtcbiAgICByZXR1cm4gdGhpcy5zdGFydCArIHRoaXMuZHVyYXRpb247XG4gIH1cbiAgZ2V0IGxvYWRlZCgpIHtcbiAgICBjb25zdCB7XG4gICAgICBlbGVtZW50YXJ5U3RyZWFtc1xuICAgIH0gPSB0aGlzO1xuICAgIHJldHVybiAhIShlbGVtZW50YXJ5U3RyZWFtcy5hdWRpbyB8fCBlbGVtZW50YXJ5U3RyZWFtcy52aWRlbyB8fCBlbGVtZW50YXJ5U3RyZWFtcy5hdWRpb3ZpZGVvKTtcbiAgfVxufVxuXG5jb25zdCBERUZBVUxUX1RBUkdFVF9EVVJBVElPTiA9IDEwO1xuXG4vKipcbiAqIE9iamVjdCByZXByZXNlbnRpbmcgcGFyc2VkIGRhdGEgZnJvbSBhbiBITFMgTWVkaWEgUGxheWxpc3QuIEZvdW5kIGluIHtAbGluayBobHMuanMjTGV2ZWwuZGV0YWlsc30uXG4gKi9cbmNsYXNzIExldmVsRGV0YWlscyB7XG4gIGNvbnN0cnVjdG9yKGJhc2VVcmwpIHtcbiAgICB0aGlzLlBUU0tub3duID0gZmFsc2U7XG4gICAgdGhpcy5hbGlnbmVkU2xpZGluZyA9IGZhbHNlO1xuICAgIHRoaXMuYXZlcmFnZXRhcmdldGR1cmF0aW9uID0gdm9pZCAwO1xuICAgIHRoaXMuZW5kQ0MgPSAwO1xuICAgIHRoaXMuZW5kU04gPSAwO1xuICAgIHRoaXMuZnJhZ21lbnRzID0gdm9pZCAwO1xuICAgIHRoaXMuZnJhZ21lbnRIaW50ID0gdm9pZCAwO1xuICAgIHRoaXMucGFydExpc3QgPSBudWxsO1xuICAgIHRoaXMuZGF0ZVJhbmdlcyA9IHZvaWQgMDtcbiAgICB0aGlzLmxpdmUgPSB0cnVlO1xuICAgIHRoaXMuYWdlSGVhZGVyID0gMDtcbiAgICB0aGlzLmFkdmFuY2VkRGF0ZVRpbWUgPSB2b2lkIDA7XG4gICAgdGhpcy51cGRhdGVkID0gdHJ1ZTtcbiAgICB0aGlzLmFkdmFuY2VkID0gdHJ1ZTtcbiAgICB0aGlzLmF2YWlsYWJpbGl0eURlbGF5ID0gdm9pZCAwO1xuICAgIC8vIE1hbmlmZXN0IHJlbG9hZCBzeW5jaHJvbml6YXRpb25cbiAgICB0aGlzLm1pc3NlcyA9IDA7XG4gICAgdGhpcy5zdGFydENDID0gMDtcbiAgICB0aGlzLnN0YXJ0U04gPSAwO1xuICAgIHRoaXMuc3RhcnRUaW1lT2Zmc2V0ID0gbnVsbDtcbiAgICB0aGlzLnRhcmdldGR1cmF0aW9uID0gMDtcbiAgICB0aGlzLnRvdGFsZHVyYXRpb24gPSAwO1xuICAgIHRoaXMudHlwZSA9IG51bGw7XG4gICAgdGhpcy51cmwgPSB2b2lkIDA7XG4gICAgdGhpcy5tM3U4ID0gJyc7XG4gICAgdGhpcy52ZXJzaW9uID0gbnVsbDtcbiAgICB0aGlzLmNhbkJsb2NrUmVsb2FkID0gZmFsc2U7XG4gICAgdGhpcy5jYW5Ta2lwVW50aWwgPSAwO1xuICAgIHRoaXMuY2FuU2tpcERhdGVSYW5nZXMgPSBmYWxzZTtcbiAgICB0aGlzLnNraXBwZWRTZWdtZW50cyA9IDA7XG4gICAgdGhpcy5yZWNlbnRseVJlbW92ZWREYXRlcmFuZ2VzID0gdm9pZCAwO1xuICAgIHRoaXMucGFydEhvbGRCYWNrID0gMDtcbiAgICB0aGlzLmhvbGRCYWNrID0gMDtcbiAgICB0aGlzLnBhcnRUYXJnZXQgPSAwO1xuICAgIHRoaXMucHJlbG9hZEhpbnQgPSB2b2lkIDA7XG4gICAgdGhpcy5yZW5kaXRpb25SZXBvcnRzID0gdm9pZCAwO1xuICAgIHRoaXMudHVuZUluR29hbCA9IDA7XG4gICAgdGhpcy5kZWx0YVVwZGF0ZUZhaWxlZCA9IHZvaWQgMDtcbiAgICB0aGlzLmRyaWZ0U3RhcnRUaW1lID0gMDtcbiAgICB0aGlzLmRyaWZ0RW5kVGltZSA9IDA7XG4gICAgdGhpcy5kcmlmdFN0YXJ0ID0gMDtcbiAgICB0aGlzLmRyaWZ0RW5kID0gMDtcbiAgICB0aGlzLmVuY3J5cHRlZEZyYWdtZW50cyA9IHZvaWQgMDtcbiAgICB0aGlzLnBsYXlsaXN0UGFyc2luZ0Vycm9yID0gbnVsbDtcbiAgICB0aGlzLnZhcmlhYmxlTGlzdCA9IG51bGw7XG4gICAgdGhpcy5oYXNWYXJpYWJsZVJlZnMgPSBmYWxzZTtcbiAgICB0aGlzLmZyYWdtZW50cyA9IFtdO1xuICAgIHRoaXMuZW5jcnlwdGVkRnJhZ21lbnRzID0gW107XG4gICAgdGhpcy5kYXRlUmFuZ2VzID0ge307XG4gICAgdGhpcy51cmwgPSBiYXNlVXJsO1xuICB9XG4gIHJlbG9hZGVkKHByZXZpb3VzKSB7XG4gICAgaWYgKCFwcmV2aW91cykge1xuICAgICAgdGhpcy5hZHZhbmNlZCA9IHRydWU7XG4gICAgICB0aGlzLnVwZGF0ZWQgPSB0cnVlO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBwYXJ0U25EaWZmID0gdGhpcy5sYXN0UGFydFNuIC0gcHJldmlvdXMubGFzdFBhcnRTbjtcbiAgICBjb25zdCBwYXJ0SW5kZXhEaWZmID0gdGhpcy5sYXN0UGFydEluZGV4IC0gcHJldmlvdXMubGFzdFBhcnRJbmRleDtcbiAgICB0aGlzLnVwZGF0ZWQgPSB0aGlzLmVuZFNOICE9PSBwcmV2aW91cy5lbmRTTiB8fCAhIXBhcnRJbmRleERpZmYgfHwgISFwYXJ0U25EaWZmIHx8ICF0aGlzLmxpdmU7XG4gICAgdGhpcy5hZHZhbmNlZCA9IHRoaXMuZW5kU04gPiBwcmV2aW91cy5lbmRTTiB8fCBwYXJ0U25EaWZmID4gMCB8fCBwYXJ0U25EaWZmID09PSAwICYmIHBhcnRJbmRleERpZmYgPiAwO1xuICAgIGlmICh0aGlzLnVwZGF0ZWQgfHwgdGhpcy5hZHZhbmNlZCkge1xuICAgICAgdGhpcy5taXNzZXMgPSBNYXRoLmZsb29yKHByZXZpb3VzLm1pc3NlcyAqIDAuNik7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMubWlzc2VzID0gcHJldmlvdXMubWlzc2VzICsgMTtcbiAgICB9XG4gICAgdGhpcy5hdmFpbGFiaWxpdHlEZWxheSA9IHByZXZpb3VzLmF2YWlsYWJpbGl0eURlbGF5O1xuICB9XG4gIGdldCBoYXNQcm9ncmFtRGF0ZVRpbWUoKSB7XG4gICAgaWYgKHRoaXMuZnJhZ21lbnRzLmxlbmd0aCkge1xuICAgICAgcmV0dXJuIGlzRmluaXRlTnVtYmVyKHRoaXMuZnJhZ21lbnRzW3RoaXMuZnJhZ21lbnRzLmxlbmd0aCAtIDFdLnByb2dyYW1EYXRlVGltZSk7XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICBnZXQgbGV2ZWxUYXJnZXREdXJhdGlvbigpIHtcbiAgICByZXR1cm4gdGhpcy5hdmVyYWdldGFyZ2V0ZHVyYXRpb24gfHwgdGhpcy50YXJnZXRkdXJhdGlvbiB8fCBERUZBVUxUX1RBUkdFVF9EVVJBVElPTjtcbiAgfVxuICBnZXQgZHJpZnQoKSB7XG4gICAgY29uc3QgcnVuVGltZSA9IHRoaXMuZHJpZnRFbmRUaW1lIC0gdGhpcy5kcmlmdFN0YXJ0VGltZTtcbiAgICBpZiAocnVuVGltZSA+IDApIHtcbiAgICAgIGNvbnN0IHJ1bkR1cmF0aW9uID0gdGhpcy5kcmlmdEVuZCAtIHRoaXMuZHJpZnRTdGFydDtcbiAgICAgIHJldHVybiBydW5EdXJhdGlvbiAqIDEwMDAgLyBydW5UaW1lO1xuICAgIH1cbiAgICByZXR1cm4gMTtcbiAgfVxuICBnZXQgZWRnZSgpIHtcbiAgICByZXR1cm4gdGhpcy5wYXJ0RW5kIHx8IHRoaXMuZnJhZ21lbnRFbmQ7XG4gIH1cbiAgZ2V0IHBhcnRFbmQoKSB7XG4gICAgdmFyIF90aGlzJHBhcnRMaXN0O1xuICAgIGlmICgoX3RoaXMkcGFydExpc3QgPSB0aGlzLnBhcnRMaXN0KSAhPSBudWxsICYmIF90aGlzJHBhcnRMaXN0Lmxlbmd0aCkge1xuICAgICAgcmV0dXJuIHRoaXMucGFydExpc3RbdGhpcy5wYXJ0TGlzdC5sZW5ndGggLSAxXS5lbmQ7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmZyYWdtZW50RW5kO1xuICB9XG4gIGdldCBmcmFnbWVudEVuZCgpIHtcbiAgICB2YXIgX3RoaXMkZnJhZ21lbnRzO1xuICAgIGlmICgoX3RoaXMkZnJhZ21lbnRzID0gdGhpcy5mcmFnbWVudHMpICE9IG51bGwgJiYgX3RoaXMkZnJhZ21lbnRzLmxlbmd0aCkge1xuICAgICAgcmV0dXJuIHRoaXMuZnJhZ21lbnRzW3RoaXMuZnJhZ21lbnRzLmxlbmd0aCAtIDFdLmVuZDtcbiAgICB9XG4gICAgcmV0dXJuIDA7XG4gIH1cbiAgZ2V0IGFnZSgpIHtcbiAgICBpZiAodGhpcy5hZHZhbmNlZERhdGVUaW1lKSB7XG4gICAgICByZXR1cm4gTWF0aC5tYXgoRGF0ZS5ub3coKSAtIHRoaXMuYWR2YW5jZWREYXRlVGltZSwgMCkgLyAxMDAwO1xuICAgIH1cbiAgICByZXR1cm4gMDtcbiAgfVxuICBnZXQgbGFzdFBhcnRJbmRleCgpIHtcbiAgICB2YXIgX3RoaXMkcGFydExpc3QyO1xuICAgIGlmICgoX3RoaXMkcGFydExpc3QyID0gdGhpcy5wYXJ0TGlzdCkgIT0gbnVsbCAmJiBfdGhpcyRwYXJ0TGlzdDIubGVuZ3RoKSB7XG4gICAgICByZXR1cm4gdGhpcy5wYXJ0TGlzdFt0aGlzLnBhcnRMaXN0Lmxlbmd0aCAtIDFdLmluZGV4O1xuICAgIH1cbiAgICByZXR1cm4gLTE7XG4gIH1cbiAgZ2V0IGxhc3RQYXJ0U24oKSB7XG4gICAgdmFyIF90aGlzJHBhcnRMaXN0MztcbiAgICBpZiAoKF90aGlzJHBhcnRMaXN0MyA9IHRoaXMucGFydExpc3QpICE9IG51bGwgJiYgX3RoaXMkcGFydExpc3QzLmxlbmd0aCkge1xuICAgICAgcmV0dXJuIHRoaXMucGFydExpc3RbdGhpcy5wYXJ0TGlzdC5sZW5ndGggLSAxXS5mcmFnbWVudC5zbjtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZW5kU047XG4gIH1cbn1cblxuZnVuY3Rpb24gYmFzZTY0RGVjb2RlKGJhc2U2NGVuY29kZWRTdHIpIHtcbiAgcmV0dXJuIFVpbnQ4QXJyYXkuZnJvbShhdG9iKGJhc2U2NGVuY29kZWRTdHIpLCBjID0+IGMuY2hhckNvZGVBdCgwKSk7XG59XG5cbmZ1bmN0aW9uIGdldEtleUlkQnl0ZXMoc3RyKSB7XG4gIGNvbnN0IGtleUlkYnl0ZXMgPSBzdHJUb1V0ZjhhcnJheShzdHIpLnN1YmFycmF5KDAsIDE2KTtcbiAgY29uc3QgcGFkZGVka2V5SWRieXRlcyA9IG5ldyBVaW50OEFycmF5KDE2KTtcbiAgcGFkZGVka2V5SWRieXRlcy5zZXQoa2V5SWRieXRlcywgMTYgLSBrZXlJZGJ5dGVzLmxlbmd0aCk7XG4gIHJldHVybiBwYWRkZWRrZXlJZGJ5dGVzO1xufVxuZnVuY3Rpb24gY2hhbmdlRW5kaWFubmVzcyhrZXlJZCkge1xuICBjb25zdCBzd2FwID0gZnVuY3Rpb24gc3dhcChhcnJheSwgZnJvbSwgdG8pIHtcbiAgICBjb25zdCBjdXIgPSBhcnJheVtmcm9tXTtcbiAgICBhcnJheVtmcm9tXSA9IGFycmF5W3RvXTtcbiAgICBhcnJheVt0b10gPSBjdXI7XG4gIH07XG4gIHN3YXAoa2V5SWQsIDAsIDMpO1xuICBzd2FwKGtleUlkLCAxLCAyKTtcbiAgc3dhcChrZXlJZCwgNCwgNSk7XG4gIHN3YXAoa2V5SWQsIDYsIDcpO1xufVxuZnVuY3Rpb24gY29udmVydERhdGFVcmlUb0FycmF5Qnl0ZXModXJpKSB7XG4gIC8vIGRhdGE6WzxtZWRpYSB0eXBlXVs7YXR0cmlidXRlPXZhbHVlXVs7YmFzZTY0XSw8ZGF0YT5cbiAgY29uc3QgY29sb25zcGxpdCA9IHVyaS5zcGxpdCgnOicpO1xuICBsZXQga2V5ZGF0YSA9IG51bGw7XG4gIGlmIChjb2xvbnNwbGl0WzBdID09PSAnZGF0YScgJiYgY29sb25zcGxpdC5sZW5ndGggPT09IDIpIHtcbiAgICBjb25zdCBzZW1pY29sb25zcGxpdCA9IGNvbG9uc3BsaXRbMV0uc3BsaXQoJzsnKTtcbiAgICBjb25zdCBjb21tYXNwbGl0ID0gc2VtaWNvbG9uc3BsaXRbc2VtaWNvbG9uc3BsaXQubGVuZ3RoIC0gMV0uc3BsaXQoJywnKTtcbiAgICBpZiAoY29tbWFzcGxpdC5sZW5ndGggPT09IDIpIHtcbiAgICAgIGNvbnN0IGlzYmFzZTY0ID0gY29tbWFzcGxpdFswXSA9PT0gJ2Jhc2U2NCc7XG4gICAgICBjb25zdCBkYXRhID0gY29tbWFzcGxpdFsxXTtcbiAgICAgIGlmIChpc2Jhc2U2NCkge1xuICAgICAgICBzZW1pY29sb25zcGxpdC5zcGxpY2UoLTEsIDEpOyAvLyByZW1vdmUgZnJvbSBwcm9jZXNzaW5nXG4gICAgICAgIGtleWRhdGEgPSBiYXNlNjREZWNvZGUoZGF0YSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBrZXlkYXRhID0gZ2V0S2V5SWRCeXRlcyhkYXRhKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgcmV0dXJuIGtleWRhdGE7XG59XG5mdW5jdGlvbiBzdHJUb1V0ZjhhcnJheShzdHIpIHtcbiAgcmV0dXJuIFVpbnQ4QXJyYXkuZnJvbSh1bmVzY2FwZShlbmNvZGVVUklDb21wb25lbnQoc3RyKSksIGMgPT4gYy5jaGFyQ29kZUF0KDApKTtcbn1cblxuLyoqIHJldHVybnMgYHVuZGVmaW5lZGAgaXMgYHNlbGZgIGlzIG1pc3NpbmcsIGUuZy4gaW4gbm9kZSAqL1xuY29uc3Qgb3B0aW9uYWxTZWxmID0gdHlwZW9mIHNlbGYgIT09ICd1bmRlZmluZWQnID8gc2VsZiA6IHVuZGVmaW5lZDtcblxuLyoqXG4gKiBAc2VlIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9OYXZpZ2F0b3IvcmVxdWVzdE1lZGlhS2V5U3lzdGVtQWNjZXNzXG4gKi9cbnZhciBLZXlTeXN0ZW1zID0ge1xuICBDTEVBUktFWTogXCJvcmcudzMuY2xlYXJrZXlcIixcbiAgRkFJUlBMQVk6IFwiY29tLmFwcGxlLmZwc1wiLFxuICBQTEFZUkVBRFk6IFwiY29tLm1pY3Jvc29mdC5wbGF5cmVhZHlcIixcbiAgV0lERVZJTkU6IFwiY29tLndpZGV2aW5lLmFscGhhXCJcbn07XG5cbi8vIFBsYXlsaXN0ICNFWFQtWC1LRVkgS0VZRk9STUFUIHZhbHVlc1xudmFyIEtleVN5c3RlbUZvcm1hdHMgPSB7XG4gIENMRUFSS0VZOiBcIm9yZy53My5jbGVhcmtleVwiLFxuICBGQUlSUExBWTogXCJjb20uYXBwbGUuc3RyZWFtaW5na2V5ZGVsaXZlcnlcIixcbiAgUExBWVJFQURZOiBcImNvbS5taWNyb3NvZnQucGxheXJlYWR5XCIsXG4gIFdJREVWSU5FOiBcInVybjp1dWlkOmVkZWY4YmE5LTc5ZDYtNGFjZS1hM2M4LTI3ZGNkNTFkMjFlZFwiXG59O1xuZnVuY3Rpb24ga2V5U3lzdGVtRm9ybWF0VG9LZXlTeXN0ZW1Eb21haW4oZm9ybWF0KSB7XG4gIHN3aXRjaCAoZm9ybWF0KSB7XG4gICAgY2FzZSBLZXlTeXN0ZW1Gb3JtYXRzLkZBSVJQTEFZOlxuICAgICAgcmV0dXJuIEtleVN5c3RlbXMuRkFJUlBMQVk7XG4gICAgY2FzZSBLZXlTeXN0ZW1Gb3JtYXRzLlBMQVlSRUFEWTpcbiAgICAgIHJldHVybiBLZXlTeXN0ZW1zLlBMQVlSRUFEWTtcbiAgICBjYXNlIEtleVN5c3RlbUZvcm1hdHMuV0lERVZJTkU6XG4gICAgICByZXR1cm4gS2V5U3lzdGVtcy5XSURFVklORTtcbiAgICBjYXNlIEtleVN5c3RlbUZvcm1hdHMuQ0xFQVJLRVk6XG4gICAgICByZXR1cm4gS2V5U3lzdGVtcy5DTEVBUktFWTtcbiAgfVxufVxuXG4vLyBTeXN0ZW0gSURzIGZvciB3aGljaCB3ZSBjYW4gZXh0cmFjdCBhIGtleSBJRCBmcm9tIFwiZW5jcnlwdGVkXCIgZXZlbnQgUFNTSFxudmFyIEtleVN5c3RlbUlkcyA9IHtcbiAgQ0VOQzogXCIxMDc3ZWZlY2MwYjI0ZDAyYWNlMzNjMWU1MmUyZmI0YlwiLFxuICBDTEVBUktFWTogXCJlMjcxOWQ1OGE5ODViM2M5NzgxYWIwMzBhZjc4ZDMwZVwiLFxuICBGQUlSUExBWTogXCI5NGNlODZmYjA3ZmY0ZjQzYWRiODkzZDJmYTk2OGNhMlwiLFxuICBQTEFZUkVBRFk6IFwiOWEwNGYwNzk5ODQwNDI4NmFiOTJlNjViZTA4ODVmOTVcIixcbiAgV0lERVZJTkU6IFwiZWRlZjhiYTk3OWQ2NGFjZWEzYzgyN2RjZDUxZDIxZWRcIlxufTtcbmZ1bmN0aW9uIGtleVN5c3RlbUlkVG9LZXlTeXN0ZW1Eb21haW4oc3lzdGVtSWQpIHtcbiAgaWYgKHN5c3RlbUlkID09PSBLZXlTeXN0ZW1JZHMuV0lERVZJTkUpIHtcbiAgICByZXR1cm4gS2V5U3lzdGVtcy5XSURFVklORTtcbiAgfSBlbHNlIGlmIChzeXN0ZW1JZCA9PT0gS2V5U3lzdGVtSWRzLlBMQVlSRUFEWSkge1xuICAgIHJldHVybiBLZXlTeXN0ZW1zLlBMQVlSRUFEWTtcbiAgfSBlbHNlIGlmIChzeXN0ZW1JZCA9PT0gS2V5U3lzdGVtSWRzLkNFTkMgfHwgc3lzdGVtSWQgPT09IEtleVN5c3RlbUlkcy5DTEVBUktFWSkge1xuICAgIHJldHVybiBLZXlTeXN0ZW1zLkNMRUFSS0VZO1xuICB9XG59XG5mdW5jdGlvbiBrZXlTeXN0ZW1Eb21haW5Ub0tleVN5c3RlbUZvcm1hdChrZXlTeXN0ZW0pIHtcbiAgc3dpdGNoIChrZXlTeXN0ZW0pIHtcbiAgICBjYXNlIEtleVN5c3RlbXMuRkFJUlBMQVk6XG4gICAgICByZXR1cm4gS2V5U3lzdGVtRm9ybWF0cy5GQUlSUExBWTtcbiAgICBjYXNlIEtleVN5c3RlbXMuUExBWVJFQURZOlxuICAgICAgcmV0dXJuIEtleVN5c3RlbUZvcm1hdHMuUExBWVJFQURZO1xuICAgIGNhc2UgS2V5U3lzdGVtcy5XSURFVklORTpcbiAgICAgIHJldHVybiBLZXlTeXN0ZW1Gb3JtYXRzLldJREVWSU5FO1xuICAgIGNhc2UgS2V5U3lzdGVtcy5DTEVBUktFWTpcbiAgICAgIHJldHVybiBLZXlTeXN0ZW1Gb3JtYXRzLkNMRUFSS0VZO1xuICB9XG59XG5mdW5jdGlvbiBnZXRLZXlTeXN0ZW1zRm9yQ29uZmlnKGNvbmZpZykge1xuICBjb25zdCB7XG4gICAgZHJtU3lzdGVtcyxcbiAgICB3aWRldmluZUxpY2Vuc2VVcmxcbiAgfSA9IGNvbmZpZztcbiAgY29uc3Qga2V5U3lzdGVtc1RvQXR0ZW1wdCA9IGRybVN5c3RlbXMgPyBbS2V5U3lzdGVtcy5GQUlSUExBWSwgS2V5U3lzdGVtcy5XSURFVklORSwgS2V5U3lzdGVtcy5QTEFZUkVBRFksIEtleVN5c3RlbXMuQ0xFQVJLRVldLmZpbHRlcihrZXlTeXN0ZW0gPT4gISFkcm1TeXN0ZW1zW2tleVN5c3RlbV0pIDogW107XG4gIGlmICgha2V5U3lzdGVtc1RvQXR0ZW1wdFtLZXlTeXN0ZW1zLldJREVWSU5FXSAmJiB3aWRldmluZUxpY2Vuc2VVcmwpIHtcbiAgICBrZXlTeXN0ZW1zVG9BdHRlbXB0LnB1c2goS2V5U3lzdGVtcy5XSURFVklORSk7XG4gIH1cbiAgcmV0dXJuIGtleVN5c3RlbXNUb0F0dGVtcHQ7XG59XG5jb25zdCByZXF1ZXN0TWVkaWFLZXlTeXN0ZW1BY2Nlc3MgPSBmdW5jdGlvbiAoX29wdGlvbmFsU2VsZiRuYXZpZ2F0KSB7XG4gIGlmIChvcHRpb25hbFNlbGYgIT0gbnVsbCAmJiAoX29wdGlvbmFsU2VsZiRuYXZpZ2F0ID0gb3B0aW9uYWxTZWxmLm5hdmlnYXRvcikgIT0gbnVsbCAmJiBfb3B0aW9uYWxTZWxmJG5hdmlnYXQucmVxdWVzdE1lZGlhS2V5U3lzdGVtQWNjZXNzKSB7XG4gICAgcmV0dXJuIHNlbGYubmF2aWdhdG9yLnJlcXVlc3RNZWRpYUtleVN5c3RlbUFjY2Vzcy5iaW5kKHNlbGYubmF2aWdhdG9yKTtcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxufSgpO1xuXG4vKipcbiAqIEBzZWUgaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQVBJL01lZGlhS2V5U3lzdGVtQ29uZmlndXJhdGlvblxuICovXG5mdW5jdGlvbiBnZXRTdXBwb3J0ZWRNZWRpYUtleVN5c3RlbUNvbmZpZ3VyYXRpb25zKGtleVN5c3RlbSwgYXVkaW9Db2RlY3MsIHZpZGVvQ29kZWNzLCBkcm1TeXN0ZW1PcHRpb25zKSB7XG4gIGxldCBpbml0RGF0YVR5cGVzO1xuICBzd2l0Y2ggKGtleVN5c3RlbSkge1xuICAgIGNhc2UgS2V5U3lzdGVtcy5GQUlSUExBWTpcbiAgICAgIGluaXREYXRhVHlwZXMgPSBbJ2NlbmMnLCAnc2luZiddO1xuICAgICAgYnJlYWs7XG4gICAgY2FzZSBLZXlTeXN0ZW1zLldJREVWSU5FOlxuICAgIGNhc2UgS2V5U3lzdGVtcy5QTEFZUkVBRFk6XG4gICAgICBpbml0RGF0YVR5cGVzID0gWydjZW5jJ107XG4gICAgICBicmVhaztcbiAgICBjYXNlIEtleVN5c3RlbXMuQ0xFQVJLRVk6XG4gICAgICBpbml0RGF0YVR5cGVzID0gWydjZW5jJywgJ2tleWlkcyddO1xuICAgICAgYnJlYWs7XG4gICAgZGVmYXVsdDpcbiAgICAgIHRocm93IG5ldyBFcnJvcihgVW5rbm93biBrZXktc3lzdGVtOiAke2tleVN5c3RlbX1gKTtcbiAgfVxuICByZXR1cm4gY3JlYXRlTWVkaWFLZXlTeXN0ZW1Db25maWd1cmF0aW9ucyhpbml0RGF0YVR5cGVzLCBhdWRpb0NvZGVjcywgdmlkZW9Db2RlY3MsIGRybVN5c3RlbU9wdGlvbnMpO1xufVxuZnVuY3Rpb24gY3JlYXRlTWVkaWFLZXlTeXN0ZW1Db25maWd1cmF0aW9ucyhpbml0RGF0YVR5cGVzLCBhdWRpb0NvZGVjcywgdmlkZW9Db2RlY3MsIGRybVN5c3RlbU9wdGlvbnMpIHtcbiAgY29uc3QgYmFzZUNvbmZpZyA9IHtcbiAgICBpbml0RGF0YVR5cGVzOiBpbml0RGF0YVR5cGVzLFxuICAgIHBlcnNpc3RlbnRTdGF0ZTogZHJtU3lzdGVtT3B0aW9ucy5wZXJzaXN0ZW50U3RhdGUgfHwgJ29wdGlvbmFsJyxcbiAgICBkaXN0aW5jdGl2ZUlkZW50aWZpZXI6IGRybVN5c3RlbU9wdGlvbnMuZGlzdGluY3RpdmVJZGVudGlmaWVyIHx8ICdvcHRpb25hbCcsXG4gICAgc2Vzc2lvblR5cGVzOiBkcm1TeXN0ZW1PcHRpb25zLnNlc3Npb25UeXBlcyB8fCBbZHJtU3lzdGVtT3B0aW9ucy5zZXNzaW9uVHlwZSB8fCAndGVtcG9yYXJ5J10sXG4gICAgYXVkaW9DYXBhYmlsaXRpZXM6IGF1ZGlvQ29kZWNzLm1hcChjb2RlYyA9PiAoe1xuICAgICAgY29udGVudFR5cGU6IGBhdWRpby9tcDQ7IGNvZGVjcz1cIiR7Y29kZWN9XCJgLFxuICAgICAgcm9idXN0bmVzczogZHJtU3lzdGVtT3B0aW9ucy5hdWRpb1JvYnVzdG5lc3MgfHwgJycsXG4gICAgICBlbmNyeXB0aW9uU2NoZW1lOiBkcm1TeXN0ZW1PcHRpb25zLmF1ZGlvRW5jcnlwdGlvblNjaGVtZSB8fCBudWxsXG4gICAgfSkpLFxuICAgIHZpZGVvQ2FwYWJpbGl0aWVzOiB2aWRlb0NvZGVjcy5tYXAoY29kZWMgPT4gKHtcbiAgICAgIGNvbnRlbnRUeXBlOiBgdmlkZW8vbXA0OyBjb2RlY3M9XCIke2NvZGVjfVwiYCxcbiAgICAgIHJvYnVzdG5lc3M6IGRybVN5c3RlbU9wdGlvbnMudmlkZW9Sb2J1c3RuZXNzIHx8ICcnLFxuICAgICAgZW5jcnlwdGlvblNjaGVtZTogZHJtU3lzdGVtT3B0aW9ucy52aWRlb0VuY3J5cHRpb25TY2hlbWUgfHwgbnVsbFxuICAgIH0pKVxuICB9O1xuICByZXR1cm4gW2Jhc2VDb25maWddO1xufVxuXG5mdW5jdGlvbiBzbGljZVVpbnQ4KGFycmF5LCBzdGFydCwgZW5kKSB7XG4gIC8vIEB0cy1leHBlY3QtZXJyb3IgVGhpcyBwb2x5ZmlsbHMgSUUxMSB1c2FnZSBvZiBVaW50OEFycmF5IHNsaWNlLlxuICAvLyBJdCBhbHdheXMgZXhpc3RzIGluIHRoZSBUeXBlU2NyaXB0IGRlZmluaXRpb24gc28gZmFpbHMsIGJ1dCBpdCBmYWlscyBhdCBydW50aW1lIG9uIElFMTEuXG4gIHJldHVybiBVaW50OEFycmF5LnByb3RvdHlwZS5zbGljZSA/IGFycmF5LnNsaWNlKHN0YXJ0LCBlbmQpIDogbmV3IFVpbnQ4QXJyYXkoQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJyYXksIHN0YXJ0LCBlbmQpKTtcbn1cblxuLy8gYnJlYWtpbmcgdXAgdGhvc2UgdHdvIHR5cGVzIGluIG9yZGVyIHRvIGNsYXJpZnkgd2hhdCBpcyBoYXBwZW5pbmcgaW4gdGhlIGRlY29kaW5nIHBhdGguXG5cbi8qKlxuICogUmV0dXJucyB0cnVlIGlmIGFuIElEMyBoZWFkZXIgY2FuIGJlIGZvdW5kIGF0IG9mZnNldCBpbiBkYXRhXG4gKiBAcGFyYW0gZGF0YSAtIFRoZSBkYXRhIHRvIHNlYXJjaFxuICogQHBhcmFtIG9mZnNldCAtIFRoZSBvZmZzZXQgYXQgd2hpY2ggdG8gc3RhcnQgc2VhcmNoaW5nXG4gKi9cbmNvbnN0IGlzSGVhZGVyJDIgPSAoZGF0YSwgb2Zmc2V0KSA9PiB7XG4gIC8qXG4gICAqIGh0dHA6Ly9pZDMub3JnL2lkM3YyLjMuMFxuICAgKiBbMF0gICAgID0gJ0knXG4gICAqIFsxXSAgICAgPSAnRCdcbiAgICogWzJdICAgICA9ICczJ1xuICAgKiBbMyw0XSAgID0ge1ZlcnNpb259XG4gICAqIFs1XSAgICAgPSB7RmxhZ3N9XG4gICAqIFs2LTldICAgPSB7SUQzIFNpemV9XG4gICAqXG4gICAqIEFuIElEM3YyIHRhZyBjYW4gYmUgZGV0ZWN0ZWQgd2l0aCB0aGUgZm9sbG93aW5nIHBhdHRlcm46XG4gICAqICAkNDkgNDQgMzMgeXkgeXkgeHggenogenogenogenpcbiAgICogV2hlcmUgeXkgaXMgbGVzcyB0aGFuICRGRiwgeHggaXMgdGhlICdmbGFncycgYnl0ZSBhbmQgenogaXMgbGVzcyB0aGFuICQ4MFxuICAgKi9cbiAgaWYgKG9mZnNldCArIDEwIDw9IGRhdGEubGVuZ3RoKSB7XG4gICAgLy8gbG9vayBmb3IgJ0lEMycgaWRlbnRpZmllclxuICAgIGlmIChkYXRhW29mZnNldF0gPT09IDB4NDkgJiYgZGF0YVtvZmZzZXQgKyAxXSA9PT0gMHg0NCAmJiBkYXRhW29mZnNldCArIDJdID09PSAweDMzKSB7XG4gICAgICAvLyBjaGVjayB2ZXJzaW9uIGlzIHdpdGhpbiByYW5nZVxuICAgICAgaWYgKGRhdGFbb2Zmc2V0ICsgM10gPCAweGZmICYmIGRhdGFbb2Zmc2V0ICsgNF0gPCAweGZmKSB7XG4gICAgICAgIC8vIGNoZWNrIHNpemUgaXMgd2l0aGluIHJhbmdlXG4gICAgICAgIGlmIChkYXRhW29mZnNldCArIDZdIDwgMHg4MCAmJiBkYXRhW29mZnNldCArIDddIDwgMHg4MCAmJiBkYXRhW29mZnNldCArIDhdIDwgMHg4MCAmJiBkYXRhW29mZnNldCArIDldIDwgMHg4MCkge1xuICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG4gIHJldHVybiBmYWxzZTtcbn07XG5cbi8qKlxuICogUmV0dXJucyB0cnVlIGlmIGFuIElEMyBmb290ZXIgY2FuIGJlIGZvdW5kIGF0IG9mZnNldCBpbiBkYXRhXG4gKiBAcGFyYW0gZGF0YSAtIFRoZSBkYXRhIHRvIHNlYXJjaFxuICogQHBhcmFtIG9mZnNldCAtIFRoZSBvZmZzZXQgYXQgd2hpY2ggdG8gc3RhcnQgc2VhcmNoaW5nXG4gKi9cbmNvbnN0IGlzRm9vdGVyID0gKGRhdGEsIG9mZnNldCkgPT4ge1xuICAvKlxuICAgKiBUaGUgZm9vdGVyIGlzIGEgY29weSBvZiB0aGUgaGVhZGVyLCBidXQgd2l0aCBhIGRpZmZlcmVudCBpZGVudGlmaWVyXG4gICAqL1xuICBpZiAob2Zmc2V0ICsgMTAgPD0gZGF0YS5sZW5ndGgpIHtcbiAgICAvLyBsb29rIGZvciAnM0RJJyBpZGVudGlmaWVyXG4gICAgaWYgKGRhdGFbb2Zmc2V0XSA9PT0gMHgzMyAmJiBkYXRhW29mZnNldCArIDFdID09PSAweDQ0ICYmIGRhdGFbb2Zmc2V0ICsgMl0gPT09IDB4NDkpIHtcbiAgICAgIC8vIGNoZWNrIHZlcnNpb24gaXMgd2l0aGluIHJhbmdlXG4gICAgICBpZiAoZGF0YVtvZmZzZXQgKyAzXSA8IDB4ZmYgJiYgZGF0YVtvZmZzZXQgKyA0XSA8IDB4ZmYpIHtcbiAgICAgICAgLy8gY2hlY2sgc2l6ZSBpcyB3aXRoaW4gcmFuZ2VcbiAgICAgICAgaWYgKGRhdGFbb2Zmc2V0ICsgNl0gPCAweDgwICYmIGRhdGFbb2Zmc2V0ICsgN10gPCAweDgwICYmIGRhdGFbb2Zmc2V0ICsgOF0gPCAweDgwICYmIGRhdGFbb2Zmc2V0ICsgOV0gPCAweDgwKSB7XG4gICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgcmV0dXJuIGZhbHNlO1xufTtcblxuLyoqXG4gKiBSZXR1cm5zIGFueSBhZGphY2VudCBJRDMgdGFncyBmb3VuZCBpbiBkYXRhIHN0YXJ0aW5nIGF0IG9mZnNldCwgYXMgb25lIGJsb2NrIG9mIGRhdGFcbiAqIEBwYXJhbSBkYXRhIC0gVGhlIGRhdGEgdG8gc2VhcmNoIGluXG4gKiBAcGFyYW0gb2Zmc2V0IC0gVGhlIG9mZnNldCBhdCB3aGljaCB0byBzdGFydCBzZWFyY2hpbmdcbiAqIEByZXR1cm5zIHRoZSBibG9jayBvZiBkYXRhIGNvbnRhaW5pbmcgYW55IElEMyB0YWdzIGZvdW5kXG4gKiBvciAqdW5kZWZpbmVkKiBpZiBubyBoZWFkZXIgaXMgZm91bmQgYXQgdGhlIHN0YXJ0aW5nIG9mZnNldFxuICovXG5jb25zdCBnZXRJRDNEYXRhID0gKGRhdGEsIG9mZnNldCkgPT4ge1xuICBjb25zdCBmcm9udCA9IG9mZnNldDtcbiAgbGV0IGxlbmd0aCA9IDA7XG4gIHdoaWxlIChpc0hlYWRlciQyKGRhdGEsIG9mZnNldCkpIHtcbiAgICAvLyBJRDMgaGVhZGVyIGlzIDEwIGJ5dGVzXG4gICAgbGVuZ3RoICs9IDEwO1xuICAgIGNvbnN0IHNpemUgPSByZWFkU2l6ZShkYXRhLCBvZmZzZXQgKyA2KTtcbiAgICBsZW5ndGggKz0gc2l6ZTtcbiAgICBpZiAoaXNGb290ZXIoZGF0YSwgb2Zmc2V0ICsgMTApKSB7XG4gICAgICAvLyBJRDMgZm9vdGVyIGlzIDEwIGJ5dGVzXG4gICAgICBsZW5ndGggKz0gMTA7XG4gICAgfVxuICAgIG9mZnNldCArPSBsZW5ndGg7XG4gIH1cbiAgaWYgKGxlbmd0aCA+IDApIHtcbiAgICByZXR1cm4gZGF0YS5zdWJhcnJheShmcm9udCwgZnJvbnQgKyBsZW5ndGgpO1xuICB9XG4gIHJldHVybiB1bmRlZmluZWQ7XG59O1xuY29uc3QgcmVhZFNpemUgPSAoZGF0YSwgb2Zmc2V0KSA9PiB7XG4gIGxldCBzaXplID0gMDtcbiAgc2l6ZSA9IChkYXRhW29mZnNldF0gJiAweDdmKSA8PCAyMTtcbiAgc2l6ZSB8PSAoZGF0YVtvZmZzZXQgKyAxXSAmIDB4N2YpIDw8IDE0O1xuICBzaXplIHw9IChkYXRhW29mZnNldCArIDJdICYgMHg3ZikgPDwgNztcbiAgc2l6ZSB8PSBkYXRhW29mZnNldCArIDNdICYgMHg3ZjtcbiAgcmV0dXJuIHNpemU7XG59O1xuY29uc3QgY2FuUGFyc2UkMiA9IChkYXRhLCBvZmZzZXQpID0+IHtcbiAgcmV0dXJuIGlzSGVhZGVyJDIoZGF0YSwgb2Zmc2V0KSAmJiByZWFkU2l6ZShkYXRhLCBvZmZzZXQgKyA2KSArIDEwIDw9IGRhdGEubGVuZ3RoIC0gb2Zmc2V0O1xufTtcblxuLyoqXG4gKiBTZWFyY2hlcyBmb3IgdGhlIEVsZW1lbnRhcnkgU3RyZWFtIHRpbWVzdGFtcCBmb3VuZCBpbiB0aGUgSUQzIGRhdGEgY2h1bmtcbiAqIEBwYXJhbSBkYXRhIC0gQmxvY2sgb2YgZGF0YSBjb250YWluaW5nIG9uZSBvciBtb3JlIElEMyB0YWdzXG4gKi9cbmNvbnN0IGdldFRpbWVTdGFtcCA9IGRhdGEgPT4ge1xuICBjb25zdCBmcmFtZXMgPSBnZXRJRDNGcmFtZXMoZGF0YSk7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgZnJhbWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgY29uc3QgZnJhbWUgPSBmcmFtZXNbaV07XG4gICAgaWYgKGlzVGltZVN0YW1wRnJhbWUoZnJhbWUpKSB7XG4gICAgICByZXR1cm4gcmVhZFRpbWVTdGFtcChmcmFtZSk7XG4gICAgfVxuICB9XG4gIHJldHVybiB1bmRlZmluZWQ7XG59O1xuXG4vKipcbiAqIFJldHVybnMgdHJ1ZSBpZiB0aGUgSUQzIGZyYW1lIGlzIGFuIEVsZW1lbnRhcnkgU3RyZWFtIHRpbWVzdGFtcCBmcmFtZVxuICovXG5jb25zdCBpc1RpbWVTdGFtcEZyYW1lID0gZnJhbWUgPT4ge1xuICByZXR1cm4gZnJhbWUgJiYgZnJhbWUua2V5ID09PSAnUFJJVicgJiYgZnJhbWUuaW5mbyA9PT0gJ2NvbS5hcHBsZS5zdHJlYW1pbmcudHJhbnNwb3J0U3RyZWFtVGltZXN0YW1wJztcbn07XG5jb25zdCBnZXRGcmFtZURhdGEgPSBkYXRhID0+IHtcbiAgLypcbiAgRnJhbWUgSUQgICAgICAgJHh4IHh4IHh4IHh4IChmb3VyIGNoYXJhY3RlcnMpXG4gIFNpemUgICAgICAgICAgICR4eCB4eCB4eCB4eFxuICBGbGFncyAgICAgICAgICAkeHggeHhcbiAgKi9cbiAgY29uc3QgdHlwZSA9IFN0cmluZy5mcm9tQ2hhckNvZGUoZGF0YVswXSwgZGF0YVsxXSwgZGF0YVsyXSwgZGF0YVszXSk7XG4gIGNvbnN0IHNpemUgPSByZWFkU2l6ZShkYXRhLCA0KTtcblxuICAvLyBza2lwIGZyYW1lIGlkLCBzaXplLCBhbmQgZmxhZ3NcbiAgY29uc3Qgb2Zmc2V0ID0gMTA7XG4gIHJldHVybiB7XG4gICAgdHlwZSxcbiAgICBzaXplLFxuICAgIGRhdGE6IGRhdGEuc3ViYXJyYXkob2Zmc2V0LCBvZmZzZXQgKyBzaXplKVxuICB9O1xufTtcblxuLyoqXG4gKiBSZXR1cm5zIGFuIGFycmF5IG9mIElEMyBmcmFtZXMgZm91bmQgaW4gYWxsIHRoZSBJRDMgdGFncyBpbiB0aGUgaWQzRGF0YVxuICogQHBhcmFtIGlkM0RhdGEgLSBUaGUgSUQzIGRhdGEgY29udGFpbmluZyBvbmUgb3IgbW9yZSBJRDMgdGFnc1xuICovXG5jb25zdCBnZXRJRDNGcmFtZXMgPSBpZDNEYXRhID0+IHtcbiAgbGV0IG9mZnNldCA9IDA7XG4gIGNvbnN0IGZyYW1lcyA9IFtdO1xuICB3aGlsZSAoaXNIZWFkZXIkMihpZDNEYXRhLCBvZmZzZXQpKSB7XG4gICAgY29uc3Qgc2l6ZSA9IHJlYWRTaXplKGlkM0RhdGEsIG9mZnNldCArIDYpO1xuICAgIC8vIHNraXAgcGFzdCBJRDMgaGVhZGVyXG4gICAgb2Zmc2V0ICs9IDEwO1xuICAgIGNvbnN0IGVuZCA9IG9mZnNldCArIHNpemU7XG4gICAgLy8gbG9vcCB0aHJvdWdoIGZyYW1lcyBpbiB0aGUgSUQzIHRhZ1xuICAgIHdoaWxlIChvZmZzZXQgKyA4IDwgZW5kKSB7XG4gICAgICBjb25zdCBmcmFtZURhdGEgPSBnZXRGcmFtZURhdGEoaWQzRGF0YS5zdWJhcnJheShvZmZzZXQpKTtcbiAgICAgIGNvbnN0IGZyYW1lID0gZGVjb2RlRnJhbWUoZnJhbWVEYXRhKTtcbiAgICAgIGlmIChmcmFtZSkge1xuICAgICAgICBmcmFtZXMucHVzaChmcmFtZSk7XG4gICAgICB9XG5cbiAgICAgIC8vIHNraXAgZnJhbWUgaGVhZGVyIGFuZCBmcmFtZSBkYXRhXG4gICAgICBvZmZzZXQgKz0gZnJhbWVEYXRhLnNpemUgKyAxMDtcbiAgICB9XG4gICAgaWYgKGlzRm9vdGVyKGlkM0RhdGEsIG9mZnNldCkpIHtcbiAgICAgIG9mZnNldCArPSAxMDtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGZyYW1lcztcbn07XG5jb25zdCBkZWNvZGVGcmFtZSA9IGZyYW1lID0+IHtcbiAgaWYgKGZyYW1lLnR5cGUgPT09ICdQUklWJykge1xuICAgIHJldHVybiBkZWNvZGVQcml2RnJhbWUoZnJhbWUpO1xuICB9IGVsc2UgaWYgKGZyYW1lLnR5cGVbMF0gPT09ICdXJykge1xuICAgIHJldHVybiBkZWNvZGVVUkxGcmFtZShmcmFtZSk7XG4gIH1cbiAgcmV0dXJuIGRlY29kZVRleHRGcmFtZShmcmFtZSk7XG59O1xuY29uc3QgZGVjb2RlUHJpdkZyYW1lID0gZnJhbWUgPT4ge1xuICAvKlxuICBGb3JtYXQ6IDx0ZXh0IHN0cmluZz5cXDA8YmluYXJ5IGRhdGE+XG4gICovXG4gIGlmIChmcmFtZS5zaXplIDwgMikge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbiAgY29uc3Qgb3duZXIgPSB1dGY4QXJyYXlUb1N0cihmcmFtZS5kYXRhLCB0cnVlKTtcbiAgY29uc3QgcHJpdmF0ZURhdGEgPSBuZXcgVWludDhBcnJheShmcmFtZS5kYXRhLnN1YmFycmF5KG93bmVyLmxlbmd0aCArIDEpKTtcbiAgcmV0dXJuIHtcbiAgICBrZXk6IGZyYW1lLnR5cGUsXG4gICAgaW5mbzogb3duZXIsXG4gICAgZGF0YTogcHJpdmF0ZURhdGEuYnVmZmVyXG4gIH07XG59O1xuY29uc3QgZGVjb2RlVGV4dEZyYW1lID0gZnJhbWUgPT4ge1xuICBpZiAoZnJhbWUuc2l6ZSA8IDIpIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG4gIGlmIChmcmFtZS50eXBlID09PSAnVFhYWCcpIHtcbiAgICAvKlxuICAgIEZvcm1hdDpcbiAgICBbMF0gICA9IHtUZXh0IEVuY29kaW5nfVxuICAgIFsxLT9dID0ge0Rlc2NyaXB0aW9ufVxcMHtWYWx1ZX1cbiAgICAqL1xuICAgIGxldCBpbmRleCA9IDE7XG4gICAgY29uc3QgZGVzY3JpcHRpb24gPSB1dGY4QXJyYXlUb1N0cihmcmFtZS5kYXRhLnN1YmFycmF5KGluZGV4KSwgdHJ1ZSk7XG4gICAgaW5kZXggKz0gZGVzY3JpcHRpb24ubGVuZ3RoICsgMTtcbiAgICBjb25zdCB2YWx1ZSA9IHV0ZjhBcnJheVRvU3RyKGZyYW1lLmRhdGEuc3ViYXJyYXkoaW5kZXgpKTtcbiAgICByZXR1cm4ge1xuICAgICAga2V5OiBmcmFtZS50eXBlLFxuICAgICAgaW5mbzogZGVzY3JpcHRpb24sXG4gICAgICBkYXRhOiB2YWx1ZVxuICAgIH07XG4gIH1cbiAgLypcbiAgRm9ybWF0OlxuICBbMF0gICA9IHtUZXh0IEVuY29kaW5nfVxuICBbMS0/XSA9IHtWYWx1ZX1cbiAgKi9cbiAgY29uc3QgdGV4dCA9IHV0ZjhBcnJheVRvU3RyKGZyYW1lLmRhdGEuc3ViYXJyYXkoMSkpO1xuICByZXR1cm4ge1xuICAgIGtleTogZnJhbWUudHlwZSxcbiAgICBkYXRhOiB0ZXh0XG4gIH07XG59O1xuY29uc3QgZGVjb2RlVVJMRnJhbWUgPSBmcmFtZSA9PiB7XG4gIGlmIChmcmFtZS50eXBlID09PSAnV1hYWCcpIHtcbiAgICAvKlxuICAgIEZvcm1hdDpcbiAgICBbMF0gICA9IHtUZXh0IEVuY29kaW5nfVxuICAgIFsxLT9dID0ge0Rlc2NyaXB0aW9ufVxcMHtVUkx9XG4gICAgKi9cbiAgICBpZiAoZnJhbWUuc2l6ZSA8IDIpIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICAgIGxldCBpbmRleCA9IDE7XG4gICAgY29uc3QgZGVzY3JpcHRpb24gPSB1dGY4QXJyYXlUb1N0cihmcmFtZS5kYXRhLnN1YmFycmF5KGluZGV4KSwgdHJ1ZSk7XG4gICAgaW5kZXggKz0gZGVzY3JpcHRpb24ubGVuZ3RoICsgMTtcbiAgICBjb25zdCB2YWx1ZSA9IHV0ZjhBcnJheVRvU3RyKGZyYW1lLmRhdGEuc3ViYXJyYXkoaW5kZXgpKTtcbiAgICByZXR1cm4ge1xuICAgICAga2V5OiBmcmFtZS50eXBlLFxuICAgICAgaW5mbzogZGVzY3JpcHRpb24sXG4gICAgICBkYXRhOiB2YWx1ZVxuICAgIH07XG4gIH1cbiAgLypcbiAgRm9ybWF0OlxuICBbMC0/XSA9IHtVUkx9XG4gICovXG4gIGNvbnN0IHVybCA9IHV0ZjhBcnJheVRvU3RyKGZyYW1lLmRhdGEpO1xuICByZXR1cm4ge1xuICAgIGtleTogZnJhbWUudHlwZSxcbiAgICBkYXRhOiB1cmxcbiAgfTtcbn07XG5jb25zdCByZWFkVGltZVN0YW1wID0gdGltZVN0YW1wRnJhbWUgPT4ge1xuICBpZiAodGltZVN0YW1wRnJhbWUuZGF0YS5ieXRlTGVuZ3RoID09PSA4KSB7XG4gICAgY29uc3QgZGF0YSA9IG5ldyBVaW50OEFycmF5KHRpbWVTdGFtcEZyYW1lLmRhdGEpO1xuICAgIC8vIHRpbWVzdGFtcCBpcyAzMyBiaXQgZXhwcmVzc2VkIGFzIGEgYmlnLWVuZGlhbiBlaWdodC1vY3RldCBudW1iZXIsXG4gICAgLy8gd2l0aCB0aGUgdXBwZXIgMzEgYml0cyBzZXQgdG8gemVyby5cbiAgICBjb25zdCBwdHMzM0JpdCA9IGRhdGFbM10gJiAweDE7XG4gICAgbGV0IHRpbWVzdGFtcCA9IChkYXRhWzRdIDw8IDIzKSArIChkYXRhWzVdIDw8IDE1KSArIChkYXRhWzZdIDw8IDcpICsgZGF0YVs3XTtcbiAgICB0aW1lc3RhbXAgLz0gNDU7XG4gICAgaWYgKHB0czMzQml0KSB7XG4gICAgICB0aW1lc3RhbXAgKz0gNDc3MjE4NTguODQ7XG4gICAgfSAvLyAyXjMyIC8gOTBcblxuICAgIHJldHVybiBNYXRoLnJvdW5kKHRpbWVzdGFtcCk7XG4gIH1cbiAgcmV0dXJuIHVuZGVmaW5lZDtcbn07XG5cbi8vIGh0dHA6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvODkzNjk4NC91aW50OGFycmF5LXRvLXN0cmluZy1pbi1qYXZhc2NyaXB0LzIyMzczMTk3XG4vLyBodHRwOi8vd3d3Lm9uaWNvcy5jb20vc3RhZmYvaXovYW11c2UvamF2YXNjcmlwdC9leHBlcnQvdXRmLnR4dFxuLyogdXRmLmpzIC0gVVRGLTggPD0+IFVURi0xNiBjb252ZXJ0aW9uXG4gKlxuICogQ29weXJpZ2h0IChDKSAxOTk5IE1hc2FuYW8gSXp1bW8gPGl6QG9uaWNvcy5jby5qcD5cbiAqIFZlcnNpb246IDEuMFxuICogTGFzdE1vZGlmaWVkOiBEZWMgMjUgMTk5OVxuICogVGhpcyBsaWJyYXJ5IGlzIGZyZWUuICBZb3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5IGl0LlxuICovXG5jb25zdCB1dGY4QXJyYXlUb1N0ciA9IChhcnJheSwgZXhpdE9uTnVsbCA9IGZhbHNlKSA9PiB7XG4gIGNvbnN0IGRlY29kZXIgPSBnZXRUZXh0RGVjb2RlcigpO1xuICBpZiAoZGVjb2Rlcikge1xuICAgIGNvbnN0IGRlY29kZWQgPSBkZWNvZGVyLmRlY29kZShhcnJheSk7XG4gICAgaWYgKGV4aXRPbk51bGwpIHtcbiAgICAgIC8vIGdyYWIgdXAgdG8gdGhlIGZpcnN0IG51bGxcbiAgICAgIGNvbnN0IGlkeCA9IGRlY29kZWQuaW5kZXhPZignXFwwJyk7XG4gICAgICByZXR1cm4gaWR4ICE9PSAtMSA/IGRlY29kZWQuc3Vic3RyaW5nKDAsIGlkeCkgOiBkZWNvZGVkO1xuICAgIH1cblxuICAgIC8vIHJlbW92ZSBhbnkgbnVsbCBjaGFyYWN0ZXJzXG4gICAgcmV0dXJuIGRlY29kZWQucmVwbGFjZSgvXFwwL2csICcnKTtcbiAgfVxuICBjb25zdCBsZW4gPSBhcnJheS5sZW5ndGg7XG4gIGxldCBjO1xuICBsZXQgY2hhcjI7XG4gIGxldCBjaGFyMztcbiAgbGV0IG91dCA9ICcnO1xuICBsZXQgaSA9IDA7XG4gIHdoaWxlIChpIDwgbGVuKSB7XG4gICAgYyA9IGFycmF5W2krK107XG4gICAgaWYgKGMgPT09IDB4MDAgJiYgZXhpdE9uTnVsbCkge1xuICAgICAgcmV0dXJuIG91dDtcbiAgICB9IGVsc2UgaWYgKGMgPT09IDB4MDAgfHwgYyA9PT0gMHgwMykge1xuICAgICAgLy8gSWYgdGhlIGNoYXJhY3RlciBpcyAzIChFTkRfT0ZfVEVYVCkgb3IgMCAoTlVMTCkgdGhlbiBza2lwIGl0XG4gICAgICBjb250aW51ZTtcbiAgICB9XG4gICAgc3dpdGNoIChjID4+IDQpIHtcbiAgICAgIGNhc2UgMDpcbiAgICAgIGNhc2UgMTpcbiAgICAgIGNhc2UgMjpcbiAgICAgIGNhc2UgMzpcbiAgICAgIGNhc2UgNDpcbiAgICAgIGNhc2UgNTpcbiAgICAgIGNhc2UgNjpcbiAgICAgIGNhc2UgNzpcbiAgICAgICAgLy8gMHh4eHh4eHhcbiAgICAgICAgb3V0ICs9IFN0cmluZy5mcm9tQ2hhckNvZGUoYyk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAxMjpcbiAgICAgIGNhc2UgMTM6XG4gICAgICAgIC8vIDExMHggeHh4eCAgIDEweHggeHh4eFxuICAgICAgICBjaGFyMiA9IGFycmF5W2krK107XG4gICAgICAgIG91dCArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKChjICYgMHgxZikgPDwgNiB8IGNoYXIyICYgMHgzZik7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAxNDpcbiAgICAgICAgLy8gMTExMCB4eHh4ICAxMHh4IHh4eHggIDEweHggeHh4eFxuICAgICAgICBjaGFyMiA9IGFycmF5W2krK107XG4gICAgICAgIGNoYXIzID0gYXJyYXlbaSsrXTtcbiAgICAgICAgb3V0ICs9IFN0cmluZy5mcm9tQ2hhckNvZGUoKGMgJiAweDBmKSA8PCAxMiB8IChjaGFyMiAmIDB4M2YpIDw8IDYgfCAoY2hhcjMgJiAweDNmKSA8PCAwKTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuICB9XG4gIHJldHVybiBvdXQ7XG59O1xubGV0IGRlY29kZXI7XG5mdW5jdGlvbiBnZXRUZXh0RGVjb2RlcigpIHtcbiAgLy8gT24gUGxheSBTdGF0aW9uIDQsIFRleHREZWNvZGVyIGlzIGRlZmluZWQgYnV0IHBhcnRpYWxseSBpbXBsZW1lbnRlZC5cbiAgLy8gTWFudWFsIGRlY29kaW5nIG9wdGlvbiBpcyBwcmVmZXJhYmxlXG4gIGlmIChuYXZpZ2F0b3IudXNlckFnZW50LmluY2x1ZGVzKCdQbGF5U3RhdGlvbiA0JykpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgaWYgKCFkZWNvZGVyICYmIHR5cGVvZiBzZWxmLlRleHREZWNvZGVyICE9PSAndW5kZWZpbmVkJykge1xuICAgIGRlY29kZXIgPSBuZXcgc2VsZi5UZXh0RGVjb2RlcigndXRmLTgnKTtcbiAgfVxuICByZXR1cm4gZGVjb2Rlcjtcbn1cblxuLyoqXG4gKiAgaGV4IGR1bXAgaGVscGVyIGNsYXNzXG4gKi9cblxuY29uc3QgSGV4ID0ge1xuICBoZXhEdW1wOiBmdW5jdGlvbiAoYXJyYXkpIHtcbiAgICBsZXQgc3RyID0gJyc7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhcnJheS5sZW5ndGg7IGkrKykge1xuICAgICAgbGV0IGggPSBhcnJheVtpXS50b1N0cmluZygxNik7XG4gICAgICBpZiAoaC5sZW5ndGggPCAyKSB7XG4gICAgICAgIGggPSAnMCcgKyBoO1xuICAgICAgfVxuICAgICAgc3RyICs9IGg7XG4gICAgfVxuICAgIHJldHVybiBzdHI7XG4gIH1cbn07XG5cbmNvbnN0IFVJTlQzMl9NQVgkMSA9IE1hdGgucG93KDIsIDMyKSAtIDE7XG5jb25zdCBwdXNoID0gW10ucHVzaDtcblxuLy8gV2UgYXJlIHVzaW5nIGZpeGVkIHRyYWNrIElEcyBmb3IgZHJpdmluZyB0aGUgTVA0IHJlbXV4ZXJcbi8vIGluc3RlYWQgb2YgZm9sbG93aW5nIHRoZSBUUyBQSURzLlxuLy8gVGhlcmUgaXMgbm8gcmVhc29uIG5vdCB0byBkbyB0aGlzIGFuZCBzb21lIGJyb3dzZXJzL1NvdXJjZUJ1ZmZlci1kZW11eGVyc1xuLy8gbWF5IG5vdCBsaWtlIGlmIHRoZXJlIGFyZSBUcmFja0lEIFwic3dpdGNoZXNcIlxuLy8gU2VlIGh0dHBzOi8vZ2l0aHViLmNvbS92aWRlby1kZXYvaGxzLmpzL2lzc3Vlcy8xMzMxXG4vLyBIZXJlIHdlIGFyZSBtYXBwaW5nIG91ciBpbnRlcm5hbCB0cmFjayB0eXBlcyB0byBjb25zdGFudCBNUDQgdHJhY2sgSURzXG4vLyBXaXRoIE1TRSBjdXJyZW50bHkgb25lIGNhbiBvbmx5IGhhdmUgb25lIHRyYWNrIG9mIGVhY2gsIGFuZCB3ZSBhcmUgbXV4aW5nXG4vLyB3aGF0ZXZlciB2aWRlby9hdWRpbyByZW5kaXRpb24gaW4gdGhlbS5cbmNvbnN0IFJlbXV4ZXJUcmFja0lkQ29uZmlnID0ge1xuICB2aWRlbzogMSxcbiAgYXVkaW86IDIsXG4gIGlkMzogMyxcbiAgdGV4dDogNFxufTtcbmZ1bmN0aW9uIGJpbjJzdHIoZGF0YSkge1xuICByZXR1cm4gU3RyaW5nLmZyb21DaGFyQ29kZS5hcHBseShudWxsLCBkYXRhKTtcbn1cbmZ1bmN0aW9uIHJlYWRVaW50MTYoYnVmZmVyLCBvZmZzZXQpIHtcbiAgY29uc3QgdmFsID0gYnVmZmVyW29mZnNldF0gPDwgOCB8IGJ1ZmZlcltvZmZzZXQgKyAxXTtcbiAgcmV0dXJuIHZhbCA8IDAgPyA2NTUzNiArIHZhbCA6IHZhbDtcbn1cbmZ1bmN0aW9uIHJlYWRVaW50MzIoYnVmZmVyLCBvZmZzZXQpIHtcbiAgY29uc3QgdmFsID0gcmVhZFNpbnQzMihidWZmZXIsIG9mZnNldCk7XG4gIHJldHVybiB2YWwgPCAwID8gNDI5NDk2NzI5NiArIHZhbCA6IHZhbDtcbn1cbmZ1bmN0aW9uIHJlYWRVaW50NjQoYnVmZmVyLCBvZmZzZXQpIHtcbiAgbGV0IHJlc3VsdCA9IHJlYWRVaW50MzIoYnVmZmVyLCBvZmZzZXQpO1xuICByZXN1bHQgKj0gTWF0aC5wb3coMiwgMzIpO1xuICByZXN1bHQgKz0gcmVhZFVpbnQzMihidWZmZXIsIG9mZnNldCArIDQpO1xuICByZXR1cm4gcmVzdWx0O1xufVxuZnVuY3Rpb24gcmVhZFNpbnQzMihidWZmZXIsIG9mZnNldCkge1xuICByZXR1cm4gYnVmZmVyW29mZnNldF0gPDwgMjQgfCBidWZmZXJbb2Zmc2V0ICsgMV0gPDwgMTYgfCBidWZmZXJbb2Zmc2V0ICsgMl0gPDwgOCB8IGJ1ZmZlcltvZmZzZXQgKyAzXTtcbn1cbmZ1bmN0aW9uIHdyaXRlVWludDMyKGJ1ZmZlciwgb2Zmc2V0LCB2YWx1ZSkge1xuICBidWZmZXJbb2Zmc2V0XSA9IHZhbHVlID4+IDI0O1xuICBidWZmZXJbb2Zmc2V0ICsgMV0gPSB2YWx1ZSA+PiAxNiAmIDB4ZmY7XG4gIGJ1ZmZlcltvZmZzZXQgKyAyXSA9IHZhbHVlID4+IDggJiAweGZmO1xuICBidWZmZXJbb2Zmc2V0ICsgM10gPSB2YWx1ZSAmIDB4ZmY7XG59XG5cbi8vIEZpbmQgXCJtb29mXCIgYm94XG5mdW5jdGlvbiBoYXNNb29mRGF0YShkYXRhKSB7XG4gIGNvbnN0IGVuZCA9IGRhdGEuYnl0ZUxlbmd0aDtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBlbmQ7KSB7XG4gICAgY29uc3Qgc2l6ZSA9IHJlYWRVaW50MzIoZGF0YSwgaSk7XG4gICAgaWYgKHNpemUgPiA4ICYmIGRhdGFbaSArIDRdID09PSAweDZkICYmIGRhdGFbaSArIDVdID09PSAweDZmICYmIGRhdGFbaSArIDZdID09PSAweDZmICYmIGRhdGFbaSArIDddID09PSAweDY2KSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgaSA9IHNpemUgPiAxID8gaSArIHNpemUgOiBlbmQ7XG4gIH1cbiAgcmV0dXJuIGZhbHNlO1xufVxuXG4vLyBGaW5kIHRoZSBkYXRhIGZvciBhIGJveCBzcGVjaWZpZWQgYnkgaXRzIHBhdGhcbmZ1bmN0aW9uIGZpbmRCb3goZGF0YSwgcGF0aCkge1xuICBjb25zdCByZXN1bHRzID0gW107XG4gIGlmICghcGF0aC5sZW5ndGgpIHtcbiAgICAvLyBzaG9ydC1jaXJjdWl0IHRoZSBzZWFyY2ggZm9yIGVtcHR5IHBhdGhzXG4gICAgcmV0dXJuIHJlc3VsdHM7XG4gIH1cbiAgY29uc3QgZW5kID0gZGF0YS5ieXRlTGVuZ3RoO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGVuZDspIHtcbiAgICBjb25zdCBzaXplID0gcmVhZFVpbnQzMihkYXRhLCBpKTtcbiAgICBjb25zdCB0eXBlID0gYmluMnN0cihkYXRhLnN1YmFycmF5KGkgKyA0LCBpICsgOCkpO1xuICAgIGNvbnN0IGVuZGJveCA9IHNpemUgPiAxID8gaSArIHNpemUgOiBlbmQ7XG4gICAgaWYgKHR5cGUgPT09IHBhdGhbMF0pIHtcbiAgICAgIGlmIChwYXRoLmxlbmd0aCA9PT0gMSkge1xuICAgICAgICAvLyB0aGlzIGlzIHRoZSBlbmQgb2YgdGhlIHBhdGggYW5kIHdlJ3ZlIGZvdW5kIHRoZSBib3ggd2Ugd2VyZVxuICAgICAgICAvLyBsb29raW5nIGZvclxuICAgICAgICByZXN1bHRzLnB1c2goZGF0YS5zdWJhcnJheShpICsgOCwgZW5kYm94KSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyByZWN1cnNpdmVseSBzZWFyY2ggZm9yIHRoZSBuZXh0IGJveCBhbG9uZyB0aGUgcGF0aFxuICAgICAgICBjb25zdCBzdWJyZXN1bHRzID0gZmluZEJveChkYXRhLnN1YmFycmF5KGkgKyA4LCBlbmRib3gpLCBwYXRoLnNsaWNlKDEpKTtcbiAgICAgICAgaWYgKHN1YnJlc3VsdHMubGVuZ3RoKSB7XG4gICAgICAgICAgcHVzaC5hcHBseShyZXN1bHRzLCBzdWJyZXN1bHRzKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICBpID0gZW5kYm94O1xuICB9XG5cbiAgLy8gd2UndmUgZmluaXNoZWQgc2VhcmNoaW5nIGFsbCBvZiBkYXRhXG4gIHJldHVybiByZXN1bHRzO1xufVxuZnVuY3Rpb24gcGFyc2VTZWdtZW50SW5kZXgoc2lkeCkge1xuICBjb25zdCByZWZlcmVuY2VzID0gW107XG4gIGNvbnN0IHZlcnNpb24gPSBzaWR4WzBdO1xuXG4gIC8vIHNldCBpbml0aWFsIG9mZnNldCwgd2Ugc2tpcCB0aGUgcmVmZXJlbmNlIElEIChub3QgbmVlZGVkKVxuICBsZXQgaW5kZXggPSA4O1xuICBjb25zdCB0aW1lc2NhbGUgPSByZWFkVWludDMyKHNpZHgsIGluZGV4KTtcbiAgaW5kZXggKz0gNDtcbiAgbGV0IGVhcmxpZXN0UHJlc2VudGF0aW9uVGltZSA9IDA7XG4gIGxldCBmaXJzdE9mZnNldCA9IDA7XG4gIGlmICh2ZXJzaW9uID09PSAwKSB7XG4gICAgZWFybGllc3RQcmVzZW50YXRpb25UaW1lID0gcmVhZFVpbnQzMihzaWR4LCBpbmRleCk7XG4gICAgZmlyc3RPZmZzZXQgPSByZWFkVWludDMyKHNpZHgsIGluZGV4ICsgNCk7XG4gICAgaW5kZXggKz0gODtcbiAgfSBlbHNlIHtcbiAgICBlYXJsaWVzdFByZXNlbnRhdGlvblRpbWUgPSByZWFkVWludDY0KHNpZHgsIGluZGV4KTtcbiAgICBmaXJzdE9mZnNldCA9IHJlYWRVaW50NjQoc2lkeCwgaW5kZXggKyA4KTtcbiAgICBpbmRleCArPSAxNjtcbiAgfVxuXG4gIC8vIHNraXAgcmVzZXJ2ZWRcbiAgaW5kZXggKz0gMjtcbiAgbGV0IHN0YXJ0Qnl0ZSA9IHNpZHgubGVuZ3RoICsgZmlyc3RPZmZzZXQ7XG4gIGNvbnN0IHJlZmVyZW5jZXNDb3VudCA9IHJlYWRVaW50MTYoc2lkeCwgaW5kZXgpO1xuICBpbmRleCArPSAyO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IHJlZmVyZW5jZXNDb3VudDsgaSsrKSB7XG4gICAgbGV0IHJlZmVyZW5jZUluZGV4ID0gaW5kZXg7XG4gICAgY29uc3QgcmVmZXJlbmNlSW5mbyA9IHJlYWRVaW50MzIoc2lkeCwgcmVmZXJlbmNlSW5kZXgpO1xuICAgIHJlZmVyZW5jZUluZGV4ICs9IDQ7XG4gICAgY29uc3QgcmVmZXJlbmNlU2l6ZSA9IHJlZmVyZW5jZUluZm8gJiAweDdmZmZmZmZmO1xuICAgIGNvbnN0IHJlZmVyZW5jZVR5cGUgPSAocmVmZXJlbmNlSW5mbyAmIDB4ODAwMDAwMDApID4+PiAzMTtcbiAgICBpZiAocmVmZXJlbmNlVHlwZSA9PT0gMSkge1xuICAgICAgbG9nZ2VyLndhcm4oJ1NJRFggaGFzIGhpZXJhcmNoaWNhbCByZWZlcmVuY2VzIChub3Qgc3VwcG9ydGVkKScpO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIGNvbnN0IHN1YnNlZ21lbnREdXJhdGlvbiA9IHJlYWRVaW50MzIoc2lkeCwgcmVmZXJlbmNlSW5kZXgpO1xuICAgIHJlZmVyZW5jZUluZGV4ICs9IDQ7XG4gICAgcmVmZXJlbmNlcy5wdXNoKHtcbiAgICAgIHJlZmVyZW5jZVNpemUsXG4gICAgICBzdWJzZWdtZW50RHVyYXRpb24sXG4gICAgICAvLyB1bnNjYWxlZFxuICAgICAgaW5mbzoge1xuICAgICAgICBkdXJhdGlvbjogc3Vic2VnbWVudER1cmF0aW9uIC8gdGltZXNjYWxlLFxuICAgICAgICBzdGFydDogc3RhcnRCeXRlLFxuICAgICAgICBlbmQ6IHN0YXJ0Qnl0ZSArIHJlZmVyZW5jZVNpemUgLSAxXG4gICAgICB9XG4gICAgfSk7XG4gICAgc3RhcnRCeXRlICs9IHJlZmVyZW5jZVNpemU7XG5cbiAgICAvLyBTa2lwcGluZyAxIGJpdCBmb3IgfHN0YXJ0c1dpdGhTYXB8LCAzIGJpdHMgZm9yIHxzYXBUeXBlfCwgYW5kIDI4IGJpdHNcbiAgICAvLyBmb3IgfHNhcERlbHRhfC5cbiAgICByZWZlcmVuY2VJbmRleCArPSA0O1xuXG4gICAgLy8gc2tpcCB0byBuZXh0IHJlZlxuICAgIGluZGV4ID0gcmVmZXJlbmNlSW5kZXg7XG4gIH1cbiAgcmV0dXJuIHtcbiAgICBlYXJsaWVzdFByZXNlbnRhdGlvblRpbWUsXG4gICAgdGltZXNjYWxlLFxuICAgIHZlcnNpb24sXG4gICAgcmVmZXJlbmNlc0NvdW50LFxuICAgIHJlZmVyZW5jZXNcbiAgfTtcbn1cblxuLyoqXG4gKiBQYXJzZXMgYW4gTVA0IGluaXRpYWxpemF0aW9uIHNlZ21lbnQgYW5kIGV4dHJhY3RzIHN0cmVhbSB0eXBlIGFuZFxuICogdGltZXNjYWxlIHZhbHVlcyBmb3IgYW55IGRlY2xhcmVkIHRyYWNrcy4gVGltZXNjYWxlIHZhbHVlcyBpbmRpY2F0ZSB0aGVcbiAqIG51bWJlciBvZiBjbG9jayB0aWNrcyBwZXIgc2Vjb25kIHRvIGFzc3VtZSBmb3IgdGltZS1iYXNlZCB2YWx1ZXNcbiAqIGVsc2V3aGVyZSBpbiB0aGUgTVA0LlxuICpcbiAqIFRvIGRldGVybWluZSB0aGUgc3RhcnQgdGltZSBvZiBhbiBNUDQsIHlvdSBuZWVkIHR3byBwaWVjZXMgb2ZcbiAqIGluZm9ybWF0aW9uOiB0aGUgdGltZXNjYWxlIHVuaXQgYW5kIHRoZSBlYXJsaWVzdCBiYXNlIG1lZGlhIGRlY29kZVxuICogdGltZS4gTXVsdGlwbGUgdGltZXNjYWxlcyBjYW4gYmUgc3BlY2lmaWVkIHdpdGhpbiBhbiBNUDQgYnV0IHRoZVxuICogYmFzZSBtZWRpYSBkZWNvZGUgdGltZSBpcyBhbHdheXMgZXhwcmVzc2VkIGluIHRoZSB0aW1lc2NhbGUgZnJvbVxuICogdGhlIG1lZGlhIGhlYWRlciBib3ggZm9yIHRoZSB0cmFjazpcbiAqIGBgYFxuICogbW9vdiA+IHRyYWsgPiBtZGlhID4gbWRoZC50aW1lc2NhbGVcbiAqIG1vb3YgPiB0cmFrID4gbWRpYSA+IGhkbHJcbiAqIGBgYFxuICogQHBhcmFtIGluaXRTZWdtZW50IHRoZSBieXRlcyBvZiB0aGUgaW5pdCBzZWdtZW50XG4gKiBAcmV0dXJucyBhIGhhc2ggb2YgdHJhY2sgdHlwZSB0byB0aW1lc2NhbGUgdmFsdWVzIG9yIG51bGwgaWZcbiAqIHRoZSBpbml0IHNlZ21lbnQgaXMgbWFsZm9ybWVkLlxuICovXG5cbmZ1bmN0aW9uIHBhcnNlSW5pdFNlZ21lbnQoaW5pdFNlZ21lbnQpIHtcbiAgY29uc3QgcmVzdWx0ID0gW107XG4gIGNvbnN0IHRyYWtzID0gZmluZEJveChpbml0U2VnbWVudCwgWydtb292JywgJ3RyYWsnXSk7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgdHJha3MubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCB0cmFrID0gdHJha3NbaV07XG4gICAgY29uc3QgdGtoZCA9IGZpbmRCb3godHJhaywgWyd0a2hkJ10pWzBdO1xuICAgIGlmICh0a2hkKSB7XG4gICAgICBsZXQgdmVyc2lvbiA9IHRraGRbMF07XG4gICAgICBjb25zdCB0cmFja0lkID0gcmVhZFVpbnQzMih0a2hkLCB2ZXJzaW9uID09PSAwID8gMTIgOiAyMCk7XG4gICAgICBjb25zdCBtZGhkID0gZmluZEJveCh0cmFrLCBbJ21kaWEnLCAnbWRoZCddKVswXTtcbiAgICAgIGlmIChtZGhkKSB7XG4gICAgICAgIHZlcnNpb24gPSBtZGhkWzBdO1xuICAgICAgICBjb25zdCB0aW1lc2NhbGUgPSByZWFkVWludDMyKG1kaGQsIHZlcnNpb24gPT09IDAgPyAxMiA6IDIwKTtcbiAgICAgICAgY29uc3QgaGRsciA9IGZpbmRCb3godHJhaywgWydtZGlhJywgJ2hkbHInXSlbMF07XG4gICAgICAgIGlmIChoZGxyKSB7XG4gICAgICAgICAgY29uc3QgaGRsclR5cGUgPSBiaW4yc3RyKGhkbHIuc3ViYXJyYXkoOCwgMTIpKTtcbiAgICAgICAgICBjb25zdCB0eXBlID0ge1xuICAgICAgICAgICAgc291bjogRWxlbWVudGFyeVN0cmVhbVR5cGVzLkFVRElPLFxuICAgICAgICAgICAgdmlkZTogRWxlbWVudGFyeVN0cmVhbVR5cGVzLlZJREVPXG4gICAgICAgICAgfVtoZGxyVHlwZV07XG4gICAgICAgICAgaWYgKHR5cGUpIHtcbiAgICAgICAgICAgIC8vIFBhcnNlIGNvZGVjIGRldGFpbHNcbiAgICAgICAgICAgIGNvbnN0IHN0c2QgPSBmaW5kQm94KHRyYWssIFsnbWRpYScsICdtaW5mJywgJ3N0YmwnLCAnc3RzZCddKVswXTtcbiAgICAgICAgICAgIGNvbnN0IHN0c2REYXRhID0gcGFyc2VTdHNkKHN0c2QpO1xuICAgICAgICAgICAgcmVzdWx0W3RyYWNrSWRdID0ge1xuICAgICAgICAgICAgICB0aW1lc2NhbGUsXG4gICAgICAgICAgICAgIHR5cGVcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICByZXN1bHRbdHlwZV0gPSBfb2JqZWN0U3ByZWFkMih7XG4gICAgICAgICAgICAgIHRpbWVzY2FsZSxcbiAgICAgICAgICAgICAgaWQ6IHRyYWNrSWRcbiAgICAgICAgICAgIH0sIHN0c2REYXRhKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgY29uc3QgdHJleCA9IGZpbmRCb3goaW5pdFNlZ21lbnQsIFsnbW9vdicsICdtdmV4JywgJ3RyZXgnXSk7XG4gIHRyZXguZm9yRWFjaCh0cmV4ID0+IHtcbiAgICBjb25zdCB0cmFja0lkID0gcmVhZFVpbnQzMih0cmV4LCA0KTtcbiAgICBjb25zdCB0cmFjayA9IHJlc3VsdFt0cmFja0lkXTtcbiAgICBpZiAodHJhY2spIHtcbiAgICAgIHRyYWNrLmRlZmF1bHQgPSB7XG4gICAgICAgIGR1cmF0aW9uOiByZWFkVWludDMyKHRyZXgsIDEyKSxcbiAgICAgICAgZmxhZ3M6IHJlYWRVaW50MzIodHJleCwgMjApXG4gICAgICB9O1xuICAgIH1cbiAgfSk7XG4gIHJldHVybiByZXN1bHQ7XG59XG5mdW5jdGlvbiBwYXJzZVN0c2Qoc3RzZCkge1xuICBjb25zdCBzYW1wbGVFbnRyaWVzID0gc3RzZC5zdWJhcnJheSg4KTtcbiAgY29uc3Qgc2FtcGxlRW50cmllc0VuZCA9IHNhbXBsZUVudHJpZXMuc3ViYXJyYXkoOCArIDc4KTtcbiAgY29uc3QgZm91ckNDID0gYmluMnN0cihzYW1wbGVFbnRyaWVzLnN1YmFycmF5KDQsIDgpKTtcbiAgbGV0IGNvZGVjID0gZm91ckNDO1xuICBjb25zdCBlbmNyeXB0ZWQgPSBmb3VyQ0MgPT09ICdlbmNhJyB8fCBmb3VyQ0MgPT09ICdlbmN2JztcbiAgaWYgKGVuY3J5cHRlZCkge1xuICAgIGNvbnN0IGVuY0JveCA9IGZpbmRCb3goc2FtcGxlRW50cmllcywgW2ZvdXJDQ10pWzBdO1xuICAgIGNvbnN0IGVuY0JveENoaWxkcmVuID0gZW5jQm94LnN1YmFycmF5KGZvdXJDQyA9PT0gJ2VuY2EnID8gMjggOiA3OCk7XG4gICAgY29uc3Qgc2luZnMgPSBmaW5kQm94KGVuY0JveENoaWxkcmVuLCBbJ3NpbmYnXSk7XG4gICAgc2luZnMuZm9yRWFjaChzaW5mID0+IHtcbiAgICAgIGNvbnN0IHNjaG0gPSBmaW5kQm94KHNpbmYsIFsnc2NobSddKVswXTtcbiAgICAgIGlmIChzY2htKSB7XG4gICAgICAgIGNvbnN0IHNjaGVtZSA9IGJpbjJzdHIoc2NobS5zdWJhcnJheSg0LCA4KSk7XG4gICAgICAgIGlmIChzY2hlbWUgPT09ICdjYmNzJyB8fCBzY2hlbWUgPT09ICdjZW5jJykge1xuICAgICAgICAgIGNvbnN0IGZybWEgPSBmaW5kQm94KHNpbmYsIFsnZnJtYSddKVswXTtcbiAgICAgICAgICBpZiAoZnJtYSkge1xuICAgICAgICAgICAgLy8gZm9yIGVuY3J5cHRlZCBjb250ZW50IGNvZGVjIGZvdXJDQyB3aWxsIGJlIGluIGZybWFcbiAgICAgICAgICAgIGNvZGVjID0gYmluMnN0cihmcm1hKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuICBzd2l0Y2ggKGNvZGVjKSB7XG4gICAgY2FzZSAnYXZjMSc6XG4gICAgY2FzZSAnYXZjMic6XG4gICAgY2FzZSAnYXZjMyc6XG4gICAgY2FzZSAnYXZjNCc6XG4gICAgICB7XG4gICAgICAgIC8vIGV4dHJhY3QgcHJvZmlsZSArIGNvbXBhdGliaWxpdHkgKyBsZXZlbCBvdXQgb2YgYXZjQyBib3hcbiAgICAgICAgY29uc3QgYXZjQ0JveCA9IGZpbmRCb3goc2FtcGxlRW50cmllc0VuZCwgWydhdmNDJ10pWzBdO1xuICAgICAgICBjb2RlYyArPSAnLicgKyB0b0hleChhdmNDQm94WzFdKSArIHRvSGV4KGF2Y0NCb3hbMl0pICsgdG9IZXgoYXZjQ0JveFszXSk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIGNhc2UgJ21wNGEnOlxuICAgICAge1xuICAgICAgICBjb25zdCBjb2RlY0JveCA9IGZpbmRCb3goc2FtcGxlRW50cmllcywgW2ZvdXJDQ10pWzBdO1xuICAgICAgICBjb25zdCBlc2RzQm94ID0gZmluZEJveChjb2RlY0JveC5zdWJhcnJheSgyOCksIFsnZXNkcyddKVswXTtcbiAgICAgICAgaWYgKGVzZHNCb3ggJiYgZXNkc0JveC5sZW5ndGggPiAxMikge1xuICAgICAgICAgIGxldCBpID0gNDtcbiAgICAgICAgICAvLyBFUyBEZXNjcmlwdG9yIHRhZ1xuICAgICAgICAgIGlmIChlc2RzQm94W2krK10gIT09IDB4MDMpIHtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpID0gc2tpcEJFUkludGVnZXIoZXNkc0JveCwgaSk7XG4gICAgICAgICAgaSArPSAyOyAvLyBza2lwIGVzX2lkO1xuICAgICAgICAgIGNvbnN0IGZsYWdzID0gZXNkc0JveFtpKytdO1xuICAgICAgICAgIGlmIChmbGFncyAmIDB4ODApIHtcbiAgICAgICAgICAgIGkgKz0gMjsgLy8gc2tpcCBkZXBlbmRlbmN5IGVzX2lkXG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChmbGFncyAmIDB4NDApIHtcbiAgICAgICAgICAgIGkgKz0gZXNkc0JveFtpKytdOyAvLyBza2lwIFVSTFxuICAgICAgICAgIH1cbiAgICAgICAgICAvLyBEZWNvZGVyIGNvbmZpZyBkZXNjcmlwdG9yXG4gICAgICAgICAgaWYgKGVzZHNCb3hbaSsrXSAhPT0gMHgwNCkge1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICAgIGkgPSBza2lwQkVSSW50ZWdlcihlc2RzQm94LCBpKTtcbiAgICAgICAgICBjb25zdCBvYmplY3RUeXBlID0gZXNkc0JveFtpKytdO1xuICAgICAgICAgIGlmIChvYmplY3RUeXBlID09PSAweDQwKSB7XG4gICAgICAgICAgICBjb2RlYyArPSAnLicgKyB0b0hleChvYmplY3RUeXBlKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICAgIGkgKz0gMTI7XG4gICAgICAgICAgLy8gRGVjb2RlciBzcGVjaWZpYyBpbmZvXG4gICAgICAgICAgaWYgKGVzZHNCb3hbaSsrXSAhPT0gMHgwNSkge1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICAgIGkgPSBza2lwQkVSSW50ZWdlcihlc2RzQm94LCBpKTtcbiAgICAgICAgICBjb25zdCBmaXJzdEJ5dGUgPSBlc2RzQm94W2krK107XG4gICAgICAgICAgbGV0IGF1ZGlvT2JqZWN0VHlwZSA9IChmaXJzdEJ5dGUgJiAweGY4KSA+PiAzO1xuICAgICAgICAgIGlmIChhdWRpb09iamVjdFR5cGUgPT09IDMxKSB7XG4gICAgICAgICAgICBhdWRpb09iamVjdFR5cGUgKz0gMSArICgoZmlyc3RCeXRlICYgMHg3KSA8PCAzKSArICgoZXNkc0JveFtpXSAmIDB4ZTApID4+IDUpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBjb2RlYyArPSAnLicgKyBhdWRpb09iamVjdFR5cGU7XG4gICAgICAgIH1cbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgY2FzZSAnaHZjMSc6XG4gICAgY2FzZSAnaGV2MSc6XG4gICAgICB7XG4gICAgICAgIGNvbnN0IGh2Y0NCb3ggPSBmaW5kQm94KHNhbXBsZUVudHJpZXNFbmQsIFsnaHZjQyddKVswXTtcbiAgICAgICAgY29uc3QgcHJvZmlsZUJ5dGUgPSBodmNDQm94WzFdO1xuICAgICAgICBjb25zdCBwcm9maWxlU3BhY2UgPSBbJycsICdBJywgJ0InLCAnQyddW3Byb2ZpbGVCeXRlID4+IDZdO1xuICAgICAgICBjb25zdCBnZW5lcmFsUHJvZmlsZUlkYyA9IHByb2ZpbGVCeXRlICYgMHgxZjtcbiAgICAgICAgY29uc3QgcHJvZmlsZUNvbXBhdCA9IHJlYWRVaW50MzIoaHZjQ0JveCwgMik7XG4gICAgICAgIGNvbnN0IHRpZXJGbGFnID0gKHByb2ZpbGVCeXRlICYgMHgyMCkgPj4gNSA/ICdIJyA6ICdMJztcbiAgICAgICAgY29uc3QgbGV2ZWxJREMgPSBodmNDQm94WzEyXTtcbiAgICAgICAgY29uc3QgY29uc3RyYWludEluZGljYXRvciA9IGh2Y0NCb3guc3ViYXJyYXkoNiwgMTIpO1xuICAgICAgICBjb2RlYyArPSAnLicgKyBwcm9maWxlU3BhY2UgKyBnZW5lcmFsUHJvZmlsZUlkYztcbiAgICAgICAgY29kZWMgKz0gJy4nICsgcHJvZmlsZUNvbXBhdC50b1N0cmluZygxNikudG9VcHBlckNhc2UoKTtcbiAgICAgICAgY29kZWMgKz0gJy4nICsgdGllckZsYWcgKyBsZXZlbElEQztcbiAgICAgICAgbGV0IGNvbnN0cmFpbnRTdHJpbmcgPSAnJztcbiAgICAgICAgZm9yIChsZXQgaSA9IGNvbnN0cmFpbnRJbmRpY2F0b3IubGVuZ3RoOyBpLS07KSB7XG4gICAgICAgICAgY29uc3QgYnl0ZSA9IGNvbnN0cmFpbnRJbmRpY2F0b3JbaV07XG4gICAgICAgICAgaWYgKGJ5dGUgfHwgY29uc3RyYWludFN0cmluZykge1xuICAgICAgICAgICAgY29uc3QgZW5jb2RlZEJ5dGUgPSBieXRlLnRvU3RyaW5nKDE2KS50b1VwcGVyQ2FzZSgpO1xuICAgICAgICAgICAgY29uc3RyYWludFN0cmluZyA9ICcuJyArIGVuY29kZWRCeXRlICsgY29uc3RyYWludFN0cmluZztcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgY29kZWMgKz0gY29uc3RyYWludFN0cmluZztcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgY2FzZSAnZHZoMSc6XG4gICAgY2FzZSAnZHZoZSc6XG4gICAgICB7XG4gICAgICAgIGNvbnN0IGR2Y0NCb3ggPSBmaW5kQm94KHNhbXBsZUVudHJpZXNFbmQsIFsnZHZjQyddKVswXTtcbiAgICAgICAgY29uc3QgcHJvZmlsZSA9IGR2Y0NCb3hbMl0gPj4gMSAmIDB4N2Y7XG4gICAgICAgIGNvbnN0IGxldmVsID0gZHZjQ0JveFsyXSA8PCA1ICYgMHgyMCB8IGR2Y0NCb3hbM10gPj4gMyAmIDB4MWY7XG4gICAgICAgIGNvZGVjICs9ICcuJyArIGFkZExlYWRpbmdaZXJvKHByb2ZpbGUpICsgJy4nICsgYWRkTGVhZGluZ1plcm8obGV2ZWwpO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICBjYXNlICd2cDA5JzpcbiAgICAgIHtcbiAgICAgICAgY29uc3QgdnBjQ0JveCA9IGZpbmRCb3goc2FtcGxlRW50cmllc0VuZCwgWyd2cGNDJ10pWzBdO1xuICAgICAgICBjb25zdCBwcm9maWxlID0gdnBjQ0JveFs0XTtcbiAgICAgICAgY29uc3QgbGV2ZWwgPSB2cGNDQm94WzVdO1xuICAgICAgICBjb25zdCBiaXREZXB0aCA9IHZwY0NCb3hbNl0gPj4gNCAmIDB4MGY7XG4gICAgICAgIGNvZGVjICs9ICcuJyArIGFkZExlYWRpbmdaZXJvKHByb2ZpbGUpICsgJy4nICsgYWRkTGVhZGluZ1plcm8obGV2ZWwpICsgJy4nICsgYWRkTGVhZGluZ1plcm8oYml0RGVwdGgpO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICBjYXNlICdhdjAxJzpcbiAgICAgIHtcbiAgICAgICAgY29uc3QgYXYxQ0JveCA9IGZpbmRCb3goc2FtcGxlRW50cmllc0VuZCwgWydhdjFDJ10pWzBdO1xuICAgICAgICBjb25zdCBwcm9maWxlID0gYXYxQ0JveFsxXSA+Pj4gNTtcbiAgICAgICAgY29uc3QgbGV2ZWwgPSBhdjFDQm94WzFdICYgMHgxZjtcbiAgICAgICAgY29uc3QgdGllckZsYWcgPSBhdjFDQm94WzJdID4+PiA3ID8gJ0gnIDogJ00nO1xuICAgICAgICBjb25zdCBoaWdoQml0RGVwdGggPSAoYXYxQ0JveFsyXSAmIDB4NDApID4+IDY7XG4gICAgICAgIGNvbnN0IHR3ZWx2ZUJpdCA9IChhdjFDQm94WzJdICYgMHgyMCkgPj4gNTtcbiAgICAgICAgY29uc3QgYml0RGVwdGggPSBwcm9maWxlID09PSAyICYmIGhpZ2hCaXREZXB0aCA/IHR3ZWx2ZUJpdCA/IDEyIDogMTAgOiBoaWdoQml0RGVwdGggPyAxMCA6IDg7XG4gICAgICAgIGNvbnN0IG1vbm9jaHJvbWUgPSAoYXYxQ0JveFsyXSAmIDB4MTApID4+IDQ7XG4gICAgICAgIGNvbnN0IGNocm9tYVN1YnNhbXBsaW5nWCA9IChhdjFDQm94WzJdICYgMHgwOCkgPj4gMztcbiAgICAgICAgY29uc3QgY2hyb21hU3Vic2FtcGxpbmdZID0gKGF2MUNCb3hbMl0gJiAweDA0KSA+PiAyO1xuICAgICAgICBjb25zdCBjaHJvbWFTYW1wbGVQb3NpdGlvbiA9IGF2MUNCb3hbMl0gJiAweDAzO1xuICAgICAgICAvLyBUT0RPOiBwYXJzZSBjb2xvcl9kZXNjcmlwdGlvbl9wcmVzZW50X2ZsYWdcbiAgICAgICAgLy8gZGVmYXVsdCBpdCB0byBCVC43MDkvbGltaXRlZCByYW5nZSBmb3Igbm93XG4gICAgICAgIC8vIG1vcmUgaW5mbyBodHRwczovL2FvbWVkaWFjb2RlYy5naXRodWIuaW8vYXYxLWlzb2JtZmYvI2F2MWNvZGVjY29uZmlndXJhdGlvbmJveC1zeW50YXhcbiAgICAgICAgY29uc3QgY29sb3JQcmltYXJpZXMgPSAxO1xuICAgICAgICBjb25zdCB0cmFuc2ZlckNoYXJhY3RlcmlzdGljcyA9IDE7XG4gICAgICAgIGNvbnN0IG1hdHJpeENvZWZmaWNpZW50cyA9IDE7XG4gICAgICAgIGNvbnN0IHZpZGVvRnVsbFJhbmdlRmxhZyA9IDA7XG4gICAgICAgIGNvZGVjICs9ICcuJyArIHByb2ZpbGUgKyAnLicgKyBhZGRMZWFkaW5nWmVybyhsZXZlbCkgKyB0aWVyRmxhZyArICcuJyArIGFkZExlYWRpbmdaZXJvKGJpdERlcHRoKSArICcuJyArIG1vbm9jaHJvbWUgKyAnLicgKyBjaHJvbWFTdWJzYW1wbGluZ1ggKyBjaHJvbWFTdWJzYW1wbGluZ1kgKyBjaHJvbWFTYW1wbGVQb3NpdGlvbiArICcuJyArIGFkZExlYWRpbmdaZXJvKGNvbG9yUHJpbWFyaWVzKSArICcuJyArIGFkZExlYWRpbmdaZXJvKHRyYW5zZmVyQ2hhcmFjdGVyaXN0aWNzKSArICcuJyArIGFkZExlYWRpbmdaZXJvKG1hdHJpeENvZWZmaWNpZW50cykgKyAnLicgKyB2aWRlb0Z1bGxSYW5nZUZsYWc7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICB9XG4gIHJldHVybiB7XG4gICAgY29kZWMsXG4gICAgZW5jcnlwdGVkXG4gIH07XG59XG5mdW5jdGlvbiBza2lwQkVSSW50ZWdlcihieXRlcywgaSkge1xuICBjb25zdCBsaW1pdCA9IGkgKyA1O1xuICB3aGlsZSAoYnl0ZXNbaSsrXSAmIDB4ODAgJiYgaSA8IGxpbWl0KSB7fVxuICByZXR1cm4gaTtcbn1cbmZ1bmN0aW9uIHRvSGV4KHgpIHtcbiAgcmV0dXJuICgnMCcgKyB4LnRvU3RyaW5nKDE2KS50b1VwcGVyQ2FzZSgpKS5zbGljZSgtMik7XG59XG5mdW5jdGlvbiBhZGRMZWFkaW5nWmVybyhudW0pIHtcbiAgcmV0dXJuIChudW0gPCAxMCA/ICcwJyA6ICcnKSArIG51bTtcbn1cbmZ1bmN0aW9uIHBhdGNoRW5jeXB0aW9uRGF0YShpbml0U2VnbWVudCwgZGVjcnlwdGRhdGEpIHtcbiAgaWYgKCFpbml0U2VnbWVudCB8fCAhZGVjcnlwdGRhdGEpIHtcbiAgICByZXR1cm4gaW5pdFNlZ21lbnQ7XG4gIH1cbiAgY29uc3Qga2V5SWQgPSBkZWNyeXB0ZGF0YS5rZXlJZDtcbiAgaWYgKGtleUlkICYmIGRlY3J5cHRkYXRhLmlzQ29tbW9uRW5jcnlwdGlvbikge1xuICAgIGNvbnN0IHRyYWtzID0gZmluZEJveChpbml0U2VnbWVudCwgWydtb292JywgJ3RyYWsnXSk7XG4gICAgdHJha3MuZm9yRWFjaCh0cmFrID0+IHtcbiAgICAgIGNvbnN0IHN0c2QgPSBmaW5kQm94KHRyYWssIFsnbWRpYScsICdtaW5mJywgJ3N0YmwnLCAnc3RzZCddKVswXTtcblxuICAgICAgLy8gc2tpcCB0aGUgc2FtcGxlIGVudHJ5IGNvdW50XG4gICAgICBjb25zdCBzYW1wbGVFbnRyaWVzID0gc3RzZC5zdWJhcnJheSg4KTtcbiAgICAgIGxldCBlbmNCb3hlcyA9IGZpbmRCb3goc2FtcGxlRW50cmllcywgWydlbmNhJ10pO1xuICAgICAgY29uc3QgaXNBdWRpbyA9IGVuY0JveGVzLmxlbmd0aCA+IDA7XG4gICAgICBpZiAoIWlzQXVkaW8pIHtcbiAgICAgICAgZW5jQm94ZXMgPSBmaW5kQm94KHNhbXBsZUVudHJpZXMsIFsnZW5jdiddKTtcbiAgICAgIH1cbiAgICAgIGVuY0JveGVzLmZvckVhY2goZW5jID0+IHtcbiAgICAgICAgY29uc3QgZW5jQm94Q2hpbGRyZW4gPSBpc0F1ZGlvID8gZW5jLnN1YmFycmF5KDI4KSA6IGVuYy5zdWJhcnJheSg3OCk7XG4gICAgICAgIGNvbnN0IHNpbmZCb3hlcyA9IGZpbmRCb3goZW5jQm94Q2hpbGRyZW4sIFsnc2luZiddKTtcbiAgICAgICAgc2luZkJveGVzLmZvckVhY2goc2luZiA9PiB7XG4gICAgICAgICAgY29uc3QgdGVuYyA9IHBhcnNlU2luZihzaW5mKTtcbiAgICAgICAgICBpZiAodGVuYykge1xuICAgICAgICAgICAgLy8gTG9vayBmb3IgZGVmYXVsdCBrZXkgaWQgKGtleUlEIG9mZnNldCBpcyBhbHdheXMgOCB3aXRoaW4gdGhlIHRlbmMgYm94KTpcbiAgICAgICAgICAgIGNvbnN0IHRlbmNLZXlJZCA9IHRlbmMuc3ViYXJyYXkoOCwgMjQpO1xuICAgICAgICAgICAgaWYgKCF0ZW5jS2V5SWQuc29tZShiID0+IGIgIT09IDApKSB7XG4gICAgICAgICAgICAgIGxvZ2dlci5sb2coYFtlbWVdIFBhdGNoaW5nIGtleUlkIGluICdlbmMke2lzQXVkaW8gPyAnYScgOiAndid9PnNpbmY+PnRlbmMnIGJveDogJHtIZXguaGV4RHVtcCh0ZW5jS2V5SWQpfSAtPiAke0hleC5oZXhEdW1wKGtleUlkKX1gKTtcbiAgICAgICAgICAgICAgdGVuYy5zZXQoa2V5SWQsIDgpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuICByZXR1cm4gaW5pdFNlZ21lbnQ7XG59XG5mdW5jdGlvbiBwYXJzZVNpbmYoc2luZikge1xuICBjb25zdCBzY2htID0gZmluZEJveChzaW5mLCBbJ3NjaG0nXSlbMF07XG4gIGlmIChzY2htKSB7XG4gICAgY29uc3Qgc2NoZW1lID0gYmluMnN0cihzY2htLnN1YmFycmF5KDQsIDgpKTtcbiAgICBpZiAoc2NoZW1lID09PSAnY2JjcycgfHwgc2NoZW1lID09PSAnY2VuYycpIHtcbiAgICAgIHJldHVybiBmaW5kQm94KHNpbmYsIFsnc2NoaScsICd0ZW5jJ10pWzBdO1xuICAgIH1cbiAgfVxuICByZXR1cm4gbnVsbDtcbn1cblxuLyoqXG4gKiBEZXRlcm1pbmUgdGhlIGJhc2UgbWVkaWEgZGVjb2RlIHN0YXJ0IHRpbWUsIGluIHNlY29uZHMsIGZvciBhbiBNUDRcbiAqIGZyYWdtZW50LiBJZiBtdWx0aXBsZSBmcmFnbWVudHMgYXJlIHNwZWNpZmllZCwgdGhlIGVhcmxpZXN0IHRpbWUgaXNcbiAqIHJldHVybmVkLlxuICpcbiAqIFRoZSBiYXNlIG1lZGlhIGRlY29kZSB0aW1lIGNhbiBiZSBwYXJzZWQgZnJvbSB0cmFjayBmcmFnbWVudFxuICogbWV0YWRhdGE6XG4gKiBgYGBcbiAqIG1vb2YgPiB0cmFmID4gdGZkdC5iYXNlTWVkaWFEZWNvZGVUaW1lXG4gKiBgYGBcbiAqIEl0IHJlcXVpcmVzIHRoZSB0aW1lc2NhbGUgdmFsdWUgZnJvbSB0aGUgbWRoZCB0byBpbnRlcnByZXQuXG4gKlxuICogQHBhcmFtIGluaXREYXRhIC0gYSBoYXNoIG9mIHRyYWNrIHR5cGUgdG8gdGltZXNjYWxlIHZhbHVlc1xuICogQHBhcmFtIGZtcDQgLSB0aGUgYnl0ZXMgb2YgdGhlIG1wNCBmcmFnbWVudFxuICogQHJldHVybnMgdGhlIGVhcmxpZXN0IGJhc2UgbWVkaWEgZGVjb2RlIHN0YXJ0IHRpbWUgZm9yIHRoZVxuICogZnJhZ21lbnQsIGluIHNlY29uZHNcbiAqL1xuZnVuY3Rpb24gZ2V0U3RhcnREVFMoaW5pdERhdGEsIGZtcDQpIHtcbiAgLy8gd2UgbmVlZCBpbmZvIGZyb20gdHdvIGNoaWxkcmVuIG9mIGVhY2ggdHJhY2sgZnJhZ21lbnQgYm94XG4gIHJldHVybiBmaW5kQm94KGZtcDQsIFsnbW9vZicsICd0cmFmJ10pLnJlZHVjZSgocmVzdWx0LCB0cmFmKSA9PiB7XG4gICAgY29uc3QgdGZkdCA9IGZpbmRCb3godHJhZiwgWyd0ZmR0J10pWzBdO1xuICAgIGNvbnN0IHZlcnNpb24gPSB0ZmR0WzBdO1xuICAgIGNvbnN0IHN0YXJ0ID0gZmluZEJveCh0cmFmLCBbJ3RmaGQnXSkucmVkdWNlKChyZXN1bHQsIHRmaGQpID0+IHtcbiAgICAgIC8vIGdldCB0aGUgdHJhY2sgaWQgZnJvbSB0aGUgdGZoZFxuICAgICAgY29uc3QgaWQgPSByZWFkVWludDMyKHRmaGQsIDQpO1xuICAgICAgY29uc3QgdHJhY2sgPSBpbml0RGF0YVtpZF07XG4gICAgICBpZiAodHJhY2spIHtcbiAgICAgICAgbGV0IGJhc2VUaW1lID0gcmVhZFVpbnQzMih0ZmR0LCA0KTtcbiAgICAgICAgaWYgKHZlcnNpb24gPT09IDEpIHtcbiAgICAgICAgICAvLyBJZiB2YWx1ZSBpcyB0b28gbGFyZ2UsIGFzc3VtZSBzaWduZWQgNjQtYml0LiBOZWdhdGl2ZSB0cmFjayBmcmFnbWVudCBkZWNvZGUgdGltZXMgYXJlIGludmFsaWQsIGJ1dCB0aGV5IGV4aXN0IGluIHRoZSB3aWxkLlxuICAgICAgICAgIC8vIFRoaXMgcHJldmVudHMgbGFyZ2UgdmFsdWVzIGZyb20gYmVpbmcgdXNlZCBmb3IgaW5pdFBUUywgd2hpY2ggY2FuIGNhdXNlIHBsYXlsaXN0IHN5bmMgaXNzdWVzLlxuICAgICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS92aWRlby1kZXYvaGxzLmpzL2lzc3Vlcy81MzAzXG4gICAgICAgICAgaWYgKGJhc2VUaW1lID09PSBVSU5UMzJfTUFYJDEpIHtcbiAgICAgICAgICAgIGxvZ2dlci53YXJuKGBbbXA0LWRlbXV4ZXJdOiBJZ25vcmluZyBhc3N1bWVkIGludmFsaWQgc2lnbmVkIDY0LWJpdCB0cmFjayBmcmFnbWVudCBkZWNvZGUgdGltZWApO1xuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgICB9XG4gICAgICAgICAgYmFzZVRpbWUgKj0gVUlOVDMyX01BWCQxICsgMTtcbiAgICAgICAgICBiYXNlVGltZSArPSByZWFkVWludDMyKHRmZHQsIDgpO1xuICAgICAgICB9XG4gICAgICAgIC8vIGFzc3VtZSBhIDkwa0h6IGNsb2NrIGlmIG5vIHRpbWVzY2FsZSB3YXMgc3BlY2lmaWVkXG4gICAgICAgIGNvbnN0IHNjYWxlID0gdHJhY2sudGltZXNjYWxlIHx8IDkwZTM7XG4gICAgICAgIC8vIGNvbnZlcnQgYmFzZSB0aW1lIHRvIHNlY29uZHNcbiAgICAgICAgY29uc3Qgc3RhcnRUaW1lID0gYmFzZVRpbWUgLyBzY2FsZTtcbiAgICAgICAgaWYgKGlzRmluaXRlTnVtYmVyKHN0YXJ0VGltZSkgJiYgKHJlc3VsdCA9PT0gbnVsbCB8fCBzdGFydFRpbWUgPCByZXN1bHQpKSB7XG4gICAgICAgICAgcmV0dXJuIHN0YXJ0VGltZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9LCBudWxsKTtcbiAgICBpZiAoc3RhcnQgIT09IG51bGwgJiYgaXNGaW5pdGVOdW1iZXIoc3RhcnQpICYmIChyZXN1bHQgPT09IG51bGwgfHwgc3RhcnQgPCByZXN1bHQpKSB7XG4gICAgICByZXR1cm4gc3RhcnQ7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH0sIG51bGwpO1xufVxuXG4vKlxuICBGb3IgUmVmZXJlbmNlOlxuICBhbGlnbmVkKDgpIGNsYXNzIFRyYWNrRnJhZ21lbnRIZWFkZXJCb3hcbiAgICAgICAgICAgZXh0ZW5kcyBGdWxsQm94KOKAmHRmaGTigJksIDAsIHRmX2ZsYWdzKXtcbiAgICAgdW5zaWduZWQgaW50KDMyKSAgdHJhY2tfSUQ7XG4gICAgIC8vIGFsbCB0aGUgZm9sbG93aW5nIGFyZSBvcHRpb25hbCBmaWVsZHNcbiAgICAgdW5zaWduZWQgaW50KDY0KSAgYmFzZV9kYXRhX29mZnNldDtcbiAgICAgdW5zaWduZWQgaW50KDMyKSAgc2FtcGxlX2Rlc2NyaXB0aW9uX2luZGV4O1xuICAgICB1bnNpZ25lZCBpbnQoMzIpICBkZWZhdWx0X3NhbXBsZV9kdXJhdGlvbjtcbiAgICAgdW5zaWduZWQgaW50KDMyKSAgZGVmYXVsdF9zYW1wbGVfc2l6ZTtcbiAgICAgdW5zaWduZWQgaW50KDMyKSAgZGVmYXVsdF9zYW1wbGVfZmxhZ3NcbiAgfVxuICovXG5mdW5jdGlvbiBnZXREdXJhdGlvbihkYXRhLCBpbml0RGF0YSkge1xuICBsZXQgcmF3RHVyYXRpb24gPSAwO1xuICBsZXQgdmlkZW9EdXJhdGlvbiA9IDA7XG4gIGxldCBhdWRpb0R1cmF0aW9uID0gMDtcbiAgY29uc3QgdHJhZnMgPSBmaW5kQm94KGRhdGEsIFsnbW9vZicsICd0cmFmJ10pO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IHRyYWZzLmxlbmd0aDsgaSsrKSB7XG4gICAgY29uc3QgdHJhZiA9IHRyYWZzW2ldO1xuICAgIC8vIFRoZXJlIGlzIG9ubHkgb25lIHRmaGQgJiB0cnVuIHBlciB0cmFmXG4gICAgLy8gVGhpcyBpcyB0cnVlIGZvciBDTUFGIHN0eWxlIGNvbnRlbnQsIGFuZCB3ZSBzaG91bGQgcGVyaGFwcyBjaGVjayB0aGUgZnR5cFxuICAgIC8vIGFuZCBvbmx5IGxvb2sgZm9yIGEgc2luZ2xlIHRydW4gdGhlbiwgYnV0IGZvciBJU09CTUZGIHdlIHNob3VsZCBjaGVja1xuICAgIC8vIGZvciBtdWx0aXBsZSB0cmFjayBydW5zLlxuICAgIGNvbnN0IHRmaGQgPSBmaW5kQm94KHRyYWYsIFsndGZoZCddKVswXTtcbiAgICAvLyBnZXQgdGhlIHRyYWNrIGlkIGZyb20gdGhlIHRmaGRcbiAgICBjb25zdCBpZCA9IHJlYWRVaW50MzIodGZoZCwgNCk7XG4gICAgY29uc3QgdHJhY2sgPSBpbml0RGF0YVtpZF07XG4gICAgaWYgKCF0cmFjaykge1xuICAgICAgY29udGludWU7XG4gICAgfVxuICAgIGNvbnN0IHRyYWNrRGVmYXVsdCA9IHRyYWNrLmRlZmF1bHQ7XG4gICAgY29uc3QgdGZoZEZsYWdzID0gcmVhZFVpbnQzMih0ZmhkLCAwKSB8ICh0cmFja0RlZmF1bHQgPT0gbnVsbCA/IHZvaWQgMCA6IHRyYWNrRGVmYXVsdC5mbGFncyk7XG4gICAgbGV0IHNhbXBsZUR1cmF0aW9uID0gdHJhY2tEZWZhdWx0ID09IG51bGwgPyB2b2lkIDAgOiB0cmFja0RlZmF1bHQuZHVyYXRpb247XG4gICAgaWYgKHRmaGRGbGFncyAmIDB4MDAwMDA4KSB7XG4gICAgICAvLyAweDAwMDAwOCBpbmRpY2F0ZXMgdGhlIHByZXNlbmNlIG9mIHRoZSBkZWZhdWx0X3NhbXBsZV9kdXJhdGlvbiBmaWVsZFxuICAgICAgaWYgKHRmaGRGbGFncyAmIDB4MDAwMDAyKSB7XG4gICAgICAgIC8vIDB4MDAwMDAyIGluZGljYXRlcyB0aGUgcHJlc2VuY2Ugb2YgdGhlIHNhbXBsZV9kZXNjcmlwdGlvbl9pbmRleCBmaWVsZCwgd2hpY2ggcHJlY2VkZXMgZGVmYXVsdF9zYW1wbGVfZHVyYXRpb25cbiAgICAgICAgLy8gSWYgcHJlc2VudCwgdGhlIGRlZmF1bHRfc2FtcGxlX2R1cmF0aW9uIGV4aXN0cyBhdCBieXRlIG9mZnNldCAxMlxuICAgICAgICBzYW1wbGVEdXJhdGlvbiA9IHJlYWRVaW50MzIodGZoZCwgMTIpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gT3RoZXJ3aXNlLCB0aGUgZHVyYXRpb24gaXMgYXQgYnl0ZSBvZmZzZXQgOFxuICAgICAgICBzYW1wbGVEdXJhdGlvbiA9IHJlYWRVaW50MzIodGZoZCwgOCk7XG4gICAgICB9XG4gICAgfVxuICAgIC8vIGFzc3VtZSBhIDkwa0h6IGNsb2NrIGlmIG5vIHRpbWVzY2FsZSB3YXMgc3BlY2lmaWVkXG4gICAgY29uc3QgdGltZXNjYWxlID0gdHJhY2sudGltZXNjYWxlIHx8IDkwZTM7XG4gICAgY29uc3QgdHJ1bnMgPSBmaW5kQm94KHRyYWYsIFsndHJ1biddKTtcbiAgICBmb3IgKGxldCBqID0gMDsgaiA8IHRydW5zLmxlbmd0aDsgaisrKSB7XG4gICAgICByYXdEdXJhdGlvbiA9IGNvbXB1dGVSYXdEdXJhdGlvbkZyb21TYW1wbGVzKHRydW5zW2pdKTtcbiAgICAgIGlmICghcmF3RHVyYXRpb24gJiYgc2FtcGxlRHVyYXRpb24pIHtcbiAgICAgICAgY29uc3Qgc2FtcGxlQ291bnQgPSByZWFkVWludDMyKHRydW5zW2pdLCA0KTtcbiAgICAgICAgcmF3RHVyYXRpb24gPSBzYW1wbGVEdXJhdGlvbiAqIHNhbXBsZUNvdW50O1xuICAgICAgfVxuICAgICAgaWYgKHRyYWNrLnR5cGUgPT09IEVsZW1lbnRhcnlTdHJlYW1UeXBlcy5WSURFTykge1xuICAgICAgICB2aWRlb0R1cmF0aW9uICs9IHJhd0R1cmF0aW9uIC8gdGltZXNjYWxlO1xuICAgICAgfSBlbHNlIGlmICh0cmFjay50eXBlID09PSBFbGVtZW50YXJ5U3RyZWFtVHlwZXMuQVVESU8pIHtcbiAgICAgICAgYXVkaW9EdXJhdGlvbiArPSByYXdEdXJhdGlvbiAvIHRpbWVzY2FsZTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgaWYgKHZpZGVvRHVyYXRpb24gPT09IDAgJiYgYXVkaW9EdXJhdGlvbiA9PT0gMCkge1xuICAgIC8vIElmIGR1cmF0aW9uIHNhbXBsZXMgYXJlIG5vdCBhdmFpbGFibGUgaW4gdGhlIHRyYWYgdXNlIHNpZHggc3Vic2VnbWVudF9kdXJhdGlvblxuICAgIGxldCBzaWR4TWluU3RhcnQgPSBJbmZpbml0eTtcbiAgICBsZXQgc2lkeE1heEVuZCA9IDA7XG4gICAgbGV0IHNpZHhEdXJhdGlvbiA9IDA7XG4gICAgY29uc3Qgc2lkeHMgPSBmaW5kQm94KGRhdGEsIFsnc2lkeCddKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHNpZHhzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBzaWR4ID0gcGFyc2VTZWdtZW50SW5kZXgoc2lkeHNbaV0pO1xuICAgICAgaWYgKHNpZHggIT0gbnVsbCAmJiBzaWR4LnJlZmVyZW5jZXMpIHtcbiAgICAgICAgc2lkeE1pblN0YXJ0ID0gTWF0aC5taW4oc2lkeE1pblN0YXJ0LCBzaWR4LmVhcmxpZXN0UHJlc2VudGF0aW9uVGltZSAvIHNpZHgudGltZXNjYWxlKTtcbiAgICAgICAgY29uc3Qgc3ViU2VnbWVudER1cmF0aW9uID0gc2lkeC5yZWZlcmVuY2VzLnJlZHVjZSgoZHVyLCByZWYpID0+IGR1ciArIHJlZi5pbmZvLmR1cmF0aW9uIHx8IDAsIDApO1xuICAgICAgICBzaWR4TWF4RW5kID0gTWF0aC5tYXgoc2lkeE1heEVuZCwgc3ViU2VnbWVudER1cmF0aW9uICsgc2lkeC5lYXJsaWVzdFByZXNlbnRhdGlvblRpbWUgLyBzaWR4LnRpbWVzY2FsZSk7XG4gICAgICAgIHNpZHhEdXJhdGlvbiA9IHNpZHhNYXhFbmQgLSBzaWR4TWluU3RhcnQ7XG4gICAgICB9XG4gICAgfVxuICAgIGlmIChzaWR4RHVyYXRpb24gJiYgaXNGaW5pdGVOdW1iZXIoc2lkeER1cmF0aW9uKSkge1xuICAgICAgcmV0dXJuIHNpZHhEdXJhdGlvbjtcbiAgICB9XG4gIH1cbiAgaWYgKHZpZGVvRHVyYXRpb24pIHtcbiAgICByZXR1cm4gdmlkZW9EdXJhdGlvbjtcbiAgfVxuICByZXR1cm4gYXVkaW9EdXJhdGlvbjtcbn1cblxuLypcbiAgRm9yIFJlZmVyZW5jZTpcbiAgYWxpZ25lZCg4KSBjbGFzcyBUcmFja1J1bkJveFxuICAgICAgICAgICBleHRlbmRzIEZ1bGxCb3go4oCYdHJ1buKAmSwgdmVyc2lvbiwgdHJfZmxhZ3MpIHtcbiAgICAgdW5zaWduZWQgaW50KDMyKSAgc2FtcGxlX2NvdW50O1xuICAgICAvLyB0aGUgZm9sbG93aW5nIGFyZSBvcHRpb25hbCBmaWVsZHNcbiAgICAgc2lnbmVkIGludCgzMikgZGF0YV9vZmZzZXQ7XG4gICAgIHVuc2lnbmVkIGludCgzMikgIGZpcnN0X3NhbXBsZV9mbGFncztcbiAgICAgLy8gYWxsIGZpZWxkcyBpbiB0aGUgZm9sbG93aW5nIGFycmF5IGFyZSBvcHRpb25hbFxuICAgICB7XG4gICAgICAgIHVuc2lnbmVkIGludCgzMikgIHNhbXBsZV9kdXJhdGlvbjtcbiAgICAgICAgdW5zaWduZWQgaW50KDMyKSAgc2FtcGxlX3NpemU7XG4gICAgICAgIHVuc2lnbmVkIGludCgzMikgIHNhbXBsZV9mbGFnc1xuICAgICAgICBpZiAodmVyc2lvbiA9PSAwKVxuICAgICAgICAgICB7IHVuc2lnbmVkIGludCgzMilcbiAgICAgICAgZWxzZVxuICAgICAgICAgICB7IHNpZ25lZCBpbnQoMzIpXG4gICAgIH1bIHNhbXBsZV9jb3VudCBdXG4gIH1cbiAqL1xuZnVuY3Rpb24gY29tcHV0ZVJhd0R1cmF0aW9uRnJvbVNhbXBsZXModHJ1bikge1xuICBjb25zdCBmbGFncyA9IHJlYWRVaW50MzIodHJ1biwgMCk7XG4gIC8vIEZsYWdzIGFyZSBhdCBvZmZzZXQgMCwgbm9uLW9wdGlvbmFsIHNhbXBsZV9jb3VudCBpcyBhdCBvZmZzZXQgNC4gVGhlcmVmb3JlIHdlIHN0YXJ0IDggYnl0ZXMgaW4uXG4gIC8vIEVhY2ggZmllbGQgaXMgYW4gaW50MzIsIHdoaWNoIGlzIDQgYnl0ZXNcbiAgbGV0IG9mZnNldCA9IDg7XG4gIC8vIGRhdGEtb2Zmc2V0LXByZXNlbnQgZmxhZ1xuICBpZiAoZmxhZ3MgJiAweDAwMDAwMSkge1xuICAgIG9mZnNldCArPSA0O1xuICB9XG4gIC8vIGZpcnN0LXNhbXBsZS1mbGFncy1wcmVzZW50IGZsYWdcbiAgaWYgKGZsYWdzICYgMHgwMDAwMDQpIHtcbiAgICBvZmZzZXQgKz0gNDtcbiAgfVxuICBsZXQgZHVyYXRpb24gPSAwO1xuICBjb25zdCBzYW1wbGVDb3VudCA9IHJlYWRVaW50MzIodHJ1biwgNCk7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgc2FtcGxlQ291bnQ7IGkrKykge1xuICAgIC8vIHNhbXBsZS1kdXJhdGlvbi1wcmVzZW50IGZsYWdcbiAgICBpZiAoZmxhZ3MgJiAweDAwMDEwMCkge1xuICAgICAgY29uc3Qgc2FtcGxlRHVyYXRpb24gPSByZWFkVWludDMyKHRydW4sIG9mZnNldCk7XG4gICAgICBkdXJhdGlvbiArPSBzYW1wbGVEdXJhdGlvbjtcbiAgICAgIG9mZnNldCArPSA0O1xuICAgIH1cbiAgICAvLyBzYW1wbGUtc2l6ZS1wcmVzZW50IGZsYWdcbiAgICBpZiAoZmxhZ3MgJiAweDAwMDIwMCkge1xuICAgICAgb2Zmc2V0ICs9IDQ7XG4gICAgfVxuICAgIC8vIHNhbXBsZS1mbGFncy1wcmVzZW50IGZsYWdcbiAgICBpZiAoZmxhZ3MgJiAweDAwMDQwMCkge1xuICAgICAgb2Zmc2V0ICs9IDQ7XG4gICAgfVxuICAgIC8vIHNhbXBsZS1jb21wb3NpdGlvbi10aW1lLW9mZnNldHMtcHJlc2VudCBmbGFnXG4gICAgaWYgKGZsYWdzICYgMHgwMDA4MDApIHtcbiAgICAgIG9mZnNldCArPSA0O1xuICAgIH1cbiAgfVxuICByZXR1cm4gZHVyYXRpb247XG59XG5mdW5jdGlvbiBvZmZzZXRTdGFydERUUyhpbml0RGF0YSwgZm1wNCwgdGltZU9mZnNldCkge1xuICBmaW5kQm94KGZtcDQsIFsnbW9vZicsICd0cmFmJ10pLmZvckVhY2godHJhZiA9PiB7XG4gICAgZmluZEJveCh0cmFmLCBbJ3RmaGQnXSkuZm9yRWFjaCh0ZmhkID0+IHtcbiAgICAgIC8vIGdldCB0aGUgdHJhY2sgaWQgZnJvbSB0aGUgdGZoZFxuICAgICAgY29uc3QgaWQgPSByZWFkVWludDMyKHRmaGQsIDQpO1xuICAgICAgY29uc3QgdHJhY2sgPSBpbml0RGF0YVtpZF07XG4gICAgICBpZiAoIXRyYWNrKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIC8vIGFzc3VtZSBhIDkwa0h6IGNsb2NrIGlmIG5vIHRpbWVzY2FsZSB3YXMgc3BlY2lmaWVkXG4gICAgICBjb25zdCB0aW1lc2NhbGUgPSB0cmFjay50aW1lc2NhbGUgfHwgOTBlMztcbiAgICAgIC8vIGdldCB0aGUgYmFzZSBtZWRpYSBkZWNvZGUgdGltZSBmcm9tIHRoZSB0ZmR0XG4gICAgICBmaW5kQm94KHRyYWYsIFsndGZkdCddKS5mb3JFYWNoKHRmZHQgPT4ge1xuICAgICAgICBjb25zdCB2ZXJzaW9uID0gdGZkdFswXTtcbiAgICAgICAgY29uc3Qgb2Zmc2V0ID0gdGltZU9mZnNldCAqIHRpbWVzY2FsZTtcbiAgICAgICAgaWYgKG9mZnNldCkge1xuICAgICAgICAgIGxldCBiYXNlTWVkaWFEZWNvZGVUaW1lID0gcmVhZFVpbnQzMih0ZmR0LCA0KTtcbiAgICAgICAgICBpZiAodmVyc2lvbiA9PT0gMCkge1xuICAgICAgICAgICAgYmFzZU1lZGlhRGVjb2RlVGltZSAtPSBvZmZzZXQ7XG4gICAgICAgICAgICBiYXNlTWVkaWFEZWNvZGVUaW1lID0gTWF0aC5tYXgoYmFzZU1lZGlhRGVjb2RlVGltZSwgMCk7XG4gICAgICAgICAgICB3cml0ZVVpbnQzMih0ZmR0LCA0LCBiYXNlTWVkaWFEZWNvZGVUaW1lKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgYmFzZU1lZGlhRGVjb2RlVGltZSAqPSBNYXRoLnBvdygyLCAzMik7XG4gICAgICAgICAgICBiYXNlTWVkaWFEZWNvZGVUaW1lICs9IHJlYWRVaW50MzIodGZkdCwgOCk7XG4gICAgICAgICAgICBiYXNlTWVkaWFEZWNvZGVUaW1lIC09IG9mZnNldDtcbiAgICAgICAgICAgIGJhc2VNZWRpYURlY29kZVRpbWUgPSBNYXRoLm1heChiYXNlTWVkaWFEZWNvZGVUaW1lLCAwKTtcbiAgICAgICAgICAgIGNvbnN0IHVwcGVyID0gTWF0aC5mbG9vcihiYXNlTWVkaWFEZWNvZGVUaW1lIC8gKFVJTlQzMl9NQVgkMSArIDEpKTtcbiAgICAgICAgICAgIGNvbnN0IGxvd2VyID0gTWF0aC5mbG9vcihiYXNlTWVkaWFEZWNvZGVUaW1lICUgKFVJTlQzMl9NQVgkMSArIDEpKTtcbiAgICAgICAgICAgIHdyaXRlVWludDMyKHRmZHQsIDQsIHVwcGVyKTtcbiAgICAgICAgICAgIHdyaXRlVWludDMyKHRmZHQsIDgsIGxvd2VyKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH0pO1xuICB9KTtcbn1cblxuLy8gVE9ETzogQ2hlY2sgaWYgdGhlIGxhc3QgbW9vZittZGF0IHBhaXIgaXMgcGFydCBvZiB0aGUgdmFsaWQgcmFuZ2VcbmZ1bmN0aW9uIHNlZ21lbnRWYWxpZFJhbmdlKGRhdGEpIHtcbiAgY29uc3Qgc2VnbWVudGVkUmFuZ2UgPSB7XG4gICAgdmFsaWQ6IG51bGwsXG4gICAgcmVtYWluZGVyOiBudWxsXG4gIH07XG4gIGNvbnN0IG1vb2ZzID0gZmluZEJveChkYXRhLCBbJ21vb2YnXSk7XG4gIGlmIChtb29mcy5sZW5ndGggPCAyKSB7XG4gICAgc2VnbWVudGVkUmFuZ2UucmVtYWluZGVyID0gZGF0YTtcbiAgICByZXR1cm4gc2VnbWVudGVkUmFuZ2U7XG4gIH1cbiAgY29uc3QgbGFzdCA9IG1vb2ZzW21vb2ZzLmxlbmd0aCAtIDFdO1xuICAvLyBPZmZzZXQgYnkgOCBieXRlczsgZmluZEJveCBvZmZzZXRzIHRoZSBzdGFydCBieSBhcyBtdWNoXG4gIHNlZ21lbnRlZFJhbmdlLnZhbGlkID0gc2xpY2VVaW50OChkYXRhLCAwLCBsYXN0LmJ5dGVPZmZzZXQgLSA4KTtcbiAgc2VnbWVudGVkUmFuZ2UucmVtYWluZGVyID0gc2xpY2VVaW50OChkYXRhLCBsYXN0LmJ5dGVPZmZzZXQgLSA4KTtcbiAgcmV0dXJuIHNlZ21lbnRlZFJhbmdlO1xufVxuZnVuY3Rpb24gYXBwZW5kVWludDhBcnJheShkYXRhMSwgZGF0YTIpIHtcbiAgY29uc3QgdGVtcCA9IG5ldyBVaW50OEFycmF5KGRhdGExLmxlbmd0aCArIGRhdGEyLmxlbmd0aCk7XG4gIHRlbXAuc2V0KGRhdGExKTtcbiAgdGVtcC5zZXQoZGF0YTIsIGRhdGExLmxlbmd0aCk7XG4gIHJldHVybiB0ZW1wO1xufVxuZnVuY3Rpb24gcGFyc2VTYW1wbGVzKHRpbWVPZmZzZXQsIHRyYWNrKSB7XG4gIGNvbnN0IHNlaVNhbXBsZXMgPSBbXTtcbiAgY29uc3QgdmlkZW9EYXRhID0gdHJhY2suc2FtcGxlcztcbiAgY29uc3QgdGltZXNjYWxlID0gdHJhY2sudGltZXNjYWxlO1xuICBjb25zdCB0cmFja0lkID0gdHJhY2suaWQ7XG4gIGxldCBpc0hFVkNGbGF2b3IgPSBmYWxzZTtcbiAgY29uc3QgbW9vZnMgPSBmaW5kQm94KHZpZGVvRGF0YSwgWydtb29mJ10pO1xuICBtb29mcy5tYXAobW9vZiA9PiB7XG4gICAgY29uc3QgbW9vZk9mZnNldCA9IG1vb2YuYnl0ZU9mZnNldCAtIDg7XG4gICAgY29uc3QgdHJhZnMgPSBmaW5kQm94KG1vb2YsIFsndHJhZiddKTtcbiAgICB0cmFmcy5tYXAodHJhZiA9PiB7XG4gICAgICAvLyBnZXQgdGhlIGJhc2UgbWVkaWEgZGVjb2RlIHRpbWUgZnJvbSB0aGUgdGZkdFxuICAgICAgY29uc3QgYmFzZVRpbWUgPSBmaW5kQm94KHRyYWYsIFsndGZkdCddKS5tYXAodGZkdCA9PiB7XG4gICAgICAgIGNvbnN0IHZlcnNpb24gPSB0ZmR0WzBdO1xuICAgICAgICBsZXQgcmVzdWx0ID0gcmVhZFVpbnQzMih0ZmR0LCA0KTtcbiAgICAgICAgaWYgKHZlcnNpb24gPT09IDEpIHtcbiAgICAgICAgICByZXN1bHQgKj0gTWF0aC5wb3coMiwgMzIpO1xuICAgICAgICAgIHJlc3VsdCArPSByZWFkVWludDMyKHRmZHQsIDgpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiByZXN1bHQgLyB0aW1lc2NhbGU7XG4gICAgICB9KVswXTtcbiAgICAgIGlmIChiYXNlVGltZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRpbWVPZmZzZXQgPSBiYXNlVGltZTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBmaW5kQm94KHRyYWYsIFsndGZoZCddKS5tYXAodGZoZCA9PiB7XG4gICAgICAgIGNvbnN0IGlkID0gcmVhZFVpbnQzMih0ZmhkLCA0KTtcbiAgICAgICAgY29uc3QgdGZoZEZsYWdzID0gcmVhZFVpbnQzMih0ZmhkLCAwKSAmIDB4ZmZmZmZmO1xuICAgICAgICBjb25zdCBiYXNlRGF0YU9mZnNldFByZXNlbnQgPSAodGZoZEZsYWdzICYgMHgwMDAwMDEpICE9PSAwO1xuICAgICAgICBjb25zdCBzYW1wbGVEZXNjcmlwdGlvbkluZGV4UHJlc2VudCA9ICh0ZmhkRmxhZ3MgJiAweDAwMDAwMikgIT09IDA7XG4gICAgICAgIGNvbnN0IGRlZmF1bHRTYW1wbGVEdXJhdGlvblByZXNlbnQgPSAodGZoZEZsYWdzICYgMHgwMDAwMDgpICE9PSAwO1xuICAgICAgICBsZXQgZGVmYXVsdFNhbXBsZUR1cmF0aW9uID0gMDtcbiAgICAgICAgY29uc3QgZGVmYXVsdFNhbXBsZVNpemVQcmVzZW50ID0gKHRmaGRGbGFncyAmIDB4MDAwMDEwKSAhPT0gMDtcbiAgICAgICAgbGV0IGRlZmF1bHRTYW1wbGVTaXplID0gMDtcbiAgICAgICAgY29uc3QgZGVmYXVsdFNhbXBsZUZsYWdzUHJlc2VudCA9ICh0ZmhkRmxhZ3MgJiAweDAwMDAyMCkgIT09IDA7XG4gICAgICAgIGxldCB0ZmhkT2Zmc2V0ID0gODtcbiAgICAgICAgaWYgKGlkID09PSB0cmFja0lkKSB7XG4gICAgICAgICAgaWYgKGJhc2VEYXRhT2Zmc2V0UHJlc2VudCkge1xuICAgICAgICAgICAgdGZoZE9mZnNldCArPSA4O1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoc2FtcGxlRGVzY3JpcHRpb25JbmRleFByZXNlbnQpIHtcbiAgICAgICAgICAgIHRmaGRPZmZzZXQgKz0gNDtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGRlZmF1bHRTYW1wbGVEdXJhdGlvblByZXNlbnQpIHtcbiAgICAgICAgICAgIGRlZmF1bHRTYW1wbGVEdXJhdGlvbiA9IHJlYWRVaW50MzIodGZoZCwgdGZoZE9mZnNldCk7XG4gICAgICAgICAgICB0ZmhkT2Zmc2V0ICs9IDQ7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChkZWZhdWx0U2FtcGxlU2l6ZVByZXNlbnQpIHtcbiAgICAgICAgICAgIGRlZmF1bHRTYW1wbGVTaXplID0gcmVhZFVpbnQzMih0ZmhkLCB0ZmhkT2Zmc2V0KTtcbiAgICAgICAgICAgIHRmaGRPZmZzZXQgKz0gNDtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGRlZmF1bHRTYW1wbGVGbGFnc1ByZXNlbnQpIHtcbiAgICAgICAgICAgIHRmaGRPZmZzZXQgKz0gNDtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKHRyYWNrLnR5cGUgPT09ICd2aWRlbycpIHtcbiAgICAgICAgICAgIGlzSEVWQ0ZsYXZvciA9IGlzSEVWQyh0cmFjay5jb2RlYyk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGZpbmRCb3godHJhZiwgWyd0cnVuJ10pLm1hcCh0cnVuID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHZlcnNpb24gPSB0cnVuWzBdO1xuICAgICAgICAgICAgY29uc3QgZmxhZ3MgPSByZWFkVWludDMyKHRydW4sIDApICYgMHhmZmZmZmY7XG4gICAgICAgICAgICBjb25zdCBkYXRhT2Zmc2V0UHJlc2VudCA9IChmbGFncyAmIDB4MDAwMDAxKSAhPT0gMDtcbiAgICAgICAgICAgIGxldCBkYXRhT2Zmc2V0ID0gMDtcbiAgICAgICAgICAgIGNvbnN0IGZpcnN0U2FtcGxlRmxhZ3NQcmVzZW50ID0gKGZsYWdzICYgMHgwMDAwMDQpICE9PSAwO1xuICAgICAgICAgICAgY29uc3Qgc2FtcGxlRHVyYXRpb25QcmVzZW50ID0gKGZsYWdzICYgMHgwMDAxMDApICE9PSAwO1xuICAgICAgICAgICAgbGV0IHNhbXBsZUR1cmF0aW9uID0gMDtcbiAgICAgICAgICAgIGNvbnN0IHNhbXBsZVNpemVQcmVzZW50ID0gKGZsYWdzICYgMHgwMDAyMDApICE9PSAwO1xuICAgICAgICAgICAgbGV0IHNhbXBsZVNpemUgPSAwO1xuICAgICAgICAgICAgY29uc3Qgc2FtcGxlRmxhZ3NQcmVzZW50ID0gKGZsYWdzICYgMHgwMDA0MDApICE9PSAwO1xuICAgICAgICAgICAgY29uc3Qgc2FtcGxlQ29tcG9zaXRpb25PZmZzZXRzUHJlc2VudCA9IChmbGFncyAmIDB4MDAwODAwKSAhPT0gMDtcbiAgICAgICAgICAgIGxldCBjb21wb3NpdGlvbk9mZnNldCA9IDA7XG4gICAgICAgICAgICBjb25zdCBzYW1wbGVDb3VudCA9IHJlYWRVaW50MzIodHJ1biwgNCk7XG4gICAgICAgICAgICBsZXQgdHJ1bk9mZnNldCA9IDg7IC8vIHBhc3QgdmVyc2lvbiwgZmxhZ3MsIGFuZCBzYW1wbGUgY291bnRcblxuICAgICAgICAgICAgaWYgKGRhdGFPZmZzZXRQcmVzZW50KSB7XG4gICAgICAgICAgICAgIGRhdGFPZmZzZXQgPSByZWFkVWludDMyKHRydW4sIHRydW5PZmZzZXQpO1xuICAgICAgICAgICAgICB0cnVuT2Zmc2V0ICs9IDQ7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoZmlyc3RTYW1wbGVGbGFnc1ByZXNlbnQpIHtcbiAgICAgICAgICAgICAgdHJ1bk9mZnNldCArPSA0O1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgbGV0IHNhbXBsZU9mZnNldCA9IGRhdGFPZmZzZXQgKyBtb29mT2Zmc2V0O1xuICAgICAgICAgICAgZm9yIChsZXQgaXggPSAwOyBpeCA8IHNhbXBsZUNvdW50OyBpeCsrKSB7XG4gICAgICAgICAgICAgIGlmIChzYW1wbGVEdXJhdGlvblByZXNlbnQpIHtcbiAgICAgICAgICAgICAgICBzYW1wbGVEdXJhdGlvbiA9IHJlYWRVaW50MzIodHJ1biwgdHJ1bk9mZnNldCk7XG4gICAgICAgICAgICAgICAgdHJ1bk9mZnNldCArPSA0O1xuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHNhbXBsZUR1cmF0aW9uID0gZGVmYXVsdFNhbXBsZUR1cmF0aW9uO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGlmIChzYW1wbGVTaXplUHJlc2VudCkge1xuICAgICAgICAgICAgICAgIHNhbXBsZVNpemUgPSByZWFkVWludDMyKHRydW4sIHRydW5PZmZzZXQpO1xuICAgICAgICAgICAgICAgIHRydW5PZmZzZXQgKz0gNDtcbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBzYW1wbGVTaXplID0gZGVmYXVsdFNhbXBsZVNpemU7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgaWYgKHNhbXBsZUZsYWdzUHJlc2VudCkge1xuICAgICAgICAgICAgICAgIHRydW5PZmZzZXQgKz0gNDtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBpZiAoc2FtcGxlQ29tcG9zaXRpb25PZmZzZXRzUHJlc2VudCkge1xuICAgICAgICAgICAgICAgIGlmICh2ZXJzaW9uID09PSAwKSB7XG4gICAgICAgICAgICAgICAgICBjb21wb3NpdGlvbk9mZnNldCA9IHJlYWRVaW50MzIodHJ1biwgdHJ1bk9mZnNldCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgIGNvbXBvc2l0aW9uT2Zmc2V0ID0gcmVhZFNpbnQzMih0cnVuLCB0cnVuT2Zmc2V0KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgdHJ1bk9mZnNldCArPSA0O1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGlmICh0cmFjay50eXBlID09PSBFbGVtZW50YXJ5U3RyZWFtVHlwZXMuVklERU8pIHtcbiAgICAgICAgICAgICAgICBsZXQgbmFsdVRvdGFsU2l6ZSA9IDA7XG4gICAgICAgICAgICAgICAgd2hpbGUgKG5hbHVUb3RhbFNpemUgPCBzYW1wbGVTaXplKSB7XG4gICAgICAgICAgICAgICAgICBjb25zdCBuYWx1U2l6ZSA9IHJlYWRVaW50MzIodmlkZW9EYXRhLCBzYW1wbGVPZmZzZXQpO1xuICAgICAgICAgICAgICAgICAgc2FtcGxlT2Zmc2V0ICs9IDQ7XG4gICAgICAgICAgICAgICAgICBpZiAoaXNTRUlNZXNzYWdlKGlzSEVWQ0ZsYXZvciwgdmlkZW9EYXRhW3NhbXBsZU9mZnNldF0pKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGRhdGEgPSB2aWRlb0RhdGEuc3ViYXJyYXkoc2FtcGxlT2Zmc2V0LCBzYW1wbGVPZmZzZXQgKyBuYWx1U2l6ZSk7XG4gICAgICAgICAgICAgICAgICAgIHBhcnNlU0VJTWVzc2FnZUZyb21OQUx1KGRhdGEsIGlzSEVWQ0ZsYXZvciA/IDIgOiAxLCB0aW1lT2Zmc2V0ICsgY29tcG9zaXRpb25PZmZzZXQgLyB0aW1lc2NhbGUsIHNlaVNhbXBsZXMpO1xuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgc2FtcGxlT2Zmc2V0ICs9IG5hbHVTaXplO1xuICAgICAgICAgICAgICAgICAgbmFsdVRvdGFsU2l6ZSArPSBuYWx1U2l6ZSArIDQ7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHRpbWVPZmZzZXQgKz0gc2FtcGxlRHVyYXRpb24gLyB0aW1lc2NhbGU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH0pO1xuICB9KTtcbiAgcmV0dXJuIHNlaVNhbXBsZXM7XG59XG5mdW5jdGlvbiBpc0hFVkMoY29kZWMpIHtcbiAgaWYgKCFjb2RlYykge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICBjb25zdCBkZWxpbWl0ID0gY29kZWMuaW5kZXhPZignLicpO1xuICBjb25zdCBiYXNlQ29kZWMgPSBkZWxpbWl0IDwgMCA/IGNvZGVjIDogY29kZWMuc3Vic3RyaW5nKDAsIGRlbGltaXQpO1xuICByZXR1cm4gYmFzZUNvZGVjID09PSAnaHZjMScgfHwgYmFzZUNvZGVjID09PSAnaGV2MScgfHxcbiAgLy8gRG9sYnkgVmlzaW9uXG4gIGJhc2VDb2RlYyA9PT0gJ2R2aDEnIHx8IGJhc2VDb2RlYyA9PT0gJ2R2aGUnO1xufVxuZnVuY3Rpb24gaXNTRUlNZXNzYWdlKGlzSEVWQ0ZsYXZvciwgbmFsdUhlYWRlcikge1xuICBpZiAoaXNIRVZDRmxhdm9yKSB7XG4gICAgY29uc3QgbmFsdVR5cGUgPSBuYWx1SGVhZGVyID4+IDEgJiAweDNmO1xuICAgIHJldHVybiBuYWx1VHlwZSA9PT0gMzkgfHwgbmFsdVR5cGUgPT09IDQwO1xuICB9IGVsc2Uge1xuICAgIGNvbnN0IG5hbHVUeXBlID0gbmFsdUhlYWRlciAmIDB4MWY7XG4gICAgcmV0dXJuIG5hbHVUeXBlID09PSA2O1xuICB9XG59XG5mdW5jdGlvbiBwYXJzZVNFSU1lc3NhZ2VGcm9tTkFMdSh1bmVzY2FwZWREYXRhLCBoZWFkZXJTaXplLCBwdHMsIHNhbXBsZXMpIHtcbiAgY29uc3QgZGF0YSA9IGRpc2NhcmRFUEIodW5lc2NhcGVkRGF0YSk7XG4gIGxldCBzZWlQdHIgPSAwO1xuICAvLyBza2lwIG5hbCBoZWFkZXJcbiAgc2VpUHRyICs9IGhlYWRlclNpemU7XG4gIGxldCBwYXlsb2FkVHlwZSA9IDA7XG4gIGxldCBwYXlsb2FkU2l6ZSA9IDA7XG4gIGxldCBiID0gMDtcbiAgd2hpbGUgKHNlaVB0ciA8IGRhdGEubGVuZ3RoKSB7XG4gICAgcGF5bG9hZFR5cGUgPSAwO1xuICAgIGRvIHtcbiAgICAgIGlmIChzZWlQdHIgPj0gZGF0YS5sZW5ndGgpIHtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBiID0gZGF0YVtzZWlQdHIrK107XG4gICAgICBwYXlsb2FkVHlwZSArPSBiO1xuICAgIH0gd2hpbGUgKGIgPT09IDB4ZmYpO1xuXG4gICAgLy8gUGFyc2UgcGF5bG9hZCBzaXplLlxuICAgIHBheWxvYWRTaXplID0gMDtcbiAgICBkbyB7XG4gICAgICBpZiAoc2VpUHRyID49IGRhdGEubGVuZ3RoKSB7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgICAgYiA9IGRhdGFbc2VpUHRyKytdO1xuICAgICAgcGF5bG9hZFNpemUgKz0gYjtcbiAgICB9IHdoaWxlIChiID09PSAweGZmKTtcbiAgICBjb25zdCBsZWZ0T3ZlciA9IGRhdGEubGVuZ3RoIC0gc2VpUHRyO1xuICAgIC8vIENyZWF0ZSBhIHZhcmlhYmxlIHRvIHByb2Nlc3MgdGhlIHBheWxvYWRcbiAgICBsZXQgcGF5UHRyID0gc2VpUHRyO1xuXG4gICAgLy8gSW5jcmVtZW50IHRoZSBzZWlQdHIgdG8gdGhlIGVuZCBvZiB0aGUgcGF5bG9hZFxuICAgIGlmIChwYXlsb2FkU2l6ZSA8IGxlZnRPdmVyKSB7XG4gICAgICBzZWlQdHIgKz0gcGF5bG9hZFNpemU7XG4gICAgfSBlbHNlIGlmIChwYXlsb2FkU2l6ZSA+IGxlZnRPdmVyKSB7XG4gICAgICAvLyBTb21lIHR5cGUgb2YgY29ycnVwdGlvbiBoYXMgaGFwcGVuZWQ/XG4gICAgICBsb2dnZXIuZXJyb3IoYE1hbGZvcm1lZCBTRUkgcGF5bG9hZC4gJHtwYXlsb2FkU2l6ZX0gaXMgdG9vIHNtYWxsLCBvbmx5ICR7bGVmdE92ZXJ9IGJ5dGVzIGxlZnQgdG8gcGFyc2UuYCk7XG4gICAgICAvLyBXZSBtaWdodCBiZSBhYmxlIHRvIHBhcnNlIHNvbWUgZGF0YSwgYnV0IGxldCdzIGJlIHNhZmUgYW5kIGlnbm9yZSBpdC5cbiAgICAgIGJyZWFrO1xuICAgIH1cbiAgICBpZiAocGF5bG9hZFR5cGUgPT09IDQpIHtcbiAgICAgIGNvbnN0IGNvdW50cnlDb2RlID0gZGF0YVtwYXlQdHIrK107XG4gICAgICBpZiAoY291bnRyeUNvZGUgPT09IDE4MSkge1xuICAgICAgICBjb25zdCBwcm92aWRlckNvZGUgPSByZWFkVWludDE2KGRhdGEsIHBheVB0cik7XG4gICAgICAgIHBheVB0ciArPSAyO1xuICAgICAgICBpZiAocHJvdmlkZXJDb2RlID09PSA0OSkge1xuICAgICAgICAgIGNvbnN0IHVzZXJTdHJ1Y3R1cmUgPSByZWFkVWludDMyKGRhdGEsIHBheVB0cik7XG4gICAgICAgICAgcGF5UHRyICs9IDQ7XG4gICAgICAgICAgaWYgKHVzZXJTdHJ1Y3R1cmUgPT09IDB4NDc0MTM5MzQpIHtcbiAgICAgICAgICAgIGNvbnN0IHVzZXJEYXRhVHlwZSA9IGRhdGFbcGF5UHRyKytdO1xuXG4gICAgICAgICAgICAvLyBSYXcgQ0VBLTYwOCBieXRlcyB3cmFwcGVkIGluIENFQS03MDggcGFja2V0XG4gICAgICAgICAgICBpZiAodXNlckRhdGFUeXBlID09PSAzKSB7XG4gICAgICAgICAgICAgIGNvbnN0IGZpcnN0Qnl0ZSA9IGRhdGFbcGF5UHRyKytdO1xuICAgICAgICAgICAgICBjb25zdCB0b3RhbENDcyA9IDB4MWYgJiBmaXJzdEJ5dGU7XG4gICAgICAgICAgICAgIGNvbnN0IGVuYWJsZWQgPSAweDQwICYgZmlyc3RCeXRlO1xuICAgICAgICAgICAgICBjb25zdCB0b3RhbEJ5dGVzID0gZW5hYmxlZCA/IDIgKyB0b3RhbENDcyAqIDMgOiAwO1xuICAgICAgICAgICAgICBjb25zdCBieXRlQXJyYXkgPSBuZXcgVWludDhBcnJheSh0b3RhbEJ5dGVzKTtcbiAgICAgICAgICAgICAgaWYgKGVuYWJsZWQpIHtcbiAgICAgICAgICAgICAgICBieXRlQXJyYXlbMF0gPSBmaXJzdEJ5dGU7XG4gICAgICAgICAgICAgICAgZm9yIChsZXQgaSA9IDE7IGkgPCB0b3RhbEJ5dGVzOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgIGJ5dGVBcnJheVtpXSA9IGRhdGFbcGF5UHRyKytdO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBzYW1wbGVzLnB1c2goe1xuICAgICAgICAgICAgICAgIHR5cGU6IHVzZXJEYXRhVHlwZSxcbiAgICAgICAgICAgICAgICBwYXlsb2FkVHlwZSxcbiAgICAgICAgICAgICAgICBwdHMsXG4gICAgICAgICAgICAgICAgYnl0ZXM6IGJ5dGVBcnJheVxuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKHBheWxvYWRUeXBlID09PSA1KSB7XG4gICAgICBpZiAocGF5bG9hZFNpemUgPiAxNikge1xuICAgICAgICBjb25zdCB1dWlkU3RyQXJyYXkgPSBbXTtcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCAxNjsgaSsrKSB7XG4gICAgICAgICAgY29uc3QgX2IgPSBkYXRhW3BheVB0cisrXS50b1N0cmluZygxNik7XG4gICAgICAgICAgdXVpZFN0ckFycmF5LnB1c2goX2IubGVuZ3RoID09IDEgPyAnMCcgKyBfYiA6IF9iKTtcbiAgICAgICAgICBpZiAoaSA9PT0gMyB8fCBpID09PSA1IHx8IGkgPT09IDcgfHwgaSA9PT0gOSkge1xuICAgICAgICAgICAgdXVpZFN0ckFycmF5LnB1c2goJy0nKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgbGVuZ3RoID0gcGF5bG9hZFNpemUgLSAxNjtcbiAgICAgICAgY29uc3QgdXNlckRhdGFCeXRlcyA9IG5ldyBVaW50OEFycmF5KGxlbmd0aCk7XG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICB1c2VyRGF0YUJ5dGVzW2ldID0gZGF0YVtwYXlQdHIrK107XG4gICAgICAgIH1cbiAgICAgICAgc2FtcGxlcy5wdXNoKHtcbiAgICAgICAgICBwYXlsb2FkVHlwZSxcbiAgICAgICAgICBwdHMsXG4gICAgICAgICAgdXVpZDogdXVpZFN0ckFycmF5LmpvaW4oJycpLFxuICAgICAgICAgIHVzZXJEYXRhOiB1dGY4QXJyYXlUb1N0cih1c2VyRGF0YUJ5dGVzKSxcbiAgICAgICAgICB1c2VyRGF0YUJ5dGVzXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIHJlbW92ZSBFbXVsYXRpb24gUHJldmVudGlvbiBieXRlcyBmcm9tIGEgUkJTUFxuICovXG5mdW5jdGlvbiBkaXNjYXJkRVBCKGRhdGEpIHtcbiAgY29uc3QgbGVuZ3RoID0gZGF0YS5ieXRlTGVuZ3RoO1xuICBjb25zdCBFUEJQb3NpdGlvbnMgPSBbXTtcbiAgbGV0IGkgPSAxO1xuXG4gIC8vIEZpbmQgYWxsIGBFbXVsYXRpb24gUHJldmVudGlvbiBCeXRlc2BcbiAgd2hpbGUgKGkgPCBsZW5ndGggLSAyKSB7XG4gICAgaWYgKGRhdGFbaV0gPT09IDAgJiYgZGF0YVtpICsgMV0gPT09IDAgJiYgZGF0YVtpICsgMl0gPT09IDB4MDMpIHtcbiAgICAgIEVQQlBvc2l0aW9ucy5wdXNoKGkgKyAyKTtcbiAgICAgIGkgKz0gMjtcbiAgICB9IGVsc2Uge1xuICAgICAgaSsrO1xuICAgIH1cbiAgfVxuXG4gIC8vIElmIG5vIEVtdWxhdGlvbiBQcmV2ZW50aW9uIEJ5dGVzIHdlcmUgZm91bmQganVzdCByZXR1cm4gdGhlIG9yaWdpbmFsXG4gIC8vIGFycmF5XG4gIGlmIChFUEJQb3NpdGlvbnMubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIGRhdGE7XG4gIH1cblxuICAvLyBDcmVhdGUgYSBuZXcgYXJyYXkgdG8gaG9sZCB0aGUgTkFMIHVuaXQgZGF0YVxuICBjb25zdCBuZXdMZW5ndGggPSBsZW5ndGggLSBFUEJQb3NpdGlvbnMubGVuZ3RoO1xuICBjb25zdCBuZXdEYXRhID0gbmV3IFVpbnQ4QXJyYXkobmV3TGVuZ3RoKTtcbiAgbGV0IHNvdXJjZUluZGV4ID0gMDtcbiAgZm9yIChpID0gMDsgaSA8IG5ld0xlbmd0aDsgc291cmNlSW5kZXgrKywgaSsrKSB7XG4gICAgaWYgKHNvdXJjZUluZGV4ID09PSBFUEJQb3NpdGlvbnNbMF0pIHtcbiAgICAgIC8vIFNraXAgdGhpcyBieXRlXG4gICAgICBzb3VyY2VJbmRleCsrO1xuICAgICAgLy8gUmVtb3ZlIHRoaXMgcG9zaXRpb24gaW5kZXhcbiAgICAgIEVQQlBvc2l0aW9ucy5zaGlmdCgpO1xuICAgIH1cbiAgICBuZXdEYXRhW2ldID0gZGF0YVtzb3VyY2VJbmRleF07XG4gIH1cbiAgcmV0dXJuIG5ld0RhdGE7XG59XG5mdW5jdGlvbiBwYXJzZUVtc2coZGF0YSkge1xuICBjb25zdCB2ZXJzaW9uID0gZGF0YVswXTtcbiAgbGV0IHNjaGVtZUlkVXJpID0gJyc7XG4gIGxldCB2YWx1ZSA9ICcnO1xuICBsZXQgdGltZVNjYWxlID0gMDtcbiAgbGV0IHByZXNlbnRhdGlvblRpbWVEZWx0YSA9IDA7XG4gIGxldCBwcmVzZW50YXRpb25UaW1lID0gMDtcbiAgbGV0IGV2ZW50RHVyYXRpb24gPSAwO1xuICBsZXQgaWQgPSAwO1xuICBsZXQgb2Zmc2V0ID0gMDtcbiAgaWYgKHZlcnNpb24gPT09IDApIHtcbiAgICB3aGlsZSAoYmluMnN0cihkYXRhLnN1YmFycmF5KG9mZnNldCwgb2Zmc2V0ICsgMSkpICE9PSAnXFwwJykge1xuICAgICAgc2NoZW1lSWRVcmkgKz0gYmluMnN0cihkYXRhLnN1YmFycmF5KG9mZnNldCwgb2Zmc2V0ICsgMSkpO1xuICAgICAgb2Zmc2V0ICs9IDE7XG4gICAgfVxuICAgIHNjaGVtZUlkVXJpICs9IGJpbjJzdHIoZGF0YS5zdWJhcnJheShvZmZzZXQsIG9mZnNldCArIDEpKTtcbiAgICBvZmZzZXQgKz0gMTtcbiAgICB3aGlsZSAoYmluMnN0cihkYXRhLnN1YmFycmF5KG9mZnNldCwgb2Zmc2V0ICsgMSkpICE9PSAnXFwwJykge1xuICAgICAgdmFsdWUgKz0gYmluMnN0cihkYXRhLnN1YmFycmF5KG9mZnNldCwgb2Zmc2V0ICsgMSkpO1xuICAgICAgb2Zmc2V0ICs9IDE7XG4gICAgfVxuICAgIHZhbHVlICs9IGJpbjJzdHIoZGF0YS5zdWJhcnJheShvZmZzZXQsIG9mZnNldCArIDEpKTtcbiAgICBvZmZzZXQgKz0gMTtcbiAgICB0aW1lU2NhbGUgPSByZWFkVWludDMyKGRhdGEsIDEyKTtcbiAgICBwcmVzZW50YXRpb25UaW1lRGVsdGEgPSByZWFkVWludDMyKGRhdGEsIDE2KTtcbiAgICBldmVudER1cmF0aW9uID0gcmVhZFVpbnQzMihkYXRhLCAyMCk7XG4gICAgaWQgPSByZWFkVWludDMyKGRhdGEsIDI0KTtcbiAgICBvZmZzZXQgPSAyODtcbiAgfSBlbHNlIGlmICh2ZXJzaW9uID09PSAxKSB7XG4gICAgb2Zmc2V0ICs9IDQ7XG4gICAgdGltZVNjYWxlID0gcmVhZFVpbnQzMihkYXRhLCBvZmZzZXQpO1xuICAgIG9mZnNldCArPSA0O1xuICAgIGNvbnN0IGxlZnRQcmVzZW50YXRpb25UaW1lID0gcmVhZFVpbnQzMihkYXRhLCBvZmZzZXQpO1xuICAgIG9mZnNldCArPSA0O1xuICAgIGNvbnN0IHJpZ2h0UHJlc2VudGF0aW9uVGltZSA9IHJlYWRVaW50MzIoZGF0YSwgb2Zmc2V0KTtcbiAgICBvZmZzZXQgKz0gNDtcbiAgICBwcmVzZW50YXRpb25UaW1lID0gMiAqKiAzMiAqIGxlZnRQcmVzZW50YXRpb25UaW1lICsgcmlnaHRQcmVzZW50YXRpb25UaW1lO1xuICAgIGlmICghaXNTYWZlSW50ZWdlcihwcmVzZW50YXRpb25UaW1lKSkge1xuICAgICAgcHJlc2VudGF0aW9uVGltZSA9IE51bWJlci5NQVhfU0FGRV9JTlRFR0VSO1xuICAgICAgbG9nZ2VyLndhcm4oJ1ByZXNlbnRhdGlvbiB0aW1lIGV4Y2VlZHMgc2FmZSBpbnRlZ2VyIGxpbWl0IGFuZCB3cmFwcGVkIHRvIG1heCBzYWZlIGludGVnZXIgaW4gcGFyc2luZyBlbXNnIGJveCcpO1xuICAgIH1cbiAgICBldmVudER1cmF0aW9uID0gcmVhZFVpbnQzMihkYXRhLCBvZmZzZXQpO1xuICAgIG9mZnNldCArPSA0O1xuICAgIGlkID0gcmVhZFVpbnQzMihkYXRhLCBvZmZzZXQpO1xuICAgIG9mZnNldCArPSA0O1xuICAgIHdoaWxlIChiaW4yc3RyKGRhdGEuc3ViYXJyYXkob2Zmc2V0LCBvZmZzZXQgKyAxKSkgIT09ICdcXDAnKSB7XG4gICAgICBzY2hlbWVJZFVyaSArPSBiaW4yc3RyKGRhdGEuc3ViYXJyYXkob2Zmc2V0LCBvZmZzZXQgKyAxKSk7XG4gICAgICBvZmZzZXQgKz0gMTtcbiAgICB9XG4gICAgc2NoZW1lSWRVcmkgKz0gYmluMnN0cihkYXRhLnN1YmFycmF5KG9mZnNldCwgb2Zmc2V0ICsgMSkpO1xuICAgIG9mZnNldCArPSAxO1xuICAgIHdoaWxlIChiaW4yc3RyKGRhdGEuc3ViYXJyYXkob2Zmc2V0LCBvZmZzZXQgKyAxKSkgIT09ICdcXDAnKSB7XG4gICAgICB2YWx1ZSArPSBiaW4yc3RyKGRhdGEuc3ViYXJyYXkob2Zmc2V0LCBvZmZzZXQgKyAxKSk7XG4gICAgICBvZmZzZXQgKz0gMTtcbiAgICB9XG4gICAgdmFsdWUgKz0gYmluMnN0cihkYXRhLnN1YmFycmF5KG9mZnNldCwgb2Zmc2V0ICsgMSkpO1xuICAgIG9mZnNldCArPSAxO1xuICB9XG4gIGNvbnN0IHBheWxvYWQgPSBkYXRhLnN1YmFycmF5KG9mZnNldCwgZGF0YS5ieXRlTGVuZ3RoKTtcbiAgcmV0dXJuIHtcbiAgICBzY2hlbWVJZFVyaSxcbiAgICB2YWx1ZSxcbiAgICB0aW1lU2NhbGUsXG4gICAgcHJlc2VudGF0aW9uVGltZSxcbiAgICBwcmVzZW50YXRpb25UaW1lRGVsdGEsXG4gICAgZXZlbnREdXJhdGlvbixcbiAgICBpZCxcbiAgICBwYXlsb2FkXG4gIH07XG59XG5mdW5jdGlvbiBtcDRCb3godHlwZSwgLi4ucGF5bG9hZCkge1xuICBjb25zdCBsZW4gPSBwYXlsb2FkLmxlbmd0aDtcbiAgbGV0IHNpemUgPSA4O1xuICBsZXQgaSA9IGxlbjtcbiAgd2hpbGUgKGktLSkge1xuICAgIHNpemUgKz0gcGF5bG9hZFtpXS5ieXRlTGVuZ3RoO1xuICB9XG4gIGNvbnN0IHJlc3VsdCA9IG5ldyBVaW50OEFycmF5KHNpemUpO1xuICByZXN1bHRbMF0gPSBzaXplID4+IDI0ICYgMHhmZjtcbiAgcmVzdWx0WzFdID0gc2l6ZSA+PiAxNiAmIDB4ZmY7XG4gIHJlc3VsdFsyXSA9IHNpemUgPj4gOCAmIDB4ZmY7XG4gIHJlc3VsdFszXSA9IHNpemUgJiAweGZmO1xuICByZXN1bHQuc2V0KHR5cGUsIDQpO1xuICBmb3IgKGkgPSAwLCBzaXplID0gODsgaSA8IGxlbjsgaSsrKSB7XG4gICAgcmVzdWx0LnNldChwYXlsb2FkW2ldLCBzaXplKTtcbiAgICBzaXplICs9IHBheWxvYWRbaV0uYnl0ZUxlbmd0aDtcbiAgfVxuICByZXR1cm4gcmVzdWx0O1xufVxuZnVuY3Rpb24gbXA0cHNzaChzeXN0ZW1JZCwga2V5aWRzLCBkYXRhKSB7XG4gIGlmIChzeXN0ZW1JZC5ieXRlTGVuZ3RoICE9PSAxNikge1xuICAgIHRocm93IG5ldyBSYW5nZUVycm9yKCdJbnZhbGlkIHN5c3RlbSBpZCcpO1xuICB9XG4gIGxldCB2ZXJzaW9uO1xuICBsZXQga2lkcztcbiAgaWYgKGtleWlkcykge1xuICAgIHZlcnNpb24gPSAxO1xuICAgIGtpZHMgPSBuZXcgVWludDhBcnJheShrZXlpZHMubGVuZ3RoICogMTYpO1xuICAgIGZvciAobGV0IGl4ID0gMDsgaXggPCBrZXlpZHMubGVuZ3RoOyBpeCsrKSB7XG4gICAgICBjb25zdCBrID0ga2V5aWRzW2l4XTsgLy8gdWludDhhcnJheVxuICAgICAgaWYgKGsuYnl0ZUxlbmd0aCAhPT0gMTYpIHtcbiAgICAgICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ0ludmFsaWQga2V5Jyk7XG4gICAgICB9XG4gICAgICBraWRzLnNldChrLCBpeCAqIDE2KTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgdmVyc2lvbiA9IDA7XG4gICAga2lkcyA9IG5ldyBVaW50OEFycmF5KCk7XG4gIH1cbiAgbGV0IGtpZENvdW50O1xuICBpZiAodmVyc2lvbiA+IDApIHtcbiAgICBraWRDb3VudCA9IG5ldyBVaW50OEFycmF5KDQpO1xuICAgIGlmIChrZXlpZHMubGVuZ3RoID4gMCkge1xuICAgICAgbmV3IERhdGFWaWV3KGtpZENvdW50LmJ1ZmZlcikuc2V0VWludDMyKDAsIGtleWlkcy5sZW5ndGgsIGZhbHNlKTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAga2lkQ291bnQgPSBuZXcgVWludDhBcnJheSgpO1xuICB9XG4gIGNvbnN0IGRhdGFTaXplID0gbmV3IFVpbnQ4QXJyYXkoNCk7XG4gIGlmIChkYXRhICYmIGRhdGEuYnl0ZUxlbmd0aCA+IDApIHtcbiAgICBuZXcgRGF0YVZpZXcoZGF0YVNpemUuYnVmZmVyKS5zZXRVaW50MzIoMCwgZGF0YS5ieXRlTGVuZ3RoLCBmYWxzZSk7XG4gIH1cbiAgcmV0dXJuIG1wNEJveChbMTEyLCAxMTUsIDExNSwgMTA0XSwgbmV3IFVpbnQ4QXJyYXkoW3ZlcnNpb24sIDB4MDAsIDB4MDAsIDB4MDAgLy8gRmxhZ3NcbiAgXSksIHN5c3RlbUlkLFxuICAvLyAxNiBieXRlc1xuICBraWRDb3VudCwga2lkcywgZGF0YVNpemUsIGRhdGEgfHwgbmV3IFVpbnQ4QXJyYXkoKSk7XG59XG5mdW5jdGlvbiBwYXJzZU11bHRpUHNzaChpbml0RGF0YSkge1xuICBjb25zdCByZXN1bHRzID0gW107XG4gIGlmIChpbml0RGF0YSBpbnN0YW5jZW9mIEFycmF5QnVmZmVyKSB7XG4gICAgY29uc3QgbGVuZ3RoID0gaW5pdERhdGEuYnl0ZUxlbmd0aDtcbiAgICBsZXQgb2Zmc2V0ID0gMDtcbiAgICB3aGlsZSAob2Zmc2V0ICsgMzIgPCBsZW5ndGgpIHtcbiAgICAgIGNvbnN0IHZpZXcgPSBuZXcgRGF0YVZpZXcoaW5pdERhdGEsIG9mZnNldCk7XG4gICAgICBjb25zdCBwc3NoID0gcGFyc2VQc3NoKHZpZXcpO1xuICAgICAgcmVzdWx0cy5wdXNoKHBzc2gpO1xuICAgICAgb2Zmc2V0ICs9IHBzc2guc2l6ZTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHJlc3VsdHM7XG59XG5mdW5jdGlvbiBwYXJzZVBzc2godmlldykge1xuICBjb25zdCBzaXplID0gdmlldy5nZXRVaW50MzIoMCk7XG4gIGNvbnN0IG9mZnNldCA9IHZpZXcuYnl0ZU9mZnNldDtcbiAgY29uc3QgbGVuZ3RoID0gdmlldy5ieXRlTGVuZ3RoO1xuICBpZiAobGVuZ3RoIDwgc2l6ZSkge1xuICAgIHJldHVybiB7XG4gICAgICBvZmZzZXQsXG4gICAgICBzaXplOiBsZW5ndGhcbiAgICB9O1xuICB9XG4gIGNvbnN0IHR5cGUgPSB2aWV3LmdldFVpbnQzMig0KTtcbiAgaWYgKHR5cGUgIT09IDB4NzA3MzczNjgpIHtcbiAgICByZXR1cm4ge1xuICAgICAgb2Zmc2V0LFxuICAgICAgc2l6ZVxuICAgIH07XG4gIH1cbiAgY29uc3QgdmVyc2lvbiA9IHZpZXcuZ2V0VWludDMyKDgpID4+PiAyNDtcbiAgaWYgKHZlcnNpb24gIT09IDAgJiYgdmVyc2lvbiAhPT0gMSkge1xuICAgIHJldHVybiB7XG4gICAgICBvZmZzZXQsXG4gICAgICBzaXplXG4gICAgfTtcbiAgfVxuICBjb25zdCBidWZmZXIgPSB2aWV3LmJ1ZmZlcjtcbiAgY29uc3Qgc3lzdGVtSWQgPSBIZXguaGV4RHVtcChuZXcgVWludDhBcnJheShidWZmZXIsIG9mZnNldCArIDEyLCAxNikpO1xuICBjb25zdCBkYXRhU2l6ZU9yS2lkQ291bnQgPSB2aWV3LmdldFVpbnQzMigyOCk7XG4gIGxldCBraWRzID0gbnVsbDtcbiAgbGV0IGRhdGEgPSBudWxsO1xuICBpZiAodmVyc2lvbiA9PT0gMCkge1xuICAgIGlmIChzaXplIC0gMzIgPCBkYXRhU2l6ZU9yS2lkQ291bnQgfHwgZGF0YVNpemVPcktpZENvdW50IDwgMjIpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIG9mZnNldCxcbiAgICAgICAgc2l6ZVxuICAgICAgfTtcbiAgICB9XG4gICAgZGF0YSA9IG5ldyBVaW50OEFycmF5KGJ1ZmZlciwgb2Zmc2V0ICsgMzIsIGRhdGFTaXplT3JLaWRDb3VudCk7XG4gIH0gZWxzZSBpZiAodmVyc2lvbiA9PT0gMSkge1xuICAgIGlmICghZGF0YVNpemVPcktpZENvdW50IHx8IGxlbmd0aCA8IG9mZnNldCArIDMyICsgZGF0YVNpemVPcktpZENvdW50ICogMTYgKyAxNikge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgb2Zmc2V0LFxuICAgICAgICBzaXplXG4gICAgICB9O1xuICAgIH1cbiAgICBraWRzID0gW107XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBkYXRhU2l6ZU9yS2lkQ291bnQ7IGkrKykge1xuICAgICAga2lkcy5wdXNoKG5ldyBVaW50OEFycmF5KGJ1ZmZlciwgb2Zmc2V0ICsgMzIgKyBpICogMTYsIDE2KSk7XG4gICAgfVxuICB9XG4gIHJldHVybiB7XG4gICAgdmVyc2lvbixcbiAgICBzeXN0ZW1JZCxcbiAgICBraWRzLFxuICAgIGRhdGEsXG4gICAgb2Zmc2V0LFxuICAgIHNpemVcbiAgfTtcbn1cblxubGV0IGtleVVyaVRvS2V5SWRNYXAgPSB7fTtcbmNsYXNzIExldmVsS2V5IHtcbiAgc3RhdGljIGNsZWFyS2V5VXJpVG9LZXlJZE1hcCgpIHtcbiAgICBrZXlVcmlUb0tleUlkTWFwID0ge307XG4gIH1cbiAgY29uc3RydWN0b3IobWV0aG9kLCB1cmksIGZvcm1hdCwgZm9ybWF0dmVyc2lvbnMgPSBbMV0sIGl2ID0gbnVsbCkge1xuICAgIHRoaXMudXJpID0gdm9pZCAwO1xuICAgIHRoaXMubWV0aG9kID0gdm9pZCAwO1xuICAgIHRoaXMua2V5Rm9ybWF0ID0gdm9pZCAwO1xuICAgIHRoaXMua2V5Rm9ybWF0VmVyc2lvbnMgPSB2b2lkIDA7XG4gICAgdGhpcy5lbmNyeXB0ZWQgPSB2b2lkIDA7XG4gICAgdGhpcy5pc0NvbW1vbkVuY3J5cHRpb24gPSB2b2lkIDA7XG4gICAgdGhpcy5pdiA9IG51bGw7XG4gICAgdGhpcy5rZXkgPSBudWxsO1xuICAgIHRoaXMua2V5SWQgPSBudWxsO1xuICAgIHRoaXMucHNzaCA9IG51bGw7XG4gICAgdGhpcy5tZXRob2QgPSBtZXRob2Q7XG4gICAgdGhpcy51cmkgPSB1cmk7XG4gICAgdGhpcy5rZXlGb3JtYXQgPSBmb3JtYXQ7XG4gICAgdGhpcy5rZXlGb3JtYXRWZXJzaW9ucyA9IGZvcm1hdHZlcnNpb25zO1xuICAgIHRoaXMuaXYgPSBpdjtcbiAgICB0aGlzLmVuY3J5cHRlZCA9IG1ldGhvZCA/IG1ldGhvZCAhPT0gJ05PTkUnIDogZmFsc2U7XG4gICAgdGhpcy5pc0NvbW1vbkVuY3J5cHRpb24gPSB0aGlzLmVuY3J5cHRlZCAmJiBtZXRob2QgIT09ICdBRVMtMTI4JztcbiAgfVxuICBpc1N1cHBvcnRlZCgpIHtcbiAgICAvLyBJZiBpdCdzIFNlZ21lbnQgZW5jcnlwdGlvbiBvciBObyBlbmNyeXB0aW9uLCBqdXN0IHNlbGVjdCB0aGF0IGtleSBzeXN0ZW1cbiAgICBpZiAodGhpcy5tZXRob2QpIHtcbiAgICAgIGlmICh0aGlzLm1ldGhvZCA9PT0gJ0FFUy0xMjgnIHx8IHRoaXMubWV0aG9kID09PSAnTk9ORScpIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgICBpZiAodGhpcy5rZXlGb3JtYXQgPT09ICdpZGVudGl0eScpIHtcbiAgICAgICAgLy8gTWFpbnRhaW4gc3VwcG9ydCBmb3IgY2xlYXIgU0FNUExFLUFFUyB3aXRoIE1QRUctMyBUU1xuICAgICAgICByZXR1cm4gdGhpcy5tZXRob2QgPT09ICdTQU1QTEUtQUVTJztcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHN3aXRjaCAodGhpcy5rZXlGb3JtYXQpIHtcbiAgICAgICAgICBjYXNlIEtleVN5c3RlbUZvcm1hdHMuRkFJUlBMQVk6XG4gICAgICAgICAgY2FzZSBLZXlTeXN0ZW1Gb3JtYXRzLldJREVWSU5FOlxuICAgICAgICAgIGNhc2UgS2V5U3lzdGVtRm9ybWF0cy5QTEFZUkVBRFk6XG4gICAgICAgICAgY2FzZSBLZXlTeXN0ZW1Gb3JtYXRzLkNMRUFSS0VZOlxuICAgICAgICAgICAgcmV0dXJuIFsnSVNPLTIzMDAxLTcnLCAnU0FNUExFLUFFUycsICdTQU1QTEUtQUVTLUNFTkMnLCAnU0FNUExFLUFFUy1DVFInXS5pbmRleE9mKHRoaXMubWV0aG9kKSAhPT0gLTE7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIGdldERlY3J5cHREYXRhKHNuKSB7XG4gICAgaWYgKCF0aGlzLmVuY3J5cHRlZCB8fCAhdGhpcy51cmkpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICBpZiAodGhpcy5tZXRob2QgPT09ICdBRVMtMTI4JyAmJiB0aGlzLnVyaSAmJiAhdGhpcy5pdikge1xuICAgICAgaWYgKHR5cGVvZiBzbiAhPT0gJ251bWJlcicpIHtcbiAgICAgICAgLy8gV2UgYXJlIGZldGNoaW5nIGRlY3J5cHRpb24gZGF0YSBmb3IgYSBpbml0aWFsaXphdGlvbiBzZWdtZW50XG4gICAgICAgIC8vIElmIHRoZSBzZWdtZW50IHdhcyBlbmNyeXB0ZWQgd2l0aCBBRVMtMTI4XG4gICAgICAgIC8vIEl0IG11c3QgaGF2ZSBhbiBJViBkZWZpbmVkLiBXZSBjYW5ub3Qgc3Vic3RpdHV0ZSB0aGUgU2VnbWVudCBOdW1iZXIgaW4uXG4gICAgICAgIGlmICh0aGlzLm1ldGhvZCA9PT0gJ0FFUy0xMjgnICYmICF0aGlzLml2KSB7XG4gICAgICAgICAgbG9nZ2VyLndhcm4oYG1pc3NpbmcgSVYgZm9yIGluaXRpYWxpemF0aW9uIHNlZ21lbnQgd2l0aCBtZXRob2Q9XCIke3RoaXMubWV0aG9kfVwiIC0gY29tcGxpYW5jZSBpc3N1ZWApO1xuICAgICAgICB9XG4gICAgICAgIC8vIEV4cGxpY2l0bHkgc2V0IHNuIHRvIHJlc3VsdGluZyB2YWx1ZSBmcm9tIGltcGxpY2l0IGNvbnZlcnNpb25zICdpbml0U2VnbWVudCcgdmFsdWVzIGZvciBJViBnZW5lcmF0aW9uLlxuICAgICAgICBzbiA9IDA7XG4gICAgICB9XG4gICAgICBjb25zdCBpdiA9IGNyZWF0ZUluaXRpYWxpemF0aW9uVmVjdG9yKHNuKTtcbiAgICAgIGNvbnN0IGRlY3J5cHRkYXRhID0gbmV3IExldmVsS2V5KHRoaXMubWV0aG9kLCB0aGlzLnVyaSwgJ2lkZW50aXR5JywgdGhpcy5rZXlGb3JtYXRWZXJzaW9ucywgaXYpO1xuICAgICAgcmV0dXJuIGRlY3J5cHRkYXRhO1xuICAgIH1cblxuICAgIC8vIEluaXRpYWxpemUga2V5SWQgaWYgcG9zc2libGVcbiAgICBjb25zdCBrZXlCeXRlcyA9IGNvbnZlcnREYXRhVXJpVG9BcnJheUJ5dGVzKHRoaXMudXJpKTtcbiAgICBpZiAoa2V5Qnl0ZXMpIHtcbiAgICAgIHN3aXRjaCAodGhpcy5rZXlGb3JtYXQpIHtcbiAgICAgICAgY2FzZSBLZXlTeXN0ZW1Gb3JtYXRzLldJREVWSU5FOlxuICAgICAgICAgIHRoaXMucHNzaCA9IGtleUJ5dGVzO1xuICAgICAgICAgIC8vIEluIGNhc2Ugb2Ygd2lkZXZpbmUga2V5SUQgaXMgZW1iZWRkZWQgaW4gUFNTSCBib3guIFJlYWQgS2V5IElELlxuICAgICAgICAgIGlmIChrZXlCeXRlcy5sZW5ndGggPj0gMjIpIHtcbiAgICAgICAgICAgIHRoaXMua2V5SWQgPSBrZXlCeXRlcy5zdWJhcnJheShrZXlCeXRlcy5sZW5ndGggLSAyMiwga2V5Qnl0ZXMubGVuZ3RoIC0gNik7XG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIEtleVN5c3RlbUZvcm1hdHMuUExBWVJFQURZOlxuICAgICAgICAgIHtcbiAgICAgICAgICAgIGNvbnN0IFBsYXlSZWFkeUtleVN5c3RlbVVVSUQgPSBuZXcgVWludDhBcnJheShbMHg5YSwgMHgwNCwgMHhmMCwgMHg3OSwgMHg5OCwgMHg0MCwgMHg0MiwgMHg4NiwgMHhhYiwgMHg5MiwgMHhlNiwgMHg1YiwgMHhlMCwgMHg4OCwgMHg1ZiwgMHg5NV0pO1xuICAgICAgICAgICAgdGhpcy5wc3NoID0gbXA0cHNzaChQbGF5UmVhZHlLZXlTeXN0ZW1VVUlELCBudWxsLCBrZXlCeXRlcyk7XG4gICAgICAgICAgICBjb25zdCBrZXlCeXRlc1V0ZjE2ID0gbmV3IFVpbnQxNkFycmF5KGtleUJ5dGVzLmJ1ZmZlciwga2V5Qnl0ZXMuYnl0ZU9mZnNldCwga2V5Qnl0ZXMuYnl0ZUxlbmd0aCAvIDIpO1xuICAgICAgICAgICAgY29uc3Qga2V5Qnl0ZVN0ciA9IFN0cmluZy5mcm9tQ2hhckNvZGUuYXBwbHkobnVsbCwgQXJyYXkuZnJvbShrZXlCeXRlc1V0ZjE2KSk7XG5cbiAgICAgICAgICAgIC8vIFBhcnNlIFBsYXlyZWFkeSBXUk1IZWFkZXIgWE1MXG4gICAgICAgICAgICBjb25zdCB4bWxLZXlCeXRlcyA9IGtleUJ5dGVTdHIuc3Vic3RyaW5nKGtleUJ5dGVTdHIuaW5kZXhPZignPCcpLCBrZXlCeXRlU3RyLmxlbmd0aCk7XG4gICAgICAgICAgICBjb25zdCBwYXJzZXIgPSBuZXcgRE9NUGFyc2VyKCk7XG4gICAgICAgICAgICBjb25zdCB4bWxEb2MgPSBwYXJzZXIucGFyc2VGcm9tU3RyaW5nKHhtbEtleUJ5dGVzLCAndGV4dC94bWwnKTtcbiAgICAgICAgICAgIGNvbnN0IGtleURhdGEgPSB4bWxEb2MuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ0tJRCcpWzBdO1xuICAgICAgICAgICAgaWYgKGtleURhdGEpIHtcbiAgICAgICAgICAgICAgY29uc3Qga2V5SWQgPSBrZXlEYXRhLmNoaWxkTm9kZXNbMF0gPyBrZXlEYXRhLmNoaWxkTm9kZXNbMF0ubm9kZVZhbHVlIDoga2V5RGF0YS5nZXRBdHRyaWJ1dGUoJ1ZBTFVFJyk7XG4gICAgICAgICAgICAgIGlmIChrZXlJZCkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGtleUlkQXJyYXkgPSBiYXNlNjREZWNvZGUoa2V5SWQpLnN1YmFycmF5KDAsIDE2KTtcbiAgICAgICAgICAgICAgICAvLyBLSUQgdmFsdWUgaW4gUFJPIGlzIGEgYmFzZTY0LWVuY29kZWQgbGl0dGxlIGVuZGlhbiBHVUlEIGludGVycHJldGF0aW9uIG9mIFVVSURcbiAgICAgICAgICAgICAgICAvLyBLSUQgdmFsdWUgaW4g4oCYdGVuY+KAmSBpcyBhIGJpZyBlbmRpYW4gVVVJRCBHVUlEIGludGVycHJldGF0aW9uIG9mIFVVSURcbiAgICAgICAgICAgICAgICBjaGFuZ2VFbmRpYW5uZXNzKGtleUlkQXJyYXkpO1xuICAgICAgICAgICAgICAgIHRoaXMua2V5SWQgPSBrZXlJZEFycmF5O1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICB9XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAge1xuICAgICAgICAgICAgbGV0IGtleWRhdGEgPSBrZXlCeXRlcy5zdWJhcnJheSgwLCAxNik7XG4gICAgICAgICAgICBpZiAoa2V5ZGF0YS5sZW5ndGggIT09IDE2KSB7XG4gICAgICAgICAgICAgIGNvbnN0IHBhZGRlZCA9IG5ldyBVaW50OEFycmF5KDE2KTtcbiAgICAgICAgICAgICAgcGFkZGVkLnNldChrZXlkYXRhLCAxNiAtIGtleWRhdGEubGVuZ3RoKTtcbiAgICAgICAgICAgICAga2V5ZGF0YSA9IHBhZGRlZDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMua2V5SWQgPSBrZXlkYXRhO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIERlZmF1bHQgYmVoYXZpb3I6IGFzc2lnbiBhIG5ldyBrZXlJZCBmb3IgZWFjaCB1cmlcbiAgICBpZiAoIXRoaXMua2V5SWQgfHwgdGhpcy5rZXlJZC5ieXRlTGVuZ3RoICE9PSAxNikge1xuICAgICAgbGV0IGtleUlkID0ga2V5VXJpVG9LZXlJZE1hcFt0aGlzLnVyaV07XG4gICAgICBpZiAoIWtleUlkKSB7XG4gICAgICAgIGNvbnN0IHZhbCA9IE9iamVjdC5rZXlzKGtleVVyaVRvS2V5SWRNYXApLmxlbmd0aCAlIE51bWJlci5NQVhfU0FGRV9JTlRFR0VSO1xuICAgICAgICBrZXlJZCA9IG5ldyBVaW50OEFycmF5KDE2KTtcbiAgICAgICAgY29uc3QgZHYgPSBuZXcgRGF0YVZpZXcoa2V5SWQuYnVmZmVyLCAxMiwgNCk7IC8vIEp1c3Qgc2V0IHRoZSBsYXN0IDQgYnl0ZXNcbiAgICAgICAgZHYuc2V0VWludDMyKDAsIHZhbCk7XG4gICAgICAgIGtleVVyaVRvS2V5SWRNYXBbdGhpcy51cmldID0ga2V5SWQ7XG4gICAgICB9XG4gICAgICB0aGlzLmtleUlkID0ga2V5SWQ7XG4gICAgfVxuICAgIHJldHVybiB0aGlzO1xuICB9XG59XG5mdW5jdGlvbiBjcmVhdGVJbml0aWFsaXphdGlvblZlY3RvcihzZWdtZW50TnVtYmVyKSB7XG4gIGNvbnN0IHVpbnQ4VmlldyA9IG5ldyBVaW50OEFycmF5KDE2KTtcbiAgZm9yIChsZXQgaSA9IDEyOyBpIDwgMTY7IGkrKykge1xuICAgIHVpbnQ4Vmlld1tpXSA9IHNlZ21lbnROdW1iZXIgPj4gOCAqICgxNSAtIGkpICYgMHhmZjtcbiAgfVxuICByZXR1cm4gdWludDhWaWV3O1xufVxuXG5jb25zdCBWQVJJQUJMRV9SRVBMQUNFTUVOVF9SRUdFWCA9IC9cXHtcXCQoW2EtekEtWjAtOS1fXSspXFx9L2c7XG5mdW5jdGlvbiBoYXNWYXJpYWJsZVJlZmVyZW5jZXMoc3RyKSB7XG4gIHJldHVybiBWQVJJQUJMRV9SRVBMQUNFTUVOVF9SRUdFWC50ZXN0KHN0cik7XG59XG5mdW5jdGlvbiBzdWJzdGl0dXRlVmFyaWFibGVzSW5BdHRyaWJ1dGVzKHBhcnNlZCwgYXR0ciwgYXR0cmlidXRlTmFtZXMpIHtcbiAgaWYgKHBhcnNlZC52YXJpYWJsZUxpc3QgIT09IG51bGwgfHwgcGFyc2VkLmhhc1ZhcmlhYmxlUmVmcykge1xuICAgIGZvciAobGV0IGkgPSBhdHRyaWJ1dGVOYW1lcy5sZW5ndGg7IGktLTspIHtcbiAgICAgIGNvbnN0IG5hbWUgPSBhdHRyaWJ1dGVOYW1lc1tpXTtcbiAgICAgIGNvbnN0IHZhbHVlID0gYXR0cltuYW1lXTtcbiAgICAgIGlmICh2YWx1ZSkge1xuICAgICAgICBhdHRyW25hbWVdID0gc3Vic3RpdHV0ZVZhcmlhYmxlcyhwYXJzZWQsIHZhbHVlKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbn1cbmZ1bmN0aW9uIHN1YnN0aXR1dGVWYXJpYWJsZXMocGFyc2VkLCB2YWx1ZSkge1xuICBpZiAocGFyc2VkLnZhcmlhYmxlTGlzdCAhPT0gbnVsbCB8fCBwYXJzZWQuaGFzVmFyaWFibGVSZWZzKSB7XG4gICAgY29uc3QgdmFyaWFibGVMaXN0ID0gcGFyc2VkLnZhcmlhYmxlTGlzdDtcbiAgICByZXR1cm4gdmFsdWUucmVwbGFjZShWQVJJQUJMRV9SRVBMQUNFTUVOVF9SRUdFWCwgdmFyaWFibGVSZWZlcmVuY2UgPT4ge1xuICAgICAgY29uc3QgdmFyaWFibGVOYW1lID0gdmFyaWFibGVSZWZlcmVuY2Uuc3Vic3RyaW5nKDIsIHZhcmlhYmxlUmVmZXJlbmNlLmxlbmd0aCAtIDEpO1xuICAgICAgY29uc3QgdmFyaWFibGVWYWx1ZSA9IHZhcmlhYmxlTGlzdCA9PSBudWxsID8gdm9pZCAwIDogdmFyaWFibGVMaXN0W3ZhcmlhYmxlTmFtZV07XG4gICAgICBpZiAodmFyaWFibGVWYWx1ZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHBhcnNlZC5wbGF5bGlzdFBhcnNpbmdFcnJvciB8fCAocGFyc2VkLnBsYXlsaXN0UGFyc2luZ0Vycm9yID0gbmV3IEVycm9yKGBNaXNzaW5nIHByZWNlZGluZyBFWFQtWC1ERUZJTkUgdGFnIGZvciBWYXJpYWJsZSBSZWZlcmVuY2U6IFwiJHt2YXJpYWJsZU5hbWV9XCJgKSk7XG4gICAgICAgIHJldHVybiB2YXJpYWJsZVJlZmVyZW5jZTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB2YXJpYWJsZVZhbHVlO1xuICAgIH0pO1xuICB9XG4gIHJldHVybiB2YWx1ZTtcbn1cbmZ1bmN0aW9uIGFkZFZhcmlhYmxlRGVmaW5pdGlvbihwYXJzZWQsIGF0dHIsIHBhcmVudFVybCkge1xuICBsZXQgdmFyaWFibGVMaXN0ID0gcGFyc2VkLnZhcmlhYmxlTGlzdDtcbiAgaWYgKCF2YXJpYWJsZUxpc3QpIHtcbiAgICBwYXJzZWQudmFyaWFibGVMaXN0ID0gdmFyaWFibGVMaXN0ID0ge307XG4gIH1cbiAgbGV0IE5BTUU7XG4gIGxldCBWQUxVRTtcbiAgaWYgKCdRVUVSWVBBUkFNJyBpbiBhdHRyKSB7XG4gICAgTkFNRSA9IGF0dHIuUVVFUllQQVJBTTtcbiAgICB0cnkge1xuICAgICAgY29uc3Qgc2VhcmNoUGFyYW1zID0gbmV3IHNlbGYuVVJMKHBhcmVudFVybCkuc2VhcmNoUGFyYW1zO1xuICAgICAgaWYgKHNlYXJjaFBhcmFtcy5oYXMoTkFNRSkpIHtcbiAgICAgICAgVkFMVUUgPSBzZWFyY2hQYXJhbXMuZ2V0KE5BTUUpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBcIiR7TkFNRX1cIiBkb2VzIG5vdCBtYXRjaCBhbnkgcXVlcnkgcGFyYW1ldGVyIGluIFVSSTogXCIke3BhcmVudFVybH1cImApO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBwYXJzZWQucGxheWxpc3RQYXJzaW5nRXJyb3IgfHwgKHBhcnNlZC5wbGF5bGlzdFBhcnNpbmdFcnJvciA9IG5ldyBFcnJvcihgRVhULVgtREVGSU5FIFFVRVJZUEFSQU06ICR7ZXJyb3IubWVzc2FnZX1gKSk7XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIE5BTUUgPSBhdHRyLk5BTUU7XG4gICAgVkFMVUUgPSBhdHRyLlZBTFVFO1xuICB9XG4gIGlmIChOQU1FIGluIHZhcmlhYmxlTGlzdCkge1xuICAgIHBhcnNlZC5wbGF5bGlzdFBhcnNpbmdFcnJvciB8fCAocGFyc2VkLnBsYXlsaXN0UGFyc2luZ0Vycm9yID0gbmV3IEVycm9yKGBFWFQtWC1ERUZJTkUgZHVwbGljYXRlIFZhcmlhYmxlIE5hbWUgZGVjbGFyYXRpb25zOiBcIiR7TkFNRX1cImApKTtcbiAgfSBlbHNlIHtcbiAgICB2YXJpYWJsZUxpc3RbTkFNRV0gPSBWQUxVRSB8fCAnJztcbiAgfVxufVxuZnVuY3Rpb24gaW1wb3J0VmFyaWFibGVEZWZpbml0aW9uKHBhcnNlZCwgYXR0ciwgc291cmNlVmFyaWFibGVMaXN0KSB7XG4gIGNvbnN0IElNUE9SVCA9IGF0dHIuSU1QT1JUO1xuICBpZiAoc291cmNlVmFyaWFibGVMaXN0ICYmIElNUE9SVCBpbiBzb3VyY2VWYXJpYWJsZUxpc3QpIHtcbiAgICBsZXQgdmFyaWFibGVMaXN0ID0gcGFyc2VkLnZhcmlhYmxlTGlzdDtcbiAgICBpZiAoIXZhcmlhYmxlTGlzdCkge1xuICAgICAgcGFyc2VkLnZhcmlhYmxlTGlzdCA9IHZhcmlhYmxlTGlzdCA9IHt9O1xuICAgIH1cbiAgICB2YXJpYWJsZUxpc3RbSU1QT1JUXSA9IHNvdXJjZVZhcmlhYmxlTGlzdFtJTVBPUlRdO1xuICB9IGVsc2Uge1xuICAgIHBhcnNlZC5wbGF5bGlzdFBhcnNpbmdFcnJvciB8fCAocGFyc2VkLnBsYXlsaXN0UGFyc2luZ0Vycm9yID0gbmV3IEVycm9yKGBFWFQtWC1ERUZJTkUgSU1QT1JUIGF0dHJpYnV0ZSBub3QgZm91bmQgaW4gTXVsdGl2YXJpYW50IFBsYXlsaXN0OiBcIiR7SU1QT1JUfVwiYCkpO1xuICB9XG59XG5cbi8qKlxuICogTWVkaWFTb3VyY2UgaGVscGVyXG4gKi9cblxuZnVuY3Rpb24gZ2V0TWVkaWFTb3VyY2UocHJlZmVyTWFuYWdlZE1lZGlhU291cmNlID0gdHJ1ZSkge1xuICBpZiAodHlwZW9mIHNlbGYgPT09ICd1bmRlZmluZWQnKSByZXR1cm4gdW5kZWZpbmVkO1xuICBjb25zdCBtbXMgPSAocHJlZmVyTWFuYWdlZE1lZGlhU291cmNlIHx8ICFzZWxmLk1lZGlhU291cmNlKSAmJiBzZWxmLk1hbmFnZWRNZWRpYVNvdXJjZTtcbiAgcmV0dXJuIG1tcyB8fCBzZWxmLk1lZGlhU291cmNlIHx8IHNlbGYuV2ViS2l0TWVkaWFTb3VyY2U7XG59XG5mdW5jdGlvbiBpc01hbmFnZWRNZWRpYVNvdXJjZShzb3VyY2UpIHtcbiAgcmV0dXJuIHR5cGVvZiBzZWxmICE9PSAndW5kZWZpbmVkJyAmJiBzb3VyY2UgPT09IHNlbGYuTWFuYWdlZE1lZGlhU291cmNlO1xufVxuXG4vLyBmcm9tIGh0dHA6Ly9tcDRyYS5vcmcvY29kZWNzLmh0bWxcbi8vIHZhbHVlcyBpbmRpY2F0ZSBjb2RlYyBzZWxlY3Rpb24gcHJlZmVyZW5jZSAobG93ZXIgaXMgaGlnaGVyIHByaW9yaXR5KVxuY29uc3Qgc2FtcGxlRW50cnlDb2Rlc0lTTyA9IHtcbiAgYXVkaW86IHtcbiAgICBhM2RzOiAxLFxuICAgICdhYy0zJzogMC45NSxcbiAgICAnYWMtNCc6IDEsXG4gICAgYWxhYzogMC45LFxuICAgIGFsYXc6IDEsXG4gICAgZHJhMTogMSxcbiAgICAnZHRzKyc6IDEsXG4gICAgJ2R0cy0nOiAxLFxuICAgIGR0c2M6IDEsXG4gICAgZHRzZTogMSxcbiAgICBkdHNoOiAxLFxuICAgICdlYy0zJzogMC45LFxuICAgIGVuY2E6IDEsXG4gICAgZkxhQzogMC45LFxuICAgIC8vIE1QNC1SQSBsaXN0ZWQgY29kZWMgZW50cnkgZm9yIEZMQUNcbiAgICBmbGFjOiAwLjksXG4gICAgLy8gbGVnYWN5IGJyb3dzZXIgY29kZWMgbmFtZSBmb3IgRkxBQ1xuICAgIEZMQUM6IDAuOSxcbiAgICAvLyBzb21lIG1hbmlmZXN0cyBtYXkgbGlzdCBcIkZMQUNcIiB3aXRoIEFwcGxlJ3MgdG9vbHNcbiAgICBnNzE5OiAxLFxuICAgIGc3MjY6IDEsXG4gICAgbTRhZTogMSxcbiAgICBtaGExOiAxLFxuICAgIG1oYTI6IDEsXG4gICAgbWhtMTogMSxcbiAgICBtaG0yOiAxLFxuICAgIG1scGE6IDEsXG4gICAgbXA0YTogMSxcbiAgICAncmF3ICc6IDEsXG4gICAgT3B1czogMSxcbiAgICBvcHVzOiAxLFxuICAgIC8vIGJyb3dzZXJzIGV4cGVjdCB0aGlzIHRvIGJlIGxvd2VyY2FzZSBkZXNwaXRlIE1QNFJBIHNheXMgJ09wdXMnXG4gICAgc2FtcjogMSxcbiAgICBzYXdiOiAxLFxuICAgIHNhd3A6IDEsXG4gICAgc2V2YzogMSxcbiAgICBzcWNwOiAxLFxuICAgIHNzbXY6IDEsXG4gICAgdHdvczogMSxcbiAgICB1bGF3OiAxXG4gIH0sXG4gIHZpZGVvOiB7XG4gICAgYXZjMTogMSxcbiAgICBhdmMyOiAxLFxuICAgIGF2YzM6IDEsXG4gICAgYXZjNDogMSxcbiAgICBhdmNwOiAxLFxuICAgIGF2MDE6IDAuOCxcbiAgICBkcmFjOiAxLFxuICAgIGR2YTE6IDEsXG4gICAgZHZhdjogMSxcbiAgICBkdmgxOiAwLjcsXG4gICAgZHZoZTogMC43LFxuICAgIGVuY3Y6IDEsXG4gICAgaGV2MTogMC43NSxcbiAgICBodmMxOiAwLjc1LFxuICAgIG1qcDI6IDEsXG4gICAgbXA0djogMSxcbiAgICBtdmMxOiAxLFxuICAgIG12YzI6IDEsXG4gICAgbXZjMzogMSxcbiAgICBtdmM0OiAxLFxuICAgIHJlc3Y6IDEsXG4gICAgcnY2MDogMSxcbiAgICBzMjYzOiAxLFxuICAgIHN2YzE6IDEsXG4gICAgc3ZjMjogMSxcbiAgICAndmMtMSc6IDEsXG4gICAgdnAwODogMSxcbiAgICB2cDA5OiAwLjlcbiAgfSxcbiAgdGV4dDoge1xuICAgIHN0cHA6IDEsXG4gICAgd3Z0dDogMVxuICB9XG59O1xuZnVuY3Rpb24gaXNDb2RlY1R5cGUoY29kZWMsIHR5cGUpIHtcbiAgY29uc3QgdHlwZUNvZGVzID0gc2FtcGxlRW50cnlDb2Rlc0lTT1t0eXBlXTtcbiAgcmV0dXJuICEhdHlwZUNvZGVzICYmICEhdHlwZUNvZGVzW2NvZGVjLnNsaWNlKDAsIDQpXTtcbn1cbmZ1bmN0aW9uIGFyZUNvZGVjc01lZGlhU291cmNlU3VwcG9ydGVkKGNvZGVjcywgdHlwZSwgcHJlZmVyTWFuYWdlZE1lZGlhU291cmNlID0gdHJ1ZSkge1xuICByZXR1cm4gIWNvZGVjcy5zcGxpdCgnLCcpLnNvbWUoY29kZWMgPT4gIWlzQ29kZWNNZWRpYVNvdXJjZVN1cHBvcnRlZChjb2RlYywgdHlwZSwgcHJlZmVyTWFuYWdlZE1lZGlhU291cmNlKSk7XG59XG5mdW5jdGlvbiBpc0NvZGVjTWVkaWFTb3VyY2VTdXBwb3J0ZWQoY29kZWMsIHR5cGUsIHByZWZlck1hbmFnZWRNZWRpYVNvdXJjZSA9IHRydWUpIHtcbiAgdmFyIF9NZWRpYVNvdXJjZSRpc1R5cGVTdTtcbiAgY29uc3QgTWVkaWFTb3VyY2UgPSBnZXRNZWRpYVNvdXJjZShwcmVmZXJNYW5hZ2VkTWVkaWFTb3VyY2UpO1xuICByZXR1cm4gKF9NZWRpYVNvdXJjZSRpc1R5cGVTdSA9IE1lZGlhU291cmNlID09IG51bGwgPyB2b2lkIDAgOiBNZWRpYVNvdXJjZS5pc1R5cGVTdXBwb3J0ZWQobWltZVR5cGVGb3JDb2RlYyhjb2RlYywgdHlwZSkpKSAhPSBudWxsID8gX01lZGlhU291cmNlJGlzVHlwZVN1IDogZmFsc2U7XG59XG5mdW5jdGlvbiBtaW1lVHlwZUZvckNvZGVjKGNvZGVjLCB0eXBlKSB7XG4gIHJldHVybiBgJHt0eXBlfS9tcDQ7Y29kZWNzPVwiJHtjb2RlY31cImA7XG59XG5mdW5jdGlvbiB2aWRlb0NvZGVjUHJlZmVyZW5jZVZhbHVlKHZpZGVvQ29kZWMpIHtcbiAgaWYgKHZpZGVvQ29kZWMpIHtcbiAgICBjb25zdCBmb3VyQ0MgPSB2aWRlb0NvZGVjLnN1YnN0cmluZygwLCA0KTtcbiAgICByZXR1cm4gc2FtcGxlRW50cnlDb2Rlc0lTTy52aWRlb1tmb3VyQ0NdO1xuICB9XG4gIHJldHVybiAyO1xufVxuZnVuY3Rpb24gY29kZWNzU2V0U2VsZWN0aW9uUHJlZmVyZW5jZVZhbHVlKGNvZGVjU2V0KSB7XG4gIHJldHVybiBjb2RlY1NldC5zcGxpdCgnLCcpLnJlZHVjZSgobnVtLCBmb3VyQ0MpID0+IHtcbiAgICBjb25zdCBwcmVmZXJlbmNlVmFsdWUgPSBzYW1wbGVFbnRyeUNvZGVzSVNPLnZpZGVvW2ZvdXJDQ107XG4gICAgaWYgKHByZWZlcmVuY2VWYWx1ZSkge1xuICAgICAgcmV0dXJuIChwcmVmZXJlbmNlVmFsdWUgKiAyICsgbnVtKSAvIChudW0gPyAzIDogMik7XG4gICAgfVxuICAgIHJldHVybiAoc2FtcGxlRW50cnlDb2Rlc0lTTy5hdWRpb1tmb3VyQ0NdICsgbnVtKSAvIChudW0gPyAyIDogMSk7XG4gIH0sIDApO1xufVxuY29uc3QgQ09ERUNfQ09NUEFUSUJMRV9OQU1FUyA9IHt9O1xuZnVuY3Rpb24gZ2V0Q29kZWNDb21wYXRpYmxlTmFtZUxvd2VyKGxvd2VyQ2FzZUNvZGVjLCBwcmVmZXJNYW5hZ2VkTWVkaWFTb3VyY2UgPSB0cnVlKSB7XG4gIGlmIChDT0RFQ19DT01QQVRJQkxFX05BTUVTW2xvd2VyQ2FzZUNvZGVjXSkge1xuICAgIHJldHVybiBDT0RFQ19DT01QQVRJQkxFX05BTUVTW2xvd2VyQ2FzZUNvZGVjXTtcbiAgfVxuXG4gIC8vIElkZWFseSBmTGFDIGFuZCBPcHVzIHdvdWxkIGJlIGZpcnN0IChzcGVjLWNvbXBsaWFudCkgYnV0XG4gIC8vIHNvbWUgYnJvd3NlcnMgd2lsbCByZXBvcnQgdGhhdCBmTGFDIGlzIHN1cHBvcnRlZCB0aGVuIGZhaWwuXG4gIC8vIHNlZTogaHR0cHM6Ly9idWdzLmNocm9taXVtLm9yZy9wL2Nocm9taXVtL2lzc3Vlcy9kZXRhaWw/aWQ9MTQyMjcyOFxuICBjb25zdCBjb2RlY3NUb0NoZWNrID0ge1xuICAgIGZsYWM6IFsnZmxhYycsICdmTGFDJywgJ0ZMQUMnXSxcbiAgICBvcHVzOiBbJ29wdXMnLCAnT3B1cyddXG4gIH1bbG93ZXJDYXNlQ29kZWNdO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGNvZGVjc1RvQ2hlY2subGVuZ3RoOyBpKyspIHtcbiAgICBpZiAoaXNDb2RlY01lZGlhU291cmNlU3VwcG9ydGVkKGNvZGVjc1RvQ2hlY2tbaV0sICdhdWRpbycsIHByZWZlck1hbmFnZWRNZWRpYVNvdXJjZSkpIHtcbiAgICAgIENPREVDX0NPTVBBVElCTEVfTkFNRVNbbG93ZXJDYXNlQ29kZWNdID0gY29kZWNzVG9DaGVja1tpXTtcbiAgICAgIHJldHVybiBjb2RlY3NUb0NoZWNrW2ldO1xuICAgIH1cbiAgfVxuICByZXR1cm4gbG93ZXJDYXNlQ29kZWM7XG59XG5jb25zdCBBVURJT19DT0RFQ19SRUdFWFAgPSAvZmxhY3xvcHVzL2k7XG5mdW5jdGlvbiBnZXRDb2RlY0NvbXBhdGlibGVOYW1lKGNvZGVjLCBwcmVmZXJNYW5hZ2VkTWVkaWFTb3VyY2UgPSB0cnVlKSB7XG4gIHJldHVybiBjb2RlYy5yZXBsYWNlKEFVRElPX0NPREVDX1JFR0VYUCwgbSA9PiBnZXRDb2RlY0NvbXBhdGlibGVOYW1lTG93ZXIobS50b0xvd2VyQ2FzZSgpLCBwcmVmZXJNYW5hZ2VkTWVkaWFTb3VyY2UpKTtcbn1cbmZ1bmN0aW9uIHBpY2tNb3N0Q29tcGxldGVDb2RlY05hbWUocGFyc2VkQ29kZWMsIGxldmVsQ29kZWMpIHtcbiAgLy8gUGFyc2luZyBvZiBtcDRhIGNvZGVjcyBzdHJpbmdzIGluIG1wNC10b29scyBmcm9tIG1lZGlhIGlzIGluY29tcGxldGUgYXMgb2YgZDhjNmM3YVxuICAvLyBzbyB1c2UgbGV2ZWwgY29kZWMgaXMgcGFyc2VkIGNvZGVjIGlzIHVuYXZhaWxhYmxlIG9yIGluY29tcGxldGVcbiAgaWYgKHBhcnNlZENvZGVjICYmIHBhcnNlZENvZGVjICE9PSAnbXA0YScpIHtcbiAgICByZXR1cm4gcGFyc2VkQ29kZWM7XG4gIH1cbiAgcmV0dXJuIGxldmVsQ29kZWMgPyBsZXZlbENvZGVjLnNwbGl0KCcsJylbMF0gOiBsZXZlbENvZGVjO1xufVxuZnVuY3Rpb24gY29udmVydEFWQzFUb0FWQ09USShjb2RlYykge1xuICAvLyBDb252ZXJ0IGF2YzEgY29kZWMgc3RyaW5nIGZyb20gUkZDLTQyODEgdG8gUkZDLTYzODEgZm9yIE1lZGlhU291cmNlLmlzVHlwZVN1cHBvcnRlZFxuICAvLyBFeGFtcGxlczogYXZjMS42Ni4zMCB0byBhdmMxLjQyMDAxZSBhbmQgYXZjMS43Ny4zMCxhdmMxLjY2LjMwIHRvIGF2YzEuNGQwMDFlLGF2YzEuNDIwMDFlLlxuICBjb25zdCBjb2RlY3MgPSBjb2RlYy5zcGxpdCgnLCcpO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGNvZGVjcy5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IGF2Y2RhdGEgPSBjb2RlY3NbaV0uc3BsaXQoJy4nKTtcbiAgICBpZiAoYXZjZGF0YS5sZW5ndGggPiAyKSB7XG4gICAgICBsZXQgcmVzdWx0ID0gYXZjZGF0YS5zaGlmdCgpICsgJy4nO1xuICAgICAgcmVzdWx0ICs9IHBhcnNlSW50KGF2Y2RhdGEuc2hpZnQoKSkudG9TdHJpbmcoMTYpO1xuICAgICAgcmVzdWx0ICs9ICgnMDAwJyArIHBhcnNlSW50KGF2Y2RhdGEuc2hpZnQoKSkudG9TdHJpbmcoMTYpKS5zbGljZSgtNCk7XG4gICAgICBjb2RlY3NbaV0gPSByZXN1bHQ7XG4gICAgfVxuICB9XG4gIHJldHVybiBjb2RlY3Muam9pbignLCcpO1xufVxuXG5jb25zdCBNQVNURVJfUExBWUxJU1RfUkVHRVggPSAvI0VYVC1YLVNUUkVBTS1JTkY6KFteXFxyXFxuXSopKD86W1xcclxcbl0oPzojW15cXHJcXG5dKik/KSooW15cXHJcXG5dKyl8I0VYVC1YLShTRVNTSU9OLURBVEF8U0VTU0lPTi1LRVl8REVGSU5FfENPTlRFTlQtU1RFRVJJTkd8U1RBUlQpOihbXlxcclxcbl0qKVtcXHJcXG5dKy9nO1xuY29uc3QgTUFTVEVSX1BMQVlMSVNUX01FRElBX1JFR0VYID0gLyNFWFQtWC1NRURJQTooLiopL2c7XG5jb25zdCBJU19NRURJQV9QTEFZTElTVCA9IC9eI0VYVCg/OklORnwtWC1UQVJHRVREVVJBVElPTik6L207IC8vIEhhbmRsZSBlbXB0eSBNZWRpYSBQbGF5bGlzdCAoZmlyc3QgRVhUSU5GIG5vdCBzaWduYWxlZCwgYnV0IFRBUkdFVERVUkFUSU9OIHByZXNlbnQpXG5cbmNvbnN0IExFVkVMX1BMQVlMSVNUX1JFR0VYX0ZBU1QgPSBuZXcgUmVnRXhwKFsvI0VYVElORjpcXHMqKFxcZCooPzpcXC5cXGQrKT8pKD86LCguKilcXHMrKT8vLnNvdXJjZSxcbi8vIGR1cmF0aW9uICgjRVhUSU5GOjxkdXJhdGlvbj4sPHRpdGxlPiksIGdyb3VwIDEgPT4gZHVyYXRpb24sIGdyb3VwIDIgPT4gdGl0bGVcbi8oPyEjKSAqKFxcU1teXFxyXFxuXSopLy5zb3VyY2UsXG4vLyBzZWdtZW50IFVSSSwgZ3JvdXAgMyA9PiB0aGUgVVJJIChub3RlIG5ld2xpbmUgaXMgbm90IGVhdGVuKVxuLyNFWFQtWC1CWVRFUkFOR0U6KiguKykvLnNvdXJjZSxcbi8vIG5leHQgc2VnbWVudCdzIGJ5dGVyYW5nZSwgZ3JvdXAgNCA9PiByYW5nZSBzcGVjICh4QHkpXG4vI0VYVC1YLVBST0dSQU0tREFURS1USU1FOiguKykvLnNvdXJjZSxcbi8vIG5leHQgc2VnbWVudCdzIHByb2dyYW0gZGF0ZS90aW1lIGdyb3VwIDUgPT4gdGhlIGRhdGV0aW1lIHNwZWNcbi8jLiovLnNvdXJjZSAvLyBBbGwgb3RoZXIgbm9uLXNlZ21lbnQgb3JpZW50ZWQgdGFncyB3aWxsIG1hdGNoIHdpdGggYWxsIGdyb3VwcyBlbXB0eVxuXS5qb2luKCd8JyksICdnJyk7XG5jb25zdCBMRVZFTF9QTEFZTElTVF9SRUdFWF9TTE9XID0gbmV3IFJlZ0V4cChbLyMoRVhUTTNVKS8uc291cmNlLCAvI0VYVC1YLShEQVRFUkFOR0V8REVGSU5FfEtFWXxNQVB8UEFSVHxQQVJULUlORnxQTEFZTElTVC1UWVBFfFBSRUxPQUQtSElOVHxSRU5ESVRJT04tUkVQT1JUfFNFUlZFUi1DT05UUk9MfFNLSVB8U1RBUlQpOiguKykvLnNvdXJjZSwgLyNFWFQtWC0oQklUUkFURXxESVNDT05USU5VSVRZLVNFUVVFTkNFfE1FRElBLVNFUVVFTkNFfFRBUkdFVERVUkFUSU9OfFZFUlNJT04pOiAqKFxcZCspLy5zb3VyY2UsIC8jRVhULVgtKERJU0NPTlRJTlVJVFl8RU5ETElTVHxHQVB8SU5ERVBFTkRFTlQtU0VHTUVOVFMpLy5zb3VyY2UsIC8oIykoW146XSopOiguKikvLnNvdXJjZSwgLygjKSguKikoPzouKilcXHI/XFxuPy8uc291cmNlXS5qb2luKCd8JykpO1xuY2xhc3MgTTNVOFBhcnNlciB7XG4gIHN0YXRpYyBmaW5kR3JvdXAoZ3JvdXBzLCBtZWRpYUdyb3VwSWQpIHtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGdyb3Vwcy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgZ3JvdXAgPSBncm91cHNbaV07XG4gICAgICBpZiAoZ3JvdXAuaWQgPT09IG1lZGlhR3JvdXBJZCkge1xuICAgICAgICByZXR1cm4gZ3JvdXA7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIHN0YXRpYyByZXNvbHZlKHVybCwgYmFzZVVybCkge1xuICAgIHJldHVybiB1cmxUb29sa2l0RXhwb3J0cy5idWlsZEFic29sdXRlVVJMKGJhc2VVcmwsIHVybCwge1xuICAgICAgYWx3YXlzTm9ybWFsaXplOiB0cnVlXG4gICAgfSk7XG4gIH1cbiAgc3RhdGljIGlzTWVkaWFQbGF5bGlzdChzdHIpIHtcbiAgICByZXR1cm4gSVNfTUVESUFfUExBWUxJU1QudGVzdChzdHIpO1xuICB9XG4gIHN0YXRpYyBwYXJzZU1hc3RlclBsYXlsaXN0KHN0cmluZywgYmFzZXVybCkge1xuICAgIGNvbnN0IGhhc1ZhcmlhYmxlUmVmcyA9IGhhc1ZhcmlhYmxlUmVmZXJlbmNlcyhzdHJpbmcpIDtcbiAgICBjb25zdCBwYXJzZWQgPSB7XG4gICAgICBjb250ZW50U3RlZXJpbmc6IG51bGwsXG4gICAgICBsZXZlbHM6IFtdLFxuICAgICAgcGxheWxpc3RQYXJzaW5nRXJyb3I6IG51bGwsXG4gICAgICBzZXNzaW9uRGF0YTogbnVsbCxcbiAgICAgIHNlc3Npb25LZXlzOiBudWxsLFxuICAgICAgc3RhcnRUaW1lT2Zmc2V0OiBudWxsLFxuICAgICAgdmFyaWFibGVMaXN0OiBudWxsLFxuICAgICAgaGFzVmFyaWFibGVSZWZzXG4gICAgfTtcbiAgICBjb25zdCBsZXZlbHNXaXRoS25vd25Db2RlY3MgPSBbXTtcbiAgICBNQVNURVJfUExBWUxJU1RfUkVHRVgubGFzdEluZGV4ID0gMDtcbiAgICBsZXQgcmVzdWx0O1xuICAgIHdoaWxlICgocmVzdWx0ID0gTUFTVEVSX1BMQVlMSVNUX1JFR0VYLmV4ZWMoc3RyaW5nKSkgIT0gbnVsbCkge1xuICAgICAgaWYgKHJlc3VsdFsxXSkge1xuICAgICAgICB2YXIgX2xldmVsJHVua25vd25Db2RlY3M7XG4gICAgICAgIC8vICcjRVhULVgtU1RSRUFNLUlORicgaXMgZm91bmQsIHBhcnNlIGxldmVsIHRhZyAgaW4gZ3JvdXAgMVxuICAgICAgICBjb25zdCBhdHRycyA9IG5ldyBBdHRyTGlzdChyZXN1bHRbMV0pO1xuICAgICAgICB7XG4gICAgICAgICAgc3Vic3RpdHV0ZVZhcmlhYmxlc0luQXR0cmlidXRlcyhwYXJzZWQsIGF0dHJzLCBbJ0NPREVDUycsICdTVVBQTEVNRU5UQUwtQ09ERUNTJywgJ0FMTE9XRUQtQ1BDJywgJ1BBVEhXQVktSUQnLCAnU1RBQkxFLVZBUklBTlQtSUQnLCAnQVVESU8nLCAnVklERU8nLCAnU1VCVElUTEVTJywgJ0NMT1NFRC1DQVBUSU9OUycsICdOQU1FJ10pO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHVyaSA9IHN1YnN0aXR1dGVWYXJpYWJsZXMocGFyc2VkLCByZXN1bHRbMl0pIDtcbiAgICAgICAgY29uc3QgbGV2ZWwgPSB7XG4gICAgICAgICAgYXR0cnMsXG4gICAgICAgICAgYml0cmF0ZTogYXR0cnMuZGVjaW1hbEludGVnZXIoJ0JBTkRXSURUSCcpIHx8IGF0dHJzLmRlY2ltYWxJbnRlZ2VyKCdBVkVSQUdFLUJBTkRXSURUSCcpLFxuICAgICAgICAgIG5hbWU6IGF0dHJzLk5BTUUsXG4gICAgICAgICAgdXJsOiBNM1U4UGFyc2VyLnJlc29sdmUodXJpLCBiYXNldXJsKVxuICAgICAgICB9O1xuICAgICAgICBjb25zdCByZXNvbHV0aW9uID0gYXR0cnMuZGVjaW1hbFJlc29sdXRpb24oJ1JFU09MVVRJT04nKTtcbiAgICAgICAgaWYgKHJlc29sdXRpb24pIHtcbiAgICAgICAgICBsZXZlbC53aWR0aCA9IHJlc29sdXRpb24ud2lkdGg7XG4gICAgICAgICAgbGV2ZWwuaGVpZ2h0ID0gcmVzb2x1dGlvbi5oZWlnaHQ7XG4gICAgICAgIH1cbiAgICAgICAgc2V0Q29kZWNzKGF0dHJzLkNPREVDUywgbGV2ZWwpO1xuICAgICAgICBpZiAoISgoX2xldmVsJHVua25vd25Db2RlY3MgPSBsZXZlbC51bmtub3duQ29kZWNzKSAhPSBudWxsICYmIF9sZXZlbCR1bmtub3duQ29kZWNzLmxlbmd0aCkpIHtcbiAgICAgICAgICBsZXZlbHNXaXRoS25vd25Db2RlY3MucHVzaChsZXZlbCk7XG4gICAgICAgIH1cbiAgICAgICAgcGFyc2VkLmxldmVscy5wdXNoKGxldmVsKTtcbiAgICAgIH0gZWxzZSBpZiAocmVzdWx0WzNdKSB7XG4gICAgICAgIGNvbnN0IHRhZyA9IHJlc3VsdFszXTtcbiAgICAgICAgY29uc3QgYXR0cmlidXRlcyA9IHJlc3VsdFs0XTtcbiAgICAgICAgc3dpdGNoICh0YWcpIHtcbiAgICAgICAgICBjYXNlICdTRVNTSU9OLURBVEEnOlxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAvLyAjRVhULVgtU0VTU0lPTi1EQVRBXG4gICAgICAgICAgICAgIGNvbnN0IHNlc3Npb25BdHRycyA9IG5ldyBBdHRyTGlzdChhdHRyaWJ1dGVzKTtcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHN1YnN0aXR1dGVWYXJpYWJsZXNJbkF0dHJpYnV0ZXMocGFyc2VkLCBzZXNzaW9uQXR0cnMsIFsnREFUQS1JRCcsICdMQU5HVUFHRScsICdWQUxVRScsICdVUkknXSk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgY29uc3QgZGF0YUlkID0gc2Vzc2lvbkF0dHJzWydEQVRBLUlEJ107XG4gICAgICAgICAgICAgIGlmIChkYXRhSWQpIHtcbiAgICAgICAgICAgICAgICBpZiAocGFyc2VkLnNlc3Npb25EYXRhID09PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICBwYXJzZWQuc2Vzc2lvbkRhdGEgPSB7fTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcGFyc2VkLnNlc3Npb25EYXRhW2RhdGFJZF0gPSBzZXNzaW9uQXR0cnM7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgY2FzZSAnU0VTU0lPTi1LRVknOlxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAvLyAjRVhULVgtU0VTU0lPTi1LRVlcbiAgICAgICAgICAgICAgY29uc3Qgc2Vzc2lvbktleSA9IHBhcnNlS2V5KGF0dHJpYnV0ZXMsIGJhc2V1cmwsIHBhcnNlZCk7XG4gICAgICAgICAgICAgIGlmIChzZXNzaW9uS2V5LmVuY3J5cHRlZCAmJiBzZXNzaW9uS2V5LmlzU3VwcG9ydGVkKCkpIHtcbiAgICAgICAgICAgICAgICBpZiAocGFyc2VkLnNlc3Npb25LZXlzID09PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgICBwYXJzZWQuc2Vzc2lvbktleXMgPSBbXTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcGFyc2VkLnNlc3Npb25LZXlzLnB1c2goc2Vzc2lvbktleSk7XG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgbG9nZ2VyLndhcm4oYFtLZXlzXSBJZ25vcmluZyBpbnZhbGlkIEVYVC1YLVNFU1NJT04tS0VZIHRhZzogXCIke2F0dHJpYnV0ZXN9XCJgKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICBjYXNlICdERUZJTkUnOlxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAvLyAjRVhULVgtREVGSU5FXG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBjb25zdCB2YXJpYWJsZUF0dHJpYnV0ZXMgPSBuZXcgQXR0ckxpc3QoYXR0cmlidXRlcyk7XG4gICAgICAgICAgICAgICAgc3Vic3RpdHV0ZVZhcmlhYmxlc0luQXR0cmlidXRlcyhwYXJzZWQsIHZhcmlhYmxlQXR0cmlidXRlcywgWydOQU1FJywgJ1ZBTFVFJywgJ1FVRVJZUEFSQU0nXSk7XG4gICAgICAgICAgICAgICAgYWRkVmFyaWFibGVEZWZpbml0aW9uKHBhcnNlZCwgdmFyaWFibGVBdHRyaWJ1dGVzLCBiYXNldXJsKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICBjYXNlICdDT05URU5ULVNURUVSSU5HJzpcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgLy8gI0VYVC1YLUNPTlRFTlQtU1RFRVJJTkdcbiAgICAgICAgICAgICAgY29uc3QgY29udGVudFN0ZWVyaW5nQXR0cmlidXRlcyA9IG5ldyBBdHRyTGlzdChhdHRyaWJ1dGVzKTtcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHN1YnN0aXR1dGVWYXJpYWJsZXNJbkF0dHJpYnV0ZXMocGFyc2VkLCBjb250ZW50U3RlZXJpbmdBdHRyaWJ1dGVzLCBbJ1NFUlZFUi1VUkknLCAnUEFUSFdBWS1JRCddKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBwYXJzZWQuY29udGVudFN0ZWVyaW5nID0ge1xuICAgICAgICAgICAgICAgIHVyaTogTTNVOFBhcnNlci5yZXNvbHZlKGNvbnRlbnRTdGVlcmluZ0F0dHJpYnV0ZXNbJ1NFUlZFUi1VUkknXSwgYmFzZXVybCksXG4gICAgICAgICAgICAgICAgcGF0aHdheUlkOiBjb250ZW50U3RlZXJpbmdBdHRyaWJ1dGVzWydQQVRIV0FZLUlEJ10gfHwgJy4nXG4gICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIGNhc2UgJ1NUQVJUJzpcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgLy8gI0VYVC1YLVNUQVJUXG4gICAgICAgICAgICAgIHBhcnNlZC5zdGFydFRpbWVPZmZzZXQgPSBwYXJzZVN0YXJ0VGltZU9mZnNldChhdHRyaWJ1dGVzKTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgLy8gRmlsdGVyIG91dCBsZXZlbHMgd2l0aCB1bmtub3duIGNvZGVjcyBpZiBpdCBkb2VzIG5vdCByZW1vdmUgYWxsIGxldmVsc1xuICAgIGNvbnN0IHN0cmlwVW5rbm93bkNvZGVjTGV2ZWxzID0gbGV2ZWxzV2l0aEtub3duQ29kZWNzLmxlbmd0aCA+IDAgJiYgbGV2ZWxzV2l0aEtub3duQ29kZWNzLmxlbmd0aCA8IHBhcnNlZC5sZXZlbHMubGVuZ3RoO1xuICAgIHBhcnNlZC5sZXZlbHMgPSBzdHJpcFVua25vd25Db2RlY0xldmVscyA/IGxldmVsc1dpdGhLbm93bkNvZGVjcyA6IHBhcnNlZC5sZXZlbHM7XG4gICAgaWYgKHBhcnNlZC5sZXZlbHMubGVuZ3RoID09PSAwKSB7XG4gICAgICBwYXJzZWQucGxheWxpc3RQYXJzaW5nRXJyb3IgPSBuZXcgRXJyb3IoJ25vIGxldmVscyBmb3VuZCBpbiBtYW5pZmVzdCcpO1xuICAgIH1cbiAgICByZXR1cm4gcGFyc2VkO1xuICB9XG4gIHN0YXRpYyBwYXJzZU1hc3RlclBsYXlsaXN0TWVkaWEoc3RyaW5nLCBiYXNldXJsLCBwYXJzZWQpIHtcbiAgICBsZXQgcmVzdWx0O1xuICAgIGNvbnN0IHJlc3VsdHMgPSB7fTtcbiAgICBjb25zdCBsZXZlbHMgPSBwYXJzZWQubGV2ZWxzO1xuICAgIGNvbnN0IGdyb3Vwc0J5VHlwZSA9IHtcbiAgICAgIEFVRElPOiBsZXZlbHMubWFwKGxldmVsID0+ICh7XG4gICAgICAgIGlkOiBsZXZlbC5hdHRycy5BVURJTyxcbiAgICAgICAgYXVkaW9Db2RlYzogbGV2ZWwuYXVkaW9Db2RlY1xuICAgICAgfSkpLFxuICAgICAgU1VCVElUTEVTOiBsZXZlbHMubWFwKGxldmVsID0+ICh7XG4gICAgICAgIGlkOiBsZXZlbC5hdHRycy5TVUJUSVRMRVMsXG4gICAgICAgIHRleHRDb2RlYzogbGV2ZWwudGV4dENvZGVjXG4gICAgICB9KSksXG4gICAgICAnQ0xPU0VELUNBUFRJT05TJzogW11cbiAgICB9O1xuICAgIGxldCBpZCA9IDA7XG4gICAgTUFTVEVSX1BMQVlMSVNUX01FRElBX1JFR0VYLmxhc3RJbmRleCA9IDA7XG4gICAgd2hpbGUgKChyZXN1bHQgPSBNQVNURVJfUExBWUxJU1RfTUVESUFfUkVHRVguZXhlYyhzdHJpbmcpKSAhPT0gbnVsbCkge1xuICAgICAgY29uc3QgYXR0cnMgPSBuZXcgQXR0ckxpc3QocmVzdWx0WzFdKTtcbiAgICAgIGNvbnN0IHR5cGUgPSBhdHRycy5UWVBFO1xuICAgICAgaWYgKHR5cGUpIHtcbiAgICAgICAgY29uc3QgZ3JvdXBzID0gZ3JvdXBzQnlUeXBlW3R5cGVdO1xuICAgICAgICBjb25zdCBtZWRpYXMgPSByZXN1bHRzW3R5cGVdIHx8IFtdO1xuICAgICAgICByZXN1bHRzW3R5cGVdID0gbWVkaWFzO1xuICAgICAgICB7XG4gICAgICAgICAgc3Vic3RpdHV0ZVZhcmlhYmxlc0luQXR0cmlidXRlcyhwYXJzZWQsIGF0dHJzLCBbJ1VSSScsICdHUk9VUC1JRCcsICdMQU5HVUFHRScsICdBU1NPQy1MQU5HVUFHRScsICdTVEFCTEUtUkVORElUSU9OLUlEJywgJ05BTUUnLCAnSU5TVFJFQU0tSUQnLCAnQ0hBUkFDVEVSSVNUSUNTJywgJ0NIQU5ORUxTJ10pO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGxhbmcgPSBhdHRycy5MQU5HVUFHRTtcbiAgICAgICAgY29uc3QgYXNzb2NMYW5nID0gYXR0cnNbJ0FTU09DLUxBTkdVQUdFJ107XG4gICAgICAgIGNvbnN0IGNoYW5uZWxzID0gYXR0cnMuQ0hBTk5FTFM7XG4gICAgICAgIGNvbnN0IGNoYXJhY3RlcmlzdGljcyA9IGF0dHJzLkNIQVJBQ1RFUklTVElDUztcbiAgICAgICAgY29uc3QgaW5zdHJlYW1JZCA9IGF0dHJzWydJTlNUUkVBTS1JRCddO1xuICAgICAgICBjb25zdCBtZWRpYSA9IHtcbiAgICAgICAgICBhdHRycyxcbiAgICAgICAgICBiaXRyYXRlOiAwLFxuICAgICAgICAgIGlkOiBpZCsrLFxuICAgICAgICAgIGdyb3VwSWQ6IGF0dHJzWydHUk9VUC1JRCddIHx8ICcnLFxuICAgICAgICAgIG5hbWU6IGF0dHJzLk5BTUUgfHwgbGFuZyB8fCAnJyxcbiAgICAgICAgICB0eXBlLFxuICAgICAgICAgIGRlZmF1bHQ6IGF0dHJzLmJvb2woJ0RFRkFVTFQnKSxcbiAgICAgICAgICBhdXRvc2VsZWN0OiBhdHRycy5ib29sKCdBVVRPU0VMRUNUJyksXG4gICAgICAgICAgZm9yY2VkOiBhdHRycy5ib29sKCdGT1JDRUQnKSxcbiAgICAgICAgICBsYW5nLFxuICAgICAgICAgIHVybDogYXR0cnMuVVJJID8gTTNVOFBhcnNlci5yZXNvbHZlKGF0dHJzLlVSSSwgYmFzZXVybCkgOiAnJ1xuICAgICAgICB9O1xuICAgICAgICBpZiAoYXNzb2NMYW5nKSB7XG4gICAgICAgICAgbWVkaWEuYXNzb2NMYW5nID0gYXNzb2NMYW5nO1xuICAgICAgICB9XG4gICAgICAgIGlmIChjaGFubmVscykge1xuICAgICAgICAgIG1lZGlhLmNoYW5uZWxzID0gY2hhbm5lbHM7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNoYXJhY3RlcmlzdGljcykge1xuICAgICAgICAgIG1lZGlhLmNoYXJhY3RlcmlzdGljcyA9IGNoYXJhY3RlcmlzdGljcztcbiAgICAgICAgfVxuICAgICAgICBpZiAoaW5zdHJlYW1JZCkge1xuICAgICAgICAgIG1lZGlhLmluc3RyZWFtSWQgPSBpbnN0cmVhbUlkO1xuICAgICAgICB9XG4gICAgICAgIGlmIChncm91cHMgIT0gbnVsbCAmJiBncm91cHMubGVuZ3RoKSB7XG4gICAgICAgICAgLy8gSWYgdGhlcmUgYXJlIGF1ZGlvIG9yIHRleHQgZ3JvdXBzIHNpZ25hbGxlZCBpbiB0aGUgbWFuaWZlc3QsIGxldCdzIGxvb2sgZm9yIGEgbWF0Y2hpbmcgY29kZWMgc3RyaW5nIGZvciB0aGlzIHRyYWNrXG4gICAgICAgICAgLy8gSWYgd2UgZG9uJ3QgZmluZCB0aGUgdHJhY2sgc2lnbmFsbGVkLCBsZXRzIHVzZSB0aGUgZmlyc3QgYXVkaW8gZ3JvdXBzIGNvZGVjIHdlIGhhdmVcbiAgICAgICAgICAvLyBBY3RpbmcgYXMgYSBiZXN0IGd1ZXNzXG4gICAgICAgICAgY29uc3QgZ3JvdXBDb2RlYyA9IE0zVThQYXJzZXIuZmluZEdyb3VwKGdyb3VwcywgbWVkaWEuZ3JvdXBJZCkgfHwgZ3JvdXBzWzBdO1xuICAgICAgICAgIGFzc2lnbkNvZGVjKG1lZGlhLCBncm91cENvZGVjLCAnYXVkaW9Db2RlYycpO1xuICAgICAgICAgIGFzc2lnbkNvZGVjKG1lZGlhLCBncm91cENvZGVjLCAndGV4dENvZGVjJyk7XG4gICAgICAgIH1cbiAgICAgICAgbWVkaWFzLnB1c2gobWVkaWEpO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0cztcbiAgfVxuICBzdGF0aWMgcGFyc2VMZXZlbFBsYXlsaXN0KHN0cmluZywgYmFzZXVybCwgaWQsIHR5cGUsIGxldmVsVXJsSWQsIG11bHRpdmFyaWFudFZhcmlhYmxlTGlzdCkge1xuICAgIGNvbnN0IGxldmVsID0gbmV3IExldmVsRGV0YWlscyhiYXNldXJsKTtcbiAgICBjb25zdCBmcmFnbWVudHMgPSBsZXZlbC5mcmFnbWVudHM7XG4gICAgLy8gVGhlIG1vc3QgcmVjZW50IGluaXQgc2VnbWVudCBzZWVuIChhcHBsaWVzIHRvIGFsbCBzdWJzZXF1ZW50IHNlZ21lbnRzKVxuICAgIGxldCBjdXJyZW50SW5pdFNlZ21lbnQgPSBudWxsO1xuICAgIGxldCBjdXJyZW50U04gPSAwO1xuICAgIGxldCBjdXJyZW50UGFydCA9IDA7XG4gICAgbGV0IHRvdGFsZHVyYXRpb24gPSAwO1xuICAgIGxldCBkaXNjb250aW51aXR5Q291bnRlciA9IDA7XG4gICAgbGV0IHByZXZGcmFnID0gbnVsbDtcbiAgICBsZXQgZnJhZyA9IG5ldyBGcmFnbWVudCh0eXBlLCBiYXNldXJsKTtcbiAgICBsZXQgcmVzdWx0O1xuICAgIGxldCBpO1xuICAgIGxldCBsZXZlbGtleXM7XG4gICAgbGV0IGZpcnN0UGR0SW5kZXggPSAtMTtcbiAgICBsZXQgY3JlYXRlTmV4dEZyYWcgPSBmYWxzZTtcbiAgICBsZXQgbmV4dEJ5dGVSYW5nZSA9IG51bGw7XG4gICAgTEVWRUxfUExBWUxJU1RfUkVHRVhfRkFTVC5sYXN0SW5kZXggPSAwO1xuICAgIGxldmVsLm0zdTggPSBzdHJpbmc7XG4gICAgbGV2ZWwuaGFzVmFyaWFibGVSZWZzID0gaGFzVmFyaWFibGVSZWZlcmVuY2VzKHN0cmluZykgO1xuICAgIHdoaWxlICgocmVzdWx0ID0gTEVWRUxfUExBWUxJU1RfUkVHRVhfRkFTVC5leGVjKHN0cmluZykpICE9PSBudWxsKSB7XG4gICAgICBpZiAoY3JlYXRlTmV4dEZyYWcpIHtcbiAgICAgICAgY3JlYXRlTmV4dEZyYWcgPSBmYWxzZTtcbiAgICAgICAgZnJhZyA9IG5ldyBGcmFnbWVudCh0eXBlLCBiYXNldXJsKTtcbiAgICAgICAgLy8gc2V0dXAgdGhlIG5leHQgZnJhZ21lbnQgZm9yIHBhcnQgbG9hZGluZ1xuICAgICAgICBmcmFnLnN0YXJ0ID0gdG90YWxkdXJhdGlvbjtcbiAgICAgICAgZnJhZy5zbiA9IGN1cnJlbnRTTjtcbiAgICAgICAgZnJhZy5jYyA9IGRpc2NvbnRpbnVpdHlDb3VudGVyO1xuICAgICAgICBmcmFnLmxldmVsID0gaWQ7XG4gICAgICAgIGlmIChjdXJyZW50SW5pdFNlZ21lbnQpIHtcbiAgICAgICAgICBmcmFnLmluaXRTZWdtZW50ID0gY3VycmVudEluaXRTZWdtZW50O1xuICAgICAgICAgIGZyYWcucmF3UHJvZ3JhbURhdGVUaW1lID0gY3VycmVudEluaXRTZWdtZW50LnJhd1Byb2dyYW1EYXRlVGltZTtcbiAgICAgICAgICBjdXJyZW50SW5pdFNlZ21lbnQucmF3UHJvZ3JhbURhdGVUaW1lID0gbnVsbDtcbiAgICAgICAgICBpZiAobmV4dEJ5dGVSYW5nZSkge1xuICAgICAgICAgICAgZnJhZy5zZXRCeXRlUmFuZ2UobmV4dEJ5dGVSYW5nZSk7XG4gICAgICAgICAgICBuZXh0Qnl0ZVJhbmdlID0gbnVsbDtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGNvbnN0IGR1cmF0aW9uID0gcmVzdWx0WzFdO1xuICAgICAgaWYgKGR1cmF0aW9uKSB7XG4gICAgICAgIC8vIElORlxuICAgICAgICBmcmFnLmR1cmF0aW9uID0gcGFyc2VGbG9hdChkdXJhdGlvbik7XG4gICAgICAgIC8vIGF2b2lkIHNsaWNlZCBzdHJpbmdzICAgIGh0dHBzOi8vZ2l0aHViLmNvbS92aWRlby1kZXYvaGxzLmpzL2lzc3Vlcy85MzlcbiAgICAgICAgY29uc3QgdGl0bGUgPSAoJyAnICsgcmVzdWx0WzJdKS5zbGljZSgxKTtcbiAgICAgICAgZnJhZy50aXRsZSA9IHRpdGxlIHx8IG51bGw7XG4gICAgICAgIGZyYWcudGFnTGlzdC5wdXNoKHRpdGxlID8gWydJTkYnLCBkdXJhdGlvbiwgdGl0bGVdIDogWydJTkYnLCBkdXJhdGlvbl0pO1xuICAgICAgfSBlbHNlIGlmIChyZXN1bHRbM10pIHtcbiAgICAgICAgLy8gdXJsXG4gICAgICAgIGlmIChpc0Zpbml0ZU51bWJlcihmcmFnLmR1cmF0aW9uKSkge1xuICAgICAgICAgIGZyYWcuc3RhcnQgPSB0b3RhbGR1cmF0aW9uO1xuICAgICAgICAgIGlmIChsZXZlbGtleXMpIHtcbiAgICAgICAgICAgIHNldEZyYWdMZXZlbEtleXMoZnJhZywgbGV2ZWxrZXlzLCBsZXZlbCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGZyYWcuc24gPSBjdXJyZW50U047XG4gICAgICAgICAgZnJhZy5sZXZlbCA9IGlkO1xuICAgICAgICAgIGZyYWcuY2MgPSBkaXNjb250aW51aXR5Q291bnRlcjtcbiAgICAgICAgICBmcmFnbWVudHMucHVzaChmcmFnKTtcbiAgICAgICAgICAvLyBhdm9pZCBzbGljZWQgc3RyaW5ncyAgICBodHRwczovL2dpdGh1Yi5jb20vdmlkZW8tZGV2L2hscy5qcy9pc3N1ZXMvOTM5XG4gICAgICAgICAgY29uc3QgdXJpID0gKCcgJyArIHJlc3VsdFszXSkuc2xpY2UoMSk7XG4gICAgICAgICAgZnJhZy5yZWx1cmwgPSBzdWJzdGl0dXRlVmFyaWFibGVzKGxldmVsLCB1cmkpIDtcbiAgICAgICAgICBhc3NpZ25Qcm9ncmFtRGF0ZVRpbWUoZnJhZywgcHJldkZyYWcpO1xuICAgICAgICAgIHByZXZGcmFnID0gZnJhZztcbiAgICAgICAgICB0b3RhbGR1cmF0aW9uICs9IGZyYWcuZHVyYXRpb247XG4gICAgICAgICAgY3VycmVudFNOKys7XG4gICAgICAgICAgY3VycmVudFBhcnQgPSAwO1xuICAgICAgICAgIGNyZWF0ZU5leHRGcmFnID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChyZXN1bHRbNF0pIHtcbiAgICAgICAgLy8gWC1CWVRFUkFOR0VcbiAgICAgICAgY29uc3QgZGF0YSA9ICgnICcgKyByZXN1bHRbNF0pLnNsaWNlKDEpO1xuICAgICAgICBpZiAocHJldkZyYWcpIHtcbiAgICAgICAgICBmcmFnLnNldEJ5dGVSYW5nZShkYXRhLCBwcmV2RnJhZyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZnJhZy5zZXRCeXRlUmFuZ2UoZGF0YSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAocmVzdWx0WzVdKSB7XG4gICAgICAgIC8vIFBST0dSQU0tREFURS1USU1FXG4gICAgICAgIC8vIGF2b2lkIHNsaWNlZCBzdHJpbmdzICAgIGh0dHBzOi8vZ2l0aHViLmNvbS92aWRlby1kZXYvaGxzLmpzL2lzc3Vlcy85MzlcbiAgICAgICAgZnJhZy5yYXdQcm9ncmFtRGF0ZVRpbWUgPSAoJyAnICsgcmVzdWx0WzVdKS5zbGljZSgxKTtcbiAgICAgICAgZnJhZy50YWdMaXN0LnB1c2goWydQUk9HUkFNLURBVEUtVElNRScsIGZyYWcucmF3UHJvZ3JhbURhdGVUaW1lXSk7XG4gICAgICAgIGlmIChmaXJzdFBkdEluZGV4ID09PSAtMSkge1xuICAgICAgICAgIGZpcnN0UGR0SW5kZXggPSBmcmFnbWVudHMubGVuZ3RoO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXN1bHQgPSByZXN1bHRbMF0ubWF0Y2goTEVWRUxfUExBWUxJU1RfUkVHRVhfU0xPVyk7XG4gICAgICAgIGlmICghcmVzdWx0KSB7XG4gICAgICAgICAgbG9nZ2VyLndhcm4oJ05vIG1hdGNoZXMgb24gc2xvdyByZWdleCBtYXRjaCBmb3IgbGV2ZWwgcGxheWxpc3QhJyk7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChpID0gMTsgaSA8IHJlc3VsdC5sZW5ndGg7IGkrKykge1xuICAgICAgICAgIGlmICh0eXBlb2YgcmVzdWx0W2ldICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gYXZvaWQgc2xpY2VkIHN0cmluZ3MgICAgaHR0cHM6Ly9naXRodWIuY29tL3ZpZGVvLWRldi9obHMuanMvaXNzdWVzLzkzOVxuICAgICAgICBjb25zdCB0YWcgPSAoJyAnICsgcmVzdWx0W2ldKS5zbGljZSgxKTtcbiAgICAgICAgY29uc3QgdmFsdWUxID0gKCcgJyArIHJlc3VsdFtpICsgMV0pLnNsaWNlKDEpO1xuICAgICAgICBjb25zdCB2YWx1ZTIgPSByZXN1bHRbaSArIDJdID8gKCcgJyArIHJlc3VsdFtpICsgMl0pLnNsaWNlKDEpIDogJyc7XG4gICAgICAgIHN3aXRjaCAodGFnKSB7XG4gICAgICAgICAgY2FzZSAnUExBWUxJU1QtVFlQRSc6XG4gICAgICAgICAgICBsZXZlbC50eXBlID0gdmFsdWUxLnRvVXBwZXJDYXNlKCk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlICdNRURJQS1TRVFVRU5DRSc6XG4gICAgICAgICAgICBjdXJyZW50U04gPSBsZXZlbC5zdGFydFNOID0gcGFyc2VJbnQodmFsdWUxKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgJ1NLSVAnOlxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBjb25zdCBza2lwQXR0cnMgPSBuZXcgQXR0ckxpc3QodmFsdWUxKTtcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHN1YnN0aXR1dGVWYXJpYWJsZXNJbkF0dHJpYnV0ZXMobGV2ZWwsIHNraXBBdHRycywgWydSRUNFTlRMWS1SRU1PVkVELURBVEVSQU5HRVMnXSk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgY29uc3Qgc2tpcHBlZFNlZ21lbnRzID0gc2tpcEF0dHJzLmRlY2ltYWxJbnRlZ2VyKCdTS0lQUEVELVNFR01FTlRTJyk7XG4gICAgICAgICAgICAgIGlmIChpc0Zpbml0ZU51bWJlcihza2lwcGVkU2VnbWVudHMpKSB7XG4gICAgICAgICAgICAgICAgbGV2ZWwuc2tpcHBlZFNlZ21lbnRzID0gc2tpcHBlZFNlZ21lbnRzO1xuICAgICAgICAgICAgICAgIC8vIFRoaXMgd2lsbCByZXN1bHQgaW4gZnJhZ21lbnRzW10gY29udGFpbmluZyB1bmRlZmluZWQgdmFsdWVzLCB3aGljaCB3ZSB3aWxsIGZpbGwgaW4gd2l0aCBgbWVyZ2VEZXRhaWxzYFxuICAgICAgICAgICAgICAgIGZvciAobGV0IF9pID0gc2tpcHBlZFNlZ21lbnRzOyBfaS0tOykge1xuICAgICAgICAgICAgICAgICAgZnJhZ21lbnRzLnVuc2hpZnQobnVsbCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGN1cnJlbnRTTiArPSBza2lwcGVkU2VnbWVudHM7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgY29uc3QgcmVjZW50bHlSZW1vdmVkRGF0ZXJhbmdlcyA9IHNraXBBdHRycy5lbnVtZXJhdGVkU3RyaW5nKCdSRUNFTlRMWS1SRU1PVkVELURBVEVSQU5HRVMnKTtcbiAgICAgICAgICAgICAgaWYgKHJlY2VudGx5UmVtb3ZlZERhdGVyYW5nZXMpIHtcbiAgICAgICAgICAgICAgICBsZXZlbC5yZWNlbnRseVJlbW92ZWREYXRlcmFuZ2VzID0gcmVjZW50bHlSZW1vdmVkRGF0ZXJhbmdlcy5zcGxpdCgnXFx0Jyk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgY2FzZSAnVEFSR0VURFVSQVRJT04nOlxuICAgICAgICAgICAgbGV2ZWwudGFyZ2V0ZHVyYXRpb24gPSBNYXRoLm1heChwYXJzZUludCh2YWx1ZTEpLCAxKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgJ1ZFUlNJT04nOlxuICAgICAgICAgICAgbGV2ZWwudmVyc2lvbiA9IHBhcnNlSW50KHZhbHVlMSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlICdJTkRFUEVOREVOVC1TRUdNRU5UUyc6XG4gICAgICAgICAgY2FzZSAnRVhUTTNVJzpcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgJ0VORExJU1QnOlxuICAgICAgICAgICAgbGV2ZWwubGl2ZSA9IGZhbHNlO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSAnIyc6XG4gICAgICAgICAgICBpZiAodmFsdWUxIHx8IHZhbHVlMikge1xuICAgICAgICAgICAgICBmcmFnLnRhZ0xpc3QucHVzaCh2YWx1ZTIgPyBbdmFsdWUxLCB2YWx1ZTJdIDogW3ZhbHVlMV0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSAnRElTQ09OVElOVUlUWSc6XG4gICAgICAgICAgICBkaXNjb250aW51aXR5Q291bnRlcisrO1xuICAgICAgICAgICAgZnJhZy50YWdMaXN0LnB1c2goWydESVMnXSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlICdHQVAnOlxuICAgICAgICAgICAgZnJhZy5nYXAgPSB0cnVlO1xuICAgICAgICAgICAgZnJhZy50YWdMaXN0LnB1c2goW3RhZ10pO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSAnQklUUkFURSc6XG4gICAgICAgICAgICBmcmFnLnRhZ0xpc3QucHVzaChbdGFnLCB2YWx1ZTFdKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgJ0RBVEVSQU5HRSc6XG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGNvbnN0IGRhdGVSYW5nZUF0dHIgPSBuZXcgQXR0ckxpc3QodmFsdWUxKTtcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHN1YnN0aXR1dGVWYXJpYWJsZXNJbkF0dHJpYnV0ZXMobGV2ZWwsIGRhdGVSYW5nZUF0dHIsIFsnSUQnLCAnQ0xBU1MnLCAnU1RBUlQtREFURScsICdFTkQtREFURScsICdTQ1RFMzUtQ01EJywgJ1NDVEUzNS1PVVQnLCAnU0NURTM1LUlOJ10pO1xuICAgICAgICAgICAgICAgIHN1YnN0aXR1dGVWYXJpYWJsZXNJbkF0dHJpYnV0ZXMobGV2ZWwsIGRhdGVSYW5nZUF0dHIsIGRhdGVSYW5nZUF0dHIuY2xpZW50QXR0cnMpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGNvbnN0IGRhdGVSYW5nZSA9IG5ldyBEYXRlUmFuZ2UoZGF0ZVJhbmdlQXR0ciwgbGV2ZWwuZGF0ZVJhbmdlc1tkYXRlUmFuZ2VBdHRyLklEXSk7XG4gICAgICAgICAgICAgIGlmIChkYXRlUmFuZ2UuaXNWYWxpZCB8fCBsZXZlbC5za2lwcGVkU2VnbWVudHMpIHtcbiAgICAgICAgICAgICAgICBsZXZlbC5kYXRlUmFuZ2VzW2RhdGVSYW5nZS5pZF0gPSBkYXRlUmFuZ2U7XG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgbG9nZ2VyLndhcm4oYElnbm9yaW5nIGludmFsaWQgREFURVJBTkdFIHRhZzogXCIke3ZhbHVlMX1cImApO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIC8vIEFkZCB0byBmcmFnbWVudCB0YWcgbGlzdCBmb3IgYmFja3dhcmRzIGNvbXBhdGliaWxpdHkgKDwgdjEuMi4wKVxuICAgICAgICAgICAgICBmcmFnLnRhZ0xpc3QucHVzaChbJ0VYVC1YLURBVEVSQU5HRScsIHZhbHVlMV0pO1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICBjYXNlICdERUZJTkUnOlxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgY29uc3QgdmFyaWFibGVBdHRyaWJ1dGVzID0gbmV3IEF0dHJMaXN0KHZhbHVlMSk7XG4gICAgICAgICAgICAgICAgc3Vic3RpdHV0ZVZhcmlhYmxlc0luQXR0cmlidXRlcyhsZXZlbCwgdmFyaWFibGVBdHRyaWJ1dGVzLCBbJ05BTUUnLCAnVkFMVUUnLCAnSU1QT1JUJywgJ1FVRVJZUEFSQU0nXSk7XG4gICAgICAgICAgICAgICAgaWYgKCdJTVBPUlQnIGluIHZhcmlhYmxlQXR0cmlidXRlcykge1xuICAgICAgICAgICAgICAgICAgaW1wb3J0VmFyaWFibGVEZWZpbml0aW9uKGxldmVsLCB2YXJpYWJsZUF0dHJpYnV0ZXMsIG11bHRpdmFyaWFudFZhcmlhYmxlTGlzdCk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgIGFkZFZhcmlhYmxlRGVmaW5pdGlvbihsZXZlbCwgdmFyaWFibGVBdHRyaWJ1dGVzLCBiYXNldXJsKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgY2FzZSAnRElTQ09OVElOVUlUWS1TRVFVRU5DRSc6XG4gICAgICAgICAgICBkaXNjb250aW51aXR5Q291bnRlciA9IHBhcnNlSW50KHZhbHVlMSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlICdLRVknOlxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBjb25zdCBsZXZlbEtleSA9IHBhcnNlS2V5KHZhbHVlMSwgYmFzZXVybCwgbGV2ZWwpO1xuICAgICAgICAgICAgICBpZiAobGV2ZWxLZXkuaXNTdXBwb3J0ZWQoKSkge1xuICAgICAgICAgICAgICAgIGlmIChsZXZlbEtleS5tZXRob2QgPT09ICdOT05FJykge1xuICAgICAgICAgICAgICAgICAgbGV2ZWxrZXlzID0gdW5kZWZpbmVkO1xuICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmICghbGV2ZWxrZXlzKSB7XG4gICAgICAgICAgICAgICAgICBsZXZlbGtleXMgPSB7fTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKGxldmVsa2V5c1tsZXZlbEtleS5rZXlGb3JtYXRdKSB7XG4gICAgICAgICAgICAgICAgICBsZXZlbGtleXMgPSBfZXh0ZW5kcyh7fSwgbGV2ZWxrZXlzKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgbGV2ZWxrZXlzW2xldmVsS2V5LmtleUZvcm1hdF0gPSBsZXZlbEtleTtcbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBsb2dnZXIud2FybihgW0tleXNdIElnbm9yaW5nIGludmFsaWQgRVhULVgtS0VZIHRhZzogXCIke3ZhbHVlMX1cImApO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIGNhc2UgJ1NUQVJUJzpcbiAgICAgICAgICAgIGxldmVsLnN0YXJ0VGltZU9mZnNldCA9IHBhcnNlU3RhcnRUaW1lT2Zmc2V0KHZhbHVlMSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlICdNQVAnOlxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBjb25zdCBtYXBBdHRycyA9IG5ldyBBdHRyTGlzdCh2YWx1ZTEpO1xuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgc3Vic3RpdHV0ZVZhcmlhYmxlc0luQXR0cmlidXRlcyhsZXZlbCwgbWFwQXR0cnMsIFsnQllURVJBTkdFJywgJ1VSSSddKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBpZiAoZnJhZy5kdXJhdGlvbikge1xuICAgICAgICAgICAgICAgIC8vIEluaXRpYWwgc2VnbWVudCB0YWcgaXMgYWZ0ZXIgc2VnbWVudCBkdXJhdGlvbiB0YWcuXG4gICAgICAgICAgICAgICAgLy8gICAjRVhUSU5GOiA2LjBcbiAgICAgICAgICAgICAgICAvLyAgICNFWFQtWC1NQVA6VVJJPVwiaW5pdC5tcDRcbiAgICAgICAgICAgICAgICBjb25zdCBpbml0ID0gbmV3IEZyYWdtZW50KHR5cGUsIGJhc2V1cmwpO1xuICAgICAgICAgICAgICAgIHNldEluaXRTZWdtZW50KGluaXQsIG1hcEF0dHJzLCBpZCwgbGV2ZWxrZXlzKTtcbiAgICAgICAgICAgICAgICBjdXJyZW50SW5pdFNlZ21lbnQgPSBpbml0O1xuICAgICAgICAgICAgICAgIGZyYWcuaW5pdFNlZ21lbnQgPSBjdXJyZW50SW5pdFNlZ21lbnQ7XG4gICAgICAgICAgICAgICAgaWYgKGN1cnJlbnRJbml0U2VnbWVudC5yYXdQcm9ncmFtRGF0ZVRpbWUgJiYgIWZyYWcucmF3UHJvZ3JhbURhdGVUaW1lKSB7XG4gICAgICAgICAgICAgICAgICBmcmFnLnJhd1Byb2dyYW1EYXRlVGltZSA9IGN1cnJlbnRJbml0U2VnbWVudC5yYXdQcm9ncmFtRGF0ZVRpbWU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIC8vIEluaXRpYWwgc2VnbWVudCB0YWcgaXMgYmVmb3JlIHNlZ21lbnQgZHVyYXRpb24gdGFnXG4gICAgICAgICAgICAgICAgLy8gSGFuZGxlIGNhc2Ugd2hlcmUgRVhULVgtTUFQIGlzIGRlY2xhcmVkIGFmdGVyIEVYVC1YLUJZVEVSQU5HRVxuICAgICAgICAgICAgICAgIGNvbnN0IGVuZCA9IGZyYWcuYnl0ZVJhbmdlRW5kT2Zmc2V0O1xuICAgICAgICAgICAgICAgIGlmIChlbmQpIHtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IHN0YXJ0ID0gZnJhZy5ieXRlUmFuZ2VTdGFydE9mZnNldDtcbiAgICAgICAgICAgICAgICAgIG5leHRCeXRlUmFuZ2UgPSBgJHtlbmQgLSBzdGFydH1AJHtzdGFydH1gO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICBuZXh0Qnl0ZVJhbmdlID0gbnVsbDtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgc2V0SW5pdFNlZ21lbnQoZnJhZywgbWFwQXR0cnMsIGlkLCBsZXZlbGtleXMpO1xuICAgICAgICAgICAgICAgIGN1cnJlbnRJbml0U2VnbWVudCA9IGZyYWc7XG4gICAgICAgICAgICAgICAgY3JlYXRlTmV4dEZyYWcgPSB0cnVlO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIGNhc2UgJ1NFUlZFUi1DT05UUk9MJzpcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgY29uc3Qgc2VydmVyQ29udHJvbEF0dHJzID0gbmV3IEF0dHJMaXN0KHZhbHVlMSk7XG4gICAgICAgICAgICAgIGxldmVsLmNhbkJsb2NrUmVsb2FkID0gc2VydmVyQ29udHJvbEF0dHJzLmJvb2woJ0NBTi1CTE9DSy1SRUxPQUQnKTtcbiAgICAgICAgICAgICAgbGV2ZWwuY2FuU2tpcFVudGlsID0gc2VydmVyQ29udHJvbEF0dHJzLm9wdGlvbmFsRmxvYXQoJ0NBTi1TS0lQLVVOVElMJywgMCk7XG4gICAgICAgICAgICAgIGxldmVsLmNhblNraXBEYXRlUmFuZ2VzID0gbGV2ZWwuY2FuU2tpcFVudGlsID4gMCAmJiBzZXJ2ZXJDb250cm9sQXR0cnMuYm9vbCgnQ0FOLVNLSVAtREFURVJBTkdFUycpO1xuICAgICAgICAgICAgICBsZXZlbC5wYXJ0SG9sZEJhY2sgPSBzZXJ2ZXJDb250cm9sQXR0cnMub3B0aW9uYWxGbG9hdCgnUEFSVC1IT0xELUJBQ0snLCAwKTtcbiAgICAgICAgICAgICAgbGV2ZWwuaG9sZEJhY2sgPSBzZXJ2ZXJDb250cm9sQXR0cnMub3B0aW9uYWxGbG9hdCgnSE9MRC1CQUNLJywgMCk7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIGNhc2UgJ1BBUlQtSU5GJzpcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgY29uc3QgcGFydEluZkF0dHJzID0gbmV3IEF0dHJMaXN0KHZhbHVlMSk7XG4gICAgICAgICAgICAgIGxldmVsLnBhcnRUYXJnZXQgPSBwYXJ0SW5mQXR0cnMuZGVjaW1hbEZsb2F0aW5nUG9pbnQoJ1BBUlQtVEFSR0VUJyk7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIGNhc2UgJ1BBUlQnOlxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBsZXQgcGFydExpc3QgPSBsZXZlbC5wYXJ0TGlzdDtcbiAgICAgICAgICAgICAgaWYgKCFwYXJ0TGlzdCkge1xuICAgICAgICAgICAgICAgIHBhcnRMaXN0ID0gbGV2ZWwucGFydExpc3QgPSBbXTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBjb25zdCBwcmV2aW91c0ZyYWdtZW50UGFydCA9IGN1cnJlbnRQYXJ0ID4gMCA/IHBhcnRMaXN0W3BhcnRMaXN0Lmxlbmd0aCAtIDFdIDogdW5kZWZpbmVkO1xuICAgICAgICAgICAgICBjb25zdCBpbmRleCA9IGN1cnJlbnRQYXJ0Kys7XG4gICAgICAgICAgICAgIGNvbnN0IHBhcnRBdHRycyA9IG5ldyBBdHRyTGlzdCh2YWx1ZTEpO1xuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgc3Vic3RpdHV0ZVZhcmlhYmxlc0luQXR0cmlidXRlcyhsZXZlbCwgcGFydEF0dHJzLCBbJ0JZVEVSQU5HRScsICdVUkknXSk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgY29uc3QgcGFydCA9IG5ldyBQYXJ0KHBhcnRBdHRycywgZnJhZywgYmFzZXVybCwgaW5kZXgsIHByZXZpb3VzRnJhZ21lbnRQYXJ0KTtcbiAgICAgICAgICAgICAgcGFydExpc3QucHVzaChwYXJ0KTtcbiAgICAgICAgICAgICAgZnJhZy5kdXJhdGlvbiArPSBwYXJ0LmR1cmF0aW9uO1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICBjYXNlICdQUkVMT0FELUhJTlQnOlxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBjb25zdCBwcmVsb2FkSGludEF0dHJzID0gbmV3IEF0dHJMaXN0KHZhbHVlMSk7XG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBzdWJzdGl0dXRlVmFyaWFibGVzSW5BdHRyaWJ1dGVzKGxldmVsLCBwcmVsb2FkSGludEF0dHJzLCBbJ1VSSSddKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBsZXZlbC5wcmVsb2FkSGludCA9IHByZWxvYWRIaW50QXR0cnM7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIGNhc2UgJ1JFTkRJVElPTi1SRVBPUlQnOlxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBjb25zdCByZW5kaXRpb25SZXBvcnRBdHRycyA9IG5ldyBBdHRyTGlzdCh2YWx1ZTEpO1xuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgc3Vic3RpdHV0ZVZhcmlhYmxlc0luQXR0cmlidXRlcyhsZXZlbCwgcmVuZGl0aW9uUmVwb3J0QXR0cnMsIFsnVVJJJ10pO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGxldmVsLnJlbmRpdGlvblJlcG9ydHMgPSBsZXZlbC5yZW5kaXRpb25SZXBvcnRzIHx8IFtdO1xuICAgICAgICAgICAgICBsZXZlbC5yZW5kaXRpb25SZXBvcnRzLnB1c2gocmVuZGl0aW9uUmVwb3J0QXR0cnMpO1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgbG9nZ2VyLndhcm4oYGxpbmUgcGFyc2VkIGJ1dCBub3QgaGFuZGxlZDogJHtyZXN1bHR9YCk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICBpZiAocHJldkZyYWcgJiYgIXByZXZGcmFnLnJlbHVybCkge1xuICAgICAgZnJhZ21lbnRzLnBvcCgpO1xuICAgICAgdG90YWxkdXJhdGlvbiAtPSBwcmV2RnJhZy5kdXJhdGlvbjtcbiAgICAgIGlmIChsZXZlbC5wYXJ0TGlzdCkge1xuICAgICAgICBsZXZlbC5mcmFnbWVudEhpbnQgPSBwcmV2RnJhZztcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKGxldmVsLnBhcnRMaXN0KSB7XG4gICAgICBhc3NpZ25Qcm9ncmFtRGF0ZVRpbWUoZnJhZywgcHJldkZyYWcpO1xuICAgICAgZnJhZy5jYyA9IGRpc2NvbnRpbnVpdHlDb3VudGVyO1xuICAgICAgbGV2ZWwuZnJhZ21lbnRIaW50ID0gZnJhZztcbiAgICAgIGlmIChsZXZlbGtleXMpIHtcbiAgICAgICAgc2V0RnJhZ0xldmVsS2V5cyhmcmFnLCBsZXZlbGtleXMsIGxldmVsKTtcbiAgICAgIH1cbiAgICB9XG4gICAgY29uc3QgZnJhZ21lbnRMZW5ndGggPSBmcmFnbWVudHMubGVuZ3RoO1xuICAgIGNvbnN0IGZpcnN0RnJhZ21lbnQgPSBmcmFnbWVudHNbMF07XG4gICAgY29uc3QgbGFzdEZyYWdtZW50ID0gZnJhZ21lbnRzW2ZyYWdtZW50TGVuZ3RoIC0gMV07XG4gICAgdG90YWxkdXJhdGlvbiArPSBsZXZlbC5za2lwcGVkU2VnbWVudHMgKiBsZXZlbC50YXJnZXRkdXJhdGlvbjtcbiAgICBpZiAodG90YWxkdXJhdGlvbiA+IDAgJiYgZnJhZ21lbnRMZW5ndGggJiYgbGFzdEZyYWdtZW50KSB7XG4gICAgICBsZXZlbC5hdmVyYWdldGFyZ2V0ZHVyYXRpb24gPSB0b3RhbGR1cmF0aW9uIC8gZnJhZ21lbnRMZW5ndGg7XG4gICAgICBjb25zdCBsYXN0U24gPSBsYXN0RnJhZ21lbnQuc247XG4gICAgICBsZXZlbC5lbmRTTiA9IGxhc3RTbiAhPT0gJ2luaXRTZWdtZW50JyA/IGxhc3RTbiA6IDA7XG4gICAgICBpZiAoIWxldmVsLmxpdmUpIHtcbiAgICAgICAgbGFzdEZyYWdtZW50LmVuZExpc3QgPSB0cnVlO1xuICAgICAgfVxuICAgICAgaWYgKGZpcnN0RnJhZ21lbnQpIHtcbiAgICAgICAgbGV2ZWwuc3RhcnRDQyA9IGZpcnN0RnJhZ21lbnQuY2M7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGxldmVsLmVuZFNOID0gMDtcbiAgICAgIGxldmVsLnN0YXJ0Q0MgPSAwO1xuICAgIH1cbiAgICBpZiAobGV2ZWwuZnJhZ21lbnRIaW50KSB7XG4gICAgICB0b3RhbGR1cmF0aW9uICs9IGxldmVsLmZyYWdtZW50SGludC5kdXJhdGlvbjtcbiAgICB9XG4gICAgbGV2ZWwudG90YWxkdXJhdGlvbiA9IHRvdGFsZHVyYXRpb247XG4gICAgbGV2ZWwuZW5kQ0MgPSBkaXNjb250aW51aXR5Q291bnRlcjtcblxuICAgIC8qKlxuICAgICAqIEJhY2tmaWxsIGFueSBtaXNzaW5nIFBEVCB2YWx1ZXNcbiAgICAgKiBcIklmIHRoZSBmaXJzdCBFWFQtWC1QUk9HUkFNLURBVEUtVElNRSB0YWcgaW4gYSBQbGF5bGlzdCBhcHBlYXJzIGFmdGVyXG4gICAgICogb25lIG9yIG1vcmUgTWVkaWEgU2VnbWVudCBVUklzLCB0aGUgY2xpZW50IFNIT1VMRCBleHRyYXBvbGF0ZVxuICAgICAqIGJhY2t3YXJkIGZyb20gdGhhdCB0YWcgKHVzaW5nIEVYVElORiBkdXJhdGlvbnMgYW5kL29yIG1lZGlhXG4gICAgICogdGltZXN0YW1wcykgdG8gYXNzb2NpYXRlIGRhdGVzIHdpdGggdGhvc2Ugc2VnbWVudHMuXCJcbiAgICAgKiBXZSBoYXZlIGFscmVhZHkgZXh0cmFwb2xhdGVkIGZvcndhcmQsIGJ1dCBhbGwgZnJhZ21lbnRzIHVwIHRvIHRoZSBmaXJzdCBpbnN0YW5jZSBvZiBQRFQgZG8gbm90IGhhdmUgdGhlaXIgUERUc1xuICAgICAqIGNvbXB1dGVkLlxuICAgICAqL1xuICAgIGlmIChmaXJzdFBkdEluZGV4ID4gMCkge1xuICAgICAgYmFja2ZpbGxQcm9ncmFtRGF0ZVRpbWVzKGZyYWdtZW50cywgZmlyc3RQZHRJbmRleCk7XG4gICAgfVxuICAgIHJldHVybiBsZXZlbDtcbiAgfVxufVxuZnVuY3Rpb24gcGFyc2VLZXkoa2V5VGFnQXR0cmlidXRlcywgYmFzZXVybCwgcGFyc2VkKSB7XG4gIHZhciBfa2V5QXR0cnMkTUVUSE9ELCBfa2V5QXR0cnMkS0VZRk9STUFUO1xuICAvLyBodHRwczovL3Rvb2xzLmlldGYub3JnL2h0bWwvcmZjODIxNiNzZWN0aW9uLTQuMy4yLjRcbiAgY29uc3Qga2V5QXR0cnMgPSBuZXcgQXR0ckxpc3Qoa2V5VGFnQXR0cmlidXRlcyk7XG4gIHtcbiAgICBzdWJzdGl0dXRlVmFyaWFibGVzSW5BdHRyaWJ1dGVzKHBhcnNlZCwga2V5QXR0cnMsIFsnS0VZRk9STUFUJywgJ0tFWUZPUk1BVFZFUlNJT05TJywgJ1VSSScsICdJVicsICdVUkknXSk7XG4gIH1cbiAgY29uc3QgZGVjcnlwdG1ldGhvZCA9IChfa2V5QXR0cnMkTUVUSE9EID0ga2V5QXR0cnMuTUVUSE9EKSAhPSBudWxsID8gX2tleUF0dHJzJE1FVEhPRCA6ICcnO1xuICBjb25zdCBkZWNyeXB0dXJpID0ga2V5QXR0cnMuVVJJO1xuICBjb25zdCBkZWNyeXB0aXYgPSBrZXlBdHRycy5oZXhhZGVjaW1hbEludGVnZXIoJ0lWJyk7XG4gIGNvbnN0IGRlY3J5cHRrZXlmb3JtYXR2ZXJzaW9ucyA9IGtleUF0dHJzLktFWUZPUk1BVFZFUlNJT05TO1xuICAvLyBGcm9tIFJGQzogVGhpcyBhdHRyaWJ1dGUgaXMgT1BUSU9OQUw7IGl0cyBhYnNlbmNlIGluZGljYXRlcyBhbiBpbXBsaWNpdCB2YWx1ZSBvZiBcImlkZW50aXR5XCIuXG4gIGNvbnN0IGRlY3J5cHRrZXlmb3JtYXQgPSAoX2tleUF0dHJzJEtFWUZPUk1BVCA9IGtleUF0dHJzLktFWUZPUk1BVCkgIT0gbnVsbCA/IF9rZXlBdHRycyRLRVlGT1JNQVQgOiAnaWRlbnRpdHknO1xuICBpZiAoZGVjcnlwdHVyaSAmJiBrZXlBdHRycy5JViAmJiAhZGVjcnlwdGl2KSB7XG4gICAgbG9nZ2VyLmVycm9yKGBJbnZhbGlkIElWOiAke2tleUF0dHJzLklWfWApO1xuICB9XG4gIC8vIElmIGRlY3J5cHR1cmkgaXMgYSBVUkkgd2l0aCBhIHNjaGVtZSwgdGhlbiBiYXNldXJsIHdpbGwgYmUgaWdub3JlZFxuICAvLyBObyB1cmkgaXMgYWxsb3dlZCB3aGVuIE1FVEhPRCBpcyBOT05FXG4gIGNvbnN0IHJlc29sdmVkVXJpID0gZGVjcnlwdHVyaSA/IE0zVThQYXJzZXIucmVzb2x2ZShkZWNyeXB0dXJpLCBiYXNldXJsKSA6ICcnO1xuICBjb25zdCBrZXlGb3JtYXRWZXJzaW9ucyA9IChkZWNyeXB0a2V5Zm9ybWF0dmVyc2lvbnMgPyBkZWNyeXB0a2V5Zm9ybWF0dmVyc2lvbnMgOiAnMScpLnNwbGl0KCcvJykubWFwKE51bWJlcikuZmlsdGVyKE51bWJlci5pc0Zpbml0ZSk7XG4gIHJldHVybiBuZXcgTGV2ZWxLZXkoZGVjcnlwdG1ldGhvZCwgcmVzb2x2ZWRVcmksIGRlY3J5cHRrZXlmb3JtYXQsIGtleUZvcm1hdFZlcnNpb25zLCBkZWNyeXB0aXYpO1xufVxuZnVuY3Rpb24gcGFyc2VTdGFydFRpbWVPZmZzZXQoc3RhcnRBdHRyaWJ1dGVzKSB7XG4gIGNvbnN0IHN0YXJ0QXR0cnMgPSBuZXcgQXR0ckxpc3Qoc3RhcnRBdHRyaWJ1dGVzKTtcbiAgY29uc3Qgc3RhcnRUaW1lT2Zmc2V0ID0gc3RhcnRBdHRycy5kZWNpbWFsRmxvYXRpbmdQb2ludCgnVElNRS1PRkZTRVQnKTtcbiAgaWYgKGlzRmluaXRlTnVtYmVyKHN0YXJ0VGltZU9mZnNldCkpIHtcbiAgICByZXR1cm4gc3RhcnRUaW1lT2Zmc2V0O1xuICB9XG4gIHJldHVybiBudWxsO1xufVxuZnVuY3Rpb24gc2V0Q29kZWNzKGNvZGVjc0F0dHJpYnV0ZVZhbHVlLCBsZXZlbCkge1xuICBsZXQgY29kZWNzID0gKGNvZGVjc0F0dHJpYnV0ZVZhbHVlIHx8ICcnKS5zcGxpdCgvWyAsXSsvKS5maWx0ZXIoYyA9PiBjKTtcbiAgWyd2aWRlbycsICdhdWRpbycsICd0ZXh0J10uZm9yRWFjaCh0eXBlID0+IHtcbiAgICBjb25zdCBmaWx0ZXJlZCA9IGNvZGVjcy5maWx0ZXIoY29kZWMgPT4gaXNDb2RlY1R5cGUoY29kZWMsIHR5cGUpKTtcbiAgICBpZiAoZmlsdGVyZWQubGVuZ3RoKSB7XG4gICAgICAvLyBDb21tYSBzZXBhcmF0ZWQgbGlzdCBvZiBhbGwgY29kZWNzIGZvciB0eXBlXG4gICAgICBsZXZlbFtgJHt0eXBlfUNvZGVjYF0gPSBmaWx0ZXJlZC5qb2luKCcsJyk7XG4gICAgICAvLyBSZW1vdmUga25vd24gY29kZWNzIHNvIHRoYXQgb25seSB1bmtub3duQ29kZWNzIGFyZSBsZWZ0IGFmdGVyIGl0ZXJhdGluZyB0aHJvdWdoIGVhY2ggdHlwZVxuICAgICAgY29kZWNzID0gY29kZWNzLmZpbHRlcihjb2RlYyA9PiBmaWx0ZXJlZC5pbmRleE9mKGNvZGVjKSA9PT0gLTEpO1xuICAgIH1cbiAgfSk7XG4gIGxldmVsLnVua25vd25Db2RlY3MgPSBjb2RlY3M7XG59XG5mdW5jdGlvbiBhc3NpZ25Db2RlYyhtZWRpYSwgZ3JvdXBJdGVtLCBjb2RlY1Byb3BlcnR5KSB7XG4gIGNvbnN0IGNvZGVjVmFsdWUgPSBncm91cEl0ZW1bY29kZWNQcm9wZXJ0eV07XG4gIGlmIChjb2RlY1ZhbHVlKSB7XG4gICAgbWVkaWFbY29kZWNQcm9wZXJ0eV0gPSBjb2RlY1ZhbHVlO1xuICB9XG59XG5mdW5jdGlvbiBiYWNrZmlsbFByb2dyYW1EYXRlVGltZXMoZnJhZ21lbnRzLCBmaXJzdFBkdEluZGV4KSB7XG4gIGxldCBmcmFnUHJldiA9IGZyYWdtZW50c1tmaXJzdFBkdEluZGV4XTtcbiAgZm9yIChsZXQgaSA9IGZpcnN0UGR0SW5kZXg7IGktLTspIHtcbiAgICBjb25zdCBmcmFnID0gZnJhZ21lbnRzW2ldO1xuICAgIC8vIEV4aXQgb24gZGVsdGEtcGxheWxpc3Qgc2tpcHBlZCBzZWdtZW50c1xuICAgIGlmICghZnJhZykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBmcmFnLnByb2dyYW1EYXRlVGltZSA9IGZyYWdQcmV2LnByb2dyYW1EYXRlVGltZSAtIGZyYWcuZHVyYXRpb24gKiAxMDAwO1xuICAgIGZyYWdQcmV2ID0gZnJhZztcbiAgfVxufVxuZnVuY3Rpb24gYXNzaWduUHJvZ3JhbURhdGVUaW1lKGZyYWcsIHByZXZGcmFnKSB7XG4gIGlmIChmcmFnLnJhd1Byb2dyYW1EYXRlVGltZSkge1xuICAgIGZyYWcucHJvZ3JhbURhdGVUaW1lID0gRGF0ZS5wYXJzZShmcmFnLnJhd1Byb2dyYW1EYXRlVGltZSk7XG4gIH0gZWxzZSBpZiAocHJldkZyYWcgIT0gbnVsbCAmJiBwcmV2RnJhZy5wcm9ncmFtRGF0ZVRpbWUpIHtcbiAgICBmcmFnLnByb2dyYW1EYXRlVGltZSA9IHByZXZGcmFnLmVuZFByb2dyYW1EYXRlVGltZTtcbiAgfVxuICBpZiAoIWlzRmluaXRlTnVtYmVyKGZyYWcucHJvZ3JhbURhdGVUaW1lKSkge1xuICAgIGZyYWcucHJvZ3JhbURhdGVUaW1lID0gbnVsbDtcbiAgICBmcmFnLnJhd1Byb2dyYW1EYXRlVGltZSA9IG51bGw7XG4gIH1cbn1cbmZ1bmN0aW9uIHNldEluaXRTZWdtZW50KGZyYWcsIG1hcEF0dHJzLCBpZCwgbGV2ZWxrZXlzKSB7XG4gIGZyYWcucmVsdXJsID0gbWFwQXR0cnMuVVJJO1xuICBpZiAobWFwQXR0cnMuQllURVJBTkdFKSB7XG4gICAgZnJhZy5zZXRCeXRlUmFuZ2UobWFwQXR0cnMuQllURVJBTkdFKTtcbiAgfVxuICBmcmFnLmxldmVsID0gaWQ7XG4gIGZyYWcuc24gPSAnaW5pdFNlZ21lbnQnO1xuICBpZiAobGV2ZWxrZXlzKSB7XG4gICAgZnJhZy5sZXZlbGtleXMgPSBsZXZlbGtleXM7XG4gIH1cbiAgZnJhZy5pbml0U2VnbWVudCA9IG51bGw7XG59XG5mdW5jdGlvbiBzZXRGcmFnTGV2ZWxLZXlzKGZyYWcsIGxldmVsa2V5cywgbGV2ZWwpIHtcbiAgZnJhZy5sZXZlbGtleXMgPSBsZXZlbGtleXM7XG4gIGNvbnN0IHtcbiAgICBlbmNyeXB0ZWRGcmFnbWVudHNcbiAgfSA9IGxldmVsO1xuICBpZiAoKCFlbmNyeXB0ZWRGcmFnbWVudHMubGVuZ3RoIHx8IGVuY3J5cHRlZEZyYWdtZW50c1tlbmNyeXB0ZWRGcmFnbWVudHMubGVuZ3RoIC0gMV0ubGV2ZWxrZXlzICE9PSBsZXZlbGtleXMpICYmIE9iamVjdC5rZXlzKGxldmVsa2V5cykuc29tZShmb3JtYXQgPT4gbGV2ZWxrZXlzW2Zvcm1hdF0uaXNDb21tb25FbmNyeXB0aW9uKSkge1xuICAgIGVuY3J5cHRlZEZyYWdtZW50cy5wdXNoKGZyYWcpO1xuICB9XG59XG5cbnZhciBQbGF5bGlzdENvbnRleHRUeXBlID0ge1xuICBNQU5JRkVTVDogXCJtYW5pZmVzdFwiLFxuICBMRVZFTDogXCJsZXZlbFwiLFxuICBBVURJT19UUkFDSzogXCJhdWRpb1RyYWNrXCIsXG4gIFNVQlRJVExFX1RSQUNLOiBcInN1YnRpdGxlVHJhY2tcIlxufTtcbnZhciBQbGF5bGlzdExldmVsVHlwZSA9IHtcbiAgTUFJTjogXCJtYWluXCIsXG4gIEFVRElPOiBcImF1ZGlvXCIsXG4gIFNVQlRJVExFOiBcInN1YnRpdGxlXCJcbn07XG5cbmZ1bmN0aW9uIG1hcENvbnRleHRUb0xldmVsVHlwZShjb250ZXh0KSB7XG4gIGNvbnN0IHtcbiAgICB0eXBlXG4gIH0gPSBjb250ZXh0O1xuICBzd2l0Y2ggKHR5cGUpIHtcbiAgICBjYXNlIFBsYXlsaXN0Q29udGV4dFR5cGUuQVVESU9fVFJBQ0s6XG4gICAgICByZXR1cm4gUGxheWxpc3RMZXZlbFR5cGUuQVVESU87XG4gICAgY2FzZSBQbGF5bGlzdENvbnRleHRUeXBlLlNVQlRJVExFX1RSQUNLOlxuICAgICAgcmV0dXJuIFBsYXlsaXN0TGV2ZWxUeXBlLlNVQlRJVExFO1xuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4gUGxheWxpc3RMZXZlbFR5cGUuTUFJTjtcbiAgfVxufVxuZnVuY3Rpb24gZ2V0UmVzcG9uc2VVcmwocmVzcG9uc2UsIGNvbnRleHQpIHtcbiAgbGV0IHVybCA9IHJlc3BvbnNlLnVybDtcbiAgLy8gcmVzcG9uc2VVUkwgbm90IHN1cHBvcnRlZCBvbiBzb21lIGJyb3dzZXJzIChpdCBpcyB1c2VkIHRvIGRldGVjdCBVUkwgcmVkaXJlY3Rpb24pXG4gIC8vIGRhdGEtdXJpIG1vZGUgYWxzbyBub3Qgc3VwcG9ydGVkIChidXQgbm8gbmVlZCB0byBkZXRlY3QgcmVkaXJlY3Rpb24pXG4gIGlmICh1cmwgPT09IHVuZGVmaW5lZCB8fCB1cmwuaW5kZXhPZignZGF0YTonKSA9PT0gMCkge1xuICAgIC8vIGZhbGxiYWNrIHRvIGluaXRpYWwgVVJMXG4gICAgdXJsID0gY29udGV4dC51cmw7XG4gIH1cbiAgcmV0dXJuIHVybDtcbn1cbmNsYXNzIFBsYXlsaXN0TG9hZGVyIHtcbiAgY29uc3RydWN0b3IoaGxzKSB7XG4gICAgdGhpcy5obHMgPSB2b2lkIDA7XG4gICAgdGhpcy5sb2FkZXJzID0gT2JqZWN0LmNyZWF0ZShudWxsKTtcbiAgICB0aGlzLnZhcmlhYmxlTGlzdCA9IG51bGw7XG4gICAgdGhpcy5obHMgPSBobHM7XG4gICAgdGhpcy5yZWdpc3Rlckxpc3RlbmVycygpO1xuICB9XG4gIHN0YXJ0TG9hZChzdGFydFBvc2l0aW9uKSB7fVxuICBzdG9wTG9hZCgpIHtcbiAgICB0aGlzLmRlc3Ryb3lJbnRlcm5hbExvYWRlcnMoKTtcbiAgfVxuICByZWdpc3Rlckxpc3RlbmVycygpIHtcbiAgICBjb25zdCB7XG4gICAgICBobHNcbiAgICB9ID0gdGhpcztcbiAgICBobHMub24oRXZlbnRzLk1BTklGRVNUX0xPQURJTkcsIHRoaXMub25NYW5pZmVzdExvYWRpbmcsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuTEVWRUxfTE9BRElORywgdGhpcy5vbkxldmVsTG9hZGluZywgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5BVURJT19UUkFDS19MT0FESU5HLCB0aGlzLm9uQXVkaW9UcmFja0xvYWRpbmcsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuU1VCVElUTEVfVFJBQ0tfTE9BRElORywgdGhpcy5vblN1YnRpdGxlVHJhY2tMb2FkaW5nLCB0aGlzKTtcbiAgfVxuICB1bnJlZ2lzdGVyTGlzdGVuZXJzKCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGhsc1xuICAgIH0gPSB0aGlzO1xuICAgIGhscy5vZmYoRXZlbnRzLk1BTklGRVNUX0xPQURJTkcsIHRoaXMub25NYW5pZmVzdExvYWRpbmcsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkxFVkVMX0xPQURJTkcsIHRoaXMub25MZXZlbExvYWRpbmcsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkFVRElPX1RSQUNLX0xPQURJTkcsIHRoaXMub25BdWRpb1RyYWNrTG9hZGluZywgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuU1VCVElUTEVfVFJBQ0tfTE9BRElORywgdGhpcy5vblN1YnRpdGxlVHJhY2tMb2FkaW5nLCB0aGlzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGRlZmF1bHRzIG9yIGNvbmZpZ3VyZWQgbG9hZGVyLXR5cGUgb3ZlcmxvYWRzIChwTG9hZGVyIGFuZCBsb2FkZXIgY29uZmlnIHBhcmFtcylcbiAgICovXG4gIGNyZWF0ZUludGVybmFsTG9hZGVyKGNvbnRleHQpIHtcbiAgICBjb25zdCBjb25maWcgPSB0aGlzLmhscy5jb25maWc7XG4gICAgY29uc3QgUExvYWRlciA9IGNvbmZpZy5wTG9hZGVyO1xuICAgIGNvbnN0IExvYWRlciA9IGNvbmZpZy5sb2FkZXI7XG4gICAgY29uc3QgSW50ZXJuYWxMb2FkZXIgPSBQTG9hZGVyIHx8IExvYWRlcjtcbiAgICBjb25zdCBsb2FkZXIgPSBuZXcgSW50ZXJuYWxMb2FkZXIoY29uZmlnKTtcbiAgICB0aGlzLmxvYWRlcnNbY29udGV4dC50eXBlXSA9IGxvYWRlcjtcbiAgICByZXR1cm4gbG9hZGVyO1xuICB9XG4gIGdldEludGVybmFsTG9hZGVyKGNvbnRleHQpIHtcbiAgICByZXR1cm4gdGhpcy5sb2FkZXJzW2NvbnRleHQudHlwZV07XG4gIH1cbiAgcmVzZXRJbnRlcm5hbExvYWRlcihjb250ZXh0VHlwZSkge1xuICAgIGlmICh0aGlzLmxvYWRlcnNbY29udGV4dFR5cGVdKSB7XG4gICAgICBkZWxldGUgdGhpcy5sb2FkZXJzW2NvbnRleHRUeXBlXTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ2FsbCBgZGVzdHJveWAgb24gYWxsIGludGVybmFsIGxvYWRlciBpbnN0YW5jZXMgbWFwcGVkIChvbmUgcGVyIGNvbnRleHQgdHlwZSlcbiAgICovXG4gIGRlc3Ryb3lJbnRlcm5hbExvYWRlcnMoKSB7XG4gICAgZm9yIChjb25zdCBjb250ZXh0VHlwZSBpbiB0aGlzLmxvYWRlcnMpIHtcbiAgICAgIGNvbnN0IGxvYWRlciA9IHRoaXMubG9hZGVyc1tjb250ZXh0VHlwZV07XG4gICAgICBpZiAobG9hZGVyKSB7XG4gICAgICAgIGxvYWRlci5kZXN0cm95KCk7XG4gICAgICB9XG4gICAgICB0aGlzLnJlc2V0SW50ZXJuYWxMb2FkZXIoY29udGV4dFR5cGUpO1xuICAgIH1cbiAgfVxuICBkZXN0cm95KCkge1xuICAgIHRoaXMudmFyaWFibGVMaXN0ID0gbnVsbDtcbiAgICB0aGlzLnVucmVnaXN0ZXJMaXN0ZW5lcnMoKTtcbiAgICB0aGlzLmRlc3Ryb3lJbnRlcm5hbExvYWRlcnMoKTtcbiAgfVxuICBvbk1hbmlmZXN0TG9hZGluZyhldmVudCwgZGF0YSkge1xuICAgIGNvbnN0IHtcbiAgICAgIHVybFxuICAgIH0gPSBkYXRhO1xuICAgIHRoaXMudmFyaWFibGVMaXN0ID0gbnVsbDtcbiAgICB0aGlzLmxvYWQoe1xuICAgICAgaWQ6IG51bGwsXG4gICAgICBsZXZlbDogMCxcbiAgICAgIHJlc3BvbnNlVHlwZTogJ3RleHQnLFxuICAgICAgdHlwZTogUGxheWxpc3RDb250ZXh0VHlwZS5NQU5JRkVTVCxcbiAgICAgIHVybCxcbiAgICAgIGRlbGl2ZXJ5RGlyZWN0aXZlczogbnVsbFxuICAgIH0pO1xuICB9XG4gIG9uTGV2ZWxMb2FkaW5nKGV2ZW50LCBkYXRhKSB7XG4gICAgY29uc3Qge1xuICAgICAgaWQsXG4gICAgICBsZXZlbCxcbiAgICAgIHBhdGh3YXlJZCxcbiAgICAgIHVybCxcbiAgICAgIGRlbGl2ZXJ5RGlyZWN0aXZlc1xuICAgIH0gPSBkYXRhO1xuICAgIHRoaXMubG9hZCh7XG4gICAgICBpZCxcbiAgICAgIGxldmVsLFxuICAgICAgcGF0aHdheUlkLFxuICAgICAgcmVzcG9uc2VUeXBlOiAndGV4dCcsXG4gICAgICB0eXBlOiBQbGF5bGlzdENvbnRleHRUeXBlLkxFVkVMLFxuICAgICAgdXJsLFxuICAgICAgZGVsaXZlcnlEaXJlY3RpdmVzXG4gICAgfSk7XG4gIH1cbiAgb25BdWRpb1RyYWNrTG9hZGluZyhldmVudCwgZGF0YSkge1xuICAgIGNvbnN0IHtcbiAgICAgIGlkLFxuICAgICAgZ3JvdXBJZCxcbiAgICAgIHVybCxcbiAgICAgIGRlbGl2ZXJ5RGlyZWN0aXZlc1xuICAgIH0gPSBkYXRhO1xuICAgIHRoaXMubG9hZCh7XG4gICAgICBpZCxcbiAgICAgIGdyb3VwSWQsXG4gICAgICBsZXZlbDogbnVsbCxcbiAgICAgIHJlc3BvbnNlVHlwZTogJ3RleHQnLFxuICAgICAgdHlwZTogUGxheWxpc3RDb250ZXh0VHlwZS5BVURJT19UUkFDSyxcbiAgICAgIHVybCxcbiAgICAgIGRlbGl2ZXJ5RGlyZWN0aXZlc1xuICAgIH0pO1xuICB9XG4gIG9uU3VidGl0bGVUcmFja0xvYWRpbmcoZXZlbnQsIGRhdGEpIHtcbiAgICBjb25zdCB7XG4gICAgICBpZCxcbiAgICAgIGdyb3VwSWQsXG4gICAgICB1cmwsXG4gICAgICBkZWxpdmVyeURpcmVjdGl2ZXNcbiAgICB9ID0gZGF0YTtcbiAgICB0aGlzLmxvYWQoe1xuICAgICAgaWQsXG4gICAgICBncm91cElkLFxuICAgICAgbGV2ZWw6IG51bGwsXG4gICAgICByZXNwb25zZVR5cGU6ICd0ZXh0JyxcbiAgICAgIHR5cGU6IFBsYXlsaXN0Q29udGV4dFR5cGUuU1VCVElUTEVfVFJBQ0ssXG4gICAgICB1cmwsXG4gICAgICBkZWxpdmVyeURpcmVjdGl2ZXNcbiAgICB9KTtcbiAgfVxuICBsb2FkKGNvbnRleHQpIHtcbiAgICB2YXIgX2NvbnRleHQkZGVsaXZlcnlEaXJlO1xuICAgIGNvbnN0IGNvbmZpZyA9IHRoaXMuaGxzLmNvbmZpZztcblxuICAgIC8vIGxvZ2dlci5kZWJ1ZyhgW3BsYXlsaXN0LWxvYWRlcl06IExvYWRpbmcgcGxheWxpc3Qgb2YgdHlwZSAke2NvbnRleHQudHlwZX0sIGxldmVsOiAke2NvbnRleHQubGV2ZWx9LCBpZDogJHtjb250ZXh0LmlkfWApO1xuXG4gICAgLy8gQ2hlY2sgaWYgYSBsb2FkZXIgZm9yIHRoaXMgY29udGV4dCBhbHJlYWR5IGV4aXN0c1xuICAgIGxldCBsb2FkZXIgPSB0aGlzLmdldEludGVybmFsTG9hZGVyKGNvbnRleHQpO1xuICAgIGlmIChsb2FkZXIpIHtcbiAgICAgIGNvbnN0IGxvYWRlckNvbnRleHQgPSBsb2FkZXIuY29udGV4dDtcbiAgICAgIGlmIChsb2FkZXJDb250ZXh0ICYmIGxvYWRlckNvbnRleHQudXJsID09PSBjb250ZXh0LnVybCAmJiBsb2FkZXJDb250ZXh0LmxldmVsID09PSBjb250ZXh0LmxldmVsKSB7XG4gICAgICAgIC8vIHNhbWUgVVJMIGNhbid0IG92ZXJsYXBcbiAgICAgICAgbG9nZ2VyLnRyYWNlKCdbcGxheWxpc3QtbG9hZGVyXTogcGxheWxpc3QgcmVxdWVzdCBvbmdvaW5nJyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGxvZ2dlci5sb2coYFtwbGF5bGlzdC1sb2FkZXJdOiBhYm9ydGluZyBwcmV2aW91cyBsb2FkZXIgZm9yIHR5cGU6ICR7Y29udGV4dC50eXBlfWApO1xuICAgICAgbG9hZGVyLmFib3J0KCk7XG4gICAgfVxuXG4gICAgLy8gYXBwbHkgZGlmZmVyZW50IGNvbmZpZ3MgZm9yIHJldHJpZXMgZGVwZW5kaW5nIG9uXG4gICAgLy8gY29udGV4dCAobWFuaWZlc3QsIGxldmVsLCBhdWRpby9zdWJzIHBsYXlsaXN0KVxuICAgIGxldCBsb2FkUG9saWN5O1xuICAgIGlmIChjb250ZXh0LnR5cGUgPT09IFBsYXlsaXN0Q29udGV4dFR5cGUuTUFOSUZFU1QpIHtcbiAgICAgIGxvYWRQb2xpY3kgPSBjb25maWcubWFuaWZlc3RMb2FkUG9saWN5LmRlZmF1bHQ7XG4gICAgfSBlbHNlIHtcbiAgICAgIGxvYWRQb2xpY3kgPSBfZXh0ZW5kcyh7fSwgY29uZmlnLnBsYXlsaXN0TG9hZFBvbGljeS5kZWZhdWx0LCB7XG4gICAgICAgIHRpbWVvdXRSZXRyeTogbnVsbCxcbiAgICAgICAgZXJyb3JSZXRyeTogbnVsbFxuICAgICAgfSk7XG4gICAgfVxuICAgIGxvYWRlciA9IHRoaXMuY3JlYXRlSW50ZXJuYWxMb2FkZXIoY29udGV4dCk7XG5cbiAgICAvLyBPdmVycmlkZSBsZXZlbC90cmFjayB0aW1lb3V0IGZvciBMTC1ITFMgcmVxdWVzdHNcbiAgICAvLyAodGhlIGRlZmF1bHQgb2YgMTAwMDBtcyBpcyBjb3VudGVyIHByb2R1Y3RpdmUgdG8gYmxvY2tpbmcgcGxheWxpc3QgcmVsb2FkIHJlcXVlc3RzKVxuICAgIGlmIChpc0Zpbml0ZU51bWJlcigoX2NvbnRleHQkZGVsaXZlcnlEaXJlID0gY29udGV4dC5kZWxpdmVyeURpcmVjdGl2ZXMpID09IG51bGwgPyB2b2lkIDAgOiBfY29udGV4dCRkZWxpdmVyeURpcmUucGFydCkpIHtcbiAgICAgIGxldCBsZXZlbERldGFpbHM7XG4gICAgICBpZiAoY29udGV4dC50eXBlID09PSBQbGF5bGlzdENvbnRleHRUeXBlLkxFVkVMICYmIGNvbnRleHQubGV2ZWwgIT09IG51bGwpIHtcbiAgICAgICAgbGV2ZWxEZXRhaWxzID0gdGhpcy5obHMubGV2ZWxzW2NvbnRleHQubGV2ZWxdLmRldGFpbHM7XG4gICAgICB9IGVsc2UgaWYgKGNvbnRleHQudHlwZSA9PT0gUGxheWxpc3RDb250ZXh0VHlwZS5BVURJT19UUkFDSyAmJiBjb250ZXh0LmlkICE9PSBudWxsKSB7XG4gICAgICAgIGxldmVsRGV0YWlscyA9IHRoaXMuaGxzLmF1ZGlvVHJhY2tzW2NvbnRleHQuaWRdLmRldGFpbHM7XG4gICAgICB9IGVsc2UgaWYgKGNvbnRleHQudHlwZSA9PT0gUGxheWxpc3RDb250ZXh0VHlwZS5TVUJUSVRMRV9UUkFDSyAmJiBjb250ZXh0LmlkICE9PSBudWxsKSB7XG4gICAgICAgIGxldmVsRGV0YWlscyA9IHRoaXMuaGxzLnN1YnRpdGxlVHJhY2tzW2NvbnRleHQuaWRdLmRldGFpbHM7XG4gICAgICB9XG4gICAgICBpZiAobGV2ZWxEZXRhaWxzKSB7XG4gICAgICAgIGNvbnN0IHBhcnRUYXJnZXQgPSBsZXZlbERldGFpbHMucGFydFRhcmdldDtcbiAgICAgICAgY29uc3QgdGFyZ2V0RHVyYXRpb24gPSBsZXZlbERldGFpbHMudGFyZ2V0ZHVyYXRpb247XG4gICAgICAgIGlmIChwYXJ0VGFyZ2V0ICYmIHRhcmdldER1cmF0aW9uKSB7XG4gICAgICAgICAgY29uc3QgbWF4TG93TGF0ZW5jeVBsYXlsaXN0UmVmcmVzaCA9IE1hdGgubWF4KHBhcnRUYXJnZXQgKiAzLCB0YXJnZXREdXJhdGlvbiAqIDAuOCkgKiAxMDAwO1xuICAgICAgICAgIGxvYWRQb2xpY3kgPSBfZXh0ZW5kcyh7fSwgbG9hZFBvbGljeSwge1xuICAgICAgICAgICAgbWF4VGltZVRvRmlyc3RCeXRlTXM6IE1hdGgubWluKG1heExvd0xhdGVuY3lQbGF5bGlzdFJlZnJlc2gsIGxvYWRQb2xpY3kubWF4VGltZVRvRmlyc3RCeXRlTXMpLFxuICAgICAgICAgICAgbWF4TG9hZFRpbWVNczogTWF0aC5taW4obWF4TG93TGF0ZW5jeVBsYXlsaXN0UmVmcmVzaCwgbG9hZFBvbGljeS5tYXhUaW1lVG9GaXJzdEJ5dGVNcylcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICBjb25zdCBsZWdhY3lSZXRyeUNvbXBhdGliaWxpdHkgPSBsb2FkUG9saWN5LmVycm9yUmV0cnkgfHwgbG9hZFBvbGljeS50aW1lb3V0UmV0cnkgfHwge307XG4gICAgY29uc3QgbG9hZGVyQ29uZmlnID0ge1xuICAgICAgbG9hZFBvbGljeSxcbiAgICAgIHRpbWVvdXQ6IGxvYWRQb2xpY3kubWF4TG9hZFRpbWVNcyxcbiAgICAgIG1heFJldHJ5OiBsZWdhY3lSZXRyeUNvbXBhdGliaWxpdHkubWF4TnVtUmV0cnkgfHwgMCxcbiAgICAgIHJldHJ5RGVsYXk6IGxlZ2FjeVJldHJ5Q29tcGF0aWJpbGl0eS5yZXRyeURlbGF5TXMgfHwgMCxcbiAgICAgIG1heFJldHJ5RGVsYXk6IGxlZ2FjeVJldHJ5Q29tcGF0aWJpbGl0eS5tYXhSZXRyeURlbGF5TXMgfHwgMFxuICAgIH07XG4gICAgY29uc3QgbG9hZGVyQ2FsbGJhY2tzID0ge1xuICAgICAgb25TdWNjZXNzOiAocmVzcG9uc2UsIHN0YXRzLCBjb250ZXh0LCBuZXR3b3JrRGV0YWlscykgPT4ge1xuICAgICAgICBjb25zdCBsb2FkZXIgPSB0aGlzLmdldEludGVybmFsTG9hZGVyKGNvbnRleHQpO1xuICAgICAgICB0aGlzLnJlc2V0SW50ZXJuYWxMb2FkZXIoY29udGV4dC50eXBlKTtcbiAgICAgICAgY29uc3Qgc3RyaW5nID0gcmVzcG9uc2UuZGF0YTtcblxuICAgICAgICAvLyBWYWxpZGF0ZSBpZiBpdCBpcyBhbiBNM1U4IGF0IGFsbFxuICAgICAgICBpZiAoc3RyaW5nLmluZGV4T2YoJyNFWFRNM1UnKSAhPT0gMCkge1xuICAgICAgICAgIHRoaXMuaGFuZGxlTWFuaWZlc3RQYXJzaW5nRXJyb3IocmVzcG9uc2UsIGNvbnRleHQsIG5ldyBFcnJvcignbm8gRVhUTTNVIGRlbGltaXRlcicpLCBuZXR3b3JrRGV0YWlscyB8fCBudWxsLCBzdGF0cyk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHN0YXRzLnBhcnNpbmcuc3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgICAgICAgaWYgKE0zVThQYXJzZXIuaXNNZWRpYVBsYXlsaXN0KHN0cmluZykpIHtcbiAgICAgICAgICB0aGlzLmhhbmRsZVRyYWNrT3JMZXZlbFBsYXlsaXN0KHJlc3BvbnNlLCBzdGF0cywgY29udGV4dCwgbmV0d29ya0RldGFpbHMgfHwgbnVsbCwgbG9hZGVyKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0aGlzLmhhbmRsZU1hc3RlclBsYXlsaXN0KHJlc3BvbnNlLCBzdGF0cywgY29udGV4dCwgbmV0d29ya0RldGFpbHMpO1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgb25FcnJvcjogKHJlc3BvbnNlLCBjb250ZXh0LCBuZXR3b3JrRGV0YWlscywgc3RhdHMpID0+IHtcbiAgICAgICAgdGhpcy5oYW5kbGVOZXR3b3JrRXJyb3IoY29udGV4dCwgbmV0d29ya0RldGFpbHMsIGZhbHNlLCByZXNwb25zZSwgc3RhdHMpO1xuICAgICAgfSxcbiAgICAgIG9uVGltZW91dDogKHN0YXRzLCBjb250ZXh0LCBuZXR3b3JrRGV0YWlscykgPT4ge1xuICAgICAgICB0aGlzLmhhbmRsZU5ldHdvcmtFcnJvcihjb250ZXh0LCBuZXR3b3JrRGV0YWlscywgdHJ1ZSwgdW5kZWZpbmVkLCBzdGF0cyk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIC8vIGxvZ2dlci5kZWJ1ZyhgW3BsYXlsaXN0LWxvYWRlcl06IENhbGxpbmcgaW50ZXJuYWwgbG9hZGVyIGRlbGVnYXRlIGZvciBVUkw6ICR7Y29udGV4dC51cmx9YCk7XG5cbiAgICBsb2FkZXIubG9hZChjb250ZXh0LCBsb2FkZXJDb25maWcsIGxvYWRlckNhbGxiYWNrcyk7XG4gIH1cbiAgaGFuZGxlTWFzdGVyUGxheWxpc3QocmVzcG9uc2UsIHN0YXRzLCBjb250ZXh0LCBuZXR3b3JrRGV0YWlscykge1xuICAgIGNvbnN0IGhscyA9IHRoaXMuaGxzO1xuICAgIGNvbnN0IHN0cmluZyA9IHJlc3BvbnNlLmRhdGE7XG4gICAgY29uc3QgdXJsID0gZ2V0UmVzcG9uc2VVcmwocmVzcG9uc2UsIGNvbnRleHQpO1xuICAgIGNvbnN0IHBhcnNlZFJlc3VsdCA9IE0zVThQYXJzZXIucGFyc2VNYXN0ZXJQbGF5bGlzdChzdHJpbmcsIHVybCk7XG4gICAgaWYgKHBhcnNlZFJlc3VsdC5wbGF5bGlzdFBhcnNpbmdFcnJvcikge1xuICAgICAgdGhpcy5oYW5kbGVNYW5pZmVzdFBhcnNpbmdFcnJvcihyZXNwb25zZSwgY29udGV4dCwgcGFyc2VkUmVzdWx0LnBsYXlsaXN0UGFyc2luZ0Vycm9yLCBuZXR3b3JrRGV0YWlscywgc3RhdHMpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB7XG4gICAgICBjb250ZW50U3RlZXJpbmcsXG4gICAgICBsZXZlbHMsXG4gICAgICBzZXNzaW9uRGF0YSxcbiAgICAgIHNlc3Npb25LZXlzLFxuICAgICAgc3RhcnRUaW1lT2Zmc2V0LFxuICAgICAgdmFyaWFibGVMaXN0XG4gICAgfSA9IHBhcnNlZFJlc3VsdDtcbiAgICB0aGlzLnZhcmlhYmxlTGlzdCA9IHZhcmlhYmxlTGlzdDtcbiAgICBjb25zdCB7XG4gICAgICBBVURJTzogYXVkaW9UcmFja3MgPSBbXSxcbiAgICAgIFNVQlRJVExFUzogc3VidGl0bGVzLFxuICAgICAgJ0NMT1NFRC1DQVBUSU9OUyc6IGNhcHRpb25zXG4gICAgfSA9IE0zVThQYXJzZXIucGFyc2VNYXN0ZXJQbGF5bGlzdE1lZGlhKHN0cmluZywgdXJsLCBwYXJzZWRSZXN1bHQpO1xuICAgIGlmIChhdWRpb1RyYWNrcy5sZW5ndGgpIHtcbiAgICAgIC8vIGNoZWNrIGlmIHdlIGhhdmUgZm91bmQgYW4gYXVkaW8gdHJhY2sgZW1iZWRkZWQgaW4gbWFpbiBwbGF5bGlzdCAoYXVkaW8gdHJhY2sgd2l0aG91dCBVUkkgYXR0cmlidXRlKVxuICAgICAgY29uc3QgZW1iZWRkZWRBdWRpb0ZvdW5kID0gYXVkaW9UcmFja3Muc29tZShhdWRpb1RyYWNrID0+ICFhdWRpb1RyYWNrLnVybCk7XG5cbiAgICAgIC8vIGlmIG5vIGVtYmVkZGVkIGF1ZGlvIHRyYWNrIGRlZmluZWQsIGJ1dCBhdWRpbyBjb2RlYyBzaWduYWxlZCBpbiBxdWFsaXR5IGxldmVsLFxuICAgICAgLy8gd2UgbmVlZCB0byBzaWduYWwgdGhpcyBtYWluIGF1ZGlvIHRyYWNrIHRoaXMgY291bGQgaGFwcGVuIHdpdGggcGxheWxpc3RzIHdpdGhcbiAgICAgIC8vIGFsdCBhdWRpbyByZW5kaXRpb24gaW4gd2hpY2ggcXVhbGl0eSBsZXZlbHMgKG1haW4pXG4gICAgICAvLyBjb250YWlucyBib3RoIGF1ZGlvK3ZpZGVvLiBidXQgd2l0aCBtaXhlZCBhdWRpbyB0cmFjayBub3Qgc2lnbmFsZWRcbiAgICAgIGlmICghZW1iZWRkZWRBdWRpb0ZvdW5kICYmIGxldmVsc1swXS5hdWRpb0NvZGVjICYmICFsZXZlbHNbMF0uYXR0cnMuQVVESU8pIHtcbiAgICAgICAgbG9nZ2VyLmxvZygnW3BsYXlsaXN0LWxvYWRlcl06IGF1ZGlvIGNvZGVjIHNpZ25hbGVkIGluIHF1YWxpdHkgbGV2ZWwsIGJ1dCBubyBlbWJlZGRlZCBhdWRpbyB0cmFjayBzaWduYWxlZCwgY3JlYXRlIG9uZScpO1xuICAgICAgICBhdWRpb1RyYWNrcy51bnNoaWZ0KHtcbiAgICAgICAgICB0eXBlOiAnbWFpbicsXG4gICAgICAgICAgbmFtZTogJ21haW4nLFxuICAgICAgICAgIGdyb3VwSWQ6ICdtYWluJyxcbiAgICAgICAgICBkZWZhdWx0OiBmYWxzZSxcbiAgICAgICAgICBhdXRvc2VsZWN0OiBmYWxzZSxcbiAgICAgICAgICBmb3JjZWQ6IGZhbHNlLFxuICAgICAgICAgIGlkOiAtMSxcbiAgICAgICAgICBhdHRyczogbmV3IEF0dHJMaXN0KHt9KSxcbiAgICAgICAgICBiaXRyYXRlOiAwLFxuICAgICAgICAgIHVybDogJydcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuICAgIGhscy50cmlnZ2VyKEV2ZW50cy5NQU5JRkVTVF9MT0FERUQsIHtcbiAgICAgIGxldmVscyxcbiAgICAgIGF1ZGlvVHJhY2tzLFxuICAgICAgc3VidGl0bGVzLFxuICAgICAgY2FwdGlvbnMsXG4gICAgICBjb250ZW50U3RlZXJpbmcsXG4gICAgICB1cmwsXG4gICAgICBzdGF0cyxcbiAgICAgIG5ldHdvcmtEZXRhaWxzLFxuICAgICAgc2Vzc2lvbkRhdGEsXG4gICAgICBzZXNzaW9uS2V5cyxcbiAgICAgIHN0YXJ0VGltZU9mZnNldCxcbiAgICAgIHZhcmlhYmxlTGlzdFxuICAgIH0pO1xuICB9XG4gIGhhbmRsZVRyYWNrT3JMZXZlbFBsYXlsaXN0KHJlc3BvbnNlLCBzdGF0cywgY29udGV4dCwgbmV0d29ya0RldGFpbHMsIGxvYWRlcikge1xuICAgIGNvbnN0IGhscyA9IHRoaXMuaGxzO1xuICAgIGNvbnN0IHtcbiAgICAgIGlkLFxuICAgICAgbGV2ZWwsXG4gICAgICB0eXBlXG4gICAgfSA9IGNvbnRleHQ7XG4gICAgY29uc3QgdXJsID0gZ2V0UmVzcG9uc2VVcmwocmVzcG9uc2UsIGNvbnRleHQpO1xuICAgIGNvbnN0IGxldmVsVXJsSWQgPSAwO1xuICAgIGNvbnN0IGxldmVsSWQgPSBpc0Zpbml0ZU51bWJlcihsZXZlbCkgPyBsZXZlbCA6IGlzRmluaXRlTnVtYmVyKGlkKSA/IGlkIDogMDtcbiAgICBjb25zdCBsZXZlbFR5cGUgPSBtYXBDb250ZXh0VG9MZXZlbFR5cGUoY29udGV4dCk7XG4gICAgY29uc3QgbGV2ZWxEZXRhaWxzID0gTTNVOFBhcnNlci5wYXJzZUxldmVsUGxheWxpc3QocmVzcG9uc2UuZGF0YSwgdXJsLCBsZXZlbElkLCBsZXZlbFR5cGUsIGxldmVsVXJsSWQsIHRoaXMudmFyaWFibGVMaXN0KTtcblxuICAgIC8vIFdlIGhhdmUgZG9uZSBvdXIgZmlyc3QgcmVxdWVzdCAoTWFuaWZlc3QtdHlwZSkgYW5kIHJlY2VpdmVcbiAgICAvLyBub3QgYSBtYXN0ZXIgcGxheWxpc3QgYnV0IGEgY2h1bmstbGlzdCAodHJhY2svbGV2ZWwpXG4gICAgLy8gV2UgZmlyZSB0aGUgbWFuaWZlc3QtbG9hZGVkIGV2ZW50IGFueXdheSB3aXRoIHRoZSBwYXJzZWQgbGV2ZWwtZGV0YWlsc1xuICAgIC8vIGJ5IGNyZWF0aW5nIGEgc2luZ2xlLWxldmVsIHN0cnVjdHVyZSBmb3IgaXQuXG4gICAgaWYgKHR5cGUgPT09IFBsYXlsaXN0Q29udGV4dFR5cGUuTUFOSUZFU1QpIHtcbiAgICAgIGNvbnN0IHNpbmdsZUxldmVsID0ge1xuICAgICAgICBhdHRyczogbmV3IEF0dHJMaXN0KHt9KSxcbiAgICAgICAgYml0cmF0ZTogMCxcbiAgICAgICAgZGV0YWlsczogbGV2ZWxEZXRhaWxzLFxuICAgICAgICBuYW1lOiAnJyxcbiAgICAgICAgdXJsXG4gICAgICB9O1xuICAgICAgaGxzLnRyaWdnZXIoRXZlbnRzLk1BTklGRVNUX0xPQURFRCwge1xuICAgICAgICBsZXZlbHM6IFtzaW5nbGVMZXZlbF0sXG4gICAgICAgIGF1ZGlvVHJhY2tzOiBbXSxcbiAgICAgICAgdXJsLFxuICAgICAgICBzdGF0cyxcbiAgICAgICAgbmV0d29ya0RldGFpbHMsXG4gICAgICAgIHNlc3Npb25EYXRhOiBudWxsLFxuICAgICAgICBzZXNzaW9uS2V5czogbnVsbCxcbiAgICAgICAgY29udGVudFN0ZWVyaW5nOiBudWxsLFxuICAgICAgICBzdGFydFRpbWVPZmZzZXQ6IG51bGwsXG4gICAgICAgIHZhcmlhYmxlTGlzdDogbnVsbFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gc2F2ZSBwYXJzaW5nIHRpbWVcbiAgICBzdGF0cy5wYXJzaW5nLmVuZCA9IHBlcmZvcm1hbmNlLm5vdygpO1xuXG4gICAgLy8gZXh0ZW5kIHRoZSBjb250ZXh0IHdpdGggdGhlIG5ldyBsZXZlbERldGFpbHMgcHJvcGVydHlcbiAgICBjb250ZXh0LmxldmVsRGV0YWlscyA9IGxldmVsRGV0YWlscztcbiAgICB0aGlzLmhhbmRsZVBsYXlsaXN0TG9hZGVkKGxldmVsRGV0YWlscywgcmVzcG9uc2UsIHN0YXRzLCBjb250ZXh0LCBuZXR3b3JrRGV0YWlscywgbG9hZGVyKTtcbiAgfVxuICBoYW5kbGVNYW5pZmVzdFBhcnNpbmdFcnJvcihyZXNwb25zZSwgY29udGV4dCwgZXJyb3IsIG5ldHdvcmtEZXRhaWxzLCBzdGF0cykge1xuICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLkVSUk9SLCB7XG4gICAgICB0eXBlOiBFcnJvclR5cGVzLk5FVFdPUktfRVJST1IsXG4gICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuTUFOSUZFU1RfUEFSU0lOR19FUlJPUixcbiAgICAgIGZhdGFsOiBjb250ZXh0LnR5cGUgPT09IFBsYXlsaXN0Q29udGV4dFR5cGUuTUFOSUZFU1QsXG4gICAgICB1cmw6IHJlc3BvbnNlLnVybCxcbiAgICAgIGVycjogZXJyb3IsXG4gICAgICBlcnJvcixcbiAgICAgIHJlYXNvbjogZXJyb3IubWVzc2FnZSxcbiAgICAgIHJlc3BvbnNlLFxuICAgICAgY29udGV4dCxcbiAgICAgIG5ldHdvcmtEZXRhaWxzLFxuICAgICAgc3RhdHNcbiAgICB9KTtcbiAgfVxuICBoYW5kbGVOZXR3b3JrRXJyb3IoY29udGV4dCwgbmV0d29ya0RldGFpbHMsIHRpbWVvdXQgPSBmYWxzZSwgcmVzcG9uc2UsIHN0YXRzKSB7XG4gICAgbGV0IG1lc3NhZ2UgPSBgQSBuZXR3b3JrICR7dGltZW91dCA/ICd0aW1lb3V0JyA6ICdlcnJvcicgKyAocmVzcG9uc2UgPyAnIChzdGF0dXMgJyArIHJlc3BvbnNlLmNvZGUgKyAnKScgOiAnJyl9IG9jY3VycmVkIHdoaWxlIGxvYWRpbmcgJHtjb250ZXh0LnR5cGV9YDtcbiAgICBpZiAoY29udGV4dC50eXBlID09PSBQbGF5bGlzdENvbnRleHRUeXBlLkxFVkVMKSB7XG4gICAgICBtZXNzYWdlICs9IGA6ICR7Y29udGV4dC5sZXZlbH0gaWQ6ICR7Y29udGV4dC5pZH1gO1xuICAgIH0gZWxzZSBpZiAoY29udGV4dC50eXBlID09PSBQbGF5bGlzdENvbnRleHRUeXBlLkFVRElPX1RSQUNLIHx8IGNvbnRleHQudHlwZSA9PT0gUGxheWxpc3RDb250ZXh0VHlwZS5TVUJUSVRMRV9UUkFDSykge1xuICAgICAgbWVzc2FnZSArPSBgIGlkOiAke2NvbnRleHQuaWR9IGdyb3VwLWlkOiBcIiR7Y29udGV4dC5ncm91cElkfVwiYDtcbiAgICB9XG4gICAgY29uc3QgZXJyb3IgPSBuZXcgRXJyb3IobWVzc2FnZSk7XG4gICAgbG9nZ2VyLndhcm4oYFtwbGF5bGlzdC1sb2FkZXJdOiAke21lc3NhZ2V9YCk7XG4gICAgbGV0IGRldGFpbHMgPSBFcnJvckRldGFpbHMuVU5LTk9XTjtcbiAgICBsZXQgZmF0YWwgPSBmYWxzZTtcbiAgICBjb25zdCBsb2FkZXIgPSB0aGlzLmdldEludGVybmFsTG9hZGVyKGNvbnRleHQpO1xuICAgIHN3aXRjaCAoY29udGV4dC50eXBlKSB7XG4gICAgICBjYXNlIFBsYXlsaXN0Q29udGV4dFR5cGUuTUFOSUZFU1Q6XG4gICAgICAgIGRldGFpbHMgPSB0aW1lb3V0ID8gRXJyb3JEZXRhaWxzLk1BTklGRVNUX0xPQURfVElNRU9VVCA6IEVycm9yRGV0YWlscy5NQU5JRkVTVF9MT0FEX0VSUk9SO1xuICAgICAgICBmYXRhbCA9IHRydWU7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSBQbGF5bGlzdENvbnRleHRUeXBlLkxFVkVMOlxuICAgICAgICBkZXRhaWxzID0gdGltZW91dCA/IEVycm9yRGV0YWlscy5MRVZFTF9MT0FEX1RJTUVPVVQgOiBFcnJvckRldGFpbHMuTEVWRUxfTE9BRF9FUlJPUjtcbiAgICAgICAgZmF0YWwgPSBmYWxzZTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIFBsYXlsaXN0Q29udGV4dFR5cGUuQVVESU9fVFJBQ0s6XG4gICAgICAgIGRldGFpbHMgPSB0aW1lb3V0ID8gRXJyb3JEZXRhaWxzLkFVRElPX1RSQUNLX0xPQURfVElNRU9VVCA6IEVycm9yRGV0YWlscy5BVURJT19UUkFDS19MT0FEX0VSUk9SO1xuICAgICAgICBmYXRhbCA9IGZhbHNlO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgUGxheWxpc3RDb250ZXh0VHlwZS5TVUJUSVRMRV9UUkFDSzpcbiAgICAgICAgZGV0YWlscyA9IHRpbWVvdXQgPyBFcnJvckRldGFpbHMuU1VCVElUTEVfVFJBQ0tfTE9BRF9USU1FT1VUIDogRXJyb3JEZXRhaWxzLlNVQlRJVExFX0xPQURfRVJST1I7XG4gICAgICAgIGZhdGFsID0gZmFsc2U7XG4gICAgICAgIGJyZWFrO1xuICAgIH1cbiAgICBpZiAobG9hZGVyKSB7XG4gICAgICB0aGlzLnJlc2V0SW50ZXJuYWxMb2FkZXIoY29udGV4dC50eXBlKTtcbiAgICB9XG4gICAgY29uc3QgZXJyb3JEYXRhID0ge1xuICAgICAgdHlwZTogRXJyb3JUeXBlcy5ORVRXT1JLX0VSUk9SLFxuICAgICAgZGV0YWlscyxcbiAgICAgIGZhdGFsLFxuICAgICAgdXJsOiBjb250ZXh0LnVybCxcbiAgICAgIGxvYWRlcixcbiAgICAgIGNvbnRleHQsXG4gICAgICBlcnJvcixcbiAgICAgIG5ldHdvcmtEZXRhaWxzLFxuICAgICAgc3RhdHNcbiAgICB9O1xuICAgIGlmIChyZXNwb25zZSkge1xuICAgICAgY29uc3QgdXJsID0gKG5ldHdvcmtEZXRhaWxzID09IG51bGwgPyB2b2lkIDAgOiBuZXR3b3JrRGV0YWlscy51cmwpIHx8IGNvbnRleHQudXJsO1xuICAgICAgZXJyb3JEYXRhLnJlc3BvbnNlID0gX29iamVjdFNwcmVhZDIoe1xuICAgICAgICB1cmwsXG4gICAgICAgIGRhdGE6IHVuZGVmaW5lZFxuICAgICAgfSwgcmVzcG9uc2UpO1xuICAgIH1cbiAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5FUlJPUiwgZXJyb3JEYXRhKTtcbiAgfVxuICBoYW5kbGVQbGF5bGlzdExvYWRlZChsZXZlbERldGFpbHMsIHJlc3BvbnNlLCBzdGF0cywgY29udGV4dCwgbmV0d29ya0RldGFpbHMsIGxvYWRlcikge1xuICAgIGNvbnN0IGhscyA9IHRoaXMuaGxzO1xuICAgIGNvbnN0IHtcbiAgICAgIHR5cGUsXG4gICAgICBsZXZlbCxcbiAgICAgIGlkLFxuICAgICAgZ3JvdXBJZCxcbiAgICAgIGRlbGl2ZXJ5RGlyZWN0aXZlc1xuICAgIH0gPSBjb250ZXh0O1xuICAgIGNvbnN0IHVybCA9IGdldFJlc3BvbnNlVXJsKHJlc3BvbnNlLCBjb250ZXh0KTtcbiAgICBjb25zdCBwYXJlbnQgPSBtYXBDb250ZXh0VG9MZXZlbFR5cGUoY29udGV4dCk7XG4gICAgY29uc3QgbGV2ZWxJbmRleCA9IHR5cGVvZiBjb250ZXh0LmxldmVsID09PSAnbnVtYmVyJyAmJiBwYXJlbnQgPT09IFBsYXlsaXN0TGV2ZWxUeXBlLk1BSU4gPyBsZXZlbCA6IHVuZGVmaW5lZDtcbiAgICBpZiAoIWxldmVsRGV0YWlscy5mcmFnbWVudHMubGVuZ3RoKSB7XG4gICAgICBjb25zdCBfZXJyb3IgPSBuZXcgRXJyb3IoJ05vIFNlZ21lbnRzIGZvdW5kIGluIFBsYXlsaXN0Jyk7XG4gICAgICBobHMudHJpZ2dlcihFdmVudHMuRVJST1IsIHtcbiAgICAgICAgdHlwZTogRXJyb3JUeXBlcy5ORVRXT1JLX0VSUk9SLFxuICAgICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuTEVWRUxfRU1QVFlfRVJST1IsXG4gICAgICAgIGZhdGFsOiBmYWxzZSxcbiAgICAgICAgdXJsLFxuICAgICAgICBlcnJvcjogX2Vycm9yLFxuICAgICAgICByZWFzb246IF9lcnJvci5tZXNzYWdlLFxuICAgICAgICByZXNwb25zZSxcbiAgICAgICAgY29udGV4dCxcbiAgICAgICAgbGV2ZWw6IGxldmVsSW5kZXgsXG4gICAgICAgIHBhcmVudCxcbiAgICAgICAgbmV0d29ya0RldGFpbHMsXG4gICAgICAgIHN0YXRzXG4gICAgICB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKCFsZXZlbERldGFpbHMudGFyZ2V0ZHVyYXRpb24pIHtcbiAgICAgIGxldmVsRGV0YWlscy5wbGF5bGlzdFBhcnNpbmdFcnJvciA9IG5ldyBFcnJvcignTWlzc2luZyBUYXJnZXQgRHVyYXRpb24nKTtcbiAgICB9XG4gICAgY29uc3QgZXJyb3IgPSBsZXZlbERldGFpbHMucGxheWxpc3RQYXJzaW5nRXJyb3I7XG4gICAgaWYgKGVycm9yKSB7XG4gICAgICBobHMudHJpZ2dlcihFdmVudHMuRVJST1IsIHtcbiAgICAgICAgdHlwZTogRXJyb3JUeXBlcy5ORVRXT1JLX0VSUk9SLFxuICAgICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuTEVWRUxfUEFSU0lOR19FUlJPUixcbiAgICAgICAgZmF0YWw6IGZhbHNlLFxuICAgICAgICB1cmwsXG4gICAgICAgIGVycm9yLFxuICAgICAgICByZWFzb246IGVycm9yLm1lc3NhZ2UsXG4gICAgICAgIHJlc3BvbnNlLFxuICAgICAgICBjb250ZXh0LFxuICAgICAgICBsZXZlbDogbGV2ZWxJbmRleCxcbiAgICAgICAgcGFyZW50LFxuICAgICAgICBuZXR3b3JrRGV0YWlscyxcbiAgICAgICAgc3RhdHNcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAobGV2ZWxEZXRhaWxzLmxpdmUgJiYgbG9hZGVyKSB7XG4gICAgICBpZiAobG9hZGVyLmdldENhY2hlQWdlKSB7XG4gICAgICAgIGxldmVsRGV0YWlscy5hZ2VIZWFkZXIgPSBsb2FkZXIuZ2V0Q2FjaGVBZ2UoKSB8fCAwO1xuICAgICAgfVxuICAgICAgaWYgKCFsb2FkZXIuZ2V0Q2FjaGVBZ2UgfHwgaXNOYU4obGV2ZWxEZXRhaWxzLmFnZUhlYWRlcikpIHtcbiAgICAgICAgbGV2ZWxEZXRhaWxzLmFnZUhlYWRlciA9IDA7XG4gICAgICB9XG4gICAgfVxuICAgIHN3aXRjaCAodHlwZSkge1xuICAgICAgY2FzZSBQbGF5bGlzdENvbnRleHRUeXBlLk1BTklGRVNUOlxuICAgICAgY2FzZSBQbGF5bGlzdENvbnRleHRUeXBlLkxFVkVMOlxuICAgICAgICBobHMudHJpZ2dlcihFdmVudHMuTEVWRUxfTE9BREVELCB7XG4gICAgICAgICAgZGV0YWlsczogbGV2ZWxEZXRhaWxzLFxuICAgICAgICAgIGxldmVsOiBsZXZlbEluZGV4IHx8IDAsXG4gICAgICAgICAgaWQ6IGlkIHx8IDAsXG4gICAgICAgICAgc3RhdHMsXG4gICAgICAgICAgbmV0d29ya0RldGFpbHMsXG4gICAgICAgICAgZGVsaXZlcnlEaXJlY3RpdmVzXG4gICAgICAgIH0pO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgUGxheWxpc3RDb250ZXh0VHlwZS5BVURJT19UUkFDSzpcbiAgICAgICAgaGxzLnRyaWdnZXIoRXZlbnRzLkFVRElPX1RSQUNLX0xPQURFRCwge1xuICAgICAgICAgIGRldGFpbHM6IGxldmVsRGV0YWlscyxcbiAgICAgICAgICBpZDogaWQgfHwgMCxcbiAgICAgICAgICBncm91cElkOiBncm91cElkIHx8ICcnLFxuICAgICAgICAgIHN0YXRzLFxuICAgICAgICAgIG5ldHdvcmtEZXRhaWxzLFxuICAgICAgICAgIGRlbGl2ZXJ5RGlyZWN0aXZlc1xuICAgICAgICB9KTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIFBsYXlsaXN0Q29udGV4dFR5cGUuU1VCVElUTEVfVFJBQ0s6XG4gICAgICAgIGhscy50cmlnZ2VyKEV2ZW50cy5TVUJUSVRMRV9UUkFDS19MT0FERUQsIHtcbiAgICAgICAgICBkZXRhaWxzOiBsZXZlbERldGFpbHMsXG4gICAgICAgICAgaWQ6IGlkIHx8IDAsXG4gICAgICAgICAgZ3JvdXBJZDogZ3JvdXBJZCB8fCAnJyxcbiAgICAgICAgICBzdGF0cyxcbiAgICAgICAgICBuZXR3b3JrRGV0YWlscyxcbiAgICAgICAgICBkZWxpdmVyeURpcmVjdGl2ZXNcbiAgICAgICAgfSk7XG4gICAgICAgIGJyZWFrO1xuICAgIH1cbiAgfVxufVxuXG5mdW5jdGlvbiBzZW5kQWRkVHJhY2tFdmVudCh0cmFjaywgdmlkZW9FbCkge1xuICBsZXQgZXZlbnQ7XG4gIHRyeSB7XG4gICAgZXZlbnQgPSBuZXcgRXZlbnQoJ2FkZHRyYWNrJyk7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIC8vIGZvciBJRTExXG4gICAgZXZlbnQgPSBkb2N1bWVudC5jcmVhdGVFdmVudCgnRXZlbnQnKTtcbiAgICBldmVudC5pbml0RXZlbnQoJ2FkZHRyYWNrJywgZmFsc2UsIGZhbHNlKTtcbiAgfVxuICBldmVudC50cmFjayA9IHRyYWNrO1xuICB2aWRlb0VsLmRpc3BhdGNoRXZlbnQoZXZlbnQpO1xufVxuZnVuY3Rpb24gYWRkQ3VlVG9UcmFjayh0cmFjaywgY3VlKSB7XG4gIC8vIFNvbWV0aW1lcyB0aGVyZSBhcmUgY3VlIG92ZXJsYXBzIG9uIHNlZ21lbnRlZCB2dHRzIHNvIHRoZSBzYW1lXG4gIC8vIGN1ZSBjYW4gYXBwZWFyIG1vcmUgdGhhbiBvbmNlIGluIGRpZmZlcmVudCB2dHQgZmlsZXMuXG4gIC8vIFRoaXMgYXZvaWQgc2hvd2luZyBkdXBsaWNhdGVkIGN1ZXMgd2l0aCBzYW1lIHRpbWVjb2RlIGFuZCB0ZXh0LlxuICBjb25zdCBtb2RlID0gdHJhY2subW9kZTtcbiAgaWYgKG1vZGUgPT09ICdkaXNhYmxlZCcpIHtcbiAgICB0cmFjay5tb2RlID0gJ2hpZGRlbic7XG4gIH1cbiAgaWYgKHRyYWNrLmN1ZXMgJiYgIXRyYWNrLmN1ZXMuZ2V0Q3VlQnlJZChjdWUuaWQpKSB7XG4gICAgdHJ5IHtcbiAgICAgIHRyYWNrLmFkZEN1ZShjdWUpO1xuICAgICAgaWYgKCF0cmFjay5jdWVzLmdldEN1ZUJ5SWQoY3VlLmlkKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYGFkZEN1ZSBpcyBmYWlsZWQgZm9yOiAke2N1ZX1gKTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZyhgW3RleHR0cmFjay11dGlsc106ICR7ZXJyfWApO1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgdGV4dFRyYWNrQ3VlID0gbmV3IHNlbGYuVGV4dFRyYWNrQ3VlKGN1ZS5zdGFydFRpbWUsIGN1ZS5lbmRUaW1lLCBjdWUudGV4dCk7XG4gICAgICAgIHRleHRUcmFja0N1ZS5pZCA9IGN1ZS5pZDtcbiAgICAgICAgdHJhY2suYWRkQ3VlKHRleHRUcmFja0N1ZSk7XG4gICAgICB9IGNhdGNoIChlcnIyKSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZyhgW3RleHR0cmFjay11dGlsc106IExlZ2FjeSBUZXh0VHJhY2tDdWUgZmFsbGJhY2sgZmFpbGVkOiAke2VycjJ9YCk7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIGlmIChtb2RlID09PSAnZGlzYWJsZWQnKSB7XG4gICAgdHJhY2subW9kZSA9IG1vZGU7XG4gIH1cbn1cbmZ1bmN0aW9uIGNsZWFyQ3VycmVudEN1ZXModHJhY2spIHtcbiAgLy8gV2hlbiB0cmFjay5tb2RlIGlzIGRpc2FibGVkLCB0cmFjay5jdWVzIHdpbGwgYmUgbnVsbC5cbiAgLy8gVG8gZ3VhcmFudGVlIHRoZSByZW1vdmFsIG9mIGN1ZXMsIHdlIG5lZWQgdG8gdGVtcG9yYXJpbHlcbiAgLy8gY2hhbmdlIHRoZSBtb2RlIHRvIGhpZGRlblxuICBjb25zdCBtb2RlID0gdHJhY2subW9kZTtcbiAgaWYgKG1vZGUgPT09ICdkaXNhYmxlZCcpIHtcbiAgICB0cmFjay5tb2RlID0gJ2hpZGRlbic7XG4gIH1cbiAgaWYgKHRyYWNrLmN1ZXMpIHtcbiAgICBmb3IgKGxldCBpID0gdHJhY2suY3Vlcy5sZW5ndGg7IGktLTspIHtcbiAgICAgIHRyYWNrLnJlbW92ZUN1ZSh0cmFjay5jdWVzW2ldKTtcbiAgICB9XG4gIH1cbiAgaWYgKG1vZGUgPT09ICdkaXNhYmxlZCcpIHtcbiAgICB0cmFjay5tb2RlID0gbW9kZTtcbiAgfVxufVxuZnVuY3Rpb24gcmVtb3ZlQ3Vlc0luUmFuZ2UodHJhY2ssIHN0YXJ0LCBlbmQsIHByZWRpY2F0ZSkge1xuICBjb25zdCBtb2RlID0gdHJhY2subW9kZTtcbiAgaWYgKG1vZGUgPT09ICdkaXNhYmxlZCcpIHtcbiAgICB0cmFjay5tb2RlID0gJ2hpZGRlbic7XG4gIH1cbiAgaWYgKHRyYWNrLmN1ZXMgJiYgdHJhY2suY3Vlcy5sZW5ndGggPiAwKSB7XG4gICAgY29uc3QgY3VlcyA9IGdldEN1ZXNJblJhbmdlKHRyYWNrLmN1ZXMsIHN0YXJ0LCBlbmQpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgY3Vlcy5sZW5ndGg7IGkrKykge1xuICAgICAgaWYgKCFwcmVkaWNhdGUgfHwgcHJlZGljYXRlKGN1ZXNbaV0pKSB7XG4gICAgICAgIHRyYWNrLnJlbW92ZUN1ZShjdWVzW2ldKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgaWYgKG1vZGUgPT09ICdkaXNhYmxlZCcpIHtcbiAgICB0cmFjay5tb2RlID0gbW9kZTtcbiAgfVxufVxuXG4vLyBGaW5kIGZpcnN0IGN1ZSBzdGFydGluZyBhZnRlciBnaXZlbiB0aW1lLlxuLy8gTW9kaWZpZWQgdmVyc2lvbiBvZiBiaW5hcnkgc2VhcmNoIE8obG9nKG4pKS5cbmZ1bmN0aW9uIGdldEZpcnN0Q3VlSW5kZXhBZnRlclRpbWUoY3VlcywgdGltZSkge1xuICAvLyBJZiBmaXJzdCBjdWUgc3RhcnRzIGFmdGVyIHRpbWUsIHN0YXJ0IHRoZXJlXG4gIGlmICh0aW1lIDwgY3Vlc1swXS5zdGFydFRpbWUpIHtcbiAgICByZXR1cm4gMDtcbiAgfVxuICAvLyBJZiB0aGUgbGFzdCBjdWUgZW5kcyBiZWZvcmUgdGltZSB0aGVyZSBpcyBubyBvdmVybGFwXG4gIGNvbnN0IGxlbiA9IGN1ZXMubGVuZ3RoIC0gMTtcbiAgaWYgKHRpbWUgPiBjdWVzW2xlbl0uZW5kVGltZSkge1xuICAgIHJldHVybiAtMTtcbiAgfVxuICBsZXQgbGVmdCA9IDA7XG4gIGxldCByaWdodCA9IGxlbjtcbiAgd2hpbGUgKGxlZnQgPD0gcmlnaHQpIHtcbiAgICBjb25zdCBtaWQgPSBNYXRoLmZsb29yKChyaWdodCArIGxlZnQpIC8gMik7XG4gICAgaWYgKHRpbWUgPCBjdWVzW21pZF0uc3RhcnRUaW1lKSB7XG4gICAgICByaWdodCA9IG1pZCAtIDE7XG4gICAgfSBlbHNlIGlmICh0aW1lID4gY3Vlc1ttaWRdLnN0YXJ0VGltZSAmJiBsZWZ0IDwgbGVuKSB7XG4gICAgICBsZWZ0ID0gbWlkICsgMTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gSWYgaXQncyBub3QgbG93ZXIgb3IgaGlnaGVyLCBpdCBtdXN0IGJlIGVxdWFsLlxuICAgICAgcmV0dXJuIG1pZDtcbiAgICB9XG4gIH1cbiAgLy8gQXQgdGhpcyBwb2ludCwgbGVmdCBhbmQgcmlnaHQgaGF2ZSBzd2FwcGVkLlxuICAvLyBObyBkaXJlY3QgbWF0Y2ggd2FzIGZvdW5kLCBsZWZ0IG9yIHJpZ2h0IGVsZW1lbnQgbXVzdCBiZSB0aGUgY2xvc2VzdC4gQ2hlY2sgd2hpY2ggb25lIGhhcyB0aGUgc21hbGxlc3QgZGlmZi5cbiAgcmV0dXJuIGN1ZXNbbGVmdF0uc3RhcnRUaW1lIC0gdGltZSA8IHRpbWUgLSBjdWVzW3JpZ2h0XS5zdGFydFRpbWUgPyBsZWZ0IDogcmlnaHQ7XG59XG5mdW5jdGlvbiBnZXRDdWVzSW5SYW5nZShjdWVzLCBzdGFydCwgZW5kKSB7XG4gIGNvbnN0IGN1ZXNGb3VuZCA9IFtdO1xuICBjb25zdCBmaXJzdEN1ZUluUmFuZ2UgPSBnZXRGaXJzdEN1ZUluZGV4QWZ0ZXJUaW1lKGN1ZXMsIHN0YXJ0KTtcbiAgaWYgKGZpcnN0Q3VlSW5SYW5nZSA+IC0xKSB7XG4gICAgZm9yIChsZXQgaSA9IGZpcnN0Q3VlSW5SYW5nZSwgbGVuID0gY3Vlcy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuICAgICAgY29uc3QgY3VlID0gY3Vlc1tpXTtcbiAgICAgIGlmIChjdWUuc3RhcnRUaW1lID49IHN0YXJ0ICYmIGN1ZS5lbmRUaW1lIDw9IGVuZCkge1xuICAgICAgICBjdWVzRm91bmQucHVzaChjdWUpO1xuICAgICAgfSBlbHNlIGlmIChjdWUuc3RhcnRUaW1lID4gZW5kKSB7XG4gICAgICAgIHJldHVybiBjdWVzRm91bmQ7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIHJldHVybiBjdWVzRm91bmQ7XG59XG5mdW5jdGlvbiBmaWx0ZXJTdWJ0aXRsZVRyYWNrcyh0ZXh0VHJhY2tMaXN0KSB7XG4gIGNvbnN0IHRyYWNrcyA9IFtdO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IHRleHRUcmFja0xpc3QubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCB0cmFjayA9IHRleHRUcmFja0xpc3RbaV07XG4gICAgLy8gRWRnZSBhZGRzIGEgdHJhY2sgd2l0aG91dCBhIGxhYmVsOyB3ZSBkb24ndCB3YW50IHRvIHVzZSBpdFxuICAgIGlmICgodHJhY2sua2luZCA9PT0gJ3N1YnRpdGxlcycgfHwgdHJhY2sua2luZCA9PT0gJ2NhcHRpb25zJykgJiYgdHJhY2subGFiZWwpIHtcbiAgICAgIHRyYWNrcy5wdXNoKHRleHRUcmFja0xpc3RbaV0pO1xuICAgIH1cbiAgfVxuICByZXR1cm4gdHJhY2tzO1xufVxuXG52YXIgTWV0YWRhdGFTY2hlbWEgPSB7XG4gIGF1ZGlvSWQzOiBcIm9yZy5pZDNcIixcbiAgZGF0ZVJhbmdlOiBcImNvbS5hcHBsZS5xdWlja3RpbWUuSExTXCIsXG4gIGVtc2c6IFwiaHR0cHM6Ly9hb21lZGlhLm9yZy9lbXNnL0lEM1wiXG59O1xuXG5jb25zdCBNSU5fQ1VFX0RVUkFUSU9OID0gMC4yNTtcbmZ1bmN0aW9uIGdldEN1ZUNsYXNzKCkge1xuICBpZiAodHlwZW9mIHNlbGYgPT09ICd1bmRlZmluZWQnKSByZXR1cm4gdW5kZWZpbmVkO1xuICByZXR1cm4gc2VsZi5WVFRDdWUgfHwgc2VsZi5UZXh0VHJhY2tDdWU7XG59XG5mdW5jdGlvbiBjcmVhdGVDdWVXaXRoRGF0YUZpZWxkcyhDdWUsIHN0YXJ0VGltZSwgZW5kVGltZSwgZGF0YSwgdHlwZSkge1xuICBsZXQgY3VlID0gbmV3IEN1ZShzdGFydFRpbWUsIGVuZFRpbWUsICcnKTtcbiAgdHJ5IHtcbiAgICBjdWUudmFsdWUgPSBkYXRhO1xuICAgIGlmICh0eXBlKSB7XG4gICAgICBjdWUudHlwZSA9IHR5cGU7XG4gICAgfVxuICB9IGNhdGNoIChlKSB7XG4gICAgY3VlID0gbmV3IEN1ZShzdGFydFRpbWUsIGVuZFRpbWUsIEpTT04uc3RyaW5naWZ5KHR5cGUgPyBfb2JqZWN0U3ByZWFkMih7XG4gICAgICB0eXBlXG4gICAgfSwgZGF0YSkgOiBkYXRhKSk7XG4gIH1cbiAgcmV0dXJuIGN1ZTtcbn1cblxuLy8gVlRUQ3VlIGxhdGVzdCBkcmFmdCBhbGxvd3MgYW4gaW5maW5pdGUgZHVyYXRpb24sIGZhbGxiYWNrXG4vLyB0byBNQVhfVkFMVUUgaWYgbmVjZXNzYXJ5XG5jb25zdCBNQVhfQ1VFX0VORFRJTUUgPSAoKCkgPT4ge1xuICBjb25zdCBDdWUgPSBnZXRDdWVDbGFzcygpO1xuICB0cnkge1xuICAgIEN1ZSAmJiBuZXcgQ3VlKDAsIE51bWJlci5QT1NJVElWRV9JTkZJTklUWSwgJycpO1xuICB9IGNhdGNoIChlKSB7XG4gICAgcmV0dXJuIE51bWJlci5NQVhfVkFMVUU7XG4gIH1cbiAgcmV0dXJuIE51bWJlci5QT1NJVElWRV9JTkZJTklUWTtcbn0pKCk7XG5mdW5jdGlvbiBkYXRlUmFuZ2VEYXRlVG9UaW1lbGluZVNlY29uZHMoZGF0ZSwgb2Zmc2V0KSB7XG4gIHJldHVybiBkYXRlLmdldFRpbWUoKSAvIDEwMDAgLSBvZmZzZXQ7XG59XG5mdW5jdGlvbiBoZXhUb0FycmF5QnVmZmVyKHN0cikge1xuICByZXR1cm4gVWludDhBcnJheS5mcm9tKHN0ci5yZXBsYWNlKC9eMHgvLCAnJykucmVwbGFjZSgvKFtcXGRhLWZBLUZdezJ9KSA/L2csICcweCQxICcpLnJlcGxhY2UoLyArJC8sICcnKS5zcGxpdCgnICcpKS5idWZmZXI7XG59XG5jbGFzcyBJRDNUcmFja0NvbnRyb2xsZXIge1xuICBjb25zdHJ1Y3RvcihobHMpIHtcbiAgICB0aGlzLmhscyA9IHZvaWQgMDtcbiAgICB0aGlzLmlkM1RyYWNrID0gbnVsbDtcbiAgICB0aGlzLm1lZGlhID0gbnVsbDtcbiAgICB0aGlzLmRhdGVSYW5nZUN1ZXNBcHBlbmRlZCA9IHt9O1xuICAgIHRoaXMuaGxzID0gaGxzO1xuICAgIHRoaXMuX3JlZ2lzdGVyTGlzdGVuZXJzKCk7XG4gIH1cbiAgZGVzdHJveSgpIHtcbiAgICB0aGlzLl91bnJlZ2lzdGVyTGlzdGVuZXJzKCk7XG4gICAgdGhpcy5pZDNUcmFjayA9IG51bGw7XG4gICAgdGhpcy5tZWRpYSA9IG51bGw7XG4gICAgdGhpcy5kYXRlUmFuZ2VDdWVzQXBwZW5kZWQgPSB7fTtcbiAgICAvLyBAdHMtaWdub3JlXG4gICAgdGhpcy5obHMgPSBudWxsO1xuICB9XG4gIF9yZWdpc3Rlckxpc3RlbmVycygpIHtcbiAgICBjb25zdCB7XG4gICAgICBobHNcbiAgICB9ID0gdGhpcztcbiAgICBobHMub24oRXZlbnRzLk1FRElBX0FUVEFDSEVELCB0aGlzLm9uTWVkaWFBdHRhY2hlZCwgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5NRURJQV9ERVRBQ0hJTkcsIHRoaXMub25NZWRpYURldGFjaGluZywgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5NQU5JRkVTVF9MT0FESU5HLCB0aGlzLm9uTWFuaWZlc3RMb2FkaW5nLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkZSQUdfUEFSU0lOR19NRVRBREFUQSwgdGhpcy5vbkZyYWdQYXJzaW5nTWV0YWRhdGEsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuQlVGRkVSX0ZMVVNISU5HLCB0aGlzLm9uQnVmZmVyRmx1c2hpbmcsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuTEVWRUxfVVBEQVRFRCwgdGhpcy5vbkxldmVsVXBkYXRlZCwgdGhpcyk7XG4gIH1cbiAgX3VucmVnaXN0ZXJMaXN0ZW5lcnMoKSB7XG4gICAgY29uc3Qge1xuICAgICAgaGxzXG4gICAgfSA9IHRoaXM7XG4gICAgaGxzLm9mZihFdmVudHMuTUVESUFfQVRUQUNIRUQsIHRoaXMub25NZWRpYUF0dGFjaGVkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5NRURJQV9ERVRBQ0hJTkcsIHRoaXMub25NZWRpYURldGFjaGluZywgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuTUFOSUZFU1RfTE9BRElORywgdGhpcy5vbk1hbmlmZXN0TG9hZGluZywgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuRlJBR19QQVJTSU5HX01FVEFEQVRBLCB0aGlzLm9uRnJhZ1BhcnNpbmdNZXRhZGF0YSwgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuQlVGRkVSX0ZMVVNISU5HLCB0aGlzLm9uQnVmZmVyRmx1c2hpbmcsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkxFVkVMX1VQREFURUQsIHRoaXMub25MZXZlbFVwZGF0ZWQsIHRoaXMpO1xuICB9XG5cbiAgLy8gQWRkIElEMyBtZXRhdGFkYXRhIHRleHQgdHJhY2suXG4gIG9uTWVkaWFBdHRhY2hlZChldmVudCwgZGF0YSkge1xuICAgIHRoaXMubWVkaWEgPSBkYXRhLm1lZGlhO1xuICB9XG4gIG9uTWVkaWFEZXRhY2hpbmcoKSB7XG4gICAgaWYgKCF0aGlzLmlkM1RyYWNrKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNsZWFyQ3VycmVudEN1ZXModGhpcy5pZDNUcmFjayk7XG4gICAgdGhpcy5pZDNUcmFjayA9IG51bGw7XG4gICAgdGhpcy5tZWRpYSA9IG51bGw7XG4gICAgdGhpcy5kYXRlUmFuZ2VDdWVzQXBwZW5kZWQgPSB7fTtcbiAgfVxuICBvbk1hbmlmZXN0TG9hZGluZygpIHtcbiAgICB0aGlzLmRhdGVSYW5nZUN1ZXNBcHBlbmRlZCA9IHt9O1xuICB9XG4gIGNyZWF0ZVRyYWNrKG1lZGlhKSB7XG4gICAgY29uc3QgdHJhY2sgPSB0aGlzLmdldElEM1RyYWNrKG1lZGlhLnRleHRUcmFja3MpO1xuICAgIHRyYWNrLm1vZGUgPSAnaGlkZGVuJztcbiAgICByZXR1cm4gdHJhY2s7XG4gIH1cbiAgZ2V0SUQzVHJhY2sodGV4dFRyYWNrcykge1xuICAgIGlmICghdGhpcy5tZWRpYSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRleHRUcmFja3MubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IHRleHRUcmFjayA9IHRleHRUcmFja3NbaV07XG4gICAgICBpZiAodGV4dFRyYWNrLmtpbmQgPT09ICdtZXRhZGF0YScgJiYgdGV4dFRyYWNrLmxhYmVsID09PSAnaWQzJykge1xuICAgICAgICAvLyBzZW5kICdhZGR0cmFjaycgd2hlbiByZXVzaW5nIHRoZSB0ZXh0VHJhY2sgZm9yIG1ldGFkYXRhLFxuICAgICAgICAvLyBzYW1lIGFzIHdoYXQgd2UgZG8gZm9yIGNhcHRpb25zXG4gICAgICAgIHNlbmRBZGRUcmFja0V2ZW50KHRleHRUcmFjaywgdGhpcy5tZWRpYSk7XG4gICAgICAgIHJldHVybiB0ZXh0VHJhY2s7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB0aGlzLm1lZGlhLmFkZFRleHRUcmFjaygnbWV0YWRhdGEnLCAnaWQzJyk7XG4gIH1cbiAgb25GcmFnUGFyc2luZ01ldGFkYXRhKGV2ZW50LCBkYXRhKSB7XG4gICAgaWYgKCF0aGlzLm1lZGlhKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHtcbiAgICAgIGhsczoge1xuICAgICAgICBjb25maWc6IHtcbiAgICAgICAgICBlbmFibGVFbXNnTWV0YWRhdGFDdWVzLFxuICAgICAgICAgIGVuYWJsZUlEM01ldGFkYXRhQ3Vlc1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSA9IHRoaXM7XG4gICAgaWYgKCFlbmFibGVFbXNnTWV0YWRhdGFDdWVzICYmICFlbmFibGVJRDNNZXRhZGF0YUN1ZXMpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3Qge1xuICAgICAgc2FtcGxlc1xuICAgIH0gPSBkYXRhO1xuXG4gICAgLy8gY3JlYXRlIHRyYWNrIGR5bmFtaWNhbGx5XG4gICAgaWYgKCF0aGlzLmlkM1RyYWNrKSB7XG4gICAgICB0aGlzLmlkM1RyYWNrID0gdGhpcy5jcmVhdGVUcmFjayh0aGlzLm1lZGlhKTtcbiAgICB9XG4gICAgY29uc3QgQ3VlID0gZ2V0Q3VlQ2xhc3MoKTtcbiAgICBpZiAoIUN1ZSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHNhbXBsZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IHR5cGUgPSBzYW1wbGVzW2ldLnR5cGU7XG4gICAgICBpZiAodHlwZSA9PT0gTWV0YWRhdGFTY2hlbWEuZW1zZyAmJiAhZW5hYmxlRW1zZ01ldGFkYXRhQ3VlcyB8fCAhZW5hYmxlSUQzTWV0YWRhdGFDdWVzKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgY29uc3QgZnJhbWVzID0gZ2V0SUQzRnJhbWVzKHNhbXBsZXNbaV0uZGF0YSk7XG4gICAgICBpZiAoZnJhbWVzKSB7XG4gICAgICAgIGNvbnN0IHN0YXJ0VGltZSA9IHNhbXBsZXNbaV0ucHRzO1xuICAgICAgICBsZXQgZW5kVGltZSA9IHN0YXJ0VGltZSArIHNhbXBsZXNbaV0uZHVyYXRpb247XG4gICAgICAgIGlmIChlbmRUaW1lID4gTUFYX0NVRV9FTkRUSU1FKSB7XG4gICAgICAgICAgZW5kVGltZSA9IE1BWF9DVUVfRU5EVElNRTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCB0aW1lRGlmZiA9IGVuZFRpbWUgLSBzdGFydFRpbWU7XG4gICAgICAgIGlmICh0aW1lRGlmZiA8PSAwKSB7XG4gICAgICAgICAgZW5kVGltZSA9IHN0YXJ0VGltZSArIE1JTl9DVUVfRFVSQVRJT047XG4gICAgICAgIH1cbiAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCBmcmFtZXMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgICBjb25zdCBmcmFtZSA9IGZyYW1lc1tqXTtcbiAgICAgICAgICAvLyBTYWZhcmkgZG9lc24ndCBwdXQgdGhlIHRpbWVzdGFtcCBmcmFtZSBpbiB0aGUgVGV4dFRyYWNrXG4gICAgICAgICAgaWYgKCFpc1RpbWVTdGFtcEZyYW1lKGZyYW1lKSkge1xuICAgICAgICAgICAgLy8gYWRkIGEgYm91bmRzIHRvIGFueSB1bmJvdW5kZWQgY3Vlc1xuICAgICAgICAgICAgdGhpcy51cGRhdGVJZDNDdWVFbmRzKHN0YXJ0VGltZSwgdHlwZSk7XG4gICAgICAgICAgICBjb25zdCBjdWUgPSBjcmVhdGVDdWVXaXRoRGF0YUZpZWxkcyhDdWUsIHN0YXJ0VGltZSwgZW5kVGltZSwgZnJhbWUsIHR5cGUpO1xuICAgICAgICAgICAgaWYgKGN1ZSkge1xuICAgICAgICAgICAgICB0aGlzLmlkM1RyYWNrLmFkZEN1ZShjdWUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuICB1cGRhdGVJZDNDdWVFbmRzKHN0YXJ0VGltZSwgdHlwZSkge1xuICAgIHZhciBfdGhpcyRpZDNUcmFjaztcbiAgICBjb25zdCBjdWVzID0gKF90aGlzJGlkM1RyYWNrID0gdGhpcy5pZDNUcmFjaykgPT0gbnVsbCA/IHZvaWQgMCA6IF90aGlzJGlkM1RyYWNrLmN1ZXM7XG4gICAgaWYgKGN1ZXMpIHtcbiAgICAgIGZvciAobGV0IGkgPSBjdWVzLmxlbmd0aDsgaS0tOykge1xuICAgICAgICBjb25zdCBjdWUgPSBjdWVzW2ldO1xuICAgICAgICBpZiAoY3VlLnR5cGUgPT09IHR5cGUgJiYgY3VlLnN0YXJ0VGltZSA8IHN0YXJ0VGltZSAmJiBjdWUuZW5kVGltZSA9PT0gTUFYX0NVRV9FTkRUSU1FKSB7XG4gICAgICAgICAgY3VlLmVuZFRpbWUgPSBzdGFydFRpbWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgb25CdWZmZXJGbHVzaGluZyhldmVudCwge1xuICAgIHN0YXJ0T2Zmc2V0LFxuICAgIGVuZE9mZnNldCxcbiAgICB0eXBlXG4gIH0pIHtcbiAgICBjb25zdCB7XG4gICAgICBpZDNUcmFjayxcbiAgICAgIGhsc1xuICAgIH0gPSB0aGlzO1xuICAgIGlmICghaGxzKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHtcbiAgICAgIGNvbmZpZzoge1xuICAgICAgICBlbmFibGVFbXNnTWV0YWRhdGFDdWVzLFxuICAgICAgICBlbmFibGVJRDNNZXRhZGF0YUN1ZXNcbiAgICAgIH1cbiAgICB9ID0gaGxzO1xuICAgIGlmIChpZDNUcmFjayAmJiAoZW5hYmxlRW1zZ01ldGFkYXRhQ3VlcyB8fCBlbmFibGVJRDNNZXRhZGF0YUN1ZXMpKSB7XG4gICAgICBsZXQgcHJlZGljYXRlO1xuICAgICAgaWYgKHR5cGUgPT09ICdhdWRpbycpIHtcbiAgICAgICAgcHJlZGljYXRlID0gY3VlID0+IGN1ZS50eXBlID09PSBNZXRhZGF0YVNjaGVtYS5hdWRpb0lkMyAmJiBlbmFibGVJRDNNZXRhZGF0YUN1ZXM7XG4gICAgICB9IGVsc2UgaWYgKHR5cGUgPT09ICd2aWRlbycpIHtcbiAgICAgICAgcHJlZGljYXRlID0gY3VlID0+IGN1ZS50eXBlID09PSBNZXRhZGF0YVNjaGVtYS5lbXNnICYmIGVuYWJsZUVtc2dNZXRhZGF0YUN1ZXM7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBwcmVkaWNhdGUgPSBjdWUgPT4gY3VlLnR5cGUgPT09IE1ldGFkYXRhU2NoZW1hLmF1ZGlvSWQzICYmIGVuYWJsZUlEM01ldGFkYXRhQ3VlcyB8fCBjdWUudHlwZSA9PT0gTWV0YWRhdGFTY2hlbWEuZW1zZyAmJiBlbmFibGVFbXNnTWV0YWRhdGFDdWVzO1xuICAgICAgfVxuICAgICAgcmVtb3ZlQ3Vlc0luUmFuZ2UoaWQzVHJhY2ssIHN0YXJ0T2Zmc2V0LCBlbmRPZmZzZXQsIHByZWRpY2F0ZSk7XG4gICAgfVxuICB9XG4gIG9uTGV2ZWxVcGRhdGVkKGV2ZW50LCB7XG4gICAgZGV0YWlsc1xuICB9KSB7XG4gICAgaWYgKCF0aGlzLm1lZGlhIHx8ICFkZXRhaWxzLmhhc1Byb2dyYW1EYXRlVGltZSB8fCAhdGhpcy5obHMuY29uZmlnLmVuYWJsZURhdGVSYW5nZU1ldGFkYXRhQ3Vlcykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB7XG4gICAgICBkYXRlUmFuZ2VDdWVzQXBwZW5kZWQsXG4gICAgICBpZDNUcmFja1xuICAgIH0gPSB0aGlzO1xuICAgIGNvbnN0IHtcbiAgICAgIGRhdGVSYW5nZXNcbiAgICB9ID0gZGV0YWlscztcbiAgICBjb25zdCBpZHMgPSBPYmplY3Qua2V5cyhkYXRlUmFuZ2VzKTtcbiAgICAvLyBSZW1vdmUgY3VlcyBmcm9tIHRyYWNrIG5vdCBmb3VuZCBpbiBkZXRhaWxzLmRhdGVSYW5nZXNcbiAgICBpZiAoaWQzVHJhY2spIHtcbiAgICAgIGNvbnN0IGlkc1RvUmVtb3ZlID0gT2JqZWN0LmtleXMoZGF0ZVJhbmdlQ3Vlc0FwcGVuZGVkKS5maWx0ZXIoaWQgPT4gIWlkcy5pbmNsdWRlcyhpZCkpO1xuICAgICAgZm9yIChsZXQgaSA9IGlkc1RvUmVtb3ZlLmxlbmd0aDsgaS0tOykge1xuICAgICAgICBjb25zdCBpZCA9IGlkc1RvUmVtb3ZlW2ldO1xuICAgICAgICBPYmplY3Qua2V5cyhkYXRlUmFuZ2VDdWVzQXBwZW5kZWRbaWRdLmN1ZXMpLmZvckVhY2goa2V5ID0+IHtcbiAgICAgICAgICBpZDNUcmFjay5yZW1vdmVDdWUoZGF0ZVJhbmdlQ3Vlc0FwcGVuZGVkW2lkXS5jdWVzW2tleV0pO1xuICAgICAgICB9KTtcbiAgICAgICAgZGVsZXRlIGRhdGVSYW5nZUN1ZXNBcHBlbmRlZFtpZF07XG4gICAgICB9XG4gICAgfVxuICAgIC8vIEV4aXQgaWYgdGhlIHBsYXlsaXN0IGRvZXMgbm90IGhhdmUgRGF0ZSBSYW5nZXMgb3IgZG9lcyBub3QgaGF2ZSBQcm9ncmFtIERhdGUgVGltZVxuICAgIGNvbnN0IGxhc3RGcmFnbWVudCA9IGRldGFpbHMuZnJhZ21lbnRzW2RldGFpbHMuZnJhZ21lbnRzLmxlbmd0aCAtIDFdO1xuICAgIGlmIChpZHMubGVuZ3RoID09PSAwIHx8ICFpc0Zpbml0ZU51bWJlcihsYXN0RnJhZ21lbnQgPT0gbnVsbCA/IHZvaWQgMCA6IGxhc3RGcmFnbWVudC5wcm9ncmFtRGF0ZVRpbWUpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmICghdGhpcy5pZDNUcmFjaykge1xuICAgICAgdGhpcy5pZDNUcmFjayA9IHRoaXMuY3JlYXRlVHJhY2sodGhpcy5tZWRpYSk7XG4gICAgfVxuICAgIGNvbnN0IGRhdGVUaW1lT2Zmc2V0ID0gbGFzdEZyYWdtZW50LnByb2dyYW1EYXRlVGltZSAvIDEwMDAgLSBsYXN0RnJhZ21lbnQuc3RhcnQ7XG4gICAgY29uc3QgQ3VlID0gZ2V0Q3VlQ2xhc3MoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGlkcy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgaWQgPSBpZHNbaV07XG4gICAgICBjb25zdCBkYXRlUmFuZ2UgPSBkYXRlUmFuZ2VzW2lkXTtcbiAgICAgIGNvbnN0IHN0YXJ0VGltZSA9IGRhdGVSYW5nZURhdGVUb1RpbWVsaW5lU2Vjb25kcyhkYXRlUmFuZ2Uuc3RhcnREYXRlLCBkYXRlVGltZU9mZnNldCk7XG5cbiAgICAgIC8vIFByb2Nlc3MgRGF0ZVJhbmdlcyB0byBkZXRlcm1pbmUgZW5kLXRpbWUgKGtub3duIERVUkFUSU9OLCBFTkQtREFURSwgb3IgRU5ELU9OLU5FWFQpXG4gICAgICBjb25zdCBhcHBlbmRlZERhdGVSYW5nZUN1ZXMgPSBkYXRlUmFuZ2VDdWVzQXBwZW5kZWRbaWRdO1xuICAgICAgY29uc3QgY3VlcyA9IChhcHBlbmRlZERhdGVSYW5nZUN1ZXMgPT0gbnVsbCA/IHZvaWQgMCA6IGFwcGVuZGVkRGF0ZVJhbmdlQ3Vlcy5jdWVzKSB8fCB7fTtcbiAgICAgIGxldCBkdXJhdGlvbktub3duID0gKGFwcGVuZGVkRGF0ZVJhbmdlQ3VlcyA9PSBudWxsID8gdm9pZCAwIDogYXBwZW5kZWREYXRlUmFuZ2VDdWVzLmR1cmF0aW9uS25vd24pIHx8IGZhbHNlO1xuICAgICAgbGV0IGVuZFRpbWUgPSBNQVhfQ1VFX0VORFRJTUU7XG4gICAgICBjb25zdCBlbmREYXRlID0gZGF0ZVJhbmdlLmVuZERhdGU7XG4gICAgICBpZiAoZW5kRGF0ZSkge1xuICAgICAgICBlbmRUaW1lID0gZGF0ZVJhbmdlRGF0ZVRvVGltZWxpbmVTZWNvbmRzKGVuZERhdGUsIGRhdGVUaW1lT2Zmc2V0KTtcbiAgICAgICAgZHVyYXRpb25Lbm93biA9IHRydWU7XG4gICAgICB9IGVsc2UgaWYgKGRhdGVSYW5nZS5lbmRPbk5leHQgJiYgIWR1cmF0aW9uS25vd24pIHtcbiAgICAgICAgY29uc3QgbmV4dERhdGVSYW5nZVdpdGhTYW1lQ2xhc3MgPSBpZHMucmVkdWNlKChjYW5kaWRhdGVEYXRlUmFuZ2UsIGlkKSA9PiB7XG4gICAgICAgICAgaWYgKGlkICE9PSBkYXRlUmFuZ2UuaWQpIHtcbiAgICAgICAgICAgIGNvbnN0IG90aGVyRGF0ZVJhbmdlID0gZGF0ZVJhbmdlc1tpZF07XG4gICAgICAgICAgICBpZiAob3RoZXJEYXRlUmFuZ2UuY2xhc3MgPT09IGRhdGVSYW5nZS5jbGFzcyAmJiBvdGhlckRhdGVSYW5nZS5zdGFydERhdGUgPiBkYXRlUmFuZ2Uuc3RhcnREYXRlICYmICghY2FuZGlkYXRlRGF0ZVJhbmdlIHx8IGRhdGVSYW5nZS5zdGFydERhdGUgPCBjYW5kaWRhdGVEYXRlUmFuZ2Uuc3RhcnREYXRlKSkge1xuICAgICAgICAgICAgICByZXR1cm4gb3RoZXJEYXRlUmFuZ2U7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBjYW5kaWRhdGVEYXRlUmFuZ2U7XG4gICAgICAgIH0sIG51bGwpO1xuICAgICAgICBpZiAobmV4dERhdGVSYW5nZVdpdGhTYW1lQ2xhc3MpIHtcbiAgICAgICAgICBlbmRUaW1lID0gZGF0ZVJhbmdlRGF0ZVRvVGltZWxpbmVTZWNvbmRzKG5leHREYXRlUmFuZ2VXaXRoU2FtZUNsYXNzLnN0YXJ0RGF0ZSwgZGF0ZVRpbWVPZmZzZXQpO1xuICAgICAgICAgIGR1cmF0aW9uS25vd24gPSB0cnVlO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIENyZWF0ZSBUZXh0VHJhY2sgQ3VlcyBmb3IgZWFjaCBNZXRhZGF0YUdyb3VwIEl0ZW0gKHNlbGVjdCBEYXRlUmFuZ2UgYXR0cmlidXRlKVxuICAgICAgLy8gVGhpcyBpcyB0byBlbXVsYXRlIFNhZmFyaSBITFMgcGxheWJhY2sgaGFuZGxpbmcgb2YgRGF0ZVJhbmdlIHRhZ3NcbiAgICAgIGNvbnN0IGF0dHJpYnV0ZXMgPSBPYmplY3Qua2V5cyhkYXRlUmFuZ2UuYXR0cik7XG4gICAgICBmb3IgKGxldCBqID0gMDsgaiA8IGF0dHJpYnV0ZXMubGVuZ3RoOyBqKyspIHtcbiAgICAgICAgY29uc3Qga2V5ID0gYXR0cmlidXRlc1tqXTtcbiAgICAgICAgaWYgKCFpc0RhdGVSYW5nZUN1ZUF0dHJpYnV0ZShrZXkpKSB7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgY3VlID0gY3Vlc1trZXldO1xuICAgICAgICBpZiAoY3VlKSB7XG4gICAgICAgICAgaWYgKGR1cmF0aW9uS25vd24gJiYgIWFwcGVuZGVkRGF0ZVJhbmdlQ3Vlcy5kdXJhdGlvbktub3duKSB7XG4gICAgICAgICAgICBjdWUuZW5kVGltZSA9IGVuZFRpbWU7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKEN1ZSkge1xuICAgICAgICAgIGxldCBkYXRhID0gZGF0ZVJhbmdlLmF0dHJba2V5XTtcbiAgICAgICAgICBpZiAoaXNTQ1RFMzVBdHRyaWJ1dGUoa2V5KSkge1xuICAgICAgICAgICAgZGF0YSA9IGhleFRvQXJyYXlCdWZmZXIoZGF0YSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnN0IF9jdWUgPSBjcmVhdGVDdWVXaXRoRGF0YUZpZWxkcyhDdWUsIHN0YXJ0VGltZSwgZW5kVGltZSwge1xuICAgICAgICAgICAga2V5LFxuICAgICAgICAgICAgZGF0YVxuICAgICAgICAgIH0sIE1ldGFkYXRhU2NoZW1hLmRhdGVSYW5nZSk7XG4gICAgICAgICAgaWYgKF9jdWUpIHtcbiAgICAgICAgICAgIF9jdWUuaWQgPSBpZDtcbiAgICAgICAgICAgIHRoaXMuaWQzVHJhY2suYWRkQ3VlKF9jdWUpO1xuICAgICAgICAgICAgY3Vlc1trZXldID0gX2N1ZTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gS2VlcCB0cmFjayBvZiBwcm9jZXNzZWQgRGF0ZVJhbmdlcyBieSBJRCBmb3IgdXBkYXRpbmcgY3VlcyB3aXRoIG5ldyBEYXRlUmFuZ2UgdGFnIGF0dHJpYnV0ZXNcbiAgICAgIGRhdGVSYW5nZUN1ZXNBcHBlbmRlZFtpZF0gPSB7XG4gICAgICAgIGN1ZXMsXG4gICAgICAgIGRhdGVSYW5nZSxcbiAgICAgICAgZHVyYXRpb25Lbm93blxuICAgICAgfTtcbiAgICB9XG4gIH1cbn1cblxuY2xhc3MgTGF0ZW5jeUNvbnRyb2xsZXIge1xuICBjb25zdHJ1Y3RvcihobHMpIHtcbiAgICB0aGlzLmhscyA9IHZvaWQgMDtcbiAgICB0aGlzLmNvbmZpZyA9IHZvaWQgMDtcbiAgICB0aGlzLm1lZGlhID0gbnVsbDtcbiAgICB0aGlzLmxldmVsRGV0YWlscyA9IG51bGw7XG4gICAgdGhpcy5jdXJyZW50VGltZSA9IDA7XG4gICAgdGhpcy5zdGFsbENvdW50ID0gMDtcbiAgICB0aGlzLl9sYXRlbmN5ID0gbnVsbDtcbiAgICB0aGlzLnRpbWV1cGRhdGVIYW5kbGVyID0gKCkgPT4gdGhpcy50aW1ldXBkYXRlKCk7XG4gICAgdGhpcy5obHMgPSBobHM7XG4gICAgdGhpcy5jb25maWcgPSBobHMuY29uZmlnO1xuICAgIHRoaXMucmVnaXN0ZXJMaXN0ZW5lcnMoKTtcbiAgfVxuICBnZXQgbGF0ZW5jeSgpIHtcbiAgICByZXR1cm4gdGhpcy5fbGF0ZW5jeSB8fCAwO1xuICB9XG4gIGdldCBtYXhMYXRlbmN5KCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGNvbmZpZyxcbiAgICAgIGxldmVsRGV0YWlsc1xuICAgIH0gPSB0aGlzO1xuICAgIGlmIChjb25maWcubGl2ZU1heExhdGVuY3lEdXJhdGlvbiAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gY29uZmlnLmxpdmVNYXhMYXRlbmN5RHVyYXRpb247XG4gICAgfVxuICAgIHJldHVybiBsZXZlbERldGFpbHMgPyBjb25maWcubGl2ZU1heExhdGVuY3lEdXJhdGlvbkNvdW50ICogbGV2ZWxEZXRhaWxzLnRhcmdldGR1cmF0aW9uIDogMDtcbiAgfVxuICBnZXQgdGFyZ2V0TGF0ZW5jeSgpIHtcbiAgICBjb25zdCB7XG4gICAgICBsZXZlbERldGFpbHNcbiAgICB9ID0gdGhpcztcbiAgICBpZiAobGV2ZWxEZXRhaWxzID09PSBudWxsKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgY29uc3Qge1xuICAgICAgaG9sZEJhY2ssXG4gICAgICBwYXJ0SG9sZEJhY2ssXG4gICAgICB0YXJnZXRkdXJhdGlvblxuICAgIH0gPSBsZXZlbERldGFpbHM7XG4gICAgY29uc3Qge1xuICAgICAgbGl2ZVN5bmNEdXJhdGlvbixcbiAgICAgIGxpdmVTeW5jRHVyYXRpb25Db3VudCxcbiAgICAgIGxvd0xhdGVuY3lNb2RlXG4gICAgfSA9IHRoaXMuY29uZmlnO1xuICAgIGNvbnN0IHVzZXJDb25maWcgPSB0aGlzLmhscy51c2VyQ29uZmlnO1xuICAgIGxldCB0YXJnZXRMYXRlbmN5ID0gbG93TGF0ZW5jeU1vZGUgPyBwYXJ0SG9sZEJhY2sgfHwgaG9sZEJhY2sgOiBob2xkQmFjaztcbiAgICBpZiAodXNlckNvbmZpZy5saXZlU3luY0R1cmF0aW9uIHx8IHVzZXJDb25maWcubGl2ZVN5bmNEdXJhdGlvbkNvdW50IHx8IHRhcmdldExhdGVuY3kgPT09IDApIHtcbiAgICAgIHRhcmdldExhdGVuY3kgPSBsaXZlU3luY0R1cmF0aW9uICE9PSB1bmRlZmluZWQgPyBsaXZlU3luY0R1cmF0aW9uIDogbGl2ZVN5bmNEdXJhdGlvbkNvdW50ICogdGFyZ2V0ZHVyYXRpb247XG4gICAgfVxuICAgIGNvbnN0IG1heExpdmVTeW5jT25TdGFsbEluY3JlYXNlID0gdGFyZ2V0ZHVyYXRpb247XG4gICAgY29uc3QgbGl2ZVN5bmNPblN0YWxsSW5jcmVhc2UgPSAxLjA7XG4gICAgcmV0dXJuIHRhcmdldExhdGVuY3kgKyBNYXRoLm1pbih0aGlzLnN0YWxsQ291bnQgKiBsaXZlU3luY09uU3RhbGxJbmNyZWFzZSwgbWF4TGl2ZVN5bmNPblN0YWxsSW5jcmVhc2UpO1xuICB9XG4gIGdldCBsaXZlU3luY1Bvc2l0aW9uKCkge1xuICAgIGNvbnN0IGxpdmVFZGdlID0gdGhpcy5lc3RpbWF0ZUxpdmVFZGdlKCk7XG4gICAgY29uc3QgdGFyZ2V0TGF0ZW5jeSA9IHRoaXMudGFyZ2V0TGF0ZW5jeTtcbiAgICBjb25zdCBsZXZlbERldGFpbHMgPSB0aGlzLmxldmVsRGV0YWlscztcbiAgICBpZiAobGl2ZUVkZ2UgPT09IG51bGwgfHwgdGFyZ2V0TGF0ZW5jeSA9PT0gbnVsbCB8fCBsZXZlbERldGFpbHMgPT09IG51bGwpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICBjb25zdCBlZGdlID0gbGV2ZWxEZXRhaWxzLmVkZ2U7XG4gICAgY29uc3Qgc3luY1Bvc2l0aW9uID0gbGl2ZUVkZ2UgLSB0YXJnZXRMYXRlbmN5IC0gdGhpcy5lZGdlU3RhbGxlZDtcbiAgICBjb25zdCBtaW4gPSBlZGdlIC0gbGV2ZWxEZXRhaWxzLnRvdGFsZHVyYXRpb247XG4gICAgY29uc3QgbWF4ID0gZWRnZSAtICh0aGlzLmNvbmZpZy5sb3dMYXRlbmN5TW9kZSAmJiBsZXZlbERldGFpbHMucGFydFRhcmdldCB8fCBsZXZlbERldGFpbHMudGFyZ2V0ZHVyYXRpb24pO1xuICAgIHJldHVybiBNYXRoLm1pbihNYXRoLm1heChtaW4sIHN5bmNQb3NpdGlvbiksIG1heCk7XG4gIH1cbiAgZ2V0IGRyaWZ0KCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGxldmVsRGV0YWlsc1xuICAgIH0gPSB0aGlzO1xuICAgIGlmIChsZXZlbERldGFpbHMgPT09IG51bGwpIHtcbiAgICAgIHJldHVybiAxO1xuICAgIH1cbiAgICByZXR1cm4gbGV2ZWxEZXRhaWxzLmRyaWZ0O1xuICB9XG4gIGdldCBlZGdlU3RhbGxlZCgpIHtcbiAgICBjb25zdCB7XG4gICAgICBsZXZlbERldGFpbHNcbiAgICB9ID0gdGhpcztcbiAgICBpZiAobGV2ZWxEZXRhaWxzID09PSBudWxsKSB7XG4gICAgICByZXR1cm4gMDtcbiAgICB9XG4gICAgY29uc3QgbWF4TGV2ZWxVcGRhdGVBZ2UgPSAodGhpcy5jb25maWcubG93TGF0ZW5jeU1vZGUgJiYgbGV2ZWxEZXRhaWxzLnBhcnRUYXJnZXQgfHwgbGV2ZWxEZXRhaWxzLnRhcmdldGR1cmF0aW9uKSAqIDM7XG4gICAgcmV0dXJuIE1hdGgubWF4KGxldmVsRGV0YWlscy5hZ2UgLSBtYXhMZXZlbFVwZGF0ZUFnZSwgMCk7XG4gIH1cbiAgZ2V0IGZvcndhcmRCdWZmZXJMZW5ndGgoKSB7XG4gICAgY29uc3Qge1xuICAgICAgbWVkaWEsXG4gICAgICBsZXZlbERldGFpbHNcbiAgICB9ID0gdGhpcztcbiAgICBpZiAoIW1lZGlhIHx8ICFsZXZlbERldGFpbHMpIHtcbiAgICAgIHJldHVybiAwO1xuICAgIH1cbiAgICBjb25zdCBidWZmZXJlZFJhbmdlcyA9IG1lZGlhLmJ1ZmZlcmVkLmxlbmd0aDtcbiAgICByZXR1cm4gKGJ1ZmZlcmVkUmFuZ2VzID8gbWVkaWEuYnVmZmVyZWQuZW5kKGJ1ZmZlcmVkUmFuZ2VzIC0gMSkgOiBsZXZlbERldGFpbHMuZWRnZSkgLSB0aGlzLmN1cnJlbnRUaW1lO1xuICB9XG4gIGRlc3Ryb3koKSB7XG4gICAgdGhpcy51bnJlZ2lzdGVyTGlzdGVuZXJzKCk7XG4gICAgdGhpcy5vbk1lZGlhRGV0YWNoaW5nKCk7XG4gICAgdGhpcy5sZXZlbERldGFpbHMgPSBudWxsO1xuICAgIC8vIEB0cy1pZ25vcmVcbiAgICB0aGlzLmhscyA9IHRoaXMudGltZXVwZGF0ZUhhbmRsZXIgPSBudWxsO1xuICB9XG4gIHJlZ2lzdGVyTGlzdGVuZXJzKCkge1xuICAgIHRoaXMuaGxzLm9uKEV2ZW50cy5NRURJQV9BVFRBQ0hFRCwgdGhpcy5vbk1lZGlhQXR0YWNoZWQsIHRoaXMpO1xuICAgIHRoaXMuaGxzLm9uKEV2ZW50cy5NRURJQV9ERVRBQ0hJTkcsIHRoaXMub25NZWRpYURldGFjaGluZywgdGhpcyk7XG4gICAgdGhpcy5obHMub24oRXZlbnRzLk1BTklGRVNUX0xPQURJTkcsIHRoaXMub25NYW5pZmVzdExvYWRpbmcsIHRoaXMpO1xuICAgIHRoaXMuaGxzLm9uKEV2ZW50cy5MRVZFTF9VUERBVEVELCB0aGlzLm9uTGV2ZWxVcGRhdGVkLCB0aGlzKTtcbiAgICB0aGlzLmhscy5vbihFdmVudHMuRVJST1IsIHRoaXMub25FcnJvciwgdGhpcyk7XG4gIH1cbiAgdW5yZWdpc3Rlckxpc3RlbmVycygpIHtcbiAgICB0aGlzLmhscy5vZmYoRXZlbnRzLk1FRElBX0FUVEFDSEVELCB0aGlzLm9uTWVkaWFBdHRhY2hlZCwgdGhpcyk7XG4gICAgdGhpcy5obHMub2ZmKEV2ZW50cy5NRURJQV9ERVRBQ0hJTkcsIHRoaXMub25NZWRpYURldGFjaGluZywgdGhpcyk7XG4gICAgdGhpcy5obHMub2ZmKEV2ZW50cy5NQU5JRkVTVF9MT0FESU5HLCB0aGlzLm9uTWFuaWZlc3RMb2FkaW5nLCB0aGlzKTtcbiAgICB0aGlzLmhscy5vZmYoRXZlbnRzLkxFVkVMX1VQREFURUQsIHRoaXMub25MZXZlbFVwZGF0ZWQsIHRoaXMpO1xuICAgIHRoaXMuaGxzLm9mZihFdmVudHMuRVJST1IsIHRoaXMub25FcnJvciwgdGhpcyk7XG4gIH1cbiAgb25NZWRpYUF0dGFjaGVkKGV2ZW50LCBkYXRhKSB7XG4gICAgdGhpcy5tZWRpYSA9IGRhdGEubWVkaWE7XG4gICAgdGhpcy5tZWRpYS5hZGRFdmVudExpc3RlbmVyKCd0aW1ldXBkYXRlJywgdGhpcy50aW1ldXBkYXRlSGFuZGxlcik7XG4gIH1cbiAgb25NZWRpYURldGFjaGluZygpIHtcbiAgICBpZiAodGhpcy5tZWRpYSkge1xuICAgICAgdGhpcy5tZWRpYS5yZW1vdmVFdmVudExpc3RlbmVyKCd0aW1ldXBkYXRlJywgdGhpcy50aW1ldXBkYXRlSGFuZGxlcik7XG4gICAgICB0aGlzLm1lZGlhID0gbnVsbDtcbiAgICB9XG4gIH1cbiAgb25NYW5pZmVzdExvYWRpbmcoKSB7XG4gICAgdGhpcy5sZXZlbERldGFpbHMgPSBudWxsO1xuICAgIHRoaXMuX2xhdGVuY3kgPSBudWxsO1xuICAgIHRoaXMuc3RhbGxDb3VudCA9IDA7XG4gIH1cbiAgb25MZXZlbFVwZGF0ZWQoZXZlbnQsIHtcbiAgICBkZXRhaWxzXG4gIH0pIHtcbiAgICB0aGlzLmxldmVsRGV0YWlscyA9IGRldGFpbHM7XG4gICAgaWYgKGRldGFpbHMuYWR2YW5jZWQpIHtcbiAgICAgIHRoaXMudGltZXVwZGF0ZSgpO1xuICAgIH1cbiAgICBpZiAoIWRldGFpbHMubGl2ZSAmJiB0aGlzLm1lZGlhKSB7XG4gICAgICB0aGlzLm1lZGlhLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3RpbWV1cGRhdGUnLCB0aGlzLnRpbWV1cGRhdGVIYW5kbGVyKTtcbiAgICB9XG4gIH1cbiAgb25FcnJvcihldmVudCwgZGF0YSkge1xuICAgIHZhciBfdGhpcyRsZXZlbERldGFpbHM7XG4gICAgaWYgKGRhdGEuZGV0YWlscyAhPT0gRXJyb3JEZXRhaWxzLkJVRkZFUl9TVEFMTEVEX0VSUk9SKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuc3RhbGxDb3VudCsrO1xuICAgIGlmICgoX3RoaXMkbGV2ZWxEZXRhaWxzID0gdGhpcy5sZXZlbERldGFpbHMpICE9IG51bGwgJiYgX3RoaXMkbGV2ZWxEZXRhaWxzLmxpdmUpIHtcbiAgICAgIGxvZ2dlci53YXJuKCdbcGxheWJhY2stcmF0ZS1jb250cm9sbGVyXTogU3RhbGwgZGV0ZWN0ZWQsIGFkanVzdGluZyB0YXJnZXQgbGF0ZW5jeScpO1xuICAgIH1cbiAgfVxuICB0aW1ldXBkYXRlKCkge1xuICAgIGNvbnN0IHtcbiAgICAgIG1lZGlhLFxuICAgICAgbGV2ZWxEZXRhaWxzXG4gICAgfSA9IHRoaXM7XG4gICAgaWYgKCFtZWRpYSB8fCAhbGV2ZWxEZXRhaWxzKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuY3VycmVudFRpbWUgPSBtZWRpYS5jdXJyZW50VGltZTtcbiAgICBjb25zdCBsYXRlbmN5ID0gdGhpcy5jb21wdXRlTGF0ZW5jeSgpO1xuICAgIGlmIChsYXRlbmN5ID09PSBudWxsKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuX2xhdGVuY3kgPSBsYXRlbmN5O1xuXG4gICAgLy8gQWRhcHQgcGxheWJhY2tSYXRlIHRvIG1lZXQgdGFyZ2V0IGxhdGVuY3kgaW4gbG93LWxhdGVuY3kgbW9kZVxuICAgIGNvbnN0IHtcbiAgICAgIGxvd0xhdGVuY3lNb2RlLFxuICAgICAgbWF4TGl2ZVN5bmNQbGF5YmFja1JhdGVcbiAgICB9ID0gdGhpcy5jb25maWc7XG4gICAgaWYgKCFsb3dMYXRlbmN5TW9kZSB8fCBtYXhMaXZlU3luY1BsYXliYWNrUmF0ZSA9PT0gMSB8fCAhbGV2ZWxEZXRhaWxzLmxpdmUpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgdGFyZ2V0TGF0ZW5jeSA9IHRoaXMudGFyZ2V0TGF0ZW5jeTtcbiAgICBpZiAodGFyZ2V0TGF0ZW5jeSA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBkaXN0YW5jZUZyb21UYXJnZXQgPSBsYXRlbmN5IC0gdGFyZ2V0TGF0ZW5jeTtcbiAgICAvLyBPbmx5IGFkanVzdCBwbGF5YmFja1JhdGUgd2hlbiB3aXRoaW4gb25lIHRhcmdldCBkdXJhdGlvbiBvZiB0YXJnZXRMYXRlbmN5XG4gICAgLy8gYW5kIG1vcmUgdGhhbiBvbmUgc2Vjb25kIGZyb20gdW5kZXItYnVmZmVyaW5nLlxuICAgIC8vIFBsYXliYWNrIGZ1cnRoZXIgdGhhbiBvbmUgdGFyZ2V0IGR1cmF0aW9uIGZyb20gdGFyZ2V0IGNhbiBiZSBjb25zaWRlcmVkIERWUiBwbGF5YmFjay5cbiAgICBjb25zdCBsaXZlTWluTGF0ZW5jeUR1cmF0aW9uID0gTWF0aC5taW4odGhpcy5tYXhMYXRlbmN5LCB0YXJnZXRMYXRlbmN5ICsgbGV2ZWxEZXRhaWxzLnRhcmdldGR1cmF0aW9uKTtcbiAgICBjb25zdCBpbkxpdmVSYW5nZSA9IGRpc3RhbmNlRnJvbVRhcmdldCA8IGxpdmVNaW5MYXRlbmN5RHVyYXRpb247XG4gICAgaWYgKGluTGl2ZVJhbmdlICYmIGRpc3RhbmNlRnJvbVRhcmdldCA+IDAuMDUgJiYgdGhpcy5mb3J3YXJkQnVmZmVyTGVuZ3RoID4gMSkge1xuICAgICAgY29uc3QgbWF4ID0gTWF0aC5taW4oMiwgTWF0aC5tYXgoMS4wLCBtYXhMaXZlU3luY1BsYXliYWNrUmF0ZSkpO1xuICAgICAgY29uc3QgcmF0ZSA9IE1hdGgucm91bmQoMiAvICgxICsgTWF0aC5leHAoLTAuNzUgKiBkaXN0YW5jZUZyb21UYXJnZXQgLSB0aGlzLmVkZ2VTdGFsbGVkKSkgKiAyMCkgLyAyMDtcbiAgICAgIG1lZGlhLnBsYXliYWNrUmF0ZSA9IE1hdGgubWluKG1heCwgTWF0aC5tYXgoMSwgcmF0ZSkpO1xuICAgIH0gZWxzZSBpZiAobWVkaWEucGxheWJhY2tSYXRlICE9PSAxICYmIG1lZGlhLnBsYXliYWNrUmF0ZSAhPT0gMCkge1xuICAgICAgbWVkaWEucGxheWJhY2tSYXRlID0gMTtcbiAgICB9XG4gIH1cbiAgZXN0aW1hdGVMaXZlRWRnZSgpIHtcbiAgICBjb25zdCB7XG4gICAgICBsZXZlbERldGFpbHNcbiAgICB9ID0gdGhpcztcbiAgICBpZiAobGV2ZWxEZXRhaWxzID09PSBudWxsKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgcmV0dXJuIGxldmVsRGV0YWlscy5lZGdlICsgbGV2ZWxEZXRhaWxzLmFnZTtcbiAgfVxuICBjb21wdXRlTGF0ZW5jeSgpIHtcbiAgICBjb25zdCBsaXZlRWRnZSA9IHRoaXMuZXN0aW1hdGVMaXZlRWRnZSgpO1xuICAgIGlmIChsaXZlRWRnZSA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIHJldHVybiBsaXZlRWRnZSAtIHRoaXMuY3VycmVudFRpbWU7XG4gIH1cbn1cblxuY29uc3QgSGRjcExldmVscyA9IFsnTk9ORScsICdUWVBFLTAnLCAnVFlQRS0xJywgbnVsbF07XG5mdW5jdGlvbiBpc0hkY3BMZXZlbCh2YWx1ZSkge1xuICByZXR1cm4gSGRjcExldmVscy5pbmRleE9mKHZhbHVlKSA+IC0xO1xufVxuY29uc3QgVmlkZW9SYW5nZVZhbHVlcyA9IFsnU0RSJywgJ1BRJywgJ0hMRyddO1xuZnVuY3Rpb24gaXNWaWRlb1JhbmdlKHZhbHVlKSB7XG4gIHJldHVybiAhIXZhbHVlICYmIFZpZGVvUmFuZ2VWYWx1ZXMuaW5kZXhPZih2YWx1ZSkgPiAtMTtcbn1cbnZhciBIbHNTa2lwID0ge1xuICBObzogXCJcIixcbiAgWWVzOiBcIllFU1wiLFxuICB2MjogXCJ2MlwiXG59O1xuZnVuY3Rpb24gZ2V0U2tpcFZhbHVlKGRldGFpbHMpIHtcbiAgY29uc3Qge1xuICAgIGNhblNraXBVbnRpbCxcbiAgICBjYW5Ta2lwRGF0ZVJhbmdlcyxcbiAgICBhZ2VcbiAgfSA9IGRldGFpbHM7XG4gIC8vIEEgQ2xpZW50IFNIT1VMRCBOT1QgcmVxdWVzdCBhIFBsYXlsaXN0IERlbHRhIFVwZGF0ZSB1bmxlc3MgaXQgYWxyZWFkeVxuICAvLyBoYXMgYSB2ZXJzaW9uIG9mIHRoZSBQbGF5bGlzdCB0aGF0IGlzIG5vIG9sZGVyIHRoYW4gb25lLWhhbGYgb2YgdGhlIFNraXAgQm91bmRhcnkuXG4gIC8vIEBzZWU6IGh0dHBzOi8vZGF0YXRyYWNrZXIuaWV0Zi5vcmcvZG9jL2h0bWwvZHJhZnQtcGFudG9zLWhscy1yZmM4MjE2YmlzI3NlY3Rpb24tNi4zLjdcbiAgY29uc3QgcGxheWxpc3RSZWNlbnRFbm91Z2ggPSBhZ2UgPCBjYW5Ta2lwVW50aWwgLyAyO1xuICBpZiAoY2FuU2tpcFVudGlsICYmIHBsYXlsaXN0UmVjZW50RW5vdWdoKSB7XG4gICAgaWYgKGNhblNraXBEYXRlUmFuZ2VzKSB7XG4gICAgICByZXR1cm4gSGxzU2tpcC52MjtcbiAgICB9XG4gICAgcmV0dXJuIEhsc1NraXAuWWVzO1xuICB9XG4gIHJldHVybiBIbHNTa2lwLk5vO1xufVxuY2xhc3MgSGxzVXJsUGFyYW1ldGVycyB7XG4gIGNvbnN0cnVjdG9yKG1zbiwgcGFydCwgc2tpcCkge1xuICAgIHRoaXMubXNuID0gdm9pZCAwO1xuICAgIHRoaXMucGFydCA9IHZvaWQgMDtcbiAgICB0aGlzLnNraXAgPSB2b2lkIDA7XG4gICAgdGhpcy5tc24gPSBtc247XG4gICAgdGhpcy5wYXJ0ID0gcGFydDtcbiAgICB0aGlzLnNraXAgPSBza2lwO1xuICB9XG4gIGFkZERpcmVjdGl2ZXModXJpKSB7XG4gICAgY29uc3QgdXJsID0gbmV3IHNlbGYuVVJMKHVyaSk7XG4gICAgaWYgKHRoaXMubXNuICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHVybC5zZWFyY2hQYXJhbXMuc2V0KCdfSExTX21zbicsIHRoaXMubXNuLnRvU3RyaW5nKCkpO1xuICAgIH1cbiAgICBpZiAodGhpcy5wYXJ0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHVybC5zZWFyY2hQYXJhbXMuc2V0KCdfSExTX3BhcnQnLCB0aGlzLnBhcnQudG9TdHJpbmcoKSk7XG4gICAgfVxuICAgIGlmICh0aGlzLnNraXApIHtcbiAgICAgIHVybC5zZWFyY2hQYXJhbXMuc2V0KCdfSExTX3NraXAnLCB0aGlzLnNraXApO1xuICAgIH1cbiAgICByZXR1cm4gdXJsLmhyZWY7XG4gIH1cbn1cbmNsYXNzIExldmVsIHtcbiAgY29uc3RydWN0b3IoZGF0YSkge1xuICAgIHRoaXMuX2F0dHJzID0gdm9pZCAwO1xuICAgIHRoaXMuYXVkaW9Db2RlYyA9IHZvaWQgMDtcbiAgICB0aGlzLmJpdHJhdGUgPSB2b2lkIDA7XG4gICAgdGhpcy5jb2RlY1NldCA9IHZvaWQgMDtcbiAgICB0aGlzLnVybCA9IHZvaWQgMDtcbiAgICB0aGlzLmZyYW1lUmF0ZSA9IHZvaWQgMDtcbiAgICB0aGlzLmhlaWdodCA9IHZvaWQgMDtcbiAgICB0aGlzLmlkID0gdm9pZCAwO1xuICAgIHRoaXMubmFtZSA9IHZvaWQgMDtcbiAgICB0aGlzLnZpZGVvQ29kZWMgPSB2b2lkIDA7XG4gICAgdGhpcy53aWR0aCA9IHZvaWQgMDtcbiAgICB0aGlzLmRldGFpbHMgPSB2b2lkIDA7XG4gICAgdGhpcy5mcmFnbWVudEVycm9yID0gMDtcbiAgICB0aGlzLmxvYWRFcnJvciA9IDA7XG4gICAgdGhpcy5sb2FkZWQgPSB2b2lkIDA7XG4gICAgdGhpcy5yZWFsQml0cmF0ZSA9IDA7XG4gICAgdGhpcy5zdXBwb3J0ZWRQcm9taXNlID0gdm9pZCAwO1xuICAgIHRoaXMuc3VwcG9ydGVkUmVzdWx0ID0gdm9pZCAwO1xuICAgIHRoaXMuX2F2Z0JpdHJhdGUgPSAwO1xuICAgIHRoaXMuX2F1ZGlvR3JvdXBzID0gdm9pZCAwO1xuICAgIHRoaXMuX3N1YnRpdGxlR3JvdXBzID0gdm9pZCAwO1xuICAgIC8vIERlcHJlY2F0ZWQgKHJldGFpbmVkIGZvciBiYWNrd2FyZHMgY29tcGF0aWJpbGl0eSlcbiAgICB0aGlzLl91cmxJZCA9IDA7XG4gICAgdGhpcy51cmwgPSBbZGF0YS51cmxdO1xuICAgIHRoaXMuX2F0dHJzID0gW2RhdGEuYXR0cnNdO1xuICAgIHRoaXMuYml0cmF0ZSA9IGRhdGEuYml0cmF0ZTtcbiAgICBpZiAoZGF0YS5kZXRhaWxzKSB7XG4gICAgICB0aGlzLmRldGFpbHMgPSBkYXRhLmRldGFpbHM7XG4gICAgfVxuICAgIHRoaXMuaWQgPSBkYXRhLmlkIHx8IDA7XG4gICAgdGhpcy5uYW1lID0gZGF0YS5uYW1lO1xuICAgIHRoaXMud2lkdGggPSBkYXRhLndpZHRoIHx8IDA7XG4gICAgdGhpcy5oZWlnaHQgPSBkYXRhLmhlaWdodCB8fCAwO1xuICAgIHRoaXMuZnJhbWVSYXRlID0gZGF0YS5hdHRycy5vcHRpb25hbEZsb2F0KCdGUkFNRS1SQVRFJywgMCk7XG4gICAgdGhpcy5fYXZnQml0cmF0ZSA9IGRhdGEuYXR0cnMuZGVjaW1hbEludGVnZXIoJ0FWRVJBR0UtQkFORFdJRFRIJyk7XG4gICAgdGhpcy5hdWRpb0NvZGVjID0gZGF0YS5hdWRpb0NvZGVjO1xuICAgIHRoaXMudmlkZW9Db2RlYyA9IGRhdGEudmlkZW9Db2RlYztcbiAgICB0aGlzLmNvZGVjU2V0ID0gW2RhdGEudmlkZW9Db2RlYywgZGF0YS5hdWRpb0NvZGVjXS5maWx0ZXIoYyA9PiAhIWMpLm1hcChzID0+IHMuc3Vic3RyaW5nKDAsIDQpKS5qb2luKCcsJyk7XG4gICAgdGhpcy5hZGRHcm91cElkKCdhdWRpbycsIGRhdGEuYXR0cnMuQVVESU8pO1xuICAgIHRoaXMuYWRkR3JvdXBJZCgndGV4dCcsIGRhdGEuYXR0cnMuU1VCVElUTEVTKTtcbiAgfVxuICBnZXQgbWF4Qml0cmF0ZSgpIHtcbiAgICByZXR1cm4gTWF0aC5tYXgodGhpcy5yZWFsQml0cmF0ZSwgdGhpcy5iaXRyYXRlKTtcbiAgfVxuICBnZXQgYXZlcmFnZUJpdHJhdGUoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2F2Z0JpdHJhdGUgfHwgdGhpcy5yZWFsQml0cmF0ZSB8fCB0aGlzLmJpdHJhdGU7XG4gIH1cbiAgZ2V0IGF0dHJzKCkge1xuICAgIHJldHVybiB0aGlzLl9hdHRyc1swXTtcbiAgfVxuICBnZXQgY29kZWNzKCkge1xuICAgIHJldHVybiB0aGlzLmF0dHJzLkNPREVDUyB8fCAnJztcbiAgfVxuICBnZXQgcGF0aHdheUlkKCkge1xuICAgIHJldHVybiB0aGlzLmF0dHJzWydQQVRIV0FZLUlEJ10gfHwgJy4nO1xuICB9XG4gIGdldCB2aWRlb1JhbmdlKCkge1xuICAgIHJldHVybiB0aGlzLmF0dHJzWydWSURFTy1SQU5HRSddIHx8ICdTRFInO1xuICB9XG4gIGdldCBzY29yZSgpIHtcbiAgICByZXR1cm4gdGhpcy5hdHRycy5vcHRpb25hbEZsb2F0KCdTQ09SRScsIDApO1xuICB9XG4gIGdldCB1cmkoKSB7XG4gICAgcmV0dXJuIHRoaXMudXJsWzBdIHx8ICcnO1xuICB9XG4gIGhhc0F1ZGlvR3JvdXAoZ3JvdXBJZCkge1xuICAgIHJldHVybiBoYXNHcm91cCh0aGlzLl9hdWRpb0dyb3VwcywgZ3JvdXBJZCk7XG4gIH1cbiAgaGFzU3VidGl0bGVHcm91cChncm91cElkKSB7XG4gICAgcmV0dXJuIGhhc0dyb3VwKHRoaXMuX3N1YnRpdGxlR3JvdXBzLCBncm91cElkKTtcbiAgfVxuICBnZXQgYXVkaW9Hcm91cHMoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2F1ZGlvR3JvdXBzO1xuICB9XG4gIGdldCBzdWJ0aXRsZUdyb3VwcygpIHtcbiAgICByZXR1cm4gdGhpcy5fc3VidGl0bGVHcm91cHM7XG4gIH1cbiAgYWRkR3JvdXBJZCh0eXBlLCBncm91cElkKSB7XG4gICAgaWYgKCFncm91cElkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmICh0eXBlID09PSAnYXVkaW8nKSB7XG4gICAgICBsZXQgYXVkaW9Hcm91cHMgPSB0aGlzLl9hdWRpb0dyb3VwcztcbiAgICAgIGlmICghYXVkaW9Hcm91cHMpIHtcbiAgICAgICAgYXVkaW9Hcm91cHMgPSB0aGlzLl9hdWRpb0dyb3VwcyA9IFtdO1xuICAgICAgfVxuICAgICAgaWYgKGF1ZGlvR3JvdXBzLmluZGV4T2YoZ3JvdXBJZCkgPT09IC0xKSB7XG4gICAgICAgIGF1ZGlvR3JvdXBzLnB1c2goZ3JvdXBJZCk7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmICh0eXBlID09PSAndGV4dCcpIHtcbiAgICAgIGxldCBzdWJ0aXRsZUdyb3VwcyA9IHRoaXMuX3N1YnRpdGxlR3JvdXBzO1xuICAgICAgaWYgKCFzdWJ0aXRsZUdyb3Vwcykge1xuICAgICAgICBzdWJ0aXRsZUdyb3VwcyA9IHRoaXMuX3N1YnRpdGxlR3JvdXBzID0gW107XG4gICAgICB9XG4gICAgICBpZiAoc3VidGl0bGVHcm91cHMuaW5kZXhPZihncm91cElkKSA9PT0gLTEpIHtcbiAgICAgICAgc3VidGl0bGVHcm91cHMucHVzaChncm91cElkKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBEZXByZWNhdGVkIG1ldGhvZHMgKHJldGFpbmVkIGZvciBiYWNrd2FyZHMgY29tcGF0aWJpbGl0eSlcbiAgZ2V0IHVybElkKCkge1xuICAgIHJldHVybiAwO1xuICB9XG4gIHNldCB1cmxJZCh2YWx1ZSkge31cbiAgZ2V0IGF1ZGlvR3JvdXBJZHMoKSB7XG4gICAgcmV0dXJuIHRoaXMuYXVkaW9Hcm91cHMgPyBbdGhpcy5hdWRpb0dyb3VwSWRdIDogdW5kZWZpbmVkO1xuICB9XG4gIGdldCB0ZXh0R3JvdXBJZHMoKSB7XG4gICAgcmV0dXJuIHRoaXMuc3VidGl0bGVHcm91cHMgPyBbdGhpcy50ZXh0R3JvdXBJZF0gOiB1bmRlZmluZWQ7XG4gIH1cbiAgZ2V0IGF1ZGlvR3JvdXBJZCgpIHtcbiAgICB2YXIgX3RoaXMkYXVkaW9Hcm91cHM7XG4gICAgcmV0dXJuIChfdGhpcyRhdWRpb0dyb3VwcyA9IHRoaXMuYXVkaW9Hcm91cHMpID09IG51bGwgPyB2b2lkIDAgOiBfdGhpcyRhdWRpb0dyb3Vwc1swXTtcbiAgfVxuICBnZXQgdGV4dEdyb3VwSWQoKSB7XG4gICAgdmFyIF90aGlzJHN1YnRpdGxlR3JvdXBzO1xuICAgIHJldHVybiAoX3RoaXMkc3VidGl0bGVHcm91cHMgPSB0aGlzLnN1YnRpdGxlR3JvdXBzKSA9PSBudWxsID8gdm9pZCAwIDogX3RoaXMkc3VidGl0bGVHcm91cHNbMF07XG4gIH1cbiAgYWRkRmFsbGJhY2soKSB7fVxufVxuZnVuY3Rpb24gaGFzR3JvdXAoZ3JvdXBzLCBncm91cElkKSB7XG4gIGlmICghZ3JvdXBJZCB8fCAhZ3JvdXBzKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIHJldHVybiBncm91cHMuaW5kZXhPZihncm91cElkKSAhPT0gLTE7XG59XG5cbmZ1bmN0aW9uIHVwZGF0ZUZyb21Ub1BUUyhmcmFnRnJvbSwgZnJhZ1RvKSB7XG4gIGNvbnN0IGZyYWdUb1BUUyA9IGZyYWdUby5zdGFydFBUUztcbiAgLy8gaWYgd2Uga25vdyBzdGFydFBUU1t0b0lkeF1cbiAgaWYgKGlzRmluaXRlTnVtYmVyKGZyYWdUb1BUUykpIHtcbiAgICAvLyB1cGRhdGUgZnJhZ21lbnQgZHVyYXRpb24uXG4gICAgLy8gaXQgaGVscHMgdG8gZml4IGRyaWZ0cyBiZXR3ZWVuIHBsYXlsaXN0IHJlcG9ydGVkIGR1cmF0aW9uIGFuZCBmcmFnbWVudCByZWFsIGR1cmF0aW9uXG4gICAgbGV0IGR1cmF0aW9uID0gMDtcbiAgICBsZXQgZnJhZztcbiAgICBpZiAoZnJhZ1RvLnNuID4gZnJhZ0Zyb20uc24pIHtcbiAgICAgIGR1cmF0aW9uID0gZnJhZ1RvUFRTIC0gZnJhZ0Zyb20uc3RhcnQ7XG4gICAgICBmcmFnID0gZnJhZ0Zyb207XG4gICAgfSBlbHNlIHtcbiAgICAgIGR1cmF0aW9uID0gZnJhZ0Zyb20uc3RhcnQgLSBmcmFnVG9QVFM7XG4gICAgICBmcmFnID0gZnJhZ1RvO1xuICAgIH1cbiAgICBpZiAoZnJhZy5kdXJhdGlvbiAhPT0gZHVyYXRpb24pIHtcbiAgICAgIGZyYWcuZHVyYXRpb24gPSBkdXJhdGlvbjtcbiAgICB9XG4gICAgLy8gd2UgZG9udCBrbm93IHN0YXJ0UFRTW3RvSWR4XVxuICB9IGVsc2UgaWYgKGZyYWdUby5zbiA+IGZyYWdGcm9tLnNuKSB7XG4gICAgY29uc3QgY29udGlndW91cyA9IGZyYWdGcm9tLmNjID09PSBmcmFnVG8uY2M7XG4gICAgLy8gVE9ETzogV2l0aCBwYXJ0LWxvYWRpbmcgZW5kL2R1cmF0aW9ucyB3ZSBuZWVkIHRvIGNvbmZpcm0gdGhlIHdob2xlIGZyYWdtZW50IGlzIGxvYWRlZCBiZWZvcmUgdXNpbmcgKG9yIHNldHRpbmcpIG1pbkVuZFBUU1xuICAgIGlmIChjb250aWd1b3VzICYmIGZyYWdGcm9tLm1pbkVuZFBUUykge1xuICAgICAgZnJhZ1RvLnN0YXJ0ID0gZnJhZ0Zyb20uc3RhcnQgKyAoZnJhZ0Zyb20ubWluRW5kUFRTIC0gZnJhZ0Zyb20uc3RhcnQpO1xuICAgIH0gZWxzZSB7XG4gICAgICBmcmFnVG8uc3RhcnQgPSBmcmFnRnJvbS5zdGFydCArIGZyYWdGcm9tLmR1cmF0aW9uO1xuICAgIH1cbiAgfSBlbHNlIHtcbiAgICBmcmFnVG8uc3RhcnQgPSBNYXRoLm1heChmcmFnRnJvbS5zdGFydCAtIGZyYWdUby5kdXJhdGlvbiwgMCk7XG4gIH1cbn1cbmZ1bmN0aW9uIHVwZGF0ZUZyYWdQVFNEVFMoZGV0YWlscywgZnJhZywgc3RhcnRQVFMsIGVuZFBUUywgc3RhcnREVFMsIGVuZERUUykge1xuICBjb25zdCBwYXJzZWRNZWRpYUR1cmF0aW9uID0gZW5kUFRTIC0gc3RhcnRQVFM7XG4gIGlmIChwYXJzZWRNZWRpYUR1cmF0aW9uIDw9IDApIHtcbiAgICBsb2dnZXIud2FybignRnJhZ21lbnQgc2hvdWxkIGhhdmUgYSBwb3NpdGl2ZSBkdXJhdGlvbicsIGZyYWcpO1xuICAgIGVuZFBUUyA9IHN0YXJ0UFRTICsgZnJhZy5kdXJhdGlvbjtcbiAgICBlbmREVFMgPSBzdGFydERUUyArIGZyYWcuZHVyYXRpb247XG4gIH1cbiAgbGV0IG1heFN0YXJ0UFRTID0gc3RhcnRQVFM7XG4gIGxldCBtaW5FbmRQVFMgPSBlbmRQVFM7XG4gIGNvbnN0IGZyYWdTdGFydFB0cyA9IGZyYWcuc3RhcnRQVFM7XG4gIGNvbnN0IGZyYWdFbmRQdHMgPSBmcmFnLmVuZFBUUztcbiAgaWYgKGlzRmluaXRlTnVtYmVyKGZyYWdTdGFydFB0cykpIHtcbiAgICAvLyBkZWx0YSBQVFMgYmV0d2VlbiBhdWRpbyBhbmQgdmlkZW9cbiAgICBjb25zdCBkZWx0YVBUUyA9IE1hdGguYWJzKGZyYWdTdGFydFB0cyAtIHN0YXJ0UFRTKTtcbiAgICBpZiAoIWlzRmluaXRlTnVtYmVyKGZyYWcuZGVsdGFQVFMpKSB7XG4gICAgICBmcmFnLmRlbHRhUFRTID0gZGVsdGFQVFM7XG4gICAgfSBlbHNlIHtcbiAgICAgIGZyYWcuZGVsdGFQVFMgPSBNYXRoLm1heChkZWx0YVBUUywgZnJhZy5kZWx0YVBUUyk7XG4gICAgfVxuICAgIG1heFN0YXJ0UFRTID0gTWF0aC5tYXgoc3RhcnRQVFMsIGZyYWdTdGFydFB0cyk7XG4gICAgc3RhcnRQVFMgPSBNYXRoLm1pbihzdGFydFBUUywgZnJhZ1N0YXJ0UHRzKTtcbiAgICBzdGFydERUUyA9IE1hdGgubWluKHN0YXJ0RFRTLCBmcmFnLnN0YXJ0RFRTKTtcbiAgICBtaW5FbmRQVFMgPSBNYXRoLm1pbihlbmRQVFMsIGZyYWdFbmRQdHMpO1xuICAgIGVuZFBUUyA9IE1hdGgubWF4KGVuZFBUUywgZnJhZ0VuZFB0cyk7XG4gICAgZW5kRFRTID0gTWF0aC5tYXgoZW5kRFRTLCBmcmFnLmVuZERUUyk7XG4gIH1cbiAgY29uc3QgZHJpZnQgPSBzdGFydFBUUyAtIGZyYWcuc3RhcnQ7XG4gIGlmIChmcmFnLnN0YXJ0ICE9PSAwKSB7XG4gICAgZnJhZy5zdGFydCA9IHN0YXJ0UFRTO1xuICB9XG4gIGZyYWcuZHVyYXRpb24gPSBlbmRQVFMgLSBmcmFnLnN0YXJ0O1xuICBmcmFnLnN0YXJ0UFRTID0gc3RhcnRQVFM7XG4gIGZyYWcubWF4U3RhcnRQVFMgPSBtYXhTdGFydFBUUztcbiAgZnJhZy5zdGFydERUUyA9IHN0YXJ0RFRTO1xuICBmcmFnLmVuZFBUUyA9IGVuZFBUUztcbiAgZnJhZy5taW5FbmRQVFMgPSBtaW5FbmRQVFM7XG4gIGZyYWcuZW5kRFRTID0gZW5kRFRTO1xuICBjb25zdCBzbiA9IGZyYWcuc247IC8vICdpbml0U2VnbWVudCdcbiAgLy8gZXhpdCBpZiBzbiBvdXQgb2YgcmFuZ2VcbiAgaWYgKCFkZXRhaWxzIHx8IHNuIDwgZGV0YWlscy5zdGFydFNOIHx8IHNuID4gZGV0YWlscy5lbmRTTikge1xuICAgIHJldHVybiAwO1xuICB9XG4gIGxldCBpO1xuICBjb25zdCBmcmFnSWR4ID0gc24gLSBkZXRhaWxzLnN0YXJ0U047XG4gIGNvbnN0IGZyYWdtZW50cyA9IGRldGFpbHMuZnJhZ21lbnRzO1xuICAvLyB1cGRhdGUgZnJhZyByZWZlcmVuY2UgaW4gZnJhZ21lbnRzIGFycmF5XG4gIC8vIHJhdGlvbmFsZSBpcyB0aGF0IGZyYWdtZW50cyBhcnJheSBtaWdodCBub3QgY29udGFpbiB0aGlzIGZyYWcgb2JqZWN0LlxuICAvLyB0aGlzIHdpbGwgaGFwcGVuIGlmIHBsYXlsaXN0IGhhcyBiZWVuIHJlZnJlc2hlZCBiZXR3ZWVuIGZyYWcgbG9hZGluZyBhbmQgY2FsbCB0byB1cGRhdGVGcmFnUFRTRFRTKClcbiAgLy8gaWYgd2UgZG9uJ3QgdXBkYXRlIGZyYWcsIHdlIHdvbid0IGJlIGFibGUgdG8gcHJvcGFnYXRlIFBUUyBpbmZvIG9uIHRoZSBwbGF5bGlzdFxuICAvLyByZXN1bHRpbmcgaW4gaW52YWxpZCBzbGlkaW5nIGNvbXB1dGF0aW9uXG4gIGZyYWdtZW50c1tmcmFnSWR4XSA9IGZyYWc7XG4gIC8vIGFkanVzdCBmcmFnbWVudCBQVFMvZHVyYXRpb24gZnJvbSBzZXFudW0tMSB0byBmcmFnIDBcbiAgZm9yIChpID0gZnJhZ0lkeDsgaSA+IDA7IGktLSkge1xuICAgIHVwZGF0ZUZyb21Ub1BUUyhmcmFnbWVudHNbaV0sIGZyYWdtZW50c1tpIC0gMV0pO1xuICB9XG5cbiAgLy8gYWRqdXN0IGZyYWdtZW50IFBUUy9kdXJhdGlvbiBmcm9tIHNlcW51bSB0byBsYXN0IGZyYWdcbiAgZm9yIChpID0gZnJhZ0lkeDsgaSA8IGZyYWdtZW50cy5sZW5ndGggLSAxOyBpKyspIHtcbiAgICB1cGRhdGVGcm9tVG9QVFMoZnJhZ21lbnRzW2ldLCBmcmFnbWVudHNbaSArIDFdKTtcbiAgfVxuICBpZiAoZGV0YWlscy5mcmFnbWVudEhpbnQpIHtcbiAgICB1cGRhdGVGcm9tVG9QVFMoZnJhZ21lbnRzW2ZyYWdtZW50cy5sZW5ndGggLSAxXSwgZGV0YWlscy5mcmFnbWVudEhpbnQpO1xuICB9XG4gIGRldGFpbHMuUFRTS25vd24gPSBkZXRhaWxzLmFsaWduZWRTbGlkaW5nID0gdHJ1ZTtcbiAgcmV0dXJuIGRyaWZ0O1xufVxuZnVuY3Rpb24gbWVyZ2VEZXRhaWxzKG9sZERldGFpbHMsIG5ld0RldGFpbHMpIHtcbiAgLy8gVHJhY2sgdGhlIGxhc3QgaW5pdFNlZ21lbnQgcHJvY2Vzc2VkLiBJbml0aWFsaXplIGl0IHRvIHRoZSBsYXN0IG9uZSBvbiB0aGUgdGltZWxpbmUuXG4gIGxldCBjdXJyZW50SW5pdFNlZ21lbnQgPSBudWxsO1xuICBjb25zdCBvbGRGcmFnbWVudHMgPSBvbGREZXRhaWxzLmZyYWdtZW50cztcbiAgZm9yIChsZXQgaSA9IG9sZEZyYWdtZW50cy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xuICAgIGNvbnN0IG9sZEluaXQgPSBvbGRGcmFnbWVudHNbaV0uaW5pdFNlZ21lbnQ7XG4gICAgaWYgKG9sZEluaXQpIHtcbiAgICAgIGN1cnJlbnRJbml0U2VnbWVudCA9IG9sZEluaXQ7XG4gICAgICBicmVhaztcbiAgICB9XG4gIH1cbiAgaWYgKG9sZERldGFpbHMuZnJhZ21lbnRIaW50KSB7XG4gICAgLy8gcHJldmVudCBQVFMgYW5kIGR1cmF0aW9uIGZyb20gYmVpbmcgYWRqdXN0ZWQgb24gdGhlIG5leHQgaGludFxuICAgIGRlbGV0ZSBvbGREZXRhaWxzLmZyYWdtZW50SGludC5lbmRQVFM7XG4gIH1cbiAgLy8gY2hlY2sgaWYgb2xkL25ldyBwbGF5bGlzdHMgaGF2ZSBmcmFnbWVudHMgaW4gY29tbW9uXG4gIC8vIGxvb3AgdGhyb3VnaCBvdmVybGFwcGluZyBTTiBhbmQgdXBkYXRlIHN0YXJ0UFRTICwgY2MsIGFuZCBkdXJhdGlvbiBpZiBhbnkgZm91bmRcbiAgbGV0IGNjT2Zmc2V0ID0gMDtcbiAgbGV0IFBUU0ZyYWc7XG4gIG1hcEZyYWdtZW50SW50ZXJzZWN0aW9uKG9sZERldGFpbHMsIG5ld0RldGFpbHMsIChvbGRGcmFnLCBuZXdGcmFnKSA9PiB7XG4gICAgaWYgKG9sZEZyYWcucmVsdXJsKSB7XG4gICAgICAvLyBEbyBub3QgY29tcGFyZSBDQyBpZiB0aGUgb2xkIGZyYWdtZW50IGhhcyBubyB1cmwuIFRoaXMgaXMgYSBsZXZlbC5mcmFnbWVudEhpbnQgdXNlZCBieSBMTC1ITFMgcGFydHMuXG4gICAgICAvLyBJdCBtYXliZSBiZSBvZmYgYnkgMSBpZiBpdCB3YXMgY3JlYXRlZCBiZWZvcmUgYW55IHBhcnRzIG9yIGRpc2NvbnRpbnVpdHkgdGFncyB3ZXJlIGFwcGVuZGVkIHRvIHRoZSBlbmRcbiAgICAgIC8vIG9mIHRoZSBwbGF5bGlzdC5cbiAgICAgIGNjT2Zmc2V0ID0gb2xkRnJhZy5jYyAtIG5ld0ZyYWcuY2M7XG4gICAgfVxuICAgIGlmIChpc0Zpbml0ZU51bWJlcihvbGRGcmFnLnN0YXJ0UFRTKSAmJiBpc0Zpbml0ZU51bWJlcihvbGRGcmFnLmVuZFBUUykpIHtcbiAgICAgIG5ld0ZyYWcuc3RhcnQgPSBuZXdGcmFnLnN0YXJ0UFRTID0gb2xkRnJhZy5zdGFydFBUUztcbiAgICAgIG5ld0ZyYWcuc3RhcnREVFMgPSBvbGRGcmFnLnN0YXJ0RFRTO1xuICAgICAgbmV3RnJhZy5tYXhTdGFydFBUUyA9IG9sZEZyYWcubWF4U3RhcnRQVFM7XG4gICAgICBuZXdGcmFnLmVuZFBUUyA9IG9sZEZyYWcuZW5kUFRTO1xuICAgICAgbmV3RnJhZy5lbmREVFMgPSBvbGRGcmFnLmVuZERUUztcbiAgICAgIG5ld0ZyYWcubWluRW5kUFRTID0gb2xkRnJhZy5taW5FbmRQVFM7XG4gICAgICBuZXdGcmFnLmR1cmF0aW9uID0gb2xkRnJhZy5lbmRQVFMgLSBvbGRGcmFnLnN0YXJ0UFRTO1xuICAgICAgaWYgKG5ld0ZyYWcuZHVyYXRpb24pIHtcbiAgICAgICAgUFRTRnJhZyA9IG5ld0ZyYWc7XG4gICAgICB9XG5cbiAgICAgIC8vIFBUUyBpcyBrbm93biB3aGVuIGFueSBzZWdtZW50IGhhcyBzdGFydFBUUyBhbmQgZW5kUFRTXG4gICAgICBuZXdEZXRhaWxzLlBUU0tub3duID0gbmV3RGV0YWlscy5hbGlnbmVkU2xpZGluZyA9IHRydWU7XG4gICAgfVxuICAgIG5ld0ZyYWcuZWxlbWVudGFyeVN0cmVhbXMgPSBvbGRGcmFnLmVsZW1lbnRhcnlTdHJlYW1zO1xuICAgIG5ld0ZyYWcubG9hZGVyID0gb2xkRnJhZy5sb2FkZXI7XG4gICAgbmV3RnJhZy5zdGF0cyA9IG9sZEZyYWcuc3RhdHM7XG4gICAgaWYgKG9sZEZyYWcuaW5pdFNlZ21lbnQpIHtcbiAgICAgIG5ld0ZyYWcuaW5pdFNlZ21lbnQgPSBvbGRGcmFnLmluaXRTZWdtZW50O1xuICAgICAgY3VycmVudEluaXRTZWdtZW50ID0gb2xkRnJhZy5pbml0U2VnbWVudDtcbiAgICB9XG4gIH0pO1xuICBpZiAoY3VycmVudEluaXRTZWdtZW50KSB7XG4gICAgY29uc3QgZnJhZ21lbnRzVG9DaGVjayA9IG5ld0RldGFpbHMuZnJhZ21lbnRIaW50ID8gbmV3RGV0YWlscy5mcmFnbWVudHMuY29uY2F0KG5ld0RldGFpbHMuZnJhZ21lbnRIaW50KSA6IG5ld0RldGFpbHMuZnJhZ21lbnRzO1xuICAgIGZyYWdtZW50c1RvQ2hlY2suZm9yRWFjaChmcmFnID0+IHtcbiAgICAgIHZhciBfY3VycmVudEluaXRTZWdtZW50O1xuICAgICAgaWYgKGZyYWcgJiYgKCFmcmFnLmluaXRTZWdtZW50IHx8IGZyYWcuaW5pdFNlZ21lbnQucmVsdXJsID09PSAoKF9jdXJyZW50SW5pdFNlZ21lbnQgPSBjdXJyZW50SW5pdFNlZ21lbnQpID09IG51bGwgPyB2b2lkIDAgOiBfY3VycmVudEluaXRTZWdtZW50LnJlbHVybCkpKSB7XG4gICAgICAgIGZyYWcuaW5pdFNlZ21lbnQgPSBjdXJyZW50SW5pdFNlZ21lbnQ7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbiAgaWYgKG5ld0RldGFpbHMuc2tpcHBlZFNlZ21lbnRzKSB7XG4gICAgbmV3RGV0YWlscy5kZWx0YVVwZGF0ZUZhaWxlZCA9IG5ld0RldGFpbHMuZnJhZ21lbnRzLnNvbWUoZnJhZyA9PiAhZnJhZyk7XG4gICAgaWYgKG5ld0RldGFpbHMuZGVsdGFVcGRhdGVGYWlsZWQpIHtcbiAgICAgIGxvZ2dlci53YXJuKCdbbGV2ZWwtaGVscGVyXSBQcmV2aW91cyBwbGF5bGlzdCBtaXNzaW5nIHNlZ21lbnRzIHNraXBwZWQgaW4gZGVsdGEgcGxheWxpc3QnKTtcbiAgICAgIGZvciAobGV0IGkgPSBuZXdEZXRhaWxzLnNraXBwZWRTZWdtZW50czsgaS0tOykge1xuICAgICAgICBuZXdEZXRhaWxzLmZyYWdtZW50cy5zaGlmdCgpO1xuICAgICAgfVxuICAgICAgbmV3RGV0YWlscy5zdGFydFNOID0gbmV3RGV0YWlscy5mcmFnbWVudHNbMF0uc247XG4gICAgICBuZXdEZXRhaWxzLnN0YXJ0Q0MgPSBuZXdEZXRhaWxzLmZyYWdtZW50c1swXS5jYztcbiAgICB9IGVsc2UgaWYgKG5ld0RldGFpbHMuY2FuU2tpcERhdGVSYW5nZXMpIHtcbiAgICAgIG5ld0RldGFpbHMuZGF0ZVJhbmdlcyA9IG1lcmdlRGF0ZVJhbmdlcyhvbGREZXRhaWxzLmRhdGVSYW5nZXMsIG5ld0RldGFpbHMuZGF0ZVJhbmdlcywgbmV3RGV0YWlscy5yZWNlbnRseVJlbW92ZWREYXRlcmFuZ2VzKTtcbiAgICB9XG4gIH1cbiAgY29uc3QgbmV3RnJhZ21lbnRzID0gbmV3RGV0YWlscy5mcmFnbWVudHM7XG4gIGlmIChjY09mZnNldCkge1xuICAgIGxvZ2dlci53YXJuKCdkaXNjb250aW51aXR5IHNsaWRpbmcgZnJvbSBwbGF5bGlzdCwgdGFrZSBkcmlmdCBpbnRvIGFjY291bnQnKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG5ld0ZyYWdtZW50cy5sZW5ndGg7IGkrKykge1xuICAgICAgbmV3RnJhZ21lbnRzW2ldLmNjICs9IGNjT2Zmc2V0O1xuICAgIH1cbiAgfVxuICBpZiAobmV3RGV0YWlscy5za2lwcGVkU2VnbWVudHMpIHtcbiAgICBuZXdEZXRhaWxzLnN0YXJ0Q0MgPSBuZXdEZXRhaWxzLmZyYWdtZW50c1swXS5jYztcbiAgfVxuXG4gIC8vIE1lcmdlIHBhcnRzXG4gIG1hcFBhcnRJbnRlcnNlY3Rpb24ob2xkRGV0YWlscy5wYXJ0TGlzdCwgbmV3RGV0YWlscy5wYXJ0TGlzdCwgKG9sZFBhcnQsIG5ld1BhcnQpID0+IHtcbiAgICBuZXdQYXJ0LmVsZW1lbnRhcnlTdHJlYW1zID0gb2xkUGFydC5lbGVtZW50YXJ5U3RyZWFtcztcbiAgICBuZXdQYXJ0LnN0YXRzID0gb2xkUGFydC5zdGF0cztcbiAgfSk7XG5cbiAgLy8gaWYgYXQgbGVhc3Qgb25lIGZyYWdtZW50IGNvbnRhaW5zIFBUUyBpbmZvLCByZWNvbXB1dGUgUFRTIGluZm9ybWF0aW9uIGZvciBhbGwgZnJhZ21lbnRzXG4gIGlmIChQVFNGcmFnKSB7XG4gICAgdXBkYXRlRnJhZ1BUU0RUUyhuZXdEZXRhaWxzLCBQVFNGcmFnLCBQVFNGcmFnLnN0YXJ0UFRTLCBQVFNGcmFnLmVuZFBUUywgUFRTRnJhZy5zdGFydERUUywgUFRTRnJhZy5lbmREVFMpO1xuICB9IGVsc2Uge1xuICAgIC8vIGVuc3VyZSB0aGF0IGRlbHRhIGlzIHdpdGhpbiBvbGRGcmFnbWVudHMgcmFuZ2VcbiAgICAvLyBhbHNvIGFkanVzdCBzbGlkaW5nIGluIGNhc2UgZGVsdGEgaXMgMCAod2UgY291bGQgaGF2ZSBvbGQ9WzUwLTYwXSBhbmQgbmV3PW9sZD1bNTAtNjFdKVxuICAgIC8vIGluIHRoYXQgY2FzZSB3ZSBhbHNvIG5lZWQgdG8gYWRqdXN0IHN0YXJ0IG9mZnNldCBvZiBhbGwgZnJhZ21lbnRzXG4gICAgYWRqdXN0U2xpZGluZyhvbGREZXRhaWxzLCBuZXdEZXRhaWxzKTtcbiAgfVxuICBpZiAobmV3RnJhZ21lbnRzLmxlbmd0aCkge1xuICAgIG5ld0RldGFpbHMudG90YWxkdXJhdGlvbiA9IG5ld0RldGFpbHMuZWRnZSAtIG5ld0ZyYWdtZW50c1swXS5zdGFydDtcbiAgfVxuICBuZXdEZXRhaWxzLmRyaWZ0U3RhcnRUaW1lID0gb2xkRGV0YWlscy5kcmlmdFN0YXJ0VGltZTtcbiAgbmV3RGV0YWlscy5kcmlmdFN0YXJ0ID0gb2xkRGV0YWlscy5kcmlmdFN0YXJ0O1xuICBjb25zdCBhZHZhbmNlZERhdGVUaW1lID0gbmV3RGV0YWlscy5hZHZhbmNlZERhdGVUaW1lO1xuICBpZiAobmV3RGV0YWlscy5hZHZhbmNlZCAmJiBhZHZhbmNlZERhdGVUaW1lKSB7XG4gICAgY29uc3QgZWRnZSA9IG5ld0RldGFpbHMuZWRnZTtcbiAgICBpZiAoIW5ld0RldGFpbHMuZHJpZnRTdGFydCkge1xuICAgICAgbmV3RGV0YWlscy5kcmlmdFN0YXJ0VGltZSA9IGFkdmFuY2VkRGF0ZVRpbWU7XG4gICAgICBuZXdEZXRhaWxzLmRyaWZ0U3RhcnQgPSBlZGdlO1xuICAgIH1cbiAgICBuZXdEZXRhaWxzLmRyaWZ0RW5kVGltZSA9IGFkdmFuY2VkRGF0ZVRpbWU7XG4gICAgbmV3RGV0YWlscy5kcmlmdEVuZCA9IGVkZ2U7XG4gIH0gZWxzZSB7XG4gICAgbmV3RGV0YWlscy5kcmlmdEVuZFRpbWUgPSBvbGREZXRhaWxzLmRyaWZ0RW5kVGltZTtcbiAgICBuZXdEZXRhaWxzLmRyaWZ0RW5kID0gb2xkRGV0YWlscy5kcmlmdEVuZDtcbiAgICBuZXdEZXRhaWxzLmFkdmFuY2VkRGF0ZVRpbWUgPSBvbGREZXRhaWxzLmFkdmFuY2VkRGF0ZVRpbWU7XG4gIH1cbn1cbmZ1bmN0aW9uIG1lcmdlRGF0ZVJhbmdlcyhvbGREYXRlUmFuZ2VzLCBkZWx0YURhdGVSYW5nZXMsIHJlY2VudGx5UmVtb3ZlZERhdGVyYW5nZXMpIHtcbiAgY29uc3QgZGF0ZVJhbmdlcyA9IF9leHRlbmRzKHt9LCBvbGREYXRlUmFuZ2VzKTtcbiAgaWYgKHJlY2VudGx5UmVtb3ZlZERhdGVyYW5nZXMpIHtcbiAgICByZWNlbnRseVJlbW92ZWREYXRlcmFuZ2VzLmZvckVhY2goaWQgPT4ge1xuICAgICAgZGVsZXRlIGRhdGVSYW5nZXNbaWRdO1xuICAgIH0pO1xuICB9XG4gIE9iamVjdC5rZXlzKGRlbHRhRGF0ZVJhbmdlcykuZm9yRWFjaChpZCA9PiB7XG4gICAgY29uc3QgZGF0ZVJhbmdlID0gbmV3IERhdGVSYW5nZShkZWx0YURhdGVSYW5nZXNbaWRdLmF0dHIsIGRhdGVSYW5nZXNbaWRdKTtcbiAgICBpZiAoZGF0ZVJhbmdlLmlzVmFsaWQpIHtcbiAgICAgIGRhdGVSYW5nZXNbaWRdID0gZGF0ZVJhbmdlO1xuICAgIH0gZWxzZSB7XG4gICAgICBsb2dnZXIud2FybihgSWdub3JpbmcgaW52YWxpZCBQbGF5bGlzdCBEZWx0YSBVcGRhdGUgREFURVJBTkdFIHRhZzogXCIke0pTT04uc3RyaW5naWZ5KGRlbHRhRGF0ZVJhbmdlc1tpZF0uYXR0cil9XCJgKTtcbiAgICB9XG4gIH0pO1xuICByZXR1cm4gZGF0ZVJhbmdlcztcbn1cbmZ1bmN0aW9uIG1hcFBhcnRJbnRlcnNlY3Rpb24ob2xkUGFydHMsIG5ld1BhcnRzLCBpbnRlcnNlY3Rpb25Gbikge1xuICBpZiAob2xkUGFydHMgJiYgbmV3UGFydHMpIHtcbiAgICBsZXQgZGVsdGEgPSAwO1xuICAgIGZvciAobGV0IGkgPSAwLCBsZW4gPSBvbGRQYXJ0cy5sZW5ndGg7IGkgPD0gbGVuOyBpKyspIHtcbiAgICAgIGNvbnN0IG9sZFBhcnQgPSBvbGRQYXJ0c1tpXTtcbiAgICAgIGNvbnN0IG5ld1BhcnQgPSBuZXdQYXJ0c1tpICsgZGVsdGFdO1xuICAgICAgaWYgKG9sZFBhcnQgJiYgbmV3UGFydCAmJiBvbGRQYXJ0LmluZGV4ID09PSBuZXdQYXJ0LmluZGV4ICYmIG9sZFBhcnQuZnJhZ21lbnQuc24gPT09IG5ld1BhcnQuZnJhZ21lbnQuc24pIHtcbiAgICAgICAgaW50ZXJzZWN0aW9uRm4ob2xkUGFydCwgbmV3UGFydCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBkZWx0YS0tO1xuICAgICAgfVxuICAgIH1cbiAgfVxufVxuZnVuY3Rpb24gbWFwRnJhZ21lbnRJbnRlcnNlY3Rpb24ob2xkRGV0YWlscywgbmV3RGV0YWlscywgaW50ZXJzZWN0aW9uRm4pIHtcbiAgY29uc3Qgc2tpcHBlZFNlZ21lbnRzID0gbmV3RGV0YWlscy5za2lwcGVkU2VnbWVudHM7XG4gIGNvbnN0IHN0YXJ0ID0gTWF0aC5tYXgob2xkRGV0YWlscy5zdGFydFNOLCBuZXdEZXRhaWxzLnN0YXJ0U04pIC0gbmV3RGV0YWlscy5zdGFydFNOO1xuICBjb25zdCBlbmQgPSAob2xkRGV0YWlscy5mcmFnbWVudEhpbnQgPyAxIDogMCkgKyAoc2tpcHBlZFNlZ21lbnRzID8gbmV3RGV0YWlscy5lbmRTTiA6IE1hdGgubWluKG9sZERldGFpbHMuZW5kU04sIG5ld0RldGFpbHMuZW5kU04pKSAtIG5ld0RldGFpbHMuc3RhcnRTTjtcbiAgY29uc3QgZGVsdGEgPSBuZXdEZXRhaWxzLnN0YXJ0U04gLSBvbGREZXRhaWxzLnN0YXJ0U047XG4gIGNvbnN0IG5ld0ZyYWdzID0gbmV3RGV0YWlscy5mcmFnbWVudEhpbnQgPyBuZXdEZXRhaWxzLmZyYWdtZW50cy5jb25jYXQobmV3RGV0YWlscy5mcmFnbWVudEhpbnQpIDogbmV3RGV0YWlscy5mcmFnbWVudHM7XG4gIGNvbnN0IG9sZEZyYWdzID0gb2xkRGV0YWlscy5mcmFnbWVudEhpbnQgPyBvbGREZXRhaWxzLmZyYWdtZW50cy5jb25jYXQob2xkRGV0YWlscy5mcmFnbWVudEhpbnQpIDogb2xkRGV0YWlscy5mcmFnbWVudHM7XG4gIGZvciAobGV0IGkgPSBzdGFydDsgaSA8PSBlbmQ7IGkrKykge1xuICAgIGNvbnN0IG9sZEZyYWcgPSBvbGRGcmFnc1tkZWx0YSArIGldO1xuICAgIGxldCBuZXdGcmFnID0gbmV3RnJhZ3NbaV07XG4gICAgaWYgKHNraXBwZWRTZWdtZW50cyAmJiAhbmV3RnJhZyAmJiBpIDwgc2tpcHBlZFNlZ21lbnRzKSB7XG4gICAgICAvLyBGaWxsIGluIHNraXBwZWQgc2VnbWVudHMgaW4gZGVsdGEgcGxheWxpc3RcbiAgICAgIG5ld0ZyYWcgPSBuZXdEZXRhaWxzLmZyYWdtZW50c1tpXSA9IG9sZEZyYWc7XG4gICAgfVxuICAgIGlmIChvbGRGcmFnICYmIG5ld0ZyYWcpIHtcbiAgICAgIGludGVyc2VjdGlvbkZuKG9sZEZyYWcsIG5ld0ZyYWcpO1xuICAgIH1cbiAgfVxufVxuZnVuY3Rpb24gYWRqdXN0U2xpZGluZyhvbGREZXRhaWxzLCBuZXdEZXRhaWxzKSB7XG4gIGNvbnN0IGRlbHRhID0gbmV3RGV0YWlscy5zdGFydFNOICsgbmV3RGV0YWlscy5za2lwcGVkU2VnbWVudHMgLSBvbGREZXRhaWxzLnN0YXJ0U047XG4gIGNvbnN0IG9sZEZyYWdtZW50cyA9IG9sZERldGFpbHMuZnJhZ21lbnRzO1xuICBpZiAoZGVsdGEgPCAwIHx8IGRlbHRhID49IG9sZEZyYWdtZW50cy5sZW5ndGgpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgYWRkU2xpZGluZyhuZXdEZXRhaWxzLCBvbGRGcmFnbWVudHNbZGVsdGFdLnN0YXJ0KTtcbn1cbmZ1bmN0aW9uIGFkZFNsaWRpbmcoZGV0YWlscywgc3RhcnQpIHtcbiAgaWYgKHN0YXJ0KSB7XG4gICAgY29uc3QgZnJhZ21lbnRzID0gZGV0YWlscy5mcmFnbWVudHM7XG4gICAgZm9yIChsZXQgaSA9IGRldGFpbHMuc2tpcHBlZFNlZ21lbnRzOyBpIDwgZnJhZ21lbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBmcmFnbWVudHNbaV0uc3RhcnQgKz0gc3RhcnQ7XG4gICAgfVxuICAgIGlmIChkZXRhaWxzLmZyYWdtZW50SGludCkge1xuICAgICAgZGV0YWlscy5mcmFnbWVudEhpbnQuc3RhcnQgKz0gc3RhcnQ7XG4gICAgfVxuICB9XG59XG5mdW5jdGlvbiBjb21wdXRlUmVsb2FkSW50ZXJ2YWwobmV3RGV0YWlscywgZGlzdGFuY2VUb0xpdmVFZGdlTXMgPSBJbmZpbml0eSkge1xuICBsZXQgcmVsb2FkSW50ZXJ2YWwgPSAxMDAwICogbmV3RGV0YWlscy50YXJnZXRkdXJhdGlvbjtcbiAgaWYgKG5ld0RldGFpbHMudXBkYXRlZCkge1xuICAgIC8vIFVzZSBsYXN0IHNlZ21lbnQgZHVyYXRpb24gd2hlbiBzaG9ydGVyIHRoYW4gdGFyZ2V0IGR1cmF0aW9uIGFuZCBuZWFyIGxpdmUgZWRnZVxuICAgIGNvbnN0IGZyYWdtZW50cyA9IG5ld0RldGFpbHMuZnJhZ21lbnRzO1xuICAgIGNvbnN0IGxpdmVFZGdlTWF4VGFyZ2V0RHVyYXRpb25zID0gNDtcbiAgICBpZiAoZnJhZ21lbnRzLmxlbmd0aCAmJiByZWxvYWRJbnRlcnZhbCAqIGxpdmVFZGdlTWF4VGFyZ2V0RHVyYXRpb25zID4gZGlzdGFuY2VUb0xpdmVFZGdlTXMpIHtcbiAgICAgIGNvbnN0IGxhc3RTZWdtZW50RHVyYXRpb24gPSBmcmFnbWVudHNbZnJhZ21lbnRzLmxlbmd0aCAtIDFdLmR1cmF0aW9uICogMTAwMDtcbiAgICAgIGlmIChsYXN0U2VnbWVudER1cmF0aW9uIDwgcmVsb2FkSW50ZXJ2YWwpIHtcbiAgICAgICAgcmVsb2FkSW50ZXJ2YWwgPSBsYXN0U2VnbWVudER1cmF0aW9uO1xuICAgICAgfVxuICAgIH1cbiAgfSBlbHNlIHtcbiAgICAvLyBlc3RpbWF0ZSA9ICdtaXNzIGhhbGYgYXZlcmFnZSc7XG4gICAgLy8gZm9sbG93IEhMUyBTcGVjLCBJZiB0aGUgY2xpZW50IHJlbG9hZHMgYSBQbGF5bGlzdCBmaWxlIGFuZCBmaW5kcyB0aGF0IGl0IGhhcyBub3RcbiAgICAvLyBjaGFuZ2VkIHRoZW4gaXQgTVVTVCB3YWl0IGZvciBhIHBlcmlvZCBvZiBvbmUtaGFsZiB0aGUgdGFyZ2V0XG4gICAgLy8gZHVyYXRpb24gYmVmb3JlIHJldHJ5aW5nLlxuICAgIHJlbG9hZEludGVydmFsIC89IDI7XG4gIH1cbiAgcmV0dXJuIE1hdGgucm91bmQocmVsb2FkSW50ZXJ2YWwpO1xufVxuZnVuY3Rpb24gZ2V0RnJhZ21lbnRXaXRoU04obGV2ZWwsIHNuLCBmcmFnQ3VycmVudCkge1xuICBpZiAoIShsZXZlbCAhPSBudWxsICYmIGxldmVsLmRldGFpbHMpKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgY29uc3QgbGV2ZWxEZXRhaWxzID0gbGV2ZWwuZGV0YWlscztcbiAgbGV0IGZyYWdtZW50ID0gbGV2ZWxEZXRhaWxzLmZyYWdtZW50c1tzbiAtIGxldmVsRGV0YWlscy5zdGFydFNOXTtcbiAgaWYgKGZyYWdtZW50KSB7XG4gICAgcmV0dXJuIGZyYWdtZW50O1xuICB9XG4gIGZyYWdtZW50ID0gbGV2ZWxEZXRhaWxzLmZyYWdtZW50SGludDtcbiAgaWYgKGZyYWdtZW50ICYmIGZyYWdtZW50LnNuID09PSBzbikge1xuICAgIHJldHVybiBmcmFnbWVudDtcbiAgfVxuICBpZiAoc24gPCBsZXZlbERldGFpbHMuc3RhcnRTTiAmJiBmcmFnQ3VycmVudCAmJiBmcmFnQ3VycmVudC5zbiA9PT0gc24pIHtcbiAgICByZXR1cm4gZnJhZ0N1cnJlbnQ7XG4gIH1cbiAgcmV0dXJuIG51bGw7XG59XG5mdW5jdGlvbiBnZXRQYXJ0V2l0aChsZXZlbCwgc24sIHBhcnRJbmRleCkge1xuICB2YXIgX2xldmVsJGRldGFpbHM7XG4gIGlmICghKGxldmVsICE9IG51bGwgJiYgbGV2ZWwuZGV0YWlscykpIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICByZXR1cm4gZmluZFBhcnQoKF9sZXZlbCRkZXRhaWxzID0gbGV2ZWwuZGV0YWlscykgPT0gbnVsbCA/IHZvaWQgMCA6IF9sZXZlbCRkZXRhaWxzLnBhcnRMaXN0LCBzbiwgcGFydEluZGV4KTtcbn1cbmZ1bmN0aW9uIGZpbmRQYXJ0KHBhcnRMaXN0LCBzbiwgcGFydEluZGV4KSB7XG4gIGlmIChwYXJ0TGlzdCkge1xuICAgIGZvciAobGV0IGkgPSBwYXJ0TGlzdC5sZW5ndGg7IGktLTspIHtcbiAgICAgIGNvbnN0IHBhcnQgPSBwYXJ0TGlzdFtpXTtcbiAgICAgIGlmIChwYXJ0LmluZGV4ID09PSBwYXJ0SW5kZXggJiYgcGFydC5mcmFnbWVudC5zbiA9PT0gc24pIHtcbiAgICAgICAgcmV0dXJuIHBhcnQ7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIHJldHVybiBudWxsO1xufVxuZnVuY3Rpb24gcmVhc3NpZ25GcmFnbWVudExldmVsSW5kZXhlcyhsZXZlbHMpIHtcbiAgbGV2ZWxzLmZvckVhY2goKGxldmVsLCBpbmRleCkgPT4ge1xuICAgIGNvbnN0IHtcbiAgICAgIGRldGFpbHNcbiAgICB9ID0gbGV2ZWw7XG4gICAgaWYgKGRldGFpbHMgIT0gbnVsbCAmJiBkZXRhaWxzLmZyYWdtZW50cykge1xuICAgICAgZGV0YWlscy5mcmFnbWVudHMuZm9yRWFjaChmcmFnbWVudCA9PiB7XG4gICAgICAgIGZyYWdtZW50LmxldmVsID0gaW5kZXg7XG4gICAgICB9KTtcbiAgICB9XG4gIH0pO1xufVxuXG5mdW5jdGlvbiBpc1RpbWVvdXRFcnJvcihlcnJvcikge1xuICBzd2l0Y2ggKGVycm9yLmRldGFpbHMpIHtcbiAgICBjYXNlIEVycm9yRGV0YWlscy5GUkFHX0xPQURfVElNRU9VVDpcbiAgICBjYXNlIEVycm9yRGV0YWlscy5LRVlfTE9BRF9USU1FT1VUOlxuICAgIGNhc2UgRXJyb3JEZXRhaWxzLkxFVkVMX0xPQURfVElNRU9VVDpcbiAgICBjYXNlIEVycm9yRGV0YWlscy5NQU5JRkVTVF9MT0FEX1RJTUVPVVQ6XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuICByZXR1cm4gZmFsc2U7XG59XG5mdW5jdGlvbiBnZXRSZXRyeUNvbmZpZyhsb2FkUG9saWN5LCBlcnJvcikge1xuICBjb25zdCBpc1RpbWVvdXQgPSBpc1RpbWVvdXRFcnJvcihlcnJvcik7XG4gIHJldHVybiBsb2FkUG9saWN5LmRlZmF1bHRbYCR7aXNUaW1lb3V0ID8gJ3RpbWVvdXQnIDogJ2Vycm9yJ31SZXRyeWBdO1xufVxuZnVuY3Rpb24gZ2V0UmV0cnlEZWxheShyZXRyeUNvbmZpZywgcmV0cnlDb3VudCkge1xuICAvLyBleHBvbmVudGlhbCBiYWNrb2ZmIGNhcHBlZCB0byBtYXggcmV0cnkgZGVsYXlcbiAgY29uc3QgYmFja29mZkZhY3RvciA9IHJldHJ5Q29uZmlnLmJhY2tvZmYgPT09ICdsaW5lYXInID8gMSA6IE1hdGgucG93KDIsIHJldHJ5Q291bnQpO1xuICByZXR1cm4gTWF0aC5taW4oYmFja29mZkZhY3RvciAqIHJldHJ5Q29uZmlnLnJldHJ5RGVsYXlNcywgcmV0cnlDb25maWcubWF4UmV0cnlEZWxheU1zKTtcbn1cbmZ1bmN0aW9uIGdldExvYWRlckNvbmZpZ1dpdGhvdXRSZXRpZXMobG9kZXJDb25maWcpIHtcbiAgcmV0dXJuIF9vYmplY3RTcHJlYWQyKF9vYmplY3RTcHJlYWQyKHt9LCBsb2RlckNvbmZpZyksIHtcbiAgICBlcnJvclJldHJ5OiBudWxsLFxuICAgIHRpbWVvdXRSZXRyeTogbnVsbFxuICB9KTtcbn1cbmZ1bmN0aW9uIHNob3VsZFJldHJ5KHJldHJ5Q29uZmlnLCByZXRyeUNvdW50LCBpc1RpbWVvdXQsIGxvYWRlclJlc3BvbnNlKSB7XG4gIGlmICghcmV0cnlDb25maWcpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgY29uc3QgaHR0cFN0YXR1cyA9IGxvYWRlclJlc3BvbnNlID09IG51bGwgPyB2b2lkIDAgOiBsb2FkZXJSZXNwb25zZS5jb2RlO1xuICBjb25zdCByZXRyeSA9IHJldHJ5Q291bnQgPCByZXRyeUNvbmZpZy5tYXhOdW1SZXRyeSAmJiAocmV0cnlGb3JIdHRwU3RhdHVzKGh0dHBTdGF0dXMpIHx8ICEhaXNUaW1lb3V0KTtcbiAgcmV0dXJuIHJldHJ5Q29uZmlnLnNob3VsZFJldHJ5ID8gcmV0cnlDb25maWcuc2hvdWxkUmV0cnkocmV0cnlDb25maWcsIHJldHJ5Q291bnQsIGlzVGltZW91dCwgbG9hZGVyUmVzcG9uc2UsIHJldHJ5KSA6IHJldHJ5O1xufVxuZnVuY3Rpb24gcmV0cnlGb3JIdHRwU3RhdHVzKGh0dHBTdGF0dXMpIHtcbiAgLy8gRG8gbm90IHJldHJ5IG9uIHN0YXR1cyA0eHgsIHN0YXR1cyAwIChDT1JTIGVycm9yKSwgb3IgdW5kZWZpbmVkIChkZWNyeXB0L2dhcC9wYXJzZSBlcnJvcilcbiAgcmV0dXJuIGh0dHBTdGF0dXMgPT09IDAgJiYgbmF2aWdhdG9yLm9uTGluZSA9PT0gZmFsc2UgfHwgISFodHRwU3RhdHVzICYmIChodHRwU3RhdHVzIDwgNDAwIHx8IGh0dHBTdGF0dXMgPiA0OTkpO1xufVxuXG5jb25zdCBCaW5hcnlTZWFyY2ggPSB7XG4gIC8qKlxuICAgKiBTZWFyY2hlcyBmb3IgYW4gaXRlbSBpbiBhbiBhcnJheSB3aGljaCBtYXRjaGVzIGEgY2VydGFpbiBjb25kaXRpb24uXG4gICAqIFRoaXMgcmVxdWlyZXMgdGhlIGNvbmRpdGlvbiB0byBvbmx5IG1hdGNoIG9uZSBpdGVtIGluIHRoZSBhcnJheSxcbiAgICogYW5kIGZvciB0aGUgYXJyYXkgdG8gYmUgb3JkZXJlZC5cbiAgICpcbiAgICogQHBhcmFtIGxpc3QgVGhlIGFycmF5IHRvIHNlYXJjaC5cbiAgICogQHBhcmFtIGNvbXBhcmlzb25GblxuICAgKiAgICAgIENhbGxlZCBhbmQgcHJvdmlkZWQgYSBjYW5kaWRhdGUgaXRlbSBhcyB0aGUgZmlyc3QgYXJndW1lbnQuXG4gICAqICAgICAgU2hvdWxkIHJldHVybjpcbiAgICogICAgICAgICAgPiAtMSBpZiB0aGUgaXRlbSBzaG91bGQgYmUgbG9jYXRlZCBhdCBhIGxvd2VyIGluZGV4IHRoYW4gdGhlIHByb3ZpZGVkIGl0ZW0uXG4gICAqICAgICAgICAgID4gMSBpZiB0aGUgaXRlbSBzaG91bGQgYmUgbG9jYXRlZCBhdCBhIGhpZ2hlciBpbmRleCB0aGFuIHRoZSBwcm92aWRlZCBpdGVtLlxuICAgKiAgICAgICAgICA+IDAgaWYgdGhlIGl0ZW0gaXMgdGhlIGl0ZW0geW91J3JlIGxvb2tpbmcgZm9yLlxuICAgKlxuICAgKiBAcmV0dXJucyB0aGUgb2JqZWN0IGlmIGZvdW5kLCBvdGhlcndpc2UgcmV0dXJucyBudWxsXG4gICAqL1xuICBzZWFyY2g6IGZ1bmN0aW9uIChsaXN0LCBjb21wYXJpc29uRm4pIHtcbiAgICBsZXQgbWluSW5kZXggPSAwO1xuICAgIGxldCBtYXhJbmRleCA9IGxpc3QubGVuZ3RoIC0gMTtcbiAgICBsZXQgY3VycmVudEluZGV4ID0gbnVsbDtcbiAgICBsZXQgY3VycmVudEVsZW1lbnQgPSBudWxsO1xuICAgIHdoaWxlIChtaW5JbmRleCA8PSBtYXhJbmRleCkge1xuICAgICAgY3VycmVudEluZGV4ID0gKG1pbkluZGV4ICsgbWF4SW5kZXgpIC8gMiB8IDA7XG4gICAgICBjdXJyZW50RWxlbWVudCA9IGxpc3RbY3VycmVudEluZGV4XTtcbiAgICAgIGNvbnN0IGNvbXBhcmlzb25SZXN1bHQgPSBjb21wYXJpc29uRm4oY3VycmVudEVsZW1lbnQpO1xuICAgICAgaWYgKGNvbXBhcmlzb25SZXN1bHQgPiAwKSB7XG4gICAgICAgIG1pbkluZGV4ID0gY3VycmVudEluZGV4ICsgMTtcbiAgICAgIH0gZWxzZSBpZiAoY29tcGFyaXNvblJlc3VsdCA8IDApIHtcbiAgICAgICAgbWF4SW5kZXggPSBjdXJyZW50SW5kZXggLSAxO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIGN1cnJlbnRFbGVtZW50O1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxufTtcblxuLyoqXG4gKiBSZXR1cm5zIGZpcnN0IGZyYWdtZW50IHdob3NlIGVuZFBkdCB2YWx1ZSBleGNlZWRzIHRoZSBnaXZlbiBQRFQsIG9yIG51bGwuXG4gKiBAcGFyYW0gZnJhZ21lbnRzIC0gVGhlIGFycmF5IG9mIGNhbmRpZGF0ZSBmcmFnbWVudHNcbiAqIEBwYXJhbSBQRFRWYWx1ZSAtIFRoZSBQRFQgdmFsdWUgd2hpY2ggbXVzdCBiZSBleGNlZWRlZFxuICogQHBhcmFtIG1heEZyYWdMb29rVXBUb2xlcmFuY2UgLSBUaGUgYW1vdW50IG9mIHRpbWUgdGhhdCBhIGZyYWdtZW50J3Mgc3RhcnQvZW5kIGNhbiBiZSB3aXRoaW4gaW4gb3JkZXIgdG8gYmUgY29uc2lkZXJlZCBjb250aWd1b3VzXG4gKi9cbmZ1bmN0aW9uIGZpbmRGcmFnbWVudEJ5UERUKGZyYWdtZW50cywgUERUVmFsdWUsIG1heEZyYWdMb29rVXBUb2xlcmFuY2UpIHtcbiAgaWYgKFBEVFZhbHVlID09PSBudWxsIHx8ICFBcnJheS5pc0FycmF5KGZyYWdtZW50cykgfHwgIWZyYWdtZW50cy5sZW5ndGggfHwgIWlzRmluaXRlTnVtYmVyKFBEVFZhbHVlKSkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLy8gaWYgbGVzcyB0aGFuIHN0YXJ0XG4gIGNvbnN0IHN0YXJ0UERUID0gZnJhZ21lbnRzWzBdLnByb2dyYW1EYXRlVGltZTtcbiAgaWYgKFBEVFZhbHVlIDwgKHN0YXJ0UERUIHx8IDApKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgY29uc3QgZW5kUERUID0gZnJhZ21lbnRzW2ZyYWdtZW50cy5sZW5ndGggLSAxXS5lbmRQcm9ncmFtRGF0ZVRpbWU7XG4gIGlmIChQRFRWYWx1ZSA+PSAoZW5kUERUIHx8IDApKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSA9IG1heEZyYWdMb29rVXBUb2xlcmFuY2UgfHwgMDtcbiAgZm9yIChsZXQgc2VnID0gMDsgc2VnIDwgZnJhZ21lbnRzLmxlbmd0aDsgKytzZWcpIHtcbiAgICBjb25zdCBmcmFnID0gZnJhZ21lbnRzW3NlZ107XG4gICAgaWYgKHBkdFdpdGhpblRvbGVyYW5jZVRlc3QoUERUVmFsdWUsIG1heEZyYWdMb29rVXBUb2xlcmFuY2UsIGZyYWcpKSB7XG4gICAgICByZXR1cm4gZnJhZztcbiAgICB9XG4gIH1cbiAgcmV0dXJuIG51bGw7XG59XG5cbi8qKlxuICogRmluZHMgYSBmcmFnbWVudCBiYXNlZCBvbiB0aGUgU04gb2YgdGhlIHByZXZpb3VzIGZyYWdtZW50OyBvciBiYXNlZCBvbiB0aGUgbmVlZHMgb2YgdGhlIGN1cnJlbnQgYnVmZmVyLlxuICogVGhpcyBtZXRob2QgY29tcGVuc2F0ZXMgZm9yIHNtYWxsIGJ1ZmZlciBnYXBzIGJ5IGFwcGx5aW5nIGEgdG9sZXJhbmNlIHRvIHRoZSBzdGFydCBvZiBhbnkgY2FuZGlkYXRlIGZyYWdtZW50LCB0aHVzXG4gKiBicmVha2luZyBhbnkgdHJhcHMgd2hpY2ggd291bGQgY2F1c2UgdGhlIHNhbWUgZnJhZ21lbnQgdG8gYmUgY29udGludW91c2x5IHNlbGVjdGVkIHdpdGhpbiBhIHNtYWxsIHJhbmdlLlxuICogQHBhcmFtIGZyYWdQcmV2aW91cyAtIFRoZSBsYXN0IGZyYWcgc3VjY2Vzc2Z1bGx5IGFwcGVuZGVkXG4gKiBAcGFyYW0gZnJhZ21lbnRzIC0gVGhlIGFycmF5IG9mIGNhbmRpZGF0ZSBmcmFnbWVudHNcbiAqIEBwYXJhbSBidWZmZXJFbmQgLSBUaGUgZW5kIG9mIHRoZSBjb250aWd1b3VzIGJ1ZmZlcmVkIHJhbmdlIHRoZSBwbGF5aGVhZCBpcyBjdXJyZW50bHkgd2l0aGluXG4gKiBAcGFyYW0gbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSAtIFRoZSBhbW91bnQgb2YgdGltZSB0aGF0IGEgZnJhZ21lbnQncyBzdGFydC9lbmQgY2FuIGJlIHdpdGhpbiBpbiBvcmRlciB0byBiZSBjb25zaWRlcmVkIGNvbnRpZ3VvdXNcbiAqIEByZXR1cm5zIGEgbWF0Y2hpbmcgZnJhZ21lbnQgb3IgbnVsbFxuICovXG5mdW5jdGlvbiBmaW5kRnJhZ21lbnRCeVBUUyhmcmFnUHJldmlvdXMsIGZyYWdtZW50cywgYnVmZmVyRW5kID0gMCwgbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSA9IDAsIG5leHRGcmFnTG9va3VwVG9sZXJhbmNlID0gMC4wMDUpIHtcbiAgbGV0IGZyYWdOZXh0ID0gbnVsbDtcbiAgaWYgKGZyYWdQcmV2aW91cykge1xuICAgIGZyYWdOZXh0ID0gZnJhZ21lbnRzW2ZyYWdQcmV2aW91cy5zbiAtIGZyYWdtZW50c1swXS5zbiArIDFdIHx8IG51bGw7XG4gICAgLy8gY2hlY2sgZm9yIGJ1ZmZlci1lbmQgcm91bmRpbmcgZXJyb3JcbiAgICBjb25zdCBidWZmZXJFZGdlRXJyb3IgPSBmcmFnUHJldmlvdXMuZW5kRFRTIC0gYnVmZmVyRW5kO1xuICAgIGlmIChidWZmZXJFZGdlRXJyb3IgPiAwICYmIGJ1ZmZlckVkZ2VFcnJvciA8IDAuMDAwMDAxNSkge1xuICAgICAgYnVmZmVyRW5kICs9IDAuMDAwMDAxNTtcbiAgICB9XG4gIH0gZWxzZSBpZiAoYnVmZmVyRW5kID09PSAwICYmIGZyYWdtZW50c1swXS5zdGFydCA9PT0gMCkge1xuICAgIGZyYWdOZXh0ID0gZnJhZ21lbnRzWzBdO1xuICB9XG4gIC8vIFByZWZlciB0aGUgbmV4dCBmcmFnbWVudCBpZiBpdCdzIHdpdGhpbiB0b2xlcmFuY2VcbiAgaWYgKGZyYWdOZXh0ICYmICgoIWZyYWdQcmV2aW91cyB8fCBmcmFnUHJldmlvdXMubGV2ZWwgPT09IGZyYWdOZXh0LmxldmVsKSAmJiBmcmFnbWVudFdpdGhpblRvbGVyYW5jZVRlc3QoYnVmZmVyRW5kLCBtYXhGcmFnTG9va1VwVG9sZXJhbmNlLCBmcmFnTmV4dCkgPT09IDAgfHwgZnJhZ21lbnRXaXRoaW5GYXN0U3RhcnRTd2l0Y2goZnJhZ05leHQsIGZyYWdQcmV2aW91cywgTWF0aC5taW4obmV4dEZyYWdMb29rdXBUb2xlcmFuY2UsIG1heEZyYWdMb29rVXBUb2xlcmFuY2UpKSkpIHtcbiAgICByZXR1cm4gZnJhZ05leHQ7XG4gIH1cbiAgLy8gV2UgbWlnaHQgYmUgc2Vla2luZyBwYXN0IHRoZSB0b2xlcmFuY2Ugc28gZmluZCB0aGUgYmVzdCBtYXRjaFxuICBjb25zdCBmb3VuZEZyYWdtZW50ID0gQmluYXJ5U2VhcmNoLnNlYXJjaChmcmFnbWVudHMsIGZyYWdtZW50V2l0aGluVG9sZXJhbmNlVGVzdC5iaW5kKG51bGwsIGJ1ZmZlckVuZCwgbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSkpO1xuICBpZiAoZm91bmRGcmFnbWVudCAmJiAoZm91bmRGcmFnbWVudCAhPT0gZnJhZ1ByZXZpb3VzIHx8ICFmcmFnTmV4dCkpIHtcbiAgICByZXR1cm4gZm91bmRGcmFnbWVudDtcbiAgfVxuICAvLyBJZiBubyBtYXRjaCB3YXMgZm91bmQgcmV0dXJuIHRoZSBuZXh0IGZyYWdtZW50IGFmdGVyIGZyYWdQcmV2aW91cywgb3IgbnVsbFxuICByZXR1cm4gZnJhZ05leHQ7XG59XG5mdW5jdGlvbiBmcmFnbWVudFdpdGhpbkZhc3RTdGFydFN3aXRjaChmcmFnTmV4dCwgZnJhZ1ByZXZpb3VzLCBuZXh0RnJhZ0xvb2t1cFRvbGVyYW5jZSkge1xuICBpZiAoZnJhZ1ByZXZpb3VzICYmIGZyYWdQcmV2aW91cy5zdGFydCA9PT0gMCAmJiBmcmFnUHJldmlvdXMubGV2ZWwgPCBmcmFnTmV4dC5sZXZlbCAmJiAoZnJhZ1ByZXZpb3VzLmVuZFBUUyB8fCAwKSA+IDApIHtcbiAgICBjb25zdCBmaXJzdER1cmF0aW9uID0gZnJhZ1ByZXZpb3VzLnRhZ0xpc3QucmVkdWNlKChkdXJhdGlvbiwgdGFnKSA9PiB7XG4gICAgICBpZiAodGFnWzBdID09PSAnSU5GJykge1xuICAgICAgICBkdXJhdGlvbiArPSBwYXJzZUZsb2F0KHRhZ1sxXSk7XG4gICAgICB9XG4gICAgICByZXR1cm4gZHVyYXRpb247XG4gICAgfSwgbmV4dEZyYWdMb29rdXBUb2xlcmFuY2UpO1xuICAgIHJldHVybiBmcmFnTmV4dC5zdGFydCA8PSBmaXJzdER1cmF0aW9uO1xuICB9XG4gIHJldHVybiBmYWxzZTtcbn1cblxuLyoqXG4gKiBUaGUgdGVzdCBmdW5jdGlvbiB1c2VkIGJ5IHRoZSBmaW5kRnJhZ21lbnRCeVNuJ3MgQmluYXJ5U2VhcmNoIHRvIGxvb2sgZm9yIHRoZSBiZXN0IG1hdGNoIHRvIHRoZSBjdXJyZW50IGJ1ZmZlciBjb25kaXRpb25zLlxuICogQHBhcmFtIGNhbmRpZGF0ZSAtIFRoZSBmcmFnbWVudCB0byB0ZXN0XG4gKiBAcGFyYW0gYnVmZmVyRW5kIC0gVGhlIGVuZCBvZiB0aGUgY3VycmVudCBidWZmZXJlZCByYW5nZSB0aGUgcGxheWhlYWQgaXMgY3VycmVudGx5IHdpdGhpblxuICogQHBhcmFtIG1heEZyYWdMb29rVXBUb2xlcmFuY2UgLSBUaGUgYW1vdW50IG9mIHRpbWUgdGhhdCBhIGZyYWdtZW50J3Mgc3RhcnQgY2FuIGJlIHdpdGhpbiBpbiBvcmRlciB0byBiZSBjb25zaWRlcmVkIGNvbnRpZ3VvdXNcbiAqIEByZXR1cm5zIDAgaWYgaXQgbWF0Y2hlcywgMSBpZiB0b28gbG93LCAtMSBpZiB0b28gaGlnaFxuICovXG5mdW5jdGlvbiBmcmFnbWVudFdpdGhpblRvbGVyYW5jZVRlc3QoYnVmZmVyRW5kID0gMCwgbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSA9IDAsIGNhbmRpZGF0ZSkge1xuICAvLyBlYWdlcmx5IGFjY2VwdCBhbiBhY2N1cmF0ZSBtYXRjaCAobm8gdG9sZXJhbmNlKVxuICBpZiAoY2FuZGlkYXRlLnN0YXJ0IDw9IGJ1ZmZlckVuZCAmJiBjYW5kaWRhdGUuc3RhcnQgKyBjYW5kaWRhdGUuZHVyYXRpb24gPiBidWZmZXJFbmQpIHtcbiAgICByZXR1cm4gMDtcbiAgfVxuICAvLyBvZmZzZXQgc2hvdWxkIGJlIHdpdGhpbiBmcmFnbWVudCBib3VuZGFyeSAtIGNvbmZpZy5tYXhGcmFnTG9va1VwVG9sZXJhbmNlXG4gIC8vIHRoaXMgaXMgdG8gY29wZSB3aXRoIHNpdHVhdGlvbnMgbGlrZVxuICAvLyBidWZmZXJFbmQgPSA5Ljk5MVxuICAvLyBmcmFnW8OYXSA6IFswLDEwXVxuICAvLyBmcmFnWzFdIDogWzEwLDIwXVxuICAvLyBidWZmZXJFbmQgaXMgd2l0aGluIGZyYWdbMF0gcmFuZ2UgLi4uIGFsdGhvdWdoIHdoYXQgd2UgYXJlIGV4cGVjdGluZyBpcyB0byByZXR1cm4gZnJhZ1sxXSBoZXJlXG4gIC8vICAgICAgICAgICAgICBmcmFnIHN0YXJ0ICAgICAgICAgICAgICAgZnJhZyBzdGFydCtkdXJhdGlvblxuICAvLyAgICAgICAgICAgICAgICAgIHwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXxcbiAgLy8gICAgICAgICAgICAgIDwtLS0+ICAgICAgICAgICAgICAgICAgICAgICAgIDwtLS0+XG4gIC8vICAuLi4tLS0tLS0tLT48LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+PC0tLS0tLS0tLS4uLi5cbiAgLy8gcHJldmlvdXMgZnJhZyAgICAgICAgIG1hdGNoaW5nIGZyYWdtZW50ICAgICAgICAgbmV4dCBmcmFnXG4gIC8vICByZXR1cm4gLTEgICAgICAgICAgICAgcmV0dXJuIDAgICAgICAgICAgICAgICAgIHJldHVybiAxXG4gIC8vIGxvZ2dlci5sb2coYGxldmVsL3NuL3N0YXJ0L2VuZC9idWZFbmQ6JHtsZXZlbH0vJHtjYW5kaWRhdGUuc259LyR7Y2FuZGlkYXRlLnN0YXJ0fS8keyhjYW5kaWRhdGUuc3RhcnQrY2FuZGlkYXRlLmR1cmF0aW9uKX0vJHtidWZmZXJFbmR9YCk7XG4gIC8vIFNldCB0aGUgbG9va3VwIHRvbGVyYW5jZSB0byBiZSBzbWFsbCBlbm91Z2ggdG8gZGV0ZWN0IHRoZSBjdXJyZW50IHNlZ21lbnQgLSBlbnN1cmVzIHdlIGRvbid0IHNraXAgb3ZlciB2ZXJ5IHNtYWxsIHNlZ21lbnRzXG4gIGNvbnN0IGNhbmRpZGF0ZUxvb2t1cFRvbGVyYW5jZSA9IE1hdGgubWluKG1heEZyYWdMb29rVXBUb2xlcmFuY2UsIGNhbmRpZGF0ZS5kdXJhdGlvbiArIChjYW5kaWRhdGUuZGVsdGFQVFMgPyBjYW5kaWRhdGUuZGVsdGFQVFMgOiAwKSk7XG4gIGlmIChjYW5kaWRhdGUuc3RhcnQgKyBjYW5kaWRhdGUuZHVyYXRpb24gLSBjYW5kaWRhdGVMb29rdXBUb2xlcmFuY2UgPD0gYnVmZmVyRW5kKSB7XG4gICAgcmV0dXJuIDE7XG4gIH0gZWxzZSBpZiAoY2FuZGlkYXRlLnN0YXJ0IC0gY2FuZGlkYXRlTG9va3VwVG9sZXJhbmNlID4gYnVmZmVyRW5kICYmIGNhbmRpZGF0ZS5zdGFydCkge1xuICAgIC8vIGlmIG1heEZyYWdMb29rVXBUb2xlcmFuY2Ugd2lsbCBoYXZlIG5lZ2F0aXZlIHZhbHVlIHRoZW4gZG9uJ3QgcmV0dXJuIC0xIGZvciBmaXJzdCBlbGVtZW50XG4gICAgcmV0dXJuIC0xO1xuICB9XG4gIHJldHVybiAwO1xufVxuXG4vKipcbiAqIFRoZSB0ZXN0IGZ1bmN0aW9uIHVzZWQgYnkgdGhlIGZpbmRGcmFnbWVudEJ5UGR0J3MgQmluYXJ5U2VhcmNoIHRvIGxvb2sgZm9yIHRoZSBiZXN0IG1hdGNoIHRvIHRoZSBjdXJyZW50IGJ1ZmZlciBjb25kaXRpb25zLlxuICogVGhpcyBmdW5jdGlvbiB0ZXN0cyB0aGUgY2FuZGlkYXRlJ3MgcHJvZ3JhbSBkYXRlIHRpbWUgdmFsdWVzLCBhcyByZXByZXNlbnRlZCBpbiBVbml4IHRpbWVcbiAqIEBwYXJhbSBjYW5kaWRhdGUgLSBUaGUgZnJhZ21lbnQgdG8gdGVzdFxuICogQHBhcmFtIHBkdEJ1ZmZlckVuZCAtIFRoZSBVbml4IHRpbWUgcmVwcmVzZW50aW5nIHRoZSBlbmQgb2YgdGhlIGN1cnJlbnQgYnVmZmVyZWQgcmFuZ2VcbiAqIEBwYXJhbSBtYXhGcmFnTG9va1VwVG9sZXJhbmNlIC0gVGhlIGFtb3VudCBvZiB0aW1lIHRoYXQgYSBmcmFnbWVudCdzIHN0YXJ0IGNhbiBiZSB3aXRoaW4gaW4gb3JkZXIgdG8gYmUgY29uc2lkZXJlZCBjb250aWd1b3VzXG4gKiBAcmV0dXJucyB0cnVlIGlmIGNvbnRpZ3VvdXMsIGZhbHNlIG90aGVyd2lzZVxuICovXG5mdW5jdGlvbiBwZHRXaXRoaW5Ub2xlcmFuY2VUZXN0KHBkdEJ1ZmZlckVuZCwgbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSwgY2FuZGlkYXRlKSB7XG4gIGNvbnN0IGNhbmRpZGF0ZUxvb2t1cFRvbGVyYW5jZSA9IE1hdGgubWluKG1heEZyYWdMb29rVXBUb2xlcmFuY2UsIGNhbmRpZGF0ZS5kdXJhdGlvbiArIChjYW5kaWRhdGUuZGVsdGFQVFMgPyBjYW5kaWRhdGUuZGVsdGFQVFMgOiAwKSkgKiAxMDAwO1xuXG4gIC8vIGVuZFByb2dyYW1EYXRlVGltZSBjYW4gYmUgbnVsbCwgZGVmYXVsdCB0byB6ZXJvXG4gIGNvbnN0IGVuZFByb2dyYW1EYXRlVGltZSA9IGNhbmRpZGF0ZS5lbmRQcm9ncmFtRGF0ZVRpbWUgfHwgMDtcbiAgcmV0dXJuIGVuZFByb2dyYW1EYXRlVGltZSAtIGNhbmRpZGF0ZUxvb2t1cFRvbGVyYW5jZSA+IHBkdEJ1ZmZlckVuZDtcbn1cbmZ1bmN0aW9uIGZpbmRGcmFnV2l0aENDKGZyYWdtZW50cywgY2MpIHtcbiAgcmV0dXJuIEJpbmFyeVNlYXJjaC5zZWFyY2goZnJhZ21lbnRzLCBjYW5kaWRhdGUgPT4ge1xuICAgIGlmIChjYW5kaWRhdGUuY2MgPCBjYykge1xuICAgICAgcmV0dXJuIDE7XG4gICAgfSBlbHNlIGlmIChjYW5kaWRhdGUuY2MgPiBjYykge1xuICAgICAgcmV0dXJuIC0xO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gMDtcbiAgICB9XG4gIH0pO1xufVxuXG52YXIgTmV0d29ya0Vycm9yQWN0aW9uID0ge1xuICBEb05vdGhpbmc6IDAsXG4gIFNlbmRFbmRDYWxsYmFjazogMSxcbiAgU2VuZEFsdGVybmF0ZVRvUGVuYWx0eUJveDogMixcbiAgUmVtb3ZlQWx0ZXJuYXRlUGVybWFuZW50bHk6IDMsXG4gIEluc2VydERpc2NvbnRpbnVpdHk6IDQsXG4gIFJldHJ5UmVxdWVzdDogNVxufTtcbnZhciBFcnJvckFjdGlvbkZsYWdzID0ge1xuICBOb25lOiAwLFxuICBNb3ZlQWxsQWx0ZXJuYXRlc01hdGNoaW5nSG9zdDogMSxcbiAgTW92ZUFsbEFsdGVybmF0ZXNNYXRjaGluZ0hEQ1A6IDIsXG4gIFN3aXRjaFRvU0RSOiA0XG59OyAvLyBSZXNlcnZlZCBmb3IgZnV0dXJlIHVzZVxuY2xhc3MgRXJyb3JDb250cm9sbGVyIHtcbiAgY29uc3RydWN0b3IoaGxzKSB7XG4gICAgdGhpcy5obHMgPSB2b2lkIDA7XG4gICAgdGhpcy5wbGF5bGlzdEVycm9yID0gMDtcbiAgICB0aGlzLnBlbmFsaXplZFJlbmRpdGlvbnMgPSB7fTtcbiAgICB0aGlzLmxvZyA9IHZvaWQgMDtcbiAgICB0aGlzLndhcm4gPSB2b2lkIDA7XG4gICAgdGhpcy5lcnJvciA9IHZvaWQgMDtcbiAgICB0aGlzLmhscyA9IGhscztcbiAgICB0aGlzLmxvZyA9IGxvZ2dlci5sb2cuYmluZChsb2dnZXIsIGBbaW5mb106YCk7XG4gICAgdGhpcy53YXJuID0gbG9nZ2VyLndhcm4uYmluZChsb2dnZXIsIGBbd2FybmluZ106YCk7XG4gICAgdGhpcy5lcnJvciA9IGxvZ2dlci5lcnJvci5iaW5kKGxvZ2dlciwgYFtlcnJvcl06YCk7XG4gICAgdGhpcy5yZWdpc3Rlckxpc3RlbmVycygpO1xuICB9XG4gIHJlZ2lzdGVyTGlzdGVuZXJzKCkge1xuICAgIGNvbnN0IGhscyA9IHRoaXMuaGxzO1xuICAgIGhscy5vbihFdmVudHMuRVJST1IsIHRoaXMub25FcnJvciwgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5NQU5JRkVTVF9MT0FESU5HLCB0aGlzLm9uTWFuaWZlc3RMb2FkaW5nLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkxFVkVMX1VQREFURUQsIHRoaXMub25MZXZlbFVwZGF0ZWQsIHRoaXMpO1xuICB9XG4gIHVucmVnaXN0ZXJMaXN0ZW5lcnMoKSB7XG4gICAgY29uc3QgaGxzID0gdGhpcy5obHM7XG4gICAgaWYgKCFobHMpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaGxzLm9mZihFdmVudHMuRVJST1IsIHRoaXMub25FcnJvciwgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuRVJST1IsIHRoaXMub25FcnJvck91dCwgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuTUFOSUZFU1RfTE9BRElORywgdGhpcy5vbk1hbmlmZXN0TG9hZGluZywgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuTEVWRUxfVVBEQVRFRCwgdGhpcy5vbkxldmVsVXBkYXRlZCwgdGhpcyk7XG4gIH1cbiAgZGVzdHJveSgpIHtcbiAgICB0aGlzLnVucmVnaXN0ZXJMaXN0ZW5lcnMoKTtcbiAgICAvLyBAdHMtaWdub3JlXG4gICAgdGhpcy5obHMgPSBudWxsO1xuICAgIHRoaXMucGVuYWxpemVkUmVuZGl0aW9ucyA9IHt9O1xuICB9XG4gIHN0YXJ0TG9hZChzdGFydFBvc2l0aW9uKSB7fVxuICBzdG9wTG9hZCgpIHtcbiAgICB0aGlzLnBsYXlsaXN0RXJyb3IgPSAwO1xuICB9XG4gIGdldFZhcmlhbnRMZXZlbEluZGV4KGZyYWcpIHtcbiAgICByZXR1cm4gKGZyYWcgPT0gbnVsbCA/IHZvaWQgMCA6IGZyYWcudHlwZSkgPT09IFBsYXlsaXN0TGV2ZWxUeXBlLk1BSU4gPyBmcmFnLmxldmVsIDogdGhpcy5obHMubG9hZExldmVsO1xuICB9XG4gIG9uTWFuaWZlc3RMb2FkaW5nKCkge1xuICAgIHRoaXMucGxheWxpc3RFcnJvciA9IDA7XG4gICAgdGhpcy5wZW5hbGl6ZWRSZW5kaXRpb25zID0ge307XG4gIH1cbiAgb25MZXZlbFVwZGF0ZWQoKSB7XG4gICAgdGhpcy5wbGF5bGlzdEVycm9yID0gMDtcbiAgfVxuICBvbkVycm9yKGV2ZW50LCBkYXRhKSB7XG4gICAgdmFyIF9kYXRhJGZyYWcsIF9kYXRhJGxldmVsO1xuICAgIGlmIChkYXRhLmZhdGFsKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGhscyA9IHRoaXMuaGxzO1xuICAgIGNvbnN0IGNvbnRleHQgPSBkYXRhLmNvbnRleHQ7XG4gICAgc3dpdGNoIChkYXRhLmRldGFpbHMpIHtcbiAgICAgIGNhc2UgRXJyb3JEZXRhaWxzLkZSQUdfTE9BRF9FUlJPUjpcbiAgICAgIGNhc2UgRXJyb3JEZXRhaWxzLkZSQUdfTE9BRF9USU1FT1VUOlxuICAgICAgY2FzZSBFcnJvckRldGFpbHMuS0VZX0xPQURfRVJST1I6XG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5LRVlfTE9BRF9USU1FT1VUOlxuICAgICAgICBkYXRhLmVycm9yQWN0aW9uID0gdGhpcy5nZXRGcmFnUmV0cnlPclN3aXRjaEFjdGlvbihkYXRhKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSBFcnJvckRldGFpbHMuRlJBR19QQVJTSU5HX0VSUk9SOlxuICAgICAgICAvLyBpZ25vcmUgZW1wdHkgc2VnbWVudCBlcnJvcnMgbWFya2VkIGFzIGdhcFxuICAgICAgICBpZiAoKF9kYXRhJGZyYWcgPSBkYXRhLmZyYWcpICE9IG51bGwgJiYgX2RhdGEkZnJhZy5nYXApIHtcbiAgICAgICAgICBkYXRhLmVycm9yQWN0aW9uID0ge1xuICAgICAgICAgICAgYWN0aW9uOiBOZXR3b3JrRXJyb3JBY3Rpb24uRG9Ob3RoaW5nLFxuICAgICAgICAgICAgZmxhZ3M6IEVycm9yQWN0aW9uRmxhZ3MuTm9uZVxuICAgICAgICAgIH07XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAvLyBmYWxscyB0aHJvdWdoXG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5GUkFHX0dBUDpcbiAgICAgIGNhc2UgRXJyb3JEZXRhaWxzLkZSQUdfREVDUllQVF9FUlJPUjpcbiAgICAgICAge1xuICAgICAgICAgIC8vIFN3aXRjaCBsZXZlbCBpZiBwb3NzaWJsZSwgb3RoZXJ3aXNlIGFsbG93IHJldHJ5IGNvdW50IHRvIHJlYWNoIG1heCBlcnJvciByZXRyaWVzXG4gICAgICAgICAgZGF0YS5lcnJvckFjdGlvbiA9IHRoaXMuZ2V0RnJhZ1JldHJ5T3JTd2l0Y2hBY3Rpb24oZGF0YSk7XG4gICAgICAgICAgZGF0YS5lcnJvckFjdGlvbi5hY3Rpb24gPSBOZXR3b3JrRXJyb3JBY3Rpb24uU2VuZEFsdGVybmF0ZVRvUGVuYWx0eUJveDtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgIGNhc2UgRXJyb3JEZXRhaWxzLkxFVkVMX0VNUFRZX0VSUk9SOlxuICAgICAgY2FzZSBFcnJvckRldGFpbHMuTEVWRUxfUEFSU0lOR19FUlJPUjpcbiAgICAgICAge1xuICAgICAgICAgIHZhciBfZGF0YSRjb250ZXh0LCBfZGF0YSRjb250ZXh0JGxldmVsRGU7XG4gICAgICAgICAgLy8gT25seSByZXRyeSB3aGVuIGVtcHR5IGFuZCBsaXZlXG4gICAgICAgICAgY29uc3QgbGV2ZWxJbmRleCA9IGRhdGEucGFyZW50ID09PSBQbGF5bGlzdExldmVsVHlwZS5NQUlOID8gZGF0YS5sZXZlbCA6IGhscy5sb2FkTGV2ZWw7XG4gICAgICAgICAgaWYgKGRhdGEuZGV0YWlscyA9PT0gRXJyb3JEZXRhaWxzLkxFVkVMX0VNUFRZX0VSUk9SICYmICEhKChfZGF0YSRjb250ZXh0ID0gZGF0YS5jb250ZXh0KSAhPSBudWxsICYmIChfZGF0YSRjb250ZXh0JGxldmVsRGUgPSBfZGF0YSRjb250ZXh0LmxldmVsRGV0YWlscykgIT0gbnVsbCAmJiBfZGF0YSRjb250ZXh0JGxldmVsRGUubGl2ZSkpIHtcbiAgICAgICAgICAgIGRhdGEuZXJyb3JBY3Rpb24gPSB0aGlzLmdldFBsYXlsaXN0UmV0cnlPclN3aXRjaEFjdGlvbihkYXRhLCBsZXZlbEluZGV4KTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gRXNjYWxhdGUgdG8gZmF0YWwgaWYgbm90IHJldHJ5aW5nIG9yIHN3aXRjaGluZ1xuICAgICAgICAgICAgZGF0YS5sZXZlbFJldHJ5ID0gZmFsc2U7XG4gICAgICAgICAgICBkYXRhLmVycm9yQWN0aW9uID0gdGhpcy5nZXRMZXZlbFN3aXRjaEFjdGlvbihkYXRhLCBsZXZlbEluZGV4KTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSBFcnJvckRldGFpbHMuTEVWRUxfTE9BRF9FUlJPUjpcbiAgICAgIGNhc2UgRXJyb3JEZXRhaWxzLkxFVkVMX0xPQURfVElNRU9VVDpcbiAgICAgICAgaWYgKHR5cGVvZiAoY29udGV4dCA9PSBudWxsID8gdm9pZCAwIDogY29udGV4dC5sZXZlbCkgPT09ICdudW1iZXInKSB7XG4gICAgICAgICAgZGF0YS5lcnJvckFjdGlvbiA9IHRoaXMuZ2V0UGxheWxpc3RSZXRyeU9yU3dpdGNoQWN0aW9uKGRhdGEsIGNvbnRleHQubGV2ZWwpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybjtcbiAgICAgIGNhc2UgRXJyb3JEZXRhaWxzLkFVRElPX1RSQUNLX0xPQURfRVJST1I6XG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5BVURJT19UUkFDS19MT0FEX1RJTUVPVVQ6XG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5TVUJUSVRMRV9MT0FEX0VSUk9SOlxuICAgICAgY2FzZSBFcnJvckRldGFpbHMuU1VCVElUTEVfVFJBQ0tfTE9BRF9USU1FT1VUOlxuICAgICAgICBpZiAoY29udGV4dCkge1xuICAgICAgICAgIGNvbnN0IGxldmVsID0gaGxzLmxldmVsc1tobHMubG9hZExldmVsXTtcbiAgICAgICAgICBpZiAobGV2ZWwgJiYgKGNvbnRleHQudHlwZSA9PT0gUGxheWxpc3RDb250ZXh0VHlwZS5BVURJT19UUkFDSyAmJiBsZXZlbC5oYXNBdWRpb0dyb3VwKGNvbnRleHQuZ3JvdXBJZCkgfHwgY29udGV4dC50eXBlID09PSBQbGF5bGlzdENvbnRleHRUeXBlLlNVQlRJVExFX1RSQUNLICYmIGxldmVsLmhhc1N1YnRpdGxlR3JvdXAoY29udGV4dC5ncm91cElkKSkpIHtcbiAgICAgICAgICAgIC8vIFBlcmZvcm0gUGF0aHdheSBzd2l0Y2ggb3IgUmVkdW5kYW50IGZhaWxvdmVyIGlmIHBvc3NpYmxlIGZvciBmYXN0ZXN0IHJlY292ZXJ5XG4gICAgICAgICAgICAvLyBvdGhlcndpc2UgYWxsb3cgcGxheWxpc3QgcmV0cnkgY291bnQgdG8gcmVhY2ggbWF4IGVycm9yIHJldHJpZXNcbiAgICAgICAgICAgIGRhdGEuZXJyb3JBY3Rpb24gPSB0aGlzLmdldFBsYXlsaXN0UmV0cnlPclN3aXRjaEFjdGlvbihkYXRhLCBobHMubG9hZExldmVsKTtcbiAgICAgICAgICAgIGRhdGEuZXJyb3JBY3Rpb24uYWN0aW9uID0gTmV0d29ya0Vycm9yQWN0aW9uLlNlbmRBbHRlcm5hdGVUb1BlbmFsdHlCb3g7XG4gICAgICAgICAgICBkYXRhLmVycm9yQWN0aW9uLmZsYWdzID0gRXJyb3JBY3Rpb25GbGFncy5Nb3ZlQWxsQWx0ZXJuYXRlc01hdGNoaW5nSG9zdDtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSBFcnJvckRldGFpbHMuS0VZX1NZU1RFTV9TVEFUVVNfT1VUUFVUX1JFU1RSSUNURUQ6XG4gICAgICAgIHtcbiAgICAgICAgICBjb25zdCBsZXZlbCA9IGhscy5sZXZlbHNbaGxzLmxvYWRMZXZlbF07XG4gICAgICAgICAgY29uc3QgcmVzdHJpY3RlZEhkY3BMZXZlbCA9IGxldmVsID09IG51bGwgPyB2b2lkIDAgOiBsZXZlbC5hdHRyc1snSERDUC1MRVZFTCddO1xuICAgICAgICAgIGlmIChyZXN0cmljdGVkSGRjcExldmVsKSB7XG4gICAgICAgICAgICBkYXRhLmVycm9yQWN0aW9uID0ge1xuICAgICAgICAgICAgICBhY3Rpb246IE5ldHdvcmtFcnJvckFjdGlvbi5TZW5kQWx0ZXJuYXRlVG9QZW5hbHR5Qm94LFxuICAgICAgICAgICAgICBmbGFnczogRXJyb3JBY3Rpb25GbGFncy5Nb3ZlQWxsQWx0ZXJuYXRlc01hdGNoaW5nSERDUCxcbiAgICAgICAgICAgICAgaGRjcExldmVsOiByZXN0cmljdGVkSGRjcExldmVsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLmtleVN5c3RlbUVycm9yKGRhdGEpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm47XG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5CVUZGRVJfQUREX0NPREVDX0VSUk9SOlxuICAgICAgY2FzZSBFcnJvckRldGFpbHMuUkVNVVhfQUxMT0NfRVJST1I6XG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5CVUZGRVJfQVBQRU5EX0VSUk9SOlxuICAgICAgICBkYXRhLmVycm9yQWN0aW9uID0gdGhpcy5nZXRMZXZlbFN3aXRjaEFjdGlvbihkYXRhLCAoX2RhdGEkbGV2ZWwgPSBkYXRhLmxldmVsKSAhPSBudWxsID8gX2RhdGEkbGV2ZWwgOiBobHMubG9hZExldmVsKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgY2FzZSBFcnJvckRldGFpbHMuSU5URVJOQUxfRVhDRVBUSU9OOlxuICAgICAgY2FzZSBFcnJvckRldGFpbHMuQlVGRkVSX0FQUEVORElOR19FUlJPUjpcbiAgICAgIGNhc2UgRXJyb3JEZXRhaWxzLkJVRkZFUl9GVUxMX0VSUk9SOlxuICAgICAgY2FzZSBFcnJvckRldGFpbHMuTEVWRUxfU1dJVENIX0VSUk9SOlxuICAgICAgY2FzZSBFcnJvckRldGFpbHMuQlVGRkVSX1NUQUxMRURfRVJST1I6XG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5CVUZGRVJfU0VFS19PVkVSX0hPTEU6XG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5CVUZGRVJfTlVER0VfT05fU1RBTEw6XG4gICAgICAgIGRhdGEuZXJyb3JBY3Rpb24gPSB7XG4gICAgICAgICAgYWN0aW9uOiBOZXR3b3JrRXJyb3JBY3Rpb24uRG9Ob3RoaW5nLFxuICAgICAgICAgIGZsYWdzOiBFcnJvckFjdGlvbkZsYWdzLk5vbmVcbiAgICAgICAgfTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAoZGF0YS50eXBlID09PSBFcnJvclR5cGVzLktFWV9TWVNURU1fRVJST1IpIHtcbiAgICAgIHRoaXMua2V5U3lzdGVtRXJyb3IoZGF0YSk7XG4gICAgfVxuICB9XG4gIGtleVN5c3RlbUVycm9yKGRhdGEpIHtcbiAgICBjb25zdCBsZXZlbEluZGV4ID0gdGhpcy5nZXRWYXJpYW50TGV2ZWxJbmRleChkYXRhLmZyYWcpO1xuICAgIC8vIERvIG5vdCByZXRyeSBsZXZlbC4gRXNjYWxhdGUgdG8gZmF0YWwgaWYgc3dpdGNoaW5nIGxldmVscyBmYWlscy5cbiAgICBkYXRhLmxldmVsUmV0cnkgPSBmYWxzZTtcbiAgICBkYXRhLmVycm9yQWN0aW9uID0gdGhpcy5nZXRMZXZlbFN3aXRjaEFjdGlvbihkYXRhLCBsZXZlbEluZGV4KTtcbiAgfVxuICBnZXRQbGF5bGlzdFJldHJ5T3JTd2l0Y2hBY3Rpb24oZGF0YSwgbGV2ZWxJbmRleCkge1xuICAgIGNvbnN0IGhscyA9IHRoaXMuaGxzO1xuICAgIGNvbnN0IHJldHJ5Q29uZmlnID0gZ2V0UmV0cnlDb25maWcoaGxzLmNvbmZpZy5wbGF5bGlzdExvYWRQb2xpY3ksIGRhdGEpO1xuICAgIGNvbnN0IHJldHJ5Q291bnQgPSB0aGlzLnBsYXlsaXN0RXJyb3IrKztcbiAgICBjb25zdCByZXRyeSA9IHNob3VsZFJldHJ5KHJldHJ5Q29uZmlnLCByZXRyeUNvdW50LCBpc1RpbWVvdXRFcnJvcihkYXRhKSwgZGF0YS5yZXNwb25zZSk7XG4gICAgaWYgKHJldHJ5KSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBhY3Rpb246IE5ldHdvcmtFcnJvckFjdGlvbi5SZXRyeVJlcXVlc3QsXG4gICAgICAgIGZsYWdzOiBFcnJvckFjdGlvbkZsYWdzLk5vbmUsXG4gICAgICAgIHJldHJ5Q29uZmlnLFxuICAgICAgICByZXRyeUNvdW50XG4gICAgICB9O1xuICAgIH1cbiAgICBjb25zdCBlcnJvckFjdGlvbiA9IHRoaXMuZ2V0TGV2ZWxTd2l0Y2hBY3Rpb24oZGF0YSwgbGV2ZWxJbmRleCk7XG4gICAgaWYgKHJldHJ5Q29uZmlnKSB7XG4gICAgICBlcnJvckFjdGlvbi5yZXRyeUNvbmZpZyA9IHJldHJ5Q29uZmlnO1xuICAgICAgZXJyb3JBY3Rpb24ucmV0cnlDb3VudCA9IHJldHJ5Q291bnQ7XG4gICAgfVxuICAgIHJldHVybiBlcnJvckFjdGlvbjtcbiAgfVxuICBnZXRGcmFnUmV0cnlPclN3aXRjaEFjdGlvbihkYXRhKSB7XG4gICAgY29uc3QgaGxzID0gdGhpcy5obHM7XG4gICAgLy8gU2hhcmUgZnJhZ21lbnQgZXJyb3IgY291bnQgYWNjcm9zcyBtZWRpYSBvcHRpb25zIChtYWluLCBhdWRpbywgc3VicylcbiAgICAvLyBUaGlzIGFsbG93cyBmb3IgbGV2ZWwgYmFzZWQgcmVuZGl0aW9uIHN3aXRjaGluZyB3aGVuIG1lZGlhIG9wdGlvbiBhc3NldHMgZmFpbFxuICAgIGNvbnN0IHZhcmlhbnRMZXZlbEluZGV4ID0gdGhpcy5nZXRWYXJpYW50TGV2ZWxJbmRleChkYXRhLmZyYWcpO1xuICAgIGNvbnN0IGxldmVsID0gaGxzLmxldmVsc1t2YXJpYW50TGV2ZWxJbmRleF07XG4gICAgY29uc3Qge1xuICAgICAgZnJhZ0xvYWRQb2xpY3ksXG4gICAgICBrZXlMb2FkUG9saWN5XG4gICAgfSA9IGhscy5jb25maWc7XG4gICAgY29uc3QgcmV0cnlDb25maWcgPSBnZXRSZXRyeUNvbmZpZyhkYXRhLmRldGFpbHMuc3RhcnRzV2l0aCgna2V5JykgPyBrZXlMb2FkUG9saWN5IDogZnJhZ0xvYWRQb2xpY3ksIGRhdGEpO1xuICAgIGNvbnN0IGZyYWdtZW50RXJyb3JzID0gaGxzLmxldmVscy5yZWR1Y2UoKGFjYywgbGV2ZWwpID0+IGFjYyArIGxldmVsLmZyYWdtZW50RXJyb3IsIDApO1xuICAgIC8vIFN3aXRjaCBsZXZlbHMgd2hlbiBvdXQgb2YgcmV0cmllZCBvciBsZXZlbCBpbmRleCBvdXQgb2YgYm91bmRzXG4gICAgaWYgKGxldmVsKSB7XG4gICAgICBpZiAoZGF0YS5kZXRhaWxzICE9PSBFcnJvckRldGFpbHMuRlJBR19HQVApIHtcbiAgICAgICAgbGV2ZWwuZnJhZ21lbnRFcnJvcisrO1xuICAgICAgfVxuICAgICAgY29uc3QgcmV0cnkgPSBzaG91bGRSZXRyeShyZXRyeUNvbmZpZywgZnJhZ21lbnRFcnJvcnMsIGlzVGltZW91dEVycm9yKGRhdGEpLCBkYXRhLnJlc3BvbnNlKTtcbiAgICAgIGlmIChyZXRyeSkge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIGFjdGlvbjogTmV0d29ya0Vycm9yQWN0aW9uLlJldHJ5UmVxdWVzdCxcbiAgICAgICAgICBmbGFnczogRXJyb3JBY3Rpb25GbGFncy5Ob25lLFxuICAgICAgICAgIHJldHJ5Q29uZmlnLFxuICAgICAgICAgIHJldHJ5Q291bnQ6IGZyYWdtZW50RXJyb3JzXG4gICAgICAgIH07XG4gICAgICB9XG4gICAgfVxuICAgIC8vIFJlYWNoIG1heCByZXRyeSBjb3VudCwgb3IgTWlzc2luZyBsZXZlbCByZWZlcmVuY2VcbiAgICAvLyBTd2l0Y2ggdG8gdmFsaWQgaW5kZXhcbiAgICBjb25zdCBlcnJvckFjdGlvbiA9IHRoaXMuZ2V0TGV2ZWxTd2l0Y2hBY3Rpb24oZGF0YSwgdmFyaWFudExldmVsSW5kZXgpO1xuICAgIC8vIEFkZCByZXRyeSBkZXRhaWxzIHRvIGFsbG93IHNraXBwaW5nIG9mIEZSQUdfUEFSU0lOR19FUlJPUlxuICAgIGlmIChyZXRyeUNvbmZpZykge1xuICAgICAgZXJyb3JBY3Rpb24ucmV0cnlDb25maWcgPSByZXRyeUNvbmZpZztcbiAgICAgIGVycm9yQWN0aW9uLnJldHJ5Q291bnQgPSBmcmFnbWVudEVycm9ycztcbiAgICB9XG4gICAgcmV0dXJuIGVycm9yQWN0aW9uO1xuICB9XG4gIGdldExldmVsU3dpdGNoQWN0aW9uKGRhdGEsIGxldmVsSW5kZXgpIHtcbiAgICBjb25zdCBobHMgPSB0aGlzLmhscztcbiAgICBpZiAobGV2ZWxJbmRleCA9PT0gbnVsbCB8fCBsZXZlbEluZGV4ID09PSB1bmRlZmluZWQpIHtcbiAgICAgIGxldmVsSW5kZXggPSBobHMubG9hZExldmVsO1xuICAgIH1cbiAgICBjb25zdCBsZXZlbCA9IHRoaXMuaGxzLmxldmVsc1tsZXZlbEluZGV4XTtcbiAgICBpZiAobGV2ZWwpIHtcbiAgICAgIHZhciBfZGF0YSRmcmFnMiwgX2RhdGEkY29udGV4dDI7XG4gICAgICBjb25zdCBlcnJvckRldGFpbHMgPSBkYXRhLmRldGFpbHM7XG4gICAgICBsZXZlbC5sb2FkRXJyb3IrKztcbiAgICAgIGlmIChlcnJvckRldGFpbHMgPT09IEVycm9yRGV0YWlscy5CVUZGRVJfQVBQRU5EX0VSUk9SKSB7XG4gICAgICAgIGxldmVsLmZyYWdtZW50RXJyb3IrKztcbiAgICAgIH1cbiAgICAgIC8vIFNlYXJjaCBmb3IgbmV4dCBsZXZlbCB0byByZXRyeVxuICAgICAgbGV0IG5leHRMZXZlbCA9IC0xO1xuICAgICAgY29uc3Qge1xuICAgICAgICBsZXZlbHMsXG4gICAgICAgIGxvYWRMZXZlbCxcbiAgICAgICAgbWluQXV0b0xldmVsLFxuICAgICAgICBtYXhBdXRvTGV2ZWxcbiAgICAgIH0gPSBobHM7XG4gICAgICBpZiAoIWhscy5hdXRvTGV2ZWxFbmFibGVkKSB7XG4gICAgICAgIGhscy5sb2FkTGV2ZWwgPSAtMTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGZyYWdFcnJvclR5cGUgPSAoX2RhdGEkZnJhZzIgPSBkYXRhLmZyYWcpID09IG51bGwgPyB2b2lkIDAgOiBfZGF0YSRmcmFnMi50eXBlO1xuICAgICAgLy8gRmluZCBhbHRlcm5hdGUgYXVkaW8gY29kZWMgaWYgYXZhaWxhYmxlIG9uIGF1ZGlvIGNvZGVjIGVycm9yXG4gICAgICBjb25zdCBpc0F1ZGlvQ29kZWNFcnJvciA9IGZyYWdFcnJvclR5cGUgPT09IFBsYXlsaXN0TGV2ZWxUeXBlLkFVRElPICYmIGVycm9yRGV0YWlscyA9PT0gRXJyb3JEZXRhaWxzLkZSQUdfUEFSU0lOR19FUlJPUiB8fCBkYXRhLnNvdXJjZUJ1ZmZlck5hbWUgPT09ICdhdWRpbycgJiYgKGVycm9yRGV0YWlscyA9PT0gRXJyb3JEZXRhaWxzLkJVRkZFUl9BRERfQ09ERUNfRVJST1IgfHwgZXJyb3JEZXRhaWxzID09PSBFcnJvckRldGFpbHMuQlVGRkVSX0FQUEVORF9FUlJPUik7XG4gICAgICBjb25zdCBmaW5kQXVkaW9Db2RlY0FsdGVybmF0ZSA9IGlzQXVkaW9Db2RlY0Vycm9yICYmIGxldmVscy5zb21lKCh7XG4gICAgICAgIGF1ZGlvQ29kZWNcbiAgICAgIH0pID0+IGxldmVsLmF1ZGlvQ29kZWMgIT09IGF1ZGlvQ29kZWMpO1xuICAgICAgLy8gRmluZCBhbHRlcm5hdGUgdmlkZW8gY29kZWMgaWYgYXZhaWxhYmxlIG9uIHZpZGVvIGNvZGVjIGVycm9yXG4gICAgICBjb25zdCBpc1ZpZGVvQ29kZWNFcnJvciA9IGRhdGEuc291cmNlQnVmZmVyTmFtZSA9PT0gJ3ZpZGVvJyAmJiAoZXJyb3JEZXRhaWxzID09PSBFcnJvckRldGFpbHMuQlVGRkVSX0FERF9DT0RFQ19FUlJPUiB8fCBlcnJvckRldGFpbHMgPT09IEVycm9yRGV0YWlscy5CVUZGRVJfQVBQRU5EX0VSUk9SKTtcbiAgICAgIGNvbnN0IGZpbmRWaWRlb0NvZGVjQWx0ZXJuYXRlID0gaXNWaWRlb0NvZGVjRXJyb3IgJiYgbGV2ZWxzLnNvbWUoKHtcbiAgICAgICAgY29kZWNTZXQsXG4gICAgICAgIGF1ZGlvQ29kZWNcbiAgICAgIH0pID0+IGxldmVsLmNvZGVjU2V0ICE9PSBjb2RlY1NldCAmJiBsZXZlbC5hdWRpb0NvZGVjID09PSBhdWRpb0NvZGVjKTtcbiAgICAgIGNvbnN0IHtcbiAgICAgICAgdHlwZTogcGxheWxpc3RFcnJvclR5cGUsXG4gICAgICAgIGdyb3VwSWQ6IHBsYXlsaXN0RXJyb3JHcm91cElkXG4gICAgICB9ID0gKF9kYXRhJGNvbnRleHQyID0gZGF0YS5jb250ZXh0KSAhPSBudWxsID8gX2RhdGEkY29udGV4dDIgOiB7fTtcbiAgICAgIGZvciAobGV0IGkgPSBsZXZlbHMubGVuZ3RoOyBpLS07KSB7XG4gICAgICAgIGNvbnN0IGNhbmRpZGF0ZSA9IChpICsgbG9hZExldmVsKSAlIGxldmVscy5sZW5ndGg7XG4gICAgICAgIGlmIChjYW5kaWRhdGUgIT09IGxvYWRMZXZlbCAmJiBjYW5kaWRhdGUgPj0gbWluQXV0b0xldmVsICYmIGNhbmRpZGF0ZSA8PSBtYXhBdXRvTGV2ZWwgJiYgbGV2ZWxzW2NhbmRpZGF0ZV0ubG9hZEVycm9yID09PSAwKSB7XG4gICAgICAgICAgdmFyIF9sZXZlbCRhdWRpb0dyb3VwcywgX2xldmVsJHN1YnRpdGxlR3JvdXBzO1xuICAgICAgICAgIGNvbnN0IGxldmVsQ2FuZGlkYXRlID0gbGV2ZWxzW2NhbmRpZGF0ZV07XG4gICAgICAgICAgLy8gU2tpcCBsZXZlbCBzd2l0Y2ggaWYgR0FQIHRhZyBpcyBmb3VuZCBpbiBuZXh0IGxldmVsIGF0IHNhbWUgcG9zaXRpb25cbiAgICAgICAgICBpZiAoZXJyb3JEZXRhaWxzID09PSBFcnJvckRldGFpbHMuRlJBR19HQVAgJiYgZnJhZ0Vycm9yVHlwZSA9PT0gUGxheWxpc3RMZXZlbFR5cGUuTUFJTiAmJiBkYXRhLmZyYWcpIHtcbiAgICAgICAgICAgIGNvbnN0IGxldmVsRGV0YWlscyA9IGxldmVsc1tjYW5kaWRhdGVdLmRldGFpbHM7XG4gICAgICAgICAgICBpZiAobGV2ZWxEZXRhaWxzKSB7XG4gICAgICAgICAgICAgIGNvbnN0IGZyYWdDYW5kaWRhdGUgPSBmaW5kRnJhZ21lbnRCeVBUUyhkYXRhLmZyYWcsIGxldmVsRGV0YWlscy5mcmFnbWVudHMsIGRhdGEuZnJhZy5zdGFydCk7XG4gICAgICAgICAgICAgIGlmIChmcmFnQ2FuZGlkYXRlICE9IG51bGwgJiYgZnJhZ0NhbmRpZGF0ZS5nYXApIHtcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSBpZiAocGxheWxpc3RFcnJvclR5cGUgPT09IFBsYXlsaXN0Q29udGV4dFR5cGUuQVVESU9fVFJBQ0sgJiYgbGV2ZWxDYW5kaWRhdGUuaGFzQXVkaW9Hcm91cChwbGF5bGlzdEVycm9yR3JvdXBJZCkgfHwgcGxheWxpc3RFcnJvclR5cGUgPT09IFBsYXlsaXN0Q29udGV4dFR5cGUuU1VCVElUTEVfVFJBQ0sgJiYgbGV2ZWxDYW5kaWRhdGUuaGFzU3VidGl0bGVHcm91cChwbGF5bGlzdEVycm9yR3JvdXBJZCkpIHtcbiAgICAgICAgICAgIC8vIEZvciBhdWRpby9zdWJzIHBsYXlsaXN0IGVycm9ycyBmaW5kIGFub3RoZXIgZ3JvdXAgSUQgb3IgZmFsbHRocm91Z2ggdG8gcmVkdW5kYW50IGZhaWwtb3ZlclxuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfSBlbHNlIGlmIChmcmFnRXJyb3JUeXBlID09PSBQbGF5bGlzdExldmVsVHlwZS5BVURJTyAmJiAoX2xldmVsJGF1ZGlvR3JvdXBzID0gbGV2ZWwuYXVkaW9Hcm91cHMpICE9IG51bGwgJiYgX2xldmVsJGF1ZGlvR3JvdXBzLnNvbWUoZ3JvdXBJZCA9PiBsZXZlbENhbmRpZGF0ZS5oYXNBdWRpb0dyb3VwKGdyb3VwSWQpKSB8fCBmcmFnRXJyb3JUeXBlID09PSBQbGF5bGlzdExldmVsVHlwZS5TVUJUSVRMRSAmJiAoX2xldmVsJHN1YnRpdGxlR3JvdXBzID0gbGV2ZWwuc3VidGl0bGVHcm91cHMpICE9IG51bGwgJiYgX2xldmVsJHN1YnRpdGxlR3JvdXBzLnNvbWUoZ3JvdXBJZCA9PiBsZXZlbENhbmRpZGF0ZS5oYXNTdWJ0aXRsZUdyb3VwKGdyb3VwSWQpKSB8fCBmaW5kQXVkaW9Db2RlY0FsdGVybmF0ZSAmJiBsZXZlbC5hdWRpb0NvZGVjID09PSBsZXZlbENhbmRpZGF0ZS5hdWRpb0NvZGVjIHx8ICFmaW5kQXVkaW9Db2RlY0FsdGVybmF0ZSAmJiBsZXZlbC5hdWRpb0NvZGVjICE9PSBsZXZlbENhbmRpZGF0ZS5hdWRpb0NvZGVjIHx8IGZpbmRWaWRlb0NvZGVjQWx0ZXJuYXRlICYmIGxldmVsLmNvZGVjU2V0ID09PSBsZXZlbENhbmRpZGF0ZS5jb2RlY1NldCkge1xuICAgICAgICAgICAgLy8gRm9yIHZpZGVvL2F1ZGlvL3N1YnMgZnJhZyBlcnJvcnMgZmluZCBhbm90aGVyIGdyb3VwIElEIG9yIGZhbGx0aHJvdWdoIHRvIHJlZHVuZGFudCBmYWlsLW92ZXJcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgIH1cbiAgICAgICAgICBuZXh0TGV2ZWwgPSBjYW5kaWRhdGU7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmIChuZXh0TGV2ZWwgPiAtMSAmJiBobHMubG9hZExldmVsICE9PSBuZXh0TGV2ZWwpIHtcbiAgICAgICAgZGF0YS5sZXZlbFJldHJ5ID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5wbGF5bGlzdEVycm9yID0gMDtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBhY3Rpb246IE5ldHdvcmtFcnJvckFjdGlvbi5TZW5kQWx0ZXJuYXRlVG9QZW5hbHR5Qm94LFxuICAgICAgICAgIGZsYWdzOiBFcnJvckFjdGlvbkZsYWdzLk5vbmUsXG4gICAgICAgICAgbmV4dEF1dG9MZXZlbDogbmV4dExldmVsXG4gICAgICAgIH07XG4gICAgICB9XG4gICAgfVxuICAgIC8vIE5vIGxldmVscyB0byBzd2l0Y2ggLyBNYW51YWwgbGV2ZWwgc2VsZWN0aW9uIC8gTGV2ZWwgbm90IGZvdW5kXG4gICAgLy8gUmVzb2x2ZSB3aXRoIFBhdGh3YXkgc3dpdGNoLCBSZWR1bmRhbnQgZmFpbC1vdmVyLCBvciBzdGF5IG9uIGxvd2VzdCBMZXZlbFxuICAgIHJldHVybiB7XG4gICAgICBhY3Rpb246IE5ldHdvcmtFcnJvckFjdGlvbi5TZW5kQWx0ZXJuYXRlVG9QZW5hbHR5Qm94LFxuICAgICAgZmxhZ3M6IEVycm9yQWN0aW9uRmxhZ3MuTW92ZUFsbEFsdGVybmF0ZXNNYXRjaGluZ0hvc3RcbiAgICB9O1xuICB9XG4gIG9uRXJyb3JPdXQoZXZlbnQsIGRhdGEpIHtcbiAgICB2YXIgX2RhdGEkZXJyb3JBY3Rpb247XG4gICAgc3dpdGNoICgoX2RhdGEkZXJyb3JBY3Rpb24gPSBkYXRhLmVycm9yQWN0aW9uKSA9PSBudWxsID8gdm9pZCAwIDogX2RhdGEkZXJyb3JBY3Rpb24uYWN0aW9uKSB7XG4gICAgICBjYXNlIE5ldHdvcmtFcnJvckFjdGlvbi5Eb05vdGhpbmc6XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSBOZXR3b3JrRXJyb3JBY3Rpb24uU2VuZEFsdGVybmF0ZVRvUGVuYWx0eUJveDpcbiAgICAgICAgdGhpcy5zZW5kQWx0ZXJuYXRlVG9QZW5hbHR5Qm94KGRhdGEpO1xuICAgICAgICBpZiAoIWRhdGEuZXJyb3JBY3Rpb24ucmVzb2x2ZWQgJiYgZGF0YS5kZXRhaWxzICE9PSBFcnJvckRldGFpbHMuRlJBR19HQVApIHtcbiAgICAgICAgICBkYXRhLmZhdGFsID0gdHJ1ZTtcbiAgICAgICAgfSBlbHNlIGlmICgvTWVkaWFTb3VyY2UgcmVhZHlTdGF0ZTogZW5kZWQvLnRlc3QoZGF0YS5lcnJvci5tZXNzYWdlKSkge1xuICAgICAgICAgIHRoaXMud2FybihgTWVkaWFTb3VyY2UgZW5kZWQgYWZ0ZXIgXCIke2RhdGEuc291cmNlQnVmZmVyTmFtZX1cIiBzb3VyY2VCdWZmZXIgYXBwZW5kIGVycm9yLiBBdHRlbXB0aW5nIHRvIHJlY292ZXIgZnJvbSBtZWRpYSBlcnJvci5gKTtcbiAgICAgICAgICB0aGlzLmhscy5yZWNvdmVyTWVkaWFFcnJvcigpO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSBOZXR3b3JrRXJyb3JBY3Rpb24uUmV0cnlSZXF1ZXN0OlxuICAgICAgICAvLyBoYW5kbGVkIGJ5IHN0cmVhbSBhbmQgcGxheWxpc3QvbGV2ZWwgY29udHJvbGxlcnNcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuICAgIGlmIChkYXRhLmZhdGFsKSB7XG4gICAgICB0aGlzLmhscy5zdG9wTG9hZCgpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgfVxuICBzZW5kQWx0ZXJuYXRlVG9QZW5hbHR5Qm94KGRhdGEpIHtcbiAgICBjb25zdCBobHMgPSB0aGlzLmhscztcbiAgICBjb25zdCBlcnJvckFjdGlvbiA9IGRhdGEuZXJyb3JBY3Rpb247XG4gICAgaWYgKCFlcnJvckFjdGlvbikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB7XG4gICAgICBmbGFncyxcbiAgICAgIGhkY3BMZXZlbCxcbiAgICAgIG5leHRBdXRvTGV2ZWxcbiAgICB9ID0gZXJyb3JBY3Rpb247XG4gICAgc3dpdGNoIChmbGFncykge1xuICAgICAgY2FzZSBFcnJvckFjdGlvbkZsYWdzLk5vbmU6XG4gICAgICAgIHRoaXMuc3dpdGNoTGV2ZWwoZGF0YSwgbmV4dEF1dG9MZXZlbCk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSBFcnJvckFjdGlvbkZsYWdzLk1vdmVBbGxBbHRlcm5hdGVzTWF0Y2hpbmdIRENQOlxuICAgICAgICBpZiAoaGRjcExldmVsKSB7XG4gICAgICAgICAgaGxzLm1heEhkY3BMZXZlbCA9IEhkY3BMZXZlbHNbSGRjcExldmVscy5pbmRleE9mKGhkY3BMZXZlbCkgLSAxXTtcbiAgICAgICAgICBlcnJvckFjdGlvbi5yZXNvbHZlZCA9IHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy53YXJuKGBSZXN0cmljdGluZyBwbGF5YmFjayB0byBIRENQLUxFVkVMIG9mIFwiJHtobHMubWF4SGRjcExldmVsfVwiIG9yIGxvd2VyYCk7XG4gICAgICAgIGJyZWFrO1xuICAgIH1cbiAgICAvLyBJZiBub3QgcmVzb2x2ZWQgYnkgcHJldmlvdXMgYWN0aW9ucyB0cnkgdG8gc3dpdGNoIHRvIG5leHQgbGV2ZWxcbiAgICBpZiAoIWVycm9yQWN0aW9uLnJlc29sdmVkKSB7XG4gICAgICB0aGlzLnN3aXRjaExldmVsKGRhdGEsIG5leHRBdXRvTGV2ZWwpO1xuICAgIH1cbiAgfVxuICBzd2l0Y2hMZXZlbChkYXRhLCBsZXZlbEluZGV4KSB7XG4gICAgaWYgKGxldmVsSW5kZXggIT09IHVuZGVmaW5lZCAmJiBkYXRhLmVycm9yQWN0aW9uKSB7XG4gICAgICB0aGlzLndhcm4oYHN3aXRjaGluZyB0byBsZXZlbCAke2xldmVsSW5kZXh9IGFmdGVyICR7ZGF0YS5kZXRhaWxzfWApO1xuICAgICAgdGhpcy5obHMubmV4dEF1dG9MZXZlbCA9IGxldmVsSW5kZXg7XG4gICAgICBkYXRhLmVycm9yQWN0aW9uLnJlc29sdmVkID0gdHJ1ZTtcbiAgICAgIC8vIFN0cmVhbSBjb250cm9sbGVyIGlzIHJlc3BvbnNpYmxlIGZvciB0aGlzIGJ1dCB3b24ndCBzd2l0Y2ggb24gZmFsc2Ugc3RhcnRcbiAgICAgIHRoaXMuaGxzLm5leHRMb2FkTGV2ZWwgPSB0aGlzLmhscy5uZXh0QXV0b0xldmVsO1xuICAgIH1cbiAgfVxufVxuXG5jbGFzcyBCYXNlUGxheWxpc3RDb250cm9sbGVyIHtcbiAgY29uc3RydWN0b3IoaGxzLCBsb2dQcmVmaXgpIHtcbiAgICB0aGlzLmhscyA9IHZvaWQgMDtcbiAgICB0aGlzLnRpbWVyID0gLTE7XG4gICAgdGhpcy5yZXF1ZXN0U2NoZWR1bGVkID0gLTE7XG4gICAgdGhpcy5jYW5Mb2FkID0gZmFsc2U7XG4gICAgdGhpcy5sb2cgPSB2b2lkIDA7XG4gICAgdGhpcy53YXJuID0gdm9pZCAwO1xuICAgIHRoaXMubG9nID0gbG9nZ2VyLmxvZy5iaW5kKGxvZ2dlciwgYCR7bG9nUHJlZml4fTpgKTtcbiAgICB0aGlzLndhcm4gPSBsb2dnZXIud2Fybi5iaW5kKGxvZ2dlciwgYCR7bG9nUHJlZml4fTpgKTtcbiAgICB0aGlzLmhscyA9IGhscztcbiAgfVxuICBkZXN0cm95KCkge1xuICAgIHRoaXMuY2xlYXJUaW1lcigpO1xuICAgIC8vIEB0cy1pZ25vcmVcbiAgICB0aGlzLmhscyA9IHRoaXMubG9nID0gdGhpcy53YXJuID0gbnVsbDtcbiAgfVxuICBjbGVhclRpbWVyKCkge1xuICAgIGlmICh0aGlzLnRpbWVyICE9PSAtMSkge1xuICAgICAgc2VsZi5jbGVhclRpbWVvdXQodGhpcy50aW1lcik7XG4gICAgICB0aGlzLnRpbWVyID0gLTE7XG4gICAgfVxuICB9XG4gIHN0YXJ0TG9hZCgpIHtcbiAgICB0aGlzLmNhbkxvYWQgPSB0cnVlO1xuICAgIHRoaXMucmVxdWVzdFNjaGVkdWxlZCA9IC0xO1xuICAgIHRoaXMubG9hZFBsYXlsaXN0KCk7XG4gIH1cbiAgc3RvcExvYWQoKSB7XG4gICAgdGhpcy5jYW5Mb2FkID0gZmFsc2U7XG4gICAgdGhpcy5jbGVhclRpbWVyKCk7XG4gIH1cbiAgc3dpdGNoUGFyYW1zKHBsYXlsaXN0VXJpLCBwcmV2aW91cywgY3VycmVudCkge1xuICAgIGNvbnN0IHJlbmRpdGlvblJlcG9ydHMgPSBwcmV2aW91cyA9PSBudWxsID8gdm9pZCAwIDogcHJldmlvdXMucmVuZGl0aW9uUmVwb3J0cztcbiAgICBpZiAocmVuZGl0aW9uUmVwb3J0cykge1xuICAgICAgbGV0IGZvdW5kSW5kZXggPSAtMTtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcmVuZGl0aW9uUmVwb3J0cy5sZW5ndGg7IGkrKykge1xuICAgICAgICBjb25zdCBhdHRyID0gcmVuZGl0aW9uUmVwb3J0c1tpXTtcbiAgICAgICAgbGV0IHVyaTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICB1cmkgPSBuZXcgc2VsZi5VUkwoYXR0ci5VUkksIHByZXZpb3VzLnVybCkuaHJlZjtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICBsb2dnZXIud2FybihgQ291bGQgbm90IGNvbnN0cnVjdCBuZXcgVVJMIGZvciBSZW5kaXRpb24gUmVwb3J0OiAke2Vycm9yfWApO1xuICAgICAgICAgIHVyaSA9IGF0dHIuVVJJIHx8ICcnO1xuICAgICAgICB9XG4gICAgICAgIC8vIFVzZSBleGFjdCBtYXRjaC4gT3RoZXJ3aXNlLCB0aGUgbGFzdCBwYXJ0aWFsIG1hdGNoLCBpZiBhbnksIHdpbGwgYmUgdXNlZFxuICAgICAgICAvLyAoUGxheWxpc3QgVVJJIGluY2x1ZGVzIGEgcXVlcnkgc3RyaW5nIHRoYXQgdGhlIFJlbmRpdGlvbiBSZXBvcnQgZG9lcyBub3QpXG4gICAgICAgIGlmICh1cmkgPT09IHBsYXlsaXN0VXJpKSB7XG4gICAgICAgICAgZm91bmRJbmRleCA9IGk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH0gZWxzZSBpZiAodXJpID09PSBwbGF5bGlzdFVyaS5zdWJzdHJpbmcoMCwgdXJpLmxlbmd0aCkpIHtcbiAgICAgICAgICBmb3VuZEluZGV4ID0gaTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgaWYgKGZvdW5kSW5kZXggIT09IC0xKSB7XG4gICAgICAgIGNvbnN0IGF0dHIgPSByZW5kaXRpb25SZXBvcnRzW2ZvdW5kSW5kZXhdO1xuICAgICAgICBjb25zdCBtc24gPSBwYXJzZUludChhdHRyWydMQVNULU1TTiddKSB8fCAocHJldmlvdXMgPT0gbnVsbCA/IHZvaWQgMCA6IHByZXZpb3VzLmxhc3RQYXJ0U24pO1xuICAgICAgICBsZXQgcGFydCA9IHBhcnNlSW50KGF0dHJbJ0xBU1QtUEFSVCddKSB8fCAocHJldmlvdXMgPT0gbnVsbCA/IHZvaWQgMCA6IHByZXZpb3VzLmxhc3RQYXJ0SW5kZXgpO1xuICAgICAgICBpZiAodGhpcy5obHMuY29uZmlnLmxvd0xhdGVuY3lNb2RlKSB7XG4gICAgICAgICAgY29uc3QgY3VycmVudEdvYWwgPSBNYXRoLm1pbihwcmV2aW91cy5hZ2UgLSBwcmV2aW91cy5wYXJ0VGFyZ2V0LCBwcmV2aW91cy50YXJnZXRkdXJhdGlvbik7XG4gICAgICAgICAgaWYgKHBhcnQgPj0gMCAmJiBjdXJyZW50R29hbCA+IHByZXZpb3VzLnBhcnRUYXJnZXQpIHtcbiAgICAgICAgICAgIHBhcnQgKz0gMTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgY29uc3Qgc2tpcCA9IGN1cnJlbnQgJiYgZ2V0U2tpcFZhbHVlKGN1cnJlbnQpO1xuICAgICAgICByZXR1cm4gbmV3IEhsc1VybFBhcmFtZXRlcnMobXNuLCBwYXJ0ID49IDAgPyBwYXJ0IDogdW5kZWZpbmVkLCBza2lwKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgbG9hZFBsYXlsaXN0KGhsc1VybFBhcmFtZXRlcnMpIHtcbiAgICBpZiAodGhpcy5yZXF1ZXN0U2NoZWR1bGVkID09PSAtMSkge1xuICAgICAgdGhpcy5yZXF1ZXN0U2NoZWR1bGVkID0gc2VsZi5wZXJmb3JtYW5jZS5ub3coKTtcbiAgICB9XG4gICAgLy8gTG9hZGluZyBpcyBoYW5kbGVkIGJ5IHRoZSBzdWJjbGFzc2VzXG4gIH1cbiAgc2hvdWxkTG9hZFBsYXlsaXN0KHBsYXlsaXN0KSB7XG4gICAgcmV0dXJuIHRoaXMuY2FuTG9hZCAmJiAhIXBsYXlsaXN0ICYmICEhcGxheWxpc3QudXJsICYmICghcGxheWxpc3QuZGV0YWlscyB8fCBwbGF5bGlzdC5kZXRhaWxzLmxpdmUpO1xuICB9XG4gIHNob3VsZFJlbG9hZFBsYXlsaXN0KHBsYXlsaXN0KSB7XG4gICAgcmV0dXJuIHRoaXMudGltZXIgPT09IC0xICYmIHRoaXMucmVxdWVzdFNjaGVkdWxlZCA9PT0gLTEgJiYgdGhpcy5zaG91bGRMb2FkUGxheWxpc3QocGxheWxpc3QpO1xuICB9XG4gIHBsYXlsaXN0TG9hZGVkKGluZGV4LCBkYXRhLCBwcmV2aW91c0RldGFpbHMpIHtcbiAgICBjb25zdCB7XG4gICAgICBkZXRhaWxzLFxuICAgICAgc3RhdHNcbiAgICB9ID0gZGF0YTtcblxuICAgIC8vIFNldCBsYXN0IHVwZGF0ZWQgZGF0ZS10aW1lXG4gICAgY29uc3Qgbm93ID0gc2VsZi5wZXJmb3JtYW5jZS5ub3coKTtcbiAgICBjb25zdCBlbGFwc2VkID0gc3RhdHMubG9hZGluZy5maXJzdCA/IE1hdGgubWF4KDAsIG5vdyAtIHN0YXRzLmxvYWRpbmcuZmlyc3QpIDogMDtcbiAgICBkZXRhaWxzLmFkdmFuY2VkRGF0ZVRpbWUgPSBEYXRlLm5vdygpIC0gZWxhcHNlZDtcblxuICAgIC8vIGlmIGN1cnJlbnQgcGxheWxpc3QgaXMgYSBsaXZlIHBsYXlsaXN0LCBhcm0gYSB0aW1lciB0byByZWxvYWQgaXRcbiAgICBpZiAoZGV0YWlscy5saXZlIHx8IHByZXZpb3VzRGV0YWlscyAhPSBudWxsICYmIHByZXZpb3VzRGV0YWlscy5saXZlKSB7XG4gICAgICBkZXRhaWxzLnJlbG9hZGVkKHByZXZpb3VzRGV0YWlscyk7XG4gICAgICBpZiAocHJldmlvdXNEZXRhaWxzKSB7XG4gICAgICAgIHRoaXMubG9nKGBsaXZlIHBsYXlsaXN0ICR7aW5kZXh9ICR7ZGV0YWlscy5hZHZhbmNlZCA/ICdSRUZSRVNIRUQgJyArIGRldGFpbHMubGFzdFBhcnRTbiArICctJyArIGRldGFpbHMubGFzdFBhcnRJbmRleCA6IGRldGFpbHMudXBkYXRlZCA/ICdVUERBVEVEJyA6ICdNSVNTRUQnfWApO1xuICAgICAgfVxuICAgICAgLy8gTWVyZ2UgbGl2ZSBwbGF5bGlzdHMgdG8gYWRqdXN0IGZyYWdtZW50IHN0YXJ0cyBhbmQgZmlsbCBpbiBkZWx0YSBwbGF5bGlzdCBza2lwcGVkIHNlZ21lbnRzXG4gICAgICBpZiAocHJldmlvdXNEZXRhaWxzICYmIGRldGFpbHMuZnJhZ21lbnRzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgbWVyZ2VEZXRhaWxzKHByZXZpb3VzRGV0YWlscywgZGV0YWlscyk7XG4gICAgICB9XG4gICAgICBpZiAoIXRoaXMuY2FuTG9hZCB8fCAhZGV0YWlscy5saXZlKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGxldCBkZWxpdmVyeURpcmVjdGl2ZXM7XG4gICAgICBsZXQgbXNuID0gdW5kZWZpbmVkO1xuICAgICAgbGV0IHBhcnQgPSB1bmRlZmluZWQ7XG4gICAgICBpZiAoZGV0YWlscy5jYW5CbG9ja1JlbG9hZCAmJiBkZXRhaWxzLmVuZFNOICYmIGRldGFpbHMuYWR2YW5jZWQpIHtcbiAgICAgICAgLy8gTG9hZCBsZXZlbCB3aXRoIExMLUhMUyBkZWxpdmVyeSBkaXJlY3RpdmVzXG4gICAgICAgIGNvbnN0IGxvd0xhdGVuY3lNb2RlID0gdGhpcy5obHMuY29uZmlnLmxvd0xhdGVuY3lNb2RlO1xuICAgICAgICBjb25zdCBsYXN0UGFydFNuID0gZGV0YWlscy5sYXN0UGFydFNuO1xuICAgICAgICBjb25zdCBlbmRTbiA9IGRldGFpbHMuZW5kU047XG4gICAgICAgIGNvbnN0IGxhc3RQYXJ0SW5kZXggPSBkZXRhaWxzLmxhc3RQYXJ0SW5kZXg7XG4gICAgICAgIGNvbnN0IGhhc1BhcnRzID0gbGFzdFBhcnRJbmRleCAhPT0gLTE7XG4gICAgICAgIGNvbnN0IGxhc3RQYXJ0ID0gbGFzdFBhcnRTbiA9PT0gZW5kU247XG4gICAgICAgIC8vIFdoZW4gbG93IGxhdGVuY3kgbW9kZSBpcyBkaXNhYmxlZCwgd2UnbGwgc2tpcCBwYXJ0IHJlcXVlc3RzIG9uY2UgdGhlIGxhc3QgcGFydCBpbmRleCBpcyBmb3VuZFxuICAgICAgICBjb25zdCBuZXh0U25TdGFydEluZGV4ID0gbG93TGF0ZW5jeU1vZGUgPyAwIDogbGFzdFBhcnRJbmRleDtcbiAgICAgICAgaWYgKGhhc1BhcnRzKSB7XG4gICAgICAgICAgbXNuID0gbGFzdFBhcnQgPyBlbmRTbiArIDEgOiBsYXN0UGFydFNuO1xuICAgICAgICAgIHBhcnQgPSBsYXN0UGFydCA/IG5leHRTblN0YXJ0SW5kZXggOiBsYXN0UGFydEluZGV4ICsgMTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBtc24gPSBlbmRTbiArIDE7XG4gICAgICAgIH1cbiAgICAgICAgLy8gTG93LUxhdGVuY3kgQ0ROIFR1bmUtaW46IFwiYWdlXCIgaGVhZGVyIGFuZCB0aW1lIHNpbmNlIGxvYWQgaW5kaWNhdGVzIHdlJ3JlIGJlaGluZCBieSBtb3JlIHRoYW4gb25lIHBhcnRcbiAgICAgICAgLy8gVXBkYXRlIGRpcmVjdGl2ZXMgdG8gb2J0YWluIHRoZSBQbGF5bGlzdCB0aGF0IGhhcyB0aGUgZXN0aW1hdGVkIGFkZGl0aW9uYWwgZHVyYXRpb24gb2YgbWVkaWFcbiAgICAgICAgY29uc3QgbGFzdEFkdmFuY2VkID0gZGV0YWlscy5hZ2U7XG4gICAgICAgIGNvbnN0IGNkbkFnZSA9IGxhc3RBZHZhbmNlZCArIGRldGFpbHMuYWdlSGVhZGVyO1xuICAgICAgICBsZXQgY3VycmVudEdvYWwgPSBNYXRoLm1pbihjZG5BZ2UgLSBkZXRhaWxzLnBhcnRUYXJnZXQsIGRldGFpbHMudGFyZ2V0ZHVyYXRpb24gKiAxLjUpO1xuICAgICAgICBpZiAoY3VycmVudEdvYWwgPiAwKSB7XG4gICAgICAgICAgaWYgKHByZXZpb3VzRGV0YWlscyAmJiBjdXJyZW50R29hbCA+IHByZXZpb3VzRGV0YWlscy50dW5lSW5Hb2FsKSB7XG4gICAgICAgICAgICAvLyBJZiB3ZSBhdHRlbXB0ZWQgdG8gZ2V0IHRoZSBuZXh0IG9yIGxhdGVzdCBwbGF5bGlzdCB1cGRhdGUsIGJ1dCBjdXJyZW50R29hbCBpbmNyZWFzZWQsXG4gICAgICAgICAgICAvLyB0aGVuIHdlIGVpdGhlciBjYW4ndCBjYXRjaHVwLCBvciB0aGUgXCJhZ2VcIiBoZWFkZXIgY2Fubm90IGJlIHRydXN0ZWQuXG4gICAgICAgICAgICB0aGlzLndhcm4oYENETiBUdW5lLWluIGdvYWwgaW5jcmVhc2VkIGZyb206ICR7cHJldmlvdXNEZXRhaWxzLnR1bmVJbkdvYWx9IHRvOiAke2N1cnJlbnRHb2FsfSB3aXRoIHBsYXlsaXN0IGFnZTogJHtkZXRhaWxzLmFnZX1gKTtcbiAgICAgICAgICAgIGN1cnJlbnRHb2FsID0gMDtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29uc3Qgc2VnbWVudHMgPSBNYXRoLmZsb29yKGN1cnJlbnRHb2FsIC8gZGV0YWlscy50YXJnZXRkdXJhdGlvbik7XG4gICAgICAgICAgICBtc24gKz0gc2VnbWVudHM7XG4gICAgICAgICAgICBpZiAocGFydCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgIGNvbnN0IHBhcnRzID0gTWF0aC5yb3VuZChjdXJyZW50R29hbCAlIGRldGFpbHMudGFyZ2V0ZHVyYXRpb24gLyBkZXRhaWxzLnBhcnRUYXJnZXQpO1xuICAgICAgICAgICAgICBwYXJ0ICs9IHBhcnRzO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5sb2coYENETiBUdW5lLWluIGFnZTogJHtkZXRhaWxzLmFnZUhlYWRlcn1zIGxhc3QgYWR2YW5jZWQgJHtsYXN0QWR2YW5jZWQudG9GaXhlZCgyKX1zIGdvYWw6ICR7Y3VycmVudEdvYWx9IHNraXAgc24gJHtzZWdtZW50c30gdG8gcGFydCAke3BhcnR9YCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGRldGFpbHMudHVuZUluR29hbCA9IGN1cnJlbnRHb2FsO1xuICAgICAgICB9XG4gICAgICAgIGRlbGl2ZXJ5RGlyZWN0aXZlcyA9IHRoaXMuZ2V0RGVsaXZlcnlEaXJlY3RpdmVzKGRldGFpbHMsIGRhdGEuZGVsaXZlcnlEaXJlY3RpdmVzLCBtc24sIHBhcnQpO1xuICAgICAgICBpZiAobG93TGF0ZW5jeU1vZGUgfHwgIWxhc3RQYXJ0KSB7XG4gICAgICAgICAgdGhpcy5sb2FkUGxheWxpc3QoZGVsaXZlcnlEaXJlY3RpdmVzKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoZGV0YWlscy5jYW5CbG9ja1JlbG9hZCB8fCBkZXRhaWxzLmNhblNraXBVbnRpbCkge1xuICAgICAgICBkZWxpdmVyeURpcmVjdGl2ZXMgPSB0aGlzLmdldERlbGl2ZXJ5RGlyZWN0aXZlcyhkZXRhaWxzLCBkYXRhLmRlbGl2ZXJ5RGlyZWN0aXZlcywgbXNuLCBwYXJ0KTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGJ1ZmZlckluZm8gPSB0aGlzLmhscy5tYWluRm9yd2FyZEJ1ZmZlckluZm87XG4gICAgICBjb25zdCBwb3NpdGlvbiA9IGJ1ZmZlckluZm8gPyBidWZmZXJJbmZvLmVuZCAtIGJ1ZmZlckluZm8ubGVuIDogMDtcbiAgICAgIGNvbnN0IGRpc3RhbmNlVG9MaXZlRWRnZU1zID0gKGRldGFpbHMuZWRnZSAtIHBvc2l0aW9uKSAqIDEwMDA7XG4gICAgICBjb25zdCByZWxvYWRJbnRlcnZhbCA9IGNvbXB1dGVSZWxvYWRJbnRlcnZhbChkZXRhaWxzLCBkaXN0YW5jZVRvTGl2ZUVkZ2VNcyk7XG4gICAgICBpZiAoZGV0YWlscy51cGRhdGVkICYmIG5vdyA+IHRoaXMucmVxdWVzdFNjaGVkdWxlZCArIHJlbG9hZEludGVydmFsKSB7XG4gICAgICAgIHRoaXMucmVxdWVzdFNjaGVkdWxlZCA9IHN0YXRzLmxvYWRpbmcuc3RhcnQ7XG4gICAgICB9XG4gICAgICBpZiAobXNuICE9PSB1bmRlZmluZWQgJiYgZGV0YWlscy5jYW5CbG9ja1JlbG9hZCkge1xuICAgICAgICB0aGlzLnJlcXVlc3RTY2hlZHVsZWQgPSBzdGF0cy5sb2FkaW5nLmZpcnN0ICsgcmVsb2FkSW50ZXJ2YWwgLSAoZGV0YWlscy5wYXJ0VGFyZ2V0ICogMTAwMCB8fCAxMDAwKTtcbiAgICAgIH0gZWxzZSBpZiAodGhpcy5yZXF1ZXN0U2NoZWR1bGVkID09PSAtMSB8fCB0aGlzLnJlcXVlc3RTY2hlZHVsZWQgKyByZWxvYWRJbnRlcnZhbCA8IG5vdykge1xuICAgICAgICB0aGlzLnJlcXVlc3RTY2hlZHVsZWQgPSBub3c7XG4gICAgICB9IGVsc2UgaWYgKHRoaXMucmVxdWVzdFNjaGVkdWxlZCAtIG5vdyA8PSAwKSB7XG4gICAgICAgIHRoaXMucmVxdWVzdFNjaGVkdWxlZCArPSByZWxvYWRJbnRlcnZhbDtcbiAgICAgIH1cbiAgICAgIGxldCBlc3RpbWF0ZWRUaW1lVW50aWxVcGRhdGUgPSB0aGlzLnJlcXVlc3RTY2hlZHVsZWQgLSBub3c7XG4gICAgICBlc3RpbWF0ZWRUaW1lVW50aWxVcGRhdGUgPSBNYXRoLm1heCgwLCBlc3RpbWF0ZWRUaW1lVW50aWxVcGRhdGUpO1xuICAgICAgdGhpcy5sb2coYHJlbG9hZCBsaXZlIHBsYXlsaXN0ICR7aW5kZXh9IGluICR7TWF0aC5yb3VuZChlc3RpbWF0ZWRUaW1lVW50aWxVcGRhdGUpfSBtc2ApO1xuICAgICAgLy8gdGhpcy5sb2coXG4gICAgICAvLyAgIGBsaXZlIHJlbG9hZCAke2RldGFpbHMudXBkYXRlZCA/ICdSRUZSRVNIRUQnIDogJ01JU1NFRCd9XG4gICAgICAvLyByZWxvYWQgaW4gJHtlc3RpbWF0ZWRUaW1lVW50aWxVcGRhdGUgLyAxMDAwfVxuICAgICAgLy8gcm91bmQgdHJpcCAkeyhzdGF0cy5sb2FkaW5nLmVuZCAtIHN0YXRzLmxvYWRpbmcuc3RhcnQpIC8gMTAwMH1cbiAgICAgIC8vIGRpZmYgJHtcbiAgICAgIC8vICAgKHJlbG9hZEludGVydmFsIC1cbiAgICAgIC8vICAgICAoZXN0aW1hdGVkVGltZVVudGlsVXBkYXRlICtcbiAgICAgIC8vICAgICAgIHN0YXRzLmxvYWRpbmcuZW5kIC1cbiAgICAgIC8vICAgICAgIHN0YXRzLmxvYWRpbmcuc3RhcnQpKSAvXG4gICAgICAvLyAgIDEwMDBcbiAgICAgIC8vIH1cbiAgICAgIC8vIHJlbG9hZCBpbnRlcnZhbCAke3JlbG9hZEludGVydmFsIC8gMTAwMH1cbiAgICAgIC8vIHRhcmdldCBkdXJhdGlvbiAke2RldGFpbHMudGFyZ2V0ZHVyYXRpb259XG4gICAgICAvLyBkaXN0YW5jZSB0byBlZGdlICR7ZGlzdGFuY2VUb0xpdmVFZGdlTXMgLyAxMDAwfWBcbiAgICAgIC8vICk7XG5cbiAgICAgIHRoaXMudGltZXIgPSBzZWxmLnNldFRpbWVvdXQoKCkgPT4gdGhpcy5sb2FkUGxheWxpc3QoZGVsaXZlcnlEaXJlY3RpdmVzKSwgZXN0aW1hdGVkVGltZVVudGlsVXBkYXRlKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5jbGVhclRpbWVyKCk7XG4gICAgfVxuICB9XG4gIGdldERlbGl2ZXJ5RGlyZWN0aXZlcyhkZXRhaWxzLCBwcmV2aW91c0RlbGl2ZXJ5RGlyZWN0aXZlcywgbXNuLCBwYXJ0KSB7XG4gICAgbGV0IHNraXAgPSBnZXRTa2lwVmFsdWUoZGV0YWlscyk7XG4gICAgaWYgKHByZXZpb3VzRGVsaXZlcnlEaXJlY3RpdmVzICE9IG51bGwgJiYgcHJldmlvdXNEZWxpdmVyeURpcmVjdGl2ZXMuc2tpcCAmJiBkZXRhaWxzLmRlbHRhVXBkYXRlRmFpbGVkKSB7XG4gICAgICBtc24gPSBwcmV2aW91c0RlbGl2ZXJ5RGlyZWN0aXZlcy5tc247XG4gICAgICBwYXJ0ID0gcHJldmlvdXNEZWxpdmVyeURpcmVjdGl2ZXMucGFydDtcbiAgICAgIHNraXAgPSBIbHNTa2lwLk5vO1xuICAgIH1cbiAgICByZXR1cm4gbmV3IEhsc1VybFBhcmFtZXRlcnMobXNuLCBwYXJ0LCBza2lwKTtcbiAgfVxuICBjaGVja1JldHJ5KGVycm9yRXZlbnQpIHtcbiAgICBjb25zdCBlcnJvckRldGFpbHMgPSBlcnJvckV2ZW50LmRldGFpbHM7XG4gICAgY29uc3QgaXNUaW1lb3V0ID0gaXNUaW1lb3V0RXJyb3IoZXJyb3JFdmVudCk7XG4gICAgY29uc3QgZXJyb3JBY3Rpb24gPSBlcnJvckV2ZW50LmVycm9yQWN0aW9uO1xuICAgIGNvbnN0IHtcbiAgICAgIGFjdGlvbixcbiAgICAgIHJldHJ5Q291bnQgPSAwLFxuICAgICAgcmV0cnlDb25maWdcbiAgICB9ID0gZXJyb3JBY3Rpb24gfHwge307XG4gICAgY29uc3QgcmV0cnkgPSAhIWVycm9yQWN0aW9uICYmICEhcmV0cnlDb25maWcgJiYgKGFjdGlvbiA9PT0gTmV0d29ya0Vycm9yQWN0aW9uLlJldHJ5UmVxdWVzdCB8fCAhZXJyb3JBY3Rpb24ucmVzb2x2ZWQgJiYgYWN0aW9uID09PSBOZXR3b3JrRXJyb3JBY3Rpb24uU2VuZEFsdGVybmF0ZVRvUGVuYWx0eUJveCk7XG4gICAgaWYgKHJldHJ5KSB7XG4gICAgICB2YXIgX2Vycm9yRXZlbnQkY29udGV4dDtcbiAgICAgIHRoaXMucmVxdWVzdFNjaGVkdWxlZCA9IC0xO1xuICAgICAgaWYgKHJldHJ5Q291bnQgPj0gcmV0cnlDb25maWcubWF4TnVtUmV0cnkpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuICAgICAgaWYgKGlzVGltZW91dCAmJiAoX2Vycm9yRXZlbnQkY29udGV4dCA9IGVycm9yRXZlbnQuY29udGV4dCkgIT0gbnVsbCAmJiBfZXJyb3JFdmVudCRjb250ZXh0LmRlbGl2ZXJ5RGlyZWN0aXZlcykge1xuICAgICAgICAvLyBUaGUgTEwtSExTIHJlcXVlc3QgYWxyZWFkeSB0aW1lZCBvdXQgc28gcmV0cnkgaW1tZWRpYXRlbHlcbiAgICAgICAgdGhpcy53YXJuKGBSZXRyeWluZyBwbGF5bGlzdCBsb2FkaW5nICR7cmV0cnlDb3VudCArIDF9LyR7cmV0cnlDb25maWcubWF4TnVtUmV0cnl9IGFmdGVyIFwiJHtlcnJvckRldGFpbHN9XCIgd2l0aG91dCBkZWxpdmVyeS1kaXJlY3RpdmVzYCk7XG4gICAgICAgIHRoaXMubG9hZFBsYXlsaXN0KCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCBkZWxheSA9IGdldFJldHJ5RGVsYXkocmV0cnlDb25maWcsIHJldHJ5Q291bnQpO1xuICAgICAgICAvLyBTY2hlZHVsZSBsZXZlbC90cmFjayByZWxvYWRcbiAgICAgICAgdGhpcy50aW1lciA9IHNlbGYuc2V0VGltZW91dCgoKSA9PiB0aGlzLmxvYWRQbGF5bGlzdCgpLCBkZWxheSk7XG4gICAgICAgIHRoaXMud2FybihgUmV0cnlpbmcgcGxheWxpc3QgbG9hZGluZyAke3JldHJ5Q291bnQgKyAxfS8ke3JldHJ5Q29uZmlnLm1heE51bVJldHJ5fSBhZnRlciBcIiR7ZXJyb3JEZXRhaWxzfVwiIGluICR7ZGVsYXl9bXNgKTtcbiAgICAgIH1cbiAgICAgIC8vIGBsZXZlbFJldHJ5ID0gdHJ1ZWAgdXNlZCB0byBpbmZvcm0gb3RoZXIgY29udHJvbGxlcnMgdGhhdCBhIHJldHJ5IGlzIGhhcHBlbmluZ1xuICAgICAgZXJyb3JFdmVudC5sZXZlbFJldHJ5ID0gdHJ1ZTtcbiAgICAgIGVycm9yQWN0aW9uLnJlc29sdmVkID0gdHJ1ZTtcbiAgICB9XG4gICAgcmV0dXJuIHJldHJ5O1xuICB9XG59XG5cbi8qXG4gKiBjb21wdXRlIGFuIEV4cG9uZW50aWFsIFdlaWdodGVkIG1vdmluZyBhdmVyYWdlXG4gKiAtIGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL01vdmluZ19hdmVyYWdlI0V4cG9uZW50aWFsX21vdmluZ19hdmVyYWdlXG4gKiAgLSBoZWF2aWx5IGluc3BpcmVkIGZyb20gc2hha2EtcGxheWVyXG4gKi9cblxuY2xhc3MgRVdNQSB7XG4gIC8vICBBYm91dCBoYWxmIG9mIHRoZSBlc3RpbWF0ZWQgdmFsdWUgd2lsbCBiZSBmcm9tIHRoZSBsYXN0IHxoYWxmTGlmZXwgc2FtcGxlcyBieSB3ZWlnaHQuXG4gIGNvbnN0cnVjdG9yKGhhbGZMaWZlLCBlc3RpbWF0ZSA9IDAsIHdlaWdodCA9IDApIHtcbiAgICB0aGlzLmhhbGZMaWZlID0gdm9pZCAwO1xuICAgIHRoaXMuYWxwaGFfID0gdm9pZCAwO1xuICAgIHRoaXMuZXN0aW1hdGVfID0gdm9pZCAwO1xuICAgIHRoaXMudG90YWxXZWlnaHRfID0gdm9pZCAwO1xuICAgIHRoaXMuaGFsZkxpZmUgPSBoYWxmTGlmZTtcbiAgICAvLyBMYXJnZXIgdmFsdWVzIG9mIGFscGhhIGV4cGlyZSBoaXN0b3JpY2FsIGRhdGEgbW9yZSBzbG93bHkuXG4gICAgdGhpcy5hbHBoYV8gPSBoYWxmTGlmZSA/IE1hdGguZXhwKE1hdGgubG9nKDAuNSkgLyBoYWxmTGlmZSkgOiAwO1xuICAgIHRoaXMuZXN0aW1hdGVfID0gZXN0aW1hdGU7XG4gICAgdGhpcy50b3RhbFdlaWdodF8gPSB3ZWlnaHQ7XG4gIH1cbiAgc2FtcGxlKHdlaWdodCwgdmFsdWUpIHtcbiAgICBjb25zdCBhZGpBbHBoYSA9IE1hdGgucG93KHRoaXMuYWxwaGFfLCB3ZWlnaHQpO1xuICAgIHRoaXMuZXN0aW1hdGVfID0gdmFsdWUgKiAoMSAtIGFkakFscGhhKSArIGFkakFscGhhICogdGhpcy5lc3RpbWF0ZV87XG4gICAgdGhpcy50b3RhbFdlaWdodF8gKz0gd2VpZ2h0O1xuICB9XG4gIGdldFRvdGFsV2VpZ2h0KCkge1xuICAgIHJldHVybiB0aGlzLnRvdGFsV2VpZ2h0XztcbiAgfVxuICBnZXRFc3RpbWF0ZSgpIHtcbiAgICBpZiAodGhpcy5hbHBoYV8pIHtcbiAgICAgIGNvbnN0IHplcm9GYWN0b3IgPSAxIC0gTWF0aC5wb3codGhpcy5hbHBoYV8sIHRoaXMudG90YWxXZWlnaHRfKTtcbiAgICAgIGlmICh6ZXJvRmFjdG9yKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmVzdGltYXRlXyAvIHplcm9GYWN0b3I7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmVzdGltYXRlXztcbiAgfVxufVxuXG4vKlxuICogRVdNQSBCYW5kd2lkdGggRXN0aW1hdG9yXG4gKiAgLSBoZWF2aWx5IGluc3BpcmVkIGZyb20gc2hha2EtcGxheWVyXG4gKiBUcmFja3MgYmFuZHdpZHRoIHNhbXBsZXMgYW5kIGVzdGltYXRlcyBhdmFpbGFibGUgYmFuZHdpZHRoLlxuICogQmFzZWQgb24gdGhlIG1pbmltdW0gb2YgdHdvIGV4cG9uZW50aWFsbHktd2VpZ2h0ZWQgbW92aW5nIGF2ZXJhZ2VzIHdpdGhcbiAqIGRpZmZlcmVudCBoYWxmLWxpdmVzLlxuICovXG5cbmNsYXNzIEV3bWFCYW5kV2lkdGhFc3RpbWF0b3Ige1xuICBjb25zdHJ1Y3RvcihzbG93LCBmYXN0LCBkZWZhdWx0RXN0aW1hdGUsIGRlZmF1bHRUVEZCID0gMTAwKSB7XG4gICAgdGhpcy5kZWZhdWx0RXN0aW1hdGVfID0gdm9pZCAwO1xuICAgIHRoaXMubWluV2VpZ2h0XyA9IHZvaWQgMDtcbiAgICB0aGlzLm1pbkRlbGF5TXNfID0gdm9pZCAwO1xuICAgIHRoaXMuc2xvd18gPSB2b2lkIDA7XG4gICAgdGhpcy5mYXN0XyA9IHZvaWQgMDtcbiAgICB0aGlzLmRlZmF1bHRUVEZCXyA9IHZvaWQgMDtcbiAgICB0aGlzLnR0ZmJfID0gdm9pZCAwO1xuICAgIHRoaXMuZGVmYXVsdEVzdGltYXRlXyA9IGRlZmF1bHRFc3RpbWF0ZTtcbiAgICB0aGlzLm1pbldlaWdodF8gPSAwLjAwMTtcbiAgICB0aGlzLm1pbkRlbGF5TXNfID0gNTA7XG4gICAgdGhpcy5zbG93XyA9IG5ldyBFV01BKHNsb3cpO1xuICAgIHRoaXMuZmFzdF8gPSBuZXcgRVdNQShmYXN0KTtcbiAgICB0aGlzLmRlZmF1bHRUVEZCXyA9IGRlZmF1bHRUVEZCO1xuICAgIHRoaXMudHRmYl8gPSBuZXcgRVdNQShzbG93KTtcbiAgfVxuICB1cGRhdGUoc2xvdywgZmFzdCkge1xuICAgIGNvbnN0IHtcbiAgICAgIHNsb3dfLFxuICAgICAgZmFzdF8sXG4gICAgICB0dGZiX1xuICAgIH0gPSB0aGlzO1xuICAgIGlmIChzbG93Xy5oYWxmTGlmZSAhPT0gc2xvdykge1xuICAgICAgdGhpcy5zbG93XyA9IG5ldyBFV01BKHNsb3csIHNsb3dfLmdldEVzdGltYXRlKCksIHNsb3dfLmdldFRvdGFsV2VpZ2h0KCkpO1xuICAgIH1cbiAgICBpZiAoZmFzdF8uaGFsZkxpZmUgIT09IGZhc3QpIHtcbiAgICAgIHRoaXMuZmFzdF8gPSBuZXcgRVdNQShmYXN0LCBmYXN0Xy5nZXRFc3RpbWF0ZSgpLCBmYXN0Xy5nZXRUb3RhbFdlaWdodCgpKTtcbiAgICB9XG4gICAgaWYgKHR0ZmJfLmhhbGZMaWZlICE9PSBzbG93KSB7XG4gICAgICB0aGlzLnR0ZmJfID0gbmV3IEVXTUEoc2xvdywgdHRmYl8uZ2V0RXN0aW1hdGUoKSwgdHRmYl8uZ2V0VG90YWxXZWlnaHQoKSk7XG4gICAgfVxuICB9XG4gIHNhbXBsZShkdXJhdGlvbk1zLCBudW1CeXRlcykge1xuICAgIGR1cmF0aW9uTXMgPSBNYXRoLm1heChkdXJhdGlvbk1zLCB0aGlzLm1pbkRlbGF5TXNfKTtcbiAgICBjb25zdCBudW1CaXRzID0gOCAqIG51bUJ5dGVzO1xuICAgIC8vIHdlaWdodCBpcyBkdXJhdGlvbiBpbiBzZWNvbmRzXG4gICAgY29uc3QgZHVyYXRpb25TID0gZHVyYXRpb25NcyAvIDEwMDA7XG4gICAgLy8gdmFsdWUgaXMgYmFuZHdpZHRoIGluIGJpdHMvc1xuICAgIGNvbnN0IGJhbmR3aWR0aEluQnBzID0gbnVtQml0cyAvIGR1cmF0aW9uUztcbiAgICB0aGlzLmZhc3RfLnNhbXBsZShkdXJhdGlvblMsIGJhbmR3aWR0aEluQnBzKTtcbiAgICB0aGlzLnNsb3dfLnNhbXBsZShkdXJhdGlvblMsIGJhbmR3aWR0aEluQnBzKTtcbiAgfVxuICBzYW1wbGVUVEZCKHR0ZmIpIHtcbiAgICAvLyB3ZWlnaHQgaXMgZnJlcXVlbmN5IGN1cnZlIGFwcGxpZWQgdG8gVFRGQiBpbiBzZWNvbmRzXG4gICAgLy8gKGxvbmdlciB0aW1lcyBoYXZlIGxlc3Mgd2VpZ2h0IHdpdGggZXhwZWN0ZWQgaW5wdXQgdW5kZXIgMSBzZWNvbmQpXG4gICAgY29uc3Qgc2Vjb25kcyA9IHR0ZmIgLyAxMDAwO1xuICAgIGNvbnN0IHdlaWdodCA9IE1hdGguc3FydCgyKSAqIE1hdGguZXhwKC1NYXRoLnBvdyhzZWNvbmRzLCAyKSAvIDIpO1xuICAgIHRoaXMudHRmYl8uc2FtcGxlKHdlaWdodCwgTWF0aC5tYXgodHRmYiwgNSkpO1xuICB9XG4gIGNhbkVzdGltYXRlKCkge1xuICAgIHJldHVybiB0aGlzLmZhc3RfLmdldFRvdGFsV2VpZ2h0KCkgPj0gdGhpcy5taW5XZWlnaHRfO1xuICB9XG4gIGdldEVzdGltYXRlKCkge1xuICAgIGlmICh0aGlzLmNhbkVzdGltYXRlKCkpIHtcbiAgICAgIC8vIGNvbnNvbGUubG9nKCdzbG93IGVzdGltYXRlOicrIE1hdGgucm91bmQodGhpcy5zbG93Xy5nZXRFc3RpbWF0ZSgpKSk7XG4gICAgICAvLyBjb25zb2xlLmxvZygnZmFzdCBlc3RpbWF0ZTonKyBNYXRoLnJvdW5kKHRoaXMuZmFzdF8uZ2V0RXN0aW1hdGUoKSkpO1xuICAgICAgLy8gVGFrZSB0aGUgbWluaW11bSBvZiB0aGVzZSB0d28gZXN0aW1hdGVzLiAgVGhpcyBzaG91bGQgaGF2ZSB0aGUgZWZmZWN0IG9mXG4gICAgICAvLyBhZGFwdGluZyBkb3duIHF1aWNrbHksIGJ1dCB1cCBtb3JlIHNsb3dseS5cbiAgICAgIHJldHVybiBNYXRoLm1pbih0aGlzLmZhc3RfLmdldEVzdGltYXRlKCksIHRoaXMuc2xvd18uZ2V0RXN0aW1hdGUoKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiB0aGlzLmRlZmF1bHRFc3RpbWF0ZV87XG4gICAgfVxuICB9XG4gIGdldEVzdGltYXRlVFRGQigpIHtcbiAgICBpZiAodGhpcy50dGZiXy5nZXRUb3RhbFdlaWdodCgpID49IHRoaXMubWluV2VpZ2h0Xykge1xuICAgICAgcmV0dXJuIHRoaXMudHRmYl8uZ2V0RXN0aW1hdGUoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIHRoaXMuZGVmYXVsdFRURkJfO1xuICAgIH1cbiAgfVxuICBkZXN0cm95KCkge31cbn1cblxuY29uc3QgU1VQUE9SVEVEX0lORk9fREVGQVVMVCA9IHtcbiAgc3VwcG9ydGVkOiB0cnVlLFxuICBjb25maWd1cmF0aW9uczogW10sXG4gIGRlY29kaW5nSW5mb1Jlc3VsdHM6IFt7XG4gICAgc3VwcG9ydGVkOiB0cnVlLFxuICAgIHBvd2VyRWZmaWNpZW50OiB0cnVlLFxuICAgIHNtb290aDogdHJ1ZVxuICB9XVxufTtcbmNvbnN0IFNVUFBPUlRFRF9JTkZPX0NBQ0hFID0ge307XG5mdW5jdGlvbiByZXF1aXJlc01lZGlhQ2FwYWJpbGl0aWVzRGVjb2RpbmdJbmZvKGxldmVsLCBhdWRpb1RyYWNrc0J5R3JvdXAsIGN1cnJlbnRWaWRlb1JhbmdlLCBjdXJyZW50RnJhbWVSYXRlLCBjdXJyZW50QncsIGF1ZGlvUHJlZmVyZW5jZSkge1xuICAvLyBPbmx5IHRlc3Qgc3VwcG9ydCB3aGVuIGNvbmZpZ3VyYXRpb24gaXMgZXhjZWVkcyBtaW5pbXVtIG9wdGlvbnNcbiAgY29uc3QgYXVkaW9Hcm91cHMgPSBsZXZlbC5hdWRpb0NvZGVjID8gbGV2ZWwuYXVkaW9Hcm91cHMgOiBudWxsO1xuICBjb25zdCBhdWRpb0NvZGVjUHJlZmVyZW5jZSA9IGF1ZGlvUHJlZmVyZW5jZSA9PSBudWxsID8gdm9pZCAwIDogYXVkaW9QcmVmZXJlbmNlLmF1ZGlvQ29kZWM7XG4gIGNvbnN0IGNoYW5uZWxzUHJlZmVyZW5jZSA9IGF1ZGlvUHJlZmVyZW5jZSA9PSBudWxsID8gdm9pZCAwIDogYXVkaW9QcmVmZXJlbmNlLmNoYW5uZWxzO1xuICBjb25zdCBtYXhDaGFubmVscyA9IGNoYW5uZWxzUHJlZmVyZW5jZSA/IHBhcnNlSW50KGNoYW5uZWxzUHJlZmVyZW5jZSkgOiBhdWRpb0NvZGVjUHJlZmVyZW5jZSA/IEluZmluaXR5IDogMjtcbiAgbGV0IGF1ZGlvQ2hhbm5lbHMgPSBudWxsO1xuICBpZiAoYXVkaW9Hcm91cHMgIT0gbnVsbCAmJiBhdWRpb0dyb3Vwcy5sZW5ndGgpIHtcbiAgICB0cnkge1xuICAgICAgaWYgKGF1ZGlvR3JvdXBzLmxlbmd0aCA9PT0gMSAmJiBhdWRpb0dyb3Vwc1swXSkge1xuICAgICAgICBhdWRpb0NoYW5uZWxzID0gYXVkaW9UcmFja3NCeUdyb3VwLmdyb3Vwc1thdWRpb0dyb3Vwc1swXV0uY2hhbm5lbHM7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBhdWRpb0NoYW5uZWxzID0gYXVkaW9Hcm91cHMucmVkdWNlKChhY2MsIGdyb3VwSWQpID0+IHtcbiAgICAgICAgICBpZiAoZ3JvdXBJZCkge1xuICAgICAgICAgICAgY29uc3QgYXVkaW9UcmFja0dyb3VwID0gYXVkaW9UcmFja3NCeUdyb3VwLmdyb3Vwc1tncm91cElkXTtcbiAgICAgICAgICAgIGlmICghYXVkaW9UcmFja0dyb3VwKSB7XG4gICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgQXVkaW8gdHJhY2sgZ3JvdXAgJHtncm91cElkfSBub3QgZm91bmRgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIFN1bSBhbGwgY2hhbm5lbCBrZXkgdmFsdWVzXG4gICAgICAgICAgICBPYmplY3Qua2V5cyhhdWRpb1RyYWNrR3JvdXAuY2hhbm5lbHMpLmZvckVhY2goa2V5ID0+IHtcbiAgICAgICAgICAgICAgYWNjW2tleV0gPSAoYWNjW2tleV0gfHwgMCkgKyBhdWRpb1RyYWNrR3JvdXAuY2hhbm5lbHNba2V5XTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgICB9LCB7XG4gICAgICAgICAgMjogMFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICB9XG4gIHJldHVybiBsZXZlbC52aWRlb0NvZGVjICE9PSB1bmRlZmluZWQgJiYgKGxldmVsLndpZHRoID4gMTkyMCAmJiBsZXZlbC5oZWlnaHQgPiAxMDg4IHx8IGxldmVsLmhlaWdodCA+IDE5MjAgJiYgbGV2ZWwud2lkdGggPiAxMDg4IHx8IGxldmVsLmZyYW1lUmF0ZSA+IE1hdGgubWF4KGN1cnJlbnRGcmFtZVJhdGUsIDMwKSB8fCBsZXZlbC52aWRlb1JhbmdlICE9PSAnU0RSJyAmJiBsZXZlbC52aWRlb1JhbmdlICE9PSBjdXJyZW50VmlkZW9SYW5nZSB8fCBsZXZlbC5iaXRyYXRlID4gTWF0aC5tYXgoY3VycmVudEJ3LCA4ZTYpKSB8fCAhIWF1ZGlvQ2hhbm5lbHMgJiYgaXNGaW5pdGVOdW1iZXIobWF4Q2hhbm5lbHMpICYmIE9iamVjdC5rZXlzKGF1ZGlvQ2hhbm5lbHMpLnNvbWUoY2hhbm5lbHMgPT4gcGFyc2VJbnQoY2hhbm5lbHMpID4gbWF4Q2hhbm5lbHMpO1xufVxuZnVuY3Rpb24gZ2V0TWVkaWFEZWNvZGluZ0luZm9Qcm9taXNlKGxldmVsLCBhdWRpb1RyYWNrc0J5R3JvdXAsIG1lZGlhQ2FwYWJpbGl0aWVzKSB7XG4gIGNvbnN0IHZpZGVvQ29kZWNzID0gbGV2ZWwudmlkZW9Db2RlYztcbiAgY29uc3QgYXVkaW9Db2RlY3MgPSBsZXZlbC5hdWRpb0NvZGVjO1xuICBpZiAoIXZpZGVvQ29kZWNzIHx8ICFhdWRpb0NvZGVjcyB8fCAhbWVkaWFDYXBhYmlsaXRpZXMpIHtcbiAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKFNVUFBPUlRFRF9JTkZPX0RFRkFVTFQpO1xuICB9XG4gIGNvbnN0IGJhc2VWaWRlb0NvbmZpZ3VyYXRpb24gPSB7XG4gICAgd2lkdGg6IGxldmVsLndpZHRoLFxuICAgIGhlaWdodDogbGV2ZWwuaGVpZ2h0LFxuICAgIGJpdHJhdGU6IE1hdGguY2VpbChNYXRoLm1heChsZXZlbC5iaXRyYXRlICogMC45LCBsZXZlbC5hdmVyYWdlQml0cmF0ZSkpLFxuICAgIC8vIEFzc3VtZSBhIGZyYW1lcmF0ZSBvZiAzMGZwcyBzaW5jZSBNZWRpYUNhcGFiaWxpdGllcyB3aWxsIG5vdCBhY2NlcHQgTGV2ZWwgZGVmYXVsdCBvZiAwLlxuICAgIGZyYW1lcmF0ZTogbGV2ZWwuZnJhbWVSYXRlIHx8IDMwXG4gIH07XG4gIGNvbnN0IHZpZGVvUmFuZ2UgPSBsZXZlbC52aWRlb1JhbmdlO1xuICBpZiAodmlkZW9SYW5nZSAhPT0gJ1NEUicpIHtcbiAgICBiYXNlVmlkZW9Db25maWd1cmF0aW9uLnRyYW5zZmVyRnVuY3Rpb24gPSB2aWRlb1JhbmdlLnRvTG93ZXJDYXNlKCk7XG4gIH1cbiAgY29uc3QgY29uZmlndXJhdGlvbnMgPSB2aWRlb0NvZGVjcy5zcGxpdCgnLCcpLm1hcCh2aWRlb0NvZGVjID0+ICh7XG4gICAgdHlwZTogJ21lZGlhLXNvdXJjZScsXG4gICAgdmlkZW86IF9vYmplY3RTcHJlYWQyKF9vYmplY3RTcHJlYWQyKHt9LCBiYXNlVmlkZW9Db25maWd1cmF0aW9uKSwge30sIHtcbiAgICAgIGNvbnRlbnRUeXBlOiBtaW1lVHlwZUZvckNvZGVjKHZpZGVvQ29kZWMsICd2aWRlbycpXG4gICAgfSlcbiAgfSkpO1xuICBpZiAoYXVkaW9Db2RlY3MgJiYgbGV2ZWwuYXVkaW9Hcm91cHMpIHtcbiAgICBsZXZlbC5hdWRpb0dyb3Vwcy5mb3JFYWNoKGF1ZGlvR3JvdXBJZCA9PiB7XG4gICAgICB2YXIgX2F1ZGlvVHJhY2tzQnlHcm91cCRnO1xuICAgICAgaWYgKCFhdWRpb0dyb3VwSWQpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgKF9hdWRpb1RyYWNrc0J5R3JvdXAkZyA9IGF1ZGlvVHJhY2tzQnlHcm91cC5ncm91cHNbYXVkaW9Hcm91cElkXSkgPT0gbnVsbCA/IHZvaWQgMCA6IF9hdWRpb1RyYWNrc0J5R3JvdXAkZy50cmFja3MuZm9yRWFjaChhdWRpb1RyYWNrID0+IHtcbiAgICAgICAgaWYgKGF1ZGlvVHJhY2suZ3JvdXBJZCA9PT0gYXVkaW9Hcm91cElkKSB7XG4gICAgICAgICAgY29uc3QgY2hhbm5lbHMgPSBhdWRpb1RyYWNrLmNoYW5uZWxzIHx8ICcnO1xuICAgICAgICAgIGNvbnN0IGNoYW5uZWxzTnVtYmVyID0gcGFyc2VGbG9hdChjaGFubmVscyk7XG4gICAgICAgICAgaWYgKGlzRmluaXRlTnVtYmVyKGNoYW5uZWxzTnVtYmVyKSAmJiBjaGFubmVsc051bWJlciA+IDIpIHtcbiAgICAgICAgICAgIGNvbmZpZ3VyYXRpb25zLnB1c2guYXBwbHkoY29uZmlndXJhdGlvbnMsIGF1ZGlvQ29kZWNzLnNwbGl0KCcsJykubWFwKGF1ZGlvQ29kZWMgPT4gKHtcbiAgICAgICAgICAgICAgdHlwZTogJ21lZGlhLXNvdXJjZScsXG4gICAgICAgICAgICAgIGF1ZGlvOiB7XG4gICAgICAgICAgICAgICAgY29udGVudFR5cGU6IG1pbWVUeXBlRm9yQ29kZWMoYXVkaW9Db2RlYywgJ2F1ZGlvJyksXG4gICAgICAgICAgICAgICAgY2hhbm5lbHM6ICcnICsgY2hhbm5lbHNOdW1iZXJcbiAgICAgICAgICAgICAgICAvLyBzcGF0aWFsUmVuZGVyaW5nOlxuICAgICAgICAgICAgICAgIC8vICAgYXVkaW9Db2RlYyA9PT0gJ2VjLTMnICYmIGNoYW5uZWxzLmluZGV4T2YoJ0pPQycpLFxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KSkpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfSk7XG4gIH1cbiAgcmV0dXJuIFByb21pc2UuYWxsKGNvbmZpZ3VyYXRpb25zLm1hcChjb25maWd1cmF0aW9uID0+IHtcbiAgICAvLyBDYWNoZSBNZWRpYUNhcGFiaWxpdGllcyBwcm9taXNlc1xuICAgIGNvbnN0IGRlY29kaW5nSW5mb0tleSA9IGdldE1lZGlhRGVjb2RpbmdJbmZvS2V5KGNvbmZpZ3VyYXRpb24pO1xuICAgIHJldHVybiBTVVBQT1JURURfSU5GT19DQUNIRVtkZWNvZGluZ0luZm9LZXldIHx8IChTVVBQT1JURURfSU5GT19DQUNIRVtkZWNvZGluZ0luZm9LZXldID0gbWVkaWFDYXBhYmlsaXRpZXMuZGVjb2RpbmdJbmZvKGNvbmZpZ3VyYXRpb24pKTtcbiAgfSkpLnRoZW4oZGVjb2RpbmdJbmZvUmVzdWx0cyA9PiAoe1xuICAgIHN1cHBvcnRlZDogIWRlY29kaW5nSW5mb1Jlc3VsdHMuc29tZShpbmZvID0+ICFpbmZvLnN1cHBvcnRlZCksXG4gICAgY29uZmlndXJhdGlvbnMsXG4gICAgZGVjb2RpbmdJbmZvUmVzdWx0c1xuICB9KSkuY2F0Y2goZXJyb3IgPT4gKHtcbiAgICBzdXBwb3J0ZWQ6IGZhbHNlLFxuICAgIGNvbmZpZ3VyYXRpb25zLFxuICAgIGRlY29kaW5nSW5mb1Jlc3VsdHM6IFtdLFxuICAgIGVycm9yXG4gIH0pKTtcbn1cbmZ1bmN0aW9uIGdldE1lZGlhRGVjb2RpbmdJbmZvS2V5KGNvbmZpZykge1xuICBjb25zdCB7XG4gICAgYXVkaW8sXG4gICAgdmlkZW9cbiAgfSA9IGNvbmZpZztcbiAgY29uc3QgbWVkaWFDb25maWcgPSB2aWRlbyB8fCBhdWRpbztcbiAgaWYgKG1lZGlhQ29uZmlnKSB7XG4gICAgY29uc3QgY29kZWMgPSBtZWRpYUNvbmZpZy5jb250ZW50VHlwZS5zcGxpdCgnXCInKVsxXTtcbiAgICBpZiAodmlkZW8pIHtcbiAgICAgIHJldHVybiBgciR7dmlkZW8uaGVpZ2h0fXgke3ZpZGVvLndpZHRofWYke01hdGguY2VpbCh2aWRlby5mcmFtZXJhdGUpfSR7dmlkZW8udHJhbnNmZXJGdW5jdGlvbiB8fCAnc2QnfV8ke2NvZGVjfV8ke01hdGguY2VpbCh2aWRlby5iaXRyYXRlIC8gMWU1KX1gO1xuICAgIH1cbiAgICBpZiAoYXVkaW8pIHtcbiAgICAgIHJldHVybiBgYyR7YXVkaW8uY2hhbm5lbHN9JHthdWRpby5zcGF0aWFsUmVuZGVyaW5nID8gJ3MnIDogJ24nfV8ke2NvZGVjfWA7XG4gICAgfVxuICB9XG4gIHJldHVybiAnJztcbn1cblxuLyoqXG4gKiBAcmV0dXJucyBXaGV0aGVyIHdlIGNhbiBkZXRlY3QgYW5kIHZhbGlkYXRlIEhEUiBjYXBhYmlsaXR5IHdpdGhpbiB0aGUgd2luZG93IGNvbnRleHRcbiAqL1xuZnVuY3Rpb24gaXNIZHJTdXBwb3J0ZWQoKSB7XG4gIGlmICh0eXBlb2YgbWF0Y2hNZWRpYSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgIGNvbnN0IG1lZGlhUXVlcnlMaXN0ID0gbWF0Y2hNZWRpYSgnKGR5bmFtaWMtcmFuZ2U6IGhpZ2gpJyk7XG4gICAgY29uc3QgYmFkUXVlcnkgPSBtYXRjaE1lZGlhKCdiYWQgcXVlcnknKTtcbiAgICBpZiAobWVkaWFRdWVyeUxpc3QubWVkaWEgIT09IGJhZFF1ZXJ5Lm1lZGlhKSB7XG4gICAgICByZXR1cm4gbWVkaWFRdWVyeUxpc3QubWF0Y2hlcyA9PT0gdHJ1ZTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGZhbHNlO1xufVxuXG4vKipcbiAqIFNhbml0aXplcyBpbnB1dHMgdG8gcmV0dXJuIHRoZSBhY3RpdmUgdmlkZW8gc2VsZWN0aW9uIG9wdGlvbnMgZm9yIEhEUi9TRFIuXG4gKiBXaGVuIGJvdGggaW5wdXRzIGFyZSBudWxsOlxuICpcbiAqICAgIGB7IHByZWZlckhEUjogZmFsc2UsIGFsbG93ZWRWaWRlb1JhbmdlczogW10gfWBcbiAqXG4gKiBXaGVuIGBjdXJyZW50VmlkZW9SYW5nZWAgbm9uLW51bGwsIG1haW50YWluIHRoZSBhY3RpdmUgcmFuZ2U6XG4gKlxuICogICAgYHsgcHJlZmVySERSOiBjdXJyZW50VmlkZW9SYW5nZSAhPT0gJ1NEUicsIGFsbG93ZWRWaWRlb1JhbmdlczogW2N1cnJlbnRWaWRlb1JhbmdlXSB9YFxuICpcbiAqIFdoZW4gVmlkZW9TZWxlY3Rpb25PcHRpb24gbm9uLW51bGw6XG4gKlxuICogIC0gQWxsb3cgYWxsIHZpZGVvIHJhbmdlcyBpZiBgYWxsb3dlZFZpZGVvUmFuZ2VzYCB1bnNwZWNpZmllZC5cbiAqICAtIElmIGBwcmVmZXJIRFJgIGlzIG5vbi1udWxsIHVzZSB0aGUgdmFsdWUgdG8gZmlsdGVyIGBhbGxvd2VkVmlkZW9SYW5nZXNgLlxuICogIC0gRWxzZSBjaGVjayB3aW5kb3cgZm9yIEhEUiBzdXBwb3J0IGFuZCBzZXQgYHByZWZlckhEUmAgdG8gdGhlIHJlc3VsdC5cbiAqXG4gKiBAcGFyYW0gY3VycmVudFZpZGVvUmFuZ2VcbiAqIEBwYXJhbSB2aWRlb1ByZWZlcmVuY2VcbiAqL1xuZnVuY3Rpb24gZ2V0VmlkZW9TZWxlY3Rpb25PcHRpb25zKGN1cnJlbnRWaWRlb1JhbmdlLCB2aWRlb1ByZWZlcmVuY2UpIHtcbiAgbGV0IHByZWZlckhEUiA9IGZhbHNlO1xuICBsZXQgYWxsb3dlZFZpZGVvUmFuZ2VzID0gW107XG4gIGlmIChjdXJyZW50VmlkZW9SYW5nZSkge1xuICAgIHByZWZlckhEUiA9IGN1cnJlbnRWaWRlb1JhbmdlICE9PSAnU0RSJztcbiAgICBhbGxvd2VkVmlkZW9SYW5nZXMgPSBbY3VycmVudFZpZGVvUmFuZ2VdO1xuICB9XG4gIGlmICh2aWRlb1ByZWZlcmVuY2UpIHtcbiAgICBhbGxvd2VkVmlkZW9SYW5nZXMgPSB2aWRlb1ByZWZlcmVuY2UuYWxsb3dlZFZpZGVvUmFuZ2VzIHx8IFZpZGVvUmFuZ2VWYWx1ZXMuc2xpY2UoMCk7XG4gICAgcHJlZmVySERSID0gdmlkZW9QcmVmZXJlbmNlLnByZWZlckhEUiAhPT0gdW5kZWZpbmVkID8gdmlkZW9QcmVmZXJlbmNlLnByZWZlckhEUiA6IGlzSGRyU3VwcG9ydGVkKCk7XG4gICAgaWYgKHByZWZlckhEUikge1xuICAgICAgYWxsb3dlZFZpZGVvUmFuZ2VzID0gYWxsb3dlZFZpZGVvUmFuZ2VzLmZpbHRlcihyYW5nZSA9PiByYW5nZSAhPT0gJ1NEUicpO1xuICAgIH0gZWxzZSB7XG4gICAgICBhbGxvd2VkVmlkZW9SYW5nZXMgPSBbJ1NEUiddO1xuICAgIH1cbiAgfVxuICByZXR1cm4ge1xuICAgIHByZWZlckhEUixcbiAgICBhbGxvd2VkVmlkZW9SYW5nZXNcbiAgfTtcbn1cblxuZnVuY3Rpb24gZ2V0U3RhcnRDb2RlY1RpZXIoY29kZWNUaWVycywgY3VycmVudFZpZGVvUmFuZ2UsIGN1cnJlbnRCdywgYXVkaW9QcmVmZXJlbmNlLCB2aWRlb1ByZWZlcmVuY2UpIHtcbiAgY29uc3QgY29kZWNTZXRzID0gT2JqZWN0LmtleXMoY29kZWNUaWVycyk7XG4gIGNvbnN0IGNoYW5uZWxzUHJlZmVyZW5jZSA9IGF1ZGlvUHJlZmVyZW5jZSA9PSBudWxsID8gdm9pZCAwIDogYXVkaW9QcmVmZXJlbmNlLmNoYW5uZWxzO1xuICBjb25zdCBhdWRpb0NvZGVjUHJlZmVyZW5jZSA9IGF1ZGlvUHJlZmVyZW5jZSA9PSBudWxsID8gdm9pZCAwIDogYXVkaW9QcmVmZXJlbmNlLmF1ZGlvQ29kZWM7XG4gIGNvbnN0IHByZWZlclN0ZXJlbyA9IGNoYW5uZWxzUHJlZmVyZW5jZSAmJiBwYXJzZUludChjaGFubmVsc1ByZWZlcmVuY2UpID09PSAyO1xuICAvLyBVc2UgZmlyc3QgbGV2ZWwgc2V0IHRvIGRldGVybWluZSBzdGVyZW8sIGFuZCBtaW5pbXVtIHJlc29sdXRpb24gYW5kIGZyYW1lcmF0ZVxuICBsZXQgaGFzU3RlcmVvID0gdHJ1ZTtcbiAgbGV0IGhhc0N1cnJlbnRWaWRlb1JhbmdlID0gZmFsc2U7XG4gIGxldCBtaW5IZWlnaHQgPSBJbmZpbml0eTtcbiAgbGV0IG1pbkZyYW1lcmF0ZSA9IEluZmluaXR5O1xuICBsZXQgbWluQml0cmF0ZSA9IEluZmluaXR5O1xuICBsZXQgc2VsZWN0ZWRTY29yZSA9IDA7XG4gIGxldCB2aWRlb1JhbmdlcyA9IFtdO1xuICBjb25zdCB7XG4gICAgcHJlZmVySERSLFxuICAgIGFsbG93ZWRWaWRlb1Jhbmdlc1xuICB9ID0gZ2V0VmlkZW9TZWxlY3Rpb25PcHRpb25zKGN1cnJlbnRWaWRlb1JhbmdlLCB2aWRlb1ByZWZlcmVuY2UpO1xuICBmb3IgKGxldCBpID0gY29kZWNTZXRzLmxlbmd0aDsgaS0tOykge1xuICAgIGNvbnN0IHRpZXIgPSBjb2RlY1RpZXJzW2NvZGVjU2V0c1tpXV07XG4gICAgaGFzU3RlcmVvID0gdGllci5jaGFubmVsc1syXSA+IDA7XG4gICAgbWluSGVpZ2h0ID0gTWF0aC5taW4obWluSGVpZ2h0LCB0aWVyLm1pbkhlaWdodCk7XG4gICAgbWluRnJhbWVyYXRlID0gTWF0aC5taW4obWluRnJhbWVyYXRlLCB0aWVyLm1pbkZyYW1lcmF0ZSk7XG4gICAgbWluQml0cmF0ZSA9IE1hdGgubWluKG1pbkJpdHJhdGUsIHRpZXIubWluQml0cmF0ZSk7XG4gICAgY29uc3QgbWF0Y2hpbmdWaWRlb1JhbmdlcyA9IGFsbG93ZWRWaWRlb1Jhbmdlcy5maWx0ZXIocmFuZ2UgPT4gdGllci52aWRlb1Jhbmdlc1tyYW5nZV0gPiAwKTtcbiAgICBpZiAobWF0Y2hpbmdWaWRlb1Jhbmdlcy5sZW5ndGggPiAwKSB7XG4gICAgICBoYXNDdXJyZW50VmlkZW9SYW5nZSA9IHRydWU7XG4gICAgICB2aWRlb1JhbmdlcyA9IG1hdGNoaW5nVmlkZW9SYW5nZXM7XG4gICAgfVxuICB9XG4gIG1pbkhlaWdodCA9IGlzRmluaXRlTnVtYmVyKG1pbkhlaWdodCkgPyBtaW5IZWlnaHQgOiAwO1xuICBtaW5GcmFtZXJhdGUgPSBpc0Zpbml0ZU51bWJlcihtaW5GcmFtZXJhdGUpID8gbWluRnJhbWVyYXRlIDogMDtcbiAgY29uc3QgbWF4SGVpZ2h0ID0gTWF0aC5tYXgoMTA4MCwgbWluSGVpZ2h0KTtcbiAgY29uc3QgbWF4RnJhbWVyYXRlID0gTWF0aC5tYXgoMzAsIG1pbkZyYW1lcmF0ZSk7XG4gIG1pbkJpdHJhdGUgPSBpc0Zpbml0ZU51bWJlcihtaW5CaXRyYXRlKSA/IG1pbkJpdHJhdGUgOiBjdXJyZW50Qnc7XG4gIGN1cnJlbnRCdyA9IE1hdGgubWF4KG1pbkJpdHJhdGUsIGN1cnJlbnRCdyk7XG4gIC8vIElmIHRoZXJlIGFyZSBubyB2YXJpYW50cyB3aXRoIG1hdGNoaW5nIHByZWZlcmVuY2UsIHNldCBjdXJyZW50VmlkZW9SYW5nZSB0byB1bmRlZmluZWRcbiAgaWYgKCFoYXNDdXJyZW50VmlkZW9SYW5nZSkge1xuICAgIGN1cnJlbnRWaWRlb1JhbmdlID0gdW5kZWZpbmVkO1xuICAgIHZpZGVvUmFuZ2VzID0gW107XG4gIH1cbiAgY29uc3QgY29kZWNTZXQgPSBjb2RlY1NldHMucmVkdWNlKChzZWxlY3RlZCwgY2FuZGlkYXRlKSA9PiB7XG4gICAgLy8gUmVtb3ZlIGNhbmRpYXRlcyB3aGljaCBkbyBub3QgbWVldCBiaXRyYXRlLCBkZWZhdWx0IGF1ZGlvLCBzdGVyZW8gb3IgY2hhbm5lbHMgcHJlZmVyZW5jZSwgMTA4MHAgb3IgbG93ZXIsIDMwZnBzIG9yIGxvd2VyLCBvciBTRFIvSERSIHNlbGVjdGlvbiBpZiBwcmVzZW50XG4gICAgY29uc3QgY2FuZGlkYXRlVGllciA9IGNvZGVjVGllcnNbY2FuZGlkYXRlXTtcbiAgICBpZiAoY2FuZGlkYXRlID09PSBzZWxlY3RlZCkge1xuICAgICAgcmV0dXJuIHNlbGVjdGVkO1xuICAgIH1cbiAgICBpZiAoY2FuZGlkYXRlVGllci5taW5CaXRyYXRlID4gY3VycmVudEJ3KSB7XG4gICAgICBsb2dTdGFydENvZGVjQ2FuZGlkYXRlSWdub3JlZChjYW5kaWRhdGUsIGBtaW4gYml0cmF0ZSBvZiAke2NhbmRpZGF0ZVRpZXIubWluQml0cmF0ZX0gPiBjdXJyZW50IGVzdGltYXRlIG9mICR7Y3VycmVudEJ3fWApO1xuICAgICAgcmV0dXJuIHNlbGVjdGVkO1xuICAgIH1cbiAgICBpZiAoIWNhbmRpZGF0ZVRpZXIuaGFzRGVmYXVsdEF1ZGlvKSB7XG4gICAgICBsb2dTdGFydENvZGVjQ2FuZGlkYXRlSWdub3JlZChjYW5kaWRhdGUsIGBubyByZW5kaXRpb25zIHdpdGggZGVmYXVsdCBvciBhdXRvLXNlbGVjdCBzb3VuZCBmb3VuZGApO1xuICAgICAgcmV0dXJuIHNlbGVjdGVkO1xuICAgIH1cbiAgICBpZiAoYXVkaW9Db2RlY1ByZWZlcmVuY2UgJiYgY2FuZGlkYXRlLmluZGV4T2YoYXVkaW9Db2RlY1ByZWZlcmVuY2Uuc3Vic3RyaW5nKDAsIDQpKSAlIDUgIT09IDApIHtcbiAgICAgIGxvZ1N0YXJ0Q29kZWNDYW5kaWRhdGVJZ25vcmVkKGNhbmRpZGF0ZSwgYGF1ZGlvIGNvZGVjIHByZWZlcmVuY2UgXCIke2F1ZGlvQ29kZWNQcmVmZXJlbmNlfVwiIG5vdCBmb3VuZGApO1xuICAgICAgcmV0dXJuIHNlbGVjdGVkO1xuICAgIH1cbiAgICBpZiAoY2hhbm5lbHNQcmVmZXJlbmNlICYmICFwcmVmZXJTdGVyZW8pIHtcbiAgICAgIGlmICghY2FuZGlkYXRlVGllci5jaGFubmVsc1tjaGFubmVsc1ByZWZlcmVuY2VdKSB7XG4gICAgICAgIGxvZ1N0YXJ0Q29kZWNDYW5kaWRhdGVJZ25vcmVkKGNhbmRpZGF0ZSwgYG5vIHJlbmRpdGlvbnMgd2l0aCAke2NoYW5uZWxzUHJlZmVyZW5jZX0gY2hhbm5lbCBzb3VuZCBmb3VuZCAoY2hhbm5lbHMgb3B0aW9uczogJHtPYmplY3Qua2V5cyhjYW5kaWRhdGVUaWVyLmNoYW5uZWxzKX0pYCk7XG4gICAgICAgIHJldHVybiBzZWxlY3RlZDtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKCghYXVkaW9Db2RlY1ByZWZlcmVuY2UgfHwgcHJlZmVyU3RlcmVvKSAmJiBoYXNTdGVyZW8gJiYgY2FuZGlkYXRlVGllci5jaGFubmVsc1snMiddID09PSAwKSB7XG4gICAgICBsb2dTdGFydENvZGVjQ2FuZGlkYXRlSWdub3JlZChjYW5kaWRhdGUsIGBubyByZW5kaXRpb25zIHdpdGggc3RlcmVvIHNvdW5kIGZvdW5kYCk7XG4gICAgICByZXR1cm4gc2VsZWN0ZWQ7XG4gICAgfVxuICAgIGlmIChjYW5kaWRhdGVUaWVyLm1pbkhlaWdodCA+IG1heEhlaWdodCkge1xuICAgICAgbG9nU3RhcnRDb2RlY0NhbmRpZGF0ZUlnbm9yZWQoY2FuZGlkYXRlLCBgbWluIHJlc29sdXRpb24gb2YgJHtjYW5kaWRhdGVUaWVyLm1pbkhlaWdodH0gPiBtYXhpbXVtIG9mICR7bWF4SGVpZ2h0fWApO1xuICAgICAgcmV0dXJuIHNlbGVjdGVkO1xuICAgIH1cbiAgICBpZiAoY2FuZGlkYXRlVGllci5taW5GcmFtZXJhdGUgPiBtYXhGcmFtZXJhdGUpIHtcbiAgICAgIGxvZ1N0YXJ0Q29kZWNDYW5kaWRhdGVJZ25vcmVkKGNhbmRpZGF0ZSwgYG1pbiBmcmFtZXJhdGUgb2YgJHtjYW5kaWRhdGVUaWVyLm1pbkZyYW1lcmF0ZX0gPiBtYXhpbXVtIG9mICR7bWF4RnJhbWVyYXRlfWApO1xuICAgICAgcmV0dXJuIHNlbGVjdGVkO1xuICAgIH1cbiAgICBpZiAoIXZpZGVvUmFuZ2VzLnNvbWUocmFuZ2UgPT4gY2FuZGlkYXRlVGllci52aWRlb1Jhbmdlc1tyYW5nZV0gPiAwKSkge1xuICAgICAgbG9nU3RhcnRDb2RlY0NhbmRpZGF0ZUlnbm9yZWQoY2FuZGlkYXRlLCBgbm8gdmFyaWFudHMgd2l0aCBWSURFTy1SQU5HRSBvZiAke0pTT04uc3RyaW5naWZ5KHZpZGVvUmFuZ2VzKX0gZm91bmRgKTtcbiAgICAgIHJldHVybiBzZWxlY3RlZDtcbiAgICB9XG4gICAgaWYgKGNhbmRpZGF0ZVRpZXIubWF4U2NvcmUgPCBzZWxlY3RlZFNjb3JlKSB7XG4gICAgICBsb2dTdGFydENvZGVjQ2FuZGlkYXRlSWdub3JlZChjYW5kaWRhdGUsIGBtYXggc2NvcmUgb2YgJHtjYW5kaWRhdGVUaWVyLm1heFNjb3JlfSA8IHNlbGVjdGVkIG1heCBvZiAke3NlbGVjdGVkU2NvcmV9YCk7XG4gICAgICByZXR1cm4gc2VsZWN0ZWQ7XG4gICAgfVxuICAgIC8vIFJlbW92ZSBjYW5kaWF0ZXMgd2l0aCBsZXNzIHByZWZlcnJlZCBjb2RlY3Mgb3IgbW9yZSBlcnJvcnNcbiAgICBpZiAoc2VsZWN0ZWQgJiYgKGNvZGVjc1NldFNlbGVjdGlvblByZWZlcmVuY2VWYWx1ZShjYW5kaWRhdGUpID49IGNvZGVjc1NldFNlbGVjdGlvblByZWZlcmVuY2VWYWx1ZShzZWxlY3RlZCkgfHwgY2FuZGlkYXRlVGllci5mcmFnbWVudEVycm9yID4gY29kZWNUaWVyc1tzZWxlY3RlZF0uZnJhZ21lbnRFcnJvcikpIHtcbiAgICAgIHJldHVybiBzZWxlY3RlZDtcbiAgICB9XG4gICAgc2VsZWN0ZWRTY29yZSA9IGNhbmRpZGF0ZVRpZXIubWF4U2NvcmU7XG4gICAgcmV0dXJuIGNhbmRpZGF0ZTtcbiAgfSwgdW5kZWZpbmVkKTtcbiAgcmV0dXJuIHtcbiAgICBjb2RlY1NldCxcbiAgICB2aWRlb1JhbmdlcyxcbiAgICBwcmVmZXJIRFIsXG4gICAgbWluRnJhbWVyYXRlLFxuICAgIG1pbkJpdHJhdGVcbiAgfTtcbn1cbmZ1bmN0aW9uIGxvZ1N0YXJ0Q29kZWNDYW5kaWRhdGVJZ25vcmVkKGNvZGVTZXQsIHJlYXNvbikge1xuICBsb2dnZXIubG9nKGBbYWJyXSBzdGFydCBjYW5kaWRhdGVzIHdpdGggXCIke2NvZGVTZXR9XCIgaWdub3JlZCBiZWNhdXNlICR7cmVhc29ufWApO1xufVxuZnVuY3Rpb24gZ2V0QXVkaW9UcmFja3NCeUdyb3VwKGFsbEF1ZGlvVHJhY2tzKSB7XG4gIHJldHVybiBhbGxBdWRpb1RyYWNrcy5yZWR1Y2UoKGF1ZGlvVHJhY2tzQnlHcm91cCwgdHJhY2spID0+IHtcbiAgICBsZXQgdHJhY2tHcm91cCA9IGF1ZGlvVHJhY2tzQnlHcm91cC5ncm91cHNbdHJhY2suZ3JvdXBJZF07XG4gICAgaWYgKCF0cmFja0dyb3VwKSB7XG4gICAgICB0cmFja0dyb3VwID0gYXVkaW9UcmFja3NCeUdyb3VwLmdyb3Vwc1t0cmFjay5ncm91cElkXSA9IHtcbiAgICAgICAgdHJhY2tzOiBbXSxcbiAgICAgICAgY2hhbm5lbHM6IHtcbiAgICAgICAgICAyOiAwXG4gICAgICAgIH0sXG4gICAgICAgIGhhc0RlZmF1bHQ6IGZhbHNlLFxuICAgICAgICBoYXNBdXRvU2VsZWN0OiBmYWxzZVxuICAgICAgfTtcbiAgICB9XG4gICAgdHJhY2tHcm91cC50cmFja3MucHVzaCh0cmFjayk7XG4gICAgY29uc3QgY2hhbm5lbHNLZXkgPSB0cmFjay5jaGFubmVscyB8fCAnMic7XG4gICAgdHJhY2tHcm91cC5jaGFubmVsc1tjaGFubmVsc0tleV0gPSAodHJhY2tHcm91cC5jaGFubmVsc1tjaGFubmVsc0tleV0gfHwgMCkgKyAxO1xuICAgIHRyYWNrR3JvdXAuaGFzRGVmYXVsdCA9IHRyYWNrR3JvdXAuaGFzRGVmYXVsdCB8fCB0cmFjay5kZWZhdWx0O1xuICAgIHRyYWNrR3JvdXAuaGFzQXV0b1NlbGVjdCA9IHRyYWNrR3JvdXAuaGFzQXV0b1NlbGVjdCB8fCB0cmFjay5hdXRvc2VsZWN0O1xuICAgIGlmICh0cmFja0dyb3VwLmhhc0RlZmF1bHQpIHtcbiAgICAgIGF1ZGlvVHJhY2tzQnlHcm91cC5oYXNEZWZhdWx0QXVkaW8gPSB0cnVlO1xuICAgIH1cbiAgICBpZiAodHJhY2tHcm91cC5oYXNBdXRvU2VsZWN0KSB7XG4gICAgICBhdWRpb1RyYWNrc0J5R3JvdXAuaGFzQXV0b1NlbGVjdEF1ZGlvID0gdHJ1ZTtcbiAgICB9XG4gICAgcmV0dXJuIGF1ZGlvVHJhY2tzQnlHcm91cDtcbiAgfSwge1xuICAgIGhhc0RlZmF1bHRBdWRpbzogZmFsc2UsXG4gICAgaGFzQXV0b1NlbGVjdEF1ZGlvOiBmYWxzZSxcbiAgICBncm91cHM6IHt9XG4gIH0pO1xufVxuZnVuY3Rpb24gZ2V0Q29kZWNUaWVycyhsZXZlbHMsIGF1ZGlvVHJhY2tzQnlHcm91cCwgbWluQXV0b0xldmVsLCBtYXhBdXRvTGV2ZWwpIHtcbiAgcmV0dXJuIGxldmVscy5zbGljZShtaW5BdXRvTGV2ZWwsIG1heEF1dG9MZXZlbCArIDEpLnJlZHVjZSgodGllcnMsIGxldmVsKSA9PiB7XG4gICAgaWYgKCFsZXZlbC5jb2RlY1NldCkge1xuICAgICAgcmV0dXJuIHRpZXJzO1xuICAgIH1cbiAgICBjb25zdCBhdWRpb0dyb3VwcyA9IGxldmVsLmF1ZGlvR3JvdXBzO1xuICAgIGxldCB0aWVyID0gdGllcnNbbGV2ZWwuY29kZWNTZXRdO1xuICAgIGlmICghdGllcikge1xuICAgICAgdGllcnNbbGV2ZWwuY29kZWNTZXRdID0gdGllciA9IHtcbiAgICAgICAgbWluQml0cmF0ZTogSW5maW5pdHksXG4gICAgICAgIG1pbkhlaWdodDogSW5maW5pdHksXG4gICAgICAgIG1pbkZyYW1lcmF0ZTogSW5maW5pdHksXG4gICAgICAgIG1heFNjb3JlOiAwLFxuICAgICAgICB2aWRlb1Jhbmdlczoge1xuICAgICAgICAgIFNEUjogMFxuICAgICAgICB9LFxuICAgICAgICBjaGFubmVsczoge1xuICAgICAgICAgICcyJzogMFxuICAgICAgICB9LFxuICAgICAgICBoYXNEZWZhdWx0QXVkaW86ICFhdWRpb0dyb3VwcyxcbiAgICAgICAgZnJhZ21lbnRFcnJvcjogMFxuICAgICAgfTtcbiAgICB9XG4gICAgdGllci5taW5CaXRyYXRlID0gTWF0aC5taW4odGllci5taW5CaXRyYXRlLCBsZXZlbC5iaXRyYXRlKTtcbiAgICBjb25zdCBsZXNzZXJXaWR0aE9ySGVpZ2h0ID0gTWF0aC5taW4obGV2ZWwuaGVpZ2h0LCBsZXZlbC53aWR0aCk7XG4gICAgdGllci5taW5IZWlnaHQgPSBNYXRoLm1pbih0aWVyLm1pbkhlaWdodCwgbGVzc2VyV2lkdGhPckhlaWdodCk7XG4gICAgdGllci5taW5GcmFtZXJhdGUgPSBNYXRoLm1pbih0aWVyLm1pbkZyYW1lcmF0ZSwgbGV2ZWwuZnJhbWVSYXRlKTtcbiAgICB0aWVyLm1heFNjb3JlID0gTWF0aC5tYXgodGllci5tYXhTY29yZSwgbGV2ZWwuc2NvcmUpO1xuICAgIHRpZXIuZnJhZ21lbnRFcnJvciArPSBsZXZlbC5mcmFnbWVudEVycm9yO1xuICAgIHRpZXIudmlkZW9SYW5nZXNbbGV2ZWwudmlkZW9SYW5nZV0gPSAodGllci52aWRlb1Jhbmdlc1tsZXZlbC52aWRlb1JhbmdlXSB8fCAwKSArIDE7XG4gICAgaWYgKGF1ZGlvR3JvdXBzKSB7XG4gICAgICBhdWRpb0dyb3Vwcy5mb3JFYWNoKGF1ZGlvR3JvdXBJZCA9PiB7XG4gICAgICAgIGlmICghYXVkaW9Hcm91cElkKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGF1ZGlvR3JvdXAgPSBhdWRpb1RyYWNrc0J5R3JvdXAuZ3JvdXBzW2F1ZGlvR3JvdXBJZF07XG4gICAgICAgIGlmICghYXVkaW9Hcm91cCkge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICAvLyBEZWZhdWx0IGF1ZGlvIGlzIGFueSBncm91cCB3aXRoIERFRkFVTFQ9WUVTLCBvciBpZiBtaXNzaW5nIHRoZW4gYW55IGdyb3VwIHdpdGggQVVUT1NFTEVDVD1ZRVMsIG9yIGFsbCB2YXJpYW50c1xuICAgICAgICB0aWVyLmhhc0RlZmF1bHRBdWRpbyA9IHRpZXIuaGFzRGVmYXVsdEF1ZGlvIHx8IGF1ZGlvVHJhY2tzQnlHcm91cC5oYXNEZWZhdWx0QXVkaW8gPyBhdWRpb0dyb3VwLmhhc0RlZmF1bHQgOiBhdWRpb0dyb3VwLmhhc0F1dG9TZWxlY3QgfHwgIWF1ZGlvVHJhY2tzQnlHcm91cC5oYXNEZWZhdWx0QXVkaW8gJiYgIWF1ZGlvVHJhY2tzQnlHcm91cC5oYXNBdXRvU2VsZWN0QXVkaW87XG4gICAgICAgIE9iamVjdC5rZXlzKGF1ZGlvR3JvdXAuY2hhbm5lbHMpLmZvckVhY2goY2hhbm5lbHMgPT4ge1xuICAgICAgICAgIHRpZXIuY2hhbm5lbHNbY2hhbm5lbHNdID0gKHRpZXIuY2hhbm5lbHNbY2hhbm5lbHNdIHx8IDApICsgYXVkaW9Hcm91cC5jaGFubmVsc1tjaGFubmVsc107XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG4gICAgfVxuICAgIHJldHVybiB0aWVycztcbiAgfSwge30pO1xufVxuZnVuY3Rpb24gZmluZE1hdGNoaW5nT3B0aW9uKG9wdGlvbiwgdHJhY2tzLCBtYXRjaFByZWRpY2F0ZSkge1xuICBpZiAoJ2F0dHJzJyBpbiBvcHRpb24pIHtcbiAgICBjb25zdCBpbmRleCA9IHRyYWNrcy5pbmRleE9mKG9wdGlvbik7XG4gICAgaWYgKGluZGV4ICE9PSAtMSkge1xuICAgICAgcmV0dXJuIGluZGV4O1xuICAgIH1cbiAgfVxuICBmb3IgKGxldCBpID0gMDsgaSA8IHRyYWNrcy5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IHRyYWNrID0gdHJhY2tzW2ldO1xuICAgIGlmIChtYXRjaGVzT3B0aW9uKG9wdGlvbiwgdHJhY2ssIG1hdGNoUHJlZGljYXRlKSkge1xuICAgICAgcmV0dXJuIGk7XG4gICAgfVxuICB9XG4gIHJldHVybiAtMTtcbn1cbmZ1bmN0aW9uIG1hdGNoZXNPcHRpb24ob3B0aW9uLCB0cmFjaywgbWF0Y2hQcmVkaWNhdGUpIHtcbiAgY29uc3Qge1xuICAgIGdyb3VwSWQsXG4gICAgbmFtZSxcbiAgICBsYW5nLFxuICAgIGFzc29jTGFuZyxcbiAgICBjaGFyYWN0ZXJpc3RpY3MsXG4gICAgZGVmYXVsdDogaXNEZWZhdWx0XG4gIH0gPSBvcHRpb247XG4gIGNvbnN0IGZvcmNlZCA9IG9wdGlvbi5mb3JjZWQ7XG4gIHJldHVybiAoZ3JvdXBJZCA9PT0gdW5kZWZpbmVkIHx8IHRyYWNrLmdyb3VwSWQgPT09IGdyb3VwSWQpICYmIChuYW1lID09PSB1bmRlZmluZWQgfHwgdHJhY2submFtZSA9PT0gbmFtZSkgJiYgKGxhbmcgPT09IHVuZGVmaW5lZCB8fCB0cmFjay5sYW5nID09PSBsYW5nKSAmJiAobGFuZyA9PT0gdW5kZWZpbmVkIHx8IHRyYWNrLmFzc29jTGFuZyA9PT0gYXNzb2NMYW5nKSAmJiAoaXNEZWZhdWx0ID09PSB1bmRlZmluZWQgfHwgdHJhY2suZGVmYXVsdCA9PT0gaXNEZWZhdWx0KSAmJiAoZm9yY2VkID09PSB1bmRlZmluZWQgfHwgdHJhY2suZm9yY2VkID09PSBmb3JjZWQpICYmIChjaGFyYWN0ZXJpc3RpY3MgPT09IHVuZGVmaW5lZCB8fCBjaGFyYWN0ZXJpc3RpY3NNYXRjaChjaGFyYWN0ZXJpc3RpY3MsIHRyYWNrLmNoYXJhY3RlcmlzdGljcykpICYmIChtYXRjaFByZWRpY2F0ZSA9PT0gdW5kZWZpbmVkIHx8IG1hdGNoUHJlZGljYXRlKG9wdGlvbiwgdHJhY2spKTtcbn1cbmZ1bmN0aW9uIGNoYXJhY3RlcmlzdGljc01hdGNoKGNoYXJhY3RlcmlzdGljc0EsIGNoYXJhY3RlcmlzdGljc0IgPSAnJykge1xuICBjb25zdCBhcnJBID0gY2hhcmFjdGVyaXN0aWNzQS5zcGxpdCgnLCcpO1xuICBjb25zdCBhcnJCID0gY2hhcmFjdGVyaXN0aWNzQi5zcGxpdCgnLCcpO1xuICAvLyBFeHBlY3RzIGVhY2ggaXRlbSB0byBiZSB1bmlxdWU6XG4gIHJldHVybiBhcnJBLmxlbmd0aCA9PT0gYXJyQi5sZW5ndGggJiYgIWFyckEuc29tZShlbCA9PiBhcnJCLmluZGV4T2YoZWwpID09PSAtMSk7XG59XG5mdW5jdGlvbiBhdWRpb01hdGNoUHJlZGljYXRlKG9wdGlvbiwgdHJhY2spIHtcbiAgY29uc3Qge1xuICAgIGF1ZGlvQ29kZWMsXG4gICAgY2hhbm5lbHNcbiAgfSA9IG9wdGlvbjtcbiAgcmV0dXJuIChhdWRpb0NvZGVjID09PSB1bmRlZmluZWQgfHwgKHRyYWNrLmF1ZGlvQ29kZWMgfHwgJycpLnN1YnN0cmluZygwLCA0KSA9PT0gYXVkaW9Db2RlYy5zdWJzdHJpbmcoMCwgNCkpICYmIChjaGFubmVscyA9PT0gdW5kZWZpbmVkIHx8IGNoYW5uZWxzID09PSAodHJhY2suY2hhbm5lbHMgfHwgJzInKSk7XG59XG5mdW5jdGlvbiBmaW5kQ2xvc2VzdExldmVsV2l0aEF1ZGlvR3JvdXAob3B0aW9uLCBsZXZlbHMsIGFsbEF1ZGlvVHJhY2tzLCBzZWFyY2hJbmRleCwgbWF0Y2hQcmVkaWNhdGUpIHtcbiAgY29uc3QgY3VycmVudExldmVsID0gbGV2ZWxzW3NlYXJjaEluZGV4XTtcbiAgLy8gQXJlIHRoZXJlIHZhcmlhbnRzIHdpdGggc2FtZSBVUkkgYXMgY3VycmVudCBsZXZlbD9cbiAgLy8gSWYgc28sIGZpbmQgYSBtYXRjaCB0aGF0IGRvZXMgbm90IHJlcXVpcmUgYW55IGxldmVsIFVSSSBjaGFuZ2VcbiAgY29uc3QgdmFyaWFudHMgPSBsZXZlbHMucmVkdWNlKCh2YXJpYW50TWFwLCBsZXZlbCwgaW5kZXgpID0+IHtcbiAgICBjb25zdCB1cmkgPSBsZXZlbC51cmk7XG4gICAgY29uc3QgcmVuZGl0aW9ucyA9IHZhcmlhbnRNYXBbdXJpXSB8fCAodmFyaWFudE1hcFt1cmldID0gW10pO1xuICAgIHJlbmRpdGlvbnMucHVzaChpbmRleCk7XG4gICAgcmV0dXJuIHZhcmlhbnRNYXA7XG4gIH0sIHt9KTtcbiAgY29uc3QgcmVuZGl0aW9ucyA9IHZhcmlhbnRzW2N1cnJlbnRMZXZlbC51cmldO1xuICBpZiAocmVuZGl0aW9ucy5sZW5ndGggPiAxKSB7XG4gICAgc2VhcmNoSW5kZXggPSBNYXRoLm1heC5hcHBseShNYXRoLCByZW5kaXRpb25zKTtcbiAgfVxuICAvLyBGaW5kIGJlc3QgbWF0Y2hcbiAgY29uc3QgY3VycmVudFZpZGVvUmFuZ2UgPSBjdXJyZW50TGV2ZWwudmlkZW9SYW5nZTtcbiAgY29uc3QgY3VycmVudEZyYW1lUmF0ZSA9IGN1cnJlbnRMZXZlbC5mcmFtZVJhdGU7XG4gIGNvbnN0IGN1cnJlbnRWaWRlb0NvZGVjID0gY3VycmVudExldmVsLmNvZGVjU2V0LnN1YnN0cmluZygwLCA0KTtcbiAgY29uc3QgbWF0Y2hpbmdWaWRlbyA9IHNlYXJjaERvd25BbmRVcExpc3QobGV2ZWxzLCBzZWFyY2hJbmRleCwgbGV2ZWwgPT4ge1xuICAgIGlmIChsZXZlbC52aWRlb1JhbmdlICE9PSBjdXJyZW50VmlkZW9SYW5nZSB8fCBsZXZlbC5mcmFtZVJhdGUgIT09IGN1cnJlbnRGcmFtZVJhdGUgfHwgbGV2ZWwuY29kZWNTZXQuc3Vic3RyaW5nKDAsIDQpICE9PSBjdXJyZW50VmlkZW9Db2RlYykge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBjb25zdCBhdWRpb0dyb3VwcyA9IGxldmVsLmF1ZGlvR3JvdXBzO1xuICAgIGNvbnN0IHRyYWNrcyA9IGFsbEF1ZGlvVHJhY2tzLmZpbHRlcih0cmFjayA9PiAhYXVkaW9Hcm91cHMgfHwgYXVkaW9Hcm91cHMuaW5kZXhPZih0cmFjay5ncm91cElkKSAhPT0gLTEpO1xuICAgIHJldHVybiBmaW5kTWF0Y2hpbmdPcHRpb24ob3B0aW9uLCB0cmFja3MsIG1hdGNoUHJlZGljYXRlKSA+IC0xO1xuICB9KTtcbiAgaWYgKG1hdGNoaW5nVmlkZW8gPiAtMSkge1xuICAgIHJldHVybiBtYXRjaGluZ1ZpZGVvO1xuICB9XG4gIHJldHVybiBzZWFyY2hEb3duQW5kVXBMaXN0KGxldmVscywgc2VhcmNoSW5kZXgsIGxldmVsID0+IHtcbiAgICBjb25zdCBhdWRpb0dyb3VwcyA9IGxldmVsLmF1ZGlvR3JvdXBzO1xuICAgIGNvbnN0IHRyYWNrcyA9IGFsbEF1ZGlvVHJhY2tzLmZpbHRlcih0cmFjayA9PiAhYXVkaW9Hcm91cHMgfHwgYXVkaW9Hcm91cHMuaW5kZXhPZih0cmFjay5ncm91cElkKSAhPT0gLTEpO1xuICAgIHJldHVybiBmaW5kTWF0Y2hpbmdPcHRpb24ob3B0aW9uLCB0cmFja3MsIG1hdGNoUHJlZGljYXRlKSA+IC0xO1xuICB9KTtcbn1cbmZ1bmN0aW9uIHNlYXJjaERvd25BbmRVcExpc3QoYXJyLCBzZWFyY2hJbmRleCwgcHJlZGljYXRlKSB7XG4gIGZvciAobGV0IGkgPSBzZWFyY2hJbmRleDsgaTsgaS0tKSB7XG4gICAgaWYgKHByZWRpY2F0ZShhcnJbaV0pKSB7XG4gICAgICByZXR1cm4gaTtcbiAgICB9XG4gIH1cbiAgZm9yIChsZXQgaSA9IHNlYXJjaEluZGV4ICsgMTsgaSA8IGFyci5sZW5ndGg7IGkrKykge1xuICAgIGlmIChwcmVkaWNhdGUoYXJyW2ldKSkge1xuICAgICAgcmV0dXJuIGk7XG4gICAgfVxuICB9XG4gIHJldHVybiAtMTtcbn1cblxuY2xhc3MgQWJyQ29udHJvbGxlciB7XG4gIGNvbnN0cnVjdG9yKF9obHMpIHtcbiAgICB0aGlzLmhscyA9IHZvaWQgMDtcbiAgICB0aGlzLmxhc3RMZXZlbExvYWRTZWMgPSAwO1xuICAgIHRoaXMubGFzdExvYWRlZEZyYWdMZXZlbCA9IC0xO1xuICAgIHRoaXMuZmlyc3RTZWxlY3Rpb24gPSAtMTtcbiAgICB0aGlzLl9uZXh0QXV0b0xldmVsID0gLTE7XG4gICAgdGhpcy5uZXh0QXV0b0xldmVsS2V5ID0gJyc7XG4gICAgdGhpcy5hdWRpb1RyYWNrc0J5R3JvdXAgPSBudWxsO1xuICAgIHRoaXMuY29kZWNUaWVycyA9IG51bGw7XG4gICAgdGhpcy50aW1lciA9IC0xO1xuICAgIHRoaXMuZnJhZ0N1cnJlbnQgPSBudWxsO1xuICAgIHRoaXMucGFydEN1cnJlbnQgPSBudWxsO1xuICAgIHRoaXMuYml0cmF0ZVRlc3REZWxheSA9IDA7XG4gICAgdGhpcy5id0VzdGltYXRvciA9IHZvaWQgMDtcbiAgICAvKlxuICAgICAgICBUaGlzIG1ldGhvZCBtb25pdG9ycyB0aGUgZG93bmxvYWQgcmF0ZSBvZiB0aGUgY3VycmVudCBmcmFnbWVudCwgYW5kIHdpbGwgZG93bnN3aXRjaCBpZiB0aGF0IGZyYWdtZW50IHdpbGwgbm90IGxvYWRcbiAgICAgICAgcXVpY2tseSBlbm91Z2ggdG8gcHJldmVudCB1bmRlcmJ1ZmZlcmluZ1xuICAgICAgKi9cbiAgICB0aGlzLl9hYmFuZG9uUnVsZXNDaGVjayA9ICgpID0+IHtcbiAgICAgIGNvbnN0IHtcbiAgICAgICAgZnJhZ0N1cnJlbnQ6IGZyYWcsXG4gICAgICAgIHBhcnRDdXJyZW50OiBwYXJ0LFxuICAgICAgICBobHNcbiAgICAgIH0gPSB0aGlzO1xuICAgICAgY29uc3Qge1xuICAgICAgICBhdXRvTGV2ZWxFbmFibGVkLFxuICAgICAgICBtZWRpYVxuICAgICAgfSA9IGhscztcbiAgICAgIGlmICghZnJhZyB8fCAhbWVkaWEpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY29uc3Qgbm93ID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gICAgICBjb25zdCBzdGF0cyA9IHBhcnQgPyBwYXJ0LnN0YXRzIDogZnJhZy5zdGF0cztcbiAgICAgIGNvbnN0IGR1cmF0aW9uID0gcGFydCA/IHBhcnQuZHVyYXRpb24gOiBmcmFnLmR1cmF0aW9uO1xuICAgICAgY29uc3QgdGltZUxvYWRpbmcgPSBub3cgLSBzdGF0cy5sb2FkaW5nLnN0YXJ0O1xuICAgICAgY29uc3QgbWluQXV0b0xldmVsID0gaGxzLm1pbkF1dG9MZXZlbDtcbiAgICAgIC8vIElmIGZyYWcgbG9hZGluZyBpcyBhYm9ydGVkLCBjb21wbGV0ZSwgb3IgZnJvbSBsb3dlc3QgbGV2ZWwsIHN0b3AgdGltZXIgYW5kIHJldHVyblxuICAgICAgaWYgKHN0YXRzLmFib3J0ZWQgfHwgc3RhdHMubG9hZGVkICYmIHN0YXRzLmxvYWRlZCA9PT0gc3RhdHMudG90YWwgfHwgZnJhZy5sZXZlbCA8PSBtaW5BdXRvTGV2ZWwpIHtcbiAgICAgICAgdGhpcy5jbGVhclRpbWVyKCk7XG4gICAgICAgIC8vIHJlc2V0IGZvcmNlZCBhdXRvIGxldmVsIHZhbHVlIHNvIHRoYXQgbmV4dCBsZXZlbCB3aWxsIGJlIHNlbGVjdGVkXG4gICAgICAgIHRoaXMuX25leHRBdXRvTGV2ZWwgPSAtMTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICAvLyBUaGlzIGNoZWNrIG9ubHkgcnVucyBpZiB3ZSdyZSBpbiBBQlIgbW9kZSBhbmQgYWN0dWFsbHkgcGxheWluZ1xuICAgICAgaWYgKCFhdXRvTGV2ZWxFbmFibGVkIHx8IG1lZGlhLnBhdXNlZCB8fCAhbWVkaWEucGxheWJhY2tSYXRlIHx8ICFtZWRpYS5yZWFkeVN0YXRlKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGJ1ZmZlckluZm8gPSBobHMubWFpbkZvcndhcmRCdWZmZXJJbmZvO1xuICAgICAgaWYgKGJ1ZmZlckluZm8gPT09IG51bGwpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY29uc3QgdHRmYkVzdGltYXRlID0gdGhpcy5id0VzdGltYXRvci5nZXRFc3RpbWF0ZVRURkIoKTtcbiAgICAgIGNvbnN0IHBsYXliYWNrUmF0ZSA9IE1hdGguYWJzKG1lZGlhLnBsYXliYWNrUmF0ZSk7XG4gICAgICAvLyBUbyBtYWludGFpbiBzdGFibGUgYWRhcHRpdmUgcGxheWJhY2ssIG9ubHkgYmVnaW4gbW9uaXRvcmluZyBmcmFnIGxvYWRpbmcgYWZ0ZXIgaGFsZiBvciBtb3JlIG9mIGl0cyBwbGF5YmFjayBkdXJhdGlvbiBoYXMgcGFzc2VkXG4gICAgICBpZiAodGltZUxvYWRpbmcgPD0gTWF0aC5tYXgodHRmYkVzdGltYXRlLCAxMDAwICogKGR1cmF0aW9uIC8gKHBsYXliYWNrUmF0ZSAqIDIpKSkpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICAvLyBidWZmZXJTdGFydmF0aW9uRGVsYXkgaXMgYW4gZXN0aW1hdGUgb2YgdGhlIGFtb3VudCB0aW1lIChpbiBzZWNvbmRzKSBpdCB3aWxsIHRha2UgdG8gZXhoYXVzdCB0aGUgYnVmZmVyXG4gICAgICBjb25zdCBidWZmZXJTdGFydmF0aW9uRGVsYXkgPSBidWZmZXJJbmZvLmxlbiAvIHBsYXliYWNrUmF0ZTtcbiAgICAgIGNvbnN0IHR0ZmIgPSBzdGF0cy5sb2FkaW5nLmZpcnN0ID8gc3RhdHMubG9hZGluZy5maXJzdCAtIHN0YXRzLmxvYWRpbmcuc3RhcnQgOiAtMTtcbiAgICAgIGNvbnN0IGxvYWRlZEZpcnN0Qnl0ZSA9IHN0YXRzLmxvYWRlZCAmJiB0dGZiID4gLTE7XG4gICAgICBjb25zdCBid0VzdGltYXRlID0gdGhpcy5nZXRCd0VzdGltYXRlKCk7XG4gICAgICBjb25zdCBsZXZlbHMgPSBobHMubGV2ZWxzO1xuICAgICAgY29uc3QgbGV2ZWwgPSBsZXZlbHNbZnJhZy5sZXZlbF07XG4gICAgICBjb25zdCBleHBlY3RlZExlbiA9IHN0YXRzLnRvdGFsIHx8IE1hdGgubWF4KHN0YXRzLmxvYWRlZCwgTWF0aC5yb3VuZChkdXJhdGlvbiAqIGxldmVsLmF2ZXJhZ2VCaXRyYXRlIC8gOCkpO1xuICAgICAgbGV0IHRpbWVTdHJlYW1pbmcgPSBsb2FkZWRGaXJzdEJ5dGUgPyB0aW1lTG9hZGluZyAtIHR0ZmIgOiB0aW1lTG9hZGluZztcbiAgICAgIGlmICh0aW1lU3RyZWFtaW5nIDwgMSAmJiBsb2FkZWRGaXJzdEJ5dGUpIHtcbiAgICAgICAgdGltZVN0cmVhbWluZyA9IE1hdGgubWluKHRpbWVMb2FkaW5nLCBzdGF0cy5sb2FkZWQgKiA4IC8gYndFc3RpbWF0ZSk7XG4gICAgICB9XG4gICAgICBjb25zdCBsb2FkUmF0ZSA9IGxvYWRlZEZpcnN0Qnl0ZSA/IHN0YXRzLmxvYWRlZCAqIDEwMDAgLyB0aW1lU3RyZWFtaW5nIDogMDtcbiAgICAgIC8vIGZyYWdMb2FkRGVsYXkgaXMgYW4gZXN0aW1hdGUgb2YgdGhlIHRpbWUgKGluIHNlY29uZHMpIGl0IHdpbGwgdGFrZSB0byBidWZmZXIgdGhlIHJlbWFpbmRlciBvZiB0aGUgZnJhZ21lbnRcbiAgICAgIGNvbnN0IGZyYWdMb2FkZWREZWxheSA9IGxvYWRSYXRlID8gKGV4cGVjdGVkTGVuIC0gc3RhdHMubG9hZGVkKSAvIGxvYWRSYXRlIDogZXhwZWN0ZWRMZW4gKiA4IC8gYndFc3RpbWF0ZSArIHR0ZmJFc3RpbWF0ZSAvIDEwMDA7XG4gICAgICAvLyBPbmx5IGRvd25zd2l0Y2ggaWYgdGhlIHRpbWUgdG8gZmluaXNoIGxvYWRpbmcgdGhlIGN1cnJlbnQgZnJhZ21lbnQgaXMgZ3JlYXRlciB0aGFuIHRoZSBhbW91bnQgb2YgYnVmZmVyIGxlZnRcbiAgICAgIGlmIChmcmFnTG9hZGVkRGVsYXkgPD0gYnVmZmVyU3RhcnZhdGlvbkRlbGF5KSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGJ3ZSA9IGxvYWRSYXRlID8gbG9hZFJhdGUgKiA4IDogYndFc3RpbWF0ZTtcbiAgICAgIGxldCBmcmFnTGV2ZWxOZXh0TG9hZGVkRGVsYXkgPSBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFk7XG4gICAgICBsZXQgbmV4dExvYWRMZXZlbDtcbiAgICAgIC8vIEl0ZXJhdGUgdGhyb3VnaCBsb3dlciBsZXZlbCBhbmQgdHJ5IHRvIGZpbmQgdGhlIGxhcmdlc3Qgb25lIHRoYXQgYXZvaWRzIHJlYnVmZmVyaW5nXG4gICAgICBmb3IgKG5leHRMb2FkTGV2ZWwgPSBmcmFnLmxldmVsIC0gMTsgbmV4dExvYWRMZXZlbCA+IG1pbkF1dG9MZXZlbDsgbmV4dExvYWRMZXZlbC0tKSB7XG4gICAgICAgIC8vIGNvbXB1dGUgdGltZSB0byBsb2FkIG5leHQgZnJhZ21lbnQgYXQgbG93ZXIgbGV2ZWxcbiAgICAgICAgLy8gOCA9IGJpdHMgcGVyIGJ5dGUgKGJwcy9CcHMpXG4gICAgICAgIGNvbnN0IGxldmVsTmV4dEJpdHJhdGUgPSBsZXZlbHNbbmV4dExvYWRMZXZlbF0ubWF4Qml0cmF0ZTtcbiAgICAgICAgZnJhZ0xldmVsTmV4dExvYWRlZERlbGF5ID0gdGhpcy5nZXRUaW1lVG9Mb2FkRnJhZyh0dGZiRXN0aW1hdGUgLyAxMDAwLCBid2UsIGR1cmF0aW9uICogbGV2ZWxOZXh0Qml0cmF0ZSwgIWxldmVsc1tuZXh0TG9hZExldmVsXS5kZXRhaWxzKTtcbiAgICAgICAgaWYgKGZyYWdMZXZlbE5leHRMb2FkZWREZWxheSA8IGJ1ZmZlclN0YXJ2YXRpb25EZWxheSkge1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICAvLyBPbmx5IGVtZXJnZW5jeSBzd2l0Y2ggZG93biBpZiBpdCB0YWtlcyBsZXNzIHRpbWUgdG8gbG9hZCBhIG5ldyBmcmFnbWVudCBhdCBsb3dlc3QgbGV2ZWwgaW5zdGVhZCBvZiBjb250aW51aW5nXG4gICAgICAvLyB0byBsb2FkIHRoZSBjdXJyZW50IG9uZVxuICAgICAgaWYgKGZyYWdMZXZlbE5leHRMb2FkZWREZWxheSA+PSBmcmFnTG9hZGVkRGVsYXkpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICAvLyBpZiBlc3RpbWF0ZWQgbG9hZCB0aW1lIG9mIG5ldyBzZWdtZW50IGlzIGNvbXBsZXRlbHkgdW5yZWFzb25hYmxlLCBpZ25vcmUgYW5kIGRvIG5vdCBlbWVyZ2VuY3kgc3dpdGNoIGRvd25cbiAgICAgIGlmIChmcmFnTGV2ZWxOZXh0TG9hZGVkRGVsYXkgPiBkdXJhdGlvbiAqIDEwKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGhscy5uZXh0TG9hZExldmVsID0gaGxzLm5leHRBdXRvTGV2ZWwgPSBuZXh0TG9hZExldmVsO1xuICAgICAgaWYgKGxvYWRlZEZpcnN0Qnl0ZSkge1xuICAgICAgICAvLyBJZiB0aGVyZSBoYXMgYmVlbiBsb2FkaW5nIHByb2dyZXNzLCBzYW1wbGUgYmFuZHdpZHRoIHVzaW5nIGxvYWRpbmcgdGltZSBvZmZzZXQgYnkgbWluaW11bSBUVEZCIHRpbWVcbiAgICAgICAgdGhpcy5id0VzdGltYXRvci5zYW1wbGUodGltZUxvYWRpbmcgLSBNYXRoLm1pbih0dGZiRXN0aW1hdGUsIHR0ZmIpLCBzdGF0cy5sb2FkZWQpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gSWYgdGhlcmUgaGFzIGJlZW4gbm8gbG9hZGluZyBwcm9ncmVzcywgc2FtcGxlIFRURkJcbiAgICAgICAgdGhpcy5id0VzdGltYXRvci5zYW1wbGVUVEZCKHRpbWVMb2FkaW5nKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IG5leHRMb2FkTGV2ZWxCaXRyYXRlID0gbGV2ZWxzW25leHRMb2FkTGV2ZWxdLm1heEJpdHJhdGU7XG4gICAgICBpZiAodGhpcy5nZXRCd0VzdGltYXRlKCkgKiB0aGlzLmhscy5jb25maWcuYWJyQmFuZFdpZHRoVXBGYWN0b3IgPiBuZXh0TG9hZExldmVsQml0cmF0ZSkge1xuICAgICAgICB0aGlzLnJlc2V0RXN0aW1hdG9yKG5leHRMb2FkTGV2ZWxCaXRyYXRlKTtcbiAgICAgIH1cbiAgICAgIHRoaXMuY2xlYXJUaW1lcigpO1xuICAgICAgbG9nZ2VyLndhcm4oYFthYnJdIEZyYWdtZW50ICR7ZnJhZy5zbn0ke3BhcnQgPyAnIHBhcnQgJyArIHBhcnQuaW5kZXggOiAnJ30gb2YgbGV2ZWwgJHtmcmFnLmxldmVsfSBpcyBsb2FkaW5nIHRvbyBzbG93bHk7XG4gICAgICBUaW1lIHRvIHVuZGVyYnVmZmVyOiAke2J1ZmZlclN0YXJ2YXRpb25EZWxheS50b0ZpeGVkKDMpfSBzXG4gICAgICBFc3RpbWF0ZWQgbG9hZCB0aW1lIGZvciBjdXJyZW50IGZyYWdtZW50OiAke2ZyYWdMb2FkZWREZWxheS50b0ZpeGVkKDMpfSBzXG4gICAgICBFc3RpbWF0ZWQgbG9hZCB0aW1lIGZvciBkb3duIHN3aXRjaCBmcmFnbWVudDogJHtmcmFnTGV2ZWxOZXh0TG9hZGVkRGVsYXkudG9GaXhlZCgzKX0gc1xuICAgICAgVFRGQiBlc3RpbWF0ZTogJHt0dGZiIHwgMH0gbXNcbiAgICAgIEN1cnJlbnQgQlcgZXN0aW1hdGU6ICR7aXNGaW5pdGVOdW1iZXIoYndFc3RpbWF0ZSkgPyBid0VzdGltYXRlIHwgMCA6ICdVbmtub3duJ30gYnBzXG4gICAgICBOZXcgQlcgZXN0aW1hdGU6ICR7dGhpcy5nZXRCd0VzdGltYXRlKCkgfCAwfSBicHNcbiAgICAgIFN3aXRjaGluZyB0byBsZXZlbCAke25leHRMb2FkTGV2ZWx9IEAgJHtuZXh0TG9hZExldmVsQml0cmF0ZSB8IDB9IGJwc2ApO1xuICAgICAgaGxzLnRyaWdnZXIoRXZlbnRzLkZSQUdfTE9BRF9FTUVSR0VOQ1lfQUJPUlRFRCwge1xuICAgICAgICBmcmFnLFxuICAgICAgICBwYXJ0LFxuICAgICAgICBzdGF0c1xuICAgICAgfSk7XG4gICAgfTtcbiAgICB0aGlzLmhscyA9IF9obHM7XG4gICAgdGhpcy5id0VzdGltYXRvciA9IHRoaXMuaW5pdEVzdGltYXRvcigpO1xuICAgIHRoaXMucmVnaXN0ZXJMaXN0ZW5lcnMoKTtcbiAgfVxuICByZXNldEVzdGltYXRvcihhYnJFd21hRGVmYXVsdEVzdGltYXRlKSB7XG4gICAgaWYgKGFickV3bWFEZWZhdWx0RXN0aW1hdGUpIHtcbiAgICAgIGxvZ2dlci5sb2coYHNldHRpbmcgaW5pdGlhbCBid2UgdG8gJHthYnJFd21hRGVmYXVsdEVzdGltYXRlfWApO1xuICAgICAgdGhpcy5obHMuY29uZmlnLmFickV3bWFEZWZhdWx0RXN0aW1hdGUgPSBhYnJFd21hRGVmYXVsdEVzdGltYXRlO1xuICAgIH1cbiAgICB0aGlzLmZpcnN0U2VsZWN0aW9uID0gLTE7XG4gICAgdGhpcy5id0VzdGltYXRvciA9IHRoaXMuaW5pdEVzdGltYXRvcigpO1xuICB9XG4gIGluaXRFc3RpbWF0b3IoKSB7XG4gICAgY29uc3QgY29uZmlnID0gdGhpcy5obHMuY29uZmlnO1xuICAgIHJldHVybiBuZXcgRXdtYUJhbmRXaWR0aEVzdGltYXRvcihjb25maWcuYWJyRXdtYVNsb3dWb0QsIGNvbmZpZy5hYnJFd21hRmFzdFZvRCwgY29uZmlnLmFickV3bWFEZWZhdWx0RXN0aW1hdGUpO1xuICB9XG4gIHJlZ2lzdGVyTGlzdGVuZXJzKCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGhsc1xuICAgIH0gPSB0aGlzO1xuICAgIGhscy5vbihFdmVudHMuTUFOSUZFU1RfTE9BRElORywgdGhpcy5vbk1hbmlmZXN0TG9hZGluZywgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5GUkFHX0xPQURJTkcsIHRoaXMub25GcmFnTG9hZGluZywgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5GUkFHX0xPQURFRCwgdGhpcy5vbkZyYWdMb2FkZWQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuRlJBR19CVUZGRVJFRCwgdGhpcy5vbkZyYWdCdWZmZXJlZCwgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5MRVZFTF9TV0lUQ0hJTkcsIHRoaXMub25MZXZlbFN3aXRjaGluZywgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5MRVZFTF9MT0FERUQsIHRoaXMub25MZXZlbExvYWRlZCwgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5MRVZFTFNfVVBEQVRFRCwgdGhpcy5vbkxldmVsc1VwZGF0ZWQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuTUFYX0FVVE9fTEVWRUxfVVBEQVRFRCwgdGhpcy5vbk1heEF1dG9MZXZlbFVwZGF0ZWQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuRVJST1IsIHRoaXMub25FcnJvciwgdGhpcyk7XG4gIH1cbiAgdW5yZWdpc3Rlckxpc3RlbmVycygpIHtcbiAgICBjb25zdCB7XG4gICAgICBobHNcbiAgICB9ID0gdGhpcztcbiAgICBpZiAoIWhscykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBobHMub2ZmKEV2ZW50cy5NQU5JRkVTVF9MT0FESU5HLCB0aGlzLm9uTWFuaWZlc3RMb2FkaW5nLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5GUkFHX0xPQURJTkcsIHRoaXMub25GcmFnTG9hZGluZywgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuRlJBR19MT0FERUQsIHRoaXMub25GcmFnTG9hZGVkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5GUkFHX0JVRkZFUkVELCB0aGlzLm9uRnJhZ0J1ZmZlcmVkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5MRVZFTF9TV0lUQ0hJTkcsIHRoaXMub25MZXZlbFN3aXRjaGluZywgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuTEVWRUxfTE9BREVELCB0aGlzLm9uTGV2ZWxMb2FkZWQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkxFVkVMU19VUERBVEVELCB0aGlzLm9uTGV2ZWxzVXBkYXRlZCwgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuTUFYX0FVVE9fTEVWRUxfVVBEQVRFRCwgdGhpcy5vbk1heEF1dG9MZXZlbFVwZGF0ZWQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkVSUk9SLCB0aGlzLm9uRXJyb3IsIHRoaXMpO1xuICB9XG4gIGRlc3Ryb3koKSB7XG4gICAgdGhpcy51bnJlZ2lzdGVyTGlzdGVuZXJzKCk7XG4gICAgdGhpcy5jbGVhclRpbWVyKCk7XG4gICAgLy8gQHRzLWlnbm9yZVxuICAgIHRoaXMuaGxzID0gdGhpcy5fYWJhbmRvblJ1bGVzQ2hlY2sgPSBudWxsO1xuICAgIHRoaXMuZnJhZ0N1cnJlbnQgPSB0aGlzLnBhcnRDdXJyZW50ID0gbnVsbDtcbiAgfVxuICBvbk1hbmlmZXN0TG9hZGluZyhldmVudCwgZGF0YSkge1xuICAgIHRoaXMubGFzdExvYWRlZEZyYWdMZXZlbCA9IC0xO1xuICAgIHRoaXMuZmlyc3RTZWxlY3Rpb24gPSAtMTtcbiAgICB0aGlzLmxhc3RMZXZlbExvYWRTZWMgPSAwO1xuICAgIHRoaXMuZnJhZ0N1cnJlbnQgPSB0aGlzLnBhcnRDdXJyZW50ID0gbnVsbDtcbiAgICB0aGlzLm9uTGV2ZWxzVXBkYXRlZCgpO1xuICAgIHRoaXMuY2xlYXJUaW1lcigpO1xuICB9XG4gIG9uTGV2ZWxzVXBkYXRlZCgpIHtcbiAgICBpZiAodGhpcy5sYXN0TG9hZGVkRnJhZ0xldmVsID4gLTEgJiYgdGhpcy5mcmFnQ3VycmVudCkge1xuICAgICAgdGhpcy5sYXN0TG9hZGVkRnJhZ0xldmVsID0gdGhpcy5mcmFnQ3VycmVudC5sZXZlbDtcbiAgICB9XG4gICAgdGhpcy5fbmV4dEF1dG9MZXZlbCA9IC0xO1xuICAgIHRoaXMub25NYXhBdXRvTGV2ZWxVcGRhdGVkKCk7XG4gICAgdGhpcy5jb2RlY1RpZXJzID0gbnVsbDtcbiAgICB0aGlzLmF1ZGlvVHJhY2tzQnlHcm91cCA9IG51bGw7XG4gIH1cbiAgb25NYXhBdXRvTGV2ZWxVcGRhdGVkKCkge1xuICAgIHRoaXMuZmlyc3RTZWxlY3Rpb24gPSAtMTtcbiAgICB0aGlzLm5leHRBdXRvTGV2ZWxLZXkgPSAnJztcbiAgfVxuICBvbkZyYWdMb2FkaW5nKGV2ZW50LCBkYXRhKSB7XG4gICAgY29uc3QgZnJhZyA9IGRhdGEuZnJhZztcbiAgICBpZiAodGhpcy5pZ25vcmVGcmFnbWVudChmcmFnKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAoIWZyYWcuYml0cmF0ZVRlc3QpIHtcbiAgICAgIHZhciBfZGF0YSRwYXJ0O1xuICAgICAgdGhpcy5mcmFnQ3VycmVudCA9IGZyYWc7XG4gICAgICB0aGlzLnBhcnRDdXJyZW50ID0gKF9kYXRhJHBhcnQgPSBkYXRhLnBhcnQpICE9IG51bGwgPyBfZGF0YSRwYXJ0IDogbnVsbDtcbiAgICB9XG4gICAgdGhpcy5jbGVhclRpbWVyKCk7XG4gICAgdGhpcy50aW1lciA9IHNlbGYuc2V0SW50ZXJ2YWwodGhpcy5fYWJhbmRvblJ1bGVzQ2hlY2ssIDEwMCk7XG4gIH1cbiAgb25MZXZlbFN3aXRjaGluZyhldmVudCwgZGF0YSkge1xuICAgIHRoaXMuY2xlYXJUaW1lcigpO1xuICB9XG4gIG9uRXJyb3IoZXZlbnQsIGRhdGEpIHtcbiAgICBpZiAoZGF0YS5mYXRhbCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBzd2l0Y2ggKGRhdGEuZGV0YWlscykge1xuICAgICAgY2FzZSBFcnJvckRldGFpbHMuQlVGRkVSX0FERF9DT0RFQ19FUlJPUjpcbiAgICAgIGNhc2UgRXJyb3JEZXRhaWxzLkJVRkZFUl9BUFBFTkRfRVJST1I6XG4gICAgICAgIC8vIFJlc2V0IGxhc3QgbG9hZGVkIGxldmVsIHNvIHRoYXQgYSBuZXcgc2VsZWN0aW9uIGNhbiBiZSBtYWRlIGFmdGVyIGNhbGxpbmcgcmVjb3Zlck1lZGlhRXJyb3JcbiAgICAgICAgdGhpcy5sYXN0TG9hZGVkRnJhZ0xldmVsID0gLTE7XG4gICAgICAgIHRoaXMuZmlyc3RTZWxlY3Rpb24gPSAtMTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5GUkFHX0xPQURfVElNRU9VVDpcbiAgICAgICAge1xuICAgICAgICAgIGNvbnN0IGZyYWcgPSBkYXRhLmZyYWc7XG4gICAgICAgICAgY29uc3Qge1xuICAgICAgICAgICAgZnJhZ0N1cnJlbnQsXG4gICAgICAgICAgICBwYXJ0Q3VycmVudDogcGFydFxuICAgICAgICAgIH0gPSB0aGlzO1xuICAgICAgICAgIGlmIChmcmFnICYmIGZyYWdDdXJyZW50ICYmIGZyYWcuc24gPT09IGZyYWdDdXJyZW50LnNuICYmIGZyYWcubGV2ZWwgPT09IGZyYWdDdXJyZW50LmxldmVsKSB7XG4gICAgICAgICAgICBjb25zdCBub3cgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgICAgICAgICAgIGNvbnN0IHN0YXRzID0gcGFydCA/IHBhcnQuc3RhdHMgOiBmcmFnLnN0YXRzO1xuICAgICAgICAgICAgY29uc3QgdGltZUxvYWRpbmcgPSBub3cgLSBzdGF0cy5sb2FkaW5nLnN0YXJ0O1xuICAgICAgICAgICAgY29uc3QgdHRmYiA9IHN0YXRzLmxvYWRpbmcuZmlyc3QgPyBzdGF0cy5sb2FkaW5nLmZpcnN0IC0gc3RhdHMubG9hZGluZy5zdGFydCA6IC0xO1xuICAgICAgICAgICAgY29uc3QgbG9hZGVkRmlyc3RCeXRlID0gc3RhdHMubG9hZGVkICYmIHR0ZmIgPiAtMTtcbiAgICAgICAgICAgIGlmIChsb2FkZWRGaXJzdEJ5dGUpIHtcbiAgICAgICAgICAgICAgY29uc3QgdHRmYkVzdGltYXRlID0gdGhpcy5id0VzdGltYXRvci5nZXRFc3RpbWF0ZVRURkIoKTtcbiAgICAgICAgICAgICAgdGhpcy5id0VzdGltYXRvci5zYW1wbGUodGltZUxvYWRpbmcgLSBNYXRoLm1pbih0dGZiRXN0aW1hdGUsIHR0ZmIpLCBzdGF0cy5sb2FkZWQpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgdGhpcy5id0VzdGltYXRvci5zYW1wbGVUVEZCKHRpbWVMb2FkaW5nKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICB9XG4gIH1cbiAgZ2V0VGltZVRvTG9hZEZyYWcodGltZVRvRmlyc3RCeXRlU2VjLCBiYW5kd2lkdGgsIGZyYWdTaXplQml0cywgaXNTd2l0Y2gpIHtcbiAgICBjb25zdCBmcmFnTG9hZFNlYyA9IHRpbWVUb0ZpcnN0Qnl0ZVNlYyArIGZyYWdTaXplQml0cyAvIGJhbmR3aWR0aDtcbiAgICBjb25zdCBwbGF5bGlzdExvYWRTZWMgPSBpc1N3aXRjaCA/IHRoaXMubGFzdExldmVsTG9hZFNlYyA6IDA7XG4gICAgcmV0dXJuIGZyYWdMb2FkU2VjICsgcGxheWxpc3RMb2FkU2VjO1xuICB9XG4gIG9uTGV2ZWxMb2FkZWQoZXZlbnQsIGRhdGEpIHtcbiAgICBjb25zdCBjb25maWcgPSB0aGlzLmhscy5jb25maWc7XG4gICAgY29uc3Qge1xuICAgICAgbG9hZGluZ1xuICAgIH0gPSBkYXRhLnN0YXRzO1xuICAgIGNvbnN0IHRpbWVMb2FkaW5nTXMgPSBsb2FkaW5nLmVuZCAtIGxvYWRpbmcuc3RhcnQ7XG4gICAgaWYgKGlzRmluaXRlTnVtYmVyKHRpbWVMb2FkaW5nTXMpKSB7XG4gICAgICB0aGlzLmxhc3RMZXZlbExvYWRTZWMgPSB0aW1lTG9hZGluZ01zIC8gMTAwMDtcbiAgICB9XG4gICAgaWYgKGRhdGEuZGV0YWlscy5saXZlKSB7XG4gICAgICB0aGlzLmJ3RXN0aW1hdG9yLnVwZGF0ZShjb25maWcuYWJyRXdtYVNsb3dMaXZlLCBjb25maWcuYWJyRXdtYUZhc3RMaXZlKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5id0VzdGltYXRvci51cGRhdGUoY29uZmlnLmFickV3bWFTbG93Vm9ELCBjb25maWcuYWJyRXdtYUZhc3RWb0QpO1xuICAgIH1cbiAgfVxuICBvbkZyYWdMb2FkZWQoZXZlbnQsIHtcbiAgICBmcmFnLFxuICAgIHBhcnRcbiAgfSkge1xuICAgIGNvbnN0IHN0YXRzID0gcGFydCA/IHBhcnQuc3RhdHMgOiBmcmFnLnN0YXRzO1xuICAgIGlmIChmcmFnLnR5cGUgPT09IFBsYXlsaXN0TGV2ZWxUeXBlLk1BSU4pIHtcbiAgICAgIHRoaXMuYndFc3RpbWF0b3Iuc2FtcGxlVFRGQihzdGF0cy5sb2FkaW5nLmZpcnN0IC0gc3RhdHMubG9hZGluZy5zdGFydCk7XG4gICAgfVxuICAgIGlmICh0aGlzLmlnbm9yZUZyYWdtZW50KGZyYWcpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIHN0b3AgbW9uaXRvcmluZyBidyBvbmNlIGZyYWcgbG9hZGVkXG4gICAgdGhpcy5jbGVhclRpbWVyKCk7XG4gICAgLy8gcmVzZXQgZm9yY2VkIGF1dG8gbGV2ZWwgdmFsdWUgc28gdGhhdCBuZXh0IGxldmVsIHdpbGwgYmUgc2VsZWN0ZWRcbiAgICBpZiAoZnJhZy5sZXZlbCA9PT0gdGhpcy5fbmV4dEF1dG9MZXZlbCkge1xuICAgICAgdGhpcy5fbmV4dEF1dG9MZXZlbCA9IC0xO1xuICAgIH1cbiAgICB0aGlzLmZpcnN0U2VsZWN0aW9uID0gLTE7XG5cbiAgICAvLyBjb21wdXRlIGxldmVsIGF2ZXJhZ2UgYml0cmF0ZVxuICAgIGlmICh0aGlzLmhscy5jb25maWcuYWJyTWF4V2l0aFJlYWxCaXRyYXRlKSB7XG4gICAgICBjb25zdCBkdXJhdGlvbiA9IHBhcnQgPyBwYXJ0LmR1cmF0aW9uIDogZnJhZy5kdXJhdGlvbjtcbiAgICAgIGNvbnN0IGxldmVsID0gdGhpcy5obHMubGV2ZWxzW2ZyYWcubGV2ZWxdO1xuICAgICAgY29uc3QgbG9hZGVkQnl0ZXMgPSAobGV2ZWwubG9hZGVkID8gbGV2ZWwubG9hZGVkLmJ5dGVzIDogMCkgKyBzdGF0cy5sb2FkZWQ7XG4gICAgICBjb25zdCBsb2FkZWREdXJhdGlvbiA9IChsZXZlbC5sb2FkZWQgPyBsZXZlbC5sb2FkZWQuZHVyYXRpb24gOiAwKSArIGR1cmF0aW9uO1xuICAgICAgbGV2ZWwubG9hZGVkID0ge1xuICAgICAgICBieXRlczogbG9hZGVkQnl0ZXMsXG4gICAgICAgIGR1cmF0aW9uOiBsb2FkZWREdXJhdGlvblxuICAgICAgfTtcbiAgICAgIGxldmVsLnJlYWxCaXRyYXRlID0gTWF0aC5yb3VuZCg4ICogbG9hZGVkQnl0ZXMgLyBsb2FkZWREdXJhdGlvbik7XG4gICAgfVxuICAgIGlmIChmcmFnLmJpdHJhdGVUZXN0KSB7XG4gICAgICBjb25zdCBmcmFnQnVmZmVyZWREYXRhID0ge1xuICAgICAgICBzdGF0cyxcbiAgICAgICAgZnJhZyxcbiAgICAgICAgcGFydCxcbiAgICAgICAgaWQ6IGZyYWcudHlwZVxuICAgICAgfTtcbiAgICAgIHRoaXMub25GcmFnQnVmZmVyZWQoRXZlbnRzLkZSQUdfQlVGRkVSRUQsIGZyYWdCdWZmZXJlZERhdGEpO1xuICAgICAgZnJhZy5iaXRyYXRlVGVzdCA9IGZhbHNlO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBzdG9yZSBsZXZlbCBpZCBhZnRlciBzdWNjZXNzZnVsIGZyYWdtZW50IGxvYWQgZm9yIHBsYXliYWNrXG4gICAgICB0aGlzLmxhc3RMb2FkZWRGcmFnTGV2ZWwgPSBmcmFnLmxldmVsO1xuICAgIH1cbiAgfVxuICBvbkZyYWdCdWZmZXJlZChldmVudCwgZGF0YSkge1xuICAgIGNvbnN0IHtcbiAgICAgIGZyYWcsXG4gICAgICBwYXJ0XG4gICAgfSA9IGRhdGE7XG4gICAgY29uc3Qgc3RhdHMgPSBwYXJ0ICE9IG51bGwgJiYgcGFydC5zdGF0cy5sb2FkZWQgPyBwYXJ0LnN0YXRzIDogZnJhZy5zdGF0cztcbiAgICBpZiAoc3RhdHMuYWJvcnRlZCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAodGhpcy5pZ25vcmVGcmFnbWVudChmcmFnKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyBVc2UgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBwYXJzaW5nIGFuZCByZXF1ZXN0IGluc3RlYWQgb2YgYnVmZmVyaW5nIGFuZCByZXF1ZXN0IHRvIGNvbXB1dGUgZnJhZ0xvYWRpbmdQcm9jZXNzaW5nO1xuICAgIC8vIHJhdGlvbmFsZSBpcyB0aGF0IGJ1ZmZlciBhcHBlbmRpbmcgb25seSBoYXBwZW5zIG9uY2UgbWVkaWEgaXMgYXR0YWNoZWQuIFRoaXMgY2FuIGhhcHBlbiB3aGVuIGNvbmZpZy5zdGFydEZyYWdQcmVmZXRjaFxuICAgIC8vIGlzIHVzZWQuIElmIHdlIHVzZWQgYnVmZmVyaW5nIGluIHRoYXQgY2FzZSwgb3VyIEJXIGVzdGltYXRlIHNhbXBsZSB3aWxsIGJlIHZlcnkgbGFyZ2UuXG4gICAgY29uc3QgcHJvY2Vzc2luZ01zID0gc3RhdHMucGFyc2luZy5lbmQgLSBzdGF0cy5sb2FkaW5nLnN0YXJ0IC0gTWF0aC5taW4oc3RhdHMubG9hZGluZy5maXJzdCAtIHN0YXRzLmxvYWRpbmcuc3RhcnQsIHRoaXMuYndFc3RpbWF0b3IuZ2V0RXN0aW1hdGVUVEZCKCkpO1xuICAgIHRoaXMuYndFc3RpbWF0b3Iuc2FtcGxlKHByb2Nlc3NpbmdNcywgc3RhdHMubG9hZGVkKTtcbiAgICBzdGF0cy5id0VzdGltYXRlID0gdGhpcy5nZXRCd0VzdGltYXRlKCk7XG4gICAgaWYgKGZyYWcuYml0cmF0ZVRlc3QpIHtcbiAgICAgIHRoaXMuYml0cmF0ZVRlc3REZWxheSA9IHByb2Nlc3NpbmdNcyAvIDEwMDA7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuYml0cmF0ZVRlc3REZWxheSA9IDA7XG4gICAgfVxuICB9XG4gIGlnbm9yZUZyYWdtZW50KGZyYWcpIHtcbiAgICAvLyBPbmx5IGNvdW50IG5vbi1hbHQtYXVkaW8gZnJhZ3Mgd2hpY2ggd2VyZSBhY3R1YWxseSBidWZmZXJlZCBpbiBvdXIgQlcgY2FsY3VsYXRpb25zXG4gICAgcmV0dXJuIGZyYWcudHlwZSAhPT0gUGxheWxpc3RMZXZlbFR5cGUuTUFJTiB8fCBmcmFnLnNuID09PSAnaW5pdFNlZ21lbnQnO1xuICB9XG4gIGNsZWFyVGltZXIoKSB7XG4gICAgaWYgKHRoaXMudGltZXIgPiAtMSkge1xuICAgICAgc2VsZi5jbGVhckludGVydmFsKHRoaXMudGltZXIpO1xuICAgICAgdGhpcy50aW1lciA9IC0xO1xuICAgIH1cbiAgfVxuICBnZXQgZmlyc3RBdXRvTGV2ZWwoKSB7XG4gICAgY29uc3Qge1xuICAgICAgbWF4QXV0b0xldmVsLFxuICAgICAgbWluQXV0b0xldmVsXG4gICAgfSA9IHRoaXMuaGxzO1xuICAgIGNvbnN0IGJ3RXN0aW1hdGUgPSB0aGlzLmdldEJ3RXN0aW1hdGUoKTtcbiAgICBjb25zdCBtYXhTdGFydERlbGF5ID0gdGhpcy5obHMuY29uZmlnLm1heFN0YXJ2YXRpb25EZWxheTtcbiAgICBjb25zdCBhYnJBdXRvTGV2ZWwgPSB0aGlzLmZpbmRCZXN0TGV2ZWwoYndFc3RpbWF0ZSwgbWluQXV0b0xldmVsLCBtYXhBdXRvTGV2ZWwsIDAsIG1heFN0YXJ0RGVsYXksIDEsIDEpO1xuICAgIGlmIChhYnJBdXRvTGV2ZWwgPiAtMSkge1xuICAgICAgcmV0dXJuIGFickF1dG9MZXZlbDtcbiAgICB9XG4gICAgY29uc3QgZmlyc3RMZXZlbCA9IHRoaXMuaGxzLmZpcnN0TGV2ZWw7XG4gICAgY29uc3QgY2xhbXBlZCA9IE1hdGgubWluKE1hdGgubWF4KGZpcnN0TGV2ZWwsIG1pbkF1dG9MZXZlbCksIG1heEF1dG9MZXZlbCk7XG4gICAgbG9nZ2VyLndhcm4oYFthYnJdIENvdWxkIG5vdCBmaW5kIGJlc3Qgc3RhcnRpbmcgYXV0byBsZXZlbC4gRGVmYXVsdGluZyB0byBmaXJzdCBpbiBwbGF5bGlzdCAke2ZpcnN0TGV2ZWx9IGNsYW1wZWQgdG8gJHtjbGFtcGVkfWApO1xuICAgIHJldHVybiBjbGFtcGVkO1xuICB9XG4gIGdldCBmb3JjZWRBdXRvTGV2ZWwoKSB7XG4gICAgaWYgKHRoaXMubmV4dEF1dG9MZXZlbEtleSkge1xuICAgICAgcmV0dXJuIC0xO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fbmV4dEF1dG9MZXZlbDtcbiAgfVxuXG4gIC8vIHJldHVybiBuZXh0IGF1dG8gbGV2ZWxcbiAgZ2V0IG5leHRBdXRvTGV2ZWwoKSB7XG4gICAgY29uc3QgZm9yY2VkQXV0b0xldmVsID0gdGhpcy5mb3JjZWRBdXRvTGV2ZWw7XG4gICAgY29uc3QgYndFc3RpbWF0b3IgPSB0aGlzLmJ3RXN0aW1hdG9yO1xuICAgIGNvbnN0IHVzZUVzdGltYXRlID0gYndFc3RpbWF0b3IuY2FuRXN0aW1hdGUoKTtcbiAgICBjb25zdCBsb2FkZWRGaXJzdEZyYWcgPSB0aGlzLmxhc3RMb2FkZWRGcmFnTGV2ZWwgPiAtMTtcbiAgICAvLyBpbiBjYXNlIG5leHQgYXV0byBsZXZlbCBoYXMgYmVlbiBmb3JjZWQsIGFuZCBidyBub3QgYXZhaWxhYmxlIG9yIG5vdCByZWxpYWJsZSwgcmV0dXJuIGZvcmNlZCB2YWx1ZVxuICAgIGlmIChmb3JjZWRBdXRvTGV2ZWwgIT09IC0xICYmICghdXNlRXN0aW1hdGUgfHwgIWxvYWRlZEZpcnN0RnJhZyB8fCB0aGlzLm5leHRBdXRvTGV2ZWxLZXkgPT09IHRoaXMuZ2V0QXV0b0xldmVsS2V5KCkpKSB7XG4gICAgICByZXR1cm4gZm9yY2VkQXV0b0xldmVsO1xuICAgIH1cblxuICAgIC8vIGNvbXB1dGUgbmV4dCBsZXZlbCB1c2luZyBBQlIgbG9naWNcbiAgICBjb25zdCBuZXh0QUJSQXV0b0xldmVsID0gdXNlRXN0aW1hdGUgJiYgbG9hZGVkRmlyc3RGcmFnID8gdGhpcy5nZXROZXh0QUJSQXV0b0xldmVsKCkgOiB0aGlzLmZpcnN0QXV0b0xldmVsO1xuXG4gICAgLy8gdXNlIGZvcmNlZCBhdXRvIGxldmVsIHdoaWxlIGl0IGhhc24ndCBlcnJvcmVkIG1vcmUgdGhhbiBBQlIgc2VsZWN0aW9uXG4gICAgaWYgKGZvcmNlZEF1dG9MZXZlbCAhPT0gLTEpIHtcbiAgICAgIGNvbnN0IGxldmVscyA9IHRoaXMuaGxzLmxldmVscztcbiAgICAgIGlmIChsZXZlbHMubGVuZ3RoID4gTWF0aC5tYXgoZm9yY2VkQXV0b0xldmVsLCBuZXh0QUJSQXV0b0xldmVsKSAmJiBsZXZlbHNbZm9yY2VkQXV0b0xldmVsXS5sb2FkRXJyb3IgPD0gbGV2ZWxzW25leHRBQlJBdXRvTGV2ZWxdLmxvYWRFcnJvcikge1xuICAgICAgICByZXR1cm4gZm9yY2VkQXV0b0xldmVsO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIHNhdmUgcmVzdWx0IHVudGlsIHN0YXRlIGhhcyBjaGFuZ2VkXG4gICAgdGhpcy5fbmV4dEF1dG9MZXZlbCA9IG5leHRBQlJBdXRvTGV2ZWw7XG4gICAgdGhpcy5uZXh0QXV0b0xldmVsS2V5ID0gdGhpcy5nZXRBdXRvTGV2ZWxLZXkoKTtcbiAgICByZXR1cm4gbmV4dEFCUkF1dG9MZXZlbDtcbiAgfVxuICBnZXRBdXRvTGV2ZWxLZXkoKSB7XG4gICAgcmV0dXJuIGAke3RoaXMuZ2V0QndFc3RpbWF0ZSgpfV8ke3RoaXMuZ2V0U3RhcnZhdGlvbkRlbGF5KCkudG9GaXhlZCgyKX1gO1xuICB9XG4gIGdldE5leHRBQlJBdXRvTGV2ZWwoKSB7XG4gICAgY29uc3Qge1xuICAgICAgZnJhZ0N1cnJlbnQsXG4gICAgICBwYXJ0Q3VycmVudCxcbiAgICAgIGhsc1xuICAgIH0gPSB0aGlzO1xuICAgIGNvbnN0IHtcbiAgICAgIG1heEF1dG9MZXZlbCxcbiAgICAgIGNvbmZpZyxcbiAgICAgIG1pbkF1dG9MZXZlbFxuICAgIH0gPSBobHM7XG4gICAgY29uc3QgY3VycmVudEZyYWdEdXJhdGlvbiA9IHBhcnRDdXJyZW50ID8gcGFydEN1cnJlbnQuZHVyYXRpb24gOiBmcmFnQ3VycmVudCA/IGZyYWdDdXJyZW50LmR1cmF0aW9uIDogMDtcbiAgICBjb25zdCBhdmdidyA9IHRoaXMuZ2V0QndFc3RpbWF0ZSgpO1xuICAgIC8vIGJ1ZmZlclN0YXJ2YXRpb25EZWxheSBpcyB0aGUgd2FsbC1jbG9jayB0aW1lIGxlZnQgdW50aWwgdGhlIHBsYXliYWNrIGJ1ZmZlciBpcyBleGhhdXN0ZWQuXG4gICAgY29uc3QgYnVmZmVyU3RhcnZhdGlvbkRlbGF5ID0gdGhpcy5nZXRTdGFydmF0aW9uRGVsYXkoKTtcbiAgICBsZXQgYndGYWN0b3IgPSBjb25maWcuYWJyQmFuZFdpZHRoRmFjdG9yO1xuICAgIGxldCBid1VwRmFjdG9yID0gY29uZmlnLmFickJhbmRXaWR0aFVwRmFjdG9yO1xuXG4gICAgLy8gRmlyc3QsIGxvb2sgdG8gc2VlIGlmIHdlIGNhbiBmaW5kIGEgbGV2ZWwgbWF0Y2hpbmcgd2l0aCBvdXIgYXZnIGJhbmR3aWR0aCBBTkQgdGhhdCBjb3VsZCBhbHNvIGd1YXJhbnRlZSBubyByZWJ1ZmZlcmluZyBhdCBhbGxcbiAgICBpZiAoYnVmZmVyU3RhcnZhdGlvbkRlbGF5KSB7XG4gICAgICBjb25zdCBfYmVzdExldmVsID0gdGhpcy5maW5kQmVzdExldmVsKGF2Z2J3LCBtaW5BdXRvTGV2ZWwsIG1heEF1dG9MZXZlbCwgYnVmZmVyU3RhcnZhdGlvbkRlbGF5LCAwLCBid0ZhY3RvciwgYndVcEZhY3Rvcik7XG4gICAgICBpZiAoX2Jlc3RMZXZlbCA+PSAwKSB7XG4gICAgICAgIHJldHVybiBfYmVzdExldmVsO1xuICAgICAgfVxuICAgIH1cbiAgICAvLyBub3QgcG9zc2libGUgdG8gZ2V0IHJpZCBvZiByZWJ1ZmZlcmluZy4uLiB0cnkgdG8gZmluZCBsZXZlbCB0aGF0IHdpbGwgZ3VhcmFudGVlIGxlc3MgdGhhbiBtYXhTdGFydmF0aW9uRGVsYXkgb2YgcmVidWZmZXJpbmdcbiAgICBsZXQgbWF4U3RhcnZhdGlvbkRlbGF5ID0gY3VycmVudEZyYWdEdXJhdGlvbiA/IE1hdGgubWluKGN1cnJlbnRGcmFnRHVyYXRpb24sIGNvbmZpZy5tYXhTdGFydmF0aW9uRGVsYXkpIDogY29uZmlnLm1heFN0YXJ2YXRpb25EZWxheTtcbiAgICBpZiAoIWJ1ZmZlclN0YXJ2YXRpb25EZWxheSkge1xuICAgICAgLy8gaW4gY2FzZSBidWZmZXIgaXMgZW1wdHksIGxldCdzIGNoZWNrIGlmIHByZXZpb3VzIGZyYWdtZW50IHdhcyBsb2FkZWQgdG8gcGVyZm9ybSBhIGJpdHJhdGUgdGVzdFxuICAgICAgY29uc3QgYml0cmF0ZVRlc3REZWxheSA9IHRoaXMuYml0cmF0ZVRlc3REZWxheTtcbiAgICAgIGlmIChiaXRyYXRlVGVzdERlbGF5KSB7XG4gICAgICAgIC8vIGlmIGl0IGlzIHRoZSBjYXNlLCB0aGVuIHdlIG5lZWQgdG8gYWRqdXN0IG91ciBtYXggc3RhcnZhdGlvbiBkZWxheSB1c2luZyBtYXhMb2FkaW5nRGVsYXkgY29uZmlnIHZhbHVlXG4gICAgICAgIC8vIG1heCB2aWRlbyBsb2FkaW5nIGRlbGF5IHVzZWQgaW4gIGF1dG9tYXRpYyBzdGFydCBsZXZlbCBzZWxlY3Rpb24gOlxuICAgICAgICAvLyBpbiB0aGF0IG1vZGUgQUJSIGNvbnRyb2xsZXIgd2lsbCBlbnN1cmUgdGhhdCB2aWRlbyBsb2FkaW5nIHRpbWUgKGllIHRoZSB0aW1lIHRvIGZldGNoIHRoZSBmaXJzdCBmcmFnbWVudCBhdCBsb3dlc3QgcXVhbGl0eSBsZXZlbCArXG4gICAgICAgIC8vIHRoZSB0aW1lIHRvIGZldGNoIHRoZSBmcmFnbWVudCBhdCB0aGUgYXBwcm9wcmlhdGUgcXVhbGl0eSBsZXZlbCBpcyBsZXNzIHRoYW4gYGBgbWF4TG9hZGluZ0RlbGF5YGBgIClcbiAgICAgICAgLy8gY2FwIG1heExvYWRpbmdEZWxheSBhbmQgZW5zdXJlIGl0IGlzIG5vdCBiaWdnZXIgJ3RoYW4gYml0cmF0ZSB0ZXN0JyBmcmFnIGR1cmF0aW9uXG4gICAgICAgIGNvbnN0IG1heExvYWRpbmdEZWxheSA9IGN1cnJlbnRGcmFnRHVyYXRpb24gPyBNYXRoLm1pbihjdXJyZW50RnJhZ0R1cmF0aW9uLCBjb25maWcubWF4TG9hZGluZ0RlbGF5KSA6IGNvbmZpZy5tYXhMb2FkaW5nRGVsYXk7XG4gICAgICAgIG1heFN0YXJ2YXRpb25EZWxheSA9IG1heExvYWRpbmdEZWxheSAtIGJpdHJhdGVUZXN0RGVsYXk7XG4gICAgICAgIGxvZ2dlci5pbmZvKGBbYWJyXSBiaXRyYXRlIHRlc3QgdG9vayAke01hdGgucm91bmQoMTAwMCAqIGJpdHJhdGVUZXN0RGVsYXkpfW1zLCBzZXQgZmlyc3QgZnJhZ21lbnQgbWF4IGZldGNoRHVyYXRpb24gdG8gJHtNYXRoLnJvdW5kKDEwMDAgKiBtYXhTdGFydmF0aW9uRGVsYXkpfSBtc2ApO1xuICAgICAgICAvLyBkb24ndCB1c2UgY29uc2VydmF0aXZlIGZhY3RvciBvbiBiaXRyYXRlIHRlc3RcbiAgICAgICAgYndGYWN0b3IgPSBid1VwRmFjdG9yID0gMTtcbiAgICAgIH1cbiAgICB9XG4gICAgY29uc3QgYmVzdExldmVsID0gdGhpcy5maW5kQmVzdExldmVsKGF2Z2J3LCBtaW5BdXRvTGV2ZWwsIG1heEF1dG9MZXZlbCwgYnVmZmVyU3RhcnZhdGlvbkRlbGF5LCBtYXhTdGFydmF0aW9uRGVsYXksIGJ3RmFjdG9yLCBid1VwRmFjdG9yKTtcbiAgICBsb2dnZXIuaW5mbyhgW2Ficl0gJHtidWZmZXJTdGFydmF0aW9uRGVsYXkgPyAncmVidWZmZXJpbmcgZXhwZWN0ZWQnIDogJ2J1ZmZlciBpcyBlbXB0eSd9LCBvcHRpbWFsIHF1YWxpdHkgbGV2ZWwgJHtiZXN0TGV2ZWx9YCk7XG4gICAgaWYgKGJlc3RMZXZlbCA+IC0xKSB7XG4gICAgICByZXR1cm4gYmVzdExldmVsO1xuICAgIH1cbiAgICAvLyBJZiBubyBtYXRjaGluZyBsZXZlbCBmb3VuZCwgc2VlIGlmIG1pbiBhdXRvIGxldmVsIHdvdWxkIGJlIGEgYmV0dGVyIG9wdGlvblxuICAgIGNvbnN0IG1pbkxldmVsID0gaGxzLmxldmVsc1ttaW5BdXRvTGV2ZWxdO1xuICAgIGNvbnN0IGF1dG9MZXZlbCA9IGhscy5sZXZlbHNbaGxzLmxvYWRMZXZlbF07XG4gICAgaWYgKChtaW5MZXZlbCA9PSBudWxsID8gdm9pZCAwIDogbWluTGV2ZWwuYml0cmF0ZSkgPCAoYXV0b0xldmVsID09IG51bGwgPyB2b2lkIDAgOiBhdXRvTGV2ZWwuYml0cmF0ZSkpIHtcbiAgICAgIHJldHVybiBtaW5BdXRvTGV2ZWw7XG4gICAgfVxuICAgIC8vIG9yIGlmIGJpdHJhdGUgaXMgbm90IGxvd2VyLCBjb250aW51ZSB0byB1c2UgbG9hZExldmVsXG4gICAgcmV0dXJuIGhscy5sb2FkTGV2ZWw7XG4gIH1cbiAgZ2V0U3RhcnZhdGlvbkRlbGF5KCkge1xuICAgIGNvbnN0IGhscyA9IHRoaXMuaGxzO1xuICAgIGNvbnN0IG1lZGlhID0gaGxzLm1lZGlhO1xuICAgIGlmICghbWVkaWEpIHtcbiAgICAgIHJldHVybiBJbmZpbml0eTtcbiAgICB9XG4gICAgLy8gcGxheWJhY2tSYXRlIGlzIHRoZSBhYnNvbHV0ZSB2YWx1ZSBvZiB0aGUgcGxheWJhY2sgcmF0ZTsgaWYgbWVkaWEucGxheWJhY2tSYXRlIGlzIDAsIHdlIHVzZSAxIHRvIGxvYWQgYXNcbiAgICAvLyBpZiB3ZSdyZSBwbGF5aW5nIGJhY2sgYXQgdGhlIG5vcm1hbCByYXRlLlxuICAgIGNvbnN0IHBsYXliYWNrUmF0ZSA9IG1lZGlhICYmIG1lZGlhLnBsYXliYWNrUmF0ZSAhPT0gMCA/IE1hdGguYWJzKG1lZGlhLnBsYXliYWNrUmF0ZSkgOiAxLjA7XG4gICAgY29uc3QgYnVmZmVySW5mbyA9IGhscy5tYWluRm9yd2FyZEJ1ZmZlckluZm87XG4gICAgcmV0dXJuIChidWZmZXJJbmZvID8gYnVmZmVySW5mby5sZW4gOiAwKSAvIHBsYXliYWNrUmF0ZTtcbiAgfVxuICBnZXRCd0VzdGltYXRlKCkge1xuICAgIHJldHVybiB0aGlzLmJ3RXN0aW1hdG9yLmNhbkVzdGltYXRlKCkgPyB0aGlzLmJ3RXN0aW1hdG9yLmdldEVzdGltYXRlKCkgOiB0aGlzLmhscy5jb25maWcuYWJyRXdtYURlZmF1bHRFc3RpbWF0ZTtcbiAgfVxuICBmaW5kQmVzdExldmVsKGN1cnJlbnRCdywgbWluQXV0b0xldmVsLCBtYXhBdXRvTGV2ZWwsIGJ1ZmZlclN0YXJ2YXRpb25EZWxheSwgbWF4U3RhcnZhdGlvbkRlbGF5LCBid0ZhY3RvciwgYndVcEZhY3Rvcikge1xuICAgIHZhciBfbGV2ZWwkZGV0YWlscztcbiAgICBjb25zdCBtYXhGZXRjaER1cmF0aW9uID0gYnVmZmVyU3RhcnZhdGlvbkRlbGF5ICsgbWF4U3RhcnZhdGlvbkRlbGF5O1xuICAgIGNvbnN0IGxhc3RMb2FkZWRGcmFnTGV2ZWwgPSB0aGlzLmxhc3RMb2FkZWRGcmFnTGV2ZWw7XG4gICAgY29uc3Qgc2VsZWN0aW9uQmFzZUxldmVsID0gbGFzdExvYWRlZEZyYWdMZXZlbCA9PT0gLTEgPyB0aGlzLmhscy5maXJzdExldmVsIDogbGFzdExvYWRlZEZyYWdMZXZlbDtcbiAgICBjb25zdCB7XG4gICAgICBmcmFnQ3VycmVudCxcbiAgICAgIHBhcnRDdXJyZW50XG4gICAgfSA9IHRoaXM7XG4gICAgY29uc3Qge1xuICAgICAgbGV2ZWxzLFxuICAgICAgYWxsQXVkaW9UcmFja3MsXG4gICAgICBsb2FkTGV2ZWwsXG4gICAgICBjb25maWdcbiAgICB9ID0gdGhpcy5obHM7XG4gICAgaWYgKGxldmVscy5sZW5ndGggPT09IDEpIHtcbiAgICAgIHJldHVybiAwO1xuICAgIH1cbiAgICBjb25zdCBsZXZlbCA9IGxldmVsc1tzZWxlY3Rpb25CYXNlTGV2ZWxdO1xuICAgIGNvbnN0IGxpdmUgPSAhIShsZXZlbCAhPSBudWxsICYmIChfbGV2ZWwkZGV0YWlscyA9IGxldmVsLmRldGFpbHMpICE9IG51bGwgJiYgX2xldmVsJGRldGFpbHMubGl2ZSk7XG4gICAgY29uc3QgZmlyc3RTZWxlY3Rpb24gPSBsb2FkTGV2ZWwgPT09IC0xIHx8IGxhc3RMb2FkZWRGcmFnTGV2ZWwgPT09IC0xO1xuICAgIGxldCBjdXJyZW50Q29kZWNTZXQ7XG4gICAgbGV0IGN1cnJlbnRWaWRlb1JhbmdlID0gJ1NEUic7XG4gICAgbGV0IGN1cnJlbnRGcmFtZVJhdGUgPSAobGV2ZWwgPT0gbnVsbCA/IHZvaWQgMCA6IGxldmVsLmZyYW1lUmF0ZSkgfHwgMDtcbiAgICBjb25zdCB7XG4gICAgICBhdWRpb1ByZWZlcmVuY2UsXG4gICAgICB2aWRlb1ByZWZlcmVuY2VcbiAgICB9ID0gY29uZmlnO1xuICAgIGNvbnN0IGF1ZGlvVHJhY2tzQnlHcm91cCA9IHRoaXMuYXVkaW9UcmFja3NCeUdyb3VwIHx8ICh0aGlzLmF1ZGlvVHJhY2tzQnlHcm91cCA9IGdldEF1ZGlvVHJhY2tzQnlHcm91cChhbGxBdWRpb1RyYWNrcykpO1xuICAgIGlmIChmaXJzdFNlbGVjdGlvbikge1xuICAgICAgaWYgKHRoaXMuZmlyc3RTZWxlY3Rpb24gIT09IC0xKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmZpcnN0U2VsZWN0aW9uO1xuICAgICAgfVxuICAgICAgY29uc3QgY29kZWNUaWVycyA9IHRoaXMuY29kZWNUaWVycyB8fCAodGhpcy5jb2RlY1RpZXJzID0gZ2V0Q29kZWNUaWVycyhsZXZlbHMsIGF1ZGlvVHJhY2tzQnlHcm91cCwgbWluQXV0b0xldmVsLCBtYXhBdXRvTGV2ZWwpKTtcbiAgICAgIGNvbnN0IHN0YXJ0VGllciA9IGdldFN0YXJ0Q29kZWNUaWVyKGNvZGVjVGllcnMsIGN1cnJlbnRWaWRlb1JhbmdlLCBjdXJyZW50QncsIGF1ZGlvUHJlZmVyZW5jZSwgdmlkZW9QcmVmZXJlbmNlKTtcbiAgICAgIGNvbnN0IHtcbiAgICAgICAgY29kZWNTZXQsXG4gICAgICAgIHZpZGVvUmFuZ2VzLFxuICAgICAgICBtaW5GcmFtZXJhdGUsXG4gICAgICAgIG1pbkJpdHJhdGUsXG4gICAgICAgIHByZWZlckhEUlxuICAgICAgfSA9IHN0YXJ0VGllcjtcbiAgICAgIGN1cnJlbnRDb2RlY1NldCA9IGNvZGVjU2V0O1xuICAgICAgY3VycmVudFZpZGVvUmFuZ2UgPSBwcmVmZXJIRFIgPyB2aWRlb1Jhbmdlc1t2aWRlb1Jhbmdlcy5sZW5ndGggLSAxXSA6IHZpZGVvUmFuZ2VzWzBdO1xuICAgICAgY3VycmVudEZyYW1lUmF0ZSA9IG1pbkZyYW1lcmF0ZTtcbiAgICAgIGN1cnJlbnRCdyA9IE1hdGgubWF4KGN1cnJlbnRCdywgbWluQml0cmF0ZSk7XG4gICAgICBsb2dnZXIubG9nKGBbYWJyXSBwaWNrZWQgc3RhcnQgdGllciAke0pTT04uc3RyaW5naWZ5KHN0YXJ0VGllcil9YCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGN1cnJlbnRDb2RlY1NldCA9IGxldmVsID09IG51bGwgPyB2b2lkIDAgOiBsZXZlbC5jb2RlY1NldDtcbiAgICAgIGN1cnJlbnRWaWRlb1JhbmdlID0gbGV2ZWwgPT0gbnVsbCA/IHZvaWQgMCA6IGxldmVsLnZpZGVvUmFuZ2U7XG4gICAgfVxuICAgIGNvbnN0IGN1cnJlbnRGcmFnRHVyYXRpb24gPSBwYXJ0Q3VycmVudCA/IHBhcnRDdXJyZW50LmR1cmF0aW9uIDogZnJhZ0N1cnJlbnQgPyBmcmFnQ3VycmVudC5kdXJhdGlvbiA6IDA7XG4gICAgY29uc3QgdHRmYkVzdGltYXRlU2VjID0gdGhpcy5id0VzdGltYXRvci5nZXRFc3RpbWF0ZVRURkIoKSAvIDEwMDA7XG4gICAgY29uc3QgbGV2ZWxzU2tpcHBlZCA9IFtdO1xuICAgIGZvciAobGV0IGkgPSBtYXhBdXRvTGV2ZWw7IGkgPj0gbWluQXV0b0xldmVsOyBpLS0pIHtcbiAgICAgIHZhciBfbGV2ZWxJbmZvJHN1cHBvcnRlZFI7XG4gICAgICBjb25zdCBsZXZlbEluZm8gPSBsZXZlbHNbaV07XG4gICAgICBjb25zdCB1cFN3aXRjaCA9IGkgPiBzZWxlY3Rpb25CYXNlTGV2ZWw7XG4gICAgICBpZiAoIWxldmVsSW5mbykge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIGlmIChjb25maWcudXNlTWVkaWFDYXBhYmlsaXRpZXMgJiYgIWxldmVsSW5mby5zdXBwb3J0ZWRSZXN1bHQgJiYgIWxldmVsSW5mby5zdXBwb3J0ZWRQcm9taXNlKSB7XG4gICAgICAgIGNvbnN0IG1lZGlhQ2FwYWJpbGl0aWVzID0gbmF2aWdhdG9yLm1lZGlhQ2FwYWJpbGl0aWVzO1xuICAgICAgICBpZiAodHlwZW9mIChtZWRpYUNhcGFiaWxpdGllcyA9PSBudWxsID8gdm9pZCAwIDogbWVkaWFDYXBhYmlsaXRpZXMuZGVjb2RpbmdJbmZvKSA9PT0gJ2Z1bmN0aW9uJyAmJiByZXF1aXJlc01lZGlhQ2FwYWJpbGl0aWVzRGVjb2RpbmdJbmZvKGxldmVsSW5mbywgYXVkaW9UcmFja3NCeUdyb3VwLCBjdXJyZW50VmlkZW9SYW5nZSwgY3VycmVudEZyYW1lUmF0ZSwgY3VycmVudEJ3LCBhdWRpb1ByZWZlcmVuY2UpKSB7XG4gICAgICAgICAgbGV2ZWxJbmZvLnN1cHBvcnRlZFByb21pc2UgPSBnZXRNZWRpYURlY29kaW5nSW5mb1Byb21pc2UobGV2ZWxJbmZvLCBhdWRpb1RyYWNrc0J5R3JvdXAsIG1lZGlhQ2FwYWJpbGl0aWVzKTtcbiAgICAgICAgICBsZXZlbEluZm8uc3VwcG9ydGVkUHJvbWlzZS50aGVuKGRlY29kaW5nSW5mbyA9PiB7XG4gICAgICAgICAgICBpZiAoIXRoaXMuaGxzKSB7XG4gICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGxldmVsSW5mby5zdXBwb3J0ZWRSZXN1bHQgPSBkZWNvZGluZ0luZm87XG4gICAgICAgICAgICBjb25zdCBsZXZlbHMgPSB0aGlzLmhscy5sZXZlbHM7XG4gICAgICAgICAgICBjb25zdCBpbmRleCA9IGxldmVscy5pbmRleE9mKGxldmVsSW5mbyk7XG4gICAgICAgICAgICBpZiAoZGVjb2RpbmdJbmZvLmVycm9yKSB7XG4gICAgICAgICAgICAgIGxvZ2dlci53YXJuKGBbYWJyXSBNZWRpYUNhcGFiaWxpdGllcyBkZWNvZGluZ0luZm8gZXJyb3I6IFwiJHtkZWNvZGluZ0luZm8uZXJyb3J9XCIgZm9yIGxldmVsICR7aW5kZXh9ICR7SlNPTi5zdHJpbmdpZnkoZGVjb2RpbmdJbmZvKX1gKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoIWRlY29kaW5nSW5mby5zdXBwb3J0ZWQpIHtcbiAgICAgICAgICAgICAgbG9nZ2VyLndhcm4oYFthYnJdIFVuc3VwcG9ydGVkIE1lZGlhQ2FwYWJpbGl0aWVzIGRlY29kaW5nSW5mbyByZXN1bHQgZm9yIGxldmVsICR7aW5kZXh9ICR7SlNPTi5zdHJpbmdpZnkoZGVjb2RpbmdJbmZvKX1gKTtcbiAgICAgICAgICAgICAgaWYgKGluZGV4ID4gLTEgJiYgbGV2ZWxzLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgICAgICAgICBsb2dnZXIubG9nKGBbYWJyXSBSZW1vdmluZyB1bnN1cHBvcnRlZCBsZXZlbCAke2luZGV4fWApO1xuICAgICAgICAgICAgICAgIHRoaXMuaGxzLnJlbW92ZUxldmVsKGluZGV4KTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGxldmVsSW5mby5zdXBwb3J0ZWRSZXN1bHQgPSBTVVBQT1JURURfSU5GT19ERUZBVUxUO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIHNraXAgY2FuZGlkYXRlcyB3aGljaCBjaGFuZ2UgY29kZWMtZmFtaWx5IG9yIHZpZGVvLXJhbmdlLFxuICAgICAgLy8gYW5kIHdoaWNoIGRlY3JlYXNlIG9yIGluY3JlYXNlIGZyYW1lLXJhdGUgZm9yIHVwIGFuZCBkb3duLXN3aXRjaCByZXNwZWN0ZnVsbHlcbiAgICAgIGlmIChjdXJyZW50Q29kZWNTZXQgJiYgbGV2ZWxJbmZvLmNvZGVjU2V0ICE9PSBjdXJyZW50Q29kZWNTZXQgfHwgY3VycmVudFZpZGVvUmFuZ2UgJiYgbGV2ZWxJbmZvLnZpZGVvUmFuZ2UgIT09IGN1cnJlbnRWaWRlb1JhbmdlIHx8IHVwU3dpdGNoICYmIGN1cnJlbnRGcmFtZVJhdGUgPiBsZXZlbEluZm8uZnJhbWVSYXRlIHx8ICF1cFN3aXRjaCAmJiBjdXJyZW50RnJhbWVSYXRlID4gMCAmJiBjdXJyZW50RnJhbWVSYXRlIDwgbGV2ZWxJbmZvLmZyYW1lUmF0ZSB8fCBsZXZlbEluZm8uc3VwcG9ydGVkUmVzdWx0ICYmICEoKF9sZXZlbEluZm8kc3VwcG9ydGVkUiA9IGxldmVsSW5mby5zdXBwb3J0ZWRSZXN1bHQuZGVjb2RpbmdJbmZvUmVzdWx0cykgIT0gbnVsbCAmJiBfbGV2ZWxJbmZvJHN1cHBvcnRlZFJbMF0uc21vb3RoKSkge1xuICAgICAgICBsZXZlbHNTa2lwcGVkLnB1c2goaSk7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgY29uc3QgbGV2ZWxEZXRhaWxzID0gbGV2ZWxJbmZvLmRldGFpbHM7XG4gICAgICBjb25zdCBhdmdEdXJhdGlvbiA9IChwYXJ0Q3VycmVudCA/IGxldmVsRGV0YWlscyA9PSBudWxsID8gdm9pZCAwIDogbGV2ZWxEZXRhaWxzLnBhcnRUYXJnZXQgOiBsZXZlbERldGFpbHMgPT0gbnVsbCA/IHZvaWQgMCA6IGxldmVsRGV0YWlscy5hdmVyYWdldGFyZ2V0ZHVyYXRpb24pIHx8IGN1cnJlbnRGcmFnRHVyYXRpb247XG4gICAgICBsZXQgYWRqdXN0ZWRidztcbiAgICAgIC8vIGZvbGxvdyBhbGdvcml0aG0gY2FwdHVyZWQgZnJvbSBzdGFnZWZyaWdodCA6XG4gICAgICAvLyBodHRwczovL2FuZHJvaWQuZ29vZ2xlc291cmNlLmNvbS9wbGF0Zm9ybS9mcmFtZXdvcmtzL2F2LysvbWFzdGVyL21lZGlhL2xpYnN0YWdlZnJpZ2h0L2h0dHBsaXZlL0xpdmVTZXNzaW9uLmNwcFxuICAgICAgLy8gUGljayB0aGUgaGlnaGVzdCBiYW5kd2lkdGggc3RyZWFtIGJlbG93IG9yIGVxdWFsIHRvIGVzdGltYXRlZCBiYW5kd2lkdGguXG4gICAgICAvLyBjb25zaWRlciBvbmx5IDgwJSBvZiB0aGUgYXZhaWxhYmxlIGJhbmR3aWR0aCwgYnV0IGlmIHdlIGFyZSBzd2l0Y2hpbmcgdXAsXG4gICAgICAvLyBiZSBldmVuIG1vcmUgY29uc2VydmF0aXZlICg3MCUpIHRvIGF2b2lkIG92ZXJlc3RpbWF0aW5nIGFuZCBpbW1lZGlhdGVseVxuICAgICAgLy8gc3dpdGNoaW5nIGJhY2suXG4gICAgICBpZiAoIXVwU3dpdGNoKSB7XG4gICAgICAgIGFkanVzdGVkYncgPSBid0ZhY3RvciAqIGN1cnJlbnRCdztcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGFkanVzdGVkYncgPSBid1VwRmFjdG9yICogY3VycmVudEJ3O1xuICAgICAgfVxuXG4gICAgICAvLyBVc2UgYXZlcmFnZSBiaXRyYXRlIHdoZW4gc3RhcnZhdGlvbiBkZWxheSAoYnVmZmVyIGxlbmd0aCkgaXMgZ3Qgb3IgZXEgdHdvIHNlZ21lbnQgZHVyYXRpb25zIGFuZCByZWJ1ZmZlcmluZyBpcyBub3QgZXhwZWN0ZWQgKG1heFN0YXJ2YXRpb25EZWxheSA+IDApXG4gICAgICBjb25zdCBiaXRyYXRlID0gY3VycmVudEZyYWdEdXJhdGlvbiAmJiBidWZmZXJTdGFydmF0aW9uRGVsYXkgPj0gY3VycmVudEZyYWdEdXJhdGlvbiAqIDIgJiYgbWF4U3RhcnZhdGlvbkRlbGF5ID09PSAwID8gbGV2ZWxzW2ldLmF2ZXJhZ2VCaXRyYXRlIDogbGV2ZWxzW2ldLm1heEJpdHJhdGU7XG4gICAgICBjb25zdCBmZXRjaER1cmF0aW9uID0gdGhpcy5nZXRUaW1lVG9Mb2FkRnJhZyh0dGZiRXN0aW1hdGVTZWMsIGFkanVzdGVkYncsIGJpdHJhdGUgKiBhdmdEdXJhdGlvbiwgbGV2ZWxEZXRhaWxzID09PSB1bmRlZmluZWQpO1xuICAgICAgY29uc3QgY2FuU3dpdGNoV2l0aGluVG9sZXJhbmNlID1cbiAgICAgIC8vIGlmIGFkanVzdGVkIGJ3IGlzIGdyZWF0ZXIgdGhhbiBsZXZlbCBiaXRyYXRlIEFORFxuICAgICAgYWRqdXN0ZWRidyA+PSBiaXRyYXRlICYmIChcbiAgICAgIC8vIG5vIGxldmVsIGNoYW5nZSwgb3IgbmV3IGxldmVsIGhhcyBubyBlcnJvciBoaXN0b3J5XG4gICAgICBpID09PSBsYXN0TG9hZGVkRnJhZ0xldmVsIHx8IGxldmVsSW5mby5sb2FkRXJyb3IgPT09IDAgJiYgbGV2ZWxJbmZvLmZyYWdtZW50RXJyb3IgPT09IDApICYmIChcbiAgICAgIC8vIGZyYWdtZW50IGZldGNoRHVyYXRpb24gdW5rbm93biBPUiBsaXZlIHN0cmVhbSBPUiBmcmFnbWVudCBmZXRjaER1cmF0aW9uIGxlc3MgdGhhbiBtYXggYWxsb3dlZCBmZXRjaCBkdXJhdGlvbiwgdGhlbiB0aGlzIGxldmVsIG1hdGNoZXNcbiAgICAgIC8vIHdlIGRvbid0IGFjY291bnQgZm9yIG1heCBGZXRjaCBEdXJhdGlvbiBmb3IgbGl2ZSBzdHJlYW1zLCB0aGlzIGlzIHRvIGF2b2lkIHN3aXRjaGluZyBkb3duIHdoZW4gbmVhciB0aGUgZWRnZSBvZiBsaXZlIHNsaWRpbmcgd2luZG93IC4uLlxuICAgICAgLy8gc3BlY2lhbCBjYXNlIHRvIHN1cHBvcnQgc3RhcnRMZXZlbCA9IC0xIChiaXRyYXRlVGVzdCkgb24gbGl2ZSBzdHJlYW1zIDogaW4gdGhhdCBjYXNlIHdlIHNob3VsZCBub3QgZXhpdCBsb29wIHNvIHRoYXQgZmluZEJlc3RMZXZlbCB3aWxsIHJldHVybiAtMVxuICAgICAgZmV0Y2hEdXJhdGlvbiA8PSB0dGZiRXN0aW1hdGVTZWMgfHwgIWlzRmluaXRlTnVtYmVyKGZldGNoRHVyYXRpb24pIHx8IGxpdmUgJiYgIXRoaXMuYml0cmF0ZVRlc3REZWxheSB8fCBmZXRjaER1cmF0aW9uIDwgbWF4RmV0Y2hEdXJhdGlvbik7XG4gICAgICBpZiAoY2FuU3dpdGNoV2l0aGluVG9sZXJhbmNlKSB7XG4gICAgICAgIGNvbnN0IGZvcmNlZEF1dG9MZXZlbCA9IHRoaXMuZm9yY2VkQXV0b0xldmVsO1xuICAgICAgICBpZiAoaSAhPT0gbG9hZExldmVsICYmIChmb3JjZWRBdXRvTGV2ZWwgPT09IC0xIHx8IGZvcmNlZEF1dG9MZXZlbCAhPT0gbG9hZExldmVsKSkge1xuICAgICAgICAgIGlmIChsZXZlbHNTa2lwcGVkLmxlbmd0aCkge1xuICAgICAgICAgICAgbG9nZ2VyLnRyYWNlKGBbYWJyXSBTa2lwcGVkIGxldmVsKHMpICR7bGV2ZWxzU2tpcHBlZC5qb2luKCcsJyl9IG9mICR7bWF4QXV0b0xldmVsfSBtYXggd2l0aCBDT0RFQ1MgYW5kIFZJREVPLVJBTkdFOlwiJHtsZXZlbHNbbGV2ZWxzU2tpcHBlZFswXV0uY29kZWNzfVwiICR7bGV2ZWxzW2xldmVsc1NraXBwZWRbMF1dLnZpZGVvUmFuZ2V9OyBub3QgY29tcGF0aWJsZSB3aXRoIFwiJHtsZXZlbC5jb2RlY3N9XCIgJHtjdXJyZW50VmlkZW9SYW5nZX1gKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgbG9nZ2VyLmluZm8oYFthYnJdIHN3aXRjaCBjYW5kaWRhdGU6JHtzZWxlY3Rpb25CYXNlTGV2ZWx9LT4ke2l9IGFkanVzdGVkYncoJHtNYXRoLnJvdW5kKGFkanVzdGVkYncpfSktYml0cmF0ZT0ke01hdGgucm91bmQoYWRqdXN0ZWRidyAtIGJpdHJhdGUpfSB0dGZiOiR7dHRmYkVzdGltYXRlU2VjLnRvRml4ZWQoMSl9IGF2Z0R1cmF0aW9uOiR7YXZnRHVyYXRpb24udG9GaXhlZCgxKX0gbWF4RmV0Y2hEdXJhdGlvbjoke21heEZldGNoRHVyYXRpb24udG9GaXhlZCgxKX0gZmV0Y2hEdXJhdGlvbjoke2ZldGNoRHVyYXRpb24udG9GaXhlZCgxKX0gZmlyc3RTZWxlY3Rpb246JHtmaXJzdFNlbGVjdGlvbn0gY29kZWNTZXQ6JHtjdXJyZW50Q29kZWNTZXR9IHZpZGVvUmFuZ2U6JHtjdXJyZW50VmlkZW9SYW5nZX0gaGxzLmxvYWRMZXZlbDoke2xvYWRMZXZlbH1gKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoZmlyc3RTZWxlY3Rpb24pIHtcbiAgICAgICAgICB0aGlzLmZpcnN0U2VsZWN0aW9uID0gaTtcbiAgICAgICAgfVxuICAgICAgICAvLyBhcyB3ZSBhcmUgbG9vcGluZyBmcm9tIGhpZ2hlc3QgdG8gbG93ZXN0LCB0aGlzIHdpbGwgcmV0dXJuIHRoZSBiZXN0IGFjaGlldmFibGUgcXVhbGl0eSBsZXZlbFxuICAgICAgICByZXR1cm4gaTtcbiAgICAgIH1cbiAgICB9XG4gICAgLy8gbm90IGVub3VnaCB0aW1lIGJ1ZGdldCBldmVuIHdpdGggcXVhbGl0eSBsZXZlbCAwIC4uLiByZWJ1ZmZlcmluZyBtaWdodCBoYXBwZW5cbiAgICByZXR1cm4gLTE7XG4gIH1cbiAgc2V0IG5leHRBdXRvTGV2ZWwobmV4dExldmVsKSB7XG4gICAgY29uc3Qge1xuICAgICAgbWF4QXV0b0xldmVsLFxuICAgICAgbWluQXV0b0xldmVsXG4gICAgfSA9IHRoaXMuaGxzO1xuICAgIGNvbnN0IHZhbHVlID0gTWF0aC5taW4oTWF0aC5tYXgobmV4dExldmVsLCBtaW5BdXRvTGV2ZWwpLCBtYXhBdXRvTGV2ZWwpO1xuICAgIGlmICh0aGlzLl9uZXh0QXV0b0xldmVsICE9PSB2YWx1ZSkge1xuICAgICAgdGhpcy5uZXh0QXV0b0xldmVsS2V5ID0gJyc7XG4gICAgICB0aGlzLl9uZXh0QXV0b0xldmVsID0gdmFsdWU7XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogQGlnbm9yZVxuICogU3ViLWNsYXNzIHNwZWNpYWxpemF0aW9uIG9mIEV2ZW50SGFuZGxlciBiYXNlIGNsYXNzLlxuICpcbiAqIFRhc2tMb29wIGFsbG93cyB0byBzY2hlZHVsZSBhIHRhc2sgZnVuY3Rpb24gYmVpbmcgY2FsbGVkIChvcHRpb25uYWx5IHJlcGVhdGVkbHkpIG9uIHRoZSBtYWluIGxvb3AsXG4gKiBzY2hlZHVsZWQgYXN5bmNocm9uZW91c2x5LCBhdm9pZGluZyByZWN1cnNpdmUgY2FsbHMgaW4gdGhlIHNhbWUgdGljay5cbiAqXG4gKiBUaGUgdGFzayBpdHNlbGYgaXMgaW1wbGVtZW50ZWQgaW4gYGRvVGlja2AuIEl0IGNhbiBiZSByZXF1ZXN0ZWQgYW5kIGNhbGxlZCBmb3Igc2luZ2xlIGV4ZWN1dGlvblxuICogdXNpbmcgdGhlIGB0aWNrYCBtZXRob2QuXG4gKlxuICogSXQgd2lsbCBiZSBhc3N1cmVkIHRoYXQgdGhlIHRhc2sgZXhlY3V0aW9uIG1ldGhvZCAoYHRpY2tgKSBvbmx5IGdldHMgY2FsbGVkIG9uY2UgcGVyIG1haW4gbG9vcCBcInRpY2tcIixcbiAqIG5vIG1hdHRlciBob3cgb2Z0ZW4gaXQgZ2V0cyByZXF1ZXN0ZWQgZm9yIGV4ZWN1dGlvbi4gRXhlY3V0aW9uIGluIGZ1cnRoZXIgdGlja3Mgd2lsbCBiZSBzY2hlZHVsZWQgYWNjb3JkaW5nbHkuXG4gKlxuICogSWYgZnVydGhlciBleGVjdXRpb24gcmVxdWVzdHMgaGF2ZSBhbHJlYWR5IGJlZW4gc2NoZWR1bGVkIG9uIHRoZSBuZXh0IHRpY2ssIGl0IGNhbiBiZSBjaGVja2VkIHdpdGggYGhhc05leHRUaWNrYCxcbiAqIGFuZCBjYW5jZWxsZWQgd2l0aCBgY2xlYXJOZXh0VGlja2AuXG4gKlxuICogVGhlIHRhc2sgY2FuIGJlIHNjaGVkdWxlZCBhcyBhbiBpbnRlcnZhbCByZXBlYXRlZGx5IHdpdGggYSBwZXJpb2QgYXMgcGFyYW1ldGVyIChzZWUgYHNldEludGVydmFsYCwgYGNsZWFySW50ZXJ2YWxgKS5cbiAqXG4gKiBTdWItY2xhc3NlcyBuZWVkIHRvIGltcGxlbWVudCB0aGUgYGRvVGlja2AgbWV0aG9kIHdoaWNoIHdpbGwgZWZmZWN0aXZlbHkgaGF2ZSB0aGUgdGFzayBleGVjdXRpb24gcm91dGluZS5cbiAqXG4gKiBGdXJ0aGVyIGV4cGxhbmF0aW9uczpcbiAqXG4gKiBUaGUgYmFzZWNsYXNzIGhhcyBhIGB0aWNrYCBtZXRob2QgdGhhdCB3aWxsIHNjaGVkdWxlIHRoZSBkb1RpY2sgY2FsbC4gSXQgbWF5IGJlIGNhbGxlZCBzeW5jaHJvbmVvdXNseVxuICogb25seSBmb3IgYSBzdGFjay1kZXB0aCBvZiBvbmUuIE9uIHJlLWVudHJhbnQgY2FsbHMsIHN1Yi1zZXF1ZW50IGNhbGxzIGFyZSBzY2hlZHVsZWQgZm9yIG5leHQgbWFpbiBsb29wIHRpY2tzLlxuICpcbiAqIFdoZW4gdGhlIHRhc2sgZXhlY3V0aW9uIChgdGlja2AgbWV0aG9kKSBpcyBjYWxsZWQgaW4gcmUtZW50cmFudCB3YXkgdGhpcyBpcyBkZXRlY3RlZCBhbmRcbiAqIHdlIGFyZSBsaW1pdGluZyB0aGUgdGFzayBleGVjdXRpb24gcGVyIGNhbGwgc3RhY2sgdG8gZXhhY3RseSBvbmUsIGJ1dCBzY2hlZHVsaW5nL3Bvc3QtcG9uaW5nIGZ1cnRoZXJcbiAqIHRhc2sgcHJvY2Vzc2luZyBvbiB0aGUgbmV4dCBtYWluIGxvb3AgaXRlcmF0aW9uIChhbHNvIGtub3duIGFzIFwibmV4dCB0aWNrXCIgaW4gdGhlIE5vZGUvSlMgcnVudGltZSBsaW5nbykuXG4gKi9cbmNsYXNzIFRhc2tMb29wIHtcbiAgY29uc3RydWN0b3IoKSB7XG4gICAgdGhpcy5fYm91bmRUaWNrID0gdm9pZCAwO1xuICAgIHRoaXMuX3RpY2tUaW1lciA9IG51bGw7XG4gICAgdGhpcy5fdGlja0ludGVydmFsID0gbnVsbDtcbiAgICB0aGlzLl90aWNrQ2FsbENvdW50ID0gMDtcbiAgICB0aGlzLl9ib3VuZFRpY2sgPSB0aGlzLnRpY2suYmluZCh0aGlzKTtcbiAgfVxuICBkZXN0cm95KCkge1xuICAgIHRoaXMub25IYW5kbGVyRGVzdHJveWluZygpO1xuICAgIHRoaXMub25IYW5kbGVyRGVzdHJveWVkKCk7XG4gIH1cbiAgb25IYW5kbGVyRGVzdHJveWluZygpIHtcbiAgICAvLyBjbGVhciBhbGwgdGltZXJzIGJlZm9yZSB1bnJlZ2lzdGVyaW5nIGZyb20gZXZlbnQgYnVzXG4gICAgdGhpcy5jbGVhck5leHRUaWNrKCk7XG4gICAgdGhpcy5jbGVhckludGVydmFsKCk7XG4gIH1cbiAgb25IYW5kbGVyRGVzdHJveWVkKCkge31cbiAgaGFzSW50ZXJ2YWwoKSB7XG4gICAgcmV0dXJuICEhdGhpcy5fdGlja0ludGVydmFsO1xuICB9XG4gIGhhc05leHRUaWNrKCkge1xuICAgIHJldHVybiAhIXRoaXMuX3RpY2tUaW1lcjtcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0gbWlsbGlzIC0gSW50ZXJ2YWwgdGltZSAobXMpXG4gICAqIEBldHVybnMgVHJ1ZSB3aGVuIGludGVydmFsIGhhcyBiZWVuIHNjaGVkdWxlZCwgZmFsc2Ugd2hlbiBhbHJlYWR5IHNjaGVkdWxlZCAobm8gZWZmZWN0KVxuICAgKi9cbiAgc2V0SW50ZXJ2YWwobWlsbGlzKSB7XG4gICAgaWYgKCF0aGlzLl90aWNrSW50ZXJ2YWwpIHtcbiAgICAgIHRoaXMuX3RpY2tDYWxsQ291bnQgPSAwO1xuICAgICAgdGhpcy5fdGlja0ludGVydmFsID0gc2VsZi5zZXRJbnRlcnZhbCh0aGlzLl9ib3VuZFRpY2ssIG1pbGxpcyk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgLyoqXG4gICAqIEByZXR1cm5zIFRydWUgd2hlbiBpbnRlcnZhbCB3YXMgY2xlYXJlZCwgZmFsc2Ugd2hlbiBub25lIHdhcyBzZXQgKG5vIGVmZmVjdClcbiAgICovXG4gIGNsZWFySW50ZXJ2YWwoKSB7XG4gICAgaWYgKHRoaXMuX3RpY2tJbnRlcnZhbCkge1xuICAgICAgc2VsZi5jbGVhckludGVydmFsKHRoaXMuX3RpY2tJbnRlcnZhbCk7XG4gICAgICB0aGlzLl90aWNrSW50ZXJ2YWwgPSBudWxsO1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAcmV0dXJucyBUcnVlIHdoZW4gdGltZW91dCB3YXMgY2xlYXJlZCwgZmFsc2Ugd2hlbiBub25lIHdhcyBzZXQgKG5vIGVmZmVjdClcbiAgICovXG4gIGNsZWFyTmV4dFRpY2soKSB7XG4gICAgaWYgKHRoaXMuX3RpY2tUaW1lcikge1xuICAgICAgc2VsZi5jbGVhclRpbWVvdXQodGhpcy5fdGlja1RpbWVyKTtcbiAgICAgIHRoaXMuX3RpY2tUaW1lciA9IG51bGw7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgLyoqXG4gICAqIFdpbGwgY2FsbCB0aGUgc3ViY2xhc3MgZG9UaWNrIGltcGxlbWVudGF0aW9uIGluIHRoaXMgbWFpbiBsb29wIHRpY2tcbiAgICogb3IgaW4gdGhlIG5leHQgb25lICh2aWEgc2V0VGltZW91dCgsMCkpIGluIGNhc2UgaXQgaGFzIGFscmVhZHkgYmVlbiBjYWxsZWRcbiAgICogaW4gdGhpcyB0aWNrIChpbiBjYXNlIHRoaXMgaXMgYSByZS1lbnRyYW50IGNhbGwpLlxuICAgKi9cbiAgdGljaygpIHtcbiAgICB0aGlzLl90aWNrQ2FsbENvdW50Kys7XG4gICAgaWYgKHRoaXMuX3RpY2tDYWxsQ291bnQgPT09IDEpIHtcbiAgICAgIHRoaXMuZG9UaWNrKCk7XG4gICAgICAvLyByZS1lbnRyYW50IGNhbGwgdG8gdGljayBmcm9tIHByZXZpb3VzIGRvVGljayBjYWxsIHN0YWNrXG4gICAgICAvLyAtPiBzY2hlZHVsZSBhIGNhbGwgb24gdGhlIG5leHQgbWFpbiBsb29wIGl0ZXJhdGlvbiB0byBwcm9jZXNzIHRoaXMgdGFzayBwcm9jZXNzaW5nIHJlcXVlc3RcbiAgICAgIGlmICh0aGlzLl90aWNrQ2FsbENvdW50ID4gMSkge1xuICAgICAgICAvLyBtYWtlIHN1cmUgb25seSBvbmUgdGltZXIgZXhpc3RzIGF0IGFueSB0aW1lIGF0IG1heFxuICAgICAgICB0aGlzLnRpY2tJbW1lZGlhdGUoKTtcbiAgICAgIH1cbiAgICAgIHRoaXMuX3RpY2tDYWxsQ291bnQgPSAwO1xuICAgIH1cbiAgfVxuICB0aWNrSW1tZWRpYXRlKCkge1xuICAgIHRoaXMuY2xlYXJOZXh0VGljaygpO1xuICAgIHRoaXMuX3RpY2tUaW1lciA9IHNlbGYuc2V0VGltZW91dCh0aGlzLl9ib3VuZFRpY2ssIDApO1xuICB9XG5cbiAgLyoqXG4gICAqIEZvciBzdWJjbGFzcyB0byBpbXBsZW1lbnQgdGFzayBsb2dpY1xuICAgKiBAYWJzdHJhY3RcbiAgICovXG4gIGRvVGljaygpIHt9XG59XG5cbnZhciBGcmFnbWVudFN0YXRlID0ge1xuICBOT1RfTE9BREVEOiBcIk5PVF9MT0FERURcIixcbiAgQVBQRU5ESU5HOiBcIkFQUEVORElOR1wiLFxuICBQQVJUSUFMOiBcIlBBUlRJQUxcIixcbiAgT0s6IFwiT0tcIlxufTtcbmNsYXNzIEZyYWdtZW50VHJhY2tlciB7XG4gIGNvbnN0cnVjdG9yKGhscykge1xuICAgIHRoaXMuYWN0aXZlUGFydExpc3RzID0gT2JqZWN0LmNyZWF0ZShudWxsKTtcbiAgICB0aGlzLmVuZExpc3RGcmFnbWVudHMgPSBPYmplY3QuY3JlYXRlKG51bGwpO1xuICAgIHRoaXMuZnJhZ21lbnRzID0gT2JqZWN0LmNyZWF0ZShudWxsKTtcbiAgICB0aGlzLnRpbWVSYW5nZXMgPSBPYmplY3QuY3JlYXRlKG51bGwpO1xuICAgIHRoaXMuYnVmZmVyUGFkZGluZyA9IDAuMjtcbiAgICB0aGlzLmhscyA9IHZvaWQgMDtcbiAgICB0aGlzLmhhc0dhcHMgPSBmYWxzZTtcbiAgICB0aGlzLmhscyA9IGhscztcbiAgICB0aGlzLl9yZWdpc3Rlckxpc3RlbmVycygpO1xuICB9XG4gIF9yZWdpc3Rlckxpc3RlbmVycygpIHtcbiAgICBjb25zdCB7XG4gICAgICBobHNcbiAgICB9ID0gdGhpcztcbiAgICBobHMub24oRXZlbnRzLkJVRkZFUl9BUFBFTkRFRCwgdGhpcy5vbkJ1ZmZlckFwcGVuZGVkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkZSQUdfQlVGRkVSRUQsIHRoaXMub25GcmFnQnVmZmVyZWQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuRlJBR19MT0FERUQsIHRoaXMub25GcmFnTG9hZGVkLCB0aGlzKTtcbiAgfVxuICBfdW5yZWdpc3Rlckxpc3RlbmVycygpIHtcbiAgICBjb25zdCB7XG4gICAgICBobHNcbiAgICB9ID0gdGhpcztcbiAgICBobHMub2ZmKEV2ZW50cy5CVUZGRVJfQVBQRU5ERUQsIHRoaXMub25CdWZmZXJBcHBlbmRlZCwgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuRlJBR19CVUZGRVJFRCwgdGhpcy5vbkZyYWdCdWZmZXJlZCwgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuRlJBR19MT0FERUQsIHRoaXMub25GcmFnTG9hZGVkLCB0aGlzKTtcbiAgfVxuICBkZXN0cm95KCkge1xuICAgIHRoaXMuX3VucmVnaXN0ZXJMaXN0ZW5lcnMoKTtcbiAgICAvLyBAdHMtaWdub3JlXG4gICAgdGhpcy5mcmFnbWVudHMgPVxuICAgIC8vIEB0cy1pZ25vcmVcbiAgICB0aGlzLmFjdGl2ZVBhcnRMaXN0cyA9XG4gICAgLy8gQHRzLWlnbm9yZVxuICAgIHRoaXMuZW5kTGlzdEZyYWdtZW50cyA9IHRoaXMudGltZVJhbmdlcyA9IG51bGw7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJuIGEgRnJhZ21lbnQgb3IgUGFydCB3aXRoIGFuIGFwcGVuZGVkIHJhbmdlIHRoYXQgbWF0Y2hlcyB0aGUgcG9zaXRpb24gYW5kIGxldmVsVHlwZVxuICAgKiBPdGhlcndpc2UsIHJldHVybiBudWxsXG4gICAqL1xuICBnZXRBcHBlbmRlZEZyYWcocG9zaXRpb24sIGxldmVsVHlwZSkge1xuICAgIGNvbnN0IGFjdGl2ZVBhcnRzID0gdGhpcy5hY3RpdmVQYXJ0TGlzdHNbbGV2ZWxUeXBlXTtcbiAgICBpZiAoYWN0aXZlUGFydHMpIHtcbiAgICAgIGZvciAobGV0IGkgPSBhY3RpdmVQYXJ0cy5sZW5ndGg7IGktLTspIHtcbiAgICAgICAgY29uc3QgYWN0aXZlUGFydCA9IGFjdGl2ZVBhcnRzW2ldO1xuICAgICAgICBpZiAoIWFjdGl2ZVBhcnQpIHtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBhcHBlbmRlZFBUUyA9IGFjdGl2ZVBhcnQuZW5kO1xuICAgICAgICBpZiAoYWN0aXZlUGFydC5zdGFydCA8PSBwb3NpdGlvbiAmJiBhcHBlbmRlZFBUUyAhPT0gbnVsbCAmJiBwb3NpdGlvbiA8PSBhcHBlbmRlZFBUUykge1xuICAgICAgICAgIHJldHVybiBhY3RpdmVQYXJ0O1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmdldEJ1ZmZlcmVkRnJhZyhwb3NpdGlvbiwgbGV2ZWxUeXBlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm4gYSBidWZmZXJlZCBGcmFnbWVudCB0aGF0IG1hdGNoZXMgdGhlIHBvc2l0aW9uIGFuZCBsZXZlbFR5cGUuXG4gICAqIEEgYnVmZmVyZWQgRnJhZ21lbnQgaXMgb25lIHdob3NlIGxvYWRpbmcsIHBhcnNpbmcgYW5kIGFwcGVuZGluZyBpcyBkb25lIChjb21wbGV0ZWQgb3IgXCJwYXJ0aWFsXCIgbWVhbmluZyBhYm9ydGVkKS5cbiAgICogSWYgbm90IGZvdW5kIGFueSBGcmFnbWVudCwgcmV0dXJuIG51bGxcbiAgICovXG4gIGdldEJ1ZmZlcmVkRnJhZyhwb3NpdGlvbiwgbGV2ZWxUeXBlKSB7XG4gICAgY29uc3Qge1xuICAgICAgZnJhZ21lbnRzXG4gICAgfSA9IHRoaXM7XG4gICAgY29uc3Qga2V5cyA9IE9iamVjdC5rZXlzKGZyYWdtZW50cyk7XG4gICAgZm9yIChsZXQgaSA9IGtleXMubGVuZ3RoOyBpLS07KSB7XG4gICAgICBjb25zdCBmcmFnbWVudEVudGl0eSA9IGZyYWdtZW50c1trZXlzW2ldXTtcbiAgICAgIGlmICgoZnJhZ21lbnRFbnRpdHkgPT0gbnVsbCA/IHZvaWQgMCA6IGZyYWdtZW50RW50aXR5LmJvZHkudHlwZSkgPT09IGxldmVsVHlwZSAmJiBmcmFnbWVudEVudGl0eS5idWZmZXJlZCkge1xuICAgICAgICBjb25zdCBmcmFnID0gZnJhZ21lbnRFbnRpdHkuYm9keTtcbiAgICAgICAgaWYgKGZyYWcuc3RhcnQgPD0gcG9zaXRpb24gJiYgcG9zaXRpb24gPD0gZnJhZy5lbmQpIHtcbiAgICAgICAgICByZXR1cm4gZnJhZztcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBQYXJ0aWFsIGZyYWdtZW50cyBlZmZlY3RlZCBieSBjb2RlZCBmcmFtZSBldmljdGlvbiB3aWxsIGJlIHJlbW92ZWRcbiAgICogVGhlIGJyb3dzZXIgd2lsbCB1bmxvYWQgcGFydHMgb2YgdGhlIGJ1ZmZlciB0byBmcmVlIHVwIG1lbW9yeSBmb3IgbmV3IGJ1ZmZlciBkYXRhXG4gICAqIEZyYWdtZW50cyB3aWxsIG5lZWQgdG8gYmUgcmVsb2FkZWQgd2hlbiB0aGUgYnVmZmVyIGlzIGZyZWVkIHVwLCByZW1vdmluZyBwYXJ0aWFsIGZyYWdtZW50cyB3aWxsIGFsbG93IHRoZW0gdG8gcmVsb2FkKHNpbmNlIHRoZXJlIG1pZ2h0IGJlIHBhcnRzIHRoYXQgYXJlIHN0aWxsIHBsYXlhYmxlKVxuICAgKi9cbiAgZGV0ZWN0RXZpY3RlZEZyYWdtZW50cyhlbGVtZW50YXJ5U3RyZWFtLCB0aW1lUmFuZ2UsIHBsYXlsaXN0VHlwZSwgYXBwZW5kZWRQYXJ0KSB7XG4gICAgaWYgKHRoaXMudGltZVJhbmdlcykge1xuICAgICAgdGhpcy50aW1lUmFuZ2VzW2VsZW1lbnRhcnlTdHJlYW1dID0gdGltZVJhbmdlO1xuICAgIH1cbiAgICAvLyBDaGVjayBpZiBhbnkgZmxhZ2dlZCBmcmFnbWVudHMgaGF2ZSBiZWVuIHVubG9hZGVkXG4gICAgLy8gZXhjbHVkaW5nIGFueXRoaW5nIG5ld2VyIHRoYW4gYXBwZW5kZWRQYXJ0U25cbiAgICBjb25zdCBhcHBlbmRlZFBhcnRTbiA9IChhcHBlbmRlZFBhcnQgPT0gbnVsbCA/IHZvaWQgMCA6IGFwcGVuZGVkUGFydC5mcmFnbWVudC5zbikgfHwgLTE7XG4gICAgT2JqZWN0LmtleXModGhpcy5mcmFnbWVudHMpLmZvckVhY2goa2V5ID0+IHtcbiAgICAgIGNvbnN0IGZyYWdtZW50RW50aXR5ID0gdGhpcy5mcmFnbWVudHNba2V5XTtcbiAgICAgIGlmICghZnJhZ21lbnRFbnRpdHkpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgaWYgKGFwcGVuZGVkUGFydFNuID49IGZyYWdtZW50RW50aXR5LmJvZHkuc24pIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgaWYgKCFmcmFnbWVudEVudGl0eS5idWZmZXJlZCAmJiAhZnJhZ21lbnRFbnRpdHkubG9hZGVkKSB7XG4gICAgICAgIGlmIChmcmFnbWVudEVudGl0eS5ib2R5LnR5cGUgPT09IHBsYXlsaXN0VHlwZSkge1xuICAgICAgICAgIHRoaXMucmVtb3ZlRnJhZ21lbnQoZnJhZ21lbnRFbnRpdHkuYm9keSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY29uc3QgZXNEYXRhID0gZnJhZ21lbnRFbnRpdHkucmFuZ2VbZWxlbWVudGFyeVN0cmVhbV07XG4gICAgICBpZiAoIWVzRGF0YSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBlc0RhdGEudGltZS5zb21lKHRpbWUgPT4ge1xuICAgICAgICBjb25zdCBpc05vdEJ1ZmZlcmVkID0gIXRoaXMuaXNUaW1lQnVmZmVyZWQodGltZS5zdGFydFBUUywgdGltZS5lbmRQVFMsIHRpbWVSYW5nZSk7XG4gICAgICAgIGlmIChpc05vdEJ1ZmZlcmVkKSB7XG4gICAgICAgICAgLy8gVW5yZWdpc3RlciBwYXJ0aWFsIGZyYWdtZW50IGFzIGl0IG5lZWRzIHRvIGxvYWQgYWdhaW4gdG8gYmUgcmV1c2VkXG4gICAgICAgICAgdGhpcy5yZW1vdmVGcmFnbWVudChmcmFnbWVudEVudGl0eS5ib2R5KTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gaXNOb3RCdWZmZXJlZDtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrcyBpZiB0aGUgZnJhZ21lbnQgcGFzc2VkIGluIGlzIGxvYWRlZCBpbiB0aGUgYnVmZmVyIHByb3Blcmx5XG4gICAqIFBhcnRpYWxseSBsb2FkZWQgZnJhZ21lbnRzIHdpbGwgYmUgcmVnaXN0ZXJlZCBhcyBhIHBhcnRpYWwgZnJhZ21lbnRcbiAgICovXG4gIGRldGVjdFBhcnRpYWxGcmFnbWVudHMoZGF0YSkge1xuICAgIGNvbnN0IHRpbWVSYW5nZXMgPSB0aGlzLnRpbWVSYW5nZXM7XG4gICAgY29uc3Qge1xuICAgICAgZnJhZyxcbiAgICAgIHBhcnRcbiAgICB9ID0gZGF0YTtcbiAgICBpZiAoIXRpbWVSYW5nZXMgfHwgZnJhZy5zbiA9PT0gJ2luaXRTZWdtZW50Jykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBmcmFnS2V5ID0gZ2V0RnJhZ21lbnRLZXkoZnJhZyk7XG4gICAgY29uc3QgZnJhZ21lbnRFbnRpdHkgPSB0aGlzLmZyYWdtZW50c1tmcmFnS2V5XTtcbiAgICBpZiAoIWZyYWdtZW50RW50aXR5IHx8IGZyYWdtZW50RW50aXR5LmJ1ZmZlcmVkICYmIGZyYWcuZ2FwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGlzRnJhZ0hpbnQgPSAhZnJhZy5yZWx1cmw7XG4gICAgT2JqZWN0LmtleXModGltZVJhbmdlcykuZm9yRWFjaChlbGVtZW50YXJ5U3RyZWFtID0+IHtcbiAgICAgIGNvbnN0IHN0cmVhbUluZm8gPSBmcmFnLmVsZW1lbnRhcnlTdHJlYW1zW2VsZW1lbnRhcnlTdHJlYW1dO1xuICAgICAgaWYgKCFzdHJlYW1JbmZvKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHRpbWVSYW5nZSA9IHRpbWVSYW5nZXNbZWxlbWVudGFyeVN0cmVhbV07XG4gICAgICBjb25zdCBwYXJ0aWFsID0gaXNGcmFnSGludCB8fCBzdHJlYW1JbmZvLnBhcnRpYWwgPT09IHRydWU7XG4gICAgICBmcmFnbWVudEVudGl0eS5yYW5nZVtlbGVtZW50YXJ5U3RyZWFtXSA9IHRoaXMuZ2V0QnVmZmVyZWRUaW1lcyhmcmFnLCBwYXJ0LCBwYXJ0aWFsLCB0aW1lUmFuZ2UpO1xuICAgIH0pO1xuICAgIGZyYWdtZW50RW50aXR5LmxvYWRlZCA9IG51bGw7XG4gICAgaWYgKE9iamVjdC5rZXlzKGZyYWdtZW50RW50aXR5LnJhbmdlKS5sZW5ndGgpIHtcbiAgICAgIGZyYWdtZW50RW50aXR5LmJ1ZmZlcmVkID0gdHJ1ZTtcbiAgICAgIGNvbnN0IGVuZExpc3QgPSBmcmFnbWVudEVudGl0eS5ib2R5LmVuZExpc3QgPSBmcmFnLmVuZExpc3QgfHwgZnJhZ21lbnRFbnRpdHkuYm9keS5lbmRMaXN0O1xuICAgICAgaWYgKGVuZExpc3QpIHtcbiAgICAgICAgdGhpcy5lbmRMaXN0RnJhZ21lbnRzW2ZyYWdtZW50RW50aXR5LmJvZHkudHlwZV0gPSBmcmFnbWVudEVudGl0eTtcbiAgICAgIH1cbiAgICAgIGlmICghaXNQYXJ0aWFsKGZyYWdtZW50RW50aXR5KSkge1xuICAgICAgICAvLyBSZW1vdmUgb2xkZXIgZnJhZ21lbnQgcGFydHMgZnJvbSBsb29rdXAgYWZ0ZXIgZnJhZyBpcyB0cmFja2VkIGFzIGJ1ZmZlcmVkXG4gICAgICAgIHRoaXMucmVtb3ZlUGFydHMoZnJhZy5zbiAtIDEsIGZyYWcudHlwZSk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIHJlbW92ZSBmcmFnbWVudCBpZiBub3RoaW5nIHdhcyBhcHBlbmRlZFxuICAgICAgdGhpcy5yZW1vdmVGcmFnbWVudChmcmFnbWVudEVudGl0eS5ib2R5KTtcbiAgICB9XG4gIH1cbiAgcmVtb3ZlUGFydHMoc25Ub0tlZXAsIGxldmVsVHlwZSkge1xuICAgIGNvbnN0IGFjdGl2ZVBhcnRzID0gdGhpcy5hY3RpdmVQYXJ0TGlzdHNbbGV2ZWxUeXBlXTtcbiAgICBpZiAoIWFjdGl2ZVBhcnRzKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuYWN0aXZlUGFydExpc3RzW2xldmVsVHlwZV0gPSBhY3RpdmVQYXJ0cy5maWx0ZXIocGFydCA9PiBwYXJ0LmZyYWdtZW50LnNuID49IHNuVG9LZWVwKTtcbiAgfVxuICBmcmFnQnVmZmVyZWQoZnJhZywgZm9yY2UpIHtcbiAgICBjb25zdCBmcmFnS2V5ID0gZ2V0RnJhZ21lbnRLZXkoZnJhZyk7XG4gICAgbGV0IGZyYWdtZW50RW50aXR5ID0gdGhpcy5mcmFnbWVudHNbZnJhZ0tleV07XG4gICAgaWYgKCFmcmFnbWVudEVudGl0eSAmJiBmb3JjZSkge1xuICAgICAgZnJhZ21lbnRFbnRpdHkgPSB0aGlzLmZyYWdtZW50c1tmcmFnS2V5XSA9IHtcbiAgICAgICAgYm9keTogZnJhZyxcbiAgICAgICAgYXBwZW5kZWRQVFM6IG51bGwsXG4gICAgICAgIGxvYWRlZDogbnVsbCxcbiAgICAgICAgYnVmZmVyZWQ6IGZhbHNlLFxuICAgICAgICByYW5nZTogT2JqZWN0LmNyZWF0ZShudWxsKVxuICAgICAgfTtcbiAgICAgIGlmIChmcmFnLmdhcCkge1xuICAgICAgICB0aGlzLmhhc0dhcHMgPSB0cnVlO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAoZnJhZ21lbnRFbnRpdHkpIHtcbiAgICAgIGZyYWdtZW50RW50aXR5LmxvYWRlZCA9IG51bGw7XG4gICAgICBmcmFnbWVudEVudGl0eS5idWZmZXJlZCA9IHRydWU7XG4gICAgfVxuICB9XG4gIGdldEJ1ZmZlcmVkVGltZXMoZnJhZ21lbnQsIHBhcnQsIHBhcnRpYWwsIHRpbWVSYW5nZSkge1xuICAgIGNvbnN0IGJ1ZmZlcmVkID0ge1xuICAgICAgdGltZTogW10sXG4gICAgICBwYXJ0aWFsXG4gICAgfTtcbiAgICBjb25zdCBzdGFydFBUUyA9IGZyYWdtZW50LnN0YXJ0O1xuICAgIGNvbnN0IGVuZFBUUyA9IGZyYWdtZW50LmVuZDtcbiAgICBjb25zdCBtaW5FbmRQVFMgPSBmcmFnbWVudC5taW5FbmRQVFMgfHwgZW5kUFRTO1xuICAgIGNvbnN0IG1heFN0YXJ0UFRTID0gZnJhZ21lbnQubWF4U3RhcnRQVFMgfHwgc3RhcnRQVFM7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aW1lUmFuZ2UubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IHN0YXJ0VGltZSA9IHRpbWVSYW5nZS5zdGFydChpKSAtIHRoaXMuYnVmZmVyUGFkZGluZztcbiAgICAgIGNvbnN0IGVuZFRpbWUgPSB0aW1lUmFuZ2UuZW5kKGkpICsgdGhpcy5idWZmZXJQYWRkaW5nO1xuICAgICAgaWYgKG1heFN0YXJ0UFRTID49IHN0YXJ0VGltZSAmJiBtaW5FbmRQVFMgPD0gZW5kVGltZSkge1xuICAgICAgICAvLyBGcmFnbWVudCBpcyBlbnRpcmVseSBjb250YWluZWQgaW4gYnVmZmVyXG4gICAgICAgIC8vIE5vIG5lZWQgdG8gY2hlY2sgdGhlIG90aGVyIHRpbWVSYW5nZSB0aW1lcyBzaW5jZSBpdCdzIGNvbXBsZXRlbHkgcGxheWFibGVcbiAgICAgICAgYnVmZmVyZWQudGltZS5wdXNoKHtcbiAgICAgICAgICBzdGFydFBUUzogTWF0aC5tYXgoc3RhcnRQVFMsIHRpbWVSYW5nZS5zdGFydChpKSksXG4gICAgICAgICAgZW5kUFRTOiBNYXRoLm1pbihlbmRQVFMsIHRpbWVSYW5nZS5lbmQoaSkpXG4gICAgICAgIH0pO1xuICAgICAgICBicmVhaztcbiAgICAgIH0gZWxzZSBpZiAoc3RhcnRQVFMgPCBlbmRUaW1lICYmIGVuZFBUUyA+IHN0YXJ0VGltZSkge1xuICAgICAgICBjb25zdCBzdGFydCA9IE1hdGgubWF4KHN0YXJ0UFRTLCB0aW1lUmFuZ2Uuc3RhcnQoaSkpO1xuICAgICAgICBjb25zdCBlbmQgPSBNYXRoLm1pbihlbmRQVFMsIHRpbWVSYW5nZS5lbmQoaSkpO1xuICAgICAgICBpZiAoZW5kID4gc3RhcnQpIHtcbiAgICAgICAgICBidWZmZXJlZC5wYXJ0aWFsID0gdHJ1ZTtcbiAgICAgICAgICAvLyBDaGVjayBmb3IgaW50ZXJzZWN0aW9uIHdpdGggYnVmZmVyXG4gICAgICAgICAgLy8gR2V0IHBsYXlhYmxlIHNlY3Rpb25zIG9mIHRoZSBmcmFnbWVudFxuICAgICAgICAgIGJ1ZmZlcmVkLnRpbWUucHVzaCh7XG4gICAgICAgICAgICBzdGFydFBUUzogc3RhcnQsXG4gICAgICAgICAgICBlbmRQVFM6IGVuZFxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGVuZFBUUyA8PSBzdGFydFRpbWUpIHtcbiAgICAgICAgLy8gTm8gbmVlZCB0byBjaGVjayB0aGUgcmVzdCBvZiB0aGUgdGltZVJhbmdlIGFzIGl0IGlzIGluIG9yZGVyXG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gYnVmZmVyZWQ7XG4gIH1cblxuICAvKipcbiAgICogR2V0cyB0aGUgcGFydGlhbCBmcmFnbWVudCBmb3IgYSBjZXJ0YWluIHRpbWVcbiAgICovXG4gIGdldFBhcnRpYWxGcmFnbWVudCh0aW1lKSB7XG4gICAgbGV0IGJlc3RGcmFnbWVudCA9IG51bGw7XG4gICAgbGV0IHRpbWVQYWRkaW5nO1xuICAgIGxldCBzdGFydFRpbWU7XG4gICAgbGV0IGVuZFRpbWU7XG4gICAgbGV0IGJlc3RPdmVybGFwID0gMDtcbiAgICBjb25zdCB7XG4gICAgICBidWZmZXJQYWRkaW5nLFxuICAgICAgZnJhZ21lbnRzXG4gICAgfSA9IHRoaXM7XG4gICAgT2JqZWN0LmtleXMoZnJhZ21lbnRzKS5mb3JFYWNoKGtleSA9PiB7XG4gICAgICBjb25zdCBmcmFnbWVudEVudGl0eSA9IGZyYWdtZW50c1trZXldO1xuICAgICAgaWYgKCFmcmFnbWVudEVudGl0eSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBpZiAoaXNQYXJ0aWFsKGZyYWdtZW50RW50aXR5KSkge1xuICAgICAgICBzdGFydFRpbWUgPSBmcmFnbWVudEVudGl0eS5ib2R5LnN0YXJ0IC0gYnVmZmVyUGFkZGluZztcbiAgICAgICAgZW5kVGltZSA9IGZyYWdtZW50RW50aXR5LmJvZHkuZW5kICsgYnVmZmVyUGFkZGluZztcbiAgICAgICAgaWYgKHRpbWUgPj0gc3RhcnRUaW1lICYmIHRpbWUgPD0gZW5kVGltZSkge1xuICAgICAgICAgIC8vIFVzZSB0aGUgZnJhZ21lbnQgdGhhdCBoYXMgdGhlIG1vc3QgcGFkZGluZyBmcm9tIHN0YXJ0IGFuZCBlbmQgdGltZVxuICAgICAgICAgIHRpbWVQYWRkaW5nID0gTWF0aC5taW4odGltZSAtIHN0YXJ0VGltZSwgZW5kVGltZSAtIHRpbWUpO1xuICAgICAgICAgIGlmIChiZXN0T3ZlcmxhcCA8PSB0aW1lUGFkZGluZykge1xuICAgICAgICAgICAgYmVzdEZyYWdtZW50ID0gZnJhZ21lbnRFbnRpdHkuYm9keTtcbiAgICAgICAgICAgIGJlc3RPdmVybGFwID0gdGltZVBhZGRpbmc7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gICAgcmV0dXJuIGJlc3RGcmFnbWVudDtcbiAgfVxuICBpc0VuZExpc3RBcHBlbmRlZCh0eXBlKSB7XG4gICAgY29uc3QgbGFzdEZyYWdtZW50RW50aXR5ID0gdGhpcy5lbmRMaXN0RnJhZ21lbnRzW3R5cGVdO1xuICAgIHJldHVybiBsYXN0RnJhZ21lbnRFbnRpdHkgIT09IHVuZGVmaW5lZCAmJiAobGFzdEZyYWdtZW50RW50aXR5LmJ1ZmZlcmVkIHx8IGlzUGFydGlhbChsYXN0RnJhZ21lbnRFbnRpdHkpKTtcbiAgfVxuICBnZXRTdGF0ZShmcmFnbWVudCkge1xuICAgIGNvbnN0IGZyYWdLZXkgPSBnZXRGcmFnbWVudEtleShmcmFnbWVudCk7XG4gICAgY29uc3QgZnJhZ21lbnRFbnRpdHkgPSB0aGlzLmZyYWdtZW50c1tmcmFnS2V5XTtcbiAgICBpZiAoZnJhZ21lbnRFbnRpdHkpIHtcbiAgICAgIGlmICghZnJhZ21lbnRFbnRpdHkuYnVmZmVyZWQpIHtcbiAgICAgICAgcmV0dXJuIEZyYWdtZW50U3RhdGUuQVBQRU5ESU5HO1xuICAgICAgfSBlbHNlIGlmIChpc1BhcnRpYWwoZnJhZ21lbnRFbnRpdHkpKSB7XG4gICAgICAgIHJldHVybiBGcmFnbWVudFN0YXRlLlBBUlRJQUw7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gRnJhZ21lbnRTdGF0ZS5PSztcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIEZyYWdtZW50U3RhdGUuTk9UX0xPQURFRDtcbiAgfVxuICBpc1RpbWVCdWZmZXJlZChzdGFydFBUUywgZW5kUFRTLCB0aW1lUmFuZ2UpIHtcbiAgICBsZXQgc3RhcnRUaW1lO1xuICAgIGxldCBlbmRUaW1lO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGltZVJhbmdlLmxlbmd0aDsgaSsrKSB7XG4gICAgICBzdGFydFRpbWUgPSB0aW1lUmFuZ2Uuc3RhcnQoaSkgLSB0aGlzLmJ1ZmZlclBhZGRpbmc7XG4gICAgICBlbmRUaW1lID0gdGltZVJhbmdlLmVuZChpKSArIHRoaXMuYnVmZmVyUGFkZGluZztcbiAgICAgIGlmIChzdGFydFBUUyA+PSBzdGFydFRpbWUgJiYgZW5kUFRTIDw9IGVuZFRpbWUpIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgICBpZiAoZW5kUFRTIDw9IHN0YXJ0VGltZSkge1xuICAgICAgICAvLyBObyBuZWVkIHRvIGNoZWNrIHRoZSByZXN0IG9mIHRoZSB0aW1lUmFuZ2UgYXMgaXQgaXMgaW4gb3JkZXJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgb25GcmFnTG9hZGVkKGV2ZW50LCBkYXRhKSB7XG4gICAgY29uc3Qge1xuICAgICAgZnJhZyxcbiAgICAgIHBhcnRcbiAgICB9ID0gZGF0YTtcbiAgICAvLyBkb24ndCB0cmFjayBpbml0c2VnbWVudCAoZm9yIHdoaWNoIHNuIGlzIG5vdCBhIG51bWJlcilcbiAgICAvLyBkb24ndCB0cmFjayBmcmFncyB1c2VkIGZvciBiaXRyYXRlVGVzdCwgdGhleSdyZSBpcnJlbGV2YW50LlxuICAgIGlmIChmcmFnLnNuID09PSAnaW5pdFNlZ21lbnQnIHx8IGZyYWcuYml0cmF0ZVRlc3QpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBGcmFnbWVudCBlbnRpdHkgYGxvYWRlZGAgRnJhZ0xvYWRlZERhdGEgaXMgbnVsbCB3aGVuIGxvYWRpbmcgcGFydHNcbiAgICBjb25zdCBsb2FkZWQgPSBwYXJ0ID8gbnVsbCA6IGRhdGE7XG4gICAgY29uc3QgZnJhZ0tleSA9IGdldEZyYWdtZW50S2V5KGZyYWcpO1xuICAgIHRoaXMuZnJhZ21lbnRzW2ZyYWdLZXldID0ge1xuICAgICAgYm9keTogZnJhZyxcbiAgICAgIGFwcGVuZGVkUFRTOiBudWxsLFxuICAgICAgbG9hZGVkLFxuICAgICAgYnVmZmVyZWQ6IGZhbHNlLFxuICAgICAgcmFuZ2U6IE9iamVjdC5jcmVhdGUobnVsbClcbiAgICB9O1xuICB9XG4gIG9uQnVmZmVyQXBwZW5kZWQoZXZlbnQsIGRhdGEpIHtcbiAgICBjb25zdCB7XG4gICAgICBmcmFnLFxuICAgICAgcGFydCxcbiAgICAgIHRpbWVSYW5nZXNcbiAgICB9ID0gZGF0YTtcbiAgICBpZiAoZnJhZy5zbiA9PT0gJ2luaXRTZWdtZW50Jykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBwbGF5bGlzdFR5cGUgPSBmcmFnLnR5cGU7XG4gICAgaWYgKHBhcnQpIHtcbiAgICAgIGxldCBhY3RpdmVQYXJ0cyA9IHRoaXMuYWN0aXZlUGFydExpc3RzW3BsYXlsaXN0VHlwZV07XG4gICAgICBpZiAoIWFjdGl2ZVBhcnRzKSB7XG4gICAgICAgIHRoaXMuYWN0aXZlUGFydExpc3RzW3BsYXlsaXN0VHlwZV0gPSBhY3RpdmVQYXJ0cyA9IFtdO1xuICAgICAgfVxuICAgICAgYWN0aXZlUGFydHMucHVzaChwYXJ0KTtcbiAgICB9XG4gICAgLy8gU3RvcmUgdGhlIGxhdGVzdCB0aW1lUmFuZ2VzIGxvYWRlZCBpbiB0aGUgYnVmZmVyXG4gICAgdGhpcy50aW1lUmFuZ2VzID0gdGltZVJhbmdlcztcbiAgICBPYmplY3Qua2V5cyh0aW1lUmFuZ2VzKS5mb3JFYWNoKGVsZW1lbnRhcnlTdHJlYW0gPT4ge1xuICAgICAgY29uc3QgdGltZVJhbmdlID0gdGltZVJhbmdlc1tlbGVtZW50YXJ5U3RyZWFtXTtcbiAgICAgIHRoaXMuZGV0ZWN0RXZpY3RlZEZyYWdtZW50cyhlbGVtZW50YXJ5U3RyZWFtLCB0aW1lUmFuZ2UsIHBsYXlsaXN0VHlwZSwgcGFydCk7XG4gICAgfSk7XG4gIH1cbiAgb25GcmFnQnVmZmVyZWQoZXZlbnQsIGRhdGEpIHtcbiAgICB0aGlzLmRldGVjdFBhcnRpYWxGcmFnbWVudHMoZGF0YSk7XG4gIH1cbiAgaGFzRnJhZ21lbnQoZnJhZ21lbnQpIHtcbiAgICBjb25zdCBmcmFnS2V5ID0gZ2V0RnJhZ21lbnRLZXkoZnJhZ21lbnQpO1xuICAgIHJldHVybiAhIXRoaXMuZnJhZ21lbnRzW2ZyYWdLZXldO1xuICB9XG4gIGhhc1BhcnRzKHR5cGUpIHtcbiAgICB2YXIgX3RoaXMkYWN0aXZlUGFydExpc3RzO1xuICAgIHJldHVybiAhISgoX3RoaXMkYWN0aXZlUGFydExpc3RzID0gdGhpcy5hY3RpdmVQYXJ0TGlzdHNbdHlwZV0pICE9IG51bGwgJiYgX3RoaXMkYWN0aXZlUGFydExpc3RzLmxlbmd0aCk7XG4gIH1cbiAgcmVtb3ZlRnJhZ21lbnRzSW5SYW5nZShzdGFydCwgZW5kLCBwbGF5bGlzdFR5cGUsIHdpdGhHYXBPbmx5LCB1bmJ1ZmZlcmVkT25seSkge1xuICAgIGlmICh3aXRoR2FwT25seSAmJiAhdGhpcy5oYXNHYXBzKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIE9iamVjdC5rZXlzKHRoaXMuZnJhZ21lbnRzKS5mb3JFYWNoKGtleSA9PiB7XG4gICAgICBjb25zdCBmcmFnbWVudEVudGl0eSA9IHRoaXMuZnJhZ21lbnRzW2tleV07XG4gICAgICBpZiAoIWZyYWdtZW50RW50aXR5KSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGZyYWcgPSBmcmFnbWVudEVudGl0eS5ib2R5O1xuICAgICAgaWYgKGZyYWcudHlwZSAhPT0gcGxheWxpc3RUeXBlIHx8IHdpdGhHYXBPbmx5ICYmICFmcmFnLmdhcCkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBpZiAoZnJhZy5zdGFydCA8IGVuZCAmJiBmcmFnLmVuZCA+IHN0YXJ0ICYmIChmcmFnbWVudEVudGl0eS5idWZmZXJlZCB8fCB1bmJ1ZmZlcmVkT25seSkpIHtcbiAgICAgICAgdGhpcy5yZW1vdmVGcmFnbWVudChmcmFnKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuICByZW1vdmVGcmFnbWVudChmcmFnbWVudCkge1xuICAgIGNvbnN0IGZyYWdLZXkgPSBnZXRGcmFnbWVudEtleShmcmFnbWVudCk7XG4gICAgZnJhZ21lbnQuc3RhdHMubG9hZGVkID0gMDtcbiAgICBmcmFnbWVudC5jbGVhckVsZW1lbnRhcnlTdHJlYW1JbmZvKCk7XG4gICAgY29uc3QgYWN0aXZlUGFydHMgPSB0aGlzLmFjdGl2ZVBhcnRMaXN0c1tmcmFnbWVudC50eXBlXTtcbiAgICBpZiAoYWN0aXZlUGFydHMpIHtcbiAgICAgIGNvbnN0IHNuVG9SZW1vdmUgPSBmcmFnbWVudC5zbjtcbiAgICAgIHRoaXMuYWN0aXZlUGFydExpc3RzW2ZyYWdtZW50LnR5cGVdID0gYWN0aXZlUGFydHMuZmlsdGVyKHBhcnQgPT4gcGFydC5mcmFnbWVudC5zbiAhPT0gc25Ub1JlbW92ZSk7XG4gICAgfVxuICAgIGRlbGV0ZSB0aGlzLmZyYWdtZW50c1tmcmFnS2V5XTtcbiAgICBpZiAoZnJhZ21lbnQuZW5kTGlzdCkge1xuICAgICAgZGVsZXRlIHRoaXMuZW5kTGlzdEZyYWdtZW50c1tmcmFnbWVudC50eXBlXTtcbiAgICB9XG4gIH1cbiAgcmVtb3ZlQWxsRnJhZ21lbnRzKCkge1xuICAgIHRoaXMuZnJhZ21lbnRzID0gT2JqZWN0LmNyZWF0ZShudWxsKTtcbiAgICB0aGlzLmVuZExpc3RGcmFnbWVudHMgPSBPYmplY3QuY3JlYXRlKG51bGwpO1xuICAgIHRoaXMuYWN0aXZlUGFydExpc3RzID0gT2JqZWN0LmNyZWF0ZShudWxsKTtcbiAgICB0aGlzLmhhc0dhcHMgPSBmYWxzZTtcbiAgfVxufVxuZnVuY3Rpb24gaXNQYXJ0aWFsKGZyYWdtZW50RW50aXR5KSB7XG4gIHZhciBfZnJhZ21lbnRFbnRpdHkkcmFuZ2UsIF9mcmFnbWVudEVudGl0eSRyYW5nZTIsIF9mcmFnbWVudEVudGl0eSRyYW5nZTM7XG4gIHJldHVybiBmcmFnbWVudEVudGl0eS5idWZmZXJlZCAmJiAoZnJhZ21lbnRFbnRpdHkuYm9keS5nYXAgfHwgKChfZnJhZ21lbnRFbnRpdHkkcmFuZ2UgPSBmcmFnbWVudEVudGl0eS5yYW5nZS52aWRlbykgPT0gbnVsbCA/IHZvaWQgMCA6IF9mcmFnbWVudEVudGl0eSRyYW5nZS5wYXJ0aWFsKSB8fCAoKF9mcmFnbWVudEVudGl0eSRyYW5nZTIgPSBmcmFnbWVudEVudGl0eS5yYW5nZS5hdWRpbykgPT0gbnVsbCA/IHZvaWQgMCA6IF9mcmFnbWVudEVudGl0eSRyYW5nZTIucGFydGlhbCkgfHwgKChfZnJhZ21lbnRFbnRpdHkkcmFuZ2UzID0gZnJhZ21lbnRFbnRpdHkucmFuZ2UuYXVkaW92aWRlbykgPT0gbnVsbCA/IHZvaWQgMCA6IF9mcmFnbWVudEVudGl0eSRyYW5nZTMucGFydGlhbCkpO1xufVxuZnVuY3Rpb24gZ2V0RnJhZ21lbnRLZXkoZnJhZ21lbnQpIHtcbiAgcmV0dXJuIGAke2ZyYWdtZW50LnR5cGV9XyR7ZnJhZ21lbnQubGV2ZWx9XyR7ZnJhZ21lbnQuc259YDtcbn1cblxuLyoqXG4gKiBQcm92aWRlcyBtZXRob2RzIGRlYWxpbmcgd2l0aCBidWZmZXIgbGVuZ3RoIHJldHJpZXZhbCBmb3IgZXhhbXBsZS5cbiAqXG4gKiBJbiBnZW5lcmFsLCBhIGhlbHBlciBhcm91bmQgSFRNTDUgTWVkaWFFbGVtZW50IFRpbWVSYW5nZXMgZ2F0aGVyZWQgZnJvbSBgYnVmZmVyZWRgIHByb3BlcnR5LlxuICpcbiAqIEFsc28gQHNlZSBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9BUEkvSFRNTE1lZGlhRWxlbWVudC9idWZmZXJlZFxuICovXG5cbmNvbnN0IG5vb3BCdWZmZXJlZCA9IHtcbiAgbGVuZ3RoOiAwLFxuICBzdGFydDogKCkgPT4gMCxcbiAgZW5kOiAoKSA9PiAwXG59O1xuY2xhc3MgQnVmZmVySGVscGVyIHtcbiAgLyoqXG4gICAqIFJldHVybiB0cnVlIGlmIGBtZWRpYWAncyBidWZmZXJlZCBpbmNsdWRlIGBwb3NpdGlvbmBcbiAgICovXG4gIHN0YXRpYyBpc0J1ZmZlcmVkKG1lZGlhLCBwb3NpdGlvbikge1xuICAgIHRyeSB7XG4gICAgICBpZiAobWVkaWEpIHtcbiAgICAgICAgY29uc3QgYnVmZmVyZWQgPSBCdWZmZXJIZWxwZXIuZ2V0QnVmZmVyZWQobWVkaWEpO1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGJ1ZmZlcmVkLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgaWYgKHBvc2l0aW9uID49IGJ1ZmZlcmVkLnN0YXJ0KGkpICYmIHBvc2l0aW9uIDw9IGJ1ZmZlcmVkLmVuZChpKSkge1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIC8vIHRoaXMgaXMgdG8gY2F0Y2hcbiAgICAgIC8vIEludmFsaWRTdGF0ZUVycm9yOiBGYWlsZWQgdG8gcmVhZCB0aGUgJ2J1ZmZlcmVkJyBwcm9wZXJ0eSBmcm9tICdTb3VyY2VCdWZmZXInOlxuICAgICAgLy8gVGhpcyBTb3VyY2VCdWZmZXIgaGFzIGJlZW4gcmVtb3ZlZCBmcm9tIHRoZSBwYXJlbnQgbWVkaWEgc291cmNlXG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICBzdGF0aWMgYnVmZmVySW5mbyhtZWRpYSwgcG9zLCBtYXhIb2xlRHVyYXRpb24pIHtcbiAgICB0cnkge1xuICAgICAgaWYgKG1lZGlhKSB7XG4gICAgICAgIGNvbnN0IHZidWZmZXJlZCA9IEJ1ZmZlckhlbHBlci5nZXRCdWZmZXJlZChtZWRpYSk7XG4gICAgICAgIGNvbnN0IGJ1ZmZlcmVkID0gW107XG4gICAgICAgIGxldCBpO1xuICAgICAgICBmb3IgKGkgPSAwOyBpIDwgdmJ1ZmZlcmVkLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgYnVmZmVyZWQucHVzaCh7XG4gICAgICAgICAgICBzdGFydDogdmJ1ZmZlcmVkLnN0YXJ0KGkpLFxuICAgICAgICAgICAgZW5kOiB2YnVmZmVyZWQuZW5kKGkpXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXMuYnVmZmVyZWRJbmZvKGJ1ZmZlcmVkLCBwb3MsIG1heEhvbGVEdXJhdGlvbik7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIC8vIHRoaXMgaXMgdG8gY2F0Y2hcbiAgICAgIC8vIEludmFsaWRTdGF0ZUVycm9yOiBGYWlsZWQgdG8gcmVhZCB0aGUgJ2J1ZmZlcmVkJyBwcm9wZXJ0eSBmcm9tICdTb3VyY2VCdWZmZXInOlxuICAgICAgLy8gVGhpcyBTb3VyY2VCdWZmZXIgaGFzIGJlZW4gcmVtb3ZlZCBmcm9tIHRoZSBwYXJlbnQgbWVkaWEgc291cmNlXG4gICAgfVxuICAgIHJldHVybiB7XG4gICAgICBsZW46IDAsXG4gICAgICBzdGFydDogcG9zLFxuICAgICAgZW5kOiBwb3MsXG4gICAgICBuZXh0U3RhcnQ6IHVuZGVmaW5lZFxuICAgIH07XG4gIH1cbiAgc3RhdGljIGJ1ZmZlcmVkSW5mbyhidWZmZXJlZCwgcG9zLCBtYXhIb2xlRHVyYXRpb24pIHtcbiAgICBwb3MgPSBNYXRoLm1heCgwLCBwb3MpO1xuICAgIC8vIHNvcnQgb24gYnVmZmVyLnN0YXJ0L3NtYWxsZXIgZW5kIChJRSBkb2VzIG5vdCBhbHdheXMgcmV0dXJuIHNvcnRlZCBidWZmZXJlZCByYW5nZSlcbiAgICBidWZmZXJlZC5zb3J0KGZ1bmN0aW9uIChhLCBiKSB7XG4gICAgICBjb25zdCBkaWZmID0gYS5zdGFydCAtIGIuc3RhcnQ7XG4gICAgICBpZiAoZGlmZikge1xuICAgICAgICByZXR1cm4gZGlmZjtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiBiLmVuZCAtIGEuZW5kO1xuICAgICAgfVxuICAgIH0pO1xuICAgIGxldCBidWZmZXJlZDIgPSBbXTtcbiAgICBpZiAobWF4SG9sZUR1cmF0aW9uKSB7XG4gICAgICAvLyB0aGVyZSBtaWdodCBiZSBzb21lIHNtYWxsIGhvbGVzIGJldHdlZW4gYnVmZmVyIHRpbWUgcmFuZ2VcbiAgICAgIC8vIGNvbnNpZGVyIHRoYXQgaG9sZXMgc21hbGxlciB0aGFuIG1heEhvbGVEdXJhdGlvbiBhcmUgaXJyZWxldmFudCBhbmQgYnVpbGQgYW5vdGhlclxuICAgICAgLy8gYnVmZmVyIHRpbWUgcmFuZ2UgcmVwcmVzZW50YXRpb25zIHRoYXQgZGlzY2FyZHMgdGhvc2UgaG9sZXNcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYnVmZmVyZWQubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgY29uc3QgYnVmMmxlbiA9IGJ1ZmZlcmVkMi5sZW5ndGg7XG4gICAgICAgIGlmIChidWYybGVuKSB7XG4gICAgICAgICAgY29uc3QgYnVmMmVuZCA9IGJ1ZmZlcmVkMltidWYybGVuIC0gMV0uZW5kO1xuICAgICAgICAgIC8vIGlmIHNtYWxsIGhvbGUgKHZhbHVlIGJldHdlZW4gMCBvciBtYXhIb2xlRHVyYXRpb24gKSBvciBvdmVybGFwcGluZyAobmVnYXRpdmUpXG4gICAgICAgICAgaWYgKGJ1ZmZlcmVkW2ldLnN0YXJ0IC0gYnVmMmVuZCA8IG1heEhvbGVEdXJhdGlvbikge1xuICAgICAgICAgICAgLy8gbWVyZ2Ugb3ZlcmxhcHBpbmcgdGltZSByYW5nZXNcbiAgICAgICAgICAgIC8vIHVwZGF0ZSBsYXN0UmFuZ2UuZW5kIG9ubHkgaWYgc21hbGxlciB0aGFuIGl0ZW0uZW5kXG4gICAgICAgICAgICAvLyBlLmcuICBbIDEsIDE1XSB3aXRoICBbIDIsOF0gPT4gWyAxLDE1XSAobm8gbmVlZCB0byBtb2RpZnkgbGFzdFJhbmdlLmVuZClcbiAgICAgICAgICAgIC8vIHdoZXJlYXMgWyAxLCA4XSB3aXRoICBbIDIsMTVdID0+IFsgMSwxNV0gKCBsYXN0UmFuZ2Ugc2hvdWxkIHN3aXRjaCBmcm9tIFsxLDhdIHRvIFsxLDE1XSlcbiAgICAgICAgICAgIGlmIChidWZmZXJlZFtpXS5lbmQgPiBidWYyZW5kKSB7XG4gICAgICAgICAgICAgIGJ1ZmZlcmVkMltidWYybGVuIC0gMV0uZW5kID0gYnVmZmVyZWRbaV0uZW5kO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBiaWcgaG9sZVxuICAgICAgICAgICAgYnVmZmVyZWQyLnB1c2goYnVmZmVyZWRbaV0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBmaXJzdCB2YWx1ZVxuICAgICAgICAgIGJ1ZmZlcmVkMi5wdXNoKGJ1ZmZlcmVkW2ldKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBidWZmZXJlZDIgPSBidWZmZXJlZDtcbiAgICB9XG4gICAgbGV0IGJ1ZmZlckxlbiA9IDA7XG5cbiAgICAvLyBidWZmZXJTdGFydE5leHQgY2FuIHBvc3NpYmx5IGJlIHVuZGVmaW5lZCBiYXNlZCBvbiB0aGUgY29uZGl0aW9uYWwgbG9naWMgYmVsb3dcbiAgICBsZXQgYnVmZmVyU3RhcnROZXh0O1xuXG4gICAgLy8gYnVmZmVyU3RhcnQgYW5kIGJ1ZmZlckVuZCBhcmUgYnVmZmVyIGJvdW5kYXJpZXMgYXJvdW5kIGN1cnJlbnQgdmlkZW8gcG9zaXRpb25cbiAgICBsZXQgYnVmZmVyU3RhcnQgPSBwb3M7XG4gICAgbGV0IGJ1ZmZlckVuZCA9IHBvcztcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGJ1ZmZlcmVkMi5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3Qgc3RhcnQgPSBidWZmZXJlZDJbaV0uc3RhcnQ7XG4gICAgICBjb25zdCBlbmQgPSBidWZmZXJlZDJbaV0uZW5kO1xuICAgICAgLy8gbG9nZ2VyLmxvZygnYnVmIHN0YXJ0L2VuZDonICsgYnVmZmVyZWQuc3RhcnQoaSkgKyAnLycgKyBidWZmZXJlZC5lbmQoaSkpO1xuICAgICAgaWYgKHBvcyArIG1heEhvbGVEdXJhdGlvbiA+PSBzdGFydCAmJiBwb3MgPCBlbmQpIHtcbiAgICAgICAgLy8gcGxheSBwb3NpdGlvbiBpcyBpbnNpZGUgdGhpcyBidWZmZXIgVGltZVJhbmdlLCByZXRyaWV2ZSBlbmQgb2YgYnVmZmVyIHBvc2l0aW9uIGFuZCBidWZmZXIgbGVuZ3RoXG4gICAgICAgIGJ1ZmZlclN0YXJ0ID0gc3RhcnQ7XG4gICAgICAgIGJ1ZmZlckVuZCA9IGVuZDtcbiAgICAgICAgYnVmZmVyTGVuID0gYnVmZmVyRW5kIC0gcG9zO1xuICAgICAgfSBlbHNlIGlmIChwb3MgKyBtYXhIb2xlRHVyYXRpb24gPCBzdGFydCkge1xuICAgICAgICBidWZmZXJTdGFydE5leHQgPSBzdGFydDtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB7XG4gICAgICBsZW46IGJ1ZmZlckxlbixcbiAgICAgIHN0YXJ0OiBidWZmZXJTdGFydCB8fCAwLFxuICAgICAgZW5kOiBidWZmZXJFbmQgfHwgMCxcbiAgICAgIG5leHRTdGFydDogYnVmZmVyU3RhcnROZXh0XG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTYWZlIG1ldGhvZCB0byBnZXQgYnVmZmVyZWQgcHJvcGVydHkuXG4gICAqIFNvdXJjZUJ1ZmZlci5idWZmZXJlZCBtYXkgdGhyb3cgaWYgU291cmNlQnVmZmVyIGlzIHJlbW92ZWQgZnJvbSBpdCdzIE1lZGlhU291cmNlXG4gICAqL1xuICBzdGF0aWMgZ2V0QnVmZmVyZWQobWVkaWEpIHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIG1lZGlhLmJ1ZmZlcmVkO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGxvZ2dlci5sb2coJ2ZhaWxlZCB0byBnZXQgbWVkaWEuYnVmZmVyZWQnLCBlKTtcbiAgICAgIHJldHVybiBub29wQnVmZmVyZWQ7XG4gICAgfVxuICB9XG59XG5cbmNsYXNzIENodW5rTWV0YWRhdGEge1xuICBjb25zdHJ1Y3RvcihsZXZlbCwgc24sIGlkLCBzaXplID0gMCwgcGFydCA9IC0xLCBwYXJ0aWFsID0gZmFsc2UpIHtcbiAgICB0aGlzLmxldmVsID0gdm9pZCAwO1xuICAgIHRoaXMuc24gPSB2b2lkIDA7XG4gICAgdGhpcy5wYXJ0ID0gdm9pZCAwO1xuICAgIHRoaXMuaWQgPSB2b2lkIDA7XG4gICAgdGhpcy5zaXplID0gdm9pZCAwO1xuICAgIHRoaXMucGFydGlhbCA9IHZvaWQgMDtcbiAgICB0aGlzLnRyYW5zbXV4aW5nID0gZ2V0TmV3UGVyZm9ybWFuY2VUaW1pbmcoKTtcbiAgICB0aGlzLmJ1ZmZlcmluZyA9IHtcbiAgICAgIGF1ZGlvOiBnZXROZXdQZXJmb3JtYW5jZVRpbWluZygpLFxuICAgICAgdmlkZW86IGdldE5ld1BlcmZvcm1hbmNlVGltaW5nKCksXG4gICAgICBhdWRpb3ZpZGVvOiBnZXROZXdQZXJmb3JtYW5jZVRpbWluZygpXG4gICAgfTtcbiAgICB0aGlzLmxldmVsID0gbGV2ZWw7XG4gICAgdGhpcy5zbiA9IHNuO1xuICAgIHRoaXMuaWQgPSBpZDtcbiAgICB0aGlzLnNpemUgPSBzaXplO1xuICAgIHRoaXMucGFydCA9IHBhcnQ7XG4gICAgdGhpcy5wYXJ0aWFsID0gcGFydGlhbDtcbiAgfVxufVxuZnVuY3Rpb24gZ2V0TmV3UGVyZm9ybWFuY2VUaW1pbmcoKSB7XG4gIHJldHVybiB7XG4gICAgc3RhcnQ6IDAsXG4gICAgZXhlY3V0ZVN0YXJ0OiAwLFxuICAgIGV4ZWN1dGVFbmQ6IDAsXG4gICAgZW5kOiAwXG4gIH07XG59XG5cbmZ1bmN0aW9uIGZpbmRGaXJzdEZyYWdXaXRoQ0MoZnJhZ21lbnRzLCBjYykge1xuICBmb3IgKGxldCBpID0gMCwgbGVuID0gZnJhZ21lbnRzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG4gICAgdmFyIF9mcmFnbWVudHMkaTtcbiAgICBpZiAoKChfZnJhZ21lbnRzJGkgPSBmcmFnbWVudHNbaV0pID09IG51bGwgPyB2b2lkIDAgOiBfZnJhZ21lbnRzJGkuY2MpID09PSBjYykge1xuICAgICAgcmV0dXJuIGZyYWdtZW50c1tpXTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIG51bGw7XG59XG5mdW5jdGlvbiBzaG91bGRBbGlnbk9uRGlzY29udGludWl0aWVzKGxhc3RGcmFnLCBzd2l0Y2hEZXRhaWxzLCBkZXRhaWxzKSB7XG4gIGlmIChzd2l0Y2hEZXRhaWxzKSB7XG4gICAgaWYgKGRldGFpbHMuZW5kQ0MgPiBkZXRhaWxzLnN0YXJ0Q0MgfHwgbGFzdEZyYWcgJiYgbGFzdEZyYWcuY2MgPCBkZXRhaWxzLnN0YXJ0Q0MpIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgfVxuICByZXR1cm4gZmFsc2U7XG59XG5cbi8vIEZpbmQgdGhlIGZpcnN0IGZyYWcgaW4gdGhlIHByZXZpb3VzIGxldmVsIHdoaWNoIG1hdGNoZXMgdGhlIENDIG9mIHRoZSBmaXJzdCBmcmFnIG9mIHRoZSBuZXcgbGV2ZWxcbmZ1bmN0aW9uIGZpbmREaXNjb250aW51b3VzUmVmZXJlbmNlRnJhZyhwcmV2RGV0YWlscywgY3VyRGV0YWlscykge1xuICBjb25zdCBwcmV2RnJhZ3MgPSBwcmV2RGV0YWlscy5mcmFnbWVudHM7XG4gIGNvbnN0IGN1ckZyYWdzID0gY3VyRGV0YWlscy5mcmFnbWVudHM7XG4gIGlmICghY3VyRnJhZ3MubGVuZ3RoIHx8ICFwcmV2RnJhZ3MubGVuZ3RoKSB7XG4gICAgbG9nZ2VyLmxvZygnTm8gZnJhZ21lbnRzIHRvIGFsaWduJyk7XG4gICAgcmV0dXJuO1xuICB9XG4gIGNvbnN0IHByZXZTdGFydEZyYWcgPSBmaW5kRmlyc3RGcmFnV2l0aENDKHByZXZGcmFncywgY3VyRnJhZ3NbMF0uY2MpO1xuICBpZiAoIXByZXZTdGFydEZyYWcgfHwgcHJldlN0YXJ0RnJhZyAmJiAhcHJldlN0YXJ0RnJhZy5zdGFydFBUUykge1xuICAgIGxvZ2dlci5sb2coJ05vIGZyYWcgaW4gcHJldmlvdXMgbGV2ZWwgdG8gYWxpZ24gb24nKTtcbiAgICByZXR1cm47XG4gIH1cbiAgcmV0dXJuIHByZXZTdGFydEZyYWc7XG59XG5mdW5jdGlvbiBhZGp1c3RGcmFnbWVudFN0YXJ0KGZyYWcsIHNsaWRpbmcpIHtcbiAgaWYgKGZyYWcpIHtcbiAgICBjb25zdCBzdGFydCA9IGZyYWcuc3RhcnQgKyBzbGlkaW5nO1xuICAgIGZyYWcuc3RhcnQgPSBmcmFnLnN0YXJ0UFRTID0gc3RhcnQ7XG4gICAgZnJhZy5lbmRQVFMgPSBzdGFydCArIGZyYWcuZHVyYXRpb247XG4gIH1cbn1cbmZ1bmN0aW9uIGFkanVzdFNsaWRpbmdTdGFydChzbGlkaW5nLCBkZXRhaWxzKSB7XG4gIC8vIFVwZGF0ZSBzZWdtZW50c1xuICBjb25zdCBmcmFnbWVudHMgPSBkZXRhaWxzLmZyYWdtZW50cztcbiAgZm9yIChsZXQgaSA9IDAsIGxlbiA9IGZyYWdtZW50cy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuICAgIGFkanVzdEZyYWdtZW50U3RhcnQoZnJhZ21lbnRzW2ldLCBzbGlkaW5nKTtcbiAgfVxuICAvLyBVcGRhdGUgTEwtSExTIHBhcnRzIGF0IHRoZSBlbmQgb2YgdGhlIHBsYXlsaXN0XG4gIGlmIChkZXRhaWxzLmZyYWdtZW50SGludCkge1xuICAgIGFkanVzdEZyYWdtZW50U3RhcnQoZGV0YWlscy5mcmFnbWVudEhpbnQsIHNsaWRpbmcpO1xuICB9XG4gIGRldGFpbHMuYWxpZ25lZFNsaWRpbmcgPSB0cnVlO1xufVxuXG4vKipcbiAqIFVzaW5nIHRoZSBwYXJhbWV0ZXJzIG9mIHRoZSBsYXN0IGxldmVsLCB0aGlzIGZ1bmN0aW9uIGNvbXB1dGVzIFBUUycgb2YgdGhlIG5ldyBmcmFnbWVudHMgc28gdGhhdCB0aGV5IGZvcm0gYVxuICogY29udGlndW91cyBzdHJlYW0gd2l0aCB0aGUgbGFzdCBmcmFnbWVudHMuXG4gKiBUaGUgUFRTIG9mIGEgZnJhZ21lbnQgbGV0cyBIbHMuanMga25vdyB3aGVyZSBpdCBmaXRzIGludG8gYSBzdHJlYW0gLSBieSBrbm93aW5nIGV2ZXJ5IFBUUywgd2Uga25vdyB3aGljaCBmcmFnbWVudCB0b1xuICogZG93bmxvYWQgYXQgYW55IGdpdmVuIHRpbWUuIFBUUyBpcyBub3JtYWxseSBjb21wdXRlZCB3aGVuIHRoZSBmcmFnbWVudCBpcyBkZW11eGVkLCBzbyB0YWtpbmcgdGhpcyBzdGVwIHNhdmVzIHVzIHRpbWVcbiAqIGFuZCBhbiBleHRyYSBkb3dubG9hZC5cbiAqIEBwYXJhbSBsYXN0RnJhZ1xuICogQHBhcmFtIGxhc3RMZXZlbFxuICogQHBhcmFtIGRldGFpbHNcbiAqL1xuZnVuY3Rpb24gYWxpZ25TdHJlYW0obGFzdEZyYWcsIHN3aXRjaERldGFpbHMsIGRldGFpbHMpIHtcbiAgaWYgKCFzd2l0Y2hEZXRhaWxzKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGFsaWduRGlzY29udGludWl0aWVzKGxhc3RGcmFnLCBkZXRhaWxzLCBzd2l0Y2hEZXRhaWxzKTtcbiAgaWYgKCFkZXRhaWxzLmFsaWduZWRTbGlkaW5nICYmIHN3aXRjaERldGFpbHMpIHtcbiAgICAvLyBJZiB0aGUgUFRTIHdhc24ndCBmaWd1cmVkIG91dCB2aWEgZGlzY29udGludWl0eSBzZXF1ZW5jZSB0aGF0IG1lYW5zIHRoZXJlIHdhcyBubyBDQyBpbmNyZWFzZSB3aXRoaW4gdGhlIGxldmVsLlxuICAgIC8vIEFsaWduaW5nIHZpYSBQcm9ncmFtIERhdGUgVGltZSBzaG91bGQgdGhlcmVmb3JlIGJlIHJlbGlhYmxlLCBzaW5jZSBQRFQgc2hvdWxkIGJlIHRoZSBzYW1lIHdpdGhpbiB0aGUgc2FtZVxuICAgIC8vIGRpc2NvbnRpbnVpdHkgc2VxdWVuY2UuXG4gICAgYWxpZ25NZWRpYVBsYXlsaXN0QnlQRFQoZGV0YWlscywgc3dpdGNoRGV0YWlscyk7XG4gIH1cbiAgaWYgKCFkZXRhaWxzLmFsaWduZWRTbGlkaW5nICYmIHN3aXRjaERldGFpbHMgJiYgIWRldGFpbHMuc2tpcHBlZFNlZ21lbnRzKSB7XG4gICAgLy8gVHJ5IHRvIGFsaWduIG9uIHNuIHNvIHRoYXQgd2UgcGljayBhIGJldHRlciBzdGFydCBmcmFnbWVudC5cbiAgICAvLyBEbyBub3QgcGVyZm9ybSB0aGlzIG9uIHBsYXlsaXN0cyB3aXRoIGRlbHRhIHVwZGF0ZXMgYXMgdGhpcyBpcyBvbmx5IHRvIGFsaWduIGxldmVscyBvbiBzd2l0Y2hcbiAgICAvLyBhbmQgYWRqdXN0U2xpZGluZyBvbmx5IGFkanVzdHMgZnJhZ21lbnRzIGFmdGVyIHNraXBwZWRTZWdtZW50cy5cbiAgICBhZGp1c3RTbGlkaW5nKHN3aXRjaERldGFpbHMsIGRldGFpbHMpO1xuICB9XG59XG5cbi8qKlxuICogQ29tcHV0ZXMgdGhlIFBUUyBpZiBhIG5ldyBsZXZlbCdzIGZyYWdtZW50cyB1c2luZyB0aGUgUFRTIG9mIGEgZnJhZ21lbnQgaW4gdGhlIGxhc3QgbGV2ZWwgd2hpY2ggc2hhcmVzIHRoZSBzYW1lXG4gKiBkaXNjb250aW51aXR5IHNlcXVlbmNlLlxuICogQHBhcmFtIGxhc3RGcmFnIC0gVGhlIGxhc3QgRnJhZ21lbnQgd2hpY2ggc2hhcmVzIHRoZSBzYW1lIGRpc2NvbnRpbnVpdHkgc2VxdWVuY2VcbiAqIEBwYXJhbSBsYXN0TGV2ZWwgLSBUaGUgZGV0YWlscyBvZiB0aGUgbGFzdCBsb2FkZWQgbGV2ZWxcbiAqIEBwYXJhbSBkZXRhaWxzIC0gVGhlIGRldGFpbHMgb2YgdGhlIG5ldyBsZXZlbFxuICovXG5mdW5jdGlvbiBhbGlnbkRpc2NvbnRpbnVpdGllcyhsYXN0RnJhZywgZGV0YWlscywgc3dpdGNoRGV0YWlscykge1xuICBpZiAoc2hvdWxkQWxpZ25PbkRpc2NvbnRpbnVpdGllcyhsYXN0RnJhZywgc3dpdGNoRGV0YWlscywgZGV0YWlscykpIHtcbiAgICBjb25zdCByZWZlcmVuY2VGcmFnID0gZmluZERpc2NvbnRpbnVvdXNSZWZlcmVuY2VGcmFnKHN3aXRjaERldGFpbHMsIGRldGFpbHMpO1xuICAgIGlmIChyZWZlcmVuY2VGcmFnICYmIGlzRmluaXRlTnVtYmVyKHJlZmVyZW5jZUZyYWcuc3RhcnQpKSB7XG4gICAgICBsb2dnZXIubG9nKGBBZGp1c3RpbmcgUFRTIHVzaW5nIGxhc3QgbGV2ZWwgZHVlIHRvIENDIGluY3JlYXNlIHdpdGhpbiBjdXJyZW50IGxldmVsICR7ZGV0YWlscy51cmx9YCk7XG4gICAgICBhZGp1c3RTbGlkaW5nU3RhcnQocmVmZXJlbmNlRnJhZy5zdGFydCwgZGV0YWlscyk7XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogRW5zdXJlcyBhcHByb3ByaWF0ZSB0aW1lLWFsaWdubWVudCBiZXR3ZWVuIHJlbmRpdGlvbnMgYmFzZWQgb24gUERULlxuICogVGhpcyBmdW5jdGlvbiBhc3N1bWVzIHRoZSB0aW1lbGluZXMgcmVwcmVzZW50ZWQgaW4gYHJlZkRldGFpbHNgIGFyZSBhY2N1cmF0ZSwgaW5jbHVkaW5nIHRoZSBQRFRzXG4gKiBmb3IgdGhlIGxhc3QgZGlzY29udGludWl0eSBzZXF1ZW5jZSBudW1iZXIgc2hhcmVkIGJ5IGJvdGggcGxheWxpc3RzIHdoZW4gcHJlc2VudCxcbiAqIGFuZCB1c2VzIHRoZSBcIndhbGxjbG9ja1wiL1BEVCB0aW1lbGluZSBhcyBhIGNyb3NzLXJlZmVyZW5jZSB0byBgZGV0YWlsc2AsIGFkanVzdGluZyB0aGUgcHJlc2VudGF0aW9uXG4gKiB0aW1lcy90aW1lbGluZXMgb2YgYGRldGFpbHNgIGFjY29yZGluZ2x5LlxuICogR2l2ZW4gdGhlIGFzeW5jaHJvbm91cyBuYXR1cmUgb2YgZmV0Y2hlcyBhbmQgaW5pdGlhbCBsb2FkcyBvZiBsaXZlIGBtYWluYCBhbmQgYXVkaW8vc3VidGl0bGUgdHJhY2tzLFxuICogdGhlIHByaW1hcnkgcHVycG9zZSBvZiB0aGlzIGZ1bmN0aW9uIGlzIHRvIGVuc3VyZSB0aGUgXCJsb2NhbCB0aW1lbGluZXNcIiBvZiBhdWRpby9zdWJ0aXRsZSB0cmFja3NcbiAqIGFyZSBhbGlnbmVkIHRvIHRoZSBtYWluL3ZpZGVvIHRpbWVsaW5lLCB1c2luZyBQRFQgYXMgdGhlIGNyb3NzLXJlZmVyZW5jZS9cImFuY2hvclwiIHRoYXQgc2hvdWxkXG4gKiBiZSBjb25zaXN0ZW50IGFjcm9zcyBwbGF5bGlzdHMsIHBlciB0aGUgSExTIHNwZWMuXG4gKiBAcGFyYW0gZGV0YWlscyAtIFRoZSBkZXRhaWxzIG9mIHRoZSByZW5kaXRpb24geW91J2QgbGlrZSB0byB0aW1lLWFsaWduIChlLmcuIGFuIGF1ZGlvIHJlbmRpdGlvbikuXG4gKiBAcGFyYW0gcmVmRGV0YWlscyAtIFRoZSBkZXRhaWxzIG9mIHRoZSByZWZlcmVuY2UgcmVuZGl0aW9uIHdpdGggc3RhcnQgYW5kIFBEVCB0aW1lcyBmb3IgYWxpZ25tZW50LlxuICovXG5mdW5jdGlvbiBhbGlnbk1lZGlhUGxheWxpc3RCeVBEVChkZXRhaWxzLCByZWZEZXRhaWxzKSB7XG4gIGlmICghZGV0YWlscy5oYXNQcm9ncmFtRGF0ZVRpbWUgfHwgIXJlZkRldGFpbHMuaGFzUHJvZ3JhbURhdGVUaW1lKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGNvbnN0IGZyYWdtZW50cyA9IGRldGFpbHMuZnJhZ21lbnRzO1xuICBjb25zdCByZWZGcmFnbWVudHMgPSByZWZEZXRhaWxzLmZyYWdtZW50cztcbiAgaWYgKCFmcmFnbWVudHMubGVuZ3RoIHx8ICFyZWZGcmFnbWVudHMubGVuZ3RoKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLy8gQ2FsY3VsYXRlIGEgZGVsdGEgdG8gYXBwbHkgdG8gYWxsIGZyYWdtZW50cyBhY2NvcmRpbmcgdG8gdGhlIGRlbHRhIGluIFBEVCB0aW1lcyBhbmQgc3RhcnQgdGltZXNcbiAgLy8gb2YgYSBmcmFnbWVudCBpbiB0aGUgcmVmZXJlbmNlIGRldGFpbHMsIGFuZCBhIGZyYWdtZW50IGluIHRoZSB0YXJnZXQgZGV0YWlscyBvZiB0aGUgc2FtZSBkaXNjb250aW51aXR5LlxuICAvLyBJZiBhIGZyYWdtZW50IG9mIHRoZSBzYW1lIGRpc2NvbnRpbnVpdHkgd2FzIG5vdCBmb3VuZCB1c2UgdGhlIG1pZGRsZSBmcmFnbWVudCBvZiBib3RoLlxuICBsZXQgcmVmRnJhZztcbiAgbGV0IGZyYWc7XG4gIGNvbnN0IHRhcmdldENDID0gTWF0aC5taW4ocmVmRGV0YWlscy5lbmRDQywgZGV0YWlscy5lbmRDQyk7XG4gIGlmIChyZWZEZXRhaWxzLnN0YXJ0Q0MgPCB0YXJnZXRDQyAmJiBkZXRhaWxzLnN0YXJ0Q0MgPCB0YXJnZXRDQykge1xuICAgIHJlZkZyYWcgPSBmaW5kRmlyc3RGcmFnV2l0aENDKHJlZkZyYWdtZW50cywgdGFyZ2V0Q0MpO1xuICAgIGZyYWcgPSBmaW5kRmlyc3RGcmFnV2l0aENDKGZyYWdtZW50cywgdGFyZ2V0Q0MpO1xuICB9XG4gIGlmICghcmVmRnJhZyB8fCAhZnJhZykge1xuICAgIHJlZkZyYWcgPSByZWZGcmFnbWVudHNbTWF0aC5mbG9vcihyZWZGcmFnbWVudHMubGVuZ3RoIC8gMildO1xuICAgIGZyYWcgPSBmaW5kRmlyc3RGcmFnV2l0aENDKGZyYWdtZW50cywgcmVmRnJhZy5jYykgfHwgZnJhZ21lbnRzW01hdGguZmxvb3IoZnJhZ21lbnRzLmxlbmd0aCAvIDIpXTtcbiAgfVxuICBjb25zdCByZWZQRFQgPSByZWZGcmFnLnByb2dyYW1EYXRlVGltZTtcbiAgY29uc3QgdGFyZ2V0UERUID0gZnJhZy5wcm9ncmFtRGF0ZVRpbWU7XG4gIGlmICghcmVmUERUIHx8ICF0YXJnZXRQRFQpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgY29uc3QgZGVsdGEgPSAodGFyZ2V0UERUIC0gcmVmUERUKSAvIDEwMDAgLSAoZnJhZy5zdGFydCAtIHJlZkZyYWcuc3RhcnQpO1xuICBhZGp1c3RTbGlkaW5nU3RhcnQoZGVsdGEsIGRldGFpbHMpO1xufVxuXG5jb25zdCBNSU5fQ0hVTktfU0laRSA9IE1hdGgucG93KDIsIDE3KTsgLy8gMTI4a2JcblxuY2xhc3MgRnJhZ21lbnRMb2FkZXIge1xuICBjb25zdHJ1Y3Rvcihjb25maWcpIHtcbiAgICB0aGlzLmNvbmZpZyA9IHZvaWQgMDtcbiAgICB0aGlzLmxvYWRlciA9IG51bGw7XG4gICAgdGhpcy5wYXJ0TG9hZFRpbWVvdXQgPSAtMTtcbiAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZztcbiAgfVxuICBkZXN0cm95KCkge1xuICAgIGlmICh0aGlzLmxvYWRlcikge1xuICAgICAgdGhpcy5sb2FkZXIuZGVzdHJveSgpO1xuICAgICAgdGhpcy5sb2FkZXIgPSBudWxsO1xuICAgIH1cbiAgfVxuICBhYm9ydCgpIHtcbiAgICBpZiAodGhpcy5sb2FkZXIpIHtcbiAgICAgIC8vIEFib3J0IHRoZSBsb2FkZXIgZm9yIGN1cnJlbnQgZnJhZ21lbnQuIE9ubHkgb25lIG1heSBsb2FkIGF0IGFueSBnaXZlbiB0aW1lXG4gICAgICB0aGlzLmxvYWRlci5hYm9ydCgpO1xuICAgIH1cbiAgfVxuICBsb2FkKGZyYWcsIG9uUHJvZ3Jlc3MpIHtcbiAgICBjb25zdCB1cmwgPSBmcmFnLnVybDtcbiAgICBpZiAoIXVybCkge1xuICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBMb2FkRXJyb3Ioe1xuICAgICAgICB0eXBlOiBFcnJvclR5cGVzLk5FVFdPUktfRVJST1IsXG4gICAgICAgIGRldGFpbHM6IEVycm9yRGV0YWlscy5GUkFHX0xPQURfRVJST1IsXG4gICAgICAgIGZhdGFsOiBmYWxzZSxcbiAgICAgICAgZnJhZyxcbiAgICAgICAgZXJyb3I6IG5ldyBFcnJvcihgRnJhZ21lbnQgZG9lcyBub3QgaGF2ZSBhICR7dXJsID8gJ3BhcnQgbGlzdCcgOiAndXJsJ31gKSxcbiAgICAgICAgbmV0d29ya0RldGFpbHM6IG51bGxcbiAgICAgIH0pKTtcbiAgICB9XG4gICAgdGhpcy5hYm9ydCgpO1xuICAgIGNvbnN0IGNvbmZpZyA9IHRoaXMuY29uZmlnO1xuICAgIGNvbnN0IEZyYWdtZW50SUxvYWRlciA9IGNvbmZpZy5mTG9hZGVyO1xuICAgIGNvbnN0IERlZmF1bHRJTG9hZGVyID0gY29uZmlnLmxvYWRlcjtcbiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgaWYgKHRoaXMubG9hZGVyKSB7XG4gICAgICAgIHRoaXMubG9hZGVyLmRlc3Ryb3koKTtcbiAgICAgIH1cbiAgICAgIGlmIChmcmFnLmdhcCkge1xuICAgICAgICBpZiAoZnJhZy50YWdMaXN0LnNvbWUodGFncyA9PiB0YWdzWzBdID09PSAnR0FQJykpIHtcbiAgICAgICAgICByZWplY3QoY3JlYXRlR2FwTG9hZEVycm9yKGZyYWcpKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gUmVzZXQgdGVtcG9yYXJ5IHRyZWF0bWVudCBhcyBHQVAgdGFnXG4gICAgICAgICAgZnJhZy5nYXAgPSBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgY29uc3QgbG9hZGVyID0gdGhpcy5sb2FkZXIgPSBmcmFnLmxvYWRlciA9IEZyYWdtZW50SUxvYWRlciA/IG5ldyBGcmFnbWVudElMb2FkZXIoY29uZmlnKSA6IG5ldyBEZWZhdWx0SUxvYWRlcihjb25maWcpO1xuICAgICAgY29uc3QgbG9hZGVyQ29udGV4dCA9IGNyZWF0ZUxvYWRlckNvbnRleHQoZnJhZyk7XG4gICAgICBjb25zdCBsb2FkUG9saWN5ID0gZ2V0TG9hZGVyQ29uZmlnV2l0aG91dFJldGllcyhjb25maWcuZnJhZ0xvYWRQb2xpY3kuZGVmYXVsdCk7XG4gICAgICBjb25zdCBsb2FkZXJDb25maWcgPSB7XG4gICAgICAgIGxvYWRQb2xpY3ksXG4gICAgICAgIHRpbWVvdXQ6IGxvYWRQb2xpY3kubWF4TG9hZFRpbWVNcyxcbiAgICAgICAgbWF4UmV0cnk6IDAsXG4gICAgICAgIHJldHJ5RGVsYXk6IDAsXG4gICAgICAgIG1heFJldHJ5RGVsYXk6IDAsXG4gICAgICAgIGhpZ2hXYXRlck1hcms6IGZyYWcuc24gPT09ICdpbml0U2VnbWVudCcgPyBJbmZpbml0eSA6IE1JTl9DSFVOS19TSVpFXG4gICAgICB9O1xuICAgICAgLy8gQXNzaWduIGZyYWcgc3RhdHMgdG8gdGhlIGxvYWRlcidzIHN0YXRzIHJlZmVyZW5jZVxuICAgICAgZnJhZy5zdGF0cyA9IGxvYWRlci5zdGF0cztcbiAgICAgIGxvYWRlci5sb2FkKGxvYWRlckNvbnRleHQsIGxvYWRlckNvbmZpZywge1xuICAgICAgICBvblN1Y2Nlc3M6IChyZXNwb25zZSwgc3RhdHMsIGNvbnRleHQsIG5ldHdvcmtEZXRhaWxzKSA9PiB7XG4gICAgICAgICAgdGhpcy5yZXNldExvYWRlcihmcmFnLCBsb2FkZXIpO1xuICAgICAgICAgIGxldCBwYXlsb2FkID0gcmVzcG9uc2UuZGF0YTtcbiAgICAgICAgICBpZiAoY29udGV4dC5yZXNldElWICYmIGZyYWcuZGVjcnlwdGRhdGEpIHtcbiAgICAgICAgICAgIGZyYWcuZGVjcnlwdGRhdGEuaXYgPSBuZXcgVWludDhBcnJheShwYXlsb2FkLnNsaWNlKDAsIDE2KSk7XG4gICAgICAgICAgICBwYXlsb2FkID0gcGF5bG9hZC5zbGljZSgxNik7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgICAgZnJhZyxcbiAgICAgICAgICAgIHBhcnQ6IG51bGwsXG4gICAgICAgICAgICBwYXlsb2FkLFxuICAgICAgICAgICAgbmV0d29ya0RldGFpbHNcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSxcbiAgICAgICAgb25FcnJvcjogKHJlc3BvbnNlLCBjb250ZXh0LCBuZXR3b3JrRGV0YWlscywgc3RhdHMpID0+IHtcbiAgICAgICAgICB0aGlzLnJlc2V0TG9hZGVyKGZyYWcsIGxvYWRlcik7XG4gICAgICAgICAgcmVqZWN0KG5ldyBMb2FkRXJyb3Ioe1xuICAgICAgICAgICAgdHlwZTogRXJyb3JUeXBlcy5ORVRXT1JLX0VSUk9SLFxuICAgICAgICAgICAgZGV0YWlsczogRXJyb3JEZXRhaWxzLkZSQUdfTE9BRF9FUlJPUixcbiAgICAgICAgICAgIGZhdGFsOiBmYWxzZSxcbiAgICAgICAgICAgIGZyYWcsXG4gICAgICAgICAgICByZXNwb25zZTogX29iamVjdFNwcmVhZDIoe1xuICAgICAgICAgICAgICB1cmwsXG4gICAgICAgICAgICAgIGRhdGE6IHVuZGVmaW5lZFxuICAgICAgICAgICAgfSwgcmVzcG9uc2UpLFxuICAgICAgICAgICAgZXJyb3I6IG5ldyBFcnJvcihgSFRUUCBFcnJvciAke3Jlc3BvbnNlLmNvZGV9ICR7cmVzcG9uc2UudGV4dH1gKSxcbiAgICAgICAgICAgIG5ldHdvcmtEZXRhaWxzLFxuICAgICAgICAgICAgc3RhdHNcbiAgICAgICAgICB9KSk7XG4gICAgICAgIH0sXG4gICAgICAgIG9uQWJvcnQ6IChzdGF0cywgY29udGV4dCwgbmV0d29ya0RldGFpbHMpID0+IHtcbiAgICAgICAgICB0aGlzLnJlc2V0TG9hZGVyKGZyYWcsIGxvYWRlcik7XG4gICAgICAgICAgcmVqZWN0KG5ldyBMb2FkRXJyb3Ioe1xuICAgICAgICAgICAgdHlwZTogRXJyb3JUeXBlcy5ORVRXT1JLX0VSUk9SLFxuICAgICAgICAgICAgZGV0YWlsczogRXJyb3JEZXRhaWxzLklOVEVSTkFMX0FCT1JURUQsXG4gICAgICAgICAgICBmYXRhbDogZmFsc2UsXG4gICAgICAgICAgICBmcmFnLFxuICAgICAgICAgICAgZXJyb3I6IG5ldyBFcnJvcignQWJvcnRlZCcpLFxuICAgICAgICAgICAgbmV0d29ya0RldGFpbHMsXG4gICAgICAgICAgICBzdGF0c1xuICAgICAgICAgIH0pKTtcbiAgICAgICAgfSxcbiAgICAgICAgb25UaW1lb3V0OiAoc3RhdHMsIGNvbnRleHQsIG5ldHdvcmtEZXRhaWxzKSA9PiB7XG4gICAgICAgICAgdGhpcy5yZXNldExvYWRlcihmcmFnLCBsb2FkZXIpO1xuICAgICAgICAgIHJlamVjdChuZXcgTG9hZEVycm9yKHtcbiAgICAgICAgICAgIHR5cGU6IEVycm9yVHlwZXMuTkVUV09SS19FUlJPUixcbiAgICAgICAgICAgIGRldGFpbHM6IEVycm9yRGV0YWlscy5GUkFHX0xPQURfVElNRU9VVCxcbiAgICAgICAgICAgIGZhdGFsOiBmYWxzZSxcbiAgICAgICAgICAgIGZyYWcsXG4gICAgICAgICAgICBlcnJvcjogbmV3IEVycm9yKGBUaW1lb3V0IGFmdGVyICR7bG9hZGVyQ29uZmlnLnRpbWVvdXR9bXNgKSxcbiAgICAgICAgICAgIG5ldHdvcmtEZXRhaWxzLFxuICAgICAgICAgICAgc3RhdHNcbiAgICAgICAgICB9KSk7XG4gICAgICAgIH0sXG4gICAgICAgIG9uUHJvZ3Jlc3M6IChzdGF0cywgY29udGV4dCwgZGF0YSwgbmV0d29ya0RldGFpbHMpID0+IHtcbiAgICAgICAgICBpZiAob25Qcm9ncmVzcykge1xuICAgICAgICAgICAgb25Qcm9ncmVzcyh7XG4gICAgICAgICAgICAgIGZyYWcsXG4gICAgICAgICAgICAgIHBhcnQ6IG51bGwsXG4gICAgICAgICAgICAgIHBheWxvYWQ6IGRhdGEsXG4gICAgICAgICAgICAgIG5ldHdvcmtEZXRhaWxzXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG4gIGxvYWRQYXJ0KGZyYWcsIHBhcnQsIG9uUHJvZ3Jlc3MpIHtcbiAgICB0aGlzLmFib3J0KCk7XG4gICAgY29uc3QgY29uZmlnID0gdGhpcy5jb25maWc7XG4gICAgY29uc3QgRnJhZ21lbnRJTG9hZGVyID0gY29uZmlnLmZMb2FkZXI7XG4gICAgY29uc3QgRGVmYXVsdElMb2FkZXIgPSBjb25maWcubG9hZGVyO1xuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBpZiAodGhpcy5sb2FkZXIpIHtcbiAgICAgICAgdGhpcy5sb2FkZXIuZGVzdHJveSgpO1xuICAgICAgfVxuICAgICAgaWYgKGZyYWcuZ2FwIHx8IHBhcnQuZ2FwKSB7XG4gICAgICAgIHJlamVjdChjcmVhdGVHYXBMb2FkRXJyb3IoZnJhZywgcGFydCkpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjb25zdCBsb2FkZXIgPSB0aGlzLmxvYWRlciA9IGZyYWcubG9hZGVyID0gRnJhZ21lbnRJTG9hZGVyID8gbmV3IEZyYWdtZW50SUxvYWRlcihjb25maWcpIDogbmV3IERlZmF1bHRJTG9hZGVyKGNvbmZpZyk7XG4gICAgICBjb25zdCBsb2FkZXJDb250ZXh0ID0gY3JlYXRlTG9hZGVyQ29udGV4dChmcmFnLCBwYXJ0KTtcbiAgICAgIC8vIFNob3VsZCB3ZSBkZWZpbmUgYW5vdGhlciBsb2FkIHBvbGljeSBmb3IgcGFydHM/XG4gICAgICBjb25zdCBsb2FkUG9saWN5ID0gZ2V0TG9hZGVyQ29uZmlnV2l0aG91dFJldGllcyhjb25maWcuZnJhZ0xvYWRQb2xpY3kuZGVmYXVsdCk7XG4gICAgICBjb25zdCBsb2FkZXJDb25maWcgPSB7XG4gICAgICAgIGxvYWRQb2xpY3ksXG4gICAgICAgIHRpbWVvdXQ6IGxvYWRQb2xpY3kubWF4TG9hZFRpbWVNcyxcbiAgICAgICAgbWF4UmV0cnk6IDAsXG4gICAgICAgIHJldHJ5RGVsYXk6IDAsXG4gICAgICAgIG1heFJldHJ5RGVsYXk6IDAsXG4gICAgICAgIGhpZ2hXYXRlck1hcms6IE1JTl9DSFVOS19TSVpFXG4gICAgICB9O1xuICAgICAgLy8gQXNzaWduIHBhcnQgc3RhdHMgdG8gdGhlIGxvYWRlcidzIHN0YXRzIHJlZmVyZW5jZVxuICAgICAgcGFydC5zdGF0cyA9IGxvYWRlci5zdGF0cztcbiAgICAgIGxvYWRlci5sb2FkKGxvYWRlckNvbnRleHQsIGxvYWRlckNvbmZpZywge1xuICAgICAgICBvblN1Y2Nlc3M6IChyZXNwb25zZSwgc3RhdHMsIGNvbnRleHQsIG5ldHdvcmtEZXRhaWxzKSA9PiB7XG4gICAgICAgICAgdGhpcy5yZXNldExvYWRlcihmcmFnLCBsb2FkZXIpO1xuICAgICAgICAgIHRoaXMudXBkYXRlU3RhdHNGcm9tUGFydChmcmFnLCBwYXJ0KTtcbiAgICAgICAgICBjb25zdCBwYXJ0TG9hZGVkRGF0YSA9IHtcbiAgICAgICAgICAgIGZyYWcsXG4gICAgICAgICAgICBwYXJ0LFxuICAgICAgICAgICAgcGF5bG9hZDogcmVzcG9uc2UuZGF0YSxcbiAgICAgICAgICAgIG5ldHdvcmtEZXRhaWxzXG4gICAgICAgICAgfTtcbiAgICAgICAgICBvblByb2dyZXNzKHBhcnRMb2FkZWREYXRhKTtcbiAgICAgICAgICByZXNvbHZlKHBhcnRMb2FkZWREYXRhKTtcbiAgICAgICAgfSxcbiAgICAgICAgb25FcnJvcjogKHJlc3BvbnNlLCBjb250ZXh0LCBuZXR3b3JrRGV0YWlscywgc3RhdHMpID0+IHtcbiAgICAgICAgICB0aGlzLnJlc2V0TG9hZGVyKGZyYWcsIGxvYWRlcik7XG4gICAgICAgICAgcmVqZWN0KG5ldyBMb2FkRXJyb3Ioe1xuICAgICAgICAgICAgdHlwZTogRXJyb3JUeXBlcy5ORVRXT1JLX0VSUk9SLFxuICAgICAgICAgICAgZGV0YWlsczogRXJyb3JEZXRhaWxzLkZSQUdfTE9BRF9FUlJPUixcbiAgICAgICAgICAgIGZhdGFsOiBmYWxzZSxcbiAgICAgICAgICAgIGZyYWcsXG4gICAgICAgICAgICBwYXJ0LFxuICAgICAgICAgICAgcmVzcG9uc2U6IF9vYmplY3RTcHJlYWQyKHtcbiAgICAgICAgICAgICAgdXJsOiBsb2FkZXJDb250ZXh0LnVybCxcbiAgICAgICAgICAgICAgZGF0YTogdW5kZWZpbmVkXG4gICAgICAgICAgICB9LCByZXNwb25zZSksXG4gICAgICAgICAgICBlcnJvcjogbmV3IEVycm9yKGBIVFRQIEVycm9yICR7cmVzcG9uc2UuY29kZX0gJHtyZXNwb25zZS50ZXh0fWApLFxuICAgICAgICAgICAgbmV0d29ya0RldGFpbHMsXG4gICAgICAgICAgICBzdGF0c1xuICAgICAgICAgIH0pKTtcbiAgICAgICAgfSxcbiAgICAgICAgb25BYm9ydDogKHN0YXRzLCBjb250ZXh0LCBuZXR3b3JrRGV0YWlscykgPT4ge1xuICAgICAgICAgIGZyYWcuc3RhdHMuYWJvcnRlZCA9IHBhcnQuc3RhdHMuYWJvcnRlZDtcbiAgICAgICAgICB0aGlzLnJlc2V0TG9hZGVyKGZyYWcsIGxvYWRlcik7XG4gICAgICAgICAgcmVqZWN0KG5ldyBMb2FkRXJyb3Ioe1xuICAgICAgICAgICAgdHlwZTogRXJyb3JUeXBlcy5ORVRXT1JLX0VSUk9SLFxuICAgICAgICAgICAgZGV0YWlsczogRXJyb3JEZXRhaWxzLklOVEVSTkFMX0FCT1JURUQsXG4gICAgICAgICAgICBmYXRhbDogZmFsc2UsXG4gICAgICAgICAgICBmcmFnLFxuICAgICAgICAgICAgcGFydCxcbiAgICAgICAgICAgIGVycm9yOiBuZXcgRXJyb3IoJ0Fib3J0ZWQnKSxcbiAgICAgICAgICAgIG5ldHdvcmtEZXRhaWxzLFxuICAgICAgICAgICAgc3RhdHNcbiAgICAgICAgICB9KSk7XG4gICAgICAgIH0sXG4gICAgICAgIG9uVGltZW91dDogKHN0YXRzLCBjb250ZXh0LCBuZXR3b3JrRGV0YWlscykgPT4ge1xuICAgICAgICAgIHRoaXMucmVzZXRMb2FkZXIoZnJhZywgbG9hZGVyKTtcbiAgICAgICAgICByZWplY3QobmV3IExvYWRFcnJvcih7XG4gICAgICAgICAgICB0eXBlOiBFcnJvclR5cGVzLk5FVFdPUktfRVJST1IsXG4gICAgICAgICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuRlJBR19MT0FEX1RJTUVPVVQsXG4gICAgICAgICAgICBmYXRhbDogZmFsc2UsXG4gICAgICAgICAgICBmcmFnLFxuICAgICAgICAgICAgcGFydCxcbiAgICAgICAgICAgIGVycm9yOiBuZXcgRXJyb3IoYFRpbWVvdXQgYWZ0ZXIgJHtsb2FkZXJDb25maWcudGltZW91dH1tc2ApLFxuICAgICAgICAgICAgbmV0d29ya0RldGFpbHMsXG4gICAgICAgICAgICBzdGF0c1xuICAgICAgICAgIH0pKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfSk7XG4gIH1cbiAgdXBkYXRlU3RhdHNGcm9tUGFydChmcmFnLCBwYXJ0KSB7XG4gICAgY29uc3QgZnJhZ1N0YXRzID0gZnJhZy5zdGF0cztcbiAgICBjb25zdCBwYXJ0U3RhdHMgPSBwYXJ0LnN0YXRzO1xuICAgIGNvbnN0IHBhcnRUb3RhbCA9IHBhcnRTdGF0cy50b3RhbDtcbiAgICBmcmFnU3RhdHMubG9hZGVkICs9IHBhcnRTdGF0cy5sb2FkZWQ7XG4gICAgaWYgKHBhcnRUb3RhbCkge1xuICAgICAgY29uc3QgZXN0VG90YWxQYXJ0cyA9IE1hdGgucm91bmQoZnJhZy5kdXJhdGlvbiAvIHBhcnQuZHVyYXRpb24pO1xuICAgICAgY29uc3QgZXN0TG9hZGVkUGFydHMgPSBNYXRoLm1pbihNYXRoLnJvdW5kKGZyYWdTdGF0cy5sb2FkZWQgLyBwYXJ0VG90YWwpLCBlc3RUb3RhbFBhcnRzKTtcbiAgICAgIGNvbnN0IGVzdFJlbWFpbmluZ1BhcnRzID0gZXN0VG90YWxQYXJ0cyAtIGVzdExvYWRlZFBhcnRzO1xuICAgICAgY29uc3QgZXN0UmVtYWluaW5nQnl0ZXMgPSBlc3RSZW1haW5pbmdQYXJ0cyAqIE1hdGgucm91bmQoZnJhZ1N0YXRzLmxvYWRlZCAvIGVzdExvYWRlZFBhcnRzKTtcbiAgICAgIGZyYWdTdGF0cy50b3RhbCA9IGZyYWdTdGF0cy5sb2FkZWQgKyBlc3RSZW1haW5pbmdCeXRlcztcbiAgICB9IGVsc2Uge1xuICAgICAgZnJhZ1N0YXRzLnRvdGFsID0gTWF0aC5tYXgoZnJhZ1N0YXRzLmxvYWRlZCwgZnJhZ1N0YXRzLnRvdGFsKTtcbiAgICB9XG4gICAgY29uc3QgZnJhZ0xvYWRpbmcgPSBmcmFnU3RhdHMubG9hZGluZztcbiAgICBjb25zdCBwYXJ0TG9hZGluZyA9IHBhcnRTdGF0cy5sb2FkaW5nO1xuICAgIGlmIChmcmFnTG9hZGluZy5zdGFydCkge1xuICAgICAgLy8gYWRkIHRvIGZyYWdtZW50IGxvYWRlciBsYXRlbmN5XG4gICAgICBmcmFnTG9hZGluZy5maXJzdCArPSBwYXJ0TG9hZGluZy5maXJzdCAtIHBhcnRMb2FkaW5nLnN0YXJ0O1xuICAgIH0gZWxzZSB7XG4gICAgICBmcmFnTG9hZGluZy5zdGFydCA9IHBhcnRMb2FkaW5nLnN0YXJ0O1xuICAgICAgZnJhZ0xvYWRpbmcuZmlyc3QgPSBwYXJ0TG9hZGluZy5maXJzdDtcbiAgICB9XG4gICAgZnJhZ0xvYWRpbmcuZW5kID0gcGFydExvYWRpbmcuZW5kO1xuICB9XG4gIHJlc2V0TG9hZGVyKGZyYWcsIGxvYWRlcikge1xuICAgIGZyYWcubG9hZGVyID0gbnVsbDtcbiAgICBpZiAodGhpcy5sb2FkZXIgPT09IGxvYWRlcikge1xuICAgICAgc2VsZi5jbGVhclRpbWVvdXQodGhpcy5wYXJ0TG9hZFRpbWVvdXQpO1xuICAgICAgdGhpcy5sb2FkZXIgPSBudWxsO1xuICAgIH1cbiAgICBsb2FkZXIuZGVzdHJveSgpO1xuICB9XG59XG5mdW5jdGlvbiBjcmVhdGVMb2FkZXJDb250ZXh0KGZyYWcsIHBhcnQgPSBudWxsKSB7XG4gIGNvbnN0IHNlZ21lbnQgPSBwYXJ0IHx8IGZyYWc7XG4gIGNvbnN0IGxvYWRlckNvbnRleHQgPSB7XG4gICAgZnJhZyxcbiAgICBwYXJ0LFxuICAgIHJlc3BvbnNlVHlwZTogJ2FycmF5YnVmZmVyJyxcbiAgICB1cmw6IHNlZ21lbnQudXJsLFxuICAgIGhlYWRlcnM6IHt9LFxuICAgIHJhbmdlU3RhcnQ6IDAsXG4gICAgcmFuZ2VFbmQ6IDBcbiAgfTtcbiAgY29uc3Qgc3RhcnQgPSBzZWdtZW50LmJ5dGVSYW5nZVN0YXJ0T2Zmc2V0O1xuICBjb25zdCBlbmQgPSBzZWdtZW50LmJ5dGVSYW5nZUVuZE9mZnNldDtcbiAgaWYgKGlzRmluaXRlTnVtYmVyKHN0YXJ0KSAmJiBpc0Zpbml0ZU51bWJlcihlbmQpKSB7XG4gICAgdmFyIF9mcmFnJGRlY3J5cHRkYXRhO1xuICAgIGxldCBieXRlUmFuZ2VTdGFydCA9IHN0YXJ0O1xuICAgIGxldCBieXRlUmFuZ2VFbmQgPSBlbmQ7XG4gICAgaWYgKGZyYWcuc24gPT09ICdpbml0U2VnbWVudCcgJiYgKChfZnJhZyRkZWNyeXB0ZGF0YSA9IGZyYWcuZGVjcnlwdGRhdGEpID09IG51bGwgPyB2b2lkIDAgOiBfZnJhZyRkZWNyeXB0ZGF0YS5tZXRob2QpID09PSAnQUVTLTEyOCcpIHtcbiAgICAgIC8vIE1BUCBzZWdtZW50IGVuY3J5cHRlZCB3aXRoIG1ldGhvZCAnQUVTLTEyOCcsIHdoZW4gc2VydmVkIHdpdGggSFRUUCBSYW5nZSxcbiAgICAgIC8vIGhhcyB0aGUgdW5lbmNyeXB0ZWQgc2l6ZSBzcGVjaWZpZWQgaW4gdGhlIHJhbmdlLlxuICAgICAgLy8gUmVmOiBodHRwczovL3Rvb2xzLmlldGYub3JnL2h0bWwvZHJhZnQtcGFudG9zLWhscy1yZmM4MjE2YmlzLTA4I3NlY3Rpb24tNi4zLjZcbiAgICAgIGNvbnN0IGZyYWdtZW50TGVuID0gZW5kIC0gc3RhcnQ7XG4gICAgICBpZiAoZnJhZ21lbnRMZW4gJSAxNikge1xuICAgICAgICBieXRlUmFuZ2VFbmQgPSBlbmQgKyAoMTYgLSBmcmFnbWVudExlbiAlIDE2KTtcbiAgICAgIH1cbiAgICAgIGlmIChzdGFydCAhPT0gMCkge1xuICAgICAgICBsb2FkZXJDb250ZXh0LnJlc2V0SVYgPSB0cnVlO1xuICAgICAgICBieXRlUmFuZ2VTdGFydCA9IHN0YXJ0IC0gMTY7XG4gICAgICB9XG4gICAgfVxuICAgIGxvYWRlckNvbnRleHQucmFuZ2VTdGFydCA9IGJ5dGVSYW5nZVN0YXJ0O1xuICAgIGxvYWRlckNvbnRleHQucmFuZ2VFbmQgPSBieXRlUmFuZ2VFbmQ7XG4gIH1cbiAgcmV0dXJuIGxvYWRlckNvbnRleHQ7XG59XG5mdW5jdGlvbiBjcmVhdGVHYXBMb2FkRXJyb3IoZnJhZywgcGFydCkge1xuICBjb25zdCBlcnJvciA9IG5ldyBFcnJvcihgR0FQICR7ZnJhZy5nYXAgPyAndGFnJyA6ICdhdHRyaWJ1dGUnfSBmb3VuZGApO1xuICBjb25zdCBlcnJvckRhdGEgPSB7XG4gICAgdHlwZTogRXJyb3JUeXBlcy5NRURJQV9FUlJPUixcbiAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuRlJBR19HQVAsXG4gICAgZmF0YWw6IGZhbHNlLFxuICAgIGZyYWcsXG4gICAgZXJyb3IsXG4gICAgbmV0d29ya0RldGFpbHM6IG51bGxcbiAgfTtcbiAgaWYgKHBhcnQpIHtcbiAgICBlcnJvckRhdGEucGFydCA9IHBhcnQ7XG4gIH1cbiAgKHBhcnQgPyBwYXJ0IDogZnJhZykuc3RhdHMuYWJvcnRlZCA9IHRydWU7XG4gIHJldHVybiBuZXcgTG9hZEVycm9yKGVycm9yRGF0YSk7XG59XG5jbGFzcyBMb2FkRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKGRhdGEpIHtcbiAgICBzdXBlcihkYXRhLmVycm9yLm1lc3NhZ2UpO1xuICAgIHRoaXMuZGF0YSA9IHZvaWQgMDtcbiAgICB0aGlzLmRhdGEgPSBkYXRhO1xuICB9XG59XG5cbmNsYXNzIEFFU0NyeXB0byB7XG4gIGNvbnN0cnVjdG9yKHN1YnRsZSwgaXYpIHtcbiAgICB0aGlzLnN1YnRsZSA9IHZvaWQgMDtcbiAgICB0aGlzLmFlc0lWID0gdm9pZCAwO1xuICAgIHRoaXMuc3VidGxlID0gc3VidGxlO1xuICAgIHRoaXMuYWVzSVYgPSBpdjtcbiAgfVxuICBkZWNyeXB0KGRhdGEsIGtleSkge1xuICAgIHJldHVybiB0aGlzLnN1YnRsZS5kZWNyeXB0KHtcbiAgICAgIG5hbWU6ICdBRVMtQ0JDJyxcbiAgICAgIGl2OiB0aGlzLmFlc0lWXG4gICAgfSwga2V5LCBkYXRhKTtcbiAgfVxufVxuXG5jbGFzcyBGYXN0QUVTS2V5IHtcbiAgY29uc3RydWN0b3Ioc3VidGxlLCBrZXkpIHtcbiAgICB0aGlzLnN1YnRsZSA9IHZvaWQgMDtcbiAgICB0aGlzLmtleSA9IHZvaWQgMDtcbiAgICB0aGlzLnN1YnRsZSA9IHN1YnRsZTtcbiAgICB0aGlzLmtleSA9IGtleTtcbiAgfVxuICBleHBhbmRLZXkoKSB7XG4gICAgcmV0dXJuIHRoaXMuc3VidGxlLmltcG9ydEtleSgncmF3JywgdGhpcy5rZXksIHtcbiAgICAgIG5hbWU6ICdBRVMtQ0JDJ1xuICAgIH0sIGZhbHNlLCBbJ2VuY3J5cHQnLCAnZGVjcnlwdCddKTtcbiAgfVxufVxuXG4vLyBQS0NTN1xuZnVuY3Rpb24gcmVtb3ZlUGFkZGluZyhhcnJheSkge1xuICBjb25zdCBvdXRwdXRCeXRlcyA9IGFycmF5LmJ5dGVMZW5ndGg7XG4gIGNvbnN0IHBhZGRpbmdCeXRlcyA9IG91dHB1dEJ5dGVzICYmIG5ldyBEYXRhVmlldyhhcnJheS5idWZmZXIpLmdldFVpbnQ4KG91dHB1dEJ5dGVzIC0gMSk7XG4gIGlmIChwYWRkaW5nQnl0ZXMpIHtcbiAgICByZXR1cm4gc2xpY2VVaW50OChhcnJheSwgMCwgb3V0cHV0Qnl0ZXMgLSBwYWRkaW5nQnl0ZXMpO1xuICB9XG4gIHJldHVybiBhcnJheTtcbn1cbmNsYXNzIEFFU0RlY3J5cHRvciB7XG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHRoaXMucmNvbiA9IFsweDAsIDB4MSwgMHgyLCAweDQsIDB4OCwgMHgxMCwgMHgyMCwgMHg0MCwgMHg4MCwgMHgxYiwgMHgzNl07XG4gICAgdGhpcy5zdWJNaXggPSBbbmV3IFVpbnQzMkFycmF5KDI1NiksIG5ldyBVaW50MzJBcnJheSgyNTYpLCBuZXcgVWludDMyQXJyYXkoMjU2KSwgbmV3IFVpbnQzMkFycmF5KDI1NildO1xuICAgIHRoaXMuaW52U3ViTWl4ID0gW25ldyBVaW50MzJBcnJheSgyNTYpLCBuZXcgVWludDMyQXJyYXkoMjU2KSwgbmV3IFVpbnQzMkFycmF5KDI1NiksIG5ldyBVaW50MzJBcnJheSgyNTYpXTtcbiAgICB0aGlzLnNCb3ggPSBuZXcgVWludDMyQXJyYXkoMjU2KTtcbiAgICB0aGlzLmludlNCb3ggPSBuZXcgVWludDMyQXJyYXkoMjU2KTtcbiAgICB0aGlzLmtleSA9IG5ldyBVaW50MzJBcnJheSgwKTtcbiAgICB0aGlzLmtzUm93cyA9IDA7XG4gICAgdGhpcy5rZXlTaXplID0gMDtcbiAgICB0aGlzLmtleVNjaGVkdWxlID0gdm9pZCAwO1xuICAgIHRoaXMuaW52S2V5U2NoZWR1bGUgPSB2b2lkIDA7XG4gICAgdGhpcy5pbml0VGFibGUoKTtcbiAgfVxuXG4gIC8vIFVzaW5nIHZpZXcuZ2V0VWludDMyKCkgYWxzbyBzd2FwcyB0aGUgYnl0ZSBvcmRlci5cbiAgdWludDhBcnJheVRvVWludDMyQXJyYXlfKGFycmF5QnVmZmVyKSB7XG4gICAgY29uc3QgdmlldyA9IG5ldyBEYXRhVmlldyhhcnJheUJ1ZmZlcik7XG4gICAgY29uc3QgbmV3QXJyYXkgPSBuZXcgVWludDMyQXJyYXkoNCk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCA0OyBpKyspIHtcbiAgICAgIG5ld0FycmF5W2ldID0gdmlldy5nZXRVaW50MzIoaSAqIDQpO1xuICAgIH1cbiAgICByZXR1cm4gbmV3QXJyYXk7XG4gIH1cbiAgaW5pdFRhYmxlKCkge1xuICAgIGNvbnN0IHNCb3ggPSB0aGlzLnNCb3g7XG4gICAgY29uc3QgaW52U0JveCA9IHRoaXMuaW52U0JveDtcbiAgICBjb25zdCBzdWJNaXggPSB0aGlzLnN1Yk1peDtcbiAgICBjb25zdCBzdWJNaXgwID0gc3ViTWl4WzBdO1xuICAgIGNvbnN0IHN1Yk1peDEgPSBzdWJNaXhbMV07XG4gICAgY29uc3Qgc3ViTWl4MiA9IHN1Yk1peFsyXTtcbiAgICBjb25zdCBzdWJNaXgzID0gc3ViTWl4WzNdO1xuICAgIGNvbnN0IGludlN1Yk1peCA9IHRoaXMuaW52U3ViTWl4O1xuICAgIGNvbnN0IGludlN1Yk1peDAgPSBpbnZTdWJNaXhbMF07XG4gICAgY29uc3QgaW52U3ViTWl4MSA9IGludlN1Yk1peFsxXTtcbiAgICBjb25zdCBpbnZTdWJNaXgyID0gaW52U3ViTWl4WzJdO1xuICAgIGNvbnN0IGludlN1Yk1peDMgPSBpbnZTdWJNaXhbM107XG4gICAgY29uc3QgZCA9IG5ldyBVaW50MzJBcnJheSgyNTYpO1xuICAgIGxldCB4ID0gMDtcbiAgICBsZXQgeGkgPSAwO1xuICAgIGxldCBpID0gMDtcbiAgICBmb3IgKGkgPSAwOyBpIDwgMjU2OyBpKyspIHtcbiAgICAgIGlmIChpIDwgMTI4KSB7XG4gICAgICAgIGRbaV0gPSBpIDw8IDE7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBkW2ldID0gaSA8PCAxIF4gMHgxMWI7XG4gICAgICB9XG4gICAgfVxuICAgIGZvciAoaSA9IDA7IGkgPCAyNTY7IGkrKykge1xuICAgICAgbGV0IHN4ID0geGkgXiB4aSA8PCAxIF4geGkgPDwgMiBeIHhpIDw8IDMgXiB4aSA8PCA0O1xuICAgICAgc3ggPSBzeCA+Pj4gOCBeIHN4ICYgMHhmZiBeIDB4NjM7XG4gICAgICBzQm94W3hdID0gc3g7XG4gICAgICBpbnZTQm94W3N4XSA9IHg7XG5cbiAgICAgIC8vIENvbXB1dGUgbXVsdGlwbGljYXRpb25cbiAgICAgIGNvbnN0IHgyID0gZFt4XTtcbiAgICAgIGNvbnN0IHg0ID0gZFt4Ml07XG4gICAgICBjb25zdCB4OCA9IGRbeDRdO1xuXG4gICAgICAvLyBDb21wdXRlIHN1Yi9pbnZTdWIgYnl0ZXMsIG1peCBjb2x1bW5zIHRhYmxlc1xuICAgICAgbGV0IHQgPSBkW3N4XSAqIDB4MTAxIF4gc3ggKiAweDEwMTAxMDA7XG4gICAgICBzdWJNaXgwW3hdID0gdCA8PCAyNCB8IHQgPj4+IDg7XG4gICAgICBzdWJNaXgxW3hdID0gdCA8PCAxNiB8IHQgPj4+IDE2O1xuICAgICAgc3ViTWl4Mlt4XSA9IHQgPDwgOCB8IHQgPj4+IDI0O1xuICAgICAgc3ViTWl4M1t4XSA9IHQ7XG5cbiAgICAgIC8vIENvbXB1dGUgaW52IHN1YiBieXRlcywgaW52IG1peCBjb2x1bW5zIHRhYmxlc1xuICAgICAgdCA9IHg4ICogMHgxMDEwMTAxIF4geDQgKiAweDEwMDAxIF4geDIgKiAweDEwMSBeIHggKiAweDEwMTAxMDA7XG4gICAgICBpbnZTdWJNaXgwW3N4XSA9IHQgPDwgMjQgfCB0ID4+PiA4O1xuICAgICAgaW52U3ViTWl4MVtzeF0gPSB0IDw8IDE2IHwgdCA+Pj4gMTY7XG4gICAgICBpbnZTdWJNaXgyW3N4XSA9IHQgPDwgOCB8IHQgPj4+IDI0O1xuICAgICAgaW52U3ViTWl4M1tzeF0gPSB0O1xuXG4gICAgICAvLyBDb21wdXRlIG5leHQgY291bnRlclxuICAgICAgaWYgKCF4KSB7XG4gICAgICAgIHggPSB4aSA9IDE7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB4ID0geDIgXiBkW2RbZFt4OCBeIHgyXV1dO1xuICAgICAgICB4aSBePSBkW2RbeGldXTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgZXhwYW5kS2V5KGtleUJ1ZmZlcikge1xuICAgIC8vIGNvbnZlcnQga2V5QnVmZmVyIHRvIFVpbnQzMkFycmF5XG4gICAgY29uc3Qga2V5ID0gdGhpcy51aW50OEFycmF5VG9VaW50MzJBcnJheV8oa2V5QnVmZmVyKTtcbiAgICBsZXQgc2FtZUtleSA9IHRydWU7XG4gICAgbGV0IG9mZnNldCA9IDA7XG4gICAgd2hpbGUgKG9mZnNldCA8IGtleS5sZW5ndGggJiYgc2FtZUtleSkge1xuICAgICAgc2FtZUtleSA9IGtleVtvZmZzZXRdID09PSB0aGlzLmtleVtvZmZzZXRdO1xuICAgICAgb2Zmc2V0Kys7XG4gICAgfVxuICAgIGlmIChzYW1lS2V5KSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMua2V5ID0ga2V5O1xuICAgIGNvbnN0IGtleVNpemUgPSB0aGlzLmtleVNpemUgPSBrZXkubGVuZ3RoO1xuICAgIGlmIChrZXlTaXplICE9PSA0ICYmIGtleVNpemUgIT09IDYgJiYga2V5U2l6ZSAhPT0gOCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIGFlcyBrZXkgc2l6ZT0nICsga2V5U2l6ZSk7XG4gICAgfVxuICAgIGNvbnN0IGtzUm93cyA9IHRoaXMua3NSb3dzID0gKGtleVNpemUgKyA2ICsgMSkgKiA0O1xuICAgIGxldCBrc1JvdztcbiAgICBsZXQgaW52S3NSb3c7XG4gICAgY29uc3Qga2V5U2NoZWR1bGUgPSB0aGlzLmtleVNjaGVkdWxlID0gbmV3IFVpbnQzMkFycmF5KGtzUm93cyk7XG4gICAgY29uc3QgaW52S2V5U2NoZWR1bGUgPSB0aGlzLmludktleVNjaGVkdWxlID0gbmV3IFVpbnQzMkFycmF5KGtzUm93cyk7XG4gICAgY29uc3Qgc2JveCA9IHRoaXMuc0JveDtcbiAgICBjb25zdCByY29uID0gdGhpcy5yY29uO1xuICAgIGNvbnN0IGludlN1Yk1peCA9IHRoaXMuaW52U3ViTWl4O1xuICAgIGNvbnN0IGludlN1Yk1peDAgPSBpbnZTdWJNaXhbMF07XG4gICAgY29uc3QgaW52U3ViTWl4MSA9IGludlN1Yk1peFsxXTtcbiAgICBjb25zdCBpbnZTdWJNaXgyID0gaW52U3ViTWl4WzJdO1xuICAgIGNvbnN0IGludlN1Yk1peDMgPSBpbnZTdWJNaXhbM107XG4gICAgbGV0IHByZXY7XG4gICAgbGV0IHQ7XG4gICAgZm9yIChrc1JvdyA9IDA7IGtzUm93IDwga3NSb3dzOyBrc1JvdysrKSB7XG4gICAgICBpZiAoa3NSb3cgPCBrZXlTaXplKSB7XG4gICAgICAgIHByZXYgPSBrZXlTY2hlZHVsZVtrc1Jvd10gPSBrZXlba3NSb3ddO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIHQgPSBwcmV2O1xuICAgICAgaWYgKGtzUm93ICUga2V5U2l6ZSA9PT0gMCkge1xuICAgICAgICAvLyBSb3Qgd29yZFxuICAgICAgICB0ID0gdCA8PCA4IHwgdCA+Pj4gMjQ7XG5cbiAgICAgICAgLy8gU3ViIHdvcmRcbiAgICAgICAgdCA9IHNib3hbdCA+Pj4gMjRdIDw8IDI0IHwgc2JveFt0ID4+PiAxNiAmIDB4ZmZdIDw8IDE2IHwgc2JveFt0ID4+PiA4ICYgMHhmZl0gPDwgOCB8IHNib3hbdCAmIDB4ZmZdO1xuXG4gICAgICAgIC8vIE1peCBSY29uXG4gICAgICAgIHQgXj0gcmNvbltrc1JvdyAvIGtleVNpemUgfCAwXSA8PCAyNDtcbiAgICAgIH0gZWxzZSBpZiAoa2V5U2l6ZSA+IDYgJiYga3NSb3cgJSBrZXlTaXplID09PSA0KSB7XG4gICAgICAgIC8vIFN1YiB3b3JkXG4gICAgICAgIHQgPSBzYm94W3QgPj4+IDI0XSA8PCAyNCB8IHNib3hbdCA+Pj4gMTYgJiAweGZmXSA8PCAxNiB8IHNib3hbdCA+Pj4gOCAmIDB4ZmZdIDw8IDggfCBzYm94W3QgJiAweGZmXTtcbiAgICAgIH1cbiAgICAgIGtleVNjaGVkdWxlW2tzUm93XSA9IHByZXYgPSAoa2V5U2NoZWR1bGVba3NSb3cgLSBrZXlTaXplXSBeIHQpID4+PiAwO1xuICAgIH1cbiAgICBmb3IgKGludktzUm93ID0gMDsgaW52S3NSb3cgPCBrc1Jvd3M7IGludktzUm93KyspIHtcbiAgICAgIGtzUm93ID0ga3NSb3dzIC0gaW52S3NSb3c7XG4gICAgICBpZiAoaW52S3NSb3cgJiAzKSB7XG4gICAgICAgIHQgPSBrZXlTY2hlZHVsZVtrc1Jvd107XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0ID0ga2V5U2NoZWR1bGVba3NSb3cgLSA0XTtcbiAgICAgIH1cbiAgICAgIGlmIChpbnZLc1JvdyA8IDQgfHwga3NSb3cgPD0gNCkge1xuICAgICAgICBpbnZLZXlTY2hlZHVsZVtpbnZLc1Jvd10gPSB0O1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaW52S2V5U2NoZWR1bGVbaW52S3NSb3ddID0gaW52U3ViTWl4MFtzYm94W3QgPj4+IDI0XV0gXiBpbnZTdWJNaXgxW3Nib3hbdCA+Pj4gMTYgJiAweGZmXV0gXiBpbnZTdWJNaXgyW3Nib3hbdCA+Pj4gOCAmIDB4ZmZdXSBeIGludlN1Yk1peDNbc2JveFt0ICYgMHhmZl1dO1xuICAgICAgfVxuICAgICAgaW52S2V5U2NoZWR1bGVbaW52S3NSb3ddID0gaW52S2V5U2NoZWR1bGVbaW52S3NSb3ddID4+PiAwO1xuICAgIH1cbiAgfVxuXG4gIC8vIEFkZGluZyB0aGlzIGFzIGEgbWV0aG9kIGdyZWF0bHkgaW1wcm92ZXMgcGVyZm9ybWFuY2UuXG4gIG5ldHdvcmtUb0hvc3RPcmRlclN3YXAod29yZCkge1xuICAgIHJldHVybiB3b3JkIDw8IDI0IHwgKHdvcmQgJiAweGZmMDApIDw8IDggfCAod29yZCAmIDB4ZmYwMDAwKSA+PiA4IHwgd29yZCA+Pj4gMjQ7XG4gIH1cbiAgZGVjcnlwdChpbnB1dEFycmF5QnVmZmVyLCBvZmZzZXQsIGFlc0lWKSB7XG4gICAgY29uc3QgblJvdW5kcyA9IHRoaXMua2V5U2l6ZSArIDY7XG4gICAgY29uc3QgaW52S2V5U2NoZWR1bGUgPSB0aGlzLmludktleVNjaGVkdWxlO1xuICAgIGNvbnN0IGludlNCT1ggPSB0aGlzLmludlNCb3g7XG4gICAgY29uc3QgaW52U3ViTWl4ID0gdGhpcy5pbnZTdWJNaXg7XG4gICAgY29uc3QgaW52U3ViTWl4MCA9IGludlN1Yk1peFswXTtcbiAgICBjb25zdCBpbnZTdWJNaXgxID0gaW52U3ViTWl4WzFdO1xuICAgIGNvbnN0IGludlN1Yk1peDIgPSBpbnZTdWJNaXhbMl07XG4gICAgY29uc3QgaW52U3ViTWl4MyA9IGludlN1Yk1peFszXTtcbiAgICBjb25zdCBpbml0VmVjdG9yID0gdGhpcy51aW50OEFycmF5VG9VaW50MzJBcnJheV8oYWVzSVYpO1xuICAgIGxldCBpbml0VmVjdG9yMCA9IGluaXRWZWN0b3JbMF07XG4gICAgbGV0IGluaXRWZWN0b3IxID0gaW5pdFZlY3RvclsxXTtcbiAgICBsZXQgaW5pdFZlY3RvcjIgPSBpbml0VmVjdG9yWzJdO1xuICAgIGxldCBpbml0VmVjdG9yMyA9IGluaXRWZWN0b3JbM107XG4gICAgY29uc3QgaW5wdXRJbnQzMiA9IG5ldyBJbnQzMkFycmF5KGlucHV0QXJyYXlCdWZmZXIpO1xuICAgIGNvbnN0IG91dHB1dEludDMyID0gbmV3IEludDMyQXJyYXkoaW5wdXRJbnQzMi5sZW5ndGgpO1xuICAgIGxldCB0MCwgdDEsIHQyLCB0MztcbiAgICBsZXQgczAsIHMxLCBzMiwgczM7XG4gICAgbGV0IGlucHV0V29yZHMwLCBpbnB1dFdvcmRzMSwgaW5wdXRXb3JkczIsIGlucHV0V29yZHMzO1xuICAgIGxldCBrc1JvdywgaTtcbiAgICBjb25zdCBzd2FwV29yZCA9IHRoaXMubmV0d29ya1RvSG9zdE9yZGVyU3dhcDtcbiAgICB3aGlsZSAob2Zmc2V0IDwgaW5wdXRJbnQzMi5sZW5ndGgpIHtcbiAgICAgIGlucHV0V29yZHMwID0gc3dhcFdvcmQoaW5wdXRJbnQzMltvZmZzZXRdKTtcbiAgICAgIGlucHV0V29yZHMxID0gc3dhcFdvcmQoaW5wdXRJbnQzMltvZmZzZXQgKyAxXSk7XG4gICAgICBpbnB1dFdvcmRzMiA9IHN3YXBXb3JkKGlucHV0SW50MzJbb2Zmc2V0ICsgMl0pO1xuICAgICAgaW5wdXRXb3JkczMgPSBzd2FwV29yZChpbnB1dEludDMyW29mZnNldCArIDNdKTtcbiAgICAgIHMwID0gaW5wdXRXb3JkczAgXiBpbnZLZXlTY2hlZHVsZVswXTtcbiAgICAgIHMxID0gaW5wdXRXb3JkczMgXiBpbnZLZXlTY2hlZHVsZVsxXTtcbiAgICAgIHMyID0gaW5wdXRXb3JkczIgXiBpbnZLZXlTY2hlZHVsZVsyXTtcbiAgICAgIHMzID0gaW5wdXRXb3JkczEgXiBpbnZLZXlTY2hlZHVsZVszXTtcbiAgICAgIGtzUm93ID0gNDtcblxuICAgICAgLy8gSXRlcmF0ZSB0aHJvdWdoIHRoZSByb3VuZHMgb2YgZGVjcnlwdGlvblxuICAgICAgZm9yIChpID0gMTsgaSA8IG5Sb3VuZHM7IGkrKykge1xuICAgICAgICB0MCA9IGludlN1Yk1peDBbczAgPj4+IDI0XSBeIGludlN1Yk1peDFbczEgPj4gMTYgJiAweGZmXSBeIGludlN1Yk1peDJbczIgPj4gOCAmIDB4ZmZdIF4gaW52U3ViTWl4M1tzMyAmIDB4ZmZdIF4gaW52S2V5U2NoZWR1bGVba3NSb3ddO1xuICAgICAgICB0MSA9IGludlN1Yk1peDBbczEgPj4+IDI0XSBeIGludlN1Yk1peDFbczIgPj4gMTYgJiAweGZmXSBeIGludlN1Yk1peDJbczMgPj4gOCAmIDB4ZmZdIF4gaW52U3ViTWl4M1tzMCAmIDB4ZmZdIF4gaW52S2V5U2NoZWR1bGVba3NSb3cgKyAxXTtcbiAgICAgICAgdDIgPSBpbnZTdWJNaXgwW3MyID4+PiAyNF0gXiBpbnZTdWJNaXgxW3MzID4+IDE2ICYgMHhmZl0gXiBpbnZTdWJNaXgyW3MwID4+IDggJiAweGZmXSBeIGludlN1Yk1peDNbczEgJiAweGZmXSBeIGludktleVNjaGVkdWxlW2tzUm93ICsgMl07XG4gICAgICAgIHQzID0gaW52U3ViTWl4MFtzMyA+Pj4gMjRdIF4gaW52U3ViTWl4MVtzMCA+PiAxNiAmIDB4ZmZdIF4gaW52U3ViTWl4MltzMSA+PiA4ICYgMHhmZl0gXiBpbnZTdWJNaXgzW3MyICYgMHhmZl0gXiBpbnZLZXlTY2hlZHVsZVtrc1JvdyArIDNdO1xuICAgICAgICAvLyBVcGRhdGUgc3RhdGVcbiAgICAgICAgczAgPSB0MDtcbiAgICAgICAgczEgPSB0MTtcbiAgICAgICAgczIgPSB0MjtcbiAgICAgICAgczMgPSB0MztcbiAgICAgICAga3NSb3cgPSBrc1JvdyArIDQ7XG4gICAgICB9XG5cbiAgICAgIC8vIFNoaWZ0IHJvd3MsIHN1YiBieXRlcywgYWRkIHJvdW5kIGtleVxuICAgICAgdDAgPSBpbnZTQk9YW3MwID4+PiAyNF0gPDwgMjQgXiBpbnZTQk9YW3MxID4+IDE2ICYgMHhmZl0gPDwgMTYgXiBpbnZTQk9YW3MyID4+IDggJiAweGZmXSA8PCA4IF4gaW52U0JPWFtzMyAmIDB4ZmZdIF4gaW52S2V5U2NoZWR1bGVba3NSb3ddO1xuICAgICAgdDEgPSBpbnZTQk9YW3MxID4+PiAyNF0gPDwgMjQgXiBpbnZTQk9YW3MyID4+IDE2ICYgMHhmZl0gPDwgMTYgXiBpbnZTQk9YW3MzID4+IDggJiAweGZmXSA8PCA4IF4gaW52U0JPWFtzMCAmIDB4ZmZdIF4gaW52S2V5U2NoZWR1bGVba3NSb3cgKyAxXTtcbiAgICAgIHQyID0gaW52U0JPWFtzMiA+Pj4gMjRdIDw8IDI0IF4gaW52U0JPWFtzMyA+PiAxNiAmIDB4ZmZdIDw8IDE2IF4gaW52U0JPWFtzMCA+PiA4ICYgMHhmZl0gPDwgOCBeIGludlNCT1hbczEgJiAweGZmXSBeIGludktleVNjaGVkdWxlW2tzUm93ICsgMl07XG4gICAgICB0MyA9IGludlNCT1hbczMgPj4+IDI0XSA8PCAyNCBeIGludlNCT1hbczAgPj4gMTYgJiAweGZmXSA8PCAxNiBeIGludlNCT1hbczEgPj4gOCAmIDB4ZmZdIDw8IDggXiBpbnZTQk9YW3MyICYgMHhmZl0gXiBpbnZLZXlTY2hlZHVsZVtrc1JvdyArIDNdO1xuXG4gICAgICAvLyBXcml0ZVxuICAgICAgb3V0cHV0SW50MzJbb2Zmc2V0XSA9IHN3YXBXb3JkKHQwIF4gaW5pdFZlY3RvcjApO1xuICAgICAgb3V0cHV0SW50MzJbb2Zmc2V0ICsgMV0gPSBzd2FwV29yZCh0MyBeIGluaXRWZWN0b3IxKTtcbiAgICAgIG91dHB1dEludDMyW29mZnNldCArIDJdID0gc3dhcFdvcmQodDIgXiBpbml0VmVjdG9yMik7XG4gICAgICBvdXRwdXRJbnQzMltvZmZzZXQgKyAzXSA9IHN3YXBXb3JkKHQxIF4gaW5pdFZlY3RvcjMpO1xuXG4gICAgICAvLyByZXNldCBpbml0VmVjdG9yIHRvIGxhc3QgNCB1bnNpZ25lZCBpbnRcbiAgICAgIGluaXRWZWN0b3IwID0gaW5wdXRXb3JkczA7XG4gICAgICBpbml0VmVjdG9yMSA9IGlucHV0V29yZHMxO1xuICAgICAgaW5pdFZlY3RvcjIgPSBpbnB1dFdvcmRzMjtcbiAgICAgIGluaXRWZWN0b3IzID0gaW5wdXRXb3JkczM7XG4gICAgICBvZmZzZXQgPSBvZmZzZXQgKyA0O1xuICAgIH1cbiAgICByZXR1cm4gb3V0cHV0SW50MzIuYnVmZmVyO1xuICB9XG59XG5cbmNvbnN0IENIVU5LX1NJWkUgPSAxNjsgLy8gMTYgYnl0ZXMsIDEyOCBiaXRzXG5cbmNsYXNzIERlY3J5cHRlciB7XG4gIGNvbnN0cnVjdG9yKGNvbmZpZywge1xuICAgIHJlbW92ZVBLQ1M3UGFkZGluZyA9IHRydWVcbiAgfSA9IHt9KSB7XG4gICAgdGhpcy5sb2dFbmFibGVkID0gdHJ1ZTtcbiAgICB0aGlzLnJlbW92ZVBLQ1M3UGFkZGluZyA9IHZvaWQgMDtcbiAgICB0aGlzLnN1YnRsZSA9IG51bGw7XG4gICAgdGhpcy5zb2Z0d2FyZURlY3J5cHRlciA9IG51bGw7XG4gICAgdGhpcy5rZXkgPSBudWxsO1xuICAgIHRoaXMuZmFzdEFlc0tleSA9IG51bGw7XG4gICAgdGhpcy5yZW1haW5kZXJEYXRhID0gbnVsbDtcbiAgICB0aGlzLmN1cnJlbnRJViA9IG51bGw7XG4gICAgdGhpcy5jdXJyZW50UmVzdWx0ID0gbnVsbDtcbiAgICB0aGlzLnVzZVNvZnR3YXJlID0gdm9pZCAwO1xuICAgIHRoaXMudXNlU29mdHdhcmUgPSBjb25maWcuZW5hYmxlU29mdHdhcmVBRVM7XG4gICAgdGhpcy5yZW1vdmVQS0NTN1BhZGRpbmcgPSByZW1vdmVQS0NTN1BhZGRpbmc7XG4gICAgLy8gYnVpbHQgaW4gZGVjcnlwdG9yIGV4cGVjdHMgUEtDUzcgcGFkZGluZ1xuICAgIGlmIChyZW1vdmVQS0NTN1BhZGRpbmcpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IGJyb3dzZXJDcnlwdG8gPSBzZWxmLmNyeXB0bztcbiAgICAgICAgaWYgKGJyb3dzZXJDcnlwdG8pIHtcbiAgICAgICAgICB0aGlzLnN1YnRsZSA9IGJyb3dzZXJDcnlwdG8uc3VidGxlIHx8IGJyb3dzZXJDcnlwdG8ud2Via2l0U3VidGxlO1xuICAgICAgICB9XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIC8qIG5vLW9wICovXG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMudXNlU29mdHdhcmUgPSAhdGhpcy5zdWJ0bGU7XG4gIH1cbiAgZGVzdHJveSgpIHtcbiAgICB0aGlzLnN1YnRsZSA9IG51bGw7XG4gICAgdGhpcy5zb2Z0d2FyZURlY3J5cHRlciA9IG51bGw7XG4gICAgdGhpcy5rZXkgPSBudWxsO1xuICAgIHRoaXMuZmFzdEFlc0tleSA9IG51bGw7XG4gICAgdGhpcy5yZW1haW5kZXJEYXRhID0gbnVsbDtcbiAgICB0aGlzLmN1cnJlbnRJViA9IG51bGw7XG4gICAgdGhpcy5jdXJyZW50UmVzdWx0ID0gbnVsbDtcbiAgfVxuICBpc1N5bmMoKSB7XG4gICAgcmV0dXJuIHRoaXMudXNlU29mdHdhcmU7XG4gIH1cbiAgZmx1c2goKSB7XG4gICAgY29uc3Qge1xuICAgICAgY3VycmVudFJlc3VsdCxcbiAgICAgIHJlbWFpbmRlckRhdGFcbiAgICB9ID0gdGhpcztcbiAgICBpZiAoIWN1cnJlbnRSZXN1bHQgfHwgcmVtYWluZGVyRGF0YSkge1xuICAgICAgdGhpcy5yZXNldCgpO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIGNvbnN0IGRhdGEgPSBuZXcgVWludDhBcnJheShjdXJyZW50UmVzdWx0KTtcbiAgICB0aGlzLnJlc2V0KCk7XG4gICAgaWYgKHRoaXMucmVtb3ZlUEtDUzdQYWRkaW5nKSB7XG4gICAgICByZXR1cm4gcmVtb3ZlUGFkZGluZyhkYXRhKTtcbiAgICB9XG4gICAgcmV0dXJuIGRhdGE7XG4gIH1cbiAgcmVzZXQoKSB7XG4gICAgdGhpcy5jdXJyZW50UmVzdWx0ID0gbnVsbDtcbiAgICB0aGlzLmN1cnJlbnRJViA9IG51bGw7XG4gICAgdGhpcy5yZW1haW5kZXJEYXRhID0gbnVsbDtcbiAgICBpZiAodGhpcy5zb2Z0d2FyZURlY3J5cHRlcikge1xuICAgICAgdGhpcy5zb2Z0d2FyZURlY3J5cHRlciA9IG51bGw7XG4gICAgfVxuICB9XG4gIGRlY3J5cHQoZGF0YSwga2V5LCBpdikge1xuICAgIGlmICh0aGlzLnVzZVNvZnR3YXJlKSB7XG4gICAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICB0aGlzLnNvZnR3YXJlRGVjcnlwdChuZXcgVWludDhBcnJheShkYXRhKSwga2V5LCBpdik7XG4gICAgICAgIGNvbnN0IGRlY3J5cHRSZXN1bHQgPSB0aGlzLmZsdXNoKCk7XG4gICAgICAgIGlmIChkZWNyeXB0UmVzdWx0KSB7XG4gICAgICAgICAgcmVzb2x2ZShkZWNyeXB0UmVzdWx0LmJ1ZmZlcik7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcignW3NvZnR3YXJlRGVjcnlwdF0gRmFpbGVkIHRvIGRlY3J5cHQgZGF0YScpKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLndlYkNyeXB0b0RlY3J5cHQobmV3IFVpbnQ4QXJyYXkoZGF0YSksIGtleSwgaXYpO1xuICB9XG5cbiAgLy8gU29mdHdhcmUgZGVjcnlwdGlvbiBpcyBwcm9ncmVzc2l2ZS4gUHJvZ3Jlc3NpdmUgZGVjcnlwdGlvbiBtYXkgbm90IHJldHVybiBhIHJlc3VsdCBvbiBlYWNoIGNhbGwuIEFueSBjYWNoZWRcbiAgLy8gZGF0YSBpcyBoYW5kbGVkIGluIHRoZSBmbHVzaCgpIGNhbGxcbiAgc29mdHdhcmVEZWNyeXB0KGRhdGEsIGtleSwgaXYpIHtcbiAgICBjb25zdCB7XG4gICAgICBjdXJyZW50SVYsXG4gICAgICBjdXJyZW50UmVzdWx0LFxuICAgICAgcmVtYWluZGVyRGF0YVxuICAgIH0gPSB0aGlzO1xuICAgIHRoaXMubG9nT25jZSgnSlMgQUVTIGRlY3J5cHQnKTtcbiAgICAvLyBUaGUgb3V0cHV0IGlzIHN0YWdnZXJlZCBkdXJpbmcgcHJvZ3Jlc3NpdmUgcGFyc2luZyAtIHRoZSBjdXJyZW50IHJlc3VsdCBpcyBjYWNoZWQsIGFuZCBlbWl0dGVkIG9uIHRoZSBuZXh0IGNhbGxcbiAgICAvLyBUaGlzIGlzIGRvbmUgaW4gb3JkZXIgdG8gc3RyaXAgUEtDUzcgcGFkZGluZywgd2hpY2ggaXMgZm91bmQgYXQgdGhlIGVuZCBvZiBlYWNoIHNlZ21lbnQuIFdlIG9ubHkga25vdyB3ZSd2ZSByZWFjaGVkXG4gICAgLy8gdGhlIGVuZCBvbiBmbHVzaCgpLCBidXQgYnkgdGhhdCB0aW1lIHdlIGhhdmUgYWxyZWFkeSByZWNlaXZlZCBhbGwgYnl0ZXMgZm9yIHRoZSBzZWdtZW50LlxuICAgIC8vIFByb2dyZXNzaXZlIGRlY3J5cHRpb24gZG9lcyBub3Qgd29yayB3aXRoIFdlYkNyeXB0b1xuXG4gICAgaWYgKHJlbWFpbmRlckRhdGEpIHtcbiAgICAgIGRhdGEgPSBhcHBlbmRVaW50OEFycmF5KHJlbWFpbmRlckRhdGEsIGRhdGEpO1xuICAgICAgdGhpcy5yZW1haW5kZXJEYXRhID0gbnVsbDtcbiAgICB9XG5cbiAgICAvLyBCeXRlIGxlbmd0aCBtdXN0IGJlIGEgbXVsdGlwbGUgb2YgMTYgKEFFUy0xMjggPSAxMjggYml0IGJsb2NrcyA9IDE2IGJ5dGVzKVxuICAgIGNvbnN0IGN1cnJlbnRDaHVuayA9IHRoaXMuZ2V0VmFsaWRDaHVuayhkYXRhKTtcbiAgICBpZiAoIWN1cnJlbnRDaHVuay5sZW5ndGgpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICBpZiAoY3VycmVudElWKSB7XG4gICAgICBpdiA9IGN1cnJlbnRJVjtcbiAgICB9XG4gICAgbGV0IHNvZnR3YXJlRGVjcnlwdGVyID0gdGhpcy5zb2Z0d2FyZURlY3J5cHRlcjtcbiAgICBpZiAoIXNvZnR3YXJlRGVjcnlwdGVyKSB7XG4gICAgICBzb2Z0d2FyZURlY3J5cHRlciA9IHRoaXMuc29mdHdhcmVEZWNyeXB0ZXIgPSBuZXcgQUVTRGVjcnlwdG9yKCk7XG4gICAgfVxuICAgIHNvZnR3YXJlRGVjcnlwdGVyLmV4cGFuZEtleShrZXkpO1xuICAgIGNvbnN0IHJlc3VsdCA9IGN1cnJlbnRSZXN1bHQ7XG4gICAgdGhpcy5jdXJyZW50UmVzdWx0ID0gc29mdHdhcmVEZWNyeXB0ZXIuZGVjcnlwdChjdXJyZW50Q2h1bmsuYnVmZmVyLCAwLCBpdik7XG4gICAgdGhpcy5jdXJyZW50SVYgPSBzbGljZVVpbnQ4KGN1cnJlbnRDaHVuaywgLTE2KS5idWZmZXI7XG4gICAgaWYgKCFyZXN1bHQpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG4gIHdlYkNyeXB0b0RlY3J5cHQoZGF0YSwga2V5LCBpdikge1xuICAgIGlmICh0aGlzLmtleSAhPT0ga2V5IHx8ICF0aGlzLmZhc3RBZXNLZXkpIHtcbiAgICAgIGlmICghdGhpcy5zdWJ0bGUpIHtcbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSh0aGlzLm9uV2ViQ3J5cHRvRXJyb3IoZGF0YSwga2V5LCBpdikpO1xuICAgICAgfVxuICAgICAgdGhpcy5rZXkgPSBrZXk7XG4gICAgICB0aGlzLmZhc3RBZXNLZXkgPSBuZXcgRmFzdEFFU0tleSh0aGlzLnN1YnRsZSwga2V5KTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZmFzdEFlc0tleS5leHBhbmRLZXkoKS50aGVuKGFlc0tleSA9PiB7XG4gICAgICAvLyBkZWNyeXB0IHVzaW5nIHdlYiBjcnlwdG9cbiAgICAgIGlmICghdGhpcy5zdWJ0bGUpIHtcbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcignd2ViIGNyeXB0byBub3QgaW5pdGlhbGl6ZWQnKSk7XG4gICAgICB9XG4gICAgICB0aGlzLmxvZ09uY2UoJ1dlYkNyeXB0byBBRVMgZGVjcnlwdCcpO1xuICAgICAgY29uc3QgY3J5cHRvID0gbmV3IEFFU0NyeXB0byh0aGlzLnN1YnRsZSwgbmV3IFVpbnQ4QXJyYXkoaXYpKTtcbiAgICAgIHJldHVybiBjcnlwdG8uZGVjcnlwdChkYXRhLmJ1ZmZlciwgYWVzS2V5KTtcbiAgICB9KS5jYXRjaChlcnIgPT4ge1xuICAgICAgbG9nZ2VyLndhcm4oYFtkZWNyeXB0ZXJdOiBXZWJDcnlwdG8gRXJyb3IsIGRpc2FibGUgV2ViQ3J5cHRvIEFQSSwgJHtlcnIubmFtZX06ICR7ZXJyLm1lc3NhZ2V9YCk7XG4gICAgICByZXR1cm4gdGhpcy5vbldlYkNyeXB0b0Vycm9yKGRhdGEsIGtleSwgaXYpO1xuICAgIH0pO1xuICB9XG4gIG9uV2ViQ3J5cHRvRXJyb3IoZGF0YSwga2V5LCBpdikge1xuICAgIHRoaXMudXNlU29mdHdhcmUgPSB0cnVlO1xuICAgIHRoaXMubG9nRW5hYmxlZCA9IHRydWU7XG4gICAgdGhpcy5zb2Z0d2FyZURlY3J5cHQoZGF0YSwga2V5LCBpdik7XG4gICAgY29uc3QgZGVjcnlwdFJlc3VsdCA9IHRoaXMuZmx1c2goKTtcbiAgICBpZiAoZGVjcnlwdFJlc3VsdCkge1xuICAgICAgcmV0dXJuIGRlY3J5cHRSZXN1bHQuYnVmZmVyO1xuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoJ1dlYkNyeXB0byBhbmQgc29mdHdhcmVEZWNyeXB0OiBmYWlsZWQgdG8gZGVjcnlwdCBkYXRhJyk7XG4gIH1cbiAgZ2V0VmFsaWRDaHVuayhkYXRhKSB7XG4gICAgbGV0IGN1cnJlbnRDaHVuayA9IGRhdGE7XG4gICAgY29uc3Qgc3BsaXRQb2ludCA9IGRhdGEubGVuZ3RoIC0gZGF0YS5sZW5ndGggJSBDSFVOS19TSVpFO1xuICAgIGlmIChzcGxpdFBvaW50ICE9PSBkYXRhLmxlbmd0aCkge1xuICAgICAgY3VycmVudENodW5rID0gc2xpY2VVaW50OChkYXRhLCAwLCBzcGxpdFBvaW50KTtcbiAgICAgIHRoaXMucmVtYWluZGVyRGF0YSA9IHNsaWNlVWludDgoZGF0YSwgc3BsaXRQb2ludCk7XG4gICAgfVxuICAgIHJldHVybiBjdXJyZW50Q2h1bms7XG4gIH1cbiAgbG9nT25jZShtc2cpIHtcbiAgICBpZiAoIXRoaXMubG9nRW5hYmxlZCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBsb2dnZXIubG9nKGBbZGVjcnlwdGVyXTogJHttc2d9YCk7XG4gICAgdGhpcy5sb2dFbmFibGVkID0gZmFsc2U7XG4gIH1cbn1cblxuLyoqXG4gKiAgVGltZVJhbmdlcyB0byBzdHJpbmcgaGVscGVyXG4gKi9cblxuY29uc3QgVGltZVJhbmdlcyA9IHtcbiAgdG9TdHJpbmc6IGZ1bmN0aW9uIChyKSB7XG4gICAgbGV0IGxvZyA9ICcnO1xuICAgIGNvbnN0IGxlbiA9IHIubGVuZ3RoO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgIGxvZyArPSBgWyR7ci5zdGFydChpKS50b0ZpeGVkKDMpfS0ke3IuZW5kKGkpLnRvRml4ZWQoMyl9XWA7XG4gICAgfVxuICAgIHJldHVybiBsb2c7XG4gIH1cbn07XG5cbmNvbnN0IFN0YXRlID0ge1xuICBTVE9QUEVEOiAnU1RPUFBFRCcsXG4gIElETEU6ICdJRExFJyxcbiAgS0VZX0xPQURJTkc6ICdLRVlfTE9BRElORycsXG4gIEZSQUdfTE9BRElORzogJ0ZSQUdfTE9BRElORycsXG4gIEZSQUdfTE9BRElOR19XQUlUSU5HX1JFVFJZOiAnRlJBR19MT0FESU5HX1dBSVRJTkdfUkVUUlknLFxuICBXQUlUSU5HX1RSQUNLOiAnV0FJVElOR19UUkFDSycsXG4gIFBBUlNJTkc6ICdQQVJTSU5HJyxcbiAgUEFSU0VEOiAnUEFSU0VEJyxcbiAgRU5ERUQ6ICdFTkRFRCcsXG4gIEVSUk9SOiAnRVJST1InLFxuICBXQUlUSU5HX0lOSVRfUFRTOiAnV0FJVElOR19JTklUX1BUUycsXG4gIFdBSVRJTkdfTEVWRUw6ICdXQUlUSU5HX0xFVkVMJ1xufTtcbmNsYXNzIEJhc2VTdHJlYW1Db250cm9sbGVyIGV4dGVuZHMgVGFza0xvb3Age1xuICBjb25zdHJ1Y3RvcihobHMsIGZyYWdtZW50VHJhY2tlciwga2V5TG9hZGVyLCBsb2dQcmVmaXgsIHBsYXlsaXN0VHlwZSkge1xuICAgIHN1cGVyKCk7XG4gICAgdGhpcy5obHMgPSB2b2lkIDA7XG4gICAgdGhpcy5mcmFnUHJldmlvdXMgPSBudWxsO1xuICAgIHRoaXMuZnJhZ0N1cnJlbnQgPSBudWxsO1xuICAgIHRoaXMuZnJhZ21lbnRUcmFja2VyID0gdm9pZCAwO1xuICAgIHRoaXMudHJhbnNtdXhlciA9IG51bGw7XG4gICAgdGhpcy5fc3RhdGUgPSBTdGF0ZS5TVE9QUEVEO1xuICAgIHRoaXMucGxheWxpc3RUeXBlID0gdm9pZCAwO1xuICAgIHRoaXMubWVkaWEgPSBudWxsO1xuICAgIHRoaXMubWVkaWFCdWZmZXIgPSBudWxsO1xuICAgIHRoaXMuY29uZmlnID0gdm9pZCAwO1xuICAgIHRoaXMuYml0cmF0ZVRlc3QgPSBmYWxzZTtcbiAgICB0aGlzLmxhc3RDdXJyZW50VGltZSA9IDA7XG4gICAgdGhpcy5uZXh0TG9hZFBvc2l0aW9uID0gMDtcbiAgICB0aGlzLnN0YXJ0UG9zaXRpb24gPSAwO1xuICAgIHRoaXMuc3RhcnRUaW1lT2Zmc2V0ID0gbnVsbDtcbiAgICB0aGlzLmxvYWRlZG1ldGFkYXRhID0gZmFsc2U7XG4gICAgdGhpcy5yZXRyeURhdGUgPSAwO1xuICAgIHRoaXMubGV2ZWxzID0gbnVsbDtcbiAgICB0aGlzLmZyYWdtZW50TG9hZGVyID0gdm9pZCAwO1xuICAgIHRoaXMua2V5TG9hZGVyID0gdm9pZCAwO1xuICAgIHRoaXMubGV2ZWxMYXN0TG9hZGVkID0gbnVsbDtcbiAgICB0aGlzLnN0YXJ0RnJhZ1JlcXVlc3RlZCA9IGZhbHNlO1xuICAgIHRoaXMuZGVjcnlwdGVyID0gdm9pZCAwO1xuICAgIHRoaXMuaW5pdFBUUyA9IFtdO1xuICAgIHRoaXMub252c2Vla2luZyA9IG51bGw7XG4gICAgdGhpcy5vbnZlbmRlZCA9IG51bGw7XG4gICAgdGhpcy5sb2dQcmVmaXggPSAnJztcbiAgICB0aGlzLmxvZyA9IHZvaWQgMDtcbiAgICB0aGlzLndhcm4gPSB2b2lkIDA7XG4gICAgdGhpcy5wbGF5bGlzdFR5cGUgPSBwbGF5bGlzdFR5cGU7XG4gICAgdGhpcy5sb2dQcmVmaXggPSBsb2dQcmVmaXg7XG4gICAgdGhpcy5sb2cgPSBsb2dnZXIubG9nLmJpbmQobG9nZ2VyLCBgJHtsb2dQcmVmaXh9OmApO1xuICAgIHRoaXMud2FybiA9IGxvZ2dlci53YXJuLmJpbmQobG9nZ2VyLCBgJHtsb2dQcmVmaXh9OmApO1xuICAgIHRoaXMuaGxzID0gaGxzO1xuICAgIHRoaXMuZnJhZ21lbnRMb2FkZXIgPSBuZXcgRnJhZ21lbnRMb2FkZXIoaGxzLmNvbmZpZyk7XG4gICAgdGhpcy5rZXlMb2FkZXIgPSBrZXlMb2FkZXI7XG4gICAgdGhpcy5mcmFnbWVudFRyYWNrZXIgPSBmcmFnbWVudFRyYWNrZXI7XG4gICAgdGhpcy5jb25maWcgPSBobHMuY29uZmlnO1xuICAgIHRoaXMuZGVjcnlwdGVyID0gbmV3IERlY3J5cHRlcihobHMuY29uZmlnKTtcbiAgICBobHMub24oRXZlbnRzLk1BTklGRVNUX0xPQURFRCwgdGhpcy5vbk1hbmlmZXN0TG9hZGVkLCB0aGlzKTtcbiAgfVxuICBkb1RpY2soKSB7XG4gICAgdGhpcy5vblRpY2tFbmQoKTtcbiAgfVxuICBvblRpY2tFbmQoKSB7fVxuXG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tdW51c2VkLXZhcnNcbiAgc3RhcnRMb2FkKHN0YXJ0UG9zaXRpb24pIHt9XG4gIHN0b3BMb2FkKCkge1xuICAgIHRoaXMuZnJhZ21lbnRMb2FkZXIuYWJvcnQoKTtcbiAgICB0aGlzLmtleUxvYWRlci5hYm9ydCh0aGlzLnBsYXlsaXN0VHlwZSk7XG4gICAgY29uc3QgZnJhZyA9IHRoaXMuZnJhZ0N1cnJlbnQ7XG4gICAgaWYgKGZyYWcgIT0gbnVsbCAmJiBmcmFnLmxvYWRlcikge1xuICAgICAgZnJhZy5hYm9ydFJlcXVlc3RzKCk7XG4gICAgICB0aGlzLmZyYWdtZW50VHJhY2tlci5yZW1vdmVGcmFnbWVudChmcmFnKTtcbiAgICB9XG4gICAgdGhpcy5yZXNldFRyYW5zbXV4ZXIoKTtcbiAgICB0aGlzLmZyYWdDdXJyZW50ID0gbnVsbDtcbiAgICB0aGlzLmZyYWdQcmV2aW91cyA9IG51bGw7XG4gICAgdGhpcy5jbGVhckludGVydmFsKCk7XG4gICAgdGhpcy5jbGVhck5leHRUaWNrKCk7XG4gICAgdGhpcy5zdGF0ZSA9IFN0YXRlLlNUT1BQRUQ7XG4gIH1cbiAgX3N0cmVhbUVuZGVkKGJ1ZmZlckluZm8sIGxldmVsRGV0YWlscykge1xuICAgIC8vIElmIHBsYXlsaXN0IGlzIGxpdmUsIHRoZXJlIGlzIGFub3RoZXIgYnVmZmVyZWQgcmFuZ2UgYWZ0ZXIgdGhlIGN1cnJlbnQgcmFuZ2UsIG5vdGhpbmcgYnVmZmVyZWQsIG1lZGlhIGlzIGRldGFjaGVkLFxuICAgIC8vIG9mIG5vdGhpbmcgbG9hZGluZy9sb2FkZWQgcmV0dXJuIGZhbHNlXG4gICAgaWYgKGxldmVsRGV0YWlscy5saXZlIHx8IGJ1ZmZlckluZm8ubmV4dFN0YXJ0IHx8ICFidWZmZXJJbmZvLmVuZCB8fCAhdGhpcy5tZWRpYSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBjb25zdCBwYXJ0TGlzdCA9IGxldmVsRGV0YWlscy5wYXJ0TGlzdDtcbiAgICAvLyBTaW5jZSB0aGUgbGFzdCBwYXJ0IGlzbid0IGd1YXJhbnRlZWQgdG8gY29ycmVzcG9uZCB0byB0aGUgbGFzdCBwbGF5bGlzdCBzZWdtZW50IGZvciBMb3ctTGF0ZW5jeSBITFMsXG4gICAgLy8gY2hlY2sgaW5zdGVhZCBpZiB0aGUgbGFzdCBwYXJ0IGlzIGJ1ZmZlcmVkLlxuICAgIGlmIChwYXJ0TGlzdCAhPSBudWxsICYmIHBhcnRMaXN0Lmxlbmd0aCkge1xuICAgICAgY29uc3QgbGFzdFBhcnQgPSBwYXJ0TGlzdFtwYXJ0TGlzdC5sZW5ndGggLSAxXTtcblxuICAgICAgLy8gQ2hlY2tpbmcgdGhlIG1pZHBvaW50IG9mIHRoZSBwYXJ0IGZvciBwb3RlbnRpYWwgbWFyZ2luIG9mIGVycm9yIGFuZCByZWxhdGVkIGlzc3Vlcy5cbiAgICAgIC8vIE5PVEU6IFRlY2huaWNhbGx5IEkgYmVsaWV2ZSBwYXJ0cyBjb3VsZCB5aWVsZCBjb250ZW50IHRoYXQgaXMgPCB0aGUgY29tcHV0ZWQgZHVyYXRpb24gKGluY2x1ZGluZyBwb3RlbnRpYWwgYSBkdXJhdGlvbiBvZiAwKVxuICAgICAgLy8gYW5kIHN0aWxsIGJlIHNwZWMtY29tcGxpYW50LCBzbyB0aGVyZSBtYXkgc3RpbGwgYmUgZWRnZSBjYXNlcyBoZXJlLiBMaWtld2lzZSwgdGhlcmUgY291bGQgYmUgaXNzdWVzIGluIGVuZCBvZiBzdHJlYW1cbiAgICAgIC8vIHBhcnQgbWlzbWF0Y2hlcyBmb3IgaW5kZXBlbmRlbnQgYXVkaW8gYW5kIHZpZGVvIHBsYXlsaXN0cy9zZWdtZW50cy5cbiAgICAgIGNvbnN0IGxhc3RQYXJ0QnVmZmVyZWQgPSBCdWZmZXJIZWxwZXIuaXNCdWZmZXJlZCh0aGlzLm1lZGlhLCBsYXN0UGFydC5zdGFydCArIGxhc3RQYXJ0LmR1cmF0aW9uIC8gMik7XG4gICAgICByZXR1cm4gbGFzdFBhcnRCdWZmZXJlZDtcbiAgICB9XG4gICAgY29uc3QgcGxheWxpc3RUeXBlID0gbGV2ZWxEZXRhaWxzLmZyYWdtZW50c1tsZXZlbERldGFpbHMuZnJhZ21lbnRzLmxlbmd0aCAtIDFdLnR5cGU7XG4gICAgcmV0dXJuIHRoaXMuZnJhZ21lbnRUcmFja2VyLmlzRW5kTGlzdEFwcGVuZGVkKHBsYXlsaXN0VHlwZSk7XG4gIH1cbiAgZ2V0TGV2ZWxEZXRhaWxzKCkge1xuICAgIGlmICh0aGlzLmxldmVscyAmJiB0aGlzLmxldmVsTGFzdExvYWRlZCAhPT0gbnVsbCkge1xuICAgICAgdmFyIF90aGlzJGxldmVsTGFzdExvYWRlZDtcbiAgICAgIHJldHVybiAoX3RoaXMkbGV2ZWxMYXN0TG9hZGVkID0gdGhpcy5sZXZlbExhc3RMb2FkZWQpID09IG51bGwgPyB2b2lkIDAgOiBfdGhpcyRsZXZlbExhc3RMb2FkZWQuZGV0YWlscztcbiAgICB9XG4gIH1cbiAgb25NZWRpYUF0dGFjaGVkKGV2ZW50LCBkYXRhKSB7XG4gICAgY29uc3QgbWVkaWEgPSB0aGlzLm1lZGlhID0gdGhpcy5tZWRpYUJ1ZmZlciA9IGRhdGEubWVkaWE7XG4gICAgdGhpcy5vbnZzZWVraW5nID0gdGhpcy5vbk1lZGlhU2Vla2luZy5iaW5kKHRoaXMpO1xuICAgIHRoaXMub252ZW5kZWQgPSB0aGlzLm9uTWVkaWFFbmRlZC5iaW5kKHRoaXMpO1xuICAgIG1lZGlhLmFkZEV2ZW50TGlzdGVuZXIoJ3NlZWtpbmcnLCB0aGlzLm9udnNlZWtpbmcpO1xuICAgIG1lZGlhLmFkZEV2ZW50TGlzdGVuZXIoJ2VuZGVkJywgdGhpcy5vbnZlbmRlZCk7XG4gICAgY29uc3QgY29uZmlnID0gdGhpcy5jb25maWc7XG4gICAgaWYgKHRoaXMubGV2ZWxzICYmIGNvbmZpZy5hdXRvU3RhcnRMb2FkICYmIHRoaXMuc3RhdGUgPT09IFN0YXRlLlNUT1BQRUQpIHtcbiAgICAgIHRoaXMuc3RhcnRMb2FkKGNvbmZpZy5zdGFydFBvc2l0aW9uKTtcbiAgICB9XG4gIH1cbiAgb25NZWRpYURldGFjaGluZygpIHtcbiAgICBjb25zdCBtZWRpYSA9IHRoaXMubWVkaWE7XG4gICAgaWYgKG1lZGlhICE9IG51bGwgJiYgbWVkaWEuZW5kZWQpIHtcbiAgICAgIHRoaXMubG9nKCdNU0UgZGV0YWNoaW5nIGFuZCB2aWRlbyBlbmRlZCwgcmVzZXQgc3RhcnRQb3NpdGlvbicpO1xuICAgICAgdGhpcy5zdGFydFBvc2l0aW9uID0gdGhpcy5sYXN0Q3VycmVudFRpbWUgPSAwO1xuICAgIH1cblxuICAgIC8vIHJlbW92ZSB2aWRlbyBsaXN0ZW5lcnNcbiAgICBpZiAobWVkaWEgJiYgdGhpcy5vbnZzZWVraW5nICYmIHRoaXMub252ZW5kZWQpIHtcbiAgICAgIG1lZGlhLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3NlZWtpbmcnLCB0aGlzLm9udnNlZWtpbmcpO1xuICAgICAgbWVkaWEucmVtb3ZlRXZlbnRMaXN0ZW5lcignZW5kZWQnLCB0aGlzLm9udmVuZGVkKTtcbiAgICAgIHRoaXMub252c2Vla2luZyA9IHRoaXMub252ZW5kZWQgPSBudWxsO1xuICAgIH1cbiAgICBpZiAodGhpcy5rZXlMb2FkZXIpIHtcbiAgICAgIHRoaXMua2V5TG9hZGVyLmRldGFjaCgpO1xuICAgIH1cbiAgICB0aGlzLm1lZGlhID0gdGhpcy5tZWRpYUJ1ZmZlciA9IG51bGw7XG4gICAgdGhpcy5sb2FkZWRtZXRhZGF0YSA9IGZhbHNlO1xuICAgIHRoaXMuZnJhZ21lbnRUcmFja2VyLnJlbW92ZUFsbEZyYWdtZW50cygpO1xuICAgIHRoaXMuc3RvcExvYWQoKTtcbiAgfVxuICBvbk1lZGlhU2Vla2luZygpIHtcbiAgICBjb25zdCB7XG4gICAgICBjb25maWcsXG4gICAgICBmcmFnQ3VycmVudCxcbiAgICAgIG1lZGlhLFxuICAgICAgbWVkaWFCdWZmZXIsXG4gICAgICBzdGF0ZVxuICAgIH0gPSB0aGlzO1xuICAgIGNvbnN0IGN1cnJlbnRUaW1lID0gbWVkaWEgPyBtZWRpYS5jdXJyZW50VGltZSA6IDA7XG4gICAgY29uc3QgYnVmZmVySW5mbyA9IEJ1ZmZlckhlbHBlci5idWZmZXJJbmZvKG1lZGlhQnVmZmVyID8gbWVkaWFCdWZmZXIgOiBtZWRpYSwgY3VycmVudFRpbWUsIGNvbmZpZy5tYXhCdWZmZXJIb2xlKTtcbiAgICB0aGlzLmxvZyhgbWVkaWEgc2Vla2luZyB0byAke2lzRmluaXRlTnVtYmVyKGN1cnJlbnRUaW1lKSA/IGN1cnJlbnRUaW1lLnRvRml4ZWQoMykgOiBjdXJyZW50VGltZX0sIHN0YXRlOiAke3N0YXRlfWApO1xuICAgIGlmICh0aGlzLnN0YXRlID09PSBTdGF0ZS5FTkRFRCkge1xuICAgICAgdGhpcy5yZXNldExvYWRpbmdTdGF0ZSgpO1xuICAgIH0gZWxzZSBpZiAoZnJhZ0N1cnJlbnQpIHtcbiAgICAgIC8vIFNlZWtpbmcgd2hpbGUgZnJhZyBsb2FkIGlzIGluIHByb2dyZXNzXG4gICAgICBjb25zdCB0b2xlcmFuY2UgPSBjb25maWcubWF4RnJhZ0xvb2tVcFRvbGVyYW5jZTtcbiAgICAgIGNvbnN0IGZyYWdTdGFydE9mZnNldCA9IGZyYWdDdXJyZW50LnN0YXJ0IC0gdG9sZXJhbmNlO1xuICAgICAgY29uc3QgZnJhZ0VuZE9mZnNldCA9IGZyYWdDdXJyZW50LnN0YXJ0ICsgZnJhZ0N1cnJlbnQuZHVyYXRpb24gKyB0b2xlcmFuY2U7XG4gICAgICAvLyBpZiBzZWVraW5nIG91dCBvZiBidWZmZXJlZCByYW5nZSBvciBpbnRvIG5ldyBvbmVcbiAgICAgIGlmICghYnVmZmVySW5mby5sZW4gfHwgZnJhZ0VuZE9mZnNldCA8IGJ1ZmZlckluZm8uc3RhcnQgfHwgZnJhZ1N0YXJ0T2Zmc2V0ID4gYnVmZmVySW5mby5lbmQpIHtcbiAgICAgICAgY29uc3QgcGFzdEZyYWdtZW50ID0gY3VycmVudFRpbWUgPiBmcmFnRW5kT2Zmc2V0O1xuICAgICAgICAvLyBpZiB0aGUgc2VlayBwb3NpdGlvbiBpcyBvdXRzaWRlIHRoZSBjdXJyZW50IGZyYWdtZW50IHJhbmdlXG4gICAgICAgIGlmIChjdXJyZW50VGltZSA8IGZyYWdTdGFydE9mZnNldCB8fCBwYXN0RnJhZ21lbnQpIHtcbiAgICAgICAgICBpZiAocGFzdEZyYWdtZW50ICYmIGZyYWdDdXJyZW50LmxvYWRlcikge1xuICAgICAgICAgICAgdGhpcy5sb2coJ3NlZWtpbmcgb3V0c2lkZSBvZiBidWZmZXIgd2hpbGUgZnJhZ21lbnQgbG9hZCBpbiBwcm9ncmVzcywgY2FuY2VsIGZyYWdtZW50IGxvYWQnKTtcbiAgICAgICAgICAgIGZyYWdDdXJyZW50LmFib3J0UmVxdWVzdHMoKTtcbiAgICAgICAgICAgIHRoaXMucmVzZXRMb2FkaW5nU3RhdGUoKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgdGhpcy5mcmFnUHJldmlvdXMgPSBudWxsO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIGlmIChtZWRpYSkge1xuICAgICAgLy8gUmVtb3ZlIGdhcCBmcmFnbWVudHNcbiAgICAgIHRoaXMuZnJhZ21lbnRUcmFja2VyLnJlbW92ZUZyYWdtZW50c0luUmFuZ2UoY3VycmVudFRpbWUsIEluZmluaXR5LCB0aGlzLnBsYXlsaXN0VHlwZSwgdHJ1ZSk7XG4gICAgICB0aGlzLmxhc3RDdXJyZW50VGltZSA9IGN1cnJlbnRUaW1lO1xuICAgIH1cblxuICAgIC8vIGluIGNhc2Ugc2Vla2luZyBvY2N1cnMgYWx0aG91Z2ggbm8gbWVkaWEgYnVmZmVyZWQsIGFkanVzdCBzdGFydFBvc2l0aW9uIGFuZCBuZXh0TG9hZFBvc2l0aW9uIHRvIHNlZWsgdGFyZ2V0XG4gICAgaWYgKCF0aGlzLmxvYWRlZG1ldGFkYXRhICYmICFidWZmZXJJbmZvLmxlbikge1xuICAgICAgdGhpcy5uZXh0TG9hZFBvc2l0aW9uID0gdGhpcy5zdGFydFBvc2l0aW9uID0gY3VycmVudFRpbWU7XG4gICAgfVxuXG4gICAgLy8gQXN5bmMgdGljayB0byBzcGVlZCB1cCBwcm9jZXNzaW5nXG4gICAgdGhpcy50aWNrSW1tZWRpYXRlKCk7XG4gIH1cbiAgb25NZWRpYUVuZGVkKCkge1xuICAgIC8vIHJlc2V0IHN0YXJ0UG9zaXRpb24gYW5kIGxhc3RDdXJyZW50VGltZSB0byByZXN0YXJ0IHBsYXliYWNrIEAgc3RyZWFtIGJlZ2lubmluZ1xuICAgIHRoaXMuc3RhcnRQb3NpdGlvbiA9IHRoaXMubGFzdEN1cnJlbnRUaW1lID0gMDtcbiAgfVxuICBvbk1hbmlmZXN0TG9hZGVkKGV2ZW50LCBkYXRhKSB7XG4gICAgdGhpcy5zdGFydFRpbWVPZmZzZXQgPSBkYXRhLnN0YXJ0VGltZU9mZnNldDtcbiAgICB0aGlzLmluaXRQVFMgPSBbXTtcbiAgfVxuICBvbkhhbmRsZXJEZXN0cm95aW5nKCkge1xuICAgIHRoaXMuaGxzLm9mZihFdmVudHMuTUFOSUZFU1RfTE9BREVELCB0aGlzLm9uTWFuaWZlc3RMb2FkZWQsIHRoaXMpO1xuICAgIHRoaXMuc3RvcExvYWQoKTtcbiAgICBzdXBlci5vbkhhbmRsZXJEZXN0cm95aW5nKCk7XG4gICAgLy8gQHRzLWlnbm9yZVxuICAgIHRoaXMuaGxzID0gbnVsbDtcbiAgfVxuICBvbkhhbmRsZXJEZXN0cm95ZWQoKSB7XG4gICAgdGhpcy5zdGF0ZSA9IFN0YXRlLlNUT1BQRUQ7XG4gICAgaWYgKHRoaXMuZnJhZ21lbnRMb2FkZXIpIHtcbiAgICAgIHRoaXMuZnJhZ21lbnRMb2FkZXIuZGVzdHJveSgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5rZXlMb2FkZXIpIHtcbiAgICAgIHRoaXMua2V5TG9hZGVyLmRlc3Ryb3koKTtcbiAgICB9XG4gICAgaWYgKHRoaXMuZGVjcnlwdGVyKSB7XG4gICAgICB0aGlzLmRlY3J5cHRlci5kZXN0cm95KCk7XG4gICAgfVxuICAgIHRoaXMuaGxzID0gdGhpcy5sb2cgPSB0aGlzLndhcm4gPSB0aGlzLmRlY3J5cHRlciA9IHRoaXMua2V5TG9hZGVyID0gdGhpcy5mcmFnbWVudExvYWRlciA9IHRoaXMuZnJhZ21lbnRUcmFja2VyID0gbnVsbDtcbiAgICBzdXBlci5vbkhhbmRsZXJEZXN0cm95ZWQoKTtcbiAgfVxuICBsb2FkRnJhZ21lbnQoZnJhZywgbGV2ZWwsIHRhcmdldEJ1ZmZlclRpbWUpIHtcbiAgICB0aGlzLl9sb2FkRnJhZ0ZvclBsYXliYWNrKGZyYWcsIGxldmVsLCB0YXJnZXRCdWZmZXJUaW1lKTtcbiAgfVxuICBfbG9hZEZyYWdGb3JQbGF5YmFjayhmcmFnLCBsZXZlbCwgdGFyZ2V0QnVmZmVyVGltZSkge1xuICAgIGNvbnN0IHByb2dyZXNzQ2FsbGJhY2sgPSBkYXRhID0+IHtcbiAgICAgIGlmICh0aGlzLmZyYWdDb250ZXh0Q2hhbmdlZChmcmFnKSkge1xuICAgICAgICB0aGlzLndhcm4oYEZyYWdtZW50ICR7ZnJhZy5zbn0ke2RhdGEucGFydCA/ICcgcDogJyArIGRhdGEucGFydC5pbmRleCA6ICcnfSBvZiBsZXZlbCAke2ZyYWcubGV2ZWx9IHdhcyBkcm9wcGVkIGR1cmluZyBkb3dubG9hZC5gKTtcbiAgICAgICAgdGhpcy5mcmFnbWVudFRyYWNrZXIucmVtb3ZlRnJhZ21lbnQoZnJhZyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGZyYWcuc3RhdHMuY2h1bmtDb3VudCsrO1xuICAgICAgdGhpcy5faGFuZGxlRnJhZ21lbnRMb2FkUHJvZ3Jlc3MoZGF0YSk7XG4gICAgfTtcbiAgICB0aGlzLl9kb0ZyYWdMb2FkKGZyYWcsIGxldmVsLCB0YXJnZXRCdWZmZXJUaW1lLCBwcm9ncmVzc0NhbGxiYWNrKS50aGVuKGRhdGEgPT4ge1xuICAgICAgaWYgKCFkYXRhKSB7XG4gICAgICAgIC8vIGlmIHdlJ3JlIGhlcmUgd2UgcHJvYmFibHkgbmVlZGVkIHRvIGJhY2t0cmFjayBvciBhcmUgd2FpdGluZyBmb3IgbW9yZSBwYXJ0c1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjb25zdCBzdGF0ZSA9IHRoaXMuc3RhdGU7XG4gICAgICBpZiAodGhpcy5mcmFnQ29udGV4dENoYW5nZWQoZnJhZykpIHtcbiAgICAgICAgaWYgKHN0YXRlID09PSBTdGF0ZS5GUkFHX0xPQURJTkcgfHwgIXRoaXMuZnJhZ0N1cnJlbnQgJiYgc3RhdGUgPT09IFN0YXRlLlBBUlNJTkcpIHtcbiAgICAgICAgICB0aGlzLmZyYWdtZW50VHJhY2tlci5yZW1vdmVGcmFnbWVudChmcmFnKTtcbiAgICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBpZiAoJ3BheWxvYWQnIGluIGRhdGEpIHtcbiAgICAgICAgdGhpcy5sb2coYExvYWRlZCBmcmFnbWVudCAke2ZyYWcuc259IG9mIGxldmVsICR7ZnJhZy5sZXZlbH1gKTtcbiAgICAgICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuRlJBR19MT0FERUQsIGRhdGEpO1xuICAgICAgfVxuXG4gICAgICAvLyBQYXNzIHRocm91Z2ggdGhlIHdob2xlIHBheWxvYWQ7IGNvbnRyb2xsZXJzIG5vdCBpbXBsZW1lbnRpbmcgcHJvZ3Jlc3NpdmUgbG9hZGluZyByZWNlaXZlIGRhdGEgZnJvbSB0aGlzIGNhbGxiYWNrXG4gICAgICB0aGlzLl9oYW5kbGVGcmFnbWVudExvYWRDb21wbGV0ZShkYXRhKTtcbiAgICB9KS5jYXRjaChyZWFzb24gPT4ge1xuICAgICAgaWYgKHRoaXMuc3RhdGUgPT09IFN0YXRlLlNUT1BQRUQgfHwgdGhpcy5zdGF0ZSA9PT0gU3RhdGUuRVJST1IpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdGhpcy53YXJuKGBGcmFnIGVycm9yOiAkeyhyZWFzb24gPT0gbnVsbCA/IHZvaWQgMCA6IHJlYXNvbi5tZXNzYWdlKSB8fCByZWFzb259YCk7XG4gICAgICB0aGlzLnJlc2V0RnJhZ21lbnRMb2FkaW5nKGZyYWcpO1xuICAgIH0pO1xuICB9XG4gIGNsZWFyVHJhY2tlcklmTmVlZGVkKGZyYWcpIHtcbiAgICB2YXIgX3RoaXMkbWVkaWFCdWZmZXI7XG4gICAgY29uc3Qge1xuICAgICAgZnJhZ21lbnRUcmFja2VyXG4gICAgfSA9IHRoaXM7XG4gICAgY29uc3QgZnJhZ1N0YXRlID0gZnJhZ21lbnRUcmFja2VyLmdldFN0YXRlKGZyYWcpO1xuICAgIGlmIChmcmFnU3RhdGUgPT09IEZyYWdtZW50U3RhdGUuQVBQRU5ESU5HKSB7XG4gICAgICAvLyBMb3dlciB0aGUgbWF4IGJ1ZmZlciBsZW5ndGggYW5kIHRyeSBhZ2FpblxuICAgICAgY29uc3QgcGxheWxpc3RUeXBlID0gZnJhZy50eXBlO1xuICAgICAgY29uc3QgYnVmZmVyZWRJbmZvID0gdGhpcy5nZXRGd2RCdWZmZXJJbmZvKHRoaXMubWVkaWFCdWZmZXIsIHBsYXlsaXN0VHlwZSk7XG4gICAgICBjb25zdCBtaW5Gb3J3YXJkQnVmZmVyTGVuZ3RoID0gTWF0aC5tYXgoZnJhZy5kdXJhdGlvbiwgYnVmZmVyZWRJbmZvID8gYnVmZmVyZWRJbmZvLmxlbiA6IHRoaXMuY29uZmlnLm1heEJ1ZmZlckxlbmd0aCk7XG4gICAgICAvLyBJZiBiYWNrdHJhY2tpbmcsIGFsd2F5cyByZW1vdmUgZnJvbSB0aGUgdHJhY2tlciB3aXRob3V0IHJlZHVjaW5nIG1heCBidWZmZXIgbGVuZ3RoXG4gICAgICBjb25zdCBiYWNrdHJhY2tGcmFnbWVudCA9IHRoaXMuYmFja3RyYWNrRnJhZ21lbnQ7XG4gICAgICBjb25zdCBiYWNrdHJhY2tlZCA9IGJhY2t0cmFja0ZyYWdtZW50ID8gZnJhZy5zbiAtIGJhY2t0cmFja0ZyYWdtZW50LnNuIDogMDtcbiAgICAgIGlmIChiYWNrdHJhY2tlZCA9PT0gMSB8fCB0aGlzLnJlZHVjZU1heEJ1ZmZlckxlbmd0aChtaW5Gb3J3YXJkQnVmZmVyTGVuZ3RoLCBmcmFnLmR1cmF0aW9uKSkge1xuICAgICAgICBmcmFnbWVudFRyYWNrZXIucmVtb3ZlRnJhZ21lbnQoZnJhZyk7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmICgoKF90aGlzJG1lZGlhQnVmZmVyID0gdGhpcy5tZWRpYUJ1ZmZlcikgPT0gbnVsbCA/IHZvaWQgMCA6IF90aGlzJG1lZGlhQnVmZmVyLmJ1ZmZlcmVkLmxlbmd0aCkgPT09IDApIHtcbiAgICAgIC8vIFN0b3AgZ2FwIGZvciBiYWQgdHJhY2tlciAvIGJ1ZmZlciBmbHVzaCBiZWhhdmlvclxuICAgICAgZnJhZ21lbnRUcmFja2VyLnJlbW92ZUFsbEZyYWdtZW50cygpO1xuICAgIH0gZWxzZSBpZiAoZnJhZ21lbnRUcmFja2VyLmhhc1BhcnRzKGZyYWcudHlwZSkpIHtcbiAgICAgIC8vIEluIGxvdyBsYXRlbmN5IG1vZGUsIHJlbW92ZSBmcmFnbWVudHMgZm9yIHdoaWNoIG9ubHkgc29tZSBwYXJ0cyB3ZXJlIGJ1ZmZlcmVkXG4gICAgICBmcmFnbWVudFRyYWNrZXIuZGV0ZWN0UGFydGlhbEZyYWdtZW50cyh7XG4gICAgICAgIGZyYWcsXG4gICAgICAgIHBhcnQ6IG51bGwsXG4gICAgICAgIHN0YXRzOiBmcmFnLnN0YXRzLFxuICAgICAgICBpZDogZnJhZy50eXBlXG4gICAgICB9KTtcbiAgICAgIGlmIChmcmFnbWVudFRyYWNrZXIuZ2V0U3RhdGUoZnJhZykgPT09IEZyYWdtZW50U3RhdGUuUEFSVElBTCkge1xuICAgICAgICBmcmFnbWVudFRyYWNrZXIucmVtb3ZlRnJhZ21lbnQoZnJhZyk7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIGNoZWNrTGl2ZVVwZGF0ZShkZXRhaWxzKSB7XG4gICAgaWYgKGRldGFpbHMudXBkYXRlZCAmJiAhZGV0YWlscy5saXZlKSB7XG4gICAgICAvLyBMaXZlIHN0cmVhbSBlbmRlZCwgdXBkYXRlIGZyYWdtZW50IHRyYWNrZXJcbiAgICAgIGNvbnN0IGxhc3RGcmFnbWVudCA9IGRldGFpbHMuZnJhZ21lbnRzW2RldGFpbHMuZnJhZ21lbnRzLmxlbmd0aCAtIDFdO1xuICAgICAgdGhpcy5mcmFnbWVudFRyYWNrZXIuZGV0ZWN0UGFydGlhbEZyYWdtZW50cyh7XG4gICAgICAgIGZyYWc6IGxhc3RGcmFnbWVudCxcbiAgICAgICAgcGFydDogbnVsbCxcbiAgICAgICAgc3RhdHM6IGxhc3RGcmFnbWVudC5zdGF0cyxcbiAgICAgICAgaWQ6IGxhc3RGcmFnbWVudC50eXBlXG4gICAgICB9KTtcbiAgICB9XG4gICAgaWYgKCFkZXRhaWxzLmZyYWdtZW50c1swXSkge1xuICAgICAgZGV0YWlscy5kZWx0YVVwZGF0ZUZhaWxlZCA9IHRydWU7XG4gICAgfVxuICB9XG4gIGZsdXNoTWFpbkJ1ZmZlcihzdGFydE9mZnNldCwgZW5kT2Zmc2V0LCB0eXBlID0gbnVsbCkge1xuICAgIGlmICghKHN0YXJ0T2Zmc2V0IC0gZW5kT2Zmc2V0KSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyBXaGVuIGFsdGVybmF0ZSBhdWRpbyBpcyBwbGF5aW5nLCB0aGUgYXVkaW8tc3RyZWFtLWNvbnRyb2xsZXIgaXMgcmVzcG9uc2libGUgZm9yIHRoZSBhdWRpbyBidWZmZXIuIE90aGVyd2lzZSxcbiAgICAvLyBwYXNzaW5nIGEgbnVsbCB0eXBlIGZsdXNoZXMgYm90aCBidWZmZXJzXG4gICAgY29uc3QgZmx1c2hTY29wZSA9IHtcbiAgICAgIHN0YXJ0T2Zmc2V0LFxuICAgICAgZW5kT2Zmc2V0LFxuICAgICAgdHlwZVxuICAgIH07XG4gICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuQlVGRkVSX0ZMVVNISU5HLCBmbHVzaFNjb3BlKTtcbiAgfVxuICBfbG9hZEluaXRTZWdtZW50KGZyYWcsIGxldmVsKSB7XG4gICAgdGhpcy5fZG9GcmFnTG9hZChmcmFnLCBsZXZlbCkudGhlbihkYXRhID0+IHtcbiAgICAgIGlmICghZGF0YSB8fCB0aGlzLmZyYWdDb250ZXh0Q2hhbmdlZChmcmFnKSB8fCAhdGhpcy5sZXZlbHMpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdpbml0IGxvYWQgYWJvcnRlZCcpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGRhdGE7XG4gICAgfSkudGhlbihkYXRhID0+IHtcbiAgICAgIGNvbnN0IHtcbiAgICAgICAgaGxzXG4gICAgICB9ID0gdGhpcztcbiAgICAgIGNvbnN0IHtcbiAgICAgICAgcGF5bG9hZFxuICAgICAgfSA9IGRhdGE7XG4gICAgICBjb25zdCBkZWNyeXB0RGF0YSA9IGZyYWcuZGVjcnlwdGRhdGE7XG5cbiAgICAgIC8vIGNoZWNrIHRvIHNlZSBpZiB0aGUgcGF5bG9hZCBuZWVkcyB0byBiZSBkZWNyeXB0ZWRcbiAgICAgIGlmIChwYXlsb2FkICYmIHBheWxvYWQuYnl0ZUxlbmd0aCA+IDAgJiYgZGVjcnlwdERhdGEgIT0gbnVsbCAmJiBkZWNyeXB0RGF0YS5rZXkgJiYgZGVjcnlwdERhdGEuaXYgJiYgZGVjcnlwdERhdGEubWV0aG9kID09PSAnQUVTLTEyOCcpIHtcbiAgICAgICAgY29uc3Qgc3RhcnRUaW1lID0gc2VsZi5wZXJmb3JtYW5jZS5ub3coKTtcbiAgICAgICAgLy8gZGVjcnlwdCBpbml0IHNlZ21lbnQgZGF0YVxuICAgICAgICByZXR1cm4gdGhpcy5kZWNyeXB0ZXIuZGVjcnlwdChuZXcgVWludDhBcnJheShwYXlsb2FkKSwgZGVjcnlwdERhdGEua2V5LmJ1ZmZlciwgZGVjcnlwdERhdGEuaXYuYnVmZmVyKS5jYXRjaChlcnIgPT4ge1xuICAgICAgICAgIGhscy50cmlnZ2VyKEV2ZW50cy5FUlJPUiwge1xuICAgICAgICAgICAgdHlwZTogRXJyb3JUeXBlcy5NRURJQV9FUlJPUixcbiAgICAgICAgICAgIGRldGFpbHM6IEVycm9yRGV0YWlscy5GUkFHX0RFQ1JZUFRfRVJST1IsXG4gICAgICAgICAgICBmYXRhbDogZmFsc2UsXG4gICAgICAgICAgICBlcnJvcjogZXJyLFxuICAgICAgICAgICAgcmVhc29uOiBlcnIubWVzc2FnZSxcbiAgICAgICAgICAgIGZyYWdcbiAgICAgICAgICB9KTtcbiAgICAgICAgICB0aHJvdyBlcnI7XG4gICAgICAgIH0pLnRoZW4oZGVjcnlwdGVkRGF0YSA9PiB7XG4gICAgICAgICAgY29uc3QgZW5kVGltZSA9IHNlbGYucGVyZm9ybWFuY2Uubm93KCk7XG4gICAgICAgICAgaGxzLnRyaWdnZXIoRXZlbnRzLkZSQUdfREVDUllQVEVELCB7XG4gICAgICAgICAgICBmcmFnLFxuICAgICAgICAgICAgcGF5bG9hZDogZGVjcnlwdGVkRGF0YSxcbiAgICAgICAgICAgIHN0YXRzOiB7XG4gICAgICAgICAgICAgIHRzdGFydDogc3RhcnRUaW1lLFxuICAgICAgICAgICAgICB0ZGVjcnlwdDogZW5kVGltZVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIGRhdGEucGF5bG9hZCA9IGRlY3J5cHRlZERhdGE7XG4gICAgICAgICAgcmV0dXJuIHRoaXMuY29tcGxldGVJbml0U2VnbWVudExvYWQoZGF0YSk7XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMuY29tcGxldGVJbml0U2VnbWVudExvYWQoZGF0YSk7XG4gICAgfSkuY2F0Y2gocmVhc29uID0+IHtcbiAgICAgIGlmICh0aGlzLnN0YXRlID09PSBTdGF0ZS5TVE9QUEVEIHx8IHRoaXMuc3RhdGUgPT09IFN0YXRlLkVSUk9SKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHRoaXMud2FybihyZWFzb24pO1xuICAgICAgdGhpcy5yZXNldEZyYWdtZW50TG9hZGluZyhmcmFnKTtcbiAgICB9KTtcbiAgfVxuICBjb21wbGV0ZUluaXRTZWdtZW50TG9hZChkYXRhKSB7XG4gICAgY29uc3Qge1xuICAgICAgbGV2ZWxzXG4gICAgfSA9IHRoaXM7XG4gICAgaWYgKCFsZXZlbHMpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignaW5pdCBsb2FkIGFib3J0ZWQsIG1pc3NpbmcgbGV2ZWxzJyk7XG4gICAgfVxuICAgIGNvbnN0IHN0YXRzID0gZGF0YS5mcmFnLnN0YXRzO1xuICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgIGRhdGEuZnJhZy5kYXRhID0gbmV3IFVpbnQ4QXJyYXkoZGF0YS5wYXlsb2FkKTtcbiAgICBzdGF0cy5wYXJzaW5nLnN0YXJ0ID0gc3RhdHMuYnVmZmVyaW5nLnN0YXJ0ID0gc2VsZi5wZXJmb3JtYW5jZS5ub3coKTtcbiAgICBzdGF0cy5wYXJzaW5nLmVuZCA9IHN0YXRzLmJ1ZmZlcmluZy5lbmQgPSBzZWxmLnBlcmZvcm1hbmNlLm5vdygpO1xuICAgIHRoaXMudGljaygpO1xuICB9XG4gIGZyYWdDb250ZXh0Q2hhbmdlZChmcmFnKSB7XG4gICAgY29uc3Qge1xuICAgICAgZnJhZ0N1cnJlbnRcbiAgICB9ID0gdGhpcztcbiAgICByZXR1cm4gIWZyYWcgfHwgIWZyYWdDdXJyZW50IHx8IGZyYWcuc24gIT09IGZyYWdDdXJyZW50LnNuIHx8IGZyYWcubGV2ZWwgIT09IGZyYWdDdXJyZW50LmxldmVsO1xuICB9XG4gIGZyYWdCdWZmZXJlZENvbXBsZXRlKGZyYWcsIHBhcnQpIHtcbiAgICB2YXIgX2ZyYWckc3RhcnRQVFMsIF9mcmFnJGVuZFBUUywgX3RoaXMkZnJhZ0N1cnJlbnQsIF90aGlzJGZyYWdQcmV2aW91cztcbiAgICBjb25zdCBtZWRpYSA9IHRoaXMubWVkaWFCdWZmZXIgPyB0aGlzLm1lZGlhQnVmZmVyIDogdGhpcy5tZWRpYTtcbiAgICB0aGlzLmxvZyhgQnVmZmVyZWQgJHtmcmFnLnR5cGV9IHNuOiAke2ZyYWcuc259JHtwYXJ0ID8gJyBwYXJ0OiAnICsgcGFydC5pbmRleCA6ICcnfSBvZiAke3RoaXMucGxheWxpc3RUeXBlID09PSBQbGF5bGlzdExldmVsVHlwZS5NQUlOID8gJ2xldmVsJyA6ICd0cmFjayd9ICR7ZnJhZy5sZXZlbH0gKGZyYWc6WyR7KChfZnJhZyRzdGFydFBUUyA9IGZyYWcuc3RhcnRQVFMpICE9IG51bGwgPyBfZnJhZyRzdGFydFBUUyA6IE5hTikudG9GaXhlZCgzKX0tJHsoKF9mcmFnJGVuZFBUUyA9IGZyYWcuZW5kUFRTKSAhPSBudWxsID8gX2ZyYWckZW5kUFRTIDogTmFOKS50b0ZpeGVkKDMpfV0gPiBidWZmZXI6JHttZWRpYSA/IFRpbWVSYW5nZXMudG9TdHJpbmcoQnVmZmVySGVscGVyLmdldEJ1ZmZlcmVkKG1lZGlhKSkgOiAnKGRldGFjaGVkKSd9KWApO1xuICAgIGlmIChmcmFnLnNuICE9PSAnaW5pdFNlZ21lbnQnKSB7XG4gICAgICB2YXIgX3RoaXMkbGV2ZWxzO1xuICAgICAgaWYgKGZyYWcudHlwZSAhPT0gUGxheWxpc3RMZXZlbFR5cGUuU1VCVElUTEUpIHtcbiAgICAgICAgY29uc3QgZWwgPSBmcmFnLmVsZW1lbnRhcnlTdHJlYW1zO1xuICAgICAgICBpZiAoIU9iamVjdC5rZXlzKGVsKS5zb21lKHR5cGUgPT4gISFlbFt0eXBlXSkpIHtcbiAgICAgICAgICAvLyBlbXB0eSBzZWdtZW50XG4gICAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBjb25zdCBsZXZlbCA9IChfdGhpcyRsZXZlbHMgPSB0aGlzLmxldmVscykgPT0gbnVsbCA/IHZvaWQgMCA6IF90aGlzJGxldmVsc1tmcmFnLmxldmVsXTtcbiAgICAgIGlmIChsZXZlbCAhPSBudWxsICYmIGxldmVsLmZyYWdtZW50RXJyb3IpIHtcbiAgICAgICAgdGhpcy5sb2coYFJlc2V0dGluZyBsZXZlbCBmcmFnbWVudCBlcnJvciBjb3VudCBvZiAke2xldmVsLmZyYWdtZW50RXJyb3J9IG9uIGZyYWcgYnVmZmVyZWRgKTtcbiAgICAgICAgbGV2ZWwuZnJhZ21lbnRFcnJvciA9IDA7XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgIGlmICghbWVkaWEpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKCF0aGlzLmxvYWRlZG1ldGFkYXRhICYmIGZyYWcudHlwZSA9PSBQbGF5bGlzdExldmVsVHlwZS5NQUlOICYmIG1lZGlhLmJ1ZmZlcmVkLmxlbmd0aCAmJiAoKF90aGlzJGZyYWdDdXJyZW50ID0gdGhpcy5mcmFnQ3VycmVudCkgPT0gbnVsbCA/IHZvaWQgMCA6IF90aGlzJGZyYWdDdXJyZW50LnNuKSA9PT0gKChfdGhpcyRmcmFnUHJldmlvdXMgPSB0aGlzLmZyYWdQcmV2aW91cykgPT0gbnVsbCA/IHZvaWQgMCA6IF90aGlzJGZyYWdQcmV2aW91cy5zbikpIHtcbiAgICAgIHRoaXMubG9hZGVkbWV0YWRhdGEgPSB0cnVlO1xuICAgICAgdGhpcy5zZWVrVG9TdGFydFBvcygpO1xuICAgIH1cbiAgICB0aGlzLnRpY2soKTtcbiAgfVxuICBzZWVrVG9TdGFydFBvcygpIHt9XG4gIF9oYW5kbGVGcmFnbWVudExvYWRDb21wbGV0ZShmcmFnTG9hZGVkRW5kRGF0YSkge1xuICAgIGNvbnN0IHtcbiAgICAgIHRyYW5zbXV4ZXJcbiAgICB9ID0gdGhpcztcbiAgICBpZiAoIXRyYW5zbXV4ZXIpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3Qge1xuICAgICAgZnJhZyxcbiAgICAgIHBhcnQsXG4gICAgICBwYXJ0c0xvYWRlZFxuICAgIH0gPSBmcmFnTG9hZGVkRW5kRGF0YTtcbiAgICAvLyBJZiB3ZSBkaWQgbm90IGxvYWQgcGFydHMsIG9yIGxvYWRlZCBhbGwgcGFydHMsIHdlIGhhdmUgY29tcGxldGUgKG5vdCBwYXJ0aWFsKSBmcmFnbWVudCBkYXRhXG4gICAgY29uc3QgY29tcGxldGUgPSAhcGFydHNMb2FkZWQgfHwgcGFydHNMb2FkZWQubGVuZ3RoID09PSAwIHx8IHBhcnRzTG9hZGVkLnNvbWUoZnJhZ0xvYWRlZCA9PiAhZnJhZ0xvYWRlZCk7XG4gICAgY29uc3QgY2h1bmtNZXRhID0gbmV3IENodW5rTWV0YWRhdGEoZnJhZy5sZXZlbCwgZnJhZy5zbiwgZnJhZy5zdGF0cy5jaHVua0NvdW50ICsgMSwgMCwgcGFydCA/IHBhcnQuaW5kZXggOiAtMSwgIWNvbXBsZXRlKTtcbiAgICB0cmFuc211eGVyLmZsdXNoKGNodW5rTWV0YSk7XG4gIH1cblxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXVudXNlZC12YXJzXG4gIF9oYW5kbGVGcmFnbWVudExvYWRQcm9ncmVzcyhmcmFnKSB7fVxuICBfZG9GcmFnTG9hZChmcmFnLCBsZXZlbCwgdGFyZ2V0QnVmZmVyVGltZSA9IG51bGwsIHByb2dyZXNzQ2FsbGJhY2spIHtcbiAgICB2YXIgX2ZyYWckZGVjcnlwdGRhdGE7XG4gICAgY29uc3QgZGV0YWlscyA9IGxldmVsID09IG51bGwgPyB2b2lkIDAgOiBsZXZlbC5kZXRhaWxzO1xuICAgIGlmICghdGhpcy5sZXZlbHMgfHwgIWRldGFpbHMpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgZnJhZyBsb2FkIGFib3J0ZWQsIG1pc3NpbmcgbGV2ZWwke2RldGFpbHMgPyAnJyA6ICcgZGV0YWlsJ31zYCk7XG4gICAgfVxuICAgIGxldCBrZXlMb2FkaW5nUHJvbWlzZSA9IG51bGw7XG4gICAgaWYgKGZyYWcuZW5jcnlwdGVkICYmICEoKF9mcmFnJGRlY3J5cHRkYXRhID0gZnJhZy5kZWNyeXB0ZGF0YSkgIT0gbnVsbCAmJiBfZnJhZyRkZWNyeXB0ZGF0YS5rZXkpKSB7XG4gICAgICB0aGlzLmxvZyhgTG9hZGluZyBrZXkgZm9yICR7ZnJhZy5zbn0gb2YgWyR7ZGV0YWlscy5zdGFydFNOfS0ke2RldGFpbHMuZW5kU059XSwgJHt0aGlzLmxvZ1ByZWZpeCA9PT0gJ1tzdHJlYW0tY29udHJvbGxlcl0nID8gJ2xldmVsJyA6ICd0cmFjayd9ICR7ZnJhZy5sZXZlbH1gKTtcbiAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5LRVlfTE9BRElORztcbiAgICAgIHRoaXMuZnJhZ0N1cnJlbnQgPSBmcmFnO1xuICAgICAga2V5TG9hZGluZ1Byb21pc2UgPSB0aGlzLmtleUxvYWRlci5sb2FkKGZyYWcpLnRoZW4oa2V5TG9hZGVkRGF0YSA9PiB7XG4gICAgICAgIGlmICghdGhpcy5mcmFnQ29udGV4dENoYW5nZWQoa2V5TG9hZGVkRGF0YS5mcmFnKSkge1xuICAgICAgICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLktFWV9MT0FERUQsIGtleUxvYWRlZERhdGEpO1xuICAgICAgICAgIGlmICh0aGlzLnN0YXRlID09PSBTdGF0ZS5LRVlfTE9BRElORykge1xuICAgICAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBrZXlMb2FkZWREYXRhO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLktFWV9MT0FESU5HLCB7XG4gICAgICAgIGZyYWdcbiAgICAgIH0pO1xuICAgICAgaWYgKHRoaXMuZnJhZ0N1cnJlbnQgPT09IG51bGwpIHtcbiAgICAgICAga2V5TG9hZGluZ1Byb21pc2UgPSBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoYGZyYWcgbG9hZCBhYm9ydGVkLCBjb250ZXh0IGNoYW5nZWQgaW4gS0VZX0xPQURJTkdgKSk7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmICghZnJhZy5lbmNyeXB0ZWQgJiYgZGV0YWlscy5lbmNyeXB0ZWRGcmFnbWVudHMubGVuZ3RoKSB7XG4gICAgICB0aGlzLmtleUxvYWRlci5sb2FkQ2xlYXIoZnJhZywgZGV0YWlscy5lbmNyeXB0ZWRGcmFnbWVudHMpO1xuICAgIH1cbiAgICB0YXJnZXRCdWZmZXJUaW1lID0gTWF0aC5tYXgoZnJhZy5zdGFydCwgdGFyZ2V0QnVmZmVyVGltZSB8fCAwKTtcbiAgICBpZiAodGhpcy5jb25maWcubG93TGF0ZW5jeU1vZGUgJiYgZnJhZy5zbiAhPT0gJ2luaXRTZWdtZW50Jykge1xuICAgICAgY29uc3QgcGFydExpc3QgPSBkZXRhaWxzLnBhcnRMaXN0O1xuICAgICAgaWYgKHBhcnRMaXN0ICYmIHByb2dyZXNzQ2FsbGJhY2spIHtcbiAgICAgICAgaWYgKHRhcmdldEJ1ZmZlclRpbWUgPiBmcmFnLmVuZCAmJiBkZXRhaWxzLmZyYWdtZW50SGludCkge1xuICAgICAgICAgIGZyYWcgPSBkZXRhaWxzLmZyYWdtZW50SGludDtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBwYXJ0SW5kZXggPSB0aGlzLmdldE5leHRQYXJ0KHBhcnRMaXN0LCBmcmFnLCB0YXJnZXRCdWZmZXJUaW1lKTtcbiAgICAgICAgaWYgKHBhcnRJbmRleCA+IC0xKSB7XG4gICAgICAgICAgY29uc3QgcGFydCA9IHBhcnRMaXN0W3BhcnRJbmRleF07XG4gICAgICAgICAgdGhpcy5sb2coYExvYWRpbmcgcGFydCBzbjogJHtmcmFnLnNufSBwOiAke3BhcnQuaW5kZXh9IGNjOiAke2ZyYWcuY2N9IG9mIHBsYXlsaXN0IFske2RldGFpbHMuc3RhcnRTTn0tJHtkZXRhaWxzLmVuZFNOfV0gcGFydHMgWzAtJHtwYXJ0SW5kZXh9LSR7cGFydExpc3QubGVuZ3RoIC0gMX1dICR7dGhpcy5sb2dQcmVmaXggPT09ICdbc3RyZWFtLWNvbnRyb2xsZXJdJyA/ICdsZXZlbCcgOiAndHJhY2snfTogJHtmcmFnLmxldmVsfSwgdGFyZ2V0OiAke3BhcnNlRmxvYXQodGFyZ2V0QnVmZmVyVGltZS50b0ZpeGVkKDMpKX1gKTtcbiAgICAgICAgICB0aGlzLm5leHRMb2FkUG9zaXRpb24gPSBwYXJ0LnN0YXJ0ICsgcGFydC5kdXJhdGlvbjtcbiAgICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuRlJBR19MT0FESU5HO1xuICAgICAgICAgIGxldCBfcmVzdWx0O1xuICAgICAgICAgIGlmIChrZXlMb2FkaW5nUHJvbWlzZSkge1xuICAgICAgICAgICAgX3Jlc3VsdCA9IGtleUxvYWRpbmdQcm9taXNlLnRoZW4oa2V5TG9hZGVkRGF0YSA9PiB7XG4gICAgICAgICAgICAgIGlmICgha2V5TG9hZGVkRGF0YSB8fCB0aGlzLmZyYWdDb250ZXh0Q2hhbmdlZChrZXlMb2FkZWREYXRhLmZyYWcpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuZG9GcmFnUGFydHNMb2FkKGZyYWcsIHBhcnQsIGxldmVsLCBwcm9ncmVzc0NhbGxiYWNrKTtcbiAgICAgICAgICAgIH0pLmNhdGNoKGVycm9yID0+IHRoaXMuaGFuZGxlRnJhZ0xvYWRFcnJvcihlcnJvcikpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBfcmVzdWx0ID0gdGhpcy5kb0ZyYWdQYXJ0c0xvYWQoZnJhZywgcGFydCwgbGV2ZWwsIHByb2dyZXNzQ2FsbGJhY2spLmNhdGNoKGVycm9yID0+IHRoaXMuaGFuZGxlRnJhZ0xvYWRFcnJvcihlcnJvcikpO1xuICAgICAgICAgIH1cbiAgICAgICAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5GUkFHX0xPQURJTkcsIHtcbiAgICAgICAgICAgIGZyYWcsXG4gICAgICAgICAgICBwYXJ0LFxuICAgICAgICAgICAgdGFyZ2V0QnVmZmVyVGltZVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIGlmICh0aGlzLmZyYWdDdXJyZW50ID09PSBudWxsKSB7XG4gICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKGBmcmFnIGxvYWQgYWJvcnRlZCwgY29udGV4dCBjaGFuZ2VkIGluIEZSQUdfTE9BRElORyBwYXJ0c2ApKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIF9yZXN1bHQ7XG4gICAgICAgIH0gZWxzZSBpZiAoIWZyYWcudXJsIHx8IHRoaXMubG9hZGVkRW5kT2ZQYXJ0cyhwYXJ0TGlzdCwgdGFyZ2V0QnVmZmVyVGltZSkpIHtcbiAgICAgICAgICAvLyBGcmFnbWVudCBoaW50IGhhcyBubyBwYXJ0c1xuICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUobnVsbCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5sb2coYExvYWRpbmcgZnJhZ21lbnQgJHtmcmFnLnNufSBjYzogJHtmcmFnLmNjfSAke2RldGFpbHMgPyAnb2YgWycgKyBkZXRhaWxzLnN0YXJ0U04gKyAnLScgKyBkZXRhaWxzLmVuZFNOICsgJ10gJyA6ICcnfSR7dGhpcy5sb2dQcmVmaXggPT09ICdbc3RyZWFtLWNvbnRyb2xsZXJdJyA/ICdsZXZlbCcgOiAndHJhY2snfTogJHtmcmFnLmxldmVsfSwgdGFyZ2V0OiAke3BhcnNlRmxvYXQodGFyZ2V0QnVmZmVyVGltZS50b0ZpeGVkKDMpKX1gKTtcbiAgICAvLyBEb24ndCB1cGRhdGUgbmV4dExvYWRQb3NpdGlvbiBmb3IgZnJhZ21lbnRzIHdoaWNoIGFyZSBub3QgYnVmZmVyZWRcbiAgICBpZiAoaXNGaW5pdGVOdW1iZXIoZnJhZy5zbikgJiYgIXRoaXMuYml0cmF0ZVRlc3QpIHtcbiAgICAgIHRoaXMubmV4dExvYWRQb3NpdGlvbiA9IGZyYWcuc3RhcnQgKyBmcmFnLmR1cmF0aW9uO1xuICAgIH1cbiAgICB0aGlzLnN0YXRlID0gU3RhdGUuRlJBR19MT0FESU5HO1xuXG4gICAgLy8gTG9hZCBrZXkgYmVmb3JlIHN0cmVhbWluZyBmcmFnbWVudCBkYXRhXG4gICAgY29uc3QgZGF0YU9uUHJvZ3Jlc3MgPSB0aGlzLmNvbmZpZy5wcm9ncmVzc2l2ZTtcbiAgICBsZXQgcmVzdWx0O1xuICAgIGlmIChkYXRhT25Qcm9ncmVzcyAmJiBrZXlMb2FkaW5nUHJvbWlzZSkge1xuICAgICAgcmVzdWx0ID0ga2V5TG9hZGluZ1Byb21pc2UudGhlbihrZXlMb2FkZWREYXRhID0+IHtcbiAgICAgICAgaWYgKCFrZXlMb2FkZWREYXRhIHx8IHRoaXMuZnJhZ0NvbnRleHRDaGFuZ2VkKGtleUxvYWRlZERhdGEgPT0gbnVsbCA/IHZvaWQgMCA6IGtleUxvYWRlZERhdGEuZnJhZykpIHtcbiAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcy5mcmFnbWVudExvYWRlci5sb2FkKGZyYWcsIHByb2dyZXNzQ2FsbGJhY2spO1xuICAgICAgfSkuY2F0Y2goZXJyb3IgPT4gdGhpcy5oYW5kbGVGcmFnTG9hZEVycm9yKGVycm9yKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIGxvYWQgdW5lbmNyeXB0ZWQgZnJhZ21lbnQgZGF0YSB3aXRoIHByb2dyZXNzIGV2ZW50LFxuICAgICAgLy8gb3IgaGFuZGxlIGZyYWdtZW50IHJlc3VsdCBhZnRlciBrZXkgYW5kIGZyYWdtZW50IGFyZSBmaW5pc2hlZCBsb2FkaW5nXG4gICAgICByZXN1bHQgPSBQcm9taXNlLmFsbChbdGhpcy5mcmFnbWVudExvYWRlci5sb2FkKGZyYWcsIGRhdGFPblByb2dyZXNzID8gcHJvZ3Jlc3NDYWxsYmFjayA6IHVuZGVmaW5lZCksIGtleUxvYWRpbmdQcm9taXNlXSkudGhlbigoW2ZyYWdMb2FkZWREYXRhXSkgPT4ge1xuICAgICAgICBpZiAoIWRhdGFPblByb2dyZXNzICYmIGZyYWdMb2FkZWREYXRhICYmIHByb2dyZXNzQ2FsbGJhY2spIHtcbiAgICAgICAgICBwcm9ncmVzc0NhbGxiYWNrKGZyYWdMb2FkZWREYXRhKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZnJhZ0xvYWRlZERhdGE7XG4gICAgICB9KS5jYXRjaChlcnJvciA9PiB0aGlzLmhhbmRsZUZyYWdMb2FkRXJyb3IoZXJyb3IpKTtcbiAgICB9XG4gICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuRlJBR19MT0FESU5HLCB7XG4gICAgICBmcmFnLFxuICAgICAgdGFyZ2V0QnVmZmVyVGltZVxuICAgIH0pO1xuICAgIGlmICh0aGlzLmZyYWdDdXJyZW50ID09PSBudWxsKSB7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKGBmcmFnIGxvYWQgYWJvcnRlZCwgY29udGV4dCBjaGFuZ2VkIGluIEZSQUdfTE9BRElOR2ApKTtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuICBkb0ZyYWdQYXJ0c0xvYWQoZnJhZywgZnJvbVBhcnQsIGxldmVsLCBwcm9ncmVzc0NhbGxiYWNrKSB7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIHZhciBfbGV2ZWwkZGV0YWlscztcbiAgICAgIGNvbnN0IHBhcnRzTG9hZGVkID0gW107XG4gICAgICBjb25zdCBpbml0aWFsUGFydExpc3QgPSAoX2xldmVsJGRldGFpbHMgPSBsZXZlbC5kZXRhaWxzKSA9PSBudWxsID8gdm9pZCAwIDogX2xldmVsJGRldGFpbHMucGFydExpc3Q7XG4gICAgICBjb25zdCBsb2FkUGFydCA9IHBhcnQgPT4ge1xuICAgICAgICB0aGlzLmZyYWdtZW50TG9hZGVyLmxvYWRQYXJ0KGZyYWcsIHBhcnQsIHByb2dyZXNzQ2FsbGJhY2spLnRoZW4ocGFydExvYWRlZERhdGEgPT4ge1xuICAgICAgICAgIHBhcnRzTG9hZGVkW3BhcnQuaW5kZXhdID0gcGFydExvYWRlZERhdGE7XG4gICAgICAgICAgY29uc3QgbG9hZGVkUGFydCA9IHBhcnRMb2FkZWREYXRhLnBhcnQ7XG4gICAgICAgICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuRlJBR19MT0FERUQsIHBhcnRMb2FkZWREYXRhKTtcbiAgICAgICAgICBjb25zdCBuZXh0UGFydCA9IGdldFBhcnRXaXRoKGxldmVsLCBmcmFnLnNuLCBwYXJ0LmluZGV4ICsgMSkgfHwgZmluZFBhcnQoaW5pdGlhbFBhcnRMaXN0LCBmcmFnLnNuLCBwYXJ0LmluZGV4ICsgMSk7XG4gICAgICAgICAgaWYgKG5leHRQYXJ0KSB7XG4gICAgICAgICAgICBsb2FkUGFydChuZXh0UGFydCk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiByZXNvbHZlKHtcbiAgICAgICAgICAgICAgZnJhZyxcbiAgICAgICAgICAgICAgcGFydDogbG9hZGVkUGFydCxcbiAgICAgICAgICAgICAgcGFydHNMb2FkZWRcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfSkuY2F0Y2gocmVqZWN0KTtcbiAgICAgIH07XG4gICAgICBsb2FkUGFydChmcm9tUGFydCk7XG4gICAgfSk7XG4gIH1cbiAgaGFuZGxlRnJhZ0xvYWRFcnJvcihlcnJvcikge1xuICAgIGlmICgnZGF0YScgaW4gZXJyb3IpIHtcbiAgICAgIGNvbnN0IGRhdGEgPSBlcnJvci5kYXRhO1xuICAgICAgaWYgKGVycm9yLmRhdGEgJiYgZGF0YS5kZXRhaWxzID09PSBFcnJvckRldGFpbHMuSU5URVJOQUxfQUJPUlRFRCkge1xuICAgICAgICB0aGlzLmhhbmRsZUZyYWdMb2FkQWJvcnRlZChkYXRhLmZyYWcsIGRhdGEucGFydCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5FUlJPUiwgZGF0YSk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLkVSUk9SLCB7XG4gICAgICAgIHR5cGU6IEVycm9yVHlwZXMuT1RIRVJfRVJST1IsXG4gICAgICAgIGRldGFpbHM6IEVycm9yRGV0YWlscy5JTlRFUk5BTF9FWENFUFRJT04sXG4gICAgICAgIGVycjogZXJyb3IsXG4gICAgICAgIGVycm9yLFxuICAgICAgICBmYXRhbDogdHJ1ZVxuICAgICAgfSk7XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG4gIF9oYW5kbGVUcmFuc211eGVyRmx1c2goY2h1bmtNZXRhKSB7XG4gICAgY29uc3QgY29udGV4dCA9IHRoaXMuZ2V0Q3VycmVudENvbnRleHQoY2h1bmtNZXRhKTtcbiAgICBpZiAoIWNvbnRleHQgfHwgdGhpcy5zdGF0ZSAhPT0gU3RhdGUuUEFSU0lORykge1xuICAgICAgaWYgKCF0aGlzLmZyYWdDdXJyZW50ICYmIHRoaXMuc3RhdGUgIT09IFN0YXRlLlNUT1BQRUQgJiYgdGhpcy5zdGF0ZSAhPT0gU3RhdGUuRVJST1IpIHtcbiAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gICAgICB9XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHtcbiAgICAgIGZyYWcsXG4gICAgICBwYXJ0LFxuICAgICAgbGV2ZWxcbiAgICB9ID0gY29udGV4dDtcbiAgICBjb25zdCBub3cgPSBzZWxmLnBlcmZvcm1hbmNlLm5vdygpO1xuICAgIGZyYWcuc3RhdHMucGFyc2luZy5lbmQgPSBub3c7XG4gICAgaWYgKHBhcnQpIHtcbiAgICAgIHBhcnQuc3RhdHMucGFyc2luZy5lbmQgPSBub3c7XG4gICAgfVxuICAgIHRoaXMudXBkYXRlTGV2ZWxUaW1pbmcoZnJhZywgcGFydCwgbGV2ZWwsIGNodW5rTWV0YS5wYXJ0aWFsKTtcbiAgfVxuICBnZXRDdXJyZW50Q29udGV4dChjaHVua01ldGEpIHtcbiAgICBjb25zdCB7XG4gICAgICBsZXZlbHMsXG4gICAgICBmcmFnQ3VycmVudFxuICAgIH0gPSB0aGlzO1xuICAgIGNvbnN0IHtcbiAgICAgIGxldmVsOiBsZXZlbEluZGV4LFxuICAgICAgc24sXG4gICAgICBwYXJ0OiBwYXJ0SW5kZXhcbiAgICB9ID0gY2h1bmtNZXRhO1xuICAgIGlmICghKGxldmVscyAhPSBudWxsICYmIGxldmVsc1tsZXZlbEluZGV4XSkpIHtcbiAgICAgIHRoaXMud2FybihgTGV2ZWxzIG9iamVjdCB3YXMgdW5zZXQgd2hpbGUgYnVmZmVyaW5nIGZyYWdtZW50ICR7c259IG9mIGxldmVsICR7bGV2ZWxJbmRleH0uIFRoZSBjdXJyZW50IGNodW5rIHdpbGwgbm90IGJlIGJ1ZmZlcmVkLmApO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIGNvbnN0IGxldmVsID0gbGV2ZWxzW2xldmVsSW5kZXhdO1xuICAgIGNvbnN0IHBhcnQgPSBwYXJ0SW5kZXggPiAtMSA/IGdldFBhcnRXaXRoKGxldmVsLCBzbiwgcGFydEluZGV4KSA6IG51bGw7XG4gICAgY29uc3QgZnJhZyA9IHBhcnQgPyBwYXJ0LmZyYWdtZW50IDogZ2V0RnJhZ21lbnRXaXRoU04obGV2ZWwsIHNuLCBmcmFnQ3VycmVudCk7XG4gICAgaWYgKCFmcmFnKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgaWYgKGZyYWdDdXJyZW50ICYmIGZyYWdDdXJyZW50ICE9PSBmcmFnKSB7XG4gICAgICBmcmFnLnN0YXRzID0gZnJhZ0N1cnJlbnQuc3RhdHM7XG4gICAgfVxuICAgIHJldHVybiB7XG4gICAgICBmcmFnLFxuICAgICAgcGFydCxcbiAgICAgIGxldmVsXG4gICAgfTtcbiAgfVxuICBidWZmZXJGcmFnbWVudERhdGEoZGF0YSwgZnJhZywgcGFydCwgY2h1bmtNZXRhLCBub0JhY2t0cmFja2luZykge1xuICAgIHZhciBfYnVmZmVyO1xuICAgIGlmICghZGF0YSB8fCB0aGlzLnN0YXRlICE9PSBTdGF0ZS5QQVJTSU5HKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHtcbiAgICAgIGRhdGExLFxuICAgICAgZGF0YTJcbiAgICB9ID0gZGF0YTtcbiAgICBsZXQgYnVmZmVyID0gZGF0YTE7XG4gICAgaWYgKGRhdGExICYmIGRhdGEyKSB7XG4gICAgICAvLyBDb21iaW5lIHRoZSBtb29mICsgbWRhdCBzbyB0aGF0IHdlIGJ1ZmZlciB3aXRoIGEgc2luZ2xlIGFwcGVuZFxuICAgICAgYnVmZmVyID0gYXBwZW5kVWludDhBcnJheShkYXRhMSwgZGF0YTIpO1xuICAgIH1cbiAgICBpZiAoISgoX2J1ZmZlciA9IGJ1ZmZlcikgIT0gbnVsbCAmJiBfYnVmZmVyLmxlbmd0aCkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3Qgc2VnbWVudCA9IHtcbiAgICAgIHR5cGU6IGRhdGEudHlwZSxcbiAgICAgIGZyYWcsXG4gICAgICBwYXJ0LFxuICAgICAgY2h1bmtNZXRhLFxuICAgICAgcGFyZW50OiBmcmFnLnR5cGUsXG4gICAgICBkYXRhOiBidWZmZXJcbiAgICB9O1xuICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLkJVRkZFUl9BUFBFTkRJTkcsIHNlZ21lbnQpO1xuICAgIGlmIChkYXRhLmRyb3BwZWQgJiYgZGF0YS5pbmRlcGVuZGVudCAmJiAhcGFydCkge1xuICAgICAgaWYgKG5vQmFja3RyYWNraW5nKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIC8vIENsZWFyIGJ1ZmZlciBzbyB0aGF0IHdlIHJlbG9hZCBwcmV2aW91cyBzZWdtZW50cyBzZXF1ZW50aWFsbHkgaWYgcmVxdWlyZWRcbiAgICAgIHRoaXMuZmx1c2hCdWZmZXJHYXAoZnJhZyk7XG4gICAgfVxuICB9XG4gIGZsdXNoQnVmZmVyR2FwKGZyYWcpIHtcbiAgICBjb25zdCBtZWRpYSA9IHRoaXMubWVkaWE7XG4gICAgaWYgKCFtZWRpYSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyBJZiBjdXJyZW50VGltZSBpcyBub3QgYnVmZmVyZWQsIGNsZWFyIHRoZSBiYWNrIGJ1ZmZlciBzbyB0aGF0IHdlIGNhbiBiYWNrdHJhY2sgYXMgbXVjaCBhcyBuZWVkZWRcbiAgICBpZiAoIUJ1ZmZlckhlbHBlci5pc0J1ZmZlcmVkKG1lZGlhLCBtZWRpYS5jdXJyZW50VGltZSkpIHtcbiAgICAgIHRoaXMuZmx1c2hNYWluQnVmZmVyKDAsIGZyYWcuc3RhcnQpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyBSZW1vdmUgYmFjay1idWZmZXIgd2l0aG91dCBpbnRlcnJ1cHRpbmcgcGxheWJhY2sgdG8gYWxsb3cgYmFjayB0cmFja2luZ1xuICAgIGNvbnN0IGN1cnJlbnRUaW1lID0gbWVkaWEuY3VycmVudFRpbWU7XG4gICAgY29uc3QgYnVmZmVySW5mbyA9IEJ1ZmZlckhlbHBlci5idWZmZXJJbmZvKG1lZGlhLCBjdXJyZW50VGltZSwgMCk7XG4gICAgY29uc3QgZnJhZ0R1cmF0aW9uID0gZnJhZy5kdXJhdGlvbjtcbiAgICBjb25zdCBzZWdtZW50RnJhY3Rpb24gPSBNYXRoLm1pbih0aGlzLmNvbmZpZy5tYXhGcmFnTG9va1VwVG9sZXJhbmNlICogMiwgZnJhZ0R1cmF0aW9uICogMC4yNSk7XG4gICAgY29uc3Qgc3RhcnQgPSBNYXRoLm1heChNYXRoLm1pbihmcmFnLnN0YXJ0IC0gc2VnbWVudEZyYWN0aW9uLCBidWZmZXJJbmZvLmVuZCAtIHNlZ21lbnRGcmFjdGlvbiksIGN1cnJlbnRUaW1lICsgc2VnbWVudEZyYWN0aW9uKTtcbiAgICBpZiAoZnJhZy5zdGFydCAtIHN0YXJ0ID4gc2VnbWVudEZyYWN0aW9uKSB7XG4gICAgICB0aGlzLmZsdXNoTWFpbkJ1ZmZlcihzdGFydCwgZnJhZy5zdGFydCk7XG4gICAgfVxuICB9XG4gIGdldEZ3ZEJ1ZmZlckluZm8oYnVmZmVyYWJsZSwgdHlwZSkge1xuICAgIGNvbnN0IHBvcyA9IHRoaXMuZ2V0TG9hZFBvc2l0aW9uKCk7XG4gICAgaWYgKCFpc0Zpbml0ZU51bWJlcihwb3MpKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuZ2V0RndkQnVmZmVySW5mb0F0UG9zKGJ1ZmZlcmFibGUsIHBvcywgdHlwZSk7XG4gIH1cbiAgZ2V0RndkQnVmZmVySW5mb0F0UG9zKGJ1ZmZlcmFibGUsIHBvcywgdHlwZSkge1xuICAgIGNvbnN0IHtcbiAgICAgIGNvbmZpZzoge1xuICAgICAgICBtYXhCdWZmZXJIb2xlXG4gICAgICB9XG4gICAgfSA9IHRoaXM7XG4gICAgY29uc3QgYnVmZmVySW5mbyA9IEJ1ZmZlckhlbHBlci5idWZmZXJJbmZvKGJ1ZmZlcmFibGUsIHBvcywgbWF4QnVmZmVySG9sZSk7XG4gICAgLy8gV29ya2Fyb3VuZCBmbGF3IGluIGdldHRpbmcgZm9yd2FyZCBidWZmZXIgd2hlbiBtYXhCdWZmZXJIb2xlIGlzIHNtYWxsZXIgdGhhbiBnYXAgYXQgY3VycmVudCBwb3NcbiAgICBpZiAoYnVmZmVySW5mby5sZW4gPT09IDAgJiYgYnVmZmVySW5mby5uZXh0U3RhcnQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgY29uc3QgYnVmZmVyZWRGcmFnQXRQb3MgPSB0aGlzLmZyYWdtZW50VHJhY2tlci5nZXRCdWZmZXJlZEZyYWcocG9zLCB0eXBlKTtcbiAgICAgIGlmIChidWZmZXJlZEZyYWdBdFBvcyAmJiBidWZmZXJJbmZvLm5leHRTdGFydCA8IGJ1ZmZlcmVkRnJhZ0F0UG9zLmVuZCkge1xuICAgICAgICByZXR1cm4gQnVmZmVySGVscGVyLmJ1ZmZlckluZm8oYnVmZmVyYWJsZSwgcG9zLCBNYXRoLm1heChidWZmZXJJbmZvLm5leHRTdGFydCwgbWF4QnVmZmVySG9sZSkpO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gYnVmZmVySW5mbztcbiAgfVxuICBnZXRNYXhCdWZmZXJMZW5ndGgobGV2ZWxCaXRyYXRlKSB7XG4gICAgY29uc3Qge1xuICAgICAgY29uZmlnXG4gICAgfSA9IHRoaXM7XG4gICAgbGV0IG1heEJ1ZkxlbjtcbiAgICBpZiAobGV2ZWxCaXRyYXRlKSB7XG4gICAgICBtYXhCdWZMZW4gPSBNYXRoLm1heCg4ICogY29uZmlnLm1heEJ1ZmZlclNpemUgLyBsZXZlbEJpdHJhdGUsIGNvbmZpZy5tYXhCdWZmZXJMZW5ndGgpO1xuICAgIH0gZWxzZSB7XG4gICAgICBtYXhCdWZMZW4gPSBjb25maWcubWF4QnVmZmVyTGVuZ3RoO1xuICAgIH1cbiAgICByZXR1cm4gTWF0aC5taW4obWF4QnVmTGVuLCBjb25maWcubWF4TWF4QnVmZmVyTGVuZ3RoKTtcbiAgfVxuICByZWR1Y2VNYXhCdWZmZXJMZW5ndGgodGhyZXNob2xkLCBmcmFnRHVyYXRpb24pIHtcbiAgICBjb25zdCBjb25maWcgPSB0aGlzLmNvbmZpZztcbiAgICBjb25zdCBtaW5MZW5ndGggPSBNYXRoLm1heChNYXRoLm1pbih0aHJlc2hvbGQgLSBmcmFnRHVyYXRpb24sIGNvbmZpZy5tYXhCdWZmZXJMZW5ndGgpLCBmcmFnRHVyYXRpb24pO1xuICAgIGNvbnN0IHJlZHVjZWRMZW5ndGggPSBNYXRoLm1heCh0aHJlc2hvbGQgLSBmcmFnRHVyYXRpb24gKiAzLCBjb25maWcubWF4TWF4QnVmZmVyTGVuZ3RoIC8gMiwgbWluTGVuZ3RoKTtcbiAgICBpZiAocmVkdWNlZExlbmd0aCA+PSBtaW5MZW5ndGgpIHtcbiAgICAgIC8vIHJlZHVjZSBtYXggYnVmZmVyIGxlbmd0aCBhcyBpdCBtaWdodCBiZSB0b28gaGlnaC4gd2UgZG8gdGhpcyB0byBhdm9pZCBsb29wIGZsdXNoaW5nIC4uLlxuICAgICAgY29uZmlnLm1heE1heEJ1ZmZlckxlbmd0aCA9IHJlZHVjZWRMZW5ndGg7XG4gICAgICB0aGlzLndhcm4oYFJlZHVjZSBtYXggYnVmZmVyIGxlbmd0aCB0byAke3JlZHVjZWRMZW5ndGh9c2ApO1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICBnZXRBcHBlbmRlZEZyYWcocG9zaXRpb24sIHBsYXlsaXN0VHlwZSA9IFBsYXlsaXN0TGV2ZWxUeXBlLk1BSU4pIHtcbiAgICBjb25zdCBmcmFnT3JQYXJ0ID0gdGhpcy5mcmFnbWVudFRyYWNrZXIuZ2V0QXBwZW5kZWRGcmFnKHBvc2l0aW9uLCBQbGF5bGlzdExldmVsVHlwZS5NQUlOKTtcbiAgICBpZiAoZnJhZ09yUGFydCAmJiAnZnJhZ21lbnQnIGluIGZyYWdPclBhcnQpIHtcbiAgICAgIHJldHVybiBmcmFnT3JQYXJ0LmZyYWdtZW50O1xuICAgIH1cbiAgICByZXR1cm4gZnJhZ09yUGFydDtcbiAgfVxuICBnZXROZXh0RnJhZ21lbnQocG9zLCBsZXZlbERldGFpbHMpIHtcbiAgICBjb25zdCBmcmFnbWVudHMgPSBsZXZlbERldGFpbHMuZnJhZ21lbnRzO1xuICAgIGNvbnN0IGZyYWdMZW4gPSBmcmFnbWVudHMubGVuZ3RoO1xuICAgIGlmICghZnJhZ0xlbikge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgLy8gZmluZCBmcmFnbWVudCBpbmRleCwgY29udGlndW91cyB3aXRoIGVuZCBvZiBidWZmZXIgcG9zaXRpb25cbiAgICBjb25zdCB7XG4gICAgICBjb25maWdcbiAgICB9ID0gdGhpcztcbiAgICBjb25zdCBzdGFydCA9IGZyYWdtZW50c1swXS5zdGFydDtcbiAgICBsZXQgZnJhZztcbiAgICBpZiAobGV2ZWxEZXRhaWxzLmxpdmUpIHtcbiAgICAgIGNvbnN0IGluaXRpYWxMaXZlTWFuaWZlc3RTaXplID0gY29uZmlnLmluaXRpYWxMaXZlTWFuaWZlc3RTaXplO1xuICAgICAgaWYgKGZyYWdMZW4gPCBpbml0aWFsTGl2ZU1hbmlmZXN0U2l6ZSkge1xuICAgICAgICB0aGlzLndhcm4oYE5vdCBlbm91Z2ggZnJhZ21lbnRzIHRvIHN0YXJ0IHBsYXliYWNrIChoYXZlOiAke2ZyYWdMZW59LCBuZWVkOiAke2luaXRpYWxMaXZlTWFuaWZlc3RTaXplfSlgKTtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG4gICAgICAvLyBUaGUgcmVhbCBmcmFnbWVudCBzdGFydCB0aW1lcyBmb3IgYSBsaXZlIHN0cmVhbSBhcmUgb25seSBrbm93biBhZnRlciB0aGUgUFRTIHJhbmdlIGZvciB0aGF0IGxldmVsIGlzIGtub3duLlxuICAgICAgLy8gSW4gb3JkZXIgdG8gZGlzY292ZXIgdGhlIHJhbmdlLCB3ZSBsb2FkIHRoZSBiZXN0IG1hdGNoaW5nIGZyYWdtZW50IGZvciB0aGF0IGxldmVsIGFuZCBkZW11eCBpdC5cbiAgICAgIC8vIERvIG5vdCBsb2FkIHVzaW5nIGxpdmUgbG9naWMgaWYgdGhlIHN0YXJ0aW5nIGZyYWcgaXMgcmVxdWVzdGVkIC0gd2Ugd2FudCB0byB1c2UgZ2V0RnJhZ21lbnRBdFBvc2l0aW9uKCkgc28gdGhhdFxuICAgICAgLy8gd2UgZ2V0IHRoZSBmcmFnbWVudCBtYXRjaGluZyB0aGF0IHN0YXJ0IHRpbWVcbiAgICAgIGlmICghbGV2ZWxEZXRhaWxzLlBUU0tub3duICYmICF0aGlzLnN0YXJ0RnJhZ1JlcXVlc3RlZCAmJiB0aGlzLnN0YXJ0UG9zaXRpb24gPT09IC0xIHx8IHBvcyA8IHN0YXJ0KSB7XG4gICAgICAgIGZyYWcgPSB0aGlzLmdldEluaXRpYWxMaXZlRnJhZ21lbnQobGV2ZWxEZXRhaWxzLCBmcmFnbWVudHMpO1xuICAgICAgICB0aGlzLnN0YXJ0UG9zaXRpb24gPSB0aGlzLm5leHRMb2FkUG9zaXRpb24gPSBmcmFnID8gdGhpcy5obHMubGl2ZVN5bmNQb3NpdGlvbiB8fCBmcmFnLnN0YXJ0IDogcG9zO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAocG9zIDw9IHN0YXJ0KSB7XG4gICAgICAvLyBWb0QgcGxheWxpc3Q6IGlmIGxvYWRQb3NpdGlvbiBiZWZvcmUgc3RhcnQgb2YgcGxheWxpc3QsIGxvYWQgZmlyc3QgZnJhZ21lbnRcbiAgICAgIGZyYWcgPSBmcmFnbWVudHNbMF07XG4gICAgfVxuXG4gICAgLy8gSWYgd2UgaGF2ZW4ndCBydW4gaW50byBhbnkgc3BlY2lhbCBjYXNlcyBhbHJlYWR5LCBqdXN0IGxvYWQgdGhlIGZyYWdtZW50IG1vc3QgY2xvc2VseSBtYXRjaGluZyB0aGUgcmVxdWVzdGVkIHBvc2l0aW9uXG4gICAgaWYgKCFmcmFnKSB7XG4gICAgICBjb25zdCBlbmQgPSBjb25maWcubG93TGF0ZW5jeU1vZGUgPyBsZXZlbERldGFpbHMucGFydEVuZCA6IGxldmVsRGV0YWlscy5mcmFnbWVudEVuZDtcbiAgICAgIGZyYWcgPSB0aGlzLmdldEZyYWdtZW50QXRQb3NpdGlvbihwb3MsIGVuZCwgbGV2ZWxEZXRhaWxzKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMubWFwVG9Jbml0RnJhZ1doZW5SZXF1aXJlZChmcmFnKTtcbiAgfVxuICBpc0xvb3BMb2FkaW5nKGZyYWcsIHRhcmdldEJ1ZmZlclRpbWUpIHtcbiAgICBjb25zdCB0cmFja2VyU3RhdGUgPSB0aGlzLmZyYWdtZW50VHJhY2tlci5nZXRTdGF0ZShmcmFnKTtcbiAgICByZXR1cm4gKHRyYWNrZXJTdGF0ZSA9PT0gRnJhZ21lbnRTdGF0ZS5PSyB8fCB0cmFja2VyU3RhdGUgPT09IEZyYWdtZW50U3RhdGUuUEFSVElBTCAmJiAhIWZyYWcuZ2FwKSAmJiB0aGlzLm5leHRMb2FkUG9zaXRpb24gPiB0YXJnZXRCdWZmZXJUaW1lO1xuICB9XG4gIGdldE5leHRGcmFnbWVudExvb3BMb2FkaW5nKGZyYWcsIGxldmVsRGV0YWlscywgYnVmZmVySW5mbywgcGxheWxpc3RUeXBlLCBtYXhCdWZMZW4pIHtcbiAgICBjb25zdCBnYXBTdGFydCA9IGZyYWcuZ2FwO1xuICAgIGNvbnN0IG5leHRGcmFnbWVudCA9IHRoaXMuZ2V0TmV4dEZyYWdtZW50KHRoaXMubmV4dExvYWRQb3NpdGlvbiwgbGV2ZWxEZXRhaWxzKTtcbiAgICBpZiAobmV4dEZyYWdtZW50ID09PSBudWxsKSB7XG4gICAgICByZXR1cm4gbmV4dEZyYWdtZW50O1xuICAgIH1cbiAgICBmcmFnID0gbmV4dEZyYWdtZW50O1xuICAgIGlmIChnYXBTdGFydCAmJiBmcmFnICYmICFmcmFnLmdhcCAmJiBidWZmZXJJbmZvLm5leHRTdGFydCkge1xuICAgICAgLy8gTWVkaWEgYnVmZmVyZWQgYWZ0ZXIgR0FQIHRhZ3Mgc2hvdWxkIG5vdCBtYWtlIHRoZSBuZXh0IGJ1ZmZlciB0aW1lcmFuZ2UgZXhjZWVkIGZvcndhcmQgYnVmZmVyIGxlbmd0aFxuICAgICAgY29uc3QgbmV4dGJ1ZmZlckluZm8gPSB0aGlzLmdldEZ3ZEJ1ZmZlckluZm9BdFBvcyh0aGlzLm1lZGlhQnVmZmVyID8gdGhpcy5tZWRpYUJ1ZmZlciA6IHRoaXMubWVkaWEsIGJ1ZmZlckluZm8ubmV4dFN0YXJ0LCBwbGF5bGlzdFR5cGUpO1xuICAgICAgaWYgKG5leHRidWZmZXJJbmZvICE9PSBudWxsICYmIGJ1ZmZlckluZm8ubGVuICsgbmV4dGJ1ZmZlckluZm8ubGVuID49IG1heEJ1Zkxlbikge1xuICAgICAgICAvLyBSZXR1cm5pbmcgaGVyZSBtaWdodCByZXN1bHQgaW4gbm90IGZpbmRpbmcgYW4gYXVkaW8gYW5kIHZpZGVvIGNhbmRpYXRlIHRvIHNraXAgdG9cbiAgICAgICAgdGhpcy5sb2coYGJ1ZmZlciBmdWxsIGFmdGVyIGdhcHMgaW4gXCIke3BsYXlsaXN0VHlwZX1cIiBwbGF5bGlzdCBzdGFydGluZyBhdCBzbjogJHtmcmFnLnNufWApO1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGZyYWc7XG4gIH1cbiAgbWFwVG9Jbml0RnJhZ1doZW5SZXF1aXJlZChmcmFnKSB7XG4gICAgLy8gSWYgYW4gaW5pdFNlZ21lbnQgaXMgcHJlc2VudCwgaXQgbXVzdCBiZSBidWZmZXJlZCBmaXJzdFxuICAgIGlmIChmcmFnICE9IG51bGwgJiYgZnJhZy5pbml0U2VnbWVudCAmJiAhKGZyYWcgIT0gbnVsbCAmJiBmcmFnLmluaXRTZWdtZW50LmRhdGEpICYmICF0aGlzLmJpdHJhdGVUZXN0KSB7XG4gICAgICByZXR1cm4gZnJhZy5pbml0U2VnbWVudDtcbiAgICB9XG4gICAgcmV0dXJuIGZyYWc7XG4gIH1cbiAgZ2V0TmV4dFBhcnQocGFydExpc3QsIGZyYWcsIHRhcmdldEJ1ZmZlclRpbWUpIHtcbiAgICBsZXQgbmV4dFBhcnQgPSAtMTtcbiAgICBsZXQgY29udGlndW91cyA9IGZhbHNlO1xuICAgIGxldCBpbmRlcGVuZGVudEF0dHJPbWl0dGVkID0gdHJ1ZTtcbiAgICBmb3IgKGxldCBpID0gMCwgbGVuID0gcGFydExpc3QubGVuZ3RoOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgIGNvbnN0IHBhcnQgPSBwYXJ0TGlzdFtpXTtcbiAgICAgIGluZGVwZW5kZW50QXR0ck9taXR0ZWQgPSBpbmRlcGVuZGVudEF0dHJPbWl0dGVkICYmICFwYXJ0LmluZGVwZW5kZW50O1xuICAgICAgaWYgKG5leHRQYXJ0ID4gLTEgJiYgdGFyZ2V0QnVmZmVyVGltZSA8IHBhcnQuc3RhcnQpIHtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBjb25zdCBsb2FkZWQgPSBwYXJ0LmxvYWRlZDtcbiAgICAgIGlmIChsb2FkZWQpIHtcbiAgICAgICAgbmV4dFBhcnQgPSAtMTtcbiAgICAgIH0gZWxzZSBpZiAoKGNvbnRpZ3VvdXMgfHwgcGFydC5pbmRlcGVuZGVudCB8fCBpbmRlcGVuZGVudEF0dHJPbWl0dGVkKSAmJiBwYXJ0LmZyYWdtZW50ID09PSBmcmFnKSB7XG4gICAgICAgIG5leHRQYXJ0ID0gaTtcbiAgICAgIH1cbiAgICAgIGNvbnRpZ3VvdXMgPSBsb2FkZWQ7XG4gICAgfVxuICAgIHJldHVybiBuZXh0UGFydDtcbiAgfVxuICBsb2FkZWRFbmRPZlBhcnRzKHBhcnRMaXN0LCB0YXJnZXRCdWZmZXJUaW1lKSB7XG4gICAgY29uc3QgbGFzdFBhcnQgPSBwYXJ0TGlzdFtwYXJ0TGlzdC5sZW5ndGggLSAxXTtcbiAgICByZXR1cm4gbGFzdFBhcnQgJiYgdGFyZ2V0QnVmZmVyVGltZSA+IGxhc3RQYXJ0LnN0YXJ0ICYmIGxhc3RQYXJ0LmxvYWRlZDtcbiAgfVxuXG4gIC8qXG4gICBUaGlzIG1ldGhvZCBpcyB1c2VkIGZpbmQgdGhlIGJlc3QgbWF0Y2hpbmcgZmlyc3QgZnJhZ21lbnQgZm9yIGEgbGl2ZSBwbGF5bGlzdC4gVGhpcyBmcmFnbWVudCBpcyB1c2VkIHRvIGNhbGN1bGF0ZSB0aGVcbiAgIFwic2xpZGluZ1wiIG9mIHRoZSBwbGF5bGlzdCwgd2hpY2ggaXMgaXRzIG9mZnNldCBmcm9tIHRoZSBzdGFydCBvZiBwbGF5YmFjay4gQWZ0ZXIgc2xpZGluZyB3ZSBjYW4gY29tcHV0ZSB0aGUgcmVhbFxuICAgc3RhcnQgYW5kIGVuZCB0aW1lcyBmb3IgZWFjaCBmcmFnbWVudCBpbiB0aGUgcGxheWxpc3QgKGFmdGVyIHdoaWNoIHRoaXMgbWV0aG9kIHdpbGwgbm90IG5lZWQgdG8gYmUgY2FsbGVkKS5cbiAgKi9cbiAgZ2V0SW5pdGlhbExpdmVGcmFnbWVudChsZXZlbERldGFpbHMsIGZyYWdtZW50cykge1xuICAgIGNvbnN0IGZyYWdQcmV2aW91cyA9IHRoaXMuZnJhZ1ByZXZpb3VzO1xuICAgIGxldCBmcmFnID0gbnVsbDtcbiAgICBpZiAoZnJhZ1ByZXZpb3VzKSB7XG4gICAgICBpZiAobGV2ZWxEZXRhaWxzLmhhc1Byb2dyYW1EYXRlVGltZSkge1xuICAgICAgICAvLyBQcmVmZXIgdXNpbmcgUERULCBiZWNhdXNlIGl0IGNhbiBiZSBhY2N1cmF0ZSBlbm91Z2ggdG8gY2hvb3NlIHRoZSBjb3JyZWN0IGZyYWdtZW50IHdpdGhvdXQga25vd2luZyB0aGUgbGV2ZWwgc2xpZGluZ1xuICAgICAgICB0aGlzLmxvZyhgTGl2ZSBwbGF5bGlzdCwgc3dpdGNoaW5nIHBsYXlsaXN0LCBsb2FkIGZyYWcgd2l0aCBzYW1lIFBEVDogJHtmcmFnUHJldmlvdXMucHJvZ3JhbURhdGVUaW1lfWApO1xuICAgICAgICBmcmFnID0gZmluZEZyYWdtZW50QnlQRFQoZnJhZ21lbnRzLCBmcmFnUHJldmlvdXMuZW5kUHJvZ3JhbURhdGVUaW1lLCB0aGlzLmNvbmZpZy5tYXhGcmFnTG9va1VwVG9sZXJhbmNlKTtcbiAgICAgIH1cbiAgICAgIGlmICghZnJhZykge1xuICAgICAgICAvLyBTTiBkb2VzIG5vdCBuZWVkIHRvIGJlIGFjY3VyYXRlIGJldHdlZW4gcmVuZGl0aW9ucywgYnV0IGRlcGVuZGluZyBvbiB0aGUgcGFja2FnaW5nIGl0IG1heSBiZSBzby5cbiAgICAgICAgY29uc3QgdGFyZ2V0U04gPSBmcmFnUHJldmlvdXMuc24gKyAxO1xuICAgICAgICBpZiAodGFyZ2V0U04gPj0gbGV2ZWxEZXRhaWxzLnN0YXJ0U04gJiYgdGFyZ2V0U04gPD0gbGV2ZWxEZXRhaWxzLmVuZFNOKSB7XG4gICAgICAgICAgY29uc3QgZnJhZ05leHQgPSBmcmFnbWVudHNbdGFyZ2V0U04gLSBsZXZlbERldGFpbHMuc3RhcnRTTl07XG4gICAgICAgICAgLy8gRW5zdXJlIHRoYXQgd2UncmUgc3RheWluZyB3aXRoaW4gdGhlIGNvbnRpbnVpdHkgcmFuZ2UsIHNpbmNlIFBUUyByZXNldHMgdXBvbiBhIG5ldyByYW5nZVxuICAgICAgICAgIGlmIChmcmFnUHJldmlvdXMuY2MgPT09IGZyYWdOZXh0LmNjKSB7XG4gICAgICAgICAgICBmcmFnID0gZnJhZ05leHQ7XG4gICAgICAgICAgICB0aGlzLmxvZyhgTGl2ZSBwbGF5bGlzdCwgc3dpdGNoaW5nIHBsYXlsaXN0LCBsb2FkIGZyYWcgd2l0aCBuZXh0IFNOOiAke2ZyYWcuc259YCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIC8vIEl0J3MgaW1wb3J0YW50IHRvIHN0YXkgd2l0aGluIHRoZSBjb250aW51aXR5IHJhbmdlIGlmIGF2YWlsYWJsZTsgb3RoZXJ3aXNlIHRoZSBmcmFnbWVudHMgaW4gdGhlIHBsYXlsaXN0XG4gICAgICAgIC8vIHdpbGwgaGF2ZSB0aGUgd3Jvbmcgc3RhcnQgdGltZXNcbiAgICAgICAgaWYgKCFmcmFnKSB7XG4gICAgICAgICAgZnJhZyA9IGZpbmRGcmFnV2l0aENDKGZyYWdtZW50cywgZnJhZ1ByZXZpb3VzLmNjKTtcbiAgICAgICAgICBpZiAoZnJhZykge1xuICAgICAgICAgICAgdGhpcy5sb2coYExpdmUgcGxheWxpc3QsIHN3aXRjaGluZyBwbGF5bGlzdCwgbG9hZCBmcmFnIHdpdGggc2FtZSBDQzogJHtmcmFnLnNufWApO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyBGaW5kIGEgbmV3IHN0YXJ0IGZyYWdtZW50IHdoZW4gZnJhZ1ByZXZpb3VzIGlzIG51bGxcbiAgICAgIGNvbnN0IGxpdmVTdGFydCA9IHRoaXMuaGxzLmxpdmVTeW5jUG9zaXRpb247XG4gICAgICBpZiAobGl2ZVN0YXJ0ICE9PSBudWxsKSB7XG4gICAgICAgIGZyYWcgPSB0aGlzLmdldEZyYWdtZW50QXRQb3NpdGlvbihsaXZlU3RhcnQsIHRoaXMuYml0cmF0ZVRlc3QgPyBsZXZlbERldGFpbHMuZnJhZ21lbnRFbmQgOiBsZXZlbERldGFpbHMuZWRnZSwgbGV2ZWxEZXRhaWxzKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGZyYWc7XG4gIH1cblxuICAvKlxuICBUaGlzIG1ldGhvZCBmaW5kcyB0aGUgYmVzdCBtYXRjaGluZyBmcmFnbWVudCBnaXZlbiB0aGUgcHJvdmlkZWQgcG9zaXRpb24uXG4gICAqL1xuICBnZXRGcmFnbWVudEF0UG9zaXRpb24oYnVmZmVyRW5kLCBlbmQsIGxldmVsRGV0YWlscykge1xuICAgIGNvbnN0IHtcbiAgICAgIGNvbmZpZ1xuICAgIH0gPSB0aGlzO1xuICAgIGxldCB7XG4gICAgICBmcmFnUHJldmlvdXNcbiAgICB9ID0gdGhpcztcbiAgICBsZXQge1xuICAgICAgZnJhZ21lbnRzLFxuICAgICAgZW5kU05cbiAgICB9ID0gbGV2ZWxEZXRhaWxzO1xuICAgIGNvbnN0IHtcbiAgICAgIGZyYWdtZW50SGludFxuICAgIH0gPSBsZXZlbERldGFpbHM7XG4gICAgY29uc3Qge1xuICAgICAgbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZVxuICAgIH0gPSBjb25maWc7XG4gICAgY29uc3QgcGFydExpc3QgPSBsZXZlbERldGFpbHMucGFydExpc3Q7XG4gICAgY29uc3QgbG9hZGluZ1BhcnRzID0gISEoY29uZmlnLmxvd0xhdGVuY3lNb2RlICYmIHBhcnRMaXN0ICE9IG51bGwgJiYgcGFydExpc3QubGVuZ3RoICYmIGZyYWdtZW50SGludCk7XG4gICAgaWYgKGxvYWRpbmdQYXJ0cyAmJiBmcmFnbWVudEhpbnQgJiYgIXRoaXMuYml0cmF0ZVRlc3QpIHtcbiAgICAgIC8vIEluY2x1ZGUgaW5jb21wbGV0ZSBmcmFnbWVudCB3aXRoIHBhcnRzIGF0IGVuZFxuICAgICAgZnJhZ21lbnRzID0gZnJhZ21lbnRzLmNvbmNhdChmcmFnbWVudEhpbnQpO1xuICAgICAgZW5kU04gPSBmcmFnbWVudEhpbnQuc247XG4gICAgfVxuICAgIGxldCBmcmFnO1xuICAgIGlmIChidWZmZXJFbmQgPCBlbmQpIHtcbiAgICAgIGNvbnN0IGxvb2t1cFRvbGVyYW5jZSA9IGJ1ZmZlckVuZCA+IGVuZCAtIG1heEZyYWdMb29rVXBUb2xlcmFuY2UgPyAwIDogbWF4RnJhZ0xvb2tVcFRvbGVyYW5jZTtcbiAgICAgIC8vIFJlbW92ZSB0aGUgdG9sZXJhbmNlIGlmIGl0IHdvdWxkIHB1dCB0aGUgYnVmZmVyRW5kIHBhc3QgdGhlIGFjdHVhbCBlbmQgb2Ygc3RyZWFtXG4gICAgICAvLyBVc2VzIGJ1ZmZlciBhbmQgc2VxdWVuY2UgbnVtYmVyIHRvIGNhbGN1bGF0ZSBzd2l0Y2ggc2VnbWVudCAocmVxdWlyZWQgaWYgdXNpbmcgRVhULVgtRElTQ09OVElOVUlUWS1TRVFVRU5DRSlcbiAgICAgIGZyYWcgPSBmaW5kRnJhZ21lbnRCeVBUUyhmcmFnUHJldmlvdXMsIGZyYWdtZW50cywgYnVmZmVyRW5kLCBsb29rdXBUb2xlcmFuY2UpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyByZWFjaCBlbmQgb2YgcGxheWxpc3RcbiAgICAgIGZyYWcgPSBmcmFnbWVudHNbZnJhZ21lbnRzLmxlbmd0aCAtIDFdO1xuICAgIH1cbiAgICBpZiAoZnJhZykge1xuICAgICAgY29uc3QgY3VyU05JZHggPSBmcmFnLnNuIC0gbGV2ZWxEZXRhaWxzLnN0YXJ0U047XG4gICAgICAvLyBNb3ZlIGZyYWdQcmV2aW91cyBmb3J3YXJkIHRvIHN1cHBvcnQgZm9yY2luZyB0aGUgbmV4dCBmcmFnbWVudCB0byBsb2FkXG4gICAgICAvLyB3aGVuIHRoZSBidWZmZXIgY2F0Y2hlcyB1cCB0byBhIHByZXZpb3VzbHkgYnVmZmVyZWQgcmFuZ2UuXG4gICAgICBjb25zdCBmcmFnU3RhdGUgPSB0aGlzLmZyYWdtZW50VHJhY2tlci5nZXRTdGF0ZShmcmFnKTtcbiAgICAgIGlmIChmcmFnU3RhdGUgPT09IEZyYWdtZW50U3RhdGUuT0sgfHwgZnJhZ1N0YXRlID09PSBGcmFnbWVudFN0YXRlLlBBUlRJQUwgJiYgZnJhZy5nYXApIHtcbiAgICAgICAgZnJhZ1ByZXZpb3VzID0gZnJhZztcbiAgICAgIH1cbiAgICAgIGlmIChmcmFnUHJldmlvdXMgJiYgZnJhZy5zbiA9PT0gZnJhZ1ByZXZpb3VzLnNuICYmICghbG9hZGluZ1BhcnRzIHx8IHBhcnRMaXN0WzBdLmZyYWdtZW50LnNuID4gZnJhZy5zbikpIHtcbiAgICAgICAgLy8gRm9yY2UgdGhlIG5leHQgZnJhZ21lbnQgdG8gbG9hZCBpZiB0aGUgcHJldmlvdXMgb25lIHdhcyBhbHJlYWR5IHNlbGVjdGVkLiBUaGlzIGNhbiBvY2Nhc2lvbmFsbHkgaGFwcGVuIHdpdGhcbiAgICAgICAgLy8gbm9uLXVuaWZvcm0gZnJhZ21lbnQgZHVyYXRpb25zXG4gICAgICAgIGNvbnN0IHNhbWVMZXZlbCA9IGZyYWdQcmV2aW91cyAmJiBmcmFnLmxldmVsID09PSBmcmFnUHJldmlvdXMubGV2ZWw7XG4gICAgICAgIGlmIChzYW1lTGV2ZWwpIHtcbiAgICAgICAgICBjb25zdCBuZXh0RnJhZyA9IGZyYWdtZW50c1tjdXJTTklkeCArIDFdO1xuICAgICAgICAgIGlmIChmcmFnLnNuIDwgZW5kU04gJiYgdGhpcy5mcmFnbWVudFRyYWNrZXIuZ2V0U3RhdGUobmV4dEZyYWcpICE9PSBGcmFnbWVudFN0YXRlLk9LKSB7XG4gICAgICAgICAgICBmcmFnID0gbmV4dEZyYWc7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGZyYWcgPSBudWxsO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZnJhZztcbiAgfVxuICBzeW5jaHJvbml6ZVRvTGl2ZUVkZ2UobGV2ZWxEZXRhaWxzKSB7XG4gICAgY29uc3Qge1xuICAgICAgY29uZmlnLFxuICAgICAgbWVkaWFcbiAgICB9ID0gdGhpcztcbiAgICBpZiAoIW1lZGlhKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGxpdmVTeW5jUG9zaXRpb24gPSB0aGlzLmhscy5saXZlU3luY1Bvc2l0aW9uO1xuICAgIGNvbnN0IGN1cnJlbnRUaW1lID0gbWVkaWEuY3VycmVudFRpbWU7XG4gICAgY29uc3Qgc3RhcnQgPSBsZXZlbERldGFpbHMuZnJhZ21lbnRzWzBdLnN0YXJ0O1xuICAgIGNvbnN0IGVuZCA9IGxldmVsRGV0YWlscy5lZGdlO1xuICAgIGNvbnN0IHdpdGhpblNsaWRpbmdXaW5kb3cgPSBjdXJyZW50VGltZSA+PSBzdGFydCAtIGNvbmZpZy5tYXhGcmFnTG9va1VwVG9sZXJhbmNlICYmIGN1cnJlbnRUaW1lIDw9IGVuZDtcbiAgICAvLyBDb250aW51ZSBpZiB3ZSBjYW4gc2VlayBmb3J3YXJkIHRvIHN5bmMgcG9zaXRpb24gb3IgaWYgY3VycmVudCB0aW1lIGlzIG91dHNpZGUgb2Ygc2xpZGluZyB3aW5kb3dcbiAgICBpZiAobGl2ZVN5bmNQb3NpdGlvbiAhPT0gbnVsbCAmJiBtZWRpYS5kdXJhdGlvbiA+IGxpdmVTeW5jUG9zaXRpb24gJiYgKGN1cnJlbnRUaW1lIDwgbGl2ZVN5bmNQb3NpdGlvbiB8fCAhd2l0aGluU2xpZGluZ1dpbmRvdykpIHtcbiAgICAgIC8vIENvbnRpbnVlIGlmIGJ1ZmZlciBpcyBzdGFydmluZyBvciBpZiBjdXJyZW50IHRpbWUgaXMgYmVoaW5kIG1heCBsYXRlbmN5XG4gICAgICBjb25zdCBtYXhMYXRlbmN5ID0gY29uZmlnLmxpdmVNYXhMYXRlbmN5RHVyYXRpb24gIT09IHVuZGVmaW5lZCA/IGNvbmZpZy5saXZlTWF4TGF0ZW5jeUR1cmF0aW9uIDogY29uZmlnLmxpdmVNYXhMYXRlbmN5RHVyYXRpb25Db3VudCAqIGxldmVsRGV0YWlscy50YXJnZXRkdXJhdGlvbjtcbiAgICAgIGlmICghd2l0aGluU2xpZGluZ1dpbmRvdyAmJiBtZWRpYS5yZWFkeVN0YXRlIDwgNCB8fCBjdXJyZW50VGltZSA8IGVuZCAtIG1heExhdGVuY3kpIHtcbiAgICAgICAgaWYgKCF0aGlzLmxvYWRlZG1ldGFkYXRhKSB7XG4gICAgICAgICAgdGhpcy5uZXh0TG9hZFBvc2l0aW9uID0gbGl2ZVN5bmNQb3NpdGlvbjtcbiAgICAgICAgfVxuICAgICAgICAvLyBPbmx5IHNlZWsgaWYgcmVhZHkgYW5kIHRoZXJlIGlzIG5vdCBhIHNpZ25pZmljYW50IGZvcndhcmQgYnVmZmVyIGF2YWlsYWJsZSBmb3IgcGxheWJhY2tcbiAgICAgICAgaWYgKG1lZGlhLnJlYWR5U3RhdGUpIHtcbiAgICAgICAgICB0aGlzLndhcm4oYFBsYXliYWNrOiAke2N1cnJlbnRUaW1lLnRvRml4ZWQoMyl9IGlzIGxvY2F0ZWQgdG9vIGZhciBmcm9tIHRoZSBlbmQgb2YgbGl2ZSBzbGlkaW5nIHBsYXlsaXN0OiAke2VuZH0sIHJlc2V0IGN1cnJlbnRUaW1lIHRvIDogJHtsaXZlU3luY1Bvc2l0aW9uLnRvRml4ZWQoMyl9YCk7XG4gICAgICAgICAgbWVkaWEuY3VycmVudFRpbWUgPSBsaXZlU3luY1Bvc2l0aW9uO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG4gIGFsaWduUGxheWxpc3RzKGRldGFpbHMsIHByZXZpb3VzRGV0YWlscywgc3dpdGNoRGV0YWlscykge1xuICAgIC8vIEZJWE1FOiBJZiBub3QgZm9yIGBzaG91bGRBbGlnbk9uRGlzY29udGludWl0aWVzYCByZXF1aXJpbmcgZnJhZ1ByZXZpb3VzLmNjLFxuICAgIC8vICB0aGlzIGNvdWxkIGFsbCBnbyBpbiBsZXZlbC1oZWxwZXIgbWVyZ2VEZXRhaWxzKClcbiAgICBjb25zdCBsZW5ndGggPSBkZXRhaWxzLmZyYWdtZW50cy5sZW5ndGg7XG4gICAgaWYgKCFsZW5ndGgpIHtcbiAgICAgIHRoaXMud2FybihgTm8gZnJhZ21lbnRzIGluIGxpdmUgcGxheWxpc3RgKTtcbiAgICAgIHJldHVybiAwO1xuICAgIH1cbiAgICBjb25zdCBzbGlkaW5nU3RhcnQgPSBkZXRhaWxzLmZyYWdtZW50c1swXS5zdGFydDtcbiAgICBjb25zdCBmaXJzdExldmVsTG9hZCA9ICFwcmV2aW91c0RldGFpbHM7XG4gICAgY29uc3QgYWxpZ25lZCA9IGRldGFpbHMuYWxpZ25lZFNsaWRpbmcgJiYgaXNGaW5pdGVOdW1iZXIoc2xpZGluZ1N0YXJ0KTtcbiAgICBpZiAoZmlyc3RMZXZlbExvYWQgfHwgIWFsaWduZWQgJiYgIXNsaWRpbmdTdGFydCkge1xuICAgICAgY29uc3Qge1xuICAgICAgICBmcmFnUHJldmlvdXNcbiAgICAgIH0gPSB0aGlzO1xuICAgICAgYWxpZ25TdHJlYW0oZnJhZ1ByZXZpb3VzLCBzd2l0Y2hEZXRhaWxzLCBkZXRhaWxzKTtcbiAgICAgIGNvbnN0IGFsaWduZWRTbGlkaW5nU3RhcnQgPSBkZXRhaWxzLmZyYWdtZW50c1swXS5zdGFydDtcbiAgICAgIHRoaXMubG9nKGBMaXZlIHBsYXlsaXN0IHNsaWRpbmc6ICR7YWxpZ25lZFNsaWRpbmdTdGFydC50b0ZpeGVkKDIpfSBzdGFydC1zbjogJHtwcmV2aW91c0RldGFpbHMgPyBwcmV2aW91c0RldGFpbHMuc3RhcnRTTiA6ICduYSd9LT4ke2RldGFpbHMuc3RhcnRTTn0gcHJldi1zbjogJHtmcmFnUHJldmlvdXMgPyBmcmFnUHJldmlvdXMuc24gOiAnbmEnfSBmcmFnbWVudHM6ICR7bGVuZ3RofWApO1xuICAgICAgcmV0dXJuIGFsaWduZWRTbGlkaW5nU3RhcnQ7XG4gICAgfVxuICAgIHJldHVybiBzbGlkaW5nU3RhcnQ7XG4gIH1cbiAgd2FpdEZvckNkblR1bmVJbihkZXRhaWxzKSB7XG4gICAgLy8gV2FpdCBmb3IgTG93LUxhdGVuY3kgQ0ROIFR1bmUtaW4gdG8gZ2V0IGFuIHVwZGF0ZWQgcGxheWxpc3RcbiAgICBjb25zdCBhZHZhbmNlUGFydExpbWl0ID0gMztcbiAgICByZXR1cm4gZGV0YWlscy5saXZlICYmIGRldGFpbHMuY2FuQmxvY2tSZWxvYWQgJiYgZGV0YWlscy5wYXJ0VGFyZ2V0ICYmIGRldGFpbHMudHVuZUluR29hbCA+IE1hdGgubWF4KGRldGFpbHMucGFydEhvbGRCYWNrLCBkZXRhaWxzLnBhcnRUYXJnZXQgKiBhZHZhbmNlUGFydExpbWl0KTtcbiAgfVxuICBzZXRTdGFydFBvc2l0aW9uKGRldGFpbHMsIHNsaWRpbmcpIHtcbiAgICAvLyBjb21wdXRlIHN0YXJ0IHBvc2l0aW9uIGlmIHNldCB0byAtMS4gdXNlIGl0IHN0cmFpZ2h0IGF3YXkgaWYgdmFsdWUgaXMgZGVmaW5lZFxuICAgIGxldCBzdGFydFBvc2l0aW9uID0gdGhpcy5zdGFydFBvc2l0aW9uO1xuICAgIGlmIChzdGFydFBvc2l0aW9uIDwgc2xpZGluZykge1xuICAgICAgc3RhcnRQb3NpdGlvbiA9IC0xO1xuICAgIH1cbiAgICBpZiAoc3RhcnRQb3NpdGlvbiA9PT0gLTEgfHwgdGhpcy5sYXN0Q3VycmVudFRpbWUgPT09IC0xKSB7XG4gICAgICAvLyBVc2UgUGxheWxpc3QgRVhULVgtU1RBUlQ6VElNRS1PRkZTRVQgd2hlbiBzZXRcbiAgICAgIC8vIFByaW9yaXRpemUgTXVsdGl2YXJpYW50IFBsYXlsaXN0IG9mZnNldCBzbyB0aGF0IG1haW4sIGF1ZGlvLCBhbmQgc3VidGl0bGUgc3RyZWFtLWNvbnRyb2xsZXIgc3RhcnQgdGltZXMgbWF0Y2hcbiAgICAgIGNvbnN0IG9mZnNldEluTXVsdGl2YXJpYW50UGxheWxpc3QgPSB0aGlzLnN0YXJ0VGltZU9mZnNldCAhPT0gbnVsbDtcbiAgICAgIGNvbnN0IHN0YXJ0VGltZU9mZnNldCA9IG9mZnNldEluTXVsdGl2YXJpYW50UGxheWxpc3QgPyB0aGlzLnN0YXJ0VGltZU9mZnNldCA6IGRldGFpbHMuc3RhcnRUaW1lT2Zmc2V0O1xuICAgICAgaWYgKHN0YXJ0VGltZU9mZnNldCAhPT0gbnVsbCAmJiBpc0Zpbml0ZU51bWJlcihzdGFydFRpbWVPZmZzZXQpKSB7XG4gICAgICAgIHN0YXJ0UG9zaXRpb24gPSBzbGlkaW5nICsgc3RhcnRUaW1lT2Zmc2V0O1xuICAgICAgICBpZiAoc3RhcnRUaW1lT2Zmc2V0IDwgMCkge1xuICAgICAgICAgIHN0YXJ0UG9zaXRpb24gKz0gZGV0YWlscy50b3RhbGR1cmF0aW9uO1xuICAgICAgICB9XG4gICAgICAgIHN0YXJ0UG9zaXRpb24gPSBNYXRoLm1pbihNYXRoLm1heChzbGlkaW5nLCBzdGFydFBvc2l0aW9uKSwgc2xpZGluZyArIGRldGFpbHMudG90YWxkdXJhdGlvbik7XG4gICAgICAgIHRoaXMubG9nKGBTdGFydCB0aW1lIG9mZnNldCAke3N0YXJ0VGltZU9mZnNldH0gZm91bmQgaW4gJHtvZmZzZXRJbk11bHRpdmFyaWFudFBsYXlsaXN0ID8gJ211bHRpdmFyaWFudCcgOiAnbWVkaWEnfSBwbGF5bGlzdCwgYWRqdXN0IHN0YXJ0UG9zaXRpb24gdG8gJHtzdGFydFBvc2l0aW9ufWApO1xuICAgICAgICB0aGlzLnN0YXJ0UG9zaXRpb24gPSBzdGFydFBvc2l0aW9uO1xuICAgICAgfSBlbHNlIGlmIChkZXRhaWxzLmxpdmUpIHtcbiAgICAgICAgLy8gTGVhdmUgdGhpcy5zdGFydFBvc2l0aW9uIGF0IC0xLCBzbyB0aGF0IHdlIGNhbiB1c2UgYGdldEluaXRpYWxMaXZlRnJhZ21lbnRgIGxvZ2ljIHdoZW4gc3RhcnRQb3NpdGlvbiBoYXNcbiAgICAgICAgLy8gbm90IGJlZW4gc3BlY2lmaWVkIHZpYSB0aGUgY29uZmlnIG9yIGFuIGFzIGFuIGFyZ3VtZW50IHRvIHN0YXJ0TG9hZCAoIzM3MzYpLlxuICAgICAgICBzdGFydFBvc2l0aW9uID0gdGhpcy5obHMubGl2ZVN5bmNQb3NpdGlvbiB8fCBzbGlkaW5nO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5zdGFydFBvc2l0aW9uID0gc3RhcnRQb3NpdGlvbiA9IDA7XG4gICAgICB9XG4gICAgICB0aGlzLmxhc3RDdXJyZW50VGltZSA9IHN0YXJ0UG9zaXRpb247XG4gICAgfVxuICAgIHRoaXMubmV4dExvYWRQb3NpdGlvbiA9IHN0YXJ0UG9zaXRpb247XG4gIH1cbiAgZ2V0TG9hZFBvc2l0aW9uKCkge1xuICAgIGNvbnN0IHtcbiAgICAgIG1lZGlhXG4gICAgfSA9IHRoaXM7XG4gICAgLy8gaWYgd2UgaGF2ZSBub3QgeWV0IGxvYWRlZCBhbnkgZnJhZ21lbnQsIHN0YXJ0IGxvYWRpbmcgZnJvbSBzdGFydCBwb3NpdGlvblxuICAgIGxldCBwb3MgPSAwO1xuICAgIGlmICh0aGlzLmxvYWRlZG1ldGFkYXRhICYmIG1lZGlhKSB7XG4gICAgICBwb3MgPSBtZWRpYS5jdXJyZW50VGltZTtcbiAgICB9IGVsc2UgaWYgKHRoaXMubmV4dExvYWRQb3NpdGlvbikge1xuICAgICAgcG9zID0gdGhpcy5uZXh0TG9hZFBvc2l0aW9uO1xuICAgIH1cbiAgICByZXR1cm4gcG9zO1xuICB9XG4gIGhhbmRsZUZyYWdMb2FkQWJvcnRlZChmcmFnLCBwYXJ0KSB7XG4gICAgaWYgKHRoaXMudHJhbnNtdXhlciAmJiBmcmFnLnNuICE9PSAnaW5pdFNlZ21lbnQnICYmIGZyYWcuc3RhdHMuYWJvcnRlZCkge1xuICAgICAgdGhpcy53YXJuKGBGcmFnbWVudCAke2ZyYWcuc259JHtwYXJ0ID8gJyBwYXJ0ICcgKyBwYXJ0LmluZGV4IDogJyd9IG9mIGxldmVsICR7ZnJhZy5sZXZlbH0gd2FzIGFib3J0ZWRgKTtcbiAgICAgIHRoaXMucmVzZXRGcmFnbWVudExvYWRpbmcoZnJhZyk7XG4gICAgfVxuICB9XG4gIHJlc2V0RnJhZ21lbnRMb2FkaW5nKGZyYWcpIHtcbiAgICBpZiAoIXRoaXMuZnJhZ0N1cnJlbnQgfHwgIXRoaXMuZnJhZ0NvbnRleHRDaGFuZ2VkKGZyYWcpICYmIHRoaXMuc3RhdGUgIT09IFN0YXRlLkZSQUdfTE9BRElOR19XQUlUSU5HX1JFVFJZKSB7XG4gICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICB9XG4gIH1cbiAgb25GcmFnbWVudE9yS2V5TG9hZEVycm9yKGZpbHRlclR5cGUsIGRhdGEpIHtcbiAgICBpZiAoZGF0YS5jaHVua01ldGEgJiYgIWRhdGEuZnJhZykge1xuICAgICAgY29uc3QgY29udGV4dCA9IHRoaXMuZ2V0Q3VycmVudENvbnRleHQoZGF0YS5jaHVua01ldGEpO1xuICAgICAgaWYgKGNvbnRleHQpIHtcbiAgICAgICAgZGF0YS5mcmFnID0gY29udGV4dC5mcmFnO1xuICAgICAgfVxuICAgIH1cbiAgICBjb25zdCBmcmFnID0gZGF0YS5mcmFnO1xuICAgIC8vIEhhbmRsZSBmcmFnIGVycm9yIHJlbGF0ZWQgdG8gY2FsbGVyJ3MgZmlsdGVyVHlwZVxuICAgIGlmICghZnJhZyB8fCBmcmFnLnR5cGUgIT09IGZpbHRlclR5cGUgfHwgIXRoaXMubGV2ZWxzKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmICh0aGlzLmZyYWdDb250ZXh0Q2hhbmdlZChmcmFnKSkge1xuICAgICAgdmFyIF90aGlzJGZyYWdDdXJyZW50MjtcbiAgICAgIHRoaXMud2FybihgRnJhZyBsb2FkIGVycm9yIG11c3QgbWF0Y2ggY3VycmVudCBmcmFnIHRvIHJldHJ5ICR7ZnJhZy51cmx9ID4gJHsoX3RoaXMkZnJhZ0N1cnJlbnQyID0gdGhpcy5mcmFnQ3VycmVudCkgPT0gbnVsbCA/IHZvaWQgMCA6IF90aGlzJGZyYWdDdXJyZW50Mi51cmx9YCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGdhcFRhZ0VuY291bnRlcmVkID0gZGF0YS5kZXRhaWxzID09PSBFcnJvckRldGFpbHMuRlJBR19HQVA7XG4gICAgaWYgKGdhcFRhZ0VuY291bnRlcmVkKSB7XG4gICAgICB0aGlzLmZyYWdtZW50VHJhY2tlci5mcmFnQnVmZmVyZWQoZnJhZywgdHJ1ZSk7XG4gICAgfVxuICAgIC8vIGtlZXAgcmV0cnlpbmcgdW50aWwgdGhlIGxpbWl0IHdpbGwgYmUgcmVhY2hlZFxuICAgIGNvbnN0IGVycm9yQWN0aW9uID0gZGF0YS5lcnJvckFjdGlvbjtcbiAgICBjb25zdCB7XG4gICAgICBhY3Rpb24sXG4gICAgICByZXRyeUNvdW50ID0gMCxcbiAgICAgIHJldHJ5Q29uZmlnXG4gICAgfSA9IGVycm9yQWN0aW9uIHx8IHt9O1xuICAgIGlmIChlcnJvckFjdGlvbiAmJiBhY3Rpb24gPT09IE5ldHdvcmtFcnJvckFjdGlvbi5SZXRyeVJlcXVlc3QgJiYgcmV0cnlDb25maWcpIHtcbiAgICAgIHRoaXMucmVzZXRTdGFydFdoZW5Ob3RMb2FkZWQodGhpcy5sZXZlbExhc3RMb2FkZWQpO1xuICAgICAgY29uc3QgZGVsYXkgPSBnZXRSZXRyeURlbGF5KHJldHJ5Q29uZmlnLCByZXRyeUNvdW50KTtcbiAgICAgIHRoaXMud2FybihgRnJhZ21lbnQgJHtmcmFnLnNufSBvZiAke2ZpbHRlclR5cGV9ICR7ZnJhZy5sZXZlbH0gZXJyb3JlZCB3aXRoICR7ZGF0YS5kZXRhaWxzfSwgcmV0cnlpbmcgbG9hZGluZyAke3JldHJ5Q291bnQgKyAxfS8ke3JldHJ5Q29uZmlnLm1heE51bVJldHJ5fSBpbiAke2RlbGF5fW1zYCk7XG4gICAgICBlcnJvckFjdGlvbi5yZXNvbHZlZCA9IHRydWU7XG4gICAgICB0aGlzLnJldHJ5RGF0ZSA9IHNlbGYucGVyZm9ybWFuY2Uubm93KCkgKyBkZWxheTtcbiAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5GUkFHX0xPQURJTkdfV0FJVElOR19SRVRSWTtcbiAgICB9IGVsc2UgaWYgKHJldHJ5Q29uZmlnICYmIGVycm9yQWN0aW9uKSB7XG4gICAgICB0aGlzLnJlc2V0RnJhZ21lbnRFcnJvcnMoZmlsdGVyVHlwZSk7XG4gICAgICBpZiAocmV0cnlDb3VudCA8IHJldHJ5Q29uZmlnLm1heE51bVJldHJ5KSB7XG4gICAgICAgIC8vIE5ldHdvcmsgcmV0cnkgaXMgc2tpcHBlZCB3aGVuIGxldmVsIHN3aXRjaCBpcyBwcmVmZXJyZWRcbiAgICAgICAgaWYgKCFnYXBUYWdFbmNvdW50ZXJlZCAmJiBhY3Rpb24gIT09IE5ldHdvcmtFcnJvckFjdGlvbi5SZW1vdmVBbHRlcm5hdGVQZXJtYW5lbnRseSkge1xuICAgICAgICAgIGVycm9yQWN0aW9uLnJlc29sdmVkID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oYCR7ZGF0YS5kZXRhaWxzfSByZWFjaGVkIG9yIGV4Y2VlZGVkIG1heCByZXRyeSAoJHtyZXRyeUNvdW50fSlgKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAoKGVycm9yQWN0aW9uID09IG51bGwgPyB2b2lkIDAgOiBlcnJvckFjdGlvbi5hY3Rpb24pID09PSBOZXR3b3JrRXJyb3JBY3Rpb24uU2VuZEFsdGVybmF0ZVRvUGVuYWx0eUJveCkge1xuICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLldBSVRJTkdfTEVWRUw7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5FUlJPUjtcbiAgICB9XG4gICAgLy8gUGVyZm9ybSBuZXh0IGFzeW5jIHRpY2sgc29vbmVyIHRvIHNwZWVkIHVwIGVycm9yIGFjdGlvbiByZXNvbHV0aW9uXG4gICAgdGhpcy50aWNrSW1tZWRpYXRlKCk7XG4gIH1cbiAgcmVkdWNlTGVuZ3RoQW5kRmx1c2hCdWZmZXIoZGF0YSkge1xuICAgIC8vIGlmIGluIGFwcGVuZGluZyBzdGF0ZVxuICAgIGlmICh0aGlzLnN0YXRlID09PSBTdGF0ZS5QQVJTSU5HIHx8IHRoaXMuc3RhdGUgPT09IFN0YXRlLlBBUlNFRCkge1xuICAgICAgY29uc3QgZnJhZyA9IGRhdGEuZnJhZztcbiAgICAgIGNvbnN0IHBsYXlsaXN0VHlwZSA9IGRhdGEucGFyZW50O1xuICAgICAgY29uc3QgYnVmZmVyZWRJbmZvID0gdGhpcy5nZXRGd2RCdWZmZXJJbmZvKHRoaXMubWVkaWFCdWZmZXIsIHBsYXlsaXN0VHlwZSk7XG4gICAgICAvLyAwLjUgOiB0b2xlcmFuY2UgbmVlZGVkIGFzIHNvbWUgYnJvd3NlcnMgc3RhbGxzIHBsYXliYWNrIGJlZm9yZSByZWFjaGluZyBidWZmZXJlZCBlbmRcbiAgICAgIC8vIHJlZHVjZSBtYXggYnVmIGxlbiBpZiBjdXJyZW50IHBvc2l0aW9uIGlzIGJ1ZmZlcmVkXG4gICAgICBjb25zdCBidWZmZXJlZCA9IGJ1ZmZlcmVkSW5mbyAmJiBidWZmZXJlZEluZm8ubGVuID4gMC41O1xuICAgICAgaWYgKGJ1ZmZlcmVkKSB7XG4gICAgICAgIHRoaXMucmVkdWNlTWF4QnVmZmVyTGVuZ3RoKGJ1ZmZlcmVkSW5mby5sZW4sIChmcmFnID09IG51bGwgPyB2b2lkIDAgOiBmcmFnLmR1cmF0aW9uKSB8fCAxMCk7XG4gICAgICB9XG4gICAgICBjb25zdCBmbHVzaEJ1ZmZlciA9ICFidWZmZXJlZDtcbiAgICAgIGlmIChmbHVzaEJ1ZmZlcikge1xuICAgICAgICAvLyBjdXJyZW50IHBvc2l0aW9uIGlzIG5vdCBidWZmZXJlZCwgYnV0IGJyb3dzZXIgaXMgc3RpbGwgY29tcGxhaW5pbmcgYWJvdXQgYnVmZmVyIGZ1bGwgZXJyb3JcbiAgICAgICAgLy8gdGhpcyBoYXBwZW5zIG9uIElFL0VkZ2UsIHJlZmVyIHRvIGh0dHBzOi8vZ2l0aHViLmNvbS92aWRlby1kZXYvaGxzLmpzL3B1bGwvNzA4XG4gICAgICAgIC8vIGluIHRoYXQgY2FzZSBmbHVzaCB0aGUgd2hvbGUgYXVkaW8gYnVmZmVyIHRvIHJlY292ZXJcbiAgICAgICAgdGhpcy53YXJuKGBCdWZmZXIgZnVsbCBlcnJvciB3aGlsZSBtZWRpYS5jdXJyZW50VGltZSBpcyBub3QgYnVmZmVyZWQsIGZsdXNoICR7cGxheWxpc3RUeXBlfSBidWZmZXJgKTtcbiAgICAgIH1cbiAgICAgIGlmIChmcmFnKSB7XG4gICAgICAgIHRoaXMuZnJhZ21lbnRUcmFja2VyLnJlbW92ZUZyYWdtZW50KGZyYWcpO1xuICAgICAgICB0aGlzLm5leHRMb2FkUG9zaXRpb24gPSBmcmFnLnN0YXJ0O1xuICAgICAgfVxuICAgICAgdGhpcy5yZXNldExvYWRpbmdTdGF0ZSgpO1xuICAgICAgcmV0dXJuIGZsdXNoQnVmZmVyO1xuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgcmVzZXRGcmFnbWVudEVycm9ycyhmaWx0ZXJUeXBlKSB7XG4gICAgaWYgKGZpbHRlclR5cGUgPT09IFBsYXlsaXN0TGV2ZWxUeXBlLkFVRElPKSB7XG4gICAgICAvLyBSZXNldCBjdXJyZW50IGZyYWdtZW50IHNpbmNlIGF1ZGlvIHRyYWNrIGF1ZGlvIGlzIGVzc2VudGlhbCBhbmQgbWF5IG5vdCBoYXZlIGEgZmFpbC1vdmVyIHRyYWNrXG4gICAgICB0aGlzLmZyYWdDdXJyZW50ID0gbnVsbDtcbiAgICB9XG4gICAgLy8gRnJhZ21lbnQgZXJyb3JzIHRoYXQgcmVzdWx0IGluIGEgbGV2ZWwgc3dpdGNoIG9yIHJlZHVuZGFudCBmYWlsLW92ZXJcbiAgICAvLyBzaG91bGQgcmVzZXQgdGhlIHN0cmVhbSBjb250cm9sbGVyIHN0YXRlIHRvIGlkbGVcbiAgICBpZiAoIXRoaXMubG9hZGVkbWV0YWRhdGEpIHtcbiAgICAgIHRoaXMuc3RhcnRGcmFnUmVxdWVzdGVkID0gZmFsc2U7XG4gICAgfVxuICAgIGlmICh0aGlzLnN0YXRlICE9PSBTdGF0ZS5TVE9QUEVEKSB7XG4gICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICB9XG4gIH1cbiAgYWZ0ZXJCdWZmZXJGbHVzaGVkKG1lZGlhLCBidWZmZXJUeXBlLCBwbGF5bGlzdFR5cGUpIHtcbiAgICBpZiAoIW1lZGlhKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIEFmdGVyIHN1Y2Nlc3NmdWwgYnVmZmVyIGZsdXNoaW5nLCBmaWx0ZXIgZmx1c2hlZCBmcmFnbWVudHMgZnJvbSBidWZmZXJlZEZyYWdzIHVzZSBtZWRpYUJ1ZmZlcmVkIGluc3RlYWQgb2YgbWVkaWFcbiAgICAvLyAoc28gdGhhdCB3ZSB3aWxsIGNoZWNrIGFnYWluc3QgdmlkZW8uYnVmZmVyZWQgcmFuZ2VzIGluIGNhc2Ugb2YgYWx0IGF1ZGlvIHRyYWNrKVxuICAgIGNvbnN0IGJ1ZmZlcmVkVGltZVJhbmdlcyA9IEJ1ZmZlckhlbHBlci5nZXRCdWZmZXJlZChtZWRpYSk7XG4gICAgdGhpcy5mcmFnbWVudFRyYWNrZXIuZGV0ZWN0RXZpY3RlZEZyYWdtZW50cyhidWZmZXJUeXBlLCBidWZmZXJlZFRpbWVSYW5nZXMsIHBsYXlsaXN0VHlwZSk7XG4gICAgaWYgKHRoaXMuc3RhdGUgPT09IFN0YXRlLkVOREVEKSB7XG4gICAgICB0aGlzLnJlc2V0TG9hZGluZ1N0YXRlKCk7XG4gICAgfVxuICB9XG4gIHJlc2V0TG9hZGluZ1N0YXRlKCkge1xuICAgIHRoaXMubG9nKCdSZXNldCBsb2FkaW5nIHN0YXRlJyk7XG4gICAgdGhpcy5mcmFnQ3VycmVudCA9IG51bGw7XG4gICAgdGhpcy5mcmFnUHJldmlvdXMgPSBudWxsO1xuICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICB9XG4gIHJlc2V0U3RhcnRXaGVuTm90TG9hZGVkKGxldmVsKSB7XG4gICAgLy8gaWYgbG9hZGVkbWV0YWRhdGEgaXMgbm90IHNldCwgaXQgbWVhbnMgdGhhdCBmaXJzdCBmcmFnIHJlcXVlc3QgZmFpbGVkXG4gICAgLy8gaW4gdGhhdCBjYXNlLCByZXNldCBzdGFydEZyYWdSZXF1ZXN0ZWQgZmxhZ1xuICAgIGlmICghdGhpcy5sb2FkZWRtZXRhZGF0YSkge1xuICAgICAgdGhpcy5zdGFydEZyYWdSZXF1ZXN0ZWQgPSBmYWxzZTtcbiAgICAgIGNvbnN0IGRldGFpbHMgPSBsZXZlbCA/IGxldmVsLmRldGFpbHMgOiBudWxsO1xuICAgICAgaWYgKGRldGFpbHMgIT0gbnVsbCAmJiBkZXRhaWxzLmxpdmUpIHtcbiAgICAgICAgLy8gVXBkYXRlIHRoZSBzdGFydCBwb3NpdGlvbiBhbmQgcmV0dXJuIHRvIElETEUgdG8gcmVjb3ZlciBsaXZlIHN0YXJ0XG4gICAgICAgIHRoaXMuc3RhcnRQb3NpdGlvbiA9IC0xO1xuICAgICAgICB0aGlzLnNldFN0YXJ0UG9zaXRpb24oZGV0YWlscywgMCk7XG4gICAgICAgIHRoaXMucmVzZXRMb2FkaW5nU3RhdGUoKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMubmV4dExvYWRQb3NpdGlvbiA9IHRoaXMuc3RhcnRQb3NpdGlvbjtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgcmVzZXRXaGVuTWlzc2luZ0NvbnRleHQoY2h1bmtNZXRhKSB7XG4gICAgdGhpcy53YXJuKGBUaGUgbG9hZGluZyBjb250ZXh0IGNoYW5nZWQgd2hpbGUgYnVmZmVyaW5nIGZyYWdtZW50ICR7Y2h1bmtNZXRhLnNufSBvZiBsZXZlbCAke2NodW5rTWV0YS5sZXZlbH0uIFRoaXMgY2h1bmsgd2lsbCBub3QgYmUgYnVmZmVyZWQuYCk7XG4gICAgdGhpcy5yZW1vdmVVbmJ1ZmZlcmVkRnJhZ3MoKTtcbiAgICB0aGlzLnJlc2V0U3RhcnRXaGVuTm90TG9hZGVkKHRoaXMubGV2ZWxMYXN0TG9hZGVkKTtcbiAgICB0aGlzLnJlc2V0TG9hZGluZ1N0YXRlKCk7XG4gIH1cbiAgcmVtb3ZlVW5idWZmZXJlZEZyYWdzKHN0YXJ0ID0gMCkge1xuICAgIHRoaXMuZnJhZ21lbnRUcmFja2VyLnJlbW92ZUZyYWdtZW50c0luUmFuZ2Uoc3RhcnQsIEluZmluaXR5LCB0aGlzLnBsYXlsaXN0VHlwZSwgZmFsc2UsIHRydWUpO1xuICB9XG4gIHVwZGF0ZUxldmVsVGltaW5nKGZyYWcsIHBhcnQsIGxldmVsLCBwYXJ0aWFsKSB7XG4gICAgdmFyIF90aGlzJHRyYW5zbXV4ZXI7XG4gICAgY29uc3QgZGV0YWlscyA9IGxldmVsLmRldGFpbHM7XG4gICAgaWYgKCFkZXRhaWxzKSB7XG4gICAgICB0aGlzLndhcm4oJ2xldmVsLmRldGFpbHMgdW5kZWZpbmVkJyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHBhcnNlZCA9IE9iamVjdC5rZXlzKGZyYWcuZWxlbWVudGFyeVN0cmVhbXMpLnJlZHVjZSgocmVzdWx0LCB0eXBlKSA9PiB7XG4gICAgICBjb25zdCBpbmZvID0gZnJhZy5lbGVtZW50YXJ5U3RyZWFtc1t0eXBlXTtcbiAgICAgIGlmIChpbmZvKSB7XG4gICAgICAgIGNvbnN0IHBhcnNlZER1cmF0aW9uID0gaW5mby5lbmRQVFMgLSBpbmZvLnN0YXJ0UFRTO1xuICAgICAgICBpZiAocGFyc2VkRHVyYXRpb24gPD0gMCkge1xuICAgICAgICAgIC8vIERlc3Ryb3kgdGhlIHRyYW5zbXV4ZXIgYWZ0ZXIgaXQncyBuZXh0IHRpbWUgb2Zmc2V0IGZhaWxlZCB0byBhZHZhbmNlIGJlY2F1c2UgZHVyYXRpb24gd2FzIDw9IDAuXG4gICAgICAgICAgLy8gVGhlIG5ldyB0cmFuc211eGVyIHdpbGwgYmUgY29uZmlndXJlZCB3aXRoIGEgdGltZSBvZmZzZXQgbWF0Y2hpbmcgdGhlIG5leHQgZnJhZ21lbnQgc3RhcnQsXG4gICAgICAgICAgLy8gcHJldmVudGluZyB0aGUgdGltZWxpbmUgZnJvbSBzaGlmdGluZy5cbiAgICAgICAgICB0aGlzLndhcm4oYENvdWxkIG5vdCBwYXJzZSBmcmFnbWVudCAke2ZyYWcuc259ICR7dHlwZX0gZHVyYXRpb24gcmVsaWFibHkgKCR7cGFyc2VkRHVyYXRpb259KWApO1xuICAgICAgICAgIHJldHVybiByZXN1bHQgfHwgZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgZHJpZnQgPSBwYXJ0aWFsID8gMCA6IHVwZGF0ZUZyYWdQVFNEVFMoZGV0YWlscywgZnJhZywgaW5mby5zdGFydFBUUywgaW5mby5lbmRQVFMsIGluZm8uc3RhcnREVFMsIGluZm8uZW5kRFRTKTtcbiAgICAgICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuTEVWRUxfUFRTX1VQREFURUQsIHtcbiAgICAgICAgICBkZXRhaWxzLFxuICAgICAgICAgIGxldmVsLFxuICAgICAgICAgIGRyaWZ0LFxuICAgICAgICAgIHR5cGUsXG4gICAgICAgICAgZnJhZyxcbiAgICAgICAgICBzdGFydDogaW5mby5zdGFydFBUUyxcbiAgICAgICAgICBlbmQ6IGluZm8uZW5kUFRTXG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH1cbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfSwgZmFsc2UpO1xuICAgIGlmICghcGFyc2VkICYmICgoX3RoaXMkdHJhbnNtdXhlciA9IHRoaXMudHJhbnNtdXhlcikgPT0gbnVsbCA/IHZvaWQgMCA6IF90aGlzJHRyYW5zbXV4ZXIuZXJyb3IpID09PSBudWxsKSB7XG4gICAgICBjb25zdCBlcnJvciA9IG5ldyBFcnJvcihgRm91bmQgbm8gbWVkaWEgaW4gZnJhZ21lbnQgJHtmcmFnLnNufSBvZiBsZXZlbCAke2ZyYWcubGV2ZWx9IHJlc2V0dGluZyB0cmFuc211eGVyIHRvIGZhbGxiYWNrIHRvIHBsYXlsaXN0IHRpbWluZ2ApO1xuICAgICAgaWYgKGxldmVsLmZyYWdtZW50RXJyb3IgPT09IDApIHtcbiAgICAgICAgLy8gTWFyayBhbmQgdHJhY2sgdGhlIG9kZCBlbXB0eSBzZWdtZW50IGFzIGEgZ2FwIHRvIGF2b2lkIHJlbG9hZGluZ1xuICAgICAgICBsZXZlbC5mcmFnbWVudEVycm9yKys7XG4gICAgICAgIGZyYWcuZ2FwID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5mcmFnbWVudFRyYWNrZXIucmVtb3ZlRnJhZ21lbnQoZnJhZyk7XG4gICAgICAgIHRoaXMuZnJhZ21lbnRUcmFja2VyLmZyYWdCdWZmZXJlZChmcmFnLCB0cnVlKTtcbiAgICAgIH1cbiAgICAgIHRoaXMud2FybihlcnJvci5tZXNzYWdlKTtcbiAgICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLkVSUk9SLCB7XG4gICAgICAgIHR5cGU6IEVycm9yVHlwZXMuTUVESUFfRVJST1IsXG4gICAgICAgIGRldGFpbHM6IEVycm9yRGV0YWlscy5GUkFHX1BBUlNJTkdfRVJST1IsXG4gICAgICAgIGZhdGFsOiBmYWxzZSxcbiAgICAgICAgZXJyb3IsXG4gICAgICAgIGZyYWcsXG4gICAgICAgIHJlYXNvbjogYEZvdW5kIG5vIG1lZGlhIGluIG1zbiAke2ZyYWcuc259IG9mIGxldmVsIFwiJHtsZXZlbC51cmx9XCJgXG4gICAgICB9KTtcbiAgICAgIGlmICghdGhpcy5obHMpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdGhpcy5yZXNldFRyYW5zbXV4ZXIoKTtcbiAgICAgIC8vIEZvciB0aGlzIGVycm9yIGZhbGx0aHJvdWdoLiBNYXJraW5nIHBhcnNlZCB3aWxsIGFsbG93IGFkdmFuY2luZyB0byBuZXh0IGZyYWdtZW50LlxuICAgIH1cbiAgICB0aGlzLnN0YXRlID0gU3RhdGUuUEFSU0VEO1xuICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLkZSQUdfUEFSU0VELCB7XG4gICAgICBmcmFnLFxuICAgICAgcGFydFxuICAgIH0pO1xuICB9XG4gIHJlc2V0VHJhbnNtdXhlcigpIHtcbiAgICBpZiAodGhpcy50cmFuc211eGVyKSB7XG4gICAgICB0aGlzLnRyYW5zbXV4ZXIuZGVzdHJveSgpO1xuICAgICAgdGhpcy50cmFuc211eGVyID0gbnVsbDtcbiAgICB9XG4gIH1cbiAgcmVjb3ZlcldvcmtlckVycm9yKGRhdGEpIHtcbiAgICBpZiAoZGF0YS5ldmVudCA9PT0gJ2RlbXV4ZXJXb3JrZXInKSB7XG4gICAgICB0aGlzLmZyYWdtZW50VHJhY2tlci5yZW1vdmVBbGxGcmFnbWVudHMoKTtcbiAgICAgIHRoaXMucmVzZXRUcmFuc211eGVyKCk7XG4gICAgICB0aGlzLnJlc2V0U3RhcnRXaGVuTm90TG9hZGVkKHRoaXMubGV2ZWxMYXN0TG9hZGVkKTtcbiAgICAgIHRoaXMucmVzZXRMb2FkaW5nU3RhdGUoKTtcbiAgICB9XG4gIH1cbiAgc2V0IHN0YXRlKG5leHRTdGF0ZSkge1xuICAgIGNvbnN0IHByZXZpb3VzU3RhdGUgPSB0aGlzLl9zdGF0ZTtcbiAgICBpZiAocHJldmlvdXNTdGF0ZSAhPT0gbmV4dFN0YXRlKSB7XG4gICAgICB0aGlzLl9zdGF0ZSA9IG5leHRTdGF0ZTtcbiAgICAgIHRoaXMubG9nKGAke3ByZXZpb3VzU3RhdGV9LT4ke25leHRTdGF0ZX1gKTtcbiAgICB9XG4gIH1cbiAgZ2V0IHN0YXRlKCkge1xuICAgIHJldHVybiB0aGlzLl9zdGF0ZTtcbiAgfVxufVxuXG5jbGFzcyBDaHVua0NhY2hlIHtcbiAgY29uc3RydWN0b3IoKSB7XG4gICAgdGhpcy5jaHVua3MgPSBbXTtcbiAgICB0aGlzLmRhdGFMZW5ndGggPSAwO1xuICB9XG4gIHB1c2goY2h1bmspIHtcbiAgICB0aGlzLmNodW5rcy5wdXNoKGNodW5rKTtcbiAgICB0aGlzLmRhdGFMZW5ndGggKz0gY2h1bmsubGVuZ3RoO1xuICB9XG4gIGZsdXNoKCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGNodW5rcyxcbiAgICAgIGRhdGFMZW5ndGhcbiAgICB9ID0gdGhpcztcbiAgICBsZXQgcmVzdWx0O1xuICAgIGlmICghY2h1bmtzLmxlbmd0aCkge1xuICAgICAgcmV0dXJuIG5ldyBVaW50OEFycmF5KDApO1xuICAgIH0gZWxzZSBpZiAoY2h1bmtzLmxlbmd0aCA9PT0gMSkge1xuICAgICAgcmVzdWx0ID0gY2h1bmtzWzBdO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXN1bHQgPSBjb25jYXRVaW50OEFycmF5cyhjaHVua3MsIGRhdGFMZW5ndGgpO1xuICAgIH1cbiAgICB0aGlzLnJlc2V0KCk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuICByZXNldCgpIHtcbiAgICB0aGlzLmNodW5rcy5sZW5ndGggPSAwO1xuICAgIHRoaXMuZGF0YUxlbmd0aCA9IDA7XG4gIH1cbn1cbmZ1bmN0aW9uIGNvbmNhdFVpbnQ4QXJyYXlzKGNodW5rcywgZGF0YUxlbmd0aCkge1xuICBjb25zdCByZXN1bHQgPSBuZXcgVWludDhBcnJheShkYXRhTGVuZ3RoKTtcbiAgbGV0IG9mZnNldCA9IDA7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgY2h1bmtzLmxlbmd0aDsgaSsrKSB7XG4gICAgY29uc3QgY2h1bmsgPSBjaHVua3NbaV07XG4gICAgcmVzdWx0LnNldChjaHVuaywgb2Zmc2V0KTtcbiAgICBvZmZzZXQgKz0gY2h1bmsubGVuZ3RoO1xuICB9XG4gIHJldHVybiByZXN1bHQ7XG59XG5cbi8vIGVuc3VyZSB0aGUgd29ya2VyIGVuZHMgdXAgaW4gdGhlIGJ1bmRsZVxuLy8gSWYgdGhlIHdvcmtlciBzaG91bGQgbm90IGJlIGluY2x1ZGVkIHRoaXMgZ2V0cyBhbGlhc2VkIHRvIGVtcHR5LmpzXG5mdW5jdGlvbiBoYXNVTURXb3JrZXIoKSB7XG4gIHJldHVybiB0eXBlb2YgX19ITFNfV09SS0VSX0JVTkRMRV9fID09PSAnZnVuY3Rpb24nO1xufVxuZnVuY3Rpb24gaW5qZWN0V29ya2VyKCkge1xuICBjb25zdCBibG9iID0gbmV3IHNlbGYuQmxvYihbYHZhciBleHBvcnRzPXt9O3ZhciBtb2R1bGU9e2V4cG9ydHM6ZXhwb3J0c307ZnVuY3Rpb24gZGVmaW5lKGYpe2YoKX07ZGVmaW5lLmFtZD10cnVlOygke19fSExTX1dPUktFUl9CVU5ETEVfXy50b1N0cmluZygpfSkodHJ1ZSk7YF0sIHtcbiAgICB0eXBlOiAndGV4dC9qYXZhc2NyaXB0J1xuICB9KTtcbiAgY29uc3Qgb2JqZWN0VVJMID0gc2VsZi5VUkwuY3JlYXRlT2JqZWN0VVJMKGJsb2IpO1xuICBjb25zdCB3b3JrZXIgPSBuZXcgc2VsZi5Xb3JrZXIob2JqZWN0VVJMKTtcbiAgcmV0dXJuIHtcbiAgICB3b3JrZXIsXG4gICAgb2JqZWN0VVJMXG4gIH07XG59XG5mdW5jdGlvbiBsb2FkV29ya2VyKHBhdGgpIHtcbiAgY29uc3Qgc2NyaXB0VVJMID0gbmV3IHNlbGYuVVJMKHBhdGgsIHNlbGYubG9jYXRpb24uaHJlZikuaHJlZjtcbiAgY29uc3Qgd29ya2VyID0gbmV3IHNlbGYuV29ya2VyKHNjcmlwdFVSTCk7XG4gIHJldHVybiB7XG4gICAgd29ya2VyLFxuICAgIHNjcmlwdFVSTFxuICB9O1xufVxuXG5mdW5jdGlvbiBkdW1teVRyYWNrKHR5cGUgPSAnJywgaW5wdXRUaW1lU2NhbGUgPSA5MDAwMCkge1xuICByZXR1cm4ge1xuICAgIHR5cGUsXG4gICAgaWQ6IC0xLFxuICAgIHBpZDogLTEsXG4gICAgaW5wdXRUaW1lU2NhbGUsXG4gICAgc2VxdWVuY2VOdW1iZXI6IC0xLFxuICAgIHNhbXBsZXM6IFtdLFxuICAgIGRyb3BwZWQ6IDBcbiAgfTtcbn1cblxuY2xhc3MgQmFzZUF1ZGlvRGVtdXhlciB7XG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHRoaXMuX2F1ZGlvVHJhY2sgPSB2b2lkIDA7XG4gICAgdGhpcy5faWQzVHJhY2sgPSB2b2lkIDA7XG4gICAgdGhpcy5mcmFtZUluZGV4ID0gMDtcbiAgICB0aGlzLmNhY2hlZERhdGEgPSBudWxsO1xuICAgIHRoaXMuYmFzZVBUUyA9IG51bGw7XG4gICAgdGhpcy5pbml0UFRTID0gbnVsbDtcbiAgICB0aGlzLmxhc3RQVFMgPSBudWxsO1xuICB9XG4gIHJlc2V0SW5pdFNlZ21lbnQoaW5pdFNlZ21lbnQsIGF1ZGlvQ29kZWMsIHZpZGVvQ29kZWMsIHRyYWNrRHVyYXRpb24pIHtcbiAgICB0aGlzLl9pZDNUcmFjayA9IHtcbiAgICAgIHR5cGU6ICdpZDMnLFxuICAgICAgaWQ6IDMsXG4gICAgICBwaWQ6IC0xLFxuICAgICAgaW5wdXRUaW1lU2NhbGU6IDkwMDAwLFxuICAgICAgc2VxdWVuY2VOdW1iZXI6IDAsXG4gICAgICBzYW1wbGVzOiBbXSxcbiAgICAgIGRyb3BwZWQ6IDBcbiAgICB9O1xuICB9XG4gIHJlc2V0VGltZVN0YW1wKGRlYXVsdFRpbWVzdGFtcCkge1xuICAgIHRoaXMuaW5pdFBUUyA9IGRlYXVsdFRpbWVzdGFtcDtcbiAgICB0aGlzLnJlc2V0Q29udGlndWl0eSgpO1xuICB9XG4gIHJlc2V0Q29udGlndWl0eSgpIHtcbiAgICB0aGlzLmJhc2VQVFMgPSBudWxsO1xuICAgIHRoaXMubGFzdFBUUyA9IG51bGw7XG4gICAgdGhpcy5mcmFtZUluZGV4ID0gMDtcbiAgfVxuICBjYW5QYXJzZShkYXRhLCBvZmZzZXQpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgYXBwZW5kRnJhbWUodHJhY2ssIGRhdGEsIG9mZnNldCkge31cblxuICAvLyBmZWVkIGluY29taW5nIGRhdGEgdG8gdGhlIGZyb250IG9mIHRoZSBwYXJzaW5nIHBpcGVsaW5lXG4gIGRlbXV4KGRhdGEsIHRpbWVPZmZzZXQpIHtcbiAgICBpZiAodGhpcy5jYWNoZWREYXRhKSB7XG4gICAgICBkYXRhID0gYXBwZW5kVWludDhBcnJheSh0aGlzLmNhY2hlZERhdGEsIGRhdGEpO1xuICAgICAgdGhpcy5jYWNoZWREYXRhID0gbnVsbDtcbiAgICB9XG4gICAgbGV0IGlkM0RhdGEgPSBnZXRJRDNEYXRhKGRhdGEsIDApO1xuICAgIGxldCBvZmZzZXQgPSBpZDNEYXRhID8gaWQzRGF0YS5sZW5ndGggOiAwO1xuICAgIGxldCBsYXN0RGF0YUluZGV4O1xuICAgIGNvbnN0IHRyYWNrID0gdGhpcy5fYXVkaW9UcmFjaztcbiAgICBjb25zdCBpZDNUcmFjayA9IHRoaXMuX2lkM1RyYWNrO1xuICAgIGNvbnN0IHRpbWVzdGFtcCA9IGlkM0RhdGEgPyBnZXRUaW1lU3RhbXAoaWQzRGF0YSkgOiB1bmRlZmluZWQ7XG4gICAgY29uc3QgbGVuZ3RoID0gZGF0YS5sZW5ndGg7XG4gICAgaWYgKHRoaXMuYmFzZVBUUyA9PT0gbnVsbCB8fCB0aGlzLmZyYW1lSW5kZXggPT09IDAgJiYgaXNGaW5pdGVOdW1iZXIodGltZXN0YW1wKSkge1xuICAgICAgdGhpcy5iYXNlUFRTID0gaW5pdFBUU0ZuKHRpbWVzdGFtcCwgdGltZU9mZnNldCwgdGhpcy5pbml0UFRTKTtcbiAgICAgIHRoaXMubGFzdFBUUyA9IHRoaXMuYmFzZVBUUztcbiAgICB9XG4gICAgaWYgKHRoaXMubGFzdFBUUyA9PT0gbnVsbCkge1xuICAgICAgdGhpcy5sYXN0UFRTID0gdGhpcy5iYXNlUFRTO1xuICAgIH1cblxuICAgIC8vIG1vcmUgZXhwcmVzc2l2ZSB0aGFuIGFsdGVybmF0aXZlOiBpZDNEYXRhPy5sZW5ndGhcbiAgICBpZiAoaWQzRGF0YSAmJiBpZDNEYXRhLmxlbmd0aCA+IDApIHtcbiAgICAgIGlkM1RyYWNrLnNhbXBsZXMucHVzaCh7XG4gICAgICAgIHB0czogdGhpcy5sYXN0UFRTLFxuICAgICAgICBkdHM6IHRoaXMubGFzdFBUUyxcbiAgICAgICAgZGF0YTogaWQzRGF0YSxcbiAgICAgICAgdHlwZTogTWV0YWRhdGFTY2hlbWEuYXVkaW9JZDMsXG4gICAgICAgIGR1cmF0aW9uOiBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFlcbiAgICAgIH0pO1xuICAgIH1cbiAgICB3aGlsZSAob2Zmc2V0IDwgbGVuZ3RoKSB7XG4gICAgICBpZiAodGhpcy5jYW5QYXJzZShkYXRhLCBvZmZzZXQpKSB7XG4gICAgICAgIGNvbnN0IGZyYW1lID0gdGhpcy5hcHBlbmRGcmFtZSh0cmFjaywgZGF0YSwgb2Zmc2V0KTtcbiAgICAgICAgaWYgKGZyYW1lKSB7XG4gICAgICAgICAgdGhpcy5mcmFtZUluZGV4Kys7XG4gICAgICAgICAgdGhpcy5sYXN0UFRTID0gZnJhbWUuc2FtcGxlLnB0cztcbiAgICAgICAgICBvZmZzZXQgKz0gZnJhbWUubGVuZ3RoO1xuICAgICAgICAgIGxhc3REYXRhSW5kZXggPSBvZmZzZXQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgb2Zmc2V0ID0gbGVuZ3RoO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGNhblBhcnNlJDIoZGF0YSwgb2Zmc2V0KSkge1xuICAgICAgICAvLyBhZnRlciBhIElEMy5jYW5QYXJzZSwgYSBjYWxsIHRvIElEMy5nZXRJRDNEYXRhICpzaG91bGQqIGFsd2F5cyByZXR1cm5zIHNvbWUgZGF0YVxuICAgICAgICBpZDNEYXRhID0gZ2V0SUQzRGF0YShkYXRhLCBvZmZzZXQpO1xuICAgICAgICBpZDNUcmFjay5zYW1wbGVzLnB1c2goe1xuICAgICAgICAgIHB0czogdGhpcy5sYXN0UFRTLFxuICAgICAgICAgIGR0czogdGhpcy5sYXN0UFRTLFxuICAgICAgICAgIGRhdGE6IGlkM0RhdGEsXG4gICAgICAgICAgdHlwZTogTWV0YWRhdGFTY2hlbWEuYXVkaW9JZDMsXG4gICAgICAgICAgZHVyYXRpb246IE51bWJlci5QT1NJVElWRV9JTkZJTklUWVxuICAgICAgICB9KTtcbiAgICAgICAgb2Zmc2V0ICs9IGlkM0RhdGEubGVuZ3RoO1xuICAgICAgICBsYXN0RGF0YUluZGV4ID0gb2Zmc2V0O1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgb2Zmc2V0Kys7XG4gICAgICB9XG4gICAgICBpZiAob2Zmc2V0ID09PSBsZW5ndGggJiYgbGFzdERhdGFJbmRleCAhPT0gbGVuZ3RoKSB7XG4gICAgICAgIGNvbnN0IHBhcnRpYWxEYXRhID0gc2xpY2VVaW50OChkYXRhLCBsYXN0RGF0YUluZGV4KTtcbiAgICAgICAgaWYgKHRoaXMuY2FjaGVkRGF0YSkge1xuICAgICAgICAgIHRoaXMuY2FjaGVkRGF0YSA9IGFwcGVuZFVpbnQ4QXJyYXkodGhpcy5jYWNoZWREYXRhLCBwYXJ0aWFsRGF0YSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhpcy5jYWNoZWREYXRhID0gcGFydGlhbERhdGE7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHtcbiAgICAgIGF1ZGlvVHJhY2s6IHRyYWNrLFxuICAgICAgdmlkZW9UcmFjazogZHVtbXlUcmFjaygpLFxuICAgICAgaWQzVHJhY2ssXG4gICAgICB0ZXh0VHJhY2s6IGR1bW15VHJhY2soKVxuICAgIH07XG4gIH1cbiAgZGVtdXhTYW1wbGVBZXMoZGF0YSwga2V5RGF0YSwgdGltZU9mZnNldCkge1xuICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoYFske3RoaXN9XSBUaGlzIGRlbXV4ZXIgZG9lcyBub3Qgc3VwcG9ydCBTYW1wbGUtQUVTIGRlY3J5cHRpb25gKSk7XG4gIH1cbiAgZmx1c2godGltZU9mZnNldCkge1xuICAgIC8vIFBhcnNlIGNhY2hlIGluIGNhc2Ugb2YgcmVtYWluaW5nIGZyYW1lcy5cbiAgICBjb25zdCBjYWNoZWREYXRhID0gdGhpcy5jYWNoZWREYXRhO1xuICAgIGlmIChjYWNoZWREYXRhKSB7XG4gICAgICB0aGlzLmNhY2hlZERhdGEgPSBudWxsO1xuICAgICAgdGhpcy5kZW11eChjYWNoZWREYXRhLCAwKTtcbiAgICB9XG4gICAgcmV0dXJuIHtcbiAgICAgIGF1ZGlvVHJhY2s6IHRoaXMuX2F1ZGlvVHJhY2ssXG4gICAgICB2aWRlb1RyYWNrOiBkdW1teVRyYWNrKCksXG4gICAgICBpZDNUcmFjazogdGhpcy5faWQzVHJhY2ssXG4gICAgICB0ZXh0VHJhY2s6IGR1bW15VHJhY2soKVxuICAgIH07XG4gIH1cbiAgZGVzdHJveSgpIHt9XG59XG5cbi8qKlxuICogSW5pdGlhbGl6ZSBQVFNcbiAqIDxwPlxuICogICAgdXNlIHRpbWVzdGFtcCB1bmxlc3MgaXQgaXMgdW5kZWZpbmVkLCBOYU4gb3IgSW5maW5pdHlcbiAqIDwvcD5cbiAqL1xuY29uc3QgaW5pdFBUU0ZuID0gKHRpbWVzdGFtcCwgdGltZU9mZnNldCwgaW5pdFBUUykgPT4ge1xuICBpZiAoaXNGaW5pdGVOdW1iZXIodGltZXN0YW1wKSkge1xuICAgIHJldHVybiB0aW1lc3RhbXAgKiA5MDtcbiAgfVxuICBjb25zdCBpbml0OTBrSHogPSBpbml0UFRTID8gaW5pdFBUUy5iYXNlVGltZSAqIDkwMDAwIC8gaW5pdFBUUy50aW1lc2NhbGUgOiAwO1xuICByZXR1cm4gdGltZU9mZnNldCAqIDkwMDAwICsgaW5pdDkwa0h6O1xufTtcblxuLyoqXG4gKiBBRFRTIHBhcnNlciBoZWxwZXJcbiAqIEBsaW5rIGh0dHBzOi8vd2lraS5tdWx0aW1lZGlhLmN4L2luZGV4LnBocD90aXRsZT1BRFRTXG4gKi9cbmZ1bmN0aW9uIGdldEF1ZGlvQ29uZmlnKG9ic2VydmVyLCBkYXRhLCBvZmZzZXQsIGF1ZGlvQ29kZWMpIHtcbiAgbGV0IGFkdHNPYmplY3RUeXBlO1xuICBsZXQgYWR0c0V4dGVuc2lvblNhbXBsaW5nSW5kZXg7XG4gIGxldCBhZHRzQ2hhbm5lbENvbmZpZztcbiAgbGV0IGNvbmZpZztcbiAgY29uc3QgdXNlckFnZW50ID0gbmF2aWdhdG9yLnVzZXJBZ2VudC50b0xvd2VyQ2FzZSgpO1xuICBjb25zdCBtYW5pZmVzdENvZGVjID0gYXVkaW9Db2RlYztcbiAgY29uc3QgYWR0c1NhbXBsaW5nUmF0ZXMgPSBbOTYwMDAsIDg4MjAwLCA2NDAwMCwgNDgwMDAsIDQ0MTAwLCAzMjAwMCwgMjQwMDAsIDIyMDUwLCAxNjAwMCwgMTIwMDAsIDExMDI1LCA4MDAwLCA3MzUwXTtcbiAgLy8gYnl0ZSAyXG4gIGFkdHNPYmplY3RUeXBlID0gKChkYXRhW29mZnNldCArIDJdICYgMHhjMCkgPj4+IDYpICsgMTtcbiAgY29uc3QgYWR0c1NhbXBsaW5nSW5kZXggPSAoZGF0YVtvZmZzZXQgKyAyXSAmIDB4M2MpID4+PiAyO1xuICBpZiAoYWR0c1NhbXBsaW5nSW5kZXggPiBhZHRzU2FtcGxpbmdSYXRlcy5sZW5ndGggLSAxKSB7XG4gICAgY29uc3QgZXJyb3IgPSBuZXcgRXJyb3IoYGludmFsaWQgQURUUyBzYW1wbGluZyBpbmRleDoke2FkdHNTYW1wbGluZ0luZGV4fWApO1xuICAgIG9ic2VydmVyLmVtaXQoRXZlbnRzLkVSUk9SLCBFdmVudHMuRVJST1IsIHtcbiAgICAgIHR5cGU6IEVycm9yVHlwZXMuTUVESUFfRVJST1IsXG4gICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuRlJBR19QQVJTSU5HX0VSUk9SLFxuICAgICAgZmF0YWw6IHRydWUsXG4gICAgICBlcnJvcixcbiAgICAgIHJlYXNvbjogZXJyb3IubWVzc2FnZVxuICAgIH0pO1xuICAgIHJldHVybjtcbiAgfVxuICBhZHRzQ2hhbm5lbENvbmZpZyA9IChkYXRhW29mZnNldCArIDJdICYgMHgwMSkgPDwgMjtcbiAgLy8gYnl0ZSAzXG4gIGFkdHNDaGFubmVsQ29uZmlnIHw9IChkYXRhW29mZnNldCArIDNdICYgMHhjMCkgPj4+IDY7XG4gIGxvZ2dlci5sb2coYG1hbmlmZXN0IGNvZGVjOiR7YXVkaW9Db2RlY30sIEFEVFMgdHlwZToke2FkdHNPYmplY3RUeXBlfSwgc2FtcGxpbmdJbmRleDoke2FkdHNTYW1wbGluZ0luZGV4fWApO1xuICAvLyBmaXJlZm94OiBmcmVxIGxlc3MgdGhhbiAyNGtIeiA9IEFBQyBTQlIgKEhFLUFBQylcbiAgaWYgKC9maXJlZm94L2kudGVzdCh1c2VyQWdlbnQpKSB7XG4gICAgaWYgKGFkdHNTYW1wbGluZ0luZGV4ID49IDYpIHtcbiAgICAgIGFkdHNPYmplY3RUeXBlID0gNTtcbiAgICAgIGNvbmZpZyA9IG5ldyBBcnJheSg0KTtcbiAgICAgIC8vIEhFLUFBQyB1c2VzIFNCUiAoU3BlY3RyYWwgQmFuZCBSZXBsaWNhdGlvbikgLCBoaWdoIGZyZXF1ZW5jaWVzIGFyZSBjb25zdHJ1Y3RlZCBmcm9tIGxvdyBmcmVxdWVuY2llc1xuICAgICAgLy8gdGhlcmUgaXMgYSBmYWN0b3IgMiBiZXR3ZWVuIGZyYW1lIHNhbXBsZSByYXRlIGFuZCBvdXRwdXQgc2FtcGxlIHJhdGVcbiAgICAgIC8vIG11bHRpcGx5IGZyZXF1ZW5jeSBieSAyIChzZWUgdGFibGUgYmVsb3csIGVxdWl2YWxlbnQgdG8gc3Vic3RyYWN0IDMpXG4gICAgICBhZHRzRXh0ZW5zaW9uU2FtcGxpbmdJbmRleCA9IGFkdHNTYW1wbGluZ0luZGV4IC0gMztcbiAgICB9IGVsc2Uge1xuICAgICAgYWR0c09iamVjdFR5cGUgPSAyO1xuICAgICAgY29uZmlnID0gbmV3IEFycmF5KDIpO1xuICAgICAgYWR0c0V4dGVuc2lvblNhbXBsaW5nSW5kZXggPSBhZHRzU2FtcGxpbmdJbmRleDtcbiAgICB9XG4gICAgLy8gQW5kcm9pZCA6IGFsd2F5cyB1c2UgQUFDXG4gIH0gZWxzZSBpZiAodXNlckFnZW50LmluZGV4T2YoJ2FuZHJvaWQnKSAhPT0gLTEpIHtcbiAgICBhZHRzT2JqZWN0VHlwZSA9IDI7XG4gICAgY29uZmlnID0gbmV3IEFycmF5KDIpO1xuICAgIGFkdHNFeHRlbnNpb25TYW1wbGluZ0luZGV4ID0gYWR0c1NhbXBsaW5nSW5kZXg7XG4gIH0gZWxzZSB7XG4gICAgLyogIGZvciBvdGhlciBicm93c2VycyAoQ2hyb21lL1ZpdmFsZGkvT3BlcmEgLi4uKVxuICAgICAgICBhbHdheXMgZm9yY2UgYXVkaW8gdHlwZSB0byBiZSBIRS1BQUMgU0JSLCBhcyBzb21lIGJyb3dzZXJzIGRvIG5vdCBzdXBwb3J0IGF1ZGlvIGNvZGVjIHN3aXRjaCBwcm9wZXJseSAobGlrZSBDaHJvbWUgLi4uKVxuICAgICovXG4gICAgYWR0c09iamVjdFR5cGUgPSA1O1xuICAgIGNvbmZpZyA9IG5ldyBBcnJheSg0KTtcbiAgICAvLyBpZiAobWFuaWZlc3QgY29kZWMgaXMgSEUtQUFDIG9yIEhFLUFBQ3YyKSBPUiAobWFuaWZlc3QgY29kZWMgbm90IHNwZWNpZmllZCBBTkQgZnJlcXVlbmN5IGxlc3MgdGhhbiAyNGtIeilcbiAgICBpZiAoYXVkaW9Db2RlYyAmJiAoYXVkaW9Db2RlYy5pbmRleE9mKCdtcDRhLjQwLjI5JykgIT09IC0xIHx8IGF1ZGlvQ29kZWMuaW5kZXhPZignbXA0YS40MC41JykgIT09IC0xKSB8fCAhYXVkaW9Db2RlYyAmJiBhZHRzU2FtcGxpbmdJbmRleCA+PSA2KSB7XG4gICAgICAvLyBIRS1BQUMgdXNlcyBTQlIgKFNwZWN0cmFsIEJhbmQgUmVwbGljYXRpb24pICwgaGlnaCBmcmVxdWVuY2llcyBhcmUgY29uc3RydWN0ZWQgZnJvbSBsb3cgZnJlcXVlbmNpZXNcbiAgICAgIC8vIHRoZXJlIGlzIGEgZmFjdG9yIDIgYmV0d2VlbiBmcmFtZSBzYW1wbGUgcmF0ZSBhbmQgb3V0cHV0IHNhbXBsZSByYXRlXG4gICAgICAvLyBtdWx0aXBseSBmcmVxdWVuY3kgYnkgMiAoc2VlIHRhYmxlIGJlbG93LCBlcXVpdmFsZW50IHRvIHN1YnN0cmFjdCAzKVxuICAgICAgYWR0c0V4dGVuc2lvblNhbXBsaW5nSW5kZXggPSBhZHRzU2FtcGxpbmdJbmRleCAtIDM7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIGlmIChtYW5pZmVzdCBjb2RlYyBpcyBBQUMpIEFORCAoZnJlcXVlbmN5IGxlc3MgdGhhbiAyNGtIeiBBTkQgbmIgY2hhbm5lbCBpcyAxKSBPUiAobWFuaWZlc3QgY29kZWMgbm90IHNwZWNpZmllZCBhbmQgbW9ubyBhdWRpbylcbiAgICAgIC8vIENocm9tZSBmYWlscyB0byBwbGF5IGJhY2sgd2l0aCBsb3cgZnJlcXVlbmN5IEFBQyBMQyBtb25vIHdoZW4gaW5pdGlhbGl6ZWQgd2l0aCBIRS1BQUMuICBUaGlzIGlzIG5vdCBhIHByb2JsZW0gd2l0aCBzdGVyZW8uXG4gICAgICBpZiAoYXVkaW9Db2RlYyAmJiBhdWRpb0NvZGVjLmluZGV4T2YoJ21wNGEuNDAuMicpICE9PSAtMSAmJiAoYWR0c1NhbXBsaW5nSW5kZXggPj0gNiAmJiBhZHRzQ2hhbm5lbENvbmZpZyA9PT0gMSB8fCAvdml2YWxkaS9pLnRlc3QodXNlckFnZW50KSkgfHwgIWF1ZGlvQ29kZWMgJiYgYWR0c0NoYW5uZWxDb25maWcgPT09IDEpIHtcbiAgICAgICAgYWR0c09iamVjdFR5cGUgPSAyO1xuICAgICAgICBjb25maWcgPSBuZXcgQXJyYXkoMik7XG4gICAgICB9XG4gICAgICBhZHRzRXh0ZW5zaW9uU2FtcGxpbmdJbmRleCA9IGFkdHNTYW1wbGluZ0luZGV4O1xuICAgIH1cbiAgfVxuICAvKiByZWZlciB0byBodHRwOi8vd2lraS5tdWx0aW1lZGlhLmN4L2luZGV4LnBocD90aXRsZT1NUEVHLTRfQXVkaW8jQXVkaW9fU3BlY2lmaWNfQ29uZmlnXG4gICAgICBJU08gMTQ0OTYtMyAoQUFDKS5wZGYgLSBUYWJsZSAxLjEzIOKAlCBTeW50YXggb2YgQXVkaW9TcGVjaWZpY0NvbmZpZygpXG4gICAgQXVkaW8gUHJvZmlsZSAvIEF1ZGlvIE9iamVjdCBUeXBlXG4gICAgMDogTnVsbFxuICAgIDE6IEFBQyBNYWluXG4gICAgMjogQUFDIExDIChMb3cgQ29tcGxleGl0eSlcbiAgICAzOiBBQUMgU1NSIChTY2FsYWJsZSBTYW1wbGUgUmF0ZSlcbiAgICA0OiBBQUMgTFRQIChMb25nIFRlcm0gUHJlZGljdGlvbilcbiAgICA1OiBTQlIgKFNwZWN0cmFsIEJhbmQgUmVwbGljYXRpb24pXG4gICAgNjogQUFDIFNjYWxhYmxlXG4gICBzYW1wbGluZyBmcmVxXG4gICAgMDogOTYwMDAgSHpcbiAgICAxOiA4ODIwMCBIelxuICAgIDI6IDY0MDAwIEh6XG4gICAgMzogNDgwMDAgSHpcbiAgICA0OiA0NDEwMCBIelxuICAgIDU6IDMyMDAwIEh6XG4gICAgNjogMjQwMDAgSHpcbiAgICA3OiAyMjA1MCBIelxuICAgIDg6IDE2MDAwIEh6XG4gICAgOTogMTIwMDAgSHpcbiAgICAxMDogMTEwMjUgSHpcbiAgICAxMTogODAwMCBIelxuICAgIDEyOiA3MzUwIEh6XG4gICAgMTM6IFJlc2VydmVkXG4gICAgMTQ6IFJlc2VydmVkXG4gICAgMTU6IGZyZXF1ZW5jeSBpcyB3cml0dGVuIGV4cGxpY3RseVxuICAgIENoYW5uZWwgQ29uZmlndXJhdGlvbnNcbiAgICBUaGVzZSBhcmUgdGhlIGNoYW5uZWwgY29uZmlndXJhdGlvbnM6XG4gICAgMDogRGVmaW5lZCBpbiBBT1QgU3BlY2lmYyBDb25maWdcbiAgICAxOiAxIGNoYW5uZWw6IGZyb250LWNlbnRlclxuICAgIDI6IDIgY2hhbm5lbHM6IGZyb250LWxlZnQsIGZyb250LXJpZ2h0XG4gICovXG4gIC8vIGF1ZGlvT2JqZWN0VHlwZSA9IHByb2ZpbGUgPT4gcHJvZmlsZSwgdGhlIE1QRUctNCBBdWRpbyBPYmplY3QgVHlwZSBtaW51cyAxXG4gIGNvbmZpZ1swXSA9IGFkdHNPYmplY3RUeXBlIDw8IDM7XG4gIC8vIHNhbXBsaW5nRnJlcXVlbmN5SW5kZXhcbiAgY29uZmlnWzBdIHw9IChhZHRzU2FtcGxpbmdJbmRleCAmIDB4MGUpID4+IDE7XG4gIGNvbmZpZ1sxXSB8PSAoYWR0c1NhbXBsaW5nSW5kZXggJiAweDAxKSA8PCA3O1xuICAvLyBjaGFubmVsQ29uZmlndXJhdGlvblxuICBjb25maWdbMV0gfD0gYWR0c0NoYW5uZWxDb25maWcgPDwgMztcbiAgaWYgKGFkdHNPYmplY3RUeXBlID09PSA1KSB7XG4gICAgLy8gYWR0c0V4dGVuc2lvblNhbXBsaW5nSW5kZXhcbiAgICBjb25maWdbMV0gfD0gKGFkdHNFeHRlbnNpb25TYW1wbGluZ0luZGV4ICYgMHgwZSkgPj4gMTtcbiAgICBjb25maWdbMl0gPSAoYWR0c0V4dGVuc2lvblNhbXBsaW5nSW5kZXggJiAweDAxKSA8PCA3O1xuICAgIC8vIGFkdHNPYmplY3RUeXBlIChmb3JjZSB0byAyLCBjaHJvbWUgaXMgY2hlY2tpbmcgdGhhdCBvYmplY3QgdHlwZSBpcyBsZXNzIHRoYW4gNSA/Pz9cbiAgICAvLyAgICBodHRwczovL2Nocm9taXVtLmdvb2dsZXNvdXJjZS5jb20vY2hyb21pdW0vc3JjLmdpdC8rL21hc3Rlci9tZWRpYS9mb3JtYXRzL21wNC9hYWMuY2NcbiAgICBjb25maWdbMl0gfD0gMiA8PCAyO1xuICAgIGNvbmZpZ1szXSA9IDA7XG4gIH1cbiAgcmV0dXJuIHtcbiAgICBjb25maWcsXG4gICAgc2FtcGxlcmF0ZTogYWR0c1NhbXBsaW5nUmF0ZXNbYWR0c1NhbXBsaW5nSW5kZXhdLFxuICAgIGNoYW5uZWxDb3VudDogYWR0c0NoYW5uZWxDb25maWcsXG4gICAgY29kZWM6ICdtcDRhLjQwLicgKyBhZHRzT2JqZWN0VHlwZSxcbiAgICBtYW5pZmVzdENvZGVjXG4gIH07XG59XG5mdW5jdGlvbiBpc0hlYWRlclBhdHRlcm4kMShkYXRhLCBvZmZzZXQpIHtcbiAgcmV0dXJuIGRhdGFbb2Zmc2V0XSA9PT0gMHhmZiAmJiAoZGF0YVtvZmZzZXQgKyAxXSAmIDB4ZjYpID09PSAweGYwO1xufVxuZnVuY3Rpb24gZ2V0SGVhZGVyTGVuZ3RoKGRhdGEsIG9mZnNldCkge1xuICByZXR1cm4gZGF0YVtvZmZzZXQgKyAxXSAmIDB4MDEgPyA3IDogOTtcbn1cbmZ1bmN0aW9uIGdldEZ1bGxGcmFtZUxlbmd0aChkYXRhLCBvZmZzZXQpIHtcbiAgcmV0dXJuIChkYXRhW29mZnNldCArIDNdICYgMHgwMykgPDwgMTEgfCBkYXRhW29mZnNldCArIDRdIDw8IDMgfCAoZGF0YVtvZmZzZXQgKyA1XSAmIDB4ZTApID4+PiA1O1xufVxuZnVuY3Rpb24gY2FuR2V0RnJhbWVMZW5ndGgoZGF0YSwgb2Zmc2V0KSB7XG4gIHJldHVybiBvZmZzZXQgKyA1IDwgZGF0YS5sZW5ndGg7XG59XG5mdW5jdGlvbiBpc0hlYWRlciQxKGRhdGEsIG9mZnNldCkge1xuICAvLyBMb29rIGZvciBBRFRTIGhlYWRlciB8IDExMTEgMTExMSB8IDExMTEgWDAwWCB8IHdoZXJlIFggY2FuIGJlIGVpdGhlciAwIG9yIDFcbiAgLy8gTGF5ZXIgYml0cyAocG9zaXRpb24gMTQgYW5kIDE1KSBpbiBoZWFkZXIgc2hvdWxkIGJlIGFsd2F5cyAwIGZvciBBRFRTXG4gIC8vIE1vcmUgaW5mbyBodHRwczovL3dpa2kubXVsdGltZWRpYS5jeC9pbmRleC5waHA/dGl0bGU9QURUU1xuICByZXR1cm4gb2Zmc2V0ICsgMSA8IGRhdGEubGVuZ3RoICYmIGlzSGVhZGVyUGF0dGVybiQxKGRhdGEsIG9mZnNldCk7XG59XG5mdW5jdGlvbiBjYW5QYXJzZSQxKGRhdGEsIG9mZnNldCkge1xuICByZXR1cm4gY2FuR2V0RnJhbWVMZW5ndGgoZGF0YSwgb2Zmc2V0KSAmJiBpc0hlYWRlclBhdHRlcm4kMShkYXRhLCBvZmZzZXQpICYmIGdldEZ1bGxGcmFtZUxlbmd0aChkYXRhLCBvZmZzZXQpIDw9IGRhdGEubGVuZ3RoIC0gb2Zmc2V0O1xufVxuZnVuY3Rpb24gcHJvYmUkMShkYXRhLCBvZmZzZXQpIHtcbiAgLy8gc2FtZSBhcyBpc0hlYWRlciBidXQgd2UgYWxzbyBjaGVjayB0aGF0IEFEVFMgZnJhbWUgZm9sbG93cyBsYXN0IEFEVFMgZnJhbWVcbiAgLy8gb3IgZW5kIG9mIGRhdGEgaXMgcmVhY2hlZFxuICBpZiAoaXNIZWFkZXIkMShkYXRhLCBvZmZzZXQpKSB7XG4gICAgLy8gQURUUyBoZWFkZXIgTGVuZ3RoXG4gICAgY29uc3QgaGVhZGVyTGVuZ3RoID0gZ2V0SGVhZGVyTGVuZ3RoKGRhdGEsIG9mZnNldCk7XG4gICAgaWYgKG9mZnNldCArIGhlYWRlckxlbmd0aCA+PSBkYXRhLmxlbmd0aCkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICAvLyBBRFRTIGZyYW1lIExlbmd0aFxuICAgIGNvbnN0IGZyYW1lTGVuZ3RoID0gZ2V0RnVsbEZyYW1lTGVuZ3RoKGRhdGEsIG9mZnNldCk7XG4gICAgaWYgKGZyYW1lTGVuZ3RoIDw9IGhlYWRlckxlbmd0aCkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBjb25zdCBuZXdPZmZzZXQgPSBvZmZzZXQgKyBmcmFtZUxlbmd0aDtcbiAgICByZXR1cm4gbmV3T2Zmc2V0ID09PSBkYXRhLmxlbmd0aCB8fCBpc0hlYWRlciQxKGRhdGEsIG5ld09mZnNldCk7XG4gIH1cbiAgcmV0dXJuIGZhbHNlO1xufVxuZnVuY3Rpb24gaW5pdFRyYWNrQ29uZmlnKHRyYWNrLCBvYnNlcnZlciwgZGF0YSwgb2Zmc2V0LCBhdWRpb0NvZGVjKSB7XG4gIGlmICghdHJhY2suc2FtcGxlcmF0ZSkge1xuICAgIGNvbnN0IGNvbmZpZyA9IGdldEF1ZGlvQ29uZmlnKG9ic2VydmVyLCBkYXRhLCBvZmZzZXQsIGF1ZGlvQ29kZWMpO1xuICAgIGlmICghY29uZmlnKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRyYWNrLmNvbmZpZyA9IGNvbmZpZy5jb25maWc7XG4gICAgdHJhY2suc2FtcGxlcmF0ZSA9IGNvbmZpZy5zYW1wbGVyYXRlO1xuICAgIHRyYWNrLmNoYW5uZWxDb3VudCA9IGNvbmZpZy5jaGFubmVsQ291bnQ7XG4gICAgdHJhY2suY29kZWMgPSBjb25maWcuY29kZWM7XG4gICAgdHJhY2subWFuaWZlc3RDb2RlYyA9IGNvbmZpZy5tYW5pZmVzdENvZGVjO1xuICAgIGxvZ2dlci5sb2coYHBhcnNlZCBjb2RlYzoke3RyYWNrLmNvZGVjfSwgcmF0ZToke2NvbmZpZy5zYW1wbGVyYXRlfSwgY2hhbm5lbHM6JHtjb25maWcuY2hhbm5lbENvdW50fWApO1xuICB9XG59XG5mdW5jdGlvbiBnZXRGcmFtZUR1cmF0aW9uKHNhbXBsZXJhdGUpIHtcbiAgcmV0dXJuIDEwMjQgKiA5MDAwMCAvIHNhbXBsZXJhdGU7XG59XG5mdW5jdGlvbiBwYXJzZUZyYW1lSGVhZGVyKGRhdGEsIG9mZnNldCkge1xuICAvLyBUaGUgcHJvdGVjdGlvbiBza2lwIGJpdCB0ZWxscyB1cyBpZiB3ZSBoYXZlIDIgYnl0ZXMgb2YgQ1JDIGRhdGEgYXQgdGhlIGVuZCBvZiB0aGUgQURUUyBoZWFkZXJcbiAgY29uc3QgaGVhZGVyTGVuZ3RoID0gZ2V0SGVhZGVyTGVuZ3RoKGRhdGEsIG9mZnNldCk7XG4gIGlmIChvZmZzZXQgKyBoZWFkZXJMZW5ndGggPD0gZGF0YS5sZW5ndGgpIHtcbiAgICAvLyByZXRyaWV2ZSBmcmFtZSBzaXplXG4gICAgY29uc3QgZnJhbWVMZW5ndGggPSBnZXRGdWxsRnJhbWVMZW5ndGgoZGF0YSwgb2Zmc2V0KSAtIGhlYWRlckxlbmd0aDtcbiAgICBpZiAoZnJhbWVMZW5ndGggPiAwKSB7XG4gICAgICAvLyBsb2dnZXIubG9nKGBBQUMgZnJhbWUsIG9mZnNldC9sZW5ndGgvdG90YWwvcHRzOiR7b2Zmc2V0K2hlYWRlckxlbmd0aH0vJHtmcmFtZUxlbmd0aH0vJHtkYXRhLmJ5dGVMZW5ndGh9YCk7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBoZWFkZXJMZW5ndGgsXG4gICAgICAgIGZyYW1lTGVuZ3RoXG4gICAgICB9O1xuICAgIH1cbiAgfVxufVxuZnVuY3Rpb24gYXBwZW5kRnJhbWUkMih0cmFjaywgZGF0YSwgb2Zmc2V0LCBwdHMsIGZyYW1lSW5kZXgpIHtcbiAgY29uc3QgZnJhbWVEdXJhdGlvbiA9IGdldEZyYW1lRHVyYXRpb24odHJhY2suc2FtcGxlcmF0ZSk7XG4gIGNvbnN0IHN0YW1wID0gcHRzICsgZnJhbWVJbmRleCAqIGZyYW1lRHVyYXRpb247XG4gIGNvbnN0IGhlYWRlciA9IHBhcnNlRnJhbWVIZWFkZXIoZGF0YSwgb2Zmc2V0KTtcbiAgbGV0IHVuaXQ7XG4gIGlmIChoZWFkZXIpIHtcbiAgICBjb25zdCB7XG4gICAgICBmcmFtZUxlbmd0aCxcbiAgICAgIGhlYWRlckxlbmd0aFxuICAgIH0gPSBoZWFkZXI7XG4gICAgY29uc3QgX2xlbmd0aCA9IGhlYWRlckxlbmd0aCArIGZyYW1lTGVuZ3RoO1xuICAgIGNvbnN0IG1pc3NpbmcgPSBNYXRoLm1heCgwLCBvZmZzZXQgKyBfbGVuZ3RoIC0gZGF0YS5sZW5ndGgpO1xuICAgIC8vIGxvZ2dlci5sb2coYEFBQyBmcmFtZSAke2ZyYW1lSW5kZXh9LCBwdHM6JHtzdGFtcH0gbGVuZ3RoQG9mZnNldC90b3RhbDogJHtmcmFtZUxlbmd0aH1AJHtvZmZzZXQraGVhZGVyTGVuZ3RofS8ke2RhdGEuYnl0ZUxlbmd0aH0gbWlzc2luZzogJHttaXNzaW5nfWApO1xuICAgIGlmIChtaXNzaW5nKSB7XG4gICAgICB1bml0ID0gbmV3IFVpbnQ4QXJyYXkoX2xlbmd0aCAtIGhlYWRlckxlbmd0aCk7XG4gICAgICB1bml0LnNldChkYXRhLnN1YmFycmF5KG9mZnNldCArIGhlYWRlckxlbmd0aCwgZGF0YS5sZW5ndGgpLCAwKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdW5pdCA9IGRhdGEuc3ViYXJyYXkob2Zmc2V0ICsgaGVhZGVyTGVuZ3RoLCBvZmZzZXQgKyBfbGVuZ3RoKTtcbiAgICB9XG4gICAgY29uc3QgX3NhbXBsZSA9IHtcbiAgICAgIHVuaXQsXG4gICAgICBwdHM6IHN0YW1wXG4gICAgfTtcbiAgICBpZiAoIW1pc3NpbmcpIHtcbiAgICAgIHRyYWNrLnNhbXBsZXMucHVzaChfc2FtcGxlKTtcbiAgICB9XG4gICAgcmV0dXJuIHtcbiAgICAgIHNhbXBsZTogX3NhbXBsZSxcbiAgICAgIGxlbmd0aDogX2xlbmd0aCxcbiAgICAgIG1pc3NpbmdcbiAgICB9O1xuICB9XG4gIC8vIG92ZXJmbG93IGluY29tcGxldGUgaGVhZGVyXG4gIGNvbnN0IGxlbmd0aCA9IGRhdGEubGVuZ3RoIC0gb2Zmc2V0O1xuICB1bml0ID0gbmV3IFVpbnQ4QXJyYXkobGVuZ3RoKTtcbiAgdW5pdC5zZXQoZGF0YS5zdWJhcnJheShvZmZzZXQsIGRhdGEubGVuZ3RoKSwgMCk7XG4gIGNvbnN0IHNhbXBsZSA9IHtcbiAgICB1bml0LFxuICAgIHB0czogc3RhbXBcbiAgfTtcbiAgcmV0dXJuIHtcbiAgICBzYW1wbGUsXG4gICAgbGVuZ3RoLFxuICAgIG1pc3Npbmc6IC0xXG4gIH07XG59XG5cbi8qKlxuICogIE1QRUcgcGFyc2VyIGhlbHBlclxuICovXG5cbmxldCBjaHJvbWVWZXJzaW9uJDEgPSBudWxsO1xuY29uc3QgQml0cmF0ZXNNYXAgPSBbMzIsIDY0LCA5NiwgMTI4LCAxNjAsIDE5MiwgMjI0LCAyNTYsIDI4OCwgMzIwLCAzNTIsIDM4NCwgNDE2LCA0NDgsIDMyLCA0OCwgNTYsIDY0LCA4MCwgOTYsIDExMiwgMTI4LCAxNjAsIDE5MiwgMjI0LCAyNTYsIDMyMCwgMzg0LCAzMiwgNDAsIDQ4LCA1NiwgNjQsIDgwLCA5NiwgMTEyLCAxMjgsIDE2MCwgMTkyLCAyMjQsIDI1NiwgMzIwLCAzMiwgNDgsIDU2LCA2NCwgODAsIDk2LCAxMTIsIDEyOCwgMTQ0LCAxNjAsIDE3NiwgMTkyLCAyMjQsIDI1NiwgOCwgMTYsIDI0LCAzMiwgNDAsIDQ4LCA1NiwgNjQsIDgwLCA5NiwgMTEyLCAxMjgsIDE0NCwgMTYwXTtcbmNvbnN0IFNhbXBsaW5nUmF0ZU1hcCA9IFs0NDEwMCwgNDgwMDAsIDMyMDAwLCAyMjA1MCwgMjQwMDAsIDE2MDAwLCAxMTAyNSwgMTIwMDAsIDgwMDBdO1xuY29uc3QgU2FtcGxlc0NvZWZmaWNpZW50cyA9IFtcbi8vIE1QRUcgMi41XG5bMCxcbi8vIFJlc2VydmVkXG43Mixcbi8vIExheWVyM1xuMTQ0LFxuLy8gTGF5ZXIyXG4xMiAvLyBMYXllcjFcbl0sXG4vLyBSZXNlcnZlZFxuWzAsXG4vLyBSZXNlcnZlZFxuMCxcbi8vIExheWVyM1xuMCxcbi8vIExheWVyMlxuMCAvLyBMYXllcjFcbl0sXG4vLyBNUEVHIDJcblswLFxuLy8gUmVzZXJ2ZWRcbjcyLFxuLy8gTGF5ZXIzXG4xNDQsXG4vLyBMYXllcjJcbjEyIC8vIExheWVyMVxuXSxcbi8vIE1QRUcgMVxuWzAsXG4vLyBSZXNlcnZlZFxuMTQ0LFxuLy8gTGF5ZXIzXG4xNDQsXG4vLyBMYXllcjJcbjEyIC8vIExheWVyMVxuXV07XG5jb25zdCBCeXRlc0luU2xvdCA9IFswLFxuLy8gUmVzZXJ2ZWRcbjEsXG4vLyBMYXllcjNcbjEsXG4vLyBMYXllcjJcbjQgLy8gTGF5ZXIxXG5dO1xuZnVuY3Rpb24gYXBwZW5kRnJhbWUkMSh0cmFjaywgZGF0YSwgb2Zmc2V0LCBwdHMsIGZyYW1lSW5kZXgpIHtcbiAgLy8gVXNpbmcgaHR0cDovL3d3dy5kYXRhdm95YWdlLmNvbS9tcGdzY3JpcHQvbXBlZ2hkci5odG0gYXMgYSByZWZlcmVuY2VcbiAgaWYgKG9mZnNldCArIDI0ID4gZGF0YS5sZW5ndGgpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgY29uc3QgaGVhZGVyID0gcGFyc2VIZWFkZXIoZGF0YSwgb2Zmc2V0KTtcbiAgaWYgKGhlYWRlciAmJiBvZmZzZXQgKyBoZWFkZXIuZnJhbWVMZW5ndGggPD0gZGF0YS5sZW5ndGgpIHtcbiAgICBjb25zdCBmcmFtZUR1cmF0aW9uID0gaGVhZGVyLnNhbXBsZXNQZXJGcmFtZSAqIDkwMDAwIC8gaGVhZGVyLnNhbXBsZVJhdGU7XG4gICAgY29uc3Qgc3RhbXAgPSBwdHMgKyBmcmFtZUluZGV4ICogZnJhbWVEdXJhdGlvbjtcbiAgICBjb25zdCBzYW1wbGUgPSB7XG4gICAgICB1bml0OiBkYXRhLnN1YmFycmF5KG9mZnNldCwgb2Zmc2V0ICsgaGVhZGVyLmZyYW1lTGVuZ3RoKSxcbiAgICAgIHB0czogc3RhbXAsXG4gICAgICBkdHM6IHN0YW1wXG4gICAgfTtcbiAgICB0cmFjay5jb25maWcgPSBbXTtcbiAgICB0cmFjay5jaGFubmVsQ291bnQgPSBoZWFkZXIuY2hhbm5lbENvdW50O1xuICAgIHRyYWNrLnNhbXBsZXJhdGUgPSBoZWFkZXIuc2FtcGxlUmF0ZTtcbiAgICB0cmFjay5zYW1wbGVzLnB1c2goc2FtcGxlKTtcbiAgICByZXR1cm4ge1xuICAgICAgc2FtcGxlLFxuICAgICAgbGVuZ3RoOiBoZWFkZXIuZnJhbWVMZW5ndGgsXG4gICAgICBtaXNzaW5nOiAwXG4gICAgfTtcbiAgfVxufVxuZnVuY3Rpb24gcGFyc2VIZWFkZXIoZGF0YSwgb2Zmc2V0KSB7XG4gIGNvbnN0IG1wZWdWZXJzaW9uID0gZGF0YVtvZmZzZXQgKyAxXSA+PiAzICYgMztcbiAgY29uc3QgbXBlZ0xheWVyID0gZGF0YVtvZmZzZXQgKyAxXSA+PiAxICYgMztcbiAgY29uc3QgYml0UmF0ZUluZGV4ID0gZGF0YVtvZmZzZXQgKyAyXSA+PiA0ICYgMTU7XG4gIGNvbnN0IHNhbXBsZVJhdGVJbmRleCA9IGRhdGFbb2Zmc2V0ICsgMl0gPj4gMiAmIDM7XG4gIGlmIChtcGVnVmVyc2lvbiAhPT0gMSAmJiBiaXRSYXRlSW5kZXggIT09IDAgJiYgYml0UmF0ZUluZGV4ICE9PSAxNSAmJiBzYW1wbGVSYXRlSW5kZXggIT09IDMpIHtcbiAgICBjb25zdCBwYWRkaW5nQml0ID0gZGF0YVtvZmZzZXQgKyAyXSA+PiAxICYgMTtcbiAgICBjb25zdCBjaGFubmVsTW9kZSA9IGRhdGFbb2Zmc2V0ICsgM10gPj4gNjtcbiAgICBjb25zdCBjb2x1bW5JbkJpdHJhdGVzID0gbXBlZ1ZlcnNpb24gPT09IDMgPyAzIC0gbXBlZ0xheWVyIDogbXBlZ0xheWVyID09PSAzID8gMyA6IDQ7XG4gICAgY29uc3QgYml0UmF0ZSA9IEJpdHJhdGVzTWFwW2NvbHVtbkluQml0cmF0ZXMgKiAxNCArIGJpdFJhdGVJbmRleCAtIDFdICogMTAwMDtcbiAgICBjb25zdCBjb2x1bW5JblNhbXBsZVJhdGVzID0gbXBlZ1ZlcnNpb24gPT09IDMgPyAwIDogbXBlZ1ZlcnNpb24gPT09IDIgPyAxIDogMjtcbiAgICBjb25zdCBzYW1wbGVSYXRlID0gU2FtcGxpbmdSYXRlTWFwW2NvbHVtbkluU2FtcGxlUmF0ZXMgKiAzICsgc2FtcGxlUmF0ZUluZGV4XTtcbiAgICBjb25zdCBjaGFubmVsQ291bnQgPSBjaGFubmVsTW9kZSA9PT0gMyA/IDEgOiAyOyAvLyBJZiBiaXRzIG9mIGNoYW5uZWwgbW9kZSBhcmUgYDExYCB0aGVuIGl0IGlzIGEgc2luZ2xlIGNoYW5uZWwgKE1vbm8pXG4gICAgY29uc3Qgc2FtcGxlQ29lZmZpY2llbnQgPSBTYW1wbGVzQ29lZmZpY2llbnRzW21wZWdWZXJzaW9uXVttcGVnTGF5ZXJdO1xuICAgIGNvbnN0IGJ5dGVzSW5TbG90ID0gQnl0ZXNJblNsb3RbbXBlZ0xheWVyXTtcbiAgICBjb25zdCBzYW1wbGVzUGVyRnJhbWUgPSBzYW1wbGVDb2VmZmljaWVudCAqIDggKiBieXRlc0luU2xvdDtcbiAgICBjb25zdCBmcmFtZUxlbmd0aCA9IE1hdGguZmxvb3Ioc2FtcGxlQ29lZmZpY2llbnQgKiBiaXRSYXRlIC8gc2FtcGxlUmF0ZSArIHBhZGRpbmdCaXQpICogYnl0ZXNJblNsb3Q7XG4gICAgaWYgKGNocm9tZVZlcnNpb24kMSA9PT0gbnVsbCkge1xuICAgICAgY29uc3QgdXNlckFnZW50ID0gbmF2aWdhdG9yLnVzZXJBZ2VudCB8fCAnJztcbiAgICAgIGNvbnN0IHJlc3VsdCA9IHVzZXJBZ2VudC5tYXRjaCgvQ2hyb21lXFwvKFxcZCspL2kpO1xuICAgICAgY2hyb21lVmVyc2lvbiQxID0gcmVzdWx0ID8gcGFyc2VJbnQocmVzdWx0WzFdKSA6IDA7XG4gICAgfVxuICAgIGNvbnN0IG5lZWRDaHJvbWVGaXggPSAhIWNocm9tZVZlcnNpb24kMSAmJiBjaHJvbWVWZXJzaW9uJDEgPD0gODc7XG4gICAgaWYgKG5lZWRDaHJvbWVGaXggJiYgbXBlZ0xheWVyID09PSAyICYmIGJpdFJhdGUgPj0gMjI0MDAwICYmIGNoYW5uZWxNb2RlID09PSAwKSB7XG4gICAgICAvLyBXb3JrIGFyb3VuZCBidWcgaW4gQ2hyb21pdW0gYnkgc2V0dGluZyBjaGFubmVsTW9kZSB0byBkdWFsLWNoYW5uZWwgKDAxKSBpbnN0ZWFkIG9mIHN0ZXJlbyAoMDApXG4gICAgICBkYXRhW29mZnNldCArIDNdID0gZGF0YVtvZmZzZXQgKyAzXSB8IDB4ODA7XG4gICAgfVxuICAgIHJldHVybiB7XG4gICAgICBzYW1wbGVSYXRlLFxuICAgICAgY2hhbm5lbENvdW50LFxuICAgICAgZnJhbWVMZW5ndGgsXG4gICAgICBzYW1wbGVzUGVyRnJhbWVcbiAgICB9O1xuICB9XG59XG5mdW5jdGlvbiBpc0hlYWRlclBhdHRlcm4oZGF0YSwgb2Zmc2V0KSB7XG4gIHJldHVybiBkYXRhW29mZnNldF0gPT09IDB4ZmYgJiYgKGRhdGFbb2Zmc2V0ICsgMV0gJiAweGUwKSA9PT0gMHhlMCAmJiAoZGF0YVtvZmZzZXQgKyAxXSAmIDB4MDYpICE9PSAweDAwO1xufVxuZnVuY3Rpb24gaXNIZWFkZXIoZGF0YSwgb2Zmc2V0KSB7XG4gIC8vIExvb2sgZm9yIE1QRUcgaGVhZGVyIHwgMTExMSAxMTExIHwgMTExWCBYWVpYIHwgd2hlcmUgWCBjYW4gYmUgZWl0aGVyIDAgb3IgMSBhbmQgWSBvciBaIHNob3VsZCBiZSAxXG4gIC8vIExheWVyIGJpdHMgKHBvc2l0aW9uIDE0IGFuZCAxNSkgaW4gaGVhZGVyIHNob3VsZCBiZSBhbHdheXMgZGlmZmVyZW50IGZyb20gMCAoTGF5ZXIgSSBvciBMYXllciBJSSBvciBMYXllciBJSUkpXG4gIC8vIE1vcmUgaW5mbyBodHRwOi8vd3d3Lm1wMy10ZWNoLm9yZy9wcm9ncmFtbWVyL2ZyYW1lX2hlYWRlci5odG1sXG4gIHJldHVybiBvZmZzZXQgKyAxIDwgZGF0YS5sZW5ndGggJiYgaXNIZWFkZXJQYXR0ZXJuKGRhdGEsIG9mZnNldCk7XG59XG5mdW5jdGlvbiBjYW5QYXJzZShkYXRhLCBvZmZzZXQpIHtcbiAgY29uc3QgaGVhZGVyU2l6ZSA9IDQ7XG4gIHJldHVybiBpc0hlYWRlclBhdHRlcm4oZGF0YSwgb2Zmc2V0KSAmJiBoZWFkZXJTaXplIDw9IGRhdGEubGVuZ3RoIC0gb2Zmc2V0O1xufVxuZnVuY3Rpb24gcHJvYmUoZGF0YSwgb2Zmc2V0KSB7XG4gIC8vIHNhbWUgYXMgaXNIZWFkZXIgYnV0IHdlIGFsc28gY2hlY2sgdGhhdCBNUEVHIGZyYW1lIGZvbGxvd3MgbGFzdCBNUEVHIGZyYW1lXG4gIC8vIG9yIGVuZCBvZiBkYXRhIGlzIHJlYWNoZWRcbiAgaWYgKG9mZnNldCArIDEgPCBkYXRhLmxlbmd0aCAmJiBpc0hlYWRlclBhdHRlcm4oZGF0YSwgb2Zmc2V0KSkge1xuICAgIC8vIE1QRUcgaGVhZGVyIExlbmd0aFxuICAgIGNvbnN0IGhlYWRlckxlbmd0aCA9IDQ7XG4gICAgLy8gTVBFRyBmcmFtZSBMZW5ndGhcbiAgICBjb25zdCBoZWFkZXIgPSBwYXJzZUhlYWRlcihkYXRhLCBvZmZzZXQpO1xuICAgIGxldCBmcmFtZUxlbmd0aCA9IGhlYWRlckxlbmd0aDtcbiAgICBpZiAoaGVhZGVyICE9IG51bGwgJiYgaGVhZGVyLmZyYW1lTGVuZ3RoKSB7XG4gICAgICBmcmFtZUxlbmd0aCA9IGhlYWRlci5mcmFtZUxlbmd0aDtcbiAgICB9XG4gICAgY29uc3QgbmV3T2Zmc2V0ID0gb2Zmc2V0ICsgZnJhbWVMZW5ndGg7XG4gICAgcmV0dXJuIG5ld09mZnNldCA9PT0gZGF0YS5sZW5ndGggfHwgaXNIZWFkZXIoZGF0YSwgbmV3T2Zmc2V0KTtcbiAgfVxuICByZXR1cm4gZmFsc2U7XG59XG5cbi8qKlxuICogQUFDIGRlbXV4ZXJcbiAqL1xuY2xhc3MgQUFDRGVtdXhlciBleHRlbmRzIEJhc2VBdWRpb0RlbXV4ZXIge1xuICBjb25zdHJ1Y3RvcihvYnNlcnZlciwgY29uZmlnKSB7XG4gICAgc3VwZXIoKTtcbiAgICB0aGlzLm9ic2VydmVyID0gdm9pZCAwO1xuICAgIHRoaXMuY29uZmlnID0gdm9pZCAwO1xuICAgIHRoaXMub2JzZXJ2ZXIgPSBvYnNlcnZlcjtcbiAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZztcbiAgfVxuICByZXNldEluaXRTZWdtZW50KGluaXRTZWdtZW50LCBhdWRpb0NvZGVjLCB2aWRlb0NvZGVjLCB0cmFja0R1cmF0aW9uKSB7XG4gICAgc3VwZXIucmVzZXRJbml0U2VnbWVudChpbml0U2VnbWVudCwgYXVkaW9Db2RlYywgdmlkZW9Db2RlYywgdHJhY2tEdXJhdGlvbik7XG4gICAgdGhpcy5fYXVkaW9UcmFjayA9IHtcbiAgICAgIGNvbnRhaW5lcjogJ2F1ZGlvL2FkdHMnLFxuICAgICAgdHlwZTogJ2F1ZGlvJyxcbiAgICAgIGlkOiAyLFxuICAgICAgcGlkOiAtMSxcbiAgICAgIHNlcXVlbmNlTnVtYmVyOiAwLFxuICAgICAgc2VnbWVudENvZGVjOiAnYWFjJyxcbiAgICAgIHNhbXBsZXM6IFtdLFxuICAgICAgbWFuaWZlc3RDb2RlYzogYXVkaW9Db2RlYyxcbiAgICAgIGR1cmF0aW9uOiB0cmFja0R1cmF0aW9uLFxuICAgICAgaW5wdXRUaW1lU2NhbGU6IDkwMDAwLFxuICAgICAgZHJvcHBlZDogMFxuICAgIH07XG4gIH1cblxuICAvLyBTb3VyY2UgZm9yIHByb2JlIGluZm8gLSBodHRwczovL3dpa2kubXVsdGltZWRpYS5jeC9pbmRleC5waHA/dGl0bGU9QURUU1xuICBzdGF0aWMgcHJvYmUoZGF0YSkge1xuICAgIGlmICghZGF0YSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIC8vIENoZWNrIGZvciB0aGUgQURUUyBzeW5jIHdvcmRcbiAgICAvLyBMb29rIGZvciBBRFRTIGhlYWRlciB8IDExMTEgMTExMSB8IDExMTEgWDAwWCB8IHdoZXJlIFggY2FuIGJlIGVpdGhlciAwIG9yIDFcbiAgICAvLyBMYXllciBiaXRzIChwb3NpdGlvbiAxNCBhbmQgMTUpIGluIGhlYWRlciBzaG91bGQgYmUgYWx3YXlzIDAgZm9yIEFEVFNcbiAgICAvLyBNb3JlIGluZm8gaHR0cHM6Ly93aWtpLm11bHRpbWVkaWEuY3gvaW5kZXgucGhwP3RpdGxlPUFEVFNcbiAgICBjb25zdCBpZDNEYXRhID0gZ2V0SUQzRGF0YShkYXRhLCAwKTtcbiAgICBsZXQgb2Zmc2V0ID0gKGlkM0RhdGEgPT0gbnVsbCA/IHZvaWQgMCA6IGlkM0RhdGEubGVuZ3RoKSB8fCAwO1xuICAgIGlmIChwcm9iZShkYXRhLCBvZmZzZXQpKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGZvciAobGV0IGxlbmd0aCA9IGRhdGEubGVuZ3RoOyBvZmZzZXQgPCBsZW5ndGg7IG9mZnNldCsrKSB7XG4gICAgICBpZiAocHJvYmUkMShkYXRhLCBvZmZzZXQpKSB7XG4gICAgICAgIGxvZ2dlci5sb2coJ0FEVFMgc3luYyB3b3JkIGZvdW5kICEnKTtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICBjYW5QYXJzZShkYXRhLCBvZmZzZXQpIHtcbiAgICByZXR1cm4gY2FuUGFyc2UkMShkYXRhLCBvZmZzZXQpO1xuICB9XG4gIGFwcGVuZEZyYW1lKHRyYWNrLCBkYXRhLCBvZmZzZXQpIHtcbiAgICBpbml0VHJhY2tDb25maWcodHJhY2ssIHRoaXMub2JzZXJ2ZXIsIGRhdGEsIG9mZnNldCwgdHJhY2subWFuaWZlc3RDb2RlYyk7XG4gICAgY29uc3QgZnJhbWUgPSBhcHBlbmRGcmFtZSQyKHRyYWNrLCBkYXRhLCBvZmZzZXQsIHRoaXMuYmFzZVBUUywgdGhpcy5mcmFtZUluZGV4KTtcbiAgICBpZiAoZnJhbWUgJiYgZnJhbWUubWlzc2luZyA9PT0gMCkge1xuICAgICAgcmV0dXJuIGZyYW1lO1xuICAgIH1cbiAgfVxufVxuXG5jb25zdCBlbXNnU2NoZW1lUGF0dGVybiA9IC9cXC9lbXNnWy0vXUlEMy9pO1xuY2xhc3MgTVA0RGVtdXhlciB7XG4gIGNvbnN0cnVjdG9yKG9ic2VydmVyLCBjb25maWcpIHtcbiAgICB0aGlzLnJlbWFpbmRlckRhdGEgPSBudWxsO1xuICAgIHRoaXMudGltZU9mZnNldCA9IDA7XG4gICAgdGhpcy5jb25maWcgPSB2b2lkIDA7XG4gICAgdGhpcy52aWRlb1RyYWNrID0gdm9pZCAwO1xuICAgIHRoaXMuYXVkaW9UcmFjayA9IHZvaWQgMDtcbiAgICB0aGlzLmlkM1RyYWNrID0gdm9pZCAwO1xuICAgIHRoaXMudHh0VHJhY2sgPSB2b2lkIDA7XG4gICAgdGhpcy5jb25maWcgPSBjb25maWc7XG4gIH1cbiAgcmVzZXRUaW1lU3RhbXAoKSB7fVxuICByZXNldEluaXRTZWdtZW50KGluaXRTZWdtZW50LCBhdWRpb0NvZGVjLCB2aWRlb0NvZGVjLCB0cmFja0R1cmF0aW9uKSB7XG4gICAgY29uc3QgdmlkZW9UcmFjayA9IHRoaXMudmlkZW9UcmFjayA9IGR1bW15VHJhY2soJ3ZpZGVvJywgMSk7XG4gICAgY29uc3QgYXVkaW9UcmFjayA9IHRoaXMuYXVkaW9UcmFjayA9IGR1bW15VHJhY2soJ2F1ZGlvJywgMSk7XG4gICAgY29uc3QgY2FwdGlvblRyYWNrID0gdGhpcy50eHRUcmFjayA9IGR1bW15VHJhY2soJ3RleHQnLCAxKTtcbiAgICB0aGlzLmlkM1RyYWNrID0gZHVtbXlUcmFjaygnaWQzJywgMSk7XG4gICAgdGhpcy50aW1lT2Zmc2V0ID0gMDtcbiAgICBpZiAoIShpbml0U2VnbWVudCAhPSBudWxsICYmIGluaXRTZWdtZW50LmJ5dGVMZW5ndGgpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGluaXREYXRhID0gcGFyc2VJbml0U2VnbWVudChpbml0U2VnbWVudCk7XG4gICAgaWYgKGluaXREYXRhLnZpZGVvKSB7XG4gICAgICBjb25zdCB7XG4gICAgICAgIGlkLFxuICAgICAgICB0aW1lc2NhbGUsXG4gICAgICAgIGNvZGVjXG4gICAgICB9ID0gaW5pdERhdGEudmlkZW87XG4gICAgICB2aWRlb1RyYWNrLmlkID0gaWQ7XG4gICAgICB2aWRlb1RyYWNrLnRpbWVzY2FsZSA9IGNhcHRpb25UcmFjay50aW1lc2NhbGUgPSB0aW1lc2NhbGU7XG4gICAgICB2aWRlb1RyYWNrLmNvZGVjID0gY29kZWM7XG4gICAgfVxuICAgIGlmIChpbml0RGF0YS5hdWRpbykge1xuICAgICAgY29uc3Qge1xuICAgICAgICBpZCxcbiAgICAgICAgdGltZXNjYWxlLFxuICAgICAgICBjb2RlY1xuICAgICAgfSA9IGluaXREYXRhLmF1ZGlvO1xuICAgICAgYXVkaW9UcmFjay5pZCA9IGlkO1xuICAgICAgYXVkaW9UcmFjay50aW1lc2NhbGUgPSB0aW1lc2NhbGU7XG4gICAgICBhdWRpb1RyYWNrLmNvZGVjID0gY29kZWM7XG4gICAgfVxuICAgIGNhcHRpb25UcmFjay5pZCA9IFJlbXV4ZXJUcmFja0lkQ29uZmlnLnRleHQ7XG4gICAgdmlkZW9UcmFjay5zYW1wbGVEdXJhdGlvbiA9IDA7XG4gICAgdmlkZW9UcmFjay5kdXJhdGlvbiA9IGF1ZGlvVHJhY2suZHVyYXRpb24gPSB0cmFja0R1cmF0aW9uO1xuICB9XG4gIHJlc2V0Q29udGlndWl0eSgpIHtcbiAgICB0aGlzLnJlbWFpbmRlckRhdGEgPSBudWxsO1xuICB9XG4gIHN0YXRpYyBwcm9iZShkYXRhKSB7XG4gICAgcmV0dXJuIGhhc01vb2ZEYXRhKGRhdGEpO1xuICB9XG4gIGRlbXV4KGRhdGEsIHRpbWVPZmZzZXQpIHtcbiAgICB0aGlzLnRpbWVPZmZzZXQgPSB0aW1lT2Zmc2V0O1xuICAgIC8vIExvYWQgYWxsIGRhdGEgaW50byB0aGUgYXZjIHRyYWNrLiBUaGUgQ01BRiByZW11eGVyIHdpbGwgbG9vayBmb3IgdGhlIGRhdGEgaW4gdGhlIHNhbXBsZXMgb2JqZWN0OyB0aGUgcmVzdCBvZiB0aGUgZmllbGRzIGRvIG5vdCBtYXR0ZXJcbiAgICBsZXQgdmlkZW9TYW1wbGVzID0gZGF0YTtcbiAgICBjb25zdCB2aWRlb1RyYWNrID0gdGhpcy52aWRlb1RyYWNrO1xuICAgIGNvbnN0IHRleHRUcmFjayA9IHRoaXMudHh0VHJhY2s7XG4gICAgaWYgKHRoaXMuY29uZmlnLnByb2dyZXNzaXZlKSB7XG4gICAgICAvLyBTcGxpdCB0aGUgYnl0ZXN0cmVhbSBpbnRvIHR3byByYW5nZXM6IG9uZSBlbmNvbXBhc3NpbmcgYWxsIGRhdGEgdXAgdW50aWwgdGhlIHN0YXJ0IG9mIHRoZSBsYXN0IG1vb2YsIGFuZCBldmVyeXRoaW5nIGVsc2UuXG4gICAgICAvLyBUaGlzIGlzIGRvbmUgdG8gZ3VhcmFudGVlIHRoYXQgd2UncmUgc2VuZGluZyB2YWxpZCBkYXRhIHRvIE1TRSAtIHdoZW4gZGVtdXhpbmcgcHJvZ3Jlc3NpdmVseSwgd2UgaGF2ZSBubyBndWFyYW50ZWVcbiAgICAgIC8vIHRoYXQgdGhlIGZldGNoIGxvYWRlciBnaXZlcyB1cyBmbHVzaCBtb29mK21kYXQgcGFpcnMuIElmIHdlIHB1c2ggamFnZ2VkIGRhdGEgdG8gTVNFLCBpdCB3aWxsIHRocm93IGFuIGV4Y2VwdGlvbi5cbiAgICAgIGlmICh0aGlzLnJlbWFpbmRlckRhdGEpIHtcbiAgICAgICAgdmlkZW9TYW1wbGVzID0gYXBwZW5kVWludDhBcnJheSh0aGlzLnJlbWFpbmRlckRhdGEsIGRhdGEpO1xuICAgICAgfVxuICAgICAgY29uc3Qgc2VnbWVudGVkRGF0YSA9IHNlZ21lbnRWYWxpZFJhbmdlKHZpZGVvU2FtcGxlcyk7XG4gICAgICB0aGlzLnJlbWFpbmRlckRhdGEgPSBzZWdtZW50ZWREYXRhLnJlbWFpbmRlcjtcbiAgICAgIHZpZGVvVHJhY2suc2FtcGxlcyA9IHNlZ21lbnRlZERhdGEudmFsaWQgfHwgbmV3IFVpbnQ4QXJyYXkoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdmlkZW9UcmFjay5zYW1wbGVzID0gdmlkZW9TYW1wbGVzO1xuICAgIH1cbiAgICBjb25zdCBpZDNUcmFjayA9IHRoaXMuZXh0cmFjdElEM1RyYWNrKHZpZGVvVHJhY2ssIHRpbWVPZmZzZXQpO1xuICAgIHRleHRUcmFjay5zYW1wbGVzID0gcGFyc2VTYW1wbGVzKHRpbWVPZmZzZXQsIHZpZGVvVHJhY2spO1xuICAgIHJldHVybiB7XG4gICAgICB2aWRlb1RyYWNrLFxuICAgICAgYXVkaW9UcmFjazogdGhpcy5hdWRpb1RyYWNrLFxuICAgICAgaWQzVHJhY2ssXG4gICAgICB0ZXh0VHJhY2s6IHRoaXMudHh0VHJhY2tcbiAgICB9O1xuICB9XG4gIGZsdXNoKCkge1xuICAgIGNvbnN0IHRpbWVPZmZzZXQgPSB0aGlzLnRpbWVPZmZzZXQ7XG4gICAgY29uc3QgdmlkZW9UcmFjayA9IHRoaXMudmlkZW9UcmFjaztcbiAgICBjb25zdCB0ZXh0VHJhY2sgPSB0aGlzLnR4dFRyYWNrO1xuICAgIHZpZGVvVHJhY2suc2FtcGxlcyA9IHRoaXMucmVtYWluZGVyRGF0YSB8fCBuZXcgVWludDhBcnJheSgpO1xuICAgIHRoaXMucmVtYWluZGVyRGF0YSA9IG51bGw7XG4gICAgY29uc3QgaWQzVHJhY2sgPSB0aGlzLmV4dHJhY3RJRDNUcmFjayh2aWRlb1RyYWNrLCB0aGlzLnRpbWVPZmZzZXQpO1xuICAgIHRleHRUcmFjay5zYW1wbGVzID0gcGFyc2VTYW1wbGVzKHRpbWVPZmZzZXQsIHZpZGVvVHJhY2spO1xuICAgIHJldHVybiB7XG4gICAgICB2aWRlb1RyYWNrLFxuICAgICAgYXVkaW9UcmFjazogZHVtbXlUcmFjaygpLFxuICAgICAgaWQzVHJhY2ssXG4gICAgICB0ZXh0VHJhY2s6IGR1bW15VHJhY2soKVxuICAgIH07XG4gIH1cbiAgZXh0cmFjdElEM1RyYWNrKHZpZGVvVHJhY2ssIHRpbWVPZmZzZXQpIHtcbiAgICBjb25zdCBpZDNUcmFjayA9IHRoaXMuaWQzVHJhY2s7XG4gICAgaWYgKHZpZGVvVHJhY2suc2FtcGxlcy5sZW5ndGgpIHtcbiAgICAgIGNvbnN0IGVtc2dzID0gZmluZEJveCh2aWRlb1RyYWNrLnNhbXBsZXMsIFsnZW1zZyddKTtcbiAgICAgIGlmIChlbXNncykge1xuICAgICAgICBlbXNncy5mb3JFYWNoKGRhdGEgPT4ge1xuICAgICAgICAgIGNvbnN0IGVtc2dJbmZvID0gcGFyc2VFbXNnKGRhdGEpO1xuICAgICAgICAgIGlmIChlbXNnU2NoZW1lUGF0dGVybi50ZXN0KGVtc2dJbmZvLnNjaGVtZUlkVXJpKSkge1xuICAgICAgICAgICAgY29uc3QgcHRzID0gaXNGaW5pdGVOdW1iZXIoZW1zZ0luZm8ucHJlc2VudGF0aW9uVGltZSkgPyBlbXNnSW5mby5wcmVzZW50YXRpb25UaW1lIC8gZW1zZ0luZm8udGltZVNjYWxlIDogdGltZU9mZnNldCArIGVtc2dJbmZvLnByZXNlbnRhdGlvblRpbWVEZWx0YSAvIGVtc2dJbmZvLnRpbWVTY2FsZTtcbiAgICAgICAgICAgIGxldCBkdXJhdGlvbiA9IGVtc2dJbmZvLmV2ZW50RHVyYXRpb24gPT09IDB4ZmZmZmZmZmYgPyBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFkgOiBlbXNnSW5mby5ldmVudER1cmF0aW9uIC8gZW1zZ0luZm8udGltZVNjYWxlO1xuICAgICAgICAgICAgLy8gU2FmYXJpIHRha2VzIGFueXRoaW5nIDw9IDAuMDAxIHNlY29uZHMgYW5kIG1hcHMgaXQgdG8gSW5maW5pdHlcbiAgICAgICAgICAgIGlmIChkdXJhdGlvbiA8PSAwLjAwMSkge1xuICAgICAgICAgICAgICBkdXJhdGlvbiA9IE51bWJlci5QT1NJVElWRV9JTkZJTklUWTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IHBheWxvYWQgPSBlbXNnSW5mby5wYXlsb2FkO1xuICAgICAgICAgICAgaWQzVHJhY2suc2FtcGxlcy5wdXNoKHtcbiAgICAgICAgICAgICAgZGF0YTogcGF5bG9hZCxcbiAgICAgICAgICAgICAgbGVuOiBwYXlsb2FkLmJ5dGVMZW5ndGgsXG4gICAgICAgICAgICAgIGR0czogcHRzLFxuICAgICAgICAgICAgICBwdHM6IHB0cyxcbiAgICAgICAgICAgICAgdHlwZTogTWV0YWRhdGFTY2hlbWEuZW1zZyxcbiAgICAgICAgICAgICAgZHVyYXRpb246IGR1cmF0aW9uXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gaWQzVHJhY2s7XG4gIH1cbiAgZGVtdXhTYW1wbGVBZXMoZGF0YSwga2V5RGF0YSwgdGltZU9mZnNldCkge1xuICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoJ1RoZSBNUDQgZGVtdXhlciBkb2VzIG5vdCBzdXBwb3J0IFNBTVBMRS1BRVMgZGVjcnlwdGlvbicpKTtcbiAgfVxuICBkZXN0cm95KCkge31cbn1cblxuY29uc3QgZ2V0QXVkaW9CU0lEID0gKGRhdGEsIG9mZnNldCkgPT4ge1xuICAvLyBjaGVjayB0aGUgYnNpZCB0byBjb25maXJtIGFjLTMgfCBlYy0zXG4gIGxldCBic2lkID0gMDtcbiAgbGV0IG51bUJpdHMgPSA1O1xuICBvZmZzZXQgKz0gbnVtQml0cztcbiAgY29uc3QgdGVtcCA9IG5ldyBVaW50MzJBcnJheSgxKTsgLy8gdW5zaWduZWQgMzIgYml0IGZvciB0ZW1wb3Jhcnkgc3RvcmFnZVxuICBjb25zdCBtYXNrID0gbmV3IFVpbnQzMkFycmF5KDEpOyAvLyB1bnNpZ25lZCAzMiBiaXQgbWFzayB2YWx1ZVxuICBjb25zdCBieXRlID0gbmV3IFVpbnQ4QXJyYXkoMSk7IC8vIHVuc2lnbmVkIDggYml0IGZvciB0ZW1wb3Jhcnkgc3RvcmFnZVxuICB3aGlsZSAobnVtQml0cyA+IDApIHtcbiAgICBieXRlWzBdID0gZGF0YVtvZmZzZXRdO1xuICAgIC8vIHJlYWQgcmVtYWluaW5nIGJpdHMsIHVwdG8gOCBiaXRzIGF0IGEgdGltZVxuICAgIGNvbnN0IGJpdHMgPSBNYXRoLm1pbihudW1CaXRzLCA4KTtcbiAgICBjb25zdCBzaGlmdCA9IDggLSBiaXRzO1xuICAgIG1hc2tbMF0gPSAweGZmMDAwMDAwID4+PiAyNCArIHNoaWZ0IDw8IHNoaWZ0O1xuICAgIHRlbXBbMF0gPSAoYnl0ZVswXSAmIG1hc2tbMF0pID4+IHNoaWZ0O1xuICAgIGJzaWQgPSAhYnNpZCA/IHRlbXBbMF0gOiBic2lkIDw8IGJpdHMgfCB0ZW1wWzBdO1xuICAgIG9mZnNldCArPSAxO1xuICAgIG51bUJpdHMgLT0gYml0cztcbiAgfVxuICByZXR1cm4gYnNpZDtcbn07XG5cbmNsYXNzIEFDM0RlbXV4ZXIgZXh0ZW5kcyBCYXNlQXVkaW9EZW11eGVyIHtcbiAgY29uc3RydWN0b3Iob2JzZXJ2ZXIpIHtcbiAgICBzdXBlcigpO1xuICAgIHRoaXMub2JzZXJ2ZXIgPSB2b2lkIDA7XG4gICAgdGhpcy5vYnNlcnZlciA9IG9ic2VydmVyO1xuICB9XG4gIHJlc2V0SW5pdFNlZ21lbnQoaW5pdFNlZ21lbnQsIGF1ZGlvQ29kZWMsIHZpZGVvQ29kZWMsIHRyYWNrRHVyYXRpb24pIHtcbiAgICBzdXBlci5yZXNldEluaXRTZWdtZW50KGluaXRTZWdtZW50LCBhdWRpb0NvZGVjLCB2aWRlb0NvZGVjLCB0cmFja0R1cmF0aW9uKTtcbiAgICB0aGlzLl9hdWRpb1RyYWNrID0ge1xuICAgICAgY29udGFpbmVyOiAnYXVkaW8vYWMtMycsXG4gICAgICB0eXBlOiAnYXVkaW8nLFxuICAgICAgaWQ6IDIsXG4gICAgICBwaWQ6IC0xLFxuICAgICAgc2VxdWVuY2VOdW1iZXI6IDAsXG4gICAgICBzZWdtZW50Q29kZWM6ICdhYzMnLFxuICAgICAgc2FtcGxlczogW10sXG4gICAgICBtYW5pZmVzdENvZGVjOiBhdWRpb0NvZGVjLFxuICAgICAgZHVyYXRpb246IHRyYWNrRHVyYXRpb24sXG4gICAgICBpbnB1dFRpbWVTY2FsZTogOTAwMDAsXG4gICAgICBkcm9wcGVkOiAwXG4gICAgfTtcbiAgfVxuICBjYW5QYXJzZShkYXRhLCBvZmZzZXQpIHtcbiAgICByZXR1cm4gb2Zmc2V0ICsgNjQgPCBkYXRhLmxlbmd0aDtcbiAgfVxuICBhcHBlbmRGcmFtZSh0cmFjaywgZGF0YSwgb2Zmc2V0KSB7XG4gICAgY29uc3QgZnJhbWVMZW5ndGggPSBhcHBlbmRGcmFtZSh0cmFjaywgZGF0YSwgb2Zmc2V0LCB0aGlzLmJhc2VQVFMsIHRoaXMuZnJhbWVJbmRleCk7XG4gICAgaWYgKGZyYW1lTGVuZ3RoICE9PSAtMSkge1xuICAgICAgY29uc3Qgc2FtcGxlID0gdHJhY2suc2FtcGxlc1t0cmFjay5zYW1wbGVzLmxlbmd0aCAtIDFdO1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgc2FtcGxlLFxuICAgICAgICBsZW5ndGg6IGZyYW1lTGVuZ3RoLFxuICAgICAgICBtaXNzaW5nOiAwXG4gICAgICB9O1xuICAgIH1cbiAgfVxuICBzdGF0aWMgcHJvYmUoZGF0YSkge1xuICAgIGlmICghZGF0YSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBjb25zdCBpZDNEYXRhID0gZ2V0SUQzRGF0YShkYXRhLCAwKTtcbiAgICBpZiAoIWlkM0RhdGEpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICAvLyBsb29rIGZvciB0aGUgYWMtMyBzeW5jIGJ5dGVzXG4gICAgY29uc3Qgb2Zmc2V0ID0gaWQzRGF0YS5sZW5ndGg7XG4gICAgaWYgKGRhdGFbb2Zmc2V0XSA9PT0gMHgwYiAmJiBkYXRhW29mZnNldCArIDFdID09PSAweDc3ICYmIGdldFRpbWVTdGFtcChpZDNEYXRhKSAhPT0gdW5kZWZpbmVkICYmXG4gICAgLy8gY2hlY2sgdGhlIGJzaWQgdG8gY29uZmlybSBhYy0zXG4gICAgZ2V0QXVkaW9CU0lEKGRhdGEsIG9mZnNldCkgPCAxNikge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxufVxuZnVuY3Rpb24gYXBwZW5kRnJhbWUodHJhY2ssIGRhdGEsIHN0YXJ0LCBwdHMsIGZyYW1lSW5kZXgpIHtcbiAgaWYgKHN0YXJ0ICsgOCA+IGRhdGEubGVuZ3RoKSB7XG4gICAgcmV0dXJuIC0xOyAvLyBub3QgZW5vdWdoIGJ5dGVzIGxlZnRcbiAgfVxuICBpZiAoZGF0YVtzdGFydF0gIT09IDB4MGIgfHwgZGF0YVtzdGFydCArIDFdICE9PSAweDc3KSB7XG4gICAgcmV0dXJuIC0xOyAvLyBpbnZhbGlkIG1hZ2ljXG4gIH1cblxuICAvLyBnZXQgc2FtcGxlIHJhdGVcbiAgY29uc3Qgc2FtcGxpbmdSYXRlQ29kZSA9IGRhdGFbc3RhcnQgKyA0XSA+PiA2O1xuICBpZiAoc2FtcGxpbmdSYXRlQ29kZSA+PSAzKSB7XG4gICAgcmV0dXJuIC0xOyAvLyBpbnZhbGlkIHNhbXBsaW5nIHJhdGVcbiAgfVxuICBjb25zdCBzYW1wbGluZ1JhdGVNYXAgPSBbNDgwMDAsIDQ0MTAwLCAzMjAwMF07XG4gIGNvbnN0IHNhbXBsZVJhdGUgPSBzYW1wbGluZ1JhdGVNYXBbc2FtcGxpbmdSYXRlQ29kZV07XG5cbiAgLy8gZ2V0IGZyYW1lIHNpemVcbiAgY29uc3QgZnJhbWVTaXplQ29kZSA9IGRhdGFbc3RhcnQgKyA0XSAmIDB4M2Y7XG4gIGNvbnN0IGZyYW1lU2l6ZU1hcCA9IFs2NCwgNjksIDk2LCA2NCwgNzAsIDk2LCA4MCwgODcsIDEyMCwgODAsIDg4LCAxMjAsIDk2LCAxMDQsIDE0NCwgOTYsIDEwNSwgMTQ0LCAxMTIsIDEyMSwgMTY4LCAxMTIsIDEyMiwgMTY4LCAxMjgsIDEzOSwgMTkyLCAxMjgsIDE0MCwgMTkyLCAxNjAsIDE3NCwgMjQwLCAxNjAsIDE3NSwgMjQwLCAxOTIsIDIwOCwgMjg4LCAxOTIsIDIwOSwgMjg4LCAyMjQsIDI0MywgMzM2LCAyMjQsIDI0NCwgMzM2LCAyNTYsIDI3OCwgMzg0LCAyNTYsIDI3OSwgMzg0LCAzMjAsIDM0OCwgNDgwLCAzMjAsIDM0OSwgNDgwLCAzODQsIDQxNywgNTc2LCAzODQsIDQxOCwgNTc2LCA0NDgsIDQ4NywgNjcyLCA0NDgsIDQ4OCwgNjcyLCA1MTIsIDU1NywgNzY4LCA1MTIsIDU1OCwgNzY4LCA2NDAsIDY5NiwgOTYwLCA2NDAsIDY5NywgOTYwLCA3NjgsIDgzNSwgMTE1MiwgNzY4LCA4MzYsIDExNTIsIDg5NiwgOTc1LCAxMzQ0LCA4OTYsIDk3NiwgMTM0NCwgMTAyNCwgMTExNCwgMTUzNiwgMTAyNCwgMTExNSwgMTUzNiwgMTE1MiwgMTI1MywgMTcyOCwgMTE1MiwgMTI1NCwgMTcyOCwgMTI4MCwgMTM5MywgMTkyMCwgMTI4MCwgMTM5NCwgMTkyMF07XG4gIGNvbnN0IGZyYW1lTGVuZ3RoID0gZnJhbWVTaXplTWFwW2ZyYW1lU2l6ZUNvZGUgKiAzICsgc2FtcGxpbmdSYXRlQ29kZV0gKiAyO1xuICBpZiAoc3RhcnQgKyBmcmFtZUxlbmd0aCA+IGRhdGEubGVuZ3RoKSB7XG4gICAgcmV0dXJuIC0xO1xuICB9XG5cbiAgLy8gZ2V0IGNoYW5uZWwgY291bnRcbiAgY29uc3QgY2hhbm5lbE1vZGUgPSBkYXRhW3N0YXJ0ICsgNl0gPj4gNTtcbiAgbGV0IHNraXBDb3VudCA9IDA7XG4gIGlmIChjaGFubmVsTW9kZSA9PT0gMikge1xuICAgIHNraXBDb3VudCArPSAyO1xuICB9IGVsc2Uge1xuICAgIGlmIChjaGFubmVsTW9kZSAmIDEgJiYgY2hhbm5lbE1vZGUgIT09IDEpIHtcbiAgICAgIHNraXBDb3VudCArPSAyO1xuICAgIH1cbiAgICBpZiAoY2hhbm5lbE1vZGUgJiA0KSB7XG4gICAgICBza2lwQ291bnQgKz0gMjtcbiAgICB9XG4gIH1cbiAgY29uc3QgbGZlb24gPSAoZGF0YVtzdGFydCArIDZdIDw8IDggfCBkYXRhW3N0YXJ0ICsgN10pID4+IDEyIC0gc2tpcENvdW50ICYgMTtcbiAgY29uc3QgY2hhbm5lbHNNYXAgPSBbMiwgMSwgMiwgMywgMywgNCwgNCwgNV07XG4gIGNvbnN0IGNoYW5uZWxDb3VudCA9IGNoYW5uZWxzTWFwW2NoYW5uZWxNb2RlXSArIGxmZW9uO1xuXG4gIC8vIGJ1aWxkIGRhYzMgYm94XG4gIGNvbnN0IGJzaWQgPSBkYXRhW3N0YXJ0ICsgNV0gPj4gMztcbiAgY29uc3QgYnNtb2QgPSBkYXRhW3N0YXJ0ICsgNV0gJiA3O1xuICBjb25zdCBjb25maWcgPSBuZXcgVWludDhBcnJheShbc2FtcGxpbmdSYXRlQ29kZSA8PCA2IHwgYnNpZCA8PCAxIHwgYnNtb2QgPj4gMiwgKGJzbW9kICYgMykgPDwgNiB8IGNoYW5uZWxNb2RlIDw8IDMgfCBsZmVvbiA8PCAyIHwgZnJhbWVTaXplQ29kZSA+PiA0LCBmcmFtZVNpemVDb2RlIDw8IDQgJiAweGUwXSk7XG4gIGNvbnN0IGZyYW1lRHVyYXRpb24gPSAxNTM2IC8gc2FtcGxlUmF0ZSAqIDkwMDAwO1xuICBjb25zdCBzdGFtcCA9IHB0cyArIGZyYW1lSW5kZXggKiBmcmFtZUR1cmF0aW9uO1xuICBjb25zdCB1bml0ID0gZGF0YS5zdWJhcnJheShzdGFydCwgc3RhcnQgKyBmcmFtZUxlbmd0aCk7XG4gIHRyYWNrLmNvbmZpZyA9IGNvbmZpZztcbiAgdHJhY2suY2hhbm5lbENvdW50ID0gY2hhbm5lbENvdW50O1xuICB0cmFjay5zYW1wbGVyYXRlID0gc2FtcGxlUmF0ZTtcbiAgdHJhY2suc2FtcGxlcy5wdXNoKHtcbiAgICB1bml0LFxuICAgIHB0czogc3RhbXBcbiAgfSk7XG4gIHJldHVybiBmcmFtZUxlbmd0aDtcbn1cblxuY2xhc3MgQmFzZVZpZGVvUGFyc2VyIHtcbiAgY29uc3RydWN0b3IoKSB7XG4gICAgdGhpcy5WaWRlb1NhbXBsZSA9IG51bGw7XG4gIH1cbiAgY3JlYXRlVmlkZW9TYW1wbGUoa2V5LCBwdHMsIGR0cywgZGVidWcpIHtcbiAgICByZXR1cm4ge1xuICAgICAga2V5LFxuICAgICAgZnJhbWU6IGZhbHNlLFxuICAgICAgcHRzLFxuICAgICAgZHRzLFxuICAgICAgdW5pdHM6IFtdLFxuICAgICAgZGVidWcsXG4gICAgICBsZW5ndGg6IDBcbiAgICB9O1xuICB9XG4gIGdldExhc3ROYWxVbml0KHNhbXBsZXMpIHtcbiAgICB2YXIgX1ZpZGVvU2FtcGxlO1xuICAgIGxldCBWaWRlb1NhbXBsZSA9IHRoaXMuVmlkZW9TYW1wbGU7XG4gICAgbGV0IGxhc3RVbml0O1xuICAgIC8vIHRyeSB0byBmYWxsYmFjayB0byBwcmV2aW91cyBzYW1wbGUgaWYgY3VycmVudCBvbmUgaXMgZW1wdHlcbiAgICBpZiAoIVZpZGVvU2FtcGxlIHx8IFZpZGVvU2FtcGxlLnVuaXRzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgVmlkZW9TYW1wbGUgPSBzYW1wbGVzW3NhbXBsZXMubGVuZ3RoIC0gMV07XG4gICAgfVxuICAgIGlmICgoX1ZpZGVvU2FtcGxlID0gVmlkZW9TYW1wbGUpICE9IG51bGwgJiYgX1ZpZGVvU2FtcGxlLnVuaXRzKSB7XG4gICAgICBjb25zdCB1bml0cyA9IFZpZGVvU2FtcGxlLnVuaXRzO1xuICAgICAgbGFzdFVuaXQgPSB1bml0c1t1bml0cy5sZW5ndGggLSAxXTtcbiAgICB9XG4gICAgcmV0dXJuIGxhc3RVbml0O1xuICB9XG4gIHB1c2hBY2Nlc3NVbml0KFZpZGVvU2FtcGxlLCB2aWRlb1RyYWNrKSB7XG4gICAgaWYgKFZpZGVvU2FtcGxlLnVuaXRzLmxlbmd0aCAmJiBWaWRlb1NhbXBsZS5mcmFtZSkge1xuICAgICAgLy8gaWYgc2FtcGxlIGRvZXMgbm90IGhhdmUgUFRTL0RUUywgcGF0Y2ggd2l0aCBsYXN0IHNhbXBsZSBQVFMvRFRTXG4gICAgICBpZiAoVmlkZW9TYW1wbGUucHRzID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgY29uc3Qgc2FtcGxlcyA9IHZpZGVvVHJhY2suc2FtcGxlcztcbiAgICAgICAgY29uc3QgbmJTYW1wbGVzID0gc2FtcGxlcy5sZW5ndGg7XG4gICAgICAgIGlmIChuYlNhbXBsZXMpIHtcbiAgICAgICAgICBjb25zdCBsYXN0U2FtcGxlID0gc2FtcGxlc1tuYlNhbXBsZXMgLSAxXTtcbiAgICAgICAgICBWaWRlb1NhbXBsZS5wdHMgPSBsYXN0U2FtcGxlLnB0cztcbiAgICAgICAgICBWaWRlb1NhbXBsZS5kdHMgPSBsYXN0U2FtcGxlLmR0cztcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBkcm9wcGluZyBzYW1wbGVzLCBubyB0aW1lc3RhbXAgZm91bmRcbiAgICAgICAgICB2aWRlb1RyYWNrLmRyb3BwZWQrKztcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHZpZGVvVHJhY2suc2FtcGxlcy5wdXNoKFZpZGVvU2FtcGxlKTtcbiAgICB9XG4gICAgaWYgKFZpZGVvU2FtcGxlLmRlYnVnLmxlbmd0aCkge1xuICAgICAgbG9nZ2VyLmxvZyhWaWRlb1NhbXBsZS5wdHMgKyAnLycgKyBWaWRlb1NhbXBsZS5kdHMgKyAnOicgKyBWaWRlb1NhbXBsZS5kZWJ1Zyk7XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogUGFyc2VyIGZvciBleHBvbmVudGlhbCBHb2xvbWIgY29kZXMsIGEgdmFyaWFibGUtYml0d2lkdGggbnVtYmVyIGVuY29kaW5nIHNjaGVtZSB1c2VkIGJ5IGgyNjQuXG4gKi9cblxuY2xhc3MgRXhwR29sb21iIHtcbiAgY29uc3RydWN0b3IoZGF0YSkge1xuICAgIHRoaXMuZGF0YSA9IHZvaWQgMDtcbiAgICB0aGlzLmJ5dGVzQXZhaWxhYmxlID0gdm9pZCAwO1xuICAgIHRoaXMud29yZCA9IHZvaWQgMDtcbiAgICB0aGlzLmJpdHNBdmFpbGFibGUgPSB2b2lkIDA7XG4gICAgdGhpcy5kYXRhID0gZGF0YTtcbiAgICAvLyB0aGUgbnVtYmVyIG9mIGJ5dGVzIGxlZnQgdG8gZXhhbWluZSBpbiB0aGlzLmRhdGFcbiAgICB0aGlzLmJ5dGVzQXZhaWxhYmxlID0gZGF0YS5ieXRlTGVuZ3RoO1xuICAgIC8vIHRoZSBjdXJyZW50IHdvcmQgYmVpbmcgZXhhbWluZWRcbiAgICB0aGlzLndvcmQgPSAwOyAvLyA6dWludFxuICAgIC8vIHRoZSBudW1iZXIgb2YgYml0cyBsZWZ0IHRvIGV4YW1pbmUgaW4gdGhlIGN1cnJlbnQgd29yZFxuICAgIHRoaXMuYml0c0F2YWlsYWJsZSA9IDA7IC8vIDp1aW50XG4gIH1cblxuICAvLyAoKTp2b2lkXG4gIGxvYWRXb3JkKCkge1xuICAgIGNvbnN0IGRhdGEgPSB0aGlzLmRhdGE7XG4gICAgY29uc3QgYnl0ZXNBdmFpbGFibGUgPSB0aGlzLmJ5dGVzQXZhaWxhYmxlO1xuICAgIGNvbnN0IHBvc2l0aW9uID0gZGF0YS5ieXRlTGVuZ3RoIC0gYnl0ZXNBdmFpbGFibGU7XG4gICAgY29uc3Qgd29ya2luZ0J5dGVzID0gbmV3IFVpbnQ4QXJyYXkoNCk7XG4gICAgY29uc3QgYXZhaWxhYmxlQnl0ZXMgPSBNYXRoLm1pbig0LCBieXRlc0F2YWlsYWJsZSk7XG4gICAgaWYgKGF2YWlsYWJsZUJ5dGVzID09PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ25vIGJ5dGVzIGF2YWlsYWJsZScpO1xuICAgIH1cbiAgICB3b3JraW5nQnl0ZXMuc2V0KGRhdGEuc3ViYXJyYXkocG9zaXRpb24sIHBvc2l0aW9uICsgYXZhaWxhYmxlQnl0ZXMpKTtcbiAgICB0aGlzLndvcmQgPSBuZXcgRGF0YVZpZXcod29ya2luZ0J5dGVzLmJ1ZmZlcikuZ2V0VWludDMyKDApO1xuICAgIC8vIHRyYWNrIHRoZSBhbW91bnQgb2YgdGhpcy5kYXRhIHRoYXQgaGFzIGJlZW4gcHJvY2Vzc2VkXG4gICAgdGhpcy5iaXRzQXZhaWxhYmxlID0gYXZhaWxhYmxlQnl0ZXMgKiA4O1xuICAgIHRoaXMuYnl0ZXNBdmFpbGFibGUgLT0gYXZhaWxhYmxlQnl0ZXM7XG4gIH1cblxuICAvLyAoY291bnQ6aW50KTp2b2lkXG4gIHNraXBCaXRzKGNvdW50KSB7XG4gICAgbGV0IHNraXBCeXRlczsgLy8gOmludFxuICAgIGNvdW50ID0gTWF0aC5taW4oY291bnQsIHRoaXMuYnl0ZXNBdmFpbGFibGUgKiA4ICsgdGhpcy5iaXRzQXZhaWxhYmxlKTtcbiAgICBpZiAodGhpcy5iaXRzQXZhaWxhYmxlID4gY291bnQpIHtcbiAgICAgIHRoaXMud29yZCA8PD0gY291bnQ7XG4gICAgICB0aGlzLmJpdHNBdmFpbGFibGUgLT0gY291bnQ7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvdW50IC09IHRoaXMuYml0c0F2YWlsYWJsZTtcbiAgICAgIHNraXBCeXRlcyA9IGNvdW50ID4+IDM7XG4gICAgICBjb3VudCAtPSBza2lwQnl0ZXMgPDwgMztcbiAgICAgIHRoaXMuYnl0ZXNBdmFpbGFibGUgLT0gc2tpcEJ5dGVzO1xuICAgICAgdGhpcy5sb2FkV29yZCgpO1xuICAgICAgdGhpcy53b3JkIDw8PSBjb3VudDtcbiAgICAgIHRoaXMuYml0c0F2YWlsYWJsZSAtPSBjb3VudDtcbiAgICB9XG4gIH1cblxuICAvLyAoc2l6ZTppbnQpOnVpbnRcbiAgcmVhZEJpdHMoc2l6ZSkge1xuICAgIGxldCBiaXRzID0gTWF0aC5taW4odGhpcy5iaXRzQXZhaWxhYmxlLCBzaXplKTsgLy8gOnVpbnRcbiAgICBjb25zdCB2YWx1ID0gdGhpcy53b3JkID4+PiAzMiAtIGJpdHM7IC8vIDp1aW50XG4gICAgaWYgKHNpemUgPiAzMikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdDYW5ub3QgcmVhZCBtb3JlIHRoYW4gMzIgYml0cyBhdCBhIHRpbWUnKTtcbiAgICB9XG4gICAgdGhpcy5iaXRzQXZhaWxhYmxlIC09IGJpdHM7XG4gICAgaWYgKHRoaXMuYml0c0F2YWlsYWJsZSA+IDApIHtcbiAgICAgIHRoaXMud29yZCA8PD0gYml0cztcbiAgICB9IGVsc2UgaWYgKHRoaXMuYnl0ZXNBdmFpbGFibGUgPiAwKSB7XG4gICAgICB0aGlzLmxvYWRXb3JkKCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignbm8gYml0cyBhdmFpbGFibGUnKTtcbiAgICB9XG4gICAgYml0cyA9IHNpemUgLSBiaXRzO1xuICAgIGlmIChiaXRzID4gMCAmJiB0aGlzLmJpdHNBdmFpbGFibGUpIHtcbiAgICAgIHJldHVybiB2YWx1IDw8IGJpdHMgfCB0aGlzLnJlYWRCaXRzKGJpdHMpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gdmFsdTtcbiAgICB9XG4gIH1cblxuICAvLyAoKTp1aW50XG4gIHNraXBMWigpIHtcbiAgICBsZXQgbGVhZGluZ1plcm9Db3VudDsgLy8gOnVpbnRcbiAgICBmb3IgKGxlYWRpbmdaZXJvQ291bnQgPSAwOyBsZWFkaW5nWmVyb0NvdW50IDwgdGhpcy5iaXRzQXZhaWxhYmxlOyArK2xlYWRpbmdaZXJvQ291bnQpIHtcbiAgICAgIGlmICgodGhpcy53b3JkICYgMHg4MDAwMDAwMCA+Pj4gbGVhZGluZ1plcm9Db3VudCkgIT09IDApIHtcbiAgICAgICAgLy8gdGhlIGZpcnN0IGJpdCBvZiB3b3JraW5nIHdvcmQgaXMgMVxuICAgICAgICB0aGlzLndvcmQgPDw9IGxlYWRpbmdaZXJvQ291bnQ7XG4gICAgICAgIHRoaXMuYml0c0F2YWlsYWJsZSAtPSBsZWFkaW5nWmVyb0NvdW50O1xuICAgICAgICByZXR1cm4gbGVhZGluZ1plcm9Db3VudDtcbiAgICAgIH1cbiAgICB9XG4gICAgLy8gd2UgZXhoYXVzdGVkIHdvcmQgYW5kIHN0aWxsIGhhdmUgbm90IGZvdW5kIGEgMVxuICAgIHRoaXMubG9hZFdvcmQoKTtcbiAgICByZXR1cm4gbGVhZGluZ1plcm9Db3VudCArIHRoaXMuc2tpcExaKCk7XG4gIH1cblxuICAvLyAoKTp2b2lkXG4gIHNraXBVRUcoKSB7XG4gICAgdGhpcy5za2lwQml0cygxICsgdGhpcy5za2lwTFooKSk7XG4gIH1cblxuICAvLyAoKTp2b2lkXG4gIHNraXBFRygpIHtcbiAgICB0aGlzLnNraXBCaXRzKDEgKyB0aGlzLnNraXBMWigpKTtcbiAgfVxuXG4gIC8vICgpOnVpbnRcbiAgcmVhZFVFRygpIHtcbiAgICBjb25zdCBjbHogPSB0aGlzLnNraXBMWigpOyAvLyA6dWludFxuICAgIHJldHVybiB0aGlzLnJlYWRCaXRzKGNseiArIDEpIC0gMTtcbiAgfVxuXG4gIC8vICgpOmludFxuICByZWFkRUcoKSB7XG4gICAgY29uc3QgdmFsdSA9IHRoaXMucmVhZFVFRygpOyAvLyA6aW50XG4gICAgaWYgKDB4MDEgJiB2YWx1KSB7XG4gICAgICAvLyB0aGUgbnVtYmVyIGlzIG9kZCBpZiB0aGUgbG93IG9yZGVyIGJpdCBpcyBzZXRcbiAgICAgIHJldHVybiAxICsgdmFsdSA+Pj4gMTsgLy8gYWRkIDEgdG8gbWFrZSBpdCBldmVuLCBhbmQgZGl2aWRlIGJ5IDJcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIC0xICogKHZhbHUgPj4+IDEpOyAvLyBkaXZpZGUgYnkgdHdvIHRoZW4gbWFrZSBpdCBuZWdhdGl2ZVxuICAgIH1cbiAgfVxuXG4gIC8vIFNvbWUgY29udmVuaWVuY2UgZnVuY3Rpb25zXG4gIC8vIDpCb29sZWFuXG4gIHJlYWRCb29sZWFuKCkge1xuICAgIHJldHVybiB0aGlzLnJlYWRCaXRzKDEpID09PSAxO1xuICB9XG5cbiAgLy8gKCk6aW50XG4gIHJlYWRVQnl0ZSgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWFkQml0cyg4KTtcbiAgfVxuXG4gIC8vICgpOmludFxuICByZWFkVVNob3J0KCkge1xuICAgIHJldHVybiB0aGlzLnJlYWRCaXRzKDE2KTtcbiAgfVxuXG4gIC8vICgpOmludFxuICByZWFkVUludCgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWFkQml0cygzMik7XG4gIH1cblxuICAvKipcbiAgICogQWR2YW5jZSB0aGUgRXhwR29sb21iIGRlY29kZXIgcGFzdCBhIHNjYWxpbmcgbGlzdC4gVGhlIHNjYWxpbmdcbiAgICogbGlzdCBpcyBvcHRpb25hbGx5IHRyYW5zbWl0dGVkIGFzIHBhcnQgb2YgYSBzZXF1ZW5jZSBwYXJhbWV0ZXJcbiAgICogc2V0IGFuZCBpcyBub3QgcmVsZXZhbnQgdG8gdHJhbnNtdXhpbmcuXG4gICAqIEBwYXJhbSBjb3VudCB0aGUgbnVtYmVyIG9mIGVudHJpZXMgaW4gdGhpcyBzY2FsaW5nIGxpc3RcbiAgICogQHNlZSBSZWNvbW1lbmRhdGlvbiBJVFUtVCBILjI2NCwgU2VjdGlvbiA3LjMuMi4xLjEuMVxuICAgKi9cbiAgc2tpcFNjYWxpbmdMaXN0KGNvdW50KSB7XG4gICAgbGV0IGxhc3RTY2FsZSA9IDg7XG4gICAgbGV0IG5leHRTY2FsZSA9IDg7XG4gICAgbGV0IGRlbHRhU2NhbGU7XG4gICAgZm9yIChsZXQgaiA9IDA7IGogPCBjb3VudDsgaisrKSB7XG4gICAgICBpZiAobmV4dFNjYWxlICE9PSAwKSB7XG4gICAgICAgIGRlbHRhU2NhbGUgPSB0aGlzLnJlYWRFRygpO1xuICAgICAgICBuZXh0U2NhbGUgPSAobGFzdFNjYWxlICsgZGVsdGFTY2FsZSArIDI1NikgJSAyNTY7XG4gICAgICB9XG4gICAgICBsYXN0U2NhbGUgPSBuZXh0U2NhbGUgPT09IDAgPyBsYXN0U2NhbGUgOiBuZXh0U2NhbGU7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJlYWQgYSBzZXF1ZW5jZSBwYXJhbWV0ZXIgc2V0IGFuZCByZXR1cm4gc29tZSBpbnRlcmVzdGluZyB2aWRlb1xuICAgKiBwcm9wZXJ0aWVzLiBBIHNlcXVlbmNlIHBhcmFtZXRlciBzZXQgaXMgdGhlIEgyNjQgbWV0YWRhdGEgdGhhdFxuICAgKiBkZXNjcmliZXMgdGhlIHByb3BlcnRpZXMgb2YgdXBjb21pbmcgdmlkZW8gZnJhbWVzLlxuICAgKiBAcmV0dXJucyBhbiBvYmplY3Qgd2l0aCBjb25maWd1cmF0aW9uIHBhcnNlZCBmcm9tIHRoZVxuICAgKiBzZXF1ZW5jZSBwYXJhbWV0ZXIgc2V0LCBpbmNsdWRpbmcgdGhlIGRpbWVuc2lvbnMgb2YgdGhlXG4gICAqIGFzc29jaWF0ZWQgdmlkZW8gZnJhbWVzLlxuICAgKi9cbiAgcmVhZFNQUygpIHtcbiAgICBsZXQgZnJhbWVDcm9wTGVmdE9mZnNldCA9IDA7XG4gICAgbGV0IGZyYW1lQ3JvcFJpZ2h0T2Zmc2V0ID0gMDtcbiAgICBsZXQgZnJhbWVDcm9wVG9wT2Zmc2V0ID0gMDtcbiAgICBsZXQgZnJhbWVDcm9wQm90dG9tT2Zmc2V0ID0gMDtcbiAgICBsZXQgbnVtUmVmRnJhbWVzSW5QaWNPcmRlckNudEN5Y2xlO1xuICAgIGxldCBzY2FsaW5nTGlzdENvdW50O1xuICAgIGxldCBpO1xuICAgIGNvbnN0IHJlYWRVQnl0ZSA9IHRoaXMucmVhZFVCeXRlLmJpbmQodGhpcyk7XG4gICAgY29uc3QgcmVhZEJpdHMgPSB0aGlzLnJlYWRCaXRzLmJpbmQodGhpcyk7XG4gICAgY29uc3QgcmVhZFVFRyA9IHRoaXMucmVhZFVFRy5iaW5kKHRoaXMpO1xuICAgIGNvbnN0IHJlYWRCb29sZWFuID0gdGhpcy5yZWFkQm9vbGVhbi5iaW5kKHRoaXMpO1xuICAgIGNvbnN0IHNraXBCaXRzID0gdGhpcy5za2lwQml0cy5iaW5kKHRoaXMpO1xuICAgIGNvbnN0IHNraXBFRyA9IHRoaXMuc2tpcEVHLmJpbmQodGhpcyk7XG4gICAgY29uc3Qgc2tpcFVFRyA9IHRoaXMuc2tpcFVFRy5iaW5kKHRoaXMpO1xuICAgIGNvbnN0IHNraXBTY2FsaW5nTGlzdCA9IHRoaXMuc2tpcFNjYWxpbmdMaXN0LmJpbmQodGhpcyk7XG4gICAgcmVhZFVCeXRlKCk7XG4gICAgY29uc3QgcHJvZmlsZUlkYyA9IHJlYWRVQnl0ZSgpOyAvLyBwcm9maWxlX2lkY1xuICAgIHJlYWRCaXRzKDUpOyAvLyBwcm9maWxlQ29tcGF0IGNvbnN0cmFpbnRfc2V0WzAtNF1fZmxhZywgdSg1KVxuICAgIHNraXBCaXRzKDMpOyAvLyByZXNlcnZlZF96ZXJvXzNiaXRzIHUoMyksXG4gICAgcmVhZFVCeXRlKCk7IC8vIGxldmVsX2lkYyB1KDgpXG4gICAgc2tpcFVFRygpOyAvLyBzZXFfcGFyYW1ldGVyX3NldF9pZFxuICAgIC8vIHNvbWUgcHJvZmlsZXMgaGF2ZSBtb3JlIG9wdGlvbmFsIGRhdGEgd2UgZG9uJ3QgbmVlZFxuICAgIGlmIChwcm9maWxlSWRjID09PSAxMDAgfHwgcHJvZmlsZUlkYyA9PT0gMTEwIHx8IHByb2ZpbGVJZGMgPT09IDEyMiB8fCBwcm9maWxlSWRjID09PSAyNDQgfHwgcHJvZmlsZUlkYyA9PT0gNDQgfHwgcHJvZmlsZUlkYyA9PT0gODMgfHwgcHJvZmlsZUlkYyA9PT0gODYgfHwgcHJvZmlsZUlkYyA9PT0gMTE4IHx8IHByb2ZpbGVJZGMgPT09IDEyOCkge1xuICAgICAgY29uc3QgY2hyb21hRm9ybWF0SWRjID0gcmVhZFVFRygpO1xuICAgICAgaWYgKGNocm9tYUZvcm1hdElkYyA9PT0gMykge1xuICAgICAgICBza2lwQml0cygxKTtcbiAgICAgIH0gLy8gc2VwYXJhdGVfY29sb3VyX3BsYW5lX2ZsYWdcblxuICAgICAgc2tpcFVFRygpOyAvLyBiaXRfZGVwdGhfbHVtYV9taW51czhcbiAgICAgIHNraXBVRUcoKTsgLy8gYml0X2RlcHRoX2Nocm9tYV9taW51czhcbiAgICAgIHNraXBCaXRzKDEpOyAvLyBxcHByaW1lX3lfemVyb190cmFuc2Zvcm1fYnlwYXNzX2ZsYWdcbiAgICAgIGlmIChyZWFkQm9vbGVhbigpKSB7XG4gICAgICAgIC8vIHNlcV9zY2FsaW5nX21hdHJpeF9wcmVzZW50X2ZsYWdcbiAgICAgICAgc2NhbGluZ0xpc3RDb3VudCA9IGNocm9tYUZvcm1hdElkYyAhPT0gMyA/IDggOiAxMjtcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IHNjYWxpbmdMaXN0Q291bnQ7IGkrKykge1xuICAgICAgICAgIGlmIChyZWFkQm9vbGVhbigpKSB7XG4gICAgICAgICAgICAvLyBzZXFfc2NhbGluZ19saXN0X3ByZXNlbnRfZmxhZ1sgaSBdXG4gICAgICAgICAgICBpZiAoaSA8IDYpIHtcbiAgICAgICAgICAgICAgc2tpcFNjYWxpbmdMaXN0KDE2KTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIHNraXBTY2FsaW5nTGlzdCg2NCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHNraXBVRUcoKTsgLy8gbG9nMl9tYXhfZnJhbWVfbnVtX21pbnVzNFxuICAgIGNvbnN0IHBpY09yZGVyQ250VHlwZSA9IHJlYWRVRUcoKTtcbiAgICBpZiAocGljT3JkZXJDbnRUeXBlID09PSAwKSB7XG4gICAgICByZWFkVUVHKCk7IC8vIGxvZzJfbWF4X3BpY19vcmRlcl9jbnRfbHNiX21pbnVzNFxuICAgIH0gZWxzZSBpZiAocGljT3JkZXJDbnRUeXBlID09PSAxKSB7XG4gICAgICBza2lwQml0cygxKTsgLy8gZGVsdGFfcGljX29yZGVyX2Fsd2F5c196ZXJvX2ZsYWdcbiAgICAgIHNraXBFRygpOyAvLyBvZmZzZXRfZm9yX25vbl9yZWZfcGljXG4gICAgICBza2lwRUcoKTsgLy8gb2Zmc2V0X2Zvcl90b3BfdG9fYm90dG9tX2ZpZWxkXG4gICAgICBudW1SZWZGcmFtZXNJblBpY09yZGVyQ250Q3ljbGUgPSByZWFkVUVHKCk7XG4gICAgICBmb3IgKGkgPSAwOyBpIDwgbnVtUmVmRnJhbWVzSW5QaWNPcmRlckNudEN5Y2xlOyBpKyspIHtcbiAgICAgICAgc2tpcEVHKCk7XG4gICAgICB9IC8vIG9mZnNldF9mb3JfcmVmX2ZyYW1lWyBpIF1cbiAgICB9XG4gICAgc2tpcFVFRygpOyAvLyBtYXhfbnVtX3JlZl9mcmFtZXNcbiAgICBza2lwQml0cygxKTsgLy8gZ2Fwc19pbl9mcmFtZV9udW1fdmFsdWVfYWxsb3dlZF9mbGFnXG4gICAgY29uc3QgcGljV2lkdGhJbk1ic01pbnVzMSA9IHJlYWRVRUcoKTtcbiAgICBjb25zdCBwaWNIZWlnaHRJbk1hcFVuaXRzTWludXMxID0gcmVhZFVFRygpO1xuICAgIGNvbnN0IGZyYW1lTWJzT25seUZsYWcgPSByZWFkQml0cygxKTtcbiAgICBpZiAoZnJhbWVNYnNPbmx5RmxhZyA9PT0gMCkge1xuICAgICAgc2tpcEJpdHMoMSk7XG4gICAgfSAvLyBtYl9hZGFwdGl2ZV9mcmFtZV9maWVsZF9mbGFnXG5cbiAgICBza2lwQml0cygxKTsgLy8gZGlyZWN0Xzh4OF9pbmZlcmVuY2VfZmxhZ1xuICAgIGlmIChyZWFkQm9vbGVhbigpKSB7XG4gICAgICAvLyBmcmFtZV9jcm9wcGluZ19mbGFnXG4gICAgICBmcmFtZUNyb3BMZWZ0T2Zmc2V0ID0gcmVhZFVFRygpO1xuICAgICAgZnJhbWVDcm9wUmlnaHRPZmZzZXQgPSByZWFkVUVHKCk7XG4gICAgICBmcmFtZUNyb3BUb3BPZmZzZXQgPSByZWFkVUVHKCk7XG4gICAgICBmcmFtZUNyb3BCb3R0b21PZmZzZXQgPSByZWFkVUVHKCk7XG4gICAgfVxuICAgIGxldCBwaXhlbFJhdGlvID0gWzEsIDFdO1xuICAgIGlmIChyZWFkQm9vbGVhbigpKSB7XG4gICAgICAvLyB2dWlfcGFyYW1ldGVyc19wcmVzZW50X2ZsYWdcbiAgICAgIGlmIChyZWFkQm9vbGVhbigpKSB7XG4gICAgICAgIC8vIGFzcGVjdF9yYXRpb19pbmZvX3ByZXNlbnRfZmxhZ1xuICAgICAgICBjb25zdCBhc3BlY3RSYXRpb0lkYyA9IHJlYWRVQnl0ZSgpO1xuICAgICAgICBzd2l0Y2ggKGFzcGVjdFJhdGlvSWRjKSB7XG4gICAgICAgICAgY2FzZSAxOlxuICAgICAgICAgICAgcGl4ZWxSYXRpbyA9IFsxLCAxXTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgMjpcbiAgICAgICAgICAgIHBpeGVsUmF0aW8gPSBbMTIsIDExXTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgMzpcbiAgICAgICAgICAgIHBpeGVsUmF0aW8gPSBbMTAsIDExXTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgNDpcbiAgICAgICAgICAgIHBpeGVsUmF0aW8gPSBbMTYsIDExXTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgNTpcbiAgICAgICAgICAgIHBpeGVsUmF0aW8gPSBbNDAsIDMzXTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgNjpcbiAgICAgICAgICAgIHBpeGVsUmF0aW8gPSBbMjQsIDExXTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgNzpcbiAgICAgICAgICAgIHBpeGVsUmF0aW8gPSBbMjAsIDExXTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgODpcbiAgICAgICAgICAgIHBpeGVsUmF0aW8gPSBbMzIsIDExXTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgOTpcbiAgICAgICAgICAgIHBpeGVsUmF0aW8gPSBbODAsIDMzXTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgMTA6XG4gICAgICAgICAgICBwaXhlbFJhdGlvID0gWzE4LCAxMV07XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlIDExOlxuICAgICAgICAgICAgcGl4ZWxSYXRpbyA9IFsxNSwgMTFdO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSAxMjpcbiAgICAgICAgICAgIHBpeGVsUmF0aW8gPSBbNjQsIDMzXTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgMTM6XG4gICAgICAgICAgICBwaXhlbFJhdGlvID0gWzE2MCwgOTldO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSAxNDpcbiAgICAgICAgICAgIHBpeGVsUmF0aW8gPSBbNCwgM107XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlIDE1OlxuICAgICAgICAgICAgcGl4ZWxSYXRpbyA9IFszLCAyXTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgMTY6XG4gICAgICAgICAgICBwaXhlbFJhdGlvID0gWzIsIDFdO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSAyNTU6XG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIHBpeGVsUmF0aW8gPSBbcmVhZFVCeXRlKCkgPDwgOCB8IHJlYWRVQnl0ZSgpLCByZWFkVUJ5dGUoKSA8PCA4IHwgcmVhZFVCeXRlKCldO1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4ge1xuICAgICAgd2lkdGg6IE1hdGguY2VpbCgocGljV2lkdGhJbk1ic01pbnVzMSArIDEpICogMTYgLSBmcmFtZUNyb3BMZWZ0T2Zmc2V0ICogMiAtIGZyYW1lQ3JvcFJpZ2h0T2Zmc2V0ICogMiksXG4gICAgICBoZWlnaHQ6ICgyIC0gZnJhbWVNYnNPbmx5RmxhZykgKiAocGljSGVpZ2h0SW5NYXBVbml0c01pbnVzMSArIDEpICogMTYgLSAoZnJhbWVNYnNPbmx5RmxhZyA/IDIgOiA0KSAqIChmcmFtZUNyb3BUb3BPZmZzZXQgKyBmcmFtZUNyb3BCb3R0b21PZmZzZXQpLFxuICAgICAgcGl4ZWxSYXRpbzogcGl4ZWxSYXRpb1xuICAgIH07XG4gIH1cbiAgcmVhZFNsaWNlVHlwZSgpIHtcbiAgICAvLyBza2lwIE5BTHUgdHlwZVxuICAgIHRoaXMucmVhZFVCeXRlKCk7XG4gICAgLy8gZGlzY2FyZCBmaXJzdF9tYl9pbl9zbGljZVxuICAgIHRoaXMucmVhZFVFRygpO1xuICAgIC8vIHJldHVybiBzbGljZV90eXBlXG4gICAgcmV0dXJuIHRoaXMucmVhZFVFRygpO1xuICB9XG59XG5cbmNsYXNzIEF2Y1ZpZGVvUGFyc2VyIGV4dGVuZHMgQmFzZVZpZGVvUGFyc2VyIHtcbiAgcGFyc2VBVkNQRVModHJhY2ssIHRleHRUcmFjaywgcGVzLCBsYXN0LCBkdXJhdGlvbikge1xuICAgIGNvbnN0IHVuaXRzID0gdGhpcy5wYXJzZUFWQ05BTHUodHJhY2ssIHBlcy5kYXRhKTtcbiAgICBsZXQgVmlkZW9TYW1wbGUgPSB0aGlzLlZpZGVvU2FtcGxlO1xuICAgIGxldCBwdXNoO1xuICAgIGxldCBzcHNmb3VuZCA9IGZhbHNlO1xuICAgIC8vIGZyZWUgcGVzLmRhdGEgdG8gc2F2ZSB1cCBzb21lIG1lbW9yeVxuICAgIHBlcy5kYXRhID0gbnVsbDtcblxuICAgIC8vIGlmIG5ldyBOQUwgdW5pdHMgZm91bmQgYW5kIGxhc3Qgc2FtcGxlIHN0aWxsIHRoZXJlLCBsZXQncyBwdXNoIC4uLlxuICAgIC8vIHRoaXMgaGVscHMgcGFyc2luZyBzdHJlYW1zIHdpdGggbWlzc2luZyBBVUQgKG9ubHkgZG8gdGhpcyBpZiBBVUQgbmV2ZXIgZm91bmQpXG4gICAgaWYgKFZpZGVvU2FtcGxlICYmIHVuaXRzLmxlbmd0aCAmJiAhdHJhY2suYXVkRm91bmQpIHtcbiAgICAgIHRoaXMucHVzaEFjY2Vzc1VuaXQoVmlkZW9TYW1wbGUsIHRyYWNrKTtcbiAgICAgIFZpZGVvU2FtcGxlID0gdGhpcy5WaWRlb1NhbXBsZSA9IHRoaXMuY3JlYXRlVmlkZW9TYW1wbGUoZmFsc2UsIHBlcy5wdHMsIHBlcy5kdHMsICcnKTtcbiAgICB9XG4gICAgdW5pdHMuZm9yRWFjaCh1bml0ID0+IHtcbiAgICAgIHZhciBfVmlkZW9TYW1wbGUyO1xuICAgICAgc3dpdGNoICh1bml0LnR5cGUpIHtcbiAgICAgICAgLy8gTkRSXG4gICAgICAgIGNhc2UgMTpcbiAgICAgICAgICB7XG4gICAgICAgICAgICBsZXQgaXNrZXkgPSBmYWxzZTtcbiAgICAgICAgICAgIHB1c2ggPSB0cnVlO1xuICAgICAgICAgICAgY29uc3QgZGF0YSA9IHVuaXQuZGF0YTtcbiAgICAgICAgICAgIC8vIG9ubHkgY2hlY2sgc2xpY2UgdHlwZSB0byBkZXRlY3QgS0YgaW4gY2FzZSBTUFMgZm91bmQgaW4gc2FtZSBwYWNrZXQgKGFueSBrZXlmcmFtZSBpcyBwcmVjZWRlZCBieSBTUFMgLi4uKVxuICAgICAgICAgICAgaWYgKHNwc2ZvdW5kICYmIGRhdGEubGVuZ3RoID4gNCkge1xuICAgICAgICAgICAgICAvLyByZXRyaWV2ZSBzbGljZSB0eXBlIGJ5IHBhcnNpbmcgYmVnaW5uaW5nIG9mIE5BTCB1bml0IChmb2xsb3cgSDI2NCBzcGVjLCBzbGljZV9oZWFkZXIgZGVmaW5pdGlvbikgdG8gZGV0ZWN0IGtleWZyYW1lIGVtYmVkZGVkIGluIE5EUlxuICAgICAgICAgICAgICBjb25zdCBzbGljZVR5cGUgPSBuZXcgRXhwR29sb21iKGRhdGEpLnJlYWRTbGljZVR5cGUoKTtcbiAgICAgICAgICAgICAgLy8gMiA6IEkgc2xpY2UsIDQgOiBTSSBzbGljZSwgNyA6IEkgc2xpY2UsIDk6IFNJIHNsaWNlXG4gICAgICAgICAgICAgIC8vIFNJIHNsaWNlIDogQSBzbGljZSB0aGF0IGlzIGNvZGVkIHVzaW5nIGludHJhIHByZWRpY3Rpb24gb25seSBhbmQgdXNpbmcgcXVhbnRpc2F0aW9uIG9mIHRoZSBwcmVkaWN0aW9uIHNhbXBsZXMuXG4gICAgICAgICAgICAgIC8vIEFuIFNJIHNsaWNlIGNhbiBiZSBjb2RlZCBzdWNoIHRoYXQgaXRzIGRlY29kZWQgc2FtcGxlcyBjYW4gYmUgY29uc3RydWN0ZWQgaWRlbnRpY2FsbHkgdG8gYW4gU1Agc2xpY2UuXG4gICAgICAgICAgICAgIC8vIEkgc2xpY2U6IEEgc2xpY2UgdGhhdCBpcyBub3QgYW4gU0kgc2xpY2UgdGhhdCBpcyBkZWNvZGVkIHVzaW5nIGludHJhIHByZWRpY3Rpb24gb25seS5cbiAgICAgICAgICAgICAgLy8gaWYgKHNsaWNlVHlwZSA9PT0gMiB8fCBzbGljZVR5cGUgPT09IDcpIHtcbiAgICAgICAgICAgICAgaWYgKHNsaWNlVHlwZSA9PT0gMiB8fCBzbGljZVR5cGUgPT09IDQgfHwgc2xpY2VUeXBlID09PSA3IHx8IHNsaWNlVHlwZSA9PT0gOSkge1xuICAgICAgICAgICAgICAgIGlza2V5ID0gdHJ1ZTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKGlza2V5KSB7XG4gICAgICAgICAgICAgIHZhciBfVmlkZW9TYW1wbGU7XG4gICAgICAgICAgICAgIC8vIGlmIHdlIGhhdmUgbm9uLWtleWZyYW1lIGRhdGEgYWxyZWFkeSwgdGhhdCBjYW5ub3QgYmVsb25nIHRvIHRoZSBzYW1lIGZyYW1lIGFzIGEga2V5ZnJhbWUsIHNvIGZvcmNlIGEgcHVzaFxuICAgICAgICAgICAgICBpZiAoKF9WaWRlb1NhbXBsZSA9IFZpZGVvU2FtcGxlKSAhPSBudWxsICYmIF9WaWRlb1NhbXBsZS5mcmFtZSAmJiAhVmlkZW9TYW1wbGUua2V5KSB7XG4gICAgICAgICAgICAgICAgdGhpcy5wdXNoQWNjZXNzVW5pdChWaWRlb1NhbXBsZSwgdHJhY2spO1xuICAgICAgICAgICAgICAgIFZpZGVvU2FtcGxlID0gdGhpcy5WaWRlb1NhbXBsZSA9IG51bGw7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmICghVmlkZW9TYW1wbGUpIHtcbiAgICAgICAgICAgICAgVmlkZW9TYW1wbGUgPSB0aGlzLlZpZGVvU2FtcGxlID0gdGhpcy5jcmVhdGVWaWRlb1NhbXBsZSh0cnVlLCBwZXMucHRzLCBwZXMuZHRzLCAnJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBWaWRlb1NhbXBsZS5mcmFtZSA9IHRydWU7XG4gICAgICAgICAgICBWaWRlb1NhbXBsZS5rZXkgPSBpc2tleTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgLy8gSURSXG4gICAgICAgICAgfVxuICAgICAgICBjYXNlIDU6XG4gICAgICAgICAgcHVzaCA9IHRydWU7XG4gICAgICAgICAgLy8gaGFuZGxlIFBFUyBub3Qgc3RhcnRpbmcgd2l0aCBBVURcbiAgICAgICAgICAvLyBpZiB3ZSBoYXZlIGZyYW1lIGRhdGEgYWxyZWFkeSwgdGhhdCBjYW5ub3QgYmVsb25nIHRvIHRoZSBzYW1lIGZyYW1lLCBzbyBmb3JjZSBhIHB1c2hcbiAgICAgICAgICBpZiAoKF9WaWRlb1NhbXBsZTIgPSBWaWRlb1NhbXBsZSkgIT0gbnVsbCAmJiBfVmlkZW9TYW1wbGUyLmZyYW1lICYmICFWaWRlb1NhbXBsZS5rZXkpIHtcbiAgICAgICAgICAgIHRoaXMucHVzaEFjY2Vzc1VuaXQoVmlkZW9TYW1wbGUsIHRyYWNrKTtcbiAgICAgICAgICAgIFZpZGVvU2FtcGxlID0gdGhpcy5WaWRlb1NhbXBsZSA9IG51bGw7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICghVmlkZW9TYW1wbGUpIHtcbiAgICAgICAgICAgIFZpZGVvU2FtcGxlID0gdGhpcy5WaWRlb1NhbXBsZSA9IHRoaXMuY3JlYXRlVmlkZW9TYW1wbGUodHJ1ZSwgcGVzLnB0cywgcGVzLmR0cywgJycpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBWaWRlb1NhbXBsZS5rZXkgPSB0cnVlO1xuICAgICAgICAgIFZpZGVvU2FtcGxlLmZyYW1lID0gdHJ1ZTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgLy8gU0VJXG4gICAgICAgIGNhc2UgNjpcbiAgICAgICAgICB7XG4gICAgICAgICAgICBwdXNoID0gdHJ1ZTtcbiAgICAgICAgICAgIHBhcnNlU0VJTWVzc2FnZUZyb21OQUx1KHVuaXQuZGF0YSwgMSwgcGVzLnB0cywgdGV4dFRyYWNrLnNhbXBsZXMpO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAvLyBTUFNcbiAgICAgICAgICB9XG4gICAgICAgIGNhc2UgNzpcbiAgICAgICAgICB7XG4gICAgICAgICAgICB2YXIgX3RyYWNrJHBpeGVsUmF0aW8sIF90cmFjayRwaXhlbFJhdGlvMjtcbiAgICAgICAgICAgIHB1c2ggPSB0cnVlO1xuICAgICAgICAgICAgc3BzZm91bmQgPSB0cnVlO1xuICAgICAgICAgICAgY29uc3Qgc3BzID0gdW5pdC5kYXRhO1xuICAgICAgICAgICAgY29uc3QgZXhwR29sb21iRGVjb2RlciA9IG5ldyBFeHBHb2xvbWIoc3BzKTtcbiAgICAgICAgICAgIGNvbnN0IGNvbmZpZyA9IGV4cEdvbG9tYkRlY29kZXIucmVhZFNQUygpO1xuICAgICAgICAgICAgaWYgKCF0cmFjay5zcHMgfHwgdHJhY2sud2lkdGggIT09IGNvbmZpZy53aWR0aCB8fCB0cmFjay5oZWlnaHQgIT09IGNvbmZpZy5oZWlnaHQgfHwgKChfdHJhY2skcGl4ZWxSYXRpbyA9IHRyYWNrLnBpeGVsUmF0aW8pID09IG51bGwgPyB2b2lkIDAgOiBfdHJhY2skcGl4ZWxSYXRpb1swXSkgIT09IGNvbmZpZy5waXhlbFJhdGlvWzBdIHx8ICgoX3RyYWNrJHBpeGVsUmF0aW8yID0gdHJhY2sucGl4ZWxSYXRpbykgPT0gbnVsbCA/IHZvaWQgMCA6IF90cmFjayRwaXhlbFJhdGlvMlsxXSkgIT09IGNvbmZpZy5waXhlbFJhdGlvWzFdKSB7XG4gICAgICAgICAgICAgIHRyYWNrLndpZHRoID0gY29uZmlnLndpZHRoO1xuICAgICAgICAgICAgICB0cmFjay5oZWlnaHQgPSBjb25maWcuaGVpZ2h0O1xuICAgICAgICAgICAgICB0cmFjay5waXhlbFJhdGlvID0gY29uZmlnLnBpeGVsUmF0aW87XG4gICAgICAgICAgICAgIHRyYWNrLnNwcyA9IFtzcHNdO1xuICAgICAgICAgICAgICB0cmFjay5kdXJhdGlvbiA9IGR1cmF0aW9uO1xuICAgICAgICAgICAgICBjb25zdCBjb2RlY2FycmF5ID0gc3BzLnN1YmFycmF5KDEsIDQpO1xuICAgICAgICAgICAgICBsZXQgY29kZWNzdHJpbmcgPSAnYXZjMS4nO1xuICAgICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IDM7IGkrKykge1xuICAgICAgICAgICAgICAgIGxldCBoID0gY29kZWNhcnJheVtpXS50b1N0cmluZygxNik7XG4gICAgICAgICAgICAgICAgaWYgKGgubGVuZ3RoIDwgMikge1xuICAgICAgICAgICAgICAgICAgaCA9ICcwJyArIGg7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGNvZGVjc3RyaW5nICs9IGg7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgdHJhY2suY29kZWMgPSBjb2RlY3N0cmluZztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cbiAgICAgICAgLy8gUFBTXG4gICAgICAgIGNhc2UgODpcbiAgICAgICAgICBwdXNoID0gdHJ1ZTtcbiAgICAgICAgICB0cmFjay5wcHMgPSBbdW5pdC5kYXRhXTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgLy8gQVVEXG4gICAgICAgIGNhc2UgOTpcbiAgICAgICAgICBwdXNoID0gdHJ1ZTtcbiAgICAgICAgICB0cmFjay5hdWRGb3VuZCA9IHRydWU7XG4gICAgICAgICAgaWYgKFZpZGVvU2FtcGxlKSB7XG4gICAgICAgICAgICB0aGlzLnB1c2hBY2Nlc3NVbml0KFZpZGVvU2FtcGxlLCB0cmFjayk7XG4gICAgICAgICAgfVxuICAgICAgICAgIFZpZGVvU2FtcGxlID0gdGhpcy5WaWRlb1NhbXBsZSA9IHRoaXMuY3JlYXRlVmlkZW9TYW1wbGUoZmFsc2UsIHBlcy5wdHMsIHBlcy5kdHMsICcnKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgLy8gRmlsbGVyIERhdGFcbiAgICAgICAgY2FzZSAxMjpcbiAgICAgICAgICBwdXNoID0gdHJ1ZTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICBwdXNoID0gZmFsc2U7XG4gICAgICAgICAgaWYgKFZpZGVvU2FtcGxlKSB7XG4gICAgICAgICAgICBWaWRlb1NhbXBsZS5kZWJ1ZyArPSAndW5rbm93biBOQUwgJyArIHVuaXQudHlwZSArICcgJztcbiAgICAgICAgICB9XG4gICAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBpZiAoVmlkZW9TYW1wbGUgJiYgcHVzaCkge1xuICAgICAgICBjb25zdCB1bml0cyA9IFZpZGVvU2FtcGxlLnVuaXRzO1xuICAgICAgICB1bml0cy5wdXNoKHVuaXQpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIC8vIGlmIGxhc3QgUEVTIHBhY2tldCwgcHVzaCBzYW1wbGVzXG4gICAgaWYgKGxhc3QgJiYgVmlkZW9TYW1wbGUpIHtcbiAgICAgIHRoaXMucHVzaEFjY2Vzc1VuaXQoVmlkZW9TYW1wbGUsIHRyYWNrKTtcbiAgICAgIHRoaXMuVmlkZW9TYW1wbGUgPSBudWxsO1xuICAgIH1cbiAgfVxuICBwYXJzZUFWQ05BTHUodHJhY2ssIGFycmF5KSB7XG4gICAgY29uc3QgbGVuID0gYXJyYXkuYnl0ZUxlbmd0aDtcbiAgICBsZXQgc3RhdGUgPSB0cmFjay5uYWx1U3RhdGUgfHwgMDtcbiAgICBjb25zdCBsYXN0U3RhdGUgPSBzdGF0ZTtcbiAgICBjb25zdCB1bml0cyA9IFtdO1xuICAgIGxldCBpID0gMDtcbiAgICBsZXQgdmFsdWU7XG4gICAgbGV0IG92ZXJmbG93O1xuICAgIGxldCB1bml0VHlwZTtcbiAgICBsZXQgbGFzdFVuaXRTdGFydCA9IC0xO1xuICAgIGxldCBsYXN0VW5pdFR5cGUgPSAwO1xuICAgIC8vIGxvZ2dlci5sb2coJ1BFUzonICsgSGV4LmhleER1bXAoYXJyYXkpKTtcblxuICAgIGlmIChzdGF0ZSA9PT0gLTEpIHtcbiAgICAgIC8vIHNwZWNpYWwgdXNlIGNhc2Ugd2hlcmUgd2UgZm91bmQgMyBvciA0LWJ5dGUgc3RhcnQgY29kZXMgZXhhY3RseSBhdCB0aGUgZW5kIG9mIHByZXZpb3VzIFBFUyBwYWNrZXRcbiAgICAgIGxhc3RVbml0U3RhcnQgPSAwO1xuICAgICAgLy8gTkFMdSB0eXBlIGlzIHZhbHVlIHJlYWQgZnJvbSBvZmZzZXQgMFxuICAgICAgbGFzdFVuaXRUeXBlID0gYXJyYXlbMF0gJiAweDFmO1xuICAgICAgc3RhdGUgPSAwO1xuICAgICAgaSA9IDE7XG4gICAgfVxuICAgIHdoaWxlIChpIDwgbGVuKSB7XG4gICAgICB2YWx1ZSA9IGFycmF5W2krK107XG4gICAgICAvLyBvcHRpbWl6YXRpb24uIHN0YXRlIDAgYW5kIDEgYXJlIHRoZSBwcmVkb21pbmFudCBjYXNlLiBsZXQncyBoYW5kbGUgdGhlbSBvdXRzaWRlIG9mIHRoZSBzd2l0Y2gvY2FzZVxuICAgICAgaWYgKCFzdGF0ZSkge1xuICAgICAgICBzdGF0ZSA9IHZhbHVlID8gMCA6IDE7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgaWYgKHN0YXRlID09PSAxKSB7XG4gICAgICAgIHN0YXRlID0gdmFsdWUgPyAwIDogMjtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICAvLyBoZXJlIHdlIGhhdmUgc3RhdGUgZWl0aGVyIGVxdWFsIHRvIDIgb3IgM1xuICAgICAgaWYgKCF2YWx1ZSkge1xuICAgICAgICBzdGF0ZSA9IDM7XG4gICAgICB9IGVsc2UgaWYgKHZhbHVlID09PSAxKSB7XG4gICAgICAgIG92ZXJmbG93ID0gaSAtIHN0YXRlIC0gMTtcbiAgICAgICAgaWYgKGxhc3RVbml0U3RhcnQgPj0gMCkge1xuICAgICAgICAgIGNvbnN0IHVuaXQgPSB7XG4gICAgICAgICAgICBkYXRhOiBhcnJheS5zdWJhcnJheShsYXN0VW5pdFN0YXJ0LCBvdmVyZmxvdyksXG4gICAgICAgICAgICB0eXBlOiBsYXN0VW5pdFR5cGVcbiAgICAgICAgICB9O1xuICAgICAgICAgIC8vIGxvZ2dlci5sb2coJ3B1c2hpbmcgTkFMVSwgdHlwZS9zaXplOicgKyB1bml0LnR5cGUgKyAnLycgKyB1bml0LmRhdGEuYnl0ZUxlbmd0aCk7XG4gICAgICAgICAgdW5pdHMucHVzaCh1bml0KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBsYXN0VW5pdFN0YXJ0IGlzIHVuZGVmaW5lZCA9PiB0aGlzIGlzIHRoZSBmaXJzdCBzdGFydCBjb2RlIGZvdW5kIGluIHRoaXMgUEVTIHBhY2tldFxuICAgICAgICAgIC8vIGZpcnN0IGNoZWNrIGlmIHN0YXJ0IGNvZGUgZGVsaW1pdGVyIGlzIG92ZXJsYXBwaW5nIGJldHdlZW4gMiBQRVMgcGFja2V0cyxcbiAgICAgICAgICAvLyBpZSBpdCBzdGFydGVkIGluIGxhc3QgcGFja2V0IChsYXN0U3RhdGUgbm90IHplcm8pXG4gICAgICAgICAgLy8gYW5kIGVuZGVkIGF0IHRoZSBiZWdpbm5pbmcgb2YgdGhpcyBQRVMgcGFja2V0IChpIDw9IDQgLSBsYXN0U3RhdGUpXG4gICAgICAgICAgY29uc3QgbGFzdFVuaXQgPSB0aGlzLmdldExhc3ROYWxVbml0KHRyYWNrLnNhbXBsZXMpO1xuICAgICAgICAgIGlmIChsYXN0VW5pdCkge1xuICAgICAgICAgICAgaWYgKGxhc3RTdGF0ZSAmJiBpIDw9IDQgLSBsYXN0U3RhdGUpIHtcbiAgICAgICAgICAgICAgLy8gc3RhcnQgZGVsaW1pdGVyIG92ZXJsYXBwaW5nIGJldHdlZW4gUEVTIHBhY2tldHNcbiAgICAgICAgICAgICAgLy8gc3RyaXAgc3RhcnQgZGVsaW1pdGVyIGJ5dGVzIGZyb20gdGhlIGVuZCBvZiBsYXN0IE5BTCB1bml0XG4gICAgICAgICAgICAgIC8vIGNoZWNrIGlmIGxhc3RVbml0IGhhZCBhIHN0YXRlIGRpZmZlcmVudCBmcm9tIHplcm9cbiAgICAgICAgICAgICAgaWYgKGxhc3RVbml0LnN0YXRlKSB7XG4gICAgICAgICAgICAgICAgLy8gc3RyaXAgbGFzdCBieXRlc1xuICAgICAgICAgICAgICAgIGxhc3RVbml0LmRhdGEgPSBsYXN0VW5pdC5kYXRhLnN1YmFycmF5KDAsIGxhc3RVbml0LmRhdGEuYnl0ZUxlbmd0aCAtIGxhc3RTdGF0ZSk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIElmIE5BTCB1bml0cyBhcmUgbm90IHN0YXJ0aW5nIHJpZ2h0IGF0IHRoZSBiZWdpbm5pbmcgb2YgdGhlIFBFUyBwYWNrZXQsIHB1c2ggcHJlY2VkaW5nIGRhdGEgaW50byBwcmV2aW91cyBOQUwgdW5pdC5cblxuICAgICAgICAgICAgaWYgKG92ZXJmbG93ID4gMCkge1xuICAgICAgICAgICAgICAvLyBsb2dnZXIubG9nKCdmaXJzdCBOQUxVIGZvdW5kIHdpdGggb3ZlcmZsb3c6JyArIG92ZXJmbG93KTtcbiAgICAgICAgICAgICAgbGFzdFVuaXQuZGF0YSA9IGFwcGVuZFVpbnQ4QXJyYXkobGFzdFVuaXQuZGF0YSwgYXJyYXkuc3ViYXJyYXkoMCwgb3ZlcmZsb3cpKTtcbiAgICAgICAgICAgICAgbGFzdFVuaXQuc3RhdGUgPSAwO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICAvLyBjaGVjayBpZiB3ZSBjYW4gcmVhZCB1bml0IHR5cGVcbiAgICAgICAgaWYgKGkgPCBsZW4pIHtcbiAgICAgICAgICB1bml0VHlwZSA9IGFycmF5W2ldICYgMHgxZjtcbiAgICAgICAgICAvLyBsb2dnZXIubG9nKCdmaW5kIE5BTFUgQCBvZmZzZXQ6JyArIGkgKyAnLHR5cGU6JyArIHVuaXRUeXBlKTtcbiAgICAgICAgICBsYXN0VW5pdFN0YXJ0ID0gaTtcbiAgICAgICAgICBsYXN0VW5pdFR5cGUgPSB1bml0VHlwZTtcbiAgICAgICAgICBzdGF0ZSA9IDA7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gbm90IGVub3VnaCBieXRlIHRvIHJlYWQgdW5pdCB0eXBlLiBsZXQncyByZWFkIGl0IG9uIG5leHQgUEVTIHBhcnNpbmdcbiAgICAgICAgICBzdGF0ZSA9IC0xO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBzdGF0ZSA9IDA7XG4gICAgICB9XG4gICAgfVxuICAgIGlmIChsYXN0VW5pdFN0YXJ0ID49IDAgJiYgc3RhdGUgPj0gMCkge1xuICAgICAgY29uc3QgdW5pdCA9IHtcbiAgICAgICAgZGF0YTogYXJyYXkuc3ViYXJyYXkobGFzdFVuaXRTdGFydCwgbGVuKSxcbiAgICAgICAgdHlwZTogbGFzdFVuaXRUeXBlLFxuICAgICAgICBzdGF0ZTogc3RhdGVcbiAgICAgIH07XG4gICAgICB1bml0cy5wdXNoKHVuaXQpO1xuICAgICAgLy8gbG9nZ2VyLmxvZygncHVzaGluZyBOQUxVLCB0eXBlL3NpemUvc3RhdGU6JyArIHVuaXQudHlwZSArICcvJyArIHVuaXQuZGF0YS5ieXRlTGVuZ3RoICsgJy8nICsgc3RhdGUpO1xuICAgIH1cbiAgICAvLyBubyBOQUx1IGZvdW5kXG4gICAgaWYgKHVuaXRzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgLy8gYXBwZW5kIHBlcy5kYXRhIHRvIHByZXZpb3VzIE5BTCB1bml0XG4gICAgICBjb25zdCBsYXN0VW5pdCA9IHRoaXMuZ2V0TGFzdE5hbFVuaXQodHJhY2suc2FtcGxlcyk7XG4gICAgICBpZiAobGFzdFVuaXQpIHtcbiAgICAgICAgbGFzdFVuaXQuZGF0YSA9IGFwcGVuZFVpbnQ4QXJyYXkobGFzdFVuaXQuZGF0YSwgYXJyYXkpO1xuICAgICAgfVxuICAgIH1cbiAgICB0cmFjay5uYWx1U3RhdGUgPSBzdGF0ZTtcbiAgICByZXR1cm4gdW5pdHM7XG4gIH1cbn1cblxuLyoqXG4gKiBTQU1QTEUtQUVTIGRlY3J5cHRlclxuICovXG5cbmNsYXNzIFNhbXBsZUFlc0RlY3J5cHRlciB7XG4gIGNvbnN0cnVjdG9yKG9ic2VydmVyLCBjb25maWcsIGtleURhdGEpIHtcbiAgICB0aGlzLmtleURhdGEgPSB2b2lkIDA7XG4gICAgdGhpcy5kZWNyeXB0ZXIgPSB2b2lkIDA7XG4gICAgdGhpcy5rZXlEYXRhID0ga2V5RGF0YTtcbiAgICB0aGlzLmRlY3J5cHRlciA9IG5ldyBEZWNyeXB0ZXIoY29uZmlnLCB7XG4gICAgICByZW1vdmVQS0NTN1BhZGRpbmc6IGZhbHNlXG4gICAgfSk7XG4gIH1cbiAgZGVjcnlwdEJ1ZmZlcihlbmNyeXB0ZWREYXRhKSB7XG4gICAgcmV0dXJuIHRoaXMuZGVjcnlwdGVyLmRlY3J5cHQoZW5jcnlwdGVkRGF0YSwgdGhpcy5rZXlEYXRhLmtleS5idWZmZXIsIHRoaXMua2V5RGF0YS5pdi5idWZmZXIpO1xuICB9XG5cbiAgLy8gQUFDIC0gZW5jcnlwdCBhbGwgZnVsbCAxNiBieXRlcyBibG9ja3Mgc3RhcnRpbmcgZnJvbSBvZmZzZXQgMTZcbiAgZGVjcnlwdEFhY1NhbXBsZShzYW1wbGVzLCBzYW1wbGVJbmRleCwgY2FsbGJhY2spIHtcbiAgICBjb25zdCBjdXJVbml0ID0gc2FtcGxlc1tzYW1wbGVJbmRleF0udW5pdDtcbiAgICBpZiAoY3VyVW5pdC5sZW5ndGggPD0gMTYpIHtcbiAgICAgIC8vIE5vIGVuY3J5cHRlZCBwb3J0aW9uIGluIHRoaXMgc2FtcGxlIChmaXJzdCAxNiBieXRlcyBpcyBub3RcbiAgICAgIC8vIGVuY3J5cHRlZCwgc2VlIGh0dHBzOi8vZGV2ZWxvcGVyLmFwcGxlLmNvbS9saWJyYXJ5L2FyY2hpdmUvZG9jdW1lbnRhdGlvbi9BdWRpb1ZpZGVvL0NvbmNlcHR1YWwvSExTX1NhbXBsZV9FbmNyeXB0aW9uL0VuY3J5cHRpb24vRW5jcnlwdGlvbi5odG1sKSxcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgZW5jcnlwdGVkRGF0YSA9IGN1clVuaXQuc3ViYXJyYXkoMTYsIGN1clVuaXQubGVuZ3RoIC0gY3VyVW5pdC5sZW5ndGggJSAxNik7XG4gICAgY29uc3QgZW5jcnlwdGVkQnVmZmVyID0gZW5jcnlwdGVkRGF0YS5idWZmZXIuc2xpY2UoZW5jcnlwdGVkRGF0YS5ieXRlT2Zmc2V0LCBlbmNyeXB0ZWREYXRhLmJ5dGVPZmZzZXQgKyBlbmNyeXB0ZWREYXRhLmxlbmd0aCk7XG4gICAgdGhpcy5kZWNyeXB0QnVmZmVyKGVuY3J5cHRlZEJ1ZmZlcikudGhlbihkZWNyeXB0ZWRCdWZmZXIgPT4ge1xuICAgICAgY29uc3QgZGVjcnlwdGVkRGF0YSA9IG5ldyBVaW50OEFycmF5KGRlY3J5cHRlZEJ1ZmZlcik7XG4gICAgICBjdXJVbml0LnNldChkZWNyeXB0ZWREYXRhLCAxNik7XG4gICAgICBpZiAoIXRoaXMuZGVjcnlwdGVyLmlzU3luYygpKSB7XG4gICAgICAgIHRoaXMuZGVjcnlwdEFhY1NhbXBsZXMoc2FtcGxlcywgc2FtcGxlSW5kZXggKyAxLCBjYWxsYmFjayk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbiAgZGVjcnlwdEFhY1NhbXBsZXMoc2FtcGxlcywgc2FtcGxlSW5kZXgsIGNhbGxiYWNrKSB7XG4gICAgZm9yICg7OyBzYW1wbGVJbmRleCsrKSB7XG4gICAgICBpZiAoc2FtcGxlSW5kZXggPj0gc2FtcGxlcy5sZW5ndGgpIHtcbiAgICAgICAgY2FsbGJhY2soKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgaWYgKHNhbXBsZXNbc2FtcGxlSW5kZXhdLnVuaXQubGVuZ3RoIDwgMzIpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICB0aGlzLmRlY3J5cHRBYWNTYW1wbGUoc2FtcGxlcywgc2FtcGxlSW5kZXgsIGNhbGxiYWNrKTtcbiAgICAgIGlmICghdGhpcy5kZWNyeXB0ZXIuaXNTeW5jKCkpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8vIEFWQyAtIGVuY3J5cHQgb25lIDE2IGJ5dGVzIGJsb2NrIG91dCBvZiB0ZW4sIHN0YXJ0aW5nIGZyb20gb2Zmc2V0IDMyXG4gIGdldEF2Y0VuY3J5cHRlZERhdGEoZGVjb2RlZERhdGEpIHtcbiAgICBjb25zdCBlbmNyeXB0ZWREYXRhTGVuID0gTWF0aC5mbG9vcigoZGVjb2RlZERhdGEubGVuZ3RoIC0gNDgpIC8gMTYwKSAqIDE2ICsgMTY7XG4gICAgY29uc3QgZW5jcnlwdGVkRGF0YSA9IG5ldyBJbnQ4QXJyYXkoZW5jcnlwdGVkRGF0YUxlbik7XG4gICAgbGV0IG91dHB1dFBvcyA9IDA7XG4gICAgZm9yIChsZXQgaW5wdXRQb3MgPSAzMjsgaW5wdXRQb3MgPCBkZWNvZGVkRGF0YS5sZW5ndGggLSAxNjsgaW5wdXRQb3MgKz0gMTYwLCBvdXRwdXRQb3MgKz0gMTYpIHtcbiAgICAgIGVuY3J5cHRlZERhdGEuc2V0KGRlY29kZWREYXRhLnN1YmFycmF5KGlucHV0UG9zLCBpbnB1dFBvcyArIDE2KSwgb3V0cHV0UG9zKTtcbiAgICB9XG4gICAgcmV0dXJuIGVuY3J5cHRlZERhdGE7XG4gIH1cbiAgZ2V0QXZjRGVjcnlwdGVkVW5pdChkZWNvZGVkRGF0YSwgZGVjcnlwdGVkRGF0YSkge1xuICAgIGNvbnN0IHVpbnQ4RGVjcnlwdGVkRGF0YSA9IG5ldyBVaW50OEFycmF5KGRlY3J5cHRlZERhdGEpO1xuICAgIGxldCBpbnB1dFBvcyA9IDA7XG4gICAgZm9yIChsZXQgb3V0cHV0UG9zID0gMzI7IG91dHB1dFBvcyA8IGRlY29kZWREYXRhLmxlbmd0aCAtIDE2OyBvdXRwdXRQb3MgKz0gMTYwLCBpbnB1dFBvcyArPSAxNikge1xuICAgICAgZGVjb2RlZERhdGEuc2V0KHVpbnQ4RGVjcnlwdGVkRGF0YS5zdWJhcnJheShpbnB1dFBvcywgaW5wdXRQb3MgKyAxNiksIG91dHB1dFBvcyk7XG4gICAgfVxuICAgIHJldHVybiBkZWNvZGVkRGF0YTtcbiAgfVxuICBkZWNyeXB0QXZjU2FtcGxlKHNhbXBsZXMsIHNhbXBsZUluZGV4LCB1bml0SW5kZXgsIGNhbGxiYWNrLCBjdXJVbml0KSB7XG4gICAgY29uc3QgZGVjb2RlZERhdGEgPSBkaXNjYXJkRVBCKGN1clVuaXQuZGF0YSk7XG4gICAgY29uc3QgZW5jcnlwdGVkRGF0YSA9IHRoaXMuZ2V0QXZjRW5jcnlwdGVkRGF0YShkZWNvZGVkRGF0YSk7XG4gICAgdGhpcy5kZWNyeXB0QnVmZmVyKGVuY3J5cHRlZERhdGEuYnVmZmVyKS50aGVuKGRlY3J5cHRlZEJ1ZmZlciA9PiB7XG4gICAgICBjdXJVbml0LmRhdGEgPSB0aGlzLmdldEF2Y0RlY3J5cHRlZFVuaXQoZGVjb2RlZERhdGEsIGRlY3J5cHRlZEJ1ZmZlcik7XG4gICAgICBpZiAoIXRoaXMuZGVjcnlwdGVyLmlzU3luYygpKSB7XG4gICAgICAgIHRoaXMuZGVjcnlwdEF2Y1NhbXBsZXMoc2FtcGxlcywgc2FtcGxlSW5kZXgsIHVuaXRJbmRleCArIDEsIGNhbGxiYWNrKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuICBkZWNyeXB0QXZjU2FtcGxlcyhzYW1wbGVzLCBzYW1wbGVJbmRleCwgdW5pdEluZGV4LCBjYWxsYmFjaykge1xuICAgIGlmIChzYW1wbGVzIGluc3RhbmNlb2YgVWludDhBcnJheSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdDYW5ub3QgZGVjcnlwdCBzYW1wbGVzIG9mIHR5cGUgVWludDhBcnJheScpO1xuICAgIH1cbiAgICBmb3IgKDs7IHNhbXBsZUluZGV4KyssIHVuaXRJbmRleCA9IDApIHtcbiAgICAgIGlmIChzYW1wbGVJbmRleCA+PSBzYW1wbGVzLmxlbmd0aCkge1xuICAgICAgICBjYWxsYmFjaygpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjb25zdCBjdXJVbml0cyA9IHNhbXBsZXNbc2FtcGxlSW5kZXhdLnVuaXRzO1xuICAgICAgZm9yICg7OyB1bml0SW5kZXgrKykge1xuICAgICAgICBpZiAodW5pdEluZGV4ID49IGN1clVuaXRzLmxlbmd0aCkge1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGN1clVuaXQgPSBjdXJVbml0c1t1bml0SW5kZXhdO1xuICAgICAgICBpZiAoY3VyVW5pdC5kYXRhLmxlbmd0aCA8PSA0OCB8fCBjdXJVbml0LnR5cGUgIT09IDEgJiYgY3VyVW5pdC50eXBlICE9PSA1KSB7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5kZWNyeXB0QXZjU2FtcGxlKHNhbXBsZXMsIHNhbXBsZUluZGV4LCB1bml0SW5kZXgsIGNhbGxiYWNrLCBjdXJVbml0KTtcbiAgICAgICAgaWYgKCF0aGlzLmRlY3J5cHRlci5pc1N5bmMoKSkge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxufVxuXG5jb25zdCBQQUNLRVRfTEVOR1RIID0gMTg4O1xuY2xhc3MgVFNEZW11eGVyIHtcbiAgY29uc3RydWN0b3Iob2JzZXJ2ZXIsIGNvbmZpZywgdHlwZVN1cHBvcnRlZCkge1xuICAgIHRoaXMub2JzZXJ2ZXIgPSB2b2lkIDA7XG4gICAgdGhpcy5jb25maWcgPSB2b2lkIDA7XG4gICAgdGhpcy50eXBlU3VwcG9ydGVkID0gdm9pZCAwO1xuICAgIHRoaXMuc2FtcGxlQWVzID0gbnVsbDtcbiAgICB0aGlzLnBtdFBhcnNlZCA9IGZhbHNlO1xuICAgIHRoaXMuYXVkaW9Db2RlYyA9IHZvaWQgMDtcbiAgICB0aGlzLnZpZGVvQ29kZWMgPSB2b2lkIDA7XG4gICAgdGhpcy5fZHVyYXRpb24gPSAwO1xuICAgIHRoaXMuX3BtdElkID0gLTE7XG4gICAgdGhpcy5fdmlkZW9UcmFjayA9IHZvaWQgMDtcbiAgICB0aGlzLl9hdWRpb1RyYWNrID0gdm9pZCAwO1xuICAgIHRoaXMuX2lkM1RyYWNrID0gdm9pZCAwO1xuICAgIHRoaXMuX3R4dFRyYWNrID0gdm9pZCAwO1xuICAgIHRoaXMuYWFjT3ZlckZsb3cgPSBudWxsO1xuICAgIHRoaXMucmVtYWluZGVyRGF0YSA9IG51bGw7XG4gICAgdGhpcy52aWRlb1BhcnNlciA9IHZvaWQgMDtcbiAgICB0aGlzLm9ic2VydmVyID0gb2JzZXJ2ZXI7XG4gICAgdGhpcy5jb25maWcgPSBjb25maWc7XG4gICAgdGhpcy50eXBlU3VwcG9ydGVkID0gdHlwZVN1cHBvcnRlZDtcbiAgICB0aGlzLnZpZGVvUGFyc2VyID0gbmV3IEF2Y1ZpZGVvUGFyc2VyKCk7XG4gIH1cbiAgc3RhdGljIHByb2JlKGRhdGEpIHtcbiAgICBjb25zdCBzeW5jT2Zmc2V0ID0gVFNEZW11eGVyLnN5bmNPZmZzZXQoZGF0YSk7XG4gICAgaWYgKHN5bmNPZmZzZXQgPiAwKSB7XG4gICAgICBsb2dnZXIud2FybihgTVBFRzItVFMgZGV0ZWN0ZWQgYnV0IGZpcnN0IHN5bmMgd29yZCBmb3VuZCBAIG9mZnNldCAke3N5bmNPZmZzZXR9YCk7XG4gICAgfVxuICAgIHJldHVybiBzeW5jT2Zmc2V0ICE9PSAtMTtcbiAgfVxuICBzdGF0aWMgc3luY09mZnNldChkYXRhKSB7XG4gICAgY29uc3QgbGVuZ3RoID0gZGF0YS5sZW5ndGg7XG4gICAgbGV0IHNjYW53aW5kb3cgPSBNYXRoLm1pbihQQUNLRVRfTEVOR1RIICogNSwgbGVuZ3RoIC0gUEFDS0VUX0xFTkdUSCkgKyAxO1xuICAgIGxldCBpID0gMDtcbiAgICB3aGlsZSAoaSA8IHNjYW53aW5kb3cpIHtcbiAgICAgIC8vIGEgVFMgaW5pdCBzZWdtZW50IHNob3VsZCBjb250YWluIGF0IGxlYXN0IDIgVFMgcGFja2V0czogUEFUIGFuZCBQTVQsIGVhY2ggc3RhcnRpbmcgd2l0aCAweDQ3XG4gICAgICBsZXQgZm91bmRQYXQgPSBmYWxzZTtcbiAgICAgIGxldCBwYWNrZXRTdGFydCA9IC0xO1xuICAgICAgbGV0IHRzUGFja2V0cyA9IDA7XG4gICAgICBmb3IgKGxldCBqID0gaTsgaiA8IGxlbmd0aDsgaiArPSBQQUNLRVRfTEVOR1RIKSB7XG4gICAgICAgIGlmIChkYXRhW2pdID09PSAweDQ3ICYmIChsZW5ndGggLSBqID09PSBQQUNLRVRfTEVOR1RIIHx8IGRhdGFbaiArIFBBQ0tFVF9MRU5HVEhdID09PSAweDQ3KSkge1xuICAgICAgICAgIHRzUGFja2V0cysrO1xuICAgICAgICAgIGlmIChwYWNrZXRTdGFydCA9PT0gLTEpIHtcbiAgICAgICAgICAgIHBhY2tldFN0YXJ0ID0gajtcbiAgICAgICAgICAgIC8vIEZpcnN0IHN5bmMgd29yZCBmb3VuZCBhdCBvZmZzZXQsIGluY3JlYXNlIHNjYW4gbGVuZ3RoICgjNTI1MSlcbiAgICAgICAgICAgIGlmIChwYWNrZXRTdGFydCAhPT0gMCkge1xuICAgICAgICAgICAgICBzY2Fud2luZG93ID0gTWF0aC5taW4ocGFja2V0U3RhcnQgKyBQQUNLRVRfTEVOR1RIICogOTksIGRhdGEubGVuZ3RoIC0gUEFDS0VUX0xFTkdUSCkgKyAxO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoIWZvdW5kUGF0KSB7XG4gICAgICAgICAgICBmb3VuZFBhdCA9IHBhcnNlUElEKGRhdGEsIGopID09PSAwO1xuICAgICAgICAgIH1cbiAgICAgICAgICAvLyBTeW5jIHdvcmQgZm91bmQgYXQgMCB3aXRoIDMgcGFja2V0cywgb3IgZm91bmQgYXQgb2Zmc2V0IGxlYXN0IDIgcGFja2V0cyB1cCB0byBzY2Fud2luZG93ICgjNTUwMSlcbiAgICAgICAgICBpZiAoZm91bmRQYXQgJiYgdHNQYWNrZXRzID4gMSAmJiAocGFja2V0U3RhcnQgPT09IDAgJiYgdHNQYWNrZXRzID4gMiB8fCBqICsgUEFDS0VUX0xFTkdUSCA+IHNjYW53aW5kb3cpKSB7XG4gICAgICAgICAgICByZXR1cm4gcGFja2V0U3RhcnQ7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKHRzUGFja2V0cykge1xuICAgICAgICAgIC8vIEV4aXQgaWYgc3luYyB3b3JkIGZvdW5kLCBidXQgZG9lcyBub3QgY29udGFpbiBjb250aWd1b3VzIHBhY2tldHNcbiAgICAgICAgICByZXR1cm4gLTE7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGkrKztcbiAgICB9XG4gICAgcmV0dXJuIC0xO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSB0cmFjayBtb2RlbCBpbnRlcm5hbCB0byBkZW11eGVyIHVzZWQgdG8gZHJpdmUgcmVtdXhpbmcgaW5wdXRcbiAgICovXG4gIHN0YXRpYyBjcmVhdGVUcmFjayh0eXBlLCBkdXJhdGlvbikge1xuICAgIHJldHVybiB7XG4gICAgICBjb250YWluZXI6IHR5cGUgPT09ICd2aWRlbycgfHwgdHlwZSA9PT0gJ2F1ZGlvJyA/ICd2aWRlby9tcDJ0JyA6IHVuZGVmaW5lZCxcbiAgICAgIHR5cGUsXG4gICAgICBpZDogUmVtdXhlclRyYWNrSWRDb25maWdbdHlwZV0sXG4gICAgICBwaWQ6IC0xLFxuICAgICAgaW5wdXRUaW1lU2NhbGU6IDkwMDAwLFxuICAgICAgc2VxdWVuY2VOdW1iZXI6IDAsXG4gICAgICBzYW1wbGVzOiBbXSxcbiAgICAgIGRyb3BwZWQ6IDAsXG4gICAgICBkdXJhdGlvbjogdHlwZSA9PT0gJ2F1ZGlvJyA/IGR1cmF0aW9uIDogdW5kZWZpbmVkXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbml0aWFsaXplcyBhIG5ldyBpbml0IHNlZ21lbnQgb24gdGhlIGRlbXV4ZXIvcmVtdXhlciBpbnRlcmZhY2UuIE5lZWRlZCBmb3IgZGlzY29udGludWl0aWVzL3RyYWNrLXN3aXRjaGVzIChvciBhdCBzdHJlYW0gc3RhcnQpXG4gICAqIFJlc2V0cyBhbGwgaW50ZXJuYWwgdHJhY2sgaW5zdGFuY2VzIG9mIHRoZSBkZW11eGVyLlxuICAgKi9cbiAgcmVzZXRJbml0U2VnbWVudChpbml0U2VnbWVudCwgYXVkaW9Db2RlYywgdmlkZW9Db2RlYywgdHJhY2tEdXJhdGlvbikge1xuICAgIHRoaXMucG10UGFyc2VkID0gZmFsc2U7XG4gICAgdGhpcy5fcG10SWQgPSAtMTtcbiAgICB0aGlzLl92aWRlb1RyYWNrID0gVFNEZW11eGVyLmNyZWF0ZVRyYWNrKCd2aWRlbycpO1xuICAgIHRoaXMuX2F1ZGlvVHJhY2sgPSBUU0RlbXV4ZXIuY3JlYXRlVHJhY2soJ2F1ZGlvJywgdHJhY2tEdXJhdGlvbik7XG4gICAgdGhpcy5faWQzVHJhY2sgPSBUU0RlbXV4ZXIuY3JlYXRlVHJhY2soJ2lkMycpO1xuICAgIHRoaXMuX3R4dFRyYWNrID0gVFNEZW11eGVyLmNyZWF0ZVRyYWNrKCd0ZXh0Jyk7XG4gICAgdGhpcy5fYXVkaW9UcmFjay5zZWdtZW50Q29kZWMgPSAnYWFjJztcblxuICAgIC8vIGZsdXNoIGFueSBwYXJ0aWFsIGNvbnRlbnRcbiAgICB0aGlzLmFhY092ZXJGbG93ID0gbnVsbDtcbiAgICB0aGlzLnJlbWFpbmRlckRhdGEgPSBudWxsO1xuICAgIHRoaXMuYXVkaW9Db2RlYyA9IGF1ZGlvQ29kZWM7XG4gICAgdGhpcy52aWRlb0NvZGVjID0gdmlkZW9Db2RlYztcbiAgICB0aGlzLl9kdXJhdGlvbiA9IHRyYWNrRHVyYXRpb247XG4gIH1cbiAgcmVzZXRUaW1lU3RhbXAoKSB7fVxuICByZXNldENvbnRpZ3VpdHkoKSB7XG4gICAgY29uc3Qge1xuICAgICAgX2F1ZGlvVHJhY2ssXG4gICAgICBfdmlkZW9UcmFjayxcbiAgICAgIF9pZDNUcmFja1xuICAgIH0gPSB0aGlzO1xuICAgIGlmIChfYXVkaW9UcmFjaykge1xuICAgICAgX2F1ZGlvVHJhY2sucGVzRGF0YSA9IG51bGw7XG4gICAgfVxuICAgIGlmIChfdmlkZW9UcmFjaykge1xuICAgICAgX3ZpZGVvVHJhY2sucGVzRGF0YSA9IG51bGw7XG4gICAgfVxuICAgIGlmIChfaWQzVHJhY2spIHtcbiAgICAgIF9pZDNUcmFjay5wZXNEYXRhID0gbnVsbDtcbiAgICB9XG4gICAgdGhpcy5hYWNPdmVyRmxvdyA9IG51bGw7XG4gICAgdGhpcy5yZW1haW5kZXJEYXRhID0gbnVsbDtcbiAgfVxuICBkZW11eChkYXRhLCB0aW1lT2Zmc2V0LCBpc1NhbXBsZUFlcyA9IGZhbHNlLCBmbHVzaCA9IGZhbHNlKSB7XG4gICAgaWYgKCFpc1NhbXBsZUFlcykge1xuICAgICAgdGhpcy5zYW1wbGVBZXMgPSBudWxsO1xuICAgIH1cbiAgICBsZXQgcGVzO1xuICAgIGNvbnN0IHZpZGVvVHJhY2sgPSB0aGlzLl92aWRlb1RyYWNrO1xuICAgIGNvbnN0IGF1ZGlvVHJhY2sgPSB0aGlzLl9hdWRpb1RyYWNrO1xuICAgIGNvbnN0IGlkM1RyYWNrID0gdGhpcy5faWQzVHJhY2s7XG4gICAgY29uc3QgdGV4dFRyYWNrID0gdGhpcy5fdHh0VHJhY2s7XG4gICAgbGV0IHZpZGVvUGlkID0gdmlkZW9UcmFjay5waWQ7XG4gICAgbGV0IHZpZGVvRGF0YSA9IHZpZGVvVHJhY2sucGVzRGF0YTtcbiAgICBsZXQgYXVkaW9QaWQgPSBhdWRpb1RyYWNrLnBpZDtcbiAgICBsZXQgaWQzUGlkID0gaWQzVHJhY2sucGlkO1xuICAgIGxldCBhdWRpb0RhdGEgPSBhdWRpb1RyYWNrLnBlc0RhdGE7XG4gICAgbGV0IGlkM0RhdGEgPSBpZDNUcmFjay5wZXNEYXRhO1xuICAgIGxldCB1bmtub3duUElEID0gbnVsbDtcbiAgICBsZXQgcG10UGFyc2VkID0gdGhpcy5wbXRQYXJzZWQ7XG4gICAgbGV0IHBtdElkID0gdGhpcy5fcG10SWQ7XG4gICAgbGV0IGxlbiA9IGRhdGEubGVuZ3RoO1xuICAgIGlmICh0aGlzLnJlbWFpbmRlckRhdGEpIHtcbiAgICAgIGRhdGEgPSBhcHBlbmRVaW50OEFycmF5KHRoaXMucmVtYWluZGVyRGF0YSwgZGF0YSk7XG4gICAgICBsZW4gPSBkYXRhLmxlbmd0aDtcbiAgICAgIHRoaXMucmVtYWluZGVyRGF0YSA9IG51bGw7XG4gICAgfVxuICAgIGlmIChsZW4gPCBQQUNLRVRfTEVOR1RIICYmICFmbHVzaCkge1xuICAgICAgdGhpcy5yZW1haW5kZXJEYXRhID0gZGF0YTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGF1ZGlvVHJhY2ssXG4gICAgICAgIHZpZGVvVHJhY2ssXG4gICAgICAgIGlkM1RyYWNrLFxuICAgICAgICB0ZXh0VHJhY2tcbiAgICAgIH07XG4gICAgfVxuICAgIGNvbnN0IHN5bmNPZmZzZXQgPSBNYXRoLm1heCgwLCBUU0RlbXV4ZXIuc3luY09mZnNldChkYXRhKSk7XG4gICAgbGVuIC09IChsZW4gLSBzeW5jT2Zmc2V0KSAlIFBBQ0tFVF9MRU5HVEg7XG4gICAgaWYgKGxlbiA8IGRhdGEuYnl0ZUxlbmd0aCAmJiAhZmx1c2gpIHtcbiAgICAgIHRoaXMucmVtYWluZGVyRGF0YSA9IG5ldyBVaW50OEFycmF5KGRhdGEuYnVmZmVyLCBsZW4sIGRhdGEuYnVmZmVyLmJ5dGVMZW5ndGggLSBsZW4pO1xuICAgIH1cblxuICAgIC8vIGxvb3AgdGhyb3VnaCBUUyBwYWNrZXRzXG4gICAgbGV0IHRzUGFja2V0RXJyb3JzID0gMDtcbiAgICBmb3IgKGxldCBzdGFydCA9IHN5bmNPZmZzZXQ7IHN0YXJ0IDwgbGVuOyBzdGFydCArPSBQQUNLRVRfTEVOR1RIKSB7XG4gICAgICBpZiAoZGF0YVtzdGFydF0gPT09IDB4NDcpIHtcbiAgICAgICAgY29uc3Qgc3R0ID0gISEoZGF0YVtzdGFydCArIDFdICYgMHg0MCk7XG4gICAgICAgIGNvbnN0IHBpZCA9IHBhcnNlUElEKGRhdGEsIHN0YXJ0KTtcbiAgICAgICAgY29uc3QgYXRmID0gKGRhdGFbc3RhcnQgKyAzXSAmIDB4MzApID4+IDQ7XG5cbiAgICAgICAgLy8gaWYgYW4gYWRhcHRpb24gZmllbGQgaXMgcHJlc2VudCwgaXRzIGxlbmd0aCBpcyBzcGVjaWZpZWQgYnkgdGhlIGZpZnRoIGJ5dGUgb2YgdGhlIFRTIHBhY2tldCBoZWFkZXIuXG4gICAgICAgIGxldCBvZmZzZXQ7XG4gICAgICAgIGlmIChhdGYgPiAxKSB7XG4gICAgICAgICAgb2Zmc2V0ID0gc3RhcnQgKyA1ICsgZGF0YVtzdGFydCArIDRdO1xuICAgICAgICAgIC8vIGNvbnRpbnVlIGlmIHRoZXJlIGlzIG9ubHkgYWRhcHRhdGlvbiBmaWVsZFxuICAgICAgICAgIGlmIChvZmZzZXQgPT09IHN0YXJ0ICsgUEFDS0VUX0xFTkdUSCkge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIG9mZnNldCA9IHN0YXJ0ICsgNDtcbiAgICAgICAgfVxuICAgICAgICBzd2l0Y2ggKHBpZCkge1xuICAgICAgICAgIGNhc2UgdmlkZW9QaWQ6XG4gICAgICAgICAgICBpZiAoc3R0KSB7XG4gICAgICAgICAgICAgIGlmICh2aWRlb0RhdGEgJiYgKHBlcyA9IHBhcnNlUEVTKHZpZGVvRGF0YSkpKSB7XG4gICAgICAgICAgICAgICAgdGhpcy52aWRlb1BhcnNlci5wYXJzZUFWQ1BFUyh2aWRlb1RyYWNrLCB0ZXh0VHJhY2ssIHBlcywgZmFsc2UsIHRoaXMuX2R1cmF0aW9uKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB2aWRlb0RhdGEgPSB7XG4gICAgICAgICAgICAgICAgZGF0YTogW10sXG4gICAgICAgICAgICAgICAgc2l6ZTogMFxuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHZpZGVvRGF0YSkge1xuICAgICAgICAgICAgICB2aWRlb0RhdGEuZGF0YS5wdXNoKGRhdGEuc3ViYXJyYXkob2Zmc2V0LCBzdGFydCArIFBBQ0tFVF9MRU5HVEgpKTtcbiAgICAgICAgICAgICAgdmlkZW9EYXRhLnNpemUgKz0gc3RhcnQgKyBQQUNLRVRfTEVOR1RIIC0gb2Zmc2V0O1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSBhdWRpb1BpZDpcbiAgICAgICAgICAgIGlmIChzdHQpIHtcbiAgICAgICAgICAgICAgaWYgKGF1ZGlvRGF0YSAmJiAocGVzID0gcGFyc2VQRVMoYXVkaW9EYXRhKSkpIHtcbiAgICAgICAgICAgICAgICBzd2l0Y2ggKGF1ZGlvVHJhY2suc2VnbWVudENvZGVjKSB7XG4gICAgICAgICAgICAgICAgICBjYXNlICdhYWMnOlxuICAgICAgICAgICAgICAgICAgICB0aGlzLnBhcnNlQUFDUEVTKGF1ZGlvVHJhY2ssIHBlcyk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgY2FzZSAnbXAzJzpcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5wYXJzZU1QRUdQRVMoYXVkaW9UcmFjaywgcGVzKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICBjYXNlICdhYzMnOlxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgdGhpcy5wYXJzZUFDM1BFUyhhdWRpb1RyYWNrLCBwZXMpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBhdWRpb0RhdGEgPSB7XG4gICAgICAgICAgICAgICAgZGF0YTogW10sXG4gICAgICAgICAgICAgICAgc2l6ZTogMFxuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKGF1ZGlvRGF0YSkge1xuICAgICAgICAgICAgICBhdWRpb0RhdGEuZGF0YS5wdXNoKGRhdGEuc3ViYXJyYXkob2Zmc2V0LCBzdGFydCArIFBBQ0tFVF9MRU5HVEgpKTtcbiAgICAgICAgICAgICAgYXVkaW9EYXRhLnNpemUgKz0gc3RhcnQgKyBQQUNLRVRfTEVOR1RIIC0gb2Zmc2V0O1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSBpZDNQaWQ6XG4gICAgICAgICAgICBpZiAoc3R0KSB7XG4gICAgICAgICAgICAgIGlmIChpZDNEYXRhICYmIChwZXMgPSBwYXJzZVBFUyhpZDNEYXRhKSkpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnBhcnNlSUQzUEVTKGlkM1RyYWNrLCBwZXMpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGlkM0RhdGEgPSB7XG4gICAgICAgICAgICAgICAgZGF0YTogW10sXG4gICAgICAgICAgICAgICAgc2l6ZTogMFxuICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKGlkM0RhdGEpIHtcbiAgICAgICAgICAgICAgaWQzRGF0YS5kYXRhLnB1c2goZGF0YS5zdWJhcnJheShvZmZzZXQsIHN0YXJ0ICsgUEFDS0VUX0xFTkdUSCkpO1xuICAgICAgICAgICAgICBpZDNEYXRhLnNpemUgKz0gc3RhcnQgKyBQQUNLRVRfTEVOR1RIIC0gb2Zmc2V0O1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgY2FzZSAwOlxuICAgICAgICAgICAgaWYgKHN0dCkge1xuICAgICAgICAgICAgICBvZmZzZXQgKz0gZGF0YVtvZmZzZXRdICsgMTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHBtdElkID0gdGhpcy5fcG10SWQgPSBwYXJzZVBBVChkYXRhLCBvZmZzZXQpO1xuICAgICAgICAgICAgLy8gbG9nZ2VyLmxvZygnUE1UIFBJRDonICArIHRoaXMuX3BtdElkKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgcG10SWQ6XG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGlmIChzdHQpIHtcbiAgICAgICAgICAgICAgICBvZmZzZXQgKz0gZGF0YVtvZmZzZXRdICsgMTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBjb25zdCBwYXJzZWRQSURzID0gcGFyc2VQTVQoZGF0YSwgb2Zmc2V0LCB0aGlzLnR5cGVTdXBwb3J0ZWQsIGlzU2FtcGxlQWVzLCB0aGlzLm9ic2VydmVyKTtcblxuICAgICAgICAgICAgICAvLyBvbmx5IHVwZGF0ZSB0cmFjayBpZCBpZiB0cmFjayBQSUQgZm91bmQgd2hpbGUgcGFyc2luZyBQTVRcbiAgICAgICAgICAgICAgLy8gdGhpcyBpcyB0byBhdm9pZCByZXNldHRpbmcgdGhlIFBJRCB0byAtMSBpbiBjYXNlXG4gICAgICAgICAgICAgIC8vIHRyYWNrIFBJRCB0cmFuc2llbnRseSBkaXNhcHBlYXJzIGZyb20gdGhlIHN0cmVhbVxuICAgICAgICAgICAgICAvLyB0aGlzIGNvdWxkIGhhcHBlbiBpbiBjYXNlIG9mIHRyYW5zaWVudCBtaXNzaW5nIGF1ZGlvIHNhbXBsZXMgZm9yIGV4YW1wbGVcbiAgICAgICAgICAgICAgLy8gTk9URSB0aGlzIGlzIG9ubHkgdGhlIFBJRCBvZiB0aGUgdHJhY2sgYXMgZm91bmQgaW4gVFMsXG4gICAgICAgICAgICAgIC8vIGJ1dCB3ZSBhcmUgbm90IHVzaW5nIHRoaXMgZm9yIE1QNCB0cmFjayBJRHMuXG4gICAgICAgICAgICAgIHZpZGVvUGlkID0gcGFyc2VkUElEcy52aWRlb1BpZDtcbiAgICAgICAgICAgICAgaWYgKHZpZGVvUGlkID4gMCkge1xuICAgICAgICAgICAgICAgIHZpZGVvVHJhY2sucGlkID0gdmlkZW9QaWQ7XG4gICAgICAgICAgICAgICAgdmlkZW9UcmFjay5zZWdtZW50Q29kZWMgPSBwYXJzZWRQSURzLnNlZ21lbnRWaWRlb0NvZGVjO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGF1ZGlvUGlkID0gcGFyc2VkUElEcy5hdWRpb1BpZDtcbiAgICAgICAgICAgICAgaWYgKGF1ZGlvUGlkID4gMCkge1xuICAgICAgICAgICAgICAgIGF1ZGlvVHJhY2sucGlkID0gYXVkaW9QaWQ7XG4gICAgICAgICAgICAgICAgYXVkaW9UcmFjay5zZWdtZW50Q29kZWMgPSBwYXJzZWRQSURzLnNlZ21lbnRBdWRpb0NvZGVjO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGlkM1BpZCA9IHBhcnNlZFBJRHMuaWQzUGlkO1xuICAgICAgICAgICAgICBpZiAoaWQzUGlkID4gMCkge1xuICAgICAgICAgICAgICAgIGlkM1RyYWNrLnBpZCA9IGlkM1BpZDtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBpZiAodW5rbm93blBJRCAhPT0gbnVsbCAmJiAhcG10UGFyc2VkKSB7XG4gICAgICAgICAgICAgICAgbG9nZ2VyLndhcm4oYE1QRUctVFMgUE1UIGZvdW5kIGF0ICR7c3RhcnR9IGFmdGVyIHVua25vd24gUElEICcke3Vua25vd25QSUR9Jy4gQmFja3RyYWNraW5nIHRvIHN5bmMgYnl0ZSBAJHtzeW5jT2Zmc2V0fSB0byBwYXJzZSBhbGwgVFMgcGFja2V0cy5gKTtcbiAgICAgICAgICAgICAgICB1bmtub3duUElEID0gbnVsbDtcbiAgICAgICAgICAgICAgICAvLyB3ZSBzZXQgaXQgdG8gLTE4OCwgdGhlICs9IDE4OCBpbiB0aGUgZm9yIGxvb3Agd2lsbCByZXNldCBzdGFydCB0byAwXG4gICAgICAgICAgICAgICAgc3RhcnQgPSBzeW5jT2Zmc2V0IC0gMTg4O1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHBtdFBhcnNlZCA9IHRoaXMucG10UGFyc2VkID0gdHJ1ZTtcbiAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgY2FzZSAweDExOlxuICAgICAgICAgIGNhc2UgMHgxZmZmOlxuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgIHVua25vd25QSUQgPSBwaWQ7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdHNQYWNrZXRFcnJvcnMrKztcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKHRzUGFja2V0RXJyb3JzID4gMCkge1xuICAgICAgZW1pdFBhcnNpbmdFcnJvcih0aGlzLm9ic2VydmVyLCBuZXcgRXJyb3IoYEZvdW5kICR7dHNQYWNrZXRFcnJvcnN9IFRTIHBhY2tldC9zIHRoYXQgZG8gbm90IHN0YXJ0IHdpdGggMHg0N2ApKTtcbiAgICB9XG4gICAgdmlkZW9UcmFjay5wZXNEYXRhID0gdmlkZW9EYXRhO1xuICAgIGF1ZGlvVHJhY2sucGVzRGF0YSA9IGF1ZGlvRGF0YTtcbiAgICBpZDNUcmFjay5wZXNEYXRhID0gaWQzRGF0YTtcbiAgICBjb25zdCBkZW11eFJlc3VsdCA9IHtcbiAgICAgIGF1ZGlvVHJhY2ssXG4gICAgICB2aWRlb1RyYWNrLFxuICAgICAgaWQzVHJhY2ssXG4gICAgICB0ZXh0VHJhY2tcbiAgICB9O1xuICAgIGlmIChmbHVzaCkge1xuICAgICAgdGhpcy5leHRyYWN0UmVtYWluaW5nU2FtcGxlcyhkZW11eFJlc3VsdCk7XG4gICAgfVxuICAgIHJldHVybiBkZW11eFJlc3VsdDtcbiAgfVxuICBmbHVzaCgpIHtcbiAgICBjb25zdCB7XG4gICAgICByZW1haW5kZXJEYXRhXG4gICAgfSA9IHRoaXM7XG4gICAgdGhpcy5yZW1haW5kZXJEYXRhID0gbnVsbDtcbiAgICBsZXQgcmVzdWx0O1xuICAgIGlmIChyZW1haW5kZXJEYXRhKSB7XG4gICAgICByZXN1bHQgPSB0aGlzLmRlbXV4KHJlbWFpbmRlckRhdGEsIC0xLCBmYWxzZSwgdHJ1ZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJlc3VsdCA9IHtcbiAgICAgICAgdmlkZW9UcmFjazogdGhpcy5fdmlkZW9UcmFjayxcbiAgICAgICAgYXVkaW9UcmFjazogdGhpcy5fYXVkaW9UcmFjayxcbiAgICAgICAgaWQzVHJhY2s6IHRoaXMuX2lkM1RyYWNrLFxuICAgICAgICB0ZXh0VHJhY2s6IHRoaXMuX3R4dFRyYWNrXG4gICAgICB9O1xuICAgIH1cbiAgICB0aGlzLmV4dHJhY3RSZW1haW5pbmdTYW1wbGVzKHJlc3VsdCk7XG4gICAgaWYgKHRoaXMuc2FtcGxlQWVzKSB7XG4gICAgICByZXR1cm4gdGhpcy5kZWNyeXB0KHJlc3VsdCwgdGhpcy5zYW1wbGVBZXMpO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG4gIGV4dHJhY3RSZW1haW5pbmdTYW1wbGVzKGRlbXV4UmVzdWx0KSB7XG4gICAgY29uc3Qge1xuICAgICAgYXVkaW9UcmFjayxcbiAgICAgIHZpZGVvVHJhY2ssXG4gICAgICBpZDNUcmFjayxcbiAgICAgIHRleHRUcmFja1xuICAgIH0gPSBkZW11eFJlc3VsdDtcbiAgICBjb25zdCB2aWRlb0RhdGEgPSB2aWRlb1RyYWNrLnBlc0RhdGE7XG4gICAgY29uc3QgYXVkaW9EYXRhID0gYXVkaW9UcmFjay5wZXNEYXRhO1xuICAgIGNvbnN0IGlkM0RhdGEgPSBpZDNUcmFjay5wZXNEYXRhO1xuICAgIC8vIHRyeSB0byBwYXJzZSBsYXN0IFBFUyBwYWNrZXRzXG4gICAgbGV0IHBlcztcbiAgICBpZiAodmlkZW9EYXRhICYmIChwZXMgPSBwYXJzZVBFUyh2aWRlb0RhdGEpKSkge1xuICAgICAgdGhpcy52aWRlb1BhcnNlci5wYXJzZUFWQ1BFUyh2aWRlb1RyYWNrLCB0ZXh0VHJhY2ssIHBlcywgdHJ1ZSwgdGhpcy5fZHVyYXRpb24pO1xuICAgICAgdmlkZW9UcmFjay5wZXNEYXRhID0gbnVsbDtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gZWl0aGVyIGF2Y0RhdGEgbnVsbCBvciBQRVMgdHJ1bmNhdGVkLCBrZWVwIGl0IGZvciBuZXh0IGZyYWcgcGFyc2luZ1xuICAgICAgdmlkZW9UcmFjay5wZXNEYXRhID0gdmlkZW9EYXRhO1xuICAgIH1cbiAgICBpZiAoYXVkaW9EYXRhICYmIChwZXMgPSBwYXJzZVBFUyhhdWRpb0RhdGEpKSkge1xuICAgICAgc3dpdGNoIChhdWRpb1RyYWNrLnNlZ21lbnRDb2RlYykge1xuICAgICAgICBjYXNlICdhYWMnOlxuICAgICAgICAgIHRoaXMucGFyc2VBQUNQRVMoYXVkaW9UcmFjaywgcGVzKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnbXAzJzpcbiAgICAgICAgICB0aGlzLnBhcnNlTVBFR1BFUyhhdWRpb1RyYWNrLCBwZXMpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdhYzMnOlxuICAgICAgICAgIHtcbiAgICAgICAgICAgIHRoaXMucGFyc2VBQzNQRVMoYXVkaW9UcmFjaywgcGVzKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgICBhdWRpb1RyYWNrLnBlc0RhdGEgPSBudWxsO1xuICAgIH0gZWxzZSB7XG4gICAgICBpZiAoYXVkaW9EYXRhICE9IG51bGwgJiYgYXVkaW9EYXRhLnNpemUpIHtcbiAgICAgICAgbG9nZ2VyLmxvZygnbGFzdCBBQUMgUEVTIHBhY2tldCB0cnVuY2F0ZWQsbWlnaHQgb3ZlcmxhcCBiZXR3ZWVuIGZyYWdtZW50cycpO1xuICAgICAgfVxuXG4gICAgICAvLyBlaXRoZXIgYXVkaW9EYXRhIG51bGwgb3IgUEVTIHRydW5jYXRlZCwga2VlcCBpdCBmb3IgbmV4dCBmcmFnIHBhcnNpbmdcbiAgICAgIGF1ZGlvVHJhY2sucGVzRGF0YSA9IGF1ZGlvRGF0YTtcbiAgICB9XG4gICAgaWYgKGlkM0RhdGEgJiYgKHBlcyA9IHBhcnNlUEVTKGlkM0RhdGEpKSkge1xuICAgICAgdGhpcy5wYXJzZUlEM1BFUyhpZDNUcmFjaywgcGVzKTtcbiAgICAgIGlkM1RyYWNrLnBlc0RhdGEgPSBudWxsO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBlaXRoZXIgaWQzRGF0YSBudWxsIG9yIFBFUyB0cnVuY2F0ZWQsIGtlZXAgaXQgZm9yIG5leHQgZnJhZyBwYXJzaW5nXG4gICAgICBpZDNUcmFjay5wZXNEYXRhID0gaWQzRGF0YTtcbiAgICB9XG4gIH1cbiAgZGVtdXhTYW1wbGVBZXMoZGF0YSwga2V5RGF0YSwgdGltZU9mZnNldCkge1xuICAgIGNvbnN0IGRlbXV4UmVzdWx0ID0gdGhpcy5kZW11eChkYXRhLCB0aW1lT2Zmc2V0LCB0cnVlLCAhdGhpcy5jb25maWcucHJvZ3Jlc3NpdmUpO1xuICAgIGNvbnN0IHNhbXBsZUFlcyA9IHRoaXMuc2FtcGxlQWVzID0gbmV3IFNhbXBsZUFlc0RlY3J5cHRlcih0aGlzLm9ic2VydmVyLCB0aGlzLmNvbmZpZywga2V5RGF0YSk7XG4gICAgcmV0dXJuIHRoaXMuZGVjcnlwdChkZW11eFJlc3VsdCwgc2FtcGxlQWVzKTtcbiAgfVxuICBkZWNyeXB0KGRlbXV4UmVzdWx0LCBzYW1wbGVBZXMpIHtcbiAgICByZXR1cm4gbmV3IFByb21pc2UocmVzb2x2ZSA9PiB7XG4gICAgICBjb25zdCB7XG4gICAgICAgIGF1ZGlvVHJhY2ssXG4gICAgICAgIHZpZGVvVHJhY2tcbiAgICAgIH0gPSBkZW11eFJlc3VsdDtcbiAgICAgIGlmIChhdWRpb1RyYWNrLnNhbXBsZXMgJiYgYXVkaW9UcmFjay5zZWdtZW50Q29kZWMgPT09ICdhYWMnKSB7XG4gICAgICAgIHNhbXBsZUFlcy5kZWNyeXB0QWFjU2FtcGxlcyhhdWRpb1RyYWNrLnNhbXBsZXMsIDAsICgpID0+IHtcbiAgICAgICAgICBpZiAodmlkZW9UcmFjay5zYW1wbGVzKSB7XG4gICAgICAgICAgICBzYW1wbGVBZXMuZGVjcnlwdEF2Y1NhbXBsZXModmlkZW9UcmFjay5zYW1wbGVzLCAwLCAwLCAoKSA9PiB7XG4gICAgICAgICAgICAgIHJlc29sdmUoZGVtdXhSZXN1bHQpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJlc29sdmUoZGVtdXhSZXN1bHQpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2UgaWYgKHZpZGVvVHJhY2suc2FtcGxlcykge1xuICAgICAgICBzYW1wbGVBZXMuZGVjcnlwdEF2Y1NhbXBsZXModmlkZW9UcmFjay5zYW1wbGVzLCAwLCAwLCAoKSA9PiB7XG4gICAgICAgICAgcmVzb2x2ZShkZW11eFJlc3VsdCk7XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG4gIGRlc3Ryb3koKSB7XG4gICAgdGhpcy5fZHVyYXRpb24gPSAwO1xuICB9XG4gIHBhcnNlQUFDUEVTKHRyYWNrLCBwZXMpIHtcbiAgICBsZXQgc3RhcnRPZmZzZXQgPSAwO1xuICAgIGNvbnN0IGFhY092ZXJGbG93ID0gdGhpcy5hYWNPdmVyRmxvdztcbiAgICBsZXQgZGF0YSA9IHBlcy5kYXRhO1xuICAgIGlmIChhYWNPdmVyRmxvdykge1xuICAgICAgdGhpcy5hYWNPdmVyRmxvdyA9IG51bGw7XG4gICAgICBjb25zdCBmcmFtZU1pc3NpbmdCeXRlcyA9IGFhY092ZXJGbG93Lm1pc3Npbmc7XG4gICAgICBjb25zdCBzYW1wbGVMZW5ndGggPSBhYWNPdmVyRmxvdy5zYW1wbGUudW5pdC5ieXRlTGVuZ3RoO1xuICAgICAgLy8gbG9nZ2VyLmxvZyhgQUFDOiBhcHBlbmQgb3ZlcmZsb3dpbmcgJHtzYW1wbGVMZW5ndGh9IGJ5dGVzIHRvIGJlZ2lubmluZyBvZiBuZXcgUEVTYCk7XG4gICAgICBpZiAoZnJhbWVNaXNzaW5nQnl0ZXMgPT09IC0xKSB7XG4gICAgICAgIGRhdGEgPSBhcHBlbmRVaW50OEFycmF5KGFhY092ZXJGbG93LnNhbXBsZS51bml0LCBkYXRhKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IGZyYW1lT3ZlcmZsb3dCeXRlcyA9IHNhbXBsZUxlbmd0aCAtIGZyYW1lTWlzc2luZ0J5dGVzO1xuICAgICAgICBhYWNPdmVyRmxvdy5zYW1wbGUudW5pdC5zZXQoZGF0YS5zdWJhcnJheSgwLCBmcmFtZU1pc3NpbmdCeXRlcyksIGZyYW1lT3ZlcmZsb3dCeXRlcyk7XG4gICAgICAgIHRyYWNrLnNhbXBsZXMucHVzaChhYWNPdmVyRmxvdy5zYW1wbGUpO1xuICAgICAgICBzdGFydE9mZnNldCA9IGFhY092ZXJGbG93Lm1pc3Npbmc7XG4gICAgICB9XG4gICAgfVxuICAgIC8vIGxvb2sgZm9yIEFEVFMgaGVhZGVyICgweEZGRngpXG4gICAgbGV0IG9mZnNldDtcbiAgICBsZXQgbGVuO1xuICAgIGZvciAob2Zmc2V0ID0gc3RhcnRPZmZzZXQsIGxlbiA9IGRhdGEubGVuZ3RoOyBvZmZzZXQgPCBsZW4gLSAxOyBvZmZzZXQrKykge1xuICAgICAgaWYgKGlzSGVhZGVyJDEoZGF0YSwgb2Zmc2V0KSkge1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gICAgLy8gaWYgQURUUyBoZWFkZXIgZG9lcyBub3Qgc3RhcnQgc3RyYWlnaHQgZnJvbSB0aGUgYmVnaW5uaW5nIG9mIHRoZSBQRVMgcGF5bG9hZCwgcmFpc2UgYW4gZXJyb3JcbiAgICBpZiAob2Zmc2V0ICE9PSBzdGFydE9mZnNldCkge1xuICAgICAgbGV0IHJlYXNvbjtcbiAgICAgIGNvbnN0IHJlY292ZXJhYmxlID0gb2Zmc2V0IDwgbGVuIC0gMTtcbiAgICAgIGlmIChyZWNvdmVyYWJsZSkge1xuICAgICAgICByZWFzb24gPSBgQUFDIFBFUyBkaWQgbm90IHN0YXJ0IHdpdGggQURUUyBoZWFkZXIsb2Zmc2V0OiR7b2Zmc2V0fWA7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZWFzb24gPSAnTm8gQURUUyBoZWFkZXIgZm91bmQgaW4gQUFDIFBFUyc7XG4gICAgICB9XG4gICAgICBlbWl0UGFyc2luZ0Vycm9yKHRoaXMub2JzZXJ2ZXIsIG5ldyBFcnJvcihyZWFzb24pLCByZWNvdmVyYWJsZSk7XG4gICAgICBpZiAoIXJlY292ZXJhYmxlKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICB9XG4gICAgaW5pdFRyYWNrQ29uZmlnKHRyYWNrLCB0aGlzLm9ic2VydmVyLCBkYXRhLCBvZmZzZXQsIHRoaXMuYXVkaW9Db2RlYyk7XG4gICAgbGV0IHB0cztcbiAgICBpZiAocGVzLnB0cyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBwdHMgPSBwZXMucHRzO1xuICAgIH0gZWxzZSBpZiAoYWFjT3ZlckZsb3cpIHtcbiAgICAgIC8vIGlmIGxhc3QgQUFDIGZyYW1lIGlzIG92ZXJmbG93aW5nLCB3ZSBzaG91bGQgZW5zdXJlIHRpbWVzdGFtcHMgYXJlIGNvbnRpZ3VvdXM6XG4gICAgICAvLyBmaXJzdCBzYW1wbGUgUFRTIHNob3VsZCBiZSBlcXVhbCB0byBsYXN0IHNhbXBsZSBQVFMgKyBmcmFtZUR1cmF0aW9uXG4gICAgICBjb25zdCBmcmFtZUR1cmF0aW9uID0gZ2V0RnJhbWVEdXJhdGlvbih0cmFjay5zYW1wbGVyYXRlKTtcbiAgICAgIHB0cyA9IGFhY092ZXJGbG93LnNhbXBsZS5wdHMgKyBmcmFtZUR1cmF0aW9uO1xuICAgIH0gZWxzZSB7XG4gICAgICBsb2dnZXIud2FybignW3RzZGVtdXhlcl06IEFBQyBQRVMgdW5rbm93biBQVFMnKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBzY2FuIGZvciBhYWMgc2FtcGxlc1xuICAgIGxldCBmcmFtZUluZGV4ID0gMDtcbiAgICBsZXQgZnJhbWU7XG4gICAgd2hpbGUgKG9mZnNldCA8IGxlbikge1xuICAgICAgZnJhbWUgPSBhcHBlbmRGcmFtZSQyKHRyYWNrLCBkYXRhLCBvZmZzZXQsIHB0cywgZnJhbWVJbmRleCk7XG4gICAgICBvZmZzZXQgKz0gZnJhbWUubGVuZ3RoO1xuICAgICAgaWYgKCFmcmFtZS5taXNzaW5nKSB7XG4gICAgICAgIGZyYW1lSW5kZXgrKztcbiAgICAgICAgZm9yICg7IG9mZnNldCA8IGxlbiAtIDE7IG9mZnNldCsrKSB7XG4gICAgICAgICAgaWYgKGlzSGVhZGVyJDEoZGF0YSwgb2Zmc2V0KSkge1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLmFhY092ZXJGbG93ID0gZnJhbWU7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBwYXJzZU1QRUdQRVModHJhY2ssIHBlcykge1xuICAgIGNvbnN0IGRhdGEgPSBwZXMuZGF0YTtcbiAgICBjb25zdCBsZW5ndGggPSBkYXRhLmxlbmd0aDtcbiAgICBsZXQgZnJhbWVJbmRleCA9IDA7XG4gICAgbGV0IG9mZnNldCA9IDA7XG4gICAgY29uc3QgcHRzID0gcGVzLnB0cztcbiAgICBpZiAocHRzID09PSB1bmRlZmluZWQpIHtcbiAgICAgIGxvZ2dlci53YXJuKCdbdHNkZW11eGVyXTogTVBFRyBQRVMgdW5rbm93biBQVFMnKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgd2hpbGUgKG9mZnNldCA8IGxlbmd0aCkge1xuICAgICAgaWYgKGlzSGVhZGVyKGRhdGEsIG9mZnNldCkpIHtcbiAgICAgICAgY29uc3QgZnJhbWUgPSBhcHBlbmRGcmFtZSQxKHRyYWNrLCBkYXRhLCBvZmZzZXQsIHB0cywgZnJhbWVJbmRleCk7XG4gICAgICAgIGlmIChmcmFtZSkge1xuICAgICAgICAgIG9mZnNldCArPSBmcmFtZS5sZW5ndGg7XG4gICAgICAgICAgZnJhbWVJbmRleCsrO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIGxvZ2dlci5sb2coJ1VuYWJsZSB0byBwYXJzZSBNcGVnIGF1ZGlvIGZyYW1lJyk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIG5vdGhpbmcgZm91bmQsIGtlZXAgbG9va2luZ1xuICAgICAgICBvZmZzZXQrKztcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgcGFyc2VBQzNQRVModHJhY2ssIHBlcykge1xuICAgIHtcbiAgICAgIGNvbnN0IGRhdGEgPSBwZXMuZGF0YTtcbiAgICAgIGNvbnN0IHB0cyA9IHBlcy5wdHM7XG4gICAgICBpZiAocHRzID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oJ1t0c2RlbXV4ZXJdOiBBQzMgUEVTIHVua25vd24gUFRTJyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGxlbmd0aCA9IGRhdGEubGVuZ3RoO1xuICAgICAgbGV0IGZyYW1lSW5kZXggPSAwO1xuICAgICAgbGV0IG9mZnNldCA9IDA7XG4gICAgICBsZXQgcGFyc2VkO1xuICAgICAgd2hpbGUgKG9mZnNldCA8IGxlbmd0aCAmJiAocGFyc2VkID0gYXBwZW5kRnJhbWUodHJhY2ssIGRhdGEsIG9mZnNldCwgcHRzLCBmcmFtZUluZGV4KyspKSA+IDApIHtcbiAgICAgICAgb2Zmc2V0ICs9IHBhcnNlZDtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgcGFyc2VJRDNQRVMoaWQzVHJhY2ssIHBlcykge1xuICAgIGlmIChwZXMucHRzID09PSB1bmRlZmluZWQpIHtcbiAgICAgIGxvZ2dlci53YXJuKCdbdHNkZW11eGVyXTogSUQzIFBFUyB1bmtub3duIFBUUycpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBpZDNTYW1wbGUgPSBfZXh0ZW5kcyh7fSwgcGVzLCB7XG4gICAgICB0eXBlOiB0aGlzLl92aWRlb1RyYWNrID8gTWV0YWRhdGFTY2hlbWEuZW1zZyA6IE1ldGFkYXRhU2NoZW1hLmF1ZGlvSWQzLFxuICAgICAgZHVyYXRpb246IE51bWJlci5QT1NJVElWRV9JTkZJTklUWVxuICAgIH0pO1xuICAgIGlkM1RyYWNrLnNhbXBsZXMucHVzaChpZDNTYW1wbGUpO1xuICB9XG59XG5mdW5jdGlvbiBwYXJzZVBJRChkYXRhLCBvZmZzZXQpIHtcbiAgLy8gcGlkIGlzIGEgMTMtYml0IGZpZWxkIHN0YXJ0aW5nIGF0IHRoZSBsYXN0IGJpdCBvZiBUU1sxXVxuICByZXR1cm4gKChkYXRhW29mZnNldCArIDFdICYgMHgxZikgPDwgOCkgKyBkYXRhW29mZnNldCArIDJdO1xufVxuZnVuY3Rpb24gcGFyc2VQQVQoZGF0YSwgb2Zmc2V0KSB7XG4gIC8vIHNraXAgdGhlIFBTSSBoZWFkZXIgYW5kIHBhcnNlIHRoZSBmaXJzdCBQTVQgZW50cnlcbiAgcmV0dXJuIChkYXRhW29mZnNldCArIDEwXSAmIDB4MWYpIDw8IDggfCBkYXRhW29mZnNldCArIDExXTtcbn1cbmZ1bmN0aW9uIHBhcnNlUE1UKGRhdGEsIG9mZnNldCwgdHlwZVN1cHBvcnRlZCwgaXNTYW1wbGVBZXMsIG9ic2VydmVyKSB7XG4gIGNvbnN0IHJlc3VsdCA9IHtcbiAgICBhdWRpb1BpZDogLTEsXG4gICAgdmlkZW9QaWQ6IC0xLFxuICAgIGlkM1BpZDogLTEsXG4gICAgc2VnbWVudFZpZGVvQ29kZWM6ICdhdmMnLFxuICAgIHNlZ21lbnRBdWRpb0NvZGVjOiAnYWFjJ1xuICB9O1xuICBjb25zdCBzZWN0aW9uTGVuZ3RoID0gKGRhdGFbb2Zmc2V0ICsgMV0gJiAweDBmKSA8PCA4IHwgZGF0YVtvZmZzZXQgKyAyXTtcbiAgY29uc3QgdGFibGVFbmQgPSBvZmZzZXQgKyAzICsgc2VjdGlvbkxlbmd0aCAtIDQ7XG4gIC8vIHRvIGRldGVybWluZSB3aGVyZSB0aGUgdGFibGUgaXMsIHdlIGhhdmUgdG8gZmlndXJlIG91dCBob3dcbiAgLy8gbG9uZyB0aGUgcHJvZ3JhbSBpbmZvIGRlc2NyaXB0b3JzIGFyZVxuICBjb25zdCBwcm9ncmFtSW5mb0xlbmd0aCA9IChkYXRhW29mZnNldCArIDEwXSAmIDB4MGYpIDw8IDggfCBkYXRhW29mZnNldCArIDExXTtcbiAgLy8gYWR2YW5jZSB0aGUgb2Zmc2V0IHRvIHRoZSBmaXJzdCBlbnRyeSBpbiB0aGUgbWFwcGluZyB0YWJsZVxuICBvZmZzZXQgKz0gMTIgKyBwcm9ncmFtSW5mb0xlbmd0aDtcbiAgd2hpbGUgKG9mZnNldCA8IHRhYmxlRW5kKSB7XG4gICAgY29uc3QgcGlkID0gcGFyc2VQSUQoZGF0YSwgb2Zmc2V0KTtcbiAgICBjb25zdCBlc0luZm9MZW5ndGggPSAoZGF0YVtvZmZzZXQgKyAzXSAmIDB4MGYpIDw8IDggfCBkYXRhW29mZnNldCArIDRdO1xuICAgIHN3aXRjaCAoZGF0YVtvZmZzZXRdKSB7XG4gICAgICBjYXNlIDB4Y2Y6XG4gICAgICAgIC8vIFNBTVBMRS1BRVMgQUFDXG4gICAgICAgIGlmICghaXNTYW1wbGVBZXMpIHtcbiAgICAgICAgICBsb2dFbmNyeXB0ZWRTYW1wbGVzRm91bmRJblVuZW5jcnlwdGVkU3RyZWFtKCdBRFRTIEFBQycpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgICBjYXNlIDB4MGY6XG4gICAgICAgIC8vIElTTy9JRUMgMTM4MTgtNyBBRFRTIEFBQyAoTVBFRy0yIGxvd2VyIGJpdC1yYXRlIGF1ZGlvKVxuICAgICAgICAvLyBsb2dnZXIubG9nKCdBQUMgUElEOicgICsgcGlkKTtcbiAgICAgICAgaWYgKHJlc3VsdC5hdWRpb1BpZCA9PT0gLTEpIHtcbiAgICAgICAgICByZXN1bHQuYXVkaW9QaWQgPSBwaWQ7XG4gICAgICAgIH1cbiAgICAgICAgYnJlYWs7XG5cbiAgICAgIC8vIFBhY2tldGl6ZWQgbWV0YWRhdGEgKElEMylcbiAgICAgIGNhc2UgMHgxNTpcbiAgICAgICAgLy8gbG9nZ2VyLmxvZygnSUQzIFBJRDonICArIHBpZCk7XG4gICAgICAgIGlmIChyZXN1bHQuaWQzUGlkID09PSAtMSkge1xuICAgICAgICAgIHJlc3VsdC5pZDNQaWQgPSBwaWQ7XG4gICAgICAgIH1cbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIDB4ZGI6XG4gICAgICAgIC8vIFNBTVBMRS1BRVMgQVZDXG4gICAgICAgIGlmICghaXNTYW1wbGVBZXMpIHtcbiAgICAgICAgICBsb2dFbmNyeXB0ZWRTYW1wbGVzRm91bmRJblVuZW5jcnlwdGVkU3RyZWFtKCdILjI2NCcpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgICBjYXNlIDB4MWI6XG4gICAgICAgIC8vIElUVS1UIFJlYy4gSC4yNjQgYW5kIElTTy9JRUMgMTQ0OTYtMTAgKGxvd2VyIGJpdC1yYXRlIHZpZGVvKVxuICAgICAgICAvLyBsb2dnZXIubG9nKCdBVkMgUElEOicgICsgcGlkKTtcbiAgICAgICAgaWYgKHJlc3VsdC52aWRlb1BpZCA9PT0gLTEpIHtcbiAgICAgICAgICByZXN1bHQudmlkZW9QaWQgPSBwaWQ7XG4gICAgICAgICAgcmVzdWx0LnNlZ21lbnRWaWRlb0NvZGVjID0gJ2F2Yyc7XG4gICAgICAgIH1cbiAgICAgICAgYnJlYWs7XG5cbiAgICAgIC8vIElTTy9JRUMgMTExNzItMyAoTVBFRy0xIGF1ZGlvKVxuICAgICAgLy8gb3IgSVNPL0lFQyAxMzgxOC0zIChNUEVHLTIgaGFsdmVkIHNhbXBsZSByYXRlIGF1ZGlvKVxuICAgICAgY2FzZSAweDAzOlxuICAgICAgY2FzZSAweDA0OlxuICAgICAgICAvLyBsb2dnZXIubG9nKCdNUEVHIFBJRDonICArIHBpZCk7XG4gICAgICAgIGlmICghdHlwZVN1cHBvcnRlZC5tcGVnICYmICF0eXBlU3VwcG9ydGVkLm1wMykge1xuICAgICAgICAgIGxvZ2dlci5sb2coJ01QRUcgYXVkaW8gZm91bmQsIG5vdCBzdXBwb3J0ZWQgaW4gdGhpcyBicm93c2VyJyk7XG4gICAgICAgIH0gZWxzZSBpZiAocmVzdWx0LmF1ZGlvUGlkID09PSAtMSkge1xuICAgICAgICAgIHJlc3VsdC5hdWRpb1BpZCA9IHBpZDtcbiAgICAgICAgICByZXN1bHQuc2VnbWVudEF1ZGlvQ29kZWMgPSAnbXAzJztcbiAgICAgICAgfVxuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgMHhjMTpcbiAgICAgICAgLy8gU0FNUExFLUFFUyBBQzNcbiAgICAgICAgaWYgKCFpc1NhbXBsZUFlcykge1xuICAgICAgICAgIGxvZ0VuY3J5cHRlZFNhbXBsZXNGb3VuZEluVW5lbmNyeXB0ZWRTdHJlYW0oJ0FDLTMnKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgICAgY2FzZSAweDgxOlxuICAgICAgICB7XG4gICAgICAgICAgaWYgKCF0eXBlU3VwcG9ydGVkLmFjMykge1xuICAgICAgICAgICAgbG9nZ2VyLmxvZygnQUMtMyBhdWRpbyBmb3VuZCwgbm90IHN1cHBvcnRlZCBpbiB0aGlzIGJyb3dzZXInKTtcbiAgICAgICAgICB9IGVsc2UgaWYgKHJlc3VsdC5hdWRpb1BpZCA9PT0gLTEpIHtcbiAgICAgICAgICAgIHJlc3VsdC5hdWRpb1BpZCA9IHBpZDtcbiAgICAgICAgICAgIHJlc3VsdC5zZWdtZW50QXVkaW9Db2RlYyA9ICdhYzMnO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgMHgwNjpcbiAgICAgICAgLy8gc3RyZWFtX3R5cGUgNiBjYW4gbWVhbiBhIGxvdCBvZiBkaWZmZXJlbnQgdGhpbmdzIGluIGNhc2Ugb2YgRFZCLlxuICAgICAgICAvLyBXZSBuZWVkIHRvIGxvb2sgYXQgdGhlIGRlc2NyaXB0b3JzLiBSaWdodCBub3csIHdlJ3JlIG9ubHkgaW50ZXJlc3RlZFxuICAgICAgICAvLyBpbiBBQy0zIGF1ZGlvLCBzbyB3ZSBkbyB0aGUgZGVzY3JpcHRvciBwYXJzaW5nIG9ubHkgd2hlbiB3ZSBkb24ndCBoYXZlXG4gICAgICAgIC8vIGFuIGF1ZGlvIFBJRCB5ZXQuXG4gICAgICAgIGlmIChyZXN1bHQuYXVkaW9QaWQgPT09IC0xICYmIGVzSW5mb0xlbmd0aCA+IDApIHtcbiAgICAgICAgICBsZXQgcGFyc2VQb3MgPSBvZmZzZXQgKyA1O1xuICAgICAgICAgIGxldCByZW1haW5pbmcgPSBlc0luZm9MZW5ndGg7XG4gICAgICAgICAgd2hpbGUgKHJlbWFpbmluZyA+IDIpIHtcbiAgICAgICAgICAgIGNvbnN0IGRlc2NyaXB0b3JJZCA9IGRhdGFbcGFyc2VQb3NdO1xuICAgICAgICAgICAgc3dpdGNoIChkZXNjcmlwdG9ySWQpIHtcbiAgICAgICAgICAgICAgY2FzZSAweDZhOlxuICAgICAgICAgICAgICAgIC8vIERWQiBEZXNjcmlwdG9yIGZvciBBQy0zXG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgaWYgKHR5cGVTdXBwb3J0ZWQuYWMzICE9PSB0cnVlKSB7XG4gICAgICAgICAgICAgICAgICAgIGxvZ2dlci5sb2coJ0FDLTMgYXVkaW8gZm91bmQsIG5vdCBzdXBwb3J0ZWQgaW4gdGhpcyBicm93c2VyIGZvciBub3cnKTtcbiAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc3VsdC5hdWRpb1BpZCA9IHBpZDtcbiAgICAgICAgICAgICAgICAgICAgcmVzdWx0LnNlZ21lbnRBdWRpb0NvZGVjID0gJ2FjMyc7XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc3QgZGVzY3JpcHRvckxlbiA9IGRhdGFbcGFyc2VQb3MgKyAxXSArIDI7XG4gICAgICAgICAgICBwYXJzZVBvcyArPSBkZXNjcmlwdG9yTGVuO1xuICAgICAgICAgICAgcmVtYWluaW5nIC09IGRlc2NyaXB0b3JMZW47XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAweGMyOiAvLyBTQU1QTEUtQUVTIEVDM1xuICAgICAgLyogZmFsbHMgdGhyb3VnaCAqL1xuICAgICAgY2FzZSAweDg3OlxuICAgICAgICBlbWl0UGFyc2luZ0Vycm9yKG9ic2VydmVyLCBuZXcgRXJyb3IoJ1Vuc3VwcG9ydGVkIEVDLTMgaW4gTTJUUyBmb3VuZCcpKTtcbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgIGNhc2UgMHgyNDpcbiAgICAgICAgZW1pdFBhcnNpbmdFcnJvcihvYnNlcnZlciwgbmV3IEVycm9yKCdVbnN1cHBvcnRlZCBIRVZDIGluIE0yVFMgZm91bmQnKSk7XG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuICAgIC8vIG1vdmUgdG8gdGhlIG5leHQgdGFibGUgZW50cnlcbiAgICAvLyBza2lwIHBhc3QgdGhlIGVsZW1lbnRhcnkgc3RyZWFtIGRlc2NyaXB0b3JzLCBpZiBwcmVzZW50XG4gICAgb2Zmc2V0ICs9IGVzSW5mb0xlbmd0aCArIDU7XG4gIH1cbiAgcmV0dXJuIHJlc3VsdDtcbn1cbmZ1bmN0aW9uIGVtaXRQYXJzaW5nRXJyb3Iob2JzZXJ2ZXIsIGVycm9yLCBsZXZlbFJldHJ5KSB7XG4gIGxvZ2dlci53YXJuKGBwYXJzaW5nIGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XG4gIG9ic2VydmVyLmVtaXQoRXZlbnRzLkVSUk9SLCBFdmVudHMuRVJST1IsIHtcbiAgICB0eXBlOiBFcnJvclR5cGVzLk1FRElBX0VSUk9SLFxuICAgIGRldGFpbHM6IEVycm9yRGV0YWlscy5GUkFHX1BBUlNJTkdfRVJST1IsXG4gICAgZmF0YWw6IGZhbHNlLFxuICAgIGxldmVsUmV0cnksXG4gICAgZXJyb3IsXG4gICAgcmVhc29uOiBlcnJvci5tZXNzYWdlXG4gIH0pO1xufVxuZnVuY3Rpb24gbG9nRW5jcnlwdGVkU2FtcGxlc0ZvdW5kSW5VbmVuY3J5cHRlZFN0cmVhbSh0eXBlKSB7XG4gIGxvZ2dlci5sb2coYCR7dHlwZX0gd2l0aCBBRVMtMTI4LUNCQyBlbmNyeXB0aW9uIGZvdW5kIGluIHVuZW5jcnlwdGVkIHN0cmVhbWApO1xufVxuZnVuY3Rpb24gcGFyc2VQRVMoc3RyZWFtKSB7XG4gIGxldCBpID0gMDtcbiAgbGV0IGZyYWc7XG4gIGxldCBwZXNMZW47XG4gIGxldCBwZXNIZHJMZW47XG4gIGxldCBwZXNQdHM7XG4gIGxldCBwZXNEdHM7XG4gIGNvbnN0IGRhdGEgPSBzdHJlYW0uZGF0YTtcbiAgLy8gc2FmZXR5IGNoZWNrXG4gIGlmICghc3RyZWFtIHx8IHN0cmVhbS5zaXplID09PSAwKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICAvLyB3ZSBtaWdodCBuZWVkIHVwIHRvIDE5IGJ5dGVzIHRvIHJlYWQgUEVTIGhlYWRlclxuICAvLyBpZiBmaXJzdCBjaHVuayBvZiBkYXRhIGlzIGxlc3MgdGhhbiAxOSBieXRlcywgbGV0J3MgbWVyZ2UgaXQgd2l0aCBmb2xsb3dpbmcgb25lcyB1bnRpbCB3ZSBnZXQgMTkgYnl0ZXNcbiAgLy8gdXN1YWxseSBvbmx5IG9uZSBtZXJnZSBpcyBuZWVkZWQgKGFuZCB0aGlzIGlzIHJhcmUgLi4uKVxuICB3aGlsZSAoZGF0YVswXS5sZW5ndGggPCAxOSAmJiBkYXRhLmxlbmd0aCA+IDEpIHtcbiAgICBkYXRhWzBdID0gYXBwZW5kVWludDhBcnJheShkYXRhWzBdLCBkYXRhWzFdKTtcbiAgICBkYXRhLnNwbGljZSgxLCAxKTtcbiAgfVxuICAvLyByZXRyaWV2ZSBQVFMvRFRTIGZyb20gZmlyc3QgZnJhZ21lbnRcbiAgZnJhZyA9IGRhdGFbMF07XG4gIGNvbnN0IHBlc1ByZWZpeCA9IChmcmFnWzBdIDw8IDE2KSArIChmcmFnWzFdIDw8IDgpICsgZnJhZ1syXTtcbiAgaWYgKHBlc1ByZWZpeCA9PT0gMSkge1xuICAgIHBlc0xlbiA9IChmcmFnWzRdIDw8IDgpICsgZnJhZ1s1XTtcbiAgICAvLyBpZiBQRVMgcGFyc2VkIGxlbmd0aCBpcyBub3QgemVybyBhbmQgZ3JlYXRlciB0aGFuIHRvdGFsIHJlY2VpdmVkIGxlbmd0aCwgc3RvcCBwYXJzaW5nLiBQRVMgbWlnaHQgYmUgdHJ1bmNhdGVkXG4gICAgLy8gbWludXMgNiA6IFBFUyBoZWFkZXIgc2l6ZVxuICAgIGlmIChwZXNMZW4gJiYgcGVzTGVuID4gc3RyZWFtLnNpemUgLSA2KSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgY29uc3QgcGVzRmxhZ3MgPSBmcmFnWzddO1xuICAgIGlmIChwZXNGbGFncyAmIDB4YzApIHtcbiAgICAgIC8qIFBFUyBoZWFkZXIgZGVzY3JpYmVkIGhlcmUgOiBodHRwOi8vZHZkLnNvdXJjZWZvcmdlLm5ldC9kdmRpbmZvL3Blcy1oZHIuaHRtbFxuICAgICAgICAgIGFzIFBUUyAvIERUUyBpcyAzMyBiaXQgd2UgY2Fubm90IHVzZSBiaXR3aXNlIG9wZXJhdG9yIGluIEpTLFxuICAgICAgICAgIGFzIEJpdHdpc2Ugb3BlcmF0b3JzIHRyZWF0IHRoZWlyIG9wZXJhbmRzIGFzIGEgc2VxdWVuY2Ugb2YgMzIgYml0cyAqL1xuICAgICAgcGVzUHRzID0gKGZyYWdbOV0gJiAweDBlKSAqIDUzNjg3MDkxMiArXG4gICAgICAvLyAxIDw8IDI5XG4gICAgICAoZnJhZ1sxMF0gJiAweGZmKSAqIDQxOTQzMDQgK1xuICAgICAgLy8gMSA8PCAyMlxuICAgICAgKGZyYWdbMTFdICYgMHhmZSkgKiAxNjM4NCArXG4gICAgICAvLyAxIDw8IDE0XG4gICAgICAoZnJhZ1sxMl0gJiAweGZmKSAqIDEyOCArXG4gICAgICAvLyAxIDw8IDdcbiAgICAgIChmcmFnWzEzXSAmIDB4ZmUpIC8gMjtcbiAgICAgIGlmIChwZXNGbGFncyAmIDB4NDApIHtcbiAgICAgICAgcGVzRHRzID0gKGZyYWdbMTRdICYgMHgwZSkgKiA1MzY4NzA5MTIgK1xuICAgICAgICAvLyAxIDw8IDI5XG4gICAgICAgIChmcmFnWzE1XSAmIDB4ZmYpICogNDE5NDMwNCArXG4gICAgICAgIC8vIDEgPDwgMjJcbiAgICAgICAgKGZyYWdbMTZdICYgMHhmZSkgKiAxNjM4NCArXG4gICAgICAgIC8vIDEgPDwgMTRcbiAgICAgICAgKGZyYWdbMTddICYgMHhmZikgKiAxMjggK1xuICAgICAgICAvLyAxIDw8IDdcbiAgICAgICAgKGZyYWdbMThdICYgMHhmZSkgLyAyO1xuICAgICAgICBpZiAocGVzUHRzIC0gcGVzRHRzID4gNjAgKiA5MDAwMCkge1xuICAgICAgICAgIGxvZ2dlci53YXJuKGAke01hdGgucm91bmQoKHBlc1B0cyAtIHBlc0R0cykgLyA5MDAwMCl9cyBkZWx0YSBiZXR3ZWVuIFBUUyBhbmQgRFRTLCBhbGlnbiB0aGVtYCk7XG4gICAgICAgICAgcGVzUHRzID0gcGVzRHRzO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBwZXNEdHMgPSBwZXNQdHM7XG4gICAgICB9XG4gICAgfVxuICAgIHBlc0hkckxlbiA9IGZyYWdbOF07XG4gICAgLy8gOSBieXRlcyA6IDYgYnl0ZXMgZm9yIFBFUyBoZWFkZXIgKyAzIGJ5dGVzIGZvciBQRVMgZXh0ZW5zaW9uXG4gICAgbGV0IHBheWxvYWRTdGFydE9mZnNldCA9IHBlc0hkckxlbiArIDk7XG4gICAgaWYgKHN0cmVhbS5zaXplIDw9IHBheWxvYWRTdGFydE9mZnNldCkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIHN0cmVhbS5zaXplIC09IHBheWxvYWRTdGFydE9mZnNldDtcbiAgICAvLyByZWFzc2VtYmxlIFBFUyBwYWNrZXRcbiAgICBjb25zdCBwZXNEYXRhID0gbmV3IFVpbnQ4QXJyYXkoc3RyZWFtLnNpemUpO1xuICAgIGZvciAobGV0IGogPSAwLCBkYXRhTGVuID0gZGF0YS5sZW5ndGg7IGogPCBkYXRhTGVuOyBqKyspIHtcbiAgICAgIGZyYWcgPSBkYXRhW2pdO1xuICAgICAgbGV0IGxlbiA9IGZyYWcuYnl0ZUxlbmd0aDtcbiAgICAgIGlmIChwYXlsb2FkU3RhcnRPZmZzZXQpIHtcbiAgICAgICAgaWYgKHBheWxvYWRTdGFydE9mZnNldCA+IGxlbikge1xuICAgICAgICAgIC8vIHRyaW0gZnVsbCBmcmFnIGlmIFBFUyBoZWFkZXIgYmlnZ2VyIHRoYW4gZnJhZ1xuICAgICAgICAgIHBheWxvYWRTdGFydE9mZnNldCAtPSBsZW47XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gdHJpbSBwYXJ0aWFsIGZyYWcgaWYgUEVTIGhlYWRlciBzbWFsbGVyIHRoYW4gZnJhZ1xuICAgICAgICAgIGZyYWcgPSBmcmFnLnN1YmFycmF5KHBheWxvYWRTdGFydE9mZnNldCk7XG4gICAgICAgICAgbGVuIC09IHBheWxvYWRTdGFydE9mZnNldDtcbiAgICAgICAgICBwYXlsb2FkU3RhcnRPZmZzZXQgPSAwO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBwZXNEYXRhLnNldChmcmFnLCBpKTtcbiAgICAgIGkgKz0gbGVuO1xuICAgIH1cbiAgICBpZiAocGVzTGVuKSB7XG4gICAgICAvLyBwYXlsb2FkIHNpemUgOiByZW1vdmUgUEVTIGhlYWRlciArIFBFUyBleHRlbnNpb25cbiAgICAgIHBlc0xlbiAtPSBwZXNIZHJMZW4gKyAzO1xuICAgIH1cbiAgICByZXR1cm4ge1xuICAgICAgZGF0YTogcGVzRGF0YSxcbiAgICAgIHB0czogcGVzUHRzLFxuICAgICAgZHRzOiBwZXNEdHMsXG4gICAgICBsZW46IHBlc0xlblxuICAgIH07XG4gIH1cbiAgcmV0dXJuIG51bGw7XG59XG5cbi8qKlxuICogTVAzIGRlbXV4ZXJcbiAqL1xuY2xhc3MgTVAzRGVtdXhlciBleHRlbmRzIEJhc2VBdWRpb0RlbXV4ZXIge1xuICByZXNldEluaXRTZWdtZW50KGluaXRTZWdtZW50LCBhdWRpb0NvZGVjLCB2aWRlb0NvZGVjLCB0cmFja0R1cmF0aW9uKSB7XG4gICAgc3VwZXIucmVzZXRJbml0U2VnbWVudChpbml0U2VnbWVudCwgYXVkaW9Db2RlYywgdmlkZW9Db2RlYywgdHJhY2tEdXJhdGlvbik7XG4gICAgdGhpcy5fYXVkaW9UcmFjayA9IHtcbiAgICAgIGNvbnRhaW5lcjogJ2F1ZGlvL21wZWcnLFxuICAgICAgdHlwZTogJ2F1ZGlvJyxcbiAgICAgIGlkOiAyLFxuICAgICAgcGlkOiAtMSxcbiAgICAgIHNlcXVlbmNlTnVtYmVyOiAwLFxuICAgICAgc2VnbWVudENvZGVjOiAnbXAzJyxcbiAgICAgIHNhbXBsZXM6IFtdLFxuICAgICAgbWFuaWZlc3RDb2RlYzogYXVkaW9Db2RlYyxcbiAgICAgIGR1cmF0aW9uOiB0cmFja0R1cmF0aW9uLFxuICAgICAgaW5wdXRUaW1lU2NhbGU6IDkwMDAwLFxuICAgICAgZHJvcHBlZDogMFxuICAgIH07XG4gIH1cbiAgc3RhdGljIHByb2JlKGRhdGEpIHtcbiAgICBpZiAoIWRhdGEpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICAvLyBjaGVjayBpZiBkYXRhIGNvbnRhaW5zIElEMyB0aW1lc3RhbXAgYW5kIE1QRUcgc3luYyB3b3JkXG4gICAgLy8gTG9vayBmb3IgTVBFRyBoZWFkZXIgfCAxMTExIDExMTEgfCAxMTFYIFhZWlggfCB3aGVyZSBYIGNhbiBiZSBlaXRoZXIgMCBvciAxIGFuZCBZIG9yIFogc2hvdWxkIGJlIDFcbiAgICAvLyBMYXllciBiaXRzIChwb3NpdGlvbiAxNCBhbmQgMTUpIGluIGhlYWRlciBzaG91bGQgYmUgYWx3YXlzIGRpZmZlcmVudCBmcm9tIDAgKExheWVyIEkgb3IgTGF5ZXIgSUkgb3IgTGF5ZXIgSUlJKVxuICAgIC8vIE1vcmUgaW5mbyBodHRwOi8vd3d3Lm1wMy10ZWNoLm9yZy9wcm9ncmFtbWVyL2ZyYW1lX2hlYWRlci5odG1sXG4gICAgY29uc3QgaWQzRGF0YSA9IGdldElEM0RhdGEoZGF0YSwgMCk7XG4gICAgbGV0IG9mZnNldCA9IChpZDNEYXRhID09IG51bGwgPyB2b2lkIDAgOiBpZDNEYXRhLmxlbmd0aCkgfHwgMDtcblxuICAgIC8vIENoZWNrIGZvciBhYy0zfGVjLTMgc3luYyBieXRlcyBhbmQgcmV0dXJuIGZhbHNlIGlmIHByZXNlbnRcbiAgICBpZiAoaWQzRGF0YSAmJiBkYXRhW29mZnNldF0gPT09IDB4MGIgJiYgZGF0YVtvZmZzZXQgKyAxXSA9PT0gMHg3NyAmJiBnZXRUaW1lU3RhbXAoaWQzRGF0YSkgIT09IHVuZGVmaW5lZCAmJlxuICAgIC8vIGNoZWNrIHRoZSBic2lkIHRvIGNvbmZpcm0gYWMtMyBvciBlYy0zIChub3QgbXAzKVxuICAgIGdldEF1ZGlvQlNJRChkYXRhLCBvZmZzZXQpIDw9IDE2KSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGZvciAobGV0IGxlbmd0aCA9IGRhdGEubGVuZ3RoOyBvZmZzZXQgPCBsZW5ndGg7IG9mZnNldCsrKSB7XG4gICAgICBpZiAocHJvYmUoZGF0YSwgb2Zmc2V0KSkge1xuICAgICAgICBsb2dnZXIubG9nKCdNUEVHIEF1ZGlvIHN5bmMgd29yZCBmb3VuZCAhJyk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgY2FuUGFyc2UoZGF0YSwgb2Zmc2V0KSB7XG4gICAgcmV0dXJuIGNhblBhcnNlKGRhdGEsIG9mZnNldCk7XG4gIH1cbiAgYXBwZW5kRnJhbWUodHJhY2ssIGRhdGEsIG9mZnNldCkge1xuICAgIGlmICh0aGlzLmJhc2VQVFMgPT09IG51bGwpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgcmV0dXJuIGFwcGVuZEZyYW1lJDEodHJhY2ssIGRhdGEsIG9mZnNldCwgdGhpcy5iYXNlUFRTLCB0aGlzLmZyYW1lSW5kZXgpO1xuICB9XG59XG5cbi8qKlxuICogIEFBQyBoZWxwZXJcbiAqL1xuXG5jbGFzcyBBQUMge1xuICBzdGF0aWMgZ2V0U2lsZW50RnJhbWUoY29kZWMsIGNoYW5uZWxDb3VudCkge1xuICAgIHN3aXRjaCAoY29kZWMpIHtcbiAgICAgIGNhc2UgJ21wNGEuNDAuMic6XG4gICAgICAgIGlmIChjaGFubmVsQ291bnQgPT09IDEpIHtcbiAgICAgICAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkoWzB4MDAsIDB4YzgsIDB4MDAsIDB4ODAsIDB4MjMsIDB4ODBdKTtcbiAgICAgICAgfSBlbHNlIGlmIChjaGFubmVsQ291bnQgPT09IDIpIHtcbiAgICAgICAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkoWzB4MjEsIDB4MDAsIDB4NDksIDB4OTAsIDB4MDIsIDB4MTksIDB4MDAsIDB4MjMsIDB4ODBdKTtcbiAgICAgICAgfSBlbHNlIGlmIChjaGFubmVsQ291bnQgPT09IDMpIHtcbiAgICAgICAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkoWzB4MDAsIDB4YzgsIDB4MDAsIDB4ODAsIDB4MjAsIDB4ODQsIDB4MDEsIDB4MjYsIDB4NDAsIDB4MDgsIDB4NjQsIDB4MDAsIDB4OGVdKTtcbiAgICAgICAgfSBlbHNlIGlmIChjaGFubmVsQ291bnQgPT09IDQpIHtcbiAgICAgICAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkoWzB4MDAsIDB4YzgsIDB4MDAsIDB4ODAsIDB4MjAsIDB4ODQsIDB4MDEsIDB4MjYsIDB4NDAsIDB4MDgsIDB4NjQsIDB4MDAsIDB4ODAsIDB4MmMsIDB4ODAsIDB4MDgsIDB4MDIsIDB4MzhdKTtcbiAgICAgICAgfSBlbHNlIGlmIChjaGFubmVsQ291bnQgPT09IDUpIHtcbiAgICAgICAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkoWzB4MDAsIDB4YzgsIDB4MDAsIDB4ODAsIDB4MjAsIDB4ODQsIDB4MDEsIDB4MjYsIDB4NDAsIDB4MDgsIDB4NjQsIDB4MDAsIDB4ODIsIDB4MzAsIDB4MDQsIDB4OTksIDB4MDAsIDB4MjEsIDB4OTAsIDB4MDIsIDB4MzhdKTtcbiAgICAgICAgfSBlbHNlIGlmIChjaGFubmVsQ291bnQgPT09IDYpIHtcbiAgICAgICAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkoWzB4MDAsIDB4YzgsIDB4MDAsIDB4ODAsIDB4MjAsIDB4ODQsIDB4MDEsIDB4MjYsIDB4NDAsIDB4MDgsIDB4NjQsIDB4MDAsIDB4ODIsIDB4MzAsIDB4MDQsIDB4OTksIDB4MDAsIDB4MjEsIDB4OTAsIDB4MDIsIDB4MDAsIDB4YjIsIDB4MDAsIDB4MjAsIDB4MDgsIDB4ZTBdKTtcbiAgICAgICAgfVxuICAgICAgICBicmVhaztcbiAgICAgIC8vIGhhbmRsZSBIRS1BQUMgYmVsb3cgKG1wNGEuNDAuNSAvIG1wNGEuNDAuMjkpXG4gICAgICBkZWZhdWx0OlxuICAgICAgICBpZiAoY2hhbm5lbENvdW50ID09PSAxKSB7XG4gICAgICAgICAgLy8gZmZtcGVnIC15IC1mIGxhdmZpIC1pIFwiYWV2YWxzcmM9MDpkPTAuMDVcIiAtYzphIGxpYmZka19hYWMgLXByb2ZpbGU6YSBhYWNfaGUgLWI6YSA0ayBvdXRwdXQuYWFjICYmIGhleGR1bXAgLXYgLWUgJzE2LzEgXCIweCV4LFwiIFwiXFxuXCInIC12IG91dHB1dC5hYWNcbiAgICAgICAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkoWzB4MSwgMHg0MCwgMHgyMiwgMHg4MCwgMHhhMywgMHg0ZSwgMHhlNiwgMHg4MCwgMHhiYSwgMHg4LCAweDAsIDB4MCwgMHgwLCAweDFjLCAweDYsIDB4ZjEsIDB4YzEsIDB4YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1YSwgMHg1ZV0pO1xuICAgICAgICB9IGVsc2UgaWYgKGNoYW5uZWxDb3VudCA9PT0gMikge1xuICAgICAgICAgIC8vIGZmbXBlZyAteSAtZiBsYXZmaSAtaSBcImFldmFsc3JjPTB8MDpkPTAuMDVcIiAtYzphIGxpYmZka19hYWMgLXByb2ZpbGU6YSBhYWNfaGVfdjIgLWI6YSA0ayBvdXRwdXQuYWFjICYmIGhleGR1bXAgLXYgLWUgJzE2LzEgXCIweCV4LFwiIFwiXFxuXCInIC12IG91dHB1dC5hYWNcbiAgICAgICAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkoWzB4MSwgMHg0MCwgMHgyMiwgMHg4MCwgMHhhMywgMHg1ZSwgMHhlNiwgMHg4MCwgMHhiYSwgMHg4LCAweDAsIDB4MCwgMHgwLCAweDAsIDB4OTUsIDB4MCwgMHg2LCAweGYxLCAweGExLCAweGEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWVdKTtcbiAgICAgICAgfSBlbHNlIGlmIChjaGFubmVsQ291bnQgPT09IDMpIHtcbiAgICAgICAgICAvLyBmZm1wZWcgLXkgLWYgbGF2ZmkgLWkgXCJhZXZhbHNyYz0wfDB8MDpkPTAuMDVcIiAtYzphIGxpYmZka19hYWMgLXByb2ZpbGU6YSBhYWNfaGVfdjIgLWI6YSA0ayBvdXRwdXQuYWFjICYmIGhleGR1bXAgLXYgLWUgJzE2LzEgXCIweCV4LFwiIFwiXFxuXCInIC12IG91dHB1dC5hYWNcbiAgICAgICAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkoWzB4MSwgMHg0MCwgMHgyMiwgMHg4MCwgMHhhMywgMHg1ZSwgMHhlNiwgMHg4MCwgMHhiYSwgMHg4LCAweDAsIDB4MCwgMHgwLCAweDAsIDB4OTUsIDB4MCwgMHg2LCAweGYxLCAweGExLCAweGEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWEsIDB4NWVdKTtcbiAgICAgICAgfVxuICAgICAgICBicmVhaztcbiAgICB9XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxufVxuXG4vKipcbiAqIEdlbmVyYXRlIE1QNCBCb3hcbiAqL1xuXG5jb25zdCBVSU5UMzJfTUFYID0gTWF0aC5wb3coMiwgMzIpIC0gMTtcbmNsYXNzIE1QNCB7XG4gIHN0YXRpYyBpbml0KCkge1xuICAgIE1QNC50eXBlcyA9IHtcbiAgICAgIGF2YzE6IFtdLFxuICAgICAgLy8gY29kaW5nbmFtZVxuICAgICAgYXZjQzogW10sXG4gICAgICBidHJ0OiBbXSxcbiAgICAgIGRpbmY6IFtdLFxuICAgICAgZHJlZjogW10sXG4gICAgICBlc2RzOiBbXSxcbiAgICAgIGZ0eXA6IFtdLFxuICAgICAgaGRscjogW10sXG4gICAgICBtZGF0OiBbXSxcbiAgICAgIG1kaGQ6IFtdLFxuICAgICAgbWRpYTogW10sXG4gICAgICBtZmhkOiBbXSxcbiAgICAgIG1pbmY6IFtdLFxuICAgICAgbW9vZjogW10sXG4gICAgICBtb292OiBbXSxcbiAgICAgIG1wNGE6IFtdLFxuICAgICAgJy5tcDMnOiBbXSxcbiAgICAgIGRhYzM6IFtdLFxuICAgICAgJ2FjLTMnOiBbXSxcbiAgICAgIG12ZXg6IFtdLFxuICAgICAgbXZoZDogW10sXG4gICAgICBwYXNwOiBbXSxcbiAgICAgIHNkdHA6IFtdLFxuICAgICAgc3RibDogW10sXG4gICAgICBzdGNvOiBbXSxcbiAgICAgIHN0c2M6IFtdLFxuICAgICAgc3RzZDogW10sXG4gICAgICBzdHN6OiBbXSxcbiAgICAgIHN0dHM6IFtdLFxuICAgICAgdGZkdDogW10sXG4gICAgICB0ZmhkOiBbXSxcbiAgICAgIHRyYWY6IFtdLFxuICAgICAgdHJhazogW10sXG4gICAgICB0cnVuOiBbXSxcbiAgICAgIHRyZXg6IFtdLFxuICAgICAgdGtoZDogW10sXG4gICAgICB2bWhkOiBbXSxcbiAgICAgIHNtaGQ6IFtdXG4gICAgfTtcbiAgICBsZXQgaTtcbiAgICBmb3IgKGkgaW4gTVA0LnR5cGVzKSB7XG4gICAgICBpZiAoTVA0LnR5cGVzLmhhc093blByb3BlcnR5KGkpKSB7XG4gICAgICAgIE1QNC50eXBlc1tpXSA9IFtpLmNoYXJDb2RlQXQoMCksIGkuY2hhckNvZGVBdCgxKSwgaS5jaGFyQ29kZUF0KDIpLCBpLmNoYXJDb2RlQXQoMyldO1xuICAgICAgfVxuICAgIH1cbiAgICBjb25zdCB2aWRlb0hkbHIgPSBuZXcgVWludDhBcnJheShbMHgwMCxcbiAgICAvLyB2ZXJzaW9uIDBcbiAgICAweDAwLCAweDAwLCAweDAwLFxuICAgIC8vIGZsYWdzXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCxcbiAgICAvLyBwcmVfZGVmaW5lZFxuICAgIDB4NzYsIDB4NjksIDB4NjQsIDB4NjUsXG4gICAgLy8gaGFuZGxlcl90eXBlOiAndmlkZSdcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLFxuICAgIC8vIHJlc2VydmVkXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCxcbiAgICAvLyByZXNlcnZlZFxuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsXG4gICAgLy8gcmVzZXJ2ZWRcbiAgICAweDU2LCAweDY5LCAweDY0LCAweDY1LCAweDZmLCAweDQ4LCAweDYxLCAweDZlLCAweDY0LCAweDZjLCAweDY1LCAweDcyLCAweDAwIC8vIG5hbWU6ICdWaWRlb0hhbmRsZXInXG4gICAgXSk7XG4gICAgY29uc3QgYXVkaW9IZGxyID0gbmV3IFVpbnQ4QXJyYXkoWzB4MDAsXG4gICAgLy8gdmVyc2lvbiAwXG4gICAgMHgwMCwgMHgwMCwgMHgwMCxcbiAgICAvLyBmbGFnc1xuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsXG4gICAgLy8gcHJlX2RlZmluZWRcbiAgICAweDczLCAweDZmLCAweDc1LCAweDZlLFxuICAgIC8vIGhhbmRsZXJfdHlwZTogJ3NvdW4nXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCxcbiAgICAvLyByZXNlcnZlZFxuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsXG4gICAgLy8gcmVzZXJ2ZWRcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLFxuICAgIC8vIHJlc2VydmVkXG4gICAgMHg1MywgMHg2ZiwgMHg3NSwgMHg2ZSwgMHg2NCwgMHg0OCwgMHg2MSwgMHg2ZSwgMHg2NCwgMHg2YywgMHg2NSwgMHg3MiwgMHgwMCAvLyBuYW1lOiAnU291bmRIYW5kbGVyJ1xuICAgIF0pO1xuICAgIE1QNC5IRExSX1RZUEVTID0ge1xuICAgICAgdmlkZW86IHZpZGVvSGRscixcbiAgICAgIGF1ZGlvOiBhdWRpb0hkbHJcbiAgICB9O1xuICAgIGNvbnN0IGRyZWYgPSBuZXcgVWludDhBcnJheShbMHgwMCxcbiAgICAvLyB2ZXJzaW9uIDBcbiAgICAweDAwLCAweDAwLCAweDAwLFxuICAgIC8vIGZsYWdzXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMSxcbiAgICAvLyBlbnRyeV9jb3VudFxuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MGMsXG4gICAgLy8gZW50cnlfc2l6ZVxuICAgIDB4NzUsIDB4NzIsIDB4NmMsIDB4MjAsXG4gICAgLy8gJ3VybCcgdHlwZVxuICAgIDB4MDAsXG4gICAgLy8gdmVyc2lvbiAwXG4gICAgMHgwMCwgMHgwMCwgMHgwMSAvLyBlbnRyeV9mbGFnc1xuICAgIF0pO1xuICAgIGNvbnN0IHN0Y28gPSBuZXcgVWludDhBcnJheShbMHgwMCxcbiAgICAvLyB2ZXJzaW9uXG4gICAgMHgwMCwgMHgwMCwgMHgwMCxcbiAgICAvLyBmbGFnc1xuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAgLy8gZW50cnlfY291bnRcbiAgICBdKTtcbiAgICBNUDQuU1RUUyA9IE1QNC5TVFNDID0gTVA0LlNUQ08gPSBzdGNvO1xuICAgIE1QNC5TVFNaID0gbmV3IFVpbnQ4QXJyYXkoWzB4MDAsXG4gICAgLy8gdmVyc2lvblxuICAgIDB4MDAsIDB4MDAsIDB4MDAsXG4gICAgLy8gZmxhZ3NcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLFxuICAgIC8vIHNhbXBsZV9zaXplXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCAvLyBzYW1wbGVfY291bnRcbiAgICBdKTtcbiAgICBNUDQuVk1IRCA9IG5ldyBVaW50OEFycmF5KFsweDAwLFxuICAgIC8vIHZlcnNpb25cbiAgICAweDAwLCAweDAwLCAweDAxLFxuICAgIC8vIGZsYWdzXG4gICAgMHgwMCwgMHgwMCxcbiAgICAvLyBncmFwaGljc21vZGVcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwIC8vIG9wY29sb3JcbiAgICBdKTtcbiAgICBNUDQuU01IRCA9IG5ldyBVaW50OEFycmF5KFsweDAwLFxuICAgIC8vIHZlcnNpb25cbiAgICAweDAwLCAweDAwLCAweDAwLFxuICAgIC8vIGZsYWdzXG4gICAgMHgwMCwgMHgwMCxcbiAgICAvLyBiYWxhbmNlXG4gICAgMHgwMCwgMHgwMCAvLyByZXNlcnZlZFxuICAgIF0pO1xuICAgIE1QNC5TVFNEID0gbmV3IFVpbnQ4QXJyYXkoWzB4MDAsXG4gICAgLy8gdmVyc2lvbiAwXG4gICAgMHgwMCwgMHgwMCwgMHgwMCxcbiAgICAvLyBmbGFnc1xuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDFdKTsgLy8gZW50cnlfY291bnRcblxuICAgIGNvbnN0IG1ham9yQnJhbmQgPSBuZXcgVWludDhBcnJheShbMTA1LCAxMTUsIDExMSwgMTA5XSk7IC8vIGlzb21cbiAgICBjb25zdCBhdmMxQnJhbmQgPSBuZXcgVWludDhBcnJheShbOTcsIDExOCwgOTksIDQ5XSk7IC8vIGF2YzFcbiAgICBjb25zdCBtaW5vclZlcnNpb24gPSBuZXcgVWludDhBcnJheShbMCwgMCwgMCwgMV0pO1xuICAgIE1QNC5GVFlQID0gTVA0LmJveChNUDQudHlwZXMuZnR5cCwgbWFqb3JCcmFuZCwgbWlub3JWZXJzaW9uLCBtYWpvckJyYW5kLCBhdmMxQnJhbmQpO1xuICAgIE1QNC5ESU5GID0gTVA0LmJveChNUDQudHlwZXMuZGluZiwgTVA0LmJveChNUDQudHlwZXMuZHJlZiwgZHJlZikpO1xuICB9XG4gIHN0YXRpYyBib3godHlwZSwgLi4ucGF5bG9hZCkge1xuICAgIGxldCBzaXplID0gODtcbiAgICBsZXQgaSA9IHBheWxvYWQubGVuZ3RoO1xuICAgIGNvbnN0IGxlbiA9IGk7XG4gICAgLy8gY2FsY3VsYXRlIHRoZSB0b3RhbCBzaXplIHdlIG5lZWQgdG8gYWxsb2NhdGVcbiAgICB3aGlsZSAoaS0tKSB7XG4gICAgICBzaXplICs9IHBheWxvYWRbaV0uYnl0ZUxlbmd0aDtcbiAgICB9XG4gICAgY29uc3QgcmVzdWx0ID0gbmV3IFVpbnQ4QXJyYXkoc2l6ZSk7XG4gICAgcmVzdWx0WzBdID0gc2l6ZSA+PiAyNCAmIDB4ZmY7XG4gICAgcmVzdWx0WzFdID0gc2l6ZSA+PiAxNiAmIDB4ZmY7XG4gICAgcmVzdWx0WzJdID0gc2l6ZSA+PiA4ICYgMHhmZjtcbiAgICByZXN1bHRbM10gPSBzaXplICYgMHhmZjtcbiAgICByZXN1bHQuc2V0KHR5cGUsIDQpO1xuICAgIC8vIGNvcHkgdGhlIHBheWxvYWQgaW50byB0aGUgcmVzdWx0XG4gICAgZm9yIChpID0gMCwgc2l6ZSA9IDg7IGkgPCBsZW47IGkrKykge1xuICAgICAgLy8gY29weSBwYXlsb2FkW2ldIGFycmF5IEAgb2Zmc2V0IHNpemVcbiAgICAgIHJlc3VsdC5zZXQocGF5bG9hZFtpXSwgc2l6ZSk7XG4gICAgICBzaXplICs9IHBheWxvYWRbaV0uYnl0ZUxlbmd0aDtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuICBzdGF0aWMgaGRscih0eXBlKSB7XG4gICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLmhkbHIsIE1QNC5IRExSX1RZUEVTW3R5cGVdKTtcbiAgfVxuICBzdGF0aWMgbWRhdChkYXRhKSB7XG4gICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLm1kYXQsIGRhdGEpO1xuICB9XG4gIHN0YXRpYyBtZGhkKHRpbWVzY2FsZSwgZHVyYXRpb24pIHtcbiAgICBkdXJhdGlvbiAqPSB0aW1lc2NhbGU7XG4gICAgY29uc3QgdXBwZXJXb3JkRHVyYXRpb24gPSBNYXRoLmZsb29yKGR1cmF0aW9uIC8gKFVJTlQzMl9NQVggKyAxKSk7XG4gICAgY29uc3QgbG93ZXJXb3JkRHVyYXRpb24gPSBNYXRoLmZsb29yKGR1cmF0aW9uICUgKFVJTlQzMl9NQVggKyAxKSk7XG4gICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLm1kaGQsIG5ldyBVaW50OEFycmF5KFsweDAxLFxuICAgIC8vIHZlcnNpb24gMVxuICAgIDB4MDAsIDB4MDAsIDB4MDAsXG4gICAgLy8gZmxhZ3NcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAyLFxuICAgIC8vIGNyZWF0aW9uX3RpbWVcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAzLFxuICAgIC8vIG1vZGlmaWNhdGlvbl90aW1lXG4gICAgdGltZXNjYWxlID4+IDI0ICYgMHhmZiwgdGltZXNjYWxlID4+IDE2ICYgMHhmZiwgdGltZXNjYWxlID4+IDggJiAweGZmLCB0aW1lc2NhbGUgJiAweGZmLFxuICAgIC8vIHRpbWVzY2FsZVxuICAgIHVwcGVyV29yZER1cmF0aW9uID4+IDI0LCB1cHBlcldvcmREdXJhdGlvbiA+PiAxNiAmIDB4ZmYsIHVwcGVyV29yZER1cmF0aW9uID4+IDggJiAweGZmLCB1cHBlcldvcmREdXJhdGlvbiAmIDB4ZmYsIGxvd2VyV29yZER1cmF0aW9uID4+IDI0LCBsb3dlcldvcmREdXJhdGlvbiA+PiAxNiAmIDB4ZmYsIGxvd2VyV29yZER1cmF0aW9uID4+IDggJiAweGZmLCBsb3dlcldvcmREdXJhdGlvbiAmIDB4ZmYsIDB4NTUsIDB4YzQsXG4gICAgLy8gJ3VuZCcgbGFuZ3VhZ2UgKHVuZGV0ZXJtaW5lZClcbiAgICAweDAwLCAweDAwXSkpO1xuICB9XG4gIHN0YXRpYyBtZGlhKHRyYWNrKSB7XG4gICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLm1kaWEsIE1QNC5tZGhkKHRyYWNrLnRpbWVzY2FsZSwgdHJhY2suZHVyYXRpb24pLCBNUDQuaGRscih0cmFjay50eXBlKSwgTVA0Lm1pbmYodHJhY2spKTtcbiAgfVxuICBzdGF0aWMgbWZoZChzZXF1ZW5jZU51bWJlcikge1xuICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlcy5tZmhkLCBuZXcgVWludDhBcnJheShbMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCxcbiAgICAvLyBmbGFnc1xuICAgIHNlcXVlbmNlTnVtYmVyID4+IDI0LCBzZXF1ZW5jZU51bWJlciA+PiAxNiAmIDB4ZmYsIHNlcXVlbmNlTnVtYmVyID4+IDggJiAweGZmLCBzZXF1ZW5jZU51bWJlciAmIDB4ZmYgLy8gc2VxdWVuY2VfbnVtYmVyXG4gICAgXSkpO1xuICB9XG4gIHN0YXRpYyBtaW5mKHRyYWNrKSB7XG4gICAgaWYgKHRyYWNrLnR5cGUgPT09ICdhdWRpbycpIHtcbiAgICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlcy5taW5mLCBNUDQuYm94KE1QNC50eXBlcy5zbWhkLCBNUDQuU01IRCksIE1QNC5ESU5GLCBNUDQuc3RibCh0cmFjaykpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gTVA0LmJveChNUDQudHlwZXMubWluZiwgTVA0LmJveChNUDQudHlwZXMudm1oZCwgTVA0LlZNSEQpLCBNUDQuRElORiwgTVA0LnN0YmwodHJhY2spKTtcbiAgICB9XG4gIH1cbiAgc3RhdGljIG1vb2Yoc24sIGJhc2VNZWRpYURlY29kZVRpbWUsIHRyYWNrKSB7XG4gICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLm1vb2YsIE1QNC5tZmhkKHNuKSwgTVA0LnRyYWYodHJhY2ssIGJhc2VNZWRpYURlY29kZVRpbWUpKTtcbiAgfVxuICBzdGF0aWMgbW9vdih0cmFja3MpIHtcbiAgICBsZXQgaSA9IHRyYWNrcy5sZW5ndGg7XG4gICAgY29uc3QgYm94ZXMgPSBbXTtcbiAgICB3aGlsZSAoaS0tKSB7XG4gICAgICBib3hlc1tpXSA9IE1QNC50cmFrKHRyYWNrc1tpXSk7XG4gICAgfVxuICAgIHJldHVybiBNUDQuYm94LmFwcGx5KG51bGwsIFtNUDQudHlwZXMubW9vdiwgTVA0Lm12aGQodHJhY2tzWzBdLnRpbWVzY2FsZSwgdHJhY2tzWzBdLmR1cmF0aW9uKV0uY29uY2F0KGJveGVzKS5jb25jYXQoTVA0Lm12ZXgodHJhY2tzKSkpO1xuICB9XG4gIHN0YXRpYyBtdmV4KHRyYWNrcykge1xuICAgIGxldCBpID0gdHJhY2tzLmxlbmd0aDtcbiAgICBjb25zdCBib3hlcyA9IFtdO1xuICAgIHdoaWxlIChpLS0pIHtcbiAgICAgIGJveGVzW2ldID0gTVA0LnRyZXgodHJhY2tzW2ldKTtcbiAgICB9XG4gICAgcmV0dXJuIE1QNC5ib3guYXBwbHkobnVsbCwgW01QNC50eXBlcy5tdmV4LCAuLi5ib3hlc10pO1xuICB9XG4gIHN0YXRpYyBtdmhkKHRpbWVzY2FsZSwgZHVyYXRpb24pIHtcbiAgICBkdXJhdGlvbiAqPSB0aW1lc2NhbGU7XG4gICAgY29uc3QgdXBwZXJXb3JkRHVyYXRpb24gPSBNYXRoLmZsb29yKGR1cmF0aW9uIC8gKFVJTlQzMl9NQVggKyAxKSk7XG4gICAgY29uc3QgbG93ZXJXb3JkRHVyYXRpb24gPSBNYXRoLmZsb29yKGR1cmF0aW9uICUgKFVJTlQzMl9NQVggKyAxKSk7XG4gICAgY29uc3QgYnl0ZXMgPSBuZXcgVWludDhBcnJheShbMHgwMSxcbiAgICAvLyB2ZXJzaW9uIDFcbiAgICAweDAwLCAweDAwLCAweDAwLFxuICAgIC8vIGZsYWdzXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMixcbiAgICAvLyBjcmVhdGlvbl90aW1lXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMyxcbiAgICAvLyBtb2RpZmljYXRpb25fdGltZVxuICAgIHRpbWVzY2FsZSA+PiAyNCAmIDB4ZmYsIHRpbWVzY2FsZSA+PiAxNiAmIDB4ZmYsIHRpbWVzY2FsZSA+PiA4ICYgMHhmZiwgdGltZXNjYWxlICYgMHhmZixcbiAgICAvLyB0aW1lc2NhbGVcbiAgICB1cHBlcldvcmREdXJhdGlvbiA+PiAyNCwgdXBwZXJXb3JkRHVyYXRpb24gPj4gMTYgJiAweGZmLCB1cHBlcldvcmREdXJhdGlvbiA+PiA4ICYgMHhmZiwgdXBwZXJXb3JkRHVyYXRpb24gJiAweGZmLCBsb3dlcldvcmREdXJhdGlvbiA+PiAyNCwgbG93ZXJXb3JkRHVyYXRpb24gPj4gMTYgJiAweGZmLCBsb3dlcldvcmREdXJhdGlvbiA+PiA4ICYgMHhmZiwgbG93ZXJXb3JkRHVyYXRpb24gJiAweGZmLCAweDAwLCAweDAxLCAweDAwLCAweDAwLFxuICAgIC8vIDEuMCByYXRlXG4gICAgMHgwMSwgMHgwMCxcbiAgICAvLyAxLjAgdm9sdW1lXG4gICAgMHgwMCwgMHgwMCxcbiAgICAvLyByZXNlcnZlZFxuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsXG4gICAgLy8gcmVzZXJ2ZWRcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLFxuICAgIC8vIHJlc2VydmVkXG4gICAgMHgwMCwgMHgwMSwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMSwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHg0MCwgMHgwMCwgMHgwMCwgMHgwMCxcbiAgICAvLyB0cmFuc2Zvcm1hdGlvbjogdW5pdHkgbWF0cml4XG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCxcbiAgICAvLyBwcmVfZGVmaW5lZFxuICAgIDB4ZmYsIDB4ZmYsIDB4ZmYsIDB4ZmYgLy8gbmV4dF90cmFja19JRFxuICAgIF0pO1xuICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlcy5tdmhkLCBieXRlcyk7XG4gIH1cbiAgc3RhdGljIHNkdHAodHJhY2spIHtcbiAgICBjb25zdCBzYW1wbGVzID0gdHJhY2suc2FtcGxlcyB8fCBbXTtcbiAgICBjb25zdCBieXRlcyA9IG5ldyBVaW50OEFycmF5KDQgKyBzYW1wbGVzLmxlbmd0aCk7XG4gICAgbGV0IGk7XG4gICAgbGV0IGZsYWdzO1xuICAgIC8vIGxlYXZlIHRoZSBmdWxsIGJveCBoZWFkZXIgKDQgYnl0ZXMpIGFsbCB6ZXJvXG4gICAgLy8gd3JpdGUgdGhlIHNhbXBsZSB0YWJsZVxuICAgIGZvciAoaSA9IDA7IGkgPCBzYW1wbGVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBmbGFncyA9IHNhbXBsZXNbaV0uZmxhZ3M7XG4gICAgICBieXRlc1tpICsgNF0gPSBmbGFncy5kZXBlbmRzT24gPDwgNCB8IGZsYWdzLmlzRGVwZW5kZWRPbiA8PCAyIHwgZmxhZ3MuaGFzUmVkdW5kYW5jeTtcbiAgICB9XG4gICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLnNkdHAsIGJ5dGVzKTtcbiAgfVxuICBzdGF0aWMgc3RibCh0cmFjaykge1xuICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlcy5zdGJsLCBNUDQuc3RzZCh0cmFjayksIE1QNC5ib3goTVA0LnR5cGVzLnN0dHMsIE1QNC5TVFRTKSwgTVA0LmJveChNUDQudHlwZXMuc3RzYywgTVA0LlNUU0MpLCBNUDQuYm94KE1QNC50eXBlcy5zdHN6LCBNUDQuU1RTWiksIE1QNC5ib3goTVA0LnR5cGVzLnN0Y28sIE1QNC5TVENPKSk7XG4gIH1cbiAgc3RhdGljIGF2YzEodHJhY2spIHtcbiAgICBsZXQgc3BzID0gW107XG4gICAgbGV0IHBwcyA9IFtdO1xuICAgIGxldCBpO1xuICAgIGxldCBkYXRhO1xuICAgIGxldCBsZW47XG4gICAgLy8gYXNzZW1ibGUgdGhlIFNQU3NcblxuICAgIGZvciAoaSA9IDA7IGkgPCB0cmFjay5zcHMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGRhdGEgPSB0cmFjay5zcHNbaV07XG4gICAgICBsZW4gPSBkYXRhLmJ5dGVMZW5ndGg7XG4gICAgICBzcHMucHVzaChsZW4gPj4+IDggJiAweGZmKTtcbiAgICAgIHNwcy5wdXNoKGxlbiAmIDB4ZmYpO1xuXG4gICAgICAvLyBTUFNcbiAgICAgIHNwcyA9IHNwcy5jb25jYXQoQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoZGF0YSkpO1xuICAgIH1cblxuICAgIC8vIGFzc2VtYmxlIHRoZSBQUFNzXG4gICAgZm9yIChpID0gMDsgaSA8IHRyYWNrLnBwcy5sZW5ndGg7IGkrKykge1xuICAgICAgZGF0YSA9IHRyYWNrLnBwc1tpXTtcbiAgICAgIGxlbiA9IGRhdGEuYnl0ZUxlbmd0aDtcbiAgICAgIHBwcy5wdXNoKGxlbiA+Pj4gOCAmIDB4ZmYpO1xuICAgICAgcHBzLnB1c2gobGVuICYgMHhmZik7XG4gICAgICBwcHMgPSBwcHMuY29uY2F0KEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGRhdGEpKTtcbiAgICB9XG4gICAgY29uc3QgYXZjYyA9IE1QNC5ib3goTVA0LnR5cGVzLmF2Y0MsIG5ldyBVaW50OEFycmF5KFsweDAxLFxuICAgIC8vIHZlcnNpb25cbiAgICBzcHNbM10sXG4gICAgLy8gcHJvZmlsZVxuICAgIHNwc1s0XSxcbiAgICAvLyBwcm9maWxlIGNvbXBhdFxuICAgIHNwc1s1XSxcbiAgICAvLyBsZXZlbFxuICAgIDB4ZmMgfCAzLFxuICAgIC8vIGxlbmd0aFNpemVNaW51c09uZSwgaGFyZC1jb2RlZCB0byA0IGJ5dGVzXG4gICAgMHhlMCB8IHRyYWNrLnNwcy5sZW5ndGggLy8gM2JpdCByZXNlcnZlZCAoMTExKSArIG51bU9mU2VxdWVuY2VQYXJhbWV0ZXJTZXRzXG4gICAgXS5jb25jYXQoc3BzKS5jb25jYXQoW3RyYWNrLnBwcy5sZW5ndGggLy8gbnVtT2ZQaWN0dXJlUGFyYW1ldGVyU2V0c1xuICAgIF0pLmNvbmNhdChwcHMpKSk7IC8vIFwiUFBTXCJcbiAgICBjb25zdCB3aWR0aCA9IHRyYWNrLndpZHRoO1xuICAgIGNvbnN0IGhlaWdodCA9IHRyYWNrLmhlaWdodDtcbiAgICBjb25zdCBoU3BhY2luZyA9IHRyYWNrLnBpeGVsUmF0aW9bMF07XG4gICAgY29uc3QgdlNwYWNpbmcgPSB0cmFjay5waXhlbFJhdGlvWzFdO1xuICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlcy5hdmMxLCBuZXcgVWludDhBcnJheShbMHgwMCwgMHgwMCwgMHgwMCxcbiAgICAvLyByZXNlcnZlZFxuICAgIDB4MDAsIDB4MDAsIDB4MDAsXG4gICAgLy8gcmVzZXJ2ZWRcbiAgICAweDAwLCAweDAxLFxuICAgIC8vIGRhdGFfcmVmZXJlbmNlX2luZGV4XG4gICAgMHgwMCwgMHgwMCxcbiAgICAvLyBwcmVfZGVmaW5lZFxuICAgIDB4MDAsIDB4MDAsXG4gICAgLy8gcmVzZXJ2ZWRcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLCAweDAwLFxuICAgIC8vIHByZV9kZWZpbmVkXG4gICAgd2lkdGggPj4gOCAmIDB4ZmYsIHdpZHRoICYgMHhmZixcbiAgICAvLyB3aWR0aFxuICAgIGhlaWdodCA+PiA4ICYgMHhmZiwgaGVpZ2h0ICYgMHhmZixcbiAgICAvLyBoZWlnaHRcbiAgICAweDAwLCAweDQ4LCAweDAwLCAweDAwLFxuICAgIC8vIGhvcml6cmVzb2x1dGlvblxuICAgIDB4MDAsIDB4NDgsIDB4MDAsIDB4MDAsXG4gICAgLy8gdmVydHJlc29sdXRpb25cbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLFxuICAgIC8vIHJlc2VydmVkXG4gICAgMHgwMCwgMHgwMSxcbiAgICAvLyBmcmFtZV9jb3VudFxuICAgIDB4MTIsIDB4NjQsIDB4NjEsIDB4NjksIDB4NmMsXG4gICAgLy8gZGFpbHltb3Rpb24vaGxzLmpzXG4gICAgMHg3OSwgMHg2ZCwgMHg2ZiwgMHg3NCwgMHg2OSwgMHg2ZiwgMHg2ZSwgMHgyZiwgMHg2OCwgMHg2YywgMHg3MywgMHgyZSwgMHg2YSwgMHg3MywgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCxcbiAgICAvLyBjb21wcmVzc29ybmFtZVxuICAgIDB4MDAsIDB4MTgsXG4gICAgLy8gZGVwdGggPSAyNFxuICAgIDB4MTEsIDB4MTFdKSxcbiAgICAvLyBwcmVfZGVmaW5lZCA9IC0xXG4gICAgYXZjYywgTVA0LmJveChNUDQudHlwZXMuYnRydCwgbmV3IFVpbnQ4QXJyYXkoWzB4MDAsIDB4MWMsIDB4OWMsIDB4ODAsXG4gICAgLy8gYnVmZmVyU2l6ZURCXG4gICAgMHgwMCwgMHgyZCwgMHhjNiwgMHhjMCxcbiAgICAvLyBtYXhCaXRyYXRlXG4gICAgMHgwMCwgMHgyZCwgMHhjNiwgMHhjMF0pKSxcbiAgICAvLyBhdmdCaXRyYXRlXG4gICAgTVA0LmJveChNUDQudHlwZXMucGFzcCwgbmV3IFVpbnQ4QXJyYXkoW2hTcGFjaW5nID4+IDI0LFxuICAgIC8vIGhTcGFjaW5nXG4gICAgaFNwYWNpbmcgPj4gMTYgJiAweGZmLCBoU3BhY2luZyA+PiA4ICYgMHhmZiwgaFNwYWNpbmcgJiAweGZmLCB2U3BhY2luZyA+PiAyNCxcbiAgICAvLyB2U3BhY2luZ1xuICAgIHZTcGFjaW5nID4+IDE2ICYgMHhmZiwgdlNwYWNpbmcgPj4gOCAmIDB4ZmYsIHZTcGFjaW5nICYgMHhmZl0pKSk7XG4gIH1cbiAgc3RhdGljIGVzZHModHJhY2spIHtcbiAgICBjb25zdCBjb25maWdsZW4gPSB0cmFjay5jb25maWcubGVuZ3RoO1xuICAgIHJldHVybiBuZXcgVWludDhBcnJheShbMHgwMCxcbiAgICAvLyB2ZXJzaW9uIDBcbiAgICAweDAwLCAweDAwLCAweDAwLFxuICAgIC8vIGZsYWdzXG5cbiAgICAweDAzLFxuICAgIC8vIGRlc2NyaXB0b3JfdHlwZVxuICAgIDB4MTcgKyBjb25maWdsZW4sXG4gICAgLy8gbGVuZ3RoXG4gICAgMHgwMCwgMHgwMSxcbiAgICAvLyBlc19pZFxuICAgIDB4MDAsXG4gICAgLy8gc3RyZWFtX3ByaW9yaXR5XG5cbiAgICAweDA0LFxuICAgIC8vIGRlc2NyaXB0b3JfdHlwZVxuICAgIDB4MGYgKyBjb25maWdsZW4sXG4gICAgLy8gbGVuZ3RoXG4gICAgMHg0MCxcbiAgICAvLyBjb2RlYyA6IG1wZWc0X2F1ZGlvXG4gICAgMHgxNSxcbiAgICAvLyBzdHJlYW1fdHlwZVxuICAgIDB4MDAsIDB4MDAsIDB4MDAsXG4gICAgLy8gYnVmZmVyX3NpemVcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLFxuICAgIC8vIG1heEJpdHJhdGVcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLFxuICAgIC8vIGF2Z0JpdHJhdGVcblxuICAgIDB4MDUgLy8gZGVzY3JpcHRvcl90eXBlXG4gICAgXS5jb25jYXQoW2NvbmZpZ2xlbl0pLmNvbmNhdCh0cmFjay5jb25maWcpLmNvbmNhdChbMHgwNiwgMHgwMSwgMHgwMl0pKTsgLy8gR0FTcGVjaWZpY0NvbmZpZykpOyAvLyBsZW5ndGggKyBhdWRpbyBjb25maWcgZGVzY3JpcHRvclxuICB9XG4gIHN0YXRpYyBhdWRpb1N0c2QodHJhY2spIHtcbiAgICBjb25zdCBzYW1wbGVyYXRlID0gdHJhY2suc2FtcGxlcmF0ZTtcbiAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkoWzB4MDAsIDB4MDAsIDB4MDAsXG4gICAgLy8gcmVzZXJ2ZWRcbiAgICAweDAwLCAweDAwLCAweDAwLFxuICAgIC8vIHJlc2VydmVkXG4gICAgMHgwMCwgMHgwMSxcbiAgICAvLyBkYXRhX3JlZmVyZW5jZV9pbmRleFxuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsXG4gICAgLy8gcmVzZXJ2ZWRcbiAgICAweDAwLCB0cmFjay5jaGFubmVsQ291bnQsXG4gICAgLy8gY2hhbm5lbGNvdW50XG4gICAgMHgwMCwgMHgxMCxcbiAgICAvLyBzYW1wbGVTaXplOjE2Yml0c1xuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsXG4gICAgLy8gcmVzZXJ2ZWQyXG4gICAgc2FtcGxlcmF0ZSA+PiA4ICYgMHhmZiwgc2FtcGxlcmF0ZSAmIDB4ZmYsXG4gICAgLy9cbiAgICAweDAwLCAweDAwXSk7XG4gIH1cbiAgc3RhdGljIG1wNGEodHJhY2spIHtcbiAgICByZXR1cm4gTVA0LmJveChNUDQudHlwZXMubXA0YSwgTVA0LmF1ZGlvU3RzZCh0cmFjayksIE1QNC5ib3goTVA0LnR5cGVzLmVzZHMsIE1QNC5lc2RzKHRyYWNrKSkpO1xuICB9XG4gIHN0YXRpYyBtcDModHJhY2spIHtcbiAgICByZXR1cm4gTVA0LmJveChNUDQudHlwZXNbJy5tcDMnXSwgTVA0LmF1ZGlvU3RzZCh0cmFjaykpO1xuICB9XG4gIHN0YXRpYyBhYzModHJhY2spIHtcbiAgICByZXR1cm4gTVA0LmJveChNUDQudHlwZXNbJ2FjLTMnXSwgTVA0LmF1ZGlvU3RzZCh0cmFjayksIE1QNC5ib3goTVA0LnR5cGVzLmRhYzMsIHRyYWNrLmNvbmZpZykpO1xuICB9XG4gIHN0YXRpYyBzdHNkKHRyYWNrKSB7XG4gICAgaWYgKHRyYWNrLnR5cGUgPT09ICdhdWRpbycpIHtcbiAgICAgIGlmICh0cmFjay5zZWdtZW50Q29kZWMgPT09ICdtcDMnICYmIHRyYWNrLmNvZGVjID09PSAnbXAzJykge1xuICAgICAgICByZXR1cm4gTVA0LmJveChNUDQudHlwZXMuc3RzZCwgTVA0LlNUU0QsIE1QNC5tcDModHJhY2spKTtcbiAgICAgIH1cbiAgICAgIGlmICh0cmFjay5zZWdtZW50Q29kZWMgPT09ICdhYzMnKSB7XG4gICAgICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlcy5zdHNkLCBNUDQuU1RTRCwgTVA0LmFjMyh0cmFjaykpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIE1QNC5ib3goTVA0LnR5cGVzLnN0c2QsIE1QNC5TVFNELCBNUDQubXA0YSh0cmFjaykpO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gTVA0LmJveChNUDQudHlwZXMuc3RzZCwgTVA0LlNUU0QsIE1QNC5hdmMxKHRyYWNrKSk7XG4gICAgfVxuICB9XG4gIHN0YXRpYyB0a2hkKHRyYWNrKSB7XG4gICAgY29uc3QgaWQgPSB0cmFjay5pZDtcbiAgICBjb25zdCBkdXJhdGlvbiA9IHRyYWNrLmR1cmF0aW9uICogdHJhY2sudGltZXNjYWxlO1xuICAgIGNvbnN0IHdpZHRoID0gdHJhY2sud2lkdGg7XG4gICAgY29uc3QgaGVpZ2h0ID0gdHJhY2suaGVpZ2h0O1xuICAgIGNvbnN0IHVwcGVyV29yZER1cmF0aW9uID0gTWF0aC5mbG9vcihkdXJhdGlvbiAvIChVSU5UMzJfTUFYICsgMSkpO1xuICAgIGNvbnN0IGxvd2VyV29yZER1cmF0aW9uID0gTWF0aC5mbG9vcihkdXJhdGlvbiAlIChVSU5UMzJfTUFYICsgMSkpO1xuICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlcy50a2hkLCBuZXcgVWludDhBcnJheShbMHgwMSxcbiAgICAvLyB2ZXJzaW9uIDFcbiAgICAweDAwLCAweDAwLCAweDA3LFxuICAgIC8vIGZsYWdzXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMixcbiAgICAvLyBjcmVhdGlvbl90aW1lXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMyxcbiAgICAvLyBtb2RpZmljYXRpb25fdGltZVxuICAgIGlkID4+IDI0ICYgMHhmZiwgaWQgPj4gMTYgJiAweGZmLCBpZCA+PiA4ICYgMHhmZiwgaWQgJiAweGZmLFxuICAgIC8vIHRyYWNrX0lEXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCxcbiAgICAvLyByZXNlcnZlZFxuICAgIHVwcGVyV29yZER1cmF0aW9uID4+IDI0LCB1cHBlcldvcmREdXJhdGlvbiA+PiAxNiAmIDB4ZmYsIHVwcGVyV29yZER1cmF0aW9uID4+IDggJiAweGZmLCB1cHBlcldvcmREdXJhdGlvbiAmIDB4ZmYsIGxvd2VyV29yZER1cmF0aW9uID4+IDI0LCBsb3dlcldvcmREdXJhdGlvbiA+PiAxNiAmIDB4ZmYsIGxvd2VyV29yZER1cmF0aW9uID4+IDggJiAweGZmLCBsb3dlcldvcmREdXJhdGlvbiAmIDB4ZmYsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsXG4gICAgLy8gcmVzZXJ2ZWRcbiAgICAweDAwLCAweDAwLFxuICAgIC8vIGxheWVyXG4gICAgMHgwMCwgMHgwMCxcbiAgICAvLyBhbHRlcm5hdGVfZ3JvdXBcbiAgICAweDAwLCAweDAwLFxuICAgIC8vIG5vbi1hdWRpbyB0cmFjayB2b2x1bWVcbiAgICAweDAwLCAweDAwLFxuICAgIC8vIHJlc2VydmVkXG4gICAgMHgwMCwgMHgwMSwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMSwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCwgMHg0MCwgMHgwMCwgMHgwMCwgMHgwMCxcbiAgICAvLyB0cmFuc2Zvcm1hdGlvbjogdW5pdHkgbWF0cml4XG4gICAgd2lkdGggPj4gOCAmIDB4ZmYsIHdpZHRoICYgMHhmZiwgMHgwMCwgMHgwMCxcbiAgICAvLyB3aWR0aFxuICAgIGhlaWdodCA+PiA4ICYgMHhmZiwgaGVpZ2h0ICYgMHhmZiwgMHgwMCwgMHgwMCAvLyBoZWlnaHRcbiAgICBdKSk7XG4gIH1cbiAgc3RhdGljIHRyYWYodHJhY2ssIGJhc2VNZWRpYURlY29kZVRpbWUpIHtcbiAgICBjb25zdCBzYW1wbGVEZXBlbmRlbmN5VGFibGUgPSBNUDQuc2R0cCh0cmFjayk7XG4gICAgY29uc3QgaWQgPSB0cmFjay5pZDtcbiAgICBjb25zdCB1cHBlcldvcmRCYXNlTWVkaWFEZWNvZGVUaW1lID0gTWF0aC5mbG9vcihiYXNlTWVkaWFEZWNvZGVUaW1lIC8gKFVJTlQzMl9NQVggKyAxKSk7XG4gICAgY29uc3QgbG93ZXJXb3JkQmFzZU1lZGlhRGVjb2RlVGltZSA9IE1hdGguZmxvb3IoYmFzZU1lZGlhRGVjb2RlVGltZSAlIChVSU5UMzJfTUFYICsgMSkpO1xuICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlcy50cmFmLCBNUDQuYm94KE1QNC50eXBlcy50ZmhkLCBuZXcgVWludDhBcnJheShbMHgwMCxcbiAgICAvLyB2ZXJzaW9uIDBcbiAgICAweDAwLCAweDAwLCAweDAwLFxuICAgIC8vIGZsYWdzXG4gICAgaWQgPj4gMjQsIGlkID4+IDE2ICYgMHhmZiwgaWQgPj4gOCAmIDB4ZmYsIGlkICYgMHhmZiAvLyB0cmFja19JRFxuICAgIF0pKSwgTVA0LmJveChNUDQudHlwZXMudGZkdCwgbmV3IFVpbnQ4QXJyYXkoWzB4MDEsXG4gICAgLy8gdmVyc2lvbiAxXG4gICAgMHgwMCwgMHgwMCwgMHgwMCxcbiAgICAvLyBmbGFnc1xuICAgIHVwcGVyV29yZEJhc2VNZWRpYURlY29kZVRpbWUgPj4gMjQsIHVwcGVyV29yZEJhc2VNZWRpYURlY29kZVRpbWUgPj4gMTYgJiAweGZmLCB1cHBlcldvcmRCYXNlTWVkaWFEZWNvZGVUaW1lID4+IDggJiAweGZmLCB1cHBlcldvcmRCYXNlTWVkaWFEZWNvZGVUaW1lICYgMHhmZiwgbG93ZXJXb3JkQmFzZU1lZGlhRGVjb2RlVGltZSA+PiAyNCwgbG93ZXJXb3JkQmFzZU1lZGlhRGVjb2RlVGltZSA+PiAxNiAmIDB4ZmYsIGxvd2VyV29yZEJhc2VNZWRpYURlY29kZVRpbWUgPj4gOCAmIDB4ZmYsIGxvd2VyV29yZEJhc2VNZWRpYURlY29kZVRpbWUgJiAweGZmXSkpLCBNUDQudHJ1bih0cmFjaywgc2FtcGxlRGVwZW5kZW5jeVRhYmxlLmxlbmd0aCArIDE2ICtcbiAgICAvLyB0ZmhkXG4gICAgMjAgK1xuICAgIC8vIHRmZHRcbiAgICA4ICtcbiAgICAvLyB0cmFmIGhlYWRlclxuICAgIDE2ICtcbiAgICAvLyBtZmhkXG4gICAgOCArXG4gICAgLy8gbW9vZiBoZWFkZXJcbiAgICA4KSxcbiAgICAvLyBtZGF0IGhlYWRlclxuICAgIHNhbXBsZURlcGVuZGVuY3lUYWJsZSk7XG4gIH1cblxuICAvKipcbiAgICogR2VuZXJhdGUgYSB0cmFjayBib3guXG4gICAqIEBwYXJhbSB0cmFjayBhIHRyYWNrIGRlZmluaXRpb25cbiAgICovXG4gIHN0YXRpYyB0cmFrKHRyYWNrKSB7XG4gICAgdHJhY2suZHVyYXRpb24gPSB0cmFjay5kdXJhdGlvbiB8fCAweGZmZmZmZmZmO1xuICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlcy50cmFrLCBNUDQudGtoZCh0cmFjayksIE1QNC5tZGlhKHRyYWNrKSk7XG4gIH1cbiAgc3RhdGljIHRyZXgodHJhY2spIHtcbiAgICBjb25zdCBpZCA9IHRyYWNrLmlkO1xuICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlcy50cmV4LCBuZXcgVWludDhBcnJheShbMHgwMCxcbiAgICAvLyB2ZXJzaW9uIDBcbiAgICAweDAwLCAweDAwLCAweDAwLFxuICAgIC8vIGZsYWdzXG4gICAgaWQgPj4gMjQsIGlkID4+IDE2ICYgMHhmZiwgaWQgPj4gOCAmIDB4ZmYsIGlkICYgMHhmZixcbiAgICAvLyB0cmFja19JRFxuICAgIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDEsXG4gICAgLy8gZGVmYXVsdF9zYW1wbGVfZGVzY3JpcHRpb25faW5kZXhcbiAgICAweDAwLCAweDAwLCAweDAwLCAweDAwLFxuICAgIC8vIGRlZmF1bHRfc2FtcGxlX2R1cmF0aW9uXG4gICAgMHgwMCwgMHgwMCwgMHgwMCwgMHgwMCxcbiAgICAvLyBkZWZhdWx0X3NhbXBsZV9zaXplXG4gICAgMHgwMCwgMHgwMSwgMHgwMCwgMHgwMSAvLyBkZWZhdWx0X3NhbXBsZV9mbGFnc1xuICAgIF0pKTtcbiAgfVxuICBzdGF0aWMgdHJ1bih0cmFjaywgb2Zmc2V0KSB7XG4gICAgY29uc3Qgc2FtcGxlcyA9IHRyYWNrLnNhbXBsZXMgfHwgW107XG4gICAgY29uc3QgbGVuID0gc2FtcGxlcy5sZW5ndGg7XG4gICAgY29uc3QgYXJyYXlsZW4gPSAxMiArIDE2ICogbGVuO1xuICAgIGNvbnN0IGFycmF5ID0gbmV3IFVpbnQ4QXJyYXkoYXJyYXlsZW4pO1xuICAgIGxldCBpO1xuICAgIGxldCBzYW1wbGU7XG4gICAgbGV0IGR1cmF0aW9uO1xuICAgIGxldCBzaXplO1xuICAgIGxldCBmbGFncztcbiAgICBsZXQgY3RzO1xuICAgIG9mZnNldCArPSA4ICsgYXJyYXlsZW47XG4gICAgYXJyYXkuc2V0KFt0cmFjay50eXBlID09PSAndmlkZW8nID8gMHgwMSA6IDB4MDAsXG4gICAgLy8gdmVyc2lvbiAxIGZvciB2aWRlbyB3aXRoIHNpZ25lZC1pbnQgc2FtcGxlX2NvbXBvc2l0aW9uX3RpbWVfb2Zmc2V0XG4gICAgMHgwMCwgMHgwZiwgMHgwMSxcbiAgICAvLyBmbGFnc1xuICAgIGxlbiA+Pj4gMjQgJiAweGZmLCBsZW4gPj4+IDE2ICYgMHhmZiwgbGVuID4+PiA4ICYgMHhmZiwgbGVuICYgMHhmZixcbiAgICAvLyBzYW1wbGVfY291bnRcbiAgICBvZmZzZXQgPj4+IDI0ICYgMHhmZiwgb2Zmc2V0ID4+PiAxNiAmIDB4ZmYsIG9mZnNldCA+Pj4gOCAmIDB4ZmYsIG9mZnNldCAmIDB4ZmYgLy8gZGF0YV9vZmZzZXRcbiAgICBdLCAwKTtcbiAgICBmb3IgKGkgPSAwOyBpIDwgbGVuOyBpKyspIHtcbiAgICAgIHNhbXBsZSA9IHNhbXBsZXNbaV07XG4gICAgICBkdXJhdGlvbiA9IHNhbXBsZS5kdXJhdGlvbjtcbiAgICAgIHNpemUgPSBzYW1wbGUuc2l6ZTtcbiAgICAgIGZsYWdzID0gc2FtcGxlLmZsYWdzO1xuICAgICAgY3RzID0gc2FtcGxlLmN0cztcbiAgICAgIGFycmF5LnNldChbZHVyYXRpb24gPj4+IDI0ICYgMHhmZiwgZHVyYXRpb24gPj4+IDE2ICYgMHhmZiwgZHVyYXRpb24gPj4+IDggJiAweGZmLCBkdXJhdGlvbiAmIDB4ZmYsXG4gICAgICAvLyBzYW1wbGVfZHVyYXRpb25cbiAgICAgIHNpemUgPj4+IDI0ICYgMHhmZiwgc2l6ZSA+Pj4gMTYgJiAweGZmLCBzaXplID4+PiA4ICYgMHhmZiwgc2l6ZSAmIDB4ZmYsXG4gICAgICAvLyBzYW1wbGVfc2l6ZVxuICAgICAgZmxhZ3MuaXNMZWFkaW5nIDw8IDIgfCBmbGFncy5kZXBlbmRzT24sIGZsYWdzLmlzRGVwZW5kZWRPbiA8PCA2IHwgZmxhZ3MuaGFzUmVkdW5kYW5jeSA8PCA0IHwgZmxhZ3MucGFkZGluZ1ZhbHVlIDw8IDEgfCBmbGFncy5pc05vblN5bmMsIGZsYWdzLmRlZ3JhZFByaW8gJiAweGYwIDw8IDgsIGZsYWdzLmRlZ3JhZFByaW8gJiAweDBmLFxuICAgICAgLy8gc2FtcGxlX2ZsYWdzXG4gICAgICBjdHMgPj4+IDI0ICYgMHhmZiwgY3RzID4+PiAxNiAmIDB4ZmYsIGN0cyA+Pj4gOCAmIDB4ZmYsIGN0cyAmIDB4ZmYgLy8gc2FtcGxlX2NvbXBvc2l0aW9uX3RpbWVfb2Zmc2V0XG4gICAgICBdLCAxMiArIDE2ICogaSk7XG4gICAgfVxuICAgIHJldHVybiBNUDQuYm94KE1QNC50eXBlcy50cnVuLCBhcnJheSk7XG4gIH1cbiAgc3RhdGljIGluaXRTZWdtZW50KHRyYWNrcykge1xuICAgIGlmICghTVA0LnR5cGVzKSB7XG4gICAgICBNUDQuaW5pdCgpO1xuICAgIH1cbiAgICBjb25zdCBtb3ZpZSA9IE1QNC5tb292KHRyYWNrcyk7XG4gICAgY29uc3QgcmVzdWx0ID0gYXBwZW5kVWludDhBcnJheShNUDQuRlRZUCwgbW92aWUpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbn1cbk1QNC50eXBlcyA9IHZvaWQgMDtcbk1QNC5IRExSX1RZUEVTID0gdm9pZCAwO1xuTVA0LlNUVFMgPSB2b2lkIDA7XG5NUDQuU1RTQyA9IHZvaWQgMDtcbk1QNC5TVENPID0gdm9pZCAwO1xuTVA0LlNUU1ogPSB2b2lkIDA7XG5NUDQuVk1IRCA9IHZvaWQgMDtcbk1QNC5TTUhEID0gdm9pZCAwO1xuTVA0LlNUU0QgPSB2b2lkIDA7XG5NUDQuRlRZUCA9IHZvaWQgMDtcbk1QNC5ESU5GID0gdm9pZCAwO1xuXG5jb25zdCBNUEVHX1RTX0NMT0NLX0ZSRVFfSFogPSA5MDAwMDtcbmZ1bmN0aW9uIHRvVGltZXNjYWxlRnJvbUJhc2UoYmFzZVRpbWUsIGRlc3RTY2FsZSwgc3JjQmFzZSA9IDEsIHJvdW5kID0gZmFsc2UpIHtcbiAgY29uc3QgcmVzdWx0ID0gYmFzZVRpbWUgKiBkZXN0U2NhbGUgKiBzcmNCYXNlOyAvLyBlcXVpdmFsZW50IHRvIGAodmFsdWUgKiBzY2FsZSkgLyAoMSAvIGJhc2UpYFxuICByZXR1cm4gcm91bmQgPyBNYXRoLnJvdW5kKHJlc3VsdCkgOiByZXN1bHQ7XG59XG5mdW5jdGlvbiB0b1RpbWVzY2FsZUZyb21TY2FsZShiYXNlVGltZSwgZGVzdFNjYWxlLCBzcmNTY2FsZSA9IDEsIHJvdW5kID0gZmFsc2UpIHtcbiAgcmV0dXJuIHRvVGltZXNjYWxlRnJvbUJhc2UoYmFzZVRpbWUsIGRlc3RTY2FsZSwgMSAvIHNyY1NjYWxlLCByb3VuZCk7XG59XG5mdW5jdGlvbiB0b01zRnJvbU1wZWdUc0Nsb2NrKGJhc2VUaW1lLCByb3VuZCA9IGZhbHNlKSB7XG4gIHJldHVybiB0b1RpbWVzY2FsZUZyb21CYXNlKGJhc2VUaW1lLCAxMDAwLCAxIC8gTVBFR19UU19DTE9DS19GUkVRX0haLCByb3VuZCk7XG59XG5mdW5jdGlvbiB0b01wZWdUc0Nsb2NrRnJvbVRpbWVzY2FsZShiYXNlVGltZSwgc3JjU2NhbGUgPSAxKSB7XG4gIHJldHVybiB0b1RpbWVzY2FsZUZyb21CYXNlKGJhc2VUaW1lLCBNUEVHX1RTX0NMT0NLX0ZSRVFfSFosIDEgLyBzcmNTY2FsZSk7XG59XG5cbmNvbnN0IE1BWF9TSUxFTlRfRlJBTUVfRFVSQVRJT04gPSAxMCAqIDEwMDA7IC8vIDEwIHNlY29uZHNcbmNvbnN0IEFBQ19TQU1QTEVTX1BFUl9GUkFNRSA9IDEwMjQ7XG5jb25zdCBNUEVHX0FVRElPX1NBTVBMRV9QRVJfRlJBTUUgPSAxMTUyO1xuY29uc3QgQUMzX1NBTVBMRVNfUEVSX0ZSQU1FID0gMTUzNjtcbmxldCBjaHJvbWVWZXJzaW9uID0gbnVsbDtcbmxldCBzYWZhcmlXZWJraXRWZXJzaW9uID0gbnVsbDtcbmNsYXNzIE1QNFJlbXV4ZXIge1xuICBjb25zdHJ1Y3RvcihvYnNlcnZlciwgY29uZmlnLCB0eXBlU3VwcG9ydGVkLCB2ZW5kb3IgPSAnJykge1xuICAgIHRoaXMub2JzZXJ2ZXIgPSB2b2lkIDA7XG4gICAgdGhpcy5jb25maWcgPSB2b2lkIDA7XG4gICAgdGhpcy50eXBlU3VwcG9ydGVkID0gdm9pZCAwO1xuICAgIHRoaXMuSVNHZW5lcmF0ZWQgPSBmYWxzZTtcbiAgICB0aGlzLl9pbml0UFRTID0gbnVsbDtcbiAgICB0aGlzLl9pbml0RFRTID0gbnVsbDtcbiAgICB0aGlzLm5leHRBdmNEdHMgPSBudWxsO1xuICAgIHRoaXMubmV4dEF1ZGlvUHRzID0gbnVsbDtcbiAgICB0aGlzLnZpZGVvU2FtcGxlRHVyYXRpb24gPSBudWxsO1xuICAgIHRoaXMuaXNBdWRpb0NvbnRpZ3VvdXMgPSBmYWxzZTtcbiAgICB0aGlzLmlzVmlkZW9Db250aWd1b3VzID0gZmFsc2U7XG4gICAgdGhpcy52aWRlb1RyYWNrQ29uZmlnID0gdm9pZCAwO1xuICAgIHRoaXMub2JzZXJ2ZXIgPSBvYnNlcnZlcjtcbiAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZztcbiAgICB0aGlzLnR5cGVTdXBwb3J0ZWQgPSB0eXBlU3VwcG9ydGVkO1xuICAgIHRoaXMuSVNHZW5lcmF0ZWQgPSBmYWxzZTtcbiAgICBpZiAoY2hyb21lVmVyc2lvbiA9PT0gbnVsbCkge1xuICAgICAgY29uc3QgdXNlckFnZW50ID0gbmF2aWdhdG9yLnVzZXJBZ2VudCB8fCAnJztcbiAgICAgIGNvbnN0IHJlc3VsdCA9IHVzZXJBZ2VudC5tYXRjaCgvQ2hyb21lXFwvKFxcZCspL2kpO1xuICAgICAgY2hyb21lVmVyc2lvbiA9IHJlc3VsdCA/IHBhcnNlSW50KHJlc3VsdFsxXSkgOiAwO1xuICAgIH1cbiAgICBpZiAoc2FmYXJpV2Via2l0VmVyc2lvbiA9PT0gbnVsbCkge1xuICAgICAgY29uc3QgcmVzdWx0ID0gbmF2aWdhdG9yLnVzZXJBZ2VudC5tYXRjaCgvU2FmYXJpXFwvKFxcZCspL2kpO1xuICAgICAgc2FmYXJpV2Via2l0VmVyc2lvbiA9IHJlc3VsdCA/IHBhcnNlSW50KHJlc3VsdFsxXSkgOiAwO1xuICAgIH1cbiAgfVxuICBkZXN0cm95KCkge1xuICAgIC8vIEB0cy1pZ25vcmVcbiAgICB0aGlzLmNvbmZpZyA9IHRoaXMudmlkZW9UcmFja0NvbmZpZyA9IHRoaXMuX2luaXRQVFMgPSB0aGlzLl9pbml0RFRTID0gbnVsbDtcbiAgfVxuICByZXNldFRpbWVTdGFtcChkZWZhdWx0VGltZVN0YW1wKSB7XG4gICAgbG9nZ2VyLmxvZygnW21wNC1yZW11eGVyXTogaW5pdFBUUyAmIGluaXREVFMgcmVzZXQnKTtcbiAgICB0aGlzLl9pbml0UFRTID0gdGhpcy5faW5pdERUUyA9IGRlZmF1bHRUaW1lU3RhbXA7XG4gIH1cbiAgcmVzZXROZXh0VGltZXN0YW1wKCkge1xuICAgIGxvZ2dlci5sb2coJ1ttcDQtcmVtdXhlcl06IHJlc2V0IG5leHQgdGltZXN0YW1wJyk7XG4gICAgdGhpcy5pc1ZpZGVvQ29udGlndW91cyA9IGZhbHNlO1xuICAgIHRoaXMuaXNBdWRpb0NvbnRpZ3VvdXMgPSBmYWxzZTtcbiAgfVxuICByZXNldEluaXRTZWdtZW50KCkge1xuICAgIGxvZ2dlci5sb2coJ1ttcDQtcmVtdXhlcl06IElTR2VuZXJhdGVkIGZsYWcgcmVzZXQnKTtcbiAgICB0aGlzLklTR2VuZXJhdGVkID0gZmFsc2U7XG4gICAgdGhpcy52aWRlb1RyYWNrQ29uZmlnID0gdW5kZWZpbmVkO1xuICB9XG4gIGdldFZpZGVvU3RhcnRQdHModmlkZW9TYW1wbGVzKSB7XG4gICAgbGV0IHJvbGxvdmVyRGV0ZWN0ZWQgPSBmYWxzZTtcbiAgICBjb25zdCBzdGFydFBUUyA9IHZpZGVvU2FtcGxlcy5yZWR1Y2UoKG1pblBUUywgc2FtcGxlKSA9PiB7XG4gICAgICBjb25zdCBkZWx0YSA9IHNhbXBsZS5wdHMgLSBtaW5QVFM7XG4gICAgICBpZiAoZGVsdGEgPCAtNDI5NDk2NzI5Nikge1xuICAgICAgICAvLyAyXjMyLCBzZWUgUFRTTm9ybWFsaXplIGZvciByZWFzb25pbmcsIGJ1dCB3ZSdyZSBoaXR0aW5nIGEgcm9sbG92ZXIgaGVyZSwgYW5kIHdlIGRvbid0IHdhbnQgdGhhdCB0byBpbXBhY3QgdGhlIHRpbWVPZmZzZXQgY2FsY3VsYXRpb25cbiAgICAgICAgcm9sbG92ZXJEZXRlY3RlZCA9IHRydWU7XG4gICAgICAgIHJldHVybiBub3JtYWxpemVQdHMobWluUFRTLCBzYW1wbGUucHRzKTtcbiAgICAgIH0gZWxzZSBpZiAoZGVsdGEgPiAwKSB7XG4gICAgICAgIHJldHVybiBtaW5QVFM7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gc2FtcGxlLnB0cztcbiAgICAgIH1cbiAgICB9LCB2aWRlb1NhbXBsZXNbMF0ucHRzKTtcbiAgICBpZiAocm9sbG92ZXJEZXRlY3RlZCkge1xuICAgICAgbG9nZ2VyLmRlYnVnKCdQVFMgcm9sbG92ZXIgZGV0ZWN0ZWQnKTtcbiAgICB9XG4gICAgcmV0dXJuIHN0YXJ0UFRTO1xuICB9XG4gIHJlbXV4KGF1ZGlvVHJhY2ssIHZpZGVvVHJhY2ssIGlkM1RyYWNrLCB0ZXh0VHJhY2ssIHRpbWVPZmZzZXQsIGFjY3VyYXRlVGltZU9mZnNldCwgZmx1c2gsIHBsYXlsaXN0VHlwZSkge1xuICAgIGxldCB2aWRlbztcbiAgICBsZXQgYXVkaW87XG4gICAgbGV0IGluaXRTZWdtZW50O1xuICAgIGxldCB0ZXh0O1xuICAgIGxldCBpZDM7XG4gICAgbGV0IGluZGVwZW5kZW50O1xuICAgIGxldCBhdWRpb1RpbWVPZmZzZXQgPSB0aW1lT2Zmc2V0O1xuICAgIGxldCB2aWRlb1RpbWVPZmZzZXQgPSB0aW1lT2Zmc2V0O1xuXG4gICAgLy8gSWYgd2UncmUgcmVtdXhpbmcgYXVkaW8gYW5kIHZpZGVvIHByb2dyZXNzaXZlbHksIHdhaXQgdW50aWwgd2UndmUgcmVjZWl2ZWQgZW5vdWdoIHNhbXBsZXMgZm9yIGVhY2ggdHJhY2sgYmVmb3JlIHByb2NlZWRpbmcuXG4gICAgLy8gVGhpcyBpcyBkb25lIHRvIHN5bmNocm9uaXplIHRoZSBhdWRpbyBhbmQgdmlkZW8gc3RyZWFtcy4gV2Uga25vdyBpZiB0aGUgY3VycmVudCBzZWdtZW50IHdpbGwgaGF2ZSBzYW1wbGVzIGlmIHRoZSBcInBpZFwiXG4gICAgLy8gcGFyYW1ldGVyIGlzIGdyZWF0ZXIgdGhhbiAtMS4gVGhlIHBpZCBpcyBzZXQgd2hlbiB0aGUgUE1UIGlzIHBhcnNlZCwgd2hpY2ggY29udGFpbnMgdGhlIHRyYWNrcyBsaXN0LlxuICAgIC8vIEhvd2V2ZXIsIGlmIHRoZSBpbml0U2VnbWVudCBoYXMgYWxyZWFkeSBiZWVuIGdlbmVyYXRlZCwgb3Igd2UndmUgcmVhY2hlZCB0aGUgZW5kIG9mIGEgc2VnbWVudCAoZmx1c2gpLFxuICAgIC8vIHRoZW4gd2UgY2FuIHJlbXV4IG9uZSB0cmFjayB3aXRob3V0IHdhaXRpbmcgZm9yIHRoZSBvdGhlci5cbiAgICBjb25zdCBoYXNBdWRpbyA9IGF1ZGlvVHJhY2sucGlkID4gLTE7XG4gICAgY29uc3QgaGFzVmlkZW8gPSB2aWRlb1RyYWNrLnBpZCA+IC0xO1xuICAgIGNvbnN0IGxlbmd0aCA9IHZpZGVvVHJhY2suc2FtcGxlcy5sZW5ndGg7XG4gICAgY29uc3QgZW5vdWdoQXVkaW9TYW1wbGVzID0gYXVkaW9UcmFjay5zYW1wbGVzLmxlbmd0aCA+IDA7XG4gICAgY29uc3QgZW5vdWdoVmlkZW9TYW1wbGVzID0gZmx1c2ggJiYgbGVuZ3RoID4gMCB8fCBsZW5ndGggPiAxO1xuICAgIGNvbnN0IGNhblJlbXV4QXZjID0gKCFoYXNBdWRpbyB8fCBlbm91Z2hBdWRpb1NhbXBsZXMpICYmICghaGFzVmlkZW8gfHwgZW5vdWdoVmlkZW9TYW1wbGVzKSB8fCB0aGlzLklTR2VuZXJhdGVkIHx8IGZsdXNoO1xuICAgIGlmIChjYW5SZW11eEF2Yykge1xuICAgICAgaWYgKHRoaXMuSVNHZW5lcmF0ZWQpIHtcbiAgICAgICAgdmFyIF92aWRlb1RyYWNrJHBpeGVsUmF0aSwgX2NvbmZpZyRwaXhlbFJhdGlvLCBfdmlkZW9UcmFjayRwaXhlbFJhdGkyLCBfY29uZmlnJHBpeGVsUmF0aW8yO1xuICAgICAgICBjb25zdCBjb25maWcgPSB0aGlzLnZpZGVvVHJhY2tDb25maWc7XG4gICAgICAgIGlmIChjb25maWcgJiYgKHZpZGVvVHJhY2sud2lkdGggIT09IGNvbmZpZy53aWR0aCB8fCB2aWRlb1RyYWNrLmhlaWdodCAhPT0gY29uZmlnLmhlaWdodCB8fCAoKF92aWRlb1RyYWNrJHBpeGVsUmF0aSA9IHZpZGVvVHJhY2sucGl4ZWxSYXRpbykgPT0gbnVsbCA/IHZvaWQgMCA6IF92aWRlb1RyYWNrJHBpeGVsUmF0aVswXSkgIT09ICgoX2NvbmZpZyRwaXhlbFJhdGlvID0gY29uZmlnLnBpeGVsUmF0aW8pID09IG51bGwgPyB2b2lkIDAgOiBfY29uZmlnJHBpeGVsUmF0aW9bMF0pIHx8ICgoX3ZpZGVvVHJhY2skcGl4ZWxSYXRpMiA9IHZpZGVvVHJhY2sucGl4ZWxSYXRpbykgPT0gbnVsbCA/IHZvaWQgMCA6IF92aWRlb1RyYWNrJHBpeGVsUmF0aTJbMV0pICE9PSAoKF9jb25maWckcGl4ZWxSYXRpbzIgPSBjb25maWcucGl4ZWxSYXRpbykgPT0gbnVsbCA/IHZvaWQgMCA6IF9jb25maWckcGl4ZWxSYXRpbzJbMV0pKSkge1xuICAgICAgICAgIHRoaXMucmVzZXRJbml0U2VnbWVudCgpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpbml0U2VnbWVudCA9IHRoaXMuZ2VuZXJhdGVJUyhhdWRpb1RyYWNrLCB2aWRlb1RyYWNrLCB0aW1lT2Zmc2V0LCBhY2N1cmF0ZVRpbWVPZmZzZXQpO1xuICAgICAgfVxuICAgICAgY29uc3QgaXNWaWRlb0NvbnRpZ3VvdXMgPSB0aGlzLmlzVmlkZW9Db250aWd1b3VzO1xuICAgICAgbGV0IGZpcnN0S2V5RnJhbWVJbmRleCA9IC0xO1xuICAgICAgbGV0IGZpcnN0S2V5RnJhbWVQVFM7XG4gICAgICBpZiAoZW5vdWdoVmlkZW9TYW1wbGVzKSB7XG4gICAgICAgIGZpcnN0S2V5RnJhbWVJbmRleCA9IGZpbmRLZXlmcmFtZUluZGV4KHZpZGVvVHJhY2suc2FtcGxlcyk7XG4gICAgICAgIGlmICghaXNWaWRlb0NvbnRpZ3VvdXMgJiYgdGhpcy5jb25maWcuZm9yY2VLZXlGcmFtZU9uRGlzY29udGludWl0eSkge1xuICAgICAgICAgIGluZGVwZW5kZW50ID0gdHJ1ZTtcbiAgICAgICAgICBpZiAoZmlyc3RLZXlGcmFtZUluZGV4ID4gMCkge1xuICAgICAgICAgICAgbG9nZ2VyLndhcm4oYFttcDQtcmVtdXhlcl06IERyb3BwZWQgJHtmaXJzdEtleUZyYW1lSW5kZXh9IG91dCBvZiAke2xlbmd0aH0gdmlkZW8gc2FtcGxlcyBkdWUgdG8gYSBtaXNzaW5nIGtleWZyYW1lYCk7XG4gICAgICAgICAgICBjb25zdCBzdGFydFBUUyA9IHRoaXMuZ2V0VmlkZW9TdGFydFB0cyh2aWRlb1RyYWNrLnNhbXBsZXMpO1xuICAgICAgICAgICAgdmlkZW9UcmFjay5zYW1wbGVzID0gdmlkZW9UcmFjay5zYW1wbGVzLnNsaWNlKGZpcnN0S2V5RnJhbWVJbmRleCk7XG4gICAgICAgICAgICB2aWRlb1RyYWNrLmRyb3BwZWQgKz0gZmlyc3RLZXlGcmFtZUluZGV4O1xuICAgICAgICAgICAgdmlkZW9UaW1lT2Zmc2V0ICs9ICh2aWRlb1RyYWNrLnNhbXBsZXNbMF0ucHRzIC0gc3RhcnRQVFMpIC8gdmlkZW9UcmFjay5pbnB1dFRpbWVTY2FsZTtcbiAgICAgICAgICAgIGZpcnN0S2V5RnJhbWVQVFMgPSB2aWRlb1RpbWVPZmZzZXQ7XG4gICAgICAgICAgfSBlbHNlIGlmIChmaXJzdEtleUZyYW1lSW5kZXggPT09IC0xKSB7XG4gICAgICAgICAgICBsb2dnZXIud2FybihgW21wNC1yZW11eGVyXTogTm8ga2V5ZnJhbWUgZm91bmQgb3V0IG9mICR7bGVuZ3RofSB2aWRlbyBzYW1wbGVzYCk7XG4gICAgICAgICAgICBpbmRlcGVuZGVudCA9IGZhbHNlO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgaWYgKHRoaXMuSVNHZW5lcmF0ZWQpIHtcbiAgICAgICAgaWYgKGVub3VnaEF1ZGlvU2FtcGxlcyAmJiBlbm91Z2hWaWRlb1NhbXBsZXMpIHtcbiAgICAgICAgICAvLyB0aW1lT2Zmc2V0IGlzIGV4cGVjdGVkIHRvIGJlIHRoZSBvZmZzZXQgb2YgdGhlIGZpcnN0IHRpbWVzdGFtcCBvZiB0aGlzIGZyYWdtZW50IChmaXJzdCBEVFMpXG4gICAgICAgICAgLy8gaWYgZmlyc3QgYXVkaW8gRFRTIGlzIG5vdCBhbGlnbmVkIHdpdGggZmlyc3QgdmlkZW8gRFRTIHRoZW4gd2UgbmVlZCB0byB0YWtlIHRoYXQgaW50byBhY2NvdW50XG4gICAgICAgICAgLy8gd2hlbiBwcm92aWRpbmcgdGltZU9mZnNldCB0byByZW11eEF1ZGlvIC8gcmVtdXhWaWRlby4gaWYgd2UgZG9uJ3QgZG8gdGhhdCwgdGhlcmUgbWlnaHQgYmUgYSBwZXJtYW5lbnQgLyBzbWFsbFxuICAgICAgICAgIC8vIGRyaWZ0IGJldHdlZW4gYXVkaW8gYW5kIHZpZGVvIHN0cmVhbXNcbiAgICAgICAgICBjb25zdCBzdGFydFBUUyA9IHRoaXMuZ2V0VmlkZW9TdGFydFB0cyh2aWRlb1RyYWNrLnNhbXBsZXMpO1xuICAgICAgICAgIGNvbnN0IHRzRGVsdGEgPSBub3JtYWxpemVQdHMoYXVkaW9UcmFjay5zYW1wbGVzWzBdLnB0cywgc3RhcnRQVFMpIC0gc3RhcnRQVFM7XG4gICAgICAgICAgY29uc3QgYXVkaW92aWRlb1RpbWVzdGFtcERlbHRhID0gdHNEZWx0YSAvIHZpZGVvVHJhY2suaW5wdXRUaW1lU2NhbGU7XG4gICAgICAgICAgYXVkaW9UaW1lT2Zmc2V0ICs9IE1hdGgubWF4KDAsIGF1ZGlvdmlkZW9UaW1lc3RhbXBEZWx0YSk7XG4gICAgICAgICAgdmlkZW9UaW1lT2Zmc2V0ICs9IE1hdGgubWF4KDAsIC1hdWRpb3ZpZGVvVGltZXN0YW1wRGVsdGEpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gUHVycG9zZWZ1bGx5IHJlbXV4aW5nIGF1ZGlvIGJlZm9yZSB2aWRlbywgc28gdGhhdCByZW11eFZpZGVvIGNhbiB1c2UgbmV4dEF1ZGlvUHRzLCB3aGljaCBpcyBjYWxjdWxhdGVkIGluIHJlbXV4QXVkaW8uXG4gICAgICAgIGlmIChlbm91Z2hBdWRpb1NhbXBsZXMpIHtcbiAgICAgICAgICAvLyBpZiBpbml0U2VnbWVudCB3YXMgZ2VuZXJhdGVkIHdpdGhvdXQgYXVkaW8gc2FtcGxlcywgcmVnZW5lcmF0ZSBpdCBhZ2FpblxuICAgICAgICAgIGlmICghYXVkaW9UcmFjay5zYW1wbGVyYXRlKSB7XG4gICAgICAgICAgICBsb2dnZXIud2FybignW21wNC1yZW11eGVyXTogcmVnZW5lcmF0ZSBJbml0U2VnbWVudCBhcyBhdWRpbyBkZXRlY3RlZCcpO1xuICAgICAgICAgICAgaW5pdFNlZ21lbnQgPSB0aGlzLmdlbmVyYXRlSVMoYXVkaW9UcmFjaywgdmlkZW9UcmFjaywgdGltZU9mZnNldCwgYWNjdXJhdGVUaW1lT2Zmc2V0KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgYXVkaW8gPSB0aGlzLnJlbXV4QXVkaW8oYXVkaW9UcmFjaywgYXVkaW9UaW1lT2Zmc2V0LCB0aGlzLmlzQXVkaW9Db250aWd1b3VzLCBhY2N1cmF0ZVRpbWVPZmZzZXQsIGhhc1ZpZGVvIHx8IGVub3VnaFZpZGVvU2FtcGxlcyB8fCBwbGF5bGlzdFR5cGUgPT09IFBsYXlsaXN0TGV2ZWxUeXBlLkFVRElPID8gdmlkZW9UaW1lT2Zmc2V0IDogdW5kZWZpbmVkKTtcbiAgICAgICAgICBpZiAoZW5vdWdoVmlkZW9TYW1wbGVzKSB7XG4gICAgICAgICAgICBjb25zdCBhdWRpb1RyYWNrTGVuZ3RoID0gYXVkaW8gPyBhdWRpby5lbmRQVFMgLSBhdWRpby5zdGFydFBUUyA6IDA7XG4gICAgICAgICAgICAvLyBpZiBpbml0U2VnbWVudCB3YXMgZ2VuZXJhdGVkIHdpdGhvdXQgdmlkZW8gc2FtcGxlcywgcmVnZW5lcmF0ZSBpdCBhZ2FpblxuICAgICAgICAgICAgaWYgKCF2aWRlb1RyYWNrLmlucHV0VGltZVNjYWxlKSB7XG4gICAgICAgICAgICAgIGxvZ2dlci53YXJuKCdbbXA0LXJlbXV4ZXJdOiByZWdlbmVyYXRlIEluaXRTZWdtZW50IGFzIHZpZGVvIGRldGVjdGVkJyk7XG4gICAgICAgICAgICAgIGluaXRTZWdtZW50ID0gdGhpcy5nZW5lcmF0ZUlTKGF1ZGlvVHJhY2ssIHZpZGVvVHJhY2ssIHRpbWVPZmZzZXQsIGFjY3VyYXRlVGltZU9mZnNldCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB2aWRlbyA9IHRoaXMucmVtdXhWaWRlbyh2aWRlb1RyYWNrLCB2aWRlb1RpbWVPZmZzZXQsIGlzVmlkZW9Db250aWd1b3VzLCBhdWRpb1RyYWNrTGVuZ3RoKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoZW5vdWdoVmlkZW9TYW1wbGVzKSB7XG4gICAgICAgICAgdmlkZW8gPSB0aGlzLnJlbXV4VmlkZW8odmlkZW9UcmFjaywgdmlkZW9UaW1lT2Zmc2V0LCBpc1ZpZGVvQ29udGlndW91cywgMCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHZpZGVvKSB7XG4gICAgICAgICAgdmlkZW8uZmlyc3RLZXlGcmFtZSA9IGZpcnN0S2V5RnJhbWVJbmRleDtcbiAgICAgICAgICB2aWRlby5pbmRlcGVuZGVudCA9IGZpcnN0S2V5RnJhbWVJbmRleCAhPT0gLTE7XG4gICAgICAgICAgdmlkZW8uZmlyc3RLZXlGcmFtZVBUUyA9IGZpcnN0S2V5RnJhbWVQVFM7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBBbGxvdyBJRDMgYW5kIHRleHQgdG8gcmVtdXgsIGV2ZW4gaWYgbW9yZSBhdWRpby92aWRlbyBzYW1wbGVzIGFyZSByZXF1aXJlZFxuICAgIGlmICh0aGlzLklTR2VuZXJhdGVkICYmIHRoaXMuX2luaXRQVFMgJiYgdGhpcy5faW5pdERUUykge1xuICAgICAgaWYgKGlkM1RyYWNrLnNhbXBsZXMubGVuZ3RoKSB7XG4gICAgICAgIGlkMyA9IGZsdXNoVGV4dFRyYWNrTWV0YWRhdGFDdWVTYW1wbGVzKGlkM1RyYWNrLCB0aW1lT2Zmc2V0LCB0aGlzLl9pbml0UFRTLCB0aGlzLl9pbml0RFRTKTtcbiAgICAgIH1cbiAgICAgIGlmICh0ZXh0VHJhY2suc2FtcGxlcy5sZW5ndGgpIHtcbiAgICAgICAgdGV4dCA9IGZsdXNoVGV4dFRyYWNrVXNlcmRhdGFDdWVTYW1wbGVzKHRleHRUcmFjaywgdGltZU9mZnNldCwgdGhpcy5faW5pdFBUUyk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB7XG4gICAgICBhdWRpbyxcbiAgICAgIHZpZGVvLFxuICAgICAgaW5pdFNlZ21lbnQsXG4gICAgICBpbmRlcGVuZGVudCxcbiAgICAgIHRleHQsXG4gICAgICBpZDNcbiAgICB9O1xuICB9XG4gIGdlbmVyYXRlSVMoYXVkaW9UcmFjaywgdmlkZW9UcmFjaywgdGltZU9mZnNldCwgYWNjdXJhdGVUaW1lT2Zmc2V0KSB7XG4gICAgY29uc3QgYXVkaW9TYW1wbGVzID0gYXVkaW9UcmFjay5zYW1wbGVzO1xuICAgIGNvbnN0IHZpZGVvU2FtcGxlcyA9IHZpZGVvVHJhY2suc2FtcGxlcztcbiAgICBjb25zdCB0eXBlU3VwcG9ydGVkID0gdGhpcy50eXBlU3VwcG9ydGVkO1xuICAgIGNvbnN0IHRyYWNrcyA9IHt9O1xuICAgIGNvbnN0IF9pbml0UFRTID0gdGhpcy5faW5pdFBUUztcbiAgICBsZXQgY29tcHV0ZVBUU0RUUyA9ICFfaW5pdFBUUyB8fCBhY2N1cmF0ZVRpbWVPZmZzZXQ7XG4gICAgbGV0IGNvbnRhaW5lciA9ICdhdWRpby9tcDQnO1xuICAgIGxldCBpbml0UFRTO1xuICAgIGxldCBpbml0RFRTO1xuICAgIGxldCB0aW1lc2NhbGU7XG4gICAgaWYgKGNvbXB1dGVQVFNEVFMpIHtcbiAgICAgIGluaXRQVFMgPSBpbml0RFRTID0gSW5maW5pdHk7XG4gICAgfVxuICAgIGlmIChhdWRpb1RyYWNrLmNvbmZpZyAmJiBhdWRpb1NhbXBsZXMubGVuZ3RoKSB7XG4gICAgICAvLyBsZXQncyB1c2UgYXVkaW8gc2FtcGxpbmcgcmF0ZSBhcyBNUDQgdGltZSBzY2FsZS5cbiAgICAgIC8vIHJhdGlvbmFsZSBpcyB0aGF0IHRoZXJlIGlzIGEgaW50ZWdlciBuYiBvZiBhdWRpbyBmcmFtZXMgcGVyIGF1ZGlvIHNhbXBsZSAoMTAyNCBmb3IgQUFDKVxuICAgICAgLy8gdXNpbmcgYXVkaW8gc2FtcGxpbmcgcmF0ZSBoZXJlIGhlbHBzIGhhdmluZyBhbiBpbnRlZ2VyIE1QNCBmcmFtZSBkdXJhdGlvblxuICAgICAgLy8gdGhpcyBhdm9pZHMgcG90ZW50aWFsIHJvdW5kaW5nIGlzc3VlIGFuZCBBViBzeW5jIGlzc3VlXG4gICAgICBhdWRpb1RyYWNrLnRpbWVzY2FsZSA9IGF1ZGlvVHJhY2suc2FtcGxlcmF0ZTtcbiAgICAgIHN3aXRjaCAoYXVkaW9UcmFjay5zZWdtZW50Q29kZWMpIHtcbiAgICAgICAgY2FzZSAnbXAzJzpcbiAgICAgICAgICBpZiAodHlwZVN1cHBvcnRlZC5tcGVnKSB7XG4gICAgICAgICAgICAvLyBDaHJvbWUgYW5kIFNhZmFyaVxuICAgICAgICAgICAgY29udGFpbmVyID0gJ2F1ZGlvL21wZWcnO1xuICAgICAgICAgICAgYXVkaW9UcmFjay5jb2RlYyA9ICcnO1xuICAgICAgICAgIH0gZWxzZSBpZiAodHlwZVN1cHBvcnRlZC5tcDMpIHtcbiAgICAgICAgICAgIC8vIEZpcmVmb3hcbiAgICAgICAgICAgIGF1ZGlvVHJhY2suY29kZWMgPSAnbXAzJztcbiAgICAgICAgICB9XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ2FjMyc6XG4gICAgICAgICAgYXVkaW9UcmFjay5jb2RlYyA9ICdhYy0zJztcbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICAgIHRyYWNrcy5hdWRpbyA9IHtcbiAgICAgICAgaWQ6ICdhdWRpbycsXG4gICAgICAgIGNvbnRhaW5lcjogY29udGFpbmVyLFxuICAgICAgICBjb2RlYzogYXVkaW9UcmFjay5jb2RlYyxcbiAgICAgICAgaW5pdFNlZ21lbnQ6IGF1ZGlvVHJhY2suc2VnbWVudENvZGVjID09PSAnbXAzJyAmJiB0eXBlU3VwcG9ydGVkLm1wZWcgPyBuZXcgVWludDhBcnJheSgwKSA6IE1QNC5pbml0U2VnbWVudChbYXVkaW9UcmFja10pLFxuICAgICAgICBtZXRhZGF0YToge1xuICAgICAgICAgIGNoYW5uZWxDb3VudDogYXVkaW9UcmFjay5jaGFubmVsQ291bnRcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGlmIChjb21wdXRlUFRTRFRTKSB7XG4gICAgICAgIHRpbWVzY2FsZSA9IGF1ZGlvVHJhY2suaW5wdXRUaW1lU2NhbGU7XG4gICAgICAgIGlmICghX2luaXRQVFMgfHwgdGltZXNjYWxlICE9PSBfaW5pdFBUUy50aW1lc2NhbGUpIHtcbiAgICAgICAgICAvLyByZW1lbWJlciBmaXJzdCBQVFMgb2YgdGhpcyBkZW11eGluZyBjb250ZXh0LiBmb3IgYXVkaW8sIFBUUyA9IERUU1xuICAgICAgICAgIGluaXRQVFMgPSBpbml0RFRTID0gYXVkaW9TYW1wbGVzWzBdLnB0cyAtIE1hdGgucm91bmQodGltZXNjYWxlICogdGltZU9mZnNldCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY29tcHV0ZVBUU0RUUyA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIGlmICh2aWRlb1RyYWNrLnNwcyAmJiB2aWRlb1RyYWNrLnBwcyAmJiB2aWRlb1NhbXBsZXMubGVuZ3RoKSB7XG4gICAgICAvLyBsZXQncyB1c2UgaW5wdXQgdGltZSBzY2FsZSBhcyBNUDQgdmlkZW8gdGltZXNjYWxlXG4gICAgICAvLyB3ZSB1c2UgaW5wdXQgdGltZSBzY2FsZSBzdHJhaWdodCBhd2F5IHRvIGF2b2lkIHJvdW5kaW5nIGlzc3VlcyBvbiBmcmFtZSBkdXJhdGlvbiAvIGN0cyBjb21wdXRhdGlvblxuICAgICAgdmlkZW9UcmFjay50aW1lc2NhbGUgPSB2aWRlb1RyYWNrLmlucHV0VGltZVNjYWxlO1xuICAgICAgdHJhY2tzLnZpZGVvID0ge1xuICAgICAgICBpZDogJ21haW4nLFxuICAgICAgICBjb250YWluZXI6ICd2aWRlby9tcDQnLFxuICAgICAgICBjb2RlYzogdmlkZW9UcmFjay5jb2RlYyxcbiAgICAgICAgaW5pdFNlZ21lbnQ6IE1QNC5pbml0U2VnbWVudChbdmlkZW9UcmFja10pLFxuICAgICAgICBtZXRhZGF0YToge1xuICAgICAgICAgIHdpZHRoOiB2aWRlb1RyYWNrLndpZHRoLFxuICAgICAgICAgIGhlaWdodDogdmlkZW9UcmFjay5oZWlnaHRcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGlmIChjb21wdXRlUFRTRFRTKSB7XG4gICAgICAgIHRpbWVzY2FsZSA9IHZpZGVvVHJhY2suaW5wdXRUaW1lU2NhbGU7XG4gICAgICAgIGlmICghX2luaXRQVFMgfHwgdGltZXNjYWxlICE9PSBfaW5pdFBUUy50aW1lc2NhbGUpIHtcbiAgICAgICAgICBjb25zdCBzdGFydFBUUyA9IHRoaXMuZ2V0VmlkZW9TdGFydFB0cyh2aWRlb1NhbXBsZXMpO1xuICAgICAgICAgIGNvbnN0IHN0YXJ0T2Zmc2V0ID0gTWF0aC5yb3VuZCh0aW1lc2NhbGUgKiB0aW1lT2Zmc2V0KTtcbiAgICAgICAgICBpbml0RFRTID0gTWF0aC5taW4oaW5pdERUUywgbm9ybWFsaXplUHRzKHZpZGVvU2FtcGxlc1swXS5kdHMsIHN0YXJ0UFRTKSAtIHN0YXJ0T2Zmc2V0KTtcbiAgICAgICAgICBpbml0UFRTID0gTWF0aC5taW4oaW5pdFBUUywgc3RhcnRQVFMgLSBzdGFydE9mZnNldCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY29tcHV0ZVBUU0RUUyA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICB0aGlzLnZpZGVvVHJhY2tDb25maWcgPSB7XG4gICAgICAgIHdpZHRoOiB2aWRlb1RyYWNrLndpZHRoLFxuICAgICAgICBoZWlnaHQ6IHZpZGVvVHJhY2suaGVpZ2h0LFxuICAgICAgICBwaXhlbFJhdGlvOiB2aWRlb1RyYWNrLnBpeGVsUmF0aW9cbiAgICAgIH07XG4gICAgfVxuICAgIGlmIChPYmplY3Qua2V5cyh0cmFja3MpLmxlbmd0aCkge1xuICAgICAgdGhpcy5JU0dlbmVyYXRlZCA9IHRydWU7XG4gICAgICBpZiAoY29tcHV0ZVBUU0RUUykge1xuICAgICAgICB0aGlzLl9pbml0UFRTID0ge1xuICAgICAgICAgIGJhc2VUaW1lOiBpbml0UFRTLFxuICAgICAgICAgIHRpbWVzY2FsZTogdGltZXNjYWxlXG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuX2luaXREVFMgPSB7XG4gICAgICAgICAgYmFzZVRpbWU6IGluaXREVFMsXG4gICAgICAgICAgdGltZXNjYWxlOiB0aW1lc2NhbGVcbiAgICAgICAgfTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGluaXRQVFMgPSB0aW1lc2NhbGUgPSB1bmRlZmluZWQ7XG4gICAgICB9XG4gICAgICByZXR1cm4ge1xuICAgICAgICB0cmFja3MsXG4gICAgICAgIGluaXRQVFMsXG4gICAgICAgIHRpbWVzY2FsZVxuICAgICAgfTtcbiAgICB9XG4gIH1cbiAgcmVtdXhWaWRlbyh0cmFjaywgdGltZU9mZnNldCwgY29udGlndW91cywgYXVkaW9UcmFja0xlbmd0aCkge1xuICAgIGNvbnN0IHRpbWVTY2FsZSA9IHRyYWNrLmlucHV0VGltZVNjYWxlO1xuICAgIGNvbnN0IGlucHV0U2FtcGxlcyA9IHRyYWNrLnNhbXBsZXM7XG4gICAgY29uc3Qgb3V0cHV0U2FtcGxlcyA9IFtdO1xuICAgIGNvbnN0IG5iU2FtcGxlcyA9IGlucHV0U2FtcGxlcy5sZW5ndGg7XG4gICAgY29uc3QgaW5pdFBUUyA9IHRoaXMuX2luaXRQVFM7XG4gICAgbGV0IG5leHRBdmNEdHMgPSB0aGlzLm5leHRBdmNEdHM7XG4gICAgbGV0IG9mZnNldCA9IDg7XG4gICAgbGV0IG1wNFNhbXBsZUR1cmF0aW9uID0gdGhpcy52aWRlb1NhbXBsZUR1cmF0aW9uO1xuICAgIGxldCBmaXJzdERUUztcbiAgICBsZXQgbGFzdERUUztcbiAgICBsZXQgbWluUFRTID0gTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZO1xuICAgIGxldCBtYXhQVFMgPSBOdW1iZXIuTkVHQVRJVkVfSU5GSU5JVFk7XG4gICAgbGV0IHNvcnRTYW1wbGVzID0gZmFsc2U7XG5cbiAgICAvLyBpZiBwYXJzZWQgZnJhZ21lbnQgaXMgY29udGlndW91cyB3aXRoIGxhc3Qgb25lLCBsZXQncyB1c2UgbGFzdCBEVFMgdmFsdWUgYXMgcmVmZXJlbmNlXG4gICAgaWYgKCFjb250aWd1b3VzIHx8IG5leHRBdmNEdHMgPT09IG51bGwpIHtcbiAgICAgIGNvbnN0IHB0cyA9IHRpbWVPZmZzZXQgKiB0aW1lU2NhbGU7XG4gICAgICBjb25zdCBjdHMgPSBpbnB1dFNhbXBsZXNbMF0ucHRzIC0gbm9ybWFsaXplUHRzKGlucHV0U2FtcGxlc1swXS5kdHMsIGlucHV0U2FtcGxlc1swXS5wdHMpO1xuICAgICAgaWYgKGNocm9tZVZlcnNpb24gJiYgbmV4dEF2Y0R0cyAhPT0gbnVsbCAmJiBNYXRoLmFicyhwdHMgLSBjdHMgLSBuZXh0QXZjRHRzKSA8IDE1MDAwKSB7XG4gICAgICAgIC8vIHRyZWF0IGFzIGNvbnRpZ291cyB0byBhZGp1c3Qgc2FtcGxlcyB0aGF0IHdvdWxkIG90aGVyd2lzZSBwcm9kdWNlIHZpZGVvIGJ1ZmZlciBnYXBzIGluIENocm9tZVxuICAgICAgICBjb250aWd1b3VzID0gdHJ1ZTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIGlmIG5vdCBjb250aWd1b3VzLCBsZXQncyB1c2UgdGFyZ2V0IHRpbWVPZmZzZXRcbiAgICAgICAgbmV4dEF2Y0R0cyA9IHB0cyAtIGN0cztcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBQVFMgaXMgY29kZWQgb24gMzNiaXRzLCBhbmQgY2FuIGxvb3AgZnJvbSAtMl4zMiB0byAyXjMyXG4gICAgLy8gUFRTTm9ybWFsaXplIHdpbGwgbWFrZSBQVFMvRFRTIHZhbHVlIG1vbm90b25pYywgd2UgdXNlIGxhc3Qga25vd24gRFRTIHZhbHVlIGFzIHJlZmVyZW5jZSB2YWx1ZVxuICAgIGNvbnN0IGluaXRUaW1lID0gaW5pdFBUUy5iYXNlVGltZSAqIHRpbWVTY2FsZSAvIGluaXRQVFMudGltZXNjYWxlO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbmJTYW1wbGVzOyBpKyspIHtcbiAgICAgIGNvbnN0IHNhbXBsZSA9IGlucHV0U2FtcGxlc1tpXTtcbiAgICAgIHNhbXBsZS5wdHMgPSBub3JtYWxpemVQdHMoc2FtcGxlLnB0cyAtIGluaXRUaW1lLCBuZXh0QXZjRHRzKTtcbiAgICAgIHNhbXBsZS5kdHMgPSBub3JtYWxpemVQdHMoc2FtcGxlLmR0cyAtIGluaXRUaW1lLCBuZXh0QXZjRHRzKTtcbiAgICAgIGlmIChzYW1wbGUuZHRzIDwgaW5wdXRTYW1wbGVzW2kgPiAwID8gaSAtIDEgOiBpXS5kdHMpIHtcbiAgICAgICAgc29ydFNhbXBsZXMgPSB0cnVlO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIHNvcnQgdmlkZW8gc2FtcGxlcyBieSBEVFMgdGhlbiBQVFMgdGhlbiBkZW11eCBpZCBvcmRlclxuICAgIGlmIChzb3J0U2FtcGxlcykge1xuICAgICAgaW5wdXRTYW1wbGVzLnNvcnQoZnVuY3Rpb24gKGEsIGIpIHtcbiAgICAgICAgY29uc3QgZGVsdGFkdHMgPSBhLmR0cyAtIGIuZHRzO1xuICAgICAgICBjb25zdCBkZWx0YXB0cyA9IGEucHRzIC0gYi5wdHM7XG4gICAgICAgIHJldHVybiBkZWx0YWR0cyB8fCBkZWx0YXB0cztcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIEdldCBmaXJzdC9sYXN0IERUU1xuICAgIGZpcnN0RFRTID0gaW5wdXRTYW1wbGVzWzBdLmR0cztcbiAgICBsYXN0RFRTID0gaW5wdXRTYW1wbGVzW2lucHV0U2FtcGxlcy5sZW5ndGggLSAxXS5kdHM7XG5cbiAgICAvLyBTYW1wbGUgZHVyYXRpb24gKGFzIGV4cGVjdGVkIGJ5IHRydW4gTVA0IGJveGVzKSwgc2hvdWxkIGJlIHRoZSBkZWx0YSBiZXR3ZWVuIHNhbXBsZSBEVFNcbiAgICAvLyBzZXQgdGhpcyBjb25zdGFudCBkdXJhdGlvbiBhcyBiZWluZyB0aGUgYXZnIGRlbHRhIGJldHdlZW4gY29uc2VjdXRpdmUgRFRTLlxuICAgIGNvbnN0IGlucHV0RHVyYXRpb24gPSBsYXN0RFRTIC0gZmlyc3REVFM7XG4gICAgY29uc3QgYXZlcmFnZVNhbXBsZUR1cmF0aW9uID0gaW5wdXREdXJhdGlvbiA/IE1hdGgucm91bmQoaW5wdXREdXJhdGlvbiAvIChuYlNhbXBsZXMgLSAxKSkgOiBtcDRTYW1wbGVEdXJhdGlvbiB8fCB0cmFjay5pbnB1dFRpbWVTY2FsZSAvIDMwO1xuXG4gICAgLy8gaWYgZnJhZ21lbnQgYXJlIGNvbnRpZ3VvdXMsIGRldGVjdCBob2xlL292ZXJsYXBwaW5nIGJldHdlZW4gZnJhZ21lbnRzXG4gICAgaWYgKGNvbnRpZ3VvdXMpIHtcbiAgICAgIC8vIGNoZWNrIHRpbWVzdGFtcCBjb250aW51aXR5IGFjcm9zcyBjb25zZWN1dGl2ZSBmcmFnbWVudHMgKHRoaXMgaXMgdG8gcmVtb3ZlIGludGVyLWZyYWdtZW50IGdhcC9ob2xlKVxuICAgICAgY29uc3QgZGVsdGEgPSBmaXJzdERUUyAtIG5leHRBdmNEdHM7XG4gICAgICBjb25zdCBmb3VuZEhvbGUgPSBkZWx0YSA+IGF2ZXJhZ2VTYW1wbGVEdXJhdGlvbjtcbiAgICAgIGNvbnN0IGZvdW5kT3ZlcmxhcCA9IGRlbHRhIDwgLTE7XG4gICAgICBpZiAoZm91bmRIb2xlIHx8IGZvdW5kT3ZlcmxhcCkge1xuICAgICAgICBpZiAoZm91bmRIb2xlKSB7XG4gICAgICAgICAgbG9nZ2VyLndhcm4oYEFWQzogJHt0b01zRnJvbU1wZWdUc0Nsb2NrKGRlbHRhLCB0cnVlKX0gbXMgKCR7ZGVsdGF9ZHRzKSBob2xlIGJldHdlZW4gZnJhZ21lbnRzIGRldGVjdGVkIGF0ICR7dGltZU9mZnNldC50b0ZpeGVkKDMpfWApO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGxvZ2dlci53YXJuKGBBVkM6ICR7dG9Nc0Zyb21NcGVnVHNDbG9jaygtZGVsdGEsIHRydWUpfSBtcyAoJHtkZWx0YX1kdHMpIG92ZXJsYXBwaW5nIGJldHdlZW4gZnJhZ21lbnRzIGRldGVjdGVkIGF0ICR7dGltZU9mZnNldC50b0ZpeGVkKDMpfWApO1xuICAgICAgICB9XG4gICAgICAgIGlmICghZm91bmRPdmVybGFwIHx8IG5leHRBdmNEdHMgPj0gaW5wdXRTYW1wbGVzWzBdLnB0cyB8fCBjaHJvbWVWZXJzaW9uKSB7XG4gICAgICAgICAgZmlyc3REVFMgPSBuZXh0QXZjRHRzO1xuICAgICAgICAgIGNvbnN0IGZpcnN0UFRTID0gaW5wdXRTYW1wbGVzWzBdLnB0cyAtIGRlbHRhO1xuICAgICAgICAgIGlmIChmb3VuZEhvbGUpIHtcbiAgICAgICAgICAgIGlucHV0U2FtcGxlc1swXS5kdHMgPSBmaXJzdERUUztcbiAgICAgICAgICAgIGlucHV0U2FtcGxlc1swXS5wdHMgPSBmaXJzdFBUUztcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBpbnB1dFNhbXBsZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgaWYgKGlucHV0U2FtcGxlc1tpXS5kdHMgPiBmaXJzdFBUUykge1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGlucHV0U2FtcGxlc1tpXS5kdHMgLT0gZGVsdGE7XG4gICAgICAgICAgICAgIGlucHV0U2FtcGxlc1tpXS5wdHMgLT0gZGVsdGE7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGxvZ2dlci5sb2coYFZpZGVvOiBJbml0aWFsIFBUUy9EVFMgYWRqdXN0ZWQ6ICR7dG9Nc0Zyb21NcGVnVHNDbG9jayhmaXJzdFBUUywgdHJ1ZSl9LyR7dG9Nc0Zyb21NcGVnVHNDbG9jayhmaXJzdERUUywgdHJ1ZSl9LCBkZWx0YTogJHt0b01zRnJvbU1wZWdUc0Nsb2NrKGRlbHRhLCB0cnVlKX0gbXNgKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICBmaXJzdERUUyA9IE1hdGgubWF4KDAsIGZpcnN0RFRTKTtcbiAgICBsZXQgbmJOYWx1ID0gMDtcbiAgICBsZXQgbmFsdUxlbiA9IDA7XG4gICAgbGV0IGR0c1N0ZXAgPSBmaXJzdERUUztcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG5iU2FtcGxlczsgaSsrKSB7XG4gICAgICAvLyBjb21wdXRlIHRvdGFsL2F2YyBzYW1wbGUgbGVuZ3RoIGFuZCBuYiBvZiBOQUwgdW5pdHNcbiAgICAgIGNvbnN0IHNhbXBsZSA9IGlucHV0U2FtcGxlc1tpXTtcbiAgICAgIGNvbnN0IHVuaXRzID0gc2FtcGxlLnVuaXRzO1xuICAgICAgY29uc3QgbmJVbml0cyA9IHVuaXRzLmxlbmd0aDtcbiAgICAgIGxldCBzYW1wbGVMZW4gPSAwO1xuICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCBuYlVuaXRzOyBqKyspIHtcbiAgICAgICAgc2FtcGxlTGVuICs9IHVuaXRzW2pdLmRhdGEubGVuZ3RoO1xuICAgICAgfVxuICAgICAgbmFsdUxlbiArPSBzYW1wbGVMZW47XG4gICAgICBuYk5hbHUgKz0gbmJVbml0cztcbiAgICAgIHNhbXBsZS5sZW5ndGggPSBzYW1wbGVMZW47XG5cbiAgICAgIC8vIGVuc3VyZSBzYW1wbGUgbW9ub3RvbmljIERUU1xuICAgICAgaWYgKHNhbXBsZS5kdHMgPCBkdHNTdGVwKSB7XG4gICAgICAgIHNhbXBsZS5kdHMgPSBkdHNTdGVwO1xuICAgICAgICBkdHNTdGVwICs9IGF2ZXJhZ2VTYW1wbGVEdXJhdGlvbiAvIDQgfCAwIHx8IDE7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBkdHNTdGVwID0gc2FtcGxlLmR0cztcbiAgICAgIH1cbiAgICAgIG1pblBUUyA9IE1hdGgubWluKHNhbXBsZS5wdHMsIG1pblBUUyk7XG4gICAgICBtYXhQVFMgPSBNYXRoLm1heChzYW1wbGUucHRzLCBtYXhQVFMpO1xuICAgIH1cbiAgICBsYXN0RFRTID0gaW5wdXRTYW1wbGVzW25iU2FtcGxlcyAtIDFdLmR0cztcblxuICAgIC8qIGNvbmNhdGVuYXRlIHRoZSB2aWRlbyBkYXRhIGFuZCBjb25zdHJ1Y3QgdGhlIG1kYXQgaW4gcGxhY2VcbiAgICAgIChuZWVkIDggbW9yZSBieXRlcyB0byBmaWxsIGxlbmd0aCBhbmQgbXBkYXQgdHlwZSkgKi9cbiAgICBjb25zdCBtZGF0U2l6ZSA9IG5hbHVMZW4gKyA0ICogbmJOYWx1ICsgODtcbiAgICBsZXQgbWRhdDtcbiAgICB0cnkge1xuICAgICAgbWRhdCA9IG5ldyBVaW50OEFycmF5KG1kYXRTaXplKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIHRoaXMub2JzZXJ2ZXIuZW1pdChFdmVudHMuRVJST1IsIEV2ZW50cy5FUlJPUiwge1xuICAgICAgICB0eXBlOiBFcnJvclR5cGVzLk1VWF9FUlJPUixcbiAgICAgICAgZGV0YWlsczogRXJyb3JEZXRhaWxzLlJFTVVYX0FMTE9DX0VSUk9SLFxuICAgICAgICBmYXRhbDogZmFsc2UsXG4gICAgICAgIGVycm9yOiBlcnIsXG4gICAgICAgIGJ5dGVzOiBtZGF0U2l6ZSxcbiAgICAgICAgcmVhc29uOiBgZmFpbCBhbGxvY2F0aW5nIHZpZGVvIG1kYXQgJHttZGF0U2l6ZX1gXG4gICAgICB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgdmlldyA9IG5ldyBEYXRhVmlldyhtZGF0LmJ1ZmZlcik7XG4gICAgdmlldy5zZXRVaW50MzIoMCwgbWRhdFNpemUpO1xuICAgIG1kYXQuc2V0KE1QNC50eXBlcy5tZGF0LCA0KTtcbiAgICBsZXQgc3RyZXRjaGVkTGFzdEZyYW1lID0gZmFsc2U7XG4gICAgbGV0IG1pbkR0c0RlbHRhID0gTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZO1xuICAgIGxldCBtaW5QdHNEZWx0YSA9IE51bWJlci5QT1NJVElWRV9JTkZJTklUWTtcbiAgICBsZXQgbWF4RHRzRGVsdGEgPSBOdW1iZXIuTkVHQVRJVkVfSU5GSU5JVFk7XG4gICAgbGV0IG1heFB0c0RlbHRhID0gTnVtYmVyLk5FR0FUSVZFX0lORklOSVRZO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbmJTYW1wbGVzOyBpKyspIHtcbiAgICAgIGNvbnN0IFZpZGVvU2FtcGxlID0gaW5wdXRTYW1wbGVzW2ldO1xuICAgICAgY29uc3QgVmlkZW9TYW1wbGVVbml0cyA9IFZpZGVvU2FtcGxlLnVuaXRzO1xuICAgICAgbGV0IG1wNFNhbXBsZUxlbmd0aCA9IDA7XG4gICAgICAvLyBjb252ZXJ0IE5BTFUgYml0c3RyZWFtIHRvIE1QNCBmb3JtYXQgKHByZXBlbmQgTkFMVSB3aXRoIHNpemUgZmllbGQpXG4gICAgICBmb3IgKGxldCBqID0gMCwgbmJVbml0cyA9IFZpZGVvU2FtcGxlVW5pdHMubGVuZ3RoOyBqIDwgbmJVbml0czsgaisrKSB7XG4gICAgICAgIGNvbnN0IHVuaXQgPSBWaWRlb1NhbXBsZVVuaXRzW2pdO1xuICAgICAgICBjb25zdCB1bml0RGF0YSA9IHVuaXQuZGF0YTtcbiAgICAgICAgY29uc3QgdW5pdERhdGFMZW4gPSB1bml0LmRhdGEuYnl0ZUxlbmd0aDtcbiAgICAgICAgdmlldy5zZXRVaW50MzIob2Zmc2V0LCB1bml0RGF0YUxlbik7XG4gICAgICAgIG9mZnNldCArPSA0O1xuICAgICAgICBtZGF0LnNldCh1bml0RGF0YSwgb2Zmc2V0KTtcbiAgICAgICAgb2Zmc2V0ICs9IHVuaXREYXRhTGVuO1xuICAgICAgICBtcDRTYW1wbGVMZW5ndGggKz0gNCArIHVuaXREYXRhTGVuO1xuICAgICAgfVxuXG4gICAgICAvLyBleHBlY3RlZCBzYW1wbGUgZHVyYXRpb24gaXMgdGhlIERlY29kaW5nIFRpbWVzdGFtcCBkaWZmIG9mIGNvbnNlY3V0aXZlIHNhbXBsZXNcbiAgICAgIGxldCBwdHNEZWx0YTtcbiAgICAgIGlmIChpIDwgbmJTYW1wbGVzIC0gMSkge1xuICAgICAgICBtcDRTYW1wbGVEdXJhdGlvbiA9IGlucHV0U2FtcGxlc1tpICsgMV0uZHRzIC0gVmlkZW9TYW1wbGUuZHRzO1xuICAgICAgICBwdHNEZWx0YSA9IGlucHV0U2FtcGxlc1tpICsgMV0ucHRzIC0gVmlkZW9TYW1wbGUucHRzO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3QgY29uZmlnID0gdGhpcy5jb25maWc7XG4gICAgICAgIGNvbnN0IGxhc3RGcmFtZUR1cmF0aW9uID0gaSA+IDAgPyBWaWRlb1NhbXBsZS5kdHMgLSBpbnB1dFNhbXBsZXNbaSAtIDFdLmR0cyA6IGF2ZXJhZ2VTYW1wbGVEdXJhdGlvbjtcbiAgICAgICAgcHRzRGVsdGEgPSBpID4gMCA/IFZpZGVvU2FtcGxlLnB0cyAtIGlucHV0U2FtcGxlc1tpIC0gMV0ucHRzIDogYXZlcmFnZVNhbXBsZUR1cmF0aW9uO1xuICAgICAgICBpZiAoY29uZmlnLnN0cmV0Y2hTaG9ydFZpZGVvVHJhY2sgJiYgdGhpcy5uZXh0QXVkaW9QdHMgIT09IG51bGwpIHtcbiAgICAgICAgICAvLyBJbiBzb21lIGNhc2VzLCBhIHNlZ21lbnQncyBhdWRpbyB0cmFjayBkdXJhdGlvbiBtYXkgZXhjZWVkIHRoZSB2aWRlbyB0cmFjayBkdXJhdGlvbi5cbiAgICAgICAgICAvLyBTaW5jZSB3ZSd2ZSBhbHJlYWR5IHJlbXV4ZWQgYXVkaW8sIGFuZCB3ZSBrbm93IGhvdyBsb25nIHRoZSBhdWRpbyB0cmFjayBpcywgd2UgbG9vayB0b1xuICAgICAgICAgIC8vIHNlZSBpZiB0aGUgZGVsdGEgdG8gdGhlIG5leHQgc2VnbWVudCBpcyBsb25nZXIgdGhhbiBtYXhCdWZmZXJIb2xlLlxuICAgICAgICAgIC8vIElmIHNvLCBwbGF5YmFjayB3b3VsZCBwb3RlbnRpYWxseSBnZXQgc3R1Y2ssIHNvIHdlIGFydGlmaWNpYWxseSBpbmZsYXRlXG4gICAgICAgICAgLy8gdGhlIGR1cmF0aW9uIG9mIHRoZSBsYXN0IGZyYW1lIHRvIG1pbmltaXplIGFueSBwb3RlbnRpYWwgZ2FwIGJldHdlZW4gc2VnbWVudHMuXG4gICAgICAgICAgY29uc3QgZ2FwVG9sZXJhbmNlID0gTWF0aC5mbG9vcihjb25maWcubWF4QnVmZmVySG9sZSAqIHRpbWVTY2FsZSk7XG4gICAgICAgICAgY29uc3QgZGVsdGFUb0ZyYW1lRW5kID0gKGF1ZGlvVHJhY2tMZW5ndGggPyBtaW5QVFMgKyBhdWRpb1RyYWNrTGVuZ3RoICogdGltZVNjYWxlIDogdGhpcy5uZXh0QXVkaW9QdHMpIC0gVmlkZW9TYW1wbGUucHRzO1xuICAgICAgICAgIGlmIChkZWx0YVRvRnJhbWVFbmQgPiBnYXBUb2xlcmFuY2UpIHtcbiAgICAgICAgICAgIC8vIFdlIHN1YnRyYWN0IGxhc3RGcmFtZUR1cmF0aW9uIGZyb20gZGVsdGFUb0ZyYW1lRW5kIHRvIHRyeSB0byBwcmV2ZW50IGFueSB2aWRlb1xuICAgICAgICAgICAgLy8gZnJhbWUgb3ZlcmxhcC4gbWF4QnVmZmVySG9sZSBzaG91bGQgYmUgPj4gbGFzdEZyYW1lRHVyYXRpb24gYW55d2F5LlxuICAgICAgICAgICAgbXA0U2FtcGxlRHVyYXRpb24gPSBkZWx0YVRvRnJhbWVFbmQgLSBsYXN0RnJhbWVEdXJhdGlvbjtcbiAgICAgICAgICAgIGlmIChtcDRTYW1wbGVEdXJhdGlvbiA8IDApIHtcbiAgICAgICAgICAgICAgbXA0U2FtcGxlRHVyYXRpb24gPSBsYXN0RnJhbWVEdXJhdGlvbjtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIHN0cmV0Y2hlZExhc3RGcmFtZSA9IHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBsb2dnZXIubG9nKGBbbXA0LXJlbXV4ZXJdOiBJdCBpcyBhcHByb3hpbWF0ZWx5ICR7ZGVsdGFUb0ZyYW1lRW5kIC8gOTB9IG1zIHRvIHRoZSBuZXh0IHNlZ21lbnQ7IHVzaW5nIGR1cmF0aW9uICR7bXA0U2FtcGxlRHVyYXRpb24gLyA5MH0gbXMgZm9yIHRoZSBsYXN0IHZpZGVvIGZyYW1lLmApO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBtcDRTYW1wbGVEdXJhdGlvbiA9IGxhc3RGcmFtZUR1cmF0aW9uO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBtcDRTYW1wbGVEdXJhdGlvbiA9IGxhc3RGcmFtZUR1cmF0aW9uO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBjb25zdCBjb21wb3NpdGlvblRpbWVPZmZzZXQgPSBNYXRoLnJvdW5kKFZpZGVvU2FtcGxlLnB0cyAtIFZpZGVvU2FtcGxlLmR0cyk7XG4gICAgICBtaW5EdHNEZWx0YSA9IE1hdGgubWluKG1pbkR0c0RlbHRhLCBtcDRTYW1wbGVEdXJhdGlvbik7XG4gICAgICBtYXhEdHNEZWx0YSA9IE1hdGgubWF4KG1heER0c0RlbHRhLCBtcDRTYW1wbGVEdXJhdGlvbik7XG4gICAgICBtaW5QdHNEZWx0YSA9IE1hdGgubWluKG1pblB0c0RlbHRhLCBwdHNEZWx0YSk7XG4gICAgICBtYXhQdHNEZWx0YSA9IE1hdGgubWF4KG1heFB0c0RlbHRhLCBwdHNEZWx0YSk7XG4gICAgICBvdXRwdXRTYW1wbGVzLnB1c2gobmV3IE1wNFNhbXBsZShWaWRlb1NhbXBsZS5rZXksIG1wNFNhbXBsZUR1cmF0aW9uLCBtcDRTYW1wbGVMZW5ndGgsIGNvbXBvc2l0aW9uVGltZU9mZnNldCkpO1xuICAgIH1cbiAgICBpZiAob3V0cHV0U2FtcGxlcy5sZW5ndGgpIHtcbiAgICAgIGlmIChjaHJvbWVWZXJzaW9uKSB7XG4gICAgICAgIGlmIChjaHJvbWVWZXJzaW9uIDwgNzApIHtcbiAgICAgICAgICAvLyBDaHJvbWUgd29ya2Fyb3VuZCwgbWFyayBmaXJzdCBzYW1wbGUgYXMgYmVpbmcgYSBSYW5kb20gQWNjZXNzIFBvaW50IChrZXlmcmFtZSkgdG8gYXZvaWQgc291cmNlYnVmZmVyIGFwcGVuZCBpc3N1ZVxuICAgICAgICAgIC8vIGh0dHBzOi8vY29kZS5nb29nbGUuY29tL3AvY2hyb21pdW0vaXNzdWVzL2RldGFpbD9pZD0yMjk0MTJcbiAgICAgICAgICBjb25zdCBmbGFncyA9IG91dHB1dFNhbXBsZXNbMF0uZmxhZ3M7XG4gICAgICAgICAgZmxhZ3MuZGVwZW5kc09uID0gMjtcbiAgICAgICAgICBmbGFncy5pc05vblN5bmMgPSAwO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKHNhZmFyaVdlYmtpdFZlcnNpb24pIHtcbiAgICAgICAgLy8gRml4IGZvciBcIkNOTiBzcGVjaWFsIHJlcG9ydCwgd2l0aCBDQ1wiIGluIHRlc3Qtc3RyZWFtcyAoU2FmYXJpIGJyb3dzZXIgb25seSlcbiAgICAgICAgLy8gSWdub3JlIERUUyB3aGVuIGZyYW1lIGR1cmF0aW9ucyBhcmUgaXJyZWd1bGFyLiBTYWZhcmkgTVNFIGRvZXMgbm90IGhhbmRsZSB0aGlzIGxlYWRpbmcgdG8gZ2Fwcy5cbiAgICAgICAgaWYgKG1heFB0c0RlbHRhIC0gbWluUHRzRGVsdGEgPCBtYXhEdHNEZWx0YSAtIG1pbkR0c0RlbHRhICYmIGF2ZXJhZ2VTYW1wbGVEdXJhdGlvbiAvIG1heER0c0RlbHRhIDwgMC4wMjUgJiYgb3V0cHV0U2FtcGxlc1swXS5jdHMgPT09IDApIHtcbiAgICAgICAgICBsb2dnZXIud2FybignRm91bmQgaXJyZWd1bGFyIGdhcHMgaW4gc2FtcGxlIGR1cmF0aW9uLiBVc2luZyBQVFMgaW5zdGVhZCBvZiBEVFMgdG8gZGV0ZXJtaW5lIE1QNCBzYW1wbGUgZHVyYXRpb24uJyk7XG4gICAgICAgICAgbGV0IGR0cyA9IGZpcnN0RFRTO1xuICAgICAgICAgIGZvciAobGV0IGkgPSAwLCBsZW4gPSBvdXRwdXRTYW1wbGVzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG4gICAgICAgICAgICBjb25zdCBuZXh0RHRzID0gZHRzICsgb3V0cHV0U2FtcGxlc1tpXS5kdXJhdGlvbjtcbiAgICAgICAgICAgIGNvbnN0IHB0cyA9IGR0cyArIG91dHB1dFNhbXBsZXNbaV0uY3RzO1xuICAgICAgICAgICAgaWYgKGkgPCBsZW4gLSAxKSB7XG4gICAgICAgICAgICAgIGNvbnN0IG5leHRQdHMgPSBuZXh0RHRzICsgb3V0cHV0U2FtcGxlc1tpICsgMV0uY3RzO1xuICAgICAgICAgICAgICBvdXRwdXRTYW1wbGVzW2ldLmR1cmF0aW9uID0gbmV4dFB0cyAtIHB0cztcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIG91dHB1dFNhbXBsZXNbaV0uZHVyYXRpb24gPSBpID8gb3V0cHV0U2FtcGxlc1tpIC0gMV0uZHVyYXRpb24gOiBhdmVyYWdlU2FtcGxlRHVyYXRpb247XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBvdXRwdXRTYW1wbGVzW2ldLmN0cyA9IDA7XG4gICAgICAgICAgICBkdHMgPSBuZXh0RHRzO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICAvLyBuZXh0IEFWQyBzYW1wbGUgRFRTIHNob3VsZCBiZSBlcXVhbCB0byBsYXN0IHNhbXBsZSBEVFMgKyBsYXN0IHNhbXBsZSBkdXJhdGlvbiAoaW4gUEVTIHRpbWVzY2FsZSlcbiAgICBtcDRTYW1wbGVEdXJhdGlvbiA9IHN0cmV0Y2hlZExhc3RGcmFtZSB8fCAhbXA0U2FtcGxlRHVyYXRpb24gPyBhdmVyYWdlU2FtcGxlRHVyYXRpb24gOiBtcDRTYW1wbGVEdXJhdGlvbjtcbiAgICB0aGlzLm5leHRBdmNEdHMgPSBuZXh0QXZjRHRzID0gbGFzdERUUyArIG1wNFNhbXBsZUR1cmF0aW9uO1xuICAgIHRoaXMudmlkZW9TYW1wbGVEdXJhdGlvbiA9IG1wNFNhbXBsZUR1cmF0aW9uO1xuICAgIHRoaXMuaXNWaWRlb0NvbnRpZ3VvdXMgPSB0cnVlO1xuICAgIGNvbnN0IG1vb2YgPSBNUDQubW9vZih0cmFjay5zZXF1ZW5jZU51bWJlcisrLCBmaXJzdERUUywgX2V4dGVuZHMoe30sIHRyYWNrLCB7XG4gICAgICBzYW1wbGVzOiBvdXRwdXRTYW1wbGVzXG4gICAgfSkpO1xuICAgIGNvbnN0IHR5cGUgPSAndmlkZW8nO1xuICAgIGNvbnN0IGRhdGEgPSB7XG4gICAgICBkYXRhMTogbW9vZixcbiAgICAgIGRhdGEyOiBtZGF0LFxuICAgICAgc3RhcnRQVFM6IG1pblBUUyAvIHRpbWVTY2FsZSxcbiAgICAgIGVuZFBUUzogKG1heFBUUyArIG1wNFNhbXBsZUR1cmF0aW9uKSAvIHRpbWVTY2FsZSxcbiAgICAgIHN0YXJ0RFRTOiBmaXJzdERUUyAvIHRpbWVTY2FsZSxcbiAgICAgIGVuZERUUzogbmV4dEF2Y0R0cyAvIHRpbWVTY2FsZSxcbiAgICAgIHR5cGUsXG4gICAgICBoYXNBdWRpbzogZmFsc2UsXG4gICAgICBoYXNWaWRlbzogdHJ1ZSxcbiAgICAgIG5iOiBvdXRwdXRTYW1wbGVzLmxlbmd0aCxcbiAgICAgIGRyb3BwZWQ6IHRyYWNrLmRyb3BwZWRcbiAgICB9O1xuICAgIHRyYWNrLnNhbXBsZXMgPSBbXTtcbiAgICB0cmFjay5kcm9wcGVkID0gMDtcbiAgICByZXR1cm4gZGF0YTtcbiAgfVxuICBnZXRTYW1wbGVzUGVyRnJhbWUodHJhY2spIHtcbiAgICBzd2l0Y2ggKHRyYWNrLnNlZ21lbnRDb2RlYykge1xuICAgICAgY2FzZSAnbXAzJzpcbiAgICAgICAgcmV0dXJuIE1QRUdfQVVESU9fU0FNUExFX1BFUl9GUkFNRTtcbiAgICAgIGNhc2UgJ2FjMyc6XG4gICAgICAgIHJldHVybiBBQzNfU0FNUExFU19QRVJfRlJBTUU7XG4gICAgICBkZWZhdWx0OlxuICAgICAgICByZXR1cm4gQUFDX1NBTVBMRVNfUEVSX0ZSQU1FO1xuICAgIH1cbiAgfVxuICByZW11eEF1ZGlvKHRyYWNrLCB0aW1lT2Zmc2V0LCBjb250aWd1b3VzLCBhY2N1cmF0ZVRpbWVPZmZzZXQsIHZpZGVvVGltZU9mZnNldCkge1xuICAgIGNvbnN0IGlucHV0VGltZVNjYWxlID0gdHJhY2suaW5wdXRUaW1lU2NhbGU7XG4gICAgY29uc3QgbXA0dGltZVNjYWxlID0gdHJhY2suc2FtcGxlcmF0ZSA/IHRyYWNrLnNhbXBsZXJhdGUgOiBpbnB1dFRpbWVTY2FsZTtcbiAgICBjb25zdCBzY2FsZUZhY3RvciA9IGlucHV0VGltZVNjYWxlIC8gbXA0dGltZVNjYWxlO1xuICAgIGNvbnN0IG1wNFNhbXBsZUR1cmF0aW9uID0gdGhpcy5nZXRTYW1wbGVzUGVyRnJhbWUodHJhY2spO1xuICAgIGNvbnN0IGlucHV0U2FtcGxlRHVyYXRpb24gPSBtcDRTYW1wbGVEdXJhdGlvbiAqIHNjYWxlRmFjdG9yO1xuICAgIGNvbnN0IGluaXRQVFMgPSB0aGlzLl9pbml0UFRTO1xuICAgIGNvbnN0IHJhd01QRUcgPSB0cmFjay5zZWdtZW50Q29kZWMgPT09ICdtcDMnICYmIHRoaXMudHlwZVN1cHBvcnRlZC5tcGVnO1xuICAgIGNvbnN0IG91dHB1dFNhbXBsZXMgPSBbXTtcbiAgICBjb25zdCBhbGlnbmVkV2l0aFZpZGVvID0gdmlkZW9UaW1lT2Zmc2V0ICE9PSB1bmRlZmluZWQ7XG4gICAgbGV0IGlucHV0U2FtcGxlcyA9IHRyYWNrLnNhbXBsZXM7XG4gICAgbGV0IG9mZnNldCA9IHJhd01QRUcgPyAwIDogODtcbiAgICBsZXQgbmV4dEF1ZGlvUHRzID0gdGhpcy5uZXh0QXVkaW9QdHMgfHwgLTE7XG5cbiAgICAvLyB3aW5kb3cuYXVkaW9TYW1wbGVzID8gd2luZG93LmF1ZGlvU2FtcGxlcy5wdXNoKGlucHV0U2FtcGxlcy5tYXAocyA9PiBzLnB0cykpIDogKHdpbmRvdy5hdWRpb1NhbXBsZXMgPSBbaW5wdXRTYW1wbGVzLm1hcChzID0+IHMucHRzKV0pO1xuXG4gICAgLy8gZm9yIGF1ZGlvIHNhbXBsZXMsIGFsc28gY29uc2lkZXIgY29uc2VjdXRpdmUgZnJhZ21lbnRzIGFzIGJlaW5nIGNvbnRpZ3VvdXMgKGV2ZW4gaWYgYSBsZXZlbCBzd2l0Y2ggb2NjdXJzKSxcbiAgICAvLyBmb3Igc2FrZSBvZiBjbGFyaXR5OlxuICAgIC8vIGNvbnNlY3V0aXZlIGZyYWdtZW50cyBhcmUgZnJhZ3Mgd2l0aFxuICAgIC8vICAtIGxlc3MgdGhhbiAxMDBtcyBnYXBzIGJldHdlZW4gbmV3IHRpbWUgb2Zmc2V0IChpZiBhY2N1cmF0ZSkgYW5kIG5leHQgZXhwZWN0ZWQgUFRTIE9SXG4gICAgLy8gIC0gbGVzcyB0aGFuIDIwIGF1ZGlvIGZyYW1lcyBkaXN0YW5jZVxuICAgIC8vIGNvbnRpZ3VvdXMgZnJhZ21lbnRzIGFyZSBjb25zZWN1dGl2ZSBmcmFnbWVudHMgZnJvbSBzYW1lIHF1YWxpdHkgbGV2ZWwgKHNhbWUgbGV2ZWwsIG5ldyBTTiA9IG9sZCBTTiArIDEpXG4gICAgLy8gdGhpcyBoZWxwcyBlbnN1cmluZyBhdWRpbyBjb250aW51aXR5XG4gICAgLy8gYW5kIHRoaXMgYWxzbyBhdm9pZHMgYXVkaW8gZ2xpdGNoZXMvY3V0IHdoZW4gc3dpdGNoaW5nIHF1YWxpdHksIG9yIHJlcG9ydGluZyB3cm9uZyBkdXJhdGlvbiBvbiBmaXJzdCBhdWRpbyBmcmFtZVxuICAgIGNvbnN0IHRpbWVPZmZzZXRNcGVnVFMgPSB0aW1lT2Zmc2V0ICogaW5wdXRUaW1lU2NhbGU7XG4gICAgY29uc3QgaW5pdFRpbWUgPSBpbml0UFRTLmJhc2VUaW1lICogaW5wdXRUaW1lU2NhbGUgLyBpbml0UFRTLnRpbWVzY2FsZTtcbiAgICB0aGlzLmlzQXVkaW9Db250aWd1b3VzID0gY29udGlndW91cyA9IGNvbnRpZ3VvdXMgfHwgaW5wdXRTYW1wbGVzLmxlbmd0aCAmJiBuZXh0QXVkaW9QdHMgPiAwICYmIChhY2N1cmF0ZVRpbWVPZmZzZXQgJiYgTWF0aC5hYnModGltZU9mZnNldE1wZWdUUyAtIG5leHRBdWRpb1B0cykgPCA5MDAwIHx8IE1hdGguYWJzKG5vcm1hbGl6ZVB0cyhpbnB1dFNhbXBsZXNbMF0ucHRzIC0gaW5pdFRpbWUsIHRpbWVPZmZzZXRNcGVnVFMpIC0gbmV4dEF1ZGlvUHRzKSA8IDIwICogaW5wdXRTYW1wbGVEdXJhdGlvbik7XG5cbiAgICAvLyBjb21wdXRlIG5vcm1hbGl6ZWQgUFRTXG4gICAgaW5wdXRTYW1wbGVzLmZvckVhY2goZnVuY3Rpb24gKHNhbXBsZSkge1xuICAgICAgc2FtcGxlLnB0cyA9IG5vcm1hbGl6ZVB0cyhzYW1wbGUucHRzIC0gaW5pdFRpbWUsIHRpbWVPZmZzZXRNcGVnVFMpO1xuICAgIH0pO1xuICAgIGlmICghY29udGlndW91cyB8fCBuZXh0QXVkaW9QdHMgPCAwKSB7XG4gICAgICAvLyBmaWx0ZXIgb3V0IHNhbXBsZSB3aXRoIG5lZ2F0aXZlIFBUUyB0aGF0IGFyZSBub3QgcGxheWFibGUgYW55d2F5XG4gICAgICAvLyBpZiB3ZSBkb24ndCByZW1vdmUgdGhlc2UgbmVnYXRpdmUgc2FtcGxlcywgdGhleSB3aWxsIHNoaWZ0IGFsbCBhdWRpbyBzYW1wbGVzIGZvcndhcmQuXG4gICAgICAvLyBsZWFkaW5nIHRvIGF1ZGlvIG92ZXJsYXAgYmV0d2VlbiBjdXJyZW50IC8gbmV4dCBmcmFnbWVudFxuICAgICAgaW5wdXRTYW1wbGVzID0gaW5wdXRTYW1wbGVzLmZpbHRlcihzYW1wbGUgPT4gc2FtcGxlLnB0cyA+PSAwKTtcblxuICAgICAgLy8gaW4gY2FzZSBhbGwgc2FtcGxlcyBoYXZlIG5lZ2F0aXZlIFBUUywgYW5kIGhhdmUgYmVlbiBmaWx0ZXJlZCBvdXQsIHJldHVybiBub3dcbiAgICAgIGlmICghaW5wdXRTYW1wbGVzLmxlbmd0aCkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBpZiAodmlkZW9UaW1lT2Zmc2V0ID09PSAwKSB7XG4gICAgICAgIC8vIFNldCB0aGUgc3RhcnQgdG8gMCB0byBtYXRjaCB2aWRlbyBzbyB0aGF0IHN0YXJ0IGdhcHMgbGFyZ2VyIHRoYW4gaW5wdXRTYW1wbGVEdXJhdGlvbiBhcmUgZmlsbGVkIHdpdGggc2lsZW5jZVxuICAgICAgICBuZXh0QXVkaW9QdHMgPSAwO1xuICAgICAgfSBlbHNlIGlmIChhY2N1cmF0ZVRpbWVPZmZzZXQgJiYgIWFsaWduZWRXaXRoVmlkZW8pIHtcbiAgICAgICAgLy8gV2hlbiBub3Qgc2Vla2luZywgbm90IGxpdmUsIGFuZCBMZXZlbERldGFpbHMuUFRTS25vd24sIHVzZSBmcmFnbWVudCBzdGFydCBhcyBwcmVkaWN0ZWQgbmV4dCBhdWRpbyBQVFNcbiAgICAgICAgbmV4dEF1ZGlvUHRzID0gTWF0aC5tYXgoMCwgdGltZU9mZnNldE1wZWdUUyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBpZiBmcmFncyBhcmUgbm90IGNvbnRpZ3VvdXMgYW5kIGlmIHdlIGNhbnQgdHJ1c3QgdGltZSBvZmZzZXQsIGxldCdzIHVzZSBmaXJzdCBzYW1wbGUgUFRTIGFzIG5leHQgYXVkaW8gUFRTXG4gICAgICAgIG5leHRBdWRpb1B0cyA9IGlucHV0U2FtcGxlc1swXS5wdHM7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gSWYgdGhlIGF1ZGlvIHRyYWNrIGlzIG1pc3Npbmcgc2FtcGxlcywgdGhlIGZyYW1lcyBzZWVtIHRvIGdldCBcImxlZnQtc2hpZnRlZFwiIHdpdGhpbiB0aGVcbiAgICAvLyByZXN1bHRpbmcgbXA0IHNlZ21lbnQsIGNhdXNpbmcgc3luYyBpc3N1ZXMgYW5kIGxlYXZpbmcgZ2FwcyBhdCB0aGUgZW5kIG9mIHRoZSBhdWRpbyBzZWdtZW50LlxuICAgIC8vIEluIGFuIGVmZm9ydCB0byBwcmV2ZW50IHRoaXMgZnJvbSBoYXBwZW5pbmcsIHdlIGluamVjdCBmcmFtZXMgaGVyZSB3aGVyZSB0aGVyZSBhcmUgZ2Fwcy5cbiAgICAvLyBXaGVuIHBvc3NpYmxlLCB3ZSBpbmplY3QgYSBzaWxlbnQgZnJhbWU7IHdoZW4gdGhhdCdzIG5vdCBwb3NzaWJsZSwgd2UgZHVwbGljYXRlIHRoZSBsYXN0XG4gICAgLy8gZnJhbWUuXG5cbiAgICBpZiAodHJhY2suc2VnbWVudENvZGVjID09PSAnYWFjJykge1xuICAgICAgY29uc3QgbWF4QXVkaW9GcmFtZXNEcmlmdCA9IHRoaXMuY29uZmlnLm1heEF1ZGlvRnJhbWVzRHJpZnQ7XG4gICAgICBmb3IgKGxldCBpID0gMCwgbmV4dFB0cyA9IG5leHRBdWRpb1B0czsgaSA8IGlucHV0U2FtcGxlcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAvLyBGaXJzdCwgbGV0J3Mgc2VlIGhvdyBmYXIgb2ZmIHRoaXMgZnJhbWUgaXMgZnJvbSB3aGVyZSB3ZSBleHBlY3QgaXQgdG8gYmVcbiAgICAgICAgY29uc3Qgc2FtcGxlID0gaW5wdXRTYW1wbGVzW2ldO1xuICAgICAgICBjb25zdCBwdHMgPSBzYW1wbGUucHRzO1xuICAgICAgICBjb25zdCBkZWx0YSA9IHB0cyAtIG5leHRQdHM7XG4gICAgICAgIGNvbnN0IGR1cmF0aW9uID0gTWF0aC5hYnMoMTAwMCAqIGRlbHRhIC8gaW5wdXRUaW1lU2NhbGUpO1xuXG4gICAgICAgIC8vIFdoZW4gcmVtdXhpbmcgd2l0aCB2aWRlbywgaWYgd2UncmUgb3ZlcmxhcHBpbmcgYnkgbW9yZSB0aGFuIGEgZHVyYXRpb24sIGRyb3AgdGhpcyBzYW1wbGUgdG8gc3RheSBpbiBzeW5jXG4gICAgICAgIGlmIChkZWx0YSA8PSAtbWF4QXVkaW9GcmFtZXNEcmlmdCAqIGlucHV0U2FtcGxlRHVyYXRpb24gJiYgYWxpZ25lZFdpdGhWaWRlbykge1xuICAgICAgICAgIGlmIChpID09PSAwKSB7XG4gICAgICAgICAgICBsb2dnZXIud2FybihgQXVkaW8gZnJhbWUgQCAkeyhwdHMgLyBpbnB1dFRpbWVTY2FsZSkudG9GaXhlZCgzKX1zIG92ZXJsYXBzIG5leHRBdWRpb1B0cyBieSAke01hdGgucm91bmQoMTAwMCAqIGRlbHRhIC8gaW5wdXRUaW1lU2NhbGUpfSBtcy5gKTtcbiAgICAgICAgICAgIHRoaXMubmV4dEF1ZGlvUHRzID0gbmV4dEF1ZGlvUHRzID0gbmV4dFB0cyA9IHB0cztcbiAgICAgICAgICB9XG4gICAgICAgIH0gLy8gZXNsaW50LWRpc2FibGUtbGluZSBicmFjZS1zdHlsZVxuXG4gICAgICAgIC8vIEluc2VydCBtaXNzaW5nIGZyYW1lcyBpZjpcbiAgICAgICAgLy8gMTogV2UncmUgbW9yZSB0aGFuIG1heEF1ZGlvRnJhbWVzRHJpZnQgZnJhbWUgYXdheVxuICAgICAgICAvLyAyOiBOb3QgbW9yZSB0aGFuIE1BWF9TSUxFTlRfRlJBTUVfRFVSQVRJT04gYXdheVxuICAgICAgICAvLyAzOiBjdXJyZW50VGltZSAoYWthIG5leHRQdHNOb3JtKSBpcyBub3QgMFxuICAgICAgICAvLyA0OiByZW11eGluZyB3aXRoIHZpZGVvICh2aWRlb1RpbWVPZmZzZXQgIT09IHVuZGVmaW5lZClcbiAgICAgICAgZWxzZSBpZiAoZGVsdGEgPj0gbWF4QXVkaW9GcmFtZXNEcmlmdCAqIGlucHV0U2FtcGxlRHVyYXRpb24gJiYgZHVyYXRpb24gPCBNQVhfU0lMRU5UX0ZSQU1FX0RVUkFUSU9OICYmIGFsaWduZWRXaXRoVmlkZW8pIHtcbiAgICAgICAgICBsZXQgbWlzc2luZyA9IE1hdGgucm91bmQoZGVsdGEgLyBpbnB1dFNhbXBsZUR1cmF0aW9uKTtcbiAgICAgICAgICAvLyBBZGp1c3QgbmV4dFB0cyBzbyB0aGF0IHNpbGVudCBzYW1wbGVzIGFyZSBhbGlnbmVkIHdpdGggbWVkaWEgcHRzLiBUaGlzIHdpbGwgcHJldmVudCBtZWRpYSBzYW1wbGVzIGZyb21cbiAgICAgICAgICAvLyBsYXRlciBiZWluZyBzaGlmdGVkIGlmIG5leHRQdHMgaXMgYmFzZWQgb24gdGltZU9mZnNldCBhbmQgZGVsdGEgaXMgbm90IGEgbXVsdGlwbGUgb2YgaW5wdXRTYW1wbGVEdXJhdGlvbi5cbiAgICAgICAgICBuZXh0UHRzID0gcHRzIC0gbWlzc2luZyAqIGlucHV0U2FtcGxlRHVyYXRpb247XG4gICAgICAgICAgaWYgKG5leHRQdHMgPCAwKSB7XG4gICAgICAgICAgICBtaXNzaW5nLS07XG4gICAgICAgICAgICBuZXh0UHRzICs9IGlucHV0U2FtcGxlRHVyYXRpb247XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChpID09PSAwKSB7XG4gICAgICAgICAgICB0aGlzLm5leHRBdWRpb1B0cyA9IG5leHRBdWRpb1B0cyA9IG5leHRQdHM7XG4gICAgICAgICAgfVxuICAgICAgICAgIGxvZ2dlci53YXJuKGBbbXA0LXJlbXV4ZXJdOiBJbmplY3RpbmcgJHttaXNzaW5nfSBhdWRpbyBmcmFtZSBAICR7KG5leHRQdHMgLyBpbnB1dFRpbWVTY2FsZSkudG9GaXhlZCgzKX1zIGR1ZSB0byAke01hdGgucm91bmQoMTAwMCAqIGRlbHRhIC8gaW5wdXRUaW1lU2NhbGUpfSBtcyBnYXAuYCk7XG4gICAgICAgICAgZm9yIChsZXQgaiA9IDA7IGogPCBtaXNzaW5nOyBqKyspIHtcbiAgICAgICAgICAgIGNvbnN0IG5ld1N0YW1wID0gTWF0aC5tYXgobmV4dFB0cywgMCk7XG4gICAgICAgICAgICBsZXQgZmlsbEZyYW1lID0gQUFDLmdldFNpbGVudEZyYW1lKHRyYWNrLm1hbmlmZXN0Q29kZWMgfHwgdHJhY2suY29kZWMsIHRyYWNrLmNoYW5uZWxDb3VudCk7XG4gICAgICAgICAgICBpZiAoIWZpbGxGcmFtZSkge1xuICAgICAgICAgICAgICBsb2dnZXIubG9nKCdbbXA0LXJlbXV4ZXJdOiBVbmFibGUgdG8gZ2V0IHNpbGVudCBmcmFtZSBmb3IgZ2l2ZW4gYXVkaW8gY29kZWM7IGR1cGxpY2F0aW5nIGxhc3QgZnJhbWUgaW5zdGVhZC4nKTtcbiAgICAgICAgICAgICAgZmlsbEZyYW1lID0gc2FtcGxlLnVuaXQuc3ViYXJyYXkoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlucHV0U2FtcGxlcy5zcGxpY2UoaSwgMCwge1xuICAgICAgICAgICAgICB1bml0OiBmaWxsRnJhbWUsXG4gICAgICAgICAgICAgIHB0czogbmV3U3RhbXBcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgbmV4dFB0cyArPSBpbnB1dFNhbXBsZUR1cmF0aW9uO1xuICAgICAgICAgICAgaSsrO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBzYW1wbGUucHRzID0gbmV4dFB0cztcbiAgICAgICAgbmV4dFB0cyArPSBpbnB1dFNhbXBsZUR1cmF0aW9uO1xuICAgICAgfVxuICAgIH1cbiAgICBsZXQgZmlyc3RQVFMgPSBudWxsO1xuICAgIGxldCBsYXN0UFRTID0gbnVsbDtcbiAgICBsZXQgbWRhdDtcbiAgICBsZXQgbWRhdFNpemUgPSAwO1xuICAgIGxldCBzYW1wbGVMZW5ndGggPSBpbnB1dFNhbXBsZXMubGVuZ3RoO1xuICAgIHdoaWxlIChzYW1wbGVMZW5ndGgtLSkge1xuICAgICAgbWRhdFNpemUgKz0gaW5wdXRTYW1wbGVzW3NhbXBsZUxlbmd0aF0udW5pdC5ieXRlTGVuZ3RoO1xuICAgIH1cbiAgICBmb3IgKGxldCBqID0gMCwgX25iU2FtcGxlcyA9IGlucHV0U2FtcGxlcy5sZW5ndGg7IGogPCBfbmJTYW1wbGVzOyBqKyspIHtcbiAgICAgIGNvbnN0IGF1ZGlvU2FtcGxlID0gaW5wdXRTYW1wbGVzW2pdO1xuICAgICAgY29uc3QgdW5pdCA9IGF1ZGlvU2FtcGxlLnVuaXQ7XG4gICAgICBsZXQgcHRzID0gYXVkaW9TYW1wbGUucHRzO1xuICAgICAgaWYgKGxhc3RQVFMgIT09IG51bGwpIHtcbiAgICAgICAgLy8gSWYgd2UgaGF2ZSBtb3JlIHRoYW4gb25lIHNhbXBsZSwgc2V0IHRoZSBkdXJhdGlvbiBvZiB0aGUgc2FtcGxlIHRvIHRoZSBcInJlYWxcIiBkdXJhdGlvbjsgdGhlIFBUUyBkaWZmIHdpdGhcbiAgICAgICAgLy8gdGhlIHByZXZpb3VzIHNhbXBsZVxuICAgICAgICBjb25zdCBwcmV2U2FtcGxlID0gb3V0cHV0U2FtcGxlc1tqIC0gMV07XG4gICAgICAgIHByZXZTYW1wbGUuZHVyYXRpb24gPSBNYXRoLnJvdW5kKChwdHMgLSBsYXN0UFRTKSAvIHNjYWxlRmFjdG9yKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGlmIChjb250aWd1b3VzICYmIHRyYWNrLnNlZ21lbnRDb2RlYyA9PT0gJ2FhYycpIHtcbiAgICAgICAgICAvLyBzZXQgUFRTL0RUUyB0byBleHBlY3RlZCBQVFMvRFRTXG4gICAgICAgICAgcHRzID0gbmV4dEF1ZGlvUHRzO1xuICAgICAgICB9XG4gICAgICAgIC8vIHJlbWVtYmVyIGZpcnN0IFBUUyBvZiBvdXIgYXVkaW9TYW1wbGVzXG4gICAgICAgIGZpcnN0UFRTID0gcHRzO1xuICAgICAgICBpZiAobWRhdFNpemUgPiAwKSB7XG4gICAgICAgICAgLyogY29uY2F0ZW5hdGUgdGhlIGF1ZGlvIGRhdGEgYW5kIGNvbnN0cnVjdCB0aGUgbWRhdCBpbiBwbGFjZVxuICAgICAgICAgICAgKG5lZWQgOCBtb3JlIGJ5dGVzIHRvIGZpbGwgbGVuZ3RoIGFuZCBtZGF0IHR5cGUpICovXG4gICAgICAgICAgbWRhdFNpemUgKz0gb2Zmc2V0O1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBtZGF0ID0gbmV3IFVpbnQ4QXJyYXkobWRhdFNpemUpO1xuICAgICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgdGhpcy5vYnNlcnZlci5lbWl0KEV2ZW50cy5FUlJPUiwgRXZlbnRzLkVSUk9SLCB7XG4gICAgICAgICAgICAgIHR5cGU6IEVycm9yVHlwZXMuTVVYX0VSUk9SLFxuICAgICAgICAgICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuUkVNVVhfQUxMT0NfRVJST1IsXG4gICAgICAgICAgICAgIGZhdGFsOiBmYWxzZSxcbiAgICAgICAgICAgICAgZXJyb3I6IGVycixcbiAgICAgICAgICAgICAgYnl0ZXM6IG1kYXRTaXplLFxuICAgICAgICAgICAgICByZWFzb246IGBmYWlsIGFsbG9jYXRpbmcgYXVkaW8gbWRhdCAke21kYXRTaXplfWBcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoIXJhd01QRUcpIHtcbiAgICAgICAgICAgIGNvbnN0IHZpZXcgPSBuZXcgRGF0YVZpZXcobWRhdC5idWZmZXIpO1xuICAgICAgICAgICAgdmlldy5zZXRVaW50MzIoMCwgbWRhdFNpemUpO1xuICAgICAgICAgICAgbWRhdC5zZXQoTVA0LnR5cGVzLm1kYXQsIDQpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBubyBhdWRpbyBzYW1wbGVzXG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBtZGF0LnNldCh1bml0LCBvZmZzZXQpO1xuICAgICAgY29uc3QgdW5pdExlbiA9IHVuaXQuYnl0ZUxlbmd0aDtcbiAgICAgIG9mZnNldCArPSB1bml0TGVuO1xuICAgICAgLy8gRGVmYXVsdCB0aGUgc2FtcGxlJ3MgZHVyYXRpb24gdG8gdGhlIGNvbXB1dGVkIG1wNFNhbXBsZUR1cmF0aW9uLCB3aGljaCB3aWxsIGVpdGhlciBiZSAxMDI0IGZvciBBQUMgb3IgMTE1MiBmb3IgTVBFR1xuICAgICAgLy8gSW4gdGhlIGNhc2UgdGhhdCB3ZSBoYXZlIDEgc2FtcGxlLCB0aGlzIHdpbGwgYmUgdGhlIGR1cmF0aW9uLiBJZiB3ZSBoYXZlIG1vcmUgdGhhbiBvbmUgc2FtcGxlLCB0aGUgZHVyYXRpb25cbiAgICAgIC8vIGJlY29tZXMgdGhlIFBUUyBkaWZmIHdpdGggdGhlIHByZXZpb3VzIHNhbXBsZVxuICAgICAgb3V0cHV0U2FtcGxlcy5wdXNoKG5ldyBNcDRTYW1wbGUodHJ1ZSwgbXA0U2FtcGxlRHVyYXRpb24sIHVuaXRMZW4sIDApKTtcbiAgICAgIGxhc3RQVFMgPSBwdHM7XG4gICAgfVxuXG4gICAgLy8gV2UgY291bGQgZW5kIHVwIHdpdGggbm8gYXVkaW8gc2FtcGxlcyBpZiBhbGwgaW5wdXQgc2FtcGxlcyB3ZXJlIG92ZXJsYXBwaW5nIHdpdGggdGhlIHByZXZpb3VzbHkgcmVtdXhlZCBvbmVzXG4gICAgY29uc3QgbmJTYW1wbGVzID0gb3V0cHV0U2FtcGxlcy5sZW5ndGg7XG4gICAgaWYgKCFuYlNhbXBsZXMpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBUaGUgbmV4dCBhdWRpbyBzYW1wbGUgUFRTIHNob3VsZCBiZSBlcXVhbCB0byBsYXN0IHNhbXBsZSBQVFMgKyBkdXJhdGlvblxuICAgIGNvbnN0IGxhc3RTYW1wbGUgPSBvdXRwdXRTYW1wbGVzW291dHB1dFNhbXBsZXMubGVuZ3RoIC0gMV07XG4gICAgdGhpcy5uZXh0QXVkaW9QdHMgPSBuZXh0QXVkaW9QdHMgPSBsYXN0UFRTICsgc2NhbGVGYWN0b3IgKiBsYXN0U2FtcGxlLmR1cmF0aW9uO1xuXG4gICAgLy8gU2V0IHRoZSB0cmFjayBzYW1wbGVzIGZyb20gaW5wdXRTYW1wbGVzIHRvIG91dHB1dFNhbXBsZXMgYmVmb3JlIHJlbXV4aW5nXG4gICAgY29uc3QgbW9vZiA9IHJhd01QRUcgPyBuZXcgVWludDhBcnJheSgwKSA6IE1QNC5tb29mKHRyYWNrLnNlcXVlbmNlTnVtYmVyKyssIGZpcnN0UFRTIC8gc2NhbGVGYWN0b3IsIF9leHRlbmRzKHt9LCB0cmFjaywge1xuICAgICAgc2FtcGxlczogb3V0cHV0U2FtcGxlc1xuICAgIH0pKTtcblxuICAgIC8vIENsZWFyIHRoZSB0cmFjayBzYW1wbGVzLiBUaGlzIGFsc28gY2xlYXJzIHRoZSBzYW1wbGVzIGFycmF5IGluIHRoZSBkZW11eGVyLCBzaW5jZSB0aGUgcmVmZXJlbmNlIGlzIHNoYXJlZFxuICAgIHRyYWNrLnNhbXBsZXMgPSBbXTtcbiAgICBjb25zdCBzdGFydCA9IGZpcnN0UFRTIC8gaW5wdXRUaW1lU2NhbGU7XG4gICAgY29uc3QgZW5kID0gbmV4dEF1ZGlvUHRzIC8gaW5wdXRUaW1lU2NhbGU7XG4gICAgY29uc3QgdHlwZSA9ICdhdWRpbyc7XG4gICAgY29uc3QgYXVkaW9EYXRhID0ge1xuICAgICAgZGF0YTE6IG1vb2YsXG4gICAgICBkYXRhMjogbWRhdCxcbiAgICAgIHN0YXJ0UFRTOiBzdGFydCxcbiAgICAgIGVuZFBUUzogZW5kLFxuICAgICAgc3RhcnREVFM6IHN0YXJ0LFxuICAgICAgZW5kRFRTOiBlbmQsXG4gICAgICB0eXBlLFxuICAgICAgaGFzQXVkaW86IHRydWUsXG4gICAgICBoYXNWaWRlbzogZmFsc2UsXG4gICAgICBuYjogbmJTYW1wbGVzXG4gICAgfTtcbiAgICB0aGlzLmlzQXVkaW9Db250aWd1b3VzID0gdHJ1ZTtcbiAgICByZXR1cm4gYXVkaW9EYXRhO1xuICB9XG4gIHJlbXV4RW1wdHlBdWRpbyh0cmFjaywgdGltZU9mZnNldCwgY29udGlndW91cywgdmlkZW9EYXRhKSB7XG4gICAgY29uc3QgaW5wdXRUaW1lU2NhbGUgPSB0cmFjay5pbnB1dFRpbWVTY2FsZTtcbiAgICBjb25zdCBtcDR0aW1lU2NhbGUgPSB0cmFjay5zYW1wbGVyYXRlID8gdHJhY2suc2FtcGxlcmF0ZSA6IGlucHV0VGltZVNjYWxlO1xuICAgIGNvbnN0IHNjYWxlRmFjdG9yID0gaW5wdXRUaW1lU2NhbGUgLyBtcDR0aW1lU2NhbGU7XG4gICAgY29uc3QgbmV4dEF1ZGlvUHRzID0gdGhpcy5uZXh0QXVkaW9QdHM7XG4gICAgLy8gc3luYyB3aXRoIHZpZGVvJ3MgdGltZXN0YW1wXG4gICAgY29uc3QgaW5pdERUUyA9IHRoaXMuX2luaXREVFM7XG4gICAgY29uc3QgaW5pdDkwa0h6ID0gaW5pdERUUy5iYXNlVGltZSAqIDkwMDAwIC8gaW5pdERUUy50aW1lc2NhbGU7XG4gICAgY29uc3Qgc3RhcnREVFMgPSAobmV4dEF1ZGlvUHRzICE9PSBudWxsID8gbmV4dEF1ZGlvUHRzIDogdmlkZW9EYXRhLnN0YXJ0RFRTICogaW5wdXRUaW1lU2NhbGUpICsgaW5pdDkwa0h6O1xuICAgIGNvbnN0IGVuZERUUyA9IHZpZGVvRGF0YS5lbmREVFMgKiBpbnB1dFRpbWVTY2FsZSArIGluaXQ5MGtIejtcbiAgICAvLyBvbmUgc2FtcGxlJ3MgZHVyYXRpb24gdmFsdWVcbiAgICBjb25zdCBmcmFtZUR1cmF0aW9uID0gc2NhbGVGYWN0b3IgKiBBQUNfU0FNUExFU19QRVJfRlJBTUU7XG4gICAgLy8gc2FtcGxlcyBjb3VudCBvZiB0aGlzIHNlZ21lbnQncyBkdXJhdGlvblxuICAgIGNvbnN0IG5iU2FtcGxlcyA9IE1hdGguY2VpbCgoZW5kRFRTIC0gc3RhcnREVFMpIC8gZnJhbWVEdXJhdGlvbik7XG4gICAgLy8gc2lsZW50IGZyYW1lXG4gICAgY29uc3Qgc2lsZW50RnJhbWUgPSBBQUMuZ2V0U2lsZW50RnJhbWUodHJhY2subWFuaWZlc3RDb2RlYyB8fCB0cmFjay5jb2RlYywgdHJhY2suY2hhbm5lbENvdW50KTtcbiAgICBsb2dnZXIud2FybignW21wNC1yZW11eGVyXTogcmVtdXggZW1wdHkgQXVkaW8nKTtcbiAgICAvLyBDYW4ndCByZW11eCBpZiB3ZSBjYW4ndCBnZW5lcmF0ZSBhIHNpbGVudCBmcmFtZS4uLlxuICAgIGlmICghc2lsZW50RnJhbWUpIHtcbiAgICAgIGxvZ2dlci50cmFjZSgnW21wNC1yZW11eGVyXTogVW5hYmxlIHRvIHJlbXV4RW1wdHlBdWRpbyBzaW5jZSB3ZSB3ZXJlIHVuYWJsZSB0byBnZXQgYSBzaWxlbnQgZnJhbWUgZm9yIGdpdmVuIGF1ZGlvIGNvZGVjJyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHNhbXBsZXMgPSBbXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG5iU2FtcGxlczsgaSsrKSB7XG4gICAgICBjb25zdCBzdGFtcCA9IHN0YXJ0RFRTICsgaSAqIGZyYW1lRHVyYXRpb247XG4gICAgICBzYW1wbGVzLnB1c2goe1xuICAgICAgICB1bml0OiBzaWxlbnRGcmFtZSxcbiAgICAgICAgcHRzOiBzdGFtcCxcbiAgICAgICAgZHRzOiBzdGFtcFxuICAgICAgfSk7XG4gICAgfVxuICAgIHRyYWNrLnNhbXBsZXMgPSBzYW1wbGVzO1xuICAgIHJldHVybiB0aGlzLnJlbXV4QXVkaW8odHJhY2ssIHRpbWVPZmZzZXQsIGNvbnRpZ3VvdXMsIGZhbHNlKTtcbiAgfVxufVxuZnVuY3Rpb24gbm9ybWFsaXplUHRzKHZhbHVlLCByZWZlcmVuY2UpIHtcbiAgbGV0IG9mZnNldDtcbiAgaWYgKHJlZmVyZW5jZSA9PT0gbnVsbCkge1xuICAgIHJldHVybiB2YWx1ZTtcbiAgfVxuICBpZiAocmVmZXJlbmNlIDwgdmFsdWUpIHtcbiAgICAvLyAtIDJeMzNcbiAgICBvZmZzZXQgPSAtODU4OTkzNDU5MjtcbiAgfSBlbHNlIHtcbiAgICAvLyArIDJeMzNcbiAgICBvZmZzZXQgPSA4NTg5OTM0NTkyO1xuICB9XG4gIC8qIFBUUyBpcyAzM2JpdCAoZnJvbSAwIHRvIDJeMzMgLTEpXG4gICAgaWYgZGlmZiBiZXR3ZWVuIHZhbHVlIGFuZCByZWZlcmVuY2UgaXMgYmlnZ2VyIHRoYW4gaGFsZiBvZiB0aGUgYW1wbGl0dWRlICgyXjMyKSB0aGVuIGl0IG1lYW5zIHRoYXRcbiAgICBQVFMgbG9vcGluZyBvY2N1cmVkLiBmaWxsIHRoZSBnYXAgKi9cbiAgd2hpbGUgKE1hdGguYWJzKHZhbHVlIC0gcmVmZXJlbmNlKSA+IDQyOTQ5NjcyOTYpIHtcbiAgICB2YWx1ZSArPSBvZmZzZXQ7XG4gIH1cbiAgcmV0dXJuIHZhbHVlO1xufVxuZnVuY3Rpb24gZmluZEtleWZyYW1lSW5kZXgoc2FtcGxlcykge1xuICBmb3IgKGxldCBpID0gMDsgaSA8IHNhbXBsZXMubGVuZ3RoOyBpKyspIHtcbiAgICBpZiAoc2FtcGxlc1tpXS5rZXkpIHtcbiAgICAgIHJldHVybiBpO1xuICAgIH1cbiAgfVxuICByZXR1cm4gLTE7XG59XG5mdW5jdGlvbiBmbHVzaFRleHRUcmFja01ldGFkYXRhQ3VlU2FtcGxlcyh0cmFjaywgdGltZU9mZnNldCwgaW5pdFBUUywgaW5pdERUUykge1xuICBjb25zdCBsZW5ndGggPSB0cmFjay5zYW1wbGVzLmxlbmd0aDtcbiAgaWYgKCFsZW5ndGgpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgY29uc3QgaW5wdXRUaW1lU2NhbGUgPSB0cmFjay5pbnB1dFRpbWVTY2FsZTtcbiAgZm9yIChsZXQgaW5kZXggPSAwOyBpbmRleCA8IGxlbmd0aDsgaW5kZXgrKykge1xuICAgIGNvbnN0IHNhbXBsZSA9IHRyYWNrLnNhbXBsZXNbaW5kZXhdO1xuICAgIC8vIHNldHRpbmcgaWQzIHB0cywgZHRzIHRvIHJlbGF0aXZlIHRpbWVcbiAgICAvLyB1c2luZyB0aGlzLl9pbml0UFRTIGFuZCB0aGlzLl9pbml0RFRTIHRvIGNhbGN1bGF0ZSByZWxhdGl2ZSB0aW1lXG4gICAgc2FtcGxlLnB0cyA9IG5vcm1hbGl6ZVB0cyhzYW1wbGUucHRzIC0gaW5pdFBUUy5iYXNlVGltZSAqIGlucHV0VGltZVNjYWxlIC8gaW5pdFBUUy50aW1lc2NhbGUsIHRpbWVPZmZzZXQgKiBpbnB1dFRpbWVTY2FsZSkgLyBpbnB1dFRpbWVTY2FsZTtcbiAgICBzYW1wbGUuZHRzID0gbm9ybWFsaXplUHRzKHNhbXBsZS5kdHMgLSBpbml0RFRTLmJhc2VUaW1lICogaW5wdXRUaW1lU2NhbGUgLyBpbml0RFRTLnRpbWVzY2FsZSwgdGltZU9mZnNldCAqIGlucHV0VGltZVNjYWxlKSAvIGlucHV0VGltZVNjYWxlO1xuICB9XG4gIGNvbnN0IHNhbXBsZXMgPSB0cmFjay5zYW1wbGVzO1xuICB0cmFjay5zYW1wbGVzID0gW107XG4gIHJldHVybiB7XG4gICAgc2FtcGxlc1xuICB9O1xufVxuZnVuY3Rpb24gZmx1c2hUZXh0VHJhY2tVc2VyZGF0YUN1ZVNhbXBsZXModHJhY2ssIHRpbWVPZmZzZXQsIGluaXRQVFMpIHtcbiAgY29uc3QgbGVuZ3RoID0gdHJhY2suc2FtcGxlcy5sZW5ndGg7XG4gIGlmICghbGVuZ3RoKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGNvbnN0IGlucHV0VGltZVNjYWxlID0gdHJhY2suaW5wdXRUaW1lU2NhbGU7XG4gIGZvciAobGV0IGluZGV4ID0gMDsgaW5kZXggPCBsZW5ndGg7IGluZGV4KyspIHtcbiAgICBjb25zdCBzYW1wbGUgPSB0cmFjay5zYW1wbGVzW2luZGV4XTtcbiAgICAvLyBzZXR0aW5nIHRleHQgcHRzLCBkdHMgdG8gcmVsYXRpdmUgdGltZVxuICAgIC8vIHVzaW5nIHRoaXMuX2luaXRQVFMgYW5kIHRoaXMuX2luaXREVFMgdG8gY2FsY3VsYXRlIHJlbGF0aXZlIHRpbWVcbiAgICBzYW1wbGUucHRzID0gbm9ybWFsaXplUHRzKHNhbXBsZS5wdHMgLSBpbml0UFRTLmJhc2VUaW1lICogaW5wdXRUaW1lU2NhbGUgLyBpbml0UFRTLnRpbWVzY2FsZSwgdGltZU9mZnNldCAqIGlucHV0VGltZVNjYWxlKSAvIGlucHV0VGltZVNjYWxlO1xuICB9XG4gIHRyYWNrLnNhbXBsZXMuc29ydCgoYSwgYikgPT4gYS5wdHMgLSBiLnB0cyk7XG4gIGNvbnN0IHNhbXBsZXMgPSB0cmFjay5zYW1wbGVzO1xuICB0cmFjay5zYW1wbGVzID0gW107XG4gIHJldHVybiB7XG4gICAgc2FtcGxlc1xuICB9O1xufVxuY2xhc3MgTXA0U2FtcGxlIHtcbiAgY29uc3RydWN0b3IoaXNLZXlmcmFtZSwgZHVyYXRpb24sIHNpemUsIGN0cykge1xuICAgIHRoaXMuc2l6ZSA9IHZvaWQgMDtcbiAgICB0aGlzLmR1cmF0aW9uID0gdm9pZCAwO1xuICAgIHRoaXMuY3RzID0gdm9pZCAwO1xuICAgIHRoaXMuZmxhZ3MgPSB2b2lkIDA7XG4gICAgdGhpcy5kdXJhdGlvbiA9IGR1cmF0aW9uO1xuICAgIHRoaXMuc2l6ZSA9IHNpemU7XG4gICAgdGhpcy5jdHMgPSBjdHM7XG4gICAgdGhpcy5mbGFncyA9IHtcbiAgICAgIGlzTGVhZGluZzogMCxcbiAgICAgIGlzRGVwZW5kZWRPbjogMCxcbiAgICAgIGhhc1JlZHVuZGFuY3k6IDAsXG4gICAgICBkZWdyYWRQcmlvOiAwLFxuICAgICAgZGVwZW5kc09uOiBpc0tleWZyYW1lID8gMiA6IDEsXG4gICAgICBpc05vblN5bmM6IGlzS2V5ZnJhbWUgPyAwIDogMVxuICAgIH07XG4gIH1cbn1cblxuY2xhc3MgUGFzc1Rocm91Z2hSZW11eGVyIHtcbiAgY29uc3RydWN0b3IoKSB7XG4gICAgdGhpcy5lbWl0SW5pdFNlZ21lbnQgPSBmYWxzZTtcbiAgICB0aGlzLmF1ZGlvQ29kZWMgPSB2b2lkIDA7XG4gICAgdGhpcy52aWRlb0NvZGVjID0gdm9pZCAwO1xuICAgIHRoaXMuaW5pdERhdGEgPSB2b2lkIDA7XG4gICAgdGhpcy5pbml0UFRTID0gbnVsbDtcbiAgICB0aGlzLmluaXRUcmFja3MgPSB2b2lkIDA7XG4gICAgdGhpcy5sYXN0RW5kVGltZSA9IG51bGw7XG4gIH1cbiAgZGVzdHJveSgpIHt9XG4gIHJlc2V0VGltZVN0YW1wKGRlZmF1bHRJbml0UFRTKSB7XG4gICAgdGhpcy5pbml0UFRTID0gZGVmYXVsdEluaXRQVFM7XG4gICAgdGhpcy5sYXN0RW5kVGltZSA9IG51bGw7XG4gIH1cbiAgcmVzZXROZXh0VGltZXN0YW1wKCkge1xuICAgIHRoaXMubGFzdEVuZFRpbWUgPSBudWxsO1xuICB9XG4gIHJlc2V0SW5pdFNlZ21lbnQoaW5pdFNlZ21lbnQsIGF1ZGlvQ29kZWMsIHZpZGVvQ29kZWMsIGRlY3J5cHRkYXRhKSB7XG4gICAgdGhpcy5hdWRpb0NvZGVjID0gYXVkaW9Db2RlYztcbiAgICB0aGlzLnZpZGVvQ29kZWMgPSB2aWRlb0NvZGVjO1xuICAgIHRoaXMuZ2VuZXJhdGVJbml0U2VnbWVudChwYXRjaEVuY3lwdGlvbkRhdGEoaW5pdFNlZ21lbnQsIGRlY3J5cHRkYXRhKSk7XG4gICAgdGhpcy5lbWl0SW5pdFNlZ21lbnQgPSB0cnVlO1xuICB9XG4gIGdlbmVyYXRlSW5pdFNlZ21lbnQoaW5pdFNlZ21lbnQpIHtcbiAgICBsZXQge1xuICAgICAgYXVkaW9Db2RlYyxcbiAgICAgIHZpZGVvQ29kZWNcbiAgICB9ID0gdGhpcztcbiAgICBpZiAoIShpbml0U2VnbWVudCAhPSBudWxsICYmIGluaXRTZWdtZW50LmJ5dGVMZW5ndGgpKSB7XG4gICAgICB0aGlzLmluaXRUcmFja3MgPSB1bmRlZmluZWQ7XG4gICAgICB0aGlzLmluaXREYXRhID0gdW5kZWZpbmVkO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBpbml0RGF0YSA9IHRoaXMuaW5pdERhdGEgPSBwYXJzZUluaXRTZWdtZW50KGluaXRTZWdtZW50KTtcblxuICAgIC8vIEdldCBjb2RlYyBmcm9tIGluaXRTZWdtZW50IG9yIGZhbGxiYWNrIHRvIGRlZmF1bHRcbiAgICBpZiAoaW5pdERhdGEuYXVkaW8pIHtcbiAgICAgIGF1ZGlvQ29kZWMgPSBnZXRQYXJzZWRUcmFja0NvZGVjKGluaXREYXRhLmF1ZGlvLCBFbGVtZW50YXJ5U3RyZWFtVHlwZXMuQVVESU8pO1xuICAgIH1cbiAgICBpZiAoaW5pdERhdGEudmlkZW8pIHtcbiAgICAgIHZpZGVvQ29kZWMgPSBnZXRQYXJzZWRUcmFja0NvZGVjKGluaXREYXRhLnZpZGVvLCBFbGVtZW50YXJ5U3RyZWFtVHlwZXMuVklERU8pO1xuICAgIH1cbiAgICBjb25zdCB0cmFja3MgPSB7fTtcbiAgICBpZiAoaW5pdERhdGEuYXVkaW8gJiYgaW5pdERhdGEudmlkZW8pIHtcbiAgICAgIHRyYWNrcy5hdWRpb3ZpZGVvID0ge1xuICAgICAgICBjb250YWluZXI6ICd2aWRlby9tcDQnLFxuICAgICAgICBjb2RlYzogYXVkaW9Db2RlYyArICcsJyArIHZpZGVvQ29kZWMsXG4gICAgICAgIGluaXRTZWdtZW50LFxuICAgICAgICBpZDogJ21haW4nXG4gICAgICB9O1xuICAgIH0gZWxzZSBpZiAoaW5pdERhdGEuYXVkaW8pIHtcbiAgICAgIHRyYWNrcy5hdWRpbyA9IHtcbiAgICAgICAgY29udGFpbmVyOiAnYXVkaW8vbXA0JyxcbiAgICAgICAgY29kZWM6IGF1ZGlvQ29kZWMsXG4gICAgICAgIGluaXRTZWdtZW50LFxuICAgICAgICBpZDogJ2F1ZGlvJ1xuICAgICAgfTtcbiAgICB9IGVsc2UgaWYgKGluaXREYXRhLnZpZGVvKSB7XG4gICAgICB0cmFja3MudmlkZW8gPSB7XG4gICAgICAgIGNvbnRhaW5lcjogJ3ZpZGVvL21wNCcsXG4gICAgICAgIGNvZGVjOiB2aWRlb0NvZGVjLFxuICAgICAgICBpbml0U2VnbWVudCxcbiAgICAgICAgaWQ6ICdtYWluJ1xuICAgICAgfTtcbiAgICB9IGVsc2Uge1xuICAgICAgbG9nZ2VyLndhcm4oJ1twYXNzdGhyb3VnaC1yZW11eGVyLnRzXTogaW5pdFNlZ21lbnQgZG9lcyBub3QgY29udGFpbiBtb292IG9yIHRyYWsgYm94ZXMuJyk7XG4gICAgfVxuICAgIHRoaXMuaW5pdFRyYWNrcyA9IHRyYWNrcztcbiAgfVxuICByZW11eChhdWRpb1RyYWNrLCB2aWRlb1RyYWNrLCBpZDNUcmFjaywgdGV4dFRyYWNrLCB0aW1lT2Zmc2V0LCBhY2N1cmF0ZVRpbWVPZmZzZXQpIHtcbiAgICB2YXIgX2luaXREYXRhLCBfaW5pdERhdGEyO1xuICAgIGxldCB7XG4gICAgICBpbml0UFRTLFxuICAgICAgbGFzdEVuZFRpbWVcbiAgICB9ID0gdGhpcztcbiAgICBjb25zdCByZXN1bHQgPSB7XG4gICAgICBhdWRpbzogdW5kZWZpbmVkLFxuICAgICAgdmlkZW86IHVuZGVmaW5lZCxcbiAgICAgIHRleHQ6IHRleHRUcmFjayxcbiAgICAgIGlkMzogaWQzVHJhY2ssXG4gICAgICBpbml0U2VnbWVudDogdW5kZWZpbmVkXG4gICAgfTtcblxuICAgIC8vIElmIHdlIGhhdmVuJ3QgeWV0IHNldCBhIGxhc3RFbmREVFMsIG9yIGl0IHdhcyByZXNldCwgc2V0IGl0IHRvIHRoZSBwcm92aWRlZCB0aW1lT2Zmc2V0LiBXZSB3YW50IHRvIHVzZSB0aGVcbiAgICAvLyBsYXN0RW5kRFRTIG92ZXIgdGltZU9mZnNldCB3aGVuZXZlciBwb3NzaWJsZTsgZHVyaW5nIHByb2dyZXNzaXZlIHBsYXliYWNrLCB0aGUgbWVkaWEgc291cmNlIHdpbGwgbm90IHVwZGF0ZVxuICAgIC8vIHRoZSBtZWRpYSBkdXJhdGlvbiAod2hpY2ggaXMgd2hhdCB0aW1lT2Zmc2V0IGlzIHByb3ZpZGVkIGFzKSBiZWZvcmUgd2UgbmVlZCB0byBwcm9jZXNzIHRoZSBuZXh0IGNodW5rLlxuICAgIGlmICghaXNGaW5pdGVOdW1iZXIobGFzdEVuZFRpbWUpKSB7XG4gICAgICBsYXN0RW5kVGltZSA9IHRoaXMubGFzdEVuZFRpbWUgPSB0aW1lT2Zmc2V0IHx8IDA7XG4gICAgfVxuXG4gICAgLy8gVGhlIGJpbmFyeSBzZWdtZW50IGRhdGEgaXMgYWRkZWQgdG8gdGhlIHZpZGVvVHJhY2sgaW4gdGhlIG1wNGRlbXV4ZXIuIFdlIGRvbid0IGNoZWNrIHRvIHNlZSBpZiB0aGUgZGF0YSBpcyBvbmx5XG4gICAgLy8gYXVkaW8gb3IgdmlkZW8gKG9yIGJvdGgpOyBhZGRpbmcgaXQgdG8gdmlkZW8gd2FzIGFuIGFyYml0cmFyeSBjaG9pY2UuXG4gICAgY29uc3QgZGF0YSA9IHZpZGVvVHJhY2suc2FtcGxlcztcbiAgICBpZiAoIShkYXRhICE9IG51bGwgJiYgZGF0YS5sZW5ndGgpKSB7XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cbiAgICBjb25zdCBpbml0U2VnbWVudCA9IHtcbiAgICAgIGluaXRQVFM6IHVuZGVmaW5lZCxcbiAgICAgIHRpbWVzY2FsZTogMVxuICAgIH07XG4gICAgbGV0IGluaXREYXRhID0gdGhpcy5pbml0RGF0YTtcbiAgICBpZiAoISgoX2luaXREYXRhID0gaW5pdERhdGEpICE9IG51bGwgJiYgX2luaXREYXRhLmxlbmd0aCkpIHtcbiAgICAgIHRoaXMuZ2VuZXJhdGVJbml0U2VnbWVudChkYXRhKTtcbiAgICAgIGluaXREYXRhID0gdGhpcy5pbml0RGF0YTtcbiAgICB9XG4gICAgaWYgKCEoKF9pbml0RGF0YTIgPSBpbml0RGF0YSkgIT0gbnVsbCAmJiBfaW5pdERhdGEyLmxlbmd0aCkpIHtcbiAgICAgIC8vIFdlIGNhbid0IHJlbXV4IGlmIHRoZSBpbml0U2VnbWVudCBjb3VsZCBub3QgYmUgZ2VuZXJhdGVkXG4gICAgICBsb2dnZXIud2FybignW3Bhc3N0aHJvdWdoLXJlbXV4ZXIudHNdOiBGYWlsZWQgdG8gZ2VuZXJhdGUgaW5pdFNlZ21lbnQuJyk7XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cbiAgICBpZiAodGhpcy5lbWl0SW5pdFNlZ21lbnQpIHtcbiAgICAgIGluaXRTZWdtZW50LnRyYWNrcyA9IHRoaXMuaW5pdFRyYWNrcztcbiAgICAgIHRoaXMuZW1pdEluaXRTZWdtZW50ID0gZmFsc2U7XG4gICAgfVxuICAgIGNvbnN0IGR1cmF0aW9uID0gZ2V0RHVyYXRpb24oZGF0YSwgaW5pdERhdGEpO1xuICAgIGNvbnN0IHN0YXJ0RFRTID0gZ2V0U3RhcnREVFMoaW5pdERhdGEsIGRhdGEpO1xuICAgIGNvbnN0IGRlY29kZVRpbWUgPSBzdGFydERUUyA9PT0gbnVsbCA/IHRpbWVPZmZzZXQgOiBzdGFydERUUztcbiAgICBpZiAoaXNJbnZhbGlkSW5pdFB0cyhpbml0UFRTLCBkZWNvZGVUaW1lLCB0aW1lT2Zmc2V0LCBkdXJhdGlvbikgfHwgaW5pdFNlZ21lbnQudGltZXNjYWxlICE9PSBpbml0UFRTLnRpbWVzY2FsZSAmJiBhY2N1cmF0ZVRpbWVPZmZzZXQpIHtcbiAgICAgIGluaXRTZWdtZW50LmluaXRQVFMgPSBkZWNvZGVUaW1lIC0gdGltZU9mZnNldDtcbiAgICAgIGlmIChpbml0UFRTICYmIGluaXRQVFMudGltZXNjYWxlID09PSAxKSB7XG4gICAgICAgIGxvZ2dlci53YXJuKGBBZGp1c3RpbmcgaW5pdFBUUyBieSAke2luaXRTZWdtZW50LmluaXRQVFMgLSBpbml0UFRTLmJhc2VUaW1lfWApO1xuICAgICAgfVxuICAgICAgdGhpcy5pbml0UFRTID0gaW5pdFBUUyA9IHtcbiAgICAgICAgYmFzZVRpbWU6IGluaXRTZWdtZW50LmluaXRQVFMsXG4gICAgICAgIHRpbWVzY2FsZTogMVxuICAgICAgfTtcbiAgICB9XG4gICAgY29uc3Qgc3RhcnRUaW1lID0gYXVkaW9UcmFjayA/IGRlY29kZVRpbWUgLSBpbml0UFRTLmJhc2VUaW1lIC8gaW5pdFBUUy50aW1lc2NhbGUgOiBsYXN0RW5kVGltZTtcbiAgICBjb25zdCBlbmRUaW1lID0gc3RhcnRUaW1lICsgZHVyYXRpb247XG4gICAgb2Zmc2V0U3RhcnREVFMoaW5pdERhdGEsIGRhdGEsIGluaXRQVFMuYmFzZVRpbWUgLyBpbml0UFRTLnRpbWVzY2FsZSk7XG4gICAgaWYgKGR1cmF0aW9uID4gMCkge1xuICAgICAgdGhpcy5sYXN0RW5kVGltZSA9IGVuZFRpbWU7XG4gICAgfSBlbHNlIHtcbiAgICAgIGxvZ2dlci53YXJuKCdEdXJhdGlvbiBwYXJzZWQgZnJvbSBtcDQgc2hvdWxkIGJlIGdyZWF0ZXIgdGhhbiB6ZXJvJyk7XG4gICAgICB0aGlzLnJlc2V0TmV4dFRpbWVzdGFtcCgpO1xuICAgIH1cbiAgICBjb25zdCBoYXNBdWRpbyA9ICEhaW5pdERhdGEuYXVkaW87XG4gICAgY29uc3QgaGFzVmlkZW8gPSAhIWluaXREYXRhLnZpZGVvO1xuICAgIGxldCB0eXBlID0gJyc7XG4gICAgaWYgKGhhc0F1ZGlvKSB7XG4gICAgICB0eXBlICs9ICdhdWRpbyc7XG4gICAgfVxuICAgIGlmIChoYXNWaWRlbykge1xuICAgICAgdHlwZSArPSAndmlkZW8nO1xuICAgIH1cbiAgICBjb25zdCB0cmFjayA9IHtcbiAgICAgIGRhdGExOiBkYXRhLFxuICAgICAgc3RhcnRQVFM6IHN0YXJ0VGltZSxcbiAgICAgIHN0YXJ0RFRTOiBzdGFydFRpbWUsXG4gICAgICBlbmRQVFM6IGVuZFRpbWUsXG4gICAgICBlbmREVFM6IGVuZFRpbWUsXG4gICAgICB0eXBlLFxuICAgICAgaGFzQXVkaW8sXG4gICAgICBoYXNWaWRlbyxcbiAgICAgIG5iOiAxLFxuICAgICAgZHJvcHBlZDogMFxuICAgIH07XG4gICAgcmVzdWx0LmF1ZGlvID0gdHJhY2sudHlwZSA9PT0gJ2F1ZGlvJyA/IHRyYWNrIDogdW5kZWZpbmVkO1xuICAgIHJlc3VsdC52aWRlbyA9IHRyYWNrLnR5cGUgIT09ICdhdWRpbycgPyB0cmFjayA6IHVuZGVmaW5lZDtcbiAgICByZXN1bHQuaW5pdFNlZ21lbnQgPSBpbml0U2VnbWVudDtcbiAgICByZXN1bHQuaWQzID0gZmx1c2hUZXh0VHJhY2tNZXRhZGF0YUN1ZVNhbXBsZXMoaWQzVHJhY2ssIHRpbWVPZmZzZXQsIGluaXRQVFMsIGluaXRQVFMpO1xuICAgIGlmICh0ZXh0VHJhY2suc2FtcGxlcy5sZW5ndGgpIHtcbiAgICAgIHJlc3VsdC50ZXh0ID0gZmx1c2hUZXh0VHJhY2tVc2VyZGF0YUN1ZVNhbXBsZXModGV4dFRyYWNrLCB0aW1lT2Zmc2V0LCBpbml0UFRTKTtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxufVxuZnVuY3Rpb24gaXNJbnZhbGlkSW5pdFB0cyhpbml0UFRTLCBzdGFydERUUywgdGltZU9mZnNldCwgZHVyYXRpb24pIHtcbiAgaWYgKGluaXRQVFMgPT09IG51bGwpIHtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuICAvLyBJbml0UFRTIGlzIGludmFsaWQgd2hlbiBkaXN0YW5jZSBmcm9tIHByb2dyYW0gd291bGQgYmUgbW9yZSB0aGFuIHNlZ21lbnQgZHVyYXRpb24gb3IgYSBtaW5pbXVtIG9mIG9uZSBzZWNvbmRcbiAgY29uc3QgbWluRHVyYXRpb24gPSBNYXRoLm1heChkdXJhdGlvbiwgMSk7XG4gIGNvbnN0IHN0YXJ0VGltZSA9IHN0YXJ0RFRTIC0gaW5pdFBUUy5iYXNlVGltZSAvIGluaXRQVFMudGltZXNjYWxlO1xuICByZXR1cm4gTWF0aC5hYnMoc3RhcnRUaW1lIC0gdGltZU9mZnNldCkgPiBtaW5EdXJhdGlvbjtcbn1cbmZ1bmN0aW9uIGdldFBhcnNlZFRyYWNrQ29kZWModHJhY2ssIHR5cGUpIHtcbiAgY29uc3QgcGFyc2VkQ29kZWMgPSB0cmFjayA9PSBudWxsID8gdm9pZCAwIDogdHJhY2suY29kZWM7XG4gIGlmIChwYXJzZWRDb2RlYyAmJiBwYXJzZWRDb2RlYy5sZW5ndGggPiA0KSB7XG4gICAgcmV0dXJuIHBhcnNlZENvZGVjO1xuICB9XG4gIGlmICh0eXBlID09PSBFbGVtZW50YXJ5U3RyZWFtVHlwZXMuQVVESU8pIHtcbiAgICBpZiAocGFyc2VkQ29kZWMgPT09ICdlYy0zJyB8fCBwYXJzZWRDb2RlYyA9PT0gJ2FjLTMnIHx8IHBhcnNlZENvZGVjID09PSAnYWxhYycpIHtcbiAgICAgIHJldHVybiBwYXJzZWRDb2RlYztcbiAgICB9XG4gICAgaWYgKHBhcnNlZENvZGVjID09PSAnZkxhQycgfHwgcGFyc2VkQ29kZWMgPT09ICdPcHVzJykge1xuICAgICAgLy8gT3B0aW5nIG5vdCB0byBnZXQgYHByZWZlck1hbmFnZWRNZWRpYVNvdXJjZWAgZnJvbSBwbGF5ZXIgY29uZmlnIGZvciBpc1N1cHBvcnRlZCgpIGNoZWNrIGZvciBzaW1wbGljaXR5XG4gICAgICBjb25zdCBwcmVmZXJNYW5hZ2VkTWVkaWFTb3VyY2UgPSBmYWxzZTtcbiAgICAgIHJldHVybiBnZXRDb2RlY0NvbXBhdGlibGVOYW1lKHBhcnNlZENvZGVjLCBwcmVmZXJNYW5hZ2VkTWVkaWFTb3VyY2UpO1xuICAgIH1cbiAgICBjb25zdCByZXN1bHQgPSAnbXA0YS40MC41JztcbiAgICBsb2dnZXIuaW5mbyhgUGFyc2VkIGF1ZGlvIGNvZGVjIFwiJHtwYXJzZWRDb2RlY31cIiBvciBhdWRpbyBvYmplY3QgdHlwZSBub3QgaGFuZGxlZC4gVXNpbmcgXCIke3Jlc3VsdH1cImApO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbiAgLy8gUHJvdmlkZSBkZWZhdWx0cyBiYXNlZCBvbiBjb2RlYyB0eXBlXG4gIC8vIFRoaXMgYWxsb3dzIGZvciBzb21lIHBsYXliYWNrIG9mIHNvbWUgZm1wNCBwbGF5bGlzdHMgd2l0aG91dCBDT0RFQ1MgZGVmaW5lZCBpbiBtYW5pZmVzdFxuICBsb2dnZXIud2FybihgVW5oYW5kbGVkIHZpZGVvIGNvZGVjIFwiJHtwYXJzZWRDb2RlY31cImApO1xuICBpZiAocGFyc2VkQ29kZWMgPT09ICdodmMxJyB8fCBwYXJzZWRDb2RlYyA9PT0gJ2hldjEnKSB7XG4gICAgcmV0dXJuICdodmMxLjEuNi5MMTIwLjkwJztcbiAgfVxuICBpZiAocGFyc2VkQ29kZWMgPT09ICdhdjAxJykge1xuICAgIHJldHVybiAnYXYwMS4wLjA0TS4wOCc7XG4gIH1cbiAgcmV0dXJuICdhdmMxLjQyZTAxZSc7XG59XG5cbmxldCBub3c7XG4vLyBwZXJmb3JtYW5jZS5ub3coKSBub3QgYXZhaWxhYmxlIG9uIFdlYldvcmtlciwgYXQgbGVhc3Qgb24gU2FmYXJpIERlc2t0b3BcbnRyeSB7XG4gIG5vdyA9IHNlbGYucGVyZm9ybWFuY2Uubm93LmJpbmQoc2VsZi5wZXJmb3JtYW5jZSk7XG59IGNhdGNoIChlcnIpIHtcbiAgbG9nZ2VyLmRlYnVnKCdVbmFibGUgdG8gdXNlIFBlcmZvcm1hbmNlIEFQSSBvbiB0aGlzIGVudmlyb25tZW50Jyk7XG4gIG5vdyA9IG9wdGlvbmFsU2VsZiA9PSBudWxsID8gdm9pZCAwIDogb3B0aW9uYWxTZWxmLkRhdGUubm93O1xufVxuY29uc3QgbXV4Q29uZmlnID0gW3tcbiAgZGVtdXg6IE1QNERlbXV4ZXIsXG4gIHJlbXV4OiBQYXNzVGhyb3VnaFJlbXV4ZXJcbn0sIHtcbiAgZGVtdXg6IFRTRGVtdXhlcixcbiAgcmVtdXg6IE1QNFJlbXV4ZXJcbn0sIHtcbiAgZGVtdXg6IEFBQ0RlbXV4ZXIsXG4gIHJlbXV4OiBNUDRSZW11eGVyXG59LCB7XG4gIGRlbXV4OiBNUDNEZW11eGVyLFxuICByZW11eDogTVA0UmVtdXhlclxufV07XG57XG4gIG11eENvbmZpZy5zcGxpY2UoMiwgMCwge1xuICAgIGRlbXV4OiBBQzNEZW11eGVyLFxuICAgIHJlbXV4OiBNUDRSZW11eGVyXG4gIH0pO1xufVxuY2xhc3MgVHJhbnNtdXhlciB7XG4gIGNvbnN0cnVjdG9yKG9ic2VydmVyLCB0eXBlU3VwcG9ydGVkLCBjb25maWcsIHZlbmRvciwgaWQpIHtcbiAgICB0aGlzLmFzeW5jID0gZmFsc2U7XG4gICAgdGhpcy5vYnNlcnZlciA9IHZvaWQgMDtcbiAgICB0aGlzLnR5cGVTdXBwb3J0ZWQgPSB2b2lkIDA7XG4gICAgdGhpcy5jb25maWcgPSB2b2lkIDA7XG4gICAgdGhpcy52ZW5kb3IgPSB2b2lkIDA7XG4gICAgdGhpcy5pZCA9IHZvaWQgMDtcbiAgICB0aGlzLmRlbXV4ZXIgPSB2b2lkIDA7XG4gICAgdGhpcy5yZW11eGVyID0gdm9pZCAwO1xuICAgIHRoaXMuZGVjcnlwdGVyID0gdm9pZCAwO1xuICAgIHRoaXMucHJvYmUgPSB2b2lkIDA7XG4gICAgdGhpcy5kZWNyeXB0aW9uUHJvbWlzZSA9IG51bGw7XG4gICAgdGhpcy50cmFuc211eENvbmZpZyA9IHZvaWQgMDtcbiAgICB0aGlzLmN1cnJlbnRUcmFuc211eFN0YXRlID0gdm9pZCAwO1xuICAgIHRoaXMub2JzZXJ2ZXIgPSBvYnNlcnZlcjtcbiAgICB0aGlzLnR5cGVTdXBwb3J0ZWQgPSB0eXBlU3VwcG9ydGVkO1xuICAgIHRoaXMuY29uZmlnID0gY29uZmlnO1xuICAgIHRoaXMudmVuZG9yID0gdmVuZG9yO1xuICAgIHRoaXMuaWQgPSBpZDtcbiAgfVxuICBjb25maWd1cmUodHJhbnNtdXhDb25maWcpIHtcbiAgICB0aGlzLnRyYW5zbXV4Q29uZmlnID0gdHJhbnNtdXhDb25maWc7XG4gICAgaWYgKHRoaXMuZGVjcnlwdGVyKSB7XG4gICAgICB0aGlzLmRlY3J5cHRlci5yZXNldCgpO1xuICAgIH1cbiAgfVxuICBwdXNoKGRhdGEsIGRlY3J5cHRkYXRhLCBjaHVua01ldGEsIHN0YXRlKSB7XG4gICAgY29uc3Qgc3RhdHMgPSBjaHVua01ldGEudHJhbnNtdXhpbmc7XG4gICAgc3RhdHMuZXhlY3V0ZVN0YXJ0ID0gbm93KCk7XG4gICAgbGV0IHVpbnREYXRhID0gbmV3IFVpbnQ4QXJyYXkoZGF0YSk7XG4gICAgY29uc3Qge1xuICAgICAgY3VycmVudFRyYW5zbXV4U3RhdGUsXG4gICAgICB0cmFuc211eENvbmZpZ1xuICAgIH0gPSB0aGlzO1xuICAgIGlmIChzdGF0ZSkge1xuICAgICAgdGhpcy5jdXJyZW50VHJhbnNtdXhTdGF0ZSA9IHN0YXRlO1xuICAgIH1cbiAgICBjb25zdCB7XG4gICAgICBjb250aWd1b3VzLFxuICAgICAgZGlzY29udGludWl0eSxcbiAgICAgIHRyYWNrU3dpdGNoLFxuICAgICAgYWNjdXJhdGVUaW1lT2Zmc2V0LFxuICAgICAgdGltZU9mZnNldCxcbiAgICAgIGluaXRTZWdtZW50Q2hhbmdlXG4gICAgfSA9IHN0YXRlIHx8IGN1cnJlbnRUcmFuc211eFN0YXRlO1xuICAgIGNvbnN0IHtcbiAgICAgIGF1ZGlvQ29kZWMsXG4gICAgICB2aWRlb0NvZGVjLFxuICAgICAgZGVmYXVsdEluaXRQdHMsXG4gICAgICBkdXJhdGlvbixcbiAgICAgIGluaXRTZWdtZW50RGF0YVxuICAgIH0gPSB0cmFuc211eENvbmZpZztcbiAgICBjb25zdCBrZXlEYXRhID0gZ2V0RW5jcnlwdGlvblR5cGUodWludERhdGEsIGRlY3J5cHRkYXRhKTtcbiAgICBpZiAoa2V5RGF0YSAmJiBrZXlEYXRhLm1ldGhvZCA9PT0gJ0FFUy0xMjgnKSB7XG4gICAgICBjb25zdCBkZWNyeXB0ZXIgPSB0aGlzLmdldERlY3J5cHRlcigpO1xuICAgICAgLy8gU29mdHdhcmUgZGVjcnlwdGlvbiBpcyBzeW5jaHJvbm91czsgd2ViQ3J5cHRvIGlzIG5vdFxuICAgICAgaWYgKGRlY3J5cHRlci5pc1N5bmMoKSkge1xuICAgICAgICAvLyBTb2Z0d2FyZSBkZWNyeXB0aW9uIGlzIHByb2dyZXNzaXZlLiBQcm9ncmVzc2l2ZSBkZWNyeXB0aW9uIG1heSBub3QgcmV0dXJuIGEgcmVzdWx0IG9uIGVhY2ggY2FsbC4gQW55IGNhY2hlZFxuICAgICAgICAvLyBkYXRhIGlzIGhhbmRsZWQgaW4gdGhlIGZsdXNoKCkgY2FsbFxuICAgICAgICBsZXQgZGVjcnlwdGVkRGF0YSA9IGRlY3J5cHRlci5zb2Z0d2FyZURlY3J5cHQodWludERhdGEsIGtleURhdGEua2V5LmJ1ZmZlciwga2V5RGF0YS5pdi5idWZmZXIpO1xuICAgICAgICAvLyBGb3IgTG93LUxhdGVuY3kgSExTIFBhcnRzLCBkZWNyeXB0IGluIHBsYWNlLCBzaW5jZSBwYXJ0IHBhcnNpbmcgaXMgZXhwZWN0ZWQgb24gcHVzaCBwcm9ncmVzc1xuICAgICAgICBjb25zdCBsb2FkaW5nUGFydHMgPSBjaHVua01ldGEucGFydCA+IC0xO1xuICAgICAgICBpZiAobG9hZGluZ1BhcnRzKSB7XG4gICAgICAgICAgZGVjcnlwdGVkRGF0YSA9IGRlY3J5cHRlci5mbHVzaCgpO1xuICAgICAgICB9XG4gICAgICAgIGlmICghZGVjcnlwdGVkRGF0YSkge1xuICAgICAgICAgIHN0YXRzLmV4ZWN1dGVFbmQgPSBub3coKTtcbiAgICAgICAgICByZXR1cm4gZW1wdHlSZXN1bHQoY2h1bmtNZXRhKTtcbiAgICAgICAgfVxuICAgICAgICB1aW50RGF0YSA9IG5ldyBVaW50OEFycmF5KGRlY3J5cHRlZERhdGEpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5kZWNyeXB0aW9uUHJvbWlzZSA9IGRlY3J5cHRlci53ZWJDcnlwdG9EZWNyeXB0KHVpbnREYXRhLCBrZXlEYXRhLmtleS5idWZmZXIsIGtleURhdGEuaXYuYnVmZmVyKS50aGVuKGRlY3J5cHRlZERhdGEgPT4ge1xuICAgICAgICAgIC8vIENhbGxpbmcgcHVzaCBoZXJlIGlzIGltcG9ydGFudDsgaWYgZmx1c2goKSBpcyBjYWxsZWQgd2hpbGUgdGhpcyBpcyBzdGlsbCByZXNvbHZpbmcsIHRoaXMgZW5zdXJlcyB0aGF0XG4gICAgICAgICAgLy8gdGhlIGRlY3J5cHRlZCBkYXRhIGhhcyBiZWVuIHRyYW5zbXV4ZWRcbiAgICAgICAgICBjb25zdCByZXN1bHQgPSB0aGlzLnB1c2goZGVjcnlwdGVkRGF0YSwgbnVsbCwgY2h1bmtNZXRhKTtcbiAgICAgICAgICB0aGlzLmRlY3J5cHRpb25Qcm9taXNlID0gbnVsbDtcbiAgICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIHRoaXMuZGVjcnlwdGlvblByb21pc2U7XG4gICAgICB9XG4gICAgfVxuICAgIGNvbnN0IHJlc2V0TXV4ZXJzID0gdGhpcy5uZWVkc1Byb2JpbmcoZGlzY29udGludWl0eSwgdHJhY2tTd2l0Y2gpO1xuICAgIGlmIChyZXNldE11eGVycykge1xuICAgICAgY29uc3QgZXJyb3IgPSB0aGlzLmNvbmZpZ3VyZVRyYW5zbXV4ZXIodWludERhdGEpO1xuICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgIGxvZ2dlci53YXJuKGBbdHJhbnNtdXhlcl0gJHtlcnJvci5tZXNzYWdlfWApO1xuICAgICAgICB0aGlzLm9ic2VydmVyLmVtaXQoRXZlbnRzLkVSUk9SLCBFdmVudHMuRVJST1IsIHtcbiAgICAgICAgICB0eXBlOiBFcnJvclR5cGVzLk1FRElBX0VSUk9SLFxuICAgICAgICAgIGRldGFpbHM6IEVycm9yRGV0YWlscy5GUkFHX1BBUlNJTkdfRVJST1IsXG4gICAgICAgICAgZmF0YWw6IGZhbHNlLFxuICAgICAgICAgIGVycm9yLFxuICAgICAgICAgIHJlYXNvbjogZXJyb3IubWVzc2FnZVxuICAgICAgICB9KTtcbiAgICAgICAgc3RhdHMuZXhlY3V0ZUVuZCA9IG5vdygpO1xuICAgICAgICByZXR1cm4gZW1wdHlSZXN1bHQoY2h1bmtNZXRhKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKGRpc2NvbnRpbnVpdHkgfHwgdHJhY2tTd2l0Y2ggfHwgaW5pdFNlZ21lbnRDaGFuZ2UgfHwgcmVzZXRNdXhlcnMpIHtcbiAgICAgIHRoaXMucmVzZXRJbml0U2VnbWVudChpbml0U2VnbWVudERhdGEsIGF1ZGlvQ29kZWMsIHZpZGVvQ29kZWMsIGR1cmF0aW9uLCBkZWNyeXB0ZGF0YSk7XG4gICAgfVxuICAgIGlmIChkaXNjb250aW51aXR5IHx8IGluaXRTZWdtZW50Q2hhbmdlIHx8IHJlc2V0TXV4ZXJzKSB7XG4gICAgICB0aGlzLnJlc2V0SW5pdGlhbFRpbWVzdGFtcChkZWZhdWx0SW5pdFB0cyk7XG4gICAgfVxuICAgIGlmICghY29udGlndW91cykge1xuICAgICAgdGhpcy5yZXNldENvbnRpZ3VpdHkoKTtcbiAgICB9XG4gICAgY29uc3QgcmVzdWx0ID0gdGhpcy50cmFuc211eCh1aW50RGF0YSwga2V5RGF0YSwgdGltZU9mZnNldCwgYWNjdXJhdGVUaW1lT2Zmc2V0LCBjaHVua01ldGEpO1xuICAgIGNvbnN0IGN1cnJlbnRTdGF0ZSA9IHRoaXMuY3VycmVudFRyYW5zbXV4U3RhdGU7XG4gICAgY3VycmVudFN0YXRlLmNvbnRpZ3VvdXMgPSB0cnVlO1xuICAgIGN1cnJlbnRTdGF0ZS5kaXNjb250aW51aXR5ID0gZmFsc2U7XG4gICAgY3VycmVudFN0YXRlLnRyYWNrU3dpdGNoID0gZmFsc2U7XG4gICAgc3RhdHMuZXhlY3V0ZUVuZCA9IG5vdygpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cblxuICAvLyBEdWUgdG8gZGF0YSBjYWNoaW5nLCBmbHVzaCBjYWxscyBjYW4gcHJvZHVjZSBtb3JlIHRoYW4gb25lIFRyYW5zbXV4ZXJSZXN1bHQgKGhlbmNlIHRoZSBBcnJheSB0eXBlKVxuICBmbHVzaChjaHVua01ldGEpIHtcbiAgICBjb25zdCBzdGF0cyA9IGNodW5rTWV0YS50cmFuc211eGluZztcbiAgICBzdGF0cy5leGVjdXRlU3RhcnQgPSBub3coKTtcbiAgICBjb25zdCB7XG4gICAgICBkZWNyeXB0ZXIsXG4gICAgICBjdXJyZW50VHJhbnNtdXhTdGF0ZSxcbiAgICAgIGRlY3J5cHRpb25Qcm9taXNlXG4gICAgfSA9IHRoaXM7XG4gICAgaWYgKGRlY3J5cHRpb25Qcm9taXNlKSB7XG4gICAgICAvLyBVcG9uIHJlc29sdXRpb24sIHRoZSBkZWNyeXB0aW9uIHByb21pc2UgY2FsbHMgcHVzaCgpIGFuZCByZXR1cm5zIGl0cyBUcmFuc211eGVyUmVzdWx0IHVwIHRoZSBzdGFjay4gVGhlcmVmb3JlXG4gICAgICAvLyBvbmx5IGZsdXNoaW5nIGlzIHJlcXVpcmVkIGZvciBhc3luYyBkZWNyeXB0aW9uXG4gICAgICByZXR1cm4gZGVjcnlwdGlvblByb21pc2UudGhlbigoKSA9PiB7XG4gICAgICAgIHJldHVybiB0aGlzLmZsdXNoKGNodW5rTWV0YSk7XG4gICAgICB9KTtcbiAgICB9XG4gICAgY29uc3QgdHJhbnNtdXhSZXN1bHRzID0gW107XG4gICAgY29uc3Qge1xuICAgICAgdGltZU9mZnNldFxuICAgIH0gPSBjdXJyZW50VHJhbnNtdXhTdGF0ZTtcbiAgICBpZiAoZGVjcnlwdGVyKSB7XG4gICAgICAvLyBUaGUgZGVjcnlwdGVyIG1heSBoYXZlIGRhdGEgY2FjaGVkLCB3aGljaCBuZWVkcyB0byBiZSBkZW11eGVkLiBJbiB0aGlzIGNhc2Ugd2UnbGwgaGF2ZSB0d28gVHJhbnNtdXhSZXN1bHRzXG4gICAgICAvLyBUaGlzIGhhcHBlbnMgaW4gdGhlIGNhc2UgdGhhdCB3ZSByZWNlaXZlIG9ubHkgMSBwdXNoIGNhbGwgZm9yIGEgc2VnbWVudCAoZWl0aGVyIGZvciBub24tcHJvZ3Jlc3NpdmUgZG93bmxvYWRzLFxuICAgICAgLy8gb3IgZm9yIHByb2dyZXNzaXZlIGRvd25sb2FkcyB3aXRoIHNtYWxsIHNlZ21lbnRzKVxuICAgICAgY29uc3QgZGVjcnlwdGVkRGF0YSA9IGRlY3J5cHRlci5mbHVzaCgpO1xuICAgICAgaWYgKGRlY3J5cHRlZERhdGEpIHtcbiAgICAgICAgLy8gUHVzaCBhbHdheXMgcmV0dXJucyBhIFRyYW5zbXV4ZXJSZXN1bHQgaWYgZGVjcnlwdGRhdGEgaXMgbnVsbFxuICAgICAgICB0cmFuc211eFJlc3VsdHMucHVzaCh0aGlzLnB1c2goZGVjcnlwdGVkRGF0YSwgbnVsbCwgY2h1bmtNZXRhKSk7XG4gICAgICB9XG4gICAgfVxuICAgIGNvbnN0IHtcbiAgICAgIGRlbXV4ZXIsXG4gICAgICByZW11eGVyXG4gICAgfSA9IHRoaXM7XG4gICAgaWYgKCFkZW11eGVyIHx8ICFyZW11eGVyKSB7XG4gICAgICAvLyBJZiBwcm9iaW5nIGZhaWxlZCwgdGhlbiBIbHMuanMgaGFzIGJlZW4gZ2l2ZW4gY29udGVudCBpdHMgbm90IGFibGUgdG8gaGFuZGxlXG4gICAgICBzdGF0cy5leGVjdXRlRW5kID0gbm93KCk7XG4gICAgICByZXR1cm4gW2VtcHR5UmVzdWx0KGNodW5rTWV0YSldO1xuICAgIH1cbiAgICBjb25zdCBkZW11eFJlc3VsdE9yUHJvbWlzZSA9IGRlbXV4ZXIuZmx1c2godGltZU9mZnNldCk7XG4gICAgaWYgKGlzUHJvbWlzZShkZW11eFJlc3VsdE9yUHJvbWlzZSkpIHtcbiAgICAgIC8vIERlY3J5cHQgZmluYWwgU0FNUExFLUFFUyBzYW1wbGVzXG4gICAgICByZXR1cm4gZGVtdXhSZXN1bHRPclByb21pc2UudGhlbihkZW11eFJlc3VsdCA9PiB7XG4gICAgICAgIHRoaXMuZmx1c2hSZW11eCh0cmFuc211eFJlc3VsdHMsIGRlbXV4UmVzdWx0LCBjaHVua01ldGEpO1xuICAgICAgICByZXR1cm4gdHJhbnNtdXhSZXN1bHRzO1xuICAgICAgfSk7XG4gICAgfVxuICAgIHRoaXMuZmx1c2hSZW11eCh0cmFuc211eFJlc3VsdHMsIGRlbXV4UmVzdWx0T3JQcm9taXNlLCBjaHVua01ldGEpO1xuICAgIHJldHVybiB0cmFuc211eFJlc3VsdHM7XG4gIH1cbiAgZmx1c2hSZW11eCh0cmFuc211eFJlc3VsdHMsIGRlbXV4UmVzdWx0LCBjaHVua01ldGEpIHtcbiAgICBjb25zdCB7XG4gICAgICBhdWRpb1RyYWNrLFxuICAgICAgdmlkZW9UcmFjayxcbiAgICAgIGlkM1RyYWNrLFxuICAgICAgdGV4dFRyYWNrXG4gICAgfSA9IGRlbXV4UmVzdWx0O1xuICAgIGNvbnN0IHtcbiAgICAgIGFjY3VyYXRlVGltZU9mZnNldCxcbiAgICAgIHRpbWVPZmZzZXRcbiAgICB9ID0gdGhpcy5jdXJyZW50VHJhbnNtdXhTdGF0ZTtcbiAgICBsb2dnZXIubG9nKGBbdHJhbnNtdXhlci50c106IEZsdXNoZWQgZnJhZ21lbnQgJHtjaHVua01ldGEuc259JHtjaHVua01ldGEucGFydCA+IC0xID8gJyBwOiAnICsgY2h1bmtNZXRhLnBhcnQgOiAnJ30gb2YgbGV2ZWwgJHtjaHVua01ldGEubGV2ZWx9YCk7XG4gICAgY29uc3QgcmVtdXhSZXN1bHQgPSB0aGlzLnJlbXV4ZXIucmVtdXgoYXVkaW9UcmFjaywgdmlkZW9UcmFjaywgaWQzVHJhY2ssIHRleHRUcmFjaywgdGltZU9mZnNldCwgYWNjdXJhdGVUaW1lT2Zmc2V0LCB0cnVlLCB0aGlzLmlkKTtcbiAgICB0cmFuc211eFJlc3VsdHMucHVzaCh7XG4gICAgICByZW11eFJlc3VsdCxcbiAgICAgIGNodW5rTWV0YVxuICAgIH0pO1xuICAgIGNodW5rTWV0YS50cmFuc211eGluZy5leGVjdXRlRW5kID0gbm93KCk7XG4gIH1cbiAgcmVzZXRJbml0aWFsVGltZXN0YW1wKGRlZmF1bHRJbml0UHRzKSB7XG4gICAgY29uc3Qge1xuICAgICAgZGVtdXhlcixcbiAgICAgIHJlbXV4ZXJcbiAgICB9ID0gdGhpcztcbiAgICBpZiAoIWRlbXV4ZXIgfHwgIXJlbXV4ZXIpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgZGVtdXhlci5yZXNldFRpbWVTdGFtcChkZWZhdWx0SW5pdFB0cyk7XG4gICAgcmVtdXhlci5yZXNldFRpbWVTdGFtcChkZWZhdWx0SW5pdFB0cyk7XG4gIH1cbiAgcmVzZXRDb250aWd1aXR5KCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGRlbXV4ZXIsXG4gICAgICByZW11eGVyXG4gICAgfSA9IHRoaXM7XG4gICAgaWYgKCFkZW11eGVyIHx8ICFyZW11eGVyKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGRlbXV4ZXIucmVzZXRDb250aWd1aXR5KCk7XG4gICAgcmVtdXhlci5yZXNldE5leHRUaW1lc3RhbXAoKTtcbiAgfVxuICByZXNldEluaXRTZWdtZW50KGluaXRTZWdtZW50RGF0YSwgYXVkaW9Db2RlYywgdmlkZW9Db2RlYywgdHJhY2tEdXJhdGlvbiwgZGVjcnlwdGRhdGEpIHtcbiAgICBjb25zdCB7XG4gICAgICBkZW11eGVyLFxuICAgICAgcmVtdXhlclxuICAgIH0gPSB0aGlzO1xuICAgIGlmICghZGVtdXhlciB8fCAhcmVtdXhlcikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBkZW11eGVyLnJlc2V0SW5pdFNlZ21lbnQoaW5pdFNlZ21lbnREYXRhLCBhdWRpb0NvZGVjLCB2aWRlb0NvZGVjLCB0cmFja0R1cmF0aW9uKTtcbiAgICByZW11eGVyLnJlc2V0SW5pdFNlZ21lbnQoaW5pdFNlZ21lbnREYXRhLCBhdWRpb0NvZGVjLCB2aWRlb0NvZGVjLCBkZWNyeXB0ZGF0YSk7XG4gIH1cbiAgZGVzdHJveSgpIHtcbiAgICBpZiAodGhpcy5kZW11eGVyKSB7XG4gICAgICB0aGlzLmRlbXV4ZXIuZGVzdHJveSgpO1xuICAgICAgdGhpcy5kZW11eGVyID0gdW5kZWZpbmVkO1xuICAgIH1cbiAgICBpZiAodGhpcy5yZW11eGVyKSB7XG4gICAgICB0aGlzLnJlbXV4ZXIuZGVzdHJveSgpO1xuICAgICAgdGhpcy5yZW11eGVyID0gdW5kZWZpbmVkO1xuICAgIH1cbiAgfVxuICB0cmFuc211eChkYXRhLCBrZXlEYXRhLCB0aW1lT2Zmc2V0LCBhY2N1cmF0ZVRpbWVPZmZzZXQsIGNodW5rTWV0YSkge1xuICAgIGxldCByZXN1bHQ7XG4gICAgaWYgKGtleURhdGEgJiYga2V5RGF0YS5tZXRob2QgPT09ICdTQU1QTEUtQUVTJykge1xuICAgICAgcmVzdWx0ID0gdGhpcy50cmFuc211eFNhbXBsZUFlcyhkYXRhLCBrZXlEYXRhLCB0aW1lT2Zmc2V0LCBhY2N1cmF0ZVRpbWVPZmZzZXQsIGNodW5rTWV0YSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJlc3VsdCA9IHRoaXMudHJhbnNtdXhVbmVuY3J5cHRlZChkYXRhLCB0aW1lT2Zmc2V0LCBhY2N1cmF0ZVRpbWVPZmZzZXQsIGNodW5rTWV0YSk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbiAgdHJhbnNtdXhVbmVuY3J5cHRlZChkYXRhLCB0aW1lT2Zmc2V0LCBhY2N1cmF0ZVRpbWVPZmZzZXQsIGNodW5rTWV0YSkge1xuICAgIGNvbnN0IHtcbiAgICAgIGF1ZGlvVHJhY2ssXG4gICAgICB2aWRlb1RyYWNrLFxuICAgICAgaWQzVHJhY2ssXG4gICAgICB0ZXh0VHJhY2tcbiAgICB9ID0gdGhpcy5kZW11eGVyLmRlbXV4KGRhdGEsIHRpbWVPZmZzZXQsIGZhbHNlLCAhdGhpcy5jb25maWcucHJvZ3Jlc3NpdmUpO1xuICAgIGNvbnN0IHJlbXV4UmVzdWx0ID0gdGhpcy5yZW11eGVyLnJlbXV4KGF1ZGlvVHJhY2ssIHZpZGVvVHJhY2ssIGlkM1RyYWNrLCB0ZXh0VHJhY2ssIHRpbWVPZmZzZXQsIGFjY3VyYXRlVGltZU9mZnNldCwgZmFsc2UsIHRoaXMuaWQpO1xuICAgIHJldHVybiB7XG4gICAgICByZW11eFJlc3VsdCxcbiAgICAgIGNodW5rTWV0YVxuICAgIH07XG4gIH1cbiAgdHJhbnNtdXhTYW1wbGVBZXMoZGF0YSwgZGVjcnlwdERhdGEsIHRpbWVPZmZzZXQsIGFjY3VyYXRlVGltZU9mZnNldCwgY2h1bmtNZXRhKSB7XG4gICAgcmV0dXJuIHRoaXMuZGVtdXhlci5kZW11eFNhbXBsZUFlcyhkYXRhLCBkZWNyeXB0RGF0YSwgdGltZU9mZnNldCkudGhlbihkZW11eFJlc3VsdCA9PiB7XG4gICAgICBjb25zdCByZW11eFJlc3VsdCA9IHRoaXMucmVtdXhlci5yZW11eChkZW11eFJlc3VsdC5hdWRpb1RyYWNrLCBkZW11eFJlc3VsdC52aWRlb1RyYWNrLCBkZW11eFJlc3VsdC5pZDNUcmFjaywgZGVtdXhSZXN1bHQudGV4dFRyYWNrLCB0aW1lT2Zmc2V0LCBhY2N1cmF0ZVRpbWVPZmZzZXQsIGZhbHNlLCB0aGlzLmlkKTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHJlbXV4UmVzdWx0LFxuICAgICAgICBjaHVua01ldGFcbiAgICAgIH07XG4gICAgfSk7XG4gIH1cbiAgY29uZmlndXJlVHJhbnNtdXhlcihkYXRhKSB7XG4gICAgY29uc3Qge1xuICAgICAgY29uZmlnLFxuICAgICAgb2JzZXJ2ZXIsXG4gICAgICB0eXBlU3VwcG9ydGVkLFxuICAgICAgdmVuZG9yXG4gICAgfSA9IHRoaXM7XG4gICAgLy8gcHJvYmUgZm9yIGNvbnRlbnQgdHlwZVxuICAgIGxldCBtdXg7XG4gICAgZm9yIChsZXQgaSA9IDAsIGxlbiA9IG11eENvbmZpZy5sZW5ndGg7IGkgPCBsZW47IGkrKykge1xuICAgICAgdmFyIF9tdXhDb25maWckaSRkZW11eDtcbiAgICAgIGlmICgoX211eENvbmZpZyRpJGRlbXV4ID0gbXV4Q29uZmlnW2ldLmRlbXV4KSAhPSBudWxsICYmIF9tdXhDb25maWckaSRkZW11eC5wcm9iZShkYXRhKSkge1xuICAgICAgICBtdXggPSBtdXhDb25maWdbaV07XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAoIW11eCkge1xuICAgICAgcmV0dXJuIG5ldyBFcnJvcignRmFpbGVkIHRvIGZpbmQgZGVtdXhlciBieSBwcm9iaW5nIGZyYWdtZW50IGRhdGEnKTtcbiAgICB9XG4gICAgLy8gc28gbGV0J3MgY2hlY2sgdGhhdCBjdXJyZW50IHJlbXV4ZXIgYW5kIGRlbXV4ZXIgYXJlIHN0aWxsIHZhbGlkXG4gICAgY29uc3QgZGVtdXhlciA9IHRoaXMuZGVtdXhlcjtcbiAgICBjb25zdCByZW11eGVyID0gdGhpcy5yZW11eGVyO1xuICAgIGNvbnN0IFJlbXV4ZXIgPSBtdXgucmVtdXg7XG4gICAgY29uc3QgRGVtdXhlciA9IG11eC5kZW11eDtcbiAgICBpZiAoIXJlbXV4ZXIgfHwgIShyZW11eGVyIGluc3RhbmNlb2YgUmVtdXhlcikpIHtcbiAgICAgIHRoaXMucmVtdXhlciA9IG5ldyBSZW11eGVyKG9ic2VydmVyLCBjb25maWcsIHR5cGVTdXBwb3J0ZWQsIHZlbmRvcik7XG4gICAgfVxuICAgIGlmICghZGVtdXhlciB8fCAhKGRlbXV4ZXIgaW5zdGFuY2VvZiBEZW11eGVyKSkge1xuICAgICAgdGhpcy5kZW11eGVyID0gbmV3IERlbXV4ZXIob2JzZXJ2ZXIsIGNvbmZpZywgdHlwZVN1cHBvcnRlZCk7XG4gICAgICB0aGlzLnByb2JlID0gRGVtdXhlci5wcm9iZTtcbiAgICB9XG4gIH1cbiAgbmVlZHNQcm9iaW5nKGRpc2NvbnRpbnVpdHksIHRyYWNrU3dpdGNoKSB7XG4gICAgLy8gaW4gY2FzZSBvZiBjb250aW51aXR5IGNoYW5nZSwgb3IgdHJhY2sgc3dpdGNoXG4gICAgLy8gd2UgbWlnaHQgc3dpdGNoIGZyb20gY29udGVudCB0eXBlIChBQUMgY29udGFpbmVyIHRvIFRTIGNvbnRhaW5lciwgb3IgVFMgdG8gZm1wNCBmb3IgZXhhbXBsZSlcbiAgICByZXR1cm4gIXRoaXMuZGVtdXhlciB8fCAhdGhpcy5yZW11eGVyIHx8IGRpc2NvbnRpbnVpdHkgfHwgdHJhY2tTd2l0Y2g7XG4gIH1cbiAgZ2V0RGVjcnlwdGVyKCkge1xuICAgIGxldCBkZWNyeXB0ZXIgPSB0aGlzLmRlY3J5cHRlcjtcbiAgICBpZiAoIWRlY3J5cHRlcikge1xuICAgICAgZGVjcnlwdGVyID0gdGhpcy5kZWNyeXB0ZXIgPSBuZXcgRGVjcnlwdGVyKHRoaXMuY29uZmlnKTtcbiAgICB9XG4gICAgcmV0dXJuIGRlY3J5cHRlcjtcbiAgfVxufVxuZnVuY3Rpb24gZ2V0RW5jcnlwdGlvblR5cGUoZGF0YSwgZGVjcnlwdERhdGEpIHtcbiAgbGV0IGVuY3J5cHRpb25UeXBlID0gbnVsbDtcbiAgaWYgKGRhdGEuYnl0ZUxlbmd0aCA+IDAgJiYgKGRlY3J5cHREYXRhID09IG51bGwgPyB2b2lkIDAgOiBkZWNyeXB0RGF0YS5rZXkpICE9IG51bGwgJiYgZGVjcnlwdERhdGEuaXYgIT09IG51bGwgJiYgZGVjcnlwdERhdGEubWV0aG9kICE9IG51bGwpIHtcbiAgICBlbmNyeXB0aW9uVHlwZSA9IGRlY3J5cHREYXRhO1xuICB9XG4gIHJldHVybiBlbmNyeXB0aW9uVHlwZTtcbn1cbmNvbnN0IGVtcHR5UmVzdWx0ID0gY2h1bmtNZXRhID0+ICh7XG4gIHJlbXV4UmVzdWx0OiB7fSxcbiAgY2h1bmtNZXRhXG59KTtcbmZ1bmN0aW9uIGlzUHJvbWlzZShwKSB7XG4gIHJldHVybiAndGhlbicgaW4gcCAmJiBwLnRoZW4gaW5zdGFuY2VvZiBGdW5jdGlvbjtcbn1cbmNsYXNzIFRyYW5zbXV4Q29uZmlnIHtcbiAgY29uc3RydWN0b3IoYXVkaW9Db2RlYywgdmlkZW9Db2RlYywgaW5pdFNlZ21lbnREYXRhLCBkdXJhdGlvbiwgZGVmYXVsdEluaXRQdHMpIHtcbiAgICB0aGlzLmF1ZGlvQ29kZWMgPSB2b2lkIDA7XG4gICAgdGhpcy52aWRlb0NvZGVjID0gdm9pZCAwO1xuICAgIHRoaXMuaW5pdFNlZ21lbnREYXRhID0gdm9pZCAwO1xuICAgIHRoaXMuZHVyYXRpb24gPSB2b2lkIDA7XG4gICAgdGhpcy5kZWZhdWx0SW5pdFB0cyA9IHZvaWQgMDtcbiAgICB0aGlzLmF1ZGlvQ29kZWMgPSBhdWRpb0NvZGVjO1xuICAgIHRoaXMudmlkZW9Db2RlYyA9IHZpZGVvQ29kZWM7XG4gICAgdGhpcy5pbml0U2VnbWVudERhdGEgPSBpbml0U2VnbWVudERhdGE7XG4gICAgdGhpcy5kdXJhdGlvbiA9IGR1cmF0aW9uO1xuICAgIHRoaXMuZGVmYXVsdEluaXRQdHMgPSBkZWZhdWx0SW5pdFB0cyB8fCBudWxsO1xuICB9XG59XG5jbGFzcyBUcmFuc211eFN0YXRlIHtcbiAgY29uc3RydWN0b3IoZGlzY29udGludWl0eSwgY29udGlndW91cywgYWNjdXJhdGVUaW1lT2Zmc2V0LCB0cmFja1N3aXRjaCwgdGltZU9mZnNldCwgaW5pdFNlZ21lbnRDaGFuZ2UpIHtcbiAgICB0aGlzLmRpc2NvbnRpbnVpdHkgPSB2b2lkIDA7XG4gICAgdGhpcy5jb250aWd1b3VzID0gdm9pZCAwO1xuICAgIHRoaXMuYWNjdXJhdGVUaW1lT2Zmc2V0ID0gdm9pZCAwO1xuICAgIHRoaXMudHJhY2tTd2l0Y2ggPSB2b2lkIDA7XG4gICAgdGhpcy50aW1lT2Zmc2V0ID0gdm9pZCAwO1xuICAgIHRoaXMuaW5pdFNlZ21lbnRDaGFuZ2UgPSB2b2lkIDA7XG4gICAgdGhpcy5kaXNjb250aW51aXR5ID0gZGlzY29udGludWl0eTtcbiAgICB0aGlzLmNvbnRpZ3VvdXMgPSBjb250aWd1b3VzO1xuICAgIHRoaXMuYWNjdXJhdGVUaW1lT2Zmc2V0ID0gYWNjdXJhdGVUaW1lT2Zmc2V0O1xuICAgIHRoaXMudHJhY2tTd2l0Y2ggPSB0cmFja1N3aXRjaDtcbiAgICB0aGlzLnRpbWVPZmZzZXQgPSB0aW1lT2Zmc2V0O1xuICAgIHRoaXMuaW5pdFNlZ21lbnRDaGFuZ2UgPSBpbml0U2VnbWVudENoYW5nZTtcbiAgfVxufVxuXG52YXIgZXZlbnRlbWl0dGVyMyA9IHtleHBvcnRzOiB7fX07XG5cbihmdW5jdGlvbiAobW9kdWxlKSB7XG5cblx0dmFyIGhhcyA9IE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHlcblx0ICAsIHByZWZpeCA9ICd+JztcblxuXHQvKipcblx0ICogQ29uc3RydWN0b3IgdG8gY3JlYXRlIGEgc3RvcmFnZSBmb3Igb3VyIGBFRWAgb2JqZWN0cy5cblx0ICogQW4gYEV2ZW50c2AgaW5zdGFuY2UgaXMgYSBwbGFpbiBvYmplY3Qgd2hvc2UgcHJvcGVydGllcyBhcmUgZXZlbnQgbmFtZXMuXG5cdCAqXG5cdCAqIEBjb25zdHJ1Y3RvclxuXHQgKiBAcHJpdmF0ZVxuXHQgKi9cblx0ZnVuY3Rpb24gRXZlbnRzKCkge31cblxuXHQvL1xuXHQvLyBXZSB0cnkgdG8gbm90IGluaGVyaXQgZnJvbSBgT2JqZWN0LnByb3RvdHlwZWAuIEluIHNvbWUgZW5naW5lcyBjcmVhdGluZyBhblxuXHQvLyBpbnN0YW5jZSBpbiB0aGlzIHdheSBpcyBmYXN0ZXIgdGhhbiBjYWxsaW5nIGBPYmplY3QuY3JlYXRlKG51bGwpYCBkaXJlY3RseS5cblx0Ly8gSWYgYE9iamVjdC5jcmVhdGUobnVsbClgIGlzIG5vdCBzdXBwb3J0ZWQgd2UgcHJlZml4IHRoZSBldmVudCBuYW1lcyB3aXRoIGFcblx0Ly8gY2hhcmFjdGVyIHRvIG1ha2Ugc3VyZSB0aGF0IHRoZSBidWlsdC1pbiBvYmplY3QgcHJvcGVydGllcyBhcmUgbm90XG5cdC8vIG92ZXJyaWRkZW4gb3IgdXNlZCBhcyBhbiBhdHRhY2sgdmVjdG9yLlxuXHQvL1xuXHRpZiAoT2JqZWN0LmNyZWF0ZSkge1xuXHQgIEV2ZW50cy5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKG51bGwpO1xuXG5cdCAgLy9cblx0ICAvLyBUaGlzIGhhY2sgaXMgbmVlZGVkIGJlY2F1c2UgdGhlIGBfX3Byb3RvX19gIHByb3BlcnR5IGlzIHN0aWxsIGluaGVyaXRlZCBpblxuXHQgIC8vIHNvbWUgb2xkIGJyb3dzZXJzIGxpa2UgQW5kcm9pZCA0LCBpUGhvbmUgNS4xLCBPcGVyYSAxMSBhbmQgU2FmYXJpIDUuXG5cdCAgLy9cblx0ICBpZiAoIW5ldyBFdmVudHMoKS5fX3Byb3RvX18pIHByZWZpeCA9IGZhbHNlO1xuXHR9XG5cblx0LyoqXG5cdCAqIFJlcHJlc2VudGF0aW9uIG9mIGEgc2luZ2xlIGV2ZW50IGxpc3RlbmVyLlxuXHQgKlxuXHQgKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiBUaGUgbGlzdGVuZXIgZnVuY3Rpb24uXG5cdCAqIEBwYXJhbSB7Kn0gY29udGV4dCBUaGUgY29udGV4dCB0byBpbnZva2UgdGhlIGxpc3RlbmVyIHdpdGguXG5cdCAqIEBwYXJhbSB7Qm9vbGVhbn0gW29uY2U9ZmFsc2VdIFNwZWNpZnkgaWYgdGhlIGxpc3RlbmVyIGlzIGEgb25lLXRpbWUgbGlzdGVuZXIuXG5cdCAqIEBjb25zdHJ1Y3RvclxuXHQgKiBAcHJpdmF0ZVxuXHQgKi9cblx0ZnVuY3Rpb24gRUUoZm4sIGNvbnRleHQsIG9uY2UpIHtcblx0ICB0aGlzLmZuID0gZm47XG5cdCAgdGhpcy5jb250ZXh0ID0gY29udGV4dDtcblx0ICB0aGlzLm9uY2UgPSBvbmNlIHx8IGZhbHNlO1xuXHR9XG5cblx0LyoqXG5cdCAqIEFkZCBhIGxpc3RlbmVyIGZvciBhIGdpdmVuIGV2ZW50LlxuXHQgKlxuXHQgKiBAcGFyYW0ge0V2ZW50RW1pdHRlcn0gZW1pdHRlciBSZWZlcmVuY2UgdG8gdGhlIGBFdmVudEVtaXR0ZXJgIGluc3RhbmNlLlxuXHQgKiBAcGFyYW0geyhTdHJpbmd8U3ltYm9sKX0gZXZlbnQgVGhlIGV2ZW50IG5hbWUuXG5cdCAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIFRoZSBsaXN0ZW5lciBmdW5jdGlvbi5cblx0ICogQHBhcmFtIHsqfSBjb250ZXh0IFRoZSBjb250ZXh0IHRvIGludm9rZSB0aGUgbGlzdGVuZXIgd2l0aC5cblx0ICogQHBhcmFtIHtCb29sZWFufSBvbmNlIFNwZWNpZnkgaWYgdGhlIGxpc3RlbmVyIGlzIGEgb25lLXRpbWUgbGlzdGVuZXIuXG5cdCAqIEByZXR1cm5zIHtFdmVudEVtaXR0ZXJ9XG5cdCAqIEBwcml2YXRlXG5cdCAqL1xuXHRmdW5jdGlvbiBhZGRMaXN0ZW5lcihlbWl0dGVyLCBldmVudCwgZm4sIGNvbnRleHQsIG9uY2UpIHtcblx0ICBpZiAodHlwZW9mIGZuICE9PSAnZnVuY3Rpb24nKSB7XG5cdCAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdUaGUgbGlzdGVuZXIgbXVzdCBiZSBhIGZ1bmN0aW9uJyk7XG5cdCAgfVxuXG5cdCAgdmFyIGxpc3RlbmVyID0gbmV3IEVFKGZuLCBjb250ZXh0IHx8IGVtaXR0ZXIsIG9uY2UpXG5cdCAgICAsIGV2dCA9IHByZWZpeCA/IHByZWZpeCArIGV2ZW50IDogZXZlbnQ7XG5cblx0ICBpZiAoIWVtaXR0ZXIuX2V2ZW50c1tldnRdKSBlbWl0dGVyLl9ldmVudHNbZXZ0XSA9IGxpc3RlbmVyLCBlbWl0dGVyLl9ldmVudHNDb3VudCsrO1xuXHQgIGVsc2UgaWYgKCFlbWl0dGVyLl9ldmVudHNbZXZ0XS5mbikgZW1pdHRlci5fZXZlbnRzW2V2dF0ucHVzaChsaXN0ZW5lcik7XG5cdCAgZWxzZSBlbWl0dGVyLl9ldmVudHNbZXZ0XSA9IFtlbWl0dGVyLl9ldmVudHNbZXZ0XSwgbGlzdGVuZXJdO1xuXG5cdCAgcmV0dXJuIGVtaXR0ZXI7XG5cdH1cblxuXHQvKipcblx0ICogQ2xlYXIgZXZlbnQgYnkgbmFtZS5cblx0ICpcblx0ICogQHBhcmFtIHtFdmVudEVtaXR0ZXJ9IGVtaXR0ZXIgUmVmZXJlbmNlIHRvIHRoZSBgRXZlbnRFbWl0dGVyYCBpbnN0YW5jZS5cblx0ICogQHBhcmFtIHsoU3RyaW5nfFN5bWJvbCl9IGV2dCBUaGUgRXZlbnQgbmFtZS5cblx0ICogQHByaXZhdGVcblx0ICovXG5cdGZ1bmN0aW9uIGNsZWFyRXZlbnQoZW1pdHRlciwgZXZ0KSB7XG5cdCAgaWYgKC0tZW1pdHRlci5fZXZlbnRzQ291bnQgPT09IDApIGVtaXR0ZXIuX2V2ZW50cyA9IG5ldyBFdmVudHMoKTtcblx0ICBlbHNlIGRlbGV0ZSBlbWl0dGVyLl9ldmVudHNbZXZ0XTtcblx0fVxuXG5cdC8qKlxuXHQgKiBNaW5pbWFsIGBFdmVudEVtaXR0ZXJgIGludGVyZmFjZSB0aGF0IGlzIG1vbGRlZCBhZ2FpbnN0IHRoZSBOb2RlLmpzXG5cdCAqIGBFdmVudEVtaXR0ZXJgIGludGVyZmFjZS5cblx0ICpcblx0ICogQGNvbnN0cnVjdG9yXG5cdCAqIEBwdWJsaWNcblx0ICovXG5cdGZ1bmN0aW9uIEV2ZW50RW1pdHRlcigpIHtcblx0ICB0aGlzLl9ldmVudHMgPSBuZXcgRXZlbnRzKCk7XG5cdCAgdGhpcy5fZXZlbnRzQ291bnQgPSAwO1xuXHR9XG5cblx0LyoqXG5cdCAqIFJldHVybiBhbiBhcnJheSBsaXN0aW5nIHRoZSBldmVudHMgZm9yIHdoaWNoIHRoZSBlbWl0dGVyIGhhcyByZWdpc3RlcmVkXG5cdCAqIGxpc3RlbmVycy5cblx0ICpcblx0ICogQHJldHVybnMge0FycmF5fVxuXHQgKiBAcHVibGljXG5cdCAqL1xuXHRFdmVudEVtaXR0ZXIucHJvdG90eXBlLmV2ZW50TmFtZXMgPSBmdW5jdGlvbiBldmVudE5hbWVzKCkge1xuXHQgIHZhciBuYW1lcyA9IFtdXG5cdCAgICAsIGV2ZW50c1xuXHQgICAgLCBuYW1lO1xuXG5cdCAgaWYgKHRoaXMuX2V2ZW50c0NvdW50ID09PSAwKSByZXR1cm4gbmFtZXM7XG5cblx0ICBmb3IgKG5hbWUgaW4gKGV2ZW50cyA9IHRoaXMuX2V2ZW50cykpIHtcblx0ICAgIGlmIChoYXMuY2FsbChldmVudHMsIG5hbWUpKSBuYW1lcy5wdXNoKHByZWZpeCA/IG5hbWUuc2xpY2UoMSkgOiBuYW1lKTtcblx0ICB9XG5cblx0ICBpZiAoT2JqZWN0LmdldE93blByb3BlcnR5U3ltYm9scykge1xuXHQgICAgcmV0dXJuIG5hbWVzLmNvbmNhdChPYmplY3QuZ2V0T3duUHJvcGVydHlTeW1ib2xzKGV2ZW50cykpO1xuXHQgIH1cblxuXHQgIHJldHVybiBuYW1lcztcblx0fTtcblxuXHQvKipcblx0ICogUmV0dXJuIHRoZSBsaXN0ZW5lcnMgcmVnaXN0ZXJlZCBmb3IgYSBnaXZlbiBldmVudC5cblx0ICpcblx0ICogQHBhcmFtIHsoU3RyaW5nfFN5bWJvbCl9IGV2ZW50IFRoZSBldmVudCBuYW1lLlxuXHQgKiBAcmV0dXJucyB7QXJyYXl9IFRoZSByZWdpc3RlcmVkIGxpc3RlbmVycy5cblx0ICogQHB1YmxpY1xuXHQgKi9cblx0RXZlbnRFbWl0dGVyLnByb3RvdHlwZS5saXN0ZW5lcnMgPSBmdW5jdGlvbiBsaXN0ZW5lcnMoZXZlbnQpIHtcblx0ICB2YXIgZXZ0ID0gcHJlZml4ID8gcHJlZml4ICsgZXZlbnQgOiBldmVudFxuXHQgICAgLCBoYW5kbGVycyA9IHRoaXMuX2V2ZW50c1tldnRdO1xuXG5cdCAgaWYgKCFoYW5kbGVycykgcmV0dXJuIFtdO1xuXHQgIGlmIChoYW5kbGVycy5mbikgcmV0dXJuIFtoYW5kbGVycy5mbl07XG5cblx0ICBmb3IgKHZhciBpID0gMCwgbCA9IGhhbmRsZXJzLmxlbmd0aCwgZWUgPSBuZXcgQXJyYXkobCk7IGkgPCBsOyBpKyspIHtcblx0ICAgIGVlW2ldID0gaGFuZGxlcnNbaV0uZm47XG5cdCAgfVxuXG5cdCAgcmV0dXJuIGVlO1xuXHR9O1xuXG5cdC8qKlxuXHQgKiBSZXR1cm4gdGhlIG51bWJlciBvZiBsaXN0ZW5lcnMgbGlzdGVuaW5nIHRvIGEgZ2l2ZW4gZXZlbnQuXG5cdCAqXG5cdCAqIEBwYXJhbSB7KFN0cmluZ3xTeW1ib2wpfSBldmVudCBUaGUgZXZlbnQgbmFtZS5cblx0ICogQHJldHVybnMge051bWJlcn0gVGhlIG51bWJlciBvZiBsaXN0ZW5lcnMuXG5cdCAqIEBwdWJsaWNcblx0ICovXG5cdEV2ZW50RW1pdHRlci5wcm90b3R5cGUubGlzdGVuZXJDb3VudCA9IGZ1bmN0aW9uIGxpc3RlbmVyQ291bnQoZXZlbnQpIHtcblx0ICB2YXIgZXZ0ID0gcHJlZml4ID8gcHJlZml4ICsgZXZlbnQgOiBldmVudFxuXHQgICAgLCBsaXN0ZW5lcnMgPSB0aGlzLl9ldmVudHNbZXZ0XTtcblxuXHQgIGlmICghbGlzdGVuZXJzKSByZXR1cm4gMDtcblx0ICBpZiAobGlzdGVuZXJzLmZuKSByZXR1cm4gMTtcblx0ICByZXR1cm4gbGlzdGVuZXJzLmxlbmd0aDtcblx0fTtcblxuXHQvKipcblx0ICogQ2FsbHMgZWFjaCBvZiB0aGUgbGlzdGVuZXJzIHJlZ2lzdGVyZWQgZm9yIGEgZ2l2ZW4gZXZlbnQuXG5cdCAqXG5cdCAqIEBwYXJhbSB7KFN0cmluZ3xTeW1ib2wpfSBldmVudCBUaGUgZXZlbnQgbmFtZS5cblx0ICogQHJldHVybnMge0Jvb2xlYW59IGB0cnVlYCBpZiB0aGUgZXZlbnQgaGFkIGxpc3RlbmVycywgZWxzZSBgZmFsc2VgLlxuXHQgKiBAcHVibGljXG5cdCAqL1xuXHRFdmVudEVtaXR0ZXIucHJvdG90eXBlLmVtaXQgPSBmdW5jdGlvbiBlbWl0KGV2ZW50LCBhMSwgYTIsIGEzLCBhNCwgYTUpIHtcblx0ICB2YXIgZXZ0ID0gcHJlZml4ID8gcHJlZml4ICsgZXZlbnQgOiBldmVudDtcblxuXHQgIGlmICghdGhpcy5fZXZlbnRzW2V2dF0pIHJldHVybiBmYWxzZTtcblxuXHQgIHZhciBsaXN0ZW5lcnMgPSB0aGlzLl9ldmVudHNbZXZ0XVxuXHQgICAgLCBsZW4gPSBhcmd1bWVudHMubGVuZ3RoXG5cdCAgICAsIGFyZ3Ncblx0ICAgICwgaTtcblxuXHQgIGlmIChsaXN0ZW5lcnMuZm4pIHtcblx0ICAgIGlmIChsaXN0ZW5lcnMub25jZSkgdGhpcy5yZW1vdmVMaXN0ZW5lcihldmVudCwgbGlzdGVuZXJzLmZuLCB1bmRlZmluZWQsIHRydWUpO1xuXG5cdCAgICBzd2l0Y2ggKGxlbikge1xuXHQgICAgICBjYXNlIDE6IHJldHVybiBsaXN0ZW5lcnMuZm4uY2FsbChsaXN0ZW5lcnMuY29udGV4dCksIHRydWU7XG5cdCAgICAgIGNhc2UgMjogcmV0dXJuIGxpc3RlbmVycy5mbi5jYWxsKGxpc3RlbmVycy5jb250ZXh0LCBhMSksIHRydWU7XG5cdCAgICAgIGNhc2UgMzogcmV0dXJuIGxpc3RlbmVycy5mbi5jYWxsKGxpc3RlbmVycy5jb250ZXh0LCBhMSwgYTIpLCB0cnVlO1xuXHQgICAgICBjYXNlIDQ6IHJldHVybiBsaXN0ZW5lcnMuZm4uY2FsbChsaXN0ZW5lcnMuY29udGV4dCwgYTEsIGEyLCBhMyksIHRydWU7XG5cdCAgICAgIGNhc2UgNTogcmV0dXJuIGxpc3RlbmVycy5mbi5jYWxsKGxpc3RlbmVycy5jb250ZXh0LCBhMSwgYTIsIGEzLCBhNCksIHRydWU7XG5cdCAgICAgIGNhc2UgNjogcmV0dXJuIGxpc3RlbmVycy5mbi5jYWxsKGxpc3RlbmVycy5jb250ZXh0LCBhMSwgYTIsIGEzLCBhNCwgYTUpLCB0cnVlO1xuXHQgICAgfVxuXG5cdCAgICBmb3IgKGkgPSAxLCBhcmdzID0gbmV3IEFycmF5KGxlbiAtMSk7IGkgPCBsZW47IGkrKykge1xuXHQgICAgICBhcmdzW2kgLSAxXSA9IGFyZ3VtZW50c1tpXTtcblx0ICAgIH1cblxuXHQgICAgbGlzdGVuZXJzLmZuLmFwcGx5KGxpc3RlbmVycy5jb250ZXh0LCBhcmdzKTtcblx0ICB9IGVsc2Uge1xuXHQgICAgdmFyIGxlbmd0aCA9IGxpc3RlbmVycy5sZW5ndGhcblx0ICAgICAgLCBqO1xuXG5cdCAgICBmb3IgKGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHtcblx0ICAgICAgaWYgKGxpc3RlbmVyc1tpXS5vbmNlKSB0aGlzLnJlbW92ZUxpc3RlbmVyKGV2ZW50LCBsaXN0ZW5lcnNbaV0uZm4sIHVuZGVmaW5lZCwgdHJ1ZSk7XG5cblx0ICAgICAgc3dpdGNoIChsZW4pIHtcblx0ICAgICAgICBjYXNlIDE6IGxpc3RlbmVyc1tpXS5mbi5jYWxsKGxpc3RlbmVyc1tpXS5jb250ZXh0KTsgYnJlYWs7XG5cdCAgICAgICAgY2FzZSAyOiBsaXN0ZW5lcnNbaV0uZm4uY2FsbChsaXN0ZW5lcnNbaV0uY29udGV4dCwgYTEpOyBicmVhaztcblx0ICAgICAgICBjYXNlIDM6IGxpc3RlbmVyc1tpXS5mbi5jYWxsKGxpc3RlbmVyc1tpXS5jb250ZXh0LCBhMSwgYTIpOyBicmVhaztcblx0ICAgICAgICBjYXNlIDQ6IGxpc3RlbmVyc1tpXS5mbi5jYWxsKGxpc3RlbmVyc1tpXS5jb250ZXh0LCBhMSwgYTIsIGEzKTsgYnJlYWs7XG5cdCAgICAgICAgZGVmYXVsdDpcblx0ICAgICAgICAgIGlmICghYXJncykgZm9yIChqID0gMSwgYXJncyA9IG5ldyBBcnJheShsZW4gLTEpOyBqIDwgbGVuOyBqKyspIHtcblx0ICAgICAgICAgICAgYXJnc1tqIC0gMV0gPSBhcmd1bWVudHNbal07XG5cdCAgICAgICAgICB9XG5cblx0ICAgICAgICAgIGxpc3RlbmVyc1tpXS5mbi5hcHBseShsaXN0ZW5lcnNbaV0uY29udGV4dCwgYXJncyk7XG5cdCAgICAgIH1cblx0ICAgIH1cblx0ICB9XG5cblx0ICByZXR1cm4gdHJ1ZTtcblx0fTtcblxuXHQvKipcblx0ICogQWRkIGEgbGlzdGVuZXIgZm9yIGEgZ2l2ZW4gZXZlbnQuXG5cdCAqXG5cdCAqIEBwYXJhbSB7KFN0cmluZ3xTeW1ib2wpfSBldmVudCBUaGUgZXZlbnQgbmFtZS5cblx0ICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gVGhlIGxpc3RlbmVyIGZ1bmN0aW9uLlxuXHQgKiBAcGFyYW0geyp9IFtjb250ZXh0PXRoaXNdIFRoZSBjb250ZXh0IHRvIGludm9rZSB0aGUgbGlzdGVuZXIgd2l0aC5cblx0ICogQHJldHVybnMge0V2ZW50RW1pdHRlcn0gYHRoaXNgLlxuXHQgKiBAcHVibGljXG5cdCAqL1xuXHRFdmVudEVtaXR0ZXIucHJvdG90eXBlLm9uID0gZnVuY3Rpb24gb24oZXZlbnQsIGZuLCBjb250ZXh0KSB7XG5cdCAgcmV0dXJuIGFkZExpc3RlbmVyKHRoaXMsIGV2ZW50LCBmbiwgY29udGV4dCwgZmFsc2UpO1xuXHR9O1xuXG5cdC8qKlxuXHQgKiBBZGQgYSBvbmUtdGltZSBsaXN0ZW5lciBmb3IgYSBnaXZlbiBldmVudC5cblx0ICpcblx0ICogQHBhcmFtIHsoU3RyaW5nfFN5bWJvbCl9IGV2ZW50IFRoZSBldmVudCBuYW1lLlxuXHQgKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiBUaGUgbGlzdGVuZXIgZnVuY3Rpb24uXG5cdCAqIEBwYXJhbSB7Kn0gW2NvbnRleHQ9dGhpc10gVGhlIGNvbnRleHQgdG8gaW52b2tlIHRoZSBsaXN0ZW5lciB3aXRoLlxuXHQgKiBAcmV0dXJucyB7RXZlbnRFbWl0dGVyfSBgdGhpc2AuXG5cdCAqIEBwdWJsaWNcblx0ICovXG5cdEV2ZW50RW1pdHRlci5wcm90b3R5cGUub25jZSA9IGZ1bmN0aW9uIG9uY2UoZXZlbnQsIGZuLCBjb250ZXh0KSB7XG5cdCAgcmV0dXJuIGFkZExpc3RlbmVyKHRoaXMsIGV2ZW50LCBmbiwgY29udGV4dCwgdHJ1ZSk7XG5cdH07XG5cblx0LyoqXG5cdCAqIFJlbW92ZSB0aGUgbGlzdGVuZXJzIG9mIGEgZ2l2ZW4gZXZlbnQuXG5cdCAqXG5cdCAqIEBwYXJhbSB7KFN0cmluZ3xTeW1ib2wpfSBldmVudCBUaGUgZXZlbnQgbmFtZS5cblx0ICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gT25seSByZW1vdmUgdGhlIGxpc3RlbmVycyB0aGF0IG1hdGNoIHRoaXMgZnVuY3Rpb24uXG5cdCAqIEBwYXJhbSB7Kn0gY29udGV4dCBPbmx5IHJlbW92ZSB0aGUgbGlzdGVuZXJzIHRoYXQgaGF2ZSB0aGlzIGNvbnRleHQuXG5cdCAqIEBwYXJhbSB7Qm9vbGVhbn0gb25jZSBPbmx5IHJlbW92ZSBvbmUtdGltZSBsaXN0ZW5lcnMuXG5cdCAqIEByZXR1cm5zIHtFdmVudEVtaXR0ZXJ9IGB0aGlzYC5cblx0ICogQHB1YmxpY1xuXHQgKi9cblx0RXZlbnRFbWl0dGVyLnByb3RvdHlwZS5yZW1vdmVMaXN0ZW5lciA9IGZ1bmN0aW9uIHJlbW92ZUxpc3RlbmVyKGV2ZW50LCBmbiwgY29udGV4dCwgb25jZSkge1xuXHQgIHZhciBldnQgPSBwcmVmaXggPyBwcmVmaXggKyBldmVudCA6IGV2ZW50O1xuXG5cdCAgaWYgKCF0aGlzLl9ldmVudHNbZXZ0XSkgcmV0dXJuIHRoaXM7XG5cdCAgaWYgKCFmbikge1xuXHQgICAgY2xlYXJFdmVudCh0aGlzLCBldnQpO1xuXHQgICAgcmV0dXJuIHRoaXM7XG5cdCAgfVxuXG5cdCAgdmFyIGxpc3RlbmVycyA9IHRoaXMuX2V2ZW50c1tldnRdO1xuXG5cdCAgaWYgKGxpc3RlbmVycy5mbikge1xuXHQgICAgaWYgKFxuXHQgICAgICBsaXN0ZW5lcnMuZm4gPT09IGZuICYmXG5cdCAgICAgICghb25jZSB8fCBsaXN0ZW5lcnMub25jZSkgJiZcblx0ICAgICAgKCFjb250ZXh0IHx8IGxpc3RlbmVycy5jb250ZXh0ID09PSBjb250ZXh0KVxuXHQgICAgKSB7XG5cdCAgICAgIGNsZWFyRXZlbnQodGhpcywgZXZ0KTtcblx0ICAgIH1cblx0ICB9IGVsc2Uge1xuXHQgICAgZm9yICh2YXIgaSA9IDAsIGV2ZW50cyA9IFtdLCBsZW5ndGggPSBsaXN0ZW5lcnMubGVuZ3RoOyBpIDwgbGVuZ3RoOyBpKyspIHtcblx0ICAgICAgaWYgKFxuXHQgICAgICAgIGxpc3RlbmVyc1tpXS5mbiAhPT0gZm4gfHxcblx0ICAgICAgICAob25jZSAmJiAhbGlzdGVuZXJzW2ldLm9uY2UpIHx8XG5cdCAgICAgICAgKGNvbnRleHQgJiYgbGlzdGVuZXJzW2ldLmNvbnRleHQgIT09IGNvbnRleHQpXG5cdCAgICAgICkge1xuXHQgICAgICAgIGV2ZW50cy5wdXNoKGxpc3RlbmVyc1tpXSk7XG5cdCAgICAgIH1cblx0ICAgIH1cblxuXHQgICAgLy9cblx0ICAgIC8vIFJlc2V0IHRoZSBhcnJheSwgb3IgcmVtb3ZlIGl0IGNvbXBsZXRlbHkgaWYgd2UgaGF2ZSBubyBtb3JlIGxpc3RlbmVycy5cblx0ICAgIC8vXG5cdCAgICBpZiAoZXZlbnRzLmxlbmd0aCkgdGhpcy5fZXZlbnRzW2V2dF0gPSBldmVudHMubGVuZ3RoID09PSAxID8gZXZlbnRzWzBdIDogZXZlbnRzO1xuXHQgICAgZWxzZSBjbGVhckV2ZW50KHRoaXMsIGV2dCk7XG5cdCAgfVxuXG5cdCAgcmV0dXJuIHRoaXM7XG5cdH07XG5cblx0LyoqXG5cdCAqIFJlbW92ZSBhbGwgbGlzdGVuZXJzLCBvciB0aG9zZSBvZiB0aGUgc3BlY2lmaWVkIGV2ZW50LlxuXHQgKlxuXHQgKiBAcGFyYW0geyhTdHJpbmd8U3ltYm9sKX0gW2V2ZW50XSBUaGUgZXZlbnQgbmFtZS5cblx0ICogQHJldHVybnMge0V2ZW50RW1pdHRlcn0gYHRoaXNgLlxuXHQgKiBAcHVibGljXG5cdCAqL1xuXHRFdmVudEVtaXR0ZXIucHJvdG90eXBlLnJlbW92ZUFsbExpc3RlbmVycyA9IGZ1bmN0aW9uIHJlbW92ZUFsbExpc3RlbmVycyhldmVudCkge1xuXHQgIHZhciBldnQ7XG5cblx0ICBpZiAoZXZlbnQpIHtcblx0ICAgIGV2dCA9IHByZWZpeCA/IHByZWZpeCArIGV2ZW50IDogZXZlbnQ7XG5cdCAgICBpZiAodGhpcy5fZXZlbnRzW2V2dF0pIGNsZWFyRXZlbnQodGhpcywgZXZ0KTtcblx0ICB9IGVsc2Uge1xuXHQgICAgdGhpcy5fZXZlbnRzID0gbmV3IEV2ZW50cygpO1xuXHQgICAgdGhpcy5fZXZlbnRzQ291bnQgPSAwO1xuXHQgIH1cblxuXHQgIHJldHVybiB0aGlzO1xuXHR9O1xuXG5cdC8vXG5cdC8vIEFsaWFzIG1ldGhvZHMgbmFtZXMgYmVjYXVzZSBwZW9wbGUgcm9sbCBsaWtlIHRoYXQuXG5cdC8vXG5cdEV2ZW50RW1pdHRlci5wcm90b3R5cGUub2ZmID0gRXZlbnRFbWl0dGVyLnByb3RvdHlwZS5yZW1vdmVMaXN0ZW5lcjtcblx0RXZlbnRFbWl0dGVyLnByb3RvdHlwZS5hZGRMaXN0ZW5lciA9IEV2ZW50RW1pdHRlci5wcm90b3R5cGUub247XG5cblx0Ly9cblx0Ly8gRXhwb3NlIHRoZSBwcmVmaXguXG5cdC8vXG5cdEV2ZW50RW1pdHRlci5wcmVmaXhlZCA9IHByZWZpeDtcblxuXHQvL1xuXHQvLyBBbGxvdyBgRXZlbnRFbWl0dGVyYCB0byBiZSBpbXBvcnRlZCBhcyBtb2R1bGUgbmFtZXNwYWNlLlxuXHQvL1xuXHRFdmVudEVtaXR0ZXIuRXZlbnRFbWl0dGVyID0gRXZlbnRFbWl0dGVyO1xuXG5cdC8vXG5cdC8vIEV4cG9zZSB0aGUgbW9kdWxlLlxuXHQvL1xuXHR7XG5cdCAgbW9kdWxlLmV4cG9ydHMgPSBFdmVudEVtaXR0ZXI7XG5cdH0gXG59IChldmVudGVtaXR0ZXIzKSk7XG5cbnZhciBldmVudGVtaXR0ZXIzRXhwb3J0cyA9IGV2ZW50ZW1pdHRlcjMuZXhwb3J0cztcbnZhciBFdmVudEVtaXR0ZXIgPSAvKkBfX1BVUkVfXyovZ2V0RGVmYXVsdEV4cG9ydEZyb21DanMoZXZlbnRlbWl0dGVyM0V4cG9ydHMpO1xuXG5jbGFzcyBUcmFuc211eGVySW50ZXJmYWNlIHtcbiAgY29uc3RydWN0b3IoaGxzLCBpZCwgb25UcmFuc211eENvbXBsZXRlLCBvbkZsdXNoKSB7XG4gICAgdGhpcy5lcnJvciA9IG51bGw7XG4gICAgdGhpcy5obHMgPSB2b2lkIDA7XG4gICAgdGhpcy5pZCA9IHZvaWQgMDtcbiAgICB0aGlzLm9ic2VydmVyID0gdm9pZCAwO1xuICAgIHRoaXMuZnJhZyA9IG51bGw7XG4gICAgdGhpcy5wYXJ0ID0gbnVsbDtcbiAgICB0aGlzLnVzZVdvcmtlciA9IHZvaWQgMDtcbiAgICB0aGlzLndvcmtlckNvbnRleHQgPSBudWxsO1xuICAgIHRoaXMub253bXNnID0gdm9pZCAwO1xuICAgIHRoaXMudHJhbnNtdXhlciA9IG51bGw7XG4gICAgdGhpcy5vblRyYW5zbXV4Q29tcGxldGUgPSB2b2lkIDA7XG4gICAgdGhpcy5vbkZsdXNoID0gdm9pZCAwO1xuICAgIGNvbnN0IGNvbmZpZyA9IGhscy5jb25maWc7XG4gICAgdGhpcy5obHMgPSBobHM7XG4gICAgdGhpcy5pZCA9IGlkO1xuICAgIHRoaXMudXNlV29ya2VyID0gISFjb25maWcuZW5hYmxlV29ya2VyO1xuICAgIHRoaXMub25UcmFuc211eENvbXBsZXRlID0gb25UcmFuc211eENvbXBsZXRlO1xuICAgIHRoaXMub25GbHVzaCA9IG9uRmx1c2g7XG4gICAgY29uc3QgZm9yd2FyZE1lc3NhZ2UgPSAoZXYsIGRhdGEpID0+IHtcbiAgICAgIGRhdGEgPSBkYXRhIHx8IHt9O1xuICAgICAgZGF0YS5mcmFnID0gdGhpcy5mcmFnO1xuICAgICAgZGF0YS5pZCA9IHRoaXMuaWQ7XG4gICAgICBpZiAoZXYgPT09IEV2ZW50cy5FUlJPUikge1xuICAgICAgICB0aGlzLmVycm9yID0gZGF0YS5lcnJvcjtcbiAgICAgIH1cbiAgICAgIHRoaXMuaGxzLnRyaWdnZXIoZXYsIGRhdGEpO1xuICAgIH07XG5cbiAgICAvLyBmb3J3YXJkIGV2ZW50cyB0byBtYWluIHRocmVhZFxuICAgIHRoaXMub2JzZXJ2ZXIgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG4gICAgdGhpcy5vYnNlcnZlci5vbihFdmVudHMuRlJBR19ERUNSWVBURUQsIGZvcndhcmRNZXNzYWdlKTtcbiAgICB0aGlzLm9ic2VydmVyLm9uKEV2ZW50cy5FUlJPUiwgZm9yd2FyZE1lc3NhZ2UpO1xuICAgIGNvbnN0IE1lZGlhU291cmNlID0gZ2V0TWVkaWFTb3VyY2UoY29uZmlnLnByZWZlck1hbmFnZWRNZWRpYVNvdXJjZSkgfHwge1xuICAgICAgaXNUeXBlU3VwcG9ydGVkOiAoKSA9PiBmYWxzZVxuICAgIH07XG4gICAgY29uc3QgbTJ0c1R5cGVTdXBwb3J0ZWQgPSB7XG4gICAgICBtcGVnOiBNZWRpYVNvdXJjZS5pc1R5cGVTdXBwb3J0ZWQoJ2F1ZGlvL21wZWcnKSxcbiAgICAgIG1wMzogTWVkaWFTb3VyY2UuaXNUeXBlU3VwcG9ydGVkKCdhdWRpby9tcDQ7IGNvZGVjcz1cIm1wM1wiJyksXG4gICAgICBhYzM6IE1lZGlhU291cmNlLmlzVHlwZVN1cHBvcnRlZCgnYXVkaW8vbXA0OyBjb2RlY3M9XCJhYy0zXCInKSBcbiAgICB9O1xuICAgIGlmICh0aGlzLnVzZVdvcmtlciAmJiB0eXBlb2YgV29ya2VyICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgY29uc3QgY2FuQ3JlYXRlV29ya2VyID0gY29uZmlnLndvcmtlclBhdGggfHwgaGFzVU1EV29ya2VyKCk7XG4gICAgICBpZiAoY2FuQ3JlYXRlV29ya2VyKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgaWYgKGNvbmZpZy53b3JrZXJQYXRoKSB7XG4gICAgICAgICAgICBsb2dnZXIubG9nKGBsb2FkaW5nIFdlYiBXb3JrZXIgJHtjb25maWcud29ya2VyUGF0aH0gZm9yIFwiJHtpZH1cImApO1xuICAgICAgICAgICAgdGhpcy53b3JrZXJDb250ZXh0ID0gbG9hZFdvcmtlcihjb25maWcud29ya2VyUGF0aCk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGxvZ2dlci5sb2coYGluamVjdGluZyBXZWIgV29ya2VyIGZvciBcIiR7aWR9XCJgKTtcbiAgICAgICAgICAgIHRoaXMud29ya2VyQ29udGV4dCA9IGluamVjdFdvcmtlcigpO1xuICAgICAgICAgIH1cbiAgICAgICAgICB0aGlzLm9ud21zZyA9IGV2ZW50ID0+IHRoaXMub25Xb3JrZXJNZXNzYWdlKGV2ZW50KTtcbiAgICAgICAgICBjb25zdCB7XG4gICAgICAgICAgICB3b3JrZXJcbiAgICAgICAgICB9ID0gdGhpcy53b3JrZXJDb250ZXh0O1xuICAgICAgICAgIHdvcmtlci5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgdGhpcy5vbndtc2cpO1xuICAgICAgICAgIHdvcmtlci5vbmVycm9yID0gZXZlbnQgPT4ge1xuICAgICAgICAgICAgY29uc3QgZXJyb3IgPSBuZXcgRXJyb3IoYCR7ZXZlbnQubWVzc2FnZX0gICgke2V2ZW50LmZpbGVuYW1lfToke2V2ZW50LmxpbmVub30pYCk7XG4gICAgICAgICAgICBjb25maWcuZW5hYmxlV29ya2VyID0gZmFsc2U7XG4gICAgICAgICAgICBsb2dnZXIud2FybihgRXJyb3IgaW4gXCIke2lkfVwiIFdlYiBXb3JrZXIsIGZhbGxiYWNrIHRvIGlubGluZWApO1xuICAgICAgICAgICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuRVJST1IsIHtcbiAgICAgICAgICAgICAgdHlwZTogRXJyb3JUeXBlcy5PVEhFUl9FUlJPUixcbiAgICAgICAgICAgICAgZGV0YWlsczogRXJyb3JEZXRhaWxzLklOVEVSTkFMX0VYQ0VQVElPTixcbiAgICAgICAgICAgICAgZmF0YWw6IGZhbHNlLFxuICAgICAgICAgICAgICBldmVudDogJ2RlbXV4ZXJXb3JrZXInLFxuICAgICAgICAgICAgICBlcnJvclxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfTtcbiAgICAgICAgICB3b3JrZXIucG9zdE1lc3NhZ2Uoe1xuICAgICAgICAgICAgY21kOiAnaW5pdCcsXG4gICAgICAgICAgICB0eXBlU3VwcG9ydGVkOiBtMnRzVHlwZVN1cHBvcnRlZCxcbiAgICAgICAgICAgIHZlbmRvcjogJycsXG4gICAgICAgICAgICBpZDogaWQsXG4gICAgICAgICAgICBjb25maWc6IEpTT04uc3RyaW5naWZ5KGNvbmZpZylcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgbG9nZ2VyLndhcm4oYEVycm9yIHNldHRpbmcgdXAgXCIke2lkfVwiIFdlYiBXb3JrZXIsIGZhbGxiYWNrIHRvIGlubGluZWAsIGVycik7XG4gICAgICAgICAgdGhpcy5yZXNldFdvcmtlcigpO1xuICAgICAgICAgIHRoaXMuZXJyb3IgPSBudWxsO1xuICAgICAgICAgIHRoaXMudHJhbnNtdXhlciA9IG5ldyBUcmFuc211eGVyKHRoaXMub2JzZXJ2ZXIsIG0ydHNUeXBlU3VwcG9ydGVkLCBjb25maWcsICcnLCBpZCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgIH1cbiAgICB0aGlzLnRyYW5zbXV4ZXIgPSBuZXcgVHJhbnNtdXhlcih0aGlzLm9ic2VydmVyLCBtMnRzVHlwZVN1cHBvcnRlZCwgY29uZmlnLCAnJywgaWQpO1xuICB9XG4gIHJlc2V0V29ya2VyKCkge1xuICAgIGlmICh0aGlzLndvcmtlckNvbnRleHQpIHtcbiAgICAgIGNvbnN0IHtcbiAgICAgICAgd29ya2VyLFxuICAgICAgICBvYmplY3RVUkxcbiAgICAgIH0gPSB0aGlzLndvcmtlckNvbnRleHQ7XG4gICAgICBpZiAob2JqZWN0VVJMKSB7XG4gICAgICAgIC8vIHJldm9rZSB0aGUgT2JqZWN0IFVSTCB0aGF0IHdhcyB1c2VkIHRvIGNyZWF0ZSB0cmFuc211eGVyIHdvcmtlciwgc28gYXMgbm90IHRvIGxlYWsgaXRcbiAgICAgICAgc2VsZi5VUkwucmV2b2tlT2JqZWN0VVJMKG9iamVjdFVSTCk7XG4gICAgICB9XG4gICAgICB3b3JrZXIucmVtb3ZlRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIHRoaXMub253bXNnKTtcbiAgICAgIHdvcmtlci5vbmVycm9yID0gbnVsbDtcbiAgICAgIHdvcmtlci50ZXJtaW5hdGUoKTtcbiAgICAgIHRoaXMud29ya2VyQ29udGV4dCA9IG51bGw7XG4gICAgfVxuICB9XG4gIGRlc3Ryb3koKSB7XG4gICAgaWYgKHRoaXMud29ya2VyQ29udGV4dCkge1xuICAgICAgdGhpcy5yZXNldFdvcmtlcigpO1xuICAgICAgdGhpcy5vbndtc2cgPSB1bmRlZmluZWQ7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IHRyYW5zbXV4ZXIgPSB0aGlzLnRyYW5zbXV4ZXI7XG4gICAgICBpZiAodHJhbnNtdXhlcikge1xuICAgICAgICB0cmFuc211eGVyLmRlc3Ryb3koKTtcbiAgICAgICAgdGhpcy50cmFuc211eGVyID0gbnVsbDtcbiAgICAgIH1cbiAgICB9XG4gICAgY29uc3Qgb2JzZXJ2ZXIgPSB0aGlzLm9ic2VydmVyO1xuICAgIGlmIChvYnNlcnZlcikge1xuICAgICAgb2JzZXJ2ZXIucmVtb3ZlQWxsTGlzdGVuZXJzKCk7XG4gICAgfVxuICAgIHRoaXMuZnJhZyA9IG51bGw7XG4gICAgLy8gQHRzLWlnbm9yZVxuICAgIHRoaXMub2JzZXJ2ZXIgPSBudWxsO1xuICAgIC8vIEB0cy1pZ25vcmVcbiAgICB0aGlzLmhscyA9IG51bGw7XG4gIH1cbiAgcHVzaChkYXRhLCBpbml0U2VnbWVudERhdGEsIGF1ZGlvQ29kZWMsIHZpZGVvQ29kZWMsIGZyYWcsIHBhcnQsIGR1cmF0aW9uLCBhY2N1cmF0ZVRpbWVPZmZzZXQsIGNodW5rTWV0YSwgZGVmYXVsdEluaXRQVFMpIHtcbiAgICB2YXIgX2ZyYWckaW5pdFNlZ21lbnQsIF9sYXN0RnJhZyRpbml0U2VnbWVudDtcbiAgICBjaHVua01ldGEudHJhbnNtdXhpbmcuc3RhcnQgPSBzZWxmLnBlcmZvcm1hbmNlLm5vdygpO1xuICAgIGNvbnN0IHtcbiAgICAgIHRyYW5zbXV4ZXJcbiAgICB9ID0gdGhpcztcbiAgICBjb25zdCB0aW1lT2Zmc2V0ID0gcGFydCA/IHBhcnQuc3RhcnQgOiBmcmFnLnN0YXJ0O1xuICAgIC8vIFRPRE86IHB1c2ggXCJjbGVhci1sZWFkXCIgZGVjcnlwdCBkYXRhIGZvciB1bmVuY3J5cHRlZCBmcmFnbWVudHMgaW4gc3RyZWFtcyB3aXRoIGVuY3J5cHRlZCBvbmVzXG4gICAgY29uc3QgZGVjcnlwdGRhdGEgPSBmcmFnLmRlY3J5cHRkYXRhO1xuICAgIGNvbnN0IGxhc3RGcmFnID0gdGhpcy5mcmFnO1xuICAgIGNvbnN0IGRpc2NvbnRpbnVpdHkgPSAhKGxhc3RGcmFnICYmIGZyYWcuY2MgPT09IGxhc3RGcmFnLmNjKTtcbiAgICBjb25zdCB0cmFja1N3aXRjaCA9ICEobGFzdEZyYWcgJiYgY2h1bmtNZXRhLmxldmVsID09PSBsYXN0RnJhZy5sZXZlbCk7XG4gICAgY29uc3Qgc25EaWZmID0gbGFzdEZyYWcgPyBjaHVua01ldGEuc24gLSBsYXN0RnJhZy5zbiA6IC0xO1xuICAgIGNvbnN0IHBhcnREaWZmID0gdGhpcy5wYXJ0ID8gY2h1bmtNZXRhLnBhcnQgLSB0aGlzLnBhcnQuaW5kZXggOiAtMTtcbiAgICBjb25zdCBwcm9ncmVzc2l2ZSA9IHNuRGlmZiA9PT0gMCAmJiBjaHVua01ldGEuaWQgPiAxICYmIGNodW5rTWV0YS5pZCA9PT0gKGxhc3RGcmFnID09IG51bGwgPyB2b2lkIDAgOiBsYXN0RnJhZy5zdGF0cy5jaHVua0NvdW50KTtcbiAgICBjb25zdCBjb250aWd1b3VzID0gIXRyYWNrU3dpdGNoICYmIChzbkRpZmYgPT09IDEgfHwgc25EaWZmID09PSAwICYmIChwYXJ0RGlmZiA9PT0gMSB8fCBwcm9ncmVzc2l2ZSAmJiBwYXJ0RGlmZiA8PSAwKSk7XG4gICAgY29uc3Qgbm93ID0gc2VsZi5wZXJmb3JtYW5jZS5ub3coKTtcbiAgICBpZiAodHJhY2tTd2l0Y2ggfHwgc25EaWZmIHx8IGZyYWcuc3RhdHMucGFyc2luZy5zdGFydCA9PT0gMCkge1xuICAgICAgZnJhZy5zdGF0cy5wYXJzaW5nLnN0YXJ0ID0gbm93O1xuICAgIH1cbiAgICBpZiAocGFydCAmJiAocGFydERpZmYgfHwgIWNvbnRpZ3VvdXMpKSB7XG4gICAgICBwYXJ0LnN0YXRzLnBhcnNpbmcuc3RhcnQgPSBub3c7XG4gICAgfVxuICAgIGNvbnN0IGluaXRTZWdtZW50Q2hhbmdlID0gIShsYXN0RnJhZyAmJiAoKF9mcmFnJGluaXRTZWdtZW50ID0gZnJhZy5pbml0U2VnbWVudCkgPT0gbnVsbCA/IHZvaWQgMCA6IF9mcmFnJGluaXRTZWdtZW50LnVybCkgPT09ICgoX2xhc3RGcmFnJGluaXRTZWdtZW50ID0gbGFzdEZyYWcuaW5pdFNlZ21lbnQpID09IG51bGwgPyB2b2lkIDAgOiBfbGFzdEZyYWckaW5pdFNlZ21lbnQudXJsKSk7XG4gICAgY29uc3Qgc3RhdGUgPSBuZXcgVHJhbnNtdXhTdGF0ZShkaXNjb250aW51aXR5LCBjb250aWd1b3VzLCBhY2N1cmF0ZVRpbWVPZmZzZXQsIHRyYWNrU3dpdGNoLCB0aW1lT2Zmc2V0LCBpbml0U2VnbWVudENoYW5nZSk7XG4gICAgaWYgKCFjb250aWd1b3VzIHx8IGRpc2NvbnRpbnVpdHkgfHwgaW5pdFNlZ21lbnRDaGFuZ2UpIHtcbiAgICAgIGxvZ2dlci5sb2coYFt0cmFuc211eGVyLWludGVyZmFjZSwgJHtmcmFnLnR5cGV9XTogU3RhcnRpbmcgbmV3IHRyYW5zbXV4IHNlc3Npb24gZm9yIHNuOiAke2NodW5rTWV0YS5zbn0gcDogJHtjaHVua01ldGEucGFydH0gbGV2ZWw6ICR7Y2h1bmtNZXRhLmxldmVsfSBpZDogJHtjaHVua01ldGEuaWR9XG4gICAgICAgIGRpc2NvbnRpbnVpdHk6ICR7ZGlzY29udGludWl0eX1cbiAgICAgICAgdHJhY2tTd2l0Y2g6ICR7dHJhY2tTd2l0Y2h9XG4gICAgICAgIGNvbnRpZ3VvdXM6ICR7Y29udGlndW91c31cbiAgICAgICAgYWNjdXJhdGVUaW1lT2Zmc2V0OiAke2FjY3VyYXRlVGltZU9mZnNldH1cbiAgICAgICAgdGltZU9mZnNldDogJHt0aW1lT2Zmc2V0fVxuICAgICAgICBpbml0U2VnbWVudENoYW5nZTogJHtpbml0U2VnbWVudENoYW5nZX1gKTtcbiAgICAgIGNvbnN0IGNvbmZpZyA9IG5ldyBUcmFuc211eENvbmZpZyhhdWRpb0NvZGVjLCB2aWRlb0NvZGVjLCBpbml0U2VnbWVudERhdGEsIGR1cmF0aW9uLCBkZWZhdWx0SW5pdFBUUyk7XG4gICAgICB0aGlzLmNvbmZpZ3VyZVRyYW5zbXV4ZXIoY29uZmlnKTtcbiAgICB9XG4gICAgdGhpcy5mcmFnID0gZnJhZztcbiAgICB0aGlzLnBhcnQgPSBwYXJ0O1xuXG4gICAgLy8gRnJhZ3Mgd2l0aCBzbiBvZiAnaW5pdFNlZ21lbnQnIGFyZSBub3QgdHJhbnNtdXhlZFxuICAgIGlmICh0aGlzLndvcmtlckNvbnRleHQpIHtcbiAgICAgIC8vIHBvc3QgZnJhZ21lbnQgcGF5bG9hZCBhcyB0cmFuc2ZlcmFibGUgb2JqZWN0cyBmb3IgQXJyYXlCdWZmZXIgKG5vIGNvcHkpXG4gICAgICB0aGlzLndvcmtlckNvbnRleHQud29ya2VyLnBvc3RNZXNzYWdlKHtcbiAgICAgICAgY21kOiAnZGVtdXgnLFxuICAgICAgICBkYXRhLFxuICAgICAgICBkZWNyeXB0ZGF0YSxcbiAgICAgICAgY2h1bmtNZXRhLFxuICAgICAgICBzdGF0ZVxuICAgICAgfSwgZGF0YSBpbnN0YW5jZW9mIEFycmF5QnVmZmVyID8gW2RhdGFdIDogW10pO1xuICAgIH0gZWxzZSBpZiAodHJhbnNtdXhlcikge1xuICAgICAgY29uc3QgdHJhbnNtdXhSZXN1bHQgPSB0cmFuc211eGVyLnB1c2goZGF0YSwgZGVjcnlwdGRhdGEsIGNodW5rTWV0YSwgc3RhdGUpO1xuICAgICAgaWYgKGlzUHJvbWlzZSh0cmFuc211eFJlc3VsdCkpIHtcbiAgICAgICAgdHJhbnNtdXhlci5hc3luYyA9IHRydWU7XG4gICAgICAgIHRyYW5zbXV4UmVzdWx0LnRoZW4oZGF0YSA9PiB7XG4gICAgICAgICAgdGhpcy5oYW5kbGVUcmFuc211eENvbXBsZXRlKGRhdGEpO1xuICAgICAgICB9KS5jYXRjaChlcnJvciA9PiB7XG4gICAgICAgICAgdGhpcy50cmFuc211eGVyRXJyb3IoZXJyb3IsIGNodW5rTWV0YSwgJ3RyYW5zbXV4ZXItaW50ZXJmYWNlIHB1c2ggZXJyb3InKTtcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0cmFuc211eGVyLmFzeW5jID0gZmFsc2U7XG4gICAgICAgIHRoaXMuaGFuZGxlVHJhbnNtdXhDb21wbGV0ZSh0cmFuc211eFJlc3VsdCk7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIGZsdXNoKGNodW5rTWV0YSkge1xuICAgIGNodW5rTWV0YS50cmFuc211eGluZy5zdGFydCA9IHNlbGYucGVyZm9ybWFuY2Uubm93KCk7XG4gICAgY29uc3Qge1xuICAgICAgdHJhbnNtdXhlclxuICAgIH0gPSB0aGlzO1xuICAgIGlmICh0aGlzLndvcmtlckNvbnRleHQpIHtcbiAgICAgIHRoaXMud29ya2VyQ29udGV4dC53b3JrZXIucG9zdE1lc3NhZ2Uoe1xuICAgICAgICBjbWQ6ICdmbHVzaCcsXG4gICAgICAgIGNodW5rTWV0YVxuICAgICAgfSk7XG4gICAgfSBlbHNlIGlmICh0cmFuc211eGVyKSB7XG4gICAgICBsZXQgdHJhbnNtdXhSZXN1bHQgPSB0cmFuc211eGVyLmZsdXNoKGNodW5rTWV0YSk7XG4gICAgICBjb25zdCBhc3luY0ZsdXNoID0gaXNQcm9taXNlKHRyYW5zbXV4UmVzdWx0KTtcbiAgICAgIGlmIChhc3luY0ZsdXNoIHx8IHRyYW5zbXV4ZXIuYXN5bmMpIHtcbiAgICAgICAgaWYgKCFpc1Byb21pc2UodHJhbnNtdXhSZXN1bHQpKSB7XG4gICAgICAgICAgdHJhbnNtdXhSZXN1bHQgPSBQcm9taXNlLnJlc29sdmUodHJhbnNtdXhSZXN1bHQpO1xuICAgICAgICB9XG4gICAgICAgIHRyYW5zbXV4UmVzdWx0LnRoZW4oZGF0YSA9PiB7XG4gICAgICAgICAgdGhpcy5oYW5kbGVGbHVzaFJlc3VsdChkYXRhLCBjaHVua01ldGEpO1xuICAgICAgICB9KS5jYXRjaChlcnJvciA9PiB7XG4gICAgICAgICAgdGhpcy50cmFuc211eGVyRXJyb3IoZXJyb3IsIGNodW5rTWV0YSwgJ3RyYW5zbXV4ZXItaW50ZXJmYWNlIGZsdXNoIGVycm9yJyk7XG4gICAgICAgIH0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5oYW5kbGVGbHVzaFJlc3VsdCh0cmFuc211eFJlc3VsdCwgY2h1bmtNZXRhKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgdHJhbnNtdXhlckVycm9yKGVycm9yLCBjaHVua01ldGEsIHJlYXNvbikge1xuICAgIGlmICghdGhpcy5obHMpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5lcnJvciA9IGVycm9yO1xuICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLkVSUk9SLCB7XG4gICAgICB0eXBlOiBFcnJvclR5cGVzLk1FRElBX0VSUk9SLFxuICAgICAgZGV0YWlsczogRXJyb3JEZXRhaWxzLkZSQUdfUEFSU0lOR19FUlJPUixcbiAgICAgIGNodW5rTWV0YSxcbiAgICAgIGZyYWc6IHRoaXMuZnJhZyB8fCB1bmRlZmluZWQsXG4gICAgICBmYXRhbDogZmFsc2UsXG4gICAgICBlcnJvcixcbiAgICAgIGVycjogZXJyb3IsXG4gICAgICByZWFzb25cbiAgICB9KTtcbiAgfVxuICBoYW5kbGVGbHVzaFJlc3VsdChyZXN1bHRzLCBjaHVua01ldGEpIHtcbiAgICByZXN1bHRzLmZvckVhY2gocmVzdWx0ID0+IHtcbiAgICAgIHRoaXMuaGFuZGxlVHJhbnNtdXhDb21wbGV0ZShyZXN1bHQpO1xuICAgIH0pO1xuICAgIHRoaXMub25GbHVzaChjaHVua01ldGEpO1xuICB9XG4gIG9uV29ya2VyTWVzc2FnZShldmVudCkge1xuICAgIGNvbnN0IGRhdGEgPSBldmVudC5kYXRhO1xuICAgIGlmICghKGRhdGEgIT0gbnVsbCAmJiBkYXRhLmV2ZW50KSkge1xuICAgICAgbG9nZ2VyLndhcm4oYHdvcmtlciBtZXNzYWdlIHJlY2VpdmVkIHdpdGggbm8gJHtkYXRhID8gJ2V2ZW50IG5hbWUnIDogJ2RhdGEnfWApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBobHMgPSB0aGlzLmhscztcbiAgICBpZiAoIXRoaXMuaGxzKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHN3aXRjaCAoZGF0YS5ldmVudCkge1xuICAgICAgY2FzZSAnaW5pdCc6XG4gICAgICAgIHtcbiAgICAgICAgICB2YXIgX3RoaXMkd29ya2VyQ29udGV4dDtcbiAgICAgICAgICBjb25zdCBvYmplY3RVUkwgPSAoX3RoaXMkd29ya2VyQ29udGV4dCA9IHRoaXMud29ya2VyQ29udGV4dCkgPT0gbnVsbCA/IHZvaWQgMCA6IF90aGlzJHdvcmtlckNvbnRleHQub2JqZWN0VVJMO1xuICAgICAgICAgIGlmIChvYmplY3RVUkwpIHtcbiAgICAgICAgICAgIC8vIHJldm9rZSB0aGUgT2JqZWN0IFVSTCB0aGF0IHdhcyB1c2VkIHRvIGNyZWF0ZSB0cmFuc211eGVyIHdvcmtlciwgc28gYXMgbm90IHRvIGxlYWsgaXRcbiAgICAgICAgICAgIHNlbGYuVVJMLnJldm9rZU9iamVjdFVSTChvYmplY3RVUkwpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgY2FzZSAndHJhbnNtdXhDb21wbGV0ZSc6XG4gICAgICAgIHtcbiAgICAgICAgICB0aGlzLmhhbmRsZVRyYW5zbXV4Q29tcGxldGUoZGF0YS5kYXRhKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgY2FzZSAnZmx1c2gnOlxuICAgICAgICB7XG4gICAgICAgICAgdGhpcy5vbkZsdXNoKGRhdGEuZGF0YSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cblxuICAgICAgLy8gcGFzcyBsb2dzIGZyb20gdGhlIHdvcmtlciB0aHJlYWQgdG8gdGhlIG1haW4gbG9nZ2VyXG4gICAgICBjYXNlICd3b3JrZXJMb2cnOlxuICAgICAgICBpZiAobG9nZ2VyW2RhdGEuZGF0YS5sb2dUeXBlXSkge1xuICAgICAgICAgIGxvZ2dlcltkYXRhLmRhdGEubG9nVHlwZV0oZGF0YS5kYXRhLm1lc3NhZ2UpO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAge1xuICAgICAgICAgIGRhdGEuZGF0YSA9IGRhdGEuZGF0YSB8fCB7fTtcbiAgICAgICAgICBkYXRhLmRhdGEuZnJhZyA9IHRoaXMuZnJhZztcbiAgICAgICAgICBkYXRhLmRhdGEuaWQgPSB0aGlzLmlkO1xuICAgICAgICAgIGhscy50cmlnZ2VyKGRhdGEuZXZlbnQsIGRhdGEuZGF0YSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICB9XG4gIH1cbiAgY29uZmlndXJlVHJhbnNtdXhlcihjb25maWcpIHtcbiAgICBjb25zdCB7XG4gICAgICB0cmFuc211eGVyXG4gICAgfSA9IHRoaXM7XG4gICAgaWYgKHRoaXMud29ya2VyQ29udGV4dCkge1xuICAgICAgdGhpcy53b3JrZXJDb250ZXh0Lndvcmtlci5wb3N0TWVzc2FnZSh7XG4gICAgICAgIGNtZDogJ2NvbmZpZ3VyZScsXG4gICAgICAgIGNvbmZpZ1xuICAgICAgfSk7XG4gICAgfSBlbHNlIGlmICh0cmFuc211eGVyKSB7XG4gICAgICB0cmFuc211eGVyLmNvbmZpZ3VyZShjb25maWcpO1xuICAgIH1cbiAgfVxuICBoYW5kbGVUcmFuc211eENvbXBsZXRlKHJlc3VsdCkge1xuICAgIHJlc3VsdC5jaHVua01ldGEudHJhbnNtdXhpbmcuZW5kID0gc2VsZi5wZXJmb3JtYW5jZS5ub3coKTtcbiAgICB0aGlzLm9uVHJhbnNtdXhDb21wbGV0ZShyZXN1bHQpO1xuICB9XG59XG5cbmZ1bmN0aW9uIHN1YnRpdGxlT3B0aW9uc0lkZW50aWNhbCh0cmFja0xpc3QxLCB0cmFja0xpc3QyKSB7XG4gIGlmICh0cmFja0xpc3QxLmxlbmd0aCAhPT0gdHJhY2tMaXN0Mi5sZW5ndGgpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCB0cmFja0xpc3QxLmxlbmd0aDsgaSsrKSB7XG4gICAgaWYgKCFtZWRpYUF0dHJpYnV0ZXNJZGVudGljYWwodHJhY2tMaXN0MVtpXS5hdHRycywgdHJhY2tMaXN0MltpXS5hdHRycykpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHRydWU7XG59XG5mdW5jdGlvbiBtZWRpYUF0dHJpYnV0ZXNJZGVudGljYWwoYXR0cnMxLCBhdHRyczIsIGN1c3RvbUF0dHJpYnV0ZXMpIHtcbiAgLy8gTWVkaWEgb3B0aW9ucyB3aXRoIHRoZSBzYW1lIHJlbmRpdGlvbiBJRCBtdXN0IGJlIGJpdCBpZGVudGljYWxcbiAgY29uc3Qgc3RhYmxlUmVuZGl0aW9uSWQgPSBhdHRyczFbJ1NUQUJMRS1SRU5ESVRJT04tSUQnXTtcbiAgaWYgKHN0YWJsZVJlbmRpdGlvbklkICYmICFjdXN0b21BdHRyaWJ1dGVzKSB7XG4gICAgcmV0dXJuIHN0YWJsZVJlbmRpdGlvbklkID09PSBhdHRyczJbJ1NUQUJMRS1SRU5ESVRJT04tSUQnXTtcbiAgfVxuICAvLyBXaGVuIHJlbmRpdGlvbiBJRCBpcyBub3QgcHJlc2VudCwgY29tcGFyZSBhdHRyaWJ1dGVzXG4gIHJldHVybiAhKGN1c3RvbUF0dHJpYnV0ZXMgfHwgWydMQU5HVUFHRScsICdOQU1FJywgJ0NIQVJBQ1RFUklTVElDUycsICdBVVRPU0VMRUNUJywgJ0RFRkFVTFQnLCAnRk9SQ0VEJywgJ0FTU09DLUxBTkdVQUdFJ10pLnNvbWUoc3VidGl0bGVBdHRyaWJ1dGUgPT4gYXR0cnMxW3N1YnRpdGxlQXR0cmlidXRlXSAhPT0gYXR0cnMyW3N1YnRpdGxlQXR0cmlidXRlXSk7XG59XG5mdW5jdGlvbiBzdWJ0aXRsZVRyYWNrTWF0Y2hlc1RleHRUcmFjayhzdWJ0aXRsZVRyYWNrLCB0ZXh0VHJhY2spIHtcbiAgcmV0dXJuIHRleHRUcmFjay5sYWJlbC50b0xvd2VyQ2FzZSgpID09PSBzdWJ0aXRsZVRyYWNrLm5hbWUudG9Mb3dlckNhc2UoKSAmJiAoIXRleHRUcmFjay5sYW5ndWFnZSB8fCB0ZXh0VHJhY2subGFuZ3VhZ2UudG9Mb3dlckNhc2UoKSA9PT0gKHN1YnRpdGxlVHJhY2subGFuZyB8fCAnJykudG9Mb3dlckNhc2UoKSk7XG59XG5cbmNvbnN0IFRJQ0tfSU5URVJWQUwkMiA9IDEwMDsgLy8gaG93IG9mdGVuIHRvIHRpY2sgaW4gbXNcblxuY2xhc3MgQXVkaW9TdHJlYW1Db250cm9sbGVyIGV4dGVuZHMgQmFzZVN0cmVhbUNvbnRyb2xsZXIge1xuICBjb25zdHJ1Y3RvcihobHMsIGZyYWdtZW50VHJhY2tlciwga2V5TG9hZGVyKSB7XG4gICAgc3VwZXIoaGxzLCBmcmFnbWVudFRyYWNrZXIsIGtleUxvYWRlciwgJ1thdWRpby1zdHJlYW0tY29udHJvbGxlcl0nLCBQbGF5bGlzdExldmVsVHlwZS5BVURJTyk7XG4gICAgdGhpcy52aWRlb0J1ZmZlciA9IG51bGw7XG4gICAgdGhpcy52aWRlb1RyYWNrQ0MgPSAtMTtcbiAgICB0aGlzLndhaXRpbmdWaWRlb0NDID0gLTE7XG4gICAgdGhpcy5idWZmZXJlZFRyYWNrID0gbnVsbDtcbiAgICB0aGlzLnN3aXRjaGluZ1RyYWNrID0gbnVsbDtcbiAgICB0aGlzLnRyYWNrSWQgPSAtMTtcbiAgICB0aGlzLndhaXRpbmdEYXRhID0gbnVsbDtcbiAgICB0aGlzLm1haW5EZXRhaWxzID0gbnVsbDtcbiAgICB0aGlzLmZsdXNoaW5nID0gZmFsc2U7XG4gICAgdGhpcy5idWZmZXJGbHVzaGVkID0gZmFsc2U7XG4gICAgdGhpcy5jYWNoZWRUcmFja0xvYWRlZERhdGEgPSBudWxsO1xuICAgIHRoaXMuX3JlZ2lzdGVyTGlzdGVuZXJzKCk7XG4gIH1cbiAgb25IYW5kbGVyRGVzdHJveWluZygpIHtcbiAgICB0aGlzLl91bnJlZ2lzdGVyTGlzdGVuZXJzKCk7XG4gICAgc3VwZXIub25IYW5kbGVyRGVzdHJveWluZygpO1xuICAgIHRoaXMubWFpbkRldGFpbHMgPSBudWxsO1xuICAgIHRoaXMuYnVmZmVyZWRUcmFjayA9IG51bGw7XG4gICAgdGhpcy5zd2l0Y2hpbmdUcmFjayA9IG51bGw7XG4gIH1cbiAgX3JlZ2lzdGVyTGlzdGVuZXJzKCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGhsc1xuICAgIH0gPSB0aGlzO1xuICAgIGhscy5vbihFdmVudHMuTUVESUFfQVRUQUNIRUQsIHRoaXMub25NZWRpYUF0dGFjaGVkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLk1FRElBX0RFVEFDSElORywgdGhpcy5vbk1lZGlhRGV0YWNoaW5nLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLk1BTklGRVNUX0xPQURJTkcsIHRoaXMub25NYW5pZmVzdExvYWRpbmcsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuTEVWRUxfTE9BREVELCB0aGlzLm9uTGV2ZWxMb2FkZWQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuQVVESU9fVFJBQ0tTX1VQREFURUQsIHRoaXMub25BdWRpb1RyYWNrc1VwZGF0ZWQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuQVVESU9fVFJBQ0tfU1dJVENISU5HLCB0aGlzLm9uQXVkaW9UcmFja1N3aXRjaGluZywgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5BVURJT19UUkFDS19MT0FERUQsIHRoaXMub25BdWRpb1RyYWNrTG9hZGVkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkVSUk9SLCB0aGlzLm9uRXJyb3IsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuQlVGRkVSX1JFU0VULCB0aGlzLm9uQnVmZmVyUmVzZXQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuQlVGRkVSX0NSRUFURUQsIHRoaXMub25CdWZmZXJDcmVhdGVkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkJVRkZFUl9GTFVTSElORywgdGhpcy5vbkJ1ZmZlckZsdXNoaW5nLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkJVRkZFUl9GTFVTSEVELCB0aGlzLm9uQnVmZmVyRmx1c2hlZCwgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5JTklUX1BUU19GT1VORCwgdGhpcy5vbkluaXRQdHNGb3VuZCwgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5GUkFHX0JVRkZFUkVELCB0aGlzLm9uRnJhZ0J1ZmZlcmVkLCB0aGlzKTtcbiAgfVxuICBfdW5yZWdpc3Rlckxpc3RlbmVycygpIHtcbiAgICBjb25zdCB7XG4gICAgICBobHNcbiAgICB9ID0gdGhpcztcbiAgICBobHMub2ZmKEV2ZW50cy5NRURJQV9BVFRBQ0hFRCwgdGhpcy5vbk1lZGlhQXR0YWNoZWQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLk1FRElBX0RFVEFDSElORywgdGhpcy5vbk1lZGlhRGV0YWNoaW5nLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5NQU5JRkVTVF9MT0FESU5HLCB0aGlzLm9uTWFuaWZlc3RMb2FkaW5nLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5MRVZFTF9MT0FERUQsIHRoaXMub25MZXZlbExvYWRlZCwgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuQVVESU9fVFJBQ0tTX1VQREFURUQsIHRoaXMub25BdWRpb1RyYWNrc1VwZGF0ZWQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkFVRElPX1RSQUNLX1NXSVRDSElORywgdGhpcy5vbkF1ZGlvVHJhY2tTd2l0Y2hpbmcsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkFVRElPX1RSQUNLX0xPQURFRCwgdGhpcy5vbkF1ZGlvVHJhY2tMb2FkZWQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkVSUk9SLCB0aGlzLm9uRXJyb3IsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkJVRkZFUl9SRVNFVCwgdGhpcy5vbkJ1ZmZlclJlc2V0LCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5CVUZGRVJfQ1JFQVRFRCwgdGhpcy5vbkJ1ZmZlckNyZWF0ZWQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkJVRkZFUl9GTFVTSElORywgdGhpcy5vbkJ1ZmZlckZsdXNoaW5nLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5CVUZGRVJfRkxVU0hFRCwgdGhpcy5vbkJ1ZmZlckZsdXNoZWQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLklOSVRfUFRTX0ZPVU5ELCB0aGlzLm9uSW5pdFB0c0ZvdW5kLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5GUkFHX0JVRkZFUkVELCB0aGlzLm9uRnJhZ0J1ZmZlcmVkLCB0aGlzKTtcbiAgfVxuXG4gIC8vIElOSVRfUFRTX0ZPVU5EIGlzIHRyaWdnZXJlZCB3aGVuIHRoZSB2aWRlbyB0cmFjayBwYXJzZWQgaW4gdGhlIHN0cmVhbS1jb250cm9sbGVyIGhhcyBhIG5ldyBQVFMgdmFsdWVcbiAgb25Jbml0UHRzRm91bmQoZXZlbnQsIHtcbiAgICBmcmFnLFxuICAgIGlkLFxuICAgIGluaXRQVFMsXG4gICAgdGltZXNjYWxlXG4gIH0pIHtcbiAgICAvLyBBbHdheXMgdXBkYXRlIHRoZSBuZXcgSU5JVCBQVFNcbiAgICAvLyBDYW4gY2hhbmdlIGR1ZSBsZXZlbCBzd2l0Y2hcbiAgICBpZiAoaWQgPT09ICdtYWluJykge1xuICAgICAgY29uc3QgY2MgPSBmcmFnLmNjO1xuICAgICAgdGhpcy5pbml0UFRTW2ZyYWcuY2NdID0ge1xuICAgICAgICBiYXNlVGltZTogaW5pdFBUUyxcbiAgICAgICAgdGltZXNjYWxlXG4gICAgICB9O1xuICAgICAgdGhpcy5sb2coYEluaXRQVFMgZm9yIGNjOiAke2NjfSBmb3VuZCBmcm9tIG1haW46ICR7aW5pdFBUU31gKTtcbiAgICAgIHRoaXMudmlkZW9UcmFja0NDID0gY2M7XG4gICAgICAvLyBJZiB3ZSBhcmUgd2FpdGluZywgdGljayBpbW1lZGlhdGVseSB0byB1bmJsb2NrIGF1ZGlvIGZyYWdtZW50IHRyYW5zbXV4aW5nXG4gICAgICBpZiAodGhpcy5zdGF0ZSA9PT0gU3RhdGUuV0FJVElOR19JTklUX1BUUykge1xuICAgICAgICB0aGlzLnRpY2soKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgc3RhcnRMb2FkKHN0YXJ0UG9zaXRpb24pIHtcbiAgICBpZiAoIXRoaXMubGV2ZWxzKSB7XG4gICAgICB0aGlzLnN0YXJ0UG9zaXRpb24gPSBzdGFydFBvc2l0aW9uO1xuICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLlNUT1BQRUQ7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGxhc3RDdXJyZW50VGltZSA9IHRoaXMubGFzdEN1cnJlbnRUaW1lO1xuICAgIHRoaXMuc3RvcExvYWQoKTtcbiAgICB0aGlzLnNldEludGVydmFsKFRJQ0tfSU5URVJWQUwkMik7XG4gICAgaWYgKGxhc3RDdXJyZW50VGltZSA+IDAgJiYgc3RhcnRQb3NpdGlvbiA9PT0gLTEpIHtcbiAgICAgIHRoaXMubG9nKGBPdmVycmlkZSBzdGFydFBvc2l0aW9uIHdpdGggbGFzdEN1cnJlbnRUaW1lIEAke2xhc3RDdXJyZW50VGltZS50b0ZpeGVkKDMpfWApO1xuICAgICAgc3RhcnRQb3NpdGlvbiA9IGxhc3RDdXJyZW50VGltZTtcbiAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmxvYWRlZG1ldGFkYXRhID0gZmFsc2U7XG4gICAgICB0aGlzLnN0YXRlID0gU3RhdGUuV0FJVElOR19UUkFDSztcbiAgICB9XG4gICAgdGhpcy5uZXh0TG9hZFBvc2l0aW9uID0gdGhpcy5zdGFydFBvc2l0aW9uID0gdGhpcy5sYXN0Q3VycmVudFRpbWUgPSBzdGFydFBvc2l0aW9uO1xuICAgIHRoaXMudGljaygpO1xuICB9XG4gIGRvVGljaygpIHtcbiAgICBzd2l0Y2ggKHRoaXMuc3RhdGUpIHtcbiAgICAgIGNhc2UgU3RhdGUuSURMRTpcbiAgICAgICAgdGhpcy5kb1RpY2tJZGxlKCk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSBTdGF0ZS5XQUlUSU5HX1RSQUNLOlxuICAgICAgICB7XG4gICAgICAgICAgdmFyIF9sZXZlbHMkdHJhY2tJZDtcbiAgICAgICAgICBjb25zdCB7XG4gICAgICAgICAgICBsZXZlbHMsXG4gICAgICAgICAgICB0cmFja0lkXG4gICAgICAgICAgfSA9IHRoaXM7XG4gICAgICAgICAgY29uc3QgZGV0YWlscyA9IGxldmVscyA9PSBudWxsID8gdm9pZCAwIDogKF9sZXZlbHMkdHJhY2tJZCA9IGxldmVsc1t0cmFja0lkXSkgPT0gbnVsbCA/IHZvaWQgMCA6IF9sZXZlbHMkdHJhY2tJZC5kZXRhaWxzO1xuICAgICAgICAgIGlmIChkZXRhaWxzKSB7XG4gICAgICAgICAgICBpZiAodGhpcy53YWl0Rm9yQ2RuVHVuZUluKGRldGFpbHMpKSB7XG4gICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLldBSVRJTkdfSU5JVF9QVFM7XG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICBjYXNlIFN0YXRlLkZSQUdfTE9BRElOR19XQUlUSU5HX1JFVFJZOlxuICAgICAgICB7XG4gICAgICAgICAgdmFyIF90aGlzJG1lZGlhO1xuICAgICAgICAgIGNvbnN0IG5vdyA9IHBlcmZvcm1hbmNlLm5vdygpO1xuICAgICAgICAgIGNvbnN0IHJldHJ5RGF0ZSA9IHRoaXMucmV0cnlEYXRlO1xuICAgICAgICAgIC8vIGlmIGN1cnJlbnQgdGltZSBpcyBndCB0aGFuIHJldHJ5RGF0ZSwgb3IgaWYgbWVkaWEgc2Vla2luZyBsZXQncyBzd2l0Y2ggdG8gSURMRSBzdGF0ZSB0byByZXRyeSBsb2FkaW5nXG4gICAgICAgICAgaWYgKCFyZXRyeURhdGUgfHwgbm93ID49IHJldHJ5RGF0ZSB8fCAoX3RoaXMkbWVkaWEgPSB0aGlzLm1lZGlhKSAhPSBudWxsICYmIF90aGlzJG1lZGlhLnNlZWtpbmcpIHtcbiAgICAgICAgICAgIGNvbnN0IHtcbiAgICAgICAgICAgICAgbGV2ZWxzLFxuICAgICAgICAgICAgICB0cmFja0lkXG4gICAgICAgICAgICB9ID0gdGhpcztcbiAgICAgICAgICAgIHRoaXMubG9nKCdSZXRyeURhdGUgcmVhY2hlZCwgc3dpdGNoIGJhY2sgdG8gSURMRSBzdGF0ZScpO1xuICAgICAgICAgICAgdGhpcy5yZXNldFN0YXJ0V2hlbk5vdExvYWRlZCgobGV2ZWxzID09IG51bGwgPyB2b2lkIDAgOiBsZXZlbHNbdHJhY2tJZF0pIHx8IG51bGwpO1xuICAgICAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICBjYXNlIFN0YXRlLldBSVRJTkdfSU5JVF9QVFM6XG4gICAgICAgIHtcbiAgICAgICAgICAvLyBFbnN1cmUgd2UgZG9uJ3QgZ2V0IHN0dWNrIGluIHRoZSBXQUlUSU5HX0lOSVRfUFRTIHN0YXRlIGlmIHRoZSB3YWl0aW5nIGZyYWcgQ0MgZG9lc24ndCBtYXRjaCBhbnkgaW5pdFBUU1xuICAgICAgICAgIGNvbnN0IHdhaXRpbmdEYXRhID0gdGhpcy53YWl0aW5nRGF0YTtcbiAgICAgICAgICBpZiAod2FpdGluZ0RhdGEpIHtcbiAgICAgICAgICAgIGNvbnN0IHtcbiAgICAgICAgICAgICAgZnJhZyxcbiAgICAgICAgICAgICAgcGFydCxcbiAgICAgICAgICAgICAgY2FjaGUsXG4gICAgICAgICAgICAgIGNvbXBsZXRlXG4gICAgICAgICAgICB9ID0gd2FpdGluZ0RhdGE7XG4gICAgICAgICAgICBpZiAodGhpcy5pbml0UFRTW2ZyYWcuY2NdICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgdGhpcy53YWl0aW5nRGF0YSA9IG51bGw7XG4gICAgICAgICAgICAgIHRoaXMud2FpdGluZ1ZpZGVvQ0MgPSAtMTtcbiAgICAgICAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLkZSQUdfTE9BRElORztcbiAgICAgICAgICAgICAgY29uc3QgcGF5bG9hZCA9IGNhY2hlLmZsdXNoKCk7XG4gICAgICAgICAgICAgIGNvbnN0IGRhdGEgPSB7XG4gICAgICAgICAgICAgICAgZnJhZyxcbiAgICAgICAgICAgICAgICBwYXJ0LFxuICAgICAgICAgICAgICAgIHBheWxvYWQsXG4gICAgICAgICAgICAgICAgbmV0d29ya0RldGFpbHM6IG51bGxcbiAgICAgICAgICAgICAgfTtcbiAgICAgICAgICAgICAgdGhpcy5faGFuZGxlRnJhZ21lbnRMb2FkUHJvZ3Jlc3MoZGF0YSk7XG4gICAgICAgICAgICAgIGlmIChjb21wbGV0ZSkge1xuICAgICAgICAgICAgICAgIHN1cGVyLl9oYW5kbGVGcmFnbWVudExvYWRDb21wbGV0ZShkYXRhKTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIGlmICh0aGlzLnZpZGVvVHJhY2tDQyAhPT0gdGhpcy53YWl0aW5nVmlkZW9DQykge1xuICAgICAgICAgICAgICAvLyBEcm9wIHdhaXRpbmcgZnJhZ21lbnQgaWYgdmlkZW9UcmFja0NDIGhhcyBjaGFuZ2VkIHNpbmNlIHdhaXRpbmdGcmFnbWVudCB3YXMgc2V0IGFuZCBpbml0UFRTIHdhcyBub3QgZm91bmRcbiAgICAgICAgICAgICAgdGhpcy5sb2coYFdhaXRpbmcgZnJhZ21lbnQgY2MgKCR7ZnJhZy5jY30pIGNhbmNlbGxlZCBiZWNhdXNlIHZpZGVvIGlzIGF0IGNjICR7dGhpcy52aWRlb1RyYWNrQ0N9YCk7XG4gICAgICAgICAgICAgIHRoaXMuY2xlYXJXYWl0aW5nRnJhZ21lbnQoKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIC8vIERyb3Agd2FpdGluZyBmcmFnbWVudCBpZiBhbiBlYXJsaWVyIGZyYWdtZW50IGlzIG5lZWRlZFxuICAgICAgICAgICAgICBjb25zdCBwb3MgPSB0aGlzLmdldExvYWRQb3NpdGlvbigpO1xuICAgICAgICAgICAgICBjb25zdCBidWZmZXJJbmZvID0gQnVmZmVySGVscGVyLmJ1ZmZlckluZm8odGhpcy5tZWRpYUJ1ZmZlciwgcG9zLCB0aGlzLmNvbmZpZy5tYXhCdWZmZXJIb2xlKTtcbiAgICAgICAgICAgICAgY29uc3Qgd2FpdGluZ0ZyYWdtZW50QXRQb3NpdGlvbiA9IGZyYWdtZW50V2l0aGluVG9sZXJhbmNlVGVzdChidWZmZXJJbmZvLmVuZCwgdGhpcy5jb25maWcubWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSwgZnJhZyk7XG4gICAgICAgICAgICAgIGlmICh3YWl0aW5nRnJhZ21lbnRBdFBvc2l0aW9uIDwgMCkge1xuICAgICAgICAgICAgICAgIHRoaXMubG9nKGBXYWl0aW5nIGZyYWdtZW50IGNjICgke2ZyYWcuY2N9KSBAICR7ZnJhZy5zdGFydH0gY2FuY2VsbGVkIGJlY2F1c2UgYW5vdGhlciBmcmFnbWVudCBhdCAke2J1ZmZlckluZm8uZW5kfSBpcyBuZWVkZWRgKTtcbiAgICAgICAgICAgICAgICB0aGlzLmNsZWFyV2FpdGluZ0ZyYWdtZW50KCk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuICAgIHRoaXMub25UaWNrRW5kKCk7XG4gIH1cbiAgY2xlYXJXYWl0aW5nRnJhZ21lbnQoKSB7XG4gICAgY29uc3Qgd2FpdGluZ0RhdGEgPSB0aGlzLndhaXRpbmdEYXRhO1xuICAgIGlmICh3YWl0aW5nRGF0YSkge1xuICAgICAgdGhpcy5mcmFnbWVudFRyYWNrZXIucmVtb3ZlRnJhZ21lbnQod2FpdGluZ0RhdGEuZnJhZyk7XG4gICAgICB0aGlzLndhaXRpbmdEYXRhID0gbnVsbDtcbiAgICAgIHRoaXMud2FpdGluZ1ZpZGVvQ0MgPSAtMTtcbiAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgIH1cbiAgfVxuICByZXNldExvYWRpbmdTdGF0ZSgpIHtcbiAgICB0aGlzLmNsZWFyV2FpdGluZ0ZyYWdtZW50KCk7XG4gICAgc3VwZXIucmVzZXRMb2FkaW5nU3RhdGUoKTtcbiAgfVxuICBvblRpY2tFbmQoKSB7XG4gICAgY29uc3Qge1xuICAgICAgbWVkaWFcbiAgICB9ID0gdGhpcztcbiAgICBpZiAoIShtZWRpYSAhPSBudWxsICYmIG1lZGlhLnJlYWR5U3RhdGUpKSB7XG4gICAgICAvLyBFeGl0IGVhcmx5IGlmIHdlIGRvbid0IGhhdmUgbWVkaWEgb3IgaWYgdGhlIG1lZGlhIGhhc24ndCBidWZmZXJlZCBhbnl0aGluZyB5ZXQgKHJlYWR5U3RhdGUgMClcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5sYXN0Q3VycmVudFRpbWUgPSBtZWRpYS5jdXJyZW50VGltZTtcbiAgfVxuICBkb1RpY2tJZGxlKCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGhscyxcbiAgICAgIGxldmVscyxcbiAgICAgIG1lZGlhLFxuICAgICAgdHJhY2tJZFxuICAgIH0gPSB0aGlzO1xuICAgIGNvbnN0IGNvbmZpZyA9IGhscy5jb25maWc7XG5cbiAgICAvLyAxLiBpZiB2aWRlbyBub3QgYXR0YWNoZWQgQU5EXG4gICAgLy8gICAgc3RhcnQgZnJhZ21lbnQgYWxyZWFkeSByZXF1ZXN0ZWQgT1Igc3RhcnQgZnJhZyBwcmVmZXRjaCBub3QgZW5hYmxlZFxuICAgIC8vIDIuIGlmIHRyYWNrcyBvciB0cmFjayBub3QgbG9hZGVkIGFuZCBzZWxlY3RlZFxuICAgIC8vIHRoZW4gZXhpdCBsb29wXG4gICAgLy8gPT4gaWYgbWVkaWEgbm90IGF0dGFjaGVkIGJ1dCBzdGFydCBmcmFnIHByZWZldGNoIGlzIGVuYWJsZWQgYW5kIHN0YXJ0IGZyYWcgbm90IHJlcXVlc3RlZCB5ZXQsIHdlIHdpbGwgbm90IGV4aXQgbG9vcFxuICAgIGlmICghbWVkaWEgJiYgKHRoaXMuc3RhcnRGcmFnUmVxdWVzdGVkIHx8ICFjb25maWcuc3RhcnRGcmFnUHJlZmV0Y2gpIHx8ICEobGV2ZWxzICE9IG51bGwgJiYgbGV2ZWxzW3RyYWNrSWRdKSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBsZXZlbEluZm8gPSBsZXZlbHNbdHJhY2tJZF07XG4gICAgY29uc3QgdHJhY2tEZXRhaWxzID0gbGV2ZWxJbmZvLmRldGFpbHM7XG4gICAgaWYgKCF0cmFja0RldGFpbHMgfHwgdHJhY2tEZXRhaWxzLmxpdmUgJiYgdGhpcy5sZXZlbExhc3RMb2FkZWQgIT09IGxldmVsSW5mbyB8fCB0aGlzLndhaXRGb3JDZG5UdW5lSW4odHJhY2tEZXRhaWxzKSkge1xuICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLldBSVRJTkdfVFJBQ0s7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGJ1ZmZlcmFibGUgPSB0aGlzLm1lZGlhQnVmZmVyID8gdGhpcy5tZWRpYUJ1ZmZlciA6IHRoaXMubWVkaWE7XG4gICAgaWYgKHRoaXMuYnVmZmVyRmx1c2hlZCAmJiBidWZmZXJhYmxlKSB7XG4gICAgICB0aGlzLmJ1ZmZlckZsdXNoZWQgPSBmYWxzZTtcbiAgICAgIHRoaXMuYWZ0ZXJCdWZmZXJGbHVzaGVkKGJ1ZmZlcmFibGUsIEVsZW1lbnRhcnlTdHJlYW1UeXBlcy5BVURJTywgUGxheWxpc3RMZXZlbFR5cGUuQVVESU8pO1xuICAgIH1cbiAgICBjb25zdCBidWZmZXJJbmZvID0gdGhpcy5nZXRGd2RCdWZmZXJJbmZvKGJ1ZmZlcmFibGUsIFBsYXlsaXN0TGV2ZWxUeXBlLkFVRElPKTtcbiAgICBpZiAoYnVmZmVySW5mbyA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB7XG4gICAgICBidWZmZXJlZFRyYWNrLFxuICAgICAgc3dpdGNoaW5nVHJhY2tcbiAgICB9ID0gdGhpcztcbiAgICBpZiAoIXN3aXRjaGluZ1RyYWNrICYmIHRoaXMuX3N0cmVhbUVuZGVkKGJ1ZmZlckluZm8sIHRyYWNrRGV0YWlscykpIHtcbiAgICAgIGhscy50cmlnZ2VyKEV2ZW50cy5CVUZGRVJfRU9TLCB7XG4gICAgICAgIHR5cGU6ICdhdWRpbydcbiAgICAgIH0pO1xuICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLkVOREVEO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBtYWluQnVmZmVySW5mbyA9IHRoaXMuZ2V0RndkQnVmZmVySW5mbyh0aGlzLnZpZGVvQnVmZmVyID8gdGhpcy52aWRlb0J1ZmZlciA6IHRoaXMubWVkaWEsIFBsYXlsaXN0TGV2ZWxUeXBlLk1BSU4pO1xuICAgIGNvbnN0IGJ1ZmZlckxlbiA9IGJ1ZmZlckluZm8ubGVuO1xuICAgIGNvbnN0IG1heEJ1ZkxlbiA9IHRoaXMuZ2V0TWF4QnVmZmVyTGVuZ3RoKG1haW5CdWZmZXJJbmZvID09IG51bGwgPyB2b2lkIDAgOiBtYWluQnVmZmVySW5mby5sZW4pO1xuICAgIGNvbnN0IGZyYWdtZW50cyA9IHRyYWNrRGV0YWlscy5mcmFnbWVudHM7XG4gICAgY29uc3Qgc3RhcnQgPSBmcmFnbWVudHNbMF0uc3RhcnQ7XG4gICAgbGV0IHRhcmdldEJ1ZmZlclRpbWUgPSB0aGlzLmZsdXNoaW5nID8gdGhpcy5nZXRMb2FkUG9zaXRpb24oKSA6IGJ1ZmZlckluZm8uZW5kO1xuICAgIGlmIChzd2l0Y2hpbmdUcmFjayAmJiBtZWRpYSkge1xuICAgICAgY29uc3QgcG9zID0gdGhpcy5nZXRMb2FkUG9zaXRpb24oKTtcbiAgICAgIC8vIFNUQUJMRVxuICAgICAgaWYgKGJ1ZmZlcmVkVHJhY2sgJiYgIW1lZGlhQXR0cmlidXRlc0lkZW50aWNhbChzd2l0Y2hpbmdUcmFjay5hdHRycywgYnVmZmVyZWRUcmFjay5hdHRycykpIHtcbiAgICAgICAgdGFyZ2V0QnVmZmVyVGltZSA9IHBvcztcbiAgICAgIH1cbiAgICAgIC8vIGlmIGN1cnJlbnRUaW1lIChwb3MpIGlzIGxlc3MgdGhhbiBhbHQgYXVkaW8gcGxheWxpc3Qgc3RhcnQgdGltZSwgaXQgbWVhbnMgdGhhdCBhbHQgYXVkaW8gaXMgYWhlYWQgb2YgY3VycmVudFRpbWVcbiAgICAgIGlmICh0cmFja0RldGFpbHMuUFRTS25vd24gJiYgcG9zIDwgc3RhcnQpIHtcbiAgICAgICAgLy8gaWYgZXZlcnl0aGluZyBpcyBidWZmZXJlZCBmcm9tIHBvcyB0byBzdGFydCBvciBpZiBhdWRpbyBidWZmZXIgdXBmcm9udCwgbGV0J3Mgc2VlayB0byBzdGFydFxuICAgICAgICBpZiAoYnVmZmVySW5mby5lbmQgPiBzdGFydCB8fCBidWZmZXJJbmZvLm5leHRTdGFydCkge1xuICAgICAgICAgIHRoaXMubG9nKCdBbHQgYXVkaW8gdHJhY2sgYWhlYWQgb2YgbWFpbiB0cmFjaywgc2VlayB0byBzdGFydCBvZiBhbHQgYXVkaW8gdHJhY2snKTtcbiAgICAgICAgICBtZWRpYS5jdXJyZW50VGltZSA9IHN0YXJ0ICsgMC4wNTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIGlmIGJ1ZmZlciBsZW5ndGggaXMgbGVzcyB0aGFuIG1heEJ1Zkxlbiwgb3IgbmVhciB0aGUgZW5kLCBmaW5kIGEgZnJhZ21lbnQgdG8gbG9hZFxuICAgIGlmIChidWZmZXJMZW4gPj0gbWF4QnVmTGVuICYmICFzd2l0Y2hpbmdUcmFjayAmJiB0YXJnZXRCdWZmZXJUaW1lIDwgZnJhZ21lbnRzW2ZyYWdtZW50cy5sZW5ndGggLSAxXS5zdGFydCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBsZXQgZnJhZyA9IHRoaXMuZ2V0TmV4dEZyYWdtZW50KHRhcmdldEJ1ZmZlclRpbWUsIHRyYWNrRGV0YWlscyk7XG4gICAgbGV0IGF0R2FwID0gZmFsc2U7XG4gICAgLy8gQXZvaWQgbG9vcCBsb2FkaW5nIGJ5IHVzaW5nIG5leHRMb2FkUG9zaXRpb24gc2V0IGZvciBiYWNrdHJhY2tpbmcgYW5kIHNraXBwaW5nIGNvbnNlY3V0aXZlIEdBUCB0YWdzXG4gICAgaWYgKGZyYWcgJiYgdGhpcy5pc0xvb3BMb2FkaW5nKGZyYWcsIHRhcmdldEJ1ZmZlclRpbWUpKSB7XG4gICAgICBhdEdhcCA9ICEhZnJhZy5nYXA7XG4gICAgICBmcmFnID0gdGhpcy5nZXROZXh0RnJhZ21lbnRMb29wTG9hZGluZyhmcmFnLCB0cmFja0RldGFpbHMsIGJ1ZmZlckluZm8sIFBsYXlsaXN0TGV2ZWxUeXBlLk1BSU4sIG1heEJ1Zkxlbik7XG4gICAgfVxuICAgIGlmICghZnJhZykge1xuICAgICAgdGhpcy5idWZmZXJGbHVzaGVkID0gdHJ1ZTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBCdWZmZXIgYXVkaW8gdXAgdG8gb25lIHRhcmdldCBkdXJhdGlvbiBhaGVhZCBvZiBtYWluIGJ1ZmZlclxuICAgIGNvbnN0IGF0QnVmZmVyU3luY0xpbWl0ID0gbWFpbkJ1ZmZlckluZm8gJiYgZnJhZy5zdGFydCA+IG1haW5CdWZmZXJJbmZvLmVuZCArIHRyYWNrRGV0YWlscy50YXJnZXRkdXJhdGlvbjtcbiAgICBpZiAoYXRCdWZmZXJTeW5jTGltaXQgfHxcbiAgICAvLyBPciB3YWl0IGZvciBtYWluIGJ1ZmZlciBhZnRlciBidWZmaW5nIHNvbWUgYXVkaW9cbiAgICAhKG1haW5CdWZmZXJJbmZvICE9IG51bGwgJiYgbWFpbkJ1ZmZlckluZm8ubGVuKSAmJiBidWZmZXJJbmZvLmxlbikge1xuICAgICAgLy8gQ2hlY2sgZnJhZ21lbnQtdHJhY2tlciBmb3IgbWFpbiBmcmFnbWVudHMgc2luY2UgR0FQIHNlZ21lbnRzIGRvIG5vdCBzaG93IHVwIGluIGJ1ZmZlckluZm9cbiAgICAgIGNvbnN0IG1haW5GcmFnID0gdGhpcy5nZXRBcHBlbmRlZEZyYWcoZnJhZy5zdGFydCwgUGxheWxpc3RMZXZlbFR5cGUuTUFJTik7XG4gICAgICBpZiAobWFpbkZyYWcgPT09IG51bGwpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgLy8gQnJpZGdlIGdhcHMgaW4gbWFpbiBidWZmZXJcbiAgICAgIGF0R2FwIHx8IChhdEdhcCA9ICEhbWFpbkZyYWcuZ2FwIHx8ICEhYXRCdWZmZXJTeW5jTGltaXQgJiYgbWFpbkJ1ZmZlckluZm8ubGVuID09PSAwKTtcbiAgICAgIGlmIChhdEJ1ZmZlclN5bmNMaW1pdCAmJiAhYXRHYXAgfHwgYXRHYXAgJiYgYnVmZmVySW5mby5uZXh0U3RhcnQgJiYgYnVmZmVySW5mby5uZXh0U3RhcnQgPCBtYWluRnJhZy5lbmQpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgIH1cbiAgICB0aGlzLmxvYWRGcmFnbWVudChmcmFnLCBsZXZlbEluZm8sIHRhcmdldEJ1ZmZlclRpbWUpO1xuICB9XG4gIGdldE1heEJ1ZmZlckxlbmd0aChtYWluQnVmZmVyTGVuZ3RoKSB7XG4gICAgY29uc3QgbWF4Q29uZmlnQnVmZmVyID0gc3VwZXIuZ2V0TWF4QnVmZmVyTGVuZ3RoKCk7XG4gICAgaWYgKCFtYWluQnVmZmVyTGVuZ3RoKSB7XG4gICAgICByZXR1cm4gbWF4Q29uZmlnQnVmZmVyO1xuICAgIH1cbiAgICByZXR1cm4gTWF0aC5taW4oTWF0aC5tYXgobWF4Q29uZmlnQnVmZmVyLCBtYWluQnVmZmVyTGVuZ3RoKSwgdGhpcy5jb25maWcubWF4TWF4QnVmZmVyTGVuZ3RoKTtcbiAgfVxuICBvbk1lZGlhRGV0YWNoaW5nKCkge1xuICAgIHRoaXMudmlkZW9CdWZmZXIgPSBudWxsO1xuICAgIHRoaXMuYnVmZmVyRmx1c2hlZCA9IHRoaXMuZmx1c2hpbmcgPSBmYWxzZTtcbiAgICBzdXBlci5vbk1lZGlhRGV0YWNoaW5nKCk7XG4gIH1cbiAgb25BdWRpb1RyYWNrc1VwZGF0ZWQoZXZlbnQsIHtcbiAgICBhdWRpb1RyYWNrc1xuICB9KSB7XG4gICAgLy8gUmVzZXQgdHJhbnhtdXhlciBpcyBlc3NlbnRpYWwgZm9yIGxhcmdlIGNvbnRleHQgc3dpdGNoZXMgKENvbnRlbnQgU3RlZXJpbmcpXG4gICAgdGhpcy5yZXNldFRyYW5zbXV4ZXIoKTtcbiAgICB0aGlzLmxldmVscyA9IGF1ZGlvVHJhY2tzLm1hcChtZWRpYVBsYXlsaXN0ID0+IG5ldyBMZXZlbChtZWRpYVBsYXlsaXN0KSk7XG4gIH1cbiAgb25BdWRpb1RyYWNrU3dpdGNoaW5nKGV2ZW50LCBkYXRhKSB7XG4gICAgLy8gaWYgYW55IFVSTCBmb3VuZCBvbiBuZXcgYXVkaW8gdHJhY2ssIGl0IGlzIGFuIGFsdGVybmF0ZSBhdWRpbyB0cmFja1xuICAgIGNvbnN0IGFsdEF1ZGlvID0gISFkYXRhLnVybDtcbiAgICB0aGlzLnRyYWNrSWQgPSBkYXRhLmlkO1xuICAgIGNvbnN0IHtcbiAgICAgIGZyYWdDdXJyZW50XG4gICAgfSA9IHRoaXM7XG4gICAgaWYgKGZyYWdDdXJyZW50KSB7XG4gICAgICBmcmFnQ3VycmVudC5hYm9ydFJlcXVlc3RzKCk7XG4gICAgICB0aGlzLnJlbW92ZVVuYnVmZmVyZWRGcmFncyhmcmFnQ3VycmVudC5zdGFydCk7XG4gICAgfVxuICAgIHRoaXMucmVzZXRMb2FkaW5nU3RhdGUoKTtcbiAgICAvLyBkZXN0cm95IHVzZWxlc3MgdHJhbnNtdXhlciB3aGVuIHN3aXRjaGluZyBhdWRpbyB0byBtYWluXG4gICAgaWYgKCFhbHRBdWRpbykge1xuICAgICAgdGhpcy5yZXNldFRyYW5zbXV4ZXIoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gc3dpdGNoaW5nIHRvIGF1ZGlvIHRyYWNrLCBzdGFydCB0aW1lciBpZiBub3QgYWxyZWFkeSBzdGFydGVkXG4gICAgICB0aGlzLnNldEludGVydmFsKFRJQ0tfSU5URVJWQUwkMik7XG4gICAgfVxuXG4gICAgLy8gc2hvdWxkIHdlIHN3aXRjaCB0cmFja3MgP1xuICAgIGlmIChhbHRBdWRpbykge1xuICAgICAgdGhpcy5zd2l0Y2hpbmdUcmFjayA9IGRhdGE7XG4gICAgICAvLyBtYWluIGF1ZGlvIHRyYWNrIGFyZSBoYW5kbGVkIGJ5IHN0cmVhbS1jb250cm9sbGVyLCBqdXN0IGRvIHNvbWV0aGluZyBpZiBzd2l0Y2hpbmcgdG8gYWx0IGF1ZGlvIHRyYWNrXG4gICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICAgIHRoaXMuZmx1c2hBdWRpb0lmTmVlZGVkKGRhdGEpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnN3aXRjaGluZ1RyYWNrID0gbnVsbDtcbiAgICAgIHRoaXMuYnVmZmVyZWRUcmFjayA9IGRhdGE7XG4gICAgICB0aGlzLnN0YXRlID0gU3RhdGUuU1RPUFBFRDtcbiAgICB9XG4gICAgdGhpcy50aWNrKCk7XG4gIH1cbiAgb25NYW5pZmVzdExvYWRpbmcoKSB7XG4gICAgdGhpcy5mcmFnbWVudFRyYWNrZXIucmVtb3ZlQWxsRnJhZ21lbnRzKCk7XG4gICAgdGhpcy5zdGFydFBvc2l0aW9uID0gdGhpcy5sYXN0Q3VycmVudFRpbWUgPSAwO1xuICAgIHRoaXMuYnVmZmVyRmx1c2hlZCA9IHRoaXMuZmx1c2hpbmcgPSBmYWxzZTtcbiAgICB0aGlzLmxldmVscyA9IHRoaXMubWFpbkRldGFpbHMgPSB0aGlzLndhaXRpbmdEYXRhID0gdGhpcy5idWZmZXJlZFRyYWNrID0gdGhpcy5jYWNoZWRUcmFja0xvYWRlZERhdGEgPSB0aGlzLnN3aXRjaGluZ1RyYWNrID0gbnVsbDtcbiAgICB0aGlzLnN0YXJ0RnJhZ1JlcXVlc3RlZCA9IGZhbHNlO1xuICAgIHRoaXMudHJhY2tJZCA9IHRoaXMudmlkZW9UcmFja0NDID0gdGhpcy53YWl0aW5nVmlkZW9DQyA9IC0xO1xuICB9XG4gIG9uTGV2ZWxMb2FkZWQoZXZlbnQsIGRhdGEpIHtcbiAgICB0aGlzLm1haW5EZXRhaWxzID0gZGF0YS5kZXRhaWxzO1xuICAgIGlmICh0aGlzLmNhY2hlZFRyYWNrTG9hZGVkRGF0YSAhPT0gbnVsbCkge1xuICAgICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuQVVESU9fVFJBQ0tfTE9BREVELCB0aGlzLmNhY2hlZFRyYWNrTG9hZGVkRGF0YSk7XG4gICAgICB0aGlzLmNhY2hlZFRyYWNrTG9hZGVkRGF0YSA9IG51bGw7XG4gICAgfVxuICB9XG4gIG9uQXVkaW9UcmFja0xvYWRlZChldmVudCwgZGF0YSkge1xuICAgIHZhciBfdHJhY2skZGV0YWlscztcbiAgICBpZiAodGhpcy5tYWluRGV0YWlscyA9PSBudWxsKSB7XG4gICAgICB0aGlzLmNhY2hlZFRyYWNrTG9hZGVkRGF0YSA9IGRhdGE7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHtcbiAgICAgIGxldmVsc1xuICAgIH0gPSB0aGlzO1xuICAgIGNvbnN0IHtcbiAgICAgIGRldGFpbHM6IG5ld0RldGFpbHMsXG4gICAgICBpZDogdHJhY2tJZFxuICAgIH0gPSBkYXRhO1xuICAgIGlmICghbGV2ZWxzKSB7XG4gICAgICB0aGlzLndhcm4oYEF1ZGlvIHRyYWNrcyB3ZXJlIHJlc2V0IHdoaWxlIGxvYWRpbmcgbGV2ZWwgJHt0cmFja0lkfWApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLmxvZyhgQXVkaW8gdHJhY2sgJHt0cmFja0lkfSBsb2FkZWQgWyR7bmV3RGV0YWlscy5zdGFydFNOfSwke25ld0RldGFpbHMuZW5kU059XSR7bmV3RGV0YWlscy5sYXN0UGFydFNuID8gYFtwYXJ0LSR7bmV3RGV0YWlscy5sYXN0UGFydFNufS0ke25ld0RldGFpbHMubGFzdFBhcnRJbmRleH1dYCA6ICcnfSxkdXJhdGlvbjoke25ld0RldGFpbHMudG90YWxkdXJhdGlvbn1gKTtcbiAgICBjb25zdCB0cmFjayA9IGxldmVsc1t0cmFja0lkXTtcbiAgICBsZXQgc2xpZGluZyA9IDA7XG4gICAgaWYgKG5ld0RldGFpbHMubGl2ZSB8fCAoX3RyYWNrJGRldGFpbHMgPSB0cmFjay5kZXRhaWxzKSAhPSBudWxsICYmIF90cmFjayRkZXRhaWxzLmxpdmUpIHtcbiAgICAgIHRoaXMuY2hlY2tMaXZlVXBkYXRlKG5ld0RldGFpbHMpO1xuICAgICAgY29uc3QgbWFpbkRldGFpbHMgPSB0aGlzLm1haW5EZXRhaWxzO1xuICAgICAgaWYgKG5ld0RldGFpbHMuZGVsdGFVcGRhdGVGYWlsZWQgfHwgIW1haW5EZXRhaWxzKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGlmICghdHJhY2suZGV0YWlscyAmJiBuZXdEZXRhaWxzLmhhc1Byb2dyYW1EYXRlVGltZSAmJiBtYWluRGV0YWlscy5oYXNQcm9ncmFtRGF0ZVRpbWUpIHtcbiAgICAgICAgLy8gTWFrZSBzdXJlIG91ciBhdWRpbyByZW5kaXRpb24gaXMgYWxpZ25lZCB3aXRoIHRoZSBcIm1haW5cIiByZW5kaXRpb24sIHVzaW5nXG4gICAgICAgIC8vIHBkdCBhcyBvdXIgcmVmZXJlbmNlIHRpbWVzLlxuICAgICAgICBhbGlnbk1lZGlhUGxheWxpc3RCeVBEVChuZXdEZXRhaWxzLCBtYWluRGV0YWlscyk7XG4gICAgICAgIHNsaWRpbmcgPSBuZXdEZXRhaWxzLmZyYWdtZW50c1swXS5zdGFydDtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHZhciBfdGhpcyRsZXZlbExhc3RMb2FkZWQ7XG4gICAgICAgIHNsaWRpbmcgPSB0aGlzLmFsaWduUGxheWxpc3RzKG5ld0RldGFpbHMsIHRyYWNrLmRldGFpbHMsIChfdGhpcyRsZXZlbExhc3RMb2FkZWQgPSB0aGlzLmxldmVsTGFzdExvYWRlZCkgPT0gbnVsbCA/IHZvaWQgMCA6IF90aGlzJGxldmVsTGFzdExvYWRlZC5kZXRhaWxzKTtcbiAgICAgIH1cbiAgICB9XG4gICAgdHJhY2suZGV0YWlscyA9IG5ld0RldGFpbHM7XG4gICAgdGhpcy5sZXZlbExhc3RMb2FkZWQgPSB0cmFjaztcblxuICAgIC8vIGNvbXB1dGUgc3RhcnQgcG9zaXRpb24gaWYgd2UgYXJlIGFsaWduZWQgd2l0aCB0aGUgbWFpbiBwbGF5bGlzdFxuICAgIGlmICghdGhpcy5zdGFydEZyYWdSZXF1ZXN0ZWQgJiYgKHRoaXMubWFpbkRldGFpbHMgfHwgIW5ld0RldGFpbHMubGl2ZSkpIHtcbiAgICAgIHRoaXMuc2V0U3RhcnRQb3NpdGlvbih0aGlzLm1haW5EZXRhaWxzIHx8IG5ld0RldGFpbHMsIHNsaWRpbmcpO1xuICAgIH1cbiAgICAvLyBvbmx5IHN3aXRjaCBiYWNrIHRvIElETEUgc3RhdGUgaWYgd2Ugd2VyZSB3YWl0aW5nIGZvciB0cmFjayB0byBzdGFydCBkb3dubG9hZGluZyBhIG5ldyBmcmFnbWVudFxuICAgIGlmICh0aGlzLnN0YXRlID09PSBTdGF0ZS5XQUlUSU5HX1RSQUNLICYmICF0aGlzLndhaXRGb3JDZG5UdW5lSW4obmV3RGV0YWlscykpIHtcbiAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgIH1cblxuICAgIC8vIHRyaWdnZXIgaGFuZGxlciByaWdodCBub3dcbiAgICB0aGlzLnRpY2soKTtcbiAgfVxuICBfaGFuZGxlRnJhZ21lbnRMb2FkUHJvZ3Jlc3MoZGF0YSkge1xuICAgIHZhciBfZnJhZyRpbml0U2VnbWVudDtcbiAgICBjb25zdCB7XG4gICAgICBmcmFnLFxuICAgICAgcGFydCxcbiAgICAgIHBheWxvYWRcbiAgICB9ID0gZGF0YTtcbiAgICBjb25zdCB7XG4gICAgICBjb25maWcsXG4gICAgICB0cmFja0lkLFxuICAgICAgbGV2ZWxzXG4gICAgfSA9IHRoaXM7XG4gICAgaWYgKCFsZXZlbHMpIHtcbiAgICAgIHRoaXMud2FybihgQXVkaW8gdHJhY2tzIHdlcmUgcmVzZXQgd2hpbGUgZnJhZ21lbnQgbG9hZCB3YXMgaW4gcHJvZ3Jlc3MuIEZyYWdtZW50ICR7ZnJhZy5zbn0gb2YgbGV2ZWwgJHtmcmFnLmxldmVsfSB3aWxsIG5vdCBiZSBidWZmZXJlZGApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB0cmFjayA9IGxldmVsc1t0cmFja0lkXTtcbiAgICBpZiAoIXRyYWNrKSB7XG4gICAgICB0aGlzLndhcm4oJ0F1ZGlvIHRyYWNrIGlzIHVuZGVmaW5lZCBvbiBmcmFnbWVudCBsb2FkIHByb2dyZXNzJyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGRldGFpbHMgPSB0cmFjay5kZXRhaWxzO1xuICAgIGlmICghZGV0YWlscykge1xuICAgICAgdGhpcy53YXJuKCdBdWRpbyB0cmFjayBkZXRhaWxzIHVuZGVmaW5lZCBvbiBmcmFnbWVudCBsb2FkIHByb2dyZXNzJyk7XG4gICAgICB0aGlzLnJlbW92ZVVuYnVmZmVyZWRGcmFncyhmcmFnLnN0YXJ0KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgYXVkaW9Db2RlYyA9IGNvbmZpZy5kZWZhdWx0QXVkaW9Db2RlYyB8fCB0cmFjay5hdWRpb0NvZGVjIHx8ICdtcDRhLjQwLjInO1xuICAgIGxldCB0cmFuc211eGVyID0gdGhpcy50cmFuc211eGVyO1xuICAgIGlmICghdHJhbnNtdXhlcikge1xuICAgICAgdHJhbnNtdXhlciA9IHRoaXMudHJhbnNtdXhlciA9IG5ldyBUcmFuc211eGVySW50ZXJmYWNlKHRoaXMuaGxzLCBQbGF5bGlzdExldmVsVHlwZS5BVURJTywgdGhpcy5faGFuZGxlVHJhbnNtdXhDb21wbGV0ZS5iaW5kKHRoaXMpLCB0aGlzLl9oYW5kbGVUcmFuc211eGVyRmx1c2guYmluZCh0aGlzKSk7XG4gICAgfVxuXG4gICAgLy8gQ2hlY2sgaWYgd2UgaGF2ZSB2aWRlbyBpbml0UFRTXG4gICAgLy8gSWYgbm90IHdlIG5lZWQgdG8gd2FpdCBmb3IgaXRcbiAgICBjb25zdCBpbml0UFRTID0gdGhpcy5pbml0UFRTW2ZyYWcuY2NdO1xuICAgIGNvbnN0IGluaXRTZWdtZW50RGF0YSA9IChfZnJhZyRpbml0U2VnbWVudCA9IGZyYWcuaW5pdFNlZ21lbnQpID09IG51bGwgPyB2b2lkIDAgOiBfZnJhZyRpbml0U2VnbWVudC5kYXRhO1xuICAgIGlmIChpbml0UFRTICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIC8vIHRoaXMubG9nKGBUcmFuc211eGluZyAke3NufSBvZiBbJHtkZXRhaWxzLnN0YXJ0U059ICwke2RldGFpbHMuZW5kU059XSx0cmFjayAke3RyYWNrSWR9YCk7XG4gICAgICAvLyB0aW1lIE9mZnNldCBpcyBhY2N1cmF0ZSBpZiBsZXZlbCBQVFMgaXMga25vd24sIG9yIGlmIHBsYXlsaXN0IGlzIG5vdCBzbGlkaW5nIChub3QgbGl2ZSlcbiAgICAgIGNvbnN0IGFjY3VyYXRlVGltZU9mZnNldCA9IGZhbHNlOyAvLyBkZXRhaWxzLlBUU0tub3duIHx8ICFkZXRhaWxzLmxpdmU7XG4gICAgICBjb25zdCBwYXJ0SW5kZXggPSBwYXJ0ID8gcGFydC5pbmRleCA6IC0xO1xuICAgICAgY29uc3QgcGFydGlhbCA9IHBhcnRJbmRleCAhPT0gLTE7XG4gICAgICBjb25zdCBjaHVua01ldGEgPSBuZXcgQ2h1bmtNZXRhZGF0YShmcmFnLmxldmVsLCBmcmFnLnNuLCBmcmFnLnN0YXRzLmNodW5rQ291bnQsIHBheWxvYWQuYnl0ZUxlbmd0aCwgcGFydEluZGV4LCBwYXJ0aWFsKTtcbiAgICAgIHRyYW5zbXV4ZXIucHVzaChwYXlsb2FkLCBpbml0U2VnbWVudERhdGEsIGF1ZGlvQ29kZWMsICcnLCBmcmFnLCBwYXJ0LCBkZXRhaWxzLnRvdGFsZHVyYXRpb24sIGFjY3VyYXRlVGltZU9mZnNldCwgY2h1bmtNZXRhLCBpbml0UFRTKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5sb2coYFVua25vd24gdmlkZW8gUFRTIGZvciBjYyAke2ZyYWcuY2N9LCB3YWl0aW5nIGZvciB2aWRlbyBQVFMgYmVmb3JlIGRlbXV4aW5nIGF1ZGlvIGZyYWcgJHtmcmFnLnNufSBvZiBbJHtkZXRhaWxzLnN0YXJ0U059ICwke2RldGFpbHMuZW5kU059XSx0cmFjayAke3RyYWNrSWR9YCk7XG4gICAgICBjb25zdCB7XG4gICAgICAgIGNhY2hlXG4gICAgICB9ID0gdGhpcy53YWl0aW5nRGF0YSA9IHRoaXMud2FpdGluZ0RhdGEgfHwge1xuICAgICAgICBmcmFnLFxuICAgICAgICBwYXJ0LFxuICAgICAgICBjYWNoZTogbmV3IENodW5rQ2FjaGUoKSxcbiAgICAgICAgY29tcGxldGU6IGZhbHNlXG4gICAgICB9O1xuICAgICAgY2FjaGUucHVzaChuZXcgVWludDhBcnJheShwYXlsb2FkKSk7XG4gICAgICB0aGlzLndhaXRpbmdWaWRlb0NDID0gdGhpcy52aWRlb1RyYWNrQ0M7XG4gICAgICB0aGlzLnN0YXRlID0gU3RhdGUuV0FJVElOR19JTklUX1BUUztcbiAgICB9XG4gIH1cbiAgX2hhbmRsZUZyYWdtZW50TG9hZENvbXBsZXRlKGZyYWdMb2FkZWREYXRhKSB7XG4gICAgaWYgKHRoaXMud2FpdGluZ0RhdGEpIHtcbiAgICAgIHRoaXMud2FpdGluZ0RhdGEuY29tcGxldGUgPSB0cnVlO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBzdXBlci5faGFuZGxlRnJhZ21lbnRMb2FkQ29tcGxldGUoZnJhZ0xvYWRlZERhdGEpO1xuICB9XG4gIG9uQnVmZmVyUmVzZXQoIC8qIGV2ZW50OiBFdmVudHMuQlVGRkVSX1JFU0VUICovXG4gICkge1xuICAgIC8vIHJlc2V0IHJlZmVyZW5jZSB0byBzb3VyY2VidWZmZXJzXG4gICAgdGhpcy5tZWRpYUJ1ZmZlciA9IHRoaXMudmlkZW9CdWZmZXIgPSBudWxsO1xuICAgIHRoaXMubG9hZGVkbWV0YWRhdGEgPSBmYWxzZTtcbiAgfVxuICBvbkJ1ZmZlckNyZWF0ZWQoZXZlbnQsIGRhdGEpIHtcbiAgICBjb25zdCBhdWRpb1RyYWNrID0gZGF0YS50cmFja3MuYXVkaW87XG4gICAgaWYgKGF1ZGlvVHJhY2spIHtcbiAgICAgIHRoaXMubWVkaWFCdWZmZXIgPSBhdWRpb1RyYWNrLmJ1ZmZlciB8fCBudWxsO1xuICAgIH1cbiAgICBpZiAoZGF0YS50cmFja3MudmlkZW8pIHtcbiAgICAgIHRoaXMudmlkZW9CdWZmZXIgPSBkYXRhLnRyYWNrcy52aWRlby5idWZmZXIgfHwgbnVsbDtcbiAgICB9XG4gIH1cbiAgb25GcmFnQnVmZmVyZWQoZXZlbnQsIGRhdGEpIHtcbiAgICBjb25zdCB7XG4gICAgICBmcmFnLFxuICAgICAgcGFydFxuICAgIH0gPSBkYXRhO1xuICAgIGlmIChmcmFnLnR5cGUgIT09IFBsYXlsaXN0TGV2ZWxUeXBlLkFVRElPKSB7XG4gICAgICBpZiAoIXRoaXMubG9hZGVkbWV0YWRhdGEgJiYgZnJhZy50eXBlID09PSBQbGF5bGlzdExldmVsVHlwZS5NQUlOKSB7XG4gICAgICAgIGNvbnN0IGJ1ZmZlcmFibGUgPSB0aGlzLnZpZGVvQnVmZmVyIHx8IHRoaXMubWVkaWE7XG4gICAgICAgIGlmIChidWZmZXJhYmxlKSB7XG4gICAgICAgICAgY29uc3QgYnVmZmVyZWRUaW1lUmFuZ2VzID0gQnVmZmVySGVscGVyLmdldEJ1ZmZlcmVkKGJ1ZmZlcmFibGUpO1xuICAgICAgICAgIGlmIChidWZmZXJlZFRpbWVSYW5nZXMubGVuZ3RoKSB7XG4gICAgICAgICAgICB0aGlzLmxvYWRlZG1ldGFkYXRhID0gdHJ1ZTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKHRoaXMuZnJhZ0NvbnRleHRDaGFuZ2VkKGZyYWcpKSB7XG4gICAgICAvLyBJZiBhIGxldmVsIHN3aXRjaCB3YXMgcmVxdWVzdGVkIHdoaWxlIGEgZnJhZ21lbnQgd2FzIGJ1ZmZlcmluZywgaXQgd2lsbCBlbWl0IHRoZSBGUkFHX0JVRkZFUkVEIGV2ZW50IHVwb24gY29tcGxldGlvblxuICAgICAgLy8gQXZvaWQgc2V0dGluZyBzdGF0ZSBiYWNrIHRvIElETEUgb3IgY29uY2x1ZGluZyB0aGUgYXVkaW8gc3dpdGNoOyBvdGhlcndpc2UsIHRoZSBzd2l0Y2hlZC10byB0cmFjayB3aWxsIG5vdCBidWZmZXJcbiAgICAgIHRoaXMud2FybihgRnJhZ21lbnQgJHtmcmFnLnNufSR7cGFydCA/ICcgcDogJyArIHBhcnQuaW5kZXggOiAnJ30gb2YgbGV2ZWwgJHtmcmFnLmxldmVsfSBmaW5pc2hlZCBidWZmZXJpbmcsIGJ1dCB3YXMgYWJvcnRlZC4gc3RhdGU6ICR7dGhpcy5zdGF0ZX0sIGF1ZGlvU3dpdGNoOiAke3RoaXMuc3dpdGNoaW5nVHJhY2sgPyB0aGlzLnN3aXRjaGluZ1RyYWNrLm5hbWUgOiAnZmFsc2UnfWApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAoZnJhZy5zbiAhPT0gJ2luaXRTZWdtZW50Jykge1xuICAgICAgdGhpcy5mcmFnUHJldmlvdXMgPSBmcmFnO1xuICAgICAgY29uc3QgdHJhY2sgPSB0aGlzLnN3aXRjaGluZ1RyYWNrO1xuICAgICAgaWYgKHRyYWNrKSB7XG4gICAgICAgIHRoaXMuYnVmZmVyZWRUcmFjayA9IHRyYWNrO1xuICAgICAgICB0aGlzLnN3aXRjaGluZ1RyYWNrID0gbnVsbDtcbiAgICAgICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuQVVESU9fVFJBQ0tfU1dJVENIRUQsIF9vYmplY3RTcHJlYWQyKHt9LCB0cmFjaykpO1xuICAgICAgfVxuICAgIH1cbiAgICB0aGlzLmZyYWdCdWZmZXJlZENvbXBsZXRlKGZyYWcsIHBhcnQpO1xuICB9XG4gIG9uRXJyb3IoZXZlbnQsIGRhdGEpIHtcbiAgICB2YXIgX2RhdGEkY29udGV4dDtcbiAgICBpZiAoZGF0YS5mYXRhbCkge1xuICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLkVSUk9SO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBzd2l0Y2ggKGRhdGEuZGV0YWlscykge1xuICAgICAgY2FzZSBFcnJvckRldGFpbHMuRlJBR19HQVA6XG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5GUkFHX1BBUlNJTkdfRVJST1I6XG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5GUkFHX0RFQ1JZUFRfRVJST1I6XG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5GUkFHX0xPQURfRVJST1I6XG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5GUkFHX0xPQURfVElNRU9VVDpcbiAgICAgIGNhc2UgRXJyb3JEZXRhaWxzLktFWV9MT0FEX0VSUk9SOlxuICAgICAgY2FzZSBFcnJvckRldGFpbHMuS0VZX0xPQURfVElNRU9VVDpcbiAgICAgICAgdGhpcy5vbkZyYWdtZW50T3JLZXlMb2FkRXJyb3IoUGxheWxpc3RMZXZlbFR5cGUuQVVESU8sIGRhdGEpO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgRXJyb3JEZXRhaWxzLkFVRElPX1RSQUNLX0xPQURfRVJST1I6XG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5BVURJT19UUkFDS19MT0FEX1RJTUVPVVQ6XG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5MRVZFTF9QQVJTSU5HX0VSUk9SOlxuICAgICAgICAvLyBpbiBjYXNlIG9mIG5vbiBmYXRhbCBlcnJvciB3aGlsZSBsb2FkaW5nIHRyYWNrLCBpZiBub3QgcmV0cnlpbmcgdG8gbG9hZCB0cmFjaywgc3dpdGNoIGJhY2sgdG8gSURMRVxuICAgICAgICBpZiAoIWRhdGEubGV2ZWxSZXRyeSAmJiB0aGlzLnN0YXRlID09PSBTdGF0ZS5XQUlUSU5HX1RSQUNLICYmICgoX2RhdGEkY29udGV4dCA9IGRhdGEuY29udGV4dCkgPT0gbnVsbCA/IHZvaWQgMCA6IF9kYXRhJGNvbnRleHQudHlwZSkgPT09IFBsYXlsaXN0Q29udGV4dFR5cGUuQVVESU9fVFJBQ0spIHtcbiAgICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICAgICAgfVxuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgRXJyb3JEZXRhaWxzLkJVRkZFUl9BUFBFTkRfRVJST1I6XG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5CVUZGRVJfRlVMTF9FUlJPUjpcbiAgICAgICAgaWYgKCFkYXRhLnBhcmVudCB8fCBkYXRhLnBhcmVudCAhPT0gJ2F1ZGlvJykge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBpZiAoZGF0YS5kZXRhaWxzID09PSBFcnJvckRldGFpbHMuQlVGRkVSX0FQUEVORF9FUlJPUikge1xuICAgICAgICAgIHRoaXMucmVzZXRMb2FkaW5nU3RhdGUoKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMucmVkdWNlTGVuZ3RoQW5kRmx1c2hCdWZmZXIoZGF0YSkpIHtcbiAgICAgICAgICB0aGlzLmJ1ZmZlcmVkVHJhY2sgPSBudWxsO1xuICAgICAgICAgIHN1cGVyLmZsdXNoTWFpbkJ1ZmZlcigwLCBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFksICdhdWRpbycpO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSBFcnJvckRldGFpbHMuSU5URVJOQUxfRVhDRVBUSU9OOlxuICAgICAgICB0aGlzLnJlY292ZXJXb3JrZXJFcnJvcihkYXRhKTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuICB9XG4gIG9uQnVmZmVyRmx1c2hpbmcoZXZlbnQsIHtcbiAgICB0eXBlXG4gIH0pIHtcbiAgICBpZiAodHlwZSAhPT0gRWxlbWVudGFyeVN0cmVhbVR5cGVzLlZJREVPKSB7XG4gICAgICB0aGlzLmZsdXNoaW5nID0gdHJ1ZTtcbiAgICB9XG4gIH1cbiAgb25CdWZmZXJGbHVzaGVkKGV2ZW50LCB7XG4gICAgdHlwZVxuICB9KSB7XG4gICAgaWYgKHR5cGUgIT09IEVsZW1lbnRhcnlTdHJlYW1UeXBlcy5WSURFTykge1xuICAgICAgdGhpcy5mbHVzaGluZyA9IGZhbHNlO1xuICAgICAgdGhpcy5idWZmZXJGbHVzaGVkID0gdHJ1ZTtcbiAgICAgIGlmICh0aGlzLnN0YXRlID09PSBTdGF0ZS5FTkRFRCkge1xuICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IG1lZGlhQnVmZmVyID0gdGhpcy5tZWRpYUJ1ZmZlciB8fCB0aGlzLm1lZGlhO1xuICAgICAgaWYgKG1lZGlhQnVmZmVyKSB7XG4gICAgICAgIHRoaXMuYWZ0ZXJCdWZmZXJGbHVzaGVkKG1lZGlhQnVmZmVyLCB0eXBlLCBQbGF5bGlzdExldmVsVHlwZS5BVURJTyk7XG4gICAgICAgIHRoaXMudGljaygpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBfaGFuZGxlVHJhbnNtdXhDb21wbGV0ZSh0cmFuc211eFJlc3VsdCkge1xuICAgIHZhciBfaWQzJHNhbXBsZXM7XG4gICAgY29uc3QgaWQgPSAnYXVkaW8nO1xuICAgIGNvbnN0IHtcbiAgICAgIGhsc1xuICAgIH0gPSB0aGlzO1xuICAgIGNvbnN0IHtcbiAgICAgIHJlbXV4UmVzdWx0LFxuICAgICAgY2h1bmtNZXRhXG4gICAgfSA9IHRyYW5zbXV4UmVzdWx0O1xuICAgIGNvbnN0IGNvbnRleHQgPSB0aGlzLmdldEN1cnJlbnRDb250ZXh0KGNodW5rTWV0YSk7XG4gICAgaWYgKCFjb250ZXh0KSB7XG4gICAgICB0aGlzLnJlc2V0V2hlbk1pc3NpbmdDb250ZXh0KGNodW5rTWV0YSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHtcbiAgICAgIGZyYWcsXG4gICAgICBwYXJ0LFxuICAgICAgbGV2ZWxcbiAgICB9ID0gY29udGV4dDtcbiAgICBjb25zdCB7XG4gICAgICBkZXRhaWxzXG4gICAgfSA9IGxldmVsO1xuICAgIGNvbnN0IHtcbiAgICAgIGF1ZGlvLFxuICAgICAgdGV4dCxcbiAgICAgIGlkMyxcbiAgICAgIGluaXRTZWdtZW50XG4gICAgfSA9IHJlbXV4UmVzdWx0O1xuXG4gICAgLy8gQ2hlY2sgaWYgdGhlIGN1cnJlbnQgZnJhZ21lbnQgaGFzIGJlZW4gYWJvcnRlZC4gV2UgY2hlY2sgdGhpcyBieSBmaXJzdCBzZWVpbmcgaWYgd2UncmUgc3RpbGwgcGxheWluZyB0aGUgY3VycmVudCBsZXZlbC5cbiAgICAvLyBJZiB3ZSBhcmUsIHN1YnNlcXVlbnRseSBjaGVjayBpZiB0aGUgY3VycmVudGx5IGxvYWRpbmcgZnJhZ21lbnQgKGZyYWdDdXJyZW50KSBoYXMgY2hhbmdlZC5cbiAgICBpZiAodGhpcy5mcmFnQ29udGV4dENoYW5nZWQoZnJhZykgfHwgIWRldGFpbHMpIHtcbiAgICAgIHRoaXMuZnJhZ21lbnRUcmFja2VyLnJlbW92ZUZyYWdtZW50KGZyYWcpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLnN0YXRlID0gU3RhdGUuUEFSU0lORztcbiAgICBpZiAodGhpcy5zd2l0Y2hpbmdUcmFjayAmJiBhdWRpbykge1xuICAgICAgdGhpcy5jb21wbGV0ZUF1ZGlvU3dpdGNoKHRoaXMuc3dpdGNoaW5nVHJhY2spO1xuICAgIH1cbiAgICBpZiAoaW5pdFNlZ21lbnQgIT0gbnVsbCAmJiBpbml0U2VnbWVudC50cmFja3MpIHtcbiAgICAgIGNvbnN0IG1hcEZyYWdtZW50ID0gZnJhZy5pbml0U2VnbWVudCB8fCBmcmFnO1xuICAgICAgdGhpcy5fYnVmZmVySW5pdFNlZ21lbnQobGV2ZWwsIGluaXRTZWdtZW50LnRyYWNrcywgbWFwRnJhZ21lbnQsIGNodW5rTWV0YSk7XG4gICAgICBobHMudHJpZ2dlcihFdmVudHMuRlJBR19QQVJTSU5HX0lOSVRfU0VHTUVOVCwge1xuICAgICAgICBmcmFnOiBtYXBGcmFnbWVudCxcbiAgICAgICAgaWQsXG4gICAgICAgIHRyYWNrczogaW5pdFNlZ21lbnQudHJhY2tzXG4gICAgICB9KTtcbiAgICAgIC8vIE9ubHkgZmx1c2ggYXVkaW8gZnJvbSBvbGQgYXVkaW8gdHJhY2tzIHdoZW4gUFRTIGlzIGtub3duIG9uIG5ldyBhdWRpbyB0cmFja1xuICAgIH1cbiAgICBpZiAoYXVkaW8pIHtcbiAgICAgIGNvbnN0IHtcbiAgICAgICAgc3RhcnRQVFMsXG4gICAgICAgIGVuZFBUUyxcbiAgICAgICAgc3RhcnREVFMsXG4gICAgICAgIGVuZERUU1xuICAgICAgfSA9IGF1ZGlvO1xuICAgICAgaWYgKHBhcnQpIHtcbiAgICAgICAgcGFydC5lbGVtZW50YXJ5U3RyZWFtc1tFbGVtZW50YXJ5U3RyZWFtVHlwZXMuQVVESU9dID0ge1xuICAgICAgICAgIHN0YXJ0UFRTLFxuICAgICAgICAgIGVuZFBUUyxcbiAgICAgICAgICBzdGFydERUUyxcbiAgICAgICAgICBlbmREVFNcbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICAgIGZyYWcuc2V0RWxlbWVudGFyeVN0cmVhbUluZm8oRWxlbWVudGFyeVN0cmVhbVR5cGVzLkFVRElPLCBzdGFydFBUUywgZW5kUFRTLCBzdGFydERUUywgZW5kRFRTKTtcbiAgICAgIHRoaXMuYnVmZmVyRnJhZ21lbnREYXRhKGF1ZGlvLCBmcmFnLCBwYXJ0LCBjaHVua01ldGEpO1xuICAgIH1cbiAgICBpZiAoaWQzICE9IG51bGwgJiYgKF9pZDMkc2FtcGxlcyA9IGlkMy5zYW1wbGVzKSAhPSBudWxsICYmIF9pZDMkc2FtcGxlcy5sZW5ndGgpIHtcbiAgICAgIGNvbnN0IGVtaXR0ZWRJRDMgPSBfZXh0ZW5kcyh7XG4gICAgICAgIGlkLFxuICAgICAgICBmcmFnLFxuICAgICAgICBkZXRhaWxzXG4gICAgICB9LCBpZDMpO1xuICAgICAgaGxzLnRyaWdnZXIoRXZlbnRzLkZSQUdfUEFSU0lOR19NRVRBREFUQSwgZW1pdHRlZElEMyk7XG4gICAgfVxuICAgIGlmICh0ZXh0KSB7XG4gICAgICBjb25zdCBlbWl0dGVkVGV4dCA9IF9leHRlbmRzKHtcbiAgICAgICAgaWQsXG4gICAgICAgIGZyYWcsXG4gICAgICAgIGRldGFpbHNcbiAgICAgIH0sIHRleHQpO1xuICAgICAgaGxzLnRyaWdnZXIoRXZlbnRzLkZSQUdfUEFSU0lOR19VU0VSREFUQSwgZW1pdHRlZFRleHQpO1xuICAgIH1cbiAgfVxuICBfYnVmZmVySW5pdFNlZ21lbnQoY3VycmVudExldmVsLCB0cmFja3MsIGZyYWcsIGNodW5rTWV0YSkge1xuICAgIGlmICh0aGlzLnN0YXRlICE9PSBTdGF0ZS5QQVJTSU5HKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIGRlbGV0ZSBhbnkgdmlkZW8gdHJhY2sgZm91bmQgb24gYXVkaW8gdHJhbnNtdXhlclxuICAgIGlmICh0cmFja3MudmlkZW8pIHtcbiAgICAgIGRlbGV0ZSB0cmFja3MudmlkZW87XG4gICAgfVxuXG4gICAgLy8gaW5jbHVkZSBsZXZlbENvZGVjIGluIGF1ZGlvIGFuZCB2aWRlbyB0cmFja3NcbiAgICBjb25zdCB0cmFjayA9IHRyYWNrcy5hdWRpbztcbiAgICBpZiAoIXRyYWNrKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRyYWNrLmlkID0gJ2F1ZGlvJztcbiAgICBjb25zdCB2YXJpYW50QXVkaW9Db2RlY3MgPSBjdXJyZW50TGV2ZWwuYXVkaW9Db2RlYztcbiAgICB0aGlzLmxvZyhgSW5pdCBhdWRpbyBidWZmZXIsIGNvbnRhaW5lcjoke3RyYWNrLmNvbnRhaW5lcn0sIGNvZGVjc1tsZXZlbC9wYXJzZWRdPVske3ZhcmlhbnRBdWRpb0NvZGVjc30vJHt0cmFjay5jb2RlY31dYCk7XG4gICAgLy8gU291cmNlQnVmZmVyIHdpbGwgdXNlIHRyYWNrLmxldmVsQ29kZWMgaWYgZGVmaW5lZFxuICAgIGlmICh2YXJpYW50QXVkaW9Db2RlY3MgJiYgdmFyaWFudEF1ZGlvQ29kZWNzLnNwbGl0KCcsJykubGVuZ3RoID09PSAxKSB7XG4gICAgICB0cmFjay5sZXZlbENvZGVjID0gdmFyaWFudEF1ZGlvQ29kZWNzO1xuICAgIH1cbiAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5CVUZGRVJfQ09ERUNTLCB0cmFja3MpO1xuICAgIGNvbnN0IGluaXRTZWdtZW50ID0gdHJhY2suaW5pdFNlZ21lbnQ7XG4gICAgaWYgKGluaXRTZWdtZW50ICE9IG51bGwgJiYgaW5pdFNlZ21lbnQuYnl0ZUxlbmd0aCkge1xuICAgICAgY29uc3Qgc2VnbWVudCA9IHtcbiAgICAgICAgdHlwZTogJ2F1ZGlvJyxcbiAgICAgICAgZnJhZyxcbiAgICAgICAgcGFydDogbnVsbCxcbiAgICAgICAgY2h1bmtNZXRhLFxuICAgICAgICBwYXJlbnQ6IGZyYWcudHlwZSxcbiAgICAgICAgZGF0YTogaW5pdFNlZ21lbnRcbiAgICAgIH07XG4gICAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5CVUZGRVJfQVBQRU5ESU5HLCBzZWdtZW50KTtcbiAgICB9XG4gICAgLy8gdHJpZ2dlciBoYW5kbGVyIHJpZ2h0IG5vd1xuICAgIHRoaXMudGlja0ltbWVkaWF0ZSgpO1xuICB9XG4gIGxvYWRGcmFnbWVudChmcmFnLCB0cmFjaywgdGFyZ2V0QnVmZmVyVGltZSkge1xuICAgIC8vIG9ubHkgbG9hZCBpZiBmcmFnbWVudCBpcyBub3QgbG9hZGVkIG9yIGlmIGluIGF1ZGlvIHN3aXRjaFxuICAgIGNvbnN0IGZyYWdTdGF0ZSA9IHRoaXMuZnJhZ21lbnRUcmFja2VyLmdldFN0YXRlKGZyYWcpO1xuICAgIHRoaXMuZnJhZ0N1cnJlbnQgPSBmcmFnO1xuXG4gICAgLy8gd2UgZm9yY2UgYSBmcmFnIGxvYWRpbmcgaW4gYXVkaW8gc3dpdGNoIGFzIGZyYWdtZW50IHRyYWNrZXIgbWlnaHQgbm90IGhhdmUgZXZpY3RlZCBwcmV2aW91cyBmcmFncyBpbiBjYXNlIG9mIHF1aWNrIGF1ZGlvIHN3aXRjaFxuICAgIGlmICh0aGlzLnN3aXRjaGluZ1RyYWNrIHx8IGZyYWdTdGF0ZSA9PT0gRnJhZ21lbnRTdGF0ZS5OT1RfTE9BREVEIHx8IGZyYWdTdGF0ZSA9PT0gRnJhZ21lbnRTdGF0ZS5QQVJUSUFMKSB7XG4gICAgICB2YXIgX3RyYWNrJGRldGFpbHMyO1xuICAgICAgaWYgKGZyYWcuc24gPT09ICdpbml0U2VnbWVudCcpIHtcbiAgICAgICAgdGhpcy5fbG9hZEluaXRTZWdtZW50KGZyYWcsIHRyYWNrKTtcbiAgICAgIH0gZWxzZSBpZiAoKF90cmFjayRkZXRhaWxzMiA9IHRyYWNrLmRldGFpbHMpICE9IG51bGwgJiYgX3RyYWNrJGRldGFpbHMyLmxpdmUgJiYgIXRoaXMuaW5pdFBUU1tmcmFnLmNjXSkge1xuICAgICAgICB0aGlzLmxvZyhgV2FpdGluZyBmb3IgdmlkZW8gUFRTIGluIGNvbnRpbnVpdHkgY291bnRlciAke2ZyYWcuY2N9IG9mIGxpdmUgc3RyZWFtIGJlZm9yZSBsb2FkaW5nIGF1ZGlvIGZyYWdtZW50ICR7ZnJhZy5zbn0gb2YgbGV2ZWwgJHt0aGlzLnRyYWNrSWR9YCk7XG4gICAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5XQUlUSU5HX0lOSVRfUFRTO1xuICAgICAgICBjb25zdCBtYWluRGV0YWlscyA9IHRoaXMubWFpbkRldGFpbHM7XG4gICAgICAgIGlmIChtYWluRGV0YWlscyAmJiBtYWluRGV0YWlscy5mcmFnbWVudHNbMF0uc3RhcnQgIT09IHRyYWNrLmRldGFpbHMuZnJhZ21lbnRzWzBdLnN0YXJ0KSB7XG4gICAgICAgICAgYWxpZ25NZWRpYVBsYXlsaXN0QnlQRFQodHJhY2suZGV0YWlscywgbWFpbkRldGFpbHMpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLnN0YXJ0RnJhZ1JlcXVlc3RlZCA9IHRydWU7XG4gICAgICAgIHN1cGVyLmxvYWRGcmFnbWVudChmcmFnLCB0cmFjaywgdGFyZ2V0QnVmZmVyVGltZSk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuY2xlYXJUcmFja2VySWZOZWVkZWQoZnJhZyk7XG4gICAgfVxuICB9XG4gIGZsdXNoQXVkaW9JZk5lZWRlZChzd2l0Y2hpbmdUcmFjaykge1xuICAgIGNvbnN0IHtcbiAgICAgIG1lZGlhLFxuICAgICAgYnVmZmVyZWRUcmFja1xuICAgIH0gPSB0aGlzO1xuICAgIGNvbnN0IGJ1ZmZlcmVkQXR0cmlidXRlcyA9IGJ1ZmZlcmVkVHJhY2sgPT0gbnVsbCA/IHZvaWQgMCA6IGJ1ZmZlcmVkVHJhY2suYXR0cnM7XG4gICAgY29uc3Qgc3dpdGNoQXR0cmlidXRlcyA9IHN3aXRjaGluZ1RyYWNrLmF0dHJzO1xuICAgIGlmIChtZWRpYSAmJiBidWZmZXJlZEF0dHJpYnV0ZXMgJiYgKGJ1ZmZlcmVkQXR0cmlidXRlcy5DSEFOTkVMUyAhPT0gc3dpdGNoQXR0cmlidXRlcy5DSEFOTkVMUyB8fCBidWZmZXJlZFRyYWNrLm5hbWUgIT09IHN3aXRjaGluZ1RyYWNrLm5hbWUgfHwgYnVmZmVyZWRUcmFjay5sYW5nICE9PSBzd2l0Y2hpbmdUcmFjay5sYW5nKSkge1xuICAgICAgdGhpcy5sb2coJ1N3aXRjaGluZyBhdWRpbyB0cmFjayA6IGZsdXNoaW5nIGFsbCBhdWRpbycpO1xuICAgICAgc3VwZXIuZmx1c2hNYWluQnVmZmVyKDAsIE51bWJlci5QT1NJVElWRV9JTkZJTklUWSwgJ2F1ZGlvJyk7XG4gICAgICB0aGlzLmJ1ZmZlcmVkVHJhY2sgPSBudWxsO1xuICAgIH1cbiAgfVxuICBjb21wbGV0ZUF1ZGlvU3dpdGNoKHN3aXRjaGluZ1RyYWNrKSB7XG4gICAgY29uc3Qge1xuICAgICAgaGxzXG4gICAgfSA9IHRoaXM7XG4gICAgdGhpcy5mbHVzaEF1ZGlvSWZOZWVkZWQoc3dpdGNoaW5nVHJhY2spO1xuICAgIHRoaXMuYnVmZmVyZWRUcmFjayA9IHN3aXRjaGluZ1RyYWNrO1xuICAgIHRoaXMuc3dpdGNoaW5nVHJhY2sgPSBudWxsO1xuICAgIGhscy50cmlnZ2VyKEV2ZW50cy5BVURJT19UUkFDS19TV0lUQ0hFRCwgX29iamVjdFNwcmVhZDIoe30sIHN3aXRjaGluZ1RyYWNrKSk7XG4gIH1cbn1cblxuY2xhc3MgQXVkaW9UcmFja0NvbnRyb2xsZXIgZXh0ZW5kcyBCYXNlUGxheWxpc3RDb250cm9sbGVyIHtcbiAgY29uc3RydWN0b3IoaGxzKSB7XG4gICAgc3VwZXIoaGxzLCAnW2F1ZGlvLXRyYWNrLWNvbnRyb2xsZXJdJyk7XG4gICAgdGhpcy50cmFja3MgPSBbXTtcbiAgICB0aGlzLmdyb3VwSWRzID0gbnVsbDtcbiAgICB0aGlzLnRyYWNrc0luR3JvdXAgPSBbXTtcbiAgICB0aGlzLnRyYWNrSWQgPSAtMTtcbiAgICB0aGlzLmN1cnJlbnRUcmFjayA9IG51bGw7XG4gICAgdGhpcy5zZWxlY3REZWZhdWx0VHJhY2sgPSB0cnVlO1xuICAgIHRoaXMucmVnaXN0ZXJMaXN0ZW5lcnMoKTtcbiAgfVxuICByZWdpc3Rlckxpc3RlbmVycygpIHtcbiAgICBjb25zdCB7XG4gICAgICBobHNcbiAgICB9ID0gdGhpcztcbiAgICBobHMub24oRXZlbnRzLk1BTklGRVNUX0xPQURJTkcsIHRoaXMub25NYW5pZmVzdExvYWRpbmcsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuTUFOSUZFU1RfUEFSU0VELCB0aGlzLm9uTWFuaWZlc3RQYXJzZWQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuTEVWRUxfTE9BRElORywgdGhpcy5vbkxldmVsTG9hZGluZywgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5MRVZFTF9TV0lUQ0hJTkcsIHRoaXMub25MZXZlbFN3aXRjaGluZywgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5BVURJT19UUkFDS19MT0FERUQsIHRoaXMub25BdWRpb1RyYWNrTG9hZGVkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkVSUk9SLCB0aGlzLm9uRXJyb3IsIHRoaXMpO1xuICB9XG4gIHVucmVnaXN0ZXJMaXN0ZW5lcnMoKSB7XG4gICAgY29uc3Qge1xuICAgICAgaGxzXG4gICAgfSA9IHRoaXM7XG4gICAgaGxzLm9mZihFdmVudHMuTUFOSUZFU1RfTE9BRElORywgdGhpcy5vbk1hbmlmZXN0TG9hZGluZywgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuTUFOSUZFU1RfUEFSU0VELCB0aGlzLm9uTWFuaWZlc3RQYXJzZWQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkxFVkVMX0xPQURJTkcsIHRoaXMub25MZXZlbExvYWRpbmcsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkxFVkVMX1NXSVRDSElORywgdGhpcy5vbkxldmVsU3dpdGNoaW5nLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5BVURJT19UUkFDS19MT0FERUQsIHRoaXMub25BdWRpb1RyYWNrTG9hZGVkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5FUlJPUiwgdGhpcy5vbkVycm9yLCB0aGlzKTtcbiAgfVxuICBkZXN0cm95KCkge1xuICAgIHRoaXMudW5yZWdpc3Rlckxpc3RlbmVycygpO1xuICAgIHRoaXMudHJhY2tzLmxlbmd0aCA9IDA7XG4gICAgdGhpcy50cmFja3NJbkdyb3VwLmxlbmd0aCA9IDA7XG4gICAgdGhpcy5jdXJyZW50VHJhY2sgPSBudWxsO1xuICAgIHN1cGVyLmRlc3Ryb3koKTtcbiAgfVxuICBvbk1hbmlmZXN0TG9hZGluZygpIHtcbiAgICB0aGlzLnRyYWNrcyA9IFtdO1xuICAgIHRoaXMudHJhY2tzSW5Hcm91cCA9IFtdO1xuICAgIHRoaXMuZ3JvdXBJZHMgPSBudWxsO1xuICAgIHRoaXMuY3VycmVudFRyYWNrID0gbnVsbDtcbiAgICB0aGlzLnRyYWNrSWQgPSAtMTtcbiAgICB0aGlzLnNlbGVjdERlZmF1bHRUcmFjayA9IHRydWU7XG4gIH1cbiAgb25NYW5pZmVzdFBhcnNlZChldmVudCwgZGF0YSkge1xuICAgIHRoaXMudHJhY2tzID0gZGF0YS5hdWRpb1RyYWNrcyB8fCBbXTtcbiAgfVxuICBvbkF1ZGlvVHJhY2tMb2FkZWQoZXZlbnQsIGRhdGEpIHtcbiAgICBjb25zdCB7XG4gICAgICBpZCxcbiAgICAgIGdyb3VwSWQsXG4gICAgICBkZXRhaWxzXG4gICAgfSA9IGRhdGE7XG4gICAgY29uc3QgdHJhY2tJbkFjdGl2ZUdyb3VwID0gdGhpcy50cmFja3NJbkdyb3VwW2lkXTtcbiAgICBpZiAoIXRyYWNrSW5BY3RpdmVHcm91cCB8fCB0cmFja0luQWN0aXZlR3JvdXAuZ3JvdXBJZCAhPT0gZ3JvdXBJZCkge1xuICAgICAgdGhpcy53YXJuKGBBdWRpbyB0cmFjayB3aXRoIGlkOiR7aWR9IGFuZCBncm91cDoke2dyb3VwSWR9IG5vdCBmb3VuZCBpbiBhY3RpdmUgZ3JvdXAgJHt0cmFja0luQWN0aXZlR3JvdXAgPT0gbnVsbCA/IHZvaWQgMCA6IHRyYWNrSW5BY3RpdmVHcm91cC5ncm91cElkfWApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBjdXJEZXRhaWxzID0gdHJhY2tJbkFjdGl2ZUdyb3VwLmRldGFpbHM7XG4gICAgdHJhY2tJbkFjdGl2ZUdyb3VwLmRldGFpbHMgPSBkYXRhLmRldGFpbHM7XG4gICAgdGhpcy5sb2coYEF1ZGlvIHRyYWNrICR7aWR9IFwiJHt0cmFja0luQWN0aXZlR3JvdXAubmFtZX1cIiBsYW5nOiR7dHJhY2tJbkFjdGl2ZUdyb3VwLmxhbmd9IGdyb3VwOiR7Z3JvdXBJZH0gbG9hZGVkIFske2RldGFpbHMuc3RhcnRTTn0tJHtkZXRhaWxzLmVuZFNOfV1gKTtcbiAgICBpZiAoaWQgPT09IHRoaXMudHJhY2tJZCkge1xuICAgICAgdGhpcy5wbGF5bGlzdExvYWRlZChpZCwgZGF0YSwgY3VyRGV0YWlscyk7XG4gICAgfVxuICB9XG4gIG9uTGV2ZWxMb2FkaW5nKGV2ZW50LCBkYXRhKSB7XG4gICAgdGhpcy5zd2l0Y2hMZXZlbChkYXRhLmxldmVsKTtcbiAgfVxuICBvbkxldmVsU3dpdGNoaW5nKGV2ZW50LCBkYXRhKSB7XG4gICAgdGhpcy5zd2l0Y2hMZXZlbChkYXRhLmxldmVsKTtcbiAgfVxuICBzd2l0Y2hMZXZlbChsZXZlbEluZGV4KSB7XG4gICAgY29uc3QgbGV2ZWxJbmZvID0gdGhpcy5obHMubGV2ZWxzW2xldmVsSW5kZXhdO1xuICAgIGlmICghbGV2ZWxJbmZvKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGF1ZGlvR3JvdXBzID0gbGV2ZWxJbmZvLmF1ZGlvR3JvdXBzIHx8IG51bGw7XG4gICAgY29uc3QgY3VycmVudEdyb3VwcyA9IHRoaXMuZ3JvdXBJZHM7XG4gICAgbGV0IGN1cnJlbnRUcmFjayA9IHRoaXMuY3VycmVudFRyYWNrO1xuICAgIGlmICghYXVkaW9Hcm91cHMgfHwgKGN1cnJlbnRHcm91cHMgPT0gbnVsbCA/IHZvaWQgMCA6IGN1cnJlbnRHcm91cHMubGVuZ3RoKSAhPT0gKGF1ZGlvR3JvdXBzID09IG51bGwgPyB2b2lkIDAgOiBhdWRpb0dyb3Vwcy5sZW5ndGgpIHx8IGF1ZGlvR3JvdXBzICE9IG51bGwgJiYgYXVkaW9Hcm91cHMuc29tZShncm91cElkID0+IChjdXJyZW50R3JvdXBzID09IG51bGwgPyB2b2lkIDAgOiBjdXJyZW50R3JvdXBzLmluZGV4T2YoZ3JvdXBJZCkpID09PSAtMSkpIHtcbiAgICAgIHRoaXMuZ3JvdXBJZHMgPSBhdWRpb0dyb3VwcztcbiAgICAgIHRoaXMudHJhY2tJZCA9IC0xO1xuICAgICAgdGhpcy5jdXJyZW50VHJhY2sgPSBudWxsO1xuICAgICAgY29uc3QgYXVkaW9UcmFja3MgPSB0aGlzLnRyYWNrcy5maWx0ZXIodHJhY2sgPT4gIWF1ZGlvR3JvdXBzIHx8IGF1ZGlvR3JvdXBzLmluZGV4T2YodHJhY2suZ3JvdXBJZCkgIT09IC0xKTtcbiAgICAgIGlmIChhdWRpb1RyYWNrcy5sZW5ndGgpIHtcbiAgICAgICAgLy8gRGlzYWJsZSBzZWxlY3REZWZhdWx0VHJhY2sgaWYgdGhlcmUgYXJlIG5vIGRlZmF1bHQgdHJhY2tzXG4gICAgICAgIGlmICh0aGlzLnNlbGVjdERlZmF1bHRUcmFjayAmJiAhYXVkaW9UcmFja3Muc29tZSh0cmFjayA9PiB0cmFjay5kZWZhdWx0KSkge1xuICAgICAgICAgIHRoaXMuc2VsZWN0RGVmYXVsdFRyYWNrID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgLy8gdHJhY2suaWQgc2hvdWxkIG1hdGNoIGhscy5hdWRpb1RyYWNrcyBpbmRleFxuICAgICAgICBhdWRpb1RyYWNrcy5mb3JFYWNoKCh0cmFjaywgaSkgPT4ge1xuICAgICAgICAgIHRyYWNrLmlkID0gaTtcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2UgaWYgKCFjdXJyZW50VHJhY2sgJiYgIXRoaXMudHJhY2tzSW5Hcm91cC5sZW5ndGgpIHtcbiAgICAgICAgLy8gRG8gbm90IGRpc3BhdGNoIEFVRElPX1RSQUNLU19VUERBVEVEIHdoZW4gdGhlcmUgd2VyZSBhbmQgYXJlIG5vIHRyYWNrc1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aGlzLnRyYWNrc0luR3JvdXAgPSBhdWRpb1RyYWNrcztcblxuICAgICAgLy8gRmluZCBwcmVmZXJyZWQgdHJhY2tcbiAgICAgIGNvbnN0IGF1ZGlvUHJlZmVyZW5jZSA9IHRoaXMuaGxzLmNvbmZpZy5hdWRpb1ByZWZlcmVuY2U7XG4gICAgICBpZiAoIWN1cnJlbnRUcmFjayAmJiBhdWRpb1ByZWZlcmVuY2UpIHtcbiAgICAgICAgY29uc3QgZ3JvdXBJbmRleCA9IGZpbmRNYXRjaGluZ09wdGlvbihhdWRpb1ByZWZlcmVuY2UsIGF1ZGlvVHJhY2tzLCBhdWRpb01hdGNoUHJlZGljYXRlKTtcbiAgICAgICAgaWYgKGdyb3VwSW5kZXggPiAtMSkge1xuICAgICAgICAgIGN1cnJlbnRUcmFjayA9IGF1ZGlvVHJhY2tzW2dyb3VwSW5kZXhdO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGNvbnN0IGFsbEluZGV4ID0gZmluZE1hdGNoaW5nT3B0aW9uKGF1ZGlvUHJlZmVyZW5jZSwgdGhpcy50cmFja3MpO1xuICAgICAgICAgIGN1cnJlbnRUcmFjayA9IHRoaXMudHJhY2tzW2FsbEluZGV4XTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBTZWxlY3QgaW5pdGlhbCB0cmFja1xuICAgICAgbGV0IHRyYWNrSWQgPSB0aGlzLmZpbmRUcmFja0lkKGN1cnJlbnRUcmFjayk7XG4gICAgICBpZiAodHJhY2tJZCA9PT0gLTEgJiYgY3VycmVudFRyYWNrKSB7XG4gICAgICAgIHRyYWNrSWQgPSB0aGlzLmZpbmRUcmFja0lkKG51bGwpO1xuICAgICAgfVxuXG4gICAgICAvLyBEaXNwYXRjaCBldmVudHMgYW5kIGxvYWQgdHJhY2sgaWYgbmVlZGVkXG4gICAgICBjb25zdCBhdWRpb1RyYWNrc1VwZGF0ZWQgPSB7XG4gICAgICAgIGF1ZGlvVHJhY2tzXG4gICAgICB9O1xuICAgICAgdGhpcy5sb2coYFVwZGF0aW5nIGF1ZGlvIHRyYWNrcywgJHthdWRpb1RyYWNrcy5sZW5ndGh9IHRyYWNrKHMpIGZvdW5kIGluIGdyb3VwKHMpOiAke2F1ZGlvR3JvdXBzID09IG51bGwgPyB2b2lkIDAgOiBhdWRpb0dyb3Vwcy5qb2luKCcsJyl9YCk7XG4gICAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5BVURJT19UUkFDS1NfVVBEQVRFRCwgYXVkaW9UcmFja3NVcGRhdGVkKTtcbiAgICAgIGNvbnN0IHNlbGVjdGVkVHJhY2tJZCA9IHRoaXMudHJhY2tJZDtcbiAgICAgIGlmICh0cmFja0lkICE9PSAtMSAmJiBzZWxlY3RlZFRyYWNrSWQgPT09IC0xKSB7XG4gICAgICAgIHRoaXMuc2V0QXVkaW9UcmFjayh0cmFja0lkKTtcbiAgICAgIH0gZWxzZSBpZiAoYXVkaW9UcmFja3MubGVuZ3RoICYmIHNlbGVjdGVkVHJhY2tJZCA9PT0gLTEpIHtcbiAgICAgICAgdmFyIF90aGlzJGdyb3VwSWRzO1xuICAgICAgICBjb25zdCBlcnJvciA9IG5ldyBFcnJvcihgTm8gYXVkaW8gdHJhY2sgc2VsZWN0ZWQgZm9yIGN1cnJlbnQgYXVkaW8gZ3JvdXAtSUQocyk6ICR7KF90aGlzJGdyb3VwSWRzID0gdGhpcy5ncm91cElkcykgPT0gbnVsbCA/IHZvaWQgMCA6IF90aGlzJGdyb3VwSWRzLmpvaW4oJywnKX0gdHJhY2sgY291bnQ6ICR7YXVkaW9UcmFja3MubGVuZ3RofWApO1xuICAgICAgICB0aGlzLndhcm4oZXJyb3IubWVzc2FnZSk7XG4gICAgICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLkVSUk9SLCB7XG4gICAgICAgICAgdHlwZTogRXJyb3JUeXBlcy5NRURJQV9FUlJPUixcbiAgICAgICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuQVVESU9fVFJBQ0tfTE9BRF9FUlJPUixcbiAgICAgICAgICBmYXRhbDogdHJ1ZSxcbiAgICAgICAgICBlcnJvclxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKHRoaXMuc2hvdWxkUmVsb2FkUGxheWxpc3QoY3VycmVudFRyYWNrKSkge1xuICAgICAgLy8gUmV0cnkgcGxheWxpc3QgbG9hZGluZyBpZiBubyBwbGF5bGlzdCBpcyBvciBoYXMgYmVlbiBsb2FkZWQgeWV0XG4gICAgICB0aGlzLnNldEF1ZGlvVHJhY2sodGhpcy50cmFja0lkKTtcbiAgICB9XG4gIH1cbiAgb25FcnJvcihldmVudCwgZGF0YSkge1xuICAgIGlmIChkYXRhLmZhdGFsIHx8ICFkYXRhLmNvbnRleHQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKGRhdGEuY29udGV4dC50eXBlID09PSBQbGF5bGlzdENvbnRleHRUeXBlLkFVRElPX1RSQUNLICYmIGRhdGEuY29udGV4dC5pZCA9PT0gdGhpcy50cmFja0lkICYmICghdGhpcy5ncm91cElkcyB8fCB0aGlzLmdyb3VwSWRzLmluZGV4T2YoZGF0YS5jb250ZXh0Lmdyb3VwSWQpICE9PSAtMSkpIHtcbiAgICAgIHRoaXMucmVxdWVzdFNjaGVkdWxlZCA9IC0xO1xuICAgICAgdGhpcy5jaGVja1JldHJ5KGRhdGEpO1xuICAgIH1cbiAgfVxuICBnZXQgYWxsQXVkaW9UcmFja3MoKSB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2tzO1xuICB9XG4gIGdldCBhdWRpb1RyYWNrcygpIHtcbiAgICByZXR1cm4gdGhpcy50cmFja3NJbkdyb3VwO1xuICB9XG4gIGdldCBhdWRpb1RyYWNrKCkge1xuICAgIHJldHVybiB0aGlzLnRyYWNrSWQ7XG4gIH1cbiAgc2V0IGF1ZGlvVHJhY2sobmV3SWQpIHtcbiAgICAvLyBJZiBhdWRpbyB0cmFjayBpcyBzZWxlY3RlZCBmcm9tIEFQSSB0aGVuIGRvbid0IGNob29zZSBmcm9tIHRoZSBtYW5pZmVzdCBkZWZhdWx0IHRyYWNrXG4gICAgdGhpcy5zZWxlY3REZWZhdWx0VHJhY2sgPSBmYWxzZTtcbiAgICB0aGlzLnNldEF1ZGlvVHJhY2sobmV3SWQpO1xuICB9XG4gIHNldEF1ZGlvT3B0aW9uKGF1ZGlvT3B0aW9uKSB7XG4gICAgY29uc3QgaGxzID0gdGhpcy5obHM7XG4gICAgaGxzLmNvbmZpZy5hdWRpb1ByZWZlcmVuY2UgPSBhdWRpb09wdGlvbjtcbiAgICBpZiAoYXVkaW9PcHRpb24pIHtcbiAgICAgIGNvbnN0IGFsbEF1ZGlvVHJhY2tzID0gdGhpcy5hbGxBdWRpb1RyYWNrcztcbiAgICAgIHRoaXMuc2VsZWN0RGVmYXVsdFRyYWNrID0gZmFsc2U7XG4gICAgICBpZiAoYWxsQXVkaW9UcmFja3MubGVuZ3RoKSB7XG4gICAgICAgIC8vIEZpcnN0IHNlZSBpZiBjdXJyZW50IG9wdGlvbiBtYXRjaGVzIChubyBzd2l0Y2ggb3ApXG4gICAgICAgIGNvbnN0IGN1cnJlbnRUcmFjayA9IHRoaXMuY3VycmVudFRyYWNrO1xuICAgICAgICBpZiAoY3VycmVudFRyYWNrICYmIG1hdGNoZXNPcHRpb24oYXVkaW9PcHRpb24sIGN1cnJlbnRUcmFjaywgYXVkaW9NYXRjaFByZWRpY2F0ZSkpIHtcbiAgICAgICAgICByZXR1cm4gY3VycmVudFRyYWNrO1xuICAgICAgICB9XG4gICAgICAgIC8vIEZpbmQgb3B0aW9uIGluIGF2YWlsYWJsZSB0cmFja3MgKHRyYWNrc0luR3JvdXApXG4gICAgICAgIGNvbnN0IGdyb3VwSW5kZXggPSBmaW5kTWF0Y2hpbmdPcHRpb24oYXVkaW9PcHRpb24sIHRoaXMudHJhY2tzSW5Hcm91cCwgYXVkaW9NYXRjaFByZWRpY2F0ZSk7XG4gICAgICAgIGlmIChncm91cEluZGV4ID4gLTEpIHtcbiAgICAgICAgICBjb25zdCB0cmFjayA9IHRoaXMudHJhY2tzSW5Hcm91cFtncm91cEluZGV4XTtcbiAgICAgICAgICB0aGlzLnNldEF1ZGlvVHJhY2soZ3JvdXBJbmRleCk7XG4gICAgICAgICAgcmV0dXJuIHRyYWNrO1xuICAgICAgICB9IGVsc2UgaWYgKGN1cnJlbnRUcmFjaykge1xuICAgICAgICAgIC8vIEZpbmQgb3B0aW9uIGluIG5lYXJlc3QgbGV2ZWwgYXVkaW8gZ3JvdXBcbiAgICAgICAgICBsZXQgc2VhcmNoSW5kZXggPSBobHMubG9hZExldmVsO1xuICAgICAgICAgIGlmIChzZWFyY2hJbmRleCA9PT0gLTEpIHtcbiAgICAgICAgICAgIHNlYXJjaEluZGV4ID0gaGxzLmZpcnN0QXV0b0xldmVsO1xuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zdCBzd2l0Y2hJbmRleCA9IGZpbmRDbG9zZXN0TGV2ZWxXaXRoQXVkaW9Hcm91cChhdWRpb09wdGlvbiwgaGxzLmxldmVscywgYWxsQXVkaW9UcmFja3MsIHNlYXJjaEluZGV4LCBhdWRpb01hdGNoUHJlZGljYXRlKTtcbiAgICAgICAgICBpZiAoc3dpdGNoSW5kZXggPT09IC0xKSB7XG4gICAgICAgICAgICAvLyBjb3VsZCBub3QgZmluZCBtYXRjaGluZyB2YXJpYW50XG4gICAgICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgICAgICB9XG4gICAgICAgICAgLy8gYW5kIHN3aXRjaCBsZXZlbCB0byBhY2hlaXZlIHRoZSBhdWRpbyBncm91cCBzd2l0Y2hcbiAgICAgICAgICBobHMubmV4dExvYWRMZXZlbCA9IHN3aXRjaEluZGV4O1xuICAgICAgICB9XG4gICAgICAgIGlmIChhdWRpb09wdGlvbi5jaGFubmVscyB8fCBhdWRpb09wdGlvbi5hdWRpb0NvZGVjKSB7XG4gICAgICAgICAgLy8gQ291bGQgbm90IGZpbmQgYSBtYXRjaCB3aXRoIGNvZGVjIC8gY2hhbm5lbHMgcHJlZGljYXRlXG4gICAgICAgICAgLy8gRmluZCBhIG1hdGNoIHdpdGhvdXQgY2hhbm5lbHMgb3IgY29kZWNcbiAgICAgICAgICBjb25zdCB3aXRob3V0Q29kZWNBbmRDaGFubmVsc01hdGNoID0gZmluZE1hdGNoaW5nT3B0aW9uKGF1ZGlvT3B0aW9uLCBhbGxBdWRpb1RyYWNrcyk7XG4gICAgICAgICAgaWYgKHdpdGhvdXRDb2RlY0FuZENoYW5uZWxzTWF0Y2ggPiAtMSkge1xuICAgICAgICAgICAgcmV0dXJuIGFsbEF1ZGlvVHJhY2tzW3dpdGhvdXRDb2RlY0FuZENoYW5uZWxzTWF0Y2hdO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBzZXRBdWRpb1RyYWNrKG5ld0lkKSB7XG4gICAgY29uc3QgdHJhY2tzID0gdGhpcy50cmFja3NJbkdyb3VwO1xuXG4gICAgLy8gY2hlY2sgaWYgbGV2ZWwgaWR4IGlzIHZhbGlkXG4gICAgaWYgKG5ld0lkIDwgMCB8fCBuZXdJZCA+PSB0cmFja3MubGVuZ3RoKSB7XG4gICAgICB0aGlzLndhcm4oYEludmFsaWQgYXVkaW8gdHJhY2sgaWQ6ICR7bmV3SWR9YCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gc3RvcHBpbmcgbGl2ZSByZWxvYWRpbmcgdGltZXIgaWYgYW55XG4gICAgdGhpcy5jbGVhclRpbWVyKCk7XG4gICAgdGhpcy5zZWxlY3REZWZhdWx0VHJhY2sgPSBmYWxzZTtcbiAgICBjb25zdCBsYXN0VHJhY2sgPSB0aGlzLmN1cnJlbnRUcmFjaztcbiAgICBjb25zdCB0cmFjayA9IHRyYWNrc1tuZXdJZF07XG4gICAgY29uc3QgdHJhY2tMb2FkZWQgPSB0cmFjay5kZXRhaWxzICYmICF0cmFjay5kZXRhaWxzLmxpdmU7XG4gICAgaWYgKG5ld0lkID09PSB0aGlzLnRyYWNrSWQgJiYgdHJhY2sgPT09IGxhc3RUcmFjayAmJiB0cmFja0xvYWRlZCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLmxvZyhgU3dpdGNoaW5nIHRvIGF1ZGlvLXRyYWNrICR7bmV3SWR9IFwiJHt0cmFjay5uYW1lfVwiIGxhbmc6JHt0cmFjay5sYW5nfSBncm91cDoke3RyYWNrLmdyb3VwSWR9IGNoYW5uZWxzOiR7dHJhY2suY2hhbm5lbHN9YCk7XG4gICAgdGhpcy50cmFja0lkID0gbmV3SWQ7XG4gICAgdGhpcy5jdXJyZW50VHJhY2sgPSB0cmFjaztcbiAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5BVURJT19UUkFDS19TV0lUQ0hJTkcsIF9vYmplY3RTcHJlYWQyKHt9LCB0cmFjaykpO1xuICAgIC8vIERvIG5vdCByZWxvYWQgdHJhY2sgdW5sZXNzIGxpdmVcbiAgICBpZiAodHJhY2tMb2FkZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgaGxzVXJsUGFyYW1ldGVycyA9IHRoaXMuc3dpdGNoUGFyYW1zKHRyYWNrLnVybCwgbGFzdFRyYWNrID09IG51bGwgPyB2b2lkIDAgOiBsYXN0VHJhY2suZGV0YWlscywgdHJhY2suZGV0YWlscyk7XG4gICAgdGhpcy5sb2FkUGxheWxpc3QoaGxzVXJsUGFyYW1ldGVycyk7XG4gIH1cbiAgZmluZFRyYWNrSWQoY3VycmVudFRyYWNrKSB7XG4gICAgY29uc3QgYXVkaW9UcmFja3MgPSB0aGlzLnRyYWNrc0luR3JvdXA7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhdWRpb1RyYWNrcy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgdHJhY2sgPSBhdWRpb1RyYWNrc1tpXTtcbiAgICAgIGlmICh0aGlzLnNlbGVjdERlZmF1bHRUcmFjayAmJiAhdHJhY2suZGVmYXVsdCkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIGlmICghY3VycmVudFRyYWNrIHx8IG1hdGNoZXNPcHRpb24oY3VycmVudFRyYWNrLCB0cmFjaywgYXVkaW9NYXRjaFByZWRpY2F0ZSkpIHtcbiAgICAgICAgcmV0dXJuIGk7XG4gICAgICB9XG4gICAgfVxuICAgIGlmIChjdXJyZW50VHJhY2spIHtcbiAgICAgIGNvbnN0IHtcbiAgICAgICAgbmFtZSxcbiAgICAgICAgbGFuZyxcbiAgICAgICAgYXNzb2NMYW5nLFxuICAgICAgICBjaGFyYWN0ZXJpc3RpY3MsXG4gICAgICAgIGF1ZGlvQ29kZWMsXG4gICAgICAgIGNoYW5uZWxzXG4gICAgICB9ID0gY3VycmVudFRyYWNrO1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhdWRpb1RyYWNrcy5sZW5ndGg7IGkrKykge1xuICAgICAgICBjb25zdCB0cmFjayA9IGF1ZGlvVHJhY2tzW2ldO1xuICAgICAgICBpZiAobWF0Y2hlc09wdGlvbih7XG4gICAgICAgICAgbmFtZSxcbiAgICAgICAgICBsYW5nLFxuICAgICAgICAgIGFzc29jTGFuZyxcbiAgICAgICAgICBjaGFyYWN0ZXJpc3RpY3MsXG4gICAgICAgICAgYXVkaW9Db2RlYyxcbiAgICAgICAgICBjaGFubmVsc1xuICAgICAgICB9LCB0cmFjaywgYXVkaW9NYXRjaFByZWRpY2F0ZSkpIHtcbiAgICAgICAgICByZXR1cm4gaTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBhdWRpb1RyYWNrcy5sZW5ndGg7IGkrKykge1xuICAgICAgICBjb25zdCB0cmFjayA9IGF1ZGlvVHJhY2tzW2ldO1xuICAgICAgICBpZiAobWVkaWFBdHRyaWJ1dGVzSWRlbnRpY2FsKGN1cnJlbnRUcmFjay5hdHRycywgdHJhY2suYXR0cnMsIFsnTEFOR1VBR0UnLCAnQVNTT0MtTEFOR1VBR0UnLCAnQ0hBUkFDVEVSSVNUSUNTJ10pKSB7XG4gICAgICAgICAgcmV0dXJuIGk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYXVkaW9UcmFja3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgY29uc3QgdHJhY2sgPSBhdWRpb1RyYWNrc1tpXTtcbiAgICAgICAgaWYgKG1lZGlhQXR0cmlidXRlc0lkZW50aWNhbChjdXJyZW50VHJhY2suYXR0cnMsIHRyYWNrLmF0dHJzLCBbJ0xBTkdVQUdFJ10pKSB7XG4gICAgICAgICAgcmV0dXJuIGk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIC0xO1xuICB9XG4gIGxvYWRQbGF5bGlzdChobHNVcmxQYXJhbWV0ZXJzKSB7XG4gICAgY29uc3QgYXVkaW9UcmFjayA9IHRoaXMuY3VycmVudFRyYWNrO1xuICAgIGlmICh0aGlzLnNob3VsZExvYWRQbGF5bGlzdChhdWRpb1RyYWNrKSAmJiBhdWRpb1RyYWNrKSB7XG4gICAgICBzdXBlci5sb2FkUGxheWxpc3QoKTtcbiAgICAgIGNvbnN0IGlkID0gYXVkaW9UcmFjay5pZDtcbiAgICAgIGNvbnN0IGdyb3VwSWQgPSBhdWRpb1RyYWNrLmdyb3VwSWQ7XG4gICAgICBsZXQgdXJsID0gYXVkaW9UcmFjay51cmw7XG4gICAgICBpZiAoaGxzVXJsUGFyYW1ldGVycykge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIHVybCA9IGhsc1VybFBhcmFtZXRlcnMuYWRkRGlyZWN0aXZlcyh1cmwpO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIHRoaXMud2FybihgQ291bGQgbm90IGNvbnN0cnVjdCBuZXcgVVJMIHdpdGggSExTIERlbGl2ZXJ5IERpcmVjdGl2ZXM6ICR7ZXJyb3J9YCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIC8vIHRyYWNrIG5vdCByZXRyaWV2ZWQgeWV0LCBvciBsaXZlIHBsYXlsaXN0IHdlIG5lZWQgdG8gKHJlKWxvYWQgaXRcbiAgICAgIHRoaXMubG9nKGBsb2FkaW5nIGF1ZGlvLXRyYWNrIHBsYXlsaXN0ICR7aWR9IFwiJHthdWRpb1RyYWNrLm5hbWV9XCIgbGFuZzoke2F1ZGlvVHJhY2subGFuZ30gZ3JvdXA6JHtncm91cElkfWApO1xuICAgICAgdGhpcy5jbGVhclRpbWVyKCk7XG4gICAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5BVURJT19UUkFDS19MT0FESU5HLCB7XG4gICAgICAgIHVybCxcbiAgICAgICAgaWQsXG4gICAgICAgIGdyb3VwSWQsXG4gICAgICAgIGRlbGl2ZXJ5RGlyZWN0aXZlczogaGxzVXJsUGFyYW1ldGVycyB8fCBudWxsXG4gICAgICB9KTtcbiAgICB9XG4gIH1cbn1cblxuY29uc3QgVElDS19JTlRFUlZBTCQxID0gNTAwOyAvLyBob3cgb2Z0ZW4gdG8gdGljayBpbiBtc1xuXG5jbGFzcyBTdWJ0aXRsZVN0cmVhbUNvbnRyb2xsZXIgZXh0ZW5kcyBCYXNlU3RyZWFtQ29udHJvbGxlciB7XG4gIGNvbnN0cnVjdG9yKGhscywgZnJhZ21lbnRUcmFja2VyLCBrZXlMb2FkZXIpIHtcbiAgICBzdXBlcihobHMsIGZyYWdtZW50VHJhY2tlciwga2V5TG9hZGVyLCAnW3N1YnRpdGxlLXN0cmVhbS1jb250cm9sbGVyXScsIFBsYXlsaXN0TGV2ZWxUeXBlLlNVQlRJVExFKTtcbiAgICB0aGlzLmN1cnJlbnRUcmFja0lkID0gLTE7XG4gICAgdGhpcy50cmFja3NCdWZmZXJlZCA9IFtdO1xuICAgIHRoaXMubWFpbkRldGFpbHMgPSBudWxsO1xuICAgIHRoaXMuX3JlZ2lzdGVyTGlzdGVuZXJzKCk7XG4gIH1cbiAgb25IYW5kbGVyRGVzdHJveWluZygpIHtcbiAgICB0aGlzLl91bnJlZ2lzdGVyTGlzdGVuZXJzKCk7XG4gICAgc3VwZXIub25IYW5kbGVyRGVzdHJveWluZygpO1xuICAgIHRoaXMubWFpbkRldGFpbHMgPSBudWxsO1xuICB9XG4gIF9yZWdpc3Rlckxpc3RlbmVycygpIHtcbiAgICBjb25zdCB7XG4gICAgICBobHNcbiAgICB9ID0gdGhpcztcbiAgICBobHMub24oRXZlbnRzLk1FRElBX0FUVEFDSEVELCB0aGlzLm9uTWVkaWFBdHRhY2hlZCwgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5NRURJQV9ERVRBQ0hJTkcsIHRoaXMub25NZWRpYURldGFjaGluZywgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5NQU5JRkVTVF9MT0FESU5HLCB0aGlzLm9uTWFuaWZlc3RMb2FkaW5nLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkxFVkVMX0xPQURFRCwgdGhpcy5vbkxldmVsTG9hZGVkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkVSUk9SLCB0aGlzLm9uRXJyb3IsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuU1VCVElUTEVfVFJBQ0tTX1VQREFURUQsIHRoaXMub25TdWJ0aXRsZVRyYWNrc1VwZGF0ZWQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuU1VCVElUTEVfVFJBQ0tfU1dJVENILCB0aGlzLm9uU3VidGl0bGVUcmFja1N3aXRjaCwgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5TVUJUSVRMRV9UUkFDS19MT0FERUQsIHRoaXMub25TdWJ0aXRsZVRyYWNrTG9hZGVkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLlNVQlRJVExFX0ZSQUdfUFJPQ0VTU0VELCB0aGlzLm9uU3VidGl0bGVGcmFnUHJvY2Vzc2VkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkJVRkZFUl9GTFVTSElORywgdGhpcy5vbkJ1ZmZlckZsdXNoaW5nLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkZSQUdfQlVGRkVSRUQsIHRoaXMub25GcmFnQnVmZmVyZWQsIHRoaXMpO1xuICB9XG4gIF91bnJlZ2lzdGVyTGlzdGVuZXJzKCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGhsc1xuICAgIH0gPSB0aGlzO1xuICAgIGhscy5vZmYoRXZlbnRzLk1FRElBX0FUVEFDSEVELCB0aGlzLm9uTWVkaWFBdHRhY2hlZCwgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuTUVESUFfREVUQUNISU5HLCB0aGlzLm9uTWVkaWFEZXRhY2hpbmcsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLk1BTklGRVNUX0xPQURJTkcsIHRoaXMub25NYW5pZmVzdExvYWRpbmcsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkxFVkVMX0xPQURFRCwgdGhpcy5vbkxldmVsTG9hZGVkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5FUlJPUiwgdGhpcy5vbkVycm9yLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5TVUJUSVRMRV9UUkFDS1NfVVBEQVRFRCwgdGhpcy5vblN1YnRpdGxlVHJhY2tzVXBkYXRlZCwgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuU1VCVElUTEVfVFJBQ0tfU1dJVENILCB0aGlzLm9uU3VidGl0bGVUcmFja1N3aXRjaCwgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuU1VCVElUTEVfVFJBQ0tfTE9BREVELCB0aGlzLm9uU3VidGl0bGVUcmFja0xvYWRlZCwgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuU1VCVElUTEVfRlJBR19QUk9DRVNTRUQsIHRoaXMub25TdWJ0aXRsZUZyYWdQcm9jZXNzZWQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkJVRkZFUl9GTFVTSElORywgdGhpcy5vbkJ1ZmZlckZsdXNoaW5nLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5GUkFHX0JVRkZFUkVELCB0aGlzLm9uRnJhZ0J1ZmZlcmVkLCB0aGlzKTtcbiAgfVxuICBzdGFydExvYWQoc3RhcnRQb3NpdGlvbikge1xuICAgIHRoaXMuc3RvcExvYWQoKTtcbiAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICB0aGlzLnNldEludGVydmFsKFRJQ0tfSU5URVJWQUwkMSk7XG4gICAgdGhpcy5uZXh0TG9hZFBvc2l0aW9uID0gdGhpcy5zdGFydFBvc2l0aW9uID0gdGhpcy5sYXN0Q3VycmVudFRpbWUgPSBzdGFydFBvc2l0aW9uO1xuICAgIHRoaXMudGljaygpO1xuICB9XG4gIG9uTWFuaWZlc3RMb2FkaW5nKCkge1xuICAgIHRoaXMubWFpbkRldGFpbHMgPSBudWxsO1xuICAgIHRoaXMuZnJhZ21lbnRUcmFja2VyLnJlbW92ZUFsbEZyYWdtZW50cygpO1xuICB9XG4gIG9uTWVkaWFEZXRhY2hpbmcoKSB7XG4gICAgdGhpcy50cmFja3NCdWZmZXJlZCA9IFtdO1xuICAgIHN1cGVyLm9uTWVkaWFEZXRhY2hpbmcoKTtcbiAgfVxuICBvbkxldmVsTG9hZGVkKGV2ZW50LCBkYXRhKSB7XG4gICAgdGhpcy5tYWluRGV0YWlscyA9IGRhdGEuZGV0YWlscztcbiAgfVxuICBvblN1YnRpdGxlRnJhZ1Byb2Nlc3NlZChldmVudCwgZGF0YSkge1xuICAgIGNvbnN0IHtcbiAgICAgIGZyYWcsXG4gICAgICBzdWNjZXNzXG4gICAgfSA9IGRhdGE7XG4gICAgdGhpcy5mcmFnUHJldmlvdXMgPSBmcmFnO1xuICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgIGlmICghc3VjY2Vzcykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBidWZmZXJlZCA9IHRoaXMudHJhY2tzQnVmZmVyZWRbdGhpcy5jdXJyZW50VHJhY2tJZF07XG4gICAgaWYgKCFidWZmZXJlZCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIENyZWF0ZS91cGRhdGUgYSBidWZmZXJlZCBhcnJheSBtYXRjaGluZyB0aGUgaW50ZXJmYWNlIHVzZWQgYnkgQnVmZmVySGVscGVyLmJ1ZmZlcmVkSW5mb1xuICAgIC8vIHNvIHdlIGNhbiByZS11c2UgdGhlIGxvZ2ljIHVzZWQgdG8gZGV0ZWN0IGhvdyBtdWNoIGhhcyBiZWVuIGJ1ZmZlcmVkXG4gICAgbGV0IHRpbWVSYW5nZTtcbiAgICBjb25zdCBmcmFnU3RhcnQgPSBmcmFnLnN0YXJ0O1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgYnVmZmVyZWQubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmIChmcmFnU3RhcnQgPj0gYnVmZmVyZWRbaV0uc3RhcnQgJiYgZnJhZ1N0YXJ0IDw9IGJ1ZmZlcmVkW2ldLmVuZCkge1xuICAgICAgICB0aW1lUmFuZ2UgPSBidWZmZXJlZFtpXTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuICAgIGNvbnN0IGZyYWdFbmQgPSBmcmFnLnN0YXJ0ICsgZnJhZy5kdXJhdGlvbjtcbiAgICBpZiAodGltZVJhbmdlKSB7XG4gICAgICB0aW1lUmFuZ2UuZW5kID0gZnJhZ0VuZDtcbiAgICB9IGVsc2Uge1xuICAgICAgdGltZVJhbmdlID0ge1xuICAgICAgICBzdGFydDogZnJhZ1N0YXJ0LFxuICAgICAgICBlbmQ6IGZyYWdFbmRcbiAgICAgIH07XG4gICAgICBidWZmZXJlZC5wdXNoKHRpbWVSYW5nZSk7XG4gICAgfVxuICAgIHRoaXMuZnJhZ21lbnRUcmFja2VyLmZyYWdCdWZmZXJlZChmcmFnKTtcbiAgICB0aGlzLmZyYWdCdWZmZXJlZENvbXBsZXRlKGZyYWcsIG51bGwpO1xuICB9XG4gIG9uQnVmZmVyRmx1c2hpbmcoZXZlbnQsIGRhdGEpIHtcbiAgICBjb25zdCB7XG4gICAgICBzdGFydE9mZnNldCxcbiAgICAgIGVuZE9mZnNldFxuICAgIH0gPSBkYXRhO1xuICAgIGlmIChzdGFydE9mZnNldCA9PT0gMCAmJiBlbmRPZmZzZXQgIT09IE51bWJlci5QT1NJVElWRV9JTkZJTklUWSkge1xuICAgICAgY29uc3QgZW5kT2Zmc2V0U3VidGl0bGVzID0gZW5kT2Zmc2V0IC0gMTtcbiAgICAgIGlmIChlbmRPZmZzZXRTdWJ0aXRsZXMgPD0gMCkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBkYXRhLmVuZE9mZnNldFN1YnRpdGxlcyA9IE1hdGgubWF4KDAsIGVuZE9mZnNldFN1YnRpdGxlcyk7XG4gICAgICB0aGlzLnRyYWNrc0J1ZmZlcmVkLmZvckVhY2goYnVmZmVyZWQgPT4ge1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGJ1ZmZlcmVkLmxlbmd0aDspIHtcbiAgICAgICAgICBpZiAoYnVmZmVyZWRbaV0uZW5kIDw9IGVuZE9mZnNldFN1YnRpdGxlcykge1xuICAgICAgICAgICAgYnVmZmVyZWQuc2hpZnQoKTtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgIH0gZWxzZSBpZiAoYnVmZmVyZWRbaV0uc3RhcnQgPCBlbmRPZmZzZXRTdWJ0aXRsZXMpIHtcbiAgICAgICAgICAgIGJ1ZmZlcmVkW2ldLnN0YXJ0ID0gZW5kT2Zmc2V0U3VidGl0bGVzO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICB9XG4gICAgICAgICAgaSsrO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIHRoaXMuZnJhZ21lbnRUcmFja2VyLnJlbW92ZUZyYWdtZW50c0luUmFuZ2Uoc3RhcnRPZmZzZXQsIGVuZE9mZnNldFN1YnRpdGxlcywgUGxheWxpc3RMZXZlbFR5cGUuU1VCVElUTEUpO1xuICAgIH1cbiAgfVxuICBvbkZyYWdCdWZmZXJlZChldmVudCwgZGF0YSkge1xuICAgIGlmICghdGhpcy5sb2FkZWRtZXRhZGF0YSAmJiBkYXRhLmZyYWcudHlwZSA9PT0gUGxheWxpc3RMZXZlbFR5cGUuTUFJTikge1xuICAgICAgdmFyIF90aGlzJG1lZGlhO1xuICAgICAgaWYgKChfdGhpcyRtZWRpYSA9IHRoaXMubWVkaWEpICE9IG51bGwgJiYgX3RoaXMkbWVkaWEuYnVmZmVyZWQubGVuZ3RoKSB7XG4gICAgICAgIHRoaXMubG9hZGVkbWV0YWRhdGEgPSB0cnVlO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8vIElmIHNvbWV0aGluZyBnb2VzIHdyb25nLCBwcm9jZWVkIHRvIG5leHQgZnJhZywgaWYgd2Ugd2VyZSBwcm9jZXNzaW5nIG9uZS5cbiAgb25FcnJvcihldmVudCwgZGF0YSkge1xuICAgIGNvbnN0IGZyYWcgPSBkYXRhLmZyYWc7XG4gICAgaWYgKChmcmFnID09IG51bGwgPyB2b2lkIDAgOiBmcmFnLnR5cGUpID09PSBQbGF5bGlzdExldmVsVHlwZS5TVUJUSVRMRSkge1xuICAgICAgaWYgKGRhdGEuZGV0YWlscyA9PT0gRXJyb3JEZXRhaWxzLkZSQUdfR0FQKSB7XG4gICAgICAgIHRoaXMuZnJhZ21lbnRUcmFja2VyLmZyYWdCdWZmZXJlZChmcmFnLCB0cnVlKTtcbiAgICAgIH1cbiAgICAgIGlmICh0aGlzLmZyYWdDdXJyZW50KSB7XG4gICAgICAgIHRoaXMuZnJhZ0N1cnJlbnQuYWJvcnRSZXF1ZXN0cygpO1xuICAgICAgfVxuICAgICAgaWYgKHRoaXMuc3RhdGUgIT09IFN0YXRlLlNUT1BQRUQpIHtcbiAgICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLy8gR290IGFsbCBuZXcgc3VidGl0bGUgbGV2ZWxzLlxuICBvblN1YnRpdGxlVHJhY2tzVXBkYXRlZChldmVudCwge1xuICAgIHN1YnRpdGxlVHJhY2tzXG4gIH0pIHtcbiAgICBpZiAodGhpcy5sZXZlbHMgJiYgc3VidGl0bGVPcHRpb25zSWRlbnRpY2FsKHRoaXMubGV2ZWxzLCBzdWJ0aXRsZVRyYWNrcykpIHtcbiAgICAgIHRoaXMubGV2ZWxzID0gc3VidGl0bGVUcmFja3MubWFwKG1lZGlhUGxheWxpc3QgPT4gbmV3IExldmVsKG1lZGlhUGxheWxpc3QpKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy50cmFja3NCdWZmZXJlZCA9IFtdO1xuICAgIHRoaXMubGV2ZWxzID0gc3VidGl0bGVUcmFja3MubWFwKG1lZGlhUGxheWxpc3QgPT4ge1xuICAgICAgY29uc3QgbGV2ZWwgPSBuZXcgTGV2ZWwobWVkaWFQbGF5bGlzdCk7XG4gICAgICB0aGlzLnRyYWNrc0J1ZmZlcmVkW2xldmVsLmlkXSA9IFtdO1xuICAgICAgcmV0dXJuIGxldmVsO1xuICAgIH0pO1xuICAgIHRoaXMuZnJhZ21lbnRUcmFja2VyLnJlbW92ZUZyYWdtZW50c0luUmFuZ2UoMCwgTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZLCBQbGF5bGlzdExldmVsVHlwZS5TVUJUSVRMRSk7XG4gICAgdGhpcy5mcmFnUHJldmlvdXMgPSBudWxsO1xuICAgIHRoaXMubWVkaWFCdWZmZXIgPSBudWxsO1xuICB9XG4gIG9uU3VidGl0bGVUcmFja1N3aXRjaChldmVudCwgZGF0YSkge1xuICAgIHZhciBfdGhpcyRsZXZlbHM7XG4gICAgdGhpcy5jdXJyZW50VHJhY2tJZCA9IGRhdGEuaWQ7XG4gICAgaWYgKCEoKF90aGlzJGxldmVscyA9IHRoaXMubGV2ZWxzKSAhPSBudWxsICYmIF90aGlzJGxldmVscy5sZW5ndGgpIHx8IHRoaXMuY3VycmVudFRyYWNrSWQgPT09IC0xKSB7XG4gICAgICB0aGlzLmNsZWFySW50ZXJ2YWwoKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBDaGVjayBpZiB0cmFjayBoYXMgdGhlIG5lY2Vzc2FyeSBkZXRhaWxzIHRvIGxvYWQgZnJhZ21lbnRzXG4gICAgY29uc3QgY3VycmVudFRyYWNrID0gdGhpcy5sZXZlbHNbdGhpcy5jdXJyZW50VHJhY2tJZF07XG4gICAgaWYgKGN1cnJlbnRUcmFjayAhPSBudWxsICYmIGN1cnJlbnRUcmFjay5kZXRhaWxzKSB7XG4gICAgICB0aGlzLm1lZGlhQnVmZmVyID0gdGhpcy5tZWRpYUJ1ZmZlclRpbWVSYW5nZXM7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMubWVkaWFCdWZmZXIgPSBudWxsO1xuICAgIH1cbiAgICBpZiAoY3VycmVudFRyYWNrKSB7XG4gICAgICB0aGlzLnNldEludGVydmFsKFRJQ0tfSU5URVJWQUwkMSk7XG4gICAgfVxuICB9XG5cbiAgLy8gR290IGEgbmV3IHNldCBvZiBzdWJ0aXRsZSBmcmFnbWVudHMuXG4gIG9uU3VidGl0bGVUcmFja0xvYWRlZChldmVudCwgZGF0YSkge1xuICAgIHZhciBfdHJhY2skZGV0YWlscztcbiAgICBjb25zdCB7XG4gICAgICBjdXJyZW50VHJhY2tJZCxcbiAgICAgIGxldmVsc1xuICAgIH0gPSB0aGlzO1xuICAgIGNvbnN0IHtcbiAgICAgIGRldGFpbHM6IG5ld0RldGFpbHMsXG4gICAgICBpZDogdHJhY2tJZFxuICAgIH0gPSBkYXRhO1xuICAgIGlmICghbGV2ZWxzKSB7XG4gICAgICB0aGlzLndhcm4oYFN1YnRpdGxlIHRyYWNrcyB3ZXJlIHJlc2V0IHdoaWxlIGxvYWRpbmcgbGV2ZWwgJHt0cmFja0lkfWApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB0cmFjayA9IGxldmVsc1t0cmFja0lkXTtcbiAgICBpZiAodHJhY2tJZCA+PSBsZXZlbHMubGVuZ3RoIHx8ICF0cmFjaykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLmxvZyhgU3VidGl0bGUgdHJhY2sgJHt0cmFja0lkfSBsb2FkZWQgWyR7bmV3RGV0YWlscy5zdGFydFNOfSwke25ld0RldGFpbHMuZW5kU059XSR7bmV3RGV0YWlscy5sYXN0UGFydFNuID8gYFtwYXJ0LSR7bmV3RGV0YWlscy5sYXN0UGFydFNufS0ke25ld0RldGFpbHMubGFzdFBhcnRJbmRleH1dYCA6ICcnfSxkdXJhdGlvbjoke25ld0RldGFpbHMudG90YWxkdXJhdGlvbn1gKTtcbiAgICB0aGlzLm1lZGlhQnVmZmVyID0gdGhpcy5tZWRpYUJ1ZmZlclRpbWVSYW5nZXM7XG4gICAgbGV0IHNsaWRpbmcgPSAwO1xuICAgIGlmIChuZXdEZXRhaWxzLmxpdmUgfHwgKF90cmFjayRkZXRhaWxzID0gdHJhY2suZGV0YWlscykgIT0gbnVsbCAmJiBfdHJhY2skZGV0YWlscy5saXZlKSB7XG4gICAgICBjb25zdCBtYWluRGV0YWlscyA9IHRoaXMubWFpbkRldGFpbHM7XG4gICAgICBpZiAobmV3RGV0YWlscy5kZWx0YVVwZGF0ZUZhaWxlZCB8fCAhbWFpbkRldGFpbHMpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY29uc3QgbWFpblNsaWRpbmdTdGFydEZyYWdtZW50ID0gbWFpbkRldGFpbHMuZnJhZ21lbnRzWzBdO1xuICAgICAgaWYgKCF0cmFjay5kZXRhaWxzKSB7XG4gICAgICAgIGlmIChuZXdEZXRhaWxzLmhhc1Byb2dyYW1EYXRlVGltZSAmJiBtYWluRGV0YWlscy5oYXNQcm9ncmFtRGF0ZVRpbWUpIHtcbiAgICAgICAgICBhbGlnbk1lZGlhUGxheWxpc3RCeVBEVChuZXdEZXRhaWxzLCBtYWluRGV0YWlscyk7XG4gICAgICAgICAgc2xpZGluZyA9IG5ld0RldGFpbHMuZnJhZ21lbnRzWzBdLnN0YXJ0O1xuICAgICAgICB9IGVsc2UgaWYgKG1haW5TbGlkaW5nU3RhcnRGcmFnbWVudCkge1xuICAgICAgICAgIC8vIGxpbmUgdXAgbGl2ZSBwbGF5bGlzdCB3aXRoIG1haW4gc28gdGhhdCBmcmFnbWVudHMgaW4gcmFuZ2UgYXJlIGxvYWRlZFxuICAgICAgICAgIHNsaWRpbmcgPSBtYWluU2xpZGluZ1N0YXJ0RnJhZ21lbnQuc3RhcnQ7XG4gICAgICAgICAgYWRkU2xpZGluZyhuZXdEZXRhaWxzLCBzbGlkaW5nKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdmFyIF90aGlzJGxldmVsTGFzdExvYWRlZDtcbiAgICAgICAgc2xpZGluZyA9IHRoaXMuYWxpZ25QbGF5bGlzdHMobmV3RGV0YWlscywgdHJhY2suZGV0YWlscywgKF90aGlzJGxldmVsTGFzdExvYWRlZCA9IHRoaXMubGV2ZWxMYXN0TG9hZGVkKSA9PSBudWxsID8gdm9pZCAwIDogX3RoaXMkbGV2ZWxMYXN0TG9hZGVkLmRldGFpbHMpO1xuICAgICAgICBpZiAoc2xpZGluZyA9PT0gMCAmJiBtYWluU2xpZGluZ1N0YXJ0RnJhZ21lbnQpIHtcbiAgICAgICAgICAvLyByZWFsaWduIHdpdGggbWFpbiB3aGVuIHRoZXJlIGlzIG5vIG92ZXJsYXAgd2l0aCBsYXN0IHJlZnJlc2hcbiAgICAgICAgICBzbGlkaW5nID0gbWFpblNsaWRpbmdTdGFydEZyYWdtZW50LnN0YXJ0O1xuICAgICAgICAgIGFkZFNsaWRpbmcobmV3RGV0YWlscywgc2xpZGluZyk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgdHJhY2suZGV0YWlscyA9IG5ld0RldGFpbHM7XG4gICAgdGhpcy5sZXZlbExhc3RMb2FkZWQgPSB0cmFjaztcbiAgICBpZiAodHJhY2tJZCAhPT0gY3VycmVudFRyYWNrSWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKCF0aGlzLnN0YXJ0RnJhZ1JlcXVlc3RlZCAmJiAodGhpcy5tYWluRGV0YWlscyB8fCAhbmV3RGV0YWlscy5saXZlKSkge1xuICAgICAgdGhpcy5zZXRTdGFydFBvc2l0aW9uKHRoaXMubWFpbkRldGFpbHMgfHwgbmV3RGV0YWlscywgc2xpZGluZyk7XG4gICAgfVxuXG4gICAgLy8gdHJpZ2dlciBoYW5kbGVyIHJpZ2h0IG5vd1xuICAgIHRoaXMudGljaygpO1xuXG4gICAgLy8gSWYgcGxheWxpc3QgaXMgbWlzYWxpZ25lZCBiZWNhdXNlIG9mIGJhZCBQRFQgb3IgZHJpZnQsIGRlbGV0ZSBkZXRhaWxzIHRvIHJlc3luYyB3aXRoIG1haW4gb24gcmVsb2FkXG4gICAgaWYgKG5ld0RldGFpbHMubGl2ZSAmJiAhdGhpcy5mcmFnQ3VycmVudCAmJiB0aGlzLm1lZGlhICYmIHRoaXMuc3RhdGUgPT09IFN0YXRlLklETEUpIHtcbiAgICAgIGNvbnN0IGZvdW5kRnJhZyA9IGZpbmRGcmFnbWVudEJ5UFRTKG51bGwsIG5ld0RldGFpbHMuZnJhZ21lbnRzLCB0aGlzLm1lZGlhLmN1cnJlbnRUaW1lLCAwKTtcbiAgICAgIGlmICghZm91bmRGcmFnKSB7XG4gICAgICAgIHRoaXMud2FybignU3VidGl0bGUgcGxheWxpc3Qgbm90IGFsaWduZWQgd2l0aCBwbGF5YmFjaycpO1xuICAgICAgICB0cmFjay5kZXRhaWxzID0gdW5kZWZpbmVkO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBfaGFuZGxlRnJhZ21lbnRMb2FkQ29tcGxldGUoZnJhZ0xvYWRlZERhdGEpIHtcbiAgICBjb25zdCB7XG4gICAgICBmcmFnLFxuICAgICAgcGF5bG9hZFxuICAgIH0gPSBmcmFnTG9hZGVkRGF0YTtcbiAgICBjb25zdCBkZWNyeXB0RGF0YSA9IGZyYWcuZGVjcnlwdGRhdGE7XG4gICAgY29uc3QgaGxzID0gdGhpcy5obHM7XG4gICAgaWYgKHRoaXMuZnJhZ0NvbnRleHRDaGFuZ2VkKGZyYWcpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIGNoZWNrIHRvIHNlZSBpZiB0aGUgcGF5bG9hZCBuZWVkcyB0byBiZSBkZWNyeXB0ZWRcbiAgICBpZiAocGF5bG9hZCAmJiBwYXlsb2FkLmJ5dGVMZW5ndGggPiAwICYmIGRlY3J5cHREYXRhICE9IG51bGwgJiYgZGVjcnlwdERhdGEua2V5ICYmIGRlY3J5cHREYXRhLml2ICYmIGRlY3J5cHREYXRhLm1ldGhvZCA9PT0gJ0FFUy0xMjgnKSB7XG4gICAgICBjb25zdCBzdGFydFRpbWUgPSBwZXJmb3JtYW5jZS5ub3coKTtcbiAgICAgIC8vIGRlY3J5cHQgdGhlIHN1YnRpdGxlc1xuICAgICAgdGhpcy5kZWNyeXB0ZXIuZGVjcnlwdChuZXcgVWludDhBcnJheShwYXlsb2FkKSwgZGVjcnlwdERhdGEua2V5LmJ1ZmZlciwgZGVjcnlwdERhdGEuaXYuYnVmZmVyKS5jYXRjaChlcnIgPT4ge1xuICAgICAgICBobHMudHJpZ2dlcihFdmVudHMuRVJST1IsIHtcbiAgICAgICAgICB0eXBlOiBFcnJvclR5cGVzLk1FRElBX0VSUk9SLFxuICAgICAgICAgIGRldGFpbHM6IEVycm9yRGV0YWlscy5GUkFHX0RFQ1JZUFRfRVJST1IsXG4gICAgICAgICAgZmF0YWw6IGZhbHNlLFxuICAgICAgICAgIGVycm9yOiBlcnIsXG4gICAgICAgICAgcmVhc29uOiBlcnIubWVzc2FnZSxcbiAgICAgICAgICBmcmFnXG4gICAgICAgIH0pO1xuICAgICAgICB0aHJvdyBlcnI7XG4gICAgICB9KS50aGVuKGRlY3J5cHRlZERhdGEgPT4ge1xuICAgICAgICBjb25zdCBlbmRUaW1lID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gICAgICAgIGhscy50cmlnZ2VyKEV2ZW50cy5GUkFHX0RFQ1JZUFRFRCwge1xuICAgICAgICAgIGZyYWcsXG4gICAgICAgICAgcGF5bG9hZDogZGVjcnlwdGVkRGF0YSxcbiAgICAgICAgICBzdGF0czoge1xuICAgICAgICAgICAgdHN0YXJ0OiBzdGFydFRpbWUsXG4gICAgICAgICAgICB0ZGVjcnlwdDogZW5kVGltZVxuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICB9KS5jYXRjaChlcnIgPT4ge1xuICAgICAgICB0aGlzLndhcm4oYCR7ZXJyLm5hbWV9OiAke2Vyci5tZXNzYWdlfWApO1xuICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuICBkb1RpY2soKSB7XG4gICAgaWYgKCF0aGlzLm1lZGlhKSB7XG4gICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKHRoaXMuc3RhdGUgPT09IFN0YXRlLklETEUpIHtcbiAgICAgIGNvbnN0IHtcbiAgICAgICAgY3VycmVudFRyYWNrSWQsXG4gICAgICAgIGxldmVsc1xuICAgICAgfSA9IHRoaXM7XG4gICAgICBjb25zdCB0cmFjayA9IGxldmVscyA9PSBudWxsID8gdm9pZCAwIDogbGV2ZWxzW2N1cnJlbnRUcmFja0lkXTtcbiAgICAgIGlmICghdHJhY2sgfHwgIWxldmVscy5sZW5ndGggfHwgIXRyYWNrLmRldGFpbHMpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY29uc3Qge1xuICAgICAgICBjb25maWdcbiAgICAgIH0gPSB0aGlzO1xuICAgICAgY29uc3QgY3VycmVudFRpbWUgPSB0aGlzLmdldExvYWRQb3NpdGlvbigpO1xuICAgICAgY29uc3QgYnVmZmVyZWRJbmZvID0gQnVmZmVySGVscGVyLmJ1ZmZlcmVkSW5mbyh0aGlzLnRyYWNrc0J1ZmZlcmVkW3RoaXMuY3VycmVudFRyYWNrSWRdIHx8IFtdLCBjdXJyZW50VGltZSwgY29uZmlnLm1heEJ1ZmZlckhvbGUpO1xuICAgICAgY29uc3Qge1xuICAgICAgICBlbmQ6IHRhcmdldEJ1ZmZlclRpbWUsXG4gICAgICAgIGxlbjogYnVmZmVyTGVuXG4gICAgICB9ID0gYnVmZmVyZWRJbmZvO1xuICAgICAgY29uc3QgbWFpbkJ1ZmZlckluZm8gPSB0aGlzLmdldEZ3ZEJ1ZmZlckluZm8odGhpcy5tZWRpYSwgUGxheWxpc3RMZXZlbFR5cGUuTUFJTik7XG4gICAgICBjb25zdCB0cmFja0RldGFpbHMgPSB0cmFjay5kZXRhaWxzO1xuICAgICAgY29uc3QgbWF4QnVmTGVuID0gdGhpcy5nZXRNYXhCdWZmZXJMZW5ndGgobWFpbkJ1ZmZlckluZm8gPT0gbnVsbCA/IHZvaWQgMCA6IG1haW5CdWZmZXJJbmZvLmxlbikgKyB0cmFja0RldGFpbHMubGV2ZWxUYXJnZXREdXJhdGlvbjtcbiAgICAgIGlmIChidWZmZXJMZW4gPiBtYXhCdWZMZW4pIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY29uc3QgZnJhZ21lbnRzID0gdHJhY2tEZXRhaWxzLmZyYWdtZW50cztcbiAgICAgIGNvbnN0IGZyYWdMZW4gPSBmcmFnbWVudHMubGVuZ3RoO1xuICAgICAgY29uc3QgZW5kID0gdHJhY2tEZXRhaWxzLmVkZ2U7XG4gICAgICBsZXQgZm91bmRGcmFnID0gbnVsbDtcbiAgICAgIGNvbnN0IGZyYWdQcmV2aW91cyA9IHRoaXMuZnJhZ1ByZXZpb3VzO1xuICAgICAgaWYgKHRhcmdldEJ1ZmZlclRpbWUgPCBlbmQpIHtcbiAgICAgICAgY29uc3QgdG9sZXJhbmNlID0gY29uZmlnLm1heEZyYWdMb29rVXBUb2xlcmFuY2U7XG4gICAgICAgIGNvbnN0IGxvb2t1cFRvbGVyYW5jZSA9IHRhcmdldEJ1ZmZlclRpbWUgPiBlbmQgLSB0b2xlcmFuY2UgPyAwIDogdG9sZXJhbmNlO1xuICAgICAgICBmb3VuZEZyYWcgPSBmaW5kRnJhZ21lbnRCeVBUUyhmcmFnUHJldmlvdXMsIGZyYWdtZW50cywgTWF0aC5tYXgoZnJhZ21lbnRzWzBdLnN0YXJ0LCB0YXJnZXRCdWZmZXJUaW1lKSwgbG9va3VwVG9sZXJhbmNlKTtcbiAgICAgICAgaWYgKCFmb3VuZEZyYWcgJiYgZnJhZ1ByZXZpb3VzICYmIGZyYWdQcmV2aW91cy5zdGFydCA8IGZyYWdtZW50c1swXS5zdGFydCkge1xuICAgICAgICAgIGZvdW5kRnJhZyA9IGZyYWdtZW50c1swXTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZm91bmRGcmFnID0gZnJhZ21lbnRzW2ZyYWdMZW4gLSAxXTtcbiAgICAgIH1cbiAgICAgIGlmICghZm91bmRGcmFnKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGZvdW5kRnJhZyA9IHRoaXMubWFwVG9Jbml0RnJhZ1doZW5SZXF1aXJlZChmb3VuZEZyYWcpO1xuICAgICAgaWYgKGZvdW5kRnJhZy5zbiAhPT0gJ2luaXRTZWdtZW50Jykge1xuICAgICAgICAvLyBMb2FkIGVhcmxpZXIgZnJhZ21lbnQgaW4gc2FtZSBkaXNjb250aW51aXR5IHRvIG1ha2UgdXAgZm9yIG1pc2FsaWduZWQgcGxheWxpc3RzIGFuZCBjdWVzIHRoYXQgZXh0ZW5kIGJleW9uZCBlbmQgb2Ygc2VnbWVudFxuICAgICAgICBjb25zdCBjdXJTTklkeCA9IGZvdW5kRnJhZy5zbiAtIHRyYWNrRGV0YWlscy5zdGFydFNOO1xuICAgICAgICBjb25zdCBwcmV2RnJhZyA9IGZyYWdtZW50c1tjdXJTTklkeCAtIDFdO1xuICAgICAgICBpZiAocHJldkZyYWcgJiYgcHJldkZyYWcuY2MgPT09IGZvdW5kRnJhZy5jYyAmJiB0aGlzLmZyYWdtZW50VHJhY2tlci5nZXRTdGF0ZShwcmV2RnJhZykgPT09IEZyYWdtZW50U3RhdGUuTk9UX0xPQURFRCkge1xuICAgICAgICAgIGZvdW5kRnJhZyA9IHByZXZGcmFnO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAodGhpcy5mcmFnbWVudFRyYWNrZXIuZ2V0U3RhdGUoZm91bmRGcmFnKSA9PT0gRnJhZ21lbnRTdGF0ZS5OT1RfTE9BREVEKSB7XG4gICAgICAgIC8vIG9ubHkgbG9hZCBpZiBmcmFnbWVudCBpcyBub3QgbG9hZGVkXG4gICAgICAgIHRoaXMubG9hZEZyYWdtZW50KGZvdW5kRnJhZywgdHJhY2ssIHRhcmdldEJ1ZmZlclRpbWUpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBnZXRNYXhCdWZmZXJMZW5ndGgobWFpbkJ1ZmZlckxlbmd0aCkge1xuICAgIGNvbnN0IG1heENvbmZpZ0J1ZmZlciA9IHN1cGVyLmdldE1heEJ1ZmZlckxlbmd0aCgpO1xuICAgIGlmICghbWFpbkJ1ZmZlckxlbmd0aCkge1xuICAgICAgcmV0dXJuIG1heENvbmZpZ0J1ZmZlcjtcbiAgICB9XG4gICAgcmV0dXJuIE1hdGgubWF4KG1heENvbmZpZ0J1ZmZlciwgbWFpbkJ1ZmZlckxlbmd0aCk7XG4gIH1cbiAgbG9hZEZyYWdtZW50KGZyYWcsIGxldmVsLCB0YXJnZXRCdWZmZXJUaW1lKSB7XG4gICAgdGhpcy5mcmFnQ3VycmVudCA9IGZyYWc7XG4gICAgaWYgKGZyYWcuc24gPT09ICdpbml0U2VnbWVudCcpIHtcbiAgICAgIHRoaXMuX2xvYWRJbml0U2VnbWVudChmcmFnLCBsZXZlbCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuc3RhcnRGcmFnUmVxdWVzdGVkID0gdHJ1ZTtcbiAgICAgIHN1cGVyLmxvYWRGcmFnbWVudChmcmFnLCBsZXZlbCwgdGFyZ2V0QnVmZmVyVGltZSk7XG4gICAgfVxuICB9XG4gIGdldCBtZWRpYUJ1ZmZlclRpbWVSYW5nZXMoKSB7XG4gICAgcmV0dXJuIG5ldyBCdWZmZXJhYmxlSW5zdGFuY2UodGhpcy50cmFja3NCdWZmZXJlZFt0aGlzLmN1cnJlbnRUcmFja0lkXSB8fCBbXSk7XG4gIH1cbn1cbmNsYXNzIEJ1ZmZlcmFibGVJbnN0YW5jZSB7XG4gIGNvbnN0cnVjdG9yKHRpbWVyYW5nZXMpIHtcbiAgICB0aGlzLmJ1ZmZlcmVkID0gdm9pZCAwO1xuICAgIGNvbnN0IGdldFJhbmdlID0gKG5hbWUsIGluZGV4LCBsZW5ndGgpID0+IHtcbiAgICAgIGluZGV4ID0gaW5kZXggPj4+IDA7XG4gICAgICBpZiAoaW5kZXggPiBsZW5ndGggLSAxKSB7XG4gICAgICAgIHRocm93IG5ldyBET01FeGNlcHRpb24oYEZhaWxlZCB0byBleGVjdXRlICcke25hbWV9JyBvbiAnVGltZVJhbmdlcyc6IFRoZSBpbmRleCBwcm92aWRlZCAoJHtpbmRleH0pIGlzIGdyZWF0ZXIgdGhhbiB0aGUgbWF4aW11bSBib3VuZCAoJHtsZW5ndGh9KWApO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRpbWVyYW5nZXNbaW5kZXhdW25hbWVdO1xuICAgIH07XG4gICAgdGhpcy5idWZmZXJlZCA9IHtcbiAgICAgIGdldCBsZW5ndGgoKSB7XG4gICAgICAgIHJldHVybiB0aW1lcmFuZ2VzLmxlbmd0aDtcbiAgICAgIH0sXG4gICAgICBlbmQoaW5kZXgpIHtcbiAgICAgICAgcmV0dXJuIGdldFJhbmdlKCdlbmQnLCBpbmRleCwgdGltZXJhbmdlcy5sZW5ndGgpO1xuICAgICAgfSxcbiAgICAgIHN0YXJ0KGluZGV4KSB7XG4gICAgICAgIHJldHVybiBnZXRSYW5nZSgnc3RhcnQnLCBpbmRleCwgdGltZXJhbmdlcy5sZW5ndGgpO1xuICAgICAgfVxuICAgIH07XG4gIH1cbn1cblxuY2xhc3MgU3VidGl0bGVUcmFja0NvbnRyb2xsZXIgZXh0ZW5kcyBCYXNlUGxheWxpc3RDb250cm9sbGVyIHtcbiAgY29uc3RydWN0b3IoaGxzKSB7XG4gICAgc3VwZXIoaGxzLCAnW3N1YnRpdGxlLXRyYWNrLWNvbnRyb2xsZXJdJyk7XG4gICAgdGhpcy5tZWRpYSA9IG51bGw7XG4gICAgdGhpcy50cmFja3MgPSBbXTtcbiAgICB0aGlzLmdyb3VwSWRzID0gbnVsbDtcbiAgICB0aGlzLnRyYWNrc0luR3JvdXAgPSBbXTtcbiAgICB0aGlzLnRyYWNrSWQgPSAtMTtcbiAgICB0aGlzLmN1cnJlbnRUcmFjayA9IG51bGw7XG4gICAgdGhpcy5zZWxlY3REZWZhdWx0VHJhY2sgPSB0cnVlO1xuICAgIHRoaXMucXVldWVkRGVmYXVsdFRyYWNrID0gLTE7XG4gICAgdGhpcy5hc3luY1BvbGxUcmFja0NoYW5nZSA9ICgpID0+IHRoaXMucG9sbFRyYWNrQ2hhbmdlKDApO1xuICAgIHRoaXMudXNlVGV4dFRyYWNrUG9sbGluZyA9IGZhbHNlO1xuICAgIHRoaXMuc3VidGl0bGVQb2xsaW5nSW50ZXJ2YWwgPSAtMTtcbiAgICB0aGlzLl9zdWJ0aXRsZURpc3BsYXkgPSB0cnVlO1xuICAgIHRoaXMub25UZXh0VHJhY2tzQ2hhbmdlZCA9ICgpID0+IHtcbiAgICAgIGlmICghdGhpcy51c2VUZXh0VHJhY2tQb2xsaW5nKSB7XG4gICAgICAgIHNlbGYuY2xlYXJJbnRlcnZhbCh0aGlzLnN1YnRpdGxlUG9sbGluZ0ludGVydmFsKTtcbiAgICAgIH1cbiAgICAgIC8vIE1lZGlhIGlzIHVuZGVmaW5lZCB3aGVuIHN3aXRjaGluZyBzdHJlYW1zIHZpYSBsb2FkU291cmNlKClcbiAgICAgIGlmICghdGhpcy5tZWRpYSB8fCAhdGhpcy5obHMuY29uZmlnLnJlbmRlclRleHRUcmFja3NOYXRpdmVseSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBsZXQgdGV4dFRyYWNrID0gbnVsbDtcbiAgICAgIGNvbnN0IHRyYWNrcyA9IGZpbHRlclN1YnRpdGxlVHJhY2tzKHRoaXMubWVkaWEudGV4dFRyYWNrcyk7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRyYWNrcy5sZW5ndGg7IGkrKykge1xuICAgICAgICBpZiAodHJhY2tzW2ldLm1vZGUgPT09ICdoaWRkZW4nKSB7XG4gICAgICAgICAgLy8gRG8gbm90IGJyZWFrIGluIGNhc2UgdGhlcmUgaXMgYSBmb2xsb3dpbmcgdHJhY2sgd2l0aCBzaG93aW5nLlxuICAgICAgICAgIHRleHRUcmFjayA9IHRyYWNrc1tpXTtcbiAgICAgICAgfSBlbHNlIGlmICh0cmFja3NbaV0ubW9kZSA9PT0gJ3Nob3dpbmcnKSB7XG4gICAgICAgICAgdGV4dFRyYWNrID0gdHJhY2tzW2ldO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIEZpbmQgaW50ZXJuYWwgdHJhY2sgaW5kZXggZm9yIFRleHRUcmFja1xuICAgICAgY29uc3QgdHJhY2tJZCA9IHRoaXMuZmluZFRyYWNrRm9yVGV4dFRyYWNrKHRleHRUcmFjayk7XG4gICAgICBpZiAodGhpcy5zdWJ0aXRsZVRyYWNrICE9PSB0cmFja0lkKSB7XG4gICAgICAgIHRoaXMuc2V0U3VidGl0bGVUcmFjayh0cmFja0lkKTtcbiAgICAgIH1cbiAgICB9O1xuICAgIHRoaXMucmVnaXN0ZXJMaXN0ZW5lcnMoKTtcbiAgfVxuICBkZXN0cm95KCkge1xuICAgIHRoaXMudW5yZWdpc3Rlckxpc3RlbmVycygpO1xuICAgIHRoaXMudHJhY2tzLmxlbmd0aCA9IDA7XG4gICAgdGhpcy50cmFja3NJbkdyb3VwLmxlbmd0aCA9IDA7XG4gICAgdGhpcy5jdXJyZW50VHJhY2sgPSBudWxsO1xuICAgIHRoaXMub25UZXh0VHJhY2tzQ2hhbmdlZCA9IHRoaXMuYXN5bmNQb2xsVHJhY2tDaGFuZ2UgPSBudWxsO1xuICAgIHN1cGVyLmRlc3Ryb3koKTtcbiAgfVxuICBnZXQgc3VidGl0bGVEaXNwbGF5KCkge1xuICAgIHJldHVybiB0aGlzLl9zdWJ0aXRsZURpc3BsYXk7XG4gIH1cbiAgc2V0IHN1YnRpdGxlRGlzcGxheSh2YWx1ZSkge1xuICAgIHRoaXMuX3N1YnRpdGxlRGlzcGxheSA9IHZhbHVlO1xuICAgIGlmICh0aGlzLnRyYWNrSWQgPiAtMSkge1xuICAgICAgdGhpcy50b2dnbGVUcmFja01vZGVzKCk7XG4gICAgfVxuICB9XG4gIHJlZ2lzdGVyTGlzdGVuZXJzKCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGhsc1xuICAgIH0gPSB0aGlzO1xuICAgIGhscy5vbihFdmVudHMuTUVESUFfQVRUQUNIRUQsIHRoaXMub25NZWRpYUF0dGFjaGVkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLk1FRElBX0RFVEFDSElORywgdGhpcy5vbk1lZGlhRGV0YWNoaW5nLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLk1BTklGRVNUX0xPQURJTkcsIHRoaXMub25NYW5pZmVzdExvYWRpbmcsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuTUFOSUZFU1RfUEFSU0VELCB0aGlzLm9uTWFuaWZlc3RQYXJzZWQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuTEVWRUxfTE9BRElORywgdGhpcy5vbkxldmVsTG9hZGluZywgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5MRVZFTF9TV0lUQ0hJTkcsIHRoaXMub25MZXZlbFN3aXRjaGluZywgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5TVUJUSVRMRV9UUkFDS19MT0FERUQsIHRoaXMub25TdWJ0aXRsZVRyYWNrTG9hZGVkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkVSUk9SLCB0aGlzLm9uRXJyb3IsIHRoaXMpO1xuICB9XG4gIHVucmVnaXN0ZXJMaXN0ZW5lcnMoKSB7XG4gICAgY29uc3Qge1xuICAgICAgaGxzXG4gICAgfSA9IHRoaXM7XG4gICAgaGxzLm9mZihFdmVudHMuTUVESUFfQVRUQUNIRUQsIHRoaXMub25NZWRpYUF0dGFjaGVkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5NRURJQV9ERVRBQ0hJTkcsIHRoaXMub25NZWRpYURldGFjaGluZywgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuTUFOSUZFU1RfTE9BRElORywgdGhpcy5vbk1hbmlmZXN0TG9hZGluZywgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuTUFOSUZFU1RfUEFSU0VELCB0aGlzLm9uTWFuaWZlc3RQYXJzZWQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkxFVkVMX0xPQURJTkcsIHRoaXMub25MZXZlbExvYWRpbmcsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkxFVkVMX1NXSVRDSElORywgdGhpcy5vbkxldmVsU3dpdGNoaW5nLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5TVUJUSVRMRV9UUkFDS19MT0FERUQsIHRoaXMub25TdWJ0aXRsZVRyYWNrTG9hZGVkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5FUlJPUiwgdGhpcy5vbkVycm9yLCB0aGlzKTtcbiAgfVxuXG4gIC8vIExpc3RlbiBmb3Igc3VidGl0bGUgdHJhY2sgY2hhbmdlLCB0aGVuIGV4dHJhY3QgdGhlIGN1cnJlbnQgdHJhY2sgSUQuXG4gIG9uTWVkaWFBdHRhY2hlZChldmVudCwgZGF0YSkge1xuICAgIHRoaXMubWVkaWEgPSBkYXRhLm1lZGlhO1xuICAgIGlmICghdGhpcy5tZWRpYSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAodGhpcy5xdWV1ZWREZWZhdWx0VHJhY2sgPiAtMSkge1xuICAgICAgdGhpcy5zdWJ0aXRsZVRyYWNrID0gdGhpcy5xdWV1ZWREZWZhdWx0VHJhY2s7XG4gICAgICB0aGlzLnF1ZXVlZERlZmF1bHRUcmFjayA9IC0xO1xuICAgIH1cbiAgICB0aGlzLnVzZVRleHRUcmFja1BvbGxpbmcgPSAhKHRoaXMubWVkaWEudGV4dFRyYWNrcyAmJiAnb25jaGFuZ2UnIGluIHRoaXMubWVkaWEudGV4dFRyYWNrcyk7XG4gICAgaWYgKHRoaXMudXNlVGV4dFRyYWNrUG9sbGluZykge1xuICAgICAgdGhpcy5wb2xsVHJhY2tDaGFuZ2UoNTAwKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5tZWRpYS50ZXh0VHJhY2tzLmFkZEV2ZW50TGlzdGVuZXIoJ2NoYW5nZScsIHRoaXMuYXN5bmNQb2xsVHJhY2tDaGFuZ2UpO1xuICAgIH1cbiAgfVxuICBwb2xsVHJhY2tDaGFuZ2UodGltZW91dCkge1xuICAgIHNlbGYuY2xlYXJJbnRlcnZhbCh0aGlzLnN1YnRpdGxlUG9sbGluZ0ludGVydmFsKTtcbiAgICB0aGlzLnN1YnRpdGxlUG9sbGluZ0ludGVydmFsID0gc2VsZi5zZXRJbnRlcnZhbCh0aGlzLm9uVGV4dFRyYWNrc0NoYW5nZWQsIHRpbWVvdXQpO1xuICB9XG4gIG9uTWVkaWFEZXRhY2hpbmcoKSB7XG4gICAgaWYgKCF0aGlzLm1lZGlhKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHNlbGYuY2xlYXJJbnRlcnZhbCh0aGlzLnN1YnRpdGxlUG9sbGluZ0ludGVydmFsKTtcbiAgICBpZiAoIXRoaXMudXNlVGV4dFRyYWNrUG9sbGluZykge1xuICAgICAgdGhpcy5tZWRpYS50ZXh0VHJhY2tzLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2NoYW5nZScsIHRoaXMuYXN5bmNQb2xsVHJhY2tDaGFuZ2UpO1xuICAgIH1cbiAgICBpZiAodGhpcy50cmFja0lkID4gLTEpIHtcbiAgICAgIHRoaXMucXVldWVkRGVmYXVsdFRyYWNrID0gdGhpcy50cmFja0lkO1xuICAgIH1cbiAgICBjb25zdCB0ZXh0VHJhY2tzID0gZmlsdGVyU3VidGl0bGVUcmFja3ModGhpcy5tZWRpYS50ZXh0VHJhY2tzKTtcbiAgICAvLyBDbGVhciBsb2FkZWQgY3VlcyBvbiBtZWRpYSBkZXRhY2htZW50IGZyb20gdHJhY2tzXG4gICAgdGV4dFRyYWNrcy5mb3JFYWNoKHRyYWNrID0+IHtcbiAgICAgIGNsZWFyQ3VycmVudEN1ZXModHJhY2spO1xuICAgIH0pO1xuICAgIC8vIERpc2FibGUgYWxsIHN1YnRpdGxlIHRyYWNrcyBiZWZvcmUgZGV0YWNobWVudCBzbyB3aGVuIHJlYXR0YWNoZWQgb25seSB0cmFja3MgaW4gdGhhdCBjb250ZW50IGFyZSBlbmFibGVkLlxuICAgIHRoaXMuc3VidGl0bGVUcmFjayA9IC0xO1xuICAgIHRoaXMubWVkaWEgPSBudWxsO1xuICB9XG4gIG9uTWFuaWZlc3RMb2FkaW5nKCkge1xuICAgIHRoaXMudHJhY2tzID0gW107XG4gICAgdGhpcy5ncm91cElkcyA9IG51bGw7XG4gICAgdGhpcy50cmFja3NJbkdyb3VwID0gW107XG4gICAgdGhpcy50cmFja0lkID0gLTE7XG4gICAgdGhpcy5jdXJyZW50VHJhY2sgPSBudWxsO1xuICAgIHRoaXMuc2VsZWN0RGVmYXVsdFRyYWNrID0gdHJ1ZTtcbiAgfVxuXG4gIC8vIEZpcmVkIHdoZW5ldmVyIGEgbmV3IG1hbmlmZXN0IGlzIGxvYWRlZC5cbiAgb25NYW5pZmVzdFBhcnNlZChldmVudCwgZGF0YSkge1xuICAgIHRoaXMudHJhY2tzID0gZGF0YS5zdWJ0aXRsZVRyYWNrcztcbiAgfVxuICBvblN1YnRpdGxlVHJhY2tMb2FkZWQoZXZlbnQsIGRhdGEpIHtcbiAgICBjb25zdCB7XG4gICAgICBpZCxcbiAgICAgIGdyb3VwSWQsXG4gICAgICBkZXRhaWxzXG4gICAgfSA9IGRhdGE7XG4gICAgY29uc3QgdHJhY2tJbkFjdGl2ZUdyb3VwID0gdGhpcy50cmFja3NJbkdyb3VwW2lkXTtcbiAgICBpZiAoIXRyYWNrSW5BY3RpdmVHcm91cCB8fCB0cmFja0luQWN0aXZlR3JvdXAuZ3JvdXBJZCAhPT0gZ3JvdXBJZCkge1xuICAgICAgdGhpcy53YXJuKGBTdWJ0aXRsZSB0cmFjayB3aXRoIGlkOiR7aWR9IGFuZCBncm91cDoke2dyb3VwSWR9IG5vdCBmb3VuZCBpbiBhY3RpdmUgZ3JvdXAgJHt0cmFja0luQWN0aXZlR3JvdXAgPT0gbnVsbCA/IHZvaWQgMCA6IHRyYWNrSW5BY3RpdmVHcm91cC5ncm91cElkfWApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBjdXJEZXRhaWxzID0gdHJhY2tJbkFjdGl2ZUdyb3VwLmRldGFpbHM7XG4gICAgdHJhY2tJbkFjdGl2ZUdyb3VwLmRldGFpbHMgPSBkYXRhLmRldGFpbHM7XG4gICAgdGhpcy5sb2coYFN1YnRpdGxlIHRyYWNrICR7aWR9IFwiJHt0cmFja0luQWN0aXZlR3JvdXAubmFtZX1cIiBsYW5nOiR7dHJhY2tJbkFjdGl2ZUdyb3VwLmxhbmd9IGdyb3VwOiR7Z3JvdXBJZH0gbG9hZGVkIFske2RldGFpbHMuc3RhcnRTTn0tJHtkZXRhaWxzLmVuZFNOfV1gKTtcbiAgICBpZiAoaWQgPT09IHRoaXMudHJhY2tJZCkge1xuICAgICAgdGhpcy5wbGF5bGlzdExvYWRlZChpZCwgZGF0YSwgY3VyRGV0YWlscyk7XG4gICAgfVxuICB9XG4gIG9uTGV2ZWxMb2FkaW5nKGV2ZW50LCBkYXRhKSB7XG4gICAgdGhpcy5zd2l0Y2hMZXZlbChkYXRhLmxldmVsKTtcbiAgfVxuICBvbkxldmVsU3dpdGNoaW5nKGV2ZW50LCBkYXRhKSB7XG4gICAgdGhpcy5zd2l0Y2hMZXZlbChkYXRhLmxldmVsKTtcbiAgfVxuICBzd2l0Y2hMZXZlbChsZXZlbEluZGV4KSB7XG4gICAgY29uc3QgbGV2ZWxJbmZvID0gdGhpcy5obHMubGV2ZWxzW2xldmVsSW5kZXhdO1xuICAgIGlmICghbGV2ZWxJbmZvKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHN1YnRpdGxlR3JvdXBzID0gbGV2ZWxJbmZvLnN1YnRpdGxlR3JvdXBzIHx8IG51bGw7XG4gICAgY29uc3QgY3VycmVudEdyb3VwcyA9IHRoaXMuZ3JvdXBJZHM7XG4gICAgbGV0IGN1cnJlbnRUcmFjayA9IHRoaXMuY3VycmVudFRyYWNrO1xuICAgIGlmICghc3VidGl0bGVHcm91cHMgfHwgKGN1cnJlbnRHcm91cHMgPT0gbnVsbCA/IHZvaWQgMCA6IGN1cnJlbnRHcm91cHMubGVuZ3RoKSAhPT0gKHN1YnRpdGxlR3JvdXBzID09IG51bGwgPyB2b2lkIDAgOiBzdWJ0aXRsZUdyb3Vwcy5sZW5ndGgpIHx8IHN1YnRpdGxlR3JvdXBzICE9IG51bGwgJiYgc3VidGl0bGVHcm91cHMuc29tZShncm91cElkID0+IChjdXJyZW50R3JvdXBzID09IG51bGwgPyB2b2lkIDAgOiBjdXJyZW50R3JvdXBzLmluZGV4T2YoZ3JvdXBJZCkpID09PSAtMSkpIHtcbiAgICAgIHRoaXMuZ3JvdXBJZHMgPSBzdWJ0aXRsZUdyb3VwcztcbiAgICAgIHRoaXMudHJhY2tJZCA9IC0xO1xuICAgICAgdGhpcy5jdXJyZW50VHJhY2sgPSBudWxsO1xuICAgICAgY29uc3Qgc3VidGl0bGVUcmFja3MgPSB0aGlzLnRyYWNrcy5maWx0ZXIodHJhY2sgPT4gIXN1YnRpdGxlR3JvdXBzIHx8IHN1YnRpdGxlR3JvdXBzLmluZGV4T2YodHJhY2suZ3JvdXBJZCkgIT09IC0xKTtcbiAgICAgIGlmIChzdWJ0aXRsZVRyYWNrcy5sZW5ndGgpIHtcbiAgICAgICAgLy8gRGlzYWJsZSBzZWxlY3REZWZhdWx0VHJhY2sgaWYgdGhlcmUgYXJlIG5vIGRlZmF1bHQgdHJhY2tzXG4gICAgICAgIGlmICh0aGlzLnNlbGVjdERlZmF1bHRUcmFjayAmJiAhc3VidGl0bGVUcmFja3Muc29tZSh0cmFjayA9PiB0cmFjay5kZWZhdWx0KSkge1xuICAgICAgICAgIHRoaXMuc2VsZWN0RGVmYXVsdFRyYWNrID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgLy8gdHJhY2suaWQgc2hvdWxkIG1hdGNoIGhscy5hdWRpb1RyYWNrcyBpbmRleFxuICAgICAgICBzdWJ0aXRsZVRyYWNrcy5mb3JFYWNoKCh0cmFjaywgaSkgPT4ge1xuICAgICAgICAgIHRyYWNrLmlkID0gaTtcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2UgaWYgKCFjdXJyZW50VHJhY2sgJiYgIXRoaXMudHJhY2tzSW5Hcm91cC5sZW5ndGgpIHtcbiAgICAgICAgLy8gRG8gbm90IGRpc3BhdGNoIFNVQlRJVExFX1RSQUNLU19VUERBVEVEIHdoZW4gdGhlcmUgd2VyZSBhbmQgYXJlIG5vIHRyYWNrc1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aGlzLnRyYWNrc0luR3JvdXAgPSBzdWJ0aXRsZVRyYWNrcztcblxuICAgICAgLy8gRmluZCBwcmVmZXJyZWQgdHJhY2tcbiAgICAgIGNvbnN0IHN1YnRpdGxlUHJlZmVyZW5jZSA9IHRoaXMuaGxzLmNvbmZpZy5zdWJ0aXRsZVByZWZlcmVuY2U7XG4gICAgICBpZiAoIWN1cnJlbnRUcmFjayAmJiBzdWJ0aXRsZVByZWZlcmVuY2UpIHtcbiAgICAgICAgdGhpcy5zZWxlY3REZWZhdWx0VHJhY2sgPSBmYWxzZTtcbiAgICAgICAgY29uc3QgZ3JvdXBJbmRleCA9IGZpbmRNYXRjaGluZ09wdGlvbihzdWJ0aXRsZVByZWZlcmVuY2UsIHN1YnRpdGxlVHJhY2tzKTtcbiAgICAgICAgaWYgKGdyb3VwSW5kZXggPiAtMSkge1xuICAgICAgICAgIGN1cnJlbnRUcmFjayA9IHN1YnRpdGxlVHJhY2tzW2dyb3VwSW5kZXhdO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGNvbnN0IGFsbEluZGV4ID0gZmluZE1hdGNoaW5nT3B0aW9uKHN1YnRpdGxlUHJlZmVyZW5jZSwgdGhpcy50cmFja3MpO1xuICAgICAgICAgIGN1cnJlbnRUcmFjayA9IHRoaXMudHJhY2tzW2FsbEluZGV4XTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBTZWxlY3QgaW5pdGlhbCB0cmFja1xuICAgICAgbGV0IHRyYWNrSWQgPSB0aGlzLmZpbmRUcmFja0lkKGN1cnJlbnRUcmFjayk7XG4gICAgICBpZiAodHJhY2tJZCA9PT0gLTEgJiYgY3VycmVudFRyYWNrKSB7XG4gICAgICAgIHRyYWNrSWQgPSB0aGlzLmZpbmRUcmFja0lkKG51bGwpO1xuICAgICAgfVxuXG4gICAgICAvLyBEaXNwYXRjaCBldmVudHMgYW5kIGxvYWQgdHJhY2sgaWYgbmVlZGVkXG4gICAgICBjb25zdCBzdWJ0aXRsZVRyYWNrc1VwZGF0ZWQgPSB7XG4gICAgICAgIHN1YnRpdGxlVHJhY2tzXG4gICAgICB9O1xuICAgICAgdGhpcy5sb2coYFVwZGF0aW5nIHN1YnRpdGxlIHRyYWNrcywgJHtzdWJ0aXRsZVRyYWNrcy5sZW5ndGh9IHRyYWNrKHMpIGZvdW5kIGluIFwiJHtzdWJ0aXRsZUdyb3VwcyA9PSBudWxsID8gdm9pZCAwIDogc3VidGl0bGVHcm91cHMuam9pbignLCcpfVwiIGdyb3VwLWlkYCk7XG4gICAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5TVUJUSVRMRV9UUkFDS1NfVVBEQVRFRCwgc3VidGl0bGVUcmFja3NVcGRhdGVkKTtcbiAgICAgIGlmICh0cmFja0lkICE9PSAtMSAmJiB0aGlzLnRyYWNrSWQgPT09IC0xKSB7XG4gICAgICAgIHRoaXMuc2V0U3VidGl0bGVUcmFjayh0cmFja0lkKTtcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKHRoaXMuc2hvdWxkUmVsb2FkUGxheWxpc3QoY3VycmVudFRyYWNrKSkge1xuICAgICAgLy8gUmV0cnkgcGxheWxpc3QgbG9hZGluZyBpZiBubyBwbGF5bGlzdCBpcyBvciBoYXMgYmVlbiBsb2FkZWQgeWV0XG4gICAgICB0aGlzLnNldFN1YnRpdGxlVHJhY2sodGhpcy50cmFja0lkKTtcbiAgICB9XG4gIH1cbiAgZmluZFRyYWNrSWQoY3VycmVudFRyYWNrKSB7XG4gICAgY29uc3QgdHJhY2tzID0gdGhpcy50cmFja3NJbkdyb3VwO1xuICAgIGNvbnN0IHNlbGVjdERlZmF1bHQgPSB0aGlzLnNlbGVjdERlZmF1bHRUcmFjaztcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRyYWNrcy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgdHJhY2sgPSB0cmFja3NbaV07XG4gICAgICBpZiAoc2VsZWN0RGVmYXVsdCAmJiAhdHJhY2suZGVmYXVsdCB8fCAhc2VsZWN0RGVmYXVsdCAmJiAhY3VycmVudFRyYWNrKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgaWYgKCFjdXJyZW50VHJhY2sgfHwgbWF0Y2hlc09wdGlvbih0cmFjaywgY3VycmVudFRyYWNrKSkge1xuICAgICAgICByZXR1cm4gaTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKGN1cnJlbnRUcmFjaykge1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0cmFja3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgY29uc3QgdHJhY2sgPSB0cmFja3NbaV07XG4gICAgICAgIGlmIChtZWRpYUF0dHJpYnV0ZXNJZGVudGljYWwoY3VycmVudFRyYWNrLmF0dHJzLCB0cmFjay5hdHRycywgWydMQU5HVUFHRScsICdBU1NPQy1MQU5HVUFHRScsICdDSEFSQUNURVJJU1RJQ1MnXSkpIHtcbiAgICAgICAgICByZXR1cm4gaTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0cmFja3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgY29uc3QgdHJhY2sgPSB0cmFja3NbaV07XG4gICAgICAgIGlmIChtZWRpYUF0dHJpYnV0ZXNJZGVudGljYWwoY3VycmVudFRyYWNrLmF0dHJzLCB0cmFjay5hdHRycywgWydMQU5HVUFHRSddKSkge1xuICAgICAgICAgIHJldHVybiBpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiAtMTtcbiAgfVxuICBmaW5kVHJhY2tGb3JUZXh0VHJhY2sodGV4dFRyYWNrKSB7XG4gICAgaWYgKHRleHRUcmFjaykge1xuICAgICAgY29uc3QgdHJhY2tzID0gdGhpcy50cmFja3NJbkdyb3VwO1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0cmFja3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgY29uc3QgdHJhY2sgPSB0cmFja3NbaV07XG4gICAgICAgIGlmIChzdWJ0aXRsZVRyYWNrTWF0Y2hlc1RleHRUcmFjayh0cmFjaywgdGV4dFRyYWNrKSkge1xuICAgICAgICAgIHJldHVybiBpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiAtMTtcbiAgfVxuICBvbkVycm9yKGV2ZW50LCBkYXRhKSB7XG4gICAgaWYgKGRhdGEuZmF0YWwgfHwgIWRhdGEuY29udGV4dCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAoZGF0YS5jb250ZXh0LnR5cGUgPT09IFBsYXlsaXN0Q29udGV4dFR5cGUuU1VCVElUTEVfVFJBQ0sgJiYgZGF0YS5jb250ZXh0LmlkID09PSB0aGlzLnRyYWNrSWQgJiYgKCF0aGlzLmdyb3VwSWRzIHx8IHRoaXMuZ3JvdXBJZHMuaW5kZXhPZihkYXRhLmNvbnRleHQuZ3JvdXBJZCkgIT09IC0xKSkge1xuICAgICAgdGhpcy5jaGVja1JldHJ5KGRhdGEpO1xuICAgIH1cbiAgfVxuICBnZXQgYWxsU3VidGl0bGVUcmFja3MoKSB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2tzO1xuICB9XG5cbiAgLyoqIGdldCBhbHRlcm5hdGUgc3VidGl0bGUgdHJhY2tzIGxpc3QgZnJvbSBwbGF5bGlzdCAqKi9cbiAgZ2V0IHN1YnRpdGxlVHJhY2tzKCkge1xuICAgIHJldHVybiB0aGlzLnRyYWNrc0luR3JvdXA7XG4gIH1cblxuICAvKiogZ2V0L3NldCBpbmRleCBvZiB0aGUgc2VsZWN0ZWQgc3VidGl0bGUgdHJhY2sgKGJhc2VkIG9uIGluZGV4IGluIHN1YnRpdGxlIHRyYWNrIGxpc3RzKSAqKi9cbiAgZ2V0IHN1YnRpdGxlVHJhY2soKSB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2tJZDtcbiAgfVxuICBzZXQgc3VidGl0bGVUcmFjayhuZXdJZCkge1xuICAgIHRoaXMuc2VsZWN0RGVmYXVsdFRyYWNrID0gZmFsc2U7XG4gICAgdGhpcy5zZXRTdWJ0aXRsZVRyYWNrKG5ld0lkKTtcbiAgfVxuICBzZXRTdWJ0aXRsZU9wdGlvbihzdWJ0aXRsZU9wdGlvbikge1xuICAgIHRoaXMuaGxzLmNvbmZpZy5zdWJ0aXRsZVByZWZlcmVuY2UgPSBzdWJ0aXRsZU9wdGlvbjtcbiAgICBpZiAoc3VidGl0bGVPcHRpb24pIHtcbiAgICAgIGNvbnN0IGFsbFN1YnRpdGxlVHJhY2tzID0gdGhpcy5hbGxTdWJ0aXRsZVRyYWNrcztcbiAgICAgIHRoaXMuc2VsZWN0RGVmYXVsdFRyYWNrID0gZmFsc2U7XG4gICAgICBpZiAoYWxsU3VidGl0bGVUcmFja3MubGVuZ3RoKSB7XG4gICAgICAgIC8vIEZpcnN0IHNlZSBpZiBjdXJyZW50IG9wdGlvbiBtYXRjaGVzIChubyBzd2l0Y2ggb3ApXG4gICAgICAgIGNvbnN0IGN1cnJlbnRUcmFjayA9IHRoaXMuY3VycmVudFRyYWNrO1xuICAgICAgICBpZiAoY3VycmVudFRyYWNrICYmIG1hdGNoZXNPcHRpb24oc3VidGl0bGVPcHRpb24sIGN1cnJlbnRUcmFjaykpIHtcbiAgICAgICAgICByZXR1cm4gY3VycmVudFRyYWNrO1xuICAgICAgICB9XG4gICAgICAgIC8vIEZpbmQgb3B0aW9uIGluIGN1cnJlbnQgZ3JvdXBcbiAgICAgICAgY29uc3QgZ3JvdXBJbmRleCA9IGZpbmRNYXRjaGluZ09wdGlvbihzdWJ0aXRsZU9wdGlvbiwgdGhpcy50cmFja3NJbkdyb3VwKTtcbiAgICAgICAgaWYgKGdyb3VwSW5kZXggPiAtMSkge1xuICAgICAgICAgIGNvbnN0IHRyYWNrID0gdGhpcy50cmFja3NJbkdyb3VwW2dyb3VwSW5kZXhdO1xuICAgICAgICAgIHRoaXMuc2V0U3VidGl0bGVUcmFjayhncm91cEluZGV4KTtcbiAgICAgICAgICByZXR1cm4gdHJhY2s7XG4gICAgICAgIH0gZWxzZSBpZiAoY3VycmVudFRyYWNrKSB7XG4gICAgICAgICAgLy8gSWYgdGhpcyBpcyBub3QgdGhlIGluaXRpYWwgc2VsZWN0aW9uIHJldHVybiBudWxsXG4gICAgICAgICAgLy8gb3B0aW9uIHNob3VsZCBoYXZlIG1hdGNoZWQgb25lIGluIGFjdGl2ZSBncm91cFxuICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIEZpbmQgdGhlIG9wdGlvbiBpbiBhbGwgdHJhY2tzIGZvciBpbml0aWFsIHNlbGVjdGlvblxuICAgICAgICAgIGNvbnN0IGFsbEluZGV4ID0gZmluZE1hdGNoaW5nT3B0aW9uKHN1YnRpdGxlT3B0aW9uLCBhbGxTdWJ0aXRsZVRyYWNrcyk7XG4gICAgICAgICAgaWYgKGFsbEluZGV4ID4gLTEpIHtcbiAgICAgICAgICAgIHJldHVybiBhbGxTdWJ0aXRsZVRyYWNrc1thbGxJbmRleF07XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG4gIGxvYWRQbGF5bGlzdChobHNVcmxQYXJhbWV0ZXJzKSB7XG4gICAgc3VwZXIubG9hZFBsYXlsaXN0KCk7XG4gICAgY29uc3QgY3VycmVudFRyYWNrID0gdGhpcy5jdXJyZW50VHJhY2s7XG4gICAgaWYgKHRoaXMuc2hvdWxkTG9hZFBsYXlsaXN0KGN1cnJlbnRUcmFjaykgJiYgY3VycmVudFRyYWNrKSB7XG4gICAgICBjb25zdCBpZCA9IGN1cnJlbnRUcmFjay5pZDtcbiAgICAgIGNvbnN0IGdyb3VwSWQgPSBjdXJyZW50VHJhY2suZ3JvdXBJZDtcbiAgICAgIGxldCB1cmwgPSBjdXJyZW50VHJhY2sudXJsO1xuICAgICAgaWYgKGhsc1VybFBhcmFtZXRlcnMpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICB1cmwgPSBobHNVcmxQYXJhbWV0ZXJzLmFkZERpcmVjdGl2ZXModXJsKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICB0aGlzLndhcm4oYENvdWxkIG5vdCBjb25zdHJ1Y3QgbmV3IFVSTCB3aXRoIEhMUyBEZWxpdmVyeSBEaXJlY3RpdmVzOiAke2Vycm9yfWApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICB0aGlzLmxvZyhgTG9hZGluZyBzdWJ0aXRsZSBwbGF5bGlzdCBmb3IgaWQgJHtpZH1gKTtcbiAgICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLlNVQlRJVExFX1RSQUNLX0xPQURJTkcsIHtcbiAgICAgICAgdXJsLFxuICAgICAgICBpZCxcbiAgICAgICAgZ3JvdXBJZCxcbiAgICAgICAgZGVsaXZlcnlEaXJlY3RpdmVzOiBobHNVcmxQYXJhbWV0ZXJzIHx8IG51bGxcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBEaXNhYmxlcyB0aGUgb2xkIHN1YnRpdGxlVHJhY2sgYW5kIHNldHMgY3VycmVudCBtb2RlIG9uIHRoZSBuZXh0IHN1YnRpdGxlVHJhY2suXG4gICAqIFRoaXMgb3BlcmF0ZXMgb24gdGhlIERPTSB0ZXh0VHJhY2tzLlxuICAgKiBBIHZhbHVlIG9mIC0xIHdpbGwgZGlzYWJsZSBhbGwgc3VidGl0bGUgdHJhY2tzLlxuICAgKi9cbiAgdG9nZ2xlVHJhY2tNb2RlcygpIHtcbiAgICBjb25zdCB7XG4gICAgICBtZWRpYVxuICAgIH0gPSB0aGlzO1xuICAgIGlmICghbWVkaWEpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgdGV4dFRyYWNrcyA9IGZpbHRlclN1YnRpdGxlVHJhY2tzKG1lZGlhLnRleHRUcmFja3MpO1xuICAgIGNvbnN0IGN1cnJlbnRUcmFjayA9IHRoaXMuY3VycmVudFRyYWNrO1xuICAgIGxldCBuZXh0VHJhY2s7XG4gICAgaWYgKGN1cnJlbnRUcmFjaykge1xuICAgICAgbmV4dFRyYWNrID0gdGV4dFRyYWNrcy5maWx0ZXIodGV4dFRyYWNrID0+IHN1YnRpdGxlVHJhY2tNYXRjaGVzVGV4dFRyYWNrKGN1cnJlbnRUcmFjaywgdGV4dFRyYWNrKSlbMF07XG4gICAgICBpZiAoIW5leHRUcmFjaykge1xuICAgICAgICB0aGlzLndhcm4oYFVuYWJsZSB0byBmaW5kIHN1YnRpdGxlIFRleHRUcmFjayB3aXRoIG5hbWUgXCIke2N1cnJlbnRUcmFjay5uYW1lfVwiIGFuZCBsYW5ndWFnZSBcIiR7Y3VycmVudFRyYWNrLmxhbmd9XCJgKTtcbiAgICAgIH1cbiAgICB9XG4gICAgW10uc2xpY2UuY2FsbCh0ZXh0VHJhY2tzKS5mb3JFYWNoKHRyYWNrID0+IHtcbiAgICAgIGlmICh0cmFjay5tb2RlICE9PSAnZGlzYWJsZWQnICYmIHRyYWNrICE9PSBuZXh0VHJhY2spIHtcbiAgICAgICAgdHJhY2subW9kZSA9ICdkaXNhYmxlZCc7XG4gICAgICB9XG4gICAgfSk7XG4gICAgaWYgKG5leHRUcmFjaykge1xuICAgICAgY29uc3QgbW9kZSA9IHRoaXMuc3VidGl0bGVEaXNwbGF5ID8gJ3Nob3dpbmcnIDogJ2hpZGRlbic7XG4gICAgICBpZiAobmV4dFRyYWNrLm1vZGUgIT09IG1vZGUpIHtcbiAgICAgICAgbmV4dFRyYWNrLm1vZGUgPSBtb2RlO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBUaGlzIG1ldGhvZCBpcyByZXNwb25zaWJsZSBmb3IgdmFsaWRhdGluZyB0aGUgc3VidGl0bGUgaW5kZXggYW5kIHBlcmlvZGljYWxseSByZWxvYWRpbmcgaWYgbGl2ZS5cbiAgICogRGlzcGF0Y2hlcyB0aGUgU1VCVElUTEVfVFJBQ0tfU1dJVENIIGV2ZW50LCB3aGljaCBpbnN0cnVjdHMgdGhlIHN1YnRpdGxlLXN0cmVhbS1jb250cm9sbGVyIHRvIGxvYWQgdGhlIHNlbGVjdGVkIHRyYWNrLlxuICAgKi9cbiAgc2V0U3VidGl0bGVUcmFjayhuZXdJZCkge1xuICAgIGNvbnN0IHRyYWNrcyA9IHRoaXMudHJhY2tzSW5Hcm91cDtcblxuICAgIC8vIHNldHRpbmcgdGhpcy5zdWJ0aXRsZVRyYWNrIHdpbGwgdHJpZ2dlciBpbnRlcm5hbCBsb2dpY1xuICAgIC8vIGlmIG1lZGlhIGhhcyBub3QgYmVlbiBhdHRhY2hlZCB5ZXQsIGl0IHdpbGwgZmFpbFxuICAgIC8vIHdlIGtlZXAgYSByZWZlcmVuY2UgdG8gdGhlIGRlZmF1bHQgdHJhY2sgaWRcbiAgICAvLyBhbmQgd2UnbGwgc2V0IHN1YnRpdGxlVHJhY2sgd2hlbiBvbk1lZGlhQXR0YWNoZWQgaXMgdHJpZ2dlcmVkXG4gICAgaWYgKCF0aGlzLm1lZGlhKSB7XG4gICAgICB0aGlzLnF1ZXVlZERlZmF1bHRUcmFjayA9IG5ld0lkO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIGV4aXQgaWYgdHJhY2sgaWQgYXMgYWxyZWFkeSBzZXQgb3IgaW52YWxpZFxuICAgIGlmIChuZXdJZCA8IC0xIHx8IG5ld0lkID49IHRyYWNrcy5sZW5ndGggfHwgIWlzRmluaXRlTnVtYmVyKG5ld0lkKSkge1xuICAgICAgdGhpcy53YXJuKGBJbnZhbGlkIHN1YnRpdGxlIHRyYWNrIGlkOiAke25ld0lkfWApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIHN0b3BwaW5nIGxpdmUgcmVsb2FkaW5nIHRpbWVyIGlmIGFueVxuICAgIHRoaXMuY2xlYXJUaW1lcigpO1xuICAgIHRoaXMuc2VsZWN0RGVmYXVsdFRyYWNrID0gZmFsc2U7XG4gICAgY29uc3QgbGFzdFRyYWNrID0gdGhpcy5jdXJyZW50VHJhY2s7XG4gICAgY29uc3QgdHJhY2sgPSB0cmFja3NbbmV3SWRdIHx8IG51bGw7XG4gICAgdGhpcy50cmFja0lkID0gbmV3SWQ7XG4gICAgdGhpcy5jdXJyZW50VHJhY2sgPSB0cmFjaztcbiAgICB0aGlzLnRvZ2dsZVRyYWNrTW9kZXMoKTtcbiAgICBpZiAoIXRyYWNrKSB7XG4gICAgICAvLyBzd2l0Y2ggdG8gLTFcbiAgICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLlNVQlRJVExFX1RSQUNLX1NXSVRDSCwge1xuICAgICAgICBpZDogbmV3SWRcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB0cmFja0xvYWRlZCA9ICEhdHJhY2suZGV0YWlscyAmJiAhdHJhY2suZGV0YWlscy5saXZlO1xuICAgIGlmIChuZXdJZCA9PT0gdGhpcy50cmFja0lkICYmIHRyYWNrID09PSBsYXN0VHJhY2sgJiYgdHJhY2tMb2FkZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5sb2coYFN3aXRjaGluZyB0byBzdWJ0aXRsZS10cmFjayAke25ld0lkfWAgKyAodHJhY2sgPyBgIFwiJHt0cmFjay5uYW1lfVwiIGxhbmc6JHt0cmFjay5sYW5nfSBncm91cDoke3RyYWNrLmdyb3VwSWR9YCA6ICcnKSk7XG4gICAgY29uc3Qge1xuICAgICAgaWQsXG4gICAgICBncm91cElkID0gJycsXG4gICAgICBuYW1lLFxuICAgICAgdHlwZSxcbiAgICAgIHVybFxuICAgIH0gPSB0cmFjaztcbiAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5TVUJUSVRMRV9UUkFDS19TV0lUQ0gsIHtcbiAgICAgIGlkLFxuICAgICAgZ3JvdXBJZCxcbiAgICAgIG5hbWUsXG4gICAgICB0eXBlLFxuICAgICAgdXJsXG4gICAgfSk7XG4gICAgY29uc3QgaGxzVXJsUGFyYW1ldGVycyA9IHRoaXMuc3dpdGNoUGFyYW1zKHRyYWNrLnVybCwgbGFzdFRyYWNrID09IG51bGwgPyB2b2lkIDAgOiBsYXN0VHJhY2suZGV0YWlscywgdHJhY2suZGV0YWlscyk7XG4gICAgdGhpcy5sb2FkUGxheWxpc3QoaGxzVXJsUGFyYW1ldGVycyk7XG4gIH1cbn1cblxuY2xhc3MgQnVmZmVyT3BlcmF0aW9uUXVldWUge1xuICBjb25zdHJ1Y3Rvcihzb3VyY2VCdWZmZXJSZWZlcmVuY2UpIHtcbiAgICB0aGlzLmJ1ZmZlcnMgPSB2b2lkIDA7XG4gICAgdGhpcy5xdWV1ZXMgPSB7XG4gICAgICB2aWRlbzogW10sXG4gICAgICBhdWRpbzogW10sXG4gICAgICBhdWRpb3ZpZGVvOiBbXVxuICAgIH07XG4gICAgdGhpcy5idWZmZXJzID0gc291cmNlQnVmZmVyUmVmZXJlbmNlO1xuICB9XG4gIGFwcGVuZChvcGVyYXRpb24sIHR5cGUsIHBlbmRpbmcpIHtcbiAgICBjb25zdCBxdWV1ZSA9IHRoaXMucXVldWVzW3R5cGVdO1xuICAgIHF1ZXVlLnB1c2gob3BlcmF0aW9uKTtcbiAgICBpZiAocXVldWUubGVuZ3RoID09PSAxICYmICFwZW5kaW5nKSB7XG4gICAgICB0aGlzLmV4ZWN1dGVOZXh0KHR5cGUpO1xuICAgIH1cbiAgfVxuICBpbnNlcnRBYm9ydChvcGVyYXRpb24sIHR5cGUpIHtcbiAgICBjb25zdCBxdWV1ZSA9IHRoaXMucXVldWVzW3R5cGVdO1xuICAgIHF1ZXVlLnVuc2hpZnQob3BlcmF0aW9uKTtcbiAgICB0aGlzLmV4ZWN1dGVOZXh0KHR5cGUpO1xuICB9XG4gIGFwcGVuZEJsb2NrZXIodHlwZSkge1xuICAgIGxldCBleGVjdXRlO1xuICAgIGNvbnN0IHByb21pc2UgPSBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHtcbiAgICAgIGV4ZWN1dGUgPSByZXNvbHZlO1xuICAgIH0pO1xuICAgIGNvbnN0IG9wZXJhdGlvbiA9IHtcbiAgICAgIGV4ZWN1dGUsXG4gICAgICBvblN0YXJ0OiAoKSA9PiB7fSxcbiAgICAgIG9uQ29tcGxldGU6ICgpID0+IHt9LFxuICAgICAgb25FcnJvcjogKCkgPT4ge31cbiAgICB9O1xuICAgIHRoaXMuYXBwZW5kKG9wZXJhdGlvbiwgdHlwZSk7XG4gICAgcmV0dXJuIHByb21pc2U7XG4gIH1cbiAgZXhlY3V0ZU5leHQodHlwZSkge1xuICAgIGNvbnN0IHF1ZXVlID0gdGhpcy5xdWV1ZXNbdHlwZV07XG4gICAgaWYgKHF1ZXVlLmxlbmd0aCkge1xuICAgICAgY29uc3Qgb3BlcmF0aW9uID0gcXVldWVbMF07XG4gICAgICB0cnkge1xuICAgICAgICAvLyBPcGVyYXRpb25zIGFyZSBleHBlY3RlZCB0byByZXN1bHQgaW4gYW4gJ3VwZGF0ZWVuZCcgZXZlbnQgYmVpbmcgZmlyZWQuIElmIG5vdCwgdGhlIHF1ZXVlIHdpbGwgbG9jay4gT3BlcmF0aW9uc1xuICAgICAgICAvLyB3aGljaCBkbyBub3QgZW5kIHdpdGggdGhpcyBldmVudCBtdXN0IGNhbGwgX29uU0JVcGRhdGVFbmQgbWFudWFsbHlcbiAgICAgICAgb3BlcmF0aW9uLmV4ZWN1dGUoKTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGxvZ2dlci53YXJuKGBbYnVmZmVyLW9wZXJhdGlvbi1xdWV1ZV06IEV4Y2VwdGlvbiBleGVjdXRpbmcgXCIke3R5cGV9XCIgU291cmNlQnVmZmVyIG9wZXJhdGlvbjogJHtlcnJvcn1gKTtcbiAgICAgICAgb3BlcmF0aW9uLm9uRXJyb3IoZXJyb3IpO1xuXG4gICAgICAgIC8vIE9ubHkgc2hpZnQgdGhlIGN1cnJlbnQgb3BlcmF0aW9uIG9mZiwgb3RoZXJ3aXNlIHRoZSB1cGRhdGVlbmQgaGFuZGxlciB3aWxsIGRvIHRoaXMgZm9yIHVzXG4gICAgICAgIGNvbnN0IHNiID0gdGhpcy5idWZmZXJzW3R5cGVdO1xuICAgICAgICBpZiAoIShzYiAhPSBudWxsICYmIHNiLnVwZGF0aW5nKSkge1xuICAgICAgICAgIHRoaXMuc2hpZnRBbmRFeGVjdXRlTmV4dCh0eXBlKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuICBzaGlmdEFuZEV4ZWN1dGVOZXh0KHR5cGUpIHtcbiAgICB0aGlzLnF1ZXVlc1t0eXBlXS5zaGlmdCgpO1xuICAgIHRoaXMuZXhlY3V0ZU5leHQodHlwZSk7XG4gIH1cbiAgY3VycmVudCh0eXBlKSB7XG4gICAgcmV0dXJuIHRoaXMucXVldWVzW3R5cGVdWzBdO1xuICB9XG59XG5cbmNvbnN0IFZJREVPX0NPREVDX1BST0ZJTEVfUkVQTEFDRSA9IC8oYXZjWzEyMzRdfGh2YzF8aGV2MXxkdmhbMWVdfHZwMDl8YXYwMSkoPzpcXC5bXi4sXSspKy87XG5jbGFzcyBCdWZmZXJDb250cm9sbGVyIHtcbiAgY29uc3RydWN0b3IoaGxzKSB7XG4gICAgLy8gVGhlIGxldmVsIGRldGFpbHMgdXNlZCB0byBkZXRlcm1pbmUgZHVyYXRpb24sIHRhcmdldC1kdXJhdGlvbiBhbmQgbGl2ZVxuICAgIHRoaXMuZGV0YWlscyA9IG51bGw7XG4gICAgLy8gY2FjaGUgdGhlIHNlbGYgZ2VuZXJhdGVkIG9iamVjdCB1cmwgdG8gZGV0ZWN0IGhpamFjayBvZiB2aWRlbyB0YWdcbiAgICB0aGlzLl9vYmplY3RVcmwgPSBudWxsO1xuICAgIC8vIEEgcXVldWUgb2YgYnVmZmVyIG9wZXJhdGlvbnMgd2hpY2ggcmVxdWlyZSB0aGUgU291cmNlQnVmZmVyIHRvIG5vdCBiZSB1cGRhdGluZyB1cG9uIGV4ZWN1dGlvblxuICAgIHRoaXMub3BlcmF0aW9uUXVldWUgPSB2b2lkIDA7XG4gICAgLy8gUmVmZXJlbmNlcyB0byBldmVudCBsaXN0ZW5lcnMgZm9yIGVhY2ggU291cmNlQnVmZmVyLCBzbyB0aGF0IHRoZXkgY2FuIGJlIHJlZmVyZW5jZWQgZm9yIGV2ZW50IHJlbW92YWxcbiAgICB0aGlzLmxpc3RlbmVycyA9IHZvaWQgMDtcbiAgICB0aGlzLmhscyA9IHZvaWQgMDtcbiAgICAvLyBUaGUgbnVtYmVyIG9mIEJVRkZFUl9DT0RFQyBldmVudHMgcmVjZWl2ZWQgYmVmb3JlIGFueSBzb3VyY2VCdWZmZXJzIGFyZSBjcmVhdGVkXG4gICAgdGhpcy5idWZmZXJDb2RlY0V2ZW50c0V4cGVjdGVkID0gMDtcbiAgICAvLyBUaGUgdG90YWwgbnVtYmVyIG9mIEJVRkZFUl9DT0RFQyBldmVudHMgcmVjZWl2ZWRcbiAgICB0aGlzLl9idWZmZXJDb2RlY0V2ZW50c1RvdGFsID0gMDtcbiAgICAvLyBBIHJlZmVyZW5jZSB0byB0aGUgYXR0YWNoZWQgbWVkaWEgZWxlbWVudFxuICAgIHRoaXMubWVkaWEgPSBudWxsO1xuICAgIC8vIEEgcmVmZXJlbmNlIHRvIHRoZSBhY3RpdmUgbWVkaWEgc291cmNlXG4gICAgdGhpcy5tZWRpYVNvdXJjZSA9IG51bGw7XG4gICAgLy8gTGFzdCBNUDMgYXVkaW8gY2h1bmsgYXBwZW5kZWRcbiAgICB0aGlzLmxhc3RNcGVnQXVkaW9DaHVuayA9IG51bGw7XG4gICAgdGhpcy5hcHBlbmRTb3VyY2UgPSB2b2lkIDA7XG4gICAgLy8gY291bnRlcnNcbiAgICB0aGlzLmFwcGVuZEVycm9ycyA9IHtcbiAgICAgIGF1ZGlvOiAwLFxuICAgICAgdmlkZW86IDAsXG4gICAgICBhdWRpb3ZpZGVvOiAwXG4gICAgfTtcbiAgICB0aGlzLnRyYWNrcyA9IHt9O1xuICAgIHRoaXMucGVuZGluZ1RyYWNrcyA9IHt9O1xuICAgIHRoaXMuc291cmNlQnVmZmVyID0gdm9pZCAwO1xuICAgIHRoaXMubG9nID0gdm9pZCAwO1xuICAgIHRoaXMud2FybiA9IHZvaWQgMDtcbiAgICB0aGlzLmVycm9yID0gdm9pZCAwO1xuICAgIHRoaXMuX29uRW5kU3RyZWFtaW5nID0gZXZlbnQgPT4ge1xuICAgICAgaWYgKCF0aGlzLmhscykge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aGlzLmhscy5wYXVzZUJ1ZmZlcmluZygpO1xuICAgIH07XG4gICAgdGhpcy5fb25TdGFydFN0cmVhbWluZyA9IGV2ZW50ID0+IHtcbiAgICAgIGlmICghdGhpcy5obHMpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdGhpcy5obHMucmVzdW1lQnVmZmVyaW5nKCk7XG4gICAgfTtcbiAgICAvLyBLZWVwIGFzIGFycm93IGZ1bmN0aW9ucyBzbyB0aGF0IHdlIGNhbiBkaXJlY3RseSByZWZlcmVuY2UgdGhlc2UgZnVuY3Rpb25zIGRpcmVjdGx5IGFzIGV2ZW50IGxpc3RlbmVyc1xuICAgIHRoaXMuX29uTWVkaWFTb3VyY2VPcGVuID0gKCkgPT4ge1xuICAgICAgY29uc3Qge1xuICAgICAgICBtZWRpYSxcbiAgICAgICAgbWVkaWFTb3VyY2VcbiAgICAgIH0gPSB0aGlzO1xuICAgICAgdGhpcy5sb2coJ01lZGlhIHNvdXJjZSBvcGVuZWQnKTtcbiAgICAgIGlmIChtZWRpYSkge1xuICAgICAgICBtZWRpYS5yZW1vdmVFdmVudExpc3RlbmVyKCdlbXB0aWVkJywgdGhpcy5fb25NZWRpYUVtcHRpZWQpO1xuICAgICAgICB0aGlzLnVwZGF0ZU1lZGlhRWxlbWVudER1cmF0aW9uKCk7XG4gICAgICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLk1FRElBX0FUVEFDSEVELCB7XG4gICAgICAgICAgbWVkaWEsXG4gICAgICAgICAgbWVkaWFTb3VyY2U6IG1lZGlhU291cmNlXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgICAgaWYgKG1lZGlhU291cmNlKSB7XG4gICAgICAgIC8vIG9uY2UgcmVjZWl2ZWQsIGRvbid0IGxpc3RlbiBhbnltb3JlIHRvIHNvdXJjZW9wZW4gZXZlbnRcbiAgICAgICAgbWVkaWFTb3VyY2UucmVtb3ZlRXZlbnRMaXN0ZW5lcignc291cmNlb3BlbicsIHRoaXMuX29uTWVkaWFTb3VyY2VPcGVuKTtcbiAgICAgIH1cbiAgICAgIHRoaXMuY2hlY2tQZW5kaW5nVHJhY2tzKCk7XG4gICAgfTtcbiAgICB0aGlzLl9vbk1lZGlhU291cmNlQ2xvc2UgPSAoKSA9PiB7XG4gICAgICB0aGlzLmxvZygnTWVkaWEgc291cmNlIGNsb3NlZCcpO1xuICAgIH07XG4gICAgdGhpcy5fb25NZWRpYVNvdXJjZUVuZGVkID0gKCkgPT4ge1xuICAgICAgdGhpcy5sb2coJ01lZGlhIHNvdXJjZSBlbmRlZCcpO1xuICAgIH07XG4gICAgdGhpcy5fb25NZWRpYUVtcHRpZWQgPSAoKSA9PiB7XG4gICAgICBjb25zdCB7XG4gICAgICAgIG1lZGlhU3JjLFxuICAgICAgICBfb2JqZWN0VXJsXG4gICAgICB9ID0gdGhpcztcbiAgICAgIGlmIChtZWRpYVNyYyAhPT0gX29iamVjdFVybCkge1xuICAgICAgICBsb2dnZXIuZXJyb3IoYE1lZGlhIGVsZW1lbnQgc3JjIHdhcyBzZXQgd2hpbGUgYXR0YWNoaW5nIE1lZGlhU291cmNlICgke19vYmplY3RVcmx9ID4gJHttZWRpYVNyY30pYCk7XG4gICAgICB9XG4gICAgfTtcbiAgICB0aGlzLmhscyA9IGhscztcbiAgICBjb25zdCBsb2dQcmVmaXggPSAnW2J1ZmZlci1jb250cm9sbGVyXSc7XG4gICAgdGhpcy5hcHBlbmRTb3VyY2UgPSBpc01hbmFnZWRNZWRpYVNvdXJjZShnZXRNZWRpYVNvdXJjZShobHMuY29uZmlnLnByZWZlck1hbmFnZWRNZWRpYVNvdXJjZSkpO1xuICAgIHRoaXMubG9nID0gbG9nZ2VyLmxvZy5iaW5kKGxvZ2dlciwgbG9nUHJlZml4KTtcbiAgICB0aGlzLndhcm4gPSBsb2dnZXIud2Fybi5iaW5kKGxvZ2dlciwgbG9nUHJlZml4KTtcbiAgICB0aGlzLmVycm9yID0gbG9nZ2VyLmVycm9yLmJpbmQobG9nZ2VyLCBsb2dQcmVmaXgpO1xuICAgIHRoaXMuX2luaXRTb3VyY2VCdWZmZXIoKTtcbiAgICB0aGlzLnJlZ2lzdGVyTGlzdGVuZXJzKCk7XG4gIH1cbiAgaGFzU291cmNlVHlwZXMoKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0U291cmNlQnVmZmVyVHlwZXMoKS5sZW5ndGggPiAwIHx8IE9iamVjdC5rZXlzKHRoaXMucGVuZGluZ1RyYWNrcykubGVuZ3RoID4gMDtcbiAgfVxuICBkZXN0cm95KCkge1xuICAgIHRoaXMudW5yZWdpc3Rlckxpc3RlbmVycygpO1xuICAgIHRoaXMuZGV0YWlscyA9IG51bGw7XG4gICAgdGhpcy5sYXN0TXBlZ0F1ZGlvQ2h1bmsgPSBudWxsO1xuICAgIC8vIEB0cy1pZ25vcmVcbiAgICB0aGlzLmhscyA9IG51bGw7XG4gIH1cbiAgcmVnaXN0ZXJMaXN0ZW5lcnMoKSB7XG4gICAgY29uc3Qge1xuICAgICAgaGxzXG4gICAgfSA9IHRoaXM7XG4gICAgaGxzLm9uKEV2ZW50cy5NRURJQV9BVFRBQ0hJTkcsIHRoaXMub25NZWRpYUF0dGFjaGluZywgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5NRURJQV9ERVRBQ0hJTkcsIHRoaXMub25NZWRpYURldGFjaGluZywgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5NQU5JRkVTVF9MT0FESU5HLCB0aGlzLm9uTWFuaWZlc3RMb2FkaW5nLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLk1BTklGRVNUX1BBUlNFRCwgdGhpcy5vbk1hbmlmZXN0UGFyc2VkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkJVRkZFUl9SRVNFVCwgdGhpcy5vbkJ1ZmZlclJlc2V0LCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkJVRkZFUl9BUFBFTkRJTkcsIHRoaXMub25CdWZmZXJBcHBlbmRpbmcsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuQlVGRkVSX0NPREVDUywgdGhpcy5vbkJ1ZmZlckNvZGVjcywgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5CVUZGRVJfRU9TLCB0aGlzLm9uQnVmZmVyRW9zLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkJVRkZFUl9GTFVTSElORywgdGhpcy5vbkJ1ZmZlckZsdXNoaW5nLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkxFVkVMX1VQREFURUQsIHRoaXMub25MZXZlbFVwZGF0ZWQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuRlJBR19QQVJTRUQsIHRoaXMub25GcmFnUGFyc2VkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkZSQUdfQ0hBTkdFRCwgdGhpcy5vbkZyYWdDaGFuZ2VkLCB0aGlzKTtcbiAgfVxuICB1bnJlZ2lzdGVyTGlzdGVuZXJzKCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGhsc1xuICAgIH0gPSB0aGlzO1xuICAgIGhscy5vZmYoRXZlbnRzLk1FRElBX0FUVEFDSElORywgdGhpcy5vbk1lZGlhQXR0YWNoaW5nLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5NRURJQV9ERVRBQ0hJTkcsIHRoaXMub25NZWRpYURldGFjaGluZywgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuTUFOSUZFU1RfTE9BRElORywgdGhpcy5vbk1hbmlmZXN0TG9hZGluZywgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuTUFOSUZFU1RfUEFSU0VELCB0aGlzLm9uTWFuaWZlc3RQYXJzZWQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkJVRkZFUl9SRVNFVCwgdGhpcy5vbkJ1ZmZlclJlc2V0LCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5CVUZGRVJfQVBQRU5ESU5HLCB0aGlzLm9uQnVmZmVyQXBwZW5kaW5nLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5CVUZGRVJfQ09ERUNTLCB0aGlzLm9uQnVmZmVyQ29kZWNzLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5CVUZGRVJfRU9TLCB0aGlzLm9uQnVmZmVyRW9zLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5CVUZGRVJfRkxVU0hJTkcsIHRoaXMub25CdWZmZXJGbHVzaGluZywgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuTEVWRUxfVVBEQVRFRCwgdGhpcy5vbkxldmVsVXBkYXRlZCwgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuRlJBR19QQVJTRUQsIHRoaXMub25GcmFnUGFyc2VkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5GUkFHX0NIQU5HRUQsIHRoaXMub25GcmFnQ2hhbmdlZCwgdGhpcyk7XG4gIH1cbiAgX2luaXRTb3VyY2VCdWZmZXIoKSB7XG4gICAgdGhpcy5zb3VyY2VCdWZmZXIgPSB7fTtcbiAgICB0aGlzLm9wZXJhdGlvblF1ZXVlID0gbmV3IEJ1ZmZlck9wZXJhdGlvblF1ZXVlKHRoaXMuc291cmNlQnVmZmVyKTtcbiAgICB0aGlzLmxpc3RlbmVycyA9IHtcbiAgICAgIGF1ZGlvOiBbXSxcbiAgICAgIHZpZGVvOiBbXSxcbiAgICAgIGF1ZGlvdmlkZW86IFtdXG4gICAgfTtcbiAgICB0aGlzLmFwcGVuZEVycm9ycyA9IHtcbiAgICAgIGF1ZGlvOiAwLFxuICAgICAgdmlkZW86IDAsXG4gICAgICBhdWRpb3ZpZGVvOiAwXG4gICAgfTtcbiAgICB0aGlzLmxhc3RNcGVnQXVkaW9DaHVuayA9IG51bGw7XG4gIH1cbiAgb25NYW5pZmVzdExvYWRpbmcoKSB7XG4gICAgdGhpcy5idWZmZXJDb2RlY0V2ZW50c0V4cGVjdGVkID0gdGhpcy5fYnVmZmVyQ29kZWNFdmVudHNUb3RhbCA9IDA7XG4gICAgdGhpcy5kZXRhaWxzID0gbnVsbDtcbiAgfVxuICBvbk1hbmlmZXN0UGFyc2VkKGV2ZW50LCBkYXRhKSB7XG4gICAgLy8gaW4gY2FzZSBvZiBhbHQgYXVkaW8gMiBCVUZGRVJfQ09ERUNTIGV2ZW50cyB3aWxsIGJlIHRyaWdnZXJlZCwgb25lIHBlciBzdHJlYW0gY29udHJvbGxlclxuICAgIC8vIHNvdXJjZWJ1ZmZlcnMgd2lsbCBiZSBjcmVhdGVkIGFsbCBhdCBvbmNlIHdoZW4gdGhlIGV4cGVjdGVkIG5iIG9mIHRyYWNrcyB3aWxsIGJlIHJlYWNoZWRcbiAgICAvLyBpbiBjYXNlIGFsdCBhdWRpbyBpcyBub3QgdXNlZCwgb25seSBvbmUgQlVGRkVSX0NPREVDIGV2ZW50IHdpbGwgYmUgZmlyZWQgZnJvbSBtYWluIHN0cmVhbSBjb250cm9sbGVyXG4gICAgLy8gaXQgd2lsbCBjb250YWluIHRoZSBleHBlY3RlZCBuYiBvZiBzb3VyY2UgYnVmZmVycywgbm8gbmVlZCB0byBjb21wdXRlIGl0XG4gICAgbGV0IGNvZGVjRXZlbnRzID0gMjtcbiAgICBpZiAoZGF0YS5hdWRpbyAmJiAhZGF0YS52aWRlbyB8fCAhZGF0YS5hbHRBdWRpbyB8fCAhdHJ1ZSkge1xuICAgICAgY29kZWNFdmVudHMgPSAxO1xuICAgIH1cbiAgICB0aGlzLmJ1ZmZlckNvZGVjRXZlbnRzRXhwZWN0ZWQgPSB0aGlzLl9idWZmZXJDb2RlY0V2ZW50c1RvdGFsID0gY29kZWNFdmVudHM7XG4gICAgdGhpcy5sb2coYCR7dGhpcy5idWZmZXJDb2RlY0V2ZW50c0V4cGVjdGVkfSBidWZmZXJDb2RlYyBldmVudChzKSBleHBlY3RlZGApO1xuICB9XG4gIG9uTWVkaWFBdHRhY2hpbmcoZXZlbnQsIGRhdGEpIHtcbiAgICBjb25zdCBtZWRpYSA9IHRoaXMubWVkaWEgPSBkYXRhLm1lZGlhO1xuICAgIGNvbnN0IE1lZGlhU291cmNlID0gZ2V0TWVkaWFTb3VyY2UodGhpcy5hcHBlbmRTb3VyY2UpO1xuICAgIGlmIChtZWRpYSAmJiBNZWRpYVNvdXJjZSkge1xuICAgICAgdmFyIF9tcyRjb25zdHJ1Y3RvcjtcbiAgICAgIGNvbnN0IG1zID0gdGhpcy5tZWRpYVNvdXJjZSA9IG5ldyBNZWRpYVNvdXJjZSgpO1xuICAgICAgdGhpcy5sb2coYGNyZWF0ZWQgbWVkaWEgc291cmNlOiAkeyhfbXMkY29uc3RydWN0b3IgPSBtcy5jb25zdHJ1Y3RvcikgPT0gbnVsbCA/IHZvaWQgMCA6IF9tcyRjb25zdHJ1Y3Rvci5uYW1lfWApO1xuICAgICAgLy8gTWVkaWFTb3VyY2UgbGlzdGVuZXJzIGFyZSBhcnJvdyBmdW5jdGlvbnMgd2l0aCBhIGxleGljYWwgc2NvcGUsIGFuZCBkbyBub3QgbmVlZCB0byBiZSBib3VuZFxuICAgICAgbXMuYWRkRXZlbnRMaXN0ZW5lcignc291cmNlb3BlbicsIHRoaXMuX29uTWVkaWFTb3VyY2VPcGVuKTtcbiAgICAgIG1zLmFkZEV2ZW50TGlzdGVuZXIoJ3NvdXJjZWVuZGVkJywgdGhpcy5fb25NZWRpYVNvdXJjZUVuZGVkKTtcbiAgICAgIG1zLmFkZEV2ZW50TGlzdGVuZXIoJ3NvdXJjZWNsb3NlJywgdGhpcy5fb25NZWRpYVNvdXJjZUNsb3NlKTtcbiAgICAgIGlmICh0aGlzLmFwcGVuZFNvdXJjZSkge1xuICAgICAgICBtcy5hZGRFdmVudExpc3RlbmVyKCdzdGFydHN0cmVhbWluZycsIHRoaXMuX29uU3RhcnRTdHJlYW1pbmcpO1xuICAgICAgICBtcy5hZGRFdmVudExpc3RlbmVyKCdlbmRzdHJlYW1pbmcnLCB0aGlzLl9vbkVuZFN0cmVhbWluZyk7XG4gICAgICB9XG5cbiAgICAgIC8vIGNhY2hlIHRoZSBsb2NhbGx5IGdlbmVyYXRlZCBvYmplY3QgdXJsXG4gICAgICBjb25zdCBvYmplY3RVcmwgPSB0aGlzLl9vYmplY3RVcmwgPSBzZWxmLlVSTC5jcmVhdGVPYmplY3RVUkwobXMpO1xuICAgICAgLy8gbGluayB2aWRlbyBhbmQgbWVkaWEgU291cmNlXG4gICAgICBpZiAodGhpcy5hcHBlbmRTb3VyY2UpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBtZWRpYS5yZW1vdmVBdHRyaWJ1dGUoJ3NyYycpO1xuICAgICAgICAgIC8vIE1hbmFnZWRNZWRpYVNvdXJjZSB3aWxsIG5vdCBvcGVuIHdpdGhvdXQgZGlzYWJsZVJlbW90ZVBsYXliYWNrIHNldCB0byBmYWxzZSBvciBzb3VyY2UgYWx0ZXJuYXRpdmVzXG4gICAgICAgICAgY29uc3QgTU1TID0gc2VsZi5NYW5hZ2VkTWVkaWFTb3VyY2U7XG4gICAgICAgICAgbWVkaWEuZGlzYWJsZVJlbW90ZVBsYXliYWNrID0gbWVkaWEuZGlzYWJsZVJlbW90ZVBsYXliYWNrIHx8IE1NUyAmJiBtcyBpbnN0YW5jZW9mIE1NUztcbiAgICAgICAgICByZW1vdmVTb3VyY2VDaGlsZHJlbihtZWRpYSk7XG4gICAgICAgICAgYWRkU291cmNlKG1lZGlhLCBvYmplY3RVcmwpO1xuICAgICAgICAgIG1lZGlhLmxvYWQoKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICBtZWRpYS5zcmMgPSBvYmplY3RVcmw7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIG1lZGlhLnNyYyA9IG9iamVjdFVybDtcbiAgICAgIH1cbiAgICAgIG1lZGlhLmFkZEV2ZW50TGlzdGVuZXIoJ2VtcHRpZWQnLCB0aGlzLl9vbk1lZGlhRW1wdGllZCk7XG4gICAgfVxuICB9XG4gIG9uTWVkaWFEZXRhY2hpbmcoKSB7XG4gICAgY29uc3Qge1xuICAgICAgbWVkaWEsXG4gICAgICBtZWRpYVNvdXJjZSxcbiAgICAgIF9vYmplY3RVcmxcbiAgICB9ID0gdGhpcztcbiAgICBpZiAobWVkaWFTb3VyY2UpIHtcbiAgICAgIHRoaXMubG9nKCdtZWRpYSBzb3VyY2UgZGV0YWNoaW5nJyk7XG4gICAgICBpZiAobWVkaWFTb3VyY2UucmVhZHlTdGF0ZSA9PT0gJ29wZW4nKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgLy8gZW5kT2ZTdHJlYW0gY291bGQgdHJpZ2dlciBleGNlcHRpb24gaWYgYW55IHNvdXJjZWJ1ZmZlciBpcyBpbiB1cGRhdGluZyBzdGF0ZVxuICAgICAgICAgIC8vIHdlIGRvbid0IHJlYWxseSBjYXJlIGFib3V0IGNoZWNraW5nIHNvdXJjZWJ1ZmZlciBzdGF0ZSBoZXJlLFxuICAgICAgICAgIC8vIGFzIHdlIGFyZSBhbnl3YXkgZGV0YWNoaW5nIHRoZSBNZWRpYVNvdXJjZVxuICAgICAgICAgIC8vIGxldCdzIGp1c3QgYXZvaWQgdGhpcyBleGNlcHRpb24gdG8gcHJvcGFnYXRlXG4gICAgICAgICAgbWVkaWFTb3VyY2UuZW5kT2ZTdHJlYW0oKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgdGhpcy53YXJuKGBvbk1lZGlhRGV0YWNoaW5nOiAke2Vyci5tZXNzYWdlfSB3aGlsZSBjYWxsaW5nIGVuZE9mU3RyZWFtYCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIC8vIENsZWFuIHVwIHRoZSBTb3VyY2VCdWZmZXJzIGJ5IGludm9raW5nIG9uQnVmZmVyUmVzZXRcbiAgICAgIHRoaXMub25CdWZmZXJSZXNldCgpO1xuICAgICAgbWVkaWFTb3VyY2UucmVtb3ZlRXZlbnRMaXN0ZW5lcignc291cmNlb3BlbicsIHRoaXMuX29uTWVkaWFTb3VyY2VPcGVuKTtcbiAgICAgIG1lZGlhU291cmNlLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3NvdXJjZWVuZGVkJywgdGhpcy5fb25NZWRpYVNvdXJjZUVuZGVkKTtcbiAgICAgIG1lZGlhU291cmNlLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3NvdXJjZWNsb3NlJywgdGhpcy5fb25NZWRpYVNvdXJjZUNsb3NlKTtcbiAgICAgIGlmICh0aGlzLmFwcGVuZFNvdXJjZSkge1xuICAgICAgICBtZWRpYVNvdXJjZS5yZW1vdmVFdmVudExpc3RlbmVyKCdzdGFydHN0cmVhbWluZycsIHRoaXMuX29uU3RhcnRTdHJlYW1pbmcpO1xuICAgICAgICBtZWRpYVNvdXJjZS5yZW1vdmVFdmVudExpc3RlbmVyKCdlbmRzdHJlYW1pbmcnLCB0aGlzLl9vbkVuZFN0cmVhbWluZyk7XG4gICAgICB9XG5cbiAgICAgIC8vIERldGFjaCBwcm9wZXJseSB0aGUgTWVkaWFTb3VyY2UgZnJvbSB0aGUgSFRNTE1lZGlhRWxlbWVudCBhc1xuICAgICAgLy8gc3VnZ2VzdGVkIGluIGh0dHBzOi8vZ2l0aHViLmNvbS93M2MvbWVkaWEtc291cmNlL2lzc3Vlcy81My5cbiAgICAgIGlmIChtZWRpYSkge1xuICAgICAgICBtZWRpYS5yZW1vdmVFdmVudExpc3RlbmVyKCdlbXB0aWVkJywgdGhpcy5fb25NZWRpYUVtcHRpZWQpO1xuICAgICAgICBpZiAoX29iamVjdFVybCkge1xuICAgICAgICAgIHNlbGYuVVJMLnJldm9rZU9iamVjdFVSTChfb2JqZWN0VXJsKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGNsZWFuIHVwIHZpZGVvIHRhZyBzcmMgb25seSBpZiBpdCdzIG91ciBvd24gdXJsLiBzb21lIGV4dGVybmFsIGxpYnJhcmllcyBtaWdodFxuICAgICAgICAvLyBoaWphY2sgdGhlIHZpZGVvIHRhZyBhbmQgY2hhbmdlIGl0cyAnc3JjJyB3aXRob3V0IGRlc3Ryb3lpbmcgdGhlIEhscyBpbnN0YW5jZSBmaXJzdFxuICAgICAgICBpZiAodGhpcy5tZWRpYVNyYyA9PT0gX29iamVjdFVybCkge1xuICAgICAgICAgIG1lZGlhLnJlbW92ZUF0dHJpYnV0ZSgnc3JjJyk7XG4gICAgICAgICAgaWYgKHRoaXMuYXBwZW5kU291cmNlKSB7XG4gICAgICAgICAgICByZW1vdmVTb3VyY2VDaGlsZHJlbihtZWRpYSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIG1lZGlhLmxvYWQoKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0aGlzLndhcm4oJ21lZGlhfHNvdXJjZS5zcmMgd2FzIGNoYW5nZWQgYnkgYSB0aGlyZCBwYXJ0eSAtIHNraXAgY2xlYW51cCcpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICB0aGlzLm1lZGlhU291cmNlID0gbnVsbDtcbiAgICAgIHRoaXMubWVkaWEgPSBudWxsO1xuICAgICAgdGhpcy5fb2JqZWN0VXJsID0gbnVsbDtcbiAgICAgIHRoaXMuYnVmZmVyQ29kZWNFdmVudHNFeHBlY3RlZCA9IHRoaXMuX2J1ZmZlckNvZGVjRXZlbnRzVG90YWw7XG4gICAgICB0aGlzLnBlbmRpbmdUcmFja3MgPSB7fTtcbiAgICAgIHRoaXMudHJhY2tzID0ge307XG4gICAgfVxuICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLk1FRElBX0RFVEFDSEVELCB1bmRlZmluZWQpO1xuICB9XG4gIG9uQnVmZmVyUmVzZXQoKSB7XG4gICAgdGhpcy5nZXRTb3VyY2VCdWZmZXJUeXBlcygpLmZvckVhY2godHlwZSA9PiB7XG4gICAgICB0aGlzLnJlc2V0QnVmZmVyKHR5cGUpO1xuICAgIH0pO1xuICAgIHRoaXMuX2luaXRTb3VyY2VCdWZmZXIoKTtcbiAgfVxuICByZXNldEJ1ZmZlcih0eXBlKSB7XG4gICAgY29uc3Qgc2IgPSB0aGlzLnNvdXJjZUJ1ZmZlclt0eXBlXTtcbiAgICB0cnkge1xuICAgICAgaWYgKHNiKSB7XG4gICAgICAgIHZhciBfdGhpcyRtZWRpYVNvdXJjZTtcbiAgICAgICAgdGhpcy5yZW1vdmVCdWZmZXJMaXN0ZW5lcnModHlwZSk7XG4gICAgICAgIC8vIFN5bmNocm9ub3VzbHkgcmVtb3ZlIHRoZSBTQiBmcm9tIHRoZSBtYXAgYmVmb3JlIHRoZSBuZXh0IGNhbGwgaW4gb3JkZXIgdG8gcHJldmVudCBhbiBhc3luYyBmdW5jdGlvbiBmcm9tXG4gICAgICAgIC8vIGFjY2Vzc2luZyBpdFxuICAgICAgICB0aGlzLnNvdXJjZUJ1ZmZlclt0eXBlXSA9IHVuZGVmaW5lZDtcbiAgICAgICAgaWYgKChfdGhpcyRtZWRpYVNvdXJjZSA9IHRoaXMubWVkaWFTb3VyY2UpICE9IG51bGwgJiYgX3RoaXMkbWVkaWFTb3VyY2Uuc291cmNlQnVmZmVycy5sZW5ndGgpIHtcbiAgICAgICAgICB0aGlzLm1lZGlhU291cmNlLnJlbW92ZVNvdXJjZUJ1ZmZlcihzYik7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIHRoaXMud2Fybihgb25CdWZmZXJSZXNldCAke3R5cGV9YCwgZXJyKTtcbiAgICB9XG4gIH1cbiAgb25CdWZmZXJDb2RlY3MoZXZlbnQsIGRhdGEpIHtcbiAgICBjb25zdCBzb3VyY2VCdWZmZXJDb3VudCA9IHRoaXMuZ2V0U291cmNlQnVmZmVyVHlwZXMoKS5sZW5ndGg7XG4gICAgY29uc3QgdHJhY2tOYW1lcyA9IE9iamVjdC5rZXlzKGRhdGEpO1xuICAgIHRyYWNrTmFtZXMuZm9yRWFjaCh0cmFja05hbWUgPT4ge1xuICAgICAgaWYgKHNvdXJjZUJ1ZmZlckNvdW50KSB7XG4gICAgICAgIC8vIGNoZWNrIGlmIFNvdXJjZUJ1ZmZlciBjb2RlYyBuZWVkcyB0byBjaGFuZ2VcbiAgICAgICAgY29uc3QgdHJhY2sgPSB0aGlzLnRyYWNrc1t0cmFja05hbWVdO1xuICAgICAgICBpZiAodHJhY2sgJiYgdHlwZW9mIHRyYWNrLmJ1ZmZlci5jaGFuZ2VUeXBlID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgdmFyIF90cmFja0NvZGVjO1xuICAgICAgICAgIGNvbnN0IHtcbiAgICAgICAgICAgIGlkLFxuICAgICAgICAgICAgY29kZWMsXG4gICAgICAgICAgICBsZXZlbENvZGVjLFxuICAgICAgICAgICAgY29udGFpbmVyLFxuICAgICAgICAgICAgbWV0YWRhdGFcbiAgICAgICAgICB9ID0gZGF0YVt0cmFja05hbWVdO1xuICAgICAgICAgIGNvbnN0IGN1cnJlbnRDb2RlY0Z1bGwgPSBwaWNrTW9zdENvbXBsZXRlQ29kZWNOYW1lKHRyYWNrLmNvZGVjLCB0cmFjay5sZXZlbENvZGVjKTtcbiAgICAgICAgICBjb25zdCBjdXJyZW50Q29kZWMgPSBjdXJyZW50Q29kZWNGdWxsID09IG51bGwgPyB2b2lkIDAgOiBjdXJyZW50Q29kZWNGdWxsLnJlcGxhY2UoVklERU9fQ09ERUNfUFJPRklMRV9SRVBMQUNFLCAnJDEnKTtcbiAgICAgICAgICBsZXQgdHJhY2tDb2RlYyA9IHBpY2tNb3N0Q29tcGxldGVDb2RlY05hbWUoY29kZWMsIGxldmVsQ29kZWMpO1xuICAgICAgICAgIGNvbnN0IG5leHRDb2RlYyA9IChfdHJhY2tDb2RlYyA9IHRyYWNrQ29kZWMpID09IG51bGwgPyB2b2lkIDAgOiBfdHJhY2tDb2RlYy5yZXBsYWNlKFZJREVPX0NPREVDX1BST0ZJTEVfUkVQTEFDRSwgJyQxJyk7XG4gICAgICAgICAgaWYgKHRyYWNrQ29kZWMgJiYgY3VycmVudENvZGVjICE9PSBuZXh0Q29kZWMpIHtcbiAgICAgICAgICAgIGlmICh0cmFja05hbWUuc2xpY2UoMCwgNSkgPT09ICdhdWRpbycpIHtcbiAgICAgICAgICAgICAgdHJhY2tDb2RlYyA9IGdldENvZGVjQ29tcGF0aWJsZU5hbWUodHJhY2tDb2RlYywgdGhpcy5hcHBlbmRTb3VyY2UpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc3QgbWltZVR5cGUgPSBgJHtjb250YWluZXJ9O2NvZGVjcz0ke3RyYWNrQ29kZWN9YDtcbiAgICAgICAgICAgIHRoaXMuYXBwZW5kQ2hhbmdlVHlwZSh0cmFja05hbWUsIG1pbWVUeXBlKTtcbiAgICAgICAgICAgIHRoaXMubG9nKGBzd2l0Y2hpbmcgY29kZWMgJHtjdXJyZW50Q29kZWNGdWxsfSB0byAke3RyYWNrQ29kZWN9YCk7XG4gICAgICAgICAgICB0aGlzLnRyYWNrc1t0cmFja05hbWVdID0ge1xuICAgICAgICAgICAgICBidWZmZXI6IHRyYWNrLmJ1ZmZlcixcbiAgICAgICAgICAgICAgY29kZWMsXG4gICAgICAgICAgICAgIGNvbnRhaW5lcixcbiAgICAgICAgICAgICAgbGV2ZWxDb2RlYyxcbiAgICAgICAgICAgICAgbWV0YWRhdGEsXG4gICAgICAgICAgICAgIGlkXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gaWYgc291cmNlIGJ1ZmZlcihzKSBub3QgY3JlYXRlZCB5ZXQsIGFwcGVuZGVkIGJ1ZmZlciB0cmFja3MgaW4gdGhpcy5wZW5kaW5nVHJhY2tzXG4gICAgICAgIHRoaXMucGVuZGluZ1RyYWNrc1t0cmFja05hbWVdID0gZGF0YVt0cmFja05hbWVdO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgLy8gaWYgc291cmNlYnVmZmVycyBhbHJlYWR5IGNyZWF0ZWQsIGRvIG5vdGhpbmcgLi4uXG4gICAgaWYgKHNvdXJjZUJ1ZmZlckNvdW50KSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGJ1ZmZlckNvZGVjRXZlbnRzRXhwZWN0ZWQgPSBNYXRoLm1heCh0aGlzLmJ1ZmZlckNvZGVjRXZlbnRzRXhwZWN0ZWQgLSAxLCAwKTtcbiAgICBpZiAodGhpcy5idWZmZXJDb2RlY0V2ZW50c0V4cGVjdGVkICE9PSBidWZmZXJDb2RlY0V2ZW50c0V4cGVjdGVkKSB7XG4gICAgICB0aGlzLmxvZyhgJHtidWZmZXJDb2RlY0V2ZW50c0V4cGVjdGVkfSBidWZmZXJDb2RlYyBldmVudChzKSBleHBlY3RlZCAke3RyYWNrTmFtZXMuam9pbignLCcpfWApO1xuICAgICAgdGhpcy5idWZmZXJDb2RlY0V2ZW50c0V4cGVjdGVkID0gYnVmZmVyQ29kZWNFdmVudHNFeHBlY3RlZDtcbiAgICB9XG4gICAgaWYgKHRoaXMubWVkaWFTb3VyY2UgJiYgdGhpcy5tZWRpYVNvdXJjZS5yZWFkeVN0YXRlID09PSAnb3BlbicpIHtcbiAgICAgIHRoaXMuY2hlY2tQZW5kaW5nVHJhY2tzKCk7XG4gICAgfVxuICB9XG4gIGFwcGVuZENoYW5nZVR5cGUodHlwZSwgbWltZVR5cGUpIHtcbiAgICBjb25zdCB7XG4gICAgICBvcGVyYXRpb25RdWV1ZVxuICAgIH0gPSB0aGlzO1xuICAgIGNvbnN0IG9wZXJhdGlvbiA9IHtcbiAgICAgIGV4ZWN1dGU6ICgpID0+IHtcbiAgICAgICAgY29uc3Qgc2IgPSB0aGlzLnNvdXJjZUJ1ZmZlclt0eXBlXTtcbiAgICAgICAgaWYgKHNiKSB7XG4gICAgICAgICAgdGhpcy5sb2coYGNoYW5naW5nICR7dHlwZX0gc291cmNlQnVmZmVyIHR5cGUgdG8gJHttaW1lVHlwZX1gKTtcbiAgICAgICAgICBzYi5jaGFuZ2VUeXBlKG1pbWVUeXBlKTtcbiAgICAgICAgfVxuICAgICAgICBvcGVyYXRpb25RdWV1ZS5zaGlmdEFuZEV4ZWN1dGVOZXh0KHR5cGUpO1xuICAgICAgfSxcbiAgICAgIG9uU3RhcnQ6ICgpID0+IHt9LFxuICAgICAgb25Db21wbGV0ZTogKCkgPT4ge30sXG4gICAgICBvbkVycm9yOiBlcnJvciA9PiB7XG4gICAgICAgIHRoaXMud2FybihgRmFpbGVkIHRvIGNoYW5nZSAke3R5cGV9IFNvdXJjZUJ1ZmZlciB0eXBlYCwgZXJyb3IpO1xuICAgICAgfVxuICAgIH07XG4gICAgb3BlcmF0aW9uUXVldWUuYXBwZW5kKG9wZXJhdGlvbiwgdHlwZSwgISF0aGlzLnBlbmRpbmdUcmFja3NbdHlwZV0pO1xuICB9XG4gIG9uQnVmZmVyQXBwZW5kaW5nKGV2ZW50LCBldmVudERhdGEpIHtcbiAgICBjb25zdCB7XG4gICAgICBobHMsXG4gICAgICBvcGVyYXRpb25RdWV1ZSxcbiAgICAgIHRyYWNrc1xuICAgIH0gPSB0aGlzO1xuICAgIGNvbnN0IHtcbiAgICAgIGRhdGEsXG4gICAgICB0eXBlLFxuICAgICAgZnJhZyxcbiAgICAgIHBhcnQsXG4gICAgICBjaHVua01ldGFcbiAgICB9ID0gZXZlbnREYXRhO1xuICAgIGNvbnN0IGNodW5rU3RhdHMgPSBjaHVua01ldGEuYnVmZmVyaW5nW3R5cGVdO1xuICAgIGNvbnN0IGJ1ZmZlckFwcGVuZGluZ1N0YXJ0ID0gc2VsZi5wZXJmb3JtYW5jZS5ub3coKTtcbiAgICBjaHVua1N0YXRzLnN0YXJ0ID0gYnVmZmVyQXBwZW5kaW5nU3RhcnQ7XG4gICAgY29uc3QgZnJhZ0J1ZmZlcmluZyA9IGZyYWcuc3RhdHMuYnVmZmVyaW5nO1xuICAgIGNvbnN0IHBhcnRCdWZmZXJpbmcgPSBwYXJ0ID8gcGFydC5zdGF0cy5idWZmZXJpbmcgOiBudWxsO1xuICAgIGlmIChmcmFnQnVmZmVyaW5nLnN0YXJ0ID09PSAwKSB7XG4gICAgICBmcmFnQnVmZmVyaW5nLnN0YXJ0ID0gYnVmZmVyQXBwZW5kaW5nU3RhcnQ7XG4gICAgfVxuICAgIGlmIChwYXJ0QnVmZmVyaW5nICYmIHBhcnRCdWZmZXJpbmcuc3RhcnQgPT09IDApIHtcbiAgICAgIHBhcnRCdWZmZXJpbmcuc3RhcnQgPSBidWZmZXJBcHBlbmRpbmdTdGFydDtcbiAgICB9XG5cbiAgICAvLyBUT0RPOiBPbmx5IHVwZGF0ZSB0aW1lc3RhbXBPZmZzZXQgd2hlbiBhdWRpby9tcGVnIGZyYWdtZW50IG9yIHBhcnQgaXMgbm90IGNvbnRpZ3VvdXMgd2l0aCBwcmV2aW91c2x5IGFwcGVuZGVkXG4gICAgLy8gQWRqdXN0aW5nIGBTb3VyY2VCdWZmZXIudGltZXN0YW1wT2Zmc2V0YCAoZGVzaXJlZCBwb2ludCBpbiB0aGUgdGltZWxpbmUgd2hlcmUgdGhlIG5leHQgZnJhbWVzIHNob3VsZCBiZSBhcHBlbmRlZClcbiAgICAvLyBpbiBDaHJvbWUgYnJvd3NlciB3aGVuIHdlIGRldGVjdCBNUEVHIGF1ZGlvIGNvbnRhaW5lciBhbmQgdGltZSBkZWx0YSBiZXR3ZWVuIGxldmVsIFBUUyBhbmQgYFNvdXJjZUJ1ZmZlci50aW1lc3RhbXBPZmZzZXRgXG4gICAgLy8gaXMgZ3JlYXRlciB0aGFuIDEwMG1zICh0aGlzIGlzIGVub3VnaCB0byBoYW5kbGUgc2VlayBmb3IgVk9EIG9yIGxldmVsIGNoYW5nZSBmb3IgTElWRSB2aWRlb3MpLlxuICAgIC8vIE1vcmUgaW5mbyBoZXJlOiBodHRwczovL2dpdGh1Yi5jb20vdmlkZW8tZGV2L2hscy5qcy9pc3N1ZXMvMzMyI2lzc3VlY29tbWVudC0yNTc5ODY0ODZcbiAgICBjb25zdCBhdWRpb1RyYWNrID0gdHJhY2tzLmF1ZGlvO1xuICAgIGxldCBjaGVja1RpbWVzdGFtcE9mZnNldCA9IGZhbHNlO1xuICAgIGlmICh0eXBlID09PSAnYXVkaW8nICYmIChhdWRpb1RyYWNrID09IG51bGwgPyB2b2lkIDAgOiBhdWRpb1RyYWNrLmNvbnRhaW5lcikgPT09ICdhdWRpby9tcGVnJykge1xuICAgICAgY2hlY2tUaW1lc3RhbXBPZmZzZXQgPSAhdGhpcy5sYXN0TXBlZ0F1ZGlvQ2h1bmsgfHwgY2h1bmtNZXRhLmlkID09PSAxIHx8IHRoaXMubGFzdE1wZWdBdWRpb0NodW5rLnNuICE9PSBjaHVua01ldGEuc247XG4gICAgICB0aGlzLmxhc3RNcGVnQXVkaW9DaHVuayA9IGNodW5rTWV0YTtcbiAgICB9XG4gICAgY29uc3QgZnJhZ1N0YXJ0ID0gZnJhZy5zdGFydDtcbiAgICBjb25zdCBvcGVyYXRpb24gPSB7XG4gICAgICBleGVjdXRlOiAoKSA9PiB7XG4gICAgICAgIGNodW5rU3RhdHMuZXhlY3V0ZVN0YXJ0ID0gc2VsZi5wZXJmb3JtYW5jZS5ub3coKTtcbiAgICAgICAgaWYgKGNoZWNrVGltZXN0YW1wT2Zmc2V0KSB7XG4gICAgICAgICAgY29uc3Qgc2IgPSB0aGlzLnNvdXJjZUJ1ZmZlclt0eXBlXTtcbiAgICAgICAgICBpZiAoc2IpIHtcbiAgICAgICAgICAgIGNvbnN0IGRlbHRhID0gZnJhZ1N0YXJ0IC0gc2IudGltZXN0YW1wT2Zmc2V0O1xuICAgICAgICAgICAgaWYgKE1hdGguYWJzKGRlbHRhKSA+PSAwLjEpIHtcbiAgICAgICAgICAgICAgdGhpcy5sb2coYFVwZGF0aW5nIGF1ZGlvIFNvdXJjZUJ1ZmZlciB0aW1lc3RhbXBPZmZzZXQgdG8gJHtmcmFnU3RhcnR9IChkZWx0YTogJHtkZWx0YX0pIHNuOiAke2ZyYWcuc259KWApO1xuICAgICAgICAgICAgICBzYi50aW1lc3RhbXBPZmZzZXQgPSBmcmFnU3RhcnQ7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHRoaXMuYXBwZW5kRXhlY3V0b3IoZGF0YSwgdHlwZSk7XG4gICAgICB9LFxuICAgICAgb25TdGFydDogKCkgPT4ge1xuICAgICAgICAvLyBsb2dnZXIuZGVidWcoYFtidWZmZXItY29udHJvbGxlcl06ICR7dHlwZX0gU291cmNlQnVmZmVyIHVwZGF0ZXN0YXJ0YCk7XG4gICAgICB9LFxuICAgICAgb25Db21wbGV0ZTogKCkgPT4ge1xuICAgICAgICAvLyBsb2dnZXIuZGVidWcoYFtidWZmZXItY29udHJvbGxlcl06ICR7dHlwZX0gU291cmNlQnVmZmVyIHVwZGF0ZWVuZGApO1xuICAgICAgICBjb25zdCBlbmQgPSBzZWxmLnBlcmZvcm1hbmNlLm5vdygpO1xuICAgICAgICBjaHVua1N0YXRzLmV4ZWN1dGVFbmQgPSBjaHVua1N0YXRzLmVuZCA9IGVuZDtcbiAgICAgICAgaWYgKGZyYWdCdWZmZXJpbmcuZmlyc3QgPT09IDApIHtcbiAgICAgICAgICBmcmFnQnVmZmVyaW5nLmZpcnN0ID0gZW5kO1xuICAgICAgICB9XG4gICAgICAgIGlmIChwYXJ0QnVmZmVyaW5nICYmIHBhcnRCdWZmZXJpbmcuZmlyc3QgPT09IDApIHtcbiAgICAgICAgICBwYXJ0QnVmZmVyaW5nLmZpcnN0ID0gZW5kO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHtcbiAgICAgICAgICBzb3VyY2VCdWZmZXJcbiAgICAgICAgfSA9IHRoaXM7XG4gICAgICAgIGNvbnN0IHRpbWVSYW5nZXMgPSB7fTtcbiAgICAgICAgZm9yIChjb25zdCB0eXBlIGluIHNvdXJjZUJ1ZmZlcikge1xuICAgICAgICAgIHRpbWVSYW5nZXNbdHlwZV0gPSBCdWZmZXJIZWxwZXIuZ2V0QnVmZmVyZWQoc291cmNlQnVmZmVyW3R5cGVdKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmFwcGVuZEVycm9yc1t0eXBlXSA9IDA7XG4gICAgICAgIGlmICh0eXBlID09PSAnYXVkaW8nIHx8IHR5cGUgPT09ICd2aWRlbycpIHtcbiAgICAgICAgICB0aGlzLmFwcGVuZEVycm9ycy5hdWRpb3ZpZGVvID0gMDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0aGlzLmFwcGVuZEVycm9ycy5hdWRpbyA9IDA7XG4gICAgICAgICAgdGhpcy5hcHBlbmRFcnJvcnMudmlkZW8gPSAwO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLkJVRkZFUl9BUFBFTkRFRCwge1xuICAgICAgICAgIHR5cGUsXG4gICAgICAgICAgZnJhZyxcbiAgICAgICAgICBwYXJ0LFxuICAgICAgICAgIGNodW5rTWV0YSxcbiAgICAgICAgICBwYXJlbnQ6IGZyYWcudHlwZSxcbiAgICAgICAgICB0aW1lUmFuZ2VzXG4gICAgICAgIH0pO1xuICAgICAgfSxcbiAgICAgIG9uRXJyb3I6IGVycm9yID0+IHtcbiAgICAgICAgLy8gaW4gY2FzZSBhbnkgZXJyb3Igb2NjdXJlZCB3aGlsZSBhcHBlbmRpbmcsIHB1dCBiYWNrIHNlZ21lbnQgaW4gc2VnbWVudHMgdGFibGVcbiAgICAgICAgY29uc3QgZXZlbnQgPSB7XG4gICAgICAgICAgdHlwZTogRXJyb3JUeXBlcy5NRURJQV9FUlJPUixcbiAgICAgICAgICBwYXJlbnQ6IGZyYWcudHlwZSxcbiAgICAgICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuQlVGRkVSX0FQUEVORF9FUlJPUixcbiAgICAgICAgICBzb3VyY2VCdWZmZXJOYW1lOiB0eXBlLFxuICAgICAgICAgIGZyYWcsXG4gICAgICAgICAgcGFydCxcbiAgICAgICAgICBjaHVua01ldGEsXG4gICAgICAgICAgZXJyb3IsXG4gICAgICAgICAgZXJyOiBlcnJvcixcbiAgICAgICAgICBmYXRhbDogZmFsc2VcbiAgICAgICAgfTtcbiAgICAgICAgaWYgKGVycm9yLmNvZGUgPT09IERPTUV4Y2VwdGlvbi5RVU9UQV9FWENFRURFRF9FUlIpIHtcbiAgICAgICAgICAvLyBRdW90YUV4Y2VlZGVkRXJyb3I6IGh0dHA6Ly93d3cudzMub3JnL1RSL2h0bWw1L2luZnJhc3RydWN0dXJlLmh0bWwjcXVvdGFleGNlZWRlZGVycm9yXG4gICAgICAgICAgLy8gbGV0J3Mgc3RvcCBhcHBlbmRpbmcgYW55IHNlZ21lbnRzLCBhbmQgcmVwb3J0IEJVRkZFUl9GVUxMX0VSUk9SIGVycm9yXG4gICAgICAgICAgZXZlbnQuZGV0YWlscyA9IEVycm9yRGV0YWlscy5CVUZGRVJfRlVMTF9FUlJPUjtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjb25zdCBhcHBlbmRFcnJvckNvdW50ID0gKyt0aGlzLmFwcGVuZEVycm9yc1t0eXBlXTtcbiAgICAgICAgICBldmVudC5kZXRhaWxzID0gRXJyb3JEZXRhaWxzLkJVRkZFUl9BUFBFTkRfRVJST1I7XG4gICAgICAgICAgLyogd2l0aCBVSEQgY29udGVudCwgd2UgY291bGQgZ2V0IGxvb3Agb2YgcXVvdGEgZXhjZWVkZWQgZXJyb3IgdW50aWxcbiAgICAgICAgICAgIGJyb3dzZXIgaXMgYWJsZSB0byBldmljdCBzb21lIGRhdGEgZnJvbSBzb3VyY2VidWZmZXIuIFJldHJ5aW5nIGNhbiBoZWxwIHJlY292ZXIuXG4gICAgICAgICAgKi9cbiAgICAgICAgICB0aGlzLndhcm4oYEZhaWxlZCAke2FwcGVuZEVycm9yQ291bnR9LyR7aGxzLmNvbmZpZy5hcHBlbmRFcnJvck1heFJldHJ5fSB0aW1lcyB0byBhcHBlbmQgc2VnbWVudCBpbiBcIiR7dHlwZX1cIiBzb3VyY2VCdWZmZXJgKTtcbiAgICAgICAgICBpZiAoYXBwZW5kRXJyb3JDb3VudCA+PSBobHMuY29uZmlnLmFwcGVuZEVycm9yTWF4UmV0cnkpIHtcbiAgICAgICAgICAgIGV2ZW50LmZhdGFsID0gdHJ1ZTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaGxzLnRyaWdnZXIoRXZlbnRzLkVSUk9SLCBldmVudCk7XG4gICAgICB9XG4gICAgfTtcbiAgICBvcGVyYXRpb25RdWV1ZS5hcHBlbmQob3BlcmF0aW9uLCB0eXBlLCAhIXRoaXMucGVuZGluZ1RyYWNrc1t0eXBlXSk7XG4gIH1cbiAgb25CdWZmZXJGbHVzaGluZyhldmVudCwgZGF0YSkge1xuICAgIGNvbnN0IHtcbiAgICAgIG9wZXJhdGlvblF1ZXVlXG4gICAgfSA9IHRoaXM7XG4gICAgY29uc3QgZmx1c2hPcGVyYXRpb24gPSB0eXBlID0+ICh7XG4gICAgICBleGVjdXRlOiB0aGlzLnJlbW92ZUV4ZWN1dG9yLmJpbmQodGhpcywgdHlwZSwgZGF0YS5zdGFydE9mZnNldCwgZGF0YS5lbmRPZmZzZXQpLFxuICAgICAgb25TdGFydDogKCkgPT4ge1xuICAgICAgICAvLyBsb2dnZXIuZGVidWcoYFtidWZmZXItY29udHJvbGxlcl06IFN0YXJ0ZWQgZmx1c2hpbmcgJHtkYXRhLnN0YXJ0T2Zmc2V0fSAtPiAke2RhdGEuZW5kT2Zmc2V0fSBmb3IgJHt0eXBlfSBTb3VyY2UgQnVmZmVyYCk7XG4gICAgICB9LFxuICAgICAgb25Db21wbGV0ZTogKCkgPT4ge1xuICAgICAgICAvLyBsb2dnZXIuZGVidWcoYFtidWZmZXItY29udHJvbGxlcl06IEZpbmlzaGVkIGZsdXNoaW5nICR7ZGF0YS5zdGFydE9mZnNldH0gLT4gJHtkYXRhLmVuZE9mZnNldH0gZm9yICR7dHlwZX0gU291cmNlIEJ1ZmZlcmApO1xuICAgICAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5CVUZGRVJfRkxVU0hFRCwge1xuICAgICAgICAgIHR5cGVcbiAgICAgICAgfSk7XG4gICAgICB9LFxuICAgICAgb25FcnJvcjogZXJyb3IgPT4ge1xuICAgICAgICB0aGlzLndhcm4oYEZhaWxlZCB0byByZW1vdmUgZnJvbSAke3R5cGV9IFNvdXJjZUJ1ZmZlcmAsIGVycm9yKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICBpZiAoZGF0YS50eXBlKSB7XG4gICAgICBvcGVyYXRpb25RdWV1ZS5hcHBlbmQoZmx1c2hPcGVyYXRpb24oZGF0YS50eXBlKSwgZGF0YS50eXBlKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5nZXRTb3VyY2VCdWZmZXJUeXBlcygpLmZvckVhY2godHlwZSA9PiB7XG4gICAgICAgIG9wZXJhdGlvblF1ZXVlLmFwcGVuZChmbHVzaE9wZXJhdGlvbih0eXBlKSwgdHlwZSk7XG4gICAgICB9KTtcbiAgICB9XG4gIH1cbiAgb25GcmFnUGFyc2VkKGV2ZW50LCBkYXRhKSB7XG4gICAgY29uc3Qge1xuICAgICAgZnJhZyxcbiAgICAgIHBhcnRcbiAgICB9ID0gZGF0YTtcbiAgICBjb25zdCBidWZmZXJzQXBwZW5kZWRUbyA9IFtdO1xuICAgIGNvbnN0IGVsZW1lbnRhcnlTdHJlYW1zID0gcGFydCA/IHBhcnQuZWxlbWVudGFyeVN0cmVhbXMgOiBmcmFnLmVsZW1lbnRhcnlTdHJlYW1zO1xuICAgIGlmIChlbGVtZW50YXJ5U3RyZWFtc1tFbGVtZW50YXJ5U3RyZWFtVHlwZXMuQVVESU9WSURFT10pIHtcbiAgICAgIGJ1ZmZlcnNBcHBlbmRlZFRvLnB1c2goJ2F1ZGlvdmlkZW8nKTtcbiAgICB9IGVsc2Uge1xuICAgICAgaWYgKGVsZW1lbnRhcnlTdHJlYW1zW0VsZW1lbnRhcnlTdHJlYW1UeXBlcy5BVURJT10pIHtcbiAgICAgICAgYnVmZmVyc0FwcGVuZGVkVG8ucHVzaCgnYXVkaW8nKTtcbiAgICAgIH1cbiAgICAgIGlmIChlbGVtZW50YXJ5U3RyZWFtc1tFbGVtZW50YXJ5U3RyZWFtVHlwZXMuVklERU9dKSB7XG4gICAgICAgIGJ1ZmZlcnNBcHBlbmRlZFRvLnB1c2goJ3ZpZGVvJyk7XG4gICAgICB9XG4gICAgfVxuICAgIGNvbnN0IG9uVW5ibG9ja2VkID0gKCkgPT4ge1xuICAgICAgY29uc3Qgbm93ID0gc2VsZi5wZXJmb3JtYW5jZS5ub3coKTtcbiAgICAgIGZyYWcuc3RhdHMuYnVmZmVyaW5nLmVuZCA9IG5vdztcbiAgICAgIGlmIChwYXJ0KSB7XG4gICAgICAgIHBhcnQuc3RhdHMuYnVmZmVyaW5nLmVuZCA9IG5vdztcbiAgICAgIH1cbiAgICAgIGNvbnN0IHN0YXRzID0gcGFydCA/IHBhcnQuc3RhdHMgOiBmcmFnLnN0YXRzO1xuICAgICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuRlJBR19CVUZGRVJFRCwge1xuICAgICAgICBmcmFnLFxuICAgICAgICBwYXJ0LFxuICAgICAgICBzdGF0cyxcbiAgICAgICAgaWQ6IGZyYWcudHlwZVxuICAgICAgfSk7XG4gICAgfTtcbiAgICBpZiAoYnVmZmVyc0FwcGVuZGVkVG8ubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aGlzLndhcm4oYEZyYWdtZW50cyBtdXN0IGhhdmUgYXQgbGVhc3Qgb25lIEVsZW1lbnRhcnlTdHJlYW1UeXBlIHNldC4gdHlwZTogJHtmcmFnLnR5cGV9IGxldmVsOiAke2ZyYWcubGV2ZWx9IHNuOiAke2ZyYWcuc259YCk7XG4gICAgfVxuICAgIHRoaXMuYmxvY2tCdWZmZXJzKG9uVW5ibG9ja2VkLCBidWZmZXJzQXBwZW5kZWRUbyk7XG4gIH1cbiAgb25GcmFnQ2hhbmdlZChldmVudCwgZGF0YSkge1xuICAgIHRoaXMudHJpbUJ1ZmZlcnMoKTtcbiAgfVxuXG4gIC8vIG9uIEJVRkZFUl9FT1MgbWFyayBtYXRjaGluZyBzb3VyY2VidWZmZXIocykgYXMgZW5kZWQgYW5kIHRyaWdnZXIgY2hlY2tFb3MoKVxuICAvLyBhbiB1bmRlZmluZWQgZGF0YS50eXBlIHdpbGwgbWFyayBhbGwgYnVmZmVycyBhcyBFT1MuXG4gIG9uQnVmZmVyRW9zKGV2ZW50LCBkYXRhKSB7XG4gICAgY29uc3QgZW5kZWQgPSB0aGlzLmdldFNvdXJjZUJ1ZmZlclR5cGVzKCkucmVkdWNlKChhY2MsIHR5cGUpID0+IHtcbiAgICAgIGNvbnN0IHNiID0gdGhpcy5zb3VyY2VCdWZmZXJbdHlwZV07XG4gICAgICBpZiAoc2IgJiYgKCFkYXRhLnR5cGUgfHwgZGF0YS50eXBlID09PSB0eXBlKSkge1xuICAgICAgICBzYi5lbmRpbmcgPSB0cnVlO1xuICAgICAgICBpZiAoIXNiLmVuZGVkKSB7XG4gICAgICAgICAgc2IuZW5kZWQgPSB0cnVlO1xuICAgICAgICAgIHRoaXMubG9nKGAke3R5cGV9IHNvdXJjZUJ1ZmZlciBub3cgRU9TYCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBhY2MgJiYgISEoIXNiIHx8IHNiLmVuZGVkKTtcbiAgICB9LCB0cnVlKTtcbiAgICBpZiAoZW5kZWQpIHtcbiAgICAgIHRoaXMubG9nKGBRdWV1ZWluZyBtZWRpYVNvdXJjZS5lbmRPZlN0cmVhbSgpYCk7XG4gICAgICB0aGlzLmJsb2NrQnVmZmVycygoKSA9PiB7XG4gICAgICAgIHRoaXMuZ2V0U291cmNlQnVmZmVyVHlwZXMoKS5mb3JFYWNoKHR5cGUgPT4ge1xuICAgICAgICAgIGNvbnN0IHNiID0gdGhpcy5zb3VyY2VCdWZmZXJbdHlwZV07XG4gICAgICAgICAgaWYgKHNiKSB7XG4gICAgICAgICAgICBzYi5lbmRpbmcgPSBmYWxzZTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICBjb25zdCB7XG4gICAgICAgICAgbWVkaWFTb3VyY2VcbiAgICAgICAgfSA9IHRoaXM7XG4gICAgICAgIGlmICghbWVkaWFTb3VyY2UgfHwgbWVkaWFTb3VyY2UucmVhZHlTdGF0ZSAhPT0gJ29wZW4nKSB7XG4gICAgICAgICAgaWYgKG1lZGlhU291cmNlKSB7XG4gICAgICAgICAgICB0aGlzLmxvZyhgQ291bGQgbm90IGNhbGwgbWVkaWFTb3VyY2UuZW5kT2ZTdHJlYW0oKS4gbWVkaWFTb3VyY2UucmVhZHlTdGF0ZTogJHttZWRpYVNvdXJjZS5yZWFkeVN0YXRlfWApO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5sb2coYENhbGxpbmcgbWVkaWFTb3VyY2UuZW5kT2ZTdHJlYW0oKWApO1xuICAgICAgICAvLyBBbGxvdyB0aGlzIHRvIHRocm93IGFuZCBiZSBjYXVnaHQgYnkgdGhlIGVucXVldWVpbmcgZnVuY3Rpb25cbiAgICAgICAgbWVkaWFTb3VyY2UuZW5kT2ZTdHJlYW0oKTtcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuICBvbkxldmVsVXBkYXRlZChldmVudCwge1xuICAgIGRldGFpbHNcbiAgfSkge1xuICAgIGlmICghZGV0YWlscy5mcmFnbWVudHMubGVuZ3RoKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuZGV0YWlscyA9IGRldGFpbHM7XG4gICAgaWYgKHRoaXMuZ2V0U291cmNlQnVmZmVyVHlwZXMoKS5sZW5ndGgpIHtcbiAgICAgIHRoaXMuYmxvY2tCdWZmZXJzKHRoaXMudXBkYXRlTWVkaWFFbGVtZW50RHVyYXRpb24uYmluZCh0aGlzKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMudXBkYXRlTWVkaWFFbGVtZW50RHVyYXRpb24oKTtcbiAgICB9XG4gIH1cbiAgdHJpbUJ1ZmZlcnMoKSB7XG4gICAgY29uc3Qge1xuICAgICAgaGxzLFxuICAgICAgZGV0YWlscyxcbiAgICAgIG1lZGlhXG4gICAgfSA9IHRoaXM7XG4gICAgaWYgKCFtZWRpYSB8fCBkZXRhaWxzID09PSBudWxsKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHNvdXJjZUJ1ZmZlclR5cGVzID0gdGhpcy5nZXRTb3VyY2VCdWZmZXJUeXBlcygpO1xuICAgIGlmICghc291cmNlQnVmZmVyVHlwZXMubGVuZ3RoKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGNvbmZpZyA9IGhscy5jb25maWc7XG4gICAgY29uc3QgY3VycmVudFRpbWUgPSBtZWRpYS5jdXJyZW50VGltZTtcbiAgICBjb25zdCB0YXJnZXREdXJhdGlvbiA9IGRldGFpbHMubGV2ZWxUYXJnZXREdXJhdGlvbjtcblxuICAgIC8vIFN1cHBvcnQgZm9yIGRlcHJlY2F0ZWQgbGl2ZUJhY2tCdWZmZXJMZW5ndGhcbiAgICBjb25zdCBiYWNrQnVmZmVyTGVuZ3RoID0gZGV0YWlscy5saXZlICYmIGNvbmZpZy5saXZlQmFja0J1ZmZlckxlbmd0aCAhPT0gbnVsbCA/IGNvbmZpZy5saXZlQmFja0J1ZmZlckxlbmd0aCA6IGNvbmZpZy5iYWNrQnVmZmVyTGVuZ3RoO1xuICAgIGlmIChpc0Zpbml0ZU51bWJlcihiYWNrQnVmZmVyTGVuZ3RoKSAmJiBiYWNrQnVmZmVyTGVuZ3RoID4gMCkge1xuICAgICAgY29uc3QgbWF4QmFja0J1ZmZlckxlbmd0aCA9IE1hdGgubWF4KGJhY2tCdWZmZXJMZW5ndGgsIHRhcmdldER1cmF0aW9uKTtcbiAgICAgIGNvbnN0IHRhcmdldEJhY2tCdWZmZXJQb3NpdGlvbiA9IE1hdGguZmxvb3IoY3VycmVudFRpbWUgLyB0YXJnZXREdXJhdGlvbikgKiB0YXJnZXREdXJhdGlvbiAtIG1heEJhY2tCdWZmZXJMZW5ndGg7XG4gICAgICB0aGlzLmZsdXNoQmFja0J1ZmZlcihjdXJyZW50VGltZSwgdGFyZ2V0RHVyYXRpb24sIHRhcmdldEJhY2tCdWZmZXJQb3NpdGlvbik7XG4gICAgfVxuICAgIGlmIChpc0Zpbml0ZU51bWJlcihjb25maWcuZnJvbnRCdWZmZXJGbHVzaFRocmVzaG9sZCkgJiYgY29uZmlnLmZyb250QnVmZmVyRmx1c2hUaHJlc2hvbGQgPiAwKSB7XG4gICAgICBjb25zdCBmcm9udEJ1ZmZlckxlbmd0aCA9IE1hdGgubWF4KGNvbmZpZy5tYXhCdWZmZXJMZW5ndGgsIGNvbmZpZy5mcm9udEJ1ZmZlckZsdXNoVGhyZXNob2xkKTtcbiAgICAgIGNvbnN0IG1heEZyb250QnVmZmVyTGVuZ3RoID0gTWF0aC5tYXgoZnJvbnRCdWZmZXJMZW5ndGgsIHRhcmdldER1cmF0aW9uKTtcbiAgICAgIGNvbnN0IHRhcmdldEZyb250QnVmZmVyUG9zaXRpb24gPSBNYXRoLmZsb29yKGN1cnJlbnRUaW1lIC8gdGFyZ2V0RHVyYXRpb24pICogdGFyZ2V0RHVyYXRpb24gKyBtYXhGcm9udEJ1ZmZlckxlbmd0aDtcbiAgICAgIHRoaXMuZmx1c2hGcm9udEJ1ZmZlcihjdXJyZW50VGltZSwgdGFyZ2V0RHVyYXRpb24sIHRhcmdldEZyb250QnVmZmVyUG9zaXRpb24pO1xuICAgIH1cbiAgfVxuICBmbHVzaEJhY2tCdWZmZXIoY3VycmVudFRpbWUsIHRhcmdldER1cmF0aW9uLCB0YXJnZXRCYWNrQnVmZmVyUG9zaXRpb24pIHtcbiAgICBjb25zdCB7XG4gICAgICBkZXRhaWxzLFxuICAgICAgc291cmNlQnVmZmVyXG4gICAgfSA9IHRoaXM7XG4gICAgY29uc3Qgc291cmNlQnVmZmVyVHlwZXMgPSB0aGlzLmdldFNvdXJjZUJ1ZmZlclR5cGVzKCk7XG4gICAgc291cmNlQnVmZmVyVHlwZXMuZm9yRWFjaCh0eXBlID0+IHtcbiAgICAgIGNvbnN0IHNiID0gc291cmNlQnVmZmVyW3R5cGVdO1xuICAgICAgaWYgKHNiKSB7XG4gICAgICAgIGNvbnN0IGJ1ZmZlcmVkID0gQnVmZmVySGVscGVyLmdldEJ1ZmZlcmVkKHNiKTtcbiAgICAgICAgLy8gd2hlbiB0YXJnZXQgYnVmZmVyIHN0YXJ0IGV4Y2VlZHMgYWN0dWFsIGJ1ZmZlciBzdGFydFxuICAgICAgICBpZiAoYnVmZmVyZWQubGVuZ3RoID4gMCAmJiB0YXJnZXRCYWNrQnVmZmVyUG9zaXRpb24gPiBidWZmZXJlZC5zdGFydCgwKSkge1xuICAgICAgICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLkJBQ0tfQlVGRkVSX1JFQUNIRUQsIHtcbiAgICAgICAgICAgIGJ1ZmZlckVuZDogdGFyZ2V0QmFja0J1ZmZlclBvc2l0aW9uXG4gICAgICAgICAgfSk7XG5cbiAgICAgICAgICAvLyBTdXBwb3J0IGZvciBkZXByZWNhdGVkIGV2ZW50OlxuICAgICAgICAgIGlmIChkZXRhaWxzICE9IG51bGwgJiYgZGV0YWlscy5saXZlKSB7XG4gICAgICAgICAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5MSVZFX0JBQ0tfQlVGRkVSX1JFQUNIRUQsIHtcbiAgICAgICAgICAgICAgYnVmZmVyRW5kOiB0YXJnZXRCYWNrQnVmZmVyUG9zaXRpb25cbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH0gZWxzZSBpZiAoc2IuZW5kZWQgJiYgYnVmZmVyZWQuZW5kKGJ1ZmZlcmVkLmxlbmd0aCAtIDEpIC0gY3VycmVudFRpbWUgPCB0YXJnZXREdXJhdGlvbiAqIDIpIHtcbiAgICAgICAgICAgIHRoaXMubG9nKGBDYW5ub3QgZmx1c2ggJHt0eXBlfSBiYWNrIGJ1ZmZlciB3aGlsZSBTb3VyY2VCdWZmZXIgaXMgaW4gZW5kZWQgc3RhdGVgKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuQlVGRkVSX0ZMVVNISU5HLCB7XG4gICAgICAgICAgICBzdGFydE9mZnNldDogMCxcbiAgICAgICAgICAgIGVuZE9mZnNldDogdGFyZ2V0QmFja0J1ZmZlclBvc2l0aW9uLFxuICAgICAgICAgICAgdHlwZVxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbiAgZmx1c2hGcm9udEJ1ZmZlcihjdXJyZW50VGltZSwgdGFyZ2V0RHVyYXRpb24sIHRhcmdldEZyb250QnVmZmVyUG9zaXRpb24pIHtcbiAgICBjb25zdCB7XG4gICAgICBzb3VyY2VCdWZmZXJcbiAgICB9ID0gdGhpcztcbiAgICBjb25zdCBzb3VyY2VCdWZmZXJUeXBlcyA9IHRoaXMuZ2V0U291cmNlQnVmZmVyVHlwZXMoKTtcbiAgICBzb3VyY2VCdWZmZXJUeXBlcy5mb3JFYWNoKHR5cGUgPT4ge1xuICAgICAgY29uc3Qgc2IgPSBzb3VyY2VCdWZmZXJbdHlwZV07XG4gICAgICBpZiAoc2IpIHtcbiAgICAgICAgY29uc3QgYnVmZmVyZWQgPSBCdWZmZXJIZWxwZXIuZ2V0QnVmZmVyZWQoc2IpO1xuICAgICAgICBjb25zdCBudW1CdWZmZXJlZFJhbmdlcyA9IGJ1ZmZlcmVkLmxlbmd0aDtcbiAgICAgICAgLy8gVGhlIGJ1ZmZlciBpcyBlaXRoZXIgZW1wdHkgb3IgY29udGlndW91c1xuICAgICAgICBpZiAobnVtQnVmZmVyZWRSYW5nZXMgPCAyKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGJ1ZmZlclN0YXJ0ID0gYnVmZmVyZWQuc3RhcnQobnVtQnVmZmVyZWRSYW5nZXMgLSAxKTtcbiAgICAgICAgY29uc3QgYnVmZmVyRW5kID0gYnVmZmVyZWQuZW5kKG51bUJ1ZmZlcmVkUmFuZ2VzIC0gMSk7XG4gICAgICAgIC8vIE5vIGZsdXNoIGlmIHdlIGNhbiB0b2xlcmF0ZSB0aGUgY3VycmVudCBidWZmZXIgbGVuZ3RoIG9yIHRoZSBjdXJyZW50IGJ1ZmZlciByYW5nZSB3ZSB3b3VsZCBmbHVzaCBpcyBjb250aWd1b3VzIHdpdGggY3VycmVudCBwb3NpdGlvblxuICAgICAgICBpZiAodGFyZ2V0RnJvbnRCdWZmZXJQb3NpdGlvbiA+IGJ1ZmZlclN0YXJ0IHx8IGN1cnJlbnRUaW1lID49IGJ1ZmZlclN0YXJ0ICYmIGN1cnJlbnRUaW1lIDw9IGJ1ZmZlckVuZCkge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfSBlbHNlIGlmIChzYi5lbmRlZCAmJiBjdXJyZW50VGltZSAtIGJ1ZmZlckVuZCA8IDIgKiB0YXJnZXREdXJhdGlvbikge1xuICAgICAgICAgIHRoaXMubG9nKGBDYW5ub3QgZmx1c2ggJHt0eXBlfSBmcm9udCBidWZmZXIgd2hpbGUgU291cmNlQnVmZmVyIGlzIGluIGVuZGVkIHN0YXRlYCk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLkJVRkZFUl9GTFVTSElORywge1xuICAgICAgICAgIHN0YXJ0T2Zmc2V0OiBidWZmZXJTdGFydCxcbiAgICAgICAgICBlbmRPZmZzZXQ6IEluZmluaXR5LFxuICAgICAgICAgIHR5cGVcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlIE1lZGlhIFNvdXJjZSBkdXJhdGlvbiB0byBjdXJyZW50IGxldmVsIGR1cmF0aW9uIG9yIG92ZXJyaWRlIHRvIEluZmluaXR5IGlmIGNvbmZpZ3VyYXRpb24gcGFyYW1ldGVyXG4gICAqICdsaXZlRHVyYXRpb25JbmZpbml0eWAgaXMgc2V0IHRvIGB0cnVlYFxuICAgKiBNb3JlIGRldGFpbHM6IGh0dHBzOi8vZ2l0aHViLmNvbS92aWRlby1kZXYvaGxzLmpzL2lzc3Vlcy8zNTVcbiAgICovXG4gIHVwZGF0ZU1lZGlhRWxlbWVudER1cmF0aW9uKCkge1xuICAgIGlmICghdGhpcy5kZXRhaWxzIHx8ICF0aGlzLm1lZGlhIHx8ICF0aGlzLm1lZGlhU291cmNlIHx8IHRoaXMubWVkaWFTb3VyY2UucmVhZHlTdGF0ZSAhPT0gJ29wZW4nKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHtcbiAgICAgIGRldGFpbHMsXG4gICAgICBobHMsXG4gICAgICBtZWRpYSxcbiAgICAgIG1lZGlhU291cmNlXG4gICAgfSA9IHRoaXM7XG4gICAgY29uc3QgbGV2ZWxEdXJhdGlvbiA9IGRldGFpbHMuZnJhZ21lbnRzWzBdLnN0YXJ0ICsgZGV0YWlscy50b3RhbGR1cmF0aW9uO1xuICAgIGNvbnN0IG1lZGlhRHVyYXRpb24gPSBtZWRpYS5kdXJhdGlvbjtcbiAgICBjb25zdCBtc0R1cmF0aW9uID0gaXNGaW5pdGVOdW1iZXIobWVkaWFTb3VyY2UuZHVyYXRpb24pID8gbWVkaWFTb3VyY2UuZHVyYXRpb24gOiAwO1xuICAgIGlmIChkZXRhaWxzLmxpdmUgJiYgaGxzLmNvbmZpZy5saXZlRHVyYXRpb25JbmZpbml0eSkge1xuICAgICAgLy8gT3ZlcnJpZGUgZHVyYXRpb24gdG8gSW5maW5pdHlcbiAgICAgIG1lZGlhU291cmNlLmR1cmF0aW9uID0gSW5maW5pdHk7XG4gICAgICB0aGlzLnVwZGF0ZVNlZWthYmxlUmFuZ2UoZGV0YWlscyk7XG4gICAgfSBlbHNlIGlmIChsZXZlbER1cmF0aW9uID4gbXNEdXJhdGlvbiAmJiBsZXZlbER1cmF0aW9uID4gbWVkaWFEdXJhdGlvbiB8fCAhaXNGaW5pdGVOdW1iZXIobWVkaWFEdXJhdGlvbikpIHtcbiAgICAgIC8vIGxldmVsRHVyYXRpb24gd2FzIHRoZSBsYXN0IHZhbHVlIHdlIHNldC5cbiAgICAgIC8vIG5vdCB1c2luZyBtZWRpYVNvdXJjZS5kdXJhdGlvbiBhcyB0aGUgYnJvd3NlciBtYXkgdHdlYWsgdGhpcyB2YWx1ZVxuICAgICAgLy8gb25seSB1cGRhdGUgTWVkaWEgU291cmNlIGR1cmF0aW9uIGlmIGl0cyB2YWx1ZSBpbmNyZWFzZSwgdGhpcyBpcyB0byBhdm9pZFxuICAgICAgLy8gZmx1c2hpbmcgYWxyZWFkeSBidWZmZXJlZCBwb3J0aW9uIHdoZW4gc3dpdGNoaW5nIGJldHdlZW4gcXVhbGl0eSBsZXZlbFxuICAgICAgdGhpcy5sb2coYFVwZGF0aW5nIE1lZGlhIFNvdXJjZSBkdXJhdGlvbiB0byAke2xldmVsRHVyYXRpb24udG9GaXhlZCgzKX1gKTtcbiAgICAgIG1lZGlhU291cmNlLmR1cmF0aW9uID0gbGV2ZWxEdXJhdGlvbjtcbiAgICB9XG4gIH1cbiAgdXBkYXRlU2Vla2FibGVSYW5nZShsZXZlbERldGFpbHMpIHtcbiAgICBjb25zdCBtZWRpYVNvdXJjZSA9IHRoaXMubWVkaWFTb3VyY2U7XG4gICAgY29uc3QgZnJhZ21lbnRzID0gbGV2ZWxEZXRhaWxzLmZyYWdtZW50cztcbiAgICBjb25zdCBsZW4gPSBmcmFnbWVudHMubGVuZ3RoO1xuICAgIGlmIChsZW4gJiYgbGV2ZWxEZXRhaWxzLmxpdmUgJiYgbWVkaWFTb3VyY2UgIT0gbnVsbCAmJiBtZWRpYVNvdXJjZS5zZXRMaXZlU2Vla2FibGVSYW5nZSkge1xuICAgICAgY29uc3Qgc3RhcnQgPSBNYXRoLm1heCgwLCBmcmFnbWVudHNbMF0uc3RhcnQpO1xuICAgICAgY29uc3QgZW5kID0gTWF0aC5tYXgoc3RhcnQsIHN0YXJ0ICsgbGV2ZWxEZXRhaWxzLnRvdGFsZHVyYXRpb24pO1xuICAgICAgdGhpcy5sb2coYE1lZGlhIFNvdXJjZSBkdXJhdGlvbiBpcyBzZXQgdG8gJHttZWRpYVNvdXJjZS5kdXJhdGlvbn0uIFNldHRpbmcgc2Vla2FibGUgcmFuZ2UgdG8gJHtzdGFydH0tJHtlbmR9LmApO1xuICAgICAgbWVkaWFTb3VyY2Uuc2V0TGl2ZVNlZWthYmxlUmFuZ2Uoc3RhcnQsIGVuZCk7XG4gICAgfVxuICB9XG4gIGNoZWNrUGVuZGluZ1RyYWNrcygpIHtcbiAgICBjb25zdCB7XG4gICAgICBidWZmZXJDb2RlY0V2ZW50c0V4cGVjdGVkLFxuICAgICAgb3BlcmF0aW9uUXVldWUsXG4gICAgICBwZW5kaW5nVHJhY2tzXG4gICAgfSA9IHRoaXM7XG5cbiAgICAvLyBDaGVjayBpZiB3ZSd2ZSByZWNlaXZlZCBhbGwgb2YgdGhlIGV4cGVjdGVkIGJ1ZmZlckNvZGVjIGV2ZW50cy4gV2hlbiBub25lIHJlbWFpbiwgY3JlYXRlIGFsbCB0aGUgc291cmNlQnVmZmVycyBhdCBvbmNlLlxuICAgIC8vIFRoaXMgaXMgaW1wb3J0YW50IGJlY2F1c2UgdGhlIE1TRSBzcGVjIGFsbG93cyBpbXBsZW1lbnRhdGlvbnMgdG8gdGhyb3cgUXVvdGFFeGNlZWRlZEVycm9ycyBpZiBjcmVhdGluZyBuZXcgc291cmNlQnVmZmVycyBhZnRlclxuICAgIC8vIGRhdGEgaGFzIGJlZW4gYXBwZW5kZWQgdG8gZXhpc3Rpbmcgb25lcy5cbiAgICAvLyAyIHRyYWNrcyBpcyB0aGUgbWF4IChvbmUgZm9yIGF1ZGlvLCBvbmUgZm9yIHZpZGVvKS4gSWYgd2UndmUgcmVhY2ggdGhpcyBtYXggZ28gYWhlYWQgYW5kIGNyZWF0ZSB0aGUgYnVmZmVycy5cbiAgICBjb25zdCBwZW5kaW5nVHJhY2tzQ291bnQgPSBPYmplY3Qua2V5cyhwZW5kaW5nVHJhY2tzKS5sZW5ndGg7XG4gICAgaWYgKHBlbmRpbmdUcmFja3NDb3VudCAmJiAoIWJ1ZmZlckNvZGVjRXZlbnRzRXhwZWN0ZWQgfHwgcGVuZGluZ1RyYWNrc0NvdW50ID09PSAyIHx8ICdhdWRpb3ZpZGVvJyBpbiBwZW5kaW5nVHJhY2tzKSkge1xuICAgICAgLy8gb2ssIGxldCdzIGNyZWF0ZSB0aGVtIG5vdyAhXG4gICAgICB0aGlzLmNyZWF0ZVNvdXJjZUJ1ZmZlcnMocGVuZGluZ1RyYWNrcyk7XG4gICAgICB0aGlzLnBlbmRpbmdUcmFja3MgPSB7fTtcbiAgICAgIC8vIGFwcGVuZCBhbnkgcGVuZGluZyBzZWdtZW50cyBub3cgIVxuICAgICAgY29uc3QgYnVmZmVycyA9IHRoaXMuZ2V0U291cmNlQnVmZmVyVHlwZXMoKTtcbiAgICAgIGlmIChidWZmZXJzLmxlbmd0aCkge1xuICAgICAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5CVUZGRVJfQ1JFQVRFRCwge1xuICAgICAgICAgIHRyYWNrczogdGhpcy50cmFja3NcbiAgICAgICAgfSk7XG4gICAgICAgIGJ1ZmZlcnMuZm9yRWFjaCh0eXBlID0+IHtcbiAgICAgICAgICBvcGVyYXRpb25RdWV1ZS5leGVjdXRlTmV4dCh0eXBlKTtcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCBlcnJvciA9IG5ldyBFcnJvcignY291bGQgbm90IGNyZWF0ZSBzb3VyY2UgYnVmZmVyIGZvciBtZWRpYSBjb2RlYyhzKScpO1xuICAgICAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5FUlJPUiwge1xuICAgICAgICAgIHR5cGU6IEVycm9yVHlwZXMuTUVESUFfRVJST1IsXG4gICAgICAgICAgZGV0YWlsczogRXJyb3JEZXRhaWxzLkJVRkZFUl9JTkNPTVBBVElCTEVfQ09ERUNTX0VSUk9SLFxuICAgICAgICAgIGZhdGFsOiB0cnVlLFxuICAgICAgICAgIGVycm9yLFxuICAgICAgICAgIHJlYXNvbjogZXJyb3IubWVzc2FnZVxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgY3JlYXRlU291cmNlQnVmZmVycyh0cmFja3MpIHtcbiAgICBjb25zdCB7XG4gICAgICBzb3VyY2VCdWZmZXIsXG4gICAgICBtZWRpYVNvdXJjZVxuICAgIH0gPSB0aGlzO1xuICAgIGlmICghbWVkaWFTb3VyY2UpIHtcbiAgICAgIHRocm93IEVycm9yKCdjcmVhdGVTb3VyY2VCdWZmZXJzIGNhbGxlZCB3aGVuIG1lZGlhU291cmNlIHdhcyBudWxsJyk7XG4gICAgfVxuICAgIGZvciAoY29uc3QgdHJhY2tOYW1lIGluIHRyYWNrcykge1xuICAgICAgaWYgKCFzb3VyY2VCdWZmZXJbdHJhY2tOYW1lXSkge1xuICAgICAgICB2YXIgX3RyYWNrJGxldmVsQ29kZWM7XG4gICAgICAgIGNvbnN0IHRyYWNrID0gdHJhY2tzW3RyYWNrTmFtZV07XG4gICAgICAgIGlmICghdHJhY2spIHtcbiAgICAgICAgICB0aHJvdyBFcnJvcihgc291cmNlIGJ1ZmZlciBleGlzdHMgZm9yIHRyYWNrICR7dHJhY2tOYW1lfSwgaG93ZXZlciB0cmFjayBkb2VzIG5vdGApO1xuICAgICAgICB9XG4gICAgICAgIC8vIHVzZSBsZXZlbENvZGVjIGFzIGZpcnN0IHByaW9yaXR5IHVubGVzcyBpdCBjb250YWlucyBtdWx0aXBsZSBjb21tYS1zZXBhcmF0ZWQgY29kZWMgdmFsdWVzXG4gICAgICAgIGxldCBjb2RlYyA9ICgoX3RyYWNrJGxldmVsQ29kZWMgPSB0cmFjay5sZXZlbENvZGVjKSA9PSBudWxsID8gdm9pZCAwIDogX3RyYWNrJGxldmVsQ29kZWMuaW5kZXhPZignLCcpKSA9PT0gLTEgPyB0cmFjay5sZXZlbENvZGVjIDogdHJhY2suY29kZWM7XG4gICAgICAgIGlmIChjb2RlYykge1xuICAgICAgICAgIGlmICh0cmFja05hbWUuc2xpY2UoMCwgNSkgPT09ICdhdWRpbycpIHtcbiAgICAgICAgICAgIGNvZGVjID0gZ2V0Q29kZWNDb21wYXRpYmxlTmFtZShjb2RlYywgdGhpcy5hcHBlbmRTb3VyY2UpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBjb25zdCBtaW1lVHlwZSA9IGAke3RyYWNrLmNvbnRhaW5lcn07Y29kZWNzPSR7Y29kZWN9YDtcbiAgICAgICAgdGhpcy5sb2coYGNyZWF0aW5nIHNvdXJjZUJ1ZmZlcigke21pbWVUeXBlfSlgKTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBzYiA9IHNvdXJjZUJ1ZmZlclt0cmFja05hbWVdID0gbWVkaWFTb3VyY2UuYWRkU291cmNlQnVmZmVyKG1pbWVUeXBlKTtcbiAgICAgICAgICBjb25zdCBzYk5hbWUgPSB0cmFja05hbWU7XG4gICAgICAgICAgdGhpcy5hZGRCdWZmZXJMaXN0ZW5lcihzYk5hbWUsICd1cGRhdGVzdGFydCcsIHRoaXMuX29uU0JVcGRhdGVTdGFydCk7XG4gICAgICAgICAgdGhpcy5hZGRCdWZmZXJMaXN0ZW5lcihzYk5hbWUsICd1cGRhdGVlbmQnLCB0aGlzLl9vblNCVXBkYXRlRW5kKTtcbiAgICAgICAgICB0aGlzLmFkZEJ1ZmZlckxpc3RlbmVyKHNiTmFtZSwgJ2Vycm9yJywgdGhpcy5fb25TQlVwZGF0ZUVycm9yKTtcbiAgICAgICAgICAvLyBNYW5hZ2VkU291cmNlQnVmZmVyIGJ1ZmZlcmVkY2hhbmdlIGV2ZW50XG4gICAgICAgICAgaWYgKHRoaXMuYXBwZW5kU291cmNlKSB7XG4gICAgICAgICAgICB0aGlzLmFkZEJ1ZmZlckxpc3RlbmVyKHNiTmFtZSwgJ2J1ZmZlcmVkY2hhbmdlJywgKHR5cGUsIGV2ZW50KSA9PiB7XG4gICAgICAgICAgICAgIC8vIElmIG1lZGlhIHdhcyBlamVjdGVkIGNoZWNrIGZvciBhIGNoYW5nZS4gQWRkZWQgcmFuZ2VzIGFyZSByZWR1bmRhbnQgd2l0aCBjaGFuZ2VzIG9uICd1cGRhdGVlbmQnIGV2ZW50LlxuICAgICAgICAgICAgICBjb25zdCByZW1vdmVkUmFuZ2VzID0gZXZlbnQucmVtb3ZlZFJhbmdlcztcbiAgICAgICAgICAgICAgaWYgKHJlbW92ZWRSYW5nZXMgIT0gbnVsbCAmJiByZW1vdmVkUmFuZ2VzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLkJVRkZFUl9GTFVTSEVELCB7XG4gICAgICAgICAgICAgICAgICB0eXBlOiB0cmFja05hbWVcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHRoaXMudHJhY2tzW3RyYWNrTmFtZV0gPSB7XG4gICAgICAgICAgICBidWZmZXI6IHNiLFxuICAgICAgICAgICAgY29kZWM6IGNvZGVjLFxuICAgICAgICAgICAgY29udGFpbmVyOiB0cmFjay5jb250YWluZXIsXG4gICAgICAgICAgICBsZXZlbENvZGVjOiB0cmFjay5sZXZlbENvZGVjLFxuICAgICAgICAgICAgbWV0YWRhdGE6IHRyYWNrLm1ldGFkYXRhLFxuICAgICAgICAgICAgaWQ6IHRyYWNrLmlkXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgdGhpcy5lcnJvcihgZXJyb3Igd2hpbGUgdHJ5aW5nIHRvIGFkZCBzb3VyY2VCdWZmZXI6ICR7ZXJyLm1lc3NhZ2V9YCk7XG4gICAgICAgICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuRVJST1IsIHtcbiAgICAgICAgICAgIHR5cGU6IEVycm9yVHlwZXMuTUVESUFfRVJST1IsXG4gICAgICAgICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuQlVGRkVSX0FERF9DT0RFQ19FUlJPUixcbiAgICAgICAgICAgIGZhdGFsOiBmYWxzZSxcbiAgICAgICAgICAgIGVycm9yOiBlcnIsXG4gICAgICAgICAgICBzb3VyY2VCdWZmZXJOYW1lOiB0cmFja05hbWUsXG4gICAgICAgICAgICBtaW1lVHlwZTogbWltZVR5cGVcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuICBnZXQgbWVkaWFTcmMoKSB7XG4gICAgdmFyIF90aGlzJG1lZGlhLCBfdGhpcyRtZWRpYSRxdWVyeVNlbGU7XG4gICAgY29uc3QgbWVkaWEgPSAoKF90aGlzJG1lZGlhID0gdGhpcy5tZWRpYSkgPT0gbnVsbCA/IHZvaWQgMCA6IChfdGhpcyRtZWRpYSRxdWVyeVNlbGUgPSBfdGhpcyRtZWRpYS5xdWVyeVNlbGVjdG9yKSA9PSBudWxsID8gdm9pZCAwIDogX3RoaXMkbWVkaWEkcXVlcnlTZWxlLmNhbGwoX3RoaXMkbWVkaWEsICdzb3VyY2UnKSkgfHwgdGhpcy5tZWRpYTtcbiAgICByZXR1cm4gbWVkaWEgPT0gbnVsbCA/IHZvaWQgMCA6IG1lZGlhLnNyYztcbiAgfVxuICBfb25TQlVwZGF0ZVN0YXJ0KHR5cGUpIHtcbiAgICBjb25zdCB7XG4gICAgICBvcGVyYXRpb25RdWV1ZVxuICAgIH0gPSB0aGlzO1xuICAgIGNvbnN0IG9wZXJhdGlvbiA9IG9wZXJhdGlvblF1ZXVlLmN1cnJlbnQodHlwZSk7XG4gICAgb3BlcmF0aW9uLm9uU3RhcnQoKTtcbiAgfVxuICBfb25TQlVwZGF0ZUVuZCh0eXBlKSB7XG4gICAgdmFyIF90aGlzJG1lZGlhU291cmNlMjtcbiAgICBpZiAoKChfdGhpcyRtZWRpYVNvdXJjZTIgPSB0aGlzLm1lZGlhU291cmNlKSA9PSBudWxsID8gdm9pZCAwIDogX3RoaXMkbWVkaWFTb3VyY2UyLnJlYWR5U3RhdGUpID09PSAnY2xvc2VkJykge1xuICAgICAgdGhpcy5yZXNldEJ1ZmZlcih0eXBlKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3Qge1xuICAgICAgb3BlcmF0aW9uUXVldWVcbiAgICB9ID0gdGhpcztcbiAgICBjb25zdCBvcGVyYXRpb24gPSBvcGVyYXRpb25RdWV1ZS5jdXJyZW50KHR5cGUpO1xuICAgIG9wZXJhdGlvbi5vbkNvbXBsZXRlKCk7XG4gICAgb3BlcmF0aW9uUXVldWUuc2hpZnRBbmRFeGVjdXRlTmV4dCh0eXBlKTtcbiAgfVxuICBfb25TQlVwZGF0ZUVycm9yKHR5cGUsIGV2ZW50KSB7XG4gICAgdmFyIF90aGlzJG1lZGlhU291cmNlMztcbiAgICBjb25zdCBlcnJvciA9IG5ldyBFcnJvcihgJHt0eXBlfSBTb3VyY2VCdWZmZXIgZXJyb3IuIE1lZGlhU291cmNlIHJlYWR5U3RhdGU6ICR7KF90aGlzJG1lZGlhU291cmNlMyA9IHRoaXMubWVkaWFTb3VyY2UpID09IG51bGwgPyB2b2lkIDAgOiBfdGhpcyRtZWRpYVNvdXJjZTMucmVhZHlTdGF0ZX1gKTtcbiAgICB0aGlzLmVycm9yKGAke2Vycm9yfWAsIGV2ZW50KTtcbiAgICAvLyBhY2NvcmRpbmcgdG8gaHR0cDovL3d3dy53My5vcmcvVFIvbWVkaWEtc291cmNlLyNzb3VyY2VidWZmZXItYXBwZW5kLWVycm9yXG4gICAgLy8gU291cmNlQnVmZmVyIGVycm9ycyBhcmUgbm90IG5lY2Vzc2FyaWx5IGZhdGFsOyBpZiBzbywgdGhlIEhUTUxNZWRpYUVsZW1lbnQgd2lsbCBmaXJlIGFuIGVycm9yIGV2ZW50XG4gICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuRVJST1IsIHtcbiAgICAgIHR5cGU6IEVycm9yVHlwZXMuTUVESUFfRVJST1IsXG4gICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuQlVGRkVSX0FQUEVORElOR19FUlJPUixcbiAgICAgIHNvdXJjZUJ1ZmZlck5hbWU6IHR5cGUsXG4gICAgICBlcnJvcixcbiAgICAgIGZhdGFsOiBmYWxzZVxuICAgIH0pO1xuICAgIC8vIHVwZGF0ZWVuZCBpcyBhbHdheXMgZmlyZWQgYWZ0ZXIgZXJyb3IsIHNvIHdlJ2xsIGFsbG93IHRoYXQgdG8gc2hpZnQgdGhlIGN1cnJlbnQgb3BlcmF0aW9uIG9mZiBvZiB0aGUgcXVldWVcbiAgICBjb25zdCBvcGVyYXRpb24gPSB0aGlzLm9wZXJhdGlvblF1ZXVlLmN1cnJlbnQodHlwZSk7XG4gICAgaWYgKG9wZXJhdGlvbikge1xuICAgICAgb3BlcmF0aW9uLm9uRXJyb3IoZXJyb3IpO1xuICAgIH1cbiAgfVxuXG4gIC8vIFRoaXMgbWV0aG9kIG11c3QgcmVzdWx0IGluIGFuIHVwZGF0ZWVuZCBldmVudDsgaWYgcmVtb3ZlIGlzIG5vdCBjYWxsZWQsIF9vblNCVXBkYXRlRW5kIG11c3QgYmUgY2FsbGVkIG1hbnVhbGx5XG4gIHJlbW92ZUV4ZWN1dG9yKHR5cGUsIHN0YXJ0T2Zmc2V0LCBlbmRPZmZzZXQpIHtcbiAgICBjb25zdCB7XG4gICAgICBtZWRpYSxcbiAgICAgIG1lZGlhU291cmNlLFxuICAgICAgb3BlcmF0aW9uUXVldWUsXG4gICAgICBzb3VyY2VCdWZmZXJcbiAgICB9ID0gdGhpcztcbiAgICBjb25zdCBzYiA9IHNvdXJjZUJ1ZmZlclt0eXBlXTtcbiAgICBpZiAoIW1lZGlhIHx8ICFtZWRpYVNvdXJjZSB8fCAhc2IpIHtcbiAgICAgIHRoaXMud2FybihgQXR0ZW1wdGluZyB0byByZW1vdmUgZnJvbSB0aGUgJHt0eXBlfSBTb3VyY2VCdWZmZXIsIGJ1dCBpdCBkb2VzIG5vdCBleGlzdGApO1xuICAgICAgb3BlcmF0aW9uUXVldWUuc2hpZnRBbmRFeGVjdXRlTmV4dCh0eXBlKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgbWVkaWFEdXJhdGlvbiA9IGlzRmluaXRlTnVtYmVyKG1lZGlhLmR1cmF0aW9uKSA/IG1lZGlhLmR1cmF0aW9uIDogSW5maW5pdHk7XG4gICAgY29uc3QgbXNEdXJhdGlvbiA9IGlzRmluaXRlTnVtYmVyKG1lZGlhU291cmNlLmR1cmF0aW9uKSA/IG1lZGlhU291cmNlLmR1cmF0aW9uIDogSW5maW5pdHk7XG4gICAgY29uc3QgcmVtb3ZlU3RhcnQgPSBNYXRoLm1heCgwLCBzdGFydE9mZnNldCk7XG4gICAgY29uc3QgcmVtb3ZlRW5kID0gTWF0aC5taW4oZW5kT2Zmc2V0LCBtZWRpYUR1cmF0aW9uLCBtc0R1cmF0aW9uKTtcbiAgICBpZiAocmVtb3ZlRW5kID4gcmVtb3ZlU3RhcnQgJiYgKCFzYi5lbmRpbmcgfHwgc2IuZW5kZWQpKSB7XG4gICAgICBzYi5lbmRlZCA9IGZhbHNlO1xuICAgICAgdGhpcy5sb2coYFJlbW92aW5nIFske3JlbW92ZVN0YXJ0fSwke3JlbW92ZUVuZH1dIGZyb20gdGhlICR7dHlwZX0gU291cmNlQnVmZmVyYCk7XG4gICAgICBzYi5yZW1vdmUocmVtb3ZlU3RhcnQsIHJlbW92ZUVuZCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIEN5Y2xlIHRoZSBxdWV1ZVxuICAgICAgb3BlcmF0aW9uUXVldWUuc2hpZnRBbmRFeGVjdXRlTmV4dCh0eXBlKTtcbiAgICB9XG4gIH1cblxuICAvLyBUaGlzIG1ldGhvZCBtdXN0IHJlc3VsdCBpbiBhbiB1cGRhdGVlbmQgZXZlbnQ7IGlmIGFwcGVuZCBpcyBub3QgY2FsbGVkLCBfb25TQlVwZGF0ZUVuZCBtdXN0IGJlIGNhbGxlZCBtYW51YWxseVxuICBhcHBlbmRFeGVjdXRvcihkYXRhLCB0eXBlKSB7XG4gICAgY29uc3Qgc2IgPSB0aGlzLnNvdXJjZUJ1ZmZlclt0eXBlXTtcbiAgICBpZiAoIXNiKSB7XG4gICAgICBpZiAoIXRoaXMucGVuZGluZ1RyYWNrc1t0eXBlXSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEF0dGVtcHRpbmcgdG8gYXBwZW5kIHRvIHRoZSAke3R5cGV9IFNvdXJjZUJ1ZmZlciwgYnV0IGl0IGRvZXMgbm90IGV4aXN0YCk7XG4gICAgICB9XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHNiLmVuZGVkID0gZmFsc2U7XG4gICAgc2IuYXBwZW5kQnVmZmVyKGRhdGEpO1xuICB9XG5cbiAgLy8gRW5xdWV1ZXMgYW4gb3BlcmF0aW9uIHRvIGVhY2ggU291cmNlQnVmZmVyIHF1ZXVlIHdoaWNoLCB1cG9uIGV4ZWN1dGlvbiwgcmVzb2x2ZXMgYSBwcm9taXNlLiBXaGVuIGFsbCBwcm9taXNlc1xuICAvLyByZXNvbHZlLCB0aGUgb25VbmJsb2NrZWQgZnVuY3Rpb24gaXMgZXhlY3V0ZWQuIEZ1bmN0aW9ucyBjYWxsaW5nIHRoaXMgbWV0aG9kIGRvIG5vdCBuZWVkIHRvIHVuYmxvY2sgdGhlIHF1ZXVlXG4gIC8vIHVwb24gY29tcGxldGlvbiwgc2luY2Ugd2UgYWxyZWFkeSBkbyBpdCBoZXJlXG4gIGJsb2NrQnVmZmVycyhvblVuYmxvY2tlZCwgYnVmZmVycyA9IHRoaXMuZ2V0U291cmNlQnVmZmVyVHlwZXMoKSkge1xuICAgIGlmICghYnVmZmVycy5sZW5ndGgpIHtcbiAgICAgIHRoaXMubG9nKCdCbG9ja2luZyBvcGVyYXRpb24gcmVxdWVzdGVkLCBidXQgbm8gU291cmNlQnVmZmVycyBleGlzdCcpO1xuICAgICAgUHJvbWlzZS5yZXNvbHZlKCkudGhlbihvblVuYmxvY2tlZCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHtcbiAgICAgIG9wZXJhdGlvblF1ZXVlXG4gICAgfSA9IHRoaXM7XG5cbiAgICAvLyBsb2dnZXIuZGVidWcoYFtidWZmZXItY29udHJvbGxlcl06IEJsb2NraW5nICR7YnVmZmVyc30gU291cmNlQnVmZmVyYCk7XG4gICAgY29uc3QgYmxvY2tpbmdPcGVyYXRpb25zID0gYnVmZmVycy5tYXAodHlwZSA9PiBvcGVyYXRpb25RdWV1ZS5hcHBlbmRCbG9ja2VyKHR5cGUpKTtcbiAgICBQcm9taXNlLmFsbChibG9ja2luZ09wZXJhdGlvbnMpLnRoZW4oKCkgPT4ge1xuICAgICAgLy8gbG9nZ2VyLmRlYnVnKGBbYnVmZmVyLWNvbnRyb2xsZXJdOiBCbG9ja2luZyBvcGVyYXRpb24gcmVzb2x2ZWQ7IHVuYmxvY2tpbmcgJHtidWZmZXJzfSBTb3VyY2VCdWZmZXJgKTtcbiAgICAgIG9uVW5ibG9ja2VkKCk7XG4gICAgICBidWZmZXJzLmZvckVhY2godHlwZSA9PiB7XG4gICAgICAgIGNvbnN0IHNiID0gdGhpcy5zb3VyY2VCdWZmZXJbdHlwZV07XG4gICAgICAgIC8vIE9ubHkgY3ljbGUgdGhlIHF1ZXVlIGlmIHRoZSBTQiBpcyBub3QgdXBkYXRpbmcuIFRoZXJlJ3MgYSBidWcgaW4gQ2hyb21lIHdoaWNoIHNldHMgdGhlIFNCIHVwZGF0aW5nIGZsYWcgdG9cbiAgICAgICAgLy8gdHJ1ZSB3aGVuIGNoYW5naW5nIHRoZSBNZWRpYVNvdXJjZSBkdXJhdGlvbiAoaHR0cHM6Ly9idWdzLmNocm9taXVtLm9yZy9wL2Nocm9taXVtL2lzc3Vlcy9kZXRhaWw/aWQ9OTU5MzU5JmNhbj0yJnE9bWVkaWFzb3VyY2UlMjBkdXJhdGlvbilcbiAgICAgICAgLy8gV2hpbGUgdGhpcyBpcyBhIHdvcmthcm91bmQsIGl0J3MgcHJvYmFibHkgdXNlZnVsIHRvIGhhdmUgYXJvdW5kXG4gICAgICAgIGlmICghKHNiICE9IG51bGwgJiYgc2IudXBkYXRpbmcpKSB7XG4gICAgICAgICAgb3BlcmF0aW9uUXVldWUuc2hpZnRBbmRFeGVjdXRlTmV4dCh0eXBlKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfSk7XG4gIH1cbiAgZ2V0U291cmNlQnVmZmVyVHlwZXMoKSB7XG4gICAgcmV0dXJuIE9iamVjdC5rZXlzKHRoaXMuc291cmNlQnVmZmVyKTtcbiAgfVxuICBhZGRCdWZmZXJMaXN0ZW5lcih0eXBlLCBldmVudCwgZm4pIHtcbiAgICBjb25zdCBidWZmZXIgPSB0aGlzLnNvdXJjZUJ1ZmZlclt0eXBlXTtcbiAgICBpZiAoIWJ1ZmZlcikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBsaXN0ZW5lciA9IGZuLmJpbmQodGhpcywgdHlwZSk7XG4gICAgdGhpcy5saXN0ZW5lcnNbdHlwZV0ucHVzaCh7XG4gICAgICBldmVudCxcbiAgICAgIGxpc3RlbmVyXG4gICAgfSk7XG4gICAgYnVmZmVyLmFkZEV2ZW50TGlzdGVuZXIoZXZlbnQsIGxpc3RlbmVyKTtcbiAgfVxuICByZW1vdmVCdWZmZXJMaXN0ZW5lcnModHlwZSkge1xuICAgIGNvbnN0IGJ1ZmZlciA9IHRoaXMuc291cmNlQnVmZmVyW3R5cGVdO1xuICAgIGlmICghYnVmZmVyKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMubGlzdGVuZXJzW3R5cGVdLmZvckVhY2gobCA9PiB7XG4gICAgICBidWZmZXIucmVtb3ZlRXZlbnRMaXN0ZW5lcihsLmV2ZW50LCBsLmxpc3RlbmVyKTtcbiAgICB9KTtcbiAgfVxufVxuZnVuY3Rpb24gcmVtb3ZlU291cmNlQ2hpbGRyZW4obm9kZSkge1xuICBjb25zdCBzb3VyY2VDaGlsZHJlbiA9IG5vZGUucXVlcnlTZWxlY3RvckFsbCgnc291cmNlJyk7XG4gIFtdLnNsaWNlLmNhbGwoc291cmNlQ2hpbGRyZW4pLmZvckVhY2goc291cmNlID0+IHtcbiAgICBub2RlLnJlbW92ZUNoaWxkKHNvdXJjZSk7XG4gIH0pO1xufVxuZnVuY3Rpb24gYWRkU291cmNlKG1lZGlhLCB1cmwpIHtcbiAgY29uc3Qgc291cmNlID0gc2VsZi5kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzb3VyY2UnKTtcbiAgc291cmNlLnR5cGUgPSAndmlkZW8vbXA0JztcbiAgc291cmNlLnNyYyA9IHVybDtcbiAgbWVkaWEuYXBwZW5kQ2hpbGQoc291cmNlKTtcbn1cblxuLyoqXG4gKlxuICogVGhpcyBjb2RlIHdhcyBwb3J0ZWQgZnJvbSB0aGUgZGFzaC5qcyBwcm9qZWN0IGF0OlxuICogICBodHRwczovL2dpdGh1Yi5jb20vRGFzaC1JbmR1c3RyeS1Gb3J1bS9kYXNoLmpzL2Jsb2IvZGV2ZWxvcG1lbnQvZXh0ZXJuYWxzL2NlYTYwOC1wYXJzZXIuanNcbiAqICAgaHR0cHM6Ly9naXRodWIuY29tL0Rhc2gtSW5kdXN0cnktRm9ydW0vZGFzaC5qcy9jb21taXQvODI2OWIyNmE3NjFlMDg1M2JiMjFkNzg3ODBlZDk0NTE0NGVjZGQ0ZCNkaWZmLTcxYmMyOTVhMmQ2YjZiNzA5M2ExZDMyOTBkNTNhNGIyXG4gKlxuICogVGhlIG9yaWdpbmFsIGNvcHlyaWdodCBhcHBlYXJzIGJlbG93OlxuICpcbiAqIFRoZSBjb3B5cmlnaHQgaW4gdGhpcyBzb2Z0d2FyZSBpcyBiZWluZyBtYWRlIGF2YWlsYWJsZSB1bmRlciB0aGUgQlNEIExpY2Vuc2UsXG4gKiBpbmNsdWRlZCBiZWxvdy4gVGhpcyBzb2Z0d2FyZSBtYXkgYmUgc3ViamVjdCB0byBvdGhlciB0aGlyZCBwYXJ0eSBhbmQgY29udHJpYnV0b3JcbiAqIHJpZ2h0cywgaW5jbHVkaW5nIHBhdGVudCByaWdodHMsIGFuZCBubyBzdWNoIHJpZ2h0cyBhcmUgZ3JhbnRlZCB1bmRlciB0aGlzIGxpY2Vuc2UuXG4gKlxuICogQ29weXJpZ2h0IChjKSAyMDE1LTIwMTYsIERBU0ggSW5kdXN0cnkgRm9ydW0uXG4gKiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICpcbiAqIFJlZGlzdHJpYnV0aW9uIGFuZCB1c2UgaW4gc291cmNlIGFuZCBiaW5hcnkgZm9ybXMsIHdpdGggb3Igd2l0aG91dCBtb2RpZmljYXRpb24sXG4gKiBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zIGFyZSBtZXQ6XG4gKiAgMS4gUmVkaXN0cmlidXRpb25zIG9mIHNvdXJjZSBjb2RlIG11c3QgcmV0YWluIHRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlLCB0aGlzXG4gKiAgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIuXG4gKiAgKiBSZWRpc3RyaWJ1dGlvbnMgaW4gYmluYXJ5IGZvcm0gbXVzdCByZXByb2R1Y2UgdGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UsXG4gKiAgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiB0aGUgZG9jdW1lbnRhdGlvbiBhbmQvb3JcbiAqICBvdGhlciBtYXRlcmlhbHMgcHJvdmlkZWQgd2l0aCB0aGUgZGlzdHJpYnV0aW9uLlxuICogIDIuIE5laXRoZXIgdGhlIG5hbWUgb2YgRGFzaCBJbmR1c3RyeSBGb3J1bSBub3IgdGhlIG5hbWVzIG9mIGl0c1xuICogIGNvbnRyaWJ1dG9ycyBtYXkgYmUgdXNlZCB0byBlbmRvcnNlIG9yIHByb21vdGUgcHJvZHVjdHMgZGVyaXZlZCBmcm9tIHRoaXMgc29mdHdhcmVcbiAqICB3aXRob3V0IHNwZWNpZmljIHByaW9yIHdyaXR0ZW4gcGVybWlzc2lvbi5cbiAqXG4gKiAgVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUyBBUyBJUyBBTkQgQU5ZXG4gKiAgRVhQUkVTUyBPUiBJTVBMSUVEIFdBUlJBTlRJRVMsIElOQ0xVRElORywgQlVUIE5PVCBMSU1JVEVEIFRPLCBUSEUgSU1QTElFRFxuICogIFdBUlJBTlRJRVMgT0YgTUVSQ0hBTlRBQklMSVRZIEFORCBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBUkUgRElTQ0xBSU1FRC5cbiAqICBJTiBOTyBFVkVOVCBTSEFMTCBUSEUgQ09QWVJJR0hUIEhPTERFUiBPUiBDT05UUklCVVRPUlMgQkUgTElBQkxFIEZPUiBBTlkgRElSRUNULFxuICogIElORElSRUNULCBJTkNJREVOVEFMLCBTUEVDSUFMLCBFWEVNUExBUlksIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyAoSU5DTFVESU5HLCBCVVRcbiAqICBOT1QgTElNSVRFRCBUTywgUFJPQ1VSRU1FTlQgT0YgU1VCU1RJVFVURSBHT09EUyBPUiBTRVJWSUNFUzsgTE9TUyBPRiBVU0UsIERBVEEsIE9SXG4gKiAgUFJPRklUUzsgT1IgQlVTSU5FU1MgSU5URVJSVVBUSU9OKSBIT1dFVkVSIENBVVNFRCBBTkQgT04gQU5ZIFRIRU9SWSBPRiBMSUFCSUxJVFksXG4gKiAgV0hFVEhFUiBJTiBDT05UUkFDVCwgU1RSSUNUIExJQUJJTElUWSwgT1IgVE9SVCAoSU5DTFVESU5HIE5FR0xJR0VOQ0UgT1IgT1RIRVJXSVNFKVxuICogIEFSSVNJTkcgSU4gQU5ZIFdBWSBPVVQgT0YgVEhFIFVTRSBPRiBUSElTIFNPRlRXQVJFLCBFVkVOIElGIEFEVklTRUQgT0YgVEhFXG4gKiAgUE9TU0lCSUxJVFkgT0YgU1VDSCBEQU1BR0UuXG4gKi9cbi8qKlxuICogIEV4Y2VwdGlvbnMgZnJvbSByZWd1bGFyIEFTQ0lJLiBDb2RlUG9pbnRzIGFyZSBtYXBwZWQgdG8gVVRGLTE2IGNvZGVzXG4gKi9cblxuY29uc3Qgc3BlY2lhbENlYTYwOENoYXJzQ29kZXMgPSB7XG4gIDB4MmE6IDB4ZTEsXG4gIC8vIGxvd2VyY2FzZSBhLCBhY3V0ZSBhY2NlbnRcbiAgMHg1YzogMHhlOSxcbiAgLy8gbG93ZXJjYXNlIGUsIGFjdXRlIGFjY2VudFxuICAweDVlOiAweGVkLFxuICAvLyBsb3dlcmNhc2UgaSwgYWN1dGUgYWNjZW50XG4gIDB4NWY6IDB4ZjMsXG4gIC8vIGxvd2VyY2FzZSBvLCBhY3V0ZSBhY2NlbnRcbiAgMHg2MDogMHhmYSxcbiAgLy8gbG93ZXJjYXNlIHUsIGFjdXRlIGFjY2VudFxuICAweDdiOiAweGU3LFxuICAvLyBsb3dlcmNhc2UgYyB3aXRoIGNlZGlsbGFcbiAgMHg3YzogMHhmNyxcbiAgLy8gZGl2aXNpb24gc3ltYm9sXG4gIDB4N2Q6IDB4ZDEsXG4gIC8vIHVwcGVyY2FzZSBOIHRpbGRlXG4gIDB4N2U6IDB4ZjEsXG4gIC8vIGxvd2VyY2FzZSBuIHRpbGRlXG4gIDB4N2Y6IDB4MjU4OCxcbiAgLy8gRnVsbCBibG9ja1xuICAvLyBUSElTIEJMT0NLIElOQ0xVREVTIFRIRSAxNiBFWFRFTkRFRCAoVFdPLUJZVEUpIExJTkUgMjEgQ0hBUkFDVEVSU1xuICAvLyBUSEFUIENPTUUgRlJPTSBISSBCWVRFPTB4MTEgQU5EIExPVyBCRVRXRUVOIDB4MzAgQU5EIDB4M0ZcbiAgLy8gVEhJUyBNRUFOUyBUSEFUIFxceDUwIE1VU1QgQkUgQURERUQgVE8gVEhFIFZBTFVFU1xuICAweDgwOiAweGFlLFxuICAvLyBSZWdpc3RlcmVkIHN5bWJvbCAoUilcbiAgMHg4MTogMHhiMCxcbiAgLy8gZGVncmVlIHNpZ25cbiAgMHg4MjogMHhiZCxcbiAgLy8gMS8yIHN5bWJvbFxuICAweDgzOiAweGJmLFxuICAvLyBJbnZlcnRlZCAob3BlbikgcXVlc3Rpb24gbWFya1xuICAweDg0OiAweDIxMjIsXG4gIC8vIFRyYWRlbWFyayBzeW1ib2wgKFRNKVxuICAweDg1OiAweGEyLFxuICAvLyBDZW50cyBzeW1ib2xcbiAgMHg4NjogMHhhMyxcbiAgLy8gUG91bmRzIHN0ZXJsaW5nXG4gIDB4ODc6IDB4MjY2YSxcbiAgLy8gTXVzaWMgOCd0aCBub3RlXG4gIDB4ODg6IDB4ZTAsXG4gIC8vIGxvd2VyY2FzZSBhLCBncmF2ZSBhY2NlbnRcbiAgMHg4OTogMHgyMCxcbiAgLy8gdHJhbnNwYXJlbnQgc3BhY2UgKHJlZ3VsYXIpXG4gIDB4OGE6IDB4ZTgsXG4gIC8vIGxvd2VyY2FzZSBlLCBncmF2ZSBhY2NlbnRcbiAgMHg4YjogMHhlMixcbiAgLy8gbG93ZXJjYXNlIGEsIGNpcmN1bWZsZXggYWNjZW50XG4gIDB4OGM6IDB4ZWEsXG4gIC8vIGxvd2VyY2FzZSBlLCBjaXJjdW1mbGV4IGFjY2VudFxuICAweDhkOiAweGVlLFxuICAvLyBsb3dlcmNhc2UgaSwgY2lyY3VtZmxleCBhY2NlbnRcbiAgMHg4ZTogMHhmNCxcbiAgLy8gbG93ZXJjYXNlIG8sIGNpcmN1bWZsZXggYWNjZW50XG4gIDB4OGY6IDB4ZmIsXG4gIC8vIGxvd2VyY2FzZSB1LCBjaXJjdW1mbGV4IGFjY2VudFxuICAvLyBUSElTIEJMT0NLIElOQ0xVREVTIFRIRSAzMiBFWFRFTkRFRCAoVFdPLUJZVEUpIExJTkUgMjEgQ0hBUkFDVEVSU1xuICAvLyBUSEFUIENPTUUgRlJPTSBISSBCWVRFPTB4MTIgQU5EIExPVyBCRVRXRUVOIDB4MjAgQU5EIDB4M0ZcbiAgMHg5MDogMHhjMSxcbiAgLy8gY2FwaXRhbCBsZXR0ZXIgQSB3aXRoIGFjdXRlXG4gIDB4OTE6IDB4YzksXG4gIC8vIGNhcGl0YWwgbGV0dGVyIEUgd2l0aCBhY3V0ZVxuICAweDkyOiAweGQzLFxuICAvLyBjYXBpdGFsIGxldHRlciBPIHdpdGggYWN1dGVcbiAgMHg5MzogMHhkYSxcbiAgLy8gY2FwaXRhbCBsZXR0ZXIgVSB3aXRoIGFjdXRlXG4gIDB4OTQ6IDB4ZGMsXG4gIC8vIGNhcGl0YWwgbGV0dGVyIFUgd2l0aCBkaWFyZXNpc1xuICAweDk1OiAweGZjLFxuICAvLyBsb3dlcmNhc2UgbGV0dGVyIFUgd2l0aCBkaWFlcmVzaXNcbiAgMHg5NjogMHgyMDE4LFxuICAvLyBvcGVuaW5nIHNpbmdsZSBxdW90ZVxuICAweDk3OiAweGExLFxuICAvLyBpbnZlcnRlZCBleGNsYW1hdGlvbiBtYXJrXG4gIDB4OTg6IDB4MmEsXG4gIC8vIGFzdGVyaXNrXG4gIDB4OTk6IDB4MjAxOSxcbiAgLy8gY2xvc2luZyBzaW5nbGUgcXVvdGVcbiAgMHg5YTogMHgyNTAxLFxuICAvLyBib3ggZHJhd2luZ3MgaGVhdnkgaG9yaXpvbnRhbFxuICAweDliOiAweGE5LFxuICAvLyBjb3B5cmlnaHQgc2lnblxuICAweDljOiAweDIxMjAsXG4gIC8vIFNlcnZpY2UgbWFya1xuICAweDlkOiAweDIwMjIsXG4gIC8vIChyb3VuZCkgYnVsbGV0XG4gIDB4OWU6IDB4MjAxYyxcbiAgLy8gTGVmdCBkb3VibGUgcXVvdGF0aW9uIG1hcmtcbiAgMHg5ZjogMHgyMDFkLFxuICAvLyBSaWdodCBkb3VibGUgcXVvdGF0aW9uIG1hcmtcbiAgMHhhMDogMHhjMCxcbiAgLy8gdXBwZXJjYXNlIEEsIGdyYXZlIGFjY2VudFxuICAweGExOiAweGMyLFxuICAvLyB1cHBlcmNhc2UgQSwgY2lyY3VtZmxleFxuICAweGEyOiAweGM3LFxuICAvLyB1cHBlcmNhc2UgQyB3aXRoIGNlZGlsbGFcbiAgMHhhMzogMHhjOCxcbiAgLy8gdXBwZXJjYXNlIEUsIGdyYXZlIGFjY2VudFxuICAweGE0OiAweGNhLFxuICAvLyB1cHBlcmNhc2UgRSwgY2lyY3VtZmxleFxuICAweGE1OiAweGNiLFxuICAvLyBjYXBpdGFsIGxldHRlciBFIHdpdGggZGlhcmVzaXNcbiAgMHhhNjogMHhlYixcbiAgLy8gbG93ZXJjYXNlIGxldHRlciBlIHdpdGggZGlhcmVzaXNcbiAgMHhhNzogMHhjZSxcbiAgLy8gdXBwZXJjYXNlIEksIGNpcmN1bWZsZXhcbiAgMHhhODogMHhjZixcbiAgLy8gdXBwZXJjYXNlIEksIHdpdGggZGlhcmVzaXNcbiAgMHhhOTogMHhlZixcbiAgLy8gbG93ZXJjYXNlIGksIHdpdGggZGlhcmVzaXNcbiAgMHhhYTogMHhkNCxcbiAgLy8gdXBwZXJjYXNlIE8sIGNpcmN1bWZsZXhcbiAgMHhhYjogMHhkOSxcbiAgLy8gdXBwZXJjYXNlIFUsIGdyYXZlIGFjY2VudFxuICAweGFjOiAweGY5LFxuICAvLyBsb3dlcmNhc2UgdSwgZ3JhdmUgYWNjZW50XG4gIDB4YWQ6IDB4ZGIsXG4gIC8vIHVwcGVyY2FzZSBVLCBjaXJjdW1mbGV4XG4gIDB4YWU6IDB4YWIsXG4gIC8vIGxlZnQtcG9pbnRpbmcgZG91YmxlIGFuZ2xlIHF1b3RhdGlvbiBtYXJrXG4gIDB4YWY6IDB4YmIsXG4gIC8vIHJpZ2h0LXBvaW50aW5nIGRvdWJsZSBhbmdsZSBxdW90YXRpb24gbWFya1xuICAvLyBUSElTIEJMT0NLIElOQ0xVREVTIFRIRSAzMiBFWFRFTkRFRCAoVFdPLUJZVEUpIExJTkUgMjEgQ0hBUkFDVEVSU1xuICAvLyBUSEFUIENPTUUgRlJPTSBISSBCWVRFPTB4MTMgQU5EIExPVyBCRVRXRUVOIDB4MjAgQU5EIDB4M0ZcbiAgMHhiMDogMHhjMyxcbiAgLy8gVXBwZXJjYXNlIEEsIHRpbGRlXG4gIDB4YjE6IDB4ZTMsXG4gIC8vIExvd2VyY2FzZSBhLCB0aWxkZVxuICAweGIyOiAweGNkLFxuICAvLyBVcHBlcmNhc2UgSSwgYWN1dGUgYWNjZW50XG4gIDB4YjM6IDB4Y2MsXG4gIC8vIFVwcGVyY2FzZSBJLCBncmF2ZSBhY2NlbnRcbiAgMHhiNDogMHhlYyxcbiAgLy8gTG93ZXJjYXNlIGksIGdyYXZlIGFjY2VudFxuICAweGI1OiAweGQyLFxuICAvLyBVcHBlcmNhc2UgTywgZ3JhdmUgYWNjZW50XG4gIDB4YjY6IDB4ZjIsXG4gIC8vIExvd2VyY2FzZSBvLCBncmF2ZSBhY2NlbnRcbiAgMHhiNzogMHhkNSxcbiAgLy8gVXBwZXJjYXNlIE8sIHRpbGRlXG4gIDB4Yjg6IDB4ZjUsXG4gIC8vIExvd2VyY2FzZSBvLCB0aWxkZVxuICAweGI5OiAweDdiLFxuICAvLyBPcGVuIGN1cmx5IGJyYWNlXG4gIDB4YmE6IDB4N2QsXG4gIC8vIENsb3NpbmcgY3VybHkgYnJhY2VcbiAgMHhiYjogMHg1YyxcbiAgLy8gQmFja3NsYXNoXG4gIDB4YmM6IDB4NWUsXG4gIC8vIENhcmV0XG4gIDB4YmQ6IDB4NWYsXG4gIC8vIFVuZGVyc2NvcmVcbiAgMHhiZTogMHg3YyxcbiAgLy8gUGlwZSAodmVydGljYWwgbGluZSlcbiAgMHhiZjogMHgyMjNjLFxuICAvLyBUaWxkZSBvcGVyYXRvclxuICAweGMwOiAweGM0LFxuICAvLyBVcHBlcmNhc2UgQSwgdW1sYXV0XG4gIDB4YzE6IDB4ZTQsXG4gIC8vIExvd2VyY2FzZSBBLCB1bWxhdXRcbiAgMHhjMjogMHhkNixcbiAgLy8gVXBwZXJjYXNlIE8sIHVtbGF1dFxuICAweGMzOiAweGY2LFxuICAvLyBMb3dlcmNhc2UgbywgdW1sYXV0XG4gIDB4YzQ6IDB4ZGYsXG4gIC8vIEVzc3pldHQgKHNoYXJwIFMpXG4gIDB4YzU6IDB4YTUsXG4gIC8vIFllbiBzeW1ib2xcbiAgMHhjNjogMHhhNCxcbiAgLy8gR2VuZXJpYyBjdXJyZW5jeSBzaWduXG4gIDB4Yzc6IDB4MjUwMyxcbiAgLy8gQm94IGRyYXdpbmdzIGhlYXZ5IHZlcnRpY2FsXG4gIDB4Yzg6IDB4YzUsXG4gIC8vIFVwcGVyY2FzZSBBLCByaW5nXG4gIDB4Yzk6IDB4ZTUsXG4gIC8vIExvd2VyY2FzZSBBLCByaW5nXG4gIDB4Y2E6IDB4ZDgsXG4gIC8vIFVwcGVyY2FzZSBPLCBzdHJva2VcbiAgMHhjYjogMHhmOCxcbiAgLy8gTG93ZXJjYXNlIG8sIHN0cm9rXG4gIDB4Y2M6IDB4MjUwZixcbiAgLy8gQm94IGRyYXdpbmdzIGhlYXZ5IGRvd24gYW5kIHJpZ2h0XG4gIDB4Y2Q6IDB4MjUxMyxcbiAgLy8gQm94IGRyYXdpbmdzIGhlYXZ5IGRvd24gYW5kIGxlZnRcbiAgMHhjZTogMHgyNTE3LFxuICAvLyBCb3ggZHJhd2luZ3MgaGVhdnkgdXAgYW5kIHJpZ2h0XG4gIDB4Y2Y6IDB4MjUxYiAvLyBCb3ggZHJhd2luZ3MgaGVhdnkgdXAgYW5kIGxlZnRcbn07XG5cbi8qKlxuICogVXRpbHNcbiAqL1xuY29uc3QgZ2V0Q2hhckZvckJ5dGUgPSBieXRlID0+IFN0cmluZy5mcm9tQ2hhckNvZGUoc3BlY2lhbENlYTYwOENoYXJzQ29kZXNbYnl0ZV0gfHwgYnl0ZSk7XG5jb25zdCBOUl9ST1dTID0gMTU7XG5jb25zdCBOUl9DT0xTID0gMTAwO1xuLy8gVGFibGVzIHRvIGxvb2sgdXAgcm93IGZyb20gUEFDIGRhdGFcbmNvbnN0IHJvd3NMb3dDaDEgPSB7XG4gIDB4MTE6IDEsXG4gIDB4MTI6IDMsXG4gIDB4MTU6IDUsXG4gIDB4MTY6IDcsXG4gIDB4MTc6IDksXG4gIDB4MTA6IDExLFxuICAweDEzOiAxMixcbiAgMHgxNDogMTRcbn07XG5jb25zdCByb3dzSGlnaENoMSA9IHtcbiAgMHgxMTogMixcbiAgMHgxMjogNCxcbiAgMHgxNTogNixcbiAgMHgxNjogOCxcbiAgMHgxNzogMTAsXG4gIDB4MTM6IDEzLFxuICAweDE0OiAxNVxufTtcbmNvbnN0IHJvd3NMb3dDaDIgPSB7XG4gIDB4MTk6IDEsXG4gIDB4MWE6IDMsXG4gIDB4MWQ6IDUsXG4gIDB4MWU6IDcsXG4gIDB4MWY6IDksXG4gIDB4MTg6IDExLFxuICAweDFiOiAxMixcbiAgMHgxYzogMTRcbn07XG5jb25zdCByb3dzSGlnaENoMiA9IHtcbiAgMHgxOTogMixcbiAgMHgxYTogNCxcbiAgMHgxZDogNixcbiAgMHgxZTogOCxcbiAgMHgxZjogMTAsXG4gIDB4MWI6IDEzLFxuICAweDFjOiAxNVxufTtcbmNvbnN0IGJhY2tncm91bmRDb2xvcnMgPSBbJ3doaXRlJywgJ2dyZWVuJywgJ2JsdWUnLCAnY3lhbicsICdyZWQnLCAneWVsbG93JywgJ21hZ2VudGEnLCAnYmxhY2snLCAndHJhbnNwYXJlbnQnXTtcbmNsYXNzIENhcHRpb25zTG9nZ2VyIHtcbiAgY29uc3RydWN0b3IoKSB7XG4gICAgdGhpcy50aW1lID0gbnVsbDtcbiAgICB0aGlzLnZlcmJvc2VMZXZlbCA9IDA7XG4gIH1cbiAgbG9nKHNldmVyaXR5LCBtc2cpIHtcbiAgICBpZiAodGhpcy52ZXJib3NlTGV2ZWwgPj0gc2V2ZXJpdHkpIHtcbiAgICAgIGNvbnN0IG0gPSB0eXBlb2YgbXNnID09PSAnZnVuY3Rpb24nID8gbXNnKCkgOiBtc2c7XG4gICAgICBsb2dnZXIubG9nKGAke3RoaXMudGltZX0gWyR7c2V2ZXJpdHl9XSAke219YCk7XG4gICAgfVxuICB9XG59XG5jb25zdCBudW1BcnJheVRvSGV4QXJyYXkgPSBmdW5jdGlvbiBudW1BcnJheVRvSGV4QXJyYXkobnVtQXJyYXkpIHtcbiAgY29uc3QgaGV4QXJyYXkgPSBbXTtcbiAgZm9yIChsZXQgaiA9IDA7IGogPCBudW1BcnJheS5sZW5ndGg7IGorKykge1xuICAgIGhleEFycmF5LnB1c2gobnVtQXJyYXlbal0udG9TdHJpbmcoMTYpKTtcbiAgfVxuICByZXR1cm4gaGV4QXJyYXk7XG59O1xuY2xhc3MgUGVuU3RhdGUge1xuICBjb25zdHJ1Y3RvcigpIHtcbiAgICB0aGlzLmZvcmVncm91bmQgPSAnd2hpdGUnO1xuICAgIHRoaXMudW5kZXJsaW5lID0gZmFsc2U7XG4gICAgdGhpcy5pdGFsaWNzID0gZmFsc2U7XG4gICAgdGhpcy5iYWNrZ3JvdW5kID0gJ2JsYWNrJztcbiAgICB0aGlzLmZsYXNoID0gZmFsc2U7XG4gIH1cbiAgcmVzZXQoKSB7XG4gICAgdGhpcy5mb3JlZ3JvdW5kID0gJ3doaXRlJztcbiAgICB0aGlzLnVuZGVybGluZSA9IGZhbHNlO1xuICAgIHRoaXMuaXRhbGljcyA9IGZhbHNlO1xuICAgIHRoaXMuYmFja2dyb3VuZCA9ICdibGFjayc7XG4gICAgdGhpcy5mbGFzaCA9IGZhbHNlO1xuICB9XG4gIHNldFN0eWxlcyhzdHlsZXMpIHtcbiAgICBjb25zdCBhdHRyaWJzID0gWydmb3JlZ3JvdW5kJywgJ3VuZGVybGluZScsICdpdGFsaWNzJywgJ2JhY2tncm91bmQnLCAnZmxhc2gnXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGF0dHJpYnMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IHN0eWxlID0gYXR0cmlic1tpXTtcbiAgICAgIGlmIChzdHlsZXMuaGFzT3duUHJvcGVydHkoc3R5bGUpKSB7XG4gICAgICAgIHRoaXNbc3R5bGVdID0gc3R5bGVzW3N0eWxlXTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgaXNEZWZhdWx0KCkge1xuICAgIHJldHVybiB0aGlzLmZvcmVncm91bmQgPT09ICd3aGl0ZScgJiYgIXRoaXMudW5kZXJsaW5lICYmICF0aGlzLml0YWxpY3MgJiYgdGhpcy5iYWNrZ3JvdW5kID09PSAnYmxhY2snICYmICF0aGlzLmZsYXNoO1xuICB9XG4gIGVxdWFscyhvdGhlcikge1xuICAgIHJldHVybiB0aGlzLmZvcmVncm91bmQgPT09IG90aGVyLmZvcmVncm91bmQgJiYgdGhpcy51bmRlcmxpbmUgPT09IG90aGVyLnVuZGVybGluZSAmJiB0aGlzLml0YWxpY3MgPT09IG90aGVyLml0YWxpY3MgJiYgdGhpcy5iYWNrZ3JvdW5kID09PSBvdGhlci5iYWNrZ3JvdW5kICYmIHRoaXMuZmxhc2ggPT09IG90aGVyLmZsYXNoO1xuICB9XG4gIGNvcHkobmV3UGVuU3RhdGUpIHtcbiAgICB0aGlzLmZvcmVncm91bmQgPSBuZXdQZW5TdGF0ZS5mb3JlZ3JvdW5kO1xuICAgIHRoaXMudW5kZXJsaW5lID0gbmV3UGVuU3RhdGUudW5kZXJsaW5lO1xuICAgIHRoaXMuaXRhbGljcyA9IG5ld1BlblN0YXRlLml0YWxpY3M7XG4gICAgdGhpcy5iYWNrZ3JvdW5kID0gbmV3UGVuU3RhdGUuYmFja2dyb3VuZDtcbiAgICB0aGlzLmZsYXNoID0gbmV3UGVuU3RhdGUuZmxhc2g7XG4gIH1cbiAgdG9TdHJpbmcoKSB7XG4gICAgcmV0dXJuICdjb2xvcj0nICsgdGhpcy5mb3JlZ3JvdW5kICsgJywgdW5kZXJsaW5lPScgKyB0aGlzLnVuZGVybGluZSArICcsIGl0YWxpY3M9JyArIHRoaXMuaXRhbGljcyArICcsIGJhY2tncm91bmQ9JyArIHRoaXMuYmFja2dyb3VuZCArICcsIGZsYXNoPScgKyB0aGlzLmZsYXNoO1xuICB9XG59XG5cbi8qKlxuICogVW5pY29kZSBjaGFyYWN0ZXIgd2l0aCBzdHlsaW5nIGFuZCBiYWNrZ3JvdW5kLlxuICogQGNvbnN0cnVjdG9yXG4gKi9cbmNsYXNzIFN0eWxlZFVuaWNvZGVDaGFyIHtcbiAgY29uc3RydWN0b3IoKSB7XG4gICAgdGhpcy51Y2hhciA9ICcgJztcbiAgICB0aGlzLnBlblN0YXRlID0gbmV3IFBlblN0YXRlKCk7XG4gIH1cbiAgcmVzZXQoKSB7XG4gICAgdGhpcy51Y2hhciA9ICcgJztcbiAgICB0aGlzLnBlblN0YXRlLnJlc2V0KCk7XG4gIH1cbiAgc2V0Q2hhcih1Y2hhciwgbmV3UGVuU3RhdGUpIHtcbiAgICB0aGlzLnVjaGFyID0gdWNoYXI7XG4gICAgdGhpcy5wZW5TdGF0ZS5jb3B5KG5ld1BlblN0YXRlKTtcbiAgfVxuICBzZXRQZW5TdGF0ZShuZXdQZW5TdGF0ZSkge1xuICAgIHRoaXMucGVuU3RhdGUuY29weShuZXdQZW5TdGF0ZSk7XG4gIH1cbiAgZXF1YWxzKG90aGVyKSB7XG4gICAgcmV0dXJuIHRoaXMudWNoYXIgPT09IG90aGVyLnVjaGFyICYmIHRoaXMucGVuU3RhdGUuZXF1YWxzKG90aGVyLnBlblN0YXRlKTtcbiAgfVxuICBjb3B5KG5ld0NoYXIpIHtcbiAgICB0aGlzLnVjaGFyID0gbmV3Q2hhci51Y2hhcjtcbiAgICB0aGlzLnBlblN0YXRlLmNvcHkobmV3Q2hhci5wZW5TdGF0ZSk7XG4gIH1cbiAgaXNFbXB0eSgpIHtcbiAgICByZXR1cm4gdGhpcy51Y2hhciA9PT0gJyAnICYmIHRoaXMucGVuU3RhdGUuaXNEZWZhdWx0KCk7XG4gIH1cbn1cblxuLyoqXG4gKiBDRUEtNjA4IHJvdyBjb25zaXN0aW5nIG9mIE5SX0NPTFMgaW5zdGFuY2VzIG9mIFN0eWxlZFVuaWNvZGVDaGFyLlxuICogQGNvbnN0cnVjdG9yXG4gKi9cbmNsYXNzIFJvdyB7XG4gIGNvbnN0cnVjdG9yKGxvZ2dlcikge1xuICAgIHRoaXMuY2hhcnMgPSBbXTtcbiAgICB0aGlzLnBvcyA9IDA7XG4gICAgdGhpcy5jdXJyUGVuU3RhdGUgPSBuZXcgUGVuU3RhdGUoKTtcbiAgICB0aGlzLmN1ZVN0YXJ0VGltZSA9IG51bGw7XG4gICAgdGhpcy5sb2dnZXIgPSB2b2lkIDA7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBOUl9DT0xTOyBpKyspIHtcbiAgICAgIHRoaXMuY2hhcnMucHVzaChuZXcgU3R5bGVkVW5pY29kZUNoYXIoKSk7XG4gICAgfVxuICAgIHRoaXMubG9nZ2VyID0gbG9nZ2VyO1xuICB9XG4gIGVxdWFscyhvdGhlcikge1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgTlJfQ09MUzsgaSsrKSB7XG4gICAgICBpZiAoIXRoaXMuY2hhcnNbaV0uZXF1YWxzKG90aGVyLmNoYXJzW2ldKSkge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xuICB9XG4gIGNvcHkob3RoZXIpIHtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IE5SX0NPTFM7IGkrKykge1xuICAgICAgdGhpcy5jaGFyc1tpXS5jb3B5KG90aGVyLmNoYXJzW2ldKTtcbiAgICB9XG4gIH1cbiAgaXNFbXB0eSgpIHtcbiAgICBsZXQgZW1wdHkgPSB0cnVlO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgTlJfQ09MUzsgaSsrKSB7XG4gICAgICBpZiAoIXRoaXMuY2hhcnNbaV0uaXNFbXB0eSgpKSB7XG4gICAgICAgIGVtcHR5ID0gZmFsc2U7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZW1wdHk7XG4gIH1cblxuICAvKipcbiAgICogIFNldCB0aGUgY3Vyc29yIHRvIGEgdmFsaWQgY29sdW1uLlxuICAgKi9cbiAgc2V0Q3Vyc29yKGFic1Bvcykge1xuICAgIGlmICh0aGlzLnBvcyAhPT0gYWJzUG9zKSB7XG4gICAgICB0aGlzLnBvcyA9IGFic1BvcztcbiAgICB9XG4gICAgaWYgKHRoaXMucG9zIDwgMCkge1xuICAgICAgdGhpcy5sb2dnZXIubG9nKDMsICdOZWdhdGl2ZSBjdXJzb3IgcG9zaXRpb24gJyArIHRoaXMucG9zKTtcbiAgICAgIHRoaXMucG9zID0gMDtcbiAgICB9IGVsc2UgaWYgKHRoaXMucG9zID4gTlJfQ09MUykge1xuICAgICAgdGhpcy5sb2dnZXIubG9nKDMsICdUb28gbGFyZ2UgY3Vyc29yIHBvc2l0aW9uICcgKyB0aGlzLnBvcyk7XG4gICAgICB0aGlzLnBvcyA9IE5SX0NPTFM7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIE1vdmUgdGhlIGN1cnNvciByZWxhdGl2ZSB0byBjdXJyZW50IHBvc2l0aW9uLlxuICAgKi9cbiAgbW92ZUN1cnNvcihyZWxQb3MpIHtcbiAgICBjb25zdCBuZXdQb3MgPSB0aGlzLnBvcyArIHJlbFBvcztcbiAgICBpZiAocmVsUG9zID4gMSkge1xuICAgICAgZm9yIChsZXQgaSA9IHRoaXMucG9zICsgMTsgaSA8IG5ld1BvcyArIDE7IGkrKykge1xuICAgICAgICB0aGlzLmNoYXJzW2ldLnNldFBlblN0YXRlKHRoaXMuY3VyclBlblN0YXRlKTtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5zZXRDdXJzb3IobmV3UG9zKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBCYWNrc3BhY2UsIG1vdmUgb25lIHN0ZXAgYmFjayBhbmQgY2xlYXIgY2hhcmFjdGVyLlxuICAgKi9cbiAgYmFja1NwYWNlKCkge1xuICAgIHRoaXMubW92ZUN1cnNvcigtMSk7XG4gICAgdGhpcy5jaGFyc1t0aGlzLnBvc10uc2V0Q2hhcignICcsIHRoaXMuY3VyclBlblN0YXRlKTtcbiAgfVxuICBpbnNlcnRDaGFyKGJ5dGUpIHtcbiAgICBpZiAoYnl0ZSA+PSAweDkwKSB7XG4gICAgICAvLyBFeHRlbmRlZCBjaGFyXG4gICAgICB0aGlzLmJhY2tTcGFjZSgpO1xuICAgIH1cbiAgICBjb25zdCBjaGFyID0gZ2V0Q2hhckZvckJ5dGUoYnl0ZSk7XG4gICAgaWYgKHRoaXMucG9zID49IE5SX0NPTFMpIHtcbiAgICAgIHRoaXMubG9nZ2VyLmxvZygwLCAoKSA9PiAnQ2Fubm90IGluc2VydCAnICsgYnl0ZS50b1N0cmluZygxNikgKyAnICgnICsgY2hhciArICcpIGF0IHBvc2l0aW9uICcgKyB0aGlzLnBvcyArICcuIFNraXBwaW5nIGl0IScpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLmNoYXJzW3RoaXMucG9zXS5zZXRDaGFyKGNoYXIsIHRoaXMuY3VyclBlblN0YXRlKTtcbiAgICB0aGlzLm1vdmVDdXJzb3IoMSk7XG4gIH1cbiAgY2xlYXJGcm9tUG9zKHN0YXJ0UG9zKSB7XG4gICAgbGV0IGk7XG4gICAgZm9yIChpID0gc3RhcnRQb3M7IGkgPCBOUl9DT0xTOyBpKyspIHtcbiAgICAgIHRoaXMuY2hhcnNbaV0ucmVzZXQoKTtcbiAgICB9XG4gIH1cbiAgY2xlYXIoKSB7XG4gICAgdGhpcy5jbGVhckZyb21Qb3MoMCk7XG4gICAgdGhpcy5wb3MgPSAwO1xuICAgIHRoaXMuY3VyclBlblN0YXRlLnJlc2V0KCk7XG4gIH1cbiAgY2xlYXJUb0VuZE9mUm93KCkge1xuICAgIHRoaXMuY2xlYXJGcm9tUG9zKHRoaXMucG9zKTtcbiAgfVxuICBnZXRUZXh0U3RyaW5nKCkge1xuICAgIGNvbnN0IGNoYXJzID0gW107XG4gICAgbGV0IGVtcHR5ID0gdHJ1ZTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IE5SX0NPTFM7IGkrKykge1xuICAgICAgY29uc3QgY2hhciA9IHRoaXMuY2hhcnNbaV0udWNoYXI7XG4gICAgICBpZiAoY2hhciAhPT0gJyAnKSB7XG4gICAgICAgIGVtcHR5ID0gZmFsc2U7XG4gICAgICB9XG4gICAgICBjaGFycy5wdXNoKGNoYXIpO1xuICAgIH1cbiAgICBpZiAoZW1wdHkpIHtcbiAgICAgIHJldHVybiAnJztcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIGNoYXJzLmpvaW4oJycpO1xuICAgIH1cbiAgfVxuICBzZXRQZW5TdHlsZXMoc3R5bGVzKSB7XG4gICAgdGhpcy5jdXJyUGVuU3RhdGUuc2V0U3R5bGVzKHN0eWxlcyk7XG4gICAgY29uc3QgY3VyckNoYXIgPSB0aGlzLmNoYXJzW3RoaXMucG9zXTtcbiAgICBjdXJyQ2hhci5zZXRQZW5TdGF0ZSh0aGlzLmN1cnJQZW5TdGF0ZSk7XG4gIH1cbn1cblxuLyoqXG4gKiBLZWVwIGEgQ0VBLTYwOCBzY3JlZW4gb2YgMzJ4MTUgc3R5bGVkIGNoYXJhY3RlcnNcbiAqIEBjb25zdHJ1Y3RvclxuICovXG5jbGFzcyBDYXB0aW9uU2NyZWVuIHtcbiAgY29uc3RydWN0b3IobG9nZ2VyKSB7XG4gICAgdGhpcy5yb3dzID0gW107XG4gICAgdGhpcy5jdXJyUm93ID0gTlJfUk9XUyAtIDE7XG4gICAgdGhpcy5uclJvbGxVcFJvd3MgPSBudWxsO1xuICAgIHRoaXMubGFzdE91dHB1dFNjcmVlbiA9IG51bGw7XG4gICAgdGhpcy5sb2dnZXIgPSB2b2lkIDA7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBOUl9ST1dTOyBpKyspIHtcbiAgICAgIHRoaXMucm93cy5wdXNoKG5ldyBSb3cobG9nZ2VyKSk7XG4gICAgfVxuICAgIHRoaXMubG9nZ2VyID0gbG9nZ2VyO1xuICB9XG4gIHJlc2V0KCkge1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgTlJfUk9XUzsgaSsrKSB7XG4gICAgICB0aGlzLnJvd3NbaV0uY2xlYXIoKTtcbiAgICB9XG4gICAgdGhpcy5jdXJyUm93ID0gTlJfUk9XUyAtIDE7XG4gIH1cbiAgZXF1YWxzKG90aGVyKSB7XG4gICAgbGV0IGVxdWFsID0gdHJ1ZTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IE5SX1JPV1M7IGkrKykge1xuICAgICAgaWYgKCF0aGlzLnJvd3NbaV0uZXF1YWxzKG90aGVyLnJvd3NbaV0pKSB7XG4gICAgICAgIGVxdWFsID0gZmFsc2U7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZXF1YWw7XG4gIH1cbiAgY29weShvdGhlcikge1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgTlJfUk9XUzsgaSsrKSB7XG4gICAgICB0aGlzLnJvd3NbaV0uY29weShvdGhlci5yb3dzW2ldKTtcbiAgICB9XG4gIH1cbiAgaXNFbXB0eSgpIHtcbiAgICBsZXQgZW1wdHkgPSB0cnVlO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgTlJfUk9XUzsgaSsrKSB7XG4gICAgICBpZiAoIXRoaXMucm93c1tpXS5pc0VtcHR5KCkpIHtcbiAgICAgICAgZW1wdHkgPSBmYWxzZTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBlbXB0eTtcbiAgfVxuICBiYWNrU3BhY2UoKSB7XG4gICAgY29uc3Qgcm93ID0gdGhpcy5yb3dzW3RoaXMuY3VyclJvd107XG4gICAgcm93LmJhY2tTcGFjZSgpO1xuICB9XG4gIGNsZWFyVG9FbmRPZlJvdygpIHtcbiAgICBjb25zdCByb3cgPSB0aGlzLnJvd3NbdGhpcy5jdXJyUm93XTtcbiAgICByb3cuY2xlYXJUb0VuZE9mUm93KCk7XG4gIH1cblxuICAvKipcbiAgICogSW5zZXJ0IGEgY2hhcmFjdGVyICh3aXRob3V0IHN0eWxpbmcpIGluIHRoZSBjdXJyZW50IHJvdy5cbiAgICovXG4gIGluc2VydENoYXIoY2hhcikge1xuICAgIGNvbnN0IHJvdyA9IHRoaXMucm93c1t0aGlzLmN1cnJSb3ddO1xuICAgIHJvdy5pbnNlcnRDaGFyKGNoYXIpO1xuICB9XG4gIHNldFBlbihzdHlsZXMpIHtcbiAgICBjb25zdCByb3cgPSB0aGlzLnJvd3NbdGhpcy5jdXJyUm93XTtcbiAgICByb3cuc2V0UGVuU3R5bGVzKHN0eWxlcyk7XG4gIH1cbiAgbW92ZUN1cnNvcihyZWxQb3MpIHtcbiAgICBjb25zdCByb3cgPSB0aGlzLnJvd3NbdGhpcy5jdXJyUm93XTtcbiAgICByb3cubW92ZUN1cnNvcihyZWxQb3MpO1xuICB9XG4gIHNldEN1cnNvcihhYnNQb3MpIHtcbiAgICB0aGlzLmxvZ2dlci5sb2coMiwgJ3NldEN1cnNvcjogJyArIGFic1Bvcyk7XG4gICAgY29uc3Qgcm93ID0gdGhpcy5yb3dzW3RoaXMuY3VyclJvd107XG4gICAgcm93LnNldEN1cnNvcihhYnNQb3MpO1xuICB9XG4gIHNldFBBQyhwYWNEYXRhKSB7XG4gICAgdGhpcy5sb2dnZXIubG9nKDIsICgpID0+ICdwYWNEYXRhID0gJyArIEpTT04uc3RyaW5naWZ5KHBhY0RhdGEpKTtcbiAgICBsZXQgbmV3Um93ID0gcGFjRGF0YS5yb3cgLSAxO1xuICAgIGlmICh0aGlzLm5yUm9sbFVwUm93cyAmJiBuZXdSb3cgPCB0aGlzLm5yUm9sbFVwUm93cyAtIDEpIHtcbiAgICAgIG5ld1JvdyA9IHRoaXMubnJSb2xsVXBSb3dzIC0gMTtcbiAgICB9XG5cbiAgICAvLyBNYWtlIHN1cmUgdGhpcyBvbmx5IGFmZmVjdHMgUm9sbC11cCBDYXB0aW9ucyBieSBjaGVja2luZyB0aGlzLm5yUm9sbFVwUm93c1xuICAgIGlmICh0aGlzLm5yUm9sbFVwUm93cyAmJiB0aGlzLmN1cnJSb3cgIT09IG5ld1Jvdykge1xuICAgICAgLy8gY2xlYXIgYWxsIHJvd3MgZmlyc3RcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgTlJfUk9XUzsgaSsrKSB7XG4gICAgICAgIHRoaXMucm93c1tpXS5jbGVhcigpO1xuICAgICAgfVxuXG4gICAgICAvLyBDb3B5IHRoaXMubnJSb2xsVXBSb3dzIHJvd3MgZnJvbSBsYXN0T3V0cHV0U2NyZWVuIGFuZCBwbGFjZSBpdCBpbiB0aGUgbmV3Um93IGxvY2F0aW9uXG4gICAgICAvLyB0b3BSb3dJbmRleCAtIHRoZSBzdGFydCBvZiByb3dzIHRvIGNvcHkgKGluY2x1c2l2ZSBpbmRleClcbiAgICAgIGNvbnN0IHRvcFJvd0luZGV4ID0gdGhpcy5jdXJyUm93ICsgMSAtIHRoaXMubnJSb2xsVXBSb3dzO1xuICAgICAgLy8gV2Ugb25seSBjb3B5IGlmIHRoZSBsYXN0IHBvc2l0aW9uIHdhcyBhbHJlYWR5IHNob3duLlxuICAgICAgLy8gV2UgdXNlIHRoZSBjdWVTdGFydFRpbWUgdmFsdWUgdG8gY2hlY2sgdGhpcy5cbiAgICAgIGNvbnN0IGxhc3RPdXRwdXRTY3JlZW4gPSB0aGlzLmxhc3RPdXRwdXRTY3JlZW47XG4gICAgICBpZiAobGFzdE91dHB1dFNjcmVlbikge1xuICAgICAgICBjb25zdCBwcmV2TGluZVRpbWUgPSBsYXN0T3V0cHV0U2NyZWVuLnJvd3NbdG9wUm93SW5kZXhdLmN1ZVN0YXJ0VGltZTtcbiAgICAgICAgY29uc3QgdGltZSA9IHRoaXMubG9nZ2VyLnRpbWU7XG4gICAgICAgIGlmIChwcmV2TGluZVRpbWUgIT09IG51bGwgJiYgdGltZSAhPT0gbnVsbCAmJiBwcmV2TGluZVRpbWUgPCB0aW1lKSB7XG4gICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLm5yUm9sbFVwUm93czsgaSsrKSB7XG4gICAgICAgICAgICB0aGlzLnJvd3NbbmV3Um93IC0gdGhpcy5uclJvbGxVcFJvd3MgKyBpICsgMV0uY29weShsYXN0T3V0cHV0U2NyZWVuLnJvd3NbdG9wUm93SW5kZXggKyBpXSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMuY3VyclJvdyA9IG5ld1JvdztcbiAgICBjb25zdCByb3cgPSB0aGlzLnJvd3NbdGhpcy5jdXJyUm93XTtcbiAgICBpZiAocGFjRGF0YS5pbmRlbnQgIT09IG51bGwpIHtcbiAgICAgIGNvbnN0IGluZGVudCA9IHBhY0RhdGEuaW5kZW50O1xuICAgICAgY29uc3QgcHJldlBvcyA9IE1hdGgubWF4KGluZGVudCAtIDEsIDApO1xuICAgICAgcm93LnNldEN1cnNvcihwYWNEYXRhLmluZGVudCk7XG4gICAgICBwYWNEYXRhLmNvbG9yID0gcm93LmNoYXJzW3ByZXZQb3NdLnBlblN0YXRlLmZvcmVncm91bmQ7XG4gICAgfVxuICAgIGNvbnN0IHN0eWxlcyA9IHtcbiAgICAgIGZvcmVncm91bmQ6IHBhY0RhdGEuY29sb3IsXG4gICAgICB1bmRlcmxpbmU6IHBhY0RhdGEudW5kZXJsaW5lLFxuICAgICAgaXRhbGljczogcGFjRGF0YS5pdGFsaWNzLFxuICAgICAgYmFja2dyb3VuZDogJ2JsYWNrJyxcbiAgICAgIGZsYXNoOiBmYWxzZVxuICAgIH07XG4gICAgdGhpcy5zZXRQZW4oc3R5bGVzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXQgYmFja2dyb3VuZC9leHRyYSBmb3JlZ3JvdW5kLCBidXQgZmlyc3QgZG8gYmFja19zcGFjZSwgYW5kIHRoZW4gaW5zZXJ0IHNwYWNlIChiYWNrd2FyZHMgY29tcGF0aWJpbGl0eSkuXG4gICAqL1xuICBzZXRCa2dEYXRhKGJrZ0RhdGEpIHtcbiAgICB0aGlzLmxvZ2dlci5sb2coMiwgKCkgPT4gJ2JrZ0RhdGEgPSAnICsgSlNPTi5zdHJpbmdpZnkoYmtnRGF0YSkpO1xuICAgIHRoaXMuYmFja1NwYWNlKCk7XG4gICAgdGhpcy5zZXRQZW4oYmtnRGF0YSk7XG4gICAgdGhpcy5pbnNlcnRDaGFyKDB4MjApOyAvLyBTcGFjZVxuICB9XG4gIHNldFJvbGxVcFJvd3MobnJSb3dzKSB7XG4gICAgdGhpcy5uclJvbGxVcFJvd3MgPSBuclJvd3M7XG4gIH1cbiAgcm9sbFVwKCkge1xuICAgIGlmICh0aGlzLm5yUm9sbFVwUm93cyA9PT0gbnVsbCkge1xuICAgICAgdGhpcy5sb2dnZXIubG9nKDMsICdyb2xsX3VwIGJ1dCBuclJvbGxVcFJvd3Mgbm90IHNldCB5ZXQnKTtcbiAgICAgIHJldHVybjsgLy8gTm90IHByb3Blcmx5IHNldHVwXG4gICAgfVxuICAgIHRoaXMubG9nZ2VyLmxvZygxLCAoKSA9PiB0aGlzLmdldERpc3BsYXlUZXh0KCkpO1xuICAgIGNvbnN0IHRvcFJvd0luZGV4ID0gdGhpcy5jdXJyUm93ICsgMSAtIHRoaXMubnJSb2xsVXBSb3dzO1xuICAgIGNvbnN0IHRvcFJvdyA9IHRoaXMucm93cy5zcGxpY2UodG9wUm93SW5kZXgsIDEpWzBdO1xuICAgIHRvcFJvdy5jbGVhcigpO1xuICAgIHRoaXMucm93cy5zcGxpY2UodGhpcy5jdXJyUm93LCAwLCB0b3BSb3cpO1xuICAgIHRoaXMubG9nZ2VyLmxvZygyLCAnUm9sbGluZyB1cCcpO1xuICAgIC8vIHRoaXMubG9nZ2VyLmxvZyhWZXJib3NlTGV2ZWwuVEVYVCwgdGhpcy5nZXRfZGlzcGxheV90ZXh0KCkpXG4gIH1cblxuICAvKipcbiAgICogR2V0IGFsbCBub24tZW1wdHkgcm93cyB3aXRoIGFzIHVuaWNvZGUgdGV4dC5cbiAgICovXG4gIGdldERpc3BsYXlUZXh0KGFzT25lUm93KSB7XG4gICAgYXNPbmVSb3cgPSBhc09uZVJvdyB8fCBmYWxzZTtcbiAgICBjb25zdCBkaXNwbGF5VGV4dCA9IFtdO1xuICAgIGxldCB0ZXh0ID0gJyc7XG4gICAgbGV0IHJvd05yID0gLTE7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBOUl9ST1dTOyBpKyspIHtcbiAgICAgIGNvbnN0IHJvd1RleHQgPSB0aGlzLnJvd3NbaV0uZ2V0VGV4dFN0cmluZygpO1xuICAgICAgaWYgKHJvd1RleHQpIHtcbiAgICAgICAgcm93TnIgPSBpICsgMTtcbiAgICAgICAgaWYgKGFzT25lUm93KSB7XG4gICAgICAgICAgZGlzcGxheVRleHQucHVzaCgnUm93ICcgKyByb3dOciArIFwiOiAnXCIgKyByb3dUZXh0ICsgXCInXCIpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGRpc3BsYXlUZXh0LnB1c2gocm93VGV4dC50cmltKCkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIGlmIChkaXNwbGF5VGV4dC5sZW5ndGggPiAwKSB7XG4gICAgICBpZiAoYXNPbmVSb3cpIHtcbiAgICAgICAgdGV4dCA9ICdbJyArIGRpc3BsYXlUZXh0LmpvaW4oJyB8ICcpICsgJ10nO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGV4dCA9IGRpc3BsYXlUZXh0LmpvaW4oJ1xcbicpO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gdGV4dDtcbiAgfVxuICBnZXRUZXh0QW5kRm9ybWF0KCkge1xuICAgIHJldHVybiB0aGlzLnJvd3M7XG4gIH1cbn1cblxuLy8gdmFyIG1vZGVzID0gWydNT0RFX1JPTEwtVVAnLCAnTU9ERV9QT1AtT04nLCAnTU9ERV9QQUlOVC1PTicsICdNT0RFX1RFWFQnXTtcblxuY2xhc3MgQ2VhNjA4Q2hhbm5lbCB7XG4gIGNvbnN0cnVjdG9yKGNoYW5uZWxOdW1iZXIsIG91dHB1dEZpbHRlciwgbG9nZ2VyKSB7XG4gICAgdGhpcy5jaE5yID0gdm9pZCAwO1xuICAgIHRoaXMub3V0cHV0RmlsdGVyID0gdm9pZCAwO1xuICAgIHRoaXMubW9kZSA9IHZvaWQgMDtcbiAgICB0aGlzLnZlcmJvc2UgPSB2b2lkIDA7XG4gICAgdGhpcy5kaXNwbGF5ZWRNZW1vcnkgPSB2b2lkIDA7XG4gICAgdGhpcy5ub25EaXNwbGF5ZWRNZW1vcnkgPSB2b2lkIDA7XG4gICAgdGhpcy5sYXN0T3V0cHV0U2NyZWVuID0gdm9pZCAwO1xuICAgIHRoaXMuY3VyclJvbGxVcFJvdyA9IHZvaWQgMDtcbiAgICB0aGlzLndyaXRlU2NyZWVuID0gdm9pZCAwO1xuICAgIHRoaXMuY3VlU3RhcnRUaW1lID0gdm9pZCAwO1xuICAgIHRoaXMubG9nZ2VyID0gdm9pZCAwO1xuICAgIHRoaXMuY2hOciA9IGNoYW5uZWxOdW1iZXI7XG4gICAgdGhpcy5vdXRwdXRGaWx0ZXIgPSBvdXRwdXRGaWx0ZXI7XG4gICAgdGhpcy5tb2RlID0gbnVsbDtcbiAgICB0aGlzLnZlcmJvc2UgPSAwO1xuICAgIHRoaXMuZGlzcGxheWVkTWVtb3J5ID0gbmV3IENhcHRpb25TY3JlZW4obG9nZ2VyKTtcbiAgICB0aGlzLm5vbkRpc3BsYXllZE1lbW9yeSA9IG5ldyBDYXB0aW9uU2NyZWVuKGxvZ2dlcik7XG4gICAgdGhpcy5sYXN0T3V0cHV0U2NyZWVuID0gbmV3IENhcHRpb25TY3JlZW4obG9nZ2VyKTtcbiAgICB0aGlzLmN1cnJSb2xsVXBSb3cgPSB0aGlzLmRpc3BsYXllZE1lbW9yeS5yb3dzW05SX1JPV1MgLSAxXTtcbiAgICB0aGlzLndyaXRlU2NyZWVuID0gdGhpcy5kaXNwbGF5ZWRNZW1vcnk7XG4gICAgdGhpcy5tb2RlID0gbnVsbDtcbiAgICB0aGlzLmN1ZVN0YXJ0VGltZSA9IG51bGw7IC8vIEtlZXBzIHRyYWNrIG9mIHdoZXJlIGEgY3VlIHN0YXJ0ZWQuXG4gICAgdGhpcy5sb2dnZXIgPSBsb2dnZXI7XG4gIH1cbiAgcmVzZXQoKSB7XG4gICAgdGhpcy5tb2RlID0gbnVsbDtcbiAgICB0aGlzLmRpc3BsYXllZE1lbW9yeS5yZXNldCgpO1xuICAgIHRoaXMubm9uRGlzcGxheWVkTWVtb3J5LnJlc2V0KCk7XG4gICAgdGhpcy5sYXN0T3V0cHV0U2NyZWVuLnJlc2V0KCk7XG4gICAgdGhpcy5vdXRwdXRGaWx0ZXIucmVzZXQoKTtcbiAgICB0aGlzLmN1cnJSb2xsVXBSb3cgPSB0aGlzLmRpc3BsYXllZE1lbW9yeS5yb3dzW05SX1JPV1MgLSAxXTtcbiAgICB0aGlzLndyaXRlU2NyZWVuID0gdGhpcy5kaXNwbGF5ZWRNZW1vcnk7XG4gICAgdGhpcy5tb2RlID0gbnVsbDtcbiAgICB0aGlzLmN1ZVN0YXJ0VGltZSA9IG51bGw7XG4gIH1cbiAgZ2V0SGFuZGxlcigpIHtcbiAgICByZXR1cm4gdGhpcy5vdXRwdXRGaWx0ZXI7XG4gIH1cbiAgc2V0SGFuZGxlcihuZXdIYW5kbGVyKSB7XG4gICAgdGhpcy5vdXRwdXRGaWx0ZXIgPSBuZXdIYW5kbGVyO1xuICB9XG4gIHNldFBBQyhwYWNEYXRhKSB7XG4gICAgdGhpcy53cml0ZVNjcmVlbi5zZXRQQUMocGFjRGF0YSk7XG4gIH1cbiAgc2V0QmtnRGF0YShia2dEYXRhKSB7XG4gICAgdGhpcy53cml0ZVNjcmVlbi5zZXRCa2dEYXRhKGJrZ0RhdGEpO1xuICB9XG4gIHNldE1vZGUobmV3TW9kZSkge1xuICAgIGlmIChuZXdNb2RlID09PSB0aGlzLm1vZGUpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5tb2RlID0gbmV3TW9kZTtcbiAgICB0aGlzLmxvZ2dlci5sb2coMiwgKCkgPT4gJ01PREU9JyArIG5ld01vZGUpO1xuICAgIGlmICh0aGlzLm1vZGUgPT09ICdNT0RFX1BPUC1PTicpIHtcbiAgICAgIHRoaXMud3JpdGVTY3JlZW4gPSB0aGlzLm5vbkRpc3BsYXllZE1lbW9yeTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy53cml0ZVNjcmVlbiA9IHRoaXMuZGlzcGxheWVkTWVtb3J5O1xuICAgICAgdGhpcy53cml0ZVNjcmVlbi5yZXNldCgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5tb2RlICE9PSAnTU9ERV9ST0xMLVVQJykge1xuICAgICAgdGhpcy5kaXNwbGF5ZWRNZW1vcnkubnJSb2xsVXBSb3dzID0gbnVsbDtcbiAgICAgIHRoaXMubm9uRGlzcGxheWVkTWVtb3J5Lm5yUm9sbFVwUm93cyA9IG51bGw7XG4gICAgfVxuICAgIHRoaXMubW9kZSA9IG5ld01vZGU7XG4gIH1cbiAgaW5zZXJ0Q2hhcnMoY2hhcnMpIHtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGNoYXJzLmxlbmd0aDsgaSsrKSB7XG4gICAgICB0aGlzLndyaXRlU2NyZWVuLmluc2VydENoYXIoY2hhcnNbaV0pO1xuICAgIH1cbiAgICBjb25zdCBzY3JlZW4gPSB0aGlzLndyaXRlU2NyZWVuID09PSB0aGlzLmRpc3BsYXllZE1lbW9yeSA/ICdESVNQJyA6ICdOT05fRElTUCc7XG4gICAgdGhpcy5sb2dnZXIubG9nKDIsICgpID0+IHNjcmVlbiArICc6ICcgKyB0aGlzLndyaXRlU2NyZWVuLmdldERpc3BsYXlUZXh0KHRydWUpKTtcbiAgICBpZiAodGhpcy5tb2RlID09PSAnTU9ERV9QQUlOVC1PTicgfHwgdGhpcy5tb2RlID09PSAnTU9ERV9ST0xMLVVQJykge1xuICAgICAgdGhpcy5sb2dnZXIubG9nKDEsICgpID0+ICdESVNQTEFZRUQ6ICcgKyB0aGlzLmRpc3BsYXllZE1lbW9yeS5nZXREaXNwbGF5VGV4dCh0cnVlKSk7XG4gICAgICB0aGlzLm91dHB1dERhdGFVcGRhdGUoKTtcbiAgICB9XG4gIH1cbiAgY2NSQ0woKSB7XG4gICAgLy8gUmVzdW1lIENhcHRpb24gTG9hZGluZyAoc3dpdGNoIG1vZGUgdG8gUG9wIE9uKVxuICAgIHRoaXMubG9nZ2VyLmxvZygyLCAnUkNMIC0gUmVzdW1lIENhcHRpb24gTG9hZGluZycpO1xuICAgIHRoaXMuc2V0TW9kZSgnTU9ERV9QT1AtT04nKTtcbiAgfVxuICBjY0JTKCkge1xuICAgIC8vIEJhY2tTcGFjZVxuICAgIHRoaXMubG9nZ2VyLmxvZygyLCAnQlMgLSBCYWNrU3BhY2UnKTtcbiAgICBpZiAodGhpcy5tb2RlID09PSAnTU9ERV9URVhUJykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLndyaXRlU2NyZWVuLmJhY2tTcGFjZSgpO1xuICAgIGlmICh0aGlzLndyaXRlU2NyZWVuID09PSB0aGlzLmRpc3BsYXllZE1lbW9yeSkge1xuICAgICAgdGhpcy5vdXRwdXREYXRhVXBkYXRlKCk7XG4gICAgfVxuICB9XG4gIGNjQU9GKCkge1xuICAgIC8vIFJlc2VydmVkIChmb3JtZXJseSBBbGFybSBPZmYpXG4gIH1cbiAgY2NBT04oKSB7XG4gICAgLy8gUmVzZXJ2ZWQgKGZvcm1lcmx5IEFsYXJtIE9uKVxuICB9XG4gIGNjREVSKCkge1xuICAgIC8vIERlbGV0ZSB0byBFbmQgb2YgUm93XG4gICAgdGhpcy5sb2dnZXIubG9nKDIsICdERVItIERlbGV0ZSB0byBFbmQgb2YgUm93Jyk7XG4gICAgdGhpcy53cml0ZVNjcmVlbi5jbGVhclRvRW5kT2ZSb3coKTtcbiAgICB0aGlzLm91dHB1dERhdGFVcGRhdGUoKTtcbiAgfVxuICBjY1JVKG5yUm93cykge1xuICAgIC8vIFJvbGwtVXAgQ2FwdGlvbnMtMiwzLG9yIDQgUm93c1xuICAgIHRoaXMubG9nZ2VyLmxvZygyLCAnUlUoJyArIG5yUm93cyArICcpIC0gUm9sbCBVcCcpO1xuICAgIHRoaXMud3JpdGVTY3JlZW4gPSB0aGlzLmRpc3BsYXllZE1lbW9yeTtcbiAgICB0aGlzLnNldE1vZGUoJ01PREVfUk9MTC1VUCcpO1xuICAgIHRoaXMud3JpdGVTY3JlZW4uc2V0Um9sbFVwUm93cyhuclJvd3MpO1xuICB9XG4gIGNjRk9OKCkge1xuICAgIC8vIEZsYXNoIE9uXG4gICAgdGhpcy5sb2dnZXIubG9nKDIsICdGT04gLSBGbGFzaCBPbicpO1xuICAgIHRoaXMud3JpdGVTY3JlZW4uc2V0UGVuKHtcbiAgICAgIGZsYXNoOiB0cnVlXG4gICAgfSk7XG4gIH1cbiAgY2NSREMoKSB7XG4gICAgLy8gUmVzdW1lIERpcmVjdCBDYXB0aW9uaW5nIChzd2l0Y2ggbW9kZSB0byBQYWludE9uKVxuICAgIHRoaXMubG9nZ2VyLmxvZygyLCAnUkRDIC0gUmVzdW1lIERpcmVjdCBDYXB0aW9uaW5nJyk7XG4gICAgdGhpcy5zZXRNb2RlKCdNT0RFX1BBSU5ULU9OJyk7XG4gIH1cbiAgY2NUUigpIHtcbiAgICAvLyBUZXh0IFJlc3RhcnQgaW4gdGV4dCBtb2RlIChub3Qgc3VwcG9ydGVkLCBob3dldmVyKVxuICAgIHRoaXMubG9nZ2VyLmxvZygyLCAnVFInKTtcbiAgICB0aGlzLnNldE1vZGUoJ01PREVfVEVYVCcpO1xuICB9XG4gIGNjUlREKCkge1xuICAgIC8vIFJlc3VtZSBUZXh0IERpc3BsYXkgaW4gVGV4dCBtb2RlIChub3Qgc3VwcG9ydGVkLCBob3dldmVyKVxuICAgIHRoaXMubG9nZ2VyLmxvZygyLCAnUlREJyk7XG4gICAgdGhpcy5zZXRNb2RlKCdNT0RFX1RFWFQnKTtcbiAgfVxuICBjY0VETSgpIHtcbiAgICAvLyBFcmFzZSBEaXNwbGF5ZWQgTWVtb3J5XG4gICAgdGhpcy5sb2dnZXIubG9nKDIsICdFRE0gLSBFcmFzZSBEaXNwbGF5ZWQgTWVtb3J5Jyk7XG4gICAgdGhpcy5kaXNwbGF5ZWRNZW1vcnkucmVzZXQoKTtcbiAgICB0aGlzLm91dHB1dERhdGFVcGRhdGUodHJ1ZSk7XG4gIH1cbiAgY2NDUigpIHtcbiAgICAvLyBDYXJyaWFnZSBSZXR1cm5cbiAgICB0aGlzLmxvZ2dlci5sb2coMiwgJ0NSIC0gQ2FycmlhZ2UgUmV0dXJuJyk7XG4gICAgdGhpcy53cml0ZVNjcmVlbi5yb2xsVXAoKTtcbiAgICB0aGlzLm91dHB1dERhdGFVcGRhdGUodHJ1ZSk7XG4gIH1cbiAgY2NFTk0oKSB7XG4gICAgLy8gRXJhc2UgTm9uLURpc3BsYXllZCBNZW1vcnlcbiAgICB0aGlzLmxvZ2dlci5sb2coMiwgJ0VOTSAtIEVyYXNlIE5vbi1kaXNwbGF5ZWQgTWVtb3J5Jyk7XG4gICAgdGhpcy5ub25EaXNwbGF5ZWRNZW1vcnkucmVzZXQoKTtcbiAgfVxuICBjY0VPQygpIHtcbiAgICAvLyBFbmQgb2YgQ2FwdGlvbiAoRmxpcCBNZW1vcmllcylcbiAgICB0aGlzLmxvZ2dlci5sb2coMiwgJ0VPQyAtIEVuZCBPZiBDYXB0aW9uJyk7XG4gICAgaWYgKHRoaXMubW9kZSA9PT0gJ01PREVfUE9QLU9OJykge1xuICAgICAgY29uc3QgdG1wID0gdGhpcy5kaXNwbGF5ZWRNZW1vcnk7XG4gICAgICB0aGlzLmRpc3BsYXllZE1lbW9yeSA9IHRoaXMubm9uRGlzcGxheWVkTWVtb3J5O1xuICAgICAgdGhpcy5ub25EaXNwbGF5ZWRNZW1vcnkgPSB0bXA7XG4gICAgICB0aGlzLndyaXRlU2NyZWVuID0gdGhpcy5ub25EaXNwbGF5ZWRNZW1vcnk7XG4gICAgICB0aGlzLmxvZ2dlci5sb2coMSwgKCkgPT4gJ0RJU1A6ICcgKyB0aGlzLmRpc3BsYXllZE1lbW9yeS5nZXREaXNwbGF5VGV4dCgpKTtcbiAgICB9XG4gICAgdGhpcy5vdXRwdXREYXRhVXBkYXRlKHRydWUpO1xuICB9XG4gIGNjVE8obnJDb2xzKSB7XG4gICAgLy8gVGFiIE9mZnNldCAxLDIsIG9yIDMgY29sdW1uc1xuICAgIHRoaXMubG9nZ2VyLmxvZygyLCAnVE8oJyArIG5yQ29scyArICcpIC0gVGFiIE9mZnNldCcpO1xuICAgIHRoaXMud3JpdGVTY3JlZW4ubW92ZUN1cnNvcihuckNvbHMpO1xuICB9XG4gIGNjTUlEUk9XKHNlY29uZEJ5dGUpIHtcbiAgICAvLyBQYXJzZSBNSURST1cgY29tbWFuZFxuICAgIGNvbnN0IHN0eWxlcyA9IHtcbiAgICAgIGZsYXNoOiBmYWxzZVxuICAgIH07XG4gICAgc3R5bGVzLnVuZGVybGluZSA9IHNlY29uZEJ5dGUgJSAyID09PSAxO1xuICAgIHN0eWxlcy5pdGFsaWNzID0gc2Vjb25kQnl0ZSA+PSAweDJlO1xuICAgIGlmICghc3R5bGVzLml0YWxpY3MpIHtcbiAgICAgIGNvbnN0IGNvbG9ySW5kZXggPSBNYXRoLmZsb29yKHNlY29uZEJ5dGUgLyAyKSAtIDB4MTA7XG4gICAgICBjb25zdCBjb2xvcnMgPSBbJ3doaXRlJywgJ2dyZWVuJywgJ2JsdWUnLCAnY3lhbicsICdyZWQnLCAneWVsbG93JywgJ21hZ2VudGEnXTtcbiAgICAgIHN0eWxlcy5mb3JlZ3JvdW5kID0gY29sb3JzW2NvbG9ySW5kZXhdO1xuICAgIH0gZWxzZSB7XG4gICAgICBzdHlsZXMuZm9yZWdyb3VuZCA9ICd3aGl0ZSc7XG4gICAgfVxuICAgIHRoaXMubG9nZ2VyLmxvZygyLCAnTUlEUk9XOiAnICsgSlNPTi5zdHJpbmdpZnkoc3R5bGVzKSk7XG4gICAgdGhpcy53cml0ZVNjcmVlbi5zZXRQZW4oc3R5bGVzKTtcbiAgfVxuICBvdXRwdXREYXRhVXBkYXRlKGRpc3BhdGNoID0gZmFsc2UpIHtcbiAgICBjb25zdCB0aW1lID0gdGhpcy5sb2dnZXIudGltZTtcbiAgICBpZiAodGltZSA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAodGhpcy5vdXRwdXRGaWx0ZXIpIHtcbiAgICAgIGlmICh0aGlzLmN1ZVN0YXJ0VGltZSA9PT0gbnVsbCAmJiAhdGhpcy5kaXNwbGF5ZWRNZW1vcnkuaXNFbXB0eSgpKSB7XG4gICAgICAgIC8vIFN0YXJ0IG9mIGEgbmV3IGN1ZVxuICAgICAgICB0aGlzLmN1ZVN0YXJ0VGltZSA9IHRpbWU7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpZiAoIXRoaXMuZGlzcGxheWVkTWVtb3J5LmVxdWFscyh0aGlzLmxhc3RPdXRwdXRTY3JlZW4pKSB7XG4gICAgICAgICAgdGhpcy5vdXRwdXRGaWx0ZXIubmV3Q3VlKHRoaXMuY3VlU3RhcnRUaW1lLCB0aW1lLCB0aGlzLmxhc3RPdXRwdXRTY3JlZW4pO1xuICAgICAgICAgIGlmIChkaXNwYXRjaCAmJiB0aGlzLm91dHB1dEZpbHRlci5kaXNwYXRjaEN1ZSkge1xuICAgICAgICAgICAgdGhpcy5vdXRwdXRGaWx0ZXIuZGlzcGF0Y2hDdWUoKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgdGhpcy5jdWVTdGFydFRpbWUgPSB0aGlzLmRpc3BsYXllZE1lbW9yeS5pc0VtcHR5KCkgPyBudWxsIDogdGltZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgdGhpcy5sYXN0T3V0cHV0U2NyZWVuLmNvcHkodGhpcy5kaXNwbGF5ZWRNZW1vcnkpO1xuICAgIH1cbiAgfVxuICBjdWVTcGxpdEF0VGltZSh0KSB7XG4gICAgaWYgKHRoaXMub3V0cHV0RmlsdGVyKSB7XG4gICAgICBpZiAoIXRoaXMuZGlzcGxheWVkTWVtb3J5LmlzRW1wdHkoKSkge1xuICAgICAgICBpZiAodGhpcy5vdXRwdXRGaWx0ZXIubmV3Q3VlKSB7XG4gICAgICAgICAgdGhpcy5vdXRwdXRGaWx0ZXIubmV3Q3VlKHRoaXMuY3VlU3RhcnRUaW1lLCB0LCB0aGlzLmRpc3BsYXllZE1lbW9yeSk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5jdWVTdGFydFRpbWUgPSB0O1xuICAgICAgfVxuICAgIH1cbiAgfVxufVxuXG4vLyBXaWxsIGJlIDEgb3IgMiB3aGVuIHBhcnNpbmcgY2FwdGlvbnNcblxuY2xhc3MgQ2VhNjA4UGFyc2VyIHtcbiAgY29uc3RydWN0b3IoZmllbGQsIG91dDEsIG91dDIpIHtcbiAgICB0aGlzLmNoYW5uZWxzID0gdm9pZCAwO1xuICAgIHRoaXMuY3VycmVudENoYW5uZWwgPSAwO1xuICAgIHRoaXMuY21kSGlzdG9yeSA9IGNyZWF0ZUNtZEhpc3RvcnkoKTtcbiAgICB0aGlzLmxvZ2dlciA9IHZvaWQgMDtcbiAgICBjb25zdCBsb2dnZXIgPSB0aGlzLmxvZ2dlciA9IG5ldyBDYXB0aW9uc0xvZ2dlcigpO1xuICAgIHRoaXMuY2hhbm5lbHMgPSBbbnVsbCwgbmV3IENlYTYwOENoYW5uZWwoZmllbGQsIG91dDEsIGxvZ2dlciksIG5ldyBDZWE2MDhDaGFubmVsKGZpZWxkICsgMSwgb3V0MiwgbG9nZ2VyKV07XG4gIH1cbiAgZ2V0SGFuZGxlcihjaGFubmVsKSB7XG4gICAgcmV0dXJuIHRoaXMuY2hhbm5lbHNbY2hhbm5lbF0uZ2V0SGFuZGxlcigpO1xuICB9XG4gIHNldEhhbmRsZXIoY2hhbm5lbCwgbmV3SGFuZGxlcikge1xuICAgIHRoaXMuY2hhbm5lbHNbY2hhbm5lbF0uc2V0SGFuZGxlcihuZXdIYW5kbGVyKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGQgZGF0YSBmb3IgdGltZSB0IGluIGZvcm1zIG9mIGxpc3Qgb2YgYnl0ZXMgKHVuc2lnbmVkIGludHMpLiBUaGUgYnl0ZXMgYXJlIHRyZWF0ZWQgYXMgcGFpcnMuXG4gICAqL1xuICBhZGREYXRhKHRpbWUsIGJ5dGVMaXN0KSB7XG4gICAgdGhpcy5sb2dnZXIudGltZSA9IHRpbWU7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBieXRlTGlzdC5sZW5ndGg7IGkgKz0gMikge1xuICAgICAgY29uc3QgYSA9IGJ5dGVMaXN0W2ldICYgMHg3ZjtcbiAgICAgIGNvbnN0IGIgPSBieXRlTGlzdFtpICsgMV0gJiAweDdmO1xuICAgICAgbGV0IGNtZEZvdW5kID0gZmFsc2U7XG4gICAgICBsZXQgY2hhcnNGb3VuZCA9IG51bGw7XG4gICAgICBpZiAoYSA9PT0gMCAmJiBiID09PSAwKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5sb2dnZXIubG9nKDMsICgpID0+ICdbJyArIG51bUFycmF5VG9IZXhBcnJheShbYnl0ZUxpc3RbaV0sIGJ5dGVMaXN0W2kgKyAxXV0pICsgJ10gLT4gKCcgKyBudW1BcnJheVRvSGV4QXJyYXkoW2EsIGJdKSArICcpJyk7XG4gICAgICB9XG4gICAgICBjb25zdCBjbWRIaXN0b3J5ID0gdGhpcy5jbWRIaXN0b3J5O1xuICAgICAgY29uc3QgaXNDb250cm9sQ29kZSA9IGEgPj0gMHgxMCAmJiBhIDw9IDB4MWY7XG4gICAgICBpZiAoaXNDb250cm9sQ29kZSkge1xuICAgICAgICAvLyBTa2lwIHJlZHVuZGFudCBjb250cm9sIGNvZGVzXG4gICAgICAgIGlmIChoYXNDbWRSZXBlYXRlZChhLCBiLCBjbWRIaXN0b3J5KSkge1xuICAgICAgICAgIHNldExhc3RDbWQobnVsbCwgbnVsbCwgY21kSGlzdG9yeSk7XG4gICAgICAgICAgdGhpcy5sb2dnZXIubG9nKDMsICgpID0+ICdSZXBlYXRlZCBjb21tYW5kICgnICsgbnVtQXJyYXlUb0hleEFycmF5KFthLCBiXSkgKyAnKSBpcyBkcm9wcGVkJyk7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgc2V0TGFzdENtZChhLCBiLCB0aGlzLmNtZEhpc3RvcnkpO1xuICAgICAgICBjbWRGb3VuZCA9IHRoaXMucGFyc2VDbWQoYSwgYik7XG4gICAgICAgIGlmICghY21kRm91bmQpIHtcbiAgICAgICAgICBjbWRGb3VuZCA9IHRoaXMucGFyc2VNaWRyb3coYSwgYik7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFjbWRGb3VuZCkge1xuICAgICAgICAgIGNtZEZvdW5kID0gdGhpcy5wYXJzZVBBQyhhLCBiKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIWNtZEZvdW5kKSB7XG4gICAgICAgICAgY21kRm91bmQgPSB0aGlzLnBhcnNlQmFja2dyb3VuZEF0dHJpYnV0ZXMoYSwgYik7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHNldExhc3RDbWQobnVsbCwgbnVsbCwgY21kSGlzdG9yeSk7XG4gICAgICB9XG4gICAgICBpZiAoIWNtZEZvdW5kKSB7XG4gICAgICAgIGNoYXJzRm91bmQgPSB0aGlzLnBhcnNlQ2hhcnMoYSwgYik7XG4gICAgICAgIGlmIChjaGFyc0ZvdW5kKSB7XG4gICAgICAgICAgY29uc3QgY3VyckNoTnIgPSB0aGlzLmN1cnJlbnRDaGFubmVsO1xuICAgICAgICAgIGlmIChjdXJyQ2hOciAmJiBjdXJyQ2hOciA+IDApIHtcbiAgICAgICAgICAgIGNvbnN0IGNoYW5uZWwgPSB0aGlzLmNoYW5uZWxzW2N1cnJDaE5yXTtcbiAgICAgICAgICAgIGNoYW5uZWwuaW5zZXJ0Q2hhcnMoY2hhcnNGb3VuZCk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMubG9nZ2VyLmxvZygyLCAnTm8gY2hhbm5lbCBmb3VuZCB5ZXQuIFRFWFQtTU9ERT8nKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmICghY21kRm91bmQgJiYgIWNoYXJzRm91bmQpIHtcbiAgICAgICAgdGhpcy5sb2dnZXIubG9nKDIsICgpID0+IFwiQ291bGRuJ3QgcGFyc2UgY2xlYW5lZCBkYXRhIFwiICsgbnVtQXJyYXlUb0hleEFycmF5KFthLCBiXSkgKyAnIG9yaWc6ICcgKyBudW1BcnJheVRvSGV4QXJyYXkoW2J5dGVMaXN0W2ldLCBieXRlTGlzdFtpICsgMV1dKSk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFBhcnNlIENvbW1hbmQuXG4gICAqIEByZXR1cm5zIFRydWUgaWYgYSBjb21tYW5kIHdhcyBmb3VuZFxuICAgKi9cbiAgcGFyc2VDbWQoYSwgYikge1xuICAgIGNvbnN0IGNvbmQxID0gKGEgPT09IDB4MTQgfHwgYSA9PT0gMHgxYyB8fCBhID09PSAweDE1IHx8IGEgPT09IDB4MWQpICYmIGIgPj0gMHgyMCAmJiBiIDw9IDB4MmY7XG4gICAgY29uc3QgY29uZDIgPSAoYSA9PT0gMHgxNyB8fCBhID09PSAweDFmKSAmJiBiID49IDB4MjEgJiYgYiA8PSAweDIzO1xuICAgIGlmICghKGNvbmQxIHx8IGNvbmQyKSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBjb25zdCBjaE5yID0gYSA9PT0gMHgxNCB8fCBhID09PSAweDE1IHx8IGEgPT09IDB4MTcgPyAxIDogMjtcbiAgICBjb25zdCBjaGFubmVsID0gdGhpcy5jaGFubmVsc1tjaE5yXTtcbiAgICBpZiAoYSA9PT0gMHgxNCB8fCBhID09PSAweDE1IHx8IGEgPT09IDB4MWMgfHwgYSA9PT0gMHgxZCkge1xuICAgICAgaWYgKGIgPT09IDB4MjApIHtcbiAgICAgICAgY2hhbm5lbC5jY1JDTCgpO1xuICAgICAgfSBlbHNlIGlmIChiID09PSAweDIxKSB7XG4gICAgICAgIGNoYW5uZWwuY2NCUygpO1xuICAgICAgfSBlbHNlIGlmIChiID09PSAweDIyKSB7XG4gICAgICAgIGNoYW5uZWwuY2NBT0YoKTtcbiAgICAgIH0gZWxzZSBpZiAoYiA9PT0gMHgyMykge1xuICAgICAgICBjaGFubmVsLmNjQU9OKCk7XG4gICAgICB9IGVsc2UgaWYgKGIgPT09IDB4MjQpIHtcbiAgICAgICAgY2hhbm5lbC5jY0RFUigpO1xuICAgICAgfSBlbHNlIGlmIChiID09PSAweDI1KSB7XG4gICAgICAgIGNoYW5uZWwuY2NSVSgyKTtcbiAgICAgIH0gZWxzZSBpZiAoYiA9PT0gMHgyNikge1xuICAgICAgICBjaGFubmVsLmNjUlUoMyk7XG4gICAgICB9IGVsc2UgaWYgKGIgPT09IDB4MjcpIHtcbiAgICAgICAgY2hhbm5lbC5jY1JVKDQpO1xuICAgICAgfSBlbHNlIGlmIChiID09PSAweDI4KSB7XG4gICAgICAgIGNoYW5uZWwuY2NGT04oKTtcbiAgICAgIH0gZWxzZSBpZiAoYiA9PT0gMHgyOSkge1xuICAgICAgICBjaGFubmVsLmNjUkRDKCk7XG4gICAgICB9IGVsc2UgaWYgKGIgPT09IDB4MmEpIHtcbiAgICAgICAgY2hhbm5lbC5jY1RSKCk7XG4gICAgICB9IGVsc2UgaWYgKGIgPT09IDB4MmIpIHtcbiAgICAgICAgY2hhbm5lbC5jY1JURCgpO1xuICAgICAgfSBlbHNlIGlmIChiID09PSAweDJjKSB7XG4gICAgICAgIGNoYW5uZWwuY2NFRE0oKTtcbiAgICAgIH0gZWxzZSBpZiAoYiA9PT0gMHgyZCkge1xuICAgICAgICBjaGFubmVsLmNjQ1IoKTtcbiAgICAgIH0gZWxzZSBpZiAoYiA9PT0gMHgyZSkge1xuICAgICAgICBjaGFubmVsLmNjRU5NKCk7XG4gICAgICB9IGVsc2UgaWYgKGIgPT09IDB4MmYpIHtcbiAgICAgICAgY2hhbm5lbC5jY0VPQygpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyBhID09IDB4MTcgfHwgYSA9PSAweDFGXG4gICAgICBjaGFubmVsLmNjVE8oYiAtIDB4MjApO1xuICAgIH1cbiAgICB0aGlzLmN1cnJlbnRDaGFubmVsID0gY2hOcjtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQYXJzZSBtaWRyb3cgc3R5bGluZyBjb21tYW5kXG4gICAqL1xuICBwYXJzZU1pZHJvdyhhLCBiKSB7XG4gICAgbGV0IGNoTnIgPSAwO1xuICAgIGlmICgoYSA9PT0gMHgxMSB8fCBhID09PSAweDE5KSAmJiBiID49IDB4MjAgJiYgYiA8PSAweDJmKSB7XG4gICAgICBpZiAoYSA9PT0gMHgxMSkge1xuICAgICAgICBjaE5yID0gMTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNoTnIgPSAyO1xuICAgICAgfVxuICAgICAgaWYgKGNoTnIgIT09IHRoaXMuY3VycmVudENoYW5uZWwpIHtcbiAgICAgICAgdGhpcy5sb2dnZXIubG9nKDAsICdNaXNtYXRjaCBjaGFubmVsIGluIG1pZHJvdyBwYXJzaW5nJyk7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGNoYW5uZWwgPSB0aGlzLmNoYW5uZWxzW2NoTnJdO1xuICAgICAgaWYgKCFjaGFubmVsKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICAgIGNoYW5uZWwuY2NNSURST1coYik7XG4gICAgICB0aGlzLmxvZ2dlci5sb2coMywgKCkgPT4gJ01JRFJPVyAoJyArIG51bUFycmF5VG9IZXhBcnJheShbYSwgYl0pICsgJyknKTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICAvKipcbiAgICogUGFyc2UgUHJlYWJsZSBBY2Nlc3MgQ29kZXMgKFRhYmxlIDUzKS5cbiAgICogQHJldHVybnMge0Jvb2xlYW59IFRlbGxzIGlmIFBBQyBmb3VuZFxuICAgKi9cbiAgcGFyc2VQQUMoYSwgYikge1xuICAgIGxldCByb3c7XG4gICAgY29uc3QgY2FzZTEgPSAoYSA+PSAweDExICYmIGEgPD0gMHgxNyB8fCBhID49IDB4MTkgJiYgYSA8PSAweDFmKSAmJiBiID49IDB4NDAgJiYgYiA8PSAweDdmO1xuICAgIGNvbnN0IGNhc2UyID0gKGEgPT09IDB4MTAgfHwgYSA9PT0gMHgxOCkgJiYgYiA+PSAweDQwICYmIGIgPD0gMHg1ZjtcbiAgICBpZiAoIShjYXNlMSB8fCBjYXNlMikpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgY29uc3QgY2hOciA9IGEgPD0gMHgxNyA/IDEgOiAyO1xuICAgIGlmIChiID49IDB4NDAgJiYgYiA8PSAweDVmKSB7XG4gICAgICByb3cgPSBjaE5yID09PSAxID8gcm93c0xvd0NoMVthXSA6IHJvd3NMb3dDaDJbYV07XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIDB4NjAgPD0gYiA8PSAweDdGXG4gICAgICByb3cgPSBjaE5yID09PSAxID8gcm93c0hpZ2hDaDFbYV0gOiByb3dzSGlnaENoMlthXTtcbiAgICB9XG4gICAgY29uc3QgY2hhbm5lbCA9IHRoaXMuY2hhbm5lbHNbY2hOcl07XG4gICAgaWYgKCFjaGFubmVsKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIGNoYW5uZWwuc2V0UEFDKHRoaXMuaW50ZXJwcmV0UEFDKHJvdywgYikpO1xuICAgIHRoaXMuY3VycmVudENoYW5uZWwgPSBjaE5yO1xuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqIEludGVycHJldCB0aGUgc2Vjb25kIGJ5dGUgb2YgdGhlIHBhYywgYW5kIHJldHVybiB0aGUgaW5mb3JtYXRpb24uXG4gICAqIEByZXR1cm5zIHBhY0RhdGEgd2l0aCBzdHlsZSBwYXJhbWV0ZXJzXG4gICAqL1xuICBpbnRlcnByZXRQQUMocm93LCBieXRlKSB7XG4gICAgbGV0IHBhY0luZGV4O1xuICAgIGNvbnN0IHBhY0RhdGEgPSB7XG4gICAgICBjb2xvcjogbnVsbCxcbiAgICAgIGl0YWxpY3M6IGZhbHNlLFxuICAgICAgaW5kZW50OiBudWxsLFxuICAgICAgdW5kZXJsaW5lOiBmYWxzZSxcbiAgICAgIHJvdzogcm93XG4gICAgfTtcbiAgICBpZiAoYnl0ZSA+IDB4NWYpIHtcbiAgICAgIHBhY0luZGV4ID0gYnl0ZSAtIDB4NjA7XG4gICAgfSBlbHNlIHtcbiAgICAgIHBhY0luZGV4ID0gYnl0ZSAtIDB4NDA7XG4gICAgfVxuICAgIHBhY0RhdGEudW5kZXJsaW5lID0gKHBhY0luZGV4ICYgMSkgPT09IDE7XG4gICAgaWYgKHBhY0luZGV4IDw9IDB4ZCkge1xuICAgICAgcGFjRGF0YS5jb2xvciA9IFsnd2hpdGUnLCAnZ3JlZW4nLCAnYmx1ZScsICdjeWFuJywgJ3JlZCcsICd5ZWxsb3cnLCAnbWFnZW50YScsICd3aGl0ZSddW01hdGguZmxvb3IocGFjSW5kZXggLyAyKV07XG4gICAgfSBlbHNlIGlmIChwYWNJbmRleCA8PSAweGYpIHtcbiAgICAgIHBhY0RhdGEuaXRhbGljcyA9IHRydWU7XG4gICAgICBwYWNEYXRhLmNvbG9yID0gJ3doaXRlJztcbiAgICB9IGVsc2Uge1xuICAgICAgcGFjRGF0YS5pbmRlbnQgPSBNYXRoLmZsb29yKChwYWNJbmRleCAtIDB4MTApIC8gMikgKiA0O1xuICAgIH1cbiAgICByZXR1cm4gcGFjRGF0YTsgLy8gTm90ZSB0aGF0IHJvdyBoYXMgemVybyBvZmZzZXQuIFRoZSBzcGVjIHVzZXMgMS5cbiAgfVxuXG4gIC8qKlxuICAgKiBQYXJzZSBjaGFyYWN0ZXJzLlxuICAgKiBAcmV0dXJucyBBbiBhcnJheSB3aXRoIDEgdG8gMiBjb2RlcyBjb3JyZXNwb25kaW5nIHRvIGNoYXJzLCBpZiBmb3VuZC4gbnVsbCBvdGhlcndpc2UuXG4gICAqL1xuICBwYXJzZUNoYXJzKGEsIGIpIHtcbiAgICBsZXQgY2hhbm5lbE5yO1xuICAgIGxldCBjaGFyQ29kZXMgPSBudWxsO1xuICAgIGxldCBjaGFyQ29kZTEgPSBudWxsO1xuICAgIGlmIChhID49IDB4MTkpIHtcbiAgICAgIGNoYW5uZWxOciA9IDI7XG4gICAgICBjaGFyQ29kZTEgPSBhIC0gODtcbiAgICB9IGVsc2Uge1xuICAgICAgY2hhbm5lbE5yID0gMTtcbiAgICAgIGNoYXJDb2RlMSA9IGE7XG4gICAgfVxuICAgIGlmIChjaGFyQ29kZTEgPj0gMHgxMSAmJiBjaGFyQ29kZTEgPD0gMHgxMykge1xuICAgICAgLy8gU3BlY2lhbCBjaGFyYWN0ZXJcbiAgICAgIGxldCBvbmVDb2RlO1xuICAgICAgaWYgKGNoYXJDb2RlMSA9PT0gMHgxMSkge1xuICAgICAgICBvbmVDb2RlID0gYiArIDB4NTA7XG4gICAgICB9IGVsc2UgaWYgKGNoYXJDb2RlMSA9PT0gMHgxMikge1xuICAgICAgICBvbmVDb2RlID0gYiArIDB4NzA7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBvbmVDb2RlID0gYiArIDB4OTA7XG4gICAgICB9XG4gICAgICB0aGlzLmxvZ2dlci5sb2coMiwgKCkgPT4gXCJTcGVjaWFsIGNoYXIgJ1wiICsgZ2V0Q2hhckZvckJ5dGUob25lQ29kZSkgKyBcIicgaW4gY2hhbm5lbCBcIiArIGNoYW5uZWxOcik7XG4gICAgICBjaGFyQ29kZXMgPSBbb25lQ29kZV07XG4gICAgfSBlbHNlIGlmIChhID49IDB4MjAgJiYgYSA8PSAweDdmKSB7XG4gICAgICBjaGFyQ29kZXMgPSBiID09PSAwID8gW2FdIDogW2EsIGJdO1xuICAgIH1cbiAgICBpZiAoY2hhckNvZGVzKSB7XG4gICAgICB0aGlzLmxvZ2dlci5sb2coMywgKCkgPT4gJ0NoYXIgY29kZXMgPSAgJyArIG51bUFycmF5VG9IZXhBcnJheShjaGFyQ29kZXMpLmpvaW4oJywnKSk7XG4gICAgfVxuICAgIHJldHVybiBjaGFyQ29kZXM7XG4gIH1cblxuICAvKipcbiAgICogUGFyc2UgZXh0ZW5kZWQgYmFja2dyb3VuZCBhdHRyaWJ1dGVzIGFzIHdlbGwgYXMgbmV3IGZvcmVncm91bmQgY29sb3IgYmxhY2suXG4gICAqIEByZXR1cm5zIFRydWUgaWYgYmFja2dyb3VuZCBhdHRyaWJ1dGVzIGFyZSBmb3VuZFxuICAgKi9cbiAgcGFyc2VCYWNrZ3JvdW5kQXR0cmlidXRlcyhhLCBiKSB7XG4gICAgY29uc3QgY2FzZTEgPSAoYSA9PT0gMHgxMCB8fCBhID09PSAweDE4KSAmJiBiID49IDB4MjAgJiYgYiA8PSAweDJmO1xuICAgIGNvbnN0IGNhc2UyID0gKGEgPT09IDB4MTcgfHwgYSA9PT0gMHgxZikgJiYgYiA+PSAweDJkICYmIGIgPD0gMHgyZjtcbiAgICBpZiAoIShjYXNlMSB8fCBjYXNlMikpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgbGV0IGluZGV4O1xuICAgIGNvbnN0IGJrZ0RhdGEgPSB7fTtcbiAgICBpZiAoYSA9PT0gMHgxMCB8fCBhID09PSAweDE4KSB7XG4gICAgICBpbmRleCA9IE1hdGguZmxvb3IoKGIgLSAweDIwKSAvIDIpO1xuICAgICAgYmtnRGF0YS5iYWNrZ3JvdW5kID0gYmFja2dyb3VuZENvbG9yc1tpbmRleF07XG4gICAgICBpZiAoYiAlIDIgPT09IDEpIHtcbiAgICAgICAgYmtnRGF0YS5iYWNrZ3JvdW5kID0gYmtnRGF0YS5iYWNrZ3JvdW5kICsgJ19zZW1pJztcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKGIgPT09IDB4MmQpIHtcbiAgICAgIGJrZ0RhdGEuYmFja2dyb3VuZCA9ICd0cmFuc3BhcmVudCc7XG4gICAgfSBlbHNlIHtcbiAgICAgIGJrZ0RhdGEuZm9yZWdyb3VuZCA9ICdibGFjayc7XG4gICAgICBpZiAoYiA9PT0gMHgyZikge1xuICAgICAgICBia2dEYXRhLnVuZGVybGluZSA9IHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIGNvbnN0IGNoTnIgPSBhIDw9IDB4MTcgPyAxIDogMjtcbiAgICBjb25zdCBjaGFubmVsID0gdGhpcy5jaGFubmVsc1tjaE5yXTtcbiAgICBjaGFubmVsLnNldEJrZ0RhdGEoYmtnRGF0YSk7XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICAvKipcbiAgICogUmVzZXQgc3RhdGUgb2YgcGFyc2VyIGFuZCBpdHMgY2hhbm5lbHMuXG4gICAqL1xuICByZXNldCgpIHtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IE9iamVjdC5rZXlzKHRoaXMuY2hhbm5lbHMpLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBjaGFubmVsID0gdGhpcy5jaGFubmVsc1tpXTtcbiAgICAgIGlmIChjaGFubmVsKSB7XG4gICAgICAgIGNoYW5uZWwucmVzZXQoKTtcbiAgICAgIH1cbiAgICB9XG4gICAgc2V0TGFzdENtZChudWxsLCBudWxsLCB0aGlzLmNtZEhpc3RvcnkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRyaWdnZXIgdGhlIGdlbmVyYXRpb24gb2YgYSBjdWUsIGFuZCB0aGUgc3RhcnQgb2YgYSBuZXcgb25lIGlmIGRpc3BsYXlTY3JlZW5zIGFyZSBub3QgZW1wdHkuXG4gICAqL1xuICBjdWVTcGxpdEF0VGltZSh0KSB7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLmNoYW5uZWxzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBjaGFubmVsID0gdGhpcy5jaGFubmVsc1tpXTtcbiAgICAgIGlmIChjaGFubmVsKSB7XG4gICAgICAgIGNoYW5uZWwuY3VlU3BsaXRBdFRpbWUodCk7XG4gICAgICB9XG4gICAgfVxuICB9XG59XG5mdW5jdGlvbiBzZXRMYXN0Q21kKGEsIGIsIGNtZEhpc3RvcnkpIHtcbiAgY21kSGlzdG9yeS5hID0gYTtcbiAgY21kSGlzdG9yeS5iID0gYjtcbn1cbmZ1bmN0aW9uIGhhc0NtZFJlcGVhdGVkKGEsIGIsIGNtZEhpc3RvcnkpIHtcbiAgcmV0dXJuIGNtZEhpc3RvcnkuYSA9PT0gYSAmJiBjbWRIaXN0b3J5LmIgPT09IGI7XG59XG5mdW5jdGlvbiBjcmVhdGVDbWRIaXN0b3J5KCkge1xuICByZXR1cm4ge1xuICAgIGE6IG51bGwsXG4gICAgYjogbnVsbFxuICB9O1xufVxuXG5jbGFzcyBPdXRwdXRGaWx0ZXIge1xuICBjb25zdHJ1Y3Rvcih0aW1lbGluZUNvbnRyb2xsZXIsIHRyYWNrTmFtZSkge1xuICAgIHRoaXMudGltZWxpbmVDb250cm9sbGVyID0gdm9pZCAwO1xuICAgIHRoaXMuY3VlUmFuZ2VzID0gW107XG4gICAgdGhpcy50cmFja05hbWUgPSB2b2lkIDA7XG4gICAgdGhpcy5zdGFydFRpbWUgPSBudWxsO1xuICAgIHRoaXMuZW5kVGltZSA9IG51bGw7XG4gICAgdGhpcy5zY3JlZW4gPSBudWxsO1xuICAgIHRoaXMudGltZWxpbmVDb250cm9sbGVyID0gdGltZWxpbmVDb250cm9sbGVyO1xuICAgIHRoaXMudHJhY2tOYW1lID0gdHJhY2tOYW1lO1xuICB9XG4gIGRpc3BhdGNoQ3VlKCkge1xuICAgIGlmICh0aGlzLnN0YXJ0VGltZSA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLnRpbWVsaW5lQ29udHJvbGxlci5hZGRDdWVzKHRoaXMudHJhY2tOYW1lLCB0aGlzLnN0YXJ0VGltZSwgdGhpcy5lbmRUaW1lLCB0aGlzLnNjcmVlbiwgdGhpcy5jdWVSYW5nZXMpO1xuICAgIHRoaXMuc3RhcnRUaW1lID0gbnVsbDtcbiAgfVxuICBuZXdDdWUoc3RhcnRUaW1lLCBlbmRUaW1lLCBzY3JlZW4pIHtcbiAgICBpZiAodGhpcy5zdGFydFRpbWUgPT09IG51bGwgfHwgdGhpcy5zdGFydFRpbWUgPiBzdGFydFRpbWUpIHtcbiAgICAgIHRoaXMuc3RhcnRUaW1lID0gc3RhcnRUaW1lO1xuICAgIH1cbiAgICB0aGlzLmVuZFRpbWUgPSBlbmRUaW1lO1xuICAgIHRoaXMuc2NyZWVuID0gc2NyZWVuO1xuICAgIHRoaXMudGltZWxpbmVDb250cm9sbGVyLmNyZWF0ZUNhcHRpb25zVHJhY2sodGhpcy50cmFja05hbWUpO1xuICB9XG4gIHJlc2V0KCkge1xuICAgIHRoaXMuY3VlUmFuZ2VzID0gW107XG4gICAgdGhpcy5zdGFydFRpbWUgPSBudWxsO1xuICB9XG59XG5cbi8qKlxuICogQ29weXJpZ2h0IDIwMTMgdnR0LmpzIENvbnRyaWJ1dG9yc1xuICpcbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAnTGljZW5zZScpO1xuICogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuICogWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4gKlxuICogICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiAnQVMgSVMnIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqL1xuXG52YXIgVlRUQ3VlID0gKGZ1bmN0aW9uICgpIHtcbiAgaWYgKG9wdGlvbmFsU2VsZiAhPSBudWxsICYmIG9wdGlvbmFsU2VsZi5WVFRDdWUpIHtcbiAgICByZXR1cm4gc2VsZi5WVFRDdWU7XG4gIH1cbiAgY29uc3QgQWxsb3dlZERpcmVjdGlvbnMgPSBbJycsICdscicsICdybCddO1xuICBjb25zdCBBbGxvd2VkQWxpZ25tZW50cyA9IFsnc3RhcnQnLCAnbWlkZGxlJywgJ2VuZCcsICdsZWZ0JywgJ3JpZ2h0J107XG4gIGZ1bmN0aW9uIGlzQWxsb3dlZFZhbHVlKGFsbG93ZWQsIHZhbHVlKSB7XG4gICAgaWYgKHR5cGVvZiB2YWx1ZSAhPT0gJ3N0cmluZycpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgLy8gbmVjZXNzYXJ5IGZvciBhc3N1cmluZyB0aGUgZ2VuZXJpYyBjb25mb3JtcyB0byB0aGUgQXJyYXkgaW50ZXJmYWNlXG4gICAgaWYgKCFBcnJheS5pc0FycmF5KGFsbG93ZWQpKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIC8vIHJlc2V0IHRoZSB0eXBlIHNvIHRoYXQgdGhlIG5leHQgbmFycm93aW5nIHdvcmtzIHdlbGxcbiAgICBjb25zdCBsY1ZhbHVlID0gdmFsdWUudG9Mb3dlckNhc2UoKTtcbiAgICAvLyB1c2UgdGhlIGFsbG93IGxpc3QgdG8gbmFycm93IHRoZSB0eXBlIHRvIGEgc3BlY2lmaWMgc3Vic2V0IG9mIHN0cmluZ3NcbiAgICBpZiAofmFsbG93ZWQuaW5kZXhPZihsY1ZhbHVlKSkge1xuICAgICAgcmV0dXJuIGxjVmFsdWU7XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICBmdW5jdGlvbiBmaW5kRGlyZWN0aW9uU2V0dGluZyh2YWx1ZSkge1xuICAgIHJldHVybiBpc0FsbG93ZWRWYWx1ZShBbGxvd2VkRGlyZWN0aW9ucywgdmFsdWUpO1xuICB9XG4gIGZ1bmN0aW9uIGZpbmRBbGlnblNldHRpbmcodmFsdWUpIHtcbiAgICByZXR1cm4gaXNBbGxvd2VkVmFsdWUoQWxsb3dlZEFsaWdubWVudHMsIHZhbHVlKTtcbiAgfVxuICBmdW5jdGlvbiBleHRlbmQob2JqLCAuLi5yZXN0KSB7XG4gICAgbGV0IGkgPSAxO1xuICAgIGZvciAoOyBpIDwgYXJndW1lbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBjb2JqID0gYXJndW1lbnRzW2ldO1xuICAgICAgZm9yIChjb25zdCBwIGluIGNvYmopIHtcbiAgICAgICAgb2JqW3BdID0gY29ialtwXTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG9iajtcbiAgfVxuICBmdW5jdGlvbiBWVFRDdWUoc3RhcnRUaW1lLCBlbmRUaW1lLCB0ZXh0KSB7XG4gICAgY29uc3QgY3VlID0gdGhpcztcbiAgICBjb25zdCBiYXNlT2JqID0ge1xuICAgICAgZW51bWVyYWJsZTogdHJ1ZVxuICAgIH07XG4gICAgLyoqXG4gICAgICogU2hpbSBpbXBsZW1lbnRhdGlvbiBzcGVjaWZpYyBwcm9wZXJ0aWVzLiBUaGVzZSBwcm9wZXJ0aWVzIGFyZSBub3QgaW5cbiAgICAgKiB0aGUgc3BlYy5cbiAgICAgKi9cblxuICAgIC8vIExldHMgdXMga25vdyB3aGVuIHRoZSBWVFRDdWUncyBkYXRhIGhhcyBjaGFuZ2VkIGluIHN1Y2ggYSB3YXkgdGhhdCB3ZSBuZWVkXG4gICAgLy8gdG8gcmVjb21wdXRlIGl0cyBkaXNwbGF5IHN0YXRlLiBUaGlzIGxldHMgdXMgY29tcHV0ZSBpdHMgZGlzcGxheSBzdGF0ZVxuICAgIC8vIGxhemlseS5cbiAgICBjdWUuaGFzQmVlblJlc2V0ID0gZmFsc2U7XG5cbiAgICAvKipcbiAgICAgKiBWVFRDdWUgYW5kIFRleHRUcmFja0N1ZSBwcm9wZXJ0aWVzXG4gICAgICogaHR0cDovL2Rldi53My5vcmcvaHRtbDUvd2VidnR0LyN2dHRjdWUtaW50ZXJmYWNlXG4gICAgICovXG5cbiAgICBsZXQgX2lkID0gJyc7XG4gICAgbGV0IF9wYXVzZU9uRXhpdCA9IGZhbHNlO1xuICAgIGxldCBfc3RhcnRUaW1lID0gc3RhcnRUaW1lO1xuICAgIGxldCBfZW5kVGltZSA9IGVuZFRpbWU7XG4gICAgbGV0IF90ZXh0ID0gdGV4dDtcbiAgICBsZXQgX3JlZ2lvbiA9IG51bGw7XG4gICAgbGV0IF92ZXJ0aWNhbCA9ICcnO1xuICAgIGxldCBfc25hcFRvTGluZXMgPSB0cnVlO1xuICAgIGxldCBfbGluZSA9ICdhdXRvJztcbiAgICBsZXQgX2xpbmVBbGlnbiA9ICdzdGFydCc7XG4gICAgbGV0IF9wb3NpdGlvbiA9IDUwO1xuICAgIGxldCBfcG9zaXRpb25BbGlnbiA9ICdtaWRkbGUnO1xuICAgIGxldCBfc2l6ZSA9IDUwO1xuICAgIGxldCBfYWxpZ24gPSAnbWlkZGxlJztcbiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkoY3VlLCAnaWQnLCBleHRlbmQoe30sIGJhc2VPYmosIHtcbiAgICAgIGdldDogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gX2lkO1xuICAgICAgfSxcbiAgICAgIHNldDogZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgIF9pZCA9ICcnICsgdmFsdWU7XG4gICAgICB9XG4gICAgfSkpO1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShjdWUsICdwYXVzZU9uRXhpdCcsIGV4dGVuZCh7fSwgYmFzZU9iaiwge1xuICAgICAgZ2V0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBfcGF1c2VPbkV4aXQ7XG4gICAgICB9LFxuICAgICAgc2V0OiBmdW5jdGlvbiAodmFsdWUpIHtcbiAgICAgICAgX3BhdXNlT25FeGl0ID0gISF2YWx1ZTtcbiAgICAgIH1cbiAgICB9KSk7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KGN1ZSwgJ3N0YXJ0VGltZScsIGV4dGVuZCh7fSwgYmFzZU9iaiwge1xuICAgICAgZ2V0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBfc3RhcnRUaW1lO1xuICAgICAgfSxcbiAgICAgIHNldDogZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgIGlmICh0eXBlb2YgdmFsdWUgIT09ICdudW1iZXInKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignU3RhcnQgdGltZSBtdXN0IGJlIHNldCB0byBhIG51bWJlci4nKTtcbiAgICAgICAgfVxuICAgICAgICBfc3RhcnRUaW1lID0gdmFsdWU7XG4gICAgICAgIHRoaXMuaGFzQmVlblJlc2V0ID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9KSk7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KGN1ZSwgJ2VuZFRpbWUnLCBleHRlbmQoe30sIGJhc2VPYmosIHtcbiAgICAgIGdldDogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gX2VuZFRpbWU7XG4gICAgICB9LFxuICAgICAgc2V0OiBmdW5jdGlvbiAodmFsdWUpIHtcbiAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSAhPT0gJ251bWJlcicpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdFbmQgdGltZSBtdXN0IGJlIHNldCB0byBhIG51bWJlci4nKTtcbiAgICAgICAgfVxuICAgICAgICBfZW5kVGltZSA9IHZhbHVlO1xuICAgICAgICB0aGlzLmhhc0JlZW5SZXNldCA9IHRydWU7XG4gICAgICB9XG4gICAgfSkpO1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShjdWUsICd0ZXh0JywgZXh0ZW5kKHt9LCBiYXNlT2JqLCB7XG4gICAgICBnZXQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIF90ZXh0O1xuICAgICAgfSxcbiAgICAgIHNldDogZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgIF90ZXh0ID0gJycgKyB2YWx1ZTtcbiAgICAgICAgdGhpcy5oYXNCZWVuUmVzZXQgPSB0cnVlO1xuICAgICAgfVxuICAgIH0pKTtcblxuICAgIC8vIHRvZG86IGltcGxlbWVudCBWVFRSZWdpb24gcG9seWZpbGw/XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KGN1ZSwgJ3JlZ2lvbicsIGV4dGVuZCh7fSwgYmFzZU9iaiwge1xuICAgICAgZ2V0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBfcmVnaW9uO1xuICAgICAgfSxcbiAgICAgIHNldDogZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgIF9yZWdpb24gPSB2YWx1ZTtcbiAgICAgICAgdGhpcy5oYXNCZWVuUmVzZXQgPSB0cnVlO1xuICAgICAgfVxuICAgIH0pKTtcbiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkoY3VlLCAndmVydGljYWwnLCBleHRlbmQoe30sIGJhc2VPYmosIHtcbiAgICAgIGdldDogZnVuY3Rpb24gKCkge1xuICAgICAgICByZXR1cm4gX3ZlcnRpY2FsO1xuICAgICAgfSxcbiAgICAgIHNldDogZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgIGNvbnN0IHNldHRpbmcgPSBmaW5kRGlyZWN0aW9uU2V0dGluZyh2YWx1ZSk7XG4gICAgICAgIC8vIEhhdmUgdG8gY2hlY2sgZm9yIGZhbHNlIGJlY2F1c2UgdGhlIHNldHRpbmcgYW4gYmUgYW4gZW1wdHkgc3RyaW5nLlxuICAgICAgICBpZiAoc2V0dGluZyA9PT0gZmFsc2UpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgU3ludGF4RXJyb3IoJ0FuIGludmFsaWQgb3IgaWxsZWdhbCBzdHJpbmcgd2FzIHNwZWNpZmllZC4nKTtcbiAgICAgICAgfVxuICAgICAgICBfdmVydGljYWwgPSBzZXR0aW5nO1xuICAgICAgICB0aGlzLmhhc0JlZW5SZXNldCA9IHRydWU7XG4gICAgICB9XG4gICAgfSkpO1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShjdWUsICdzbmFwVG9MaW5lcycsIGV4dGVuZCh7fSwgYmFzZU9iaiwge1xuICAgICAgZ2V0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBfc25hcFRvTGluZXM7XG4gICAgICB9LFxuICAgICAgc2V0OiBmdW5jdGlvbiAodmFsdWUpIHtcbiAgICAgICAgX3NuYXBUb0xpbmVzID0gISF2YWx1ZTtcbiAgICAgICAgdGhpcy5oYXNCZWVuUmVzZXQgPSB0cnVlO1xuICAgICAgfVxuICAgIH0pKTtcbiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkoY3VlLCAnbGluZScsIGV4dGVuZCh7fSwgYmFzZU9iaiwge1xuICAgICAgZ2V0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBfbGluZTtcbiAgICAgIH0sXG4gICAgICBzZXQ6IGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgICAgICBpZiAodHlwZW9mIHZhbHVlICE9PSAnbnVtYmVyJyAmJiB2YWx1ZSAhPT0gJ2F1dG8nKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IFN5bnRheEVycm9yKCdBbiBpbnZhbGlkIG51bWJlciBvciBpbGxlZ2FsIHN0cmluZyB3YXMgc3BlY2lmaWVkLicpO1xuICAgICAgICB9XG4gICAgICAgIF9saW5lID0gdmFsdWU7XG4gICAgICAgIHRoaXMuaGFzQmVlblJlc2V0ID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9KSk7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KGN1ZSwgJ2xpbmVBbGlnbicsIGV4dGVuZCh7fSwgYmFzZU9iaiwge1xuICAgICAgZ2V0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBfbGluZUFsaWduO1xuICAgICAgfSxcbiAgICAgIHNldDogZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgIGNvbnN0IHNldHRpbmcgPSBmaW5kQWxpZ25TZXR0aW5nKHZhbHVlKTtcbiAgICAgICAgaWYgKCFzZXR0aW5nKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IFN5bnRheEVycm9yKCdBbiBpbnZhbGlkIG9yIGlsbGVnYWwgc3RyaW5nIHdhcyBzcGVjaWZpZWQuJyk7XG4gICAgICAgIH1cbiAgICAgICAgX2xpbmVBbGlnbiA9IHNldHRpbmc7XG4gICAgICAgIHRoaXMuaGFzQmVlblJlc2V0ID0gdHJ1ZTtcbiAgICAgIH1cbiAgICB9KSk7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KGN1ZSwgJ3Bvc2l0aW9uJywgZXh0ZW5kKHt9LCBiYXNlT2JqLCB7XG4gICAgICBnZXQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIF9wb3NpdGlvbjtcbiAgICAgIH0sXG4gICAgICBzZXQ6IGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgICAgICBpZiAodmFsdWUgPCAwIHx8IHZhbHVlID4gMTAwKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdQb3NpdGlvbiBtdXN0IGJlIGJldHdlZW4gMCBhbmQgMTAwLicpO1xuICAgICAgICB9XG4gICAgICAgIF9wb3NpdGlvbiA9IHZhbHVlO1xuICAgICAgICB0aGlzLmhhc0JlZW5SZXNldCA9IHRydWU7XG4gICAgICB9XG4gICAgfSkpO1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShjdWUsICdwb3NpdGlvbkFsaWduJywgZXh0ZW5kKHt9LCBiYXNlT2JqLCB7XG4gICAgICBnZXQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIF9wb3NpdGlvbkFsaWduO1xuICAgICAgfSxcbiAgICAgIHNldDogZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgIGNvbnN0IHNldHRpbmcgPSBmaW5kQWxpZ25TZXR0aW5nKHZhbHVlKTtcbiAgICAgICAgaWYgKCFzZXR0aW5nKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IFN5bnRheEVycm9yKCdBbiBpbnZhbGlkIG9yIGlsbGVnYWwgc3RyaW5nIHdhcyBzcGVjaWZpZWQuJyk7XG4gICAgICAgIH1cbiAgICAgICAgX3Bvc2l0aW9uQWxpZ24gPSBzZXR0aW5nO1xuICAgICAgICB0aGlzLmhhc0JlZW5SZXNldCA9IHRydWU7XG4gICAgICB9XG4gICAgfSkpO1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShjdWUsICdzaXplJywgZXh0ZW5kKHt9LCBiYXNlT2JqLCB7XG4gICAgICBnZXQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIF9zaXplO1xuICAgICAgfSxcbiAgICAgIHNldDogZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgIGlmICh2YWx1ZSA8IDAgfHwgdmFsdWUgPiAxMDApIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1NpemUgbXVzdCBiZSBiZXR3ZWVuIDAgYW5kIDEwMC4nKTtcbiAgICAgICAgfVxuICAgICAgICBfc2l6ZSA9IHZhbHVlO1xuICAgICAgICB0aGlzLmhhc0JlZW5SZXNldCA9IHRydWU7XG4gICAgICB9XG4gICAgfSkpO1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShjdWUsICdhbGlnbicsIGV4dGVuZCh7fSwgYmFzZU9iaiwge1xuICAgICAgZ2V0OiBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiBfYWxpZ247XG4gICAgICB9LFxuICAgICAgc2V0OiBmdW5jdGlvbiAodmFsdWUpIHtcbiAgICAgICAgY29uc3Qgc2V0dGluZyA9IGZpbmRBbGlnblNldHRpbmcodmFsdWUpO1xuICAgICAgICBpZiAoIXNldHRpbmcpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgU3ludGF4RXJyb3IoJ0FuIGludmFsaWQgb3IgaWxsZWdhbCBzdHJpbmcgd2FzIHNwZWNpZmllZC4nKTtcbiAgICAgICAgfVxuICAgICAgICBfYWxpZ24gPSBzZXR0aW5nO1xuICAgICAgICB0aGlzLmhhc0JlZW5SZXNldCA9IHRydWU7XG4gICAgICB9XG4gICAgfSkpO1xuXG4gICAgLyoqXG4gICAgICogT3RoZXIgPHRyYWNrPiBzcGVjIGRlZmluZWQgcHJvcGVydGllc1xuICAgICAqL1xuXG4gICAgLy8gaHR0cDovL3d3dy53aGF0d2cub3JnL3NwZWNzL3dlYi1hcHBzL2N1cnJlbnQtd29yay9tdWx0aXBhZ2UvdGhlLXZpZGVvLWVsZW1lbnQuaHRtbCN0ZXh0LXRyYWNrLWN1ZS1kaXNwbGF5LXN0YXRlXG4gICAgY3VlLmRpc3BsYXlTdGF0ZSA9IHVuZGVmaW5lZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBWVFRDdWUgbWV0aG9kc1xuICAgKi9cblxuICBWVFRDdWUucHJvdG90eXBlLmdldEN1ZUFzSFRNTCA9IGZ1bmN0aW9uICgpIHtcbiAgICAvLyBBc3N1bWUgV2ViVlRULmNvbnZlcnRDdWVUb0RPTVRyZWUgaXMgb24gdGhlIGdsb2JhbC5cbiAgICBjb25zdCBXZWJWVFQgPSBzZWxmLldlYlZUVDtcbiAgICByZXR1cm4gV2ViVlRULmNvbnZlcnRDdWVUb0RPTVRyZWUoc2VsZiwgdGhpcy50ZXh0KTtcbiAgfTtcbiAgLy8gdGhpcyBpcyBhIHBvbHlmaWxsIGhhY2tcbiAgcmV0dXJuIFZUVEN1ZTtcbn0pKCk7XG5cbi8qXG4gKiBTb3VyY2U6IGh0dHBzOi8vZ2l0aHViLmNvbS9tb3ppbGxhL3Z0dC5qcy9ibG9iL21hc3Rlci9kaXN0L3Z0dC5qc1xuICovXG5cbmNsYXNzIFN0cmluZ0RlY29kZXIge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXVudXNlZC12YXJzXG4gIGRlY29kZShkYXRhLCBvcHRpb25zKSB7XG4gICAgaWYgKCFkYXRhKSB7XG4gICAgICByZXR1cm4gJyc7XG4gICAgfVxuICAgIGlmICh0eXBlb2YgZGF0YSAhPT0gJ3N0cmluZycpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignRXJyb3IgLSBleHBlY3RlZCBzdHJpbmcgZGF0YS4nKTtcbiAgICB9XG4gICAgcmV0dXJuIGRlY29kZVVSSUNvbXBvbmVudChlbmNvZGVVUklDb21wb25lbnQoZGF0YSkpO1xuICB9XG59XG5cbi8vIFRyeSB0byBwYXJzZSBpbnB1dCBhcyBhIHRpbWUgc3RhbXAuXG5mdW5jdGlvbiBwYXJzZVRpbWVTdGFtcChpbnB1dCkge1xuICBmdW5jdGlvbiBjb21wdXRlU2Vjb25kcyhoLCBtLCBzLCBmKSB7XG4gICAgcmV0dXJuIChoIHwgMCkgKiAzNjAwICsgKG0gfCAwKSAqIDYwICsgKHMgfCAwKSArIHBhcnNlRmxvYXQoZiB8fCAwKTtcbiAgfVxuICBjb25zdCBtID0gaW5wdXQubWF0Y2goL14oPzooXFxkKyk6KT8oXFxkezJ9KTooXFxkezJ9KShcXC5cXGQrKT8vKTtcbiAgaWYgKCFtKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgaWYgKHBhcnNlRmxvYXQobVsyXSkgPiA1OSkge1xuICAgIC8vIFRpbWVzdGFtcCB0YWtlcyB0aGUgZm9ybSBvZiBbaG91cnNdOlttaW51dGVzXS5bbWlsbGlzZWNvbmRzXVxuICAgIC8vIEZpcnN0IHBvc2l0aW9uIGlzIGhvdXJzIGFzIGl0J3Mgb3ZlciA1OS5cbiAgICByZXR1cm4gY29tcHV0ZVNlY29uZHMobVsyXSwgbVszXSwgMCwgbVs0XSk7XG4gIH1cbiAgLy8gVGltZXN0YW1wIHRha2VzIHRoZSBmb3JtIG9mIFtob3VycyAob3B0aW9uYWwpXTpbbWludXRlc106W3NlY29uZHNdLlttaWxsaXNlY29uZHNdXG4gIHJldHVybiBjb21wdXRlU2Vjb25kcyhtWzFdLCBtWzJdLCBtWzNdLCBtWzRdKTtcbn1cblxuLy8gQSBzZXR0aW5ncyBvYmplY3QgaG9sZHMga2V5L3ZhbHVlIHBhaXJzIGFuZCB3aWxsIGlnbm9yZSBhbnl0aGluZyBidXQgdGhlIGZpcnN0XG4vLyBhc3NpZ25tZW50IHRvIGEgc3BlY2lmaWMga2V5LlxuY2xhc3MgU2V0dGluZ3Mge1xuICBjb25zdHJ1Y3RvcigpIHtcbiAgICB0aGlzLnZhbHVlcyA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gIH1cbiAgLy8gT25seSBhY2NlcHQgdGhlIGZpcnN0IGFzc2lnbm1lbnQgdG8gYW55IGtleS5cbiAgc2V0KGssIHYpIHtcbiAgICBpZiAoIXRoaXMuZ2V0KGspICYmIHYgIT09ICcnKSB7XG4gICAgICB0aGlzLnZhbHVlc1trXSA9IHY7XG4gICAgfVxuICB9XG4gIC8vIFJldHVybiB0aGUgdmFsdWUgZm9yIGEga2V5LCBvciBhIGRlZmF1bHQgdmFsdWUuXG4gIC8vIElmICdkZWZhdWx0S2V5JyBpcyBwYXNzZWQgdGhlbiAnZGZsdCcgaXMgYXNzdW1lZCB0byBiZSBhbiBvYmplY3Qgd2l0aFxuICAvLyBhIG51bWJlciBvZiBwb3NzaWJsZSBkZWZhdWx0IHZhbHVlcyBhcyBwcm9wZXJ0aWVzIHdoZXJlICdkZWZhdWx0S2V5JyBpc1xuICAvLyB0aGUga2V5IG9mIHRoZSBwcm9wZXJ0eSB0aGF0IHdpbGwgYmUgY2hvc2VuOyBvdGhlcndpc2UgaXQncyBhc3N1bWVkIHRvIGJlXG4gIC8vIGEgc2luZ2xlIHZhbHVlLlxuICBnZXQoaywgZGZsdCwgZGVmYXVsdEtleSkge1xuICAgIGlmIChkZWZhdWx0S2V5KSB7XG4gICAgICByZXR1cm4gdGhpcy5oYXMoaykgPyB0aGlzLnZhbHVlc1trXSA6IGRmbHRbZGVmYXVsdEtleV07XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmhhcyhrKSA/IHRoaXMudmFsdWVzW2tdIDogZGZsdDtcbiAgfVxuICAvLyBDaGVjayB3aGV0aGVyIHdlIGhhdmUgYSB2YWx1ZSBmb3IgYSBrZXkuXG4gIGhhcyhrKSB7XG4gICAgcmV0dXJuIGsgaW4gdGhpcy52YWx1ZXM7XG4gIH1cbiAgLy8gQWNjZXB0IGEgc2V0dGluZyBpZiBpdHMgb25lIG9mIHRoZSBnaXZlbiBhbHRlcm5hdGl2ZXMuXG4gIGFsdChrLCB2LCBhKSB7XG4gICAgZm9yIChsZXQgbiA9IDA7IG4gPCBhLmxlbmd0aDsgKytuKSB7XG4gICAgICBpZiAodiA9PT0gYVtuXSkge1xuICAgICAgICB0aGlzLnNldChrLCB2KTtcbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIC8vIEFjY2VwdCBhIHNldHRpbmcgaWYgaXRzIGEgdmFsaWQgKHNpZ25lZCkgaW50ZWdlci5cbiAgaW50ZWdlcihrLCB2KSB7XG4gICAgaWYgKC9eLT9cXGQrJC8udGVzdCh2KSkge1xuICAgICAgLy8gaW50ZWdlclxuICAgICAgdGhpcy5zZXQoaywgcGFyc2VJbnQodiwgMTApKTtcbiAgICB9XG4gIH1cbiAgLy8gQWNjZXB0IGEgc2V0dGluZyBpZiBpdHMgYSB2YWxpZCBwZXJjZW50YWdlLlxuICBwZXJjZW50KGssIHYpIHtcbiAgICBpZiAoL14oW1xcZF17MSwzfSkoXFwuW1xcZF0qKT8lJC8udGVzdCh2KSkge1xuICAgICAgY29uc3QgcGVyY2VudCA9IHBhcnNlRmxvYXQodik7XG4gICAgICBpZiAocGVyY2VudCA+PSAwICYmIHBlcmNlbnQgPD0gMTAwKSB7XG4gICAgICAgIHRoaXMuc2V0KGssIHBlcmNlbnQpO1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG5cbi8vIEhlbHBlciBmdW5jdGlvbiB0byBwYXJzZSBpbnB1dCBpbnRvIGdyb3VwcyBzZXBhcmF0ZWQgYnkgJ2dyb3VwRGVsaW0nLCBhbmRcbi8vIGludGVycHJldCBlYWNoIGdyb3VwIGFzIGEga2V5L3ZhbHVlIHBhaXIgc2VwYXJhdGVkIGJ5ICdrZXlWYWx1ZURlbGltJy5cbmZ1bmN0aW9uIHBhcnNlT3B0aW9ucyhpbnB1dCwgY2FsbGJhY2ssIGtleVZhbHVlRGVsaW0sIGdyb3VwRGVsaW0pIHtcbiAgY29uc3QgZ3JvdXBzID0gZ3JvdXBEZWxpbSA/IGlucHV0LnNwbGl0KGdyb3VwRGVsaW0pIDogW2lucHV0XTtcbiAgZm9yIChjb25zdCBpIGluIGdyb3Vwcykge1xuICAgIGlmICh0eXBlb2YgZ3JvdXBzW2ldICE9PSAnc3RyaW5nJykge1xuICAgICAgY29udGludWU7XG4gICAgfVxuICAgIGNvbnN0IGt2ID0gZ3JvdXBzW2ldLnNwbGl0KGtleVZhbHVlRGVsaW0pO1xuICAgIGlmIChrdi5sZW5ndGggIT09IDIpIHtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICBjb25zdCBrID0ga3ZbMF07XG4gICAgY29uc3QgdiA9IGt2WzFdO1xuICAgIGNhbGxiYWNrKGssIHYpO1xuICB9XG59XG5jb25zdCBkZWZhdWx0cyA9IG5ldyBWVFRDdWUoMCwgMCwgJycpO1xuLy8gJ21pZGRsZScgd2FzIGNoYW5nZWQgdG8gJ2NlbnRlcicgaW4gdGhlIHNwZWM6IGh0dHBzOi8vZ2l0aHViLmNvbS93M2Mvd2VidnR0L3B1bGwvMjQ0XG4vLyAgU2FmYXJpIGRvZXNuJ3QgeWV0IHN1cHBvcnQgdGhpcyBjaGFuZ2UsIGJ1dCBGRiBhbmQgQ2hyb21lIGRvLlxuY29uc3QgY2VudGVyID0gZGVmYXVsdHMuYWxpZ24gPT09ICdtaWRkbGUnID8gJ21pZGRsZScgOiAnY2VudGVyJztcbmZ1bmN0aW9uIHBhcnNlQ3VlKGlucHV0LCBjdWUsIHJlZ2lvbkxpc3QpIHtcbiAgLy8gUmVtZW1iZXIgdGhlIG9yaWdpbmFsIGlucHV0IGlmIHdlIG5lZWQgdG8gdGhyb3cgYW4gZXJyb3IuXG4gIGNvbnN0IG9JbnB1dCA9IGlucHV0O1xuICAvLyA0LjEgV2ViVlRUIHRpbWVzdGFtcFxuICBmdW5jdGlvbiBjb25zdW1lVGltZVN0YW1wKCkge1xuICAgIGNvbnN0IHRzID0gcGFyc2VUaW1lU3RhbXAoaW5wdXQpO1xuICAgIGlmICh0cyA9PT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdNYWxmb3JtZWQgdGltZXN0YW1wOiAnICsgb0lucHV0KTtcbiAgICB9XG5cbiAgICAvLyBSZW1vdmUgdGltZSBzdGFtcCBmcm9tIGlucHV0LlxuICAgIGlucHV0ID0gaW5wdXQucmVwbGFjZSgvXlteXFxzYS16QS1aLV0rLywgJycpO1xuICAgIHJldHVybiB0cztcbiAgfVxuXG4gIC8vIDQuNC4yIFdlYlZUVCBjdWUgc2V0dGluZ3NcbiAgZnVuY3Rpb24gY29uc3VtZUN1ZVNldHRpbmdzKGlucHV0LCBjdWUpIHtcbiAgICBjb25zdCBzZXR0aW5ncyA9IG5ldyBTZXR0aW5ncygpO1xuICAgIHBhcnNlT3B0aW9ucyhpbnB1dCwgZnVuY3Rpb24gKGssIHYpIHtcbiAgICAgIGxldCB2YWxzO1xuICAgICAgc3dpdGNoIChrKSB7XG4gICAgICAgIGNhc2UgJ3JlZ2lvbic6XG4gICAgICAgICAgLy8gRmluZCB0aGUgbGFzdCByZWdpb24gd2UgcGFyc2VkIHdpdGggdGhlIHNhbWUgcmVnaW9uIGlkLlxuICAgICAgICAgIGZvciAobGV0IGkgPSByZWdpb25MaXN0Lmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKSB7XG4gICAgICAgICAgICBpZiAocmVnaW9uTGlzdFtpXS5pZCA9PT0gdikge1xuICAgICAgICAgICAgICBzZXR0aW5ncy5zZXQoaywgcmVnaW9uTGlzdFtpXS5yZWdpb24pO1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ3ZlcnRpY2FsJzpcbiAgICAgICAgICBzZXR0aW5ncy5hbHQoaywgdiwgWydybCcsICdsciddKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnbGluZSc6XG4gICAgICAgICAgdmFscyA9IHYuc3BsaXQoJywnKTtcbiAgICAgICAgICBzZXR0aW5ncy5pbnRlZ2VyKGssIHZhbHNbMF0pO1xuICAgICAgICAgIGlmIChzZXR0aW5ncy5wZXJjZW50KGssIHZhbHNbMF0pKSB7XG4gICAgICAgICAgICBzZXR0aW5ncy5zZXQoJ3NuYXBUb0xpbmVzJywgZmFsc2UpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBzZXR0aW5ncy5hbHQoaywgdmFsc1swXSwgWydhdXRvJ10pO1xuICAgICAgICAgIGlmICh2YWxzLmxlbmd0aCA9PT0gMikge1xuICAgICAgICAgICAgc2V0dGluZ3MuYWx0KCdsaW5lQWxpZ24nLCB2YWxzWzFdLCBbJ3N0YXJ0JywgY2VudGVyLCAnZW5kJ10pO1xuICAgICAgICAgIH1cbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAncG9zaXRpb24nOlxuICAgICAgICAgIHZhbHMgPSB2LnNwbGl0KCcsJyk7XG4gICAgICAgICAgc2V0dGluZ3MucGVyY2VudChrLCB2YWxzWzBdKTtcbiAgICAgICAgICBpZiAodmFscy5sZW5ndGggPT09IDIpIHtcbiAgICAgICAgICAgIHNldHRpbmdzLmFsdCgncG9zaXRpb25BbGlnbicsIHZhbHNbMV0sIFsnc3RhcnQnLCBjZW50ZXIsICdlbmQnLCAnbGluZS1sZWZ0JywgJ2xpbmUtcmlnaHQnLCAnYXV0byddKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ3NpemUnOlxuICAgICAgICAgIHNldHRpbmdzLnBlcmNlbnQoaywgdik7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ2FsaWduJzpcbiAgICAgICAgICBzZXR0aW5ncy5hbHQoaywgdiwgWydzdGFydCcsIGNlbnRlciwgJ2VuZCcsICdsZWZ0JywgJ3JpZ2h0J10pO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH0sIC86LywgL1xccy8pO1xuXG4gICAgLy8gQXBwbHkgZGVmYXVsdCB2YWx1ZXMgZm9yIGFueSBtaXNzaW5nIGZpZWxkcy5cbiAgICBjdWUucmVnaW9uID0gc2V0dGluZ3MuZ2V0KCdyZWdpb24nLCBudWxsKTtcbiAgICBjdWUudmVydGljYWwgPSBzZXR0aW5ncy5nZXQoJ3ZlcnRpY2FsJywgJycpO1xuICAgIGxldCBsaW5lID0gc2V0dGluZ3MuZ2V0KCdsaW5lJywgJ2F1dG8nKTtcbiAgICBpZiAobGluZSA9PT0gJ2F1dG8nICYmIGRlZmF1bHRzLmxpbmUgPT09IC0xKSB7XG4gICAgICAvLyBzZXQgbnVtZXJpYyBsaW5lIG51bWJlciBmb3IgU2FmYXJpXG4gICAgICBsaW5lID0gLTE7XG4gICAgfVxuICAgIGN1ZS5saW5lID0gbGluZTtcbiAgICBjdWUubGluZUFsaWduID0gc2V0dGluZ3MuZ2V0KCdsaW5lQWxpZ24nLCAnc3RhcnQnKTtcbiAgICBjdWUuc25hcFRvTGluZXMgPSBzZXR0aW5ncy5nZXQoJ3NuYXBUb0xpbmVzJywgdHJ1ZSk7XG4gICAgY3VlLnNpemUgPSBzZXR0aW5ncy5nZXQoJ3NpemUnLCAxMDApO1xuICAgIGN1ZS5hbGlnbiA9IHNldHRpbmdzLmdldCgnYWxpZ24nLCBjZW50ZXIpO1xuICAgIGxldCBwb3NpdGlvbiA9IHNldHRpbmdzLmdldCgncG9zaXRpb24nLCAnYXV0bycpO1xuICAgIGlmIChwb3NpdGlvbiA9PT0gJ2F1dG8nICYmIGRlZmF1bHRzLnBvc2l0aW9uID09PSA1MCkge1xuICAgICAgLy8gc2V0IG51bWVyaWMgcG9zaXRpb24gZm9yIFNhZmFyaVxuICAgICAgcG9zaXRpb24gPSBjdWUuYWxpZ24gPT09ICdzdGFydCcgfHwgY3VlLmFsaWduID09PSAnbGVmdCcgPyAwIDogY3VlLmFsaWduID09PSAnZW5kJyB8fCBjdWUuYWxpZ24gPT09ICdyaWdodCcgPyAxMDAgOiA1MDtcbiAgICB9XG4gICAgY3VlLnBvc2l0aW9uID0gcG9zaXRpb247XG4gIH1cbiAgZnVuY3Rpb24gc2tpcFdoaXRlc3BhY2UoKSB7XG4gICAgaW5wdXQgPSBpbnB1dC5yZXBsYWNlKC9eXFxzKy8sICcnKTtcbiAgfVxuXG4gIC8vIDQuMSBXZWJWVFQgY3VlIHRpbWluZ3MuXG4gIHNraXBXaGl0ZXNwYWNlKCk7XG4gIGN1ZS5zdGFydFRpbWUgPSBjb25zdW1lVGltZVN0YW1wKCk7IC8vICgxKSBjb2xsZWN0IGN1ZSBzdGFydCB0aW1lXG4gIHNraXBXaGl0ZXNwYWNlKCk7XG4gIGlmIChpbnB1dC5zbGljZSgwLCAzKSAhPT0gJy0tPicpIHtcbiAgICAvLyAoMykgbmV4dCBjaGFyYWN0ZXJzIG11c3QgbWF0Y2ggJy0tPidcbiAgICB0aHJvdyBuZXcgRXJyb3IoXCJNYWxmb3JtZWQgdGltZSBzdGFtcCAodGltZSBzdGFtcHMgbXVzdCBiZSBzZXBhcmF0ZWQgYnkgJy0tPicpOiBcIiArIG9JbnB1dCk7XG4gIH1cbiAgaW5wdXQgPSBpbnB1dC5zbGljZSgzKTtcbiAgc2tpcFdoaXRlc3BhY2UoKTtcbiAgY3VlLmVuZFRpbWUgPSBjb25zdW1lVGltZVN0YW1wKCk7IC8vICg1KSBjb2xsZWN0IGN1ZSBlbmQgdGltZVxuXG4gIC8vIDQuMSBXZWJWVFQgY3VlIHNldHRpbmdzIGxpc3QuXG4gIHNraXBXaGl0ZXNwYWNlKCk7XG4gIGNvbnN1bWVDdWVTZXR0aW5ncyhpbnB1dCwgY3VlKTtcbn1cbmZ1bmN0aW9uIGZpeExpbmVCcmVha3MoaW5wdXQpIHtcbiAgcmV0dXJuIGlucHV0LnJlcGxhY2UoLzxicig/OiBcXC8pPz4vZ2ksICdcXG4nKTtcbn1cbmNsYXNzIFZUVFBhcnNlciB7XG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHRoaXMuc3RhdGUgPSAnSU5JVElBTCc7XG4gICAgdGhpcy5idWZmZXIgPSAnJztcbiAgICB0aGlzLmRlY29kZXIgPSBuZXcgU3RyaW5nRGVjb2RlcigpO1xuICAgIHRoaXMucmVnaW9uTGlzdCA9IFtdO1xuICAgIHRoaXMuY3VlID0gbnVsbDtcbiAgICB0aGlzLm9uY3VlID0gdm9pZCAwO1xuICAgIHRoaXMub25wYXJzaW5nZXJyb3IgPSB2b2lkIDA7XG4gICAgdGhpcy5vbmZsdXNoID0gdm9pZCAwO1xuICB9XG4gIHBhcnNlKGRhdGEpIHtcbiAgICBjb25zdCBfdGhpcyA9IHRoaXM7XG5cbiAgICAvLyBJZiB0aGVyZSBpcyBubyBkYXRhIHRoZW4gd2Ugd29uJ3QgZGVjb2RlIGl0LCBidXQgd2lsbCBqdXN0IHRyeSB0byBwYXJzZVxuICAgIC8vIHdoYXRldmVyIGlzIGluIGJ1ZmZlciBhbHJlYWR5LiBUaGlzIG1heSBvY2N1ciBpbiBjaXJjdW1zdGFuY2VzLCBmb3JcbiAgICAvLyBleGFtcGxlIHdoZW4gZmx1c2goKSBpcyBjYWxsZWQuXG4gICAgaWYgKGRhdGEpIHtcbiAgICAgIC8vIFRyeSB0byBkZWNvZGUgdGhlIGRhdGEgdGhhdCB3ZSByZWNlaXZlZC5cbiAgICAgIF90aGlzLmJ1ZmZlciArPSBfdGhpcy5kZWNvZGVyLmRlY29kZShkYXRhLCB7XG4gICAgICAgIHN0cmVhbTogdHJ1ZVxuICAgICAgfSk7XG4gICAgfVxuICAgIGZ1bmN0aW9uIGNvbGxlY3ROZXh0TGluZSgpIHtcbiAgICAgIGxldCBidWZmZXIgPSBfdGhpcy5idWZmZXI7XG4gICAgICBsZXQgcG9zID0gMDtcbiAgICAgIGJ1ZmZlciA9IGZpeExpbmVCcmVha3MoYnVmZmVyKTtcbiAgICAgIHdoaWxlIChwb3MgPCBidWZmZXIubGVuZ3RoICYmIGJ1ZmZlcltwb3NdICE9PSAnXFxyJyAmJiBidWZmZXJbcG9zXSAhPT0gJ1xcbicpIHtcbiAgICAgICAgKytwb3M7XG4gICAgICB9XG4gICAgICBjb25zdCBsaW5lID0gYnVmZmVyLnNsaWNlKDAsIHBvcyk7XG4gICAgICAvLyBBZHZhbmNlIHRoZSBidWZmZXIgZWFybHkgaW4gY2FzZSB3ZSBmYWlsIGJlbG93LlxuICAgICAgaWYgKGJ1ZmZlcltwb3NdID09PSAnXFxyJykge1xuICAgICAgICArK3BvcztcbiAgICAgIH1cbiAgICAgIGlmIChidWZmZXJbcG9zXSA9PT0gJ1xcbicpIHtcbiAgICAgICAgKytwb3M7XG4gICAgICB9XG4gICAgICBfdGhpcy5idWZmZXIgPSBidWZmZXIuc2xpY2UocG9zKTtcbiAgICAgIHJldHVybiBsaW5lO1xuICAgIH1cblxuICAgIC8vIDMuMiBXZWJWVFQgbWV0YWRhdGEgaGVhZGVyIHN5bnRheFxuICAgIGZ1bmN0aW9uIHBhcnNlSGVhZGVyKGlucHV0KSB7XG4gICAgICBwYXJzZU9wdGlvbnMoaW5wdXQsIGZ1bmN0aW9uIChrLCB2KSB7XG4gICAgICAgIC8vIHN3aXRjaCAoaykge1xuICAgICAgICAvLyBjYXNlICdyZWdpb24nOlxuICAgICAgICAvLyAzLjMgV2ViVlRUIHJlZ2lvbiBtZXRhZGF0YSBoZWFkZXIgc3ludGF4XG4gICAgICAgIC8vIGNvbnNvbGUubG9nKCdwYXJzZSByZWdpb24nLCB2KTtcbiAgICAgICAgLy8gcGFyc2VSZWdpb24odik7XG4gICAgICAgIC8vIGJyZWFrO1xuICAgICAgICAvLyB9XG4gICAgICB9LCAvOi8pO1xuICAgIH1cblxuICAgIC8vIDUuMSBXZWJWVFQgZmlsZSBwYXJzaW5nLlxuICAgIHRyeSB7XG4gICAgICBsZXQgbGluZSA9ICcnO1xuICAgICAgaWYgKF90aGlzLnN0YXRlID09PSAnSU5JVElBTCcpIHtcbiAgICAgICAgLy8gV2UgY2FuJ3Qgc3RhcnQgcGFyc2luZyB1bnRpbCB3ZSBoYXZlIHRoZSBmaXJzdCBsaW5lLlxuICAgICAgICBpZiAoIS9cXHJcXG58XFxuLy50ZXN0KF90aGlzLmJ1ZmZlcikpIHtcbiAgICAgICAgICByZXR1cm4gdGhpcztcbiAgICAgICAgfVxuICAgICAgICBsaW5lID0gY29sbGVjdE5leHRMaW5lKCk7XG4gICAgICAgIC8vIHN0cmlwIG9mIFVURi04IEJPTSBpZiBhbnlcbiAgICAgICAgLy8gaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQnl0ZV9vcmRlcl9tYXJrI1VURi04XG4gICAgICAgIGNvbnN0IG0gPSBsaW5lLm1hdGNoKC9eKMOvwrvCvyk/V0VCVlRUKFsgXFx0XS4qKT8kLyk7XG4gICAgICAgIGlmICghKG0gIT0gbnVsbCAmJiBtWzBdKSkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcignTWFsZm9ybWVkIFdlYlZUVCBzaWduYXR1cmUuJyk7XG4gICAgICAgIH1cbiAgICAgICAgX3RoaXMuc3RhdGUgPSAnSEVBREVSJztcbiAgICAgIH1cbiAgICAgIGxldCBhbHJlYWR5Q29sbGVjdGVkTGluZSA9IGZhbHNlO1xuICAgICAgd2hpbGUgKF90aGlzLmJ1ZmZlcikge1xuICAgICAgICAvLyBXZSBjYW4ndCBwYXJzZSBhIGxpbmUgdW50aWwgd2UgaGF2ZSB0aGUgZnVsbCBsaW5lLlxuICAgICAgICBpZiAoIS9cXHJcXG58XFxuLy50ZXN0KF90aGlzLmJ1ZmZlcikpIHtcbiAgICAgICAgICByZXR1cm4gdGhpcztcbiAgICAgICAgfVxuICAgICAgICBpZiAoIWFscmVhZHlDb2xsZWN0ZWRMaW5lKSB7XG4gICAgICAgICAgbGluZSA9IGNvbGxlY3ROZXh0TGluZSgpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGFscmVhZHlDb2xsZWN0ZWRMaW5lID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgc3dpdGNoIChfdGhpcy5zdGF0ZSkge1xuICAgICAgICAgIGNhc2UgJ0hFQURFUic6XG4gICAgICAgICAgICAvLyAxMy0xOCAtIEFsbG93IGEgaGVhZGVyIChtZXRhZGF0YSkgdW5kZXIgdGhlIFdFQlZUVCBsaW5lLlxuICAgICAgICAgICAgaWYgKC86Ly50ZXN0KGxpbmUpKSB7XG4gICAgICAgICAgICAgIHBhcnNlSGVhZGVyKGxpbmUpO1xuICAgICAgICAgICAgfSBlbHNlIGlmICghbGluZSkge1xuICAgICAgICAgICAgICAvLyBBbiBlbXB0eSBsaW5lIHRlcm1pbmF0ZXMgdGhlIGhlYWRlciBhbmQgc3RhcnRzIHRoZSBib2R5IChjdWVzKS5cbiAgICAgICAgICAgICAgX3RoaXMuc3RhdGUgPSAnSUQnO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgY2FzZSAnTk9URSc6XG4gICAgICAgICAgICAvLyBJZ25vcmUgTk9URSBibG9ja3MuXG4gICAgICAgICAgICBpZiAoIWxpbmUpIHtcbiAgICAgICAgICAgICAgX3RoaXMuc3RhdGUgPSAnSUQnO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgY2FzZSAnSUQnOlxuICAgICAgICAgICAgLy8gQ2hlY2sgZm9yIHRoZSBzdGFydCBvZiBOT1RFIGJsb2Nrcy5cbiAgICAgICAgICAgIGlmICgvXk5PVEUoJHxbIFxcdF0pLy50ZXN0KGxpbmUpKSB7XG4gICAgICAgICAgICAgIF90aGlzLnN0YXRlID0gJ05PVEUnO1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIDE5LTI5IC0gQWxsb3cgYW55IG51bWJlciBvZiBsaW5lIHRlcm1pbmF0b3JzLCB0aGVuIGluaXRpYWxpemUgbmV3IGN1ZSB2YWx1ZXMuXG4gICAgICAgICAgICBpZiAoIWxpbmUpIHtcbiAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBfdGhpcy5jdWUgPSBuZXcgVlRUQ3VlKDAsIDAsICcnKTtcbiAgICAgICAgICAgIF90aGlzLnN0YXRlID0gJ0NVRSc7XG4gICAgICAgICAgICAvLyAzMC0zOSAtIENoZWNrIGlmIHNlbGYgbGluZSBjb250YWlucyBhbiBvcHRpb25hbCBpZGVudGlmaWVyIG9yIHRpbWluZyBkYXRhLlxuICAgICAgICAgICAgaWYgKGxpbmUuaW5kZXhPZignLS0+JykgPT09IC0xKSB7XG4gICAgICAgICAgICAgIF90aGlzLmN1ZS5pZCA9IGxpbmU7XG4gICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIC8vIFByb2Nlc3MgbGluZSBhcyBzdGFydCBvZiBhIGN1ZS5cbiAgICAgICAgICAvKiBmYWxscyB0aHJvdWdoICovXG4gICAgICAgICAgY2FzZSAnQ1VFJzpcbiAgICAgICAgICAgIC8vIDQwIC0gQ29sbGVjdCBjdWUgdGltaW5ncyBhbmQgc2V0dGluZ3MuXG4gICAgICAgICAgICBpZiAoIV90aGlzLmN1ZSkge1xuICAgICAgICAgICAgICBfdGhpcy5zdGF0ZSA9ICdCQURDVUUnO1xuICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgIHBhcnNlQ3VlKGxpbmUsIF90aGlzLmN1ZSwgX3RoaXMucmVnaW9uTGlzdCk7XG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgIC8vIEluIGNhc2Ugb2YgYW4gZXJyb3IgaWdub3JlIHJlc3Qgb2YgdGhlIGN1ZS5cbiAgICAgICAgICAgICAgX3RoaXMuY3VlID0gbnVsbDtcbiAgICAgICAgICAgICAgX3RoaXMuc3RhdGUgPSAnQkFEQ1VFJztcbiAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBfdGhpcy5zdGF0ZSA9ICdDVUVURVhUJztcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgIGNhc2UgJ0NVRVRFWFQnOlxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBjb25zdCBoYXNTdWJzdHJpbmcgPSBsaW5lLmluZGV4T2YoJy0tPicpICE9PSAtMTtcbiAgICAgICAgICAgICAgLy8gMzQgLSBJZiB3ZSBoYXZlIGFuIGVtcHR5IGxpbmUgdGhlbiByZXBvcnQgdGhlIGN1ZS5cbiAgICAgICAgICAgICAgLy8gMzUgLSBJZiB3ZSBoYXZlIHRoZSBzcGVjaWFsIHN1YnN0cmluZyAnLS0+JyB0aGVuIHJlcG9ydCB0aGUgY3VlLFxuICAgICAgICAgICAgICAvLyBidXQgZG8gbm90IGNvbGxlY3QgdGhlIGxpbmUgYXMgd2UgbmVlZCB0byBwcm9jZXNzIHRoZSBjdXJyZW50XG4gICAgICAgICAgICAgIC8vIG9uZSBhcyBhIG5ldyBjdWUuXG4gICAgICAgICAgICAgIGlmICghbGluZSB8fCBoYXNTdWJzdHJpbmcgJiYgKGFscmVhZHlDb2xsZWN0ZWRMaW5lID0gdHJ1ZSkpIHtcbiAgICAgICAgICAgICAgICAvLyBXZSBhcmUgZG9uZSBwYXJzaW5nIHNlbGYgY3VlLlxuICAgICAgICAgICAgICAgIGlmIChfdGhpcy5vbmN1ZSAmJiBfdGhpcy5jdWUpIHtcbiAgICAgICAgICAgICAgICAgIF90aGlzLm9uY3VlKF90aGlzLmN1ZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIF90aGlzLmN1ZSA9IG51bGw7XG4gICAgICAgICAgICAgICAgX3RoaXMuc3RhdGUgPSAnSUQnO1xuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGlmIChfdGhpcy5jdWUgPT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBpZiAoX3RoaXMuY3VlLnRleHQpIHtcbiAgICAgICAgICAgICAgICBfdGhpcy5jdWUudGV4dCArPSAnXFxuJztcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBfdGhpcy5jdWUudGV4dCArPSBsaW5lO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgY2FzZSAnQkFEQ1VFJzpcbiAgICAgICAgICAgIC8vIDU0LTYyIC0gQ29sbGVjdCBhbmQgZGlzY2FyZCB0aGUgcmVtYWluaW5nIGN1ZS5cbiAgICAgICAgICAgIGlmICghbGluZSkge1xuICAgICAgICAgICAgICBfdGhpcy5zdGF0ZSA9ICdJRCc7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAvLyBJZiB3ZSBhcmUgY3VycmVudGx5IHBhcnNpbmcgYSBjdWUsIHJlcG9ydCB3aGF0IHdlIGhhdmUuXG4gICAgICBpZiAoX3RoaXMuc3RhdGUgPT09ICdDVUVURVhUJyAmJiBfdGhpcy5jdWUgJiYgX3RoaXMub25jdWUpIHtcbiAgICAgICAgX3RoaXMub25jdWUoX3RoaXMuY3VlKTtcbiAgICAgIH1cbiAgICAgIF90aGlzLmN1ZSA9IG51bGw7XG4gICAgICAvLyBFbnRlciBCQURXRUJWVFQgc3RhdGUgaWYgaGVhZGVyIHdhcyBub3QgcGFyc2VkIGNvcnJlY3RseSBvdGhlcndpc2VcbiAgICAgIC8vIGFub3RoZXIgZXhjZXB0aW9uIG9jY3VycmVkIHNvIGVudGVyIEJBRENVRSBzdGF0ZS5cbiAgICAgIF90aGlzLnN0YXRlID0gX3RoaXMuc3RhdGUgPT09ICdJTklUSUFMJyA/ICdCQURXRUJWVFQnIDogJ0JBRENVRSc7XG4gICAgfVxuICAgIHJldHVybiB0aGlzO1xuICB9XG4gIGZsdXNoKCkge1xuICAgIGNvbnN0IF90aGlzID0gdGhpcztcbiAgICB0cnkge1xuICAgICAgLy8gRmluaXNoIGRlY29kaW5nIHRoZSBzdHJlYW0uXG4gICAgICAvLyBfdGhpcy5idWZmZXIgKz0gX3RoaXMuZGVjb2Rlci5kZWNvZGUoKTtcbiAgICAgIC8vIFN5bnRoZXNpemUgdGhlIGVuZCBvZiB0aGUgY3VycmVudCBjdWUgb3IgcmVnaW9uLlxuICAgICAgaWYgKF90aGlzLmN1ZSB8fCBfdGhpcy5zdGF0ZSA9PT0gJ0hFQURFUicpIHtcbiAgICAgICAgX3RoaXMuYnVmZmVyICs9ICdcXG5cXG4nO1xuICAgICAgICBfdGhpcy5wYXJzZSgpO1xuICAgICAgfVxuICAgICAgLy8gSWYgd2UndmUgZmx1c2hlZCwgcGFyc2VkLCBhbmQgd2UncmUgc3RpbGwgb24gdGhlIElOSVRJQUwgc3RhdGUgdGhlblxuICAgICAgLy8gdGhhdCBtZWFucyB3ZSBkb24ndCBoYXZlIGVub3VnaCBvZiB0aGUgc3RyZWFtIHRvIHBhcnNlIHRoZSBmaXJzdFxuICAgICAgLy8gbGluZS5cbiAgICAgIGlmIChfdGhpcy5zdGF0ZSA9PT0gJ0lOSVRJQUwnIHx8IF90aGlzLnN0YXRlID09PSAnQkFEV0VCVlRUJykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ01hbGZvcm1lZCBXZWJWVFQgc2lnbmF0dXJlLicpO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGlmIChfdGhpcy5vbnBhcnNpbmdlcnJvcikge1xuICAgICAgICBfdGhpcy5vbnBhcnNpbmdlcnJvcihlKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKF90aGlzLm9uZmx1c2gpIHtcbiAgICAgIF90aGlzLm9uZmx1c2goKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cbn1cblxuY29uc3QgTElORUJSRUFLUyA9IC9cXHJcXG58XFxuXFxyfFxcbnxcXHIvZztcblxuLy8gU3RyaW5nLnByb3RvdHlwZS5zdGFydHNXaXRoIGlzIG5vdCBzdXBwb3J0ZWQgaW4gSUUxMVxuY29uc3Qgc3RhcnRzV2l0aCA9IGZ1bmN0aW9uIHN0YXJ0c1dpdGgoaW5wdXRTdHJpbmcsIHNlYXJjaFN0cmluZywgcG9zaXRpb24gPSAwKSB7XG4gIHJldHVybiBpbnB1dFN0cmluZy5zbGljZShwb3NpdGlvbiwgcG9zaXRpb24gKyBzZWFyY2hTdHJpbmcubGVuZ3RoKSA9PT0gc2VhcmNoU3RyaW5nO1xufTtcbmNvbnN0IGN1ZVN0cmluZzJtaWxsaXMgPSBmdW5jdGlvbiBjdWVTdHJpbmcybWlsbGlzKHRpbWVTdHJpbmcpIHtcbiAgbGV0IHRzID0gcGFyc2VJbnQodGltZVN0cmluZy5zbGljZSgtMykpO1xuICBjb25zdCBzZWNzID0gcGFyc2VJbnQodGltZVN0cmluZy5zbGljZSgtNiwgLTQpKTtcbiAgY29uc3QgbWlucyA9IHBhcnNlSW50KHRpbWVTdHJpbmcuc2xpY2UoLTksIC03KSk7XG4gIGNvbnN0IGhvdXJzID0gdGltZVN0cmluZy5sZW5ndGggPiA5ID8gcGFyc2VJbnQodGltZVN0cmluZy5zdWJzdHJpbmcoMCwgdGltZVN0cmluZy5pbmRleE9mKCc6JykpKSA6IDA7XG4gIGlmICghaXNGaW5pdGVOdW1iZXIodHMpIHx8ICFpc0Zpbml0ZU51bWJlcihzZWNzKSB8fCAhaXNGaW5pdGVOdW1iZXIobWlucykgfHwgIWlzRmluaXRlTnVtYmVyKGhvdXJzKSkge1xuICAgIHRocm93IEVycm9yKGBNYWxmb3JtZWQgWC1USU1FU1RBTVAtTUFQOiBMb2NhbDoke3RpbWVTdHJpbmd9YCk7XG4gIH1cbiAgdHMgKz0gMTAwMCAqIHNlY3M7XG4gIHRzICs9IDYwICogMTAwMCAqIG1pbnM7XG4gIHRzICs9IDYwICogNjAgKiAxMDAwICogaG91cnM7XG4gIHJldHVybiB0cztcbn07XG5cbi8vIEZyb20gaHR0cHM6Ly9naXRodWIuY29tL2Rhcmtza3lhcHAvc3RyaW5nLWhhc2hcbmNvbnN0IGhhc2ggPSBmdW5jdGlvbiBoYXNoKHRleHQpIHtcbiAgbGV0IF9oYXNoID0gNTM4MTtcbiAgbGV0IGkgPSB0ZXh0Lmxlbmd0aDtcbiAgd2hpbGUgKGkpIHtcbiAgICBfaGFzaCA9IF9oYXNoICogMzMgXiB0ZXh0LmNoYXJDb2RlQXQoLS1pKTtcbiAgfVxuICByZXR1cm4gKF9oYXNoID4+PiAwKS50b1N0cmluZygpO1xufTtcblxuLy8gQ3JlYXRlIGEgdW5pcXVlIGhhc2ggaWQgZm9yIGEgY3VlIGJhc2VkIG9uIHN0YXJ0L2VuZCB0aW1lcyBhbmQgdGV4dC5cbi8vIFRoaXMgaGVscHMgdGltZWxpbmUtY29udHJvbGxlciB0byBhdm9pZCBzaG93aW5nIHJlcGVhdGVkIGNhcHRpb25zLlxuZnVuY3Rpb24gZ2VuZXJhdGVDdWVJZChzdGFydFRpbWUsIGVuZFRpbWUsIHRleHQpIHtcbiAgcmV0dXJuIGhhc2goc3RhcnRUaW1lLnRvU3RyaW5nKCkpICsgaGFzaChlbmRUaW1lLnRvU3RyaW5nKCkpICsgaGFzaCh0ZXh0KTtcbn1cbmNvbnN0IGNhbGN1bGF0ZU9mZnNldCA9IGZ1bmN0aW9uIGNhbGN1bGF0ZU9mZnNldCh2dHRDQ3MsIGNjLCBwcmVzZW50YXRpb25UaW1lKSB7XG4gIGxldCBjdXJyQ0MgPSB2dHRDQ3NbY2NdO1xuICBsZXQgcHJldkNDID0gdnR0Q0NzW2N1cnJDQy5wcmV2Q0NdO1xuXG4gIC8vIFRoaXMgaXMgdGhlIGZpcnN0IGRpc2NvbnRpbnVpdHkgb3IgY3VlcyBoYXZlIGJlZW4gcHJvY2Vzc2VkIHNpbmNlIHRoZSBsYXN0IGRpc2NvbnRpbnVpdHlcbiAgLy8gT2Zmc2V0ID0gY3VycmVudCBkaXNjb250aW51aXR5IHRpbWVcbiAgaWYgKCFwcmV2Q0MgfHwgIXByZXZDQy5uZXcgJiYgY3VyckNDLm5ldykge1xuICAgIHZ0dENDcy5jY09mZnNldCA9IHZ0dENDcy5wcmVzZW50YXRpb25PZmZzZXQgPSBjdXJyQ0Muc3RhcnQ7XG4gICAgY3VyckNDLm5ldyA9IGZhbHNlO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIC8vIFRoZXJlIGhhdmUgYmVlbiBkaXNjb250aW51aXRpZXMgc2luY2UgY3VlcyB3ZXJlIGxhc3QgcGFyc2VkLlxuICAvLyBPZmZzZXQgPSB0aW1lIGVsYXBzZWRcbiAgd2hpbGUgKChfcHJldkNDID0gcHJldkNDKSAhPSBudWxsICYmIF9wcmV2Q0MubmV3KSB7XG4gICAgdmFyIF9wcmV2Q0M7XG4gICAgdnR0Q0NzLmNjT2Zmc2V0ICs9IGN1cnJDQy5zdGFydCAtIHByZXZDQy5zdGFydDtcbiAgICBjdXJyQ0MubmV3ID0gZmFsc2U7XG4gICAgY3VyckNDID0gcHJldkNDO1xuICAgIHByZXZDQyA9IHZ0dENDc1tjdXJyQ0MucHJldkNDXTtcbiAgfVxuICB2dHRDQ3MucHJlc2VudGF0aW9uT2Zmc2V0ID0gcHJlc2VudGF0aW9uVGltZTtcbn07XG5mdW5jdGlvbiBwYXJzZVdlYlZUVCh2dHRCeXRlQXJyYXksIGluaXRQVFMsIHZ0dENDcywgY2MsIHRpbWVPZmZzZXQsIGNhbGxCYWNrLCBlcnJvckNhbGxCYWNrKSB7XG4gIGNvbnN0IHBhcnNlciA9IG5ldyBWVFRQYXJzZXIoKTtcbiAgLy8gQ29udmVydCBieXRlQXJyYXkgaW50byBzdHJpbmcsIHJlcGxhY2luZyBhbnkgc29tZXdoYXQgZXhvdGljIGxpbmVmZWVkcyB3aXRoIFwiXFxuXCIsIHRoZW4gc3BsaXQgb24gdGhhdCBjaGFyYWN0ZXIuXG4gIC8vIFVpbnQ4QXJyYXkucHJvdG90eXBlLnJlZHVjZSBpcyBub3QgaW1wbGVtZW50ZWQgaW4gSUUxMVxuICBjb25zdCB2dHRMaW5lcyA9IHV0ZjhBcnJheVRvU3RyKG5ldyBVaW50OEFycmF5KHZ0dEJ5dGVBcnJheSkpLnRyaW0oKS5yZXBsYWNlKExJTkVCUkVBS1MsICdcXG4nKS5zcGxpdCgnXFxuJyk7XG4gIGNvbnN0IGN1ZXMgPSBbXTtcbiAgY29uc3QgaW5pdDkwa0h6ID0gaW5pdFBUUyA/IHRvTXBlZ1RzQ2xvY2tGcm9tVGltZXNjYWxlKGluaXRQVFMuYmFzZVRpbWUsIGluaXRQVFMudGltZXNjYWxlKSA6IDA7XG4gIGxldCBjdWVUaW1lID0gJzAwOjAwLjAwMCc7XG4gIGxldCB0aW1lc3RhbXBNYXBNUEVHVFMgPSAwO1xuICBsZXQgdGltZXN0YW1wTWFwTE9DQUwgPSAwO1xuICBsZXQgcGFyc2luZ0Vycm9yO1xuICBsZXQgaW5IZWFkZXIgPSB0cnVlO1xuICBwYXJzZXIub25jdWUgPSBmdW5jdGlvbiAoY3VlKSB7XG4gICAgLy8gQWRqdXN0IGN1ZSB0aW1pbmc7IGNsYW1wIGN1ZXMgdG8gc3RhcnQgbm8gZWFybGllciB0aGFuIC0gYW5kIGRyb3AgY3VlcyB0aGF0IGRvbid0IGVuZCBhZnRlciAtIDAgb24gdGltZWxpbmUuXG4gICAgY29uc3QgY3VyckNDID0gdnR0Q0NzW2NjXTtcbiAgICBsZXQgY3VlT2Zmc2V0ID0gdnR0Q0NzLmNjT2Zmc2V0O1xuXG4gICAgLy8gQ2FsY3VsYXRlIHN1YnRpdGxlIFBUUyBvZmZzZXRcbiAgICBjb25zdCB3ZWJWdHRNcGVnVHNNYXBPZmZzZXQgPSAodGltZXN0YW1wTWFwTVBFR1RTIC0gaW5pdDkwa0h6KSAvIDkwMDAwO1xuXG4gICAgLy8gVXBkYXRlIG9mZnNldHMgZm9yIG5ldyBkaXNjb250aW51aXRpZXNcbiAgICBpZiAoY3VyckNDICE9IG51bGwgJiYgY3VyckNDLm5ldykge1xuICAgICAgaWYgKHRpbWVzdGFtcE1hcExPQ0FMICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgLy8gV2hlbiBsb2NhbCB0aW1lIGlzIHByb3ZpZGVkLCBvZmZzZXQgPSBkaXNjb250aW51aXR5IHN0YXJ0IHRpbWUgLSBsb2NhbCB0aW1lXG4gICAgICAgIGN1ZU9mZnNldCA9IHZ0dENDcy5jY09mZnNldCA9IGN1cnJDQy5zdGFydDtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNhbGN1bGF0ZU9mZnNldCh2dHRDQ3MsIGNjLCB3ZWJWdHRNcGVnVHNNYXBPZmZzZXQpO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAod2ViVnR0TXBlZ1RzTWFwT2Zmc2V0KSB7XG4gICAgICBpZiAoIWluaXRQVFMpIHtcbiAgICAgICAgcGFyc2luZ0Vycm9yID0gbmV3IEVycm9yKCdNaXNzaW5nIGluaXRQVFMgZm9yIFZUVCBNUEVHVFMnKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgLy8gSWYgd2UgaGF2ZSBNUEVHVFMsIG9mZnNldCA9IHByZXNlbnRhdGlvbiB0aW1lICsgZGlzY29udGludWl0eSBvZmZzZXRcbiAgICAgIGN1ZU9mZnNldCA9IHdlYlZ0dE1wZWdUc01hcE9mZnNldCAtIHZ0dENDcy5wcmVzZW50YXRpb25PZmZzZXQ7XG4gICAgfVxuICAgIGNvbnN0IGR1cmF0aW9uID0gY3VlLmVuZFRpbWUgLSBjdWUuc3RhcnRUaW1lO1xuICAgIGNvbnN0IHN0YXJ0VGltZSA9IG5vcm1hbGl6ZVB0cygoY3VlLnN0YXJ0VGltZSArIGN1ZU9mZnNldCAtIHRpbWVzdGFtcE1hcExPQ0FMKSAqIDkwMDAwLCB0aW1lT2Zmc2V0ICogOTAwMDApIC8gOTAwMDA7XG4gICAgY3VlLnN0YXJ0VGltZSA9IE1hdGgubWF4KHN0YXJ0VGltZSwgMCk7XG4gICAgY3VlLmVuZFRpbWUgPSBNYXRoLm1heChzdGFydFRpbWUgKyBkdXJhdGlvbiwgMCk7XG5cbiAgICAvL3RyaW0gdHJhaWxpbmcgd2VidnR0IGJsb2NrIHdoaXRlc3BhY2VzXG4gICAgY29uc3QgdGV4dCA9IGN1ZS50ZXh0LnRyaW0oKTtcblxuICAgIC8vIEZpeCBlbmNvZGluZyBvZiBzcGVjaWFsIGNoYXJhY3RlcnNcbiAgICBjdWUudGV4dCA9IGRlY29kZVVSSUNvbXBvbmVudChlbmNvZGVVUklDb21wb25lbnQodGV4dCkpO1xuXG4gICAgLy8gSWYgdGhlIGN1ZSB3YXMgbm90IGFzc2lnbmVkIGFuIGlkIGZyb20gdGhlIFZUVCBmaWxlIChsaW5lIGFib3ZlIHRoZSBjb250ZW50KSwgY3JlYXRlIG9uZS5cbiAgICBpZiAoIWN1ZS5pZCkge1xuICAgICAgY3VlLmlkID0gZ2VuZXJhdGVDdWVJZChjdWUuc3RhcnRUaW1lLCBjdWUuZW5kVGltZSwgdGV4dCk7XG4gICAgfVxuICAgIGlmIChjdWUuZW5kVGltZSA+IDApIHtcbiAgICAgIGN1ZXMucHVzaChjdWUpO1xuICAgIH1cbiAgfTtcbiAgcGFyc2VyLm9ucGFyc2luZ2Vycm9yID0gZnVuY3Rpb24gKGVycm9yKSB7XG4gICAgcGFyc2luZ0Vycm9yID0gZXJyb3I7XG4gIH07XG4gIHBhcnNlci5vbmZsdXNoID0gZnVuY3Rpb24gKCkge1xuICAgIGlmIChwYXJzaW5nRXJyb3IpIHtcbiAgICAgIGVycm9yQ2FsbEJhY2socGFyc2luZ0Vycm9yKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY2FsbEJhY2soY3Vlcyk7XG4gIH07XG5cbiAgLy8gR28gdGhyb3VnaCBjb250ZW50cyBsaW5lIGJ5IGxpbmUuXG4gIHZ0dExpbmVzLmZvckVhY2gobGluZSA9PiB7XG4gICAgaWYgKGluSGVhZGVyKSB7XG4gICAgICAvLyBMb29rIGZvciBYLVRJTUVTVEFNUC1NQVAgaW4gaGVhZGVyLlxuICAgICAgaWYgKHN0YXJ0c1dpdGgobGluZSwgJ1gtVElNRVNUQU1QLU1BUD0nKSkge1xuICAgICAgICAvLyBPbmNlIGZvdW5kLCBubyBtb3JlIGFyZSBhbGxvd2VkIGFueXdheSwgc28gc3RvcCBzZWFyY2hpbmcuXG4gICAgICAgIGluSGVhZGVyID0gZmFsc2U7XG4gICAgICAgIC8vIEV4dHJhY3QgTE9DQUwgYW5kIE1QRUdUUy5cbiAgICAgICAgbGluZS5zbGljZSgxNikuc3BsaXQoJywnKS5mb3JFYWNoKHRpbWVzdGFtcCA9PiB7XG4gICAgICAgICAgaWYgKHN0YXJ0c1dpdGgodGltZXN0YW1wLCAnTE9DQUw6JykpIHtcbiAgICAgICAgICAgIGN1ZVRpbWUgPSB0aW1lc3RhbXAuc2xpY2UoNik7XG4gICAgICAgICAgfSBlbHNlIGlmIChzdGFydHNXaXRoKHRpbWVzdGFtcCwgJ01QRUdUUzonKSkge1xuICAgICAgICAgICAgdGltZXN0YW1wTWFwTVBFR1RTID0gcGFyc2VJbnQodGltZXN0YW1wLnNsaWNlKDcpKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICB0cnkge1xuICAgICAgICAgIC8vIENvbnZlcnQgY3VlIHRpbWUgdG8gc2Vjb25kc1xuICAgICAgICAgIHRpbWVzdGFtcE1hcExPQ0FMID0gY3VlU3RyaW5nMm1pbGxpcyhjdWVUaW1lKSAvIDEwMDA7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgcGFyc2luZ0Vycm9yID0gZXJyb3I7XG4gICAgICAgIH1cbiAgICAgICAgLy8gUmV0dXJuIHdpdGhvdXQgcGFyc2luZyBYLVRJTUVTVEFNUC1NQVAgbGluZS5cbiAgICAgICAgcmV0dXJuO1xuICAgICAgfSBlbHNlIGlmIChsaW5lID09PSAnJykge1xuICAgICAgICBpbkhlYWRlciA9IGZhbHNlO1xuICAgICAgfVxuICAgIH1cbiAgICAvLyBQYXJzZSBsaW5lIGJ5IGRlZmF1bHQuXG4gICAgcGFyc2VyLnBhcnNlKGxpbmUgKyAnXFxuJyk7XG4gIH0pO1xuICBwYXJzZXIuZmx1c2goKTtcbn1cblxuY29uc3QgSU1TQzFfQ09ERUMgPSAnc3RwcC50dG1sLmltMXQnO1xuXG4vLyBUaW1lIGZvcm1hdDogaDptOnM6ZnJhbWVzKC5zdWJmcmFtZXMpXG5jb25zdCBITVNGX1JFR0VYID0gL14oXFxkezIsfSk6KFxcZHsyfSk6KFxcZHsyfSk6KFxcZHsyfSlcXC4/KFxcZCspPyQvO1xuXG4vLyBUaW1lIGZvcm1hdDogaG91cnMsIG1pbnV0ZXMsIHNlY29uZHMsIG1pbGxpc2Vjb25kcywgZnJhbWVzLCB0aWNrc1xuY29uc3QgVElNRV9VTklUX1JFR0VYID0gL14oXFxkKig/OlxcLlxcZCopPykoaHxtfHN8bXN8Znx0KSQvO1xuY29uc3QgdGV4dEFsaWduVG9MaW5lQWxpZ24gPSB7XG4gIGxlZnQ6ICdzdGFydCcsXG4gIGNlbnRlcjogJ2NlbnRlcicsXG4gIHJpZ2h0OiAnZW5kJyxcbiAgc3RhcnQ6ICdzdGFydCcsXG4gIGVuZDogJ2VuZCdcbn07XG5mdW5jdGlvbiBwYXJzZUlNU0MxKHBheWxvYWQsIGluaXRQVFMsIGNhbGxCYWNrLCBlcnJvckNhbGxCYWNrKSB7XG4gIGNvbnN0IHJlc3VsdHMgPSBmaW5kQm94KG5ldyBVaW50OEFycmF5KHBheWxvYWQpLCBbJ21kYXQnXSk7XG4gIGlmIChyZXN1bHRzLmxlbmd0aCA9PT0gMCkge1xuICAgIGVycm9yQ2FsbEJhY2sobmV3IEVycm9yKCdDb3VsZCBub3QgcGFyc2UgSU1TQzEgbWRhdCcpKTtcbiAgICByZXR1cm47XG4gIH1cbiAgY29uc3QgdHRtbExpc3QgPSByZXN1bHRzLm1hcChtZGF0ID0+IHV0ZjhBcnJheVRvU3RyKG1kYXQpKTtcbiAgY29uc3Qgc3luY1RpbWUgPSB0b1RpbWVzY2FsZUZyb21TY2FsZShpbml0UFRTLmJhc2VUaW1lLCAxLCBpbml0UFRTLnRpbWVzY2FsZSk7XG4gIHRyeSB7XG4gICAgdHRtbExpc3QuZm9yRWFjaCh0dG1sID0+IGNhbGxCYWNrKHBhcnNlVFRNTCh0dG1sLCBzeW5jVGltZSkpKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBlcnJvckNhbGxCYWNrKGVycm9yKTtcbiAgfVxufVxuZnVuY3Rpb24gcGFyc2VUVE1MKHR0bWwsIHN5bmNUaW1lKSB7XG4gIGNvbnN0IHBhcnNlciA9IG5ldyBET01QYXJzZXIoKTtcbiAgY29uc3QgeG1sRG9jID0gcGFyc2VyLnBhcnNlRnJvbVN0cmluZyh0dG1sLCAndGV4dC94bWwnKTtcbiAgY29uc3QgdHQgPSB4bWxEb2MuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ3R0JylbMF07XG4gIGlmICghdHQpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgdHRtbCcpO1xuICB9XG4gIGNvbnN0IGRlZmF1bHRSYXRlSW5mbyA9IHtcbiAgICBmcmFtZVJhdGU6IDMwLFxuICAgIHN1YkZyYW1lUmF0ZTogMSxcbiAgICBmcmFtZVJhdGVNdWx0aXBsaWVyOiAwLFxuICAgIHRpY2tSYXRlOiAwXG4gIH07XG4gIGNvbnN0IHJhdGVJbmZvID0gT2JqZWN0LmtleXMoZGVmYXVsdFJhdGVJbmZvKS5yZWR1Y2UoKHJlc3VsdCwga2V5KSA9PiB7XG4gICAgcmVzdWx0W2tleV0gPSB0dC5nZXRBdHRyaWJ1dGUoYHR0cDoke2tleX1gKSB8fCBkZWZhdWx0UmF0ZUluZm9ba2V5XTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9LCB7fSk7XG4gIGNvbnN0IHRyaW0gPSB0dC5nZXRBdHRyaWJ1dGUoJ3htbDpzcGFjZScpICE9PSAncHJlc2VydmUnO1xuICBjb25zdCBzdHlsZUVsZW1lbnRzID0gY29sbGVjdGlvblRvRGljdGlvbmFyeShnZXRFbGVtZW50Q29sbGVjdGlvbih0dCwgJ3N0eWxpbmcnLCAnc3R5bGUnKSk7XG4gIGNvbnN0IHJlZ2lvbkVsZW1lbnRzID0gY29sbGVjdGlvblRvRGljdGlvbmFyeShnZXRFbGVtZW50Q29sbGVjdGlvbih0dCwgJ2xheW91dCcsICdyZWdpb24nKSk7XG4gIGNvbnN0IGN1ZUVsZW1lbnRzID0gZ2V0RWxlbWVudENvbGxlY3Rpb24odHQsICdib2R5JywgJ1tiZWdpbl0nKTtcbiAgcmV0dXJuIFtdLm1hcC5jYWxsKGN1ZUVsZW1lbnRzLCBjdWVFbGVtZW50ID0+IHtcbiAgICBjb25zdCBjdWVUZXh0ID0gZ2V0VGV4dENvbnRlbnQoY3VlRWxlbWVudCwgdHJpbSk7XG4gICAgaWYgKCFjdWVUZXh0IHx8ICFjdWVFbGVtZW50Lmhhc0F0dHJpYnV0ZSgnYmVnaW4nKSkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIGNvbnN0IHN0YXJ0VGltZSA9IHBhcnNlVHRtbFRpbWUoY3VlRWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2JlZ2luJyksIHJhdGVJbmZvKTtcbiAgICBjb25zdCBkdXJhdGlvbiA9IHBhcnNlVHRtbFRpbWUoY3VlRWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2R1cicpLCByYXRlSW5mbyk7XG4gICAgbGV0IGVuZFRpbWUgPSBwYXJzZVR0bWxUaW1lKGN1ZUVsZW1lbnQuZ2V0QXR0cmlidXRlKCdlbmQnKSwgcmF0ZUluZm8pO1xuICAgIGlmIChzdGFydFRpbWUgPT09IG51bGwpIHtcbiAgICAgIHRocm93IHRpbWVzdGFtcFBhcnNpbmdFcnJvcihjdWVFbGVtZW50KTtcbiAgICB9XG4gICAgaWYgKGVuZFRpbWUgPT09IG51bGwpIHtcbiAgICAgIGlmIChkdXJhdGlvbiA9PT0gbnVsbCkge1xuICAgICAgICB0aHJvdyB0aW1lc3RhbXBQYXJzaW5nRXJyb3IoY3VlRWxlbWVudCk7XG4gICAgICB9XG4gICAgICBlbmRUaW1lID0gc3RhcnRUaW1lICsgZHVyYXRpb247XG4gICAgfVxuICAgIGNvbnN0IGN1ZSA9IG5ldyBWVFRDdWUoc3RhcnRUaW1lIC0gc3luY1RpbWUsIGVuZFRpbWUgLSBzeW5jVGltZSwgY3VlVGV4dCk7XG4gICAgY3VlLmlkID0gZ2VuZXJhdGVDdWVJZChjdWUuc3RhcnRUaW1lLCBjdWUuZW5kVGltZSwgY3VlLnRleHQpO1xuICAgIGNvbnN0IHJlZ2lvbiA9IHJlZ2lvbkVsZW1lbnRzW2N1ZUVsZW1lbnQuZ2V0QXR0cmlidXRlKCdyZWdpb24nKV07XG4gICAgY29uc3Qgc3R5bGUgPSBzdHlsZUVsZW1lbnRzW2N1ZUVsZW1lbnQuZ2V0QXR0cmlidXRlKCdzdHlsZScpXTtcblxuICAgIC8vIEFwcGx5IHN0eWxlcyB0byBjdWVcbiAgICBjb25zdCBzdHlsZXMgPSBnZXRUdG1sU3R5bGVzKHJlZ2lvbiwgc3R5bGUsIHN0eWxlRWxlbWVudHMpO1xuICAgIGNvbnN0IHtcbiAgICAgIHRleHRBbGlnblxuICAgIH0gPSBzdHlsZXM7XG4gICAgaWYgKHRleHRBbGlnbikge1xuICAgICAgLy8gY3VlLnBvc2l0aW9uQWxpZ24gbm90IHNldHRhYmxlIGluIEZGfjIwMTZcbiAgICAgIGNvbnN0IGxpbmVBbGlnbiA9IHRleHRBbGlnblRvTGluZUFsaWduW3RleHRBbGlnbl07XG4gICAgICBpZiAobGluZUFsaWduKSB7XG4gICAgICAgIGN1ZS5saW5lQWxpZ24gPSBsaW5lQWxpZ247XG4gICAgICB9XG4gICAgICBjdWUuYWxpZ24gPSB0ZXh0QWxpZ247XG4gICAgfVxuICAgIF9leHRlbmRzKGN1ZSwgc3R5bGVzKTtcbiAgICByZXR1cm4gY3VlO1xuICB9KS5maWx0ZXIoY3VlID0+IGN1ZSAhPT0gbnVsbCk7XG59XG5mdW5jdGlvbiBnZXRFbGVtZW50Q29sbGVjdGlvbihmcm9tRWxlbWVudCwgcGFyZW50TmFtZSwgY2hpbGROYW1lKSB7XG4gIGNvbnN0IHBhcmVudCA9IGZyb21FbGVtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKHBhcmVudE5hbWUpWzBdO1xuICBpZiAocGFyZW50KSB7XG4gICAgcmV0dXJuIFtdLnNsaWNlLmNhbGwocGFyZW50LnF1ZXJ5U2VsZWN0b3JBbGwoY2hpbGROYW1lKSk7XG4gIH1cbiAgcmV0dXJuIFtdO1xufVxuZnVuY3Rpb24gY29sbGVjdGlvblRvRGljdGlvbmFyeShlbGVtZW50c1dpdGhJZCkge1xuICByZXR1cm4gZWxlbWVudHNXaXRoSWQucmVkdWNlKChkaWN0LCBlbGVtZW50KSA9PiB7XG4gICAgY29uc3QgaWQgPSBlbGVtZW50LmdldEF0dHJpYnV0ZSgneG1sOmlkJyk7XG4gICAgaWYgKGlkKSB7XG4gICAgICBkaWN0W2lkXSA9IGVsZW1lbnQ7XG4gICAgfVxuICAgIHJldHVybiBkaWN0O1xuICB9LCB7fSk7XG59XG5mdW5jdGlvbiBnZXRUZXh0Q29udGVudChlbGVtZW50LCB0cmltKSB7XG4gIHJldHVybiBbXS5zbGljZS5jYWxsKGVsZW1lbnQuY2hpbGROb2RlcykucmVkdWNlKChzdHIsIG5vZGUsIGkpID0+IHtcbiAgICB2YXIgX25vZGUkY2hpbGROb2RlcztcbiAgICBpZiAobm9kZS5ub2RlTmFtZSA9PT0gJ2JyJyAmJiBpKSB7XG4gICAgICByZXR1cm4gc3RyICsgJ1xcbic7XG4gICAgfVxuICAgIGlmICgoX25vZGUkY2hpbGROb2RlcyA9IG5vZGUuY2hpbGROb2RlcykgIT0gbnVsbCAmJiBfbm9kZSRjaGlsZE5vZGVzLmxlbmd0aCkge1xuICAgICAgcmV0dXJuIGdldFRleHRDb250ZW50KG5vZGUsIHRyaW0pO1xuICAgIH0gZWxzZSBpZiAodHJpbSkge1xuICAgICAgcmV0dXJuIHN0ciArIG5vZGUudGV4dENvbnRlbnQudHJpbSgpLnJlcGxhY2UoL1xccysvZywgJyAnKTtcbiAgICB9XG4gICAgcmV0dXJuIHN0ciArIG5vZGUudGV4dENvbnRlbnQ7XG4gIH0sICcnKTtcbn1cbmZ1bmN0aW9uIGdldFR0bWxTdHlsZXMocmVnaW9uLCBzdHlsZSwgc3R5bGVFbGVtZW50cykge1xuICBjb25zdCB0dHNOcyA9ICdodHRwOi8vd3d3LnczLm9yZy9ucy90dG1sI3N0eWxpbmcnO1xuICBsZXQgcmVnaW9uU3R5bGUgPSBudWxsO1xuICBjb25zdCBzdHlsZUF0dHJpYnV0ZXMgPSBbJ2Rpc3BsYXlBbGlnbicsICd0ZXh0QWxpZ24nLCAnY29sb3InLCAnYmFja2dyb3VuZENvbG9yJywgJ2ZvbnRTaXplJywgJ2ZvbnRGYW1pbHknXG4gIC8vICdmb250V2VpZ2h0JyxcbiAgLy8gJ2xpbmVIZWlnaHQnLFxuICAvLyAnd3JhcE9wdGlvbicsXG4gIC8vICdmb250U3R5bGUnLFxuICAvLyAnZGlyZWN0aW9uJyxcbiAgLy8gJ3dyaXRpbmdNb2RlJ1xuICBdO1xuICBjb25zdCByZWdpb25TdHlsZU5hbWUgPSByZWdpb24gIT0gbnVsbCAmJiByZWdpb24uaGFzQXR0cmlidXRlKCdzdHlsZScpID8gcmVnaW9uLmdldEF0dHJpYnV0ZSgnc3R5bGUnKSA6IG51bGw7XG4gIGlmIChyZWdpb25TdHlsZU5hbWUgJiYgc3R5bGVFbGVtZW50cy5oYXNPd25Qcm9wZXJ0eShyZWdpb25TdHlsZU5hbWUpKSB7XG4gICAgcmVnaW9uU3R5bGUgPSBzdHlsZUVsZW1lbnRzW3JlZ2lvblN0eWxlTmFtZV07XG4gIH1cbiAgcmV0dXJuIHN0eWxlQXR0cmlidXRlcy5yZWR1Y2UoKHN0eWxlcywgbmFtZSkgPT4ge1xuICAgIGNvbnN0IHZhbHVlID0gZ2V0QXR0cmlidXRlTlMoc3R5bGUsIHR0c05zLCBuYW1lKSB8fCBnZXRBdHRyaWJ1dGVOUyhyZWdpb24sIHR0c05zLCBuYW1lKSB8fCBnZXRBdHRyaWJ1dGVOUyhyZWdpb25TdHlsZSwgdHRzTnMsIG5hbWUpO1xuICAgIGlmICh2YWx1ZSkge1xuICAgICAgc3R5bGVzW25hbWVdID0gdmFsdWU7XG4gICAgfVxuICAgIHJldHVybiBzdHlsZXM7XG4gIH0sIHt9KTtcbn1cbmZ1bmN0aW9uIGdldEF0dHJpYnV0ZU5TKGVsZW1lbnQsIG5zLCBuYW1lKSB7XG4gIGlmICghZWxlbWVudCkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG4gIHJldHVybiBlbGVtZW50Lmhhc0F0dHJpYnV0ZU5TKG5zLCBuYW1lKSA/IGVsZW1lbnQuZ2V0QXR0cmlidXRlTlMobnMsIG5hbWUpIDogbnVsbDtcbn1cbmZ1bmN0aW9uIHRpbWVzdGFtcFBhcnNpbmdFcnJvcihub2RlKSB7XG4gIHJldHVybiBuZXcgRXJyb3IoYENvdWxkIG5vdCBwYXJzZSB0dG1sIHRpbWVzdGFtcCAke25vZGV9YCk7XG59XG5mdW5jdGlvbiBwYXJzZVR0bWxUaW1lKHRpbWVBdHRyaWJ1dGVWYWx1ZSwgcmF0ZUluZm8pIHtcbiAgaWYgKCF0aW1lQXR0cmlidXRlVmFsdWUpIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBsZXQgc2Vjb25kcyA9IHBhcnNlVGltZVN0YW1wKHRpbWVBdHRyaWJ1dGVWYWx1ZSk7XG4gIGlmIChzZWNvbmRzID09PSBudWxsKSB7XG4gICAgaWYgKEhNU0ZfUkVHRVgudGVzdCh0aW1lQXR0cmlidXRlVmFsdWUpKSB7XG4gICAgICBzZWNvbmRzID0gcGFyc2VIb3Vyc01pbnV0ZXNTZWNvbmRzRnJhbWVzKHRpbWVBdHRyaWJ1dGVWYWx1ZSwgcmF0ZUluZm8pO1xuICAgIH0gZWxzZSBpZiAoVElNRV9VTklUX1JFR0VYLnRlc3QodGltZUF0dHJpYnV0ZVZhbHVlKSkge1xuICAgICAgc2Vjb25kcyA9IHBhcnNlVGltZVVuaXRzKHRpbWVBdHRyaWJ1dGVWYWx1ZSwgcmF0ZUluZm8pO1xuICAgIH1cbiAgfVxuICByZXR1cm4gc2Vjb25kcztcbn1cbmZ1bmN0aW9uIHBhcnNlSG91cnNNaW51dGVzU2Vjb25kc0ZyYW1lcyh0aW1lQXR0cmlidXRlVmFsdWUsIHJhdGVJbmZvKSB7XG4gIGNvbnN0IG0gPSBITVNGX1JFR0VYLmV4ZWModGltZUF0dHJpYnV0ZVZhbHVlKTtcbiAgY29uc3QgZnJhbWVzID0gKG1bNF0gfCAwKSArIChtWzVdIHwgMCkgLyByYXRlSW5mby5zdWJGcmFtZVJhdGU7XG4gIHJldHVybiAobVsxXSB8IDApICogMzYwMCArIChtWzJdIHwgMCkgKiA2MCArIChtWzNdIHwgMCkgKyBmcmFtZXMgLyByYXRlSW5mby5mcmFtZVJhdGU7XG59XG5mdW5jdGlvbiBwYXJzZVRpbWVVbml0cyh0aW1lQXR0cmlidXRlVmFsdWUsIHJhdGVJbmZvKSB7XG4gIGNvbnN0IG0gPSBUSU1FX1VOSVRfUkVHRVguZXhlYyh0aW1lQXR0cmlidXRlVmFsdWUpO1xuICBjb25zdCB2YWx1ZSA9IE51bWJlcihtWzFdKTtcbiAgY29uc3QgdW5pdCA9IG1bMl07XG4gIHN3aXRjaCAodW5pdCkge1xuICAgIGNhc2UgJ2gnOlxuICAgICAgcmV0dXJuIHZhbHVlICogMzYwMDtcbiAgICBjYXNlICdtJzpcbiAgICAgIHJldHVybiB2YWx1ZSAqIDYwO1xuICAgIGNhc2UgJ21zJzpcbiAgICAgIHJldHVybiB2YWx1ZSAqIDEwMDA7XG4gICAgY2FzZSAnZic6XG4gICAgICByZXR1cm4gdmFsdWUgLyByYXRlSW5mby5mcmFtZVJhdGU7XG4gICAgY2FzZSAndCc6XG4gICAgICByZXR1cm4gdmFsdWUgLyByYXRlSW5mby50aWNrUmF0ZTtcbiAgfVxuICByZXR1cm4gdmFsdWU7XG59XG5cbmNsYXNzIFRpbWVsaW5lQ29udHJvbGxlciB7XG4gIGNvbnN0cnVjdG9yKGhscykge1xuICAgIHRoaXMuaGxzID0gdm9pZCAwO1xuICAgIHRoaXMubWVkaWEgPSBudWxsO1xuICAgIHRoaXMuY29uZmlnID0gdm9pZCAwO1xuICAgIHRoaXMuZW5hYmxlZCA9IHRydWU7XG4gICAgdGhpcy5DdWVzID0gdm9pZCAwO1xuICAgIHRoaXMudGV4dFRyYWNrcyA9IFtdO1xuICAgIHRoaXMudHJhY2tzID0gW107XG4gICAgdGhpcy5pbml0UFRTID0gW107XG4gICAgdGhpcy51bnBhcnNlZFZ0dEZyYWdzID0gW107XG4gICAgdGhpcy5jYXB0aW9uc1RyYWNrcyA9IHt9O1xuICAgIHRoaXMubm9uTmF0aXZlQ2FwdGlvbnNUcmFja3MgPSB7fTtcbiAgICB0aGlzLmNlYTYwOFBhcnNlcjEgPSB2b2lkIDA7XG4gICAgdGhpcy5jZWE2MDhQYXJzZXIyID0gdm9pZCAwO1xuICAgIHRoaXMubGFzdENjID0gLTE7XG4gICAgLy8gTGFzdCB2aWRlbyAoQ0VBLTYwOCkgZnJhZ21lbnQgQ0NcbiAgICB0aGlzLmxhc3RTbiA9IC0xO1xuICAgIC8vIExhc3QgdmlkZW8gKENFQS02MDgpIGZyYWdtZW50IE1TTlxuICAgIHRoaXMubGFzdFBhcnRJbmRleCA9IC0xO1xuICAgIC8vIExhc3QgdmlkZW8gKENFQS02MDgpIGZyYWdtZW50IFBhcnQgSW5kZXhcbiAgICB0aGlzLnByZXZDQyA9IC0xO1xuICAgIC8vIExhc3Qgc3VidGl0bGUgZnJhZ21lbnQgQ0NcbiAgICB0aGlzLnZ0dENDcyA9IG5ld1ZUVENDcygpO1xuICAgIHRoaXMuY2FwdGlvbnNQcm9wZXJ0aWVzID0gdm9pZCAwO1xuICAgIHRoaXMuaGxzID0gaGxzO1xuICAgIHRoaXMuY29uZmlnID0gaGxzLmNvbmZpZztcbiAgICB0aGlzLkN1ZXMgPSBobHMuY29uZmlnLmN1ZUhhbmRsZXI7XG4gICAgdGhpcy5jYXB0aW9uc1Byb3BlcnRpZXMgPSB7XG4gICAgICB0ZXh0VHJhY2sxOiB7XG4gICAgICAgIGxhYmVsOiB0aGlzLmNvbmZpZy5jYXB0aW9uc1RleHRUcmFjazFMYWJlbCxcbiAgICAgICAgbGFuZ3VhZ2VDb2RlOiB0aGlzLmNvbmZpZy5jYXB0aW9uc1RleHRUcmFjazFMYW5ndWFnZUNvZGVcbiAgICAgIH0sXG4gICAgICB0ZXh0VHJhY2syOiB7XG4gICAgICAgIGxhYmVsOiB0aGlzLmNvbmZpZy5jYXB0aW9uc1RleHRUcmFjazJMYWJlbCxcbiAgICAgICAgbGFuZ3VhZ2VDb2RlOiB0aGlzLmNvbmZpZy5jYXB0aW9uc1RleHRUcmFjazJMYW5ndWFnZUNvZGVcbiAgICAgIH0sXG4gICAgICB0ZXh0VHJhY2szOiB7XG4gICAgICAgIGxhYmVsOiB0aGlzLmNvbmZpZy5jYXB0aW9uc1RleHRUcmFjazNMYWJlbCxcbiAgICAgICAgbGFuZ3VhZ2VDb2RlOiB0aGlzLmNvbmZpZy5jYXB0aW9uc1RleHRUcmFjazNMYW5ndWFnZUNvZGVcbiAgICAgIH0sXG4gICAgICB0ZXh0VHJhY2s0OiB7XG4gICAgICAgIGxhYmVsOiB0aGlzLmNvbmZpZy5jYXB0aW9uc1RleHRUcmFjazRMYWJlbCxcbiAgICAgICAgbGFuZ3VhZ2VDb2RlOiB0aGlzLmNvbmZpZy5jYXB0aW9uc1RleHRUcmFjazRMYW5ndWFnZUNvZGVcbiAgICAgIH1cbiAgICB9O1xuICAgIGhscy5vbihFdmVudHMuTUVESUFfQVRUQUNISU5HLCB0aGlzLm9uTWVkaWFBdHRhY2hpbmcsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuTUVESUFfREVUQUNISU5HLCB0aGlzLm9uTWVkaWFEZXRhY2hpbmcsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuTUFOSUZFU1RfTE9BRElORywgdGhpcy5vbk1hbmlmZXN0TG9hZGluZywgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5NQU5JRkVTVF9MT0FERUQsIHRoaXMub25NYW5pZmVzdExvYWRlZCwgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5TVUJUSVRMRV9UUkFDS1NfVVBEQVRFRCwgdGhpcy5vblN1YnRpdGxlVHJhY2tzVXBkYXRlZCwgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5GUkFHX0xPQURJTkcsIHRoaXMub25GcmFnTG9hZGluZywgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5GUkFHX0xPQURFRCwgdGhpcy5vbkZyYWdMb2FkZWQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuRlJBR19QQVJTSU5HX1VTRVJEQVRBLCB0aGlzLm9uRnJhZ1BhcnNpbmdVc2VyZGF0YSwgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5GUkFHX0RFQ1JZUFRFRCwgdGhpcy5vbkZyYWdEZWNyeXB0ZWQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuSU5JVF9QVFNfRk9VTkQsIHRoaXMub25Jbml0UHRzRm91bmQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuU1VCVElUTEVfVFJBQ0tTX0NMRUFSRUQsIHRoaXMub25TdWJ0aXRsZVRyYWNrc0NsZWFyZWQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuQlVGRkVSX0ZMVVNISU5HLCB0aGlzLm9uQnVmZmVyRmx1c2hpbmcsIHRoaXMpO1xuICB9XG4gIGRlc3Ryb3koKSB7XG4gICAgY29uc3Qge1xuICAgICAgaGxzXG4gICAgfSA9IHRoaXM7XG4gICAgaGxzLm9mZihFdmVudHMuTUVESUFfQVRUQUNISU5HLCB0aGlzLm9uTWVkaWFBdHRhY2hpbmcsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLk1FRElBX0RFVEFDSElORywgdGhpcy5vbk1lZGlhRGV0YWNoaW5nLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5NQU5JRkVTVF9MT0FESU5HLCB0aGlzLm9uTWFuaWZlc3RMb2FkaW5nLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5NQU5JRkVTVF9MT0FERUQsIHRoaXMub25NYW5pZmVzdExvYWRlZCwgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuU1VCVElUTEVfVFJBQ0tTX1VQREFURUQsIHRoaXMub25TdWJ0aXRsZVRyYWNrc1VwZGF0ZWQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkZSQUdfTE9BRElORywgdGhpcy5vbkZyYWdMb2FkaW5nLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5GUkFHX0xPQURFRCwgdGhpcy5vbkZyYWdMb2FkZWQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkZSQUdfUEFSU0lOR19VU0VSREFUQSwgdGhpcy5vbkZyYWdQYXJzaW5nVXNlcmRhdGEsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkZSQUdfREVDUllQVEVELCB0aGlzLm9uRnJhZ0RlY3J5cHRlZCwgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuSU5JVF9QVFNfRk9VTkQsIHRoaXMub25Jbml0UHRzRm91bmQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLlNVQlRJVExFX1RSQUNLU19DTEVBUkVELCB0aGlzLm9uU3VidGl0bGVUcmFja3NDbGVhcmVkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5CVUZGRVJfRkxVU0hJTkcsIHRoaXMub25CdWZmZXJGbHVzaGluZywgdGhpcyk7XG4gICAgLy8gQHRzLWlnbm9yZVxuICAgIHRoaXMuaGxzID0gdGhpcy5jb25maWcgPSBudWxsO1xuICAgIHRoaXMuY2VhNjA4UGFyc2VyMSA9IHRoaXMuY2VhNjA4UGFyc2VyMiA9IHVuZGVmaW5lZDtcbiAgfVxuICBpbml0Q2VhNjA4UGFyc2VycygpIHtcbiAgICBpZiAodGhpcy5jb25maWcuZW5hYmxlQ0VBNzA4Q2FwdGlvbnMgJiYgKCF0aGlzLmNlYTYwOFBhcnNlcjEgfHwgIXRoaXMuY2VhNjA4UGFyc2VyMikpIHtcbiAgICAgIGNvbnN0IGNoYW5uZWwxID0gbmV3IE91dHB1dEZpbHRlcih0aGlzLCAndGV4dFRyYWNrMScpO1xuICAgICAgY29uc3QgY2hhbm5lbDIgPSBuZXcgT3V0cHV0RmlsdGVyKHRoaXMsICd0ZXh0VHJhY2syJyk7XG4gICAgICBjb25zdCBjaGFubmVsMyA9IG5ldyBPdXRwdXRGaWx0ZXIodGhpcywgJ3RleHRUcmFjazMnKTtcbiAgICAgIGNvbnN0IGNoYW5uZWw0ID0gbmV3IE91dHB1dEZpbHRlcih0aGlzLCAndGV4dFRyYWNrNCcpO1xuICAgICAgdGhpcy5jZWE2MDhQYXJzZXIxID0gbmV3IENlYTYwOFBhcnNlcigxLCBjaGFubmVsMSwgY2hhbm5lbDIpO1xuICAgICAgdGhpcy5jZWE2MDhQYXJzZXIyID0gbmV3IENlYTYwOFBhcnNlcigzLCBjaGFubmVsMywgY2hhbm5lbDQpO1xuICAgIH1cbiAgfVxuICBhZGRDdWVzKHRyYWNrTmFtZSwgc3RhcnRUaW1lLCBlbmRUaW1lLCBzY3JlZW4sIGN1ZVJhbmdlcykge1xuICAgIC8vIHNraXAgY3VlcyB3aGljaCBvdmVybGFwIG1vcmUgdGhhbiA1MCUgd2l0aCBwcmV2aW91c2x5IHBhcnNlZCB0aW1lIHJhbmdlc1xuICAgIGxldCBtZXJnZWQgPSBmYWxzZTtcbiAgICBmb3IgKGxldCBpID0gY3VlUmFuZ2VzLmxlbmd0aDsgaS0tOykge1xuICAgICAgY29uc3QgY3VlUmFuZ2UgPSBjdWVSYW5nZXNbaV07XG4gICAgICBjb25zdCBvdmVybGFwID0gaW50ZXJzZWN0aW9uKGN1ZVJhbmdlWzBdLCBjdWVSYW5nZVsxXSwgc3RhcnRUaW1lLCBlbmRUaW1lKTtcbiAgICAgIGlmIChvdmVybGFwID49IDApIHtcbiAgICAgICAgY3VlUmFuZ2VbMF0gPSBNYXRoLm1pbihjdWVSYW5nZVswXSwgc3RhcnRUaW1lKTtcbiAgICAgICAgY3VlUmFuZ2VbMV0gPSBNYXRoLm1heChjdWVSYW5nZVsxXSwgZW5kVGltZSk7XG4gICAgICAgIG1lcmdlZCA9IHRydWU7XG4gICAgICAgIGlmIChvdmVybGFwIC8gKGVuZFRpbWUgLSBzdGFydFRpbWUpID4gMC41KSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIGlmICghbWVyZ2VkKSB7XG4gICAgICBjdWVSYW5nZXMucHVzaChbc3RhcnRUaW1lLCBlbmRUaW1lXSk7XG4gICAgfVxuICAgIGlmICh0aGlzLmNvbmZpZy5yZW5kZXJUZXh0VHJhY2tzTmF0aXZlbHkpIHtcbiAgICAgIGNvbnN0IHRyYWNrID0gdGhpcy5jYXB0aW9uc1RyYWNrc1t0cmFja05hbWVdO1xuICAgICAgdGhpcy5DdWVzLm5ld0N1ZSh0cmFjaywgc3RhcnRUaW1lLCBlbmRUaW1lLCBzY3JlZW4pO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCBjdWVzID0gdGhpcy5DdWVzLm5ld0N1ZShudWxsLCBzdGFydFRpbWUsIGVuZFRpbWUsIHNjcmVlbik7XG4gICAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5DVUVTX1BBUlNFRCwge1xuICAgICAgICB0eXBlOiAnY2FwdGlvbnMnLFxuICAgICAgICBjdWVzLFxuICAgICAgICB0cmFjazogdHJhY2tOYW1lXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICAvLyBUcmlnZ2VyZWQgd2hlbiBhbiBpbml0aWFsIFBUUyBpcyBmb3VuZDsgdXNlZCBmb3Igc3luY2hyb25pc2F0aW9uIG9mIFdlYlZUVC5cbiAgb25Jbml0UHRzRm91bmQoZXZlbnQsIHtcbiAgICBmcmFnLFxuICAgIGlkLFxuICAgIGluaXRQVFMsXG4gICAgdGltZXNjYWxlXG4gIH0pIHtcbiAgICBjb25zdCB7XG4gICAgICB1bnBhcnNlZFZ0dEZyYWdzXG4gICAgfSA9IHRoaXM7XG4gICAgaWYgKGlkID09PSAnbWFpbicpIHtcbiAgICAgIHRoaXMuaW5pdFBUU1tmcmFnLmNjXSA9IHtcbiAgICAgICAgYmFzZVRpbWU6IGluaXRQVFMsXG4gICAgICAgIHRpbWVzY2FsZVxuICAgICAgfTtcbiAgICB9XG5cbiAgICAvLyBEdWUgdG8gYXN5bmNocm9ub3VzIHByb2Nlc3NpbmcsIGluaXRpYWwgUFRTIG1heSBhcnJpdmUgbGF0ZXIgdGhhbiB0aGUgZmlyc3QgVlRUIGZyYWdtZW50cyBhcmUgbG9hZGVkLlxuICAgIC8vIFBhcnNlIGFueSB1bnBhcnNlZCBmcmFnbWVudHMgdXBvbiByZWNlaXZpbmcgdGhlIGluaXRpYWwgUFRTLlxuICAgIGlmICh1bnBhcnNlZFZ0dEZyYWdzLmxlbmd0aCkge1xuICAgICAgdGhpcy51bnBhcnNlZFZ0dEZyYWdzID0gW107XG4gICAgICB1bnBhcnNlZFZ0dEZyYWdzLmZvckVhY2goZnJhZyA9PiB7XG4gICAgICAgIHRoaXMub25GcmFnTG9hZGVkKEV2ZW50cy5GUkFHX0xPQURFRCwgZnJhZyk7XG4gICAgICB9KTtcbiAgICB9XG4gIH1cbiAgZ2V0RXhpc3RpbmdUcmFjayhsYWJlbCwgbGFuZ3VhZ2UpIHtcbiAgICBjb25zdCB7XG4gICAgICBtZWRpYVxuICAgIH0gPSB0aGlzO1xuICAgIGlmIChtZWRpYSkge1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBtZWRpYS50ZXh0VHJhY2tzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGNvbnN0IHRleHRUcmFjayA9IG1lZGlhLnRleHRUcmFja3NbaV07XG4gICAgICAgIGlmIChjYW5SZXVzZVZ0dFRleHRUcmFjayh0ZXh0VHJhY2ssIHtcbiAgICAgICAgICBuYW1lOiBsYWJlbCxcbiAgICAgICAgICBsYW5nOiBsYW5ndWFnZSxcbiAgICAgICAgICBhdHRyczoge31cbiAgICAgICAgfSkpIHtcbiAgICAgICAgICByZXR1cm4gdGV4dFRyYWNrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG4gIGNyZWF0ZUNhcHRpb25zVHJhY2sodHJhY2tOYW1lKSB7XG4gICAgaWYgKHRoaXMuY29uZmlnLnJlbmRlclRleHRUcmFja3NOYXRpdmVseSkge1xuICAgICAgdGhpcy5jcmVhdGVOYXRpdmVUcmFjayh0cmFja05hbWUpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmNyZWF0ZU5vbk5hdGl2ZVRyYWNrKHRyYWNrTmFtZSk7XG4gICAgfVxuICB9XG4gIGNyZWF0ZU5hdGl2ZVRyYWNrKHRyYWNrTmFtZSkge1xuICAgIGlmICh0aGlzLmNhcHRpb25zVHJhY2tzW3RyYWNrTmFtZV0pIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3Qge1xuICAgICAgY2FwdGlvbnNQcm9wZXJ0aWVzLFxuICAgICAgY2FwdGlvbnNUcmFja3MsXG4gICAgICBtZWRpYVxuICAgIH0gPSB0aGlzO1xuICAgIGNvbnN0IHtcbiAgICAgIGxhYmVsLFxuICAgICAgbGFuZ3VhZ2VDb2RlXG4gICAgfSA9IGNhcHRpb25zUHJvcGVydGllc1t0cmFja05hbWVdO1xuICAgIC8vIEVuYWJsZSByZXVzZSBvZiBleGlzdGluZyB0ZXh0IHRyYWNrLlxuICAgIGNvbnN0IGV4aXN0aW5nVHJhY2sgPSB0aGlzLmdldEV4aXN0aW5nVHJhY2sobGFiZWwsIGxhbmd1YWdlQ29kZSk7XG4gICAgaWYgKCFleGlzdGluZ1RyYWNrKSB7XG4gICAgICBjb25zdCB0ZXh0VHJhY2sgPSB0aGlzLmNyZWF0ZVRleHRUcmFjaygnY2FwdGlvbnMnLCBsYWJlbCwgbGFuZ3VhZ2VDb2RlKTtcbiAgICAgIGlmICh0ZXh0VHJhY2spIHtcbiAgICAgICAgLy8gU2V0IGEgc3BlY2lhbCBwcm9wZXJ0eSBvbiB0aGUgdHJhY2sgc28gd2Uga25vdyBpdCdzIG1hbmFnZWQgYnkgSGxzLmpzXG4gICAgICAgIHRleHRUcmFja1t0cmFja05hbWVdID0gdHJ1ZTtcbiAgICAgICAgY2FwdGlvbnNUcmFja3NbdHJhY2tOYW1lXSA9IHRleHRUcmFjaztcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgY2FwdGlvbnNUcmFja3NbdHJhY2tOYW1lXSA9IGV4aXN0aW5nVHJhY2s7XG4gICAgICBjbGVhckN1cnJlbnRDdWVzKGNhcHRpb25zVHJhY2tzW3RyYWNrTmFtZV0pO1xuICAgICAgc2VuZEFkZFRyYWNrRXZlbnQoY2FwdGlvbnNUcmFja3NbdHJhY2tOYW1lXSwgbWVkaWEpO1xuICAgIH1cbiAgfVxuICBjcmVhdGVOb25OYXRpdmVUcmFjayh0cmFja05hbWUpIHtcbiAgICBpZiAodGhpcy5ub25OYXRpdmVDYXB0aW9uc1RyYWNrc1t0cmFja05hbWVdKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIENyZWF0ZSBhIGxpc3Qgb2YgYSBzaW5nbGUgdHJhY2sgZm9yIHRoZSBwcm92aWRlciB0byBjb25zdW1lXG4gICAgY29uc3QgdHJhY2tQcm9wZXJ0aWVzID0gdGhpcy5jYXB0aW9uc1Byb3BlcnRpZXNbdHJhY2tOYW1lXTtcbiAgICBpZiAoIXRyYWNrUHJvcGVydGllcykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBsYWJlbCA9IHRyYWNrUHJvcGVydGllcy5sYWJlbDtcbiAgICBjb25zdCB0cmFjayA9IHtcbiAgICAgIF9pZDogdHJhY2tOYW1lLFxuICAgICAgbGFiZWwsXG4gICAgICBraW5kOiAnY2FwdGlvbnMnLFxuICAgICAgZGVmYXVsdDogdHJhY2tQcm9wZXJ0aWVzLm1lZGlhID8gISF0cmFja1Byb3BlcnRpZXMubWVkaWEuZGVmYXVsdCA6IGZhbHNlLFxuICAgICAgY2xvc2VkQ2FwdGlvbnM6IHRyYWNrUHJvcGVydGllcy5tZWRpYVxuICAgIH07XG4gICAgdGhpcy5ub25OYXRpdmVDYXB0aW9uc1RyYWNrc1t0cmFja05hbWVdID0gdHJhY2s7XG4gICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuTk9OX05BVElWRV9URVhUX1RSQUNLU19GT1VORCwge1xuICAgICAgdHJhY2tzOiBbdHJhY2tdXG4gICAgfSk7XG4gIH1cbiAgY3JlYXRlVGV4dFRyYWNrKGtpbmQsIGxhYmVsLCBsYW5nKSB7XG4gICAgY29uc3QgbWVkaWEgPSB0aGlzLm1lZGlhO1xuICAgIGlmICghbWVkaWEpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgcmV0dXJuIG1lZGlhLmFkZFRleHRUcmFjayhraW5kLCBsYWJlbCwgbGFuZyk7XG4gIH1cbiAgb25NZWRpYUF0dGFjaGluZyhldmVudCwgZGF0YSkge1xuICAgIHRoaXMubWVkaWEgPSBkYXRhLm1lZGlhO1xuICAgIHRoaXMuX2NsZWFuVHJhY2tzKCk7XG4gIH1cbiAgb25NZWRpYURldGFjaGluZygpIHtcbiAgICBjb25zdCB7XG4gICAgICBjYXB0aW9uc1RyYWNrc1xuICAgIH0gPSB0aGlzO1xuICAgIE9iamVjdC5rZXlzKGNhcHRpb25zVHJhY2tzKS5mb3JFYWNoKHRyYWNrTmFtZSA9PiB7XG4gICAgICBjbGVhckN1cnJlbnRDdWVzKGNhcHRpb25zVHJhY2tzW3RyYWNrTmFtZV0pO1xuICAgICAgZGVsZXRlIGNhcHRpb25zVHJhY2tzW3RyYWNrTmFtZV07XG4gICAgfSk7XG4gICAgdGhpcy5ub25OYXRpdmVDYXB0aW9uc1RyYWNrcyA9IHt9O1xuICB9XG4gIG9uTWFuaWZlc3RMb2FkaW5nKCkge1xuICAgIC8vIERldGVjdCBkaXNjb250aW51aXR5IGluIHZpZGVvIGZyYWdtZW50IChDRUEtNjA4KSBwYXJzaW5nXG4gICAgdGhpcy5sYXN0Q2MgPSAtMTtcbiAgICB0aGlzLmxhc3RTbiA9IC0xO1xuICAgIHRoaXMubGFzdFBhcnRJbmRleCA9IC0xO1xuICAgIC8vIERldGVjdCBkaXNjb250aW51aXR5IGluIHN1YnRpdGxlIG1hbmlmZXN0c1xuICAgIHRoaXMucHJldkNDID0gLTE7XG4gICAgdGhpcy52dHRDQ3MgPSBuZXdWVFRDQ3MoKTtcbiAgICAvLyBSZXNldCB0cmFja3NcbiAgICB0aGlzLl9jbGVhblRyYWNrcygpO1xuICAgIHRoaXMudHJhY2tzID0gW107XG4gICAgdGhpcy5jYXB0aW9uc1RyYWNrcyA9IHt9O1xuICAgIHRoaXMubm9uTmF0aXZlQ2FwdGlvbnNUcmFja3MgPSB7fTtcbiAgICB0aGlzLnRleHRUcmFja3MgPSBbXTtcbiAgICB0aGlzLnVucGFyc2VkVnR0RnJhZ3MgPSBbXTtcbiAgICB0aGlzLmluaXRQVFMgPSBbXTtcbiAgICBpZiAodGhpcy5jZWE2MDhQYXJzZXIxICYmIHRoaXMuY2VhNjA4UGFyc2VyMikge1xuICAgICAgdGhpcy5jZWE2MDhQYXJzZXIxLnJlc2V0KCk7XG4gICAgICB0aGlzLmNlYTYwOFBhcnNlcjIucmVzZXQoKTtcbiAgICB9XG4gIH1cbiAgX2NsZWFuVHJhY2tzKCkge1xuICAgIC8vIGNsZWFyIG91dGRhdGVkIHN1YnRpdGxlc1xuICAgIGNvbnN0IHtcbiAgICAgIG1lZGlhXG4gICAgfSA9IHRoaXM7XG4gICAgaWYgKCFtZWRpYSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB0ZXh0VHJhY2tzID0gbWVkaWEudGV4dFRyYWNrcztcbiAgICBpZiAodGV4dFRyYWNrcykge1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0ZXh0VHJhY2tzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGNsZWFyQ3VycmVudEN1ZXModGV4dFRyYWNrc1tpXSk7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIG9uU3VidGl0bGVUcmFja3NVcGRhdGVkKGV2ZW50LCBkYXRhKSB7XG4gICAgY29uc3QgdHJhY2tzID0gZGF0YS5zdWJ0aXRsZVRyYWNrcyB8fCBbXTtcbiAgICBjb25zdCBoYXNJTVNDMSA9IHRyYWNrcy5zb21lKHRyYWNrID0+IHRyYWNrLnRleHRDb2RlYyA9PT0gSU1TQzFfQ09ERUMpO1xuICAgIGlmICh0aGlzLmNvbmZpZy5lbmFibGVXZWJWVFQgfHwgaGFzSU1TQzEgJiYgdGhpcy5jb25maWcuZW5hYmxlSU1TQzEpIHtcbiAgICAgIGNvbnN0IGxpc3RJc0lkZW50aWNhbCA9IHN1YnRpdGxlT3B0aW9uc0lkZW50aWNhbCh0aGlzLnRyYWNrcywgdHJhY2tzKTtcbiAgICAgIGlmIChsaXN0SXNJZGVudGljYWwpIHtcbiAgICAgICAgdGhpcy50cmFja3MgPSB0cmFja3M7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHRoaXMudGV4dFRyYWNrcyA9IFtdO1xuICAgICAgdGhpcy50cmFja3MgPSB0cmFja3M7XG4gICAgICBpZiAodGhpcy5jb25maWcucmVuZGVyVGV4dFRyYWNrc05hdGl2ZWx5KSB7XG4gICAgICAgIGNvbnN0IG1lZGlhID0gdGhpcy5tZWRpYTtcbiAgICAgICAgY29uc3QgaW5Vc2VUcmFja3MgPSBtZWRpYSA/IGZpbHRlclN1YnRpdGxlVHJhY2tzKG1lZGlhLnRleHRUcmFja3MpIDogbnVsbDtcbiAgICAgICAgdGhpcy50cmFja3MuZm9yRWFjaCgodHJhY2ssIGluZGV4KSA9PiB7XG4gICAgICAgICAgLy8gUmV1c2UgdHJhY2tzIHdpdGggdGhlIHNhbWUgbGFiZWwgYW5kIGxhbmcsIGJ1dCBkbyBub3QgcmV1c2UgNjA4LzcwOCB0cmFja3NcbiAgICAgICAgICBsZXQgdGV4dFRyYWNrO1xuICAgICAgICAgIGlmIChpblVzZVRyYWNrcykge1xuICAgICAgICAgICAgbGV0IGluVXNlVHJhY2sgPSBudWxsO1xuICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBpblVzZVRyYWNrcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICBpZiAoaW5Vc2VUcmFja3NbaV0gJiYgY2FuUmV1c2VWdHRUZXh0VHJhY2soaW5Vc2VUcmFja3NbaV0sIHRyYWNrKSkge1xuICAgICAgICAgICAgICAgIGluVXNlVHJhY2sgPSBpblVzZVRyYWNrc1tpXTtcbiAgICAgICAgICAgICAgICBpblVzZVRyYWNrc1tpXSA9IG51bGw7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChpblVzZVRyYWNrKSB7XG4gICAgICAgICAgICAgIHRleHRUcmFjayA9IGluVXNlVHJhY2s7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICh0ZXh0VHJhY2spIHtcbiAgICAgICAgICAgIGNsZWFyQ3VycmVudEN1ZXModGV4dFRyYWNrKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29uc3QgdGV4dFRyYWNrS2luZCA9IGNhcHRpb25zT3JTdWJ0aXRsZXNGcm9tQ2hhcmFjdGVyaXN0aWNzKHRyYWNrKTtcbiAgICAgICAgICAgIHRleHRUcmFjayA9IHRoaXMuY3JlYXRlVGV4dFRyYWNrKHRleHRUcmFja0tpbmQsIHRyYWNrLm5hbWUsIHRyYWNrLmxhbmcpO1xuICAgICAgICAgICAgaWYgKHRleHRUcmFjaykge1xuICAgICAgICAgICAgICB0ZXh0VHJhY2subW9kZSA9ICdkaXNhYmxlZCc7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICh0ZXh0VHJhY2spIHtcbiAgICAgICAgICAgIHRoaXMudGV4dFRyYWNrcy5wdXNoKHRleHRUcmFjayk7XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgLy8gV2FybiB3aGVuIHZpZGVvIGVsZW1lbnQgaGFzIGNhcHRpb25zIG9yIHN1YnRpdGxlIFRleHRUcmFja3MgY2FycmllZCBvdmVyIGZyb20gYW5vdGhlciBzb3VyY2VcbiAgICAgICAgaWYgKGluVXNlVHJhY2tzICE9IG51bGwgJiYgaW5Vc2VUcmFja3MubGVuZ3RoKSB7XG4gICAgICAgICAgY29uc3QgdW51c2VkVGV4dFRyYWNrcyA9IGluVXNlVHJhY2tzLmZpbHRlcih0ID0+IHQgIT09IG51bGwpLm1hcCh0ID0+IHQubGFiZWwpO1xuICAgICAgICAgIGlmICh1bnVzZWRUZXh0VHJhY2tzLmxlbmd0aCkge1xuICAgICAgICAgICAgbG9nZ2VyLndhcm4oYE1lZGlhIGVsZW1lbnQgY29udGFpbnMgdW51c2VkIHN1YnRpdGxlIHRyYWNrczogJHt1bnVzZWRUZXh0VHJhY2tzLmpvaW4oJywgJyl9LiBSZXBsYWNlIG1lZGlhIGVsZW1lbnQgZm9yIGVhY2ggc291cmNlIHRvIGNsZWFyIFRleHRUcmFja3MgYW5kIGNhcHRpb25zIG1lbnUuYCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKHRoaXMudHJhY2tzLmxlbmd0aCkge1xuICAgICAgICAvLyBDcmVhdGUgYSBsaXN0IG9mIHRyYWNrcyBmb3IgdGhlIHByb3ZpZGVyIHRvIGNvbnN1bWVcbiAgICAgICAgY29uc3QgdHJhY2tzTGlzdCA9IHRoaXMudHJhY2tzLm1hcCh0cmFjayA9PiB7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGxhYmVsOiB0cmFjay5uYW1lLFxuICAgICAgICAgICAga2luZDogdHJhY2sudHlwZS50b0xvd2VyQ2FzZSgpLFxuICAgICAgICAgICAgZGVmYXVsdDogdHJhY2suZGVmYXVsdCxcbiAgICAgICAgICAgIHN1YnRpdGxlVHJhY2s6IHRyYWNrXG4gICAgICAgICAgfTtcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLk5PTl9OQVRJVkVfVEVYVF9UUkFDS1NfRk9VTkQsIHtcbiAgICAgICAgICB0cmFja3M6IHRyYWNrc0xpc3RcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIG9uTWFuaWZlc3RMb2FkZWQoZXZlbnQsIGRhdGEpIHtcbiAgICBpZiAodGhpcy5jb25maWcuZW5hYmxlQ0VBNzA4Q2FwdGlvbnMgJiYgZGF0YS5jYXB0aW9ucykge1xuICAgICAgZGF0YS5jYXB0aW9ucy5mb3JFYWNoKGNhcHRpb25zVHJhY2sgPT4ge1xuICAgICAgICBjb25zdCBpbnN0cmVhbUlkTWF0Y2ggPSAvKD86Q0N8U0VSVklDRSkoWzEtNF0pLy5leGVjKGNhcHRpb25zVHJhY2suaW5zdHJlYW1JZCk7XG4gICAgICAgIGlmICghaW5zdHJlYW1JZE1hdGNoKSB7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHRyYWNrTmFtZSA9IGB0ZXh0VHJhY2ske2luc3RyZWFtSWRNYXRjaFsxXX1gO1xuICAgICAgICBjb25zdCB0cmFja1Byb3BlcnRpZXMgPSB0aGlzLmNhcHRpb25zUHJvcGVydGllc1t0cmFja05hbWVdO1xuICAgICAgICBpZiAoIXRyYWNrUHJvcGVydGllcykge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICB0cmFja1Byb3BlcnRpZXMubGFiZWwgPSBjYXB0aW9uc1RyYWNrLm5hbWU7XG4gICAgICAgIGlmIChjYXB0aW9uc1RyYWNrLmxhbmcpIHtcbiAgICAgICAgICAvLyBvcHRpb25hbCBhdHRyaWJ1dGVcbiAgICAgICAgICB0cmFja1Byb3BlcnRpZXMubGFuZ3VhZ2VDb2RlID0gY2FwdGlvbnNUcmFjay5sYW5nO1xuICAgICAgICB9XG4gICAgICAgIHRyYWNrUHJvcGVydGllcy5tZWRpYSA9IGNhcHRpb25zVHJhY2s7XG4gICAgICB9KTtcbiAgICB9XG4gIH1cbiAgY2xvc2VkQ2FwdGlvbnNGb3JMZXZlbChmcmFnKSB7XG4gICAgY29uc3QgbGV2ZWwgPSB0aGlzLmhscy5sZXZlbHNbZnJhZy5sZXZlbF07XG4gICAgcmV0dXJuIGxldmVsID09IG51bGwgPyB2b2lkIDAgOiBsZXZlbC5hdHRyc1snQ0xPU0VELUNBUFRJT05TJ107XG4gIH1cbiAgb25GcmFnTG9hZGluZyhldmVudCwgZGF0YSkge1xuICAgIC8vIGlmIHRoaXMgZnJhZyBpc24ndCBjb250aWd1b3VzLCBjbGVhciB0aGUgcGFyc2VyIHNvIGN1ZXMgd2l0aCBiYWQgc3RhcnQvZW5kIHRpbWVzIGFyZW4ndCBhZGRlZCB0byB0aGUgdGV4dFRyYWNrXG4gICAgaWYgKHRoaXMuZW5hYmxlZCAmJiBkYXRhLmZyYWcudHlwZSA9PT0gUGxheWxpc3RMZXZlbFR5cGUuTUFJTikge1xuICAgICAgdmFyIF9kYXRhJHBhcnQkaW5kZXgsIF9kYXRhJHBhcnQ7XG4gICAgICBjb25zdCB7XG4gICAgICAgIGNlYTYwOFBhcnNlcjEsXG4gICAgICAgIGNlYTYwOFBhcnNlcjIsXG4gICAgICAgIGxhc3RTblxuICAgICAgfSA9IHRoaXM7XG4gICAgICBjb25zdCB7XG4gICAgICAgIGNjLFxuICAgICAgICBzblxuICAgICAgfSA9IGRhdGEuZnJhZztcbiAgICAgIGNvbnN0IHBhcnRJbmRleCA9IChfZGF0YSRwYXJ0JGluZGV4ID0gKF9kYXRhJHBhcnQgPSBkYXRhLnBhcnQpID09IG51bGwgPyB2b2lkIDAgOiBfZGF0YSRwYXJ0LmluZGV4KSAhPSBudWxsID8gX2RhdGEkcGFydCRpbmRleCA6IC0xO1xuICAgICAgaWYgKGNlYTYwOFBhcnNlcjEgJiYgY2VhNjA4UGFyc2VyMikge1xuICAgICAgICBpZiAoc24gIT09IGxhc3RTbiArIDEgfHwgc24gPT09IGxhc3RTbiAmJiBwYXJ0SW5kZXggIT09IHRoaXMubGFzdFBhcnRJbmRleCArIDEgfHwgY2MgIT09IHRoaXMubGFzdENjKSB7XG4gICAgICAgICAgY2VhNjA4UGFyc2VyMS5yZXNldCgpO1xuICAgICAgICAgIGNlYTYwOFBhcnNlcjIucmVzZXQoKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgdGhpcy5sYXN0Q2MgPSBjYztcbiAgICAgIHRoaXMubGFzdFNuID0gc247XG4gICAgICB0aGlzLmxhc3RQYXJ0SW5kZXggPSBwYXJ0SW5kZXg7XG4gICAgfVxuICB9XG4gIG9uRnJhZ0xvYWRlZChldmVudCwgZGF0YSkge1xuICAgIGNvbnN0IHtcbiAgICAgIGZyYWcsXG4gICAgICBwYXlsb2FkXG4gICAgfSA9IGRhdGE7XG4gICAgaWYgKGZyYWcudHlwZSA9PT0gUGxheWxpc3RMZXZlbFR5cGUuU1VCVElUTEUpIHtcbiAgICAgIC8vIElmIGZyYWdtZW50IGlzIHN1YnRpdGxlIHR5cGUsIHBhcnNlIGFzIFdlYlZUVC5cbiAgICAgIGlmIChwYXlsb2FkLmJ5dGVMZW5ndGgpIHtcbiAgICAgICAgY29uc3QgZGVjcnlwdERhdGEgPSBmcmFnLmRlY3J5cHRkYXRhO1xuICAgICAgICAvLyBmcmFnbWVudCBhZnRlciBkZWNyeXB0aW9uIGhhcyBhIHN0YXRzIG9iamVjdFxuICAgICAgICBjb25zdCBkZWNyeXB0ZWQgPSAoJ3N0YXRzJyBpbiBkYXRhKTtcbiAgICAgICAgLy8gSWYgdGhlIHN1YnRpdGxlcyBhcmUgbm90IGVuY3J5cHRlZCwgcGFyc2UgVlRUcyBub3cuIE90aGVyd2lzZSwgd2UgbmVlZCB0byB3YWl0LlxuICAgICAgICBpZiAoZGVjcnlwdERhdGEgPT0gbnVsbCB8fCAhZGVjcnlwdERhdGEuZW5jcnlwdGVkIHx8IGRlY3J5cHRlZCkge1xuICAgICAgICAgIGNvbnN0IHRyYWNrUGxheWxpc3RNZWRpYSA9IHRoaXMudHJhY2tzW2ZyYWcubGV2ZWxdO1xuICAgICAgICAgIGNvbnN0IHZ0dENDcyA9IHRoaXMudnR0Q0NzO1xuICAgICAgICAgIGlmICghdnR0Q0NzW2ZyYWcuY2NdKSB7XG4gICAgICAgICAgICB2dHRDQ3NbZnJhZy5jY10gPSB7XG4gICAgICAgICAgICAgIHN0YXJ0OiBmcmFnLnN0YXJ0LFxuICAgICAgICAgICAgICBwcmV2Q0M6IHRoaXMucHJldkNDLFxuICAgICAgICAgICAgICBuZXc6IHRydWVcbiAgICAgICAgICAgIH07XG4gICAgICAgICAgICB0aGlzLnByZXZDQyA9IGZyYWcuY2M7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICh0cmFja1BsYXlsaXN0TWVkaWEgJiYgdHJhY2tQbGF5bGlzdE1lZGlhLnRleHRDb2RlYyA9PT0gSU1TQzFfQ09ERUMpIHtcbiAgICAgICAgICAgIHRoaXMuX3BhcnNlSU1TQzEoZnJhZywgcGF5bG9hZCk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuX3BhcnNlVlRUcyhkYXRhKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIEluIGNhc2UgdGhlcmUgaXMgbm8gcGF5bG9hZCwgZmluaXNoIHVuc3VjY2Vzc2Z1bGx5LlxuICAgICAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5TVUJUSVRMRV9GUkFHX1BST0NFU1NFRCwge1xuICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgIGZyYWcsXG4gICAgICAgICAgZXJyb3I6IG5ldyBFcnJvcignRW1wdHkgc3VidGl0bGUgcGF5bG9hZCcpXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBfcGFyc2VJTVNDMShmcmFnLCBwYXlsb2FkKSB7XG4gICAgY29uc3QgaGxzID0gdGhpcy5obHM7XG4gICAgcGFyc2VJTVNDMShwYXlsb2FkLCB0aGlzLmluaXRQVFNbZnJhZy5jY10sIGN1ZXMgPT4ge1xuICAgICAgdGhpcy5fYXBwZW5kQ3VlcyhjdWVzLCBmcmFnLmxldmVsKTtcbiAgICAgIGhscy50cmlnZ2VyKEV2ZW50cy5TVUJUSVRMRV9GUkFHX1BST0NFU1NFRCwge1xuICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICBmcmFnOiBmcmFnXG4gICAgICB9KTtcbiAgICB9LCBlcnJvciA9PiB7XG4gICAgICBsb2dnZXIubG9nKGBGYWlsZWQgdG8gcGFyc2UgSU1TQzE6ICR7ZXJyb3J9YCk7XG4gICAgICBobHMudHJpZ2dlcihFdmVudHMuU1VCVElUTEVfRlJBR19QUk9DRVNTRUQsIHtcbiAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgIGZyYWc6IGZyYWcsXG4gICAgICAgIGVycm9yXG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuICBfcGFyc2VWVFRzKGRhdGEpIHtcbiAgICB2YXIgX2ZyYWckaW5pdFNlZ21lbnQ7XG4gICAgY29uc3Qge1xuICAgICAgZnJhZyxcbiAgICAgIHBheWxvYWRcbiAgICB9ID0gZGF0YTtcbiAgICAvLyBXZSBuZWVkIGFuIGluaXRpYWwgc3luY2hyb25pc2F0aW9uIFBUUy4gU3RvcmUgZnJhZ21lbnRzIGFzIGxvbmcgYXMgbm9uZSBoYXMgYXJyaXZlZFxuICAgIGNvbnN0IHtcbiAgICAgIGluaXRQVFMsXG4gICAgICB1bnBhcnNlZFZ0dEZyYWdzXG4gICAgfSA9IHRoaXM7XG4gICAgY29uc3QgbWF4QXZDQyA9IGluaXRQVFMubGVuZ3RoIC0gMTtcbiAgICBpZiAoIWluaXRQVFNbZnJhZy5jY10gJiYgbWF4QXZDQyA9PT0gLTEpIHtcbiAgICAgIHVucGFyc2VkVnR0RnJhZ3MucHVzaChkYXRhKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgaGxzID0gdGhpcy5obHM7XG4gICAgLy8gUGFyc2UgdGhlIFdlYlZUVCBmaWxlIGNvbnRlbnRzLlxuICAgIGNvbnN0IHBheWxvYWRXZWJWVFQgPSAoX2ZyYWckaW5pdFNlZ21lbnQgPSBmcmFnLmluaXRTZWdtZW50KSAhPSBudWxsICYmIF9mcmFnJGluaXRTZWdtZW50LmRhdGEgPyBhcHBlbmRVaW50OEFycmF5KGZyYWcuaW5pdFNlZ21lbnQuZGF0YSwgbmV3IFVpbnQ4QXJyYXkocGF5bG9hZCkpIDogcGF5bG9hZDtcbiAgICBwYXJzZVdlYlZUVChwYXlsb2FkV2ViVlRULCB0aGlzLmluaXRQVFNbZnJhZy5jY10sIHRoaXMudnR0Q0NzLCBmcmFnLmNjLCBmcmFnLnN0YXJ0LCBjdWVzID0+IHtcbiAgICAgIHRoaXMuX2FwcGVuZEN1ZXMoY3VlcywgZnJhZy5sZXZlbCk7XG4gICAgICBobHMudHJpZ2dlcihFdmVudHMuU1VCVElUTEVfRlJBR19QUk9DRVNTRUQsIHtcbiAgICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgICAgZnJhZzogZnJhZ1xuICAgICAgfSk7XG4gICAgfSwgZXJyb3IgPT4ge1xuICAgICAgY29uc3QgbWlzc2luZ0luaXRQVFMgPSBlcnJvci5tZXNzYWdlID09PSAnTWlzc2luZyBpbml0UFRTIGZvciBWVFQgTVBFR1RTJztcbiAgICAgIGlmIChtaXNzaW5nSW5pdFBUUykge1xuICAgICAgICB1bnBhcnNlZFZ0dEZyYWdzLnB1c2goZGF0YSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLl9mYWxsYmFja1RvSU1TQzEoZnJhZywgcGF5bG9hZCk7XG4gICAgICB9XG4gICAgICAvLyBTb21ldGhpbmcgd2VudCB3cm9uZyB3aGlsZSBwYXJzaW5nLiBUcmlnZ2VyIGV2ZW50IHdpdGggc3VjY2VzcyBmYWxzZS5cbiAgICAgIGxvZ2dlci5sb2coYEZhaWxlZCB0byBwYXJzZSBWVFQgY3VlOiAke2Vycm9yfWApO1xuICAgICAgaWYgKG1pc3NpbmdJbml0UFRTICYmIG1heEF2Q0MgPiBmcmFnLmNjKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGhscy50cmlnZ2VyKEV2ZW50cy5TVUJUSVRMRV9GUkFHX1BST0NFU1NFRCwge1xuICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgZnJhZzogZnJhZyxcbiAgICAgICAgZXJyb3JcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG4gIF9mYWxsYmFja1RvSU1TQzEoZnJhZywgcGF5bG9hZCkge1xuICAgIC8vIElmIHRleHRDb2RlYyBpcyB1bmtub3duLCB0cnkgcGFyc2luZyBhcyBJTVNDMS4gU2V0IHRleHRDb2RlYyBiYXNlZCBvbiB0aGUgcmVzdWx0XG4gICAgY29uc3QgdHJhY2tQbGF5bGlzdE1lZGlhID0gdGhpcy50cmFja3NbZnJhZy5sZXZlbF07XG4gICAgaWYgKCF0cmFja1BsYXlsaXN0TWVkaWEudGV4dENvZGVjKSB7XG4gICAgICBwYXJzZUlNU0MxKHBheWxvYWQsIHRoaXMuaW5pdFBUU1tmcmFnLmNjXSwgKCkgPT4ge1xuICAgICAgICB0cmFja1BsYXlsaXN0TWVkaWEudGV4dENvZGVjID0gSU1TQzFfQ09ERUM7XG4gICAgICAgIHRoaXMuX3BhcnNlSU1TQzEoZnJhZywgcGF5bG9hZCk7XG4gICAgICB9LCAoKSA9PiB7XG4gICAgICAgIHRyYWNrUGxheWxpc3RNZWRpYS50ZXh0Q29kZWMgPSAnd3Z0dCc7XG4gICAgICB9KTtcbiAgICB9XG4gIH1cbiAgX2FwcGVuZEN1ZXMoY3VlcywgZnJhZ0xldmVsKSB7XG4gICAgY29uc3QgaGxzID0gdGhpcy5obHM7XG4gICAgaWYgKHRoaXMuY29uZmlnLnJlbmRlclRleHRUcmFja3NOYXRpdmVseSkge1xuICAgICAgY29uc3QgdGV4dFRyYWNrID0gdGhpcy50ZXh0VHJhY2tzW2ZyYWdMZXZlbF07XG4gICAgICAvLyBXZWJWVFRQYXJzZXIucGFyc2UgaXMgYW4gYXN5bmMgbWV0aG9kIGFuZCBpZiB0aGUgY3VycmVudGx5IHNlbGVjdGVkIHRleHQgdHJhY2sgbW9kZSBpcyBzZXQgdG8gXCJkaXNhYmxlZFwiXG4gICAgICAvLyBiZWZvcmUgcGFyc2luZyBpcyBkb25lIHRoZW4gZG9uJ3QgdHJ5IHRvIGFjY2VzcyBjdXJyZW50VHJhY2suY3Vlcy5nZXRDdWVCeUlkIGFzIGN1ZXMgd2lsbCBiZSBudWxsXG4gICAgICAvLyBhbmQgdHJ5aW5nIHRvIGFjY2VzcyBnZXRDdWVCeUlkIG1ldGhvZCBvZiBjdWVzIHdpbGwgdGhyb3cgYW4gZXhjZXB0aW9uXG4gICAgICAvLyBCZWNhdXNlIHdlIGNoZWNrIGlmIHRoZSBtb2RlIGlzIGRpc2FibGVkLCB3ZSBjYW4gZm9yY2UgY2hlY2sgYGN1ZXNgIGJlbG93LiBUaGV5IGNhbid0IGJlIG51bGwuXG4gICAgICBpZiAoIXRleHRUcmFjayB8fCB0ZXh0VHJhY2subW9kZSA9PT0gJ2Rpc2FibGVkJykge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjdWVzLmZvckVhY2goY3VlID0+IGFkZEN1ZVRvVHJhY2sodGV4dFRyYWNrLCBjdWUpKTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgY3VycmVudFRyYWNrID0gdGhpcy50cmFja3NbZnJhZ0xldmVsXTtcbiAgICAgIGlmICghY3VycmVudFRyYWNrKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHRyYWNrID0gY3VycmVudFRyYWNrLmRlZmF1bHQgPyAnZGVmYXVsdCcgOiAnc3VidGl0bGVzJyArIGZyYWdMZXZlbDtcbiAgICAgIGhscy50cmlnZ2VyKEV2ZW50cy5DVUVTX1BBUlNFRCwge1xuICAgICAgICB0eXBlOiAnc3VidGl0bGVzJyxcbiAgICAgICAgY3VlcyxcbiAgICAgICAgdHJhY2tcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuICBvbkZyYWdEZWNyeXB0ZWQoZXZlbnQsIGRhdGEpIHtcbiAgICBjb25zdCB7XG4gICAgICBmcmFnXG4gICAgfSA9IGRhdGE7XG4gICAgaWYgKGZyYWcudHlwZSA9PT0gUGxheWxpc3RMZXZlbFR5cGUuU1VCVElUTEUpIHtcbiAgICAgIHRoaXMub25GcmFnTG9hZGVkKEV2ZW50cy5GUkFHX0xPQURFRCwgZGF0YSk7XG4gICAgfVxuICB9XG4gIG9uU3VidGl0bGVUcmFja3NDbGVhcmVkKCkge1xuICAgIHRoaXMudHJhY2tzID0gW107XG4gICAgdGhpcy5jYXB0aW9uc1RyYWNrcyA9IHt9O1xuICB9XG4gIG9uRnJhZ1BhcnNpbmdVc2VyZGF0YShldmVudCwgZGF0YSkge1xuICAgIHRoaXMuaW5pdENlYTYwOFBhcnNlcnMoKTtcbiAgICBjb25zdCB7XG4gICAgICBjZWE2MDhQYXJzZXIxLFxuICAgICAgY2VhNjA4UGFyc2VyMlxuICAgIH0gPSB0aGlzO1xuICAgIGlmICghdGhpcy5lbmFibGVkIHx8ICFjZWE2MDhQYXJzZXIxIHx8ICFjZWE2MDhQYXJzZXIyKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHtcbiAgICAgIGZyYWcsXG4gICAgICBzYW1wbGVzXG4gICAgfSA9IGRhdGE7XG4gICAgaWYgKGZyYWcudHlwZSA9PT0gUGxheWxpc3RMZXZlbFR5cGUuTUFJTiAmJiB0aGlzLmNsb3NlZENhcHRpb25zRm9yTGV2ZWwoZnJhZykgPT09ICdOT05FJykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyBJZiB0aGUgZXZlbnQgY29udGFpbnMgY2FwdGlvbnMgKGZvdW5kIGluIHRoZSBieXRlcyBwcm9wZXJ0eSksIHB1c2ggYWxsIGJ5dGVzIGludG8gdGhlIHBhcnNlciBpbW1lZGlhdGVseVxuICAgIC8vIEl0IHdpbGwgY3JlYXRlIHRoZSBwcm9wZXIgdGltZXN0YW1wcyBiYXNlZCBvbiB0aGUgUFRTIHZhbHVlXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBzYW1wbGVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBjY0J5dGVzID0gc2FtcGxlc1tpXS5ieXRlcztcbiAgICAgIGlmIChjY0J5dGVzKSB7XG4gICAgICAgIGNvbnN0IGNjZGF0YXMgPSB0aGlzLmV4dHJhY3RDZWE2MDhEYXRhKGNjQnl0ZXMpO1xuICAgICAgICBjZWE2MDhQYXJzZXIxLmFkZERhdGEoc2FtcGxlc1tpXS5wdHMsIGNjZGF0YXNbMF0pO1xuICAgICAgICBjZWE2MDhQYXJzZXIyLmFkZERhdGEoc2FtcGxlc1tpXS5wdHMsIGNjZGF0YXNbMV0pO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBvbkJ1ZmZlckZsdXNoaW5nKGV2ZW50LCB7XG4gICAgc3RhcnRPZmZzZXQsXG4gICAgZW5kT2Zmc2V0LFxuICAgIGVuZE9mZnNldFN1YnRpdGxlcyxcbiAgICB0eXBlXG4gIH0pIHtcbiAgICBjb25zdCB7XG4gICAgICBtZWRpYVxuICAgIH0gPSB0aGlzO1xuICAgIGlmICghbWVkaWEgfHwgbWVkaWEuY3VycmVudFRpbWUgPCBlbmRPZmZzZXQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgLy8gQ2xlYXIgNjA4IGNhcHRpb24gY3VlcyBmcm9tIHRoZSBjYXB0aW9ucyBUZXh0VHJhY2tzIHdoZW4gdGhlIHZpZGVvIGJhY2sgYnVmZmVyIGlzIGZsdXNoZWRcbiAgICAvLyBGb3J3YXJkIGN1ZXMgYXJlIG5ldmVyIHJlbW92ZWQgYmVjYXVzZSB3ZSBjYW4gbG9vc2Ugc3RyZWFtZWQgNjA4IGNvbnRlbnQgZnJvbSByZWNlbnQgZnJhZ21lbnRzXG4gICAgaWYgKCF0eXBlIHx8IHR5cGUgPT09ICd2aWRlbycpIHtcbiAgICAgIGNvbnN0IHtcbiAgICAgICAgY2FwdGlvbnNUcmFja3NcbiAgICAgIH0gPSB0aGlzO1xuICAgICAgT2JqZWN0LmtleXMoY2FwdGlvbnNUcmFja3MpLmZvckVhY2godHJhY2tOYW1lID0+IHJlbW92ZUN1ZXNJblJhbmdlKGNhcHRpb25zVHJhY2tzW3RyYWNrTmFtZV0sIHN0YXJ0T2Zmc2V0LCBlbmRPZmZzZXQpKTtcbiAgICB9XG4gICAgaWYgKHRoaXMuY29uZmlnLnJlbmRlclRleHRUcmFja3NOYXRpdmVseSkge1xuICAgICAgLy8gQ2xlYXIgVlRUL0lNU0MxIHN1YnRpdGxlIGN1ZXMgZnJvbSB0aGUgc3VidGl0bGUgVGV4dFRyYWNrcyB3aGVuIHRoZSBiYWNrIGJ1ZmZlciBpcyBmbHVzaGVkXG4gICAgICBpZiAoc3RhcnRPZmZzZXQgPT09IDAgJiYgZW5kT2Zmc2V0U3VidGl0bGVzICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgY29uc3Qge1xuICAgICAgICAgIHRleHRUcmFja3NcbiAgICAgICAgfSA9IHRoaXM7XG4gICAgICAgIE9iamVjdC5rZXlzKHRleHRUcmFja3MpLmZvckVhY2godHJhY2tOYW1lID0+IHJlbW92ZUN1ZXNJblJhbmdlKHRleHRUcmFja3NbdHJhY2tOYW1lXSwgc3RhcnRPZmZzZXQsIGVuZE9mZnNldFN1YnRpdGxlcykpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBleHRyYWN0Q2VhNjA4RGF0YShieXRlQXJyYXkpIHtcbiAgICBjb25zdCBhY3R1YWxDQ0J5dGVzID0gW1tdLCBbXV07XG4gICAgY29uc3QgY291bnQgPSBieXRlQXJyYXlbMF0gJiAweDFmO1xuICAgIGxldCBwb3NpdGlvbiA9IDI7XG4gICAgZm9yIChsZXQgaiA9IDA7IGogPCBjb3VudDsgaisrKSB7XG4gICAgICBjb25zdCB0bXBCeXRlID0gYnl0ZUFycmF5W3Bvc2l0aW9uKytdO1xuICAgICAgY29uc3QgY2NieXRlMSA9IDB4N2YgJiBieXRlQXJyYXlbcG9zaXRpb24rK107XG4gICAgICBjb25zdCBjY2J5dGUyID0gMHg3ZiAmIGJ5dGVBcnJheVtwb3NpdGlvbisrXTtcbiAgICAgIGlmIChjY2J5dGUxID09PSAwICYmIGNjYnl0ZTIgPT09IDApIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICBjb25zdCBjY1ZhbGlkID0gKDB4MDQgJiB0bXBCeXRlKSAhPT0gMDsgLy8gU3VwcG9ydCBhbGwgZm91ciBjaGFubmVsc1xuICAgICAgaWYgKGNjVmFsaWQpIHtcbiAgICAgICAgY29uc3QgY2NUeXBlID0gMHgwMyAmIHRtcEJ5dGU7XG4gICAgICAgIGlmICgweDAwIC8qIENFQTYwOCBmaWVsZDEqLyA9PT0gY2NUeXBlIHx8IDB4MDEgLyogQ0VBNjA4IGZpZWxkMiovID09PSBjY1R5cGUpIHtcbiAgICAgICAgICAvLyBFeGNsdWRlIENFQTcwOCBDQyBkYXRhLlxuICAgICAgICAgIGFjdHVhbENDQnl0ZXNbY2NUeXBlXS5wdXNoKGNjYnl0ZTEpO1xuICAgICAgICAgIGFjdHVhbENDQnl0ZXNbY2NUeXBlXS5wdXNoKGNjYnl0ZTIpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBhY3R1YWxDQ0J5dGVzO1xuICB9XG59XG5mdW5jdGlvbiBjYXB0aW9uc09yU3VidGl0bGVzRnJvbUNoYXJhY3RlcmlzdGljcyh0cmFjaykge1xuICBpZiAodHJhY2suY2hhcmFjdGVyaXN0aWNzKSB7XG4gICAgaWYgKC90cmFuc2NyaWJlcy1zcG9rZW4tZGlhbG9nL2dpLnRlc3QodHJhY2suY2hhcmFjdGVyaXN0aWNzKSAmJiAvZGVzY3JpYmVzLW11c2ljLWFuZC1zb3VuZC9naS50ZXN0KHRyYWNrLmNoYXJhY3RlcmlzdGljcykpIHtcbiAgICAgIHJldHVybiAnY2FwdGlvbnMnO1xuICAgIH1cbiAgfVxuICByZXR1cm4gJ3N1YnRpdGxlcyc7XG59XG5mdW5jdGlvbiBjYW5SZXVzZVZ0dFRleHRUcmFjayhpblVzZVRyYWNrLCBtYW5pZmVzdFRyYWNrKSB7XG4gIHJldHVybiAhIWluVXNlVHJhY2sgJiYgaW5Vc2VUcmFjay5raW5kID09PSBjYXB0aW9uc09yU3VidGl0bGVzRnJvbUNoYXJhY3RlcmlzdGljcyhtYW5pZmVzdFRyYWNrKSAmJiBzdWJ0aXRsZVRyYWNrTWF0Y2hlc1RleHRUcmFjayhtYW5pZmVzdFRyYWNrLCBpblVzZVRyYWNrKTtcbn1cbmZ1bmN0aW9uIGludGVyc2VjdGlvbih4MSwgeDIsIHkxLCB5Mikge1xuICByZXR1cm4gTWF0aC5taW4oeDIsIHkyKSAtIE1hdGgubWF4KHgxLCB5MSk7XG59XG5mdW5jdGlvbiBuZXdWVFRDQ3MoKSB7XG4gIHJldHVybiB7XG4gICAgY2NPZmZzZXQ6IDAsXG4gICAgcHJlc2VudGF0aW9uT2Zmc2V0OiAwLFxuICAgIDA6IHtcbiAgICAgIHN0YXJ0OiAwLFxuICAgICAgcHJldkNDOiAtMSxcbiAgICAgIG5ldzogdHJ1ZVxuICAgIH1cbiAgfTtcbn1cblxuY2xhc3MgQ2FwTGV2ZWxDb250cm9sbGVyIHtcbiAgY29uc3RydWN0b3IoaGxzKSB7XG4gICAgdGhpcy5obHMgPSB2b2lkIDA7XG4gICAgdGhpcy5hdXRvTGV2ZWxDYXBwaW5nID0gdm9pZCAwO1xuICAgIHRoaXMuZmlyc3RMZXZlbCA9IHZvaWQgMDtcbiAgICB0aGlzLm1lZGlhID0gdm9pZCAwO1xuICAgIHRoaXMucmVzdHJpY3RlZExldmVscyA9IHZvaWQgMDtcbiAgICB0aGlzLnRpbWVyID0gdm9pZCAwO1xuICAgIHRoaXMuY2xpZW50UmVjdCA9IHZvaWQgMDtcbiAgICB0aGlzLnN0cmVhbUNvbnRyb2xsZXIgPSB2b2lkIDA7XG4gICAgdGhpcy5obHMgPSBobHM7XG4gICAgdGhpcy5hdXRvTGV2ZWxDYXBwaW5nID0gTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZO1xuICAgIHRoaXMuZmlyc3RMZXZlbCA9IC0xO1xuICAgIHRoaXMubWVkaWEgPSBudWxsO1xuICAgIHRoaXMucmVzdHJpY3RlZExldmVscyA9IFtdO1xuICAgIHRoaXMudGltZXIgPSB1bmRlZmluZWQ7XG4gICAgdGhpcy5jbGllbnRSZWN0ID0gbnVsbDtcbiAgICB0aGlzLnJlZ2lzdGVyTGlzdGVuZXJzKCk7XG4gIH1cbiAgc2V0U3RyZWFtQ29udHJvbGxlcihzdHJlYW1Db250cm9sbGVyKSB7XG4gICAgdGhpcy5zdHJlYW1Db250cm9sbGVyID0gc3RyZWFtQ29udHJvbGxlcjtcbiAgfVxuICBkZXN0cm95KCkge1xuICAgIGlmICh0aGlzLmhscykge1xuICAgICAgdGhpcy51bnJlZ2lzdGVyTGlzdGVuZXIoKTtcbiAgICB9XG4gICAgaWYgKHRoaXMudGltZXIpIHtcbiAgICAgIHRoaXMuc3RvcENhcHBpbmcoKTtcbiAgICB9XG4gICAgdGhpcy5tZWRpYSA9IG51bGw7XG4gICAgdGhpcy5jbGllbnRSZWN0ID0gbnVsbDtcbiAgICAvLyBAdHMtaWdub3JlXG4gICAgdGhpcy5obHMgPSB0aGlzLnN0cmVhbUNvbnRyb2xsZXIgPSBudWxsO1xuICB9XG4gIHJlZ2lzdGVyTGlzdGVuZXJzKCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGhsc1xuICAgIH0gPSB0aGlzO1xuICAgIGhscy5vbihFdmVudHMuRlBTX0RST1BfTEVWRUxfQ0FQUElORywgdGhpcy5vbkZwc0Ryb3BMZXZlbENhcHBpbmcsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuTUVESUFfQVRUQUNISU5HLCB0aGlzLm9uTWVkaWFBdHRhY2hpbmcsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuTUFOSUZFU1RfUEFSU0VELCB0aGlzLm9uTWFuaWZlc3RQYXJzZWQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuTEVWRUxTX1VQREFURUQsIHRoaXMub25MZXZlbHNVcGRhdGVkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkJVRkZFUl9DT0RFQ1MsIHRoaXMub25CdWZmZXJDb2RlY3MsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuTUVESUFfREVUQUNISU5HLCB0aGlzLm9uTWVkaWFEZXRhY2hpbmcsIHRoaXMpO1xuICB9XG4gIHVucmVnaXN0ZXJMaXN0ZW5lcigpIHtcbiAgICBjb25zdCB7XG4gICAgICBobHNcbiAgICB9ID0gdGhpcztcbiAgICBobHMub2ZmKEV2ZW50cy5GUFNfRFJPUF9MRVZFTF9DQVBQSU5HLCB0aGlzLm9uRnBzRHJvcExldmVsQ2FwcGluZywgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuTUVESUFfQVRUQUNISU5HLCB0aGlzLm9uTWVkaWFBdHRhY2hpbmcsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLk1BTklGRVNUX1BBUlNFRCwgdGhpcy5vbk1hbmlmZXN0UGFyc2VkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5MRVZFTFNfVVBEQVRFRCwgdGhpcy5vbkxldmVsc1VwZGF0ZWQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkJVRkZFUl9DT0RFQ1MsIHRoaXMub25CdWZmZXJDb2RlY3MsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLk1FRElBX0RFVEFDSElORywgdGhpcy5vbk1lZGlhRGV0YWNoaW5nLCB0aGlzKTtcbiAgfVxuICBvbkZwc0Ryb3BMZXZlbENhcHBpbmcoZXZlbnQsIGRhdGEpIHtcbiAgICAvLyBEb24ndCBhZGQgYSByZXN0cmljdGVkIGxldmVsIG1vcmUgdGhhbiBvbmNlXG4gICAgY29uc3QgbGV2ZWwgPSB0aGlzLmhscy5sZXZlbHNbZGF0YS5kcm9wcGVkTGV2ZWxdO1xuICAgIGlmICh0aGlzLmlzTGV2ZWxBbGxvd2VkKGxldmVsKSkge1xuICAgICAgdGhpcy5yZXN0cmljdGVkTGV2ZWxzLnB1c2goe1xuICAgICAgICBiaXRyYXRlOiBsZXZlbC5iaXRyYXRlLFxuICAgICAgICBoZWlnaHQ6IGxldmVsLmhlaWdodCxcbiAgICAgICAgd2lkdGg6IGxldmVsLndpZHRoXG4gICAgICB9KTtcbiAgICB9XG4gIH1cbiAgb25NZWRpYUF0dGFjaGluZyhldmVudCwgZGF0YSkge1xuICAgIHRoaXMubWVkaWEgPSBkYXRhLm1lZGlhIGluc3RhbmNlb2YgSFRNTFZpZGVvRWxlbWVudCA/IGRhdGEubWVkaWEgOiBudWxsO1xuICAgIHRoaXMuY2xpZW50UmVjdCA9IG51bGw7XG4gICAgaWYgKHRoaXMudGltZXIgJiYgdGhpcy5obHMubGV2ZWxzLmxlbmd0aCkge1xuICAgICAgdGhpcy5kZXRlY3RQbGF5ZXJTaXplKCk7XG4gICAgfVxuICB9XG4gIG9uTWFuaWZlc3RQYXJzZWQoZXZlbnQsIGRhdGEpIHtcbiAgICBjb25zdCBobHMgPSB0aGlzLmhscztcbiAgICB0aGlzLnJlc3RyaWN0ZWRMZXZlbHMgPSBbXTtcbiAgICB0aGlzLmZpcnN0TGV2ZWwgPSBkYXRhLmZpcnN0TGV2ZWw7XG4gICAgaWYgKGhscy5jb25maWcuY2FwTGV2ZWxUb1BsYXllclNpemUgJiYgZGF0YS52aWRlbykge1xuICAgICAgLy8gU3RhcnQgY2FwcGluZyBpbW1lZGlhdGVseSBpZiB0aGUgbWFuaWZlc3QgaGFzIHNpZ25hbGVkIHZpZGVvIGNvZGVjc1xuICAgICAgdGhpcy5zdGFydENhcHBpbmcoKTtcbiAgICB9XG4gIH1cbiAgb25MZXZlbHNVcGRhdGVkKGV2ZW50LCBkYXRhKSB7XG4gICAgaWYgKHRoaXMudGltZXIgJiYgaXNGaW5pdGVOdW1iZXIodGhpcy5hdXRvTGV2ZWxDYXBwaW5nKSkge1xuICAgICAgdGhpcy5kZXRlY3RQbGF5ZXJTaXplKCk7XG4gICAgfVxuICB9XG5cbiAgLy8gT25seSBhY3RpdmF0ZSBjYXBwaW5nIHdoZW4gcGxheWluZyBhIHZpZGVvIHN0cmVhbTsgb3RoZXJ3aXNlLCBtdWx0aS1iaXRyYXRlIGF1ZGlvLW9ubHkgc3RyZWFtcyB3aWxsIGJlIHJlc3RyaWN0ZWRcbiAgLy8gdG8gdGhlIGZpcnN0IGxldmVsXG4gIG9uQnVmZmVyQ29kZWNzKGV2ZW50LCBkYXRhKSB7XG4gICAgY29uc3QgaGxzID0gdGhpcy5obHM7XG4gICAgaWYgKGhscy5jb25maWcuY2FwTGV2ZWxUb1BsYXllclNpemUgJiYgZGF0YS52aWRlbykge1xuICAgICAgLy8gSWYgdGhlIG1hbmlmZXN0IGRpZCBub3Qgc2lnbmFsIGEgdmlkZW8gY29kZWMgY2FwcGluZyBoYXMgYmVlbiBkZWZlcnJlZCB1bnRpbCB3ZSdyZSBjZXJ0YWluIHZpZGVvIGlzIHByZXNlbnRcbiAgICAgIHRoaXMuc3RhcnRDYXBwaW5nKCk7XG4gICAgfVxuICB9XG4gIG9uTWVkaWFEZXRhY2hpbmcoKSB7XG4gICAgdGhpcy5zdG9wQ2FwcGluZygpO1xuICB9XG4gIGRldGVjdFBsYXllclNpemUoKSB7XG4gICAgaWYgKHRoaXMubWVkaWEpIHtcbiAgICAgIGlmICh0aGlzLm1lZGlhSGVpZ2h0IDw9IDAgfHwgdGhpcy5tZWRpYVdpZHRoIDw9IDApIHtcbiAgICAgICAgdGhpcy5jbGllbnRSZWN0ID0gbnVsbDtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY29uc3QgbGV2ZWxzID0gdGhpcy5obHMubGV2ZWxzO1xuICAgICAgaWYgKGxldmVscy5sZW5ndGgpIHtcbiAgICAgICAgY29uc3QgaGxzID0gdGhpcy5obHM7XG4gICAgICAgIGNvbnN0IG1heExldmVsID0gdGhpcy5nZXRNYXhMZXZlbChsZXZlbHMubGVuZ3RoIC0gMSk7XG4gICAgICAgIGlmIChtYXhMZXZlbCAhPT0gdGhpcy5hdXRvTGV2ZWxDYXBwaW5nKSB7XG4gICAgICAgICAgbG9nZ2VyLmxvZyhgU2V0dGluZyBhdXRvTGV2ZWxDYXBwaW5nIHRvICR7bWF4TGV2ZWx9OiAke2xldmVsc1ttYXhMZXZlbF0uaGVpZ2h0fXBAJHtsZXZlbHNbbWF4TGV2ZWxdLmJpdHJhdGV9IGZvciBtZWRpYSAke3RoaXMubWVkaWFXaWR0aH14JHt0aGlzLm1lZGlhSGVpZ2h0fWApO1xuICAgICAgICB9XG4gICAgICAgIGhscy5hdXRvTGV2ZWxDYXBwaW5nID0gbWF4TGV2ZWw7XG4gICAgICAgIGlmIChobHMuYXV0b0xldmVsQ2FwcGluZyA+IHRoaXMuYXV0b0xldmVsQ2FwcGluZyAmJiB0aGlzLnN0cmVhbUNvbnRyb2xsZXIpIHtcbiAgICAgICAgICAvLyBpZiBhdXRvIGxldmVsIGNhcHBpbmcgaGFzIGEgaGlnaGVyIHZhbHVlIGZvciB0aGUgcHJldmlvdXMgb25lLCBmbHVzaCB0aGUgYnVmZmVyIHVzaW5nIG5leHRMZXZlbFN3aXRjaFxuICAgICAgICAgIC8vIHVzdWFsbHkgaGFwcGVuIHdoZW4gdGhlIHVzZXIgZ28gdG8gdGhlIGZ1bGxzY3JlZW4gbW9kZS5cbiAgICAgICAgICB0aGlzLnN0cmVhbUNvbnRyb2xsZXIubmV4dExldmVsU3dpdGNoKCk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5hdXRvTGV2ZWxDYXBwaW5nID0gaGxzLmF1dG9MZXZlbENhcHBpbmc7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLypcbiAgICogcmV0dXJucyBsZXZlbCBzaG91bGQgYmUgdGhlIG9uZSB3aXRoIHRoZSBkaW1lbnNpb25zIGVxdWFsIG9yIGdyZWF0ZXIgdGhhbiB0aGUgbWVkaWEgKHBsYXllcikgZGltZW5zaW9ucyAoc28gdGhlIHZpZGVvIHdpbGwgYmUgZG93bnNjYWxlZClcbiAgICovXG4gIGdldE1heExldmVsKGNhcExldmVsSW5kZXgpIHtcbiAgICBjb25zdCBsZXZlbHMgPSB0aGlzLmhscy5sZXZlbHM7XG4gICAgaWYgKCFsZXZlbHMubGVuZ3RoKSB7XG4gICAgICByZXR1cm4gLTE7XG4gICAgfVxuICAgIGNvbnN0IHZhbGlkTGV2ZWxzID0gbGV2ZWxzLmZpbHRlcigobGV2ZWwsIGluZGV4KSA9PiB0aGlzLmlzTGV2ZWxBbGxvd2VkKGxldmVsKSAmJiBpbmRleCA8PSBjYXBMZXZlbEluZGV4KTtcbiAgICB0aGlzLmNsaWVudFJlY3QgPSBudWxsO1xuICAgIHJldHVybiBDYXBMZXZlbENvbnRyb2xsZXIuZ2V0TWF4TGV2ZWxCeU1lZGlhU2l6ZSh2YWxpZExldmVscywgdGhpcy5tZWRpYVdpZHRoLCB0aGlzLm1lZGlhSGVpZ2h0KTtcbiAgfVxuICBzdGFydENhcHBpbmcoKSB7XG4gICAgaWYgKHRoaXMudGltZXIpIHtcbiAgICAgIC8vIERvbid0IHJlc2V0IGNhcHBpbmcgaWYgc3RhcnRlZCB0d2ljZTsgdGhpcyBjYW4gaGFwcGVuIGlmIHRoZSBtYW5pZmVzdCBzaWduYWxzIGEgdmlkZW8gY29kZWNcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5hdXRvTGV2ZWxDYXBwaW5nID0gTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZO1xuICAgIHNlbGYuY2xlYXJJbnRlcnZhbCh0aGlzLnRpbWVyKTtcbiAgICB0aGlzLnRpbWVyID0gc2VsZi5zZXRJbnRlcnZhbCh0aGlzLmRldGVjdFBsYXllclNpemUuYmluZCh0aGlzKSwgMTAwMCk7XG4gICAgdGhpcy5kZXRlY3RQbGF5ZXJTaXplKCk7XG4gIH1cbiAgc3RvcENhcHBpbmcoKSB7XG4gICAgdGhpcy5yZXN0cmljdGVkTGV2ZWxzID0gW107XG4gICAgdGhpcy5maXJzdExldmVsID0gLTE7XG4gICAgdGhpcy5hdXRvTGV2ZWxDYXBwaW5nID0gTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZO1xuICAgIGlmICh0aGlzLnRpbWVyKSB7XG4gICAgICBzZWxmLmNsZWFySW50ZXJ2YWwodGhpcy50aW1lcik7XG4gICAgICB0aGlzLnRpbWVyID0gdW5kZWZpbmVkO1xuICAgIH1cbiAgfVxuICBnZXREaW1lbnNpb25zKCkge1xuICAgIGlmICh0aGlzLmNsaWVudFJlY3QpIHtcbiAgICAgIHJldHVybiB0aGlzLmNsaWVudFJlY3Q7XG4gICAgfVxuICAgIGNvbnN0IG1lZGlhID0gdGhpcy5tZWRpYTtcbiAgICBjb25zdCBib3VuZHNSZWN0ID0ge1xuICAgICAgd2lkdGg6IDAsXG4gICAgICBoZWlnaHQ6IDBcbiAgICB9O1xuICAgIGlmIChtZWRpYSkge1xuICAgICAgY29uc3QgY2xpZW50UmVjdCA9IG1lZGlhLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgYm91bmRzUmVjdC53aWR0aCA9IGNsaWVudFJlY3Qud2lkdGg7XG4gICAgICBib3VuZHNSZWN0LmhlaWdodCA9IGNsaWVudFJlY3QuaGVpZ2h0O1xuICAgICAgaWYgKCFib3VuZHNSZWN0LndpZHRoICYmICFib3VuZHNSZWN0LmhlaWdodCkge1xuICAgICAgICAvLyBXaGVuIHRoZSBtZWRpYSBlbGVtZW50IGhhcyBubyB3aWR0aCBvciBoZWlnaHQgKGVxdWl2YWxlbnQgdG8gbm90IGJlaW5nIGluIHRoZSBET00pLFxuICAgICAgICAvLyB0aGVuIHVzZSBpdHMgd2lkdGggYW5kIGhlaWdodCBhdHRyaWJ1dGVzIChtZWRpYS53aWR0aCwgbWVkaWEuaGVpZ2h0KVxuICAgICAgICBib3VuZHNSZWN0LndpZHRoID0gY2xpZW50UmVjdC5yaWdodCAtIGNsaWVudFJlY3QubGVmdCB8fCBtZWRpYS53aWR0aCB8fCAwO1xuICAgICAgICBib3VuZHNSZWN0LmhlaWdodCA9IGNsaWVudFJlY3QuYm90dG9tIC0gY2xpZW50UmVjdC50b3AgfHwgbWVkaWEuaGVpZ2h0IHx8IDA7XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMuY2xpZW50UmVjdCA9IGJvdW5kc1JlY3Q7XG4gICAgcmV0dXJuIGJvdW5kc1JlY3Q7XG4gIH1cbiAgZ2V0IG1lZGlhV2lkdGgoKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0RGltZW5zaW9ucygpLndpZHRoICogdGhpcy5jb250ZW50U2NhbGVGYWN0b3I7XG4gIH1cbiAgZ2V0IG1lZGlhSGVpZ2h0KCkge1xuICAgIHJldHVybiB0aGlzLmdldERpbWVuc2lvbnMoKS5oZWlnaHQgKiB0aGlzLmNvbnRlbnRTY2FsZUZhY3RvcjtcbiAgfVxuICBnZXQgY29udGVudFNjYWxlRmFjdG9yKCkge1xuICAgIGxldCBwaXhlbFJhdGlvID0gMTtcbiAgICBpZiAoIXRoaXMuaGxzLmNvbmZpZy5pZ25vcmVEZXZpY2VQaXhlbFJhdGlvKSB7XG4gICAgICB0cnkge1xuICAgICAgICBwaXhlbFJhdGlvID0gc2VsZi5kZXZpY2VQaXhlbFJhdGlvO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAvKiBuby1vcCAqL1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gcGl4ZWxSYXRpbztcbiAgfVxuICBpc0xldmVsQWxsb3dlZChsZXZlbCkge1xuICAgIGNvbnN0IHJlc3RyaWN0ZWRMZXZlbHMgPSB0aGlzLnJlc3RyaWN0ZWRMZXZlbHM7XG4gICAgcmV0dXJuICFyZXN0cmljdGVkTGV2ZWxzLnNvbWUocmVzdHJpY3RlZExldmVsID0+IHtcbiAgICAgIHJldHVybiBsZXZlbC5iaXRyYXRlID09PSByZXN0cmljdGVkTGV2ZWwuYml0cmF0ZSAmJiBsZXZlbC53aWR0aCA9PT0gcmVzdHJpY3RlZExldmVsLndpZHRoICYmIGxldmVsLmhlaWdodCA9PT0gcmVzdHJpY3RlZExldmVsLmhlaWdodDtcbiAgICB9KTtcbiAgfVxuICBzdGF0aWMgZ2V0TWF4TGV2ZWxCeU1lZGlhU2l6ZShsZXZlbHMsIHdpZHRoLCBoZWlnaHQpIHtcbiAgICBpZiAoIShsZXZlbHMgIT0gbnVsbCAmJiBsZXZlbHMubGVuZ3RoKSkge1xuICAgICAgcmV0dXJuIC0xO1xuICAgIH1cblxuICAgIC8vIExldmVscyBjYW4gaGF2ZSB0aGUgc2FtZSBkaW1lbnNpb25zIGJ1dCBkaWZmZXJpbmcgYmFuZHdpZHRocyAtIHNpbmNlIGxldmVscyBhcmUgb3JkZXJlZCwgd2UgY2FuIGxvb2sgdG8gdGhlIG5leHRcbiAgICAvLyB0byBkZXRlcm1pbmUgd2hldGhlciB3ZSd2ZSBjaG9zZW4gdGhlIGdyZWF0ZXN0IGJhbmR3aWR0aCBmb3IgdGhlIG1lZGlhJ3MgZGltZW5zaW9uc1xuICAgIGNvbnN0IGF0R3JlYXRlc3RCYW5kd2lkdGggPSAoY3VyTGV2ZWwsIG5leHRMZXZlbCkgPT4ge1xuICAgICAgaWYgKCFuZXh0TGV2ZWwpIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgICByZXR1cm4gY3VyTGV2ZWwud2lkdGggIT09IG5leHRMZXZlbC53aWR0aCB8fCBjdXJMZXZlbC5oZWlnaHQgIT09IG5leHRMZXZlbC5oZWlnaHQ7XG4gICAgfTtcblxuICAgIC8vIElmIHdlIHJ1biB0aHJvdWdoIHRoZSBsb29wIHdpdGhvdXQgYnJlYWtpbmcsIHRoZSBtZWRpYSdzIGRpbWVuc2lvbnMgYXJlIGdyZWF0ZXIgdGhhbiBldmVyeSBsZXZlbCwgc28gZGVmYXVsdCB0b1xuICAgIC8vIHRoZSBtYXggbGV2ZWxcbiAgICBsZXQgbWF4TGV2ZWxJbmRleCA9IGxldmVscy5sZW5ndGggLSAxO1xuICAgIC8vIFByZXZlbnQgY2hhbmdlcyBpbiBhc3BlY3QtcmF0aW8gZnJvbSBjYXVzaW5nIGNhcHBpbmcgdG8gdG9nZ2xlIGJhY2sgYW5kIGZvcnRoXG4gICAgY29uc3Qgc3F1YXJlU2l6ZSA9IE1hdGgubWF4KHdpZHRoLCBoZWlnaHQpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbGV2ZWxzLmxlbmd0aDsgaSArPSAxKSB7XG4gICAgICBjb25zdCBsZXZlbCA9IGxldmVsc1tpXTtcbiAgICAgIGlmICgobGV2ZWwud2lkdGggPj0gc3F1YXJlU2l6ZSB8fCBsZXZlbC5oZWlnaHQgPj0gc3F1YXJlU2l6ZSkgJiYgYXRHcmVhdGVzdEJhbmR3aWR0aChsZXZlbCwgbGV2ZWxzW2kgKyAxXSkpIHtcbiAgICAgICAgbWF4TGV2ZWxJbmRleCA9IGk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbWF4TGV2ZWxJbmRleDtcbiAgfVxufVxuXG5jbGFzcyBGUFNDb250cm9sbGVyIHtcbiAgY29uc3RydWN0b3IoaGxzKSB7XG4gICAgdGhpcy5obHMgPSB2b2lkIDA7XG4gICAgdGhpcy5pc1ZpZGVvUGxheWJhY2tRdWFsaXR5QXZhaWxhYmxlID0gZmFsc2U7XG4gICAgdGhpcy50aW1lciA9IHZvaWQgMDtcbiAgICB0aGlzLm1lZGlhID0gbnVsbDtcbiAgICB0aGlzLmxhc3RUaW1lID0gdm9pZCAwO1xuICAgIHRoaXMubGFzdERyb3BwZWRGcmFtZXMgPSAwO1xuICAgIHRoaXMubGFzdERlY29kZWRGcmFtZXMgPSAwO1xuICAgIC8vIHN0cmVhbSBjb250cm9sbGVyIG11c3QgYmUgcHJvdmlkZWQgYXMgYSBkZXBlbmRlbmN5IVxuICAgIHRoaXMuc3RyZWFtQ29udHJvbGxlciA9IHZvaWQgMDtcbiAgICB0aGlzLmhscyA9IGhscztcbiAgICB0aGlzLnJlZ2lzdGVyTGlzdGVuZXJzKCk7XG4gIH1cbiAgc2V0U3RyZWFtQ29udHJvbGxlcihzdHJlYW1Db250cm9sbGVyKSB7XG4gICAgdGhpcy5zdHJlYW1Db250cm9sbGVyID0gc3RyZWFtQ29udHJvbGxlcjtcbiAgfVxuICByZWdpc3Rlckxpc3RlbmVycygpIHtcbiAgICB0aGlzLmhscy5vbihFdmVudHMuTUVESUFfQVRUQUNISU5HLCB0aGlzLm9uTWVkaWFBdHRhY2hpbmcsIHRoaXMpO1xuICB9XG4gIHVucmVnaXN0ZXJMaXN0ZW5lcnMoKSB7XG4gICAgdGhpcy5obHMub2ZmKEV2ZW50cy5NRURJQV9BVFRBQ0hJTkcsIHRoaXMub25NZWRpYUF0dGFjaGluZywgdGhpcyk7XG4gIH1cbiAgZGVzdHJveSgpIHtcbiAgICBpZiAodGhpcy50aW1lcikge1xuICAgICAgY2xlYXJJbnRlcnZhbCh0aGlzLnRpbWVyKTtcbiAgICB9XG4gICAgdGhpcy51bnJlZ2lzdGVyTGlzdGVuZXJzKCk7XG4gICAgdGhpcy5pc1ZpZGVvUGxheWJhY2tRdWFsaXR5QXZhaWxhYmxlID0gZmFsc2U7XG4gICAgdGhpcy5tZWRpYSA9IG51bGw7XG4gIH1cbiAgb25NZWRpYUF0dGFjaGluZyhldmVudCwgZGF0YSkge1xuICAgIGNvbnN0IGNvbmZpZyA9IHRoaXMuaGxzLmNvbmZpZztcbiAgICBpZiAoY29uZmlnLmNhcExldmVsT25GUFNEcm9wKSB7XG4gICAgICBjb25zdCBtZWRpYSA9IGRhdGEubWVkaWEgaW5zdGFuY2VvZiBzZWxmLkhUTUxWaWRlb0VsZW1lbnQgPyBkYXRhLm1lZGlhIDogbnVsbDtcbiAgICAgIHRoaXMubWVkaWEgPSBtZWRpYTtcbiAgICAgIGlmIChtZWRpYSAmJiB0eXBlb2YgbWVkaWEuZ2V0VmlkZW9QbGF5YmFja1F1YWxpdHkgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgdGhpcy5pc1ZpZGVvUGxheWJhY2tRdWFsaXR5QXZhaWxhYmxlID0gdHJ1ZTtcbiAgICAgIH1cbiAgICAgIHNlbGYuY2xlYXJJbnRlcnZhbCh0aGlzLnRpbWVyKTtcbiAgICAgIHRoaXMudGltZXIgPSBzZWxmLnNldEludGVydmFsKHRoaXMuY2hlY2tGUFNJbnRlcnZhbC5iaW5kKHRoaXMpLCBjb25maWcuZnBzRHJvcHBlZE1vbml0b3JpbmdQZXJpb2QpO1xuICAgIH1cbiAgfVxuICBjaGVja0ZQUyh2aWRlbywgZGVjb2RlZEZyYW1lcywgZHJvcHBlZEZyYW1lcykge1xuICAgIGNvbnN0IGN1cnJlbnRUaW1lID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gICAgaWYgKGRlY29kZWRGcmFtZXMpIHtcbiAgICAgIGlmICh0aGlzLmxhc3RUaW1lKSB7XG4gICAgICAgIGNvbnN0IGN1cnJlbnRQZXJpb2QgPSBjdXJyZW50VGltZSAtIHRoaXMubGFzdFRpbWU7XG4gICAgICAgIGNvbnN0IGN1cnJlbnREcm9wcGVkID0gZHJvcHBlZEZyYW1lcyAtIHRoaXMubGFzdERyb3BwZWRGcmFtZXM7XG4gICAgICAgIGNvbnN0IGN1cnJlbnREZWNvZGVkID0gZGVjb2RlZEZyYW1lcyAtIHRoaXMubGFzdERlY29kZWRGcmFtZXM7XG4gICAgICAgIGNvbnN0IGRyb3BwZWRGUFMgPSAxMDAwICogY3VycmVudERyb3BwZWQgLyBjdXJyZW50UGVyaW9kO1xuICAgICAgICBjb25zdCBobHMgPSB0aGlzLmhscztcbiAgICAgICAgaGxzLnRyaWdnZXIoRXZlbnRzLkZQU19EUk9QLCB7XG4gICAgICAgICAgY3VycmVudERyb3BwZWQ6IGN1cnJlbnREcm9wcGVkLFxuICAgICAgICAgIGN1cnJlbnREZWNvZGVkOiBjdXJyZW50RGVjb2RlZCxcbiAgICAgICAgICB0b3RhbERyb3BwZWRGcmFtZXM6IGRyb3BwZWRGcmFtZXNcbiAgICAgICAgfSk7XG4gICAgICAgIGlmIChkcm9wcGVkRlBTID4gMCkge1xuICAgICAgICAgIC8vIGxvZ2dlci5sb2coJ2NoZWNrRlBTIDogZHJvcHBlZEZQUy9kZWNvZGVkRlBTOicgKyBkcm9wcGVkRlBTLygxMDAwICogY3VycmVudERlY29kZWQgLyBjdXJyZW50UGVyaW9kKSk7XG4gICAgICAgICAgaWYgKGN1cnJlbnREcm9wcGVkID4gaGxzLmNvbmZpZy5mcHNEcm9wcGVkTW9uaXRvcmluZ1RocmVzaG9sZCAqIGN1cnJlbnREZWNvZGVkKSB7XG4gICAgICAgICAgICBsZXQgY3VycmVudExldmVsID0gaGxzLmN1cnJlbnRMZXZlbDtcbiAgICAgICAgICAgIGxvZ2dlci53YXJuKCdkcm9wIEZQUyByYXRpbyBncmVhdGVyIHRoYW4gbWF4IGFsbG93ZWQgdmFsdWUgZm9yIGN1cnJlbnRMZXZlbDogJyArIGN1cnJlbnRMZXZlbCk7XG4gICAgICAgICAgICBpZiAoY3VycmVudExldmVsID4gMCAmJiAoaGxzLmF1dG9MZXZlbENhcHBpbmcgPT09IC0xIHx8IGhscy5hdXRvTGV2ZWxDYXBwaW5nID49IGN1cnJlbnRMZXZlbCkpIHtcbiAgICAgICAgICAgICAgY3VycmVudExldmVsID0gY3VycmVudExldmVsIC0gMTtcbiAgICAgICAgICAgICAgaGxzLnRyaWdnZXIoRXZlbnRzLkZQU19EUk9QX0xFVkVMX0NBUFBJTkcsIHtcbiAgICAgICAgICAgICAgICBsZXZlbDogY3VycmVudExldmVsLFxuICAgICAgICAgICAgICAgIGRyb3BwZWRMZXZlbDogaGxzLmN1cnJlbnRMZXZlbFxuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgaGxzLmF1dG9MZXZlbENhcHBpbmcgPSBjdXJyZW50TGV2ZWw7XG4gICAgICAgICAgICAgIHRoaXMuc3RyZWFtQ29udHJvbGxlci5uZXh0TGV2ZWxTd2l0Y2goKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHRoaXMubGFzdFRpbWUgPSBjdXJyZW50VGltZTtcbiAgICAgIHRoaXMubGFzdERyb3BwZWRGcmFtZXMgPSBkcm9wcGVkRnJhbWVzO1xuICAgICAgdGhpcy5sYXN0RGVjb2RlZEZyYW1lcyA9IGRlY29kZWRGcmFtZXM7XG4gICAgfVxuICB9XG4gIGNoZWNrRlBTSW50ZXJ2YWwoKSB7XG4gICAgY29uc3QgdmlkZW8gPSB0aGlzLm1lZGlhO1xuICAgIGlmICh2aWRlbykge1xuICAgICAgaWYgKHRoaXMuaXNWaWRlb1BsYXliYWNrUXVhbGl0eUF2YWlsYWJsZSkge1xuICAgICAgICBjb25zdCB2aWRlb1BsYXliYWNrUXVhbGl0eSA9IHZpZGVvLmdldFZpZGVvUGxheWJhY2tRdWFsaXR5KCk7XG4gICAgICAgIHRoaXMuY2hlY2tGUFModmlkZW8sIHZpZGVvUGxheWJhY2tRdWFsaXR5LnRvdGFsVmlkZW9GcmFtZXMsIHZpZGVvUGxheWJhY2tRdWFsaXR5LmRyb3BwZWRWaWRlb0ZyYW1lcyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBIVE1MVmlkZW9FbGVtZW50IGRvZXNuJ3QgaW5jbHVkZSB0aGUgd2Via2l0IHR5cGVzXG4gICAgICAgIHRoaXMuY2hlY2tGUFModmlkZW8sIHZpZGVvLndlYmtpdERlY29kZWRGcmFtZUNvdW50LCB2aWRlby53ZWJraXREcm9wcGVkRnJhbWVDb3VudCk7XG4gICAgICB9XG4gICAgfVxuICB9XG59XG5cbmNvbnN0IExPR0dFUl9QUkVGSVggPSAnW2VtZV0nO1xuLyoqXG4gKiBDb250cm9sbGVyIHRvIGRlYWwgd2l0aCBlbmNyeXB0ZWQgbWVkaWEgZXh0ZW5zaW9ucyAoRU1FKVxuICogQHNlZSBodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9BUEkvRW5jcnlwdGVkX01lZGlhX0V4dGVuc2lvbnNfQVBJXG4gKlxuICogQGNsYXNzXG4gKiBAY29uc3RydWN0b3JcbiAqL1xuY2xhc3MgRU1FQ29udHJvbGxlciB7XG4gIGNvbnN0cnVjdG9yKGhscykge1xuICAgIHRoaXMuaGxzID0gdm9pZCAwO1xuICAgIHRoaXMuY29uZmlnID0gdm9pZCAwO1xuICAgIHRoaXMubWVkaWEgPSBudWxsO1xuICAgIHRoaXMua2V5Rm9ybWF0UHJvbWlzZSA9IG51bGw7XG4gICAgdGhpcy5rZXlTeXN0ZW1BY2Nlc3NQcm9taXNlcyA9IHt9O1xuICAgIHRoaXMuX3JlcXVlc3RMaWNlbnNlRmFpbHVyZUNvdW50ID0gMDtcbiAgICB0aGlzLm1lZGlhS2V5U2Vzc2lvbnMgPSBbXTtcbiAgICB0aGlzLmtleUlkVG9LZXlTZXNzaW9uUHJvbWlzZSA9IHt9O1xuICAgIHRoaXMuc2V0TWVkaWFLZXlzUXVldWUgPSBFTUVDb250cm9sbGVyLkNETUNsZWFudXBQcm9taXNlID8gW0VNRUNvbnRyb2xsZXIuQ0RNQ2xlYW51cFByb21pc2VdIDogW107XG4gICAgdGhpcy5vbk1lZGlhRW5jcnlwdGVkID0gdGhpcy5fb25NZWRpYUVuY3J5cHRlZC5iaW5kKHRoaXMpO1xuICAgIHRoaXMub25XYWl0aW5nRm9yS2V5ID0gdGhpcy5fb25XYWl0aW5nRm9yS2V5LmJpbmQodGhpcyk7XG4gICAgdGhpcy5kZWJ1ZyA9IGxvZ2dlci5kZWJ1Zy5iaW5kKGxvZ2dlciwgTE9HR0VSX1BSRUZJWCk7XG4gICAgdGhpcy5sb2cgPSBsb2dnZXIubG9nLmJpbmQobG9nZ2VyLCBMT0dHRVJfUFJFRklYKTtcbiAgICB0aGlzLndhcm4gPSBsb2dnZXIud2Fybi5iaW5kKGxvZ2dlciwgTE9HR0VSX1BSRUZJWCk7XG4gICAgdGhpcy5lcnJvciA9IGxvZ2dlci5lcnJvci5iaW5kKGxvZ2dlciwgTE9HR0VSX1BSRUZJWCk7XG4gICAgdGhpcy5obHMgPSBobHM7XG4gICAgdGhpcy5jb25maWcgPSBobHMuY29uZmlnO1xuICAgIHRoaXMucmVnaXN0ZXJMaXN0ZW5lcnMoKTtcbiAgfVxuICBkZXN0cm95KCkge1xuICAgIHRoaXMudW5yZWdpc3Rlckxpc3RlbmVycygpO1xuICAgIHRoaXMub25NZWRpYURldGFjaGVkKCk7XG4gICAgLy8gUmVtb3ZlIGFueSByZWZlcmVuY2VzIHRoYXQgY291bGQgYmUgaGVsZCBpbiBjb25maWcgb3B0aW9ucyBvciBjYWxsYmFja3NcbiAgICBjb25zdCBjb25maWcgPSB0aGlzLmNvbmZpZztcbiAgICBjb25maWcucmVxdWVzdE1lZGlhS2V5U3lzdGVtQWNjZXNzRnVuYyA9IG51bGw7XG4gICAgY29uZmlnLmxpY2Vuc2VYaHJTZXR1cCA9IGNvbmZpZy5saWNlbnNlUmVzcG9uc2VDYWxsYmFjayA9IHVuZGVmaW5lZDtcbiAgICBjb25maWcuZHJtU3lzdGVtcyA9IGNvbmZpZy5kcm1TeXN0ZW1PcHRpb25zID0ge307XG4gICAgLy8gQHRzLWlnbm9yZVxuICAgIHRoaXMuaGxzID0gdGhpcy5vbk1lZGlhRW5jcnlwdGVkID0gdGhpcy5vbldhaXRpbmdGb3JLZXkgPSB0aGlzLmtleUlkVG9LZXlTZXNzaW9uUHJvbWlzZSA9IG51bGw7XG4gICAgLy8gQHRzLWlnbm9yZVxuICAgIHRoaXMuY29uZmlnID0gbnVsbDtcbiAgfVxuICByZWdpc3Rlckxpc3RlbmVycygpIHtcbiAgICB0aGlzLmhscy5vbihFdmVudHMuTUVESUFfQVRUQUNIRUQsIHRoaXMub25NZWRpYUF0dGFjaGVkLCB0aGlzKTtcbiAgICB0aGlzLmhscy5vbihFdmVudHMuTUVESUFfREVUQUNIRUQsIHRoaXMub25NZWRpYURldGFjaGVkLCB0aGlzKTtcbiAgICB0aGlzLmhscy5vbihFdmVudHMuTUFOSUZFU1RfTE9BRElORywgdGhpcy5vbk1hbmlmZXN0TG9hZGluZywgdGhpcyk7XG4gICAgdGhpcy5obHMub24oRXZlbnRzLk1BTklGRVNUX0xPQURFRCwgdGhpcy5vbk1hbmlmZXN0TG9hZGVkLCB0aGlzKTtcbiAgfVxuICB1bnJlZ2lzdGVyTGlzdGVuZXJzKCkge1xuICAgIHRoaXMuaGxzLm9mZihFdmVudHMuTUVESUFfQVRUQUNIRUQsIHRoaXMub25NZWRpYUF0dGFjaGVkLCB0aGlzKTtcbiAgICB0aGlzLmhscy5vZmYoRXZlbnRzLk1FRElBX0RFVEFDSEVELCB0aGlzLm9uTWVkaWFEZXRhY2hlZCwgdGhpcyk7XG4gICAgdGhpcy5obHMub2ZmKEV2ZW50cy5NQU5JRkVTVF9MT0FESU5HLCB0aGlzLm9uTWFuaWZlc3RMb2FkaW5nLCB0aGlzKTtcbiAgICB0aGlzLmhscy5vZmYoRXZlbnRzLk1BTklGRVNUX0xPQURFRCwgdGhpcy5vbk1hbmlmZXN0TG9hZGVkLCB0aGlzKTtcbiAgfVxuICBnZXRMaWNlbnNlU2VydmVyVXJsKGtleVN5c3RlbSkge1xuICAgIGNvbnN0IHtcbiAgICAgIGRybVN5c3RlbXMsXG4gICAgICB3aWRldmluZUxpY2Vuc2VVcmxcbiAgICB9ID0gdGhpcy5jb25maWc7XG4gICAgY29uc3Qga2V5U3lzdGVtQ29uZmlndXJhdGlvbiA9IGRybVN5c3RlbXNba2V5U3lzdGVtXTtcbiAgICBpZiAoa2V5U3lzdGVtQ29uZmlndXJhdGlvbikge1xuICAgICAgcmV0dXJuIGtleVN5c3RlbUNvbmZpZ3VyYXRpb24ubGljZW5zZVVybDtcbiAgICB9XG5cbiAgICAvLyBGb3IgYmFja3dhcmQgY29tcGF0aWJpbGl0eVxuICAgIGlmIChrZXlTeXN0ZW0gPT09IEtleVN5c3RlbXMuV0lERVZJTkUgJiYgd2lkZXZpbmVMaWNlbnNlVXJsKSB7XG4gICAgICByZXR1cm4gd2lkZXZpbmVMaWNlbnNlVXJsO1xuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoYG5vIGxpY2Vuc2Ugc2VydmVyIFVSTCBjb25maWd1cmVkIGZvciBrZXktc3lzdGVtIFwiJHtrZXlTeXN0ZW19XCJgKTtcbiAgfVxuICBnZXRTZXJ2ZXJDZXJ0aWZpY2F0ZVVybChrZXlTeXN0ZW0pIHtcbiAgICBjb25zdCB7XG4gICAgICBkcm1TeXN0ZW1zXG4gICAgfSA9IHRoaXMuY29uZmlnO1xuICAgIGNvbnN0IGtleVN5c3RlbUNvbmZpZ3VyYXRpb24gPSBkcm1TeXN0ZW1zW2tleVN5c3RlbV07XG4gICAgaWYgKGtleVN5c3RlbUNvbmZpZ3VyYXRpb24pIHtcbiAgICAgIHJldHVybiBrZXlTeXN0ZW1Db25maWd1cmF0aW9uLnNlcnZlckNlcnRpZmljYXRlVXJsO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmxvZyhgTm8gU2VydmVyIENlcnRpZmljYXRlIGluIGNvbmZpZy5kcm1TeXN0ZW1zW1wiJHtrZXlTeXN0ZW19XCJdYCk7XG4gICAgfVxuICB9XG4gIGF0dGVtcHRLZXlTeXN0ZW1BY2Nlc3Moa2V5U3lzdGVtc1RvQXR0ZW1wdCkge1xuICAgIGNvbnN0IGxldmVscyA9IHRoaXMuaGxzLmxldmVscztcbiAgICBjb25zdCB1bmlxdWVDb2RlYyA9ICh2YWx1ZSwgaSwgYSkgPT4gISF2YWx1ZSAmJiBhLmluZGV4T2YodmFsdWUpID09PSBpO1xuICAgIGNvbnN0IGF1ZGlvQ29kZWNzID0gbGV2ZWxzLm1hcChsZXZlbCA9PiBsZXZlbC5hdWRpb0NvZGVjKS5maWx0ZXIodW5pcXVlQ29kZWMpO1xuICAgIGNvbnN0IHZpZGVvQ29kZWNzID0gbGV2ZWxzLm1hcChsZXZlbCA9PiBsZXZlbC52aWRlb0NvZGVjKS5maWx0ZXIodW5pcXVlQ29kZWMpO1xuICAgIGlmIChhdWRpb0NvZGVjcy5sZW5ndGggKyB2aWRlb0NvZGVjcy5sZW5ndGggPT09IDApIHtcbiAgICAgIHZpZGVvQ29kZWNzLnB1c2goJ2F2YzEuNDJlMDFlJyk7XG4gICAgfVxuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBjb25zdCBhdHRlbXB0ID0ga2V5U3lzdGVtcyA9PiB7XG4gICAgICAgIGNvbnN0IGtleVN5c3RlbSA9IGtleVN5c3RlbXMuc2hpZnQoKTtcbiAgICAgICAgdGhpcy5nZXRNZWRpYUtleXNQcm9taXNlKGtleVN5c3RlbSwgYXVkaW9Db2RlY3MsIHZpZGVvQ29kZWNzKS50aGVuKG1lZGlhS2V5cyA9PiByZXNvbHZlKHtcbiAgICAgICAgICBrZXlTeXN0ZW0sXG4gICAgICAgICAgbWVkaWFLZXlzXG4gICAgICAgIH0pKS5jYXRjaChlcnJvciA9PiB7XG4gICAgICAgICAgaWYgKGtleVN5c3RlbXMubGVuZ3RoKSB7XG4gICAgICAgICAgICBhdHRlbXB0KGtleVN5c3RlbXMpO1xuICAgICAgICAgIH0gZWxzZSBpZiAoZXJyb3IgaW5zdGFuY2VvZiBFTUVLZXlFcnJvcikge1xuICAgICAgICAgICAgcmVqZWN0KGVycm9yKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmVqZWN0KG5ldyBFTUVLZXlFcnJvcih7XG4gICAgICAgICAgICAgIHR5cGU6IEVycm9yVHlwZXMuS0VZX1NZU1RFTV9FUlJPUixcbiAgICAgICAgICAgICAgZGV0YWlsczogRXJyb3JEZXRhaWxzLktFWV9TWVNURU1fTk9fQUNDRVNTLFxuICAgICAgICAgICAgICBlcnJvcixcbiAgICAgICAgICAgICAgZmF0YWw6IHRydWVcbiAgICAgICAgICAgIH0sIGVycm9yLm1lc3NhZ2UpKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgfTtcbiAgICAgIGF0dGVtcHQoa2V5U3lzdGVtc1RvQXR0ZW1wdCk7XG4gICAgfSk7XG4gIH1cbiAgcmVxdWVzdE1lZGlhS2V5U3lzdGVtQWNjZXNzKGtleVN5c3RlbSwgc3VwcG9ydGVkQ29uZmlndXJhdGlvbnMpIHtcbiAgICBjb25zdCB7XG4gICAgICByZXF1ZXN0TWVkaWFLZXlTeXN0ZW1BY2Nlc3NGdW5jXG4gICAgfSA9IHRoaXMuY29uZmlnO1xuICAgIGlmICghKHR5cGVvZiByZXF1ZXN0TWVkaWFLZXlTeXN0ZW1BY2Nlc3NGdW5jID09PSAnZnVuY3Rpb24nKSkge1xuICAgICAgbGV0IGVyck1lc3NhZ2UgPSBgQ29uZmlndXJlZCByZXF1ZXN0TWVkaWFLZXlTeXN0ZW1BY2Nlc3MgaXMgbm90IGEgZnVuY3Rpb24gJHtyZXF1ZXN0TWVkaWFLZXlTeXN0ZW1BY2Nlc3NGdW5jfWA7XG4gICAgICBpZiAocmVxdWVzdE1lZGlhS2V5U3lzdGVtQWNjZXNzID09PSBudWxsICYmIHNlbGYubG9jYXRpb24ucHJvdG9jb2wgPT09ICdodHRwOicpIHtcbiAgICAgICAgZXJyTWVzc2FnZSA9IGBuYXZpZ2F0b3IucmVxdWVzdE1lZGlhS2V5U3lzdGVtQWNjZXNzIGlzIG5vdCBhdmFpbGFibGUgb3ZlciBpbnNlY3VyZSBwcm90b2NvbCAke2xvY2F0aW9uLnByb3RvY29sfWA7XG4gICAgICB9XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKGVyck1lc3NhZ2UpKTtcbiAgICB9XG4gICAgcmV0dXJuIHJlcXVlc3RNZWRpYUtleVN5c3RlbUFjY2Vzc0Z1bmMoa2V5U3lzdGVtLCBzdXBwb3J0ZWRDb25maWd1cmF0aW9ucyk7XG4gIH1cbiAgZ2V0TWVkaWFLZXlzUHJvbWlzZShrZXlTeXN0ZW0sIGF1ZGlvQ29kZWNzLCB2aWRlb0NvZGVjcykge1xuICAgIC8vIFRoaXMgY2FuIHRocm93LCBidXQgaXMgY2F1Z2h0IGluIGV2ZW50IGhhbmRsZXIgY2FsbHBhdGhcbiAgICBjb25zdCBtZWRpYUtleVN5c3RlbUNvbmZpZ3MgPSBnZXRTdXBwb3J0ZWRNZWRpYUtleVN5c3RlbUNvbmZpZ3VyYXRpb25zKGtleVN5c3RlbSwgYXVkaW9Db2RlY3MsIHZpZGVvQ29kZWNzLCB0aGlzLmNvbmZpZy5kcm1TeXN0ZW1PcHRpb25zKTtcbiAgICBjb25zdCBrZXlTeXN0ZW1BY2Nlc3NQcm9taXNlcyA9IHRoaXMua2V5U3lzdGVtQWNjZXNzUHJvbWlzZXNba2V5U3lzdGVtXTtcbiAgICBsZXQga2V5U3lzdGVtQWNjZXNzID0ga2V5U3lzdGVtQWNjZXNzUHJvbWlzZXMgPT0gbnVsbCA/IHZvaWQgMCA6IGtleVN5c3RlbUFjY2Vzc1Byb21pc2VzLmtleVN5c3RlbUFjY2VzcztcbiAgICBpZiAoIWtleVN5c3RlbUFjY2Vzcykge1xuICAgICAgdGhpcy5sb2coYFJlcXVlc3RpbmcgZW5jcnlwdGVkIG1lZGlhIFwiJHtrZXlTeXN0ZW19XCIga2V5LXN5c3RlbSBhY2Nlc3Mgd2l0aCBjb25maWc6ICR7SlNPTi5zdHJpbmdpZnkobWVkaWFLZXlTeXN0ZW1Db25maWdzKX1gKTtcbiAgICAgIGtleVN5c3RlbUFjY2VzcyA9IHRoaXMucmVxdWVzdE1lZGlhS2V5U3lzdGVtQWNjZXNzKGtleVN5c3RlbSwgbWVkaWFLZXlTeXN0ZW1Db25maWdzKTtcbiAgICAgIGNvbnN0IF9rZXlTeXN0ZW1BY2Nlc3NQcm9taXNlcyA9IHRoaXMua2V5U3lzdGVtQWNjZXNzUHJvbWlzZXNba2V5U3lzdGVtXSA9IHtcbiAgICAgICAga2V5U3lzdGVtQWNjZXNzXG4gICAgICB9O1xuICAgICAga2V5U3lzdGVtQWNjZXNzLmNhdGNoKGVycm9yID0+IHtcbiAgICAgICAgdGhpcy5sb2coYEZhaWxlZCB0byBvYnRhaW4gYWNjZXNzIHRvIGtleS1zeXN0ZW0gXCIke2tleVN5c3RlbX1cIjogJHtlcnJvcn1gKTtcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIGtleVN5c3RlbUFjY2Vzcy50aGVuKG1lZGlhS2V5U3lzdGVtQWNjZXNzID0+IHtcbiAgICAgICAgdGhpcy5sb2coYEFjY2VzcyBmb3Iga2V5LXN5c3RlbSBcIiR7bWVkaWFLZXlTeXN0ZW1BY2Nlc3Mua2V5U3lzdGVtfVwiIG9idGFpbmVkYCk7XG4gICAgICAgIGNvbnN0IGNlcnRpZmljYXRlUmVxdWVzdCA9IHRoaXMuZmV0Y2hTZXJ2ZXJDZXJ0aWZpY2F0ZShrZXlTeXN0ZW0pO1xuICAgICAgICB0aGlzLmxvZyhgQ3JlYXRlIG1lZGlhLWtleXMgZm9yIFwiJHtrZXlTeXN0ZW19XCJgKTtcbiAgICAgICAgX2tleVN5c3RlbUFjY2Vzc1Byb21pc2VzLm1lZGlhS2V5cyA9IG1lZGlhS2V5U3lzdGVtQWNjZXNzLmNyZWF0ZU1lZGlhS2V5cygpLnRoZW4obWVkaWFLZXlzID0+IHtcbiAgICAgICAgICB0aGlzLmxvZyhgTWVkaWEta2V5cyBjcmVhdGVkIGZvciBcIiR7a2V5U3lzdGVtfVwiYCk7XG4gICAgICAgICAgcmV0dXJuIGNlcnRpZmljYXRlUmVxdWVzdC50aGVuKGNlcnRpZmljYXRlID0+IHtcbiAgICAgICAgICAgIGlmIChjZXJ0aWZpY2F0ZSkge1xuICAgICAgICAgICAgICByZXR1cm4gdGhpcy5zZXRNZWRpYUtleXNTZXJ2ZXJDZXJ0aWZpY2F0ZShtZWRpYUtleXMsIGtleVN5c3RlbSwgY2VydGlmaWNhdGUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIG1lZGlhS2V5cztcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICAgIF9rZXlTeXN0ZW1BY2Nlc3NQcm9taXNlcy5tZWRpYUtleXMuY2F0Y2goZXJyb3IgPT4ge1xuICAgICAgICAgIHRoaXMuZXJyb3IoYEZhaWxlZCB0byBjcmVhdGUgbWVkaWEta2V5cyBmb3IgXCIke2tleVN5c3RlbX1cIn06ICR7ZXJyb3J9YCk7XG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gX2tleVN5c3RlbUFjY2Vzc1Byb21pc2VzLm1lZGlhS2V5cztcbiAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4ga2V5U3lzdGVtQWNjZXNzLnRoZW4oKCkgPT4ga2V5U3lzdGVtQWNjZXNzUHJvbWlzZXMubWVkaWFLZXlzKTtcbiAgfVxuICBjcmVhdGVNZWRpYUtleVNlc3Npb25Db250ZXh0KHtcbiAgICBkZWNyeXB0ZGF0YSxcbiAgICBrZXlTeXN0ZW0sXG4gICAgbWVkaWFLZXlzXG4gIH0pIHtcbiAgICB0aGlzLmxvZyhgQ3JlYXRpbmcga2V5LXN5c3RlbSBzZXNzaW9uIFwiJHtrZXlTeXN0ZW19XCIga2V5SWQ6ICR7SGV4LmhleER1bXAoZGVjcnlwdGRhdGEua2V5SWQgfHwgW10pfWApO1xuICAgIGNvbnN0IG1lZGlhS2V5c1Nlc3Npb24gPSBtZWRpYUtleXMuY3JlYXRlU2Vzc2lvbigpO1xuICAgIGNvbnN0IG1lZGlhS2V5U2Vzc2lvbkNvbnRleHQgPSB7XG4gICAgICBkZWNyeXB0ZGF0YSxcbiAgICAgIGtleVN5c3RlbSxcbiAgICAgIG1lZGlhS2V5cyxcbiAgICAgIG1lZGlhS2V5c1Nlc3Npb24sXG4gICAgICBrZXlTdGF0dXM6ICdzdGF0dXMtcGVuZGluZydcbiAgICB9O1xuICAgIHRoaXMubWVkaWFLZXlTZXNzaW9ucy5wdXNoKG1lZGlhS2V5U2Vzc2lvbkNvbnRleHQpO1xuICAgIHJldHVybiBtZWRpYUtleVNlc3Npb25Db250ZXh0O1xuICB9XG4gIHJlbmV3S2V5U2Vzc2lvbihtZWRpYUtleVNlc3Npb25Db250ZXh0KSB7XG4gICAgY29uc3QgZGVjcnlwdGRhdGEgPSBtZWRpYUtleVNlc3Npb25Db250ZXh0LmRlY3J5cHRkYXRhO1xuICAgIGlmIChkZWNyeXB0ZGF0YS5wc3NoKSB7XG4gICAgICBjb25zdCBrZXlTZXNzaW9uQ29udGV4dCA9IHRoaXMuY3JlYXRlTWVkaWFLZXlTZXNzaW9uQ29udGV4dChtZWRpYUtleVNlc3Npb25Db250ZXh0KTtcbiAgICAgIGNvbnN0IGtleUlkID0gdGhpcy5nZXRLZXlJZFN0cmluZyhkZWNyeXB0ZGF0YSk7XG4gICAgICBjb25zdCBzY2hlbWUgPSAnY2VuYyc7XG4gICAgICB0aGlzLmtleUlkVG9LZXlTZXNzaW9uUHJvbWlzZVtrZXlJZF0gPSB0aGlzLmdlbmVyYXRlUmVxdWVzdFdpdGhQcmVmZXJyZWRLZXlTZXNzaW9uKGtleVNlc3Npb25Db250ZXh0LCBzY2hlbWUsIGRlY3J5cHRkYXRhLnBzc2gsICdleHBpcmVkJyk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMud2FybihgQ291bGQgbm90IHJlbmV3IGV4cGlyZWQgc2Vzc2lvbi4gTWlzc2luZyBwc3NoIGluaXREYXRhLmApO1xuICAgIH1cbiAgICB0aGlzLnJlbW92ZVNlc3Npb24obWVkaWFLZXlTZXNzaW9uQ29udGV4dCk7XG4gIH1cbiAgZ2V0S2V5SWRTdHJpbmcoZGVjcnlwdGRhdGEpIHtcbiAgICBpZiAoIWRlY3J5cHRkYXRhKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0NvdWxkIG5vdCByZWFkIGtleUlkIG9mIHVuZGVmaW5lZCBkZWNyeXB0ZGF0YScpO1xuICAgIH1cbiAgICBpZiAoZGVjcnlwdGRhdGEua2V5SWQgPT09IG51bGwpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcigna2V5SWQgaXMgbnVsbCcpO1xuICAgIH1cbiAgICByZXR1cm4gSGV4LmhleER1bXAoZGVjcnlwdGRhdGEua2V5SWQpO1xuICB9XG4gIHVwZGF0ZUtleVNlc3Npb24obWVkaWFLZXlTZXNzaW9uQ29udGV4dCwgZGF0YSkge1xuICAgIHZhciBfbWVkaWFLZXlTZXNzaW9uQ29udGU7XG4gICAgY29uc3Qga2V5U2Vzc2lvbiA9IG1lZGlhS2V5U2Vzc2lvbkNvbnRleHQubWVkaWFLZXlzU2Vzc2lvbjtcbiAgICB0aGlzLmxvZyhgVXBkYXRpbmcga2V5LXNlc3Npb24gXCIke2tleVNlc3Npb24uc2Vzc2lvbklkfVwiIGZvciBrZXlJRCAke0hleC5oZXhEdW1wKCgoX21lZGlhS2V5U2Vzc2lvbkNvbnRlID0gbWVkaWFLZXlTZXNzaW9uQ29udGV4dC5kZWNyeXB0ZGF0YSkgPT0gbnVsbCA/IHZvaWQgMCA6IF9tZWRpYUtleVNlc3Npb25Db250ZS5rZXlJZCkgfHwgW10pfVxuICAgICAgfSAoZGF0YSBsZW5ndGg6ICR7ZGF0YSA/IGRhdGEuYnl0ZUxlbmd0aCA6IGRhdGF9KWApO1xuICAgIHJldHVybiBrZXlTZXNzaW9uLnVwZGF0ZShkYXRhKTtcbiAgfVxuICBzZWxlY3RLZXlTeXN0ZW1Gb3JtYXQoZnJhZykge1xuICAgIGNvbnN0IGtleUZvcm1hdHMgPSBPYmplY3Qua2V5cyhmcmFnLmxldmVsa2V5cyB8fCB7fSk7XG4gICAgaWYgKCF0aGlzLmtleUZvcm1hdFByb21pc2UpIHtcbiAgICAgIHRoaXMubG9nKGBTZWxlY3Rpbmcga2V5LXN5c3RlbSBmcm9tIGZyYWdtZW50IChzbjogJHtmcmFnLnNufSAke2ZyYWcudHlwZX06ICR7ZnJhZy5sZXZlbH0pIGtleSBmb3JtYXRzICR7a2V5Rm9ybWF0cy5qb2luKCcsICcpfWApO1xuICAgICAgdGhpcy5rZXlGb3JtYXRQcm9taXNlID0gdGhpcy5nZXRLZXlGb3JtYXRQcm9taXNlKGtleUZvcm1hdHMpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5rZXlGb3JtYXRQcm9taXNlO1xuICB9XG4gIGdldEtleUZvcm1hdFByb21pc2Uoa2V5Rm9ybWF0cykge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBjb25zdCBrZXlTeXN0ZW1zSW5Db25maWcgPSBnZXRLZXlTeXN0ZW1zRm9yQ29uZmlnKHRoaXMuY29uZmlnKTtcbiAgICAgIGNvbnN0IGtleVN5c3RlbXNUb0F0dGVtcHQgPSBrZXlGb3JtYXRzLm1hcChrZXlTeXN0ZW1Gb3JtYXRUb0tleVN5c3RlbURvbWFpbikuZmlsdGVyKHZhbHVlID0+ICEhdmFsdWUgJiYga2V5U3lzdGVtc0luQ29uZmlnLmluZGV4T2YodmFsdWUpICE9PSAtMSk7XG4gICAgICByZXR1cm4gdGhpcy5nZXRLZXlTeXN0ZW1TZWxlY3Rpb25Qcm9taXNlKGtleVN5c3RlbXNUb0F0dGVtcHQpLnRoZW4oKHtcbiAgICAgICAga2V5U3lzdGVtXG4gICAgICB9KSA9PiB7XG4gICAgICAgIGNvbnN0IGtleVN5c3RlbUZvcm1hdCA9IGtleVN5c3RlbURvbWFpblRvS2V5U3lzdGVtRm9ybWF0KGtleVN5c3RlbSk7XG4gICAgICAgIGlmIChrZXlTeXN0ZW1Gb3JtYXQpIHtcbiAgICAgICAgICByZXNvbHZlKGtleVN5c3RlbUZvcm1hdCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgVW5hYmxlIHRvIGZpbmQgZm9ybWF0IGZvciBrZXktc3lzdGVtIFwiJHtrZXlTeXN0ZW19XCJgKSk7XG4gICAgICAgIH1cbiAgICAgIH0pLmNhdGNoKHJlamVjdCk7XG4gICAgfSk7XG4gIH1cbiAgbG9hZEtleShkYXRhKSB7XG4gICAgY29uc3QgZGVjcnlwdGRhdGEgPSBkYXRhLmtleUluZm8uZGVjcnlwdGRhdGE7XG4gICAgY29uc3Qga2V5SWQgPSB0aGlzLmdldEtleUlkU3RyaW5nKGRlY3J5cHRkYXRhKTtcbiAgICBjb25zdCBrZXlEZXRhaWxzID0gYChrZXlJZDogJHtrZXlJZH0gZm9ybWF0OiBcIiR7ZGVjcnlwdGRhdGEua2V5Rm9ybWF0fVwiIG1ldGhvZDogJHtkZWNyeXB0ZGF0YS5tZXRob2R9IHVyaTogJHtkZWNyeXB0ZGF0YS51cml9KWA7XG4gICAgdGhpcy5sb2coYFN0YXJ0aW5nIHNlc3Npb24gZm9yIGtleSAke2tleURldGFpbHN9YCk7XG4gICAgbGV0IGtleVNlc3Npb25Db250ZXh0UHJvbWlzZSA9IHRoaXMua2V5SWRUb0tleVNlc3Npb25Qcm9taXNlW2tleUlkXTtcbiAgICBpZiAoIWtleVNlc3Npb25Db250ZXh0UHJvbWlzZSkge1xuICAgICAga2V5U2Vzc2lvbkNvbnRleHRQcm9taXNlID0gdGhpcy5rZXlJZFRvS2V5U2Vzc2lvblByb21pc2Vba2V5SWRdID0gdGhpcy5nZXRLZXlTeXN0ZW1Gb3JLZXlQcm9taXNlKGRlY3J5cHRkYXRhKS50aGVuKCh7XG4gICAgICAgIGtleVN5c3RlbSxcbiAgICAgICAgbWVkaWFLZXlzXG4gICAgICB9KSA9PiB7XG4gICAgICAgIHRoaXMudGhyb3dJZkRlc3Ryb3llZCgpO1xuICAgICAgICB0aGlzLmxvZyhgSGFuZGxlIGVuY3J5cHRlZCBtZWRpYSBzbjogJHtkYXRhLmZyYWcuc259ICR7ZGF0YS5mcmFnLnR5cGV9OiAke2RhdGEuZnJhZy5sZXZlbH0gdXNpbmcga2V5ICR7a2V5RGV0YWlsc31gKTtcbiAgICAgICAgcmV0dXJuIHRoaXMuYXR0ZW1wdFNldE1lZGlhS2V5cyhrZXlTeXN0ZW0sIG1lZGlhS2V5cykudGhlbigoKSA9PiB7XG4gICAgICAgICAgdGhpcy50aHJvd0lmRGVzdHJveWVkKCk7XG4gICAgICAgICAgY29uc3Qga2V5U2Vzc2lvbkNvbnRleHQgPSB0aGlzLmNyZWF0ZU1lZGlhS2V5U2Vzc2lvbkNvbnRleHQoe1xuICAgICAgICAgICAga2V5U3lzdGVtLFxuICAgICAgICAgICAgbWVkaWFLZXlzLFxuICAgICAgICAgICAgZGVjcnlwdGRhdGFcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBjb25zdCBzY2hlbWUgPSAnY2VuYyc7XG4gICAgICAgICAgcmV0dXJuIHRoaXMuZ2VuZXJhdGVSZXF1ZXN0V2l0aFByZWZlcnJlZEtleVNlc3Npb24oa2V5U2Vzc2lvbkNvbnRleHQsIHNjaGVtZSwgZGVjcnlwdGRhdGEucHNzaCwgJ3BsYXlsaXN0LWtleScpO1xuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgICAga2V5U2Vzc2lvbkNvbnRleHRQcm9taXNlLmNhdGNoKGVycm9yID0+IHRoaXMuaGFuZGxlRXJyb3IoZXJyb3IpKTtcbiAgICB9XG4gICAgcmV0dXJuIGtleVNlc3Npb25Db250ZXh0UHJvbWlzZTtcbiAgfVxuICB0aHJvd0lmRGVzdHJveWVkKG1lc3NhZ2UgPSAnSW52YWxpZCBzdGF0ZScpIHtcbiAgICBpZiAoIXRoaXMuaGxzKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2ludmFsaWQgc3RhdGUnKTtcbiAgICB9XG4gIH1cbiAgaGFuZGxlRXJyb3IoZXJyb3IpIHtcbiAgICBpZiAoIXRoaXMuaGxzKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuZXJyb3IoZXJyb3IubWVzc2FnZSk7XG4gICAgaWYgKGVycm9yIGluc3RhbmNlb2YgRU1FS2V5RXJyb3IpIHtcbiAgICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLkVSUk9SLCBlcnJvci5kYXRhKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuRVJST1IsIHtcbiAgICAgICAgdHlwZTogRXJyb3JUeXBlcy5LRVlfU1lTVEVNX0VSUk9SLFxuICAgICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuS0VZX1NZU1RFTV9OT19LRVlTLFxuICAgICAgICBlcnJvcixcbiAgICAgICAgZmF0YWw6IHRydWVcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuICBnZXRLZXlTeXN0ZW1Gb3JLZXlQcm9taXNlKGRlY3J5cHRkYXRhKSB7XG4gICAgY29uc3Qga2V5SWQgPSB0aGlzLmdldEtleUlkU3RyaW5nKGRlY3J5cHRkYXRhKTtcbiAgICBjb25zdCBtZWRpYUtleVNlc3Npb25Db250ZXh0ID0gdGhpcy5rZXlJZFRvS2V5U2Vzc2lvblByb21pc2Vba2V5SWRdO1xuICAgIGlmICghbWVkaWFLZXlTZXNzaW9uQ29udGV4dCkge1xuICAgICAgY29uc3Qga2V5U3lzdGVtID0ga2V5U3lzdGVtRm9ybWF0VG9LZXlTeXN0ZW1Eb21haW4oZGVjcnlwdGRhdGEua2V5Rm9ybWF0KTtcbiAgICAgIGNvbnN0IGtleVN5c3RlbXNUb0F0dGVtcHQgPSBrZXlTeXN0ZW0gPyBba2V5U3lzdGVtXSA6IGdldEtleVN5c3RlbXNGb3JDb25maWcodGhpcy5jb25maWcpO1xuICAgICAgcmV0dXJuIHRoaXMuYXR0ZW1wdEtleVN5c3RlbUFjY2VzcyhrZXlTeXN0ZW1zVG9BdHRlbXB0KTtcbiAgICB9XG4gICAgcmV0dXJuIG1lZGlhS2V5U2Vzc2lvbkNvbnRleHQ7XG4gIH1cbiAgZ2V0S2V5U3lzdGVtU2VsZWN0aW9uUHJvbWlzZShrZXlTeXN0ZW1zVG9BdHRlbXB0KSB7XG4gICAgaWYgKCFrZXlTeXN0ZW1zVG9BdHRlbXB0Lmxlbmd0aCkge1xuICAgICAga2V5U3lzdGVtc1RvQXR0ZW1wdCA9IGdldEtleVN5c3RlbXNGb3JDb25maWcodGhpcy5jb25maWcpO1xuICAgIH1cbiAgICBpZiAoa2V5U3lzdGVtc1RvQXR0ZW1wdC5sZW5ndGggPT09IDApIHtcbiAgICAgIHRocm93IG5ldyBFTUVLZXlFcnJvcih7XG4gICAgICAgIHR5cGU6IEVycm9yVHlwZXMuS0VZX1NZU1RFTV9FUlJPUixcbiAgICAgICAgZGV0YWlsczogRXJyb3JEZXRhaWxzLktFWV9TWVNURU1fTk9fQ09ORklHVVJFRF9MSUNFTlNFLFxuICAgICAgICBmYXRhbDogdHJ1ZVxuICAgICAgfSwgYE1pc3Npbmcga2V5LXN5c3RlbSBsaWNlbnNlIGNvbmZpZ3VyYXRpb24gb3B0aW9ucyAke0pTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgZHJtU3lzdGVtczogdGhpcy5jb25maWcuZHJtU3lzdGVtc1xuICAgICAgfSl9YCk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmF0dGVtcHRLZXlTeXN0ZW1BY2Nlc3Moa2V5U3lzdGVtc1RvQXR0ZW1wdCk7XG4gIH1cbiAgX29uTWVkaWFFbmNyeXB0ZWQoZXZlbnQpIHtcbiAgICBjb25zdCB7XG4gICAgICBpbml0RGF0YVR5cGUsXG4gICAgICBpbml0RGF0YVxuICAgIH0gPSBldmVudDtcbiAgICBjb25zdCBsb2dNZXNzYWdlID0gYFwiJHtldmVudC50eXBlfVwiIGV2ZW50OiBpbml0IGRhdGEgdHlwZTogXCIke2luaXREYXRhVHlwZX1cImA7XG4gICAgdGhpcy5kZWJ1Zyhsb2dNZXNzYWdlKTtcblxuICAgIC8vIElnbm9yZSBldmVudCB3aGVuIGluaXREYXRhIGlzIG51bGxcbiAgICBpZiAoaW5pdERhdGEgPT09IG51bGwpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgbGV0IGtleUlkO1xuICAgIGxldCBrZXlTeXN0ZW1Eb21haW47XG4gICAgaWYgKGluaXREYXRhVHlwZSA9PT0gJ3NpbmYnICYmIHRoaXMuY29uZmlnLmRybVN5c3RlbXNbS2V5U3lzdGVtcy5GQUlSUExBWV0pIHtcbiAgICAgIC8vIE1hdGNoIHNpbmYga2V5SWQgdG8gcGxheWxpc3Qgc2tkOi8va2V5SWQ9XG4gICAgICBjb25zdCBqc29uID0gYmluMnN0cihuZXcgVWludDhBcnJheShpbml0RGF0YSkpO1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3Qgc2luZiA9IGJhc2U2NERlY29kZShKU09OLnBhcnNlKGpzb24pLnNpbmYpO1xuICAgICAgICBjb25zdCB0ZW5jID0gcGFyc2VTaW5mKG5ldyBVaW50OEFycmF5KHNpbmYpKTtcbiAgICAgICAgaWYgKCF0ZW5jKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGAnc2NobScgYm94IG1pc3Npbmcgb3Igbm90IGNiY3MvY2VuYyB3aXRoIHNjaGkgPiB0ZW5jYCk7XG4gICAgICAgIH1cbiAgICAgICAga2V5SWQgPSB0ZW5jLnN1YmFycmF5KDgsIDI0KTtcbiAgICAgICAga2V5U3lzdGVtRG9tYWluID0gS2V5U3lzdGVtcy5GQUlSUExBWTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIHRoaXMud2FybihgJHtsb2dNZXNzYWdlfSBGYWlsZWQgdG8gcGFyc2Ugc2luZjogJHtlcnJvcn1gKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyBTdXBwb3J0IFdpZGV2aW5lIGNsZWFyLWxlYWQga2V5LXNlc3Npb24gY3JlYXRpb24gKG90aGVyd2lzZSBkZXBlbmQgb24gcGxheWxpc3Qga2V5cylcbiAgICAgIGNvbnN0IHBzc2hSZXN1bHRzID0gcGFyc2VNdWx0aVBzc2goaW5pdERhdGEpO1xuICAgICAgY29uc3QgcHNzaEluZm8gPSBwc3NoUmVzdWx0cy5maWx0ZXIocHNzaCA9PiBwc3NoLnN5c3RlbUlkID09PSBLZXlTeXN0ZW1JZHMuV0lERVZJTkUpWzBdO1xuICAgICAgaWYgKCFwc3NoSW5mbykge1xuICAgICAgICBpZiAocHNzaFJlc3VsdHMubGVuZ3RoID09PSAwIHx8IHBzc2hSZXN1bHRzLnNvbWUocHNzaCA9PiAhcHNzaC5zeXN0ZW1JZCkpIHtcbiAgICAgICAgICB0aGlzLndhcm4oYCR7bG9nTWVzc2FnZX0gY29udGFpbnMgaW5jb21wbGV0ZSBvciBpbnZhbGlkIHBzc2ggZGF0YWApO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRoaXMubG9nKGBpZ25vcmluZyAke2xvZ01lc3NhZ2V9IGZvciAke3Bzc2hSZXN1bHRzLm1hcChwc3NoID0+IGtleVN5c3RlbUlkVG9LZXlTeXN0ZW1Eb21haW4ocHNzaC5zeXN0ZW1JZCkpLmpvaW4oJywnKX0gcHNzaCBkYXRhIGluIGZhdm9yIG9mIHBsYXlsaXN0IGtleXNgKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBrZXlTeXN0ZW1Eb21haW4gPSBrZXlTeXN0ZW1JZFRvS2V5U3lzdGVtRG9tYWluKHBzc2hJbmZvLnN5c3RlbUlkKTtcbiAgICAgIGlmIChwc3NoSW5mby52ZXJzaW9uID09PSAwICYmIHBzc2hJbmZvLmRhdGEpIHtcbiAgICAgICAgY29uc3Qgb2Zmc2V0ID0gcHNzaEluZm8uZGF0YS5sZW5ndGggLSAyMjtcbiAgICAgICAga2V5SWQgPSBwc3NoSW5mby5kYXRhLnN1YmFycmF5KG9mZnNldCwgb2Zmc2V0ICsgMTYpO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAoIWtleVN5c3RlbURvbWFpbiB8fCAha2V5SWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3Qga2V5SWRIZXggPSBIZXguaGV4RHVtcChrZXlJZCk7XG4gICAgY29uc3Qge1xuICAgICAga2V5SWRUb0tleVNlc3Npb25Qcm9taXNlLFxuICAgICAgbWVkaWFLZXlTZXNzaW9uc1xuICAgIH0gPSB0aGlzO1xuICAgIGxldCBrZXlTZXNzaW9uQ29udGV4dFByb21pc2UgPSBrZXlJZFRvS2V5U2Vzc2lvblByb21pc2Vba2V5SWRIZXhdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbWVkaWFLZXlTZXNzaW9ucy5sZW5ndGg7IGkrKykge1xuICAgICAgLy8gTWF0Y2ggcGxheWxpc3Qga2V5XG4gICAgICBjb25zdCBrZXlDb250ZXh0ID0gbWVkaWFLZXlTZXNzaW9uc1tpXTtcbiAgICAgIGNvbnN0IGRlY3J5cHRkYXRhID0ga2V5Q29udGV4dC5kZWNyeXB0ZGF0YTtcbiAgICAgIGlmICghZGVjcnlwdGRhdGEua2V5SWQpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG4gICAgICBjb25zdCBvbGRLZXlJZEhleCA9IEhleC5oZXhEdW1wKGRlY3J5cHRkYXRhLmtleUlkKTtcbiAgICAgIGlmIChrZXlJZEhleCA9PT0gb2xkS2V5SWRIZXggfHwgZGVjcnlwdGRhdGEudXJpLnJlcGxhY2UoLy0vZywgJycpLmluZGV4T2Yoa2V5SWRIZXgpICE9PSAtMSkge1xuICAgICAgICBrZXlTZXNzaW9uQ29udGV4dFByb21pc2UgPSBrZXlJZFRvS2V5U2Vzc2lvblByb21pc2Vbb2xkS2V5SWRIZXhdO1xuICAgICAgICBpZiAoZGVjcnlwdGRhdGEucHNzaCkge1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIGRlbGV0ZSBrZXlJZFRvS2V5U2Vzc2lvblByb21pc2Vbb2xkS2V5SWRIZXhdO1xuICAgICAgICBkZWNyeXB0ZGF0YS5wc3NoID0gbmV3IFVpbnQ4QXJyYXkoaW5pdERhdGEpO1xuICAgICAgICBkZWNyeXB0ZGF0YS5rZXlJZCA9IGtleUlkO1xuICAgICAgICBrZXlTZXNzaW9uQ29udGV4dFByb21pc2UgPSBrZXlJZFRvS2V5U2Vzc2lvblByb21pc2Vba2V5SWRIZXhdID0ga2V5U2Vzc2lvbkNvbnRleHRQcm9taXNlLnRoZW4oKCkgPT4ge1xuICAgICAgICAgIHJldHVybiB0aGlzLmdlbmVyYXRlUmVxdWVzdFdpdGhQcmVmZXJyZWRLZXlTZXNzaW9uKGtleUNvbnRleHQsIGluaXREYXRhVHlwZSwgaW5pdERhdGEsICdlbmNyeXB0ZWQtZXZlbnQta2V5LW1hdGNoJyk7XG4gICAgICAgIH0pO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKCFrZXlTZXNzaW9uQ29udGV4dFByb21pc2UpIHtcbiAgICAgIC8vIENsZWFyLWxlYWQga2V5IChub3QgZW5jb3VudGVyZWQgaW4gcGxheWxpc3QpXG4gICAgICBrZXlTZXNzaW9uQ29udGV4dFByb21pc2UgPSBrZXlJZFRvS2V5U2Vzc2lvblByb21pc2Vba2V5SWRIZXhdID0gdGhpcy5nZXRLZXlTeXN0ZW1TZWxlY3Rpb25Qcm9taXNlKFtrZXlTeXN0ZW1Eb21haW5dKS50aGVuKCh7XG4gICAgICAgIGtleVN5c3RlbSxcbiAgICAgICAgbWVkaWFLZXlzXG4gICAgICB9KSA9PiB7XG4gICAgICAgIHZhciBfa2V5U3lzdGVtVG9LZXlTeXN0ZW07XG4gICAgICAgIHRoaXMudGhyb3dJZkRlc3Ryb3llZCgpO1xuICAgICAgICBjb25zdCBkZWNyeXB0ZGF0YSA9IG5ldyBMZXZlbEtleSgnSVNPLTIzMDAxLTcnLCBrZXlJZEhleCwgKF9rZXlTeXN0ZW1Ub0tleVN5c3RlbSA9IGtleVN5c3RlbURvbWFpblRvS2V5U3lzdGVtRm9ybWF0KGtleVN5c3RlbSkpICE9IG51bGwgPyBfa2V5U3lzdGVtVG9LZXlTeXN0ZW0gOiAnJyk7XG4gICAgICAgIGRlY3J5cHRkYXRhLnBzc2ggPSBuZXcgVWludDhBcnJheShpbml0RGF0YSk7XG4gICAgICAgIGRlY3J5cHRkYXRhLmtleUlkID0ga2V5SWQ7XG4gICAgICAgIHJldHVybiB0aGlzLmF0dGVtcHRTZXRNZWRpYUtleXMoa2V5U3lzdGVtLCBtZWRpYUtleXMpLnRoZW4oKCkgPT4ge1xuICAgICAgICAgIHRoaXMudGhyb3dJZkRlc3Ryb3llZCgpO1xuICAgICAgICAgIGNvbnN0IGtleVNlc3Npb25Db250ZXh0ID0gdGhpcy5jcmVhdGVNZWRpYUtleVNlc3Npb25Db250ZXh0KHtcbiAgICAgICAgICAgIGRlY3J5cHRkYXRhLFxuICAgICAgICAgICAga2V5U3lzdGVtLFxuICAgICAgICAgICAgbWVkaWFLZXlzXG4gICAgICAgICAgfSk7XG4gICAgICAgICAgcmV0dXJuIHRoaXMuZ2VuZXJhdGVSZXF1ZXN0V2l0aFByZWZlcnJlZEtleVNlc3Npb24oa2V5U2Vzc2lvbkNvbnRleHQsIGluaXREYXRhVHlwZSwgaW5pdERhdGEsICdlbmNyeXB0ZWQtZXZlbnQtbm8tbWF0Y2gnKTtcbiAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICB9XG4gICAga2V5U2Vzc2lvbkNvbnRleHRQcm9taXNlLmNhdGNoKGVycm9yID0+IHRoaXMuaGFuZGxlRXJyb3IoZXJyb3IpKTtcbiAgfVxuICBfb25XYWl0aW5nRm9yS2V5KGV2ZW50KSB7XG4gICAgdGhpcy5sb2coYFwiJHtldmVudC50eXBlfVwiIGV2ZW50YCk7XG4gIH1cbiAgYXR0ZW1wdFNldE1lZGlhS2V5cyhrZXlTeXN0ZW0sIG1lZGlhS2V5cykge1xuICAgIGNvbnN0IHF1ZXVlID0gdGhpcy5zZXRNZWRpYUtleXNRdWV1ZS5zbGljZSgpO1xuICAgIHRoaXMubG9nKGBTZXR0aW5nIG1lZGlhLWtleXMgZm9yIFwiJHtrZXlTeXN0ZW19XCJgKTtcbiAgICAvLyBPbmx5IG9uZSBzZXRNZWRpYUtleXMoKSBjYW4gcnVuIGF0IG9uZSB0aW1lLCBhbmQgbXVsdGlwbGUgc2V0TWVkaWFLZXlzKCkgb3BlcmF0aW9uc1xuICAgIC8vIGNhbiBiZSBxdWV1ZWQgZm9yIGV4ZWN1dGlvbiBmb3IgbXVsdGlwbGUga2V5IHNlc3Npb25zLlxuICAgIGNvbnN0IHNldE1lZGlhS2V5c1Byb21pc2UgPSBQcm9taXNlLmFsbChxdWV1ZSkudGhlbigoKSA9PiB7XG4gICAgICBpZiAoIXRoaXMubWVkaWEpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdBdHRlbXB0ZWQgdG8gc2V0IG1lZGlhS2V5cyB3aXRob3V0IG1lZGlhIGVsZW1lbnQgYXR0YWNoZWQnKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLm1lZGlhLnNldE1lZGlhS2V5cyhtZWRpYUtleXMpO1xuICAgIH0pO1xuICAgIHRoaXMuc2V0TWVkaWFLZXlzUXVldWUucHVzaChzZXRNZWRpYUtleXNQcm9taXNlKTtcbiAgICByZXR1cm4gc2V0TWVkaWFLZXlzUHJvbWlzZS50aGVuKCgpID0+IHtcbiAgICAgIHRoaXMubG9nKGBNZWRpYS1rZXlzIHNldCBmb3IgXCIke2tleVN5c3RlbX1cImApO1xuICAgICAgcXVldWUucHVzaChzZXRNZWRpYUtleXNQcm9taXNlKTtcbiAgICAgIHRoaXMuc2V0TWVkaWFLZXlzUXVldWUgPSB0aGlzLnNldE1lZGlhS2V5c1F1ZXVlLmZpbHRlcihwID0+IHF1ZXVlLmluZGV4T2YocCkgPT09IC0xKTtcbiAgICB9KTtcbiAgfVxuICBnZW5lcmF0ZVJlcXVlc3RXaXRoUHJlZmVycmVkS2V5U2Vzc2lvbihjb250ZXh0LCBpbml0RGF0YVR5cGUsIGluaXREYXRhLCByZWFzb24pIHtcbiAgICB2YXIgX3RoaXMkY29uZmlnJGRybVN5c3RlLCBfdGhpcyRjb25maWckZHJtU3lzdGUyO1xuICAgIGNvbnN0IGdlbmVyYXRlUmVxdWVzdEZpbHRlciA9IChfdGhpcyRjb25maWckZHJtU3lzdGUgPSB0aGlzLmNvbmZpZy5kcm1TeXN0ZW1zKSA9PSBudWxsID8gdm9pZCAwIDogKF90aGlzJGNvbmZpZyRkcm1TeXN0ZTIgPSBfdGhpcyRjb25maWckZHJtU3lzdGVbY29udGV4dC5rZXlTeXN0ZW1dKSA9PSBudWxsID8gdm9pZCAwIDogX3RoaXMkY29uZmlnJGRybVN5c3RlMi5nZW5lcmF0ZVJlcXVlc3Q7XG4gICAgaWYgKGdlbmVyYXRlUmVxdWVzdEZpbHRlcikge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgbWFwcGVkSW5pdERhdGEgPSBnZW5lcmF0ZVJlcXVlc3RGaWx0ZXIuY2FsbCh0aGlzLmhscywgaW5pdERhdGFUeXBlLCBpbml0RGF0YSwgY29udGV4dCk7XG4gICAgICAgIGlmICghbWFwcGVkSW5pdERhdGEpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgcmVzcG9uc2UgZnJvbSBjb25maWd1cmVkIGdlbmVyYXRlUmVxdWVzdCBmaWx0ZXInKTtcbiAgICAgICAgfVxuICAgICAgICBpbml0RGF0YVR5cGUgPSBtYXBwZWRJbml0RGF0YS5pbml0RGF0YVR5cGU7XG4gICAgICAgIGluaXREYXRhID0gY29udGV4dC5kZWNyeXB0ZGF0YS5wc3NoID0gbWFwcGVkSW5pdERhdGEuaW5pdERhdGEgPyBuZXcgVWludDhBcnJheShtYXBwZWRJbml0RGF0YS5pbml0RGF0YSkgOiBudWxsO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgdmFyIF90aGlzJGhscztcbiAgICAgICAgdGhpcy53YXJuKGVycm9yLm1lc3NhZ2UpO1xuICAgICAgICBpZiAoKF90aGlzJGhscyA9IHRoaXMuaGxzKSAhPSBudWxsICYmIF90aGlzJGhscy5jb25maWcuZGVidWcpIHtcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICBpZiAoaW5pdERhdGEgPT09IG51bGwpIHtcbiAgICAgIHRoaXMubG9nKGBTa2lwcGluZyBrZXktc2Vzc2lvbiByZXF1ZXN0IGZvciBcIiR7cmVhc29ufVwiIChubyBpbml0RGF0YSlgKTtcbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoY29udGV4dCk7XG4gICAgfVxuICAgIGNvbnN0IGtleUlkID0gdGhpcy5nZXRLZXlJZFN0cmluZyhjb250ZXh0LmRlY3J5cHRkYXRhKTtcbiAgICB0aGlzLmxvZyhgR2VuZXJhdGluZyBrZXktc2Vzc2lvbiByZXF1ZXN0IGZvciBcIiR7cmVhc29ufVwiOiAke2tleUlkfSAoaW5pdCBkYXRhIHR5cGU6ICR7aW5pdERhdGFUeXBlfSBsZW5ndGg6ICR7aW5pdERhdGEgPyBpbml0RGF0YS5ieXRlTGVuZ3RoIDogbnVsbH0pYCk7XG4gICAgY29uc3QgbGljZW5zZVN0YXR1cyA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcbiAgICBjb25zdCBvbm1lc3NhZ2UgPSBjb250ZXh0Ll9vbm1lc3NhZ2UgPSBldmVudCA9PiB7XG4gICAgICBjb25zdCBrZXlTZXNzaW9uID0gY29udGV4dC5tZWRpYUtleXNTZXNzaW9uO1xuICAgICAgaWYgKCFrZXlTZXNzaW9uKSB7XG4gICAgICAgIGxpY2Vuc2VTdGF0dXMuZW1pdCgnZXJyb3InLCBuZXcgRXJyb3IoJ2ludmFsaWQgc3RhdGUnKSk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHtcbiAgICAgICAgbWVzc2FnZVR5cGUsXG4gICAgICAgIG1lc3NhZ2VcbiAgICAgIH0gPSBldmVudDtcbiAgICAgIHRoaXMubG9nKGBcIiR7bWVzc2FnZVR5cGV9XCIgbWVzc2FnZSBldmVudCBmb3Igc2Vzc2lvbiBcIiR7a2V5U2Vzc2lvbi5zZXNzaW9uSWR9XCIgbWVzc2FnZSBzaXplOiAke21lc3NhZ2UuYnl0ZUxlbmd0aH1gKTtcbiAgICAgIGlmIChtZXNzYWdlVHlwZSA9PT0gJ2xpY2Vuc2UtcmVxdWVzdCcgfHwgbWVzc2FnZVR5cGUgPT09ICdsaWNlbnNlLXJlbmV3YWwnKSB7XG4gICAgICAgIHRoaXMucmVuZXdMaWNlbnNlKGNvbnRleHQsIG1lc3NhZ2UpLmNhdGNoKGVycm9yID0+IHtcbiAgICAgICAgICB0aGlzLmhhbmRsZUVycm9yKGVycm9yKTtcbiAgICAgICAgICBsaWNlbnNlU3RhdHVzLmVtaXQoJ2Vycm9yJywgZXJyb3IpO1xuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSBpZiAobWVzc2FnZVR5cGUgPT09ICdsaWNlbnNlLXJlbGVhc2UnKSB7XG4gICAgICAgIGlmIChjb250ZXh0LmtleVN5c3RlbSA9PT0gS2V5U3lzdGVtcy5GQUlSUExBWSkge1xuICAgICAgICAgIHRoaXMudXBkYXRlS2V5U2Vzc2lvbihjb250ZXh0LCBzdHJUb1V0ZjhhcnJheSgnYWNrbm93bGVkZ2VkJykpO1xuICAgICAgICAgIHRoaXMucmVtb3ZlU2Vzc2lvbihjb250ZXh0KTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy53YXJuKGB1bmhhbmRsZWQgbWVkaWEga2V5IG1lc3NhZ2UgdHlwZSBcIiR7bWVzc2FnZVR5cGV9XCJgKTtcbiAgICAgIH1cbiAgICB9O1xuICAgIGNvbnN0IG9ua2V5c3RhdHVzZXNjaGFuZ2UgPSBjb250ZXh0Ll9vbmtleXN0YXR1c2VzY2hhbmdlID0gZXZlbnQgPT4ge1xuICAgICAgY29uc3Qga2V5U2Vzc2lvbiA9IGNvbnRleHQubWVkaWFLZXlzU2Vzc2lvbjtcbiAgICAgIGlmICgha2V5U2Vzc2lvbikge1xuICAgICAgICBsaWNlbnNlU3RhdHVzLmVtaXQoJ2Vycm9yJywgbmV3IEVycm9yKCdpbnZhbGlkIHN0YXRlJykpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aGlzLm9uS2V5U3RhdHVzQ2hhbmdlKGNvbnRleHQpO1xuICAgICAgY29uc3Qga2V5U3RhdHVzID0gY29udGV4dC5rZXlTdGF0dXM7XG4gICAgICBsaWNlbnNlU3RhdHVzLmVtaXQoJ2tleVN0YXR1cycsIGtleVN0YXR1cyk7XG4gICAgICBpZiAoa2V5U3RhdHVzID09PSAnZXhwaXJlZCcpIHtcbiAgICAgICAgdGhpcy53YXJuKGAke2NvbnRleHQua2V5U3lzdGVtfSBleHBpcmVkIGZvciBrZXkgJHtrZXlJZH1gKTtcbiAgICAgICAgdGhpcy5yZW5ld0tleVNlc3Npb24oY29udGV4dCk7XG4gICAgICB9XG4gICAgfTtcbiAgICBjb250ZXh0Lm1lZGlhS2V5c1Nlc3Npb24uYWRkRXZlbnRMaXN0ZW5lcignbWVzc2FnZScsIG9ubWVzc2FnZSk7XG4gICAgY29udGV4dC5tZWRpYUtleXNTZXNzaW9uLmFkZEV2ZW50TGlzdGVuZXIoJ2tleXN0YXR1c2VzY2hhbmdlJywgb25rZXlzdGF0dXNlc2NoYW5nZSk7XG4gICAgY29uc3Qga2V5VXNhYmxlUHJvbWlzZSA9IG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIGxpY2Vuc2VTdGF0dXMub24oJ2Vycm9yJywgcmVqZWN0KTtcbiAgICAgIGxpY2Vuc2VTdGF0dXMub24oJ2tleVN0YXR1cycsIGtleVN0YXR1cyA9PiB7XG4gICAgICAgIGlmIChrZXlTdGF0dXMuc3RhcnRzV2l0aCgndXNhYmxlJykpIHtcbiAgICAgICAgICByZXNvbHZlKCk7XG4gICAgICAgIH0gZWxzZSBpZiAoa2V5U3RhdHVzID09PSAnb3V0cHV0LXJlc3RyaWN0ZWQnKSB7XG4gICAgICAgICAgcmVqZWN0KG5ldyBFTUVLZXlFcnJvcih7XG4gICAgICAgICAgICB0eXBlOiBFcnJvclR5cGVzLktFWV9TWVNURU1fRVJST1IsXG4gICAgICAgICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuS0VZX1NZU1RFTV9TVEFUVVNfT1VUUFVUX1JFU1RSSUNURUQsXG4gICAgICAgICAgICBmYXRhbDogZmFsc2VcbiAgICAgICAgICB9LCAnSERDUCBsZXZlbCBvdXRwdXQgcmVzdHJpY3RlZCcpKTtcbiAgICAgICAgfSBlbHNlIGlmIChrZXlTdGF0dXMgPT09ICdpbnRlcm5hbC1lcnJvcicpIHtcbiAgICAgICAgICByZWplY3QobmV3IEVNRUtleUVycm9yKHtcbiAgICAgICAgICAgIHR5cGU6IEVycm9yVHlwZXMuS0VZX1NZU1RFTV9FUlJPUixcbiAgICAgICAgICAgIGRldGFpbHM6IEVycm9yRGV0YWlscy5LRVlfU1lTVEVNX1NUQVRVU19JTlRFUk5BTF9FUlJPUixcbiAgICAgICAgICAgIGZhdGFsOiB0cnVlXG4gICAgICAgICAgfSwgYGtleSBzdGF0dXMgY2hhbmdlZCB0byBcIiR7a2V5U3RhdHVzfVwiYCkpO1xuICAgICAgICB9IGVsc2UgaWYgKGtleVN0YXR1cyA9PT0gJ2V4cGlyZWQnKSB7XG4gICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcigna2V5IGV4cGlyZWQgd2hpbGUgZ2VuZXJhdGluZyByZXF1ZXN0JykpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRoaXMud2FybihgdW5oYW5kbGVkIGtleSBzdGF0dXMgY2hhbmdlIFwiJHtrZXlTdGF0dXN9XCJgKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfSk7XG4gICAgcmV0dXJuIGNvbnRleHQubWVkaWFLZXlzU2Vzc2lvbi5nZW5lcmF0ZVJlcXVlc3QoaW5pdERhdGFUeXBlLCBpbml0RGF0YSkudGhlbigoKSA9PiB7XG4gICAgICB2YXIgX2NvbnRleHQkbWVkaWFLZXlzU2VzO1xuICAgICAgdGhpcy5sb2coYFJlcXVlc3QgZ2VuZXJhdGVkIGZvciBrZXktc2Vzc2lvbiBcIiR7KF9jb250ZXh0JG1lZGlhS2V5c1NlcyA9IGNvbnRleHQubWVkaWFLZXlzU2Vzc2lvbikgPT0gbnVsbCA/IHZvaWQgMCA6IF9jb250ZXh0JG1lZGlhS2V5c1Nlcy5zZXNzaW9uSWR9XCIga2V5SWQ6ICR7a2V5SWR9YCk7XG4gICAgfSkuY2F0Y2goZXJyb3IgPT4ge1xuICAgICAgdGhyb3cgbmV3IEVNRUtleUVycm9yKHtcbiAgICAgICAgdHlwZTogRXJyb3JUeXBlcy5LRVlfU1lTVEVNX0VSUk9SLFxuICAgICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuS0VZX1NZU1RFTV9OT19TRVNTSU9OLFxuICAgICAgICBlcnJvcixcbiAgICAgICAgZmF0YWw6IGZhbHNlXG4gICAgICB9LCBgRXJyb3IgZ2VuZXJhdGluZyBrZXktc2Vzc2lvbiByZXF1ZXN0OiAke2Vycm9yfWApO1xuICAgIH0pLnRoZW4oKCkgPT4ga2V5VXNhYmxlUHJvbWlzZSkuY2F0Y2goZXJyb3IgPT4ge1xuICAgICAgbGljZW5zZVN0YXR1cy5yZW1vdmVBbGxMaXN0ZW5lcnMoKTtcbiAgICAgIHRoaXMucmVtb3ZlU2Vzc2lvbihjb250ZXh0KTtcbiAgICAgIHRocm93IGVycm9yO1xuICAgIH0pLnRoZW4oKCkgPT4ge1xuICAgICAgbGljZW5zZVN0YXR1cy5yZW1vdmVBbGxMaXN0ZW5lcnMoKTtcbiAgICAgIHJldHVybiBjb250ZXh0O1xuICAgIH0pO1xuICB9XG4gIG9uS2V5U3RhdHVzQ2hhbmdlKG1lZGlhS2V5U2Vzc2lvbkNvbnRleHQpIHtcbiAgICBtZWRpYUtleVNlc3Npb25Db250ZXh0Lm1lZGlhS2V5c1Nlc3Npb24ua2V5U3RhdHVzZXMuZm9yRWFjaCgoc3RhdHVzLCBrZXlJZCkgPT4ge1xuICAgICAgdGhpcy5sb2coYGtleSBzdGF0dXMgY2hhbmdlIFwiJHtzdGF0dXN9XCIgZm9yIGtleVN0YXR1c2VzIGtleUlkOiAke0hleC5oZXhEdW1wKCdidWZmZXInIGluIGtleUlkID8gbmV3IFVpbnQ4QXJyYXkoa2V5SWQuYnVmZmVyLCBrZXlJZC5ieXRlT2Zmc2V0LCBrZXlJZC5ieXRlTGVuZ3RoKSA6IG5ldyBVaW50OEFycmF5KGtleUlkKSl9IHNlc3Npb24ga2V5SWQ6ICR7SGV4LmhleER1bXAobmV3IFVpbnQ4QXJyYXkobWVkaWFLZXlTZXNzaW9uQ29udGV4dC5kZWNyeXB0ZGF0YS5rZXlJZCB8fCBbXSkpfSB1cmk6ICR7bWVkaWFLZXlTZXNzaW9uQ29udGV4dC5kZWNyeXB0ZGF0YS51cml9YCk7XG4gICAgICBtZWRpYUtleVNlc3Npb25Db250ZXh0LmtleVN0YXR1cyA9IHN0YXR1cztcbiAgICB9KTtcbiAgfVxuICBmZXRjaFNlcnZlckNlcnRpZmljYXRlKGtleVN5c3RlbSkge1xuICAgIGNvbnN0IGNvbmZpZyA9IHRoaXMuY29uZmlnO1xuICAgIGNvbnN0IExvYWRlciA9IGNvbmZpZy5sb2FkZXI7XG4gICAgY29uc3QgY2VydExvYWRlciA9IG5ldyBMb2FkZXIoY29uZmlnKTtcbiAgICBjb25zdCB1cmwgPSB0aGlzLmdldFNlcnZlckNlcnRpZmljYXRlVXJsKGtleVN5c3RlbSk7XG4gICAgaWYgKCF1cmwpIHtcbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcbiAgICB9XG4gICAgdGhpcy5sb2coYEZldGNoaW5nIHNlcnZlciBjZXJ0aWZpY2F0ZSBmb3IgXCIke2tleVN5c3RlbX1cImApO1xuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBjb25zdCBsb2FkZXJDb250ZXh0ID0ge1xuICAgICAgICByZXNwb25zZVR5cGU6ICdhcnJheWJ1ZmZlcicsXG4gICAgICAgIHVybFxuICAgICAgfTtcbiAgICAgIGNvbnN0IGxvYWRQb2xpY3kgPSBjb25maWcuY2VydExvYWRQb2xpY3kuZGVmYXVsdDtcbiAgICAgIGNvbnN0IGxvYWRlckNvbmZpZyA9IHtcbiAgICAgICAgbG9hZFBvbGljeSxcbiAgICAgICAgdGltZW91dDogbG9hZFBvbGljeS5tYXhMb2FkVGltZU1zLFxuICAgICAgICBtYXhSZXRyeTogMCxcbiAgICAgICAgcmV0cnlEZWxheTogMCxcbiAgICAgICAgbWF4UmV0cnlEZWxheTogMFxuICAgICAgfTtcbiAgICAgIGNvbnN0IGxvYWRlckNhbGxiYWNrcyA9IHtcbiAgICAgICAgb25TdWNjZXNzOiAocmVzcG9uc2UsIHN0YXRzLCBjb250ZXh0LCBuZXR3b3JrRGV0YWlscykgPT4ge1xuICAgICAgICAgIHJlc29sdmUocmVzcG9uc2UuZGF0YSk7XG4gICAgICAgIH0sXG4gICAgICAgIG9uRXJyb3I6IChyZXNwb25zZSwgY29udGV4LCBuZXR3b3JrRGV0YWlscywgc3RhdHMpID0+IHtcbiAgICAgICAgICByZWplY3QobmV3IEVNRUtleUVycm9yKHtcbiAgICAgICAgICAgIHR5cGU6IEVycm9yVHlwZXMuS0VZX1NZU1RFTV9FUlJPUixcbiAgICAgICAgICAgIGRldGFpbHM6IEVycm9yRGV0YWlscy5LRVlfU1lTVEVNX1NFUlZFUl9DRVJUSUZJQ0FURV9SRVFVRVNUX0ZBSUxFRCxcbiAgICAgICAgICAgIGZhdGFsOiB0cnVlLFxuICAgICAgICAgICAgbmV0d29ya0RldGFpbHMsXG4gICAgICAgICAgICByZXNwb25zZTogX29iamVjdFNwcmVhZDIoe1xuICAgICAgICAgICAgICB1cmw6IGxvYWRlckNvbnRleHQudXJsLFxuICAgICAgICAgICAgICBkYXRhOiB1bmRlZmluZWRcbiAgICAgICAgICAgIH0sIHJlc3BvbnNlKVxuICAgICAgICAgIH0sIGBcIiR7a2V5U3lzdGVtfVwiIGNlcnRpZmljYXRlIHJlcXVlc3QgZmFpbGVkICgke3VybH0pLiBTdGF0dXM6ICR7cmVzcG9uc2UuY29kZX0gKCR7cmVzcG9uc2UudGV4dH0pYCkpO1xuICAgICAgICB9LFxuICAgICAgICBvblRpbWVvdXQ6IChzdGF0cywgY29udGV4dCwgbmV0d29ya0RldGFpbHMpID0+IHtcbiAgICAgICAgICByZWplY3QobmV3IEVNRUtleUVycm9yKHtcbiAgICAgICAgICAgIHR5cGU6IEVycm9yVHlwZXMuS0VZX1NZU1RFTV9FUlJPUixcbiAgICAgICAgICAgIGRldGFpbHM6IEVycm9yRGV0YWlscy5LRVlfU1lTVEVNX1NFUlZFUl9DRVJUSUZJQ0FURV9SRVFVRVNUX0ZBSUxFRCxcbiAgICAgICAgICAgIGZhdGFsOiB0cnVlLFxuICAgICAgICAgICAgbmV0d29ya0RldGFpbHMsXG4gICAgICAgICAgICByZXNwb25zZToge1xuICAgICAgICAgICAgICB1cmw6IGxvYWRlckNvbnRleHQudXJsLFxuICAgICAgICAgICAgICBkYXRhOiB1bmRlZmluZWRcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9LCBgXCIke2tleVN5c3RlbX1cIiBjZXJ0aWZpY2F0ZSByZXF1ZXN0IHRpbWVkIG91dCAoJHt1cmx9KWApKTtcbiAgICAgICAgfSxcbiAgICAgICAgb25BYm9ydDogKHN0YXRzLCBjb250ZXh0LCBuZXR3b3JrRGV0YWlscykgPT4ge1xuICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoJ2Fib3J0ZWQnKSk7XG4gICAgICAgIH1cbiAgICAgIH07XG4gICAgICBjZXJ0TG9hZGVyLmxvYWQobG9hZGVyQ29udGV4dCwgbG9hZGVyQ29uZmlnLCBsb2FkZXJDYWxsYmFja3MpO1xuICAgIH0pO1xuICB9XG4gIHNldE1lZGlhS2V5c1NlcnZlckNlcnRpZmljYXRlKG1lZGlhS2V5cywga2V5U3lzdGVtLCBjZXJ0KSB7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIG1lZGlhS2V5cy5zZXRTZXJ2ZXJDZXJ0aWZpY2F0ZShjZXJ0KS50aGVuKHN1Y2Nlc3MgPT4ge1xuICAgICAgICB0aGlzLmxvZyhgc2V0U2VydmVyQ2VydGlmaWNhdGUgJHtzdWNjZXNzID8gJ3N1Y2Nlc3MnIDogJ25vdCBzdXBwb3J0ZWQgYnkgQ0RNJ30gKCR7Y2VydCA9PSBudWxsID8gdm9pZCAwIDogY2VydC5ieXRlTGVuZ3RofSkgb24gXCIke2tleVN5c3RlbX1cImApO1xuICAgICAgICByZXNvbHZlKG1lZGlhS2V5cyk7XG4gICAgICB9KS5jYXRjaChlcnJvciA9PiB7XG4gICAgICAgIHJlamVjdChuZXcgRU1FS2V5RXJyb3Ioe1xuICAgICAgICAgIHR5cGU6IEVycm9yVHlwZXMuS0VZX1NZU1RFTV9FUlJPUixcbiAgICAgICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuS0VZX1NZU1RFTV9TRVJWRVJfQ0VSVElGSUNBVEVfVVBEQVRFX0ZBSUxFRCxcbiAgICAgICAgICBlcnJvcixcbiAgICAgICAgICBmYXRhbDogdHJ1ZVxuICAgICAgICB9LCBlcnJvci5tZXNzYWdlKSk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuICByZW5ld0xpY2Vuc2UoY29udGV4dCwga2V5TWVzc2FnZSkge1xuICAgIHJldHVybiB0aGlzLnJlcXVlc3RMaWNlbnNlKGNvbnRleHQsIG5ldyBVaW50OEFycmF5KGtleU1lc3NhZ2UpKS50aGVuKGRhdGEgPT4ge1xuICAgICAgcmV0dXJuIHRoaXMudXBkYXRlS2V5U2Vzc2lvbihjb250ZXh0LCBuZXcgVWludDhBcnJheShkYXRhKSkuY2F0Y2goZXJyb3IgPT4ge1xuICAgICAgICB0aHJvdyBuZXcgRU1FS2V5RXJyb3Ioe1xuICAgICAgICAgIHR5cGU6IEVycm9yVHlwZXMuS0VZX1NZU1RFTV9FUlJPUixcbiAgICAgICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuS0VZX1NZU1RFTV9TRVNTSU9OX1VQREFURV9GQUlMRUQsXG4gICAgICAgICAgZXJyb3IsXG4gICAgICAgICAgZmF0YWw6IHRydWVcbiAgICAgICAgfSwgZXJyb3IubWVzc2FnZSk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuICB1bnBhY2tQbGF5UmVhZHlLZXlNZXNzYWdlKHhociwgbGljZW5zZUNoYWxsZW5nZSkge1xuICAgIC8vIE9uIEVkZ2UsIHRoZSByYXcgbGljZW5zZSBtZXNzYWdlIGlzIFVURi0xNi1lbmNvZGVkIFhNTC4gIFdlIG5lZWRcbiAgICAvLyB0byB1bnBhY2sgdGhlIENoYWxsZW5nZSBlbGVtZW50IChiYXNlNjQtZW5jb2RlZCBzdHJpbmcgY29udGFpbmluZyB0aGVcbiAgICAvLyBhY3R1YWwgbGljZW5zZSByZXF1ZXN0KSBhbmQgYW55IEh0dHBIZWFkZXIgZWxlbWVudHMgKHNlbnQgYXMgcmVxdWVzdFxuICAgIC8vIGhlYWRlcnMpLlxuICAgIC8vIEZvciBQbGF5UmVhZHkgQ0RNcywgd2UgbmVlZCB0byBkaWcgdGhlIENoYWxsZW5nZSBvdXQgb2YgdGhlIFhNTC5cbiAgICBjb25zdCB4bWxTdHJpbmcgPSBTdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KG51bGwsIG5ldyBVaW50MTZBcnJheShsaWNlbnNlQ2hhbGxlbmdlLmJ1ZmZlcikpO1xuICAgIGlmICgheG1sU3RyaW5nLmluY2x1ZGVzKCdQbGF5UmVhZHlLZXlNZXNzYWdlJykpIHtcbiAgICAgIC8vIFRoaXMgZG9lcyBub3QgYXBwZWFyIHRvIGJlIGEgd3JhcHBlZCBtZXNzYWdlIGFzIG9uIEVkZ2UuICBTb21lXG4gICAgICAvLyBjbGllbnRzIGRvIG5vdCBuZWVkIHRoaXMgdW53cmFwcGluZywgc28gd2Ugd2lsbCBhc3N1bWUgdGhpcyBpcyBvbmUgb2ZcbiAgICAgIC8vIHRoZW0uICBOb3RlIHRoYXQgXCJ4bWxcIiBhdCB0aGlzIHBvaW50IHByb2JhYmx5IGxvb2tzIGxpa2UgcmFuZG9tXG4gICAgICAvLyBnYXJiYWdlLCBzaW5jZSB3ZSBpbnRlcnByZXRlZCBVVEYtOCBhcyBVVEYtMTYuXG4gICAgICB4aHIuc2V0UmVxdWVzdEhlYWRlcignQ29udGVudC1UeXBlJywgJ3RleHQveG1sOyBjaGFyc2V0PXV0Zi04Jyk7XG4gICAgICByZXR1cm4gbGljZW5zZUNoYWxsZW5nZTtcbiAgICB9XG4gICAgY29uc3Qga2V5TWVzc2FnZVhtbCA9IG5ldyBET01QYXJzZXIoKS5wYXJzZUZyb21TdHJpbmcoeG1sU3RyaW5nLCAnYXBwbGljYXRpb24veG1sJyk7XG4gICAgLy8gU2V0IHJlcXVlc3QgaGVhZGVycy5cbiAgICBjb25zdCBoZWFkZXJzID0ga2V5TWVzc2FnZVhtbC5xdWVyeVNlbGVjdG9yQWxsKCdIdHRwSGVhZGVyJyk7XG4gICAgaWYgKGhlYWRlcnMubGVuZ3RoID4gMCkge1xuICAgICAgbGV0IGhlYWRlcjtcbiAgICAgIGZvciAobGV0IGkgPSAwLCBsZW4gPSBoZWFkZXJzLmxlbmd0aDsgaSA8IGxlbjsgaSsrKSB7XG4gICAgICAgIHZhciBfaGVhZGVyJHF1ZXJ5U2VsZWN0b3IsIF9oZWFkZXIkcXVlcnlTZWxlY3RvcjI7XG4gICAgICAgIGhlYWRlciA9IGhlYWRlcnNbaV07XG4gICAgICAgIGNvbnN0IG5hbWUgPSAoX2hlYWRlciRxdWVyeVNlbGVjdG9yID0gaGVhZGVyLnF1ZXJ5U2VsZWN0b3IoJ25hbWUnKSkgPT0gbnVsbCA/IHZvaWQgMCA6IF9oZWFkZXIkcXVlcnlTZWxlY3Rvci50ZXh0Q29udGVudDtcbiAgICAgICAgY29uc3QgdmFsdWUgPSAoX2hlYWRlciRxdWVyeVNlbGVjdG9yMiA9IGhlYWRlci5xdWVyeVNlbGVjdG9yKCd2YWx1ZScpKSA9PSBudWxsID8gdm9pZCAwIDogX2hlYWRlciRxdWVyeVNlbGVjdG9yMi50ZXh0Q29udGVudDtcbiAgICAgICAgaWYgKG5hbWUgJiYgdmFsdWUpIHtcbiAgICAgICAgICB4aHIuc2V0UmVxdWVzdEhlYWRlcihuYW1lLCB2YWx1ZSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgY29uc3QgY2hhbGxlbmdlRWxlbWVudCA9IGtleU1lc3NhZ2VYbWwucXVlcnlTZWxlY3RvcignQ2hhbGxlbmdlJyk7XG4gICAgY29uc3QgY2hhbGxlbmdlVGV4dCA9IGNoYWxsZW5nZUVsZW1lbnQgPT0gbnVsbCA/IHZvaWQgMCA6IGNoYWxsZW5nZUVsZW1lbnQudGV4dENvbnRlbnQ7XG4gICAgaWYgKCFjaGFsbGVuZ2VUZXh0KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYENhbm5vdCBmaW5kIDxDaGFsbGVuZ2U+IGluIGtleSBtZXNzYWdlYCk7XG4gICAgfVxuICAgIHJldHVybiBzdHJUb1V0ZjhhcnJheShhdG9iKGNoYWxsZW5nZVRleHQpKTtcbiAgfVxuICBzZXR1cExpY2Vuc2VYSFIoeGhyLCB1cmwsIGtleXNMaXN0SXRlbSwgbGljZW5zZUNoYWxsZW5nZSkge1xuICAgIGNvbnN0IGxpY2Vuc2VYaHJTZXR1cCA9IHRoaXMuY29uZmlnLmxpY2Vuc2VYaHJTZXR1cDtcbiAgICBpZiAoIWxpY2Vuc2VYaHJTZXR1cCkge1xuICAgICAgeGhyLm9wZW4oJ1BPU1QnLCB1cmwsIHRydWUpO1xuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSh7XG4gICAgICAgIHhocixcbiAgICAgICAgbGljZW5zZUNoYWxsZW5nZVxuICAgICAgfSk7XG4gICAgfVxuICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKS50aGVuKCgpID0+IHtcbiAgICAgIGlmICgha2V5c0xpc3RJdGVtLmRlY3J5cHRkYXRhKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignS2V5IHJlbW92ZWQnKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBsaWNlbnNlWGhyU2V0dXAuY2FsbCh0aGlzLmhscywgeGhyLCB1cmwsIGtleXNMaXN0SXRlbSwgbGljZW5zZUNoYWxsZW5nZSk7XG4gICAgfSkuY2F0Y2goZXJyb3IgPT4ge1xuICAgICAgaWYgKCFrZXlzTGlzdEl0ZW0uZGVjcnlwdGRhdGEpIHtcbiAgICAgICAgLy8gS2V5IHNlc3Npb24gcmVtb3ZlZC4gQ2FuY2VsIGxpY2Vuc2UgcmVxdWVzdC5cbiAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICB9XG4gICAgICAvLyBsZXQncyB0cnkgdG8gb3BlbiBiZWZvcmUgcnVubmluZyBzZXR1cFxuICAgICAgeGhyLm9wZW4oJ1BPU1QnLCB1cmwsIHRydWUpO1xuICAgICAgcmV0dXJuIGxpY2Vuc2VYaHJTZXR1cC5jYWxsKHRoaXMuaGxzLCB4aHIsIHVybCwga2V5c0xpc3RJdGVtLCBsaWNlbnNlQ2hhbGxlbmdlKTtcbiAgICB9KS50aGVuKGxpY2Vuc2VYaHJTZXR1cFJlc3VsdCA9PiB7XG4gICAgICAvLyBpZiBsaWNlbnNlWGhyU2V0dXAgZGlkIG5vdCB5ZXQgY2FsbCBvcGVuLCBsZXQncyBkbyBpdCBub3dcbiAgICAgIGlmICgheGhyLnJlYWR5U3RhdGUpIHtcbiAgICAgICAgeGhyLm9wZW4oJ1BPU1QnLCB1cmwsIHRydWUpO1xuICAgICAgfVxuICAgICAgY29uc3QgZmluYWxMaWNlbnNlQ2hhbGxlbmdlID0gbGljZW5zZVhoclNldHVwUmVzdWx0ID8gbGljZW5zZVhoclNldHVwUmVzdWx0IDogbGljZW5zZUNoYWxsZW5nZTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHhocixcbiAgICAgICAgbGljZW5zZUNoYWxsZW5nZTogZmluYWxMaWNlbnNlQ2hhbGxlbmdlXG4gICAgICB9O1xuICAgIH0pO1xuICB9XG4gIHJlcXVlc3RMaWNlbnNlKGtleVNlc3Npb25Db250ZXh0LCBsaWNlbnNlQ2hhbGxlbmdlKSB7XG4gICAgY29uc3Qga2V5TG9hZFBvbGljeSA9IHRoaXMuY29uZmlnLmtleUxvYWRQb2xpY3kuZGVmYXVsdDtcbiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgY29uc3QgdXJsID0gdGhpcy5nZXRMaWNlbnNlU2VydmVyVXJsKGtleVNlc3Npb25Db250ZXh0LmtleVN5c3RlbSk7XG4gICAgICB0aGlzLmxvZyhgU2VuZGluZyBsaWNlbnNlIHJlcXVlc3QgdG8gVVJMOiAke3VybH1gKTtcbiAgICAgIGNvbnN0IHhociA9IG5ldyBYTUxIdHRwUmVxdWVzdCgpO1xuICAgICAgeGhyLnJlc3BvbnNlVHlwZSA9ICdhcnJheWJ1ZmZlcic7XG4gICAgICB4aHIub25yZWFkeXN0YXRlY2hhbmdlID0gKCkgPT4ge1xuICAgICAgICBpZiAoIXRoaXMuaGxzIHx8ICFrZXlTZXNzaW9uQ29udGV4dC5tZWRpYUtleXNTZXNzaW9uKSB7XG4gICAgICAgICAgcmV0dXJuIHJlamVjdChuZXcgRXJyb3IoJ2ludmFsaWQgc3RhdGUnKSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHhoci5yZWFkeVN0YXRlID09PSA0KSB7XG4gICAgICAgICAgaWYgKHhoci5zdGF0dXMgPT09IDIwMCkge1xuICAgICAgICAgICAgdGhpcy5fcmVxdWVzdExpY2Vuc2VGYWlsdXJlQ291bnQgPSAwO1xuICAgICAgICAgICAgbGV0IGRhdGEgPSB4aHIucmVzcG9uc2U7XG4gICAgICAgICAgICB0aGlzLmxvZyhgTGljZW5zZSByZWNlaXZlZCAke2RhdGEgaW5zdGFuY2VvZiBBcnJheUJ1ZmZlciA/IGRhdGEuYnl0ZUxlbmd0aCA6IGRhdGF9YCk7XG4gICAgICAgICAgICBjb25zdCBsaWNlbnNlUmVzcG9uc2VDYWxsYmFjayA9IHRoaXMuY29uZmlnLmxpY2Vuc2VSZXNwb25zZUNhbGxiYWNrO1xuICAgICAgICAgICAgaWYgKGxpY2Vuc2VSZXNwb25zZUNhbGxiYWNrKSB7XG4gICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgZGF0YSA9IGxpY2Vuc2VSZXNwb25zZUNhbGxiYWNrLmNhbGwodGhpcy5obHMsIHhociwgdXJsLCBrZXlTZXNzaW9uQ29udGV4dCk7XG4gICAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5lcnJvcihlcnJvcik7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJlc29sdmUoZGF0YSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNvbnN0IHJldHJ5Q29uZmlnID0ga2V5TG9hZFBvbGljeS5lcnJvclJldHJ5O1xuICAgICAgICAgICAgY29uc3QgbWF4TnVtUmV0cnkgPSByZXRyeUNvbmZpZyA/IHJldHJ5Q29uZmlnLm1heE51bVJldHJ5IDogMDtcbiAgICAgICAgICAgIHRoaXMuX3JlcXVlc3RMaWNlbnNlRmFpbHVyZUNvdW50Kys7XG4gICAgICAgICAgICBpZiAodGhpcy5fcmVxdWVzdExpY2Vuc2VGYWlsdXJlQ291bnQgPiBtYXhOdW1SZXRyeSB8fCB4aHIuc3RhdHVzID49IDQwMCAmJiB4aHIuc3RhdHVzIDwgNTAwKSB7XG4gICAgICAgICAgICAgIHJlamVjdChuZXcgRU1FS2V5RXJyb3Ioe1xuICAgICAgICAgICAgICAgIHR5cGU6IEVycm9yVHlwZXMuS0VZX1NZU1RFTV9FUlJPUixcbiAgICAgICAgICAgICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuS0VZX1NZU1RFTV9MSUNFTlNFX1JFUVVFU1RfRkFJTEVELFxuICAgICAgICAgICAgICAgIGZhdGFsOiB0cnVlLFxuICAgICAgICAgICAgICAgIG5ldHdvcmtEZXRhaWxzOiB4aHIsXG4gICAgICAgICAgICAgICAgcmVzcG9uc2U6IHtcbiAgICAgICAgICAgICAgICAgIHVybCxcbiAgICAgICAgICAgICAgICAgIGRhdGE6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICAgIGNvZGU6IHhoci5zdGF0dXMsXG4gICAgICAgICAgICAgICAgICB0ZXh0OiB4aHIuc3RhdHVzVGV4dFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfSwgYExpY2Vuc2UgUmVxdWVzdCBYSFIgZmFpbGVkICgke3VybH0pLiBTdGF0dXM6ICR7eGhyLnN0YXR1c30gKCR7eGhyLnN0YXR1c1RleHR9KWApKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIGNvbnN0IGF0dGVtcHRzTGVmdCA9IG1heE51bVJldHJ5IC0gdGhpcy5fcmVxdWVzdExpY2Vuc2VGYWlsdXJlQ291bnQgKyAxO1xuICAgICAgICAgICAgICB0aGlzLndhcm4oYFJldHJ5aW5nIGxpY2Vuc2UgcmVxdWVzdCwgJHthdHRlbXB0c0xlZnR9IGF0dGVtcHRzIGxlZnRgKTtcbiAgICAgICAgICAgICAgdGhpcy5yZXF1ZXN0TGljZW5zZShrZXlTZXNzaW9uQ29udGV4dCwgbGljZW5zZUNoYWxsZW5nZSkudGhlbihyZXNvbHZlLCByZWplY3QpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGlmIChrZXlTZXNzaW9uQ29udGV4dC5saWNlbnNlWGhyICYmIGtleVNlc3Npb25Db250ZXh0LmxpY2Vuc2VYaHIucmVhZHlTdGF0ZSAhPT0gWE1MSHR0cFJlcXVlc3QuRE9ORSkge1xuICAgICAgICBrZXlTZXNzaW9uQ29udGV4dC5saWNlbnNlWGhyLmFib3J0KCk7XG4gICAgICB9XG4gICAgICBrZXlTZXNzaW9uQ29udGV4dC5saWNlbnNlWGhyID0geGhyO1xuICAgICAgdGhpcy5zZXR1cExpY2Vuc2VYSFIoeGhyLCB1cmwsIGtleVNlc3Npb25Db250ZXh0LCBsaWNlbnNlQ2hhbGxlbmdlKS50aGVuKCh7XG4gICAgICAgIHhocixcbiAgICAgICAgbGljZW5zZUNoYWxsZW5nZVxuICAgICAgfSkgPT4ge1xuICAgICAgICBpZiAoa2V5U2Vzc2lvbkNvbnRleHQua2V5U3lzdGVtID09IEtleVN5c3RlbXMuUExBWVJFQURZKSB7XG4gICAgICAgICAgbGljZW5zZUNoYWxsZW5nZSA9IHRoaXMudW5wYWNrUGxheVJlYWR5S2V5TWVzc2FnZSh4aHIsIGxpY2Vuc2VDaGFsbGVuZ2UpO1xuICAgICAgICB9XG4gICAgICAgIHhoci5zZW5kKGxpY2Vuc2VDaGFsbGVuZ2UpO1xuICAgICAgfSk7XG4gICAgfSk7XG4gIH1cbiAgb25NZWRpYUF0dGFjaGVkKGV2ZW50LCBkYXRhKSB7XG4gICAgaWYgKCF0aGlzLmNvbmZpZy5lbWVFbmFibGVkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IG1lZGlhID0gZGF0YS5tZWRpYTtcblxuICAgIC8vIGtlZXAgcmVmZXJlbmNlIG9mIG1lZGlhXG4gICAgdGhpcy5tZWRpYSA9IG1lZGlhO1xuICAgIG1lZGlhLmFkZEV2ZW50TGlzdGVuZXIoJ2VuY3J5cHRlZCcsIHRoaXMub25NZWRpYUVuY3J5cHRlZCk7XG4gICAgbWVkaWEuYWRkRXZlbnRMaXN0ZW5lcignd2FpdGluZ2ZvcmtleScsIHRoaXMub25XYWl0aW5nRm9yS2V5KTtcbiAgfVxuICBvbk1lZGlhRGV0YWNoZWQoKSB7XG4gICAgY29uc3QgbWVkaWEgPSB0aGlzLm1lZGlhO1xuICAgIGNvbnN0IG1lZGlhS2V5c0xpc3QgPSB0aGlzLm1lZGlhS2V5U2Vzc2lvbnM7XG4gICAgaWYgKG1lZGlhKSB7XG4gICAgICBtZWRpYS5yZW1vdmVFdmVudExpc3RlbmVyKCdlbmNyeXB0ZWQnLCB0aGlzLm9uTWVkaWFFbmNyeXB0ZWQpO1xuICAgICAgbWVkaWEucmVtb3ZlRXZlbnRMaXN0ZW5lcignd2FpdGluZ2ZvcmtleScsIHRoaXMub25XYWl0aW5nRm9yS2V5KTtcbiAgICAgIHRoaXMubWVkaWEgPSBudWxsO1xuICAgIH1cbiAgICB0aGlzLl9yZXF1ZXN0TGljZW5zZUZhaWx1cmVDb3VudCA9IDA7XG4gICAgdGhpcy5zZXRNZWRpYUtleXNRdWV1ZSA9IFtdO1xuICAgIHRoaXMubWVkaWFLZXlTZXNzaW9ucyA9IFtdO1xuICAgIHRoaXMua2V5SWRUb0tleVNlc3Npb25Qcm9taXNlID0ge307XG4gICAgTGV2ZWxLZXkuY2xlYXJLZXlVcmlUb0tleUlkTWFwKCk7XG5cbiAgICAvLyBDbG9zZSBhbGwgc2Vzc2lvbnMgYW5kIHJlbW92ZSBtZWRpYSBrZXlzIGZyb20gdGhlIHZpZGVvIGVsZW1lbnQuXG4gICAgY29uc3Qga2V5U2Vzc2lvbkNvdW50ID0gbWVkaWFLZXlzTGlzdC5sZW5ndGg7XG4gICAgRU1FQ29udHJvbGxlci5DRE1DbGVhbnVwUHJvbWlzZSA9IFByb21pc2UuYWxsKG1lZGlhS2V5c0xpc3QubWFwKG1lZGlhS2V5U2Vzc2lvbkNvbnRleHQgPT4gdGhpcy5yZW1vdmVTZXNzaW9uKG1lZGlhS2V5U2Vzc2lvbkNvbnRleHQpKS5jb25jYXQobWVkaWEgPT0gbnVsbCA/IHZvaWQgMCA6IG1lZGlhLnNldE1lZGlhS2V5cyhudWxsKS5jYXRjaChlcnJvciA9PiB7XG4gICAgICB0aGlzLmxvZyhgQ291bGQgbm90IGNsZWFyIG1lZGlhIGtleXM6ICR7ZXJyb3J9YCk7XG4gICAgfSkpKS50aGVuKCgpID0+IHtcbiAgICAgIGlmIChrZXlTZXNzaW9uQ291bnQpIHtcbiAgICAgICAgdGhpcy5sb2coJ2ZpbmlzaGVkIGNsb3Npbmcga2V5IHNlc3Npb25zIGFuZCBjbGVhcmluZyBtZWRpYSBrZXlzJyk7XG4gICAgICAgIG1lZGlhS2V5c0xpc3QubGVuZ3RoID0gMDtcbiAgICAgIH1cbiAgICB9KS5jYXRjaChlcnJvciA9PiB7XG4gICAgICB0aGlzLmxvZyhgQ291bGQgbm90IGNsb3NlIHNlc3Npb25zIGFuZCBjbGVhciBtZWRpYSBrZXlzOiAke2Vycm9yfWApO1xuICAgIH0pO1xuICB9XG4gIG9uTWFuaWZlc3RMb2FkaW5nKCkge1xuICAgIHRoaXMua2V5Rm9ybWF0UHJvbWlzZSA9IG51bGw7XG4gIH1cbiAgb25NYW5pZmVzdExvYWRlZChldmVudCwge1xuICAgIHNlc3Npb25LZXlzXG4gIH0pIHtcbiAgICBpZiAoIXNlc3Npb25LZXlzIHx8ICF0aGlzLmNvbmZpZy5lbWVFbmFibGVkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmICghdGhpcy5rZXlGb3JtYXRQcm9taXNlKSB7XG4gICAgICBjb25zdCBrZXlGb3JtYXRzID0gc2Vzc2lvbktleXMucmVkdWNlKChmb3JtYXRzLCBzZXNzaW9uS2V5KSA9PiB7XG4gICAgICAgIGlmIChmb3JtYXRzLmluZGV4T2Yoc2Vzc2lvbktleS5rZXlGb3JtYXQpID09PSAtMSkge1xuICAgICAgICAgIGZvcm1hdHMucHVzaChzZXNzaW9uS2V5LmtleUZvcm1hdCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGZvcm1hdHM7XG4gICAgICB9LCBbXSk7XG4gICAgICB0aGlzLmxvZyhgU2VsZWN0aW5nIGtleS1zeXN0ZW0gZnJvbSBzZXNzaW9uLWtleXMgJHtrZXlGb3JtYXRzLmpvaW4oJywgJyl9YCk7XG4gICAgICB0aGlzLmtleUZvcm1hdFByb21pc2UgPSB0aGlzLmdldEtleUZvcm1hdFByb21pc2Uoa2V5Rm9ybWF0cyk7XG4gICAgfVxuICB9XG4gIHJlbW92ZVNlc3Npb24obWVkaWFLZXlTZXNzaW9uQ29udGV4dCkge1xuICAgIGNvbnN0IHtcbiAgICAgIG1lZGlhS2V5c1Nlc3Npb24sXG4gICAgICBsaWNlbnNlWGhyXG4gICAgfSA9IG1lZGlhS2V5U2Vzc2lvbkNvbnRleHQ7XG4gICAgaWYgKG1lZGlhS2V5c1Nlc3Npb24pIHtcbiAgICAgIHRoaXMubG9nKGBSZW1vdmUgbGljZW5zZXMgYW5kIGtleXMgYW5kIGNsb3NlIHNlc3Npb24gJHttZWRpYUtleXNTZXNzaW9uLnNlc3Npb25JZH1gKTtcbiAgICAgIGlmIChtZWRpYUtleVNlc3Npb25Db250ZXh0Ll9vbm1lc3NhZ2UpIHtcbiAgICAgICAgbWVkaWFLZXlzU2Vzc2lvbi5yZW1vdmVFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgbWVkaWFLZXlTZXNzaW9uQ29udGV4dC5fb25tZXNzYWdlKTtcbiAgICAgICAgbWVkaWFLZXlTZXNzaW9uQ29udGV4dC5fb25tZXNzYWdlID0gdW5kZWZpbmVkO1xuICAgICAgfVxuICAgICAgaWYgKG1lZGlhS2V5U2Vzc2lvbkNvbnRleHQuX29ua2V5c3RhdHVzZXNjaGFuZ2UpIHtcbiAgICAgICAgbWVkaWFLZXlzU2Vzc2lvbi5yZW1vdmVFdmVudExpc3RlbmVyKCdrZXlzdGF0dXNlc2NoYW5nZScsIG1lZGlhS2V5U2Vzc2lvbkNvbnRleHQuX29ua2V5c3RhdHVzZXNjaGFuZ2UpO1xuICAgICAgICBtZWRpYUtleVNlc3Npb25Db250ZXh0Ll9vbmtleXN0YXR1c2VzY2hhbmdlID0gdW5kZWZpbmVkO1xuICAgICAgfVxuICAgICAgaWYgKGxpY2Vuc2VYaHIgJiYgbGljZW5zZVhoci5yZWFkeVN0YXRlICE9PSBYTUxIdHRwUmVxdWVzdC5ET05FKSB7XG4gICAgICAgIGxpY2Vuc2VYaHIuYWJvcnQoKTtcbiAgICAgIH1cbiAgICAgIG1lZGlhS2V5U2Vzc2lvbkNvbnRleHQubWVkaWFLZXlzU2Vzc2lvbiA9IG1lZGlhS2V5U2Vzc2lvbkNvbnRleHQuZGVjcnlwdGRhdGEgPSBtZWRpYUtleVNlc3Npb25Db250ZXh0LmxpY2Vuc2VYaHIgPSB1bmRlZmluZWQ7XG4gICAgICBjb25zdCBpbmRleCA9IHRoaXMubWVkaWFLZXlTZXNzaW9ucy5pbmRleE9mKG1lZGlhS2V5U2Vzc2lvbkNvbnRleHQpO1xuICAgICAgaWYgKGluZGV4ID4gLTEpIHtcbiAgICAgICAgdGhpcy5tZWRpYUtleVNlc3Npb25zLnNwbGljZShpbmRleCwgMSk7XG4gICAgICB9XG4gICAgICByZXR1cm4gbWVkaWFLZXlzU2Vzc2lvbi5yZW1vdmUoKS5jYXRjaChlcnJvciA9PiB7XG4gICAgICAgIHRoaXMubG9nKGBDb3VsZCBub3QgcmVtb3ZlIHNlc3Npb246ICR7ZXJyb3J9YCk7XG4gICAgICB9KS50aGVuKCgpID0+IHtcbiAgICAgICAgcmV0dXJuIG1lZGlhS2V5c1Nlc3Npb24uY2xvc2UoKTtcbiAgICAgIH0pLmNhdGNoKGVycm9yID0+IHtcbiAgICAgICAgdGhpcy5sb2coYENvdWxkIG5vdCBjbG9zZSBzZXNzaW9uOiAke2Vycm9yfWApO1xuICAgICAgfSk7XG4gICAgfVxuICB9XG59XG5FTUVDb250cm9sbGVyLkNETUNsZWFudXBQcm9taXNlID0gdm9pZCAwO1xuY2xhc3MgRU1FS2V5RXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKGRhdGEsIG1lc3NhZ2UpIHtcbiAgICBzdXBlcihtZXNzYWdlKTtcbiAgICB0aGlzLmRhdGEgPSB2b2lkIDA7XG4gICAgZGF0YS5lcnJvciB8fCAoZGF0YS5lcnJvciA9IG5ldyBFcnJvcihtZXNzYWdlKSk7XG4gICAgdGhpcy5kYXRhID0gZGF0YTtcbiAgICBkYXRhLmVyciA9IGRhdGEuZXJyb3I7XG4gIH1cbn1cblxuLyoqXG4gKiBDb21tb24gTWVkaWEgT2JqZWN0IFR5cGVcbiAqXG4gKiBAZ3JvdXAgQ01DRFxuICogQGdyb3VwIENNU0RcbiAqXG4gKiBAYmV0YVxuICovXG52YXIgQ21PYmplY3RUeXBlO1xuKGZ1bmN0aW9uIChDbU9iamVjdFR5cGUpIHtcbiAgLyoqXG4gICAqIHRleHQgZmlsZSwgc3VjaCBhcyBhIG1hbmlmZXN0IG9yIHBsYXlsaXN0XG4gICAqL1xuICBDbU9iamVjdFR5cGVbXCJNQU5JRkVTVFwiXSA9IFwibVwiO1xuICAvKipcbiAgICogYXVkaW8gb25seVxuICAgKi9cbiAgQ21PYmplY3RUeXBlW1wiQVVESU9cIl0gPSBcImFcIjtcbiAgLyoqXG4gICAqIHZpZGVvIG9ubHlcbiAgICovXG4gIENtT2JqZWN0VHlwZVtcIlZJREVPXCJdID0gXCJ2XCI7XG4gIC8qKlxuICAgKiBtdXhlZCBhdWRpbyBhbmQgdmlkZW9cbiAgICovXG4gIENtT2JqZWN0VHlwZVtcIk1VWEVEXCJdID0gXCJhdlwiO1xuICAvKipcbiAgICogaW5pdCBzZWdtZW50XG4gICAqL1xuICBDbU9iamVjdFR5cGVbXCJJTklUXCJdID0gXCJpXCI7XG4gIC8qKlxuICAgKiBjYXB0aW9uIG9yIHN1YnRpdGxlXG4gICAqL1xuICBDbU9iamVjdFR5cGVbXCJDQVBUSU9OXCJdID0gXCJjXCI7XG4gIC8qKlxuICAgKiBJU09CTUZGIHRpbWVkIHRleHQgdHJhY2tcbiAgICovXG4gIENtT2JqZWN0VHlwZVtcIlRJTUVEX1RFWFRcIl0gPSBcInR0XCI7XG4gIC8qKlxuICAgKiBjcnlwdG9ncmFwaGljIGtleSwgbGljZW5zZSBvciBjZXJ0aWZpY2F0ZS5cbiAgICovXG4gIENtT2JqZWN0VHlwZVtcIktFWVwiXSA9IFwia1wiO1xuICAvKipcbiAgICogb3RoZXJcbiAgICovXG4gIENtT2JqZWN0VHlwZVtcIk9USEVSXCJdID0gXCJvXCI7XG59KShDbU9iamVjdFR5cGUgfHwgKENtT2JqZWN0VHlwZSA9IHt9KSk7XG5cbi8qKlxuICogQ29tbW9uIE1lZGlhIFN0cmVhbWluZyBGb3JtYXRcbiAqXG4gKiBAZ3JvdXAgQ01DRFxuICogQGdyb3VwIENNU0RcbiAqXG4gKiBAYmV0YVxuICovXG52YXIgQ21TdHJlYW1pbmdGb3JtYXQ7XG4oZnVuY3Rpb24gKENtU3RyZWFtaW5nRm9ybWF0KSB7XG4gIC8qKlxuICAgKiBNUEVHIERBU0hcbiAgICovXG4gIENtU3RyZWFtaW5nRm9ybWF0W1wiREFTSFwiXSA9IFwiZFwiO1xuICAvKipcbiAgICogSFRUUCBMaXZlIFN0cmVhbWluZyAoSExTKVxuICAgKi9cbiAgQ21TdHJlYW1pbmdGb3JtYXRbXCJITFNcIl0gPSBcImhcIjtcbiAgLyoqXG4gICAqIFNtb290aCBTdHJlYW1pbmdcbiAgICovXG4gIENtU3RyZWFtaW5nRm9ybWF0W1wiU01PT1RIXCJdID0gXCJzXCI7XG4gIC8qKlxuICAgKiBPdGhlclxuICAgKi9cbiAgQ21TdHJlYW1pbmdGb3JtYXRbXCJPVEhFUlwiXSA9IFwib1wiO1xufSkoQ21TdHJlYW1pbmdGb3JtYXQgfHwgKENtU3RyZWFtaW5nRm9ybWF0ID0ge30pKTtcblxuLyoqXG4gKiBDTUNEIGhlYWRlciBmaWVsZHMuXG4gKlxuICogQGdyb3VwIENNQ0RcbiAqXG4gKiBAYmV0YVxuICovXG52YXIgQ21jZEhlYWRlckZpZWxkO1xuKGZ1bmN0aW9uIChDbWNkSGVhZGVyRmllbGQpIHtcbiAgLyoqXG4gICAqIGtleXMgd2hvc2UgdmFsdWVzIHZhcnkgd2l0aCB0aGUgb2JqZWN0IGJlaW5nIHJlcXVlc3RlZC5cbiAgICovXG4gIENtY2RIZWFkZXJGaWVsZFtcIk9CSkVDVFwiXSA9IFwiQ01DRC1PYmplY3RcIjtcbiAgLyoqXG4gICAqIGtleXMgd2hvc2UgdmFsdWVzIHZhcnkgd2l0aCBlYWNoIHJlcXVlc3QuXG4gICAqL1xuICBDbWNkSGVhZGVyRmllbGRbXCJSRVFVRVNUXCJdID0gXCJDTUNELVJlcXVlc3RcIjtcbiAgLyoqXG4gICAqIGtleXMgd2hvc2UgdmFsdWVzIGFyZSBleHBlY3RlZCB0byBiZSBpbnZhcmlhbnQgb3ZlciB0aGUgbGlmZSBvZiB0aGUgc2Vzc2lvbi5cbiAgICovXG4gIENtY2RIZWFkZXJGaWVsZFtcIlNFU1NJT05cIl0gPSBcIkNNQ0QtU2Vzc2lvblwiO1xuICAvKipcbiAgICoga2V5cyB3aG9zZSB2YWx1ZXMgZG8gbm90IHZhcnkgd2l0aCBldmVyeSByZXF1ZXN0IG9yIG9iamVjdC5cbiAgICovXG4gIENtY2RIZWFkZXJGaWVsZFtcIlNUQVRVU1wiXSA9IFwiQ01DRC1TdGF0dXNcIjtcbn0pKENtY2RIZWFkZXJGaWVsZCB8fCAoQ21jZEhlYWRlckZpZWxkID0ge30pKTtcblxuLyoqXG4gKiBUaGUgbWFwIG9mIENNQ0QgaGVhZGVyIGZpZWxkcyB0byBvZmZpY2lhbCBDTUNEIGtleXMuXG4gKlxuICogQGludGVybmFsXG4gKlxuICogQGdyb3VwIENNQ0RcbiAqL1xuY29uc3QgQ21jZEhlYWRlck1hcCA9IHtcbiAgW0NtY2RIZWFkZXJGaWVsZC5PQkpFQ1RdOiBbJ2JyJywgJ2QnLCAnb3QnLCAndGInXSxcbiAgW0NtY2RIZWFkZXJGaWVsZC5SRVFVRVNUXTogWydibCcsICdkbCcsICdtdHAnLCAnbm9yJywgJ25ycicsICdzdSddLFxuICBbQ21jZEhlYWRlckZpZWxkLlNFU1NJT05dOiBbJ2NpZCcsICdwcicsICdzZicsICdzaWQnLCAnc3QnLCAndiddLFxuICBbQ21jZEhlYWRlckZpZWxkLlNUQVRVU106IFsnYnMnLCAncnRwJ11cbn07XG5cbi8qKlxuICogU3RydWN0dXJlZCBGaWVsZCBJdGVtXG4gKlxuICogQGdyb3VwIFN0cnVjdHVyZWQgRmllbGRcbiAqXG4gKiBAYmV0YVxuICovXG5jbGFzcyBTZkl0ZW0ge1xuICBjb25zdHJ1Y3Rvcih2YWx1ZSwgcGFyYW1zKSB7XG4gICAgdGhpcy52YWx1ZSA9IHZvaWQgMDtcbiAgICB0aGlzLnBhcmFtcyA9IHZvaWQgMDtcbiAgICBpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSkpIHtcbiAgICAgIHZhbHVlID0gdmFsdWUubWFwKHYgPT4gdiBpbnN0YW5jZW9mIFNmSXRlbSA/IHYgOiBuZXcgU2ZJdGVtKHYpKTtcbiAgICB9XG4gICAgdGhpcy52YWx1ZSA9IHZhbHVlO1xuICAgIHRoaXMucGFyYW1zID0gcGFyYW1zO1xuICB9XG59XG5cbi8qKlxuICogQSBjbGFzcyB0byByZXByZXNlbnQgc3RydWN0dXJlZCBmaWVsZCB0b2tlbnMgd2hlbiBgU3ltYm9sYCBpcyBub3QgYXZhaWxhYmxlLlxuICpcbiAqIEBncm91cCBTdHJ1Y3R1cmVkIEZpZWxkXG4gKlxuICogQGJldGFcbiAqL1xuY2xhc3MgU2ZUb2tlbiB7XG4gIGNvbnN0cnVjdG9yKGRlc2NyaXB0aW9uKSB7XG4gICAgdGhpcy5kZXNjcmlwdGlvbiA9IHZvaWQgMDtcbiAgICB0aGlzLmRlc2NyaXB0aW9uID0gZGVzY3JpcHRpb247XG4gIH1cbn1cblxuY29uc3QgRElDVCA9ICdEaWN0JztcblxuZnVuY3Rpb24gZm9ybWF0KHZhbHVlKSB7XG4gIGlmIChBcnJheS5pc0FycmF5KHZhbHVlKSkge1xuICAgIHJldHVybiBKU09OLnN0cmluZ2lmeSh2YWx1ZSk7XG4gIH1cbiAgaWYgKHZhbHVlIGluc3RhbmNlb2YgTWFwKSB7XG4gICAgcmV0dXJuICdNYXB7fSc7XG4gIH1cbiAgaWYgKHZhbHVlIGluc3RhbmNlb2YgU2V0KSB7XG4gICAgcmV0dXJuICdTZXR7fSc7XG4gIH1cbiAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcpIHtcbiAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkodmFsdWUpO1xuICB9XG4gIHJldHVybiBTdHJpbmcodmFsdWUpO1xufVxuZnVuY3Rpb24gdGhyb3dFcnJvcihhY3Rpb24sIHNyYywgdHlwZSwgY2F1c2UpIHtcbiAgcmV0dXJuIG5ldyBFcnJvcihgZmFpbGVkIHRvICR7YWN0aW9ufSBcIiR7Zm9ybWF0KHNyYyl9XCIgYXMgJHt0eXBlfWAsIHtcbiAgICBjYXVzZVxuICB9KTtcbn1cblxuY29uc3QgQkFSRV9JVEVNID0gJ0JhcmUgSXRlbSc7XG5cbmNvbnN0IEJPT0xFQU4gPSAnQm9vbGVhbic7XG5cbmNvbnN0IEJZVEVTID0gJ0J5dGUgU2VxdWVuY2UnO1xuXG5jb25zdCBERUNJTUFMID0gJ0RlY2ltYWwnO1xuXG5jb25zdCBJTlRFR0VSID0gJ0ludGVnZXInO1xuXG5mdW5jdGlvbiBpc0ludmFsaWRJbnQodmFsdWUpIHtcbiAgcmV0dXJuIHZhbHVlIDwgLTk5OTk5OTk5OTk5OTk5OSB8fCA5OTk5OTk5OTk5OTk5OTkgPCB2YWx1ZTtcbn1cblxuY29uc3QgU1RSSU5HX1JFR0VYID0gL1tcXHgwMC1cXHgxZlxceDdmXSsvOyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIG5vLWNvbnRyb2wtcmVnZXhcblxuY29uc3QgVE9LRU4gPSAnVG9rZW4nO1xuXG5jb25zdCBLRVkgPSAnS2V5JztcblxuZnVuY3Rpb24gc2VyaWFsaXplRXJyb3Ioc3JjLCB0eXBlLCBjYXVzZSkge1xuICByZXR1cm4gdGhyb3dFcnJvcignc2VyaWFsaXplJywgc3JjLCB0eXBlLCBjYXVzZSk7XG59XG5cbi8vIDQuMS45LiAgU2VyaWFsaXppbmcgYSBCb29sZWFuXG4vL1xuLy8gR2l2ZW4gYSBCb29sZWFuIGFzIGlucHV0X2Jvb2xlYW4sIHJldHVybiBhbiBBU0NJSSBzdHJpbmcgc3VpdGFibGUgZm9yXG4vLyB1c2UgaW4gYSBIVFRQIGZpZWxkIHZhbHVlLlxuLy9cbi8vIDEuICBJZiBpbnB1dF9ib29sZWFuIGlzIG5vdCBhIGJvb2xlYW4sIGZhaWwgc2VyaWFsaXphdGlvbi5cbi8vXG4vLyAyLiAgTGV0IG91dHB1dCBiZSBhbiBlbXB0eSBzdHJpbmcuXG4vL1xuLy8gMy4gIEFwcGVuZCBcIj9cIiB0byBvdXRwdXQuXG4vL1xuLy8gNC4gIElmIGlucHV0X2Jvb2xlYW4gaXMgdHJ1ZSwgYXBwZW5kIFwiMVwiIHRvIG91dHB1dC5cbi8vXG4vLyA1LiAgSWYgaW5wdXRfYm9vbGVhbiBpcyBmYWxzZSwgYXBwZW5kIFwiMFwiIHRvIG91dHB1dC5cbi8vXG4vLyA2LiAgUmV0dXJuIG91dHB1dC5cbmZ1bmN0aW9uIHNlcmlhbGl6ZUJvb2xlYW4odmFsdWUpIHtcbiAgaWYgKHR5cGVvZiB2YWx1ZSAhPT0gJ2Jvb2xlYW4nKSB7XG4gICAgdGhyb3cgc2VyaWFsaXplRXJyb3IodmFsdWUsIEJPT0xFQU4pO1xuICB9XG4gIHJldHVybiB2YWx1ZSA/ICc/MScgOiAnPzAnO1xufVxuXG4vKipcbiAqIEVuY29kZXMgYmluYXJ5IGRhdGEgdG8gYmFzZTY0XG4gKlxuICogQHBhcmFtIGJpbmFyeSAtIFRoZSBiaW5hcnkgZGF0YSB0byBlbmNvZGVcbiAqIEByZXR1cm5zIFRoZSBiYXNlNjQgZW5jb2RlZCBzdHJpbmdcbiAqXG4gKiBAZ3JvdXAgVXRpbHNcbiAqXG4gKiBAYmV0YVxuICovXG5mdW5jdGlvbiBiYXNlNjRlbmNvZGUoYmluYXJ5KSB7XG4gIHJldHVybiBidG9hKFN0cmluZy5mcm9tQ2hhckNvZGUoLi4uYmluYXJ5KSk7XG59XG5cbi8vIDQuMS44LiAgU2VyaWFsaXppbmcgYSBCeXRlIFNlcXVlbmNlXG4vL1xuLy8gR2l2ZW4gYSBCeXRlIFNlcXVlbmNlIGFzIGlucHV0X2J5dGVzLCByZXR1cm4gYW4gQVNDSUkgc3RyaW5nIHN1aXRhYmxlXG4vLyBmb3IgdXNlIGluIGEgSFRUUCBmaWVsZCB2YWx1ZS5cbi8vXG4vLyAxLiAgSWYgaW5wdXRfYnl0ZXMgaXMgbm90IGEgc2VxdWVuY2Ugb2YgYnl0ZXMsIGZhaWwgc2VyaWFsaXphdGlvbi5cbi8vXG4vLyAyLiAgTGV0IG91dHB1dCBiZSBhbiBlbXB0eSBzdHJpbmcuXG4vL1xuLy8gMy4gIEFwcGVuZCBcIjpcIiB0byBvdXRwdXQuXG4vL1xuLy8gNC4gIEFwcGVuZCB0aGUgcmVzdWx0IG9mIGJhc2U2NC1lbmNvZGluZyBpbnB1dF9ieXRlcyBhcyBwZXJcbi8vICAgICBbUkZDNDY0OF0sIFNlY3Rpb24gNCwgdGFraW5nIGFjY291bnQgb2YgdGhlIHJlcXVpcmVtZW50cyBiZWxvdy5cbi8vXG4vLyA1LiAgQXBwZW5kIFwiOlwiIHRvIG91dHB1dC5cbi8vXG4vLyA2LiAgUmV0dXJuIG91dHB1dC5cbi8vXG4vLyBUaGUgZW5jb2RlZCBkYXRhIGlzIHJlcXVpcmVkIHRvIGJlIHBhZGRlZCB3aXRoIFwiPVwiLCBhcyBwZXIgW1JGQzQ2NDhdLFxuLy8gU2VjdGlvbiAzLjIuXG4vL1xuLy8gTGlrZXdpc2UsIGVuY29kZWQgZGF0YSBTSE9VTEQgaGF2ZSBwYWQgYml0cyBzZXQgdG8gemVybywgYXMgcGVyXG4vLyBbUkZDNDY0OF0sIFNlY3Rpb24gMy41LCB1bmxlc3MgaXQgaXMgbm90IHBvc3NpYmxlIHRvIGRvIHNvIGR1ZSB0b1xuLy8gaW1wbGVtZW50YXRpb24gY29uc3RyYWludHMuXG5mdW5jdGlvbiBzZXJpYWxpemVCeXRlU2VxdWVuY2UodmFsdWUpIHtcbiAgaWYgKEFycmF5QnVmZmVyLmlzVmlldyh2YWx1ZSkgPT09IGZhbHNlKSB7XG4gICAgdGhyb3cgc2VyaWFsaXplRXJyb3IodmFsdWUsIEJZVEVTKTtcbiAgfVxuICByZXR1cm4gYDoke2Jhc2U2NGVuY29kZSh2YWx1ZSl9OmA7XG59XG5cbi8vIDQuMS40LiAgU2VyaWFsaXppbmcgYW4gSW50ZWdlclxuLy9cbi8vIEdpdmVuIGFuIEludGVnZXIgYXMgaW5wdXRfaW50ZWdlciwgcmV0dXJuIGFuIEFTQ0lJIHN0cmluZyBzdWl0YWJsZVxuLy8gZm9yIHVzZSBpbiBhIEhUVFAgZmllbGQgdmFsdWUuXG4vL1xuLy8gMS4gIElmIGlucHV0X2ludGVnZXIgaXMgbm90IGFuIGludGVnZXIgaW4gdGhlIHJhbmdlIG9mXG4vLyAgICAgLTk5OSw5OTksOTk5LDk5OSw5OTkgdG8gOTk5LDk5OSw5OTksOTk5LDk5OSBpbmNsdXNpdmUsIGZhaWxcbi8vICAgICBzZXJpYWxpemF0aW9uLlxuLy9cbi8vIDIuICBMZXQgb3V0cHV0IGJlIGFuIGVtcHR5IHN0cmluZy5cbi8vXG4vLyAzLiAgSWYgaW5wdXRfaW50ZWdlciBpcyBsZXNzIHRoYW4gKGJ1dCBub3QgZXF1YWwgdG8pIDAsIGFwcGVuZCBcIi1cIiB0b1xuLy8gICAgIG91dHB1dC5cbi8vXG4vLyA0LiAgQXBwZW5kIGlucHV0X2ludGVnZXIncyBudW1lcmljIHZhbHVlIHJlcHJlc2VudGVkIGluIGJhc2UgMTAgdXNpbmdcbi8vICAgICBvbmx5IGRlY2ltYWwgZGlnaXRzIHRvIG91dHB1dC5cbi8vXG4vLyA1LiAgUmV0dXJuIG91dHB1dC5cbmZ1bmN0aW9uIHNlcmlhbGl6ZUludGVnZXIodmFsdWUpIHtcbiAgaWYgKGlzSW52YWxpZEludCh2YWx1ZSkpIHtcbiAgICB0aHJvdyBzZXJpYWxpemVFcnJvcih2YWx1ZSwgSU5URUdFUik7XG4gIH1cbiAgcmV0dXJuIHZhbHVlLnRvU3RyaW5nKCk7XG59XG5cbi8vIDQuMS4xMC4gIFNlcmlhbGl6aW5nIGEgRGF0ZVxuLy9cbi8vIEdpdmVuIGEgRGF0ZSBhcyBpbnB1dF9pbnRlZ2VyLCByZXR1cm4gYW4gQVNDSUkgc3RyaW5nIHN1aXRhYmxlIGZvclxuLy8gdXNlIGluIGFuIEhUVFAgZmllbGQgdmFsdWUuXG4vLyAxLiAgTGV0IG91dHB1dCBiZSBcIkBcIi5cbi8vIDIuICBBcHBlbmQgdG8gb3V0cHV0IHRoZSByZXN1bHQgb2YgcnVubmluZyBTZXJpYWxpemluZyBhbiBJbnRlZ2VyXG4vLyAgICAgd2l0aCBpbnB1dF9kYXRlIChTZWN0aW9uIDQuMS40KS5cbi8vIDMuICBSZXR1cm4gb3V0cHV0LlxuZnVuY3Rpb24gc2VyaWFsaXplRGF0ZSh2YWx1ZSkge1xuICByZXR1cm4gYEAke3NlcmlhbGl6ZUludGVnZXIodmFsdWUuZ2V0VGltZSgpIC8gMTAwMCl9YDtcbn1cblxuLyoqXG4gKiBUaGlzIGltcGxlbWVudHMgdGhlIHJvdW5kaW5nIHByb2NlZHVyZSBkZXNjcmliZWQgaW4gc3RlcCAyIG9mIHRoZSBcIlNlcmlhbGl6aW5nIGEgRGVjaW1hbFwiIHNwZWNpZmljYXRpb24uXG4gKiBUaGlzIHJvdW5kaW5nIHN0eWxlIGlzIGtub3duIGFzIFwiZXZlbiByb3VuZGluZ1wiLCBcImJhbmtlcidzIHJvdW5kaW5nXCIsIG9yIFwiY29tbWVyY2lhbCByb3VuZGluZ1wiLlxuICpcbiAqIEBwYXJhbSB2YWx1ZSAtIFRoZSB2YWx1ZSB0byByb3VuZFxuICogQHBhcmFtIHByZWNpc2lvbiAtIFRoZSBudW1iZXIgb2YgZGVjaW1hbCBwbGFjZXMgdG8gcm91bmQgdG9cbiAqIEByZXR1cm5zIFRoZSByb3VuZGVkIHZhbHVlXG4gKlxuICogQGdyb3VwIFV0aWxzXG4gKlxuICogQGJldGFcbiAqL1xuZnVuY3Rpb24gcm91bmRUb0V2ZW4odmFsdWUsIHByZWNpc2lvbikge1xuICBpZiAodmFsdWUgPCAwKSB7XG4gICAgcmV0dXJuIC1yb3VuZFRvRXZlbigtdmFsdWUsIHByZWNpc2lvbik7XG4gIH1cbiAgY29uc3QgZGVjaW1hbFNoaWZ0ID0gTWF0aC5wb3coMTAsIHByZWNpc2lvbik7XG4gIGNvbnN0IGlzRXF1aWRpc3RhbnQgPSBNYXRoLmFicyh2YWx1ZSAqIGRlY2ltYWxTaGlmdCAlIDEgLSAwLjUpIDwgTnVtYmVyLkVQU0lMT047XG4gIGlmIChpc0VxdWlkaXN0YW50KSB7XG4gICAgLy8gSWYgdGhlIHRhaWwgb2YgdGhlIGRlY2ltYWwgcGxhY2UgaXMgJ2VxdWlkaXN0YW50JyB3ZSByb3VuZCB0byB0aGUgbmVhcmVzdCBldmVuIHZhbHVlXG4gICAgY29uc3QgZmxvb3JlZFZhbHVlID0gTWF0aC5mbG9vcih2YWx1ZSAqIGRlY2ltYWxTaGlmdCk7XG4gICAgcmV0dXJuIChmbG9vcmVkVmFsdWUgJSAyID09PSAwID8gZmxvb3JlZFZhbHVlIDogZmxvb3JlZFZhbHVlICsgMSkgLyBkZWNpbWFsU2hpZnQ7XG4gIH0gZWxzZSB7XG4gICAgLy8gT3RoZXJ3aXNlLCBwcm9jZWVkIGFzIG5vcm1hbFxuICAgIHJldHVybiBNYXRoLnJvdW5kKHZhbHVlICogZGVjaW1hbFNoaWZ0KSAvIGRlY2ltYWxTaGlmdDtcbiAgfVxufVxuXG4vLyA0LjEuNS4gIFNlcmlhbGl6aW5nIGEgRGVjaW1hbFxuLy9cbi8vIEdpdmVuIGEgZGVjaW1hbCBudW1iZXIgYXMgaW5wdXRfZGVjaW1hbCwgcmV0dXJuIGFuIEFTQ0lJIHN0cmluZ1xuLy8gc3VpdGFibGUgZm9yIHVzZSBpbiBhIEhUVFAgZmllbGQgdmFsdWUuXG4vL1xuLy8gMS4gICBJZiBpbnB1dF9kZWNpbWFsIGlzIG5vdCBhIGRlY2ltYWwgbnVtYmVyLCBmYWlsIHNlcmlhbGl6YXRpb24uXG4vL1xuLy8gMi4gICBJZiBpbnB1dF9kZWNpbWFsIGhhcyBtb3JlIHRoYW4gdGhyZWUgc2lnbmlmaWNhbnQgZGlnaXRzIHRvIHRoZVxuLy8gICAgICByaWdodCBvZiB0aGUgZGVjaW1hbCBwb2ludCwgcm91bmQgaXQgdG8gdGhyZWUgZGVjaW1hbCBwbGFjZXMsXG4vLyAgICAgIHJvdW5kaW5nIHRoZSBmaW5hbCBkaWdpdCB0byB0aGUgbmVhcmVzdCB2YWx1ZSwgb3IgdG8gdGhlIGV2ZW5cbi8vICAgICAgdmFsdWUgaWYgaXQgaXMgZXF1aWRpc3RhbnQuXG4vL1xuLy8gMy4gICBJZiBpbnB1dF9kZWNpbWFsIGhhcyBtb3JlIHRoYW4gMTIgc2lnbmlmaWNhbnQgZGlnaXRzIHRvIHRoZSBsZWZ0XG4vLyAgICAgIG9mIHRoZSBkZWNpbWFsIHBvaW50IGFmdGVyIHJvdW5kaW5nLCBmYWlsIHNlcmlhbGl6YXRpb24uXG4vL1xuLy8gNC4gICBMZXQgb3V0cHV0IGJlIGFuIGVtcHR5IHN0cmluZy5cbi8vXG4vLyA1LiAgIElmIGlucHV0X2RlY2ltYWwgaXMgbGVzcyB0aGFuIChidXQgbm90IGVxdWFsIHRvKSAwLCBhcHBlbmQgXCItXCJcbi8vICAgICAgdG8gb3V0cHV0LlxuLy9cbi8vIDYuICAgQXBwZW5kIGlucHV0X2RlY2ltYWwncyBpbnRlZ2VyIGNvbXBvbmVudCByZXByZXNlbnRlZCBpbiBiYXNlIDEwXG4vLyAgICAgICh1c2luZyBvbmx5IGRlY2ltYWwgZGlnaXRzKSB0byBvdXRwdXQ7IGlmIGl0IGlzIHplcm8sIGFwcGVuZFxuLy8gICAgICBcIjBcIi5cbi8vXG4vLyA3LiAgIEFwcGVuZCBcIi5cIiB0byBvdXRwdXQuXG4vL1xuLy8gOC4gICBJZiBpbnB1dF9kZWNpbWFsJ3MgZnJhY3Rpb25hbCBjb21wb25lbnQgaXMgemVybywgYXBwZW5kIFwiMFwiIHRvXG4vLyAgICAgIG91dHB1dC5cbi8vXG4vLyA5LiAgIE90aGVyd2lzZSwgYXBwZW5kIHRoZSBzaWduaWZpY2FudCBkaWdpdHMgb2YgaW5wdXRfZGVjaW1hbCdzXG4vLyAgICAgIGZyYWN0aW9uYWwgY29tcG9uZW50IHJlcHJlc2VudGVkIGluIGJhc2UgMTAgKHVzaW5nIG9ubHkgZGVjaW1hbFxuLy8gICAgICBkaWdpdHMpIHRvIG91dHB1dC5cbi8vXG4vLyAxMC4gIFJldHVybiBvdXRwdXQuXG5mdW5jdGlvbiBzZXJpYWxpemVEZWNpbWFsKHZhbHVlKSB7XG4gIGNvbnN0IHJvdW5kZWRWYWx1ZSA9IHJvdW5kVG9FdmVuKHZhbHVlLCAzKTsgLy8gcm91bmQgdG8gMyBkZWNpbWFsIHBsYWNlc1xuICBpZiAoTWF0aC5mbG9vcihNYXRoLmFicyhyb3VuZGVkVmFsdWUpKS50b1N0cmluZygpLmxlbmd0aCA+IDEyKSB7XG4gICAgdGhyb3cgc2VyaWFsaXplRXJyb3IodmFsdWUsIERFQ0lNQUwpO1xuICB9XG4gIGNvbnN0IHN0cmluZ1ZhbHVlID0gcm91bmRlZFZhbHVlLnRvU3RyaW5nKCk7XG4gIHJldHVybiBzdHJpbmdWYWx1ZS5pbmNsdWRlcygnLicpID8gc3RyaW5nVmFsdWUgOiBgJHtzdHJpbmdWYWx1ZX0uMGA7XG59XG5cbmNvbnN0IFNUUklORyA9ICdTdHJpbmcnO1xuXG4vLyA0LjEuNi4gIFNlcmlhbGl6aW5nIGEgU3RyaW5nXG4vL1xuLy8gR2l2ZW4gYSBTdHJpbmcgYXMgaW5wdXRfc3RyaW5nLCByZXR1cm4gYW4gQVNDSUkgc3RyaW5nIHN1aXRhYmxlIGZvclxuLy8gdXNlIGluIGEgSFRUUCBmaWVsZCB2YWx1ZS5cbi8vXG4vLyAxLiAgQ29udmVydCBpbnB1dF9zdHJpbmcgaW50byBhIHNlcXVlbmNlIG9mIEFTQ0lJIGNoYXJhY3RlcnM7IGlmXG4vLyAgICAgY29udmVyc2lvbiBmYWlscywgZmFpbCBzZXJpYWxpemF0aW9uLlxuLy9cbi8vIDIuICBJZiBpbnB1dF9zdHJpbmcgY29udGFpbnMgY2hhcmFjdGVycyBpbiB0aGUgcmFuZ2UgJXgwMC0xZiBvciAleDdmXG4vLyAgICAgKGkuZS4sIG5vdCBpbiBWQ0hBUiBvciBTUCksIGZhaWwgc2VyaWFsaXphdGlvbi5cbi8vXG4vLyAzLiAgTGV0IG91dHB1dCBiZSB0aGUgc3RyaW5nIERRVU9URS5cbi8vXG4vLyA0LiAgRm9yIGVhY2ggY2hhcmFjdGVyIGNoYXIgaW4gaW5wdXRfc3RyaW5nOlxuLy9cbi8vICAgICAxLiAgSWYgY2hhciBpcyBcIlxcXCIgb3IgRFFVT1RFOlxuLy9cbi8vICAgICAgICAgMS4gIEFwcGVuZCBcIlxcXCIgdG8gb3V0cHV0LlxuLy9cbi8vICAgICAyLiAgQXBwZW5kIGNoYXIgdG8gb3V0cHV0LlxuLy9cbi8vIDUuICBBcHBlbmQgRFFVT1RFIHRvIG91dHB1dC5cbi8vXG4vLyA2LiAgUmV0dXJuIG91dHB1dC5cbmZ1bmN0aW9uIHNlcmlhbGl6ZVN0cmluZyh2YWx1ZSkge1xuICBpZiAoU1RSSU5HX1JFR0VYLnRlc3QodmFsdWUpKSB7XG4gICAgdGhyb3cgc2VyaWFsaXplRXJyb3IodmFsdWUsIFNUUklORyk7XG4gIH1cbiAgcmV0dXJuIGBcIiR7dmFsdWUucmVwbGFjZSgvXFxcXC9nLCBgXFxcXFxcXFxgKS5yZXBsYWNlKC9cIi9nLCBgXFxcXFwiYCl9XCJgO1xufVxuXG5mdW5jdGlvbiBzeW1ib2xUb1N0cihzeW1ib2wpIHtcbiAgcmV0dXJuIHN5bWJvbC5kZXNjcmlwdGlvbiB8fCBzeW1ib2wudG9TdHJpbmcoKS5zbGljZSg3LCAtMSk7XG59XG5cbmZ1bmN0aW9uIHNlcmlhbGl6ZVRva2VuKHRva2VuKSB7XG4gIGNvbnN0IHZhbHVlID0gc3ltYm9sVG9TdHIodG9rZW4pO1xuICBpZiAoL14oW2EtekEtWipdKShbISMkJSYnKitcXC0uXl9gfH5cXHc6L10qKSQvLnRlc3QodmFsdWUpID09PSBmYWxzZSkge1xuICAgIHRocm93IHNlcmlhbGl6ZUVycm9yKHZhbHVlLCBUT0tFTik7XG4gIH1cbiAgcmV0dXJuIHZhbHVlO1xufVxuXG4vLyA0LjEuMy4xLiAgU2VyaWFsaXppbmcgYSBCYXJlIEl0ZW1cbi8vXG4vLyBHaXZlbiBhbiBJdGVtIGFzIGlucHV0X2l0ZW0sIHJldHVybiBhbiBBU0NJSSBzdHJpbmcgc3VpdGFibGUgZm9yIHVzZVxuLy8gaW4gYSBIVFRQIGZpZWxkIHZhbHVlLlxuLy9cbi8vIDEuICBJZiBpbnB1dF9pdGVtIGlzIGFuIEludGVnZXIsIHJldHVybiB0aGUgcmVzdWx0IG9mIHJ1bm5pbmdcbi8vICAgICBTZXJpYWxpemluZyBhbiBJbnRlZ2VyIChTZWN0aW9uIDQuMS40KSB3aXRoIGlucHV0X2l0ZW0uXG4vL1xuLy8gMi4gIElmIGlucHV0X2l0ZW0gaXMgYSBEZWNpbWFsLCByZXR1cm4gdGhlIHJlc3VsdCBvZiBydW5uaW5nXG4vLyAgICAgU2VyaWFsaXppbmcgYSBEZWNpbWFsIChTZWN0aW9uIDQuMS41KSB3aXRoIGlucHV0X2l0ZW0uXG4vL1xuLy8gMy4gIElmIGlucHV0X2l0ZW0gaXMgYSBTdHJpbmcsIHJldHVybiB0aGUgcmVzdWx0IG9mIHJ1bm5pbmdcbi8vICAgICBTZXJpYWxpemluZyBhIFN0cmluZyAoU2VjdGlvbiA0LjEuNikgd2l0aCBpbnB1dF9pdGVtLlxuLy9cbi8vIDQuICBJZiBpbnB1dF9pdGVtIGlzIGEgVG9rZW4sIHJldHVybiB0aGUgcmVzdWx0IG9mIHJ1bm5pbmdcbi8vICAgICBTZXJpYWxpemluZyBhIFRva2VuIChTZWN0aW9uIDQuMS43KSB3aXRoIGlucHV0X2l0ZW0uXG4vL1xuLy8gNS4gIElmIGlucHV0X2l0ZW0gaXMgYSBCb29sZWFuLCByZXR1cm4gdGhlIHJlc3VsdCBvZiBydW5uaW5nXG4vLyAgICAgU2VyaWFsaXppbmcgYSBCb29sZWFuIChTZWN0aW9uIDQuMS45KSB3aXRoIGlucHV0X2l0ZW0uXG4vL1xuLy8gNi4gIElmIGlucHV0X2l0ZW0gaXMgYSBCeXRlIFNlcXVlbmNlLCByZXR1cm4gdGhlIHJlc3VsdCBvZiBydW5uaW5nXG4vLyAgICAgU2VyaWFsaXppbmcgYSBCeXRlIFNlcXVlbmNlIChTZWN0aW9uIDQuMS44KSB3aXRoIGlucHV0X2l0ZW0uXG4vL1xuLy8gNy4gIElmIGlucHV0X2l0ZW0gaXMgYSBEYXRlLCByZXR1cm4gdGhlIHJlc3VsdCBvZiBydW5uaW5nIFNlcmlhbGl6aW5nXG4vLyAgICAgYSBEYXRlIChTZWN0aW9uIDQuMS4xMCkgd2l0aCBpbnB1dF9pdGVtLlxuLy9cbi8vIDguICBPdGhlcndpc2UsIGZhaWwgc2VyaWFsaXphdGlvbi5cbmZ1bmN0aW9uIHNlcmlhbGl6ZUJhcmVJdGVtKHZhbHVlKSB7XG4gIHN3aXRjaCAodHlwZW9mIHZhbHVlKSB7XG4gICAgY2FzZSAnbnVtYmVyJzpcbiAgICAgIGlmICghaXNGaW5pdGVOdW1iZXIodmFsdWUpKSB7XG4gICAgICAgIHRocm93IHNlcmlhbGl6ZUVycm9yKHZhbHVlLCBCQVJFX0lURU0pO1xuICAgICAgfVxuICAgICAgaWYgKE51bWJlci5pc0ludGVnZXIodmFsdWUpKSB7XG4gICAgICAgIHJldHVybiBzZXJpYWxpemVJbnRlZ2VyKHZhbHVlKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBzZXJpYWxpemVEZWNpbWFsKHZhbHVlKTtcbiAgICBjYXNlICdzdHJpbmcnOlxuICAgICAgcmV0dXJuIHNlcmlhbGl6ZVN0cmluZyh2YWx1ZSk7XG4gICAgY2FzZSAnc3ltYm9sJzpcbiAgICAgIHJldHVybiBzZXJpYWxpemVUb2tlbih2YWx1ZSk7XG4gICAgY2FzZSAnYm9vbGVhbic6XG4gICAgICByZXR1cm4gc2VyaWFsaXplQm9vbGVhbih2YWx1ZSk7XG4gICAgY2FzZSAnb2JqZWN0JzpcbiAgICAgIGlmICh2YWx1ZSBpbnN0YW5jZW9mIERhdGUpIHtcbiAgICAgICAgcmV0dXJuIHNlcmlhbGl6ZURhdGUodmFsdWUpO1xuICAgICAgfVxuICAgICAgaWYgKHZhbHVlIGluc3RhbmNlb2YgVWludDhBcnJheSkge1xuICAgICAgICByZXR1cm4gc2VyaWFsaXplQnl0ZVNlcXVlbmNlKHZhbHVlKTtcbiAgICAgIH1cbiAgICAgIGlmICh2YWx1ZSBpbnN0YW5jZW9mIFNmVG9rZW4pIHtcbiAgICAgICAgcmV0dXJuIHNlcmlhbGl6ZVRva2VuKHZhbHVlKTtcbiAgICAgIH1cbiAgICBkZWZhdWx0OlxuICAgICAgLy8gZmFpbFxuICAgICAgdGhyb3cgc2VyaWFsaXplRXJyb3IodmFsdWUsIEJBUkVfSVRFTSk7XG4gIH1cbn1cblxuLy8gNC4xLjEuMy4gIFNlcmlhbGl6aW5nIGEgS2V5XG4vL1xuLy8gR2l2ZW4gYSBrZXkgYXMgaW5wdXRfa2V5LCByZXR1cm4gYW4gQVNDSUkgc3RyaW5nIHN1aXRhYmxlIGZvciB1c2UgaW5cbi8vIGEgSFRUUCBmaWVsZCB2YWx1ZS5cbi8vXG4vLyAxLiAgQ29udmVydCBpbnB1dF9rZXkgaW50byBhIHNlcXVlbmNlIG9mIEFTQ0lJIGNoYXJhY3RlcnM7IGlmXG4vLyAgICAgY29udmVyc2lvbiBmYWlscywgZmFpbCBzZXJpYWxpemF0aW9uLlxuLy9cbi8vIDIuICBJZiBpbnB1dF9rZXkgY29udGFpbnMgY2hhcmFjdGVycyBub3QgaW4gbGNhbHBoYSwgRElHSVQsIFwiX1wiLCBcIi1cIixcbi8vICAgICBcIi5cIiwgb3IgXCIqXCIgZmFpbCBzZXJpYWxpemF0aW9uLlxuLy9cbi8vIDMuICBJZiB0aGUgZmlyc3QgY2hhcmFjdGVyIG9mIGlucHV0X2tleSBpcyBub3QgbGNhbHBoYSBvciBcIipcIiwgZmFpbFxuLy8gICAgIHNlcmlhbGl6YXRpb24uXG4vL1xuLy8gNC4gIExldCBvdXRwdXQgYmUgYW4gZW1wdHkgc3RyaW5nLlxuLy9cbi8vIDUuICBBcHBlbmQgaW5wdXRfa2V5IHRvIG91dHB1dC5cbi8vXG4vLyA2LiAgUmV0dXJuIG91dHB1dC5cbmZ1bmN0aW9uIHNlcmlhbGl6ZUtleSh2YWx1ZSkge1xuICBpZiAoL15bYS16Kl1bYS16MC05XFwtXy4qXSokLy50ZXN0KHZhbHVlKSA9PT0gZmFsc2UpIHtcbiAgICB0aHJvdyBzZXJpYWxpemVFcnJvcih2YWx1ZSwgS0VZKTtcbiAgfVxuICByZXR1cm4gdmFsdWU7XG59XG5cbi8vIDQuMS4xLjIuICBTZXJpYWxpemluZyBQYXJhbWV0ZXJzXG4vL1xuLy8gR2l2ZW4gYW4gb3JkZXJlZCBEaWN0aW9uYXJ5IGFzIGlucHV0X3BhcmFtZXRlcnMgKGVhY2ggbWVtYmVyIGhhdmluZyBhXG4vLyBwYXJhbV9uYW1lIGFuZCBhIHBhcmFtX3ZhbHVlKSwgcmV0dXJuIGFuIEFTQ0lJIHN0cmluZyBzdWl0YWJsZSBmb3Jcbi8vIHVzZSBpbiBhIEhUVFAgZmllbGQgdmFsdWUuXG4vL1xuLy8gMS4gIExldCBvdXRwdXQgYmUgYW4gZW1wdHkgc3RyaW5nLlxuLy9cbi8vIDIuICBGb3IgZWFjaCBwYXJhbV9uYW1lIHdpdGggYSB2YWx1ZSBvZiBwYXJhbV92YWx1ZSBpblxuLy8gICAgIGlucHV0X3BhcmFtZXRlcnM6XG4vL1xuLy8gICAgIDEuICBBcHBlbmQgXCI7XCIgdG8gb3V0cHV0LlxuLy9cbi8vICAgICAyLiAgQXBwZW5kIHRoZSByZXN1bHQgb2YgcnVubmluZyBTZXJpYWxpemluZyBhIEtleVxuLy8gICAgICAgICAoU2VjdGlvbiA0LjEuMS4zKSB3aXRoIHBhcmFtX25hbWUgdG8gb3V0cHV0LlxuLy9cbi8vICAgICAzLiAgSWYgcGFyYW1fdmFsdWUgaXMgbm90IEJvb2xlYW4gdHJ1ZTpcbi8vXG4vLyAgICAgICAgIDEuICBBcHBlbmQgXCI9XCIgdG8gb3V0cHV0LlxuLy9cbi8vICAgICAgICAgMi4gIEFwcGVuZCB0aGUgcmVzdWx0IG9mIHJ1bm5pbmcgU2VyaWFsaXppbmcgYSBiYXJlIEl0ZW1cbi8vICAgICAgICAgICAgIChTZWN0aW9uIDQuMS4zLjEpIHdpdGggcGFyYW1fdmFsdWUgdG8gb3V0cHV0LlxuLy9cbi8vIDMuICBSZXR1cm4gb3V0cHV0LlxuZnVuY3Rpb24gc2VyaWFsaXplUGFyYW1zKHBhcmFtcykge1xuICBpZiAocGFyYW1zID09IG51bGwpIHtcbiAgICByZXR1cm4gJyc7XG4gIH1cbiAgcmV0dXJuIE9iamVjdC5lbnRyaWVzKHBhcmFtcykubWFwKChba2V5LCB2YWx1ZV0pID0+IHtcbiAgICBpZiAodmFsdWUgPT09IHRydWUpIHtcbiAgICAgIHJldHVybiBgOyR7c2VyaWFsaXplS2V5KGtleSl9YDsgLy8gb21pdCB0cnVlXG4gICAgfVxuICAgIHJldHVybiBgOyR7c2VyaWFsaXplS2V5KGtleSl9PSR7c2VyaWFsaXplQmFyZUl0ZW0odmFsdWUpfWA7XG4gIH0pLmpvaW4oJycpO1xufVxuXG4vLyA0LjEuMy4gIFNlcmlhbGl6aW5nIGFuIEl0ZW1cbi8vXG4vLyBHaXZlbiBhbiBJdGVtIGFzIGJhcmVfaXRlbSBhbmQgUGFyYW1ldGVycyBhcyBpdGVtX3BhcmFtZXRlcnMsIHJldHVyblxuLy8gYW4gQVNDSUkgc3RyaW5nIHN1aXRhYmxlIGZvciB1c2UgaW4gYSBIVFRQIGZpZWxkIHZhbHVlLlxuLy9cbi8vIDEuICBMZXQgb3V0cHV0IGJlIGFuIGVtcHR5IHN0cmluZy5cbi8vXG4vLyAyLiAgQXBwZW5kIHRoZSByZXN1bHQgb2YgcnVubmluZyBTZXJpYWxpemluZyBhIEJhcmUgSXRlbVxuLy8gICAgIFNlY3Rpb24gNC4xLjMuMSB3aXRoIGJhcmVfaXRlbSB0byBvdXRwdXQuXG4vL1xuLy8gMy4gIEFwcGVuZCB0aGUgcmVzdWx0IG9mIHJ1bm5pbmcgU2VyaWFsaXppbmcgUGFyYW1ldGVyc1xuLy8gICAgIFNlY3Rpb24gNC4xLjEuMiB3aXRoIGl0ZW1fcGFyYW1ldGVycyB0byBvdXRwdXQuXG4vL1xuLy8gNC4gIFJldHVybiBvdXRwdXQuXG5mdW5jdGlvbiBzZXJpYWxpemVJdGVtKHZhbHVlKSB7XG4gIGlmICh2YWx1ZSBpbnN0YW5jZW9mIFNmSXRlbSkge1xuICAgIHJldHVybiBgJHtzZXJpYWxpemVCYXJlSXRlbSh2YWx1ZS52YWx1ZSl9JHtzZXJpYWxpemVQYXJhbXModmFsdWUucGFyYW1zKX1gO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiBzZXJpYWxpemVCYXJlSXRlbSh2YWx1ZSk7XG4gIH1cbn1cblxuLy8gNC4xLjEuMS4gIFNlcmlhbGl6aW5nIGFuIElubmVyIExpc3Rcbi8vXG4vLyBHaXZlbiBhbiBhcnJheSBvZiAobWVtYmVyX3ZhbHVlLCBwYXJhbWV0ZXJzKSB0dXBsZXMgYXMgaW5uZXJfbGlzdCxcbi8vIGFuZCBwYXJhbWV0ZXJzIGFzIGxpc3RfcGFyYW1ldGVycywgcmV0dXJuIGFuIEFTQ0lJIHN0cmluZyBzdWl0YWJsZVxuLy8gZm9yIHVzZSBpbiBhIEhUVFAgZmllbGQgdmFsdWUuXG4vL1xuLy8gMS4gIExldCBvdXRwdXQgYmUgdGhlIHN0cmluZyBcIihcIi5cbi8vXG4vLyAyLiAgRm9yIGVhY2ggKG1lbWJlcl92YWx1ZSwgcGFyYW1ldGVycykgb2YgaW5uZXJfbGlzdDpcbi8vXG4vLyAgICAgMS4gIEFwcGVuZCB0aGUgcmVzdWx0IG9mIHJ1bm5pbmcgU2VyaWFsaXppbmcgYW4gSXRlbVxuLy8gICAgICAgICAoU2VjdGlvbiA0LjEuMykgd2l0aCAobWVtYmVyX3ZhbHVlLCBwYXJhbWV0ZXJzKSB0byBvdXRwdXQuXG4vL1xuLy8gICAgIDIuICBJZiBtb3JlIHZhbHVlcyByZW1haW4gaW4gaW5uZXJfbGlzdCwgYXBwZW5kIGEgc2luZ2xlIFNQIHRvXG4vLyAgICAgICAgIG91dHB1dC5cbi8vXG4vLyAzLiAgQXBwZW5kIFwiKVwiIHRvIG91dHB1dC5cbi8vXG4vLyA0LiAgQXBwZW5kIHRoZSByZXN1bHQgb2YgcnVubmluZyBTZXJpYWxpemluZyBQYXJhbWV0ZXJzXG4vLyAgICAgKFNlY3Rpb24gNC4xLjEuMikgd2l0aCBsaXN0X3BhcmFtZXRlcnMgdG8gb3V0cHV0LlxuLy9cbi8vIDUuICBSZXR1cm4gb3V0cHV0LlxuZnVuY3Rpb24gc2VyaWFsaXplSW5uZXJMaXN0KHZhbHVlKSB7XG4gIHJldHVybiBgKCR7dmFsdWUudmFsdWUubWFwKHNlcmlhbGl6ZUl0ZW0pLmpvaW4oJyAnKX0pJHtzZXJpYWxpemVQYXJhbXModmFsdWUucGFyYW1zKX1gO1xufVxuXG4vLyA0LjEuMi4gIFNlcmlhbGl6aW5nIGEgRGljdGlvbmFyeVxuLy9cbi8vIEdpdmVuIGFuIG9yZGVyZWQgRGljdGlvbmFyeSBhcyBpbnB1dF9kaWN0aW9uYXJ5IChlYWNoIG1lbWJlciBoYXZpbmcgYVxuLy8gbWVtYmVyX25hbWUgYW5kIGEgdHVwbGUgdmFsdWUgb2YgKG1lbWJlcl92YWx1ZSwgcGFyYW1ldGVycykpLCByZXR1cm5cbi8vIGFuIEFTQ0lJIHN0cmluZyBzdWl0YWJsZSBmb3IgdXNlIGluIGEgSFRUUCBmaWVsZCB2YWx1ZS5cbi8vXG4vLyAxLiAgTGV0IG91dHB1dCBiZSBhbiBlbXB0eSBzdHJpbmcuXG4vL1xuLy8gMi4gIEZvciBlYWNoIG1lbWJlcl9uYW1lIHdpdGggYSB2YWx1ZSBvZiAobWVtYmVyX3ZhbHVlLCBwYXJhbWV0ZXJzKVxuLy8gICAgIGluIGlucHV0X2RpY3Rpb25hcnk6XG4vL1xuLy8gICAgIDEuICBBcHBlbmQgdGhlIHJlc3VsdCBvZiBydW5uaW5nIFNlcmlhbGl6aW5nIGEgS2V5XG4vLyAgICAgICAgIChTZWN0aW9uIDQuMS4xLjMpIHdpdGggbWVtYmVyJ3MgbWVtYmVyX25hbWUgdG8gb3V0cHV0LlxuLy9cbi8vICAgICAyLiAgSWYgbWVtYmVyX3ZhbHVlIGlzIEJvb2xlYW4gdHJ1ZTpcbi8vXG4vLyAgICAgICAgIDEuICBBcHBlbmQgdGhlIHJlc3VsdCBvZiBydW5uaW5nIFNlcmlhbGl6aW5nIFBhcmFtZXRlcnNcbi8vICAgICAgICAgICAgIChTZWN0aW9uIDQuMS4xLjIpIHdpdGggcGFyYW1ldGVycyB0byBvdXRwdXQuXG4vL1xuLy8gICAgIDMuICBPdGhlcndpc2U6XG4vL1xuLy8gICAgICAgICAxLiAgQXBwZW5kIFwiPVwiIHRvIG91dHB1dC5cbi8vXG4vLyAgICAgICAgIDIuICBJZiBtZW1iZXJfdmFsdWUgaXMgYW4gYXJyYXksIGFwcGVuZCB0aGUgcmVzdWx0IG9mIHJ1bm5pbmdcbi8vICAgICAgICAgICAgIFNlcmlhbGl6aW5nIGFuIElubmVyIExpc3QgKFNlY3Rpb24gNC4xLjEuMSkgd2l0aFxuLy8gICAgICAgICAgICAgKG1lbWJlcl92YWx1ZSwgcGFyYW1ldGVycykgdG8gb3V0cHV0LlxuLy9cbi8vICAgICAgICAgMy4gIE90aGVyd2lzZSwgYXBwZW5kIHRoZSByZXN1bHQgb2YgcnVubmluZyBTZXJpYWxpemluZyBhblxuLy8gICAgICAgICAgICAgSXRlbSAoU2VjdGlvbiA0LjEuMykgd2l0aCAobWVtYmVyX3ZhbHVlLCBwYXJhbWV0ZXJzKSB0b1xuLy8gICAgICAgICAgICAgb3V0cHV0LlxuLy9cbi8vICAgICA0LiAgSWYgbW9yZSBtZW1iZXJzIHJlbWFpbiBpbiBpbnB1dF9kaWN0aW9uYXJ5OlxuLy9cbi8vICAgICAgICAgMS4gIEFwcGVuZCBcIixcIiB0byBvdXRwdXQuXG4vL1xuLy8gICAgICAgICAyLiAgQXBwZW5kIGEgc2luZ2xlIFNQIHRvIG91dHB1dC5cbi8vXG4vLyAzLiAgUmV0dXJuIG91dHB1dC5cbmZ1bmN0aW9uIHNlcmlhbGl6ZURpY3QoZGljdCwgb3B0aW9ucyA9IHtcbiAgd2hpdGVzcGFjZTogdHJ1ZVxufSkge1xuICBpZiAodHlwZW9mIGRpY3QgIT09ICdvYmplY3QnKSB7XG4gICAgdGhyb3cgc2VyaWFsaXplRXJyb3IoZGljdCwgRElDVCk7XG4gIH1cbiAgY29uc3QgZW50cmllcyA9IGRpY3QgaW5zdGFuY2VvZiBNYXAgPyBkaWN0LmVudHJpZXMoKSA6IE9iamVjdC5lbnRyaWVzKGRpY3QpO1xuICBjb25zdCBvcHRpb25hbFdoaXRlU3BhY2UgPSBvcHRpb25zICE9IG51bGwgJiYgb3B0aW9ucy53aGl0ZXNwYWNlID8gJyAnIDogJyc7XG4gIHJldHVybiBBcnJheS5mcm9tKGVudHJpZXMpLm1hcCgoW2tleSwgaXRlbV0pID0+IHtcbiAgICBpZiAoaXRlbSBpbnN0YW5jZW9mIFNmSXRlbSA9PT0gZmFsc2UpIHtcbiAgICAgIGl0ZW0gPSBuZXcgU2ZJdGVtKGl0ZW0pO1xuICAgIH1cbiAgICBsZXQgb3V0cHV0ID0gc2VyaWFsaXplS2V5KGtleSk7XG4gICAgaWYgKGl0ZW0udmFsdWUgPT09IHRydWUpIHtcbiAgICAgIG91dHB1dCArPSBzZXJpYWxpemVQYXJhbXMoaXRlbS5wYXJhbXMpO1xuICAgIH0gZWxzZSB7XG4gICAgICBvdXRwdXQgKz0gJz0nO1xuICAgICAgaWYgKEFycmF5LmlzQXJyYXkoaXRlbS52YWx1ZSkpIHtcbiAgICAgICAgb3V0cHV0ICs9IHNlcmlhbGl6ZUlubmVyTGlzdChpdGVtKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIG91dHB1dCArPSBzZXJpYWxpemVJdGVtKGl0ZW0pO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gb3V0cHV0O1xuICB9KS5qb2luKGAsJHtvcHRpb25hbFdoaXRlU3BhY2V9YCk7XG59XG5cbi8qKlxuICogRW5jb2RlIGFuIG9iamVjdCBpbnRvIGEgc3RydWN0dXJlZCBmaWVsZCBkaWN0aW9uYXJ5XG4gKlxuICogQHBhcmFtIGlucHV0IC0gVGhlIHN0cnVjdHVyZWQgZmllbGQgZGljdGlvbmFyeSB0byBlbmNvZGVcbiAqIEByZXR1cm5zIFRoZSBzdHJ1Y3R1cmVkIGZpZWxkIHN0cmluZ1xuICpcbiAqIEBncm91cCBTdHJ1Y3R1cmVkIEZpZWxkXG4gKlxuICogQGJldGFcbiAqL1xuZnVuY3Rpb24gZW5jb2RlU2ZEaWN0KHZhbHVlLCBvcHRpb25zKSB7XG4gIHJldHVybiBzZXJpYWxpemVEaWN0KHZhbHVlLCBvcHRpb25zKTtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIGtleSBpcyBhIHRva2VuIGZpZWxkLlxuICpcbiAqIEBwYXJhbSBrZXkgLSBUaGUga2V5IHRvIGNoZWNrLlxuICpcbiAqIEByZXR1cm5zIGB0cnVlYCBpZiB0aGUga2V5IGlzIGEgdG9rZW4gZmllbGQuXG4gKlxuICogQGludGVybmFsXG4gKlxuICogQGdyb3VwIENNQ0RcbiAqL1xuY29uc3QgaXNUb2tlbkZpZWxkID0ga2V5ID0+IGtleSA9PT0gJ290JyB8fCBrZXkgPT09ICdzZicgfHwga2V5ID09PSAnc3QnO1xuXG5jb25zdCBpc1ZhbGlkID0gdmFsdWUgPT4ge1xuICBpZiAodHlwZW9mIHZhbHVlID09PSAnbnVtYmVyJykge1xuICAgIHJldHVybiBpc0Zpbml0ZU51bWJlcih2YWx1ZSk7XG4gIH1cbiAgcmV0dXJuIHZhbHVlICE9IG51bGwgJiYgdmFsdWUgIT09ICcnICYmIHZhbHVlICE9PSBmYWxzZTtcbn07XG5cbi8qKlxuICogQ29uc3RydWN0cyBhIHJlbGF0aXZlIHBhdGggZnJvbSBhIFVSTC5cbiAqXG4gKiBAcGFyYW0gdXJsIC0gVGhlIGRlc3RpbmF0aW9uIFVSTFxuICogQHBhcmFtIGJhc2UgLSBUaGUgYmFzZSBVUkxcbiAqIEByZXR1cm5zIFRoZSByZWxhdGl2ZSBwYXRoXG4gKlxuICogQGdyb3VwIFV0aWxzXG4gKlxuICogQGJldGFcbiAqL1xuZnVuY3Rpb24gdXJsVG9SZWxhdGl2ZVBhdGgodXJsLCBiYXNlKSB7XG4gIGNvbnN0IHRvID0gbmV3IFVSTCh1cmwpO1xuICBjb25zdCBmcm9tID0gbmV3IFVSTChiYXNlKTtcbiAgaWYgKHRvLm9yaWdpbiAhPT0gZnJvbS5vcmlnaW4pIHtcbiAgICByZXR1cm4gdXJsO1xuICB9XG4gIGNvbnN0IHRvUGF0aCA9IHRvLnBhdGhuYW1lLnNwbGl0KCcvJykuc2xpY2UoMSk7XG4gIGNvbnN0IGZyb21QYXRoID0gZnJvbS5wYXRobmFtZS5zcGxpdCgnLycpLnNsaWNlKDEsIC0xKTtcbiAgLy8gcmVtb3ZlIGNvbW1vbiBwYXJlbnRzXG4gIHdoaWxlICh0b1BhdGhbMF0gPT09IGZyb21QYXRoWzBdKSB7XG4gICAgdG9QYXRoLnNoaWZ0KCk7XG4gICAgZnJvbVBhdGguc2hpZnQoKTtcbiAgfVxuICAvLyBhZGQgYmFjayBwYXRoc1xuICB3aGlsZSAoZnJvbVBhdGgubGVuZ3RoKSB7XG4gICAgZnJvbVBhdGguc2hpZnQoKTtcbiAgICB0b1BhdGgudW5zaGlmdCgnLi4nKTtcbiAgfVxuICByZXR1cm4gdG9QYXRoLmpvaW4oJy8nKTtcbn1cblxuLyoqXG4gKiBHZW5lcmF0ZSBhIHJhbmRvbSB2NCBVVUlEXG4gKlxuICogQHJldHVybnMgQSByYW5kb20gdjQgVVVJRFxuICpcbiAqIEBncm91cCBVdGlsc1xuICpcbiAqIEBiZXRhXG4gKi9cbmZ1bmN0aW9uIHV1aWQoKSB7XG4gIHRyeSB7XG4gICAgcmV0dXJuIGNyeXB0by5yYW5kb21VVUlEKCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHVybCA9IFVSTC5jcmVhdGVPYmplY3RVUkwobmV3IEJsb2IoKSk7XG4gICAgICBjb25zdCB1dWlkID0gdXJsLnRvU3RyaW5nKCk7XG4gICAgICBVUkwucmV2b2tlT2JqZWN0VVJMKHVybCk7XG4gICAgICByZXR1cm4gdXVpZC5zbGljZSh1dWlkLmxhc3RJbmRleE9mKCcvJykgKyAxKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbGV0IGR0ID0gbmV3IERhdGUoKS5nZXRUaW1lKCk7XG4gICAgICBjb25zdCB1dWlkID0gJ3h4eHh4eHh4LXh4eHgtNHh4eC15eHh4LXh4eHh4eHh4eHh4eCcucmVwbGFjZSgvW3h5XS9nLCBjID0+IHtcbiAgICAgICAgY29uc3QgciA9IChkdCArIE1hdGgucmFuZG9tKCkgKiAxNikgJSAxNiB8IDA7XG4gICAgICAgIGR0ID0gTWF0aC5mbG9vcihkdCAvIDE2KTtcbiAgICAgICAgcmV0dXJuIChjID09ICd4JyA/IHIgOiByICYgMHgzIHwgMHg4KS50b1N0cmluZygxNik7XG4gICAgICB9KTtcbiAgICAgIHJldHVybiB1dWlkO1xuICAgIH1cbiAgfVxufVxuXG5jb25zdCB0b1JvdW5kZWQgPSB2YWx1ZSA9PiBNYXRoLnJvdW5kKHZhbHVlKTtcbmNvbnN0IHRvVXJsU2FmZSA9ICh2YWx1ZSwgb3B0aW9ucykgPT4ge1xuICBpZiAob3B0aW9ucyAhPSBudWxsICYmIG9wdGlvbnMuYmFzZVVybCkge1xuICAgIHZhbHVlID0gdXJsVG9SZWxhdGl2ZVBhdGgodmFsdWUsIG9wdGlvbnMuYmFzZVVybCk7XG4gIH1cbiAgcmV0dXJuIGVuY29kZVVSSUNvbXBvbmVudCh2YWx1ZSk7XG59O1xuY29uc3QgdG9IdW5kcmVkID0gdmFsdWUgPT4gdG9Sb3VuZGVkKHZhbHVlIC8gMTAwKSAqIDEwMDtcbi8qKlxuICogVGhlIGRlZmF1bHQgZm9ybWF0dGVycyBmb3IgQ01DRCB2YWx1ZXMuXG4gKlxuICogQGdyb3VwIENNQ0RcbiAqXG4gKiBAYmV0YVxuICovXG5jb25zdCBDbWNkRm9ybWF0dGVycyA9IHtcbiAgLyoqXG4gICAqIEJpdHJhdGUgKGticHMpIHJvdW5kZWQgaW50ZWdlclxuICAgKi9cbiAgYnI6IHRvUm91bmRlZCxcbiAgLyoqXG4gICAqIER1cmF0aW9uIChtaWxsaXNlY29uZHMpIHJvdW5kZWQgaW50ZWdlclxuICAgKi9cbiAgZDogdG9Sb3VuZGVkLFxuICAvKipcbiAgICogQnVmZmVyIExlbmd0aCAobWlsbGlzZWNvbmRzKSByb3VuZGVkIG5lYXJlc3QgMTAwbXNcbiAgICovXG4gIGJsOiB0b0h1bmRyZWQsXG4gIC8qKlxuICAgKiBEZWFkbGluZSAobWlsbGlzZWNvbmRzKSByb3VuZGVkIG5lYXJlc3QgMTAwbXNcbiAgICovXG4gIGRsOiB0b0h1bmRyZWQsXG4gIC8qKlxuICAgKiBNZWFzdXJlZCBUaHJvdWdocHV0IChrYnBzKSByb3VuZGVkIG5lYXJlc3QgMTAwa2Jwc1xuICAgKi9cbiAgbXRwOiB0b0h1bmRyZWQsXG4gIC8qKlxuICAgKiBOZXh0IE9iamVjdCBSZXF1ZXN0IFVSTCBlbmNvZGVkXG4gICAqL1xuICBub3I6IHRvVXJsU2FmZSxcbiAgLyoqXG4gICAqIFJlcXVlc3RlZCBtYXhpbXVtIHRocm91Z2hwdXQgKGticHMpIHJvdW5kZWQgbmVhcmVzdCAxMDBrYnBzXG4gICAqL1xuICBydHA6IHRvSHVuZHJlZCxcbiAgLyoqXG4gICAqIFRvcCBCaXRyYXRlIChrYnBzKSByb3VuZGVkIGludGVnZXJcbiAgICovXG4gIHRiOiB0b1JvdW5kZWRcbn07XG5cbi8qKlxuICogSW50ZXJuYWwgQ01DRCBwcm9jZXNzaW5nIGZ1bmN0aW9uLlxuICpcbiAqIEBwYXJhbSBvYmogLSBUaGUgQ01DRCBvYmplY3QgdG8gcHJvY2Vzcy5cbiAqIEBwYXJhbSBtYXAgLSBUaGUgbWFwcGluZyBmdW5jdGlvbiB0byB1c2UuXG4gKiBAcGFyYW0gb3B0aW9ucyAtIE9wdGlvbnMgZm9yIGVuY29kaW5nLlxuICpcbiAqIEBpbnRlcm5hbFxuICpcbiAqIEBncm91cCBDTUNEXG4gKi9cbmZ1bmN0aW9uIHByb2Nlc3NDbWNkKG9iaiwgb3B0aW9ucykge1xuICBjb25zdCByZXN1bHRzID0ge307XG4gIGlmIChvYmogPT0gbnVsbCB8fCB0eXBlb2Ygb2JqICE9PSAnb2JqZWN0Jykge1xuICAgIHJldHVybiByZXN1bHRzO1xuICB9XG4gIGNvbnN0IGtleXMgPSBPYmplY3Qua2V5cyhvYmopLnNvcnQoKTtcbiAgY29uc3QgZm9ybWF0dGVycyA9IF9leHRlbmRzKHt9LCBDbWNkRm9ybWF0dGVycywgb3B0aW9ucyA9PSBudWxsID8gdm9pZCAwIDogb3B0aW9ucy5mb3JtYXR0ZXJzKTtcbiAgY29uc3QgZmlsdGVyID0gb3B0aW9ucyA9PSBudWxsID8gdm9pZCAwIDogb3B0aW9ucy5maWx0ZXI7XG4gIGtleXMuZm9yRWFjaChrZXkgPT4ge1xuICAgIGlmIChmaWx0ZXIgIT0gbnVsbCAmJiBmaWx0ZXIoa2V5KSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBsZXQgdmFsdWUgPSBvYmpba2V5XTtcbiAgICBjb25zdCBmb3JtYXR0ZXIgPSBmb3JtYXR0ZXJzW2tleV07XG4gICAgaWYgKGZvcm1hdHRlcikge1xuICAgICAgdmFsdWUgPSBmb3JtYXR0ZXIodmFsdWUsIG9wdGlvbnMpO1xuICAgIH1cbiAgICAvLyBWZXJzaW9uIHNob3VsZCBvbmx5IGJlIHJlcG9ydGVkIGlmIG5vdCBlcXVhbCB0byAxLlxuICAgIGlmIChrZXkgPT09ICd2JyAmJiB2YWx1ZSA9PT0gMSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICAvLyBQbGF5YmFjayByYXRlIHNob3VsZCBvbmx5IGJlIHNlbnQgaWYgbm90IGVxdWFsIHRvIDEuXG4gICAgaWYgKGtleSA9PSAncHInICYmIHZhbHVlID09PSAxKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIC8vIGlnbm9yZSBpbnZhbGlkIHZhbHVlc1xuICAgIGlmICghaXNWYWxpZCh2YWx1ZSkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKGlzVG9rZW5GaWVsZChrZXkpICYmIHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycpIHtcbiAgICAgIHZhbHVlID0gbmV3IFNmVG9rZW4odmFsdWUpO1xuICAgIH1cbiAgICByZXN1bHRzW2tleV0gPSB2YWx1ZTtcbiAgfSk7XG4gIHJldHVybiByZXN1bHRzO1xufVxuXG4vKipcbiAqIEVuY29kZSBhIENNQ0Qgb2JqZWN0IHRvIGEgc3RyaW5nLlxuICpcbiAqIEBwYXJhbSBjbWNkIC0gVGhlIENNQ0Qgb2JqZWN0IHRvIGVuY29kZS5cbiAqIEBwYXJhbSBvcHRpb25zIC0gT3B0aW9ucyBmb3IgZW5jb2RpbmcuXG4gKlxuICogQHJldHVybnMgVGhlIGVuY29kZWQgQ01DRCBzdHJpbmcuXG4gKlxuICogQGdyb3VwIENNQ0RcbiAqXG4gKiBAYmV0YVxuICovXG5mdW5jdGlvbiBlbmNvZGVDbWNkKGNtY2QsIG9wdGlvbnMgPSB7fSkge1xuICBpZiAoIWNtY2QpIHtcbiAgICByZXR1cm4gJyc7XG4gIH1cbiAgcmV0dXJuIGVuY29kZVNmRGljdChwcm9jZXNzQ21jZChjbWNkLCBvcHRpb25zKSwgX2V4dGVuZHMoe1xuICAgIHdoaXRlc3BhY2U6IGZhbHNlXG4gIH0sIG9wdGlvbnMpKTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0IGEgQ01DRCBkYXRhIG9iamVjdCB0byByZXF1ZXN0IGhlYWRlcnNcbiAqXG4gKiBAcGFyYW0gY21jZCAtIFRoZSBDTUNEIGRhdGEgb2JqZWN0IHRvIGNvbnZlcnQuXG4gKiBAcGFyYW0gb3B0aW9ucyAtIE9wdGlvbnMgZm9yIGVuY29kaW5nIHRoZSBDTUNEIG9iamVjdC5cbiAqXG4gKiBAcmV0dXJucyBUaGUgQ01DRCBoZWFkZXIgc2hhcmRzLlxuICpcbiAqIEBncm91cCBDTUNEXG4gKlxuICogQGJldGFcbiAqL1xuZnVuY3Rpb24gdG9DbWNkSGVhZGVycyhjbWNkLCBvcHRpb25zID0ge30pIHtcbiAgaWYgKCFjbWNkKSB7XG4gICAgcmV0dXJuIHt9O1xuICB9XG4gIGNvbnN0IGVudHJpZXMgPSBPYmplY3QuZW50cmllcyhjbWNkKTtcbiAgY29uc3QgaGVhZGVyTWFwID0gT2JqZWN0LmVudHJpZXMoQ21jZEhlYWRlck1hcCkuY29uY2F0KE9iamVjdC5lbnRyaWVzKChvcHRpb25zID09IG51bGwgPyB2b2lkIDAgOiBvcHRpb25zLmN1c3RvbUhlYWRlck1hcCkgfHwge30pKTtcbiAgY29uc3Qgc2hhcmRzID0gZW50cmllcy5yZWR1Y2UoKGFjYywgZW50cnkpID0+IHtcbiAgICB2YXIgX2hlYWRlck1hcCRmaW5kLCBfYWNjJGZpZWxkO1xuICAgIGNvbnN0IFtrZXksIHZhbHVlXSA9IGVudHJ5O1xuICAgIGNvbnN0IGZpZWxkID0gKChfaGVhZGVyTWFwJGZpbmQgPSBoZWFkZXJNYXAuZmluZChlbnRyeSA9PiBlbnRyeVsxXS5pbmNsdWRlcyhrZXkpKSkgPT0gbnVsbCA/IHZvaWQgMCA6IF9oZWFkZXJNYXAkZmluZFswXSkgfHwgQ21jZEhlYWRlckZpZWxkLlJFUVVFU1Q7XG4gICAgKF9hY2MkZmllbGQgPSBhY2NbZmllbGRdKSAhPSBudWxsID8gX2FjYyRmaWVsZCA6IGFjY1tmaWVsZF0gPSB7fTtcbiAgICBhY2NbZmllbGRdW2tleV0gPSB2YWx1ZTtcbiAgICByZXR1cm4gYWNjO1xuICB9LCB7fSk7XG4gIHJldHVybiBPYmplY3QuZW50cmllcyhzaGFyZHMpLnJlZHVjZSgoYWNjLCBbZmllbGQsIHZhbHVlXSkgPT4ge1xuICAgIGFjY1tmaWVsZF0gPSBlbmNvZGVDbWNkKHZhbHVlLCBvcHRpb25zKTtcbiAgICByZXR1cm4gYWNjO1xuICB9LCB7fSk7XG59XG5cbi8qKlxuICogQXBwZW5kIENNQ0QgcXVlcnkgYXJncyB0byBhIGhlYWRlciBvYmplY3QuXG4gKlxuICogQHBhcmFtIGhlYWRlcnMgLSBUaGUgaGVhZGVycyB0byBhcHBlbmQgdG8uXG4gKiBAcGFyYW0gY21jZCAtIFRoZSBDTUNEIG9iamVjdCB0byBhcHBlbmQuXG4gKiBAcGFyYW0gY3VzdG9tSGVhZGVyTWFwIC0gQSBtYXAgb2YgY3VzdG9tIENNQ0Qga2V5cyB0byBoZWFkZXIgZmllbGRzLlxuICpcbiAqIEByZXR1cm5zIFRoZSBoZWFkZXJzIHdpdGggdGhlIENNQ0QgaGVhZGVyIHNoYXJkcyBhcHBlbmRlZC5cbiAqXG4gKiBAZ3JvdXAgQ01DRFxuICpcbiAqIEBiZXRhXG4gKi9cbmZ1bmN0aW9uIGFwcGVuZENtY2RIZWFkZXJzKGhlYWRlcnMsIGNtY2QsIG9wdGlvbnMpIHtcbiAgcmV0dXJuIF9leHRlbmRzKGhlYWRlcnMsIHRvQ21jZEhlYWRlcnMoY21jZCwgb3B0aW9ucykpO1xufVxuXG4vKipcbiAqIENNQ0QgcGFyYW1ldGVyIG5hbWUuXG4gKlxuICogQGdyb3VwIENNQ0RcbiAqXG4gKiBAYmV0YVxuICovXG5jb25zdCBDTUNEX1BBUkFNID0gJ0NNQ0QnO1xuXG4vKipcbiAqIENvbnZlcnQgYSBDTUNEIGRhdGEgb2JqZWN0IHRvIGEgcXVlcnkgYXJnLlxuICpcbiAqIEBwYXJhbSBjbWNkIC0gVGhlIENNQ0Qgb2JqZWN0IHRvIGNvbnZlcnQuXG4gKiBAcGFyYW0gb3B0aW9ucyAtIE9wdGlvbnMgZm9yIGVuY29kaW5nIHRoZSBDTUNEIG9iamVjdC5cbiAqXG4gKiBAcmV0dXJucyBUaGUgQ01DRCBxdWVyeSBhcmcuXG4gKlxuICogQGdyb3VwIENNQ0RcbiAqXG4gKiBAYmV0YVxuICovXG5mdW5jdGlvbiB0b0NtY2RRdWVyeShjbWNkLCBvcHRpb25zID0ge30pIHtcbiAgaWYgKCFjbWNkKSB7XG4gICAgcmV0dXJuICcnO1xuICB9XG4gIGNvbnN0IHBhcmFtcyA9IGVuY29kZUNtY2QoY21jZCwgb3B0aW9ucyk7XG4gIHJldHVybiBgJHtDTUNEX1BBUkFNfT0ke2VuY29kZVVSSUNvbXBvbmVudChwYXJhbXMpfWA7XG59XG5cbmNvbnN0IFJFR0VYID0gL0NNQ0Q9W14mI10rLztcbi8qKlxuICogQXBwZW5kIENNQ0QgcXVlcnkgYXJncyB0byBhIFVSTC5cbiAqXG4gKiBAcGFyYW0gdXJsIC0gVGhlIFVSTCB0byBhcHBlbmQgdG8uXG4gKiBAcGFyYW0gY21jZCAtIFRoZSBDTUNEIG9iamVjdCB0byBhcHBlbmQuXG4gKiBAcGFyYW0gb3B0aW9ucyAtIE9wdGlvbnMgZm9yIGVuY29kaW5nIHRoZSBDTUNEIG9iamVjdC5cbiAqXG4gKiBAcmV0dXJucyBUaGUgVVJMIHdpdGggdGhlIENNQ0QgcXVlcnkgYXJncyBhcHBlbmRlZC5cbiAqXG4gKiBAZ3JvdXAgQ01DRFxuICpcbiAqIEBiZXRhXG4gKi9cbmZ1bmN0aW9uIGFwcGVuZENtY2RRdWVyeSh1cmwsIGNtY2QsIG9wdGlvbnMpIHtcbiAgLy8gVE9ETzogUmVwbGFjZSB3aXRoIFVSTFNlYXJjaFBhcmFtcyBvbmNlIHdlIGRyb3AgU2FmYXJpIDwgMTAuMSAmIENocm9tZSA8IDQ5IHN1cHBvcnQuXG4gIC8vIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9VUkxTZWFyY2hQYXJhbXNcbiAgY29uc3QgcXVlcnkgPSB0b0NtY2RRdWVyeShjbWNkLCBvcHRpb25zKTtcbiAgaWYgKCFxdWVyeSkge1xuICAgIHJldHVybiB1cmw7XG4gIH1cbiAgaWYgKFJFR0VYLnRlc3QodXJsKSkge1xuICAgIHJldHVybiB1cmwucmVwbGFjZShSRUdFWCwgcXVlcnkpO1xuICB9XG4gIGNvbnN0IHNlcGFyYXRvciA9IHVybC5pbmNsdWRlcygnPycpID8gJyYnIDogJz8nO1xuICByZXR1cm4gYCR7dXJsfSR7c2VwYXJhdG9yfSR7cXVlcnl9YDtcbn1cblxuLyoqXG4gKiBDb250cm9sbGVyIHRvIGRlYWwgd2l0aCBDb21tb24gTWVkaWEgQ2xpZW50IERhdGEgKENNQ0QpXG4gKiBAc2VlIGh0dHBzOi8vY2RuLmN0YS50ZWNoL2N0YS9tZWRpYS9tZWRpYS9yZXNvdXJjZXMvc3RhbmRhcmRzL3BkZnMvY3RhLTUwMDQtZmluYWwucGRmXG4gKi9cbmNsYXNzIENNQ0RDb250cm9sbGVyIHtcbiAgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuby1yZXN0cmljdGVkLWdsb2JhbHNcblxuICBjb25zdHJ1Y3RvcihobHMpIHtcbiAgICB0aGlzLmhscyA9IHZvaWQgMDtcbiAgICB0aGlzLmNvbmZpZyA9IHZvaWQgMDtcbiAgICB0aGlzLm1lZGlhID0gdm9pZCAwO1xuICAgIHRoaXMuc2lkID0gdm9pZCAwO1xuICAgIHRoaXMuY2lkID0gdm9pZCAwO1xuICAgIHRoaXMudXNlSGVhZGVycyA9IGZhbHNlO1xuICAgIHRoaXMuaW5jbHVkZUtleXMgPSB2b2lkIDA7XG4gICAgdGhpcy5pbml0aWFsaXplZCA9IGZhbHNlO1xuICAgIHRoaXMuc3RhcnZlZCA9IGZhbHNlO1xuICAgIHRoaXMuYnVmZmVyaW5nID0gdHJ1ZTtcbiAgICB0aGlzLmF1ZGlvQnVmZmVyID0gdm9pZCAwO1xuICAgIC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tcmVzdHJpY3RlZC1nbG9iYWxzXG4gICAgdGhpcy52aWRlb0J1ZmZlciA9IHZvaWQgMDtcbiAgICB0aGlzLm9uV2FpdGluZyA9ICgpID0+IHtcbiAgICAgIGlmICh0aGlzLmluaXRpYWxpemVkKSB7XG4gICAgICAgIHRoaXMuc3RhcnZlZCA9IHRydWU7XG4gICAgICB9XG4gICAgICB0aGlzLmJ1ZmZlcmluZyA9IHRydWU7XG4gICAgfTtcbiAgICB0aGlzLm9uUGxheWluZyA9ICgpID0+IHtcbiAgICAgIGlmICghdGhpcy5pbml0aWFsaXplZCkge1xuICAgICAgICB0aGlzLmluaXRpYWxpemVkID0gdHJ1ZTtcbiAgICAgIH1cbiAgICAgIHRoaXMuYnVmZmVyaW5nID0gZmFsc2U7XG4gICAgfTtcbiAgICAvKipcbiAgICAgKiBBcHBseSBDTUNEIGRhdGEgdG8gYSBtYW5pZmVzdCByZXF1ZXN0LlxuICAgICAqL1xuICAgIHRoaXMuYXBwbHlQbGF5bGlzdERhdGEgPSBjb250ZXh0ID0+IHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHRoaXMuYXBwbHkoY29udGV4dCwge1xuICAgICAgICAgIG90OiBDbU9iamVjdFR5cGUuTUFOSUZFU1QsXG4gICAgICAgICAgc3U6ICF0aGlzLmluaXRpYWxpemVkXG4gICAgICAgIH0pO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oJ0NvdWxkIG5vdCBnZW5lcmF0ZSBtYW5pZmVzdCBDTUNEIGRhdGEuJywgZXJyb3IpO1xuICAgICAgfVxuICAgIH07XG4gICAgLyoqXG4gICAgICogQXBwbHkgQ01DRCBkYXRhIHRvIGEgc2VnbWVudCByZXF1ZXN0XG4gICAgICovXG4gICAgdGhpcy5hcHBseUZyYWdtZW50RGF0YSA9IGNvbnRleHQgPT4ge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgZnJhZ21lbnQgPSBjb250ZXh0LmZyYWc7XG4gICAgICAgIGNvbnN0IGxldmVsID0gdGhpcy5obHMubGV2ZWxzW2ZyYWdtZW50LmxldmVsXTtcbiAgICAgICAgY29uc3Qgb3QgPSB0aGlzLmdldE9iamVjdFR5cGUoZnJhZ21lbnQpO1xuICAgICAgICBjb25zdCBkYXRhID0ge1xuICAgICAgICAgIGQ6IGZyYWdtZW50LmR1cmF0aW9uICogMTAwMCxcbiAgICAgICAgICBvdFxuICAgICAgICB9O1xuICAgICAgICBpZiAob3QgPT09IENtT2JqZWN0VHlwZS5WSURFTyB8fCBvdCA9PT0gQ21PYmplY3RUeXBlLkFVRElPIHx8IG90ID09IENtT2JqZWN0VHlwZS5NVVhFRCkge1xuICAgICAgICAgIGRhdGEuYnIgPSBsZXZlbC5iaXRyYXRlIC8gMTAwMDtcbiAgICAgICAgICBkYXRhLnRiID0gdGhpcy5nZXRUb3BCYW5kd2lkdGgob3QpIC8gMTAwMDtcbiAgICAgICAgICBkYXRhLmJsID0gdGhpcy5nZXRCdWZmZXJMZW5ndGgob3QpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuYXBwbHkoY29udGV4dCwgZGF0YSk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBsb2dnZXIud2FybignQ291bGQgbm90IGdlbmVyYXRlIHNlZ21lbnQgQ01DRCBkYXRhLicsIGVycm9yKTtcbiAgICAgIH1cbiAgICB9O1xuICAgIHRoaXMuaGxzID0gaGxzO1xuICAgIGNvbnN0IGNvbmZpZyA9IHRoaXMuY29uZmlnID0gaGxzLmNvbmZpZztcbiAgICBjb25zdCB7XG4gICAgICBjbWNkXG4gICAgfSA9IGNvbmZpZztcbiAgICBpZiAoY21jZCAhPSBudWxsKSB7XG4gICAgICBjb25maWcucExvYWRlciA9IHRoaXMuY3JlYXRlUGxheWxpc3RMb2FkZXIoKTtcbiAgICAgIGNvbmZpZy5mTG9hZGVyID0gdGhpcy5jcmVhdGVGcmFnbWVudExvYWRlcigpO1xuICAgICAgdGhpcy5zaWQgPSBjbWNkLnNlc3Npb25JZCB8fCB1dWlkKCk7XG4gICAgICB0aGlzLmNpZCA9IGNtY2QuY29udGVudElkO1xuICAgICAgdGhpcy51c2VIZWFkZXJzID0gY21jZC51c2VIZWFkZXJzID09PSB0cnVlO1xuICAgICAgdGhpcy5pbmNsdWRlS2V5cyA9IGNtY2QuaW5jbHVkZUtleXM7XG4gICAgICB0aGlzLnJlZ2lzdGVyTGlzdGVuZXJzKCk7XG4gICAgfVxuICB9XG4gIHJlZ2lzdGVyTGlzdGVuZXJzKCkge1xuICAgIGNvbnN0IGhscyA9IHRoaXMuaGxzO1xuICAgIGhscy5vbihFdmVudHMuTUVESUFfQVRUQUNIRUQsIHRoaXMub25NZWRpYUF0dGFjaGVkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLk1FRElBX0RFVEFDSEVELCB0aGlzLm9uTWVkaWFEZXRhY2hlZCwgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5CVUZGRVJfQ1JFQVRFRCwgdGhpcy5vbkJ1ZmZlckNyZWF0ZWQsIHRoaXMpO1xuICB9XG4gIHVucmVnaXN0ZXJMaXN0ZW5lcnMoKSB7XG4gICAgY29uc3QgaGxzID0gdGhpcy5obHM7XG4gICAgaGxzLm9mZihFdmVudHMuTUVESUFfQVRUQUNIRUQsIHRoaXMub25NZWRpYUF0dGFjaGVkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5NRURJQV9ERVRBQ0hFRCwgdGhpcy5vbk1lZGlhRGV0YWNoZWQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkJVRkZFUl9DUkVBVEVELCB0aGlzLm9uQnVmZmVyQ3JlYXRlZCwgdGhpcyk7XG4gIH1cbiAgZGVzdHJveSgpIHtcbiAgICB0aGlzLnVucmVnaXN0ZXJMaXN0ZW5lcnMoKTtcbiAgICB0aGlzLm9uTWVkaWFEZXRhY2hlZCgpO1xuXG4gICAgLy8gQHRzLWlnbm9yZVxuICAgIHRoaXMuaGxzID0gdGhpcy5jb25maWcgPSB0aGlzLmF1ZGlvQnVmZmVyID0gdGhpcy52aWRlb0J1ZmZlciA9IG51bGw7XG4gICAgLy8gQHRzLWlnbm9yZVxuICAgIHRoaXMub25XYWl0aW5nID0gdGhpcy5vblBsYXlpbmcgPSBudWxsO1xuICB9XG4gIG9uTWVkaWFBdHRhY2hlZChldmVudCwgZGF0YSkge1xuICAgIHRoaXMubWVkaWEgPSBkYXRhLm1lZGlhO1xuICAgIHRoaXMubWVkaWEuYWRkRXZlbnRMaXN0ZW5lcignd2FpdGluZycsIHRoaXMub25XYWl0aW5nKTtcbiAgICB0aGlzLm1lZGlhLmFkZEV2ZW50TGlzdGVuZXIoJ3BsYXlpbmcnLCB0aGlzLm9uUGxheWluZyk7XG4gIH1cbiAgb25NZWRpYURldGFjaGVkKCkge1xuICAgIGlmICghdGhpcy5tZWRpYSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLm1lZGlhLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3dhaXRpbmcnLCB0aGlzLm9uV2FpdGluZyk7XG4gICAgdGhpcy5tZWRpYS5yZW1vdmVFdmVudExpc3RlbmVyKCdwbGF5aW5nJywgdGhpcy5vblBsYXlpbmcpO1xuXG4gICAgLy8gQHRzLWlnbm9yZVxuICAgIHRoaXMubWVkaWEgPSBudWxsO1xuICB9XG4gIG9uQnVmZmVyQ3JlYXRlZChldmVudCwgZGF0YSkge1xuICAgIHZhciBfZGF0YSR0cmFja3MkYXVkaW8sIF9kYXRhJHRyYWNrcyR2aWRlbztcbiAgICB0aGlzLmF1ZGlvQnVmZmVyID0gKF9kYXRhJHRyYWNrcyRhdWRpbyA9IGRhdGEudHJhY2tzLmF1ZGlvKSA9PSBudWxsID8gdm9pZCAwIDogX2RhdGEkdHJhY2tzJGF1ZGlvLmJ1ZmZlcjtcbiAgICB0aGlzLnZpZGVvQnVmZmVyID0gKF9kYXRhJHRyYWNrcyR2aWRlbyA9IGRhdGEudHJhY2tzLnZpZGVvKSA9PSBudWxsID8gdm9pZCAwIDogX2RhdGEkdHJhY2tzJHZpZGVvLmJ1ZmZlcjtcbiAgfVxuICAvKipcbiAgICogQ3JlYXRlIGJhc2VsaW5lIENNQ0QgZGF0YVxuICAgKi9cbiAgY3JlYXRlRGF0YSgpIHtcbiAgICB2YXIgX3RoaXMkbWVkaWE7XG4gICAgcmV0dXJuIHtcbiAgICAgIHY6IDEsXG4gICAgICBzZjogQ21TdHJlYW1pbmdGb3JtYXQuSExTLFxuICAgICAgc2lkOiB0aGlzLnNpZCxcbiAgICAgIGNpZDogdGhpcy5jaWQsXG4gICAgICBwcjogKF90aGlzJG1lZGlhID0gdGhpcy5tZWRpYSkgPT0gbnVsbCA/IHZvaWQgMCA6IF90aGlzJG1lZGlhLnBsYXliYWNrUmF0ZSxcbiAgICAgIG10cDogdGhpcy5obHMuYmFuZHdpZHRoRXN0aW1hdGUgLyAxMDAwXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBcHBseSBDTUNEIGRhdGEgdG8gYSByZXF1ZXN0LlxuICAgKi9cbiAgYXBwbHkoY29udGV4dCwgZGF0YSA9IHt9KSB7XG4gICAgLy8gYXBwbHkgYmFzZWxpbmUgZGF0YVxuICAgIF9leHRlbmRzKGRhdGEsIHRoaXMuY3JlYXRlRGF0YSgpKTtcbiAgICBjb25zdCBpc1ZpZGVvID0gZGF0YS5vdCA9PT0gQ21PYmplY3RUeXBlLklOSVQgfHwgZGF0YS5vdCA9PT0gQ21PYmplY3RUeXBlLlZJREVPIHx8IGRhdGEub3QgPT09IENtT2JqZWN0VHlwZS5NVVhFRDtcbiAgICBpZiAodGhpcy5zdGFydmVkICYmIGlzVmlkZW8pIHtcbiAgICAgIGRhdGEuYnMgPSB0cnVlO1xuICAgICAgZGF0YS5zdSA9IHRydWU7XG4gICAgICB0aGlzLnN0YXJ2ZWQgPSBmYWxzZTtcbiAgICB9XG4gICAgaWYgKGRhdGEuc3UgPT0gbnVsbCkge1xuICAgICAgZGF0YS5zdSA9IHRoaXMuYnVmZmVyaW5nO1xuICAgIH1cblxuICAgIC8vIFRPRE86IEltcGxlbWVudCBydHAsIG5yciwgbm9yLCBkbFxuXG4gICAgY29uc3Qge1xuICAgICAgaW5jbHVkZUtleXNcbiAgICB9ID0gdGhpcztcbiAgICBpZiAoaW5jbHVkZUtleXMpIHtcbiAgICAgIGRhdGEgPSBPYmplY3Qua2V5cyhkYXRhKS5yZWR1Y2UoKGFjYywga2V5KSA9PiB7XG4gICAgICAgIGluY2x1ZGVLZXlzLmluY2x1ZGVzKGtleSkgJiYgKGFjY1trZXldID0gZGF0YVtrZXldKTtcbiAgICAgICAgcmV0dXJuIGFjYztcbiAgICAgIH0sIHt9KTtcbiAgICB9XG4gICAgaWYgKHRoaXMudXNlSGVhZGVycykge1xuICAgICAgaWYgKCFjb250ZXh0LmhlYWRlcnMpIHtcbiAgICAgICAgY29udGV4dC5oZWFkZXJzID0ge307XG4gICAgICB9XG4gICAgICBhcHBlbmRDbWNkSGVhZGVycyhjb250ZXh0LmhlYWRlcnMsIGRhdGEpO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb250ZXh0LnVybCA9IGFwcGVuZENtY2RRdWVyeShjb250ZXh0LnVybCwgZGF0YSk7XG4gICAgfVxuICB9XG4gIC8qKlxuICAgKiBUaGUgQ01DRCBvYmplY3QgdHlwZS5cbiAgICovXG4gIGdldE9iamVjdFR5cGUoZnJhZ21lbnQpIHtcbiAgICBjb25zdCB7XG4gICAgICB0eXBlXG4gICAgfSA9IGZyYWdtZW50O1xuICAgIGlmICh0eXBlID09PSAnc3VidGl0bGUnKSB7XG4gICAgICByZXR1cm4gQ21PYmplY3RUeXBlLlRJTUVEX1RFWFQ7XG4gICAgfVxuICAgIGlmIChmcmFnbWVudC5zbiA9PT0gJ2luaXRTZWdtZW50Jykge1xuICAgICAgcmV0dXJuIENtT2JqZWN0VHlwZS5JTklUO1xuICAgIH1cbiAgICBpZiAodHlwZSA9PT0gJ2F1ZGlvJykge1xuICAgICAgcmV0dXJuIENtT2JqZWN0VHlwZS5BVURJTztcbiAgICB9XG4gICAgaWYgKHR5cGUgPT09ICdtYWluJykge1xuICAgICAgaWYgKCF0aGlzLmhscy5hdWRpb1RyYWNrcy5sZW5ndGgpIHtcbiAgICAgICAgcmV0dXJuIENtT2JqZWN0VHlwZS5NVVhFRDtcbiAgICAgIH1cbiAgICAgIHJldHVybiBDbU9iamVjdFR5cGUuVklERU87XG4gICAgfVxuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBoaWdoZXN0IGJpdHJhdGUuXG4gICAqL1xuICBnZXRUb3BCYW5kd2lkdGgodHlwZSkge1xuICAgIGxldCBiaXRyYXRlID0gMDtcbiAgICBsZXQgbGV2ZWxzO1xuICAgIGNvbnN0IGhscyA9IHRoaXMuaGxzO1xuICAgIGlmICh0eXBlID09PSBDbU9iamVjdFR5cGUuQVVESU8pIHtcbiAgICAgIGxldmVscyA9IGhscy5hdWRpb1RyYWNrcztcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgbWF4ID0gaGxzLm1heEF1dG9MZXZlbDtcbiAgICAgIGNvbnN0IGxlbiA9IG1heCA+IC0xID8gbWF4ICsgMSA6IGhscy5sZXZlbHMubGVuZ3RoO1xuICAgICAgbGV2ZWxzID0gaGxzLmxldmVscy5zbGljZSgwLCBsZW4pO1xuICAgIH1cbiAgICBmb3IgKGNvbnN0IGxldmVsIG9mIGxldmVscykge1xuICAgICAgaWYgKGxldmVsLmJpdHJhdGUgPiBiaXRyYXRlKSB7XG4gICAgICAgIGJpdHJhdGUgPSBsZXZlbC5iaXRyYXRlO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gYml0cmF0ZSA+IDAgPyBiaXRyYXRlIDogTmFOO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgYnVmZmVyIGxlbmd0aCBmb3IgYSBtZWRpYSB0eXBlIGluIG1pbGxpc2Vjb25kc1xuICAgKi9cbiAgZ2V0QnVmZmVyTGVuZ3RoKHR5cGUpIHtcbiAgICBjb25zdCBtZWRpYSA9IHRoaXMuaGxzLm1lZGlhO1xuICAgIGNvbnN0IGJ1ZmZlciA9IHR5cGUgPT09IENtT2JqZWN0VHlwZS5BVURJTyA/IHRoaXMuYXVkaW9CdWZmZXIgOiB0aGlzLnZpZGVvQnVmZmVyO1xuICAgIGlmICghYnVmZmVyIHx8ICFtZWRpYSkge1xuICAgICAgcmV0dXJuIE5hTjtcbiAgICB9XG4gICAgY29uc3QgaW5mbyA9IEJ1ZmZlckhlbHBlci5idWZmZXJJbmZvKGJ1ZmZlciwgbWVkaWEuY3VycmVudFRpbWUsIHRoaXMuY29uZmlnLm1heEJ1ZmZlckhvbGUpO1xuICAgIHJldHVybiBpbmZvLmxlbiAqIDEwMDA7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlIGEgcGxheWxpc3QgbG9hZGVyXG4gICAqL1xuICBjcmVhdGVQbGF5bGlzdExvYWRlcigpIHtcbiAgICBjb25zdCB7XG4gICAgICBwTG9hZGVyXG4gICAgfSA9IHRoaXMuY29uZmlnO1xuICAgIGNvbnN0IGFwcGx5ID0gdGhpcy5hcHBseVBsYXlsaXN0RGF0YTtcbiAgICBjb25zdCBDdG9yID0gcExvYWRlciB8fCB0aGlzLmNvbmZpZy5sb2FkZXI7XG4gICAgcmV0dXJuIGNsYXNzIENtY2RQbGF5bGlzdExvYWRlciB7XG4gICAgICBjb25zdHJ1Y3Rvcihjb25maWcpIHtcbiAgICAgICAgdGhpcy5sb2FkZXIgPSB2b2lkIDA7XG4gICAgICAgIHRoaXMubG9hZGVyID0gbmV3IEN0b3IoY29uZmlnKTtcbiAgICAgIH1cbiAgICAgIGdldCBzdGF0cygpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubG9hZGVyLnN0YXRzO1xuICAgICAgfVxuICAgICAgZ2V0IGNvbnRleHQoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmxvYWRlci5jb250ZXh0O1xuICAgICAgfVxuICAgICAgZGVzdHJveSgpIHtcbiAgICAgICAgdGhpcy5sb2FkZXIuZGVzdHJveSgpO1xuICAgICAgfVxuICAgICAgYWJvcnQoKSB7XG4gICAgICAgIHRoaXMubG9hZGVyLmFib3J0KCk7XG4gICAgICB9XG4gICAgICBsb2FkKGNvbnRleHQsIGNvbmZpZywgY2FsbGJhY2tzKSB7XG4gICAgICAgIGFwcGx5KGNvbnRleHQpO1xuICAgICAgICB0aGlzLmxvYWRlci5sb2FkKGNvbnRleHQsIGNvbmZpZywgY2FsbGJhY2tzKTtcbiAgICAgIH1cbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIHBsYXlsaXN0IGxvYWRlclxuICAgKi9cbiAgY3JlYXRlRnJhZ21lbnRMb2FkZXIoKSB7XG4gICAgY29uc3Qge1xuICAgICAgZkxvYWRlclxuICAgIH0gPSB0aGlzLmNvbmZpZztcbiAgICBjb25zdCBhcHBseSA9IHRoaXMuYXBwbHlGcmFnbWVudERhdGE7XG4gICAgY29uc3QgQ3RvciA9IGZMb2FkZXIgfHwgdGhpcy5jb25maWcubG9hZGVyO1xuICAgIHJldHVybiBjbGFzcyBDbWNkRnJhZ21lbnRMb2FkZXIge1xuICAgICAgY29uc3RydWN0b3IoY29uZmlnKSB7XG4gICAgICAgIHRoaXMubG9hZGVyID0gdm9pZCAwO1xuICAgICAgICB0aGlzLmxvYWRlciA9IG5ldyBDdG9yKGNvbmZpZyk7XG4gICAgICB9XG4gICAgICBnZXQgc3RhdHMoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmxvYWRlci5zdGF0cztcbiAgICAgIH1cbiAgICAgIGdldCBjb250ZXh0KCkge1xuICAgICAgICByZXR1cm4gdGhpcy5sb2FkZXIuY29udGV4dDtcbiAgICAgIH1cbiAgICAgIGRlc3Ryb3koKSB7XG4gICAgICAgIHRoaXMubG9hZGVyLmRlc3Ryb3koKTtcbiAgICAgIH1cbiAgICAgIGFib3J0KCkge1xuICAgICAgICB0aGlzLmxvYWRlci5hYm9ydCgpO1xuICAgICAgfVxuICAgICAgbG9hZChjb250ZXh0LCBjb25maWcsIGNhbGxiYWNrcykge1xuICAgICAgICBhcHBseShjb250ZXh0KTtcbiAgICAgICAgdGhpcy5sb2FkZXIubG9hZChjb250ZXh0LCBjb25maWcsIGNhbGxiYWNrcyk7XG4gICAgICB9XG4gICAgfTtcbiAgfVxufVxuXG5jb25zdCBQQVRIV0FZX1BFTkFMVFlfRFVSQVRJT05fTVMgPSAzMDAwMDA7XG5jbGFzcyBDb250ZW50U3RlZXJpbmdDb250cm9sbGVyIHtcbiAgY29uc3RydWN0b3IoaGxzKSB7XG4gICAgdGhpcy5obHMgPSB2b2lkIDA7XG4gICAgdGhpcy5sb2cgPSB2b2lkIDA7XG4gICAgdGhpcy5sb2FkZXIgPSBudWxsO1xuICAgIHRoaXMudXJpID0gbnVsbDtcbiAgICB0aGlzLnBhdGh3YXlJZCA9ICcuJztcbiAgICB0aGlzLnBhdGh3YXlQcmlvcml0eSA9IG51bGw7XG4gICAgdGhpcy50aW1lVG9Mb2FkID0gMzAwO1xuICAgIHRoaXMucmVsb2FkVGltZXIgPSAtMTtcbiAgICB0aGlzLnVwZGF0ZWQgPSAwO1xuICAgIHRoaXMuc3RhcnRlZCA9IGZhbHNlO1xuICAgIHRoaXMuZW5hYmxlZCA9IHRydWU7XG4gICAgdGhpcy5sZXZlbHMgPSBudWxsO1xuICAgIHRoaXMuYXVkaW9UcmFja3MgPSBudWxsO1xuICAgIHRoaXMuc3VidGl0bGVUcmFja3MgPSBudWxsO1xuICAgIHRoaXMucGVuYWxpemVkUGF0aHdheXMgPSB7fTtcbiAgICB0aGlzLmhscyA9IGhscztcbiAgICB0aGlzLmxvZyA9IGxvZ2dlci5sb2cuYmluZChsb2dnZXIsIGBbY29udGVudC1zdGVlcmluZ106YCk7XG4gICAgdGhpcy5yZWdpc3Rlckxpc3RlbmVycygpO1xuICB9XG4gIHJlZ2lzdGVyTGlzdGVuZXJzKCkge1xuICAgIGNvbnN0IGhscyA9IHRoaXMuaGxzO1xuICAgIGhscy5vbihFdmVudHMuTUFOSUZFU1RfTE9BRElORywgdGhpcy5vbk1hbmlmZXN0TG9hZGluZywgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5NQU5JRkVTVF9MT0FERUQsIHRoaXMub25NYW5pZmVzdExvYWRlZCwgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5NQU5JRkVTVF9QQVJTRUQsIHRoaXMub25NYW5pZmVzdFBhcnNlZCwgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5FUlJPUiwgdGhpcy5vbkVycm9yLCB0aGlzKTtcbiAgfVxuICB1bnJlZ2lzdGVyTGlzdGVuZXJzKCkge1xuICAgIGNvbnN0IGhscyA9IHRoaXMuaGxzO1xuICAgIGlmICghaGxzKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGhscy5vZmYoRXZlbnRzLk1BTklGRVNUX0xPQURJTkcsIHRoaXMub25NYW5pZmVzdExvYWRpbmcsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLk1BTklGRVNUX0xPQURFRCwgdGhpcy5vbk1hbmlmZXN0TG9hZGVkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5NQU5JRkVTVF9QQVJTRUQsIHRoaXMub25NYW5pZmVzdFBhcnNlZCwgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuRVJST1IsIHRoaXMub25FcnJvciwgdGhpcyk7XG4gIH1cbiAgc3RhcnRMb2FkKCkge1xuICAgIHRoaXMuc3RhcnRlZCA9IHRydWU7XG4gICAgdGhpcy5jbGVhclRpbWVvdXQoKTtcbiAgICBpZiAodGhpcy5lbmFibGVkICYmIHRoaXMudXJpKSB7XG4gICAgICBpZiAodGhpcy51cGRhdGVkKSB7XG4gICAgICAgIGNvbnN0IHR0bCA9IHRoaXMudGltZVRvTG9hZCAqIDEwMDAgLSAocGVyZm9ybWFuY2Uubm93KCkgLSB0aGlzLnVwZGF0ZWQpO1xuICAgICAgICBpZiAodHRsID4gMCkge1xuICAgICAgICAgIHRoaXMuc2NoZWR1bGVSZWZyZXNoKHRoaXMudXJpLCB0dGwpO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgdGhpcy5sb2FkU3RlZXJpbmdNYW5pZmVzdCh0aGlzLnVyaSk7XG4gICAgfVxuICB9XG4gIHN0b3BMb2FkKCkge1xuICAgIHRoaXMuc3RhcnRlZCA9IGZhbHNlO1xuICAgIGlmICh0aGlzLmxvYWRlcikge1xuICAgICAgdGhpcy5sb2FkZXIuZGVzdHJveSgpO1xuICAgICAgdGhpcy5sb2FkZXIgPSBudWxsO1xuICAgIH1cbiAgICB0aGlzLmNsZWFyVGltZW91dCgpO1xuICB9XG4gIGNsZWFyVGltZW91dCgpIHtcbiAgICBpZiAodGhpcy5yZWxvYWRUaW1lciAhPT0gLTEpIHtcbiAgICAgIHNlbGYuY2xlYXJUaW1lb3V0KHRoaXMucmVsb2FkVGltZXIpO1xuICAgICAgdGhpcy5yZWxvYWRUaW1lciA9IC0xO1xuICAgIH1cbiAgfVxuICBkZXN0cm95KCkge1xuICAgIHRoaXMudW5yZWdpc3Rlckxpc3RlbmVycygpO1xuICAgIHRoaXMuc3RvcExvYWQoKTtcbiAgICAvLyBAdHMtaWdub3JlXG4gICAgdGhpcy5obHMgPSBudWxsO1xuICAgIHRoaXMubGV2ZWxzID0gdGhpcy5hdWRpb1RyYWNrcyA9IHRoaXMuc3VidGl0bGVUcmFja3MgPSBudWxsO1xuICB9XG4gIHJlbW92ZUxldmVsKGxldmVsVG9SZW1vdmUpIHtcbiAgICBjb25zdCBsZXZlbHMgPSB0aGlzLmxldmVscztcbiAgICBpZiAobGV2ZWxzKSB7XG4gICAgICB0aGlzLmxldmVscyA9IGxldmVscy5maWx0ZXIobGV2ZWwgPT4gbGV2ZWwgIT09IGxldmVsVG9SZW1vdmUpO1xuICAgIH1cbiAgfVxuICBvbk1hbmlmZXN0TG9hZGluZygpIHtcbiAgICB0aGlzLnN0b3BMb2FkKCk7XG4gICAgdGhpcy5lbmFibGVkID0gdHJ1ZTtcbiAgICB0aGlzLnRpbWVUb0xvYWQgPSAzMDA7XG4gICAgdGhpcy51cGRhdGVkID0gMDtcbiAgICB0aGlzLnVyaSA9IG51bGw7XG4gICAgdGhpcy5wYXRod2F5SWQgPSAnLic7XG4gICAgdGhpcy5sZXZlbHMgPSB0aGlzLmF1ZGlvVHJhY2tzID0gdGhpcy5zdWJ0aXRsZVRyYWNrcyA9IG51bGw7XG4gIH1cbiAgb25NYW5pZmVzdExvYWRlZChldmVudCwgZGF0YSkge1xuICAgIGNvbnN0IHtcbiAgICAgIGNvbnRlbnRTdGVlcmluZ1xuICAgIH0gPSBkYXRhO1xuICAgIGlmIChjb250ZW50U3RlZXJpbmcgPT09IG51bGwpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5wYXRod2F5SWQgPSBjb250ZW50U3RlZXJpbmcucGF0aHdheUlkO1xuICAgIHRoaXMudXJpID0gY29udGVudFN0ZWVyaW5nLnVyaTtcbiAgICBpZiAodGhpcy5zdGFydGVkKSB7XG4gICAgICB0aGlzLnN0YXJ0TG9hZCgpO1xuICAgIH1cbiAgfVxuICBvbk1hbmlmZXN0UGFyc2VkKGV2ZW50LCBkYXRhKSB7XG4gICAgdGhpcy5hdWRpb1RyYWNrcyA9IGRhdGEuYXVkaW9UcmFja3M7XG4gICAgdGhpcy5zdWJ0aXRsZVRyYWNrcyA9IGRhdGEuc3VidGl0bGVUcmFja3M7XG4gIH1cbiAgb25FcnJvcihldmVudCwgZGF0YSkge1xuICAgIGNvbnN0IHtcbiAgICAgIGVycm9yQWN0aW9uXG4gICAgfSA9IGRhdGE7XG4gICAgaWYgKChlcnJvckFjdGlvbiA9PSBudWxsID8gdm9pZCAwIDogZXJyb3JBY3Rpb24uYWN0aW9uKSA9PT0gTmV0d29ya0Vycm9yQWN0aW9uLlNlbmRBbHRlcm5hdGVUb1BlbmFsdHlCb3ggJiYgZXJyb3JBY3Rpb24uZmxhZ3MgPT09IEVycm9yQWN0aW9uRmxhZ3MuTW92ZUFsbEFsdGVybmF0ZXNNYXRjaGluZ0hvc3QpIHtcbiAgICAgIGNvbnN0IGxldmVscyA9IHRoaXMubGV2ZWxzO1xuICAgICAgbGV0IHBhdGh3YXlQcmlvcml0eSA9IHRoaXMucGF0aHdheVByaW9yaXR5O1xuICAgICAgbGV0IGVycm9yUGF0aHdheSA9IHRoaXMucGF0aHdheUlkO1xuICAgICAgaWYgKGRhdGEuY29udGV4dCkge1xuICAgICAgICBjb25zdCB7XG4gICAgICAgICAgZ3JvdXBJZCxcbiAgICAgICAgICBwYXRod2F5SWQsXG4gICAgICAgICAgdHlwZVxuICAgICAgICB9ID0gZGF0YS5jb250ZXh0O1xuICAgICAgICBpZiAoZ3JvdXBJZCAmJiBsZXZlbHMpIHtcbiAgICAgICAgICBlcnJvclBhdGh3YXkgPSB0aGlzLmdldFBhdGh3YXlGb3JHcm91cElkKGdyb3VwSWQsIHR5cGUsIGVycm9yUGF0aHdheSk7XG4gICAgICAgIH0gZWxzZSBpZiAocGF0aHdheUlkKSB7XG4gICAgICAgICAgZXJyb3JQYXRod2F5ID0gcGF0aHdheUlkO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoIShlcnJvclBhdGh3YXkgaW4gdGhpcy5wZW5hbGl6ZWRQYXRod2F5cykpIHtcbiAgICAgICAgdGhpcy5wZW5hbGl6ZWRQYXRod2F5c1tlcnJvclBhdGh3YXldID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gICAgICB9XG4gICAgICBpZiAoIXBhdGh3YXlQcmlvcml0eSAmJiBsZXZlbHMpIHtcbiAgICAgICAgLy8gSWYgUEFUSFdBWS1QUklPUklUWSB3YXMgbm90IHByb3ZpZGVkLCBsaXN0IHBhdGh3YXlzIGZvciBlcnJvciBoYW5kbGluZ1xuICAgICAgICBwYXRod2F5UHJpb3JpdHkgPSBsZXZlbHMucmVkdWNlKChwYXRod2F5cywgbGV2ZWwpID0+IHtcbiAgICAgICAgICBpZiAocGF0aHdheXMuaW5kZXhPZihsZXZlbC5wYXRod2F5SWQpID09PSAtMSkge1xuICAgICAgICAgICAgcGF0aHdheXMucHVzaChsZXZlbC5wYXRod2F5SWQpO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gcGF0aHdheXM7XG4gICAgICAgIH0sIFtdKTtcbiAgICAgIH1cbiAgICAgIGlmIChwYXRod2F5UHJpb3JpdHkgJiYgcGF0aHdheVByaW9yaXR5Lmxlbmd0aCA+IDEpIHtcbiAgICAgICAgdGhpcy51cGRhdGVQYXRod2F5UHJpb3JpdHkocGF0aHdheVByaW9yaXR5KTtcbiAgICAgICAgZXJyb3JBY3Rpb24ucmVzb2x2ZWQgPSB0aGlzLnBhdGh3YXlJZCAhPT0gZXJyb3JQYXRod2F5O1xuICAgICAgfVxuICAgICAgaWYgKCFlcnJvckFjdGlvbi5yZXNvbHZlZCkge1xuICAgICAgICBsb2dnZXIud2FybihgQ291bGQgbm90IHJlc29sdmUgJHtkYXRhLmRldGFpbHN9IChcIiR7ZGF0YS5lcnJvci5tZXNzYWdlfVwiKSB3aXRoIGNvbnRlbnQtc3RlZXJpbmcgZm9yIFBhdGh3YXk6ICR7ZXJyb3JQYXRod2F5fSBsZXZlbHM6ICR7bGV2ZWxzID8gbGV2ZWxzLmxlbmd0aCA6IGxldmVsc30gcHJpb3JpdGllczogJHtKU09OLnN0cmluZ2lmeShwYXRod2F5UHJpb3JpdHkpfSBwZW5hbGl6ZWQ6ICR7SlNPTi5zdHJpbmdpZnkodGhpcy5wZW5hbGl6ZWRQYXRod2F5cyl9YCk7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIGZpbHRlclBhcnNlZExldmVscyhsZXZlbHMpIHtcbiAgICAvLyBGaWx0ZXIgbGV2ZWxzIHRvIG9ubHkgaW5jbHVkZSB0aG9zZSB0aGF0IGFyZSBpbiB0aGUgaW5pdGlhbCBwYXRod2F5XG4gICAgdGhpcy5sZXZlbHMgPSBsZXZlbHM7XG4gICAgbGV0IHBhdGh3YXlMZXZlbHMgPSB0aGlzLmdldExldmVsc0ZvclBhdGh3YXkodGhpcy5wYXRod2F5SWQpO1xuICAgIGlmIChwYXRod2F5TGV2ZWxzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgY29uc3QgcGF0aHdheUlkID0gbGV2ZWxzWzBdLnBhdGh3YXlJZDtcbiAgICAgIHRoaXMubG9nKGBObyBsZXZlbHMgZm91bmQgaW4gUGF0aHdheSAke3RoaXMucGF0aHdheUlkfS4gU2V0dGluZyBpbml0aWFsIFBhdGh3YXkgdG8gXCIke3BhdGh3YXlJZH1cImApO1xuICAgICAgcGF0aHdheUxldmVscyA9IHRoaXMuZ2V0TGV2ZWxzRm9yUGF0aHdheShwYXRod2F5SWQpO1xuICAgICAgdGhpcy5wYXRod2F5SWQgPSBwYXRod2F5SWQ7XG4gICAgfVxuICAgIGlmIChwYXRod2F5TGV2ZWxzLmxlbmd0aCAhPT0gbGV2ZWxzLmxlbmd0aCkge1xuICAgICAgdGhpcy5sb2coYEZvdW5kICR7cGF0aHdheUxldmVscy5sZW5ndGh9LyR7bGV2ZWxzLmxlbmd0aH0gbGV2ZWxzIGluIFBhdGh3YXkgXCIke3RoaXMucGF0aHdheUlkfVwiYCk7XG4gICAgICByZXR1cm4gcGF0aHdheUxldmVscztcbiAgICB9XG4gICAgcmV0dXJuIGxldmVscztcbiAgfVxuICBnZXRMZXZlbHNGb3JQYXRod2F5KHBhdGh3YXlJZCkge1xuICAgIGlmICh0aGlzLmxldmVscyA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuIFtdO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5sZXZlbHMuZmlsdGVyKGxldmVsID0+IHBhdGh3YXlJZCA9PT0gbGV2ZWwucGF0aHdheUlkKTtcbiAgfVxuICB1cGRhdGVQYXRod2F5UHJpb3JpdHkocGF0aHdheVByaW9yaXR5KSB7XG4gICAgdGhpcy5wYXRod2F5UHJpb3JpdHkgPSBwYXRod2F5UHJpb3JpdHk7XG4gICAgbGV0IGxldmVscztcblxuICAgIC8vIEV2YWx1YXRlIGlmIHdlIHNob3VsZCByZW1vdmUgdGhlIHBhdGh3YXkgZnJvbSB0aGUgcGVuYWxpemVkIGxpc3RcbiAgICBjb25zdCBwZW5hbGl6ZWRQYXRod2F5cyA9IHRoaXMucGVuYWxpemVkUGF0aHdheXM7XG4gICAgY29uc3Qgbm93ID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gICAgT2JqZWN0LmtleXMocGVuYWxpemVkUGF0aHdheXMpLmZvckVhY2gocGF0aHdheUlkID0+IHtcbiAgICAgIGlmIChub3cgLSBwZW5hbGl6ZWRQYXRod2F5c1twYXRod2F5SWRdID4gUEFUSFdBWV9QRU5BTFRZX0RVUkFUSU9OX01TKSB7XG4gICAgICAgIGRlbGV0ZSBwZW5hbGl6ZWRQYXRod2F5c1twYXRod2F5SWRdO1xuICAgICAgfVxuICAgIH0pO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcGF0aHdheVByaW9yaXR5Lmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBwYXRod2F5SWQgPSBwYXRod2F5UHJpb3JpdHlbaV07XG4gICAgICBpZiAocGF0aHdheUlkIGluIHBlbmFsaXplZFBhdGh3YXlzKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgaWYgKHBhdGh3YXlJZCA9PT0gdGhpcy5wYXRod2F5SWQpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY29uc3Qgc2VsZWN0ZWRJbmRleCA9IHRoaXMuaGxzLm5leHRMb2FkTGV2ZWw7XG4gICAgICBjb25zdCBzZWxlY3RlZExldmVsID0gdGhpcy5obHMubGV2ZWxzW3NlbGVjdGVkSW5kZXhdO1xuICAgICAgbGV2ZWxzID0gdGhpcy5nZXRMZXZlbHNGb3JQYXRod2F5KHBhdGh3YXlJZCk7XG4gICAgICBpZiAobGV2ZWxzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgdGhpcy5sb2coYFNldHRpbmcgUGF0aHdheSB0byBcIiR7cGF0aHdheUlkfVwiYCk7XG4gICAgICAgIHRoaXMucGF0aHdheUlkID0gcGF0aHdheUlkO1xuICAgICAgICByZWFzc2lnbkZyYWdtZW50TGV2ZWxJbmRleGVzKGxldmVscyk7XG4gICAgICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLkxFVkVMU19VUERBVEVELCB7XG4gICAgICAgICAgbGV2ZWxzXG4gICAgICAgIH0pO1xuICAgICAgICAvLyBTZXQgTGV2ZWxDb250cm9sbGVyJ3MgbGV2ZWwgdG8gdHJpZ2dlciBMRVZFTF9TV0lUQ0hJTkcgd2hpY2ggbG9hZHMgcGxheWxpc3QgaWYgbmVlZGVkXG4gICAgICAgIGNvbnN0IGxldmVsQWZ0ZXJDaGFuZ2UgPSB0aGlzLmhscy5sZXZlbHNbc2VsZWN0ZWRJbmRleF07XG4gICAgICAgIGlmIChzZWxlY3RlZExldmVsICYmIGxldmVsQWZ0ZXJDaGFuZ2UgJiYgdGhpcy5sZXZlbHMpIHtcbiAgICAgICAgICBpZiAobGV2ZWxBZnRlckNoYW5nZS5hdHRyc1snU1RBQkxFLVZBUklBTlQtSUQnXSAhPT0gc2VsZWN0ZWRMZXZlbC5hdHRyc1snU1RBQkxFLVZBUklBTlQtSUQnXSAmJiBsZXZlbEFmdGVyQ2hhbmdlLmJpdHJhdGUgIT09IHNlbGVjdGVkTGV2ZWwuYml0cmF0ZSkge1xuICAgICAgICAgICAgdGhpcy5sb2coYFVuc3RhYmxlIFBhdGh3YXlzIGNoYW5nZSBmcm9tIGJpdHJhdGUgJHtzZWxlY3RlZExldmVsLmJpdHJhdGV9IHRvICR7bGV2ZWxBZnRlckNoYW5nZS5iaXRyYXRlfWApO1xuICAgICAgICAgIH1cbiAgICAgICAgICB0aGlzLmhscy5uZXh0TG9hZExldmVsID0gc2VsZWN0ZWRJbmRleDtcbiAgICAgICAgfVxuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgZ2V0UGF0aHdheUZvckdyb3VwSWQoZ3JvdXBJZCwgdHlwZSwgZGVmYXVsdFBhdGh3YXkpIHtcbiAgICBjb25zdCBsZXZlbHMgPSB0aGlzLmdldExldmVsc0ZvclBhdGh3YXkoZGVmYXVsdFBhdGh3YXkpLmNvbmNhdCh0aGlzLmxldmVscyB8fCBbXSk7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBsZXZlbHMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmICh0eXBlID09PSBQbGF5bGlzdENvbnRleHRUeXBlLkFVRElPX1RSQUNLICYmIGxldmVsc1tpXS5oYXNBdWRpb0dyb3VwKGdyb3VwSWQpIHx8IHR5cGUgPT09IFBsYXlsaXN0Q29udGV4dFR5cGUuU1VCVElUTEVfVFJBQ0sgJiYgbGV2ZWxzW2ldLmhhc1N1YnRpdGxlR3JvdXAoZ3JvdXBJZCkpIHtcbiAgICAgICAgcmV0dXJuIGxldmVsc1tpXS5wYXRod2F5SWQ7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBkZWZhdWx0UGF0aHdheTtcbiAgfVxuICBjbG9uZVBhdGh3YXlzKHBhdGh3YXlDbG9uZXMpIHtcbiAgICBjb25zdCBsZXZlbHMgPSB0aGlzLmxldmVscztcbiAgICBpZiAoIWxldmVscykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBhdWRpb0dyb3VwQ2xvbmVNYXAgPSB7fTtcbiAgICBjb25zdCBzdWJ0aXRsZUdyb3VwQ2xvbmVNYXAgPSB7fTtcbiAgICBwYXRod2F5Q2xvbmVzLmZvckVhY2gocGF0aHdheUNsb25lID0+IHtcbiAgICAgIGNvbnN0IHtcbiAgICAgICAgSUQ6IGNsb25lSWQsXG4gICAgICAgICdCQVNFLUlEJzogYmFzZUlkLFxuICAgICAgICAnVVJJLVJFUExBQ0VNRU5UJzogdXJpUmVwbGFjZW1lbnRcbiAgICAgIH0gPSBwYXRod2F5Q2xvbmU7XG4gICAgICBpZiAobGV2ZWxzLnNvbWUobGV2ZWwgPT4gbGV2ZWwucGF0aHdheUlkID09PSBjbG9uZUlkKSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjb25zdCBjbG9uZWRWYXJpYW50cyA9IHRoaXMuZ2V0TGV2ZWxzRm9yUGF0aHdheShiYXNlSWQpLm1hcChiYXNlTGV2ZWwgPT4ge1xuICAgICAgICBjb25zdCBhdHRyaWJ1dGVzID0gbmV3IEF0dHJMaXN0KGJhc2VMZXZlbC5hdHRycyk7XG4gICAgICAgIGF0dHJpYnV0ZXNbJ1BBVEhXQVktSUQnXSA9IGNsb25lSWQ7XG4gICAgICAgIGNvbnN0IGNsb25lZEF1ZGlvR3JvdXBJZCA9IGF0dHJpYnV0ZXMuQVVESU8gJiYgYCR7YXR0cmlidXRlcy5BVURJT31fY2xvbmVfJHtjbG9uZUlkfWA7XG4gICAgICAgIGNvbnN0IGNsb25lZFN1YnRpdGxlR3JvdXBJZCA9IGF0dHJpYnV0ZXMuU1VCVElUTEVTICYmIGAke2F0dHJpYnV0ZXMuU1VCVElUTEVTfV9jbG9uZV8ke2Nsb25lSWR9YDtcbiAgICAgICAgaWYgKGNsb25lZEF1ZGlvR3JvdXBJZCkge1xuICAgICAgICAgIGF1ZGlvR3JvdXBDbG9uZU1hcFthdHRyaWJ1dGVzLkFVRElPXSA9IGNsb25lZEF1ZGlvR3JvdXBJZDtcbiAgICAgICAgICBhdHRyaWJ1dGVzLkFVRElPID0gY2xvbmVkQXVkaW9Hcm91cElkO1xuICAgICAgICB9XG4gICAgICAgIGlmIChjbG9uZWRTdWJ0aXRsZUdyb3VwSWQpIHtcbiAgICAgICAgICBzdWJ0aXRsZUdyb3VwQ2xvbmVNYXBbYXR0cmlidXRlcy5TVUJUSVRMRVNdID0gY2xvbmVkU3VidGl0bGVHcm91cElkO1xuICAgICAgICAgIGF0dHJpYnV0ZXMuU1VCVElUTEVTID0gY2xvbmVkU3VidGl0bGVHcm91cElkO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHVybCA9IHBlcmZvcm1VcmlSZXBsYWNlbWVudChiYXNlTGV2ZWwudXJpLCBhdHRyaWJ1dGVzWydTVEFCTEUtVkFSSUFOVC1JRCddLCAnUEVSLVZBUklBTlQtVVJJUycsIHVyaVJlcGxhY2VtZW50KTtcbiAgICAgICAgY29uc3QgY2xvbmVkTGV2ZWwgPSBuZXcgTGV2ZWwoe1xuICAgICAgICAgIGF0dHJzOiBhdHRyaWJ1dGVzLFxuICAgICAgICAgIGF1ZGlvQ29kZWM6IGJhc2VMZXZlbC5hdWRpb0NvZGVjLFxuICAgICAgICAgIGJpdHJhdGU6IGJhc2VMZXZlbC5iaXRyYXRlLFxuICAgICAgICAgIGhlaWdodDogYmFzZUxldmVsLmhlaWdodCxcbiAgICAgICAgICBuYW1lOiBiYXNlTGV2ZWwubmFtZSxcbiAgICAgICAgICB1cmwsXG4gICAgICAgICAgdmlkZW9Db2RlYzogYmFzZUxldmVsLnZpZGVvQ29kZWMsXG4gICAgICAgICAgd2lkdGg6IGJhc2VMZXZlbC53aWR0aFxuICAgICAgICB9KTtcbiAgICAgICAgaWYgKGJhc2VMZXZlbC5hdWRpb0dyb3Vwcykge1xuICAgICAgICAgIGZvciAobGV0IGkgPSAxOyBpIDwgYmFzZUxldmVsLmF1ZGlvR3JvdXBzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBjbG9uZWRMZXZlbC5hZGRHcm91cElkKCdhdWRpbycsIGAke2Jhc2VMZXZlbC5hdWRpb0dyb3Vwc1tpXX1fY2xvbmVfJHtjbG9uZUlkfWApO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZiAoYmFzZUxldmVsLnN1YnRpdGxlR3JvdXBzKSB7XG4gICAgICAgICAgZm9yIChsZXQgaSA9IDE7IGkgPCBiYXNlTGV2ZWwuc3VidGl0bGVHcm91cHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgIGNsb25lZExldmVsLmFkZEdyb3VwSWQoJ3RleHQnLCBgJHtiYXNlTGV2ZWwuc3VidGl0bGVHcm91cHNbaV19X2Nsb25lXyR7Y2xvbmVJZH1gKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGNsb25lZExldmVsO1xuICAgICAgfSk7XG4gICAgICBsZXZlbHMucHVzaCguLi5jbG9uZWRWYXJpYW50cyk7XG4gICAgICBjbG9uZVJlbmRpdGlvbkdyb3Vwcyh0aGlzLmF1ZGlvVHJhY2tzLCBhdWRpb0dyb3VwQ2xvbmVNYXAsIHVyaVJlcGxhY2VtZW50LCBjbG9uZUlkKTtcbiAgICAgIGNsb25lUmVuZGl0aW9uR3JvdXBzKHRoaXMuc3VidGl0bGVUcmFja3MsIHN1YnRpdGxlR3JvdXBDbG9uZU1hcCwgdXJpUmVwbGFjZW1lbnQsIGNsb25lSWQpO1xuICAgIH0pO1xuICB9XG4gIGxvYWRTdGVlcmluZ01hbmlmZXN0KHVyaSkge1xuICAgIGNvbnN0IGNvbmZpZyA9IHRoaXMuaGxzLmNvbmZpZztcbiAgICBjb25zdCBMb2FkZXIgPSBjb25maWcubG9hZGVyO1xuICAgIGlmICh0aGlzLmxvYWRlcikge1xuICAgICAgdGhpcy5sb2FkZXIuZGVzdHJveSgpO1xuICAgIH1cbiAgICB0aGlzLmxvYWRlciA9IG5ldyBMb2FkZXIoY29uZmlnKTtcbiAgICBsZXQgdXJsO1xuICAgIHRyeSB7XG4gICAgICB1cmwgPSBuZXcgc2VsZi5VUkwodXJpKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgdGhpcy5lbmFibGVkID0gZmFsc2U7XG4gICAgICB0aGlzLmxvZyhgRmFpbGVkIHRvIHBhcnNlIFN0ZWVyaW5nIE1hbmlmZXN0IFVSSTogJHt1cml9YCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmICh1cmwucHJvdG9jb2wgIT09ICdkYXRhOicpIHtcbiAgICAgIGNvbnN0IHRocm91Z2hwdXQgPSAodGhpcy5obHMuYmFuZHdpZHRoRXN0aW1hdGUgfHwgY29uZmlnLmFickV3bWFEZWZhdWx0RXN0aW1hdGUpIHwgMDtcbiAgICAgIHVybC5zZWFyY2hQYXJhbXMuc2V0KCdfSExTX3BhdGh3YXknLCB0aGlzLnBhdGh3YXlJZCk7XG4gICAgICB1cmwuc2VhcmNoUGFyYW1zLnNldCgnX0hMU190aHJvdWdocHV0JywgJycgKyB0aHJvdWdocHV0KTtcbiAgICB9XG4gICAgY29uc3QgY29udGV4dCA9IHtcbiAgICAgIHJlc3BvbnNlVHlwZTogJ2pzb24nLFxuICAgICAgdXJsOiB1cmwuaHJlZlxuICAgIH07XG4gICAgY29uc3QgbG9hZFBvbGljeSA9IGNvbmZpZy5zdGVlcmluZ01hbmlmZXN0TG9hZFBvbGljeS5kZWZhdWx0O1xuICAgIGNvbnN0IGxlZ2FjeVJldHJ5Q29tcGF0aWJpbGl0eSA9IGxvYWRQb2xpY3kuZXJyb3JSZXRyeSB8fCBsb2FkUG9saWN5LnRpbWVvdXRSZXRyeSB8fCB7fTtcbiAgICBjb25zdCBsb2FkZXJDb25maWcgPSB7XG4gICAgICBsb2FkUG9saWN5LFxuICAgICAgdGltZW91dDogbG9hZFBvbGljeS5tYXhMb2FkVGltZU1zLFxuICAgICAgbWF4UmV0cnk6IGxlZ2FjeVJldHJ5Q29tcGF0aWJpbGl0eS5tYXhOdW1SZXRyeSB8fCAwLFxuICAgICAgcmV0cnlEZWxheTogbGVnYWN5UmV0cnlDb21wYXRpYmlsaXR5LnJldHJ5RGVsYXlNcyB8fCAwLFxuICAgICAgbWF4UmV0cnlEZWxheTogbGVnYWN5UmV0cnlDb21wYXRpYmlsaXR5Lm1heFJldHJ5RGVsYXlNcyB8fCAwXG4gICAgfTtcbiAgICBjb25zdCBjYWxsYmFja3MgPSB7XG4gICAgICBvblN1Y2Nlc3M6IChyZXNwb25zZSwgc3RhdHMsIGNvbnRleHQsIG5ldHdvcmtEZXRhaWxzKSA9PiB7XG4gICAgICAgIHRoaXMubG9nKGBMb2FkZWQgc3RlZXJpbmcgbWFuaWZlc3Q6IFwiJHt1cmx9XCJgKTtcbiAgICAgICAgY29uc3Qgc3RlZXJpbmdEYXRhID0gcmVzcG9uc2UuZGF0YTtcbiAgICAgICAgaWYgKHN0ZWVyaW5nRGF0YS5WRVJTSU9OICE9PSAxKSB7XG4gICAgICAgICAgdGhpcy5sb2coYFN0ZWVyaW5nIFZFUlNJT04gJHtzdGVlcmluZ0RhdGEuVkVSU0lPTn0gbm90IHN1cHBvcnRlZCFgKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy51cGRhdGVkID0gcGVyZm9ybWFuY2Uubm93KCk7XG4gICAgICAgIHRoaXMudGltZVRvTG9hZCA9IHN0ZWVyaW5nRGF0YS5UVEw7XG4gICAgICAgIGNvbnN0IHtcbiAgICAgICAgICAnUkVMT0FELVVSSSc6IHJlbG9hZFVyaSxcbiAgICAgICAgICAnUEFUSFdBWS1DTE9ORVMnOiBwYXRod2F5Q2xvbmVzLFxuICAgICAgICAgICdQQVRIV0FZLVBSSU9SSVRZJzogcGF0aHdheVByaW9yaXR5XG4gICAgICAgIH0gPSBzdGVlcmluZ0RhdGE7XG4gICAgICAgIGlmIChyZWxvYWRVcmkpIHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgdGhpcy51cmkgPSBuZXcgc2VsZi5VUkwocmVsb2FkVXJpLCB1cmwpLmhyZWY7XG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIHRoaXMuZW5hYmxlZCA9IGZhbHNlO1xuICAgICAgICAgICAgdGhpcy5sb2coYEZhaWxlZCB0byBwYXJzZSBTdGVlcmluZyBNYW5pZmVzdCBSRUxPQUQtVVJJOiAke3JlbG9hZFVyaX1gKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5zY2hlZHVsZVJlZnJlc2godGhpcy51cmkgfHwgY29udGV4dC51cmwpO1xuICAgICAgICBpZiAocGF0aHdheUNsb25lcykge1xuICAgICAgICAgIHRoaXMuY2xvbmVQYXRod2F5cyhwYXRod2F5Q2xvbmVzKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBsb2FkZWRTdGVlcmluZ0RhdGEgPSB7XG4gICAgICAgICAgc3RlZXJpbmdNYW5pZmVzdDogc3RlZXJpbmdEYXRhLFxuICAgICAgICAgIHVybDogdXJsLnRvU3RyaW5nKClcbiAgICAgICAgfTtcbiAgICAgICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuU1RFRVJJTkdfTUFOSUZFU1RfTE9BREVELCBsb2FkZWRTdGVlcmluZ0RhdGEpO1xuICAgICAgICBpZiAocGF0aHdheVByaW9yaXR5KSB7XG4gICAgICAgICAgdGhpcy51cGRhdGVQYXRod2F5UHJpb3JpdHkocGF0aHdheVByaW9yaXR5KTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIG9uRXJyb3I6IChlcnJvciwgY29udGV4dCwgbmV0d29ya0RldGFpbHMsIHN0YXRzKSA9PiB7XG4gICAgICAgIHRoaXMubG9nKGBFcnJvciBsb2FkaW5nIHN0ZWVyaW5nIG1hbmlmZXN0OiAke2Vycm9yLmNvZGV9ICR7ZXJyb3IudGV4dH0gKCR7Y29udGV4dC51cmx9KWApO1xuICAgICAgICB0aGlzLnN0b3BMb2FkKCk7XG4gICAgICAgIGlmIChlcnJvci5jb2RlID09PSA0MTApIHtcbiAgICAgICAgICB0aGlzLmVuYWJsZWQgPSBmYWxzZTtcbiAgICAgICAgICB0aGlzLmxvZyhgU3RlZXJpbmcgbWFuaWZlc3QgJHtjb250ZXh0LnVybH0gbm8gbG9uZ2VyIGF2YWlsYWJsZWApO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBsZXQgdHRsID0gdGhpcy50aW1lVG9Mb2FkICogMTAwMDtcbiAgICAgICAgaWYgKGVycm9yLmNvZGUgPT09IDQyOSkge1xuICAgICAgICAgIGNvbnN0IGxvYWRlciA9IHRoaXMubG9hZGVyO1xuICAgICAgICAgIGlmICh0eXBlb2YgKGxvYWRlciA9PSBudWxsID8gdm9pZCAwIDogbG9hZGVyLmdldFJlc3BvbnNlSGVhZGVyKSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgICAgY29uc3QgcmV0cnlBZnRlciA9IGxvYWRlci5nZXRSZXNwb25zZUhlYWRlcignUmV0cnktQWZ0ZXInKTtcbiAgICAgICAgICAgIGlmIChyZXRyeUFmdGVyKSB7XG4gICAgICAgICAgICAgIHR0bCA9IHBhcnNlRmxvYXQocmV0cnlBZnRlcikgKiAxMDAwO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICB0aGlzLmxvZyhgU3RlZXJpbmcgbWFuaWZlc3QgJHtjb250ZXh0LnVybH0gcmF0ZSBsaW1pdGVkYCk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuc2NoZWR1bGVSZWZyZXNoKHRoaXMudXJpIHx8IGNvbnRleHQudXJsLCB0dGwpO1xuICAgICAgfSxcbiAgICAgIG9uVGltZW91dDogKHN0YXRzLCBjb250ZXh0LCBuZXR3b3JrRGV0YWlscykgPT4ge1xuICAgICAgICB0aGlzLmxvZyhgVGltZW91dCBsb2FkaW5nIHN0ZWVyaW5nIG1hbmlmZXN0ICgke2NvbnRleHQudXJsfSlgKTtcbiAgICAgICAgdGhpcy5zY2hlZHVsZVJlZnJlc2godGhpcy51cmkgfHwgY29udGV4dC51cmwpO1xuICAgICAgfVxuICAgIH07XG4gICAgdGhpcy5sb2coYFJlcXVlc3Rpbmcgc3RlZXJpbmcgbWFuaWZlc3Q6ICR7dXJsfWApO1xuICAgIHRoaXMubG9hZGVyLmxvYWQoY29udGV4dCwgbG9hZGVyQ29uZmlnLCBjYWxsYmFja3MpO1xuICB9XG4gIHNjaGVkdWxlUmVmcmVzaCh1cmksIHR0bE1zID0gdGhpcy50aW1lVG9Mb2FkICogMTAwMCkge1xuICAgIHRoaXMuY2xlYXJUaW1lb3V0KCk7XG4gICAgdGhpcy5yZWxvYWRUaW1lciA9IHNlbGYuc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICB2YXIgX3RoaXMkaGxzO1xuICAgICAgY29uc3QgbWVkaWEgPSAoX3RoaXMkaGxzID0gdGhpcy5obHMpID09IG51bGwgPyB2b2lkIDAgOiBfdGhpcyRobHMubWVkaWE7XG4gICAgICBpZiAobWVkaWEgJiYgIW1lZGlhLmVuZGVkKSB7XG4gICAgICAgIHRoaXMubG9hZFN0ZWVyaW5nTWFuaWZlc3QodXJpKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgdGhpcy5zY2hlZHVsZVJlZnJlc2godXJpLCB0aGlzLnRpbWVUb0xvYWQgKiAxMDAwKTtcbiAgICB9LCB0dGxNcyk7XG4gIH1cbn1cbmZ1bmN0aW9uIGNsb25lUmVuZGl0aW9uR3JvdXBzKHRyYWNrcywgZ3JvdXBDbG9uZU1hcCwgdXJpUmVwbGFjZW1lbnQsIGNsb25lSWQpIHtcbiAgaWYgKCF0cmFja3MpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgT2JqZWN0LmtleXMoZ3JvdXBDbG9uZU1hcCkuZm9yRWFjaChhdWRpb0dyb3VwSWQgPT4ge1xuICAgIGNvbnN0IGNsb25lZFRyYWNrcyA9IHRyYWNrcy5maWx0ZXIodHJhY2sgPT4gdHJhY2suZ3JvdXBJZCA9PT0gYXVkaW9Hcm91cElkKS5tYXAodHJhY2sgPT4ge1xuICAgICAgY29uc3QgY2xvbmVkVHJhY2sgPSBfZXh0ZW5kcyh7fSwgdHJhY2spO1xuICAgICAgY2xvbmVkVHJhY2suZGV0YWlscyA9IHVuZGVmaW5lZDtcbiAgICAgIGNsb25lZFRyYWNrLmF0dHJzID0gbmV3IEF0dHJMaXN0KGNsb25lZFRyYWNrLmF0dHJzKTtcbiAgICAgIGNsb25lZFRyYWNrLnVybCA9IGNsb25lZFRyYWNrLmF0dHJzLlVSSSA9IHBlcmZvcm1VcmlSZXBsYWNlbWVudCh0cmFjay51cmwsIHRyYWNrLmF0dHJzWydTVEFCTEUtUkVORElUSU9OLUlEJ10sICdQRVItUkVORElUSU9OLVVSSVMnLCB1cmlSZXBsYWNlbWVudCk7XG4gICAgICBjbG9uZWRUcmFjay5ncm91cElkID0gY2xvbmVkVHJhY2suYXR0cnNbJ0dST1VQLUlEJ10gPSBncm91cENsb25lTWFwW2F1ZGlvR3JvdXBJZF07XG4gICAgICBjbG9uZWRUcmFjay5hdHRyc1snUEFUSFdBWS1JRCddID0gY2xvbmVJZDtcbiAgICAgIHJldHVybiBjbG9uZWRUcmFjaztcbiAgICB9KTtcbiAgICB0cmFja3MucHVzaCguLi5jbG9uZWRUcmFja3MpO1xuICB9KTtcbn1cbmZ1bmN0aW9uIHBlcmZvcm1VcmlSZXBsYWNlbWVudCh1cmksIHN0YWJsZUlkLCBwZXJPcHRpb25LZXksIHVyaVJlcGxhY2VtZW50KSB7XG4gIGNvbnN0IHtcbiAgICBIT1NUOiBob3N0LFxuICAgIFBBUkFNUzogcGFyYW1zLFxuICAgIFtwZXJPcHRpb25LZXldOiBwZXJPcHRpb25VcmlzXG4gIH0gPSB1cmlSZXBsYWNlbWVudDtcbiAgbGV0IHBlclZhcmlhbnRVcmk7XG4gIGlmIChzdGFibGVJZCkge1xuICAgIHBlclZhcmlhbnRVcmkgPSBwZXJPcHRpb25VcmlzID09IG51bGwgPyB2b2lkIDAgOiBwZXJPcHRpb25VcmlzW3N0YWJsZUlkXTtcbiAgICBpZiAocGVyVmFyaWFudFVyaSkge1xuICAgICAgdXJpID0gcGVyVmFyaWFudFVyaTtcbiAgICB9XG4gIH1cbiAgY29uc3QgdXJsID0gbmV3IHNlbGYuVVJMKHVyaSk7XG4gIGlmIChob3N0ICYmICFwZXJWYXJpYW50VXJpKSB7XG4gICAgdXJsLmhvc3QgPSBob3N0O1xuICB9XG4gIGlmIChwYXJhbXMpIHtcbiAgICBPYmplY3Qua2V5cyhwYXJhbXMpLnNvcnQoKS5mb3JFYWNoKGtleSA9PiB7XG4gICAgICBpZiAoa2V5KSB7XG4gICAgICAgIHVybC5zZWFyY2hQYXJhbXMuc2V0KGtleSwgcGFyYW1zW2tleV0pO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG4gIHJldHVybiB1cmwuaHJlZjtcbn1cblxuY29uc3QgQUdFX0hFQURFUl9MSU5FX1JFR0VYID0gL15hZ2U6XFxzKltcXGQuXStcXHMqJC9pbTtcbmNsYXNzIFhockxvYWRlciB7XG4gIGNvbnN0cnVjdG9yKGNvbmZpZykge1xuICAgIHRoaXMueGhyU2V0dXAgPSB2b2lkIDA7XG4gICAgdGhpcy5yZXF1ZXN0VGltZW91dCA9IHZvaWQgMDtcbiAgICB0aGlzLnJldHJ5VGltZW91dCA9IHZvaWQgMDtcbiAgICB0aGlzLnJldHJ5RGVsYXkgPSB2b2lkIDA7XG4gICAgdGhpcy5jb25maWcgPSBudWxsO1xuICAgIHRoaXMuY2FsbGJhY2tzID0gbnVsbDtcbiAgICB0aGlzLmNvbnRleHQgPSBudWxsO1xuICAgIHRoaXMubG9hZGVyID0gbnVsbDtcbiAgICB0aGlzLnN0YXRzID0gdm9pZCAwO1xuICAgIHRoaXMueGhyU2V0dXAgPSBjb25maWcgPyBjb25maWcueGhyU2V0dXAgfHwgbnVsbCA6IG51bGw7XG4gICAgdGhpcy5zdGF0cyA9IG5ldyBMb2FkU3RhdHMoKTtcbiAgICB0aGlzLnJldHJ5RGVsYXkgPSAwO1xuICB9XG4gIGRlc3Ryb3koKSB7XG4gICAgdGhpcy5jYWxsYmFja3MgPSBudWxsO1xuICAgIHRoaXMuYWJvcnRJbnRlcm5hbCgpO1xuICAgIHRoaXMubG9hZGVyID0gbnVsbDtcbiAgICB0aGlzLmNvbmZpZyA9IG51bGw7XG4gICAgdGhpcy5jb250ZXh0ID0gbnVsbDtcbiAgICB0aGlzLnhoclNldHVwID0gbnVsbDtcbiAgfVxuICBhYm9ydEludGVybmFsKCkge1xuICAgIGNvbnN0IGxvYWRlciA9IHRoaXMubG9hZGVyO1xuICAgIHNlbGYuY2xlYXJUaW1lb3V0KHRoaXMucmVxdWVzdFRpbWVvdXQpO1xuICAgIHNlbGYuY2xlYXJUaW1lb3V0KHRoaXMucmV0cnlUaW1lb3V0KTtcbiAgICBpZiAobG9hZGVyKSB7XG4gICAgICBsb2FkZXIub25yZWFkeXN0YXRlY2hhbmdlID0gbnVsbDtcbiAgICAgIGxvYWRlci5vbnByb2dyZXNzID0gbnVsbDtcbiAgICAgIGlmIChsb2FkZXIucmVhZHlTdGF0ZSAhPT0gNCkge1xuICAgICAgICB0aGlzLnN0YXRzLmFib3J0ZWQgPSB0cnVlO1xuICAgICAgICBsb2FkZXIuYWJvcnQoKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgYWJvcnQoKSB7XG4gICAgdmFyIF90aGlzJGNhbGxiYWNrcztcbiAgICB0aGlzLmFib3J0SW50ZXJuYWwoKTtcbiAgICBpZiAoKF90aGlzJGNhbGxiYWNrcyA9IHRoaXMuY2FsbGJhY2tzKSAhPSBudWxsICYmIF90aGlzJGNhbGxiYWNrcy5vbkFib3J0KSB7XG4gICAgICB0aGlzLmNhbGxiYWNrcy5vbkFib3J0KHRoaXMuc3RhdHMsIHRoaXMuY29udGV4dCwgdGhpcy5sb2FkZXIpO1xuICAgIH1cbiAgfVxuICBsb2FkKGNvbnRleHQsIGNvbmZpZywgY2FsbGJhY2tzKSB7XG4gICAgaWYgKHRoaXMuc3RhdHMubG9hZGluZy5zdGFydCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdMb2FkZXIgY2FuIG9ubHkgYmUgdXNlZCBvbmNlLicpO1xuICAgIH1cbiAgICB0aGlzLnN0YXRzLmxvYWRpbmcuc3RhcnQgPSBzZWxmLnBlcmZvcm1hbmNlLm5vdygpO1xuICAgIHRoaXMuY29udGV4dCA9IGNvbnRleHQ7XG4gICAgdGhpcy5jb25maWcgPSBjb25maWc7XG4gICAgdGhpcy5jYWxsYmFja3MgPSBjYWxsYmFja3M7XG4gICAgdGhpcy5sb2FkSW50ZXJuYWwoKTtcbiAgfVxuICBsb2FkSW50ZXJuYWwoKSB7XG4gICAgY29uc3Qge1xuICAgICAgY29uZmlnLFxuICAgICAgY29udGV4dFxuICAgIH0gPSB0aGlzO1xuICAgIGlmICghY29uZmlnIHx8ICFjb250ZXh0KSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHhociA9IHRoaXMubG9hZGVyID0gbmV3IHNlbGYuWE1MSHR0cFJlcXVlc3QoKTtcbiAgICBjb25zdCBzdGF0cyA9IHRoaXMuc3RhdHM7XG4gICAgc3RhdHMubG9hZGluZy5maXJzdCA9IDA7XG4gICAgc3RhdHMubG9hZGVkID0gMDtcbiAgICBzdGF0cy5hYm9ydGVkID0gZmFsc2U7XG4gICAgY29uc3QgeGhyU2V0dXAgPSB0aGlzLnhoclNldHVwO1xuICAgIGlmICh4aHJTZXR1cCkge1xuICAgICAgUHJvbWlzZS5yZXNvbHZlKCkudGhlbigoKSA9PiB7XG4gICAgICAgIGlmICh0aGlzLmxvYWRlciAhPT0geGhyIHx8IHRoaXMuc3RhdHMuYWJvcnRlZCkgcmV0dXJuO1xuICAgICAgICByZXR1cm4geGhyU2V0dXAoeGhyLCBjb250ZXh0LnVybCk7XG4gICAgICB9KS5jYXRjaChlcnJvciA9PiB7XG4gICAgICAgIGlmICh0aGlzLmxvYWRlciAhPT0geGhyIHx8IHRoaXMuc3RhdHMuYWJvcnRlZCkgcmV0dXJuO1xuICAgICAgICB4aHIub3BlbignR0VUJywgY29udGV4dC51cmwsIHRydWUpO1xuICAgICAgICByZXR1cm4geGhyU2V0dXAoeGhyLCBjb250ZXh0LnVybCk7XG4gICAgICB9KS50aGVuKCgpID0+IHtcbiAgICAgICAgaWYgKHRoaXMubG9hZGVyICE9PSB4aHIgfHwgdGhpcy5zdGF0cy5hYm9ydGVkKSByZXR1cm47XG4gICAgICAgIHRoaXMub3BlbkFuZFNlbmRYaHIoeGhyLCBjb250ZXh0LCBjb25maWcpO1xuICAgICAgfSkuY2F0Y2goZXJyb3IgPT4ge1xuICAgICAgICAvLyBJRTExIHRocm93cyBhbiBleGNlcHRpb24gb24geGhyLm9wZW4gaWYgYXR0ZW1wdGluZyB0byBhY2Nlc3MgYW4gSFRUUCByZXNvdXJjZSBvdmVyIEhUVFBTXG4gICAgICAgIHRoaXMuY2FsbGJhY2tzLm9uRXJyb3Ioe1xuICAgICAgICAgIGNvZGU6IHhoci5zdGF0dXMsXG4gICAgICAgICAgdGV4dDogZXJyb3IubWVzc2FnZVxuICAgICAgICB9LCBjb250ZXh0LCB4aHIsIHN0YXRzKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMub3BlbkFuZFNlbmRYaHIoeGhyLCBjb250ZXh0LCBjb25maWcpO1xuICAgIH1cbiAgfVxuICBvcGVuQW5kU2VuZFhocih4aHIsIGNvbnRleHQsIGNvbmZpZykge1xuICAgIGlmICgheGhyLnJlYWR5U3RhdGUpIHtcbiAgICAgIHhoci5vcGVuKCdHRVQnLCBjb250ZXh0LnVybCwgdHJ1ZSk7XG4gICAgfVxuICAgIGNvbnN0IGhlYWRlcnMgPSBjb250ZXh0LmhlYWRlcnM7XG4gICAgY29uc3Qge1xuICAgICAgbWF4VGltZVRvRmlyc3RCeXRlTXMsXG4gICAgICBtYXhMb2FkVGltZU1zXG4gICAgfSA9IGNvbmZpZy5sb2FkUG9saWN5O1xuICAgIGlmIChoZWFkZXJzKSB7XG4gICAgICBmb3IgKGNvbnN0IGhlYWRlciBpbiBoZWFkZXJzKSB7XG4gICAgICAgIHhoci5zZXRSZXF1ZXN0SGVhZGVyKGhlYWRlciwgaGVhZGVyc1toZWFkZXJdKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKGNvbnRleHQucmFuZ2VFbmQpIHtcbiAgICAgIHhoci5zZXRSZXF1ZXN0SGVhZGVyKCdSYW5nZScsICdieXRlcz0nICsgY29udGV4dC5yYW5nZVN0YXJ0ICsgJy0nICsgKGNvbnRleHQucmFuZ2VFbmQgLSAxKSk7XG4gICAgfVxuICAgIHhoci5vbnJlYWR5c3RhdGVjaGFuZ2UgPSB0aGlzLnJlYWR5c3RhdGVjaGFuZ2UuYmluZCh0aGlzKTtcbiAgICB4aHIub25wcm9ncmVzcyA9IHRoaXMubG9hZHByb2dyZXNzLmJpbmQodGhpcyk7XG4gICAgeGhyLnJlc3BvbnNlVHlwZSA9IGNvbnRleHQucmVzcG9uc2VUeXBlO1xuICAgIC8vIHNldHVwIHRpbWVvdXQgYmVmb3JlIHdlIHBlcmZvcm0gcmVxdWVzdFxuICAgIHNlbGYuY2xlYXJUaW1lb3V0KHRoaXMucmVxdWVzdFRpbWVvdXQpO1xuICAgIGNvbmZpZy50aW1lb3V0ID0gbWF4VGltZVRvRmlyc3RCeXRlTXMgJiYgaXNGaW5pdGVOdW1iZXIobWF4VGltZVRvRmlyc3RCeXRlTXMpID8gbWF4VGltZVRvRmlyc3RCeXRlTXMgOiBtYXhMb2FkVGltZU1zO1xuICAgIHRoaXMucmVxdWVzdFRpbWVvdXQgPSBzZWxmLnNldFRpbWVvdXQodGhpcy5sb2FkdGltZW91dC5iaW5kKHRoaXMpLCBjb25maWcudGltZW91dCk7XG4gICAgeGhyLnNlbmQoKTtcbiAgfVxuICByZWFkeXN0YXRlY2hhbmdlKCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGNvbnRleHQsXG4gICAgICBsb2FkZXI6IHhocixcbiAgICAgIHN0YXRzXG4gICAgfSA9IHRoaXM7XG4gICAgaWYgKCFjb250ZXh0IHx8ICF4aHIpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgcmVhZHlTdGF0ZSA9IHhoci5yZWFkeVN0YXRlO1xuICAgIGNvbnN0IGNvbmZpZyA9IHRoaXMuY29uZmlnO1xuXG4gICAgLy8gZG9uJ3QgcHJvY2VlZCBpZiB4aHIgaGFzIGJlZW4gYWJvcnRlZFxuICAgIGlmIChzdGF0cy5hYm9ydGVkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gPj0gSEVBREVSU19SRUNFSVZFRFxuICAgIGlmIChyZWFkeVN0YXRlID49IDIpIHtcbiAgICAgIGlmIChzdGF0cy5sb2FkaW5nLmZpcnN0ID09PSAwKSB7XG4gICAgICAgIHN0YXRzLmxvYWRpbmcuZmlyc3QgPSBNYXRoLm1heChzZWxmLnBlcmZvcm1hbmNlLm5vdygpLCBzdGF0cy5sb2FkaW5nLnN0YXJ0KTtcbiAgICAgICAgLy8gcmVhZHlTdGF0ZSA+PSAyIEFORCByZWFkeVN0YXRlICE9PTQgKHJlYWR5U3RhdGUgPSBIRUFERVJTX1JFQ0VJVkVEIHx8IExPQURJTkcpIHJlYXJtIHRpbWVvdXQgYXMgeGhyIG5vdCBmaW5pc2hlZCB5ZXRcbiAgICAgICAgaWYgKGNvbmZpZy50aW1lb3V0ICE9PSBjb25maWcubG9hZFBvbGljeS5tYXhMb2FkVGltZU1zKSB7XG4gICAgICAgICAgc2VsZi5jbGVhclRpbWVvdXQodGhpcy5yZXF1ZXN0VGltZW91dCk7XG4gICAgICAgICAgY29uZmlnLnRpbWVvdXQgPSBjb25maWcubG9hZFBvbGljeS5tYXhMb2FkVGltZU1zO1xuICAgICAgICAgIHRoaXMucmVxdWVzdFRpbWVvdXQgPSBzZWxmLnNldFRpbWVvdXQodGhpcy5sb2FkdGltZW91dC5iaW5kKHRoaXMpLCBjb25maWcubG9hZFBvbGljeS5tYXhMb2FkVGltZU1zIC0gKHN0YXRzLmxvYWRpbmcuZmlyc3QgLSBzdGF0cy5sb2FkaW5nLnN0YXJ0KSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmIChyZWFkeVN0YXRlID09PSA0KSB7XG4gICAgICAgIHNlbGYuY2xlYXJUaW1lb3V0KHRoaXMucmVxdWVzdFRpbWVvdXQpO1xuICAgICAgICB4aHIub25yZWFkeXN0YXRlY2hhbmdlID0gbnVsbDtcbiAgICAgICAgeGhyLm9ucHJvZ3Jlc3MgPSBudWxsO1xuICAgICAgICBjb25zdCBzdGF0dXMgPSB4aHIuc3RhdHVzO1xuICAgICAgICAvLyBodHRwIHN0YXR1cyBiZXR3ZWVuIDIwMCB0byAyOTkgYXJlIGFsbCBzdWNjZXNzZnVsXG4gICAgICAgIGNvbnN0IHVzZVJlc3BvbnNlID0geGhyLnJlc3BvbnNlVHlwZSAhPT0gJ3RleHQnO1xuICAgICAgICBpZiAoc3RhdHVzID49IDIwMCAmJiBzdGF0dXMgPCAzMDAgJiYgKHVzZVJlc3BvbnNlICYmIHhoci5yZXNwb25zZSB8fCB4aHIucmVzcG9uc2VUZXh0ICE9PSBudWxsKSkge1xuICAgICAgICAgIHN0YXRzLmxvYWRpbmcuZW5kID0gTWF0aC5tYXgoc2VsZi5wZXJmb3JtYW5jZS5ub3coKSwgc3RhdHMubG9hZGluZy5maXJzdCk7XG4gICAgICAgICAgY29uc3QgZGF0YSA9IHVzZVJlc3BvbnNlID8geGhyLnJlc3BvbnNlIDogeGhyLnJlc3BvbnNlVGV4dDtcbiAgICAgICAgICBjb25zdCBsZW4gPSB4aHIucmVzcG9uc2VUeXBlID09PSAnYXJyYXlidWZmZXInID8gZGF0YS5ieXRlTGVuZ3RoIDogZGF0YS5sZW5ndGg7XG4gICAgICAgICAgc3RhdHMubG9hZGVkID0gc3RhdHMudG90YWwgPSBsZW47XG4gICAgICAgICAgc3RhdHMuYndFc3RpbWF0ZSA9IHN0YXRzLnRvdGFsICogODAwMCAvIChzdGF0cy5sb2FkaW5nLmVuZCAtIHN0YXRzLmxvYWRpbmcuZmlyc3QpO1xuICAgICAgICAgIGlmICghdGhpcy5jYWxsYmFja3MpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc3Qgb25Qcm9ncmVzcyA9IHRoaXMuY2FsbGJhY2tzLm9uUHJvZ3Jlc3M7XG4gICAgICAgICAgaWYgKG9uUHJvZ3Jlc3MpIHtcbiAgICAgICAgICAgIG9uUHJvZ3Jlc3Moc3RhdHMsIGNvbnRleHQsIGRhdGEsIHhocik7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICghdGhpcy5jYWxsYmFja3MpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc3QgcmVzcG9uc2UgPSB7XG4gICAgICAgICAgICB1cmw6IHhoci5yZXNwb25zZVVSTCxcbiAgICAgICAgICAgIGRhdGE6IGRhdGEsXG4gICAgICAgICAgICBjb2RlOiBzdGF0dXNcbiAgICAgICAgICB9O1xuICAgICAgICAgIHRoaXMuY2FsbGJhY2tzLm9uU3VjY2VzcyhyZXNwb25zZSwgc3RhdHMsIGNvbnRleHQsIHhocik7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY29uc3QgcmV0cnlDb25maWcgPSBjb25maWcubG9hZFBvbGljeS5lcnJvclJldHJ5O1xuICAgICAgICAgIGNvbnN0IHJldHJ5Q291bnQgPSBzdGF0cy5yZXRyeTtcbiAgICAgICAgICAvLyBpZiBtYXggbmIgb2YgcmV0cmllcyByZWFjaGVkIG9yIGlmIGh0dHAgc3RhdHVzIGJldHdlZW4gNDAwIGFuZCA0OTkgKHN1Y2ggZXJyb3IgY2Fubm90IGJlIHJlY292ZXJlZCwgcmV0cnlpbmcgaXMgdXNlbGVzcyksIHJldHVybiBlcnJvclxuICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0ge1xuICAgICAgICAgICAgdXJsOiBjb250ZXh0LnVybCxcbiAgICAgICAgICAgIGRhdGE6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgIGNvZGU6IHN0YXR1c1xuICAgICAgICAgIH07XG4gICAgICAgICAgaWYgKHNob3VsZFJldHJ5KHJldHJ5Q29uZmlnLCByZXRyeUNvdW50LCBmYWxzZSwgcmVzcG9uc2UpKSB7XG4gICAgICAgICAgICB0aGlzLnJldHJ5KHJldHJ5Q29uZmlnKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgbG9nZ2VyLmVycm9yKGAke3N0YXR1c30gd2hpbGUgbG9hZGluZyAke2NvbnRleHQudXJsfWApO1xuICAgICAgICAgICAgdGhpcy5jYWxsYmFja3Mub25FcnJvcih7XG4gICAgICAgICAgICAgIGNvZGU6IHN0YXR1cyxcbiAgICAgICAgICAgICAgdGV4dDogeGhyLnN0YXR1c1RleHRcbiAgICAgICAgICAgIH0sIGNvbnRleHQsIHhociwgc3RhdHMpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuICBsb2FkdGltZW91dCgpIHtcbiAgICBpZiAoIXRoaXMuY29uZmlnKSByZXR1cm47XG4gICAgY29uc3QgcmV0cnlDb25maWcgPSB0aGlzLmNvbmZpZy5sb2FkUG9saWN5LnRpbWVvdXRSZXRyeTtcbiAgICBjb25zdCByZXRyeUNvdW50ID0gdGhpcy5zdGF0cy5yZXRyeTtcbiAgICBpZiAoc2hvdWxkUmV0cnkocmV0cnlDb25maWcsIHJldHJ5Q291bnQsIHRydWUpKSB7XG4gICAgICB0aGlzLnJldHJ5KHJldHJ5Q29uZmlnKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdmFyIF90aGlzJGNvbnRleHQ7XG4gICAgICBsb2dnZXIud2FybihgdGltZW91dCB3aGlsZSBsb2FkaW5nICR7KF90aGlzJGNvbnRleHQgPSB0aGlzLmNvbnRleHQpID09IG51bGwgPyB2b2lkIDAgOiBfdGhpcyRjb250ZXh0LnVybH1gKTtcbiAgICAgIGNvbnN0IGNhbGxiYWNrcyA9IHRoaXMuY2FsbGJhY2tzO1xuICAgICAgaWYgKGNhbGxiYWNrcykge1xuICAgICAgICB0aGlzLmFib3J0SW50ZXJuYWwoKTtcbiAgICAgICAgY2FsbGJhY2tzLm9uVGltZW91dCh0aGlzLnN0YXRzLCB0aGlzLmNvbnRleHQsIHRoaXMubG9hZGVyKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgcmV0cnkocmV0cnlDb25maWcpIHtcbiAgICBjb25zdCB7XG4gICAgICBjb250ZXh0LFxuICAgICAgc3RhdHNcbiAgICB9ID0gdGhpcztcbiAgICB0aGlzLnJldHJ5RGVsYXkgPSBnZXRSZXRyeURlbGF5KHJldHJ5Q29uZmlnLCBzdGF0cy5yZXRyeSk7XG4gICAgc3RhdHMucmV0cnkrKztcbiAgICBsb2dnZXIud2FybihgJHtzdGF0dXMgPyAnSFRUUCBTdGF0dXMgJyArIHN0YXR1cyA6ICdUaW1lb3V0J30gd2hpbGUgbG9hZGluZyAke2NvbnRleHQgPT0gbnVsbCA/IHZvaWQgMCA6IGNvbnRleHQudXJsfSwgcmV0cnlpbmcgJHtzdGF0cy5yZXRyeX0vJHtyZXRyeUNvbmZpZy5tYXhOdW1SZXRyeX0gaW4gJHt0aGlzLnJldHJ5RGVsYXl9bXNgKTtcbiAgICAvLyBhYm9ydCBhbmQgcmVzZXQgaW50ZXJuYWwgc3RhdGVcbiAgICB0aGlzLmFib3J0SW50ZXJuYWwoKTtcbiAgICB0aGlzLmxvYWRlciA9IG51bGw7XG4gICAgLy8gc2NoZWR1bGUgcmV0cnlcbiAgICBzZWxmLmNsZWFyVGltZW91dCh0aGlzLnJldHJ5VGltZW91dCk7XG4gICAgdGhpcy5yZXRyeVRpbWVvdXQgPSBzZWxmLnNldFRpbWVvdXQodGhpcy5sb2FkSW50ZXJuYWwuYmluZCh0aGlzKSwgdGhpcy5yZXRyeURlbGF5KTtcbiAgfVxuICBsb2FkcHJvZ3Jlc3MoZXZlbnQpIHtcbiAgICBjb25zdCBzdGF0cyA9IHRoaXMuc3RhdHM7XG4gICAgc3RhdHMubG9hZGVkID0gZXZlbnQubG9hZGVkO1xuICAgIGlmIChldmVudC5sZW5ndGhDb21wdXRhYmxlKSB7XG4gICAgICBzdGF0cy50b3RhbCA9IGV2ZW50LnRvdGFsO1xuICAgIH1cbiAgfVxuICBnZXRDYWNoZUFnZSgpIHtcbiAgICBsZXQgcmVzdWx0ID0gbnVsbDtcbiAgICBpZiAodGhpcy5sb2FkZXIgJiYgQUdFX0hFQURFUl9MSU5FX1JFR0VYLnRlc3QodGhpcy5sb2FkZXIuZ2V0QWxsUmVzcG9uc2VIZWFkZXJzKCkpKSB7XG4gICAgICBjb25zdCBhZ2VIZWFkZXIgPSB0aGlzLmxvYWRlci5nZXRSZXNwb25zZUhlYWRlcignYWdlJyk7XG4gICAgICByZXN1bHQgPSBhZ2VIZWFkZXIgPyBwYXJzZUZsb2F0KGFnZUhlYWRlcikgOiBudWxsO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG4gIGdldFJlc3BvbnNlSGVhZGVyKG5hbWUpIHtcbiAgICBpZiAodGhpcy5sb2FkZXIgJiYgbmV3IFJlZ0V4cChgXiR7bmFtZX06XFxcXHMqW1xcXFxkLl0rXFxcXHMqJGAsICdpbScpLnRlc3QodGhpcy5sb2FkZXIuZ2V0QWxsUmVzcG9uc2VIZWFkZXJzKCkpKSB7XG4gICAgICByZXR1cm4gdGhpcy5sb2FkZXIuZ2V0UmVzcG9uc2VIZWFkZXIobmFtZSk7XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG59XG5cbmZ1bmN0aW9uIGZldGNoU3VwcG9ydGVkKCkge1xuICBpZiAoXG4gIC8vIEB0cy1pZ25vcmVcbiAgc2VsZi5mZXRjaCAmJiBzZWxmLkFib3J0Q29udHJvbGxlciAmJiBzZWxmLlJlYWRhYmxlU3RyZWFtICYmIHNlbGYuUmVxdWVzdCkge1xuICAgIHRyeSB7XG4gICAgICBuZXcgc2VsZi5SZWFkYWJsZVN0cmVhbSh7fSk7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tbmV3XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAvKiBub29wICovXG4gICAgfVxuICB9XG4gIHJldHVybiBmYWxzZTtcbn1cbmNvbnN0IEJZVEVSQU5HRSA9IC8oXFxkKyktKFxcZCspXFwvKFxcZCspLztcbmNsYXNzIEZldGNoTG9hZGVyIHtcbiAgY29uc3RydWN0b3IoY29uZmlnIC8qIEhsc0NvbmZpZyAqLykge1xuICAgIHRoaXMuZmV0Y2hTZXR1cCA9IHZvaWQgMDtcbiAgICB0aGlzLnJlcXVlc3RUaW1lb3V0ID0gdm9pZCAwO1xuICAgIHRoaXMucmVxdWVzdCA9IG51bGw7XG4gICAgdGhpcy5yZXNwb25zZSA9IG51bGw7XG4gICAgdGhpcy5jb250cm9sbGVyID0gdm9pZCAwO1xuICAgIHRoaXMuY29udGV4dCA9IG51bGw7XG4gICAgdGhpcy5jb25maWcgPSBudWxsO1xuICAgIHRoaXMuY2FsbGJhY2tzID0gbnVsbDtcbiAgICB0aGlzLnN0YXRzID0gdm9pZCAwO1xuICAgIHRoaXMubG9hZGVyID0gbnVsbDtcbiAgICB0aGlzLmZldGNoU2V0dXAgPSBjb25maWcuZmV0Y2hTZXR1cCB8fCBnZXRSZXF1ZXN0O1xuICAgIHRoaXMuY29udHJvbGxlciA9IG5ldyBzZWxmLkFib3J0Q29udHJvbGxlcigpO1xuICAgIHRoaXMuc3RhdHMgPSBuZXcgTG9hZFN0YXRzKCk7XG4gIH1cbiAgZGVzdHJveSgpIHtcbiAgICB0aGlzLmxvYWRlciA9IHRoaXMuY2FsbGJhY2tzID0gdGhpcy5jb250ZXh0ID0gdGhpcy5jb25maWcgPSB0aGlzLnJlcXVlc3QgPSBudWxsO1xuICAgIHRoaXMuYWJvcnRJbnRlcm5hbCgpO1xuICAgIHRoaXMucmVzcG9uc2UgPSBudWxsO1xuICAgIC8vIEB0cy1pZ25vcmVcbiAgICB0aGlzLmZldGNoU2V0dXAgPSB0aGlzLmNvbnRyb2xsZXIgPSB0aGlzLnN0YXRzID0gbnVsbDtcbiAgfVxuICBhYm9ydEludGVybmFsKCkge1xuICAgIGlmICh0aGlzLmNvbnRyb2xsZXIgJiYgIXRoaXMuc3RhdHMubG9hZGluZy5lbmQpIHtcbiAgICAgIHRoaXMuc3RhdHMuYWJvcnRlZCA9IHRydWU7XG4gICAgICB0aGlzLmNvbnRyb2xsZXIuYWJvcnQoKTtcbiAgICB9XG4gIH1cbiAgYWJvcnQoKSB7XG4gICAgdmFyIF90aGlzJGNhbGxiYWNrcztcbiAgICB0aGlzLmFib3J0SW50ZXJuYWwoKTtcbiAgICBpZiAoKF90aGlzJGNhbGxiYWNrcyA9IHRoaXMuY2FsbGJhY2tzKSAhPSBudWxsICYmIF90aGlzJGNhbGxiYWNrcy5vbkFib3J0KSB7XG4gICAgICB0aGlzLmNhbGxiYWNrcy5vbkFib3J0KHRoaXMuc3RhdHMsIHRoaXMuY29udGV4dCwgdGhpcy5yZXNwb25zZSk7XG4gICAgfVxuICB9XG4gIGxvYWQoY29udGV4dCwgY29uZmlnLCBjYWxsYmFja3MpIHtcbiAgICBjb25zdCBzdGF0cyA9IHRoaXMuc3RhdHM7XG4gICAgaWYgKHN0YXRzLmxvYWRpbmcuc3RhcnQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignTG9hZGVyIGNhbiBvbmx5IGJlIHVzZWQgb25jZS4nKTtcbiAgICB9XG4gICAgc3RhdHMubG9hZGluZy5zdGFydCA9IHNlbGYucGVyZm9ybWFuY2Uubm93KCk7XG4gICAgY29uc3QgaW5pdFBhcmFtcyA9IGdldFJlcXVlc3RQYXJhbWV0ZXJzKGNvbnRleHQsIHRoaXMuY29udHJvbGxlci5zaWduYWwpO1xuICAgIGNvbnN0IG9uUHJvZ3Jlc3MgPSBjYWxsYmFja3Mub25Qcm9ncmVzcztcbiAgICBjb25zdCBpc0FycmF5QnVmZmVyID0gY29udGV4dC5yZXNwb25zZVR5cGUgPT09ICdhcnJheWJ1ZmZlcic7XG4gICAgY29uc3QgTEVOR1RIID0gaXNBcnJheUJ1ZmZlciA/ICdieXRlTGVuZ3RoJyA6ICdsZW5ndGgnO1xuICAgIGNvbnN0IHtcbiAgICAgIG1heFRpbWVUb0ZpcnN0Qnl0ZU1zLFxuICAgICAgbWF4TG9hZFRpbWVNc1xuICAgIH0gPSBjb25maWcubG9hZFBvbGljeTtcbiAgICB0aGlzLmNvbnRleHQgPSBjb250ZXh0O1xuICAgIHRoaXMuY29uZmlnID0gY29uZmlnO1xuICAgIHRoaXMuY2FsbGJhY2tzID0gY2FsbGJhY2tzO1xuICAgIHRoaXMucmVxdWVzdCA9IHRoaXMuZmV0Y2hTZXR1cChjb250ZXh0LCBpbml0UGFyYW1zKTtcbiAgICBzZWxmLmNsZWFyVGltZW91dCh0aGlzLnJlcXVlc3RUaW1lb3V0KTtcbiAgICBjb25maWcudGltZW91dCA9IG1heFRpbWVUb0ZpcnN0Qnl0ZU1zICYmIGlzRmluaXRlTnVtYmVyKG1heFRpbWVUb0ZpcnN0Qnl0ZU1zKSA/IG1heFRpbWVUb0ZpcnN0Qnl0ZU1zIDogbWF4TG9hZFRpbWVNcztcbiAgICB0aGlzLnJlcXVlc3RUaW1lb3V0ID0gc2VsZi5zZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIHRoaXMuYWJvcnRJbnRlcm5hbCgpO1xuICAgICAgY2FsbGJhY2tzLm9uVGltZW91dChzdGF0cywgY29udGV4dCwgdGhpcy5yZXNwb25zZSk7XG4gICAgfSwgY29uZmlnLnRpbWVvdXQpO1xuICAgIHNlbGYuZmV0Y2godGhpcy5yZXF1ZXN0KS50aGVuKHJlc3BvbnNlID0+IHtcbiAgICAgIHRoaXMucmVzcG9uc2UgPSB0aGlzLmxvYWRlciA9IHJlc3BvbnNlO1xuICAgICAgY29uc3QgZmlyc3QgPSBNYXRoLm1heChzZWxmLnBlcmZvcm1hbmNlLm5vdygpLCBzdGF0cy5sb2FkaW5nLnN0YXJ0KTtcbiAgICAgIHNlbGYuY2xlYXJUaW1lb3V0KHRoaXMucmVxdWVzdFRpbWVvdXQpO1xuICAgICAgY29uZmlnLnRpbWVvdXQgPSBtYXhMb2FkVGltZU1zO1xuICAgICAgdGhpcy5yZXF1ZXN0VGltZW91dCA9IHNlbGYuc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgIHRoaXMuYWJvcnRJbnRlcm5hbCgpO1xuICAgICAgICBjYWxsYmFja3Mub25UaW1lb3V0KHN0YXRzLCBjb250ZXh0LCB0aGlzLnJlc3BvbnNlKTtcbiAgICAgIH0sIG1heExvYWRUaW1lTXMgLSAoZmlyc3QgLSBzdGF0cy5sb2FkaW5nLnN0YXJ0KSk7XG4gICAgICBpZiAoIXJlc3BvbnNlLm9rKSB7XG4gICAgICAgIGNvbnN0IHtcbiAgICAgICAgICBzdGF0dXMsXG4gICAgICAgICAgc3RhdHVzVGV4dFxuICAgICAgICB9ID0gcmVzcG9uc2U7XG4gICAgICAgIHRocm93IG5ldyBGZXRjaEVycm9yKHN0YXR1c1RleHQgfHwgJ2ZldGNoLCBiYWQgbmV0d29yayByZXNwb25zZScsIHN0YXR1cywgcmVzcG9uc2UpO1xuICAgICAgfVxuICAgICAgc3RhdHMubG9hZGluZy5maXJzdCA9IGZpcnN0O1xuICAgICAgc3RhdHMudG90YWwgPSBnZXRDb250ZW50TGVuZ3RoKHJlc3BvbnNlLmhlYWRlcnMpIHx8IHN0YXRzLnRvdGFsO1xuICAgICAgaWYgKG9uUHJvZ3Jlc3MgJiYgaXNGaW5pdGVOdW1iZXIoY29uZmlnLmhpZ2hXYXRlck1hcmspKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmxvYWRQcm9ncmVzc2l2ZWx5KHJlc3BvbnNlLCBzdGF0cywgY29udGV4dCwgY29uZmlnLmhpZ2hXYXRlck1hcmssIG9uUHJvZ3Jlc3MpO1xuICAgICAgfVxuICAgICAgaWYgKGlzQXJyYXlCdWZmZXIpIHtcbiAgICAgICAgcmV0dXJuIHJlc3BvbnNlLmFycmF5QnVmZmVyKCk7XG4gICAgICB9XG4gICAgICBpZiAoY29udGV4dC5yZXNwb25zZVR5cGUgPT09ICdqc29uJykge1xuICAgICAgICByZXR1cm4gcmVzcG9uc2UuanNvbigpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHJlc3BvbnNlLnRleHQoKTtcbiAgICB9KS50aGVuKHJlc3BvbnNlRGF0YSA9PiB7XG4gICAgICBjb25zdCByZXNwb25zZSA9IHRoaXMucmVzcG9uc2U7XG4gICAgICBpZiAoIXJlc3BvbnNlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignbG9hZGVyIGRlc3Ryb3llZCcpO1xuICAgICAgfVxuICAgICAgc2VsZi5jbGVhclRpbWVvdXQodGhpcy5yZXF1ZXN0VGltZW91dCk7XG4gICAgICBzdGF0cy5sb2FkaW5nLmVuZCA9IE1hdGgubWF4KHNlbGYucGVyZm9ybWFuY2Uubm93KCksIHN0YXRzLmxvYWRpbmcuZmlyc3QpO1xuICAgICAgY29uc3QgdG90YWwgPSByZXNwb25zZURhdGFbTEVOR1RIXTtcbiAgICAgIGlmICh0b3RhbCkge1xuICAgICAgICBzdGF0cy5sb2FkZWQgPSBzdGF0cy50b3RhbCA9IHRvdGFsO1xuICAgICAgfVxuICAgICAgY29uc3QgbG9hZGVyUmVzcG9uc2UgPSB7XG4gICAgICAgIHVybDogcmVzcG9uc2UudXJsLFxuICAgICAgICBkYXRhOiByZXNwb25zZURhdGEsXG4gICAgICAgIGNvZGU6IHJlc3BvbnNlLnN0YXR1c1xuICAgICAgfTtcbiAgICAgIGlmIChvblByb2dyZXNzICYmICFpc0Zpbml0ZU51bWJlcihjb25maWcuaGlnaFdhdGVyTWFyaykpIHtcbiAgICAgICAgb25Qcm9ncmVzcyhzdGF0cywgY29udGV4dCwgcmVzcG9uc2VEYXRhLCByZXNwb25zZSk7XG4gICAgICB9XG4gICAgICBjYWxsYmFja3Mub25TdWNjZXNzKGxvYWRlclJlc3BvbnNlLCBzdGF0cywgY29udGV4dCwgcmVzcG9uc2UpO1xuICAgIH0pLmNhdGNoKGVycm9yID0+IHtcbiAgICAgIHNlbGYuY2xlYXJUaW1lb3V0KHRoaXMucmVxdWVzdFRpbWVvdXQpO1xuICAgICAgaWYgKHN0YXRzLmFib3J0ZWQpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgLy8gQ09SUyBlcnJvcnMgcmVzdWx0IGluIGFuIHVuZGVmaW5lZCBjb2RlLiBTZXQgaXQgdG8gMCBoZXJlIHRvIGFsaWduIHdpdGggWEhSJ3MgYmVoYXZpb3JcbiAgICAgIC8vIHdoZW4gZGVzdHJveWluZywgJ2Vycm9yJyBpdHNlbGYgY2FuIGJlIHVuZGVmaW5lZFxuICAgICAgY29uc3QgY29kZSA9ICFlcnJvciA/IDAgOiBlcnJvci5jb2RlIHx8IDA7XG4gICAgICBjb25zdCB0ZXh0ID0gIWVycm9yID8gbnVsbCA6IGVycm9yLm1lc3NhZ2U7XG4gICAgICBjYWxsYmFja3Mub25FcnJvcih7XG4gICAgICAgIGNvZGUsXG4gICAgICAgIHRleHRcbiAgICAgIH0sIGNvbnRleHQsIGVycm9yID8gZXJyb3IuZGV0YWlscyA6IG51bGwsIHN0YXRzKTtcbiAgICB9KTtcbiAgfVxuICBnZXRDYWNoZUFnZSgpIHtcbiAgICBsZXQgcmVzdWx0ID0gbnVsbDtcbiAgICBpZiAodGhpcy5yZXNwb25zZSkge1xuICAgICAgY29uc3QgYWdlSGVhZGVyID0gdGhpcy5yZXNwb25zZS5oZWFkZXJzLmdldCgnYWdlJyk7XG4gICAgICByZXN1bHQgPSBhZ2VIZWFkZXIgPyBwYXJzZUZsb2F0KGFnZUhlYWRlcikgOiBudWxsO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG4gIGdldFJlc3BvbnNlSGVhZGVyKG5hbWUpIHtcbiAgICByZXR1cm4gdGhpcy5yZXNwb25zZSA/IHRoaXMucmVzcG9uc2UuaGVhZGVycy5nZXQobmFtZSkgOiBudWxsO1xuICB9XG4gIGxvYWRQcm9ncmVzc2l2ZWx5KHJlc3BvbnNlLCBzdGF0cywgY29udGV4dCwgaGlnaFdhdGVyTWFyayA9IDAsIG9uUHJvZ3Jlc3MpIHtcbiAgICBjb25zdCBjaHVua0NhY2hlID0gbmV3IENodW5rQ2FjaGUoKTtcbiAgICBjb25zdCByZWFkZXIgPSByZXNwb25zZS5ib2R5LmdldFJlYWRlcigpO1xuICAgIGNvbnN0IHB1bXAgPSAoKSA9PiB7XG4gICAgICByZXR1cm4gcmVhZGVyLnJlYWQoKS50aGVuKGRhdGEgPT4ge1xuICAgICAgICBpZiAoZGF0YS5kb25lKSB7XG4gICAgICAgICAgaWYgKGNodW5rQ2FjaGUuZGF0YUxlbmd0aCkge1xuICAgICAgICAgICAgb25Qcm9ncmVzcyhzdGF0cywgY29udGV4dCwgY2h1bmtDYWNoZS5mbHVzaCgpLCByZXNwb25zZSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUobmV3IEFycmF5QnVmZmVyKDApKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBjaHVuayA9IGRhdGEudmFsdWU7XG4gICAgICAgIGNvbnN0IGxlbiA9IGNodW5rLmxlbmd0aDtcbiAgICAgICAgc3RhdHMubG9hZGVkICs9IGxlbjtcbiAgICAgICAgaWYgKGxlbiA8IGhpZ2hXYXRlck1hcmsgfHwgY2h1bmtDYWNoZS5kYXRhTGVuZ3RoKSB7XG4gICAgICAgICAgLy8gVGhlIGN1cnJlbnQgY2h1bmsgaXMgdG9vIHNtYWxsIHRvIHRvIGJlIGVtaXR0ZWQgb3IgdGhlIGNhY2hlIGFscmVhZHkgaGFzIGRhdGFcbiAgICAgICAgICAvLyBQdXNoIGl0IHRvIHRoZSBjYWNoZVxuICAgICAgICAgIGNodW5rQ2FjaGUucHVzaChjaHVuayk7XG4gICAgICAgICAgaWYgKGNodW5rQ2FjaGUuZGF0YUxlbmd0aCA+PSBoaWdoV2F0ZXJNYXJrKSB7XG4gICAgICAgICAgICAvLyBmbHVzaCBpbiBvcmRlciB0byBqb2luIHRoZSB0eXBlZCBhcnJheXNcbiAgICAgICAgICAgIG9uUHJvZ3Jlc3Moc3RhdHMsIGNvbnRleHQsIGNodW5rQ2FjaGUuZmx1c2goKSwgcmVzcG9uc2UpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBJZiB0aGVyZSdzIG5vdGhpbmcgY2FjaGVkIGFscmVhZHksIGFuZCB0aGUgY2hhY2hlIGlzIGxhcmdlIGVub3VnaFxuICAgICAgICAgIC8vIGp1c3QgZW1pdCB0aGUgcHJvZ3Jlc3MgZXZlbnRcbiAgICAgICAgICBvblByb2dyZXNzKHN0YXRzLCBjb250ZXh0LCBjaHVuaywgcmVzcG9uc2UpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBwdW1wKCk7XG4gICAgICB9KS5jYXRjaCgoKSA9PiB7XG4gICAgICAgIC8qIGFib3J0ZWQgKi9cbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KCk7XG4gICAgICB9KTtcbiAgICB9O1xuICAgIHJldHVybiBwdW1wKCk7XG4gIH1cbn1cbmZ1bmN0aW9uIGdldFJlcXVlc3RQYXJhbWV0ZXJzKGNvbnRleHQsIHNpZ25hbCkge1xuICBjb25zdCBpbml0UGFyYW1zID0ge1xuICAgIG1ldGhvZDogJ0dFVCcsXG4gICAgbW9kZTogJ2NvcnMnLFxuICAgIGNyZWRlbnRpYWxzOiAnc2FtZS1vcmlnaW4nLFxuICAgIHNpZ25hbCxcbiAgICBoZWFkZXJzOiBuZXcgc2VsZi5IZWFkZXJzKF9leHRlbmRzKHt9LCBjb250ZXh0LmhlYWRlcnMpKVxuICB9O1xuICBpZiAoY29udGV4dC5yYW5nZUVuZCkge1xuICAgIGluaXRQYXJhbXMuaGVhZGVycy5zZXQoJ1JhbmdlJywgJ2J5dGVzPScgKyBjb250ZXh0LnJhbmdlU3RhcnQgKyAnLScgKyBTdHJpbmcoY29udGV4dC5yYW5nZUVuZCAtIDEpKTtcbiAgfVxuICByZXR1cm4gaW5pdFBhcmFtcztcbn1cbmZ1bmN0aW9uIGdldEJ5dGVSYW5nZUxlbmd0aChieXRlUmFuZ2VIZWFkZXIpIHtcbiAgY29uc3QgcmVzdWx0ID0gQllURVJBTkdFLmV4ZWMoYnl0ZVJhbmdlSGVhZGVyKTtcbiAgaWYgKHJlc3VsdCkge1xuICAgIHJldHVybiBwYXJzZUludChyZXN1bHRbMl0pIC0gcGFyc2VJbnQocmVzdWx0WzFdKSArIDE7XG4gIH1cbn1cbmZ1bmN0aW9uIGdldENvbnRlbnRMZW5ndGgoaGVhZGVycykge1xuICBjb25zdCBjb250ZW50UmFuZ2UgPSBoZWFkZXJzLmdldCgnQ29udGVudC1SYW5nZScpO1xuICBpZiAoY29udGVudFJhbmdlKSB7XG4gICAgY29uc3QgYnl0ZVJhbmdlTGVuZ3RoID0gZ2V0Qnl0ZVJhbmdlTGVuZ3RoKGNvbnRlbnRSYW5nZSk7XG4gICAgaWYgKGlzRmluaXRlTnVtYmVyKGJ5dGVSYW5nZUxlbmd0aCkpIHtcbiAgICAgIHJldHVybiBieXRlUmFuZ2VMZW5ndGg7XG4gICAgfVxuICB9XG4gIGNvbnN0IGNvbnRlbnRMZW5ndGggPSBoZWFkZXJzLmdldCgnQ29udGVudC1MZW5ndGgnKTtcbiAgaWYgKGNvbnRlbnRMZW5ndGgpIHtcbiAgICByZXR1cm4gcGFyc2VJbnQoY29udGVudExlbmd0aCk7XG4gIH1cbn1cbmZ1bmN0aW9uIGdldFJlcXVlc3QoY29udGV4dCwgaW5pdFBhcmFtcykge1xuICByZXR1cm4gbmV3IHNlbGYuUmVxdWVzdChjb250ZXh0LnVybCwgaW5pdFBhcmFtcyk7XG59XG5jbGFzcyBGZXRjaEVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICBjb25zdHJ1Y3RvcihtZXNzYWdlLCBjb2RlLCBkZXRhaWxzKSB7XG4gICAgc3VwZXIobWVzc2FnZSk7XG4gICAgdGhpcy5jb2RlID0gdm9pZCAwO1xuICAgIHRoaXMuZGV0YWlscyA9IHZvaWQgMDtcbiAgICB0aGlzLmNvZGUgPSBjb2RlO1xuICAgIHRoaXMuZGV0YWlscyA9IGRldGFpbHM7XG4gIH1cbn1cblxuY29uc3QgV0hJVEVTUEFDRV9DSEFSID0gL1xccy87XG5jb25zdCBDdWVzID0ge1xuICBuZXdDdWUodHJhY2ssIHN0YXJ0VGltZSwgZW5kVGltZSwgY2FwdGlvblNjcmVlbikge1xuICAgIGNvbnN0IHJlc3VsdCA9IFtdO1xuICAgIGxldCByb3c7XG4gICAgLy8gdGhlIHR5cGUgZGF0YSBzdGF0ZXMgdGhpcyBpcyBWVFRDdWUsIGJ1dCBpdCBjYW4gcG90ZW50aWFsbHkgYmUgYSBUZXh0VHJhY2tDdWUgb24gb2xkIGJyb3dzZXJzXG4gICAgbGV0IGN1ZTtcbiAgICBsZXQgaW5kZW50aW5nO1xuICAgIGxldCBpbmRlbnQ7XG4gICAgbGV0IHRleHQ7XG4gICAgY29uc3QgQ3VlID0gc2VsZi5WVFRDdWUgfHwgc2VsZi5UZXh0VHJhY2tDdWU7XG4gICAgZm9yIChsZXQgciA9IDA7IHIgPCBjYXB0aW9uU2NyZWVuLnJvd3MubGVuZ3RoOyByKyspIHtcbiAgICAgIHJvdyA9IGNhcHRpb25TY3JlZW4ucm93c1tyXTtcbiAgICAgIGluZGVudGluZyA9IHRydWU7XG4gICAgICBpbmRlbnQgPSAwO1xuICAgICAgdGV4dCA9ICcnO1xuICAgICAgaWYgKCFyb3cuaXNFbXB0eSgpKSB7XG4gICAgICAgIHZhciBfdHJhY2skY3VlcztcbiAgICAgICAgZm9yIChsZXQgYyA9IDA7IGMgPCByb3cuY2hhcnMubGVuZ3RoOyBjKyspIHtcbiAgICAgICAgICBpZiAoV0hJVEVTUEFDRV9DSEFSLnRlc3Qocm93LmNoYXJzW2NdLnVjaGFyKSAmJiBpbmRlbnRpbmcpIHtcbiAgICAgICAgICAgIGluZGVudCsrO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0ZXh0ICs9IHJvdy5jaGFyc1tjXS51Y2hhcjtcbiAgICAgICAgICAgIGluZGVudGluZyA9IGZhbHNlO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICAvLyBUbyBiZSB1c2VkIGZvciBjbGVhbmluZy11cCBvcnBoYW5lZCByb2xsLXVwIGNhcHRpb25zXG4gICAgICAgIHJvdy5jdWVTdGFydFRpbWUgPSBzdGFydFRpbWU7XG5cbiAgICAgICAgLy8gR2l2ZSBhIHNsaWdodCBidW1wIHRvIHRoZSBlbmRUaW1lIGlmIGl0J3MgZXF1YWwgdG8gc3RhcnRUaW1lIHRvIGF2b2lkIGEgU3ludGF4RXJyb3IgaW4gSUVcbiAgICAgICAgaWYgKHN0YXJ0VGltZSA9PT0gZW5kVGltZSkge1xuICAgICAgICAgIGVuZFRpbWUgKz0gMC4wMDAxO1xuICAgICAgICB9XG4gICAgICAgIGlmIChpbmRlbnQgPj0gMTYpIHtcbiAgICAgICAgICBpbmRlbnQtLTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBpbmRlbnQrKztcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBjdWVUZXh0ID0gZml4TGluZUJyZWFrcyh0ZXh0LnRyaW0oKSk7XG4gICAgICAgIGNvbnN0IGlkID0gZ2VuZXJhdGVDdWVJZChzdGFydFRpbWUsIGVuZFRpbWUsIGN1ZVRleHQpO1xuXG4gICAgICAgIC8vIElmIHRoaXMgY3VlIGFscmVhZHkgZXhpc3RzIGluIHRoZSB0cmFjayBkbyBub3QgcHVzaCBpdFxuICAgICAgICBpZiAoISh0cmFjayAhPSBudWxsICYmIChfdHJhY2skY3VlcyA9IHRyYWNrLmN1ZXMpICE9IG51bGwgJiYgX3RyYWNrJGN1ZXMuZ2V0Q3VlQnlJZChpZCkpKSB7XG4gICAgICAgICAgY3VlID0gbmV3IEN1ZShzdGFydFRpbWUsIGVuZFRpbWUsIGN1ZVRleHQpO1xuICAgICAgICAgIGN1ZS5pZCA9IGlkO1xuICAgICAgICAgIGN1ZS5saW5lID0gciArIDE7XG4gICAgICAgICAgY3VlLmFsaWduID0gJ2xlZnQnO1xuICAgICAgICAgIC8vIENsYW1wIHRoZSBwb3NpdGlvbiBiZXR3ZWVuIDEwIGFuZCA4MCBwZXJjZW50IChDRUEtNjA4IFBBQyBpbmRlbnQgY29kZSlcbiAgICAgICAgICAvLyBodHRwczovL2R2Y3MudzMub3JnL2hnL3RleHQtdHJhY2tzL3Jhdy1maWxlL2RlZmF1bHQvNjA4dG9WVFQvNjA4dG9WVFQuaHRtbCNwb3NpdGlvbmluZy1pbi1jZWEtNjA4XG4gICAgICAgICAgLy8gRmlyZWZveCB0aHJvd3MgYW4gZXhjZXB0aW9uIGFuZCBjYXB0aW9ucyBicmVhayB3aXRoIG91dCBvZiBib3VuZHMgMC0xMDAgdmFsdWVzXG4gICAgICAgICAgY3VlLnBvc2l0aW9uID0gMTAgKyBNYXRoLm1pbig4MCwgTWF0aC5mbG9vcihpbmRlbnQgKiA4IC8gMzIpICogMTApO1xuICAgICAgICAgIHJlc3VsdC5wdXNoKGN1ZSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKHRyYWNrICYmIHJlc3VsdC5sZW5ndGgpIHtcbiAgICAgIC8vIFNvcnQgYm90dG9tIGN1ZXMgaW4gcmV2ZXJzZSBvcmRlciBzbyB0aGF0IHRoZXkgcmVuZGVyIGluIGxpbmUgb3JkZXIgd2hlbiBvdmVybGFwcGluZyBpbiBDaHJvbWVcbiAgICAgIHJlc3VsdC5zb3J0KChjdWVBLCBjdWVCKSA9PiB7XG4gICAgICAgIGlmIChjdWVBLmxpbmUgPT09ICdhdXRvJyB8fCBjdWVCLmxpbmUgPT09ICdhdXRvJykge1xuICAgICAgICAgIHJldHVybiAwO1xuICAgICAgICB9XG4gICAgICAgIGlmIChjdWVBLmxpbmUgPiA4ICYmIGN1ZUIubGluZSA+IDgpIHtcbiAgICAgICAgICByZXR1cm4gY3VlQi5saW5lIC0gY3VlQS5saW5lO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBjdWVBLmxpbmUgLSBjdWVCLmxpbmU7XG4gICAgICB9KTtcbiAgICAgIHJlc3VsdC5mb3JFYWNoKGN1ZSA9PiBhZGRDdWVUb1RyYWNrKHRyYWNrLCBjdWUpKTtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxufTtcblxuLyoqXG4gKiBAZGVwcmVjYXRlZCB1c2UgZnJhZ0xvYWRQb2xpY3kuZGVmYXVsdFxuICovXG5cbi8qKlxuICogQGRlcHJlY2F0ZWQgdXNlIG1hbmlmZXN0TG9hZFBvbGljeS5kZWZhdWx0IGFuZCBwbGF5bGlzdExvYWRQb2xpY3kuZGVmYXVsdFxuICovXG5cbmNvbnN0IGRlZmF1bHRMb2FkUG9saWN5ID0ge1xuICBtYXhUaW1lVG9GaXJzdEJ5dGVNczogODAwMCxcbiAgbWF4TG9hZFRpbWVNczogMjAwMDAsXG4gIHRpbWVvdXRSZXRyeTogbnVsbCxcbiAgZXJyb3JSZXRyeTogbnVsbFxufTtcblxuLyoqXG4gKiBAaWdub3JlXG4gKiBJZiBwb3NzaWJsZSwga2VlcCBobHNEZWZhdWx0Q29uZmlnIHNoYWxsb3dcbiAqIEl0IGlzIGNsb25lZCB3aGVuZXZlciBhIG5ldyBIbHMgaW5zdGFuY2UgaXMgY3JlYXRlZCwgYnkga2VlcGluZyB0aGUgY29uZmlnXG4gKiBzaGFsbG93IHRoZSBwcm9wZXJ0aWVzIGFyZSBjbG9uZWQsIGFuZCB3ZSBkb24ndCBlbmQgdXAgbWFuaXB1bGF0aW5nIHRoZSBkZWZhdWx0XG4gKi9cbmNvbnN0IGhsc0RlZmF1bHRDb25maWcgPSBfb2JqZWN0U3ByZWFkMihfb2JqZWN0U3ByZWFkMih7XG4gIGF1dG9TdGFydExvYWQ6IHRydWUsXG4gIC8vIHVzZWQgYnkgc3RyZWFtLWNvbnRyb2xsZXJcbiAgc3RhcnRQb3NpdGlvbjogLTEsXG4gIC8vIHVzZWQgYnkgc3RyZWFtLWNvbnRyb2xsZXJcbiAgZGVmYXVsdEF1ZGlvQ29kZWM6IHVuZGVmaW5lZCxcbiAgLy8gdXNlZCBieSBzdHJlYW0tY29udHJvbGxlclxuICBkZWJ1ZzogZmFsc2UsXG4gIC8vIHVzZWQgYnkgbG9nZ2VyXG4gIGNhcExldmVsT25GUFNEcm9wOiBmYWxzZSxcbiAgLy8gdXNlZCBieSBmcHMtY29udHJvbGxlclxuICBjYXBMZXZlbFRvUGxheWVyU2l6ZTogZmFsc2UsXG4gIC8vIHVzZWQgYnkgY2FwLWxldmVsLWNvbnRyb2xsZXJcbiAgaWdub3JlRGV2aWNlUGl4ZWxSYXRpbzogZmFsc2UsXG4gIC8vIHVzZWQgYnkgY2FwLWxldmVsLWNvbnRyb2xsZXJcbiAgcHJlZmVyTWFuYWdlZE1lZGlhU291cmNlOiB0cnVlLFxuICBpbml0aWFsTGl2ZU1hbmlmZXN0U2l6ZTogMSxcbiAgLy8gdXNlZCBieSBzdHJlYW0tY29udHJvbGxlclxuICBtYXhCdWZmZXJMZW5ndGg6IDMwLFxuICAvLyB1c2VkIGJ5IHN0cmVhbS1jb250cm9sbGVyXG4gIGJhY2tCdWZmZXJMZW5ndGg6IEluZmluaXR5LFxuICAvLyB1c2VkIGJ5IGJ1ZmZlci1jb250cm9sbGVyXG4gIGZyb250QnVmZmVyRmx1c2hUaHJlc2hvbGQ6IEluZmluaXR5LFxuICBtYXhCdWZmZXJTaXplOiA2MCAqIDEwMDAgKiAxMDAwLFxuICAvLyB1c2VkIGJ5IHN0cmVhbS1jb250cm9sbGVyXG4gIG1heEJ1ZmZlckhvbGU6IDAuMSxcbiAgLy8gdXNlZCBieSBzdHJlYW0tY29udHJvbGxlclxuICBoaWdoQnVmZmVyV2F0Y2hkb2dQZXJpb2Q6IDIsXG4gIC8vIHVzZWQgYnkgc3RyZWFtLWNvbnRyb2xsZXJcbiAgbnVkZ2VPZmZzZXQ6IDAuMSxcbiAgLy8gdXNlZCBieSBzdHJlYW0tY29udHJvbGxlclxuICBudWRnZU1heFJldHJ5OiAzLFxuICAvLyB1c2VkIGJ5IHN0cmVhbS1jb250cm9sbGVyXG4gIG1heEZyYWdMb29rVXBUb2xlcmFuY2U6IDAuMjUsXG4gIC8vIHVzZWQgYnkgc3RyZWFtLWNvbnRyb2xsZXJcbiAgbGl2ZVN5bmNEdXJhdGlvbkNvdW50OiAzLFxuICAvLyB1c2VkIGJ5IGxhdGVuY3ktY29udHJvbGxlclxuICBsaXZlTWF4TGF0ZW5jeUR1cmF0aW9uQ291bnQ6IEluZmluaXR5LFxuICAvLyB1c2VkIGJ5IGxhdGVuY3ktY29udHJvbGxlclxuICBsaXZlU3luY0R1cmF0aW9uOiB1bmRlZmluZWQsXG4gIC8vIHVzZWQgYnkgbGF0ZW5jeS1jb250cm9sbGVyXG4gIGxpdmVNYXhMYXRlbmN5RHVyYXRpb246IHVuZGVmaW5lZCxcbiAgLy8gdXNlZCBieSBsYXRlbmN5LWNvbnRyb2xsZXJcbiAgbWF4TGl2ZVN5bmNQbGF5YmFja1JhdGU6IDEsXG4gIC8vIHVzZWQgYnkgbGF0ZW5jeS1jb250cm9sbGVyXG4gIGxpdmVEdXJhdGlvbkluZmluaXR5OiBmYWxzZSxcbiAgLy8gdXNlZCBieSBidWZmZXItY29udHJvbGxlclxuICAvKipcbiAgICogQGRlcHJlY2F0ZWQgdXNlIGJhY2tCdWZmZXJMZW5ndGhcbiAgICovXG4gIGxpdmVCYWNrQnVmZmVyTGVuZ3RoOiBudWxsLFxuICAvLyB1c2VkIGJ5IGJ1ZmZlci1jb250cm9sbGVyXG4gIG1heE1heEJ1ZmZlckxlbmd0aDogNjAwLFxuICAvLyB1c2VkIGJ5IHN0cmVhbS1jb250cm9sbGVyXG4gIGVuYWJsZVdvcmtlcjogdHJ1ZSxcbiAgLy8gdXNlZCBieSB0cmFuc211eGVyXG4gIHdvcmtlclBhdGg6IG51bGwsXG4gIC8vIHVzZWQgYnkgdHJhbnNtdXhlclxuICBlbmFibGVTb2Z0d2FyZUFFUzogdHJ1ZSxcbiAgLy8gdXNlZCBieSBkZWNyeXB0ZXJcbiAgc3RhcnRMZXZlbDogdW5kZWZpbmVkLFxuICAvLyB1c2VkIGJ5IGxldmVsLWNvbnRyb2xsZXJcbiAgc3RhcnRGcmFnUHJlZmV0Y2g6IGZhbHNlLFxuICAvLyB1c2VkIGJ5IHN0cmVhbS1jb250cm9sbGVyXG4gIGZwc0Ryb3BwZWRNb25pdG9yaW5nUGVyaW9kOiA1MDAwLFxuICAvLyB1c2VkIGJ5IGZwcy1jb250cm9sbGVyXG4gIGZwc0Ryb3BwZWRNb25pdG9yaW5nVGhyZXNob2xkOiAwLjIsXG4gIC8vIHVzZWQgYnkgZnBzLWNvbnRyb2xsZXJcbiAgYXBwZW5kRXJyb3JNYXhSZXRyeTogMyxcbiAgLy8gdXNlZCBieSBidWZmZXItY29udHJvbGxlclxuICBsb2FkZXI6IFhockxvYWRlcixcbiAgLy8gbG9hZGVyOiBGZXRjaExvYWRlcixcbiAgZkxvYWRlcjogdW5kZWZpbmVkLFxuICAvLyB1c2VkIGJ5IGZyYWdtZW50LWxvYWRlclxuICBwTG9hZGVyOiB1bmRlZmluZWQsXG4gIC8vIHVzZWQgYnkgcGxheWxpc3QtbG9hZGVyXG4gIHhoclNldHVwOiB1bmRlZmluZWQsXG4gIC8vIHVzZWQgYnkgeGhyLWxvYWRlclxuICBsaWNlbnNlWGhyU2V0dXA6IHVuZGVmaW5lZCxcbiAgLy8gdXNlZCBieSBlbWUtY29udHJvbGxlclxuICBsaWNlbnNlUmVzcG9uc2VDYWxsYmFjazogdW5kZWZpbmVkLFxuICAvLyB1c2VkIGJ5IGVtZS1jb250cm9sbGVyXG4gIGFickNvbnRyb2xsZXI6IEFickNvbnRyb2xsZXIsXG4gIGJ1ZmZlckNvbnRyb2xsZXI6IEJ1ZmZlckNvbnRyb2xsZXIsXG4gIGNhcExldmVsQ29udHJvbGxlcjogQ2FwTGV2ZWxDb250cm9sbGVyLFxuICBlcnJvckNvbnRyb2xsZXI6IEVycm9yQ29udHJvbGxlcixcbiAgZnBzQ29udHJvbGxlcjogRlBTQ29udHJvbGxlcixcbiAgc3RyZXRjaFNob3J0VmlkZW9UcmFjazogZmFsc2UsXG4gIC8vIHVzZWQgYnkgbXA0LXJlbXV4ZXJcbiAgbWF4QXVkaW9GcmFtZXNEcmlmdDogMSxcbiAgLy8gdXNlZCBieSBtcDQtcmVtdXhlclxuICBmb3JjZUtleUZyYW1lT25EaXNjb250aW51aXR5OiB0cnVlLFxuICAvLyB1c2VkIGJ5IHRzLWRlbXV4ZXJcbiAgYWJyRXdtYUZhc3RMaXZlOiAzLFxuICAvLyB1c2VkIGJ5IGFici1jb250cm9sbGVyXG4gIGFickV3bWFTbG93TGl2ZTogOSxcbiAgLy8gdXNlZCBieSBhYnItY29udHJvbGxlclxuICBhYnJFd21hRmFzdFZvRDogMyxcbiAgLy8gdXNlZCBieSBhYnItY29udHJvbGxlclxuICBhYnJFd21hU2xvd1ZvRDogOSxcbiAgLy8gdXNlZCBieSBhYnItY29udHJvbGxlclxuICBhYnJFd21hRGVmYXVsdEVzdGltYXRlOiA1ZTUsXG4gIC8vIDUwMCBrYnBzICAvLyB1c2VkIGJ5IGFici1jb250cm9sbGVyXG4gIGFickV3bWFEZWZhdWx0RXN0aW1hdGVNYXg6IDVlNixcbiAgLy8gNSBtYnBzXG4gIGFickJhbmRXaWR0aEZhY3RvcjogMC45NSxcbiAgLy8gdXNlZCBieSBhYnItY29udHJvbGxlclxuICBhYnJCYW5kV2lkdGhVcEZhY3RvcjogMC43LFxuICAvLyB1c2VkIGJ5IGFici1jb250cm9sbGVyXG4gIGFick1heFdpdGhSZWFsQml0cmF0ZTogZmFsc2UsXG4gIC8vIHVzZWQgYnkgYWJyLWNvbnRyb2xsZXJcbiAgbWF4U3RhcnZhdGlvbkRlbGF5OiA0LFxuICAvLyB1c2VkIGJ5IGFici1jb250cm9sbGVyXG4gIG1heExvYWRpbmdEZWxheTogNCxcbiAgLy8gdXNlZCBieSBhYnItY29udHJvbGxlclxuICBtaW5BdXRvQml0cmF0ZTogMCxcbiAgLy8gdXNlZCBieSBobHNcbiAgZW1lRW5hYmxlZDogZmFsc2UsXG4gIC8vIHVzZWQgYnkgZW1lLWNvbnRyb2xsZXJcbiAgd2lkZXZpbmVMaWNlbnNlVXJsOiB1bmRlZmluZWQsXG4gIC8vIHVzZWQgYnkgZW1lLWNvbnRyb2xsZXJcbiAgZHJtU3lzdGVtczoge30sXG4gIC8vIHVzZWQgYnkgZW1lLWNvbnRyb2xsZXJcbiAgZHJtU3lzdGVtT3B0aW9uczoge30sXG4gIC8vIHVzZWQgYnkgZW1lLWNvbnRyb2xsZXJcbiAgcmVxdWVzdE1lZGlhS2V5U3lzdGVtQWNjZXNzRnVuYzogcmVxdWVzdE1lZGlhS2V5U3lzdGVtQWNjZXNzICxcbiAgLy8gdXNlZCBieSBlbWUtY29udHJvbGxlclxuICB0ZXN0QmFuZHdpZHRoOiB0cnVlLFxuICBwcm9ncmVzc2l2ZTogZmFsc2UsXG4gIGxvd0xhdGVuY3lNb2RlOiB0cnVlLFxuICBjbWNkOiB1bmRlZmluZWQsXG4gIGVuYWJsZURhdGVSYW5nZU1ldGFkYXRhQ3VlczogdHJ1ZSxcbiAgZW5hYmxlRW1zZ01ldGFkYXRhQ3VlczogdHJ1ZSxcbiAgZW5hYmxlSUQzTWV0YWRhdGFDdWVzOiB0cnVlLFxuICB1c2VNZWRpYUNhcGFiaWxpdGllczogdHJ1ZSxcbiAgY2VydExvYWRQb2xpY3k6IHtcbiAgICBkZWZhdWx0OiBkZWZhdWx0TG9hZFBvbGljeVxuICB9LFxuICBrZXlMb2FkUG9saWN5OiB7XG4gICAgZGVmYXVsdDoge1xuICAgICAgbWF4VGltZVRvRmlyc3RCeXRlTXM6IDgwMDAsXG4gICAgICBtYXhMb2FkVGltZU1zOiAyMDAwMCxcbiAgICAgIHRpbWVvdXRSZXRyeToge1xuICAgICAgICBtYXhOdW1SZXRyeTogMSxcbiAgICAgICAgcmV0cnlEZWxheU1zOiAxMDAwLFxuICAgICAgICBtYXhSZXRyeURlbGF5TXM6IDIwMDAwLFxuICAgICAgICBiYWNrb2ZmOiAnbGluZWFyJ1xuICAgICAgfSxcbiAgICAgIGVycm9yUmV0cnk6IHtcbiAgICAgICAgbWF4TnVtUmV0cnk6IDgsXG4gICAgICAgIHJldHJ5RGVsYXlNczogMTAwMCxcbiAgICAgICAgbWF4UmV0cnlEZWxheU1zOiAyMDAwMCxcbiAgICAgICAgYmFja29mZjogJ2xpbmVhcidcbiAgICAgIH1cbiAgICB9XG4gIH0sXG4gIG1hbmlmZXN0TG9hZFBvbGljeToge1xuICAgIGRlZmF1bHQ6IHtcbiAgICAgIG1heFRpbWVUb0ZpcnN0Qnl0ZU1zOiBJbmZpbml0eSxcbiAgICAgIG1heExvYWRUaW1lTXM6IDIwMDAwLFxuICAgICAgdGltZW91dFJldHJ5OiB7XG4gICAgICAgIG1heE51bVJldHJ5OiAyLFxuICAgICAgICByZXRyeURlbGF5TXM6IDAsXG4gICAgICAgIG1heFJldHJ5RGVsYXlNczogMFxuICAgICAgfSxcbiAgICAgIGVycm9yUmV0cnk6IHtcbiAgICAgICAgbWF4TnVtUmV0cnk6IDEsXG4gICAgICAgIHJldHJ5RGVsYXlNczogMTAwMCxcbiAgICAgICAgbWF4UmV0cnlEZWxheU1zOiA4MDAwXG4gICAgICB9XG4gICAgfVxuICB9LFxuICBwbGF5bGlzdExvYWRQb2xpY3k6IHtcbiAgICBkZWZhdWx0OiB7XG4gICAgICBtYXhUaW1lVG9GaXJzdEJ5dGVNczogMTAwMDAsXG4gICAgICBtYXhMb2FkVGltZU1zOiAyMDAwMCxcbiAgICAgIHRpbWVvdXRSZXRyeToge1xuICAgICAgICBtYXhOdW1SZXRyeTogMixcbiAgICAgICAgcmV0cnlEZWxheU1zOiAwLFxuICAgICAgICBtYXhSZXRyeURlbGF5TXM6IDBcbiAgICAgIH0sXG4gICAgICBlcnJvclJldHJ5OiB7XG4gICAgICAgIG1heE51bVJldHJ5OiAyLFxuICAgICAgICByZXRyeURlbGF5TXM6IDEwMDAsXG4gICAgICAgIG1heFJldHJ5RGVsYXlNczogODAwMFxuICAgICAgfVxuICAgIH1cbiAgfSxcbiAgZnJhZ0xvYWRQb2xpY3k6IHtcbiAgICBkZWZhdWx0OiB7XG4gICAgICBtYXhUaW1lVG9GaXJzdEJ5dGVNczogMTAwMDAsXG4gICAgICBtYXhMb2FkVGltZU1zOiAxMjAwMDAsXG4gICAgICB0aW1lb3V0UmV0cnk6IHtcbiAgICAgICAgbWF4TnVtUmV0cnk6IDQsXG4gICAgICAgIHJldHJ5RGVsYXlNczogMCxcbiAgICAgICAgbWF4UmV0cnlEZWxheU1zOiAwXG4gICAgICB9LFxuICAgICAgZXJyb3JSZXRyeToge1xuICAgICAgICBtYXhOdW1SZXRyeTogNixcbiAgICAgICAgcmV0cnlEZWxheU1zOiAxMDAwLFxuICAgICAgICBtYXhSZXRyeURlbGF5TXM6IDgwMDBcbiAgICAgIH1cbiAgICB9XG4gIH0sXG4gIHN0ZWVyaW5nTWFuaWZlc3RMb2FkUG9saWN5OiB7XG4gICAgZGVmYXVsdDoge1xuICAgICAgbWF4VGltZVRvRmlyc3RCeXRlTXM6IDEwMDAwLFxuICAgICAgbWF4TG9hZFRpbWVNczogMjAwMDAsXG4gICAgICB0aW1lb3V0UmV0cnk6IHtcbiAgICAgICAgbWF4TnVtUmV0cnk6IDIsXG4gICAgICAgIHJldHJ5RGVsYXlNczogMCxcbiAgICAgICAgbWF4UmV0cnlEZWxheU1zOiAwXG4gICAgICB9LFxuICAgICAgZXJyb3JSZXRyeToge1xuICAgICAgICBtYXhOdW1SZXRyeTogMSxcbiAgICAgICAgcmV0cnlEZWxheU1zOiAxMDAwLFxuICAgICAgICBtYXhSZXRyeURlbGF5TXM6IDgwMDBcbiAgICAgIH1cbiAgICB9IFxuICB9LFxuICAvLyBUaGVzZSBkZWZhdWx0IHNldHRpbmdzIGFyZSBkZXByZWNhdGVkIGluIGZhdm9yIG9mIHRoZSBhYm92ZSBwb2xpY2llc1xuICAvLyBhbmQgYXJlIG1haW50YWluZWQgZm9yIGJhY2t3YXJkcyBjb21wYXRpYmlsaXR5XG4gIG1hbmlmZXN0TG9hZGluZ1RpbWVPdXQ6IDEwMDAwLFxuICBtYW5pZmVzdExvYWRpbmdNYXhSZXRyeTogMSxcbiAgbWFuaWZlc3RMb2FkaW5nUmV0cnlEZWxheTogMTAwMCxcbiAgbWFuaWZlc3RMb2FkaW5nTWF4UmV0cnlUaW1lb3V0OiA2NDAwMCxcbiAgbGV2ZWxMb2FkaW5nVGltZU91dDogMTAwMDAsXG4gIGxldmVsTG9hZGluZ01heFJldHJ5OiA0LFxuICBsZXZlbExvYWRpbmdSZXRyeURlbGF5OiAxMDAwLFxuICBsZXZlbExvYWRpbmdNYXhSZXRyeVRpbWVvdXQ6IDY0MDAwLFxuICBmcmFnTG9hZGluZ1RpbWVPdXQ6IDIwMDAwLFxuICBmcmFnTG9hZGluZ01heFJldHJ5OiA2LFxuICBmcmFnTG9hZGluZ1JldHJ5RGVsYXk6IDEwMDAsXG4gIGZyYWdMb2FkaW5nTWF4UmV0cnlUaW1lb3V0OiA2NDAwMFxufSwgdGltZWxpbmVDb25maWcoKSksIHt9LCB7XG4gIHN1YnRpdGxlU3RyZWFtQ29udHJvbGxlcjogU3VidGl0bGVTdHJlYW1Db250cm9sbGVyICxcbiAgc3VidGl0bGVUcmFja0NvbnRyb2xsZXI6IFN1YnRpdGxlVHJhY2tDb250cm9sbGVyICxcbiAgdGltZWxpbmVDb250cm9sbGVyOiBUaW1lbGluZUNvbnRyb2xsZXIgLFxuICBhdWRpb1N0cmVhbUNvbnRyb2xsZXI6IEF1ZGlvU3RyZWFtQ29udHJvbGxlciAsXG4gIGF1ZGlvVHJhY2tDb250cm9sbGVyOiBBdWRpb1RyYWNrQ29udHJvbGxlciAsXG4gIGVtZUNvbnRyb2xsZXI6IEVNRUNvbnRyb2xsZXIgLFxuICBjbWNkQ29udHJvbGxlcjogQ01DRENvbnRyb2xsZXIgLFxuICBjb250ZW50U3RlZXJpbmdDb250cm9sbGVyOiBDb250ZW50U3RlZXJpbmdDb250cm9sbGVyIFxufSk7XG5mdW5jdGlvbiB0aW1lbGluZUNvbmZpZygpIHtcbiAgcmV0dXJuIHtcbiAgICBjdWVIYW5kbGVyOiBDdWVzLFxuICAgIC8vIHVzZWQgYnkgdGltZWxpbmUtY29udHJvbGxlclxuICAgIGVuYWJsZVdlYlZUVDogdHJ1ZSxcbiAgICAvLyB1c2VkIGJ5IHRpbWVsaW5lLWNvbnRyb2xsZXJcbiAgICBlbmFibGVJTVNDMTogdHJ1ZSxcbiAgICAvLyB1c2VkIGJ5IHRpbWVsaW5lLWNvbnRyb2xsZXJcbiAgICBlbmFibGVDRUE3MDhDYXB0aW9uczogdHJ1ZSxcbiAgICAvLyB1c2VkIGJ5IHRpbWVsaW5lLWNvbnRyb2xsZXJcbiAgICBjYXB0aW9uc1RleHRUcmFjazFMYWJlbDogJ0VuZ2xpc2gnLFxuICAgIC8vIHVzZWQgYnkgdGltZWxpbmUtY29udHJvbGxlclxuICAgIGNhcHRpb25zVGV4dFRyYWNrMUxhbmd1YWdlQ29kZTogJ2VuJyxcbiAgICAvLyB1c2VkIGJ5IHRpbWVsaW5lLWNvbnRyb2xsZXJcbiAgICBjYXB0aW9uc1RleHRUcmFjazJMYWJlbDogJ1NwYW5pc2gnLFxuICAgIC8vIHVzZWQgYnkgdGltZWxpbmUtY29udHJvbGxlclxuICAgIGNhcHRpb25zVGV4dFRyYWNrMkxhbmd1YWdlQ29kZTogJ2VzJyxcbiAgICAvLyB1c2VkIGJ5IHRpbWVsaW5lLWNvbnRyb2xsZXJcbiAgICBjYXB0aW9uc1RleHRUcmFjazNMYWJlbDogJ1Vua25vd24gQ0MnLFxuICAgIC8vIHVzZWQgYnkgdGltZWxpbmUtY29udHJvbGxlclxuICAgIGNhcHRpb25zVGV4dFRyYWNrM0xhbmd1YWdlQ29kZTogJycsXG4gICAgLy8gdXNlZCBieSB0aW1lbGluZS1jb250cm9sbGVyXG4gICAgY2FwdGlvbnNUZXh0VHJhY2s0TGFiZWw6ICdVbmtub3duIENDJyxcbiAgICAvLyB1c2VkIGJ5IHRpbWVsaW5lLWNvbnRyb2xsZXJcbiAgICBjYXB0aW9uc1RleHRUcmFjazRMYW5ndWFnZUNvZGU6ICcnLFxuICAgIC8vIHVzZWQgYnkgdGltZWxpbmUtY29udHJvbGxlclxuICAgIHJlbmRlclRleHRUcmFja3NOYXRpdmVseTogdHJ1ZVxuICB9O1xufVxuXG4vKipcbiAqIEBpZ25vcmVcbiAqL1xuZnVuY3Rpb24gbWVyZ2VDb25maWcoZGVmYXVsdENvbmZpZywgdXNlckNvbmZpZykge1xuICBpZiAoKHVzZXJDb25maWcubGl2ZVN5bmNEdXJhdGlvbkNvdW50IHx8IHVzZXJDb25maWcubGl2ZU1heExhdGVuY3lEdXJhdGlvbkNvdW50KSAmJiAodXNlckNvbmZpZy5saXZlU3luY0R1cmF0aW9uIHx8IHVzZXJDb25maWcubGl2ZU1heExhdGVuY3lEdXJhdGlvbikpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXCJJbGxlZ2FsIGhscy5qcyBjb25maWc6IGRvbid0IG1peCB1cCBsaXZlU3luY0R1cmF0aW9uQ291bnQvbGl2ZU1heExhdGVuY3lEdXJhdGlvbkNvdW50IGFuZCBsaXZlU3luY0R1cmF0aW9uL2xpdmVNYXhMYXRlbmN5RHVyYXRpb25cIik7XG4gIH1cbiAgaWYgKHVzZXJDb25maWcubGl2ZU1heExhdGVuY3lEdXJhdGlvbkNvdW50ICE9PSB1bmRlZmluZWQgJiYgKHVzZXJDb25maWcubGl2ZVN5bmNEdXJhdGlvbkNvdW50ID09PSB1bmRlZmluZWQgfHwgdXNlckNvbmZpZy5saXZlTWF4TGF0ZW5jeUR1cmF0aW9uQ291bnQgPD0gdXNlckNvbmZpZy5saXZlU3luY0R1cmF0aW9uQ291bnQpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdJbGxlZ2FsIGhscy5qcyBjb25maWc6IFwibGl2ZU1heExhdGVuY3lEdXJhdGlvbkNvdW50XCIgbXVzdCBiZSBncmVhdGVyIHRoYW4gXCJsaXZlU3luY0R1cmF0aW9uQ291bnRcIicpO1xuICB9XG4gIGlmICh1c2VyQ29uZmlnLmxpdmVNYXhMYXRlbmN5RHVyYXRpb24gIT09IHVuZGVmaW5lZCAmJiAodXNlckNvbmZpZy5saXZlU3luY0R1cmF0aW9uID09PSB1bmRlZmluZWQgfHwgdXNlckNvbmZpZy5saXZlTWF4TGF0ZW5jeUR1cmF0aW9uIDw9IHVzZXJDb25maWcubGl2ZVN5bmNEdXJhdGlvbikpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0lsbGVnYWwgaGxzLmpzIGNvbmZpZzogXCJsaXZlTWF4TGF0ZW5jeUR1cmF0aW9uXCIgbXVzdCBiZSBncmVhdGVyIHRoYW4gXCJsaXZlU3luY0R1cmF0aW9uXCInKTtcbiAgfVxuICBjb25zdCBkZWZhdWx0c0NvcHkgPSBkZWVwQ3B5KGRlZmF1bHRDb25maWcpO1xuXG4gIC8vIEJhY2t3YXJkcyBjb21wYXRpYmlsaXR5IHdpdGggZGVwcmVjYXRlZCBjb25maWcgdmFsdWVzXG4gIGNvbnN0IGRlcHJlY2F0ZWRTZXR0aW5nVHlwZXMgPSBbJ21hbmlmZXN0JywgJ2xldmVsJywgJ2ZyYWcnXTtcbiAgY29uc3QgZGVwcmVjYXRlZFNldHRpbmdzID0gWydUaW1lT3V0JywgJ01heFJldHJ5JywgJ1JldHJ5RGVsYXknLCAnTWF4UmV0cnlUaW1lb3V0J107XG4gIGRlcHJlY2F0ZWRTZXR0aW5nVHlwZXMuZm9yRWFjaCh0eXBlID0+IHtcbiAgICBjb25zdCBwb2xpY3lOYW1lID0gYCR7dHlwZSA9PT0gJ2xldmVsJyA/ICdwbGF5bGlzdCcgOiB0eXBlfUxvYWRQb2xpY3lgO1xuICAgIGNvbnN0IHBvbGljeU5vdFNldCA9IHVzZXJDb25maWdbcG9saWN5TmFtZV0gPT09IHVuZGVmaW5lZDtcbiAgICBjb25zdCByZXBvcnQgPSBbXTtcbiAgICBkZXByZWNhdGVkU2V0dGluZ3MuZm9yRWFjaChzZXR0aW5nID0+IHtcbiAgICAgIGNvbnN0IGRlcHJlY2F0ZWRTZXR0aW5nID0gYCR7dHlwZX1Mb2FkaW5nJHtzZXR0aW5nfWA7XG4gICAgICBjb25zdCB2YWx1ZSA9IHVzZXJDb25maWdbZGVwcmVjYXRlZFNldHRpbmddO1xuICAgICAgaWYgKHZhbHVlICE9PSB1bmRlZmluZWQgJiYgcG9saWN5Tm90U2V0KSB7XG4gICAgICAgIHJlcG9ydC5wdXNoKGRlcHJlY2F0ZWRTZXR0aW5nKTtcbiAgICAgICAgY29uc3Qgc2V0dGluZ3MgPSBkZWZhdWx0c0NvcHlbcG9saWN5TmFtZV0uZGVmYXVsdDtcbiAgICAgICAgdXNlckNvbmZpZ1twb2xpY3lOYW1lXSA9IHtcbiAgICAgICAgICBkZWZhdWx0OiBzZXR0aW5nc1xuICAgICAgICB9O1xuICAgICAgICBzd2l0Y2ggKHNldHRpbmcpIHtcbiAgICAgICAgICBjYXNlICdUaW1lT3V0JzpcbiAgICAgICAgICAgIHNldHRpbmdzLm1heExvYWRUaW1lTXMgPSB2YWx1ZTtcbiAgICAgICAgICAgIHNldHRpbmdzLm1heFRpbWVUb0ZpcnN0Qnl0ZU1zID0gdmFsdWU7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBjYXNlICdNYXhSZXRyeSc6XG4gICAgICAgICAgICBzZXR0aW5ncy5lcnJvclJldHJ5Lm1heE51bVJldHJ5ID0gdmFsdWU7XG4gICAgICAgICAgICBzZXR0aW5ncy50aW1lb3V0UmV0cnkubWF4TnVtUmV0cnkgPSB2YWx1ZTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgJ1JldHJ5RGVsYXknOlxuICAgICAgICAgICAgc2V0dGluZ3MuZXJyb3JSZXRyeS5yZXRyeURlbGF5TXMgPSB2YWx1ZTtcbiAgICAgICAgICAgIHNldHRpbmdzLnRpbWVvdXRSZXRyeS5yZXRyeURlbGF5TXMgPSB2YWx1ZTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgJ01heFJldHJ5VGltZW91dCc6XG4gICAgICAgICAgICBzZXR0aW5ncy5lcnJvclJldHJ5Lm1heFJldHJ5RGVsYXlNcyA9IHZhbHVlO1xuICAgICAgICAgICAgc2V0dGluZ3MudGltZW91dFJldHJ5Lm1heFJldHJ5RGVsYXlNcyA9IHZhbHVlO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcbiAgICBpZiAocmVwb3J0Lmxlbmd0aCkge1xuICAgICAgbG9nZ2VyLndhcm4oYGhscy5qcyBjb25maWc6IFwiJHtyZXBvcnQuam9pbignXCIsIFwiJyl9XCIgc2V0dGluZyhzKSBhcmUgZGVwcmVjYXRlZCwgdXNlIFwiJHtwb2xpY3lOYW1lfVwiOiAke0pTT04uc3RyaW5naWZ5KHVzZXJDb25maWdbcG9saWN5TmFtZV0pfWApO1xuICAgIH1cbiAgfSk7XG4gIHJldHVybiBfb2JqZWN0U3ByZWFkMihfb2JqZWN0U3ByZWFkMih7fSwgZGVmYXVsdHNDb3B5KSwgdXNlckNvbmZpZyk7XG59XG5mdW5jdGlvbiBkZWVwQ3B5KG9iaikge1xuICBpZiAob2JqICYmIHR5cGVvZiBvYmogPT09ICdvYmplY3QnKSB7XG4gICAgaWYgKEFycmF5LmlzQXJyYXkob2JqKSkge1xuICAgICAgcmV0dXJuIG9iai5tYXAoZGVlcENweSk7XG4gICAgfVxuICAgIHJldHVybiBPYmplY3Qua2V5cyhvYmopLnJlZHVjZSgocmVzdWx0LCBrZXkpID0+IHtcbiAgICAgIHJlc3VsdFtrZXldID0gZGVlcENweShvYmpba2V5XSk7XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH0sIHt9KTtcbiAgfVxuICByZXR1cm4gb2JqO1xufVxuXG4vKipcbiAqIEBpZ25vcmVcbiAqL1xuZnVuY3Rpb24gZW5hYmxlU3RyZWFtaW5nTW9kZShjb25maWcpIHtcbiAgY29uc3QgY3VycmVudExvYWRlciA9IGNvbmZpZy5sb2FkZXI7XG4gIGlmIChjdXJyZW50TG9hZGVyICE9PSBGZXRjaExvYWRlciAmJiBjdXJyZW50TG9hZGVyICE9PSBYaHJMb2FkZXIpIHtcbiAgICAvLyBJZiBhIGRldmVsb3BlciBoYXMgY29uZmlndXJlZCB0aGVpciBvd24gbG9hZGVyLCByZXNwZWN0IHRoYXQgY2hvaWNlXG4gICAgbG9nZ2VyLmxvZygnW2NvbmZpZ106IEN1c3RvbSBsb2FkZXIgZGV0ZWN0ZWQsIGNhbm5vdCBlbmFibGUgcHJvZ3Jlc3NpdmUgc3RyZWFtaW5nJyk7XG4gICAgY29uZmlnLnByb2dyZXNzaXZlID0gZmFsc2U7XG4gIH0gZWxzZSB7XG4gICAgY29uc3QgY2FuU3RyZWFtUHJvZ3Jlc3NpdmVseSA9IGZldGNoU3VwcG9ydGVkKCk7XG4gICAgaWYgKGNhblN0cmVhbVByb2dyZXNzaXZlbHkpIHtcbiAgICAgIGNvbmZpZy5sb2FkZXIgPSBGZXRjaExvYWRlcjtcbiAgICAgIGNvbmZpZy5wcm9ncmVzc2l2ZSA9IHRydWU7XG4gICAgICBjb25maWcuZW5hYmxlU29mdHdhcmVBRVMgPSB0cnVlO1xuICAgICAgbG9nZ2VyLmxvZygnW2NvbmZpZ106IFByb2dyZXNzaXZlIHN0cmVhbWluZyBlbmFibGVkLCB1c2luZyBGZXRjaExvYWRlcicpO1xuICAgIH1cbiAgfVxufVxuXG5sZXQgY2hyb21lT3JGaXJlZm94O1xuY2xhc3MgTGV2ZWxDb250cm9sbGVyIGV4dGVuZHMgQmFzZVBsYXlsaXN0Q29udHJvbGxlciB7XG4gIGNvbnN0cnVjdG9yKGhscywgY29udGVudFN0ZWVyaW5nQ29udHJvbGxlcikge1xuICAgIHN1cGVyKGhscywgJ1tsZXZlbC1jb250cm9sbGVyXScpO1xuICAgIHRoaXMuX2xldmVscyA9IFtdO1xuICAgIHRoaXMuX2ZpcnN0TGV2ZWwgPSAtMTtcbiAgICB0aGlzLl9tYXhBdXRvTGV2ZWwgPSAtMTtcbiAgICB0aGlzLl9zdGFydExldmVsID0gdm9pZCAwO1xuICAgIHRoaXMuY3VycmVudExldmVsID0gbnVsbDtcbiAgICB0aGlzLmN1cnJlbnRMZXZlbEluZGV4ID0gLTE7XG4gICAgdGhpcy5tYW51YWxMZXZlbEluZGV4ID0gLTE7XG4gICAgdGhpcy5zdGVlcmluZyA9IHZvaWQgMDtcbiAgICB0aGlzLm9uUGFyc2VkQ29tcGxldGUgPSB2b2lkIDA7XG4gICAgdGhpcy5zdGVlcmluZyA9IGNvbnRlbnRTdGVlcmluZ0NvbnRyb2xsZXI7XG4gICAgdGhpcy5fcmVnaXN0ZXJMaXN0ZW5lcnMoKTtcbiAgfVxuICBfcmVnaXN0ZXJMaXN0ZW5lcnMoKSB7XG4gICAgY29uc3Qge1xuICAgICAgaGxzXG4gICAgfSA9IHRoaXM7XG4gICAgaGxzLm9uKEV2ZW50cy5NQU5JRkVTVF9MT0FESU5HLCB0aGlzLm9uTWFuaWZlc3RMb2FkaW5nLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLk1BTklGRVNUX0xPQURFRCwgdGhpcy5vbk1hbmlmZXN0TG9hZGVkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkxFVkVMX0xPQURFRCwgdGhpcy5vbkxldmVsTG9hZGVkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkxFVkVMU19VUERBVEVELCB0aGlzLm9uTGV2ZWxzVXBkYXRlZCwgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5GUkFHX0JVRkZFUkVELCB0aGlzLm9uRnJhZ0J1ZmZlcmVkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkVSUk9SLCB0aGlzLm9uRXJyb3IsIHRoaXMpO1xuICB9XG4gIF91bnJlZ2lzdGVyTGlzdGVuZXJzKCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGhsc1xuICAgIH0gPSB0aGlzO1xuICAgIGhscy5vZmYoRXZlbnRzLk1BTklGRVNUX0xPQURJTkcsIHRoaXMub25NYW5pZmVzdExvYWRpbmcsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLk1BTklGRVNUX0xPQURFRCwgdGhpcy5vbk1hbmlmZXN0TG9hZGVkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5MRVZFTF9MT0FERUQsIHRoaXMub25MZXZlbExvYWRlZCwgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuTEVWRUxTX1VQREFURUQsIHRoaXMub25MZXZlbHNVcGRhdGVkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5GUkFHX0JVRkZFUkVELCB0aGlzLm9uRnJhZ0J1ZmZlcmVkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5FUlJPUiwgdGhpcy5vbkVycm9yLCB0aGlzKTtcbiAgfVxuICBkZXN0cm95KCkge1xuICAgIHRoaXMuX3VucmVnaXN0ZXJMaXN0ZW5lcnMoKTtcbiAgICB0aGlzLnN0ZWVyaW5nID0gbnVsbDtcbiAgICB0aGlzLnJlc2V0TGV2ZWxzKCk7XG4gICAgc3VwZXIuZGVzdHJveSgpO1xuICB9XG4gIHN0b3BMb2FkKCkge1xuICAgIGNvbnN0IGxldmVscyA9IHRoaXMuX2xldmVscztcblxuICAgIC8vIGNsZWFuIHVwIGxpdmUgbGV2ZWwgZGV0YWlscyB0byBmb3JjZSByZWxvYWQgdGhlbSwgYW5kIHJlc2V0IGxvYWQgZXJyb3JzXG4gICAgbGV2ZWxzLmZvckVhY2gobGV2ZWwgPT4ge1xuICAgICAgbGV2ZWwubG9hZEVycm9yID0gMDtcbiAgICAgIGxldmVsLmZyYWdtZW50RXJyb3IgPSAwO1xuICAgIH0pO1xuICAgIHN1cGVyLnN0b3BMb2FkKCk7XG4gIH1cbiAgcmVzZXRMZXZlbHMoKSB7XG4gICAgdGhpcy5fc3RhcnRMZXZlbCA9IHVuZGVmaW5lZDtcbiAgICB0aGlzLm1hbnVhbExldmVsSW5kZXggPSAtMTtcbiAgICB0aGlzLmN1cnJlbnRMZXZlbEluZGV4ID0gLTE7XG4gICAgdGhpcy5jdXJyZW50TGV2ZWwgPSBudWxsO1xuICAgIHRoaXMuX2xldmVscyA9IFtdO1xuICAgIHRoaXMuX21heEF1dG9MZXZlbCA9IC0xO1xuICB9XG4gIG9uTWFuaWZlc3RMb2FkaW5nKGV2ZW50LCBkYXRhKSB7XG4gICAgdGhpcy5yZXNldExldmVscygpO1xuICB9XG4gIG9uTWFuaWZlc3RMb2FkZWQoZXZlbnQsIGRhdGEpIHtcbiAgICBjb25zdCBwcmVmZXJNYW5hZ2VkTWVkaWFTb3VyY2UgPSB0aGlzLmhscy5jb25maWcucHJlZmVyTWFuYWdlZE1lZGlhU291cmNlO1xuICAgIGNvbnN0IGxldmVscyA9IFtdO1xuICAgIGNvbnN0IHJlZHVuZGFudFNldCA9IHt9O1xuICAgIGNvbnN0IGdlbmVyYXRlUGF0aHdheVNldCA9IHt9O1xuICAgIGxldCByZXNvbHV0aW9uRm91bmQgPSBmYWxzZTtcbiAgICBsZXQgdmlkZW9Db2RlY0ZvdW5kID0gZmFsc2U7XG4gICAgbGV0IGF1ZGlvQ29kZWNGb3VuZCA9IGZhbHNlO1xuICAgIGRhdGEubGV2ZWxzLmZvckVhY2gobGV2ZWxQYXJzZWQgPT4ge1xuICAgICAgdmFyIF9hdWRpb0NvZGVjLCBfdmlkZW9Db2RlYztcbiAgICAgIGNvbnN0IGF0dHJpYnV0ZXMgPSBsZXZlbFBhcnNlZC5hdHRycztcblxuICAgICAgLy8gZXJhc2UgYXVkaW8gY29kZWMgaW5mbyBpZiBicm93c2VyIGRvZXMgbm90IHN1cHBvcnQgbXA0YS40MC4zNC5cbiAgICAgIC8vIGRlbXV4ZXIgd2lsbCBhdXRvZGV0ZWN0IGNvZGVjIGFuZCBmYWxsYmFjayB0byBtcGVnL2F1ZGlvXG4gICAgICBsZXQge1xuICAgICAgICBhdWRpb0NvZGVjLFxuICAgICAgICB2aWRlb0NvZGVjXG4gICAgICB9ID0gbGV2ZWxQYXJzZWQ7XG4gICAgICBpZiAoKChfYXVkaW9Db2RlYyA9IGF1ZGlvQ29kZWMpID09IG51bGwgPyB2b2lkIDAgOiBfYXVkaW9Db2RlYy5pbmRleE9mKCdtcDRhLjQwLjM0JykpICE9PSAtMSkge1xuICAgICAgICBjaHJvbWVPckZpcmVmb3ggfHwgKGNocm9tZU9yRmlyZWZveCA9IC9jaHJvbWV8ZmlyZWZveC9pLnRlc3QobmF2aWdhdG9yLnVzZXJBZ2VudCkpO1xuICAgICAgICBpZiAoY2hyb21lT3JGaXJlZm94KSB7XG4gICAgICAgICAgbGV2ZWxQYXJzZWQuYXVkaW9Db2RlYyA9IGF1ZGlvQ29kZWMgPSB1bmRlZmluZWQ7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmIChhdWRpb0NvZGVjKSB7XG4gICAgICAgIGxldmVsUGFyc2VkLmF1ZGlvQ29kZWMgPSBhdWRpb0NvZGVjID0gZ2V0Q29kZWNDb21wYXRpYmxlTmFtZShhdWRpb0NvZGVjLCBwcmVmZXJNYW5hZ2VkTWVkaWFTb3VyY2UpO1xuICAgICAgfVxuICAgICAgaWYgKCgoX3ZpZGVvQ29kZWMgPSB2aWRlb0NvZGVjKSA9PSBudWxsID8gdm9pZCAwIDogX3ZpZGVvQ29kZWMuaW5kZXhPZignYXZjMScpKSA9PT0gMCkge1xuICAgICAgICB2aWRlb0NvZGVjID0gbGV2ZWxQYXJzZWQudmlkZW9Db2RlYyA9IGNvbnZlcnRBVkMxVG9BVkNPVEkodmlkZW9Db2RlYyk7XG4gICAgICB9XG5cbiAgICAgIC8vIG9ubHkga2VlcCBsZXZlbHMgd2l0aCBzdXBwb3J0ZWQgYXVkaW8vdmlkZW8gY29kZWNzXG4gICAgICBjb25zdCB7XG4gICAgICAgIHdpZHRoLFxuICAgICAgICBoZWlnaHQsXG4gICAgICAgIHVua25vd25Db2RlY3NcbiAgICAgIH0gPSBsZXZlbFBhcnNlZDtcbiAgICAgIHJlc29sdXRpb25Gb3VuZCB8fCAocmVzb2x1dGlvbkZvdW5kID0gISEod2lkdGggJiYgaGVpZ2h0KSk7XG4gICAgICB2aWRlb0NvZGVjRm91bmQgfHwgKHZpZGVvQ29kZWNGb3VuZCA9ICEhdmlkZW9Db2RlYyk7XG4gICAgICBhdWRpb0NvZGVjRm91bmQgfHwgKGF1ZGlvQ29kZWNGb3VuZCA9ICEhYXVkaW9Db2RlYyk7XG4gICAgICBpZiAodW5rbm93bkNvZGVjcyAhPSBudWxsICYmIHVua25vd25Db2RlY3MubGVuZ3RoIHx8IGF1ZGlvQ29kZWMgJiYgIWFyZUNvZGVjc01lZGlhU291cmNlU3VwcG9ydGVkKGF1ZGlvQ29kZWMsICdhdWRpbycsIHByZWZlck1hbmFnZWRNZWRpYVNvdXJjZSkgfHwgdmlkZW9Db2RlYyAmJiAhYXJlQ29kZWNzTWVkaWFTb3VyY2VTdXBwb3J0ZWQodmlkZW9Db2RlYywgJ3ZpZGVvJywgcHJlZmVyTWFuYWdlZE1lZGlhU291cmNlKSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjb25zdCB7XG4gICAgICAgIENPREVDUyxcbiAgICAgICAgJ0ZSQU1FLVJBVEUnOiBGUkFNRVJBVEUsXG4gICAgICAgICdIRENQLUxFVkVMJzogSERDUCxcbiAgICAgICAgJ1BBVEhXQVktSUQnOiBQQVRIV0FZLFxuICAgICAgICBSRVNPTFVUSU9OLFxuICAgICAgICAnVklERU8tUkFOR0UnOiBWSURFT19SQU5HRVxuICAgICAgfSA9IGF0dHJpYnV0ZXM7XG4gICAgICBjb25zdCBjb250ZW50U3RlZXJpbmdQcmVmaXggPSBgJHtQQVRIV0FZIHx8ICcuJ30tYDtcbiAgICAgIGNvbnN0IGxldmVsS2V5ID0gYCR7Y29udGVudFN0ZWVyaW5nUHJlZml4fSR7bGV2ZWxQYXJzZWQuYml0cmF0ZX0tJHtSRVNPTFVUSU9OfS0ke0ZSQU1FUkFURX0tJHtDT0RFQ1N9LSR7VklERU9fUkFOR0V9LSR7SERDUH1gO1xuICAgICAgaWYgKCFyZWR1bmRhbnRTZXRbbGV2ZWxLZXldKSB7XG4gICAgICAgIGNvbnN0IGxldmVsID0gbmV3IExldmVsKGxldmVsUGFyc2VkKTtcbiAgICAgICAgcmVkdW5kYW50U2V0W2xldmVsS2V5XSA9IGxldmVsO1xuICAgICAgICBnZW5lcmF0ZVBhdGh3YXlTZXRbbGV2ZWxLZXldID0gMTtcbiAgICAgICAgbGV2ZWxzLnB1c2gobGV2ZWwpO1xuICAgICAgfSBlbHNlIGlmIChyZWR1bmRhbnRTZXRbbGV2ZWxLZXldLnVyaSAhPT0gbGV2ZWxQYXJzZWQudXJsICYmICFsZXZlbFBhcnNlZC5hdHRyc1snUEFUSFdBWS1JRCddKSB7XG4gICAgICAgIC8vIEFzc2lnbiBQYXRod2F5IElEcyB0byBSZWR1bmRhbnQgU3RyZWFtcyAoZGVmYXVsdCBQYXRod2F5cyBpcyBcIi5cIi4gUmVkdW5kYW50IFN0cmVhbXMgXCIuLlwiLCBcIi4uLlwiLCBhbmQgc28gb24uKVxuICAgICAgICAvLyBDb250ZW50IFN0ZWVyaW5nIGNvbnRyb2xsZXIgdG8gaGFuZGxlcyBQYXRod2F5IGZhbGxiYWNrIG9uIGVycm9yXG4gICAgICAgIGNvbnN0IHBhdGh3YXlDb3VudCA9IGdlbmVyYXRlUGF0aHdheVNldFtsZXZlbEtleV0gKz0gMTtcbiAgICAgICAgbGV2ZWxQYXJzZWQuYXR0cnNbJ1BBVEhXQVktSUQnXSA9IG5ldyBBcnJheShwYXRod2F5Q291bnQgKyAxKS5qb2luKCcuJyk7XG4gICAgICAgIGNvbnN0IGxldmVsID0gbmV3IExldmVsKGxldmVsUGFyc2VkKTtcbiAgICAgICAgcmVkdW5kYW50U2V0W2xldmVsS2V5XSA9IGxldmVsO1xuICAgICAgICBsZXZlbHMucHVzaChsZXZlbCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZWR1bmRhbnRTZXRbbGV2ZWxLZXldLmFkZEdyb3VwSWQoJ2F1ZGlvJywgYXR0cmlidXRlcy5BVURJTyk7XG4gICAgICAgIHJlZHVuZGFudFNldFtsZXZlbEtleV0uYWRkR3JvdXBJZCgndGV4dCcsIGF0dHJpYnV0ZXMuU1VCVElUTEVTKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICB0aGlzLmZpbHRlckFuZFNvcnRNZWRpYU9wdGlvbnMobGV2ZWxzLCBkYXRhLCByZXNvbHV0aW9uRm91bmQsIHZpZGVvQ29kZWNGb3VuZCwgYXVkaW9Db2RlY0ZvdW5kKTtcbiAgfVxuICBmaWx0ZXJBbmRTb3J0TWVkaWFPcHRpb25zKGZpbHRlcmVkTGV2ZWxzLCBkYXRhLCByZXNvbHV0aW9uRm91bmQsIHZpZGVvQ29kZWNGb3VuZCwgYXVkaW9Db2RlY0ZvdW5kKSB7XG4gICAgbGV0IGF1ZGlvVHJhY2tzID0gW107XG4gICAgbGV0IHN1YnRpdGxlVHJhY2tzID0gW107XG4gICAgbGV0IGxldmVscyA9IGZpbHRlcmVkTGV2ZWxzO1xuXG4gICAgLy8gcmVtb3ZlIGF1ZGlvLW9ubHkgYW5kIGludmFsaWQgdmlkZW8tcmFuZ2UgbGV2ZWxzIGlmIHdlIGFsc28gaGF2ZSBsZXZlbHMgd2l0aCB2aWRlbyBjb2RlY3Mgb3IgUkVTT0xVVElPTiBzaWduYWxsZWRcbiAgICBpZiAoKHJlc29sdXRpb25Gb3VuZCB8fCB2aWRlb0NvZGVjRm91bmQpICYmIGF1ZGlvQ29kZWNGb3VuZCkge1xuICAgICAgbGV2ZWxzID0gbGV2ZWxzLmZpbHRlcigoe1xuICAgICAgICB2aWRlb0NvZGVjLFxuICAgICAgICB2aWRlb1JhbmdlLFxuICAgICAgICB3aWR0aCxcbiAgICAgICAgaGVpZ2h0XG4gICAgICB9KSA9PiAoISF2aWRlb0NvZGVjIHx8ICEhKHdpZHRoICYmIGhlaWdodCkpICYmIGlzVmlkZW9SYW5nZSh2aWRlb1JhbmdlKSk7XG4gICAgfVxuICAgIGlmIChsZXZlbHMubGVuZ3RoID09PSAwKSB7XG4gICAgICAvLyBEaXNwYXRjaCBlcnJvciBhZnRlciBNQU5JRkVTVF9MT0FERUQgaXMgZG9uZSBwcm9wYWdhdGluZ1xuICAgICAgUHJvbWlzZS5yZXNvbHZlKCkudGhlbigoKSA9PiB7XG4gICAgICAgIGlmICh0aGlzLmhscykge1xuICAgICAgICAgIGlmIChkYXRhLmxldmVscy5sZW5ndGgpIHtcbiAgICAgICAgICAgIHRoaXMud2FybihgT25lIG9yIG1vcmUgQ09ERUNTIGluIHZhcmlhbnQgbm90IHN1cHBvcnRlZDogJHtKU09OLnN0cmluZ2lmeShkYXRhLmxldmVsc1swXS5hdHRycyl9YCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnN0IGVycm9yID0gbmV3IEVycm9yKCdubyBsZXZlbCB3aXRoIGNvbXBhdGlibGUgY29kZWNzIGZvdW5kIGluIG1hbmlmZXN0Jyk7XG4gICAgICAgICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuRVJST1IsIHtcbiAgICAgICAgICAgIHR5cGU6IEVycm9yVHlwZXMuTUVESUFfRVJST1IsXG4gICAgICAgICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuTUFOSUZFU1RfSU5DT01QQVRJQkxFX0NPREVDU19FUlJPUixcbiAgICAgICAgICAgIGZhdGFsOiB0cnVlLFxuICAgICAgICAgICAgdXJsOiBkYXRhLnVybCxcbiAgICAgICAgICAgIGVycm9yLFxuICAgICAgICAgICAgcmVhc29uOiBlcnJvci5tZXNzYWdlXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAoZGF0YS5hdWRpb1RyYWNrcykge1xuICAgICAgY29uc3Qge1xuICAgICAgICBwcmVmZXJNYW5hZ2VkTWVkaWFTb3VyY2VcbiAgICAgIH0gPSB0aGlzLmhscy5jb25maWc7XG4gICAgICBhdWRpb1RyYWNrcyA9IGRhdGEuYXVkaW9UcmFja3MuZmlsdGVyKHRyYWNrID0+ICF0cmFjay5hdWRpb0NvZGVjIHx8IGFyZUNvZGVjc01lZGlhU291cmNlU3VwcG9ydGVkKHRyYWNrLmF1ZGlvQ29kZWMsICdhdWRpbycsIHByZWZlck1hbmFnZWRNZWRpYVNvdXJjZSkpO1xuICAgICAgLy8gQXNzaWduIGlkcyBhZnRlciBmaWx0ZXJpbmcgYXMgYXJyYXkgaW5kaWNlcyBieSBncm91cC1pZFxuICAgICAgYXNzaWduVHJhY2tJZHNCeUdyb3VwKGF1ZGlvVHJhY2tzKTtcbiAgICB9XG4gICAgaWYgKGRhdGEuc3VidGl0bGVzKSB7XG4gICAgICBzdWJ0aXRsZVRyYWNrcyA9IGRhdGEuc3VidGl0bGVzO1xuICAgICAgYXNzaWduVHJhY2tJZHNCeUdyb3VwKHN1YnRpdGxlVHJhY2tzKTtcbiAgICB9XG4gICAgLy8gc3RhcnQgYml0cmF0ZSBpcyB0aGUgZmlyc3QgYml0cmF0ZSBvZiB0aGUgbWFuaWZlc3RcbiAgICBjb25zdCB1bnNvcnRlZExldmVscyA9IGxldmVscy5zbGljZSgwKTtcbiAgICAvLyBzb3J0IGxldmVscyBmcm9tIGxvd2VzdCB0byBoaWdoZXN0XG4gICAgbGV2ZWxzLnNvcnQoKGEsIGIpID0+IHtcbiAgICAgIGlmIChhLmF0dHJzWydIRENQLUxFVkVMJ10gIT09IGIuYXR0cnNbJ0hEQ1AtTEVWRUwnXSkge1xuICAgICAgICByZXR1cm4gKGEuYXR0cnNbJ0hEQ1AtTEVWRUwnXSB8fCAnJykgPiAoYi5hdHRyc1snSERDUC1MRVZFTCddIHx8ICcnKSA/IDEgOiAtMTtcbiAgICAgIH1cbiAgICAgIC8vIHNvcnQgb24gaGVpZ2h0IGJlZm9yZSBiaXRyYXRlIGZvciBjYXAtbGV2ZWwtY29udHJvbGxlclxuICAgICAgaWYgKHJlc29sdXRpb25Gb3VuZCAmJiBhLmhlaWdodCAhPT0gYi5oZWlnaHQpIHtcbiAgICAgICAgcmV0dXJuIGEuaGVpZ2h0IC0gYi5oZWlnaHQ7XG4gICAgICB9XG4gICAgICBpZiAoYS5mcmFtZVJhdGUgIT09IGIuZnJhbWVSYXRlKSB7XG4gICAgICAgIHJldHVybiBhLmZyYW1lUmF0ZSAtIGIuZnJhbWVSYXRlO1xuICAgICAgfVxuICAgICAgaWYgKGEudmlkZW9SYW5nZSAhPT0gYi52aWRlb1JhbmdlKSB7XG4gICAgICAgIHJldHVybiBWaWRlb1JhbmdlVmFsdWVzLmluZGV4T2YoYS52aWRlb1JhbmdlKSAtIFZpZGVvUmFuZ2VWYWx1ZXMuaW5kZXhPZihiLnZpZGVvUmFuZ2UpO1xuICAgICAgfVxuICAgICAgaWYgKGEudmlkZW9Db2RlYyAhPT0gYi52aWRlb0NvZGVjKSB7XG4gICAgICAgIGNvbnN0IHZhbHVlQSA9IHZpZGVvQ29kZWNQcmVmZXJlbmNlVmFsdWUoYS52aWRlb0NvZGVjKTtcbiAgICAgICAgY29uc3QgdmFsdWVCID0gdmlkZW9Db2RlY1ByZWZlcmVuY2VWYWx1ZShiLnZpZGVvQ29kZWMpO1xuICAgICAgICBpZiAodmFsdWVBICE9PSB2YWx1ZUIpIHtcbiAgICAgICAgICByZXR1cm4gdmFsdWVCIC0gdmFsdWVBO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoYS51cmkgPT09IGIudXJpICYmIGEuY29kZWNTZXQgIT09IGIuY29kZWNTZXQpIHtcbiAgICAgICAgY29uc3QgdmFsdWVBID0gY29kZWNzU2V0U2VsZWN0aW9uUHJlZmVyZW5jZVZhbHVlKGEuY29kZWNTZXQpO1xuICAgICAgICBjb25zdCB2YWx1ZUIgPSBjb2RlY3NTZXRTZWxlY3Rpb25QcmVmZXJlbmNlVmFsdWUoYi5jb2RlY1NldCk7XG4gICAgICAgIGlmICh2YWx1ZUEgIT09IHZhbHVlQikge1xuICAgICAgICAgIHJldHVybiB2YWx1ZUIgLSB2YWx1ZUE7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmIChhLmF2ZXJhZ2VCaXRyYXRlICE9PSBiLmF2ZXJhZ2VCaXRyYXRlKSB7XG4gICAgICAgIHJldHVybiBhLmF2ZXJhZ2VCaXRyYXRlIC0gYi5hdmVyYWdlQml0cmF0ZTtcbiAgICAgIH1cbiAgICAgIHJldHVybiAwO1xuICAgIH0pO1xuICAgIGxldCBmaXJzdExldmVsSW5QbGF5bGlzdCA9IHVuc29ydGVkTGV2ZWxzWzBdO1xuICAgIGlmICh0aGlzLnN0ZWVyaW5nKSB7XG4gICAgICBsZXZlbHMgPSB0aGlzLnN0ZWVyaW5nLmZpbHRlclBhcnNlZExldmVscyhsZXZlbHMpO1xuICAgICAgaWYgKGxldmVscy5sZW5ndGggIT09IHVuc29ydGVkTGV2ZWxzLmxlbmd0aCkge1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHVuc29ydGVkTGV2ZWxzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgaWYgKHVuc29ydGVkTGV2ZWxzW2ldLnBhdGh3YXlJZCA9PT0gbGV2ZWxzWzBdLnBhdGh3YXlJZCkge1xuICAgICAgICAgICAgZmlyc3RMZXZlbEluUGxheWxpc3QgPSB1bnNvcnRlZExldmVsc1tpXTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICB0aGlzLl9sZXZlbHMgPSBsZXZlbHM7XG5cbiAgICAvLyBmaW5kIGluZGV4IG9mIGZpcnN0IGxldmVsIGluIHNvcnRlZCBsZXZlbHNcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGxldmVscy5sZW5ndGg7IGkrKykge1xuICAgICAgaWYgKGxldmVsc1tpXSA9PT0gZmlyc3RMZXZlbEluUGxheWxpc3QpIHtcbiAgICAgICAgdmFyIF90aGlzJGhscyR1c2VyQ29uZmlnO1xuICAgICAgICB0aGlzLl9maXJzdExldmVsID0gaTtcbiAgICAgICAgY29uc3QgZmlyc3RMZXZlbEJpdHJhdGUgPSBmaXJzdExldmVsSW5QbGF5bGlzdC5iaXRyYXRlO1xuICAgICAgICBjb25zdCBiYW5kd2lkdGhFc3RpbWF0ZSA9IHRoaXMuaGxzLmJhbmR3aWR0aEVzdGltYXRlO1xuICAgICAgICB0aGlzLmxvZyhgbWFuaWZlc3QgbG9hZGVkLCAke2xldmVscy5sZW5ndGh9IGxldmVsKHMpIGZvdW5kLCBmaXJzdCBiaXRyYXRlOiAke2ZpcnN0TGV2ZWxCaXRyYXRlfWApO1xuICAgICAgICAvLyBVcGRhdGUgZGVmYXVsdCBid2UgdG8gZmlyc3QgdmFyaWFudCBiaXRyYXRlIGFzIGxvbmcgaXQgaGFzIG5vdCBiZWVuIGNvbmZpZ3VyZWQgb3Igc2V0XG4gICAgICAgIGlmICgoKF90aGlzJGhscyR1c2VyQ29uZmlnID0gdGhpcy5obHMudXNlckNvbmZpZykgPT0gbnVsbCA/IHZvaWQgMCA6IF90aGlzJGhscyR1c2VyQ29uZmlnLmFickV3bWFEZWZhdWx0RXN0aW1hdGUpID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICBjb25zdCBzdGFydGluZ0J3RXN0aW1hdGUgPSBNYXRoLm1pbihmaXJzdExldmVsQml0cmF0ZSwgdGhpcy5obHMuY29uZmlnLmFickV3bWFEZWZhdWx0RXN0aW1hdGVNYXgpO1xuICAgICAgICAgIGlmIChzdGFydGluZ0J3RXN0aW1hdGUgPiBiYW5kd2lkdGhFc3RpbWF0ZSAmJiBiYW5kd2lkdGhFc3RpbWF0ZSA9PT0gaGxzRGVmYXVsdENvbmZpZy5hYnJFd21hRGVmYXVsdEVzdGltYXRlKSB7XG4gICAgICAgICAgICB0aGlzLmhscy5iYW5kd2lkdGhFc3RpbWF0ZSA9IHN0YXJ0aW5nQndFc3RpbWF0ZTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gQXVkaW8gaXMgb25seSBhbHRlcm5hdGUgaWYgbWFuaWZlc3QgaW5jbHVkZSBhIFVSSSBhbG9uZyB3aXRoIHRoZSBhdWRpbyBncm91cCB0YWcsXG4gICAgLy8gYW5kIHRoaXMgaXMgbm90IGFuIGF1ZGlvLW9ubHkgc3RyZWFtIHdoZXJlIGxldmVscyBjb250YWluIGF1ZGlvLW9ubHlcbiAgICBjb25zdCBhdWRpb09ubHkgPSBhdWRpb0NvZGVjRm91bmQgJiYgIXZpZGVvQ29kZWNGb3VuZDtcbiAgICBjb25zdCBlZGF0YSA9IHtcbiAgICAgIGxldmVscyxcbiAgICAgIGF1ZGlvVHJhY2tzLFxuICAgICAgc3VidGl0bGVUcmFja3MsXG4gICAgICBzZXNzaW9uRGF0YTogZGF0YS5zZXNzaW9uRGF0YSxcbiAgICAgIHNlc3Npb25LZXlzOiBkYXRhLnNlc3Npb25LZXlzLFxuICAgICAgZmlyc3RMZXZlbDogdGhpcy5fZmlyc3RMZXZlbCxcbiAgICAgIHN0YXRzOiBkYXRhLnN0YXRzLFxuICAgICAgYXVkaW86IGF1ZGlvQ29kZWNGb3VuZCxcbiAgICAgIHZpZGVvOiB2aWRlb0NvZGVjRm91bmQsXG4gICAgICBhbHRBdWRpbzogIWF1ZGlvT25seSAmJiBhdWRpb1RyYWNrcy5zb21lKHQgPT4gISF0LnVybClcbiAgICB9O1xuICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLk1BTklGRVNUX1BBUlNFRCwgZWRhdGEpO1xuXG4gICAgLy8gSW5pdGlhdGUgbG9hZGluZyBhZnRlciBhbGwgY29udHJvbGxlcnMgaGF2ZSByZWNlaXZlZCBNQU5JRkVTVF9QQVJTRURcbiAgICBpZiAodGhpcy5obHMuY29uZmlnLmF1dG9TdGFydExvYWQgfHwgdGhpcy5obHMuZm9yY2VTdGFydExvYWQpIHtcbiAgICAgIHRoaXMuaGxzLnN0YXJ0TG9hZCh0aGlzLmhscy5jb25maWcuc3RhcnRQb3NpdGlvbik7XG4gICAgfVxuICB9XG4gIGdldCBsZXZlbHMoKSB7XG4gICAgaWYgKHRoaXMuX2xldmVscy5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fbGV2ZWxzO1xuICB9XG4gIGdldCBsZXZlbCgpIHtcbiAgICByZXR1cm4gdGhpcy5jdXJyZW50TGV2ZWxJbmRleDtcbiAgfVxuICBzZXQgbGV2ZWwobmV3TGV2ZWwpIHtcbiAgICBjb25zdCBsZXZlbHMgPSB0aGlzLl9sZXZlbHM7XG4gICAgaWYgKGxldmVscy5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgLy8gY2hlY2sgaWYgbGV2ZWwgaWR4IGlzIHZhbGlkXG4gICAgaWYgKG5ld0xldmVsIDwgMCB8fCBuZXdMZXZlbCA+PSBsZXZlbHMubGVuZ3RoKSB7XG4gICAgICAvLyBpbnZhbGlkIGxldmVsIGlkIGdpdmVuLCB0cmlnZ2VyIGVycm9yXG4gICAgICBjb25zdCBlcnJvciA9IG5ldyBFcnJvcignaW52YWxpZCBsZXZlbCBpZHgnKTtcbiAgICAgIGNvbnN0IGZhdGFsID0gbmV3TGV2ZWwgPCAwO1xuICAgICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuRVJST1IsIHtcbiAgICAgICAgdHlwZTogRXJyb3JUeXBlcy5PVEhFUl9FUlJPUixcbiAgICAgICAgZGV0YWlsczogRXJyb3JEZXRhaWxzLkxFVkVMX1NXSVRDSF9FUlJPUixcbiAgICAgICAgbGV2ZWw6IG5ld0xldmVsLFxuICAgICAgICBmYXRhbCxcbiAgICAgICAgZXJyb3IsXG4gICAgICAgIHJlYXNvbjogZXJyb3IubWVzc2FnZVxuICAgICAgfSk7XG4gICAgICBpZiAoZmF0YWwpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgbmV3TGV2ZWwgPSBNYXRoLm1pbihuZXdMZXZlbCwgbGV2ZWxzLmxlbmd0aCAtIDEpO1xuICAgIH1cbiAgICBjb25zdCBsYXN0TGV2ZWxJbmRleCA9IHRoaXMuY3VycmVudExldmVsSW5kZXg7XG4gICAgY29uc3QgbGFzdExldmVsID0gdGhpcy5jdXJyZW50TGV2ZWw7XG4gICAgY29uc3QgbGFzdFBhdGh3YXlJZCA9IGxhc3RMZXZlbCA/IGxhc3RMZXZlbC5hdHRyc1snUEFUSFdBWS1JRCddIDogdW5kZWZpbmVkO1xuICAgIGNvbnN0IGxldmVsID0gbGV2ZWxzW25ld0xldmVsXTtcbiAgICBjb25zdCBwYXRod2F5SWQgPSBsZXZlbC5hdHRyc1snUEFUSFdBWS1JRCddO1xuICAgIHRoaXMuY3VycmVudExldmVsSW5kZXggPSBuZXdMZXZlbDtcbiAgICB0aGlzLmN1cnJlbnRMZXZlbCA9IGxldmVsO1xuICAgIGlmIChsYXN0TGV2ZWxJbmRleCA9PT0gbmV3TGV2ZWwgJiYgbGV2ZWwuZGV0YWlscyAmJiBsYXN0TGV2ZWwgJiYgbGFzdFBhdGh3YXlJZCA9PT0gcGF0aHdheUlkKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMubG9nKGBTd2l0Y2hpbmcgdG8gbGV2ZWwgJHtuZXdMZXZlbH0gKCR7bGV2ZWwuaGVpZ2h0ID8gbGV2ZWwuaGVpZ2h0ICsgJ3AgJyA6ICcnfSR7bGV2ZWwudmlkZW9SYW5nZSA/IGxldmVsLnZpZGVvUmFuZ2UgKyAnICcgOiAnJ30ke2xldmVsLmNvZGVjU2V0ID8gbGV2ZWwuY29kZWNTZXQgKyAnICcgOiAnJ31AJHtsZXZlbC5iaXRyYXRlfSkke3BhdGh3YXlJZCA/ICcgd2l0aCBQYXRod2F5ICcgKyBwYXRod2F5SWQgOiAnJ30gZnJvbSBsZXZlbCAke2xhc3RMZXZlbEluZGV4fSR7bGFzdFBhdGh3YXlJZCA/ICcgd2l0aCBQYXRod2F5ICcgKyBsYXN0UGF0aHdheUlkIDogJyd9YCk7XG4gICAgY29uc3QgbGV2ZWxTd2l0Y2hpbmdEYXRhID0ge1xuICAgICAgbGV2ZWw6IG5ld0xldmVsLFxuICAgICAgYXR0cnM6IGxldmVsLmF0dHJzLFxuICAgICAgZGV0YWlsczogbGV2ZWwuZGV0YWlscyxcbiAgICAgIGJpdHJhdGU6IGxldmVsLmJpdHJhdGUsXG4gICAgICBhdmVyYWdlQml0cmF0ZTogbGV2ZWwuYXZlcmFnZUJpdHJhdGUsXG4gICAgICBtYXhCaXRyYXRlOiBsZXZlbC5tYXhCaXRyYXRlLFxuICAgICAgcmVhbEJpdHJhdGU6IGxldmVsLnJlYWxCaXRyYXRlLFxuICAgICAgd2lkdGg6IGxldmVsLndpZHRoLFxuICAgICAgaGVpZ2h0OiBsZXZlbC5oZWlnaHQsXG4gICAgICBjb2RlY1NldDogbGV2ZWwuY29kZWNTZXQsXG4gICAgICBhdWRpb0NvZGVjOiBsZXZlbC5hdWRpb0NvZGVjLFxuICAgICAgdmlkZW9Db2RlYzogbGV2ZWwudmlkZW9Db2RlYyxcbiAgICAgIGF1ZGlvR3JvdXBzOiBsZXZlbC5hdWRpb0dyb3VwcyxcbiAgICAgIHN1YnRpdGxlR3JvdXBzOiBsZXZlbC5zdWJ0aXRsZUdyb3VwcyxcbiAgICAgIGxvYWRlZDogbGV2ZWwubG9hZGVkLFxuICAgICAgbG9hZEVycm9yOiBsZXZlbC5sb2FkRXJyb3IsXG4gICAgICBmcmFnbWVudEVycm9yOiBsZXZlbC5mcmFnbWVudEVycm9yLFxuICAgICAgbmFtZTogbGV2ZWwubmFtZSxcbiAgICAgIGlkOiBsZXZlbC5pZCxcbiAgICAgIHVyaTogbGV2ZWwudXJpLFxuICAgICAgdXJsOiBsZXZlbC51cmwsXG4gICAgICB1cmxJZDogMCxcbiAgICAgIGF1ZGlvR3JvdXBJZHM6IGxldmVsLmF1ZGlvR3JvdXBJZHMsXG4gICAgICB0ZXh0R3JvdXBJZHM6IGxldmVsLnRleHRHcm91cElkc1xuICAgIH07XG4gICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuTEVWRUxfU1dJVENISU5HLCBsZXZlbFN3aXRjaGluZ0RhdGEpO1xuICAgIC8vIGNoZWNrIGlmIHdlIG5lZWQgdG8gbG9hZCBwbGF5bGlzdCBmb3IgdGhpcyBsZXZlbFxuICAgIGNvbnN0IGxldmVsRGV0YWlscyA9IGxldmVsLmRldGFpbHM7XG4gICAgaWYgKCFsZXZlbERldGFpbHMgfHwgbGV2ZWxEZXRhaWxzLmxpdmUpIHtcbiAgICAgIC8vIGxldmVsIG5vdCByZXRyaWV2ZWQgeWV0LCBvciBsaXZlIHBsYXlsaXN0IHdlIG5lZWQgdG8gKHJlKWxvYWQgaXRcbiAgICAgIGNvbnN0IGhsc1VybFBhcmFtZXRlcnMgPSB0aGlzLnN3aXRjaFBhcmFtcyhsZXZlbC51cmksIGxhc3RMZXZlbCA9PSBudWxsID8gdm9pZCAwIDogbGFzdExldmVsLmRldGFpbHMsIGxldmVsRGV0YWlscyk7XG4gICAgICB0aGlzLmxvYWRQbGF5bGlzdChobHNVcmxQYXJhbWV0ZXJzKTtcbiAgICB9XG4gIH1cbiAgZ2V0IG1hbnVhbExldmVsKCkge1xuICAgIHJldHVybiB0aGlzLm1hbnVhbExldmVsSW5kZXg7XG4gIH1cbiAgc2V0IG1hbnVhbExldmVsKG5ld0xldmVsKSB7XG4gICAgdGhpcy5tYW51YWxMZXZlbEluZGV4ID0gbmV3TGV2ZWw7XG4gICAgaWYgKHRoaXMuX3N0YXJ0TGV2ZWwgPT09IHVuZGVmaW5lZCkge1xuICAgICAgdGhpcy5fc3RhcnRMZXZlbCA9IG5ld0xldmVsO1xuICAgIH1cbiAgICBpZiAobmV3TGV2ZWwgIT09IC0xKSB7XG4gICAgICB0aGlzLmxldmVsID0gbmV3TGV2ZWw7XG4gICAgfVxuICB9XG4gIGdldCBmaXJzdExldmVsKCkge1xuICAgIHJldHVybiB0aGlzLl9maXJzdExldmVsO1xuICB9XG4gIHNldCBmaXJzdExldmVsKG5ld0xldmVsKSB7XG4gICAgdGhpcy5fZmlyc3RMZXZlbCA9IG5ld0xldmVsO1xuICB9XG4gIGdldCBzdGFydExldmVsKCkge1xuICAgIC8vIFNldHRpbmcgaGxzLnN0YXJ0TGV2ZWwgKHRoaXMuX3N0YXJ0TGV2ZWwpIG92ZXJyaWRlcyBjb25maWcuc3RhcnRMZXZlbFxuICAgIGlmICh0aGlzLl9zdGFydExldmVsID09PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbnN0IGNvbmZpZ1N0YXJ0TGV2ZWwgPSB0aGlzLmhscy5jb25maWcuc3RhcnRMZXZlbDtcbiAgICAgIGlmIChjb25maWdTdGFydExldmVsICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIGNvbmZpZ1N0YXJ0TGV2ZWw7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5obHMuZmlyc3RBdXRvTGV2ZWw7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLl9zdGFydExldmVsO1xuICB9XG4gIHNldCBzdGFydExldmVsKG5ld0xldmVsKSB7XG4gICAgdGhpcy5fc3RhcnRMZXZlbCA9IG5ld0xldmVsO1xuICB9XG4gIG9uRXJyb3IoZXZlbnQsIGRhdGEpIHtcbiAgICBpZiAoZGF0YS5mYXRhbCB8fCAhZGF0YS5jb250ZXh0KSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChkYXRhLmNvbnRleHQudHlwZSA9PT0gUGxheWxpc3RDb250ZXh0VHlwZS5MRVZFTCAmJiBkYXRhLmNvbnRleHQubGV2ZWwgPT09IHRoaXMubGV2ZWwpIHtcbiAgICAgIHRoaXMuY2hlY2tSZXRyeShkYXRhKTtcbiAgICB9XG4gIH1cblxuICAvLyByZXNldCBlcnJvcnMgb24gdGhlIHN1Y2Nlc3NmdWwgbG9hZCBvZiBhIGZyYWdtZW50XG4gIG9uRnJhZ0J1ZmZlcmVkKGV2ZW50LCB7XG4gICAgZnJhZ1xuICB9KSB7XG4gICAgaWYgKGZyYWcgIT09IHVuZGVmaW5lZCAmJiBmcmFnLnR5cGUgPT09IFBsYXlsaXN0TGV2ZWxUeXBlLk1BSU4pIHtcbiAgICAgIGNvbnN0IGVsID0gZnJhZy5lbGVtZW50YXJ5U3RyZWFtcztcbiAgICAgIGlmICghT2JqZWN0LmtleXMoZWwpLnNvbWUodHlwZSA9PiAhIWVsW3R5cGVdKSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjb25zdCBsZXZlbCA9IHRoaXMuX2xldmVsc1tmcmFnLmxldmVsXTtcbiAgICAgIGlmIChsZXZlbCAhPSBudWxsICYmIGxldmVsLmxvYWRFcnJvcikge1xuICAgICAgICB0aGlzLmxvZyhgUmVzZXR0aW5nIGxldmVsIGVycm9yIGNvdW50IG9mICR7bGV2ZWwubG9hZEVycm9yfSBvbiBmcmFnIGJ1ZmZlcmVkYCk7XG4gICAgICAgIGxldmVsLmxvYWRFcnJvciA9IDA7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIG9uTGV2ZWxMb2FkZWQoZXZlbnQsIGRhdGEpIHtcbiAgICB2YXIgX2RhdGEkZGVsaXZlcnlEaXJlY3RpMjtcbiAgICBjb25zdCB7XG4gICAgICBsZXZlbCxcbiAgICAgIGRldGFpbHNcbiAgICB9ID0gZGF0YTtcbiAgICBjb25zdCBjdXJMZXZlbCA9IHRoaXMuX2xldmVsc1tsZXZlbF07XG4gICAgaWYgKCFjdXJMZXZlbCkge1xuICAgICAgdmFyIF9kYXRhJGRlbGl2ZXJ5RGlyZWN0aTtcbiAgICAgIHRoaXMud2FybihgSW52YWxpZCBsZXZlbCBpbmRleCAke2xldmVsfWApO1xuICAgICAgaWYgKChfZGF0YSRkZWxpdmVyeURpcmVjdGkgPSBkYXRhLmRlbGl2ZXJ5RGlyZWN0aXZlcykgIT0gbnVsbCAmJiBfZGF0YSRkZWxpdmVyeURpcmVjdGkuc2tpcCkge1xuICAgICAgICBkZXRhaWxzLmRlbHRhVXBkYXRlRmFpbGVkID0gdHJ1ZTtcbiAgICAgIH1cbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBvbmx5IHByb2Nlc3MgbGV2ZWwgbG9hZGVkIGV2ZW50cyBtYXRjaGluZyB3aXRoIGV4cGVjdGVkIGxldmVsXG4gICAgaWYgKGxldmVsID09PSB0aGlzLmN1cnJlbnRMZXZlbEluZGV4KSB7XG4gICAgICAvLyByZXNldCBsZXZlbCBsb2FkIGVycm9yIGNvdW50ZXIgb24gc3VjY2Vzc2Z1bCBsZXZlbCBsb2FkZWQgb25seSBpZiB0aGVyZSBpcyBubyBpc3N1ZXMgd2l0aCBmcmFnbWVudHNcbiAgICAgIGlmIChjdXJMZXZlbC5mcmFnbWVudEVycm9yID09PSAwKSB7XG4gICAgICAgIGN1ckxldmVsLmxvYWRFcnJvciA9IDA7XG4gICAgICB9XG4gICAgICB0aGlzLnBsYXlsaXN0TG9hZGVkKGxldmVsLCBkYXRhLCBjdXJMZXZlbC5kZXRhaWxzKTtcbiAgICB9IGVsc2UgaWYgKChfZGF0YSRkZWxpdmVyeURpcmVjdGkyID0gZGF0YS5kZWxpdmVyeURpcmVjdGl2ZXMpICE9IG51bGwgJiYgX2RhdGEkZGVsaXZlcnlEaXJlY3RpMi5za2lwKSB7XG4gICAgICAvLyByZWNlaXZlZCBhIGRlbHRhIHBsYXlsaXN0IHVwZGF0ZSB0aGF0IGNhbm5vdCBiZSBtZXJnZWRcbiAgICAgIGRldGFpbHMuZGVsdGFVcGRhdGVGYWlsZWQgPSB0cnVlO1xuICAgIH1cbiAgfVxuICBsb2FkUGxheWxpc3QoaGxzVXJsUGFyYW1ldGVycykge1xuICAgIHN1cGVyLmxvYWRQbGF5bGlzdCgpO1xuICAgIGNvbnN0IGN1cnJlbnRMZXZlbEluZGV4ID0gdGhpcy5jdXJyZW50TGV2ZWxJbmRleDtcbiAgICBjb25zdCBjdXJyZW50TGV2ZWwgPSB0aGlzLmN1cnJlbnRMZXZlbDtcbiAgICBpZiAoY3VycmVudExldmVsICYmIHRoaXMuc2hvdWxkTG9hZFBsYXlsaXN0KGN1cnJlbnRMZXZlbCkpIHtcbiAgICAgIGxldCB1cmwgPSBjdXJyZW50TGV2ZWwudXJpO1xuICAgICAgaWYgKGhsc1VybFBhcmFtZXRlcnMpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICB1cmwgPSBobHNVcmxQYXJhbWV0ZXJzLmFkZERpcmVjdGl2ZXModXJsKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICB0aGlzLndhcm4oYENvdWxkIG5vdCBjb25zdHJ1Y3QgbmV3IFVSTCB3aXRoIEhMUyBEZWxpdmVyeSBEaXJlY3RpdmVzOiAke2Vycm9yfWApO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBjb25zdCBwYXRod2F5SWQgPSBjdXJyZW50TGV2ZWwuYXR0cnNbJ1BBVEhXQVktSUQnXTtcbiAgICAgIHRoaXMubG9nKGBMb2FkaW5nIGxldmVsIGluZGV4ICR7Y3VycmVudExldmVsSW5kZXh9JHsoaGxzVXJsUGFyYW1ldGVycyA9PSBudWxsID8gdm9pZCAwIDogaGxzVXJsUGFyYW1ldGVycy5tc24pICE9PSB1bmRlZmluZWQgPyAnIGF0IHNuICcgKyBobHNVcmxQYXJhbWV0ZXJzLm1zbiArICcgcGFydCAnICsgaGxzVXJsUGFyYW1ldGVycy5wYXJ0IDogJyd9IHdpdGgke3BhdGh3YXlJZCA/ICcgUGF0aHdheSAnICsgcGF0aHdheUlkIDogJyd9ICR7dXJsfWApO1xuXG4gICAgICAvLyBjb25zb2xlLmxvZygnQ3VycmVudCBhdWRpbyB0cmFjayBncm91cCBJRDonLCB0aGlzLmhscy5hdWRpb1RyYWNrc1t0aGlzLmhscy5hdWRpb1RyYWNrXS5ncm91cElkKTtcbiAgICAgIC8vIGNvbnNvbGUubG9nKCdOZXcgdmlkZW8gcXVhbGl0eSBsZXZlbCBhdWRpbyBncm91cCBpZDonLCBsZXZlbE9iamVjdC5hdHRycy5BVURJTywgbGV2ZWwpO1xuICAgICAgdGhpcy5jbGVhclRpbWVyKCk7XG4gICAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5MRVZFTF9MT0FESU5HLCB7XG4gICAgICAgIHVybCxcbiAgICAgICAgbGV2ZWw6IGN1cnJlbnRMZXZlbEluZGV4LFxuICAgICAgICBwYXRod2F5SWQ6IGN1cnJlbnRMZXZlbC5hdHRyc1snUEFUSFdBWS1JRCddLFxuICAgICAgICBpZDogMCxcbiAgICAgICAgLy8gRGVwcmVjYXRlZCBMZXZlbCB1cmxJZFxuICAgICAgICBkZWxpdmVyeURpcmVjdGl2ZXM6IGhsc1VybFBhcmFtZXRlcnMgfHwgbnVsbFxuICAgICAgfSk7XG4gICAgfVxuICB9XG4gIGdldCBuZXh0TG9hZExldmVsKCkge1xuICAgIGlmICh0aGlzLm1hbnVhbExldmVsSW5kZXggIT09IC0xKSB7XG4gICAgICByZXR1cm4gdGhpcy5tYW51YWxMZXZlbEluZGV4O1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gdGhpcy5obHMubmV4dEF1dG9MZXZlbDtcbiAgICB9XG4gIH1cbiAgc2V0IG5leHRMb2FkTGV2ZWwobmV4dExldmVsKSB7XG4gICAgdGhpcy5sZXZlbCA9IG5leHRMZXZlbDtcbiAgICBpZiAodGhpcy5tYW51YWxMZXZlbEluZGV4ID09PSAtMSkge1xuICAgICAgdGhpcy5obHMubmV4dEF1dG9MZXZlbCA9IG5leHRMZXZlbDtcbiAgICB9XG4gIH1cbiAgcmVtb3ZlTGV2ZWwobGV2ZWxJbmRleCkge1xuICAgIHZhciBfdGhpcyRjdXJyZW50TGV2ZWw7XG4gICAgY29uc3QgbGV2ZWxzID0gdGhpcy5fbGV2ZWxzLmZpbHRlcigobGV2ZWwsIGluZGV4KSA9PiB7XG4gICAgICBpZiAoaW5kZXggIT09IGxldmVsSW5kZXgpIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgICBpZiAodGhpcy5zdGVlcmluZykge1xuICAgICAgICB0aGlzLnN0ZWVyaW5nLnJlbW92ZUxldmVsKGxldmVsKTtcbiAgICAgIH1cbiAgICAgIGlmIChsZXZlbCA9PT0gdGhpcy5jdXJyZW50TGV2ZWwpIHtcbiAgICAgICAgdGhpcy5jdXJyZW50TGV2ZWwgPSBudWxsO1xuICAgICAgICB0aGlzLmN1cnJlbnRMZXZlbEluZGV4ID0gLTE7XG4gICAgICAgIGlmIChsZXZlbC5kZXRhaWxzKSB7XG4gICAgICAgICAgbGV2ZWwuZGV0YWlscy5mcmFnbWVudHMuZm9yRWFjaChmID0+IGYubGV2ZWwgPSAtMSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9KTtcbiAgICByZWFzc2lnbkZyYWdtZW50TGV2ZWxJbmRleGVzKGxldmVscyk7XG4gICAgdGhpcy5fbGV2ZWxzID0gbGV2ZWxzO1xuICAgIGlmICh0aGlzLmN1cnJlbnRMZXZlbEluZGV4ID4gLTEgJiYgKF90aGlzJGN1cnJlbnRMZXZlbCA9IHRoaXMuY3VycmVudExldmVsKSAhPSBudWxsICYmIF90aGlzJGN1cnJlbnRMZXZlbC5kZXRhaWxzKSB7XG4gICAgICB0aGlzLmN1cnJlbnRMZXZlbEluZGV4ID0gdGhpcy5jdXJyZW50TGV2ZWwuZGV0YWlscy5mcmFnbWVudHNbMF0ubGV2ZWw7XG4gICAgfVxuICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLkxFVkVMU19VUERBVEVELCB7XG4gICAgICBsZXZlbHNcbiAgICB9KTtcbiAgfVxuICBvbkxldmVsc1VwZGF0ZWQoZXZlbnQsIHtcbiAgICBsZXZlbHNcbiAgfSkge1xuICAgIHRoaXMuX2xldmVscyA9IGxldmVscztcbiAgfVxuICBjaGVja01heEF1dG9VcGRhdGVkKCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGF1dG9MZXZlbENhcHBpbmcsXG4gICAgICBtYXhBdXRvTGV2ZWwsXG4gICAgICBtYXhIZGNwTGV2ZWxcbiAgICB9ID0gdGhpcy5obHM7XG4gICAgaWYgKHRoaXMuX21heEF1dG9MZXZlbCAhPT0gbWF4QXV0b0xldmVsKSB7XG4gICAgICB0aGlzLl9tYXhBdXRvTGV2ZWwgPSBtYXhBdXRvTGV2ZWw7XG4gICAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5NQVhfQVVUT19MRVZFTF9VUERBVEVELCB7XG4gICAgICAgIGF1dG9MZXZlbENhcHBpbmcsXG4gICAgICAgIGxldmVsczogdGhpcy5sZXZlbHMsXG4gICAgICAgIG1heEF1dG9MZXZlbCxcbiAgICAgICAgbWluQXV0b0xldmVsOiB0aGlzLmhscy5taW5BdXRvTGV2ZWwsXG4gICAgICAgIG1heEhkY3BMZXZlbFxuICAgICAgfSk7XG4gICAgfVxuICB9XG59XG5mdW5jdGlvbiBhc3NpZ25UcmFja0lkc0J5R3JvdXAodHJhY2tzKSB7XG4gIGNvbnN0IGdyb3VwcyA9IHt9O1xuICB0cmFja3MuZm9yRWFjaCh0cmFjayA9PiB7XG4gICAgY29uc3QgZ3JvdXBJZCA9IHRyYWNrLmdyb3VwSWQgfHwgJyc7XG4gICAgdHJhY2suaWQgPSBncm91cHNbZ3JvdXBJZF0gPSBncm91cHNbZ3JvdXBJZF0gfHwgMDtcbiAgICBncm91cHNbZ3JvdXBJZF0rKztcbiAgfSk7XG59XG5cbmNsYXNzIEtleUxvYWRlciB7XG4gIGNvbnN0cnVjdG9yKGNvbmZpZykge1xuICAgIHRoaXMuY29uZmlnID0gdm9pZCAwO1xuICAgIHRoaXMua2V5VXJpVG9LZXlJbmZvID0ge307XG4gICAgdGhpcy5lbWVDb250cm9sbGVyID0gbnVsbDtcbiAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZztcbiAgfVxuICBhYm9ydCh0eXBlKSB7XG4gICAgZm9yIChjb25zdCB1cmkgaW4gdGhpcy5rZXlVcmlUb0tleUluZm8pIHtcbiAgICAgIGNvbnN0IGxvYWRlciA9IHRoaXMua2V5VXJpVG9LZXlJbmZvW3VyaV0ubG9hZGVyO1xuICAgICAgaWYgKGxvYWRlcikge1xuICAgICAgICB2YXIgX2xvYWRlciRjb250ZXh0O1xuICAgICAgICBpZiAodHlwZSAmJiB0eXBlICE9PSAoKF9sb2FkZXIkY29udGV4dCA9IGxvYWRlci5jb250ZXh0KSA9PSBudWxsID8gdm9pZCAwIDogX2xvYWRlciRjb250ZXh0LmZyYWcudHlwZSkpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgbG9hZGVyLmFib3J0KCk7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIGRldGFjaCgpIHtcbiAgICBmb3IgKGNvbnN0IHVyaSBpbiB0aGlzLmtleVVyaVRvS2V5SW5mbykge1xuICAgICAgY29uc3Qga2V5SW5mbyA9IHRoaXMua2V5VXJpVG9LZXlJbmZvW3VyaV07XG4gICAgICAvLyBSZW1vdmUgY2FjaGVkIEVNRSBrZXlzIG9uIGRldGFjaFxuICAgICAgaWYgKGtleUluZm8ubWVkaWFLZXlTZXNzaW9uQ29udGV4dCB8fCBrZXlJbmZvLmRlY3J5cHRkYXRhLmlzQ29tbW9uRW5jcnlwdGlvbikge1xuICAgICAgICBkZWxldGUgdGhpcy5rZXlVcmlUb0tleUluZm9bdXJpXTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgZGVzdHJveSgpIHtcbiAgICB0aGlzLmRldGFjaCgpO1xuICAgIGZvciAoY29uc3QgdXJpIGluIHRoaXMua2V5VXJpVG9LZXlJbmZvKSB7XG4gICAgICBjb25zdCBsb2FkZXIgPSB0aGlzLmtleVVyaVRvS2V5SW5mb1t1cmldLmxvYWRlcjtcbiAgICAgIGlmIChsb2FkZXIpIHtcbiAgICAgICAgbG9hZGVyLmRlc3Ryb3koKTtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5rZXlVcmlUb0tleUluZm8gPSB7fTtcbiAgfVxuICBjcmVhdGVLZXlMb2FkRXJyb3IoZnJhZywgZGV0YWlscyA9IEVycm9yRGV0YWlscy5LRVlfTE9BRF9FUlJPUiwgZXJyb3IsIG5ldHdvcmtEZXRhaWxzLCByZXNwb25zZSkge1xuICAgIHJldHVybiBuZXcgTG9hZEVycm9yKHtcbiAgICAgIHR5cGU6IEVycm9yVHlwZXMuTkVUV09SS19FUlJPUixcbiAgICAgIGRldGFpbHMsXG4gICAgICBmYXRhbDogZmFsc2UsXG4gICAgICBmcmFnLFxuICAgICAgcmVzcG9uc2UsXG4gICAgICBlcnJvcixcbiAgICAgIG5ldHdvcmtEZXRhaWxzXG4gICAgfSk7XG4gIH1cbiAgbG9hZENsZWFyKGxvYWRpbmdGcmFnLCBlbmNyeXB0ZWRGcmFnbWVudHMpIHtcbiAgICBpZiAodGhpcy5lbWVDb250cm9sbGVyICYmIHRoaXMuY29uZmlnLmVtZUVuYWJsZWQpIHtcbiAgICAgIC8vIGFjY2VzcyBrZXktc3lzdGVtIHdpdGggbmVhcmVzdCBrZXkgb24gc3RhcnQgKGxvYWlkbmcgZnJhZyBpcyB1bmVuY3J5cHRlZClcbiAgICAgIGNvbnN0IHtcbiAgICAgICAgc24sXG4gICAgICAgIGNjXG4gICAgICB9ID0gbG9hZGluZ0ZyYWc7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGVuY3J5cHRlZEZyYWdtZW50cy5sZW5ndGg7IGkrKykge1xuICAgICAgICBjb25zdCBmcmFnID0gZW5jcnlwdGVkRnJhZ21lbnRzW2ldO1xuICAgICAgICBpZiAoY2MgPD0gZnJhZy5jYyAmJiAoc24gPT09ICdpbml0U2VnbWVudCcgfHwgZnJhZy5zbiA9PT0gJ2luaXRTZWdtZW50JyB8fCBzbiA8IGZyYWcuc24pKSB7XG4gICAgICAgICAgdGhpcy5lbWVDb250cm9sbGVyLnNlbGVjdEtleVN5c3RlbUZvcm1hdChmcmFnKS50aGVuKGtleVN5c3RlbUZvcm1hdCA9PiB7XG4gICAgICAgICAgICBmcmFnLnNldEtleUZvcm1hdChrZXlTeXN0ZW1Gb3JtYXQpO1xuICAgICAgICAgIH0pO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG4gIGxvYWQoZnJhZykge1xuICAgIGlmICghZnJhZy5kZWNyeXB0ZGF0YSAmJiBmcmFnLmVuY3J5cHRlZCAmJiB0aGlzLmVtZUNvbnRyb2xsZXIpIHtcbiAgICAgIC8vIE11bHRpcGxlIGtleXMsIGJ1dCBub25lIHNlbGVjdGVkLCByZXNvbHZlIGluIGVtZS1jb250cm9sbGVyXG4gICAgICByZXR1cm4gdGhpcy5lbWVDb250cm9sbGVyLnNlbGVjdEtleVN5c3RlbUZvcm1hdChmcmFnKS50aGVuKGtleVN5c3RlbUZvcm1hdCA9PiB7XG4gICAgICAgIHJldHVybiB0aGlzLmxvYWRJbnRlcm5hbChmcmFnLCBrZXlTeXN0ZW1Gb3JtYXQpO1xuICAgICAgfSk7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmxvYWRJbnRlcm5hbChmcmFnKTtcbiAgfVxuICBsb2FkSW50ZXJuYWwoZnJhZywga2V5U3lzdGVtRm9ybWF0KSB7XG4gICAgdmFyIF9rZXlJbmZvLCBfa2V5SW5mbzI7XG4gICAgaWYgKGtleVN5c3RlbUZvcm1hdCkge1xuICAgICAgZnJhZy5zZXRLZXlGb3JtYXQoa2V5U3lzdGVtRm9ybWF0KTtcbiAgICB9XG4gICAgY29uc3QgZGVjcnlwdGRhdGEgPSBmcmFnLmRlY3J5cHRkYXRhO1xuICAgIGlmICghZGVjcnlwdGRhdGEpIHtcbiAgICAgIGNvbnN0IGVycm9yID0gbmV3IEVycm9yKGtleVN5c3RlbUZvcm1hdCA/IGBFeHBlY3RlZCBmcmFnLmRlY3J5cHRkYXRhIHRvIGJlIGRlZmluZWQgYWZ0ZXIgc2V0dGluZyBmb3JtYXQgJHtrZXlTeXN0ZW1Gb3JtYXR9YCA6ICdNaXNzaW5nIGRlY3J5cHRpb24gZGF0YSBvbiBmcmFnbWVudCBpbiBvbktleUxvYWRpbmcnKTtcbiAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdCh0aGlzLmNyZWF0ZUtleUxvYWRFcnJvcihmcmFnLCBFcnJvckRldGFpbHMuS0VZX0xPQURfRVJST1IsIGVycm9yKSk7XG4gICAgfVxuICAgIGNvbnN0IHVyaSA9IGRlY3J5cHRkYXRhLnVyaTtcbiAgICBpZiAoIXVyaSkge1xuICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KHRoaXMuY3JlYXRlS2V5TG9hZEVycm9yKGZyYWcsIEVycm9yRGV0YWlscy5LRVlfTE9BRF9FUlJPUiwgbmV3IEVycm9yKGBJbnZhbGlkIGtleSBVUkk6IFwiJHt1cml9XCJgKSkpO1xuICAgIH1cbiAgICBsZXQga2V5SW5mbyA9IHRoaXMua2V5VXJpVG9LZXlJbmZvW3VyaV07XG4gICAgaWYgKChfa2V5SW5mbyA9IGtleUluZm8pICE9IG51bGwgJiYgX2tleUluZm8uZGVjcnlwdGRhdGEua2V5KSB7XG4gICAgICBkZWNyeXB0ZGF0YS5rZXkgPSBrZXlJbmZvLmRlY3J5cHRkYXRhLmtleTtcbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoe1xuICAgICAgICBmcmFnLFxuICAgICAgICBrZXlJbmZvXG4gICAgICB9KTtcbiAgICB9XG4gICAgLy8gUmV0dXJuIGtleSBsb2FkIHByb21pc2UgYXMgbG9uZyBhcyBpdCBkb2VzIG5vdCBoYXZlIGEgbWVkaWFrZXkgc2Vzc2lvbiB3aXRoIGFuIHVudXNhYmxlIGtleSBzdGF0dXNcbiAgICBpZiAoKF9rZXlJbmZvMiA9IGtleUluZm8pICE9IG51bGwgJiYgX2tleUluZm8yLmtleUxvYWRQcm9taXNlKSB7XG4gICAgICB2YXIgX2tleUluZm8kbWVkaWFLZXlTZXNzO1xuICAgICAgc3dpdGNoICgoX2tleUluZm8kbWVkaWFLZXlTZXNzID0ga2V5SW5mby5tZWRpYUtleVNlc3Npb25Db250ZXh0KSA9PSBudWxsID8gdm9pZCAwIDogX2tleUluZm8kbWVkaWFLZXlTZXNzLmtleVN0YXR1cykge1xuICAgICAgICBjYXNlIHVuZGVmaW5lZDpcbiAgICAgICAgY2FzZSAnc3RhdHVzLXBlbmRpbmcnOlxuICAgICAgICBjYXNlICd1c2FibGUnOlxuICAgICAgICBjYXNlICd1c2FibGUtaW4tZnV0dXJlJzpcbiAgICAgICAgICByZXR1cm4ga2V5SW5mby5rZXlMb2FkUHJvbWlzZS50aGVuKGtleUxvYWRlZERhdGEgPT4ge1xuICAgICAgICAgICAgLy8gUmV0dXJuIHRoZSBjb3JyZWN0IGZyYWdtZW50IHdpdGggdXBkYXRlZCBkZWNyeXB0ZGF0YSBrZXkgYW5kIGxvYWRlZCBrZXlJbmZvXG4gICAgICAgICAgICBkZWNyeXB0ZGF0YS5rZXkgPSBrZXlMb2FkZWREYXRhLmtleUluZm8uZGVjcnlwdGRhdGEua2V5O1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgZnJhZyxcbiAgICAgICAgICAgICAga2V5SW5mb1xuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9KTtcbiAgICAgIH1cbiAgICAgIC8vIElmIHdlIGhhdmUgYSBrZXkgc2Vzc2lvbiBhbmQgc3RhdHVzIGFuZCBpdCBpcyBub3QgcGVuZGluZyBvciB1c2FibGUsIGNvbnRpbnVlXG4gICAgICAvLyBUaGlzIHdpbGwgZ28gYmFjayB0byB0aGUgZW1lLWNvbnRyb2xsZXIgZm9yIGV4cGlyZWQga2V5cyB0byBnZXQgYSBuZXcga2V5TG9hZFByb21pc2VcbiAgICB9XG5cbiAgICAvLyBMb2FkIHRoZSBrZXkgb3IgcmV0dXJuIHRoZSBsb2FkaW5nIHByb21pc2VcbiAgICBrZXlJbmZvID0gdGhpcy5rZXlVcmlUb0tleUluZm9bdXJpXSA9IHtcbiAgICAgIGRlY3J5cHRkYXRhLFxuICAgICAga2V5TG9hZFByb21pc2U6IG51bGwsXG4gICAgICBsb2FkZXI6IG51bGwsXG4gICAgICBtZWRpYUtleVNlc3Npb25Db250ZXh0OiBudWxsXG4gICAgfTtcbiAgICBzd2l0Y2ggKGRlY3J5cHRkYXRhLm1ldGhvZCkge1xuICAgICAgY2FzZSAnSVNPLTIzMDAxLTcnOlxuICAgICAgY2FzZSAnU0FNUExFLUFFUyc6XG4gICAgICBjYXNlICdTQU1QTEUtQUVTLUNFTkMnOlxuICAgICAgY2FzZSAnU0FNUExFLUFFUy1DVFInOlxuICAgICAgICBpZiAoZGVjcnlwdGRhdGEua2V5Rm9ybWF0ID09PSAnaWRlbnRpdHknKSB7XG4gICAgICAgICAgLy8gbG9hZEtleUhUVFAgaGFuZGxlcyBodHRwKHMpIGFuZCBkYXRhIFVSTHNcbiAgICAgICAgICByZXR1cm4gdGhpcy5sb2FkS2V5SFRUUChrZXlJbmZvLCBmcmFnKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcy5sb2FkS2V5RU1FKGtleUluZm8sIGZyYWcpO1xuICAgICAgY2FzZSAnQUVTLTEyOCc6XG4gICAgICAgIHJldHVybiB0aGlzLmxvYWRLZXlIVFRQKGtleUluZm8sIGZyYWcpO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KHRoaXMuY3JlYXRlS2V5TG9hZEVycm9yKGZyYWcsIEVycm9yRGV0YWlscy5LRVlfTE9BRF9FUlJPUiwgbmV3IEVycm9yKGBLZXkgc3VwcGxpZWQgd2l0aCB1bnN1cHBvcnRlZCBNRVRIT0Q6IFwiJHtkZWNyeXB0ZGF0YS5tZXRob2R9XCJgKSkpO1xuICAgIH1cbiAgfVxuICBsb2FkS2V5RU1FKGtleUluZm8sIGZyYWcpIHtcbiAgICBjb25zdCBrZXlMb2FkZWREYXRhID0ge1xuICAgICAgZnJhZyxcbiAgICAgIGtleUluZm9cbiAgICB9O1xuICAgIGlmICh0aGlzLmVtZUNvbnRyb2xsZXIgJiYgdGhpcy5jb25maWcuZW1lRW5hYmxlZCkge1xuICAgICAgY29uc3Qga2V5U2Vzc2lvbkNvbnRleHRQcm9taXNlID0gdGhpcy5lbWVDb250cm9sbGVyLmxvYWRLZXkoa2V5TG9hZGVkRGF0YSk7XG4gICAgICBpZiAoa2V5U2Vzc2lvbkNvbnRleHRQcm9taXNlKSB7XG4gICAgICAgIHJldHVybiAoa2V5SW5mby5rZXlMb2FkUHJvbWlzZSA9IGtleVNlc3Npb25Db250ZXh0UHJvbWlzZS50aGVuKGtleVNlc3Npb25Db250ZXh0ID0+IHtcbiAgICAgICAgICBrZXlJbmZvLm1lZGlhS2V5U2Vzc2lvbkNvbnRleHQgPSBrZXlTZXNzaW9uQ29udGV4dDtcbiAgICAgICAgICByZXR1cm4ga2V5TG9hZGVkRGF0YTtcbiAgICAgICAgfSkpLmNhdGNoKGVycm9yID0+IHtcbiAgICAgICAgICAvLyBSZW1vdmUgcHJvbWlzZSBmb3IgbGljZW5zZSByZW5ld2FsIG9yIHJldHJ5XG4gICAgICAgICAga2V5SW5mby5rZXlMb2FkUHJvbWlzZSA9IG51bGw7XG4gICAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKGtleUxvYWRlZERhdGEpO1xuICB9XG4gIGxvYWRLZXlIVFRQKGtleUluZm8sIGZyYWcpIHtcbiAgICBjb25zdCBjb25maWcgPSB0aGlzLmNvbmZpZztcbiAgICBjb25zdCBMb2FkZXIgPSBjb25maWcubG9hZGVyO1xuICAgIGNvbnN0IGtleUxvYWRlciA9IG5ldyBMb2FkZXIoY29uZmlnKTtcbiAgICBmcmFnLmtleUxvYWRlciA9IGtleUluZm8ubG9hZGVyID0ga2V5TG9hZGVyO1xuICAgIHJldHVybiBrZXlJbmZvLmtleUxvYWRQcm9taXNlID0gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgY29uc3QgbG9hZGVyQ29udGV4dCA9IHtcbiAgICAgICAga2V5SW5mbyxcbiAgICAgICAgZnJhZyxcbiAgICAgICAgcmVzcG9uc2VUeXBlOiAnYXJyYXlidWZmZXInLFxuICAgICAgICB1cmw6IGtleUluZm8uZGVjcnlwdGRhdGEudXJpXG4gICAgICB9O1xuXG4gICAgICAvLyBtYXhSZXRyeSBpcyAwIHNvIHRoYXQgaW5zdGVhZCBvZiByZXRyeWluZyB0aGUgc2FtZSBrZXkgb24gdGhlIHNhbWUgdmFyaWFudCBtdWx0aXBsZSB0aW1lcyxcbiAgICAgIC8vIGtleS1sb2FkZXIgd2lsbCB0cmlnZ2VyIGFuIGVycm9yIGFuZCByZWx5IG9uIHN0cmVhbS1jb250cm9sbGVyIHRvIGhhbmRsZSByZXRyeSBsb2dpYy5cbiAgICAgIC8vIHRoaXMgd2lsbCBhbHNvIGFsaWduIHJldHJ5IGxvZ2ljIHdpdGggZnJhZ21lbnQtbG9hZGVyXG4gICAgICBjb25zdCBsb2FkUG9saWN5ID0gY29uZmlnLmtleUxvYWRQb2xpY3kuZGVmYXVsdDtcbiAgICAgIGNvbnN0IGxvYWRlckNvbmZpZyA9IHtcbiAgICAgICAgbG9hZFBvbGljeSxcbiAgICAgICAgdGltZW91dDogbG9hZFBvbGljeS5tYXhMb2FkVGltZU1zLFxuICAgICAgICBtYXhSZXRyeTogMCxcbiAgICAgICAgcmV0cnlEZWxheTogMCxcbiAgICAgICAgbWF4UmV0cnlEZWxheTogMFxuICAgICAgfTtcbiAgICAgIGNvbnN0IGxvYWRlckNhbGxiYWNrcyA9IHtcbiAgICAgICAgb25TdWNjZXNzOiAocmVzcG9uc2UsIHN0YXRzLCBjb250ZXh0LCBuZXR3b3JrRGV0YWlscykgPT4ge1xuICAgICAgICAgIGNvbnN0IHtcbiAgICAgICAgICAgIGZyYWcsXG4gICAgICAgICAgICBrZXlJbmZvLFxuICAgICAgICAgICAgdXJsOiB1cmlcbiAgICAgICAgICB9ID0gY29udGV4dDtcbiAgICAgICAgICBpZiAoIWZyYWcuZGVjcnlwdGRhdGEgfHwga2V5SW5mbyAhPT0gdGhpcy5rZXlVcmlUb0tleUluZm9bdXJpXSkge1xuICAgICAgICAgICAgcmV0dXJuIHJlamVjdCh0aGlzLmNyZWF0ZUtleUxvYWRFcnJvcihmcmFnLCBFcnJvckRldGFpbHMuS0VZX0xPQURfRVJST1IsIG5ldyBFcnJvcignYWZ0ZXIga2V5IGxvYWQsIGRlY3J5cHRkYXRhIHVuc2V0IG9yIGNoYW5nZWQnKSwgbmV0d29ya0RldGFpbHMpKTtcbiAgICAgICAgICB9XG4gICAgICAgICAga2V5SW5mby5kZWNyeXB0ZGF0YS5rZXkgPSBmcmFnLmRlY3J5cHRkYXRhLmtleSA9IG5ldyBVaW50OEFycmF5KHJlc3BvbnNlLmRhdGEpO1xuXG4gICAgICAgICAgLy8gZGV0YWNoIGZyYWdtZW50IGtleSBsb2FkZXIgb24gbG9hZCBzdWNjZXNzXG4gICAgICAgICAgZnJhZy5rZXlMb2FkZXIgPSBudWxsO1xuICAgICAgICAgIGtleUluZm8ubG9hZGVyID0gbnVsbDtcbiAgICAgICAgICByZXNvbHZlKHtcbiAgICAgICAgICAgIGZyYWcsXG4gICAgICAgICAgICBrZXlJbmZvXG4gICAgICAgICAgfSk7XG4gICAgICAgIH0sXG4gICAgICAgIG9uRXJyb3I6IChyZXNwb25zZSwgY29udGV4dCwgbmV0d29ya0RldGFpbHMsIHN0YXRzKSA9PiB7XG4gICAgICAgICAgdGhpcy5yZXNldExvYWRlcihjb250ZXh0KTtcbiAgICAgICAgICByZWplY3QodGhpcy5jcmVhdGVLZXlMb2FkRXJyb3IoZnJhZywgRXJyb3JEZXRhaWxzLktFWV9MT0FEX0VSUk9SLCBuZXcgRXJyb3IoYEhUVFAgRXJyb3IgJHtyZXNwb25zZS5jb2RlfSBsb2FkaW5nIGtleSAke3Jlc3BvbnNlLnRleHR9YCksIG5ldHdvcmtEZXRhaWxzLCBfb2JqZWN0U3ByZWFkMih7XG4gICAgICAgICAgICB1cmw6IGxvYWRlckNvbnRleHQudXJsLFxuICAgICAgICAgICAgZGF0YTogdW5kZWZpbmVkXG4gICAgICAgICAgfSwgcmVzcG9uc2UpKSk7XG4gICAgICAgIH0sXG4gICAgICAgIG9uVGltZW91dDogKHN0YXRzLCBjb250ZXh0LCBuZXR3b3JrRGV0YWlscykgPT4ge1xuICAgICAgICAgIHRoaXMucmVzZXRMb2FkZXIoY29udGV4dCk7XG4gICAgICAgICAgcmVqZWN0KHRoaXMuY3JlYXRlS2V5TG9hZEVycm9yKGZyYWcsIEVycm9yRGV0YWlscy5LRVlfTE9BRF9USU1FT1VULCBuZXcgRXJyb3IoJ2tleSBsb2FkaW5nIHRpbWVkIG91dCcpLCBuZXR3b3JrRGV0YWlscykpO1xuICAgICAgICB9LFxuICAgICAgICBvbkFib3J0OiAoc3RhdHMsIGNvbnRleHQsIG5ldHdvcmtEZXRhaWxzKSA9PiB7XG4gICAgICAgICAgdGhpcy5yZXNldExvYWRlcihjb250ZXh0KTtcbiAgICAgICAgICByZWplY3QodGhpcy5jcmVhdGVLZXlMb2FkRXJyb3IoZnJhZywgRXJyb3JEZXRhaWxzLklOVEVSTkFMX0FCT1JURUQsIG5ldyBFcnJvcigna2V5IGxvYWRpbmcgYWJvcnRlZCcpLCBuZXR3b3JrRGV0YWlscykpO1xuICAgICAgICB9XG4gICAgICB9O1xuICAgICAga2V5TG9hZGVyLmxvYWQobG9hZGVyQ29udGV4dCwgbG9hZGVyQ29uZmlnLCBsb2FkZXJDYWxsYmFja3MpO1xuICAgIH0pO1xuICB9XG4gIHJlc2V0TG9hZGVyKGNvbnRleHQpIHtcbiAgICBjb25zdCB7XG4gICAgICBmcmFnLFxuICAgICAga2V5SW5mbyxcbiAgICAgIHVybDogdXJpXG4gICAgfSA9IGNvbnRleHQ7XG4gICAgY29uc3QgbG9hZGVyID0ga2V5SW5mby5sb2FkZXI7XG4gICAgaWYgKGZyYWcua2V5TG9hZGVyID09PSBsb2FkZXIpIHtcbiAgICAgIGZyYWcua2V5TG9hZGVyID0gbnVsbDtcbiAgICAgIGtleUluZm8ubG9hZGVyID0gbnVsbDtcbiAgICB9XG4gICAgZGVsZXRlIHRoaXMua2V5VXJpVG9LZXlJbmZvW3VyaV07XG4gICAgaWYgKGxvYWRlcikge1xuICAgICAgbG9hZGVyLmRlc3Ryb3koKTtcbiAgICB9XG4gIH1cbn1cblxuZnVuY3Rpb24gZ2V0U291cmNlQnVmZmVyKCkge1xuICByZXR1cm4gc2VsZi5Tb3VyY2VCdWZmZXIgfHwgc2VsZi5XZWJLaXRTb3VyY2VCdWZmZXI7XG59XG5mdW5jdGlvbiBpc01TRVN1cHBvcnRlZCgpIHtcbiAgY29uc3QgbWVkaWFTb3VyY2UgPSBnZXRNZWRpYVNvdXJjZSgpO1xuICBpZiAoIW1lZGlhU291cmNlKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgLy8gaWYgU291cmNlQnVmZmVyIGlzIGV4cG9zZWQgZW5zdXJlIGl0cyBBUEkgaXMgdmFsaWRcbiAgLy8gT2xkZXIgYnJvd3NlcnMgZG8gbm90IGV4cG9zZSBTb3VyY2VCdWZmZXIgZ2xvYmFsbHkgc28gY2hlY2tpbmcgU291cmNlQnVmZmVyLnByb3RvdHlwZSBpcyBpbXBvc3NpYmxlXG4gIGNvbnN0IHNvdXJjZUJ1ZmZlciA9IGdldFNvdXJjZUJ1ZmZlcigpO1xuICByZXR1cm4gIXNvdXJjZUJ1ZmZlciB8fCBzb3VyY2VCdWZmZXIucHJvdG90eXBlICYmIHR5cGVvZiBzb3VyY2VCdWZmZXIucHJvdG90eXBlLmFwcGVuZEJ1ZmZlciA9PT0gJ2Z1bmN0aW9uJyAmJiB0eXBlb2Ygc291cmNlQnVmZmVyLnByb3RvdHlwZS5yZW1vdmUgPT09ICdmdW5jdGlvbic7XG59XG5mdW5jdGlvbiBpc1N1cHBvcnRlZCgpIHtcbiAgaWYgKCFpc01TRVN1cHBvcnRlZCgpKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIGNvbnN0IG1lZGlhU291cmNlID0gZ2V0TWVkaWFTb3VyY2UoKTtcbiAgcmV0dXJuIHR5cGVvZiAobWVkaWFTb3VyY2UgPT0gbnVsbCA/IHZvaWQgMCA6IG1lZGlhU291cmNlLmlzVHlwZVN1cHBvcnRlZCkgPT09ICdmdW5jdGlvbicgJiYgKFsnYXZjMS40MkUwMUUsbXA0YS40MC4yJywgJ2F2MDEuMC4wMU0uMDgnLCAndnAwOS4wMC41MC4wOCddLnNvbWUoY29kZWNzRm9yVmlkZW9Db250YWluZXIgPT4gbWVkaWFTb3VyY2UuaXNUeXBlU3VwcG9ydGVkKG1pbWVUeXBlRm9yQ29kZWMoY29kZWNzRm9yVmlkZW9Db250YWluZXIsICd2aWRlbycpKSkgfHwgWydtcDRhLjQwLjInLCAnZkxhQyddLnNvbWUoY29kZWNGb3JBdWRpb0NvbnRhaW5lciA9PiBtZWRpYVNvdXJjZS5pc1R5cGVTdXBwb3J0ZWQobWltZVR5cGVGb3JDb2RlYyhjb2RlY0ZvckF1ZGlvQ29udGFpbmVyLCAnYXVkaW8nKSkpKTtcbn1cbmZ1bmN0aW9uIGNoYW5nZVR5cGVTdXBwb3J0ZWQoKSB7XG4gIHZhciBfc291cmNlQnVmZmVyJHByb3RvdHk7XG4gIGNvbnN0IHNvdXJjZUJ1ZmZlciA9IGdldFNvdXJjZUJ1ZmZlcigpO1xuICByZXR1cm4gdHlwZW9mIChzb3VyY2VCdWZmZXIgPT0gbnVsbCA/IHZvaWQgMCA6IChfc291cmNlQnVmZmVyJHByb3RvdHkgPSBzb3VyY2VCdWZmZXIucHJvdG90eXBlKSA9PSBudWxsID8gdm9pZCAwIDogX3NvdXJjZUJ1ZmZlciRwcm90b3R5LmNoYW5nZVR5cGUpID09PSAnZnVuY3Rpb24nO1xufVxuXG5jb25zdCBTVEFMTF9NSU5JTVVNX0RVUkFUSU9OX01TID0gMjUwO1xuY29uc3QgTUFYX1NUQVJUX0dBUF9KVU1QID0gMi4wO1xuY29uc3QgU0tJUF9CVUZGRVJfSE9MRV9TVEVQX1NFQ09ORFMgPSAwLjE7XG5jb25zdCBTS0lQX0JVRkZFUl9SQU5HRV9TVEFSVCA9IDAuMDU7XG5jbGFzcyBHYXBDb250cm9sbGVyIHtcbiAgY29uc3RydWN0b3IoY29uZmlnLCBtZWRpYSwgZnJhZ21lbnRUcmFja2VyLCBobHMpIHtcbiAgICB0aGlzLmNvbmZpZyA9IHZvaWQgMDtcbiAgICB0aGlzLm1lZGlhID0gbnVsbDtcbiAgICB0aGlzLmZyYWdtZW50VHJhY2tlciA9IHZvaWQgMDtcbiAgICB0aGlzLmhscyA9IHZvaWQgMDtcbiAgICB0aGlzLm51ZGdlUmV0cnkgPSAwO1xuICAgIHRoaXMuc3RhbGxSZXBvcnRlZCA9IGZhbHNlO1xuICAgIHRoaXMuc3RhbGxlZCA9IG51bGw7XG4gICAgdGhpcy5tb3ZlZCA9IGZhbHNlO1xuICAgIHRoaXMuc2Vla2luZyA9IGZhbHNlO1xuICAgIHRoaXMuY29uZmlnID0gY29uZmlnO1xuICAgIHRoaXMubWVkaWEgPSBtZWRpYTtcbiAgICB0aGlzLmZyYWdtZW50VHJhY2tlciA9IGZyYWdtZW50VHJhY2tlcjtcbiAgICB0aGlzLmhscyA9IGhscztcbiAgfVxuICBkZXN0cm95KCkge1xuICAgIHRoaXMubWVkaWEgPSBudWxsO1xuICAgIC8vIEB0cy1pZ25vcmVcbiAgICB0aGlzLmhscyA9IHRoaXMuZnJhZ21lbnRUcmFja2VyID0gbnVsbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVja3MgaWYgdGhlIHBsYXloZWFkIGlzIHN0dWNrIHdpdGhpbiBhIGdhcCwgYW5kIGlmIHNvLCBhdHRlbXB0cyB0byBmcmVlIGl0LlxuICAgKiBBIGdhcCBpcyBhbiB1bmJ1ZmZlcmVkIHJhbmdlIGJldHdlZW4gdHdvIGJ1ZmZlcmVkIHJhbmdlcyAob3IgdGhlIHN0YXJ0IGFuZCB0aGUgZmlyc3QgYnVmZmVyZWQgcmFuZ2UpLlxuICAgKlxuICAgKiBAcGFyYW0gbGFzdEN1cnJlbnRUaW1lIC0gUHJldmlvdXNseSByZWFkIHBsYXloZWFkIHBvc2l0aW9uXG4gICAqL1xuICBwb2xsKGxhc3RDdXJyZW50VGltZSwgYWN0aXZlRnJhZykge1xuICAgIGNvbnN0IHtcbiAgICAgIGNvbmZpZyxcbiAgICAgIG1lZGlhLFxuICAgICAgc3RhbGxlZFxuICAgIH0gPSB0aGlzO1xuICAgIGlmIChtZWRpYSA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB7XG4gICAgICBjdXJyZW50VGltZSxcbiAgICAgIHNlZWtpbmdcbiAgICB9ID0gbWVkaWE7XG4gICAgY29uc3Qgc2Vla2VkID0gdGhpcy5zZWVraW5nICYmICFzZWVraW5nO1xuICAgIGNvbnN0IGJlZ2luU2VlayA9ICF0aGlzLnNlZWtpbmcgJiYgc2Vla2luZztcbiAgICB0aGlzLnNlZWtpbmcgPSBzZWVraW5nO1xuXG4gICAgLy8gVGhlIHBsYXloZWFkIGlzIG1vdmluZywgbm8tb3BcbiAgICBpZiAoY3VycmVudFRpbWUgIT09IGxhc3RDdXJyZW50VGltZSkge1xuICAgICAgdGhpcy5tb3ZlZCA9IHRydWU7XG4gICAgICBpZiAoIXNlZWtpbmcpIHtcbiAgICAgICAgdGhpcy5udWRnZVJldHJ5ID0gMDtcbiAgICAgIH1cbiAgICAgIGlmIChzdGFsbGVkICE9PSBudWxsKSB7XG4gICAgICAgIC8vIFRoZSBwbGF5aGVhZCBpcyBub3cgbW92aW5nLCBidXQgd2FzIHByZXZpb3VzbHkgc3RhbGxlZFxuICAgICAgICBpZiAodGhpcy5zdGFsbFJlcG9ydGVkKSB7XG4gICAgICAgICAgY29uc3QgX3N0YWxsZWREdXJhdGlvbiA9IHNlbGYucGVyZm9ybWFuY2Uubm93KCkgLSBzdGFsbGVkO1xuICAgICAgICAgIGxvZ2dlci53YXJuKGBwbGF5YmFjayBub3Qgc3R1Y2sgYW55bW9yZSBAJHtjdXJyZW50VGltZX0sIGFmdGVyICR7TWF0aC5yb3VuZChfc3RhbGxlZER1cmF0aW9uKX1tc2ApO1xuICAgICAgICAgIHRoaXMuc3RhbGxSZXBvcnRlZCA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuc3RhbGxlZCA9IG51bGw7XG4gICAgICB9XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gQ2xlYXIgc3RhbGxlZCBzdGF0ZSB3aGVuIGJlZ2lubmluZyBvciBmaW5pc2hpbmcgc2Vla2luZyBzbyB0aGF0IHdlIGRvbid0IHJlcG9ydCBzdGFsbHMgY29taW5nIG91dCBvZiBhIHNlZWtcbiAgICBpZiAoYmVnaW5TZWVrIHx8IHNlZWtlZCkge1xuICAgICAgdGhpcy5zdGFsbGVkID0gbnVsbDtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBUaGUgcGxheWhlYWQgc2hvdWxkIG5vdCBiZSBtb3ZpbmdcbiAgICBpZiAobWVkaWEucGF1c2VkICYmICFzZWVraW5nIHx8IG1lZGlhLmVuZGVkIHx8IG1lZGlhLnBsYXliYWNrUmF0ZSA9PT0gMCB8fCAhQnVmZmVySGVscGVyLmdldEJ1ZmZlcmVkKG1lZGlhKS5sZW5ndGgpIHtcbiAgICAgIHRoaXMubnVkZ2VSZXRyeSA9IDA7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGJ1ZmZlckluZm8gPSBCdWZmZXJIZWxwZXIuYnVmZmVySW5mbyhtZWRpYSwgY3VycmVudFRpbWUsIDApO1xuICAgIGNvbnN0IG5leHRTdGFydCA9IGJ1ZmZlckluZm8ubmV4dFN0YXJ0IHx8IDA7XG4gICAgaWYgKHNlZWtpbmcpIHtcbiAgICAgIC8vIFdhaXRpbmcgZm9yIHNlZWtpbmcgaW4gYSBidWZmZXJlZCByYW5nZSB0byBjb21wbGV0ZVxuICAgICAgY29uc3QgaGFzRW5vdWdoQnVmZmVyID0gYnVmZmVySW5mby5sZW4gPiBNQVhfU1RBUlRfR0FQX0pVTVA7XG4gICAgICAvLyBOZXh0IGJ1ZmZlcmVkIHJhbmdlIGlzIHRvbyBmYXIgYWhlYWQgdG8ganVtcCB0byB3aGlsZSBzdGlsbCBzZWVraW5nXG4gICAgICBjb25zdCBub0J1ZmZlckdhcCA9ICFuZXh0U3RhcnQgfHwgYWN0aXZlRnJhZyAmJiBhY3RpdmVGcmFnLnN0YXJ0IDw9IGN1cnJlbnRUaW1lIHx8IG5leHRTdGFydCAtIGN1cnJlbnRUaW1lID4gTUFYX1NUQVJUX0dBUF9KVU1QICYmICF0aGlzLmZyYWdtZW50VHJhY2tlci5nZXRQYXJ0aWFsRnJhZ21lbnQoY3VycmVudFRpbWUpO1xuICAgICAgaWYgKGhhc0Vub3VnaEJ1ZmZlciB8fCBub0J1ZmZlckdhcCkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICAvLyBSZXNldCBtb3ZlZCBzdGF0ZSB3aGVuIHNlZWtpbmcgdG8gYSBwb2ludCBpbiBvciBiZWZvcmUgYSBnYXBcbiAgICAgIHRoaXMubW92ZWQgPSBmYWxzZTtcbiAgICB9XG5cbiAgICAvLyBTa2lwIHN0YXJ0IGdhcHMgaWYgd2UgaGF2ZW4ndCBwbGF5ZWQsIGJ1dCB0aGUgbGFzdCBwb2xsIGRldGVjdGVkIHRoZSBzdGFydCBvZiBhIHN0YWxsXG4gICAgLy8gVGhlIGFkZGl0aW9uIHBvbGwgZ2l2ZXMgdGhlIGJyb3dzZXIgYSBjaGFuY2UgdG8ganVtcCB0aGUgZ2FwIGZvciB1c1xuICAgIGlmICghdGhpcy5tb3ZlZCAmJiB0aGlzLnN0YWxsZWQgIT09IG51bGwpIHtcbiAgICAgIHZhciBfbGV2ZWwkZGV0YWlscztcbiAgICAgIC8vIFRoZXJlIGlzIG5vIHBsYXlhYmxlIGJ1ZmZlciAoc2Vla2VkLCB3YWl0aW5nIGZvciBidWZmZXIpXG4gICAgICBjb25zdCBpc0J1ZmZlcmVkID0gYnVmZmVySW5mby5sZW4gPiAwO1xuICAgICAgaWYgKCFpc0J1ZmZlcmVkICYmICFuZXh0U3RhcnQpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgLy8gSnVtcCBzdGFydCBnYXBzIHdpdGhpbiBqdW1wIHRocmVzaG9sZFxuICAgICAgY29uc3Qgc3RhcnRKdW1wID0gTWF0aC5tYXgobmV4dFN0YXJ0LCBidWZmZXJJbmZvLnN0YXJ0IHx8IDApIC0gY3VycmVudFRpbWU7XG5cbiAgICAgIC8vIFdoZW4gam9pbmluZyBhIGxpdmUgc3RyZWFtIHdpdGggYXVkaW8gdHJhY2tzLCBhY2NvdW50IGZvciBsaXZlIHBsYXlsaXN0IHdpbmRvdyBzbGlkaW5nIGJ5IGFsbG93aW5nXG4gICAgICAvLyBhIGxhcmdlciBqdW1wIG92ZXIgc3RhcnQgZ2FwcyBjYXVzZWQgYnkgdGhlIGF1ZGlvLXN0cmVhbS1jb250cm9sbGVyIGJ1ZmZlcmluZyBhIHN0YXJ0IGZyYWdtZW50XG4gICAgICAvLyB0aGF0IGJlZ2lucyBvdmVyIDEgdGFyZ2V0IGR1cmF0aW9uIGFmdGVyIHRoZSB2aWRlbyBzdGFydCBwb3NpdGlvbi5cbiAgICAgIGNvbnN0IGxldmVsID0gdGhpcy5obHMubGV2ZWxzID8gdGhpcy5obHMubGV2ZWxzW3RoaXMuaGxzLmN1cnJlbnRMZXZlbF0gOiBudWxsO1xuICAgICAgY29uc3QgaXNMaXZlID0gbGV2ZWwgPT0gbnVsbCA/IHZvaWQgMCA6IChfbGV2ZWwkZGV0YWlscyA9IGxldmVsLmRldGFpbHMpID09IG51bGwgPyB2b2lkIDAgOiBfbGV2ZWwkZGV0YWlscy5saXZlO1xuICAgICAgY29uc3QgbWF4U3RhcnRHYXBKdW1wID0gaXNMaXZlID8gbGV2ZWwuZGV0YWlscy50YXJnZXRkdXJhdGlvbiAqIDIgOiBNQVhfU1RBUlRfR0FQX0pVTVA7XG4gICAgICBjb25zdCBwYXJ0aWFsT3JHYXAgPSB0aGlzLmZyYWdtZW50VHJhY2tlci5nZXRQYXJ0aWFsRnJhZ21lbnQoY3VycmVudFRpbWUpO1xuICAgICAgaWYgKHN0YXJ0SnVtcCA+IDAgJiYgKHN0YXJ0SnVtcCA8PSBtYXhTdGFydEdhcEp1bXAgfHwgcGFydGlhbE9yR2FwKSkge1xuICAgICAgICBpZiAoIW1lZGlhLnBhdXNlZCkge1xuICAgICAgICAgIHRoaXMuX3RyeVNraXBCdWZmZXJIb2xlKHBhcnRpYWxPckdhcCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIFN0YXJ0IHRyYWNraW5nIHN0YWxsIHRpbWVcbiAgICBjb25zdCB0bm93ID0gc2VsZi5wZXJmb3JtYW5jZS5ub3coKTtcbiAgICBpZiAoc3RhbGxlZCA9PT0gbnVsbCkge1xuICAgICAgdGhpcy5zdGFsbGVkID0gdG5vdztcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3Qgc3RhbGxlZER1cmF0aW9uID0gdG5vdyAtIHN0YWxsZWQ7XG4gICAgaWYgKCFzZWVraW5nICYmIHN0YWxsZWREdXJhdGlvbiA+PSBTVEFMTF9NSU5JTVVNX0RVUkFUSU9OX01TKSB7XG4gICAgICAvLyBSZXBvcnQgc3RhbGxpbmcgYWZ0ZXIgdHJ5aW5nIHRvIGZpeFxuICAgICAgdGhpcy5fcmVwb3J0U3RhbGwoYnVmZmVySW5mbyk7XG4gICAgICBpZiAoIXRoaXMubWVkaWEpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgIH1cbiAgICBjb25zdCBidWZmZXJlZFdpdGhIb2xlcyA9IEJ1ZmZlckhlbHBlci5idWZmZXJJbmZvKG1lZGlhLCBjdXJyZW50VGltZSwgY29uZmlnLm1heEJ1ZmZlckhvbGUpO1xuICAgIHRoaXMuX3RyeUZpeEJ1ZmZlclN0YWxsKGJ1ZmZlcmVkV2l0aEhvbGVzLCBzdGFsbGVkRHVyYXRpb24pO1xuICB9XG5cbiAgLyoqXG4gICAqIERldGVjdHMgYW5kIGF0dGVtcHRzIHRvIGZpeCBrbm93biBidWZmZXIgc3RhbGxpbmcgaXNzdWVzLlxuICAgKiBAcGFyYW0gYnVmZmVySW5mbyAtIFRoZSBwcm9wZXJ0aWVzIG9mIHRoZSBjdXJyZW50IGJ1ZmZlci5cbiAgICogQHBhcmFtIHN0YWxsZWREdXJhdGlvbk1zIC0gVGhlIGFtb3VudCBvZiB0aW1lIEhscy5qcyBoYXMgYmVlbiBzdGFsbGluZyBmb3IuXG4gICAqIEBwcml2YXRlXG4gICAqL1xuICBfdHJ5Rml4QnVmZmVyU3RhbGwoYnVmZmVySW5mbywgc3RhbGxlZER1cmF0aW9uTXMpIHtcbiAgICBjb25zdCB7XG4gICAgICBjb25maWcsXG4gICAgICBmcmFnbWVudFRyYWNrZXIsXG4gICAgICBtZWRpYVxuICAgIH0gPSB0aGlzO1xuICAgIGlmIChtZWRpYSA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBjdXJyZW50VGltZSA9IG1lZGlhLmN1cnJlbnRUaW1lO1xuICAgIGNvbnN0IHBhcnRpYWwgPSBmcmFnbWVudFRyYWNrZXIuZ2V0UGFydGlhbEZyYWdtZW50KGN1cnJlbnRUaW1lKTtcbiAgICBpZiAocGFydGlhbCkge1xuICAgICAgLy8gVHJ5IHRvIHNraXAgb3ZlciB0aGUgYnVmZmVyIGhvbGUgY2F1c2VkIGJ5IGEgcGFydGlhbCBmcmFnbWVudFxuICAgICAgLy8gVGhpcyBtZXRob2QgaXNuJ3QgbGltaXRlZCBieSB0aGUgc2l6ZSBvZiB0aGUgZ2FwIGJldHdlZW4gYnVmZmVyZWQgcmFuZ2VzXG4gICAgICBjb25zdCB0YXJnZXRUaW1lID0gdGhpcy5fdHJ5U2tpcEJ1ZmZlckhvbGUocGFydGlhbCk7XG4gICAgICAvLyB3ZSByZXR1cm4gaGVyZSBpbiB0aGlzIGNhc2UsIG1lYW5pbmdcbiAgICAgIC8vIHRoZSBicmFuY2ggYmVsb3cgb25seSBleGVjdXRlcyB3aGVuIHdlIGhhdmVuJ3Qgc2Vla2VkIHRvIGEgbmV3IHBvc2l0aW9uXG4gICAgICBpZiAodGFyZ2V0VGltZSB8fCAhdGhpcy5tZWRpYSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gaWYgd2UgaGF2ZW4ndCBoYWQgdG8gc2tpcCBvdmVyIGEgYnVmZmVyIGhvbGUgb2YgYSBwYXJ0aWFsIGZyYWdtZW50XG4gICAgLy8gd2UgbWF5IGp1c3QgaGF2ZSB0byBcIm51ZGdlXCIgdGhlIHBsYXlsaXN0IGFzIHRoZSBicm93c2VyIGRlY29kaW5nL3JlbmRlcmluZyBlbmdpbmVcbiAgICAvLyBuZWVkcyB0byBjcm9zcyBzb21lIHNvcnQgb2YgdGhyZXNob2xkIGNvdmVyaW5nIGFsbCBzb3VyY2UtYnVmZmVycyBjb250ZW50XG4gICAgLy8gdG8gc3RhcnQgcGxheWluZyBwcm9wZXJseS5cbiAgICBpZiAoKGJ1ZmZlckluZm8ubGVuID4gY29uZmlnLm1heEJ1ZmZlckhvbGUgfHwgYnVmZmVySW5mby5uZXh0U3RhcnQgJiYgYnVmZmVySW5mby5uZXh0U3RhcnQgLSBjdXJyZW50VGltZSA8IGNvbmZpZy5tYXhCdWZmZXJIb2xlKSAmJiBzdGFsbGVkRHVyYXRpb25NcyA+IGNvbmZpZy5oaWdoQnVmZmVyV2F0Y2hkb2dQZXJpb2QgKiAxMDAwKSB7XG4gICAgICBsb2dnZXIud2FybignVHJ5aW5nIHRvIG51ZGdlIHBsYXloZWFkIG92ZXIgYnVmZmVyLWhvbGUnKTtcbiAgICAgIC8vIFRyeSB0byBudWRnZSBjdXJyZW50VGltZSBvdmVyIGEgYnVmZmVyIGhvbGUgaWYgd2UndmUgYmVlbiBzdGFsbGluZyBmb3IgdGhlIGNvbmZpZ3VyZWQgYW1vdW50IG9mIHNlY29uZHNcbiAgICAgIC8vIFdlIG9ubHkgdHJ5IHRvIGp1bXAgdGhlIGhvbGUgaWYgaXQncyB1bmRlciB0aGUgY29uZmlndXJlZCBzaXplXG4gICAgICAvLyBSZXNldCBzdGFsbGVkIHNvIHRvIHJlYXJtIHdhdGNoZG9nIHRpbWVyXG4gICAgICB0aGlzLnN0YWxsZWQgPSBudWxsO1xuICAgICAgdGhpcy5fdHJ5TnVkZ2VCdWZmZXIoKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVHJpZ2dlcnMgYSBCVUZGRVJfU1RBTExFRF9FUlJPUiBldmVudCwgYnV0IG9ubHkgb25jZSBwZXIgc3RhbGwgcGVyaW9kLlxuICAgKiBAcGFyYW0gYnVmZmVyTGVuIC0gVGhlIHBsYXloZWFkIGRpc3RhbmNlIGZyb20gdGhlIGVuZCBvZiB0aGUgY3VycmVudCBidWZmZXIgc2VnbWVudC5cbiAgICogQHByaXZhdGVcbiAgICovXG4gIF9yZXBvcnRTdGFsbChidWZmZXJJbmZvKSB7XG4gICAgY29uc3Qge1xuICAgICAgaGxzLFxuICAgICAgbWVkaWEsXG4gICAgICBzdGFsbFJlcG9ydGVkXG4gICAgfSA9IHRoaXM7XG4gICAgaWYgKCFzdGFsbFJlcG9ydGVkICYmIG1lZGlhKSB7XG4gICAgICAvLyBSZXBvcnQgc3RhbGxlZCBlcnJvciBvbmNlXG4gICAgICB0aGlzLnN0YWxsUmVwb3J0ZWQgPSB0cnVlO1xuICAgICAgY29uc3QgZXJyb3IgPSBuZXcgRXJyb3IoYFBsYXliYWNrIHN0YWxsaW5nIGF0IEAke21lZGlhLmN1cnJlbnRUaW1lfSBkdWUgdG8gbG93IGJ1ZmZlciAoJHtKU09OLnN0cmluZ2lmeShidWZmZXJJbmZvKX0pYCk7XG4gICAgICBsb2dnZXIud2FybihlcnJvci5tZXNzYWdlKTtcbiAgICAgIGhscy50cmlnZ2VyKEV2ZW50cy5FUlJPUiwge1xuICAgICAgICB0eXBlOiBFcnJvclR5cGVzLk1FRElBX0VSUk9SLFxuICAgICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuQlVGRkVSX1NUQUxMRURfRVJST1IsXG4gICAgICAgIGZhdGFsOiBmYWxzZSxcbiAgICAgICAgZXJyb3IsXG4gICAgICAgIGJ1ZmZlcjogYnVmZmVySW5mby5sZW5cbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBBdHRlbXB0cyB0byBmaXggYnVmZmVyIHN0YWxscyBieSBqdW1waW5nIG92ZXIga25vd24gZ2FwcyBjYXVzZWQgYnkgcGFydGlhbCBmcmFnbWVudHNcbiAgICogQHBhcmFtIHBhcnRpYWwgLSBUaGUgcGFydGlhbCBmcmFnbWVudCBmb3VuZCBhdCB0aGUgY3VycmVudCB0aW1lICh3aGVyZSBwbGF5YmFjayBpcyBzdGFsbGluZykuXG4gICAqIEBwcml2YXRlXG4gICAqL1xuICBfdHJ5U2tpcEJ1ZmZlckhvbGUocGFydGlhbCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGNvbmZpZyxcbiAgICAgIGhscyxcbiAgICAgIG1lZGlhXG4gICAgfSA9IHRoaXM7XG4gICAgaWYgKG1lZGlhID09PSBudWxsKSB7XG4gICAgICByZXR1cm4gMDtcbiAgICB9XG5cbiAgICAvLyBDaGVjayBpZiBjdXJyZW50VGltZSBpcyBiZXR3ZWVuIHVuYnVmZmVyZWQgcmVnaW9ucyBvZiBwYXJ0aWFsIGZyYWdtZW50c1xuICAgIGNvbnN0IGN1cnJlbnRUaW1lID0gbWVkaWEuY3VycmVudFRpbWU7XG4gICAgY29uc3QgYnVmZmVySW5mbyA9IEJ1ZmZlckhlbHBlci5idWZmZXJJbmZvKG1lZGlhLCBjdXJyZW50VGltZSwgMCk7XG4gICAgY29uc3Qgc3RhcnRUaW1lID0gY3VycmVudFRpbWUgPCBidWZmZXJJbmZvLnN0YXJ0ID8gYnVmZmVySW5mby5zdGFydCA6IGJ1ZmZlckluZm8ubmV4dFN0YXJ0O1xuICAgIGlmIChzdGFydFRpbWUpIHtcbiAgICAgIGNvbnN0IGJ1ZmZlclN0YXJ2ZWQgPSBidWZmZXJJbmZvLmxlbiA8PSBjb25maWcubWF4QnVmZmVySG9sZTtcbiAgICAgIGNvbnN0IHdhaXRpbmcgPSBidWZmZXJJbmZvLmxlbiA+IDAgJiYgYnVmZmVySW5mby5sZW4gPCAxICYmIG1lZGlhLnJlYWR5U3RhdGUgPCAzO1xuICAgICAgY29uc3QgZ2FwTGVuZ3RoID0gc3RhcnRUaW1lIC0gY3VycmVudFRpbWU7XG4gICAgICBpZiAoZ2FwTGVuZ3RoID4gMCAmJiAoYnVmZmVyU3RhcnZlZCB8fCB3YWl0aW5nKSkge1xuICAgICAgICAvLyBPbmx5IGFsbG93IGxhcmdlIGdhcHMgdG8gYmUgc2tpcHBlZCBpZiBpdCBpcyBhIHN0YXJ0IGdhcCwgb3IgYWxsIGZyYWdtZW50cyBpbiBza2lwIHJhbmdlIGFyZSBwYXJ0aWFsXG4gICAgICAgIGlmIChnYXBMZW5ndGggPiBjb25maWcubWF4QnVmZmVySG9sZSkge1xuICAgICAgICAgIGNvbnN0IHtcbiAgICAgICAgICAgIGZyYWdtZW50VHJhY2tlclxuICAgICAgICAgIH0gPSB0aGlzO1xuICAgICAgICAgIGxldCBzdGFydEdhcCA9IGZhbHNlO1xuICAgICAgICAgIGlmIChjdXJyZW50VGltZSA9PT0gMCkge1xuICAgICAgICAgICAgY29uc3Qgc3RhcnRGcmFnID0gZnJhZ21lbnRUcmFja2VyLmdldEFwcGVuZGVkRnJhZygwLCBQbGF5bGlzdExldmVsVHlwZS5NQUlOKTtcbiAgICAgICAgICAgIGlmIChzdGFydEZyYWcgJiYgc3RhcnRUaW1lIDwgc3RhcnRGcmFnLmVuZCkge1xuICAgICAgICAgICAgICBzdGFydEdhcCA9IHRydWU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICghc3RhcnRHYXApIHtcbiAgICAgICAgICAgIGNvbnN0IHN0YXJ0UHJvdmlzaW9uZWQgPSBwYXJ0aWFsIHx8IGZyYWdtZW50VHJhY2tlci5nZXRBcHBlbmRlZEZyYWcoY3VycmVudFRpbWUsIFBsYXlsaXN0TGV2ZWxUeXBlLk1BSU4pO1xuICAgICAgICAgICAgaWYgKHN0YXJ0UHJvdmlzaW9uZWQpIHtcbiAgICAgICAgICAgICAgbGV0IG1vcmVUb0xvYWQgPSBmYWxzZTtcbiAgICAgICAgICAgICAgbGV0IHBvcyA9IHN0YXJ0UHJvdmlzaW9uZWQuZW5kO1xuICAgICAgICAgICAgICB3aGlsZSAocG9zIDwgc3RhcnRUaW1lKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcHJvdmlzaW9uZWQgPSBmcmFnbWVudFRyYWNrZXIuZ2V0UGFydGlhbEZyYWdtZW50KHBvcyk7XG4gICAgICAgICAgICAgICAgaWYgKHByb3Zpc2lvbmVkKSB7XG4gICAgICAgICAgICAgICAgICBwb3MgKz0gcHJvdmlzaW9uZWQuZHVyYXRpb247XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgIG1vcmVUb0xvYWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGlmIChtb3JlVG9Mb2FkKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIDA7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgdGFyZ2V0VGltZSA9IE1hdGgubWF4KHN0YXJ0VGltZSArIFNLSVBfQlVGRkVSX1JBTkdFX1NUQVJULCBjdXJyZW50VGltZSArIFNLSVBfQlVGRkVSX0hPTEVfU1RFUF9TRUNPTkRTKTtcbiAgICAgICAgbG9nZ2VyLndhcm4oYHNraXBwaW5nIGhvbGUsIGFkanVzdGluZyBjdXJyZW50VGltZSBmcm9tICR7Y3VycmVudFRpbWV9IHRvICR7dGFyZ2V0VGltZX1gKTtcbiAgICAgICAgdGhpcy5tb3ZlZCA9IHRydWU7XG4gICAgICAgIHRoaXMuc3RhbGxlZCA9IG51bGw7XG4gICAgICAgIG1lZGlhLmN1cnJlbnRUaW1lID0gdGFyZ2V0VGltZTtcbiAgICAgICAgaWYgKHBhcnRpYWwgJiYgIXBhcnRpYWwuZ2FwKSB7XG4gICAgICAgICAgY29uc3QgZXJyb3IgPSBuZXcgRXJyb3IoYGZyYWdtZW50IGxvYWRlZCB3aXRoIGJ1ZmZlciBob2xlcywgc2Vla2luZyBmcm9tICR7Y3VycmVudFRpbWV9IHRvICR7dGFyZ2V0VGltZX1gKTtcbiAgICAgICAgICBobHMudHJpZ2dlcihFdmVudHMuRVJST1IsIHtcbiAgICAgICAgICAgIHR5cGU6IEVycm9yVHlwZXMuTUVESUFfRVJST1IsXG4gICAgICAgICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuQlVGRkVSX1NFRUtfT1ZFUl9IT0xFLFxuICAgICAgICAgICAgZmF0YWw6IGZhbHNlLFxuICAgICAgICAgICAgZXJyb3IsXG4gICAgICAgICAgICByZWFzb246IGVycm9yLm1lc3NhZ2UsXG4gICAgICAgICAgICBmcmFnOiBwYXJ0aWFsXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRhcmdldFRpbWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiAwO1xuICB9XG5cbiAgLyoqXG4gICAqIEF0dGVtcHRzIHRvIGZpeCBidWZmZXIgc3RhbGxzIGJ5IGFkdmFuY2luZyB0aGUgbWVkaWFFbGVtZW50J3MgY3VycmVudCB0aW1lIGJ5IGEgc21hbGwgYW1vdW50LlxuICAgKiBAcHJpdmF0ZVxuICAgKi9cbiAgX3RyeU51ZGdlQnVmZmVyKCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGNvbmZpZyxcbiAgICAgIGhscyxcbiAgICAgIG1lZGlhLFxuICAgICAgbnVkZ2VSZXRyeVxuICAgIH0gPSB0aGlzO1xuICAgIGlmIChtZWRpYSA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBjdXJyZW50VGltZSA9IG1lZGlhLmN1cnJlbnRUaW1lO1xuICAgIHRoaXMubnVkZ2VSZXRyeSsrO1xuICAgIGlmIChudWRnZVJldHJ5IDwgY29uZmlnLm51ZGdlTWF4UmV0cnkpIHtcbiAgICAgIGNvbnN0IHRhcmdldFRpbWUgPSBjdXJyZW50VGltZSArIChudWRnZVJldHJ5ICsgMSkgKiBjb25maWcubnVkZ2VPZmZzZXQ7XG4gICAgICAvLyBwbGF5YmFjayBzdGFsbGVkIGluIGJ1ZmZlcmVkIGFyZWEgLi4uIGxldCdzIG51ZGdlIGN1cnJlbnRUaW1lIHRvIHRyeSB0byBvdmVyY29tZSB0aGlzXG4gICAgICBjb25zdCBlcnJvciA9IG5ldyBFcnJvcihgTnVkZ2luZyAnY3VycmVudFRpbWUnIGZyb20gJHtjdXJyZW50VGltZX0gdG8gJHt0YXJnZXRUaW1lfWApO1xuICAgICAgbG9nZ2VyLndhcm4oZXJyb3IubWVzc2FnZSk7XG4gICAgICBtZWRpYS5jdXJyZW50VGltZSA9IHRhcmdldFRpbWU7XG4gICAgICBobHMudHJpZ2dlcihFdmVudHMuRVJST1IsIHtcbiAgICAgICAgdHlwZTogRXJyb3JUeXBlcy5NRURJQV9FUlJPUixcbiAgICAgICAgZGV0YWlsczogRXJyb3JEZXRhaWxzLkJVRkZFUl9OVURHRV9PTl9TVEFMTCxcbiAgICAgICAgZXJyb3IsXG4gICAgICAgIGZhdGFsOiBmYWxzZVxuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGVycm9yID0gbmV3IEVycm9yKGBQbGF5aGVhZCBzdGlsbCBub3QgbW92aW5nIHdoaWxlIGVub3VnaCBkYXRhIGJ1ZmZlcmVkIEAke2N1cnJlbnRUaW1lfSBhZnRlciAke2NvbmZpZy5udWRnZU1heFJldHJ5fSBudWRnZXNgKTtcbiAgICAgIGxvZ2dlci5lcnJvcihlcnJvci5tZXNzYWdlKTtcbiAgICAgIGhscy50cmlnZ2VyKEV2ZW50cy5FUlJPUiwge1xuICAgICAgICB0eXBlOiBFcnJvclR5cGVzLk1FRElBX0VSUk9SLFxuICAgICAgICBkZXRhaWxzOiBFcnJvckRldGFpbHMuQlVGRkVSX1NUQUxMRURfRVJST1IsXG4gICAgICAgIGVycm9yLFxuICAgICAgICBmYXRhbDogdHJ1ZVxuICAgICAgfSk7XG4gICAgfVxuICB9XG59XG5cbmNvbnN0IFRJQ0tfSU5URVJWQUwgPSAxMDA7IC8vIGhvdyBvZnRlbiB0byB0aWNrIGluIG1zXG5cbmNsYXNzIFN0cmVhbUNvbnRyb2xsZXIgZXh0ZW5kcyBCYXNlU3RyZWFtQ29udHJvbGxlciB7XG4gIGNvbnN0cnVjdG9yKGhscywgZnJhZ21lbnRUcmFja2VyLCBrZXlMb2FkZXIpIHtcbiAgICBzdXBlcihobHMsIGZyYWdtZW50VHJhY2tlciwga2V5TG9hZGVyLCAnW3N0cmVhbS1jb250cm9sbGVyXScsIFBsYXlsaXN0TGV2ZWxUeXBlLk1BSU4pO1xuICAgIHRoaXMuYXVkaW9Db2RlY1N3YXAgPSBmYWxzZTtcbiAgICB0aGlzLmdhcENvbnRyb2xsZXIgPSBudWxsO1xuICAgIHRoaXMubGV2ZWwgPSAtMTtcbiAgICB0aGlzLl9mb3JjZVN0YXJ0TG9hZCA9IGZhbHNlO1xuICAgIHRoaXMuYWx0QXVkaW8gPSBmYWxzZTtcbiAgICB0aGlzLmF1ZGlvT25seSA9IGZhbHNlO1xuICAgIHRoaXMuZnJhZ1BsYXlpbmcgPSBudWxsO1xuICAgIHRoaXMub252cGxheWluZyA9IG51bGw7XG4gICAgdGhpcy5vbnZzZWVrZWQgPSBudWxsO1xuICAgIHRoaXMuZnJhZ0xhc3RLYnBzID0gMDtcbiAgICB0aGlzLmNvdWxkQmFja3RyYWNrID0gZmFsc2U7XG4gICAgdGhpcy5iYWNrdHJhY2tGcmFnbWVudCA9IG51bGw7XG4gICAgdGhpcy5hdWRpb0NvZGVjU3dpdGNoID0gZmFsc2U7XG4gICAgdGhpcy52aWRlb0J1ZmZlciA9IG51bGw7XG4gICAgdGhpcy5fcmVnaXN0ZXJMaXN0ZW5lcnMoKTtcbiAgfVxuICBfcmVnaXN0ZXJMaXN0ZW5lcnMoKSB7XG4gICAgY29uc3Qge1xuICAgICAgaGxzXG4gICAgfSA9IHRoaXM7XG4gICAgaGxzLm9uKEV2ZW50cy5NRURJQV9BVFRBQ0hFRCwgdGhpcy5vbk1lZGlhQXR0YWNoZWQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuTUVESUFfREVUQUNISU5HLCB0aGlzLm9uTWVkaWFEZXRhY2hpbmcsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuTUFOSUZFU1RfTE9BRElORywgdGhpcy5vbk1hbmlmZXN0TG9hZGluZywgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5NQU5JRkVTVF9QQVJTRUQsIHRoaXMub25NYW5pZmVzdFBhcnNlZCwgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5MRVZFTF9MT0FESU5HLCB0aGlzLm9uTGV2ZWxMb2FkaW5nLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkxFVkVMX0xPQURFRCwgdGhpcy5vbkxldmVsTG9hZGVkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkZSQUdfTE9BRF9FTUVSR0VOQ1lfQUJPUlRFRCwgdGhpcy5vbkZyYWdMb2FkRW1lcmdlbmN5QWJvcnRlZCwgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5FUlJPUiwgdGhpcy5vbkVycm9yLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkFVRElPX1RSQUNLX1NXSVRDSElORywgdGhpcy5vbkF1ZGlvVHJhY2tTd2l0Y2hpbmcsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuQVVESU9fVFJBQ0tfU1dJVENIRUQsIHRoaXMub25BdWRpb1RyYWNrU3dpdGNoZWQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuQlVGRkVSX0NSRUFURUQsIHRoaXMub25CdWZmZXJDcmVhdGVkLCB0aGlzKTtcbiAgICBobHMub24oRXZlbnRzLkJVRkZFUl9GTFVTSEVELCB0aGlzLm9uQnVmZmVyRmx1c2hlZCwgdGhpcyk7XG4gICAgaGxzLm9uKEV2ZW50cy5MRVZFTFNfVVBEQVRFRCwgdGhpcy5vbkxldmVsc1VwZGF0ZWQsIHRoaXMpO1xuICAgIGhscy5vbihFdmVudHMuRlJBR19CVUZGRVJFRCwgdGhpcy5vbkZyYWdCdWZmZXJlZCwgdGhpcyk7XG4gIH1cbiAgX3VucmVnaXN0ZXJMaXN0ZW5lcnMoKSB7XG4gICAgY29uc3Qge1xuICAgICAgaGxzXG4gICAgfSA9IHRoaXM7XG4gICAgaGxzLm9mZihFdmVudHMuTUVESUFfQVRUQUNIRUQsIHRoaXMub25NZWRpYUF0dGFjaGVkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5NRURJQV9ERVRBQ0hJTkcsIHRoaXMub25NZWRpYURldGFjaGluZywgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuTUFOSUZFU1RfTE9BRElORywgdGhpcy5vbk1hbmlmZXN0TG9hZGluZywgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuTUFOSUZFU1RfUEFSU0VELCB0aGlzLm9uTWFuaWZlc3RQYXJzZWQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkxFVkVMX0xPQURFRCwgdGhpcy5vbkxldmVsTG9hZGVkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5GUkFHX0xPQURfRU1FUkdFTkNZX0FCT1JURUQsIHRoaXMub25GcmFnTG9hZEVtZXJnZW5jeUFib3J0ZWQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkVSUk9SLCB0aGlzLm9uRXJyb3IsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkFVRElPX1RSQUNLX1NXSVRDSElORywgdGhpcy5vbkF1ZGlvVHJhY2tTd2l0Y2hpbmcsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkFVRElPX1RSQUNLX1NXSVRDSEVELCB0aGlzLm9uQXVkaW9UcmFja1N3aXRjaGVkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5CVUZGRVJfQ1JFQVRFRCwgdGhpcy5vbkJ1ZmZlckNyZWF0ZWQsIHRoaXMpO1xuICAgIGhscy5vZmYoRXZlbnRzLkJVRkZFUl9GTFVTSEVELCB0aGlzLm9uQnVmZmVyRmx1c2hlZCwgdGhpcyk7XG4gICAgaGxzLm9mZihFdmVudHMuTEVWRUxTX1VQREFURUQsIHRoaXMub25MZXZlbHNVcGRhdGVkLCB0aGlzKTtcbiAgICBobHMub2ZmKEV2ZW50cy5GUkFHX0JVRkZFUkVELCB0aGlzLm9uRnJhZ0J1ZmZlcmVkLCB0aGlzKTtcbiAgfVxuICBvbkhhbmRsZXJEZXN0cm95aW5nKCkge1xuICAgIHRoaXMuX3VucmVnaXN0ZXJMaXN0ZW5lcnMoKTtcbiAgICBzdXBlci5vbkhhbmRsZXJEZXN0cm95aW5nKCk7XG4gIH1cbiAgc3RhcnRMb2FkKHN0YXJ0UG9zaXRpb24pIHtcbiAgICBpZiAodGhpcy5sZXZlbHMpIHtcbiAgICAgIGNvbnN0IHtcbiAgICAgICAgbGFzdEN1cnJlbnRUaW1lLFxuICAgICAgICBobHNcbiAgICAgIH0gPSB0aGlzO1xuICAgICAgdGhpcy5zdG9wTG9hZCgpO1xuICAgICAgdGhpcy5zZXRJbnRlcnZhbChUSUNLX0lOVEVSVkFMKTtcbiAgICAgIHRoaXMubGV2ZWwgPSAtMTtcbiAgICAgIGlmICghdGhpcy5zdGFydEZyYWdSZXF1ZXN0ZWQpIHtcbiAgICAgICAgLy8gZGV0ZXJtaW5lIGxvYWQgbGV2ZWxcbiAgICAgICAgbGV0IHN0YXJ0TGV2ZWwgPSBobHMuc3RhcnRMZXZlbDtcbiAgICAgICAgaWYgKHN0YXJ0TGV2ZWwgPT09IC0xKSB7XG4gICAgICAgICAgaWYgKGhscy5jb25maWcudGVzdEJhbmR3aWR0aCAmJiB0aGlzLmxldmVscy5sZW5ndGggPiAxKSB7XG4gICAgICAgICAgICAvLyAtMSA6IGd1ZXNzIHN0YXJ0IExldmVsIGJ5IGRvaW5nIGEgYml0cmF0ZSB0ZXN0IGJ5IGxvYWRpbmcgZmlyc3QgZnJhZ21lbnQgb2YgbG93ZXN0IHF1YWxpdHkgbGV2ZWxcbiAgICAgICAgICAgIHN0YXJ0TGV2ZWwgPSAwO1xuICAgICAgICAgICAgdGhpcy5iaXRyYXRlVGVzdCA9IHRydWU7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHN0YXJ0TGV2ZWwgPSBobHMuZmlyc3RBdXRvTGV2ZWw7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIC8vIHNldCBuZXcgbGV2ZWwgdG8gcGxheWxpc3QgbG9hZGVyIDogdGhpcyB3aWxsIHRyaWdnZXIgc3RhcnQgbGV2ZWwgbG9hZFxuICAgICAgICAvLyBobHMubmV4dExvYWRMZXZlbCByZW1haW5zIHVudGlsIGl0IGlzIHNldCB0byBhIG5ldyB2YWx1ZSBvciB1bnRpbCBhIG5ldyBmcmFnIGlzIHN1Y2Nlc3NmdWxseSBsb2FkZWRcbiAgICAgICAgaGxzLm5leHRMb2FkTGV2ZWwgPSBzdGFydExldmVsO1xuICAgICAgICB0aGlzLmxldmVsID0gaGxzLmxvYWRMZXZlbDtcbiAgICAgICAgdGhpcy5sb2FkZWRtZXRhZGF0YSA9IGZhbHNlO1xuICAgICAgfVxuICAgICAgLy8gaWYgc3RhcnRQb3NpdGlvbiB1bmRlZmluZWQgYnV0IGxhc3RDdXJyZW50VGltZSBzZXQsIHNldCBzdGFydFBvc2l0aW9uIHRvIGxhc3QgY3VycmVudFRpbWVcbiAgICAgIGlmIChsYXN0Q3VycmVudFRpbWUgPiAwICYmIHN0YXJ0UG9zaXRpb24gPT09IC0xKSB7XG4gICAgICAgIHRoaXMubG9nKGBPdmVycmlkZSBzdGFydFBvc2l0aW9uIHdpdGggbGFzdEN1cnJlbnRUaW1lIEAke2xhc3RDdXJyZW50VGltZS50b0ZpeGVkKDMpfWApO1xuICAgICAgICBzdGFydFBvc2l0aW9uID0gbGFzdEN1cnJlbnRUaW1lO1xuICAgICAgfVxuICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gICAgICB0aGlzLm5leHRMb2FkUG9zaXRpb24gPSB0aGlzLnN0YXJ0UG9zaXRpb24gPSB0aGlzLmxhc3RDdXJyZW50VGltZSA9IHN0YXJ0UG9zaXRpb247XG4gICAgICB0aGlzLnRpY2soKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5fZm9yY2VTdGFydExvYWQgPSB0cnVlO1xuICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLlNUT1BQRUQ7XG4gICAgfVxuICB9XG4gIHN0b3BMb2FkKCkge1xuICAgIHRoaXMuX2ZvcmNlU3RhcnRMb2FkID0gZmFsc2U7XG4gICAgc3VwZXIuc3RvcExvYWQoKTtcbiAgfVxuICBkb1RpY2soKSB7XG4gICAgc3dpdGNoICh0aGlzLnN0YXRlKSB7XG4gICAgICBjYXNlIFN0YXRlLldBSVRJTkdfTEVWRUw6XG4gICAgICAgIHtcbiAgICAgICAgICBjb25zdCB7XG4gICAgICAgICAgICBsZXZlbHMsXG4gICAgICAgICAgICBsZXZlbFxuICAgICAgICAgIH0gPSB0aGlzO1xuICAgICAgICAgIGNvbnN0IGN1cnJlbnRMZXZlbCA9IGxldmVscyA9PSBudWxsID8gdm9pZCAwIDogbGV2ZWxzW2xldmVsXTtcbiAgICAgICAgICBjb25zdCBkZXRhaWxzID0gY3VycmVudExldmVsID09IG51bGwgPyB2b2lkIDAgOiBjdXJyZW50TGV2ZWwuZGV0YWlscztcbiAgICAgICAgICBpZiAoZGV0YWlscyAmJiAoIWRldGFpbHMubGl2ZSB8fCB0aGlzLmxldmVsTGFzdExvYWRlZCA9PT0gY3VycmVudExldmVsKSkge1xuICAgICAgICAgICAgaWYgKHRoaXMud2FpdEZvckNkblR1bmVJbihkZXRhaWxzKSkge1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfSBlbHNlIGlmICh0aGlzLmhscy5uZXh0TG9hZExldmVsICE9PSB0aGlzLmxldmVsKSB7XG4gICAgICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIH1cbiAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgY2FzZSBTdGF0ZS5GUkFHX0xPQURJTkdfV0FJVElOR19SRVRSWTpcbiAgICAgICAge1xuICAgICAgICAgIHZhciBfdGhpcyRtZWRpYTtcbiAgICAgICAgICBjb25zdCBub3cgPSBzZWxmLnBlcmZvcm1hbmNlLm5vdygpO1xuICAgICAgICAgIGNvbnN0IHJldHJ5RGF0ZSA9IHRoaXMucmV0cnlEYXRlO1xuICAgICAgICAgIC8vIGlmIGN1cnJlbnQgdGltZSBpcyBndCB0aGFuIHJldHJ5RGF0ZSwgb3IgaWYgbWVkaWEgc2Vla2luZyBsZXQncyBzd2l0Y2ggdG8gSURMRSBzdGF0ZSB0byByZXRyeSBsb2FkaW5nXG4gICAgICAgICAgaWYgKCFyZXRyeURhdGUgfHwgbm93ID49IHJldHJ5RGF0ZSB8fCAoX3RoaXMkbWVkaWEgPSB0aGlzLm1lZGlhKSAhPSBudWxsICYmIF90aGlzJG1lZGlhLnNlZWtpbmcpIHtcbiAgICAgICAgICAgIGNvbnN0IHtcbiAgICAgICAgICAgICAgbGV2ZWxzLFxuICAgICAgICAgICAgICBsZXZlbFxuICAgICAgICAgICAgfSA9IHRoaXM7XG4gICAgICAgICAgICBjb25zdCBjdXJyZW50TGV2ZWwgPSBsZXZlbHMgPT0gbnVsbCA/IHZvaWQgMCA6IGxldmVsc1tsZXZlbF07XG4gICAgICAgICAgICB0aGlzLnJlc2V0U3RhcnRXaGVuTm90TG9hZGVkKGN1cnJlbnRMZXZlbCB8fCBudWxsKTtcbiAgICAgICAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBicmVhaztcbiAgICB9XG4gICAgaWYgKHRoaXMuc3RhdGUgPT09IFN0YXRlLklETEUpIHtcbiAgICAgIHRoaXMuZG9UaWNrSWRsZSgpO1xuICAgIH1cbiAgICB0aGlzLm9uVGlja0VuZCgpO1xuICB9XG4gIG9uVGlja0VuZCgpIHtcbiAgICBzdXBlci5vblRpY2tFbmQoKTtcbiAgICB0aGlzLmNoZWNrQnVmZmVyKCk7XG4gICAgdGhpcy5jaGVja0ZyYWdtZW50Q2hhbmdlZCgpO1xuICB9XG4gIGRvVGlja0lkbGUoKSB7XG4gICAgY29uc3Qge1xuICAgICAgaGxzLFxuICAgICAgbGV2ZWxMYXN0TG9hZGVkLFxuICAgICAgbGV2ZWxzLFxuICAgICAgbWVkaWFcbiAgICB9ID0gdGhpcztcblxuICAgIC8vIGlmIHN0YXJ0IGxldmVsIG5vdCBwYXJzZWQgeWV0IE9SXG4gICAgLy8gaWYgdmlkZW8gbm90IGF0dGFjaGVkIEFORCBzdGFydCBmcmFnbWVudCBhbHJlYWR5IHJlcXVlc3RlZCBPUiBzdGFydCBmcmFnIHByZWZldGNoIG5vdCBlbmFibGVkXG4gICAgLy8gZXhpdCBsb29wLCBhcyB3ZSBlaXRoZXIgbmVlZCBtb3JlIGluZm8gKGxldmVsIG5vdCBwYXJzZWQpIG9yIHdlIG5lZWQgbWVkaWEgdG8gYmUgYXR0YWNoZWQgdG8gbG9hZCBuZXcgZnJhZ21lbnRcbiAgICBpZiAobGV2ZWxMYXN0TG9hZGVkID09PSBudWxsIHx8ICFtZWRpYSAmJiAodGhpcy5zdGFydEZyYWdSZXF1ZXN0ZWQgfHwgIWhscy5jb25maWcuc3RhcnRGcmFnUHJlZmV0Y2gpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gSWYgdGhlIFwibWFpblwiIGxldmVsIGlzIGF1ZGlvLW9ubHkgYnV0IHdlIGFyZSBsb2FkaW5nIGFuIGFsdGVybmF0ZSB0cmFjayBpbiB0aGUgc2FtZSBncm91cCwgZG8gbm90IGxvYWQgYW55dGhpbmdcbiAgICBpZiAodGhpcy5hbHRBdWRpbyAmJiB0aGlzLmF1ZGlvT25seSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBsZXZlbCA9IGhscy5uZXh0TG9hZExldmVsO1xuICAgIGlmICghKGxldmVscyAhPSBudWxsICYmIGxldmVsc1tsZXZlbF0pKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGxldmVsSW5mbyA9IGxldmVsc1tsZXZlbF07XG5cbiAgICAvLyBpZiBidWZmZXIgbGVuZ3RoIGlzIGxlc3MgdGhhbiBtYXhCdWZMZW4gdHJ5IHRvIGxvYWQgYSBuZXcgZnJhZ21lbnRcblxuICAgIGNvbnN0IGJ1ZmZlckluZm8gPSB0aGlzLmdldE1haW5Gd2RCdWZmZXJJbmZvKCk7XG4gICAgaWYgKGJ1ZmZlckluZm8gPT09IG51bGwpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgbGFzdERldGFpbHMgPSB0aGlzLmdldExldmVsRGV0YWlscygpO1xuICAgIGlmIChsYXN0RGV0YWlscyAmJiB0aGlzLl9zdHJlYW1FbmRlZChidWZmZXJJbmZvLCBsYXN0RGV0YWlscykpIHtcbiAgICAgIGNvbnN0IGRhdGEgPSB7fTtcbiAgICAgIGlmICh0aGlzLmFsdEF1ZGlvKSB7XG4gICAgICAgIGRhdGEudHlwZSA9ICd2aWRlbyc7XG4gICAgICB9XG4gICAgICB0aGlzLmhscy50cmlnZ2VyKEV2ZW50cy5CVUZGRVJfRU9TLCBkYXRhKTtcbiAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5FTkRFRDtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBzZXQgbmV4dCBsb2FkIGxldmVsIDogdGhpcyB3aWxsIHRyaWdnZXIgYSBwbGF5bGlzdCBsb2FkIGlmIG5lZWRlZFxuICAgIGlmIChobHMubG9hZExldmVsICE9PSBsZXZlbCAmJiBobHMubWFudWFsTGV2ZWwgPT09IC0xKSB7XG4gICAgICB0aGlzLmxvZyhgQWRhcHRpbmcgdG8gbGV2ZWwgJHtsZXZlbH0gZnJvbSBsZXZlbCAke3RoaXMubGV2ZWx9YCk7XG4gICAgfVxuICAgIHRoaXMubGV2ZWwgPSBobHMubmV4dExvYWRMZXZlbCA9IGxldmVsO1xuICAgIGNvbnN0IGxldmVsRGV0YWlscyA9IGxldmVsSW5mby5kZXRhaWxzO1xuICAgIC8vIGlmIGxldmVsIGluZm8gbm90IHJldHJpZXZlZCB5ZXQsIHN3aXRjaCBzdGF0ZSBhbmQgd2FpdCBmb3IgbGV2ZWwgcmV0cmlldmFsXG4gICAgLy8gaWYgbGl2ZSBwbGF5bGlzdCwgZW5zdXJlIHRoYXQgbmV3IHBsYXlsaXN0IGhhcyBiZWVuIHJlZnJlc2hlZCB0byBhdm9pZCBsb2FkaW5nL3RyeSB0byBsb2FkXG4gICAgLy8gYSB1c2VsZXNzIGFuZCBvdXRkYXRlZCBmcmFnbWVudCAodGhhdCBtaWdodCBldmVuIGludHJvZHVjZSBsb2FkIGVycm9yIGlmIGl0IGlzIGFscmVhZHkgb3V0IG9mIHRoZSBsaXZlIHBsYXlsaXN0KVxuICAgIGlmICghbGV2ZWxEZXRhaWxzIHx8IHRoaXMuc3RhdGUgPT09IFN0YXRlLldBSVRJTkdfTEVWRUwgfHwgbGV2ZWxEZXRhaWxzLmxpdmUgJiYgdGhpcy5sZXZlbExhc3RMb2FkZWQgIT09IGxldmVsSW5mbykge1xuICAgICAgdGhpcy5sZXZlbCA9IGxldmVsO1xuICAgICAgdGhpcy5zdGF0ZSA9IFN0YXRlLldBSVRJTkdfTEVWRUw7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGJ1ZmZlckxlbiA9IGJ1ZmZlckluZm8ubGVuO1xuXG4gICAgLy8gY29tcHV0ZSBtYXggQnVmZmVyIExlbmd0aCB0aGF0IHdlIGNvdWxkIGdldCBmcm9tIHRoaXMgbG9hZCBsZXZlbCwgYmFzZWQgb24gbGV2ZWwgYml0cmF0ZS4gZG9uJ3QgYnVmZmVyIG1vcmUgdGhhbiA2MCBNQiBhbmQgbW9yZSB0aGFuIDMwc1xuICAgIGNvbnN0IG1heEJ1ZkxlbiA9IHRoaXMuZ2V0TWF4QnVmZmVyTGVuZ3RoKGxldmVsSW5mby5tYXhCaXRyYXRlKTtcblxuICAgIC8vIFN0YXkgaWRsZSBpZiB3ZSBhcmUgc3RpbGwgd2l0aCBidWZmZXIgbWFyZ2luc1xuICAgIGlmIChidWZmZXJMZW4gPj0gbWF4QnVmTGVuKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmICh0aGlzLmJhY2t0cmFja0ZyYWdtZW50ICYmIHRoaXMuYmFja3RyYWNrRnJhZ21lbnQuc3RhcnQgPiBidWZmZXJJbmZvLmVuZCkge1xuICAgICAgdGhpcy5iYWNrdHJhY2tGcmFnbWVudCA9IG51bGw7XG4gICAgfVxuICAgIGNvbnN0IHRhcmdldEJ1ZmZlclRpbWUgPSB0aGlzLmJhY2t0cmFja0ZyYWdtZW50ID8gdGhpcy5iYWNrdHJhY2tGcmFnbWVudC5zdGFydCA6IGJ1ZmZlckluZm8uZW5kO1xuICAgIGxldCBmcmFnID0gdGhpcy5nZXROZXh0RnJhZ21lbnQodGFyZ2V0QnVmZmVyVGltZSwgbGV2ZWxEZXRhaWxzKTtcbiAgICAvLyBBdm9pZCBiYWNrdHJhY2tpbmcgYnkgbG9hZGluZyBhbiBlYXJsaWVyIHNlZ21lbnQgaW4gc3RyZWFtcyB3aXRoIHNlZ21lbnRzIHRoYXQgZG8gbm90IHN0YXJ0IHdpdGggYSBrZXkgZnJhbWUgKGZsYWdnZWQgYnkgYGNvdWxkQmFja3RyYWNrYClcbiAgICBpZiAodGhpcy5jb3VsZEJhY2t0cmFjayAmJiAhdGhpcy5mcmFnUHJldmlvdXMgJiYgZnJhZyAmJiBmcmFnLnNuICE9PSAnaW5pdFNlZ21lbnQnICYmIHRoaXMuZnJhZ21lbnRUcmFja2VyLmdldFN0YXRlKGZyYWcpICE9PSBGcmFnbWVudFN0YXRlLk9LKSB7XG4gICAgICB2YXIgX3RoaXMkYmFja3RyYWNrRnJhZ21lO1xuICAgICAgY29uc3QgYmFja3RyYWNrU24gPSAoKF90aGlzJGJhY2t0cmFja0ZyYWdtZSA9IHRoaXMuYmFja3RyYWNrRnJhZ21lbnQpICE9IG51bGwgPyBfdGhpcyRiYWNrdHJhY2tGcmFnbWUgOiBmcmFnKS5zbjtcbiAgICAgIGNvbnN0IGZyYWdJZHggPSBiYWNrdHJhY2tTbiAtIGxldmVsRGV0YWlscy5zdGFydFNOO1xuICAgICAgY29uc3QgYmFja3RyYWNrRnJhZyA9IGxldmVsRGV0YWlscy5mcmFnbWVudHNbZnJhZ0lkeCAtIDFdO1xuICAgICAgaWYgKGJhY2t0cmFja0ZyYWcgJiYgZnJhZy5jYyA9PT0gYmFja3RyYWNrRnJhZy5jYykge1xuICAgICAgICBmcmFnID0gYmFja3RyYWNrRnJhZztcbiAgICAgICAgdGhpcy5mcmFnbWVudFRyYWNrZXIucmVtb3ZlRnJhZ21lbnQoYmFja3RyYWNrRnJhZyk7XG4gICAgICB9XG4gICAgfSBlbHNlIGlmICh0aGlzLmJhY2t0cmFja0ZyYWdtZW50ICYmIGJ1ZmZlckluZm8ubGVuKSB7XG4gICAgICB0aGlzLmJhY2t0cmFja0ZyYWdtZW50ID0gbnVsbDtcbiAgICB9XG4gICAgLy8gQXZvaWQgbG9vcCBsb2FkaW5nIGJ5IHVzaW5nIG5leHRMb2FkUG9zaXRpb24gc2V0IGZvciBiYWNrdHJhY2tpbmcgYW5kIHNraXBwaW5nIGNvbnNlY3V0aXZlIEdBUCB0YWdzXG4gICAgaWYgKGZyYWcgJiYgdGhpcy5pc0xvb3BMb2FkaW5nKGZyYWcsIHRhcmdldEJ1ZmZlclRpbWUpKSB7XG4gICAgICBjb25zdCBnYXBTdGFydCA9IGZyYWcuZ2FwO1xuICAgICAgaWYgKCFnYXBTdGFydCkge1xuICAgICAgICAvLyBDbGVhbnVwIHRoZSBmcmFnbWVudCB0cmFja2VyIGJlZm9yZSB0cnlpbmcgdG8gZmluZCB0aGUgbmV4dCB1bmJ1ZmZlcmVkIGZyYWdtZW50XG4gICAgICAgIGNvbnN0IHR5cGUgPSB0aGlzLmF1ZGlvT25seSAmJiAhdGhpcy5hbHRBdWRpbyA/IEVsZW1lbnRhcnlTdHJlYW1UeXBlcy5BVURJTyA6IEVsZW1lbnRhcnlTdHJlYW1UeXBlcy5WSURFTztcbiAgICAgICAgY29uc3QgbWVkaWFCdWZmZXIgPSAodHlwZSA9PT0gRWxlbWVudGFyeVN0cmVhbVR5cGVzLlZJREVPID8gdGhpcy52aWRlb0J1ZmZlciA6IHRoaXMubWVkaWFCdWZmZXIpIHx8IHRoaXMubWVkaWE7XG4gICAgICAgIGlmIChtZWRpYUJ1ZmZlcikge1xuICAgICAgICAgIHRoaXMuYWZ0ZXJCdWZmZXJGbHVzaGVkKG1lZGlhQnVmZmVyLCB0eXBlLCBQbGF5bGlzdExldmVsVHlwZS5NQUlOKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZnJhZyA9IHRoaXMuZ2V0TmV4dEZyYWdtZW50TG9vcExvYWRpbmcoZnJhZywgbGV2ZWxEZXRhaWxzLCBidWZmZXJJbmZvLCBQbGF5bGlzdExldmVsVHlwZS5NQUlOLCBtYXhCdWZMZW4pO1xuICAgIH1cbiAgICBpZiAoIWZyYWcpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKGZyYWcuaW5pdFNlZ21lbnQgJiYgIWZyYWcuaW5pdFNlZ21lbnQuZGF0YSAmJiAhdGhpcy5iaXRyYXRlVGVzdCkge1xuICAgICAgZnJhZyA9IGZyYWcuaW5pdFNlZ21lbnQ7XG4gICAgfVxuICAgIHRoaXMubG9hZEZyYWdtZW50KGZyYWcsIGxldmVsSW5mbywgdGFyZ2V0QnVmZmVyVGltZSk7XG4gIH1cbiAgbG9hZEZyYWdtZW50KGZyYWcsIGxldmVsLCB0YXJnZXRCdWZmZXJUaW1lKSB7XG4gICAgLy8gQ2hlY2sgaWYgZnJhZ21lbnQgaXMgbm90IGxvYWRlZFxuICAgIGNvbnN0IGZyYWdTdGF0ZSA9IHRoaXMuZnJhZ21lbnRUcmFja2VyLmdldFN0YXRlKGZyYWcpO1xuICAgIHRoaXMuZnJhZ0N1cnJlbnQgPSBmcmFnO1xuICAgIGlmIChmcmFnU3RhdGUgPT09IEZyYWdtZW50U3RhdGUuTk9UX0xPQURFRCB8fCBmcmFnU3RhdGUgPT09IEZyYWdtZW50U3RhdGUuUEFSVElBTCkge1xuICAgICAgaWYgKGZyYWcuc24gPT09ICdpbml0U2VnbWVudCcpIHtcbiAgICAgICAgdGhpcy5fbG9hZEluaXRTZWdtZW50KGZyYWcsIGxldmVsKTtcbiAgICAgIH0gZWxzZSBpZiAodGhpcy5iaXRyYXRlVGVzdCkge1xuICAgICAgICB0aGlzLmxvZyhgRnJhZ21lbnQgJHtmcmFnLnNufSBvZiBsZXZlbCAke2ZyYWcubGV2ZWx9IGlzIGJlaW5nIGRvd25sb2FkZWQgdG8gdGVzdCBiaXRyYXRlIGFuZCB3aWxsIG5vdCBiZSBidWZmZXJlZGApO1xuICAgICAgICB0aGlzLl9sb2FkQml0cmF0ZVRlc3RGcmFnKGZyYWcsIGxldmVsKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuc3RhcnRGcmFnUmVxdWVzdGVkID0gdHJ1ZTtcbiAgICAgICAgc3VwZXIubG9hZEZyYWdtZW50KGZyYWcsIGxldmVsLCB0YXJnZXRCdWZmZXJUaW1lKTtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5jbGVhclRyYWNrZXJJZk5lZWRlZChmcmFnKTtcbiAgICB9XG4gIH1cbiAgZ2V0QnVmZmVyZWRGcmFnKHBvc2l0aW9uKSB7XG4gICAgcmV0dXJuIHRoaXMuZnJhZ21lbnRUcmFja2VyLmdldEJ1ZmZlcmVkRnJhZyhwb3NpdGlvbiwgUGxheWxpc3RMZXZlbFR5cGUuTUFJTik7XG4gIH1cbiAgZm9sbG93aW5nQnVmZmVyZWRGcmFnKGZyYWcpIHtcbiAgICBpZiAoZnJhZykge1xuICAgICAgLy8gdHJ5IHRvIGdldCByYW5nZSBvZiBuZXh0IGZyYWdtZW50ICg1MDBtcyBhZnRlciB0aGlzIHJhbmdlKVxuICAgICAgcmV0dXJuIHRoaXMuZ2V0QnVmZmVyZWRGcmFnKGZyYWcuZW5kICsgMC41KTtcbiAgICB9XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICAvKlxuICAgIG9uIGltbWVkaWF0ZSBsZXZlbCBzd2l0Y2ggOlxuICAgICAtIHBhdXNlIHBsYXliYWNrIGlmIHBsYXlpbmdcbiAgICAgLSBjYW5jZWwgYW55IHBlbmRpbmcgbG9hZCByZXF1ZXN0XG4gICAgIC0gYW5kIHRyaWdnZXIgYSBidWZmZXIgZmx1c2hcbiAgKi9cbiAgaW1tZWRpYXRlTGV2ZWxTd2l0Y2goKSB7XG4gICAgdGhpcy5hYm9ydEN1cnJlbnRGcmFnKCk7XG4gICAgdGhpcy5mbHVzaE1haW5CdWZmZXIoMCwgTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZKTtcbiAgfVxuXG4gIC8qKlxuICAgKiB0cnkgdG8gc3dpdGNoIEFTQVAgd2l0aG91dCBicmVha2luZyB2aWRlbyBwbGF5YmFjazpcbiAgICogaW4gb3JkZXIgdG8gZW5zdXJlIHNtb290aCBidXQgcXVpY2sgbGV2ZWwgc3dpdGNoaW5nLFxuICAgKiB3ZSBuZWVkIHRvIGZpbmQgdGhlIG5leHQgZmx1c2hhYmxlIGJ1ZmZlciByYW5nZVxuICAgKiB3ZSBzaG91bGQgdGFrZSBpbnRvIGFjY291bnQgbmV3IHNlZ21lbnQgZmV0Y2ggdGltZVxuICAgKi9cbiAgbmV4dExldmVsU3dpdGNoKCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGxldmVscyxcbiAgICAgIG1lZGlhXG4gICAgfSA9IHRoaXM7XG4gICAgLy8gZW5zdXJlIHRoYXQgbWVkaWEgaXMgZGVmaW5lZCBhbmQgdGhhdCBtZXRhZGF0YSBhcmUgYXZhaWxhYmxlICh0byByZXRyaWV2ZSBjdXJyZW50VGltZSlcbiAgICBpZiAobWVkaWEgIT0gbnVsbCAmJiBtZWRpYS5yZWFkeVN0YXRlKSB7XG4gICAgICBsZXQgZmV0Y2hkZWxheTtcbiAgICAgIGNvbnN0IGZyYWdQbGF5aW5nQ3VycmVudCA9IHRoaXMuZ2V0QXBwZW5kZWRGcmFnKG1lZGlhLmN1cnJlbnRUaW1lKTtcbiAgICAgIGlmIChmcmFnUGxheWluZ0N1cnJlbnQgJiYgZnJhZ1BsYXlpbmdDdXJyZW50LnN0YXJ0ID4gMSkge1xuICAgICAgICAvLyBmbHVzaCBidWZmZXIgcHJlY2VkaW5nIGN1cnJlbnQgZnJhZ21lbnQgKGZsdXNoIHVudGlsIGN1cnJlbnQgZnJhZ21lbnQgc3RhcnQgb2Zmc2V0KVxuICAgICAgICAvLyBtaW51cyAxcyB0byBhdm9pZCB2aWRlbyBmcmVlemluZywgdGhhdCBjb3VsZCBoYXBwZW4gaWYgd2UgZmx1c2gga2V5ZnJhbWUgb2YgY3VycmVudCB2aWRlbyAuLi5cbiAgICAgICAgdGhpcy5mbHVzaE1haW5CdWZmZXIoMCwgZnJhZ1BsYXlpbmdDdXJyZW50LnN0YXJ0IC0gMSk7XG4gICAgICB9XG4gICAgICBjb25zdCBsZXZlbERldGFpbHMgPSB0aGlzLmdldExldmVsRGV0YWlscygpO1xuICAgICAgaWYgKGxldmVsRGV0YWlscyAhPSBudWxsICYmIGxldmVsRGV0YWlscy5saXZlKSB7XG4gICAgICAgIGNvbnN0IGJ1ZmZlckluZm8gPSB0aGlzLmdldE1haW5Gd2RCdWZmZXJJbmZvKCk7XG4gICAgICAgIC8vIERvIG5vdCBmbHVzaCBpbiBsaXZlIHN0cmVhbSB3aXRoIGxvdyBidWZmZXJcbiAgICAgICAgaWYgKCFidWZmZXJJbmZvIHx8IGJ1ZmZlckluZm8ubGVuIDwgbGV2ZWxEZXRhaWxzLnRhcmdldGR1cmF0aW9uICogMikge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgaWYgKCFtZWRpYS5wYXVzZWQgJiYgbGV2ZWxzKSB7XG4gICAgICAgIC8vIGFkZCBhIHNhZmV0eSBkZWxheSBvZiAxc1xuICAgICAgICBjb25zdCBuZXh0TGV2ZWxJZCA9IHRoaXMuaGxzLm5leHRMb2FkTGV2ZWw7XG4gICAgICAgIGNvbnN0IG5leHRMZXZlbCA9IGxldmVsc1tuZXh0TGV2ZWxJZF07XG4gICAgICAgIGNvbnN0IGZyYWdMYXN0S2JwcyA9IHRoaXMuZnJhZ0xhc3RLYnBzO1xuICAgICAgICBpZiAoZnJhZ0xhc3RLYnBzICYmIHRoaXMuZnJhZ0N1cnJlbnQpIHtcbiAgICAgICAgICBmZXRjaGRlbGF5ID0gdGhpcy5mcmFnQ3VycmVudC5kdXJhdGlvbiAqIG5leHRMZXZlbC5tYXhCaXRyYXRlIC8gKDEwMDAgKiBmcmFnTGFzdEticHMpICsgMTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBmZXRjaGRlbGF5ID0gMDtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZmV0Y2hkZWxheSA9IDA7XG4gICAgICB9XG4gICAgICAvLyB0aGlzLmxvZygnZmV0Y2hkZWxheTonK2ZldGNoZGVsYXkpO1xuICAgICAgLy8gZmluZCBidWZmZXIgcmFuZ2UgdGhhdCB3aWxsIGJlIHJlYWNoZWQgb25jZSBuZXcgZnJhZ21lbnQgd2lsbCBiZSBmZXRjaGVkXG4gICAgICBjb25zdCBidWZmZXJlZEZyYWcgPSB0aGlzLmdldEJ1ZmZlcmVkRnJhZyhtZWRpYS5jdXJyZW50VGltZSArIGZldGNoZGVsYXkpO1xuICAgICAgaWYgKGJ1ZmZlcmVkRnJhZykge1xuICAgICAgICAvLyB3ZSBjYW4gZmx1c2ggYnVmZmVyIHJhbmdlIGZvbGxvd2luZyB0aGlzIG9uZSB3aXRob3V0IHN0YWxsaW5nIHBsYXliYWNrXG4gICAgICAgIGNvbnN0IG5leHRCdWZmZXJlZEZyYWcgPSB0aGlzLmZvbGxvd2luZ0J1ZmZlcmVkRnJhZyhidWZmZXJlZEZyYWcpO1xuICAgICAgICBpZiAobmV4dEJ1ZmZlcmVkRnJhZykge1xuICAgICAgICAgIC8vIGlmIHdlIGFyZSBoZXJlLCB3ZSBjYW4gYWxzbyBjYW5jZWwgYW55IGxvYWRpbmcvZGVtdXhpbmcgaW4gcHJvZ3Jlc3MsIGFzIHRoZXkgYXJlIHVzZWxlc3NcbiAgICAgICAgICB0aGlzLmFib3J0Q3VycmVudEZyYWcoKTtcbiAgICAgICAgICAvLyBzdGFydCBmbHVzaCBwb3NpdGlvbiBpcyBpbiBuZXh0IGJ1ZmZlcmVkIGZyYWcuIExlYXZlIHNvbWUgcGFkZGluZyBmb3Igbm9uLWluZGVwZW5kZW50IHNlZ21lbnRzIGFuZCBzbW9vdGhlciBwbGF5YmFjay5cbiAgICAgICAgICBjb25zdCBtYXhTdGFydCA9IG5leHRCdWZmZXJlZEZyYWcubWF4U3RhcnRQVFMgPyBuZXh0QnVmZmVyZWRGcmFnLm1heFN0YXJ0UFRTIDogbmV4dEJ1ZmZlcmVkRnJhZy5zdGFydDtcbiAgICAgICAgICBjb25zdCBmcmFnRHVyYXRpb24gPSBuZXh0QnVmZmVyZWRGcmFnLmR1cmF0aW9uO1xuICAgICAgICAgIGNvbnN0IHN0YXJ0UHRzID0gTWF0aC5tYXgoYnVmZmVyZWRGcmFnLmVuZCwgbWF4U3RhcnQgKyBNYXRoLm1pbihNYXRoLm1heChmcmFnRHVyYXRpb24gLSB0aGlzLmNvbmZpZy5tYXhGcmFnTG9va1VwVG9sZXJhbmNlLCBmcmFnRHVyYXRpb24gKiAodGhpcy5jb3VsZEJhY2t0cmFjayA/IDAuNSA6IDAuMTI1KSksIGZyYWdEdXJhdGlvbiAqICh0aGlzLmNvdWxkQmFja3RyYWNrID8gMC43NSA6IDAuMjUpKSk7XG4gICAgICAgICAgdGhpcy5mbHVzaE1haW5CdWZmZXIoc3RhcnRQdHMsIE51bWJlci5QT1NJVElWRV9JTkZJTklUWSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgYWJvcnRDdXJyZW50RnJhZygpIHtcbiAgICBjb25zdCBmcmFnQ3VycmVudCA9IHRoaXMuZnJhZ0N1cnJlbnQ7XG4gICAgdGhpcy5mcmFnQ3VycmVudCA9IG51bGw7XG4gICAgdGhpcy5iYWNrdHJhY2tGcmFnbWVudCA9IG51bGw7XG4gICAgaWYgKGZyYWdDdXJyZW50KSB7XG4gICAgICBmcmFnQ3VycmVudC5hYm9ydFJlcXVlc3RzKCk7XG4gICAgICB0aGlzLmZyYWdtZW50VHJhY2tlci5yZW1vdmVGcmFnbWVudChmcmFnQ3VycmVudCk7XG4gICAgfVxuICAgIHN3aXRjaCAodGhpcy5zdGF0ZSkge1xuICAgICAgY2FzZSBTdGF0ZS5LRVlfTE9BRElORzpcbiAgICAgIGNhc2UgU3RhdGUuRlJBR19MT0FESU5HOlxuICAgICAgY2FzZSBTdGF0ZS5GUkFHX0xPQURJTkdfV0FJVElOR19SRVRSWTpcbiAgICAgIGNhc2UgU3RhdGUuUEFSU0lORzpcbiAgICAgIGNhc2UgU3RhdGUuUEFSU0VEOlxuICAgICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuICAgIHRoaXMubmV4dExvYWRQb3NpdGlvbiA9IHRoaXMuZ2V0TG9hZFBvc2l0aW9uKCk7XG4gIH1cbiAgZmx1c2hNYWluQnVmZmVyKHN0YXJ0T2Zmc2V0LCBlbmRPZmZzZXQpIHtcbiAgICBzdXBlci5mbHVzaE1haW5CdWZmZXIoc3RhcnRPZmZzZXQsIGVuZE9mZnNldCwgdGhpcy5hbHRBdWRpbyA/ICd2aWRlbycgOiBudWxsKTtcbiAgfVxuICBvbk1lZGlhQXR0YWNoZWQoZXZlbnQsIGRhdGEpIHtcbiAgICBzdXBlci5vbk1lZGlhQXR0YWNoZWQoZXZlbnQsIGRhdGEpO1xuICAgIGNvbnN0IG1lZGlhID0gZGF0YS5tZWRpYTtcbiAgICB0aGlzLm9udnBsYXlpbmcgPSB0aGlzLm9uTWVkaWFQbGF5aW5nLmJpbmQodGhpcyk7XG4gICAgdGhpcy5vbnZzZWVrZWQgPSB0aGlzLm9uTWVkaWFTZWVrZWQuYmluZCh0aGlzKTtcbiAgICBtZWRpYS5hZGRFdmVudExpc3RlbmVyKCdwbGF5aW5nJywgdGhpcy5vbnZwbGF5aW5nKTtcbiAgICBtZWRpYS5hZGRFdmVudExpc3RlbmVyKCdzZWVrZWQnLCB0aGlzLm9udnNlZWtlZCk7XG4gICAgdGhpcy5nYXBDb250cm9sbGVyID0gbmV3IEdhcENvbnRyb2xsZXIodGhpcy5jb25maWcsIG1lZGlhLCB0aGlzLmZyYWdtZW50VHJhY2tlciwgdGhpcy5obHMpO1xuICB9XG4gIG9uTWVkaWFEZXRhY2hpbmcoKSB7XG4gICAgY29uc3Qge1xuICAgICAgbWVkaWFcbiAgICB9ID0gdGhpcztcbiAgICBpZiAobWVkaWEgJiYgdGhpcy5vbnZwbGF5aW5nICYmIHRoaXMub252c2Vla2VkKSB7XG4gICAgICBtZWRpYS5yZW1vdmVFdmVudExpc3RlbmVyKCdwbGF5aW5nJywgdGhpcy5vbnZwbGF5aW5nKTtcbiAgICAgIG1lZGlhLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3NlZWtlZCcsIHRoaXMub252c2Vla2VkKTtcbiAgICAgIHRoaXMub252cGxheWluZyA9IHRoaXMub252c2Vla2VkID0gbnVsbDtcbiAgICAgIHRoaXMudmlkZW9CdWZmZXIgPSBudWxsO1xuICAgIH1cbiAgICB0aGlzLmZyYWdQbGF5aW5nID0gbnVsbDtcbiAgICBpZiAodGhpcy5nYXBDb250cm9sbGVyKSB7XG4gICAgICB0aGlzLmdhcENvbnRyb2xsZXIuZGVzdHJveSgpO1xuICAgICAgdGhpcy5nYXBDb250cm9sbGVyID0gbnVsbDtcbiAgICB9XG4gICAgc3VwZXIub25NZWRpYURldGFjaGluZygpO1xuICB9XG4gIG9uTWVkaWFQbGF5aW5nKCkge1xuICAgIC8vIHRpY2sgdG8gc3BlZWQgdXAgRlJBR19DSEFOR0VEIHRyaWdnZXJpbmdcbiAgICB0aGlzLnRpY2soKTtcbiAgfVxuICBvbk1lZGlhU2Vla2VkKCkge1xuICAgIGNvbnN0IG1lZGlhID0gdGhpcy5tZWRpYTtcbiAgICBjb25zdCBjdXJyZW50VGltZSA9IG1lZGlhID8gbWVkaWEuY3VycmVudFRpbWUgOiBudWxsO1xuICAgIGlmIChpc0Zpbml0ZU51bWJlcihjdXJyZW50VGltZSkpIHtcbiAgICAgIHRoaXMubG9nKGBNZWRpYSBzZWVrZWQgdG8gJHtjdXJyZW50VGltZS50b0ZpeGVkKDMpfWApO1xuICAgIH1cblxuICAgIC8vIElmIHNlZWtlZCB3YXMgaXNzdWVkIGJlZm9yZSBidWZmZXIgd2FzIGFwcGVuZGVkIGRvIG5vdCB0aWNrIGltbWVkaWF0ZWx5XG4gICAgY29uc3QgYnVmZmVySW5mbyA9IHRoaXMuZ2V0TWFpbkZ3ZEJ1ZmZlckluZm8oKTtcbiAgICBpZiAoYnVmZmVySW5mbyA9PT0gbnVsbCB8fCBidWZmZXJJbmZvLmxlbiA9PT0gMCkge1xuICAgICAgdGhpcy53YXJuKGBNYWluIGZvcndhcmQgYnVmZmVyIGxlbmd0aCBvbiBcInNlZWtlZFwiIGV2ZW50ICR7YnVmZmVySW5mbyA/IGJ1ZmZlckluZm8ubGVuIDogJ2VtcHR5J30pYCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gdGljayB0byBzcGVlZCB1cCBGUkFHX0NIQU5HRUQgdHJpZ2dlcmluZ1xuICAgIHRoaXMudGljaygpO1xuICB9XG4gIG9uTWFuaWZlc3RMb2FkaW5nKCkge1xuICAgIC8vIHJlc2V0IGJ1ZmZlciBvbiBtYW5pZmVzdCBsb2FkaW5nXG4gICAgdGhpcy5sb2coJ1RyaWdnZXIgQlVGRkVSX1JFU0VUJyk7XG4gICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuQlVGRkVSX1JFU0VULCB1bmRlZmluZWQpO1xuICAgIHRoaXMuZnJhZ21lbnRUcmFja2VyLnJlbW92ZUFsbEZyYWdtZW50cygpO1xuICAgIHRoaXMuY291bGRCYWNrdHJhY2sgPSBmYWxzZTtcbiAgICB0aGlzLnN0YXJ0UG9zaXRpb24gPSB0aGlzLmxhc3RDdXJyZW50VGltZSA9IHRoaXMuZnJhZ0xhc3RLYnBzID0gMDtcbiAgICB0aGlzLmxldmVscyA9IHRoaXMuZnJhZ1BsYXlpbmcgPSB0aGlzLmJhY2t0cmFja0ZyYWdtZW50ID0gdGhpcy5sZXZlbExhc3RMb2FkZWQgPSBudWxsO1xuICAgIHRoaXMuYWx0QXVkaW8gPSB0aGlzLmF1ZGlvT25seSA9IHRoaXMuc3RhcnRGcmFnUmVxdWVzdGVkID0gZmFsc2U7XG4gIH1cbiAgb25NYW5pZmVzdFBhcnNlZChldmVudCwgZGF0YSkge1xuICAgIC8vIGRldGVjdCBpZiB3ZSBoYXZlIGRpZmZlcmVudCBraW5kIG9mIGF1ZGlvIGNvZGVjcyB1c2VkIGFtb25nc3QgcGxheWxpc3RzXG4gICAgbGV0IGFhYyA9IGZhbHNlO1xuICAgIGxldCBoZWFhYyA9IGZhbHNlO1xuICAgIGRhdGEubGV2ZWxzLmZvckVhY2gobGV2ZWwgPT4ge1xuICAgICAgY29uc3QgY29kZWMgPSBsZXZlbC5hdWRpb0NvZGVjO1xuICAgICAgaWYgKGNvZGVjKSB7XG4gICAgICAgIGFhYyA9IGFhYyB8fCBjb2RlYy5pbmRleE9mKCdtcDRhLjQwLjInKSAhPT0gLTE7XG4gICAgICAgIGhlYWFjID0gaGVhYWMgfHwgY29kZWMuaW5kZXhPZignbXA0YS40MC41JykgIT09IC0xO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHRoaXMuYXVkaW9Db2RlY1N3aXRjaCA9IGFhYyAmJiBoZWFhYyAmJiAhY2hhbmdlVHlwZVN1cHBvcnRlZCgpO1xuICAgIGlmICh0aGlzLmF1ZGlvQ29kZWNTd2l0Y2gpIHtcbiAgICAgIHRoaXMubG9nKCdCb3RoIEFBQy9IRS1BQUMgYXVkaW8gZm91bmQgaW4gbGV2ZWxzOyBkZWNsYXJpbmcgbGV2ZWwgY29kZWMgYXMgSEUtQUFDJyk7XG4gICAgfVxuICAgIHRoaXMubGV2ZWxzID0gZGF0YS5sZXZlbHM7XG4gICAgdGhpcy5zdGFydEZyYWdSZXF1ZXN0ZWQgPSBmYWxzZTtcbiAgfVxuICBvbkxldmVsTG9hZGluZyhldmVudCwgZGF0YSkge1xuICAgIGNvbnN0IHtcbiAgICAgIGxldmVsc1xuICAgIH0gPSB0aGlzO1xuICAgIGlmICghbGV2ZWxzIHx8IHRoaXMuc3RhdGUgIT09IFN0YXRlLklETEUpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgbGV2ZWwgPSBsZXZlbHNbZGF0YS5sZXZlbF07XG4gICAgaWYgKCFsZXZlbC5kZXRhaWxzIHx8IGxldmVsLmRldGFpbHMubGl2ZSAmJiB0aGlzLmxldmVsTGFzdExvYWRlZCAhPT0gbGV2ZWwgfHwgdGhpcy53YWl0Rm9yQ2RuVHVuZUluKGxldmVsLmRldGFpbHMpKSB7XG4gICAgICB0aGlzLnN0YXRlID0gU3RhdGUuV0FJVElOR19MRVZFTDtcbiAgICB9XG4gIH1cbiAgb25MZXZlbExvYWRlZChldmVudCwgZGF0YSkge1xuICAgIHZhciBfY3VyTGV2ZWwkZGV0YWlscztcbiAgICBjb25zdCB7XG4gICAgICBsZXZlbHNcbiAgICB9ID0gdGhpcztcbiAgICBjb25zdCBuZXdMZXZlbElkID0gZGF0YS5sZXZlbDtcbiAgICBjb25zdCBuZXdEZXRhaWxzID0gZGF0YS5kZXRhaWxzO1xuICAgIGNvbnN0IGR1cmF0aW9uID0gbmV3RGV0YWlscy50b3RhbGR1cmF0aW9uO1xuICAgIGlmICghbGV2ZWxzKSB7XG4gICAgICB0aGlzLndhcm4oYExldmVscyB3ZXJlIHJlc2V0IHdoaWxlIGxvYWRpbmcgbGV2ZWwgJHtuZXdMZXZlbElkfWApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLmxvZyhgTGV2ZWwgJHtuZXdMZXZlbElkfSBsb2FkZWQgWyR7bmV3RGV0YWlscy5zdGFydFNOfSwke25ld0RldGFpbHMuZW5kU059XSR7bmV3RGV0YWlscy5sYXN0UGFydFNuID8gYFtwYXJ0LSR7bmV3RGV0YWlscy5sYXN0UGFydFNufS0ke25ld0RldGFpbHMubGFzdFBhcnRJbmRleH1dYCA6ICcnfSwgY2MgWyR7bmV3RGV0YWlscy5zdGFydENDfSwgJHtuZXdEZXRhaWxzLmVuZENDfV0gZHVyYXRpb246JHtkdXJhdGlvbn1gKTtcbiAgICBjb25zdCBjdXJMZXZlbCA9IGxldmVsc1tuZXdMZXZlbElkXTtcbiAgICBjb25zdCBmcmFnQ3VycmVudCA9IHRoaXMuZnJhZ0N1cnJlbnQ7XG4gICAgaWYgKGZyYWdDdXJyZW50ICYmICh0aGlzLnN0YXRlID09PSBTdGF0ZS5GUkFHX0xPQURJTkcgfHwgdGhpcy5zdGF0ZSA9PT0gU3RhdGUuRlJBR19MT0FESU5HX1dBSVRJTkdfUkVUUlkpKSB7XG4gICAgICBpZiAoZnJhZ0N1cnJlbnQubGV2ZWwgIT09IGRhdGEubGV2ZWwgJiYgZnJhZ0N1cnJlbnQubG9hZGVyKSB7XG4gICAgICAgIHRoaXMuYWJvcnRDdXJyZW50RnJhZygpO1xuICAgICAgfVxuICAgIH1cbiAgICBsZXQgc2xpZGluZyA9IDA7XG4gICAgaWYgKG5ld0RldGFpbHMubGl2ZSB8fCAoX2N1ckxldmVsJGRldGFpbHMgPSBjdXJMZXZlbC5kZXRhaWxzKSAhPSBudWxsICYmIF9jdXJMZXZlbCRkZXRhaWxzLmxpdmUpIHtcbiAgICAgIHZhciBfdGhpcyRsZXZlbExhc3RMb2FkZWQ7XG4gICAgICB0aGlzLmNoZWNrTGl2ZVVwZGF0ZShuZXdEZXRhaWxzKTtcbiAgICAgIGlmIChuZXdEZXRhaWxzLmRlbHRhVXBkYXRlRmFpbGVkKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHNsaWRpbmcgPSB0aGlzLmFsaWduUGxheWxpc3RzKG5ld0RldGFpbHMsIGN1ckxldmVsLmRldGFpbHMsIChfdGhpcyRsZXZlbExhc3RMb2FkZWQgPSB0aGlzLmxldmVsTGFzdExvYWRlZCkgPT0gbnVsbCA/IHZvaWQgMCA6IF90aGlzJGxldmVsTGFzdExvYWRlZC5kZXRhaWxzKTtcbiAgICB9XG4gICAgLy8gb3ZlcnJpZGUgbGV2ZWwgaW5mb1xuICAgIGN1ckxldmVsLmRldGFpbHMgPSBuZXdEZXRhaWxzO1xuICAgIHRoaXMubGV2ZWxMYXN0TG9hZGVkID0gY3VyTGV2ZWw7XG4gICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuTEVWRUxfVVBEQVRFRCwge1xuICAgICAgZGV0YWlsczogbmV3RGV0YWlscyxcbiAgICAgIGxldmVsOiBuZXdMZXZlbElkXG4gICAgfSk7XG5cbiAgICAvLyBvbmx5IHN3aXRjaCBiYWNrIHRvIElETEUgc3RhdGUgaWYgd2Ugd2VyZSB3YWl0aW5nIGZvciBsZXZlbCB0byBzdGFydCBkb3dubG9hZGluZyBhIG5ldyBmcmFnbWVudFxuICAgIGlmICh0aGlzLnN0YXRlID09PSBTdGF0ZS5XQUlUSU5HX0xFVkVMKSB7XG4gICAgICBpZiAodGhpcy53YWl0Rm9yQ2RuVHVuZUluKG5ld0RldGFpbHMpKSB7XG4gICAgICAgIC8vIFdhaXQgZm9yIExvdy1MYXRlbmN5IENETiBUdW5lLWluXG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgIH1cbiAgICBpZiAoIXRoaXMuc3RhcnRGcmFnUmVxdWVzdGVkKSB7XG4gICAgICB0aGlzLnNldFN0YXJ0UG9zaXRpb24obmV3RGV0YWlscywgc2xpZGluZyk7XG4gICAgfSBlbHNlIGlmIChuZXdEZXRhaWxzLmxpdmUpIHtcbiAgICAgIHRoaXMuc3luY2hyb25pemVUb0xpdmVFZGdlKG5ld0RldGFpbHMpO1xuICAgIH1cblxuICAgIC8vIHRyaWdnZXIgaGFuZGxlciByaWdodCBub3dcbiAgICB0aGlzLnRpY2soKTtcbiAgfVxuICBfaGFuZGxlRnJhZ21lbnRMb2FkUHJvZ3Jlc3MoZGF0YSkge1xuICAgIHZhciBfZnJhZyRpbml0U2VnbWVudDtcbiAgICBjb25zdCB7XG4gICAgICBmcmFnLFxuICAgICAgcGFydCxcbiAgICAgIHBheWxvYWRcbiAgICB9ID0gZGF0YTtcbiAgICBjb25zdCB7XG4gICAgICBsZXZlbHNcbiAgICB9ID0gdGhpcztcbiAgICBpZiAoIWxldmVscykge1xuICAgICAgdGhpcy53YXJuKGBMZXZlbHMgd2VyZSByZXNldCB3aGlsZSBmcmFnbWVudCBsb2FkIHdhcyBpbiBwcm9ncmVzcy4gRnJhZ21lbnQgJHtmcmFnLnNufSBvZiBsZXZlbCAke2ZyYWcubGV2ZWx9IHdpbGwgbm90IGJlIGJ1ZmZlcmVkYCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IGN1cnJlbnRMZXZlbCA9IGxldmVsc1tmcmFnLmxldmVsXTtcbiAgICBjb25zdCBkZXRhaWxzID0gY3VycmVudExldmVsLmRldGFpbHM7XG4gICAgaWYgKCFkZXRhaWxzKSB7XG4gICAgICB0aGlzLndhcm4oYERyb3BwaW5nIGZyYWdtZW50ICR7ZnJhZy5zbn0gb2YgbGV2ZWwgJHtmcmFnLmxldmVsfSBhZnRlciBsZXZlbCBkZXRhaWxzIHdlcmUgcmVzZXRgKTtcbiAgICAgIHRoaXMuZnJhZ21lbnRUcmFja2VyLnJlbW92ZUZyYWdtZW50KGZyYWcpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCB2aWRlb0NvZGVjID0gY3VycmVudExldmVsLnZpZGVvQ29kZWM7XG5cbiAgICAvLyB0aW1lIE9mZnNldCBpcyBhY2N1cmF0ZSBpZiBsZXZlbCBQVFMgaXMga25vd24sIG9yIGlmIHBsYXlsaXN0IGlzIG5vdCBzbGlkaW5nIChub3QgbGl2ZSlcbiAgICBjb25zdCBhY2N1cmF0ZVRpbWVPZmZzZXQgPSBkZXRhaWxzLlBUU0tub3duIHx8ICFkZXRhaWxzLmxpdmU7XG4gICAgY29uc3QgaW5pdFNlZ21lbnREYXRhID0gKF9mcmFnJGluaXRTZWdtZW50ID0gZnJhZy5pbml0U2VnbWVudCkgPT0gbnVsbCA/IHZvaWQgMCA6IF9mcmFnJGluaXRTZWdtZW50LmRhdGE7XG4gICAgY29uc3QgYXVkaW9Db2RlYyA9IHRoaXMuX2dldEF1ZGlvQ29kZWMoY3VycmVudExldmVsKTtcblxuICAgIC8vIHRyYW5zbXV4IHRoZSBNUEVHLVRTIGRhdGEgdG8gSVNPLUJNRkYgc2VnbWVudHNcbiAgICAvLyB0aGlzLmxvZyhgVHJhbnNtdXhpbmcgJHtmcmFnLnNufSBvZiBbJHtkZXRhaWxzLnN0YXJ0U059ICwke2RldGFpbHMuZW5kU059XSxsZXZlbCAke2ZyYWcubGV2ZWx9LCBjYyAke2ZyYWcuY2N9YCk7XG4gICAgY29uc3QgdHJhbnNtdXhlciA9IHRoaXMudHJhbnNtdXhlciA9IHRoaXMudHJhbnNtdXhlciB8fCBuZXcgVHJhbnNtdXhlckludGVyZmFjZSh0aGlzLmhscywgUGxheWxpc3RMZXZlbFR5cGUuTUFJTiwgdGhpcy5faGFuZGxlVHJhbnNtdXhDb21wbGV0ZS5iaW5kKHRoaXMpLCB0aGlzLl9oYW5kbGVUcmFuc211eGVyRmx1c2guYmluZCh0aGlzKSk7XG4gICAgY29uc3QgcGFydEluZGV4ID0gcGFydCA/IHBhcnQuaW5kZXggOiAtMTtcbiAgICBjb25zdCBwYXJ0aWFsID0gcGFydEluZGV4ICE9PSAtMTtcbiAgICBjb25zdCBjaHVua01ldGEgPSBuZXcgQ2h1bmtNZXRhZGF0YShmcmFnLmxldmVsLCBmcmFnLnNuLCBmcmFnLnN0YXRzLmNodW5rQ291bnQsIHBheWxvYWQuYnl0ZUxlbmd0aCwgcGFydEluZGV4LCBwYXJ0aWFsKTtcbiAgICBjb25zdCBpbml0UFRTID0gdGhpcy5pbml0UFRTW2ZyYWcuY2NdO1xuICAgIHRyYW5zbXV4ZXIucHVzaChwYXlsb2FkLCBpbml0U2VnbWVudERhdGEsIGF1ZGlvQ29kZWMsIHZpZGVvQ29kZWMsIGZyYWcsIHBhcnQsIGRldGFpbHMudG90YWxkdXJhdGlvbiwgYWNjdXJhdGVUaW1lT2Zmc2V0LCBjaHVua01ldGEsIGluaXRQVFMpO1xuICB9XG4gIG9uQXVkaW9UcmFja1N3aXRjaGluZyhldmVudCwgZGF0YSkge1xuICAgIC8vIGlmIGFueSBVUkwgZm91bmQgb24gbmV3IGF1ZGlvIHRyYWNrLCBpdCBpcyBhbiBhbHRlcm5hdGUgYXVkaW8gdHJhY2tcbiAgICBjb25zdCBmcm9tQWx0QXVkaW8gPSB0aGlzLmFsdEF1ZGlvO1xuICAgIGNvbnN0IGFsdEF1ZGlvID0gISFkYXRhLnVybDtcbiAgICAvLyBpZiB3ZSBzd2l0Y2ggb24gbWFpbiBhdWRpbywgZW5zdXJlIHRoYXQgbWFpbiBmcmFnbWVudCBzY2hlZHVsaW5nIGlzIHN5bmNlZCB3aXRoIG1lZGlhLmJ1ZmZlcmVkXG4gICAgLy8gZG9uJ3QgZG8gYW55dGhpbmcgaWYgd2Ugc3dpdGNoIHRvIGFsdCBhdWRpbzogYXVkaW8gc3RyZWFtIGNvbnRyb2xsZXIgaXMgaGFuZGxpbmcgaXQuXG4gICAgLy8gd2Ugd2lsbCBqdXN0IGhhdmUgdG8gY2hhbmdlIGJ1ZmZlciBzY2hlZHVsaW5nIG9uIGF1ZGlvVHJhY2tTd2l0Y2hlZFxuICAgIGlmICghYWx0QXVkaW8pIHtcbiAgICAgIGlmICh0aGlzLm1lZGlhQnVmZmVyICE9PSB0aGlzLm1lZGlhKSB7XG4gICAgICAgIHRoaXMubG9nKCdTd2l0Y2hpbmcgb24gbWFpbiBhdWRpbywgdXNlIG1lZGlhLmJ1ZmZlcmVkIHRvIHNjaGVkdWxlIG1haW4gZnJhZ21lbnQgbG9hZGluZycpO1xuICAgICAgICB0aGlzLm1lZGlhQnVmZmVyID0gdGhpcy5tZWRpYTtcbiAgICAgICAgY29uc3QgZnJhZ0N1cnJlbnQgPSB0aGlzLmZyYWdDdXJyZW50O1xuICAgICAgICAvLyB3ZSBuZWVkIHRvIHJlZmlsbCBhdWRpbyBidWZmZXIgZnJvbSBtYWluOiBjYW5jZWwgYW55IGZyYWcgbG9hZGluZyB0byBzcGVlZCB1cCBhdWRpbyBzd2l0Y2hcbiAgICAgICAgaWYgKGZyYWdDdXJyZW50KSB7XG4gICAgICAgICAgdGhpcy5sb2coJ1N3aXRjaGluZyB0byBtYWluIGF1ZGlvIHRyYWNrLCBjYW5jZWwgbWFpbiBmcmFnbWVudCBsb2FkJyk7XG4gICAgICAgICAgZnJhZ0N1cnJlbnQuYWJvcnRSZXF1ZXN0cygpO1xuICAgICAgICAgIHRoaXMuZnJhZ21lbnRUcmFja2VyLnJlbW92ZUZyYWdtZW50KGZyYWdDdXJyZW50KTtcbiAgICAgICAgfVxuICAgICAgICAvLyBkZXN0cm95IHRyYW5zbXV4ZXIgdG8gZm9yY2UgaW5pdCBzZWdtZW50IGdlbmVyYXRpb24gKGZvbGxvd2luZyBhdWRpbyBzd2l0Y2gpXG4gICAgICAgIHRoaXMucmVzZXRUcmFuc211eGVyKCk7XG4gICAgICAgIC8vIHN3aXRjaCB0byBJRExFIHN0YXRlIHRvIGxvYWQgbmV3IGZyYWdtZW50XG4gICAgICAgIHRoaXMucmVzZXRMb2FkaW5nU3RhdGUoKTtcbiAgICAgIH0gZWxzZSBpZiAodGhpcy5hdWRpb09ubHkpIHtcbiAgICAgICAgLy8gUmVzZXQgYXVkaW8gdHJhbnNtdXhlciBzbyB3aGVuIHN3aXRjaGluZyBiYWNrIHRvIG1haW4gYXVkaW8gd2UncmUgbm90IHN0aWxsIGFwcGVuZGluZyB3aGVyZSB3ZSBsZWZ0IG9mZlxuICAgICAgICB0aGlzLnJlc2V0VHJhbnNtdXhlcigpO1xuICAgICAgfVxuICAgICAgY29uc3QgaGxzID0gdGhpcy5obHM7XG4gICAgICAvLyBJZiBzd2l0Y2hpbmcgZnJvbSBhbHQgdG8gbWFpbiBhdWRpbywgZmx1c2ggYWxsIGF1ZGlvIGFuZCB0cmlnZ2VyIHRyYWNrIHN3aXRjaGVkXG4gICAgICBpZiAoZnJvbUFsdEF1ZGlvKSB7XG4gICAgICAgIGhscy50cmlnZ2VyKEV2ZW50cy5CVUZGRVJfRkxVU0hJTkcsIHtcbiAgICAgICAgICBzdGFydE9mZnNldDogMCxcbiAgICAgICAgICBlbmRPZmZzZXQ6IE51bWJlci5QT1NJVElWRV9JTkZJTklUWSxcbiAgICAgICAgICB0eXBlOiBudWxsXG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLmZyYWdtZW50VHJhY2tlci5yZW1vdmVBbGxGcmFnbWVudHMoKTtcbiAgICAgIH1cbiAgICAgIGhscy50cmlnZ2VyKEV2ZW50cy5BVURJT19UUkFDS19TV0lUQ0hFRCwgZGF0YSk7XG4gICAgfVxuICB9XG4gIG9uQXVkaW9UcmFja1N3aXRjaGVkKGV2ZW50LCBkYXRhKSB7XG4gICAgY29uc3QgdHJhY2tJZCA9IGRhdGEuaWQ7XG4gICAgY29uc3QgYWx0QXVkaW8gPSAhIXRoaXMuaGxzLmF1ZGlvVHJhY2tzW3RyYWNrSWRdLnVybDtcbiAgICBpZiAoYWx0QXVkaW8pIHtcbiAgICAgIGNvbnN0IHZpZGVvQnVmZmVyID0gdGhpcy52aWRlb0J1ZmZlcjtcbiAgICAgIC8vIGlmIHdlIHN3aXRjaGVkIG9uIGFsdGVybmF0ZSBhdWRpbywgZW5zdXJlIHRoYXQgbWFpbiBmcmFnbWVudCBzY2hlZHVsaW5nIGlzIHN5bmNlZCB3aXRoIHZpZGVvIHNvdXJjZWJ1ZmZlciBidWZmZXJlZFxuICAgICAgaWYgKHZpZGVvQnVmZmVyICYmIHRoaXMubWVkaWFCdWZmZXIgIT09IHZpZGVvQnVmZmVyKSB7XG4gICAgICAgIHRoaXMubG9nKCdTd2l0Y2hpbmcgb24gYWx0ZXJuYXRlIGF1ZGlvLCB1c2UgdmlkZW8uYnVmZmVyZWQgdG8gc2NoZWR1bGUgbWFpbiBmcmFnbWVudCBsb2FkaW5nJyk7XG4gICAgICAgIHRoaXMubWVkaWFCdWZmZXIgPSB2aWRlb0J1ZmZlcjtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5hbHRBdWRpbyA9IGFsdEF1ZGlvO1xuICAgIHRoaXMudGljaygpO1xuICB9XG4gIG9uQnVmZmVyQ3JlYXRlZChldmVudCwgZGF0YSkge1xuICAgIGNvbnN0IHRyYWNrcyA9IGRhdGEudHJhY2tzO1xuICAgIGxldCBtZWRpYVRyYWNrO1xuICAgIGxldCBuYW1lO1xuICAgIGxldCBhbHRlcm5hdGUgPSBmYWxzZTtcbiAgICBmb3IgKGNvbnN0IHR5cGUgaW4gdHJhY2tzKSB7XG4gICAgICBjb25zdCB0cmFjayA9IHRyYWNrc1t0eXBlXTtcbiAgICAgIGlmICh0cmFjay5pZCA9PT0gJ21haW4nKSB7XG4gICAgICAgIG5hbWUgPSB0eXBlO1xuICAgICAgICBtZWRpYVRyYWNrID0gdHJhY2s7XG4gICAgICAgIC8vIGtlZXAgdmlkZW8gc291cmNlIGJ1ZmZlciByZWZlcmVuY2VcbiAgICAgICAgaWYgKHR5cGUgPT09ICd2aWRlbycpIHtcbiAgICAgICAgICBjb25zdCB2aWRlb1RyYWNrID0gdHJhY2tzW3R5cGVdO1xuICAgICAgICAgIGlmICh2aWRlb1RyYWNrKSB7XG4gICAgICAgICAgICB0aGlzLnZpZGVvQnVmZmVyID0gdmlkZW9UcmFjay5idWZmZXI7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBhbHRlcm5hdGUgPSB0cnVlO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAoYWx0ZXJuYXRlICYmIG1lZGlhVHJhY2spIHtcbiAgICAgIHRoaXMubG9nKGBBbHRlcm5hdGUgdHJhY2sgZm91bmQsIHVzZSAke25hbWV9LmJ1ZmZlcmVkIHRvIHNjaGVkdWxlIG1haW4gZnJhZ21lbnQgbG9hZGluZ2ApO1xuICAgICAgdGhpcy5tZWRpYUJ1ZmZlciA9IG1lZGlhVHJhY2suYnVmZmVyO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLm1lZGlhQnVmZmVyID0gdGhpcy5tZWRpYTtcbiAgICB9XG4gIH1cbiAgb25GcmFnQnVmZmVyZWQoZXZlbnQsIGRhdGEpIHtcbiAgICBjb25zdCB7XG4gICAgICBmcmFnLFxuICAgICAgcGFydFxuICAgIH0gPSBkYXRhO1xuICAgIGlmIChmcmFnICYmIGZyYWcudHlwZSAhPT0gUGxheWxpc3RMZXZlbFR5cGUuTUFJTikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpZiAodGhpcy5mcmFnQ29udGV4dENoYW5nZWQoZnJhZykpIHtcbiAgICAgIC8vIElmIGEgbGV2ZWwgc3dpdGNoIHdhcyByZXF1ZXN0ZWQgd2hpbGUgYSBmcmFnbWVudCB3YXMgYnVmZmVyaW5nLCBpdCB3aWxsIGVtaXQgdGhlIEZSQUdfQlVGRkVSRUQgZXZlbnQgdXBvbiBjb21wbGV0aW9uXG4gICAgICAvLyBBdm9pZCBzZXR0aW5nIHN0YXRlIGJhY2sgdG8gSURMRSwgc2luY2UgdGhhdCB3aWxsIGludGVyZmVyZSB3aXRoIGEgbGV2ZWwgc3dpdGNoXG4gICAgICB0aGlzLndhcm4oYEZyYWdtZW50ICR7ZnJhZy5zbn0ke3BhcnQgPyAnIHA6ICcgKyBwYXJ0LmluZGV4IDogJyd9IG9mIGxldmVsICR7ZnJhZy5sZXZlbH0gZmluaXNoZWQgYnVmZmVyaW5nLCBidXQgd2FzIGFib3J0ZWQuIHN0YXRlOiAke3RoaXMuc3RhdGV9YCk7XG4gICAgICBpZiAodGhpcy5zdGF0ZSA9PT0gU3RhdGUuUEFSU0VEKSB7XG4gICAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgICAgfVxuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBzdGF0cyA9IHBhcnQgPyBwYXJ0LnN0YXRzIDogZnJhZy5zdGF0cztcbiAgICB0aGlzLmZyYWdMYXN0S2JwcyA9IE1hdGgucm91bmQoOCAqIHN0YXRzLnRvdGFsIC8gKHN0YXRzLmJ1ZmZlcmluZy5lbmQgLSBzdGF0cy5sb2FkaW5nLmZpcnN0KSk7XG4gICAgaWYgKGZyYWcuc24gIT09ICdpbml0U2VnbWVudCcpIHtcbiAgICAgIHRoaXMuZnJhZ1ByZXZpb3VzID0gZnJhZztcbiAgICB9XG4gICAgdGhpcy5mcmFnQnVmZmVyZWRDb21wbGV0ZShmcmFnLCBwYXJ0KTtcbiAgfVxuICBvbkVycm9yKGV2ZW50LCBkYXRhKSB7XG4gICAgdmFyIF9kYXRhJGNvbnRleHQ7XG4gICAgaWYgKGRhdGEuZmF0YWwpIHtcbiAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5FUlJPUjtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgc3dpdGNoIChkYXRhLmRldGFpbHMpIHtcbiAgICAgIGNhc2UgRXJyb3JEZXRhaWxzLkZSQUdfR0FQOlxuICAgICAgY2FzZSBFcnJvckRldGFpbHMuRlJBR19QQVJTSU5HX0VSUk9SOlxuICAgICAgY2FzZSBFcnJvckRldGFpbHMuRlJBR19ERUNSWVBUX0VSUk9SOlxuICAgICAgY2FzZSBFcnJvckRldGFpbHMuRlJBR19MT0FEX0VSUk9SOlxuICAgICAgY2FzZSBFcnJvckRldGFpbHMuRlJBR19MT0FEX1RJTUVPVVQ6XG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5LRVlfTE9BRF9FUlJPUjpcbiAgICAgIGNhc2UgRXJyb3JEZXRhaWxzLktFWV9MT0FEX1RJTUVPVVQ6XG4gICAgICAgIHRoaXMub25GcmFnbWVudE9yS2V5TG9hZEVycm9yKFBsYXlsaXN0TGV2ZWxUeXBlLk1BSU4sIGRhdGEpO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgRXJyb3JEZXRhaWxzLkxFVkVMX0xPQURfRVJST1I6XG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5MRVZFTF9MT0FEX1RJTUVPVVQ6XG4gICAgICBjYXNlIEVycm9yRGV0YWlscy5MRVZFTF9QQVJTSU5HX0VSUk9SOlxuICAgICAgICAvLyBpbiBjYXNlIG9mIG5vbiBmYXRhbCBlcnJvciB3aGlsZSBsb2FkaW5nIGxldmVsLCBpZiBsZXZlbCBjb250cm9sbGVyIGlzIG5vdCByZXRyeWluZyB0byBsb2FkIGxldmVsLCBzd2l0Y2ggYmFjayB0byBJRExFXG4gICAgICAgIGlmICghZGF0YS5sZXZlbFJldHJ5ICYmIHRoaXMuc3RhdGUgPT09IFN0YXRlLldBSVRJTkdfTEVWRUwgJiYgKChfZGF0YSRjb250ZXh0ID0gZGF0YS5jb250ZXh0KSA9PSBudWxsID8gdm9pZCAwIDogX2RhdGEkY29udGV4dC50eXBlKSA9PT0gUGxheWxpc3RDb250ZXh0VHlwZS5MRVZFTCkge1xuICAgICAgICAgIHRoaXMuc3RhdGUgPSBTdGF0ZS5JRExFO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSBFcnJvckRldGFpbHMuQlVGRkVSX0FQUEVORF9FUlJPUjpcbiAgICAgIGNhc2UgRXJyb3JEZXRhaWxzLkJVRkZFUl9GVUxMX0VSUk9SOlxuICAgICAgICBpZiAoIWRhdGEucGFyZW50IHx8IGRhdGEucGFyZW50ICE9PSAnbWFpbicpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGRhdGEuZGV0YWlscyA9PT0gRXJyb3JEZXRhaWxzLkJVRkZFUl9BUFBFTkRfRVJST1IpIHtcbiAgICAgICAgICB0aGlzLnJlc2V0TG9hZGluZ1N0YXRlKCk7XG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGlmICh0aGlzLnJlZHVjZUxlbmd0aEFuZEZsdXNoQnVmZmVyKGRhdGEpKSB7XG4gICAgICAgICAgdGhpcy5mbHVzaE1haW5CdWZmZXIoMCwgTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZKTtcbiAgICAgICAgfVxuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgRXJyb3JEZXRhaWxzLklOVEVSTkFMX0VYQ0VQVElPTjpcbiAgICAgICAgdGhpcy5yZWNvdmVyV29ya2VyRXJyb3IoZGF0YSk7XG4gICAgICAgIGJyZWFrO1xuICAgIH1cbiAgfVxuXG4gIC8vIENoZWNrcyB0aGUgaGVhbHRoIG9mIHRoZSBidWZmZXIgYW5kIGF0dGVtcHRzIHRvIHJlc29sdmUgcGxheWJhY2sgc3RhbGxzLlxuICBjaGVja0J1ZmZlcigpIHtcbiAgICBjb25zdCB7XG4gICAgICBtZWRpYSxcbiAgICAgIGdhcENvbnRyb2xsZXJcbiAgICB9ID0gdGhpcztcbiAgICBpZiAoIW1lZGlhIHx8ICFnYXBDb250cm9sbGVyIHx8ICFtZWRpYS5yZWFkeVN0YXRlKSB7XG4gICAgICAvLyBFeGl0IGVhcmx5IGlmIHdlIGRvbid0IGhhdmUgbWVkaWEgb3IgaWYgdGhlIG1lZGlhIGhhc24ndCBidWZmZXJlZCBhbnl0aGluZyB5ZXQgKHJlYWR5U3RhdGUgMClcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKHRoaXMubG9hZGVkbWV0YWRhdGEgfHwgIUJ1ZmZlckhlbHBlci5nZXRCdWZmZXJlZChtZWRpYSkubGVuZ3RoKSB7XG4gICAgICAvLyBSZXNvbHZlIGdhcHMgdXNpbmcgdGhlIG1haW4gYnVmZmVyLCB3aG9zZSByYW5nZXMgYXJlIHRoZSBpbnRlcnNlY3Rpb25zIG9mIHRoZSBBL1Ygc291cmNlYnVmZmVyc1xuICAgICAgY29uc3QgYWN0aXZlRnJhZyA9IHRoaXMuc3RhdGUgIT09IFN0YXRlLklETEUgPyB0aGlzLmZyYWdDdXJyZW50IDogbnVsbDtcbiAgICAgIGdhcENvbnRyb2xsZXIucG9sbCh0aGlzLmxhc3RDdXJyZW50VGltZSwgYWN0aXZlRnJhZyk7XG4gICAgfVxuICAgIHRoaXMubGFzdEN1cnJlbnRUaW1lID0gbWVkaWEuY3VycmVudFRpbWU7XG4gIH1cbiAgb25GcmFnTG9hZEVtZXJnZW5jeUFib3J0ZWQoKSB7XG4gICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gICAgLy8gaWYgbG9hZGVkbWV0YWRhdGEgaXMgbm90IHNldCwgaXQgbWVhbnMgdGhhdCB3ZSBhcmUgZW1lcmdlbmN5IHN3aXRjaCBkb3duIG9uIGZpcnN0IGZyYWdcbiAgICAvLyBpbiB0aGF0IGNhc2UsIHJlc2V0IHN0YXJ0RnJhZ1JlcXVlc3RlZCBmbGFnXG4gICAgaWYgKCF0aGlzLmxvYWRlZG1ldGFkYXRhKSB7XG4gICAgICB0aGlzLnN0YXJ0RnJhZ1JlcXVlc3RlZCA9IGZhbHNlO1xuICAgICAgdGhpcy5uZXh0TG9hZFBvc2l0aW9uID0gdGhpcy5zdGFydFBvc2l0aW9uO1xuICAgIH1cbiAgICB0aGlzLnRpY2tJbW1lZGlhdGUoKTtcbiAgfVxuICBvbkJ1ZmZlckZsdXNoZWQoZXZlbnQsIHtcbiAgICB0eXBlXG4gIH0pIHtcbiAgICBpZiAodHlwZSAhPT0gRWxlbWVudGFyeVN0cmVhbVR5cGVzLkFVRElPIHx8IHRoaXMuYXVkaW9Pbmx5ICYmICF0aGlzLmFsdEF1ZGlvKSB7XG4gICAgICBjb25zdCBtZWRpYUJ1ZmZlciA9ICh0eXBlID09PSBFbGVtZW50YXJ5U3RyZWFtVHlwZXMuVklERU8gPyB0aGlzLnZpZGVvQnVmZmVyIDogdGhpcy5tZWRpYUJ1ZmZlcikgfHwgdGhpcy5tZWRpYTtcbiAgICAgIHRoaXMuYWZ0ZXJCdWZmZXJGbHVzaGVkKG1lZGlhQnVmZmVyLCB0eXBlLCBQbGF5bGlzdExldmVsVHlwZS5NQUlOKTtcbiAgICAgIHRoaXMudGljaygpO1xuICAgIH1cbiAgfVxuICBvbkxldmVsc1VwZGF0ZWQoZXZlbnQsIGRhdGEpIHtcbiAgICBpZiAodGhpcy5sZXZlbCA+IC0xICYmIHRoaXMuZnJhZ0N1cnJlbnQpIHtcbiAgICAgIHRoaXMubGV2ZWwgPSB0aGlzLmZyYWdDdXJyZW50LmxldmVsO1xuICAgIH1cbiAgICB0aGlzLmxldmVscyA9IGRhdGEubGV2ZWxzO1xuICB9XG4gIHN3YXBBdWRpb0NvZGVjKCkge1xuICAgIHRoaXMuYXVkaW9Db2RlY1N3YXAgPSAhdGhpcy5hdWRpb0NvZGVjU3dhcDtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZWVrcyB0byB0aGUgc2V0IHN0YXJ0UG9zaXRpb24gaWYgbm90IGVxdWFsIHRvIHRoZSBtZWRpYUVsZW1lbnQncyBjdXJyZW50IHRpbWUuXG4gICAqL1xuICBzZWVrVG9TdGFydFBvcygpIHtcbiAgICBjb25zdCB7XG4gICAgICBtZWRpYVxuICAgIH0gPSB0aGlzO1xuICAgIGlmICghbWVkaWEpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgY3VycmVudFRpbWUgPSBtZWRpYS5jdXJyZW50VGltZTtcbiAgICBsZXQgc3RhcnRQb3NpdGlvbiA9IHRoaXMuc3RhcnRQb3NpdGlvbjtcbiAgICAvLyBvbmx5IGFkanVzdCBjdXJyZW50VGltZSBpZiBkaWZmZXJlbnQgZnJvbSBzdGFydFBvc2l0aW9uIG9yIGlmIHN0YXJ0UG9zaXRpb24gbm90IGJ1ZmZlcmVkXG4gICAgLy8gYXQgdGhhdCBzdGFnZSwgdGhlcmUgc2hvdWxkIGJlIG9ubHkgb25lIGJ1ZmZlcmVkIHJhbmdlLCBhcyB3ZSByZWFjaCB0aGF0IGNvZGUgYWZ0ZXIgZmlyc3QgZnJhZ21lbnQgaGFzIGJlZW4gYnVmZmVyZWRcbiAgICBpZiAoc3RhcnRQb3NpdGlvbiA+PSAwICYmIGN1cnJlbnRUaW1lIDwgc3RhcnRQb3NpdGlvbikge1xuICAgICAgaWYgKG1lZGlhLnNlZWtpbmcpIHtcbiAgICAgICAgdGhpcy5sb2coYGNvdWxkIG5vdCBzZWVrIHRvICR7c3RhcnRQb3NpdGlvbn0sIGFscmVhZHkgc2Vla2luZyBhdCAke2N1cnJlbnRUaW1lfWApO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBjb25zdCBidWZmZXJlZCA9IEJ1ZmZlckhlbHBlci5nZXRCdWZmZXJlZChtZWRpYSk7XG4gICAgICBjb25zdCBidWZmZXJTdGFydCA9IGJ1ZmZlcmVkLmxlbmd0aCA/IGJ1ZmZlcmVkLnN0YXJ0KDApIDogMDtcbiAgICAgIGNvbnN0IGRlbHRhID0gYnVmZmVyU3RhcnQgLSBzdGFydFBvc2l0aW9uO1xuICAgICAgaWYgKGRlbHRhID4gMCAmJiAoZGVsdGEgPCB0aGlzLmNvbmZpZy5tYXhCdWZmZXJIb2xlIHx8IGRlbHRhIDwgdGhpcy5jb25maWcubWF4RnJhZ0xvb2tVcFRvbGVyYW5jZSkpIHtcbiAgICAgICAgdGhpcy5sb2coYGFkanVzdGluZyBzdGFydCBwb3NpdGlvbiBieSAke2RlbHRhfSB0byBtYXRjaCBidWZmZXIgc3RhcnRgKTtcbiAgICAgICAgc3RhcnRQb3NpdGlvbiArPSBkZWx0YTtcbiAgICAgICAgdGhpcy5zdGFydFBvc2l0aW9uID0gc3RhcnRQb3NpdGlvbjtcbiAgICAgIH1cbiAgICAgIHRoaXMubG9nKGBzZWVrIHRvIHRhcmdldCBzdGFydCBwb3NpdGlvbiAke3N0YXJ0UG9zaXRpb259IGZyb20gY3VycmVudCB0aW1lICR7Y3VycmVudFRpbWV9YCk7XG4gICAgICBtZWRpYS5jdXJyZW50VGltZSA9IHN0YXJ0UG9zaXRpb247XG4gICAgfVxuICB9XG4gIF9nZXRBdWRpb0NvZGVjKGN1cnJlbnRMZXZlbCkge1xuICAgIGxldCBhdWRpb0NvZGVjID0gdGhpcy5jb25maWcuZGVmYXVsdEF1ZGlvQ29kZWMgfHwgY3VycmVudExldmVsLmF1ZGlvQ29kZWM7XG4gICAgaWYgKHRoaXMuYXVkaW9Db2RlY1N3YXAgJiYgYXVkaW9Db2RlYykge1xuICAgICAgdGhpcy5sb2coJ1N3YXBwaW5nIGF1ZGlvIGNvZGVjJyk7XG4gICAgICBpZiAoYXVkaW9Db2RlYy5pbmRleE9mKCdtcDRhLjQwLjUnKSAhPT0gLTEpIHtcbiAgICAgICAgYXVkaW9Db2RlYyA9ICdtcDRhLjQwLjInO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgYXVkaW9Db2RlYyA9ICdtcDRhLjQwLjUnO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gYXVkaW9Db2RlYztcbiAgfVxuICBfbG9hZEJpdHJhdGVUZXN0RnJhZyhmcmFnLCBsZXZlbCkge1xuICAgIGZyYWcuYml0cmF0ZVRlc3QgPSB0cnVlO1xuICAgIHRoaXMuX2RvRnJhZ0xvYWQoZnJhZywgbGV2ZWwpLnRoZW4oZGF0YSA9PiB7XG4gICAgICBjb25zdCB7XG4gICAgICAgIGhsc1xuICAgICAgfSA9IHRoaXM7XG4gICAgICBpZiAoIWRhdGEgfHwgdGhpcy5mcmFnQ29udGV4dENoYW5nZWQoZnJhZykpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgbGV2ZWwuZnJhZ21lbnRFcnJvciA9IDA7XG4gICAgICB0aGlzLnN0YXRlID0gU3RhdGUuSURMRTtcbiAgICAgIHRoaXMuc3RhcnRGcmFnUmVxdWVzdGVkID0gZmFsc2U7XG4gICAgICB0aGlzLmJpdHJhdGVUZXN0ID0gZmFsc2U7XG4gICAgICBjb25zdCBzdGF0cyA9IGZyYWcuc3RhdHM7XG4gICAgICAvLyBCaXRyYXRlIHRlc3RzIGZyYWdtZW50cyBhcmUgbmVpdGhlciBwYXJzZWQgbm9yIGJ1ZmZlcmVkXG4gICAgICBzdGF0cy5wYXJzaW5nLnN0YXJ0ID0gc3RhdHMucGFyc2luZy5lbmQgPSBzdGF0cy5idWZmZXJpbmcuc3RhcnQgPSBzdGF0cy5idWZmZXJpbmcuZW5kID0gc2VsZi5wZXJmb3JtYW5jZS5ub3coKTtcbiAgICAgIGhscy50cmlnZ2VyKEV2ZW50cy5GUkFHX0xPQURFRCwgZGF0YSk7XG4gICAgICBmcmFnLmJpdHJhdGVUZXN0ID0gZmFsc2U7XG4gICAgfSk7XG4gIH1cbiAgX2hhbmRsZVRyYW5zbXV4Q29tcGxldGUodHJhbnNtdXhSZXN1bHQpIHtcbiAgICB2YXIgX2lkMyRzYW1wbGVzO1xuICAgIGNvbnN0IGlkID0gJ21haW4nO1xuICAgIGNvbnN0IHtcbiAgICAgIGhsc1xuICAgIH0gPSB0aGlzO1xuICAgIGNvbnN0IHtcbiAgICAgIHJlbXV4UmVzdWx0LFxuICAgICAgY2h1bmtNZXRhXG4gICAgfSA9IHRyYW5zbXV4UmVzdWx0O1xuICAgIGNvbnN0IGNvbnRleHQgPSB0aGlzLmdldEN1cnJlbnRDb250ZXh0KGNodW5rTWV0YSk7XG4gICAgaWYgKCFjb250ZXh0KSB7XG4gICAgICB0aGlzLnJlc2V0V2hlbk1pc3NpbmdDb250ZXh0KGNodW5rTWV0YSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IHtcbiAgICAgIGZyYWcsXG4gICAgICBwYXJ0LFxuICAgICAgbGV2ZWxcbiAgICB9ID0gY29udGV4dDtcbiAgICBjb25zdCB7XG4gICAgICB2aWRlbyxcbiAgICAgIHRleHQsXG4gICAgICBpZDMsXG4gICAgICBpbml0U2VnbWVudFxuICAgIH0gPSByZW11eFJlc3VsdDtcbiAgICBjb25zdCB7XG4gICAgICBkZXRhaWxzXG4gICAgfSA9IGxldmVsO1xuICAgIC8vIFRoZSBhdWRpby1zdHJlYW0tY29udHJvbGxlciBoYW5kbGVzIGF1ZGlvIGJ1ZmZlcmluZyBpZiBIbHMuanMgaXMgcGxheWluZyBhbiBhbHRlcm5hdGUgYXVkaW8gdHJhY2tcbiAgICBjb25zdCBhdWRpbyA9IHRoaXMuYWx0QXVkaW8gPyB1bmRlZmluZWQgOiByZW11eFJlc3VsdC5hdWRpbztcblxuICAgIC8vIENoZWNrIGlmIHRoZSBjdXJyZW50IGZyYWdtZW50IGhhcyBiZWVuIGFib3J0ZWQuIFdlIGNoZWNrIHRoaXMgYnkgZmlyc3Qgc2VlaW5nIGlmIHdlJ3JlIHN0aWxsIHBsYXlpbmcgdGhlIGN1cnJlbnQgbGV2ZWwuXG4gICAgLy8gSWYgd2UgYXJlLCBzdWJzZXF1ZW50bHkgY2hlY2sgaWYgdGhlIGN1cnJlbnRseSBsb2FkaW5nIGZyYWdtZW50IChmcmFnQ3VycmVudCkgaGFzIGNoYW5nZWQuXG4gICAgaWYgKHRoaXMuZnJhZ0NvbnRleHRDaGFuZ2VkKGZyYWcpKSB7XG4gICAgICB0aGlzLmZyYWdtZW50VHJhY2tlci5yZW1vdmVGcmFnbWVudChmcmFnKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhpcy5zdGF0ZSA9IFN0YXRlLlBBUlNJTkc7XG4gICAgaWYgKGluaXRTZWdtZW50KSB7XG4gICAgICBpZiAoaW5pdFNlZ21lbnQgIT0gbnVsbCAmJiBpbml0U2VnbWVudC50cmFja3MpIHtcbiAgICAgICAgY29uc3QgbWFwRnJhZ21lbnQgPSBmcmFnLmluaXRTZWdtZW50IHx8IGZyYWc7XG4gICAgICAgIHRoaXMuX2J1ZmZlckluaXRTZWdtZW50KGxldmVsLCBpbml0U2VnbWVudC50cmFja3MsIG1hcEZyYWdtZW50LCBjaHVua01ldGEpO1xuICAgICAgICBobHMudHJpZ2dlcihFdmVudHMuRlJBR19QQVJTSU5HX0lOSVRfU0VHTUVOVCwge1xuICAgICAgICAgIGZyYWc6IG1hcEZyYWdtZW50LFxuICAgICAgICAgIGlkLFxuICAgICAgICAgIHRyYWNrczogaW5pdFNlZ21lbnQudHJhY2tzXG4gICAgICAgIH0pO1xuICAgICAgfVxuXG4gICAgICAvLyBUaGlzIHdvdWxkIGJlIG5pY2UgaWYgTnVtYmVyLmlzRmluaXRlIGFjdGVkIGFzIGEgdHlwZWd1YXJkLCBidXQgaXQgZG9lc24ndC4gU2VlOiBodHRwczovL2dpdGh1Yi5jb20vTWljcm9zb2Z0L1R5cGVTY3JpcHQvaXNzdWVzLzEwMDM4XG4gICAgICBjb25zdCBpbml0UFRTID0gaW5pdFNlZ21lbnQuaW5pdFBUUztcbiAgICAgIGNvbnN0IHRpbWVzY2FsZSA9IGluaXRTZWdtZW50LnRpbWVzY2FsZTtcbiAgICAgIGlmIChpc0Zpbml0ZU51bWJlcihpbml0UFRTKSkge1xuICAgICAgICB0aGlzLmluaXRQVFNbZnJhZy5jY10gPSB7XG4gICAgICAgICAgYmFzZVRpbWU6IGluaXRQVFMsXG4gICAgICAgICAgdGltZXNjYWxlXG4gICAgICAgIH07XG4gICAgICAgIGhscy50cmlnZ2VyKEV2ZW50cy5JTklUX1BUU19GT1VORCwge1xuICAgICAgICAgIGZyYWcsXG4gICAgICAgICAgaWQsXG4gICAgICAgICAgaW5pdFBUUyxcbiAgICAgICAgICB0aW1lc2NhbGVcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gQXZvaWQgYnVmZmVyaW5nIGlmIGJhY2t0cmFja2luZyB0aGlzIGZyYWdtZW50XG4gICAgaWYgKHZpZGVvICYmIGRldGFpbHMgJiYgZnJhZy5zbiAhPT0gJ2luaXRTZWdtZW50Jykge1xuICAgICAgY29uc3QgcHJldkZyYWcgPSBkZXRhaWxzLmZyYWdtZW50c1tmcmFnLnNuIC0gMSAtIGRldGFpbHMuc3RhcnRTTl07XG4gICAgICBjb25zdCBpc0ZpcnN0RnJhZ21lbnQgPSBmcmFnLnNuID09PSBkZXRhaWxzLnN0YXJ0U047XG4gICAgICBjb25zdCBpc0ZpcnN0SW5EaXNjb250aW51aXR5ID0gIXByZXZGcmFnIHx8IGZyYWcuY2MgPiBwcmV2RnJhZy5jYztcbiAgICAgIGlmIChyZW11eFJlc3VsdC5pbmRlcGVuZGVudCAhPT0gZmFsc2UpIHtcbiAgICAgICAgY29uc3Qge1xuICAgICAgICAgIHN0YXJ0UFRTLFxuICAgICAgICAgIGVuZFBUUyxcbiAgICAgICAgICBzdGFydERUUyxcbiAgICAgICAgICBlbmREVFNcbiAgICAgICAgfSA9IHZpZGVvO1xuICAgICAgICBpZiAocGFydCkge1xuICAgICAgICAgIHBhcnQuZWxlbWVudGFyeVN0cmVhbXNbdmlkZW8udHlwZV0gPSB7XG4gICAgICAgICAgICBzdGFydFBUUyxcbiAgICAgICAgICAgIGVuZFBUUyxcbiAgICAgICAgICAgIHN0YXJ0RFRTLFxuICAgICAgICAgICAgZW5kRFRTXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBpZiAodmlkZW8uZmlyc3RLZXlGcmFtZSAmJiB2aWRlby5pbmRlcGVuZGVudCAmJiBjaHVua01ldGEuaWQgPT09IDEgJiYgIWlzRmlyc3RJbkRpc2NvbnRpbnVpdHkpIHtcbiAgICAgICAgICAgIHRoaXMuY291bGRCYWNrdHJhY2sgPSB0cnVlO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAodmlkZW8uZHJvcHBlZCAmJiB2aWRlby5pbmRlcGVuZGVudCkge1xuICAgICAgICAgICAgLy8gQmFja3RyYWNrIGlmIGRyb3BwZWQgZnJhbWVzIGNyZWF0ZSBhIGdhcCBhZnRlciBjdXJyZW50VGltZVxuXG4gICAgICAgICAgICBjb25zdCBidWZmZXJJbmZvID0gdGhpcy5nZXRNYWluRndkQnVmZmVySW5mbygpO1xuICAgICAgICAgICAgY29uc3QgdGFyZ2V0QnVmZmVyVGltZSA9IChidWZmZXJJbmZvID8gYnVmZmVySW5mby5lbmQgOiB0aGlzLmdldExvYWRQb3NpdGlvbigpKSArIHRoaXMuY29uZmlnLm1heEJ1ZmZlckhvbGU7XG4gICAgICAgICAgICBjb25zdCBzdGFydFRpbWUgPSB2aWRlby5maXJzdEtleUZyYW1lUFRTID8gdmlkZW8uZmlyc3RLZXlGcmFtZVBUUyA6IHN0YXJ0UFRTO1xuICAgICAgICAgICAgaWYgKCFpc0ZpcnN0RnJhZ21lbnQgJiYgdGFyZ2V0QnVmZmVyVGltZSA8IHN0YXJ0VGltZSAtIHRoaXMuY29uZmlnLm1heEJ1ZmZlckhvbGUgJiYgIWlzRmlyc3RJbkRpc2NvbnRpbnVpdHkpIHtcbiAgICAgICAgICAgICAgdGhpcy5iYWNrdHJhY2soZnJhZyk7XG4gICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoaXNGaXJzdEluRGlzY29udGludWl0eSkge1xuICAgICAgICAgICAgICAvLyBNYXJrIHNlZ21lbnQgd2l0aCBhIGdhcCB0byBhdm9pZCBsb29wIGxvYWRpbmdcbiAgICAgICAgICAgICAgZnJhZy5nYXAgPSB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gU2V0IHZpZGVvIHN0cmVhbSBzdGFydCB0byBmcmFnbWVudCBzdGFydCBzbyB0aGF0IHRydW5jYXRlZCBzYW1wbGVzIGRvIG5vdCBkaXN0b3J0IHRoZSB0aW1lbGluZSwgYW5kIG1hcmsgaXQgcGFydGlhbFxuICAgICAgICAgICAgZnJhZy5zZXRFbGVtZW50YXJ5U3RyZWFtSW5mbyh2aWRlby50eXBlLCBmcmFnLnN0YXJ0LCBlbmRQVFMsIGZyYWcuc3RhcnQsIGVuZERUUywgdHJ1ZSk7XG4gICAgICAgICAgfSBlbHNlIGlmIChpc0ZpcnN0RnJhZ21lbnQgJiYgc3RhcnRQVFMgPiBNQVhfU1RBUlRfR0FQX0pVTVApIHtcbiAgICAgICAgICAgIC8vIE1hcmsgc2VnbWVudCB3aXRoIGEgZ2FwIHRvIHNraXAgbGFyZ2Ugc3RhcnQgZ2FwXG4gICAgICAgICAgICBmcmFnLmdhcCA9IHRydWU7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGZyYWcuc2V0RWxlbWVudGFyeVN0cmVhbUluZm8odmlkZW8udHlwZSwgc3RhcnRQVFMsIGVuZFBUUywgc3RhcnREVFMsIGVuZERUUyk7XG4gICAgICAgIGlmICh0aGlzLmJhY2t0cmFja0ZyYWdtZW50KSB7XG4gICAgICAgICAgdGhpcy5iYWNrdHJhY2tGcmFnbWVudCA9IGZyYWc7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5idWZmZXJGcmFnbWVudERhdGEodmlkZW8sIGZyYWcsIHBhcnQsIGNodW5rTWV0YSwgaXNGaXJzdEZyYWdtZW50IHx8IGlzRmlyc3RJbkRpc2NvbnRpbnVpdHkpO1xuICAgICAgfSBlbHNlIGlmIChpc0ZpcnN0RnJhZ21lbnQgfHwgaXNGaXJzdEluRGlzY29udGludWl0eSkge1xuICAgICAgICAvLyBNYXJrIHNlZ21lbnQgd2l0aCBhIGdhcCB0byBhdm9pZCBsb29wIGxvYWRpbmdcbiAgICAgICAgZnJhZy5nYXAgPSB0cnVlO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5iYWNrdHJhY2soZnJhZyk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKGF1ZGlvKSB7XG4gICAgICBjb25zdCB7XG4gICAgICAgIHN0YXJ0UFRTLFxuICAgICAgICBlbmRQVFMsXG4gICAgICAgIHN0YXJ0RFRTLFxuICAgICAgICBlbmREVFNcbiAgICAgIH0gPSBhdWRpbztcbiAgICAgIGlmIChwYXJ0KSB7XG4gICAgICAgIHBhcnQuZWxlbWVudGFyeVN0cmVhbXNbRWxlbWVudGFyeVN0cmVhbVR5cGVzLkFVRElPXSA9IHtcbiAgICAgICAgICBzdGFydFBUUyxcbiAgICAgICAgICBlbmRQVFMsXG4gICAgICAgICAgc3RhcnREVFMsXG4gICAgICAgICAgZW5kRFRTXG4gICAgICAgIH07XG4gICAgICB9XG4gICAgICBmcmFnLnNldEVsZW1lbnRhcnlTdHJlYW1JbmZvKEVsZW1lbnRhcnlTdHJlYW1UeXBlcy5BVURJTywgc3RhcnRQVFMsIGVuZFBUUywgc3RhcnREVFMsIGVuZERUUyk7XG4gICAgICB0aGlzLmJ1ZmZlckZyYWdtZW50RGF0YShhdWRpbywgZnJhZywgcGFydCwgY2h1bmtNZXRhKTtcbiAgICB9XG4gICAgaWYgKGRldGFpbHMgJiYgaWQzICE9IG51bGwgJiYgKF9pZDMkc2FtcGxlcyA9IGlkMy5zYW1wbGVzKSAhPSBudWxsICYmIF9pZDMkc2FtcGxlcy5sZW5ndGgpIHtcbiAgICAgIGNvbnN0IGVtaXR0ZWRJRDMgPSB7XG4gICAgICAgIGlkLFxuICAgICAgICBmcmFnLFxuICAgICAgICBkZXRhaWxzLFxuICAgICAgICBzYW1wbGVzOiBpZDMuc2FtcGxlc1xuICAgICAgfTtcbiAgICAgIGhscy50cmlnZ2VyKEV2ZW50cy5GUkFHX1BBUlNJTkdfTUVUQURBVEEsIGVtaXR0ZWRJRDMpO1xuICAgIH1cbiAgICBpZiAoZGV0YWlscyAmJiB0ZXh0KSB7XG4gICAgICBjb25zdCBlbWl0dGVkVGV4dCA9IHtcbiAgICAgICAgaWQsXG4gICAgICAgIGZyYWcsXG4gICAgICAgIGRldGFpbHMsXG4gICAgICAgIHNhbXBsZXM6IHRleHQuc2FtcGxlc1xuICAgICAgfTtcbiAgICAgIGhscy50cmlnZ2VyKEV2ZW50cy5GUkFHX1BBUlNJTkdfVVNFUkRBVEEsIGVtaXR0ZWRUZXh0KTtcbiAgICB9XG4gIH1cbiAgX2J1ZmZlckluaXRTZWdtZW50KGN1cnJlbnRMZXZlbCwgdHJhY2tzLCBmcmFnLCBjaHVua01ldGEpIHtcbiAgICBpZiAodGhpcy5zdGF0ZSAhPT0gU3RhdGUuUEFSU0lORykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLmF1ZGlvT25seSA9ICEhdHJhY2tzLmF1ZGlvICYmICF0cmFja3MudmlkZW87XG5cbiAgICAvLyBpZiBhdWRpbyB0cmFjayBpcyBleHBlY3RlZCB0byBjb21lIGZyb20gYXVkaW8gc3RyZWFtIGNvbnRyb2xsZXIsIGRpc2NhcmQgYW55IGNvbWluZyBmcm9tIG1haW5cbiAgICBpZiAodGhpcy5hbHRBdWRpbyAmJiAhdGhpcy5hdWRpb09ubHkpIHtcbiAgICAgIGRlbGV0ZSB0cmFja3MuYXVkaW87XG4gICAgfVxuICAgIC8vIGluY2x1ZGUgbGV2ZWxDb2RlYyBpbiBhdWRpbyBhbmQgdmlkZW8gdHJhY2tzXG4gICAgY29uc3Qge1xuICAgICAgYXVkaW8sXG4gICAgICB2aWRlbyxcbiAgICAgIGF1ZGlvdmlkZW9cbiAgICB9ID0gdHJhY2tzO1xuICAgIGlmIChhdWRpbykge1xuICAgICAgbGV0IGF1ZGlvQ29kZWMgPSBjdXJyZW50TGV2ZWwuYXVkaW9Db2RlYztcbiAgICAgIGNvbnN0IHVhID0gbmF2aWdhdG9yLnVzZXJBZ2VudC50b0xvd2VyQ2FzZSgpO1xuICAgICAgaWYgKHRoaXMuYXVkaW9Db2RlY1N3aXRjaCkge1xuICAgICAgICBpZiAoYXVkaW9Db2RlYykge1xuICAgICAgICAgIGlmIChhdWRpb0NvZGVjLmluZGV4T2YoJ21wNGEuNDAuNScpICE9PSAtMSkge1xuICAgICAgICAgICAgYXVkaW9Db2RlYyA9ICdtcDRhLjQwLjInO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBhdWRpb0NvZGVjID0gJ21wNGEuNDAuNSc7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIC8vIEluIHRoZSBjYXNlIHRoYXQgQUFDIGFuZCBIRS1BQUMgYXVkaW8gY29kZWNzIGFyZSBzaWduYWxsZWQgaW4gbWFuaWZlc3QsXG4gICAgICAgIC8vIGZvcmNlIEhFLUFBQywgYXMgaXQgc2VlbXMgdGhhdCBtb3N0IGJyb3dzZXJzIHByZWZlcnMgaXQuXG4gICAgICAgIC8vIGRvbid0IGZvcmNlIEhFLUFBQyBpZiBtb25vIHN0cmVhbSwgb3IgaW4gRmlyZWZveFxuICAgICAgICBjb25zdCBhdWRpb01ldGFkYXRhID0gYXVkaW8ubWV0YWRhdGE7XG4gICAgICAgIGlmIChhdWRpb01ldGFkYXRhICYmICdjaGFubmVsQ291bnQnIGluIGF1ZGlvTWV0YWRhdGEgJiYgKGF1ZGlvTWV0YWRhdGEuY2hhbm5lbENvdW50IHx8IDEpICE9PSAxICYmIHVhLmluZGV4T2YoJ2ZpcmVmb3gnKSA9PT0gLTEpIHtcbiAgICAgICAgICBhdWRpb0NvZGVjID0gJ21wNGEuNDAuNSc7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIC8vIEhFLUFBQyBpcyBicm9rZW4gb24gQW5kcm9pZCwgYWx3YXlzIHNpZ25hbCBhdWRpbyBjb2RlYyBhcyBBQUMgZXZlbiBpZiB2YXJpYW50IG1hbmlmZXN0IHN0YXRlcyBvdGhlcndpc2VcbiAgICAgIGlmIChhdWRpb0NvZGVjICYmIGF1ZGlvQ29kZWMuaW5kZXhPZignbXA0YS40MC41JykgIT09IC0xICYmIHVhLmluZGV4T2YoJ2FuZHJvaWQnKSAhPT0gLTEgJiYgYXVkaW8uY29udGFpbmVyICE9PSAnYXVkaW8vbXBlZycpIHtcbiAgICAgICAgLy8gRXhjbHVkZSBtcGVnIGF1ZGlvXG4gICAgICAgIGF1ZGlvQ29kZWMgPSAnbXA0YS40MC4yJztcbiAgICAgICAgdGhpcy5sb2coYEFuZHJvaWQ6IGZvcmNlIGF1ZGlvIGNvZGVjIHRvICR7YXVkaW9Db2RlY31gKTtcbiAgICAgIH1cbiAgICAgIGlmIChjdXJyZW50TGV2ZWwuYXVkaW9Db2RlYyAmJiBjdXJyZW50TGV2ZWwuYXVkaW9Db2RlYyAhPT0gYXVkaW9Db2RlYykge1xuICAgICAgICB0aGlzLmxvZyhgU3dhcHBpbmcgbWFuaWZlc3QgYXVkaW8gY29kZWMgXCIke2N1cnJlbnRMZXZlbC5hdWRpb0NvZGVjfVwiIGZvciBcIiR7YXVkaW9Db2RlY31cImApO1xuICAgICAgfVxuICAgICAgYXVkaW8ubGV2ZWxDb2RlYyA9IGF1ZGlvQ29kZWM7XG4gICAgICBhdWRpby5pZCA9ICdtYWluJztcbiAgICAgIHRoaXMubG9nKGBJbml0IGF1ZGlvIGJ1ZmZlciwgY29udGFpbmVyOiR7YXVkaW8uY29udGFpbmVyfSwgY29kZWNzW3NlbGVjdGVkL2xldmVsL3BhcnNlZF09WyR7YXVkaW9Db2RlYyB8fCAnJ30vJHtjdXJyZW50TGV2ZWwuYXVkaW9Db2RlYyB8fCAnJ30vJHthdWRpby5jb2RlY31dYCk7XG4gICAgfVxuICAgIGlmICh2aWRlbykge1xuICAgICAgdmlkZW8ubGV2ZWxDb2RlYyA9IGN1cnJlbnRMZXZlbC52aWRlb0NvZGVjO1xuICAgICAgdmlkZW8uaWQgPSAnbWFpbic7XG4gICAgICB0aGlzLmxvZyhgSW5pdCB2aWRlbyBidWZmZXIsIGNvbnRhaW5lcjoke3ZpZGVvLmNvbnRhaW5lcn0sIGNvZGVjc1tsZXZlbC9wYXJzZWRdPVske2N1cnJlbnRMZXZlbC52aWRlb0NvZGVjIHx8ICcnfS8ke3ZpZGVvLmNvZGVjfV1gKTtcbiAgICB9XG4gICAgaWYgKGF1ZGlvdmlkZW8pIHtcbiAgICAgIHRoaXMubG9nKGBJbml0IGF1ZGlvdmlkZW8gYnVmZmVyLCBjb250YWluZXI6JHthdWRpb3ZpZGVvLmNvbnRhaW5lcn0sIGNvZGVjc1tsZXZlbC9wYXJzZWRdPVske2N1cnJlbnRMZXZlbC5jb2RlY3N9LyR7YXVkaW92aWRlby5jb2RlY31dYCk7XG4gICAgfVxuICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLkJVRkZFUl9DT0RFQ1MsIHRyYWNrcyk7XG4gICAgLy8gbG9vcCB0aHJvdWdoIHRyYWNrcyB0aGF0IGFyZSBnb2luZyB0byBiZSBwcm92aWRlZCB0byBidWZmZXJDb250cm9sbGVyXG4gICAgT2JqZWN0LmtleXModHJhY2tzKS5mb3JFYWNoKHRyYWNrTmFtZSA9PiB7XG4gICAgICBjb25zdCB0cmFjayA9IHRyYWNrc1t0cmFja05hbWVdO1xuICAgICAgY29uc3QgaW5pdFNlZ21lbnQgPSB0cmFjay5pbml0U2VnbWVudDtcbiAgICAgIGlmIChpbml0U2VnbWVudCAhPSBudWxsICYmIGluaXRTZWdtZW50LmJ5dGVMZW5ndGgpIHtcbiAgICAgICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuQlVGRkVSX0FQUEVORElORywge1xuICAgICAgICAgIHR5cGU6IHRyYWNrTmFtZSxcbiAgICAgICAgICBkYXRhOiBpbml0U2VnbWVudCxcbiAgICAgICAgICBmcmFnLFxuICAgICAgICAgIHBhcnQ6IG51bGwsXG4gICAgICAgICAgY2h1bmtNZXRhLFxuICAgICAgICAgIHBhcmVudDogZnJhZy50eXBlXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0pO1xuICAgIC8vIHRyaWdnZXIgaGFuZGxlciByaWdodCBub3dcbiAgICB0aGlzLnRpY2tJbW1lZGlhdGUoKTtcbiAgfVxuICBnZXRNYWluRndkQnVmZmVySW5mbygpIHtcbiAgICByZXR1cm4gdGhpcy5nZXRGd2RCdWZmZXJJbmZvKHRoaXMubWVkaWFCdWZmZXIgPyB0aGlzLm1lZGlhQnVmZmVyIDogdGhpcy5tZWRpYSwgUGxheWxpc3RMZXZlbFR5cGUuTUFJTik7XG4gIH1cbiAgYmFja3RyYWNrKGZyYWcpIHtcbiAgICB0aGlzLmNvdWxkQmFja3RyYWNrID0gdHJ1ZTtcbiAgICAvLyBDYXVzZXMgZmluZEZyYWdtZW50cyB0byBiYWNrdHJhY2sgdGhyb3VnaCBmcmFnbWVudHMgdG8gZmluZCB0aGUga2V5ZnJhbWVcbiAgICB0aGlzLmJhY2t0cmFja0ZyYWdtZW50ID0gZnJhZztcbiAgICB0aGlzLnJlc2V0VHJhbnNtdXhlcigpO1xuICAgIHRoaXMuZmx1c2hCdWZmZXJHYXAoZnJhZyk7XG4gICAgdGhpcy5mcmFnbWVudFRyYWNrZXIucmVtb3ZlRnJhZ21lbnQoZnJhZyk7XG4gICAgdGhpcy5mcmFnUHJldmlvdXMgPSBudWxsO1xuICAgIHRoaXMubmV4dExvYWRQb3NpdGlvbiA9IGZyYWcuc3RhcnQ7XG4gICAgdGhpcy5zdGF0ZSA9IFN0YXRlLklETEU7XG4gIH1cbiAgY2hlY2tGcmFnbWVudENoYW5nZWQoKSB7XG4gICAgY29uc3QgdmlkZW8gPSB0aGlzLm1lZGlhO1xuICAgIGxldCBmcmFnUGxheWluZ0N1cnJlbnQgPSBudWxsO1xuICAgIGlmICh2aWRlbyAmJiB2aWRlby5yZWFkeVN0YXRlID4gMSAmJiB2aWRlby5zZWVraW5nID09PSBmYWxzZSkge1xuICAgICAgY29uc3QgY3VycmVudFRpbWUgPSB2aWRlby5jdXJyZW50VGltZTtcbiAgICAgIC8qIGlmIHZpZGVvIGVsZW1lbnQgaXMgaW4gc2Vla2VkIHN0YXRlLCBjdXJyZW50VGltZSBjYW4gb25seSBpbmNyZWFzZS5cbiAgICAgICAgKGFzc3VtaW5nIHRoYXQgcGxheWJhY2sgcmF0ZSBpcyBwb3NpdGl2ZSAuLi4pXG4gICAgICAgIEFzIHNvbWV0aW1lcyBjdXJyZW50VGltZSBqdW1wcyBiYWNrIHRvIHplcm8gYWZ0ZXIgYVxuICAgICAgICBtZWRpYSBkZWNvZGUgZXJyb3IsIGNoZWNrIHRoaXMsIHRvIGF2b2lkIHNlZWtpbmcgYmFjayB0b1xuICAgICAgICB3cm9uZyBwb3NpdGlvbiBhZnRlciBhIG1lZGlhIGRlY29kZSBlcnJvclxuICAgICAgKi9cblxuICAgICAgaWYgKEJ1ZmZlckhlbHBlci5pc0J1ZmZlcmVkKHZpZGVvLCBjdXJyZW50VGltZSkpIHtcbiAgICAgICAgZnJhZ1BsYXlpbmdDdXJyZW50ID0gdGhpcy5nZXRBcHBlbmRlZEZyYWcoY3VycmVudFRpbWUpO1xuICAgICAgfSBlbHNlIGlmIChCdWZmZXJIZWxwZXIuaXNCdWZmZXJlZCh2aWRlbywgY3VycmVudFRpbWUgKyAwLjEpKSB7XG4gICAgICAgIC8qIGVuc3VyZSB0aGF0IEZSQUdfQ0hBTkdFRCBldmVudCBpcyB0cmlnZ2VyZWQgYXQgc3RhcnR1cCxcbiAgICAgICAgICB3aGVuIGZpcnN0IHZpZGVvIGZyYW1lIGlzIGRpc3BsYXllZCBhbmQgcGxheWJhY2sgaXMgcGF1c2VkLlxuICAgICAgICAgIGFkZCBhIHRvbGVyYW5jZSBvZiAxMDBtcywgaW4gY2FzZSBjdXJyZW50IHBvc2l0aW9uIGlzIG5vdCBidWZmZXJlZCxcbiAgICAgICAgICBjaGVjayBpZiBjdXJyZW50IHBvcysxMDBtcyBpcyBidWZmZXJlZCBhbmQgdXNlIHRoYXQgYnVmZmVyIHJhbmdlXG4gICAgICAgICAgZm9yIEZSQUdfQ0hBTkdFRCBldmVudCByZXBvcnRpbmcgKi9cbiAgICAgICAgZnJhZ1BsYXlpbmdDdXJyZW50ID0gdGhpcy5nZXRBcHBlbmRlZEZyYWcoY3VycmVudFRpbWUgKyAwLjEpO1xuICAgICAgfVxuICAgICAgaWYgKGZyYWdQbGF5aW5nQ3VycmVudCkge1xuICAgICAgICB0aGlzLmJhY2t0cmFja0ZyYWdtZW50ID0gbnVsbDtcbiAgICAgICAgY29uc3QgZnJhZ1BsYXlpbmcgPSB0aGlzLmZyYWdQbGF5aW5nO1xuICAgICAgICBjb25zdCBmcmFnQ3VycmVudExldmVsID0gZnJhZ1BsYXlpbmdDdXJyZW50LmxldmVsO1xuICAgICAgICBpZiAoIWZyYWdQbGF5aW5nIHx8IGZyYWdQbGF5aW5nQ3VycmVudC5zbiAhPT0gZnJhZ1BsYXlpbmcuc24gfHwgZnJhZ1BsYXlpbmcubGV2ZWwgIT09IGZyYWdDdXJyZW50TGV2ZWwpIHtcbiAgICAgICAgICB0aGlzLmZyYWdQbGF5aW5nID0gZnJhZ1BsYXlpbmdDdXJyZW50O1xuICAgICAgICAgIHRoaXMuaGxzLnRyaWdnZXIoRXZlbnRzLkZSQUdfQ0hBTkdFRCwge1xuICAgICAgICAgICAgZnJhZzogZnJhZ1BsYXlpbmdDdXJyZW50XG4gICAgICAgICAgfSk7XG4gICAgICAgICAgaWYgKCFmcmFnUGxheWluZyB8fCBmcmFnUGxheWluZy5sZXZlbCAhPT0gZnJhZ0N1cnJlbnRMZXZlbCkge1xuICAgICAgICAgICAgdGhpcy5obHMudHJpZ2dlcihFdmVudHMuTEVWRUxfU1dJVENIRUQsIHtcbiAgICAgICAgICAgICAgbGV2ZWw6IGZyYWdDdXJyZW50TGV2ZWxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuICBnZXQgbmV4dExldmVsKCkge1xuICAgIGNvbnN0IGZyYWcgPSB0aGlzLm5leHRCdWZmZXJlZEZyYWc7XG4gICAgaWYgKGZyYWcpIHtcbiAgICAgIHJldHVybiBmcmFnLmxldmVsO1xuICAgIH1cbiAgICByZXR1cm4gLTE7XG4gIH1cbiAgZ2V0IGN1cnJlbnRGcmFnKCkge1xuICAgIGNvbnN0IG1lZGlhID0gdGhpcy5tZWRpYTtcbiAgICBpZiAobWVkaWEpIHtcbiAgICAgIHJldHVybiB0aGlzLmZyYWdQbGF5aW5nIHx8IHRoaXMuZ2V0QXBwZW5kZWRGcmFnKG1lZGlhLmN1cnJlbnRUaW1lKTtcbiAgICB9XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgZ2V0IGN1cnJlbnRQcm9ncmFtRGF0ZVRpbWUoKSB7XG4gICAgY29uc3QgbWVkaWEgPSB0aGlzLm1lZGlhO1xuICAgIGlmIChtZWRpYSkge1xuICAgICAgY29uc3QgY3VycmVudFRpbWUgPSBtZWRpYS5jdXJyZW50VGltZTtcbiAgICAgIGNvbnN0IGZyYWcgPSB0aGlzLmN1cnJlbnRGcmFnO1xuICAgICAgaWYgKGZyYWcgJiYgaXNGaW5pdGVOdW1iZXIoY3VycmVudFRpbWUpICYmIGlzRmluaXRlTnVtYmVyKGZyYWcucHJvZ3JhbURhdGVUaW1lKSkge1xuICAgICAgICBjb25zdCBlcG9jTXMgPSBmcmFnLnByb2dyYW1EYXRlVGltZSArIChjdXJyZW50VGltZSAtIGZyYWcuc3RhcnQpICogMTAwMDtcbiAgICAgICAgcmV0dXJuIG5ldyBEYXRlKGVwb2NNcyk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG4gIGdldCBjdXJyZW50TGV2ZWwoKSB7XG4gICAgY29uc3QgZnJhZyA9IHRoaXMuY3VycmVudEZyYWc7XG4gICAgaWYgKGZyYWcpIHtcbiAgICAgIHJldHVybiBmcmFnLmxldmVsO1xuICAgIH1cbiAgICByZXR1cm4gLTE7XG4gIH1cbiAgZ2V0IG5leHRCdWZmZXJlZEZyYWcoKSB7XG4gICAgY29uc3QgZnJhZyA9IHRoaXMuY3VycmVudEZyYWc7XG4gICAgaWYgKGZyYWcpIHtcbiAgICAgIHJldHVybiB0aGlzLmZvbGxvd2luZ0J1ZmZlcmVkRnJhZyhmcmFnKTtcbiAgICB9XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgZ2V0IGZvcmNlU3RhcnRMb2FkKCkge1xuICAgIHJldHVybiB0aGlzLl9mb3JjZVN0YXJ0TG9hZDtcbiAgfVxufVxuXG4vKipcbiAqIFRoZSBgSGxzYCBjbGFzcyBpcyB0aGUgY29yZSBvZiB0aGUgSExTLmpzIGxpYnJhcnkgdXNlZCB0byBpbnN0YW50aWF0ZSBwbGF5ZXIgaW5zdGFuY2VzLlxuICogQHB1YmxpY1xuICovXG5jbGFzcyBIbHMge1xuICAvKipcbiAgICogR2V0IHRoZSB2aWRlby1kZXYvaGxzLmpzIHBhY2thZ2UgdmVyc2lvbi5cbiAgICovXG4gIHN0YXRpYyBnZXQgdmVyc2lvbigpIHtcbiAgICByZXR1cm4gXCIxLjUuMTVcIjtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayBpZiB0aGUgcmVxdWlyZWQgTWVkaWFTb3VyY2UgRXh0ZW5zaW9ucyBhcmUgYXZhaWxhYmxlLlxuICAgKi9cbiAgc3RhdGljIGlzTVNFU3VwcG9ydGVkKCkge1xuICAgIHJldHVybiBpc01TRVN1cHBvcnRlZCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIE1lZGlhU291cmNlIEV4dGVuc2lvbnMgYXJlIGF2YWlsYWJsZSBhbmQgaXNUeXBlU3VwcG9ydGVkIGNoZWNrcyBwYXNzIGZvciBhbnkgYmFzZWxpbmUgY29kZWNzLlxuICAgKi9cbiAgc3RhdGljIGlzU3VwcG9ydGVkKCkge1xuICAgIHJldHVybiBpc1N1cHBvcnRlZCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgTWVkaWFTb3VyY2UgZ2xvYmFsIHVzZWQgZm9yIE1TRSBwbGF5YmFjayAoTWFuYWdlZE1lZGlhU291cmNlLCBNZWRpYVNvdXJjZSwgb3IgV2ViS2l0TWVkaWFTb3VyY2UpLlxuICAgKi9cbiAgc3RhdGljIGdldE1lZGlhU291cmNlKCkge1xuICAgIHJldHVybiBnZXRNZWRpYVNvdXJjZSgpO1xuICB9XG4gIHN0YXRpYyBnZXQgRXZlbnRzKCkge1xuICAgIHJldHVybiBFdmVudHM7XG4gIH1cbiAgc3RhdGljIGdldCBFcnJvclR5cGVzKCkge1xuICAgIHJldHVybiBFcnJvclR5cGVzO1xuICB9XG4gIHN0YXRpYyBnZXQgRXJyb3JEZXRhaWxzKCkge1xuICAgIHJldHVybiBFcnJvckRldGFpbHM7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBkZWZhdWx0IGNvbmZpZ3VyYXRpb24gYXBwbGllZCB0byBuZXcgaW5zdGFuY2VzLlxuICAgKi9cbiAgc3RhdGljIGdldCBEZWZhdWx0Q29uZmlnKCkge1xuICAgIGlmICghSGxzLmRlZmF1bHRDb25maWcpIHtcbiAgICAgIHJldHVybiBobHNEZWZhdWx0Q29uZmlnO1xuICAgIH1cbiAgICByZXR1cm4gSGxzLmRlZmF1bHRDb25maWc7XG4gIH1cblxuICAvKipcbiAgICogUmVwbGFjZSB0aGUgZGVmYXVsdCBjb25maWd1cmF0aW9uIGFwcGxpZWQgdG8gbmV3IGluc3RhbmNlcy5cbiAgICovXG4gIHN0YXRpYyBzZXQgRGVmYXVsdENvbmZpZyhkZWZhdWx0Q29uZmlnKSB7XG4gICAgSGxzLmRlZmF1bHRDb25maWcgPSBkZWZhdWx0Q29uZmlnO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgYW4gSExTIGNsaWVudCB0aGF0IGNhbiBhdHRhY2ggdG8gZXhhY3RseSBvbmUgYEhUTUxNZWRpYUVsZW1lbnRgLlxuICAgKiBAcGFyYW0gdXNlckNvbmZpZyAtIENvbmZpZ3VyYXRpb24gb3B0aW9ucyBhcHBsaWVkIG92ZXIgYEhscy5EZWZhdWx0Q29uZmlnYFxuICAgKi9cbiAgY29uc3RydWN0b3IodXNlckNvbmZpZyA9IHt9KSB7XG4gICAgLyoqXG4gICAgICogVGhlIHJ1bnRpbWUgY29uZmlndXJhdGlvbiB1c2VkIGJ5IHRoZSBwbGF5ZXIuIEF0IGluc3RhbnRpYXRpb24gdGhpcyBpcyBjb21iaW5hdGlvbiBvZiBgaGxzLnVzZXJDb25maWdgIG1lcmdlZCBvdmVyIGBIbHMuRGVmYXVsdENvbmZpZ2AuXG4gICAgICovXG4gICAgdGhpcy5jb25maWcgPSB2b2lkIDA7XG4gICAgLyoqXG4gICAgICogVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHByb3ZpZGVkIG9uIHBsYXllciBpbnN0YW50aWF0aW9uLlxuICAgICAqL1xuICAgIHRoaXMudXNlckNvbmZpZyA9IHZvaWQgMDtcbiAgICB0aGlzLmNvcmVDb21wb25lbnRzID0gdm9pZCAwO1xuICAgIHRoaXMubmV0d29ya0NvbnRyb2xsZXJzID0gdm9pZCAwO1xuICAgIHRoaXMuc3RhcnRlZCA9IGZhbHNlO1xuICAgIHRoaXMuX2VtaXR0ZXIgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG4gICAgdGhpcy5fYXV0b0xldmVsQ2FwcGluZyA9IC0xO1xuICAgIHRoaXMuX21heEhkY3BMZXZlbCA9IG51bGw7XG4gICAgdGhpcy5hYnJDb250cm9sbGVyID0gdm9pZCAwO1xuICAgIHRoaXMuYnVmZmVyQ29udHJvbGxlciA9IHZvaWQgMDtcbiAgICB0aGlzLmNhcExldmVsQ29udHJvbGxlciA9IHZvaWQgMDtcbiAgICB0aGlzLmxhdGVuY3lDb250cm9sbGVyID0gdm9pZCAwO1xuICAgIHRoaXMubGV2ZWxDb250cm9sbGVyID0gdm9pZCAwO1xuICAgIHRoaXMuc3RyZWFtQ29udHJvbGxlciA9IHZvaWQgMDtcbiAgICB0aGlzLmF1ZGlvVHJhY2tDb250cm9sbGVyID0gdm9pZCAwO1xuICAgIHRoaXMuc3VidGl0bGVUcmFja0NvbnRyb2xsZXIgPSB2b2lkIDA7XG4gICAgdGhpcy5lbWVDb250cm9sbGVyID0gdm9pZCAwO1xuICAgIHRoaXMuY21jZENvbnRyb2xsZXIgPSB2b2lkIDA7XG4gICAgdGhpcy5fbWVkaWEgPSBudWxsO1xuICAgIHRoaXMudXJsID0gbnVsbDtcbiAgICB0aGlzLnRyaWdnZXJpbmdFeGNlcHRpb24gPSB2b2lkIDA7XG4gICAgZW5hYmxlTG9ncyh1c2VyQ29uZmlnLmRlYnVnIHx8IGZhbHNlLCAnSGxzIGluc3RhbmNlJyk7XG4gICAgY29uc3QgY29uZmlnID0gdGhpcy5jb25maWcgPSBtZXJnZUNvbmZpZyhIbHMuRGVmYXVsdENvbmZpZywgdXNlckNvbmZpZyk7XG4gICAgdGhpcy51c2VyQ29uZmlnID0gdXNlckNvbmZpZztcbiAgICBpZiAoY29uZmlnLnByb2dyZXNzaXZlKSB7XG4gICAgICBlbmFibGVTdHJlYW1pbmdNb2RlKGNvbmZpZyk7XG4gICAgfVxuXG4gICAgLy8gY29yZSBjb250cm9sbGVycyBhbmQgbmV0d29yayBsb2FkZXJzXG4gICAgY29uc3Qge1xuICAgICAgYWJyQ29udHJvbGxlcjogQ29uZmlnQWJyQ29udHJvbGxlcixcbiAgICAgIGJ1ZmZlckNvbnRyb2xsZXI6IENvbmZpZ0J1ZmZlckNvbnRyb2xsZXIsXG4gICAgICBjYXBMZXZlbENvbnRyb2xsZXI6IENvbmZpZ0NhcExldmVsQ29udHJvbGxlcixcbiAgICAgIGVycm9yQ29udHJvbGxlcjogQ29uZmlnRXJyb3JDb250cm9sbGVyLFxuICAgICAgZnBzQ29udHJvbGxlcjogQ29uZmlnRnBzQ29udHJvbGxlclxuICAgIH0gPSBjb25maWc7XG4gICAgY29uc3QgZXJyb3JDb250cm9sbGVyID0gbmV3IENvbmZpZ0Vycm9yQ29udHJvbGxlcih0aGlzKTtcbiAgICBjb25zdCBhYnJDb250cm9sbGVyID0gdGhpcy5hYnJDb250cm9sbGVyID0gbmV3IENvbmZpZ0FickNvbnRyb2xsZXIodGhpcyk7XG4gICAgY29uc3QgYnVmZmVyQ29udHJvbGxlciA9IHRoaXMuYnVmZmVyQ29udHJvbGxlciA9IG5ldyBDb25maWdCdWZmZXJDb250cm9sbGVyKHRoaXMpO1xuICAgIGNvbnN0IGNhcExldmVsQ29udHJvbGxlciA9IHRoaXMuY2FwTGV2ZWxDb250cm9sbGVyID0gbmV3IENvbmZpZ0NhcExldmVsQ29udHJvbGxlcih0aGlzKTtcbiAgICBjb25zdCBmcHNDb250cm9sbGVyID0gbmV3IENvbmZpZ0Zwc0NvbnRyb2xsZXIodGhpcyk7XG4gICAgY29uc3QgcGxheUxpc3RMb2FkZXIgPSBuZXcgUGxheWxpc3RMb2FkZXIodGhpcyk7XG4gICAgY29uc3QgaWQzVHJhY2tDb250cm9sbGVyID0gbmV3IElEM1RyYWNrQ29udHJvbGxlcih0aGlzKTtcbiAgICBjb25zdCBDb25maWdDb250ZW50U3RlZXJpbmdDb250cm9sbGVyID0gY29uZmlnLmNvbnRlbnRTdGVlcmluZ0NvbnRyb2xsZXI7XG4gICAgLy8gQ29uZW50U3RlZXJpbmdDb250cm9sbGVyIGlzIGRlZmluZWQgYmVmb3JlIExldmVsQ29udHJvbGxlciB0byByZWNlaXZlIE11bHRpdmFyaWFudCBQbGF5bGlzdCBldmVudHMgZmlyc3RcbiAgICBjb25zdCBjb250ZW50U3RlZXJpbmcgPSBDb25maWdDb250ZW50U3RlZXJpbmdDb250cm9sbGVyID8gbmV3IENvbmZpZ0NvbnRlbnRTdGVlcmluZ0NvbnRyb2xsZXIodGhpcykgOiBudWxsO1xuICAgIGNvbnN0IGxldmVsQ29udHJvbGxlciA9IHRoaXMubGV2ZWxDb250cm9sbGVyID0gbmV3IExldmVsQ29udHJvbGxlcih0aGlzLCBjb250ZW50U3RlZXJpbmcpO1xuICAgIC8vIEZyYWdtZW50VHJhY2tlciBtdXN0IGJlIGRlZmluZWQgYmVmb3JlIFN0cmVhbUNvbnRyb2xsZXIgYmVjYXVzZSB0aGUgb3JkZXIgb2YgZXZlbnQgaGFuZGxpbmcgaXMgaW1wb3J0YW50XG4gICAgY29uc3QgZnJhZ21lbnRUcmFja2VyID0gbmV3IEZyYWdtZW50VHJhY2tlcih0aGlzKTtcbiAgICBjb25zdCBrZXlMb2FkZXIgPSBuZXcgS2V5TG9hZGVyKHRoaXMuY29uZmlnKTtcbiAgICBjb25zdCBzdHJlYW1Db250cm9sbGVyID0gdGhpcy5zdHJlYW1Db250cm9sbGVyID0gbmV3IFN0cmVhbUNvbnRyb2xsZXIodGhpcywgZnJhZ21lbnRUcmFja2VyLCBrZXlMb2FkZXIpO1xuXG4gICAgLy8gQ2FwIGxldmVsIGNvbnRyb2xsZXIgdXNlcyBzdHJlYW1Db250cm9sbGVyIHRvIGZsdXNoIHRoZSBidWZmZXJcbiAgICBjYXBMZXZlbENvbnRyb2xsZXIuc2V0U3RyZWFtQ29udHJvbGxlcihzdHJlYW1Db250cm9sbGVyKTtcbiAgICAvLyBmcHNDb250cm9sbGVyIHVzZXMgc3RyZWFtQ29udHJvbGxlciB0byBzd2l0Y2ggd2hlbiBmcmFtZXMgYXJlIGJlaW5nIGRyb3BwZWRcbiAgICBmcHNDb250cm9sbGVyLnNldFN0cmVhbUNvbnRyb2xsZXIoc3RyZWFtQ29udHJvbGxlcik7XG4gICAgY29uc3QgbmV0d29ya0NvbnRyb2xsZXJzID0gW3BsYXlMaXN0TG9hZGVyLCBsZXZlbENvbnRyb2xsZXIsIHN0cmVhbUNvbnRyb2xsZXJdO1xuICAgIGlmIChjb250ZW50U3RlZXJpbmcpIHtcbiAgICAgIG5ldHdvcmtDb250cm9sbGVycy5zcGxpY2UoMSwgMCwgY29udGVudFN0ZWVyaW5nKTtcbiAgICB9XG4gICAgdGhpcy5uZXR3b3JrQ29udHJvbGxlcnMgPSBuZXR3b3JrQ29udHJvbGxlcnM7XG4gICAgY29uc3QgY29yZUNvbXBvbmVudHMgPSBbYWJyQ29udHJvbGxlciwgYnVmZmVyQ29udHJvbGxlciwgY2FwTGV2ZWxDb250cm9sbGVyLCBmcHNDb250cm9sbGVyLCBpZDNUcmFja0NvbnRyb2xsZXIsIGZyYWdtZW50VHJhY2tlcl07XG4gICAgdGhpcy5hdWRpb1RyYWNrQ29udHJvbGxlciA9IHRoaXMuY3JlYXRlQ29udHJvbGxlcihjb25maWcuYXVkaW9UcmFja0NvbnRyb2xsZXIsIG5ldHdvcmtDb250cm9sbGVycyk7XG4gICAgY29uc3QgQXVkaW9TdHJlYW1Db250cm9sbGVyQ2xhc3MgPSBjb25maWcuYXVkaW9TdHJlYW1Db250cm9sbGVyO1xuICAgIGlmIChBdWRpb1N0cmVhbUNvbnRyb2xsZXJDbGFzcykge1xuICAgICAgbmV0d29ya0NvbnRyb2xsZXJzLnB1c2gobmV3IEF1ZGlvU3RyZWFtQ29udHJvbGxlckNsYXNzKHRoaXMsIGZyYWdtZW50VHJhY2tlciwga2V5TG9hZGVyKSk7XG4gICAgfVxuICAgIC8vIHN1YnRpdGxlVHJhY2tDb250cm9sbGVyIG11c3QgYmUgZGVmaW5lZCBiZWZvcmUgc3VidGl0bGVTdHJlYW1Db250cm9sbGVyIGJlY2F1c2UgdGhlIG9yZGVyIG9mIGV2ZW50IGhhbmRsaW5nIGlzIGltcG9ydGFudFxuICAgIHRoaXMuc3VidGl0bGVUcmFja0NvbnRyb2xsZXIgPSB0aGlzLmNyZWF0ZUNvbnRyb2xsZXIoY29uZmlnLnN1YnRpdGxlVHJhY2tDb250cm9sbGVyLCBuZXR3b3JrQ29udHJvbGxlcnMpO1xuICAgIGNvbnN0IFN1YnRpdGxlU3RyZWFtQ29udHJvbGxlckNsYXNzID0gY29uZmlnLnN1YnRpdGxlU3RyZWFtQ29udHJvbGxlcjtcbiAgICBpZiAoU3VidGl0bGVTdHJlYW1Db250cm9sbGVyQ2xhc3MpIHtcbiAgICAgIG5ldHdvcmtDb250cm9sbGVycy5wdXNoKG5ldyBTdWJ0aXRsZVN0cmVhbUNvbnRyb2xsZXJDbGFzcyh0aGlzLCBmcmFnbWVudFRyYWNrZXIsIGtleUxvYWRlcikpO1xuICAgIH1cbiAgICB0aGlzLmNyZWF0ZUNvbnRyb2xsZXIoY29uZmlnLnRpbWVsaW5lQ29udHJvbGxlciwgY29yZUNvbXBvbmVudHMpO1xuICAgIGtleUxvYWRlci5lbWVDb250cm9sbGVyID0gdGhpcy5lbWVDb250cm9sbGVyID0gdGhpcy5jcmVhdGVDb250cm9sbGVyKGNvbmZpZy5lbWVDb250cm9sbGVyLCBjb3JlQ29tcG9uZW50cyk7XG4gICAgdGhpcy5jbWNkQ29udHJvbGxlciA9IHRoaXMuY3JlYXRlQ29udHJvbGxlcihjb25maWcuY21jZENvbnRyb2xsZXIsIGNvcmVDb21wb25lbnRzKTtcbiAgICB0aGlzLmxhdGVuY3lDb250cm9sbGVyID0gdGhpcy5jcmVhdGVDb250cm9sbGVyKExhdGVuY3lDb250cm9sbGVyLCBjb3JlQ29tcG9uZW50cyk7XG4gICAgdGhpcy5jb3JlQ29tcG9uZW50cyA9IGNvcmVDb21wb25lbnRzO1xuXG4gICAgLy8gRXJyb3IgY29udHJvbGxlciBoYW5kbGVzIGVycm9ycyBiZWZvcmUgYW5kIGFmdGVyIGFsbCBvdGhlciBjb250cm9sbGVyc1xuICAgIC8vIFRoaXMgbGlzdGVuZXIgd2lsbCBiZSBpbnZva2VkIGFmdGVyIGFsbCBvdGhlciBjb250cm9sbGVycyBlcnJvciBsaXN0ZW5lcnNcbiAgICBuZXR3b3JrQ29udHJvbGxlcnMucHVzaChlcnJvckNvbnRyb2xsZXIpO1xuICAgIGNvbnN0IG9uRXJyb3JPdXQgPSBlcnJvckNvbnRyb2xsZXIub25FcnJvck91dDtcbiAgICBpZiAodHlwZW9mIG9uRXJyb3JPdXQgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgIHRoaXMub24oRXZlbnRzLkVSUk9SLCBvbkVycm9yT3V0LCBlcnJvckNvbnRyb2xsZXIpO1xuICAgIH1cbiAgfVxuICBjcmVhdGVDb250cm9sbGVyKENvbnRyb2xsZXJDbGFzcywgY29tcG9uZW50cykge1xuICAgIGlmIChDb250cm9sbGVyQ2xhc3MpIHtcbiAgICAgIGNvbnN0IGNvbnRyb2xsZXJJbnN0YW5jZSA9IG5ldyBDb250cm9sbGVyQ2xhc3ModGhpcyk7XG4gICAgICBpZiAoY29tcG9uZW50cykge1xuICAgICAgICBjb21wb25lbnRzLnB1c2goY29udHJvbGxlckluc3RhbmNlKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBjb250cm9sbGVySW5zdGFuY2U7XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLy8gRGVsZWdhdGUgdGhlIEV2ZW50RW1pdHRlciB0aHJvdWdoIHRoZSBwdWJsaWMgQVBJIG9mIEhscy5qc1xuICBvbihldmVudCwgbGlzdGVuZXIsIGNvbnRleHQgPSB0aGlzKSB7XG4gICAgdGhpcy5fZW1pdHRlci5vbihldmVudCwgbGlzdGVuZXIsIGNvbnRleHQpO1xuICB9XG4gIG9uY2UoZXZlbnQsIGxpc3RlbmVyLCBjb250ZXh0ID0gdGhpcykge1xuICAgIHRoaXMuX2VtaXR0ZXIub25jZShldmVudCwgbGlzdGVuZXIsIGNvbnRleHQpO1xuICB9XG4gIHJlbW92ZUFsbExpc3RlbmVycyhldmVudCkge1xuICAgIHRoaXMuX2VtaXR0ZXIucmVtb3ZlQWxsTGlzdGVuZXJzKGV2ZW50KTtcbiAgfVxuICBvZmYoZXZlbnQsIGxpc3RlbmVyLCBjb250ZXh0ID0gdGhpcywgb25jZSkge1xuICAgIHRoaXMuX2VtaXR0ZXIub2ZmKGV2ZW50LCBsaXN0ZW5lciwgY29udGV4dCwgb25jZSk7XG4gIH1cbiAgbGlzdGVuZXJzKGV2ZW50KSB7XG4gICAgcmV0dXJuIHRoaXMuX2VtaXR0ZXIubGlzdGVuZXJzKGV2ZW50KTtcbiAgfVxuICBlbWl0KGV2ZW50LCBuYW1lLCBldmVudE9iamVjdCkge1xuICAgIHJldHVybiB0aGlzLl9lbWl0dGVyLmVtaXQoZXZlbnQsIG5hbWUsIGV2ZW50T2JqZWN0KTtcbiAgfVxuICB0cmlnZ2VyKGV2ZW50LCBldmVudE9iamVjdCkge1xuICAgIGlmICh0aGlzLmNvbmZpZy5kZWJ1Zykge1xuICAgICAgcmV0dXJuIHRoaXMuZW1pdChldmVudCwgZXZlbnQsIGV2ZW50T2JqZWN0KTtcbiAgICB9IGVsc2Uge1xuICAgICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZW1pdChldmVudCwgZXZlbnQsIGV2ZW50T2JqZWN0KTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGxvZ2dlci5lcnJvcignQW4gaW50ZXJuYWwgZXJyb3IgaGFwcGVuZWQgd2hpbGUgaGFuZGxpbmcgZXZlbnQgJyArIGV2ZW50ICsgJy4gRXJyb3IgbWVzc2FnZTogXCInICsgZXJyb3IubWVzc2FnZSArICdcIi4gSGVyZSBpcyBhIHN0YWNrdHJhY2U6JywgZXJyb3IpO1xuICAgICAgICAvLyBQcmV2ZW50IHJlY3Vyc2lvbiBpbiBlcnJvciBldmVudCBoYW5kbGVycyB0aGF0IHRocm93ICM1NDk3XG4gICAgICAgIGlmICghdGhpcy50cmlnZ2VyaW5nRXhjZXB0aW9uKSB7XG4gICAgICAgICAgdGhpcy50cmlnZ2VyaW5nRXhjZXB0aW9uID0gdHJ1ZTtcbiAgICAgICAgICBjb25zdCBmYXRhbCA9IGV2ZW50ID09PSBFdmVudHMuRVJST1I7XG4gICAgICAgICAgdGhpcy50cmlnZ2VyKEV2ZW50cy5FUlJPUiwge1xuICAgICAgICAgICAgdHlwZTogRXJyb3JUeXBlcy5PVEhFUl9FUlJPUixcbiAgICAgICAgICAgIGRldGFpbHM6IEVycm9yRGV0YWlscy5JTlRFUk5BTF9FWENFUFRJT04sXG4gICAgICAgICAgICBmYXRhbCxcbiAgICAgICAgICAgIGV2ZW50LFxuICAgICAgICAgICAgZXJyb3JcbiAgICAgICAgICB9KTtcbiAgICAgICAgICB0aGlzLnRyaWdnZXJpbmdFeGNlcHRpb24gPSBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgbGlzdGVuZXJDb3VudChldmVudCkge1xuICAgIHJldHVybiB0aGlzLl9lbWl0dGVyLmxpc3RlbmVyQ291bnQoZXZlbnQpO1xuICB9XG5cbiAgLyoqXG4gICAqIERpc3Bvc2Ugb2YgdGhlIGluc3RhbmNlXG4gICAqL1xuICBkZXN0cm95KCkge1xuICAgIGxvZ2dlci5sb2coJ2Rlc3Ryb3knKTtcbiAgICB0aGlzLnRyaWdnZXIoRXZlbnRzLkRFU1RST1lJTkcsIHVuZGVmaW5lZCk7XG4gICAgdGhpcy5kZXRhY2hNZWRpYSgpO1xuICAgIHRoaXMucmVtb3ZlQWxsTGlzdGVuZXJzKCk7XG4gICAgdGhpcy5fYXV0b0xldmVsQ2FwcGluZyA9IC0xO1xuICAgIHRoaXMudXJsID0gbnVsbDtcbiAgICB0aGlzLm5ldHdvcmtDb250cm9sbGVycy5mb3JFYWNoKGNvbXBvbmVudCA9PiBjb21wb25lbnQuZGVzdHJveSgpKTtcbiAgICB0aGlzLm5ldHdvcmtDb250cm9sbGVycy5sZW5ndGggPSAwO1xuICAgIHRoaXMuY29yZUNvbXBvbmVudHMuZm9yRWFjaChjb21wb25lbnQgPT4gY29tcG9uZW50LmRlc3Ryb3koKSk7XG4gICAgdGhpcy5jb3JlQ29tcG9uZW50cy5sZW5ndGggPSAwO1xuICAgIC8vIFJlbW92ZSBhbnkgcmVmZXJlbmNlcyB0aGF0IGNvdWxkIGJlIGhlbGQgaW4gY29uZmlnIG9wdGlvbnMgb3IgY2FsbGJhY2tzXG4gICAgY29uc3QgY29uZmlnID0gdGhpcy5jb25maWc7XG4gICAgY29uZmlnLnhoclNldHVwID0gY29uZmlnLmZldGNoU2V0dXAgPSB1bmRlZmluZWQ7XG4gICAgLy8gQHRzLWlnbm9yZVxuICAgIHRoaXMudXNlckNvbmZpZyA9IG51bGw7XG4gIH1cblxuICAvKipcbiAgICogQXR0YWNoZXMgSGxzLmpzIHRvIGEgbWVkaWEgZWxlbWVudFxuICAgKi9cbiAgYXR0YWNoTWVkaWEobWVkaWEpIHtcbiAgICBsb2dnZXIubG9nKCdhdHRhY2hNZWRpYScpO1xuICAgIHRoaXMuX21lZGlhID0gbWVkaWE7XG4gICAgdGhpcy50cmlnZ2VyKEV2ZW50cy5NRURJQV9BVFRBQ0hJTkcsIHtcbiAgICAgIG1lZGlhOiBtZWRpYVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIERldGFjaCBIbHMuanMgZnJvbSB0aGUgbWVkaWFcbiAgICovXG4gIGRldGFjaE1lZGlhKCkge1xuICAgIGxvZ2dlci5sb2coJ2RldGFjaE1lZGlhJyk7XG4gICAgdGhpcy50cmlnZ2VyKEV2ZW50cy5NRURJQV9ERVRBQ0hJTkcsIHVuZGVmaW5lZCk7XG4gICAgdGhpcy5fbWVkaWEgPSBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldCB0aGUgc291cmNlIFVSTC4gQ2FuIGJlIHJlbGF0aXZlIG9yIGFic29sdXRlLlxuICAgKi9cbiAgbG9hZFNvdXJjZSh1cmwpIHtcbiAgICB0aGlzLnN0b3BMb2FkKCk7XG4gICAgY29uc3QgbWVkaWEgPSB0aGlzLm1lZGlhO1xuICAgIGNvbnN0IGxvYWRlZFNvdXJjZSA9IHRoaXMudXJsO1xuICAgIGNvbnN0IGxvYWRpbmdTb3VyY2UgPSB0aGlzLnVybCA9IHVybFRvb2xraXRFeHBvcnRzLmJ1aWxkQWJzb2x1dGVVUkwoc2VsZi5sb2NhdGlvbi5ocmVmLCB1cmwsIHtcbiAgICAgIGFsd2F5c05vcm1hbGl6ZTogdHJ1ZVxuICAgIH0pO1xuICAgIHRoaXMuX2F1dG9MZXZlbENhcHBpbmcgPSAtMTtcbiAgICB0aGlzLl9tYXhIZGNwTGV2ZWwgPSBudWxsO1xuICAgIGxvZ2dlci5sb2coYGxvYWRTb3VyY2U6JHtsb2FkaW5nU291cmNlfWApO1xuICAgIGlmIChtZWRpYSAmJiBsb2FkZWRTb3VyY2UgJiYgKGxvYWRlZFNvdXJjZSAhPT0gbG9hZGluZ1NvdXJjZSB8fCB0aGlzLmJ1ZmZlckNvbnRyb2xsZXIuaGFzU291cmNlVHlwZXMoKSkpIHtcbiAgICAgIHRoaXMuZGV0YWNoTWVkaWEoKTtcbiAgICAgIHRoaXMuYXR0YWNoTWVkaWEobWVkaWEpO1xuICAgIH1cbiAgICAvLyB3aGVuIGF0dGFjaGluZyB0byBhIHNvdXJjZSBVUkwsIHRyaWdnZXIgYSBwbGF5bGlzdCBsb2FkXG4gICAgdGhpcy50cmlnZ2VyKEV2ZW50cy5NQU5JRkVTVF9MT0FESU5HLCB7XG4gICAgICB1cmw6IHVybFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFN0YXJ0IGxvYWRpbmcgZGF0YSBmcm9tIHRoZSBzdHJlYW0gc291cmNlLlxuICAgKiBEZXBlbmRpbmcgb24gZGVmYXVsdCBjb25maWcsIGNsaWVudCBzdGFydHMgbG9hZGluZyBhdXRvbWF0aWNhbGx5IHdoZW4gYSBzb3VyY2UgaXMgc2V0LlxuICAgKlxuICAgKiBAcGFyYW0gc3RhcnRQb3NpdGlvbiAtIFNldCB0aGUgc3RhcnQgcG9zaXRpb24gdG8gc3RyZWFtIGZyb20uXG4gICAqIERlZmF1bHRzIHRvIC0xIChOb25lOiBzdGFydHMgZnJvbSBlYXJsaWVzdCBwb2ludClcbiAgICovXG4gIHN0YXJ0TG9hZChzdGFydFBvc2l0aW9uID0gLTEpIHtcbiAgICBsb2dnZXIubG9nKGBzdGFydExvYWQoJHtzdGFydFBvc2l0aW9ufSlgKTtcbiAgICB0aGlzLnN0YXJ0ZWQgPSB0cnVlO1xuICAgIHRoaXMubmV0d29ya0NvbnRyb2xsZXJzLmZvckVhY2goY29udHJvbGxlciA9PiB7XG4gICAgICBjb250cm9sbGVyLnN0YXJ0TG9hZChzdGFydFBvc2l0aW9uKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdG9wIGxvYWRpbmcgb2YgYW55IHN0cmVhbSBkYXRhLlxuICAgKi9cbiAgc3RvcExvYWQoKSB7XG4gICAgbG9nZ2VyLmxvZygnc3RvcExvYWQnKTtcbiAgICB0aGlzLnN0YXJ0ZWQgPSBmYWxzZTtcbiAgICB0aGlzLm5ldHdvcmtDb250cm9sbGVycy5mb3JFYWNoKGNvbnRyb2xsZXIgPT4ge1xuICAgICAgY29udHJvbGxlci5zdG9wTG9hZCgpO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlc3VtZXMgc3RyZWFtIGNvbnRyb2xsZXIgc2VnbWVudCBsb2FkaW5nIGlmIHByZXZpb3VzbHkgc3RhcnRlZC5cbiAgICovXG4gIHJlc3VtZUJ1ZmZlcmluZygpIHtcbiAgICBpZiAodGhpcy5zdGFydGVkKSB7XG4gICAgICB0aGlzLm5ldHdvcmtDb250cm9sbGVycy5mb3JFYWNoKGNvbnRyb2xsZXIgPT4ge1xuICAgICAgICBpZiAoJ2ZyYWdtZW50TG9hZGVyJyBpbiBjb250cm9sbGVyKSB7XG4gICAgICAgICAgY29udHJvbGxlci5zdGFydExvYWQoLTEpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogU3RvcHMgc3RyZWFtIGNvbnRyb2xsZXIgc2VnbWVudCBsb2FkaW5nIHdpdGhvdXQgY2hhbmdpbmcgJ3N0YXJ0ZWQnIHN0YXRlIGxpa2Ugc3RvcExvYWQoKS5cbiAgICogVGhpcyBhbGxvd3MgZm9yIG1lZGlhIGJ1ZmZlcmluZyB0byBiZSBwYXVzZWQgd2l0aG91dCBpbnRlcnVwdGluZyBwbGF5bGlzdCBsb2FkaW5nLlxuICAgKi9cbiAgcGF1c2VCdWZmZXJpbmcoKSB7XG4gICAgdGhpcy5uZXR3b3JrQ29udHJvbGxlcnMuZm9yRWFjaChjb250cm9sbGVyID0+IHtcbiAgICAgIGlmICgnZnJhZ21lbnRMb2FkZXInIGluIGNvbnRyb2xsZXIpIHtcbiAgICAgICAgY29udHJvbGxlci5zdG9wTG9hZCgpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFN3YXAgdGhyb3VnaCBwb3NzaWJsZSBhdWRpbyBjb2RlY3MgaW4gdGhlIHN0cmVhbSAoZm9yIGV4YW1wbGUgdG8gc3dpdGNoIGZyb20gc3RlcmVvIHRvIDUuMSlcbiAgICovXG4gIHN3YXBBdWRpb0NvZGVjKCkge1xuICAgIGxvZ2dlci5sb2coJ3N3YXBBdWRpb0NvZGVjJyk7XG4gICAgdGhpcy5zdHJlYW1Db250cm9sbGVyLnN3YXBBdWRpb0NvZGVjKCk7XG4gIH1cblxuICAvKipcbiAgICogV2hlbiB0aGUgbWVkaWEtZWxlbWVudCBmYWlscywgdGhpcyBhbGxvd3MgdG8gZGV0YWNoIGFuZCB0aGVuIHJlLWF0dGFjaCBpdFxuICAgKiBhcyBvbmUgY2FsbCAoY29udmVuaWVuY2UgbWV0aG9kKS5cbiAgICpcbiAgICogQXV0b21hdGljIHJlY292ZXJ5IG9mIG1lZGlhLWVycm9ycyBieSB0aGlzIHByb2Nlc3MgaXMgY29uZmlndXJhYmxlLlxuICAgKi9cbiAgcmVjb3Zlck1lZGlhRXJyb3IoKSB7XG4gICAgbG9nZ2VyLmxvZygncmVjb3Zlck1lZGlhRXJyb3InKTtcbiAgICBjb25zdCBtZWRpYSA9IHRoaXMuX21lZGlhO1xuICAgIHRoaXMuZGV0YWNoTWVkaWEoKTtcbiAgICBpZiAobWVkaWEpIHtcbiAgICAgIHRoaXMuYXR0YWNoTWVkaWEobWVkaWEpO1xuICAgIH1cbiAgfVxuICByZW1vdmVMZXZlbChsZXZlbEluZGV4KSB7XG4gICAgdGhpcy5sZXZlbENvbnRyb2xsZXIucmVtb3ZlTGV2ZWwobGV2ZWxJbmRleCk7XG4gIH1cblxuICAvKipcbiAgICogQHJldHVybnMgYW4gYXJyYXkgb2YgbGV2ZWxzICh2YXJpYW50cykgc29ydGVkIGJ5IEhEQ1AtTEVWRUwsIFJFU09MVVRJT04gKGhlaWdodCksIEZSQU1FLVJBVEUsIENPREVDUywgVklERU8tUkFOR0UsIGFuZCBCQU5EV0lEVEhcbiAgICovXG4gIGdldCBsZXZlbHMoKSB7XG4gICAgY29uc3QgbGV2ZWxzID0gdGhpcy5sZXZlbENvbnRyb2xsZXIubGV2ZWxzO1xuICAgIHJldHVybiBsZXZlbHMgPyBsZXZlbHMgOiBbXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbmRleCBvZiBxdWFsaXR5IGxldmVsICh2YXJpYW50KSBjdXJyZW50bHkgcGxheWVkXG4gICAqL1xuICBnZXQgY3VycmVudExldmVsKCkge1xuICAgIHJldHVybiB0aGlzLnN0cmVhbUNvbnRyb2xsZXIuY3VycmVudExldmVsO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldCBxdWFsaXR5IGxldmVsIGluZGV4IGltbWVkaWF0ZWx5LiBUaGlzIHdpbGwgZmx1c2ggdGhlIGN1cnJlbnQgYnVmZmVyIHRvIHJlcGxhY2UgdGhlIHF1YWxpdHkgYXNhcC4gVGhhdCBtZWFucyBwbGF5YmFjayB3aWxsIGludGVycnVwdCBhdCBsZWFzdCBzaG9ydGx5IHRvIHJlLWJ1ZmZlciBhbmQgcmUtc3luYyBldmVudHVhbGx5LiBTZXQgdG8gLTEgZm9yIGF1dG9tYXRpYyBsZXZlbCBzZWxlY3Rpb24uXG4gICAqL1xuICBzZXQgY3VycmVudExldmVsKG5ld0xldmVsKSB7XG4gICAgbG9nZ2VyLmxvZyhgc2V0IGN1cnJlbnRMZXZlbDoke25ld0xldmVsfWApO1xuICAgIHRoaXMubGV2ZWxDb250cm9sbGVyLm1hbnVhbExldmVsID0gbmV3TGV2ZWw7XG4gICAgdGhpcy5zdHJlYW1Db250cm9sbGVyLmltbWVkaWF0ZUxldmVsU3dpdGNoKCk7XG4gIH1cblxuICAvKipcbiAgICogSW5kZXggb2YgbmV4dCBxdWFsaXR5IGxldmVsIGxvYWRlZCBhcyBzY2hlZHVsZWQgYnkgc3RyZWFtIGNvbnRyb2xsZXIuXG4gICAqL1xuICBnZXQgbmV4dExldmVsKCkge1xuICAgIHJldHVybiB0aGlzLnN0cmVhbUNvbnRyb2xsZXIubmV4dExldmVsO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldCBxdWFsaXR5IGxldmVsIGluZGV4IGZvciBuZXh0IGxvYWRlZCBkYXRhLlxuICAgKiBUaGlzIHdpbGwgc3dpdGNoIHRoZSB2aWRlbyBxdWFsaXR5IGFzYXAsIHdpdGhvdXQgaW50ZXJydXB0aW5nIHBsYXliYWNrLlxuICAgKiBNYXkgYWJvcnQgY3VycmVudCBsb2FkaW5nIG9mIGRhdGEsIGFuZCBmbHVzaCBwYXJ0cyBvZiBidWZmZXIgKG91dHNpZGUgY3VycmVudGx5IHBsYXllZCBmcmFnbWVudCByZWdpb24pLlxuICAgKiBAcGFyYW0gbmV3TGV2ZWwgLSBQYXNzIC0xIGZvciBhdXRvbWF0aWMgbGV2ZWwgc2VsZWN0aW9uXG4gICAqL1xuICBzZXQgbmV4dExldmVsKG5ld0xldmVsKSB7XG4gICAgbG9nZ2VyLmxvZyhgc2V0IG5leHRMZXZlbDoke25ld0xldmVsfWApO1xuICAgIHRoaXMubGV2ZWxDb250cm9sbGVyLm1hbnVhbExldmVsID0gbmV3TGV2ZWw7XG4gICAgdGhpcy5zdHJlYW1Db250cm9sbGVyLm5leHRMZXZlbFN3aXRjaCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybiB0aGUgcXVhbGl0eSBsZXZlbCBvZiB0aGUgY3VycmVudGx5IG9yIGxhc3QgKG9mIG5vbmUgaXMgbG9hZGVkIGN1cnJlbnRseSkgc2VnbWVudFxuICAgKi9cbiAgZ2V0IGxvYWRMZXZlbCgpIHtcbiAgICByZXR1cm4gdGhpcy5sZXZlbENvbnRyb2xsZXIubGV2ZWw7XG4gIH1cblxuICAvKipcbiAgICogU2V0IHF1YWxpdHkgbGV2ZWwgaW5kZXggZm9yIG5leHQgbG9hZGVkIGRhdGEgaW4gYSBjb25zZXJ2YXRpdmUgd2F5LlxuICAgKiBUaGlzIHdpbGwgc3dpdGNoIHRoZSBxdWFsaXR5IHdpdGhvdXQgZmx1c2hpbmcsIGJ1dCBpbnRlcnJ1cHQgY3VycmVudCBsb2FkaW5nLlxuICAgKiBUaHVzIHRoZSBtb21lbnQgd2hlbiB0aGUgcXVhbGl0eSBzd2l0Y2ggd2lsbCBhcHBlYXIgaW4gZWZmZWN0IHdpbGwgb25seSBiZSBhZnRlciB0aGUgYWxyZWFkeSBleGlzdGluZyBidWZmZXIuXG4gICAqIEBwYXJhbSBuZXdMZXZlbCAtIFBhc3MgLTEgZm9yIGF1dG9tYXRpYyBsZXZlbCBzZWxlY3Rpb25cbiAgICovXG4gIHNldCBsb2FkTGV2ZWwobmV3TGV2ZWwpIHtcbiAgICBsb2dnZXIubG9nKGBzZXQgbG9hZExldmVsOiR7bmV3TGV2ZWx9YCk7XG4gICAgdGhpcy5sZXZlbENvbnRyb2xsZXIubWFudWFsTGV2ZWwgPSBuZXdMZXZlbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBnZXQgbmV4dCBxdWFsaXR5IGxldmVsIGxvYWRlZFxuICAgKi9cbiAgZ2V0IG5leHRMb2FkTGV2ZWwoKSB7XG4gICAgcmV0dXJuIHRoaXMubGV2ZWxDb250cm9sbGVyLm5leHRMb2FkTGV2ZWw7XG4gIH1cblxuICAvKipcbiAgICogU2V0IHF1YWxpdHkgbGV2ZWwgb2YgbmV4dCBsb2FkZWQgc2VnbWVudCBpbiBhIGZ1bGx5IFwibm9uLWRlc3RydWN0aXZlXCIgd2F5LlxuICAgKiBTYW1lIGFzIGBsb2FkTGV2ZWxgIGJ1dCB3aWxsIHdhaXQgZm9yIG5leHQgc3dpdGNoICh1bnRpbCBjdXJyZW50IGxvYWRpbmcgaXMgZG9uZSkuXG4gICAqL1xuICBzZXQgbmV4dExvYWRMZXZlbChsZXZlbCkge1xuICAgIHRoaXMubGV2ZWxDb250cm9sbGVyLm5leHRMb2FkTGV2ZWwgPSBsZXZlbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm4gXCJmaXJzdCBsZXZlbFwiOiBsaWtlIGEgZGVmYXVsdCBsZXZlbCwgaWYgbm90IHNldCxcbiAgICogZmFsbHMgYmFjayB0byBpbmRleCBvZiBmaXJzdCBsZXZlbCByZWZlcmVuY2VkIGluIG1hbmlmZXN0XG4gICAqL1xuICBnZXQgZmlyc3RMZXZlbCgpIHtcbiAgICByZXR1cm4gTWF0aC5tYXgodGhpcy5sZXZlbENvbnRyb2xsZXIuZmlyc3RMZXZlbCwgdGhpcy5taW5BdXRvTGV2ZWwpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldHMgXCJmaXJzdC1sZXZlbFwiLCBzZWUgZ2V0dGVyLlxuICAgKi9cbiAgc2V0IGZpcnN0TGV2ZWwobmV3TGV2ZWwpIHtcbiAgICBsb2dnZXIubG9nKGBzZXQgZmlyc3RMZXZlbDoke25ld0xldmVsfWApO1xuICAgIHRoaXMubGV2ZWxDb250cm9sbGVyLmZpcnN0TGV2ZWwgPSBuZXdMZXZlbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm4gdGhlIGRlc2lyZWQgc3RhcnQgbGV2ZWwgZm9yIHRoZSBmaXJzdCBmcmFnbWVudCB0aGF0IHdpbGwgYmUgbG9hZGVkLlxuICAgKiBUaGUgZGVmYXVsdCB2YWx1ZSBvZiAtMSBpbmRpY2F0ZXMgYXV0b21hdGljIHN0YXJ0IGxldmVsIHNlbGVjdGlvbi5cbiAgICogU2V0dGluZyBobHMubmV4dEF1dG9MZXZlbCB3aXRob3V0IHNldHRpbmcgYSBzdGFydExldmVsIHdpbGwgcmVzdWx0IGluXG4gICAqIHRoZSBuZXh0QXV0b0xldmVsIHZhbHVlIGJlaW5nIHVzZWQgZm9yIG9uZSBmcmFnbWVudCBsb2FkLlxuICAgKi9cbiAgZ2V0IHN0YXJ0TGV2ZWwoKSB7XG4gICAgY29uc3Qgc3RhcnRMZXZlbCA9IHRoaXMubGV2ZWxDb250cm9sbGVyLnN0YXJ0TGV2ZWw7XG4gICAgaWYgKHN0YXJ0TGV2ZWwgPT09IC0xICYmIHRoaXMuYWJyQ29udHJvbGxlci5mb3JjZWRBdXRvTGV2ZWwgPiAtMSkge1xuICAgICAgcmV0dXJuIHRoaXMuYWJyQ29udHJvbGxlci5mb3JjZWRBdXRvTGV2ZWw7XG4gICAgfVxuICAgIHJldHVybiBzdGFydExldmVsO1xuICB9XG5cbiAgLyoqXG4gICAqIHNldCAgc3RhcnQgbGV2ZWwgKGxldmVsIG9mIGZpcnN0IGZyYWdtZW50IHRoYXQgd2lsbCBiZSBwbGF5ZWQgYmFjaylcbiAgICogaWYgbm90IG92ZXJyaWRlZCBieSB1c2VyLCBmaXJzdCBsZXZlbCBhcHBlYXJpbmcgaW4gbWFuaWZlc3Qgd2lsbCBiZSB1c2VkIGFzIHN0YXJ0IGxldmVsXG4gICAqIGlmIC0xIDogYXV0b21hdGljIHN0YXJ0IGxldmVsIHNlbGVjdGlvbiwgcGxheWJhY2sgd2lsbCBzdGFydCBmcm9tIGxldmVsIG1hdGNoaW5nIGRvd25sb2FkIGJhbmR3aWR0aFxuICAgKiAoZGV0ZXJtaW5lZCBmcm9tIGRvd25sb2FkIG9mIGZpcnN0IHNlZ21lbnQpXG4gICAqL1xuICBzZXQgc3RhcnRMZXZlbChuZXdMZXZlbCkge1xuICAgIGxvZ2dlci5sb2coYHNldCBzdGFydExldmVsOiR7bmV3TGV2ZWx9YCk7XG4gICAgLy8gaWYgbm90IGluIGF1dG9tYXRpYyBzdGFydCBsZXZlbCBkZXRlY3Rpb24sIGVuc3VyZSBzdGFydExldmVsIGlzIGdyZWF0ZXIgdGhhbiBtaW5BdXRvTGV2ZWxcbiAgICBpZiAobmV3TGV2ZWwgIT09IC0xKSB7XG4gICAgICBuZXdMZXZlbCA9IE1hdGgubWF4KG5ld0xldmVsLCB0aGlzLm1pbkF1dG9MZXZlbCk7XG4gICAgfVxuICAgIHRoaXMubGV2ZWxDb250cm9sbGVyLnN0YXJ0TGV2ZWwgPSBuZXdMZXZlbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBXaGV0aGVyIGxldmVsIGNhcHBpbmcgaXMgZW5hYmxlZC5cbiAgICogRGVmYXVsdCB2YWx1ZSBpcyBzZXQgdmlhIGBjb25maWcuY2FwTGV2ZWxUb1BsYXllclNpemVgLlxuICAgKi9cbiAgZ2V0IGNhcExldmVsVG9QbGF5ZXJTaXplKCkge1xuICAgIHJldHVybiB0aGlzLmNvbmZpZy5jYXBMZXZlbFRvUGxheWVyU2l6ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFbmFibGVzIG9yIGRpc2FibGVzIGxldmVsIGNhcHBpbmcuIElmIGRpc2FibGVkIGFmdGVyIHByZXZpb3VzbHkgZW5hYmxlZCwgYG5leHRMZXZlbFN3aXRjaGAgd2lsbCBiZSBpbW1lZGlhdGVseSBjYWxsZWQuXG4gICAqL1xuICBzZXQgY2FwTGV2ZWxUb1BsYXllclNpemUoc2hvdWxkU3RhcnRDYXBwaW5nKSB7XG4gICAgY29uc3QgbmV3Q2FwTGV2ZWxUb1BsYXllclNpemUgPSAhIXNob3VsZFN0YXJ0Q2FwcGluZztcbiAgICBpZiAobmV3Q2FwTGV2ZWxUb1BsYXllclNpemUgIT09IHRoaXMuY29uZmlnLmNhcExldmVsVG9QbGF5ZXJTaXplKSB7XG4gICAgICBpZiAobmV3Q2FwTGV2ZWxUb1BsYXllclNpemUpIHtcbiAgICAgICAgdGhpcy5jYXBMZXZlbENvbnRyb2xsZXIuc3RhcnRDYXBwaW5nKCk7IC8vIElmIGNhcHBpbmcgb2NjdXJzLCBuZXh0TGV2ZWxTd2l0Y2ggd2lsbCBoYXBwZW4gYmFzZWQgb24gc2l6ZS5cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuY2FwTGV2ZWxDb250cm9sbGVyLnN0b3BDYXBwaW5nKCk7XG4gICAgICAgIHRoaXMuYXV0b0xldmVsQ2FwcGluZyA9IC0xO1xuICAgICAgICB0aGlzLnN0cmVhbUNvbnRyb2xsZXIubmV4dExldmVsU3dpdGNoKCk7IC8vIE5vdyB3ZSdyZSB1bmNhcHBlZCwgZ2V0IHRoZSBuZXh0IGxldmVsIGFzYXAuXG4gICAgICB9XG4gICAgICB0aGlzLmNvbmZpZy5jYXBMZXZlbFRvUGxheWVyU2l6ZSA9IG5ld0NhcExldmVsVG9QbGF5ZXJTaXplO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBDYXBwaW5nL21heCBsZXZlbCB2YWx1ZSB0aGF0IHNob3VsZCBiZSB1c2VkIGJ5IGF1dG9tYXRpYyBsZXZlbCBzZWxlY3Rpb24gYWxnb3JpdGhtIChgQUJSQ29udHJvbGxlcmApXG4gICAqL1xuICBnZXQgYXV0b0xldmVsQ2FwcGluZygpIHtcbiAgICByZXR1cm4gdGhpcy5fYXV0b0xldmVsQ2FwcGluZztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBjdXJyZW50IGJhbmR3aWR0aCBlc3RpbWF0ZSBpbiBiaXRzIHBlciBzZWNvbmQsIHdoZW4gYXZhaWxhYmxlLiBPdGhlcndpc2UsIGBOYU5gIGlzIHJldHVybmVkLlxuICAgKi9cbiAgZ2V0IGJhbmR3aWR0aEVzdGltYXRlKCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGJ3RXN0aW1hdG9yXG4gICAgfSA9IHRoaXMuYWJyQ29udHJvbGxlcjtcbiAgICBpZiAoIWJ3RXN0aW1hdG9yKSB7XG4gICAgICByZXR1cm4gTmFOO1xuICAgIH1cbiAgICByZXR1cm4gYndFc3RpbWF0b3IuZ2V0RXN0aW1hdGUoKTtcbiAgfVxuICBzZXQgYmFuZHdpZHRoRXN0aW1hdGUoYWJyRXdtYURlZmF1bHRFc3RpbWF0ZSkge1xuICAgIHRoaXMuYWJyQ29udHJvbGxlci5yZXNldEVzdGltYXRvcihhYnJFd21hRGVmYXVsdEVzdGltYXRlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBnZXQgdGltZSB0byBmaXJzdCBieXRlIGVzdGltYXRlXG4gICAqIEB0eXBlIHtudW1iZXJ9XG4gICAqL1xuICBnZXQgdHRmYkVzdGltYXRlKCkge1xuICAgIGNvbnN0IHtcbiAgICAgIGJ3RXN0aW1hdG9yXG4gICAgfSA9IHRoaXMuYWJyQ29udHJvbGxlcjtcbiAgICBpZiAoIWJ3RXN0aW1hdG9yKSB7XG4gICAgICByZXR1cm4gTmFOO1xuICAgIH1cbiAgICByZXR1cm4gYndFc3RpbWF0b3IuZ2V0RXN0aW1hdGVUVEZCKCk7XG4gIH1cblxuICAvKipcbiAgICogQ2FwcGluZy9tYXggbGV2ZWwgdmFsdWUgdGhhdCBzaG91bGQgYmUgdXNlZCBieSBhdXRvbWF0aWMgbGV2ZWwgc2VsZWN0aW9uIGFsZ29yaXRobSAoYEFCUkNvbnRyb2xsZXJgKVxuICAgKi9cbiAgc2V0IGF1dG9MZXZlbENhcHBpbmcobmV3TGV2ZWwpIHtcbiAgICBpZiAodGhpcy5fYXV0b0xldmVsQ2FwcGluZyAhPT0gbmV3TGV2ZWwpIHtcbiAgICAgIGxvZ2dlci5sb2coYHNldCBhdXRvTGV2ZWxDYXBwaW5nOiR7bmV3TGV2ZWx9YCk7XG4gICAgICB0aGlzLl9hdXRvTGV2ZWxDYXBwaW5nID0gbmV3TGV2ZWw7XG4gICAgICB0aGlzLmxldmVsQ29udHJvbGxlci5jaGVja01heEF1dG9VcGRhdGVkKCk7XG4gICAgfVxuICB9XG4gIGdldCBtYXhIZGNwTGV2ZWwoKSB7XG4gICAgcmV0dXJuIHRoaXMuX21heEhkY3BMZXZlbDtcbiAgfVxuICBzZXQgbWF4SGRjcExldmVsKHZhbHVlKSB7XG4gICAgaWYgKGlzSGRjcExldmVsKHZhbHVlKSAmJiB0aGlzLl9tYXhIZGNwTGV2ZWwgIT09IHZhbHVlKSB7XG4gICAgICB0aGlzLl9tYXhIZGNwTGV2ZWwgPSB2YWx1ZTtcbiAgICAgIHRoaXMubGV2ZWxDb250cm9sbGVyLmNoZWNrTWF4QXV0b1VwZGF0ZWQoKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVHJ1ZSB3aGVuIGF1dG9tYXRpYyBsZXZlbCBzZWxlY3Rpb24gZW5hYmxlZFxuICAgKi9cbiAgZ2V0IGF1dG9MZXZlbEVuYWJsZWQoKSB7XG4gICAgcmV0dXJuIHRoaXMubGV2ZWxDb250cm9sbGVyLm1hbnVhbExldmVsID09PSAtMTtcbiAgfVxuXG4gIC8qKlxuICAgKiBMZXZlbCBzZXQgbWFudWFsbHkgKGlmIGFueSlcbiAgICovXG4gIGdldCBtYW51YWxMZXZlbCgpIHtcbiAgICByZXR1cm4gdGhpcy5sZXZlbENvbnRyb2xsZXIubWFudWFsTGV2ZWw7XG4gIH1cblxuICAvKipcbiAgICogbWluIGxldmVsIHNlbGVjdGFibGUgaW4gYXV0byBtb2RlIGFjY29yZGluZyB0byBjb25maWcubWluQXV0b0JpdHJhdGVcbiAgICovXG4gIGdldCBtaW5BdXRvTGV2ZWwoKSB7XG4gICAgY29uc3Qge1xuICAgICAgbGV2ZWxzLFxuICAgICAgY29uZmlnOiB7XG4gICAgICAgIG1pbkF1dG9CaXRyYXRlXG4gICAgICB9XG4gICAgfSA9IHRoaXM7XG4gICAgaWYgKCFsZXZlbHMpIHJldHVybiAwO1xuICAgIGNvbnN0IGxlbiA9IGxldmVscy5sZW5ndGg7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBsZW47IGkrKykge1xuICAgICAgaWYgKGxldmVsc1tpXS5tYXhCaXRyYXRlID49IG1pbkF1dG9CaXRyYXRlKSB7XG4gICAgICAgIHJldHVybiBpO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gMDtcbiAgfVxuXG4gIC8qKlxuICAgKiBtYXggbGV2ZWwgc2VsZWN0YWJsZSBpbiBhdXRvIG1vZGUgYWNjb3JkaW5nIHRvIGF1dG9MZXZlbENhcHBpbmdcbiAgICovXG4gIGdldCBtYXhBdXRvTGV2ZWwoKSB7XG4gICAgY29uc3Qge1xuICAgICAgbGV2ZWxzLFxuICAgICAgYXV0b0xldmVsQ2FwcGluZyxcbiAgICAgIG1heEhkY3BMZXZlbFxuICAgIH0gPSB0aGlzO1xuICAgIGxldCBtYXhBdXRvTGV2ZWw7XG4gICAgaWYgKGF1dG9MZXZlbENhcHBpbmcgPT09IC0xICYmIGxldmVscyAhPSBudWxsICYmIGxldmVscy5sZW5ndGgpIHtcbiAgICAgIG1heEF1dG9MZXZlbCA9IGxldmVscy5sZW5ndGggLSAxO1xuICAgIH0gZWxzZSB7XG4gICAgICBtYXhBdXRvTGV2ZWwgPSBhdXRvTGV2ZWxDYXBwaW5nO1xuICAgIH1cbiAgICBpZiAobWF4SGRjcExldmVsKSB7XG4gICAgICBmb3IgKGxldCBpID0gbWF4QXV0b0xldmVsOyBpLS07KSB7XG4gICAgICAgIGNvbnN0IGhkY3BMZXZlbCA9IGxldmVsc1tpXS5hdHRyc1snSERDUC1MRVZFTCddO1xuICAgICAgICBpZiAoaGRjcExldmVsICYmIGhkY3BMZXZlbCA8PSBtYXhIZGNwTGV2ZWwpIHtcbiAgICAgICAgICByZXR1cm4gaTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbWF4QXV0b0xldmVsO1xuICB9XG4gIGdldCBmaXJzdEF1dG9MZXZlbCgpIHtcbiAgICByZXR1cm4gdGhpcy5hYnJDb250cm9sbGVyLmZpcnN0QXV0b0xldmVsO1xuICB9XG5cbiAgLyoqXG4gICAqIG5leHQgYXV0b21hdGljYWxseSBzZWxlY3RlZCBxdWFsaXR5IGxldmVsXG4gICAqL1xuICBnZXQgbmV4dEF1dG9MZXZlbCgpIHtcbiAgICByZXR1cm4gdGhpcy5hYnJDb250cm9sbGVyLm5leHRBdXRvTGV2ZWw7XG4gIH1cblxuICAvKipcbiAgICogdGhpcyBzZXR0ZXIgaXMgdXNlZCB0byBmb3JjZSBuZXh0IGF1dG8gbGV2ZWwuXG4gICAqIHRoaXMgaXMgdXNlZnVsIHRvIGZvcmNlIGEgc3dpdGNoIGRvd24gaW4gYXV0byBtb2RlOlxuICAgKiBpbiBjYXNlIG9mIGxvYWQgZXJyb3Igb24gbGV2ZWwgTiwgaGxzLmpzIGNhbiBzZXQgbmV4dEF1dG9MZXZlbCB0byBOLTEgZm9yIGV4YW1wbGUpXG4gICAqIGZvcmNlZCB2YWx1ZSBpcyB2YWxpZCBmb3Igb25lIGZyYWdtZW50LiB1cG9uIHN1Y2Nlc3NmdWwgZnJhZyBsb2FkaW5nIGF0IGZvcmNlZCBsZXZlbCxcbiAgICogdGhpcyB2YWx1ZSB3aWxsIGJlIHJlc2V0dGVkIHRvIC0xIGJ5IEFCUiBjb250cm9sbGVyLlxuICAgKi9cbiAgc2V0IG5leHRBdXRvTGV2ZWwobmV4dExldmVsKSB7XG4gICAgdGhpcy5hYnJDb250cm9sbGVyLm5leHRBdXRvTGV2ZWwgPSBuZXh0TGV2ZWw7XG4gIH1cblxuICAvKipcbiAgICogZ2V0IHRoZSBkYXRldGltZSB2YWx1ZSByZWxhdGl2ZSB0byBtZWRpYS5jdXJyZW50VGltZSBmb3IgdGhlIGFjdGl2ZSBsZXZlbCBQcm9ncmFtIERhdGUgVGltZSBpZiBwcmVzZW50XG4gICAqL1xuICBnZXQgcGxheWluZ0RhdGUoKSB7XG4gICAgcmV0dXJuIHRoaXMuc3RyZWFtQ29udHJvbGxlci5jdXJyZW50UHJvZ3JhbURhdGVUaW1lO1xuICB9XG4gIGdldCBtYWluRm9yd2FyZEJ1ZmZlckluZm8oKSB7XG4gICAgcmV0dXJuIHRoaXMuc3RyZWFtQ29udHJvbGxlci5nZXRNYWluRndkQnVmZmVySW5mbygpO1xuICB9XG5cbiAgLyoqXG4gICAqIEZpbmQgYW5kIHNlbGVjdCB0aGUgYmVzdCBtYXRjaGluZyBhdWRpbyB0cmFjaywgbWFraW5nIGEgbGV2ZWwgc3dpdGNoIHdoZW4gYSBHcm91cCBjaGFuZ2UgaXMgbmVjZXNzYXJ5LlxuICAgKiBVcGRhdGVzIGBobHMuY29uZmlnLmF1ZGlvUHJlZmVyZW5jZWAuIFJldHVybnMgdGhlIHNlbGVjdGVkIHRyYWNrLCBvciBudWxsIHdoZW4gbm8gbWF0Y2hpbmcgdHJhY2sgaXMgZm91bmQuXG4gICAqL1xuICBzZXRBdWRpb09wdGlvbihhdWRpb09wdGlvbikge1xuICAgIHZhciBfdGhpcyRhdWRpb1RyYWNrQ29udHI7XG4gICAgcmV0dXJuIChfdGhpcyRhdWRpb1RyYWNrQ29udHIgPSB0aGlzLmF1ZGlvVHJhY2tDb250cm9sbGVyKSA9PSBudWxsID8gdm9pZCAwIDogX3RoaXMkYXVkaW9UcmFja0NvbnRyLnNldEF1ZGlvT3B0aW9uKGF1ZGlvT3B0aW9uKTtcbiAgfVxuICAvKipcbiAgICogRmluZCBhbmQgc2VsZWN0IHRoZSBiZXN0IG1hdGNoaW5nIHN1YnRpdGxlIHRyYWNrLCBtYWtpbmcgYSBsZXZlbCBzd2l0Y2ggd2hlbiBhIEdyb3VwIGNoYW5nZSBpcyBuZWNlc3NhcnkuXG4gICAqIFVwZGF0ZXMgYGhscy5jb25maWcuc3VidGl0bGVQcmVmZXJlbmNlYC4gUmV0dXJucyB0aGUgc2VsZWN0ZWQgdHJhY2ssIG9yIG51bGwgd2hlbiBubyBtYXRjaGluZyB0cmFjayBpcyBmb3VuZC5cbiAgICovXG4gIHNldFN1YnRpdGxlT3B0aW9uKHN1YnRpdGxlT3B0aW9uKSB7XG4gICAgdmFyIF90aGlzJHN1YnRpdGxlVHJhY2tDbztcbiAgICAoX3RoaXMkc3VidGl0bGVUcmFja0NvID0gdGhpcy5zdWJ0aXRsZVRyYWNrQ29udHJvbGxlcikgPT0gbnVsbCA/IHZvaWQgMCA6IF90aGlzJHN1YnRpdGxlVHJhY2tDby5zZXRTdWJ0aXRsZU9wdGlvbihzdWJ0aXRsZU9wdGlvbik7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBjb21wbGV0ZSBsaXN0IG9mIGF1ZGlvIHRyYWNrcyBhY3Jvc3MgYWxsIG1lZGlhIGdyb3Vwc1xuICAgKi9cbiAgZ2V0IGFsbEF1ZGlvVHJhY2tzKCkge1xuICAgIGNvbnN0IGF1ZGlvVHJhY2tDb250cm9sbGVyID0gdGhpcy5hdWRpb1RyYWNrQ29udHJvbGxlcjtcbiAgICByZXR1cm4gYXVkaW9UcmFja0NvbnRyb2xsZXIgPyBhdWRpb1RyYWNrQ29udHJvbGxlci5hbGxBdWRpb1RyYWNrcyA6IFtdO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgbGlzdCBvZiBzZWxlY3RhYmxlIGF1ZGlvIHRyYWNrc1xuICAgKi9cbiAgZ2V0IGF1ZGlvVHJhY2tzKCkge1xuICAgIGNvbnN0IGF1ZGlvVHJhY2tDb250cm9sbGVyID0gdGhpcy5hdWRpb1RyYWNrQ29udHJvbGxlcjtcbiAgICByZXR1cm4gYXVkaW9UcmFja0NvbnRyb2xsZXIgPyBhdWRpb1RyYWNrQ29udHJvbGxlci5hdWRpb1RyYWNrcyA6IFtdO1xuICB9XG5cbiAgLyoqXG4gICAqIGluZGV4IG9mIHRoZSBzZWxlY3RlZCBhdWRpbyB0cmFjayAoaW5kZXggaW4gYXVkaW8gdHJhY2sgbGlzdHMpXG4gICAqL1xuICBnZXQgYXVkaW9UcmFjaygpIHtcbiAgICBjb25zdCBhdWRpb1RyYWNrQ29udHJvbGxlciA9IHRoaXMuYXVkaW9UcmFja0NvbnRyb2xsZXI7XG4gICAgcmV0dXJuIGF1ZGlvVHJhY2tDb250cm9sbGVyID8gYXVkaW9UcmFja0NvbnRyb2xsZXIuYXVkaW9UcmFjayA6IC0xO1xuICB9XG5cbiAgLyoqXG4gICAqIHNlbGVjdHMgYW4gYXVkaW8gdHJhY2ssIGJhc2VkIG9uIGl0cyBpbmRleCBpbiBhdWRpbyB0cmFjayBsaXN0c1xuICAgKi9cbiAgc2V0IGF1ZGlvVHJhY2soYXVkaW9UcmFja0lkKSB7XG4gICAgY29uc3QgYXVkaW9UcmFja0NvbnRyb2xsZXIgPSB0aGlzLmF1ZGlvVHJhY2tDb250cm9sbGVyO1xuICAgIGlmIChhdWRpb1RyYWNrQ29udHJvbGxlcikge1xuICAgICAgYXVkaW9UcmFja0NvbnRyb2xsZXIuYXVkaW9UcmFjayA9IGF1ZGlvVHJhY2tJZDtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogZ2V0IHRoZSBjb21wbGV0ZSBsaXN0IG9mIHN1YnRpdGxlIHRyYWNrcyBhY3Jvc3MgYWxsIG1lZGlhIGdyb3Vwc1xuICAgKi9cbiAgZ2V0IGFsbFN1YnRpdGxlVHJhY2tzKCkge1xuICAgIGNvbnN0IHN1YnRpdGxlVHJhY2tDb250cm9sbGVyID0gdGhpcy5zdWJ0aXRsZVRyYWNrQ29udHJvbGxlcjtcbiAgICByZXR1cm4gc3VidGl0bGVUcmFja0NvbnRyb2xsZXIgPyBzdWJ0aXRsZVRyYWNrQ29udHJvbGxlci5hbGxTdWJ0aXRsZVRyYWNrcyA6IFtdO1xuICB9XG5cbiAgLyoqXG4gICAqIGdldCBhbHRlcm5hdGUgc3VidGl0bGUgdHJhY2tzIGxpc3QgZnJvbSBwbGF5bGlzdFxuICAgKi9cbiAgZ2V0IHN1YnRpdGxlVHJhY2tzKCkge1xuICAgIGNvbnN0IHN1YnRpdGxlVHJhY2tDb250cm9sbGVyID0gdGhpcy5zdWJ0aXRsZVRyYWNrQ29udHJvbGxlcjtcbiAgICByZXR1cm4gc3VidGl0bGVUcmFja0NvbnRyb2xsZXIgPyBzdWJ0aXRsZVRyYWNrQ29udHJvbGxlci5zdWJ0aXRsZVRyYWNrcyA6IFtdO1xuICB9XG5cbiAgLyoqXG4gICAqIGluZGV4IG9mIHRoZSBzZWxlY3RlZCBzdWJ0aXRsZSB0cmFjayAoaW5kZXggaW4gc3VidGl0bGUgdHJhY2sgbGlzdHMpXG4gICAqL1xuICBnZXQgc3VidGl0bGVUcmFjaygpIHtcbiAgICBjb25zdCBzdWJ0aXRsZVRyYWNrQ29udHJvbGxlciA9IHRoaXMuc3VidGl0bGVUcmFja0NvbnRyb2xsZXI7XG4gICAgcmV0dXJuIHN1YnRpdGxlVHJhY2tDb250cm9sbGVyID8gc3VidGl0bGVUcmFja0NvbnRyb2xsZXIuc3VidGl0bGVUcmFjayA6IC0xO1xuICB9XG4gIGdldCBtZWRpYSgpIHtcbiAgICByZXR1cm4gdGhpcy5fbWVkaWE7XG4gIH1cblxuICAvKipcbiAgICogc2VsZWN0IGFuIHN1YnRpdGxlIHRyYWNrLCBiYXNlZCBvbiBpdHMgaW5kZXggaW4gc3VidGl0bGUgdHJhY2sgbGlzdHNcbiAgICovXG4gIHNldCBzdWJ0aXRsZVRyYWNrKHN1YnRpdGxlVHJhY2tJZCkge1xuICAgIGNvbnN0IHN1YnRpdGxlVHJhY2tDb250cm9sbGVyID0gdGhpcy5zdWJ0aXRsZVRyYWNrQ29udHJvbGxlcjtcbiAgICBpZiAoc3VidGl0bGVUcmFja0NvbnRyb2xsZXIpIHtcbiAgICAgIHN1YnRpdGxlVHJhY2tDb250cm9sbGVyLnN1YnRpdGxlVHJhY2sgPSBzdWJ0aXRsZVRyYWNrSWQ7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgc3VidGl0bGUgZGlzcGxheSBpcyBlbmFibGVkIG9yIG5vdFxuICAgKi9cbiAgZ2V0IHN1YnRpdGxlRGlzcGxheSgpIHtcbiAgICBjb25zdCBzdWJ0aXRsZVRyYWNrQ29udHJvbGxlciA9IHRoaXMuc3VidGl0bGVUcmFja0NvbnRyb2xsZXI7XG4gICAgcmV0dXJuIHN1YnRpdGxlVHJhY2tDb250cm9sbGVyID8gc3VidGl0bGVUcmFja0NvbnRyb2xsZXIuc3VidGl0bGVEaXNwbGF5IDogZmFsc2U7XG4gIH1cblxuICAvKipcbiAgICogRW5hYmxlL2Rpc2FibGUgc3VidGl0bGUgZGlzcGxheSByZW5kZXJpbmdcbiAgICovXG4gIHNldCBzdWJ0aXRsZURpc3BsYXkodmFsdWUpIHtcbiAgICBjb25zdCBzdWJ0aXRsZVRyYWNrQ29udHJvbGxlciA9IHRoaXMuc3VidGl0bGVUcmFja0NvbnRyb2xsZXI7XG4gICAgaWYgKHN1YnRpdGxlVHJhY2tDb250cm9sbGVyKSB7XG4gICAgICBzdWJ0aXRsZVRyYWNrQ29udHJvbGxlci5zdWJ0aXRsZURpc3BsYXkgPSB2YWx1ZTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogZ2V0IG1vZGUgZm9yIExvdy1MYXRlbmN5IEhMUyBsb2FkaW5nXG4gICAqL1xuICBnZXQgbG93TGF0ZW5jeU1vZGUoKSB7XG4gICAgcmV0dXJuIHRoaXMuY29uZmlnLmxvd0xhdGVuY3lNb2RlO1xuICB9XG5cbiAgLyoqXG4gICAqIEVuYWJsZS9kaXNhYmxlIExvdy1MYXRlbmN5IEhMUyBwYXJ0IHBsYXlsaXN0IGFuZCBzZWdtZW50IGxvYWRpbmcsIGFuZCBzdGFydCBsaXZlIHN0cmVhbXMgYXQgcGxheWxpc3QgUEFSVC1IT0xELUJBQ0sgcmF0aGVyIHRoYW4gSE9MRC1CQUNLLlxuICAgKi9cbiAgc2V0IGxvd0xhdGVuY3lNb2RlKG1vZGUpIHtcbiAgICB0aGlzLmNvbmZpZy5sb3dMYXRlbmN5TW9kZSA9IG1vZGU7XG4gIH1cblxuICAvKipcbiAgICogUG9zaXRpb24gKGluIHNlY29uZHMpIG9mIGxpdmUgc3luYyBwb2ludCAoaWUgZWRnZSBvZiBsaXZlIHBvc2l0aW9uIG1pbnVzIHNhZmV0eSBkZWxheSBkZWZpbmVkIGJ5IGBgYGhscy5jb25maWcubGl2ZVN5bmNEdXJhdGlvbmBgYClcbiAgICogQHJldHVybnMgbnVsbCBwcmlvciB0byBsb2FkaW5nIGxpdmUgUGxheWxpc3RcbiAgICovXG4gIGdldCBsaXZlU3luY1Bvc2l0aW9uKCkge1xuICAgIHJldHVybiB0aGlzLmxhdGVuY3lDb250cm9sbGVyLmxpdmVTeW5jUG9zaXRpb247XG4gIH1cblxuICAvKipcbiAgICogRXN0aW1hdGVkIHBvc2l0aW9uIChpbiBzZWNvbmRzKSBvZiBsaXZlIGVkZ2UgKGllIGVkZ2Ugb2YgbGl2ZSBwbGF5bGlzdCBwbHVzIHRpbWUgc3luYyBwbGF5bGlzdCBhZHZhbmNlZClcbiAgICogQHJldHVybnMgMCBiZWZvcmUgZmlyc3QgcGxheWxpc3QgaXMgbG9hZGVkXG4gICAqL1xuICBnZXQgbGF0ZW5jeSgpIHtcbiAgICByZXR1cm4gdGhpcy5sYXRlbmN5Q29udHJvbGxlci5sYXRlbmN5O1xuICB9XG5cbiAgLyoqXG4gICAqIG1heGltdW0gZGlzdGFuY2UgZnJvbSB0aGUgZWRnZSBiZWZvcmUgdGhlIHBsYXllciBzZWVrcyBmb3J3YXJkIHRvIGBgYGhscy5saXZlU3luY1Bvc2l0aW9uYGBgXG4gICAqIGNvbmZpZ3VyZWQgdXNpbmcgYGBgbGl2ZU1heExhdGVuY3lEdXJhdGlvbkNvdW50YGBgIChtdWx0aXBsZSBvZiB0YXJnZXQgZHVyYXRpb24pIG9yIGBgYGxpdmVNYXhMYXRlbmN5RHVyYXRpb25gYGBcbiAgICogQHJldHVybnMgMCBiZWZvcmUgZmlyc3QgcGxheWxpc3QgaXMgbG9hZGVkXG4gICAqL1xuICBnZXQgbWF4TGF0ZW5jeSgpIHtcbiAgICByZXR1cm4gdGhpcy5sYXRlbmN5Q29udHJvbGxlci5tYXhMYXRlbmN5O1xuICB9XG5cbiAgLyoqXG4gICAqIHRhcmdldCBkaXN0YW5jZSBmcm9tIHRoZSBlZGdlIGFzIGNhbGN1bGF0ZWQgYnkgdGhlIGxhdGVuY3kgY29udHJvbGxlclxuICAgKi9cbiAgZ2V0IHRhcmdldExhdGVuY3koKSB7XG4gICAgcmV0dXJuIHRoaXMubGF0ZW5jeUNvbnRyb2xsZXIudGFyZ2V0TGF0ZW5jeTtcbiAgfVxuXG4gIC8qKlxuICAgKiB0aGUgcmF0ZSBhdCB3aGljaCB0aGUgZWRnZSBvZiB0aGUgY3VycmVudCBsaXZlIHBsYXlsaXN0IGlzIGFkdmFuY2luZyBvciAxIGlmIHRoZXJlIGlzIG5vbmVcbiAgICovXG4gIGdldCBkcmlmdCgpIHtcbiAgICByZXR1cm4gdGhpcy5sYXRlbmN5Q29udHJvbGxlci5kcmlmdDtcbiAgfVxuXG4gIC8qKlxuICAgKiBzZXQgdG8gdHJ1ZSB3aGVuIHN0YXJ0TG9hZCBpcyBjYWxsZWQgYmVmb3JlIE1BTklGRVNUX1BBUlNFRCBldmVudFxuICAgKi9cbiAgZ2V0IGZvcmNlU3RhcnRMb2FkKCkge1xuICAgIHJldHVybiB0aGlzLnN0cmVhbUNvbnRyb2xsZXIuZm9yY2VTdGFydExvYWQ7XG4gIH1cbn1cbkhscy5kZWZhdWx0Q29uZmlnID0gdm9pZCAwO1xuXG5leHBvcnQgeyBBYnJDb250cm9sbGVyLCBBdHRyTGlzdCwgQXVkaW9TdHJlYW1Db250cm9sbGVyLCBBdWRpb1RyYWNrQ29udHJvbGxlciwgQmFzZVBsYXlsaXN0Q29udHJvbGxlciwgQmFzZVNlZ21lbnQsIEJhc2VTdHJlYW1Db250cm9sbGVyLCBCdWZmZXJDb250cm9sbGVyLCBDTUNEQ29udHJvbGxlciwgQ2FwTGV2ZWxDb250cm9sbGVyLCBDaHVua01ldGFkYXRhLCBDb250ZW50U3RlZXJpbmdDb250cm9sbGVyLCBEYXRlUmFuZ2UsIEVNRUNvbnRyb2xsZXIsIEVycm9yQWN0aW9uRmxhZ3MsIEVycm9yQ29udHJvbGxlciwgRXJyb3JEZXRhaWxzLCBFcnJvclR5cGVzLCBFdmVudHMsIEZQU0NvbnRyb2xsZXIsIEZyYWdtZW50LCBIbHMsIEhsc1NraXAsIEhsc1VybFBhcmFtZXRlcnMsIEtleVN5c3RlbUZvcm1hdHMsIEtleVN5c3RlbXMsIExldmVsLCBMZXZlbERldGFpbHMsIExldmVsS2V5LCBMb2FkU3RhdHMsIE1ldGFkYXRhU2NoZW1hLCBOZXR3b3JrRXJyb3JBY3Rpb24sIFBhcnQsIFBsYXlsaXN0TGV2ZWxUeXBlLCBTdWJ0aXRsZVN0cmVhbUNvbnRyb2xsZXIsIFN1YnRpdGxlVHJhY2tDb250cm9sbGVyLCBUaW1lbGluZUNvbnRyb2xsZXIsIEhscyBhcyBkZWZhdWx0LCBnZXRNZWRpYVNvdXJjZSwgaXNNU0VTdXBwb3J0ZWQsIGlzU3VwcG9ydGVkIH07XG4vLyMgc291cmNlTWFwcGluZ1VSTD1obHMubWpzLm1hcFxuIl0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9 \ No newline at end of file +"use strict";(self.webpackChunkmyhls=self.webpackChunkmyhls||[]).push([[57],{368:()=>{function t(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var e,s,i,r,n,a={exports:{}};e=/^(?=((?:[a-zA-Z0-9+\-.]+:)?))\1(?=((?:\/\/[^\/?#]*)?))\2(?=((?:(?:[^?#\/]*\/)*[^;?#\/]*)?))\3((?:;[^?#]*)?)(\?[^#]*)?(#[^]*)?$/,s=/^(?=([^\/?#]*))\1([^]*)$/,i=/(?:\/|^)\.(?=\/)/g,r=/(?:\/|^)\.\.\/(?!\.\.\/)[^\/]*(?=\/)/g,a.exports=n={buildAbsoluteURL:function(t,e,i){if(i=i||{},t=t.trim(),!(e=e.trim())){if(!i.alwaysNormalize)return t;var r=n.parseURL(t);if(!r)throw new Error("Error trying to parse base URL.");return r.path=n.normalizePath(r.path),n.buildURLFromParts(r)}var a=n.parseURL(e);if(!a)throw new Error("Error trying to parse relative URL.");if(a.scheme)return i.alwaysNormalize?(a.path=n.normalizePath(a.path),n.buildURLFromParts(a)):e;var o=n.parseURL(t);if(!o)throw new Error("Error trying to parse base URL.");if(!o.netLoc&&o.path&&"/"!==o.path[0]){var l=s.exec(o.path);o.netLoc=l[1],o.path=l[2]}o.netLoc&&!o.path&&(o.path="/");var h={scheme:o.scheme,netLoc:a.netLoc,path:null,params:a.params,query:a.query,fragment:a.fragment};if(!a.netLoc&&(h.netLoc=o.netLoc,"/"!==a.path[0]))if(a.path){var d=o.path,c=d.substring(0,d.lastIndexOf("/")+1)+a.path;h.path=n.normalizePath(c)}else h.path=o.path,a.params||(h.params=o.params,a.query||(h.query=o.query));return null===h.path&&(h.path=i.alwaysNormalize?n.normalizePath(a.path):a.path),n.buildURLFromParts(h)},parseURL:function(t){var s=e.exec(t);return s?{scheme:s[1]||"",netLoc:s[2]||"",path:s[3]||"",params:s[4]||"",query:s[5]||"",fragment:s[6]||""}:null},normalizePath:function(t){for(t=t.split("").reverse().join("").replace(i,"");t.length!==(t=t.replace(r,"")).length;);return t.split("").reverse().join("")},buildURLFromParts:function(t){return t.scheme+t.netLoc+t.path+t.params+t.query+t.fragment}};var o=a.exports;function l(t,e){var s=Object.keys(t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(t);e&&(i=i.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),s.push.apply(s,i)}return s}function h(t){for(var e=1;e<arguments.length;e++){var s=null!=arguments[e]?arguments[e]:{};e%2?l(Object(s),!0).forEach((function(e){c(t,e,s[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(s)):l(Object(s)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(s,e))}))}return t}function d(t){var e=function(t,e){if("object"!=typeof t||!t)return t;var s=t[Symbol.toPrimitive];if(void 0!==s){var i=s.call(t,e||"default");if("object"!=typeof i)return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===e?String:Number)(t)}(t,"string");return"symbol"==typeof e?e:String(e)}function c(t,e,s){return(e=d(e))in t?Object.defineProperty(t,e,{value:s,enumerable:!0,configurable:!0,writable:!0}):t[e]=s,t}function u(){return u=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var s=arguments[e];for(var i in s)Object.prototype.hasOwnProperty.call(s,i)&&(t[i]=s[i])}return t},u.apply(this,arguments)}const f=Number.isFinite||function(t){return"number"==typeof t&&isFinite(t)},g=Number.isSafeInteger||function(t){return"number"==typeof t&&Math.abs(t)<=m},m=Number.MAX_SAFE_INTEGER||9007199254740991;let p=function(t){return t.MEDIA_ATTACHING="hlsMediaAttaching",t.MEDIA_ATTACHED="hlsMediaAttached",t.MEDIA_DETACHING="hlsMediaDetaching",t.MEDIA_DETACHED="hlsMediaDetached",t.BUFFER_RESET="hlsBufferReset",t.BUFFER_CODECS="hlsBufferCodecs",t.BUFFER_CREATED="hlsBufferCreated",t.BUFFER_APPENDING="hlsBufferAppending",t.BUFFER_APPENDED="hlsBufferAppended",t.BUFFER_EOS="hlsBufferEos",t.BUFFER_FLUSHING="hlsBufferFlushing",t.BUFFER_FLUSHED="hlsBufferFlushed",t.MANIFEST_LOADING="hlsManifestLoading",t.MANIFEST_LOADED="hlsManifestLoaded",t.MANIFEST_PARSED="hlsManifestParsed",t.LEVEL_SWITCHING="hlsLevelSwitching",t.LEVEL_SWITCHED="hlsLevelSwitched",t.LEVEL_LOADING="hlsLevelLoading",t.LEVEL_LOADED="hlsLevelLoaded",t.LEVEL_UPDATED="hlsLevelUpdated",t.LEVEL_PTS_UPDATED="hlsLevelPtsUpdated",t.LEVELS_UPDATED="hlsLevelsUpdated",t.AUDIO_TRACKS_UPDATED="hlsAudioTracksUpdated",t.AUDIO_TRACK_SWITCHING="hlsAudioTrackSwitching",t.AUDIO_TRACK_SWITCHED="hlsAudioTrackSwitched",t.AUDIO_TRACK_LOADING="hlsAudioTrackLoading",t.AUDIO_TRACK_LOADED="hlsAudioTrackLoaded",t.SUBTITLE_TRACKS_UPDATED="hlsSubtitleTracksUpdated",t.SUBTITLE_TRACKS_CLEARED="hlsSubtitleTracksCleared",t.SUBTITLE_TRACK_SWITCH="hlsSubtitleTrackSwitch",t.SUBTITLE_TRACK_LOADING="hlsSubtitleTrackLoading",t.SUBTITLE_TRACK_LOADED="hlsSubtitleTrackLoaded",t.SUBTITLE_FRAG_PROCESSED="hlsSubtitleFragProcessed",t.CUES_PARSED="hlsCuesParsed",t.NON_NATIVE_TEXT_TRACKS_FOUND="hlsNonNativeTextTracksFound",t.INIT_PTS_FOUND="hlsInitPtsFound",t.FRAG_LOADING="hlsFragLoading",t.FRAG_LOAD_EMERGENCY_ABORTED="hlsFragLoadEmergencyAborted",t.FRAG_LOADED="hlsFragLoaded",t.FRAG_DECRYPTED="hlsFragDecrypted",t.FRAG_PARSING_INIT_SEGMENT="hlsFragParsingInitSegment",t.FRAG_PARSING_USERDATA="hlsFragParsingUserdata",t.FRAG_PARSING_METADATA="hlsFragParsingMetadata",t.FRAG_PARSED="hlsFragParsed",t.FRAG_BUFFERED="hlsFragBuffered",t.FRAG_CHANGED="hlsFragChanged",t.FPS_DROP="hlsFpsDrop",t.FPS_DROP_LEVEL_CAPPING="hlsFpsDropLevelCapping",t.MAX_AUTO_LEVEL_UPDATED="hlsMaxAutoLevelUpdated",t.ERROR="hlsError",t.DESTROYING="hlsDestroying",t.KEY_LOADING="hlsKeyLoading",t.KEY_LOADED="hlsKeyLoaded",t.LIVE_BACK_BUFFER_REACHED="hlsLiveBackBufferReached",t.BACK_BUFFER_REACHED="hlsBackBufferReached",t.STEERING_MANIFEST_LOADED="hlsSteeringManifestLoaded",t}({}),v=function(t){return t.NETWORK_ERROR="networkError",t.MEDIA_ERROR="mediaError",t.KEY_SYSTEM_ERROR="keySystemError",t.MUX_ERROR="muxError",t.OTHER_ERROR="otherError",t}({}),y=function(t){return t.KEY_SYSTEM_NO_KEYS="keySystemNoKeys",t.KEY_SYSTEM_NO_ACCESS="keySystemNoAccess",t.KEY_SYSTEM_NO_SESSION="keySystemNoSession",t.KEY_SYSTEM_NO_CONFIGURED_LICENSE="keySystemNoConfiguredLicense",t.KEY_SYSTEM_LICENSE_REQUEST_FAILED="keySystemLicenseRequestFailed",t.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED="keySystemServerCertificateRequestFailed",t.KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED="keySystemServerCertificateUpdateFailed",t.KEY_SYSTEM_SESSION_UPDATE_FAILED="keySystemSessionUpdateFailed",t.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED="keySystemStatusOutputRestricted",t.KEY_SYSTEM_STATUS_INTERNAL_ERROR="keySystemStatusInternalError",t.MANIFEST_LOAD_ERROR="manifestLoadError",t.MANIFEST_LOAD_TIMEOUT="manifestLoadTimeOut",t.MANIFEST_PARSING_ERROR="manifestParsingError",t.MANIFEST_INCOMPATIBLE_CODECS_ERROR="manifestIncompatibleCodecsError",t.LEVEL_EMPTY_ERROR="levelEmptyError",t.LEVEL_LOAD_ERROR="levelLoadError",t.LEVEL_LOAD_TIMEOUT="levelLoadTimeOut",t.LEVEL_PARSING_ERROR="levelParsingError",t.LEVEL_SWITCH_ERROR="levelSwitchError",t.AUDIO_TRACK_LOAD_ERROR="audioTrackLoadError",t.AUDIO_TRACK_LOAD_TIMEOUT="audioTrackLoadTimeOut",t.SUBTITLE_LOAD_ERROR="subtitleTrackLoadError",t.SUBTITLE_TRACK_LOAD_TIMEOUT="subtitleTrackLoadTimeOut",t.FRAG_LOAD_ERROR="fragLoadError",t.FRAG_LOAD_TIMEOUT="fragLoadTimeOut",t.FRAG_DECRYPT_ERROR="fragDecryptError",t.FRAG_PARSING_ERROR="fragParsingError",t.FRAG_GAP="fragGap",t.REMUX_ALLOC_ERROR="remuxAllocError",t.KEY_LOAD_ERROR="keyLoadError",t.KEY_LOAD_TIMEOUT="keyLoadTimeOut",t.BUFFER_ADD_CODEC_ERROR="bufferAddCodecError",t.BUFFER_INCOMPATIBLE_CODECS_ERROR="bufferIncompatibleCodecsError",t.BUFFER_APPEND_ERROR="bufferAppendError",t.BUFFER_APPENDING_ERROR="bufferAppendingError",t.BUFFER_STALLED_ERROR="bufferStalledError",t.BUFFER_FULL_ERROR="bufferFullError",t.BUFFER_SEEK_OVER_HOLE="bufferSeekOverHole",t.BUFFER_NUDGE_ON_STALL="bufferNudgeOnStall",t.INTERNAL_EXCEPTION="internalException",t.INTERNAL_ABORTED="aborted",t.UNKNOWN="unknown",t}({});const E=function(){},T={trace:E,debug:E,log:E,warn:E,info:E,error:E};let S=T;function L(t,...e){e.forEach((function(e){S[e]=t[e]?t[e].bind(t):function(t){const e=self.console[t];return e?e.bind(self.console,`[${t}] >`):E}(e)}))}const A=S,R=/^(\d+)x(\d+)$/,b=/(.+?)=(".*?"|.*?)(?:,|$)/g;class k{constructor(t){"string"==typeof t&&(t=k.parseAttrList(t)),u(this,t)}get clientAttrs(){return Object.keys(this).filter((t=>"X-"===t.substring(0,2)))}decimalInteger(t){const e=parseInt(this[t],10);return e>Number.MAX_SAFE_INTEGER?1/0:e}hexadecimalInteger(t){if(this[t]){let e=(this[t]||"0x").slice(2);e=(1&e.length?"0":"")+e;const s=new Uint8Array(e.length/2);for(let t=0;t<e.length/2;t++)s[t]=parseInt(e.slice(2*t,2*t+2),16);return s}return null}hexadecimalIntegerAsNumber(t){const e=parseInt(this[t],16);return e>Number.MAX_SAFE_INTEGER?1/0:e}decimalFloatingPoint(t){return parseFloat(this[t])}optionalFloat(t,e){const s=this[t];return s?parseFloat(s):e}enumeratedString(t){return this[t]}bool(t){return"YES"===this[t]}decimalResolution(t){const e=R.exec(this[t]);if(null!==e)return{width:parseInt(e[1],10),height:parseInt(e[2],10)}}static parseAttrList(t){let e;const s={};for(b.lastIndex=0;null!==(e=b.exec(t));){let t=e[2];0===t.indexOf('"')&&t.lastIndexOf('"')===t.length-1&&(t=t.slice(1,-1));s[e[1].trim()]=t}return s}}function w(t){return"SCTE35-OUT"===t||"SCTE35-IN"===t}class D{constructor(t,e){if(this.attr=void 0,this._startDate=void 0,this._endDate=void 0,this._badValueForSameId=void 0,e){const s=e.attr;for(const e in s)if(Object.prototype.hasOwnProperty.call(t,e)&&t[e]!==s[e]){A.warn(`DATERANGE tag attribute: "${e}" does not match for tags with ID: "${t.ID}"`),this._badValueForSameId=e;break}t=u(new k({}),s,t)}if(this.attr=t,this._startDate=new Date(t["START-DATE"]),"END-DATE"in this.attr){const t=new Date(this.attr["END-DATE"]);f(t.getTime())&&(this._endDate=t)}}get id(){return this.attr.ID}get class(){return this.attr.CLASS}get startDate(){return this._startDate}get endDate(){if(this._endDate)return this._endDate;const t=this.duration;return null!==t?new Date(this._startDate.getTime()+1e3*t):null}get duration(){if("DURATION"in this.attr){const t=this.attr.decimalFloatingPoint("DURATION");if(f(t))return t}else if(this._endDate)return(this._endDate.getTime()-this._startDate.getTime())/1e3;return null}get plannedDuration(){return"PLANNED-DURATION"in this.attr?this.attr.decimalFloatingPoint("PLANNED-DURATION"):null}get endOnNext(){return this.attr.bool("END-ON-NEXT")}get isValid(){return!!this.id&&!this._badValueForSameId&&f(this.startDate.getTime())&&(null===this.duration||this.duration>=0)&&(!this.endOnNext||!!this.class)}}class I{constructor(){this.aborted=!1,this.loaded=0,this.retry=0,this.total=0,this.chunkCount=0,this.bwEstimate=0,this.loading={start:0,first:0,end:0},this.parsing={start:0,end:0},this.buffering={start:0,first:0,end:0}}}var C="audio",_="video",x="audiovideo";class P{constructor(t){this._byteRange=null,this._url=null,this.baseurl=void 0,this.relurl=void 0,this.elementaryStreams={[C]:null,[_]:null,[x]:null},this.baseurl=t}setByteRange(t,e){const s=t.split("@",2);let i;i=1===s.length?(null==e?void 0:e.byteRangeEndOffset)||0:parseInt(s[1]),this._byteRange=[i,parseInt(s[0])+i]}get byteRange(){return this._byteRange?this._byteRange:[]}get byteRangeStartOffset(){return this.byteRange[0]}get byteRangeEndOffset(){return this.byteRange[1]}get url(){return!this._url&&this.baseurl&&this.relurl&&(this._url=o.buildAbsoluteURL(this.baseurl,this.relurl,{alwaysNormalize:!0})),this._url||""}set url(t){this._url=t}}class M extends P{constructor(t,e){super(e),this._decryptdata=null,this.rawProgramDateTime=null,this.programDateTime=null,this.tagList=[],this.duration=0,this.sn=0,this.levelkeys=void 0,this.type=void 0,this.loader=null,this.keyLoader=null,this.level=-1,this.cc=0,this.startPTS=void 0,this.endPTS=void 0,this.startDTS=void 0,this.endDTS=void 0,this.start=0,this.deltaPTS=void 0,this.maxStartPTS=void 0,this.minEndPTS=void 0,this.stats=new I,this.data=void 0,this.bitrateTest=!1,this.title=null,this.initSegment=null,this.endList=void 0,this.gap=void 0,this.urlId=0,this.type=t}get decryptdata(){const{levelkeys:t}=this;if(!t&&!this._decryptdata)return null;if(!this._decryptdata&&this.levelkeys&&!this.levelkeys.NONE){const t=this.levelkeys.identity;if(t)this._decryptdata=t.getDecryptData(this.sn);else{const t=Object.keys(this.levelkeys);if(1===t.length)return this._decryptdata=this.levelkeys[t[0]].getDecryptData(this.sn)}}return this._decryptdata}get end(){return this.start+this.duration}get endProgramDateTime(){if(null===this.programDateTime)return null;if(!f(this.programDateTime))return null;const t=f(this.duration)?this.duration:0;return this.programDateTime+1e3*t}get encrypted(){var t;if(null!=(t=this._decryptdata)&&t.encrypted)return!0;if(this.levelkeys){const t=Object.keys(this.levelkeys),e=t.length;if(e>1||1===e&&this.levelkeys[t[0]].encrypted)return!0}return!1}setKeyFormat(t){if(this.levelkeys){const e=this.levelkeys[t];e&&!this._decryptdata&&(this._decryptdata=e.getDecryptData(this.sn))}}abortRequests(){var t,e;null==(t=this.loader)||t.abort(),null==(e=this.keyLoader)||e.abort()}setElementaryStreamInfo(t,e,s,i,r,n=!1){const{elementaryStreams:a}=this,o=a[t];o?(o.startPTS=Math.min(o.startPTS,e),o.endPTS=Math.max(o.endPTS,s),o.startDTS=Math.min(o.startDTS,i),o.endDTS=Math.max(o.endDTS,r)):a[t]={startPTS:e,endPTS:s,startDTS:i,endDTS:r,partial:n}}clearElementaryStreamInfo(){const{elementaryStreams:t}=this;t[C]=null,t[_]=null,t[x]=null}}class F extends P{constructor(t,e,s,i,r){super(s),this.fragOffset=0,this.duration=0,this.gap=!1,this.independent=!1,this.relurl=void 0,this.fragment=void 0,this.index=void 0,this.stats=new I,this.duration=t.decimalFloatingPoint("DURATION"),this.gap=t.bool("GAP"),this.independent=t.bool("INDEPENDENT"),this.relurl=t.enumeratedString("URI"),this.fragment=e,this.index=i;const n=t.enumeratedString("BYTERANGE");n&&this.setByteRange(n,r),r&&(this.fragOffset=r.fragOffset+r.duration)}get start(){return this.fragment.start+this.fragOffset}get end(){return this.start+this.duration}get loaded(){const{elementaryStreams:t}=this;return!!(t.audio||t.video||t.audiovideo)}}class O{constructor(t){this.PTSKnown=!1,this.alignedSliding=!1,this.averagetargetduration=void 0,this.endCC=0,this.endSN=0,this.fragments=void 0,this.fragmentHint=void 0,this.partList=null,this.dateRanges=void 0,this.live=!0,this.ageHeader=0,this.advancedDateTime=void 0,this.updated=!0,this.advanced=!0,this.availabilityDelay=void 0,this.misses=0,this.startCC=0,this.startSN=0,this.startTimeOffset=null,this.targetduration=0,this.totalduration=0,this.type=null,this.url=void 0,this.m3u8="",this.version=null,this.canBlockReload=!1,this.canSkipUntil=0,this.canSkipDateRanges=!1,this.skippedSegments=0,this.recentlyRemovedDateranges=void 0,this.partHoldBack=0,this.holdBack=0,this.partTarget=0,this.preloadHint=void 0,this.renditionReports=void 0,this.tuneInGoal=0,this.deltaUpdateFailed=void 0,this.driftStartTime=0,this.driftEndTime=0,this.driftStart=0,this.driftEnd=0,this.encryptedFragments=void 0,this.playlistParsingError=null,this.variableList=null,this.hasVariableRefs=!1,this.fragments=[],this.encryptedFragments=[],this.dateRanges={},this.url=t}reloaded(t){if(!t)return this.advanced=!0,void(this.updated=!0);const e=this.lastPartSn-t.lastPartSn,s=this.lastPartIndex-t.lastPartIndex;this.updated=this.endSN!==t.endSN||!!s||!!e||!this.live,this.advanced=this.endSN>t.endSN||e>0||0===e&&s>0,this.updated||this.advanced?this.misses=Math.floor(.6*t.misses):this.misses=t.misses+1,this.availabilityDelay=t.availabilityDelay}get hasProgramDateTime(){return!!this.fragments.length&&f(this.fragments[this.fragments.length-1].programDateTime)}get levelTargetDuration(){return this.averagetargetduration||this.targetduration||10}get drift(){const t=this.driftEndTime-this.driftStartTime;if(t>0){return 1e3*(this.driftEnd-this.driftStart)/t}return 1}get edge(){return this.partEnd||this.fragmentEnd}get partEnd(){var t;return null!=(t=this.partList)&&t.length?this.partList[this.partList.length-1].end:this.fragmentEnd}get fragmentEnd(){var t;return null!=(t=this.fragments)&&t.length?this.fragments[this.fragments.length-1].end:0}get age(){return this.advancedDateTime?Math.max(Date.now()-this.advancedDateTime,0)/1e3:0}get lastPartIndex(){var t;return null!=(t=this.partList)&&t.length?this.partList[this.partList.length-1].index:-1}get lastPartSn(){var t;return null!=(t=this.partList)&&t.length?this.partList[this.partList.length-1].fragment.sn:this.endSN}}function N(t){return Uint8Array.from(atob(t),(t=>t.charCodeAt(0)))}function U(t){const e=t.split(":");let s=null;if("data"===e[0]&&2===e.length){const t=e[1].split(";"),i=t[t.length-1].split(",");if(2===i.length){const e="base64"===i[0],r=i[1];e?(t.splice(-1,1),s=N(r)):s=function(t){const e=B(t).subarray(0,16),s=new Uint8Array(16);return s.set(e,16-e.length),s}(r)}}return s}function B(t){return Uint8Array.from(unescape(encodeURIComponent(t)),(t=>t.charCodeAt(0)))}const $="undefined"!=typeof self?self:void 0;var G={CLEARKEY:"org.w3.clearkey",FAIRPLAY:"com.apple.fps",PLAYREADY:"com.microsoft.playready",WIDEVINE:"com.widevine.alpha"},K="org.w3.clearkey",H="com.apple.streamingkeydelivery",V="com.microsoft.playready",Y="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed";function W(t){switch(t){case H:return G.FAIRPLAY;case V:return G.PLAYREADY;case Y:return G.WIDEVINE;case K:return G.CLEARKEY}}var j="1077efecc0b24d02ace33c1e52e2fb4b",q="e2719d58a985b3c9781ab030af78d30e",X="9a04f07998404286ab92e65be0885f95",z="edef8ba979d64acea3c827dcd51d21ed";function Q(t){return t===z?G.WIDEVINE:t===X?G.PLAYREADY:t===j||t===q?G.CLEARKEY:void 0}function J(t){switch(t){case G.FAIRPLAY:return H;case G.PLAYREADY:return V;case G.WIDEVINE:return Y;case G.CLEARKEY:return K}}function Z(t){const{drmSystems:e,widevineLicenseUrl:s}=t,i=e?[G.FAIRPLAY,G.WIDEVINE,G.PLAYREADY,G.CLEARKEY].filter((t=>!!e[t])):[];return!i[G.WIDEVINE]&&s&&i.push(G.WIDEVINE),i}const tt=null!=$&&null!=(et=$.navigator)&&et.requestMediaKeySystemAccess?self.navigator.requestMediaKeySystemAccess.bind(self.navigator):null;var et;function st(t,e,s){return Uint8Array.prototype.slice?t.slice(e,s):new Uint8Array(Array.prototype.slice.call(t,e,s))}const it=(t,e)=>e+10<=t.length&&73===t[e]&&68===t[e+1]&&51===t[e+2]&&t[e+3]<255&&t[e+4]<255&&t[e+6]<128&&t[e+7]<128&&t[e+8]<128&&t[e+9]<128,rt=(t,e)=>e+10<=t.length&&51===t[e]&&68===t[e+1]&&73===t[e+2]&&t[e+3]<255&&t[e+4]<255&&t[e+6]<128&&t[e+7]<128&&t[e+8]<128&&t[e+9]<128,nt=(t,e)=>{const s=e;let i=0;for(;it(t,e);){i+=10;i+=at(t,e+6),rt(t,e+10)&&(i+=10),e+=i}if(i>0)return t.subarray(s,s+i)},at=(t,e)=>{let s=0;return s=(127&t[e])<<21,s|=(127&t[e+1])<<14,s|=(127&t[e+2])<<7,s|=127&t[e+3],s},ot=(t,e)=>it(t,e)&&at(t,e+6)+10<=t.length-e,lt=t=>{const e=ct(t);for(let t=0;t<e.length;t++){const s=e[t];if(ht(s))return pt(s)}},ht=t=>t&&"PRIV"===t.key&&"com.apple.streaming.transportStreamTimestamp"===t.info,dt=t=>{const e=String.fromCharCode(t[0],t[1],t[2],t[3]),s=at(t,4);return{type:e,size:s,data:t.subarray(10,10+s)}},ct=t=>{let e=0;const s=[];for(;it(t,e);){const i=at(t,e+6);e+=10;const r=e+i;for(;e+8<r;){const i=dt(t.subarray(e)),r=ut(i);r&&s.push(r),e+=i.size+10}rt(t,e)&&(e+=10)}return s},ut=t=>"PRIV"===t.type?ft(t):"W"===t.type[0]?mt(t):gt(t),ft=t=>{if(t.size<2)return;const e=vt(t.data,!0),s=new Uint8Array(t.data.subarray(e.length+1));return{key:t.type,info:e,data:s.buffer}},gt=t=>{if(t.size<2)return;if("TXXX"===t.type){let e=1;const s=vt(t.data.subarray(e),!0);e+=s.length+1;const i=vt(t.data.subarray(e));return{key:t.type,info:s,data:i}}const e=vt(t.data.subarray(1));return{key:t.type,data:e}},mt=t=>{if("WXXX"===t.type){if(t.size<2)return;let e=1;const s=vt(t.data.subarray(e),!0);e+=s.length+1;const i=vt(t.data.subarray(e));return{key:t.type,info:s,data:i}}const e=vt(t.data);return{key:t.type,data:e}},pt=t=>{if(8===t.data.byteLength){const e=new Uint8Array(t.data),s=1&e[3];let i=(e[4]<<23)+(e[5]<<15)+(e[6]<<7)+e[7];return i/=45,s&&(i+=47721858.84),Math.round(i)}},vt=(t,e=!1)=>{const s=Et();if(s){const i=s.decode(t);if(e){const t=i.indexOf("\0");return-1!==t?i.substring(0,t):i}return i.replace(/\0/g,"")}const i=t.length;let r,n,a,o="",l=0;for(;l<i;){if(r=t[l++],0===r&&e)return o;if(0!==r&&3!==r)switch(r>>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:o+=String.fromCharCode(r);break;case 12:case 13:n=t[l++],o+=String.fromCharCode((31&r)<<6|63&n);break;case 14:n=t[l++],a=t[l++],o+=String.fromCharCode((15&r)<<12|(63&n)<<6|63&a)}}return o};let yt;function Et(){if(!navigator.userAgent.includes("PlayStation 4"))return yt||void 0===self.TextDecoder||(yt=new self.TextDecoder("utf-8")),yt}const Tt=function(t){let e="";for(let s=0;s<t.length;s++){let i=t[s].toString(16);i.length<2&&(i="0"+i),e+=i}return e},St=Math.pow(2,32)-1,Lt=[].push,At={video:1,audio:2,id3:3,text:4};function Rt(t){return String.fromCharCode.apply(null,t)}function bt(t,e){const s=t[e]<<8|t[e+1];return s<0?65536+s:s}function kt(t,e){const s=Dt(t,e);return s<0?4294967296+s:s}function wt(t,e){let s=kt(t,e);return s*=Math.pow(2,32),s+=kt(t,e+4),s}function Dt(t,e){return t[e]<<24|t[e+1]<<16|t[e+2]<<8|t[e+3]}function It(t,e,s){t[e]=s>>24,t[e+1]=s>>16&255,t[e+2]=s>>8&255,t[e+3]=255&s}function Ct(t,e){const s=[];if(!e.length)return s;const i=t.byteLength;for(let r=0;r<i;){const n=kt(t,r),a=n>1?r+n:i;if(Rt(t.subarray(r+4,r+8))===e[0])if(1===e.length)s.push(t.subarray(r+8,a));else{const i=Ct(t.subarray(r+8,a),e.slice(1));i.length&&Lt.apply(s,i)}r=a}return s}function _t(t){const e=[],s=t[0];let i=8;const r=kt(t,i);i+=4;let n=0,a=0;0===s?(n=kt(t,i),a=kt(t,i+4),i+=8):(n=wt(t,i),a=wt(t,i+8),i+=16),i+=2;let o=t.length+a;const l=bt(t,i);i+=2;for(let s=0;s<l;s++){let s=i;const n=kt(t,s);s+=4;const a=2147483647&n;if(1===(2147483648&n)>>>31)return A.warn("SIDX has hierarchical references (not supported)"),null;const l=kt(t,s);s+=4,e.push({referenceSize:a,subsegmentDuration:l,info:{duration:l/r,start:o,end:o+a-1}}),o+=a,s+=4,i=s}return{earliestPresentationTime:n,timescale:r,version:s,referencesCount:l,references:e}}function xt(t){const e=[],s=Ct(t,["moov","trak"]);for(let t=0;t<s.length;t++){const i=s[t],r=Ct(i,["tkhd"])[0];if(r){let t=r[0];const s=kt(r,0===t?12:20),n=Ct(i,["mdia","mdhd"])[0];if(n){t=n[0];const r=kt(n,0===t?12:20),a=Ct(i,["mdia","hdlr"])[0];if(a){const t=Rt(a.subarray(8,12)),n={soun:C,vide:_}[t];if(n){const t=Pt(Ct(i,["mdia","minf","stbl","stsd"])[0]);e[s]={timescale:r,type:n},e[n]=h({timescale:r,id:s},t)}}}}}return Ct(t,["moov","mvex","trex"]).forEach((t=>{const s=kt(t,4),i=e[s];i&&(i.default={duration:kt(t,12),flags:kt(t,20)})})),e}function Pt(t){const e=t.subarray(8),s=e.subarray(86),i=Rt(e.subarray(4,8));let r=i;const n="enca"===i||"encv"===i;if(n){const t=Ct(e,[i])[0];Ct(t.subarray("enca"===i?28:78),["sinf"]).forEach((t=>{const e=Ct(t,["schm"])[0];if(e){const s=Rt(e.subarray(4,8));if("cbcs"===s||"cenc"===s){const e=Ct(t,["frma"])[0];e&&(r=Rt(e))}}}))}switch(r){case"avc1":case"avc2":case"avc3":case"avc4":{const t=Ct(s,["avcC"])[0];r+="."+Ft(t[1])+Ft(t[2])+Ft(t[3]);break}case"mp4a":{const t=Ct(e,[i])[0],s=Ct(t.subarray(28),["esds"])[0];if(s&&s.length>12){let t=4;if(3!==s[t++])break;t=Mt(s,t),t+=2;const e=s[t++];if(128&e&&(t+=2),64&e&&(t+=s[t++]),4!==s[t++])break;t=Mt(s,t);const i=s[t++];if(64!==i)break;if(r+="."+Ft(i),t+=12,5!==s[t++])break;t=Mt(s,t);const n=s[t++];let a=(248&n)>>3;31===a&&(a+=1+((7&n)<<3)+((224&s[t])>>5)),r+="."+a}break}case"hvc1":case"hev1":{const t=Ct(s,["hvcC"])[0],e=t[1],i=["","A","B","C"][e>>6],n=31&e,a=kt(t,2),o=(32&e)>>5?"H":"L",l=t[12],h=t.subarray(6,12);r+="."+i+n,r+="."+a.toString(16).toUpperCase(),r+="."+o+l;let d="";for(let t=h.length;t--;){const e=h[t];if(e||d){d="."+e.toString(16).toUpperCase()+d}}r+=d;break}case"dvh1":case"dvhe":{const t=Ct(s,["dvcC"])[0],e=t[2]>>1&127,i=t[2]<<5&32|t[3]>>3&31;r+="."+Ot(e)+"."+Ot(i);break}case"vp09":{const t=Ct(s,["vpcC"])[0],e=t[4],i=t[5],n=t[6]>>4&15;r+="."+Ot(e)+"."+Ot(i)+"."+Ot(n);break}case"av01":{const t=Ct(s,["av1C"])[0],e=t[1]>>>5,i=31&t[1],n=t[2]>>>7?"H":"M",a=(64&t[2])>>6,o=(32&t[2])>>5,l=2===e&&a?o?12:10:a?10:8,h=(16&t[2])>>4,d=(8&t[2])>>3,c=(4&t[2])>>2,u=3&t[2],f=1,g=1,m=1,p=0;r+="."+e+"."+Ot(i)+n+"."+Ot(l)+"."+h+"."+d+c+u+"."+Ot(f)+"."+Ot(g)+"."+Ot(m)+"."+p;break}}return{codec:r,encrypted:n}}function Mt(t,e){const s=e+5;for(;128&t[e++]&&e<s;);return e}function Ft(t){return("0"+t.toString(16).toUpperCase()).slice(-2)}function Ot(t){return(t<10?"0":"")+t}function Nt(t){const e=Ct(t,["schm"])[0];if(e){const s=Rt(e.subarray(4,8));if("cbcs"===s||"cenc"===s)return Ct(t,["schi","tenc"])[0]}return null}function Ut(t){const e=kt(t,0);let s=8;1&e&&(s+=4),4&e&&(s+=4);let i=0;const r=kt(t,4);for(let n=0;n<r;n++){if(256&e){i+=kt(t,s),s+=4}512&e&&(s+=4),1024&e&&(s+=4),2048&e&&(s+=4)}return i}function Bt(t,e){const s=new Uint8Array(t.length+e.length);return s.set(t),s.set(e,t.length),s}function $t(t,e){const s=[],i=e.samples,r=e.timescale,n=e.id;let a=!1;return Ct(i,["moof"]).map((o=>{const l=o.byteOffset-8;Ct(o,["traf"]).map((o=>{const h=Ct(o,["tfdt"]).map((t=>{const e=t[0];let s=kt(t,4);return 1===e&&(s*=Math.pow(2,32),s+=kt(t,8)),s/r}))[0];return void 0!==h&&(t=h),Ct(o,["tfhd"]).map((h=>{const d=kt(h,4),c=16777215&kt(h,0);let u=0;const f=!!(16&c);let g=0;const m=!!(32&c);let p=8;d===n&&(!!(1&c)&&(p+=8),!!(2&c)&&(p+=4),!!(8&c)&&(u=kt(h,p),p+=4),f&&(g=kt(h,p),p+=4),m&&(p+=4),"video"===e.type&&(a=function(t){if(!t)return!1;const e=t.indexOf("."),s=e<0?t:t.substring(0,e);return"hvc1"===s||"hev1"===s||"dvh1"===s||"dvhe"===s}(e.codec)),Ct(o,["trun"]).map((n=>{const o=n[0],h=16777215&kt(n,0),d=!!(1&h);let c=0;const f=!!(4&h),m=!!(256&h);let p=0;const v=!!(512&h);let y=0;const E=!!(1024&h),T=!!(2048&h);let S=0;const L=kt(n,4);let A=8;d&&(c=kt(n,A),A+=4),f&&(A+=4);let R=c+l;for(let l=0;l<L;l++){if(m?(p=kt(n,A),A+=4):p=u,v?(y=kt(n,A),A+=4):y=g,E&&(A+=4),T&&(S=0===o?kt(n,A):Dt(n,A),A+=4),e.type===_){let e=0;for(;e<y;){const n=kt(i,R);if(R+=4,Gt(a,i[R])){Kt(i.subarray(R,R+n),a?2:1,t+S/r,s)}R+=n,e+=n+4}}t+=p/r}})))}))}))})),s}function Gt(t,e){if(t){const t=e>>1&63;return 39===t||40===t}return 6===(31&e)}function Kt(t,e,s,i){const r=Ht(t);let n=0;n+=e;let a=0,o=0,l=0;for(;n<r.length;){a=0;do{if(n>=r.length)break;l=r[n++],a+=l}while(255===l);o=0;do{if(n>=r.length)break;l=r[n++],o+=l}while(255===l);const t=r.length-n;let e=n;if(o<t)n+=o;else if(o>t){A.error(`Malformed SEI payload. ${o} is too small, only ${t} bytes left to parse.`);break}if(4===a){if(181===r[e++]){const t=bt(r,e);if(e+=2,49===t){const t=kt(r,e);if(e+=4,1195456820===t){const t=r[e++];if(3===t){const n=r[e++],o=64&n,l=o?2+3*(31&n):0,h=new Uint8Array(l);if(o){h[0]=n;for(let t=1;t<l;t++)h[t]=r[e++]}i.push({type:t,payloadType:a,pts:s,bytes:h})}}}}}else if(5===a&&o>16){const t=[];for(let s=0;s<16;s++){const i=r[e++].toString(16);t.push(1==i.length?"0"+i:i),3!==s&&5!==s&&7!==s&&9!==s||t.push("-")}const n=o-16,l=new Uint8Array(n);for(let t=0;t<n;t++)l[t]=r[e++];i.push({payloadType:a,pts:s,uuid:t.join(""),userData:vt(l),userDataBytes:l})}}}function Ht(t){const e=t.byteLength,s=[];let i=1;for(;i<e-2;)0===t[i]&&0===t[i+1]&&3===t[i+2]?(s.push(i+2),i+=2):i++;if(0===s.length)return t;const r=e-s.length,n=new Uint8Array(r);let a=0;for(i=0;i<r;a++,i++)a===s[0]&&(a++,s.shift()),n[i]=t[a];return n}function Vt(t,e,s){if(16!==t.byteLength)throw new RangeError("Invalid system id");let i,r,n;if(e){i=1,r=new Uint8Array(16*e.length);for(let t=0;t<e.length;t++){const s=e[t];if(16!==s.byteLength)throw new RangeError("Invalid key");r.set(s,16*t)}}else i=0,r=new Uint8Array;i>0?(n=new Uint8Array(4),e.length>0&&new DataView(n.buffer).setUint32(0,e.length,!1)):n=new Uint8Array;const a=new Uint8Array(4);return s&&s.byteLength>0&&new DataView(a.buffer).setUint32(0,s.byteLength,!1),function(t,...e){const s=e.length;let i=8,r=s;for(;r--;)i+=e[r].byteLength;const n=new Uint8Array(i);for(n[0]=i>>24&255,n[1]=i>>16&255,n[2]=i>>8&255,n[3]=255&i,n.set(t,4),r=0,i=8;r<s;r++)n.set(e[r],i),i+=e[r].byteLength;return n}([112,115,115,104],new Uint8Array([i,0,0,0]),t,n,r,a,s||new Uint8Array)}function Yt(t){const e=t.getUint32(0),s=t.byteOffset,i=t.byteLength;if(i<e)return{offset:s,size:i};if(1886614376!==t.getUint32(4))return{offset:s,size:e};const r=t.getUint32(8)>>>24;if(0!==r&&1!==r)return{offset:s,size:e};const n=t.buffer,a=Tt(new Uint8Array(n,s+12,16)),o=t.getUint32(28);let l=null,h=null;if(0===r){if(e-32<o||o<22)return{offset:s,size:e};h=new Uint8Array(n,s+32,o)}else if(1===r){if(!o||i<s+32+16*o+16)return{offset:s,size:e};l=[];for(let t=0;t<o;t++)l.push(new Uint8Array(n,s+32+16*t,16))}return{version:r,systemId:a,kids:l,data:h,offset:s,size:e}}let Wt={};class jt{static clearKeyUriToKeyIdMap(){Wt={}}constructor(t,e,s,i=[1],r=null){this.uri=void 0,this.method=void 0,this.keyFormat=void 0,this.keyFormatVersions=void 0,this.encrypted=void 0,this.isCommonEncryption=void 0,this.iv=null,this.key=null,this.keyId=null,this.pssh=null,this.method=t,this.uri=e,this.keyFormat=s,this.keyFormatVersions=i,this.iv=r,this.encrypted=!!t&&"NONE"!==t,this.isCommonEncryption=this.encrypted&&"AES-128"!==t}isSupported(){if(this.method){if("AES-128"===this.method||"NONE"===this.method)return!0;if("identity"===this.keyFormat)return"SAMPLE-AES"===this.method;switch(this.keyFormat){case H:case Y:case V:case K:return-1!==["ISO-23001-7","SAMPLE-AES","SAMPLE-AES-CENC","SAMPLE-AES-CTR"].indexOf(this.method)}}return!1}getDecryptData(t){if(!this.encrypted||!this.uri)return null;if("AES-128"===this.method&&this.uri&&!this.iv){"number"!=typeof t&&("AES-128"!==this.method||this.iv||A.warn(`missing IV for initialization segment with method="${this.method}" - compliance issue`),t=0);const e=function(t){const e=new Uint8Array(16);for(let s=12;s<16;s++)e[s]=t>>8*(15-s)&255;return e}(t);return new jt(this.method,this.uri,"identity",this.keyFormatVersions,e)}const e=U(this.uri);if(e)switch(this.keyFormat){case Y:this.pssh=e,e.length>=22&&(this.keyId=e.subarray(e.length-22,e.length-6));break;case V:{const t=new Uint8Array([154,4,240,121,152,64,66,134,171,146,230,91,224,136,95,149]);this.pssh=Vt(t,null,e);const s=new Uint16Array(e.buffer,e.byteOffset,e.byteLength/2),i=String.fromCharCode.apply(null,Array.from(s)),r=i.substring(i.indexOf("<"),i.length),n=(new DOMParser).parseFromString(r,"text/xml").getElementsByTagName("KID")[0];if(n){const t=n.childNodes[0]?n.childNodes[0].nodeValue:n.getAttribute("VALUE");if(t){const e=N(t).subarray(0,16);!function(t){const e=function(t,e,s){const i=t[e];t[e]=t[s],t[s]=i};e(t,0,3),e(t,1,2),e(t,4,5),e(t,6,7)}(e),this.keyId=e}}break}default:{let t=e.subarray(0,16);if(16!==t.length){const e=new Uint8Array(16);e.set(t,16-t.length),t=e}this.keyId=t;break}}if(!this.keyId||16!==this.keyId.byteLength){let t=Wt[this.uri];if(!t){const e=Object.keys(Wt).length%Number.MAX_SAFE_INTEGER;t=new Uint8Array(16);new DataView(t.buffer,12,4).setUint32(0,e),Wt[this.uri]=t}this.keyId=t}return this}}const qt=/\{\$([a-zA-Z0-9-_]+)\}/g;function Xt(t){return qt.test(t)}function zt(t,e,s){if(null!==t.variableList||t.hasVariableRefs)for(let i=s.length;i--;){const r=s[i],n=e[r];n&&(e[r]=Qt(t,n))}}function Qt(t,e){if(null!==t.variableList||t.hasVariableRefs){const s=t.variableList;return e.replace(qt,(e=>{const i=e.substring(2,e.length-1),r=null==s?void 0:s[i];return void 0===r?(t.playlistParsingError||(t.playlistParsingError=new Error(`Missing preceding EXT-X-DEFINE tag for Variable Reference: "${i}"`)),e):r}))}return e}function Jt(t,e,s){let i,r,n=t.variableList;if(n||(t.variableList=n={}),"QUERYPARAM"in e){i=e.QUERYPARAM;try{const t=new self.URL(s).searchParams;if(!t.has(i))throw new Error(`"${i}" does not match any query parameter in URI: "${s}"`);r=t.get(i)}catch(e){t.playlistParsingError||(t.playlistParsingError=new Error(`EXT-X-DEFINE QUERYPARAM: ${e.message}`))}}else i=e.NAME,r=e.VALUE;i in n?t.playlistParsingError||(t.playlistParsingError=new Error(`EXT-X-DEFINE duplicate Variable Name declarations: "${i}"`)):n[i]=r||""}function Zt(t,e,s){const i=e.IMPORT;if(s&&i in s){let e=t.variableList;e||(t.variableList=e={}),e[i]=s[i]}else t.playlistParsingError||(t.playlistParsingError=new Error(`EXT-X-DEFINE IMPORT attribute not found in Multivariant Playlist: "${i}"`))}function te(t=!0){if("undefined"==typeof self)return;return(t||!self.MediaSource)&&self.ManagedMediaSource||self.MediaSource||self.WebKitMediaSource}const ee={audio:{a3ds:1,"ac-3":.95,"ac-4":1,alac:.9,alaw:1,dra1:1,"dts+":1,"dts-":1,dtsc:1,dtse:1,dtsh:1,"ec-3":.9,enca:1,fLaC:.9,flac:.9,FLAC:.9,g719:1,g726:1,m4ae:1,mha1:1,mha2:1,mhm1:1,mhm2:1,mlpa:1,mp4a:1,"raw ":1,Opus:1,opus:1,samr:1,sawb:1,sawp:1,sevc:1,sqcp:1,ssmv:1,twos:1,ulaw:1},video:{avc1:1,avc2:1,avc3:1,avc4:1,avcp:1,av01:.8,drac:1,dva1:1,dvav:1,dvh1:.7,dvhe:.7,encv:1,hev1:.75,hvc1:.75,mjp2:1,mp4v:1,mvc1:1,mvc2:1,mvc3:1,mvc4:1,resv:1,rv60:1,s263:1,svc1:1,svc2:1,"vc-1":1,vp08:1,vp09:.9},text:{stpp:1,wvtt:1}};function se(t,e,s=!0){return!t.split(",").some((t=>!ie(t,e,s)))}function ie(t,e,s=!0){var i;const r=te(s);return null!=(i=null==r?void 0:r.isTypeSupported(re(t,e)))&&i}function re(t,e){return`${e}/mp4;codecs="${t}"`}function ne(t){if(t){const e=t.substring(0,4);return ee.video[e]}return 2}function ae(t){return t.split(",").reduce(((t,e)=>{const s=ee.video[e];return s?(2*s+t)/(t?3:2):(ee.audio[e]+t)/(t?2:1)}),0)}const oe={};const le=/flac|opus/i;function he(t,e=!0){return t.replace(le,(t=>function(t,e=!0){if(oe[t])return oe[t];const s={flac:["flac","fLaC","FLAC"],opus:["opus","Opus"]}[t];for(let i=0;i<s.length;i++)if(ie(s[i],"audio",e))return oe[t]=s[i],s[i];return t}(t.toLowerCase(),e)))}function de(t,e){return t&&"mp4a"!==t?t:e?e.split(",")[0]:e}const ce=/#EXT-X-STREAM-INF:([^\r\n]*)(?:[\r\n](?:#[^\r\n]*)?)*([^\r\n]+)|#EXT-X-(SESSION-DATA|SESSION-KEY|DEFINE|CONTENT-STEERING|START):([^\r\n]*)[\r\n]+/g,ue=/#EXT-X-MEDIA:(.*)/g,fe=/^#EXT(?:INF|-X-TARGETDURATION):/m,ge=new RegExp([/#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source,/(?!#) *(\S[^\r\n]*)/.source,/#EXT-X-BYTERANGE:*(.+)/.source,/#EXT-X-PROGRAM-DATE-TIME:(.+)/.source,/#.*/.source].join("|"),"g"),me=new RegExp([/#(EXTM3U)/.source,/#EXT-X-(DATERANGE|DEFINE|KEY|MAP|PART|PART-INF|PLAYLIST-TYPE|PRELOAD-HINT|RENDITION-REPORT|SERVER-CONTROL|SKIP|START):(.+)/.source,/#EXT-X-(BITRATE|DISCONTINUITY-SEQUENCE|MEDIA-SEQUENCE|TARGETDURATION|VERSION): *(\d+)/.source,/#EXT-X-(DISCONTINUITY|ENDLIST|GAP|INDEPENDENT-SEGMENTS)/.source,/(#)([^:]*):(.*)/.source,/(#)(.*)(?:.*)\r?\n?/.source].join("|"));class pe{static findGroup(t,e){for(let s=0;s<t.length;s++){const i=t[s];if(i.id===e)return i}}static resolve(t,e){return o.buildAbsoluteURL(e,t,{alwaysNormalize:!0})}static isMediaPlaylist(t){return fe.test(t)}static parseMasterPlaylist(t,e){const s={contentSteering:null,levels:[],playlistParsingError:null,sessionData:null,sessionKeys:null,startTimeOffset:null,variableList:null,hasVariableRefs:Xt(t)},i=[];let r;for(ce.lastIndex=0;null!=(r=ce.exec(t));)if(r[1]){var n;const t=new k(r[1]);zt(s,t,["CODECS","SUPPLEMENTAL-CODECS","ALLOWED-CPC","PATHWAY-ID","STABLE-VARIANT-ID","AUDIO","VIDEO","SUBTITLES","CLOSED-CAPTIONS","NAME"]);const a=Qt(s,r[2]),o={attrs:t,bitrate:t.decimalInteger("BANDWIDTH")||t.decimalInteger("AVERAGE-BANDWIDTH"),name:t.NAME,url:pe.resolve(a,e)},l=t.decimalResolution("RESOLUTION");l&&(o.width=l.width,o.height=l.height),Ee(t.CODECS,o),null!=(n=o.unknownCodecs)&&n.length||i.push(o),s.levels.push(o)}else if(r[3]){const t=r[3],i=r[4];switch(t){case"SESSION-DATA":{const t=new k(i);zt(s,t,["DATA-ID","LANGUAGE","VALUE","URI"]);const e=t["DATA-ID"];e&&(null===s.sessionData&&(s.sessionData={}),s.sessionData[e]=t);break}case"SESSION-KEY":{const t=ve(i,e,s);t.encrypted&&t.isSupported()?(null===s.sessionKeys&&(s.sessionKeys=[]),s.sessionKeys.push(t)):A.warn(`[Keys] Ignoring invalid EXT-X-SESSION-KEY tag: "${i}"`);break}case"DEFINE":{const t=new k(i);zt(s,t,["NAME","VALUE","QUERYPARAM"]),Jt(s,t,e)}break;case"CONTENT-STEERING":{const t=new k(i);zt(s,t,["SERVER-URI","PATHWAY-ID"]),s.contentSteering={uri:pe.resolve(t["SERVER-URI"],e),pathwayId:t["PATHWAY-ID"]||"."};break}case"START":s.startTimeOffset=ye(i)}}const a=i.length>0&&i.length<s.levels.length;return s.levels=a?i:s.levels,0===s.levels.length&&(s.playlistParsingError=new Error("no levels found in manifest")),s}static parseMasterPlaylistMedia(t,e,s){let i;const r={},n=s.levels,a={AUDIO:n.map((t=>({id:t.attrs.AUDIO,audioCodec:t.audioCodec}))),SUBTITLES:n.map((t=>({id:t.attrs.SUBTITLES,textCodec:t.textCodec}))),"CLOSED-CAPTIONS":[]};let o=0;for(ue.lastIndex=0;null!==(i=ue.exec(t));){const t=new k(i[1]),n=t.TYPE;if(n){const i=a[n],l=r[n]||[];r[n]=l,zt(s,t,["URI","GROUP-ID","LANGUAGE","ASSOC-LANGUAGE","STABLE-RENDITION-ID","NAME","INSTREAM-ID","CHARACTERISTICS","CHANNELS"]);const h=t.LANGUAGE,d=t["ASSOC-LANGUAGE"],c=t.CHANNELS,u=t.CHARACTERISTICS,f=t["INSTREAM-ID"],g={attrs:t,bitrate:0,id:o++,groupId:t["GROUP-ID"]||"",name:t.NAME||h||"",type:n,default:t.bool("DEFAULT"),autoselect:t.bool("AUTOSELECT"),forced:t.bool("FORCED"),lang:h,url:t.URI?pe.resolve(t.URI,e):""};if(d&&(g.assocLang=d),c&&(g.channels=c),u&&(g.characteristics=u),f&&(g.instreamId=f),null!=i&&i.length){const t=pe.findGroup(i,g.groupId)||i[0];Te(g,t,"audioCodec"),Te(g,t,"textCodec")}l.push(g)}}return r}static parseLevelPlaylist(t,e,s,i,r,n){const a=new O(e),o=a.fragments;let l,h,d,c=null,g=0,m=0,p=0,v=0,y=null,E=new M(i,e),T=-1,S=!1,L=null;for(ge.lastIndex=0,a.m3u8=t,a.hasVariableRefs=Xt(t);null!==(l=ge.exec(t));){S&&(S=!1,E=new M(i,e),E.start=p,E.sn=g,E.cc=v,E.level=s,c&&(E.initSegment=c,E.rawProgramDateTime=c.rawProgramDateTime,c.rawProgramDateTime=null,L&&(E.setByteRange(L),L=null)));const t=l[1];if(t){E.duration=parseFloat(t);const e=(" "+l[2]).slice(1);E.title=e||null,E.tagList.push(e?["INF",t,e]:["INF",t])}else if(l[3]){if(f(E.duration)){E.start=p,d&&Ae(E,d,a),E.sn=g,E.level=s,E.cc=v,o.push(E);const t=(" "+l[3]).slice(1);E.relurl=Qt(a,t),Se(E,y),y=E,p+=E.duration,g++,m=0,S=!0}}else if(l[4]){const t=(" "+l[4]).slice(1);y?E.setByteRange(t,y):E.setByteRange(t)}else if(l[5])E.rawProgramDateTime=(" "+l[5]).slice(1),E.tagList.push(["PROGRAM-DATE-TIME",E.rawProgramDateTime]),-1===T&&(T=o.length);else{if(l=l[0].match(me),!l){A.warn("No matches on slow regex match for level playlist!");continue}for(h=1;h<l.length&&void 0===l[h];h++);const t=(" "+l[h]).slice(1),r=(" "+l[h+1]).slice(1),p=l[h+2]?(" "+l[h+2]).slice(1):"";switch(t){case"PLAYLIST-TYPE":a.type=r.toUpperCase();break;case"MEDIA-SEQUENCE":g=a.startSN=parseInt(r);break;case"SKIP":{const t=new k(r);zt(a,t,["RECENTLY-REMOVED-DATERANGES"]);const e=t.decimalInteger("SKIPPED-SEGMENTS");if(f(e)){a.skippedSegments=e;for(let t=e;t--;)o.unshift(null);g+=e}const s=t.enumeratedString("RECENTLY-REMOVED-DATERANGES");s&&(a.recentlyRemovedDateranges=s.split("\t"));break}case"TARGETDURATION":a.targetduration=Math.max(parseInt(r),1);break;case"VERSION":a.version=parseInt(r);break;case"INDEPENDENT-SEGMENTS":case"EXTM3U":break;case"ENDLIST":a.live=!1;break;case"#":(r||p)&&E.tagList.push(p?[r,p]:[r]);break;case"DISCONTINUITY":v++,E.tagList.push(["DIS"]);break;case"GAP":E.gap=!0,E.tagList.push([t]);break;case"BITRATE":E.tagList.push([t,r]);break;case"DATERANGE":{const t=new k(r);zt(a,t,["ID","CLASS","START-DATE","END-DATE","SCTE35-CMD","SCTE35-OUT","SCTE35-IN"]),zt(a,t,t.clientAttrs);const e=new D(t,a.dateRanges[t.ID]);e.isValid||a.skippedSegments?a.dateRanges[e.id]=e:A.warn(`Ignoring invalid DATERANGE tag: "${r}"`),E.tagList.push(["EXT-X-DATERANGE",r]);break}case"DEFINE":{const t=new k(r);zt(a,t,["NAME","VALUE","IMPORT","QUERYPARAM"]),"IMPORT"in t?Zt(a,t,n):Jt(a,t,e)}break;case"DISCONTINUITY-SEQUENCE":v=parseInt(r);break;case"KEY":{const t=ve(r,e,a);if(t.isSupported()){if("NONE"===t.method){d=void 0;break}d||(d={}),d[t.keyFormat]&&(d=u({},d)),d[t.keyFormat]=t}else A.warn(`[Keys] Ignoring invalid EXT-X-KEY tag: "${r}"`);break}case"START":a.startTimeOffset=ye(r);break;case"MAP":{const t=new k(r);if(zt(a,t,["BYTERANGE","URI"]),E.duration){const r=new M(i,e);Le(r,t,s,d),c=r,E.initSegment=c,c.rawProgramDateTime&&!E.rawProgramDateTime&&(E.rawProgramDateTime=c.rawProgramDateTime)}else{const e=E.byteRangeEndOffset;if(e){const t=E.byteRangeStartOffset;L=`${e-t}@${t}`}else L=null;Le(E,t,s,d),c=E,S=!0}break}case"SERVER-CONTROL":{const t=new k(r);a.canBlockReload=t.bool("CAN-BLOCK-RELOAD"),a.canSkipUntil=t.optionalFloat("CAN-SKIP-UNTIL",0),a.canSkipDateRanges=a.canSkipUntil>0&&t.bool("CAN-SKIP-DATERANGES"),a.partHoldBack=t.optionalFloat("PART-HOLD-BACK",0),a.holdBack=t.optionalFloat("HOLD-BACK",0);break}case"PART-INF":{const t=new k(r);a.partTarget=t.decimalFloatingPoint("PART-TARGET");break}case"PART":{let t=a.partList;t||(t=a.partList=[]);const s=m>0?t[t.length-1]:void 0,i=m++,n=new k(r);zt(a,n,["BYTERANGE","URI"]);const o=new F(n,E,e,i,s);t.push(o),E.duration+=o.duration;break}case"PRELOAD-HINT":{const t=new k(r);zt(a,t,["URI"]),a.preloadHint=t;break}case"RENDITION-REPORT":{const t=new k(r);zt(a,t,["URI"]),a.renditionReports=a.renditionReports||[],a.renditionReports.push(t);break}default:A.warn(`line parsed but not handled: ${l}`)}}}y&&!y.relurl?(o.pop(),p-=y.duration,a.partList&&(a.fragmentHint=y)):a.partList&&(Se(E,y),E.cc=v,a.fragmentHint=E,d&&Ae(E,d,a));const R=o.length,b=o[0],w=o[R-1];if(p+=a.skippedSegments*a.targetduration,p>0&&R&&w){a.averagetargetduration=p/R;const t=w.sn;a.endSN="initSegment"!==t?t:0,a.live||(w.endList=!0),b&&(a.startCC=b.cc)}else a.endSN=0,a.startCC=0;return a.fragmentHint&&(p+=a.fragmentHint.duration),a.totalduration=p,a.endCC=v,T>0&&function(t,e){let s=t[e];for(let i=e;i--;){const e=t[i];if(!e)return;e.programDateTime=s.programDateTime-1e3*e.duration,s=e}}(o,T),a}}function ve(t,e,s){var i,r;const n=new k(t);zt(s,n,["KEYFORMAT","KEYFORMATVERSIONS","URI","IV","URI"]);const a=null!=(i=n.METHOD)?i:"",o=n.URI,l=n.hexadecimalInteger("IV"),h=n.KEYFORMATVERSIONS,d=null!=(r=n.KEYFORMAT)?r:"identity";o&&n.IV&&!l&&A.error(`Invalid IV: ${n.IV}`);const c=o?pe.resolve(o,e):"",u=(h||"1").split("/").map(Number).filter(Number.isFinite);return new jt(a,c,d,u,l)}function ye(t){const e=new k(t).decimalFloatingPoint("TIME-OFFSET");return f(e)?e:null}function Ee(t,e){let s=(t||"").split(/[ ,]+/).filter((t=>t));["video","audio","text"].forEach((t=>{const i=s.filter((e=>function(t,e){const s=ee[e];return!!s&&!!s[t.slice(0,4)]}(e,t)));i.length&&(e[`${t}Codec`]=i.join(","),s=s.filter((t=>-1===i.indexOf(t))))})),e.unknownCodecs=s}function Te(t,e,s){const i=e[s];i&&(t[s]=i)}function Se(t,e){t.rawProgramDateTime?t.programDateTime=Date.parse(t.rawProgramDateTime):null!=e&&e.programDateTime&&(t.programDateTime=e.endProgramDateTime),f(t.programDateTime)||(t.programDateTime=null,t.rawProgramDateTime=null)}function Le(t,e,s,i){t.relurl=e.URI,e.BYTERANGE&&t.setByteRange(e.BYTERANGE),t.level=s,t.sn="initSegment",i&&(t.levelkeys=i),t.initSegment=null}function Ae(t,e,s){t.levelkeys=e;const{encryptedFragments:i}=s;i.length&&i[i.length-1].levelkeys===e||!Object.keys(e).some((t=>e[t].isCommonEncryption))||i.push(t)}var Re="manifest",be="level",ke="audioTrack",we="subtitleTrack",De="main",Ie="audio",Ce="subtitle";function _e(t){const{type:e}=t;switch(e){case ke:return Ie;case we:return Ce;default:return De}}function xe(t,e){let s=t.url;return void 0!==s&&0!==s.indexOf("data:")||(s=e.url),s}class Pe{constructor(t){this.hls=void 0,this.loaders=Object.create(null),this.variableList=null,this.hls=t,this.registerListeners()}startLoad(t){}stopLoad(){this.destroyInternalLoaders()}registerListeners(){const{hls:t}=this;t.on(p.MANIFEST_LOADING,this.onManifestLoading,this),t.on(p.LEVEL_LOADING,this.onLevelLoading,this),t.on(p.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),t.on(p.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this)}unregisterListeners(){const{hls:t}=this;t.off(p.MANIFEST_LOADING,this.onManifestLoading,this),t.off(p.LEVEL_LOADING,this.onLevelLoading,this),t.off(p.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),t.off(p.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this)}createInternalLoader(t){const e=this.hls.config,s=e.pLoader,i=e.loader,r=new(s||i)(e);return this.loaders[t.type]=r,r}getInternalLoader(t){return this.loaders[t.type]}resetInternalLoader(t){this.loaders[t]&&delete this.loaders[t]}destroyInternalLoaders(){for(const t in this.loaders){const e=this.loaders[t];e&&e.destroy(),this.resetInternalLoader(t)}}destroy(){this.variableList=null,this.unregisterListeners(),this.destroyInternalLoaders()}onManifestLoading(t,e){const{url:s}=e;this.variableList=null,this.load({id:null,level:0,responseType:"text",type:Re,url:s,deliveryDirectives:null})}onLevelLoading(t,e){const{id:s,level:i,pathwayId:r,url:n,deliveryDirectives:a}=e;this.load({id:s,level:i,pathwayId:r,responseType:"text",type:be,url:n,deliveryDirectives:a})}onAudioTrackLoading(t,e){const{id:s,groupId:i,url:r,deliveryDirectives:n}=e;this.load({id:s,groupId:i,level:null,responseType:"text",type:ke,url:r,deliveryDirectives:n})}onSubtitleTrackLoading(t,e){const{id:s,groupId:i,url:r,deliveryDirectives:n}=e;this.load({id:s,groupId:i,level:null,responseType:"text",type:we,url:r,deliveryDirectives:n})}load(t){var e;const s=this.hls.config;let i,r=this.getInternalLoader(t);if(r){const e=r.context;if(e&&e.url===t.url&&e.level===t.level)return void A.trace("[playlist-loader]: playlist request ongoing");A.log(`[playlist-loader]: aborting previous loader for type: ${t.type}`),r.abort()}if(i=t.type===Re?s.manifestLoadPolicy.default:u({},s.playlistLoadPolicy.default,{timeoutRetry:null,errorRetry:null}),r=this.createInternalLoader(t),f(null==(e=t.deliveryDirectives)?void 0:e.part)){let e;if(t.type===be&&null!==t.level?e=this.hls.levels[t.level].details:t.type===ke&&null!==t.id?e=this.hls.audioTracks[t.id].details:t.type===we&&null!==t.id&&(e=this.hls.subtitleTracks[t.id].details),e){const t=e.partTarget,s=e.targetduration;if(t&&s){const e=1e3*Math.max(3*t,.8*s);i=u({},i,{maxTimeToFirstByteMs:Math.min(e,i.maxTimeToFirstByteMs),maxLoadTimeMs:Math.min(e,i.maxTimeToFirstByteMs)})}}}const n=i.errorRetry||i.timeoutRetry||{},a={loadPolicy:i,timeout:i.maxLoadTimeMs,maxRetry:n.maxNumRetry||0,retryDelay:n.retryDelayMs||0,maxRetryDelay:n.maxRetryDelayMs||0},o={onSuccess:(t,e,s,i)=>{const r=this.getInternalLoader(s);this.resetInternalLoader(s.type);const n=t.data;0===n.indexOf("#EXTM3U")?(e.parsing.start=performance.now(),pe.isMediaPlaylist(n)?this.handleTrackOrLevelPlaylist(t,e,s,i||null,r):this.handleMasterPlaylist(t,e,s,i)):this.handleManifestParsingError(t,s,new Error("no EXTM3U delimiter"),i||null,e)},onError:(t,e,s,i)=>{this.handleNetworkError(e,s,!1,t,i)},onTimeout:(t,e,s)=>{this.handleNetworkError(e,s,!0,void 0,t)}};r.load(t,a,o)}handleMasterPlaylist(t,e,s,i){const r=this.hls,n=t.data,a=xe(t,s),o=pe.parseMasterPlaylist(n,a);if(o.playlistParsingError)return void this.handleManifestParsingError(t,s,o.playlistParsingError,i,e);const{contentSteering:l,levels:h,sessionData:d,sessionKeys:c,startTimeOffset:u,variableList:f}=o;this.variableList=f;const{AUDIO:g=[],SUBTITLES:m,"CLOSED-CAPTIONS":v}=pe.parseMasterPlaylistMedia(n,a,o);if(g.length){g.some((t=>!t.url))||!h[0].audioCodec||h[0].attrs.AUDIO||(A.log("[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one"),g.unshift({type:"main",name:"main",groupId:"main",default:!1,autoselect:!1,forced:!1,id:-1,attrs:new k({}),bitrate:0,url:""}))}r.trigger(p.MANIFEST_LOADED,{levels:h,audioTracks:g,subtitles:m,captions:v,contentSteering:l,url:a,stats:e,networkDetails:i,sessionData:d,sessionKeys:c,startTimeOffset:u,variableList:f})}handleTrackOrLevelPlaylist(t,e,s,i,r){const n=this.hls,{id:a,level:o,type:l}=s,h=xe(t,s),d=f(o)?o:f(a)?a:0,c=_e(s),u=pe.parseLevelPlaylist(t.data,h,d,c,0,this.variableList);if(l===Re){const t={attrs:new k({}),bitrate:0,details:u,name:"",url:h};n.trigger(p.MANIFEST_LOADED,{levels:[t],audioTracks:[],url:h,stats:e,networkDetails:i,sessionData:null,sessionKeys:null,contentSteering:null,startTimeOffset:null,variableList:null})}e.parsing.end=performance.now(),s.levelDetails=u,this.handlePlaylistLoaded(u,t,e,s,i,r)}handleManifestParsingError(t,e,s,i,r){this.hls.trigger(p.ERROR,{type:v.NETWORK_ERROR,details:y.MANIFEST_PARSING_ERROR,fatal:e.type===Re,url:t.url,err:s,error:s,reason:s.message,response:t,context:e,networkDetails:i,stats:r})}handleNetworkError(t,e,s=!1,i,r){let n=`A network ${s?"timeout":"error"+(i?" (status "+i.code+")":"")} occurred while loading ${t.type}`;t.type===be?n+=`: ${t.level} id: ${t.id}`:t.type!==ke&&t.type!==we||(n+=` id: ${t.id} group-id: "${t.groupId}"`);const a=new Error(n);A.warn(`[playlist-loader]: ${n}`);let o=y.UNKNOWN,l=!1;const d=this.getInternalLoader(t);switch(t.type){case Re:o=s?y.MANIFEST_LOAD_TIMEOUT:y.MANIFEST_LOAD_ERROR,l=!0;break;case be:o=s?y.LEVEL_LOAD_TIMEOUT:y.LEVEL_LOAD_ERROR,l=!1;break;case ke:o=s?y.AUDIO_TRACK_LOAD_TIMEOUT:y.AUDIO_TRACK_LOAD_ERROR,l=!1;break;case we:o=s?y.SUBTITLE_TRACK_LOAD_TIMEOUT:y.SUBTITLE_LOAD_ERROR,l=!1}d&&this.resetInternalLoader(t.type);const c={type:v.NETWORK_ERROR,details:o,fatal:l,url:t.url,loader:d,context:t,error:a,networkDetails:e,stats:r};if(i){const s=(null==e?void 0:e.url)||t.url;c.response=h({url:s,data:void 0},i)}this.hls.trigger(p.ERROR,c)}handlePlaylistLoaded(t,e,s,i,r,n){const a=this.hls,{type:o,level:l,id:h,groupId:d,deliveryDirectives:c}=i,u=xe(e,i),f=_e(i),g="number"==typeof i.level&&f===De?l:void 0;if(!t.fragments.length){const t=new Error("No Segments found in Playlist");return void a.trigger(p.ERROR,{type:v.NETWORK_ERROR,details:y.LEVEL_EMPTY_ERROR,fatal:!1,url:u,error:t,reason:t.message,response:e,context:i,level:g,parent:f,networkDetails:r,stats:s})}t.targetduration||(t.playlistParsingError=new Error("Missing Target Duration"));const m=t.playlistParsingError;if(m)a.trigger(p.ERROR,{type:v.NETWORK_ERROR,details:y.LEVEL_PARSING_ERROR,fatal:!1,url:u,error:m,reason:m.message,response:e,context:i,level:g,parent:f,networkDetails:r,stats:s});else switch(t.live&&n&&(n.getCacheAge&&(t.ageHeader=n.getCacheAge()||0),n.getCacheAge&&!isNaN(t.ageHeader)||(t.ageHeader=0)),o){case Re:case be:a.trigger(p.LEVEL_LOADED,{details:t,level:g||0,id:h||0,stats:s,networkDetails:r,deliveryDirectives:c});break;case ke:a.trigger(p.AUDIO_TRACK_LOADED,{details:t,id:h||0,groupId:d||"",stats:s,networkDetails:r,deliveryDirectives:c});break;case we:a.trigger(p.SUBTITLE_TRACK_LOADED,{details:t,id:h||0,groupId:d||"",stats:s,networkDetails:r,deliveryDirectives:c})}}}function Me(t,e){let s;try{s=new Event("addtrack")}catch(t){s=document.createEvent("Event"),s.initEvent("addtrack",!1,!1)}s.track=t,e.dispatchEvent(s)}function Fe(t,e){const s=t.mode;if("disabled"===s&&(t.mode="hidden"),t.cues&&!t.cues.getCueById(e.id))try{if(t.addCue(e),!t.cues.getCueById(e.id))throw new Error(`addCue is failed for: ${e}`)}catch(s){A.debug(`[texttrack-utils]: ${s}`);try{const s=new self.TextTrackCue(e.startTime,e.endTime,e.text);s.id=e.id,t.addCue(s)}catch(t){A.debug(`[texttrack-utils]: Legacy TextTrackCue fallback failed: ${t}`)}}"disabled"===s&&(t.mode=s)}function Oe(t){const e=t.mode;if("disabled"===e&&(t.mode="hidden"),t.cues)for(let e=t.cues.length;e--;)t.removeCue(t.cues[e]);"disabled"===e&&(t.mode=e)}function Ne(t,e,s,i){const r=t.mode;if("disabled"===r&&(t.mode="hidden"),t.cues&&t.cues.length>0){const r=function(t,e,s){const i=[],r=function(t,e){if(e<t[0].startTime)return 0;const s=t.length-1;if(e>t[s].endTime)return-1;let i=0,r=s;for(;i<=r;){const n=Math.floor((r+i)/2);if(e<t[n].startTime)r=n-1;else{if(!(e>t[n].startTime&&i<s))return n;i=n+1}}return t[i].startTime-e<e-t[r].startTime?i:r}(t,e);if(r>-1)for(let n=r,a=t.length;n<a;n++){const r=t[n];if(r.startTime>=e&&r.endTime<=s)i.push(r);else if(r.startTime>s)return i}return i}(t.cues,e,s);for(let e=0;e<r.length;e++)i&&!i(r[e])||t.removeCue(r[e])}"disabled"===r&&(t.mode=r)}function Ue(t){const e=[];for(let s=0;s<t.length;s++){const i=t[s];"subtitles"!==i.kind&&"captions"!==i.kind||!i.label||e.push(t[s])}return e}var Be="org.id3",$e="com.apple.quicktime.HLS",Ge="https://aomedia.org/emsg/ID3";function Ke(){if("undefined"!=typeof self)return self.VTTCue||self.TextTrackCue}function He(t,e,s,i,r){let n=new t(e,s,"");try{n.value=i,r&&(n.type=r)}catch(a){n=new t(e,s,JSON.stringify(r?h({type:r},i):i))}return n}const Ve=(()=>{const t=Ke();try{t&&new t(0,Number.POSITIVE_INFINITY,"")}catch(t){return Number.MAX_VALUE}return Number.POSITIVE_INFINITY})();function Ye(t,e){return t.getTime()/1e3-e}class We{constructor(t){this.hls=void 0,this.id3Track=null,this.media=null,this.dateRangeCuesAppended={},this.hls=t,this._registerListeners()}destroy(){this._unregisterListeners(),this.id3Track=null,this.media=null,this.dateRangeCuesAppended={},this.hls=null}_registerListeners(){const{hls:t}=this;t.on(p.MEDIA_ATTACHED,this.onMediaAttached,this),t.on(p.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(p.MANIFEST_LOADING,this.onManifestLoading,this),t.on(p.FRAG_PARSING_METADATA,this.onFragParsingMetadata,this),t.on(p.BUFFER_FLUSHING,this.onBufferFlushing,this),t.on(p.LEVEL_UPDATED,this.onLevelUpdated,this)}_unregisterListeners(){const{hls:t}=this;t.off(p.MEDIA_ATTACHED,this.onMediaAttached,this),t.off(p.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(p.MANIFEST_LOADING,this.onManifestLoading,this),t.off(p.FRAG_PARSING_METADATA,this.onFragParsingMetadata,this),t.off(p.BUFFER_FLUSHING,this.onBufferFlushing,this),t.off(p.LEVEL_UPDATED,this.onLevelUpdated,this)}onMediaAttached(t,e){this.media=e.media}onMediaDetaching(){this.id3Track&&(Oe(this.id3Track),this.id3Track=null,this.media=null,this.dateRangeCuesAppended={})}onManifestLoading(){this.dateRangeCuesAppended={}}createTrack(t){const e=this.getID3Track(t.textTracks);return e.mode="hidden",e}getID3Track(t){if(this.media){for(let e=0;e<t.length;e++){const s=t[e];if("metadata"===s.kind&&"id3"===s.label)return Me(s,this.media),s}return this.media.addTextTrack("metadata","id3")}}onFragParsingMetadata(t,e){if(!this.media)return;const{hls:{config:{enableEmsgMetadataCues:s,enableID3MetadataCues:i}}}=this;if(!s&&!i)return;const{samples:r}=e;this.id3Track||(this.id3Track=this.createTrack(this.media));const n=Ke();if(n)for(let t=0;t<r.length;t++){const e=r[t].type;if(e===Ge&&!s||!i)continue;const a=ct(r[t].data);if(a){const s=r[t].pts;let i=s+r[t].duration;i>Ve&&(i=Ve);i-s<=0&&(i=s+.25);for(let t=0;t<a.length;t++){const r=a[t];if(!ht(r)){this.updateId3CueEnds(s,e);const t=He(n,s,i,r,e);t&&this.id3Track.addCue(t)}}}}}updateId3CueEnds(t,e){var s;const i=null==(s=this.id3Track)?void 0:s.cues;if(i)for(let s=i.length;s--;){const r=i[s];r.type===e&&r.startTime<t&&r.endTime===Ve&&(r.endTime=t)}}onBufferFlushing(t,{startOffset:e,endOffset:s,type:i}){const{id3Track:r,hls:n}=this;if(!n)return;const{config:{enableEmsgMetadataCues:a,enableID3MetadataCues:o}}=n;if(r&&(a||o)){let t;t="audio"===i?t=>t.type===Be&&o:"video"===i?t=>t.type===Ge&&a:t=>t.type===Be&&o||t.type===Ge&&a,Ne(r,e,s,t)}}onLevelUpdated(t,{details:e}){if(!this.media||!e.hasProgramDateTime||!this.hls.config.enableDateRangeMetadataCues)return;const{dateRangeCuesAppended:s,id3Track:i}=this,{dateRanges:r}=e,n=Object.keys(r);if(i){const t=Object.keys(s).filter((t=>!n.includes(t)));for(let e=t.length;e--;){const r=t[e];Object.keys(s[r].cues).forEach((t=>{i.removeCue(s[r].cues[t])})),delete s[r]}}const a=e.fragments[e.fragments.length-1];if(0===n.length||!f(null==a?void 0:a.programDateTime))return;this.id3Track||(this.id3Track=this.createTrack(this.media));const o=a.programDateTime/1e3-a.start,l=Ke();for(let t=0;t<n.length;t++){const e=n[t],i=r[e],a=Ye(i.startDate,o),c=s[e],u=(null==c?void 0:c.cues)||{};let f=(null==c?void 0:c.durationKnown)||!1,g=Ve;const m=i.endDate;if(m)g=Ye(m,o),f=!0;else if(i.endOnNext&&!f){const t=n.reduce(((t,e)=>{if(e!==i.id){const s=r[e];if(s.class===i.class&&s.startDate>i.startDate&&(!t||i.startDate<t.startDate))return s}return t}),null);t&&(g=Ye(t.startDate,o),f=!0)}const p=Object.keys(i.attr);for(let t=0;t<p.length;t++){const s=p[t];if("ID"===(d=s)||"CLASS"===d||"START-DATE"===d||"DURATION"===d||"END-DATE"===d||"END-ON-NEXT"===d)continue;const r=u[s];if(r)f&&!c.durationKnown&&(r.endTime=g);else if(l){let t=i.attr[s];w(s)&&(h=t,t=Uint8Array.from(h.replace(/^0x/,"").replace(/([\da-fA-F]{2}) ?/g,"0x$1 ").replace(/ +$/,"").split(" ")).buffer);const r=He(l,a,g,{key:s,data:t},$e);r&&(r.id=e,this.id3Track.addCue(r),u[s]=r)}}s[e]={cues:u,dateRange:i,durationKnown:f}}var h,d}}class je{constructor(t){this.hls=void 0,this.config=void 0,this.media=null,this.levelDetails=null,this.currentTime=0,this.stallCount=0,this._latency=null,this.timeupdateHandler=()=>this.timeupdate(),this.hls=t,this.config=t.config,this.registerListeners()}get latency(){return this._latency||0}get maxLatency(){const{config:t,levelDetails:e}=this;return void 0!==t.liveMaxLatencyDuration?t.liveMaxLatencyDuration:e?t.liveMaxLatencyDurationCount*e.targetduration:0}get targetLatency(){const{levelDetails:t}=this;if(null===t)return null;const{holdBack:e,partHoldBack:s,targetduration:i}=t,{liveSyncDuration:r,liveSyncDurationCount:n,lowLatencyMode:a}=this.config,o=this.hls.userConfig;let l=a&&s||e;(o.liveSyncDuration||o.liveSyncDurationCount||0===l)&&(l=void 0!==r?r:n*i);const h=i;return l+Math.min(1*this.stallCount,h)}get liveSyncPosition(){const t=this.estimateLiveEdge(),e=this.targetLatency,s=this.levelDetails;if(null===t||null===e||null===s)return null;const i=s.edge,r=t-e-this.edgeStalled,n=i-s.totalduration,a=i-(this.config.lowLatencyMode&&s.partTarget||s.targetduration);return Math.min(Math.max(n,r),a)}get drift(){const{levelDetails:t}=this;return null===t?1:t.drift}get edgeStalled(){const{levelDetails:t}=this;if(null===t)return 0;const e=3*(this.config.lowLatencyMode&&t.partTarget||t.targetduration);return Math.max(t.age-e,0)}get forwardBufferLength(){const{media:t,levelDetails:e}=this;if(!t||!e)return 0;const s=t.buffered.length;return(s?t.buffered.end(s-1):e.edge)-this.currentTime}destroy(){this.unregisterListeners(),this.onMediaDetaching(),this.levelDetails=null,this.hls=this.timeupdateHandler=null}registerListeners(){this.hls.on(p.MEDIA_ATTACHED,this.onMediaAttached,this),this.hls.on(p.MEDIA_DETACHING,this.onMediaDetaching,this),this.hls.on(p.MANIFEST_LOADING,this.onManifestLoading,this),this.hls.on(p.LEVEL_UPDATED,this.onLevelUpdated,this),this.hls.on(p.ERROR,this.onError,this)}unregisterListeners(){this.hls.off(p.MEDIA_ATTACHED,this.onMediaAttached,this),this.hls.off(p.MEDIA_DETACHING,this.onMediaDetaching,this),this.hls.off(p.MANIFEST_LOADING,this.onManifestLoading,this),this.hls.off(p.LEVEL_UPDATED,this.onLevelUpdated,this),this.hls.off(p.ERROR,this.onError,this)}onMediaAttached(t,e){this.media=e.media,this.media.addEventListener("timeupdate",this.timeupdateHandler)}onMediaDetaching(){this.media&&(this.media.removeEventListener("timeupdate",this.timeupdateHandler),this.media=null)}onManifestLoading(){this.levelDetails=null,this._latency=null,this.stallCount=0}onLevelUpdated(t,{details:e}){this.levelDetails=e,e.advanced&&this.timeupdate(),!e.live&&this.media&&this.media.removeEventListener("timeupdate",this.timeupdateHandler)}onError(t,e){var s;e.details===y.BUFFER_STALLED_ERROR&&(this.stallCount++,null!=(s=this.levelDetails)&&s.live&&A.warn("[playback-rate-controller]: Stall detected, adjusting target latency"))}timeupdate(){const{media:t,levelDetails:e}=this;if(!t||!e)return;this.currentTime=t.currentTime;const s=this.computeLatency();if(null===s)return;this._latency=s;const{lowLatencyMode:i,maxLiveSyncPlaybackRate:r}=this.config;if(!i||1===r||!e.live)return;const n=this.targetLatency;if(null===n)return;const a=s-n;if(a<Math.min(this.maxLatency,n+e.targetduration)&&a>.05&&this.forwardBufferLength>1){const e=Math.min(2,Math.max(1,r)),s=Math.round(2/(1+Math.exp(-.75*a-this.edgeStalled))*20)/20;t.playbackRate=Math.min(e,Math.max(1,s))}else 1!==t.playbackRate&&0!==t.playbackRate&&(t.playbackRate=1)}estimateLiveEdge(){const{levelDetails:t}=this;return null===t?null:t.edge+t.age}computeLatency(){const t=this.estimateLiveEdge();return null===t?null:t-this.currentTime}}const qe=["NONE","TYPE-0","TYPE-1",null];const Xe=["SDR","PQ","HLG"];var ze="",Qe="YES",Je="v2";function Ze(t){const{canSkipUntil:e,canSkipDateRanges:s,age:i}=t;return e&&i<e/2?s?Je:Qe:ze}class ts{constructor(t,e,s){this.msn=void 0,this.part=void 0,this.skip=void 0,this.msn=t,this.part=e,this.skip=s}addDirectives(t){const e=new self.URL(t);return void 0!==this.msn&&e.searchParams.set("_HLS_msn",this.msn.toString()),void 0!==this.part&&e.searchParams.set("_HLS_part",this.part.toString()),this.skip&&e.searchParams.set("_HLS_skip",this.skip),e.href}}class es{constructor(t){this._attrs=void 0,this.audioCodec=void 0,this.bitrate=void 0,this.codecSet=void 0,this.url=void 0,this.frameRate=void 0,this.height=void 0,this.id=void 0,this.name=void 0,this.videoCodec=void 0,this.width=void 0,this.details=void 0,this.fragmentError=0,this.loadError=0,this.loaded=void 0,this.realBitrate=0,this.supportedPromise=void 0,this.supportedResult=void 0,this._avgBitrate=0,this._audioGroups=void 0,this._subtitleGroups=void 0,this._urlId=0,this.url=[t.url],this._attrs=[t.attrs],this.bitrate=t.bitrate,t.details&&(this.details=t.details),this.id=t.id||0,this.name=t.name,this.width=t.width||0,this.height=t.height||0,this.frameRate=t.attrs.optionalFloat("FRAME-RATE",0),this._avgBitrate=t.attrs.decimalInteger("AVERAGE-BANDWIDTH"),this.audioCodec=t.audioCodec,this.videoCodec=t.videoCodec,this.codecSet=[t.videoCodec,t.audioCodec].filter((t=>!!t)).map((t=>t.substring(0,4))).join(","),this.addGroupId("audio",t.attrs.AUDIO),this.addGroupId("text",t.attrs.SUBTITLES)}get maxBitrate(){return Math.max(this.realBitrate,this.bitrate)}get averageBitrate(){return this._avgBitrate||this.realBitrate||this.bitrate}get attrs(){return this._attrs[0]}get codecs(){return this.attrs.CODECS||""}get pathwayId(){return this.attrs["PATHWAY-ID"]||"."}get videoRange(){return this.attrs["VIDEO-RANGE"]||"SDR"}get score(){return this.attrs.optionalFloat("SCORE",0)}get uri(){return this.url[0]||""}hasAudioGroup(t){return ss(this._audioGroups,t)}hasSubtitleGroup(t){return ss(this._subtitleGroups,t)}get audioGroups(){return this._audioGroups}get subtitleGroups(){return this._subtitleGroups}addGroupId(t,e){if(e)if("audio"===t){let t=this._audioGroups;t||(t=this._audioGroups=[]),-1===t.indexOf(e)&&t.push(e)}else if("text"===t){let t=this._subtitleGroups;t||(t=this._subtitleGroups=[]),-1===t.indexOf(e)&&t.push(e)}}get urlId(){return 0}set urlId(t){}get audioGroupIds(){return this.audioGroups?[this.audioGroupId]:void 0}get textGroupIds(){return this.subtitleGroups?[this.textGroupId]:void 0}get audioGroupId(){var t;return null==(t=this.audioGroups)?void 0:t[0]}get textGroupId(){var t;return null==(t=this.subtitleGroups)?void 0:t[0]}addFallback(){}}function ss(t,e){return!(!e||!t)&&-1!==t.indexOf(e)}function is(t,e){const s=e.startPTS;if(f(s)){let i,r=0;e.sn>t.sn?(r=s-t.start,i=t):(r=t.start-s,i=e),i.duration!==r&&(i.duration=r)}else if(e.sn>t.sn){t.cc===e.cc&&t.minEndPTS?e.start=t.start+(t.minEndPTS-t.start):e.start=t.start+t.duration}else e.start=Math.max(t.start-e.duration,0)}function rs(t,e,s,i,r,n){i-s<=0&&(A.warn("Fragment should have a positive duration",e),i=s+e.duration,n=r+e.duration);let a=s,o=i;const l=e.startPTS,h=e.endPTS;if(f(l)){const t=Math.abs(l-s);f(e.deltaPTS)?e.deltaPTS=Math.max(t,e.deltaPTS):e.deltaPTS=t,a=Math.max(s,l),s=Math.min(s,l),r=Math.min(r,e.startDTS),o=Math.min(i,h),i=Math.max(i,h),n=Math.max(n,e.endDTS)}const d=s-e.start;0!==e.start&&(e.start=s),e.duration=i-e.start,e.startPTS=s,e.maxStartPTS=a,e.startDTS=r,e.endPTS=i,e.minEndPTS=o,e.endDTS=n;const c=e.sn;if(!t||c<t.startSN||c>t.endSN)return 0;let u;const g=c-t.startSN,m=t.fragments;for(m[g]=e,u=g;u>0;u--)is(m[u],m[u-1]);for(u=g;u<m.length-1;u++)is(m[u],m[u+1]);return t.fragmentHint&&is(m[m.length-1],t.fragmentHint),t.PTSKnown=t.alignedSliding=!0,d}function ns(t,e){let s=null;const i=t.fragments;for(let t=i.length-1;t>=0;t--){const e=i[t].initSegment;if(e){s=e;break}}t.fragmentHint&&delete t.fragmentHint.endPTS;let r,n=0;if(function(t,e,s){const i=e.skippedSegments,r=Math.max(t.startSN,e.startSN)-e.startSN,n=(t.fragmentHint?1:0)+(i?e.endSN:Math.min(t.endSN,e.endSN))-e.startSN,a=e.startSN-t.startSN,o=e.fragmentHint?e.fragments.concat(e.fragmentHint):e.fragments,l=t.fragmentHint?t.fragments.concat(t.fragmentHint):t.fragments;for(let t=r;t<=n;t++){const r=l[a+t];let n=o[t];i&&!n&&t<i&&(n=e.fragments[t]=r),r&&n&&s(r,n)}}(t,e,((t,i)=>{t.relurl&&(n=t.cc-i.cc),f(t.startPTS)&&f(t.endPTS)&&(i.start=i.startPTS=t.startPTS,i.startDTS=t.startDTS,i.maxStartPTS=t.maxStartPTS,i.endPTS=t.endPTS,i.endDTS=t.endDTS,i.minEndPTS=t.minEndPTS,i.duration=t.endPTS-t.startPTS,i.duration&&(r=i),e.PTSKnown=e.alignedSliding=!0),i.elementaryStreams=t.elementaryStreams,i.loader=t.loader,i.stats=t.stats,t.initSegment&&(i.initSegment=t.initSegment,s=t.initSegment)})),s){(e.fragmentHint?e.fragments.concat(e.fragmentHint):e.fragments).forEach((t=>{var e;!t||t.initSegment&&t.initSegment.relurl!==(null==(e=s)?void 0:e.relurl)||(t.initSegment=s)}))}if(e.skippedSegments)if(e.deltaUpdateFailed=e.fragments.some((t=>!t)),e.deltaUpdateFailed){A.warn("[level-helper] Previous playlist missing segments skipped in delta playlist");for(let t=e.skippedSegments;t--;)e.fragments.shift();e.startSN=e.fragments[0].sn,e.startCC=e.fragments[0].cc}else e.canSkipDateRanges&&(e.dateRanges=function(t,e,s){const i=u({},t);s&&s.forEach((t=>{delete i[t]}));return Object.keys(e).forEach((t=>{const s=new D(e[t].attr,i[t]);s.isValid?i[t]=s:A.warn(`Ignoring invalid Playlist Delta Update DATERANGE tag: "${JSON.stringify(e[t].attr)}"`)})),i}(t.dateRanges,e.dateRanges,e.recentlyRemovedDateranges));const a=e.fragments;if(n){A.warn("discontinuity sliding from playlist, take drift into account");for(let t=0;t<a.length;t++)a[t].cc+=n}e.skippedSegments&&(e.startCC=e.fragments[0].cc),function(t,e,s){if(t&&e){let i=0;for(let r=0,n=t.length;r<=n;r++){const n=t[r],a=e[r+i];n&&a&&n.index===a.index&&n.fragment.sn===a.fragment.sn?s(n,a):i--}}}(t.partList,e.partList,((t,e)=>{e.elementaryStreams=t.elementaryStreams,e.stats=t.stats})),r?rs(e,r,r.startPTS,r.endPTS,r.startDTS,r.endDTS):as(t,e),a.length&&(e.totalduration=e.edge-a[0].start),e.driftStartTime=t.driftStartTime,e.driftStart=t.driftStart;const o=e.advancedDateTime;if(e.advanced&&o){const t=e.edge;e.driftStart||(e.driftStartTime=o,e.driftStart=t),e.driftEndTime=o,e.driftEnd=t}else e.driftEndTime=t.driftEndTime,e.driftEnd=t.driftEnd,e.advancedDateTime=t.advancedDateTime}function as(t,e){const s=e.startSN+e.skippedSegments-t.startSN,i=t.fragments;s<0||s>=i.length||os(e,i[s].start)}function os(t,e){if(e){const s=t.fragments;for(let i=t.skippedSegments;i<s.length;i++)s[i].start+=e;t.fragmentHint&&(t.fragmentHint.start+=e)}}function ls(t,e,s){var i;return null!=t&&t.details?hs(null==(i=t.details)?void 0:i.partList,e,s):null}function hs(t,e,s){if(t)for(let i=t.length;i--;){const r=t[i];if(r.index===s&&r.fragment.sn===e)return r}return null}function ds(t){t.forEach(((t,e)=>{const{details:s}=t;null!=s&&s.fragments&&s.fragments.forEach((t=>{t.level=e}))}))}function cs(t){switch(t.details){case y.FRAG_LOAD_TIMEOUT:case y.KEY_LOAD_TIMEOUT:case y.LEVEL_LOAD_TIMEOUT:case y.MANIFEST_LOAD_TIMEOUT:return!0}return!1}function us(t,e){const s=cs(e);return t.default[(s?"timeout":"error")+"Retry"]}function fs(t,e){const s="linear"===t.backoff?1:Math.pow(2,e);return Math.min(s*t.retryDelayMs,t.maxRetryDelayMs)}function gs(t){return h(h({},t),{errorRetry:null,timeoutRetry:null})}function ms(t,e,s,i){if(!t)return!1;const r=null==i?void 0:i.code,n=e<t.maxNumRetry&&(function(t){return 0===t&&!1===navigator.onLine||!!t&&(t<400||t>499)}(r)||!!s);return t.shouldRetry?t.shouldRetry(t,e,s,i,n):n}const ps=function(t,e){let s=0,i=t.length-1,r=null,n=null;for(;s<=i;){r=(s+i)/2|0,n=t[r];const a=e(n);if(a>0)s=r+1;else{if(!(a<0))return n;i=r-1}}return null};function vs(t,e,s=0,i=0,r=.005){let n=null;if(t){n=e[t.sn-e[0].sn+1]||null;const i=t.endDTS-s;i>0&&i<15e-7&&(s+=15e-7)}else 0===s&&0===e[0].start&&(n=e[0]);if(n&&((!t||t.level===n.level)&&0===ys(s,i,n)||function(t,e,s){if(e&&0===e.start&&e.level<t.level&&(e.endPTS||0)>0){const i=e.tagList.reduce(((t,e)=>("INF"===e[0]&&(t+=parseFloat(e[1])),t)),s);return t.start<=i}return!1}(n,t,Math.min(r,i))))return n;const a=ps(e,ys.bind(null,s,i));return!a||a===t&&n?n:a}function ys(t=0,e=0,s){if(s.start<=t&&s.start+s.duration>t)return 0;const i=Math.min(e,s.duration+(s.deltaPTS?s.deltaPTS:0));return s.start+s.duration-i<=t?1:s.start-i>t&&s.start?-1:0}function Es(t,e,s){const i=1e3*Math.min(e,s.duration+(s.deltaPTS?s.deltaPTS:0));return(s.endProgramDateTime||0)-i>t}var Ts=0,Ss=2,Ls=3,As=5,Rs=0,bs=1,ks=2;class ws{constructor(t,e){this.hls=void 0,this.timer=-1,this.requestScheduled=-1,this.canLoad=!1,this.log=void 0,this.warn=void 0,this.log=A.log.bind(A,`${e}:`),this.warn=A.warn.bind(A,`${e}:`),this.hls=t}destroy(){this.clearTimer(),this.hls=this.log=this.warn=null}clearTimer(){-1!==this.timer&&(self.clearTimeout(this.timer),this.timer=-1)}startLoad(){this.canLoad=!0,this.requestScheduled=-1,this.loadPlaylist()}stopLoad(){this.canLoad=!1,this.clearTimer()}switchParams(t,e,s){const i=null==e?void 0:e.renditionReports;if(i){let r=-1;for(let s=0;s<i.length;s++){const n=i[s];let a;try{a=new self.URL(n.URI,e.url).href}catch(t){A.warn(`Could not construct new URL for Rendition Report: ${t}`),a=n.URI||""}if(a===t){r=s;break}a===t.substring(0,a.length)&&(r=s)}if(-1!==r){const t=i[r],n=parseInt(t["LAST-MSN"])||(null==e?void 0:e.lastPartSn);let a=parseInt(t["LAST-PART"])||(null==e?void 0:e.lastPartIndex);if(this.hls.config.lowLatencyMode){const t=Math.min(e.age-e.partTarget,e.targetduration);a>=0&&t>e.partTarget&&(a+=1)}const o=s&&Ze(s);return new ts(n,a>=0?a:void 0,o)}}}loadPlaylist(t){-1===this.requestScheduled&&(this.requestScheduled=self.performance.now())}shouldLoadPlaylist(t){return this.canLoad&&!!t&&!!t.url&&(!t.details||t.details.live)}shouldReloadPlaylist(t){return-1===this.timer&&-1===this.requestScheduled&&this.shouldLoadPlaylist(t)}playlistLoaded(t,e,s){const{details:i,stats:r}=e,n=self.performance.now(),a=r.loading.first?Math.max(0,n-r.loading.first):0;if(i.advancedDateTime=Date.now()-a,i.live||null!=s&&s.live){if(i.reloaded(s),s&&this.log(`live playlist ${t} ${i.advanced?"REFRESHED "+i.lastPartSn+"-"+i.lastPartIndex:i.updated?"UPDATED":"MISSED"}`),s&&i.fragments.length>0&&ns(s,i),!this.canLoad||!i.live)return;let a,o,l;if(i.canBlockReload&&i.endSN&&i.advanced){const t=this.hls.config.lowLatencyMode,r=i.lastPartSn,n=i.endSN,h=i.lastPartIndex,d=r===n;-1!==h?(o=d?n+1:r,l=d?t?0:h:h+1):o=n+1;const c=i.age,u=c+i.ageHeader;let f=Math.min(u-i.partTarget,1.5*i.targetduration);if(f>0){if(s&&f>s.tuneInGoal)this.warn(`CDN Tune-in goal increased from: ${s.tuneInGoal} to: ${f} with playlist age: ${i.age}`),f=0;else{const t=Math.floor(f/i.targetduration);if(o+=t,void 0!==l){l+=Math.round(f%i.targetduration/i.partTarget)}this.log(`CDN Tune-in age: ${i.ageHeader}s last advanced ${c.toFixed(2)}s goal: ${f} skip sn ${t} to part ${l}`)}i.tuneInGoal=f}if(a=this.getDeliveryDirectives(i,e.deliveryDirectives,o,l),t||!d)return void this.loadPlaylist(a)}else(i.canBlockReload||i.canSkipUntil)&&(a=this.getDeliveryDirectives(i,e.deliveryDirectives,o,l));const h=this.hls.mainForwardBufferInfo,d=h?h.end-h.len:0,c=function(t,e=1/0){let s=1e3*t.targetduration;if(t.updated){const i=t.fragments,r=4;if(i.length&&s*r>e){const t=1e3*i[i.length-1].duration;t<s&&(s=t)}}else s/=2;return Math.round(s)}(i,1e3*(i.edge-d));i.updated&&n>this.requestScheduled+c&&(this.requestScheduled=r.loading.start),void 0!==o&&i.canBlockReload?this.requestScheduled=r.loading.first+c-(1e3*i.partTarget||1e3):-1===this.requestScheduled||this.requestScheduled+c<n?this.requestScheduled=n:this.requestScheduled-n<=0&&(this.requestScheduled+=c);let u=this.requestScheduled-n;u=Math.max(0,u),this.log(`reload live playlist ${t} in ${Math.round(u)} ms`),this.timer=self.setTimeout((()=>this.loadPlaylist(a)),u)}else this.clearTimer()}getDeliveryDirectives(t,e,s,i){let r=Ze(t);return null!=e&&e.skip&&t.deltaUpdateFailed&&(s=e.msn,i=e.part,r=ze),new ts(s,i,r)}checkRetry(t){const e=t.details,s=cs(t),i=t.errorAction,{action:r,retryCount:n=0,retryConfig:a}=i||{},o=!!i&&!!a&&(r===As||!i.resolved&&r===Ss);if(o){var l;if(this.requestScheduled=-1,n>=a.maxNumRetry)return!1;if(s&&null!=(l=t.context)&&l.deliveryDirectives)this.warn(`Retrying playlist loading ${n+1}/${a.maxNumRetry} after "${e}" without delivery-directives`),this.loadPlaylist();else{const t=fs(a,n);this.timer=self.setTimeout((()=>this.loadPlaylist()),t),this.warn(`Retrying playlist loading ${n+1}/${a.maxNumRetry} after "${e}" in ${t}ms`)}t.levelRetry=!0,i.resolved=!0}return o}}class Ds{constructor(t,e=0,s=0){this.halfLife=void 0,this.alpha_=void 0,this.estimate_=void 0,this.totalWeight_=void 0,this.halfLife=t,this.alpha_=t?Math.exp(Math.log(.5)/t):0,this.estimate_=e,this.totalWeight_=s}sample(t,e){const s=Math.pow(this.alpha_,t);this.estimate_=e*(1-s)+s*this.estimate_,this.totalWeight_+=t}getTotalWeight(){return this.totalWeight_}getEstimate(){if(this.alpha_){const t=1-Math.pow(this.alpha_,this.totalWeight_);if(t)return this.estimate_/t}return this.estimate_}}class Is{constructor(t,e,s,i=100){this.defaultEstimate_=void 0,this.minWeight_=void 0,this.minDelayMs_=void 0,this.slow_=void 0,this.fast_=void 0,this.defaultTTFB_=void 0,this.ttfb_=void 0,this.defaultEstimate_=s,this.minWeight_=.001,this.minDelayMs_=50,this.slow_=new Ds(t),this.fast_=new Ds(e),this.defaultTTFB_=i,this.ttfb_=new Ds(t)}update(t,e){const{slow_:s,fast_:i,ttfb_:r}=this;s.halfLife!==t&&(this.slow_=new Ds(t,s.getEstimate(),s.getTotalWeight())),i.halfLife!==e&&(this.fast_=new Ds(e,i.getEstimate(),i.getTotalWeight())),r.halfLife!==t&&(this.ttfb_=new Ds(t,r.getEstimate(),r.getTotalWeight()))}sample(t,e){const s=(t=Math.max(t,this.minDelayMs_))/1e3,i=8*e/s;this.fast_.sample(s,i),this.slow_.sample(s,i)}sampleTTFB(t){const e=t/1e3,s=Math.sqrt(2)*Math.exp(-Math.pow(e,2)/2);this.ttfb_.sample(s,Math.max(t,5))}canEstimate(){return this.fast_.getTotalWeight()>=this.minWeight_}getEstimate(){return this.canEstimate()?Math.min(this.fast_.getEstimate(),this.slow_.getEstimate()):this.defaultEstimate_}getEstimateTTFB(){return this.ttfb_.getTotalWeight()>=this.minWeight_?this.ttfb_.getEstimate():this.defaultTTFB_}destroy(){}}const Cs={supported:!0,configurations:[],decodingInfoResults:[{supported:!0,powerEfficient:!0,smooth:!0}]},_s={};function xs(t,e,s,i,r,n){const a=t.audioCodec?t.audioGroups:null,o=null==n?void 0:n.audioCodec,l=null==n?void 0:n.channels,h=l?parseInt(l):o?1/0:2;let d=null;if(null!=a&&a.length)try{d=1===a.length&&a[0]?e.groups[a[0]].channels:a.reduce(((t,s)=>{if(s){const i=e.groups[s];if(!i)throw new Error(`Audio track group ${s} not found`);Object.keys(i.channels).forEach((e=>{t[e]=(t[e]||0)+i.channels[e]}))}return t}),{2:0})}catch(t){return!0}return void 0!==t.videoCodec&&(t.width>1920&&t.height>1088||t.height>1920&&t.width>1088||t.frameRate>Math.max(i,30)||"SDR"!==t.videoRange&&t.videoRange!==s||t.bitrate>Math.max(r,8e6))||!!d&&f(h)&&Object.keys(d).some((t=>parseInt(t)>h))}function Ps(t,e,s){const i=t.videoCodec,r=t.audioCodec;if(!i||!r||!s)return Promise.resolve(Cs);const n={width:t.width,height:t.height,bitrate:Math.ceil(Math.max(.9*t.bitrate,t.averageBitrate)),framerate:t.frameRate||30},a=t.videoRange;"SDR"!==a&&(n.transferFunction=a.toLowerCase());const o=i.split(",").map((t=>({type:"media-source",video:h(h({},n),{},{contentType:re(t,"video")})})));return r&&t.audioGroups&&t.audioGroups.forEach((t=>{var s;t&&(null==(s=e.groups[t])||s.tracks.forEach((e=>{if(e.groupId===t){const t=e.channels||"",s=parseFloat(t);f(s)&&s>2&&o.push.apply(o,r.split(",").map((t=>({type:"media-source",audio:{contentType:re(t,"audio"),channels:""+s}}))))}})))})),Promise.all(o.map((t=>{const e=function(t){const{audio:e,video:s}=t,i=s||e;if(i){const t=i.contentType.split('"')[1];if(s)return`r${s.height}x${s.width}f${Math.ceil(s.framerate)}${s.transferFunction||"sd"}_${t}_${Math.ceil(s.bitrate/1e5)}`;if(e)return`c${e.channels}${e.spatialRendering?"s":"n"}_${t}`}return""}(t);return _s[e]||(_s[e]=s.decodingInfo(t))}))).then((t=>({supported:!t.some((t=>!t.supported)),configurations:o,decodingInfoResults:t}))).catch((t=>({supported:!1,configurations:o,decodingInfoResults:[],error:t})))}function Ms(t,e){let s=!1,i=[];return t&&(s="SDR"!==t,i=[t]),e&&(i=e.allowedVideoRanges||Xe.slice(0),s=void 0!==e.preferHDR?e.preferHDR:function(){if("function"==typeof matchMedia){const t=matchMedia("(dynamic-range: high)"),e=matchMedia("bad query");if(t.media!==e.media)return!0===t.matches}return!1}(),i=s?i.filter((t=>"SDR"!==t)):["SDR"]),{preferHDR:s,allowedVideoRanges:i}}function Fs(t,e){A.log(`[abr] start candidates with "${t}" ignored because ${e}`)}function Os(t,e,s){if("attrs"in t){const s=e.indexOf(t);if(-1!==s)return s}for(let i=0;i<e.length;i++){if(Ns(t,e[i],s))return i}return-1}function Ns(t,e,s){const{groupId:i,name:r,lang:n,assocLang:a,characteristics:o,default:l}=t,h=t.forced;return(void 0===i||e.groupId===i)&&(void 0===r||e.name===r)&&(void 0===n||e.lang===n)&&(void 0===n||e.assocLang===a)&&(void 0===l||e.default===l)&&(void 0===h||e.forced===h)&&(void 0===o||function(t,e=""){const s=t.split(","),i=e.split(",");return s.length===i.length&&!s.some((t=>-1===i.indexOf(t)))}(o,e.characteristics))&&(void 0===s||s(t,e))}function Us(t,e){const{audioCodec:s,channels:i}=t;return!(void 0!==s&&(e.audioCodec||"").substring(0,4)!==s.substring(0,4)||void 0!==i&&i!==(e.channels||"2"))}function Bs(t,e,s){for(let i=e;i;i--)if(s(t[i]))return i;for(let i=e+1;i<t.length;i++)if(s(t[i]))return i;return-1}class $s{constructor(){this._boundTick=void 0,this._tickTimer=null,this._tickInterval=null,this._tickCallCount=0,this._boundTick=this.tick.bind(this)}destroy(){this.onHandlerDestroying(),this.onHandlerDestroyed()}onHandlerDestroying(){this.clearNextTick(),this.clearInterval()}onHandlerDestroyed(){}hasInterval(){return!!this._tickInterval}hasNextTick(){return!!this._tickTimer}setInterval(t){return!this._tickInterval&&(this._tickCallCount=0,this._tickInterval=self.setInterval(this._boundTick,t),!0)}clearInterval(){return!!this._tickInterval&&(self.clearInterval(this._tickInterval),this._tickInterval=null,!0)}clearNextTick(){return!!this._tickTimer&&(self.clearTimeout(this._tickTimer),this._tickTimer=null,!0)}tick(){this._tickCallCount++,1===this._tickCallCount&&(this.doTick(),this._tickCallCount>1&&this.tickImmediate(),this._tickCallCount=0)}tickImmediate(){this.clearNextTick(),this._tickTimer=self.setTimeout(this._boundTick,0)}doTick(){}}var Gs="NOT_LOADED",Ks="APPENDING",Hs="PARTIAL",Vs="OK";class Ys{constructor(t){this.activePartLists=Object.create(null),this.endListFragments=Object.create(null),this.fragments=Object.create(null),this.timeRanges=Object.create(null),this.bufferPadding=.2,this.hls=void 0,this.hasGaps=!1,this.hls=t,this._registerListeners()}_registerListeners(){const{hls:t}=this;t.on(p.BUFFER_APPENDED,this.onBufferAppended,this),t.on(p.FRAG_BUFFERED,this.onFragBuffered,this),t.on(p.FRAG_LOADED,this.onFragLoaded,this)}_unregisterListeners(){const{hls:t}=this;t.off(p.BUFFER_APPENDED,this.onBufferAppended,this),t.off(p.FRAG_BUFFERED,this.onFragBuffered,this),t.off(p.FRAG_LOADED,this.onFragLoaded,this)}destroy(){this._unregisterListeners(),this.fragments=this.activePartLists=this.endListFragments=this.timeRanges=null}getAppendedFrag(t,e){const s=this.activePartLists[e];if(s)for(let e=s.length;e--;){const i=s[e];if(!i)break;const r=i.end;if(i.start<=t&&null!==r&&t<=r)return i}return this.getBufferedFrag(t,e)}getBufferedFrag(t,e){const{fragments:s}=this,i=Object.keys(s);for(let r=i.length;r--;){const n=s[i[r]];if((null==n?void 0:n.body.type)===e&&n.buffered){const e=n.body;if(e.start<=t&&t<=e.end)return e}}return null}detectEvictedFragments(t,e,s,i){this.timeRanges&&(this.timeRanges[t]=e);const r=(null==i?void 0:i.fragment.sn)||-1;Object.keys(this.fragments).forEach((i=>{const n=this.fragments[i];if(!n)return;if(r>=n.body.sn)return;if(!n.buffered&&!n.loaded)return void(n.body.type===s&&this.removeFragment(n.body));const a=n.range[t];a&&a.time.some((t=>{const s=!this.isTimeBuffered(t.startPTS,t.endPTS,e);return s&&this.removeFragment(n.body),s}))}))}detectPartialFragments(t){const e=this.timeRanges,{frag:s,part:i}=t;if(!e||"initSegment"===s.sn)return;const r=js(s),n=this.fragments[r];if(!n||n.buffered&&s.gap)return;const a=!s.relurl;if(Object.keys(e).forEach((t=>{const r=s.elementaryStreams[t];if(!r)return;const o=e[t],l=a||!0===r.partial;n.range[t]=this.getBufferedTimes(s,i,l,o)})),n.loaded=null,Object.keys(n.range).length){n.buffered=!0;(n.body.endList=s.endList||n.body.endList)&&(this.endListFragments[n.body.type]=n),Ws(n)||this.removeParts(s.sn-1,s.type)}else this.removeFragment(n.body)}removeParts(t,e){const s=this.activePartLists[e];s&&(this.activePartLists[e]=s.filter((e=>e.fragment.sn>=t)))}fragBuffered(t,e){const s=js(t);let i=this.fragments[s];!i&&e&&(i=this.fragments[s]={body:t,appendedPTS:null,loaded:null,buffered:!1,range:Object.create(null)},t.gap&&(this.hasGaps=!0)),i&&(i.loaded=null,i.buffered=!0)}getBufferedTimes(t,e,s,i){const r={time:[],partial:s},n=t.start,a=t.end,o=t.minEndPTS||a,l=t.maxStartPTS||n;for(let t=0;t<i.length;t++){const e=i.start(t)-this.bufferPadding,s=i.end(t)+this.bufferPadding;if(l>=e&&o<=s){r.time.push({startPTS:Math.max(n,i.start(t)),endPTS:Math.min(a,i.end(t))});break}if(n<s&&a>e){const e=Math.max(n,i.start(t)),s=Math.min(a,i.end(t));s>e&&(r.partial=!0,r.time.push({startPTS:e,endPTS:s}))}else if(a<=e)break}return r}getPartialFragment(t){let e,s,i,r=null,n=0;const{bufferPadding:a,fragments:o}=this;return Object.keys(o).forEach((l=>{const h=o[l];h&&Ws(h)&&(s=h.body.start-a,i=h.body.end+a,t>=s&&t<=i&&(e=Math.min(t-s,i-t),n<=e&&(r=h.body,n=e)))})),r}isEndListAppended(t){const e=this.endListFragments[t];return void 0!==e&&(e.buffered||Ws(e))}getState(t){const e=js(t),s=this.fragments[e];return s?s.buffered?Ws(s)?Hs:Vs:Ks:Gs}isTimeBuffered(t,e,s){let i,r;for(let n=0;n<s.length;n++){if(i=s.start(n)-this.bufferPadding,r=s.end(n)+this.bufferPadding,t>=i&&e<=r)return!0;if(e<=i)return!1}return!1}onFragLoaded(t,e){const{frag:s,part:i}=e;if("initSegment"===s.sn||s.bitrateTest)return;const r=i?null:e,n=js(s);this.fragments[n]={body:s,appendedPTS:null,loaded:r,buffered:!1,range:Object.create(null)}}onBufferAppended(t,e){const{frag:s,part:i,timeRanges:r}=e;if("initSegment"===s.sn)return;const n=s.type;if(i){let t=this.activePartLists[n];t||(this.activePartLists[n]=t=[]),t.push(i)}this.timeRanges=r,Object.keys(r).forEach((t=>{const e=r[t];this.detectEvictedFragments(t,e,n,i)}))}onFragBuffered(t,e){this.detectPartialFragments(e)}hasFragment(t){const e=js(t);return!!this.fragments[e]}hasParts(t){var e;return!(null==(e=this.activePartLists[t])||!e.length)}removeFragmentsInRange(t,e,s,i,r){i&&!this.hasGaps||Object.keys(this.fragments).forEach((n=>{const a=this.fragments[n];if(!a)return;const o=a.body;o.type!==s||i&&!o.gap||o.start<e&&o.end>t&&(a.buffered||r)&&this.removeFragment(o)}))}removeFragment(t){const e=js(t);t.stats.loaded=0,t.clearElementaryStreamInfo();const s=this.activePartLists[t.type];if(s){const e=t.sn;this.activePartLists[t.type]=s.filter((t=>t.fragment.sn!==e))}delete this.fragments[e],t.endList&&delete this.endListFragments[t.type]}removeAllFragments(){this.fragments=Object.create(null),this.endListFragments=Object.create(null),this.activePartLists=Object.create(null),this.hasGaps=!1}}function Ws(t){var e,s,i;return t.buffered&&(t.body.gap||(null==(e=t.range.video)?void 0:e.partial)||(null==(s=t.range.audio)?void 0:s.partial)||(null==(i=t.range.audiovideo)?void 0:i.partial))}function js(t){return`${t.type}_${t.level}_${t.sn}`}const qs={length:0,start:()=>0,end:()=>0};class Xs{static isBuffered(t,e){try{if(t){const s=Xs.getBuffered(t);for(let t=0;t<s.length;t++)if(e>=s.start(t)&&e<=s.end(t))return!0}}catch(t){}return!1}static bufferInfo(t,e,s){try{if(t){const i=Xs.getBuffered(t),r=[];let n;for(n=0;n<i.length;n++)r.push({start:i.start(n),end:i.end(n)});return this.bufferedInfo(r,e,s)}}catch(t){}return{len:0,start:e,end:e,nextStart:void 0}}static bufferedInfo(t,e,s){e=Math.max(0,e),t.sort((function(t,e){const s=t.start-e.start;return s||e.end-t.end}));let i=[];if(s)for(let e=0;e<t.length;e++){const r=i.length;if(r){const n=i[r-1].end;t[e].start-n<s?t[e].end>n&&(i[r-1].end=t[e].end):i.push(t[e])}else i.push(t[e])}else i=t;let r,n=0,a=e,o=e;for(let t=0;t<i.length;t++){const l=i[t].start,h=i[t].end;if(e+s>=l&&e<h)a=l,o=h,n=o-e;else if(e+s<l){r=l;break}}return{len:n,start:a||0,end:o||0,nextStart:r}}static getBuffered(t){try{return t.buffered}catch(t){return A.log("failed to get media.buffered",t),qs}}}class zs{constructor(t,e,s,i=0,r=-1,n=!1){this.level=void 0,this.sn=void 0,this.part=void 0,this.id=void 0,this.size=void 0,this.partial=void 0,this.transmuxing={start:0,executeStart:0,executeEnd:0,end:0},this.buffering={audio:{start:0,executeStart:0,executeEnd:0,end:0},video:{start:0,executeStart:0,executeEnd:0,end:0},audiovideo:{start:0,executeStart:0,executeEnd:0,end:0}},this.level=t,this.sn=e,this.id=s,this.size=i,this.part=r,this.partial=n}}function Qs(t,e){for(let i=0,r=t.length;i<r;i++){var s;if((null==(s=t[i])?void 0:s.cc)===e)return t[i]}return null}function Js(t,e){if(t){const s=t.start+e;t.start=t.startPTS=s,t.endPTS=s+t.duration}}function Zs(t,e){const s=e.fragments;for(let e=0,i=s.length;e<i;e++)Js(s[e],t);e.fragmentHint&&Js(e.fragmentHint,t),e.alignedSliding=!0}function ti(t,e,s){e&&(!function(t,e,s){if(function(t,e,s){return!(!e||!(s.endCC>s.startCC||t&&t.cc<s.startCC))}(t,s,e)){const t=function(t,e){const s=t.fragments,i=e.fragments;if(!i.length||!s.length)return void A.log("No fragments to align");const r=Qs(s,i[0].cc);if(r&&(!r||r.startPTS))return r;A.log("No frag in previous level to align on")}(s,e);t&&f(t.start)&&(A.log(`Adjusting PTS using last level due to CC increase within current level ${e.url}`),Zs(t.start,e))}}(t,s,e),!s.alignedSliding&&e&&ei(s,e),s.alignedSliding||!e||s.skippedSegments||as(e,s))}function ei(t,e){if(!t.hasProgramDateTime||!e.hasProgramDateTime)return;const s=t.fragments,i=e.fragments;if(!s.length||!i.length)return;let r,n;const a=Math.min(e.endCC,t.endCC);e.startCC<a&&t.startCC<a&&(r=Qs(i,a),n=Qs(s,a)),r&&n||(r=i[Math.floor(i.length/2)],n=Qs(s,r.cc)||s[Math.floor(s.length/2)]);const o=r.programDateTime,l=n.programDateTime;if(!o||!l)return;Zs((l-o)/1e3-(n.start-r.start),t)}const si=Math.pow(2,17);class ii{constructor(t){this.config=void 0,this.loader=null,this.partLoadTimeout=-1,this.config=t}destroy(){this.loader&&(this.loader.destroy(),this.loader=null)}abort(){this.loader&&this.loader.abort()}load(t,e){const s=t.url;if(!s)return Promise.reject(new ai({type:v.NETWORK_ERROR,details:y.FRAG_LOAD_ERROR,fatal:!1,frag:t,error:new Error("Fragment does not have a "+(s?"part list":"url")),networkDetails:null}));this.abort();const i=this.config,r=i.fLoader,n=i.loader;return new Promise(((a,o)=>{if(this.loader&&this.loader.destroy(),t.gap){if(t.tagList.some((t=>"GAP"===t[0])))return void o(ni(t));t.gap=!1}const l=this.loader=t.loader=r?new r(i):new n(i),d=ri(t),c=gs(i.fragLoadPolicy.default),u={loadPolicy:c,timeout:c.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0,highWaterMark:"initSegment"===t.sn?1/0:si};t.stats=l.stats,l.load(d,u,{onSuccess:(e,s,i,r)=>{this.resetLoader(t,l);let n=e.data;i.resetIV&&t.decryptdata&&(t.decryptdata.iv=new Uint8Array(n.slice(0,16)),n=n.slice(16)),a({frag:t,part:null,payload:n,networkDetails:r})},onError:(e,i,r,n)=>{this.resetLoader(t,l),o(new ai({type:v.NETWORK_ERROR,details:y.FRAG_LOAD_ERROR,fatal:!1,frag:t,response:h({url:s,data:void 0},e),error:new Error(`HTTP Error ${e.code} ${e.text}`),networkDetails:r,stats:n}))},onAbort:(e,s,i)=>{this.resetLoader(t,l),o(new ai({type:v.NETWORK_ERROR,details:y.INTERNAL_ABORTED,fatal:!1,frag:t,error:new Error("Aborted"),networkDetails:i,stats:e}))},onTimeout:(e,s,i)=>{this.resetLoader(t,l),o(new ai({type:v.NETWORK_ERROR,details:y.FRAG_LOAD_TIMEOUT,fatal:!1,frag:t,error:new Error(`Timeout after ${u.timeout}ms`),networkDetails:i,stats:e}))},onProgress:(s,i,r,n)=>{e&&e({frag:t,part:null,payload:r,networkDetails:n})}})}))}loadPart(t,e,s){this.abort();const i=this.config,r=i.fLoader,n=i.loader;return new Promise(((a,o)=>{if(this.loader&&this.loader.destroy(),t.gap||e.gap)return void o(ni(t,e));const l=this.loader=t.loader=r?new r(i):new n(i),d=ri(t,e),c=gs(i.fragLoadPolicy.default),u={loadPolicy:c,timeout:c.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0,highWaterMark:si};e.stats=l.stats,l.load(d,u,{onSuccess:(i,r,n,o)=>{this.resetLoader(t,l),this.updateStatsFromPart(t,e);const h={frag:t,part:e,payload:i.data,networkDetails:o};s(h),a(h)},onError:(s,i,r,n)=>{this.resetLoader(t,l),o(new ai({type:v.NETWORK_ERROR,details:y.FRAG_LOAD_ERROR,fatal:!1,frag:t,part:e,response:h({url:d.url,data:void 0},s),error:new Error(`HTTP Error ${s.code} ${s.text}`),networkDetails:r,stats:n}))},onAbort:(s,i,r)=>{t.stats.aborted=e.stats.aborted,this.resetLoader(t,l),o(new ai({type:v.NETWORK_ERROR,details:y.INTERNAL_ABORTED,fatal:!1,frag:t,part:e,error:new Error("Aborted"),networkDetails:r,stats:s}))},onTimeout:(s,i,r)=>{this.resetLoader(t,l),o(new ai({type:v.NETWORK_ERROR,details:y.FRAG_LOAD_TIMEOUT,fatal:!1,frag:t,part:e,error:new Error(`Timeout after ${u.timeout}ms`),networkDetails:r,stats:s}))}})}))}updateStatsFromPart(t,e){const s=t.stats,i=e.stats,r=i.total;if(s.loaded+=i.loaded,r){const i=Math.round(t.duration/e.duration),n=Math.min(Math.round(s.loaded/r),i),a=(i-n)*Math.round(s.loaded/n);s.total=s.loaded+a}else s.total=Math.max(s.loaded,s.total);const n=s.loading,a=i.loading;n.start?n.first+=a.first-a.start:(n.start=a.start,n.first=a.first),n.end=a.end}resetLoader(t,e){t.loader=null,this.loader===e&&(self.clearTimeout(this.partLoadTimeout),this.loader=null),e.destroy()}}function ri(t,e=null){const s=e||t,i={frag:t,part:e,responseType:"arraybuffer",url:s.url,headers:{},rangeStart:0,rangeEnd:0},r=s.byteRangeStartOffset,n=s.byteRangeEndOffset;if(f(r)&&f(n)){var a;let e=r,s=n;if("initSegment"===t.sn&&"AES-128"===(null==(a=t.decryptdata)?void 0:a.method)){const t=n-r;t%16&&(s=n+(16-t%16)),0!==r&&(i.resetIV=!0,e=r-16)}i.rangeStart=e,i.rangeEnd=s}return i}function ni(t,e){const s=new Error(`GAP ${t.gap?"tag":"attribute"} found`),i={type:v.MEDIA_ERROR,details:y.FRAG_GAP,fatal:!1,frag:t,error:s,networkDetails:null};return e&&(i.part=e),(e||t).stats.aborted=!0,new ai(i)}class ai extends Error{constructor(t){super(t.error.message),this.data=void 0,this.data=t}}class oi{constructor(t,e){this.subtle=void 0,this.aesIV=void 0,this.subtle=t,this.aesIV=e}decrypt(t,e){return this.subtle.decrypt({name:"AES-CBC",iv:this.aesIV},e,t)}}class li{constructor(t,e){this.subtle=void 0,this.key=void 0,this.subtle=t,this.key=e}expandKey(){return this.subtle.importKey("raw",this.key,{name:"AES-CBC"},!1,["encrypt","decrypt"])}}class hi{constructor(){this.rcon=[0,1,2,4,8,16,32,64,128,27,54],this.subMix=[new Uint32Array(256),new Uint32Array(256),new Uint32Array(256),new Uint32Array(256)],this.invSubMix=[new Uint32Array(256),new Uint32Array(256),new Uint32Array(256),new Uint32Array(256)],this.sBox=new Uint32Array(256),this.invSBox=new Uint32Array(256),this.key=new Uint32Array(0),this.ksRows=0,this.keySize=0,this.keySchedule=void 0,this.invKeySchedule=void 0,this.initTable()}uint8ArrayToUint32Array_(t){const e=new DataView(t),s=new Uint32Array(4);for(let t=0;t<4;t++)s[t]=e.getUint32(4*t);return s}initTable(){const t=this.sBox,e=this.invSBox,s=this.subMix,i=s[0],r=s[1],n=s[2],a=s[3],o=this.invSubMix,l=o[0],h=o[1],d=o[2],c=o[3],u=new Uint32Array(256);let f=0,g=0,m=0;for(m=0;m<256;m++)u[m]=m<128?m<<1:m<<1^283;for(m=0;m<256;m++){let s=g^g<<1^g<<2^g<<3^g<<4;s=s>>>8^255&s^99,t[f]=s,e[s]=f;const o=u[f],m=u[o],p=u[m];let v=257*u[s]^16843008*s;i[f]=v<<24|v>>>8,r[f]=v<<16|v>>>16,n[f]=v<<8|v>>>24,a[f]=v,v=16843009*p^65537*m^257*o^16843008*f,l[s]=v<<24|v>>>8,h[s]=v<<16|v>>>16,d[s]=v<<8|v>>>24,c[s]=v,f?(f=o^u[u[u[p^o]]],g^=u[u[g]]):f=g=1}}expandKey(t){const e=this.uint8ArrayToUint32Array_(t);let s=!0,i=0;for(;i<e.length&&s;)s=e[i]===this.key[i],i++;if(s)return;this.key=e;const r=this.keySize=e.length;if(4!==r&&6!==r&&8!==r)throw new Error("Invalid aes key size="+r);const n=this.ksRows=4*(r+6+1);let a,o;const l=this.keySchedule=new Uint32Array(n),h=this.invKeySchedule=new Uint32Array(n),d=this.sBox,c=this.rcon,u=this.invSubMix,f=u[0],g=u[1],m=u[2],p=u[3];let v,y;for(a=0;a<n;a++)a<r?v=l[a]=e[a]:(y=v,a%r==0?(y=y<<8|y>>>24,y=d[y>>>24]<<24|d[y>>>16&255]<<16|d[y>>>8&255]<<8|d[255&y],y^=c[a/r|0]<<24):r>6&&a%r==4&&(y=d[y>>>24]<<24|d[y>>>16&255]<<16|d[y>>>8&255]<<8|d[255&y]),l[a]=v=(l[a-r]^y)>>>0);for(o=0;o<n;o++)a=n-o,y=3&o?l[a]:l[a-4],h[o]=o<4||a<=4?y:f[d[y>>>24]]^g[d[y>>>16&255]]^m[d[y>>>8&255]]^p[d[255&y]],h[o]=h[o]>>>0}networkToHostOrderSwap(t){return t<<24|(65280&t)<<8|(16711680&t)>>8|t>>>24}decrypt(t,e,s){const i=this.keySize+6,r=this.invKeySchedule,n=this.invSBox,a=this.invSubMix,o=a[0],l=a[1],h=a[2],d=a[3],c=this.uint8ArrayToUint32Array_(s);let u=c[0],f=c[1],g=c[2],m=c[3];const p=new Int32Array(t),v=new Int32Array(p.length);let y,E,T,S,L,A,R,b,k,w,D,I,C,_;const x=this.networkToHostOrderSwap;for(;e<p.length;){for(k=x(p[e]),w=x(p[e+1]),D=x(p[e+2]),I=x(p[e+3]),L=k^r[0],A=I^r[1],R=D^r[2],b=w^r[3],C=4,_=1;_<i;_++)y=o[L>>>24]^l[A>>16&255]^h[R>>8&255]^d[255&b]^r[C],E=o[A>>>24]^l[R>>16&255]^h[b>>8&255]^d[255&L]^r[C+1],T=o[R>>>24]^l[b>>16&255]^h[L>>8&255]^d[255&A]^r[C+2],S=o[b>>>24]^l[L>>16&255]^h[A>>8&255]^d[255&R]^r[C+3],L=y,A=E,R=T,b=S,C+=4;y=n[L>>>24]<<24^n[A>>16&255]<<16^n[R>>8&255]<<8^n[255&b]^r[C],E=n[A>>>24]<<24^n[R>>16&255]<<16^n[b>>8&255]<<8^n[255&L]^r[C+1],T=n[R>>>24]<<24^n[b>>16&255]<<16^n[L>>8&255]<<8^n[255&A]^r[C+2],S=n[b>>>24]<<24^n[L>>16&255]<<16^n[A>>8&255]<<8^n[255&R]^r[C+3],v[e]=x(y^u),v[e+1]=x(S^f),v[e+2]=x(T^g),v[e+3]=x(E^m),u=k,f=w,g=D,m=I,e+=4}return v.buffer}}class di{constructor(t,{removePKCS7Padding:e=!0}={}){if(this.logEnabled=!0,this.removePKCS7Padding=void 0,this.subtle=null,this.softwareDecrypter=null,this.key=null,this.fastAesKey=null,this.remainderData=null,this.currentIV=null,this.currentResult=null,this.useSoftware=void 0,this.useSoftware=t.enableSoftwareAES,this.removePKCS7Padding=e,e)try{const t=self.crypto;t&&(this.subtle=t.subtle||t.webkitSubtle)}catch(t){}this.useSoftware=!this.subtle}destroy(){this.subtle=null,this.softwareDecrypter=null,this.key=null,this.fastAesKey=null,this.remainderData=null,this.currentIV=null,this.currentResult=null}isSync(){return this.useSoftware}flush(){const{currentResult:t,remainderData:e}=this;if(!t||e)return this.reset(),null;const s=new Uint8Array(t);return this.reset(),this.removePKCS7Padding?function(t){const e=t.byteLength,s=e&&new DataView(t.buffer).getUint8(e-1);return s?st(t,0,e-s):t}(s):s}reset(){this.currentResult=null,this.currentIV=null,this.remainderData=null,this.softwareDecrypter&&(this.softwareDecrypter=null)}decrypt(t,e,s){return this.useSoftware?new Promise(((i,r)=>{this.softwareDecrypt(new Uint8Array(t),e,s);const n=this.flush();n?i(n.buffer):r(new Error("[softwareDecrypt] Failed to decrypt data"))})):this.webCryptoDecrypt(new Uint8Array(t),e,s)}softwareDecrypt(t,e,s){const{currentIV:i,currentResult:r,remainderData:n}=this;this.logOnce("JS AES decrypt"),n&&(t=Bt(n,t),this.remainderData=null);const a=this.getValidChunk(t);if(!a.length)return null;i&&(s=i);let o=this.softwareDecrypter;o||(o=this.softwareDecrypter=new hi),o.expandKey(e);const l=r;return this.currentResult=o.decrypt(a.buffer,0,s),this.currentIV=st(a,-16).buffer,l||null}webCryptoDecrypt(t,e,s){if(this.key!==e||!this.fastAesKey){if(!this.subtle)return Promise.resolve(this.onWebCryptoError(t,e,s));this.key=e,this.fastAesKey=new li(this.subtle,e)}return this.fastAesKey.expandKey().then((e=>{if(!this.subtle)return Promise.reject(new Error("web crypto not initialized"));this.logOnce("WebCrypto AES decrypt");return new oi(this.subtle,new Uint8Array(s)).decrypt(t.buffer,e)})).catch((i=>(A.warn(`[decrypter]: WebCrypto Error, disable WebCrypto API, ${i.name}: ${i.message}`),this.onWebCryptoError(t,e,s))))}onWebCryptoError(t,e,s){this.useSoftware=!0,this.logEnabled=!0,this.softwareDecrypt(t,e,s);const i=this.flush();if(i)return i.buffer;throw new Error("WebCrypto and softwareDecrypt: failed to decrypt data")}getValidChunk(t){let e=t;const s=t.length-t.length%16;return s!==t.length&&(e=st(t,0,s),this.remainderData=st(t,s)),e}logOnce(t){this.logEnabled&&(A.log(`[decrypter]: ${t}`),this.logEnabled=!1)}}const ci=function(t){let e="";const s=t.length;for(let i=0;i<s;i++)e+=`[${t.start(i).toFixed(3)}-${t.end(i).toFixed(3)}]`;return e},ui="STOPPED",fi="IDLE",gi="KEY_LOADING",mi="FRAG_LOADING",pi="FRAG_LOADING_WAITING_RETRY",vi="WAITING_TRACK",yi="PARSING",Ei="PARSED",Ti="ENDED",Si="ERROR",Li="WAITING_INIT_PTS",Ai="WAITING_LEVEL";class Ri extends $s{constructor(t,e,s,i,r){super(),this.hls=void 0,this.fragPrevious=null,this.fragCurrent=null,this.fragmentTracker=void 0,this.transmuxer=null,this._state=ui,this.playlistType=void 0,this.media=null,this.mediaBuffer=null,this.config=void 0,this.bitrateTest=!1,this.lastCurrentTime=0,this.nextLoadPosition=0,this.startPosition=0,this.startTimeOffset=null,this.loadedmetadata=!1,this.retryDate=0,this.levels=null,this.fragmentLoader=void 0,this.keyLoader=void 0,this.levelLastLoaded=null,this.startFragRequested=!1,this.decrypter=void 0,this.initPTS=[],this.onvseeking=null,this.onvended=null,this.logPrefix="",this.log=void 0,this.warn=void 0,this.playlistType=r,this.logPrefix=i,this.log=A.log.bind(A,`${i}:`),this.warn=A.warn.bind(A,`${i}:`),this.hls=t,this.fragmentLoader=new ii(t.config),this.keyLoader=s,this.fragmentTracker=e,this.config=t.config,this.decrypter=new di(t.config),t.on(p.MANIFEST_LOADED,this.onManifestLoaded,this)}doTick(){this.onTickEnd()}onTickEnd(){}startLoad(t){}stopLoad(){this.fragmentLoader.abort(),this.keyLoader.abort(this.playlistType);const t=this.fragCurrent;null!=t&&t.loader&&(t.abortRequests(),this.fragmentTracker.removeFragment(t)),this.resetTransmuxer(),this.fragCurrent=null,this.fragPrevious=null,this.clearInterval(),this.clearNextTick(),this.state=ui}_streamEnded(t,e){if(e.live||t.nextStart||!t.end||!this.media)return!1;const s=e.partList;if(null!=s&&s.length){const t=s[s.length-1];return Xs.isBuffered(this.media,t.start+t.duration/2)}const i=e.fragments[e.fragments.length-1].type;return this.fragmentTracker.isEndListAppended(i)}getLevelDetails(){var t;if(this.levels&&null!==this.levelLastLoaded)return null==(t=this.levelLastLoaded)?void 0:t.details}onMediaAttached(t,e){const s=this.media=this.mediaBuffer=e.media;this.onvseeking=this.onMediaSeeking.bind(this),this.onvended=this.onMediaEnded.bind(this),s.addEventListener("seeking",this.onvseeking),s.addEventListener("ended",this.onvended);const i=this.config;this.levels&&i.autoStartLoad&&this.state===ui&&this.startLoad(i.startPosition)}onMediaDetaching(){const t=this.media;null!=t&&t.ended&&(this.log("MSE detaching and video ended, reset startPosition"),this.startPosition=this.lastCurrentTime=0),t&&this.onvseeking&&this.onvended&&(t.removeEventListener("seeking",this.onvseeking),t.removeEventListener("ended",this.onvended),this.onvseeking=this.onvended=null),this.keyLoader&&this.keyLoader.detach(),this.media=this.mediaBuffer=null,this.loadedmetadata=!1,this.fragmentTracker.removeAllFragments(),this.stopLoad()}onMediaSeeking(){const{config:t,fragCurrent:e,media:s,mediaBuffer:i,state:r}=this,n=s?s.currentTime:0,a=Xs.bufferInfo(i||s,n,t.maxBufferHole);if(this.log(`media seeking to ${f(n)?n.toFixed(3):n}, state: ${r}`),this.state===Ti)this.resetLoadingState();else if(e){const s=t.maxFragLookUpTolerance,i=e.start-s,r=e.start+e.duration+s;if(!a.len||r<a.start||i>a.end){const t=n>r;(n<i||t)&&(t&&e.loader&&(this.log("seeking outside of buffer while fragment load in progress, cancel fragment load"),e.abortRequests(),this.resetLoadingState()),this.fragPrevious=null)}}s&&(this.fragmentTracker.removeFragmentsInRange(n,1/0,this.playlistType,!0),this.lastCurrentTime=n),this.loadedmetadata||a.len||(this.nextLoadPosition=this.startPosition=n),this.tickImmediate()}onMediaEnded(){this.startPosition=this.lastCurrentTime=0}onManifestLoaded(t,e){this.startTimeOffset=e.startTimeOffset,this.initPTS=[]}onHandlerDestroying(){this.hls.off(p.MANIFEST_LOADED,this.onManifestLoaded,this),this.stopLoad(),super.onHandlerDestroying(),this.hls=null}onHandlerDestroyed(){this.state=ui,this.fragmentLoader&&this.fragmentLoader.destroy(),this.keyLoader&&this.keyLoader.destroy(),this.decrypter&&this.decrypter.destroy(),this.hls=this.log=this.warn=this.decrypter=this.keyLoader=this.fragmentLoader=this.fragmentTracker=null,super.onHandlerDestroyed()}loadFragment(t,e,s){this._loadFragForPlayback(t,e,s)}_loadFragForPlayback(t,e,s){this._doFragLoad(t,e,s,(e=>{if(this.fragContextChanged(t))return this.warn(`Fragment ${t.sn}${e.part?" p: "+e.part.index:""} of level ${t.level} was dropped during download.`),void this.fragmentTracker.removeFragment(t);t.stats.chunkCount++,this._handleFragmentLoadProgress(e)})).then((e=>{if(!e)return;const s=this.state;this.fragContextChanged(t)?(s===mi||!this.fragCurrent&&s===yi)&&(this.fragmentTracker.removeFragment(t),this.state=fi):("payload"in e&&(this.log(`Loaded fragment ${t.sn} of level ${t.level}`),this.hls.trigger(p.FRAG_LOADED,e)),this._handleFragmentLoadComplete(e))})).catch((e=>{this.state!==ui&&this.state!==Si&&(this.warn(`Frag error: ${(null==e?void 0:e.message)||e}`),this.resetFragmentLoading(t))}))}clearTrackerIfNeeded(t){var e;const{fragmentTracker:s}=this;if(s.getState(t)===Ks){const e=t.type,i=this.getFwdBufferInfo(this.mediaBuffer,e),r=Math.max(t.duration,i?i.len:this.config.maxBufferLength),n=this.backtrackFragment;(1===(n?t.sn-n.sn:0)||this.reduceMaxBufferLength(r,t.duration))&&s.removeFragment(t)}else 0===(null==(e=this.mediaBuffer)?void 0:e.buffered.length)?s.removeAllFragments():s.hasParts(t.type)&&(s.detectPartialFragments({frag:t,part:null,stats:t.stats,id:t.type}),s.getState(t)===Hs&&s.removeFragment(t))}checkLiveUpdate(t){if(t.updated&&!t.live){const e=t.fragments[t.fragments.length-1];this.fragmentTracker.detectPartialFragments({frag:e,part:null,stats:e.stats,id:e.type})}t.fragments[0]||(t.deltaUpdateFailed=!0)}flushMainBuffer(t,e,s=null){if(!(t-e))return;const i={startOffset:t,endOffset:e,type:s};this.hls.trigger(p.BUFFER_FLUSHING,i)}_loadInitSegment(t,e){this._doFragLoad(t,e).then((e=>{if(!e||this.fragContextChanged(t)||!this.levels)throw new Error("init load aborted");return e})).then((e=>{const{hls:s}=this,{payload:i}=e,r=t.decryptdata;if(i&&i.byteLength>0&&null!=r&&r.key&&r.iv&&"AES-128"===r.method){const n=self.performance.now();return this.decrypter.decrypt(new Uint8Array(i),r.key.buffer,r.iv.buffer).catch((e=>{throw s.trigger(p.ERROR,{type:v.MEDIA_ERROR,details:y.FRAG_DECRYPT_ERROR,fatal:!1,error:e,reason:e.message,frag:t}),e})).then((i=>{const r=self.performance.now();return s.trigger(p.FRAG_DECRYPTED,{frag:t,payload:i,stats:{tstart:n,tdecrypt:r}}),e.payload=i,this.completeInitSegmentLoad(e)}))}return this.completeInitSegmentLoad(e)})).catch((e=>{this.state!==ui&&this.state!==Si&&(this.warn(e),this.resetFragmentLoading(t))}))}completeInitSegmentLoad(t){const{levels:e}=this;if(!e)throw new Error("init load aborted, missing levels");const s=t.frag.stats;this.state=fi,t.frag.data=new Uint8Array(t.payload),s.parsing.start=s.buffering.start=self.performance.now(),s.parsing.end=s.buffering.end=self.performance.now(),this.tick()}fragContextChanged(t){const{fragCurrent:e}=this;return!t||!e||t.sn!==e.sn||t.level!==e.level}fragBufferedComplete(t,e){var s,i,r,n;const a=this.mediaBuffer?this.mediaBuffer:this.media;if(this.log(`Buffered ${t.type} sn: ${t.sn}${e?" part: "+e.index:""} of ${this.playlistType===De?"level":"track"} ${t.level} (frag:[${(null!=(s=t.startPTS)?s:NaN).toFixed(3)}-${(null!=(i=t.endPTS)?i:NaN).toFixed(3)}] > buffer:${a?ci(Xs.getBuffered(a)):"(detached)"})`),"initSegment"!==t.sn){var o;if(t.type!==Ce){const e=t.elementaryStreams;if(!Object.keys(e).some((t=>!!e[t])))return void(this.state=fi)}const e=null==(o=this.levels)?void 0:o[t.level];null!=e&&e.fragmentError&&(this.log(`Resetting level fragment error count of ${e.fragmentError} on frag buffered`),e.fragmentError=0)}this.state=fi,a&&(!this.loadedmetadata&&t.type==De&&a.buffered.length&&(null==(r=this.fragCurrent)?void 0:r.sn)===(null==(n=this.fragPrevious)?void 0:n.sn)&&(this.loadedmetadata=!0,this.seekToStartPos()),this.tick())}seekToStartPos(){}_handleFragmentLoadComplete(t){const{transmuxer:e}=this;if(!e)return;const{frag:s,part:i,partsLoaded:r}=t,n=!r||0===r.length||r.some((t=>!t)),a=new zs(s.level,s.sn,s.stats.chunkCount+1,0,i?i.index:-1,!n);e.flush(a)}_handleFragmentLoadProgress(t){}_doFragLoad(t,e,s=null,i){var r;const n=null==e?void 0:e.details;if(!this.levels||!n)throw new Error(`frag load aborted, missing level${n?"":" detail"}s`);let a=null;if(!t.encrypted||null!=(r=t.decryptdata)&&r.key?!t.encrypted&&n.encryptedFragments.length&&this.keyLoader.loadClear(t,n.encryptedFragments):(this.log(`Loading key for ${t.sn} of [${n.startSN}-${n.endSN}], ${"[stream-controller]"===this.logPrefix?"level":"track"} ${t.level}`),this.state=gi,this.fragCurrent=t,a=this.keyLoader.load(t).then((t=>{if(!this.fragContextChanged(t.frag))return this.hls.trigger(p.KEY_LOADED,t),this.state===gi&&(this.state=fi),t})),this.hls.trigger(p.KEY_LOADING,{frag:t}),null===this.fragCurrent&&(a=Promise.reject(new Error("frag load aborted, context changed in KEY_LOADING")))),s=Math.max(t.start,s||0),this.config.lowLatencyMode&&"initSegment"!==t.sn){const r=n.partList;if(r&&i){s>t.end&&n.fragmentHint&&(t=n.fragmentHint);const o=this.getNextPart(r,t,s);if(o>-1){const l=r[o];let h;return this.log(`Loading part sn: ${t.sn} p: ${l.index} cc: ${t.cc} of playlist [${n.startSN}-${n.endSN}] parts [0-${o}-${r.length-1}] ${"[stream-controller]"===this.logPrefix?"level":"track"}: ${t.level}, target: ${parseFloat(s.toFixed(3))}`),this.nextLoadPosition=l.start+l.duration,this.state=mi,h=a?a.then((s=>!s||this.fragContextChanged(s.frag)?null:this.doFragPartsLoad(t,l,e,i))).catch((t=>this.handleFragLoadError(t))):this.doFragPartsLoad(t,l,e,i).catch((t=>this.handleFragLoadError(t))),this.hls.trigger(p.FRAG_LOADING,{frag:t,part:l,targetBufferTime:s}),null===this.fragCurrent?Promise.reject(new Error("frag load aborted, context changed in FRAG_LOADING parts")):h}if(!t.url||this.loadedEndOfParts(r,s))return Promise.resolve(null)}}this.log(`Loading fragment ${t.sn} cc: ${t.cc} ${n?"of ["+n.startSN+"-"+n.endSN+"] ":""}${"[stream-controller]"===this.logPrefix?"level":"track"}: ${t.level}, target: ${parseFloat(s.toFixed(3))}`),f(t.sn)&&!this.bitrateTest&&(this.nextLoadPosition=t.start+t.duration),this.state=mi;const o=this.config.progressive;let l;return l=o&&a?a.then((e=>!e||this.fragContextChanged(null==e?void 0:e.frag)?null:this.fragmentLoader.load(t,i))).catch((t=>this.handleFragLoadError(t))):Promise.all([this.fragmentLoader.load(t,o?i:void 0),a]).then((([t])=>(!o&&t&&i&&i(t),t))).catch((t=>this.handleFragLoadError(t))),this.hls.trigger(p.FRAG_LOADING,{frag:t,targetBufferTime:s}),null===this.fragCurrent?Promise.reject(new Error("frag load aborted, context changed in FRAG_LOADING")):l}doFragPartsLoad(t,e,s,i){return new Promise(((r,n)=>{var a;const o=[],l=null==(a=s.details)?void 0:a.partList,h=e=>{this.fragmentLoader.loadPart(t,e,i).then((i=>{o[e.index]=i;const n=i.part;this.hls.trigger(p.FRAG_LOADED,i);const a=ls(s,t.sn,e.index+1)||hs(l,t.sn,e.index+1);if(!a)return r({frag:t,part:n,partsLoaded:o});h(a)})).catch(n)};h(e)}))}handleFragLoadError(t){if("data"in t){const e=t.data;t.data&&e.details===y.INTERNAL_ABORTED?this.handleFragLoadAborted(e.frag,e.part):this.hls.trigger(p.ERROR,e)}else this.hls.trigger(p.ERROR,{type:v.OTHER_ERROR,details:y.INTERNAL_EXCEPTION,err:t,error:t,fatal:!0});return null}_handleTransmuxerFlush(t){const e=this.getCurrentContext(t);if(!e||this.state!==yi)return void(this.fragCurrent||this.state===ui||this.state===Si||(this.state=fi));const{frag:s,part:i,level:r}=e,n=self.performance.now();s.stats.parsing.end=n,i&&(i.stats.parsing.end=n),this.updateLevelTiming(s,i,r,t.partial)}getCurrentContext(t){const{levels:e,fragCurrent:s}=this,{level:i,sn:r,part:n}=t;if(null==e||!e[i])return this.warn(`Levels object was unset while buffering fragment ${r} of level ${i}. The current chunk will not be buffered.`),null;const a=e[i],o=n>-1?ls(a,r,n):null,l=o?o.fragment:function(t,e,s){if(null==t||!t.details)return null;const i=t.details;let r=i.fragments[e-i.startSN];return r||(r=i.fragmentHint,r&&r.sn===e?r:e<i.startSN&&s&&s.sn===e?s:null)}(a,r,s);return l?(s&&s!==l&&(l.stats=s.stats),{frag:l,part:o,level:a}):null}bufferFragmentData(t,e,s,i,r){var n;if(!t||this.state!==yi)return;const{data1:a,data2:o}=t;let l=a;if(a&&o&&(l=Bt(a,o)),null==(n=l)||!n.length)return;const h={type:t.type,frag:e,part:s,chunkMeta:i,parent:e.type,data:l};if(this.hls.trigger(p.BUFFER_APPENDING,h),t.dropped&&t.independent&&!s){if(r)return;this.flushBufferGap(e)}}flushBufferGap(t){const e=this.media;if(!e)return;if(!Xs.isBuffered(e,e.currentTime))return void this.flushMainBuffer(0,t.start);const s=e.currentTime,i=Xs.bufferInfo(e,s,0),r=t.duration,n=Math.min(2*this.config.maxFragLookUpTolerance,.25*r),a=Math.max(Math.min(t.start-n,i.end-n),s+n);t.start-a>n&&this.flushMainBuffer(a,t.start)}getFwdBufferInfo(t,e){const s=this.getLoadPosition();return f(s)?this.getFwdBufferInfoAtPos(t,s,e):null}getFwdBufferInfoAtPos(t,e,s){const{config:{maxBufferHole:i}}=this,r=Xs.bufferInfo(t,e,i);if(0===r.len&&void 0!==r.nextStart){const n=this.fragmentTracker.getBufferedFrag(e,s);if(n&&r.nextStart<n.end)return Xs.bufferInfo(t,e,Math.max(r.nextStart,i))}return r}getMaxBufferLength(t){const{config:e}=this;let s;return s=t?Math.max(8*e.maxBufferSize/t,e.maxBufferLength):e.maxBufferLength,Math.min(s,e.maxMaxBufferLength)}reduceMaxBufferLength(t,e){const s=this.config,i=Math.max(Math.min(t-e,s.maxBufferLength),e),r=Math.max(t-3*e,s.maxMaxBufferLength/2,i);return r>=i&&(s.maxMaxBufferLength=r,this.warn(`Reduce max buffer length to ${r}s`),!0)}getAppendedFrag(t,e=De){const s=this.fragmentTracker.getAppendedFrag(t,De);return s&&"fragment"in s?s.fragment:s}getNextFragment(t,e){const s=e.fragments,i=s.length;if(!i)return null;const{config:r}=this,n=s[0].start;let a;if(e.live){const o=r.initialLiveManifestSize;if(i<o)return this.warn(`Not enough fragments to start playback (have: ${i}, need: ${o})`),null;(!e.PTSKnown&&!this.startFragRequested&&-1===this.startPosition||t<n)&&(a=this.getInitialLiveFragment(e,s),this.startPosition=this.nextLoadPosition=a?this.hls.liveSyncPosition||a.start:t)}else t<=n&&(a=s[0]);if(!a){const s=r.lowLatencyMode?e.partEnd:e.fragmentEnd;a=this.getFragmentAtPosition(t,s,e)}return this.mapToInitFragWhenRequired(a)}isLoopLoading(t,e){const s=this.fragmentTracker.getState(t);return(s===Vs||s===Hs&&!!t.gap)&&this.nextLoadPosition>e}getNextFragmentLoopLoading(t,e,s,i,r){const n=t.gap,a=this.getNextFragment(this.nextLoadPosition,e);if(null===a)return a;if(t=a,n&&t&&!t.gap&&s.nextStart){const e=this.getFwdBufferInfoAtPos(this.mediaBuffer?this.mediaBuffer:this.media,s.nextStart,i);if(null!==e&&s.len+e.len>=r)return this.log(`buffer full after gaps in "${i}" playlist starting at sn: ${t.sn}`),null}return t}mapToInitFragWhenRequired(t){return null==t||!t.initSegment||null!=t&&t.initSegment.data||this.bitrateTest?t:t.initSegment}getNextPart(t,e,s){let i=-1,r=!1,n=!0;for(let a=0,o=t.length;a<o;a++){const o=t[a];if(n=n&&!o.independent,i>-1&&s<o.start)break;const l=o.loaded;l?i=-1:(r||o.independent||n)&&o.fragment===e&&(i=a),r=l}return i}loadedEndOfParts(t,e){const s=t[t.length-1];return s&&e>s.start&&s.loaded}getInitialLiveFragment(t,e){const s=this.fragPrevious;let i=null;if(s){if(t.hasProgramDateTime&&(this.log(`Live playlist, switching playlist, load frag with same PDT: ${s.programDateTime}`),i=function(t,e,s){if(null===e||!Array.isArray(t)||!t.length||!f(e))return null;if(e<(t[0].programDateTime||0))return null;if(e>=(t[t.length-1].endProgramDateTime||0))return null;s=s||0;for(let i=0;i<t.length;++i){const r=t[i];if(Es(e,s,r))return r}return null}(e,s.endProgramDateTime,this.config.maxFragLookUpTolerance)),!i){const r=s.sn+1;if(r>=t.startSN&&r<=t.endSN){const n=e[r-t.startSN];s.cc===n.cc&&(i=n,this.log(`Live playlist, switching playlist, load frag with next SN: ${i.sn}`))}i||(i=function(t,e){return ps(t,(t=>t.cc<e?1:t.cc>e?-1:0))}(e,s.cc),i&&this.log(`Live playlist, switching playlist, load frag with same CC: ${i.sn}`))}}else{const e=this.hls.liveSyncPosition;null!==e&&(i=this.getFragmentAtPosition(e,this.bitrateTest?t.fragmentEnd:t.edge,t))}return i}getFragmentAtPosition(t,e,s){const{config:i}=this;let{fragPrevious:r}=this,{fragments:n,endSN:a}=s;const{fragmentHint:o}=s,{maxFragLookUpTolerance:l}=i,h=s.partList,d=!!(i.lowLatencyMode&&null!=h&&h.length&&o);let c;if(d&&o&&!this.bitrateTest&&(n=n.concat(o),a=o.sn),t<e){c=vs(r,n,t,t>e-l?0:l)}else c=n[n.length-1];if(c){const t=c.sn-s.startSN,e=this.fragmentTracker.getState(c);if((e===Vs||e===Hs&&c.gap)&&(r=c),r&&c.sn===r.sn&&(!d||h[0].fragment.sn>c.sn)){if(r&&c.level===r.level){const e=n[t+1];c=c.sn<a&&this.fragmentTracker.getState(e)!==Vs?e:null}}}return c}synchronizeToLiveEdge(t){const{config:e,media:s}=this;if(!s)return;const i=this.hls.liveSyncPosition,r=s.currentTime,n=t.fragments[0].start,a=t.edge,o=r>=n-e.maxFragLookUpTolerance&&r<=a;if(null!==i&&s.duration>i&&(r<i||!o)){const n=void 0!==e.liveMaxLatencyDuration?e.liveMaxLatencyDuration:e.liveMaxLatencyDurationCount*t.targetduration;(!o&&s.readyState<4||r<a-n)&&(this.loadedmetadata||(this.nextLoadPosition=i),s.readyState&&(this.warn(`Playback: ${r.toFixed(3)} is located too far from the end of live sliding playlist: ${a}, reset currentTime to : ${i.toFixed(3)}`),s.currentTime=i))}}alignPlaylists(t,e,s){const i=t.fragments.length;if(!i)return this.warn("No fragments in live playlist"),0;const r=t.fragments[0].start,n=!e,a=t.alignedSliding&&f(r);if(n||!a&&!r){const{fragPrevious:r}=this;ti(r,s,t);const n=t.fragments[0].start;return this.log(`Live playlist sliding: ${n.toFixed(2)} start-sn: ${e?e.startSN:"na"}->${t.startSN} prev-sn: ${r?r.sn:"na"} fragments: ${i}`),n}return r}waitForCdnTuneIn(t){return t.live&&t.canBlockReload&&t.partTarget&&t.tuneInGoal>Math.max(t.partHoldBack,3*t.partTarget)}setStartPosition(t,e){let s=this.startPosition;if(s<e&&(s=-1),-1===s||-1===this.lastCurrentTime){const i=null!==this.startTimeOffset,r=i?this.startTimeOffset:t.startTimeOffset;null!==r&&f(r)?(s=e+r,r<0&&(s+=t.totalduration),s=Math.min(Math.max(e,s),e+t.totalduration),this.log(`Start time offset ${r} found in ${i?"multivariant":"media"} playlist, adjust startPosition to ${s}`),this.startPosition=s):t.live?s=this.hls.liveSyncPosition||e:this.startPosition=s=0,this.lastCurrentTime=s}this.nextLoadPosition=s}getLoadPosition(){const{media:t}=this;let e=0;return this.loadedmetadata&&t?e=t.currentTime:this.nextLoadPosition&&(e=this.nextLoadPosition),e}handleFragLoadAborted(t,e){this.transmuxer&&"initSegment"!==t.sn&&t.stats.aborted&&(this.warn(`Fragment ${t.sn}${e?" part "+e.index:""} of level ${t.level} was aborted`),this.resetFragmentLoading(t))}resetFragmentLoading(t){this.fragCurrent&&(this.fragContextChanged(t)||this.state===pi)||(this.state=fi)}onFragmentOrKeyLoadError(t,e){if(e.chunkMeta&&!e.frag){const t=this.getCurrentContext(e.chunkMeta);t&&(e.frag=t.frag)}const s=e.frag;if(!s||s.type!==t||!this.levels)return;var i;if(this.fragContextChanged(s))return void this.warn(`Frag load error must match current frag to retry ${s.url} > ${null==(i=this.fragCurrent)?void 0:i.url}`);const r=e.details===y.FRAG_GAP;r&&this.fragmentTracker.fragBuffered(s,!0);const n=e.errorAction,{action:a,retryCount:o=0,retryConfig:l}=n||{};if(n&&a===As&&l){this.resetStartWhenNotLoaded(this.levelLastLoaded);const i=fs(l,o);this.warn(`Fragment ${s.sn} of ${t} ${s.level} errored with ${e.details}, retrying loading ${o+1}/${l.maxNumRetry} in ${i}ms`),n.resolved=!0,this.retryDate=self.performance.now()+i,this.state=pi}else if(l&&n){if(this.resetFragmentErrors(t),!(o<l.maxNumRetry))return void A.warn(`${e.details} reached or exceeded max retry (${o})`);r||a===Ls||(n.resolved=!0)}else(null==n?void 0:n.action)===Ss?this.state=Ai:this.state=Si;this.tickImmediate()}reduceLengthAndFlushBuffer(t){if(this.state===yi||this.state===Ei){const e=t.frag,s=t.parent,i=this.getFwdBufferInfo(this.mediaBuffer,s),r=i&&i.len>.5;r&&this.reduceMaxBufferLength(i.len,(null==e?void 0:e.duration)||10);const n=!r;return n&&this.warn(`Buffer full error while media.currentTime is not buffered, flush ${s} buffer`),e&&(this.fragmentTracker.removeFragment(e),this.nextLoadPosition=e.start),this.resetLoadingState(),n}return!1}resetFragmentErrors(t){t===Ie&&(this.fragCurrent=null),this.loadedmetadata||(this.startFragRequested=!1),this.state!==ui&&(this.state=fi)}afterBufferFlushed(t,e,s){if(!t)return;const i=Xs.getBuffered(t);this.fragmentTracker.detectEvictedFragments(e,i,s),this.state===Ti&&this.resetLoadingState()}resetLoadingState(){this.log("Reset loading state"),this.fragCurrent=null,this.fragPrevious=null,this.state=fi}resetStartWhenNotLoaded(t){if(!this.loadedmetadata){this.startFragRequested=!1;const e=t?t.details:null;null!=e&&e.live?(this.startPosition=-1,this.setStartPosition(e,0),this.resetLoadingState()):this.nextLoadPosition=this.startPosition}}resetWhenMissingContext(t){this.warn(`The loading context changed while buffering fragment ${t.sn} of level ${t.level}. This chunk will not be buffered.`),this.removeUnbufferedFrags(),this.resetStartWhenNotLoaded(this.levelLastLoaded),this.resetLoadingState()}removeUnbufferedFrags(t=0){this.fragmentTracker.removeFragmentsInRange(t,1/0,this.playlistType,!1,!0)}updateLevelTiming(t,e,s,i){var r;const n=s.details;if(!n)return void this.warn("level.details undefined");if(!Object.keys(t.elementaryStreams).reduce(((e,r)=>{const a=t.elementaryStreams[r];if(a){const o=a.endPTS-a.startPTS;if(o<=0)return this.warn(`Could not parse fragment ${t.sn} ${r} duration reliably (${o})`),e||!1;const l=i?0:rs(n,t,a.startPTS,a.endPTS,a.startDTS,a.endDTS);return this.hls.trigger(p.LEVEL_PTS_UPDATED,{details:n,level:s,drift:l,type:r,frag:t,start:a.startPTS,end:a.endPTS}),!0}return e}),!1)&&null===(null==(r=this.transmuxer)?void 0:r.error)){const e=new Error(`Found no media in fragment ${t.sn} of level ${t.level} resetting transmuxer to fallback to playlist timing`);if(0===s.fragmentError&&(s.fragmentError++,t.gap=!0,this.fragmentTracker.removeFragment(t),this.fragmentTracker.fragBuffered(t,!0)),this.warn(e.message),this.hls.trigger(p.ERROR,{type:v.MEDIA_ERROR,details:y.FRAG_PARSING_ERROR,fatal:!1,error:e,frag:t,reason:`Found no media in msn ${t.sn} of level "${s.url}"`}),!this.hls)return;this.resetTransmuxer()}this.state=Ei,this.hls.trigger(p.FRAG_PARSED,{frag:t,part:e})}resetTransmuxer(){this.transmuxer&&(this.transmuxer.destroy(),this.transmuxer=null)}recoverWorkerError(t){"demuxerWorker"===t.event&&(this.fragmentTracker.removeAllFragments(),this.resetTransmuxer(),this.resetStartWhenNotLoaded(this.levelLastLoaded),this.resetLoadingState())}set state(t){const e=this._state;e!==t&&(this._state=t,this.log(`${e}->${t}`))}get state(){return this._state}}class bi{constructor(){this.chunks=[],this.dataLength=0}push(t){this.chunks.push(t),this.dataLength+=t.length}flush(){const{chunks:t,dataLength:e}=this;let s;return t.length?(s=1===t.length?t[0]:function(t,e){const s=new Uint8Array(e);let i=0;for(let e=0;e<t.length;e++){const r=t[e];s.set(r,i),i+=r.length}return s}(t,e),this.reset(),s):new Uint8Array(0)}reset(){this.chunks.length=0,this.dataLength=0}}function ki(t="",e=9e4){return{type:t,id:-1,pid:-1,inputTimeScale:e,sequenceNumber:-1,samples:[],dropped:0}}class wi{constructor(){this._audioTrack=void 0,this._id3Track=void 0,this.frameIndex=0,this.cachedData=null,this.basePTS=null,this.initPTS=null,this.lastPTS=null}resetInitSegment(t,e,s,i){this._id3Track={type:"id3",id:3,pid:-1,inputTimeScale:9e4,sequenceNumber:0,samples:[],dropped:0}}resetTimeStamp(t){this.initPTS=t,this.resetContiguity()}resetContiguity(){this.basePTS=null,this.lastPTS=null,this.frameIndex=0}canParse(t,e){return!1}appendFrame(t,e,s){}demux(t,e){this.cachedData&&(t=Bt(this.cachedData,t),this.cachedData=null);let s,i=nt(t,0),r=i?i.length:0;const n=this._audioTrack,a=this._id3Track,o=i?lt(i):void 0,l=t.length;for((null===this.basePTS||0===this.frameIndex&&f(o))&&(this.basePTS=Di(o,e,this.initPTS),this.lastPTS=this.basePTS),null===this.lastPTS&&(this.lastPTS=this.basePTS),i&&i.length>0&&a.samples.push({pts:this.lastPTS,dts:this.lastPTS,data:i,type:Be,duration:Number.POSITIVE_INFINITY});r<l;){if(this.canParse(t,r)){const e=this.appendFrame(n,t,r);e?(this.frameIndex++,this.lastPTS=e.sample.pts,r+=e.length,s=r):r=l}else ot(t,r)?(i=nt(t,r),a.samples.push({pts:this.lastPTS,dts:this.lastPTS,data:i,type:Be,duration:Number.POSITIVE_INFINITY}),r+=i.length,s=r):r++;if(r===l&&s!==l){const e=st(t,s);this.cachedData?this.cachedData=Bt(this.cachedData,e):this.cachedData=e}}return{audioTrack:n,videoTrack:ki(),id3Track:a,textTrack:ki()}}demuxSampleAes(t,e,s){return Promise.reject(new Error(`[${this}] This demuxer does not support Sample-AES decryption`))}flush(t){const e=this.cachedData;return e&&(this.cachedData=null,this.demux(e,0)),{audioTrack:this._audioTrack,videoTrack:ki(),id3Track:this._id3Track,textTrack:ki()}}destroy(){}}const Di=(t,e,s)=>{if(f(t))return 90*t;return 9e4*e+(s?9e4*s.baseTime/s.timescale:0)};function Ii(t,e){return 255===t[e]&&240==(246&t[e+1])}function Ci(t,e){return 1&t[e+1]?7:9}function _i(t,e){return(3&t[e+3])<<11|t[e+4]<<3|(224&t[e+5])>>>5}function xi(t,e){return e+1<t.length&&Ii(t,e)}function Pi(t,e){if(xi(t,e)){const s=Ci(t,e);if(e+s>=t.length)return!1;const i=_i(t,e);if(i<=s)return!1;const r=e+i;return r===t.length||xi(t,r)}return!1}function Mi(t,e,s,i,r){if(!t.samplerate){const n=function(t,e,s,i){let r,n,a,o;const l=navigator.userAgent.toLowerCase(),h=i,d=[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350];r=1+((192&e[s+2])>>>6);const c=(60&e[s+2])>>>2;if(!(c>d.length-1))return a=(1&e[s+2])<<2,a|=(192&e[s+3])>>>6,A.log(`manifest codec:${i}, ADTS type:${r}, samplingIndex:${c}`),/firefox/i.test(l)?c>=6?(r=5,o=new Array(4),n=c-3):(r=2,o=new Array(2),n=c):-1!==l.indexOf("android")?(r=2,o=new Array(2),n=c):(r=5,o=new Array(4),i&&(-1!==i.indexOf("mp4a.40.29")||-1!==i.indexOf("mp4a.40.5"))||!i&&c>=6?n=c-3:((i&&-1!==i.indexOf("mp4a.40.2")&&(c>=6&&1===a||/vivaldi/i.test(l))||!i&&1===a)&&(r=2,o=new Array(2)),n=c)),o[0]=r<<3,o[0]|=(14&c)>>1,o[1]|=(1&c)<<7,o[1]|=a<<3,5===r&&(o[1]|=(14&n)>>1,o[2]=(1&n)<<7,o[2]|=8,o[3]=0),{config:o,samplerate:d[c],channelCount:a,codec:"mp4a.40."+r,manifestCodec:h};{const e=new Error(`invalid ADTS sampling index:${c}`);t.emit(p.ERROR,p.ERROR,{type:v.MEDIA_ERROR,details:y.FRAG_PARSING_ERROR,fatal:!0,error:e,reason:e.message})}}(e,s,i,r);if(!n)return;t.config=n.config,t.samplerate=n.samplerate,t.channelCount=n.channelCount,t.codec=n.codec,t.manifestCodec=n.manifestCodec,A.log(`parsed codec:${t.codec}, rate:${n.samplerate}, channels:${n.channelCount}`)}}function Fi(t){return 9216e4/t}function Oi(t,e,s,i,r){const n=i+r*Fi(t.samplerate),a=function(t,e){const s=Ci(t,e);if(e+s<=t.length){const i=_i(t,e)-s;if(i>0)return{headerLength:s,frameLength:i}}}(e,s);let o;if(a){const{frameLength:i,headerLength:r}=a,l=r+i,h=Math.max(0,s+l-e.length);h?(o=new Uint8Array(l-r),o.set(e.subarray(s+r,e.length),0)):o=e.subarray(s+r,s+l);const d={unit:o,pts:n};return h||t.samples.push(d),{sample:d,length:l,missing:h}}const l=e.length-s;o=new Uint8Array(l),o.set(e.subarray(s,e.length),0);return{sample:{unit:o,pts:n},length:l,missing:-1}}let Ni=null;const Ui=[32,64,96,128,160,192,224,256,288,320,352,384,416,448,32,48,56,64,80,96,112,128,160,192,224,256,320,384,32,40,48,56,64,80,96,112,128,160,192,224,256,320,32,48,56,64,80,96,112,128,144,160,176,192,224,256,8,16,24,32,40,48,56,64,80,96,112,128,144,160],Bi=[44100,48e3,32e3,22050,24e3,16e3,11025,12e3,8e3],$i=[[0,72,144,12],[0,0,0,0],[0,72,144,12],[0,144,144,12]],Gi=[0,1,1,4];function Ki(t,e,s,i,r){if(s+24>e.length)return;const n=Hi(e,s);if(n&&s+n.frameLength<=e.length){const a=i+r*(9e4*n.samplesPerFrame/n.sampleRate),o={unit:e.subarray(s,s+n.frameLength),pts:a,dts:a};return t.config=[],t.channelCount=n.channelCount,t.samplerate=n.sampleRate,t.samples.push(o),{sample:o,length:n.frameLength,missing:0}}}function Hi(t,e){const s=t[e+1]>>3&3,i=t[e+1]>>1&3,r=t[e+2]>>4&15,n=t[e+2]>>2&3;if(1!==s&&0!==r&&15!==r&&3!==n){const a=t[e+2]>>1&1,o=t[e+3]>>6,l=1e3*Ui[14*(3===s?3-i:3===i?3:4)+r-1],h=Bi[3*(3===s?0:2===s?1:2)+n],d=3===o?1:2,c=$i[s][i],u=Gi[i],f=8*c*u,g=Math.floor(c*l/h+a)*u;if(null===Ni){const t=(navigator.userAgent||"").match(/Chrome\/(\d+)/i);Ni=t?parseInt(t[1]):0}return!!Ni&&Ni<=87&&2===i&&l>=224e3&&0===o&&(t[e+3]=128|t[e+3]),{sampleRate:h,channelCount:d,frameLength:g,samplesPerFrame:f}}}function Vi(t,e){return!(255!==t[e]||224&~t[e+1]||!(6&t[e+1]))}function Yi(t,e){return e+1<t.length&&Vi(t,e)}function Wi(t,e){if(e+1<t.length&&Vi(t,e)){const s=4,i=Hi(t,e);let r=s;null!=i&&i.frameLength&&(r=i.frameLength);const n=e+r;return n===t.length||Yi(t,n)}return!1}const ji=/\/emsg[-/]ID3/i;const qi=(t,e)=>{let s=0,i=5;e+=i;const r=new Uint32Array(1),n=new Uint32Array(1),a=new Uint8Array(1);for(;i>0;){a[0]=t[e];const o=Math.min(i,8),l=8-o;n[0]=4278190080>>>24+l<<l,r[0]=(a[0]&n[0])>>l,s=s?s<<o|r[0]:r[0],e+=1,i-=o}return s};class Xi extends wi{constructor(t){super(),this.observer=void 0,this.observer=t}resetInitSegment(t,e,s,i){super.resetInitSegment(t,e,s,i),this._audioTrack={container:"audio/ac-3",type:"audio",id:2,pid:-1,sequenceNumber:0,segmentCodec:"ac3",samples:[],manifestCodec:e,duration:i,inputTimeScale:9e4,dropped:0}}canParse(t,e){return e+64<t.length}appendFrame(t,e,s){const i=zi(t,e,s,this.basePTS,this.frameIndex);if(-1!==i){return{sample:t.samples[t.samples.length-1],length:i,missing:0}}}static probe(t){if(!t)return!1;const e=nt(t,0);if(!e)return!1;const s=e.length;return 11===t[s]&&119===t[s+1]&&void 0!==lt(e)&&qi(t,s)<16}}function zi(t,e,s,i,r){if(s+8>e.length)return-1;if(11!==e[s]||119!==e[s+1])return-1;const n=e[s+4]>>6;if(n>=3)return-1;const a=[48e3,44100,32e3][n],o=63&e[s+4],l=2*[64,69,96,64,70,96,80,87,120,80,88,120,96,104,144,96,105,144,112,121,168,112,122,168,128,139,192,128,140,192,160,174,240,160,175,240,192,208,288,192,209,288,224,243,336,224,244,336,256,278,384,256,279,384,320,348,480,320,349,480,384,417,576,384,418,576,448,487,672,448,488,672,512,557,768,512,558,768,640,696,960,640,697,960,768,835,1152,768,836,1152,896,975,1344,896,976,1344,1024,1114,1536,1024,1115,1536,1152,1253,1728,1152,1254,1728,1280,1393,1920,1280,1394,1920][3*o+n];if(s+l>e.length)return-1;const h=e[s+6]>>5;let d=0;2===h?d+=2:(1&h&&1!==h&&(d+=2),4&h&&(d+=2));const c=(e[s+6]<<8|e[s+7])>>12-d&1,u=[2,1,2,3,3,4,4,5][h]+c,f=e[s+5]>>3,g=7&e[s+5],m=new Uint8Array([n<<6|f<<1|g>>2,(3&g)<<6|h<<3|c<<2|o>>4,o<<4&224]),p=i+r*(1536/a*9e4),v=e.subarray(s,s+l);return t.config=m,t.channelCount=u,t.samplerate=a,t.samples.push({unit:v,pts:p}),l}class Qi{constructor(){this.VideoSample=null}createVideoSample(t,e,s,i){return{key:t,frame:!1,pts:e,dts:s,units:[],debug:i,length:0}}getLastNalUnit(t){var e;let s,i=this.VideoSample;if(i&&0!==i.units.length||(i=t[t.length-1]),null!=(e=i)&&e.units){const t=i.units;s=t[t.length-1]}return s}pushAccessUnit(t,e){if(t.units.length&&t.frame){if(void 0===t.pts){const s=e.samples,i=s.length;if(!i)return void e.dropped++;{const e=s[i-1];t.pts=e.pts,t.dts=e.dts}}e.samples.push(t)}t.debug.length&&A.log(t.pts+"/"+t.dts+":"+t.debug)}}class Ji{constructor(t){this.data=void 0,this.bytesAvailable=void 0,this.word=void 0,this.bitsAvailable=void 0,this.data=t,this.bytesAvailable=t.byteLength,this.word=0,this.bitsAvailable=0}loadWord(){const t=this.data,e=this.bytesAvailable,s=t.byteLength-e,i=new Uint8Array(4),r=Math.min(4,e);if(0===r)throw new Error("no bytes available");i.set(t.subarray(s,s+r)),this.word=new DataView(i.buffer).getUint32(0),this.bitsAvailable=8*r,this.bytesAvailable-=r}skipBits(t){let e;t=Math.min(t,8*this.bytesAvailable+this.bitsAvailable),this.bitsAvailable>t?(this.word<<=t,this.bitsAvailable-=t):(e=(t-=this.bitsAvailable)>>3,t-=e<<3,this.bytesAvailable-=e,this.loadWord(),this.word<<=t,this.bitsAvailable-=t)}readBits(t){let e=Math.min(this.bitsAvailable,t);const s=this.word>>>32-e;if(t>32&&A.error("Cannot read more than 32 bits at a time"),this.bitsAvailable-=e,this.bitsAvailable>0)this.word<<=e;else{if(!(this.bytesAvailable>0))throw new Error("no bits available");this.loadWord()}return e=t-e,e>0&&this.bitsAvailable?s<<e|this.readBits(e):s}skipLZ(){let t;for(t=0;t<this.bitsAvailable;++t)if(this.word&2147483648>>>t)return this.word<<=t,this.bitsAvailable-=t,t;return this.loadWord(),t+this.skipLZ()}skipUEG(){this.skipBits(1+this.skipLZ())}skipEG(){this.skipBits(1+this.skipLZ())}readUEG(){const t=this.skipLZ();return this.readBits(t+1)-1}readEG(){const t=this.readUEG();return 1&t?1+t>>>1:-1*(t>>>1)}readBoolean(){return 1===this.readBits(1)}readUByte(){return this.readBits(8)}readUShort(){return this.readBits(16)}readUInt(){return this.readBits(32)}skipScalingList(t){let e,s=8,i=8;for(let r=0;r<t;r++)0!==i&&(e=this.readEG(),i=(s+e+256)%256),s=0===i?s:i}readSPS(){let t,e,s,i=0,r=0,n=0,a=0;const o=this.readUByte.bind(this),l=this.readBits.bind(this),h=this.readUEG.bind(this),d=this.readBoolean.bind(this),c=this.skipBits.bind(this),u=this.skipEG.bind(this),f=this.skipUEG.bind(this),g=this.skipScalingList.bind(this);o();const m=o();if(l(5),c(3),o(),f(),100===m||110===m||122===m||244===m||44===m||83===m||86===m||118===m||128===m){const t=h();if(3===t&&c(1),f(),f(),c(1),d())for(e=3!==t?8:12,s=0;s<e;s++)d()&&g(s<6?16:64)}f();const p=h();if(0===p)h();else if(1===p)for(c(1),u(),u(),t=h(),s=0;s<t;s++)u();f(),c(1);const v=h(),y=h(),E=l(1);0===E&&c(1),c(1),d()&&(i=h(),r=h(),n=h(),a=h());let T=[1,1];if(d()&&d()){switch(o()){case 1:T=[1,1];break;case 2:T=[12,11];break;case 3:T=[10,11];break;case 4:T=[16,11];break;case 5:T=[40,33];break;case 6:T=[24,11];break;case 7:T=[20,11];break;case 8:T=[32,11];break;case 9:T=[80,33];break;case 10:T=[18,11];break;case 11:T=[15,11];break;case 12:T=[64,33];break;case 13:T=[160,99];break;case 14:T=[4,3];break;case 15:T=[3,2];break;case 16:T=[2,1];break;case 255:T=[o()<<8|o(),o()<<8|o()]}}return{width:Math.ceil(16*(v+1)-2*i-2*r),height:(2-E)*(y+1)*16-(E?2:4)*(n+a),pixelRatio:T}}readSliceType(){return this.readUByte(),this.readUEG(),this.readUEG()}}class Zi extends Qi{parseAVCPES(t,e,s,i,r){const n=this.parseAVCNALu(t,s.data);let a,o=this.VideoSample,l=!1;s.data=null,o&&n.length&&!t.audFound&&(this.pushAccessUnit(o,t),o=this.VideoSample=this.createVideoSample(!1,s.pts,s.dts,"")),n.forEach((i=>{var n;switch(i.type){case 1:{let e=!1;a=!0;const r=i.data;if(l&&r.length>4){const t=new Ji(r).readSliceType();2!==t&&4!==t&&7!==t&&9!==t||(e=!0)}var h;if(e)null!=(h=o)&&h.frame&&!o.key&&(this.pushAccessUnit(o,t),o=this.VideoSample=null);o||(o=this.VideoSample=this.createVideoSample(!0,s.pts,s.dts,"")),o.frame=!0,o.key=e;break}case 5:a=!0,null!=(n=o)&&n.frame&&!o.key&&(this.pushAccessUnit(o,t),o=this.VideoSample=null),o||(o=this.VideoSample=this.createVideoSample(!0,s.pts,s.dts,"")),o.key=!0,o.frame=!0;break;case 6:a=!0,Kt(i.data,1,s.pts,e.samples);break;case 7:{var d,c;a=!0,l=!0;const e=i.data,s=new Ji(e).readSPS();if(!t.sps||t.width!==s.width||t.height!==s.height||(null==(d=t.pixelRatio)?void 0:d[0])!==s.pixelRatio[0]||(null==(c=t.pixelRatio)?void 0:c[1])!==s.pixelRatio[1]){t.width=s.width,t.height=s.height,t.pixelRatio=s.pixelRatio,t.sps=[e],t.duration=r;const i=e.subarray(1,4);let n="avc1.";for(let t=0;t<3;t++){let e=i[t].toString(16);e.length<2&&(e="0"+e),n+=e}t.codec=n}break}case 8:a=!0,t.pps=[i.data];break;case 9:a=!0,t.audFound=!0,o&&this.pushAccessUnit(o,t),o=this.VideoSample=this.createVideoSample(!1,s.pts,s.dts,"");break;case 12:a=!0;break;default:a=!1,o&&(o.debug+="unknown NAL "+i.type+" ")}if(o&&a){o.units.push(i)}})),i&&o&&(this.pushAccessUnit(o,t),this.VideoSample=null)}parseAVCNALu(t,e){const s=e.byteLength;let i=t.naluState||0;const r=i,n=[];let a,o,l,h=0,d=-1,c=0;for(-1===i&&(d=0,c=31&e[0],i=0,h=1);h<s;)if(a=e[h++],i)if(1!==i)if(a)if(1===a){if(o=h-i-1,d>=0){const t={data:e.subarray(d,o),type:c};n.push(t)}else{const s=this.getLastNalUnit(t.samples);s&&(r&&h<=4-r&&s.state&&(s.data=s.data.subarray(0,s.data.byteLength-r)),o>0&&(s.data=Bt(s.data,e.subarray(0,o)),s.state=0))}h<s?(l=31&e[h],d=h,c=l,i=0):i=-1}else i=0;else i=3;else i=a?0:2;else i=a?0:1;if(d>=0&&i>=0){const t={data:e.subarray(d,s),type:c,state:i};n.push(t)}if(0===n.length){const s=this.getLastNalUnit(t.samples);s&&(s.data=Bt(s.data,e))}return t.naluState=i,n}}class tr{constructor(t,e,s){this.keyData=void 0,this.decrypter=void 0,this.keyData=s,this.decrypter=new di(e,{removePKCS7Padding:!1})}decryptBuffer(t){return this.decrypter.decrypt(t,this.keyData.key.buffer,this.keyData.iv.buffer)}decryptAacSample(t,e,s){const i=t[e].unit;if(i.length<=16)return;const r=i.subarray(16,i.length-i.length%16),n=r.buffer.slice(r.byteOffset,r.byteOffset+r.length);this.decryptBuffer(n).then((r=>{const n=new Uint8Array(r);i.set(n,16),this.decrypter.isSync()||this.decryptAacSamples(t,e+1,s)}))}decryptAacSamples(t,e,s){for(;;e++){if(e>=t.length)return void s();if(!(t[e].unit.length<32)&&(this.decryptAacSample(t,e,s),!this.decrypter.isSync()))return}}getAvcEncryptedData(t){const e=16*Math.floor((t.length-48)/160)+16,s=new Int8Array(e);let i=0;for(let e=32;e<t.length-16;e+=160,i+=16)s.set(t.subarray(e,e+16),i);return s}getAvcDecryptedUnit(t,e){const s=new Uint8Array(e);let i=0;for(let e=32;e<t.length-16;e+=160,i+=16)t.set(s.subarray(i,i+16),e);return t}decryptAvcSample(t,e,s,i,r){const n=Ht(r.data),a=this.getAvcEncryptedData(n);this.decryptBuffer(a.buffer).then((a=>{r.data=this.getAvcDecryptedUnit(n,a),this.decrypter.isSync()||this.decryptAvcSamples(t,e,s+1,i)}))}decryptAvcSamples(t,e,s,i){if(t instanceof Uint8Array)throw new Error("Cannot decrypt samples of type Uint8Array");for(;;e++,s=0){if(e>=t.length)return void i();const r=t[e].units;for(;!(s>=r.length);s++){const n=r[s];if(!(n.data.length<=48||1!==n.type&&5!==n.type||(this.decryptAvcSample(t,e,s,i,n),this.decrypter.isSync())))return}}}}const er=188;class sr{constructor(t,e,s){this.observer=void 0,this.config=void 0,this.typeSupported=void 0,this.sampleAes=null,this.pmtParsed=!1,this.audioCodec=void 0,this.videoCodec=void 0,this._duration=0,this._pmtId=-1,this._videoTrack=void 0,this._audioTrack=void 0,this._id3Track=void 0,this._txtTrack=void 0,this.aacOverFlow=null,this.remainderData=null,this.videoParser=void 0,this.observer=t,this.config=e,this.typeSupported=s,this.videoParser=new Zi}static probe(t){const e=sr.syncOffset(t);return e>0&&A.warn(`MPEG2-TS detected but first sync word found @ offset ${e}`),-1!==e}static syncOffset(t){const e=t.length;let s=Math.min(940,e-er)+1,i=0;for(;i<s;){let r=!1,n=-1,a=0;for(let o=i;o<e;o+=er){if(71!==t[o]||e-o!==er&&71!==t[o+er]){if(a)return-1;break}if(a++,-1===n&&(n=o,0!==n&&(s=Math.min(n+18612,t.length-er)+1)),r||(r=0===ir(t,o)),r&&a>1&&(0===n&&a>2||o+er>s))return n}i++}return-1}static createTrack(t,e){return{container:"video"===t||"audio"===t?"video/mp2t":void 0,type:t,id:At[t],pid:-1,inputTimeScale:9e4,sequenceNumber:0,samples:[],dropped:0,duration:"audio"===t?e:void 0}}resetInitSegment(t,e,s,i){this.pmtParsed=!1,this._pmtId=-1,this._videoTrack=sr.createTrack("video"),this._audioTrack=sr.createTrack("audio",i),this._id3Track=sr.createTrack("id3"),this._txtTrack=sr.createTrack("text"),this._audioTrack.segmentCodec="aac",this.aacOverFlow=null,this.remainderData=null,this.audioCodec=e,this.videoCodec=s,this._duration=i}resetTimeStamp(){}resetContiguity(){const{_audioTrack:t,_videoTrack:e,_id3Track:s}=this;t&&(t.pesData=null),e&&(e.pesData=null),s&&(s.pesData=null),this.aacOverFlow=null,this.remainderData=null}demux(t,e,s=!1,i=!1){let r;s||(this.sampleAes=null);const n=this._videoTrack,a=this._audioTrack,o=this._id3Track,l=this._txtTrack;let h=n.pid,d=n.pesData,c=a.pid,u=o.pid,f=a.pesData,g=o.pesData,m=null,p=this.pmtParsed,v=this._pmtId,y=t.length;if(this.remainderData&&(y=(t=Bt(this.remainderData,t)).length,this.remainderData=null),y<er&&!i)return this.remainderData=t,{audioTrack:a,videoTrack:n,id3Track:o,textTrack:l};const E=Math.max(0,sr.syncOffset(t));y-=(y-E)%er,y<t.byteLength&&!i&&(this.remainderData=new Uint8Array(t.buffer,y,t.buffer.byteLength-y));let T=0;for(let e=E;e<y;e+=er)if(71===t[e]){const i=!!(64&t[e+1]),y=ir(t,e);let T;if((48&t[e+3])>>4>1){if(T=e+5+t[e+4],T===e+er)continue}else T=e+4;switch(y){case h:i&&(d&&(r=lr(d))&&this.videoParser.parseAVCPES(n,l,r,!1,this._duration),d={data:[],size:0}),d&&(d.data.push(t.subarray(T,e+er)),d.size+=e+er-T);break;case c:if(i){if(f&&(r=lr(f)))switch(a.segmentCodec){case"aac":this.parseAACPES(a,r);break;case"mp3":this.parseMPEGPES(a,r);break;case"ac3":this.parseAC3PES(a,r)}f={data:[],size:0}}f&&(f.data.push(t.subarray(T,e+er)),f.size+=e+er-T);break;case u:i&&(g&&(r=lr(g))&&this.parseID3PES(o,r),g={data:[],size:0}),g&&(g.data.push(t.subarray(T,e+er)),g.size+=e+er-T);break;case 0:i&&(T+=t[T]+1),v=this._pmtId=rr(t,T);break;case v:{i&&(T+=t[T]+1);const r=nr(t,T,this.typeSupported,s,this.observer);h=r.videoPid,h>0&&(n.pid=h,n.segmentCodec=r.segmentVideoCodec),c=r.audioPid,c>0&&(a.pid=c,a.segmentCodec=r.segmentAudioCodec),u=r.id3Pid,u>0&&(o.pid=u),null===m||p||(A.warn(`MPEG-TS PMT found at ${e} after unknown PID '${m}'. Backtracking to sync byte @${E} to parse all TS packets.`),m=null,e=E-188),p=this.pmtParsed=!0;break}case 17:case 8191:break;default:m=y}}else T++;T>0&&ar(this.observer,new Error(`Found ${T} TS packet/s that do not start with 0x47`)),n.pesData=d,a.pesData=f,o.pesData=g;const S={audioTrack:a,videoTrack:n,id3Track:o,textTrack:l};return i&&this.extractRemainingSamples(S),S}flush(){const{remainderData:t}=this;let e;return this.remainderData=null,e=t?this.demux(t,-1,!1,!0):{videoTrack:this._videoTrack,audioTrack:this._audioTrack,id3Track:this._id3Track,textTrack:this._txtTrack},this.extractRemainingSamples(e),this.sampleAes?this.decrypt(e,this.sampleAes):e}extractRemainingSamples(t){const{audioTrack:e,videoTrack:s,id3Track:i,textTrack:r}=t,n=s.pesData,a=e.pesData,o=i.pesData;let l;if(n&&(l=lr(n))?(this.videoParser.parseAVCPES(s,r,l,!0,this._duration),s.pesData=null):s.pesData=n,a&&(l=lr(a))){switch(e.segmentCodec){case"aac":this.parseAACPES(e,l);break;case"mp3":this.parseMPEGPES(e,l);break;case"ac3":this.parseAC3PES(e,l)}e.pesData=null}else null!=a&&a.size&&A.log("last AAC PES packet truncated,might overlap between fragments"),e.pesData=a;o&&(l=lr(o))?(this.parseID3PES(i,l),i.pesData=null):i.pesData=o}demuxSampleAes(t,e,s){const i=this.demux(t,s,!0,!this.config.progressive),r=this.sampleAes=new tr(this.observer,this.config,e);return this.decrypt(i,r)}decrypt(t,e){return new Promise((s=>{const{audioTrack:i,videoTrack:r}=t;i.samples&&"aac"===i.segmentCodec?e.decryptAacSamples(i.samples,0,(()=>{r.samples?e.decryptAvcSamples(r.samples,0,0,(()=>{s(t)})):s(t)})):r.samples&&e.decryptAvcSamples(r.samples,0,0,(()=>{s(t)}))}))}destroy(){this._duration=0}parseAACPES(t,e){let s=0;const i=this.aacOverFlow;let r,n,a,o=e.data;if(i){this.aacOverFlow=null;const e=i.missing,r=i.sample.unit.byteLength;if(-1===e)o=Bt(i.sample.unit,o);else{const n=r-e;i.sample.unit.set(o.subarray(0,e),n),t.samples.push(i.sample),s=i.missing}}for(r=s,n=o.length;r<n-1&&!xi(o,r);r++);if(r!==s){let t;const e=r<n-1;if(t=e?`AAC PES did not start with ADTS header,offset:${r}`:"No ADTS header found in AAC PES",ar(this.observer,new Error(t),e),!e)return}if(Mi(t,this.observer,o,r,this.audioCodec),void 0!==e.pts)a=e.pts;else{if(!i)return void A.warn("[tsdemuxer]: AAC PES unknown PTS");{const e=Fi(t.samplerate);a=i.sample.pts+e}}let l,h=0;for(;r<n;){if(l=Oi(t,o,r,a,h),r+=l.length,l.missing){this.aacOverFlow=l;break}for(h++;r<n-1&&!xi(o,r);r++);}}parseMPEGPES(t,e){const s=e.data,i=s.length;let r=0,n=0;const a=e.pts;if(void 0!==a)for(;n<i;)if(Yi(s,n)){const e=Ki(t,s,n,a,r);if(!e)break;n+=e.length,r++}else n++;else A.warn("[tsdemuxer]: MPEG PES unknown PTS")}parseAC3PES(t,e){{const s=e.data,i=e.pts;if(void 0===i)return void A.warn("[tsdemuxer]: AC3 PES unknown PTS");const r=s.length;let n,a=0,o=0;for(;o<r&&(n=zi(t,s,o,i,a++))>0;)o+=n}}parseID3PES(t,e){if(void 0===e.pts)return void A.warn("[tsdemuxer]: ID3 PES unknown PTS");const s=u({},e,{type:this._videoTrack?Ge:Be,duration:Number.POSITIVE_INFINITY});t.samples.push(s)}}function ir(t,e){return((31&t[e+1])<<8)+t[e+2]}function rr(t,e){return(31&t[e+10])<<8|t[e+11]}function nr(t,e,s,i,r){const n={audioPid:-1,videoPid:-1,id3Pid:-1,segmentVideoCodec:"avc",segmentAudioCodec:"aac"},a=e+3+((15&t[e+1])<<8|t[e+2])-4;for(e+=12+((15&t[e+10])<<8|t[e+11]);e<a;){const a=ir(t,e),o=(15&t[e+3])<<8|t[e+4];switch(t[e]){case 207:if(!i){or("ADTS AAC");break}case 15:-1===n.audioPid&&(n.audioPid=a);break;case 21:-1===n.id3Pid&&(n.id3Pid=a);break;case 219:if(!i){or("H.264");break}case 27:-1===n.videoPid&&(n.videoPid=a,n.segmentVideoCodec="avc");break;case 3:case 4:s.mpeg||s.mp3?-1===n.audioPid&&(n.audioPid=a,n.segmentAudioCodec="mp3"):A.log("MPEG audio found, not supported in this browser");break;case 193:if(!i){or("AC-3");break}case 129:s.ac3?-1===n.audioPid&&(n.audioPid=a,n.segmentAudioCodec="ac3"):A.log("AC-3 audio found, not supported in this browser");break;case 6:if(-1===n.audioPid&&o>0){let i=e+5,r=o;for(;r>2;){if(106===t[i])!0!==s.ac3?A.log("AC-3 audio found, not supported in this browser for now"):(n.audioPid=a,n.segmentAudioCodec="ac3");const e=t[i+1]+2;i+=e,r-=e}}break;case 194:case 135:return ar(r,new Error("Unsupported EC-3 in M2TS found")),n;case 36:return ar(r,new Error("Unsupported HEVC in M2TS found")),n}e+=o+5}return n}function ar(t,e,s){A.warn(`parsing error: ${e.message}`),t.emit(p.ERROR,p.ERROR,{type:v.MEDIA_ERROR,details:y.FRAG_PARSING_ERROR,fatal:!1,levelRetry:s,error:e,reason:e.message})}function or(t){A.log(`${t} with AES-128-CBC encryption found in unencrypted stream`)}function lr(t){let e,s,i,r,n,a=0;const o=t.data;if(!t||0===t.size)return null;for(;o[0].length<19&&o.length>1;)o[0]=Bt(o[0],o[1]),o.splice(1,1);e=o[0];if(1===(e[0]<<16)+(e[1]<<8)+e[2]){if(s=(e[4]<<8)+e[5],s&&s>t.size-6)return null;const l=e[7];192&l&&(r=536870912*(14&e[9])+4194304*(255&e[10])+16384*(254&e[11])+128*(255&e[12])+(254&e[13])/2,64&l?(n=536870912*(14&e[14])+4194304*(255&e[15])+16384*(254&e[16])+128*(255&e[17])+(254&e[18])/2,r-n>54e5&&(A.warn(`${Math.round((r-n)/9e4)}s delta between PTS and DTS, align them`),r=n)):n=r),i=e[8];let h=i+9;if(t.size<=h)return null;t.size-=h;const d=new Uint8Array(t.size);for(let t=0,s=o.length;t<s;t++){e=o[t];let s=e.byteLength;if(h){if(h>s){h-=s;continue}e=e.subarray(h),s-=h,h=0}d.set(e,a),a+=s}return s&&(s-=i+3),{data:d,pts:r,dts:n,len:s}}return null}class hr{static getSilentFrame(t,e){if("mp4a.40.2"===t){if(1===e)return new Uint8Array([0,200,0,128,35,128]);if(2===e)return new Uint8Array([33,0,73,144,2,25,0,35,128]);if(3===e)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,142]);if(4===e)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,128,44,128,8,2,56]);if(5===e)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,130,48,4,153,0,33,144,2,56]);if(6===e)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,130,48,4,153,0,33,144,2,0,178,0,32,8,224])}else{if(1===e)return new Uint8Array([1,64,34,128,163,78,230,128,186,8,0,0,0,28,6,241,193,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94]);if(2===e)return new Uint8Array([1,64,34,128,163,94,230,128,186,8,0,0,0,0,149,0,6,241,161,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94]);if(3===e)return new Uint8Array([1,64,34,128,163,94,230,128,186,8,0,0,0,0,149,0,6,241,161,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94])}}}const dr=Math.pow(2,32)-1;class cr{static init(){let t;for(t in cr.types={avc1:[],avcC:[],btrt:[],dinf:[],dref:[],esds:[],ftyp:[],hdlr:[],mdat:[],mdhd:[],mdia:[],mfhd:[],minf:[],moof:[],moov:[],mp4a:[],".mp3":[],dac3:[],"ac-3":[],mvex:[],mvhd:[],pasp:[],sdtp:[],stbl:[],stco:[],stsc:[],stsd:[],stsz:[],stts:[],tfdt:[],tfhd:[],traf:[],trak:[],trun:[],trex:[],tkhd:[],vmhd:[],smhd:[]},cr.types)cr.types.hasOwnProperty(t)&&(cr.types[t]=[t.charCodeAt(0),t.charCodeAt(1),t.charCodeAt(2),t.charCodeAt(3)]);const e=new Uint8Array([0,0,0,0,0,0,0,0,118,105,100,101,0,0,0,0,0,0,0,0,0,0,0,0,86,105,100,101,111,72,97,110,100,108,101,114,0]),s=new Uint8Array([0,0,0,0,0,0,0,0,115,111,117,110,0,0,0,0,0,0,0,0,0,0,0,0,83,111,117,110,100,72,97,110,100,108,101,114,0]);cr.HDLR_TYPES={video:e,audio:s};const i=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,12,117,114,108,32,0,0,0,1]),r=new Uint8Array([0,0,0,0,0,0,0,0]);cr.STTS=cr.STSC=cr.STCO=r,cr.STSZ=new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0]),cr.VMHD=new Uint8Array([0,0,0,1,0,0,0,0,0,0,0,0]),cr.SMHD=new Uint8Array([0,0,0,0,0,0,0,0]),cr.STSD=new Uint8Array([0,0,0,0,0,0,0,1]);const n=new Uint8Array([105,115,111,109]),a=new Uint8Array([97,118,99,49]),o=new Uint8Array([0,0,0,1]);cr.FTYP=cr.box(cr.types.ftyp,n,o,n,a),cr.DINF=cr.box(cr.types.dinf,cr.box(cr.types.dref,i))}static box(t,...e){let s=8,i=e.length;const r=i;for(;i--;)s+=e[i].byteLength;const n=new Uint8Array(s);for(n[0]=s>>24&255,n[1]=s>>16&255,n[2]=s>>8&255,n[3]=255&s,n.set(t,4),i=0,s=8;i<r;i++)n.set(e[i],s),s+=e[i].byteLength;return n}static hdlr(t){return cr.box(cr.types.hdlr,cr.HDLR_TYPES[t])}static mdat(t){return cr.box(cr.types.mdat,t)}static mdhd(t,e){e*=t;const s=Math.floor(e/(dr+1)),i=Math.floor(e%(dr+1));return cr.box(cr.types.mdhd,new Uint8Array([1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,t>>24&255,t>>16&255,t>>8&255,255&t,s>>24,s>>16&255,s>>8&255,255&s,i>>24,i>>16&255,i>>8&255,255&i,85,196,0,0]))}static mdia(t){return cr.box(cr.types.mdia,cr.mdhd(t.timescale,t.duration),cr.hdlr(t.type),cr.minf(t))}static mfhd(t){return cr.box(cr.types.mfhd,new Uint8Array([0,0,0,0,t>>24,t>>16&255,t>>8&255,255&t]))}static minf(t){return"audio"===t.type?cr.box(cr.types.minf,cr.box(cr.types.smhd,cr.SMHD),cr.DINF,cr.stbl(t)):cr.box(cr.types.minf,cr.box(cr.types.vmhd,cr.VMHD),cr.DINF,cr.stbl(t))}static moof(t,e,s){return cr.box(cr.types.moof,cr.mfhd(t),cr.traf(s,e))}static moov(t){let e=t.length;const s=[];for(;e--;)s[e]=cr.trak(t[e]);return cr.box.apply(null,[cr.types.moov,cr.mvhd(t[0].timescale,t[0].duration)].concat(s).concat(cr.mvex(t)))}static mvex(t){let e=t.length;const s=[];for(;e--;)s[e]=cr.trex(t[e]);return cr.box.apply(null,[cr.types.mvex,...s])}static mvhd(t,e){e*=t;const s=Math.floor(e/(dr+1)),i=Math.floor(e%(dr+1)),r=new Uint8Array([1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,t>>24&255,t>>16&255,t>>8&255,255&t,s>>24,s>>16&255,s>>8&255,255&s,i>>24,i>>16&255,i>>8&255,255&i,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255]);return cr.box(cr.types.mvhd,r)}static sdtp(t){const e=t.samples||[],s=new Uint8Array(4+e.length);let i,r;for(i=0;i<e.length;i++)r=e[i].flags,s[i+4]=r.dependsOn<<4|r.isDependedOn<<2|r.hasRedundancy;return cr.box(cr.types.sdtp,s)}static stbl(t){return cr.box(cr.types.stbl,cr.stsd(t),cr.box(cr.types.stts,cr.STTS),cr.box(cr.types.stsc,cr.STSC),cr.box(cr.types.stsz,cr.STSZ),cr.box(cr.types.stco,cr.STCO))}static avc1(t){let e,s,i,r=[],n=[];for(e=0;e<t.sps.length;e++)s=t.sps[e],i=s.byteLength,r.push(i>>>8&255),r.push(255&i),r=r.concat(Array.prototype.slice.call(s));for(e=0;e<t.pps.length;e++)s=t.pps[e],i=s.byteLength,n.push(i>>>8&255),n.push(255&i),n=n.concat(Array.prototype.slice.call(s));const a=cr.box(cr.types.avcC,new Uint8Array([1,r[3],r[4],r[5],255,224|t.sps.length].concat(r).concat([t.pps.length]).concat(n))),o=t.width,l=t.height,h=t.pixelRatio[0],d=t.pixelRatio[1];return cr.box(cr.types.avc1,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,o>>8&255,255&o,l>>8&255,255&l,0,72,0,0,0,72,0,0,0,0,0,0,0,1,18,100,97,105,108,121,109,111,116,105,111,110,47,104,108,115,46,106,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,17,17]),a,cr.box(cr.types.btrt,new Uint8Array([0,28,156,128,0,45,198,192,0,45,198,192])),cr.box(cr.types.pasp,new Uint8Array([h>>24,h>>16&255,h>>8&255,255&h,d>>24,d>>16&255,d>>8&255,255&d])))}static esds(t){const e=t.config.length;return new Uint8Array([0,0,0,0,3,23+e,0,1,0,4,15+e,64,21,0,0,0,0,0,0,0,0,0,0,0,5].concat([e]).concat(t.config).concat([6,1,2]))}static audioStsd(t){const e=t.samplerate;return new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,t.channelCount,0,16,0,0,0,0,e>>8&255,255&e,0,0])}static mp4a(t){return cr.box(cr.types.mp4a,cr.audioStsd(t),cr.box(cr.types.esds,cr.esds(t)))}static mp3(t){return cr.box(cr.types[".mp3"],cr.audioStsd(t))}static ac3(t){return cr.box(cr.types["ac-3"],cr.audioStsd(t),cr.box(cr.types.dac3,t.config))}static stsd(t){return"audio"===t.type?"mp3"===t.segmentCodec&&"mp3"===t.codec?cr.box(cr.types.stsd,cr.STSD,cr.mp3(t)):"ac3"===t.segmentCodec?cr.box(cr.types.stsd,cr.STSD,cr.ac3(t)):cr.box(cr.types.stsd,cr.STSD,cr.mp4a(t)):cr.box(cr.types.stsd,cr.STSD,cr.avc1(t))}static tkhd(t){const e=t.id,s=t.duration*t.timescale,i=t.width,r=t.height,n=Math.floor(s/(dr+1)),a=Math.floor(s%(dr+1));return cr.box(cr.types.tkhd,new Uint8Array([1,0,0,7,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,e>>24&255,e>>16&255,e>>8&255,255&e,0,0,0,0,n>>24,n>>16&255,n>>8&255,255&n,a>>24,a>>16&255,a>>8&255,255&a,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,i>>8&255,255&i,0,0,r>>8&255,255&r,0,0]))}static traf(t,e){const s=cr.sdtp(t),i=t.id,r=Math.floor(e/(dr+1)),n=Math.floor(e%(dr+1));return cr.box(cr.types.traf,cr.box(cr.types.tfhd,new Uint8Array([0,0,0,0,i>>24,i>>16&255,i>>8&255,255&i])),cr.box(cr.types.tfdt,new Uint8Array([1,0,0,0,r>>24,r>>16&255,r>>8&255,255&r,n>>24,n>>16&255,n>>8&255,255&n])),cr.trun(t,s.length+16+20+8+16+8+8),s)}static trak(t){return t.duration=t.duration||4294967295,cr.box(cr.types.trak,cr.tkhd(t),cr.mdia(t))}static trex(t){const e=t.id;return cr.box(cr.types.trex,new Uint8Array([0,0,0,0,e>>24,e>>16&255,e>>8&255,255&e,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1]))}static trun(t,e){const s=t.samples||[],i=s.length,r=12+16*i,n=new Uint8Array(r);let a,o,l,h,d,c;for(e+=8+r,n.set(["video"===t.type?1:0,0,15,1,i>>>24&255,i>>>16&255,i>>>8&255,255&i,e>>>24&255,e>>>16&255,e>>>8&255,255&e],0),a=0;a<i;a++)o=s[a],l=o.duration,h=o.size,d=o.flags,c=o.cts,n.set([l>>>24&255,l>>>16&255,l>>>8&255,255&l,h>>>24&255,h>>>16&255,h>>>8&255,255&h,d.isLeading<<2|d.dependsOn,d.isDependedOn<<6|d.hasRedundancy<<4|d.paddingValue<<1|d.isNonSync,61440&d.degradPrio,15&d.degradPrio,c>>>24&255,c>>>16&255,c>>>8&255,255&c],12+16*a);return cr.box(cr.types.trun,n)}static initSegment(t){cr.types||cr.init();const e=cr.moov(t);return Bt(cr.FTYP,e)}}cr.types=void 0,cr.HDLR_TYPES=void 0,cr.STTS=void 0,cr.STSC=void 0,cr.STCO=void 0,cr.STSZ=void 0,cr.VMHD=void 0,cr.SMHD=void 0,cr.STSD=void 0,cr.FTYP=void 0,cr.DINF=void 0;function ur(t,e,s=1,i=!1){const r=t*e*s;return i?Math.round(r):r}function fr(t,e=!1){return ur(t,1e3,1/9e4,e)}let gr,mr=null,pr=null;class vr{constructor(t,e,s,i=""){if(this.observer=void 0,this.config=void 0,this.typeSupported=void 0,this.ISGenerated=!1,this._initPTS=null,this._initDTS=null,this.nextAvcDts=null,this.nextAudioPts=null,this.videoSampleDuration=null,this.isAudioContiguous=!1,this.isVideoContiguous=!1,this.videoTrackConfig=void 0,this.observer=t,this.config=e,this.typeSupported=s,this.ISGenerated=!1,null===mr){const t=(navigator.userAgent||"").match(/Chrome\/(\d+)/i);mr=t?parseInt(t[1]):0}if(null===pr){const t=navigator.userAgent.match(/Safari\/(\d+)/i);pr=t?parseInt(t[1]):0}}destroy(){this.config=this.videoTrackConfig=this._initPTS=this._initDTS=null}resetTimeStamp(t){A.log("[mp4-remuxer]: initPTS & initDTS reset"),this._initPTS=this._initDTS=t}resetNextTimestamp(){A.log("[mp4-remuxer]: reset next timestamp"),this.isVideoContiguous=!1,this.isAudioContiguous=!1}resetInitSegment(){A.log("[mp4-remuxer]: ISGenerated flag reset"),this.ISGenerated=!1,this.videoTrackConfig=void 0}getVideoStartPts(t){let e=!1;const s=t.reduce(((t,s)=>{const i=s.pts-t;return i<-4294967296?(e=!0,yr(t,s.pts)):i>0?t:s.pts}),t[0].pts);return e&&A.debug("PTS rollover detected"),s}remux(t,e,s,i,r,n,a,o){let l,h,d,c,u,f,g=r,m=r;const p=t.pid>-1,v=e.pid>-1,y=e.samples.length,E=t.samples.length>0,T=a&&y>0||y>1;if((!p||E)&&(!v||T)||this.ISGenerated||a){if(this.ISGenerated){var S,L,R,b;const t=this.videoTrackConfig;!t||e.width===t.width&&e.height===t.height&&(null==(S=e.pixelRatio)?void 0:S[0])===(null==(L=t.pixelRatio)?void 0:L[0])&&(null==(R=e.pixelRatio)?void 0:R[1])===(null==(b=t.pixelRatio)?void 0:b[1])||this.resetInitSegment()}else d=this.generateIS(t,e,r,n);const s=this.isVideoContiguous;let i,a=-1;if(T&&(a=function(t){for(let e=0;e<t.length;e++)if(t[e].key)return e;return-1}(e.samples),!s&&this.config.forceKeyFrameOnDiscontinuity))if(f=!0,a>0){A.warn(`[mp4-remuxer]: Dropped ${a} out of ${y} video samples due to a missing keyframe`);const t=this.getVideoStartPts(e.samples);e.samples=e.samples.slice(a),e.dropped+=a,m+=(e.samples[0].pts-t)/e.inputTimeScale,i=m}else-1===a&&(A.warn(`[mp4-remuxer]: No keyframe found out of ${y} video samples`),f=!1);if(this.ISGenerated){if(E&&T){const s=this.getVideoStartPts(e.samples),i=(yr(t.samples[0].pts,s)-s)/e.inputTimeScale;g+=Math.max(0,i),m+=Math.max(0,-i)}if(E){if(t.samplerate||(A.warn("[mp4-remuxer]: regenerate InitSegment as audio detected"),d=this.generateIS(t,e,r,n)),h=this.remuxAudio(t,g,this.isAudioContiguous,n,v||T||o===Ie?m:void 0),T){const i=h?h.endPTS-h.startPTS:0;e.inputTimeScale||(A.warn("[mp4-remuxer]: regenerate InitSegment as video detected"),d=this.generateIS(t,e,r,n)),l=this.remuxVideo(e,m,s,i)}}else T&&(l=this.remuxVideo(e,m,s,0));l&&(l.firstKeyFrame=a,l.independent=-1!==a,l.firstKeyFramePTS=i)}}return this.ISGenerated&&this._initPTS&&this._initDTS&&(s.samples.length&&(u=Er(s,r,this._initPTS,this._initDTS)),i.samples.length&&(c=Tr(i,r,this._initPTS))),{audio:h,video:l,initSegment:d,independent:f,text:c,id3:u}}generateIS(t,e,s,i){const r=t.samples,n=e.samples,a=this.typeSupported,o={},l=this._initPTS;let h,d,c,u=!l||i,f="audio/mp4";if(u&&(h=d=1/0),t.config&&r.length){switch(t.timescale=t.samplerate,t.segmentCodec){case"mp3":a.mpeg?(f="audio/mpeg",t.codec=""):a.mp3&&(t.codec="mp3");break;case"ac3":t.codec="ac-3"}o.audio={id:"audio",container:f,codec:t.codec,initSegment:"mp3"===t.segmentCodec&&a.mpeg?new Uint8Array(0):cr.initSegment([t]),metadata:{channelCount:t.channelCount}},u&&(c=t.inputTimeScale,l&&c===l.timescale?u=!1:h=d=r[0].pts-Math.round(c*s))}if(e.sps&&e.pps&&n.length){if(e.timescale=e.inputTimeScale,o.video={id:"main",container:"video/mp4",codec:e.codec,initSegment:cr.initSegment([e]),metadata:{width:e.width,height:e.height}},u)if(c=e.inputTimeScale,l&&c===l.timescale)u=!1;else{const t=this.getVideoStartPts(n),e=Math.round(c*s);d=Math.min(d,yr(n[0].dts,t)-e),h=Math.min(h,t-e)}this.videoTrackConfig={width:e.width,height:e.height,pixelRatio:e.pixelRatio}}if(Object.keys(o).length)return this.ISGenerated=!0,u?(this._initPTS={baseTime:h,timescale:c},this._initDTS={baseTime:d,timescale:c}):h=c=void 0,{tracks:o,initPTS:h,timescale:c}}remuxVideo(t,e,s,i){const r=t.inputTimeScale,n=t.samples,a=[],o=n.length,l=this._initPTS;let h,d,c=this.nextAvcDts,f=8,g=this.videoSampleDuration,m=Number.POSITIVE_INFINITY,E=Number.NEGATIVE_INFINITY,T=!1;if(!s||null===c){const t=e*r,i=n[0].pts-yr(n[0].dts,n[0].pts);mr&&null!==c&&Math.abs(t-i-c)<15e3?s=!0:c=t-i}const S=l.baseTime*r/l.timescale;for(let t=0;t<o;t++){const e=n[t];e.pts=yr(e.pts-S,c),e.dts=yr(e.dts-S,c),e.dts<n[t>0?t-1:t].dts&&(T=!0)}T&&n.sort((function(t,e){const s=t.dts-e.dts,i=t.pts-e.pts;return s||i})),h=n[0].dts,d=n[n.length-1].dts;const L=d-h,R=L?Math.round(L/(o-1)):g||t.inputTimeScale/30;if(s){const t=h-c,s=t>R,i=t<-1;if((s||i)&&(s?A.warn(`AVC: ${fr(t,!0)} ms (${t}dts) hole between fragments detected at ${e.toFixed(3)}`):A.warn(`AVC: ${fr(-t,!0)} ms (${t}dts) overlapping between fragments detected at ${e.toFixed(3)}`),!i||c>=n[0].pts||mr)){h=c;const e=n[0].pts-t;if(s)n[0].dts=h,n[0].pts=e;else for(let s=0;s<n.length&&!(n[s].dts>e);s++)n[s].dts-=t,n[s].pts-=t;A.log(`Video: Initial PTS/DTS adjusted: ${fr(e,!0)}/${fr(h,!0)}, delta: ${fr(t,!0)} ms`)}}h=Math.max(0,h);let b=0,k=0,w=h;for(let t=0;t<o;t++){const e=n[t],s=e.units,i=s.length;let r=0;for(let t=0;t<i;t++)r+=s[t].data.length;k+=r,b+=i,e.length=r,e.dts<w?(e.dts=w,w+=R/4|0||1):w=e.dts,m=Math.min(e.pts,m),E=Math.max(e.pts,E)}d=n[o-1].dts;const D=k+4*b+8;let I;try{I=new Uint8Array(D)}catch(t){return void this.observer.emit(p.ERROR,p.ERROR,{type:v.MUX_ERROR,details:y.REMUX_ALLOC_ERROR,fatal:!1,error:t,bytes:D,reason:`fail allocating video mdat ${D}`})}const C=new DataView(I.buffer);C.setUint32(0,D),I.set(cr.types.mdat,4);let _=!1,x=Number.POSITIVE_INFINITY,P=Number.POSITIVE_INFINITY,M=Number.NEGATIVE_INFINITY,F=Number.NEGATIVE_INFINITY;for(let t=0;t<o;t++){const e=n[t],s=e.units;let l,h=0;for(let t=0,e=s.length;t<e;t++){const e=s[t],i=e.data,r=e.data.byteLength;C.setUint32(f,r),f+=4,I.set(i,f),f+=r,h+=4+r}if(t<o-1)g=n[t+1].dts-e.dts,l=n[t+1].pts-e.pts;else{const s=this.config,a=t>0?e.dts-n[t-1].dts:R;if(l=t>0?e.pts-n[t-1].pts:R,s.stretchShortVideoTrack&&null!==this.nextAudioPts){const t=Math.floor(s.maxBufferHole*r),n=(i?m+i*r:this.nextAudioPts)-e.pts;n>t?(g=n-a,g<0?g=a:_=!0,A.log(`[mp4-remuxer]: It is approximately ${n/90} ms to the next segment; using duration ${g/90} ms for the last video frame.`)):g=a}else g=a}const d=Math.round(e.pts-e.dts);x=Math.min(x,g),M=Math.max(M,g),P=Math.min(P,l),F=Math.max(F,l),a.push(new Sr(e.key,g,h,d))}if(a.length)if(mr){if(mr<70){const t=a[0].flags;t.dependsOn=2,t.isNonSync=0}}else if(pr&&F-P<M-x&&R/M<.025&&0===a[0].cts){A.warn("Found irregular gaps in sample duration. Using PTS instead of DTS to determine MP4 sample duration.");let t=h;for(let e=0,s=a.length;e<s;e++){const i=t+a[e].duration,r=t+a[e].cts;if(e<s-1){const t=i+a[e+1].cts;a[e].duration=t-r}else a[e].duration=e?a[e-1].duration:R;a[e].cts=0,t=i}}g=_||!g?R:g,this.nextAvcDts=c=d+g,this.videoSampleDuration=g,this.isVideoContiguous=!0;const O={data1:cr.moof(t.sequenceNumber++,h,u({},t,{samples:a})),data2:I,startPTS:m/r,endPTS:(E+g)/r,startDTS:h/r,endDTS:c/r,type:"video",hasAudio:!1,hasVideo:!0,nb:a.length,dropped:t.dropped};return t.samples=[],t.dropped=0,O}getSamplesPerFrame(t){switch(t.segmentCodec){case"mp3":return 1152;case"ac3":return 1536;default:return 1024}}remuxAudio(t,e,s,i,r){const n=t.inputTimeScale,a=n/(t.samplerate?t.samplerate:n),o=this.getSamplesPerFrame(t),l=o*a,h=this._initPTS,d="mp3"===t.segmentCodec&&this.typeSupported.mpeg,c=[],f=void 0!==r;let g=t.samples,m=d?0:8,E=this.nextAudioPts||-1;const T=e*n,S=h.baseTime*n/h.timescale;if(this.isAudioContiguous=s=s||g.length&&E>0&&(i&&Math.abs(T-E)<9e3||Math.abs(yr(g[0].pts-S,T)-E)<20*l),g.forEach((function(t){t.pts=yr(t.pts-S,T)})),!s||E<0){if(g=g.filter((t=>t.pts>=0)),!g.length)return;E=0===r?0:i&&!f?Math.max(0,T):g[0].pts}if("aac"===t.segmentCodec){const e=this.config.maxAudioFramesDrift;for(let s=0,i=E;s<g.length;s++){const r=g[s],a=r.pts,o=a-i,h=Math.abs(1e3*o/n);if(o<=-e*l&&f)0===s&&(A.warn(`Audio frame @ ${(a/n).toFixed(3)}s overlaps nextAudioPts by ${Math.round(1e3*o/n)} ms.`),this.nextAudioPts=E=i=a);else if(o>=e*l&&h<1e4&&f){let e=Math.round(o/l);i=a-e*l,i<0&&(e--,i+=l),0===s&&(this.nextAudioPts=E=i),A.warn(`[mp4-remuxer]: Injecting ${e} audio frame @ ${(i/n).toFixed(3)}s due to ${Math.round(1e3*o/n)} ms gap.`);for(let n=0;n<e;n++){const e=Math.max(i,0);let n=hr.getSilentFrame(t.manifestCodec||t.codec,t.channelCount);n||(A.log("[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead."),n=r.unit.subarray()),g.splice(s,0,{unit:n,pts:e}),i+=l,s++}}r.pts=i,i+=l}}let L,R=null,b=null,k=0,w=g.length;for(;w--;)k+=g[w].unit.byteLength;for(let e=0,i=g.length;e<i;e++){const i=g[e],r=i.unit;let n=i.pts;if(null!==b){c[e-1].duration=Math.round((n-b)/a)}else{if(s&&"aac"===t.segmentCodec&&(n=E),R=n,!(k>0))return;k+=m;try{L=new Uint8Array(k)}catch(t){return void this.observer.emit(p.ERROR,p.ERROR,{type:v.MUX_ERROR,details:y.REMUX_ALLOC_ERROR,fatal:!1,error:t,bytes:k,reason:`fail allocating audio mdat ${k}`})}if(!d){new DataView(L.buffer).setUint32(0,k),L.set(cr.types.mdat,4)}}L.set(r,m);const l=r.byteLength;m+=l,c.push(new Sr(!0,o,l,0)),b=n}const D=c.length;if(!D)return;const I=c[c.length-1];this.nextAudioPts=E=b+a*I.duration;const C=d?new Uint8Array(0):cr.moof(t.sequenceNumber++,R/a,u({},t,{samples:c}));t.samples=[];const _=R/n,x=E/n,P={data1:C,data2:L,startPTS:_,endPTS:x,startDTS:_,endDTS:x,type:"audio",hasAudio:!0,hasVideo:!1,nb:D};return this.isAudioContiguous=!0,P}remuxEmptyAudio(t,e,s,i){const r=t.inputTimeScale,n=r/(t.samplerate?t.samplerate:r),a=this.nextAudioPts,o=this._initDTS,l=9e4*o.baseTime/o.timescale,h=(null!==a?a:i.startDTS*r)+l,d=i.endDTS*r+l,c=1024*n,u=Math.ceil((d-h)/c),f=hr.getSilentFrame(t.manifestCodec||t.codec,t.channelCount);if(A.warn("[mp4-remuxer]: remux empty Audio"),!f)return void A.trace("[mp4-remuxer]: Unable to remuxEmptyAudio since we were unable to get a silent frame for given audio codec");const g=[];for(let t=0;t<u;t++){const e=h+t*c;g.push({unit:f,pts:e,dts:e})}return t.samples=g,this.remuxAudio(t,e,s,!1)}}function yr(t,e){let s;if(null===e)return t;for(s=e<t?-8589934592:8589934592;Math.abs(t-e)>4294967296;)t+=s;return t}function Er(t,e,s,i){const r=t.samples.length;if(!r)return;const n=t.inputTimeScale;for(let a=0;a<r;a++){const r=t.samples[a];r.pts=yr(r.pts-s.baseTime*n/s.timescale,e*n)/n,r.dts=yr(r.dts-i.baseTime*n/i.timescale,e*n)/n}const a=t.samples;return t.samples=[],{samples:a}}function Tr(t,e,s){const i=t.samples.length;if(!i)return;const r=t.inputTimeScale;for(let n=0;n<i;n++){const i=t.samples[n];i.pts=yr(i.pts-s.baseTime*r/s.timescale,e*r)/r}t.samples.sort(((t,e)=>t.pts-e.pts));const n=t.samples;return t.samples=[],{samples:n}}class Sr{constructor(t,e,s,i){this.size=void 0,this.duration=void 0,this.cts=void 0,this.flags=void 0,this.duration=e,this.size=s,this.cts=i,this.flags={isLeading:0,isDependedOn:0,hasRedundancy:0,degradPrio:0,dependsOn:t?2:1,isNonSync:t?0:1}}}function Lr(t,e){const s=null==t?void 0:t.codec;if(s&&s.length>4)return s;if(e===C){if("ec-3"===s||"ac-3"===s||"alac"===s)return s;if("fLaC"===s||"Opus"===s){return he(s,!1)}const t="mp4a.40.5";return A.info(`Parsed audio codec "${s}" or audio object type not handled. Using "${t}"`),t}return A.warn(`Unhandled video codec "${s}"`),"hvc1"===s||"hev1"===s?"hvc1.1.6.L120.90":"av01"===s?"av01.0.04M.08":"avc1.42e01e"}try{gr=self.performance.now.bind(self.performance)}catch(t){A.debug("Unable to use Performance API on this environment"),gr=null==$?void 0:$.Date.now}const Ar=[{demux:class{constructor(t,e){this.remainderData=null,this.timeOffset=0,this.config=void 0,this.videoTrack=void 0,this.audioTrack=void 0,this.id3Track=void 0,this.txtTrack=void 0,this.config=e}resetTimeStamp(){}resetInitSegment(t,e,s,i){const r=this.videoTrack=ki("video",1),n=this.audioTrack=ki("audio",1),a=this.txtTrack=ki("text",1);if(this.id3Track=ki("id3",1),this.timeOffset=0,null==t||!t.byteLength)return;const o=xt(t);if(o.video){const{id:t,timescale:e,codec:s}=o.video;r.id=t,r.timescale=a.timescale=e,r.codec=s}if(o.audio){const{id:t,timescale:e,codec:s}=o.audio;n.id=t,n.timescale=e,n.codec=s}a.id=At.text,r.sampleDuration=0,r.duration=n.duration=i}resetContiguity(){this.remainderData=null}static probe(t){return function(t){const e=t.byteLength;for(let s=0;s<e;){const i=kt(t,s);if(i>8&&109===t[s+4]&&111===t[s+5]&&111===t[s+6]&&102===t[s+7])return!0;s=i>1?s+i:e}return!1}(t)}demux(t,e){this.timeOffset=e;let s=t;const i=this.videoTrack,r=this.txtTrack;if(this.config.progressive){this.remainderData&&(s=Bt(this.remainderData,t));const e=function(t){const e={valid:null,remainder:null},s=Ct(t,["moof"]);if(s.length<2)return e.remainder=t,e;const i=s[s.length-1];return e.valid=st(t,0,i.byteOffset-8),e.remainder=st(t,i.byteOffset-8),e}(s);this.remainderData=e.remainder,i.samples=e.valid||new Uint8Array}else i.samples=s;const n=this.extractID3Track(i,e);return r.samples=$t(e,i),{videoTrack:i,audioTrack:this.audioTrack,id3Track:n,textTrack:this.txtTrack}}flush(){const t=this.timeOffset,e=this.videoTrack,s=this.txtTrack;e.samples=this.remainderData||new Uint8Array,this.remainderData=null;const i=this.extractID3Track(e,this.timeOffset);return s.samples=$t(t,e),{videoTrack:e,audioTrack:ki(),id3Track:i,textTrack:ki()}}extractID3Track(t,e){const s=this.id3Track;if(t.samples.length){const i=Ct(t.samples,["emsg"]);i&&i.forEach((t=>{const i=function(t){const e=t[0];let s="",i="",r=0,n=0,a=0,o=0,l=0,h=0;if(0===e){for(;"\0"!==Rt(t.subarray(h,h+1));)s+=Rt(t.subarray(h,h+1)),h+=1;for(s+=Rt(t.subarray(h,h+1)),h+=1;"\0"!==Rt(t.subarray(h,h+1));)i+=Rt(t.subarray(h,h+1)),h+=1;i+=Rt(t.subarray(h,h+1)),h+=1,r=kt(t,12),n=kt(t,16),o=kt(t,20),l=kt(t,24),h=28}else if(1===e){h+=4,r=kt(t,h),h+=4;const e=kt(t,h);h+=4;const n=kt(t,h);for(h+=4,a=2**32*e+n,g(a)||(a=Number.MAX_SAFE_INTEGER,A.warn("Presentation time exceeds safe integer limit and wrapped to max safe integer in parsing emsg box")),o=kt(t,h),h+=4,l=kt(t,h),h+=4;"\0"!==Rt(t.subarray(h,h+1));)s+=Rt(t.subarray(h,h+1)),h+=1;for(s+=Rt(t.subarray(h,h+1)),h+=1;"\0"!==Rt(t.subarray(h,h+1));)i+=Rt(t.subarray(h,h+1)),h+=1;i+=Rt(t.subarray(h,h+1)),h+=1}return{schemeIdUri:s,value:i,timeScale:r,presentationTime:a,presentationTimeDelta:n,eventDuration:o,id:l,payload:t.subarray(h,t.byteLength)}}(t);if(ji.test(i.schemeIdUri)){const t=f(i.presentationTime)?i.presentationTime/i.timeScale:e+i.presentationTimeDelta/i.timeScale;let r=4294967295===i.eventDuration?Number.POSITIVE_INFINITY:i.eventDuration/i.timeScale;r<=.001&&(r=Number.POSITIVE_INFINITY);const n=i.payload;s.samples.push({data:n,len:n.byteLength,dts:t,pts:t,type:Ge,duration:r})}}))}return s}demuxSampleAes(t,e,s){return Promise.reject(new Error("The MP4 demuxer does not support SAMPLE-AES decryption"))}destroy(){}},remux:class{constructor(){this.emitInitSegment=!1,this.audioCodec=void 0,this.videoCodec=void 0,this.initData=void 0,this.initPTS=null,this.initTracks=void 0,this.lastEndTime=null}destroy(){}resetTimeStamp(t){this.initPTS=t,this.lastEndTime=null}resetNextTimestamp(){this.lastEndTime=null}resetInitSegment(t,e,s,i){this.audioCodec=e,this.videoCodec=s,this.generateInitSegment(function(t,e){if(!t||!e)return t;const s=e.keyId;s&&e.isCommonEncryption&&Ct(t,["moov","trak"]).forEach((t=>{const e=Ct(t,["mdia","minf","stbl","stsd"])[0].subarray(8);let i=Ct(e,["enca"]);const r=i.length>0;r||(i=Ct(e,["encv"])),i.forEach((t=>{Ct(r?t.subarray(28):t.subarray(78),["sinf"]).forEach((t=>{const e=Nt(t);if(e){const t=e.subarray(8,24);t.some((t=>0!==t))||(A.log(`[eme] Patching keyId in 'enc${r?"a":"v"}>sinf>>tenc' box: ${Tt(t)} -> ${Tt(s)}`),e.set(s,8))}}))}))}));return t}(t,i)),this.emitInitSegment=!0}generateInitSegment(t){let{audioCodec:e,videoCodec:s}=this;if(null==t||!t.byteLength)return this.initTracks=void 0,void(this.initData=void 0);const i=this.initData=xt(t);i.audio&&(e=Lr(i.audio,C)),i.video&&(s=Lr(i.video,_));const r={};i.audio&&i.video?r.audiovideo={container:"video/mp4",codec:e+","+s,initSegment:t,id:"main"}:i.audio?r.audio={container:"audio/mp4",codec:e,initSegment:t,id:"audio"}:i.video?r.video={container:"video/mp4",codec:s,initSegment:t,id:"main"}:A.warn("[passthrough-remuxer.ts]: initSegment does not contain moov or trak boxes."),this.initTracks=r}remux(t,e,s,i,r,n){var a,o;let{initPTS:l,lastEndTime:h}=this;const d={audio:void 0,video:void 0,text:i,id3:s,initSegment:void 0};f(h)||(h=this.lastEndTime=r||0);const c=e.samples;if(null==c||!c.length)return d;const u={initPTS:void 0,timescale:1};let g=this.initData;if(null!=(a=g)&&a.length||(this.generateInitSegment(c),g=this.initData),null==(o=g)||!o.length)return A.warn("[passthrough-remuxer.ts]: Failed to generate initSegment."),d;this.emitInitSegment&&(u.tracks=this.initTracks,this.emitInitSegment=!1);const m=function(t,e){let s=0,i=0,r=0;const n=Ct(t,["moof","traf"]);for(let t=0;t<n.length;t++){const a=n[t],o=Ct(a,["tfhd"])[0],l=e[kt(o,4)];if(!l)continue;const h=l.default,d=kt(o,0)|(null==h?void 0:h.flags);let c=null==h?void 0:h.duration;8&d&&(c=kt(o,2&d?12:8));const u=l.timescale||9e4,f=Ct(a,["trun"]);for(let t=0;t<f.length;t++)s=Ut(f[t]),!s&&c&&(s=c*kt(f[t],4)),l.type===_?i+=s/u:l.type===C&&(r+=s/u)}if(0===i&&0===r){let e=1/0,s=0,i=0;const r=Ct(t,["sidx"]);for(let t=0;t<r.length;t++){const n=_t(r[t]);if(null!=n&&n.references){e=Math.min(e,n.earliestPresentationTime/n.timescale);const t=n.references.reduce(((t,e)=>t+e.info.duration||0),0);s=Math.max(s,t+n.earliestPresentationTime/n.timescale),i=s-e}}if(i&&f(i))return i}return i||r}(c,g),p=function(t,e){return Ct(e,["moof","traf"]).reduce(((e,s)=>{const i=Ct(s,["tfdt"])[0],r=i[0],n=Ct(s,["tfhd"]).reduce(((e,s)=>{const n=kt(s,4),a=t[n];if(a){let t=kt(i,4);if(1===r){if(t===St)return A.warn("[mp4-demuxer]: Ignoring assumed invalid signed 64-bit track fragment decode time"),e;t*=St+1,t+=kt(i,8)}const s=t/(a.timescale||9e4);if(f(s)&&(null===e||s<e))return s}return e}),null);return null!==n&&f(n)&&(null===e||n<e)?n:e}),null)}(g,c),v=null===p?r:p;(function(t,e,s,i){if(null===t)return!0;const r=Math.max(i,1),n=e-t.baseTime/t.timescale;return Math.abs(n-s)>r}(l,v,r,m)||u.timescale!==l.timescale&&n)&&(u.initPTS=v-r,l&&1===l.timescale&&A.warn("Adjusting initPTS by "+(u.initPTS-l.baseTime)),this.initPTS=l={baseTime:u.initPTS,timescale:1});const y=t?v-l.baseTime/l.timescale:h,E=y+m;!function(t,e,s){Ct(e,["moof","traf"]).forEach((e=>{Ct(e,["tfhd"]).forEach((i=>{const r=kt(i,4),n=t[r];if(!n)return;const a=n.timescale||9e4;Ct(e,["tfdt"]).forEach((t=>{const e=t[0],i=s*a;if(i){let s=kt(t,4);if(0===e)s-=i,s=Math.max(s,0),It(t,4,s);else{s*=Math.pow(2,32),s+=kt(t,8),s-=i,s=Math.max(s,0);const e=Math.floor(s/(St+1)),r=Math.floor(s%(St+1));It(t,4,e),It(t,8,r)}}}))}))}))}(g,c,l.baseTime/l.timescale),m>0?this.lastEndTime=E:(A.warn("Duration parsed from mp4 should be greater than zero"),this.resetNextTimestamp());const T=!!g.audio,S=!!g.video;let L="";T&&(L+="audio"),S&&(L+="video");const R={data1:c,startPTS:y,startDTS:y,endPTS:E,endDTS:E,type:L,hasAudio:T,hasVideo:S,nb:1,dropped:0};return d.audio="audio"===R.type?R:void 0,d.video="audio"!==R.type?R:void 0,d.initSegment=u,d.id3=Er(s,r,l,l),i.samples.length&&(d.text=Tr(i,r,l)),d}}},{demux:sr,remux:vr},{demux:class extends wi{constructor(t,e){super(),this.observer=void 0,this.config=void 0,this.observer=t,this.config=e}resetInitSegment(t,e,s,i){super.resetInitSegment(t,e,s,i),this._audioTrack={container:"audio/adts",type:"audio",id:2,pid:-1,sequenceNumber:0,segmentCodec:"aac",samples:[],manifestCodec:e,duration:i,inputTimeScale:9e4,dropped:0}}static probe(t){if(!t)return!1;const e=nt(t,0);let s=(null==e?void 0:e.length)||0;if(Wi(t,s))return!1;for(let e=t.length;s<e;s++)if(Pi(t,s))return A.log("ADTS sync word found !"),!0;return!1}canParse(t,e){return function(t,e){return function(t,e){return e+5<t.length}(t,e)&&Ii(t,e)&&_i(t,e)<=t.length-e}(t,e)}appendFrame(t,e,s){Mi(t,this.observer,e,s,t.manifestCodec);const i=Oi(t,e,s,this.basePTS,this.frameIndex);if(i&&0===i.missing)return i}},remux:vr},{demux:class extends wi{resetInitSegment(t,e,s,i){super.resetInitSegment(t,e,s,i),this._audioTrack={container:"audio/mpeg",type:"audio",id:2,pid:-1,sequenceNumber:0,segmentCodec:"mp3",samples:[],manifestCodec:e,duration:i,inputTimeScale:9e4,dropped:0}}static probe(t){if(!t)return!1;const e=nt(t,0);let s=(null==e?void 0:e.length)||0;if(e&&11===t[s]&&119===t[s+1]&&void 0!==lt(e)&&qi(t,s)<=16)return!1;for(let e=t.length;s<e;s++)if(Wi(t,s))return A.log("MPEG Audio sync word found !"),!0;return!1}canParse(t,e){return function(t,e){return Vi(t,e)&&4<=t.length-e}(t,e)}appendFrame(t,e,s){if(null!==this.basePTS)return Ki(t,e,s,this.basePTS,this.frameIndex)}},remux:vr}];Ar.splice(2,0,{demux:Xi,remux:vr});class Rr{constructor(t,e,s,i,r){this.async=!1,this.observer=void 0,this.typeSupported=void 0,this.config=void 0,this.vendor=void 0,this.id=void 0,this.demuxer=void 0,this.remuxer=void 0,this.decrypter=void 0,this.probe=void 0,this.decryptionPromise=null,this.transmuxConfig=void 0,this.currentTransmuxState=void 0,this.observer=t,this.typeSupported=e,this.config=s,this.vendor=i,this.id=r}configure(t){this.transmuxConfig=t,this.decrypter&&this.decrypter.reset()}push(t,e,s,i){const r=s.transmuxing;r.executeStart=gr();let n=new Uint8Array(t);const{currentTransmuxState:a,transmuxConfig:o}=this;i&&(this.currentTransmuxState=i);const{contiguous:l,discontinuity:h,trackSwitch:d,accurateTimeOffset:c,timeOffset:u,initSegmentChange:f}=i||a,{audioCodec:g,videoCodec:m,defaultInitPts:E,duration:T,initSegmentData:S}=o,L=function(t,e){let s=null;t.byteLength>0&&null!=(null==e?void 0:e.key)&&null!==e.iv&&null!=e.method&&(s=e);return s}(n,e);if(L&&"AES-128"===L.method){const t=this.getDecrypter();if(!t.isSync())return this.decryptionPromise=t.webCryptoDecrypt(n,L.key.buffer,L.iv.buffer).then((t=>{const e=this.push(t,null,s);return this.decryptionPromise=null,e})),this.decryptionPromise;{let e=t.softwareDecrypt(n,L.key.buffer,L.iv.buffer);if(s.part>-1&&(e=t.flush()),!e)return r.executeEnd=gr(),br(s);n=new Uint8Array(e)}}const R=this.needsProbing(h,d);if(R){const t=this.configureTransmuxer(n);if(t)return A.warn(`[transmuxer] ${t.message}`),this.observer.emit(p.ERROR,p.ERROR,{type:v.MEDIA_ERROR,details:y.FRAG_PARSING_ERROR,fatal:!1,error:t,reason:t.message}),r.executeEnd=gr(),br(s)}(h||d||f||R)&&this.resetInitSegment(S,g,m,T,e),(h||f||R)&&this.resetInitialTimestamp(E),l||this.resetContiguity();const b=this.transmux(n,L,u,c,s),k=this.currentTransmuxState;return k.contiguous=!0,k.discontinuity=!1,k.trackSwitch=!1,r.executeEnd=gr(),b}flush(t){const e=t.transmuxing;e.executeStart=gr();const{decrypter:s,currentTransmuxState:i,decryptionPromise:r}=this;if(r)return r.then((()=>this.flush(t)));const n=[],{timeOffset:a}=i;if(s){const e=s.flush();e&&n.push(this.push(e,null,t))}const{demuxer:o,remuxer:l}=this;if(!o||!l)return e.executeEnd=gr(),[br(t)];const h=o.flush(a);return kr(h)?h.then((e=>(this.flushRemux(n,e,t),n))):(this.flushRemux(n,h,t),n)}flushRemux(t,e,s){const{audioTrack:i,videoTrack:r,id3Track:n,textTrack:a}=e,{accurateTimeOffset:o,timeOffset:l}=this.currentTransmuxState;A.log(`[transmuxer.ts]: Flushed fragment ${s.sn}${s.part>-1?" p: "+s.part:""} of level ${s.level}`);const h=this.remuxer.remux(i,r,n,a,l,o,!0,this.id);t.push({remuxResult:h,chunkMeta:s}),s.transmuxing.executeEnd=gr()}resetInitialTimestamp(t){const{demuxer:e,remuxer:s}=this;e&&s&&(e.resetTimeStamp(t),s.resetTimeStamp(t))}resetContiguity(){const{demuxer:t,remuxer:e}=this;t&&e&&(t.resetContiguity(),e.resetNextTimestamp())}resetInitSegment(t,e,s,i,r){const{demuxer:n,remuxer:a}=this;n&&a&&(n.resetInitSegment(t,e,s,i),a.resetInitSegment(t,e,s,r))}destroy(){this.demuxer&&(this.demuxer.destroy(),this.demuxer=void 0),this.remuxer&&(this.remuxer.destroy(),this.remuxer=void 0)}transmux(t,e,s,i,r){let n;return n=e&&"SAMPLE-AES"===e.method?this.transmuxSampleAes(t,e,s,i,r):this.transmuxUnencrypted(t,s,i,r),n}transmuxUnencrypted(t,e,s,i){const{audioTrack:r,videoTrack:n,id3Track:a,textTrack:o}=this.demuxer.demux(t,e,!1,!this.config.progressive);return{remuxResult:this.remuxer.remux(r,n,a,o,e,s,!1,this.id),chunkMeta:i}}transmuxSampleAes(t,e,s,i,r){return this.demuxer.demuxSampleAes(t,e,s).then((t=>({remuxResult:this.remuxer.remux(t.audioTrack,t.videoTrack,t.id3Track,t.textTrack,s,i,!1,this.id),chunkMeta:r})))}configureTransmuxer(t){const{config:e,observer:s,typeSupported:i,vendor:r}=this;let n;for(let e=0,s=Ar.length;e<s;e++){var a;if(null!=(a=Ar[e].demux)&&a.probe(t)){n=Ar[e];break}}if(!n)return new Error("Failed to find demuxer by probing fragment data");const o=this.demuxer,l=this.remuxer,h=n.remux,d=n.demux;l&&l instanceof h||(this.remuxer=new h(s,e,i,r)),o&&o instanceof d||(this.demuxer=new d(s,e,i),this.probe=d.probe)}needsProbing(t,e){return!this.demuxer||!this.remuxer||t||e}getDecrypter(){let t=this.decrypter;return t||(t=this.decrypter=new di(this.config)),t}}const br=t=>({remuxResult:{},chunkMeta:t});function kr(t){return"then"in t&&t.then instanceof Function}class wr{constructor(t,e,s,i,r){this.audioCodec=void 0,this.videoCodec=void 0,this.initSegmentData=void 0,this.duration=void 0,this.defaultInitPts=void 0,this.audioCodec=t,this.videoCodec=e,this.initSegmentData=s,this.duration=i,this.defaultInitPts=r||null}}class Dr{constructor(t,e,s,i,r,n){this.discontinuity=void 0,this.contiguous=void 0,this.accurateTimeOffset=void 0,this.trackSwitch=void 0,this.timeOffset=void 0,this.initSegmentChange=void 0,this.discontinuity=t,this.contiguous=e,this.accurateTimeOffset=s,this.trackSwitch=i,this.timeOffset=r,this.initSegmentChange=n}}var Ir={exports:{}};!function(t){var e=Object.prototype.hasOwnProperty,s="~";function i(){}function r(t,e,s){this.fn=t,this.context=e,this.once=s||!1}function n(t,e,i,n,a){if("function"!=typeof i)throw new TypeError("The listener must be a function");var o=new r(i,n||t,a),l=s?s+e:e;return t._events[l]?t._events[l].fn?t._events[l]=[t._events[l],o]:t._events[l].push(o):(t._events[l]=o,t._eventsCount++),t}function a(t,e){0==--t._eventsCount?t._events=new i:delete t._events[e]}function o(){this._events=new i,this._eventsCount=0}Object.create&&(i.prototype=Object.create(null),(new i).__proto__||(s=!1)),o.prototype.eventNames=function(){var t,i,r=[];if(0===this._eventsCount)return r;for(i in t=this._events)e.call(t,i)&&r.push(s?i.slice(1):i);return Object.getOwnPropertySymbols?r.concat(Object.getOwnPropertySymbols(t)):r},o.prototype.listeners=function(t){var e=s?s+t:t,i=this._events[e];if(!i)return[];if(i.fn)return[i.fn];for(var r=0,n=i.length,a=new Array(n);r<n;r++)a[r]=i[r].fn;return a},o.prototype.listenerCount=function(t){var e=s?s+t:t,i=this._events[e];return i?i.fn?1:i.length:0},o.prototype.emit=function(t,e,i,r,n,a){var o=s?s+t:t;if(!this._events[o])return!1;var l,h,d=this._events[o],c=arguments.length;if(d.fn){switch(d.once&&this.removeListener(t,d.fn,void 0,!0),c){case 1:return d.fn.call(d.context),!0;case 2:return d.fn.call(d.context,e),!0;case 3:return d.fn.call(d.context,e,i),!0;case 4:return d.fn.call(d.context,e,i,r),!0;case 5:return d.fn.call(d.context,e,i,r,n),!0;case 6:return d.fn.call(d.context,e,i,r,n,a),!0}for(h=1,l=new Array(c-1);h<c;h++)l[h-1]=arguments[h];d.fn.apply(d.context,l)}else{var u,f=d.length;for(h=0;h<f;h++)switch(d[h].once&&this.removeListener(t,d[h].fn,void 0,!0),c){case 1:d[h].fn.call(d[h].context);break;case 2:d[h].fn.call(d[h].context,e);break;case 3:d[h].fn.call(d[h].context,e,i);break;case 4:d[h].fn.call(d[h].context,e,i,r);break;default:if(!l)for(u=1,l=new Array(c-1);u<c;u++)l[u-1]=arguments[u];d[h].fn.apply(d[h].context,l)}}return!0},o.prototype.on=function(t,e,s){return n(this,t,e,s,!1)},o.prototype.once=function(t,e,s){return n(this,t,e,s,!0)},o.prototype.removeListener=function(t,e,i,r){var n=s?s+t:t;if(!this._events[n])return this;if(!e)return a(this,n),this;var o=this._events[n];if(o.fn)o.fn!==e||r&&!o.once||i&&o.context!==i||a(this,n);else{for(var l=0,h=[],d=o.length;l<d;l++)(o[l].fn!==e||r&&!o[l].once||i&&o[l].context!==i)&&h.push(o[l]);h.length?this._events[n]=1===h.length?h[0]:h:a(this,n)}return this},o.prototype.removeAllListeners=function(t){var e;return t?(e=s?s+t:t,this._events[e]&&a(this,e)):(this._events=new i,this._eventsCount=0),this},o.prototype.off=o.prototype.removeListener,o.prototype.addListener=o.prototype.on,o.prefixed=s,o.EventEmitter=o,t.exports=o}(Ir);var Cr=t(Ir.exports);class _r{constructor(t,e,s,i){this.error=null,this.hls=void 0,this.id=void 0,this.observer=void 0,this.frag=null,this.part=null,this.useWorker=void 0,this.workerContext=null,this.onwmsg=void 0,this.transmuxer=null,this.onTransmuxComplete=void 0,this.onFlush=void 0;const r=t.config;this.hls=t,this.id=e,this.useWorker=!!r.enableWorker,this.onTransmuxComplete=s,this.onFlush=i;const n=(t,e)=>{(e=e||{}).frag=this.frag,e.id=this.id,t===p.ERROR&&(this.error=e.error),this.hls.trigger(t,e)};this.observer=new Cr,this.observer.on(p.FRAG_DECRYPTED,n),this.observer.on(p.ERROR,n);const a=te(r.preferManagedMediaSource)||{isTypeSupported:()=>!1},o={mpeg:a.isTypeSupported("audio/mpeg"),mp3:a.isTypeSupported('audio/mp4; codecs="mp3"'),ac3:a.isTypeSupported('audio/mp4; codecs="ac-3"')};if(this.useWorker&&"undefined"!=typeof Worker){if(r.workerPath||"function"==typeof __HLS_WORKER_BUNDLE__){try{r.workerPath?(A.log(`loading Web Worker ${r.workerPath} for "${e}"`),this.workerContext=function(t){const e=new self.URL(t,self.location.href).href;return{worker:new self.Worker(e),scriptURL:e}}(r.workerPath)):(A.log(`injecting Web Worker for "${e}"`),this.workerContext=function(){const t=new self.Blob([`var exports={};var module={exports:exports};function define(f){f()};define.amd=true;(${__HLS_WORKER_BUNDLE__.toString()})(true);`],{type:"text/javascript"}),e=self.URL.createObjectURL(t);return{worker:new self.Worker(e),objectURL:e}}()),this.onwmsg=t=>this.onWorkerMessage(t);const{worker:t}=this.workerContext;t.addEventListener("message",this.onwmsg),t.onerror=t=>{const s=new Error(`${t.message} (${t.filename}:${t.lineno})`);r.enableWorker=!1,A.warn(`Error in "${e}" Web Worker, fallback to inline`),this.hls.trigger(p.ERROR,{type:v.OTHER_ERROR,details:y.INTERNAL_EXCEPTION,fatal:!1,event:"demuxerWorker",error:s})},t.postMessage({cmd:"init",typeSupported:o,vendor:"",id:e,config:JSON.stringify(r)})}catch(t){A.warn(`Error setting up "${e}" Web Worker, fallback to inline`,t),this.resetWorker(),this.error=null,this.transmuxer=new Rr(this.observer,o,r,"",e)}return}}this.transmuxer=new Rr(this.observer,o,r,"",e)}resetWorker(){if(this.workerContext){const{worker:t,objectURL:e}=this.workerContext;e&&self.URL.revokeObjectURL(e),t.removeEventListener("message",this.onwmsg),t.onerror=null,t.terminate(),this.workerContext=null}}destroy(){if(this.workerContext)this.resetWorker(),this.onwmsg=void 0;else{const t=this.transmuxer;t&&(t.destroy(),this.transmuxer=null)}const t=this.observer;t&&t.removeAllListeners(),this.frag=null,this.observer=null,this.hls=null}push(t,e,s,i,r,n,a,o,l,h){var d,c;l.transmuxing.start=self.performance.now();const{transmuxer:u}=this,f=n?n.start:r.start,g=r.decryptdata,m=this.frag,p=!(m&&r.cc===m.cc),v=!(m&&l.level===m.level),y=m?l.sn-m.sn:-1,E=this.part?l.part-this.part.index:-1,T=0===y&&l.id>1&&l.id===(null==m?void 0:m.stats.chunkCount),S=!v&&(1===y||0===y&&(1===E||T&&E<=0)),L=self.performance.now();(v||y||0===r.stats.parsing.start)&&(r.stats.parsing.start=L),!n||!E&&S||(n.stats.parsing.start=L);const R=!(m&&(null==(d=r.initSegment)?void 0:d.url)===(null==(c=m.initSegment)?void 0:c.url)),b=new Dr(p,S,o,v,f,R);if(!S||p||R){A.log(`[transmuxer-interface, ${r.type}]: Starting new transmux session for sn: ${l.sn} p: ${l.part} level: ${l.level} id: ${l.id}\n discontinuity: ${p}\n trackSwitch: ${v}\n contiguous: ${S}\n accurateTimeOffset: ${o}\n timeOffset: ${f}\n initSegmentChange: ${R}`);const t=new wr(s,i,e,a,h);this.configureTransmuxer(t)}if(this.frag=r,this.part=n,this.workerContext)this.workerContext.worker.postMessage({cmd:"demux",data:t,decryptdata:g,chunkMeta:l,state:b},t instanceof ArrayBuffer?[t]:[]);else if(u){const e=u.push(t,g,l,b);kr(e)?(u.async=!0,e.then((t=>{this.handleTransmuxComplete(t)})).catch((t=>{this.transmuxerError(t,l,"transmuxer-interface push error")}))):(u.async=!1,this.handleTransmuxComplete(e))}}flush(t){t.transmuxing.start=self.performance.now();const{transmuxer:e}=this;if(this.workerContext)this.workerContext.worker.postMessage({cmd:"flush",chunkMeta:t});else if(e){let s=e.flush(t);kr(s)||e.async?(kr(s)||(s=Promise.resolve(s)),s.then((e=>{this.handleFlushResult(e,t)})).catch((e=>{this.transmuxerError(e,t,"transmuxer-interface flush error")}))):this.handleFlushResult(s,t)}}transmuxerError(t,e,s){this.hls&&(this.error=t,this.hls.trigger(p.ERROR,{type:v.MEDIA_ERROR,details:y.FRAG_PARSING_ERROR,chunkMeta:e,frag:this.frag||void 0,fatal:!1,error:t,err:t,reason:s}))}handleFlushResult(t,e){t.forEach((t=>{this.handleTransmuxComplete(t)})),this.onFlush(e)}onWorkerMessage(t){const e=t.data;if(null==e||!e.event)return void A.warn("worker message received with no "+(e?"event name":"data"));const s=this.hls;if(this.hls)switch(e.event){case"init":{var i;const t=null==(i=this.workerContext)?void 0:i.objectURL;t&&self.URL.revokeObjectURL(t);break}case"transmuxComplete":this.handleTransmuxComplete(e.data);break;case"flush":this.onFlush(e.data);break;case"workerLog":A[e.data.logType]&&A[e.data.logType](e.data.message);break;default:e.data=e.data||{},e.data.frag=this.frag,e.data.id=this.id,s.trigger(e.event,e.data)}}configureTransmuxer(t){const{transmuxer:e}=this;this.workerContext?this.workerContext.worker.postMessage({cmd:"configure",config:t}):e&&e.configure(t)}handleTransmuxComplete(t){t.chunkMeta.transmuxing.end=self.performance.now(),this.onTransmuxComplete(t)}}function xr(t,e){if(t.length!==e.length)return!1;for(let s=0;s<t.length;s++)if(!Pr(t[s].attrs,e[s].attrs))return!1;return!0}function Pr(t,e,s){const i=t["STABLE-RENDITION-ID"];return i&&!s?i===e["STABLE-RENDITION-ID"]:!(s||["LANGUAGE","NAME","CHARACTERISTICS","AUTOSELECT","DEFAULT","FORCED","ASSOC-LANGUAGE"]).some((s=>t[s]!==e[s]))}function Mr(t,e){return e.label.toLowerCase()===t.name.toLowerCase()&&(!e.language||e.language.toLowerCase()===(t.lang||"").toLowerCase())}class Fr{constructor(t){this.buffered=void 0;const e=(e,s,i)=>{if((s>>>=0)>i-1)throw new DOMException(`Failed to execute '${e}' on 'TimeRanges': The index provided (${s}) is greater than the maximum bound (${i})`);return t[s][e]};this.buffered={get length(){return t.length},end:s=>e("end",s,t.length),start:s=>e("start",s,t.length)}}}class Or{constructor(t){this.buffers=void 0,this.queues={video:[],audio:[],audiovideo:[]},this.buffers=t}append(t,e,s){const i=this.queues[e];i.push(t),1!==i.length||s||this.executeNext(e)}insertAbort(t,e){this.queues[e].unshift(t),this.executeNext(e)}appendBlocker(t){let e;const s=new Promise((t=>{e=t})),i={execute:e,onStart:()=>{},onComplete:()=>{},onError:()=>{}};return this.append(i,t),s}executeNext(t){const e=this.queues[t];if(e.length){const s=e[0];try{s.execute()}catch(e){A.warn(`[buffer-operation-queue]: Exception executing "${t}" SourceBuffer operation: ${e}`),s.onError(e);const i=this.buffers[t];null!=i&&i.updating||this.shiftAndExecuteNext(t)}}}shiftAndExecuteNext(t){this.queues[t].shift(),this.executeNext(t)}current(t){return this.queues[t][0]}}const Nr=/(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/;function Ur(t){const e=t.querySelectorAll("source");[].slice.call(e).forEach((e=>{t.removeChild(e)}))}const Br={42:225,92:233,94:237,95:243,96:250,123:231,124:247,125:209,126:241,127:9608,128:174,129:176,130:189,131:191,132:8482,133:162,134:163,135:9834,136:224,137:32,138:232,139:226,140:234,141:238,142:244,143:251,144:193,145:201,146:211,147:218,148:220,149:252,150:8216,151:161,152:42,153:8217,154:9473,155:169,156:8480,157:8226,158:8220,159:8221,160:192,161:194,162:199,163:200,164:202,165:203,166:235,167:206,168:207,169:239,170:212,171:217,172:249,173:219,174:171,175:187,176:195,177:227,178:205,179:204,180:236,181:210,182:242,183:213,184:245,185:123,186:125,187:92,188:94,189:95,190:124,191:8764,192:196,193:228,194:214,195:246,196:223,197:165,198:164,199:9475,200:197,201:229,202:216,203:248,204:9487,205:9491,206:9495,207:9499},$r=t=>String.fromCharCode(Br[t]||t),Gr=15,Kr=100,Hr={17:1,18:3,21:5,22:7,23:9,16:11,19:12,20:14},Vr={17:2,18:4,21:6,22:8,23:10,19:13,20:15},Yr={25:1,26:3,29:5,30:7,31:9,24:11,27:12,28:14},Wr={25:2,26:4,29:6,30:8,31:10,27:13,28:15},jr=["white","green","blue","cyan","red","yellow","magenta","black","transparent"];class qr{constructor(){this.time=null,this.verboseLevel=0}log(t,e){if(this.verboseLevel>=t){const s="function"==typeof e?e():e;A.log(`${this.time} [${t}] ${s}`)}}}const Xr=function(t){const e=[];for(let s=0;s<t.length;s++)e.push(t[s].toString(16));return e};class zr{constructor(){this.foreground="white",this.underline=!1,this.italics=!1,this.background="black",this.flash=!1}reset(){this.foreground="white",this.underline=!1,this.italics=!1,this.background="black",this.flash=!1}setStyles(t){const e=["foreground","underline","italics","background","flash"];for(let s=0;s<e.length;s++){const i=e[s];t.hasOwnProperty(i)&&(this[i]=t[i])}}isDefault(){return"white"===this.foreground&&!this.underline&&!this.italics&&"black"===this.background&&!this.flash}equals(t){return this.foreground===t.foreground&&this.underline===t.underline&&this.italics===t.italics&&this.background===t.background&&this.flash===t.flash}copy(t){this.foreground=t.foreground,this.underline=t.underline,this.italics=t.italics,this.background=t.background,this.flash=t.flash}toString(){return"color="+this.foreground+", underline="+this.underline+", italics="+this.italics+", background="+this.background+", flash="+this.flash}}class Qr{constructor(){this.uchar=" ",this.penState=new zr}reset(){this.uchar=" ",this.penState.reset()}setChar(t,e){this.uchar=t,this.penState.copy(e)}setPenState(t){this.penState.copy(t)}equals(t){return this.uchar===t.uchar&&this.penState.equals(t.penState)}copy(t){this.uchar=t.uchar,this.penState.copy(t.penState)}isEmpty(){return" "===this.uchar&&this.penState.isDefault()}}class Jr{constructor(t){this.chars=[],this.pos=0,this.currPenState=new zr,this.cueStartTime=null,this.logger=void 0;for(let t=0;t<Kr;t++)this.chars.push(new Qr);this.logger=t}equals(t){for(let e=0;e<Kr;e++)if(!this.chars[e].equals(t.chars[e]))return!1;return!0}copy(t){for(let e=0;e<Kr;e++)this.chars[e].copy(t.chars[e])}isEmpty(){let t=!0;for(let e=0;e<Kr;e++)if(!this.chars[e].isEmpty()){t=!1;break}return t}setCursor(t){this.pos!==t&&(this.pos=t),this.pos<0?(this.logger.log(3,"Negative cursor position "+this.pos),this.pos=0):this.pos>Kr&&(this.logger.log(3,"Too large cursor position "+this.pos),this.pos=Kr)}moveCursor(t){const e=this.pos+t;if(t>1)for(let t=this.pos+1;t<e+1;t++)this.chars[t].setPenState(this.currPenState);this.setCursor(e)}backSpace(){this.moveCursor(-1),this.chars[this.pos].setChar(" ",this.currPenState)}insertChar(t){t>=144&&this.backSpace();const e=$r(t);this.pos>=Kr?this.logger.log(0,(()=>"Cannot insert "+t.toString(16)+" ("+e+") at position "+this.pos+". Skipping it!")):(this.chars[this.pos].setChar(e,this.currPenState),this.moveCursor(1))}clearFromPos(t){let e;for(e=t;e<Kr;e++)this.chars[e].reset()}clear(){this.clearFromPos(0),this.pos=0,this.currPenState.reset()}clearToEndOfRow(){this.clearFromPos(this.pos)}getTextString(){const t=[];let e=!0;for(let s=0;s<Kr;s++){const i=this.chars[s].uchar;" "!==i&&(e=!1),t.push(i)}return e?"":t.join("")}setPenStyles(t){this.currPenState.setStyles(t);this.chars[this.pos].setPenState(this.currPenState)}}class Zr{constructor(t){this.rows=[],this.currRow=14,this.nrRollUpRows=null,this.lastOutputScreen=null,this.logger=void 0;for(let e=0;e<Gr;e++)this.rows.push(new Jr(t));this.logger=t}reset(){for(let t=0;t<Gr;t++)this.rows[t].clear();this.currRow=14}equals(t){let e=!0;for(let s=0;s<Gr;s++)if(!this.rows[s].equals(t.rows[s])){e=!1;break}return e}copy(t){for(let e=0;e<Gr;e++)this.rows[e].copy(t.rows[e])}isEmpty(){let t=!0;for(let e=0;e<Gr;e++)if(!this.rows[e].isEmpty()){t=!1;break}return t}backSpace(){this.rows[this.currRow].backSpace()}clearToEndOfRow(){this.rows[this.currRow].clearToEndOfRow()}insertChar(t){this.rows[this.currRow].insertChar(t)}setPen(t){this.rows[this.currRow].setPenStyles(t)}moveCursor(t){this.rows[this.currRow].moveCursor(t)}setCursor(t){this.logger.log(2,"setCursor: "+t);this.rows[this.currRow].setCursor(t)}setPAC(t){this.logger.log(2,(()=>"pacData = "+JSON.stringify(t)));let e=t.row-1;if(this.nrRollUpRows&&e<this.nrRollUpRows-1&&(e=this.nrRollUpRows-1),this.nrRollUpRows&&this.currRow!==e){for(let t=0;t<Gr;t++)this.rows[t].clear();const t=this.currRow+1-this.nrRollUpRows,s=this.lastOutputScreen;if(s){const i=s.rows[t].cueStartTime,r=this.logger.time;if(null!==i&&null!==r&&i<r)for(let i=0;i<this.nrRollUpRows;i++)this.rows[e-this.nrRollUpRows+i+1].copy(s.rows[t+i])}}this.currRow=e;const s=this.rows[this.currRow];if(null!==t.indent){const e=t.indent,i=Math.max(e-1,0);s.setCursor(t.indent),t.color=s.chars[i].penState.foreground}const i={foreground:t.color,underline:t.underline,italics:t.italics,background:"black",flash:!1};this.setPen(i)}setBkgData(t){this.logger.log(2,(()=>"bkgData = "+JSON.stringify(t))),this.backSpace(),this.setPen(t),this.insertChar(32)}setRollUpRows(t){this.nrRollUpRows=t}rollUp(){if(null===this.nrRollUpRows)return void this.logger.log(3,"roll_up but nrRollUpRows not set yet");this.logger.log(1,(()=>this.getDisplayText()));const t=this.currRow+1-this.nrRollUpRows,e=this.rows.splice(t,1)[0];e.clear(),this.rows.splice(this.currRow,0,e),this.logger.log(2,"Rolling up")}getDisplayText(t){t=t||!1;const e=[];let s="",i=-1;for(let s=0;s<Gr;s++){const r=this.rows[s].getTextString();r&&(i=s+1,t?e.push("Row "+i+": '"+r+"'"):e.push(r.trim()))}return e.length>0&&(s=t?"["+e.join(" | ")+"]":e.join("\n")),s}getTextAndFormat(){return this.rows}}class tn{constructor(t,e,s){this.chNr=void 0,this.outputFilter=void 0,this.mode=void 0,this.verbose=void 0,this.displayedMemory=void 0,this.nonDisplayedMemory=void 0,this.lastOutputScreen=void 0,this.currRollUpRow=void 0,this.writeScreen=void 0,this.cueStartTime=void 0,this.logger=void 0,this.chNr=t,this.outputFilter=e,this.mode=null,this.verbose=0,this.displayedMemory=new Zr(s),this.nonDisplayedMemory=new Zr(s),this.lastOutputScreen=new Zr(s),this.currRollUpRow=this.displayedMemory.rows[14],this.writeScreen=this.displayedMemory,this.mode=null,this.cueStartTime=null,this.logger=s}reset(){this.mode=null,this.displayedMemory.reset(),this.nonDisplayedMemory.reset(),this.lastOutputScreen.reset(),this.outputFilter.reset(),this.currRollUpRow=this.displayedMemory.rows[14],this.writeScreen=this.displayedMemory,this.mode=null,this.cueStartTime=null}getHandler(){return this.outputFilter}setHandler(t){this.outputFilter=t}setPAC(t){this.writeScreen.setPAC(t)}setBkgData(t){this.writeScreen.setBkgData(t)}setMode(t){t!==this.mode&&(this.mode=t,this.logger.log(2,(()=>"MODE="+t)),"MODE_POP-ON"===this.mode?this.writeScreen=this.nonDisplayedMemory:(this.writeScreen=this.displayedMemory,this.writeScreen.reset()),"MODE_ROLL-UP"!==this.mode&&(this.displayedMemory.nrRollUpRows=null,this.nonDisplayedMemory.nrRollUpRows=null),this.mode=t)}insertChars(t){for(let e=0;e<t.length;e++)this.writeScreen.insertChar(t[e]);const e=this.writeScreen===this.displayedMemory?"DISP":"NON_DISP";this.logger.log(2,(()=>e+": "+this.writeScreen.getDisplayText(!0))),"MODE_PAINT-ON"!==this.mode&&"MODE_ROLL-UP"!==this.mode||(this.logger.log(1,(()=>"DISPLAYED: "+this.displayedMemory.getDisplayText(!0))),this.outputDataUpdate())}ccRCL(){this.logger.log(2,"RCL - Resume Caption Loading"),this.setMode("MODE_POP-ON")}ccBS(){this.logger.log(2,"BS - BackSpace"),"MODE_TEXT"!==this.mode&&(this.writeScreen.backSpace(),this.writeScreen===this.displayedMemory&&this.outputDataUpdate())}ccAOF(){}ccAON(){}ccDER(){this.logger.log(2,"DER- Delete to End of Row"),this.writeScreen.clearToEndOfRow(),this.outputDataUpdate()}ccRU(t){this.logger.log(2,"RU("+t+") - Roll Up"),this.writeScreen=this.displayedMemory,this.setMode("MODE_ROLL-UP"),this.writeScreen.setRollUpRows(t)}ccFON(){this.logger.log(2,"FON - Flash On"),this.writeScreen.setPen({flash:!0})}ccRDC(){this.logger.log(2,"RDC - Resume Direct Captioning"),this.setMode("MODE_PAINT-ON")}ccTR(){this.logger.log(2,"TR"),this.setMode("MODE_TEXT")}ccRTD(){this.logger.log(2,"RTD"),this.setMode("MODE_TEXT")}ccEDM(){this.logger.log(2,"EDM - Erase Displayed Memory"),this.displayedMemory.reset(),this.outputDataUpdate(!0)}ccCR(){this.logger.log(2,"CR - Carriage Return"),this.writeScreen.rollUp(),this.outputDataUpdate(!0)}ccENM(){this.logger.log(2,"ENM - Erase Non-displayed Memory"),this.nonDisplayedMemory.reset()}ccEOC(){if(this.logger.log(2,"EOC - End Of Caption"),"MODE_POP-ON"===this.mode){const t=this.displayedMemory;this.displayedMemory=this.nonDisplayedMemory,this.nonDisplayedMemory=t,this.writeScreen=this.nonDisplayedMemory,this.logger.log(1,(()=>"DISP: "+this.displayedMemory.getDisplayText()))}this.outputDataUpdate(!0)}ccTO(t){this.logger.log(2,"TO("+t+") - Tab Offset"),this.writeScreen.moveCursor(t)}ccMIDROW(t){const e={flash:!1};if(e.underline=t%2==1,e.italics=t>=46,e.italics)e.foreground="white";else{const s=Math.floor(t/2)-16,i=["white","green","blue","cyan","red","yellow","magenta"];e.foreground=i[s]}this.logger.log(2,"MIDROW: "+JSON.stringify(e)),this.writeScreen.setPen(e)}outputDataUpdate(t=!1){const e=this.logger.time;null!==e&&this.outputFilter&&(null!==this.cueStartTime||this.displayedMemory.isEmpty()?this.displayedMemory.equals(this.lastOutputScreen)||(this.outputFilter.newCue(this.cueStartTime,e,this.lastOutputScreen),t&&this.outputFilter.dispatchCue&&this.outputFilter.dispatchCue(),this.cueStartTime=this.displayedMemory.isEmpty()?null:e):this.cueStartTime=e,this.lastOutputScreen.copy(this.displayedMemory))}cueSplitAtTime(t){this.outputFilter&&(this.displayedMemory.isEmpty()||(this.outputFilter.newCue&&this.outputFilter.newCue(this.cueStartTime,t,this.displayedMemory),this.cueStartTime=t))}}class en{constructor(t,e,s){this.channels=void 0,this.currentChannel=0,this.cmdHistory={a:null,b:null},this.logger=void 0;const i=this.logger=new qr;this.channels=[null,new tn(t,e,i),new tn(t+1,s,i)]}getHandler(t){return this.channels[t].getHandler()}setHandler(t,e){this.channels[t].setHandler(e)}addData(t,e){this.logger.time=t;for(let t=0;t<e.length;t+=2){const s=127&e[t],i=127&e[t+1];let r=!1,n=null;if(0===s&&0===i)continue;this.logger.log(3,(()=>"["+Xr([e[t],e[t+1]])+"] -> ("+Xr([s,i])+")"));const a=this.cmdHistory;if(s>=16&&s<=31){if(rn(s,i,a)){sn(null,null,a),this.logger.log(3,(()=>"Repeated command ("+Xr([s,i])+") is dropped"));continue}sn(s,i,this.cmdHistory),r=this.parseCmd(s,i),r||(r=this.parseMidrow(s,i)),r||(r=this.parsePAC(s,i)),r||(r=this.parseBackgroundAttributes(s,i))}else sn(null,null,a);if(!r&&(n=this.parseChars(s,i),n)){const t=this.currentChannel;if(t&&t>0){this.channels[t].insertChars(n)}else this.logger.log(2,"No channel found yet. TEXT-MODE?")}r||n||this.logger.log(2,(()=>"Couldn't parse cleaned data "+Xr([s,i])+" orig: "+Xr([e[t],e[t+1]])))}}parseCmd(t,e){if(!((20===t||28===t||21===t||29===t)&&e>=32&&e<=47)&&!((23===t||31===t)&&e>=33&&e<=35))return!1;const s=20===t||21===t||23===t?1:2,i=this.channels[s];return 20===t||21===t||28===t||29===t?32===e?i.ccRCL():33===e?i.ccBS():34===e?i.ccAOF():35===e?i.ccAON():36===e?i.ccDER():37===e?i.ccRU(2):38===e?i.ccRU(3):39===e?i.ccRU(4):40===e?i.ccFON():41===e?i.ccRDC():42===e?i.ccTR():43===e?i.ccRTD():44===e?i.ccEDM():45===e?i.ccCR():46===e?i.ccENM():47===e&&i.ccEOC():i.ccTO(e-32),this.currentChannel=s,!0}parseMidrow(t,e){let s=0;if((17===t||25===t)&&e>=32&&e<=47){if(s=17===t?1:2,s!==this.currentChannel)return this.logger.log(0,"Mismatch channel in midrow parsing"),!1;const i=this.channels[s];return!!i&&(i.ccMIDROW(e),this.logger.log(3,(()=>"MIDROW ("+Xr([t,e])+")")),!0)}return!1}parsePAC(t,e){let s;if(!((t>=17&&t<=23||t>=25&&t<=31)&&e>=64&&e<=127)&&!((16===t||24===t)&&e>=64&&e<=95))return!1;const i=t<=23?1:2;s=e>=64&&e<=95?1===i?Hr[t]:Yr[t]:1===i?Vr[t]:Wr[t];const r=this.channels[i];return!!r&&(r.setPAC(this.interpretPAC(s,e)),this.currentChannel=i,!0)}interpretPAC(t,e){let s;const i={color:null,italics:!1,indent:null,underline:!1,row:t};return s=e>95?e-96:e-64,i.underline=!(1&~s),s<=13?i.color=["white","green","blue","cyan","red","yellow","magenta","white"][Math.floor(s/2)]:s<=15?(i.italics=!0,i.color="white"):i.indent=4*Math.floor((s-16)/2),i}parseChars(t,e){let s,i=null,r=null;if(t>=25?(s=2,r=t-8):(s=1,r=t),r>=17&&r<=19){let t;t=17===r?e+80:18===r?e+112:e+144,this.logger.log(2,(()=>"Special char '"+$r(t)+"' in channel "+s)),i=[t]}else t>=32&&t<=127&&(i=0===e?[t]:[t,e]);return i&&this.logger.log(3,(()=>"Char codes = "+Xr(i).join(","))),i}parseBackgroundAttributes(t,e){if(!((16===t||24===t)&&e>=32&&e<=47)&&!((23===t||31===t)&&e>=45&&e<=47))return!1;let s;const i={};16===t||24===t?(s=Math.floor((e-32)/2),i.background=jr[s],e%2==1&&(i.background=i.background+"_semi")):45===e?i.background="transparent":(i.foreground="black",47===e&&(i.underline=!0));const r=t<=23?1:2;return this.channels[r].setBkgData(i),!0}reset(){for(let t=0;t<Object.keys(this.channels).length;t++){const e=this.channels[t];e&&e.reset()}sn(null,null,this.cmdHistory)}cueSplitAtTime(t){for(let e=0;e<this.channels.length;e++){const s=this.channels[e];s&&s.cueSplitAtTime(t)}}}function sn(t,e,s){s.a=t,s.b=e}function rn(t,e,s){return s.a===t&&s.b===e}class nn{constructor(t,e){this.timelineController=void 0,this.cueRanges=[],this.trackName=void 0,this.startTime=null,this.endTime=null,this.screen=null,this.timelineController=t,this.trackName=e}dispatchCue(){null!==this.startTime&&(this.timelineController.addCues(this.trackName,this.startTime,this.endTime,this.screen,this.cueRanges),this.startTime=null)}newCue(t,e,s){(null===this.startTime||this.startTime>t)&&(this.startTime=t),this.endTime=e,this.screen=s,this.timelineController.createCaptionsTrack(this.trackName)}reset(){this.cueRanges=[],this.startTime=null}}var an=function(){if(null!=$&&$.VTTCue)return self.VTTCue;const t=["","lr","rl"],e=["start","middle","end","left","right"];function s(t,e){if("string"!=typeof e)return!1;if(!Array.isArray(t))return!1;const s=e.toLowerCase();return!!~t.indexOf(s)&&s}function i(t){return s(e,t)}function r(t,...e){let s=1;for(;s<arguments.length;s++){const e=arguments[s];for(const s in e)t[s]=e[s]}return t}function n(e,n,a){const o=this,l={enumerable:!0};o.hasBeenReset=!1;let h="",d=!1,c=e,u=n,f=a,g=null,m="",p=!0,v="auto",y="start",E=50,T="middle",S=50,L="middle";Object.defineProperty(o,"id",r({},l,{get:function(){return h},set:function(t){h=""+t}})),Object.defineProperty(o,"pauseOnExit",r({},l,{get:function(){return d},set:function(t){d=!!t}})),Object.defineProperty(o,"startTime",r({},l,{get:function(){return c},set:function(t){if("number"!=typeof t)throw new TypeError("Start time must be set to a number.");c=t,this.hasBeenReset=!0}})),Object.defineProperty(o,"endTime",r({},l,{get:function(){return u},set:function(t){if("number"!=typeof t)throw new TypeError("End time must be set to a number.");u=t,this.hasBeenReset=!0}})),Object.defineProperty(o,"text",r({},l,{get:function(){return f},set:function(t){f=""+t,this.hasBeenReset=!0}})),Object.defineProperty(o,"region",r({},l,{get:function(){return g},set:function(t){g=t,this.hasBeenReset=!0}})),Object.defineProperty(o,"vertical",r({},l,{get:function(){return m},set:function(e){const i=function(e){return s(t,e)}(e);if(!1===i)throw new SyntaxError("An invalid or illegal string was specified.");m=i,this.hasBeenReset=!0}})),Object.defineProperty(o,"snapToLines",r({},l,{get:function(){return p},set:function(t){p=!!t,this.hasBeenReset=!0}})),Object.defineProperty(o,"line",r({},l,{get:function(){return v},set:function(t){if("number"!=typeof t&&"auto"!==t)throw new SyntaxError("An invalid number or illegal string was specified.");v=t,this.hasBeenReset=!0}})),Object.defineProperty(o,"lineAlign",r({},l,{get:function(){return y},set:function(t){const e=i(t);if(!e)throw new SyntaxError("An invalid or illegal string was specified.");y=e,this.hasBeenReset=!0}})),Object.defineProperty(o,"position",r({},l,{get:function(){return E},set:function(t){if(t<0||t>100)throw new Error("Position must be between 0 and 100.");E=t,this.hasBeenReset=!0}})),Object.defineProperty(o,"positionAlign",r({},l,{get:function(){return T},set:function(t){const e=i(t);if(!e)throw new SyntaxError("An invalid or illegal string was specified.");T=e,this.hasBeenReset=!0}})),Object.defineProperty(o,"size",r({},l,{get:function(){return S},set:function(t){if(t<0||t>100)throw new Error("Size must be between 0 and 100.");S=t,this.hasBeenReset=!0}})),Object.defineProperty(o,"align",r({},l,{get:function(){return L},set:function(t){const e=i(t);if(!e)throw new SyntaxError("An invalid or illegal string was specified.");L=e,this.hasBeenReset=!0}})),o.displayState=void 0}return n.prototype.getCueAsHTML=function(){return self.WebVTT.convertCueToDOMTree(self,this.text)},n}();class on{decode(t,e){if(!t)return"";if("string"!=typeof t)throw new Error("Error - expected string data.");return decodeURIComponent(encodeURIComponent(t))}}function ln(t){function e(t,e,s,i){return 3600*(0|t)+60*(0|e)+(0|s)+parseFloat(i||0)}const s=t.match(/^(?:(\d+):)?(\d{2}):(\d{2})(\.\d+)?/);return s?parseFloat(s[2])>59?e(s[2],s[3],0,s[4]):e(s[1],s[2],s[3],s[4]):null}class hn{constructor(){this.values=Object.create(null)}set(t,e){this.get(t)||""===e||(this.values[t]=e)}get(t,e,s){return s?this.has(t)?this.values[t]:e[s]:this.has(t)?this.values[t]:e}has(t){return t in this.values}alt(t,e,s){for(let i=0;i<s.length;++i)if(e===s[i]){this.set(t,e);break}}integer(t,e){/^-?\d+$/.test(e)&&this.set(t,parseInt(e,10))}percent(t,e){if(/^([\d]{1,3})(\.[\d]*)?%$/.test(e)){const s=parseFloat(e);if(s>=0&&s<=100)return this.set(t,s),!0}return!1}}function dn(t,e,s,i){const r=i?t.split(i):[t];for(const t in r){if("string"!=typeof r[t])continue;const i=r[t].split(s);if(2!==i.length)continue;e(i[0],i[1])}}const cn=new an(0,0,""),un="middle"===cn.align?"middle":"center";function fn(t,e,s){const i=t;function r(){const e=ln(t);if(null===e)throw new Error("Malformed timestamp: "+i);return t=t.replace(/^[^\sa-zA-Z-]+/,""),e}function n(){t=t.replace(/^\s+/,"")}if(n(),e.startTime=r(),n(),"--\x3e"!==t.slice(0,3))throw new Error("Malformed time stamp (time stamps must be separated by '--\x3e'): "+i);t=t.slice(3),n(),e.endTime=r(),n(),function(t,e){const i=new hn;dn(t,(function(t,e){let r;switch(t){case"region":for(let r=s.length-1;r>=0;r--)if(s[r].id===e){i.set(t,s[r].region);break}break;case"vertical":i.alt(t,e,["rl","lr"]);break;case"line":r=e.split(","),i.integer(t,r[0]),i.percent(t,r[0])&&i.set("snapToLines",!1),i.alt(t,r[0],["auto"]),2===r.length&&i.alt("lineAlign",r[1],["start",un,"end"]);break;case"position":r=e.split(","),i.percent(t,r[0]),2===r.length&&i.alt("positionAlign",r[1],["start",un,"end","line-left","line-right","auto"]);break;case"size":i.percent(t,e);break;case"align":i.alt(t,e,["start",un,"end","left","right"])}}),/:/,/\s/),e.region=i.get("region",null),e.vertical=i.get("vertical","");let r=i.get("line","auto");"auto"===r&&-1===cn.line&&(r=-1),e.line=r,e.lineAlign=i.get("lineAlign","start"),e.snapToLines=i.get("snapToLines",!0),e.size=i.get("size",100),e.align=i.get("align",un);let n=i.get("position","auto");"auto"===n&&50===cn.position&&(n="start"===e.align||"left"===e.align?0:"end"===e.align||"right"===e.align?100:50),e.position=n}(t,e)}function gn(t){return t.replace(/<br(?: \/)?>/gi,"\n")}class mn{constructor(){this.state="INITIAL",this.buffer="",this.decoder=new on,this.regionList=[],this.cue=null,this.oncue=void 0,this.onparsingerror=void 0,this.onflush=void 0}parse(t){const e=this;function s(){let t=e.buffer,s=0;for(t=gn(t);s<t.length&&"\r"!==t[s]&&"\n"!==t[s];)++s;const i=t.slice(0,s);return"\r"===t[s]&&++s,"\n"===t[s]&&++s,e.buffer=t.slice(s),i}t&&(e.buffer+=e.decoder.decode(t,{stream:!0}));try{let t="";if("INITIAL"===e.state){if(!/\r\n|\n/.test(e.buffer))return this;t=s();const i=t.match(/^()?WEBVTT([ \t].*)?$/);if(null==i||!i[0])throw new Error("Malformed WebVTT signature.");e.state="HEADER"}let i=!1;for(;e.buffer;){if(!/\r\n|\n/.test(e.buffer))return this;switch(i?i=!1:t=s(),e.state){case"HEADER":/:/.test(t)?dn(t,(function(t,e){}),/:/):t||(e.state="ID");continue;case"NOTE":t||(e.state="ID");continue;case"ID":if(/^NOTE($|[ \t])/.test(t)){e.state="NOTE";break}if(!t)continue;if(e.cue=new an(0,0,""),e.state="CUE",-1===t.indexOf("--\x3e")){e.cue.id=t;continue}case"CUE":if(!e.cue){e.state="BADCUE";continue}try{fn(t,e.cue,e.regionList)}catch(t){e.cue=null,e.state="BADCUE";continue}e.state="CUETEXT";continue;case"CUETEXT":{const s=-1!==t.indexOf("--\x3e");if(!t||s&&(i=!0)){e.oncue&&e.cue&&e.oncue(e.cue),e.cue=null,e.state="ID";continue}if(null===e.cue)continue;e.cue.text&&(e.cue.text+="\n"),e.cue.text+=t}continue;case"BADCUE":t||(e.state="ID")}}}catch(t){"CUETEXT"===e.state&&e.cue&&e.oncue&&e.oncue(e.cue),e.cue=null,e.state="INITIAL"===e.state?"BADWEBVTT":"BADCUE"}return this}flush(){const t=this;try{if((t.cue||"HEADER"===t.state)&&(t.buffer+="\n\n",t.parse()),"INITIAL"===t.state||"BADWEBVTT"===t.state)throw new Error("Malformed WebVTT signature.")}catch(e){t.onparsingerror&&t.onparsingerror(e)}return t.onflush&&t.onflush(),this}}const pn=/\r\n|\n\r|\n|\r/g,vn=function(t,e,s=0){return t.slice(s,s+e.length)===e},yn=function(t){let e=5381,s=t.length;for(;s;)e=33*e^t.charCodeAt(--s);return(e>>>0).toString()};function En(t,e,s){return yn(t.toString())+yn(e.toString())+yn(s)}function Tn(t,e,s,i,r,n,a){const o=new mn,l=vt(new Uint8Array(t)).trim().replace(pn,"\n").split("\n"),h=[],d=e?function(t,e=1){return ur(t,9e4,1/e)}(e.baseTime,e.timescale):0;let c,u="00:00.000",g=0,m=0,p=!0;o.oncue=function(t){const n=s[i];let a=s.ccOffset;const o=(g-d)/9e4;if(null!=n&&n.new&&(void 0!==m?a=s.ccOffset=n.start:function(t,e,s){let i=t[e],r=t[i.prevCC];if(!r||!r.new&&i.new)return t.ccOffset=t.presentationOffset=i.start,void(i.new=!1);for(;null!=(n=r)&&n.new;){var n;t.ccOffset+=i.start-r.start,i.new=!1,i=r,r=t[i.prevCC]}t.presentationOffset=s}(s,i,o)),o){if(!e)return void(c=new Error("Missing initPTS for VTT MPEGTS"));a=o-s.presentationOffset}const l=t.endTime-t.startTime,u=yr(9e4*(t.startTime+a-m),9e4*r)/9e4;t.startTime=Math.max(u,0),t.endTime=Math.max(u+l,0);const f=t.text.trim();t.text=decodeURIComponent(encodeURIComponent(f)),t.id||(t.id=En(t.startTime,t.endTime,f)),t.endTime>0&&h.push(t)},o.onparsingerror=function(t){c=t},o.onflush=function(){c?a(c):n(h)},l.forEach((t=>{if(p){if(vn(t,"X-TIMESTAMP-MAP=")){p=!1,t.slice(16).split(",").forEach((t=>{vn(t,"LOCAL:")?u=t.slice(6):vn(t,"MPEGTS:")&&(g=parseInt(t.slice(7)))}));try{m=function(t){let e=parseInt(t.slice(-3));const s=parseInt(t.slice(-6,-4)),i=parseInt(t.slice(-9,-7)),r=t.length>9?parseInt(t.substring(0,t.indexOf(":"))):0;if(!(f(e)&&f(s)&&f(i)&&f(r)))throw Error(`Malformed X-TIMESTAMP-MAP: Local:${t}`);return e+=1e3*s,e+=6e4*i,e+=36e5*r,e}(u)/1e3}catch(t){c=t}return}""===t&&(p=!1)}o.parse(t+"\n")})),o.flush()}const Sn="stpp.ttml.im1t",Ln=/^(\d{2,}):(\d{2}):(\d{2}):(\d{2})\.?(\d+)?$/,An=/^(\d*(?:\.\d*)?)(h|m|s|ms|f|t)$/,Rn={left:"start",center:"center",right:"end",start:"start",end:"end"};function bn(t,e,s,i){const r=Ct(new Uint8Array(t),["mdat"]);if(0===r.length)return void i(new Error("Could not parse IMSC1 mdat"));const n=r.map((t=>vt(t))),a=function(t,e,s=1,i=!1){return ur(t,e,1/s,i)}(e.baseTime,1,e.timescale);try{n.forEach((t=>s(function(t,e){const s=(new DOMParser).parseFromString(t,"text/xml"),i=s.getElementsByTagName("tt")[0];if(!i)throw new Error("Invalid ttml");const r={frameRate:30,subFrameRate:1,frameRateMultiplier:0,tickRate:0},n=Object.keys(r).reduce(((t,e)=>(t[e]=i.getAttribute(`ttp:${e}`)||r[e],t)),{}),a="preserve"!==i.getAttribute("xml:space"),o=wn(kn(i,"styling","style")),l=wn(kn(i,"layout","region")),h=kn(i,"body","[begin]");return[].map.call(h,(t=>{const s=Dn(t,a);if(!s||!t.hasAttribute("begin"))return null;const i=_n(t.getAttribute("begin"),n),r=_n(t.getAttribute("dur"),n);let h=_n(t.getAttribute("end"),n);if(null===i)throw Cn(t);if(null===h){if(null===r)throw Cn(t);h=i+r}const d=new an(i-e,h-e,s);d.id=En(d.startTime,d.endTime,d.text);const c=function(t,e,s){const i="http://www.w3.org/ns/ttml#styling";let r=null;const n=["displayAlign","textAlign","color","backgroundColor","fontSize","fontFamily"],a=null!=t&&t.hasAttribute("style")?t.getAttribute("style"):null;a&&s.hasOwnProperty(a)&&(r=s[a]);return n.reduce(((s,n)=>{const a=In(e,i,n)||In(t,i,n)||In(r,i,n);return a&&(s[n]=a),s}),{})}(l[t.getAttribute("region")],o[t.getAttribute("style")],o),{textAlign:f}=c;if(f){const t=Rn[f];t&&(d.lineAlign=t),d.align=f}return u(d,c),d})).filter((t=>null!==t))}(t,a))))}catch(t){i(t)}}function kn(t,e,s){const i=t.getElementsByTagName(e)[0];return i?[].slice.call(i.querySelectorAll(s)):[]}function wn(t){return t.reduce(((t,e)=>{const s=e.getAttribute("xml:id");return s&&(t[s]=e),t}),{})}function Dn(t,e){return[].slice.call(t.childNodes).reduce(((t,s,i)=>{var r;return"br"===s.nodeName&&i?t+"\n":null!=(r=s.childNodes)&&r.length?Dn(s,e):e?t+s.textContent.trim().replace(/\s+/g," "):t+s.textContent}),"")}function In(t,e,s){return t&&t.hasAttributeNS(e,s)?t.getAttributeNS(e,s):null}function Cn(t){return new Error(`Could not parse ttml timestamp ${t}`)}function _n(t,e){if(!t)return null;let s=ln(t);return null===s&&(Ln.test(t)?s=function(t,e){const s=Ln.exec(t),i=(0|s[4])+(0|s[5])/e.subFrameRate;return 3600*(0|s[1])+60*(0|s[2])+(0|s[3])+i/e.frameRate}(t,e):An.test(t)&&(s=function(t,e){const s=An.exec(t),i=Number(s[1]);switch(s[2]){case"h":return 3600*i;case"m":return 60*i;case"ms":return 1e3*i;case"f":return i/e.frameRate;case"t":return i/e.tickRate}return i}(t,e))),s}function xn(t){return t.characteristics&&/transcribes-spoken-dialog/gi.test(t.characteristics)&&/describes-music-and-sound/gi.test(t.characteristics)?"captions":"subtitles"}function Pn(t,e){return!!t&&t.kind===xn(e)&&Mr(e,t)}class Mn{constructor(t){this.hls=void 0,this.autoLevelCapping=void 0,this.firstLevel=void 0,this.media=void 0,this.restrictedLevels=void 0,this.timer=void 0,this.clientRect=void 0,this.streamController=void 0,this.hls=t,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.firstLevel=-1,this.media=null,this.restrictedLevels=[],this.timer=void 0,this.clientRect=null,this.registerListeners()}setStreamController(t){this.streamController=t}destroy(){this.hls&&this.unregisterListener(),this.timer&&this.stopCapping(),this.media=null,this.clientRect=null,this.hls=this.streamController=null}registerListeners(){const{hls:t}=this;t.on(p.FPS_DROP_LEVEL_CAPPING,this.onFpsDropLevelCapping,this),t.on(p.MEDIA_ATTACHING,this.onMediaAttaching,this),t.on(p.MANIFEST_PARSED,this.onManifestParsed,this),t.on(p.LEVELS_UPDATED,this.onLevelsUpdated,this),t.on(p.BUFFER_CODECS,this.onBufferCodecs,this),t.on(p.MEDIA_DETACHING,this.onMediaDetaching,this)}unregisterListener(){const{hls:t}=this;t.off(p.FPS_DROP_LEVEL_CAPPING,this.onFpsDropLevelCapping,this),t.off(p.MEDIA_ATTACHING,this.onMediaAttaching,this),t.off(p.MANIFEST_PARSED,this.onManifestParsed,this),t.off(p.LEVELS_UPDATED,this.onLevelsUpdated,this),t.off(p.BUFFER_CODECS,this.onBufferCodecs,this),t.off(p.MEDIA_DETACHING,this.onMediaDetaching,this)}onFpsDropLevelCapping(t,e){const s=this.hls.levels[e.droppedLevel];this.isLevelAllowed(s)&&this.restrictedLevels.push({bitrate:s.bitrate,height:s.height,width:s.width})}onMediaAttaching(t,e){this.media=e.media instanceof HTMLVideoElement?e.media:null,this.clientRect=null,this.timer&&this.hls.levels.length&&this.detectPlayerSize()}onManifestParsed(t,e){const s=this.hls;this.restrictedLevels=[],this.firstLevel=e.firstLevel,s.config.capLevelToPlayerSize&&e.video&&this.startCapping()}onLevelsUpdated(t,e){this.timer&&f(this.autoLevelCapping)&&this.detectPlayerSize()}onBufferCodecs(t,e){this.hls.config.capLevelToPlayerSize&&e.video&&this.startCapping()}onMediaDetaching(){this.stopCapping()}detectPlayerSize(){if(this.media){if(this.mediaHeight<=0||this.mediaWidth<=0)return void(this.clientRect=null);const t=this.hls.levels;if(t.length){const e=this.hls,s=this.getMaxLevel(t.length-1);s!==this.autoLevelCapping&&A.log(`Setting autoLevelCapping to ${s}: ${t[s].height}p@${t[s].bitrate} for media ${this.mediaWidth}x${this.mediaHeight}`),e.autoLevelCapping=s,e.autoLevelCapping>this.autoLevelCapping&&this.streamController&&this.streamController.nextLevelSwitch(),this.autoLevelCapping=e.autoLevelCapping}}}getMaxLevel(t){const e=this.hls.levels;if(!e.length)return-1;const s=e.filter(((e,s)=>this.isLevelAllowed(e)&&s<=t));return this.clientRect=null,Mn.getMaxLevelByMediaSize(s,this.mediaWidth,this.mediaHeight)}startCapping(){this.timer||(this.autoLevelCapping=Number.POSITIVE_INFINITY,self.clearInterval(this.timer),this.timer=self.setInterval(this.detectPlayerSize.bind(this),1e3),this.detectPlayerSize())}stopCapping(){this.restrictedLevels=[],this.firstLevel=-1,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.timer&&(self.clearInterval(this.timer),this.timer=void 0)}getDimensions(){if(this.clientRect)return this.clientRect;const t=this.media,e={width:0,height:0};if(t){const s=t.getBoundingClientRect();e.width=s.width,e.height=s.height,e.width||e.height||(e.width=s.right-s.left||t.width||0,e.height=s.bottom-s.top||t.height||0)}return this.clientRect=e,e}get mediaWidth(){return this.getDimensions().width*this.contentScaleFactor}get mediaHeight(){return this.getDimensions().height*this.contentScaleFactor}get contentScaleFactor(){let t=1;if(!this.hls.config.ignoreDevicePixelRatio)try{t=self.devicePixelRatio}catch(t){}return t}isLevelAllowed(t){return!this.restrictedLevels.some((e=>t.bitrate===e.bitrate&&t.width===e.width&&t.height===e.height))}static getMaxLevelByMediaSize(t,e,s){if(null==t||!t.length)return-1;let i=t.length-1;const r=Math.max(e,s);for(let e=0;e<t.length;e+=1){const s=t[e];if((s.width>=r||s.height>=r)&&(n=s,!(a=t[e+1])||n.width!==a.width||n.height!==a.height)){i=e;break}}var n,a;return i}}const Fn="[eme]";class On{constructor(t){this.hls=void 0,this.config=void 0,this.media=null,this.keyFormatPromise=null,this.keySystemAccessPromises={},this._requestLicenseFailureCount=0,this.mediaKeySessions=[],this.keyIdToKeySessionPromise={},this.setMediaKeysQueue=On.CDMCleanupPromise?[On.CDMCleanupPromise]:[],this.onMediaEncrypted=this._onMediaEncrypted.bind(this),this.onWaitingForKey=this._onWaitingForKey.bind(this),this.debug=A.debug.bind(A,Fn),this.log=A.log.bind(A,Fn),this.warn=A.warn.bind(A,Fn),this.error=A.error.bind(A,Fn),this.hls=t,this.config=t.config,this.registerListeners()}destroy(){this.unregisterListeners(),this.onMediaDetached();const t=this.config;t.requestMediaKeySystemAccessFunc=null,t.licenseXhrSetup=t.licenseResponseCallback=void 0,t.drmSystems=t.drmSystemOptions={},this.hls=this.onMediaEncrypted=this.onWaitingForKey=this.keyIdToKeySessionPromise=null,this.config=null}registerListeners(){this.hls.on(p.MEDIA_ATTACHED,this.onMediaAttached,this),this.hls.on(p.MEDIA_DETACHED,this.onMediaDetached,this),this.hls.on(p.MANIFEST_LOADING,this.onManifestLoading,this),this.hls.on(p.MANIFEST_LOADED,this.onManifestLoaded,this)}unregisterListeners(){this.hls.off(p.MEDIA_ATTACHED,this.onMediaAttached,this),this.hls.off(p.MEDIA_DETACHED,this.onMediaDetached,this),this.hls.off(p.MANIFEST_LOADING,this.onManifestLoading,this),this.hls.off(p.MANIFEST_LOADED,this.onManifestLoaded,this)}getLicenseServerUrl(t){const{drmSystems:e,widevineLicenseUrl:s}=this.config,i=e[t];if(i)return i.licenseUrl;if(t===G.WIDEVINE&&s)return s;throw new Error(`no license server URL configured for key-system "${t}"`)}getServerCertificateUrl(t){const{drmSystems:e}=this.config,s=e[t];if(s)return s.serverCertificateUrl;this.log(`No Server Certificate in config.drmSystems["${t}"]`)}attemptKeySystemAccess(t){const e=this.hls.levels,s=(t,e,s)=>!!t&&s.indexOf(t)===e,i=e.map((t=>t.audioCodec)).filter(s),r=e.map((t=>t.videoCodec)).filter(s);return i.length+r.length===0&&r.push("avc1.42e01e"),new Promise(((e,s)=>{const n=t=>{const a=t.shift();this.getMediaKeysPromise(a,i,r).then((t=>e({keySystem:a,mediaKeys:t}))).catch((e=>{t.length?n(t):s(e instanceof Nn?e:new Nn({type:v.KEY_SYSTEM_ERROR,details:y.KEY_SYSTEM_NO_ACCESS,error:e,fatal:!0},e.message))}))};n(t)}))}requestMediaKeySystemAccess(t,e){const{requestMediaKeySystemAccessFunc:s}=this.config;if("function"!=typeof s){let t=`Configured requestMediaKeySystemAccess is not a function ${s}`;return null===tt&&"http:"===self.location.protocol&&(t=`navigator.requestMediaKeySystemAccess is not available over insecure protocol ${location.protocol}`),Promise.reject(new Error(t))}return s(t,e)}getMediaKeysPromise(t,e,s){const i=function(t,e,s,i){let r;switch(t){case G.FAIRPLAY:r=["cenc","sinf"];break;case G.WIDEVINE:case G.PLAYREADY:r=["cenc"];break;case G.CLEARKEY:r=["cenc","keyids"];break;default:throw new Error(`Unknown key-system: ${t}`)}return function(t,e,s,i){return[{initDataTypes:t,persistentState:i.persistentState||"optional",distinctiveIdentifier:i.distinctiveIdentifier||"optional",sessionTypes:i.sessionTypes||[i.sessionType||"temporary"],audioCapabilities:e.map((t=>({contentType:`audio/mp4; codecs="${t}"`,robustness:i.audioRobustness||"",encryptionScheme:i.audioEncryptionScheme||null}))),videoCapabilities:s.map((t=>({contentType:`video/mp4; codecs="${t}"`,robustness:i.videoRobustness||"",encryptionScheme:i.videoEncryptionScheme||null})))}]}(r,e,s,i)}(t,e,s,this.config.drmSystemOptions),r=this.keySystemAccessPromises[t];let n=null==r?void 0:r.keySystemAccess;if(!n){this.log(`Requesting encrypted media "${t}" key-system access with config: ${JSON.stringify(i)}`),n=this.requestMediaKeySystemAccess(t,i);const e=this.keySystemAccessPromises[t]={keySystemAccess:n};return n.catch((e=>{this.log(`Failed to obtain access to key-system "${t}": ${e}`)})),n.then((s=>{this.log(`Access for key-system "${s.keySystem}" obtained`);const i=this.fetchServerCertificate(t);return this.log(`Create media-keys for "${t}"`),e.mediaKeys=s.createMediaKeys().then((e=>(this.log(`Media-keys created for "${t}"`),i.then((s=>s?this.setMediaKeysServerCertificate(e,t,s):e))))),e.mediaKeys.catch((e=>{this.error(`Failed to create media-keys for "${t}"}: ${e}`)})),e.mediaKeys}))}return n.then((()=>r.mediaKeys))}createMediaKeySessionContext({decryptdata:t,keySystem:e,mediaKeys:s}){this.log(`Creating key-system session "${e}" keyId: ${Tt(t.keyId||[])}`);const i=s.createSession(),r={decryptdata:t,keySystem:e,mediaKeys:s,mediaKeysSession:i,keyStatus:"status-pending"};return this.mediaKeySessions.push(r),r}renewKeySession(t){const e=t.decryptdata;if(e.pssh){const s=this.createMediaKeySessionContext(t),i=this.getKeyIdString(e),r="cenc";this.keyIdToKeySessionPromise[i]=this.generateRequestWithPreferredKeySession(s,r,e.pssh,"expired")}else this.warn("Could not renew expired session. Missing pssh initData.");this.removeSession(t)}getKeyIdString(t){if(!t)throw new Error("Could not read keyId of undefined decryptdata");if(null===t.keyId)throw new Error("keyId is null");return Tt(t.keyId)}updateKeySession(t,e){var s;const i=t.mediaKeysSession;return this.log(`Updating key-session "${i.sessionId}" for keyID ${Tt((null==(s=t.decryptdata)?void 0:s.keyId)||[])}\n } (data length: ${e?e.byteLength:e})`),i.update(e)}selectKeySystemFormat(t){const e=Object.keys(t.levelkeys||{});return this.keyFormatPromise||(this.log(`Selecting key-system from fragment (sn: ${t.sn} ${t.type}: ${t.level}) key formats ${e.join(", ")}`),this.keyFormatPromise=this.getKeyFormatPromise(e)),this.keyFormatPromise}getKeyFormatPromise(t){return new Promise(((e,s)=>{const i=Z(this.config),r=t.map(W).filter((t=>!!t&&-1!==i.indexOf(t)));return this.getKeySystemSelectionPromise(r).then((({keySystem:t})=>{const i=J(t);i?e(i):s(new Error(`Unable to find format for key-system "${t}"`))})).catch(s)}))}loadKey(t){const e=t.keyInfo.decryptdata,s=this.getKeyIdString(e),i=`(keyId: ${s} format: "${e.keyFormat}" method: ${e.method} uri: ${e.uri})`;this.log(`Starting session for key ${i}`);let r=this.keyIdToKeySessionPromise[s];return r||(r=this.keyIdToKeySessionPromise[s]=this.getKeySystemForKeyPromise(e).then((({keySystem:s,mediaKeys:r})=>(this.throwIfDestroyed(),this.log(`Handle encrypted media sn: ${t.frag.sn} ${t.frag.type}: ${t.frag.level} using key ${i}`),this.attemptSetMediaKeys(s,r).then((()=>{this.throwIfDestroyed();const t=this.createMediaKeySessionContext({keySystem:s,mediaKeys:r,decryptdata:e});return this.generateRequestWithPreferredKeySession(t,"cenc",e.pssh,"playlist-key")}))))),r.catch((t=>this.handleError(t)))),r}throwIfDestroyed(t="Invalid state"){if(!this.hls)throw new Error("invalid state")}handleError(t){this.hls&&(this.error(t.message),t instanceof Nn?this.hls.trigger(p.ERROR,t.data):this.hls.trigger(p.ERROR,{type:v.KEY_SYSTEM_ERROR,details:y.KEY_SYSTEM_NO_KEYS,error:t,fatal:!0}))}getKeySystemForKeyPromise(t){const e=this.getKeyIdString(t),s=this.keyIdToKeySessionPromise[e];if(!s){const e=W(t.keyFormat),s=e?[e]:Z(this.config);return this.attemptKeySystemAccess(s)}return s}getKeySystemSelectionPromise(t){if(t.length||(t=Z(this.config)),0===t.length)throw new Nn({type:v.KEY_SYSTEM_ERROR,details:y.KEY_SYSTEM_NO_CONFIGURED_LICENSE,fatal:!0},`Missing key-system license configuration options ${JSON.stringify({drmSystems:this.config.drmSystems})}`);return this.attemptKeySystemAccess(t)}_onMediaEncrypted(t){const{initDataType:e,initData:s}=t,i=`"${t.type}" event: init data type: "${e}"`;if(this.debug(i),null===s)return;let r,n;if("sinf"===e&&this.config.drmSystems[G.FAIRPLAY]){const t=Rt(new Uint8Array(s));try{const e=N(JSON.parse(t).sinf),s=Nt(new Uint8Array(e));if(!s)throw new Error("'schm' box missing or not cbcs/cenc with schi > tenc");r=s.subarray(8,24),n=G.FAIRPLAY}catch(t){return void this.warn(`${i} Failed to parse sinf: ${t}`)}}else{const t=function(t){const e=[];if(t instanceof ArrayBuffer){const s=t.byteLength;let i=0;for(;i+32<s;){const s=Yt(new DataView(t,i));e.push(s),i+=s.size}}return e}(s),e=t.filter((t=>t.systemId===z))[0];if(!e)return void(0===t.length||t.some((t=>!t.systemId))?this.warn(`${i} contains incomplete or invalid pssh data`):this.log(`ignoring ${i} for ${t.map((t=>Q(t.systemId))).join(",")} pssh data in favor of playlist keys`));if(n=Q(e.systemId),0===e.version&&e.data){const t=e.data.length-22;r=e.data.subarray(t,t+16)}}if(!n||!r)return;const a=Tt(r),{keyIdToKeySessionPromise:o,mediaKeySessions:l}=this;let h=o[a];for(let t=0;t<l.length;t++){const i=l[t],n=i.decryptdata;if(!n.keyId)continue;const d=Tt(n.keyId);if(a===d||-1!==n.uri.replace(/-/g,"").indexOf(a)){if(h=o[d],n.pssh)break;delete o[d],n.pssh=new Uint8Array(s),n.keyId=r,h=o[a]=h.then((()=>this.generateRequestWithPreferredKeySession(i,e,s,"encrypted-event-key-match")));break}}h||(h=o[a]=this.getKeySystemSelectionPromise([n]).then((({keySystem:t,mediaKeys:i})=>{var n;this.throwIfDestroyed();const o=new jt("ISO-23001-7",a,null!=(n=J(t))?n:"");return o.pssh=new Uint8Array(s),o.keyId=r,this.attemptSetMediaKeys(t,i).then((()=>{this.throwIfDestroyed();const r=this.createMediaKeySessionContext({decryptdata:o,keySystem:t,mediaKeys:i});return this.generateRequestWithPreferredKeySession(r,e,s,"encrypted-event-no-match")}))}))),h.catch((t=>this.handleError(t)))}_onWaitingForKey(t){this.log(`"${t.type}" event`)}attemptSetMediaKeys(t,e){const s=this.setMediaKeysQueue.slice();this.log(`Setting media-keys for "${t}"`);const i=Promise.all(s).then((()=>{if(!this.media)throw new Error("Attempted to set mediaKeys without media element attached");return this.media.setMediaKeys(e)}));return this.setMediaKeysQueue.push(i),i.then((()=>{this.log(`Media-keys set for "${t}"`),s.push(i),this.setMediaKeysQueue=this.setMediaKeysQueue.filter((t=>-1===s.indexOf(t)))}))}generateRequestWithPreferredKeySession(t,e,s,i){var r,n;const a=null==(r=this.config.drmSystems)||null==(n=r[t.keySystem])?void 0:n.generateRequest;if(a)try{const i=a.call(this.hls,e,s,t);if(!i)throw new Error("Invalid response from configured generateRequest filter");e=i.initDataType,s=t.decryptdata.pssh=i.initData?new Uint8Array(i.initData):null}catch(t){var o;if(this.warn(t.message),null!=(o=this.hls)&&o.config.debug)throw t}if(null===s)return this.log(`Skipping key-session request for "${i}" (no initData)`),Promise.resolve(t);const l=this.getKeyIdString(t.decryptdata);this.log(`Generating key-session request for "${i}": ${l} (init data type: ${e} length: ${s?s.byteLength:null})`);const h=new Cr,d=t._onmessage=e=>{const s=t.mediaKeysSession;if(!s)return void h.emit("error",new Error("invalid state"));const{messageType:i,message:r}=e;this.log(`"${i}" message event for session "${s.sessionId}" message size: ${r.byteLength}`),"license-request"===i||"license-renewal"===i?this.renewLicense(t,r).catch((t=>{this.handleError(t),h.emit("error",t)})):"license-release"===i?t.keySystem===G.FAIRPLAY&&(this.updateKeySession(t,B("acknowledged")),this.removeSession(t)):this.warn(`unhandled media key message type "${i}"`)},c=t._onkeystatuseschange=e=>{if(!t.mediaKeysSession)return void h.emit("error",new Error("invalid state"));this.onKeyStatusChange(t);const s=t.keyStatus;h.emit("keyStatus",s),"expired"===s&&(this.warn(`${t.keySystem} expired for key ${l}`),this.renewKeySession(t))};t.mediaKeysSession.addEventListener("message",d),t.mediaKeysSession.addEventListener("keystatuseschange",c);const u=new Promise(((t,e)=>{h.on("error",e),h.on("keyStatus",(s=>{s.startsWith("usable")?t():"output-restricted"===s?e(new Nn({type:v.KEY_SYSTEM_ERROR,details:y.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED,fatal:!1},"HDCP level output restricted")):"internal-error"===s?e(new Nn({type:v.KEY_SYSTEM_ERROR,details:y.KEY_SYSTEM_STATUS_INTERNAL_ERROR,fatal:!0},`key status changed to "${s}"`)):"expired"===s?e(new Error("key expired while generating request")):this.warn(`unhandled key status change "${s}"`)}))}));return t.mediaKeysSession.generateRequest(e,s).then((()=>{var e;this.log(`Request generated for key-session "${null==(e=t.mediaKeysSession)?void 0:e.sessionId}" keyId: ${l}`)})).catch((t=>{throw new Nn({type:v.KEY_SYSTEM_ERROR,details:y.KEY_SYSTEM_NO_SESSION,error:t,fatal:!1},`Error generating key-session request: ${t}`)})).then((()=>u)).catch((e=>{throw h.removeAllListeners(),this.removeSession(t),e})).then((()=>(h.removeAllListeners(),t)))}onKeyStatusChange(t){t.mediaKeysSession.keyStatuses.forEach(((e,s)=>{this.log(`key status change "${e}" for keyStatuses keyId: ${Tt("buffer"in s?new Uint8Array(s.buffer,s.byteOffset,s.byteLength):new Uint8Array(s))} session keyId: ${Tt(new Uint8Array(t.decryptdata.keyId||[]))} uri: ${t.decryptdata.uri}`),t.keyStatus=e}))}fetchServerCertificate(t){const e=this.config,s=new(0,e.loader)(e),i=this.getServerCertificateUrl(t);return i?(this.log(`Fetching server certificate for "${t}"`),new Promise(((r,n)=>{const a={responseType:"arraybuffer",url:i},o=e.certLoadPolicy.default,l={loadPolicy:o,timeout:o.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0},d={onSuccess:(t,e,s,i)=>{r(t.data)},onError:(e,s,r,o)=>{n(new Nn({type:v.KEY_SYSTEM_ERROR,details:y.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED,fatal:!0,networkDetails:r,response:h({url:a.url,data:void 0},e)},`"${t}" certificate request failed (${i}). Status: ${e.code} (${e.text})`))},onTimeout:(e,s,r)=>{n(new Nn({type:v.KEY_SYSTEM_ERROR,details:y.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED,fatal:!0,networkDetails:r,response:{url:a.url,data:void 0}},`"${t}" certificate request timed out (${i})`))},onAbort:(t,e,s)=>{n(new Error("aborted"))}};s.load(a,l,d)}))):Promise.resolve()}setMediaKeysServerCertificate(t,e,s){return new Promise(((i,r)=>{t.setServerCertificate(s).then((r=>{this.log(`setServerCertificate ${r?"success":"not supported by CDM"} (${null==s?void 0:s.byteLength}) on "${e}"`),i(t)})).catch((t=>{r(new Nn({type:v.KEY_SYSTEM_ERROR,details:y.KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED,error:t,fatal:!0},t.message))}))}))}renewLicense(t,e){return this.requestLicense(t,new Uint8Array(e)).then((e=>this.updateKeySession(t,new Uint8Array(e)).catch((t=>{throw new Nn({type:v.KEY_SYSTEM_ERROR,details:y.KEY_SYSTEM_SESSION_UPDATE_FAILED,error:t,fatal:!0},t.message)}))))}unpackPlayReadyKeyMessage(t,e){const s=String.fromCharCode.apply(null,new Uint16Array(e.buffer));if(!s.includes("PlayReadyKeyMessage"))return t.setRequestHeader("Content-Type","text/xml; charset=utf-8"),e;const i=(new DOMParser).parseFromString(s,"application/xml"),r=i.querySelectorAll("HttpHeader");if(r.length>0){let e;for(let s=0,i=r.length;s<i;s++){var n,a;e=r[s];const i=null==(n=e.querySelector("name"))?void 0:n.textContent,o=null==(a=e.querySelector("value"))?void 0:a.textContent;i&&o&&t.setRequestHeader(i,o)}}const o=i.querySelector("Challenge"),l=null==o?void 0:o.textContent;if(!l)throw new Error("Cannot find <Challenge> in key message");return B(atob(l))}setupLicenseXHR(t,e,s,i){const r=this.config.licenseXhrSetup;return r?Promise.resolve().then((()=>{if(!s.decryptdata)throw new Error("Key removed");return r.call(this.hls,t,e,s,i)})).catch((n=>{if(!s.decryptdata)throw n;return t.open("POST",e,!0),r.call(this.hls,t,e,s,i)})).then((s=>{t.readyState||t.open("POST",e,!0);return{xhr:t,licenseChallenge:s||i}})):(t.open("POST",e,!0),Promise.resolve({xhr:t,licenseChallenge:i}))}requestLicense(t,e){const s=this.config.keyLoadPolicy.default;return new Promise(((i,r)=>{const n=this.getLicenseServerUrl(t.keySystem);this.log(`Sending license request to URL: ${n}`);const a=new XMLHttpRequest;a.responseType="arraybuffer",a.onreadystatechange=()=>{if(!this.hls||!t.mediaKeysSession)return r(new Error("invalid state"));if(4===a.readyState)if(200===a.status){this._requestLicenseFailureCount=0;let e=a.response;this.log(`License received ${e instanceof ArrayBuffer?e.byteLength:e}`);const s=this.config.licenseResponseCallback;if(s)try{e=s.call(this.hls,a,n,t)}catch(t){this.error(t)}i(e)}else{const o=s.errorRetry,l=o?o.maxNumRetry:0;if(this._requestLicenseFailureCount++,this._requestLicenseFailureCount>l||a.status>=400&&a.status<500)r(new Nn({type:v.KEY_SYSTEM_ERROR,details:y.KEY_SYSTEM_LICENSE_REQUEST_FAILED,fatal:!0,networkDetails:a,response:{url:n,data:void 0,code:a.status,text:a.statusText}},`License Request XHR failed (${n}). Status: ${a.status} (${a.statusText})`));else{const s=l-this._requestLicenseFailureCount+1;this.warn(`Retrying license request, ${s} attempts left`),this.requestLicense(t,e).then(i,r)}}},t.licenseXhr&&t.licenseXhr.readyState!==XMLHttpRequest.DONE&&t.licenseXhr.abort(),t.licenseXhr=a,this.setupLicenseXHR(a,n,t,e).then((({xhr:e,licenseChallenge:s})=>{t.keySystem==G.PLAYREADY&&(s=this.unpackPlayReadyKeyMessage(e,s)),e.send(s)}))}))}onMediaAttached(t,e){if(!this.config.emeEnabled)return;const s=e.media;this.media=s,s.addEventListener("encrypted",this.onMediaEncrypted),s.addEventListener("waitingforkey",this.onWaitingForKey)}onMediaDetached(){const t=this.media,e=this.mediaKeySessions;t&&(t.removeEventListener("encrypted",this.onMediaEncrypted),t.removeEventListener("waitingforkey",this.onWaitingForKey),this.media=null),this._requestLicenseFailureCount=0,this.setMediaKeysQueue=[],this.mediaKeySessions=[],this.keyIdToKeySessionPromise={},jt.clearKeyUriToKeyIdMap();const s=e.length;On.CDMCleanupPromise=Promise.all(e.map((t=>this.removeSession(t))).concat(null==t?void 0:t.setMediaKeys(null).catch((t=>{this.log(`Could not clear media keys: ${t}`)})))).then((()=>{s&&(this.log("finished closing key sessions and clearing media keys"),e.length=0)})).catch((t=>{this.log(`Could not close sessions and clear media keys: ${t}`)}))}onManifestLoading(){this.keyFormatPromise=null}onManifestLoaded(t,{sessionKeys:e}){if(e&&this.config.emeEnabled&&!this.keyFormatPromise){const t=e.reduce(((t,e)=>(-1===t.indexOf(e.keyFormat)&&t.push(e.keyFormat),t)),[]);this.log(`Selecting key-system from session-keys ${t.join(", ")}`),this.keyFormatPromise=this.getKeyFormatPromise(t)}}removeSession(t){const{mediaKeysSession:e,licenseXhr:s}=t;if(e){this.log(`Remove licenses and keys and close session ${e.sessionId}`),t._onmessage&&(e.removeEventListener("message",t._onmessage),t._onmessage=void 0),t._onkeystatuseschange&&(e.removeEventListener("keystatuseschange",t._onkeystatuseschange),t._onkeystatuseschange=void 0),s&&s.readyState!==XMLHttpRequest.DONE&&s.abort(),t.mediaKeysSession=t.decryptdata=t.licenseXhr=void 0;const i=this.mediaKeySessions.indexOf(t);return i>-1&&this.mediaKeySessions.splice(i,1),e.remove().catch((t=>{this.log(`Could not remove session: ${t}`)})).then((()=>e.close())).catch((t=>{this.log(`Could not close session: ${t}`)}))}}}On.CDMCleanupPromise=void 0;class Nn extends Error{constructor(t,e){super(e),this.data=void 0,t.error||(t.error=new Error(e)),this.data=t,t.err=t.error}}var Un,Bn,$n;!function(t){t.MANIFEST="m",t.AUDIO="a",t.VIDEO="v",t.MUXED="av",t.INIT="i",t.CAPTION="c",t.TIMED_TEXT="tt",t.KEY="k",t.OTHER="o"}(Un||(Un={})),function(t){t.DASH="d",t.HLS="h",t.SMOOTH="s",t.OTHER="o"}(Bn||(Bn={})),function(t){t.OBJECT="CMCD-Object",t.REQUEST="CMCD-Request",t.SESSION="CMCD-Session",t.STATUS="CMCD-Status"}($n||($n={}));const Gn={[$n.OBJECT]:["br","d","ot","tb"],[$n.REQUEST]:["bl","dl","mtp","nor","nrr","su"],[$n.SESSION]:["cid","pr","sf","sid","st","v"],[$n.STATUS]:["bs","rtp"]};class Kn{constructor(t,e){this.value=void 0,this.params=void 0,Array.isArray(t)&&(t=t.map((t=>t instanceof Kn?t:new Kn(t)))),this.value=t,this.params=e}}class Hn{constructor(t){this.description=void 0,this.description=t}}function Vn(t,e,s,i){return new Error(`failed to ${t} "${r=e,Array.isArray(r)?JSON.stringify(r):r instanceof Map?"Map{}":r instanceof Set?"Set{}":"object"==typeof r?JSON.stringify(r):String(r)}" as ${s}`,{cause:i});var r}const Yn="Bare Item";const Wn=/[\x00-\x1f\x7f]+/;function jn(t,e,s){return Vn("serialize",t,e,s)}function qn(t){if(!1===ArrayBuffer.isView(t))throw jn(t,"Byte Sequence");return`:${e=t,btoa(String.fromCharCode(...e))}:`;var e}function Xn(t){if(function(t){return t<-999999999999999||999999999999999<t}(t))throw jn(t,"Integer");return t.toString()}function zn(t,e){if(t<0)return-zn(-t,e);const s=Math.pow(10,e);if(Math.abs(t*s%1-.5)<Number.EPSILON){const e=Math.floor(t*s);return(e%2==0?e:e+1)/s}return Math.round(t*s)/s}function Qn(t){const e=zn(t,3);if(Math.floor(Math.abs(e)).toString().length>12)throw jn(t,"Decimal");const s=e.toString();return s.includes(".")?s:`${s}.0`}function Jn(t){const e=(s=t).description||s.toString().slice(7,-1);var s;if(!1===/^([a-zA-Z*])([!#$%&'*+\-.^_`|~\w:/]*)$/.test(e))throw jn(e,"Token");return e}function Zn(t){switch(typeof t){case"number":if(!f(t))throw jn(t,Yn);return Number.isInteger(t)?Xn(t):Qn(t);case"string":return function(t){if(Wn.test(t))throw jn(t,"String");return`"${t.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}(t);case"symbol":return Jn(t);case"boolean":return function(t){if("boolean"!=typeof t)throw jn(t,"Boolean");return t?"?1":"?0"}(t);case"object":if(t instanceof Date)return function(t){return`@${Xn(t.getTime()/1e3)}`}(t);if(t instanceof Uint8Array)return qn(t);if(t instanceof Hn)return Jn(t);default:throw jn(t,Yn)}}function ta(t){if(!1===/^[a-z*][a-z0-9\-_.*]*$/.test(t))throw jn(t,"Key");return t}function ea(t){return null==t?"":Object.entries(t).map((([t,e])=>!0===e?`;${ta(t)}`:`;${ta(t)}=${Zn(e)}`)).join("")}function sa(t){return t instanceof Kn?`${Zn(t.value)}${ea(t.params)}`:Zn(t)}function ia(t,e={whitespace:!0}){if("object"!=typeof t)throw jn(t,"Dict");const s=t instanceof Map?t.entries():Object.entries(t),i=null!=e&&e.whitespace?" ":"";return Array.from(s).map((([t,e])=>{e instanceof Kn==!1&&(e=new Kn(e));let s=ta(t);var i;return!0===e.value?s+=ea(e.params):(s+="=",Array.isArray(e.value)?s+=`(${(i=e).value.map(sa).join(" ")})${ea(i.params)}`:s+=sa(e)),s})).join(`,${i}`)}const ra=t=>Math.round(t),na=t=>100*ra(t/100),aa={br:ra,d:ra,bl:na,dl:na,mtp:na,nor:(t,e)=>(null!=e&&e.baseUrl&&(t=function(t,e){const s=new URL(t),i=new URL(e);if(s.origin!==i.origin)return t;const r=s.pathname.split("/").slice(1),n=i.pathname.split("/").slice(1,-1);for(;r[0]===n[0];)r.shift(),n.shift();for(;n.length;)n.shift(),r.unshift("..");return r.join("/")}(t,e.baseUrl)),encodeURIComponent(t)),rtp:na,tb:ra};function oa(t,e){const s={};if(null==t||"object"!=typeof t)return s;const i=Object.keys(t).sort(),r=u({},aa,null==e?void 0:e.formatters),n=null==e?void 0:e.filter;return i.forEach((i=>{if(null!=n&&n(i))return;let a=t[i];const o=r[i];o&&(a=o(a,e)),"v"===i&&1===a||"pr"==i&&1===a||(t=>"number"==typeof t?f(t):null!=t&&""!==t&&!1!==t)(a)&&((t=>"ot"===t||"sf"===t||"st"===t)(i)&&"string"==typeof a&&(a=new Hn(a)),s[i]=a)})),s}function la(t,e={}){return t?function(t,e){return ia(t,e)}(oa(t,e),u({whitespace:!1},e)):""}function ha(t,e,s){return u(t,function(t,e={}){if(!t)return{};const s=Object.entries(t),i=Object.entries(Gn).concat(Object.entries((null==e?void 0:e.customHeaderMap)||{})),r=s.reduce(((t,e)=>{var s;const[r,n]=e,a=(null==(s=i.find((t=>t[1].includes(r))))?void 0:s[0])||$n.REQUEST;return null!=t[a]||(t[a]={}),t[a][r]=n,t}),{});return Object.entries(r).reduce(((t,[s,i])=>(t[s]=la(i,e),t)),{})}(e,s))}const da=/CMCD=[^&#]+/;function ca(t,e,s){const i=function(t,e={}){if(!t)return"";const s=la(t,e);return`CMCD=${encodeURIComponent(s)}`}(e,s);if(!i)return t;if(da.test(t))return t.replace(da,i);const r=t.includes("?")?"&":"?";return`${t}${r}${i}`}function ua(t,e,s,i){t&&Object.keys(e).forEach((r=>{const n=t.filter((t=>t.groupId===r)).map((t=>{const n=u({},t);return n.details=void 0,n.attrs=new k(n.attrs),n.url=n.attrs.URI=fa(t.url,t.attrs["STABLE-RENDITION-ID"],"PER-RENDITION-URIS",s),n.groupId=n.attrs["GROUP-ID"]=e[r],n.attrs["PATHWAY-ID"]=i,n}));t.push(...n)}))}function fa(t,e,s,i){const{HOST:r,PARAMS:n,[s]:a}=i;let o;e&&(o=null==a?void 0:a[e],o&&(t=o));const l=new self.URL(t);return r&&!o&&(l.host=r),n&&Object.keys(n).sort().forEach((t=>{t&&l.searchParams.set(t,n[t])})),l.href}const ga=/^age:\s*[\d.]+\s*$/im;class ma{constructor(t){this.xhrSetup=void 0,this.requestTimeout=void 0,this.retryTimeout=void 0,this.retryDelay=void 0,this.config=null,this.callbacks=null,this.context=null,this.loader=null,this.stats=void 0,this.xhrSetup=t&&t.xhrSetup||null,this.stats=new I,this.retryDelay=0}destroy(){this.callbacks=null,this.abortInternal(),this.loader=null,this.config=null,this.context=null,this.xhrSetup=null}abortInternal(){const t=this.loader;self.clearTimeout(this.requestTimeout),self.clearTimeout(this.retryTimeout),t&&(t.onreadystatechange=null,t.onprogress=null,4!==t.readyState&&(this.stats.aborted=!0,t.abort()))}abort(){var t;this.abortInternal(),null!=(t=this.callbacks)&&t.onAbort&&this.callbacks.onAbort(this.stats,this.context,this.loader)}load(t,e,s){if(this.stats.loading.start)throw new Error("Loader can only be used once.");this.stats.loading.start=self.performance.now(),this.context=t,this.config=e,this.callbacks=s,this.loadInternal()}loadInternal(){const{config:t,context:e}=this;if(!t||!e)return;const s=this.loader=new self.XMLHttpRequest,i=this.stats;i.loading.first=0,i.loaded=0,i.aborted=!1;const r=this.xhrSetup;r?Promise.resolve().then((()=>{if(this.loader===s&&!this.stats.aborted)return r(s,e.url)})).catch((t=>{if(this.loader===s&&!this.stats.aborted)return s.open("GET",e.url,!0),r(s,e.url)})).then((()=>{this.loader!==s||this.stats.aborted||this.openAndSendXhr(s,e,t)})).catch((t=>{this.callbacks.onError({code:s.status,text:t.message},e,s,i)})):this.openAndSendXhr(s,e,t)}openAndSendXhr(t,e,s){t.readyState||t.open("GET",e.url,!0);const i=e.headers,{maxTimeToFirstByteMs:r,maxLoadTimeMs:n}=s.loadPolicy;if(i)for(const e in i)t.setRequestHeader(e,i[e]);e.rangeEnd&&t.setRequestHeader("Range","bytes="+e.rangeStart+"-"+(e.rangeEnd-1)),t.onreadystatechange=this.readystatechange.bind(this),t.onprogress=this.loadprogress.bind(this),t.responseType=e.responseType,self.clearTimeout(this.requestTimeout),s.timeout=r&&f(r)?r:n,this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),s.timeout),t.send()}readystatechange(){const{context:t,loader:e,stats:s}=this;if(!t||!e)return;const i=e.readyState,r=this.config;if(!s.aborted&&i>=2&&(0===s.loading.first&&(s.loading.first=Math.max(self.performance.now(),s.loading.start),r.timeout!==r.loadPolicy.maxLoadTimeMs&&(self.clearTimeout(this.requestTimeout),r.timeout=r.loadPolicy.maxLoadTimeMs,this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),r.loadPolicy.maxLoadTimeMs-(s.loading.first-s.loading.start)))),4===i)){self.clearTimeout(this.requestTimeout),e.onreadystatechange=null,e.onprogress=null;const i=e.status,n="text"!==e.responseType;if(i>=200&&i<300&&(n&&e.response||null!==e.responseText)){s.loading.end=Math.max(self.performance.now(),s.loading.first);const r=n?e.response:e.responseText,a="arraybuffer"===e.responseType?r.byteLength:r.length;if(s.loaded=s.total=a,s.bwEstimate=8e3*s.total/(s.loading.end-s.loading.first),!this.callbacks)return;const o=this.callbacks.onProgress;if(o&&o(s,t,r,e),!this.callbacks)return;const l={url:e.responseURL,data:r,code:i};this.callbacks.onSuccess(l,s,t,e)}else{const n=r.loadPolicy.errorRetry;ms(n,s.retry,!1,{url:t.url,data:void 0,code:i})?this.retry(n):(A.error(`${i} while loading ${t.url}`),this.callbacks.onError({code:i,text:e.statusText},t,e,s))}}}loadtimeout(){if(!this.config)return;const t=this.config.loadPolicy.timeoutRetry;if(ms(t,this.stats.retry,!0))this.retry(t);else{var e;A.warn(`timeout while loading ${null==(e=this.context)?void 0:e.url}`);const t=this.callbacks;t&&(this.abortInternal(),t.onTimeout(this.stats,this.context,this.loader))}}retry(t){const{context:e,stats:s}=this;this.retryDelay=fs(t,s.retry),s.retry++,A.warn(`${status?"HTTP Status "+status:"Timeout"} while loading ${null==e?void 0:e.url}, retrying ${s.retry}/${t.maxNumRetry} in ${this.retryDelay}ms`),this.abortInternal(),this.loader=null,self.clearTimeout(this.retryTimeout),this.retryTimeout=self.setTimeout(this.loadInternal.bind(this),this.retryDelay)}loadprogress(t){const e=this.stats;e.loaded=t.loaded,t.lengthComputable&&(e.total=t.total)}getCacheAge(){let t=null;if(this.loader&&ga.test(this.loader.getAllResponseHeaders())){const e=this.loader.getResponseHeader("age");t=e?parseFloat(e):null}return t}getResponseHeader(t){return this.loader&&new RegExp(`^${t}:\\s*[\\d.]+\\s*$`,"im").test(this.loader.getAllResponseHeaders())?this.loader.getResponseHeader(t):null}}const pa=/(\d+)-(\d+)\/(\d+)/;class va{constructor(t){this.fetchSetup=void 0,this.requestTimeout=void 0,this.request=null,this.response=null,this.controller=void 0,this.context=null,this.config=null,this.callbacks=null,this.stats=void 0,this.loader=null,this.fetchSetup=t.fetchSetup||ya,this.controller=new self.AbortController,this.stats=new I}destroy(){this.loader=this.callbacks=this.context=this.config=this.request=null,this.abortInternal(),this.response=null,this.fetchSetup=this.controller=this.stats=null}abortInternal(){this.controller&&!this.stats.loading.end&&(this.stats.aborted=!0,this.controller.abort())}abort(){var t;this.abortInternal(),null!=(t=this.callbacks)&&t.onAbort&&this.callbacks.onAbort(this.stats,this.context,this.response)}load(t,e,s){const i=this.stats;if(i.loading.start)throw new Error("Loader can only be used once.");i.loading.start=self.performance.now();const r=function(t,e){const s={method:"GET",mode:"cors",credentials:"same-origin",signal:e,headers:new self.Headers(u({},t.headers))};t.rangeEnd&&s.headers.set("Range","bytes="+t.rangeStart+"-"+String(t.rangeEnd-1));return s}(t,this.controller.signal),n=s.onProgress,a="arraybuffer"===t.responseType,o=a?"byteLength":"length",{maxTimeToFirstByteMs:l,maxLoadTimeMs:h}=e.loadPolicy;this.context=t,this.config=e,this.callbacks=s,this.request=this.fetchSetup(t,r),self.clearTimeout(this.requestTimeout),e.timeout=l&&f(l)?l:h,this.requestTimeout=self.setTimeout((()=>{this.abortInternal(),s.onTimeout(i,t,this.response)}),e.timeout),self.fetch(this.request).then((r=>{this.response=this.loader=r;const o=Math.max(self.performance.now(),i.loading.start);if(self.clearTimeout(this.requestTimeout),e.timeout=h,this.requestTimeout=self.setTimeout((()=>{this.abortInternal(),s.onTimeout(i,t,this.response)}),h-(o-i.loading.start)),!r.ok){const{status:t,statusText:e}=r;throw new Ea(e||"fetch, bad network response",t,r)}return i.loading.first=o,i.total=function(t){const e=t.get("Content-Range");if(e){const t=function(t){const e=pa.exec(t);if(e)return parseInt(e[2])-parseInt(e[1])+1}(e);if(f(t))return t}const s=t.get("Content-Length");if(s)return parseInt(s)}(r.headers)||i.total,n&&f(e.highWaterMark)?this.loadProgressively(r,i,t,e.highWaterMark,n):a?r.arrayBuffer():"json"===t.responseType?r.json():r.text()})).then((r=>{const a=this.response;if(!a)throw new Error("loader destroyed");self.clearTimeout(this.requestTimeout),i.loading.end=Math.max(self.performance.now(),i.loading.first);const l=r[o];l&&(i.loaded=i.total=l);const h={url:a.url,data:r,code:a.status};n&&!f(e.highWaterMark)&&n(i,t,r,a),s.onSuccess(h,i,t,a)})).catch((e=>{if(self.clearTimeout(this.requestTimeout),i.aborted)return;const r=e&&e.code||0,n=e?e.message:null;s.onError({code:r,text:n},t,e?e.details:null,i)}))}getCacheAge(){let t=null;if(this.response){const e=this.response.headers.get("age");t=e?parseFloat(e):null}return t}getResponseHeader(t){return this.response?this.response.headers.get(t):null}loadProgressively(t,e,s,i=0,r){const n=new bi,a=t.body.getReader(),o=()=>a.read().then((a=>{if(a.done)return n.dataLength&&r(e,s,n.flush(),t),Promise.resolve(new ArrayBuffer(0));const l=a.value,h=l.length;return e.loaded+=h,h<i||n.dataLength?(n.push(l),n.dataLength>=i&&r(e,s,n.flush(),t)):r(e,s,l,t),o()})).catch((()=>Promise.reject()));return o()}}function ya(t,e){return new self.Request(t.url,e)}class Ea extends Error{constructor(t,e,s){super(t),this.code=void 0,this.details=void 0,this.code=e,this.details=s}}const Ta=/\s/,Sa={newCue(t,e,s,i){const r=[];let n,a,o,l,h;const d=self.VTTCue||self.TextTrackCue;for(let u=0;u<i.rows.length;u++)if(n=i.rows[u],o=!0,l=0,h="",!n.isEmpty()){var c;for(let t=0;t<n.chars.length;t++)Ta.test(n.chars[t].uchar)&&o?l++:(h+=n.chars[t].uchar,o=!1);n.cueStartTime=e,e===s&&(s+=1e-4),l>=16?l--:l++;const i=gn(h.trim()),f=En(e,s,i);null!=t&&null!=(c=t.cues)&&c.getCueById(f)||(a=new d(e,s,i),a.id=f,a.line=u+1,a.align="left",a.position=10+Math.min(80,10*Math.floor(8*l/32)),r.push(a))}return t&&r.length&&(r.sort(((t,e)=>"auto"===t.line||"auto"===e.line?0:t.line>8&&e.line>8?e.line-t.line:t.line-e.line)),r.forEach((e=>Fe(t,e)))),r}},La=h(h({autoStartLoad:!0,startPosition:-1,defaultAudioCodec:void 0,debug:!1,capLevelOnFPSDrop:!1,capLevelToPlayerSize:!1,ignoreDevicePixelRatio:!1,preferManagedMediaSource:!0,initialLiveManifestSize:1,maxBufferLength:30,backBufferLength:1/0,frontBufferFlushThreshold:1/0,maxBufferSize:6e7,maxBufferHole:.1,highBufferWatchdogPeriod:2,nudgeOffset:.1,nudgeMaxRetry:3,maxFragLookUpTolerance:.25,liveSyncDurationCount:3,liveMaxLatencyDurationCount:1/0,liveSyncDuration:void 0,liveMaxLatencyDuration:void 0,maxLiveSyncPlaybackRate:1,liveDurationInfinity:!1,liveBackBufferLength:null,maxMaxBufferLength:600,enableWorker:!0,workerPath:null,enableSoftwareAES:!0,startLevel:void 0,startFragPrefetch:!1,fpsDroppedMonitoringPeriod:5e3,fpsDroppedMonitoringThreshold:.2,appendErrorMaxRetry:3,loader:ma,fLoader:void 0,pLoader:void 0,xhrSetup:void 0,licenseXhrSetup:void 0,licenseResponseCallback:void 0,abrController:class{constructor(t){this.hls=void 0,this.lastLevelLoadSec=0,this.lastLoadedFragLevel=-1,this.firstSelection=-1,this._nextAutoLevel=-1,this.nextAutoLevelKey="",this.audioTracksByGroup=null,this.codecTiers=null,this.timer=-1,this.fragCurrent=null,this.partCurrent=null,this.bitrateTestDelay=0,this.bwEstimator=void 0,this._abandonRulesCheck=()=>{const{fragCurrent:t,partCurrent:e,hls:s}=this,{autoLevelEnabled:i,media:r}=s;if(!t||!r)return;const n=performance.now(),a=e?e.stats:t.stats,o=e?e.duration:t.duration,l=n-a.loading.start,h=s.minAutoLevel;if(a.aborted||a.loaded&&a.loaded===a.total||t.level<=h)return this.clearTimer(),void(this._nextAutoLevel=-1);if(!i||r.paused||!r.playbackRate||!r.readyState)return;const d=s.mainForwardBufferInfo;if(null===d)return;const c=this.bwEstimator.getEstimateTTFB(),u=Math.abs(r.playbackRate);if(l<=Math.max(c,o/(2*u)*1e3))return;const g=d.len/u,m=a.loading.first?a.loading.first-a.loading.start:-1,v=a.loaded&&m>-1,y=this.getBwEstimate(),E=s.levels,T=E[t.level],S=a.total||Math.max(a.loaded,Math.round(o*T.averageBitrate/8));let L=v?l-m:l;L<1&&v&&(L=Math.min(l,8*a.loaded/y));const R=v?1e3*a.loaded/L:0,b=R?(S-a.loaded)/R:8*S/y+c/1e3;if(b<=g)return;const k=R?8*R:y;let w,D=Number.POSITIVE_INFINITY;for(w=t.level-1;w>h;w--){const t=E[w].maxBitrate;if(D=this.getTimeToLoadFrag(c/1e3,k,o*t,!E[w].details),D<g)break}if(D>=b)return;if(D>10*o)return;s.nextLoadLevel=s.nextAutoLevel=w,v?this.bwEstimator.sample(l-Math.min(c,m),a.loaded):this.bwEstimator.sampleTTFB(l);const I=E[w].maxBitrate;this.getBwEstimate()*this.hls.config.abrBandWidthUpFactor>I&&this.resetEstimator(I),this.clearTimer(),A.warn(`[abr] Fragment ${t.sn}${e?" part "+e.index:""} of level ${t.level} is loading too slowly;\n Time to underbuffer: ${g.toFixed(3)} s\n Estimated load time for current fragment: ${b.toFixed(3)} s\n Estimated load time for down switch fragment: ${D.toFixed(3)} s\n TTFB estimate: ${0|m} ms\n Current BW estimate: ${f(y)?0|y:"Unknown"} bps\n New BW estimate: ${0|this.getBwEstimate()} bps\n Switching to level ${w} @ ${0|I} bps`),s.trigger(p.FRAG_LOAD_EMERGENCY_ABORTED,{frag:t,part:e,stats:a})},this.hls=t,this.bwEstimator=this.initEstimator(),this.registerListeners()}resetEstimator(t){t&&(A.log(`setting initial bwe to ${t}`),this.hls.config.abrEwmaDefaultEstimate=t),this.firstSelection=-1,this.bwEstimator=this.initEstimator()}initEstimator(){const t=this.hls.config;return new Is(t.abrEwmaSlowVoD,t.abrEwmaFastVoD,t.abrEwmaDefaultEstimate)}registerListeners(){const{hls:t}=this;t.on(p.MANIFEST_LOADING,this.onManifestLoading,this),t.on(p.FRAG_LOADING,this.onFragLoading,this),t.on(p.FRAG_LOADED,this.onFragLoaded,this),t.on(p.FRAG_BUFFERED,this.onFragBuffered,this),t.on(p.LEVEL_SWITCHING,this.onLevelSwitching,this),t.on(p.LEVEL_LOADED,this.onLevelLoaded,this),t.on(p.LEVELS_UPDATED,this.onLevelsUpdated,this),t.on(p.MAX_AUTO_LEVEL_UPDATED,this.onMaxAutoLevelUpdated,this),t.on(p.ERROR,this.onError,this)}unregisterListeners(){const{hls:t}=this;t&&(t.off(p.MANIFEST_LOADING,this.onManifestLoading,this),t.off(p.FRAG_LOADING,this.onFragLoading,this),t.off(p.FRAG_LOADED,this.onFragLoaded,this),t.off(p.FRAG_BUFFERED,this.onFragBuffered,this),t.off(p.LEVEL_SWITCHING,this.onLevelSwitching,this),t.off(p.LEVEL_LOADED,this.onLevelLoaded,this),t.off(p.LEVELS_UPDATED,this.onLevelsUpdated,this),t.off(p.MAX_AUTO_LEVEL_UPDATED,this.onMaxAutoLevelUpdated,this),t.off(p.ERROR,this.onError,this))}destroy(){this.unregisterListeners(),this.clearTimer(),this.hls=this._abandonRulesCheck=null,this.fragCurrent=this.partCurrent=null}onManifestLoading(t,e){this.lastLoadedFragLevel=-1,this.firstSelection=-1,this.lastLevelLoadSec=0,this.fragCurrent=this.partCurrent=null,this.onLevelsUpdated(),this.clearTimer()}onLevelsUpdated(){this.lastLoadedFragLevel>-1&&this.fragCurrent&&(this.lastLoadedFragLevel=this.fragCurrent.level),this._nextAutoLevel=-1,this.onMaxAutoLevelUpdated(),this.codecTiers=null,this.audioTracksByGroup=null}onMaxAutoLevelUpdated(){this.firstSelection=-1,this.nextAutoLevelKey=""}onFragLoading(t,e){const s=e.frag;if(!this.ignoreFragment(s)){var i;if(!s.bitrateTest)this.fragCurrent=s,this.partCurrent=null!=(i=e.part)?i:null;this.clearTimer(),this.timer=self.setInterval(this._abandonRulesCheck,100)}}onLevelSwitching(t,e){this.clearTimer()}onError(t,e){if(!e.fatal)switch(e.details){case y.BUFFER_ADD_CODEC_ERROR:case y.BUFFER_APPEND_ERROR:this.lastLoadedFragLevel=-1,this.firstSelection=-1;break;case y.FRAG_LOAD_TIMEOUT:{const t=e.frag,{fragCurrent:s,partCurrent:i}=this;if(t&&s&&t.sn===s.sn&&t.level===s.level){const e=performance.now(),s=i?i.stats:t.stats,r=e-s.loading.start,n=s.loading.first?s.loading.first-s.loading.start:-1;if(s.loaded&&n>-1){const t=this.bwEstimator.getEstimateTTFB();this.bwEstimator.sample(r-Math.min(t,n),s.loaded)}else this.bwEstimator.sampleTTFB(r)}break}}}getTimeToLoadFrag(t,e,s,i){return t+s/e+(i?this.lastLevelLoadSec:0)}onLevelLoaded(t,e){const s=this.hls.config,{loading:i}=e.stats,r=i.end-i.start;f(r)&&(this.lastLevelLoadSec=r/1e3),e.details.live?this.bwEstimator.update(s.abrEwmaSlowLive,s.abrEwmaFastLive):this.bwEstimator.update(s.abrEwmaSlowVoD,s.abrEwmaFastVoD)}onFragLoaded(t,{frag:e,part:s}){const i=s?s.stats:e.stats;if(e.type===De&&this.bwEstimator.sampleTTFB(i.loading.first-i.loading.start),!this.ignoreFragment(e)){if(this.clearTimer(),e.level===this._nextAutoLevel&&(this._nextAutoLevel=-1),this.firstSelection=-1,this.hls.config.abrMaxWithRealBitrate){const t=s?s.duration:e.duration,r=this.hls.levels[e.level],n=(r.loaded?r.loaded.bytes:0)+i.loaded,a=(r.loaded?r.loaded.duration:0)+t;r.loaded={bytes:n,duration:a},r.realBitrate=Math.round(8*n/a)}if(e.bitrateTest){const t={stats:i,frag:e,part:s,id:e.type};this.onFragBuffered(p.FRAG_BUFFERED,t),e.bitrateTest=!1}else this.lastLoadedFragLevel=e.level}}onFragBuffered(t,e){const{frag:s,part:i}=e,r=null!=i&&i.stats.loaded?i.stats:s.stats;if(r.aborted)return;if(this.ignoreFragment(s))return;const n=r.parsing.end-r.loading.start-Math.min(r.loading.first-r.loading.start,this.bwEstimator.getEstimateTTFB());this.bwEstimator.sample(n,r.loaded),r.bwEstimate=this.getBwEstimate(),s.bitrateTest?this.bitrateTestDelay=n/1e3:this.bitrateTestDelay=0}ignoreFragment(t){return t.type!==De||"initSegment"===t.sn}clearTimer(){this.timer>-1&&(self.clearInterval(this.timer),this.timer=-1)}get firstAutoLevel(){const{maxAutoLevel:t,minAutoLevel:e}=this.hls,s=this.getBwEstimate(),i=this.hls.config.maxStarvationDelay,r=this.findBestLevel(s,e,t,0,i,1,1);if(r>-1)return r;const n=this.hls.firstLevel,a=Math.min(Math.max(n,e),t);return A.warn(`[abr] Could not find best starting auto level. Defaulting to first in playlist ${n} clamped to ${a}`),a}get forcedAutoLevel(){return this.nextAutoLevelKey?-1:this._nextAutoLevel}get nextAutoLevel(){const t=this.forcedAutoLevel,e=this.bwEstimator.canEstimate(),s=this.lastLoadedFragLevel>-1;if(!(-1===t||e&&s&&this.nextAutoLevelKey!==this.getAutoLevelKey()))return t;const i=e&&s?this.getNextABRAutoLevel():this.firstAutoLevel;if(-1!==t){const e=this.hls.levels;if(e.length>Math.max(t,i)&&e[t].loadError<=e[i].loadError)return t}return this._nextAutoLevel=i,this.nextAutoLevelKey=this.getAutoLevelKey(),i}getAutoLevelKey(){return`${this.getBwEstimate()}_${this.getStarvationDelay().toFixed(2)}`}getNextABRAutoLevel(){const{fragCurrent:t,partCurrent:e,hls:s}=this,{maxAutoLevel:i,config:r,minAutoLevel:n}=s,a=e?e.duration:t?t.duration:0,o=this.getBwEstimate(),l=this.getStarvationDelay();let h=r.abrBandWidthFactor,d=r.abrBandWidthUpFactor;if(l){const t=this.findBestLevel(o,n,i,l,0,h,d);if(t>=0)return t}let c=a?Math.min(a,r.maxStarvationDelay):r.maxStarvationDelay;if(!l){const t=this.bitrateTestDelay;if(t){c=(a?Math.min(a,r.maxLoadingDelay):r.maxLoadingDelay)-t,A.info(`[abr] bitrate test took ${Math.round(1e3*t)}ms, set first fragment max fetchDuration to ${Math.round(1e3*c)} ms`),h=d=1}}const u=this.findBestLevel(o,n,i,l,c,h,d);if(A.info(`[abr] ${l?"rebuffering expected":"buffer is empty"}, optimal quality level ${u}`),u>-1)return u;const f=s.levels[n],g=s.levels[s.loadLevel];return(null==f?void 0:f.bitrate)<(null==g?void 0:g.bitrate)?n:s.loadLevel}getStarvationDelay(){const t=this.hls,e=t.media;if(!e)return 1/0;const s=e&&0!==e.playbackRate?Math.abs(e.playbackRate):1,i=t.mainForwardBufferInfo;return(i?i.len:0)/s}getBwEstimate(){return this.bwEstimator.canEstimate()?this.bwEstimator.getEstimate():this.hls.config.abrEwmaDefaultEstimate}findBestLevel(t,e,s,i,r,n,a){var o;const l=i+r,h=this.lastLoadedFragLevel,d=-1===h?this.hls.firstLevel:h,{fragCurrent:c,partCurrent:u}=this,{levels:g,allAudioTracks:m,loadLevel:p,config:v}=this.hls;if(1===g.length)return 0;const y=g[d],E=!(null==y||null==(o=y.details)||!o.live),T=-1===p||-1===h;let S,L="SDR",R=(null==y?void 0:y.frameRate)||0;const{audioPreference:b,videoPreference:k}=v,w=this.audioTracksByGroup||(this.audioTracksByGroup=function(t){return t.reduce(((t,e)=>{let s=t.groups[e.groupId];s||(s=t.groups[e.groupId]={tracks:[],channels:{2:0},hasDefault:!1,hasAutoSelect:!1}),s.tracks.push(e);const i=e.channels||"2";return s.channels[i]=(s.channels[i]||0)+1,s.hasDefault=s.hasDefault||e.default,s.hasAutoSelect=s.hasAutoSelect||e.autoselect,s.hasDefault&&(t.hasDefaultAudio=!0),s.hasAutoSelect&&(t.hasAutoSelectAudio=!0),t}),{hasDefaultAudio:!1,hasAutoSelectAudio:!1,groups:{}})}(m));if(T){if(-1!==this.firstSelection)return this.firstSelection;const i=this.codecTiers||(this.codecTiers=function(t,e,s,i){return t.slice(s,i+1).reduce(((t,s)=>{if(!s.codecSet)return t;const i=s.audioGroups;let r=t[s.codecSet];r||(t[s.codecSet]=r={minBitrate:1/0,minHeight:1/0,minFramerate:1/0,maxScore:0,videoRanges:{SDR:0},channels:{2:0},hasDefaultAudio:!i,fragmentError:0}),r.minBitrate=Math.min(r.minBitrate,s.bitrate);const n=Math.min(s.height,s.width);return r.minHeight=Math.min(r.minHeight,n),r.minFramerate=Math.min(r.minFramerate,s.frameRate),r.maxScore=Math.max(r.maxScore,s.score),r.fragmentError+=s.fragmentError,r.videoRanges[s.videoRange]=(r.videoRanges[s.videoRange]||0)+1,i&&i.forEach((t=>{if(!t)return;const s=e.groups[t];s&&(r.hasDefaultAudio=r.hasDefaultAudio||e.hasDefaultAudio?s.hasDefault:s.hasAutoSelect||!e.hasDefaultAudio&&!e.hasAutoSelectAudio,Object.keys(s.channels).forEach((t=>{r.channels[t]=(r.channels[t]||0)+s.channels[t]})))})),t}),{})}(g,w,e,s)),r=function(t,e,s,i,r){const n=Object.keys(t),a=null==i?void 0:i.channels,o=null==i?void 0:i.audioCodec,l=a&&2===parseInt(a);let h=!0,d=!1,c=1/0,u=1/0,g=1/0,m=0,p=[];const{preferHDR:v,allowedVideoRanges:y}=Ms(e,r);for(let e=n.length;e--;){const s=t[n[e]];h=s.channels[2]>0,c=Math.min(c,s.minHeight),u=Math.min(u,s.minFramerate),g=Math.min(g,s.minBitrate);const i=y.filter((t=>s.videoRanges[t]>0));i.length>0&&(d=!0,p=i)}c=f(c)?c:0,u=f(u)?u:0;const E=Math.max(1080,c),T=Math.max(30,u);return g=f(g)?g:s,s=Math.max(g,s),d||(e=void 0,p=[]),{codecSet:n.reduce(((e,i)=>{const r=t[i];if(i===e)return e;if(r.minBitrate>s)return Fs(i,`min bitrate of ${r.minBitrate} > current estimate of ${s}`),e;if(!r.hasDefaultAudio)return Fs(i,"no renditions with default or auto-select sound found"),e;if(o&&i.indexOf(o.substring(0,4))%5!=0)return Fs(i,`audio codec preference "${o}" not found`),e;if(a&&!l){if(!r.channels[a])return Fs(i,`no renditions with ${a} channel sound found (channels options: ${Object.keys(r.channels)})`),e}else if((!o||l)&&h&&0===r.channels[2])return Fs(i,"no renditions with stereo sound found"),e;return r.minHeight>E?(Fs(i,`min resolution of ${r.minHeight} > maximum of ${E}`),e):r.minFramerate>T?(Fs(i,`min framerate of ${r.minFramerate} > maximum of ${T}`),e):p.some((t=>r.videoRanges[t]>0))?r.maxScore<m?(Fs(i,`max score of ${r.maxScore} < selected max of ${m}`),e):e&&(ae(i)>=ae(e)||r.fragmentError>t[e].fragmentError)?e:(m=r.maxScore,i):(Fs(i,`no variants with VIDEO-RANGE of ${JSON.stringify(p)} found`),e)}),void 0),videoRanges:p,preferHDR:v,minFramerate:u,minBitrate:g}}(i,L,t,b,k),{codecSet:n,videoRanges:a,minFramerate:o,minBitrate:l,preferHDR:h}=r;S=n,L=h?a[a.length-1]:a[0],R=o,t=Math.max(t,l),A.log(`[abr] picked start tier ${JSON.stringify(r)}`)}else S=null==y?void 0:y.codecSet,L=null==y?void 0:y.videoRange;const D=u?u.duration:c?c.duration:0,I=this.bwEstimator.getEstimateTTFB()/1e3,C=[];for(let o=s;o>=e;o--){var _;const e=g[o],c=o>d;if(!e)continue;if(v.useMediaCapabilities&&!e.supportedResult&&!e.supportedPromise){const s=navigator.mediaCapabilities;"function"==typeof(null==s?void 0:s.decodingInfo)&&xs(e,w,L,R,t,b)?(e.supportedPromise=Ps(e,w,s),e.supportedPromise.then((t=>{if(!this.hls)return;e.supportedResult=t;const s=this.hls.levels,i=s.indexOf(e);t.error?A.warn(`[abr] MediaCapabilities decodingInfo error: "${t.error}" for level ${i} ${JSON.stringify(t)}`):t.supported||(A.warn(`[abr] Unsupported MediaCapabilities decodingInfo result for level ${i} ${JSON.stringify(t)}`),i>-1&&s.length>1&&(A.log(`[abr] Removing unsupported level ${i}`),this.hls.removeLevel(i)))}))):e.supportedResult=Cs}if(S&&e.codecSet!==S||L&&e.videoRange!==L||c&&R>e.frameRate||!c&&R>0&&R<e.frameRate||e.supportedResult&&(null==(_=e.supportedResult.decodingInfoResults)||!_[0].smooth)){C.push(o);continue}const m=e.details,k=(u?null==m?void 0:m.partTarget:null==m?void 0:m.averagetargetduration)||D;let x;x=c?a*t:n*t;const P=D&&i>=2*D&&0===r?g[o].averageBitrate:g[o].maxBitrate,M=this.getTimeToLoadFrag(I,x,P*k,void 0===m);if(x>=P&&(o===h||0===e.loadError&&0===e.fragmentError)&&(M<=I||!f(M)||E&&!this.bitrateTestDelay||M<l)){const t=this.forcedAutoLevel;return o===p||-1!==t&&t===p||(C.length&&A.trace(`[abr] Skipped level(s) ${C.join(",")} of ${s} max with CODECS and VIDEO-RANGE:"${g[C[0]].codecs}" ${g[C[0]].videoRange}; not compatible with "${y.codecs}" ${L}`),A.info(`[abr] switch candidate:${d}->${o} adjustedbw(${Math.round(x)})-bitrate=${Math.round(x-P)} ttfb:${I.toFixed(1)} avgDuration:${k.toFixed(1)} maxFetchDuration:${l.toFixed(1)} fetchDuration:${M.toFixed(1)} firstSelection:${T} codecSet:${S} videoRange:${L} hls.loadLevel:${p}`)),T&&(this.firstSelection=o),o}}return-1}set nextAutoLevel(t){const{maxAutoLevel:e,minAutoLevel:s}=this.hls,i=Math.min(Math.max(t,s),e);this._nextAutoLevel!==i&&(this.nextAutoLevelKey="",this._nextAutoLevel=i)}},bufferController:class{constructor(t){this.details=null,this._objectUrl=null,this.operationQueue=void 0,this.listeners=void 0,this.hls=void 0,this.bufferCodecEventsExpected=0,this._bufferCodecEventsTotal=0,this.media=null,this.mediaSource=null,this.lastMpegAudioChunk=null,this.appendSource=void 0,this.appendErrors={audio:0,video:0,audiovideo:0},this.tracks={},this.pendingTracks={},this.sourceBuffer=void 0,this.log=void 0,this.warn=void 0,this.error=void 0,this._onEndStreaming=t=>{this.hls&&this.hls.pauseBuffering()},this._onStartStreaming=t=>{this.hls&&this.hls.resumeBuffering()},this._onMediaSourceOpen=()=>{const{media:t,mediaSource:e}=this;this.log("Media source opened"),t&&(t.removeEventListener("emptied",this._onMediaEmptied),this.updateMediaElementDuration(),this.hls.trigger(p.MEDIA_ATTACHED,{media:t,mediaSource:e})),e&&e.removeEventListener("sourceopen",this._onMediaSourceOpen),this.checkPendingTracks()},this._onMediaSourceClose=()=>{this.log("Media source closed")},this._onMediaSourceEnded=()=>{this.log("Media source ended")},this._onMediaEmptied=()=>{const{mediaSrc:t,_objectUrl:e}=this;t!==e&&A.error(`Media element src was set while attaching MediaSource (${e} > ${t})`)},this.hls=t;const e="[buffer-controller]";var s;this.appendSource=(s=te(t.config.preferManagedMediaSource),"undefined"!=typeof self&&s===self.ManagedMediaSource),this.log=A.log.bind(A,e),this.warn=A.warn.bind(A,e),this.error=A.error.bind(A,e),this._initSourceBuffer(),this.registerListeners()}hasSourceTypes(){return this.getSourceBufferTypes().length>0||Object.keys(this.pendingTracks).length>0}destroy(){this.unregisterListeners(),this.details=null,this.lastMpegAudioChunk=null,this.hls=null}registerListeners(){const{hls:t}=this;t.on(p.MEDIA_ATTACHING,this.onMediaAttaching,this),t.on(p.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(p.MANIFEST_LOADING,this.onManifestLoading,this),t.on(p.MANIFEST_PARSED,this.onManifestParsed,this),t.on(p.BUFFER_RESET,this.onBufferReset,this),t.on(p.BUFFER_APPENDING,this.onBufferAppending,this),t.on(p.BUFFER_CODECS,this.onBufferCodecs,this),t.on(p.BUFFER_EOS,this.onBufferEos,this),t.on(p.BUFFER_FLUSHING,this.onBufferFlushing,this),t.on(p.LEVEL_UPDATED,this.onLevelUpdated,this),t.on(p.FRAG_PARSED,this.onFragParsed,this),t.on(p.FRAG_CHANGED,this.onFragChanged,this)}unregisterListeners(){const{hls:t}=this;t.off(p.MEDIA_ATTACHING,this.onMediaAttaching,this),t.off(p.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(p.MANIFEST_LOADING,this.onManifestLoading,this),t.off(p.MANIFEST_PARSED,this.onManifestParsed,this),t.off(p.BUFFER_RESET,this.onBufferReset,this),t.off(p.BUFFER_APPENDING,this.onBufferAppending,this),t.off(p.BUFFER_CODECS,this.onBufferCodecs,this),t.off(p.BUFFER_EOS,this.onBufferEos,this),t.off(p.BUFFER_FLUSHING,this.onBufferFlushing,this),t.off(p.LEVEL_UPDATED,this.onLevelUpdated,this),t.off(p.FRAG_PARSED,this.onFragParsed,this),t.off(p.FRAG_CHANGED,this.onFragChanged,this)}_initSourceBuffer(){this.sourceBuffer={},this.operationQueue=new Or(this.sourceBuffer),this.listeners={audio:[],video:[],audiovideo:[]},this.appendErrors={audio:0,video:0,audiovideo:0},this.lastMpegAudioChunk=null}onManifestLoading(){this.bufferCodecEventsExpected=this._bufferCodecEventsTotal=0,this.details=null}onManifestParsed(t,e){let s=2;(e.audio&&!e.video||!e.altAudio)&&(s=1),this.bufferCodecEventsExpected=this._bufferCodecEventsTotal=s,this.log(`${this.bufferCodecEventsExpected} bufferCodec event(s) expected`)}onMediaAttaching(t,e){const s=this.media=e.media,i=te(this.appendSource);if(s&&i){var r;const t=this.mediaSource=new i;this.log(`created media source: ${null==(r=t.constructor)?void 0:r.name}`),t.addEventListener("sourceopen",this._onMediaSourceOpen),t.addEventListener("sourceended",this._onMediaSourceEnded),t.addEventListener("sourceclose",this._onMediaSourceClose),this.appendSource&&(t.addEventListener("startstreaming",this._onStartStreaming),t.addEventListener("endstreaming",this._onEndStreaming));const e=this._objectUrl=self.URL.createObjectURL(t);if(this.appendSource)try{s.removeAttribute("src");const i=self.ManagedMediaSource;s.disableRemotePlayback=s.disableRemotePlayback||i&&t instanceof i,Ur(s),function(t,e){const s=self.document.createElement("source");s.type="video/mp4",s.src=e,t.appendChild(s)}(s,e),s.load()}catch(t){s.src=e}else s.src=e;s.addEventListener("emptied",this._onMediaEmptied)}}onMediaDetaching(){const{media:t,mediaSource:e,_objectUrl:s}=this;if(e){if(this.log("media source detaching"),"open"===e.readyState)try{e.endOfStream()}catch(t){this.warn(`onMediaDetaching: ${t.message} while calling endOfStream`)}this.onBufferReset(),e.removeEventListener("sourceopen",this._onMediaSourceOpen),e.removeEventListener("sourceended",this._onMediaSourceEnded),e.removeEventListener("sourceclose",this._onMediaSourceClose),this.appendSource&&(e.removeEventListener("startstreaming",this._onStartStreaming),e.removeEventListener("endstreaming",this._onEndStreaming)),t&&(t.removeEventListener("emptied",this._onMediaEmptied),s&&self.URL.revokeObjectURL(s),this.mediaSrc===s?(t.removeAttribute("src"),this.appendSource&&Ur(t),t.load()):this.warn("media|source.src was changed by a third party - skip cleanup")),this.mediaSource=null,this.media=null,this._objectUrl=null,this.bufferCodecEventsExpected=this._bufferCodecEventsTotal,this.pendingTracks={},this.tracks={}}this.hls.trigger(p.MEDIA_DETACHED,void 0)}onBufferReset(){this.getSourceBufferTypes().forEach((t=>{this.resetBuffer(t)})),this._initSourceBuffer()}resetBuffer(t){const e=this.sourceBuffer[t];try{var s;if(e)this.removeBufferListeners(t),this.sourceBuffer[t]=void 0,null!=(s=this.mediaSource)&&s.sourceBuffers.length&&this.mediaSource.removeSourceBuffer(e)}catch(e){this.warn(`onBufferReset ${t}`,e)}}onBufferCodecs(t,e){const s=this.getSourceBufferTypes().length,i=Object.keys(e);if(i.forEach((t=>{if(s){const s=this.tracks[t];if(s&&"function"==typeof s.buffer.changeType){var i;const{id:r,codec:n,levelCodec:a,container:o,metadata:l}=e[t],h=de(s.codec,s.levelCodec),d=null==h?void 0:h.replace(Nr,"$1");let c=de(n,a);const u=null==(i=c)?void 0:i.replace(Nr,"$1");if(c&&d!==u){"audio"===t.slice(0,5)&&(c=he(c,this.appendSource));const e=`${o};codecs=${c}`;this.appendChangeType(t,e),this.log(`switching codec ${h} to ${c}`),this.tracks[t]={buffer:s.buffer,codec:n,container:o,levelCodec:a,metadata:l,id:r}}}}else this.pendingTracks[t]=e[t]})),s)return;const r=Math.max(this.bufferCodecEventsExpected-1,0);this.bufferCodecEventsExpected!==r&&(this.log(`${r} bufferCodec event(s) expected ${i.join(",")}`),this.bufferCodecEventsExpected=r),this.mediaSource&&"open"===this.mediaSource.readyState&&this.checkPendingTracks()}appendChangeType(t,e){const{operationQueue:s}=this,i={execute:()=>{const i=this.sourceBuffer[t];i&&(this.log(`changing ${t} sourceBuffer type to ${e}`),i.changeType(e)),s.shiftAndExecuteNext(t)},onStart:()=>{},onComplete:()=>{},onError:e=>{this.warn(`Failed to change ${t} SourceBuffer type`,e)}};s.append(i,t,!!this.pendingTracks[t])}onBufferAppending(t,e){const{hls:s,operationQueue:i,tracks:r}=this,{data:n,type:a,frag:o,part:l,chunkMeta:h}=e,d=h.buffering[a],c=self.performance.now();d.start=c;const u=o.stats.buffering,f=l?l.stats.buffering:null;0===u.start&&(u.start=c),f&&0===f.start&&(f.start=c);const g=r.audio;let m=!1;"audio"===a&&"audio/mpeg"===(null==g?void 0:g.container)&&(m=!this.lastMpegAudioChunk||1===h.id||this.lastMpegAudioChunk.sn!==h.sn,this.lastMpegAudioChunk=h);const E=o.start,T={execute:()=>{if(d.executeStart=self.performance.now(),m){const t=this.sourceBuffer[a];if(t){const e=E-t.timestampOffset;Math.abs(e)>=.1&&(this.log(`Updating audio SourceBuffer timestampOffset to ${E} (delta: ${e}) sn: ${o.sn})`),t.timestampOffset=E)}}this.appendExecutor(n,a)},onStart:()=>{},onComplete:()=>{const t=self.performance.now();d.executeEnd=d.end=t,0===u.first&&(u.first=t),f&&0===f.first&&(f.first=t);const{sourceBuffer:e}=this,s={};for(const t in e)s[t]=Xs.getBuffered(e[t]);this.appendErrors[a]=0,"audio"===a||"video"===a?this.appendErrors.audiovideo=0:(this.appendErrors.audio=0,this.appendErrors.video=0),this.hls.trigger(p.BUFFER_APPENDED,{type:a,frag:o,part:l,chunkMeta:h,parent:o.type,timeRanges:s})},onError:t=>{const e={type:v.MEDIA_ERROR,parent:o.type,details:y.BUFFER_APPEND_ERROR,sourceBufferName:a,frag:o,part:l,chunkMeta:h,error:t,err:t,fatal:!1};if(t.code===DOMException.QUOTA_EXCEEDED_ERR)e.details=y.BUFFER_FULL_ERROR;else{const t=++this.appendErrors[a];e.details=y.BUFFER_APPEND_ERROR,this.warn(`Failed ${t}/${s.config.appendErrorMaxRetry} times to append segment in "${a}" sourceBuffer`),t>=s.config.appendErrorMaxRetry&&(e.fatal=!0)}s.trigger(p.ERROR,e)}};i.append(T,a,!!this.pendingTracks[a])}onBufferFlushing(t,e){const{operationQueue:s}=this,i=t=>({execute:this.removeExecutor.bind(this,t,e.startOffset,e.endOffset),onStart:()=>{},onComplete:()=>{this.hls.trigger(p.BUFFER_FLUSHED,{type:t})},onError:e=>{this.warn(`Failed to remove from ${t} SourceBuffer`,e)}});e.type?s.append(i(e.type),e.type):this.getSourceBufferTypes().forEach((t=>{s.append(i(t),t)}))}onFragParsed(t,e){const{frag:s,part:i}=e,r=[],n=i?i.elementaryStreams:s.elementaryStreams;n[x]?r.push("audiovideo"):(n[C]&&r.push("audio"),n[_]&&r.push("video"));0===r.length&&this.warn(`Fragments must have at least one ElementaryStreamType set. type: ${s.type} level: ${s.level} sn: ${s.sn}`),this.blockBuffers((()=>{const t=self.performance.now();s.stats.buffering.end=t,i&&(i.stats.buffering.end=t);const e=i?i.stats:s.stats;this.hls.trigger(p.FRAG_BUFFERED,{frag:s,part:i,stats:e,id:s.type})}),r)}onFragChanged(t,e){this.trimBuffers()}onBufferEos(t,e){this.getSourceBufferTypes().reduce(((t,s)=>{const i=this.sourceBuffer[s];return!i||e.type&&e.type!==s||(i.ending=!0,i.ended||(i.ended=!0,this.log(`${s} sourceBuffer now EOS`))),t&&!(i&&!i.ended)}),!0)&&(this.log("Queueing mediaSource.endOfStream()"),this.blockBuffers((()=>{this.getSourceBufferTypes().forEach((t=>{const e=this.sourceBuffer[t];e&&(e.ending=!1)}));const{mediaSource:t}=this;t&&"open"===t.readyState?(this.log("Calling mediaSource.endOfStream()"),t.endOfStream()):t&&this.log(`Could not call mediaSource.endOfStream(). mediaSource.readyState: ${t.readyState}`)})))}onLevelUpdated(t,{details:e}){e.fragments.length&&(this.details=e,this.getSourceBufferTypes().length?this.blockBuffers(this.updateMediaElementDuration.bind(this)):this.updateMediaElementDuration())}trimBuffers(){const{hls:t,details:e,media:s}=this;if(!s||null===e)return;if(!this.getSourceBufferTypes().length)return;const i=t.config,r=s.currentTime,n=e.levelTargetDuration,a=e.live&&null!==i.liveBackBufferLength?i.liveBackBufferLength:i.backBufferLength;if(f(a)&&a>0){const t=Math.max(a,n),e=Math.floor(r/n)*n-t;this.flushBackBuffer(r,n,e)}if(f(i.frontBufferFlushThreshold)&&i.frontBufferFlushThreshold>0){const t=Math.max(i.maxBufferLength,i.frontBufferFlushThreshold),e=Math.max(t,n),s=Math.floor(r/n)*n+e;this.flushFrontBuffer(r,n,s)}}flushBackBuffer(t,e,s){const{details:i,sourceBuffer:r}=this;this.getSourceBufferTypes().forEach((n=>{const a=r[n];if(a){const r=Xs.getBuffered(a);if(r.length>0&&s>r.start(0)){if(this.hls.trigger(p.BACK_BUFFER_REACHED,{bufferEnd:s}),null!=i&&i.live)this.hls.trigger(p.LIVE_BACK_BUFFER_REACHED,{bufferEnd:s});else if(a.ended&&r.end(r.length-1)-t<2*e)return void this.log(`Cannot flush ${n} back buffer while SourceBuffer is in ended state`);this.hls.trigger(p.BUFFER_FLUSHING,{startOffset:0,endOffset:s,type:n})}}}))}flushFrontBuffer(t,e,s){const{sourceBuffer:i}=this;this.getSourceBufferTypes().forEach((r=>{const n=i[r];if(n){const i=Xs.getBuffered(n),a=i.length;if(a<2)return;const o=i.start(a-1),l=i.end(a-1);if(s>o||t>=o&&t<=l)return;if(n.ended&&t-l<2*e)return void this.log(`Cannot flush ${r} front buffer while SourceBuffer is in ended state`);this.hls.trigger(p.BUFFER_FLUSHING,{startOffset:o,endOffset:1/0,type:r})}}))}updateMediaElementDuration(){if(!this.details||!this.media||!this.mediaSource||"open"!==this.mediaSource.readyState)return;const{details:t,hls:e,media:s,mediaSource:i}=this,r=t.fragments[0].start+t.totalduration,n=s.duration,a=f(i.duration)?i.duration:0;t.live&&e.config.liveDurationInfinity?(i.duration=1/0,this.updateSeekableRange(t)):(r>a&&r>n||!f(n))&&(this.log(`Updating Media Source duration to ${r.toFixed(3)}`),i.duration=r)}updateSeekableRange(t){const e=this.mediaSource,s=t.fragments;if(s.length&&t.live&&null!=e&&e.setLiveSeekableRange){const i=Math.max(0,s[0].start),r=Math.max(i,i+t.totalduration);this.log(`Media Source duration is set to ${e.duration}. Setting seekable range to ${i}-${r}.`),e.setLiveSeekableRange(i,r)}}checkPendingTracks(){const{bufferCodecEventsExpected:t,operationQueue:e,pendingTracks:s}=this,i=Object.keys(s).length;if(i&&(!t||2===i||"audiovideo"in s)){this.createSourceBuffers(s),this.pendingTracks={};const t=this.getSourceBufferTypes();if(t.length)this.hls.trigger(p.BUFFER_CREATED,{tracks:this.tracks}),t.forEach((t=>{e.executeNext(t)}));else{const t=new Error("could not create source buffer for media codec(s)");this.hls.trigger(p.ERROR,{type:v.MEDIA_ERROR,details:y.BUFFER_INCOMPATIBLE_CODECS_ERROR,fatal:!0,error:t,reason:t.message})}}}createSourceBuffers(t){const{sourceBuffer:e,mediaSource:s}=this;if(!s)throw Error("createSourceBuffers called when mediaSource was null");for(const r in t)if(!e[r]){var i;const n=t[r];if(!n)throw Error(`source buffer exists for track ${r}, however track does not`);let a=-1===(null==(i=n.levelCodec)?void 0:i.indexOf(","))?n.levelCodec:n.codec;a&&"audio"===r.slice(0,5)&&(a=he(a,this.appendSource));const o=`${n.container};codecs=${a}`;this.log(`creating sourceBuffer(${o})`);try{const t=e[r]=s.addSourceBuffer(o),i=r;this.addBufferListener(i,"updatestart",this._onSBUpdateStart),this.addBufferListener(i,"updateend",this._onSBUpdateEnd),this.addBufferListener(i,"error",this._onSBUpdateError),this.appendSource&&this.addBufferListener(i,"bufferedchange",((t,e)=>{const s=e.removedRanges;null!=s&&s.length&&this.hls.trigger(p.BUFFER_FLUSHED,{type:r})})),this.tracks[r]={buffer:t,codec:a,container:n.container,levelCodec:n.levelCodec,metadata:n.metadata,id:n.id}}catch(t){this.error(`error while trying to add sourceBuffer: ${t.message}`),this.hls.trigger(p.ERROR,{type:v.MEDIA_ERROR,details:y.BUFFER_ADD_CODEC_ERROR,fatal:!1,error:t,sourceBufferName:r,mimeType:o})}}}get mediaSrc(){var t,e;const s=(null==(t=this.media)||null==(e=t.querySelector)?void 0:e.call(t,"source"))||this.media;return null==s?void 0:s.src}_onSBUpdateStart(t){const{operationQueue:e}=this;e.current(t).onStart()}_onSBUpdateEnd(t){var e;if("closed"===(null==(e=this.mediaSource)?void 0:e.readyState))return void this.resetBuffer(t);const{operationQueue:s}=this;s.current(t).onComplete(),s.shiftAndExecuteNext(t)}_onSBUpdateError(t,e){var s;const i=new Error(`${t} SourceBuffer error. MediaSource readyState: ${null==(s=this.mediaSource)?void 0:s.readyState}`);this.error(`${i}`,e),this.hls.trigger(p.ERROR,{type:v.MEDIA_ERROR,details:y.BUFFER_APPENDING_ERROR,sourceBufferName:t,error:i,fatal:!1});const r=this.operationQueue.current(t);r&&r.onError(i)}removeExecutor(t,e,s){const{media:i,mediaSource:r,operationQueue:n,sourceBuffer:a}=this,o=a[t];if(!i||!r||!o)return this.warn(`Attempting to remove from the ${t} SourceBuffer, but it does not exist`),void n.shiftAndExecuteNext(t);const l=f(i.duration)?i.duration:1/0,h=f(r.duration)?r.duration:1/0,d=Math.max(0,e),c=Math.min(s,l,h);c>d&&(!o.ending||o.ended)?(o.ended=!1,this.log(`Removing [${d},${c}] from the ${t} SourceBuffer`),o.remove(d,c)):n.shiftAndExecuteNext(t)}appendExecutor(t,e){const s=this.sourceBuffer[e];if(s)s.ended=!1,s.appendBuffer(t);else if(!this.pendingTracks[e])throw new Error(`Attempting to append to the ${e} SourceBuffer, but it does not exist`)}blockBuffers(t,e=this.getSourceBufferTypes()){if(!e.length)return this.log("Blocking operation requested, but no SourceBuffers exist"),void Promise.resolve().then(t);const{operationQueue:s}=this,i=e.map((t=>s.appendBlocker(t)));Promise.all(i).then((()=>{t(),e.forEach((t=>{const e=this.sourceBuffer[t];null!=e&&e.updating||s.shiftAndExecuteNext(t)}))}))}getSourceBufferTypes(){return Object.keys(this.sourceBuffer)}addBufferListener(t,e,s){const i=this.sourceBuffer[t];if(!i)return;const r=s.bind(this,t);this.listeners[t].push({event:e,listener:r}),i.addEventListener(e,r)}removeBufferListeners(t){const e=this.sourceBuffer[t];e&&this.listeners[t].forEach((t=>{e.removeEventListener(t.event,t.listener)}))}},capLevelController:Mn,errorController:class{constructor(t){this.hls=void 0,this.playlistError=0,this.penalizedRenditions={},this.log=void 0,this.warn=void 0,this.error=void 0,this.hls=t,this.log=A.log.bind(A,"[info]:"),this.warn=A.warn.bind(A,"[warning]:"),this.error=A.error.bind(A,"[error]:"),this.registerListeners()}registerListeners(){const t=this.hls;t.on(p.ERROR,this.onError,this),t.on(p.MANIFEST_LOADING,this.onManifestLoading,this),t.on(p.LEVEL_UPDATED,this.onLevelUpdated,this)}unregisterListeners(){const t=this.hls;t&&(t.off(p.ERROR,this.onError,this),t.off(p.ERROR,this.onErrorOut,this),t.off(p.MANIFEST_LOADING,this.onManifestLoading,this),t.off(p.LEVEL_UPDATED,this.onLevelUpdated,this))}destroy(){this.unregisterListeners(),this.hls=null,this.penalizedRenditions={}}startLoad(t){}stopLoad(){this.playlistError=0}getVariantLevelIndex(t){return(null==t?void 0:t.type)===De?t.level:this.hls.loadLevel}onManifestLoading(){this.playlistError=0,this.penalizedRenditions={}}onLevelUpdated(){this.playlistError=0}onError(t,e){var s,i;if(e.fatal)return;const r=this.hls,n=e.context;switch(e.details){case y.FRAG_LOAD_ERROR:case y.FRAG_LOAD_TIMEOUT:case y.KEY_LOAD_ERROR:case y.KEY_LOAD_TIMEOUT:return void(e.errorAction=this.getFragRetryOrSwitchAction(e));case y.FRAG_PARSING_ERROR:if(null!=(s=e.frag)&&s.gap)return void(e.errorAction={action:Ts,flags:Rs});case y.FRAG_GAP:case y.FRAG_DECRYPT_ERROR:return e.errorAction=this.getFragRetryOrSwitchAction(e),void(e.errorAction.action=Ss);case y.LEVEL_EMPTY_ERROR:case y.LEVEL_PARSING_ERROR:{var a,o;const t=e.parent===De?e.level:r.loadLevel;e.details===y.LEVEL_EMPTY_ERROR&&null!=(a=e.context)&&null!=(o=a.levelDetails)&&o.live?e.errorAction=this.getPlaylistRetryOrSwitchAction(e,t):(e.levelRetry=!1,e.errorAction=this.getLevelSwitchAction(e,t))}return;case y.LEVEL_LOAD_ERROR:case y.LEVEL_LOAD_TIMEOUT:return void("number"==typeof(null==n?void 0:n.level)&&(e.errorAction=this.getPlaylistRetryOrSwitchAction(e,n.level)));case y.AUDIO_TRACK_LOAD_ERROR:case y.AUDIO_TRACK_LOAD_TIMEOUT:case y.SUBTITLE_LOAD_ERROR:case y.SUBTITLE_TRACK_LOAD_TIMEOUT:if(n){const t=r.levels[r.loadLevel];if(t&&(n.type===ke&&t.hasAudioGroup(n.groupId)||n.type===we&&t.hasSubtitleGroup(n.groupId)))return e.errorAction=this.getPlaylistRetryOrSwitchAction(e,r.loadLevel),e.errorAction.action=Ss,void(e.errorAction.flags=bs)}return;case y.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED:{const t=r.levels[r.loadLevel],s=null==t?void 0:t.attrs["HDCP-LEVEL"];s?e.errorAction={action:Ss,flags:ks,hdcpLevel:s}:this.keySystemError(e)}return;case y.BUFFER_ADD_CODEC_ERROR:case y.REMUX_ALLOC_ERROR:case y.BUFFER_APPEND_ERROR:return void(e.errorAction=this.getLevelSwitchAction(e,null!=(i=e.level)?i:r.loadLevel));case y.INTERNAL_EXCEPTION:case y.BUFFER_APPENDING_ERROR:case y.BUFFER_FULL_ERROR:case y.LEVEL_SWITCH_ERROR:case y.BUFFER_STALLED_ERROR:case y.BUFFER_SEEK_OVER_HOLE:case y.BUFFER_NUDGE_ON_STALL:return void(e.errorAction={action:Ts,flags:Rs})}e.type===v.KEY_SYSTEM_ERROR&&this.keySystemError(e)}keySystemError(t){const e=this.getVariantLevelIndex(t.frag);t.levelRetry=!1,t.errorAction=this.getLevelSwitchAction(t,e)}getPlaylistRetryOrSwitchAction(t,e){const s=us(this.hls.config.playlistLoadPolicy,t),i=this.playlistError++;if(ms(s,i,cs(t),t.response))return{action:As,flags:Rs,retryConfig:s,retryCount:i};const r=this.getLevelSwitchAction(t,e);return s&&(r.retryConfig=s,r.retryCount=i),r}getFragRetryOrSwitchAction(t){const e=this.hls,s=this.getVariantLevelIndex(t.frag),i=e.levels[s],{fragLoadPolicy:r,keyLoadPolicy:n}=e.config,a=us(t.details.startsWith("key")?n:r,t),o=e.levels.reduce(((t,e)=>t+e.fragmentError),0);if(i){t.details!==y.FRAG_GAP&&i.fragmentError++;if(ms(a,o,cs(t),t.response))return{action:As,flags:Rs,retryConfig:a,retryCount:o}}const l=this.getLevelSwitchAction(t,s);return a&&(l.retryConfig=a,l.retryCount=o),l}getLevelSwitchAction(t,e){const s=this.hls;null==e&&(e=s.loadLevel);const i=this.hls.levels[e];if(i){var r,n;const e=t.details;i.loadError++,e===y.BUFFER_APPEND_ERROR&&i.fragmentError++;let l=-1;const{levels:h,loadLevel:d,minAutoLevel:c,maxAutoLevel:u}=s;s.autoLevelEnabled||(s.loadLevel=-1);const f=null==(r=t.frag)?void 0:r.type,g=(f===Ie&&e===y.FRAG_PARSING_ERROR||"audio"===t.sourceBufferName&&(e===y.BUFFER_ADD_CODEC_ERROR||e===y.BUFFER_APPEND_ERROR))&&h.some((({audioCodec:t})=>i.audioCodec!==t)),m="video"===t.sourceBufferName&&(e===y.BUFFER_ADD_CODEC_ERROR||e===y.BUFFER_APPEND_ERROR)&&h.some((({codecSet:t,audioCodec:e})=>i.codecSet!==t&&i.audioCodec===e)),{type:p,groupId:v}=null!=(n=t.context)?n:{};for(let s=h.length;s--;){const r=(s+d)%h.length;if(r!==d&&r>=c&&r<=u&&0===h[r].loadError){var a,o;const s=h[r];if(e===y.FRAG_GAP&&f===De&&t.frag){const e=h[r].details;if(e){const s=vs(t.frag,e.fragments,t.frag.start);if(null!=s&&s.gap)continue}}else{if(p===ke&&s.hasAudioGroup(v)||p===we&&s.hasSubtitleGroup(v))continue;if(f===Ie&&null!=(a=i.audioGroups)&&a.some((t=>s.hasAudioGroup(t)))||f===Ce&&null!=(o=i.subtitleGroups)&&o.some((t=>s.hasSubtitleGroup(t)))||g&&i.audioCodec===s.audioCodec||!g&&i.audioCodec!==s.audioCodec||m&&i.codecSet===s.codecSet)continue}l=r;break}}if(l>-1&&s.loadLevel!==l)return t.levelRetry=!0,this.playlistError=0,{action:Ss,flags:Rs,nextAutoLevel:l}}return{action:Ss,flags:bs}}onErrorOut(t,e){var s;switch(null==(s=e.errorAction)?void 0:s.action){case Ts:break;case Ss:this.sendAlternateToPenaltyBox(e),e.errorAction.resolved||e.details===y.FRAG_GAP?/MediaSource readyState: ended/.test(e.error.message)&&(this.warn(`MediaSource ended after "${e.sourceBufferName}" sourceBuffer append error. Attempting to recover from media error.`),this.hls.recoverMediaError()):e.fatal=!0}e.fatal&&this.hls.stopLoad()}sendAlternateToPenaltyBox(t){const e=this.hls,s=t.errorAction;if(!s)return;const{flags:i,hdcpLevel:r,nextAutoLevel:n}=s;switch(i){case Rs:this.switchLevel(t,n);break;case ks:r&&(e.maxHdcpLevel=qe[qe.indexOf(r)-1],s.resolved=!0),this.warn(`Restricting playback to HDCP-LEVEL of "${e.maxHdcpLevel}" or lower`)}s.resolved||this.switchLevel(t,n)}switchLevel(t,e){void 0!==e&&t.errorAction&&(this.warn(`switching to level ${e} after ${t.details}`),this.hls.nextAutoLevel=e,t.errorAction.resolved=!0,this.hls.nextLoadLevel=this.hls.nextAutoLevel)}},fpsController:class{constructor(t){this.hls=void 0,this.isVideoPlaybackQualityAvailable=!1,this.timer=void 0,this.media=null,this.lastTime=void 0,this.lastDroppedFrames=0,this.lastDecodedFrames=0,this.streamController=void 0,this.hls=t,this.registerListeners()}setStreamController(t){this.streamController=t}registerListeners(){this.hls.on(p.MEDIA_ATTACHING,this.onMediaAttaching,this)}unregisterListeners(){this.hls.off(p.MEDIA_ATTACHING,this.onMediaAttaching,this)}destroy(){this.timer&&clearInterval(this.timer),this.unregisterListeners(),this.isVideoPlaybackQualityAvailable=!1,this.media=null}onMediaAttaching(t,e){const s=this.hls.config;if(s.capLevelOnFPSDrop){const t=e.media instanceof self.HTMLVideoElement?e.media:null;this.media=t,t&&"function"==typeof t.getVideoPlaybackQuality&&(this.isVideoPlaybackQualityAvailable=!0),self.clearInterval(this.timer),this.timer=self.setInterval(this.checkFPSInterval.bind(this),s.fpsDroppedMonitoringPeriod)}}checkFPS(t,e,s){const i=performance.now();if(e){if(this.lastTime){const t=i-this.lastTime,r=s-this.lastDroppedFrames,n=e-this.lastDecodedFrames,a=1e3*r/t,o=this.hls;if(o.trigger(p.FPS_DROP,{currentDropped:r,currentDecoded:n,totalDroppedFrames:s}),a>0&&r>o.config.fpsDroppedMonitoringThreshold*n){let t=o.currentLevel;A.warn("drop FPS ratio greater than max allowed value for currentLevel: "+t),t>0&&(-1===o.autoLevelCapping||o.autoLevelCapping>=t)&&(t-=1,o.trigger(p.FPS_DROP_LEVEL_CAPPING,{level:t,droppedLevel:o.currentLevel}),o.autoLevelCapping=t,this.streamController.nextLevelSwitch())}}this.lastTime=i,this.lastDroppedFrames=s,this.lastDecodedFrames=e}}checkFPSInterval(){const t=this.media;if(t)if(this.isVideoPlaybackQualityAvailable){const e=t.getVideoPlaybackQuality();this.checkFPS(t,e.totalVideoFrames,e.droppedVideoFrames)}else this.checkFPS(t,t.webkitDecodedFrameCount,t.webkitDroppedFrameCount)}},stretchShortVideoTrack:!1,maxAudioFramesDrift:1,forceKeyFrameOnDiscontinuity:!0,abrEwmaFastLive:3,abrEwmaSlowLive:9,abrEwmaFastVoD:3,abrEwmaSlowVoD:9,abrEwmaDefaultEstimate:5e5,abrEwmaDefaultEstimateMax:5e6,abrBandWidthFactor:.95,abrBandWidthUpFactor:.7,abrMaxWithRealBitrate:!1,maxStarvationDelay:4,maxLoadingDelay:4,minAutoBitrate:0,emeEnabled:!1,widevineLicenseUrl:void 0,drmSystems:{},drmSystemOptions:{},requestMediaKeySystemAccessFunc:tt,testBandwidth:!0,progressive:!1,lowLatencyMode:!0,cmcd:void 0,enableDateRangeMetadataCues:!0,enableEmsgMetadataCues:!0,enableID3MetadataCues:!0,useMediaCapabilities:!0,certLoadPolicy:{default:{maxTimeToFirstByteMs:8e3,maxLoadTimeMs:2e4,timeoutRetry:null,errorRetry:null}},keyLoadPolicy:{default:{maxTimeToFirstByteMs:8e3,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:1,retryDelayMs:1e3,maxRetryDelayMs:2e4,backoff:"linear"},errorRetry:{maxNumRetry:8,retryDelayMs:1e3,maxRetryDelayMs:2e4,backoff:"linear"}}},manifestLoadPolicy:{default:{maxTimeToFirstByteMs:1/0,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:2,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:1,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},playlistLoadPolicy:{default:{maxTimeToFirstByteMs:1e4,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:2,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:2,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},fragLoadPolicy:{default:{maxTimeToFirstByteMs:1e4,maxLoadTimeMs:12e4,timeoutRetry:{maxNumRetry:4,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:6,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},steeringManifestLoadPolicy:{default:{maxTimeToFirstByteMs:1e4,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:2,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:1,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},manifestLoadingTimeOut:1e4,manifestLoadingMaxRetry:1,manifestLoadingRetryDelay:1e3,manifestLoadingMaxRetryTimeout:64e3,levelLoadingTimeOut:1e4,levelLoadingMaxRetry:4,levelLoadingRetryDelay:1e3,levelLoadingMaxRetryTimeout:64e3,fragLoadingTimeOut:2e4,fragLoadingMaxRetry:6,fragLoadingRetryDelay:1e3,fragLoadingMaxRetryTimeout:64e3},{cueHandler:Sa,enableWebVTT:!0,enableIMSC1:!0,enableCEA708Captions:!0,captionsTextTrack1Label:"English",captionsTextTrack1LanguageCode:"en",captionsTextTrack2Label:"Spanish",captionsTextTrack2LanguageCode:"es",captionsTextTrack3Label:"Unknown CC",captionsTextTrack3LanguageCode:"",captionsTextTrack4Label:"Unknown CC",captionsTextTrack4LanguageCode:"",renderTextTracksNatively:!0}),{},{subtitleStreamController:class extends Ri{constructor(t,e,s){super(t,e,s,"[subtitle-stream-controller]",Ce),this.currentTrackId=-1,this.tracksBuffered=[],this.mainDetails=null,this._registerListeners()}onHandlerDestroying(){this._unregisterListeners(),super.onHandlerDestroying(),this.mainDetails=null}_registerListeners(){const{hls:t}=this;t.on(p.MEDIA_ATTACHED,this.onMediaAttached,this),t.on(p.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(p.MANIFEST_LOADING,this.onManifestLoading,this),t.on(p.LEVEL_LOADED,this.onLevelLoaded,this),t.on(p.ERROR,this.onError,this),t.on(p.SUBTITLE_TRACKS_UPDATED,this.onSubtitleTracksUpdated,this),t.on(p.SUBTITLE_TRACK_SWITCH,this.onSubtitleTrackSwitch,this),t.on(p.SUBTITLE_TRACK_LOADED,this.onSubtitleTrackLoaded,this),t.on(p.SUBTITLE_FRAG_PROCESSED,this.onSubtitleFragProcessed,this),t.on(p.BUFFER_FLUSHING,this.onBufferFlushing,this),t.on(p.FRAG_BUFFERED,this.onFragBuffered,this)}_unregisterListeners(){const{hls:t}=this;t.off(p.MEDIA_ATTACHED,this.onMediaAttached,this),t.off(p.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(p.MANIFEST_LOADING,this.onManifestLoading,this),t.off(p.LEVEL_LOADED,this.onLevelLoaded,this),t.off(p.ERROR,this.onError,this),t.off(p.SUBTITLE_TRACKS_UPDATED,this.onSubtitleTracksUpdated,this),t.off(p.SUBTITLE_TRACK_SWITCH,this.onSubtitleTrackSwitch,this),t.off(p.SUBTITLE_TRACK_LOADED,this.onSubtitleTrackLoaded,this),t.off(p.SUBTITLE_FRAG_PROCESSED,this.onSubtitleFragProcessed,this),t.off(p.BUFFER_FLUSHING,this.onBufferFlushing,this),t.off(p.FRAG_BUFFERED,this.onFragBuffered,this)}startLoad(t){this.stopLoad(),this.state=fi,this.setInterval(500),this.nextLoadPosition=this.startPosition=this.lastCurrentTime=t,this.tick()}onManifestLoading(){this.mainDetails=null,this.fragmentTracker.removeAllFragments()}onMediaDetaching(){this.tracksBuffered=[],super.onMediaDetaching()}onLevelLoaded(t,e){this.mainDetails=e.details}onSubtitleFragProcessed(t,e){const{frag:s,success:i}=e;if(this.fragPrevious=s,this.state=fi,!i)return;const r=this.tracksBuffered[this.currentTrackId];if(!r)return;let n;const a=s.start;for(let t=0;t<r.length;t++)if(a>=r[t].start&&a<=r[t].end){n=r[t];break}const o=s.start+s.duration;n?n.end=o:(n={start:a,end:o},r.push(n)),this.fragmentTracker.fragBuffered(s),this.fragBufferedComplete(s,null)}onBufferFlushing(t,e){const{startOffset:s,endOffset:i}=e;if(0===s&&i!==Number.POSITIVE_INFINITY){const t=i-1;if(t<=0)return;e.endOffsetSubtitles=Math.max(0,t),this.tracksBuffered.forEach((e=>{for(let s=0;s<e.length;)if(e[s].end<=t)e.shift();else{if(!(e[s].start<t))break;e[s].start=t,s++}})),this.fragmentTracker.removeFragmentsInRange(s,t,Ce)}}onFragBuffered(t,e){var s;this.loadedmetadata||e.frag.type!==De||null!=(s=this.media)&&s.buffered.length&&(this.loadedmetadata=!0)}onError(t,e){const s=e.frag;(null==s?void 0:s.type)===Ce&&(e.details===y.FRAG_GAP&&this.fragmentTracker.fragBuffered(s,!0),this.fragCurrent&&this.fragCurrent.abortRequests(),this.state!==ui&&(this.state=fi))}onSubtitleTracksUpdated(t,{subtitleTracks:e}){this.levels&&xr(this.levels,e)?this.levels=e.map((t=>new es(t))):(this.tracksBuffered=[],this.levels=e.map((t=>{const e=new es(t);return this.tracksBuffered[e.id]=[],e})),this.fragmentTracker.removeFragmentsInRange(0,Number.POSITIVE_INFINITY,Ce),this.fragPrevious=null,this.mediaBuffer=null)}onSubtitleTrackSwitch(t,e){var s;if(this.currentTrackId=e.id,null==(s=this.levels)||!s.length||-1===this.currentTrackId)return void this.clearInterval();const i=this.levels[this.currentTrackId];null!=i&&i.details?this.mediaBuffer=this.mediaBufferTimeRanges:this.mediaBuffer=null,i&&this.setInterval(500)}onSubtitleTrackLoaded(t,e){var s;const{currentTrackId:i,levels:r}=this,{details:n,id:a}=e;if(!r)return void this.warn(`Subtitle tracks were reset while loading level ${a}`);const o=r[a];if(a>=r.length||!o)return;this.log(`Subtitle track ${a} loaded [${n.startSN},${n.endSN}]${n.lastPartSn?`[part-${n.lastPartSn}-${n.lastPartIndex}]`:""},duration:${n.totalduration}`),this.mediaBuffer=this.mediaBufferTimeRanges;let l=0;if(n.live||null!=(s=o.details)&&s.live){const t=this.mainDetails;if(n.deltaUpdateFailed||!t)return;const e=t.fragments[0];var h;if(o.details)l=this.alignPlaylists(n,o.details,null==(h=this.levelLastLoaded)?void 0:h.details),0===l&&e&&(l=e.start,os(n,l));else n.hasProgramDateTime&&t.hasProgramDateTime?(ei(n,t),l=n.fragments[0].start):e&&(l=e.start,os(n,l))}if(o.details=n,this.levelLastLoaded=o,a===i&&(this.startFragRequested||!this.mainDetails&&n.live||this.setStartPosition(this.mainDetails||n,l),this.tick(),n.live&&!this.fragCurrent&&this.media&&this.state===fi)){vs(null,n.fragments,this.media.currentTime,0)||(this.warn("Subtitle playlist not aligned with playback"),o.details=void 0)}}_handleFragmentLoadComplete(t){const{frag:e,payload:s}=t,i=e.decryptdata,r=this.hls;if(!this.fragContextChanged(e)&&s&&s.byteLength>0&&null!=i&&i.key&&i.iv&&"AES-128"===i.method){const t=performance.now();this.decrypter.decrypt(new Uint8Array(s),i.key.buffer,i.iv.buffer).catch((t=>{throw r.trigger(p.ERROR,{type:v.MEDIA_ERROR,details:y.FRAG_DECRYPT_ERROR,fatal:!1,error:t,reason:t.message,frag:e}),t})).then((s=>{const i=performance.now();r.trigger(p.FRAG_DECRYPTED,{frag:e,payload:s,stats:{tstart:t,tdecrypt:i}})})).catch((t=>{this.warn(`${t.name}: ${t.message}`),this.state=fi}))}}doTick(){if(this.media){if(this.state===fi){const{currentTrackId:t,levels:e}=this,s=null==e?void 0:e[t];if(!s||!e.length||!s.details)return;const{config:i}=this,r=this.getLoadPosition(),n=Xs.bufferedInfo(this.tracksBuffered[this.currentTrackId]||[],r,i.maxBufferHole),{end:a,len:o}=n,l=this.getFwdBufferInfo(this.media,De),h=s.details;if(o>this.getMaxBufferLength(null==l?void 0:l.len)+h.levelTargetDuration)return;const d=h.fragments,c=d.length,u=h.edge;let f=null;const g=this.fragPrevious;if(a<u){const t=i.maxFragLookUpTolerance,e=a>u-t?0:t;f=vs(g,d,Math.max(d[0].start,a),e),!f&&g&&g.start<d[0].start&&(f=d[0])}else f=d[c-1];if(!f)return;if(f=this.mapToInitFragWhenRequired(f),"initSegment"!==f.sn){const t=d[f.sn-h.startSN-1];t&&t.cc===f.cc&&this.fragmentTracker.getState(t)===Gs&&(f=t)}this.fragmentTracker.getState(f)===Gs&&this.loadFragment(f,s,a)}}else this.state=fi}getMaxBufferLength(t){const e=super.getMaxBufferLength();return t?Math.max(e,t):e}loadFragment(t,e,s){this.fragCurrent=t,"initSegment"===t.sn?this._loadInitSegment(t,e):(this.startFragRequested=!0,super.loadFragment(t,e,s))}get mediaBufferTimeRanges(){return new Fr(this.tracksBuffered[this.currentTrackId]||[])}},subtitleTrackController:class extends ws{constructor(t){super(t,"[subtitle-track-controller]"),this.media=null,this.tracks=[],this.groupIds=null,this.tracksInGroup=[],this.trackId=-1,this.currentTrack=null,this.selectDefaultTrack=!0,this.queuedDefaultTrack=-1,this.asyncPollTrackChange=()=>this.pollTrackChange(0),this.useTextTrackPolling=!1,this.subtitlePollingInterval=-1,this._subtitleDisplay=!0,this.onTextTracksChanged=()=>{if(this.useTextTrackPolling||self.clearInterval(this.subtitlePollingInterval),!this.media||!this.hls.config.renderTextTracksNatively)return;let t=null;const e=Ue(this.media.textTracks);for(let s=0;s<e.length;s++)if("hidden"===e[s].mode)t=e[s];else if("showing"===e[s].mode){t=e[s];break}const s=this.findTrackForTextTrack(t);this.subtitleTrack!==s&&this.setSubtitleTrack(s)},this.registerListeners()}destroy(){this.unregisterListeners(),this.tracks.length=0,this.tracksInGroup.length=0,this.currentTrack=null,this.onTextTracksChanged=this.asyncPollTrackChange=null,super.destroy()}get subtitleDisplay(){return this._subtitleDisplay}set subtitleDisplay(t){this._subtitleDisplay=t,this.trackId>-1&&this.toggleTrackModes()}registerListeners(){const{hls:t}=this;t.on(p.MEDIA_ATTACHED,this.onMediaAttached,this),t.on(p.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(p.MANIFEST_LOADING,this.onManifestLoading,this),t.on(p.MANIFEST_PARSED,this.onManifestParsed,this),t.on(p.LEVEL_LOADING,this.onLevelLoading,this),t.on(p.LEVEL_SWITCHING,this.onLevelSwitching,this),t.on(p.SUBTITLE_TRACK_LOADED,this.onSubtitleTrackLoaded,this),t.on(p.ERROR,this.onError,this)}unregisterListeners(){const{hls:t}=this;t.off(p.MEDIA_ATTACHED,this.onMediaAttached,this),t.off(p.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(p.MANIFEST_LOADING,this.onManifestLoading,this),t.off(p.MANIFEST_PARSED,this.onManifestParsed,this),t.off(p.LEVEL_LOADING,this.onLevelLoading,this),t.off(p.LEVEL_SWITCHING,this.onLevelSwitching,this),t.off(p.SUBTITLE_TRACK_LOADED,this.onSubtitleTrackLoaded,this),t.off(p.ERROR,this.onError,this)}onMediaAttached(t,e){this.media=e.media,this.media&&(this.queuedDefaultTrack>-1&&(this.subtitleTrack=this.queuedDefaultTrack,this.queuedDefaultTrack=-1),this.useTextTrackPolling=!(this.media.textTracks&&"onchange"in this.media.textTracks),this.useTextTrackPolling?this.pollTrackChange(500):this.media.textTracks.addEventListener("change",this.asyncPollTrackChange))}pollTrackChange(t){self.clearInterval(this.subtitlePollingInterval),this.subtitlePollingInterval=self.setInterval(this.onTextTracksChanged,t)}onMediaDetaching(){if(!this.media)return;self.clearInterval(this.subtitlePollingInterval),this.useTextTrackPolling||this.media.textTracks.removeEventListener("change",this.asyncPollTrackChange),this.trackId>-1&&(this.queuedDefaultTrack=this.trackId);Ue(this.media.textTracks).forEach((t=>{Oe(t)})),this.subtitleTrack=-1,this.media=null}onManifestLoading(){this.tracks=[],this.groupIds=null,this.tracksInGroup=[],this.trackId=-1,this.currentTrack=null,this.selectDefaultTrack=!0}onManifestParsed(t,e){this.tracks=e.subtitleTracks}onSubtitleTrackLoaded(t,e){const{id:s,groupId:i,details:r}=e,n=this.tracksInGroup[s];if(!n||n.groupId!==i)return void this.warn(`Subtitle track with id:${s} and group:${i} not found in active group ${null==n?void 0:n.groupId}`);const a=n.details;n.details=e.details,this.log(`Subtitle track ${s} "${n.name}" lang:${n.lang} group:${i} loaded [${r.startSN}-${r.endSN}]`),s===this.trackId&&this.playlistLoaded(s,e,a)}onLevelLoading(t,e){this.switchLevel(e.level)}onLevelSwitching(t,e){this.switchLevel(e.level)}switchLevel(t){const e=this.hls.levels[t];if(!e)return;const s=e.subtitleGroups||null,i=this.groupIds;let r=this.currentTrack;if(!s||(null==i?void 0:i.length)!==(null==s?void 0:s.length)||null!=s&&s.some((t=>-1===(null==i?void 0:i.indexOf(t))))){this.groupIds=s,this.trackId=-1,this.currentTrack=null;const t=this.tracks.filter((t=>!s||-1!==s.indexOf(t.groupId)));if(t.length)this.selectDefaultTrack&&!t.some((t=>t.default))&&(this.selectDefaultTrack=!1),t.forEach(((t,e)=>{t.id=e}));else if(!r&&!this.tracksInGroup.length)return;this.tracksInGroup=t;const e=this.hls.config.subtitlePreference;if(!r&&e){this.selectDefaultTrack=!1;const s=Os(e,t);if(s>-1)r=t[s];else{const t=Os(e,this.tracks);r=this.tracks[t]}}let i=this.findTrackId(r);-1===i&&r&&(i=this.findTrackId(null));const n={subtitleTracks:t};this.log(`Updating subtitle tracks, ${t.length} track(s) found in "${null==s?void 0:s.join(",")}" group-id`),this.hls.trigger(p.SUBTITLE_TRACKS_UPDATED,n),-1!==i&&-1===this.trackId&&this.setSubtitleTrack(i)}else this.shouldReloadPlaylist(r)&&this.setSubtitleTrack(this.trackId)}findTrackId(t){const e=this.tracksInGroup,s=this.selectDefaultTrack;for(let i=0;i<e.length;i++){const r=e[i];if((!s||r.default)&&(s||t)&&(!t||Ns(r,t)))return i}if(t){for(let s=0;s<e.length;s++){const i=e[s];if(Pr(t.attrs,i.attrs,["LANGUAGE","ASSOC-LANGUAGE","CHARACTERISTICS"]))return s}for(let s=0;s<e.length;s++){const i=e[s];if(Pr(t.attrs,i.attrs,["LANGUAGE"]))return s}}return-1}findTrackForTextTrack(t){if(t){const e=this.tracksInGroup;for(let s=0;s<e.length;s++){if(Mr(e[s],t))return s}}return-1}onError(t,e){!e.fatal&&e.context&&(e.context.type!==we||e.context.id!==this.trackId||this.groupIds&&-1===this.groupIds.indexOf(e.context.groupId)||this.checkRetry(e))}get allSubtitleTracks(){return this.tracks}get subtitleTracks(){return this.tracksInGroup}get subtitleTrack(){return this.trackId}set subtitleTrack(t){this.selectDefaultTrack=!1,this.setSubtitleTrack(t)}setSubtitleOption(t){if(this.hls.config.subtitlePreference=t,t){const e=this.allSubtitleTracks;if(this.selectDefaultTrack=!1,e.length){const s=this.currentTrack;if(s&&Ns(t,s))return s;const i=Os(t,this.tracksInGroup);if(i>-1){const t=this.tracksInGroup[i];return this.setSubtitleTrack(i),t}if(s)return null;{const s=Os(t,e);if(s>-1)return e[s]}}}return null}loadPlaylist(t){super.loadPlaylist();const e=this.currentTrack;if(this.shouldLoadPlaylist(e)&&e){const s=e.id,i=e.groupId;let r=e.url;if(t)try{r=t.addDirectives(r)}catch(t){this.warn(`Could not construct new URL with HLS Delivery Directives: ${t}`)}this.log(`Loading subtitle playlist for id ${s}`),this.hls.trigger(p.SUBTITLE_TRACK_LOADING,{url:r,id:s,groupId:i,deliveryDirectives:t||null})}}toggleTrackModes(){const{media:t}=this;if(!t)return;const e=Ue(t.textTracks),s=this.currentTrack;let i;if(s&&(i=e.filter((t=>Mr(s,t)))[0],i||this.warn(`Unable to find subtitle TextTrack with name "${s.name}" and language "${s.lang}"`)),[].slice.call(e).forEach((t=>{"disabled"!==t.mode&&t!==i&&(t.mode="disabled")})),i){const t=this.subtitleDisplay?"showing":"hidden";i.mode!==t&&(i.mode=t)}}setSubtitleTrack(t){const e=this.tracksInGroup;if(!this.media)return void(this.queuedDefaultTrack=t);if(t<-1||t>=e.length||!f(t))return void this.warn(`Invalid subtitle track id: ${t}`);this.clearTimer(),this.selectDefaultTrack=!1;const s=this.currentTrack,i=e[t]||null;if(this.trackId=t,this.currentTrack=i,this.toggleTrackModes(),!i)return void this.hls.trigger(p.SUBTITLE_TRACK_SWITCH,{id:t});const r=!!i.details&&!i.details.live;if(t===this.trackId&&i===s&&r)return;this.log(`Switching to subtitle-track ${t}`+(i?` "${i.name}" lang:${i.lang} group:${i.groupId}`:""));const{id:n,groupId:a="",name:o,type:l,url:h}=i;this.hls.trigger(p.SUBTITLE_TRACK_SWITCH,{id:n,groupId:a,name:o,type:l,url:h});const d=this.switchParams(i.url,null==s?void 0:s.details,i.details);this.loadPlaylist(d)}},timelineController:class{constructor(t){this.hls=void 0,this.media=null,this.config=void 0,this.enabled=!0,this.Cues=void 0,this.textTracks=[],this.tracks=[],this.initPTS=[],this.unparsedVttFrags=[],this.captionsTracks={},this.nonNativeCaptionsTracks={},this.cea608Parser1=void 0,this.cea608Parser2=void 0,this.lastCc=-1,this.lastSn=-1,this.lastPartIndex=-1,this.prevCC=-1,this.vttCCs={ccOffset:0,presentationOffset:0,0:{start:0,prevCC:-1,new:!0}},this.captionsProperties=void 0,this.hls=t,this.config=t.config,this.Cues=t.config.cueHandler,this.captionsProperties={textTrack1:{label:this.config.captionsTextTrack1Label,languageCode:this.config.captionsTextTrack1LanguageCode},textTrack2:{label:this.config.captionsTextTrack2Label,languageCode:this.config.captionsTextTrack2LanguageCode},textTrack3:{label:this.config.captionsTextTrack3Label,languageCode:this.config.captionsTextTrack3LanguageCode},textTrack4:{label:this.config.captionsTextTrack4Label,languageCode:this.config.captionsTextTrack4LanguageCode}},t.on(p.MEDIA_ATTACHING,this.onMediaAttaching,this),t.on(p.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(p.MANIFEST_LOADING,this.onManifestLoading,this),t.on(p.MANIFEST_LOADED,this.onManifestLoaded,this),t.on(p.SUBTITLE_TRACKS_UPDATED,this.onSubtitleTracksUpdated,this),t.on(p.FRAG_LOADING,this.onFragLoading,this),t.on(p.FRAG_LOADED,this.onFragLoaded,this),t.on(p.FRAG_PARSING_USERDATA,this.onFragParsingUserdata,this),t.on(p.FRAG_DECRYPTED,this.onFragDecrypted,this),t.on(p.INIT_PTS_FOUND,this.onInitPtsFound,this),t.on(p.SUBTITLE_TRACKS_CLEARED,this.onSubtitleTracksCleared,this),t.on(p.BUFFER_FLUSHING,this.onBufferFlushing,this)}destroy(){const{hls:t}=this;t.off(p.MEDIA_ATTACHING,this.onMediaAttaching,this),t.off(p.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(p.MANIFEST_LOADING,this.onManifestLoading,this),t.off(p.MANIFEST_LOADED,this.onManifestLoaded,this),t.off(p.SUBTITLE_TRACKS_UPDATED,this.onSubtitleTracksUpdated,this),t.off(p.FRAG_LOADING,this.onFragLoading,this),t.off(p.FRAG_LOADED,this.onFragLoaded,this),t.off(p.FRAG_PARSING_USERDATA,this.onFragParsingUserdata,this),t.off(p.FRAG_DECRYPTED,this.onFragDecrypted,this),t.off(p.INIT_PTS_FOUND,this.onInitPtsFound,this),t.off(p.SUBTITLE_TRACKS_CLEARED,this.onSubtitleTracksCleared,this),t.off(p.BUFFER_FLUSHING,this.onBufferFlushing,this),this.hls=this.config=null,this.cea608Parser1=this.cea608Parser2=void 0}initCea608Parsers(){if(this.config.enableCEA708Captions&&(!this.cea608Parser1||!this.cea608Parser2)){const t=new nn(this,"textTrack1"),e=new nn(this,"textTrack2"),s=new nn(this,"textTrack3"),i=new nn(this,"textTrack4");this.cea608Parser1=new en(1,t,e),this.cea608Parser2=new en(3,s,i)}}addCues(t,e,s,i,r){let n=!1;for(let t=r.length;t--;){const i=r[t],d=(a=i[0],o=i[1],l=e,h=s,Math.min(o,h)-Math.max(a,l));if(d>=0&&(i[0]=Math.min(i[0],e),i[1]=Math.max(i[1],s),n=!0,d/(s-e)>.5))return}var a,o,l,h;if(n||r.push([e,s]),this.config.renderTextTracksNatively){const r=this.captionsTracks[t];this.Cues.newCue(r,e,s,i)}else{const r=this.Cues.newCue(null,e,s,i);this.hls.trigger(p.CUES_PARSED,{type:"captions",cues:r,track:t})}}onInitPtsFound(t,{frag:e,id:s,initPTS:i,timescale:r}){const{unparsedVttFrags:n}=this;"main"===s&&(this.initPTS[e.cc]={baseTime:i,timescale:r}),n.length&&(this.unparsedVttFrags=[],n.forEach((t=>{this.onFragLoaded(p.FRAG_LOADED,t)})))}getExistingTrack(t,e){const{media:s}=this;if(s)for(let i=0;i<s.textTracks.length;i++){const r=s.textTracks[i];if(Pn(r,{name:t,lang:e,attrs:{}}))return r}return null}createCaptionsTrack(t){this.config.renderTextTracksNatively?this.createNativeTrack(t):this.createNonNativeTrack(t)}createNativeTrack(t){if(this.captionsTracks[t])return;const{captionsProperties:e,captionsTracks:s,media:i}=this,{label:r,languageCode:n}=e[t],a=this.getExistingTrack(r,n);if(a)s[t]=a,Oe(s[t]),Me(s[t],i);else{const e=this.createTextTrack("captions",r,n);e&&(e[t]=!0,s[t]=e)}}createNonNativeTrack(t){if(this.nonNativeCaptionsTracks[t])return;const e=this.captionsProperties[t];if(!e)return;const s={_id:t,label:e.label,kind:"captions",default:!!e.media&&!!e.media.default,closedCaptions:e.media};this.nonNativeCaptionsTracks[t]=s,this.hls.trigger(p.NON_NATIVE_TEXT_TRACKS_FOUND,{tracks:[s]})}createTextTrack(t,e,s){const i=this.media;if(i)return i.addTextTrack(t,e,s)}onMediaAttaching(t,e){this.media=e.media,this._cleanTracks()}onMediaDetaching(){const{captionsTracks:t}=this;Object.keys(t).forEach((e=>{Oe(t[e]),delete t[e]})),this.nonNativeCaptionsTracks={}}onManifestLoading(){this.lastCc=-1,this.lastSn=-1,this.lastPartIndex=-1,this.prevCC=-1,this.vttCCs={ccOffset:0,presentationOffset:0,0:{start:0,prevCC:-1,new:!0}},this._cleanTracks(),this.tracks=[],this.captionsTracks={},this.nonNativeCaptionsTracks={},this.textTracks=[],this.unparsedVttFrags=[],this.initPTS=[],this.cea608Parser1&&this.cea608Parser2&&(this.cea608Parser1.reset(),this.cea608Parser2.reset())}_cleanTracks(){const{media:t}=this;if(!t)return;const e=t.textTracks;if(e)for(let t=0;t<e.length;t++)Oe(e[t])}onSubtitleTracksUpdated(t,e){const s=e.subtitleTracks||[],i=s.some((t=>t.textCodec===Sn));if(this.config.enableWebVTT||i&&this.config.enableIMSC1){if(xr(this.tracks,s))return void(this.tracks=s);if(this.textTracks=[],this.tracks=s,this.config.renderTextTracksNatively){const t=this.media,e=t?Ue(t.textTracks):null;if(this.tracks.forEach(((t,s)=>{let i;if(e){let s=null;for(let i=0;i<e.length;i++)if(e[i]&&Pn(e[i],t)){s=e[i],e[i]=null;break}s&&(i=s)}if(i)Oe(i);else{const e=xn(t);i=this.createTextTrack(e,t.name,t.lang),i&&(i.mode="disabled")}i&&this.textTracks.push(i)})),null!=e&&e.length){const t=e.filter((t=>null!==t)).map((t=>t.label));t.length&&A.warn(`Media element contains unused subtitle tracks: ${t.join(", ")}. Replace media element for each source to clear TextTracks and captions menu.`)}}else if(this.tracks.length){const t=this.tracks.map((t=>({label:t.name,kind:t.type.toLowerCase(),default:t.default,subtitleTrack:t})));this.hls.trigger(p.NON_NATIVE_TEXT_TRACKS_FOUND,{tracks:t})}}}onManifestLoaded(t,e){this.config.enableCEA708Captions&&e.captions&&e.captions.forEach((t=>{const e=/(?:CC|SERVICE)([1-4])/.exec(t.instreamId);if(!e)return;const s=`textTrack${e[1]}`,i=this.captionsProperties[s];i&&(i.label=t.name,t.lang&&(i.languageCode=t.lang),i.media=t)}))}closedCaptionsForLevel(t){const e=this.hls.levels[t.level];return null==e?void 0:e.attrs["CLOSED-CAPTIONS"]}onFragLoading(t,e){if(this.enabled&&e.frag.type===De){var s,i;const{cea608Parser1:t,cea608Parser2:r,lastSn:n}=this,{cc:a,sn:o}=e.frag,l=null!=(s=null==(i=e.part)?void 0:i.index)?s:-1;t&&r&&(o!==n+1||o===n&&l!==this.lastPartIndex+1||a!==this.lastCc)&&(t.reset(),r.reset()),this.lastCc=a,this.lastSn=o,this.lastPartIndex=l}}onFragLoaded(t,e){const{frag:s,payload:i}=e;if(s.type===Ce)if(i.byteLength){const t=s.decryptdata,r="stats"in e;if(null==t||!t.encrypted||r){const t=this.tracks[s.level],r=this.vttCCs;r[s.cc]||(r[s.cc]={start:s.start,prevCC:this.prevCC,new:!0},this.prevCC=s.cc),t&&t.textCodec===Sn?this._parseIMSC1(s,i):this._parseVTTs(e)}}else this.hls.trigger(p.SUBTITLE_FRAG_PROCESSED,{success:!1,frag:s,error:new Error("Empty subtitle payload")})}_parseIMSC1(t,e){const s=this.hls;bn(e,this.initPTS[t.cc],(e=>{this._appendCues(e,t.level),s.trigger(p.SUBTITLE_FRAG_PROCESSED,{success:!0,frag:t})}),(e=>{A.log(`Failed to parse IMSC1: ${e}`),s.trigger(p.SUBTITLE_FRAG_PROCESSED,{success:!1,frag:t,error:e})}))}_parseVTTs(t){var e;const{frag:s,payload:i}=t,{initPTS:r,unparsedVttFrags:n}=this,a=r.length-1;if(!r[s.cc]&&-1===a)return void n.push(t);const o=this.hls;Tn(null!=(e=s.initSegment)&&e.data?Bt(s.initSegment.data,new Uint8Array(i)):i,this.initPTS[s.cc],this.vttCCs,s.cc,s.start,(t=>{this._appendCues(t,s.level),o.trigger(p.SUBTITLE_FRAG_PROCESSED,{success:!0,frag:s})}),(e=>{const r="Missing initPTS for VTT MPEGTS"===e.message;r?n.push(t):this._fallbackToIMSC1(s,i),A.log(`Failed to parse VTT cue: ${e}`),r&&a>s.cc||o.trigger(p.SUBTITLE_FRAG_PROCESSED,{success:!1,frag:s,error:e})}))}_fallbackToIMSC1(t,e){const s=this.tracks[t.level];s.textCodec||bn(e,this.initPTS[t.cc],(()=>{s.textCodec=Sn,this._parseIMSC1(t,e)}),(()=>{s.textCodec="wvtt"}))}_appendCues(t,e){const s=this.hls;if(this.config.renderTextTracksNatively){const s=this.textTracks[e];if(!s||"disabled"===s.mode)return;t.forEach((t=>Fe(s,t)))}else{const i=this.tracks[e];if(!i)return;const r=i.default?"default":"subtitles"+e;s.trigger(p.CUES_PARSED,{type:"subtitles",cues:t,track:r})}}onFragDecrypted(t,e){const{frag:s}=e;s.type===Ce&&this.onFragLoaded(p.FRAG_LOADED,e)}onSubtitleTracksCleared(){this.tracks=[],this.captionsTracks={}}onFragParsingUserdata(t,e){this.initCea608Parsers();const{cea608Parser1:s,cea608Parser2:i}=this;if(!this.enabled||!s||!i)return;const{frag:r,samples:n}=e;if(r.type!==De||"NONE"!==this.closedCaptionsForLevel(r))for(let t=0;t<n.length;t++){const e=n[t].bytes;if(e){const r=this.extractCea608Data(e);s.addData(n[t].pts,r[0]),i.addData(n[t].pts,r[1])}}}onBufferFlushing(t,{startOffset:e,endOffset:s,endOffsetSubtitles:i,type:r}){const{media:n}=this;if(n&&!(n.currentTime<s)){if(!r||"video"===r){const{captionsTracks:t}=this;Object.keys(t).forEach((i=>Ne(t[i],e,s)))}if(this.config.renderTextTracksNatively&&0===e&&void 0!==i){const{textTracks:t}=this;Object.keys(t).forEach((s=>Ne(t[s],e,i)))}}}extractCea608Data(t){const e=[[],[]],s=31&t[0];let i=2;for(let r=0;r<s;r++){const s=t[i++],r=127&t[i++],n=127&t[i++];if(0===r&&0===n)continue;if(!!(4&s)){const t=3&s;0!==t&&1!==t||(e[t].push(r),e[t].push(n))}}return e}},audioStreamController:class extends Ri{constructor(t,e,s){super(t,e,s,"[audio-stream-controller]",Ie),this.videoBuffer=null,this.videoTrackCC=-1,this.waitingVideoCC=-1,this.bufferedTrack=null,this.switchingTrack=null,this.trackId=-1,this.waitingData=null,this.mainDetails=null,this.flushing=!1,this.bufferFlushed=!1,this.cachedTrackLoadedData=null,this._registerListeners()}onHandlerDestroying(){this._unregisterListeners(),super.onHandlerDestroying(),this.mainDetails=null,this.bufferedTrack=null,this.switchingTrack=null}_registerListeners(){const{hls:t}=this;t.on(p.MEDIA_ATTACHED,this.onMediaAttached,this),t.on(p.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(p.MANIFEST_LOADING,this.onManifestLoading,this),t.on(p.LEVEL_LOADED,this.onLevelLoaded,this),t.on(p.AUDIO_TRACKS_UPDATED,this.onAudioTracksUpdated,this),t.on(p.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),t.on(p.AUDIO_TRACK_LOADED,this.onAudioTrackLoaded,this),t.on(p.ERROR,this.onError,this),t.on(p.BUFFER_RESET,this.onBufferReset,this),t.on(p.BUFFER_CREATED,this.onBufferCreated,this),t.on(p.BUFFER_FLUSHING,this.onBufferFlushing,this),t.on(p.BUFFER_FLUSHED,this.onBufferFlushed,this),t.on(p.INIT_PTS_FOUND,this.onInitPtsFound,this),t.on(p.FRAG_BUFFERED,this.onFragBuffered,this)}_unregisterListeners(){const{hls:t}=this;t.off(p.MEDIA_ATTACHED,this.onMediaAttached,this),t.off(p.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(p.MANIFEST_LOADING,this.onManifestLoading,this),t.off(p.LEVEL_LOADED,this.onLevelLoaded,this),t.off(p.AUDIO_TRACKS_UPDATED,this.onAudioTracksUpdated,this),t.off(p.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),t.off(p.AUDIO_TRACK_LOADED,this.onAudioTrackLoaded,this),t.off(p.ERROR,this.onError,this),t.off(p.BUFFER_RESET,this.onBufferReset,this),t.off(p.BUFFER_CREATED,this.onBufferCreated,this),t.off(p.BUFFER_FLUSHING,this.onBufferFlushing,this),t.off(p.BUFFER_FLUSHED,this.onBufferFlushed,this),t.off(p.INIT_PTS_FOUND,this.onInitPtsFound,this),t.off(p.FRAG_BUFFERED,this.onFragBuffered,this)}onInitPtsFound(t,{frag:e,id:s,initPTS:i,timescale:r}){if("main"===s){const t=e.cc;this.initPTS[e.cc]={baseTime:i,timescale:r},this.log(`InitPTS for cc: ${t} found from main: ${i}`),this.videoTrackCC=t,this.state===Li&&this.tick()}}startLoad(t){if(!this.levels)return this.startPosition=t,void(this.state=ui);const e=this.lastCurrentTime;this.stopLoad(),this.setInterval(100),e>0&&-1===t?(this.log(`Override startPosition with lastCurrentTime @${e.toFixed(3)}`),t=e,this.state=fi):(this.loadedmetadata=!1,this.state=vi),this.nextLoadPosition=this.startPosition=this.lastCurrentTime=t,this.tick()}doTick(){switch(this.state){case fi:this.doTickIdle();break;case vi:{var t;const{levels:e,trackId:s}=this,i=null==e||null==(t=e[s])?void 0:t.details;if(i){if(this.waitForCdnTuneIn(i))break;this.state=Li}break}case pi:{var e;const t=performance.now(),s=this.retryDate;if(!s||t>=s||null!=(e=this.media)&&e.seeking){const{levels:t,trackId:e}=this;this.log("RetryDate reached, switch back to IDLE state"),this.resetStartWhenNotLoaded((null==t?void 0:t[e])||null),this.state=fi}break}case Li:{const t=this.waitingData;if(t){const{frag:e,part:s,cache:i,complete:r}=t;if(void 0!==this.initPTS[e.cc]){this.waitingData=null,this.waitingVideoCC=-1,this.state=mi;const t={frag:e,part:s,payload:i.flush(),networkDetails:null};this._handleFragmentLoadProgress(t),r&&super._handleFragmentLoadComplete(t)}else if(this.videoTrackCC!==this.waitingVideoCC)this.log(`Waiting fragment cc (${e.cc}) cancelled because video is at cc ${this.videoTrackCC}`),this.clearWaitingFragment();else{const t=this.getLoadPosition(),s=Xs.bufferInfo(this.mediaBuffer,t,this.config.maxBufferHole);ys(s.end,this.config.maxFragLookUpTolerance,e)<0&&(this.log(`Waiting fragment cc (${e.cc}) @ ${e.start} cancelled because another fragment at ${s.end} is needed`),this.clearWaitingFragment())}}else this.state=fi}}this.onTickEnd()}clearWaitingFragment(){const t=this.waitingData;t&&(this.fragmentTracker.removeFragment(t.frag),this.waitingData=null,this.waitingVideoCC=-1,this.state=fi)}resetLoadingState(){this.clearWaitingFragment(),super.resetLoadingState()}onTickEnd(){const{media:t}=this;null!=t&&t.readyState&&(this.lastCurrentTime=t.currentTime)}doTickIdle(){const{hls:t,levels:e,media:s,trackId:i}=this,r=t.config;if(!s&&(this.startFragRequested||!r.startFragPrefetch)||null==e||!e[i])return;const n=e[i],a=n.details;if(!a||a.live&&this.levelLastLoaded!==n||this.waitForCdnTuneIn(a))return void(this.state=vi);const o=this.mediaBuffer?this.mediaBuffer:this.media;this.bufferFlushed&&o&&(this.bufferFlushed=!1,this.afterBufferFlushed(o,C,Ie));const l=this.getFwdBufferInfo(o,Ie);if(null===l)return;const{bufferedTrack:h,switchingTrack:d}=this;if(!d&&this._streamEnded(l,a))return t.trigger(p.BUFFER_EOS,{type:"audio"}),void(this.state=Ti);const c=this.getFwdBufferInfo(this.videoBuffer?this.videoBuffer:this.media,De),u=l.len,f=this.getMaxBufferLength(null==c?void 0:c.len),g=a.fragments,m=g[0].start;let v=this.flushing?this.getLoadPosition():l.end;if(d&&s){const t=this.getLoadPosition();h&&!Pr(d.attrs,h.attrs)&&(v=t),a.PTSKnown&&t<m&&(l.end>m||l.nextStart)&&(this.log("Alt audio track ahead of main track, seek to start of alt audio track"),s.currentTime=m+.05)}if(u>=f&&!d&&v<g[g.length-1].start)return;let y=this.getNextFragment(v,a),E=!1;if(y&&this.isLoopLoading(y,v)&&(E=!!y.gap,y=this.getNextFragmentLoopLoading(y,a,l,De,f)),!y)return void(this.bufferFlushed=!0);const T=c&&y.start>c.end+a.targetduration;if(T||(null==c||!c.len)&&l.len){const t=this.getAppendedFrag(y.start,De);if(null===t)return;if(E||(E=!!t.gap||!!T&&0===c.len),T&&!E||E&&l.nextStart&&l.nextStart<t.end)return}this.loadFragment(y,n,v)}getMaxBufferLength(t){const e=super.getMaxBufferLength();return t?Math.min(Math.max(e,t),this.config.maxMaxBufferLength):e}onMediaDetaching(){this.videoBuffer=null,this.bufferFlushed=this.flushing=!1,super.onMediaDetaching()}onAudioTracksUpdated(t,{audioTracks:e}){this.resetTransmuxer(),this.levels=e.map((t=>new es(t)))}onAudioTrackSwitching(t,e){const s=!!e.url;this.trackId=e.id;const{fragCurrent:i}=this;i&&(i.abortRequests(),this.removeUnbufferedFrags(i.start)),this.resetLoadingState(),s?this.setInterval(100):this.resetTransmuxer(),s?(this.switchingTrack=e,this.state=fi,this.flushAudioIfNeeded(e)):(this.switchingTrack=null,this.bufferedTrack=e,this.state=ui),this.tick()}onManifestLoading(){this.fragmentTracker.removeAllFragments(),this.startPosition=this.lastCurrentTime=0,this.bufferFlushed=this.flushing=!1,this.levels=this.mainDetails=this.waitingData=this.bufferedTrack=this.cachedTrackLoadedData=this.switchingTrack=null,this.startFragRequested=!1,this.trackId=this.videoTrackCC=this.waitingVideoCC=-1}onLevelLoaded(t,e){this.mainDetails=e.details,null!==this.cachedTrackLoadedData&&(this.hls.trigger(p.AUDIO_TRACK_LOADED,this.cachedTrackLoadedData),this.cachedTrackLoadedData=null)}onAudioTrackLoaded(t,e){var s;if(null==this.mainDetails)return void(this.cachedTrackLoadedData=e);const{levels:i}=this,{details:r,id:n}=e;if(!i)return void this.warn(`Audio tracks were reset while loading level ${n}`);this.log(`Audio track ${n} loaded [${r.startSN},${r.endSN}]${r.lastPartSn?`[part-${r.lastPartSn}-${r.lastPartIndex}]`:""},duration:${r.totalduration}`);const a=i[n];let o=0;if(r.live||null!=(s=a.details)&&s.live){this.checkLiveUpdate(r);const t=this.mainDetails;if(r.deltaUpdateFailed||!t)return;var l;if(!a.details&&r.hasProgramDateTime&&t.hasProgramDateTime)ei(r,t),o=r.fragments[0].start;else o=this.alignPlaylists(r,a.details,null==(l=this.levelLastLoaded)?void 0:l.details)}a.details=r,this.levelLastLoaded=a,this.startFragRequested||!this.mainDetails&&r.live||this.setStartPosition(this.mainDetails||r,o),this.state!==vi||this.waitForCdnTuneIn(r)||(this.state=fi),this.tick()}_handleFragmentLoadProgress(t){var e;const{frag:s,part:i,payload:r}=t,{config:n,trackId:a,levels:o}=this;if(!o)return void this.warn(`Audio tracks were reset while fragment load was in progress. Fragment ${s.sn} of level ${s.level} will not be buffered`);const l=o[a];if(!l)return void this.warn("Audio track is undefined on fragment load progress");const h=l.details;if(!h)return this.warn("Audio track details undefined on fragment load progress"),void this.removeUnbufferedFrags(s.start);const d=n.defaultAudioCodec||l.audioCodec||"mp4a.40.2";let c=this.transmuxer;c||(c=this.transmuxer=new _r(this.hls,Ie,this._handleTransmuxComplete.bind(this),this._handleTransmuxerFlush.bind(this)));const u=this.initPTS[s.cc],f=null==(e=s.initSegment)?void 0:e.data;if(void 0!==u){const t=!1,e=i?i.index:-1,n=-1!==e,a=new zs(s.level,s.sn,s.stats.chunkCount,r.byteLength,e,n);c.push(r,f,d,"",s,i,h.totalduration,t,a,u)}else{this.log(`Unknown video PTS for cc ${s.cc}, waiting for video PTS before demuxing audio frag ${s.sn} of [${h.startSN} ,${h.endSN}],track ${a}`);const{cache:t}=this.waitingData=this.waitingData||{frag:s,part:i,cache:new bi,complete:!1};t.push(new Uint8Array(r)),this.waitingVideoCC=this.videoTrackCC,this.state=Li}}_handleFragmentLoadComplete(t){this.waitingData?this.waitingData.complete=!0:super._handleFragmentLoadComplete(t)}onBufferReset(){this.mediaBuffer=this.videoBuffer=null,this.loadedmetadata=!1}onBufferCreated(t,e){const s=e.tracks.audio;s&&(this.mediaBuffer=s.buffer||null),e.tracks.video&&(this.videoBuffer=e.tracks.video.buffer||null)}onFragBuffered(t,e){const{frag:s,part:i}=e;if(s.type===Ie)if(this.fragContextChanged(s))this.warn(`Fragment ${s.sn}${i?" p: "+i.index:""} of level ${s.level} finished buffering, but was aborted. state: ${this.state}, audioSwitch: ${this.switchingTrack?this.switchingTrack.name:"false"}`);else{if("initSegment"!==s.sn){this.fragPrevious=s;const t=this.switchingTrack;t&&(this.bufferedTrack=t,this.switchingTrack=null,this.hls.trigger(p.AUDIO_TRACK_SWITCHED,h({},t)))}this.fragBufferedComplete(s,i)}else if(!this.loadedmetadata&&s.type===De){const t=this.videoBuffer||this.media;if(t){Xs.getBuffered(t).length&&(this.loadedmetadata=!0)}}}onError(t,e){var s;if(e.fatal)this.state=Si;else switch(e.details){case y.FRAG_GAP:case y.FRAG_PARSING_ERROR:case y.FRAG_DECRYPT_ERROR:case y.FRAG_LOAD_ERROR:case y.FRAG_LOAD_TIMEOUT:case y.KEY_LOAD_ERROR:case y.KEY_LOAD_TIMEOUT:this.onFragmentOrKeyLoadError(Ie,e);break;case y.AUDIO_TRACK_LOAD_ERROR:case y.AUDIO_TRACK_LOAD_TIMEOUT:case y.LEVEL_PARSING_ERROR:e.levelRetry||this.state!==vi||(null==(s=e.context)?void 0:s.type)!==ke||(this.state=fi);break;case y.BUFFER_APPEND_ERROR:case y.BUFFER_FULL_ERROR:if(!e.parent||"audio"!==e.parent)return;if(e.details===y.BUFFER_APPEND_ERROR)return void this.resetLoadingState();this.reduceLengthAndFlushBuffer(e)&&(this.bufferedTrack=null,super.flushMainBuffer(0,Number.POSITIVE_INFINITY,"audio"));break;case y.INTERNAL_EXCEPTION:this.recoverWorkerError(e)}}onBufferFlushing(t,{type:e}){e!==_&&(this.flushing=!0)}onBufferFlushed(t,{type:e}){if(e!==_){this.flushing=!1,this.bufferFlushed=!0,this.state===Ti&&(this.state=fi);const t=this.mediaBuffer||this.media;t&&(this.afterBufferFlushed(t,e,Ie),this.tick())}}_handleTransmuxComplete(t){var e;const s="audio",{hls:i}=this,{remuxResult:r,chunkMeta:n}=t,a=this.getCurrentContext(n);if(!a)return void this.resetWhenMissingContext(n);const{frag:o,part:l,level:h}=a,{details:d}=h,{audio:c,text:f,id3:g,initSegment:m}=r;if(!this.fragContextChanged(o)&&d){if(this.state=yi,this.switchingTrack&&c&&this.completeAudioSwitch(this.switchingTrack),null!=m&&m.tracks){const t=o.initSegment||o;this._bufferInitSegment(h,m.tracks,t,n),i.trigger(p.FRAG_PARSING_INIT_SEGMENT,{frag:t,id:s,tracks:m.tracks})}if(c){const{startPTS:t,endPTS:e,startDTS:s,endDTS:i}=c;l&&(l.elementaryStreams[C]={startPTS:t,endPTS:e,startDTS:s,endDTS:i}),o.setElementaryStreamInfo(C,t,e,s,i),this.bufferFragmentData(c,o,l,n)}if(null!=g&&null!=(e=g.samples)&&e.length){const t=u({id:s,frag:o,details:d},g);i.trigger(p.FRAG_PARSING_METADATA,t)}if(f){const t=u({id:s,frag:o,details:d},f);i.trigger(p.FRAG_PARSING_USERDATA,t)}}else this.fragmentTracker.removeFragment(o)}_bufferInitSegment(t,e,s,i){if(this.state!==yi)return;e.video&&delete e.video;const r=e.audio;if(!r)return;r.id="audio";const n=t.audioCodec;this.log(`Init audio buffer, container:${r.container}, codecs[level/parsed]=[${n}/${r.codec}]`),n&&1===n.split(",").length&&(r.levelCodec=n),this.hls.trigger(p.BUFFER_CODECS,e);const a=r.initSegment;if(null!=a&&a.byteLength){const t={type:"audio",frag:s,part:null,chunkMeta:i,parent:s.type,data:a};this.hls.trigger(p.BUFFER_APPENDING,t)}this.tickImmediate()}loadFragment(t,e,s){const i=this.fragmentTracker.getState(t);var r;if(this.fragCurrent=t,this.switchingTrack||i===Gs||i===Hs)if("initSegment"===t.sn)this._loadInitSegment(t,e);else if(null!=(r=e.details)&&r.live&&!this.initPTS[t.cc]){this.log(`Waiting for video PTS in continuity counter ${t.cc} of live stream before loading audio fragment ${t.sn} of level ${this.trackId}`),this.state=Li;const s=this.mainDetails;s&&s.fragments[0].start!==e.details.fragments[0].start&&ei(e.details,s)}else this.startFragRequested=!0,super.loadFragment(t,e,s);else this.clearTrackerIfNeeded(t)}flushAudioIfNeeded(t){const{media:e,bufferedTrack:s}=this,i=null==s?void 0:s.attrs,r=t.attrs;e&&i&&(i.CHANNELS!==r.CHANNELS||s.name!==t.name||s.lang!==t.lang)&&(this.log("Switching audio track : flushing all audio"),super.flushMainBuffer(0,Number.POSITIVE_INFINITY,"audio"),this.bufferedTrack=null)}completeAudioSwitch(t){const{hls:e}=this;this.flushAudioIfNeeded(t),this.bufferedTrack=t,this.switchingTrack=null,e.trigger(p.AUDIO_TRACK_SWITCHED,h({},t))}},audioTrackController:class extends ws{constructor(t){super(t,"[audio-track-controller]"),this.tracks=[],this.groupIds=null,this.tracksInGroup=[],this.trackId=-1,this.currentTrack=null,this.selectDefaultTrack=!0,this.registerListeners()}registerListeners(){const{hls:t}=this;t.on(p.MANIFEST_LOADING,this.onManifestLoading,this),t.on(p.MANIFEST_PARSED,this.onManifestParsed,this),t.on(p.LEVEL_LOADING,this.onLevelLoading,this),t.on(p.LEVEL_SWITCHING,this.onLevelSwitching,this),t.on(p.AUDIO_TRACK_LOADED,this.onAudioTrackLoaded,this),t.on(p.ERROR,this.onError,this)}unregisterListeners(){const{hls:t}=this;t.off(p.MANIFEST_LOADING,this.onManifestLoading,this),t.off(p.MANIFEST_PARSED,this.onManifestParsed,this),t.off(p.LEVEL_LOADING,this.onLevelLoading,this),t.off(p.LEVEL_SWITCHING,this.onLevelSwitching,this),t.off(p.AUDIO_TRACK_LOADED,this.onAudioTrackLoaded,this),t.off(p.ERROR,this.onError,this)}destroy(){this.unregisterListeners(),this.tracks.length=0,this.tracksInGroup.length=0,this.currentTrack=null,super.destroy()}onManifestLoading(){this.tracks=[],this.tracksInGroup=[],this.groupIds=null,this.currentTrack=null,this.trackId=-1,this.selectDefaultTrack=!0}onManifestParsed(t,e){this.tracks=e.audioTracks||[]}onAudioTrackLoaded(t,e){const{id:s,groupId:i,details:r}=e,n=this.tracksInGroup[s];if(!n||n.groupId!==i)return void this.warn(`Audio track with id:${s} and group:${i} not found in active group ${null==n?void 0:n.groupId}`);const a=n.details;n.details=e.details,this.log(`Audio track ${s} "${n.name}" lang:${n.lang} group:${i} loaded [${r.startSN}-${r.endSN}]`),s===this.trackId&&this.playlistLoaded(s,e,a)}onLevelLoading(t,e){this.switchLevel(e.level)}onLevelSwitching(t,e){this.switchLevel(e.level)}switchLevel(t){const e=this.hls.levels[t];if(!e)return;const s=e.audioGroups||null,i=this.groupIds;let r=this.currentTrack;if(!s||(null==i?void 0:i.length)!==(null==s?void 0:s.length)||null!=s&&s.some((t=>-1===(null==i?void 0:i.indexOf(t))))){this.groupIds=s,this.trackId=-1,this.currentTrack=null;const t=this.tracks.filter((t=>!s||-1!==s.indexOf(t.groupId)));if(t.length)this.selectDefaultTrack&&!t.some((t=>t.default))&&(this.selectDefaultTrack=!1),t.forEach(((t,e)=>{t.id=e}));else if(!r&&!this.tracksInGroup.length)return;this.tracksInGroup=t;const e=this.hls.config.audioPreference;if(!r&&e){const s=Os(e,t,Us);if(s>-1)r=t[s];else{const t=Os(e,this.tracks);r=this.tracks[t]}}let i=this.findTrackId(r);-1===i&&r&&(i=this.findTrackId(null));const a={audioTracks:t};this.log(`Updating audio tracks, ${t.length} track(s) found in group(s): ${null==s?void 0:s.join(",")}`),this.hls.trigger(p.AUDIO_TRACKS_UPDATED,a);const o=this.trackId;if(-1!==i&&-1===o)this.setAudioTrack(i);else if(t.length&&-1===o){var n;const e=new Error(`No audio track selected for current audio group-ID(s): ${null==(n=this.groupIds)?void 0:n.join(",")} track count: ${t.length}`);this.warn(e.message),this.hls.trigger(p.ERROR,{type:v.MEDIA_ERROR,details:y.AUDIO_TRACK_LOAD_ERROR,fatal:!0,error:e})}}else this.shouldReloadPlaylist(r)&&this.setAudioTrack(this.trackId)}onError(t,e){!e.fatal&&e.context&&(e.context.type!==ke||e.context.id!==this.trackId||this.groupIds&&-1===this.groupIds.indexOf(e.context.groupId)||(this.requestScheduled=-1,this.checkRetry(e)))}get allAudioTracks(){return this.tracks}get audioTracks(){return this.tracksInGroup}get audioTrack(){return this.trackId}set audioTrack(t){this.selectDefaultTrack=!1,this.setAudioTrack(t)}setAudioOption(t){const e=this.hls;if(e.config.audioPreference=t,t){const s=this.allAudioTracks;if(this.selectDefaultTrack=!1,s.length){const i=this.currentTrack;if(i&&Ns(t,i,Us))return i;const r=Os(t,this.tracksInGroup,Us);if(r>-1){const t=this.tracksInGroup[r];return this.setAudioTrack(r),t}if(i){let i=e.loadLevel;-1===i&&(i=e.firstAutoLevel);const r=function(t,e,s,i,r){const n=e[i],a=e.reduce(((t,e,s)=>{const i=e.uri;return(t[i]||(t[i]=[])).push(s),t}),{})[n.uri];a.length>1&&(i=Math.max.apply(Math,a));const o=n.videoRange,l=n.frameRate,h=n.codecSet.substring(0,4),d=Bs(e,i,(e=>{if(e.videoRange!==o||e.frameRate!==l||e.codecSet.substring(0,4)!==h)return!1;const i=e.audioGroups,n=s.filter((t=>!i||-1!==i.indexOf(t.groupId)));return Os(t,n,r)>-1}));return d>-1?d:Bs(e,i,(e=>{const i=e.audioGroups,n=s.filter((t=>!i||-1!==i.indexOf(t.groupId)));return Os(t,n,r)>-1}))}(t,e.levels,s,i,Us);if(-1===r)return null;e.nextLoadLevel=r}if(t.channels||t.audioCodec){const e=Os(t,s);if(e>-1)return s[e]}}}return null}setAudioTrack(t){const e=this.tracksInGroup;if(t<0||t>=e.length)return void this.warn(`Invalid audio track id: ${t}`);this.clearTimer(),this.selectDefaultTrack=!1;const s=this.currentTrack,i=e[t],r=i.details&&!i.details.live;if(t===this.trackId&&i===s&&r)return;if(this.log(`Switching to audio-track ${t} "${i.name}" lang:${i.lang} group:${i.groupId} channels:${i.channels}`),this.trackId=t,this.currentTrack=i,this.hls.trigger(p.AUDIO_TRACK_SWITCHING,h({},i)),r)return;const n=this.switchParams(i.url,null==s?void 0:s.details,i.details);this.loadPlaylist(n)}findTrackId(t){const e=this.tracksInGroup;for(let s=0;s<e.length;s++){const i=e[s];if((!this.selectDefaultTrack||i.default)&&(!t||Ns(t,i,Us)))return s}if(t){const{name:s,lang:i,assocLang:r,characteristics:n,audioCodec:a,channels:o}=t;for(let t=0;t<e.length;t++){if(Ns({name:s,lang:i,assocLang:r,characteristics:n,audioCodec:a,channels:o},e[t],Us))return t}for(let s=0;s<e.length;s++){const i=e[s];if(Pr(t.attrs,i.attrs,["LANGUAGE","ASSOC-LANGUAGE","CHARACTERISTICS"]))return s}for(let s=0;s<e.length;s++){const i=e[s];if(Pr(t.attrs,i.attrs,["LANGUAGE"]))return s}}return-1}loadPlaylist(t){const e=this.currentTrack;if(this.shouldLoadPlaylist(e)&&e){super.loadPlaylist();const s=e.id,i=e.groupId;let r=e.url;if(t)try{r=t.addDirectives(r)}catch(t){this.warn(`Could not construct new URL with HLS Delivery Directives: ${t}`)}this.log(`loading audio-track playlist ${s} "${e.name}" lang:${e.lang} group:${i}`),this.clearTimer(),this.hls.trigger(p.AUDIO_TRACK_LOADING,{url:r,id:s,groupId:i,deliveryDirectives:t||null})}}},emeController:On,cmcdController:class{constructor(t){this.hls=void 0,this.config=void 0,this.media=void 0,this.sid=void 0,this.cid=void 0,this.useHeaders=!1,this.includeKeys=void 0,this.initialized=!1,this.starved=!1,this.buffering=!0,this.audioBuffer=void 0,this.videoBuffer=void 0,this.onWaiting=()=>{this.initialized&&(this.starved=!0),this.buffering=!0},this.onPlaying=()=>{this.initialized||(this.initialized=!0),this.buffering=!1},this.applyPlaylistData=t=>{try{this.apply(t,{ot:Un.MANIFEST,su:!this.initialized})}catch(t){A.warn("Could not generate manifest CMCD data.",t)}},this.applyFragmentData=t=>{try{const e=t.frag,s=this.hls.levels[e.level],i=this.getObjectType(e),r={d:1e3*e.duration,ot:i};i!==Un.VIDEO&&i!==Un.AUDIO&&i!=Un.MUXED||(r.br=s.bitrate/1e3,r.tb=this.getTopBandwidth(i)/1e3,r.bl=this.getBufferLength(i)),this.apply(t,r)}catch(t){A.warn("Could not generate segment CMCD data.",t)}},this.hls=t;const e=this.config=t.config,{cmcd:s}=e;null!=s&&(e.pLoader=this.createPlaylistLoader(),e.fLoader=this.createFragmentLoader(),this.sid=s.sessionId||function(){try{return crypto.randomUUID()}catch(t){try{const t=URL.createObjectURL(new Blob),e=t.toString();return URL.revokeObjectURL(t),e.slice(e.lastIndexOf("/")+1)}catch(t){let e=(new Date).getTime();return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(t=>{const s=(e+16*Math.random())%16|0;return e=Math.floor(e/16),("x"==t?s:3&s|8).toString(16)}))}}}(),this.cid=s.contentId,this.useHeaders=!0===s.useHeaders,this.includeKeys=s.includeKeys,this.registerListeners())}registerListeners(){const t=this.hls;t.on(p.MEDIA_ATTACHED,this.onMediaAttached,this),t.on(p.MEDIA_DETACHED,this.onMediaDetached,this),t.on(p.BUFFER_CREATED,this.onBufferCreated,this)}unregisterListeners(){const t=this.hls;t.off(p.MEDIA_ATTACHED,this.onMediaAttached,this),t.off(p.MEDIA_DETACHED,this.onMediaDetached,this),t.off(p.BUFFER_CREATED,this.onBufferCreated,this)}destroy(){this.unregisterListeners(),this.onMediaDetached(),this.hls=this.config=this.audioBuffer=this.videoBuffer=null,this.onWaiting=this.onPlaying=null}onMediaAttached(t,e){this.media=e.media,this.media.addEventListener("waiting",this.onWaiting),this.media.addEventListener("playing",this.onPlaying)}onMediaDetached(){this.media&&(this.media.removeEventListener("waiting",this.onWaiting),this.media.removeEventListener("playing",this.onPlaying),this.media=null)}onBufferCreated(t,e){var s,i;this.audioBuffer=null==(s=e.tracks.audio)?void 0:s.buffer,this.videoBuffer=null==(i=e.tracks.video)?void 0:i.buffer}createData(){var t;return{v:1,sf:Bn.HLS,sid:this.sid,cid:this.cid,pr:null==(t=this.media)?void 0:t.playbackRate,mtp:this.hls.bandwidthEstimate/1e3}}apply(t,e={}){u(e,this.createData());const s=e.ot===Un.INIT||e.ot===Un.VIDEO||e.ot===Un.MUXED;this.starved&&s&&(e.bs=!0,e.su=!0,this.starved=!1),null==e.su&&(e.su=this.buffering);const{includeKeys:i}=this;i&&(e=Object.keys(e).reduce(((t,s)=>(i.includes(s)&&(t[s]=e[s]),t)),{})),this.useHeaders?(t.headers||(t.headers={}),ha(t.headers,e)):t.url=ca(t.url,e)}getObjectType(t){const{type:e}=t;return"subtitle"===e?Un.TIMED_TEXT:"initSegment"===t.sn?Un.INIT:"audio"===e?Un.AUDIO:"main"===e?this.hls.audioTracks.length?Un.VIDEO:Un.MUXED:void 0}getTopBandwidth(t){let e,s=0;const i=this.hls;if(t===Un.AUDIO)e=i.audioTracks;else{const t=i.maxAutoLevel,s=t>-1?t+1:i.levels.length;e=i.levels.slice(0,s)}for(const t of e)t.bitrate>s&&(s=t.bitrate);return s>0?s:NaN}getBufferLength(t){const e=this.hls.media,s=t===Un.AUDIO?this.audioBuffer:this.videoBuffer;if(!s||!e)return NaN;return 1e3*Xs.bufferInfo(s,e.currentTime,this.config.maxBufferHole).len}createPlaylistLoader(){const{pLoader:t}=this.config,e=this.applyPlaylistData,s=t||this.config.loader;return class{constructor(t){this.loader=void 0,this.loader=new s(t)}get stats(){return this.loader.stats}get context(){return this.loader.context}destroy(){this.loader.destroy()}abort(){this.loader.abort()}load(t,s,i){e(t),this.loader.load(t,s,i)}}}createFragmentLoader(){const{fLoader:t}=this.config,e=this.applyFragmentData,s=t||this.config.loader;return class{constructor(t){this.loader=void 0,this.loader=new s(t)}get stats(){return this.loader.stats}get context(){return this.loader.context}destroy(){this.loader.destroy()}abort(){this.loader.abort()}load(t,s,i){e(t),this.loader.load(t,s,i)}}}},contentSteeringController:class{constructor(t){this.hls=void 0,this.log=void 0,this.loader=null,this.uri=null,this.pathwayId=".",this.pathwayPriority=null,this.timeToLoad=300,this.reloadTimer=-1,this.updated=0,this.started=!1,this.enabled=!0,this.levels=null,this.audioTracks=null,this.subtitleTracks=null,this.penalizedPathways={},this.hls=t,this.log=A.log.bind(A,"[content-steering]:"),this.registerListeners()}registerListeners(){const t=this.hls;t.on(p.MANIFEST_LOADING,this.onManifestLoading,this),t.on(p.MANIFEST_LOADED,this.onManifestLoaded,this),t.on(p.MANIFEST_PARSED,this.onManifestParsed,this),t.on(p.ERROR,this.onError,this)}unregisterListeners(){const t=this.hls;t&&(t.off(p.MANIFEST_LOADING,this.onManifestLoading,this),t.off(p.MANIFEST_LOADED,this.onManifestLoaded,this),t.off(p.MANIFEST_PARSED,this.onManifestParsed,this),t.off(p.ERROR,this.onError,this))}startLoad(){if(this.started=!0,this.clearTimeout(),this.enabled&&this.uri){if(this.updated){const t=1e3*this.timeToLoad-(performance.now()-this.updated);if(t>0)return void this.scheduleRefresh(this.uri,t)}this.loadSteeringManifest(this.uri)}}stopLoad(){this.started=!1,this.loader&&(this.loader.destroy(),this.loader=null),this.clearTimeout()}clearTimeout(){-1!==this.reloadTimer&&(self.clearTimeout(this.reloadTimer),this.reloadTimer=-1)}destroy(){this.unregisterListeners(),this.stopLoad(),this.hls=null,this.levels=this.audioTracks=this.subtitleTracks=null}removeLevel(t){const e=this.levels;e&&(this.levels=e.filter((e=>e!==t)))}onManifestLoading(){this.stopLoad(),this.enabled=!0,this.timeToLoad=300,this.updated=0,this.uri=null,this.pathwayId=".",this.levels=this.audioTracks=this.subtitleTracks=null}onManifestLoaded(t,e){const{contentSteering:s}=e;null!==s&&(this.pathwayId=s.pathwayId,this.uri=s.uri,this.started&&this.startLoad())}onManifestParsed(t,e){this.audioTracks=e.audioTracks,this.subtitleTracks=e.subtitleTracks}onError(t,e){const{errorAction:s}=e;if((null==s?void 0:s.action)===Ss&&s.flags===bs){const t=this.levels;let i=this.pathwayPriority,r=this.pathwayId;if(e.context){const{groupId:s,pathwayId:i,type:n}=e.context;s&&t?r=this.getPathwayForGroupId(s,n,r):i&&(r=i)}r in this.penalizedPathways||(this.penalizedPathways[r]=performance.now()),!i&&t&&(i=t.reduce(((t,e)=>(-1===t.indexOf(e.pathwayId)&&t.push(e.pathwayId),t)),[])),i&&i.length>1&&(this.updatePathwayPriority(i),s.resolved=this.pathwayId!==r),s.resolved||A.warn(`Could not resolve ${e.details} ("${e.error.message}") with content-steering for Pathway: ${r} levels: ${t?t.length:t} priorities: ${JSON.stringify(i)} penalized: ${JSON.stringify(this.penalizedPathways)}`)}}filterParsedLevels(t){this.levels=t;let e=this.getLevelsForPathway(this.pathwayId);if(0===e.length){const s=t[0].pathwayId;this.log(`No levels found in Pathway ${this.pathwayId}. Setting initial Pathway to "${s}"`),e=this.getLevelsForPathway(s),this.pathwayId=s}return e.length!==t.length?(this.log(`Found ${e.length}/${t.length} levels in Pathway "${this.pathwayId}"`),e):t}getLevelsForPathway(t){return null===this.levels?[]:this.levels.filter((e=>t===e.pathwayId))}updatePathwayPriority(t){let e;this.pathwayPriority=t;const s=this.penalizedPathways,i=performance.now();Object.keys(s).forEach((t=>{i-s[t]>3e5&&delete s[t]}));for(let i=0;i<t.length;i++){const r=t[i];if(r in s)continue;if(r===this.pathwayId)return;const n=this.hls.nextLoadLevel,a=this.hls.levels[n];if(e=this.getLevelsForPathway(r),e.length>0){this.log(`Setting Pathway to "${r}"`),this.pathwayId=r,ds(e),this.hls.trigger(p.LEVELS_UPDATED,{levels:e});const t=this.hls.levels[n];a&&t&&this.levels&&(t.attrs["STABLE-VARIANT-ID"]!==a.attrs["STABLE-VARIANT-ID"]&&t.bitrate!==a.bitrate&&this.log(`Unstable Pathways change from bitrate ${a.bitrate} to ${t.bitrate}`),this.hls.nextLoadLevel=n);break}}}getPathwayForGroupId(t,e,s){const i=this.getLevelsForPathway(s).concat(this.levels||[]);for(let s=0;s<i.length;s++)if(e===ke&&i[s].hasAudioGroup(t)||e===we&&i[s].hasSubtitleGroup(t))return i[s].pathwayId;return s}clonePathways(t){const e=this.levels;if(!e)return;const s={},i={};t.forEach((t=>{const{ID:r,"BASE-ID":n,"URI-REPLACEMENT":a}=t;if(e.some((t=>t.pathwayId===r)))return;const o=this.getLevelsForPathway(n).map((t=>{const e=new k(t.attrs);e["PATHWAY-ID"]=r;const n=e.AUDIO&&`${e.AUDIO}_clone_${r}`,o=e.SUBTITLES&&`${e.SUBTITLES}_clone_${r}`;n&&(s[e.AUDIO]=n,e.AUDIO=n),o&&(i[e.SUBTITLES]=o,e.SUBTITLES=o);const l=fa(t.uri,e["STABLE-VARIANT-ID"],"PER-VARIANT-URIS",a),h=new es({attrs:e,audioCodec:t.audioCodec,bitrate:t.bitrate,height:t.height,name:t.name,url:l,videoCodec:t.videoCodec,width:t.width});if(t.audioGroups)for(let e=1;e<t.audioGroups.length;e++)h.addGroupId("audio",`${t.audioGroups[e]}_clone_${r}`);if(t.subtitleGroups)for(let e=1;e<t.subtitleGroups.length;e++)h.addGroupId("text",`${t.subtitleGroups[e]}_clone_${r}`);return h}));e.push(...o),ua(this.audioTracks,s,a,r),ua(this.subtitleTracks,i,a,r)}))}loadSteeringManifest(t){const e=this.hls.config,s=e.loader;let i;this.loader&&this.loader.destroy(),this.loader=new s(e);try{i=new self.URL(t)}catch(e){return this.enabled=!1,void this.log(`Failed to parse Steering Manifest URI: ${t}`)}if("data:"!==i.protocol){const t=0|(this.hls.bandwidthEstimate||e.abrEwmaDefaultEstimate);i.searchParams.set("_HLS_pathway",this.pathwayId),i.searchParams.set("_HLS_throughput",""+t)}const r={responseType:"json",url:i.href},n=e.steeringManifestLoadPolicy.default,a=n.errorRetry||n.timeoutRetry||{},o={loadPolicy:n,timeout:n.maxLoadTimeMs,maxRetry:a.maxNumRetry||0,retryDelay:a.retryDelayMs||0,maxRetryDelay:a.maxRetryDelayMs||0},l={onSuccess:(t,e,s,r)=>{this.log(`Loaded steering manifest: "${i}"`);const n=t.data;if(1!==n.VERSION)return void this.log(`Steering VERSION ${n.VERSION} not supported!`);this.updated=performance.now(),this.timeToLoad=n.TTL;const{"RELOAD-URI":a,"PATHWAY-CLONES":o,"PATHWAY-PRIORITY":l}=n;if(a)try{this.uri=new self.URL(a,i).href}catch(t){return this.enabled=!1,void this.log(`Failed to parse Steering Manifest RELOAD-URI: ${a}`)}this.scheduleRefresh(this.uri||s.url),o&&this.clonePathways(o);const h={steeringManifest:n,url:i.toString()};this.hls.trigger(p.STEERING_MANIFEST_LOADED,h),l&&this.updatePathwayPriority(l)},onError:(t,e,s,i)=>{if(this.log(`Error loading steering manifest: ${t.code} ${t.text} (${e.url})`),this.stopLoad(),410===t.code)return this.enabled=!1,void this.log(`Steering manifest ${e.url} no longer available`);let r=1e3*this.timeToLoad;if(429!==t.code)this.scheduleRefresh(this.uri||e.url,r);else{const t=this.loader;if("function"==typeof(null==t?void 0:t.getResponseHeader)){const e=t.getResponseHeader("Retry-After");e&&(r=1e3*parseFloat(e))}this.log(`Steering manifest ${e.url} rate limited`)}},onTimeout:(t,e,s)=>{this.log(`Timeout loading steering manifest (${e.url})`),this.scheduleRefresh(this.uri||e.url)}};this.log(`Requesting steering manifest: ${i}`),this.loader.load(r,o,l)}scheduleRefresh(t,e=1e3*this.timeToLoad){this.clearTimeout(),this.reloadTimer=self.setTimeout((()=>{var e;const s=null==(e=this.hls)?void 0:e.media;!s||s.ended?this.scheduleRefresh(t,1e3*this.timeToLoad):this.loadSteeringManifest(t)}),e)}}});function Aa(t){return t&&"object"==typeof t?Array.isArray(t)?t.map(Aa):Object.keys(t).reduce(((e,s)=>(e[s]=Aa(t[s]),e)),{}):t}function Ra(t){const e=t.loader;if(e!==va&&e!==ma)A.log("[config]: Custom loader detected, cannot enable progressive streaming"),t.progressive=!1;else{(function(){if(self.fetch&&self.AbortController&&self.ReadableStream&&self.Request)try{return new self.ReadableStream({}),!0}catch(t){}return!1})()&&(t.loader=va,t.progressive=!0,t.enableSoftwareAES=!0,A.log("[config]: Progressive streaming enabled, using FetchLoader"))}}let ba;class ka extends ws{constructor(t,e){super(t,"[level-controller]"),this._levels=[],this._firstLevel=-1,this._maxAutoLevel=-1,this._startLevel=void 0,this.currentLevel=null,this.currentLevelIndex=-1,this.manualLevelIndex=-1,this.steering=void 0,this.onParsedComplete=void 0,this.steering=e,this._registerListeners()}_registerListeners(){const{hls:t}=this;t.on(p.MANIFEST_LOADING,this.onManifestLoading,this),t.on(p.MANIFEST_LOADED,this.onManifestLoaded,this),t.on(p.LEVEL_LOADED,this.onLevelLoaded,this),t.on(p.LEVELS_UPDATED,this.onLevelsUpdated,this),t.on(p.FRAG_BUFFERED,this.onFragBuffered,this),t.on(p.ERROR,this.onError,this)}_unregisterListeners(){const{hls:t}=this;t.off(p.MANIFEST_LOADING,this.onManifestLoading,this),t.off(p.MANIFEST_LOADED,this.onManifestLoaded,this),t.off(p.LEVEL_LOADED,this.onLevelLoaded,this),t.off(p.LEVELS_UPDATED,this.onLevelsUpdated,this),t.off(p.FRAG_BUFFERED,this.onFragBuffered,this),t.off(p.ERROR,this.onError,this)}destroy(){this._unregisterListeners(),this.steering=null,this.resetLevels(),super.destroy()}stopLoad(){this._levels.forEach((t=>{t.loadError=0,t.fragmentError=0})),super.stopLoad()}resetLevels(){this._startLevel=void 0,this.manualLevelIndex=-1,this.currentLevelIndex=-1,this.currentLevel=null,this._levels=[],this._maxAutoLevel=-1}onManifestLoading(t,e){this.resetLevels()}onManifestLoaded(t,e){const s=this.hls.config.preferManagedMediaSource,i=[],r={},n={};let a=!1,o=!1,l=!1;e.levels.forEach((t=>{var e,h;const d=t.attrs;let{audioCodec:c,videoCodec:u}=t;-1!==(null==(e=c)?void 0:e.indexOf("mp4a.40.34"))&&(ba||(ba=/chrome|firefox/i.test(navigator.userAgent)),ba&&(t.audioCodec=c=void 0)),c&&(t.audioCodec=c=he(c,s)),0===(null==(h=u)?void 0:h.indexOf("avc1"))&&(u=t.videoCodec=function(t){const e=t.split(",");for(let t=0;t<e.length;t++){const s=e[t].split(".");if(s.length>2){let i=s.shift()+".";i+=parseInt(s.shift()).toString(16),i+=("000"+parseInt(s.shift()).toString(16)).slice(-4),e[t]=i}}return e.join(",")}(u));const{width:f,height:g,unknownCodecs:m}=t;if(a||(a=!(!f||!g)),o||(o=!!u),l||(l=!!c),null!=m&&m.length||c&&!se(c,"audio",s)||u&&!se(u,"video",s))return;const{CODECS:p,"FRAME-RATE":v,"HDCP-LEVEL":y,"PATHWAY-ID":E,RESOLUTION:T,"VIDEO-RANGE":S}=d,L=`${`${E||"."}-`}${t.bitrate}-${T}-${v}-${p}-${S}-${y}`;if(r[L])if(r[L].uri===t.url||t.attrs["PATHWAY-ID"])r[L].addGroupId("audio",d.AUDIO),r[L].addGroupId("text",d.SUBTITLES);else{const e=n[L]+=1;t.attrs["PATHWAY-ID"]=new Array(e+1).join(".");const s=new es(t);r[L]=s,i.push(s)}else{const e=new es(t);r[L]=e,n[L]=1,i.push(e)}})),this.filterAndSortMediaOptions(i,e,a,o,l)}filterAndSortMediaOptions(t,e,s,i,r){let n=[],a=[],o=t;if((s||i)&&r&&(o=o.filter((({videoCodec:t,videoRange:e,width:s,height:i})=>{return(!!t||!(!s||!i))&&(!!(r=e)&&Xe.indexOf(r)>-1);var r}))),0===o.length)return void Promise.resolve().then((()=>{if(this.hls){e.levels.length&&this.warn(`One or more CODECS in variant not supported: ${JSON.stringify(e.levels[0].attrs)}`);const t=new Error("no level with compatible codecs found in manifest");this.hls.trigger(p.ERROR,{type:v.MEDIA_ERROR,details:y.MANIFEST_INCOMPATIBLE_CODECS_ERROR,fatal:!0,url:e.url,error:t,reason:t.message})}}));if(e.audioTracks){const{preferManagedMediaSource:t}=this.hls.config;n=e.audioTracks.filter((e=>!e.audioCodec||se(e.audioCodec,"audio",t))),wa(n)}e.subtitles&&(a=e.subtitles,wa(a));const l=o.slice(0);o.sort(((t,e)=>{if(t.attrs["HDCP-LEVEL"]!==e.attrs["HDCP-LEVEL"])return(t.attrs["HDCP-LEVEL"]||"")>(e.attrs["HDCP-LEVEL"]||"")?1:-1;if(s&&t.height!==e.height)return t.height-e.height;if(t.frameRate!==e.frameRate)return t.frameRate-e.frameRate;if(t.videoRange!==e.videoRange)return Xe.indexOf(t.videoRange)-Xe.indexOf(e.videoRange);if(t.videoCodec!==e.videoCodec){const s=ne(t.videoCodec),i=ne(e.videoCodec);if(s!==i)return i-s}if(t.uri===e.uri&&t.codecSet!==e.codecSet){const s=ae(t.codecSet),i=ae(e.codecSet);if(s!==i)return i-s}return t.averageBitrate!==e.averageBitrate?t.averageBitrate-e.averageBitrate:0}));let h=l[0];if(this.steering&&(o=this.steering.filterParsedLevels(o),o.length!==l.length))for(let t=0;t<l.length;t++)if(l[t].pathwayId===o[0].pathwayId){h=l[t];break}this._levels=o;for(let t=0;t<o.length;t++)if(o[t]===h){var d;this._firstLevel=t;const e=h.bitrate,s=this.hls.bandwidthEstimate;if(this.log(`manifest loaded, ${o.length} level(s) found, first bitrate: ${e}`),void 0===(null==(d=this.hls.userConfig)?void 0:d.abrEwmaDefaultEstimate)){const t=Math.min(e,this.hls.config.abrEwmaDefaultEstimateMax);t>s&&s===La.abrEwmaDefaultEstimate&&(this.hls.bandwidthEstimate=t)}break}const c=r&&!i,u={levels:o,audioTracks:n,subtitleTracks:a,sessionData:e.sessionData,sessionKeys:e.sessionKeys,firstLevel:this._firstLevel,stats:e.stats,audio:r,video:i,altAudio:!c&&n.some((t=>!!t.url))};this.hls.trigger(p.MANIFEST_PARSED,u),(this.hls.config.autoStartLoad||this.hls.forceStartLoad)&&this.hls.startLoad(this.hls.config.startPosition)}get levels(){return 0===this._levels.length?null:this._levels}get level(){return this.currentLevelIndex}set level(t){const e=this._levels;if(0===e.length)return;if(t<0||t>=e.length){const s=new Error("invalid level idx"),i=t<0;if(this.hls.trigger(p.ERROR,{type:v.OTHER_ERROR,details:y.LEVEL_SWITCH_ERROR,level:t,fatal:i,error:s,reason:s.message}),i)return;t=Math.min(t,e.length-1)}const s=this.currentLevelIndex,i=this.currentLevel,r=i?i.attrs["PATHWAY-ID"]:void 0,n=e[t],a=n.attrs["PATHWAY-ID"];if(this.currentLevelIndex=t,this.currentLevel=n,s===t&&n.details&&i&&r===a)return;this.log(`Switching to level ${t} (${n.height?n.height+"p ":""}${n.videoRange?n.videoRange+" ":""}${n.codecSet?n.codecSet+" ":""}@${n.bitrate})${a?" with Pathway "+a:""} from level ${s}${r?" with Pathway "+r:""}`);const o={level:t,attrs:n.attrs,details:n.details,bitrate:n.bitrate,averageBitrate:n.averageBitrate,maxBitrate:n.maxBitrate,realBitrate:n.realBitrate,width:n.width,height:n.height,codecSet:n.codecSet,audioCodec:n.audioCodec,videoCodec:n.videoCodec,audioGroups:n.audioGroups,subtitleGroups:n.subtitleGroups,loaded:n.loaded,loadError:n.loadError,fragmentError:n.fragmentError,name:n.name,id:n.id,uri:n.uri,url:n.url,urlId:0,audioGroupIds:n.audioGroupIds,textGroupIds:n.textGroupIds};this.hls.trigger(p.LEVEL_SWITCHING,o);const l=n.details;if(!l||l.live){const t=this.switchParams(n.uri,null==i?void 0:i.details,l);this.loadPlaylist(t)}}get manualLevel(){return this.manualLevelIndex}set manualLevel(t){this.manualLevelIndex=t,void 0===this._startLevel&&(this._startLevel=t),-1!==t&&(this.level=t)}get firstLevel(){return this._firstLevel}set firstLevel(t){this._firstLevel=t}get startLevel(){if(void 0===this._startLevel){const t=this.hls.config.startLevel;return void 0!==t?t:this.hls.firstAutoLevel}return this._startLevel}set startLevel(t){this._startLevel=t}onError(t,e){!e.fatal&&e.context&&e.context.type===be&&e.context.level===this.level&&this.checkRetry(e)}onFragBuffered(t,{frag:e}){if(void 0!==e&&e.type===De){const t=e.elementaryStreams;if(!Object.keys(t).some((e=>!!t[e])))return;const s=this._levels[e.level];null!=s&&s.loadError&&(this.log(`Resetting level error count of ${s.loadError} on frag buffered`),s.loadError=0)}}onLevelLoaded(t,e){var s;const{level:i,details:r}=e,n=this._levels[i];var a;if(!n)return this.warn(`Invalid level index ${i}`),void(null!=(a=e.deliveryDirectives)&&a.skip&&(r.deltaUpdateFailed=!0));i===this.currentLevelIndex?(0===n.fragmentError&&(n.loadError=0),this.playlistLoaded(i,e,n.details)):null!=(s=e.deliveryDirectives)&&s.skip&&(r.deltaUpdateFailed=!0)}loadPlaylist(t){super.loadPlaylist();const e=this.currentLevelIndex,s=this.currentLevel;if(s&&this.shouldLoadPlaylist(s)){let i=s.uri;if(t)try{i=t.addDirectives(i)}catch(t){this.warn(`Could not construct new URL with HLS Delivery Directives: ${t}`)}const r=s.attrs["PATHWAY-ID"];this.log(`Loading level index ${e}${void 0!==(null==t?void 0:t.msn)?" at sn "+t.msn+" part "+t.part:""} with${r?" Pathway "+r:""} ${i}`),this.clearTimer(),this.hls.trigger(p.LEVEL_LOADING,{url:i,level:e,pathwayId:s.attrs["PATHWAY-ID"],id:0,deliveryDirectives:t||null})}}get nextLoadLevel(){return-1!==this.manualLevelIndex?this.manualLevelIndex:this.hls.nextAutoLevel}set nextLoadLevel(t){this.level=t,-1===this.manualLevelIndex&&(this.hls.nextAutoLevel=t)}removeLevel(t){var e;const s=this._levels.filter(((e,s)=>s!==t||(this.steering&&this.steering.removeLevel(e),e===this.currentLevel&&(this.currentLevel=null,this.currentLevelIndex=-1,e.details&&e.details.fragments.forEach((t=>t.level=-1))),!1)));ds(s),this._levels=s,this.currentLevelIndex>-1&&null!=(e=this.currentLevel)&&e.details&&(this.currentLevelIndex=this.currentLevel.details.fragments[0].level),this.hls.trigger(p.LEVELS_UPDATED,{levels:s})}onLevelsUpdated(t,{levels:e}){this._levels=e}checkMaxAutoUpdated(){const{autoLevelCapping:t,maxAutoLevel:e,maxHdcpLevel:s}=this.hls;this._maxAutoLevel!==e&&(this._maxAutoLevel=e,this.hls.trigger(p.MAX_AUTO_LEVEL_UPDATED,{autoLevelCapping:t,levels:this.levels,maxAutoLevel:e,minAutoLevel:this.hls.minAutoLevel,maxHdcpLevel:s}))}}function wa(t){const e={};t.forEach((t=>{const s=t.groupId||"";t.id=e[s]=e[s]||0,e[s]++}))}class Da{constructor(t){this.config=void 0,this.keyUriToKeyInfo={},this.emeController=null,this.config=t}abort(t){for(const s in this.keyUriToKeyInfo){const i=this.keyUriToKeyInfo[s].loader;if(i){var e;if(t&&t!==(null==(e=i.context)?void 0:e.frag.type))return;i.abort()}}}detach(){for(const t in this.keyUriToKeyInfo){const e=this.keyUriToKeyInfo[t];(e.mediaKeySessionContext||e.decryptdata.isCommonEncryption)&&delete this.keyUriToKeyInfo[t]}}destroy(){this.detach();for(const t in this.keyUriToKeyInfo){const e=this.keyUriToKeyInfo[t].loader;e&&e.destroy()}this.keyUriToKeyInfo={}}createKeyLoadError(t,e=y.KEY_LOAD_ERROR,s,i,r){return new ai({type:v.NETWORK_ERROR,details:e,fatal:!1,frag:t,response:r,error:s,networkDetails:i})}loadClear(t,e){if(this.emeController&&this.config.emeEnabled){const{sn:s,cc:i}=t;for(let t=0;t<e.length;t++){const r=e[t];if(i<=r.cc&&("initSegment"===s||"initSegment"===r.sn||s<r.sn)){this.emeController.selectKeySystemFormat(r).then((t=>{r.setKeyFormat(t)}));break}}}}load(t){return!t.decryptdata&&t.encrypted&&this.emeController?this.emeController.selectKeySystemFormat(t).then((e=>this.loadInternal(t,e))):this.loadInternal(t)}loadInternal(t,e){var s,i;e&&t.setKeyFormat(e);const r=t.decryptdata;if(!r){const s=new Error(e?`Expected frag.decryptdata to be defined after setting format ${e}`:"Missing decryption data on fragment in onKeyLoading");return Promise.reject(this.createKeyLoadError(t,y.KEY_LOAD_ERROR,s))}const n=r.uri;if(!n)return Promise.reject(this.createKeyLoadError(t,y.KEY_LOAD_ERROR,new Error(`Invalid key URI: "${n}"`)));let a=this.keyUriToKeyInfo[n];if(null!=(s=a)&&s.decryptdata.key)return r.key=a.decryptdata.key,Promise.resolve({frag:t,keyInfo:a});var o;if(null!=(i=a)&&i.keyLoadPromise)switch(null==(o=a.mediaKeySessionContext)?void 0:o.keyStatus){case void 0:case"status-pending":case"usable":case"usable-in-future":return a.keyLoadPromise.then((e=>(r.key=e.keyInfo.decryptdata.key,{frag:t,keyInfo:a})))}switch(a=this.keyUriToKeyInfo[n]={decryptdata:r,keyLoadPromise:null,loader:null,mediaKeySessionContext:null},r.method){case"ISO-23001-7":case"SAMPLE-AES":case"SAMPLE-AES-CENC":case"SAMPLE-AES-CTR":return"identity"===r.keyFormat?this.loadKeyHTTP(a,t):this.loadKeyEME(a,t);case"AES-128":return this.loadKeyHTTP(a,t);default:return Promise.reject(this.createKeyLoadError(t,y.KEY_LOAD_ERROR,new Error(`Key supplied with unsupported METHOD: "${r.method}"`)))}}loadKeyEME(t,e){const s={frag:e,keyInfo:t};if(this.emeController&&this.config.emeEnabled){const e=this.emeController.loadKey(s);if(e)return(t.keyLoadPromise=e.then((e=>(t.mediaKeySessionContext=e,s)))).catch((e=>{throw t.keyLoadPromise=null,e}))}return Promise.resolve(s)}loadKeyHTTP(t,e){const s=this.config,i=new(0,s.loader)(s);return e.keyLoader=t.loader=i,t.keyLoadPromise=new Promise(((r,n)=>{const a={keyInfo:t,frag:e,responseType:"arraybuffer",url:t.decryptdata.uri},o=s.keyLoadPolicy.default,l={loadPolicy:o,timeout:o.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0},d={onSuccess:(t,e,s,i)=>{const{frag:a,keyInfo:o,url:l}=s;if(!a.decryptdata||o!==this.keyUriToKeyInfo[l])return n(this.createKeyLoadError(a,y.KEY_LOAD_ERROR,new Error("after key load, decryptdata unset or changed"),i));o.decryptdata.key=a.decryptdata.key=new Uint8Array(t.data),a.keyLoader=null,o.loader=null,r({frag:a,keyInfo:o})},onError:(t,s,i,r)=>{this.resetLoader(s),n(this.createKeyLoadError(e,y.KEY_LOAD_ERROR,new Error(`HTTP Error ${t.code} loading key ${t.text}`),i,h({url:a.url,data:void 0},t)))},onTimeout:(t,s,i)=>{this.resetLoader(s),n(this.createKeyLoadError(e,y.KEY_LOAD_TIMEOUT,new Error("key loading timed out"),i))},onAbort:(t,s,i)=>{this.resetLoader(s),n(this.createKeyLoadError(e,y.INTERNAL_ABORTED,new Error("key loading aborted"),i))}};i.load(a,l,d)}))}resetLoader(t){const{frag:e,keyInfo:s,url:i}=t,r=s.loader;e.keyLoader===r&&(e.keyLoader=null,s.loader=null),delete this.keyUriToKeyInfo[i],r&&r.destroy()}}function Ia(){return self.SourceBuffer||self.WebKitSourceBuffer}function Ca(){if(!te())return!1;const t=Ia();return!t||t.prototype&&"function"==typeof t.prototype.appendBuffer&&"function"==typeof t.prototype.remove}class _a{constructor(t,e,s,i){this.config=void 0,this.media=null,this.fragmentTracker=void 0,this.hls=void 0,this.nudgeRetry=0,this.stallReported=!1,this.stalled=null,this.moved=!1,this.seeking=!1,this.config=t,this.media=e,this.fragmentTracker=s,this.hls=i}destroy(){this.media=null,this.hls=this.fragmentTracker=null}poll(t,e){const{config:s,media:i,stalled:r}=this;if(null===i)return;const{currentTime:n,seeking:a}=i,o=this.seeking&&!a,l=!this.seeking&&a;if(this.seeking=a,n!==t){if(this.moved=!0,a||(this.nudgeRetry=0),null!==r){if(this.stallReported){const t=self.performance.now()-r;A.warn(`playback not stuck anymore @${n}, after ${Math.round(t)}ms`),this.stallReported=!1}this.stalled=null}return}if(l||o)return void(this.stalled=null);if(i.paused&&!a||i.ended||0===i.playbackRate||!Xs.getBuffered(i).length)return void(this.nudgeRetry=0);const h=Xs.bufferInfo(i,n,0),d=h.nextStart||0;if(a){const t=h.len>2,s=!d||e&&e.start<=n||d-n>2&&!this.fragmentTracker.getPartialFragment(n);if(t||s)return;this.moved=!1}if(!this.moved&&null!==this.stalled){var c;if(!(h.len>0)&&!d)return;const t=Math.max(d,h.start||0)-n,e=this.hls.levels?this.hls.levels[this.hls.currentLevel]:null,s=(null==e||null==(c=e.details)?void 0:c.live)?2*e.details.targetduration:2,r=this.fragmentTracker.getPartialFragment(n);if(t>0&&(t<=s||r))return void(i.paused||this._trySkipBufferHole(r))}const u=self.performance.now();if(null===r)return void(this.stalled=u);const f=u-r;if(!a&&f>=250&&(this._reportStall(h),!this.media))return;const g=Xs.bufferInfo(i,n,s.maxBufferHole);this._tryFixBufferStall(g,f)}_tryFixBufferStall(t,e){const{config:s,fragmentTracker:i,media:r}=this;if(null===r)return;const n=r.currentTime,a=i.getPartialFragment(n);if(a){if(this._trySkipBufferHole(a)||!this.media)return}(t.len>s.maxBufferHole||t.nextStart&&t.nextStart-n<s.maxBufferHole)&&e>1e3*s.highBufferWatchdogPeriod&&(A.warn("Trying to nudge playhead over buffer-hole"),this.stalled=null,this._tryNudgeBuffer())}_reportStall(t){const{hls:e,media:s,stallReported:i}=this;if(!i&&s){this.stallReported=!0;const i=new Error(`Playback stalling at @${s.currentTime} due to low buffer (${JSON.stringify(t)})`);A.warn(i.message),e.trigger(p.ERROR,{type:v.MEDIA_ERROR,details:y.BUFFER_STALLED_ERROR,fatal:!1,error:i,buffer:t.len})}}_trySkipBufferHole(t){const{config:e,hls:s,media:i}=this;if(null===i)return 0;const r=i.currentTime,n=Xs.bufferInfo(i,r,0),a=r<n.start?n.start:n.nextStart;if(a){const o=n.len<=e.maxBufferHole,l=n.len>0&&n.len<1&&i.readyState<3,h=a-r;if(h>0&&(o||l)){if(h>e.maxBufferHole){const{fragmentTracker:e}=this;let s=!1;if(0===r){const t=e.getAppendedFrag(0,De);t&&a<t.end&&(s=!0)}if(!s){const s=t||e.getAppendedFrag(r,De);if(s){let t=!1,i=s.end;for(;i<a;){const s=e.getPartialFragment(i);if(!s){t=!0;break}i+=s.duration}if(t)return 0}}}const n=Math.max(a+.05,r+.1);if(A.warn(`skipping hole, adjusting currentTime from ${r} to ${n}`),this.moved=!0,this.stalled=null,i.currentTime=n,t&&!t.gap){const e=new Error(`fragment loaded with buffer holes, seeking from ${r} to ${n}`);s.trigger(p.ERROR,{type:v.MEDIA_ERROR,details:y.BUFFER_SEEK_OVER_HOLE,fatal:!1,error:e,reason:e.message,frag:t})}return n}}return 0}_tryNudgeBuffer(){const{config:t,hls:e,media:s,nudgeRetry:i}=this;if(null===s)return;const r=s.currentTime;if(this.nudgeRetry++,i<t.nudgeMaxRetry){const n=r+(i+1)*t.nudgeOffset,a=new Error(`Nudging 'currentTime' from ${r} to ${n}`);A.warn(a.message),s.currentTime=n,e.trigger(p.ERROR,{type:v.MEDIA_ERROR,details:y.BUFFER_NUDGE_ON_STALL,error:a,fatal:!1})}else{const s=new Error(`Playhead still not moving while enough data buffered @${r} after ${t.nudgeMaxRetry} nudges`);A.error(s.message),e.trigger(p.ERROR,{type:v.MEDIA_ERROR,details:y.BUFFER_STALLED_ERROR,error:s,fatal:!0})}}}class xa extends Ri{constructor(t,e,s){super(t,e,s,"[stream-controller]",De),this.audioCodecSwap=!1,this.gapController=null,this.level=-1,this._forceStartLoad=!1,this.altAudio=!1,this.audioOnly=!1,this.fragPlaying=null,this.onvplaying=null,this.onvseeked=null,this.fragLastKbps=0,this.couldBacktrack=!1,this.backtrackFragment=null,this.audioCodecSwitch=!1,this.videoBuffer=null,this._registerListeners()}_registerListeners(){const{hls:t}=this;t.on(p.MEDIA_ATTACHED,this.onMediaAttached,this),t.on(p.MEDIA_DETACHING,this.onMediaDetaching,this),t.on(p.MANIFEST_LOADING,this.onManifestLoading,this),t.on(p.MANIFEST_PARSED,this.onManifestParsed,this),t.on(p.LEVEL_LOADING,this.onLevelLoading,this),t.on(p.LEVEL_LOADED,this.onLevelLoaded,this),t.on(p.FRAG_LOAD_EMERGENCY_ABORTED,this.onFragLoadEmergencyAborted,this),t.on(p.ERROR,this.onError,this),t.on(p.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),t.on(p.AUDIO_TRACK_SWITCHED,this.onAudioTrackSwitched,this),t.on(p.BUFFER_CREATED,this.onBufferCreated,this),t.on(p.BUFFER_FLUSHED,this.onBufferFlushed,this),t.on(p.LEVELS_UPDATED,this.onLevelsUpdated,this),t.on(p.FRAG_BUFFERED,this.onFragBuffered,this)}_unregisterListeners(){const{hls:t}=this;t.off(p.MEDIA_ATTACHED,this.onMediaAttached,this),t.off(p.MEDIA_DETACHING,this.onMediaDetaching,this),t.off(p.MANIFEST_LOADING,this.onManifestLoading,this),t.off(p.MANIFEST_PARSED,this.onManifestParsed,this),t.off(p.LEVEL_LOADED,this.onLevelLoaded,this),t.off(p.FRAG_LOAD_EMERGENCY_ABORTED,this.onFragLoadEmergencyAborted,this),t.off(p.ERROR,this.onError,this),t.off(p.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),t.off(p.AUDIO_TRACK_SWITCHED,this.onAudioTrackSwitched,this),t.off(p.BUFFER_CREATED,this.onBufferCreated,this),t.off(p.BUFFER_FLUSHED,this.onBufferFlushed,this),t.off(p.LEVELS_UPDATED,this.onLevelsUpdated,this),t.off(p.FRAG_BUFFERED,this.onFragBuffered,this)}onHandlerDestroying(){this._unregisterListeners(),super.onHandlerDestroying()}startLoad(t){if(this.levels){const{lastCurrentTime:e,hls:s}=this;if(this.stopLoad(),this.setInterval(100),this.level=-1,!this.startFragRequested){let t=s.startLevel;-1===t&&(s.config.testBandwidth&&this.levels.length>1?(t=0,this.bitrateTest=!0):t=s.firstAutoLevel),s.nextLoadLevel=t,this.level=s.loadLevel,this.loadedmetadata=!1}e>0&&-1===t&&(this.log(`Override startPosition with lastCurrentTime @${e.toFixed(3)}`),t=e),this.state=fi,this.nextLoadPosition=this.startPosition=this.lastCurrentTime=t,this.tick()}else this._forceStartLoad=!0,this.state=ui}stopLoad(){this._forceStartLoad=!1,super.stopLoad()}doTick(){switch(this.state){case Ai:{const{levels:t,level:e}=this,s=null==t?void 0:t[e],i=null==s?void 0:s.details;if(i&&(!i.live||this.levelLastLoaded===s)){if(this.waitForCdnTuneIn(i))break;this.state=fi;break}if(this.hls.nextLoadLevel!==this.level){this.state=fi;break}break}case pi:{var t;const e=self.performance.now(),s=this.retryDate;if(!s||e>=s||null!=(t=this.media)&&t.seeking){const{levels:t,level:e}=this,s=null==t?void 0:t[e];this.resetStartWhenNotLoaded(s||null),this.state=fi}}}this.state===fi&&this.doTickIdle(),this.onTickEnd()}onTickEnd(){super.onTickEnd(),this.checkBuffer(),this.checkFragmentChanged()}doTickIdle(){const{hls:t,levelLastLoaded:e,levels:s,media:i}=this;if(null===e||!i&&(this.startFragRequested||!t.config.startFragPrefetch))return;if(this.altAudio&&this.audioOnly)return;const r=t.nextLoadLevel;if(null==s||!s[r])return;const n=s[r],a=this.getMainFwdBufferInfo();if(null===a)return;const o=this.getLevelDetails();if(o&&this._streamEnded(a,o)){const t={};return this.altAudio&&(t.type="video"),this.hls.trigger(p.BUFFER_EOS,t),void(this.state=Ti)}t.loadLevel!==r&&-1===t.manualLevel&&this.log(`Adapting to level ${r} from level ${this.level}`),this.level=t.nextLoadLevel=r;const l=n.details;if(!l||this.state===Ai||l.live&&this.levelLastLoaded!==n)return this.level=r,void(this.state=Ai);const h=a.len,d=this.getMaxBufferLength(n.maxBitrate);if(h>=d)return;this.backtrackFragment&&this.backtrackFragment.start>a.end&&(this.backtrackFragment=null);const c=this.backtrackFragment?this.backtrackFragment.start:a.end;let u=this.getNextFragment(c,l);if(this.couldBacktrack&&!this.fragPrevious&&u&&"initSegment"!==u.sn&&this.fragmentTracker.getState(u)!==Vs){var f;const t=(null!=(f=this.backtrackFragment)?f:u).sn-l.startSN,e=l.fragments[t-1];e&&u.cc===e.cc&&(u=e,this.fragmentTracker.removeFragment(e))}else this.backtrackFragment&&a.len&&(this.backtrackFragment=null);if(u&&this.isLoopLoading(u,c)){if(!u.gap){const t=this.audioOnly&&!this.altAudio?C:_,e=(t===_?this.videoBuffer:this.mediaBuffer)||this.media;e&&this.afterBufferFlushed(e,t,De)}u=this.getNextFragmentLoopLoading(u,l,a,De,d)}u&&(!u.initSegment||u.initSegment.data||this.bitrateTest||(u=u.initSegment),this.loadFragment(u,n,c))}loadFragment(t,e,s){const i=this.fragmentTracker.getState(t);this.fragCurrent=t,i===Gs||i===Hs?"initSegment"===t.sn?this._loadInitSegment(t,e):this.bitrateTest?(this.log(`Fragment ${t.sn} of level ${t.level} is being downloaded to test bitrate and will not be buffered`),this._loadBitrateTestFrag(t,e)):(this.startFragRequested=!0,super.loadFragment(t,e,s)):this.clearTrackerIfNeeded(t)}getBufferedFrag(t){return this.fragmentTracker.getBufferedFrag(t,De)}followingBufferedFrag(t){return t?this.getBufferedFrag(t.end+.5):null}immediateLevelSwitch(){this.abortCurrentFrag(),this.flushMainBuffer(0,Number.POSITIVE_INFINITY)}nextLevelSwitch(){const{levels:t,media:e}=this;if(null!=e&&e.readyState){let s;const i=this.getAppendedFrag(e.currentTime);i&&i.start>1&&this.flushMainBuffer(0,i.start-1);const r=this.getLevelDetails();if(null!=r&&r.live){const t=this.getMainFwdBufferInfo();if(!t||t.len<2*r.targetduration)return}if(!e.paused&&t){const e=t[this.hls.nextLoadLevel],i=this.fragLastKbps;s=i&&this.fragCurrent?this.fragCurrent.duration*e.maxBitrate/(1e3*i)+1:0}else s=0;const n=this.getBufferedFrag(e.currentTime+s);if(n){const t=this.followingBufferedFrag(n);if(t){this.abortCurrentFrag();const e=t.maxStartPTS?t.maxStartPTS:t.start,s=t.duration,i=Math.max(n.end,e+Math.min(Math.max(s-this.config.maxFragLookUpTolerance,s*(this.couldBacktrack?.5:.125)),s*(this.couldBacktrack?.75:.25)));this.flushMainBuffer(i,Number.POSITIVE_INFINITY)}}}}abortCurrentFrag(){const t=this.fragCurrent;switch(this.fragCurrent=null,this.backtrackFragment=null,t&&(t.abortRequests(),this.fragmentTracker.removeFragment(t)),this.state){case gi:case mi:case pi:case yi:case Ei:this.state=fi}this.nextLoadPosition=this.getLoadPosition()}flushMainBuffer(t,e){super.flushMainBuffer(t,e,this.altAudio?"video":null)}onMediaAttached(t,e){super.onMediaAttached(t,e);const s=e.media;this.onvplaying=this.onMediaPlaying.bind(this),this.onvseeked=this.onMediaSeeked.bind(this),s.addEventListener("playing",this.onvplaying),s.addEventListener("seeked",this.onvseeked),this.gapController=new _a(this.config,s,this.fragmentTracker,this.hls)}onMediaDetaching(){const{media:t}=this;t&&this.onvplaying&&this.onvseeked&&(t.removeEventListener("playing",this.onvplaying),t.removeEventListener("seeked",this.onvseeked),this.onvplaying=this.onvseeked=null,this.videoBuffer=null),this.fragPlaying=null,this.gapController&&(this.gapController.destroy(),this.gapController=null),super.onMediaDetaching()}onMediaPlaying(){this.tick()}onMediaSeeked(){const t=this.media,e=t?t.currentTime:null;f(e)&&this.log(`Media seeked to ${e.toFixed(3)}`);const s=this.getMainFwdBufferInfo();null!==s&&0!==s.len?this.tick():this.warn(`Main forward buffer length on "seeked" event ${s?s.len:"empty"})`)}onManifestLoading(){this.log("Trigger BUFFER_RESET"),this.hls.trigger(p.BUFFER_RESET,void 0),this.fragmentTracker.removeAllFragments(),this.couldBacktrack=!1,this.startPosition=this.lastCurrentTime=this.fragLastKbps=0,this.levels=this.fragPlaying=this.backtrackFragment=this.levelLastLoaded=null,this.altAudio=this.audioOnly=this.startFragRequested=!1}onManifestParsed(t,e){let s=!1,i=!1;e.levels.forEach((t=>{const e=t.audioCodec;e&&(s=s||-1!==e.indexOf("mp4a.40.2"),i=i||-1!==e.indexOf("mp4a.40.5"))})),this.audioCodecSwitch=s&&i&&!function(){var t;const e=Ia();return"function"==typeof(null==e||null==(t=e.prototype)?void 0:t.changeType)}(),this.audioCodecSwitch&&this.log("Both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC"),this.levels=e.levels,this.startFragRequested=!1}onLevelLoading(t,e){const{levels:s}=this;if(!s||this.state!==fi)return;const i=s[e.level];(!i.details||i.details.live&&this.levelLastLoaded!==i||this.waitForCdnTuneIn(i.details))&&(this.state=Ai)}onLevelLoaded(t,e){var s;const{levels:i}=this,r=e.level,n=e.details,a=n.totalduration;if(!i)return void this.warn(`Levels were reset while loading level ${r}`);this.log(`Level ${r} loaded [${n.startSN},${n.endSN}]${n.lastPartSn?`[part-${n.lastPartSn}-${n.lastPartIndex}]`:""}, cc [${n.startCC}, ${n.endCC}] duration:${a}`);const o=i[r],l=this.fragCurrent;!l||this.state!==mi&&this.state!==pi||l.level!==e.level&&l.loader&&this.abortCurrentFrag();let h=0;if(n.live||null!=(s=o.details)&&s.live){var d;if(this.checkLiveUpdate(n),n.deltaUpdateFailed)return;h=this.alignPlaylists(n,o.details,null==(d=this.levelLastLoaded)?void 0:d.details)}if(o.details=n,this.levelLastLoaded=o,this.hls.trigger(p.LEVEL_UPDATED,{details:n,level:r}),this.state===Ai){if(this.waitForCdnTuneIn(n))return;this.state=fi}this.startFragRequested?n.live&&this.synchronizeToLiveEdge(n):this.setStartPosition(n,h),this.tick()}_handleFragmentLoadProgress(t){var e;const{frag:s,part:i,payload:r}=t,{levels:n}=this;if(!n)return void this.warn(`Levels were reset while fragment load was in progress. Fragment ${s.sn} of level ${s.level} will not be buffered`);const a=n[s.level],o=a.details;if(!o)return this.warn(`Dropping fragment ${s.sn} of level ${s.level} after level details were reset`),void this.fragmentTracker.removeFragment(s);const l=a.videoCodec,h=o.PTSKnown||!o.live,d=null==(e=s.initSegment)?void 0:e.data,c=this._getAudioCodec(a),u=this.transmuxer=this.transmuxer||new _r(this.hls,De,this._handleTransmuxComplete.bind(this),this._handleTransmuxerFlush.bind(this)),f=i?i.index:-1,g=-1!==f,m=new zs(s.level,s.sn,s.stats.chunkCount,r.byteLength,f,g),p=this.initPTS[s.cc];u.push(r,d,c,l,s,i,o.totalduration,h,m,p)}onAudioTrackSwitching(t,e){const s=this.altAudio;if(!!!e.url){if(this.mediaBuffer!==this.media){this.log("Switching on main audio, use media.buffered to schedule main fragment loading"),this.mediaBuffer=this.media;const t=this.fragCurrent;t&&(this.log("Switching to main audio track, cancel main fragment load"),t.abortRequests(),this.fragmentTracker.removeFragment(t)),this.resetTransmuxer(),this.resetLoadingState()}else this.audioOnly&&this.resetTransmuxer();const t=this.hls;s&&(t.trigger(p.BUFFER_FLUSHING,{startOffset:0,endOffset:Number.POSITIVE_INFINITY,type:null}),this.fragmentTracker.removeAllFragments()),t.trigger(p.AUDIO_TRACK_SWITCHED,e)}}onAudioTrackSwitched(t,e){const s=e.id,i=!!this.hls.audioTracks[s].url;if(i){const t=this.videoBuffer;t&&this.mediaBuffer!==t&&(this.log("Switching on alternate audio, use video.buffered to schedule main fragment loading"),this.mediaBuffer=t)}this.altAudio=i,this.tick()}onBufferCreated(t,e){const s=e.tracks;let i,r,n=!1;for(const t in s){const e=s[t];if("main"===e.id){if(r=t,i=e,"video"===t){const e=s[t];e&&(this.videoBuffer=e.buffer)}}else n=!0}n&&i?(this.log(`Alternate track found, use ${r}.buffered to schedule main fragment loading`),this.mediaBuffer=i.buffer):this.mediaBuffer=this.media}onFragBuffered(t,e){const{frag:s,part:i}=e;if(s&&s.type!==De)return;if(this.fragContextChanged(s))return this.warn(`Fragment ${s.sn}${i?" p: "+i.index:""} of level ${s.level} finished buffering, but was aborted. state: ${this.state}`),void(this.state===Ei&&(this.state=fi));const r=i?i.stats:s.stats;this.fragLastKbps=Math.round(8*r.total/(r.buffering.end-r.loading.first)),"initSegment"!==s.sn&&(this.fragPrevious=s),this.fragBufferedComplete(s,i)}onError(t,e){var s;if(e.fatal)this.state=Si;else switch(e.details){case y.FRAG_GAP:case y.FRAG_PARSING_ERROR:case y.FRAG_DECRYPT_ERROR:case y.FRAG_LOAD_ERROR:case y.FRAG_LOAD_TIMEOUT:case y.KEY_LOAD_ERROR:case y.KEY_LOAD_TIMEOUT:this.onFragmentOrKeyLoadError(De,e);break;case y.LEVEL_LOAD_ERROR:case y.LEVEL_LOAD_TIMEOUT:case y.LEVEL_PARSING_ERROR:e.levelRetry||this.state!==Ai||(null==(s=e.context)?void 0:s.type)!==be||(this.state=fi);break;case y.BUFFER_APPEND_ERROR:case y.BUFFER_FULL_ERROR:if(!e.parent||"main"!==e.parent)return;if(e.details===y.BUFFER_APPEND_ERROR)return void this.resetLoadingState();this.reduceLengthAndFlushBuffer(e)&&this.flushMainBuffer(0,Number.POSITIVE_INFINITY);break;case y.INTERNAL_EXCEPTION:this.recoverWorkerError(e)}}checkBuffer(){const{media:t,gapController:e}=this;if(t&&e&&t.readyState){if(this.loadedmetadata||!Xs.getBuffered(t).length){const t=this.state!==fi?this.fragCurrent:null;e.poll(this.lastCurrentTime,t)}this.lastCurrentTime=t.currentTime}}onFragLoadEmergencyAborted(){this.state=fi,this.loadedmetadata||(this.startFragRequested=!1,this.nextLoadPosition=this.startPosition),this.tickImmediate()}onBufferFlushed(t,{type:e}){if(e!==C||this.audioOnly&&!this.altAudio){const t=(e===_?this.videoBuffer:this.mediaBuffer)||this.media;this.afterBufferFlushed(t,e,De),this.tick()}}onLevelsUpdated(t,e){this.level>-1&&this.fragCurrent&&(this.level=this.fragCurrent.level),this.levels=e.levels}swapAudioCodec(){this.audioCodecSwap=!this.audioCodecSwap}seekToStartPos(){const{media:t}=this;if(!t)return;const e=t.currentTime;let s=this.startPosition;if(s>=0&&e<s){if(t.seeking)return void this.log(`could not seek to ${s}, already seeking at ${e}`);const i=Xs.getBuffered(t),r=(i.length?i.start(0):0)-s;r>0&&(r<this.config.maxBufferHole||r<this.config.maxFragLookUpTolerance)&&(this.log(`adjusting start position by ${r} to match buffer start`),s+=r,this.startPosition=s),this.log(`seek to target start position ${s} from current time ${e}`),t.currentTime=s}}_getAudioCodec(t){let e=this.config.defaultAudioCodec||t.audioCodec;return this.audioCodecSwap&&e&&(this.log("Swapping audio codec"),e=-1!==e.indexOf("mp4a.40.5")?"mp4a.40.2":"mp4a.40.5"),e}_loadBitrateTestFrag(t,e){t.bitrateTest=!0,this._doFragLoad(t,e).then((s=>{const{hls:i}=this;if(!s||this.fragContextChanged(t))return;e.fragmentError=0,this.state=fi,this.startFragRequested=!1,this.bitrateTest=!1;const r=t.stats;r.parsing.start=r.parsing.end=r.buffering.start=r.buffering.end=self.performance.now(),i.trigger(p.FRAG_LOADED,s),t.bitrateTest=!1}))}_handleTransmuxComplete(t){var e;const s="main",{hls:i}=this,{remuxResult:r,chunkMeta:n}=t,a=this.getCurrentContext(n);if(!a)return void this.resetWhenMissingContext(n);const{frag:o,part:l,level:h}=a,{video:d,text:c,id3:u,initSegment:g}=r,{details:m}=h,v=this.altAudio?void 0:r.audio;if(this.fragContextChanged(o))this.fragmentTracker.removeFragment(o);else{if(this.state=yi,g){if(null!=g&&g.tracks){const t=o.initSegment||o;this._bufferInitSegment(h,g.tracks,t,n),i.trigger(p.FRAG_PARSING_INIT_SEGMENT,{frag:t,id:s,tracks:g.tracks})}const t=g.initPTS,e=g.timescale;f(t)&&(this.initPTS[o.cc]={baseTime:t,timescale:e},i.trigger(p.INIT_PTS_FOUND,{frag:o,id:s,initPTS:t,timescale:e}))}if(d&&m&&"initSegment"!==o.sn){const t=m.fragments[o.sn-1-m.startSN],e=o.sn===m.startSN,s=!t||o.cc>t.cc;if(!1!==r.independent){const{startPTS:t,endPTS:i,startDTS:r,endDTS:a}=d;if(l)l.elementaryStreams[d.type]={startPTS:t,endPTS:i,startDTS:r,endDTS:a};else if(d.firstKeyFrame&&d.independent&&1===n.id&&!s&&(this.couldBacktrack=!0),d.dropped&&d.independent){const r=this.getMainFwdBufferInfo(),n=(r?r.end:this.getLoadPosition())+this.config.maxBufferHole,l=d.firstKeyFramePTS?d.firstKeyFramePTS:t;if(!e&&n<l-this.config.maxBufferHole&&!s)return void this.backtrack(o);s&&(o.gap=!0),o.setElementaryStreamInfo(d.type,o.start,i,o.start,a,!0)}else e&&t>2&&(o.gap=!0);o.setElementaryStreamInfo(d.type,t,i,r,a),this.backtrackFragment&&(this.backtrackFragment=o),this.bufferFragmentData(d,o,l,n,e||s)}else{if(!e&&!s)return void this.backtrack(o);o.gap=!0}}if(v){const{startPTS:t,endPTS:e,startDTS:s,endDTS:i}=v;l&&(l.elementaryStreams[C]={startPTS:t,endPTS:e,startDTS:s,endDTS:i}),o.setElementaryStreamInfo(C,t,e,s,i),this.bufferFragmentData(v,o,l,n)}if(m&&null!=u&&null!=(e=u.samples)&&e.length){const t={id:s,frag:o,details:m,samples:u.samples};i.trigger(p.FRAG_PARSING_METADATA,t)}if(m&&c){const t={id:s,frag:o,details:m,samples:c.samples};i.trigger(p.FRAG_PARSING_USERDATA,t)}}}_bufferInitSegment(t,e,s,i){if(this.state!==yi)return;this.audioOnly=!!e.audio&&!e.video,this.altAudio&&!this.audioOnly&&delete e.audio;const{audio:r,video:n,audiovideo:a}=e;if(r){let e=t.audioCodec;const s=navigator.userAgent.toLowerCase();if(this.audioCodecSwitch){e&&(e=-1!==e.indexOf("mp4a.40.5")?"mp4a.40.2":"mp4a.40.5");const t=r.metadata;t&&"channelCount"in t&&1!==(t.channelCount||1)&&-1===s.indexOf("firefox")&&(e="mp4a.40.5")}e&&-1!==e.indexOf("mp4a.40.5")&&-1!==s.indexOf("android")&&"audio/mpeg"!==r.container&&(e="mp4a.40.2",this.log(`Android: force audio codec to ${e}`)),t.audioCodec&&t.audioCodec!==e&&this.log(`Swapping manifest audio codec "${t.audioCodec}" for "${e}"`),r.levelCodec=e,r.id="main",this.log(`Init audio buffer, container:${r.container}, codecs[selected/level/parsed]=[${e||""}/${t.audioCodec||""}/${r.codec}]`)}n&&(n.levelCodec=t.videoCodec,n.id="main",this.log(`Init video buffer, container:${n.container}, codecs[level/parsed]=[${t.videoCodec||""}/${n.codec}]`)),a&&this.log(`Init audiovideo buffer, container:${a.container}, codecs[level/parsed]=[${t.codecs}/${a.codec}]`),this.hls.trigger(p.BUFFER_CODECS,e),Object.keys(e).forEach((t=>{const r=e[t].initSegment;null!=r&&r.byteLength&&this.hls.trigger(p.BUFFER_APPENDING,{type:t,data:r,frag:s,part:null,chunkMeta:i,parent:s.type})})),this.tickImmediate()}getMainFwdBufferInfo(){return this.getFwdBufferInfo(this.mediaBuffer?this.mediaBuffer:this.media,De)}backtrack(t){this.couldBacktrack=!0,this.backtrackFragment=t,this.resetTransmuxer(),this.flushBufferGap(t),this.fragmentTracker.removeFragment(t),this.fragPrevious=null,this.nextLoadPosition=t.start,this.state=fi}checkFragmentChanged(){const t=this.media;let e=null;if(t&&t.readyState>1&&!1===t.seeking){const s=t.currentTime;if(Xs.isBuffered(t,s)?e=this.getAppendedFrag(s):Xs.isBuffered(t,s+.1)&&(e=this.getAppendedFrag(s+.1)),e){this.backtrackFragment=null;const t=this.fragPlaying,s=e.level;t&&e.sn===t.sn&&t.level===s||(this.fragPlaying=e,this.hls.trigger(p.FRAG_CHANGED,{frag:e}),t&&t.level===s||this.hls.trigger(p.LEVEL_SWITCHED,{level:s}))}}}get nextLevel(){const t=this.nextBufferedFrag;return t?t.level:-1}get currentFrag(){const t=this.media;return t?this.fragPlaying||this.getAppendedFrag(t.currentTime):null}get currentProgramDateTime(){const t=this.media;if(t){const e=t.currentTime,s=this.currentFrag;if(s&&f(e)&&f(s.programDateTime)){const t=s.programDateTime+1e3*(e-s.start);return new Date(t)}}return null}get currentLevel(){const t=this.currentFrag;return t?t.level:-1}get nextBufferedFrag(){const t=this.currentFrag;return t?this.followingBufferedFrag(t):null}get forceStartLoad(){return this._forceStartLoad}}class Pa{static get version(){return"1.5.15"}static isMSESupported(){return Ca()}static isSupported(){return function(){if(!Ca())return!1;const t=te();return"function"==typeof(null==t?void 0:t.isTypeSupported)&&(["avc1.42E01E,mp4a.40.2","av01.0.01M.08","vp09.00.50.08"].some((e=>t.isTypeSupported(re(e,"video"))))||["mp4a.40.2","fLaC"].some((e=>t.isTypeSupported(re(e,"audio")))))}()}static getMediaSource(){return te()}static get Events(){return p}static get ErrorTypes(){return v}static get ErrorDetails(){return y}static get DefaultConfig(){return Pa.defaultConfig?Pa.defaultConfig:La}static set DefaultConfig(t){Pa.defaultConfig=t}constructor(t={}){this.config=void 0,this.userConfig=void 0,this.coreComponents=void 0,this.networkControllers=void 0,this.started=!1,this._emitter=new Cr,this._autoLevelCapping=-1,this._maxHdcpLevel=null,this.abrController=void 0,this.bufferController=void 0,this.capLevelController=void 0,this.latencyController=void 0,this.levelController=void 0,this.streamController=void 0,this.audioTrackController=void 0,this.subtitleTrackController=void 0,this.emeController=void 0,this.cmcdController=void 0,this._media=null,this.url=null,this.triggeringException=void 0,function(t,e){if("object"==typeof console&&!0===t||"object"==typeof t){L(t,"debug","log","info","warn","error");try{S.log(`Debug logs enabled for "${e}" in hls.js version 1.5.15`)}catch(t){S=T}}else S=T}(t.debug||!1,"Hls instance");const e=this.config=function(t,e){if((e.liveSyncDurationCount||e.liveMaxLatencyDurationCount)&&(e.liveSyncDuration||e.liveMaxLatencyDuration))throw new Error("Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration");if(void 0!==e.liveMaxLatencyDurationCount&&(void 0===e.liveSyncDurationCount||e.liveMaxLatencyDurationCount<=e.liveSyncDurationCount))throw new Error('Illegal hls.js config: "liveMaxLatencyDurationCount" must be greater than "liveSyncDurationCount"');if(void 0!==e.liveMaxLatencyDuration&&(void 0===e.liveSyncDuration||e.liveMaxLatencyDuration<=e.liveSyncDuration))throw new Error('Illegal hls.js config: "liveMaxLatencyDuration" must be greater than "liveSyncDuration"');const s=Aa(t),i=["TimeOut","MaxRetry","RetryDelay","MaxRetryTimeout"];return["manifest","level","frag"].forEach((t=>{const r=`${"level"===t?"playlist":t}LoadPolicy`,n=void 0===e[r],a=[];i.forEach((i=>{const o=`${t}Loading${i}`,l=e[o];if(void 0!==l&&n){a.push(o);const t=s[r].default;switch(e[r]={default:t},i){case"TimeOut":t.maxLoadTimeMs=l,t.maxTimeToFirstByteMs=l;break;case"MaxRetry":t.errorRetry.maxNumRetry=l,t.timeoutRetry.maxNumRetry=l;break;case"RetryDelay":t.errorRetry.retryDelayMs=l,t.timeoutRetry.retryDelayMs=l;break;case"MaxRetryTimeout":t.errorRetry.maxRetryDelayMs=l,t.timeoutRetry.maxRetryDelayMs=l}}})),a.length&&A.warn(`hls.js config: "${a.join('", "')}" setting(s) are deprecated, use "${r}": ${JSON.stringify(e[r])}`)})),h(h({},s),e)}(Pa.DefaultConfig,t);this.userConfig=t,e.progressive&&Ra(e);const{abrController:s,bufferController:i,capLevelController:r,errorController:n,fpsController:a}=e,o=new n(this),l=this.abrController=new s(this),d=this.bufferController=new i(this),c=this.capLevelController=new r(this),u=new a(this),f=new Pe(this),g=new We(this),m=e.contentSteeringController,v=m?new m(this):null,y=this.levelController=new ka(this,v),E=new Ys(this),R=new Da(this.config),b=this.streamController=new xa(this,E,R);c.setStreamController(b),u.setStreamController(b);const k=[f,y,b];v&&k.splice(1,0,v),this.networkControllers=k;const w=[l,d,c,u,g,E];this.audioTrackController=this.createController(e.audioTrackController,k);const D=e.audioStreamController;D&&k.push(new D(this,E,R)),this.subtitleTrackController=this.createController(e.subtitleTrackController,k);const I=e.subtitleStreamController;I&&k.push(new I(this,E,R)),this.createController(e.timelineController,w),R.emeController=this.emeController=this.createController(e.emeController,w),this.cmcdController=this.createController(e.cmcdController,w),this.latencyController=this.createController(je,w),this.coreComponents=w,k.push(o);const C=o.onErrorOut;"function"==typeof C&&this.on(p.ERROR,C,o)}createController(t,e){if(t){const s=new t(this);return e&&e.push(s),s}return null}on(t,e,s=this){this._emitter.on(t,e,s)}once(t,e,s=this){this._emitter.once(t,e,s)}removeAllListeners(t){this._emitter.removeAllListeners(t)}off(t,e,s=this,i){this._emitter.off(t,e,s,i)}listeners(t){return this._emitter.listeners(t)}emit(t,e,s){return this._emitter.emit(t,e,s)}trigger(t,e){if(this.config.debug)return this.emit(t,t,e);try{return this.emit(t,t,e)}catch(e){if(A.error("An internal error happened while handling event "+t+'. Error message: "'+e.message+'". Here is a stacktrace:',e),!this.triggeringException){this.triggeringException=!0;const s=t===p.ERROR;this.trigger(p.ERROR,{type:v.OTHER_ERROR,details:y.INTERNAL_EXCEPTION,fatal:s,event:t,error:e}),this.triggeringException=!1}}return!1}listenerCount(t){return this._emitter.listenerCount(t)}destroy(){A.log("destroy"),this.trigger(p.DESTROYING,void 0),this.detachMedia(),this.removeAllListeners(),this._autoLevelCapping=-1,this.url=null,this.networkControllers.forEach((t=>t.destroy())),this.networkControllers.length=0,this.coreComponents.forEach((t=>t.destroy())),this.coreComponents.length=0;const t=this.config;t.xhrSetup=t.fetchSetup=void 0,this.userConfig=null}attachMedia(t){A.log("attachMedia"),this._media=t,this.trigger(p.MEDIA_ATTACHING,{media:t})}detachMedia(){A.log("detachMedia"),this.trigger(p.MEDIA_DETACHING,void 0),this._media=null}loadSource(t){this.stopLoad();const e=this.media,s=this.url,i=this.url=o.buildAbsoluteURL(self.location.href,t,{alwaysNormalize:!0});this._autoLevelCapping=-1,this._maxHdcpLevel=null,A.log(`loadSource:${i}`),e&&s&&(s!==i||this.bufferController.hasSourceTypes())&&(this.detachMedia(),this.attachMedia(e)),this.trigger(p.MANIFEST_LOADING,{url:t})}startLoad(t=-1){A.log(`startLoad(${t})`),this.started=!0,this.networkControllers.forEach((e=>{e.startLoad(t)}))}stopLoad(){A.log("stopLoad"),this.started=!1,this.networkControllers.forEach((t=>{t.stopLoad()}))}resumeBuffering(){this.started&&this.networkControllers.forEach((t=>{"fragmentLoader"in t&&t.startLoad(-1)}))}pauseBuffering(){this.networkControllers.forEach((t=>{"fragmentLoader"in t&&t.stopLoad()}))}swapAudioCodec(){A.log("swapAudioCodec"),this.streamController.swapAudioCodec()}recoverMediaError(){A.log("recoverMediaError");const t=this._media;this.detachMedia(),t&&this.attachMedia(t)}removeLevel(t){this.levelController.removeLevel(t)}get levels(){const t=this.levelController.levels;return t||[]}get currentLevel(){return this.streamController.currentLevel}set currentLevel(t){A.log(`set currentLevel:${t}`),this.levelController.manualLevel=t,this.streamController.immediateLevelSwitch()}get nextLevel(){return this.streamController.nextLevel}set nextLevel(t){A.log(`set nextLevel:${t}`),this.levelController.manualLevel=t,this.streamController.nextLevelSwitch()}get loadLevel(){return this.levelController.level}set loadLevel(t){A.log(`set loadLevel:${t}`),this.levelController.manualLevel=t}get nextLoadLevel(){return this.levelController.nextLoadLevel}set nextLoadLevel(t){this.levelController.nextLoadLevel=t}get firstLevel(){return Math.max(this.levelController.firstLevel,this.minAutoLevel)}set firstLevel(t){A.log(`set firstLevel:${t}`),this.levelController.firstLevel=t}get startLevel(){const t=this.levelController.startLevel;return-1===t&&this.abrController.forcedAutoLevel>-1?this.abrController.forcedAutoLevel:t}set startLevel(t){A.log(`set startLevel:${t}`),-1!==t&&(t=Math.max(t,this.minAutoLevel)),this.levelController.startLevel=t}get capLevelToPlayerSize(){return this.config.capLevelToPlayerSize}set capLevelToPlayerSize(t){const e=!!t;e!==this.config.capLevelToPlayerSize&&(e?this.capLevelController.startCapping():(this.capLevelController.stopCapping(),this.autoLevelCapping=-1,this.streamController.nextLevelSwitch()),this.config.capLevelToPlayerSize=e)}get autoLevelCapping(){return this._autoLevelCapping}get bandwidthEstimate(){const{bwEstimator:t}=this.abrController;return t?t.getEstimate():NaN}set bandwidthEstimate(t){this.abrController.resetEstimator(t)}get ttfbEstimate(){const{bwEstimator:t}=this.abrController;return t?t.getEstimateTTFB():NaN}set autoLevelCapping(t){this._autoLevelCapping!==t&&(A.log(`set autoLevelCapping:${t}`),this._autoLevelCapping=t,this.levelController.checkMaxAutoUpdated())}get maxHdcpLevel(){return this._maxHdcpLevel}set maxHdcpLevel(t){(function(t){return qe.indexOf(t)>-1})(t)&&this._maxHdcpLevel!==t&&(this._maxHdcpLevel=t,this.levelController.checkMaxAutoUpdated())}get autoLevelEnabled(){return-1===this.levelController.manualLevel}get manualLevel(){return this.levelController.manualLevel}get minAutoLevel(){const{levels:t,config:{minAutoBitrate:e}}=this;if(!t)return 0;const s=t.length;for(let i=0;i<s;i++)if(t[i].maxBitrate>=e)return i;return 0}get maxAutoLevel(){const{levels:t,autoLevelCapping:e,maxHdcpLevel:s}=this;let i;if(i=-1===e&&null!=t&&t.length?t.length-1:e,s)for(let e=i;e--;){const i=t[e].attrs["HDCP-LEVEL"];if(i&&i<=s)return e}return i}get firstAutoLevel(){return this.abrController.firstAutoLevel}get nextAutoLevel(){return this.abrController.nextAutoLevel}set nextAutoLevel(t){this.abrController.nextAutoLevel=t}get playingDate(){return this.streamController.currentProgramDateTime}get mainForwardBufferInfo(){return this.streamController.getMainFwdBufferInfo()}setAudioOption(t){var e;return null==(e=this.audioTrackController)?void 0:e.setAudioOption(t)}setSubtitleOption(t){var e;return null==(e=this.subtitleTrackController)||e.setSubtitleOption(t),null}get allAudioTracks(){const t=this.audioTrackController;return t?t.allAudioTracks:[]}get audioTracks(){const t=this.audioTrackController;return t?t.audioTracks:[]}get audioTrack(){const t=this.audioTrackController;return t?t.audioTrack:-1}set audioTrack(t){const e=this.audioTrackController;e&&(e.audioTrack=t)}get allSubtitleTracks(){const t=this.subtitleTrackController;return t?t.allSubtitleTracks:[]}get subtitleTracks(){const t=this.subtitleTrackController;return t?t.subtitleTracks:[]}get subtitleTrack(){const t=this.subtitleTrackController;return t?t.subtitleTrack:-1}get media(){return this._media}set subtitleTrack(t){const e=this.subtitleTrackController;e&&(e.subtitleTrack=t)}get subtitleDisplay(){const t=this.subtitleTrackController;return!!t&&t.subtitleDisplay}set subtitleDisplay(t){const e=this.subtitleTrackController;e&&(e.subtitleDisplay=t)}get lowLatencyMode(){return this.config.lowLatencyMode}set lowLatencyMode(t){this.config.lowLatencyMode=t}get liveSyncPosition(){return this.latencyController.liveSyncPosition}get latency(){return this.latencyController.latency}get maxLatency(){return this.latencyController.maxLatency}get targetLatency(){return this.latencyController.targetLatency}get drift(){return this.latencyController.drift}get forceStartLoad(){return this.streamController.forceStartLoad}}Pa.defaultConfig=void 0;class Ma{constructor(){this._ranges=[]}get length(){return this._ranges.length}start(t){if(t<0||t>=this._ranges.length)throw new DOMException("Invalid index","IndexSizeError");return this._ranges[t].start}end(t){if(t<0||t>=this._ranges.length)throw new DOMException("Invalid index","IndexSizeError");return this._ranges[t].end}_addRange(t,e){this._ranges.push({start:t,end:e}),this._normalizeRanges()}_removeRange(t,e){let s=[];for(let i of this._ranges)i.end<=t||i.start>=e?s.push(i):i.start<t&&i.end>e?(s.push({start:i.start,end:t}),s.push({start:e,end:i.end})):i.start>=t&&i.end<=e||(i.start<t&&i.end>t&&i.end<=e?s.push({start:i.start,end:t}):i.start>=t&&i.start<e&&i.end>e&&s.push({start:e,end:i.end}));this._ranges=s}_normalizeRanges(){this._ranges.sort(((t,e)=>t.start-e.start));let t=[];for(let e of this._ranges)if(0===t.length)t.push(e);else{let s=t[t.length-1];e.start<=s.end?s.end=Math.max(s.end,e.end):t.push(e)}this._ranges=t}}class Fa extends EventTarget{constructor(t="",e="",s=""){super(),this.kind=t,this.label=e,this.language=s,this.mode="disabled",this.cues=new Oa,this.activeCues=new Oa}addCue(t){this.cues._add(t)}removeCue(t){this.cues._remove(t)}}class Oa{constructor(){this._cues=[]}get length(){return this._cues.length}item(t){return this._cues[t]}getCueById(t){return this._cues.find((e=>e.id===t))||null}_add(t){this._cues.push(t)}_remove(t){const e=this._cues.indexOf(t);-1!==e&&this._cues.splice(e,1)}[Symbol.iterator](){return this._cues[Symbol.iterator]()}}class Na extends EventTarget{constructor(){super(),this._tracks=[]}get length(){return this._tracks.length}item(t){return this._tracks[t]}_add(t){this._tracks.push(t),this.dispatchEvent(new Event("addtrack"))}_remove(t){const e=this._tracks.indexOf(t);-1!==e&&(this._tracks.splice(e,1),this.dispatchEvent(new Event("removetrack")))}[Symbol.iterator](){return this._tracks[Symbol.iterator]()}}class Ua extends EventTarget{constructor(){super(),this.bridgeId=window.nextInternalId,window.nextInternalId+=1,window.bridgeObjectMap[this.bridgeId]=this,this._currentTime=0,this.duration=NaN,this.paused=!0,this.playbackRate=1,this.volume=1,this.muted=!1,this.readyState=0,this.networkState=0,this.buffered=new Ma,this.seeking=!1,this.loop=!1,this.autoplay=!1,this.controls=!1,this.error=null,this.src="",this.videoWidth=0,this.videoHeight=0,this.textTracks=new Na,this.isWaiting=!1,window.bridgeInvokeAsync(this.bridgeId,"VideoElement","constructor",{}),setTimeout((()=>{this.readyState=4,this.dispatchEvent(new Event("loadedmetadata")),this.dispatchEvent(new Event("loadeddata")),this.dispatchEvent(new Event("canplay")),this.dispatchEvent(new Event("canplaythrough"))}),0)}get currentTime(){return this._currentTime}set currentTime(t){this._currentTime!=t&&(this._currentTime=t,this.dispatchEvent(new Event("seeking")),window.bridgeInvokeAsync(this.bridgeId,"VideoElement","setCurrentTime",{currentTime:t}).then((t=>{this.dispatchEvent(new Event("seeked"))})))}bridgeUpdateBuffered(t){const e=t;for(var s=[],i=0;i<e.length;i+=2)s.push({start:e[i],end:e[i+1]});this.buffered._ranges=s}bridgeUpdateStatus(t){var e=!t.isPlaying,s=t.isWaiting,i=t.currentTime;this.paused!=e&&(this.paused=e,e?this.dispatchEvent(new Event("pause")):(this.dispatchEvent(new Event("play")),this.dispatchEvent(new Event("playing")))),this.isWaiting!=s&&(this.isWaiting=s,s&&this.dispatchEvent(new Event("waiting"))),this._currentTime!=i&&(this._currentTime=i,this.dispatchEvent(new Event("timeupdate")))}play(){return this.paused?window.bridgeInvokeAsync(this.bridgeId,"VideoElement","play",{}).then((t=>{this.dispatchEvent(new Event("play")),this.dispatchEvent(new Event("playing"))})):Promise.resolve()}pause(){if(!this.paused)return this.paused=!0,this.dispatchEvent(new Event("pause")),window.bridgeInvokeAsync(this.bridgeId,"VideoElement","pause",{}).then((t=>{}))}canPlayType(t){return"probably"}_getMedia(){return window.mediaSourceMap[this.src]}addTextTrack(t,e,s){const i=new Fa(t,e,s);return this.textTracks._add(i),i}}function Ba(t){const e=Array.from(t,(t=>String.fromCodePoint(t))).join("");return btoa(e)}class $a extends EventTarget{constructor(){super(),this._buffers=[]}_add(t){this._buffers.push(t),this.dispatchEvent(new Event("addsourcebuffer"))}_remove(t){const e=this._buffers.indexOf(t);return-1!==e&&(this._buffers.splice(e,1),this.dispatchEvent(new Event("removesourcebuffer")),!0)}get length(){return this._buffers.length}item(t){return this._buffers[t]}[Symbol.iterator](){return this._buffers[Symbol.iterator]()}}class Ga extends EventTarget{constructor(t,e){super(),this.mediaSource=t,this.mimeType=e,this.updating=!1,this.buffered=new Ma,this.timestampOffset=0,this.appendWindowStart=0,this.appendWindowEnd=1/0,this.bridgeId=window.nextInternalId,window.nextInternalId+=1,window.bridgeObjectMap[this.bridgeId]=this,window.bridgeInvokeAsync(this.bridgeId,"SourceBuffer","constructor",{mimeType:e})}appendBuffer(t){if(this.updating)throw new DOMException("SourceBuffer is updating","InvalidStateError");this.updating=!0,this.dispatchEvent(new Event("updatestart")),window.bridgeInvokeAsync(this.bridgeId,"SourceBuffer","appendBuffer",{data:Ba(t)}).then((t=>{const e=t.ranges;for(var s=[],i=0;i<e.length;i+=2)s.push({start:e[i],end:e[i+1]});this.buffered._ranges=s,this.mediaSource._reopen(),this.updating=!1,this.dispatchEvent(new Event("update")),this.dispatchEvent(new Event("updateend"))}))}abort(){this.updating&&(this.updating=!1,this.dispatchEvent(new Event("abort")),window.bridgeInvokeAsync(this.bridgeId,"SourceBuffer","abort",{}).then((t=>{})))}remove(t,e){if(this.updating)throw new DOMException("SourceBuffer is updating","InvalidStateError");this.updating=!0,this.dispatchEvent(new Event("updatestart")),window.bridgeInvokeAsync(this.bridgeId,"SourceBuffer","remove",{start:t,end:e}).then((t=>{const e=t.ranges;for(var s=[],i=0;i<e.length;i+=2)s.push({start:e[i],end:e[i+1]});this.buffered._ranges=s,this.mediaSource._reopen(),this.updating=!1,this.dispatchEvent(new Event("update")),this.dispatchEvent(new Event("updateend"))}))}}class Ka extends EventTarget{constructor(){super(),this.internalId=window.nextInternalId,window.nextInternalId+=1,this.bridgeId=window.nextInternalId,window.nextInternalId+=1,window.bridgeObjectMap[this.bridgeId]=this,this.sourceBuffers=new $a,this.activeSourceBuffers=new $a,this.readyState="closed",this._duration=NaN,window.bridgeInvokeAsync(this.bridgeId,"MediaSource","constructor",{}),setTimeout((()=>{this.readyState="open",this.dispatchEvent(new Event("sourceopen"))}),0)}static isTypeSupported(t){return!0}addSourceBuffer(t){if("open"!==this.readyState)throw new DOMException("MediaSource is not open","InvalidStateError");const e=new Ga(this,t);return this.sourceBuffers._add(e),this.activeSourceBuffers._add(e),e}removeSourceBuffer(t){if(!this.sourceBuffers._remove(t))throw new DOMException("SourceBuffer not found","NotFoundError");this.activeSourceBuffers._remove(t)}endOfStream(t){if("open"!==this.readyState)throw new DOMException("MediaSource is not open","InvalidStateError");this.readyState="ended",this.dispatchEvent(new Event("sourceended"))}_reopen(){"open"!==this.readyState&&(this.readyState="open",this.dispatchEvent(new Event("sourceopen")))}set duration(t){if("closed"===this.readyState)throw new DOMException("MediaSource is closed","InvalidStateError");this._duration=t,window.bridgeInvokeAsync(this.bridgeId,"MediaSource","setDuration",{duration:t}).then((t=>{}))}get duration(){return this._duration}}window.bridgeObjectMap={},window.bridgeCallbackMap={},window.bridgeInvokeAsync=function(t,e,s,i){var r,n=new Promise((function(t,e){r=t}));const a=window.nextInternalId;return window.nextInternalId+=1,window.bridgeCallbackMap[a]=r,window.webkit.messageHandlers&&window.webkit.messageHandlers.performAction.postMessage({event:"bridgeInvoke",data:{bridgeId:t,className:e,methodName:s,params:i,callbackId:a}}),n};var Ha,Va;function Ya(t,e){window.webkit&&window.webkit.messageHandlers&&window.webkit.messageHandlers.performAction.postMessage({event:t,data:e})}window.nextInternalId=0,window.mediaSourceMap={},"undefined"!=typeof window&&(window.MediaSource=Ka,window.ManagedMediaSource=Ka,window.SourceBuffer=Ga,URL.createObjectURL=function(t){const e="blob:mock-media-source:"+t.internalId;return window.mediaSourceMap[e]=t,e});var Wa=!1,ja=!1,qa=null;function Xa(){for(var t=[],e=0;e<Va.levels.length;e++){var s=Va.levels[e];t.push({index:e,bitrate:s.bitrate||0,width:s.width||0,height:s.height||0})}return t}function za(){var t=!1;!Ha.paused&&!Ha.ended&&Ha.readyState>2&&(t=!0),Ya("playerStatus",{isReady:Wa,isFirstFrameReady:ja,isPlaying:!Ha.paused,rate:t?Ha.playbackRate:0,defaultRate:Ha.playbackRate,levels:Xa(),currentLevel:Va.currentLevel}),Qa(),t?null==qa&&(qa=setTimeout((()=>{Qa()}),200)):null!=qa&&(clearTimeout(qa),qa=null)}function Qa(){Ya("playerCurrentTime",{value:Ha.currentTime}),qa=setTimeout((()=>{Qa()}),200)}window.onload=()=>{Ha=new Ua,Ya("windowOnLoad",{})},window.playerInitialize=function(t){Ha.muted=!1,Ha.addEventListener("loadeddata",(t=>{ja||(ja=!0,za())})),Ha.addEventListener("playing",(function(){za()})),Ha.addEventListener("pause",(function(){za()})),Ha.addEventListener("seeking",(function(){za()})),Ha.addEventListener("waiting",(function(){za()})),(Va=new Pa({startLevel:0,testBandwidth:!1,debug:t.debug||!0,autoStartLoad:!1,backBufferLength:30,maxBufferLength:60,maxMaxBufferLength:60,maxFragLookUpTolerance:.001,nudgeMaxRetry:1e4})).on(Pa.Events.MANIFEST_PARSED,(function(){Wa=!0,za()})),Va.on(Pa.Events.LEVEL_SWITCHED,(function(){za()})),Va.on(Pa.Events.LEVELS_UPDATED,(function(){za()})),Va.loadSource("master.m3u8"),Va.attachMedia(Ha)},window.playerLoad=function(t){Va.startLevel=t,Va.startLoad(-1,!1)},window.playerPlay=function(){Ha.play()},window.playerPause=function(){Ha.pause()},window.playerSetBaseRate=function(t){Ha.playbackRate=t},window.playerSetLevel=function(t){Va.currentLevel=t>=0?t:-1},window.playerSeek=function(t){Ha.currentTime=t},window.playerSetIsMuted=function(t){Ha.muted=t},window.bridgeInvokeCallback=function(t,e){const s=window.bridgeCallbackMap[t];s&&s(e)}}},t=>{var e;e=368,t(t.s=e)}]); \ No newline at end of file diff --git a/submodules/TelegramUniversalVideoContent/HlsBundle/index.html b/submodules/TelegramUniversalVideoContent/HlsBundle/index.html index b2a82abe48..6b1bdaf712 100644 --- a/submodules/TelegramUniversalVideoContent/HlsBundle/index.html +++ b/submodules/TelegramUniversalVideoContent/HlsBundle/index.html @@ -1,9 +1 @@ -<!DOCTYPE html> -<html> - <head> - <meta charset="utf-8"> - <title>Developement - - - - \ No newline at end of file +Production \ No newline at end of file diff --git a/submodules/TelegramUniversalVideoContent/HlsBundle/runtime.bundle.js b/submodules/TelegramUniversalVideoContent/HlsBundle/runtime.bundle.js index 34107e8741..4f4e37dbc5 100644 --- a/submodules/TelegramUniversalVideoContent/HlsBundle/runtime.bundle.js +++ b/submodules/TelegramUniversalVideoContent/HlsBundle/runtime.bundle.js @@ -1,151 +1 @@ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({}); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = __webpack_modules__; -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/chunk loaded */ -/******/ (() => { -/******/ var deferred = []; -/******/ __webpack_require__.O = (result, chunkIds, fn, priority) => { -/******/ if(chunkIds) { -/******/ priority = priority || 0; -/******/ for(var i = deferred.length; i > 0 && deferred[i - 1][2] > priority; i--) deferred[i] = deferred[i - 1]; -/******/ deferred[i] = [chunkIds, fn, priority]; -/******/ return; -/******/ } -/******/ var notFulfilled = Infinity; -/******/ for (var i = 0; i < deferred.length; i++) { -/******/ var [chunkIds, fn, priority] = deferred[i]; -/******/ var fulfilled = true; -/******/ for (var j = 0; j < chunkIds.length; j++) { -/******/ if ((priority & 1 === 0 || notFulfilled >= priority) && Object.keys(__webpack_require__.O).every((key) => (__webpack_require__.O[key](chunkIds[j])))) { -/******/ chunkIds.splice(j--, 1); -/******/ } else { -/******/ fulfilled = false; -/******/ if(priority < notFulfilled) notFulfilled = priority; -/******/ } -/******/ } -/******/ if(fulfilled) { -/******/ deferred.splice(i--, 1) -/******/ var r = fn(); -/******/ if (r !== undefined) result = r; -/******/ } -/******/ } -/******/ return result; -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/jsonp chunk loading */ -/******/ (() => { -/******/ // no baseURI -/******/ -/******/ // object to store loaded and loading chunks -/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched -/******/ // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded -/******/ var installedChunks = { -/******/ "runtime": 0 -/******/ }; -/******/ -/******/ // no chunk on demand loading -/******/ -/******/ // no prefetching -/******/ -/******/ // no preloaded -/******/ -/******/ // no HMR -/******/ -/******/ // no HMR manifest -/******/ -/******/ __webpack_require__.O.j = (chunkId) => (installedChunks[chunkId] === 0); -/******/ -/******/ // install a JSONP callback for chunk loading -/******/ var webpackJsonpCallback = (parentChunkLoadingFunction, data) => { -/******/ var [chunkIds, moreModules, runtime] = data; -/******/ // add "moreModules" to the modules object, -/******/ // then flag all "chunkIds" as loaded and fire callback -/******/ var moduleId, chunkId, i = 0; -/******/ if(chunkIds.some((id) => (installedChunks[id] !== 0))) { -/******/ for(moduleId in moreModules) { -/******/ if(__webpack_require__.o(moreModules, moduleId)) { -/******/ __webpack_require__.m[moduleId] = moreModules[moduleId]; -/******/ } -/******/ } -/******/ if(runtime) var result = runtime(__webpack_require__); -/******/ } -/******/ if(parentChunkLoadingFunction) parentChunkLoadingFunction(data); -/******/ for(;i < chunkIds.length; i++) { -/******/ chunkId = chunkIds[i]; -/******/ if(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) { -/******/ installedChunks[chunkId][0](); -/******/ } -/******/ installedChunks[chunkId] = 0; -/******/ } -/******/ return __webpack_require__.O(result); -/******/ } -/******/ -/******/ var chunkLoadingGlobal = self["webpackChunkmyhls"] = self["webpackChunkmyhls"] || []; -/******/ chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0)); -/******/ chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal)); -/******/ })(); -/******/ -/************************************************************************/ -/******/ -/******/ -/******/ })() -; -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVudGltZS5idW5kbGUuanMiLCJtYXBwaW5ncyI6Ijs7OztVQUFBO1VBQ0E7O1VBRUE7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7O1VBRUE7VUFDQTs7VUFFQTtVQUNBO1VBQ0E7O1VBRUE7VUFDQTs7Ozs7V0N6QkE7V0FDQTtXQUNBO1dBQ0E7V0FDQSwrQkFBK0Isd0NBQXdDO1dBQ3ZFO1dBQ0E7V0FDQTtXQUNBO1dBQ0EsaUJBQWlCLHFCQUFxQjtXQUN0QztXQUNBO1dBQ0Esa0JBQWtCLHFCQUFxQjtXQUN2QztXQUNBO1dBQ0EsS0FBSztXQUNMO1dBQ0E7V0FDQTtXQUNBO1dBQ0E7V0FDQTtXQUNBO1dBQ0E7V0FDQTtXQUNBO1dBQ0E7V0FDQTs7Ozs7V0MzQkE7V0FDQTtXQUNBO1dBQ0E7V0FDQSx5Q0FBeUMsd0NBQXdDO1dBQ2pGO1dBQ0E7V0FDQTs7Ozs7V0NQQTs7Ozs7V0NBQTtXQUNBO1dBQ0E7V0FDQSx1REFBdUQsaUJBQWlCO1dBQ3hFO1dBQ0EsZ0RBQWdELGFBQWE7V0FDN0Q7Ozs7O1dDTkE7O1dBRUE7V0FDQTtXQUNBO1dBQ0E7V0FDQTtXQUNBOztXQUVBOztXQUVBOztXQUVBOztXQUVBOztXQUVBOztXQUVBOztXQUVBO1dBQ0E7V0FDQTtXQUNBO1dBQ0E7V0FDQTtXQUNBO1dBQ0E7V0FDQTtXQUNBO1dBQ0E7V0FDQTtXQUNBO1dBQ0E7V0FDQTtXQUNBLE1BQU0scUJBQXFCO1dBQzNCO1dBQ0E7V0FDQTtXQUNBO1dBQ0E7V0FDQTtXQUNBO1dBQ0E7O1dBRUE7V0FDQTtXQUNBIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vbXlobHMvd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vbXlobHMvd2VicGFjay9ydW50aW1lL2NodW5rIGxvYWRlZCIsIndlYnBhY2s6Ly9teWhscy93ZWJwYWNrL3J1bnRpbWUvZGVmaW5lIHByb3BlcnR5IGdldHRlcnMiLCJ3ZWJwYWNrOi8vbXlobHMvd2VicGFjay9ydW50aW1lL2hhc093blByb3BlcnR5IHNob3J0aGFuZCIsIndlYnBhY2s6Ly9teWhscy93ZWJwYWNrL3J1bnRpbWUvbWFrZSBuYW1lc3BhY2Ugb2JqZWN0Iiwid2VicGFjazovL215aGxzL3dlYnBhY2svcnVudGltZS9qc29ucCBjaHVuayBsb2FkaW5nIiwid2VicGFjazovL215aGxzL3dlYnBhY2svYmVmb3JlLXN0YXJ0dXAiLCJ3ZWJwYWNrOi8vbXlobHMvd2VicGFjay9zdGFydHVwIiwid2VicGFjazovL215aGxzL3dlYnBhY2svYWZ0ZXItc3RhcnR1cCJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBUaGUgbW9kdWxlIGNhY2hlXG52YXIgX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fID0ge307XG5cbi8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG5mdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuXHR2YXIgY2FjaGVkTW9kdWxlID0gX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fW21vZHVsZUlkXTtcblx0aWYgKGNhY2hlZE1vZHVsZSAhPT0gdW5kZWZpbmVkKSB7XG5cdFx0cmV0dXJuIGNhY2hlZE1vZHVsZS5leHBvcnRzO1xuXHR9XG5cdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG5cdHZhciBtb2R1bGUgPSBfX3dlYnBhY2tfbW9kdWxlX2NhY2hlX19bbW9kdWxlSWRdID0ge1xuXHRcdC8vIG5vIG1vZHVsZS5pZCBuZWVkZWRcblx0XHQvLyBubyBtb2R1bGUubG9hZGVkIG5lZWRlZFxuXHRcdGV4cG9ydHM6IHt9XG5cdH07XG5cblx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG5cdF9fd2VicGFja19tb2R1bGVzX19bbW9kdWxlSWRdKG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG5cdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG5cdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbn1cblxuLy8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbl9fd2VicGFja19yZXF1aXJlX18ubSA9IF9fd2VicGFja19tb2R1bGVzX187XG5cbiIsInZhciBkZWZlcnJlZCA9IFtdO1xuX193ZWJwYWNrX3JlcXVpcmVfXy5PID0gKHJlc3VsdCwgY2h1bmtJZHMsIGZuLCBwcmlvcml0eSkgPT4ge1xuXHRpZihjaHVua0lkcykge1xuXHRcdHByaW9yaXR5ID0gcHJpb3JpdHkgfHwgMDtcblx0XHRmb3IodmFyIGkgPSBkZWZlcnJlZC5sZW5ndGg7IGkgPiAwICYmIGRlZmVycmVkW2kgLSAxXVsyXSA+IHByaW9yaXR5OyBpLS0pIGRlZmVycmVkW2ldID0gZGVmZXJyZWRbaSAtIDFdO1xuXHRcdGRlZmVycmVkW2ldID0gW2NodW5rSWRzLCBmbiwgcHJpb3JpdHldO1xuXHRcdHJldHVybjtcblx0fVxuXHR2YXIgbm90RnVsZmlsbGVkID0gSW5maW5pdHk7XG5cdGZvciAodmFyIGkgPSAwOyBpIDwgZGVmZXJyZWQubGVuZ3RoOyBpKyspIHtcblx0XHR2YXIgW2NodW5rSWRzLCBmbiwgcHJpb3JpdHldID0gZGVmZXJyZWRbaV07XG5cdFx0dmFyIGZ1bGZpbGxlZCA9IHRydWU7XG5cdFx0Zm9yICh2YXIgaiA9IDA7IGogPCBjaHVua0lkcy5sZW5ndGg7IGorKykge1xuXHRcdFx0aWYgKChwcmlvcml0eSAmIDEgPT09IDAgfHwgbm90RnVsZmlsbGVkID49IHByaW9yaXR5KSAmJiBPYmplY3Qua2V5cyhfX3dlYnBhY2tfcmVxdWlyZV9fLk8pLmV2ZXJ5KChrZXkpID0+IChfX3dlYnBhY2tfcmVxdWlyZV9fLk9ba2V5XShjaHVua0lkc1tqXSkpKSkge1xuXHRcdFx0XHRjaHVua0lkcy5zcGxpY2Uoai0tLCAxKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGZ1bGZpbGxlZCA9IGZhbHNlO1xuXHRcdFx0XHRpZihwcmlvcml0eSA8IG5vdEZ1bGZpbGxlZCkgbm90RnVsZmlsbGVkID0gcHJpb3JpdHk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdGlmKGZ1bGZpbGxlZCkge1xuXHRcdFx0ZGVmZXJyZWQuc3BsaWNlKGktLSwgMSlcblx0XHRcdHZhciByID0gZm4oKTtcblx0XHRcdGlmIChyICE9PSB1bmRlZmluZWQpIHJlc3VsdCA9IHI7XG5cdFx0fVxuXHR9XG5cdHJldHVybiByZXN1bHQ7XG59OyIsIi8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb25zIGZvciBoYXJtb255IGV4cG9ydHNcbl9fd2VicGFja19yZXF1aXJlX18uZCA9IChleHBvcnRzLCBkZWZpbml0aW9uKSA9PiB7XG5cdGZvcih2YXIga2V5IGluIGRlZmluaXRpb24pIHtcblx0XHRpZihfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZGVmaW5pdGlvbiwga2V5KSAmJiAhX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIGtleSkpIHtcblx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBrZXksIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBkZWZpbml0aW9uW2tleV0gfSk7XG5cdFx0fVxuXHR9XG59OyIsIl9fd2VicGFja19yZXF1aXJlX18ubyA9IChvYmosIHByb3ApID0+IChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqLCBwcm9wKSkiLCIvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG5fX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSAoZXhwb3J0cykgPT4ge1xuXHRpZih0eXBlb2YgU3ltYm9sICE9PSAndW5kZWZpbmVkJyAmJiBTeW1ib2wudG9TdHJpbmdUYWcpIHtcblx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgU3ltYm9sLnRvU3RyaW5nVGFnLCB7IHZhbHVlOiAnTW9kdWxlJyB9KTtcblx0fVxuXHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xufTsiLCIvLyBubyBiYXNlVVJJXG5cbi8vIG9iamVjdCB0byBzdG9yZSBsb2FkZWQgYW5kIGxvYWRpbmcgY2h1bmtzXG4vLyB1bmRlZmluZWQgPSBjaHVuayBub3QgbG9hZGVkLCBudWxsID0gY2h1bmsgcHJlbG9hZGVkL3ByZWZldGNoZWRcbi8vIFtyZXNvbHZlLCByZWplY3QsIFByb21pc2VdID0gY2h1bmsgbG9hZGluZywgMCA9IGNodW5rIGxvYWRlZFxudmFyIGluc3RhbGxlZENodW5rcyA9IHtcblx0XCJydW50aW1lXCI6IDBcbn07XG5cbi8vIG5vIGNodW5rIG9uIGRlbWFuZCBsb2FkaW5nXG5cbi8vIG5vIHByZWZldGNoaW5nXG5cbi8vIG5vIHByZWxvYWRlZFxuXG4vLyBubyBITVJcblxuLy8gbm8gSE1SIG1hbmlmZXN0XG5cbl9fd2VicGFja19yZXF1aXJlX18uTy5qID0gKGNodW5rSWQpID0+IChpbnN0YWxsZWRDaHVua3NbY2h1bmtJZF0gPT09IDApO1xuXG4vLyBpbnN0YWxsIGEgSlNPTlAgY2FsbGJhY2sgZm9yIGNodW5rIGxvYWRpbmdcbnZhciB3ZWJwYWNrSnNvbnBDYWxsYmFjayA9IChwYXJlbnRDaHVua0xvYWRpbmdGdW5jdGlvbiwgZGF0YSkgPT4ge1xuXHR2YXIgW2NodW5rSWRzLCBtb3JlTW9kdWxlcywgcnVudGltZV0gPSBkYXRhO1xuXHQvLyBhZGQgXCJtb3JlTW9kdWxlc1wiIHRvIHRoZSBtb2R1bGVzIG9iamVjdCxcblx0Ly8gdGhlbiBmbGFnIGFsbCBcImNodW5rSWRzXCIgYXMgbG9hZGVkIGFuZCBmaXJlIGNhbGxiYWNrXG5cdHZhciBtb2R1bGVJZCwgY2h1bmtJZCwgaSA9IDA7XG5cdGlmKGNodW5rSWRzLnNvbWUoKGlkKSA9PiAoaW5zdGFsbGVkQ2h1bmtzW2lkXSAhPT0gMCkpKSB7XG5cdFx0Zm9yKG1vZHVsZUlkIGluIG1vcmVNb2R1bGVzKSB7XG5cdFx0XHRpZihfX3dlYnBhY2tfcmVxdWlyZV9fLm8obW9yZU1vZHVsZXMsIG1vZHVsZUlkKSkge1xuXHRcdFx0XHRfX3dlYnBhY2tfcmVxdWlyZV9fLm1bbW9kdWxlSWRdID0gbW9yZU1vZHVsZXNbbW9kdWxlSWRdO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRpZihydW50aW1lKSB2YXIgcmVzdWx0ID0gcnVudGltZShfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblx0fVxuXHRpZihwYXJlbnRDaHVua0xvYWRpbmdGdW5jdGlvbikgcGFyZW50Q2h1bmtMb2FkaW5nRnVuY3Rpb24oZGF0YSk7XG5cdGZvcig7aSA8IGNodW5rSWRzLmxlbmd0aDsgaSsrKSB7XG5cdFx0Y2h1bmtJZCA9IGNodW5rSWRzW2ldO1xuXHRcdGlmKF9fd2VicGFja19yZXF1aXJlX18ubyhpbnN0YWxsZWRDaHVua3MsIGNodW5rSWQpICYmIGluc3RhbGxlZENodW5rc1tjaHVua0lkXSkge1xuXHRcdFx0aW5zdGFsbGVkQ2h1bmtzW2NodW5rSWRdWzBdKCk7XG5cdFx0fVxuXHRcdGluc3RhbGxlZENodW5rc1tjaHVua0lkXSA9IDA7XG5cdH1cblx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18uTyhyZXN1bHQpO1xufVxuXG52YXIgY2h1bmtMb2FkaW5nR2xvYmFsID0gc2VsZltcIndlYnBhY2tDaHVua215aGxzXCJdID0gc2VsZltcIndlYnBhY2tDaHVua215aGxzXCJdIHx8IFtdO1xuY2h1bmtMb2FkaW5nR2xvYmFsLmZvckVhY2god2VicGFja0pzb25wQ2FsbGJhY2suYmluZChudWxsLCAwKSk7XG5jaHVua0xvYWRpbmdHbG9iYWwucHVzaCA9IHdlYnBhY2tKc29ucENhbGxiYWNrLmJpbmQobnVsbCwgY2h1bmtMb2FkaW5nR2xvYmFsLnB1c2guYmluZChjaHVua0xvYWRpbmdHbG9iYWwpKTsiLCIiLCIiLCIiXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file +(()=>{"use strict";var r,e={},o={};function t(r){var n=o[r];if(void 0!==n)return n.exports;var s=o[r]={exports:{}};return e[r](s,s.exports,t),s.exports}t.m=e,r=[],t.O=(e,o,n,s)=>{if(!o){var a=1/0;for(v=0;v=s)&&Object.keys(t.O).every((r=>t.O[r](o[i])))?o.splice(i--,1):(l=!1,s0&&r[v-1][2]>s;v--)r[v]=r[v-1];r[v]=[o,n,s]},t.o=(r,e)=>Object.prototype.hasOwnProperty.call(r,e),(()=>{var r={121:0};t.O.j=e=>0===r[e];var e=(e,o)=>{var n,s,[a,l,i]=o,f=0;if(a.some((e=>0!==r[e]))){for(n in l)t.o(l,n)&&(t.m[n]=l[n]);if(i)var v=i(t)}for(e&&e(o);f=10.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", + "dev": true, + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.0.tgz", + "integrity": "sha512-AbXMTZGt40T+KON9/Fdxx0B2WK5hsgxcfXJLr5bFpZ7b4JCex2WyQPTEKdXqfHiY5nKKBScZ7yCoO6Pvgxfvnw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/express/node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "dev": true + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/http-proxy": { + "version": "1.17.15", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz", + "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.7.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz", + "integrity": "sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", + "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", + "dev": true, + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bonjour-service": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001666", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001666.tgz", + "integrity": "sha512-gD14ICmoV5ZZM1OdzPWmpx+q4GyefaK06zi8hmfHV5xe4/2nOQX3+Dw5o+fSqOws2xVwL9j+anOPFwHzdEdV4g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "dev": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-loader": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", + "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dev": true, + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.5.31", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.31.tgz", + "integrity": "sha512-QcDoBbQeYt0+3CWcK/rEbuHvwpbT/8SV9T3OSgs6cX1FlcUAkgrkqbg9zLnDrMM/rLamzQwal4LYFCiWk861Tg==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/envinfo": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", + "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/expose-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-5.0.0.tgz", + "integrity": "sha512-BtUqYRmvx1bEY5HN6eK2I9URUZgNmN0x5UANuocaNjXSgfoDlkXt+wyEMe7i5DzDNh2BKJHPc5F4rBwEdSQX6w==", + "dev": true, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/express": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-uri": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.2.tgz", + "integrity": "sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row==", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", + "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/hls.js": { + "version": "1.5.15", + "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.5.15.tgz", + "integrity": "sha512-6cD7xN6bycBHaXz2WyPIaHn/iXFizE5au2yvY5q9aC4wfihxAr16C9fUy4nxh2a3wOw0fEgLRa9dN6wsYjlpNg==" + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dev": true, + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz", + "integrity": "sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw==", + "dev": true, + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.20.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/launch-editor": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", + "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", + "dev": true, + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-to-regexp": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", + "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", + "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dev": true, + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "node_modules/selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "dev": true, + "dependencies": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dev": true, + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/spdy-transport/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/spdy-transport/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/spdy/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/spdy/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/style-loader": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", + "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.34.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.34.1.tgz", + "integrity": "sha512-FsJZ7iZLd/BXkz+4xrRTGJ26o/6VTjQytUk8b8OxkwcD2I+79VPJlz7qss1+zE7h8GNIScFqXcDyJ/KqBYZFVA==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/watchpack": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/webpack": { + "version": "5.95.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.95.0.tgz", + "integrity": "sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-attributes": "^1.9.5", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", + "colorette": "^2.0.14", + "commander": "^10.0.1", + "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/webpack-cli/node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-6.1.3.tgz", + "integrity": "sha512-A4ChP0Qj8oGociTs6UdlRUGANIGrCDL3y+pmQMc+dSsraXHCatFpmMey4mYELA+juqwUqwQsUgJJISXl1KWmiw==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.12", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server": { + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", + "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.5", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.4", + "ws": "^8.13.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", + "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-merge": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/submodules/TelegramUniversalVideoContent/PlayerSource/package.json b/submodules/TelegramUniversalVideoContent/PlayerSource/package.json new file mode 100644 index 0000000000..00b6d56091 --- /dev/null +++ b/submodules/TelegramUniversalVideoContent/PlayerSource/package.json @@ -0,0 +1,30 @@ +{ + "name": "myhls", + "version": "1.0.0", + "description": "", + "private": true, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build-development": "webpack --config webpack.dev.js", + "build-release": "webpack --config webpack.prod.js" + }, + "keywords": [], + "author": "", + "license": "MIT", + "devDependencies": { + "copy-webpack-plugin": "^11.0.0", + "css-loader": "^6.8.1", + "expose-loader": "^5.0.0", + "express": "^4.18.2", + "html-webpack-plugin": "^5.5.3", + "style-loader": "^3.3.3", + "webpack": "^5.89.0", + "webpack-cli": "^5.1.4", + "webpack-dev-middleware": "^6.1.1", + "webpack-dev-server": "^4.15.1", + "webpack-merge": "^6.0.1" + }, + "dependencies": { + "hls.js": "^1.5.15" + } +} diff --git a/submodules/TelegramUniversalVideoContent/PlayerSource/server.js b/submodules/TelegramUniversalVideoContent/PlayerSource/server.js new file mode 100644 index 0000000000..66f8abbd63 --- /dev/null +++ b/submodules/TelegramUniversalVideoContent/PlayerSource/server.js @@ -0,0 +1,20 @@ +const express = require('express'); +const webpack = require('webpack'); +const webpackDevMiddleware = require('webpack-dev-middleware'); + +const app = express(); +const config = require('./webpack.config.js'); +const compiler = webpack(config); + +// Tell express to use the webpack-dev-middleware and use the webpack.config.js +// configuration file as a base. +app.use( + webpackDevMiddleware(compiler, { + publicPath: config.output.publicPath, + }) +); + +// Serve the files on port 3000. +app.listen(3000, function () { + console.log('Example app listening on port 3000!\n'); +}); diff --git a/submodules/TelegramUniversalVideoContent/PlayerSource/src/MediaSourceStub.js b/submodules/TelegramUniversalVideoContent/PlayerSource/src/MediaSourceStub.js new file mode 100644 index 0000000000..d18b7ecd88 --- /dev/null +++ b/submodules/TelegramUniversalVideoContent/PlayerSource/src/MediaSourceStub.js @@ -0,0 +1,210 @@ +import { TimeRangesStub } from "./TimeRangesStub.js" + +function bytesToBase64(bytes) { + const binString = Array.from(bytes, (byte) => + String.fromCodePoint(byte), + ).join(""); + return btoa(binString); +} + +export class SourceBufferListStub extends EventTarget { + constructor() { + super(); + this._buffers = []; + } + + _add(buffer) { + this._buffers.push(buffer); + this.dispatchEvent(new Event('addsourcebuffer')); + } + + _remove(buffer) { + const index = this._buffers.indexOf(buffer); + if (index === -1) { + return false; + } + this._buffers.splice(index, 1); + this.dispatchEvent(new Event('removesourcebuffer')); + return true; + } + + get length() { + return this._buffers.length; + } + + item(index) { + return this._buffers[index]; + } + + [Symbol.iterator]() { + return this._buffers[Symbol.iterator](); + } +} + +export class SourceBufferStub extends EventTarget { + constructor(mediaSource, mimeType) { + super(); + this.mediaSource = mediaSource; + this.mimeType = mimeType; + this.updating = false; + this.buffered = new TimeRangesStub(); + this.timestampOffset = 0; + this.appendWindowStart = 0; + this.appendWindowEnd = Infinity; + + this.bridgeId = window.nextInternalId; + window.nextInternalId += 1; + window.bridgeObjectMap[this.bridgeId] = this; + + window.bridgeInvokeAsync(this.bridgeId, "SourceBuffer", "constructor", { + "mimeType": mimeType + }); + } + + appendBuffer(data) { + if (this.updating) { + throw new DOMException('SourceBuffer is updating', 'InvalidStateError'); + } + this.updating = true; + this.dispatchEvent(new Event('updatestart')); + + window.bridgeInvokeAsync(this.bridgeId, "SourceBuffer", "appendBuffer", { + "data": bytesToBase64(data) + }).then((result) => { + const updatedRanges = result["ranges"]; + var ranges = []; + for (var i = 0; i < updatedRanges.length; i += 2) { + ranges.push({ + start: updatedRanges[i], + end: updatedRanges[i + 1] + }); + } + this.buffered._ranges = ranges; + + this.mediaSource._reopen(); + + this.updating = false; + this.dispatchEvent(new Event('update')); + this.dispatchEvent(new Event('updateend')); + }); + } + + abort() { + if (this.updating) { + this.updating = false; + this.dispatchEvent(new Event('abort')); + + window.bridgeInvokeAsync(this.bridgeId, "SourceBuffer", "abort", {}).then((result) => { + }); + } + } + + remove(start, end) { + if (this.updating) { + throw new DOMException('SourceBuffer is updating', 'InvalidStateError'); + } + this.updating = true; + this.dispatchEvent(new Event('updatestart')); + + window.bridgeInvokeAsync(this.bridgeId, "SourceBuffer", "remove", { + "start": start, + "end": end + }).then((result) => { + const updatedRanges = result["ranges"]; + var ranges = []; + for (var i = 0; i < updatedRanges.length; i += 2) { + ranges.push({ + start: updatedRanges[i], + end: updatedRanges[i + 1] + }); + } + this.buffered._ranges = ranges; + + this.mediaSource._reopen(); + + this.updating = false; + this.dispatchEvent(new Event('update')); + this.dispatchEvent(new Event('updateend')); + }); + } +} + +export class MediaSourceStub extends EventTarget { + constructor() { + super(); + + this.internalId = window.nextInternalId; + window.nextInternalId += 1; + + this.bridgeId = window.nextInternalId; + window.nextInternalId += 1; + window.bridgeObjectMap[this.bridgeId] = this; + + this.sourceBuffers = new SourceBufferListStub(); + this.activeSourceBuffers = new SourceBufferListStub(); + this.readyState = 'closed'; + this._duration = NaN; + + window.bridgeInvokeAsync(this.bridgeId, "MediaSource", "constructor", { + }); + + // Simulate asynchronous opening of MediaSource + setTimeout(() => { + this.readyState = 'open'; + this.dispatchEvent(new Event('sourceopen')); + }, 0); + } + + static isTypeSupported(mimeType) { + // Assume all MIME types are supported in this stub + return true; + } + + addSourceBuffer(mimeType) { + if (this.readyState !== 'open') { + throw new DOMException('MediaSource is not open', 'InvalidStateError'); + } + const sourceBuffer = new SourceBufferStub(this, mimeType); + this.sourceBuffers._add(sourceBuffer); + this.activeSourceBuffers._add(sourceBuffer); + return sourceBuffer; + } + + removeSourceBuffer(sourceBuffer) { + if (!this.sourceBuffers._remove(sourceBuffer)) { + throw new DOMException('SourceBuffer not found', 'NotFoundError'); + } + this.activeSourceBuffers._remove(sourceBuffer); + } + + endOfStream(error) { + if (this.readyState !== 'open') { + throw new DOMException('MediaSource is not open', 'InvalidStateError'); + } + this.readyState = 'ended'; + this.dispatchEvent(new Event('sourceended')); + } + + _reopen() { + if (this.readyState !== 'open') { + this.readyState = 'open'; + this.dispatchEvent(new Event('sourceopen')); + } + } + + set duration(value) { + if (this.readyState === 'closed') { + throw new DOMException('MediaSource is closed', 'InvalidStateError'); + } + this._duration = value; + + window.bridgeInvokeAsync(this.bridgeId, "MediaSource", "setDuration", { + "duration": value + }).then((result) => { + }) + } + + get duration() { + return this._duration; + } +} diff --git a/submodules/TelegramUniversalVideoContent/PlayerSource/src/TextTrackStub.js b/submodules/TelegramUniversalVideoContent/PlayerSource/src/TextTrackStub.js new file mode 100644 index 0000000000..e9aebfc22e --- /dev/null +++ b/submodules/TelegramUniversalVideoContent/PlayerSource/src/TextTrackStub.js @@ -0,0 +1,85 @@ + +export class TextTrackStub extends EventTarget { + constructor(kind = '', label = '', language = '') { + super(); + this.kind = kind; + this.label = label; + this.language = language; + this.mode = 'disabled'; // 'disabled', 'hidden', or 'showing' + this.cues = new TextTrackCueListStub(); + this.activeCues = new TextTrackCueListStub(); + } + + addCue(cue) { + this.cues._add(cue); + } + + removeCue(cue) { + this.cues._remove(cue); + } +} + +export class TextTrackCueListStub { + constructor() { + this._cues = []; + } + + get length() { + return this._cues.length; + } + + item(index) { + return this._cues[index]; + } + + getCueById(id) { + return this._cues.find(cue => cue.id === id) || null; + } + + _add(cue) { + this._cues.push(cue); + } + + _remove(cue) { + const index = this._cues.indexOf(cue); + if (index !== -1) { + this._cues.splice(index, 1); + } + } + + [Symbol.iterator]() { + return this._cues[Symbol.iterator](); + } +} + +export class TextTrackListStub extends EventTarget { + constructor() { + super(); + this._tracks = []; + } + + get length() { + return this._tracks.length; + } + + item(index) { + return this._tracks[index]; + } + + _add(track) { + this._tracks.push(track); + this.dispatchEvent(new Event('addtrack')); + } + + _remove(track) { + const index = this._tracks.indexOf(track); + if (index !== -1) { + this._tracks.splice(index, 1); + this.dispatchEvent(new Event('removetrack')); + } + } + + [Symbol.iterator]() { + return this._tracks[Symbol.iterator](); + } +} diff --git a/submodules/TelegramUniversalVideoContent/PlayerSource/src/TimeRangesStub.js b/submodules/TelegramUniversalVideoContent/PlayerSource/src/TimeRangesStub.js new file mode 100644 index 0000000000..e8a9df021e --- /dev/null +++ b/submodules/TelegramUniversalVideoContent/PlayerSource/src/TimeRangesStub.js @@ -0,0 +1,74 @@ + +export class TimeRangesStub { + constructor() { + this._ranges = []; + } + + get length() { + return this._ranges.length; + } + + start(index) { + if (index < 0 || index >= this._ranges.length) { + throw new DOMException('Invalid index', 'IndexSizeError'); + } + return this._ranges[index].start; + } + + end(index) { + if (index < 0 || index >= this._ranges.length) { + throw new DOMException('Invalid index', 'IndexSizeError'); + } + return this._ranges[index].end; + } + + // Helper method to add a range + _addRange(start, end) { + this._ranges.push({ start, end }); + this._normalizeRanges(); + } + + // Helper method to remove ranges that overlap with a given range + _removeRange(start, end) { + let updatedRanges = []; + for (let range of this._ranges) { + if (range.end <= start || range.start >= end) { + // No overlap, keep the range as is + updatedRanges.push(range); + } else if (range.start < start && range.end > end) { + // The range fully covers the removal range, split into two ranges + updatedRanges.push({ start: range.start, end: start }); + updatedRanges.push({ start: end, end: range.end }); + } else if (range.start >= start && range.end <= end) { + // The range is entirely within the removal range, remove it + // Do not add to updatedRanges + } else if (range.start < start && range.end > start && range.end <= end) { + // The range overlaps with the removal range on the left + updatedRanges.push({ start: range.start, end: start }); + } else if (range.start >= start && range.start < end && range.end > end) { + // The range overlaps with the removal range on the right + updatedRanges.push({ start: end, end: range.end }); + } + } + this._ranges = updatedRanges; + } + + // Normalize and merge overlapping ranges + _normalizeRanges() { + this._ranges.sort((a, b) => a.start - b.start); + let normalized = []; + for (let range of this._ranges) { + if (normalized.length === 0) { + normalized.push(range); + } else { + let last = normalized[normalized.length - 1]; + if (range.start <= last.end) { + last.end = Math.max(last.end, range.end); + } else { + normalized.push(range); + } + } + } + this._ranges = normalized; + } +} \ No newline at end of file diff --git a/submodules/TelegramUniversalVideoContent/PlayerSource/src/VideoElementStub.js b/submodules/TelegramUniversalVideoContent/PlayerSource/src/VideoElementStub.js new file mode 100644 index 0000000000..fb3fe3e3a3 --- /dev/null +++ b/submodules/TelegramUniversalVideoContent/PlayerSource/src/VideoElementStub.js @@ -0,0 +1,173 @@ +import { TimeRangesStub } from "./TimeRangesStub.js" +import { TextTrackStub, TextTrackListStub } from "./TextTrackStub.js" + +export class VideoElementStub extends EventTarget { + constructor() { + super(); + + this.bridgeId = window.nextInternalId; + window.nextInternalId += 1; + window.bridgeObjectMap[this.bridgeId] = this; + + this._currentTime = 0.0; + this.duration = NaN; + this.paused = true; + this.playbackRate = 1.0; + this.volume = 1.0; + this.muted = false; + this.readyState = 0; + this.networkState = 0; + this.buffered = new TimeRangesStub(); + this.seeking = false; + this.loop = false; + this.autoplay = false; + this.controls = false; + this.error = null; + this.src = ''; + this.videoWidth = 0; + this.videoHeight = 0; + this.textTracks = new TextTrackListStub(); + this.isWaiting = false; + + window.bridgeInvokeAsync(this.bridgeId, "VideoElement", "constructor", { + }); + + setTimeout(() => { + this.readyState = 4; // HAVE_ENOUGH_DATA + this.dispatchEvent(new Event('loadedmetadata')); + this.dispatchEvent(new Event('loadeddata')); + this.dispatchEvent(new Event('canplay')); + this.dispatchEvent(new Event('canplaythrough')); + }, 0); + } + + get currentTime() { + return this._currentTime; + } + + set currentTime(value) { + if (this._currentTime != value) { + this._currentTime = value; + + this.dispatchEvent(new Event('seeking')); + + window.bridgeInvokeAsync(this.bridgeId, "VideoElement", "setCurrentTime", { + "currentTime": value + }).then((result) => { + this.dispatchEvent(new Event('seeked')); + }) + } + } + + bridgeUpdateBuffered(value) { + const updatedRanges = value; + var ranges = []; + for (var i = 0; i < updatedRanges.length; i += 2) { + ranges.push({ + start: updatedRanges[i], + end: updatedRanges[i + 1] + }); + } + this.buffered._ranges = ranges; + } + + bridgeUpdateStatus(dict) { + var paused = !dict["isPlaying"]; + var isWaiting = dict["isWaiting"]; + var currentTime = dict["currentTime"]; + + if (this.paused != paused) { + this.paused = paused; + + if (paused) { + this.dispatchEvent(new Event('pause')); + } else { + this.dispatchEvent(new Event('play')); + this.dispatchEvent(new Event('playing')); + } + } + + if (this.isWaiting != isWaiting) { + this.isWaiting = isWaiting; + if (isWaiting) { + this.dispatchEvent(new Event('waiting')); + } + } + + if (this._currentTime != currentTime) { + this._currentTime = currentTime; + this.dispatchEvent(new Event('timeupdate')); + } + } + + play() { + if (this.paused) { + return window.bridgeInvokeAsync(this.bridgeId, "VideoElement", "play", { + }).then((result) => { + this.dispatchEvent(new Event('play')); + this.dispatchEvent(new Event('playing')); + }) + } else { + return Promise.resolve(); + } + } + + pause() { + if (!this.paused) { + this.paused = true; + this.dispatchEvent(new Event('pause')); + + return window.bridgeInvokeAsync(this.bridgeId, "VideoElement", "pause", { + }).then((result) => { + }) + } + } + + canPlayType(type) { + return 'probably'; + } + + _getMedia() { + return window.mediaSourceMap[this.src]; + } + + /*_simulateTimeUpdate() { + if (this._isPlaying) { + // Simulate time progression + setTimeout(() => { + var bufferedEnd = 0.0; + + const media = this._getMedia(); + if (media) { + if (media.sourceBuffers.length != 0) { + this.buffered = media.sourceBuffers._buffers[0].buffered; + bufferedEnd = this.buffered.length == 0 ? 0 : this.buffered.end(this.buffered.length - 1); + } + } + + // Consume buffered data + if (this.currentTime < bufferedEnd) { + // Advance currentTime + this._currentTime += 0.1 * this.playbackRate; // Increment currentTime + this.dispatchEvent(new Event('timeupdate')); + + // Continue simulation + this._simulateTimeUpdate(); + } else { + console.log("Buffer underrun"); + // Buffer underrun + this._isPlaying = false; + this.paused = true; + this.dispatchEvent(new Event('waiting')); + // The player should react by buffering more data + } + }, 100); + } + }*/ + + addTextTrack(kind, label, language) { + const textTrack = new TextTrackStub(kind, label, language); + this.textTracks._add(textTrack); + return textTrack; + } +} diff --git a/submodules/TelegramUniversalVideoContent/PlayerSource/src/index.js b/submodules/TelegramUniversalVideoContent/PlayerSource/src/index.js new file mode 100644 index 0000000000..fe3af970f9 --- /dev/null +++ b/submodules/TelegramUniversalVideoContent/PlayerSource/src/index.js @@ -0,0 +1,234 @@ +import Hls from "hls.js"; +import { VideoElementStub } from "./VideoElementStub.js" +import { MediaSourceStub, SourceBufferStub } from "./MediaSourceStub.js" + +window.bridgeObjectMap = {}; +window.bridgeCallbackMap = {}; + +function bridgeInvokeAsync(bridgeId, className, methodName, params) { + var promiseResolve; + var promiseReject; + var result = new Promise(function(resolve, reject) { + promiseResolve = resolve; + promiseReject = reject; + }); + const callbackId = window.nextInternalId; + window.nextInternalId += 1; + window.bridgeCallbackMap[callbackId] = promiseResolve; + + if (window.webkit.messageHandlers) { + window.webkit.messageHandlers.performAction.postMessage({ + 'event': 'bridgeInvoke', + 'data': { + 'bridgeId': bridgeId, + 'className': className, + 'methodName': methodName, + 'params': params, + 'callbackId': callbackId + } + }); + } + + return result; +} +window.bridgeInvokeAsync = bridgeInvokeAsync + +export function bridgeInvokeCallback(callbackId, result) { + const callback = window.bridgeCallbackMap[callbackId]; + if (callback) { + callback(result); + } +} + +var useStubs = true; + +window.nextInternalId = 0; +window.mediaSourceMap = {}; + +// Replace the global MediaSource with our stub +if (useStubs && typeof window !== 'undefined') { + window.MediaSource = MediaSourceStub; + window.ManagedMediaSource = MediaSourceStub; + window.SourceBuffer = SourceBufferStub; + URL.createObjectURL = function(ms) { + const url = "blob:mock-media-source:" + ms.internalId; + window.mediaSourceMap[url] = ms; + return url; + }; +} + + +function postPlayerEvent(eventName, eventData) { + if (window.webkit && window.webkit.messageHandlers) { + window.webkit.messageHandlers.performAction.postMessage({'event': eventName, 'data': eventData}); + } +}; + +var video; +var hls; + +var isManifestParsed = false; +var isFirstFrameReady = false; + +var currentTimeUpdateTimeout = null; + +export function playerInitialize(params) { + video.muted = false; + + video.addEventListener('loadeddata', (event) => { + if (!isFirstFrameReady) { + isFirstFrameReady = true; + refreshPlayerStatus(); + } + }); + video.addEventListener("playing", function() { + refreshPlayerStatus(); + }); + video.addEventListener("pause", function() { + refreshPlayerStatus(); + }); + video.addEventListener("seeking", function() { + refreshPlayerStatus(); + }); + video.addEventListener("waiting", function() { + refreshPlayerStatus(); + }); + + hls = new Hls({ + startLevel: 0, + testBandwidth: false, + debug: params['debug'] || true, + autoStartLoad: false, + backBufferLength: 30, + maxBufferLength: 60, + maxMaxBufferLength: 60, + maxFragLookUpTolerance: 0.001, + nudgeMaxRetry: 10000 + }); + hls.on(Hls.Events.MANIFEST_PARSED, function() { + isManifestParsed = true; + refreshPlayerStatus(); + }); + + hls.on(Hls.Events.LEVEL_SWITCHED, function() { + refreshPlayerStatus(); + }); + hls.on(Hls.Events.LEVELS_UPDATED, function() { + refreshPlayerStatus(); + }); + + hls.loadSource('master.m3u8'); + hls.attachMedia(video); +} + +export function playerLoad(initialLevelIndex) { + hls.startLevel = initialLevelIndex; + hls.startLoad(-1, false); +} + +export function playerPlay() { + video.play(); +} + +export function playerPause() { + video.pause(); +} + +export function playerSetBaseRate(value) { + video.playbackRate = value; +} + +export function playerSetLevel(level) { + if (level >= 0) { + hls.currentLevel = level; + } else { + hls.currentLevel = -1; + } +} + +export function playerSeek(value) { + video.currentTime = value; +} + +export function playerSetIsMuted(value) { + video.muted = value; +} + +function getLevels() { + var levels = []; + for (var i = 0; i < hls.levels.length; i++) { + var level = hls.levels[i]; + levels.push({ + 'index': i, + 'bitrate': level.bitrate || 0, + 'width': level.width || 0, + 'height': level.height || 0 + }); + } + return levels; +} + +function refreshPlayerStatus() { + var isPlaying = false; + if (!video.paused && !video.ended && video.readyState > 2) { + isPlaying = true; + } + + postPlayerEvent('playerStatus', { + 'isReady': isManifestParsed, + 'isFirstFrameReady': isFirstFrameReady, + 'isPlaying': !video.paused, + 'rate': isPlaying ? video.playbackRate : 0.0, + 'defaultRate': video.playbackRate, + 'levels': getLevels(), + 'currentLevel': hls.currentLevel + }); + + refreshPlayerCurrentTime(); + + if (isPlaying) { + if (currentTimeUpdateTimeout == null) { + currentTimeUpdateTimeout = setTimeout(() => { + refreshPlayerCurrentTime(); + }, 200); + } + } else { + if(currentTimeUpdateTimeout != null){ + clearTimeout(currentTimeUpdateTimeout); + currentTimeUpdateTimeout = null; + } + } +} + +function refreshPlayerCurrentTime() { + postPlayerEvent('playerCurrentTime', { + 'value': video.currentTime + }); + currentTimeUpdateTimeout = setTimeout(() => { + refreshPlayerCurrentTime() + }, 200); +} + +window.onload = () => { + if (useStubs) { + video = new VideoElementStub(); + } else { + video = document.createElement('video'); + video.playsInline = true; + video.controls = true; + document.body.appendChild(video); + } + + postPlayerEvent('windowOnLoad', { + }); +}; + +window.playerInitialize = playerInitialize; +window.playerLoad = playerLoad; +window.playerPlay = playerPlay; +window.playerPause = playerPause; +window.playerSetBaseRate = playerSetBaseRate; +window.playerSetLevel = playerSetLevel; +window.playerSeek = playerSeek; +window.playerSetIsMuted = playerSetIsMuted; +window.bridgeInvokeCallback = bridgeInvokeCallback; \ No newline at end of file diff --git a/submodules/TelegramUniversalVideoContent/PlayerSource/src/style.css b/submodules/TelegramUniversalVideoContent/PlayerSource/src/style.css new file mode 100644 index 0000000000..4cf22426dc --- /dev/null +++ b/submodules/TelegramUniversalVideoContent/PlayerSource/src/style.css @@ -0,0 +1,15 @@ +html, body { + margin: 0; + padding: 0; + height: 100%; + width: 100%; + overflow: hidden; +} +video { + margin: 0; + padding: 0; + height: 100%; + width: 100%; + object-fit: fill; +} + diff --git a/submodules/TelegramUniversalVideoContent/PlayerSource/webpack.common.js b/submodules/TelegramUniversalVideoContent/PlayerSource/webpack.common.js new file mode 100644 index 0000000000..a8d8d45fc7 --- /dev/null +++ b/submodules/TelegramUniversalVideoContent/PlayerSource/webpack.common.js @@ -0,0 +1,35 @@ +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); + +module.exports = { + entry: { + index: './src/index.js', + }, + plugins: [ + new HtmlWebpackPlugin({ + title: 'Production', + scriptLoading: 'blocking', + }) + ], + output: { + filename: '[name].bundle.js', + path: path.resolve(__dirname, 'dist'), + clean: true, + publicPath: '', + }, + module: { + rules: [ + { + test: /\.js$/, + include: path.resolve(__dirname, 'src/index.js'), + }, + { + test: /\.css$/i, + use: [ + 'style-loader', + 'css-loader' + ], + }, + ], + }, +}; diff --git a/submodules/TelegramUniversalVideoContent/PlayerSource/webpack.dev.js b/submodules/TelegramUniversalVideoContent/PlayerSource/webpack.dev.js new file mode 100644 index 0000000000..bf02fafa86 --- /dev/null +++ b/submodules/TelegramUniversalVideoContent/PlayerSource/webpack.dev.js @@ -0,0 +1,10 @@ +const { merge } = require('webpack-merge'); +const common = require('./webpack.common.js'); + +module.exports = merge(common, { + mode: 'development', + devtool: 'source-map', + devServer: { + static: './dist', + }, +}); diff --git a/submodules/TelegramUniversalVideoContent/PlayerSource/webpack.prod.js b/submodules/TelegramUniversalVideoContent/PlayerSource/webpack.prod.js new file mode 100644 index 0000000000..5fa86d6a39 --- /dev/null +++ b/submodules/TelegramUniversalVideoContent/PlayerSource/webpack.prod.js @@ -0,0 +1,16 @@ +const { merge } = require('webpack-merge'); +const common = require('./webpack.common.js'); +const TerserPlugin = require('terser-webpack-plugin'); + +module.exports = merge(common, { + mode: 'production', + optimization: { + minimize: true, + minimizer: [new TerserPlugin({ + terserOptions: { + compress: true, + }, + })], + runtimeChunk: 'single', + }, +});