diff --git a/dist/naf-janus-adapter.js b/dist/naf-janus-adapter.js index 7e324c2..a4cffba 100644 --- a/dist/naf-janus-adapter.js +++ b/dist/naf-janus-adapter.js @@ -1,97 +1,11 @@ -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); -/******/ } -/******/ }; -/******/ -/******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ -/******/ // create a fake namespace object -/******/ // mode & 1: value is a module id, require it -/******/ // mode & 2: merge all properties of value into the ns -/******/ // mode & 4: return value when already ns object -/******/ // mode & 8|1: behave like require -/******/ __webpack_require__.t = function(value, mode) { -/******/ if(mode & 1) value = __webpack_require__(value); -/******/ if(mode & 8) return value; -/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; -/******/ var ns = Object.create(null); -/******/ __webpack_require__.r(ns); -/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); -/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); -/******/ return ns; -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = "./src/index.js"); -/******/ }) -/************************************************************************/ -/******/ ({ +/******/ (() => { // webpackBootstrap +/******/ var __webpack_modules__ = ({ -/***/ "./node_modules/minijanus/minijanus.js": -/*!*********************************************!*\ - !*** ./node_modules/minijanus/minijanus.js ***! - \*********************************************/ -/*! no static exports found */ -/***/ (function(module, exports) { +/***/ "./node_modules/@networked-aframe/minijanus/minijanus.js": +/*!***************************************************************!*\ + !*** ./node_modules/@networked-aframe/minijanus/minijanus.js ***! + \***************************************************************/ +/***/ ((module) => { /** * Represents a handle to a single Janus plugin on a Janus session. Each WebRTC connection to the Janus server will be @@ -106,8 +20,8 @@ function JanusPluginHandle(session) { } /** Attaches this handle to the Janus server and sets its ID. **/ -JanusPluginHandle.prototype.attach = function(plugin) { - var payload = { plugin: plugin, "force-bundle": true, "force-rtcp-mux": true }; +JanusPluginHandle.prototype.attach = function(plugin, loop_index) { + var payload = { plugin: plugin, loop_index: loop_index, "force-bundle": true, "force-rtcp-mux": true }; return this.session.send("attach", payload).then(resp => { this.id = resp.data.id; return resp; @@ -350,118 +264,1536 @@ module.exports = { /***/ }), -/***/ "./node_modules/sdp/sdp.js": -/*!*********************************!*\ - !*** ./node_modules/sdp/sdp.js ***! - \*********************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/* eslint-env node */ - - -// SDP helpers. -const SDPUtils = {}; - -// Generate an alphanumeric identifier for cname or mids. -// TODO: use UUIDs instead? https://gist.github.com/jed/982883 -SDPUtils.generateIdentifier = function() { - return Math.random().toString(36).substr(2, 10); -}; - -// The RTCP CNAME used by all peerconnections from the same JS. -SDPUtils.localCName = SDPUtils.generateIdentifier(); - -// Splits SDP into lines, dealing with both CRLF and LF. -SDPUtils.splitLines = function(blob) { - return blob.trim().split('\n').map(line => line.trim()); -}; -// Splits SDP into sessionpart and mediasections. Ensures CRLF. -SDPUtils.splitSections = function(blob) { - const parts = blob.split('\nm='); - return parts.map((part, index) => (index > 0 ? - 'm=' + part : part).trim() + '\r\n'); -}; - -// Returns the session description. -SDPUtils.getDescription = function(blob) { - const sections = SDPUtils.splitSections(blob); - return sections && sections[0]; -}; - -// Returns the individual media sections. -SDPUtils.getMediaSections = function(blob) { - const sections = SDPUtils.splitSections(blob); - sections.shift(); - return sections; +/***/ "./src/index.js": +/*!**********************!*\ + !*** ./src/index.js ***! + \**********************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } +function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } +function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } +function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i["return"] && (_r = _i["return"](), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } } +function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } +function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } +function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } +function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } +function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return exports; }; var exports = {}, Op = Object.prototype, hasOwn = Op.hasOwnProperty, defineProperty = Object.defineProperty || function (obj, key, desc) { obj[key] = desc.value; }, $Symbol = "function" == typeof Symbol ? Symbol : {}, iteratorSymbol = $Symbol.iterator || "@@iterator", asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator", toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag"; function define(obj, key, value) { return Object.defineProperty(obj, key, { value: value, enumerable: !0, configurable: !0, writable: !0 }), obj[key]; } try { define({}, ""); } catch (err) { define = function define(obj, key, value) { return obj[key] = value; }; } function wrap(innerFn, outerFn, self, tryLocsList) { var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator, generator = Object.create(protoGenerator.prototype), context = new Context(tryLocsList || []); return defineProperty(generator, "_invoke", { value: makeInvokeMethod(innerFn, self, context) }), generator; } function tryCatch(fn, obj, arg) { try { return { type: "normal", arg: fn.call(obj, arg) }; } catch (err) { return { type: "throw", arg: err }; } } exports.wrap = wrap; var ContinueSentinel = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var IteratorPrototype = {}; define(IteratorPrototype, iteratorSymbol, function () { return this; }); var getProto = Object.getPrototypeOf, NativeIteratorPrototype = getProto && getProto(getProto(values([]))); NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol) && (IteratorPrototype = NativeIteratorPrototype); var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype); function defineIteratorMethods(prototype) { ["next", "throw", "return"].forEach(function (method) { define(prototype, method, function (arg) { return this._invoke(method, arg); }); }); } function AsyncIterator(generator, PromiseImpl) { function invoke(method, arg, resolve, reject) { var record = tryCatch(generator[method], generator, arg); if ("throw" !== record.type) { var result = record.arg, value = result.value; return value && "object" == _typeof(value) && hasOwn.call(value, "__await") ? PromiseImpl.resolve(value.__await).then(function (value) { invoke("next", value, resolve, reject); }, function (err) { invoke("throw", err, resolve, reject); }) : PromiseImpl.resolve(value).then(function (unwrapped) { result.value = unwrapped, resolve(result); }, function (error) { return invoke("throw", error, resolve, reject); }); } reject(record.arg); } var previousPromise; defineProperty(this, "_invoke", { value: function value(method, arg) { function callInvokeWithMethodAndArg() { return new PromiseImpl(function (resolve, reject) { invoke(method, arg, resolve, reject); }); } return previousPromise = previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(innerFn, self, context) { var state = "suspendedStart"; return function (method, arg) { if ("executing" === state) throw new Error("Generator is already running"); if ("completed" === state) { if ("throw" === method) throw arg; return doneResult(); } for (context.method = method, context.arg = arg;;) { var delegate = context.delegate; if (delegate) { var delegateResult = maybeInvokeDelegate(delegate, context); if (delegateResult) { if (delegateResult === ContinueSentinel) continue; return delegateResult; } } if ("next" === context.method) context.sent = context._sent = context.arg;else if ("throw" === context.method) { if ("suspendedStart" === state) throw state = "completed", context.arg; context.dispatchException(context.arg); } else "return" === context.method && context.abrupt("return", context.arg); state = "executing"; var record = tryCatch(innerFn, self, context); if ("normal" === record.type) { if (state = context.done ? "completed" : "suspendedYield", record.arg === ContinueSentinel) continue; return { value: record.arg, done: context.done }; } "throw" === record.type && (state = "completed", context.method = "throw", context.arg = record.arg); } }; } function maybeInvokeDelegate(delegate, context) { var methodName = context.method, method = delegate.iterator[methodName]; if (undefined === method) return context.delegate = null, "throw" === methodName && delegate.iterator["return"] && (context.method = "return", context.arg = undefined, maybeInvokeDelegate(delegate, context), "throw" === context.method) || "return" !== methodName && (context.method = "throw", context.arg = new TypeError("The iterator does not provide a '" + methodName + "' method")), ContinueSentinel; var record = tryCatch(method, delegate.iterator, context.arg); if ("throw" === record.type) return context.method = "throw", context.arg = record.arg, context.delegate = null, ContinueSentinel; var info = record.arg; return info ? info.done ? (context[delegate.resultName] = info.value, context.next = delegate.nextLoc, "return" !== context.method && (context.method = "next", context.arg = undefined), context.delegate = null, ContinueSentinel) : info : (context.method = "throw", context.arg = new TypeError("iterator result is not an object"), context.delegate = null, ContinueSentinel); } function pushTryEntry(locs) { var entry = { tryLoc: locs[0] }; 1 in locs && (entry.catchLoc = locs[1]), 2 in locs && (entry.finallyLoc = locs[2], entry.afterLoc = locs[3]), this.tryEntries.push(entry); } function resetTryEntry(entry) { var record = entry.completion || {}; record.type = "normal", delete record.arg, entry.completion = record; } function Context(tryLocsList) { this.tryEntries = [{ tryLoc: "root" }], tryLocsList.forEach(pushTryEntry, this), this.reset(!0); } function values(iterable) { if (iterable) { var iteratorMethod = iterable[iteratorSymbol]; if (iteratorMethod) return iteratorMethod.call(iterable); if ("function" == typeof iterable.next) return iterable; if (!isNaN(iterable.length)) { var i = -1, next = function next() { for (; ++i < iterable.length;) if (hasOwn.call(iterable, i)) return next.value = iterable[i], next.done = !1, next; return next.value = undefined, next.done = !0, next; }; return next.next = next; } } return { next: doneResult }; } function doneResult() { return { value: undefined, done: !0 }; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, defineProperty(Gp, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), defineProperty(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"), exports.isGeneratorFunction = function (genFun) { var ctor = "function" == typeof genFun && genFun.constructor; return !!ctor && (ctor === GeneratorFunction || "GeneratorFunction" === (ctor.displayName || ctor.name)); }, exports.mark = function (genFun) { return Object.setPrototypeOf ? Object.setPrototypeOf(genFun, GeneratorFunctionPrototype) : (genFun.__proto__ = GeneratorFunctionPrototype, define(genFun, toStringTagSymbol, "GeneratorFunction")), genFun.prototype = Object.create(Gp), genFun; }, exports.awrap = function (arg) { return { __await: arg }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, asyncIteratorSymbol, function () { return this; }), exports.AsyncIterator = AsyncIterator, exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) { void 0 === PromiseImpl && (PromiseImpl = Promise); var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl); return exports.isGeneratorFunction(outerFn) ? iter : iter.next().then(function (result) { return result.done ? result.value : iter.next(); }); }, defineIteratorMethods(Gp), define(Gp, toStringTagSymbol, "Generator"), define(Gp, iteratorSymbol, function () { return this; }), define(Gp, "toString", function () { return "[object Generator]"; }), exports.keys = function (val) { var object = Object(val), keys = []; for (var key in object) keys.push(key); return keys.reverse(), function next() { for (; keys.length;) { var key = keys.pop(); if (key in object) return next.value = key, next.done = !1, next; } return next.done = !0, next; }; }, exports.values = values, Context.prototype = { constructor: Context, reset: function reset(skipTempReset) { if (this.prev = 0, this.next = 0, this.sent = this._sent = undefined, this.done = !1, this.delegate = null, this.method = "next", this.arg = undefined, this.tryEntries.forEach(resetTryEntry), !skipTempReset) for (var name in this) "t" === name.charAt(0) && hasOwn.call(this, name) && !isNaN(+name.slice(1)) && (this[name] = undefined); }, stop: function stop() { this.done = !0; var rootRecord = this.tryEntries[0].completion; if ("throw" === rootRecord.type) throw rootRecord.arg; return this.rval; }, dispatchException: function dispatchException(exception) { if (this.done) throw exception; var context = this; function handle(loc, caught) { return record.type = "throw", record.arg = exception, context.next = loc, caught && (context.method = "next", context.arg = undefined), !!caught; } for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i], record = entry.completion; if ("root" === entry.tryLoc) return handle("end"); if (entry.tryLoc <= this.prev) { var hasCatch = hasOwn.call(entry, "catchLoc"), hasFinally = hasOwn.call(entry, "finallyLoc"); if (hasCatch && hasFinally) { if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0); if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc); } else if (hasCatch) { if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0); } else { if (!hasFinally) throw new Error("try statement without catch or finally"); if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc); } } } }, abrupt: function abrupt(type, arg) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) { var finallyEntry = entry; break; } } finallyEntry && ("break" === type || "continue" === type) && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc && (finallyEntry = null); var record = finallyEntry ? finallyEntry.completion : {}; return record.type = type, record.arg = arg, finallyEntry ? (this.method = "next", this.next = finallyEntry.finallyLoc, ContinueSentinel) : this.complete(record); }, complete: function complete(record, afterLoc) { if ("throw" === record.type) throw record.arg; return "break" === record.type || "continue" === record.type ? this.next = record.arg : "return" === record.type ? (this.rval = this.arg = record.arg, this.method = "return", this.next = "end") : "normal" === record.type && afterLoc && (this.next = afterLoc), ContinueSentinel; }, finish: function finish(finallyLoc) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.finallyLoc === finallyLoc) return this.complete(entry.completion, entry.afterLoc), resetTryEntry(entry), ContinueSentinel; } }, "catch": function _catch(tryLoc) { for (var i = this.tryEntries.length - 1; i >= 0; --i) { var entry = this.tryEntries[i]; if (entry.tryLoc === tryLoc) { var record = entry.completion; if ("throw" === record.type) { var thrown = record.arg; resetTryEntry(entry); } return thrown; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(iterable, resultName, nextLoc) { return this.delegate = { iterator: values(iterable), resultName: resultName, nextLoc: nextLoc }, "next" === this.method && (this.arg = undefined), ContinueSentinel; } }, exports; } +function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } +function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } +/* global NAF */ +var mj = __webpack_require__(/*! @networked-aframe/minijanus */ "./node_modules/@networked-aframe/minijanus/minijanus.js"); +mj.JanusSession.prototype.sendOriginal = mj.JanusSession.prototype.send; +mj.JanusSession.prototype.send = function (type, signal) { + return this.sendOriginal(type, signal)["catch"](function (e) { + if (e.message && e.message.indexOf("timed out") > -1) { + console.error("web socket timed out"); + NAF.connection.adapter.reconnect(); + } else { + throw e; + } + }); }; - -// Returns lines that start with a certain prefix. -SDPUtils.matchPrefix = function(blob, prefix) { - return SDPUtils.splitLines(blob).filter(line => line.indexOf(prefix) === 0); +var sdpUtils = __webpack_require__(/*! sdp */ "./node_modules/sdp/sdp.js"); +//var debug = require("debug")("naf-janus-adapter:debug"); +//var warn = require("debug")("naf-janus-adapter:warn"); +//var error = require("debug")("naf-janus-adapter:error"); +var debug = console.log; +var warn = console.warn; +var error = console.error; +var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); +var SUBSCRIBE_TIMEOUT_MS = 15000; +function debounce(fn) { + var curr = Promise.resolve(); + return function () { + var _this = this; + var args = Array.prototype.slice.call(arguments); + curr = curr.then(function (_) { + return fn.apply(_this, args); + }); + }; +} +function randomUint() { + return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); +} +function untilDataChannelOpen(dataChannel) { + return new Promise(function (resolve, reject) { + if (dataChannel.readyState === "open") { + resolve(); + } else { + var resolver, rejector; + var clear = function clear() { + dataChannel.removeEventListener("open", resolver); + dataChannel.removeEventListener("error", rejector); + }; + resolver = function resolver() { + clear(); + resolve(); + }; + rejector = function rejector() { + clear(); + reject(); + }; + dataChannel.addEventListener("open", resolver); + dataChannel.addEventListener("error", rejector); + } + }); +} +var isH264VideoSupported = function () { + var video = document.createElement("video"); + return video.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"') !== ""; +}(); +var OPUS_PARAMETERS = { + // indicates that we want to enable DTX to elide silence packets + usedtx: 1, + // indicates that we prefer to receive mono audio (important for voip profile) + stereo: 0, + // indicates that we prefer to send mono audio (important for voip profile) + "sprop-stereo": 0 }; +var DEFAULT_PEER_CONNECTION_CONFIG = { + iceServers: [{ + urls: "stun:stun1.l.google.com:19302" + }, { + urls: "stun:stun2.l.google.com:19302" + }] +}; +var WS_NORMAL_CLOSURE = 1000; +var JanusAdapter = /*#__PURE__*/function () { + function JanusAdapter() { + _classCallCheck(this, JanusAdapter); + this.room = null; + // We expect the consumer to set a client id before connecting. + this.clientId = null; + this.joinToken = null; + this.serverUrl = null; + this.webRtcOptions = {}; + this.peerConnectionConfig = null; + this.ws = null; + this.session = null; + this.reliableTransport = "datachannel"; + this.unreliableTransport = "datachannel"; -// Parses an ICE candidate line. Sample input: -// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8 -// rport 55996" -// Input can be prefixed with a=. -SDPUtils.parseCandidate = function(line) { - let parts; - // Parse both variants. - if (line.indexOf('a=candidate:') === 0) { - parts = line.substring(12).split(' '); - } else { - parts = line.substring(10).split(' '); + // In the event the server restarts and all clients lose connection, reconnect with + // some random jitter added to prevent simultaneous reconnection requests. + this.initialReconnectionDelay = 1000 * Math.random(); + this.reconnectionDelay = this.initialReconnectionDelay; + this.reconnectionTimeout = null; + this.maxReconnectionAttempts = 10; + this.reconnectionAttempts = 0; + this.publisher = null; + this.occupants = {}; + this.leftOccupants = new Set(); + this.mediaStreams = {}; + this.localMediaStream = null; + this.pendingMediaRequests = new Map(); + this.blockedClients = new Map(); + this.frozenUpdates = new Map(); + this.timeOffsets = []; + this.serverTimeRequests = 0; + this.avgTimeOffset = 0; + this.onWebsocketOpen = this.onWebsocketOpen.bind(this); + this.onWebsocketClose = this.onWebsocketClose.bind(this); + this.onWebsocketMessage = this.onWebsocketMessage.bind(this); + this.onDataChannelMessage = this.onDataChannelMessage.bind(this); + this.onData = this.onData.bind(this); } + _createClass(JanusAdapter, [{ + key: "setServerUrl", + value: function setServerUrl(url) { + this.serverUrl = url; + } + }, { + key: "setApp", + value: function setApp(app) {} + }, { + key: "setRoom", + value: function setRoom(roomName) { + this.room = roomName; + } + }, { + key: "setJoinToken", + value: function setJoinToken(joinToken) { + this.joinToken = joinToken; + } + }, { + key: "setClientId", + value: function setClientId(clientId) { + this.clientId = clientId; + } + }, { + key: "setWebRtcOptions", + value: function setWebRtcOptions(options) { + this.webRtcOptions = options; + } + }, { + key: "setPeerConnectionConfig", + value: function setPeerConnectionConfig(peerConnectionConfig) { + this.peerConnectionConfig = peerConnectionConfig; + } + }, { + key: "setServerConnectListeners", + value: function setServerConnectListeners(successListener, failureListener) { + this.connectSuccess = successListener; + this.connectFailure = failureListener; + } + }, { + key: "setRoomOccupantListener", + value: function setRoomOccupantListener(occupantListener) { + this.onOccupantsChanged = occupantListener; + } + }, { + key: "setDataChannelListeners", + value: function setDataChannelListeners(openListener, closedListener, messageListener) { + this.onOccupantConnected = openListener; + this.onOccupantDisconnected = closedListener; + this.onOccupantMessage = messageListener; + } + }, { + key: "setReconnectionListeners", + value: function setReconnectionListeners(reconnectingListener, reconnectedListener, reconnectionErrorListener) { + // onReconnecting is called with the number of milliseconds until the next reconnection attempt + this.onReconnecting = reconnectingListener; + // onReconnected is called when the connection has been reestablished + this.onReconnected = reconnectedListener; + // onReconnectionError is called with an error when maxReconnectionAttempts has been reached + this.onReconnectionError = reconnectionErrorListener; + } + }, { + key: "setEventLoops", + value: function setEventLoops(loops) { + this.loops = loops; + } + }, { + key: "connect", + value: function connect() { + var _this2 = this; + debug("connecting to ".concat(this.serverUrl)); + var websocketConnection = new Promise(function (resolve, reject) { + _this2.ws = new WebSocket(_this2.serverUrl, "janus-protocol"); + _this2.session = new mj.JanusSession(_this2.ws.send.bind(_this2.ws), { + timeoutMs: 40000 + }); + _this2.ws.addEventListener("close", _this2.onWebsocketClose); + _this2.ws.addEventListener("message", _this2.onWebsocketMessage); + _this2.wsOnOpen = function () { + _this2.ws.removeEventListener("open", _this2.wsOnOpen); + _this2.onWebsocketOpen().then(resolve)["catch"](reject); + }; + _this2.ws.addEventListener("open", _this2.wsOnOpen); + }); + return Promise.all([websocketConnection, this.updateTimeOffset()]); + } + }, { + key: "disconnect", + value: function disconnect() { + debug("disconnecting"); + clearTimeout(this.reconnectionTimeout); + this.removeAllOccupants(); + this.leftOccupants = new Set(); + if (this.publisher) { + // Close the publisher peer connection. Which also detaches the plugin handle. + this.publisher.conn.close(); + this.publisher = null; + } + if (this.session) { + this.session.dispose(); + this.session = null; + } + if (this.ws) { + this.ws.removeEventListener("open", this.wsOnOpen); + this.ws.removeEventListener("close", this.onWebsocketClose); + this.ws.removeEventListener("message", this.onWebsocketMessage); + this.ws.close(); + this.ws = null; + } - const candidate = { - foundation: parts[0], - component: {1: 'rtp', 2: 'rtcp'}[parts[1]] || parts[1], - protocol: parts[2].toLowerCase(), - priority: parseInt(parts[3], 10), - ip: parts[4], - address: parts[4], // address is an alias for ip. - port: parseInt(parts[5], 10), - // skip parts[6] == 'typ' - type: parts[7], - }; - - for (let i = 8; i < parts.length; i += 2) { - switch (parts[i]) { - case 'raddr': - candidate.relatedAddress = parts[i + 1]; - break; - case 'rport': - candidate.relatedPort = parseInt(parts[i + 1], 10); - break; - case 'tcptype': - candidate.tcpType = parts[i + 1]; - break; - case 'ufrag': - candidate.ufrag = parts[i + 1]; // for backward compatibility. - candidate.usernameFragment = parts[i + 1]; - break; - default: // extension handling, in particular ufrag. Don't overwrite. - if (candidate[parts[i]] === undefined) { - candidate[parts[i]] = parts[i + 1]; + // Now that all RTCPeerConnection closed, be sure to not call + // reconnect() again via performDelayedReconnect if previous + // RTCPeerConnection was in the failed state. + if (this.delayedReconnectTimeout) { + clearTimeout(this.delayedReconnectTimeout); + this.delayedReconnectTimeout = null; + } + } + }, { + key: "isDisconnected", + value: function isDisconnected() { + return this.ws === null; + } + }, { + key: "onWebsocketOpen", + value: function () { + var _onWebsocketOpen = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() { + var addOccupantPromises, i, occupantId; + return _regeneratorRuntime().wrap(function _callee$(_context) { + while (1) switch (_context.prev = _context.next) { + case 0: + _context.next = 2; + return this.session.create(); + case 2: + _context.next = 4; + return this.createPublisher(); + case 4: + this.publisher = _context.sent; + // Call the naf connectSuccess callback before we start receiving WebRTC messages. + this.connectSuccess(this.clientId); + addOccupantPromises = []; + i = 0; + case 8: + if (!(i < this.publisher.initialOccupants.length)) { + _context.next = 16; + break; + } + occupantId = this.publisher.initialOccupants[i]; + if (!(occupantId === this.clientId)) { + _context.next = 12; + break; + } + return _context.abrupt("continue", 13); + case 12: + // Happens during non-graceful reconnects due to zombie sessions + addOccupantPromises.push(this.addOccupant(occupantId)); + case 13: + i++; + _context.next = 8; + break; + case 16: + _context.next = 18; + return Promise.all(addOccupantPromises); + case 18: + case "end": + return _context.stop(); + } + }, _callee, this); + })); + function onWebsocketOpen() { + return _onWebsocketOpen.apply(this, arguments); + } + return onWebsocketOpen; + }() + }, { + key: "onWebsocketClose", + value: function onWebsocketClose(event) { + var _this3 = this; + // The connection was closed successfully. Don't try to reconnect. + if (event.code === WS_NORMAL_CLOSURE) { + return; + } + console.warn("Janus websocket closed unexpectedly."); + if (this.onReconnecting) { + this.onReconnecting(this.reconnectionDelay); + } + this.reconnectionTimeout = setTimeout(function () { + return _this3.reconnect(); + }, this.reconnectionDelay); + } + }, { + key: "reconnect", + value: function reconnect() { + var _this4 = this; + // Dispose of all networked entities and other resources tied to the session. + this.disconnect(); + this.connect().then(function () { + _this4.reconnectionDelay = _this4.initialReconnectionDelay; + _this4.reconnectionAttempts = 0; + if (_this4.onReconnected) { + _this4.onReconnected(); } - break; + })["catch"](function (error) { + _this4.reconnectionDelay += 1000; + _this4.reconnectionAttempts++; + if (_this4.reconnectionAttempts > _this4.maxReconnectionAttempts && _this4.onReconnectionError) { + return _this4.onReconnectionError(new Error("Connection could not be reestablished, exceeded maximum number of reconnection attempts.")); + } + console.warn("Error during reconnect, retrying."); + console.warn(error); + if (_this4.onReconnecting) { + _this4.onReconnecting(_this4.reconnectionDelay); + } + _this4.reconnectionTimeout = setTimeout(function () { + return _this4.reconnect(); + }, _this4.reconnectionDelay); + }); } - } - return candidate; -}; - -// Translates a candidate object into SDP candidate attribute. -// This does not include the a= prefix! -SDPUtils.writeCandidate = function(candidate) { - const sdp = []; - sdp.push(candidate.foundation); + }, { + key: "performDelayedReconnect", + value: function performDelayedReconnect() { + var _this5 = this; + if (this.delayedReconnectTimeout) { + clearTimeout(this.delayedReconnectTimeout); + } + this.delayedReconnectTimeout = setTimeout(function () { + _this5.delayedReconnectTimeout = null; + _this5.reconnect(); + }, 10000); + } + }, { + key: "onWebsocketMessage", + value: function onWebsocketMessage(event) { + this.session.receive(JSON.parse(event.data)); + } + }, { + key: "addOccupant", + value: function () { + var _addOccupant = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(occupantId) { + var subscriber; + return _regeneratorRuntime().wrap(function _callee2$(_context2) { + while (1) switch (_context2.prev = _context2.next) { + case 0: + if (this.occupants[occupantId]) { + this.removeOccupant(occupantId); + } + this.leftOccupants["delete"](occupantId); + _context2.next = 4; + return this.createSubscriber(occupantId); + case 4: + subscriber = _context2.sent; + if (subscriber) { + _context2.next = 7; + break; + } + return _context2.abrupt("return"); + case 7: + this.occupants[occupantId] = subscriber; + this.setMediaStream(occupantId, subscriber.mediaStream); + + // Call the Networked AFrame callbacks for the new occupant. + this.onOccupantConnected(occupantId); + this.onOccupantsChanged(this.occupants); + return _context2.abrupt("return", subscriber); + case 12: + case "end": + return _context2.stop(); + } + }, _callee2, this); + })); + function addOccupant(_x) { + return _addOccupant.apply(this, arguments); + } + return addOccupant; + }() + }, { + key: "removeAllOccupants", + value: function removeAllOccupants() { + var _iterator = _createForOfIteratorHelper(Object.getOwnPropertyNames(this.occupants)), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var occupantId = _step.value; + this.removeOccupant(occupantId); + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + } + }, { + key: "removeOccupant", + value: function removeOccupant(occupantId) { + this.leftOccupants.add(occupantId); + if (this.occupants[occupantId]) { + // Close the subscriber peer connection. Which also detaches the plugin handle. + this.occupants[occupantId].conn.close(); + delete this.occupants[occupantId]; + } + if (this.mediaStreams[occupantId]) { + delete this.mediaStreams[occupantId]; + } + if (this.pendingMediaRequests.has(occupantId)) { + var msg = "The user disconnected before the media stream was resolved."; + this.pendingMediaRequests.get(occupantId).audio.reject(msg); + this.pendingMediaRequests.get(occupantId).video.reject(msg); + this.pendingMediaRequests["delete"](occupantId); + } - const component = candidate.component; - if (component === 'rtp') { - sdp.push(1); - } else if (component === 'rtcp') { + // Call the Networked AFrame callbacks for the removed occupant. + this.onOccupantDisconnected(occupantId); + this.onOccupantsChanged(this.occupants); + } + }, { + key: "associate", + value: function associate(conn, handle) { + var _this6 = this; + conn.addEventListener("icecandidate", function (ev) { + handle.sendTrickle(ev.candidate || null)["catch"](function (e) { + return error("Error trickling ICE: %o", e); + }); + }); + conn.addEventListener("iceconnectionstatechange", function (ev) { + if (conn.iceConnectionState === "connected") { + console.log("ICE state changed to connected"); + } + if (conn.iceConnectionState === "disconnected") { + console.warn("ICE state changed to disconnected"); + } + if (conn.iceConnectionState === "failed") { + console.warn("ICE failure detected. Reconnecting in 10s."); + _this6.performDelayedReconnect(); + } + }); + + // we have to debounce these because janus gets angry if you send it a new SDP before + // it's finished processing an existing SDP. in actuality, it seems like this is maybe + // too liberal and we need to wait some amount of time after an offer before sending another, + // but we don't currently know any good way of detecting exactly how long :( + conn.addEventListener("negotiationneeded", debounce(function (ev) { + debug("Sending new offer for handle: %o", handle); + var offer = conn.createOffer().then(_this6.configurePublisherSdp).then(_this6.fixSafariIceUFrag); + var local = offer.then(function (o) { + return conn.setLocalDescription(o); + }); + var remote = offer; + remote = remote.then(_this6.fixSafariIceUFrag).then(function (j) { + return handle.sendJsep(j); + }).then(function (r) { + return conn.setRemoteDescription(r.jsep); + }); + return Promise.all([local, remote])["catch"](function (e) { + return error("Error negotiating offer: %o", e); + }); + })); + handle.on("event", debounce(function (ev) { + var jsep = ev.jsep; + if (jsep && jsep.type == "offer") { + debug("Accepting new offer for handle: %o", handle); + var answer = conn.setRemoteDescription(_this6.configureSubscriberSdp(jsep)).then(function (_) { + return conn.createAnswer(); + }).then(_this6.fixSafariIceUFrag); + var local = answer.then(function (a) { + return conn.setLocalDescription(a); + }); + var remote = answer.then(function (j) { + return handle.sendJsep(j); + }); + return Promise.all([local, remote])["catch"](function (e) { + return error("Error negotiating answer: %o", e); + }); + } else { + // some other kind of event, nothing to do + return null; + } + })); + } + }, { + key: "createPublisher", + value: function () { + var _createPublisher = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3() { + var _this7 = this; + var handle, conn, webrtcup, reliableChannel, unreliableChannel, message, err, initialOccupants; + return _regeneratorRuntime().wrap(function _callee3$(_context3) { + while (1) switch (_context3.prev = _context3.next) { + case 0: + handle = new mj.JanusPluginHandle(this.session); + conn = new RTCPeerConnection(this.peerConnectionConfig || DEFAULT_PEER_CONNECTION_CONFIG); + debug("pub waiting for sfu"); + _context3.next = 5; + return handle.attach("janus.plugin.sfu", this.loops && this.clientId ? parseInt(this.clientId) % this.loops : undefined); + case 5: + this.associate(conn, handle); + debug("pub waiting for data channels & webrtcup"); + webrtcup = new Promise(function (resolve) { + return handle.on("webrtcup", resolve); + }); // Unreliable datachannel: sending and receiving component updates. + // Reliable datachannel: sending and recieving entity instantiations. + reliableChannel = conn.createDataChannel("reliable", { + ordered: true + }); + unreliableChannel = conn.createDataChannel("unreliable", { + ordered: false, + maxRetransmits: 0 + }); + reliableChannel.addEventListener("message", function (e) { + return _this7.onDataChannelMessage(e, "janus-reliable"); + }); + unreliableChannel.addEventListener("message", function (e) { + return _this7.onDataChannelMessage(e, "janus-unreliable"); + }); + _context3.next = 14; + return webrtcup; + case 14: + _context3.next = 16; + return untilDataChannelOpen(reliableChannel); + case 16: + _context3.next = 18; + return untilDataChannelOpen(unreliableChannel); + case 18: + // doing this here is sort of a hack around chrome renegotiation weirdness -- + // if we do it prior to webrtcup, chrome on gear VR will sometimes put a + // renegotiation offer in flight while the first offer was still being + // processed by janus. we should find some more principled way to figure out + // when janus is done in the future. + if (this.localMediaStream) { + this.localMediaStream.getTracks().forEach(function (track) { + conn.addTrack(track, _this7.localMediaStream); + }); + } + + // Handle all of the join and leave events. + handle.on("event", function (ev) { + var data = ev.plugindata.data; + if (data.event == "join" && data.room_id == _this7.room) { + if (_this7.delayedReconnectTimeout) { + // Don't create a new RTCPeerConnection, all RTCPeerConnection will be closed in less than 10s. + return; + } + _this7.addOccupant(data.user_id); + } else if (data.event == "leave" && data.room_id == _this7.room) { + _this7.removeOccupant(data.user_id); + } else if (data.event == "blocked") { + document.body.dispatchEvent(new CustomEvent("blocked", { + detail: { + clientId: data.by + } + })); + } else if (data.event == "unblocked") { + document.body.dispatchEvent(new CustomEvent("unblocked", { + detail: { + clientId: data.by + } + })); + } else if (data.event === "data") { + _this7.onData(JSON.parse(data.body), "janus-event"); + } + }); + debug("pub waiting for join"); + + // Send join message to janus. Listen for join/leave messages. Automatically subscribe to all users' WebRTC data. + _context3.next = 23; + return this.sendJoin(handle, { + notifications: true, + data: true + }); + case 23: + message = _context3.sent; + if (message.plugindata.data.success) { + _context3.next = 29; + break; + } + err = message.plugindata.data.error; + console.error(err); + // We may get here because of an expired JWT. + // Close the connection ourself otherwise janus will close it after + // session_timeout because we didn't send any keepalive and this will + // trigger a delayed reconnect because of the iceconnectionstatechange + // listener for failure state. + // Even if the app code calls disconnect in case of error, disconnect + // won't close the peer connection because this.publisher is not set. + conn.close(); + throw err; + case 29: + initialOccupants = message.plugindata.data.response.users[this.room] || []; + if (initialOccupants.includes(this.clientId)) { + console.warn("Janus still has previous session for this client. Reconnecting in 10s."); + this.performDelayedReconnect(); + } + debug("publisher ready"); + return _context3.abrupt("return", { + handle: handle, + initialOccupants: initialOccupants, + reliableChannel: reliableChannel, + unreliableChannel: unreliableChannel, + conn: conn + }); + case 33: + case "end": + return _context3.stop(); + } + }, _callee3, this); + })); + function createPublisher() { + return _createPublisher.apply(this, arguments); + } + return createPublisher; + }() + }, { + key: "configurePublisherSdp", + value: function configurePublisherSdp(jsep) { + jsep.sdp = jsep.sdp.replace(/a=fmtp:(109|111).*\r\n/g, function (line, pt) { + var parameters = Object.assign(sdpUtils.parseFmtp(line), OPUS_PARAMETERS); + return sdpUtils.writeFmtp({ + payloadType: pt, + parameters: parameters + }); + }); + return jsep; + } + }, { + key: "configureSubscriberSdp", + value: function configureSubscriberSdp(jsep) { + // todo: consider cleaning up these hacks to use sdputils + if (!isH264VideoSupported) { + if (navigator.userAgent.indexOf("HeadlessChrome") !== -1) { + // HeadlessChrome (e.g. puppeteer) doesn't support webrtc video streams, so we remove those lines from the SDP. + jsep.sdp = jsep.sdp.replace(/m=video[^]*m=/, "m="); + } + } + + // TODO: Hack to get video working on Chrome for Android. https://groups.google.com/forum/#!topic/mozilla.dev.media/Ye29vuMTpo8 + if (navigator.userAgent.indexOf("Android") === -1) { + jsep.sdp = jsep.sdp.replace("a=rtcp-fb:107 goog-remb\r\n", "a=rtcp-fb:107 goog-remb\r\na=rtcp-fb:107 transport-cc\r\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\n"); + } else { + jsep.sdp = jsep.sdp.replace("a=rtcp-fb:107 goog-remb\r\n", "a=rtcp-fb:107 goog-remb\r\na=rtcp-fb:107 transport-cc\r\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f\r\n"); + } + return jsep; + } + }, { + key: "fixSafariIceUFrag", + value: function () { + var _fixSafariIceUFrag = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(jsep) { + return _regeneratorRuntime().wrap(function _callee4$(_context4) { + while (1) switch (_context4.prev = _context4.next) { + case 0: + // Safari produces a \n instead of an \r\n for the ice-ufrag. See https://github.com/meetecho/janus-gateway/issues/1818 + jsep.sdp = jsep.sdp.replace(/[^\r]\na=ice-ufrag/g, "\r\na=ice-ufrag"); + return _context4.abrupt("return", jsep); + case 2: + case "end": + return _context4.stop(); + } + }, _callee4); + })); + function fixSafariIceUFrag(_x2) { + return _fixSafariIceUFrag.apply(this, arguments); + } + return fixSafariIceUFrag; + }() + }, { + key: "createSubscriber", + value: function () { + var _createSubscriber = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5(occupantId) { + var _this8 = this; + var maxRetries, + handle, + conn, + webrtcFailed, + webrtcup, + mediaStream, + receivers, + _args5 = arguments; + return _regeneratorRuntime().wrap(function _callee5$(_context5) { + while (1) switch (_context5.prev = _context5.next) { + case 0: + maxRetries = _args5.length > 1 && _args5[1] !== undefined ? _args5[1] : 5; + if (!this.leftOccupants.has(occupantId)) { + _context5.next = 4; + break; + } + console.warn(occupantId + ": cancelled occupant connection, occupant left before subscription negotation."); + return _context5.abrupt("return", null); + case 4: + handle = new mj.JanusPluginHandle(this.session); + conn = new RTCPeerConnection(this.peerConnectionConfig || DEFAULT_PEER_CONNECTION_CONFIG); + debug(occupantId + ": sub waiting for sfu"); + _context5.next = 9; + return handle.attach("janus.plugin.sfu", this.loops ? parseInt(occupantId) % this.loops : undefined); + case 9: + this.associate(conn, handle); + debug(occupantId + ": sub waiting for join"); + if (!this.leftOccupants.has(occupantId)) { + _context5.next = 15; + break; + } + conn.close(); + console.warn(occupantId + ": cancelled occupant connection, occupant left after attach"); + return _context5.abrupt("return", null); + case 15: + webrtcFailed = false; + webrtcup = new Promise(function (resolve) { + var leftInterval = setInterval(function () { + if (_this8.leftOccupants.has(occupantId)) { + clearInterval(leftInterval); + resolve(); + } + }, 1000); + var timeout = setTimeout(function () { + clearInterval(leftInterval); + webrtcFailed = true; + resolve(); + }, SUBSCRIBE_TIMEOUT_MS); + handle.on("webrtcup", function () { + clearTimeout(timeout); + clearInterval(leftInterval); + resolve(); + }); + }); // Send join message to janus. Don't listen for join/leave messages. Subscribe to the occupant's media. + // Janus should send us an offer for this occupant's media in response to this. + _context5.next = 19; + return this.sendJoin(handle, { + media: occupantId + }); + case 19: + if (!this.leftOccupants.has(occupantId)) { + _context5.next = 23; + break; + } + conn.close(); + console.warn(occupantId + ": cancelled occupant connection, occupant left after join"); + return _context5.abrupt("return", null); + case 23: + debug(occupantId + ": sub waiting for webrtcup"); + _context5.next = 26; + return webrtcup; + case 26: + if (!this.leftOccupants.has(occupantId)) { + _context5.next = 30; + break; + } + conn.close(); + console.warn(occupantId + ": cancel occupant connection, occupant left during or after webrtcup"); + return _context5.abrupt("return", null); + case 30: + if (!webrtcFailed) { + _context5.next = 39; + break; + } + conn.close(); + if (!(maxRetries > 0)) { + _context5.next = 37; + break; + } + console.warn(occupantId + ": webrtc up timed out, retrying"); + return _context5.abrupt("return", this.createSubscriber(occupantId, maxRetries - 1)); + case 37: + console.warn(occupantId + ": webrtc up timed out"); + return _context5.abrupt("return", null); + case 39: + if (!(isSafari && !this._iOSHackDelayedInitialPeer)) { + _context5.next = 43; + break; + } + _context5.next = 42; + return new Promise(function (resolve) { + return setTimeout(resolve, 3000); + }); + case 42: + this._iOSHackDelayedInitialPeer = true; + case 43: + mediaStream = new MediaStream(); + receivers = conn.getReceivers(); + receivers.forEach(function (receiver) { + if (receiver.track) { + mediaStream.addTrack(receiver.track); + } + }); + if (mediaStream.getTracks().length === 0) { + mediaStream = null; + } + debug(occupantId + ": subscriber ready"); + return _context5.abrupt("return", { + handle: handle, + mediaStream: mediaStream, + conn: conn + }); + case 49: + case "end": + return _context5.stop(); + } + }, _callee5, this); + })); + function createSubscriber(_x3) { + return _createSubscriber.apply(this, arguments); + } + return createSubscriber; + }() + }, { + key: "sendJoin", + value: function sendJoin(handle, subscribe) { + return handle.sendMessage({ + kind: "join", + room_id: this.room, + user_id: this.clientId, + subscribe: subscribe, + token: this.joinToken + }); + } + }, { + key: "toggleFreeze", + value: function toggleFreeze() { + if (this.frozen) { + this.unfreeze(); + } else { + this.freeze(); + } + } + }, { + key: "freeze", + value: function freeze() { + this.frozen = true; + } + }, { + key: "unfreeze", + value: function unfreeze() { + this.frozen = false; + this.flushPendingUpdates(); + } + }, { + key: "dataForUpdateMultiMessage", + value: function dataForUpdateMultiMessage(networkId, message) { + // "d" is an array of entity datas, where each item in the array represents a unique entity and contains + // metadata for the entity, and an array of components that have been updated on the entity. + // This method finds the data corresponding to the given networkId. + for (var i = 0, l = message.data.d.length; i < l; i++) { + var data = message.data.d[i]; + if (data.networkId === networkId) { + return data; + } + } + return null; + } + }, { + key: "getPendingData", + value: function getPendingData(networkId, message) { + if (!message) return null; + var data = message.dataType === "um" ? this.dataForUpdateMultiMessage(networkId, message) : message.data; + + // Ignore messages relating to users who have disconnected since freezing, their entities + // will have aleady been removed by NAF. + // Note that delete messages have no "owner" so we have to check for that as well. + if (data.owner && !this.occupants[data.owner]) return null; + + // Ignore messages from users that we may have blocked while frozen. + if (data.owner && this.blockedClients.has(data.owner)) return null; + return data; + } + + // Used externally + }, { + key: "getPendingDataForNetworkId", + value: function getPendingDataForNetworkId(networkId) { + return this.getPendingData(networkId, this.frozenUpdates.get(networkId)); + } + }, { + key: "flushPendingUpdates", + value: function flushPendingUpdates() { + var _iterator2 = _createForOfIteratorHelper(this.frozenUpdates), + _step2; + try { + for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { + var _step2$value = _slicedToArray(_step2.value, 2), + networkId = _step2$value[0], + message = _step2$value[1]; + var data = this.getPendingData(networkId, message); + if (!data) continue; + + // Override the data type on "um" messages types, since we extract entity updates from "um" messages into + // individual frozenUpdates in storeSingleMessage. + var dataType = message.dataType === "um" ? "u" : message.dataType; + this.onOccupantMessage(null, dataType, data, message.source); + } + } catch (err) { + _iterator2.e(err); + } finally { + _iterator2.f(); + } + this.frozenUpdates.clear(); + } + }, { + key: "storeMessage", + value: function storeMessage(message) { + if (message.dataType === "um") { + // UpdateMulti + for (var i = 0, l = message.data.d.length; i < l; i++) { + this.storeSingleMessage(message, i); + } + } else { + this.storeSingleMessage(message); + } + } + }, { + key: "storeSingleMessage", + value: function storeSingleMessage(message, index) { + var data = index !== undefined ? message.data.d[index] : message.data; + var dataType = message.dataType; + var source = message.source; + var networkId = data.networkId; + if (!this.frozenUpdates.has(networkId)) { + this.frozenUpdates.set(networkId, message); + } else { + var storedMessage = this.frozenUpdates.get(networkId); + var storedData = storedMessage.dataType === "um" ? this.dataForUpdateMultiMessage(networkId, storedMessage) : storedMessage.data; + + // Avoid updating components if the entity data received did not come from the current owner. + var isOutdatedMessage = data.lastOwnerTime < storedData.lastOwnerTime; + var isContemporaneousMessage = data.lastOwnerTime === storedData.lastOwnerTime; + if (isOutdatedMessage || isContemporaneousMessage && storedData.owner > data.owner) { + return; + } + if (dataType === "r") { + var createdWhileFrozen = storedData && storedData.isFirstSync; + if (createdWhileFrozen) { + // If the entity was created and deleted while frozen, don't bother conveying anything to the consumer. + this.frozenUpdates["delete"](networkId); + } else { + // Delete messages override any other messages for this entity + this.frozenUpdates.set(networkId, message); + } + } else { + // merge in component updates + if (storedData.components && data.components) { + Object.assign(storedData.components, data.components); + } + } + } + } + }, { + key: "onDataChannelMessage", + value: function onDataChannelMessage(e, source) { + this.onData(JSON.parse(e.data), source); + } + }, { + key: "onData", + value: function onData(message, source) { + if (debug.enabled) { + debug("DC in: ".concat(message)); + } + if (!message.dataType) return; + message.source = source; + if (this.frozen) { + this.storeMessage(message); + } else { + this.onOccupantMessage(null, message.dataType, message.data, message.source); + } + } + }, { + key: "shouldStartConnectionTo", + value: function shouldStartConnectionTo(client) { + return true; + } + }, { + key: "startStreamConnection", + value: function startStreamConnection(client) {} + }, { + key: "closeStreamConnection", + value: function closeStreamConnection(client) {} + }, { + key: "getConnectStatus", + value: function getConnectStatus(clientId) { + return this.occupants[clientId] ? NAF.adapters.IS_CONNECTED : NAF.adapters.NOT_CONNECTED; + } + }, { + key: "updateTimeOffset", + value: function () { + var _updateTimeOffset = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee6() { + var _this9 = this; + var clientSentTime, res, precision, serverReceivedTime, clientReceivedTime, serverTime, timeOffset; + return _regeneratorRuntime().wrap(function _callee6$(_context6) { + while (1) switch (_context6.prev = _context6.next) { + case 0: + if (!this.isDisconnected()) { + _context6.next = 2; + break; + } + return _context6.abrupt("return"); + case 2: + clientSentTime = Date.now(); + _context6.next = 5; + return fetch(document.location.href, { + method: "HEAD", + cache: "no-cache" + }); + case 5: + res = _context6.sent; + precision = 1000; + serverReceivedTime = new Date(res.headers.get("Date")).getTime() + precision / 2; + clientReceivedTime = Date.now(); + serverTime = serverReceivedTime + (clientReceivedTime - clientSentTime) / 2; + timeOffset = serverTime - clientReceivedTime; + this.serverTimeRequests++; + if (this.serverTimeRequests <= 10) { + this.timeOffsets.push(timeOffset); + } else { + this.timeOffsets[this.serverTimeRequests % 10] = timeOffset; + } + this.avgTimeOffset = this.timeOffsets.reduce(function (acc, offset) { + return acc += offset; + }, 0) / this.timeOffsets.length; + if (this.serverTimeRequests > 10) { + debug("new server time offset: ".concat(this.avgTimeOffset, "ms")); + setTimeout(function () { + return _this9.updateTimeOffset(); + }, 5 * 60 * 1000); // Sync clock every 5 minutes. + } else { + this.updateTimeOffset(); + } + case 15: + case "end": + return _context6.stop(); + } + }, _callee6, this); + })); + function updateTimeOffset() { + return _updateTimeOffset.apply(this, arguments); + } + return updateTimeOffset; + }() + }, { + key: "getServerTime", + value: function getServerTime() { + return Date.now() + this.avgTimeOffset; + } + }, { + key: "getMediaStream", + value: function getMediaStream(clientId) { + var _this10 = this; + var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "audio"; + if (this.mediaStreams[clientId]) { + debug("Already had ".concat(type, " for ").concat(clientId)); + return Promise.resolve(this.mediaStreams[clientId][type]); + } else { + debug("Waiting on ".concat(type, " for ").concat(clientId)); + if (!this.pendingMediaRequests.has(clientId)) { + this.pendingMediaRequests.set(clientId, {}); + var audioPromise = new Promise(function (resolve, reject) { + _this10.pendingMediaRequests.get(clientId).audio = { + resolve: resolve, + reject: reject + }; + }); + var videoPromise = new Promise(function (resolve, reject) { + _this10.pendingMediaRequests.get(clientId).video = { + resolve: resolve, + reject: reject + }; + }); + this.pendingMediaRequests.get(clientId).audio.promise = audioPromise; + this.pendingMediaRequests.get(clientId).video.promise = videoPromise; + audioPromise["catch"](function (e) { + return console.warn("".concat(clientId, " getMediaStream Audio Error"), e); + }); + videoPromise["catch"](function (e) { + return console.warn("".concat(clientId, " getMediaStream Video Error"), e); + }); + } + return this.pendingMediaRequests.get(clientId)[type].promise; + } + } + }, { + key: "setMediaStream", + value: function setMediaStream(clientId, stream) { + // Safari doesn't like it when you use single a mixed media stream where one of the tracks is inactive, so we + // split the tracks into two streams. + var audioStream = new MediaStream(); + try { + stream.getAudioTracks().forEach(function (track) { + return audioStream.addTrack(track); + }); + } catch (e) { + console.warn("".concat(clientId, " setMediaStream Audio Error"), e); + } + var videoStream = new MediaStream(); + try { + stream.getVideoTracks().forEach(function (track) { + return videoStream.addTrack(track); + }); + } catch (e) { + console.warn("".concat(clientId, " setMediaStream Video Error"), e); + } + this.mediaStreams[clientId] = { + audio: audioStream, + video: videoStream + }; + + // Resolve the promise for the user's media stream if it exists. + if (this.pendingMediaRequests.has(clientId)) { + this.pendingMediaRequests.get(clientId).audio.resolve(audioStream); + this.pendingMediaRequests.get(clientId).video.resolve(videoStream); + } + } + }, { + key: "setLocalMediaStream", + value: function () { + var _setLocalMediaStream = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee7(stream) { + var _this11 = this; + var existingSenders, newSenders, tracks, _loop, i; + return _regeneratorRuntime().wrap(function _callee7$(_context8) { + while (1) switch (_context8.prev = _context8.next) { + case 0: + if (!(this.publisher && this.publisher.conn)) { + _context8.next = 12; + break; + } + existingSenders = this.publisher.conn.getSenders(); + newSenders = []; + tracks = stream.getTracks(); + _loop = /*#__PURE__*/_regeneratorRuntime().mark(function _loop() { + var t, sender; + return _regeneratorRuntime().wrap(function _loop$(_context7) { + while (1) switch (_context7.prev = _context7.next) { + case 0: + t = tracks[i]; + sender = existingSenders.find(function (s) { + return s.track != null && s.track.kind == t.kind; + }); + if (!(sender != null)) { + _context7.next = 14; + break; + } + if (!sender.replaceTrack) { + _context7.next = 9; + break; + } + _context7.next = 6; + return sender.replaceTrack(t); + case 6: + // Workaround https://bugzilla.mozilla.org/show_bug.cgi?id=1576771 + if (t.kind === "video" && t.enabled && navigator.userAgent.toLowerCase().indexOf('firefox') > -1) { + t.enabled = false; + setTimeout(function () { + return t.enabled = true; + }, 1000); + } + _context7.next = 11; + break; + case 9: + // Fallback for browsers that don't support replaceTrack. At this time of this writing + // most browsers support it, and testing this code path seems to not work properly + // in Chrome anymore. + stream.removeTrack(sender.track); + stream.addTrack(t); + case 11: + newSenders.push(sender); + _context7.next = 15; + break; + case 14: + newSenders.push(_this11.publisher.conn.addTrack(t, stream)); + case 15: + case "end": + return _context7.stop(); + } + }, _loop); + }); + i = 0; + case 6: + if (!(i < tracks.length)) { + _context8.next = 11; + break; + } + return _context8.delegateYield(_loop(), "t0", 8); + case 8: + i++; + _context8.next = 6; + break; + case 11: + existingSenders.forEach(function (s) { + if (!newSenders.includes(s)) { + s.track.enabled = false; + } + }); + case 12: + this.localMediaStream = stream; + this.setMediaStream(this.clientId, stream); + case 14: + case "end": + return _context8.stop(); + } + }, _callee7, this); + })); + function setLocalMediaStream(_x4) { + return _setLocalMediaStream.apply(this, arguments); + } + return setLocalMediaStream; + }() + }, { + key: "enableMicrophone", + value: function enableMicrophone(enabled) { + if (this.publisher && this.publisher.conn) { + this.publisher.conn.getSenders().forEach(function (s) { + if (s.track.kind == "audio") { + s.track.enabled = enabled; + } + }); + } + } + }, { + key: "sendData", + value: function sendData(clientId, dataType, data) { + if (!this.publisher) { + console.warn("sendData called without a publisher"); + } else { + switch (this.unreliableTransport) { + case "websocket": + this.publisher.handle.sendMessage({ + kind: "data", + body: JSON.stringify({ + dataType: dataType, + data: data + }), + whom: clientId + }); + break; + case "datachannel": + this.publisher.unreliableChannel.send(JSON.stringify({ + clientId: clientId, + dataType: dataType, + data: data + })); + break; + default: + this.unreliableTransport(clientId, dataType, data); + break; + } + } + } + }, { + key: "sendDataGuaranteed", + value: function sendDataGuaranteed(clientId, dataType, data) { + if (!this.publisher) { + console.warn("sendDataGuaranteed called without a publisher"); + } else { + switch (this.reliableTransport) { + case "websocket": + this.publisher.handle.sendMessage({ + kind: "data", + body: JSON.stringify({ + dataType: dataType, + data: data + }), + whom: clientId + }); + break; + case "datachannel": + this.publisher.reliableChannel.send(JSON.stringify({ + clientId: clientId, + dataType: dataType, + data: data + })); + break; + default: + this.reliableTransport(clientId, dataType, data); + break; + } + } + } + }, { + key: "broadcastData", + value: function broadcastData(dataType, data) { + if (!this.publisher) { + console.warn("broadcastData called without a publisher"); + } else { + switch (this.unreliableTransport) { + case "websocket": + this.publisher.handle.sendMessage({ + kind: "data", + body: JSON.stringify({ + dataType: dataType, + data: data + }) + }); + break; + case "datachannel": + this.publisher.unreliableChannel.send(JSON.stringify({ + dataType: dataType, + data: data + })); + break; + default: + this.unreliableTransport(undefined, dataType, data); + break; + } + } + } + }, { + key: "broadcastDataGuaranteed", + value: function broadcastDataGuaranteed(dataType, data) { + if (!this.publisher) { + console.warn("broadcastDataGuaranteed called without a publisher"); + } else { + switch (this.reliableTransport) { + case "websocket": + this.publisher.handle.sendMessage({ + kind: "data", + body: JSON.stringify({ + dataType: dataType, + data: data + }) + }); + break; + case "datachannel": + this.publisher.reliableChannel.send(JSON.stringify({ + dataType: dataType, + data: data + })); + break; + default: + this.reliableTransport(undefined, dataType, data); + break; + } + } + } + }, { + key: "kick", + value: function kick(clientId, permsToken) { + return this.publisher.handle.sendMessage({ + kind: "kick", + room_id: this.room, + user_id: clientId, + token: permsToken + }).then(function () { + document.body.dispatchEvent(new CustomEvent("kicked", { + detail: { + clientId: clientId + } + })); + }); + } + }, { + key: "block", + value: function block(clientId) { + var _this12 = this; + return this.publisher.handle.sendMessage({ + kind: "block", + whom: clientId + }).then(function () { + _this12.blockedClients.set(clientId, true); + document.body.dispatchEvent(new CustomEvent("blocked", { + detail: { + clientId: clientId + } + })); + }); + } + }, { + key: "unblock", + value: function unblock(clientId) { + var _this13 = this; + return this.publisher.handle.sendMessage({ + kind: "unblock", + whom: clientId + }).then(function () { + _this13.blockedClients["delete"](clientId); + document.body.dispatchEvent(new CustomEvent("unblocked", { + detail: { + clientId: clientId + } + })); + }); + } + }]); + return JanusAdapter; +}(); +NAF.adapters.register("janus", JanusAdapter); +module.exports = JanusAdapter; + +/***/ }), + +/***/ "./node_modules/sdp/sdp.js": +/*!*********************************!*\ + !*** ./node_modules/sdp/sdp.js ***! + \*********************************/ +/***/ ((module) => { + +"use strict"; +/* eslint-env node */ + + +// SDP helpers. +const SDPUtils = {}; + +// Generate an alphanumeric identifier for cname or mids. +// TODO: use UUIDs instead? https://gist.github.com/jed/982883 +SDPUtils.generateIdentifier = function() { + return Math.random().toString(36).substring(2, 12); +}; + +// The RTCP CNAME used by all peerconnections from the same JS. +SDPUtils.localCName = SDPUtils.generateIdentifier(); + +// Splits SDP into lines, dealing with both CRLF and LF. +SDPUtils.splitLines = function(blob) { + return blob.trim().split('\n').map(line => line.trim()); +}; +// Splits SDP into sessionpart and mediasections. Ensures CRLF. +SDPUtils.splitSections = function(blob) { + const parts = blob.split('\nm='); + return parts.map((part, index) => (index > 0 ? + 'm=' + part : part).trim() + '\r\n'); +}; + +// Returns the session description. +SDPUtils.getDescription = function(blob) { + const sections = SDPUtils.splitSections(blob); + return sections && sections[0]; +}; + +// Returns the individual media sections. +SDPUtils.getMediaSections = function(blob) { + const sections = SDPUtils.splitSections(blob); + sections.shift(); + return sections; +}; + +// Returns lines that start with a certain prefix. +SDPUtils.matchPrefix = function(blob, prefix) { + return SDPUtils.splitLines(blob).filter(line => line.indexOf(prefix) === 0); +}; + +// Parses an ICE candidate line. Sample input: +// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8 +// rport 55996" +// Input can be prefixed with a=. +SDPUtils.parseCandidate = function(line) { + let parts; + // Parse both variants. + if (line.indexOf('a=candidate:') === 0) { + parts = line.substring(12).split(' '); + } else { + parts = line.substring(10).split(' '); + } + + const candidate = { + foundation: parts[0], + component: {1: 'rtp', 2: 'rtcp'}[parts[1]] || parts[1], + protocol: parts[2].toLowerCase(), + priority: parseInt(parts[3], 10), + ip: parts[4], + address: parts[4], // address is an alias for ip. + port: parseInt(parts[5], 10), + // skip parts[6] == 'typ' + type: parts[7], + }; + + for (let i = 8; i < parts.length; i += 2) { + switch (parts[i]) { + case 'raddr': + candidate.relatedAddress = parts[i + 1]; + break; + case 'rport': + candidate.relatedPort = parseInt(parts[i + 1], 10); + break; + case 'tcptype': + candidate.tcpType = parts[i + 1]; + break; + case 'ufrag': + candidate.ufrag = parts[i + 1]; // for backward compatibility. + candidate.usernameFragment = parts[i + 1]; + break; + default: // extension handling, in particular ufrag. Don't overwrite. + if (candidate[parts[i]] === undefined) { + candidate[parts[i]] = parts[i + 1]; + } + break; + } + } + return candidate; +}; + +// Translates a candidate object into SDP candidate attribute. +// This does not include the a= prefix! +SDPUtils.writeCandidate = function(candidate) { + const sdp = []; + sdp.push(candidate.foundation); + + const component = candidate.component; + if (component === 'rtp') { + sdp.push(1); + } else if (component === 'rtcp') { sdp.push(2); } else { sdp.push(component); @@ -496,13 +1828,13 @@ SDPUtils.writeCandidate = function(candidate) { // Sample input: // a=ice-options:foo bar SDPUtils.parseIceOptions = function(line) { - return line.substr(14).split(' '); + return line.substring(14).split(' '); }; // Parses a rtpmap line, returns RTCRtpCoddecParameters. Sample input: // a=rtpmap:111 opus/48000/2 SDPUtils.parseRtpMap = function(line) { - let parts = line.substr(9).split(' '); + let parts = line.substring(9).split(' '); const parsed = { payloadType: parseInt(parts.shift(), 10), // was: id }; @@ -533,11 +1865,12 @@ SDPUtils.writeRtpMap = function(codec) { // a=extmap:2 urn:ietf:params:rtp-hdrext:toffset // a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset SDPUtils.parseExtmap = function(line) { - const parts = line.substr(9).split(' '); + const parts = line.substring(9).split(' '); return { id: parseInt(parts[0], 10), direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv', uri: parts[1], + attributes: parts.slice(2).join(' '), }; }; @@ -548,7 +1881,9 @@ SDPUtils.writeExtmap = function(headerExtension) { (headerExtension.direction && headerExtension.direction !== 'sendrecv' ? '/' + headerExtension.direction : '') + - ' ' + headerExtension.uri + '\r\n'; + ' ' + headerExtension.uri + + (headerExtension.attributes ? ' ' + headerExtension.attributes : '') + + '\r\n'; }; // Parses a fmtp line, returns dictionary. Sample input: @@ -557,7 +1892,7 @@ SDPUtils.writeExtmap = function(headerExtension) { SDPUtils.parseFmtp = function(line) { const parsed = {}; let kv; - const parts = line.substr(line.indexOf(' ') + 1).split(';'); + const parts = line.substring(line.indexOf(' ') + 1).split(';'); for (let j = 0; j < parts.length; j++) { kv = parts[j].trim().split('='); parsed[kv[0].trim()] = kv[1]; @@ -589,7 +1924,7 @@ SDPUtils.writeFmtp = function(codec) { // Parses a rtcp-fb line, returns RTCPRtcpFeedback object. Sample input: // a=rtcp-fb:98 nack rpsi SDPUtils.parseRtcpFb = function(line) { - const parts = line.substr(line.indexOf(' ') + 1).split(' '); + const parts = line.substring(line.indexOf(' ') + 1).split(' '); return { type: parts.shift(), parameter: parts.join(' '), @@ -619,14 +1954,14 @@ SDPUtils.writeRtcpFb = function(codec) { SDPUtils.parseSsrcMedia = function(line) { const sp = line.indexOf(' '); const parts = { - ssrc: parseInt(line.substr(7, sp - 7), 10), + ssrc: parseInt(line.substring(7, sp), 10), }; const colon = line.indexOf(':', sp); if (colon > -1) { - parts.attribute = line.substr(sp + 1, colon - sp - 1); - parts.value = line.substr(colon + 1); + parts.attribute = line.substring(sp + 1, colon); + parts.value = line.substring(colon + 1); } else { - parts.attribute = line.substr(sp + 1); + parts.attribute = line.substring(sp + 1); } return parts; }; @@ -634,7 +1969,7 @@ SDPUtils.parseSsrcMedia = function(line) { // Parse a ssrc-group line (see RFC 5576). Sample input: // a=ssrc-group:semantics 12 34 SDPUtils.parseSsrcGroup = function(line) { - const parts = line.substr(13).split(' '); + const parts = line.substring(13).split(' '); return { semantics: parts.shift(), ssrcs: parts.map(ssrc => parseInt(ssrc, 10)), @@ -646,13 +1981,13 @@ SDPUtils.parseSsrcGroup = function(line) { SDPUtils.getMid = function(mediaSection) { const mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0]; if (mid) { - return mid.substr(6); + return mid.substring(6); } }; // Parses a fingerprint line for DTLS-SRTP. SDPUtils.parseFingerprint = function(line) { - const parts = line.substr(14).split(' '); + const parts = line.substring(14).split(' '); return { algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge. value: parts[1].toUpperCase(), // the definition is upper-case in RFC 4572. @@ -684,7 +2019,7 @@ SDPUtils.writeDtlsParameters = function(params, setupType) { // Parses a=crypto lines into // https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members SDPUtils.parseCryptoLine = function(line) { - const parts = line.substr(9).split(' '); + const parts = line.substring(9).split(' '); return { tag: parseInt(parts[0], 10), cryptoSuite: parts[1], @@ -709,7 +2044,7 @@ SDPUtils.parseCryptoKeyParams = function(keyParams) { if (keyParams.indexOf('inline:') !== 0) { return null; } - const parts = keyParams.substr(7).split('|'); + const parts = keyParams.substring(7).split('|'); return { keyMethod: 'inline', keySalt: parts[0], @@ -747,8 +2082,8 @@ SDPUtils.getIceParameters = function(mediaSection, sessionpart) { return null; } return { - usernameFragment: ufrag.substr(12), - password: pwd.substr(10), + usernameFragment: ufrag.substring(12), + password: pwd.substring(10), }; }; @@ -772,6 +2107,7 @@ SDPUtils.parseRtpParameters = function(mediaSection) { }; const lines = SDPUtils.splitLines(mediaSection); const mline = lines[0].split(' '); + description.profile = mline[2]; for (let i = 3; i < mline.length; i++) { // find all codecs from mline[3..] const pt = mline[i]; const rtpmapline = SDPUtils.matchPrefix( @@ -800,8 +2136,21 @@ SDPUtils.parseRtpParameters = function(mediaSection) { SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(line => { description.headerExtensions.push(SDPUtils.parseExtmap(line)); }); - // FIXME: parse rtcp. - return description; + const wildcardRtcpFb = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-fb:* ') + .map(SDPUtils.parseRtcpFb); + description.codecs.forEach(codec => { + wildcardRtcpFb.forEach(fb=> { + const duplicate = codec.rtcpFeedback.find(existingFeedback => { + return existingFeedback.type === fb.type && + existingFeedback.parameter === fb.parameter; + }); + if (!duplicate) { + codec.rtcpFeedback.push(fb); + } + }); + }); + // FIXME: parse rtcp. + return description; }; // Generates parts of the SDP media section describing the capabilities / @@ -812,7 +2161,7 @@ SDPUtils.writeRtpDescription = function(kind, caps) { // Build the mline. sdp += 'm=' + kind + ' '; sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs. - sdp += ' UDP/TLS/RTP/SAVPF '; + sdp += ' ' + (caps.profile || 'UDP/TLS/RTP/SAVPF') + ' '; sdp += caps.codecs.map(codec => { if (codec.preferredPayloadType !== undefined) { return codec.preferredPayloadType; @@ -865,7 +2214,7 @@ SDPUtils.parseRtpEncodingParameters = function(mediaSection) { const flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID') .map(line => { - const parts = line.substr(17).split(' '); + const parts = line.substring(17).split(' '); return parts.map(part => parseInt(part, 10)); }); if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) { @@ -902,10 +2251,10 @@ SDPUtils.parseRtpEncodingParameters = function(mediaSection) { let bandwidth = SDPUtils.matchPrefix(mediaSection, 'b='); if (bandwidth.length) { if (bandwidth[0].indexOf('b=TIAS:') === 0) { - bandwidth = parseInt(bandwidth[0].substr(7), 10); + bandwidth = parseInt(bandwidth[0].substring(7), 10); } else if (bandwidth[0].indexOf('b=AS:') === 0) { // use formula from JSEP to convert b=AS to TIAS value. - bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95 + bandwidth = parseInt(bandwidth[0].substring(5), 10) * 1000 * 0.95 - (50 * 40 * 8); } else { bandwidth = undefined; @@ -967,7 +2316,7 @@ SDPUtils.parseMsid = function(mediaSection) { let parts; const spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:'); if (spec.length === 1) { - parts = spec[0].substr(7).split(' '); + parts = spec[0].substring(7).split(' '); return {stream: parts[0], track: parts[1]}; } const planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:') @@ -987,7 +2336,7 @@ SDPUtils.parseSctpDescription = function(mediaSection) { const maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:'); let maxMessageSize; if (maxSizeLine.length > 0) { - maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10); + maxMessageSize = parseInt(maxSizeLine[0].substring(19), 10); } if (isNaN(maxMessageSize)) { maxMessageSize = 65536; @@ -995,7 +2344,7 @@ SDPUtils.parseSctpDescription = function(mediaSection) { const sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:'); if (sctpPort.length > 0) { return { - port: parseInt(sctpPort[0].substr(12), 10), + port: parseInt(sctpPort[0].substring(12), 10), protocol: mline.fmt, maxMessageSize, }; @@ -1003,7 +2352,7 @@ SDPUtils.parseSctpDescription = function(mediaSection) { const sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:'); if (sctpMapLines.length > 0) { const parts = sctpMapLines[0] - .substr(10) + .substring(10) .split(' '); return { port: parseInt(parts[0], 10), @@ -1044,7 +2393,7 @@ SDPUtils.writeSctpDescription = function(media, sctp) { // recommends using a cryptographically random +ve 64-bit value // but right now this should be acceptable and within the right range SDPUtils.generateSessionId = function() { - return Math.random().toString().substr(2, 21); + return Math.random().toString().substr(2, 22); }; // Write boiler plate for start of SDP @@ -1079,7 +2428,7 @@ SDPUtils.getDirection = function(mediaSection, sessionpart) { case 'a=sendonly': case 'a=recvonly': case 'a=inactive': - return lines[i].substr(2); + return lines[i].substring(2); default: // FIXME: What should happen here? } @@ -1093,7 +2442,7 @@ SDPUtils.getDirection = function(mediaSection, sessionpart) { SDPUtils.getKind = function(mediaSection) { const lines = SDPUtils.splitLines(mediaSection); const mline = lines[0].split(' '); - return mline[0].substr(2); + return mline[0].substring(2); }; SDPUtils.isRejected = function(mediaSection) { @@ -1102,7 +2451,7 @@ SDPUtils.isRejected = function(mediaSection) { SDPUtils.parseMLine = function(mediaSection) { const lines = SDPUtils.splitLines(mediaSection); - const parts = lines[0].substr(2).split(' '); + const parts = lines[0].substring(2).split(' '); return { kind: parts[0], port: parseInt(parts[1], 10), @@ -1113,7 +2462,7 @@ SDPUtils.parseMLine = function(mediaSection) { SDPUtils.parseOLine = function(mediaSection) { const line = SDPUtils.matchPrefix(mediaSection, 'o=')[0]; - const parts = line.substr(2).split(' '); + const parts = line.substring(2).split(' '); return { username: parts[0], sessionId: parts[1], @@ -1145,1111 +2494,41 @@ if (true) { } -/***/ }), - -/***/ "./src/index.js": -/*!**********************!*\ - !*** ./src/index.js ***! - \**********************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } - -var mj = __webpack_require__(/*! minijanus */ "./node_modules/minijanus/minijanus.js"); -mj.JanusSession.prototype.sendOriginal = mj.JanusSession.prototype.send; -mj.JanusSession.prototype.send = function (type, signal) { - return this.sendOriginal(type, signal).catch(e => { - if (e.message && e.message.indexOf("timed out") > -1) { - console.error("web socket timed out"); - NAF.connection.adapter.reconnect(); - } else { - throw e; - } - }); -}; - -var sdpUtils = __webpack_require__(/*! sdp */ "./node_modules/sdp/sdp.js"); -//var debug = require("debug")("naf-janus-adapter:debug"); -//var warn = require("debug")("naf-janus-adapter:warn"); -//var error = require("debug")("naf-janus-adapter:error"); -var debug = console.log; -var warn = console.warn; -var error = console.error; -var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); - -const SUBSCRIBE_TIMEOUT_MS = 15000; - -function debounce(fn) { - var curr = Promise.resolve(); - return function () { - var args = Array.prototype.slice.call(arguments); - curr = curr.then(_ => fn.apply(this, args)); - }; -} - -function randomUint() { - return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); -} - -function untilDataChannelOpen(dataChannel) { - return new Promise((resolve, reject) => { - if (dataChannel.readyState === "open") { - resolve(); - } else { - let resolver, rejector; - - const clear = () => { - dataChannel.removeEventListener("open", resolver); - dataChannel.removeEventListener("error", rejector); - }; - - resolver = () => { - clear(); - resolve(); - }; - rejector = () => { - clear(); - reject(); - }; - - dataChannel.addEventListener("open", resolver); - dataChannel.addEventListener("error", rejector); - } - }); -} - -const isH264VideoSupported = (() => { - const video = document.createElement("video"); - return video.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"') !== ""; -})(); - -const OPUS_PARAMETERS = { - // indicates that we want to enable DTX to elide silence packets - usedtx: 1, - // indicates that we prefer to receive mono audio (important for voip profile) - stereo: 0, - // indicates that we prefer to send mono audio (important for voip profile) - "sprop-stereo": 0 -}; - -const DEFAULT_PEER_CONNECTION_CONFIG = { - iceServers: [{ urls: "stun:stun1.l.google.com:19302" }, { urls: "stun:stun2.l.google.com:19302" }] -}; - -const WS_NORMAL_CLOSURE = 1000; - -class JanusAdapter { - constructor() { - this.room = null; - // We expect the consumer to set a client id before connecting. - this.clientId = null; - this.joinToken = null; - - this.serverUrl = null; - this.webRtcOptions = {}; - this.peerConnectionConfig = null; - this.ws = null; - this.session = null; - this.reliableTransport = "datachannel"; - this.unreliableTransport = "datachannel"; - - // In the event the server restarts and all clients lose connection, reconnect with - // some random jitter added to prevent simultaneous reconnection requests. - this.initialReconnectionDelay = 1000 * Math.random(); - this.reconnectionDelay = this.initialReconnectionDelay; - this.reconnectionTimeout = null; - this.maxReconnectionAttempts = 10; - this.reconnectionAttempts = 0; - - this.publisher = null; - this.occupants = {}; - this.leftOccupants = new Set(); - this.mediaStreams = {}; - this.localMediaStream = null; - this.pendingMediaRequests = new Map(); - - this.blockedClients = new Map(); - this.frozenUpdates = new Map(); - - this.timeOffsets = []; - this.serverTimeRequests = 0; - this.avgTimeOffset = 0; - - this.onWebsocketOpen = this.onWebsocketOpen.bind(this); - this.onWebsocketClose = this.onWebsocketClose.bind(this); - this.onWebsocketMessage = this.onWebsocketMessage.bind(this); - this.onDataChannelMessage = this.onDataChannelMessage.bind(this); - this.onData = this.onData.bind(this); - } - - setServerUrl(url) { - this.serverUrl = url; - } - - setApp(app) {} - - setRoom(roomName) { - this.room = roomName; - } - - setJoinToken(joinToken) { - this.joinToken = joinToken; - } - - setClientId(clientId) { - this.clientId = clientId; - } - - setWebRtcOptions(options) { - this.webRtcOptions = options; - } - - setPeerConnectionConfig(peerConnectionConfig) { - this.peerConnectionConfig = peerConnectionConfig; - } - - setServerConnectListeners(successListener, failureListener) { - this.connectSuccess = successListener; - this.connectFailure = failureListener; - } - - setRoomOccupantListener(occupantListener) { - this.onOccupantsChanged = occupantListener; - } - - setDataChannelListeners(openListener, closedListener, messageListener) { - this.onOccupantConnected = openListener; - this.onOccupantDisconnected = closedListener; - this.onOccupantMessage = messageListener; - } - - setReconnectionListeners(reconnectingListener, reconnectedListener, reconnectionErrorListener) { - // onReconnecting is called with the number of milliseconds until the next reconnection attempt - this.onReconnecting = reconnectingListener; - // onReconnected is called when the connection has been reestablished - this.onReconnected = reconnectedListener; - // onReconnectionError is called with an error when maxReconnectionAttempts has been reached - this.onReconnectionError = reconnectionErrorListener; - } - - connect() { - debug(`connecting to ${this.serverUrl}`); - - const websocketConnection = new Promise((resolve, reject) => { - this.ws = new WebSocket(this.serverUrl, "janus-protocol"); - - this.session = new mj.JanusSession(this.ws.send.bind(this.ws), { timeoutMs: 40000 }); - - this.ws.addEventListener("close", this.onWebsocketClose); - this.ws.addEventListener("message", this.onWebsocketMessage); - - this.wsOnOpen = () => { - this.ws.removeEventListener("open", this.wsOnOpen); - this.onWebsocketOpen().then(resolve).catch(reject); - }; - - this.ws.addEventListener("open", this.wsOnOpen); - }); - - return Promise.all([websocketConnection, this.updateTimeOffset()]); - } - - disconnect() { - debug(`disconnecting`); - - clearTimeout(this.reconnectionTimeout); - - this.removeAllOccupants(); - this.leftOccupants = new Set(); - - if (this.publisher) { - // Close the publisher peer connection. Which also detaches the plugin handle. - this.publisher.conn.close(); - this.publisher = null; - } - - if (this.session) { - this.session.dispose(); - this.session = null; - } - - if (this.ws) { - this.ws.removeEventListener("open", this.wsOnOpen); - this.ws.removeEventListener("close", this.onWebsocketClose); - this.ws.removeEventListener("message", this.onWebsocketMessage); - this.ws.close(); - this.ws = null; - } - - // Now that all RTCPeerConnection closed, be sure to not call - // reconnect() again via performDelayedReconnect if previous - // RTCPeerConnection was in the failed state. - if (this.delayedReconnectTimeout) { - clearTimeout(this.delayedReconnectTimeout); - this.delayedReconnectTimeout = null; - } - } - - isDisconnected() { - return this.ws === null; - } - - onWebsocketOpen() { - var _this = this; - - return _asyncToGenerator(function* () { - // Create the Janus Session - yield _this.session.create(); - - // Attach the SFU Plugin and create a RTCPeerConnection for the publisher. - // The publisher sends audio and opens two bidirectional data channels. - // One reliable datachannel and one unreliable. - _this.publisher = yield _this.createPublisher(); - - // Call the naf connectSuccess callback before we start receiving WebRTC messages. - _this.connectSuccess(_this.clientId); - - const addOccupantPromises = []; - - for (let i = 0; i < _this.publisher.initialOccupants.length; i++) { - const occupantId = _this.publisher.initialOccupants[i]; - if (occupantId === _this.clientId) continue; // Happens during non-graceful reconnects due to zombie sessions - addOccupantPromises.push(_this.addOccupant(occupantId)); - } - - yield Promise.all(addOccupantPromises); - })(); - } - - onWebsocketClose(event) { - // The connection was closed successfully. Don't try to reconnect. - if (event.code === WS_NORMAL_CLOSURE) { - return; - } - - console.warn("Janus websocket closed unexpectedly."); - if (this.onReconnecting) { - this.onReconnecting(this.reconnectionDelay); - } - - this.reconnectionTimeout = setTimeout(() => this.reconnect(), this.reconnectionDelay); - } - - reconnect() { - // Dispose of all networked entities and other resources tied to the session. - this.disconnect(); - - this.connect().then(() => { - this.reconnectionDelay = this.initialReconnectionDelay; - this.reconnectionAttempts = 0; - - if (this.onReconnected) { - this.onReconnected(); - } - }).catch(error => { - this.reconnectionDelay += 1000; - this.reconnectionAttempts++; - - if (this.reconnectionAttempts > this.maxReconnectionAttempts && this.onReconnectionError) { - return this.onReconnectionError(new Error("Connection could not be reestablished, exceeded maximum number of reconnection attempts.")); - } - - console.warn("Error during reconnect, retrying."); - console.warn(error); - - if (this.onReconnecting) { - this.onReconnecting(this.reconnectionDelay); - } - - this.reconnectionTimeout = setTimeout(() => this.reconnect(), this.reconnectionDelay); - }); - } - - performDelayedReconnect() { - if (this.delayedReconnectTimeout) { - clearTimeout(this.delayedReconnectTimeout); - } - - this.delayedReconnectTimeout = setTimeout(() => { - this.delayedReconnectTimeout = null; - this.reconnect(); - }, 10000); - } - - onWebsocketMessage(event) { - this.session.receive(JSON.parse(event.data)); - } - - addOccupant(occupantId) { - var _this2 = this; - - return _asyncToGenerator(function* () { - if (_this2.occupants[occupantId]) { - _this2.removeOccupant(occupantId); - } - - _this2.leftOccupants.delete(occupantId); - - var subscriber = yield _this2.createSubscriber(occupantId); - - if (!subscriber) return; - - _this2.occupants[occupantId] = subscriber; - - _this2.setMediaStream(occupantId, subscriber.mediaStream); - - // Call the Networked AFrame callbacks for the new occupant. - _this2.onOccupantConnected(occupantId); - _this2.onOccupantsChanged(_this2.occupants); - - return subscriber; - })(); - } - - removeAllOccupants() { - for (const occupantId of Object.getOwnPropertyNames(this.occupants)) { - this.removeOccupant(occupantId); - } - } - - removeOccupant(occupantId) { - this.leftOccupants.add(occupantId); - - if (this.occupants[occupantId]) { - // Close the subscriber peer connection. Which also detaches the plugin handle. - this.occupants[occupantId].conn.close(); - delete this.occupants[occupantId]; - } - - if (this.mediaStreams[occupantId]) { - delete this.mediaStreams[occupantId]; - } - - if (this.pendingMediaRequests.has(occupantId)) { - const msg = "The user disconnected before the media stream was resolved."; - this.pendingMediaRequests.get(occupantId).audio.reject(msg); - this.pendingMediaRequests.get(occupantId).video.reject(msg); - this.pendingMediaRequests.delete(occupantId); - } - - // Call the Networked AFrame callbacks for the removed occupant. - this.onOccupantDisconnected(occupantId); - this.onOccupantsChanged(this.occupants); - } - - associate(conn, handle) { - conn.addEventListener("icecandidate", ev => { - handle.sendTrickle(ev.candidate || null).catch(e => error("Error trickling ICE: %o", e)); - }); - conn.addEventListener("iceconnectionstatechange", ev => { - if (conn.iceConnectionState === "connected") { - console.log("ICE state changed to connected"); - } - if (conn.iceConnectionState === "disconnected") { - console.warn("ICE state changed to disconnected"); - } - if (conn.iceConnectionState === "failed") { - console.warn("ICE failure detected. Reconnecting in 10s."); - this.performDelayedReconnect(); - } - }); - - // we have to debounce these because janus gets angry if you send it a new SDP before - // it's finished processing an existing SDP. in actuality, it seems like this is maybe - // too liberal and we need to wait some amount of time after an offer before sending another, - // but we don't currently know any good way of detecting exactly how long :( - conn.addEventListener("negotiationneeded", debounce(ev => { - debug("Sending new offer for handle: %o", handle); - var offer = conn.createOffer().then(this.configurePublisherSdp).then(this.fixSafariIceUFrag); - var local = offer.then(o => conn.setLocalDescription(o)); - var remote = offer; - - remote = remote.then(this.fixSafariIceUFrag).then(j => handle.sendJsep(j)).then(r => conn.setRemoteDescription(r.jsep)); - return Promise.all([local, remote]).catch(e => error("Error negotiating offer: %o", e)); - })); - handle.on("event", debounce(ev => { - var jsep = ev.jsep; - if (jsep && jsep.type == "offer") { - debug("Accepting new offer for handle: %o", handle); - var answer = conn.setRemoteDescription(this.configureSubscriberSdp(jsep)).then(_ => conn.createAnswer()).then(this.fixSafariIceUFrag); - var local = answer.then(a => conn.setLocalDescription(a)); - var remote = answer.then(j => handle.sendJsep(j)); - return Promise.all([local, remote]).catch(e => error("Error negotiating answer: %o", e)); - } else { - // some other kind of event, nothing to do - return null; - } - })); - } - - createPublisher() { - var _this3 = this; - - return _asyncToGenerator(function* () { - var handle = new mj.JanusPluginHandle(_this3.session); - var conn = new RTCPeerConnection(_this3.peerConnectionConfig || DEFAULT_PEER_CONNECTION_CONFIG); - - debug("pub waiting for sfu"); - yield handle.attach("janus.plugin.sfu"); - - _this3.associate(conn, handle); - - debug("pub waiting for data channels & webrtcup"); - var webrtcup = new Promise(function (resolve) { - return handle.on("webrtcup", resolve); - }); - - // Unreliable datachannel: sending and receiving component updates. - // Reliable datachannel: sending and recieving entity instantiations. - var reliableChannel = conn.createDataChannel("reliable", { ordered: true }); - var unreliableChannel = conn.createDataChannel("unreliable", { - ordered: false, - maxRetransmits: 0 - }); - - reliableChannel.addEventListener("message", function (e) { - return _this3.onDataChannelMessage(e, "janus-reliable"); - }); - unreliableChannel.addEventListener("message", function (e) { - return _this3.onDataChannelMessage(e, "janus-unreliable"); - }); - - yield webrtcup; - yield untilDataChannelOpen(reliableChannel); - yield untilDataChannelOpen(unreliableChannel); - - // doing this here is sort of a hack around chrome renegotiation weirdness -- - // if we do it prior to webrtcup, chrome on gear VR will sometimes put a - // renegotiation offer in flight while the first offer was still being - // processed by janus. we should find some more principled way to figure out - // when janus is done in the future. - if (_this3.localMediaStream) { - _this3.localMediaStream.getTracks().forEach(function (track) { - conn.addTrack(track, _this3.localMediaStream); - }); - } - - // Handle all of the join and leave events. - handle.on("event", function (ev) { - var data = ev.plugindata.data; - if (data.event == "join" && data.room_id == _this3.room) { - if (_this3.delayedReconnectTimeout) { - // Don't create a new RTCPeerConnection, all RTCPeerConnection will be closed in less than 10s. - return; - } - _this3.addOccupant(data.user_id); - } else if (data.event == "leave" && data.room_id == _this3.room) { - _this3.removeOccupant(data.user_id); - } else if (data.event == "blocked") { - document.body.dispatchEvent(new CustomEvent("blocked", { detail: { clientId: data.by } })); - } else if (data.event == "unblocked") { - document.body.dispatchEvent(new CustomEvent("unblocked", { detail: { clientId: data.by } })); - } else if (data.event === "data") { - _this3.onData(JSON.parse(data.body), "janus-event"); - } - }); - - debug("pub waiting for join"); - - // Send join message to janus. Listen for join/leave messages. Automatically subscribe to all users' WebRTC data. - var message = yield _this3.sendJoin(handle, { - notifications: true, - data: true - }); - - if (!message.plugindata.data.success) { - const err = message.plugindata.data.error; - console.error(err); - // We may get here because of an expired JWT. - // Close the connection ourself otherwise janus will close it after - // session_timeout because we didn't send any keepalive and this will - // trigger a delayed reconnect because of the iceconnectionstatechange - // listener for failure state. - // Even if the app code calls disconnect in case of error, disconnect - // won't close the peer connection because this.publisher is not set. - conn.close(); - throw err; - } - - var initialOccupants = message.plugindata.data.response.users[_this3.room] || []; - - if (initialOccupants.includes(_this3.clientId)) { - console.warn("Janus still has previous session for this client. Reconnecting in 10s."); - _this3.performDelayedReconnect(); - } - - debug("publisher ready"); - return { - handle, - initialOccupants, - reliableChannel, - unreliableChannel, - conn - }; - })(); - } - - configurePublisherSdp(jsep) { - jsep.sdp = jsep.sdp.replace(/a=fmtp:(109|111).*\r\n/g, (line, pt) => { - const parameters = Object.assign(sdpUtils.parseFmtp(line), OPUS_PARAMETERS); - return sdpUtils.writeFmtp({ payloadType: pt, parameters: parameters }); - }); - return jsep; - } - - configureSubscriberSdp(jsep) { - // todo: consider cleaning up these hacks to use sdputils - if (!isH264VideoSupported) { - if (navigator.userAgent.indexOf("HeadlessChrome") !== -1) { - // HeadlessChrome (e.g. puppeteer) doesn't support webrtc video streams, so we remove those lines from the SDP. - jsep.sdp = jsep.sdp.replace(/m=video[^]*m=/, "m="); - } - } - - // TODO: Hack to get video working on Chrome for Android. https://groups.google.com/forum/#!topic/mozilla.dev.media/Ye29vuMTpo8 - if (navigator.userAgent.indexOf("Android") === -1) { - jsep.sdp = jsep.sdp.replace("a=rtcp-fb:107 goog-remb\r\n", "a=rtcp-fb:107 goog-remb\r\na=rtcp-fb:107 transport-cc\r\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\n"); - } else { - jsep.sdp = jsep.sdp.replace("a=rtcp-fb:107 goog-remb\r\n", "a=rtcp-fb:107 goog-remb\r\na=rtcp-fb:107 transport-cc\r\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f\r\n"); - } - return jsep; - } - - fixSafariIceUFrag(jsep) { - return _asyncToGenerator(function* () { - // Safari produces a \n instead of an \r\n for the ice-ufrag. See https://github.com/meetecho/janus-gateway/issues/1818 - jsep.sdp = jsep.sdp.replace(/[^\r]\na=ice-ufrag/g, "\r\na=ice-ufrag"); - return jsep; - })(); - } - - createSubscriber(occupantId, maxRetries = 5) { - var _this4 = this; - - return _asyncToGenerator(function* () { - if (_this4.leftOccupants.has(occupantId)) { - console.warn(occupantId + ": cancelled occupant connection, occupant left before subscription negotation."); - return null; - } - - var handle = new mj.JanusPluginHandle(_this4.session); - var conn = new RTCPeerConnection(_this4.peerConnectionConfig || DEFAULT_PEER_CONNECTION_CONFIG); - - debug(occupantId + ": sub waiting for sfu"); - yield handle.attach("janus.plugin.sfu"); - - _this4.associate(conn, handle); - - debug(occupantId + ": sub waiting for join"); - - if (_this4.leftOccupants.has(occupantId)) { - conn.close(); - console.warn(occupantId + ": cancelled occupant connection, occupant left after attach"); - return null; - } - - let webrtcFailed = false; - - const webrtcup = new Promise(function (resolve) { - const leftInterval = setInterval(function () { - if (_this4.leftOccupants.has(occupantId)) { - clearInterval(leftInterval); - resolve(); - } - }, 1000); - - const timeout = setTimeout(function () { - clearInterval(leftInterval); - webrtcFailed = true; - resolve(); - }, SUBSCRIBE_TIMEOUT_MS); - - handle.on("webrtcup", function () { - clearTimeout(timeout); - clearInterval(leftInterval); - resolve(); - }); - }); - - // Send join message to janus. Don't listen for join/leave messages. Subscribe to the occupant's media. - // Janus should send us an offer for this occupant's media in response to this. - yield _this4.sendJoin(handle, { media: occupantId }); - - if (_this4.leftOccupants.has(occupantId)) { - conn.close(); - console.warn(occupantId + ": cancelled occupant connection, occupant left after join"); - return null; - } - - debug(occupantId + ": sub waiting for webrtcup"); - yield webrtcup; - - if (_this4.leftOccupants.has(occupantId)) { - conn.close(); - console.warn(occupantId + ": cancel occupant connection, occupant left during or after webrtcup"); - return null; - } - - if (webrtcFailed) { - conn.close(); - if (maxRetries > 0) { - console.warn(occupantId + ": webrtc up timed out, retrying"); - return _this4.createSubscriber(occupantId, maxRetries - 1); - } else { - console.warn(occupantId + ": webrtc up timed out"); - return null; - } - } - - if (isSafari && !_this4._iOSHackDelayedInitialPeer) { - // HACK: the first peer on Safari during page load can fail to work if we don't - // wait some time before continuing here. See: https://github.com/mozilla/hubs/pull/1692 - yield new Promise(function (resolve) { - return setTimeout(resolve, 3000); - }); - _this4._iOSHackDelayedInitialPeer = true; - } - - var mediaStream = new MediaStream(); - var receivers = conn.getReceivers(); - receivers.forEach(function (receiver) { - if (receiver.track) { - mediaStream.addTrack(receiver.track); - } - }); - if (mediaStream.getTracks().length === 0) { - mediaStream = null; - } - - debug(occupantId + ": subscriber ready"); - return { - handle, - mediaStream, - conn - }; - })(); - } - - sendJoin(handle, subscribe) { - return handle.sendMessage({ - kind: "join", - room_id: this.room, - user_id: this.clientId, - subscribe, - token: this.joinToken - }); - } - - toggleFreeze() { - if (this.frozen) { - this.unfreeze(); - } else { - this.freeze(); - } - } - - freeze() { - this.frozen = true; - } - - unfreeze() { - this.frozen = false; - this.flushPendingUpdates(); - } - - dataForUpdateMultiMessage(networkId, message) { - // "d" is an array of entity datas, where each item in the array represents a unique entity and contains - // metadata for the entity, and an array of components that have been updated on the entity. - // This method finds the data corresponding to the given networkId. - for (let i = 0, l = message.data.d.length; i < l; i++) { - const data = message.data.d[i]; - - if (data.networkId === networkId) { - return data; - } - } - - return null; - } - - getPendingData(networkId, message) { - if (!message) return null; - - let data = message.dataType === "um" ? this.dataForUpdateMultiMessage(networkId, message) : message.data; - - // Ignore messages relating to users who have disconnected since freezing, their entities - // will have aleady been removed by NAF. - // Note that delete messages have no "owner" so we have to check for that as well. - if (data.owner && !this.occupants[data.owner]) return null; - - // Ignore messages from users that we may have blocked while frozen. - if (data.owner && this.blockedClients.has(data.owner)) return null; - - return data; - } - - // Used externally - getPendingDataForNetworkId(networkId) { - return this.getPendingData(networkId, this.frozenUpdates.get(networkId)); - } - - flushPendingUpdates() { - for (const [networkId, message] of this.frozenUpdates) { - let data = this.getPendingData(networkId, message); - if (!data) continue; - - // Override the data type on "um" messages types, since we extract entity updates from "um" messages into - // individual frozenUpdates in storeSingleMessage. - const dataType = message.dataType === "um" ? "u" : message.dataType; - - this.onOccupantMessage(null, dataType, data, message.source); - } - this.frozenUpdates.clear(); - } - - storeMessage(message) { - if (message.dataType === "um") { - // UpdateMulti - for (let i = 0, l = message.data.d.length; i < l; i++) { - this.storeSingleMessage(message, i); - } - } else { - this.storeSingleMessage(message); - } - } - - storeSingleMessage(message, index) { - const data = index !== undefined ? message.data.d[index] : message.data; - const dataType = message.dataType; - const source = message.source; - - const networkId = data.networkId; - - if (!this.frozenUpdates.has(networkId)) { - this.frozenUpdates.set(networkId, message); - } else { - const storedMessage = this.frozenUpdates.get(networkId); - const storedData = storedMessage.dataType === "um" ? this.dataForUpdateMultiMessage(networkId, storedMessage) : storedMessage.data; - - // Avoid updating components if the entity data received did not come from the current owner. - const isOutdatedMessage = data.lastOwnerTime < storedData.lastOwnerTime; - const isContemporaneousMessage = data.lastOwnerTime === storedData.lastOwnerTime; - if (isOutdatedMessage || isContemporaneousMessage && storedData.owner > data.owner) { - return; - } - - if (dataType === "r") { - const createdWhileFrozen = storedData && storedData.isFirstSync; - if (createdWhileFrozen) { - // If the entity was created and deleted while frozen, don't bother conveying anything to the consumer. - this.frozenUpdates.delete(networkId); - } else { - // Delete messages override any other messages for this entity - this.frozenUpdates.set(networkId, message); - } - } else { - // merge in component updates - if (storedData.components && data.components) { - Object.assign(storedData.components, data.components); - } - } - } - } - - onDataChannelMessage(e, source) { - this.onData(JSON.parse(e.data), source); - } - - onData(message, source) { - if (debug.enabled) { - debug(`DC in: ${message}`); - } - - if (!message.dataType) return; - - message.source = source; - - if (this.frozen) { - this.storeMessage(message); - } else { - this.onOccupantMessage(null, message.dataType, message.data, message.source); - } - } - - shouldStartConnectionTo(client) { - return true; - } - - startStreamConnection(client) {} - - closeStreamConnection(client) {} - - getConnectStatus(clientId) { - return this.occupants[clientId] ? NAF.adapters.IS_CONNECTED : NAF.adapters.NOT_CONNECTED; - } - - updateTimeOffset() { - var _this5 = this; - - return _asyncToGenerator(function* () { - if (_this5.isDisconnected()) return; - - const clientSentTime = Date.now(); - - const res = yield fetch(document.location.href, { - method: "HEAD", - cache: "no-cache" - }); - - const precision = 1000; - const serverReceivedTime = new Date(res.headers.get("Date")).getTime() + precision / 2; - const clientReceivedTime = Date.now(); - const serverTime = serverReceivedTime + (clientReceivedTime - clientSentTime) / 2; - const timeOffset = serverTime - clientReceivedTime; - - _this5.serverTimeRequests++; - - if (_this5.serverTimeRequests <= 10) { - _this5.timeOffsets.push(timeOffset); - } else { - _this5.timeOffsets[_this5.serverTimeRequests % 10] = timeOffset; - } - - _this5.avgTimeOffset = _this5.timeOffsets.reduce(function (acc, offset) { - return acc += offset; - }, 0) / _this5.timeOffsets.length; - - if (_this5.serverTimeRequests > 10) { - debug(`new server time offset: ${_this5.avgTimeOffset}ms`); - setTimeout(function () { - return _this5.updateTimeOffset(); - }, 5 * 60 * 1000); // Sync clock every 5 minutes. - } else { - _this5.updateTimeOffset(); - } - })(); - } - - getServerTime() { - return Date.now() + this.avgTimeOffset; - } - - getMediaStream(clientId, type = "audio") { - if (this.mediaStreams[clientId]) { - debug(`Already had ${type} for ${clientId}`); - return Promise.resolve(this.mediaStreams[clientId][type]); - } else { - debug(`Waiting on ${type} for ${clientId}`); - if (!this.pendingMediaRequests.has(clientId)) { - this.pendingMediaRequests.set(clientId, {}); - - const audioPromise = new Promise((resolve, reject) => { - this.pendingMediaRequests.get(clientId).audio = { resolve, reject }; - }); - const videoPromise = new Promise((resolve, reject) => { - this.pendingMediaRequests.get(clientId).video = { resolve, reject }; - }); - - this.pendingMediaRequests.get(clientId).audio.promise = audioPromise; - this.pendingMediaRequests.get(clientId).video.promise = videoPromise; - - audioPromise.catch(e => console.warn(`${clientId} getMediaStream Audio Error`, e)); - videoPromise.catch(e => console.warn(`${clientId} getMediaStream Video Error`, e)); - } - return this.pendingMediaRequests.get(clientId)[type].promise; - } - } - - setMediaStream(clientId, stream) { - // Safari doesn't like it when you use single a mixed media stream where one of the tracks is inactive, so we - // split the tracks into two streams. - const audioStream = new MediaStream(); - try { - stream.getAudioTracks().forEach(track => audioStream.addTrack(track)); - } catch (e) { - console.warn(`${clientId} setMediaStream Audio Error`, e); - } - const videoStream = new MediaStream(); - try { - stream.getVideoTracks().forEach(track => videoStream.addTrack(track)); - } catch (e) { - console.warn(`${clientId} setMediaStream Video Error`, e); - } - - this.mediaStreams[clientId] = { audio: audioStream, video: videoStream }; - - // Resolve the promise for the user's media stream if it exists. - if (this.pendingMediaRequests.has(clientId)) { - this.pendingMediaRequests.get(clientId).audio.resolve(audioStream); - this.pendingMediaRequests.get(clientId).video.resolve(videoStream); - } - } - - setLocalMediaStream(stream) { - var _this6 = this; - - return _asyncToGenerator(function* () { - // our job here is to make sure the connection winds up with RTP senders sending the stuff in this stream, - // and not the stuff that isn't in this stream. strategy is to replace existing tracks if we can, add tracks - // that we can't replace, and disable tracks that don't exist anymore. - - // note that we don't ever remove a track from the stream -- since Janus doesn't support Unified Plan, we absolutely - // can't wind up with a SDP that has >1 audio or >1 video tracks, even if one of them is inactive (what you get if - // you remove a track from an existing stream.) - if (_this6.publisher && _this6.publisher.conn) { - const existingSenders = _this6.publisher.conn.getSenders(); - const newSenders = []; - const tracks = stream.getTracks(); - - for (let i = 0; i < tracks.length; i++) { - const t = tracks[i]; - const sender = existingSenders.find(function (s) { - return s.track != null && s.track.kind == t.kind; - }); - - if (sender != null) { - if (sender.replaceTrack) { - yield sender.replaceTrack(t); - - // Workaround https://bugzilla.mozilla.org/show_bug.cgi?id=1576771 - if (t.kind === "video" && t.enabled && navigator.userAgent.toLowerCase().indexOf('firefox') > -1) { - t.enabled = false; - setTimeout(function () { - return t.enabled = true; - }, 1000); - } - } else { - // Fallback for browsers that don't support replaceTrack. At this time of this writing - // most browsers support it, and testing this code path seems to not work properly - // in Chrome anymore. - stream.removeTrack(sender.track); - stream.addTrack(t); - } - newSenders.push(sender); - } else { - newSenders.push(_this6.publisher.conn.addTrack(t, stream)); - } - } - existingSenders.forEach(function (s) { - if (!newSenders.includes(s)) { - s.track.enabled = false; - } - }); - } - _this6.localMediaStream = stream; - _this6.setMediaStream(_this6.clientId, stream); - })(); - } - - enableMicrophone(enabled) { - if (this.publisher && this.publisher.conn) { - this.publisher.conn.getSenders().forEach(s => { - if (s.track.kind == "audio") { - s.track.enabled = enabled; - } - }); - } - } - - sendData(clientId, dataType, data) { - if (!this.publisher) { - console.warn("sendData called without a publisher"); - } else { - switch (this.unreliableTransport) { - case "websocket": - this.publisher.handle.sendMessage({ kind: "data", body: JSON.stringify({ dataType, data }), whom: clientId }); - break; - case "datachannel": - this.publisher.unreliableChannel.send(JSON.stringify({ clientId, dataType, data })); - break; - default: - this.unreliableTransport(clientId, dataType, data); - break; - } - } - } - - sendDataGuaranteed(clientId, dataType, data) { - if (!this.publisher) { - console.warn("sendDataGuaranteed called without a publisher"); - } else { - switch (this.reliableTransport) { - case "websocket": - this.publisher.handle.sendMessage({ kind: "data", body: JSON.stringify({ dataType, data }), whom: clientId }); - break; - case "datachannel": - this.publisher.reliableChannel.send(JSON.stringify({ clientId, dataType, data })); - break; - default: - this.reliableTransport(clientId, dataType, data); - break; - } - } - } - - broadcastData(dataType, data) { - if (!this.publisher) { - console.warn("broadcastData called without a publisher"); - } else { - switch (this.unreliableTransport) { - case "websocket": - this.publisher.handle.sendMessage({ kind: "data", body: JSON.stringify({ dataType, data }) }); - break; - case "datachannel": - this.publisher.unreliableChannel.send(JSON.stringify({ dataType, data })); - break; - default: - this.unreliableTransport(undefined, dataType, data); - break; - } - } - } - - broadcastDataGuaranteed(dataType, data) { - if (!this.publisher) { - console.warn("broadcastDataGuaranteed called without a publisher"); - } else { - switch (this.reliableTransport) { - case "websocket": - this.publisher.handle.sendMessage({ kind: "data", body: JSON.stringify({ dataType, data }) }); - break; - case "datachannel": - this.publisher.reliableChannel.send(JSON.stringify({ dataType, data })); - break; - default: - this.reliableTransport(undefined, dataType, data); - break; - } - } - } - - kick(clientId, permsToken) { - return this.publisher.handle.sendMessage({ kind: "kick", room_id: this.room, user_id: clientId, token: permsToken }).then(() => { - document.body.dispatchEvent(new CustomEvent("kicked", { detail: { clientId: clientId } })); - }); - } - - block(clientId) { - return this.publisher.handle.sendMessage({ kind: "block", whom: clientId }).then(() => { - this.blockedClients.set(clientId, true); - document.body.dispatchEvent(new CustomEvent("blocked", { detail: { clientId: clientId } })); - }); - } - - unblock(clientId) { - return this.publisher.handle.sendMessage({ kind: "unblock", whom: clientId }).then(() => { - this.blockedClients.delete(clientId); - document.body.dispatchEvent(new CustomEvent("unblocked", { detail: { clientId: clientId } })); - }); - } -} - -NAF.adapters.register("janus", JanusAdapter); - -module.exports = JanusAdapter; - /***/ }) -/******/ }); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL21pbmlqYW51cy9taW5pamFudXMuanMiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL3NkcC9zZHAuanMiLCJ3ZWJwYWNrOi8vLy4vc3JjL2luZGV4LmpzIl0sIm5hbWVzIjpbIm1qIiwicmVxdWlyZSIsIkphbnVzU2Vzc2lvbiIsInByb3RvdHlwZSIsInNlbmRPcmlnaW5hbCIsInNlbmQiLCJ0eXBlIiwic2lnbmFsIiwiY2F0Y2giLCJlIiwibWVzc2FnZSIsImluZGV4T2YiLCJjb25zb2xlIiwiZXJyb3IiLCJOQUYiLCJjb25uZWN0aW9uIiwiYWRhcHRlciIsInJlY29ubmVjdCIsInNkcFV0aWxzIiwiZGVidWciLCJsb2ciLCJ3YXJuIiwiaXNTYWZhcmkiLCJ0ZXN0IiwibmF2aWdhdG9yIiwidXNlckFnZW50IiwiU1VCU0NSSUJFX1RJTUVPVVRfTVMiLCJkZWJvdW5jZSIsImZuIiwiY3VyciIsIlByb21pc2UiLCJyZXNvbHZlIiwiYXJncyIsIkFycmF5Iiwic2xpY2UiLCJjYWxsIiwiYXJndW1lbnRzIiwidGhlbiIsIl8iLCJhcHBseSIsInJhbmRvbVVpbnQiLCJNYXRoIiwiZmxvb3IiLCJyYW5kb20iLCJOdW1iZXIiLCJNQVhfU0FGRV9JTlRFR0VSIiwidW50aWxEYXRhQ2hhbm5lbE9wZW4iLCJkYXRhQ2hhbm5lbCIsInJlamVjdCIsInJlYWR5U3RhdGUiLCJyZXNvbHZlciIsInJlamVjdG9yIiwiY2xlYXIiLCJyZW1vdmVFdmVudExpc3RlbmVyIiwiYWRkRXZlbnRMaXN0ZW5lciIsImlzSDI2NFZpZGVvU3VwcG9ydGVkIiwidmlkZW8iLCJkb2N1bWVudCIsImNyZWF0ZUVsZW1lbnQiLCJjYW5QbGF5VHlwZSIsIk9QVVNfUEFSQU1FVEVSUyIsInVzZWR0eCIsInN0ZXJlbyIsIkRFRkFVTFRfUEVFUl9DT05ORUNUSU9OX0NPTkZJRyIsImljZVNlcnZlcnMiLCJ1cmxzIiwiV1NfTk9STUFMX0NMT1NVUkUiLCJKYW51c0FkYXB0ZXIiLCJjb25zdHJ1Y3RvciIsInJvb20iLCJjbGllbnRJZCIsImpvaW5Ub2tlbiIsInNlcnZlclVybCIsIndlYlJ0Y09wdGlvbnMiLCJwZWVyQ29ubmVjdGlvbkNvbmZpZyIsIndzIiwic2Vzc2lvbiIsInJlbGlhYmxlVHJhbnNwb3J0IiwidW5yZWxpYWJsZVRyYW5zcG9ydCIsImluaXRpYWxSZWNvbm5lY3Rpb25EZWxheSIsInJlY29ubmVjdGlvbkRlbGF5IiwicmVjb25uZWN0aW9uVGltZW91dCIsIm1heFJlY29ubmVjdGlvbkF0dGVtcHRzIiwicmVjb25uZWN0aW9uQXR0ZW1wdHMiLCJwdWJsaXNoZXIiLCJvY2N1cGFudHMiLCJsZWZ0T2NjdXBhbnRzIiwiU2V0IiwibWVkaWFTdHJlYW1zIiwibG9jYWxNZWRpYVN0cmVhbSIsInBlbmRpbmdNZWRpYVJlcXVlc3RzIiwiTWFwIiwiYmxvY2tlZENsaWVudHMiLCJmcm96ZW5VcGRhdGVzIiwidGltZU9mZnNldHMiLCJzZXJ2ZXJUaW1lUmVxdWVzdHMiLCJhdmdUaW1lT2Zmc2V0Iiwib25XZWJzb2NrZXRPcGVuIiwiYmluZCIsIm9uV2Vic29ja2V0Q2xvc2UiLCJvbldlYnNvY2tldE1lc3NhZ2UiLCJvbkRhdGFDaGFubmVsTWVzc2FnZSIsIm9uRGF0YSIsInNldFNlcnZlclVybCIsInVybCIsInNldEFwcCIsImFwcCIsInNldFJvb20iLCJyb29tTmFtZSIsInNldEpvaW5Ub2tlbiIsInNldENsaWVudElkIiwic2V0V2ViUnRjT3B0aW9ucyIsIm9wdGlvbnMiLCJzZXRQZWVyQ29ubmVjdGlvbkNvbmZpZyIsInNldFNlcnZlckNvbm5lY3RMaXN0ZW5lcnMiLCJzdWNjZXNzTGlzdGVuZXIiLCJmYWlsdXJlTGlzdGVuZXIiLCJjb25uZWN0U3VjY2VzcyIsImNvbm5lY3RGYWlsdXJlIiwic2V0Um9vbU9jY3VwYW50TGlzdGVuZXIiLCJvY2N1cGFudExpc3RlbmVyIiwib25PY2N1cGFudHNDaGFuZ2VkIiwic2V0RGF0YUNoYW5uZWxMaXN0ZW5lcnMiLCJvcGVuTGlzdGVuZXIiLCJjbG9zZWRMaXN0ZW5lciIsIm1lc3NhZ2VMaXN0ZW5lciIsIm9uT2NjdXBhbnRDb25uZWN0ZWQiLCJvbk9jY3VwYW50RGlzY29ubmVjdGVkIiwib25PY2N1cGFudE1lc3NhZ2UiLCJzZXRSZWNvbm5lY3Rpb25MaXN0ZW5lcnMiLCJyZWNvbm5lY3RpbmdMaXN0ZW5lciIsInJlY29ubmVjdGVkTGlzdGVuZXIiLCJyZWNvbm5lY3Rpb25FcnJvckxpc3RlbmVyIiwib25SZWNvbm5lY3RpbmciLCJvblJlY29ubmVjdGVkIiwib25SZWNvbm5lY3Rpb25FcnJvciIsImNvbm5lY3QiLCJ3ZWJzb2NrZXRDb25uZWN0aW9uIiwiV2ViU29ja2V0IiwidGltZW91dE1zIiwid3NPbk9wZW4iLCJhbGwiLCJ1cGRhdGVUaW1lT2Zmc2V0IiwiZGlzY29ubmVjdCIsImNsZWFyVGltZW91dCIsInJlbW92ZUFsbE9jY3VwYW50cyIsImNvbm4iLCJjbG9zZSIsImRpc3Bvc2UiLCJkZWxheWVkUmVjb25uZWN0VGltZW91dCIsImlzRGlzY29ubmVjdGVkIiwiY3JlYXRlIiwiY3JlYXRlUHVibGlzaGVyIiwiYWRkT2NjdXBhbnRQcm9taXNlcyIsImkiLCJpbml0aWFsT2NjdXBhbnRzIiwibGVuZ3RoIiwib2NjdXBhbnRJZCIsInB1c2giLCJhZGRPY2N1cGFudCIsImV2ZW50IiwiY29kZSIsInNldFRpbWVvdXQiLCJFcnJvciIsInBlcmZvcm1EZWxheWVkUmVjb25uZWN0IiwicmVjZWl2ZSIsIkpTT04iLCJwYXJzZSIsImRhdGEiLCJyZW1vdmVPY2N1cGFudCIsImRlbGV0ZSIsInN1YnNjcmliZXIiLCJjcmVhdGVTdWJzY3JpYmVyIiwic2V0TWVkaWFTdHJlYW0iLCJtZWRpYVN0cmVhbSIsIk9iamVjdCIsImdldE93blByb3BlcnR5TmFtZXMiLCJhZGQiLCJoYXMiLCJtc2ciLCJnZXQiLCJhdWRpbyIsImFzc29jaWF0ZSIsImhhbmRsZSIsImV2Iiwic2VuZFRyaWNrbGUiLCJjYW5kaWRhdGUiLCJpY2VDb25uZWN0aW9uU3RhdGUiLCJvZmZlciIsImNyZWF0ZU9mZmVyIiwiY29uZmlndXJlUHVibGlzaGVyU2RwIiwiZml4U2FmYXJpSWNlVUZyYWciLCJsb2NhbCIsIm8iLCJzZXRMb2NhbERlc2NyaXB0aW9uIiwicmVtb3RlIiwiaiIsInNlbmRKc2VwIiwiciIsInNldFJlbW90ZURlc2NyaXB0aW9uIiwianNlcCIsIm9uIiwiYW5zd2VyIiwiY29uZmlndXJlU3Vic2NyaWJlclNkcCIsImNyZWF0ZUFuc3dlciIsImEiLCJKYW51c1BsdWdpbkhhbmRsZSIsIlJUQ1BlZXJDb25uZWN0aW9uIiwiYXR0YWNoIiwid2VicnRjdXAiLCJyZWxpYWJsZUNoYW5uZWwiLCJjcmVhdGVEYXRhQ2hhbm5lbCIsIm9yZGVyZWQiLCJ1bnJlbGlhYmxlQ2hhbm5lbCIsIm1heFJldHJhbnNtaXRzIiwiZ2V0VHJhY2tzIiwiZm9yRWFjaCIsImFkZFRyYWNrIiwidHJhY2siLCJwbHVnaW5kYXRhIiwicm9vbV9pZCIsInVzZXJfaWQiLCJib2R5IiwiZGlzcGF0Y2hFdmVudCIsIkN1c3RvbUV2ZW50IiwiZGV0YWlsIiwiYnkiLCJzZW5kSm9pbiIsIm5vdGlmaWNhdGlvbnMiLCJzdWNjZXNzIiwiZXJyIiwicmVzcG9uc2UiLCJ1c2VycyIsImluY2x1ZGVzIiwic2RwIiwicmVwbGFjZSIsImxpbmUiLCJwdCIsInBhcmFtZXRlcnMiLCJhc3NpZ24iLCJwYXJzZUZtdHAiLCJ3cml0ZUZtdHAiLCJwYXlsb2FkVHlwZSIsIm1heFJldHJpZXMiLCJ3ZWJydGNGYWlsZWQiLCJsZWZ0SW50ZXJ2YWwiLCJzZXRJbnRlcnZhbCIsImNsZWFySW50ZXJ2YWwiLCJ0aW1lb3V0IiwibWVkaWEiLCJfaU9TSGFja0RlbGF5ZWRJbml0aWFsUGVlciIsIk1lZGlhU3RyZWFtIiwicmVjZWl2ZXJzIiwiZ2V0UmVjZWl2ZXJzIiwicmVjZWl2ZXIiLCJzdWJzY3JpYmUiLCJzZW5kTWVzc2FnZSIsImtpbmQiLCJ0b2tlbiIsInRvZ2dsZUZyZWV6ZSIsImZyb3plbiIsInVuZnJlZXplIiwiZnJlZXplIiwiZmx1c2hQZW5kaW5nVXBkYXRlcyIsImRhdGFGb3JVcGRhdGVNdWx0aU1lc3NhZ2UiLCJuZXR3b3JrSWQiLCJsIiwiZCIsImdldFBlbmRpbmdEYXRhIiwiZGF0YVR5cGUiLCJvd25lciIsImdldFBlbmRpbmdEYXRhRm9yTmV0d29ya0lkIiwic291cmNlIiwic3RvcmVNZXNzYWdlIiwic3RvcmVTaW5nbGVNZXNzYWdlIiwiaW5kZXgiLCJ1bmRlZmluZWQiLCJzZXQiLCJzdG9yZWRNZXNzYWdlIiwic3RvcmVkRGF0YSIsImlzT3V0ZGF0ZWRNZXNzYWdlIiwibGFzdE93bmVyVGltZSIsImlzQ29udGVtcG9yYW5lb3VzTWVzc2FnZSIsImNyZWF0ZWRXaGlsZUZyb3plbiIsImlzRmlyc3RTeW5jIiwiY29tcG9uZW50cyIsImVuYWJsZWQiLCJzaG91bGRTdGFydENvbm5lY3Rpb25UbyIsImNsaWVudCIsInN0YXJ0U3RyZWFtQ29ubmVjdGlvbiIsImNsb3NlU3RyZWFtQ29ubmVjdGlvbiIsImdldENvbm5lY3RTdGF0dXMiLCJhZGFwdGVycyIsIklTX0NPTk5FQ1RFRCIsIk5PVF9DT05ORUNURUQiLCJjbGllbnRTZW50VGltZSIsIkRhdGUiLCJub3ciLCJyZXMiLCJmZXRjaCIsImxvY2F0aW9uIiwiaHJlZiIsIm1ldGhvZCIsImNhY2hlIiwicHJlY2lzaW9uIiwic2VydmVyUmVjZWl2ZWRUaW1lIiwiaGVhZGVycyIsImdldFRpbWUiLCJjbGllbnRSZWNlaXZlZFRpbWUiLCJzZXJ2ZXJUaW1lIiwidGltZU9mZnNldCIsInJlZHVjZSIsImFjYyIsIm9mZnNldCIsImdldFNlcnZlclRpbWUiLCJnZXRNZWRpYVN0cmVhbSIsImF1ZGlvUHJvbWlzZSIsInZpZGVvUHJvbWlzZSIsInByb21pc2UiLCJzdHJlYW0iLCJhdWRpb1N0cmVhbSIsImdldEF1ZGlvVHJhY2tzIiwidmlkZW9TdHJlYW0iLCJnZXRWaWRlb1RyYWNrcyIsInNldExvY2FsTWVkaWFTdHJlYW0iLCJleGlzdGluZ1NlbmRlcnMiLCJnZXRTZW5kZXJzIiwibmV3U2VuZGVycyIsInRyYWNrcyIsInQiLCJzZW5kZXIiLCJmaW5kIiwicyIsInJlcGxhY2VUcmFjayIsInRvTG93ZXJDYXNlIiwicmVtb3ZlVHJhY2siLCJlbmFibGVNaWNyb3Bob25lIiwic2VuZERhdGEiLCJzdHJpbmdpZnkiLCJ3aG9tIiwic2VuZERhdGFHdWFyYW50ZWVkIiwiYnJvYWRjYXN0RGF0YSIsImJyb2FkY2FzdERhdGFHdWFyYW50ZWVkIiwia2ljayIsInBlcm1zVG9rZW4iLCJibG9jayIsInVuYmxvY2siLCJyZWdpc3RlciIsIm1vZHVsZSIsImV4cG9ydHMiXSwibWFwcGluZ3MiOiI7UUFBQTtRQUNBOztRQUVBO1FBQ0E7O1FBRUE7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7O1FBRUE7UUFDQTs7UUFFQTtRQUNBOztRQUVBO1FBQ0E7UUFDQTs7O1FBR0E7UUFDQTs7UUFFQTtRQUNBOztRQUVBO1FBQ0E7UUFDQTtRQUNBLDBDQUEwQyxnQ0FBZ0M7UUFDMUU7UUFDQTs7UUFFQTtRQUNBO1FBQ0E7UUFDQSx3REFBd0Qsa0JBQWtCO1FBQzFFO1FBQ0EsaURBQWlELGNBQWM7UUFDL0Q7O1FBRUE7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBLHlDQUF5QyxpQ0FBaUM7UUFDMUUsZ0hBQWdILG1CQUFtQixFQUFFO1FBQ3JJO1FBQ0E7O1FBRUE7UUFDQTtRQUNBO1FBQ0EsMkJBQTJCLDBCQUEwQixFQUFFO1FBQ3ZELGlDQUFpQyxlQUFlO1FBQ2hEO1FBQ0E7UUFDQTs7UUFFQTtRQUNBLHNEQUFzRCwrREFBK0Q7O1FBRXJIO1FBQ0E7OztRQUdBO1FBQ0E7Ozs7Ozs7Ozs7OztBQ2xGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxp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R0FBRztBQUNIO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBLDRDQUE0QztBQUM1QztBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxvQkFBb0I7QUFDcEIsMEJBQTBCO0FBQzFCO0FBQ0E7QUFDQTtBQUNBLDJEQUEyRDtBQUMzRCxpQkFBaUIsa0JBQWtCO0FBQ25DO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQSxLQUFLO0FBQ0wsaURBQWlEO0FBQ2pEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxpQkFBaUIsa0JBQWtCLE9BQU87QUFDMUM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLDRDQUE0QztBQUM1QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHdCQUF3QjtBQUN4QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FBR0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsWUFBWTtBQUNaO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNB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LENBQWVtRyxNQUFmLENBQXNCeUUsV0FBdEIsQ0FBa0MsRUFBRUMsTUFBTSxPQUFSLEVBQWlCa0YsTUFBTTdRLFFBQXZCLEVBQWxDLEVBQXFFakMsSUFBckUsQ0FBMEUsTUFBTTtBQUNyRixXQUFLdUQsY0FBTCxDQUFvQnlMLEdBQXBCLENBQXdCL00sUUFBeEIsRUFBa0MsSUFBbEM7QUFDQWIsZUFBU3FLLElBQVQsQ0FBY0MsYUFBZCxDQUE0QixJQUFJQyxXQUFKLENBQWdCLFNBQWhCLEVBQTJCLEVBQUVDLFFBQVEsRUFBRTNKLFVBQVVBLFFBQVosRUFBVixFQUEzQixDQUE1QjtBQUNELEtBSE0sQ0FBUDtBQUlEOztBQUVEb1IsVUFBUXBSLFFBQVIsRUFBa0I7QUFDaEIsV0FBTyxLQUFLYyxTQUFMLENBQWVtRyxNQUFmLENBQXNCeUUsV0FBdEIsQ0FBa0MsRUFBRUMsTUFBTSxTQUFSLEVBQW1Ca0YsTUFBTTdRLFFBQXpCLEVBQWxDLEVBQXVFakMsSUFBdkUsQ0FBNEUsTUFBTTtBQUN2RixXQUFLdUQsY0FBTCxDQUFvQjhFLE1BQXBCLENBQTJCcEcsUUFBM0I7QUFDQWIsZUFBU3FLLElBQVQsQ0FBY0MsYUFBZCxDQUE0QixJQUFJQyxXQUFKLENBQWdCLFdBQWhCLEVBQTZCLEVBQUVDLFFBQVEsRUFBRTNKLFVBQVVBLFFBQVosRUFBVixFQUE3QixDQUE1QjtBQUNELEtBSE0sQ0FBUDtBQUlEO0FBMTlCZ0I7O0FBNjlCbkJ4RCxJQUFJc1IsUUFBSixDQUFhdUQsUUFBYixDQUFzQixPQUF0QixFQUErQnhSLFlBQS9COztBQUVBeVIsT0FBT0MsT0FBUCxHQUFpQjFSLFlBQWpCLEMiLCJmaWxlIjoibmFmLWphbnVzLWFkYXB0ZXIuanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBnZXR0ZXIgfSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0aWYodHlwZW9mIFN5bWJvbCAhPT0gJ3VuZGVmaW5lZCcgJiYgU3ltYm9sLnRvU3RyaW5nVGFnKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG4gXHRcdH1cbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGNyZWF0ZSBhIGZha2UgbmFtZXNwYWNlIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDE6IHZhbHVlIGlzIGEgbW9kdWxlIGlkLCByZXF1aXJlIGl0XG4gXHQvLyBtb2RlICYgMjogbWVyZ2UgYWxsIHByb3BlcnRpZXMgb2YgdmFsdWUgaW50byB0aGUgbnNcbiBcdC8vIG1vZGUgJiA0OiByZXR1cm4gdmFsdWUgd2hlbiBhbHJlYWR5IG5zIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDh8MTogYmVoYXZlIGxpa2UgcmVxdWlyZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy50ID0gZnVuY3Rpb24odmFsdWUsIG1vZGUpIHtcbiBcdFx0aWYobW9kZSAmIDEpIHZhbHVlID0gX193ZWJwYWNrX3JlcXVpcmVfXyh2YWx1ZSk7XG4gXHRcdGlmKG1vZGUgJiA4KSByZXR1cm4gdmFsdWU7XG4gXHRcdGlmKChtb2RlICYgNCkgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJiB2YWx1ZS5fX2VzTW9kdWxlKSByZXR1cm4gdmFsdWU7XG4gXHRcdHZhciBucyA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18ucihucyk7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShucywgJ2RlZmF1bHQnLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2YWx1ZSB9KTtcbiBcdFx0aWYobW9kZSAmIDIgJiYgdHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSBmb3IodmFyIGtleSBpbiB2YWx1ZSkgX193ZWJwYWNrX3JlcXVpcmVfXy5kKG5zLCBrZXksIGZ1bmN0aW9uKGtleSkgeyByZXR1cm4gdmFsdWVba2V5XTsgfS5iaW5kKG51bGwsIGtleSkpO1xuIFx0XHRyZXR1cm4gbnM7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gXCIuL3NyYy9pbmRleC5qc1wiKTtcbiIsIi8qKlxuICogUmVwcmVzZW50cyBhIGhhbmRsZSB0byBhIHNpbmdsZSBKYW51cyBwbHVnaW4gb24gYSBKYW51cyBzZXNzaW9uLiBFYWNoIFdlYlJUQyBjb25uZWN0aW9uIHRvIHRoZSBKYW51cyBzZXJ2ZXIgd2lsbCBiZVxuICogYXNzb2NpYXRlZCB3aXRoIGEgc2luZ2xlIGhhbmRsZS4gT25jZSBhdHRhY2hlZCB0byB0aGUgc2VydmVyLCB0aGlzIGhhbmRsZSB3aWxsIGJlIGdpdmVuIGEgdW5pcXVlIElEIHdoaWNoIHNob3VsZCBiZVxuICogdXNlZCB0byBhc3NvY2lhdGUgaXQgd2l0aCBmdXR1cmUgc2lnbmFsbGluZyBtZXNzYWdlcy5cbiAqXG4gKiBTZWUgaHR0cHM6Ly9qYW51cy5jb25mLm1lZXRlY2hvLmNvbS9kb2NzL3Jlc3QuaHRtbCNoYW5kbGVzLlxuICoqL1xuZnVuY3Rpb24gSmFudXNQbHVnaW5IYW5kbGUoc2Vzc2lvbikge1xuICB0aGlzLnNlc3Npb24gPSBzZXNzaW9uO1xuICB0aGlzLmlkID0gdW5kZWZpbmVkO1xufVxuXG4vKiogQXR0YWNoZXMgdGhpcyBoYW5kbGUgdG8gdGhlIEphbnVzIHNlcnZlciBhbmQgc2V0cyBpdHMgSUQuICoqL1xuSmFudXNQbHVnaW5IYW5kbGUucHJvdG90eXBlLmF0dGFjaCA9IGZ1bmN0aW9uKHBsdWdpbikge1xuICB2YXIgcGF5bG9hZCA9IHsgcGx1Z2luOiBwbHVnaW4sIFwiZm9yY2UtYnVuZGxlXCI6IHRydWUsIFwiZm9yY2UtcnRjcC1tdXhcIjogdHJ1ZSB9O1xuICByZXR1cm4gdGhpcy5zZXNzaW9uLnNlbmQoXCJhdHRhY2hcIiwgcGF5bG9hZCkudGhlbihyZXNwID0+IHtcbiAgICB0aGlzLmlkID0gcmVzcC5kYXRhLmlkO1xuICAgIHJldHVybiByZXNwO1xuICB9KTtcbn07XG5cbi8qKiBEZXRhY2hlcyB0aGlzIGhhbmRsZS4gKiovXG5KYW51c1BsdWdpbkhhbmRsZS5wcm90b3R5cGUuZGV0YWNoID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiB0aGlzLnNlbmQoXCJkZXRhY2hcIik7XG59O1xuXG4vKiogUmVnaXN0ZXJzIGEgY2FsbGJhY2sgdG8gYmUgZmlyZWQgdXBvbiB0aGUgcmVjZXB0aW9uIG9mIGFueSBpbmNvbWluZyBKYW51cyBzaWduYWxzIGZvciB0aGlzIHBsdWdpbiBoYW5kbGUgd2l0aCB0aGVcbiAqIGBqYW51c2AgYXR0cmlidXRlIGVxdWFsIHRvIGBldmAuXG4gKiovXG5KYW51c1BsdWdpbkhhbmRsZS5wcm90b3R5cGUub24gPSBmdW5jdGlvbihldiwgY2FsbGJhY2spIHtcbiAgcmV0dXJuIHRoaXMuc2Vzc2lvbi5vbihldiwgc2lnbmFsID0+IHtcbiAgICBpZiAoc2lnbmFsLnNlbmRlciA9PSB0aGlzLmlkKSB7XG4gICAgICBjYWxsYmFjayhzaWduYWwpO1xuICAgIH1cbiAgfSk7XG59O1xuXG4vKipcbiAqIFNlbmRzIGEgc2lnbmFsIGFzc29jaWF0ZWQgd2l0aCB0aGlzIGhhbmRsZS4gU2lnbmFscyBzaG91bGQgYmUgSlNPTi1zZXJpYWxpemFibGUgb2JqZWN0cy4gUmV0dXJucyBhIHByb21pc2UgdGhhdCB3aWxsXG4gKiBiZSByZXNvbHZlZCBvciByZWplY3RlZCB3aGVuIGEgcmVzcG9uc2UgdG8gdGhpcyBzaWduYWwgaXMgcmVjZWl2ZWQsIG9yIHdoZW4gbm8gcmVzcG9uc2UgaXMgcmVjZWl2ZWQgd2l0aGluIHRoZVxuICogc2Vzc2lvbiB0aW1lb3V0LlxuICoqL1xuSmFudXNQbHVnaW5IYW5kbGUucHJvdG90eXBlLnNlbmQgPSBmdW5jdGlvbih0eXBlLCBzaWduYWwpIHtcbiAgcmV0dXJuIHRoaXMuc2Vzc2lvbi5zZW5kKHR5cGUsIE9iamVjdC5hc3NpZ24oeyBoYW5kbGVfaWQ6IHRoaXMuaWQgfSwgc2lnbmFsKSk7XG59O1xuXG4vKiogU2VuZHMgYSBwbHVnaW4tc3BlY2lmaWMgbWVzc2FnZSBhc3NvY2lhdGVkIHdpdGggdGhpcyBoYW5kbGUuICoqL1xuSmFudXNQbHVnaW5IYW5kbGUucHJvdG90eXBlLnNlbmRNZXNzYWdlID0gZnVuY3Rpb24oYm9keSkge1xuICByZXR1cm4gdGhpcy5zZW5kKFwibWVzc2FnZVwiLCB7IGJvZHk6IGJvZHkgfSk7XG59O1xuXG4vKiogU2VuZHMgYSBKU0VQIG9mZmVyIG9yIGFuc3dlciBhc3NvY2lhdGVkIHdpdGggdGhpcyBoYW5kbGUuICoqL1xuSmFudXNQbHVnaW5IYW5kbGUucHJvdG90eXBlLnNlbmRKc2VwID0gZnVuY3Rpb24oanNlcCkge1xuICByZXR1cm4gdGhpcy5zZW5kKFwibWVzc2FnZVwiLCB7IGJvZHk6IHt9LCBqc2VwOiBqc2VwIH0pO1xufTtcblxuLyoqIFNlbmRzIGFuIElDRSB0cmlja2xlIGNhbmRpZGF0ZSBhc3NvY2lhdGVkIHdpdGggdGhpcyBoYW5kbGUuICoqL1xuSmFudXNQbHVnaW5IYW5kbGUucHJvdG90eXBlLnNlbmRUcmlja2xlID0gZnVuY3Rpb24oY2FuZGlkYXRlKSB7XG4gIHJldHVybiB0aGlzLnNlbmQoXCJ0cmlja2xlXCIsIHsgY2FuZGlkYXRlOiBjYW5kaWRhdGUgfSk7XG59O1xuXG4vKipcbiAqIFJlcHJlc2VudHMgYSBKYW51cyBzZXNzaW9uIC0tIGEgSmFudXMgY29udGV4dCBmcm9tIHdpdGhpbiB3aGljaCB5b3UgY2FuIG9wZW4gbXVsdGlwbGUgaGFuZGxlcyBhbmQgY29ubmVjdGlvbnMuIE9uY2VcbiAqIGNyZWF0ZWQsIHRoaXMgc2Vzc2lvbiB3aWxsIGJlIGdpdmVuIGEgdW5pcXVlIElEIHdoaWNoIHNob3VsZCBiZSB1c2VkIHRvIGFzc29jaWF0ZSBpdCB3aXRoIGZ1dHVyZSBzaWduYWxsaW5nIG1lc3NhZ2VzLlxuICpcbiAqIFNlZSBodHRwczovL2phbnVzLmNvbmYubWVldGVjaG8uY29tL2RvY3MvcmVzdC5odG1sI3Nlc3Npb25zLlxuICoqL1xuZnVuY3Rpb24gSmFudXNTZXNzaW9uKG91dHB1dCwgb3B0aW9ucykge1xuICB0aGlzLm91dHB1dCA9IG91dHB1dDtcbiAgdGhpcy5pZCA9IHVuZGVmaW5lZDtcbiAgdGhpcy5uZXh0VHhJZCA9IDA7XG4gIHRoaXMudHhucyA9IHt9O1xuICB0aGlzLmV2ZW50SGFuZGxlcnMgPSB7fTtcbiAgdGhpcy5vcHRpb25zID0gT2JqZWN0LmFzc2lnbih7XG4gICAgdmVyYm9zZTogZmFsc2UsXG4gICAgdGltZW91dE1zOiAxMDAwMCxcbiAgICBrZWVwYWxpdmVNczogMzAwMDBcbiAgfSwgb3B0aW9ucyk7XG59XG5cbi8qKiBDcmVhdGVzIHRoaXMgc2Vzc2lvbiBvbiB0aGUgSmFudXMgc2VydmVyIGFuZCBzZXRzIGl0cyBJRC4gKiovXG5KYW51c1Nlc3Npb24ucHJvdG90eXBlLmNyZWF0ZSA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gdGhpcy5zZW5kKFwiY3JlYXRlXCIpLnRoZW4ocmVzcCA9PiB7XG4gICAgdGhpcy5pZCA9IHJlc3AuZGF0YS5pZDtcbiAgICByZXR1cm4gcmVzcDtcbiAgfSk7XG59O1xuXG4vKipcbiAqIERlc3Ryb3lzIHRoaXMgc2Vzc2lvbi4gTm90ZSB0aGF0IHVwb24gZGVzdHJ1Y3Rpb24sIEphbnVzIHdpbGwgYWxzbyBjbG9zZSB0aGUgc2lnbmFsbGluZyB0cmFuc3BvcnQgKGlmIGFwcGxpY2FibGUpIGFuZFxuICogYW55IG9wZW4gV2ViUlRDIGNvbm5lY3Rpb25zLlxuICoqL1xuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5kZXN0cm95ID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiB0aGlzLnNlbmQoXCJkZXN0cm95XCIpLnRoZW4oKHJlc3ApID0+IHtcbiAgICB0aGlzLmRpc3Bvc2UoKTtcbiAgICByZXR1cm4gcmVzcDtcbiAgfSk7XG59O1xuXG4vKipcbiAqIERpc3Bvc2VzIG9mIHRoaXMgc2Vzc2lvbiBpbiBhIHdheSBzdWNoIHRoYXQgbm8gZnVydGhlciBpbmNvbWluZyBzaWduYWxsaW5nIG1lc3NhZ2VzIHdpbGwgYmUgcHJvY2Vzc2VkLlxuICogT3V0c3RhbmRpbmcgdHJhbnNhY3Rpb25zIHdpbGwgYmUgcmVqZWN0ZWQuXG4gKiovXG5KYW51c1Nlc3Npb24ucHJvdG90eXBlLmRpc3Bvc2UgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5fa2lsbEtlZXBhbGl2ZSgpO1xuICB0aGlzLmV2ZW50SGFuZGxlcnMgPSB7fTtcbiAgZm9yICh2YXIgdHhJZCBpbiB0aGlzLnR4bnMpIHtcbiAgICBpZiAodGhpcy50eG5zLmhhc093blByb3BlcnR5KHR4SWQpKSB7XG4gICAgICB2YXIgdHhuID0gdGhpcy50eG5zW3R4SWRdO1xuICAgICAgY2xlYXJUaW1lb3V0KHR4bi50aW1lb3V0KTtcbiAgICAgIHR4bi5yZWplY3QobmV3IEVycm9yKFwiSmFudXMgc2Vzc2lvbiB3YXMgZGlzcG9zZWQuXCIpKTtcbiAgICAgIGRlbGV0ZSB0aGlzLnR4bnNbdHhJZF07XG4gICAgfVxuICB9XG59O1xuXG4vKipcbiAqIFdoZXRoZXIgdGhpcyBzaWduYWwgcmVwcmVzZW50cyBhbiBlcnJvciwgYW5kIHRoZSBhc3NvY2lhdGVkIHByb21pc2UgKGlmIGFueSkgc2hvdWxkIGJlIHJlamVjdGVkLlxuICogVXNlcnMgc2hvdWxkIG92ZXJyaWRlIHRoaXMgdG8gaGFuZGxlIGFueSBjdXN0b20gcGx1Z2luLXNwZWNpZmljIGVycm9yIGNvbnZlbnRpb25zLlxuICoqL1xuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5pc0Vycm9yID0gZnVuY3Rpb24oc2lnbmFsKSB7XG4gIHJldHVybiBzaWduYWwuamFudXMgPT09IFwiZXJyb3JcIjtcbn07XG5cbi8qKiBSZWdpc3RlcnMgYSBjYWxsYmFjayB0byBiZSBmaXJlZCB1cG9uIHRoZSByZWNlcHRpb24gb2YgYW55IGluY29taW5nIEphbnVzIHNpZ25hbHMgZm9yIHRoaXMgc2Vzc2lvbiB3aXRoIHRoZVxuICogYGphbnVzYCBhdHRyaWJ1dGUgZXF1YWwgdG8gYGV2YC5cbiAqKi9cbkphbnVzU2Vzc2lvbi5wcm90b3R5cGUub24gPSBmdW5jdGlvbihldiwgY2FsbGJhY2spIHtcbiAgdmFyIGhhbmRsZXJzID0gdGhpcy5ldmVudEhhbmRsZXJzW2V2XTtcbiAgaWYgKGhhbmRsZXJzID09IG51bGwpIHtcbiAgICBoYW5kbGVycyA9IHRoaXMuZXZlbnRIYW5kbGVyc1tldl0gPSBbXTtcbiAgfVxuICBoYW5kbGVycy5wdXNoKGNhbGxiYWNrKTtcbn07XG5cbi8qKlxuICogQ2FsbGJhY2sgZm9yIHJlY2VpdmluZyBKU09OIHNpZ25hbGxpbmcgbWVzc2FnZXMgcGVydGluZW50IHRvIHRoaXMgc2Vzc2lvbi4gSWYgdGhlIHNpZ25hbHMgYXJlIHJlc3BvbnNlcyB0byBwcmV2aW91c2x5XG4gKiBzZW50IHNpZ25hbHMsIHRoZSBwcm9taXNlcyBmb3IgdGhlIG91dGdvaW5nIHNpZ25hbHMgd2lsbCBiZSByZXNvbHZlZCBvciByZWplY3RlZCBhcHByb3ByaWF0ZWx5IHdpdGggdGhpcyBzaWduYWwgYXMgYW5cbiAqIGFyZ3VtZW50LlxuICpcbiAqIEV4dGVybmFsIGNhbGxlcnMgc2hvdWxkIGNhbGwgdGhpcyBmdW5jdGlvbiBldmVyeSB0aW1lIGEgbmV3IHNpZ25hbCBhcnJpdmVzIG9uIHRoZSB0cmFuc3BvcnQ7IGZvciBleGFtcGxlLCBpbiBhXG4gKiBXZWJTb2NrZXQncyBgbWVzc2FnZWAgZXZlbnQsIG9yIHdoZW4gYSBuZXcgZGF0dW0gc2hvd3MgdXAgaW4gYW4gSFRUUCBsb25nLXBvbGxpbmcgcmVzcG9uc2UuXG4gKiovXG5KYW51c1Nlc3Npb24ucHJvdG90eXBlLnJlY2VpdmUgPSBmdW5jdGlvbihzaWduYWwpIHtcbiAgaWYgKHRoaXMub3B0aW9ucy52ZXJib3NlKSB7XG4gICAgdGhpcy5fbG9nSW5jb21pbmcoc2lnbmFsKTtcbiAgfVxuICBpZiAoc2lnbmFsLnNlc3Npb25faWQgIT0gdGhpcy5pZCkge1xuICAgIGNvbnNvbGUud2FybihcIkluY29ycmVjdCBzZXNzaW9uIElEIHJlY2VpdmVkIGluIEphbnVzIHNpZ25hbGxpbmcgbWVzc2FnZTogd2FzIFwiICsgc2lnbmFsLnNlc3Npb25faWQgKyBcIiwgZXhwZWN0ZWQgXCIgKyB0aGlzLmlkICsgXCIuXCIpO1xuICB9XG5cbiAgdmFyIHJlc3BvbnNlVHlwZSA9IHNpZ25hbC5qYW51cztcbiAgdmFyIGhhbmRsZXJzID0gdGhpcy5ldmVudEhhbmRsZXJzW3Jlc3BvbnNlVHlwZV07XG4gIGlmIChoYW5kbGVycyAhPSBudWxsKSB7XG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBoYW5kbGVycy5sZW5ndGg7IGkrKykge1xuICAgICAgaGFuZGxlcnNbaV0oc2lnbmFsKTtcbiAgICB9XG4gIH1cblxuICBpZiAoc2lnbmFsLnRyYW5zYWN0aW9uICE9IG51bGwpIHtcbiAgICB2YXIgdHhuID0gdGhpcy50eG5zW3NpZ25hbC50cmFuc2FjdGlvbl07XG4gICAgaWYgKHR4biA9PSBudWxsKSB7XG4gICAgICAvLyB0aGlzIGlzIGEgcmVzcG9uc2UgdG8gYSB0cmFuc2FjdGlvbiB0aGF0IHdhc24ndCBjYXVzZWQgdmlhIEphbnVzU2Vzc2lvbi5zZW5kLCBvciBhIHBsdWdpbiByZXBsaWVkIHR3aWNlIHRvIGFcbiAgICAgIC8vIHNpbmdsZSByZXF1ZXN0LCBvciB0aGUgc2Vzc2lvbiB3YXMgZGlzcG9zZWQsIG9yIHNvbWV0aGluZyBlbHNlIHRoYXQgaXNuJ3QgdW5kZXIgb3VyIHB1cnZpZXc7IHRoYXQncyBmaW5lXG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKHJlc3BvbnNlVHlwZSA9PT0gXCJhY2tcIiAmJiB0eG4udHlwZSA9PSBcIm1lc3NhZ2VcIikge1xuICAgICAgLy8gdGhpcyBpcyBhbiBhY2sgb2YgYW4gYXN5bmNocm9ub3VzbHktcHJvY2Vzc2VkIHBsdWdpbiByZXF1ZXN0LCB3ZSBzaG91bGQgd2FpdCB0byByZXNvbHZlIHRoZSBwcm9taXNlIHVudGlsIHRoZVxuICAgICAgLy8gYWN0dWFsIHJlc3BvbnNlIGNvbWVzIGluXG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY2xlYXJUaW1lb3V0KHR4bi50aW1lb3V0KTtcblxuICAgIGRlbGV0ZSB0aGlzLnR4bnNbc2lnbmFsLnRyYW5zYWN0aW9uXTtcbiAgICAodGhpcy5pc0Vycm9yKHNpZ25hbCkgPyB0eG4ucmVqZWN0IDogdHhuLnJlc29sdmUpKHNpZ25hbCk7XG4gIH1cbn07XG5cbi8qKlxuICogU2VuZHMgYSBzaWduYWwgYXNzb2NpYXRlZCB3aXRoIHRoaXMgc2Vzc2lvbiwgYmVnaW5uaW5nIGEgbmV3IHRyYW5zYWN0aW9uLiBSZXR1cm5zIGEgcHJvbWlzZSB0aGF0IHdpbGwgYmUgcmVzb2x2ZWQgb3JcbiAqIHJlamVjdGVkIHdoZW4gYSByZXNwb25zZSBpcyByZWNlaXZlZCBpbiB0aGUgc2FtZSB0cmFuc2FjdGlvbiwgb3Igd2hlbiBubyByZXNwb25zZSBpcyByZWNlaXZlZCB3aXRoaW4gdGhlIHNlc3Npb25cbiAqIHRpbWVvdXQuXG4gKiovXG5KYW51c1Nlc3Npb24ucHJvdG90eXBlLnNlbmQgPSBmdW5jdGlvbih0eXBlLCBzaWduYWwpIHtcbiAgc2lnbmFsID0gT2JqZWN0LmFzc2lnbih7IHRyYW5zYWN0aW9uOiAodGhpcy5uZXh0VHhJZCsrKS50b1N0cmluZygpIH0sIHNpZ25hbCk7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgdmFyIHRpbWVvdXQgPSBudWxsO1xuICAgIGlmICh0aGlzLm9wdGlvbnMudGltZW91dE1zKSB7XG4gICAgICB0aW1lb3V0ID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgIGRlbGV0ZSB0aGlzLnR4bnNbc2lnbmFsLnRyYW5zYWN0aW9uXTtcbiAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihcIlNpZ25hbGxpbmcgdHJhbnNhY3Rpb24gd2l0aCB0eGlkIFwiICsgc2lnbmFsLnRyYW5zYWN0aW9uICsgXCIgdGltZWQgb3V0LlwiKSk7XG4gICAgICB9LCB0aGlzLm9wdGlvbnMudGltZW91dE1zKTtcbiAgICB9XG4gICAgdGhpcy50eG5zW3NpZ25hbC50cmFuc2FjdGlvbl0gPSB7IHJlc29sdmU6IHJlc29sdmUsIHJlamVjdDogcmVqZWN0LCB0aW1lb3V0OiB0aW1lb3V0LCB0eXBlOiB0eXBlIH07XG4gICAgdGhpcy5fdHJhbnNtaXQodHlwZSwgc2lnbmFsKTtcbiAgfSk7XG59O1xuXG5KYW51c1Nlc3Npb24ucHJvdG90eXBlLl90cmFuc21pdCA9IGZ1bmN0aW9uKHR5cGUsIHNpZ25hbCkge1xuICBzaWduYWwgPSBPYmplY3QuYXNzaWduKHsgamFudXM6IHR5cGUgfSwgc2lnbmFsKTtcblxuICBpZiAodGhpcy5pZCAhPSBudWxsKSB7IC8vIHRoaXMuaWQgaXMgdW5kZWZpbmVkIGluIHRoZSBzcGVjaWFsIGNhc2Ugd2hlbiB3ZSdyZSBzZW5kaW5nIHRoZSBzZXNzaW9uIGNyZWF0ZSBtZXNzYWdlXG4gICAgc2lnbmFsID0gT2JqZWN0LmFzc2lnbih7IHNlc3Npb25faWQ6IHRoaXMuaWQgfSwgc2lnbmFsKTtcbiAgfVxuXG4gIGlmICh0aGlzLm9wdGlvbnMudmVyYm9zZSkge1xuICAgIHRoaXMuX2xvZ091dGdvaW5nKHNpZ25hbCk7XG4gIH1cblxuICB0aGlzLm91dHB1dChKU09OLnN0cmluZ2lmeShzaWduYWwpKTtcbiAgdGhpcy5fcmVzZXRLZWVwYWxpdmUoKTtcbn07XG5cbkphbnVzU2Vzc2lvbi5wcm90b3R5cGUuX2xvZ091dGdvaW5nID0gZnVuY3Rpb24oc2lnbmFsKSB7XG4gIHZhciBraW5kID0gc2lnbmFsLmphbnVzO1xuICBpZiAoa2luZCA9PT0gXCJtZXNzYWdlXCIgJiYgc2lnbmFsLmpzZXApIHtcbiAgICBraW5kID0gc2lnbmFsLmpzZXAudHlwZTtcbiAgfVxuICB2YXIgbWVzc2FnZSA9IFwiPiBPdXRnb2luZyBKYW51cyBcIiArIChraW5kIHx8IFwic2lnbmFsXCIpICsgXCIgKCNcIiArIHNpZ25hbC50cmFuc2FjdGlvbiArIFwiKTogXCI7XG4gIGNvbnNvbGUuZGVidWcoXCIlY1wiICsgbWVzc2FnZSwgXCJjb2xvcjogIzA0MFwiLCBzaWduYWwpO1xufTtcblxuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5fbG9nSW5jb21pbmcgPSBmdW5jdGlvbihzaWduYWwpIHtcbiAgdmFyIGtpbmQgPSBzaWduYWwuamFudXM7XG4gIHZhciBtZXNzYWdlID0gc2lnbmFsLnRyYW5zYWN0aW9uID9cbiAgICAgIFwiPCBJbmNvbWluZyBKYW51cyBcIiArIChraW5kIHx8IFwic2lnbmFsXCIpICsgXCIgKCNcIiArIHNpZ25hbC50cmFuc2FjdGlvbiArIFwiKTogXCIgOlxuICAgICAgXCI8IEluY29taW5nIEphbnVzIFwiICsgKGtpbmQgfHwgXCJzaWduYWxcIikgKyBcIjogXCI7XG4gIGNvbnNvbGUuZGVidWcoXCIlY1wiICsgbWVzc2FnZSwgXCJjb2xvcjogIzAwNFwiLCBzaWduYWwpO1xufTtcblxuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5fc2VuZEtlZXBhbGl2ZSA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gdGhpcy5zZW5kKFwia2VlcGFsaXZlXCIpO1xufTtcblxuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5fa2lsbEtlZXBhbGl2ZSA9IGZ1bmN0aW9uKCkge1xuICBjbGVhclRpbWVvdXQodGhpcy5rZWVwYWxpdmVUaW1lb3V0KTtcbn07XG5cbkphbnVzU2Vzc2lvbi5wcm90b3R5cGUuX3Jlc2V0S2VlcGFsaXZlID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMuX2tpbGxLZWVwYWxpdmUoKTtcbiAgaWYgKHRoaXMub3B0aW9ucy5rZWVwYWxpdmVNcykge1xuICAgIHRoaXMua2VlcGFsaXZlVGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgdGhpcy5fc2VuZEtlZXBhbGl2ZSgpLmNhdGNoKGUgPT4gY29uc29sZS5lcnJvcihcIkVycm9yIHJlY2VpdmVkIGZyb20ga2VlcGFsaXZlOiBcIiwgZSkpO1xuICAgIH0sIHRoaXMub3B0aW9ucy5rZWVwYWxpdmVNcyk7XG4gIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBKYW51c1BsdWdpbkhhbmRsZSxcbiAgSmFudXNTZXNzaW9uXG59O1xuIiwiLyogZXNsaW50LWVudiBub2RlICovXG4ndXNlIHN0cmljdCc7XG5cbi8vIFNEUCBoZWxwZXJzLlxuY29uc3QgU0RQVXRpbHMgPSB7fTtcblxuLy8gR2VuZXJhdGUgYW4gYWxwaGFudW1lcmljIGlkZW50aWZpZXIgZm9yIGNuYW1lIG9yIG1pZHMuXG4vLyBUT0RPOiB1c2UgVVVJRHMgaW5zdGVhZD8gaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vamVkLzk4Mjg4M1xuU0RQVXRpbHMuZ2VuZXJhdGVJZGVudGlmaWVyID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zdWJzdHIoMiwgMTApO1xufTtcblxuLy8gVGhlIFJUQ1AgQ05BTUUgdXNlZCBieSBhbGwgcGVlcmNvbm5lY3Rpb25zIGZyb20gdGhlIHNhbWUgSlMuXG5TRFBVdGlscy5sb2NhbENOYW1lID0gU0RQVXRpbHMuZ2VuZXJhdGVJZGVudGlmaWVyKCk7XG5cbi8vIFNwbGl0cyBTRFAgaW50byBsaW5lcywgZGVhbGluZyB3aXRoIGJvdGggQ1JMRiBhbmQgTEYuXG5TRFBVdGlscy5zcGxpdExpbmVzID0gZnVuY3Rpb24oYmxvYikge1xuICByZXR1cm4gYmxvYi50cmltKCkuc3BsaXQoJ1xcbicpLm1hcChsaW5lID0+IGxpbmUudHJpbSgpKTtcbn07XG4vLyBTcGxpdHMgU0RQIGludG8gc2Vzc2lvbnBhcnQgYW5kIG1lZGlhc2VjdGlvbnMuIEVuc3VyZXMgQ1JMRi5cblNEUFV0aWxzLnNwbGl0U2VjdGlvbnMgPSBmdW5jdGlvbihibG9iKSB7XG4gIGNvbnN0IHBhcnRzID0gYmxvYi5zcGxpdCgnXFxubT0nKTtcbiAgcmV0dXJuIHBhcnRzLm1hcCgocGFydCwgaW5kZXgpID0+IChpbmRleCA+IDAgP1xuICAgICdtPScgKyBwYXJ0IDogcGFydCkudHJpbSgpICsgJ1xcclxcbicpO1xufTtcblxuLy8gUmV0dXJucyB0aGUgc2Vzc2lvbiBkZXNjcmlwdGlvbi5cblNEUFV0aWxzLmdldERlc2NyaXB0aW9uID0gZnVuY3Rpb24oYmxvYikge1xuICBjb25zdCBzZWN0aW9ucyA9IFNEUFV0aWxzLnNwbGl0U2VjdGlvbnMoYmxvYik7XG4gIHJldHVybiBzZWN0aW9ucyAmJiBzZWN0aW9uc1swXTtcbn07XG5cbi8vIFJldHVybnMgdGhlIGluZGl2aWR1YWwgbWVkaWEgc2VjdGlvbnMuXG5TRFBVdGlscy5nZXRNZWRpYVNlY3Rpb25zID0gZnVuY3Rpb24oYmxvYikge1xuICBjb25zdCBzZWN0aW9ucyA9IFNEUFV0aWxzLnNwbGl0U2VjdGlvbnMoYmxvYik7XG4gIHNlY3Rpb25zLnNoaWZ0KCk7XG4gIHJldHVybiBzZWN0aW9ucztcbn07XG5cbi8vIFJldHVybnMgbGluZXMgdGhhdCBzdGFydCB3aXRoIGEgY2VydGFpbiBwcmVmaXguXG5TRFBVdGlscy5tYXRjaFByZWZpeCA9IGZ1bmN0aW9uKGJsb2IsIHByZWZpeCkge1xuICByZXR1cm4gU0RQVXRpbHMuc3BsaXRMaW5lcyhibG9iKS5maWx0ZXIobGluZSA9PiBsaW5lLmluZGV4T2YocHJlZml4KSA9PT0gMCk7XG59O1xuXG4vLyBQYXJzZXMgYW4gSUNFIGNhbmRpZGF0ZSBsaW5lLiBTYW1wbGUgaW5wdXQ6XG4vLyBjYW5kaWRhdGU6NzAyNzg2MzUwIDIgdWRwIDQxODE5OTAyIDguOC44LjggNjA3NjkgdHlwIHJlbGF5IHJhZGRyIDguOC44Ljhcbi8vIHJwb3J0IDU1OTk2XCJcbi8vIElucHV0IGNhbiBiZSBwcmVmaXhlZCB3aXRoIGE9LlxuU0RQVXRpbHMucGFyc2VDYW5kaWRhdGUgPSBmdW5jdGlvbihsaW5lKSB7XG4gIGxldCBwYXJ0cztcbiAgLy8gUGFyc2UgYm90aCB2YXJpYW50cy5cbiAgaWYgKGxpbmUuaW5kZXhPZignYT1jYW5kaWRhdGU6JykgPT09IDApIHtcbiAgICBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDEyKS5zcGxpdCgnICcpO1xuICB9IGVsc2Uge1xuICAgIHBhcnRzID0gbGluZS5zdWJzdHJpbmcoMTApLnNwbGl0KCcgJyk7XG4gIH1cblxuICBjb25zdCBjYW5kaWRhdGUgPSB7XG4gICAgZm91bmRhdGlvbjogcGFydHNbMF0sXG4gICAgY29tcG9uZW50OiB7MTogJ3J0cCcsIDI6ICdydGNwJ31bcGFydHNbMV1dIHx8IHBhcnRzWzFdLFxuICAgIHByb3RvY29sOiBwYXJ0c1syXS50b0xvd2VyQ2FzZSgpLFxuICAgIHByaW9yaXR5OiBwYXJzZUludChwYXJ0c1szXSwgMTApLFxuICAgIGlwOiBwYXJ0c1s0XSxcbiAgICBhZGRyZXNzOiBwYXJ0c1s0XSwgLy8gYWRkcmVzcyBpcyBhbiBhbGlhcyBmb3IgaXAuXG4gICAgcG9ydDogcGFyc2VJbnQocGFydHNbNV0sIDEwKSxcbiAgICAvLyBza2lwIHBhcnRzWzZdID09ICd0eXAnXG4gICAgdHlwZTogcGFydHNbN10sXG4gIH07XG5cbiAgZm9yIChsZXQgaSA9IDg7IGkgPCBwYXJ0cy5sZW5ndGg7IGkgKz0gMikge1xuICAgIHN3aXRjaCAocGFydHNbaV0pIHtcbiAgICAgIGNhc2UgJ3JhZGRyJzpcbiAgICAgICAgY2FuZGlkYXRlLnJlbGF0ZWRBZGRyZXNzID0gcGFydHNbaSArIDFdO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ3Jwb3J0JzpcbiAgICAgICAgY2FuZGlkYXRlLnJlbGF0ZWRQb3J0ID0gcGFyc2VJbnQocGFydHNbaSArIDFdLCAxMCk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAndGNwdHlwZSc6XG4gICAgICAgIGNhbmRpZGF0ZS50Y3BUeXBlID0gcGFydHNbaSArIDFdO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ3VmcmFnJzpcbiAgICAgICAgY2FuZGlkYXRlLnVmcmFnID0gcGFydHNbaSArIDFdOyAvLyBmb3IgYmFja3dhcmQgY29tcGF0aWJpbGl0eS5cbiAgICAgICAgY2FuZGlkYXRlLnVzZXJuYW1lRnJhZ21lbnQgPSBwYXJ0c1tpICsgMV07XG4gICAgICAgIGJyZWFrO1xuICAgICAgZGVmYXVsdDogLy8gZXh0ZW5zaW9uIGhhbmRsaW5nLCBpbiBwYXJ0aWN1bGFyIHVmcmFnLiBEb24ndCBvdmVyd3JpdGUuXG4gICAgICAgIGlmIChjYW5kaWRhdGVbcGFydHNbaV1dID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICBjYW5kaWRhdGVbcGFydHNbaV1dID0gcGFydHNbaSArIDFdO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgIH1cbiAgfVxuICByZXR1cm4gY2FuZGlkYXRlO1xufTtcblxuLy8gVHJhbnNsYXRlcyBhIGNhbmRpZGF0ZSBvYmplY3QgaW50byBTRFAgY2FuZGlkYXRlIGF0dHJpYnV0ZS5cbi8vIFRoaXMgZG9lcyBub3QgaW5jbHVkZSB0aGUgYT0gcHJlZml4IVxuU0RQVXRpbHMud3JpdGVDYW5kaWRhdGUgPSBmdW5jdGlvbihjYW5kaWRhdGUpIHtcbiAgY29uc3Qgc2RwID0gW107XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5mb3VuZGF0aW9uKTtcblxuICBjb25zdCBjb21wb25lbnQgPSBjYW5kaWRhdGUuY29tcG9uZW50O1xuICBpZiAoY29tcG9uZW50ID09PSAncnRwJykge1xuICAgIHNkcC5wdXNoKDEpO1xuICB9IGVsc2UgaWYgKGNvbXBvbmVudCA9PT0gJ3J0Y3AnKSB7XG4gICAgc2RwLnB1c2goMik7XG4gIH0gZWxzZSB7XG4gICAgc2RwLnB1c2goY29tcG9uZW50KTtcbiAgfVxuICBzZHAucHVzaChjYW5kaWRhdGUucHJvdG9jb2wudG9VcHBlckNhc2UoKSk7XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5wcmlvcml0eSk7XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5hZGRyZXNzIHx8IGNhbmRpZGF0ZS5pcCk7XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5wb3J0KTtcblxuICBjb25zdCB0eXBlID0gY2FuZGlkYXRlLnR5cGU7XG4gIHNkcC5wdXNoKCd0eXAnKTtcbiAgc2RwLnB1c2godHlwZSk7XG4gIGlmICh0eXBlICE9PSAnaG9zdCcgJiYgY2FuZGlkYXRlLnJlbGF0ZWRBZGRyZXNzICYmXG4gICAgICBjYW5kaWRhdGUucmVsYXRlZFBvcnQpIHtcbiAgICBzZHAucHVzaCgncmFkZHInKTtcbiAgICBzZHAucHVzaChjYW5kaWRhdGUucmVsYXRlZEFkZHJlc3MpO1xuICAgIHNkcC5wdXNoKCdycG9ydCcpO1xuICAgIHNkcC5wdXNoKGNhbmRpZGF0ZS5yZWxhdGVkUG9ydCk7XG4gIH1cbiAgaWYgKGNhbmRpZGF0ZS50Y3BUeXBlICYmIGNhbmRpZGF0ZS5wcm90b2NvbC50b0xvd2VyQ2FzZSgpID09PSAndGNwJykge1xuICAgIHNkcC5wdXNoKCd0Y3B0eXBlJyk7XG4gICAgc2RwLnB1c2goY2FuZGlkYXRlLnRjcFR5cGUpO1xuICB9XG4gIGlmIChjYW5kaWRhdGUudXNlcm5hbWVGcmFnbWVudCB8fCBjYW5kaWRhdGUudWZyYWcpIHtcbiAgICBzZHAucHVzaCgndWZyYWcnKTtcbiAgICBzZHAucHVzaChjYW5kaWRhdGUudXNlcm5hbWVGcmFnbWVudCB8fCBjYW5kaWRhdGUudWZyYWcpO1xuICB9XG4gIHJldHVybiAnY2FuZGlkYXRlOicgKyBzZHAuam9pbignICcpO1xufTtcblxuLy8gUGFyc2VzIGFuIGljZS1vcHRpb25zIGxpbmUsIHJldHVybnMgYW4gYXJyYXkgb2Ygb3B0aW9uIHRhZ3MuXG4vLyBTYW1wbGUgaW5wdXQ6XG4vLyBhPWljZS1vcHRpb25zOmZvbyBiYXJcblNEUFV0aWxzLnBhcnNlSWNlT3B0aW9ucyA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgcmV0dXJuIGxpbmUuc3Vic3RyKDE0KS5zcGxpdCgnICcpO1xufTtcblxuLy8gUGFyc2VzIGEgcnRwbWFwIGxpbmUsIHJldHVybnMgUlRDUnRwQ29kZGVjUGFyYW1ldGVycy4gU2FtcGxlIGlucHV0OlxuLy8gYT1ydHBtYXA6MTExIG9wdXMvNDgwMDAvMlxuU0RQVXRpbHMucGFyc2VSdHBNYXAgPSBmdW5jdGlvbihsaW5lKSB7XG4gIGxldCBwYXJ0cyA9IGxpbmUuc3Vic3RyKDkpLnNwbGl0KCcgJyk7XG4gIGNvbnN0IHBhcnNlZCA9IHtcbiAgICBwYXlsb2FkVHlwZTogcGFyc2VJbnQocGFydHMuc2hpZnQoKSwgMTApLCAvLyB3YXM6IGlkXG4gIH07XG5cbiAgcGFydHMgPSBwYXJ0c1swXS5zcGxpdCgnLycpO1xuXG4gIHBhcnNlZC5uYW1lID0gcGFydHNbMF07XG4gIHBhcnNlZC5jbG9ja1JhdGUgPSBwYXJzZUludChwYXJ0c1sxXSwgMTApOyAvLyB3YXM6IGNsb2NrcmF0ZVxuICBwYXJzZWQuY2hhbm5lbHMgPSBwYXJ0cy5sZW5ndGggPT09IDMgPyBwYXJzZUludChwYXJ0c1syXSwgMTApIDogMTtcbiAgLy8gbGVnYWN5IGFsaWFzLCBnb3QgcmVuYW1lZCBiYWNrIHRvIGNoYW5uZWxzIGluIE9SVEMuXG4gIHBhcnNlZC5udW1DaGFubmVscyA9IHBhcnNlZC5jaGFubmVscztcbiAgcmV0dXJuIHBhcnNlZDtcbn07XG5cbi8vIEdlbmVyYXRlcyBhIHJ0cG1hcCBsaW5lIGZyb20gUlRDUnRwQ29kZWNDYXBhYmlsaXR5IG9yXG4vLyBSVENSdHBDb2RlY1BhcmFtZXRlcnMuXG5TRFBVdGlscy53cml0ZVJ0cE1hcCA9IGZ1bmN0aW9uKGNvZGVjKSB7XG4gIGxldCBwdCA9IGNvZGVjLnBheWxvYWRUeXBlO1xuICBpZiAoY29kZWMucHJlZmVycmVkUGF5bG9hZFR5cGUgIT09IHVuZGVmaW5lZCkge1xuICAgIHB0ID0gY29kZWMucHJlZmVycmVkUGF5bG9hZFR5cGU7XG4gIH1cbiAgY29uc3QgY2hhbm5lbHMgPSBjb2RlYy5jaGFubmVscyB8fCBjb2RlYy5udW1DaGFubmVscyB8fCAxO1xuICByZXR1cm4gJ2E9cnRwbWFwOicgKyBwdCArICcgJyArIGNvZGVjLm5hbWUgKyAnLycgKyBjb2RlYy5jbG9ja1JhdGUgK1xuICAgICAgKGNoYW5uZWxzICE9PSAxID8gJy8nICsgY2hhbm5lbHMgOiAnJykgKyAnXFxyXFxuJztcbn07XG5cbi8vIFBhcnNlcyBhIGV4dG1hcCBsaW5lIChoZWFkZXJleHRlbnNpb24gZnJvbSBSRkMgNTI4NSkuIFNhbXBsZSBpbnB1dDpcbi8vIGE9ZXh0bWFwOjIgdXJuOmlldGY6cGFyYW1zOnJ0cC1oZHJleHQ6dG9mZnNldFxuLy8gYT1leHRtYXA6Mi9zZW5kb25seSB1cm46aWV0ZjpwYXJhbXM6cnRwLWhkcmV4dDp0b2Zmc2V0XG5TRFBVdGlscy5wYXJzZUV4dG1hcCA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgY29uc3QgcGFydHMgPSBsaW5lLnN1YnN0cig5KS5zcGxpdCgnICcpO1xuICByZXR1cm4ge1xuICAgIGlkOiBwYXJzZUludChwYXJ0c1swXSwgMTApLFxuICAgIGRpcmVjdGlvbjogcGFydHNbMF0uaW5kZXhPZignLycpID4gMCA/IHBhcnRzWzBdLnNwbGl0KCcvJylbMV0gOiAnc2VuZHJlY3YnLFxuICAgIHVyaTogcGFydHNbMV0sXG4gIH07XG59O1xuXG4vLyBHZW5lcmF0ZXMgYW4gZXh0bWFwIGxpbmUgZnJvbSBSVENSdHBIZWFkZXJFeHRlbnNpb25QYXJhbWV0ZXJzIG9yXG4vLyBSVENSdHBIZWFkZXJFeHRlbnNpb24uXG5TRFBVdGlscy53cml0ZUV4dG1hcCA9IGZ1bmN0aW9uKGhlYWRlckV4dGVuc2lvbikge1xuICByZXR1cm4gJ2E9ZXh0bWFwOicgKyAoaGVhZGVyRXh0ZW5zaW9uLmlkIHx8IGhlYWRlckV4dGVuc2lvbi5wcmVmZXJyZWRJZCkgK1xuICAgICAgKGhlYWRlckV4dGVuc2lvbi5kaXJlY3Rpb24gJiYgaGVhZGVyRXh0ZW5zaW9uLmRpcmVjdGlvbiAhPT0gJ3NlbmRyZWN2J1xuICAgICAgICA/ICcvJyArIGhlYWRlckV4dGVuc2lvbi5kaXJlY3Rpb25cbiAgICAgICAgOiAnJykgK1xuICAgICAgJyAnICsgaGVhZGVyRXh0ZW5zaW9uLnVyaSArICdcXHJcXG4nO1xufTtcblxuLy8gUGFyc2VzIGEgZm10cCBsaW5lLCByZXR1cm5zIGRpY3Rpb25hcnkuIFNhbXBsZSBpbnB1dDpcbi8vIGE9Zm10cDo5NiB2YnI9b247Y25nPW9uXG4vLyBBbHNvIGRlYWxzIHdpdGggdmJyPW9uOyBjbmc9b25cblNEUFV0aWxzLnBhcnNlRm10cCA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgY29uc3QgcGFyc2VkID0ge307XG4gIGxldCBrdjtcbiAgY29uc3QgcGFydHMgPSBsaW5lLnN1YnN0cihsaW5lLmluZGV4T2YoJyAnKSArIDEpLnNwbGl0KCc7Jyk7XG4gIGZvciAobGV0IGogPSAwOyBqIDwgcGFydHMubGVuZ3RoOyBqKyspIHtcbiAgICBrdiA9IHBhcnRzW2pdLnRyaW0oKS5zcGxpdCgnPScpO1xuICAgIHBhcnNlZFtrdlswXS50cmltKCldID0ga3ZbMV07XG4gIH1cbiAgcmV0dXJuIHBhcnNlZDtcbn07XG5cbi8vIEdlbmVyYXRlcyBhIGZtdHAgbGluZSBmcm9tIFJUQ1J0cENvZGVjQ2FwYWJpbGl0eSBvciBSVENSdHBDb2RlY1BhcmFtZXRlcnMuXG5TRFBVdGlscy53cml0ZUZtdHAgPSBmdW5jdGlvbihjb2RlYykge1xuICBsZXQgbGluZSA9ICcnO1xuICBsZXQgcHQgPSBjb2RlYy5wYXlsb2FkVHlwZTtcbiAgaWYgKGNvZGVjLnByZWZlcnJlZFBheWxvYWRUeXBlICE9PSB1bmRlZmluZWQpIHtcbiAgICBwdCA9IGNvZGVjLnByZWZlcnJlZFBheWxvYWRUeXBlO1xuICB9XG4gIGlmIChjb2RlYy5wYXJhbWV0ZXJzICYmIE9iamVjdC5rZXlzKGNvZGVjLnBhcmFtZXRlcnMpLmxlbmd0aCkge1xuICAgIGNvbnN0IHBhcmFtcyA9IFtdO1xuICAgIE9iamVjdC5rZXlzKGNvZGVjLnBhcmFtZXRlcnMpLmZvckVhY2gocGFyYW0gPT4ge1xuICAgICAgaWYgKGNvZGVjLnBhcmFtZXRlcnNbcGFyYW1dICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcGFyYW1zLnB1c2gocGFyYW0gKyAnPScgKyBjb2RlYy5wYXJhbWV0ZXJzW3BhcmFtXSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBwYXJhbXMucHVzaChwYXJhbSk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgbGluZSArPSAnYT1mbXRwOicgKyBwdCArICcgJyArIHBhcmFtcy5qb2luKCc7JykgKyAnXFxyXFxuJztcbiAgfVxuICByZXR1cm4gbGluZTtcbn07XG5cbi8vIFBhcnNlcyBhIHJ0Y3AtZmIgbGluZSwgcmV0dXJucyBSVENQUnRjcEZlZWRiYWNrIG9iamVjdC4gU2FtcGxlIGlucHV0OlxuLy8gYT1ydGNwLWZiOjk4IG5hY2sgcnBzaVxuU0RQVXRpbHMucGFyc2VSdGNwRmIgPSBmdW5jdGlvbihsaW5lKSB7XG4gIGNvbnN0IHBhcnRzID0gbGluZS5zdWJzdHIobGluZS5pbmRleE9mKCcgJykgKyAxKS5zcGxpdCgnICcpO1xuICByZXR1cm4ge1xuICAgIHR5cGU6IHBhcnRzLnNoaWZ0KCksXG4gICAgcGFyYW1ldGVyOiBwYXJ0cy5qb2luKCcgJyksXG4gIH07XG59O1xuXG4vLyBHZW5lcmF0ZSBhPXJ0Y3AtZmIgbGluZXMgZnJvbSBSVENSdHBDb2RlY0NhcGFiaWxpdHkgb3IgUlRDUnRwQ29kZWNQYXJhbWV0ZXJzLlxuU0RQVXRpbHMud3JpdGVSdGNwRmIgPSBmdW5jdGlvbihjb2RlYykge1xuICBsZXQgbGluZXMgPSAnJztcbiAgbGV0IHB0ID0gY29kZWMucGF5bG9hZFR5cGU7XG4gIGlmIChjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgcHQgPSBjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZTtcbiAgfVxuICBpZiAoY29kZWMucnRjcEZlZWRiYWNrICYmIGNvZGVjLnJ0Y3BGZWVkYmFjay5sZW5ndGgpIHtcbiAgICAvLyBGSVhNRTogc3BlY2lhbCBoYW5kbGluZyBmb3IgdHJyLWludD9cbiAgICBjb2RlYy5ydGNwRmVlZGJhY2suZm9yRWFjaChmYiA9PiB7XG4gICAgICBsaW5lcyArPSAnYT1ydGNwLWZiOicgKyBwdCArICcgJyArIGZiLnR5cGUgK1xuICAgICAgKGZiLnBhcmFtZXRlciAmJiBmYi5wYXJhbWV0ZXIubGVuZ3RoID8gJyAnICsgZmIucGFyYW1ldGVyIDogJycpICtcbiAgICAgICAgICAnXFxyXFxuJztcbiAgICB9KTtcbiAgfVxuICByZXR1cm4gbGluZXM7XG59O1xuXG4vLyBQYXJzZXMgYSBSRkMgNTU3NiBzc3JjIG1lZGlhIGF0dHJpYnV0ZS4gU2FtcGxlIGlucHV0OlxuLy8gYT1zc3JjOjM3MzU5Mjg1NTkgY25hbWU6c29tZXRoaW5nXG5TRFBVdGlscy5wYXJzZVNzcmNNZWRpYSA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgY29uc3Qgc3AgPSBsaW5lLmluZGV4T2YoJyAnKTtcbiAgY29uc3QgcGFydHMgPSB7XG4gICAgc3NyYzogcGFyc2VJbnQobGluZS5zdWJzdHIoNywgc3AgLSA3KSwgMTApLFxuICB9O1xuICBjb25zdCBjb2xvbiA9IGxpbmUuaW5kZXhPZignOicsIHNwKTtcbiAgaWYgKGNvbG9uID4gLTEpIHtcbiAgICBwYXJ0cy5hdHRyaWJ1dGUgPSBsaW5lLnN1YnN0cihzcCArIDEsIGNvbG9uIC0gc3AgLSAxKTtcbiAgICBwYXJ0cy52YWx1ZSA9IGxpbmUuc3Vic3RyKGNvbG9uICsgMSk7XG4gIH0gZWxzZSB7XG4gICAgcGFydHMuYXR0cmlidXRlID0gbGluZS5zdWJzdHIoc3AgKyAxKTtcbiAgfVxuICByZXR1cm4gcGFydHM7XG59O1xuXG4vLyBQYXJzZSBhIHNzcmMtZ3JvdXAgbGluZSAoc2VlIFJGQyA1NTc2KS4gU2FtcGxlIGlucHV0OlxuLy8gYT1zc3JjLWdyb3VwOnNlbWFudGljcyAxMiAzNFxuU0RQVXRpbHMucGFyc2VTc3JjR3JvdXAgPSBmdW5jdGlvbihsaW5lKSB7XG4gIGNvbnN0IHBhcnRzID0gbGluZS5zdWJzdHIoMTMpLnNwbGl0KCcgJyk7XG4gIHJldHVybiB7XG4gICAgc2VtYW50aWNzOiBwYXJ0cy5zaGlmdCgpLFxuICAgIHNzcmNzOiBwYXJ0cy5tYXAoc3NyYyA9PiBwYXJzZUludChzc3JjLCAxMCkpLFxuICB9O1xufTtcblxuLy8gRXh0cmFjdHMgdGhlIE1JRCAoUkZDIDU4ODgpIGZyb20gYSBtZWRpYSBzZWN0aW9uLlxuLy8gUmV0dXJucyB0aGUgTUlEIG9yIHVuZGVmaW5lZCBpZiBubyBtaWQgbGluZSB3YXMgZm91bmQuXG5TRFBVdGlscy5nZXRNaWQgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgY29uc3QgbWlkID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1taWQ6JylbMF07XG4gIGlmIChtaWQpIHtcbiAgICByZXR1cm4gbWlkLnN1YnN0cig2KTtcbiAgfVxufTtcblxuLy8gUGFyc2VzIGEgZmluZ2VycHJpbnQgbGluZSBmb3IgRFRMUy1TUlRQLlxuU0RQVXRpbHMucGFyc2VGaW5nZXJwcmludCA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgY29uc3QgcGFydHMgPSBsaW5lLnN1YnN0cigxNCkuc3BsaXQoJyAnKTtcbiAgcmV0dXJuIHtcbiAgICBhbGdvcml0aG06IHBhcnRzWzBdLnRvTG93ZXJDYXNlKCksIC8vIGFsZ29yaXRobSBpcyBjYXNlLXNlbnNpdGl2ZSBpbiBFZGdlLlxuICAgIHZhbHVlOiBwYXJ0c1sxXS50b1VwcGVyQ2FzZSgpLCAvLyB0aGUgZGVmaW5pdGlvbiBpcyB1cHBlci1jYXNlIGluIFJGQyA0NTcyLlxuICB9O1xufTtcblxuLy8gRXh0cmFjdHMgRFRMUyBwYXJhbWV0ZXJzIGZyb20gU0RQIG1lZGlhIHNlY3Rpb24gb3Igc2Vzc2lvbnBhcnQuXG4vLyBGSVhNRTogZm9yIGNvbnNpc3RlbmN5IHdpdGggb3RoZXIgZnVuY3Rpb25zIHRoaXMgc2hvdWxkIG9ubHlcbi8vICAgZ2V0IHRoZSBmaW5nZXJwcmludCBsaW5lIGFzIGlucHV0LiBTZWUgYWxzbyBnZXRJY2VQYXJhbWV0ZXJzLlxuU0RQVXRpbHMuZ2V0RHRsc1BhcmFtZXRlcnMgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24sIHNlc3Npb25wYXJ0KSB7XG4gIGNvbnN0IGxpbmVzID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uICsgc2Vzc2lvbnBhcnQsXG4gICAgJ2E9ZmluZ2VycHJpbnQ6Jyk7XG4gIC8vIE5vdGU6IGE9c2V0dXAgbGluZSBpcyBpZ25vcmVkIHNpbmNlIHdlIHVzZSB0aGUgJ2F1dG8nIHJvbGUgaW4gRWRnZS5cbiAgcmV0dXJuIHtcbiAgICByb2xlOiAnYXV0bycsXG4gICAgZmluZ2VycHJpbnRzOiBsaW5lcy5tYXAoU0RQVXRpbHMucGFyc2VGaW5nZXJwcmludCksXG4gIH07XG59O1xuXG4vLyBTZXJpYWxpemVzIERUTFMgcGFyYW1ldGVycyB0byBTRFAuXG5TRFBVdGlscy53cml0ZUR0bHNQYXJhbWV0ZXJzID0gZnVuY3Rpb24ocGFyYW1zLCBzZXR1cFR5cGUpIHtcbiAgbGV0IHNkcCA9ICdhPXNldHVwOicgKyBzZXR1cFR5cGUgKyAnXFxyXFxuJztcbiAgcGFyYW1zLmZpbmdlcnByaW50cy5mb3JFYWNoKGZwID0+IHtcbiAgICBzZHAgKz0gJ2E9ZmluZ2VycHJpbnQ6JyArIGZwLmFsZ29yaXRobSArICcgJyArIGZwLnZhbHVlICsgJ1xcclxcbic7XG4gIH0pO1xuICByZXR1cm4gc2RwO1xufTtcblxuLy8gUGFyc2VzIGE9Y3J5cHRvIGxpbmVzIGludG9cbi8vICAgaHR0cHM6Ly9yYXdnaXQuY29tL2Fib2JhL2VkZ2VydGMvbWFzdGVyL21zb3J0Yy1yczQuaHRtbCNkaWN0aW9uYXJ5LXJ0Y3NydHBzZGVzcGFyYW1ldGVycy1tZW1iZXJzXG5TRFBVdGlscy5wYXJzZUNyeXB0b0xpbmUgPSBmdW5jdGlvbihsaW5lKSB7XG4gIGNvbnN0IHBhcnRzID0gbGluZS5zdWJzdHIoOSkuc3BsaXQoJyAnKTtcbiAgcmV0dXJuIHtcbiAgICB0YWc6IHBhcnNlSW50KHBhcnRzWzBdLCAxMCksXG4gICAgY3J5cHRvU3VpdGU6IHBhcnRzWzFdLFxuICAgIGtleVBhcmFtczogcGFydHNbMl0sXG4gICAgc2Vzc2lvblBhcmFtczogcGFydHMuc2xpY2UoMyksXG4gIH07XG59O1xuXG5TRFBVdGlscy53cml0ZUNyeXB0b0xpbmUgPSBmdW5jdGlvbihwYXJhbWV0ZXJzKSB7XG4gIHJldHVybiAnYT1jcnlwdG86JyArIHBhcmFtZXRlcnMudGFnICsgJyAnICtcbiAgICBwYXJhbWV0ZXJzLmNyeXB0b1N1aXRlICsgJyAnICtcbiAgICAodHlwZW9mIHBhcmFtZXRlcnMua2V5UGFyYW1zID09PSAnb2JqZWN0J1xuICAgICAgPyBTRFBVdGlscy53cml0ZUNyeXB0b0tleVBhcmFtcyhwYXJhbWV0ZXJzLmtleVBhcmFtcylcbiAgICAgIDogcGFyYW1ldGVycy5rZXlQYXJhbXMpICtcbiAgICAocGFyYW1ldGVycy5zZXNzaW9uUGFyYW1zID8gJyAnICsgcGFyYW1ldGVycy5zZXNzaW9uUGFyYW1zLmpvaW4oJyAnKSA6ICcnKSArXG4gICAgJ1xcclxcbic7XG59O1xuXG4vLyBQYXJzZXMgdGhlIGNyeXB0byBrZXkgcGFyYW1ldGVycyBpbnRvXG4vLyAgIGh0dHBzOi8vcmF3Z2l0LmNvbS9hYm9iYS9lZGdlcnRjL21hc3Rlci9tc29ydGMtcnM0Lmh0bWwjcnRjc3J0cGtleXBhcmFtKlxuU0RQVXRpbHMucGFyc2VDcnlwdG9LZXlQYXJhbXMgPSBmdW5jdGlvbihrZXlQYXJhbXMpIHtcbiAgaWYgKGtleVBhcmFtcy5pbmRleE9mKCdpbmxpbmU6JykgIT09IDApIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBjb25zdCBwYXJ0cyA9IGtleVBhcmFtcy5zdWJzdHIoNykuc3BsaXQoJ3wnKTtcbiAgcmV0dXJuIHtcbiAgICBrZXlNZXRob2Q6ICdpbmxpbmUnLFxuICAgIGtleVNhbHQ6IHBhcnRzWzBdLFxuICAgIGxpZmVUaW1lOiBwYXJ0c1sxXSxcbiAgICBta2lWYWx1ZTogcGFydHNbMl0gPyBwYXJ0c1syXS5zcGxpdCgnOicpWzBdIDogdW5kZWZpbmVkLFxuICAgIG1raUxlbmd0aDogcGFydHNbMl0gPyBwYXJ0c1syXS5zcGxpdCgnOicpWzFdIDogdW5kZWZpbmVkLFxuICB9O1xufTtcblxuU0RQVXRpbHMud3JpdGVDcnlwdG9LZXlQYXJhbXMgPSBmdW5jdGlvbihrZXlQYXJhbXMpIHtcbiAgcmV0dXJuIGtleVBhcmFtcy5rZXlNZXRob2QgKyAnOidcbiAgICArIGtleVBhcmFtcy5rZXlTYWx0ICtcbiAgICAoa2V5UGFyYW1zLmxpZmVUaW1lID8gJ3wnICsga2V5UGFyYW1zLmxpZmVUaW1lIDogJycpICtcbiAgICAoa2V5UGFyYW1zLm1raVZhbHVlICYmIGtleVBhcmFtcy5ta2lMZW5ndGhcbiAgICAgID8gJ3wnICsga2V5UGFyYW1zLm1raVZhbHVlICsgJzonICsga2V5UGFyYW1zLm1raUxlbmd0aFxuICAgICAgOiAnJyk7XG59O1xuXG4vLyBFeHRyYWN0cyBhbGwgU0RFUyBwYXJhbWV0ZXJzLlxuU0RQVXRpbHMuZ2V0Q3J5cHRvUGFyYW1ldGVycyA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2Vzc2lvbnBhcnQpIHtcbiAgY29uc3QgbGluZXMgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24gKyBzZXNzaW9ucGFydCxcbiAgICAnYT1jcnlwdG86Jyk7XG4gIHJldHVybiBsaW5lcy5tYXAoU0RQVXRpbHMucGFyc2VDcnlwdG9MaW5lKTtcbn07XG5cbi8vIFBhcnNlcyBJQ0UgaW5mb3JtYXRpb24gZnJvbSBTRFAgbWVkaWEgc2VjdGlvbiBvciBzZXNzaW9ucGFydC5cbi8vIEZJWE1FOiBmb3IgY29uc2lzdGVuY3kgd2l0aCBvdGhlciBmdW5jdGlvbnMgdGhpcyBzaG91bGQgb25seVxuLy8gICBnZXQgdGhlIGljZS11ZnJhZyBhbmQgaWNlLXB3ZCBsaW5lcyBhcyBpbnB1dC5cblNEUFV0aWxzLmdldEljZVBhcmFtZXRlcnMgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24sIHNlc3Npb25wYXJ0KSB7XG4gIGNvbnN0IHVmcmFnID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uICsgc2Vzc2lvbnBhcnQsXG4gICAgJ2E9aWNlLXVmcmFnOicpWzBdO1xuICBjb25zdCBwd2QgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24gKyBzZXNzaW9ucGFydCxcbiAgICAnYT1pY2UtcHdkOicpWzBdO1xuICBpZiAoISh1ZnJhZyAmJiBwd2QpKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgcmV0dXJuIHtcbiAgICB1c2VybmFtZUZyYWdtZW50OiB1ZnJhZy5zdWJzdHIoMTIpLFxuICAgIHBhc3N3b3JkOiBwd2Quc3Vic3RyKDEwKSxcbiAgfTtcbn07XG5cbi8vIFNlcmlhbGl6ZXMgSUNFIHBhcmFtZXRlcnMgdG8gU0RQLlxuU0RQVXRpbHMud3JpdGVJY2VQYXJhbWV0ZXJzID0gZnVuY3Rpb24ocGFyYW1zKSB7XG4gIGxldCBzZHAgPSAnYT1pY2UtdWZyYWc6JyArIHBhcmFtcy51c2VybmFtZUZyYWdtZW50ICsgJ1xcclxcbicgK1xuICAgICAgJ2E9aWNlLXB3ZDonICsgcGFyYW1zLnBhc3N3b3JkICsgJ1xcclxcbic7XG4gIGlmIChwYXJhbXMuaWNlTGl0ZSkge1xuICAgIHNkcCArPSAnYT1pY2UtbGl0ZVxcclxcbic7XG4gIH1cbiAgcmV0dXJuIHNkcDtcbn07XG5cbi8vIFBhcnNlcyB0aGUgU0RQIG1lZGlhIHNlY3Rpb24gYW5kIHJldHVybnMgUlRDUnRwUGFyYW1ldGVycy5cblNEUFV0aWxzLnBhcnNlUnRwUGFyYW1ldGVycyA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbikge1xuICBjb25zdCBkZXNjcmlwdGlvbiA9IHtcbiAgICBjb2RlY3M6IFtdLFxuICAgIGhlYWRlckV4dGVuc2lvbnM6IFtdLFxuICAgIGZlY01lY2hhbmlzbXM6IFtdLFxuICAgIHJ0Y3A6IFtdLFxuICB9O1xuICBjb25zdCBsaW5lcyA9IFNEUFV0aWxzLnNwbGl0TGluZXMobWVkaWFTZWN0aW9uKTtcbiAgY29uc3QgbWxpbmUgPSBsaW5lc1swXS5zcGxpdCgnICcpO1xuICBmb3IgKGxldCBpID0gMzsgaSA8IG1saW5lLmxlbmd0aDsgaSsrKSB7IC8vIGZpbmQgYWxsIGNvZGVjcyBmcm9tIG1saW5lWzMuLl1cbiAgICBjb25zdCBwdCA9IG1saW5lW2ldO1xuICAgIGNvbnN0IHJ0cG1hcGxpbmUgPSBTRFBVdGlscy5tYXRjaFByZWZpeChcbiAgICAgIG1lZGlhU2VjdGlvbiwgJ2E9cnRwbWFwOicgKyBwdCArICcgJylbMF07XG4gICAgaWYgKHJ0cG1hcGxpbmUpIHtcbiAgICAgIGNvbnN0IGNvZGVjID0gU0RQVXRpbHMucGFyc2VSdHBNYXAocnRwbWFwbGluZSk7XG4gICAgICBjb25zdCBmbXRwcyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KFxuICAgICAgICBtZWRpYVNlY3Rpb24sICdhPWZtdHA6JyArIHB0ICsgJyAnKTtcbiAgICAgIC8vIE9ubHkgdGhlIGZpcnN0IGE9Zm10cDo8cHQ+IGlzIGNvbnNpZGVyZWQuXG4gICAgICBjb2RlYy5wYXJhbWV0ZXJzID0gZm10cHMubGVuZ3RoID8gU0RQVXRpbHMucGFyc2VGbXRwKGZtdHBzWzBdKSA6IHt9O1xuICAgICAgY29kZWMucnRjcEZlZWRiYWNrID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgoXG4gICAgICAgIG1lZGlhU2VjdGlvbiwgJ2E9cnRjcC1mYjonICsgcHQgKyAnICcpXG4gICAgICAgIC5tYXAoU0RQVXRpbHMucGFyc2VSdGNwRmIpO1xuICAgICAgZGVzY3JpcHRpb24uY29kZWNzLnB1c2goY29kZWMpO1xuICAgICAgLy8gcGFyc2UgRkVDIG1lY2hhbmlzbXMgZnJvbSBydHBtYXAgbGluZXMuXG4gICAgICBzd2l0Y2ggKGNvZGVjLm5hbWUudG9VcHBlckNhc2UoKSkge1xuICAgICAgICBjYXNlICdSRUQnOlxuICAgICAgICBjYXNlICdVTFBGRUMnOlxuICAgICAgICAgIGRlc2NyaXB0aW9uLmZlY01lY2hhbmlzbXMucHVzaChjb2RlYy5uYW1lLnRvVXBwZXJDYXNlKCkpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OiAvLyBvbmx5IFJFRCBhbmQgVUxQRkVDIGFyZSByZWNvZ25pemVkIGFzIEZFQyBtZWNoYW5pc21zLlxuICAgICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPWV4dG1hcDonKS5mb3JFYWNoKGxpbmUgPT4ge1xuICAgIGRlc2NyaXB0aW9uLmhlYWRlckV4dGVuc2lvbnMucHVzaChTRFBVdGlscy5wYXJzZUV4dG1hcChsaW5lKSk7XG4gIH0pO1xuICAvLyBGSVhNRTogcGFyc2UgcnRjcC5cbiAgcmV0dXJuIGRlc2NyaXB0aW9uO1xufTtcblxuLy8gR2VuZXJhdGVzIHBhcnRzIG9mIHRoZSBTRFAgbWVkaWEgc2VjdGlvbiBkZXNjcmliaW5nIHRoZSBjYXBhYmlsaXRpZXMgL1xuLy8gcGFyYW1ldGVycy5cblNEUFV0aWxzLndyaXRlUnRwRGVzY3JpcHRpb24gPSBmdW5jdGlvbihraW5kLCBjYXBzKSB7XG4gIGxldCBzZHAgPSAnJztcblxuICAvLyBCdWlsZCB0aGUgbWxpbmUuXG4gIHNkcCArPSAnbT0nICsga2luZCArICcgJztcbiAgc2RwICs9IGNhcHMuY29kZWNzLmxlbmd0aCA+IDAgPyAnOScgOiAnMCc7IC8vIHJlamVjdCBpZiBubyBjb2RlY3MuXG4gIHNkcCArPSAnIFVEUC9UTFMvUlRQL1NBVlBGICc7XG4gIHNkcCArPSBjYXBzLmNvZGVjcy5tYXAoY29kZWMgPT4ge1xuICAgIGlmIChjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gY29kZWMucHJlZmVycmVkUGF5bG9hZFR5cGU7XG4gICAgfVxuICAgIHJldHVybiBjb2RlYy5wYXlsb2FkVHlwZTtcbiAgfSkuam9pbignICcpICsgJ1xcclxcbic7XG5cbiAgc2RwICs9ICdjPUlOIElQNCAwLjAuMC4wXFxyXFxuJztcbiAgc2RwICs9ICdhPXJ0Y3A6OSBJTiBJUDQgMC4wLjAuMFxcclxcbic7XG5cbiAgLy8gQWRkIGE9cnRwbWFwIGxpbmVzIGZvciBlYWNoIGNvZGVjLiBBbHNvIGZtdHAgYW5kIHJ0Y3AtZmIuXG4gIGNhcHMuY29kZWNzLmZvckVhY2goY29kZWMgPT4ge1xuICAgIHNkcCArPSBTRFBVdGlscy53cml0ZVJ0cE1hcChjb2RlYyk7XG4gICAgc2RwICs9IFNEUFV0aWxzLndyaXRlRm10cChjb2RlYyk7XG4gICAgc2RwICs9IFNEUFV0aWxzLndyaXRlUnRjcEZiKGNvZGVjKTtcbiAgfSk7XG4gIGxldCBtYXhwdGltZSA9IDA7XG4gIGNhcHMuY29kZWNzLmZvckVhY2goY29kZWMgPT4ge1xuICAgIGlmIChjb2RlYy5tYXhwdGltZSA+IG1heHB0aW1lKSB7XG4gICAgICBtYXhwdGltZSA9IGNvZGVjLm1heHB0aW1lO1xuICAgIH1cbiAgfSk7XG4gIGlmIChtYXhwdGltZSA+IDApIHtcbiAgICBzZHAgKz0gJ2E9bWF4cHRpbWU6JyArIG1heHB0aW1lICsgJ1xcclxcbic7XG4gIH1cblxuICBpZiAoY2Fwcy5oZWFkZXJFeHRlbnNpb25zKSB7XG4gICAgY2Fwcy5oZWFkZXJFeHRlbnNpb25zLmZvckVhY2goZXh0ZW5zaW9uID0+IHtcbiAgICAgIHNkcCArPSBTRFBVdGlscy53cml0ZUV4dG1hcChleHRlbnNpb24pO1xuICAgIH0pO1xuICB9XG4gIC8vIEZJWE1FOiB3cml0ZSBmZWNNZWNoYW5pc21zLlxuICByZXR1cm4gc2RwO1xufTtcblxuLy8gUGFyc2VzIHRoZSBTRFAgbWVkaWEgc2VjdGlvbiBhbmQgcmV0dXJucyBhbiBhcnJheSBvZlxuLy8gUlRDUnRwRW5jb2RpbmdQYXJhbWV0ZXJzLlxuU0RQVXRpbHMucGFyc2VSdHBFbmNvZGluZ1BhcmFtZXRlcnMgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgY29uc3QgZW5jb2RpbmdQYXJhbWV0ZXJzID0gW107XG4gIGNvbnN0IGRlc2NyaXB0aW9uID0gU0RQVXRpbHMucGFyc2VSdHBQYXJhbWV0ZXJzKG1lZGlhU2VjdGlvbik7XG4gIGNvbnN0IGhhc1JlZCA9IGRlc2NyaXB0aW9uLmZlY01lY2hhbmlzbXMuaW5kZXhPZignUkVEJykgIT09IC0xO1xuICBjb25zdCBoYXNVbHBmZWMgPSBkZXNjcmlwdGlvbi5mZWNNZWNoYW5pc21zLmluZGV4T2YoJ1VMUEZFQycpICE9PSAtMTtcblxuICAvLyBmaWx0ZXIgYT1zc3JjOi4uLiBjbmFtZTosIGlnbm9yZSBQbGFuQi1tc2lkXG4gIGNvbnN0IHNzcmNzID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1zc3JjOicpXG4gICAgLm1hcChsaW5lID0+IFNEUFV0aWxzLnBhcnNlU3NyY01lZGlhKGxpbmUpKVxuICAgIC5maWx0ZXIocGFydHMgPT4gcGFydHMuYXR0cmlidXRlID09PSAnY25hbWUnKTtcbiAgY29uc3QgcHJpbWFyeVNzcmMgPSBzc3Jjcy5sZW5ndGggPiAwICYmIHNzcmNzWzBdLnNzcmM7XG4gIGxldCBzZWNvbmRhcnlTc3JjO1xuXG4gIGNvbnN0IGZsb3dzID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1zc3JjLWdyb3VwOkZJRCcpXG4gICAgLm1hcChsaW5lID0+IHtcbiAgICAgIGNvbnN0IHBhcnRzID0gbGluZS5zdWJzdHIoMTcpLnNwbGl0KCcgJyk7XG4gICAgICByZXR1cm4gcGFydHMubWFwKHBhcnQgPT4gcGFyc2VJbnQocGFydCwgMTApKTtcbiAgICB9KTtcbiAgaWYgKGZsb3dzLmxlbmd0aCA+IDAgJiYgZmxvd3NbMF0ubGVuZ3RoID4gMSAmJiBmbG93c1swXVswXSA9PT0gcHJpbWFyeVNzcmMpIHtcbiAgICBzZWNvbmRhcnlTc3JjID0gZmxvd3NbMF1bMV07XG4gIH1cblxuICBkZXNjcmlwdGlvbi5jb2RlY3MuZm9yRWFjaChjb2RlYyA9PiB7XG4gICAgaWYgKGNvZGVjLm5hbWUudG9VcHBlckNhc2UoKSA9PT0gJ1JUWCcgJiYgY29kZWMucGFyYW1ldGVycy5hcHQpIHtcbiAgICAgIGxldCBlbmNQYXJhbSA9IHtcbiAgICAgICAgc3NyYzogcHJpbWFyeVNzcmMsXG4gICAgICAgIGNvZGVjUGF5bG9hZFR5cGU6IHBhcnNlSW50KGNvZGVjLnBhcmFtZXRlcnMuYXB0LCAxMCksXG4gICAgICB9O1xuICAgICAgaWYgKHByaW1hcnlTc3JjICYmIHNlY29uZGFyeVNzcmMpIHtcbiAgICAgICAgZW5jUGFyYW0ucnR4ID0ge3NzcmM6IHNlY29uZGFyeVNzcmN9O1xuICAgICAgfVxuICAgICAgZW5jb2RpbmdQYXJhbWV0ZXJzLnB1c2goZW5jUGFyYW0pO1xuICAgICAgaWYgKGhhc1JlZCkge1xuICAgICAgICBlbmNQYXJhbSA9IEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkoZW5jUGFyYW0pKTtcbiAgICAgICAgZW5jUGFyYW0uZmVjID0ge1xuICAgICAgICAgIHNzcmM6IHByaW1hcnlTc3JjLFxuICAgICAgICAgIG1lY2hhbmlzbTogaGFzVWxwZmVjID8gJ3JlZCt1bHBmZWMnIDogJ3JlZCcsXG4gICAgICAgIH07XG4gICAgICAgIGVuY29kaW5nUGFyYW1ldGVycy5wdXNoKGVuY1BhcmFtKTtcbiAgICAgIH1cbiAgICB9XG4gIH0pO1xuICBpZiAoZW5jb2RpbmdQYXJhbWV0ZXJzLmxlbmd0aCA9PT0gMCAmJiBwcmltYXJ5U3NyYykge1xuICAgIGVuY29kaW5nUGFyYW1ldGVycy5wdXNoKHtcbiAgICAgIHNzcmM6IHByaW1hcnlTc3JjLFxuICAgIH0pO1xuICB9XG5cbiAgLy8gd2Ugc3VwcG9ydCBib3RoIGI9QVMgYW5kIGI9VElBUyBidXQgaW50ZXJwcmV0IEFTIGFzIFRJQVMuXG4gIGxldCBiYW5kd2lkdGggPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdiPScpO1xuICBpZiAoYmFuZHdpZHRoLmxlbmd0aCkge1xuICAgIGlmIChiYW5kd2lkdGhbMF0uaW5kZXhPZignYj1USUFTOicpID09PSAwKSB7XG4gICAgICBiYW5kd2lkdGggPSBwYXJzZUludChiYW5kd2lkdGhbMF0uc3Vic3RyKDcpLCAxMCk7XG4gICAgfSBlbHNlIGlmIChiYW5kd2lkdGhbMF0uaW5kZXhPZignYj1BUzonKSA9PT0gMCkge1xuICAgICAgLy8gdXNlIGZvcm11bGEgZnJvbSBKU0VQIHRvIGNvbnZlcnQgYj1BUyB0byBUSUFTIHZhbHVlLlxuICAgICAgYmFuZHdpZHRoID0gcGFyc2VJbnQoYmFuZHdpZHRoWzBdLnN1YnN0cig1KSwgMTApICogMTAwMCAqIDAuOTVcbiAgICAgICAgICAtICg1MCAqIDQwICogOCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGJhbmR3aWR0aCA9IHVuZGVmaW5lZDtcbiAgICB9XG4gICAgZW5jb2RpbmdQYXJhbWV0ZXJzLmZvckVhY2gocGFyYW1zID0+IHtcbiAgICAgIHBhcmFtcy5tYXhCaXRyYXRlID0gYmFuZHdpZHRoO1xuICAgIH0pO1xuICB9XG4gIHJldHVybiBlbmNvZGluZ1BhcmFtZXRlcnM7XG59O1xuXG4vLyBwYXJzZXMgaHR0cDovL2RyYWZ0Lm9ydGMub3JnLyNydGNydGNwcGFyYW1ldGVycypcblNEUFV0aWxzLnBhcnNlUnRjcFBhcmFtZXRlcnMgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgY29uc3QgcnRjcFBhcmFtZXRlcnMgPSB7fTtcblxuICAvLyBHZXRzIHRoZSBmaXJzdCBTU1JDLiBOb3RlIHRoYXQgd2l0aCBSVFggdGhlcmUgbWlnaHQgYmUgbXVsdGlwbGVcbiAgLy8gU1NSQ3MuXG4gIGNvbnN0IHJlbW90ZVNzcmMgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPXNzcmM6JylcbiAgICAubWFwKGxpbmUgPT4gU0RQVXRpbHMucGFyc2VTc3JjTWVkaWEobGluZSkpXG4gICAgLmZpbHRlcihvYmogPT4gb2JqLmF0dHJpYnV0ZSA9PT0gJ2NuYW1lJylbMF07XG4gIGlmIChyZW1vdGVTc3JjKSB7XG4gICAgcnRjcFBhcmFtZXRlcnMuY25hbWUgPSByZW1vdGVTc3JjLnZhbHVlO1xuICAgIHJ0Y3BQYXJhbWV0ZXJzLnNzcmMgPSByZW1vdGVTc3JjLnNzcmM7XG4gIH1cblxuICAvLyBFZGdlIHVzZXMgdGhlIGNvbXBvdW5kIGF0dHJpYnV0ZSBpbnN0ZWFkIG9mIHJlZHVjZWRTaXplXG4gIC8vIGNvbXBvdW5kIGlzICFyZWR1Y2VkU2l6ZVxuICBjb25zdCByc2l6ZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9cnRjcC1yc2l6ZScpO1xuICBydGNwUGFyYW1ldGVycy5yZWR1Y2VkU2l6ZSA9IHJzaXplLmxlbmd0aCA+IDA7XG4gIHJ0Y3BQYXJhbWV0ZXJzLmNvbXBvdW5kID0gcnNpemUubGVuZ3RoID09PSAwO1xuXG4gIC8vIHBhcnNlcyB0aGUgcnRjcC1tdXggYXR0ctGWYnV0ZS5cbiAgLy8gTm90ZSB0aGF0IEVkZ2UgZG9lcyBub3Qgc3VwcG9ydCB1bm11eGVkIFJUQ1AuXG4gIGNvbnN0IG11eCA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9cnRjcC1tdXgnKTtcbiAgcnRjcFBhcmFtZXRlcnMubXV4ID0gbXV4Lmxlbmd0aCA+IDA7XG5cbiAgcmV0dXJuIHJ0Y3BQYXJhbWV0ZXJzO1xufTtcblxuU0RQVXRpbHMud3JpdGVSdGNwUGFyYW1ldGVycyA9IGZ1bmN0aW9uKHJ0Y3BQYXJhbWV0ZXJzKSB7XG4gIGxldCBzZHAgPSAnJztcbiAgaWYgKHJ0Y3BQYXJhbWV0ZXJzLnJlZHVjZWRTaXplKSB7XG4gICAgc2RwICs9ICdhPXJ0Y3AtcnNpemVcXHJcXG4nO1xuICB9XG4gIGlmIChydGNwUGFyYW1ldGVycy5tdXgpIHtcbiAgICBzZHAgKz0gJ2E9cnRjcC1tdXhcXHJcXG4nO1xuICB9XG4gIGlmIChydGNwUGFyYW1ldGVycy5zc3JjICE9PSB1bmRlZmluZWQgJiYgcnRjcFBhcmFtZXRlcnMuY25hbWUpIHtcbiAgICBzZHAgKz0gJ2E9c3NyYzonICsgcnRjcFBhcmFtZXRlcnMuc3NyYyArXG4gICAgICAnIGNuYW1lOicgKyBydGNwUGFyYW1ldGVycy5jbmFtZSArICdcXHJcXG4nO1xuICB9XG4gIHJldHVybiBzZHA7XG59O1xuXG5cbi8vIHBhcnNlcyBlaXRoZXIgYT1tc2lkOiBvciBhPXNzcmM6Li4uIG1zaWQgbGluZXMgYW5kIHJldHVybnNcbi8vIHRoZSBpZCBvZiB0aGUgTWVkaWFTdHJlYW0gYW5kIE1lZGlhU3RyZWFtVHJhY2suXG5TRFBVdGlscy5wYXJzZU1zaWQgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgbGV0IHBhcnRzO1xuICBjb25zdCBzcGVjID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1tc2lkOicpO1xuICBpZiAoc3BlYy5sZW5ndGggPT09IDEpIHtcbiAgICBwYXJ0cyA9IHNwZWNbMF0uc3Vic3RyKDcpLnNwbGl0KCcgJyk7XG4gICAgcmV0dXJuIHtzdHJlYW06IHBhcnRzWzBdLCB0cmFjazogcGFydHNbMV19O1xuICB9XG4gIGNvbnN0IHBsYW5CID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1zc3JjOicpXG4gICAgLm1hcChsaW5lID0+IFNEUFV0aWxzLnBhcnNlU3NyY01lZGlhKGxpbmUpKVxuICAgIC5maWx0ZXIobXNpZFBhcnRzID0+IG1zaWRQYXJ0cy5hdHRyaWJ1dGUgPT09ICdtc2lkJyk7XG4gIGlmIChwbGFuQi5sZW5ndGggPiAwKSB7XG4gICAgcGFydHMgPSBwbGFuQlswXS52YWx1ZS5zcGxpdCgnICcpO1xuICAgIHJldHVybiB7c3RyZWFtOiBwYXJ0c1swXSwgdHJhY2s6IHBhcnRzWzFdfTtcbiAgfVxufTtcblxuLy8gU0NUUFxuLy8gcGFyc2VzIGRyYWZ0LWlldGYtbW11c2ljLXNjdHAtc2RwLTI2IGZpcnN0IGFuZCBmYWxscyBiYWNrXG4vLyB0byBkcmFmdC1pZXRmLW1tdXNpYy1zY3RwLXNkcC0wNVxuU0RQVXRpbHMucGFyc2VTY3RwRGVzY3JpcHRpb24gPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgY29uc3QgbWxpbmUgPSBTRFBVdGlscy5wYXJzZU1MaW5lKG1lZGlhU2VjdGlvbik7XG4gIGNvbnN0IG1heFNpemVMaW5lID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1tYXgtbWVzc2FnZS1zaXplOicpO1xuICBsZXQgbWF4TWVzc2FnZVNpemU7XG4gIGlmIChtYXhTaXplTGluZS5sZW5ndGggPiAwKSB7XG4gICAgbWF4TWVzc2FnZVNpemUgPSBwYXJzZUludChtYXhTaXplTGluZVswXS5zdWJzdHIoMTkpLCAxMCk7XG4gIH1cbiAgaWYgKGlzTmFOKG1heE1lc3NhZ2VTaXplKSkge1xuICAgIG1heE1lc3NhZ2VTaXplID0gNjU1MzY7XG4gIH1cbiAgY29uc3Qgc2N0cFBvcnQgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPXNjdHAtcG9ydDonKTtcbiAgaWYgKHNjdHBQb3J0Lmxlbmd0aCA+IDApIHtcbiAgICByZXR1cm4ge1xuICAgICAgcG9ydDogcGFyc2VJbnQoc2N0cFBvcnRbMF0uc3Vic3RyKDEyKSwgMTApLFxuICAgICAgcHJvdG9jb2w6IG1saW5lLmZtdCxcbiAgICAgIG1heE1lc3NhZ2VTaXplLFxuICAgIH07XG4gIH1cbiAgY29uc3Qgc2N0cE1hcExpbmVzID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1zY3RwbWFwOicpO1xuICBpZiAoc2N0cE1hcExpbmVzLmxlbmd0aCA+IDApIHtcbiAgICBjb25zdCBwYXJ0cyA9IHNjdHBNYXBMaW5lc1swXVxuICAgICAgLnN1YnN0cigxMClcbiAgICAgIC5zcGxpdCgnICcpO1xuICAgIHJldHVybiB7XG4gICAgICBwb3J0OiBwYXJzZUludChwYXJ0c1swXSwgMTApLFxuICAgICAgcHJvdG9jb2w6IHBhcnRzWzFdLFxuICAgICAgbWF4TWVzc2FnZVNpemUsXG4gICAgfTtcbiAgfVxufTtcblxuLy8gU0NUUFxuLy8gb3V0cHV0cyB0aGUgZHJhZnQtaWV0Zi1tbXVzaWMtc2N0cC1zZHAtMjYgdmVyc2lvbiB0aGF0IGFsbCBicm93c2Vyc1xuLy8gc3VwcG9ydCBieSBub3cgcmVjZWl2aW5nIGluIHRoaXMgZm9ybWF0LCB1bmxlc3Mgd2Ugb3JpZ2luYWxseSBwYXJzZWRcbi8vIGFzIHRoZSBkcmFmdC1pZXRmLW1tdXNpYy1zY3RwLXNkcC0wNSBmb3JtYXQgKGluZGljYXRlZCBieSB0aGUgbS1saW5lXG4vLyBwcm90b2NvbCBvZiBEVExTL1NDVFAgLS0gd2l0aG91dCBVRFAvIG9yIFRDUC8pXG5TRFBVdGlscy53cml0ZVNjdHBEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uKG1lZGlhLCBzY3RwKSB7XG4gIGxldCBvdXRwdXQgPSBbXTtcbiAgaWYgKG1lZGlhLnByb3RvY29sICE9PSAnRFRMUy9TQ1RQJykge1xuICAgIG91dHB1dCA9IFtcbiAgICAgICdtPScgKyBtZWRpYS5raW5kICsgJyA5ICcgKyBtZWRpYS5wcm90b2NvbCArICcgJyArIHNjdHAucHJvdG9jb2wgKyAnXFxyXFxuJyxcbiAgICAgICdjPUlOIElQNCAwLjAuMC4wXFxyXFxuJyxcbiAgICAgICdhPXNjdHAtcG9ydDonICsgc2N0cC5wb3J0ICsgJ1xcclxcbicsXG4gICAgXTtcbiAgfSBlbHNlIHtcbiAgICBvdXRwdXQgPSBbXG4gICAgICAnbT0nICsgbWVkaWEua2luZCArICcgOSAnICsgbWVkaWEucHJvdG9jb2wgKyAnICcgKyBzY3RwLnBvcnQgKyAnXFxyXFxuJyxcbiAgICAgICdjPUlOIElQNCAwLjAuMC4wXFxyXFxuJyxcbiAgICAgICdhPXNjdHBtYXA6JyArIHNjdHAucG9ydCArICcgJyArIHNjdHAucHJvdG9jb2wgKyAnIDY1NTM1XFxyXFxuJyxcbiAgICBdO1xuICB9XG4gIGlmIChzY3RwLm1heE1lc3NhZ2VTaXplICE9PSB1bmRlZmluZWQpIHtcbiAgICBvdXRwdXQucHVzaCgnYT1tYXgtbWVzc2FnZS1zaXplOicgKyBzY3RwLm1heE1lc3NhZ2VTaXplICsgJ1xcclxcbicpO1xuICB9XG4gIHJldHVybiBvdXRwdXQuam9pbignJyk7XG59O1xuXG4vLyBHZW5lcmF0ZSBhIHNlc3Npb24gSUQgZm9yIFNEUC5cbi8vIGh0dHBzOi8vdG9vbHMuaWV0Zi5vcmcvaHRtbC9kcmFmdC1pZXRmLXJ0Y3dlYi1qc2VwLTIwI3NlY3Rpb24tNS4yLjFcbi8vIHJlY29tbWVuZHMgdXNpbmcgYSBjcnlwdG9ncmFwaGljYWxseSByYW5kb20gK3ZlIDY0LWJpdCB2YWx1ZVxuLy8gYnV0IHJpZ2h0IG5vdyB0aGlzIHNob3VsZCBiZSBhY2NlcHRhYmxlIGFuZCB3aXRoaW4gdGhlIHJpZ2h0IHJhbmdlXG5TRFBVdGlscy5nZW5lcmF0ZVNlc3Npb25JZCA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gTWF0aC5yYW5kb20oKS50b1N0cmluZygpLnN1YnN0cigyLCAyMSk7XG59O1xuXG4vLyBXcml0ZSBib2lsZXIgcGxhdGUgZm9yIHN0YXJ0IG9mIFNEUFxuLy8gc2Vzc0lkIGFyZ3VtZW50IGlzIG9wdGlvbmFsIC0gaWYgbm90IHN1cHBsaWVkIGl0IHdpbGxcbi8vIGJlIGdlbmVyYXRlZCByYW5kb21seVxuLy8gc2Vzc1ZlcnNpb24gaXMgb3B0aW9uYWwgYW5kIGRlZmF1bHRzIHRvIDJcbi8vIHNlc3NVc2VyIGlzIG9wdGlvbmFsIGFuZCBkZWZhdWx0cyB0byAndGhpc2lzYWRhcHRlcm9ydGMnXG5TRFBVdGlscy53cml0ZVNlc3Npb25Cb2lsZXJwbGF0ZSA9IGZ1bmN0aW9uKHNlc3NJZCwgc2Vzc1Zlciwgc2Vzc1VzZXIpIHtcbiAgbGV0IHNlc3Npb25JZDtcbiAgY29uc3QgdmVyc2lvbiA9IHNlc3NWZXIgIT09IHVuZGVmaW5lZCA/IHNlc3NWZXIgOiAyO1xuICBpZiAoc2Vzc0lkKSB7XG4gICAgc2Vzc2lvbklkID0gc2Vzc0lkO1xuICB9IGVsc2Uge1xuICAgIHNlc3Npb25JZCA9IFNEUFV0aWxzLmdlbmVyYXRlU2Vzc2lvbklkKCk7XG4gIH1cbiAgY29uc3QgdXNlciA9IHNlc3NVc2VyIHx8ICd0aGlzaXNhZGFwdGVyb3J0Yyc7XG4gIC8vIEZJWE1FOiBzZXNzLWlkIHNob3VsZCBiZSBhbiBOVFAgdGltZXN0YW1wLlxuICByZXR1cm4gJ3Y9MFxcclxcbicgK1xuICAgICAgJ289JyArIHVzZXIgKyAnICcgKyBzZXNzaW9uSWQgKyAnICcgKyB2ZXJzaW9uICtcbiAgICAgICAgJyBJTiBJUDQgMTI3LjAuMC4xXFxyXFxuJyArXG4gICAgICAncz0tXFxyXFxuJyArXG4gICAgICAndD0wIDBcXHJcXG4nO1xufTtcblxuLy8gR2V0cyB0aGUgZGlyZWN0aW9uIGZyb20gdGhlIG1lZGlhU2VjdGlvbiBvciB0aGUgc2Vzc2lvbnBhcnQuXG5TRFBVdGlscy5nZXREaXJlY3Rpb24gPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24sIHNlc3Npb25wYXJ0KSB7XG4gIC8vIExvb2sgZm9yIHNlbmRyZWN2LCBzZW5kb25seSwgcmVjdm9ubHksIGluYWN0aXZlLCBkZWZhdWx0IHRvIHNlbmRyZWN2LlxuICBjb25zdCBsaW5lcyA9IFNEUFV0aWxzLnNwbGl0TGluZXMobWVkaWFTZWN0aW9uKTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBsaW5lcy5sZW5ndGg7IGkrKykge1xuICAgIHN3aXRjaCAobGluZXNbaV0pIHtcbiAgICAgIGNhc2UgJ2E9c2VuZHJlY3YnOlxuICAgICAgY2FzZSAnYT1zZW5kb25seSc6XG4gICAgICBjYXNlICdhPXJlY3Zvbmx5JzpcbiAgICAgIGNhc2UgJ2E9aW5hY3RpdmUnOlxuICAgICAgICByZXR1cm4gbGluZXNbaV0uc3Vic3RyKDIpO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgLy8gRklYTUU6IFdoYXQgc2hvdWxkIGhhcHBlbiBoZXJlP1xuICAgIH1cbiAgfVxuICBpZiAoc2Vzc2lvbnBhcnQpIHtcbiAgICByZXR1cm4gU0RQVXRpbHMuZ2V0RGlyZWN0aW9uKHNlc3Npb25wYXJ0KTtcbiAgfVxuICByZXR1cm4gJ3NlbmRyZWN2Jztcbn07XG5cblNEUFV0aWxzLmdldEtpbmQgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgY29uc3QgbGluZXMgPSBTRFBVdGlscy5zcGxpdExpbmVzKG1lZGlhU2VjdGlvbik7XG4gIGNvbnN0IG1saW5lID0gbGluZXNbMF0uc3BsaXQoJyAnKTtcbiAgcmV0dXJuIG1saW5lWzBdLnN1YnN0cigyKTtcbn07XG5cblNEUFV0aWxzLmlzUmVqZWN0ZWQgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgcmV0dXJuIG1lZGlhU2VjdGlvbi5zcGxpdCgnICcsIDIpWzFdID09PSAnMCc7XG59O1xuXG5TRFBVdGlscy5wYXJzZU1MaW5lID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uKSB7XG4gIGNvbnN0IGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhtZWRpYVNlY3Rpb24pO1xuICBjb25zdCBwYXJ0cyA9IGxpbmVzWzBdLnN1YnN0cigyKS5zcGxpdCgnICcpO1xuICByZXR1cm4ge1xuICAgIGtpbmQ6IHBhcnRzWzBdLFxuICAgIHBvcnQ6IHBhcnNlSW50KHBhcnRzWzFdLCAxMCksXG4gICAgcHJvdG9jb2w6IHBhcnRzWzJdLFxuICAgIGZtdDogcGFydHMuc2xpY2UoMykuam9pbignICcpLFxuICB9O1xufTtcblxuU0RQVXRpbHMucGFyc2VPTGluZSA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbikge1xuICBjb25zdCBsaW5lID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnbz0nKVswXTtcbiAgY29uc3QgcGFydHMgPSBsaW5lLnN1YnN0cigyKS5zcGxpdCgnICcpO1xuICByZXR1cm4ge1xuICAgIHVzZXJuYW1lOiBwYXJ0c1swXSxcbiAgICBzZXNzaW9uSWQ6IHBhcnRzWzFdLFxuICAgIHNlc3Npb25WZXJzaW9uOiBwYXJzZUludChwYXJ0c1syXSwgMTApLFxuICAgIG5ldFR5cGU6IHBhcnRzWzNdLFxuICAgIGFkZHJlc3NUeXBlOiBwYXJ0c1s0XSxcbiAgICBhZGRyZXNzOiBwYXJ0c1s1XSxcbiAgfTtcbn07XG5cbi8vIGEgdmVyeSBuYWl2ZSBpbnRlcnByZXRhdGlvbiBvZiBhIHZhbGlkIFNEUC5cblNEUFV0aWxzLmlzVmFsaWRTRFAgPSBmdW5jdGlvbihibG9iKSB7XG4gIGlmICh0eXBlb2YgYmxvYiAhPT0gJ3N0cmluZycgfHwgYmxvYi5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgY29uc3QgbGluZXMgPSBTRFBVdGlscy5zcGxpdExpbmVzKGJsb2IpO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGxpbmVzLmxlbmd0aDsgaSsrKSB7XG4gICAgaWYgKGxpbmVzW2ldLmxlbmd0aCA8IDIgfHwgbGluZXNbaV0uY2hhckF0KDEpICE9PSAnPScpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgLy8gVE9ETzogY2hlY2sgdGhlIG1vZGlmaWVyIGEgYml0IG1vcmUuXG4gIH1cbiAgcmV0dXJuIHRydWU7XG59O1xuXG4vLyBFeHBvc2UgcHVibGljIG1ldGhvZHMuXG5pZiAodHlwZW9mIG1vZHVsZSA9PT0gJ29iamVjdCcpIHtcbiAgbW9kdWxlLmV4cG9ydHMgPSBTRFBVdGlscztcbn1cbiIsInZhciBtaiA9IHJlcXVpcmUoXCJtaW5pamFudXNcIik7XG5tai5KYW51c1Nlc3Npb24ucHJvdG90eXBlLnNlbmRPcmlnaW5hbCA9IG1qLkphbnVzU2Vzc2lvbi5wcm90b3R5cGUuc2VuZDtcbm1qLkphbnVzU2Vzc2lvbi5wcm90b3R5cGUuc2VuZCA9IGZ1bmN0aW9uKHR5cGUsIHNpZ25hbCkge1xuICByZXR1cm4gdGhpcy5zZW5kT3JpZ2luYWwodHlwZSwgc2lnbmFsKS5jYXRjaCgoZSkgPT4ge1xuICAgIGlmIChlLm1lc3NhZ2UgJiYgZS5tZXNzYWdlLmluZGV4T2YoXCJ0aW1lZCBvdXRcIikgPiAtMSkge1xuICAgICAgY29uc29sZS5lcnJvcihcIndlYiBzb2NrZXQgdGltZWQgb3V0XCIpO1xuICAgICAgTkFGLmNvbm5lY3Rpb24uYWRhcHRlci5yZWNvbm5lY3QoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3coZSk7XG4gICAgfVxuICB9KTtcbn1cblxudmFyIHNkcFV0aWxzID0gcmVxdWlyZShcInNkcFwiKTtcbi8vdmFyIGRlYnVnID0gcmVxdWlyZShcImRlYnVnXCIpKFwibmFmLWphbnVzLWFkYXB0ZXI6ZGVidWdcIik7XG4vL3ZhciB3YXJuID0gcmVxdWlyZShcImRlYnVnXCIpKFwibmFmLWphbnVzLWFkYXB0ZXI6d2FyblwiKTtcbi8vdmFyIGVycm9yID0gcmVxdWlyZShcImRlYnVnXCIpKFwibmFmLWphbnVzLWFkYXB0ZXI6ZXJyb3JcIik7XG52YXIgZGVidWcgPSBjb25zb2xlLmxvZztcbnZhciB3YXJuID0gY29uc29sZS53YXJuO1xudmFyIGVycm9yID0gY29uc29sZS5lcnJvcjtcbnZhciBpc1NhZmFyaSA9IC9eKCg/IWNocm9tZXxhbmRyb2lkKS4pKnNhZmFyaS9pLnRlc3QobmF2aWdhdG9yLnVzZXJBZ2VudCk7XG5cbmNvbnN0IFNVQlNDUklCRV9USU1FT1VUX01TID0gMTUwMDA7XG5cbmZ1bmN0aW9uIGRlYm91bmNlKGZuKSB7XG4gIHZhciBjdXJyID0gUHJvbWlzZS5yZXNvbHZlKCk7XG4gIHJldHVybiBmdW5jdGlvbigpIHtcbiAgICB2YXIgYXJncyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cyk7XG4gICAgY3VyciA9IGN1cnIudGhlbihfID0+IGZuLmFwcGx5KHRoaXMsIGFyZ3MpKTtcbiAgfTtcbn1cblxuZnVuY3Rpb24gcmFuZG9tVWludCgpIHtcbiAgcmV0dXJuIE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIE51bWJlci5NQVhfU0FGRV9JTlRFR0VSKTtcbn1cblxuZnVuY3Rpb24gdW50aWxEYXRhQ2hhbm5lbE9wZW4oZGF0YUNoYW5uZWwpIHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICBpZiAoZGF0YUNoYW5uZWwucmVhZHlTdGF0ZSA9PT0gXCJvcGVuXCIpIHtcbiAgICAgIHJlc29sdmUoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgbGV0IHJlc29sdmVyLCByZWplY3RvcjtcblxuICAgICAgY29uc3QgY2xlYXIgPSAoKSA9PiB7XG4gICAgICAgIGRhdGFDaGFubmVsLnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJvcGVuXCIsIHJlc29sdmVyKTtcbiAgICAgICAgZGF0YUNoYW5uZWwucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImVycm9yXCIsIHJlamVjdG9yKTtcbiAgICAgIH07XG5cbiAgICAgIHJlc29sdmVyID0gKCkgPT4ge1xuICAgICAgICBjbGVhcigpO1xuICAgICAgICByZXNvbHZlKCk7XG4gICAgICB9O1xuICAgICAgcmVqZWN0b3IgPSAoKSA9PiB7XG4gICAgICAgIGNsZWFyKCk7XG4gICAgICAgIHJlamVjdCgpO1xuICAgICAgfTtcblxuICAgICAgZGF0YUNoYW5uZWwuYWRkRXZlbnRMaXN0ZW5lcihcIm9wZW5cIiwgcmVzb2x2ZXIpO1xuICAgICAgZGF0YUNoYW5uZWwuYWRkRXZlbnRMaXN0ZW5lcihcImVycm9yXCIsIHJlamVjdG9yKTtcbiAgICB9XG4gIH0pO1xufVxuXG5jb25zdCBpc0gyNjRWaWRlb1N1cHBvcnRlZCA9ICgoKSA9PiB7XG4gIGNvbnN0IHZpZGVvID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcInZpZGVvXCIpO1xuICByZXR1cm4gdmlkZW8uY2FuUGxheVR5cGUoJ3ZpZGVvL21wNDsgY29kZWNzPVwiYXZjMS40MkUwMUUsIG1wNGEuNDAuMlwiJykgIT09IFwiXCI7XG59KSgpO1xuXG5jb25zdCBPUFVTX1BBUkFNRVRFUlMgPSB7XG4gIC8vIGluZGljYXRlcyB0aGF0IHdlIHdhbnQgdG8gZW5hYmxlIERUWCB0byBlbGlkZSBzaWxlbmNlIHBhY2tldHNcbiAgdXNlZHR4OiAxLFxuICAvLyBpbmRpY2F0ZXMgdGhhdCB3ZSBwcmVmZXIgdG8gcmVjZWl2ZSBtb25vIGF1ZGlvIChpbXBvcnRhbnQgZm9yIHZvaXAgcHJvZmlsZSlcbiAgc3RlcmVvOiAwLFxuICAvLyBpbmRpY2F0ZXMgdGhhdCB3ZSBwcmVmZXIgdG8gc2VuZCBtb25vIGF1ZGlvIChpbXBvcnRhbnQgZm9yIHZvaXAgcHJvZmlsZSlcbiAgXCJzcHJvcC1zdGVyZW9cIjogMFxufTtcblxuY29uc3QgREVGQVVMVF9QRUVSX0NPTk5FQ1RJT05fQ09ORklHID0ge1xuICBpY2VTZXJ2ZXJzOiBbeyB1cmxzOiBcInN0dW46c3R1bjEubC5nb29nbGUuY29tOjE5MzAyXCIgfSwgeyB1cmxzOiBcInN0dW46c3R1bjIubC5nb29nbGUuY29tOjE5MzAyXCIgfV1cbn07XG5cbmNvbnN0IFdTX05PUk1BTF9DTE9TVVJFID0gMTAwMDtcblxuY2xhc3MgSmFudXNBZGFwdGVyIHtcbiAgY29uc3RydWN0b3IoKSB7XG4gICAgdGhpcy5yb29tID0gbnVsbDtcbiAgICAvLyBXZSBleHBlY3QgdGhlIGNvbnN1bWVyIHRvIHNldCBhIGNsaWVudCBpZCBiZWZvcmUgY29ubmVjdGluZy5cbiAgICB0aGlzLmNsaWVudElkID0gbnVsbDtcbiAgICB0aGlzLmpvaW5Ub2tlbiA9IG51bGw7XG5cbiAgICB0aGlzLnNlcnZlclVybCA9IG51bGw7XG4gICAgdGhpcy53ZWJSdGNPcHRpb25zID0ge307XG4gICAgdGhpcy5wZWVyQ29ubmVjdGlvbkNvbmZpZyA9IG51bGw7XG4gICAgdGhpcy53cyA9IG51bGw7XG4gICAgdGhpcy5zZXNzaW9uID0gbnVsbDtcbiAgICB0aGlzLnJlbGlhYmxlVHJhbnNwb3J0ID0gXCJkYXRhY2hhbm5lbFwiO1xuICAgIHRoaXMudW5yZWxpYWJsZVRyYW5zcG9ydCA9IFwiZGF0YWNoYW5uZWxcIjtcblxuICAgIC8vIEluIHRoZSBldmVudCB0aGUgc2VydmVyIHJlc3RhcnRzIGFuZCBhbGwgY2xpZW50cyBsb3NlIGNvbm5lY3Rpb24sIHJlY29ubmVjdCB3aXRoXG4gICAgLy8gc29tZSByYW5kb20gaml0dGVyIGFkZGVkIHRvIHByZXZlbnQgc2ltdWx0YW5lb3VzIHJlY29ubmVjdGlvbiByZXF1ZXN0cy5cbiAgICB0aGlzLmluaXRpYWxSZWNvbm5lY3Rpb25EZWxheSA9IDEwMDAgKiBNYXRoLnJhbmRvbSgpO1xuICAgIHRoaXMucmVjb25uZWN0aW9uRGVsYXkgPSB0aGlzLmluaXRpYWxSZWNvbm5lY3Rpb25EZWxheTtcbiAgICB0aGlzLnJlY29ubmVjdGlvblRpbWVvdXQgPSBudWxsO1xuICAgIHRoaXMubWF4UmVjb25uZWN0aW9uQXR0ZW1wdHMgPSAxMDtcbiAgICB0aGlzLnJlY29ubmVjdGlvbkF0dGVtcHRzID0gMDtcblxuICAgIHRoaXMucHVibGlzaGVyID0gbnVsbDtcbiAgICB0aGlzLm9jY3VwYW50cyA9IHt9O1xuICAgIHRoaXMubGVmdE9jY3VwYW50cyA9IG5ldyBTZXQoKTtcbiAgICB0aGlzLm1lZGlhU3RyZWFtcyA9IHt9O1xuICAgIHRoaXMubG9jYWxNZWRpYVN0cmVhbSA9IG51bGw7XG4gICAgdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cyA9IG5ldyBNYXAoKTtcblxuICAgIHRoaXMuYmxvY2tlZENsaWVudHMgPSBuZXcgTWFwKCk7XG4gICAgdGhpcy5mcm96ZW5VcGRhdGVzID0gbmV3IE1hcCgpO1xuXG4gICAgdGhpcy50aW1lT2Zmc2V0cyA9IFtdO1xuICAgIHRoaXMuc2VydmVyVGltZVJlcXVlc3RzID0gMDtcbiAgICB0aGlzLmF2Z1RpbWVPZmZzZXQgPSAwO1xuXG4gICAgdGhpcy5vbldlYnNvY2tldE9wZW4gPSB0aGlzLm9uV2Vic29ja2V0T3Blbi5iaW5kKHRoaXMpO1xuICAgIHRoaXMub25XZWJzb2NrZXRDbG9zZSA9IHRoaXMub25XZWJzb2NrZXRDbG9zZS5iaW5kKHRoaXMpO1xuICAgIHRoaXMub25XZWJzb2NrZXRNZXNzYWdlID0gdGhpcy5vbldlYnNvY2tldE1lc3NhZ2UuYmluZCh0aGlzKTtcbiAgICB0aGlzLm9uRGF0YUNoYW5uZWxNZXNzYWdlID0gdGhpcy5vbkRhdGFDaGFubmVsTWVzc2FnZS5iaW5kKHRoaXMpO1xuICAgIHRoaXMub25EYXRhID0gdGhpcy5vbkRhdGEuYmluZCh0aGlzKTtcbiAgfVxuXG4gIHNldFNlcnZlclVybCh1cmwpIHtcbiAgICB0aGlzLnNlcnZlclVybCA9IHVybDtcbiAgfVxuXG4gIHNldEFwcChhcHApIHt9XG5cbiAgc2V0Um9vbShyb29tTmFtZSkge1xuICAgIHRoaXMucm9vbSA9IHJvb21OYW1lO1xuICB9XG5cbiAgc2V0Sm9pblRva2VuKGpvaW5Ub2tlbikge1xuICAgIHRoaXMuam9pblRva2VuID0gam9pblRva2VuO1xuICB9XG5cbiAgc2V0Q2xpZW50SWQoY2xpZW50SWQpIHtcbiAgICB0aGlzLmNsaWVudElkID0gY2xpZW50SWQ7XG4gIH1cblxuICBzZXRXZWJSdGNPcHRpb25zKG9wdGlvbnMpIHtcbiAgICB0aGlzLndlYlJ0Y09wdGlvbnMgPSBvcHRpb25zO1xuICB9XG5cbiAgc2V0UGVlckNvbm5lY3Rpb25Db25maWcocGVlckNvbm5lY3Rpb25Db25maWcpIHtcbiAgICB0aGlzLnBlZXJDb25uZWN0aW9uQ29uZmlnID0gcGVlckNvbm5lY3Rpb25Db25maWc7XG4gIH1cblxuICBzZXRTZXJ2ZXJDb25uZWN0TGlzdGVuZXJzKHN1Y2Nlc3NMaXN0ZW5lciwgZmFpbHVyZUxpc3RlbmVyKSB7XG4gICAgdGhpcy5jb25uZWN0U3VjY2VzcyA9IHN1Y2Nlc3NMaXN0ZW5lcjtcbiAgICB0aGlzLmNvbm5lY3RGYWlsdXJlID0gZmFpbHVyZUxpc3RlbmVyO1xuICB9XG5cbiAgc2V0Um9vbU9jY3VwYW50TGlzdGVuZXIob2NjdXBhbnRMaXN0ZW5lcikge1xuICAgIHRoaXMub25PY2N1cGFudHNDaGFuZ2VkID0gb2NjdXBhbnRMaXN0ZW5lcjtcbiAgfVxuXG4gIHNldERhdGFDaGFubmVsTGlzdGVuZXJzKG9wZW5MaXN0ZW5lciwgY2xvc2VkTGlzdGVuZXIsIG1lc3NhZ2VMaXN0ZW5lcikge1xuICAgIHRoaXMub25PY2N1cGFudENvbm5lY3RlZCA9IG9wZW5MaXN0ZW5lcjtcbiAgICB0aGlzLm9uT2NjdXBhbnREaXNjb25uZWN0ZWQgPSBjbG9zZWRMaXN0ZW5lcjtcbiAgICB0aGlzLm9uT2NjdXBhbnRNZXNzYWdlID0gbWVzc2FnZUxpc3RlbmVyO1xuICB9XG5cbiAgc2V0UmVjb25uZWN0aW9uTGlzdGVuZXJzKHJlY29ubmVjdGluZ0xpc3RlbmVyLCByZWNvbm5lY3RlZExpc3RlbmVyLCByZWNvbm5lY3Rpb25FcnJvckxpc3RlbmVyKSB7XG4gICAgLy8gb25SZWNvbm5lY3RpbmcgaXMgY2FsbGVkIHdpdGggdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdW50aWwgdGhlIG5leHQgcmVjb25uZWN0aW9uIGF0dGVtcHRcbiAgICB0aGlzLm9uUmVjb25uZWN0aW5nID0gcmVjb25uZWN0aW5nTGlzdGVuZXI7XG4gICAgLy8gb25SZWNvbm5lY3RlZCBpcyBjYWxsZWQgd2hlbiB0aGUgY29ubmVjdGlvbiBoYXMgYmVlbiByZWVzdGFibGlzaGVkXG4gICAgdGhpcy5vblJlY29ubmVjdGVkID0gcmVjb25uZWN0ZWRMaXN0ZW5lcjtcbiAgICAvLyBvblJlY29ubmVjdGlvbkVycm9yIGlzIGNhbGxlZCB3aXRoIGFuIGVycm9yIHdoZW4gbWF4UmVjb25uZWN0aW9uQXR0ZW1wdHMgaGFzIGJlZW4gcmVhY2hlZFxuICAgIHRoaXMub25SZWNvbm5lY3Rpb25FcnJvciA9IHJlY29ubmVjdGlvbkVycm9yTGlzdGVuZXI7XG4gIH1cblxuICBjb25uZWN0KCkge1xuICAgIGRlYnVnKGBjb25uZWN0aW5nIHRvICR7dGhpcy5zZXJ2ZXJVcmx9YCk7XG5cbiAgICBjb25zdCB3ZWJzb2NrZXRDb25uZWN0aW9uID0gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgdGhpcy53cyA9IG5ldyBXZWJTb2NrZXQodGhpcy5zZXJ2ZXJVcmwsIFwiamFudXMtcHJvdG9jb2xcIik7XG5cbiAgICAgIHRoaXMuc2Vzc2lvbiA9IG5ldyBtai5KYW51c1Nlc3Npb24odGhpcy53cy5zZW5kLmJpbmQodGhpcy53cyksIHsgdGltZW91dE1zOiA0MDAwMCB9KTtcblxuICAgICAgdGhpcy53cy5hZGRFdmVudExpc3RlbmVyKFwiY2xvc2VcIiwgdGhpcy5vbldlYnNvY2tldENsb3NlKTtcbiAgICAgIHRoaXMud3MuYWRkRXZlbnRMaXN0ZW5lcihcIm1lc3NhZ2VcIiwgdGhpcy5vbldlYnNvY2tldE1lc3NhZ2UpO1xuXG4gICAgICB0aGlzLndzT25PcGVuID0gKCkgPT4ge1xuICAgICAgICB0aGlzLndzLnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJvcGVuXCIsIHRoaXMud3NPbk9wZW4pO1xuICAgICAgICB0aGlzLm9uV2Vic29ja2V0T3BlbigpXG4gICAgICAgICAgLnRoZW4ocmVzb2x2ZSlcbiAgICAgICAgICAuY2F0Y2gocmVqZWN0KTtcbiAgICAgIH07XG5cbiAgICAgIHRoaXMud3MuYWRkRXZlbnRMaXN0ZW5lcihcIm9wZW5cIiwgdGhpcy53c09uT3Blbik7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gUHJvbWlzZS5hbGwoW3dlYnNvY2tldENvbm5lY3Rpb24sIHRoaXMudXBkYXRlVGltZU9mZnNldCgpXSk7XG4gIH1cblxuICBkaXNjb25uZWN0KCkge1xuICAgIGRlYnVnKGBkaXNjb25uZWN0aW5nYCk7XG5cbiAgICBjbGVhclRpbWVvdXQodGhpcy5yZWNvbm5lY3Rpb25UaW1lb3V0KTtcblxuICAgIHRoaXMucmVtb3ZlQWxsT2NjdXBhbnRzKCk7XG4gICAgdGhpcy5sZWZ0T2NjdXBhbnRzID0gbmV3IFNldCgpO1xuXG4gICAgaWYgKHRoaXMucHVibGlzaGVyKSB7XG4gICAgICAvLyBDbG9zZSB0aGUgcHVibGlzaGVyIHBlZXIgY29ubmVjdGlvbi4gV2hpY2ggYWxzbyBkZXRhY2hlcyB0aGUgcGx1Z2luIGhhbmRsZS5cbiAgICAgIHRoaXMucHVibGlzaGVyLmNvbm4uY2xvc2UoKTtcbiAgICAgIHRoaXMucHVibGlzaGVyID0gbnVsbDtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5zZXNzaW9uKSB7XG4gICAgICB0aGlzLnNlc3Npb24uZGlzcG9zZSgpO1xuICAgICAgdGhpcy5zZXNzaW9uID0gbnVsbDtcbiAgICB9XG5cbiAgICBpZiAodGhpcy53cykge1xuICAgICAgdGhpcy53cy5yZW1vdmVFdmVudExpc3RlbmVyKFwib3BlblwiLCB0aGlzLndzT25PcGVuKTtcbiAgICAgIHRoaXMud3MucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImNsb3NlXCIsIHRoaXMub25XZWJzb2NrZXRDbG9zZSk7XG4gICAgICB0aGlzLndzLnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsIHRoaXMub25XZWJzb2NrZXRNZXNzYWdlKTtcbiAgICAgIHRoaXMud3MuY2xvc2UoKTtcbiAgICAgIHRoaXMud3MgPSBudWxsO1xuICAgIH1cblxuICAgIC8vIE5vdyB0aGF0IGFsbCBSVENQZWVyQ29ubmVjdGlvbiBjbG9zZWQsIGJlIHN1cmUgdG8gbm90IGNhbGxcbiAgICAvLyByZWNvbm5lY3QoKSBhZ2FpbiB2aWEgcGVyZm9ybURlbGF5ZWRSZWNvbm5lY3QgaWYgcHJldmlvdXNcbiAgICAvLyBSVENQZWVyQ29ubmVjdGlvbiB3YXMgaW4gdGhlIGZhaWxlZCBzdGF0ZS5cbiAgICBpZiAodGhpcy5kZWxheWVkUmVjb25uZWN0VGltZW91dCkge1xuICAgICAgY2xlYXJUaW1lb3V0KHRoaXMuZGVsYXllZFJlY29ubmVjdFRpbWVvdXQpO1xuICAgICAgdGhpcy5kZWxheWVkUmVjb25uZWN0VGltZW91dCA9IG51bGw7XG4gICAgfVxuICB9XG5cbiAgaXNEaXNjb25uZWN0ZWQoKSB7XG4gICAgcmV0dXJuIHRoaXMud3MgPT09IG51bGw7XG4gIH1cblxuICBhc3luYyBvbldlYnNvY2tldE9wZW4oKSB7XG4gICAgLy8gQ3JlYXRlIHRoZSBKYW51cyBTZXNzaW9uXG4gICAgYXdhaXQgdGhpcy5zZXNzaW9uLmNyZWF0ZSgpO1xuXG4gICAgLy8gQXR0YWNoIHRoZSBTRlUgUGx1Z2luIGFuZCBjcmVhdGUgYSBSVENQZWVyQ29ubmVjdGlvbiBmb3IgdGhlIHB1Ymxpc2hlci5cbiAgICAvLyBUaGUgcHVibGlzaGVyIHNlbmRzIGF1ZGlvIGFuZCBvcGVucyB0d28gYmlkaXJlY3Rpb25hbCBkYXRhIGNoYW5uZWxzLlxuICAgIC8vIE9uZSByZWxpYWJsZSBkYXRhY2hhbm5lbCBhbmQgb25lIHVucmVsaWFibGUuXG4gICAgdGhpcy5wdWJsaXNoZXIgPSBhd2FpdCB0aGlzLmNyZWF0ZVB1Ymxpc2hlcigpO1xuXG4gICAgLy8gQ2FsbCB0aGUgbmFmIGNvbm5lY3RTdWNjZXNzIGNhbGxiYWNrIGJlZm9yZSB3ZSBzdGFydCByZWNlaXZpbmcgV2ViUlRDIG1lc3NhZ2VzLlxuICAgIHRoaXMuY29ubmVjdFN1Y2Nlc3ModGhpcy5jbGllbnRJZCk7XG5cbiAgICBjb25zdCBhZGRPY2N1cGFudFByb21pc2VzID0gW107XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMucHVibGlzaGVyLmluaXRpYWxPY2N1cGFudHMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IG9jY3VwYW50SWQgPSB0aGlzLnB1Ymxpc2hlci5pbml0aWFsT2NjdXBhbnRzW2ldO1xuICAgICAgaWYgKG9jY3VwYW50SWQgPT09IHRoaXMuY2xpZW50SWQpIGNvbnRpbnVlOyAvLyBIYXBwZW5zIGR1cmluZyBub24tZ3JhY2VmdWwgcmVjb25uZWN0cyBkdWUgdG8gem9tYmllIHNlc3Npb25zXG4gICAgICBhZGRPY2N1cGFudFByb21pc2VzLnB1c2godGhpcy5hZGRPY2N1cGFudChvY2N1cGFudElkKSk7XG4gICAgfVxuXG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoYWRkT2NjdXBhbnRQcm9taXNlcyk7XG4gIH1cblxuICBvbldlYnNvY2tldENsb3NlKGV2ZW50KSB7XG4gICAgLy8gVGhlIGNvbm5lY3Rpb24gd2FzIGNsb3NlZCBzdWNjZXNzZnVsbHkuIERvbid0IHRyeSB0byByZWNvbm5lY3QuXG4gICAgaWYgKGV2ZW50LmNvZGUgPT09IFdTX05PUk1BTF9DTE9TVVJFKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc29sZS53YXJuKFwiSmFudXMgd2Vic29ja2V0IGNsb3NlZCB1bmV4cGVjdGVkbHkuXCIpO1xuICAgIGlmICh0aGlzLm9uUmVjb25uZWN0aW5nKSB7XG4gICAgICB0aGlzLm9uUmVjb25uZWN0aW5nKHRoaXMucmVjb25uZWN0aW9uRGVsYXkpO1xuICAgIH1cblxuICAgIHRoaXMucmVjb25uZWN0aW9uVGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4gdGhpcy5yZWNvbm5lY3QoKSwgdGhpcy5yZWNvbm5lY3Rpb25EZWxheSk7XG4gIH1cblxuICByZWNvbm5lY3QoKSB7XG4gICAgLy8gRGlzcG9zZSBvZiBhbGwgbmV0d29ya2VkIGVudGl0aWVzIGFuZCBvdGhlciByZXNvdXJjZXMgdGllZCB0byB0aGUgc2Vzc2lvbi5cbiAgICB0aGlzLmRpc2Nvbm5lY3QoKTtcblxuICAgIHRoaXMuY29ubmVjdCgpXG4gICAgICAudGhlbigoKSA9PiB7XG4gICAgICAgIHRoaXMucmVjb25uZWN0aW9uRGVsYXkgPSB0aGlzLmluaXRpYWxSZWNvbm5lY3Rpb25EZWxheTtcbiAgICAgICAgdGhpcy5yZWNvbm5lY3Rpb25BdHRlbXB0cyA9IDA7XG5cbiAgICAgICAgaWYgKHRoaXMub25SZWNvbm5lY3RlZCkge1xuICAgICAgICAgIHRoaXMub25SZWNvbm5lY3RlZCgpO1xuICAgICAgICB9XG4gICAgICB9KVxuICAgICAgLmNhdGNoKGVycm9yID0+IHtcbiAgICAgICAgdGhpcy5yZWNvbm5lY3Rpb25EZWxheSArPSAxMDAwO1xuICAgICAgICB0aGlzLnJlY29ubmVjdGlvbkF0dGVtcHRzKys7XG5cbiAgICAgICAgaWYgKHRoaXMucmVjb25uZWN0aW9uQXR0ZW1wdHMgPiB0aGlzLm1heFJlY29ubmVjdGlvbkF0dGVtcHRzICYmIHRoaXMub25SZWNvbm5lY3Rpb25FcnJvcikge1xuICAgICAgICAgIHJldHVybiB0aGlzLm9uUmVjb25uZWN0aW9uRXJyb3IoXG4gICAgICAgICAgICBuZXcgRXJyb3IoXCJDb25uZWN0aW9uIGNvdWxkIG5vdCBiZSByZWVzdGFibGlzaGVkLCBleGNlZWRlZCBtYXhpbXVtIG51bWJlciBvZiByZWNvbm5lY3Rpb24gYXR0ZW1wdHMuXCIpXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnNvbGUud2FybihcIkVycm9yIGR1cmluZyByZWNvbm5lY3QsIHJldHJ5aW5nLlwiKTtcbiAgICAgICAgY29uc29sZS53YXJuKGVycm9yKTtcblxuICAgICAgICBpZiAodGhpcy5vblJlY29ubmVjdGluZykge1xuICAgICAgICAgIHRoaXMub25SZWNvbm5lY3RpbmcodGhpcy5yZWNvbm5lY3Rpb25EZWxheSk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnJlY29ubmVjdGlvblRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHRoaXMucmVjb25uZWN0KCksIHRoaXMucmVjb25uZWN0aW9uRGVsYXkpO1xuICAgICAgfSk7XG4gIH1cblxuICBwZXJmb3JtRGVsYXllZFJlY29ubmVjdCgpIHtcbiAgICBpZiAodGhpcy5kZWxheWVkUmVjb25uZWN0VGltZW91dCkge1xuICAgICAgY2xlYXJUaW1lb3V0KHRoaXMuZGVsYXllZFJlY29ubmVjdFRpbWVvdXQpO1xuICAgIH1cblxuICAgIHRoaXMuZGVsYXllZFJlY29ubmVjdFRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIHRoaXMuZGVsYXllZFJlY29ubmVjdFRpbWVvdXQgPSBudWxsO1xuICAgICAgdGhpcy5yZWNvbm5lY3QoKTtcbiAgICB9LCAxMDAwMCk7XG4gIH1cblxuICBvbldlYnNvY2tldE1lc3NhZ2UoZXZlbnQpIHtcbiAgICB0aGlzLnNlc3Npb24ucmVjZWl2ZShKU09OLnBhcnNlKGV2ZW50LmRhdGEpKTtcbiAgfVxuXG4gIGFzeW5jIGFkZE9jY3VwYW50KG9jY3VwYW50SWQpIHtcbiAgICBpZiAodGhpcy5vY2N1cGFudHNbb2NjdXBhbnRJZF0pIHtcbiAgICAgIHRoaXMucmVtb3ZlT2NjdXBhbnQob2NjdXBhbnRJZCk7XG4gICAgfVxuXG4gICAgdGhpcy5sZWZ0T2NjdXBhbnRzLmRlbGV0ZShvY2N1cGFudElkKTtcblxuICAgIHZhciBzdWJzY3JpYmVyID0gYXdhaXQgdGhpcy5jcmVhdGVTdWJzY3JpYmVyKG9jY3VwYW50SWQpO1xuXG4gICAgaWYgKCFzdWJzY3JpYmVyKSByZXR1cm47XG5cbiAgICB0aGlzLm9jY3VwYW50c1tvY2N1cGFudElkXSA9IHN1YnNjcmliZXI7XG5cbiAgICB0aGlzLnNldE1lZGlhU3RyZWFtKG9jY3VwYW50SWQsIHN1YnNjcmliZXIubWVkaWFTdHJlYW0pO1xuXG4gICAgLy8gQ2FsbCB0aGUgTmV0d29ya2VkIEFGcmFtZSBjYWxsYmFja3MgZm9yIHRoZSBuZXcgb2NjdXBhbnQuXG4gICAgdGhpcy5vbk9jY3VwYW50Q29ubmVjdGVkKG9jY3VwYW50SWQpO1xuICAgIHRoaXMub25PY2N1cGFudHNDaGFuZ2VkKHRoaXMub2NjdXBhbnRzKTtcblxuICAgIHJldHVybiBzdWJzY3JpYmVyO1xuICB9XG5cbiAgcmVtb3ZlQWxsT2NjdXBhbnRzKCkge1xuICAgIGZvciAoY29uc3Qgb2NjdXBhbnRJZCBvZiBPYmplY3QuZ2V0T3duUHJvcGVydHlOYW1lcyh0aGlzLm9jY3VwYW50cykpIHtcbiAgICAgIHRoaXMucmVtb3ZlT2NjdXBhbnQob2NjdXBhbnRJZCk7XG4gICAgfVxuICB9XG5cbiAgcmVtb3ZlT2NjdXBhbnQob2NjdXBhbnRJZCkge1xuICAgIHRoaXMubGVmdE9jY3VwYW50cy5hZGQob2NjdXBhbnRJZCk7XG5cbiAgICBpZiAodGhpcy5vY2N1cGFudHNbb2NjdXBhbnRJZF0pIHtcbiAgICAgIC8vIENsb3NlIHRoZSBzdWJzY3JpYmVyIHBlZXIgY29ubmVjdGlvbi4gV2hpY2ggYWxzbyBkZXRhY2hlcyB0aGUgcGx1Z2luIGhhbmRsZS5cbiAgICAgIHRoaXMub2NjdXBhbnRzW29jY3VwYW50SWRdLmNvbm4uY2xvc2UoKTtcbiAgICAgIGRlbGV0ZSB0aGlzLm9jY3VwYW50c1tvY2N1cGFudElkXTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5tZWRpYVN0cmVhbXNbb2NjdXBhbnRJZF0pIHtcbiAgICAgIGRlbGV0ZSB0aGlzLm1lZGlhU3RyZWFtc1tvY2N1cGFudElkXTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5oYXMob2NjdXBhbnRJZCkpIHtcbiAgICAgIGNvbnN0IG1zZyA9IFwiVGhlIHVzZXIgZGlzY29ubmVjdGVkIGJlZm9yZSB0aGUgbWVkaWEgc3RyZWFtIHdhcyByZXNvbHZlZC5cIjtcbiAgICAgIHRoaXMucGVuZGluZ01lZGlhUmVxdWVzdHMuZ2V0KG9jY3VwYW50SWQpLmF1ZGlvLnJlamVjdChtc2cpO1xuICAgICAgdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5nZXQob2NjdXBhbnRJZCkudmlkZW8ucmVqZWN0KG1zZyk7XG4gICAgICB0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmRlbGV0ZShvY2N1cGFudElkKTtcbiAgICB9XG5cbiAgICAvLyBDYWxsIHRoZSBOZXR3b3JrZWQgQUZyYW1lIGNhbGxiYWNrcyBmb3IgdGhlIHJlbW92ZWQgb2NjdXBhbnQuXG4gICAgdGhpcy5vbk9jY3VwYW50RGlzY29ubmVjdGVkKG9jY3VwYW50SWQpO1xuICAgIHRoaXMub25PY2N1cGFudHNDaGFuZ2VkKHRoaXMub2NjdXBhbnRzKTtcbiAgfVxuXG4gIGFzc29jaWF0ZShjb25uLCBoYW5kbGUpIHtcbiAgICBjb25uLmFkZEV2ZW50TGlzdGVuZXIoXCJpY2VjYW5kaWRhdGVcIiwgZXYgPT4ge1xuICAgICAgaGFuZGxlLnNlbmRUcmlja2xlKGV2LmNhbmRpZGF0ZSB8fCBudWxsKS5jYXRjaChlID0+IGVycm9yKFwiRXJyb3IgdHJpY2tsaW5nIElDRTogJW9cIiwgZSkpO1xuICAgIH0pO1xuICAgIGNvbm4uYWRkRXZlbnRMaXN0ZW5lcihcImljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZVwiLCBldiA9PiB7XG4gICAgICBpZiAoY29ubi5pY2VDb25uZWN0aW9uU3RhdGUgPT09IFwiY29ubmVjdGVkXCIpIHtcbiAgICAgICAgY29uc29sZS5sb2coXCJJQ0Ugc3RhdGUgY2hhbmdlZCB0byBjb25uZWN0ZWRcIik7XG4gICAgICB9XG4gICAgICBpZiAoY29ubi5pY2VDb25uZWN0aW9uU3RhdGUgPT09IFwiZGlzY29ubmVjdGVkXCIpIHtcbiAgICAgICAgY29uc29sZS53YXJuKFwiSUNFIHN0YXRlIGNoYW5nZWQgdG8gZGlzY29ubmVjdGVkXCIpO1xuICAgICAgfVxuICAgICAgaWYgKGNvbm4uaWNlQ29ubmVjdGlvblN0YXRlID09PSBcImZhaWxlZFwiKSB7XG4gICAgICAgIGNvbnNvbGUud2FybihcIklDRSBmYWlsdXJlIGRldGVjdGVkLiBSZWNvbm5lY3RpbmcgaW4gMTBzLlwiKTtcbiAgICAgICAgdGhpcy5wZXJmb3JtRGVsYXllZFJlY29ubmVjdCgpO1xuICAgICAgfVxuICAgIH0pXG5cbiAgICAvLyB3ZSBoYXZlIHRvIGRlYm91bmNlIHRoZXNlIGJlY2F1c2UgamFudXMgZ2V0cyBhbmdyeSBpZiB5b3Ugc2VuZCBpdCBhIG5ldyBTRFAgYmVmb3JlXG4gICAgLy8gaXQncyBmaW5pc2hlZCBwcm9jZXNzaW5nIGFuIGV4aXN0aW5nIFNEUC4gaW4gYWN0dWFsaXR5LCBpdCBzZWVtcyBsaWtlIHRoaXMgaXMgbWF5YmVcbiAgICAvLyB0b28gbGliZXJhbCBhbmQgd2UgbmVlZCB0byB3YWl0IHNvbWUgYW1vdW50IG9mIHRpbWUgYWZ0ZXIgYW4gb2ZmZXIgYmVmb3JlIHNlbmRpbmcgYW5vdGhlcixcbiAgICAvLyBidXQgd2UgZG9uJ3QgY3VycmVudGx5IGtub3cgYW55IGdvb2Qgd2F5IG9mIGRldGVjdGluZyBleGFjdGx5IGhvdyBsb25nIDooXG4gICAgY29ubi5hZGRFdmVudExpc3RlbmVyKFxuICAgICAgXCJuZWdvdGlhdGlvbm5lZWRlZFwiLFxuICAgICAgZGVib3VuY2UoZXYgPT4ge1xuICAgICAgICBkZWJ1ZyhcIlNlbmRpbmcgbmV3IG9mZmVyIGZvciBoYW5kbGU6ICVvXCIsIGhhbmRsZSk7XG4gICAgICAgIHZhciBvZmZlciA9IGNvbm4uY3JlYXRlT2ZmZXIoKS50aGVuKHRoaXMuY29uZmlndXJlUHVibGlzaGVyU2RwKS50aGVuKHRoaXMuZml4U2FmYXJpSWNlVUZyYWcpO1xuICAgICAgICB2YXIgbG9jYWwgPSBvZmZlci50aGVuKG8gPT4gY29ubi5zZXRMb2NhbERlc2NyaXB0aW9uKG8pKTtcbiAgICAgICAgdmFyIHJlbW90ZSA9IG9mZmVyO1xuXG4gICAgICAgIHJlbW90ZSA9IHJlbW90ZVxuICAgICAgICAgIC50aGVuKHRoaXMuZml4U2FmYXJpSWNlVUZyYWcpXG4gICAgICAgICAgLnRoZW4oaiA9PiBoYW5kbGUuc2VuZEpzZXAoaikpXG4gICAgICAgICAgLnRoZW4ociA9PiBjb25uLnNldFJlbW90ZURlc2NyaXB0aW9uKHIuanNlcCkpO1xuICAgICAgICByZXR1cm4gUHJvbWlzZS5hbGwoW2xvY2FsLCByZW1vdGVdKS5jYXRjaChlID0+IGVycm9yKFwiRXJyb3IgbmVnb3RpYXRpbmcgb2ZmZXI6ICVvXCIsIGUpKTtcbiAgICAgIH0pXG4gICAgKTtcbiAgICBoYW5kbGUub24oXG4gICAgICBcImV2ZW50XCIsXG4gICAgICBkZWJvdW5jZShldiA9PiB7XG4gICAgICAgIHZhciBqc2VwID0gZXYuanNlcDtcbiAgICAgICAgaWYgKGpzZXAgJiYganNlcC50eXBlID09IFwib2ZmZXJcIikge1xuICAgICAgICAgIGRlYnVnKFwiQWNjZXB0aW5nIG5ldyBvZmZlciBmb3IgaGFuZGxlOiAlb1wiLCBoYW5kbGUpO1xuICAgICAgICAgIHZhciBhbnN3ZXIgPSBjb25uXG4gICAgICAgICAgICAuc2V0UmVtb3RlRGVzY3JpcHRpb24odGhpcy5jb25maWd1cmVTdWJzY3JpYmVyU2RwKGpzZXApKVxuICAgICAgICAgICAgLnRoZW4oXyA9PiBjb25uLmNyZWF0ZUFuc3dlcigpKVxuICAgICAgICAgICAgLnRoZW4odGhpcy5maXhTYWZhcmlJY2VVRnJhZyk7XG4gICAgICAgICAgdmFyIGxvY2FsID0gYW5zd2VyLnRoZW4oYSA9PiBjb25uLnNldExvY2FsRGVzY3JpcHRpb24oYSkpO1xuICAgICAgICAgIHZhciByZW1vdGUgPSBhbnN3ZXIudGhlbihqID0+IGhhbmRsZS5zZW5kSnNlcChqKSk7XG4gICAgICAgICAgcmV0dXJuIFByb21pc2UuYWxsKFtsb2NhbCwgcmVtb3RlXSkuY2F0Y2goZSA9PiBlcnJvcihcIkVycm9yIG5lZ290aWF0aW5nIGFuc3dlcjogJW9cIiwgZSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIHNvbWUgb3RoZXIga2luZCBvZiBldmVudCwgbm90aGluZyB0byBkb1xuICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICB9XG4gICAgICB9KVxuICAgICk7XG4gIH1cblxuICBhc3luYyBjcmVhdGVQdWJsaXNoZXIoKSB7XG4gICAgdmFyIGhhbmRsZSA9IG5ldyBtai5KYW51c1BsdWdpbkhhbmRsZSh0aGlzLnNlc3Npb24pO1xuICAgIHZhciBjb25uID0gbmV3IFJUQ1BlZXJDb25uZWN0aW9uKHRoaXMucGVlckNvbm5lY3Rpb25Db25maWcgfHwgREVGQVVMVF9QRUVSX0NPTk5FQ1RJT05fQ09ORklHKTtcblxuICAgIGRlYnVnKFwicHViIHdhaXRpbmcgZm9yIHNmdVwiKTtcbiAgICBhd2FpdCBoYW5kbGUuYXR0YWNoKFwiamFudXMucGx1Z2luLnNmdVwiKTtcblxuICAgIHRoaXMuYXNzb2NpYXRlKGNvbm4sIGhhbmRsZSk7XG5cbiAgICBkZWJ1ZyhcInB1YiB3YWl0aW5nIGZvciBkYXRhIGNoYW5uZWxzICYgd2VicnRjdXBcIik7XG4gICAgdmFyIHdlYnJ0Y3VwID0gbmV3IFByb21pc2UocmVzb2x2ZSA9PiBoYW5kbGUub24oXCJ3ZWJydGN1cFwiLCByZXNvbHZlKSk7XG5cbiAgICAvLyBVbnJlbGlhYmxlIGRhdGFjaGFubmVsOiBzZW5kaW5nIGFuZCByZWNlaXZpbmcgY29tcG9uZW50IHVwZGF0ZXMuXG4gICAgLy8gUmVsaWFibGUgZGF0YWNoYW5uZWw6IHNlbmRpbmcgYW5kIHJlY2lldmluZyBlbnRpdHkgaW5zdGFudGlhdGlvbnMuXG4gICAgdmFyIHJlbGlhYmxlQ2hhbm5lbCA9IGNvbm4uY3JlYXRlRGF0YUNoYW5uZWwoXCJyZWxpYWJsZVwiLCB7IG9yZGVyZWQ6IHRydWUgfSk7XG4gICAgdmFyIHVucmVsaWFibGVDaGFubmVsID0gY29ubi5jcmVhdGVEYXRhQ2hhbm5lbChcInVucmVsaWFibGVcIiwge1xuICAgICAgb3JkZXJlZDogZmFsc2UsXG4gICAgICBtYXhSZXRyYW5zbWl0czogMFxuICAgIH0pO1xuXG4gICAgcmVsaWFibGVDaGFubmVsLmFkZEV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsIGUgPT4gdGhpcy5vbkRhdGFDaGFubmVsTWVzc2FnZShlLCBcImphbnVzLXJlbGlhYmxlXCIpKTtcbiAgICB1bnJlbGlhYmxlQ2hhbm5lbC5hZGRFdmVudExpc3RlbmVyKFwibWVzc2FnZVwiLCBlID0+IHRoaXMub25EYXRhQ2hhbm5lbE1lc3NhZ2UoZSwgXCJqYW51cy11bnJlbGlhYmxlXCIpKTtcblxuICAgIGF3YWl0IHdlYnJ0Y3VwO1xuICAgIGF3YWl0IHVudGlsRGF0YUNoYW5uZWxPcGVuKHJlbGlhYmxlQ2hhbm5lbCk7XG4gICAgYXdhaXQgdW50aWxEYXRhQ2hhbm5lbE9wZW4odW5yZWxpYWJsZUNoYW5uZWwpO1xuXG4gICAgLy8gZG9pbmcgdGhpcyBoZXJlIGlzIHNvcnQgb2YgYSBoYWNrIGFyb3VuZCBjaHJvbWUgcmVuZWdvdGlhdGlvbiB3ZWlyZG5lc3MgLS1cbiAgICAvLyBpZiB3ZSBkbyBpdCBwcmlvciB0byB3ZWJydGN1cCwgY2hyb21lIG9uIGdlYXIgVlIgd2lsbCBzb21ldGltZXMgcHV0IGFcbiAgICAvLyByZW5lZ290aWF0aW9uIG9mZmVyIGluIGZsaWdodCB3aGlsZSB0aGUgZmlyc3Qgb2ZmZXIgd2FzIHN0aWxsIGJlaW5nXG4gICAgLy8gcHJvY2Vzc2VkIGJ5IGphbnVzLiB3ZSBzaG91bGQgZmluZCBzb21lIG1vcmUgcHJpbmNpcGxlZCB3YXkgdG8gZmlndXJlIG91dFxuICAgIC8vIHdoZW4gamFudXMgaXMgZG9uZSBpbiB0aGUgZnV0dXJlLlxuICAgIGlmICh0aGlzLmxvY2FsTWVkaWFTdHJlYW0pIHtcbiAgICAgIHRoaXMubG9jYWxNZWRpYVN0cmVhbS5nZXRUcmFja3MoKS5mb3JFYWNoKHRyYWNrID0+IHtcbiAgICAgICAgY29ubi5hZGRUcmFjayh0cmFjaywgdGhpcy5sb2NhbE1lZGlhU3RyZWFtKTtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIEhhbmRsZSBhbGwgb2YgdGhlIGpvaW4gYW5kIGxlYXZlIGV2ZW50cy5cbiAgICBoYW5kbGUub24oXCJldmVudFwiLCBldiA9PiB7XG4gICAgICB2YXIgZGF0YSA9IGV2LnBsdWdpbmRhdGEuZGF0YTtcbiAgICAgIGlmIChkYXRhLmV2ZW50ID09IFwiam9pblwiICYmIGRhdGEucm9vbV9pZCA9PSB0aGlzLnJvb20pIHtcbiAgICAgICAgaWYgKHRoaXMuZGVsYXllZFJlY29ubmVjdFRpbWVvdXQpIHtcbiAgICAgICAgICAvLyBEb24ndCBjcmVhdGUgYSBuZXcgUlRDUGVlckNvbm5lY3Rpb24sIGFsbCBSVENQZWVyQ29ubmVjdGlvbiB3aWxsIGJlIGNsb3NlZCBpbiBsZXNzIHRoYW4gMTBzLlxuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmFkZE9jY3VwYW50KGRhdGEudXNlcl9pZCk7XG4gICAgICB9IGVsc2UgaWYgKGRhdGEuZXZlbnQgPT0gXCJsZWF2ZVwiICYmIGRhdGEucm9vbV9pZCA9PSB0aGlzLnJvb20pIHtcbiAgICAgICAgdGhpcy5yZW1vdmVPY2N1cGFudChkYXRhLnVzZXJfaWQpO1xuICAgICAgfSBlbHNlIGlmIChkYXRhLmV2ZW50ID09IFwiYmxvY2tlZFwiKSB7XG4gICAgICAgIGRvY3VtZW50LmJvZHkuZGlzcGF0Y2hFdmVudChuZXcgQ3VzdG9tRXZlbnQoXCJibG9ja2VkXCIsIHsgZGV0YWlsOiB7IGNsaWVudElkOiBkYXRhLmJ5IH0gfSkpO1xuICAgICAgfSBlbHNlIGlmIChkYXRhLmV2ZW50ID09IFwidW5ibG9ja2VkXCIpIHtcbiAgICAgICAgZG9jdW1lbnQuYm9keS5kaXNwYXRjaEV2ZW50KG5ldyBDdXN0b21FdmVudChcInVuYmxvY2tlZFwiLCB7IGRldGFpbDogeyBjbGllbnRJZDogZGF0YS5ieSB9IH0pKTtcbiAgICAgIH0gZWxzZSBpZiAoZGF0YS5ldmVudCA9PT0gXCJkYXRhXCIpIHtcbiAgICAgICAgdGhpcy5vbkRhdGEoSlNPTi5wYXJzZShkYXRhLmJvZHkpLCBcImphbnVzLWV2ZW50XCIpO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgZGVidWcoXCJwdWIgd2FpdGluZyBmb3Igam9pblwiKTtcblxuICAgIC8vIFNlbmQgam9pbiBtZXNzYWdlIHRvIGphbnVzLiBMaXN0ZW4gZm9yIGpvaW4vbGVhdmUgbWVzc2FnZXMuIEF1dG9tYXRpY2FsbHkgc3Vic2NyaWJlIHRvIGFsbCB1c2VycycgV2ViUlRDIGRhdGEuXG4gICAgdmFyIG1lc3NhZ2UgPSBhd2FpdCB0aGlzLnNlbmRKb2luKGhhbmRsZSwge1xuICAgICAgbm90aWZpY2F0aW9uczogdHJ1ZSxcbiAgICAgIGRhdGE6IHRydWVcbiAgICB9KTtcblxuICAgIGlmICghbWVzc2FnZS5wbHVnaW5kYXRhLmRhdGEuc3VjY2Vzcykge1xuICAgICAgY29uc3QgZXJyID0gbWVzc2FnZS5wbHVnaW5kYXRhLmRhdGEuZXJyb3I7XG4gICAgICBjb25zb2xlLmVycm9yKGVycik7XG4gICAgICAvLyBXZSBtYXkgZ2V0IGhlcmUgYmVjYXVzZSBvZiBhbiBleHBpcmVkIEpXVC5cbiAgICAgIC8vIENsb3NlIHRoZSBjb25uZWN0aW9uIG91cnNlbGYgb3RoZXJ3aXNlIGphbnVzIHdpbGwgY2xvc2UgaXQgYWZ0ZXJcbiAgICAgIC8vIHNlc3Npb25fdGltZW91dCBiZWNhdXNlIHdlIGRpZG4ndCBzZW5kIGFueSBrZWVwYWxpdmUgYW5kIHRoaXMgd2lsbFxuICAgICAgLy8gdHJpZ2dlciBhIGRlbGF5ZWQgcmVjb25uZWN0IGJlY2F1c2Ugb2YgdGhlIGljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZVxuICAgICAgLy8gbGlzdGVuZXIgZm9yIGZhaWx1cmUgc3RhdGUuXG4gICAgICAvLyBFdmVuIGlmIHRoZSBhcHAgY29kZSBjYWxscyBkaXNjb25uZWN0IGluIGNhc2Ugb2YgZXJyb3IsIGRpc2Nvbm5lY3RcbiAgICAgIC8vIHdvbid0IGNsb3NlIHRoZSBwZWVyIGNvbm5lY3Rpb24gYmVjYXVzZSB0aGlzLnB1Ymxpc2hlciBpcyBub3Qgc2V0LlxuICAgICAgY29ubi5jbG9zZSgpO1xuICAgICAgdGhyb3cgZXJyO1xuICAgIH1cblxuICAgIHZhciBpbml0aWFsT2NjdXBhbnRzID0gbWVzc2FnZS5wbHVnaW5kYXRhLmRhdGEucmVzcG9uc2UudXNlcnNbdGhpcy5yb29tXSB8fCBbXTtcblxuICAgIGlmIChpbml0aWFsT2NjdXBhbnRzLmluY2x1ZGVzKHRoaXMuY2xpZW50SWQpKSB7XG4gICAgICBjb25zb2xlLndhcm4oXCJKYW51cyBzdGlsbCBoYXMgcHJldmlvdXMgc2Vzc2lvbiBmb3IgdGhpcyBjbGllbnQuIFJlY29ubmVjdGluZyBpbiAxMHMuXCIpO1xuICAgICAgdGhpcy5wZXJmb3JtRGVsYXllZFJlY29ubmVjdCgpO1xuICAgIH1cblxuICAgIGRlYnVnKFwicHVibGlzaGVyIHJlYWR5XCIpO1xuICAgIHJldHVybiB7XG4gICAgICBoYW5kbGUsXG4gICAgICBpbml0aWFsT2NjdXBhbnRzLFxuICAgICAgcmVsaWFibGVDaGFubmVsLFxuICAgICAgdW5yZWxpYWJsZUNoYW5uZWwsXG4gICAgICBjb25uXG4gICAgfTtcbiAgfVxuXG4gIGNvbmZpZ3VyZVB1Ymxpc2hlclNkcChqc2VwKSB7XG4gICAganNlcC5zZHAgPSBqc2VwLnNkcC5yZXBsYWNlKC9hPWZtdHA6KDEwOXwxMTEpLipcXHJcXG4vZywgKGxpbmUsIHB0KSA9PiB7XG4gICAgICBjb25zdCBwYXJhbWV0ZXJzID0gT2JqZWN0LmFzc2lnbihzZHBVdGlscy5wYXJzZUZtdHAobGluZSksIE9QVVNfUEFSQU1FVEVSUyk7XG4gICAgICByZXR1cm4gc2RwVXRpbHMud3JpdGVGbXRwKHsgcGF5bG9hZFR5cGU6IHB0LCBwYXJhbWV0ZXJzOiBwYXJhbWV0ZXJzIH0pO1xuICAgIH0pO1xuICAgIHJldHVybiBqc2VwO1xuICB9XG5cbiAgY29uZmlndXJlU3Vic2NyaWJlclNkcChqc2VwKSB7XG4gICAgLy8gdG9kbzogY29uc2lkZXIgY2xlYW5pbmcgdXAgdGhlc2UgaGFja3MgdG8gdXNlIHNkcHV0aWxzXG4gICAgaWYgKCFpc0gyNjRWaWRlb1N1cHBvcnRlZCkge1xuICAgICAgaWYgKG5hdmlnYXRvci51c2VyQWdlbnQuaW5kZXhPZihcIkhlYWRsZXNzQ2hyb21lXCIpICE9PSAtMSkge1xuICAgICAgICAvLyBIZWFkbGVzc0Nocm9tZSAoZS5nLiBwdXBwZXRlZXIpIGRvZXNuJ3Qgc3VwcG9ydCB3ZWJydGMgdmlkZW8gc3RyZWFtcywgc28gd2UgcmVtb3ZlIHRob3NlIGxpbmVzIGZyb20gdGhlIFNEUC5cbiAgICAgICAganNlcC5zZHAgPSBqc2VwLnNkcC5yZXBsYWNlKC9tPXZpZGVvW15dKm09LywgXCJtPVwiKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBUT0RPOiBIYWNrIHRvIGdldCB2aWRlbyB3b3JraW5nIG9uIENocm9tZSBmb3IgQW5kcm9pZC4gaHR0cHM6Ly9ncm91cHMuZ29vZ2xlLmNvbS9mb3J1bS8jIXRvcGljL21vemlsbGEuZGV2Lm1lZGlhL1llMjl2dU1UcG84XG4gICAgaWYgKG5hdmlnYXRvci51c2VyQWdlbnQuaW5kZXhPZihcIkFuZHJvaWRcIikgPT09IC0xKSB7XG4gICAgICBqc2VwLnNkcCA9IGpzZXAuc2RwLnJlcGxhY2UoXG4gICAgICAgIFwiYT1ydGNwLWZiOjEwNyBnb29nLXJlbWJcXHJcXG5cIixcbiAgICAgICAgXCJhPXJ0Y3AtZmI6MTA3IGdvb2ctcmVtYlxcclxcbmE9cnRjcC1mYjoxMDcgdHJhbnNwb3J0LWNjXFxyXFxuYT1mbXRwOjEwNyBsZXZlbC1hc3ltbWV0cnktYWxsb3dlZD0xO3BhY2tldGl6YXRpb24tbW9kZT0xO3Byb2ZpbGUtbGV2ZWwtaWQ9NDJlMDFmXFxyXFxuXCJcbiAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGpzZXAuc2RwID0ganNlcC5zZHAucmVwbGFjZShcbiAgICAgICAgXCJhPXJ0Y3AtZmI6MTA3IGdvb2ctcmVtYlxcclxcblwiLFxuICAgICAgICBcImE9cnRjcC1mYjoxMDcgZ29vZy1yZW1iXFxyXFxuYT1ydGNwLWZiOjEwNyB0cmFuc3BvcnQtY2NcXHJcXG5hPWZtdHA6MTA3IGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTE7cGFja2V0aXphdGlvbi1tb2RlPTE7cHJvZmlsZS1sZXZlbC1pZD00MjAwMWZcXHJcXG5cIlxuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIGpzZXA7XG4gIH1cblxuICBhc3luYyBmaXhTYWZhcmlJY2VVRnJhZyhqc2VwKSB7XG4gICAgLy8gU2FmYXJpIHByb2R1Y2VzIGEgXFxuIGluc3RlYWQgb2YgYW4gXFxyXFxuIGZvciB0aGUgaWNlLXVmcmFnLiBTZWUgaHR0cHM6Ly9naXRodWIuY29tL21lZXRlY2hvL2phbnVzLWdhdGV3YXkvaXNzdWVzLzE4MThcbiAgICBqc2VwLnNkcCA9IGpzZXAuc2RwLnJlcGxhY2UoL1teXFxyXVxcbmE9aWNlLXVmcmFnL2csIFwiXFxyXFxuYT1pY2UtdWZyYWdcIik7XG4gICAgcmV0dXJuIGpzZXBcbiAgfVxuXG4gIGFzeW5jIGNyZWF0ZVN1YnNjcmliZXIob2NjdXBhbnRJZCwgbWF4UmV0cmllcyA9IDUpIHtcbiAgICBpZiAodGhpcy5sZWZ0T2NjdXBhbnRzLmhhcyhvY2N1cGFudElkKSkge1xuICAgICAgY29uc29sZS53YXJuKG9jY3VwYW50SWQgKyBcIjogY2FuY2VsbGVkIG9jY3VwYW50IGNvbm5lY3Rpb24sIG9jY3VwYW50IGxlZnQgYmVmb3JlIHN1YnNjcmlwdGlvbiBuZWdvdGF0aW9uLlwiKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIHZhciBoYW5kbGUgPSBuZXcgbWouSmFudXNQbHVnaW5IYW5kbGUodGhpcy5zZXNzaW9uKTtcbiAgICB2YXIgY29ubiA9IG5ldyBSVENQZWVyQ29ubmVjdGlvbih0aGlzLnBlZXJDb25uZWN0aW9uQ29uZmlnIHx8IERFRkFVTFRfUEVFUl9DT05ORUNUSU9OX0NPTkZJRyk7XG5cbiAgICBkZWJ1ZyhvY2N1cGFudElkICsgXCI6IHN1YiB3YWl0aW5nIGZvciBzZnVcIik7XG4gICAgYXdhaXQgaGFuZGxlLmF0dGFjaChcImphbnVzLnBsdWdpbi5zZnVcIik7XG5cbiAgICB0aGlzLmFzc29jaWF0ZShjb25uLCBoYW5kbGUpO1xuXG4gICAgZGVidWcob2NjdXBhbnRJZCArIFwiOiBzdWIgd2FpdGluZyBmb3Igam9pblwiKTtcblxuICAgIGlmICh0aGlzLmxlZnRPY2N1cGFudHMuaGFzKG9jY3VwYW50SWQpKSB7XG4gICAgICBjb25uLmNsb3NlKCk7XG4gICAgICBjb25zb2xlLndhcm4ob2NjdXBhbnRJZCArIFwiOiBjYW5jZWxsZWQgb2NjdXBhbnQgY29ubmVjdGlvbiwgb2NjdXBhbnQgbGVmdCBhZnRlciBhdHRhY2hcIik7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICBsZXQgd2VicnRjRmFpbGVkID0gZmFsc2U7XG5cbiAgICBjb25zdCB3ZWJydGN1cCA9IG5ldyBQcm9taXNlKHJlc29sdmUgPT4ge1xuICAgICAgY29uc3QgbGVmdEludGVydmFsID0gc2V0SW50ZXJ2YWwoKCkgPT4ge1xuICAgICAgICBpZiAodGhpcy5sZWZ0T2NjdXBhbnRzLmhhcyhvY2N1cGFudElkKSkge1xuICAgICAgICAgIGNsZWFySW50ZXJ2YWwobGVmdEludGVydmFsKTtcbiAgICAgICAgICByZXNvbHZlKCk7XG4gICAgICAgIH1cbiAgICAgIH0sIDEwMDApO1xuXG4gICAgICBjb25zdCB0aW1lb3V0ID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgIGNsZWFySW50ZXJ2YWwobGVmdEludGVydmFsKTtcbiAgICAgICAgd2VicnRjRmFpbGVkID0gdHJ1ZTtcbiAgICAgICAgcmVzb2x2ZSgpO1xuICAgICAgfSwgU1VCU0NSSUJFX1RJTUVPVVRfTVMpO1xuXG4gICAgICBoYW5kbGUub24oXCJ3ZWJydGN1cFwiLCAoKSA9PiB7XG4gICAgICAgIGNsZWFyVGltZW91dCh0aW1lb3V0KTtcbiAgICAgICAgY2xlYXJJbnRlcnZhbChsZWZ0SW50ZXJ2YWwpO1xuICAgICAgICByZXNvbHZlKCk7XG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIC8vIFNlbmQgam9pbiBtZXNzYWdlIHRvIGphbnVzLiBEb24ndCBsaXN0ZW4gZm9yIGpvaW4vbGVhdmUgbWVzc2FnZXMuIFN1YnNjcmliZSB0byB0aGUgb2NjdXBhbnQncyBtZWRpYS5cbiAgICAvLyBKYW51cyBzaG91bGQgc2VuZCB1cyBhbiBvZmZlciBmb3IgdGhpcyBvY2N1cGFudCdzIG1lZGlhIGluIHJlc3BvbnNlIHRvIHRoaXMuXG4gICAgYXdhaXQgdGhpcy5zZW5kSm9pbihoYW5kbGUsIHsgbWVkaWE6IG9jY3VwYW50SWQgfSk7XG5cbiAgICBpZiAodGhpcy5sZWZ0T2NjdXBhbnRzLmhhcyhvY2N1cGFudElkKSkge1xuICAgICAgY29ubi5jbG9zZSgpO1xuICAgICAgY29uc29sZS53YXJuKG9jY3VwYW50SWQgKyBcIjogY2FuY2VsbGVkIG9jY3VwYW50IGNvbm5lY3Rpb24sIG9jY3VwYW50IGxlZnQgYWZ0ZXIgam9pblwiKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGRlYnVnKG9jY3VwYW50SWQgKyBcIjogc3ViIHdhaXRpbmcgZm9yIHdlYnJ0Y3VwXCIpO1xuICAgIGF3YWl0IHdlYnJ0Y3VwO1xuXG4gICAgaWYgKHRoaXMubGVmdE9jY3VwYW50cy5oYXMob2NjdXBhbnRJZCkpIHtcbiAgICAgIGNvbm4uY2xvc2UoKTtcbiAgICAgIGNvbnNvbGUud2FybihvY2N1cGFudElkICsgXCI6IGNhbmNlbCBvY2N1cGFudCBjb25uZWN0aW9uLCBvY2N1cGFudCBsZWZ0IGR1cmluZyBvciBhZnRlciB3ZWJydGN1cFwiKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGlmICh3ZWJydGNGYWlsZWQpIHtcbiAgICAgIGNvbm4uY2xvc2UoKTtcbiAgICAgIGlmIChtYXhSZXRyaWVzID4gMCkge1xuICAgICAgICBjb25zb2xlLndhcm4ob2NjdXBhbnRJZCArIFwiOiB3ZWJydGMgdXAgdGltZWQgb3V0LCByZXRyeWluZ1wiKTtcbiAgICAgICAgcmV0dXJuIHRoaXMuY3JlYXRlU3Vic2NyaWJlcihvY2N1cGFudElkLCBtYXhSZXRyaWVzIC0gMSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zb2xlLndhcm4ob2NjdXBhbnRJZCArIFwiOiB3ZWJydGMgdXAgdGltZWQgb3V0XCIpO1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoaXNTYWZhcmkgJiYgIXRoaXMuX2lPU0hhY2tEZWxheWVkSW5pdGlhbFBlZXIpIHtcbiAgICAgIC8vIEhBQ0s6IHRoZSBmaXJzdCBwZWVyIG9uIFNhZmFyaSBkdXJpbmcgcGFnZSBsb2FkIGNhbiBmYWlsIHRvIHdvcmsgaWYgd2UgZG9uJ3RcbiAgICAgIC8vIHdhaXQgc29tZSB0aW1lIGJlZm9yZSBjb250aW51aW5nIGhlcmUuIFNlZTogaHR0cHM6Ly9naXRodWIuY29tL21vemlsbGEvaHVicy9wdWxsLzE2OTJcbiAgICAgIGF3YWl0IChuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gc2V0VGltZW91dChyZXNvbHZlLCAzMDAwKSkpO1xuICAgICAgdGhpcy5faU9TSGFja0RlbGF5ZWRJbml0aWFsUGVlciA9IHRydWU7XG4gICAgfVxuXG4gICAgdmFyIG1lZGlhU3RyZWFtID0gbmV3IE1lZGlhU3RyZWFtKCk7XG4gICAgdmFyIHJlY2VpdmVycyA9IGNvbm4uZ2V0UmVjZWl2ZXJzKCk7XG4gICAgcmVjZWl2ZXJzLmZvckVhY2gocmVjZWl2ZXIgPT4ge1xuICAgICAgaWYgKHJlY2VpdmVyLnRyYWNrKSB7XG4gICAgICAgIG1lZGlhU3RyZWFtLmFkZFRyYWNrKHJlY2VpdmVyLnRyYWNrKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICBpZiAobWVkaWFTdHJlYW0uZ2V0VHJhY2tzKCkubGVuZ3RoID09PSAwKSB7XG4gICAgICBtZWRpYVN0cmVhbSA9IG51bGw7XG4gICAgfVxuXG4gICAgZGVidWcob2NjdXBhbnRJZCArIFwiOiBzdWJzY3JpYmVyIHJlYWR5XCIpO1xuICAgIHJldHVybiB7XG4gICAgICBoYW5kbGUsXG4gICAgICBtZWRpYVN0cmVhbSxcbiAgICAgIGNvbm5cbiAgICB9O1xuICB9XG5cbiAgc2VuZEpvaW4oaGFuZGxlLCBzdWJzY3JpYmUpIHtcbiAgICByZXR1cm4gaGFuZGxlLnNlbmRNZXNzYWdlKHtcbiAgICAgIGtpbmQ6IFwiam9pblwiLFxuICAgICAgcm9vbV9pZDogdGhpcy5yb29tLFxuICAgICAgdXNlcl9pZDogdGhpcy5jbGllbnRJZCxcbiAgICAgIHN1YnNjcmliZSxcbiAgICAgIHRva2VuOiB0aGlzLmpvaW5Ub2tlblxuICAgIH0pO1xuICB9XG5cbiAgdG9nZ2xlRnJlZXplKCkge1xuICAgIGlmICh0aGlzLmZyb3plbikge1xuICAgICAgdGhpcy51bmZyZWV6ZSgpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmZyZWV6ZSgpO1xuICAgIH1cbiAgfVxuXG4gIGZyZWV6ZSgpIHtcbiAgICB0aGlzLmZyb3plbiA9IHRydWU7XG4gIH1cblxuICB1bmZyZWV6ZSgpIHtcbiAgICB0aGlzLmZyb3plbiA9IGZhbHNlO1xuICAgIHRoaXMuZmx1c2hQZW5kaW5nVXBkYXRlcygpO1xuICB9XG5cbiAgZGF0YUZvclVwZGF0ZU11bHRpTWVzc2FnZShuZXR3b3JrSWQsIG1lc3NhZ2UpIHtcbiAgICAvLyBcImRcIiBpcyBhbiBhcnJheSBvZiBlbnRpdHkgZGF0YXMsIHdoZXJlIGVhY2ggaXRlbSBpbiB0aGUgYXJyYXkgcmVwcmVzZW50cyBhIHVuaXF1ZSBlbnRpdHkgYW5kIGNvbnRhaW5zXG4gICAgLy8gbWV0YWRhdGEgZm9yIHRoZSBlbnRpdHksIGFuZCBhbiBhcnJheSBvZiBjb21wb25lbnRzIHRoYXQgaGF2ZSBiZWVuIHVwZGF0ZWQgb24gdGhlIGVudGl0eS5cbiAgICAvLyBUaGlzIG1ldGhvZCBmaW5kcyB0aGUgZGF0YSBjb3JyZXNwb25kaW5nIHRvIHRoZSBnaXZlbiBuZXR3b3JrSWQuXG4gICAgZm9yIChsZXQgaSA9IDAsIGwgPSBtZXNzYWdlLmRhdGEuZC5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAgIGNvbnN0IGRhdGEgPSBtZXNzYWdlLmRhdGEuZFtpXTtcblxuICAgICAgaWYgKGRhdGEubmV0d29ya0lkID09PSBuZXR3b3JrSWQpIHtcbiAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICBnZXRQZW5kaW5nRGF0YShuZXR3b3JrSWQsIG1lc3NhZ2UpIHtcbiAgICBpZiAoIW1lc3NhZ2UpIHJldHVybiBudWxsO1xuXG4gICAgbGV0IGRhdGEgPSBtZXNzYWdlLmRhdGFUeXBlID09PSBcInVtXCIgPyB0aGlzLmRhdGFGb3JVcGRhdGVNdWx0aU1lc3NhZ2UobmV0d29ya0lkLCBtZXNzYWdlKSA6IG1lc3NhZ2UuZGF0YTtcblxuICAgIC8vIElnbm9yZSBtZXNzYWdlcyByZWxhdGluZyB0byB1c2VycyB3aG8gaGF2ZSBkaXNjb25uZWN0ZWQgc2luY2UgZnJlZXppbmcsIHRoZWlyIGVudGl0aWVzXG4gICAgLy8gd2lsbCBoYXZlIGFsZWFkeSBiZWVuIHJlbW92ZWQgYnkgTkFGLlxuICAgIC8vIE5vdGUgdGhhdCBkZWxldGUgbWVzc2FnZXMgaGF2ZSBubyBcIm93bmVyXCIgc28gd2UgaGF2ZSB0byBjaGVjayBmb3IgdGhhdCBhcyB3ZWxsLlxuICAgIGlmIChkYXRhLm93bmVyICYmICF0aGlzLm9jY3VwYW50c1tkYXRhLm93bmVyXSkgcmV0dXJuIG51bGw7XG5cbiAgICAvLyBJZ25vcmUgbWVzc2FnZXMgZnJvbSB1c2VycyB0aGF0IHdlIG1heSBoYXZlIGJsb2NrZWQgd2hpbGUgZnJvemVuLlxuICAgIGlmIChkYXRhLm93bmVyICYmIHRoaXMuYmxvY2tlZENsaWVudHMuaGFzKGRhdGEub3duZXIpKSByZXR1cm4gbnVsbDtcblxuICAgIHJldHVybiBkYXRhXG4gIH1cblxuICAvLyBVc2VkIGV4dGVybmFsbHlcbiAgZ2V0UGVuZGluZ0RhdGFGb3JOZXR3b3JrSWQobmV0d29ya0lkKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0UGVuZGluZ0RhdGEobmV0d29ya0lkLCB0aGlzLmZyb3plblVwZGF0ZXMuZ2V0KG5ldHdvcmtJZCkpO1xuICB9XG5cbiAgZmx1c2hQZW5kaW5nVXBkYXRlcygpIHtcbiAgICBmb3IgKGNvbnN0IFtuZXR3b3JrSWQsIG1lc3NhZ2VdIG9mIHRoaXMuZnJvemVuVXBkYXRlcykge1xuICAgICAgbGV0IGRhdGEgPSB0aGlzLmdldFBlbmRpbmdEYXRhKG5ldHdvcmtJZCwgbWVzc2FnZSk7XG4gICAgICBpZiAoIWRhdGEpIGNvbnRpbnVlO1xuXG4gICAgICAvLyBPdmVycmlkZSB0aGUgZGF0YSB0eXBlIG9uIFwidW1cIiBtZXNzYWdlcyB0eXBlcywgc2luY2Ugd2UgZXh0cmFjdCBlbnRpdHkgdXBkYXRlcyBmcm9tIFwidW1cIiBtZXNzYWdlcyBpbnRvXG4gICAgICAvLyBpbmRpdmlkdWFsIGZyb3plblVwZGF0ZXMgaW4gc3RvcmVTaW5nbGVNZXNzYWdlLlxuICAgICAgY29uc3QgZGF0YVR5cGUgPSBtZXNzYWdlLmRhdGFUeXBlID09PSBcInVtXCIgPyBcInVcIiA6IG1lc3NhZ2UuZGF0YVR5cGU7XG5cbiAgICAgIHRoaXMub25PY2N1cGFudE1lc3NhZ2UobnVsbCwgZGF0YVR5cGUsIGRhdGEsIG1lc3NhZ2Uuc291cmNlKTtcbiAgICB9XG4gICAgdGhpcy5mcm96ZW5VcGRhdGVzLmNsZWFyKCk7XG4gIH1cblxuICBzdG9yZU1lc3NhZ2UobWVzc2FnZSkge1xuICAgIGlmIChtZXNzYWdlLmRhdGFUeXBlID09PSBcInVtXCIpIHsgLy8gVXBkYXRlTXVsdGlcbiAgICAgIGZvciAobGV0IGkgPSAwLCBsID0gbWVzc2FnZS5kYXRhLmQubGVuZ3RoOyBpIDwgbDsgaSsrKSB7XG4gICAgICAgIHRoaXMuc3RvcmVTaW5nbGVNZXNzYWdlKG1lc3NhZ2UsIGkpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnN0b3JlU2luZ2xlTWVzc2FnZShtZXNzYWdlKTtcbiAgICB9XG4gIH1cblxuICBzdG9yZVNpbmdsZU1lc3NhZ2UobWVzc2FnZSwgaW5kZXgpIHtcbiAgICBjb25zdCBkYXRhID0gaW5kZXggIT09IHVuZGVmaW5lZCA/IG1lc3NhZ2UuZGF0YS5kW2luZGV4XSA6IG1lc3NhZ2UuZGF0YTtcbiAgICBjb25zdCBkYXRhVHlwZSA9IG1lc3NhZ2UuZGF0YVR5cGU7XG4gICAgY29uc3Qgc291cmNlID0gbWVzc2FnZS5zb3VyY2U7XG5cbiAgICBjb25zdCBuZXR3b3JrSWQgPSBkYXRhLm5ldHdvcmtJZDtcblxuICAgIGlmICghdGhpcy5mcm96ZW5VcGRhdGVzLmhhcyhuZXR3b3JrSWQpKSB7XG4gICAgICB0aGlzLmZyb3plblVwZGF0ZXMuc2V0KG5ldHdvcmtJZCwgbWVzc2FnZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IHN0b3JlZE1lc3NhZ2UgPSB0aGlzLmZyb3plblVwZGF0ZXMuZ2V0KG5ldHdvcmtJZCk7XG4gICAgICBjb25zdCBzdG9yZWREYXRhID0gc3RvcmVkTWVzc2FnZS5kYXRhVHlwZSA9PT0gXCJ1bVwiID8gdGhpcy5kYXRhRm9yVXBkYXRlTXVsdGlNZXNzYWdlKG5ldHdvcmtJZCwgc3RvcmVkTWVzc2FnZSkgOiBzdG9yZWRNZXNzYWdlLmRhdGE7XG5cbiAgICAgIC8vIEF2b2lkIHVwZGF0aW5nIGNvbXBvbmVudHMgaWYgdGhlIGVudGl0eSBkYXRhIHJlY2VpdmVkIGRpZCBub3QgY29tZSBmcm9tIHRoZSBjdXJyZW50IG93bmVyLlxuICAgICAgY29uc3QgaXNPdXRkYXRlZE1lc3NhZ2UgPSBkYXRhLmxhc3RPd25lclRpbWUgPCBzdG9yZWREYXRhLmxhc3RPd25lclRpbWU7XG4gICAgICBjb25zdCBpc0NvbnRlbXBvcmFuZW91c01lc3NhZ2UgPSBkYXRhLmxhc3RPd25lclRpbWUgPT09IHN0b3JlZERhdGEubGFzdE93bmVyVGltZTtcbiAgICAgIGlmIChpc091dGRhdGVkTWVzc2FnZSB8fCAoaXNDb250ZW1wb3JhbmVvdXNNZXNzYWdlICYmIHN0b3JlZERhdGEub3duZXIgPiBkYXRhLm93bmVyKSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGlmIChkYXRhVHlwZSA9PT0gXCJyXCIpIHtcbiAgICAgICAgY29uc3QgY3JlYXRlZFdoaWxlRnJvemVuID0gc3RvcmVkRGF0YSAmJiBzdG9yZWREYXRhLmlzRmlyc3RTeW5jO1xuICAgICAgICBpZiAoY3JlYXRlZFdoaWxlRnJvemVuKSB7XG4gICAgICAgICAgLy8gSWYgdGhlIGVudGl0eSB3YXMgY3JlYXRlZCBhbmQgZGVsZXRlZCB3aGlsZSBmcm96ZW4sIGRvbid0IGJvdGhlciBjb252ZXlpbmcgYW55dGhpbmcgdG8gdGhlIGNvbnN1bWVyLlxuICAgICAgICAgIHRoaXMuZnJvemVuVXBkYXRlcy5kZWxldGUobmV0d29ya0lkKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBEZWxldGUgbWVzc2FnZXMgb3ZlcnJpZGUgYW55IG90aGVyIG1lc3NhZ2VzIGZvciB0aGlzIGVudGl0eVxuICAgICAgICAgIHRoaXMuZnJvemVuVXBkYXRlcy5zZXQobmV0d29ya0lkLCBtZXNzYWdlKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gbWVyZ2UgaW4gY29tcG9uZW50IHVwZGF0ZXNcbiAgICAgICAgaWYgKHN0b3JlZERhdGEuY29tcG9uZW50cyAmJiBkYXRhLmNvbXBvbmVudHMpIHtcbiAgICAgICAgICBPYmplY3QuYXNzaWduKHN0b3JlZERhdGEuY29tcG9uZW50cywgZGF0YS5jb21wb25lbnRzKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIG9uRGF0YUNoYW5uZWxNZXNzYWdlKGUsIHNvdXJjZSkge1xuICAgIHRoaXMub25EYXRhKEpTT04ucGFyc2UoZS5kYXRhKSwgc291cmNlKTtcbiAgfVxuXG4gIG9uRGF0YShtZXNzYWdlLCBzb3VyY2UpIHtcbiAgICBpZiAoZGVidWcuZW5hYmxlZCkge1xuICAgICAgZGVidWcoYERDIGluOiAke21lc3NhZ2V9YCk7XG4gICAgfVxuXG4gICAgaWYgKCFtZXNzYWdlLmRhdGFUeXBlKSByZXR1cm47XG5cbiAgICBtZXNzYWdlLnNvdXJjZSA9IHNvdXJjZTtcblxuICAgIGlmICh0aGlzLmZyb3plbikge1xuICAgICAgdGhpcy5zdG9yZU1lc3NhZ2UobWVzc2FnZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMub25PY2N1cGFudE1lc3NhZ2UobnVsbCwgbWVzc2FnZS5kYXRhVHlwZSwgbWVzc2FnZS5kYXRhLCBtZXNzYWdlLnNvdXJjZSk7XG4gICAgfVxuICB9XG5cbiAgc2hvdWxkU3RhcnRDb25uZWN0aW9uVG8oY2xpZW50KSB7XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICBzdGFydFN0cmVhbUNvbm5lY3Rpb24oY2xpZW50KSB7fVxuXG4gIGNsb3NlU3RyZWFtQ29ubmVjdGlvbihjbGllbnQpIHt9XG5cbiAgZ2V0Q29ubmVjdFN0YXR1cyhjbGllbnRJZCkge1xuICAgIHJldHVybiB0aGlzLm9jY3VwYW50c1tjbGllbnRJZF0gPyBOQUYuYWRhcHRlcnMuSVNfQ09OTkVDVEVEIDogTkFGLmFkYXB0ZXJzLk5PVF9DT05ORUNURUQ7XG4gIH1cblxuICBhc3luYyB1cGRhdGVUaW1lT2Zmc2V0KCkge1xuICAgIGlmICh0aGlzLmlzRGlzY29ubmVjdGVkKCkpIHJldHVybjtcblxuICAgIGNvbnN0IGNsaWVudFNlbnRUaW1lID0gRGF0ZS5ub3coKTtcblxuICAgIGNvbnN0IHJlcyA9IGF3YWl0IGZldGNoKGRvY3VtZW50LmxvY2F0aW9uLmhyZWYsIHtcbiAgICAgIG1ldGhvZDogXCJIRUFEXCIsXG4gICAgICBjYWNoZTogXCJuby1jYWNoZVwiXG4gICAgfSk7XG5cbiAgICBjb25zdCBwcmVjaXNpb24gPSAxMDAwO1xuICAgIGNvbnN0IHNlcnZlclJlY2VpdmVkVGltZSA9IG5ldyBEYXRlKHJlcy5oZWFkZXJzLmdldChcIkRhdGVcIikpLmdldFRpbWUoKSArIHByZWNpc2lvbiAvIDI7XG4gICAgY29uc3QgY2xpZW50UmVjZWl2ZWRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICBjb25zdCBzZXJ2ZXJUaW1lID0gc2VydmVyUmVjZWl2ZWRUaW1lICsgKGNsaWVudFJlY2VpdmVkVGltZSAtIGNsaWVudFNlbnRUaW1lKSAvIDI7XG4gICAgY29uc3QgdGltZU9mZnNldCA9IHNlcnZlclRpbWUgLSBjbGllbnRSZWNlaXZlZFRpbWU7XG5cbiAgICB0aGlzLnNlcnZlclRpbWVSZXF1ZXN0cysrO1xuXG4gICAgaWYgKHRoaXMuc2VydmVyVGltZVJlcXVlc3RzIDw9IDEwKSB7XG4gICAgICB0aGlzLnRpbWVPZmZzZXRzLnB1c2godGltZU9mZnNldCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMudGltZU9mZnNldHNbdGhpcy5zZXJ2ZXJUaW1lUmVxdWVzdHMgJSAxMF0gPSB0aW1lT2Zmc2V0O1xuICAgIH1cblxuICAgIHRoaXMuYXZnVGltZU9mZnNldCA9IHRoaXMudGltZU9mZnNldHMucmVkdWNlKChhY2MsIG9mZnNldCkgPT4gKGFjYyArPSBvZmZzZXQpLCAwKSAvIHRoaXMudGltZU9mZnNldHMubGVuZ3RoO1xuXG4gICAgaWYgKHRoaXMuc2VydmVyVGltZVJlcXVlc3RzID4gMTApIHtcbiAgICAgIGRlYnVnKGBuZXcgc2VydmVyIHRpbWUgb2Zmc2V0OiAke3RoaXMuYXZnVGltZU9mZnNldH1tc2ApO1xuICAgICAgc2V0VGltZW91dCgoKSA9PiB0aGlzLnVwZGF0ZVRpbWVPZmZzZXQoKSwgNSAqIDYwICogMTAwMCk7IC8vIFN5bmMgY2xvY2sgZXZlcnkgNSBtaW51dGVzLlxuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnVwZGF0ZVRpbWVPZmZzZXQoKTtcbiAgICB9XG4gIH1cblxuICBnZXRTZXJ2ZXJUaW1lKCkge1xuICAgIHJldHVybiBEYXRlLm5vdygpICsgdGhpcy5hdmdUaW1lT2Zmc2V0O1xuICB9XG5cbiAgZ2V0TWVkaWFTdHJlYW0oY2xpZW50SWQsIHR5cGUgPSBcImF1ZGlvXCIpIHtcbiAgICBpZiAodGhpcy5tZWRpYVN0cmVhbXNbY2xpZW50SWRdKSB7XG4gICAgICBkZWJ1ZyhgQWxyZWFkeSBoYWQgJHt0eXBlfSBmb3IgJHtjbGllbnRJZH1gKTtcbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUodGhpcy5tZWRpYVN0cmVhbXNbY2xpZW50SWRdW3R5cGVdKTtcbiAgICB9IGVsc2Uge1xuICAgICAgZGVidWcoYFdhaXRpbmcgb24gJHt0eXBlfSBmb3IgJHtjbGllbnRJZH1gKTtcbiAgICAgIGlmICghdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5oYXMoY2xpZW50SWQpKSB7XG4gICAgICAgIHRoaXMucGVuZGluZ01lZGlhUmVxdWVzdHMuc2V0KGNsaWVudElkLCB7fSk7XG5cbiAgICAgICAgY29uc3QgYXVkaW9Qcm9taXNlID0gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgIHRoaXMucGVuZGluZ01lZGlhUmVxdWVzdHMuZ2V0KGNsaWVudElkKS5hdWRpbyA9IHsgcmVzb2x2ZSwgcmVqZWN0IH07XG4gICAgICAgIH0pO1xuICAgICAgICBjb25zdCB2aWRlb1Byb21pc2UgPSBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5nZXQoY2xpZW50SWQpLnZpZGVvID0geyByZXNvbHZlLCByZWplY3QgfTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5nZXQoY2xpZW50SWQpLmF1ZGlvLnByb21pc2UgPSBhdWRpb1Byb21pc2U7XG4gICAgICAgIHRoaXMucGVuZGluZ01lZGlhUmVxdWVzdHMuZ2V0KGNsaWVudElkKS52aWRlby5wcm9taXNlID0gdmlkZW9Qcm9taXNlO1xuXG4gICAgICAgIGF1ZGlvUHJvbWlzZS5jYXRjaChlID0+IGNvbnNvbGUud2FybihgJHtjbGllbnRJZH0gZ2V0TWVkaWFTdHJlYW0gQXVkaW8gRXJyb3JgLCBlKSk7XG4gICAgICAgIHZpZGVvUHJvbWlzZS5jYXRjaChlID0+IGNvbnNvbGUud2FybihgJHtjbGllbnRJZH0gZ2V0TWVkaWFTdHJlYW0gVmlkZW8gRXJyb3JgLCBlKSk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5nZXQoY2xpZW50SWQpW3R5cGVdLnByb21pc2U7XG4gICAgfVxuICB9XG5cbiAgc2V0TWVkaWFTdHJlYW0oY2xpZW50SWQsIHN0cmVhbSkge1xuICAgIC8vIFNhZmFyaSBkb2Vzbid0IGxpa2UgaXQgd2hlbiB5b3UgdXNlIHNpbmdsZSBhIG1peGVkIG1lZGlhIHN0cmVhbSB3aGVyZSBvbmUgb2YgdGhlIHRyYWNrcyBpcyBpbmFjdGl2ZSwgc28gd2VcbiAgICAvLyBzcGxpdCB0aGUgdHJhY2tzIGludG8gdHdvIHN0cmVhbXMuXG4gICAgY29uc3QgYXVkaW9TdHJlYW0gPSBuZXcgTWVkaWFTdHJlYW0oKTtcbiAgICB0cnkge1xuICAgIHN0cmVhbS5nZXRBdWRpb1RyYWNrcygpLmZvckVhY2godHJhY2sgPT4gYXVkaW9TdHJlYW0uYWRkVHJhY2sodHJhY2spKTtcblxuICAgIH0gY2F0Y2goZSkge1xuICAgICAgY29uc29sZS53YXJuKGAke2NsaWVudElkfSBzZXRNZWRpYVN0cmVhbSBBdWRpbyBFcnJvcmAsIGUpO1xuICAgIH1cbiAgICBjb25zdCB2aWRlb1N0cmVhbSA9IG5ldyBNZWRpYVN0cmVhbSgpO1xuICAgIHRyeSB7XG4gICAgc3RyZWFtLmdldFZpZGVvVHJhY2tzKCkuZm9yRWFjaCh0cmFjayA9PiB2aWRlb1N0cmVhbS5hZGRUcmFjayh0cmFjaykpO1xuXG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgY29uc29sZS53YXJuKGAke2NsaWVudElkfSBzZXRNZWRpYVN0cmVhbSBWaWRlbyBFcnJvcmAsIGUpO1xuICAgIH1cblxuICAgIHRoaXMubWVkaWFTdHJlYW1zW2NsaWVudElkXSA9IHsgYXVkaW86IGF1ZGlvU3RyZWFtLCB2aWRlbzogdmlkZW9TdHJlYW0gfTtcblxuICAgIC8vIFJlc29sdmUgdGhlIHByb21pc2UgZm9yIHRoZSB1c2VyJ3MgbWVkaWEgc3RyZWFtIGlmIGl0IGV4aXN0cy5cbiAgICBpZiAodGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5oYXMoY2xpZW50SWQpKSB7XG4gICAgICB0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmdldChjbGllbnRJZCkuYXVkaW8ucmVzb2x2ZShhdWRpb1N0cmVhbSk7XG4gICAgICB0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmdldChjbGllbnRJZCkudmlkZW8ucmVzb2x2ZSh2aWRlb1N0cmVhbSk7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgc2V0TG9jYWxNZWRpYVN0cmVhbShzdHJlYW0pIHtcbiAgICAvLyBvdXIgam9iIGhlcmUgaXMgdG8gbWFrZSBzdXJlIHRoZSBjb25uZWN0aW9uIHdpbmRzIHVwIHdpdGggUlRQIHNlbmRlcnMgc2VuZGluZyB0aGUgc3R1ZmYgaW4gdGhpcyBzdHJlYW0sXG4gICAgLy8gYW5kIG5vdCB0aGUgc3R1ZmYgdGhhdCBpc24ndCBpbiB0aGlzIHN0cmVhbS4gc3RyYXRlZ3kgaXMgdG8gcmVwbGFjZSBleGlzdGluZyB0cmFja3MgaWYgd2UgY2FuLCBhZGQgdHJhY2tzXG4gICAgLy8gdGhhdCB3ZSBjYW4ndCByZXBsYWNlLCBhbmQgZGlzYWJsZSB0cmFja3MgdGhhdCBkb24ndCBleGlzdCBhbnltb3JlLlxuXG4gICAgLy8gbm90ZSB0aGF0IHdlIGRvbid0IGV2ZXIgcmVtb3ZlIGEgdHJhY2sgZnJvbSB0aGUgc3RyZWFtIC0tIHNpbmNlIEphbnVzIGRvZXNuJ3Qgc3VwcG9ydCBVbmlmaWVkIFBsYW4sIHdlIGFic29sdXRlbHlcbiAgICAvLyBjYW4ndCB3aW5kIHVwIHdpdGggYSBTRFAgdGhhdCBoYXMgPjEgYXVkaW8gb3IgPjEgdmlkZW8gdHJhY2tzLCBldmVuIGlmIG9uZSBvZiB0aGVtIGlzIGluYWN0aXZlICh3aGF0IHlvdSBnZXQgaWZcbiAgICAvLyB5b3UgcmVtb3ZlIGEgdHJhY2sgZnJvbSBhbiBleGlzdGluZyBzdHJlYW0uKVxuICAgIGlmICh0aGlzLnB1Ymxpc2hlciAmJiB0aGlzLnB1Ymxpc2hlci5jb25uKSB7XG4gICAgICBjb25zdCBleGlzdGluZ1NlbmRlcnMgPSB0aGlzLnB1Ymxpc2hlci5jb25uLmdldFNlbmRlcnMoKTtcbiAgICAgIGNvbnN0IG5ld1NlbmRlcnMgPSBbXTtcbiAgICAgIGNvbnN0IHRyYWNrcyA9IHN0cmVhbS5nZXRUcmFja3MoKTtcblxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0cmFja3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgY29uc3QgdCA9IHRyYWNrc1tpXTtcbiAgICAgICAgY29uc3Qgc2VuZGVyID0gZXhpc3RpbmdTZW5kZXJzLmZpbmQocyA9PiBzLnRyYWNrICE9IG51bGwgJiYgcy50cmFjay5raW5kID09IHQua2luZCk7XG5cbiAgICAgICAgaWYgKHNlbmRlciAhPSBudWxsKSB7XG4gICAgICAgICAgaWYgKHNlbmRlci5yZXBsYWNlVHJhY2spIHtcbiAgICAgICAgICAgIGF3YWl0IHNlbmRlci5yZXBsYWNlVHJhY2sodCk7XG5cbiAgICAgICAgICAgIC8vIFdvcmthcm91bmQgaHR0cHM6Ly9idWd6aWxsYS5tb3ppbGxhLm9yZy9zaG93X2J1Zy5jZ2k/aWQ9MTU3Njc3MVxuICAgICAgICAgICAgaWYgKHQua2luZCA9PT0gXCJ2aWRlb1wiICYmIHQuZW5hYmxlZCAmJiBuYXZpZ2F0b3IudXNlckFnZW50LnRvTG93ZXJDYXNlKCkuaW5kZXhPZignZmlyZWZveCcpID4gLTEpIHtcbiAgICAgICAgICAgICAgdC5lbmFibGVkID0gZmFsc2U7XG4gICAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4gdC5lbmFibGVkID0gdHJ1ZSwgMTAwMCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIEZhbGxiYWNrIGZvciBicm93c2VycyB0aGF0IGRvbid0IHN1cHBvcnQgcmVwbGFjZVRyYWNrLiBBdCB0aGlzIHRpbWUgb2YgdGhpcyB3cml0aW5nXG4gICAgICAgICAgICAvLyBtb3N0IGJyb3dzZXJzIHN1cHBvcnQgaXQsIGFuZCB0ZXN0aW5nIHRoaXMgY29kZSBwYXRoIHNlZW1zIHRvIG5vdCB3b3JrIHByb3Blcmx5XG4gICAgICAgICAgICAvLyBpbiBDaHJvbWUgYW55bW9yZS5cbiAgICAgICAgICAgIHN0cmVhbS5yZW1vdmVUcmFjayhzZW5kZXIudHJhY2spO1xuICAgICAgICAgICAgc3RyZWFtLmFkZFRyYWNrKHQpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBuZXdTZW5kZXJzLnB1c2goc2VuZGVyKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBuZXdTZW5kZXJzLnB1c2godGhpcy5wdWJsaXNoZXIuY29ubi5hZGRUcmFjayh0LCBzdHJlYW0pKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZXhpc3RpbmdTZW5kZXJzLmZvckVhY2gocyA9PiB7XG4gICAgICAgIGlmICghbmV3U2VuZGVycy5pbmNsdWRlcyhzKSkge1xuICAgICAgICAgIHMudHJhY2suZW5hYmxlZCA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG4gICAgdGhpcy5sb2NhbE1lZGlhU3RyZWFtID0gc3RyZWFtO1xuICAgIHRoaXMuc2V0TWVkaWFTdHJlYW0odGhpcy5jbGllbnRJZCwgc3RyZWFtKTtcbiAgfVxuXG4gIGVuYWJsZU1pY3JvcGhvbmUoZW5hYmxlZCkge1xuICAgIGlmICh0aGlzLnB1Ymxpc2hlciAmJiB0aGlzLnB1Ymxpc2hlci5jb25uKSB7XG4gICAgICB0aGlzLnB1Ymxpc2hlci5jb25uLmdldFNlbmRlcnMoKS5mb3JFYWNoKHMgPT4ge1xuICAgICAgICBpZiAocy50cmFjay5raW5kID09IFwiYXVkaW9cIikge1xuICAgICAgICAgIHMudHJhY2suZW5hYmxlZCA9IGVuYWJsZWQ7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIHNlbmREYXRhKGNsaWVudElkLCBkYXRhVHlwZSwgZGF0YSkge1xuICAgIGlmICghdGhpcy5wdWJsaXNoZXIpIHtcbiAgICAgIGNvbnNvbGUud2FybihcInNlbmREYXRhIGNhbGxlZCB3aXRob3V0IGEgcHVibGlzaGVyXCIpO1xuICAgIH0gZWxzZSB7XG4gICAgICBzd2l0Y2ggKHRoaXMudW5yZWxpYWJsZVRyYW5zcG9ydCkge1xuICAgICAgICBjYXNlIFwid2Vic29ja2V0XCI6XG4gICAgICAgICAgdGhpcy5wdWJsaXNoZXIuaGFuZGxlLnNlbmRNZXNzYWdlKHsga2luZDogXCJkYXRhXCIsIGJvZHk6IEpTT04uc3RyaW5naWZ5KHsgZGF0YVR5cGUsIGRhdGEgfSksIHdob206IGNsaWVudElkIH0pO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwiZGF0YWNoYW5uZWxcIjpcbiAgICAgICAgICB0aGlzLnB1Ymxpc2hlci51bnJlbGlhYmxlQ2hhbm5lbC5zZW5kKEpTT04uc3RyaW5naWZ5KHsgY2xpZW50SWQsIGRhdGFUeXBlLCBkYXRhIH0pKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICB0aGlzLnVucmVsaWFibGVUcmFuc3BvcnQoY2xpZW50SWQsIGRhdGFUeXBlLCBkYXRhKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBzZW5kRGF0YUd1YXJhbnRlZWQoY2xpZW50SWQsIGRhdGFUeXBlLCBkYXRhKSB7XG4gICAgaWYgKCF0aGlzLnB1Ymxpc2hlcikge1xuICAgICAgY29uc29sZS53YXJuKFwic2VuZERhdGFHdWFyYW50ZWVkIGNhbGxlZCB3aXRob3V0IGEgcHVibGlzaGVyXCIpO1xuICAgIH0gZWxzZSB7XG4gICAgICBzd2l0Y2ggKHRoaXMucmVsaWFibGVUcmFuc3BvcnQpIHtcbiAgICAgICAgY2FzZSBcIndlYnNvY2tldFwiOlxuICAgICAgICAgIHRoaXMucHVibGlzaGVyLmhhbmRsZS5zZW5kTWVzc2FnZSh7IGtpbmQ6IFwiZGF0YVwiLCBib2R5OiBKU09OLnN0cmluZ2lmeSh7IGRhdGFUeXBlLCBkYXRhIH0pLCB3aG9tOiBjbGllbnRJZCB9KTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBcImRhdGFjaGFubmVsXCI6XG4gICAgICAgICAgdGhpcy5wdWJsaXNoZXIucmVsaWFibGVDaGFubmVsLnNlbmQoSlNPTi5zdHJpbmdpZnkoeyBjbGllbnRJZCwgZGF0YVR5cGUsIGRhdGEgfSkpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIHRoaXMucmVsaWFibGVUcmFuc3BvcnQoY2xpZW50SWQsIGRhdGFUeXBlLCBkYXRhKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBicm9hZGNhc3REYXRhKGRhdGFUeXBlLCBkYXRhKSB7XG4gICAgaWYgKCF0aGlzLnB1Ymxpc2hlcikge1xuICAgICAgY29uc29sZS53YXJuKFwiYnJvYWRjYXN0RGF0YSBjYWxsZWQgd2l0aG91dCBhIHB1Ymxpc2hlclwiKTtcbiAgICB9IGVsc2Uge1xuICAgICAgc3dpdGNoICh0aGlzLnVucmVsaWFibGVUcmFuc3BvcnQpIHtcbiAgICAgICAgY2FzZSBcIndlYnNvY2tldFwiOlxuICAgICAgICAgIHRoaXMucHVibGlzaGVyLmhhbmRsZS5zZW5kTWVzc2FnZSh7IGtpbmQ6IFwiZGF0YVwiLCBib2R5OiBKU09OLnN0cmluZ2lmeSh7IGRhdGFUeXBlLCBkYXRhIH0pIH0pO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwiZGF0YWNoYW5uZWxcIjpcbiAgICAgICAgICB0aGlzLnB1Ymxpc2hlci51bnJlbGlhYmxlQ2hhbm5lbC5zZW5kKEpTT04uc3RyaW5naWZ5KHsgZGF0YVR5cGUsIGRhdGEgfSkpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIHRoaXMudW5yZWxpYWJsZVRyYW5zcG9ydCh1bmRlZmluZWQsIGRhdGFUeXBlLCBkYXRhKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBicm9hZGNhc3REYXRhR3VhcmFudGVlZChkYXRhVHlwZSwgZGF0YSkge1xuICAgIGlmICghdGhpcy5wdWJsaXNoZXIpIHtcbiAgICAgIGNvbnNvbGUud2FybihcImJyb2FkY2FzdERhdGFHdWFyYW50ZWVkIGNhbGxlZCB3aXRob3V0IGEgcHVibGlzaGVyXCIpO1xuICAgIH0gZWxzZSB7XG4gICAgICBzd2l0Y2ggKHRoaXMucmVsaWFibGVUcmFuc3BvcnQpIHtcbiAgICAgICAgY2FzZSBcIndlYnNvY2tldFwiOlxuICAgICAgICAgIHRoaXMucHVibGlzaGVyLmhhbmRsZS5zZW5kTWVzc2FnZSh7IGtpbmQ6IFwiZGF0YVwiLCBib2R5OiBKU09OLnN0cmluZ2lmeSh7IGRhdGFUeXBlLCBkYXRhIH0pIH0pO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwiZGF0YWNoYW5uZWxcIjpcbiAgICAgICAgICB0aGlzLnB1Ymxpc2hlci5yZWxpYWJsZUNoYW5uZWwuc2VuZChKU09OLnN0cmluZ2lmeSh7IGRhdGFUeXBlLCBkYXRhIH0pKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICB0aGlzLnJlbGlhYmxlVHJhbnNwb3J0KHVuZGVmaW5lZCwgZGF0YVR5cGUsIGRhdGEpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGtpY2soY2xpZW50SWQsIHBlcm1zVG9rZW4pIHtcbiAgICByZXR1cm4gdGhpcy5wdWJsaXNoZXIuaGFuZGxlLnNlbmRNZXNzYWdlKHsga2luZDogXCJraWNrXCIsIHJvb21faWQ6IHRoaXMucm9vbSwgdXNlcl9pZDogY2xpZW50SWQsIHRva2VuOiBwZXJtc1Rva2VuIH0pLnRoZW4oKCkgPT4ge1xuICAgICAgZG9jdW1lbnQuYm9keS5kaXNwYXRjaEV2ZW50KG5ldyBDdXN0b21FdmVudChcImtpY2tlZFwiLCB7IGRldGFpbDogeyBjbGllbnRJZDogY2xpZW50SWQgfSB9KSk7XG4gICAgfSk7XG4gIH1cblxuICBibG9jayhjbGllbnRJZCkge1xuICAgIHJldHVybiB0aGlzLnB1Ymxpc2hlci5oYW5kbGUuc2VuZE1lc3NhZ2UoeyBraW5kOiBcImJsb2NrXCIsIHdob206IGNsaWVudElkIH0pLnRoZW4oKCkgPT4ge1xuICAgICAgdGhpcy5ibG9ja2VkQ2xpZW50cy5zZXQoY2xpZW50SWQsIHRydWUpO1xuICAgICAgZG9jdW1lbnQuYm9keS5kaXNwYXRjaEV2ZW50KG5ldyBDdXN0b21FdmVudChcImJsb2NrZWRcIiwgeyBkZXRhaWw6IHsgY2xpZW50SWQ6IGNsaWVudElkIH0gfSkpO1xuICAgIH0pO1xuICB9XG5cbiAgdW5ibG9jayhjbGllbnRJZCkge1xuICAgIHJldHVybiB0aGlzLnB1Ymxpc2hlci5oYW5kbGUuc2VuZE1lc3NhZ2UoeyBraW5kOiBcInVuYmxvY2tcIiwgd2hvbTogY2xpZW50SWQgfSkudGhlbigoKSA9PiB7XG4gICAgICB0aGlzLmJsb2NrZWRDbGllbnRzLmRlbGV0ZShjbGllbnRJZCk7XG4gICAgICBkb2N1bWVudC5ib2R5LmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KFwidW5ibG9ja2VkXCIsIHsgZGV0YWlsOiB7IGNsaWVudElkOiBjbGllbnRJZCB9IH0pKTtcbiAgICB9KTtcbiAgfVxufVxuXG5OQUYuYWRhcHRlcnMucmVnaXN0ZXIoXCJqYW51c1wiLCBKYW51c0FkYXB0ZXIpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IEphbnVzQWRhcHRlcjtcbiJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file +/******/ }); +/************************************************************************/ +/******/ // 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; +/******/ } +/******/ +/************************************************************************/ +/******/ +/******/ // startup +/******/ // Load entry module and return exports +/******/ // This entry module is referenced by other modules so it can't be inlined +/******/ var __webpack_exports__ = __webpack_require__("./src/index.js"); +/******/ +/******/ })() +; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,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Q0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsUUFBUTtBQUNSO0FBQ0E7QUFDQSxLQUFLO0FBQ0wsaURBQWlEO0FBQ2pEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtCQUFrQixrQkFBa0IsT0FBTztBQUMzQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTCxHQUFHO0FBQ0g7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSw2Q0FBNkM7QUFDN0M7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx3QkFBd0I7QUFDeEI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsWUFBWTtBQUNaO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFlBQVk7QUFDWjtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0JBQWtCLGtCQUFrQjtBQUNwQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtCQUFrQixrQkFBa0I7QUFDcEM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxJQUFJLElBQTBCO0FBQzlCO0FBQ0E7Ozs7Ozs7VUNqeUJBO1VBQ0E7O1VBRUE7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7O1VBRUE7VUFDQTs7VUFFQTtVQUNBO1VBQ0E7Ozs7VUV0QkE7VUFDQTtVQUNBO1VBQ0EiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9AbmV0d29ya2VkLWFmcmFtZS9uYWYtamFudXMtYWRhcHRlci8uL25vZGVfbW9kdWxlcy9AbmV0d29ya2VkLWFmcmFtZS9taW5pamFudXMvbWluaWphbnVzLmpzIiwid2VicGFjazovL0BuZXR3b3JrZWQtYWZyYW1lL25hZi1qYW51cy1hZGFwdGVyLy4vc3JjL2luZGV4LmpzIiwid2VicGFjazovL0BuZXR3b3JrZWQtYWZyYW1lL25hZi1qYW51cy1hZGFwdGVyLy4vbm9kZV9tb2R1bGVzL3NkcC9zZHAuanMiLCJ3ZWJwYWNrOi8vQG5ldHdvcmtlZC1hZnJhbWUvbmFmLWphbnVzLWFkYXB0ZXIvd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vQG5ldHdvcmtlZC1hZnJhbWUvbmFmLWphbnVzLWFkYXB0ZXIvd2VicGFjay9iZWZvcmUtc3RhcnR1cCIsIndlYnBhY2s6Ly9AbmV0d29ya2VkLWFmcmFtZS9uYWYtamFudXMtYWRhcHRlci93ZWJwYWNrL3N0YXJ0dXAiLCJ3ZWJwYWNrOi8vQG5ldHdvcmtlZC1hZnJhbWUvbmFmLWphbnVzLWFkYXB0ZXIvd2VicGFjay9hZnRlci1zdGFydHVwIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUmVwcmVzZW50cyBhIGhhbmRsZSB0byBhIHNpbmdsZSBKYW51cyBwbHVnaW4gb24gYSBKYW51cyBzZXNzaW9uLiBFYWNoIFdlYlJUQyBjb25uZWN0aW9uIHRvIHRoZSBKYW51cyBzZXJ2ZXIgd2lsbCBiZVxuICogYXNzb2NpYXRlZCB3aXRoIGEgc2luZ2xlIGhhbmRsZS4gT25jZSBhdHRhY2hlZCB0byB0aGUgc2VydmVyLCB0aGlzIGhhbmRsZSB3aWxsIGJlIGdpdmVuIGEgdW5pcXVlIElEIHdoaWNoIHNob3VsZCBiZVxuICogdXNlZCB0byBhc3NvY2lhdGUgaXQgd2l0aCBmdXR1cmUgc2lnbmFsbGluZyBtZXNzYWdlcy5cbiAqXG4gKiBTZWUgaHR0cHM6Ly9qYW51cy5jb25mLm1lZXRlY2hvLmNvbS9kb2NzL3Jlc3QuaHRtbCNoYW5kbGVzLlxuICoqL1xuZnVuY3Rpb24gSmFudXNQbHVnaW5IYW5kbGUoc2Vzc2lvbikge1xuICB0aGlzLnNlc3Npb24gPSBzZXNzaW9uO1xuICB0aGlzLmlkID0gdW5kZWZpbmVkO1xufVxuXG4vKiogQXR0YWNoZXMgdGhpcyBoYW5kbGUgdG8gdGhlIEphbnVzIHNlcnZlciBhbmQgc2V0cyBpdHMgSUQuICoqL1xuSmFudXNQbHVnaW5IYW5kbGUucHJvdG90eXBlLmF0dGFjaCA9IGZ1bmN0aW9uKHBsdWdpbiwgbG9vcF9pbmRleCkge1xuICB2YXIgcGF5bG9hZCA9IHsgcGx1Z2luOiBwbHVnaW4sIGxvb3BfaW5kZXg6IGxvb3BfaW5kZXgsIFwiZm9yY2UtYnVuZGxlXCI6IHRydWUsIFwiZm9yY2UtcnRjcC1tdXhcIjogdHJ1ZSB9O1xuICByZXR1cm4gdGhpcy5zZXNzaW9uLnNlbmQoXCJhdHRhY2hcIiwgcGF5bG9hZCkudGhlbihyZXNwID0+IHtcbiAgICB0aGlzLmlkID0gcmVzcC5kYXRhLmlkO1xuICAgIHJldHVybiByZXNwO1xuICB9KTtcbn07XG5cbi8qKiBEZXRhY2hlcyB0aGlzIGhhbmRsZS4gKiovXG5KYW51c1BsdWdpbkhhbmRsZS5wcm90b3R5cGUuZGV0YWNoID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiB0aGlzLnNlbmQoXCJkZXRhY2hcIik7XG59O1xuXG4vKiogUmVnaXN0ZXJzIGEgY2FsbGJhY2sgdG8gYmUgZmlyZWQgdXBvbiB0aGUgcmVjZXB0aW9uIG9mIGFueSBpbmNvbWluZyBKYW51cyBzaWduYWxzIGZvciB0aGlzIHBsdWdpbiBoYW5kbGUgd2l0aCB0aGVcbiAqIGBqYW51c2AgYXR0cmlidXRlIGVxdWFsIHRvIGBldmAuXG4gKiovXG5KYW51c1BsdWdpbkhhbmRsZS5wcm90b3R5cGUub24gPSBmdW5jdGlvbihldiwgY2FsbGJhY2spIHtcbiAgcmV0dXJuIHRoaXMuc2Vzc2lvbi5vbihldiwgc2lnbmFsID0+IHtcbiAgICBpZiAoc2lnbmFsLnNlbmRlciA9PSB0aGlzLmlkKSB7XG4gICAgICBjYWxsYmFjayhzaWduYWwpO1xuICAgIH1cbiAgfSk7XG59O1xuXG4vKipcbiAqIFNlbmRzIGEgc2lnbmFsIGFzc29jaWF0ZWQgd2l0aCB0aGlzIGhhbmRsZS4gU2lnbmFscyBzaG91bGQgYmUgSlNPTi1zZXJpYWxpemFibGUgb2JqZWN0cy4gUmV0dXJucyBhIHByb21pc2UgdGhhdCB3aWxsXG4gKiBiZSByZXNvbHZlZCBvciByZWplY3RlZCB3aGVuIGEgcmVzcG9uc2UgdG8gdGhpcyBzaWduYWwgaXMgcmVjZWl2ZWQsIG9yIHdoZW4gbm8gcmVzcG9uc2UgaXMgcmVjZWl2ZWQgd2l0aGluIHRoZVxuICogc2Vzc2lvbiB0aW1lb3V0LlxuICoqL1xuSmFudXNQbHVnaW5IYW5kbGUucHJvdG90eXBlLnNlbmQgPSBmdW5jdGlvbih0eXBlLCBzaWduYWwpIHtcbiAgcmV0dXJuIHRoaXMuc2Vzc2lvbi5zZW5kKHR5cGUsIE9iamVjdC5hc3NpZ24oeyBoYW5kbGVfaWQ6IHRoaXMuaWQgfSwgc2lnbmFsKSk7XG59O1xuXG4vKiogU2VuZHMgYSBwbHVnaW4tc3BlY2lmaWMgbWVzc2FnZSBhc3NvY2lhdGVkIHdpdGggdGhpcyBoYW5kbGUuICoqL1xuSmFudXNQbHVnaW5IYW5kbGUucHJvdG90eXBlLnNlbmRNZXNzYWdlID0gZnVuY3Rpb24oYm9keSkge1xuICByZXR1cm4gdGhpcy5zZW5kKFwibWVzc2FnZVwiLCB7IGJvZHk6IGJvZHkgfSk7XG59O1xuXG4vKiogU2VuZHMgYSBKU0VQIG9mZmVyIG9yIGFuc3dlciBhc3NvY2lhdGVkIHdpdGggdGhpcyBoYW5kbGUuICoqL1xuSmFudXNQbHVnaW5IYW5kbGUucHJvdG90eXBlLnNlbmRKc2VwID0gZnVuY3Rpb24oanNlcCkge1xuICByZXR1cm4gdGhpcy5zZW5kKFwibWVzc2FnZVwiLCB7IGJvZHk6IHt9LCBqc2VwOiBqc2VwIH0pO1xufTtcblxuLyoqIFNlbmRzIGFuIElDRSB0cmlja2xlIGNhbmRpZGF0ZSBhc3NvY2lhdGVkIHdpdGggdGhpcyBoYW5kbGUuICoqL1xuSmFudXNQbHVnaW5IYW5kbGUucHJvdG90eXBlLnNlbmRUcmlja2xlID0gZnVuY3Rpb24oY2FuZGlkYXRlKSB7XG4gIHJldHVybiB0aGlzLnNlbmQoXCJ0cmlja2xlXCIsIHsgY2FuZGlkYXRlOiBjYW5kaWRhdGUgfSk7XG59O1xuXG4vKipcbiAqIFJlcHJlc2VudHMgYSBKYW51cyBzZXNzaW9uIC0tIGEgSmFudXMgY29udGV4dCBmcm9tIHdpdGhpbiB3aGljaCB5b3UgY2FuIG9wZW4gbXVsdGlwbGUgaGFuZGxlcyBhbmQgY29ubmVjdGlvbnMuIE9uY2VcbiAqIGNyZWF0ZWQsIHRoaXMgc2Vzc2lvbiB3aWxsIGJlIGdpdmVuIGEgdW5pcXVlIElEIHdoaWNoIHNob3VsZCBiZSB1c2VkIHRvIGFzc29jaWF0ZSBpdCB3aXRoIGZ1dHVyZSBzaWduYWxsaW5nIG1lc3NhZ2VzLlxuICpcbiAqIFNlZSBodHRwczovL2phbnVzLmNvbmYubWVldGVjaG8uY29tL2RvY3MvcmVzdC5odG1sI3Nlc3Npb25zLlxuICoqL1xuZnVuY3Rpb24gSmFudXNTZXNzaW9uKG91dHB1dCwgb3B0aW9ucykge1xuICB0aGlzLm91dHB1dCA9IG91dHB1dDtcbiAgdGhpcy5pZCA9IHVuZGVmaW5lZDtcbiAgdGhpcy5uZXh0VHhJZCA9IDA7XG4gIHRoaXMudHhucyA9IHt9O1xuICB0aGlzLmV2ZW50SGFuZGxlcnMgPSB7fTtcbiAgdGhpcy5vcHRpb25zID0gT2JqZWN0LmFzc2lnbih7XG4gICAgdmVyYm9zZTogZmFsc2UsXG4gICAgdGltZW91dE1zOiAxMDAwMCxcbiAgICBrZWVwYWxpdmVNczogMzAwMDBcbiAgfSwgb3B0aW9ucyk7XG59XG5cbi8qKiBDcmVhdGVzIHRoaXMgc2Vzc2lvbiBvbiB0aGUgSmFudXMgc2VydmVyIGFuZCBzZXRzIGl0cyBJRC4gKiovXG5KYW51c1Nlc3Npb24ucHJvdG90eXBlLmNyZWF0ZSA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gdGhpcy5zZW5kKFwiY3JlYXRlXCIpLnRoZW4ocmVzcCA9PiB7XG4gICAgdGhpcy5pZCA9IHJlc3AuZGF0YS5pZDtcbiAgICByZXR1cm4gcmVzcDtcbiAgfSk7XG59O1xuXG4vKipcbiAqIERlc3Ryb3lzIHRoaXMgc2Vzc2lvbi4gTm90ZSB0aGF0IHVwb24gZGVzdHJ1Y3Rpb24sIEphbnVzIHdpbGwgYWxzbyBjbG9zZSB0aGUgc2lnbmFsbGluZyB0cmFuc3BvcnQgKGlmIGFwcGxpY2FibGUpIGFuZFxuICogYW55IG9wZW4gV2ViUlRDIGNvbm5lY3Rpb25zLlxuICoqL1xuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5kZXN0cm95ID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiB0aGlzLnNlbmQoXCJkZXN0cm95XCIpLnRoZW4oKHJlc3ApID0+IHtcbiAgICB0aGlzLmRpc3Bvc2UoKTtcbiAgICByZXR1cm4gcmVzcDtcbiAgfSk7XG59O1xuXG4vKipcbiAqIERpc3Bvc2VzIG9mIHRoaXMgc2Vzc2lvbiBpbiBhIHdheSBzdWNoIHRoYXQgbm8gZnVydGhlciBpbmNvbWluZyBzaWduYWxsaW5nIG1lc3NhZ2VzIHdpbGwgYmUgcHJvY2Vzc2VkLlxuICogT3V0c3RhbmRpbmcgdHJhbnNhY3Rpb25zIHdpbGwgYmUgcmVqZWN0ZWQuXG4gKiovXG5KYW51c1Nlc3Npb24ucHJvdG90eXBlLmRpc3Bvc2UgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5fa2lsbEtlZXBhbGl2ZSgpO1xuICB0aGlzLmV2ZW50SGFuZGxlcnMgPSB7fTtcbiAgZm9yICh2YXIgdHhJZCBpbiB0aGlzLnR4bnMpIHtcbiAgICBpZiAodGhpcy50eG5zLmhhc093blByb3BlcnR5KHR4SWQpKSB7XG4gICAgICB2YXIgdHhuID0gdGhpcy50eG5zW3R4SWRdO1xuICAgICAgY2xlYXJUaW1lb3V0KHR4bi50aW1lb3V0KTtcbiAgICAgIHR4bi5yZWplY3QobmV3IEVycm9yKFwiSmFudXMgc2Vzc2lvbiB3YXMgZGlzcG9zZWQuXCIpKTtcbiAgICAgIGRlbGV0ZSB0aGlzLnR4bnNbdHhJZF07XG4gICAgfVxuICB9XG59O1xuXG4vKipcbiAqIFdoZXRoZXIgdGhpcyBzaWduYWwgcmVwcmVzZW50cyBhbiBlcnJvciwgYW5kIHRoZSBhc3NvY2lhdGVkIHByb21pc2UgKGlmIGFueSkgc2hvdWxkIGJlIHJlamVjdGVkLlxuICogVXNlcnMgc2hvdWxkIG92ZXJyaWRlIHRoaXMgdG8gaGFuZGxlIGFueSBjdXN0b20gcGx1Z2luLXNwZWNpZmljIGVycm9yIGNvbnZlbnRpb25zLlxuICoqL1xuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5pc0Vycm9yID0gZnVuY3Rpb24oc2lnbmFsKSB7XG4gIHJldHVybiBzaWduYWwuamFudXMgPT09IFwiZXJyb3JcIjtcbn07XG5cbi8qKiBSZWdpc3RlcnMgYSBjYWxsYmFjayB0byBiZSBmaXJlZCB1cG9uIHRoZSByZWNlcHRpb24gb2YgYW55IGluY29taW5nIEphbnVzIHNpZ25hbHMgZm9yIHRoaXMgc2Vzc2lvbiB3aXRoIHRoZVxuICogYGphbnVzYCBhdHRyaWJ1dGUgZXF1YWwgdG8gYGV2YC5cbiAqKi9cbkphbnVzU2Vzc2lvbi5wcm90b3R5cGUub24gPSBmdW5jdGlvbihldiwgY2FsbGJhY2spIHtcbiAgdmFyIGhhbmRsZXJzID0gdGhpcy5ldmVudEhhbmRsZXJzW2V2XTtcbiAgaWYgKGhhbmRsZXJzID09IG51bGwpIHtcbiAgICBoYW5kbGVycyA9IHRoaXMuZXZlbnRIYW5kbGVyc1tldl0gPSBbXTtcbiAgfVxuICBoYW5kbGVycy5wdXNoKGNhbGxiYWNrKTtcbn07XG5cbi8qKlxuICogQ2FsbGJhY2sgZm9yIHJlY2VpdmluZyBKU09OIHNpZ25hbGxpbmcgbWVzc2FnZXMgcGVydGluZW50IHRvIHRoaXMgc2Vzc2lvbi4gSWYgdGhlIHNpZ25hbHMgYXJlIHJlc3BvbnNlcyB0byBwcmV2aW91c2x5XG4gKiBzZW50IHNpZ25hbHMsIHRoZSBwcm9taXNlcyBmb3IgdGhlIG91dGdvaW5nIHNpZ25hbHMgd2lsbCBiZSByZXNvbHZlZCBvciByZWplY3RlZCBhcHByb3ByaWF0ZWx5IHdpdGggdGhpcyBzaWduYWwgYXMgYW5cbiAqIGFyZ3VtZW50LlxuICpcbiAqIEV4dGVybmFsIGNhbGxlcnMgc2hvdWxkIGNhbGwgdGhpcyBmdW5jdGlvbiBldmVyeSB0aW1lIGEgbmV3IHNpZ25hbCBhcnJpdmVzIG9uIHRoZSB0cmFuc3BvcnQ7IGZvciBleGFtcGxlLCBpbiBhXG4gKiBXZWJTb2NrZXQncyBgbWVzc2FnZWAgZXZlbnQsIG9yIHdoZW4gYSBuZXcgZGF0dW0gc2hvd3MgdXAgaW4gYW4gSFRUUCBsb25nLXBvbGxpbmcgcmVzcG9uc2UuXG4gKiovXG5KYW51c1Nlc3Npb24ucHJvdG90eXBlLnJlY2VpdmUgPSBmdW5jdGlvbihzaWduYWwpIHtcbiAgaWYgKHRoaXMub3B0aW9ucy52ZXJib3NlKSB7XG4gICAgdGhpcy5fbG9nSW5jb21pbmcoc2lnbmFsKTtcbiAgfVxuICBpZiAoc2lnbmFsLnNlc3Npb25faWQgIT0gdGhpcy5pZCkge1xuICAgIGNvbnNvbGUud2FybihcIkluY29ycmVjdCBzZXNzaW9uIElEIHJlY2VpdmVkIGluIEphbnVzIHNpZ25hbGxpbmcgbWVzc2FnZTogd2FzIFwiICsgc2lnbmFsLnNlc3Npb25faWQgKyBcIiwgZXhwZWN0ZWQgXCIgKyB0aGlzLmlkICsgXCIuXCIpO1xuICB9XG5cbiAgdmFyIHJlc3BvbnNlVHlwZSA9IHNpZ25hbC5qYW51cztcbiAgdmFyIGhhbmRsZXJzID0gdGhpcy5ldmVudEhhbmRsZXJzW3Jlc3BvbnNlVHlwZV07XG4gIGlmIChoYW5kbGVycyAhPSBudWxsKSB7XG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBoYW5kbGVycy5sZW5ndGg7IGkrKykge1xuICAgICAgaGFuZGxlcnNbaV0oc2lnbmFsKTtcbiAgICB9XG4gIH1cblxuICBpZiAoc2lnbmFsLnRyYW5zYWN0aW9uICE9IG51bGwpIHtcbiAgICB2YXIgdHhuID0gdGhpcy50eG5zW3NpZ25hbC50cmFuc2FjdGlvbl07XG4gICAgaWYgKHR4biA9PSBudWxsKSB7XG4gICAgICAvLyB0aGlzIGlzIGEgcmVzcG9uc2UgdG8gYSB0cmFuc2FjdGlvbiB0aGF0IHdhc24ndCBjYXVzZWQgdmlhIEphbnVzU2Vzc2lvbi5zZW5kLCBvciBhIHBsdWdpbiByZXBsaWVkIHR3aWNlIHRvIGFcbiAgICAgIC8vIHNpbmdsZSByZXF1ZXN0LCBvciB0aGUgc2Vzc2lvbiB3YXMgZGlzcG9zZWQsIG9yIHNvbWV0aGluZyBlbHNlIHRoYXQgaXNuJ3QgdW5kZXIgb3VyIHB1cnZpZXc7IHRoYXQncyBmaW5lXG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKHJlc3BvbnNlVHlwZSA9PT0gXCJhY2tcIiAmJiB0eG4udHlwZSA9PSBcIm1lc3NhZ2VcIikge1xuICAgICAgLy8gdGhpcyBpcyBhbiBhY2sgb2YgYW4gYXN5bmNocm9ub3VzbHktcHJvY2Vzc2VkIHBsdWdpbiByZXF1ZXN0LCB3ZSBzaG91bGQgd2FpdCB0byByZXNvbHZlIHRoZSBwcm9taXNlIHVudGlsIHRoZVxuICAgICAgLy8gYWN0dWFsIHJlc3BvbnNlIGNvbWVzIGluXG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY2xlYXJUaW1lb3V0KHR4bi50aW1lb3V0KTtcblxuICAgIGRlbGV0ZSB0aGlzLnR4bnNbc2lnbmFsLnRyYW5zYWN0aW9uXTtcbiAgICAodGhpcy5pc0Vycm9yKHNpZ25hbCkgPyB0eG4ucmVqZWN0IDogdHhuLnJlc29sdmUpKHNpZ25hbCk7XG4gIH1cbn07XG5cbi8qKlxuICogU2VuZHMgYSBzaWduYWwgYXNzb2NpYXRlZCB3aXRoIHRoaXMgc2Vzc2lvbiwgYmVnaW5uaW5nIGEgbmV3IHRyYW5zYWN0aW9uLiBSZXR1cm5zIGEgcHJvbWlzZSB0aGF0IHdpbGwgYmUgcmVzb2x2ZWQgb3JcbiAqIHJlamVjdGVkIHdoZW4gYSByZXNwb25zZSBpcyByZWNlaXZlZCBpbiB0aGUgc2FtZSB0cmFuc2FjdGlvbiwgb3Igd2hlbiBubyByZXNwb25zZSBpcyByZWNlaXZlZCB3aXRoaW4gdGhlIHNlc3Npb25cbiAqIHRpbWVvdXQuXG4gKiovXG5KYW51c1Nlc3Npb24ucHJvdG90eXBlLnNlbmQgPSBmdW5jdGlvbih0eXBlLCBzaWduYWwpIHtcbiAgc2lnbmFsID0gT2JqZWN0LmFzc2lnbih7IHRyYW5zYWN0aW9uOiAodGhpcy5uZXh0VHhJZCsrKS50b1N0cmluZygpIH0sIHNpZ25hbCk7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgdmFyIHRpbWVvdXQgPSBudWxsO1xuICAgIGlmICh0aGlzLm9wdGlvbnMudGltZW91dE1zKSB7XG4gICAgICB0aW1lb3V0ID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgIGRlbGV0ZSB0aGlzLnR4bnNbc2lnbmFsLnRyYW5zYWN0aW9uXTtcbiAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihcIlNpZ25hbGxpbmcgdHJhbnNhY3Rpb24gd2l0aCB0eGlkIFwiICsgc2lnbmFsLnRyYW5zYWN0aW9uICsgXCIgdGltZWQgb3V0LlwiKSk7XG4gICAgICB9LCB0aGlzLm9wdGlvbnMudGltZW91dE1zKTtcbiAgICB9XG4gICAgdGhpcy50eG5zW3NpZ25hbC50cmFuc2FjdGlvbl0gPSB7IHJlc29sdmU6IHJlc29sdmUsIHJlamVjdDogcmVqZWN0LCB0aW1lb3V0OiB0aW1lb3V0LCB0eXBlOiB0eXBlIH07XG4gICAgdGhpcy5fdHJhbnNtaXQodHlwZSwgc2lnbmFsKTtcbiAgfSk7XG59O1xuXG5KYW51c1Nlc3Npb24ucHJvdG90eXBlLl90cmFuc21pdCA9IGZ1bmN0aW9uKHR5cGUsIHNpZ25hbCkge1xuICBzaWduYWwgPSBPYmplY3QuYXNzaWduKHsgamFudXM6IHR5cGUgfSwgc2lnbmFsKTtcblxuICBpZiAodGhpcy5pZCAhPSBudWxsKSB7IC8vIHRoaXMuaWQgaXMgdW5kZWZpbmVkIGluIHRoZSBzcGVjaWFsIGNhc2Ugd2hlbiB3ZSdyZSBzZW5kaW5nIHRoZSBzZXNzaW9uIGNyZWF0ZSBtZXNzYWdlXG4gICAgc2lnbmFsID0gT2JqZWN0LmFzc2lnbih7IHNlc3Npb25faWQ6IHRoaXMuaWQgfSwgc2lnbmFsKTtcbiAgfVxuXG4gIGlmICh0aGlzLm9wdGlvbnMudmVyYm9zZSkge1xuICAgIHRoaXMuX2xvZ091dGdvaW5nKHNpZ25hbCk7XG4gIH1cblxuICB0aGlzLm91dHB1dChKU09OLnN0cmluZ2lmeShzaWduYWwpKTtcbiAgdGhpcy5fcmVzZXRLZWVwYWxpdmUoKTtcbn07XG5cbkphbnVzU2Vzc2lvbi5wcm90b3R5cGUuX2xvZ091dGdvaW5nID0gZnVuY3Rpb24oc2lnbmFsKSB7XG4gIHZhciBraW5kID0gc2lnbmFsLmphbnVzO1xuICBpZiAoa2luZCA9PT0gXCJtZXNzYWdlXCIgJiYgc2lnbmFsLmpzZXApIHtcbiAgICBraW5kID0gc2lnbmFsLmpzZXAudHlwZTtcbiAgfVxuICB2YXIgbWVzc2FnZSA9IFwiPiBPdXRnb2luZyBKYW51cyBcIiArIChraW5kIHx8IFwic2lnbmFsXCIpICsgXCIgKCNcIiArIHNpZ25hbC50cmFuc2FjdGlvbiArIFwiKTogXCI7XG4gIGNvbnNvbGUuZGVidWcoXCIlY1wiICsgbWVzc2FnZSwgXCJjb2xvcjogIzA0MFwiLCBzaWduYWwpO1xufTtcblxuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5fbG9nSW5jb21pbmcgPSBmdW5jdGlvbihzaWduYWwpIHtcbiAgdmFyIGtpbmQgPSBzaWduYWwuamFudXM7XG4gIHZhciBtZXNzYWdlID0gc2lnbmFsLnRyYW5zYWN0aW9uID9cbiAgICAgIFwiPCBJbmNvbWluZyBKYW51cyBcIiArIChraW5kIHx8IFwic2lnbmFsXCIpICsgXCIgKCNcIiArIHNpZ25hbC50cmFuc2FjdGlvbiArIFwiKTogXCIgOlxuICAgICAgXCI8IEluY29taW5nIEphbnVzIFwiICsgKGtpbmQgfHwgXCJzaWduYWxcIikgKyBcIjogXCI7XG4gIGNvbnNvbGUuZGVidWcoXCIlY1wiICsgbWVzc2FnZSwgXCJjb2xvcjogIzAwNFwiLCBzaWduYWwpO1xufTtcblxuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5fc2VuZEtlZXBhbGl2ZSA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gdGhpcy5zZW5kKFwia2VlcGFsaXZlXCIpO1xufTtcblxuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5fa2lsbEtlZXBhbGl2ZSA9IGZ1bmN0aW9uKCkge1xuICBjbGVhclRpbWVvdXQodGhpcy5rZWVwYWxpdmVUaW1lb3V0KTtcbn07XG5cbkphbnVzU2Vzc2lvbi5wcm90b3R5cGUuX3Jlc2V0S2VlcGFsaXZlID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMuX2tpbGxLZWVwYWxpdmUoKTtcbiAgaWYgKHRoaXMub3B0aW9ucy5rZWVwYWxpdmVNcykge1xuICAgIHRoaXMua2VlcGFsaXZlVGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgdGhpcy5fc2VuZEtlZXBhbGl2ZSgpLmNhdGNoKGUgPT4gY29uc29sZS5lcnJvcihcIkVycm9yIHJlY2VpdmVkIGZyb20ga2VlcGFsaXZlOiBcIiwgZSkpO1xuICAgIH0sIHRoaXMub3B0aW9ucy5rZWVwYWxpdmVNcyk7XG4gIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBKYW51c1BsdWdpbkhhbmRsZSxcbiAgSmFudXNTZXNzaW9uXG59O1xuIiwiLyogZ2xvYmFsIE5BRiAqL1xudmFyIG1qID0gcmVxdWlyZShcIkBuZXR3b3JrZWQtYWZyYW1lL21pbmlqYW51c1wiKTtcbm1qLkphbnVzU2Vzc2lvbi5wcm90b3R5cGUuc2VuZE9yaWdpbmFsID0gbWouSmFudXNTZXNzaW9uLnByb3RvdHlwZS5zZW5kO1xubWouSmFudXNTZXNzaW9uLnByb3RvdHlwZS5zZW5kID0gZnVuY3Rpb24odHlwZSwgc2lnbmFsKSB7XG4gIHJldHVybiB0aGlzLnNlbmRPcmlnaW5hbCh0eXBlLCBzaWduYWwpLmNhdGNoKChlKSA9PiB7XG4gICAgaWYgKGUubWVzc2FnZSAmJiBlLm1lc3NhZ2UuaW5kZXhPZihcInRpbWVkIG91dFwiKSA+IC0xKSB7XG4gICAgICBjb25zb2xlLmVycm9yKFwid2ViIHNvY2tldCB0aW1lZCBvdXRcIik7XG4gICAgICBOQUYuY29ubmVjdGlvbi5hZGFwdGVyLnJlY29ubmVjdCgpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyhlKTtcbiAgICB9XG4gIH0pO1xufVxuXG52YXIgc2RwVXRpbHMgPSByZXF1aXJlKFwic2RwXCIpO1xuLy92YXIgZGVidWcgPSByZXF1aXJlKFwiZGVidWdcIikoXCJuYWYtamFudXMtYWRhcHRlcjpkZWJ1Z1wiKTtcbi8vdmFyIHdhcm4gPSByZXF1aXJlKFwiZGVidWdcIikoXCJuYWYtamFudXMtYWRhcHRlcjp3YXJuXCIpO1xuLy92YXIgZXJyb3IgPSByZXF1aXJlKFwiZGVidWdcIikoXCJuYWYtamFudXMtYWRhcHRlcjplcnJvclwiKTtcbnZhciBkZWJ1ZyA9IGNvbnNvbGUubG9nO1xudmFyIHdhcm4gPSBjb25zb2xlLndhcm47XG52YXIgZXJyb3IgPSBjb25zb2xlLmVycm9yO1xudmFyIGlzU2FmYXJpID0gL14oKD8hY2hyb21lfGFuZHJvaWQpLikqc2FmYXJpL2kudGVzdChuYXZpZ2F0b3IudXNlckFnZW50KTtcblxuY29uc3QgU1VCU0NSSUJFX1RJTUVPVVRfTVMgPSAxNTAwMDtcblxuZnVuY3Rpb24gZGVib3VuY2UoZm4pIHtcbiAgdmFyIGN1cnIgPSBQcm9taXNlLnJlc29sdmUoKTtcbiAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgIHZhciBhcmdzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzKTtcbiAgICBjdXJyID0gY3Vyci50aGVuKF8gPT4gZm4uYXBwbHkodGhpcywgYXJncykpO1xuICB9O1xufVxuXG5mdW5jdGlvbiByYW5kb21VaW50KCkge1xuICByZXR1cm4gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogTnVtYmVyLk1BWF9TQUZFX0lOVEVHRVIpO1xufVxuXG5mdW5jdGlvbiB1bnRpbERhdGFDaGFubmVsT3BlbihkYXRhQ2hhbm5lbCkge1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgIGlmIChkYXRhQ2hhbm5lbC5yZWFkeVN0YXRlID09PSBcIm9wZW5cIikge1xuICAgICAgcmVzb2x2ZSgpO1xuICAgIH0gZWxzZSB7XG4gICAgICBsZXQgcmVzb2x2ZXIsIHJlamVjdG9yO1xuXG4gICAgICBjb25zdCBjbGVhciA9ICgpID0+IHtcbiAgICAgICAgZGF0YUNoYW5uZWwucmVtb3ZlRXZlbnRMaXN0ZW5lcihcIm9wZW5cIiwgcmVzb2x2ZXIpO1xuICAgICAgICBkYXRhQ2hhbm5lbC5yZW1vdmVFdmVudExpc3RlbmVyKFwiZXJyb3JcIiwgcmVqZWN0b3IpO1xuICAgICAgfTtcblxuICAgICAgcmVzb2x2ZXIgPSAoKSA9PiB7XG4gICAgICAgIGNsZWFyKCk7XG4gICAgICAgIHJlc29sdmUoKTtcbiAgICAgIH07XG4gICAgICByZWplY3RvciA9ICgpID0+IHtcbiAgICAgICAgY2xlYXIoKTtcbiAgICAgICAgcmVqZWN0KCk7XG4gICAgICB9O1xuXG4gICAgICBkYXRhQ2hhbm5lbC5hZGRFdmVudExpc3RlbmVyKFwib3BlblwiLCByZXNvbHZlcik7XG4gICAgICBkYXRhQ2hhbm5lbC5hZGRFdmVudExpc3RlbmVyKFwiZXJyb3JcIiwgcmVqZWN0b3IpO1xuICAgIH1cbiAgfSk7XG59XG5cbmNvbnN0IGlzSDI2NFZpZGVvU3VwcG9ydGVkID0gKCgpID0+IHtcbiAgY29uc3QgdmlkZW8gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwidmlkZW9cIik7XG4gIHJldHVybiB2aWRlby5jYW5QbGF5VHlwZSgndmlkZW8vbXA0OyBjb2RlY3M9XCJhdmMxLjQyRTAxRSwgbXA0YS40MC4yXCInKSAhPT0gXCJcIjtcbn0pKCk7XG5cbmNvbnN0IE9QVVNfUEFSQU1FVEVSUyA9IHtcbiAgLy8gaW5kaWNhdGVzIHRoYXQgd2Ugd2FudCB0byBlbmFibGUgRFRYIHRvIGVsaWRlIHNpbGVuY2UgcGFja2V0c1xuICB1c2VkdHg6IDEsXG4gIC8vIGluZGljYXRlcyB0aGF0IHdlIHByZWZlciB0byByZWNlaXZlIG1vbm8gYXVkaW8gKGltcG9ydGFudCBmb3Igdm9pcCBwcm9maWxlKVxuICBzdGVyZW86IDAsXG4gIC8vIGluZGljYXRlcyB0aGF0IHdlIHByZWZlciB0byBzZW5kIG1vbm8gYXVkaW8gKGltcG9ydGFudCBmb3Igdm9pcCBwcm9maWxlKVxuICBcInNwcm9wLXN0ZXJlb1wiOiAwXG59O1xuXG5jb25zdCBERUZBVUxUX1BFRVJfQ09OTkVDVElPTl9DT05GSUcgPSB7XG4gIGljZVNlcnZlcnM6IFt7IHVybHM6IFwic3R1bjpzdHVuMS5sLmdvb2dsZS5jb206MTkzMDJcIiB9LCB7IHVybHM6IFwic3R1bjpzdHVuMi5sLmdvb2dsZS5jb206MTkzMDJcIiB9XVxufTtcblxuY29uc3QgV1NfTk9STUFMX0NMT1NVUkUgPSAxMDAwO1xuXG5jbGFzcyBKYW51c0FkYXB0ZXIge1xuICBjb25zdHJ1Y3RvcigpIHtcbiAgICB0aGlzLnJvb20gPSBudWxsO1xuICAgIC8vIFdlIGV4cGVjdCB0aGUgY29uc3VtZXIgdG8gc2V0IGEgY2xpZW50IGlkIGJlZm9yZSBjb25uZWN0aW5nLlxuICAgIHRoaXMuY2xpZW50SWQgPSBudWxsO1xuICAgIHRoaXMuam9pblRva2VuID0gbnVsbDtcblxuICAgIHRoaXMuc2VydmVyVXJsID0gbnVsbDtcbiAgICB0aGlzLndlYlJ0Y09wdGlvbnMgPSB7fTtcbiAgICB0aGlzLnBlZXJDb25uZWN0aW9uQ29uZmlnID0gbnVsbDtcbiAgICB0aGlzLndzID0gbnVsbDtcbiAgICB0aGlzLnNlc3Npb24gPSBudWxsO1xuICAgIHRoaXMucmVsaWFibGVUcmFuc3BvcnQgPSBcImRhdGFjaGFubmVsXCI7XG4gICAgdGhpcy51bnJlbGlhYmxlVHJhbnNwb3J0ID0gXCJkYXRhY2hhbm5lbFwiO1xuXG4gICAgLy8gSW4gdGhlIGV2ZW50IHRoZSBzZXJ2ZXIgcmVzdGFydHMgYW5kIGFsbCBjbGllbnRzIGxvc2UgY29ubmVjdGlvbiwgcmVjb25uZWN0IHdpdGhcbiAgICAvLyBzb21lIHJhbmRvbSBqaXR0ZXIgYWRkZWQgdG8gcHJldmVudCBzaW11bHRhbmVvdXMgcmVjb25uZWN0aW9uIHJlcXVlc3RzLlxuICAgIHRoaXMuaW5pdGlhbFJlY29ubmVjdGlvbkRlbGF5ID0gMTAwMCAqIE1hdGgucmFuZG9tKCk7XG4gICAgdGhpcy5yZWNvbm5lY3Rpb25EZWxheSA9IHRoaXMuaW5pdGlhbFJlY29ubmVjdGlvbkRlbGF5O1xuICAgIHRoaXMucmVjb25uZWN0aW9uVGltZW91dCA9IG51bGw7XG4gICAgdGhpcy5tYXhSZWNvbm5lY3Rpb25BdHRlbXB0cyA9IDEwO1xuICAgIHRoaXMucmVjb25uZWN0aW9uQXR0ZW1wdHMgPSAwO1xuXG4gICAgdGhpcy5wdWJsaXNoZXIgPSBudWxsO1xuICAgIHRoaXMub2NjdXBhbnRzID0ge307XG4gICAgdGhpcy5sZWZ0T2NjdXBhbnRzID0gbmV3IFNldCgpO1xuICAgIHRoaXMubWVkaWFTdHJlYW1zID0ge307XG4gICAgdGhpcy5sb2NhbE1lZGlhU3RyZWFtID0gbnVsbDtcbiAgICB0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzID0gbmV3IE1hcCgpO1xuXG4gICAgdGhpcy5ibG9ja2VkQ2xpZW50cyA9IG5ldyBNYXAoKTtcbiAgICB0aGlzLmZyb3plblVwZGF0ZXMgPSBuZXcgTWFwKCk7XG5cbiAgICB0aGlzLnRpbWVPZmZzZXRzID0gW107XG4gICAgdGhpcy5zZXJ2ZXJUaW1lUmVxdWVzdHMgPSAwO1xuICAgIHRoaXMuYXZnVGltZU9mZnNldCA9IDA7XG5cbiAgICB0aGlzLm9uV2Vic29ja2V0T3BlbiA9IHRoaXMub25XZWJzb2NrZXRPcGVuLmJpbmQodGhpcyk7XG4gICAgdGhpcy5vbldlYnNvY2tldENsb3NlID0gdGhpcy5vbldlYnNvY2tldENsb3NlLmJpbmQodGhpcyk7XG4gICAgdGhpcy5vbldlYnNvY2tldE1lc3NhZ2UgPSB0aGlzLm9uV2Vic29ja2V0TWVzc2FnZS5iaW5kKHRoaXMpO1xuICAgIHRoaXMub25EYXRhQ2hhbm5lbE1lc3NhZ2UgPSB0aGlzLm9uRGF0YUNoYW5uZWxNZXNzYWdlLmJpbmQodGhpcyk7XG4gICAgdGhpcy5vbkRhdGEgPSB0aGlzLm9uRGF0YS5iaW5kKHRoaXMpO1xuICB9XG5cbiAgc2V0U2VydmVyVXJsKHVybCkge1xuICAgIHRoaXMuc2VydmVyVXJsID0gdXJsO1xuICB9XG5cbiAgc2V0QXBwKGFwcCkge31cblxuICBzZXRSb29tKHJvb21OYW1lKSB7XG4gICAgdGhpcy5yb29tID0gcm9vbU5hbWU7XG4gIH1cblxuICBzZXRKb2luVG9rZW4oam9pblRva2VuKSB7XG4gICAgdGhpcy5qb2luVG9rZW4gPSBqb2luVG9rZW47XG4gIH1cblxuICBzZXRDbGllbnRJZChjbGllbnRJZCkge1xuICAgIHRoaXMuY2xpZW50SWQgPSBjbGllbnRJZDtcbiAgfVxuXG4gIHNldFdlYlJ0Y09wdGlvbnMob3B0aW9ucykge1xuICAgIHRoaXMud2ViUnRjT3B0aW9ucyA9IG9wdGlvbnM7XG4gIH1cblxuICBzZXRQZWVyQ29ubmVjdGlvbkNvbmZpZyhwZWVyQ29ubmVjdGlvbkNvbmZpZykge1xuICAgIHRoaXMucGVlckNvbm5lY3Rpb25Db25maWcgPSBwZWVyQ29ubmVjdGlvbkNvbmZpZztcbiAgfVxuXG4gIHNldFNlcnZlckNvbm5lY3RMaXN0ZW5lcnMoc3VjY2Vzc0xpc3RlbmVyLCBmYWlsdXJlTGlzdGVuZXIpIHtcbiAgICB0aGlzLmNvbm5lY3RTdWNjZXNzID0gc3VjY2Vzc0xpc3RlbmVyO1xuICAgIHRoaXMuY29ubmVjdEZhaWx1cmUgPSBmYWlsdXJlTGlzdGVuZXI7XG4gIH1cblxuICBzZXRSb29tT2NjdXBhbnRMaXN0ZW5lcihvY2N1cGFudExpc3RlbmVyKSB7XG4gICAgdGhpcy5vbk9jY3VwYW50c0NoYW5nZWQgPSBvY2N1cGFudExpc3RlbmVyO1xuICB9XG5cbiAgc2V0RGF0YUNoYW5uZWxMaXN0ZW5lcnMob3Blbkxpc3RlbmVyLCBjbG9zZWRMaXN0ZW5lciwgbWVzc2FnZUxpc3RlbmVyKSB7XG4gICAgdGhpcy5vbk9jY3VwYW50Q29ubmVjdGVkID0gb3Blbkxpc3RlbmVyO1xuICAgIHRoaXMub25PY2N1cGFudERpc2Nvbm5lY3RlZCA9IGNsb3NlZExpc3RlbmVyO1xuICAgIHRoaXMub25PY2N1cGFudE1lc3NhZ2UgPSBtZXNzYWdlTGlzdGVuZXI7XG4gIH1cblxuICBzZXRSZWNvbm5lY3Rpb25MaXN0ZW5lcnMocmVjb25uZWN0aW5nTGlzdGVuZXIsIHJlY29ubmVjdGVkTGlzdGVuZXIsIHJlY29ubmVjdGlvbkVycm9yTGlzdGVuZXIpIHtcbiAgICAvLyBvblJlY29ubmVjdGluZyBpcyBjYWxsZWQgd2l0aCB0aGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB1bnRpbCB0aGUgbmV4dCByZWNvbm5lY3Rpb24gYXR0ZW1wdFxuICAgIHRoaXMub25SZWNvbm5lY3RpbmcgPSByZWNvbm5lY3RpbmdMaXN0ZW5lcjtcbiAgICAvLyBvblJlY29ubmVjdGVkIGlzIGNhbGxlZCB3aGVuIHRoZSBjb25uZWN0aW9uIGhhcyBiZWVuIHJlZXN0YWJsaXNoZWRcbiAgICB0aGlzLm9uUmVjb25uZWN0ZWQgPSByZWNvbm5lY3RlZExpc3RlbmVyO1xuICAgIC8vIG9uUmVjb25uZWN0aW9uRXJyb3IgaXMgY2FsbGVkIHdpdGggYW4gZXJyb3Igd2hlbiBtYXhSZWNvbm5lY3Rpb25BdHRlbXB0cyBoYXMgYmVlbiByZWFjaGVkXG4gICAgdGhpcy5vblJlY29ubmVjdGlvbkVycm9yID0gcmVjb25uZWN0aW9uRXJyb3JMaXN0ZW5lcjtcbiAgfVxuXG4gIHNldEV2ZW50TG9vcHMobG9vcHMpIHtcbiAgICB0aGlzLmxvb3BzID0gbG9vcHM7XG4gIH1cblxuICBjb25uZWN0KCkge1xuICAgIGRlYnVnKGBjb25uZWN0aW5nIHRvICR7dGhpcy5zZXJ2ZXJVcmx9YCk7XG5cbiAgICBjb25zdCB3ZWJzb2NrZXRDb25uZWN0aW9uID0gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgdGhpcy53cyA9IG5ldyBXZWJTb2NrZXQodGhpcy5zZXJ2ZXJVcmwsIFwiamFudXMtcHJvdG9jb2xcIik7XG5cbiAgICAgIHRoaXMuc2Vzc2lvbiA9IG5ldyBtai5KYW51c1Nlc3Npb24odGhpcy53cy5zZW5kLmJpbmQodGhpcy53cyksIHsgdGltZW91dE1zOiA0MDAwMCB9KTtcblxuICAgICAgdGhpcy53cy5hZGRFdmVudExpc3RlbmVyKFwiY2xvc2VcIiwgdGhpcy5vbldlYnNvY2tldENsb3NlKTtcbiAgICAgIHRoaXMud3MuYWRkRXZlbnRMaXN0ZW5lcihcIm1lc3NhZ2VcIiwgdGhpcy5vbldlYnNvY2tldE1lc3NhZ2UpO1xuXG4gICAgICB0aGlzLndzT25PcGVuID0gKCkgPT4ge1xuICAgICAgICB0aGlzLndzLnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJvcGVuXCIsIHRoaXMud3NPbk9wZW4pO1xuICAgICAgICB0aGlzLm9uV2Vic29ja2V0T3BlbigpXG4gICAgICAgICAgLnRoZW4ocmVzb2x2ZSlcbiAgICAgICAgICAuY2F0Y2gocmVqZWN0KTtcbiAgICAgIH07XG5cbiAgICAgIHRoaXMud3MuYWRkRXZlbnRMaXN0ZW5lcihcIm9wZW5cIiwgdGhpcy53c09uT3Blbik7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gUHJvbWlzZS5hbGwoW3dlYnNvY2tldENvbm5lY3Rpb24sIHRoaXMudXBkYXRlVGltZU9mZnNldCgpXSk7XG4gIH1cblxuICBkaXNjb25uZWN0KCkge1xuICAgIGRlYnVnKGBkaXNjb25uZWN0aW5nYCk7XG5cbiAgICBjbGVhclRpbWVvdXQodGhpcy5yZWNvbm5lY3Rpb25UaW1lb3V0KTtcblxuICAgIHRoaXMucmVtb3ZlQWxsT2NjdXBhbnRzKCk7XG4gICAgdGhpcy5sZWZ0T2NjdXBhbnRzID0gbmV3IFNldCgpO1xuXG4gICAgaWYgKHRoaXMucHVibGlzaGVyKSB7XG4gICAgICAvLyBDbG9zZSB0aGUgcHVibGlzaGVyIHBlZXIgY29ubmVjdGlvbi4gV2hpY2ggYWxzbyBkZXRhY2hlcyB0aGUgcGx1Z2luIGhhbmRsZS5cbiAgICAgIHRoaXMucHVibGlzaGVyLmNvbm4uY2xvc2UoKTtcbiAgICAgIHRoaXMucHVibGlzaGVyID0gbnVsbDtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5zZXNzaW9uKSB7XG4gICAgICB0aGlzLnNlc3Npb24uZGlzcG9zZSgpO1xuICAgICAgdGhpcy5zZXNzaW9uID0gbnVsbDtcbiAgICB9XG5cbiAgICBpZiAodGhpcy53cykge1xuICAgICAgdGhpcy53cy5yZW1vdmVFdmVudExpc3RlbmVyKFwib3BlblwiLCB0aGlzLndzT25PcGVuKTtcbiAgICAgIHRoaXMud3MucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImNsb3NlXCIsIHRoaXMub25XZWJzb2NrZXRDbG9zZSk7XG4gICAgICB0aGlzLndzLnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsIHRoaXMub25XZWJzb2NrZXRNZXNzYWdlKTtcbiAgICAgIHRoaXMud3MuY2xvc2UoKTtcbiAgICAgIHRoaXMud3MgPSBudWxsO1xuICAgIH1cblxuICAgIC8vIE5vdyB0aGF0IGFsbCBSVENQZWVyQ29ubmVjdGlvbiBjbG9zZWQsIGJlIHN1cmUgdG8gbm90IGNhbGxcbiAgICAvLyByZWNvbm5lY3QoKSBhZ2FpbiB2aWEgcGVyZm9ybURlbGF5ZWRSZWNvbm5lY3QgaWYgcHJldmlvdXNcbiAgICAvLyBSVENQZWVyQ29ubmVjdGlvbiB3YXMgaW4gdGhlIGZhaWxlZCBzdGF0ZS5cbiAgICBpZiAodGhpcy5kZWxheWVkUmVjb25uZWN0VGltZW91dCkge1xuICAgICAgY2xlYXJUaW1lb3V0KHRoaXMuZGVsYXllZFJlY29ubmVjdFRpbWVvdXQpO1xuICAgICAgdGhpcy5kZWxheWVkUmVjb25uZWN0VGltZW91dCA9IG51bGw7XG4gICAgfVxuICB9XG5cbiAgaXNEaXNjb25uZWN0ZWQoKSB7XG4gICAgcmV0dXJuIHRoaXMud3MgPT09IG51bGw7XG4gIH1cblxuICBhc3luYyBvbldlYnNvY2tldE9wZW4oKSB7XG4gICAgLy8gQ3JlYXRlIHRoZSBKYW51cyBTZXNzaW9uXG4gICAgYXdhaXQgdGhpcy5zZXNzaW9uLmNyZWF0ZSgpO1xuXG4gICAgLy8gQXR0YWNoIHRoZSBTRlUgUGx1Z2luIGFuZCBjcmVhdGUgYSBSVENQZWVyQ29ubmVjdGlvbiBmb3IgdGhlIHB1Ymxpc2hlci5cbiAgICAvLyBUaGUgcHVibGlzaGVyIHNlbmRzIGF1ZGlvIGFuZCBvcGVucyB0d28gYmlkaXJlY3Rpb25hbCBkYXRhIGNoYW5uZWxzLlxuICAgIC8vIE9uZSByZWxpYWJsZSBkYXRhY2hhbm5lbCBhbmQgb25lIHVucmVsaWFibGUuXG4gICAgdGhpcy5wdWJsaXNoZXIgPSBhd2FpdCB0aGlzLmNyZWF0ZVB1Ymxpc2hlcigpO1xuXG4gICAgLy8gQ2FsbCB0aGUgbmFmIGNvbm5lY3RTdWNjZXNzIGNhbGxiYWNrIGJlZm9yZSB3ZSBzdGFydCByZWNlaXZpbmcgV2ViUlRDIG1lc3NhZ2VzLlxuICAgIHRoaXMuY29ubmVjdFN1Y2Nlc3ModGhpcy5jbGllbnRJZCk7XG5cbiAgICBjb25zdCBhZGRPY2N1cGFudFByb21pc2VzID0gW107XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMucHVibGlzaGVyLmluaXRpYWxPY2N1cGFudHMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IG9jY3VwYW50SWQgPSB0aGlzLnB1Ymxpc2hlci5pbml0aWFsT2NjdXBhbnRzW2ldO1xuICAgICAgaWYgKG9jY3VwYW50SWQgPT09IHRoaXMuY2xpZW50SWQpIGNvbnRpbnVlOyAvLyBIYXBwZW5zIGR1cmluZyBub24tZ3JhY2VmdWwgcmVjb25uZWN0cyBkdWUgdG8gem9tYmllIHNlc3Npb25zXG4gICAgICBhZGRPY2N1cGFudFByb21pc2VzLnB1c2godGhpcy5hZGRPY2N1cGFudChvY2N1cGFudElkKSk7XG4gICAgfVxuXG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoYWRkT2NjdXBhbnRQcm9taXNlcyk7XG4gIH1cblxuICBvbldlYnNvY2tldENsb3NlKGV2ZW50KSB7XG4gICAgLy8gVGhlIGNvbm5lY3Rpb24gd2FzIGNsb3NlZCBzdWNjZXNzZnVsbHkuIERvbid0IHRyeSB0byByZWNvbm5lY3QuXG4gICAgaWYgKGV2ZW50LmNvZGUgPT09IFdTX05PUk1BTF9DTE9TVVJFKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc29sZS53YXJuKFwiSmFudXMgd2Vic29ja2V0IGNsb3NlZCB1bmV4cGVjdGVkbHkuXCIpO1xuICAgIGlmICh0aGlzLm9uUmVjb25uZWN0aW5nKSB7XG4gICAgICB0aGlzLm9uUmVjb25uZWN0aW5nKHRoaXMucmVjb25uZWN0aW9uRGVsYXkpO1xuICAgIH1cblxuICAgIHRoaXMucmVjb25uZWN0aW9uVGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4gdGhpcy5yZWNvbm5lY3QoKSwgdGhpcy5yZWNvbm5lY3Rpb25EZWxheSk7XG4gIH1cblxuICByZWNvbm5lY3QoKSB7XG4gICAgLy8gRGlzcG9zZSBvZiBhbGwgbmV0d29ya2VkIGVudGl0aWVzIGFuZCBvdGhlciByZXNvdXJjZXMgdGllZCB0byB0aGUgc2Vzc2lvbi5cbiAgICB0aGlzLmRpc2Nvbm5lY3QoKTtcblxuICAgIHRoaXMuY29ubmVjdCgpXG4gICAgICAudGhlbigoKSA9PiB7XG4gICAgICAgIHRoaXMucmVjb25uZWN0aW9uRGVsYXkgPSB0aGlzLmluaXRpYWxSZWNvbm5lY3Rpb25EZWxheTtcbiAgICAgICAgdGhpcy5yZWNvbm5lY3Rpb25BdHRlbXB0cyA9IDA7XG5cbiAgICAgICAgaWYgKHRoaXMub25SZWNvbm5lY3RlZCkge1xuICAgICAgICAgIHRoaXMub25SZWNvbm5lY3RlZCgpO1xuICAgICAgICB9XG4gICAgICB9KVxuICAgICAgLmNhdGNoKGVycm9yID0+IHtcbiAgICAgICAgdGhpcy5yZWNvbm5lY3Rpb25EZWxheSArPSAxMDAwO1xuICAgICAgICB0aGlzLnJlY29ubmVjdGlvbkF0dGVtcHRzKys7XG5cbiAgICAgICAgaWYgKHRoaXMucmVjb25uZWN0aW9uQXR0ZW1wdHMgPiB0aGlzLm1heFJlY29ubmVjdGlvbkF0dGVtcHRzICYmIHRoaXMub25SZWNvbm5lY3Rpb25FcnJvcikge1xuICAgICAgICAgIHJldHVybiB0aGlzLm9uUmVjb25uZWN0aW9uRXJyb3IoXG4gICAgICAgICAgICBuZXcgRXJyb3IoXCJDb25uZWN0aW9uIGNvdWxkIG5vdCBiZSByZWVzdGFibGlzaGVkLCBleGNlZWRlZCBtYXhpbXVtIG51bWJlciBvZiByZWNvbm5lY3Rpb24gYXR0ZW1wdHMuXCIpXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnNvbGUud2FybihcIkVycm9yIGR1cmluZyByZWNvbm5lY3QsIHJldHJ5aW5nLlwiKTtcbiAgICAgICAgY29uc29sZS53YXJuKGVycm9yKTtcblxuICAgICAgICBpZiAodGhpcy5vblJlY29ubmVjdGluZykge1xuICAgICAgICAgIHRoaXMub25SZWNvbm5lY3RpbmcodGhpcy5yZWNvbm5lY3Rpb25EZWxheSk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnJlY29ubmVjdGlvblRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHRoaXMucmVjb25uZWN0KCksIHRoaXMucmVjb25uZWN0aW9uRGVsYXkpO1xuICAgICAgfSk7XG4gIH1cblxuICBwZXJmb3JtRGVsYXllZFJlY29ubmVjdCgpIHtcbiAgICBpZiAodGhpcy5kZWxheWVkUmVjb25uZWN0VGltZW91dCkge1xuICAgICAgY2xlYXJUaW1lb3V0KHRoaXMuZGVsYXllZFJlY29ubmVjdFRpbWVvdXQpO1xuICAgIH1cblxuICAgIHRoaXMuZGVsYXllZFJlY29ubmVjdFRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIHRoaXMuZGVsYXllZFJlY29ubmVjdFRpbWVvdXQgPSBudWxsO1xuICAgICAgdGhpcy5yZWNvbm5lY3QoKTtcbiAgICB9LCAxMDAwMCk7XG4gIH1cblxuICBvbldlYnNvY2tldE1lc3NhZ2UoZXZlbnQpIHtcbiAgICB0aGlzLnNlc3Npb24ucmVjZWl2ZShKU09OLnBhcnNlKGV2ZW50LmRhdGEpKTtcbiAgfVxuXG4gIGFzeW5jIGFkZE9jY3VwYW50KG9jY3VwYW50SWQpIHtcbiAgICBpZiAodGhpcy5vY2N1cGFudHNbb2NjdXBhbnRJZF0pIHtcbiAgICAgIHRoaXMucmVtb3ZlT2NjdXBhbnQob2NjdXBhbnRJZCk7XG4gICAgfVxuXG4gICAgdGhpcy5sZWZ0T2NjdXBhbnRzLmRlbGV0ZShvY2N1cGFudElkKTtcblxuICAgIHZhciBzdWJzY3JpYmVyID0gYXdhaXQgdGhpcy5jcmVhdGVTdWJzY3JpYmVyKG9jY3VwYW50SWQpO1xuXG4gICAgaWYgKCFzdWJzY3JpYmVyKSByZXR1cm47XG5cbiAgICB0aGlzLm9jY3VwYW50c1tvY2N1cGFudElkXSA9IHN1YnNjcmliZXI7XG5cbiAgICB0aGlzLnNldE1lZGlhU3RyZWFtKG9jY3VwYW50SWQsIHN1YnNjcmliZXIubWVkaWFTdHJlYW0pO1xuXG4gICAgLy8gQ2FsbCB0aGUgTmV0d29ya2VkIEFGcmFtZSBjYWxsYmFja3MgZm9yIHRoZSBuZXcgb2NjdXBhbnQuXG4gICAgdGhpcy5vbk9jY3VwYW50Q29ubmVjdGVkKG9jY3VwYW50SWQpO1xuICAgIHRoaXMub25PY2N1cGFudHNDaGFuZ2VkKHRoaXMub2NjdXBhbnRzKTtcblxuICAgIHJldHVybiBzdWJzY3JpYmVyO1xuICB9XG5cbiAgcmVtb3ZlQWxsT2NjdXBhbnRzKCkge1xuICAgIGZvciAoY29uc3Qgb2NjdXBhbnRJZCBvZiBPYmplY3QuZ2V0T3duUHJvcGVydHlOYW1lcyh0aGlzLm9jY3VwYW50cykpIHtcbiAgICAgIHRoaXMucmVtb3ZlT2NjdXBhbnQob2NjdXBhbnRJZCk7XG4gICAgfVxuICB9XG5cbiAgcmVtb3ZlT2NjdXBhbnQob2NjdXBhbnRJZCkge1xuICAgIHRoaXMubGVmdE9jY3VwYW50cy5hZGQob2NjdXBhbnRJZCk7XG5cbiAgICBpZiAodGhpcy5vY2N1cGFudHNbb2NjdXBhbnRJZF0pIHtcbiAgICAgIC8vIENsb3NlIHRoZSBzdWJzY3JpYmVyIHBlZXIgY29ubmVjdGlvbi4gV2hpY2ggYWxzbyBkZXRhY2hlcyB0aGUgcGx1Z2luIGhhbmRsZS5cbiAgICAgIHRoaXMub2NjdXBhbnRzW29jY3VwYW50SWRdLmNvbm4uY2xvc2UoKTtcbiAgICAgIGRlbGV0ZSB0aGlzLm9jY3VwYW50c1tvY2N1cGFudElkXTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5tZWRpYVN0cmVhbXNbb2NjdXBhbnRJZF0pIHtcbiAgICAgIGRlbGV0ZSB0aGlzLm1lZGlhU3RyZWFtc1tvY2N1cGFudElkXTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5oYXMob2NjdXBhbnRJZCkpIHtcbiAgICAgIGNvbnN0IG1zZyA9IFwiVGhlIHVzZXIgZGlzY29ubmVjdGVkIGJlZm9yZSB0aGUgbWVkaWEgc3RyZWFtIHdhcyByZXNvbHZlZC5cIjtcbiAgICAgIHRoaXMucGVuZGluZ01lZGlhUmVxdWVzdHMuZ2V0KG9jY3VwYW50SWQpLmF1ZGlvLnJlamVjdChtc2cpO1xuICAgICAgdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5nZXQob2NjdXBhbnRJZCkudmlkZW8ucmVqZWN0KG1zZyk7XG4gICAgICB0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmRlbGV0ZShvY2N1cGFudElkKTtcbiAgICB9XG5cbiAgICAvLyBDYWxsIHRoZSBOZXR3b3JrZWQgQUZyYW1lIGNhbGxiYWNrcyBmb3IgdGhlIHJlbW92ZWQgb2NjdXBhbnQuXG4gICAgdGhpcy5vbk9jY3VwYW50RGlzY29ubmVjdGVkKG9jY3VwYW50SWQpO1xuICAgIHRoaXMub25PY2N1cGFudHNDaGFuZ2VkKHRoaXMub2NjdXBhbnRzKTtcbiAgfVxuXG4gIGFzc29jaWF0ZShjb25uLCBoYW5kbGUpIHtcbiAgICBjb25uLmFkZEV2ZW50TGlzdGVuZXIoXCJpY2VjYW5kaWRhdGVcIiwgZXYgPT4ge1xuICAgICAgaGFuZGxlLnNlbmRUcmlja2xlKGV2LmNhbmRpZGF0ZSB8fCBudWxsKS5jYXRjaChlID0+IGVycm9yKFwiRXJyb3IgdHJpY2tsaW5nIElDRTogJW9cIiwgZSkpO1xuICAgIH0pO1xuICAgIGNvbm4uYWRkRXZlbnRMaXN0ZW5lcihcImljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZVwiLCBldiA9PiB7XG4gICAgICBpZiAoY29ubi5pY2VDb25uZWN0aW9uU3RhdGUgPT09IFwiY29ubmVjdGVkXCIpIHtcbiAgICAgICAgY29uc29sZS5sb2coXCJJQ0Ugc3RhdGUgY2hhbmdlZCB0byBjb25uZWN0ZWRcIik7XG4gICAgICB9XG4gICAgICBpZiAoY29ubi5pY2VDb25uZWN0aW9uU3RhdGUgPT09IFwiZGlzY29ubmVjdGVkXCIpIHtcbiAgICAgICAgY29uc29sZS53YXJuKFwiSUNFIHN0YXRlIGNoYW5nZWQgdG8gZGlzY29ubmVjdGVkXCIpO1xuICAgICAgfVxuICAgICAgaWYgKGNvbm4uaWNlQ29ubmVjdGlvblN0YXRlID09PSBcImZhaWxlZFwiKSB7XG4gICAgICAgIGNvbnNvbGUud2FybihcIklDRSBmYWlsdXJlIGRldGVjdGVkLiBSZWNvbm5lY3RpbmcgaW4gMTBzLlwiKTtcbiAgICAgICAgdGhpcy5wZXJmb3JtRGVsYXllZFJlY29ubmVjdCgpO1xuICAgICAgfVxuICAgIH0pXG5cbiAgICAvLyB3ZSBoYXZlIHRvIGRlYm91bmNlIHRoZXNlIGJlY2F1c2UgamFudXMgZ2V0cyBhbmdyeSBpZiB5b3Ugc2VuZCBpdCBhIG5ldyBTRFAgYmVmb3JlXG4gICAgLy8gaXQncyBmaW5pc2hlZCBwcm9jZXNzaW5nIGFuIGV4aXN0aW5nIFNEUC4gaW4gYWN0dWFsaXR5LCBpdCBzZWVtcyBsaWtlIHRoaXMgaXMgbWF5YmVcbiAgICAvLyB0b28gbGliZXJhbCBhbmQgd2UgbmVlZCB0byB3YWl0IHNvbWUgYW1vdW50IG9mIHRpbWUgYWZ0ZXIgYW4gb2ZmZXIgYmVmb3JlIHNlbmRpbmcgYW5vdGhlcixcbiAgICAvLyBidXQgd2UgZG9uJ3QgY3VycmVudGx5IGtub3cgYW55IGdvb2Qgd2F5IG9mIGRldGVjdGluZyBleGFjdGx5IGhvdyBsb25nIDooXG4gICAgY29ubi5hZGRFdmVudExpc3RlbmVyKFxuICAgICAgXCJuZWdvdGlhdGlvbm5lZWRlZFwiLFxuICAgICAgZGVib3VuY2UoZXYgPT4ge1xuICAgICAgICBkZWJ1ZyhcIlNlbmRpbmcgbmV3IG9mZmVyIGZvciBoYW5kbGU6ICVvXCIsIGhhbmRsZSk7XG4gICAgICAgIHZhciBvZmZlciA9IGNvbm4uY3JlYXRlT2ZmZXIoKS50aGVuKHRoaXMuY29uZmlndXJlUHVibGlzaGVyU2RwKS50aGVuKHRoaXMuZml4U2FmYXJpSWNlVUZyYWcpO1xuICAgICAgICB2YXIgbG9jYWwgPSBvZmZlci50aGVuKG8gPT4gY29ubi5zZXRMb2NhbERlc2NyaXB0aW9uKG8pKTtcbiAgICAgICAgdmFyIHJlbW90ZSA9IG9mZmVyO1xuXG4gICAgICAgIHJlbW90ZSA9IHJlbW90ZVxuICAgICAgICAgIC50aGVuKHRoaXMuZml4U2FmYXJpSWNlVUZyYWcpXG4gICAgICAgICAgLnRoZW4oaiA9PiBoYW5kbGUuc2VuZEpzZXAoaikpXG4gICAgICAgICAgLnRoZW4ociA9PiBjb25uLnNldFJlbW90ZURlc2NyaXB0aW9uKHIuanNlcCkpO1xuICAgICAgICByZXR1cm4gUHJvbWlzZS5hbGwoW2xvY2FsLCByZW1vdGVdKS5jYXRjaChlID0+IGVycm9yKFwiRXJyb3IgbmVnb3RpYXRpbmcgb2ZmZXI6ICVvXCIsIGUpKTtcbiAgICAgIH0pXG4gICAgKTtcbiAgICBoYW5kbGUub24oXG4gICAgICBcImV2ZW50XCIsXG4gICAgICBkZWJvdW5jZShldiA9PiB7XG4gICAgICAgIHZhciBqc2VwID0gZXYuanNlcDtcbiAgICAgICAgaWYgKGpzZXAgJiYganNlcC50eXBlID09IFwib2ZmZXJcIikge1xuICAgICAgICAgIGRlYnVnKFwiQWNjZXB0aW5nIG5ldyBvZmZlciBmb3IgaGFuZGxlOiAlb1wiLCBoYW5kbGUpO1xuICAgICAgICAgIHZhciBhbnN3ZXIgPSBjb25uXG4gICAgICAgICAgICAuc2V0UmVtb3RlRGVzY3JpcHRpb24odGhpcy5jb25maWd1cmVTdWJzY3JpYmVyU2RwKGpzZXApKVxuICAgICAgICAgICAgLnRoZW4oXyA9PiBjb25uLmNyZWF0ZUFuc3dlcigpKVxuICAgICAgICAgICAgLnRoZW4odGhpcy5maXhTYWZhcmlJY2VVRnJhZyk7XG4gICAgICAgICAgdmFyIGxvY2FsID0gYW5zd2VyLnRoZW4oYSA9PiBjb25uLnNldExvY2FsRGVzY3JpcHRpb24oYSkpO1xuICAgICAgICAgIHZhciByZW1vdGUgPSBhbnN3ZXIudGhlbihqID0+IGhhbmRsZS5zZW5kSnNlcChqKSk7XG4gICAgICAgICAgcmV0dXJuIFByb21pc2UuYWxsKFtsb2NhbCwgcmVtb3RlXSkuY2F0Y2goZSA9PiBlcnJvcihcIkVycm9yIG5lZ290aWF0aW5nIGFuc3dlcjogJW9cIiwgZSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIHNvbWUgb3RoZXIga2luZCBvZiBldmVudCwgbm90aGluZyB0byBkb1xuICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICB9XG4gICAgICB9KVxuICAgICk7XG4gIH1cblxuICBhc3luYyBjcmVhdGVQdWJsaXNoZXIoKSB7XG4gICAgdmFyIGhhbmRsZSA9IG5ldyBtai5KYW51c1BsdWdpbkhhbmRsZSh0aGlzLnNlc3Npb24pO1xuICAgIHZhciBjb25uID0gbmV3IFJUQ1BlZXJDb25uZWN0aW9uKHRoaXMucGVlckNvbm5lY3Rpb25Db25maWcgfHwgREVGQVVMVF9QRUVSX0NPTk5FQ1RJT05fQ09ORklHKTtcblxuICAgIGRlYnVnKFwicHViIHdhaXRpbmcgZm9yIHNmdVwiKTtcbiAgICBhd2FpdCBoYW5kbGUuYXR0YWNoKFwiamFudXMucGx1Z2luLnNmdVwiLCB0aGlzLmxvb3BzICYmIHRoaXMuY2xpZW50SWQgPyBwYXJzZUludCh0aGlzLmNsaWVudElkKSAlIHRoaXMubG9vcHMgOiB1bmRlZmluZWQpO1xuXG4gICAgdGhpcy5hc3NvY2lhdGUoY29ubiwgaGFuZGxlKTtcblxuICAgIGRlYnVnKFwicHViIHdhaXRpbmcgZm9yIGRhdGEgY2hhbm5lbHMgJiB3ZWJydGN1cFwiKTtcbiAgICB2YXIgd2VicnRjdXAgPSBuZXcgUHJvbWlzZShyZXNvbHZlID0+IGhhbmRsZS5vbihcIndlYnJ0Y3VwXCIsIHJlc29sdmUpKTtcblxuICAgIC8vIFVucmVsaWFibGUgZGF0YWNoYW5uZWw6IHNlbmRpbmcgYW5kIHJlY2VpdmluZyBjb21wb25lbnQgdXBkYXRlcy5cbiAgICAvLyBSZWxpYWJsZSBkYXRhY2hhbm5lbDogc2VuZGluZyBhbmQgcmVjaWV2aW5nIGVudGl0eSBpbnN0YW50aWF0aW9ucy5cbiAgICB2YXIgcmVsaWFibGVDaGFubmVsID0gY29ubi5jcmVhdGVEYXRhQ2hhbm5lbChcInJlbGlhYmxlXCIsIHsgb3JkZXJlZDogdHJ1ZSB9KTtcbiAgICB2YXIgdW5yZWxpYWJsZUNoYW5uZWwgPSBjb25uLmNyZWF0ZURhdGFDaGFubmVsKFwidW5yZWxpYWJsZVwiLCB7XG4gICAgICBvcmRlcmVkOiBmYWxzZSxcbiAgICAgIG1heFJldHJhbnNtaXRzOiAwXG4gICAgfSk7XG5cbiAgICByZWxpYWJsZUNoYW5uZWwuYWRkRXZlbnRMaXN0ZW5lcihcIm1lc3NhZ2VcIiwgZSA9PiB0aGlzLm9uRGF0YUNoYW5uZWxNZXNzYWdlKGUsIFwiamFudXMtcmVsaWFibGVcIikpO1xuICAgIHVucmVsaWFibGVDaGFubmVsLmFkZEV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsIGUgPT4gdGhpcy5vbkRhdGFDaGFubmVsTWVzc2FnZShlLCBcImphbnVzLXVucmVsaWFibGVcIikpO1xuXG4gICAgYXdhaXQgd2VicnRjdXA7XG4gICAgYXdhaXQgdW50aWxEYXRhQ2hhbm5lbE9wZW4ocmVsaWFibGVDaGFubmVsKTtcbiAgICBhd2FpdCB1bnRpbERhdGFDaGFubmVsT3Blbih1bnJlbGlhYmxlQ2hhbm5lbCk7XG5cbiAgICAvLyBkb2luZyB0aGlzIGhlcmUgaXMgc29ydCBvZiBhIGhhY2sgYXJvdW5kIGNocm9tZSByZW5lZ290aWF0aW9uIHdlaXJkbmVzcyAtLVxuICAgIC8vIGlmIHdlIGRvIGl0IHByaW9yIHRvIHdlYnJ0Y3VwLCBjaHJvbWUgb24gZ2VhciBWUiB3aWxsIHNvbWV0aW1lcyBwdXQgYVxuICAgIC8vIHJlbmVnb3RpYXRpb24gb2ZmZXIgaW4gZmxpZ2h0IHdoaWxlIHRoZSBmaXJzdCBvZmZlciB3YXMgc3RpbGwgYmVpbmdcbiAgICAvLyBwcm9jZXNzZWQgYnkgamFudXMuIHdlIHNob3VsZCBmaW5kIHNvbWUgbW9yZSBwcmluY2lwbGVkIHdheSB0byBmaWd1cmUgb3V0XG4gICAgLy8gd2hlbiBqYW51cyBpcyBkb25lIGluIHRoZSBmdXR1cmUuXG4gICAgaWYgKHRoaXMubG9jYWxNZWRpYVN0cmVhbSkge1xuICAgICAgdGhpcy5sb2NhbE1lZGlhU3RyZWFtLmdldFRyYWNrcygpLmZvckVhY2godHJhY2sgPT4ge1xuICAgICAgICBjb25uLmFkZFRyYWNrKHRyYWNrLCB0aGlzLmxvY2FsTWVkaWFTdHJlYW0pO1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gSGFuZGxlIGFsbCBvZiB0aGUgam9pbiBhbmQgbGVhdmUgZXZlbnRzLlxuICAgIGhhbmRsZS5vbihcImV2ZW50XCIsIGV2ID0+IHtcbiAgICAgIHZhciBkYXRhID0gZXYucGx1Z2luZGF0YS5kYXRhO1xuICAgICAgaWYgKGRhdGEuZXZlbnQgPT0gXCJqb2luXCIgJiYgZGF0YS5yb29tX2lkID09IHRoaXMucm9vbSkge1xuICAgICAgICBpZiAodGhpcy5kZWxheWVkUmVjb25uZWN0VGltZW91dCkge1xuICAgICAgICAgIC8vIERvbid0IGNyZWF0ZSBhIG5ldyBSVENQZWVyQ29ubmVjdGlvbiwgYWxsIFJUQ1BlZXJDb25uZWN0aW9uIHdpbGwgYmUgY2xvc2VkIGluIGxlc3MgdGhhbiAxMHMuXG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuYWRkT2NjdXBhbnQoZGF0YS51c2VyX2lkKTtcbiAgICAgIH0gZWxzZSBpZiAoZGF0YS5ldmVudCA9PSBcImxlYXZlXCIgJiYgZGF0YS5yb29tX2lkID09IHRoaXMucm9vbSkge1xuICAgICAgICB0aGlzLnJlbW92ZU9jY3VwYW50KGRhdGEudXNlcl9pZCk7XG4gICAgICB9IGVsc2UgaWYgKGRhdGEuZXZlbnQgPT0gXCJibG9ja2VkXCIpIHtcbiAgICAgICAgZG9jdW1lbnQuYm9keS5kaXNwYXRjaEV2ZW50KG5ldyBDdXN0b21FdmVudChcImJsb2NrZWRcIiwgeyBkZXRhaWw6IHsgY2xpZW50SWQ6IGRhdGEuYnkgfSB9KSk7XG4gICAgICB9IGVsc2UgaWYgKGRhdGEuZXZlbnQgPT0gXCJ1bmJsb2NrZWRcIikge1xuICAgICAgICBkb2N1bWVudC5ib2R5LmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KFwidW5ibG9ja2VkXCIsIHsgZGV0YWlsOiB7IGNsaWVudElkOiBkYXRhLmJ5IH0gfSkpO1xuICAgICAgfSBlbHNlIGlmIChkYXRhLmV2ZW50ID09PSBcImRhdGFcIikge1xuICAgICAgICB0aGlzLm9uRGF0YShKU09OLnBhcnNlKGRhdGEuYm9keSksIFwiamFudXMtZXZlbnRcIik7XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICBkZWJ1ZyhcInB1YiB3YWl0aW5nIGZvciBqb2luXCIpO1xuXG4gICAgLy8gU2VuZCBqb2luIG1lc3NhZ2UgdG8gamFudXMuIExpc3RlbiBmb3Igam9pbi9sZWF2ZSBtZXNzYWdlcy4gQXV0b21hdGljYWxseSBzdWJzY3JpYmUgdG8gYWxsIHVzZXJzJyBXZWJSVEMgZGF0YS5cbiAgICB2YXIgbWVzc2FnZSA9IGF3YWl0IHRoaXMuc2VuZEpvaW4oaGFuZGxlLCB7XG4gICAgICBub3RpZmljYXRpb25zOiB0cnVlLFxuICAgICAgZGF0YTogdHJ1ZVxuICAgIH0pO1xuXG4gICAgaWYgKCFtZXNzYWdlLnBsdWdpbmRhdGEuZGF0YS5zdWNjZXNzKSB7XG4gICAgICBjb25zdCBlcnIgPSBtZXNzYWdlLnBsdWdpbmRhdGEuZGF0YS5lcnJvcjtcbiAgICAgIGNvbnNvbGUuZXJyb3IoZXJyKTtcbiAgICAgIC8vIFdlIG1heSBnZXQgaGVyZSBiZWNhdXNlIG9mIGFuIGV4cGlyZWQgSldULlxuICAgICAgLy8gQ2xvc2UgdGhlIGNvbm5lY3Rpb24gb3Vyc2VsZiBvdGhlcndpc2UgamFudXMgd2lsbCBjbG9zZSBpdCBhZnRlclxuICAgICAgLy8gc2Vzc2lvbl90aW1lb3V0IGJlY2F1c2Ugd2UgZGlkbid0IHNlbmQgYW55IGtlZXBhbGl2ZSBhbmQgdGhpcyB3aWxsXG4gICAgICAvLyB0cmlnZ2VyIGEgZGVsYXllZCByZWNvbm5lY3QgYmVjYXVzZSBvZiB0aGUgaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlXG4gICAgICAvLyBsaXN0ZW5lciBmb3IgZmFpbHVyZSBzdGF0ZS5cbiAgICAgIC8vIEV2ZW4gaWYgdGhlIGFwcCBjb2RlIGNhbGxzIGRpc2Nvbm5lY3QgaW4gY2FzZSBvZiBlcnJvciwgZGlzY29ubmVjdFxuICAgICAgLy8gd29uJ3QgY2xvc2UgdGhlIHBlZXIgY29ubmVjdGlvbiBiZWNhdXNlIHRoaXMucHVibGlzaGVyIGlzIG5vdCBzZXQuXG4gICAgICBjb25uLmNsb3NlKCk7XG4gICAgICB0aHJvdyBlcnI7XG4gICAgfVxuXG4gICAgdmFyIGluaXRpYWxPY2N1cGFudHMgPSBtZXNzYWdlLnBsdWdpbmRhdGEuZGF0YS5yZXNwb25zZS51c2Vyc1t0aGlzLnJvb21dIHx8IFtdO1xuXG4gICAgaWYgKGluaXRpYWxPY2N1cGFudHMuaW5jbHVkZXModGhpcy5jbGllbnRJZCkpIHtcbiAgICAgIGNvbnNvbGUud2FybihcIkphbnVzIHN0aWxsIGhhcyBwcmV2aW91cyBzZXNzaW9uIGZvciB0aGlzIGNsaWVudC4gUmVjb25uZWN0aW5nIGluIDEwcy5cIik7XG4gICAgICB0aGlzLnBlcmZvcm1EZWxheWVkUmVjb25uZWN0KCk7XG4gICAgfVxuXG4gICAgZGVidWcoXCJwdWJsaXNoZXIgcmVhZHlcIik7XG4gICAgcmV0dXJuIHtcbiAgICAgIGhhbmRsZSxcbiAgICAgIGluaXRpYWxPY2N1cGFudHMsXG4gICAgICByZWxpYWJsZUNoYW5uZWwsXG4gICAgICB1bnJlbGlhYmxlQ2hhbm5lbCxcbiAgICAgIGNvbm5cbiAgICB9O1xuICB9XG5cbiAgY29uZmlndXJlUHVibGlzaGVyU2RwKGpzZXApIHtcbiAgICBqc2VwLnNkcCA9IGpzZXAuc2RwLnJlcGxhY2UoL2E9Zm10cDooMTA5fDExMSkuKlxcclxcbi9nLCAobGluZSwgcHQpID0+IHtcbiAgICAgIGNvbnN0IHBhcmFtZXRlcnMgPSBPYmplY3QuYXNzaWduKHNkcFV0aWxzLnBhcnNlRm10cChsaW5lKSwgT1BVU19QQVJBTUVURVJTKTtcbiAgICAgIHJldHVybiBzZHBVdGlscy53cml0ZUZtdHAoeyBwYXlsb2FkVHlwZTogcHQsIHBhcmFtZXRlcnM6IHBhcmFtZXRlcnMgfSk7XG4gICAgfSk7XG4gICAgcmV0dXJuIGpzZXA7XG4gIH1cblxuICBjb25maWd1cmVTdWJzY3JpYmVyU2RwKGpzZXApIHtcbiAgICAvLyB0b2RvOiBjb25zaWRlciBjbGVhbmluZyB1cCB0aGVzZSBoYWNrcyB0byB1c2Ugc2RwdXRpbHNcbiAgICBpZiAoIWlzSDI2NFZpZGVvU3VwcG9ydGVkKSB7XG4gICAgICBpZiAobmF2aWdhdG9yLnVzZXJBZ2VudC5pbmRleE9mKFwiSGVhZGxlc3NDaHJvbWVcIikgIT09IC0xKSB7XG4gICAgICAgIC8vIEhlYWRsZXNzQ2hyb21lIChlLmcuIHB1cHBldGVlcikgZG9lc24ndCBzdXBwb3J0IHdlYnJ0YyB2aWRlbyBzdHJlYW1zLCBzbyB3ZSByZW1vdmUgdGhvc2UgbGluZXMgZnJvbSB0aGUgU0RQLlxuICAgICAgICBqc2VwLnNkcCA9IGpzZXAuc2RwLnJlcGxhY2UoL209dmlkZW9bXl0qbT0vLCBcIm09XCIpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIFRPRE86IEhhY2sgdG8gZ2V0IHZpZGVvIHdvcmtpbmcgb24gQ2hyb21lIGZvciBBbmRyb2lkLiBodHRwczovL2dyb3Vwcy5nb29nbGUuY29tL2ZvcnVtLyMhdG9waWMvbW96aWxsYS5kZXYubWVkaWEvWWUyOXZ1TVRwbzhcbiAgICBpZiAobmF2aWdhdG9yLnVzZXJBZ2VudC5pbmRleE9mKFwiQW5kcm9pZFwiKSA9PT0gLTEpIHtcbiAgICAgIGpzZXAuc2RwID0ganNlcC5zZHAucmVwbGFjZShcbiAgICAgICAgXCJhPXJ0Y3AtZmI6MTA3IGdvb2ctcmVtYlxcclxcblwiLFxuICAgICAgICBcImE9cnRjcC1mYjoxMDcgZ29vZy1yZW1iXFxyXFxuYT1ydGNwLWZiOjEwNyB0cmFuc3BvcnQtY2NcXHJcXG5hPWZtdHA6MTA3IGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTE7cGFja2V0aXphdGlvbi1tb2RlPTE7cHJvZmlsZS1sZXZlbC1pZD00MmUwMWZcXHJcXG5cIlxuICAgICAgKTtcbiAgICB9IGVsc2Uge1xuICAgICAganNlcC5zZHAgPSBqc2VwLnNkcC5yZXBsYWNlKFxuICAgICAgICBcImE9cnRjcC1mYjoxMDcgZ29vZy1yZW1iXFxyXFxuXCIsXG4gICAgICAgIFwiYT1ydGNwLWZiOjEwNyBnb29nLXJlbWJcXHJcXG5hPXJ0Y3AtZmI6MTA3IHRyYW5zcG9ydC1jY1xcclxcbmE9Zm10cDoxMDcgbGV2ZWwtYXN5bW1ldHJ5LWFsbG93ZWQ9MTtwYWNrZXRpemF0aW9uLW1vZGU9MTtwcm9maWxlLWxldmVsLWlkPTQyMDAxZlxcclxcblwiXG4gICAgICApO1xuICAgIH1cbiAgICByZXR1cm4ganNlcDtcbiAgfVxuXG4gIGFzeW5jIGZpeFNhZmFyaUljZVVGcmFnKGpzZXApIHtcbiAgICAvLyBTYWZhcmkgcHJvZHVjZXMgYSBcXG4gaW5zdGVhZCBvZiBhbiBcXHJcXG4gZm9yIHRoZSBpY2UtdWZyYWcuIFNlZSBodHRwczovL2dpdGh1Yi5jb20vbWVldGVjaG8vamFudXMtZ2F0ZXdheS9pc3N1ZXMvMTgxOFxuICAgIGpzZXAuc2RwID0ganNlcC5zZHAucmVwbGFjZSgvW15cXHJdXFxuYT1pY2UtdWZyYWcvZywgXCJcXHJcXG5hPWljZS11ZnJhZ1wiKTtcbiAgICByZXR1cm4ganNlcFxuICB9XG5cbiAgYXN5bmMgY3JlYXRlU3Vic2NyaWJlcihvY2N1cGFudElkLCBtYXhSZXRyaWVzID0gNSkge1xuICAgIGlmICh0aGlzLmxlZnRPY2N1cGFudHMuaGFzKG9jY3VwYW50SWQpKSB7XG4gICAgICBjb25zb2xlLndhcm4ob2NjdXBhbnRJZCArIFwiOiBjYW5jZWxsZWQgb2NjdXBhbnQgY29ubmVjdGlvbiwgb2NjdXBhbnQgbGVmdCBiZWZvcmUgc3Vic2NyaXB0aW9uIG5lZ290YXRpb24uXCIpO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgdmFyIGhhbmRsZSA9IG5ldyBtai5KYW51c1BsdWdpbkhhbmRsZSh0aGlzLnNlc3Npb24pO1xuICAgIHZhciBjb25uID0gbmV3IFJUQ1BlZXJDb25uZWN0aW9uKHRoaXMucGVlckNvbm5lY3Rpb25Db25maWcgfHwgREVGQVVMVF9QRUVSX0NPTk5FQ1RJT05fQ09ORklHKTtcblxuICAgIGRlYnVnKG9jY3VwYW50SWQgKyBcIjogc3ViIHdhaXRpbmcgZm9yIHNmdVwiKTtcbiAgICBhd2FpdCBoYW5kbGUuYXR0YWNoKFwiamFudXMucGx1Z2luLnNmdVwiLCB0aGlzLmxvb3BzID8gcGFyc2VJbnQob2NjdXBhbnRJZCkgJSB0aGlzLmxvb3BzIDogdW5kZWZpbmVkKTtcblxuICAgIHRoaXMuYXNzb2NpYXRlKGNvbm4sIGhhbmRsZSk7XG5cbiAgICBkZWJ1ZyhvY2N1cGFudElkICsgXCI6IHN1YiB3YWl0aW5nIGZvciBqb2luXCIpO1xuXG4gICAgaWYgKHRoaXMubGVmdE9jY3VwYW50cy5oYXMob2NjdXBhbnRJZCkpIHtcbiAgICAgIGNvbm4uY2xvc2UoKTtcbiAgICAgIGNvbnNvbGUud2FybihvY2N1cGFudElkICsgXCI6IGNhbmNlbGxlZCBvY2N1cGFudCBjb25uZWN0aW9uLCBvY2N1cGFudCBsZWZ0IGFmdGVyIGF0dGFjaFwiKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGxldCB3ZWJydGNGYWlsZWQgPSBmYWxzZTtcblxuICAgIGNvbnN0IHdlYnJ0Y3VwID0gbmV3IFByb21pc2UocmVzb2x2ZSA9PiB7XG4gICAgICBjb25zdCBsZWZ0SW50ZXJ2YWwgPSBzZXRJbnRlcnZhbCgoKSA9PiB7XG4gICAgICAgIGlmICh0aGlzLmxlZnRPY2N1cGFudHMuaGFzKG9jY3VwYW50SWQpKSB7XG4gICAgICAgICAgY2xlYXJJbnRlcnZhbChsZWZ0SW50ZXJ2YWwpO1xuICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgfVxuICAgICAgfSwgMTAwMCk7XG5cbiAgICAgIGNvbnN0IHRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgY2xlYXJJbnRlcnZhbChsZWZ0SW50ZXJ2YWwpO1xuICAgICAgICB3ZWJydGNGYWlsZWQgPSB0cnVlO1xuICAgICAgICByZXNvbHZlKCk7XG4gICAgICB9LCBTVUJTQ1JJQkVfVElNRU9VVF9NUyk7XG5cbiAgICAgIGhhbmRsZS5vbihcIndlYnJ0Y3VwXCIsICgpID0+IHtcbiAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXQpO1xuICAgICAgICBjbGVhckludGVydmFsKGxlZnRJbnRlcnZhbCk7XG4gICAgICAgIHJlc29sdmUoKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgLy8gU2VuZCBqb2luIG1lc3NhZ2UgdG8gamFudXMuIERvbid0IGxpc3RlbiBmb3Igam9pbi9sZWF2ZSBtZXNzYWdlcy4gU3Vic2NyaWJlIHRvIHRoZSBvY2N1cGFudCdzIG1lZGlhLlxuICAgIC8vIEphbnVzIHNob3VsZCBzZW5kIHVzIGFuIG9mZmVyIGZvciB0aGlzIG9jY3VwYW50J3MgbWVkaWEgaW4gcmVzcG9uc2UgdG8gdGhpcy5cbiAgICBhd2FpdCB0aGlzLnNlbmRKb2luKGhhbmRsZSwgeyBtZWRpYTogb2NjdXBhbnRJZCB9KTtcblxuICAgIGlmICh0aGlzLmxlZnRPY2N1cGFudHMuaGFzKG9jY3VwYW50SWQpKSB7XG4gICAgICBjb25uLmNsb3NlKCk7XG4gICAgICBjb25zb2xlLndhcm4ob2NjdXBhbnRJZCArIFwiOiBjYW5jZWxsZWQgb2NjdXBhbnQgY29ubmVjdGlvbiwgb2NjdXBhbnQgbGVmdCBhZnRlciBqb2luXCIpO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgZGVidWcob2NjdXBhbnRJZCArIFwiOiBzdWIgd2FpdGluZyBmb3Igd2VicnRjdXBcIik7XG4gICAgYXdhaXQgd2VicnRjdXA7XG5cbiAgICBpZiAodGhpcy5sZWZ0T2NjdXBhbnRzLmhhcyhvY2N1cGFudElkKSkge1xuICAgICAgY29ubi5jbG9zZSgpO1xuICAgICAgY29uc29sZS53YXJuKG9jY3VwYW50SWQgKyBcIjogY2FuY2VsIG9jY3VwYW50IGNvbm5lY3Rpb24sIG9jY3VwYW50IGxlZnQgZHVyaW5nIG9yIGFmdGVyIHdlYnJ0Y3VwXCIpO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgaWYgKHdlYnJ0Y0ZhaWxlZCkge1xuICAgICAgY29ubi5jbG9zZSgpO1xuICAgICAgaWYgKG1heFJldHJpZXMgPiAwKSB7XG4gICAgICAgIGNvbnNvbGUud2FybihvY2N1cGFudElkICsgXCI6IHdlYnJ0YyB1cCB0aW1lZCBvdXQsIHJldHJ5aW5nXCIpO1xuICAgICAgICByZXR1cm4gdGhpcy5jcmVhdGVTdWJzY3JpYmVyKG9jY3VwYW50SWQsIG1heFJldHJpZXMgLSAxKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnNvbGUud2FybihvY2N1cGFudElkICsgXCI6IHdlYnJ0YyB1cCB0aW1lZCBvdXRcIik7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChpc1NhZmFyaSAmJiAhdGhpcy5faU9TSGFja0RlbGF5ZWRJbml0aWFsUGVlcikge1xuICAgICAgLy8gSEFDSzogdGhlIGZpcnN0IHBlZXIgb24gU2FmYXJpIGR1cmluZyBwYWdlIGxvYWQgY2FuIGZhaWwgdG8gd29yayBpZiB3ZSBkb24ndFxuICAgICAgLy8gd2FpdCBzb21lIHRpbWUgYmVmb3JlIGNvbnRpbnVpbmcgaGVyZS4gU2VlOiBodHRwczovL2dpdGh1Yi5jb20vbW96aWxsYS9odWJzL3B1bGwvMTY5MlxuICAgICAgYXdhaXQgKG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIDMwMDApKSk7XG4gICAgICB0aGlzLl9pT1NIYWNrRGVsYXllZEluaXRpYWxQZWVyID0gdHJ1ZTtcbiAgICB9XG5cbiAgICB2YXIgbWVkaWFTdHJlYW0gPSBuZXcgTWVkaWFTdHJlYW0oKTtcbiAgICB2YXIgcmVjZWl2ZXJzID0gY29ubi5nZXRSZWNlaXZlcnMoKTtcbiAgICByZWNlaXZlcnMuZm9yRWFjaChyZWNlaXZlciA9PiB7XG4gICAgICBpZiAocmVjZWl2ZXIudHJhY2spIHtcbiAgICAgICAgbWVkaWFTdHJlYW0uYWRkVHJhY2socmVjZWl2ZXIudHJhY2spO1xuICAgICAgfVxuICAgIH0pO1xuICAgIGlmIChtZWRpYVN0cmVhbS5nZXRUcmFja3MoKS5sZW5ndGggPT09IDApIHtcbiAgICAgIG1lZGlhU3RyZWFtID0gbnVsbDtcbiAgICB9XG5cbiAgICBkZWJ1ZyhvY2N1cGFudElkICsgXCI6IHN1YnNjcmliZXIgcmVhZHlcIik7XG4gICAgcmV0dXJuIHtcbiAgICAgIGhhbmRsZSxcbiAgICAgIG1lZGlhU3RyZWFtLFxuICAgICAgY29ublxuICAgIH07XG4gIH1cblxuICBzZW5kSm9pbihoYW5kbGUsIHN1YnNjcmliZSkge1xuICAgIHJldHVybiBoYW5kbGUuc2VuZE1lc3NhZ2Uoe1xuICAgICAga2luZDogXCJqb2luXCIsXG4gICAgICByb29tX2lkOiB0aGlzLnJvb20sXG4gICAgICB1c2VyX2lkOiB0aGlzLmNsaWVudElkLFxuICAgICAgc3Vic2NyaWJlLFxuICAgICAgdG9rZW46IHRoaXMuam9pblRva2VuXG4gICAgfSk7XG4gIH1cblxuICB0b2dnbGVGcmVlemUoKSB7XG4gICAgaWYgKHRoaXMuZnJvemVuKSB7XG4gICAgICB0aGlzLnVuZnJlZXplKCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuZnJlZXplKCk7XG4gICAgfVxuICB9XG5cbiAgZnJlZXplKCkge1xuICAgIHRoaXMuZnJvemVuID0gdHJ1ZTtcbiAgfVxuXG4gIHVuZnJlZXplKCkge1xuICAgIHRoaXMuZnJvemVuID0gZmFsc2U7XG4gICAgdGhpcy5mbHVzaFBlbmRpbmdVcGRhdGVzKCk7XG4gIH1cblxuICBkYXRhRm9yVXBkYXRlTXVsdGlNZXNzYWdlKG5ldHdvcmtJZCwgbWVzc2FnZSkge1xuICAgIC8vIFwiZFwiIGlzIGFuIGFycmF5IG9mIGVudGl0eSBkYXRhcywgd2hlcmUgZWFjaCBpdGVtIGluIHRoZSBhcnJheSByZXByZXNlbnRzIGEgdW5pcXVlIGVudGl0eSBhbmQgY29udGFpbnNcbiAgICAvLyBtZXRhZGF0YSBmb3IgdGhlIGVudGl0eSwgYW5kIGFuIGFycmF5IG9mIGNvbXBvbmVudHMgdGhhdCBoYXZlIGJlZW4gdXBkYXRlZCBvbiB0aGUgZW50aXR5LlxuICAgIC8vIFRoaXMgbWV0aG9kIGZpbmRzIHRoZSBkYXRhIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGdpdmVuIG5ldHdvcmtJZC5cbiAgICBmb3IgKGxldCBpID0gMCwgbCA9IG1lc3NhZ2UuZGF0YS5kLmxlbmd0aDsgaSA8IGw7IGkrKykge1xuICAgICAgY29uc3QgZGF0YSA9IG1lc3NhZ2UuZGF0YS5kW2ldO1xuXG4gICAgICBpZiAoZGF0YS5uZXR3b3JrSWQgPT09IG5ldHdvcmtJZCkge1xuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIGdldFBlbmRpbmdEYXRhKG5ldHdvcmtJZCwgbWVzc2FnZSkge1xuICAgIGlmICghbWVzc2FnZSkgcmV0dXJuIG51bGw7XG5cbiAgICBsZXQgZGF0YSA9IG1lc3NhZ2UuZGF0YVR5cGUgPT09IFwidW1cIiA/IHRoaXMuZGF0YUZvclVwZGF0ZU11bHRpTWVzc2FnZShuZXR3b3JrSWQsIG1lc3NhZ2UpIDogbWVzc2FnZS5kYXRhO1xuXG4gICAgLy8gSWdub3JlIG1lc3NhZ2VzIHJlbGF0aW5nIHRvIHVzZXJzIHdobyBoYXZlIGRpc2Nvbm5lY3RlZCBzaW5jZSBmcmVlemluZywgdGhlaXIgZW50aXRpZXNcbiAgICAvLyB3aWxsIGhhdmUgYWxlYWR5IGJlZW4gcmVtb3ZlZCBieSBOQUYuXG4gICAgLy8gTm90ZSB0aGF0IGRlbGV0ZSBtZXNzYWdlcyBoYXZlIG5vIFwib3duZXJcIiBzbyB3ZSBoYXZlIHRvIGNoZWNrIGZvciB0aGF0IGFzIHdlbGwuXG4gICAgaWYgKGRhdGEub3duZXIgJiYgIXRoaXMub2NjdXBhbnRzW2RhdGEub3duZXJdKSByZXR1cm4gbnVsbDtcblxuICAgIC8vIElnbm9yZSBtZXNzYWdlcyBmcm9tIHVzZXJzIHRoYXQgd2UgbWF5IGhhdmUgYmxvY2tlZCB3aGlsZSBmcm96ZW4uXG4gICAgaWYgKGRhdGEub3duZXIgJiYgdGhpcy5ibG9ja2VkQ2xpZW50cy5oYXMoZGF0YS5vd25lcikpIHJldHVybiBudWxsO1xuXG4gICAgcmV0dXJuIGRhdGFcbiAgfVxuXG4gIC8vIFVzZWQgZXh0ZXJuYWxseVxuICBnZXRQZW5kaW5nRGF0YUZvck5ldHdvcmtJZChuZXR3b3JrSWQpIHtcbiAgICByZXR1cm4gdGhpcy5nZXRQZW5kaW5nRGF0YShuZXR3b3JrSWQsIHRoaXMuZnJvemVuVXBkYXRlcy5nZXQobmV0d29ya0lkKSk7XG4gIH1cblxuICBmbHVzaFBlbmRpbmdVcGRhdGVzKCkge1xuICAgIGZvciAoY29uc3QgW25ldHdvcmtJZCwgbWVzc2FnZV0gb2YgdGhpcy5mcm96ZW5VcGRhdGVzKSB7XG4gICAgICBsZXQgZGF0YSA9IHRoaXMuZ2V0UGVuZGluZ0RhdGEobmV0d29ya0lkLCBtZXNzYWdlKTtcbiAgICAgIGlmICghZGF0YSkgY29udGludWU7XG5cbiAgICAgIC8vIE92ZXJyaWRlIHRoZSBkYXRhIHR5cGUgb24gXCJ1bVwiIG1lc3NhZ2VzIHR5cGVzLCBzaW5jZSB3ZSBleHRyYWN0IGVudGl0eSB1cGRhdGVzIGZyb20gXCJ1bVwiIG1lc3NhZ2VzIGludG9cbiAgICAgIC8vIGluZGl2aWR1YWwgZnJvemVuVXBkYXRlcyBpbiBzdG9yZVNpbmdsZU1lc3NhZ2UuXG4gICAgICBjb25zdCBkYXRhVHlwZSA9IG1lc3NhZ2UuZGF0YVR5cGUgPT09IFwidW1cIiA/IFwidVwiIDogbWVzc2FnZS5kYXRhVHlwZTtcblxuICAgICAgdGhpcy5vbk9jY3VwYW50TWVzc2FnZShudWxsLCBkYXRhVHlwZSwgZGF0YSwgbWVzc2FnZS5zb3VyY2UpO1xuICAgIH1cbiAgICB0aGlzLmZyb3plblVwZGF0ZXMuY2xlYXIoKTtcbiAgfVxuXG4gIHN0b3JlTWVzc2FnZShtZXNzYWdlKSB7XG4gICAgaWYgKG1lc3NhZ2UuZGF0YVR5cGUgPT09IFwidW1cIikgeyAvLyBVcGRhdGVNdWx0aVxuICAgICAgZm9yIChsZXQgaSA9IDAsIGwgPSBtZXNzYWdlLmRhdGEuZC5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAgICAgdGhpcy5zdG9yZVNpbmdsZU1lc3NhZ2UobWVzc2FnZSwgaSk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuc3RvcmVTaW5nbGVNZXNzYWdlKG1lc3NhZ2UpO1xuICAgIH1cbiAgfVxuXG4gIHN0b3JlU2luZ2xlTWVzc2FnZShtZXNzYWdlLCBpbmRleCkge1xuICAgIGNvbnN0IGRhdGEgPSBpbmRleCAhPT0gdW5kZWZpbmVkID8gbWVzc2FnZS5kYXRhLmRbaW5kZXhdIDogbWVzc2FnZS5kYXRhO1xuICAgIGNvbnN0IGRhdGFUeXBlID0gbWVzc2FnZS5kYXRhVHlwZTtcbiAgICBjb25zdCBzb3VyY2UgPSBtZXNzYWdlLnNvdXJjZTtcblxuICAgIGNvbnN0IG5ldHdvcmtJZCA9IGRhdGEubmV0d29ya0lkO1xuXG4gICAgaWYgKCF0aGlzLmZyb3plblVwZGF0ZXMuaGFzKG5ldHdvcmtJZCkpIHtcbiAgICAgIHRoaXMuZnJvemVuVXBkYXRlcy5zZXQobmV0d29ya0lkLCBtZXNzYWdlKTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3Qgc3RvcmVkTWVzc2FnZSA9IHRoaXMuZnJvemVuVXBkYXRlcy5nZXQobmV0d29ya0lkKTtcbiAgICAgIGNvbnN0IHN0b3JlZERhdGEgPSBzdG9yZWRNZXNzYWdlLmRhdGFUeXBlID09PSBcInVtXCIgPyB0aGlzLmRhdGFGb3JVcGRhdGVNdWx0aU1lc3NhZ2UobmV0d29ya0lkLCBzdG9yZWRNZXNzYWdlKSA6IHN0b3JlZE1lc3NhZ2UuZGF0YTtcblxuICAgICAgLy8gQXZvaWQgdXBkYXRpbmcgY29tcG9uZW50cyBpZiB0aGUgZW50aXR5IGRhdGEgcmVjZWl2ZWQgZGlkIG5vdCBjb21lIGZyb20gdGhlIGN1cnJlbnQgb3duZXIuXG4gICAgICBjb25zdCBpc091dGRhdGVkTWVzc2FnZSA9IGRhdGEubGFzdE93bmVyVGltZSA8IHN0b3JlZERhdGEubGFzdE93bmVyVGltZTtcbiAgICAgIGNvbnN0IGlzQ29udGVtcG9yYW5lb3VzTWVzc2FnZSA9IGRhdGEubGFzdE93bmVyVGltZSA9PT0gc3RvcmVkRGF0YS5sYXN0T3duZXJUaW1lO1xuICAgICAgaWYgKGlzT3V0ZGF0ZWRNZXNzYWdlIHx8IChpc0NvbnRlbXBvcmFuZW91c01lc3NhZ2UgJiYgc3RvcmVkRGF0YS5vd25lciA+IGRhdGEub3duZXIpKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgaWYgKGRhdGFUeXBlID09PSBcInJcIikge1xuICAgICAgICBjb25zdCBjcmVhdGVkV2hpbGVGcm96ZW4gPSBzdG9yZWREYXRhICYmIHN0b3JlZERhdGEuaXNGaXJzdFN5bmM7XG4gICAgICAgIGlmIChjcmVhdGVkV2hpbGVGcm96ZW4pIHtcbiAgICAgICAgICAvLyBJZiB0aGUgZW50aXR5IHdhcyBjcmVhdGVkIGFuZCBkZWxldGVkIHdoaWxlIGZyb3plbiwgZG9uJ3QgYm90aGVyIGNvbnZleWluZyBhbnl0aGluZyB0byB0aGUgY29uc3VtZXIuXG4gICAgICAgICAgdGhpcy5mcm96ZW5VcGRhdGVzLmRlbGV0ZShuZXR3b3JrSWQpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIERlbGV0ZSBtZXNzYWdlcyBvdmVycmlkZSBhbnkgb3RoZXIgbWVzc2FnZXMgZm9yIHRoaXMgZW50aXR5XG4gICAgICAgICAgdGhpcy5mcm96ZW5VcGRhdGVzLnNldChuZXR3b3JrSWQsIG1lc3NhZ2UpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBtZXJnZSBpbiBjb21wb25lbnQgdXBkYXRlc1xuICAgICAgICBpZiAoc3RvcmVkRGF0YS5jb21wb25lbnRzICYmIGRhdGEuY29tcG9uZW50cykge1xuICAgICAgICAgIE9iamVjdC5hc3NpZ24oc3RvcmVkRGF0YS5jb21wb25lbnRzLCBkYXRhLmNvbXBvbmVudHMpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgb25EYXRhQ2hhbm5lbE1lc3NhZ2UoZSwgc291cmNlKSB7XG4gICAgdGhpcy5vbkRhdGEoSlNPTi5wYXJzZShlLmRhdGEpLCBzb3VyY2UpO1xuICB9XG5cbiAgb25EYXRhKG1lc3NhZ2UsIHNvdXJjZSkge1xuICAgIGlmIChkZWJ1Zy5lbmFibGVkKSB7XG4gICAgICBkZWJ1ZyhgREMgaW46ICR7bWVzc2FnZX1gKTtcbiAgICB9XG5cbiAgICBpZiAoIW1lc3NhZ2UuZGF0YVR5cGUpIHJldHVybjtcblxuICAgIG1lc3NhZ2Uuc291cmNlID0gc291cmNlO1xuXG4gICAgaWYgKHRoaXMuZnJvemVuKSB7XG4gICAgICB0aGlzLnN0b3JlTWVzc2FnZShtZXNzYWdlKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5vbk9jY3VwYW50TWVzc2FnZShudWxsLCBtZXNzYWdlLmRhdGFUeXBlLCBtZXNzYWdlLmRhdGEsIG1lc3NhZ2Uuc291cmNlKTtcbiAgICB9XG4gIH1cblxuICBzaG91bGRTdGFydENvbm5lY3Rpb25UbyhjbGllbnQpIHtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIHN0YXJ0U3RyZWFtQ29ubmVjdGlvbihjbGllbnQpIHt9XG5cbiAgY2xvc2VTdHJlYW1Db25uZWN0aW9uKGNsaWVudCkge31cblxuICBnZXRDb25uZWN0U3RhdHVzKGNsaWVudElkKSB7XG4gICAgcmV0dXJuIHRoaXMub2NjdXBhbnRzW2NsaWVudElkXSA/IE5BRi5hZGFwdGVycy5JU19DT05ORUNURUQgOiBOQUYuYWRhcHRlcnMuTk9UX0NPTk5FQ1RFRDtcbiAgfVxuXG4gIGFzeW5jIHVwZGF0ZVRpbWVPZmZzZXQoKSB7XG4gICAgaWYgKHRoaXMuaXNEaXNjb25uZWN0ZWQoKSkgcmV0dXJuO1xuXG4gICAgY29uc3QgY2xpZW50U2VudFRpbWUgPSBEYXRlLm5vdygpO1xuXG4gICAgY29uc3QgcmVzID0gYXdhaXQgZmV0Y2goZG9jdW1lbnQubG9jYXRpb24uaHJlZiwge1xuICAgICAgbWV0aG9kOiBcIkhFQURcIixcbiAgICAgIGNhY2hlOiBcIm5vLWNhY2hlXCJcbiAgICB9KTtcblxuICAgIGNvbnN0IHByZWNpc2lvbiA9IDEwMDA7XG4gICAgY29uc3Qgc2VydmVyUmVjZWl2ZWRUaW1lID0gbmV3IERhdGUocmVzLmhlYWRlcnMuZ2V0KFwiRGF0ZVwiKSkuZ2V0VGltZSgpICsgcHJlY2lzaW9uIC8gMjtcbiAgICBjb25zdCBjbGllbnRSZWNlaXZlZFRpbWUgPSBEYXRlLm5vdygpO1xuICAgIGNvbnN0IHNlcnZlclRpbWUgPSBzZXJ2ZXJSZWNlaXZlZFRpbWUgKyAoY2xpZW50UmVjZWl2ZWRUaW1lIC0gY2xpZW50U2VudFRpbWUpIC8gMjtcbiAgICBjb25zdCB0aW1lT2Zmc2V0ID0gc2VydmVyVGltZSAtIGNsaWVudFJlY2VpdmVkVGltZTtcblxuICAgIHRoaXMuc2VydmVyVGltZVJlcXVlc3RzKys7XG5cbiAgICBpZiAodGhpcy5zZXJ2ZXJUaW1lUmVxdWVzdHMgPD0gMTApIHtcbiAgICAgIHRoaXMudGltZU9mZnNldHMucHVzaCh0aW1lT2Zmc2V0KTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy50aW1lT2Zmc2V0c1t0aGlzLnNlcnZlclRpbWVSZXF1ZXN0cyAlIDEwXSA9IHRpbWVPZmZzZXQ7XG4gICAgfVxuXG4gICAgdGhpcy5hdmdUaW1lT2Zmc2V0ID0gdGhpcy50aW1lT2Zmc2V0cy5yZWR1Y2UoKGFjYywgb2Zmc2V0KSA9PiAoYWNjICs9IG9mZnNldCksIDApIC8gdGhpcy50aW1lT2Zmc2V0cy5sZW5ndGg7XG5cbiAgICBpZiAodGhpcy5zZXJ2ZXJUaW1lUmVxdWVzdHMgPiAxMCkge1xuICAgICAgZGVidWcoYG5ldyBzZXJ2ZXIgdGltZSBvZmZzZXQ6ICR7dGhpcy5hdmdUaW1lT2Zmc2V0fW1zYCk7XG4gICAgICBzZXRUaW1lb3V0KCgpID0+IHRoaXMudXBkYXRlVGltZU9mZnNldCgpLCA1ICogNjAgKiAxMDAwKTsgLy8gU3luYyBjbG9jayBldmVyeSA1IG1pbnV0ZXMuXG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMudXBkYXRlVGltZU9mZnNldCgpO1xuICAgIH1cbiAgfVxuXG4gIGdldFNlcnZlclRpbWUoKSB7XG4gICAgcmV0dXJuIERhdGUubm93KCkgKyB0aGlzLmF2Z1RpbWVPZmZzZXQ7XG4gIH1cblxuICBnZXRNZWRpYVN0cmVhbShjbGllbnRJZCwgdHlwZSA9IFwiYXVkaW9cIikge1xuICAgIGlmICh0aGlzLm1lZGlhU3RyZWFtc1tjbGllbnRJZF0pIHtcbiAgICAgIGRlYnVnKGBBbHJlYWR5IGhhZCAke3R5cGV9IGZvciAke2NsaWVudElkfWApO1xuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSh0aGlzLm1lZGlhU3RyZWFtc1tjbGllbnRJZF1bdHlwZV0pO1xuICAgIH0gZWxzZSB7XG4gICAgICBkZWJ1ZyhgV2FpdGluZyBvbiAke3R5cGV9IGZvciAke2NsaWVudElkfWApO1xuICAgICAgaWYgKCF0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmhhcyhjbGllbnRJZCkpIHtcbiAgICAgICAgdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5zZXQoY2xpZW50SWQsIHt9KTtcblxuICAgICAgICBjb25zdCBhdWRpb1Byb21pc2UgPSBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5nZXQoY2xpZW50SWQpLmF1ZGlvID0geyByZXNvbHZlLCByZWplY3QgfTtcbiAgICAgICAgfSk7XG4gICAgICAgIGNvbnN0IHZpZGVvUHJvbWlzZSA9IG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICB0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmdldChjbGllbnRJZCkudmlkZW8gPSB7IHJlc29sdmUsIHJlamVjdCB9O1xuICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmdldChjbGllbnRJZCkuYXVkaW8ucHJvbWlzZSA9IGF1ZGlvUHJvbWlzZTtcbiAgICAgICAgdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5nZXQoY2xpZW50SWQpLnZpZGVvLnByb21pc2UgPSB2aWRlb1Byb21pc2U7XG5cbiAgICAgICAgYXVkaW9Qcm9taXNlLmNhdGNoKGUgPT4gY29uc29sZS53YXJuKGAke2NsaWVudElkfSBnZXRNZWRpYVN0cmVhbSBBdWRpbyBFcnJvcmAsIGUpKTtcbiAgICAgICAgdmlkZW9Qcm9taXNlLmNhdGNoKGUgPT4gY29uc29sZS53YXJuKGAke2NsaWVudElkfSBnZXRNZWRpYVN0cmVhbSBWaWRlbyBFcnJvcmAsIGUpKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmdldChjbGllbnRJZClbdHlwZV0ucHJvbWlzZTtcbiAgICB9XG4gIH1cblxuICBzZXRNZWRpYVN0cmVhbShjbGllbnRJZCwgc3RyZWFtKSB7XG4gICAgLy8gU2FmYXJpIGRvZXNuJ3QgbGlrZSBpdCB3aGVuIHlvdSB1c2Ugc2luZ2xlIGEgbWl4ZWQgbWVkaWEgc3RyZWFtIHdoZXJlIG9uZSBvZiB0aGUgdHJhY2tzIGlzIGluYWN0aXZlLCBzbyB3ZVxuICAgIC8vIHNwbGl0IHRoZSB0cmFja3MgaW50byB0d28gc3RyZWFtcy5cbiAgICBjb25zdCBhdWRpb1N0cmVhbSA9IG5ldyBNZWRpYVN0cmVhbSgpO1xuICAgIHRyeSB7XG4gICAgc3RyZWFtLmdldEF1ZGlvVHJhY2tzKCkuZm9yRWFjaCh0cmFjayA9PiBhdWRpb1N0cmVhbS5hZGRUcmFjayh0cmFjaykpO1xuXG4gICAgfSBjYXRjaChlKSB7XG4gICAgICBjb25zb2xlLndhcm4oYCR7Y2xpZW50SWR9IHNldE1lZGlhU3RyZWFtIEF1ZGlvIEVycm9yYCwgZSk7XG4gICAgfVxuICAgIGNvbnN0IHZpZGVvU3RyZWFtID0gbmV3IE1lZGlhU3RyZWFtKCk7XG4gICAgdHJ5IHtcbiAgICBzdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5mb3JFYWNoKHRyYWNrID0+IHZpZGVvU3RyZWFtLmFkZFRyYWNrKHRyYWNrKSk7XG5cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBjb25zb2xlLndhcm4oYCR7Y2xpZW50SWR9IHNldE1lZGlhU3RyZWFtIFZpZGVvIEVycm9yYCwgZSk7XG4gICAgfVxuXG4gICAgdGhpcy5tZWRpYVN0cmVhbXNbY2xpZW50SWRdID0geyBhdWRpbzogYXVkaW9TdHJlYW0sIHZpZGVvOiB2aWRlb1N0cmVhbSB9O1xuXG4gICAgLy8gUmVzb2x2ZSB0aGUgcHJvbWlzZSBmb3IgdGhlIHVzZXIncyBtZWRpYSBzdHJlYW0gaWYgaXQgZXhpc3RzLlxuICAgIGlmICh0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmhhcyhjbGllbnRJZCkpIHtcbiAgICAgIHRoaXMucGVuZGluZ01lZGlhUmVxdWVzdHMuZ2V0KGNsaWVudElkKS5hdWRpby5yZXNvbHZlKGF1ZGlvU3RyZWFtKTtcbiAgICAgIHRoaXMucGVuZGluZ01lZGlhUmVxdWVzdHMuZ2V0KGNsaWVudElkKS52aWRlby5yZXNvbHZlKHZpZGVvU3RyZWFtKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBzZXRMb2NhbE1lZGlhU3RyZWFtKHN0cmVhbSkge1xuICAgIC8vIG91ciBqb2IgaGVyZSBpcyB0byBtYWtlIHN1cmUgdGhlIGNvbm5lY3Rpb24gd2luZHMgdXAgd2l0aCBSVFAgc2VuZGVycyBzZW5kaW5nIHRoZSBzdHVmZiBpbiB0aGlzIHN0cmVhbSxcbiAgICAvLyBhbmQgbm90IHRoZSBzdHVmZiB0aGF0IGlzbid0IGluIHRoaXMgc3RyZWFtLiBzdHJhdGVneSBpcyB0byByZXBsYWNlIGV4aXN0aW5nIHRyYWNrcyBpZiB3ZSBjYW4sIGFkZCB0cmFja3NcbiAgICAvLyB0aGF0IHdlIGNhbid0IHJlcGxhY2UsIGFuZCBkaXNhYmxlIHRyYWNrcyB0aGF0IGRvbid0IGV4aXN0IGFueW1vcmUuXG5cbiAgICAvLyBub3RlIHRoYXQgd2UgZG9uJ3QgZXZlciByZW1vdmUgYSB0cmFjayBmcm9tIHRoZSBzdHJlYW0gLS0gc2luY2UgSmFudXMgZG9lc24ndCBzdXBwb3J0IFVuaWZpZWQgUGxhbiwgd2UgYWJzb2x1dGVseVxuICAgIC8vIGNhbid0IHdpbmQgdXAgd2l0aCBhIFNEUCB0aGF0IGhhcyA+MSBhdWRpbyBvciA+MSB2aWRlbyB0cmFja3MsIGV2ZW4gaWYgb25lIG9mIHRoZW0gaXMgaW5hY3RpdmUgKHdoYXQgeW91IGdldCBpZlxuICAgIC8vIHlvdSByZW1vdmUgYSB0cmFjayBmcm9tIGFuIGV4aXN0aW5nIHN0cmVhbS4pXG4gICAgaWYgKHRoaXMucHVibGlzaGVyICYmIHRoaXMucHVibGlzaGVyLmNvbm4pIHtcbiAgICAgIGNvbnN0IGV4aXN0aW5nU2VuZGVycyA9IHRoaXMucHVibGlzaGVyLmNvbm4uZ2V0U2VuZGVycygpO1xuICAgICAgY29uc3QgbmV3U2VuZGVycyA9IFtdO1xuICAgICAgY29uc3QgdHJhY2tzID0gc3RyZWFtLmdldFRyYWNrcygpO1xuXG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRyYWNrcy5sZW5ndGg7IGkrKykge1xuICAgICAgICBjb25zdCB0ID0gdHJhY2tzW2ldO1xuICAgICAgICBjb25zdCBzZW5kZXIgPSBleGlzdGluZ1NlbmRlcnMuZmluZChzID0+IHMudHJhY2sgIT0gbnVsbCAmJiBzLnRyYWNrLmtpbmQgPT0gdC5raW5kKTtcblxuICAgICAgICBpZiAoc2VuZGVyICE9IG51bGwpIHtcbiAgICAgICAgICBpZiAoc2VuZGVyLnJlcGxhY2VUcmFjaykge1xuICAgICAgICAgICAgYXdhaXQgc2VuZGVyLnJlcGxhY2VUcmFjayh0KTtcblxuICAgICAgICAgICAgLy8gV29ya2Fyb3VuZCBodHRwczovL2J1Z3ppbGxhLm1vemlsbGEub3JnL3Nob3dfYnVnLmNnaT9pZD0xNTc2NzcxXG4gICAgICAgICAgICBpZiAodC5raW5kID09PSBcInZpZGVvXCIgJiYgdC5lbmFibGVkICYmIG5hdmlnYXRvci51c2VyQWdlbnQudG9Mb3dlckNhc2UoKS5pbmRleE9mKCdmaXJlZm94JykgPiAtMSkge1xuICAgICAgICAgICAgICB0LmVuYWJsZWQgPSBmYWxzZTtcbiAgICAgICAgICAgICAgc2V0VGltZW91dCgoKSA9PiB0LmVuYWJsZWQgPSB0cnVlLCAxMDAwKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gRmFsbGJhY2sgZm9yIGJyb3dzZXJzIHRoYXQgZG9uJ3Qgc3VwcG9ydCByZXBsYWNlVHJhY2suIEF0IHRoaXMgdGltZSBvZiB0aGlzIHdyaXRpbmdcbiAgICAgICAgICAgIC8vIG1vc3QgYnJvd3NlcnMgc3VwcG9ydCBpdCwgYW5kIHRlc3RpbmcgdGhpcyBjb2RlIHBhdGggc2VlbXMgdG8gbm90IHdvcmsgcHJvcGVybHlcbiAgICAgICAgICAgIC8vIGluIENocm9tZSBhbnltb3JlLlxuICAgICAgICAgICAgc3RyZWFtLnJlbW92ZVRyYWNrKHNlbmRlci50cmFjayk7XG4gICAgICAgICAgICBzdHJlYW0uYWRkVHJhY2sodCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIG5ld1NlbmRlcnMucHVzaChzZW5kZXIpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIG5ld1NlbmRlcnMucHVzaCh0aGlzLnB1Ymxpc2hlci5jb25uLmFkZFRyYWNrKHQsIHN0cmVhbSkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBleGlzdGluZ1NlbmRlcnMuZm9yRWFjaChzID0+IHtcbiAgICAgICAgaWYgKCFuZXdTZW5kZXJzLmluY2x1ZGVzKHMpKSB7XG4gICAgICAgICAgcy50cmFjay5lbmFibGVkID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cbiAgICB0aGlzLmxvY2FsTWVkaWFTdHJlYW0gPSBzdHJlYW07XG4gICAgdGhpcy5zZXRNZWRpYVN0cmVhbSh0aGlzLmNsaWVudElkLCBzdHJlYW0pO1xuICB9XG5cbiAgZW5hYmxlTWljcm9waG9uZShlbmFibGVkKSB7XG4gICAgaWYgKHRoaXMucHVibGlzaGVyICYmIHRoaXMucHVibGlzaGVyLmNvbm4pIHtcbiAgICAgIHRoaXMucHVibGlzaGVyLmNvbm4uZ2V0U2VuZGVycygpLmZvckVhY2gocyA9PiB7XG4gICAgICAgIGlmIChzLnRyYWNrLmtpbmQgPT0gXCJhdWRpb1wiKSB7XG4gICAgICAgICAgcy50cmFjay5lbmFibGVkID0gZW5hYmxlZDtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgc2VuZERhdGEoY2xpZW50SWQsIGRhdGFUeXBlLCBkYXRhKSB7XG4gICAgaWYgKCF0aGlzLnB1Ymxpc2hlcikge1xuICAgICAgY29uc29sZS53YXJuKFwic2VuZERhdGEgY2FsbGVkIHdpdGhvdXQgYSBwdWJsaXNoZXJcIik7XG4gICAgfSBlbHNlIHtcbiAgICAgIHN3aXRjaCAodGhpcy51bnJlbGlhYmxlVHJhbnNwb3J0KSB7XG4gICAgICAgIGNhc2UgXCJ3ZWJzb2NrZXRcIjpcbiAgICAgICAgICB0aGlzLnB1Ymxpc2hlci5oYW5kbGUuc2VuZE1lc3NhZ2UoeyBraW5kOiBcImRhdGFcIiwgYm9keTogSlNPTi5zdHJpbmdpZnkoeyBkYXRhVHlwZSwgZGF0YSB9KSwgd2hvbTogY2xpZW50SWQgfSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgXCJkYXRhY2hhbm5lbFwiOlxuICAgICAgICAgIHRoaXMucHVibGlzaGVyLnVucmVsaWFibGVDaGFubmVsLnNlbmQoSlNPTi5zdHJpbmdpZnkoeyBjbGllbnRJZCwgZGF0YVR5cGUsIGRhdGEgfSkpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIHRoaXMudW5yZWxpYWJsZVRyYW5zcG9ydChjbGllbnRJZCwgZGF0YVR5cGUsIGRhdGEpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHNlbmREYXRhR3VhcmFudGVlZChjbGllbnRJZCwgZGF0YVR5cGUsIGRhdGEpIHtcbiAgICBpZiAoIXRoaXMucHVibGlzaGVyKSB7XG4gICAgICBjb25zb2xlLndhcm4oXCJzZW5kRGF0YUd1YXJhbnRlZWQgY2FsbGVkIHdpdGhvdXQgYSBwdWJsaXNoZXJcIik7XG4gICAgfSBlbHNlIHtcbiAgICAgIHN3aXRjaCAodGhpcy5yZWxpYWJsZVRyYW5zcG9ydCkge1xuICAgICAgICBjYXNlIFwid2Vic29ja2V0XCI6XG4gICAgICAgICAgdGhpcy5wdWJsaXNoZXIuaGFuZGxlLnNlbmRNZXNzYWdlKHsga2luZDogXCJkYXRhXCIsIGJvZHk6IEpTT04uc3RyaW5naWZ5KHsgZGF0YVR5cGUsIGRhdGEgfSksIHdob206IGNsaWVudElkIH0pO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwiZGF0YWNoYW5uZWxcIjpcbiAgICAgICAgICB0aGlzLnB1Ymxpc2hlci5yZWxpYWJsZUNoYW5uZWwuc2VuZChKU09OLnN0cmluZ2lmeSh7IGNsaWVudElkLCBkYXRhVHlwZSwgZGF0YSB9KSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgdGhpcy5yZWxpYWJsZVRyYW5zcG9ydChjbGllbnRJZCwgZGF0YVR5cGUsIGRhdGEpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGJyb2FkY2FzdERhdGEoZGF0YVR5cGUsIGRhdGEpIHtcbiAgICBpZiAoIXRoaXMucHVibGlzaGVyKSB7XG4gICAgICBjb25zb2xlLndhcm4oXCJicm9hZGNhc3REYXRhIGNhbGxlZCB3aXRob3V0IGEgcHVibGlzaGVyXCIpO1xuICAgIH0gZWxzZSB7XG4gICAgICBzd2l0Y2ggKHRoaXMudW5yZWxpYWJsZVRyYW5zcG9ydCkge1xuICAgICAgICBjYXNlIFwid2Vic29ja2V0XCI6XG4gICAgICAgICAgdGhpcy5wdWJsaXNoZXIuaGFuZGxlLnNlbmRNZXNzYWdlKHsga2luZDogXCJkYXRhXCIsIGJvZHk6IEpTT04uc3RyaW5naWZ5KHsgZGF0YVR5cGUsIGRhdGEgfSkgfSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgXCJkYXRhY2hhbm5lbFwiOlxuICAgICAgICAgIHRoaXMucHVibGlzaGVyLnVucmVsaWFibGVDaGFubmVsLnNlbmQoSlNPTi5zdHJpbmdpZnkoeyBkYXRhVHlwZSwgZGF0YSB9KSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgdGhpcy51bnJlbGlhYmxlVHJhbnNwb3J0KHVuZGVmaW5lZCwgZGF0YVR5cGUsIGRhdGEpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGJyb2FkY2FzdERhdGFHdWFyYW50ZWVkKGRhdGFUeXBlLCBkYXRhKSB7XG4gICAgaWYgKCF0aGlzLnB1Ymxpc2hlcikge1xuICAgICAgY29uc29sZS53YXJuKFwiYnJvYWRjYXN0RGF0YUd1YXJhbnRlZWQgY2FsbGVkIHdpdGhvdXQgYSBwdWJsaXNoZXJcIik7XG4gICAgfSBlbHNlIHtcbiAgICAgIHN3aXRjaCAodGhpcy5yZWxpYWJsZVRyYW5zcG9ydCkge1xuICAgICAgICBjYXNlIFwid2Vic29ja2V0XCI6XG4gICAgICAgICAgdGhpcy5wdWJsaXNoZXIuaGFuZGxlLnNlbmRNZXNzYWdlKHsga2luZDogXCJkYXRhXCIsIGJvZHk6IEpTT04uc3RyaW5naWZ5KHsgZGF0YVR5cGUsIGRhdGEgfSkgfSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgXCJkYXRhY2hhbm5lbFwiOlxuICAgICAgICAgIHRoaXMucHVibGlzaGVyLnJlbGlhYmxlQ2hhbm5lbC5zZW5kKEpTT04uc3RyaW5naWZ5KHsgZGF0YVR5cGUsIGRhdGEgfSkpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIHRoaXMucmVsaWFibGVUcmFuc3BvcnQodW5kZWZpbmVkLCBkYXRhVHlwZSwgZGF0YSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAga2ljayhjbGllbnRJZCwgcGVybXNUb2tlbikge1xuICAgIHJldHVybiB0aGlzLnB1Ymxpc2hlci5oYW5kbGUuc2VuZE1lc3NhZ2UoeyBraW5kOiBcImtpY2tcIiwgcm9vbV9pZDogdGhpcy5yb29tLCB1c2VyX2lkOiBjbGllbnRJZCwgdG9rZW46IHBlcm1zVG9rZW4gfSkudGhlbigoKSA9PiB7XG4gICAgICBkb2N1bWVudC5ib2R5LmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KFwia2lja2VkXCIsIHsgZGV0YWlsOiB7IGNsaWVudElkOiBjbGllbnRJZCB9IH0pKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJsb2NrKGNsaWVudElkKSB7XG4gICAgcmV0dXJuIHRoaXMucHVibGlzaGVyLmhhbmRsZS5zZW5kTWVzc2FnZSh7IGtpbmQ6IFwiYmxvY2tcIiwgd2hvbTogY2xpZW50SWQgfSkudGhlbigoKSA9PiB7XG4gICAgICB0aGlzLmJsb2NrZWRDbGllbnRzLnNldChjbGllbnRJZCwgdHJ1ZSk7XG4gICAgICBkb2N1bWVudC5ib2R5LmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KFwiYmxvY2tlZFwiLCB7IGRldGFpbDogeyBjbGllbnRJZDogY2xpZW50SWQgfSB9KSk7XG4gICAgfSk7XG4gIH1cblxuICB1bmJsb2NrKGNsaWVudElkKSB7XG4gICAgcmV0dXJuIHRoaXMucHVibGlzaGVyLmhhbmRsZS5zZW5kTWVzc2FnZSh7IGtpbmQ6IFwidW5ibG9ja1wiLCB3aG9tOiBjbGllbnRJZCB9KS50aGVuKCgpID0+IHtcbiAgICAgIHRoaXMuYmxvY2tlZENsaWVudHMuZGVsZXRlKGNsaWVudElkKTtcbiAgICAgIGRvY3VtZW50LmJvZHkuZGlzcGF0Y2hFdmVudChuZXcgQ3VzdG9tRXZlbnQoXCJ1bmJsb2NrZWRcIiwgeyBkZXRhaWw6IHsgY2xpZW50SWQ6IGNsaWVudElkIH0gfSkpO1xuICAgIH0pO1xuICB9XG59XG5cbk5BRi5hZGFwdGVycy5yZWdpc3RlcihcImphbnVzXCIsIEphbnVzQWRhcHRlcik7XG5cbm1vZHVsZS5leHBvcnRzID0gSmFudXNBZGFwdGVyO1xuIiwiLyogZXNsaW50LWVudiBub2RlICovXG4ndXNlIHN0cmljdCc7XG5cbi8vIFNEUCBoZWxwZXJzLlxuY29uc3QgU0RQVXRpbHMgPSB7fTtcblxuLy8gR2VuZXJhdGUgYW4gYWxwaGFudW1lcmljIGlkZW50aWZpZXIgZm9yIGNuYW1lIG9yIG1pZHMuXG4vLyBUT0RPOiB1c2UgVVVJRHMgaW5zdGVhZD8gaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vamVkLzk4Mjg4M1xuU0RQVXRpbHMuZ2VuZXJhdGVJZGVudGlmaWVyID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zdWJzdHJpbmcoMiwgMTIpO1xufTtcblxuLy8gVGhlIFJUQ1AgQ05BTUUgdXNlZCBieSBhbGwgcGVlcmNvbm5lY3Rpb25zIGZyb20gdGhlIHNhbWUgSlMuXG5TRFBVdGlscy5sb2NhbENOYW1lID0gU0RQVXRpbHMuZ2VuZXJhdGVJZGVudGlmaWVyKCk7XG5cbi8vIFNwbGl0cyBTRFAgaW50byBsaW5lcywgZGVhbGluZyB3aXRoIGJvdGggQ1JMRiBhbmQgTEYuXG5TRFBVdGlscy5zcGxpdExpbmVzID0gZnVuY3Rpb24oYmxvYikge1xuICByZXR1cm4gYmxvYi50cmltKCkuc3BsaXQoJ1xcbicpLm1hcChsaW5lID0+IGxpbmUudHJpbSgpKTtcbn07XG4vLyBTcGxpdHMgU0RQIGludG8gc2Vzc2lvbnBhcnQgYW5kIG1lZGlhc2VjdGlvbnMuIEVuc3VyZXMgQ1JMRi5cblNEUFV0aWxzLnNwbGl0U2VjdGlvbnMgPSBmdW5jdGlvbihibG9iKSB7XG4gIGNvbnN0IHBhcnRzID0gYmxvYi5zcGxpdCgnXFxubT0nKTtcbiAgcmV0dXJuIHBhcnRzLm1hcCgocGFydCwgaW5kZXgpID0+IChpbmRleCA+IDAgP1xuICAgICdtPScgKyBwYXJ0IDogcGFydCkudHJpbSgpICsgJ1xcclxcbicpO1xufTtcblxuLy8gUmV0dXJucyB0aGUgc2Vzc2lvbiBkZXNjcmlwdGlvbi5cblNEUFV0aWxzLmdldERlc2NyaXB0aW9uID0gZnVuY3Rpb24oYmxvYikge1xuICBjb25zdCBzZWN0aW9ucyA9IFNEUFV0aWxzLnNwbGl0U2VjdGlvbnMoYmxvYik7XG4gIHJldHVybiBzZWN0aW9ucyAmJiBzZWN0aW9uc1swXTtcbn07XG5cbi8vIFJldHVybnMgdGhlIGluZGl2aWR1YWwgbWVkaWEgc2VjdGlvbnMuXG5TRFBVdGlscy5nZXRNZWRpYVNlY3Rpb25zID0gZnVuY3Rpb24oYmxvYikge1xuICBjb25zdCBzZWN0aW9ucyA9IFNEUFV0aWxzLnNwbGl0U2VjdGlvbnMoYmxvYik7XG4gIHNlY3Rpb25zLnNoaWZ0KCk7XG4gIHJldHVybiBzZWN0aW9ucztcbn07XG5cbi8vIFJldHVybnMgbGluZXMgdGhhdCBzdGFydCB3aXRoIGEgY2VydGFpbiBwcmVmaXguXG5TRFBVdGlscy5tYXRjaFByZWZpeCA9IGZ1bmN0aW9uKGJsb2IsIHByZWZpeCkge1xuICByZXR1cm4gU0RQVXRpbHMuc3BsaXRMaW5lcyhibG9iKS5maWx0ZXIobGluZSA9PiBsaW5lLmluZGV4T2YocHJlZml4KSA9PT0gMCk7XG59O1xuXG4vLyBQYXJzZXMgYW4gSUNFIGNhbmRpZGF0ZSBsaW5lLiBTYW1wbGUgaW5wdXQ6XG4vLyBjYW5kaWRhdGU6NzAyNzg2MzUwIDIgdWRwIDQxODE5OTAyIDguOC44LjggNjA3NjkgdHlwIHJlbGF5IHJhZGRyIDguOC44Ljhcbi8vIHJwb3J0IDU1OTk2XCJcbi8vIElucHV0IGNhbiBiZSBwcmVmaXhlZCB3aXRoIGE9LlxuU0RQVXRpbHMucGFyc2VDYW5kaWRhdGUgPSBmdW5jdGlvbihsaW5lKSB7XG4gIGxldCBwYXJ0cztcbiAgLy8gUGFyc2UgYm90aCB2YXJpYW50cy5cbiAgaWYgKGxpbmUuaW5kZXhPZignYT1jYW5kaWRhdGU6JykgPT09IDApIHtcbiAgICBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDEyKS5zcGxpdCgnICcpO1xuICB9IGVsc2Uge1xuICAgIHBhcnRzID0gbGluZS5zdWJzdHJpbmcoMTApLnNwbGl0KCcgJyk7XG4gIH1cblxuICBjb25zdCBjYW5kaWRhdGUgPSB7XG4gICAgZm91bmRhdGlvbjogcGFydHNbMF0sXG4gICAgY29tcG9uZW50OiB7MTogJ3J0cCcsIDI6ICdydGNwJ31bcGFydHNbMV1dIHx8IHBhcnRzWzFdLFxuICAgIHByb3RvY29sOiBwYXJ0c1syXS50b0xvd2VyQ2FzZSgpLFxuICAgIHByaW9yaXR5OiBwYXJzZUludChwYXJ0c1szXSwgMTApLFxuICAgIGlwOiBwYXJ0c1s0XSxcbiAgICBhZGRyZXNzOiBwYXJ0c1s0XSwgLy8gYWRkcmVzcyBpcyBhbiBhbGlhcyBmb3IgaXAuXG4gICAgcG9ydDogcGFyc2VJbnQocGFydHNbNV0sIDEwKSxcbiAgICAvLyBza2lwIHBhcnRzWzZdID09ICd0eXAnXG4gICAgdHlwZTogcGFydHNbN10sXG4gIH07XG5cbiAgZm9yIChsZXQgaSA9IDg7IGkgPCBwYXJ0cy5sZW5ndGg7IGkgKz0gMikge1xuICAgIHN3aXRjaCAocGFydHNbaV0pIHtcbiAgICAgIGNhc2UgJ3JhZGRyJzpcbiAgICAgICAgY2FuZGlkYXRlLnJlbGF0ZWRBZGRyZXNzID0gcGFydHNbaSArIDFdO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ3Jwb3J0JzpcbiAgICAgICAgY2FuZGlkYXRlLnJlbGF0ZWRQb3J0ID0gcGFyc2VJbnQocGFydHNbaSArIDFdLCAxMCk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAndGNwdHlwZSc6XG4gICAgICAgIGNhbmRpZGF0ZS50Y3BUeXBlID0gcGFydHNbaSArIDFdO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ3VmcmFnJzpcbiAgICAgICAgY2FuZGlkYXRlLnVmcmFnID0gcGFydHNbaSArIDFdOyAvLyBmb3IgYmFja3dhcmQgY29tcGF0aWJpbGl0eS5cbiAgICAgICAgY2FuZGlkYXRlLnVzZXJuYW1lRnJhZ21lbnQgPSBwYXJ0c1tpICsgMV07XG4gICAgICAgIGJyZWFrO1xuICAgICAgZGVmYXVsdDogLy8gZXh0ZW5zaW9uIGhhbmRsaW5nLCBpbiBwYXJ0aWN1bGFyIHVmcmFnLiBEb24ndCBvdmVyd3JpdGUuXG4gICAgICAgIGlmIChjYW5kaWRhdGVbcGFydHNbaV1dID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICBjYW5kaWRhdGVbcGFydHNbaV1dID0gcGFydHNbaSArIDFdO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgIH1cbiAgfVxuICByZXR1cm4gY2FuZGlkYXRlO1xufTtcblxuLy8gVHJhbnNsYXRlcyBhIGNhbmRpZGF0ZSBvYmplY3QgaW50byBTRFAgY2FuZGlkYXRlIGF0dHJpYnV0ZS5cbi8vIFRoaXMgZG9lcyBub3QgaW5jbHVkZSB0aGUgYT0gcHJlZml4IVxuU0RQVXRpbHMud3JpdGVDYW5kaWRhdGUgPSBmdW5jdGlvbihjYW5kaWRhdGUpIHtcbiAgY29uc3Qgc2RwID0gW107XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5mb3VuZGF0aW9uKTtcblxuICBjb25zdCBjb21wb25lbnQgPSBjYW5kaWRhdGUuY29tcG9uZW50O1xuICBpZiAoY29tcG9uZW50ID09PSAncnRwJykge1xuICAgIHNkcC5wdXNoKDEpO1xuICB9IGVsc2UgaWYgKGNvbXBvbmVudCA9PT0gJ3J0Y3AnKSB7XG4gICAgc2RwLnB1c2goMik7XG4gIH0gZWxzZSB7XG4gICAgc2RwLnB1c2goY29tcG9uZW50KTtcbiAgfVxuICBzZHAucHVzaChjYW5kaWRhdGUucHJvdG9jb2wudG9VcHBlckNhc2UoKSk7XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5wcmlvcml0eSk7XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5hZGRyZXNzIHx8IGNhbmRpZGF0ZS5pcCk7XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5wb3J0KTtcblxuICBjb25zdCB0eXBlID0gY2FuZGlkYXRlLnR5cGU7XG4gIHNkcC5wdXNoKCd0eXAnKTtcbiAgc2RwLnB1c2godHlwZSk7XG4gIGlmICh0eXBlICE9PSAnaG9zdCcgJiYgY2FuZGlkYXRlLnJlbGF0ZWRBZGRyZXNzICYmXG4gICAgICBjYW5kaWRhdGUucmVsYXRlZFBvcnQpIHtcbiAgICBzZHAucHVzaCgncmFkZHInKTtcbiAgICBzZHAucHVzaChjYW5kaWRhdGUucmVsYXRlZEFkZHJlc3MpO1xuICAgIHNkcC5wdXNoKCdycG9ydCcpO1xuICAgIHNkcC5wdXNoKGNhbmRpZGF0ZS5yZWxhdGVkUG9ydCk7XG4gIH1cbiAgaWYgKGNhbmRpZGF0ZS50Y3BUeXBlICYmIGNhbmRpZGF0ZS5wcm90b2NvbC50b0xvd2VyQ2FzZSgpID09PSAndGNwJykge1xuICAgIHNkcC5wdXNoKCd0Y3B0eXBlJyk7XG4gICAgc2RwLnB1c2goY2FuZGlkYXRlLnRjcFR5cGUpO1xuICB9XG4gIGlmIChjYW5kaWRhdGUudXNlcm5hbWVGcmFnbWVudCB8fCBjYW5kaWRhdGUudWZyYWcpIHtcbiAgICBzZHAucHVzaCgndWZyYWcnKTtcbiAgICBzZHAucHVzaChjYW5kaWRhdGUudXNlcm5hbWVGcmFnbWVudCB8fCBjYW5kaWRhdGUudWZyYWcpO1xuICB9XG4gIHJldHVybiAnY2FuZGlkYXRlOicgKyBzZHAuam9pbignICcpO1xufTtcblxuLy8gUGFyc2VzIGFuIGljZS1vcHRpb25zIGxpbmUsIHJldHVybnMgYW4gYXJyYXkgb2Ygb3B0aW9uIHRhZ3MuXG4vLyBTYW1wbGUgaW5wdXQ6XG4vLyBhPWljZS1vcHRpb25zOmZvbyBiYXJcblNEUFV0aWxzLnBhcnNlSWNlT3B0aW9ucyA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgcmV0dXJuIGxpbmUuc3Vic3RyaW5nKDE0KS5zcGxpdCgnICcpO1xufTtcblxuLy8gUGFyc2VzIGEgcnRwbWFwIGxpbmUsIHJldHVybnMgUlRDUnRwQ29kZGVjUGFyYW1ldGVycy4gU2FtcGxlIGlucHV0OlxuLy8gYT1ydHBtYXA6MTExIG9wdXMvNDgwMDAvMlxuU0RQVXRpbHMucGFyc2VSdHBNYXAgPSBmdW5jdGlvbihsaW5lKSB7XG4gIGxldCBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDkpLnNwbGl0KCcgJyk7XG4gIGNvbnN0IHBhcnNlZCA9IHtcbiAgICBwYXlsb2FkVHlwZTogcGFyc2VJbnQocGFydHMuc2hpZnQoKSwgMTApLCAvLyB3YXM6IGlkXG4gIH07XG5cbiAgcGFydHMgPSBwYXJ0c1swXS5zcGxpdCgnLycpO1xuXG4gIHBhcnNlZC5uYW1lID0gcGFydHNbMF07XG4gIHBhcnNlZC5jbG9ja1JhdGUgPSBwYXJzZUludChwYXJ0c1sxXSwgMTApOyAvLyB3YXM6IGNsb2NrcmF0ZVxuICBwYXJzZWQuY2hhbm5lbHMgPSBwYXJ0cy5sZW5ndGggPT09IDMgPyBwYXJzZUludChwYXJ0c1syXSwgMTApIDogMTtcbiAgLy8gbGVnYWN5IGFsaWFzLCBnb3QgcmVuYW1lZCBiYWNrIHRvIGNoYW5uZWxzIGluIE9SVEMuXG4gIHBhcnNlZC5udW1DaGFubmVscyA9IHBhcnNlZC5jaGFubmVscztcbiAgcmV0dXJuIHBhcnNlZDtcbn07XG5cbi8vIEdlbmVyYXRlcyBhIHJ0cG1hcCBsaW5lIGZyb20gUlRDUnRwQ29kZWNDYXBhYmlsaXR5IG9yXG4vLyBSVENSdHBDb2RlY1BhcmFtZXRlcnMuXG5TRFBVdGlscy53cml0ZVJ0cE1hcCA9IGZ1bmN0aW9uKGNvZGVjKSB7XG4gIGxldCBwdCA9IGNvZGVjLnBheWxvYWRUeXBlO1xuICBpZiAoY29kZWMucHJlZmVycmVkUGF5bG9hZFR5cGUgIT09IHVuZGVmaW5lZCkge1xuICAgIHB0ID0gY29kZWMucHJlZmVycmVkUGF5bG9hZFR5cGU7XG4gIH1cbiAgY29uc3QgY2hhbm5lbHMgPSBjb2RlYy5jaGFubmVscyB8fCBjb2RlYy5udW1DaGFubmVscyB8fCAxO1xuICByZXR1cm4gJ2E9cnRwbWFwOicgKyBwdCArICcgJyArIGNvZGVjLm5hbWUgKyAnLycgKyBjb2RlYy5jbG9ja1JhdGUgK1xuICAgICAgKGNoYW5uZWxzICE9PSAxID8gJy8nICsgY2hhbm5lbHMgOiAnJykgKyAnXFxyXFxuJztcbn07XG5cbi8vIFBhcnNlcyBhIGV4dG1hcCBsaW5lIChoZWFkZXJleHRlbnNpb24gZnJvbSBSRkMgNTI4NSkuIFNhbXBsZSBpbnB1dDpcbi8vIGE9ZXh0bWFwOjIgdXJuOmlldGY6cGFyYW1zOnJ0cC1oZHJleHQ6dG9mZnNldFxuLy8gYT1leHRtYXA6Mi9zZW5kb25seSB1cm46aWV0ZjpwYXJhbXM6cnRwLWhkcmV4dDp0b2Zmc2V0XG5TRFBVdGlscy5wYXJzZUV4dG1hcCA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgY29uc3QgcGFydHMgPSBsaW5lLnN1YnN0cmluZyg5KS5zcGxpdCgnICcpO1xuICByZXR1cm4ge1xuICAgIGlkOiBwYXJzZUludChwYXJ0c1swXSwgMTApLFxuICAgIGRpcmVjdGlvbjogcGFydHNbMF0uaW5kZXhPZignLycpID4gMCA/IHBhcnRzWzBdLnNwbGl0KCcvJylbMV0gOiAnc2VuZHJlY3YnLFxuICAgIHVyaTogcGFydHNbMV0sXG4gICAgYXR0cmlidXRlczogcGFydHMuc2xpY2UoMikuam9pbignICcpLFxuICB9O1xufTtcblxuLy8gR2VuZXJhdGVzIGFuIGV4dG1hcCBsaW5lIGZyb20gUlRDUnRwSGVhZGVyRXh0ZW5zaW9uUGFyYW1ldGVycyBvclxuLy8gUlRDUnRwSGVhZGVyRXh0ZW5zaW9uLlxuU0RQVXRpbHMud3JpdGVFeHRtYXAgPSBmdW5jdGlvbihoZWFkZXJFeHRlbnNpb24pIHtcbiAgcmV0dXJuICdhPWV4dG1hcDonICsgKGhlYWRlckV4dGVuc2lvbi5pZCB8fCBoZWFkZXJFeHRlbnNpb24ucHJlZmVycmVkSWQpICtcbiAgICAgIChoZWFkZXJFeHRlbnNpb24uZGlyZWN0aW9uICYmIGhlYWRlckV4dGVuc2lvbi5kaXJlY3Rpb24gIT09ICdzZW5kcmVjdidcbiAgICAgICAgPyAnLycgKyBoZWFkZXJFeHRlbnNpb24uZGlyZWN0aW9uXG4gICAgICAgIDogJycpICtcbiAgICAgICcgJyArIGhlYWRlckV4dGVuc2lvbi51cmkgK1xuICAgICAgKGhlYWRlckV4dGVuc2lvbi5hdHRyaWJ1dGVzID8gJyAnICsgaGVhZGVyRXh0ZW5zaW9uLmF0dHJpYnV0ZXMgOiAnJykgK1xuICAgICAgJ1xcclxcbic7XG59O1xuXG4vLyBQYXJzZXMgYSBmbXRwIGxpbmUsIHJldHVybnMgZGljdGlvbmFyeS4gU2FtcGxlIGlucHV0OlxuLy8gYT1mbXRwOjk2IHZicj1vbjtjbmc9b25cbi8vIEFsc28gZGVhbHMgd2l0aCB2YnI9b247IGNuZz1vblxuU0RQVXRpbHMucGFyc2VGbXRwID0gZnVuY3Rpb24obGluZSkge1xuICBjb25zdCBwYXJzZWQgPSB7fTtcbiAgbGV0IGt2O1xuICBjb25zdCBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKGxpbmUuaW5kZXhPZignICcpICsgMSkuc3BsaXQoJzsnKTtcbiAgZm9yIChsZXQgaiA9IDA7IGogPCBwYXJ0cy5sZW5ndGg7IGorKykge1xuICAgIGt2ID0gcGFydHNbal0udHJpbSgpLnNwbGl0KCc9Jyk7XG4gICAgcGFyc2VkW2t2WzBdLnRyaW0oKV0gPSBrdlsxXTtcbiAgfVxuICByZXR1cm4gcGFyc2VkO1xufTtcblxuLy8gR2VuZXJhdGVzIGEgZm10cCBsaW5lIGZyb20gUlRDUnRwQ29kZWNDYXBhYmlsaXR5IG9yIFJUQ1J0cENvZGVjUGFyYW1ldGVycy5cblNEUFV0aWxzLndyaXRlRm10cCA9IGZ1bmN0aW9uKGNvZGVjKSB7XG4gIGxldCBsaW5lID0gJyc7XG4gIGxldCBwdCA9IGNvZGVjLnBheWxvYWRUeXBlO1xuICBpZiAoY29kZWMucHJlZmVycmVkUGF5bG9hZFR5cGUgIT09IHVuZGVmaW5lZCkge1xuICAgIHB0ID0gY29kZWMucHJlZmVycmVkUGF5bG9hZFR5cGU7XG4gIH1cbiAgaWYgKGNvZGVjLnBhcmFtZXRlcnMgJiYgT2JqZWN0LmtleXMoY29kZWMucGFyYW1ldGVycykubGVuZ3RoKSB7XG4gICAgY29uc3QgcGFyYW1zID0gW107XG4gICAgT2JqZWN0LmtleXMoY29kZWMucGFyYW1ldGVycykuZm9yRWFjaChwYXJhbSA9PiB7XG4gICAgICBpZiAoY29kZWMucGFyYW1ldGVyc1twYXJhbV0gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBwYXJhbXMucHVzaChwYXJhbSArICc9JyArIGNvZGVjLnBhcmFtZXRlcnNbcGFyYW1dKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHBhcmFtcy5wdXNoKHBhcmFtKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICBsaW5lICs9ICdhPWZtdHA6JyArIHB0ICsgJyAnICsgcGFyYW1zLmpvaW4oJzsnKSArICdcXHJcXG4nO1xuICB9XG4gIHJldHVybiBsaW5lO1xufTtcblxuLy8gUGFyc2VzIGEgcnRjcC1mYiBsaW5lLCByZXR1cm5zIFJUQ1BSdGNwRmVlZGJhY2sgb2JqZWN0LiBTYW1wbGUgaW5wdXQ6XG4vLyBhPXJ0Y3AtZmI6OTggbmFjayBycHNpXG5TRFBVdGlscy5wYXJzZVJ0Y3BGYiA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgY29uc3QgcGFydHMgPSBsaW5lLnN1YnN0cmluZyhsaW5lLmluZGV4T2YoJyAnKSArIDEpLnNwbGl0KCcgJyk7XG4gIHJldHVybiB7XG4gICAgdHlwZTogcGFydHMuc2hpZnQoKSxcbiAgICBwYXJhbWV0ZXI6IHBhcnRzLmpvaW4oJyAnKSxcbiAgfTtcbn07XG5cbi8vIEdlbmVyYXRlIGE9cnRjcC1mYiBsaW5lcyBmcm9tIFJUQ1J0cENvZGVjQ2FwYWJpbGl0eSBvciBSVENSdHBDb2RlY1BhcmFtZXRlcnMuXG5TRFBVdGlscy53cml0ZVJ0Y3BGYiA9IGZ1bmN0aW9uKGNvZGVjKSB7XG4gIGxldCBsaW5lcyA9ICcnO1xuICBsZXQgcHQgPSBjb2RlYy5wYXlsb2FkVHlwZTtcbiAgaWYgKGNvZGVjLnByZWZlcnJlZFBheWxvYWRUeXBlICE9PSB1bmRlZmluZWQpIHtcbiAgICBwdCA9IGNvZGVjLnByZWZlcnJlZFBheWxvYWRUeXBlO1xuICB9XG4gIGlmIChjb2RlYy5ydGNwRmVlZGJhY2sgJiYgY29kZWMucnRjcEZlZWRiYWNrLmxlbmd0aCkge1xuICAgIC8vIEZJWE1FOiBzcGVjaWFsIGhhbmRsaW5nIGZvciB0cnItaW50P1xuICAgIGNvZGVjLnJ0Y3BGZWVkYmFjay5mb3JFYWNoKGZiID0+IHtcbiAgICAgIGxpbmVzICs9ICdhPXJ0Y3AtZmI6JyArIHB0ICsgJyAnICsgZmIudHlwZSArXG4gICAgICAoZmIucGFyYW1ldGVyICYmIGZiLnBhcmFtZXRlci5sZW5ndGggPyAnICcgKyBmYi5wYXJhbWV0ZXIgOiAnJykgK1xuICAgICAgICAgICdcXHJcXG4nO1xuICAgIH0pO1xuICB9XG4gIHJldHVybiBsaW5lcztcbn07XG5cbi8vIFBhcnNlcyBhIFJGQyA1NTc2IHNzcmMgbWVkaWEgYXR0cmlidXRlLiBTYW1wbGUgaW5wdXQ6XG4vLyBhPXNzcmM6MzczNTkyODU1OSBjbmFtZTpzb21ldGhpbmdcblNEUFV0aWxzLnBhcnNlU3NyY01lZGlhID0gZnVuY3Rpb24obGluZSkge1xuICBjb25zdCBzcCA9IGxpbmUuaW5kZXhPZignICcpO1xuICBjb25zdCBwYXJ0cyA9IHtcbiAgICBzc3JjOiBwYXJzZUludChsaW5lLnN1YnN0cmluZyg3LCBzcCksIDEwKSxcbiAgfTtcbiAgY29uc3QgY29sb24gPSBsaW5lLmluZGV4T2YoJzonLCBzcCk7XG4gIGlmIChjb2xvbiA+IC0xKSB7XG4gICAgcGFydHMuYXR0cmlidXRlID0gbGluZS5zdWJzdHJpbmcoc3AgKyAxLCBjb2xvbik7XG4gICAgcGFydHMudmFsdWUgPSBsaW5lLnN1YnN0cmluZyhjb2xvbiArIDEpO1xuICB9IGVsc2Uge1xuICAgIHBhcnRzLmF0dHJpYnV0ZSA9IGxpbmUuc3Vic3RyaW5nKHNwICsgMSk7XG4gIH1cbiAgcmV0dXJuIHBhcnRzO1xufTtcblxuLy8gUGFyc2UgYSBzc3JjLWdyb3VwIGxpbmUgKHNlZSBSRkMgNTU3NikuIFNhbXBsZSBpbnB1dDpcbi8vIGE9c3NyYy1ncm91cDpzZW1hbnRpY3MgMTIgMzRcblNEUFV0aWxzLnBhcnNlU3NyY0dyb3VwID0gZnVuY3Rpb24obGluZSkge1xuICBjb25zdCBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDEzKS5zcGxpdCgnICcpO1xuICByZXR1cm4ge1xuICAgIHNlbWFudGljczogcGFydHMuc2hpZnQoKSxcbiAgICBzc3JjczogcGFydHMubWFwKHNzcmMgPT4gcGFyc2VJbnQoc3NyYywgMTApKSxcbiAgfTtcbn07XG5cbi8vIEV4dHJhY3RzIHRoZSBNSUQgKFJGQyA1ODg4KSBmcm9tIGEgbWVkaWEgc2VjdGlvbi5cbi8vIFJldHVybnMgdGhlIE1JRCBvciB1bmRlZmluZWQgaWYgbm8gbWlkIGxpbmUgd2FzIGZvdW5kLlxuU0RQVXRpbHMuZ2V0TWlkID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uKSB7XG4gIGNvbnN0IG1pZCA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9bWlkOicpWzBdO1xuICBpZiAobWlkKSB7XG4gICAgcmV0dXJuIG1pZC5zdWJzdHJpbmcoNik7XG4gIH1cbn07XG5cbi8vIFBhcnNlcyBhIGZpbmdlcnByaW50IGxpbmUgZm9yIERUTFMtU1JUUC5cblNEUFV0aWxzLnBhcnNlRmluZ2VycHJpbnQgPSBmdW5jdGlvbihsaW5lKSB7XG4gIGNvbnN0IHBhcnRzID0gbGluZS5zdWJzdHJpbmcoMTQpLnNwbGl0KCcgJyk7XG4gIHJldHVybiB7XG4gICAgYWxnb3JpdGhtOiBwYXJ0c1swXS50b0xvd2VyQ2FzZSgpLCAvLyBhbGdvcml0aG0gaXMgY2FzZS1zZW5zaXRpdmUgaW4gRWRnZS5cbiAgICB2YWx1ZTogcGFydHNbMV0udG9VcHBlckNhc2UoKSwgLy8gdGhlIGRlZmluaXRpb24gaXMgdXBwZXItY2FzZSBpbiBSRkMgNDU3Mi5cbiAgfTtcbn07XG5cbi8vIEV4dHJhY3RzIERUTFMgcGFyYW1ldGVycyBmcm9tIFNEUCBtZWRpYSBzZWN0aW9uIG9yIHNlc3Npb25wYXJ0LlxuLy8gRklYTUU6IGZvciBjb25zaXN0ZW5jeSB3aXRoIG90aGVyIGZ1bmN0aW9ucyB0aGlzIHNob3VsZCBvbmx5XG4vLyAgIGdldCB0aGUgZmluZ2VycHJpbnQgbGluZSBhcyBpbnB1dC4gU2VlIGFsc28gZ2V0SWNlUGFyYW1ldGVycy5cblNEUFV0aWxzLmdldER0bHNQYXJhbWV0ZXJzID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uLCBzZXNzaW9ucGFydCkge1xuICBjb25zdCBsaW5lcyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiArIHNlc3Npb25wYXJ0LFxuICAgICdhPWZpbmdlcnByaW50OicpO1xuICAvLyBOb3RlOiBhPXNldHVwIGxpbmUgaXMgaWdub3JlZCBzaW5jZSB3ZSB1c2UgdGhlICdhdXRvJyByb2xlIGluIEVkZ2UuXG4gIHJldHVybiB7XG4gICAgcm9sZTogJ2F1dG8nLFxuICAgIGZpbmdlcnByaW50czogbGluZXMubWFwKFNEUFV0aWxzLnBhcnNlRmluZ2VycHJpbnQpLFxuICB9O1xufTtcblxuLy8gU2VyaWFsaXplcyBEVExTIHBhcmFtZXRlcnMgdG8gU0RQLlxuU0RQVXRpbHMud3JpdGVEdGxzUGFyYW1ldGVycyA9IGZ1bmN0aW9uKHBhcmFtcywgc2V0dXBUeXBlKSB7XG4gIGxldCBzZHAgPSAnYT1zZXR1cDonICsgc2V0dXBUeXBlICsgJ1xcclxcbic7XG4gIHBhcmFtcy5maW5nZXJwcmludHMuZm9yRWFjaChmcCA9PiB7XG4gICAgc2RwICs9ICdhPWZpbmdlcnByaW50OicgKyBmcC5hbGdvcml0aG0gKyAnICcgKyBmcC52YWx1ZSArICdcXHJcXG4nO1xuICB9KTtcbiAgcmV0dXJuIHNkcDtcbn07XG5cbi8vIFBhcnNlcyBhPWNyeXB0byBsaW5lcyBpbnRvXG4vLyAgIGh0dHBzOi8vcmF3Z2l0LmNvbS9hYm9iYS9lZGdlcnRjL21hc3Rlci9tc29ydGMtcnM0Lmh0bWwjZGljdGlvbmFyeS1ydGNzcnRwc2Rlc3BhcmFtZXRlcnMtbWVtYmVyc1xuU0RQVXRpbHMucGFyc2VDcnlwdG9MaW5lID0gZnVuY3Rpb24obGluZSkge1xuICBjb25zdCBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDkpLnNwbGl0KCcgJyk7XG4gIHJldHVybiB7XG4gICAgdGFnOiBwYXJzZUludChwYXJ0c1swXSwgMTApLFxuICAgIGNyeXB0b1N1aXRlOiBwYXJ0c1sxXSxcbiAgICBrZXlQYXJhbXM6IHBhcnRzWzJdLFxuICAgIHNlc3Npb25QYXJhbXM6IHBhcnRzLnNsaWNlKDMpLFxuICB9O1xufTtcblxuU0RQVXRpbHMud3JpdGVDcnlwdG9MaW5lID0gZnVuY3Rpb24ocGFyYW1ldGVycykge1xuICByZXR1cm4gJ2E9Y3J5cHRvOicgKyBwYXJhbWV0ZXJzLnRhZyArICcgJyArXG4gICAgcGFyYW1ldGVycy5jcnlwdG9TdWl0ZSArICcgJyArXG4gICAgKHR5cGVvZiBwYXJhbWV0ZXJzLmtleVBhcmFtcyA9PT0gJ29iamVjdCdcbiAgICAgID8gU0RQVXRpbHMud3JpdGVDcnlwdG9LZXlQYXJhbXMocGFyYW1ldGVycy5rZXlQYXJhbXMpXG4gICAgICA6IHBhcmFtZXRlcnMua2V5UGFyYW1zKSArXG4gICAgKHBhcmFtZXRlcnMuc2Vzc2lvblBhcmFtcyA/ICcgJyArIHBhcmFtZXRlcnMuc2Vzc2lvblBhcmFtcy5qb2luKCcgJykgOiAnJykgK1xuICAgICdcXHJcXG4nO1xufTtcblxuLy8gUGFyc2VzIHRoZSBjcnlwdG8ga2V5IHBhcmFtZXRlcnMgaW50b1xuLy8gICBodHRwczovL3Jhd2dpdC5jb20vYWJvYmEvZWRnZXJ0Yy9tYXN0ZXIvbXNvcnRjLXJzNC5odG1sI3J0Y3NydHBrZXlwYXJhbSpcblNEUFV0aWxzLnBhcnNlQ3J5cHRvS2V5UGFyYW1zID0gZnVuY3Rpb24oa2V5UGFyYW1zKSB7XG4gIGlmIChrZXlQYXJhbXMuaW5kZXhPZignaW5saW5lOicpICE9PSAwKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgY29uc3QgcGFydHMgPSBrZXlQYXJhbXMuc3Vic3RyaW5nKDcpLnNwbGl0KCd8Jyk7XG4gIHJldHVybiB7XG4gICAga2V5TWV0aG9kOiAnaW5saW5lJyxcbiAgICBrZXlTYWx0OiBwYXJ0c1swXSxcbiAgICBsaWZlVGltZTogcGFydHNbMV0sXG4gICAgbWtpVmFsdWU6IHBhcnRzWzJdID8gcGFydHNbMl0uc3BsaXQoJzonKVswXSA6IHVuZGVmaW5lZCxcbiAgICBta2lMZW5ndGg6IHBhcnRzWzJdID8gcGFydHNbMl0uc3BsaXQoJzonKVsxXSA6IHVuZGVmaW5lZCxcbiAgfTtcbn07XG5cblNEUFV0aWxzLndyaXRlQ3J5cHRvS2V5UGFyYW1zID0gZnVuY3Rpb24oa2V5UGFyYW1zKSB7XG4gIHJldHVybiBrZXlQYXJhbXMua2V5TWV0aG9kICsgJzonXG4gICAgKyBrZXlQYXJhbXMua2V5U2FsdCArXG4gICAgKGtleVBhcmFtcy5saWZlVGltZSA/ICd8JyArIGtleVBhcmFtcy5saWZlVGltZSA6ICcnKSArXG4gICAgKGtleVBhcmFtcy5ta2lWYWx1ZSAmJiBrZXlQYXJhbXMubWtpTGVuZ3RoXG4gICAgICA/ICd8JyArIGtleVBhcmFtcy5ta2lWYWx1ZSArICc6JyArIGtleVBhcmFtcy5ta2lMZW5ndGhcbiAgICAgIDogJycpO1xufTtcblxuLy8gRXh0cmFjdHMgYWxsIFNERVMgcGFyYW1ldGVycy5cblNEUFV0aWxzLmdldENyeXB0b1BhcmFtZXRlcnMgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24sIHNlc3Npb25wYXJ0KSB7XG4gIGNvbnN0IGxpbmVzID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uICsgc2Vzc2lvbnBhcnQsXG4gICAgJ2E9Y3J5cHRvOicpO1xuICByZXR1cm4gbGluZXMubWFwKFNEUFV0aWxzLnBhcnNlQ3J5cHRvTGluZSk7XG59O1xuXG4vLyBQYXJzZXMgSUNFIGluZm9ybWF0aW9uIGZyb20gU0RQIG1lZGlhIHNlY3Rpb24gb3Igc2Vzc2lvbnBhcnQuXG4vLyBGSVhNRTogZm9yIGNvbnNpc3RlbmN5IHdpdGggb3RoZXIgZnVuY3Rpb25zIHRoaXMgc2hvdWxkIG9ubHlcbi8vICAgZ2V0IHRoZSBpY2UtdWZyYWcgYW5kIGljZS1wd2QgbGluZXMgYXMgaW5wdXQuXG5TRFBVdGlscy5nZXRJY2VQYXJhbWV0ZXJzID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uLCBzZXNzaW9ucGFydCkge1xuICBjb25zdCB1ZnJhZyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiArIHNlc3Npb25wYXJ0LFxuICAgICdhPWljZS11ZnJhZzonKVswXTtcbiAgY29uc3QgcHdkID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uICsgc2Vzc2lvbnBhcnQsXG4gICAgJ2E9aWNlLXB3ZDonKVswXTtcbiAgaWYgKCEodWZyYWcgJiYgcHdkKSkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG4gIHJldHVybiB7XG4gICAgdXNlcm5hbWVGcmFnbWVudDogdWZyYWcuc3Vic3RyaW5nKDEyKSxcbiAgICBwYXNzd29yZDogcHdkLnN1YnN0cmluZygxMCksXG4gIH07XG59O1xuXG4vLyBTZXJpYWxpemVzIElDRSBwYXJhbWV0ZXJzIHRvIFNEUC5cblNEUFV0aWxzLndyaXRlSWNlUGFyYW1ldGVycyA9IGZ1bmN0aW9uKHBhcmFtcykge1xuICBsZXQgc2RwID0gJ2E9aWNlLXVmcmFnOicgKyBwYXJhbXMudXNlcm5hbWVGcmFnbWVudCArICdcXHJcXG4nICtcbiAgICAgICdhPWljZS1wd2Q6JyArIHBhcmFtcy5wYXNzd29yZCArICdcXHJcXG4nO1xuICBpZiAocGFyYW1zLmljZUxpdGUpIHtcbiAgICBzZHAgKz0gJ2E9aWNlLWxpdGVcXHJcXG4nO1xuICB9XG4gIHJldHVybiBzZHA7XG59O1xuXG4vLyBQYXJzZXMgdGhlIFNEUCBtZWRpYSBzZWN0aW9uIGFuZCByZXR1cm5zIFJUQ1J0cFBhcmFtZXRlcnMuXG5TRFBVdGlscy5wYXJzZVJ0cFBhcmFtZXRlcnMgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgY29uc3QgZGVzY3JpcHRpb24gPSB7XG4gICAgY29kZWNzOiBbXSxcbiAgICBoZWFkZXJFeHRlbnNpb25zOiBbXSxcbiAgICBmZWNNZWNoYW5pc21zOiBbXSxcbiAgICBydGNwOiBbXSxcbiAgfTtcbiAgY29uc3QgbGluZXMgPSBTRFBVdGlscy5zcGxpdExpbmVzKG1lZGlhU2VjdGlvbik7XG4gIGNvbnN0IG1saW5lID0gbGluZXNbMF0uc3BsaXQoJyAnKTtcbiAgZGVzY3JpcHRpb24ucHJvZmlsZSA9IG1saW5lWzJdO1xuICBmb3IgKGxldCBpID0gMzsgaSA8IG1saW5lLmxlbmd0aDsgaSsrKSB7IC8vIGZpbmQgYWxsIGNvZGVjcyBmcm9tIG1saW5lWzMuLl1cbiAgICBjb25zdCBwdCA9IG1saW5lW2ldO1xuICAgIGNvbnN0IHJ0cG1hcGxpbmUgPSBTRFBVdGlscy5tYXRjaFByZWZpeChcbiAgICAgIG1lZGlhU2VjdGlvbiwgJ2E9cnRwbWFwOicgKyBwdCArICcgJylbMF07XG4gICAgaWYgKHJ0cG1hcGxpbmUpIHtcbiAgICAgIGNvbnN0IGNvZGVjID0gU0RQVXRpbHMucGFyc2VSdHBNYXAocnRwbWFwbGluZSk7XG4gICAgICBjb25zdCBmbXRwcyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KFxuICAgICAgICBtZWRpYVNlY3Rpb24sICdhPWZtdHA6JyArIHB0ICsgJyAnKTtcbiAgICAgIC8vIE9ubHkgdGhlIGZpcnN0IGE9Zm10cDo8cHQ+IGlzIGNvbnNpZGVyZWQuXG4gICAgICBjb2RlYy5wYXJhbWV0ZXJzID0gZm10cHMubGVuZ3RoID8gU0RQVXRpbHMucGFyc2VGbXRwKGZtdHBzWzBdKSA6IHt9O1xuICAgICAgY29kZWMucnRjcEZlZWRiYWNrID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgoXG4gICAgICAgIG1lZGlhU2VjdGlvbiwgJ2E9cnRjcC1mYjonICsgcHQgKyAnICcpXG4gICAgICAgIC5tYXAoU0RQVXRpbHMucGFyc2VSdGNwRmIpO1xuICAgICAgZGVzY3JpcHRpb24uY29kZWNzLnB1c2goY29kZWMpO1xuICAgICAgLy8gcGFyc2UgRkVDIG1lY2hhbmlzbXMgZnJvbSBydHBtYXAgbGluZXMuXG4gICAgICBzd2l0Y2ggKGNvZGVjLm5hbWUudG9VcHBlckNhc2UoKSkge1xuICAgICAgICBjYXNlICdSRUQnOlxuICAgICAgICBjYXNlICdVTFBGRUMnOlxuICAgICAgICAgIGRlc2NyaXB0aW9uLmZlY01lY2hhbmlzbXMucHVzaChjb2RlYy5uYW1lLnRvVXBwZXJDYXNlKCkpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OiAvLyBvbmx5IFJFRCBhbmQgVUxQRkVDIGFyZSByZWNvZ25pemVkIGFzIEZFQyBtZWNoYW5pc21zLlxuICAgICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPWV4dG1hcDonKS5mb3JFYWNoKGxpbmUgPT4ge1xuICAgIGRlc2NyaXB0aW9uLmhlYWRlckV4dGVuc2lvbnMucHVzaChTRFBVdGlscy5wYXJzZUV4dG1hcChsaW5lKSk7XG4gIH0pO1xuICBjb25zdCB3aWxkY2FyZFJ0Y3BGYiA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9cnRjcC1mYjoqICcpXG4gICAgLm1hcChTRFBVdGlscy5wYXJzZVJ0Y3BGYik7XG4gIGRlc2NyaXB0aW9uLmNvZGVjcy5mb3JFYWNoKGNvZGVjID0+IHtcbiAgICB3aWxkY2FyZFJ0Y3BGYi5mb3JFYWNoKGZiPT4ge1xuICAgICAgY29uc3QgZHVwbGljYXRlID0gY29kZWMucnRjcEZlZWRiYWNrLmZpbmQoZXhpc3RpbmdGZWVkYmFjayA9PiB7XG4gICAgICAgIHJldHVybiBleGlzdGluZ0ZlZWRiYWNrLnR5cGUgPT09IGZiLnR5cGUgJiZcbiAgICAgICAgICBleGlzdGluZ0ZlZWRiYWNrLnBhcmFtZXRlciA9PT0gZmIucGFyYW1ldGVyO1xuICAgICAgfSk7XG4gICAgICBpZiAoIWR1cGxpY2F0ZSkge1xuICAgICAgICBjb2RlYy5ydGNwRmVlZGJhY2sucHVzaChmYik7XG4gICAgICB9XG4gICAgfSk7XG4gIH0pO1xuICAvLyBGSVhNRTogcGFyc2UgcnRjcC5cbiAgcmV0dXJuIGRlc2NyaXB0aW9uO1xufTtcblxuLy8gR2VuZXJhdGVzIHBhcnRzIG9mIHRoZSBTRFAgbWVkaWEgc2VjdGlvbiBkZXNjcmliaW5nIHRoZSBjYXBhYmlsaXRpZXMgL1xuLy8gcGFyYW1ldGVycy5cblNEUFV0aWxzLndyaXRlUnRwRGVzY3JpcHRpb24gPSBmdW5jdGlvbihraW5kLCBjYXBzKSB7XG4gIGxldCBzZHAgPSAnJztcblxuICAvLyBCdWlsZCB0aGUgbWxpbmUuXG4gIHNkcCArPSAnbT0nICsga2luZCArICcgJztcbiAgc2RwICs9IGNhcHMuY29kZWNzLmxlbmd0aCA+IDAgPyAnOScgOiAnMCc7IC8vIHJlamVjdCBpZiBubyBjb2RlY3MuXG4gIHNkcCArPSAnICcgKyAoY2Fwcy5wcm9maWxlIHx8ICdVRFAvVExTL1JUUC9TQVZQRicpICsgJyAnO1xuICBzZHAgKz0gY2Fwcy5jb2RlY3MubWFwKGNvZGVjID0+IHtcbiAgICBpZiAoY29kZWMucHJlZmVycmVkUGF5bG9hZFR5cGUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIGNvZGVjLnByZWZlcnJlZFBheWxvYWRUeXBlO1xuICAgIH1cbiAgICByZXR1cm4gY29kZWMucGF5bG9hZFR5cGU7XG4gIH0pLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuXG4gIHNkcCArPSAnYz1JTiBJUDQgMC4wLjAuMFxcclxcbic7XG4gIHNkcCArPSAnYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcXHJcXG4nO1xuXG4gIC8vIEFkZCBhPXJ0cG1hcCBsaW5lcyBmb3IgZWFjaCBjb2RlYy4gQWxzbyBmbXRwIGFuZCBydGNwLWZiLlxuICBjYXBzLmNvZGVjcy5mb3JFYWNoKGNvZGVjID0+IHtcbiAgICBzZHAgKz0gU0RQVXRpbHMud3JpdGVSdHBNYXAoY29kZWMpO1xuICAgIHNkcCArPSBTRFBVdGlscy53cml0ZUZtdHAoY29kZWMpO1xuICAgIHNkcCArPSBTRFBVdGlscy53cml0ZVJ0Y3BGYihjb2RlYyk7XG4gIH0pO1xuICBsZXQgbWF4cHRpbWUgPSAwO1xuICBjYXBzLmNvZGVjcy5mb3JFYWNoKGNvZGVjID0+IHtcbiAgICBpZiAoY29kZWMubWF4cHRpbWUgPiBtYXhwdGltZSkge1xuICAgICAgbWF4cHRpbWUgPSBjb2RlYy5tYXhwdGltZTtcbiAgICB9XG4gIH0pO1xuICBpZiAobWF4cHRpbWUgPiAwKSB7XG4gICAgc2RwICs9ICdhPW1heHB0aW1lOicgKyBtYXhwdGltZSArICdcXHJcXG4nO1xuICB9XG5cbiAgaWYgKGNhcHMuaGVhZGVyRXh0ZW5zaW9ucykge1xuICAgIGNhcHMuaGVhZGVyRXh0ZW5zaW9ucy5mb3JFYWNoKGV4dGVuc2lvbiA9PiB7XG4gICAgICBzZHAgKz0gU0RQVXRpbHMud3JpdGVFeHRtYXAoZXh0ZW5zaW9uKTtcbiAgICB9KTtcbiAgfVxuICAvLyBGSVhNRTogd3JpdGUgZmVjTWVjaGFuaXNtcy5cbiAgcmV0dXJuIHNkcDtcbn07XG5cbi8vIFBhcnNlcyB0aGUgU0RQIG1lZGlhIHNlY3Rpb24gYW5kIHJldHVybnMgYW4gYXJyYXkgb2Zcbi8vIFJUQ1J0cEVuY29kaW5nUGFyYW1ldGVycy5cblNEUFV0aWxzLnBhcnNlUnRwRW5jb2RpbmdQYXJhbWV0ZXJzID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uKSB7XG4gIGNvbnN0IGVuY29kaW5nUGFyYW1ldGVycyA9IFtdO1xuICBjb25zdCBkZXNjcmlwdGlvbiA9IFNEUFV0aWxzLnBhcnNlUnRwUGFyYW1ldGVycyhtZWRpYVNlY3Rpb24pO1xuICBjb25zdCBoYXNSZWQgPSBkZXNjcmlwdGlvbi5mZWNNZWNoYW5pc21zLmluZGV4T2YoJ1JFRCcpICE9PSAtMTtcbiAgY29uc3QgaGFzVWxwZmVjID0gZGVzY3JpcHRpb24uZmVjTWVjaGFuaXNtcy5pbmRleE9mKCdVTFBGRUMnKSAhPT0gLTE7XG5cbiAgLy8gZmlsdGVyIGE9c3NyYzouLi4gY25hbWU6LCBpZ25vcmUgUGxhbkItbXNpZFxuICBjb25zdCBzc3JjcyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9c3NyYzonKVxuICAgIC5tYXAobGluZSA9PiBTRFBVdGlscy5wYXJzZVNzcmNNZWRpYShsaW5lKSlcbiAgICAuZmlsdGVyKHBhcnRzID0+IHBhcnRzLmF0dHJpYnV0ZSA9PT0gJ2NuYW1lJyk7XG4gIGNvbnN0IHByaW1hcnlTc3JjID0gc3NyY3MubGVuZ3RoID4gMCAmJiBzc3Jjc1swXS5zc3JjO1xuICBsZXQgc2Vjb25kYXJ5U3NyYztcblxuICBjb25zdCBmbG93cyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9c3NyYy1ncm91cDpGSUQnKVxuICAgIC5tYXAobGluZSA9PiB7XG4gICAgICBjb25zdCBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDE3KS5zcGxpdCgnICcpO1xuICAgICAgcmV0dXJuIHBhcnRzLm1hcChwYXJ0ID0+IHBhcnNlSW50KHBhcnQsIDEwKSk7XG4gICAgfSk7XG4gIGlmIChmbG93cy5sZW5ndGggPiAwICYmIGZsb3dzWzBdLmxlbmd0aCA+IDEgJiYgZmxvd3NbMF1bMF0gPT09IHByaW1hcnlTc3JjKSB7XG4gICAgc2Vjb25kYXJ5U3NyYyA9IGZsb3dzWzBdWzFdO1xuICB9XG5cbiAgZGVzY3JpcHRpb24uY29kZWNzLmZvckVhY2goY29kZWMgPT4ge1xuICAgIGlmIChjb2RlYy5uYW1lLnRvVXBwZXJDYXNlKCkgPT09ICdSVFgnICYmIGNvZGVjLnBhcmFtZXRlcnMuYXB0KSB7XG4gICAgICBsZXQgZW5jUGFyYW0gPSB7XG4gICAgICAgIHNzcmM6IHByaW1hcnlTc3JjLFxuICAgICAgICBjb2RlY1BheWxvYWRUeXBlOiBwYXJzZUludChjb2RlYy5wYXJhbWV0ZXJzLmFwdCwgMTApLFxuICAgICAgfTtcbiAgICAgIGlmIChwcmltYXJ5U3NyYyAmJiBzZWNvbmRhcnlTc3JjKSB7XG4gICAgICAgIGVuY1BhcmFtLnJ0eCA9IHtzc3JjOiBzZWNvbmRhcnlTc3JjfTtcbiAgICAgIH1cbiAgICAgIGVuY29kaW5nUGFyYW1ldGVycy5wdXNoKGVuY1BhcmFtKTtcbiAgICAgIGlmIChoYXNSZWQpIHtcbiAgICAgICAgZW5jUGFyYW0gPSBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KGVuY1BhcmFtKSk7XG4gICAgICAgIGVuY1BhcmFtLmZlYyA9IHtcbiAgICAgICAgICBzc3JjOiBwcmltYXJ5U3NyYyxcbiAgICAgICAgICBtZWNoYW5pc206IGhhc1VscGZlYyA/ICdyZWQrdWxwZmVjJyA6ICdyZWQnLFxuICAgICAgICB9O1xuICAgICAgICBlbmNvZGluZ1BhcmFtZXRlcnMucHVzaChlbmNQYXJhbSk7XG4gICAgICB9XG4gICAgfVxuICB9KTtcbiAgaWYgKGVuY29kaW5nUGFyYW1ldGVycy5sZW5ndGggPT09IDAgJiYgcHJpbWFyeVNzcmMpIHtcbiAgICBlbmNvZGluZ1BhcmFtZXRlcnMucHVzaCh7XG4gICAgICBzc3JjOiBwcmltYXJ5U3NyYyxcbiAgICB9KTtcbiAgfVxuXG4gIC8vIHdlIHN1cHBvcnQgYm90aCBiPUFTIGFuZCBiPVRJQVMgYnV0IGludGVycHJldCBBUyBhcyBUSUFTLlxuICBsZXQgYmFuZHdpZHRoID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYj0nKTtcbiAgaWYgKGJhbmR3aWR0aC5sZW5ndGgpIHtcbiAgICBpZiAoYmFuZHdpZHRoWzBdLmluZGV4T2YoJ2I9VElBUzonKSA9PT0gMCkge1xuICAgICAgYmFuZHdpZHRoID0gcGFyc2VJbnQoYmFuZHdpZHRoWzBdLnN1YnN0cmluZyg3KSwgMTApO1xuICAgIH0gZWxzZSBpZiAoYmFuZHdpZHRoWzBdLmluZGV4T2YoJ2I9QVM6JykgPT09IDApIHtcbiAgICAgIC8vIHVzZSBmb3JtdWxhIGZyb20gSlNFUCB0byBjb252ZXJ0IGI9QVMgdG8gVElBUyB2YWx1ZS5cbiAgICAgIGJhbmR3aWR0aCA9IHBhcnNlSW50KGJhbmR3aWR0aFswXS5zdWJzdHJpbmcoNSksIDEwKSAqIDEwMDAgKiAwLjk1XG4gICAgICAgICAgLSAoNTAgKiA0MCAqIDgpO1xuICAgIH0gZWxzZSB7XG4gICAgICBiYW5kd2lkdGggPSB1bmRlZmluZWQ7XG4gICAgfVxuICAgIGVuY29kaW5nUGFyYW1ldGVycy5mb3JFYWNoKHBhcmFtcyA9PiB7XG4gICAgICBwYXJhbXMubWF4Qml0cmF0ZSA9IGJhbmR3aWR0aDtcbiAgICB9KTtcbiAgfVxuICByZXR1cm4gZW5jb2RpbmdQYXJhbWV0ZXJzO1xufTtcblxuLy8gcGFyc2VzIGh0dHA6Ly9kcmFmdC5vcnRjLm9yZy8jcnRjcnRjcHBhcmFtZXRlcnMqXG5TRFBVdGlscy5wYXJzZVJ0Y3BQYXJhbWV0ZXJzID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uKSB7XG4gIGNvbnN0IHJ0Y3BQYXJhbWV0ZXJzID0ge307XG5cbiAgLy8gR2V0cyB0aGUgZmlyc3QgU1NSQy4gTm90ZSB0aGF0IHdpdGggUlRYIHRoZXJlIG1pZ2h0IGJlIG11bHRpcGxlXG4gIC8vIFNTUkNzLlxuICBjb25zdCByZW1vdGVTc3JjID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1zc3JjOicpXG4gICAgLm1hcChsaW5lID0+IFNEUFV0aWxzLnBhcnNlU3NyY01lZGlhKGxpbmUpKVxuICAgIC5maWx0ZXIob2JqID0+IG9iai5hdHRyaWJ1dGUgPT09ICdjbmFtZScpWzBdO1xuICBpZiAocmVtb3RlU3NyYykge1xuICAgIHJ0Y3BQYXJhbWV0ZXJzLmNuYW1lID0gcmVtb3RlU3NyYy52YWx1ZTtcbiAgICBydGNwUGFyYW1ldGVycy5zc3JjID0gcmVtb3RlU3NyYy5zc3JjO1xuICB9XG5cbiAgLy8gRWRnZSB1c2VzIHRoZSBjb21wb3VuZCBhdHRyaWJ1dGUgaW5zdGVhZCBvZiByZWR1Y2VkU2l6ZVxuICAvLyBjb21wb3VuZCBpcyAhcmVkdWNlZFNpemVcbiAgY29uc3QgcnNpemUgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPXJ0Y3AtcnNpemUnKTtcbiAgcnRjcFBhcmFtZXRlcnMucmVkdWNlZFNpemUgPSByc2l6ZS5sZW5ndGggPiAwO1xuICBydGNwUGFyYW1ldGVycy5jb21wb3VuZCA9IHJzaXplLmxlbmd0aCA9PT0gMDtcblxuICAvLyBwYXJzZXMgdGhlIHJ0Y3AtbXV4IGF0dHLRlmJ1dGUuXG4gIC8vIE5vdGUgdGhhdCBFZGdlIGRvZXMgbm90IHN1cHBvcnQgdW5tdXhlZCBSVENQLlxuICBjb25zdCBtdXggPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPXJ0Y3AtbXV4Jyk7XG4gIHJ0Y3BQYXJhbWV0ZXJzLm11eCA9IG11eC5sZW5ndGggPiAwO1xuXG4gIHJldHVybiBydGNwUGFyYW1ldGVycztcbn07XG5cblNEUFV0aWxzLndyaXRlUnRjcFBhcmFtZXRlcnMgPSBmdW5jdGlvbihydGNwUGFyYW1ldGVycykge1xuICBsZXQgc2RwID0gJyc7XG4gIGlmIChydGNwUGFyYW1ldGVycy5yZWR1Y2VkU2l6ZSkge1xuICAgIHNkcCArPSAnYT1ydGNwLXJzaXplXFxyXFxuJztcbiAgfVxuICBpZiAocnRjcFBhcmFtZXRlcnMubXV4KSB7XG4gICAgc2RwICs9ICdhPXJ0Y3AtbXV4XFxyXFxuJztcbiAgfVxuICBpZiAocnRjcFBhcmFtZXRlcnMuc3NyYyAhPT0gdW5kZWZpbmVkICYmIHJ0Y3BQYXJhbWV0ZXJzLmNuYW1lKSB7XG4gICAgc2RwICs9ICdhPXNzcmM6JyArIHJ0Y3BQYXJhbWV0ZXJzLnNzcmMgK1xuICAgICAgJyBjbmFtZTonICsgcnRjcFBhcmFtZXRlcnMuY25hbWUgKyAnXFxyXFxuJztcbiAgfVxuICByZXR1cm4gc2RwO1xufTtcblxuXG4vLyBwYXJzZXMgZWl0aGVyIGE9bXNpZDogb3IgYT1zc3JjOi4uLiBtc2lkIGxpbmVzIGFuZCByZXR1cm5zXG4vLyB0aGUgaWQgb2YgdGhlIE1lZGlhU3RyZWFtIGFuZCBNZWRpYVN0cmVhbVRyYWNrLlxuU0RQVXRpbHMucGFyc2VNc2lkID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uKSB7XG4gIGxldCBwYXJ0cztcbiAgY29uc3Qgc3BlYyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9bXNpZDonKTtcbiAgaWYgKHNwZWMubGVuZ3RoID09PSAxKSB7XG4gICAgcGFydHMgPSBzcGVjWzBdLnN1YnN0cmluZyg3KS5zcGxpdCgnICcpO1xuICAgIHJldHVybiB7c3RyZWFtOiBwYXJ0c1swXSwgdHJhY2s6IHBhcnRzWzFdfTtcbiAgfVxuICBjb25zdCBwbGFuQiA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9c3NyYzonKVxuICAgIC5tYXAobGluZSA9PiBTRFBVdGlscy5wYXJzZVNzcmNNZWRpYShsaW5lKSlcbiAgICAuZmlsdGVyKG1zaWRQYXJ0cyA9PiBtc2lkUGFydHMuYXR0cmlidXRlID09PSAnbXNpZCcpO1xuICBpZiAocGxhbkIubGVuZ3RoID4gMCkge1xuICAgIHBhcnRzID0gcGxhbkJbMF0udmFsdWUuc3BsaXQoJyAnKTtcbiAgICByZXR1cm4ge3N0cmVhbTogcGFydHNbMF0sIHRyYWNrOiBwYXJ0c1sxXX07XG4gIH1cbn07XG5cbi8vIFNDVFBcbi8vIHBhcnNlcyBkcmFmdC1pZXRmLW1tdXNpYy1zY3RwLXNkcC0yNiBmaXJzdCBhbmQgZmFsbHMgYmFja1xuLy8gdG8gZHJhZnQtaWV0Zi1tbXVzaWMtc2N0cC1zZHAtMDVcblNEUFV0aWxzLnBhcnNlU2N0cERlc2NyaXB0aW9uID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uKSB7XG4gIGNvbnN0IG1saW5lID0gU0RQVXRpbHMucGFyc2VNTGluZShtZWRpYVNlY3Rpb24pO1xuICBjb25zdCBtYXhTaXplTGluZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9bWF4LW1lc3NhZ2Utc2l6ZTonKTtcbiAgbGV0IG1heE1lc3NhZ2VTaXplO1xuICBpZiAobWF4U2l6ZUxpbmUubGVuZ3RoID4gMCkge1xuICAgIG1heE1lc3NhZ2VTaXplID0gcGFyc2VJbnQobWF4U2l6ZUxpbmVbMF0uc3Vic3RyaW5nKDE5KSwgMTApO1xuICB9XG4gIGlmIChpc05hTihtYXhNZXNzYWdlU2l6ZSkpIHtcbiAgICBtYXhNZXNzYWdlU2l6ZSA9IDY1NTM2O1xuICB9XG4gIGNvbnN0IHNjdHBQb3J0ID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1zY3RwLXBvcnQ6Jyk7XG4gIGlmIChzY3RwUG9ydC5sZW5ndGggPiAwKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHBvcnQ6IHBhcnNlSW50KHNjdHBQb3J0WzBdLnN1YnN0cmluZygxMiksIDEwKSxcbiAgICAgIHByb3RvY29sOiBtbGluZS5mbXQsXG4gICAgICBtYXhNZXNzYWdlU2l6ZSxcbiAgICB9O1xuICB9XG4gIGNvbnN0IHNjdHBNYXBMaW5lcyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9c2N0cG1hcDonKTtcbiAgaWYgKHNjdHBNYXBMaW5lcy5sZW5ndGggPiAwKSB7XG4gICAgY29uc3QgcGFydHMgPSBzY3RwTWFwTGluZXNbMF1cbiAgICAgIC5zdWJzdHJpbmcoMTApXG4gICAgICAuc3BsaXQoJyAnKTtcbiAgICByZXR1cm4ge1xuICAgICAgcG9ydDogcGFyc2VJbnQocGFydHNbMF0sIDEwKSxcbiAgICAgIHByb3RvY29sOiBwYXJ0c1sxXSxcbiAgICAgIG1heE1lc3NhZ2VTaXplLFxuICAgIH07XG4gIH1cbn07XG5cbi8vIFNDVFBcbi8vIG91dHB1dHMgdGhlIGRyYWZ0LWlldGYtbW11c2ljLXNjdHAtc2RwLTI2IHZlcnNpb24gdGhhdCBhbGwgYnJvd3NlcnNcbi8vIHN1cHBvcnQgYnkgbm93IHJlY2VpdmluZyBpbiB0aGlzIGZvcm1hdCwgdW5sZXNzIHdlIG9yaWdpbmFsbHkgcGFyc2VkXG4vLyBhcyB0aGUgZHJhZnQtaWV0Zi1tbXVzaWMtc2N0cC1zZHAtMDUgZm9ybWF0IChpbmRpY2F0ZWQgYnkgdGhlIG0tbGluZVxuLy8gcHJvdG9jb2wgb2YgRFRMUy9TQ1RQIC0tIHdpdGhvdXQgVURQLyBvciBUQ1AvKVxuU0RQVXRpbHMud3JpdGVTY3RwRGVzY3JpcHRpb24gPSBmdW5jdGlvbihtZWRpYSwgc2N0cCkge1xuICBsZXQgb3V0cHV0ID0gW107XG4gIGlmIChtZWRpYS5wcm90b2NvbCAhPT0gJ0RUTFMvU0NUUCcpIHtcbiAgICBvdXRwdXQgPSBbXG4gICAgICAnbT0nICsgbWVkaWEua2luZCArICcgOSAnICsgbWVkaWEucHJvdG9jb2wgKyAnICcgKyBzY3RwLnByb3RvY29sICsgJ1xcclxcbicsXG4gICAgICAnYz1JTiBJUDQgMC4wLjAuMFxcclxcbicsXG4gICAgICAnYT1zY3RwLXBvcnQ6JyArIHNjdHAucG9ydCArICdcXHJcXG4nLFxuICAgIF07XG4gIH0gZWxzZSB7XG4gICAgb3V0cHV0ID0gW1xuICAgICAgJ209JyArIG1lZGlhLmtpbmQgKyAnIDkgJyArIG1lZGlhLnByb3RvY29sICsgJyAnICsgc2N0cC5wb3J0ICsgJ1xcclxcbicsXG4gICAgICAnYz1JTiBJUDQgMC4wLjAuMFxcclxcbicsXG4gICAgICAnYT1zY3RwbWFwOicgKyBzY3RwLnBvcnQgKyAnICcgKyBzY3RwLnByb3RvY29sICsgJyA2NTUzNVxcclxcbicsXG4gICAgXTtcbiAgfVxuICBpZiAoc2N0cC5tYXhNZXNzYWdlU2l6ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgb3V0cHV0LnB1c2goJ2E9bWF4LW1lc3NhZ2Utc2l6ZTonICsgc2N0cC5tYXhNZXNzYWdlU2l6ZSArICdcXHJcXG4nKTtcbiAgfVxuICByZXR1cm4gb3V0cHV0LmpvaW4oJycpO1xufTtcblxuLy8gR2VuZXJhdGUgYSBzZXNzaW9uIElEIGZvciBTRFAuXG4vLyBodHRwczovL3Rvb2xzLmlldGYub3JnL2h0bWwvZHJhZnQtaWV0Zi1ydGN3ZWItanNlcC0yMCNzZWN0aW9uLTUuMi4xXG4vLyByZWNvbW1lbmRzIHVzaW5nIGEgY3J5cHRvZ3JhcGhpY2FsbHkgcmFuZG9tICt2ZSA2NC1iaXQgdmFsdWVcbi8vIGJ1dCByaWdodCBub3cgdGhpcyBzaG91bGQgYmUgYWNjZXB0YWJsZSBhbmQgd2l0aGluIHRoZSByaWdodCByYW5nZVxuU0RQVXRpbHMuZ2VuZXJhdGVTZXNzaW9uSWQgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIE1hdGgucmFuZG9tKCkudG9TdHJpbmcoKS5zdWJzdHIoMiwgMjIpO1xufTtcblxuLy8gV3JpdGUgYm9pbGVyIHBsYXRlIGZvciBzdGFydCBvZiBTRFBcbi8vIHNlc3NJZCBhcmd1bWVudCBpcyBvcHRpb25hbCAtIGlmIG5vdCBzdXBwbGllZCBpdCB3aWxsXG4vLyBiZSBnZW5lcmF0ZWQgcmFuZG9tbHlcbi8vIHNlc3NWZXJzaW9uIGlzIG9wdGlvbmFsIGFuZCBkZWZhdWx0cyB0byAyXG4vLyBzZXNzVXNlciBpcyBvcHRpb25hbCBhbmQgZGVmYXVsdHMgdG8gJ3RoaXNpc2FkYXB0ZXJvcnRjJ1xuU0RQVXRpbHMud3JpdGVTZXNzaW9uQm9pbGVycGxhdGUgPSBmdW5jdGlvbihzZXNzSWQsIHNlc3NWZXIsIHNlc3NVc2VyKSB7XG4gIGxldCBzZXNzaW9uSWQ7XG4gIGNvbnN0IHZlcnNpb24gPSBzZXNzVmVyICE9PSB1bmRlZmluZWQgPyBzZXNzVmVyIDogMjtcbiAgaWYgKHNlc3NJZCkge1xuICAgIHNlc3Npb25JZCA9IHNlc3NJZDtcbiAgfSBlbHNlIHtcbiAgICBzZXNzaW9uSWQgPSBTRFBVdGlscy5nZW5lcmF0ZVNlc3Npb25JZCgpO1xuICB9XG4gIGNvbnN0IHVzZXIgPSBzZXNzVXNlciB8fCAndGhpc2lzYWRhcHRlcm9ydGMnO1xuICAvLyBGSVhNRTogc2Vzcy1pZCBzaG91bGQgYmUgYW4gTlRQIHRpbWVzdGFtcC5cbiAgcmV0dXJuICd2PTBcXHJcXG4nICtcbiAgICAgICdvPScgKyB1c2VyICsgJyAnICsgc2Vzc2lvbklkICsgJyAnICsgdmVyc2lvbiArXG4gICAgICAgICcgSU4gSVA0IDEyNy4wLjAuMVxcclxcbicgK1xuICAgICAgJ3M9LVxcclxcbicgK1xuICAgICAgJ3Q9MCAwXFxyXFxuJztcbn07XG5cbi8vIEdldHMgdGhlIGRpcmVjdGlvbiBmcm9tIHRoZSBtZWRpYVNlY3Rpb24gb3IgdGhlIHNlc3Npb25wYXJ0LlxuU0RQVXRpbHMuZ2V0RGlyZWN0aW9uID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uLCBzZXNzaW9ucGFydCkge1xuICAvLyBMb29rIGZvciBzZW5kcmVjdiwgc2VuZG9ubHksIHJlY3Zvbmx5LCBpbmFjdGl2ZSwgZGVmYXVsdCB0byBzZW5kcmVjdi5cbiAgY29uc3QgbGluZXMgPSBTRFBVdGlscy5zcGxpdExpbmVzKG1lZGlhU2VjdGlvbik7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbGluZXMubGVuZ3RoOyBpKyspIHtcbiAgICBzd2l0Y2ggKGxpbmVzW2ldKSB7XG4gICAgICBjYXNlICdhPXNlbmRyZWN2JzpcbiAgICAgIGNhc2UgJ2E9c2VuZG9ubHknOlxuICAgICAgY2FzZSAnYT1yZWN2b25seSc6XG4gICAgICBjYXNlICdhPWluYWN0aXZlJzpcbiAgICAgICAgcmV0dXJuIGxpbmVzW2ldLnN1YnN0cmluZygyKTtcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIC8vIEZJWE1FOiBXaGF0IHNob3VsZCBoYXBwZW4gaGVyZT9cbiAgICB9XG4gIH1cbiAgaWYgKHNlc3Npb25wYXJ0KSB7XG4gICAgcmV0dXJuIFNEUFV0aWxzLmdldERpcmVjdGlvbihzZXNzaW9ucGFydCk7XG4gIH1cbiAgcmV0dXJuICdzZW5kcmVjdic7XG59O1xuXG5TRFBVdGlscy5nZXRLaW5kID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uKSB7XG4gIGNvbnN0IGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhtZWRpYVNlY3Rpb24pO1xuICBjb25zdCBtbGluZSA9IGxpbmVzWzBdLnNwbGl0KCcgJyk7XG4gIHJldHVybiBtbGluZVswXS5zdWJzdHJpbmcoMik7XG59O1xuXG5TRFBVdGlscy5pc1JlamVjdGVkID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uKSB7XG4gIHJldHVybiBtZWRpYVNlY3Rpb24uc3BsaXQoJyAnLCAyKVsxXSA9PT0gJzAnO1xufTtcblxuU0RQVXRpbHMucGFyc2VNTGluZSA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbikge1xuICBjb25zdCBsaW5lcyA9IFNEUFV0aWxzLnNwbGl0TGluZXMobWVkaWFTZWN0aW9uKTtcbiAgY29uc3QgcGFydHMgPSBsaW5lc1swXS5zdWJzdHJpbmcoMikuc3BsaXQoJyAnKTtcbiAgcmV0dXJuIHtcbiAgICBraW5kOiBwYXJ0c1swXSxcbiAgICBwb3J0OiBwYXJzZUludChwYXJ0c1sxXSwgMTApLFxuICAgIHByb3RvY29sOiBwYXJ0c1syXSxcbiAgICBmbXQ6IHBhcnRzLnNsaWNlKDMpLmpvaW4oJyAnKSxcbiAgfTtcbn07XG5cblNEUFV0aWxzLnBhcnNlT0xpbmUgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgY29uc3QgbGluZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ289JylbMF07XG4gIGNvbnN0IHBhcnRzID0gbGluZS5zdWJzdHJpbmcoMikuc3BsaXQoJyAnKTtcbiAgcmV0dXJuIHtcbiAgICB1c2VybmFtZTogcGFydHNbMF0sXG4gICAgc2Vzc2lvbklkOiBwYXJ0c1sxXSxcbiAgICBzZXNzaW9uVmVyc2lvbjogcGFyc2VJbnQocGFydHNbMl0sIDEwKSxcbiAgICBuZXRUeXBlOiBwYXJ0c1szXSxcbiAgICBhZGRyZXNzVHlwZTogcGFydHNbNF0sXG4gICAgYWRkcmVzczogcGFydHNbNV0sXG4gIH07XG59O1xuXG4vLyBhIHZlcnkgbmFpdmUgaW50ZXJwcmV0YXRpb24gb2YgYSB2YWxpZCBTRFAuXG5TRFBVdGlscy5pc1ZhbGlkU0RQID0gZnVuY3Rpb24oYmxvYikge1xuICBpZiAodHlwZW9mIGJsb2IgIT09ICdzdHJpbmcnIHx8IGJsb2IubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIGNvbnN0IGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhibG9iKTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBsaW5lcy5sZW5ndGg7IGkrKykge1xuICAgIGlmIChsaW5lc1tpXS5sZW5ndGggPCAyIHx8IGxpbmVzW2ldLmNoYXJBdCgxKSAhPT0gJz0nKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIC8vIFRPRE86IGNoZWNrIHRoZSBtb2RpZmllciBhIGJpdCBtb3JlLlxuICB9XG4gIHJldHVybiB0cnVlO1xufTtcblxuLy8gRXhwb3NlIHB1YmxpYyBtZXRob2RzLlxuaWYgKHR5cGVvZiBtb2R1bGUgPT09ICdvYmplY3QnKSB7XG4gIG1vZHVsZS5leHBvcnRzID0gU0RQVXRpbHM7XG59XG4iLCIvLyBUaGUgbW9kdWxlIGNhY2hlXG52YXIgX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fID0ge307XG5cbi8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG5mdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuXHR2YXIgY2FjaGVkTW9kdWxlID0gX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fW21vZHVsZUlkXTtcblx0aWYgKGNhY2hlZE1vZHVsZSAhPT0gdW5kZWZpbmVkKSB7XG5cdFx0cmV0dXJuIGNhY2hlZE1vZHVsZS5leHBvcnRzO1xuXHR9XG5cdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG5cdHZhciBtb2R1bGUgPSBfX3dlYnBhY2tfbW9kdWxlX2NhY2hlX19bbW9kdWxlSWRdID0ge1xuXHRcdC8vIG5vIG1vZHVsZS5pZCBuZWVkZWRcblx0XHQvLyBubyBtb2R1bGUubG9hZGVkIG5lZWRlZFxuXHRcdGV4cG9ydHM6IHt9XG5cdH07XG5cblx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG5cdF9fd2VicGFja19tb2R1bGVzX19bbW9kdWxlSWRdKG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG5cdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG5cdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbn1cblxuIiwiIiwiLy8gc3RhcnR1cFxuLy8gTG9hZCBlbnRyeSBtb2R1bGUgYW5kIHJldHVybiBleHBvcnRzXG4vLyBUaGlzIGVudHJ5IG1vZHVsZSBpcyByZWZlcmVuY2VkIGJ5IG90aGVyIG1vZHVsZXMgc28gaXQgY2FuJ3QgYmUgaW5saW5lZFxudmFyIF9fd2VicGFja19leHBvcnRzX18gPSBfX3dlYnBhY2tfcmVxdWlyZV9fKFwiLi9zcmMvaW5kZXguanNcIik7XG4iLCIiXSwibmFtZXMiOlsiX3JlZ2VuZXJhdG9yUnVudGltZSIsImV4cG9ydHMiLCJPcCIsIk9iamVjdCIsInByb3RvdHlwZSIsImhhc093biIsImhhc093blByb3BlcnR5IiwiZGVmaW5lUHJvcGVydHkiLCJvYmoiLCJrZXkiLCJkZXNjIiwidmFsdWUiLCIkU3ltYm9sIiwiU3ltYm9sIiwiaXRlcmF0b3JTeW1ib2wiLCJpdGVyYXRvciIsImFzeW5jSXRlcmF0b3JTeW1ib2wiLCJhc3luY0l0ZXJhdG9yIiwidG9TdHJpbmdUYWdTeW1ib2wiLCJ0b1N0cmluZ1RhZyIsImRlZmluZSIsImVudW1lcmFibGUiLCJjb25maWd1cmFibGUiLCJ3cml0YWJsZSIsImVyciIsIndyYXAiLCJpbm5lckZuIiwib3V0ZXJGbiIsInNlbGYiLCJ0cnlMb2NzTGlzdCIsInByb3RvR2VuZXJhdG9yIiwiR2VuZXJhdG9yIiwiZ2VuZXJhdG9yIiwiY3JlYXRlIiwiY29udGV4dCIsIkNvbnRleHQiLCJtYWtlSW52b2tlTWV0aG9kIiwidHJ5Q2F0Y2giLCJmbiIsImFyZyIsInR5cGUiLCJjYWxsIiwiQ29udGludWVTZW50aW5lbCIsIkdlbmVyYXRvckZ1bmN0aW9uIiwiR2VuZXJhdG9yRnVuY3Rpb25Qcm90b3R5cGUiLCJJdGVyYXRvclByb3RvdHlwZSIsImdldFByb3RvIiwiZ2V0UHJvdG90eXBlT2YiLCJOYXRpdmVJdGVyYXRvclByb3RvdHlwZSIsInZhbHVlcyIsIkdwIiwiZGVmaW5lSXRlcmF0b3JNZXRob2RzIiwiZm9yRWFjaCIsIm1ldGhvZCIsIl9pbnZva2UiLCJBc3luY0l0ZXJhdG9yIiwiUHJvbWlzZUltcGwiLCJpbnZva2UiLCJyZXNvbHZlIiwicmVqZWN0IiwicmVjb3JkIiwicmVzdWx0IiwiX3R5cGVvZiIsIl9fYXdhaXQiLCJ0aGVuIiwidW53cmFwcGVkIiwiZXJyb3IiLCJwcmV2aW91c1Byb21pc2UiLCJjYWxsSW52b2tlV2l0aE1ldGhvZEFuZEFyZyIsInN0YXRlIiwiRXJyb3IiLCJkb25lUmVzdWx0IiwiZGVsZWdhdGUiLCJkZWxlZ2F0ZVJlc3VsdCIsIm1heWJlSW52b2tlRGVsZWdhdGUiLCJzZW50IiwiX3NlbnQiLCJkaXNwYXRjaEV4Y2VwdGlvbiIsImFicnVwdCIsImRvbmUiLCJtZXRob2ROYW1lIiwidW5kZWZpbmVkIiwiVHlwZUVycm9yIiwiaW5mbyIsInJlc3VsdE5hbWUiLCJuZXh0IiwibmV4dExvYyIsInB1c2hUcnlFbnRyeSIsImxvY3MiLCJlbnRyeSIsInRyeUxvYyIsImNhdGNoTG9jIiwiZmluYWxseUxvYyIsImFmdGVyTG9jIiwidHJ5RW50cmllcyIsInB1c2giLCJyZXNldFRyeUVudHJ5IiwiY29tcGxldGlvbiIsInJlc2V0IiwiaXRlcmFibGUiLCJpdGVyYXRvck1ldGhvZCIsImlzTmFOIiwibGVuZ3RoIiwiaSIsImRpc3BsYXlOYW1lIiwiaXNHZW5lcmF0b3JGdW5jdGlvbiIsImdlbkZ1biIsImN0b3IiLCJjb25zdHJ1Y3RvciIsIm5hbWUiLCJtYXJrIiwic2V0UHJvdG90eXBlT2YiLCJfX3Byb3RvX18iLCJhd3JhcCIsImFzeW5jIiwiUHJvbWlzZSIsIml0ZXIiLCJrZXlzIiwidmFsIiwib2JqZWN0IiwicmV2ZXJzZSIsInBvcCIsInNraXBUZW1wUmVzZXQiLCJwcmV2IiwiY2hhckF0Iiwic2xpY2UiLCJzdG9wIiwicm9vdFJlY29yZCIsInJ2YWwiLCJleGNlcHRpb24iLCJoYW5kbGUiLCJsb2MiLCJjYXVnaHQiLCJoYXNDYXRjaCIsImhhc0ZpbmFsbHkiLCJmaW5hbGx5RW50cnkiLCJjb21wbGV0ZSIsImZpbmlzaCIsIl9jYXRjaCIsInRocm93biIsImRlbGVnYXRlWWllbGQiLCJhc3luY0dlbmVyYXRvclN0ZXAiLCJnZW4iLCJfbmV4dCIsIl90aHJvdyIsIl9hc3luY1RvR2VuZXJhdG9yIiwiYXJncyIsImFyZ3VtZW50cyIsImFwcGx5IiwiX2NsYXNzQ2FsbENoZWNrIiwiaW5zdGFuY2UiLCJDb25zdHJ1Y3RvciIsIl9kZWZpbmVQcm9wZXJ0aWVzIiwidGFyZ2V0IiwicHJvcHMiLCJkZXNjcmlwdG9yIiwiX3RvUHJvcGVydHlLZXkiLCJfY3JlYXRlQ2xhc3MiLCJwcm90b1Byb3BzIiwic3RhdGljUHJvcHMiLCJfdG9QcmltaXRpdmUiLCJTdHJpbmciLCJpbnB1dCIsImhpbnQiLCJwcmltIiwidG9QcmltaXRpdmUiLCJyZXMiLCJOdW1iZXIiLCJtaiIsInJlcXVpcmUiLCJKYW51c1Nlc3Npb24iLCJzZW5kT3JpZ2luYWwiLCJzZW5kIiwic2lnbmFsIiwiZSIsIm1lc3NhZ2UiLCJpbmRleE9mIiwiY29uc29sZSIsIk5BRiIsImNvbm5lY3Rpb24iLCJhZGFwdGVyIiwicmVjb25uZWN0Iiwic2RwVXRpbHMiLCJkZWJ1ZyIsImxvZyIsIndhcm4iLCJpc1NhZmFyaSIsInRlc3QiLCJuYXZpZ2F0b3IiLCJ1c2VyQWdlbnQiLCJTVUJTQ1JJQkVfVElNRU9VVF9NUyIsImRlYm91bmNlIiwiY3VyciIsIl90aGlzIiwiQXJyYXkiLCJfIiwicmFuZG9tVWludCIsIk1hdGgiLCJmbG9vciIsInJhbmRvbSIsIk1BWF9TQUZFX0lOVEVHRVIiLCJ1bnRpbERhdGFDaGFubmVsT3BlbiIsImRhdGFDaGFubmVsIiwicmVhZHlTdGF0ZSIsInJlc29sdmVyIiwicmVqZWN0b3IiLCJjbGVhciIsInJlbW92ZUV2ZW50TGlzdGVuZXIiLCJhZGRFdmVudExpc3RlbmVyIiwiaXNIMjY0VmlkZW9TdXBwb3J0ZWQiLCJ2aWRlbyIsImRvY3VtZW50IiwiY3JlYXRlRWxlbWVudCIsImNhblBsYXlUeXBlIiwiT1BVU19QQVJBTUVURVJTIiwidXNlZHR4Iiwic3RlcmVvIiwiREVGQVVMVF9QRUVSX0NPTk5FQ1RJT05fQ09ORklHIiwiaWNlU2VydmVycyIsInVybHMiLCJXU19OT1JNQUxfQ0xPU1VSRSIsIkphbnVzQWRhcHRlciIsInJvb20iLCJjbGllbnRJZCIsImpvaW5Ub2tlbiIsInNlcnZlclVybCIsIndlYlJ0Y09wdGlvbnMiLCJwZWVyQ29ubmVjdGlvbkNvbmZpZyIsIndzIiwic2Vzc2lvbiIsInJlbGlhYmxlVHJhbnNwb3J0IiwidW5yZWxpYWJsZVRyYW5zcG9ydCIsImluaXRpYWxSZWNvbm5lY3Rpb25EZWxheSIsInJlY29ubmVjdGlvbkRlbGF5IiwicmVjb25uZWN0aW9uVGltZW91dCIsIm1heFJlY29ubmVjdGlvbkF0dGVtcHRzIiwicmVjb25uZWN0aW9uQXR0ZW1wdHMiLCJwdWJsaXNoZXIiLCJvY2N1cGFudHMiLCJsZWZ0T2NjdXBhbnRzIiwiU2V0IiwibWVkaWFTdHJlYW1zIiwibG9jYWxNZWRpYVN0cmVhbSIsInBlbmRpbmdNZWRpYVJlcXVlc3RzIiwiTWFwIiwiYmxvY2tlZENsaWVudHMiLCJmcm96ZW5VcGRhdGVzIiwidGltZU9mZnNldHMiLCJzZXJ2ZXJUaW1lUmVxdWVzdHMiLCJhdmdUaW1lT2Zmc2V0Iiwib25XZWJzb2NrZXRPcGVuIiwiYmluZCIsIm9uV2Vic29ja2V0Q2xvc2UiLCJvbldlYnNvY2tldE1lc3NhZ2UiLCJvbkRhdGFDaGFubmVsTWVzc2FnZSIsIm9uRGF0YSIsInNldFNlcnZlclVybCIsInVybCIsInNldEFwcCIsImFwcCIsInNldFJvb20iLCJyb29tTmFtZSIsInNldEpvaW5Ub2tlbiIsInNldENsaWVudElkIiwic2V0V2ViUnRjT3B0aW9ucyIsIm9wdGlvbnMiLCJzZXRQZWVyQ29ubmVjdGlvbkNvbmZpZyIsInNldFNlcnZlckNvbm5lY3RMaXN0ZW5lcnMiLCJzdWNjZXNzTGlzdGVuZXIiLCJmYWlsdXJlTGlzdGVuZXIiLCJjb25uZWN0U3VjY2VzcyIsImNvbm5lY3RGYWlsdXJlIiwic2V0Um9vbU9jY3VwYW50TGlzdGVuZXIiLCJvY2N1cGFudExpc3RlbmVyIiwib25PY2N1cGFudHNDaGFuZ2VkIiwic2V0RGF0YUNoYW5uZWxMaXN0ZW5lcnMiLCJvcGVuTGlzdGVuZXIiLCJjbG9zZWRMaXN0ZW5lciIsIm1lc3NhZ2VMaXN0ZW5lciIsIm9uT2NjdXBhbnRDb25uZWN0ZWQiLCJvbk9jY3VwYW50RGlzY29ubmVjdGVkIiwib25PY2N1cGFudE1lc3NhZ2UiLCJzZXRSZWNvbm5lY3Rpb25MaXN0ZW5lcnMiLCJyZWNvbm5lY3RpbmdMaXN0ZW5lciIsInJlY29ubmVjdGVkTGlzdGVuZXIiLCJyZWNvbm5lY3Rpb25FcnJvckxpc3RlbmVyIiwib25SZWNvbm5lY3RpbmciLCJvblJlY29ubmVjdGVkIiwib25SZWNvbm5lY3Rpb25FcnJvciIsInNldEV2ZW50TG9vcHMiLCJsb29wcyIsImNvbm5lY3QiLCJfdGhpczIiLCJjb25jYXQiLCJ3ZWJzb2NrZXRDb25uZWN0aW9uIiwiV2ViU29ja2V0IiwidGltZW91dE1zIiwid3NPbk9wZW4iLCJhbGwiLCJ1cGRhdGVUaW1lT2Zmc2V0IiwiZGlzY29ubmVjdCIsImNsZWFyVGltZW91dCIsInJlbW92ZUFsbE9jY3VwYW50cyIsImNvbm4iLCJjbG9zZSIsImRpc3Bvc2UiLCJkZWxheWVkUmVjb25uZWN0VGltZW91dCIsImlzRGlzY29ubmVjdGVkIiwiX29uV2Vic29ja2V0T3BlbiIsIl9jYWxsZWUiLCJhZGRPY2N1cGFudFByb21pc2VzIiwib2NjdXBhbnRJZCIsIl9jYWxsZWUkIiwiX2NvbnRleHQiLCJjcmVhdGVQdWJsaXNoZXIiLCJpbml0aWFsT2NjdXBhbnRzIiwiYWRkT2NjdXBhbnQiLCJldmVudCIsIl90aGlzMyIsImNvZGUiLCJzZXRUaW1lb3V0IiwiX3RoaXM0IiwicGVyZm9ybURlbGF5ZWRSZWNvbm5lY3QiLCJfdGhpczUiLCJyZWNlaXZlIiwiSlNPTiIsInBhcnNlIiwiZGF0YSIsIl9hZGRPY2N1cGFudCIsIl9jYWxsZWUyIiwic3Vic2NyaWJlciIsIl9jYWxsZWUyJCIsIl9jb250ZXh0MiIsInJlbW92ZU9jY3VwYW50IiwiY3JlYXRlU3Vic2NyaWJlciIsInNldE1lZGlhU3RyZWFtIiwibWVkaWFTdHJlYW0iLCJfeCIsIl9pdGVyYXRvciIsIl9jcmVhdGVGb3JPZkl0ZXJhdG9ySGVscGVyIiwiZ2V0T3duUHJvcGVydHlOYW1lcyIsIl9zdGVwIiwicyIsIm4iLCJmIiwiYWRkIiwiaGFzIiwibXNnIiwiZ2V0IiwiYXVkaW8iLCJhc3NvY2lhdGUiLCJfdGhpczYiLCJldiIsInNlbmRUcmlja2xlIiwiY2FuZGlkYXRlIiwiaWNlQ29ubmVjdGlvblN0YXRlIiwib2ZmZXIiLCJjcmVhdGVPZmZlciIsImNvbmZpZ3VyZVB1Ymxpc2hlclNkcCIsImZpeFNhZmFyaUljZVVGcmFnIiwibG9jYWwiLCJvIiwic2V0TG9jYWxEZXNjcmlwdGlvbiIsInJlbW90ZSIsImoiLCJzZW5kSnNlcCIsInIiLCJzZXRSZW1vdGVEZXNjcmlwdGlvbiIsImpzZXAiLCJvbiIsImFuc3dlciIsImNvbmZpZ3VyZVN1YnNjcmliZXJTZHAiLCJjcmVhdGVBbnN3ZXIiLCJhIiwiX2NyZWF0ZVB1Ymxpc2hlciIsIl9jYWxsZWUzIiwiX3RoaXM3Iiwid2VicnRjdXAiLCJyZWxpYWJsZUNoYW5uZWwiLCJ1bnJlbGlhYmxlQ2hhbm5lbCIsIl9jYWxsZWUzJCIsIl9jb250ZXh0MyIsIkphbnVzUGx1Z2luSGFuZGxlIiwiUlRDUGVlckNvbm5lY3Rpb24iLCJhdHRhY2giLCJwYXJzZUludCIsImNyZWF0ZURhdGFDaGFubmVsIiwib3JkZXJlZCIsIm1heFJldHJhbnNtaXRzIiwiZ2V0VHJhY2tzIiwidHJhY2siLCJhZGRUcmFjayIsInBsdWdpbmRhdGEiLCJyb29tX2lkIiwidXNlcl9pZCIsImJvZHkiLCJkaXNwYXRjaEV2ZW50IiwiQ3VzdG9tRXZlbnQiLCJkZXRhaWwiLCJieSIsInNlbmRKb2luIiwibm90aWZpY2F0aW9ucyIsInN1Y2Nlc3MiLCJyZXNwb25zZSIsInVzZXJzIiwiaW5jbHVkZXMiLCJzZHAiLCJyZXBsYWNlIiwibGluZSIsInB0IiwicGFyYW1ldGVycyIsImFzc2lnbiIsInBhcnNlRm10cCIsIndyaXRlRm10cCIsInBheWxvYWRUeXBlIiwiX2ZpeFNhZmFyaUljZVVGcmFnIiwiX2NhbGxlZTQiLCJfY2FsbGVlNCQiLCJfY29udGV4dDQiLCJfeDIiLCJfY3JlYXRlU3Vic2NyaWJlciIsIl9jYWxsZWU1IiwiX3RoaXM4IiwibWF4UmV0cmllcyIsIndlYnJ0Y0ZhaWxlZCIsInJlY2VpdmVycyIsIl9hcmdzNSIsIl9jYWxsZWU1JCIsIl9jb250ZXh0NSIsImxlZnRJbnRlcnZhbCIsInNldEludGVydmFsIiwiY2xlYXJJbnRlcnZhbCIsInRpbWVvdXQiLCJtZWRpYSIsIl9pT1NIYWNrRGVsYXllZEluaXRpYWxQZWVyIiwiTWVkaWFTdHJlYW0iLCJnZXRSZWNlaXZlcnMiLCJyZWNlaXZlciIsIl94MyIsInN1YnNjcmliZSIsInNlbmRNZXNzYWdlIiwia2luZCIsInRva2VuIiwidG9nZ2xlRnJlZXplIiwiZnJvemVuIiwidW5mcmVlemUiLCJmcmVlemUiLCJmbHVzaFBlbmRpbmdVcGRhdGVzIiwiZGF0YUZvclVwZGF0ZU11bHRpTWVzc2FnZSIsIm5ldHdvcmtJZCIsImwiLCJkIiwiZ2V0UGVuZGluZ0RhdGEiLCJkYXRhVHlwZSIsIm93bmVyIiwiZ2V0UGVuZGluZ0RhdGFGb3JOZXR3b3JrSWQiLCJfaXRlcmF0b3IyIiwiX3N0ZXAyIiwiX3N0ZXAyJHZhbHVlIiwiX3NsaWNlZFRvQXJyYXkiLCJzb3VyY2UiLCJzdG9yZU1lc3NhZ2UiLCJzdG9yZVNpbmdsZU1lc3NhZ2UiLCJpbmRleCIsInNldCIsInN0b3JlZE1lc3NhZ2UiLCJzdG9yZWREYXRhIiwiaXNPdXRkYXRlZE1lc3NhZ2UiLCJsYXN0T3duZXJUaW1lIiwiaXNDb250ZW1wb3JhbmVvdXNNZXNzYWdlIiwiY3JlYXRlZFdoaWxlRnJvemVuIiwiaXNGaXJzdFN5bmMiLCJjb21wb25lbnRzIiwiZW5hYmxlZCIsInNob3VsZFN0YXJ0Q29ubmVjdGlvblRvIiwiY2xpZW50Iiwic3RhcnRTdHJlYW1Db25uZWN0aW9uIiwiY2xvc2VTdHJlYW1Db25uZWN0aW9uIiwiZ2V0Q29ubmVjdFN0YXR1cyIsImFkYXB0ZXJzIiwiSVNfQ09OTkVDVEVEIiwiTk9UX0NPTk5FQ1RFRCIsIl91cGRhdGVUaW1lT2Zmc2V0IiwiX2NhbGxlZTYiLCJfdGhpczkiLCJjbGllbnRTZW50VGltZSIsInByZWNpc2lvbiIsInNlcnZlclJlY2VpdmVkVGltZSIsImNsaWVudFJlY2VpdmVkVGltZSIsInNlcnZlclRpbWUiLCJ0aW1lT2Zmc2V0IiwiX2NhbGxlZTYkIiwiX2NvbnRleHQ2IiwiRGF0ZSIsIm5vdyIsImZldGNoIiwibG9jYXRpb24iLCJocmVmIiwiY2FjaGUiLCJoZWFkZXJzIiwiZ2V0VGltZSIsInJlZHVjZSIsImFjYyIsIm9mZnNldCIsImdldFNlcnZlclRpbWUiLCJnZXRNZWRpYVN0cmVhbSIsIl90aGlzMTAiLCJhdWRpb1Byb21pc2UiLCJ2aWRlb1Byb21pc2UiLCJwcm9taXNlIiwic3RyZWFtIiwiYXVkaW9TdHJlYW0iLCJnZXRBdWRpb1RyYWNrcyIsInZpZGVvU3RyZWFtIiwiZ2V0VmlkZW9UcmFja3MiLCJfc2V0TG9jYWxNZWRpYVN0cmVhbSIsIl9jYWxsZWU3IiwiX3RoaXMxMSIsImV4aXN0aW5nU2VuZGVycyIsIm5ld1NlbmRlcnMiLCJ0cmFja3MiLCJfbG9vcCIsIl9jYWxsZWU3JCIsIl9jb250ZXh0OCIsImdldFNlbmRlcnMiLCJ0Iiwic2VuZGVyIiwiX2xvb3AkIiwiX2NvbnRleHQ3IiwiZmluZCIsInJlcGxhY2VUcmFjayIsInRvTG93ZXJDYXNlIiwicmVtb3ZlVHJhY2siLCJzZXRMb2NhbE1lZGlhU3RyZWFtIiwiX3g0IiwiZW5hYmxlTWljcm9waG9uZSIsInNlbmREYXRhIiwic3RyaW5naWZ5Iiwid2hvbSIsInNlbmREYXRhR3VhcmFudGVlZCIsImJyb2FkY2FzdERhdGEiLCJicm9hZGNhc3REYXRhR3VhcmFudGVlZCIsImtpY2siLCJwZXJtc1Rva2VuIiwiYmxvY2siLCJfdGhpczEyIiwidW5ibG9jayIsIl90aGlzMTMiLCJyZWdpc3RlciIsIm1vZHVsZSJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file diff --git a/dist/naf-janus-adapter.min.js b/dist/naf-janus-adapter.min.js index d97a9ab..c6580e1 100644 --- a/dist/naf-janus-adapter.min.js +++ b/dist/naf-janus-adapter.min.js @@ -1,2 +1,3 @@ -!function(e){var t={};function n(s){if(t[s])return t[s].exports;var r=t[s]={i:s,l:!1,exports:{}};return e[s].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,s){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:s})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var s=Object.create(null);if(n.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(s,r,function(t){return e[t]}.bind(null,r));return s},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){function s(e){return function(){var t=e.apply(this,arguments);return new Promise((function(e,n){return function s(r,i){try{var a=t[r](i),o=a.value}catch(e){return void n(e)}if(!a.done)return Promise.resolve(o).then((function(e){s("next",e)}),(function(e){s("throw",e)}));e(o)}("next")}))}}var r=n(1);r.JanusSession.prototype.sendOriginal=r.JanusSession.prototype.send,r.JanusSession.prototype.send=function(e,t){return this.sendOriginal(e,t).catch(e=>{if(!(e.message&&e.message.indexOf("timed out")>-1))throw e;console.error("web socket timed out"),NAF.connection.adapter.reconnect()})};var i=n(2),a=console.log,o=(console.warn,console.error),c=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);function l(e){var t=Promise.resolve();return function(){var n=Array.prototype.slice.call(arguments);t=t.then(t=>e.apply(this,n))}}function u(e){return new Promise((t,n)=>{if("open"===e.readyState)t();else{let s,r;const i=()=>{e.removeEventListener("open",s),e.removeEventListener("error",r)};s=()=>{i(),t()},r=()=>{i(),n()},e.addEventListener("open",s),e.addEventListener("error",r)}})}const d=""!==document.createElement("video").canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"'),p={usedtx:1,stereo:0,"sprop-stereo":0},h={iceServers:[{urls:"stun:stun1.l.google.com:19302"},{urls:"stun:stun2.l.google.com:19302"}]};class f{constructor(){this.room=null,this.clientId=null,this.joinToken=null,this.serverUrl=null,this.webRtcOptions={},this.peerConnectionConfig=null,this.ws=null,this.session=null,this.reliableTransport="datachannel",this.unreliableTransport="datachannel",this.initialReconnectionDelay=1e3*Math.random(),this.reconnectionDelay=this.initialReconnectionDelay,this.reconnectionTimeout=null,this.maxReconnectionAttempts=10,this.reconnectionAttempts=0,this.publisher=null,this.occupants={},this.leftOccupants=new Set,this.mediaStreams={},this.localMediaStream=null,this.pendingMediaRequests=new Map,this.blockedClients=new Map,this.frozenUpdates=new Map,this.timeOffsets=[],this.serverTimeRequests=0,this.avgTimeOffset=0,this.onWebsocketOpen=this.onWebsocketOpen.bind(this),this.onWebsocketClose=this.onWebsocketClose.bind(this),this.onWebsocketMessage=this.onWebsocketMessage.bind(this),this.onDataChannelMessage=this.onDataChannelMessage.bind(this),this.onData=this.onData.bind(this)}setServerUrl(e){this.serverUrl=e}setApp(e){}setRoom(e){this.room=e}setJoinToken(e){this.joinToken=e}setClientId(e){this.clientId=e}setWebRtcOptions(e){this.webRtcOptions=e}setPeerConnectionConfig(e){this.peerConnectionConfig=e}setServerConnectListeners(e,t){this.connectSuccess=e,this.connectFailure=t}setRoomOccupantListener(e){this.onOccupantsChanged=e}setDataChannelListeners(e,t,n){this.onOccupantConnected=e,this.onOccupantDisconnected=t,this.onOccupantMessage=n}setReconnectionListeners(e,t,n){this.onReconnecting=e,this.onReconnected=t,this.onReconnectionError=n}connect(){a("connecting to "+this.serverUrl);const e=new Promise((e,t)=>{this.ws=new WebSocket(this.serverUrl,"janus-protocol"),this.session=new r.JanusSession(this.ws.send.bind(this.ws),{timeoutMs:4e4}),this.ws.addEventListener("close",this.onWebsocketClose),this.ws.addEventListener("message",this.onWebsocketMessage),this.wsOnOpen=()=>{this.ws.removeEventListener("open",this.wsOnOpen),this.onWebsocketOpen().then(e).catch(t)},this.ws.addEventListener("open",this.wsOnOpen)});return Promise.all([e,this.updateTimeOffset()])}disconnect(){a("disconnecting"),clearTimeout(this.reconnectionTimeout),this.removeAllOccupants(),this.leftOccupants=new Set,this.publisher&&(this.publisher.conn.close(),this.publisher=null),this.session&&(this.session.dispose(),this.session=null),this.ws&&(this.ws.removeEventListener("open",this.wsOnOpen),this.ws.removeEventListener("close",this.onWebsocketClose),this.ws.removeEventListener("message",this.onWebsocketMessage),this.ws.close(),this.ws=null),this.delayedReconnectTimeout&&(clearTimeout(this.delayedReconnectTimeout),this.delayedReconnectTimeout=null)}isDisconnected(){return null===this.ws}onWebsocketOpen(){var e=this;return s((function*(){yield e.session.create(),e.publisher=yield e.createPublisher(),e.connectSuccess(e.clientId);const t=[];for(let n=0;nthis.reconnect(),this.reconnectionDelay))}reconnect(){this.disconnect(),this.connect().then(()=>{this.reconnectionDelay=this.initialReconnectionDelay,this.reconnectionAttempts=0,this.onReconnected&&this.onReconnected()}).catch(e=>{if(this.reconnectionDelay+=1e3,this.reconnectionAttempts++,this.reconnectionAttempts>this.maxReconnectionAttempts&&this.onReconnectionError)return this.onReconnectionError(new Error("Connection could not be reestablished, exceeded maximum number of reconnection attempts."));console.warn("Error during reconnect, retrying."),console.warn(e),this.onReconnecting&&this.onReconnecting(this.reconnectionDelay),this.reconnectionTimeout=setTimeout(()=>this.reconnect(),this.reconnectionDelay)})}performDelayedReconnect(){this.delayedReconnectTimeout&&clearTimeout(this.delayedReconnectTimeout),this.delayedReconnectTimeout=setTimeout(()=>{this.delayedReconnectTimeout=null,this.reconnect()},1e4)}onWebsocketMessage(e){this.session.receive(JSON.parse(e.data))}addOccupant(e){var t=this;return s((function*(){t.occupants[e]&&t.removeOccupant(e),t.leftOccupants.delete(e);var n=yield t.createSubscriber(e);if(n)return t.occupants[e]=n,t.setMediaStream(e,n.mediaStream),t.onOccupantConnected(e),t.onOccupantsChanged(t.occupants),n}))()}removeAllOccupants(){for(const e of Object.getOwnPropertyNames(this.occupants))this.removeOccupant(e)}removeOccupant(e){if(this.leftOccupants.add(e),this.occupants[e]&&(this.occupants[e].conn.close(),delete this.occupants[e]),this.mediaStreams[e]&&delete this.mediaStreams[e],this.pendingMediaRequests.has(e)){const t="The user disconnected before the media stream was resolved.";this.pendingMediaRequests.get(e).audio.reject(t),this.pendingMediaRequests.get(e).video.reject(t),this.pendingMediaRequests.delete(e)}this.onOccupantDisconnected(e),this.onOccupantsChanged(this.occupants)}associate(e,t){e.addEventListener("icecandidate",e=>{t.sendTrickle(e.candidate||null).catch(e=>o("Error trickling ICE: %o",e))}),e.addEventListener("iceconnectionstatechange",t=>{"connected"===e.iceConnectionState&&console.log("ICE state changed to connected"),"disconnected"===e.iceConnectionState&&console.warn("ICE state changed to disconnected"),"failed"===e.iceConnectionState&&(console.warn("ICE failure detected. Reconnecting in 10s."),this.performDelayedReconnect())}),e.addEventListener("negotiationneeded",l(n=>{a("Sending new offer for handle: %o",t);var s=e.createOffer().then(this.configurePublisherSdp).then(this.fixSafariIceUFrag),r=s.then(t=>e.setLocalDescription(t)),i=s;return i=i.then(this.fixSafariIceUFrag).then(e=>t.sendJsep(e)).then(t=>e.setRemoteDescription(t.jsep)),Promise.all([r,i]).catch(e=>o("Error negotiating offer: %o",e))})),t.on("event",l(n=>{var s=n.jsep;if(s&&"offer"==s.type){a("Accepting new offer for handle: %o",t);var r=e.setRemoteDescription(this.configureSubscriberSdp(s)).then(t=>e.createAnswer()).then(this.fixSafariIceUFrag),i=r.then(t=>e.setLocalDescription(t)),c=r.then(e=>t.sendJsep(e));return Promise.all([i,c]).catch(e=>o("Error negotiating answer: %o",e))}return null}))}createPublisher(){var e=this;return s((function*(){var t=new r.JanusPluginHandle(e.session),n=new RTCPeerConnection(e.peerConnectionConfig||h);a("pub waiting for sfu"),yield t.attach("janus.plugin.sfu"),e.associate(n,t),a("pub waiting for data channels & webrtcup");var s=new Promise((function(e){return t.on("webrtcup",e)})),i=n.createDataChannel("reliable",{ordered:!0}),o=n.createDataChannel("unreliable",{ordered:!1,maxRetransmits:0});i.addEventListener("message",(function(t){return e.onDataChannelMessage(t,"janus-reliable")})),o.addEventListener("message",(function(t){return e.onDataChannelMessage(t,"janus-unreliable")})),yield s,yield u(i),yield u(o),e.localMediaStream&&e.localMediaStream.getTracks().forEach((function(t){n.addTrack(t,e.localMediaStream)})),t.on("event",(function(t){var n=t.plugindata.data;if("join"==n.event&&n.room_id==e.room){if(e.delayedReconnectTimeout)return;e.addOccupant(n.user_id)}else"leave"==n.event&&n.room_id==e.room?e.removeOccupant(n.user_id):"blocked"==n.event?document.body.dispatchEvent(new CustomEvent("blocked",{detail:{clientId:n.by}})):"unblocked"==n.event?document.body.dispatchEvent(new CustomEvent("unblocked",{detail:{clientId:n.by}})):"data"===n.event&&e.onData(JSON.parse(n.body),"janus-event")})),a("pub waiting for join");var c=yield e.sendJoin(t,{notifications:!0,data:!0});if(!c.plugindata.data.success){const e=c.plugindata.data.error;throw console.error(e),n.close(),e}var l=c.plugindata.data.response.users[e.room]||[];return l.includes(e.clientId)&&(console.warn("Janus still has previous session for this client. Reconnecting in 10s."),e.performDelayedReconnect()),a("publisher ready"),{handle:t,initialOccupants:l,reliableChannel:i,unreliableChannel:o,conn:n}}))()}configurePublisherSdp(e){return e.sdp=e.sdp.replace(/a=fmtp:(109|111).*\r\n/g,(e,t)=>{const n=Object.assign(i.parseFmtp(e),p);return i.writeFmtp({payloadType:t,parameters:n})}),e}configureSubscriberSdp(e){return d||-1!==navigator.userAgent.indexOf("HeadlessChrome")&&(e.sdp=e.sdp.replace(/m=video[^]*m=/,"m=")),-1===navigator.userAgent.indexOf("Android")?e.sdp=e.sdp.replace("a=rtcp-fb:107 goog-remb\r\n","a=rtcp-fb:107 goog-remb\r\na=rtcp-fb:107 transport-cc\r\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\n"):e.sdp=e.sdp.replace("a=rtcp-fb:107 goog-remb\r\n","a=rtcp-fb:107 goog-remb\r\na=rtcp-fb:107 transport-cc\r\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f\r\n"),e}fixSafariIceUFrag(e){return s((function*(){return e.sdp=e.sdp.replace(/[^\r]\na=ice-ufrag/g,"\r\na=ice-ufrag"),e}))()}createSubscriber(e,t=5){var n=this;return s((function*(){if(n.leftOccupants.has(e))return console.warn(e+": cancelled occupant connection, occupant left before subscription negotation."),null;var s=new r.JanusPluginHandle(n.session),i=new RTCPeerConnection(n.peerConnectionConfig||h);if(a(e+": sub waiting for sfu"),yield s.attach("janus.plugin.sfu"),n.associate(i,s),a(e+": sub waiting for join"),n.leftOccupants.has(e))return i.close(),console.warn(e+": cancelled occupant connection, occupant left after attach"),null;let o=!1;const l=new Promise((function(t){const r=setInterval((function(){n.leftOccupants.has(e)&&(clearInterval(r),t())}),1e3),i=setTimeout((function(){clearInterval(r),o=!0,t()}),15e3);s.on("webrtcup",(function(){clearTimeout(i),clearInterval(r),t()}))}));if(yield n.sendJoin(s,{media:e}),n.leftOccupants.has(e))return i.close(),console.warn(e+": cancelled occupant connection, occupant left after join"),null;if(a(e+": sub waiting for webrtcup"),yield l,n.leftOccupants.has(e))return i.close(),console.warn(e+": cancel occupant connection, occupant left during or after webrtcup"),null;if(o)return i.close(),t>0?(console.warn(e+": webrtc up timed out, retrying"),n.createSubscriber(e,t-1)):(console.warn(e+": webrtc up timed out"),null);c&&!n._iOSHackDelayedInitialPeer&&(yield new Promise((function(e){return setTimeout(e,3e3)})),n._iOSHackDelayedInitialPeer=!0);var u=new MediaStream;return i.getReceivers().forEach((function(e){e.track&&u.addTrack(e.track)})),0===u.getTracks().length&&(u=null),a(e+": subscriber ready"),{handle:s,mediaStream:u,conn:i}}))()}sendJoin(e,t){return e.sendMessage({kind:"join",room_id:this.room,user_id:this.clientId,subscribe:t,token:this.joinToken})}toggleFreeze(){this.frozen?this.unfreeze():this.freeze()}freeze(){this.frozen=!0}unfreeze(){this.frozen=!1,this.flushPendingUpdates()}dataForUpdateMultiMessage(e,t){for(let n=0,s=t.data.d.length;nn.owner)return;if("r"===s){i&&i.isFirstSync?this.frozenUpdates.delete(r):this.frozenUpdates.set(r,e)}else i.components&&n.components&&Object.assign(i.components,n.components)}else this.frozenUpdates.set(r,e)}onDataChannelMessage(e,t){this.onData(JSON.parse(e.data),t)}onData(e,t){a.enabled&&a("DC in: "+e),e.dataType&&(e.source=t,this.frozen?this.storeMessage(e):this.onOccupantMessage(null,e.dataType,e.data,e.source))}shouldStartConnectionTo(e){return!0}startStreamConnection(e){}closeStreamConnection(e){}getConnectStatus(e){return this.occupants[e]?NAF.adapters.IS_CONNECTED:NAF.adapters.NOT_CONNECTED}updateTimeOffset(){var e=this;return s((function*(){if(e.isDisconnected())return;const t=Date.now(),n=yield fetch(document.location.href,{method:"HEAD",cache:"no-cache"}),s=new Date(n.headers.get("Date")).getTime()+500,r=Date.now(),i=s+(r-t)/2-r;e.serverTimeRequests++,e.serverTimeRequests<=10?e.timeOffsets.push(i):e.timeOffsets[e.serverTimeRequests%10]=i,e.avgTimeOffset=e.timeOffsets.reduce((function(e,t){return e+t}),0)/e.timeOffsets.length,e.serverTimeRequests>10?(a(`new server time offset: ${e.avgTimeOffset}ms`),setTimeout((function(){return e.updateTimeOffset()}),3e5)):e.updateTimeOffset()}))()}getServerTime(){return Date.now()+this.avgTimeOffset}getMediaStream(e,t="audio"){if(this.mediaStreams[e])return a(`Already had ${t} for ${e}`),Promise.resolve(this.mediaStreams[e][t]);if(a(`Waiting on ${t} for ${e}`),!this.pendingMediaRequests.has(e)){this.pendingMediaRequests.set(e,{});const t=new Promise((t,n)=>{this.pendingMediaRequests.get(e).audio={resolve:t,reject:n}}),n=new Promise((t,n)=>{this.pendingMediaRequests.get(e).video={resolve:t,reject:n}});this.pendingMediaRequests.get(e).audio.promise=t,this.pendingMediaRequests.get(e).video.promise=n,t.catch(t=>console.warn(e+" getMediaStream Audio Error",t)),n.catch(t=>console.warn(e+" getMediaStream Video Error",t))}return this.pendingMediaRequests.get(e)[t].promise}setMediaStream(e,t){const n=new MediaStream;try{t.getAudioTracks().forEach(e=>n.addTrack(e))}catch(t){console.warn(e+" setMediaStream Audio Error",t)}const s=new MediaStream;try{t.getVideoTracks().forEach(e=>s.addTrack(e))}catch(t){console.warn(e+" setMediaStream Video Error",t)}this.mediaStreams[e]={audio:n,video:s},this.pendingMediaRequests.has(e)&&(this.pendingMediaRequests.get(e).audio.resolve(n),this.pendingMediaRequests.get(e).video.resolve(s))}setLocalMediaStream(e){var t=this;return s((function*(){if(t.publisher&&t.publisher.conn){const n=t.publisher.conn.getSenders(),s=[],r=e.getTracks();for(let i=0;i-1&&(a.enabled=!1,setTimeout((function(){return a.enabled=!0}),1e3))):(e.removeTrack(o.track),e.addTrack(a)),s.push(o)):s.push(t.publisher.conn.addTrack(a,e))}n.forEach((function(e){s.includes(e)||(e.track.enabled=!1)}))}t.localMediaStream=e,t.setMediaStream(t.clientId,e)}))()}enableMicrophone(e){this.publisher&&this.publisher.conn&&this.publisher.conn.getSenders().forEach(t=>{"audio"==t.track.kind&&(t.track.enabled=e)})}sendData(e,t,n){if(this.publisher)switch(this.unreliableTransport){case"websocket":this.publisher.handle.sendMessage({kind:"data",body:JSON.stringify({dataType:t,data:n}),whom:e});break;case"datachannel":this.publisher.unreliableChannel.send(JSON.stringify({clientId:e,dataType:t,data:n}));break;default:this.unreliableTransport(e,t,n)}else console.warn("sendData called without a publisher")}sendDataGuaranteed(e,t,n){if(this.publisher)switch(this.reliableTransport){case"websocket":this.publisher.handle.sendMessage({kind:"data",body:JSON.stringify({dataType:t,data:n}),whom:e});break;case"datachannel":this.publisher.reliableChannel.send(JSON.stringify({clientId:e,dataType:t,data:n}));break;default:this.reliableTransport(e,t,n)}else console.warn("sendDataGuaranteed called without a publisher")}broadcastData(e,t){if(this.publisher)switch(this.unreliableTransport){case"websocket":this.publisher.handle.sendMessage({kind:"data",body:JSON.stringify({dataType:e,data:t})});break;case"datachannel":this.publisher.unreliableChannel.send(JSON.stringify({dataType:e,data:t}));break;default:this.unreliableTransport(void 0,e,t)}else console.warn("broadcastData called without a publisher")}broadcastDataGuaranteed(e,t){if(this.publisher)switch(this.reliableTransport){case"websocket":this.publisher.handle.sendMessage({kind:"data",body:JSON.stringify({dataType:e,data:t})});break;case"datachannel":this.publisher.reliableChannel.send(JSON.stringify({dataType:e,data:t}));break;default:this.reliableTransport(void 0,e,t)}else console.warn("broadcastDataGuaranteed called without a publisher")}kick(e,t){return this.publisher.handle.sendMessage({kind:"kick",room_id:this.room,user_id:e,token:t}).then(()=>{document.body.dispatchEvent(new CustomEvent("kicked",{detail:{clientId:e}}))})}block(e){return this.publisher.handle.sendMessage({kind:"block",whom:e}).then(()=>{this.blockedClients.set(e,!0),document.body.dispatchEvent(new CustomEvent("blocked",{detail:{clientId:e}}))})}unblock(e){return this.publisher.handle.sendMessage({kind:"unblock",whom:e}).then(()=>{this.blockedClients.delete(e),document.body.dispatchEvent(new CustomEvent("unblocked",{detail:{clientId:e}}))})}}NAF.adapters.register("janus",f),e.exports=f},function(e,t){function n(e){this.session=e,this.id=void 0}function s(e,t){this.output=e,this.id=void 0,this.nextTxId=0,this.txns={},this.eventHandlers={},this.options=Object.assign({verbose:!1,timeoutMs:1e4,keepaliveMs:3e4},t)}n.prototype.attach=function(e){var t={plugin:e,"force-bundle":!0,"force-rtcp-mux":!0};return this.session.send("attach",t).then(e=>(this.id=e.data.id,e))},n.prototype.detach=function(){return this.send("detach")},n.prototype.on=function(e,t){return this.session.on(e,e=>{e.sender==this.id&&t(e)})},n.prototype.send=function(e,t){return this.session.send(e,Object.assign({handle_id:this.id},t))},n.prototype.sendMessage=function(e){return this.send("message",{body:e})},n.prototype.sendJsep=function(e){return this.send("message",{body:{},jsep:e})},n.prototype.sendTrickle=function(e){return this.send("trickle",{candidate:e})},s.prototype.create=function(){return this.send("create").then(e=>(this.id=e.data.id,e))},s.prototype.destroy=function(){return this.send("destroy").then(e=>(this.dispose(),e))},s.prototype.dispose=function(){for(var e in this._killKeepalive(),this.eventHandlers={},this.txns)if(this.txns.hasOwnProperty(e)){var t=this.txns[e];clearTimeout(t.timeout),t.reject(new Error("Janus session was disposed.")),delete this.txns[e]}},s.prototype.isError=function(e){return"error"===e.janus},s.prototype.on=function(e,t){var n=this.eventHandlers[e];null==n&&(n=this.eventHandlers[e]=[]),n.push(t)},s.prototype.receive=function(e){this.options.verbose&&this._logIncoming(e),e.session_id!=this.id&&console.warn("Incorrect session ID received in Janus signalling message: was "+e.session_id+", expected "+this.id+".");var t=e.janus,n=this.eventHandlers[t];if(null!=n)for(var s=0;s{var r=null;this.options.timeoutMs&&(r=setTimeout(()=>{delete this.txns[t.transaction],s(new Error("Signalling transaction with txid "+t.transaction+" timed out."))},this.options.timeoutMs)),this.txns[t.transaction]={resolve:n,reject:s,timeout:r,type:e},this._transmit(e,t)})},s.prototype._transmit=function(e,t){t=Object.assign({janus:e},t),null!=this.id&&(t=Object.assign({session_id:this.id},t)),this.options.verbose&&this._logOutgoing(t),this.output(JSON.stringify(t)),this._resetKeepalive()},s.prototype._logOutgoing=function(e){var t=e.janus;"message"===t&&e.jsep&&(t=e.jsep.type);var n="> Outgoing Janus "+(t||"signal")+" (#"+e.transaction+"): ";console.debug("%c"+n,"color: #040",e)},s.prototype._logIncoming=function(e){var t=e.janus,n=e.transaction?"< Incoming Janus "+(t||"signal")+" (#"+e.transaction+"): ":"< Incoming Janus "+(t||"signal")+": ";console.debug("%c"+n,"color: #004",e)},s.prototype._sendKeepalive=function(){return this.send("keepalive")},s.prototype._killKeepalive=function(){clearTimeout(this.keepaliveTimeout)},s.prototype._resetKeepalive=function(){this._killKeepalive(),this.options.keepaliveMs&&(this.keepaliveTimeout=setTimeout(()=>{this._sendKeepalive().catch(e=>console.error("Error received from keepalive: ",e))},this.options.keepaliveMs))},e.exports={JanusPluginHandle:n,JanusSession:s}},function(e,t,n){"use strict";const s={generateIdentifier:function(){return Math.random().toString(36).substr(2,10)}};s.localCName=s.generateIdentifier(),s.splitLines=function(e){return e.trim().split("\n").map(e=>e.trim())},s.splitSections=function(e){return e.split("\nm=").map((e,t)=>(t>0?"m="+e:e).trim()+"\r\n")},s.getDescription=function(e){const t=s.splitSections(e);return t&&t[0]},s.getMediaSections=function(e){const t=s.splitSections(e);return t.shift(),t},s.matchPrefix=function(e,t){return s.splitLines(e).filter(e=>0===e.indexOf(t))},s.parseCandidate=function(e){let t;t=0===e.indexOf("a=candidate:")?e.substring(12).split(" "):e.substring(10).split(" ");const n={foundation:t[0],component:{1:"rtp",2:"rtcp"}[t[1]]||t[1],protocol:t[2].toLowerCase(),priority:parseInt(t[3],10),ip:t[4],address:t[4],port:parseInt(t[5],10),type:t[7]};for(let e=8;e0?t[0].split("/")[1]:"sendrecv",uri:t[1]}},s.writeExtmap=function(e){return"a=extmap:"+(e.id||e.preferredId)+(e.direction&&"sendrecv"!==e.direction?"/"+e.direction:"")+" "+e.uri+"\r\n"},s.parseFmtp=function(e){const t={};let n;const s=e.substr(e.indexOf(" ")+1).split(";");for(let e=0;e{void 0!==e.parameters[t]?s.push(t+"="+e.parameters[t]):s.push(t)}),t+="a=fmtp:"+n+" "+s.join(";")+"\r\n"}return t},s.parseRtcpFb=function(e){const t=e.substr(e.indexOf(" ")+1).split(" ");return{type:t.shift(),parameter:t.join(" ")}},s.writeRtcpFb=function(e){let t="",n=e.payloadType;return void 0!==e.preferredPayloadType&&(n=e.preferredPayloadType),e.rtcpFeedback&&e.rtcpFeedback.length&&e.rtcpFeedback.forEach(e=>{t+="a=rtcp-fb:"+n+" "+e.type+(e.parameter&&e.parameter.length?" "+e.parameter:"")+"\r\n"}),t},s.parseSsrcMedia=function(e){const t=e.indexOf(" "),n={ssrc:parseInt(e.substr(7,t-7),10)},s=e.indexOf(":",t);return s>-1?(n.attribute=e.substr(t+1,s-t-1),n.value=e.substr(s+1)):n.attribute=e.substr(t+1),n},s.parseSsrcGroup=function(e){const t=e.substr(13).split(" ");return{semantics:t.shift(),ssrcs:t.map(e=>parseInt(e,10))}},s.getMid=function(e){const t=s.matchPrefix(e,"a=mid:")[0];if(t)return t.substr(6)},s.parseFingerprint=function(e){const t=e.substr(14).split(" ");return{algorithm:t[0].toLowerCase(),value:t[1].toUpperCase()}},s.getDtlsParameters=function(e,t){return{role:"auto",fingerprints:s.matchPrefix(e+t,"a=fingerprint:").map(s.parseFingerprint)}},s.writeDtlsParameters=function(e,t){let n="a=setup:"+t+"\r\n";return e.fingerprints.forEach(e=>{n+="a=fingerprint:"+e.algorithm+" "+e.value+"\r\n"}),n},s.parseCryptoLine=function(e){const t=e.substr(9).split(" ");return{tag:parseInt(t[0],10),cryptoSuite:t[1],keyParams:t[2],sessionParams:t.slice(3)}},s.writeCryptoLine=function(e){return"a=crypto:"+e.tag+" "+e.cryptoSuite+" "+("object"==typeof e.keyParams?s.writeCryptoKeyParams(e.keyParams):e.keyParams)+(e.sessionParams?" "+e.sessionParams.join(" "):"")+"\r\n"},s.parseCryptoKeyParams=function(e){if(0!==e.indexOf("inline:"))return null;const t=e.substr(7).split("|");return{keyMethod:"inline",keySalt:t[0],lifeTime:t[1],mkiValue:t[2]?t[2].split(":")[0]:void 0,mkiLength:t[2]?t[2].split(":")[1]:void 0}},s.writeCryptoKeyParams=function(e){return e.keyMethod+":"+e.keySalt+(e.lifeTime?"|"+e.lifeTime:"")+(e.mkiValue&&e.mkiLength?"|"+e.mkiValue+":"+e.mkiLength:"")},s.getCryptoParameters=function(e,t){return s.matchPrefix(e+t,"a=crypto:").map(s.parseCryptoLine)},s.getIceParameters=function(e,t){const n=s.matchPrefix(e+t,"a=ice-ufrag:")[0],r=s.matchPrefix(e+t,"a=ice-pwd:")[0];return n&&r?{usernameFragment:n.substr(12),password:r.substr(10)}:null},s.writeIceParameters=function(e){let t="a=ice-ufrag:"+e.usernameFragment+"\r\na=ice-pwd:"+e.password+"\r\n";return e.iceLite&&(t+="a=ice-lite\r\n"),t},s.parseRtpParameters=function(e){const t={codecs:[],headerExtensions:[],fecMechanisms:[],rtcp:[]},n=s.splitLines(e)[0].split(" ");for(let r=3;r{t.headerExtensions.push(s.parseExtmap(e))}),t},s.writeRtpDescription=function(e,t){let n="";n+="m="+e+" ",n+=t.codecs.length>0?"9":"0",n+=" UDP/TLS/RTP/SAVPF ",n+=t.codecs.map(e=>void 0!==e.preferredPayloadType?e.preferredPayloadType:e.payloadType).join(" ")+"\r\n",n+="c=IN IP4 0.0.0.0\r\n",n+="a=rtcp:9 IN IP4 0.0.0.0\r\n",t.codecs.forEach(e=>{n+=s.writeRtpMap(e),n+=s.writeFmtp(e),n+=s.writeRtcpFb(e)});let r=0;return t.codecs.forEach(e=>{e.maxptime>r&&(r=e.maxptime)}),r>0&&(n+="a=maxptime:"+r+"\r\n"),t.headerExtensions&&t.headerExtensions.forEach(e=>{n+=s.writeExtmap(e)}),n},s.parseRtpEncodingParameters=function(e){const t=[],n=s.parseRtpParameters(e),r=-1!==n.fecMechanisms.indexOf("RED"),i=-1!==n.fecMechanisms.indexOf("ULPFEC"),a=s.matchPrefix(e,"a=ssrc:").map(e=>s.parseSsrcMedia(e)).filter(e=>"cname"===e.attribute),o=a.length>0&&a[0].ssrc;let c;const l=s.matchPrefix(e,"a=ssrc-group:FID").map(e=>e.substr(17).split(" ").map(e=>parseInt(e,10)));l.length>0&&l[0].length>1&&l[0][0]===o&&(c=l[0][1]),n.codecs.forEach(e=>{if("RTX"===e.name.toUpperCase()&&e.parameters.apt){let n={ssrc:o,codecPayloadType:parseInt(e.parameters.apt,10)};o&&c&&(n.rtx={ssrc:c}),t.push(n),r&&(n=JSON.parse(JSON.stringify(n)),n.fec={ssrc:o,mechanism:i?"red+ulpfec":"red"},t.push(n))}}),0===t.length&&o&&t.push({ssrc:o});let u=s.matchPrefix(e,"b=");return u.length&&(u=0===u[0].indexOf("b=TIAS:")?parseInt(u[0].substr(7),10):0===u[0].indexOf("b=AS:")?1e3*parseInt(u[0].substr(5),10)*.95-16e3:void 0,t.forEach(e=>{e.maxBitrate=u})),t},s.parseRtcpParameters=function(e){const t={},n=s.matchPrefix(e,"a=ssrc:").map(e=>s.parseSsrcMedia(e)).filter(e=>"cname"===e.attribute)[0];n&&(t.cname=n.value,t.ssrc=n.ssrc);const r=s.matchPrefix(e,"a=rtcp-rsize");t.reducedSize=r.length>0,t.compound=0===r.length;const i=s.matchPrefix(e,"a=rtcp-mux");return t.mux=i.length>0,t},s.writeRtcpParameters=function(e){let t="";return e.reducedSize&&(t+="a=rtcp-rsize\r\n"),e.mux&&(t+="a=rtcp-mux\r\n"),void 0!==e.ssrc&&e.cname&&(t+="a=ssrc:"+e.ssrc+" cname:"+e.cname+"\r\n"),t},s.parseMsid=function(e){let t;const n=s.matchPrefix(e,"a=msid:");if(1===n.length)return t=n[0].substr(7).split(" "),{stream:t[0],track:t[1]};const r=s.matchPrefix(e,"a=ssrc:").map(e=>s.parseSsrcMedia(e)).filter(e=>"msid"===e.attribute);return r.length>0?(t=r[0].value.split(" "),{stream:t[0],track:t[1]}):void 0},s.parseSctpDescription=function(e){const t=s.parseMLine(e),n=s.matchPrefix(e,"a=max-message-size:");let r;n.length>0&&(r=parseInt(n[0].substr(19),10)),isNaN(r)&&(r=65536);const i=s.matchPrefix(e,"a=sctp-port:");if(i.length>0)return{port:parseInt(i[0].substr(12),10),protocol:t.fmt,maxMessageSize:r};const a=s.matchPrefix(e,"a=sctpmap:");if(a.length>0){const e=a[0].substr(10).split(" ");return{port:parseInt(e[0],10),protocol:e[1],maxMessageSize:r}}},s.writeSctpDescription=function(e,t){let n=[];return n="DTLS/SCTP"!==e.protocol?["m="+e.kind+" 9 "+e.protocol+" "+t.protocol+"\r\n","c=IN IP4 0.0.0.0\r\n","a=sctp-port:"+t.port+"\r\n"]:["m="+e.kind+" 9 "+e.protocol+" "+t.port+"\r\n","c=IN IP4 0.0.0.0\r\n","a=sctpmap:"+t.port+" "+t.protocol+" 65535\r\n"],void 0!==t.maxMessageSize&&n.push("a=max-message-size:"+t.maxMessageSize+"\r\n"),n.join("")},s.generateSessionId=function(){return Math.random().toString().substr(2,21)},s.writeSessionBoilerplate=function(e,t,n){let r;const i=void 0!==t?t:2;r=e||s.generateSessionId();return"v=0\r\no="+(n||"thisisadapterortc")+" "+r+" "+i+" IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\n"},s.getDirection=function(e,t){const n=s.splitLines(e);for(let e=0;e{var e={806:e=>{function t(e){this.session=e,this.id=void 0}function n(e,t){this.output=e,this.id=void 0,this.nextTxId=0,this.txns={},this.eventHandlers={},this.options=Object.assign({verbose:!1,timeoutMs:1e4,keepaliveMs:3e4},t)}t.prototype.attach=function(e,t){var n={plugin:e,loop_index:t,"force-bundle":!0,"force-rtcp-mux":!0};return this.session.send("attach",n).then((e=>(this.id=e.data.id,e)))},t.prototype.detach=function(){return this.send("detach")},t.prototype.on=function(e,t){return this.session.on(e,(e=>{e.sender==this.id&&t(e)}))},t.prototype.send=function(e,t){return this.session.send(e,Object.assign({handle_id:this.id},t))},t.prototype.sendMessage=function(e){return this.send("message",{body:e})},t.prototype.sendJsep=function(e){return this.send("message",{body:{},jsep:e})},t.prototype.sendTrickle=function(e){return this.send("trickle",{candidate:e})},n.prototype.create=function(){return this.send("create").then((e=>(this.id=e.data.id,e)))},n.prototype.destroy=function(){return this.send("destroy").then((e=>(this.dispose(),e)))},n.prototype.dispose=function(){for(var e in this._killKeepalive(),this.eventHandlers={},this.txns)if(this.txns.hasOwnProperty(e)){var t=this.txns[e];clearTimeout(t.timeout),t.reject(new Error("Janus session was disposed.")),delete this.txns[e]}},n.prototype.isError=function(e){return"error"===e.janus},n.prototype.on=function(e,t){var n=this.eventHandlers[e];null==n&&(n=this.eventHandlers[e]=[]),n.push(t)},n.prototype.receive=function(e){this.options.verbose&&this._logIncoming(e),e.session_id!=this.id&&console.warn("Incorrect session ID received in Janus signalling message: was "+e.session_id+", expected "+this.id+".");var t=e.janus,n=this.eventHandlers[t];if(null!=n)for(var r=0;r{var i=null;this.options.timeoutMs&&(i=setTimeout((()=>{delete this.txns[t.transaction],r(new Error("Signalling transaction with txid "+t.transaction+" timed out."))}),this.options.timeoutMs)),this.txns[t.transaction]={resolve:n,reject:r,timeout:i,type:e},this._transmit(e,t)}))},n.prototype._transmit=function(e,t){t=Object.assign({janus:e},t),null!=this.id&&(t=Object.assign({session_id:this.id},t)),this.options.verbose&&this._logOutgoing(t),this.output(JSON.stringify(t)),this._resetKeepalive()},n.prototype._logOutgoing=function(e){var t=e.janus;"message"===t&&e.jsep&&(t=e.jsep.type);var n="> Outgoing Janus "+(t||"signal")+" (#"+e.transaction+"): ";console.debug("%c"+n,"color: #040",e)},n.prototype._logIncoming=function(e){var t=e.janus,n=e.transaction?"< Incoming Janus "+(t||"signal")+" (#"+e.transaction+"): ":"< Incoming Janus "+(t||"signal")+": ";console.debug("%c"+n,"color: #004",e)},n.prototype._sendKeepalive=function(){return this.send("keepalive")},n.prototype._killKeepalive=function(){clearTimeout(this.keepaliveTimeout)},n.prototype._resetKeepalive=function(){this._killKeepalive(),this.options.keepaliveMs&&(this.keepaliveTimeout=setTimeout((()=>{this._sendKeepalive().catch((e=>console.error("Error received from keepalive: ",e)))}),this.options.keepaliveMs))},e.exports={JanusPluginHandle:t,JanusSession:n}},579:(e,t,n)=>{function r(e){return r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},r(e)}function i(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=s(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,i=function(){};return{s:i,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,o=!0,c=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return o=e.done,e},e:function(e){c=!0,a=e},f:function(){try{o||null==n.return||n.return()}finally{if(c)throw a}}}}function s(e,t){if(e){if("string"==typeof e)return a(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?a(e,t):void 0}}function a(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n=0;--i){var s=this.tryEntries[i],a=s.completion;if("root"===s.tryLoc)return r("end");if(s.tryLoc<=this.prev){var o=n.call(s,"catchLoc"),c=n.call(s,"finallyLoc");if(o&&c){if(this.prev=0;--r){var i=this.tryEntries[r];if(i.tryLoc<=this.prev&&n.call(i,"finallyLoc")&&this.prev=0;--t){var n=this.tryEntries[t];if(n.finallyLoc===e)return this.complete(n.completion,n.afterLoc),P(n),h}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var n=this.tryEntries[t];if(n.tryLoc===e){var r=n.completion;if("throw"===r.type){var i=r.arg;P(n)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(e,t,n){return this.delegate={iterator:M(e),resultName:t,nextLoc:n},"next"===this.method&&(this.arg=void 0),h}},e}function c(e,t,n,r,i,s,a){try{var o=e[s](a),c=o.value}catch(e){return void n(e)}o.done?t(c):Promise.resolve(c).then(r,i)}function u(e){return function(){var t=this,n=arguments;return new Promise((function(r,i){var s=e.apply(t,n);function a(e){c(s,r,i,a,o,"next",e)}function o(e){c(s,r,i,a,o,"throw",e)}a(void 0)}))}}function l(e,t){for(var n=0;n-1))throw e;console.error("web socket timed out"),NAF.connection.adapter.reconnect()}))};var d=n(539),h=console.log,f=(console.warn,console.error),m=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);function g(e){var t=Promise.resolve();return function(){var n=this,r=Array.prototype.slice.call(arguments);t=t.then((function(t){return e.apply(n,r)}))}}function v(e){return new Promise((function(t,n){if("open"===e.readyState)t();else{var r,i,s=function(){e.removeEventListener("open",r),e.removeEventListener("error",i)};r=function(){s(),t()},i=function(){s(),n()},e.addEventListener("open",r),e.addEventListener("error",i)}}))}var y=""!==document.createElement("video").canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"'),b={usedtx:1,stereo:0,"sprop-stereo":0},w={iceServers:[{urls:"stun:stun1.l.google.com:19302"},{urls:"stun:stun2.l.google.com:19302"}]},k=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.room=null,this.clientId=null,this.joinToken=null,this.serverUrl=null,this.webRtcOptions={},this.peerConnectionConfig=null,this.ws=null,this.session=null,this.reliableTransport="datachannel",this.unreliableTransport="datachannel",this.initialReconnectionDelay=1e3*Math.random(),this.reconnectionDelay=this.initialReconnectionDelay,this.reconnectionTimeout=null,this.maxReconnectionAttempts=10,this.reconnectionAttempts=0,this.publisher=null,this.occupants={},this.leftOccupants=new Set,this.mediaStreams={},this.localMediaStream=null,this.pendingMediaRequests=new Map,this.blockedClients=new Map,this.frozenUpdates=new Map,this.timeOffsets=[],this.serverTimeRequests=0,this.avgTimeOffset=0,this.onWebsocketOpen=this.onWebsocketOpen.bind(this),this.onWebsocketClose=this.onWebsocketClose.bind(this),this.onWebsocketMessage=this.onWebsocketMessage.bind(this),this.onDataChannelMessage=this.onDataChannelMessage.bind(this),this.onData=this.onData.bind(this)}var t,n,r,a,c,k,x,T,S;return t=e,n=[{key:"setServerUrl",value:function(e){this.serverUrl=e}},{key:"setApp",value:function(e){}},{key:"setRoom",value:function(e){this.room=e}},{key:"setJoinToken",value:function(e){this.joinToken=e}},{key:"setClientId",value:function(e){this.clientId=e}},{key:"setWebRtcOptions",value:function(e){this.webRtcOptions=e}},{key:"setPeerConnectionConfig",value:function(e){this.peerConnectionConfig=e}},{key:"setServerConnectListeners",value:function(e,t){this.connectSuccess=e,this.connectFailure=t}},{key:"setRoomOccupantListener",value:function(e){this.onOccupantsChanged=e}},{key:"setDataChannelListeners",value:function(e,t,n){this.onOccupantConnected=e,this.onOccupantDisconnected=t,this.onOccupantMessage=n}},{key:"setReconnectionListeners",value:function(e,t,n){this.onReconnecting=e,this.onReconnected=t,this.onReconnectionError=n}},{key:"setEventLoops",value:function(e){this.loops=e}},{key:"connect",value:function(){var e=this;h("connecting to ".concat(this.serverUrl));var t=new Promise((function(t,n){e.ws=new WebSocket(e.serverUrl,"janus-protocol"),e.session=new p.JanusSession(e.ws.send.bind(e.ws),{timeoutMs:4e4}),e.ws.addEventListener("close",e.onWebsocketClose),e.ws.addEventListener("message",e.onWebsocketMessage),e.wsOnOpen=function(){e.ws.removeEventListener("open",e.wsOnOpen),e.onWebsocketOpen().then(t).catch(n)},e.ws.addEventListener("open",e.wsOnOpen)}));return Promise.all([t,this.updateTimeOffset()])}},{key:"disconnect",value:function(){h("disconnecting"),clearTimeout(this.reconnectionTimeout),this.removeAllOccupants(),this.leftOccupants=new Set,this.publisher&&(this.publisher.conn.close(),this.publisher=null),this.session&&(this.session.dispose(),this.session=null),this.ws&&(this.ws.removeEventListener("open",this.wsOnOpen),this.ws.removeEventListener("close",this.onWebsocketClose),this.ws.removeEventListener("message",this.onWebsocketMessage),this.ws.close(),this.ws=null),this.delayedReconnectTimeout&&(clearTimeout(this.delayedReconnectTimeout),this.delayedReconnectTimeout=null)}},{key:"isDisconnected",value:function(){return null===this.ws}},{key:"onWebsocketOpen",value:(S=u(o().mark((function e(){var t,n,r;return o().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.session.create();case 2:return e.next=4,this.createPublisher();case 4:this.publisher=e.sent,this.connectSuccess(this.clientId),t=[],n=0;case 8:if(!(ne.maxReconnectionAttempts&&e.onReconnectionError)return e.onReconnectionError(new Error("Connection could not be reestablished, exceeded maximum number of reconnection attempts."));console.warn("Error during reconnect, retrying."),console.warn(t),e.onReconnecting&&e.onReconnecting(e.reconnectionDelay),e.reconnectionTimeout=setTimeout((function(){return e.reconnect()}),e.reconnectionDelay)}))}},{key:"performDelayedReconnect",value:function(){var e=this;this.delayedReconnectTimeout&&clearTimeout(this.delayedReconnectTimeout),this.delayedReconnectTimeout=setTimeout((function(){e.delayedReconnectTimeout=null,e.reconnect()}),1e4)}},{key:"onWebsocketMessage",value:function(e){this.session.receive(JSON.parse(e.data))}},{key:"addOccupant",value:(T=u(o().mark((function e(t){var n;return o().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return this.occupants[t]&&this.removeOccupant(t),this.leftOccupants.delete(t),e.next=4,this.createSubscriber(t);case 4:if(n=e.sent){e.next=7;break}return e.abrupt("return");case 7:return this.occupants[t]=n,this.setMediaStream(t,n.mediaStream),this.onOccupantConnected(t),this.onOccupantsChanged(this.occupants),e.abrupt("return",n);case 12:case"end":return e.stop()}}),e,this)}))),function(e){return T.apply(this,arguments)})},{key:"removeAllOccupants",value:function(){var e,t=i(Object.getOwnPropertyNames(this.occupants));try{for(t.s();!(e=t.n()).done;){var n=e.value;this.removeOccupant(n)}}catch(e){t.e(e)}finally{t.f()}}},{key:"removeOccupant",value:function(e){if(this.leftOccupants.add(e),this.occupants[e]&&(this.occupants[e].conn.close(),delete this.occupants[e]),this.mediaStreams[e]&&delete this.mediaStreams[e],this.pendingMediaRequests.has(e)){var t="The user disconnected before the media stream was resolved.";this.pendingMediaRequests.get(e).audio.reject(t),this.pendingMediaRequests.get(e).video.reject(t),this.pendingMediaRequests.delete(e)}this.onOccupantDisconnected(e),this.onOccupantsChanged(this.occupants)}},{key:"associate",value:function(e,t){var n=this;e.addEventListener("icecandidate",(function(e){t.sendTrickle(e.candidate||null).catch((function(e){return f("Error trickling ICE: %o",e)}))})),e.addEventListener("iceconnectionstatechange",(function(t){"connected"===e.iceConnectionState&&console.log("ICE state changed to connected"),"disconnected"===e.iceConnectionState&&console.warn("ICE state changed to disconnected"),"failed"===e.iceConnectionState&&(console.warn("ICE failure detected. Reconnecting in 10s."),n.performDelayedReconnect())})),e.addEventListener("negotiationneeded",g((function(r){h("Sending new offer for handle: %o",t);var i=e.createOffer().then(n.configurePublisherSdp).then(n.fixSafariIceUFrag),s=i.then((function(t){return e.setLocalDescription(t)})),a=i;return a=a.then(n.fixSafariIceUFrag).then((function(e){return t.sendJsep(e)})).then((function(t){return e.setRemoteDescription(t.jsep)})),Promise.all([s,a]).catch((function(e){return f("Error negotiating offer: %o",e)}))}))),t.on("event",g((function(r){var i=r.jsep;if(i&&"offer"==i.type){h("Accepting new offer for handle: %o",t);var s=e.setRemoteDescription(n.configureSubscriberSdp(i)).then((function(t){return e.createAnswer()})).then(n.fixSafariIceUFrag),a=s.then((function(t){return e.setLocalDescription(t)})),o=s.then((function(e){return t.sendJsep(e)}));return Promise.all([a,o]).catch((function(e){return f("Error negotiating answer: %o",e)}))}return null})))}},{key:"createPublisher",value:(x=u(o().mark((function e(){var t,n,r,i,s,a,c,u,l=this;return o().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=new p.JanusPluginHandle(this.session),n=new RTCPeerConnection(this.peerConnectionConfig||w),h("pub waiting for sfu"),e.next=5,t.attach("janus.plugin.sfu",this.loops&&this.clientId?parseInt(this.clientId)%this.loops:void 0);case 5:return this.associate(n,t),h("pub waiting for data channels & webrtcup"),r=new Promise((function(e){return t.on("webrtcup",e)})),i=n.createDataChannel("reliable",{ordered:!0}),s=n.createDataChannel("unreliable",{ordered:!1,maxRetransmits:0}),i.addEventListener("message",(function(e){return l.onDataChannelMessage(e,"janus-reliable")})),s.addEventListener("message",(function(e){return l.onDataChannelMessage(e,"janus-unreliable")})),e.next=14,r;case 14:return e.next=16,v(i);case 16:return e.next=18,v(s);case 18:return this.localMediaStream&&this.localMediaStream.getTracks().forEach((function(e){n.addTrack(e,l.localMediaStream)})),t.on("event",(function(e){var t=e.plugindata.data;if("join"==t.event&&t.room_id==l.room){if(l.delayedReconnectTimeout)return;l.addOccupant(t.user_id)}else"leave"==t.event&&t.room_id==l.room?l.removeOccupant(t.user_id):"blocked"==t.event?document.body.dispatchEvent(new CustomEvent("blocked",{detail:{clientId:t.by}})):"unblocked"==t.event?document.body.dispatchEvent(new CustomEvent("unblocked",{detail:{clientId:t.by}})):"data"===t.event&&l.onData(JSON.parse(t.body),"janus-event")})),h("pub waiting for join"),e.next=23,this.sendJoin(t,{notifications:!0,data:!0});case 23:if((a=e.sent).plugindata.data.success){e.next=29;break}throw c=a.plugindata.data.error,console.error(c),n.close(),c;case 29:return(u=a.plugindata.data.response.users[this.room]||[]).includes(this.clientId)&&(console.warn("Janus still has previous session for this client. Reconnecting in 10s."),this.performDelayedReconnect()),h("publisher ready"),e.abrupt("return",{handle:t,initialOccupants:u,reliableChannel:i,unreliableChannel:s,conn:n});case 33:case"end":return e.stop()}}),e,this)}))),function(){return x.apply(this,arguments)})},{key:"configurePublisherSdp",value:function(e){return e.sdp=e.sdp.replace(/a=fmtp:(109|111).*\r\n/g,(function(e,t){var n=Object.assign(d.parseFmtp(e),b);return d.writeFmtp({payloadType:t,parameters:n})})),e}},{key:"configureSubscriberSdp",value:function(e){return y||-1!==navigator.userAgent.indexOf("HeadlessChrome")&&(e.sdp=e.sdp.replace(/m=video[^]*m=/,"m=")),-1===navigator.userAgent.indexOf("Android")?e.sdp=e.sdp.replace("a=rtcp-fb:107 goog-remb\r\n","a=rtcp-fb:107 goog-remb\r\na=rtcp-fb:107 transport-cc\r\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\n"):e.sdp=e.sdp.replace("a=rtcp-fb:107 goog-remb\r\n","a=rtcp-fb:107 goog-remb\r\na=rtcp-fb:107 transport-cc\r\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f\r\n"),e}},{key:"fixSafariIceUFrag",value:(k=u(o().mark((function e(t){return o().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t.sdp=t.sdp.replace(/[^\r]\na=ice-ufrag/g,"\r\na=ice-ufrag"),e.abrupt("return",t);case 2:case"end":return e.stop()}}),e)}))),function(e){return k.apply(this,arguments)})},{key:"createSubscriber",value:(c=u(o().mark((function e(t){var n,r,i,s,a,c,u=this,l=arguments;return o().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n=l.length>1&&void 0!==l[1]?l[1]:5,!this.leftOccupants.has(t)){e.next=4;break}return console.warn(t+": cancelled occupant connection, occupant left before subscription negotation."),e.abrupt("return",null);case 4:return r=new p.JanusPluginHandle(this.session),i=new RTCPeerConnection(this.peerConnectionConfig||w),h(t+": sub waiting for sfu"),e.next=9,r.attach("janus.plugin.sfu",this.loops?parseInt(t)%this.loops:void 0);case 9:if(this.associate(i,r),h(t+": sub waiting for join"),!this.leftOccupants.has(t)){e.next=15;break}return i.close(),console.warn(t+": cancelled occupant connection, occupant left after attach"),e.abrupt("return",null);case 15:return s=!1,a=new Promise((function(e){var n=setInterval((function(){u.leftOccupants.has(t)&&(clearInterval(n),e())}),1e3),i=setTimeout((function(){clearInterval(n),s=!0,e()}),15e3);r.on("webrtcup",(function(){clearTimeout(i),clearInterval(n),e()}))})),e.next=19,this.sendJoin(r,{media:t});case 19:if(!this.leftOccupants.has(t)){e.next=23;break}return i.close(),console.warn(t+": cancelled occupant connection, occupant left after join"),e.abrupt("return",null);case 23:return h(t+": sub waiting for webrtcup"),e.next=26,a;case 26:if(!this.leftOccupants.has(t)){e.next=30;break}return i.close(),console.warn(t+": cancel occupant connection, occupant left during or after webrtcup"),e.abrupt("return",null);case 30:if(!s){e.next=39;break}if(i.close(),!(n>0)){e.next=37;break}return console.warn(t+": webrtc up timed out, retrying"),e.abrupt("return",this.createSubscriber(t,n-1));case 37:return console.warn(t+": webrtc up timed out"),e.abrupt("return",null);case 39:if(!m||this._iOSHackDelayedInitialPeer){e.next=43;break}return e.next=42,new Promise((function(e){return setTimeout(e,3e3)}));case 42:this._iOSHackDelayedInitialPeer=!0;case 43:return c=new MediaStream,i.getReceivers().forEach((function(e){e.track&&c.addTrack(e.track)})),0===c.getTracks().length&&(c=null),h(t+": subscriber ready"),e.abrupt("return",{handle:r,mediaStream:c,conn:i});case 49:case"end":return e.stop()}}),e,this)}))),function(e){return c.apply(this,arguments)})},{key:"sendJoin",value:function(e,t){return e.sendMessage({kind:"join",room_id:this.room,user_id:this.clientId,subscribe:t,token:this.joinToken})}},{key:"toggleFreeze",value:function(){this.frozen?this.unfreeze():this.freeze()}},{key:"freeze",value:function(){this.frozen=!0}},{key:"unfreeze",value:function(){this.frozen=!1,this.flushPendingUpdates()}},{key:"dataForUpdateMultiMessage",value:function(e,t){for(var n=0,r=t.data.d.length;nn.owner)return;"r"===r?a&&a.isFirstSync?this.frozenUpdates.delete(i):this.frozenUpdates.set(i,e):a.components&&n.components&&Object.assign(a.components,n.components)}else this.frozenUpdates.set(i,e)}},{key:"onDataChannelMessage",value:function(e,t){this.onData(JSON.parse(e.data),t)}},{key:"onData",value:function(e,t){h.enabled&&h("DC in: ".concat(e)),e.dataType&&(e.source=t,this.frozen?this.storeMessage(e):this.onOccupantMessage(null,e.dataType,e.data,e.source))}},{key:"shouldStartConnectionTo",value:function(e){return!0}},{key:"startStreamConnection",value:function(e){}},{key:"closeStreamConnection",value:function(e){}},{key:"getConnectStatus",value:function(e){return this.occupants[e]?NAF.adapters.IS_CONNECTED:NAF.adapters.NOT_CONNECTED}},{key:"updateTimeOffset",value:(a=u(o().mark((function e(){var t,n,r,i,s,a=this;return o().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!this.isDisconnected()){e.next=2;break}return e.abrupt("return");case 2:return t=Date.now(),e.next=5,fetch(document.location.href,{method:"HEAD",cache:"no-cache"});case 5:n=e.sent,r=new Date(n.headers.get("Date")).getTime()+500,i=Date.now(),s=r+(i-t)/2-i,this.serverTimeRequests++,this.serverTimeRequests<=10?this.timeOffsets.push(s):this.timeOffsets[this.serverTimeRequests%10]=s,this.avgTimeOffset=this.timeOffsets.reduce((function(e,t){return e+t}),0)/this.timeOffsets.length,this.serverTimeRequests>10?(h("new server time offset: ".concat(this.avgTimeOffset,"ms")),setTimeout((function(){return a.updateTimeOffset()}),3e5)):this.updateTimeOffset();case 15:case"end":return e.stop()}}),e,this)}))),function(){return a.apply(this,arguments)})},{key:"getServerTime",value:function(){return Date.now()+this.avgTimeOffset}},{key:"getMediaStream",value:function(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"audio";if(this.mediaStreams[e])return h("Already had ".concat(n," for ").concat(e)),Promise.resolve(this.mediaStreams[e][n]);if(h("Waiting on ".concat(n," for ").concat(e)),!this.pendingMediaRequests.has(e)){this.pendingMediaRequests.set(e,{});var r=new Promise((function(n,r){t.pendingMediaRequests.get(e).audio={resolve:n,reject:r}})),i=new Promise((function(n,r){t.pendingMediaRequests.get(e).video={resolve:n,reject:r}}));this.pendingMediaRequests.get(e).audio.promise=r,this.pendingMediaRequests.get(e).video.promise=i,r.catch((function(t){return console.warn("".concat(e," getMediaStream Audio Error"),t)})),i.catch((function(t){return console.warn("".concat(e," getMediaStream Video Error"),t)}))}return this.pendingMediaRequests.get(e)[n].promise}},{key:"setMediaStream",value:function(e,t){var n=new MediaStream;try{t.getAudioTracks().forEach((function(e){return n.addTrack(e)}))}catch(t){console.warn("".concat(e," setMediaStream Audio Error"),t)}var r=new MediaStream;try{t.getVideoTracks().forEach((function(e){return r.addTrack(e)}))}catch(t){console.warn("".concat(e," setMediaStream Video Error"),t)}this.mediaStreams[e]={audio:n,video:r},this.pendingMediaRequests.has(e)&&(this.pendingMediaRequests.get(e).audio.resolve(n),this.pendingMediaRequests.get(e).video.resolve(r))}},{key:"setLocalMediaStream",value:(r=u(o().mark((function e(t){var n,r,i,s,a,c=this;return o().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!this.publisher||!this.publisher.conn){e.next=12;break}n=this.publisher.conn.getSenders(),r=[],i=t.getTracks(),s=o().mark((function e(){var s,u;return o().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(s=i[a],null==(u=n.find((function(e){return null!=e.track&&e.track.kind==s.kind})))){e.next=14;break}if(!u.replaceTrack){e.next=9;break}return e.next=6,u.replaceTrack(s);case 6:"video"===s.kind&&s.enabled&&navigator.userAgent.toLowerCase().indexOf("firefox")>-1&&(s.enabled=!1,setTimeout((function(){return s.enabled=!0}),1e3)),e.next=11;break;case 9:t.removeTrack(u.track),t.addTrack(s);case 11:r.push(u),e.next=15;break;case 14:r.push(c.publisher.conn.addTrack(s,t));case 15:case"end":return e.stop()}}),e)})),a=0;case 6:if(!(a{"use strict";const t={generateIdentifier:function(){return Math.random().toString(36).substring(2,12)}};t.localCName=t.generateIdentifier(),t.splitLines=function(e){return e.trim().split("\n").map((e=>e.trim()))},t.splitSections=function(e){return e.split("\nm=").map(((e,t)=>(t>0?"m="+e:e).trim()+"\r\n"))},t.getDescription=function(e){const n=t.splitSections(e);return n&&n[0]},t.getMediaSections=function(e){const n=t.splitSections(e);return n.shift(),n},t.matchPrefix=function(e,n){return t.splitLines(e).filter((e=>0===e.indexOf(n)))},t.parseCandidate=function(e){let t;t=0===e.indexOf("a=candidate:")?e.substring(12).split(" "):e.substring(10).split(" ");const n={foundation:t[0],component:{1:"rtp",2:"rtcp"}[t[1]]||t[1],protocol:t[2].toLowerCase(),priority:parseInt(t[3],10),ip:t[4],address:t[4],port:parseInt(t[5],10),type:t[7]};for(let e=8;e0?t[0].split("/")[1]:"sendrecv",uri:t[1],attributes:t.slice(2).join(" ")}},t.writeExtmap=function(e){return"a=extmap:"+(e.id||e.preferredId)+(e.direction&&"sendrecv"!==e.direction?"/"+e.direction:"")+" "+e.uri+(e.attributes?" "+e.attributes:"")+"\r\n"},t.parseFmtp=function(e){const t={};let n;const r=e.substring(e.indexOf(" ")+1).split(";");for(let e=0;e{void 0!==e.parameters[t]?r.push(t+"="+e.parameters[t]):r.push(t)})),t+="a=fmtp:"+n+" "+r.join(";")+"\r\n"}return t},t.parseRtcpFb=function(e){const t=e.substring(e.indexOf(" ")+1).split(" ");return{type:t.shift(),parameter:t.join(" ")}},t.writeRtcpFb=function(e){let t="",n=e.payloadType;return void 0!==e.preferredPayloadType&&(n=e.preferredPayloadType),e.rtcpFeedback&&e.rtcpFeedback.length&&e.rtcpFeedback.forEach((e=>{t+="a=rtcp-fb:"+n+" "+e.type+(e.parameter&&e.parameter.length?" "+e.parameter:"")+"\r\n"})),t},t.parseSsrcMedia=function(e){const t=e.indexOf(" "),n={ssrc:parseInt(e.substring(7,t),10)},r=e.indexOf(":",t);return r>-1?(n.attribute=e.substring(t+1,r),n.value=e.substring(r+1)):n.attribute=e.substring(t+1),n},t.parseSsrcGroup=function(e){const t=e.substring(13).split(" ");return{semantics:t.shift(),ssrcs:t.map((e=>parseInt(e,10)))}},t.getMid=function(e){const n=t.matchPrefix(e,"a=mid:")[0];if(n)return n.substring(6)},t.parseFingerprint=function(e){const t=e.substring(14).split(" ");return{algorithm:t[0].toLowerCase(),value:t[1].toUpperCase()}},t.getDtlsParameters=function(e,n){return{role:"auto",fingerprints:t.matchPrefix(e+n,"a=fingerprint:").map(t.parseFingerprint)}},t.writeDtlsParameters=function(e,t){let n="a=setup:"+t+"\r\n";return e.fingerprints.forEach((e=>{n+="a=fingerprint:"+e.algorithm+" "+e.value+"\r\n"})),n},t.parseCryptoLine=function(e){const t=e.substring(9).split(" ");return{tag:parseInt(t[0],10),cryptoSuite:t[1],keyParams:t[2],sessionParams:t.slice(3)}},t.writeCryptoLine=function(e){return"a=crypto:"+e.tag+" "+e.cryptoSuite+" "+("object"==typeof e.keyParams?t.writeCryptoKeyParams(e.keyParams):e.keyParams)+(e.sessionParams?" "+e.sessionParams.join(" "):"")+"\r\n"},t.parseCryptoKeyParams=function(e){if(0!==e.indexOf("inline:"))return null;const t=e.substring(7).split("|");return{keyMethod:"inline",keySalt:t[0],lifeTime:t[1],mkiValue:t[2]?t[2].split(":")[0]:void 0,mkiLength:t[2]?t[2].split(":")[1]:void 0}},t.writeCryptoKeyParams=function(e){return e.keyMethod+":"+e.keySalt+(e.lifeTime?"|"+e.lifeTime:"")+(e.mkiValue&&e.mkiLength?"|"+e.mkiValue+":"+e.mkiLength:"")},t.getCryptoParameters=function(e,n){return t.matchPrefix(e+n,"a=crypto:").map(t.parseCryptoLine)},t.getIceParameters=function(e,n){const r=t.matchPrefix(e+n,"a=ice-ufrag:")[0],i=t.matchPrefix(e+n,"a=ice-pwd:")[0];return r&&i?{usernameFragment:r.substring(12),password:i.substring(10)}:null},t.writeIceParameters=function(e){let t="a=ice-ufrag:"+e.usernameFragment+"\r\na=ice-pwd:"+e.password+"\r\n";return e.iceLite&&(t+="a=ice-lite\r\n"),t},t.parseRtpParameters=function(e){const n={codecs:[],headerExtensions:[],fecMechanisms:[],rtcp:[]},r=t.splitLines(e)[0].split(" ");n.profile=r[2];for(let i=3;i{n.headerExtensions.push(t.parseExtmap(e))}));const i=t.matchPrefix(e,"a=rtcp-fb:* ").map(t.parseRtcpFb);return n.codecs.forEach((e=>{i.forEach((t=>{e.rtcpFeedback.find((e=>e.type===t.type&&e.parameter===t.parameter))||e.rtcpFeedback.push(t)}))})),n},t.writeRtpDescription=function(e,n){let r="";r+="m="+e+" ",r+=n.codecs.length>0?"9":"0",r+=" "+(n.profile||"UDP/TLS/RTP/SAVPF")+" ",r+=n.codecs.map((e=>void 0!==e.preferredPayloadType?e.preferredPayloadType:e.payloadType)).join(" ")+"\r\n",r+="c=IN IP4 0.0.0.0\r\n",r+="a=rtcp:9 IN IP4 0.0.0.0\r\n",n.codecs.forEach((e=>{r+=t.writeRtpMap(e),r+=t.writeFmtp(e),r+=t.writeRtcpFb(e)}));let i=0;return n.codecs.forEach((e=>{e.maxptime>i&&(i=e.maxptime)})),i>0&&(r+="a=maxptime:"+i+"\r\n"),n.headerExtensions&&n.headerExtensions.forEach((e=>{r+=t.writeExtmap(e)})),r},t.parseRtpEncodingParameters=function(e){const n=[],r=t.parseRtpParameters(e),i=-1!==r.fecMechanisms.indexOf("RED"),s=-1!==r.fecMechanisms.indexOf("ULPFEC"),a=t.matchPrefix(e,"a=ssrc:").map((e=>t.parseSsrcMedia(e))).filter((e=>"cname"===e.attribute)),o=a.length>0&&a[0].ssrc;let c;const u=t.matchPrefix(e,"a=ssrc-group:FID").map((e=>e.substring(17).split(" ").map((e=>parseInt(e,10)))));u.length>0&&u[0].length>1&&u[0][0]===o&&(c=u[0][1]),r.codecs.forEach((e=>{if("RTX"===e.name.toUpperCase()&&e.parameters.apt){let t={ssrc:o,codecPayloadType:parseInt(e.parameters.apt,10)};o&&c&&(t.rtx={ssrc:c}),n.push(t),i&&(t=JSON.parse(JSON.stringify(t)),t.fec={ssrc:o,mechanism:s?"red+ulpfec":"red"},n.push(t))}})),0===n.length&&o&&n.push({ssrc:o});let l=t.matchPrefix(e,"b=");return l.length&&(l=0===l[0].indexOf("b=TIAS:")?parseInt(l[0].substring(7),10):0===l[0].indexOf("b=AS:")?1e3*parseInt(l[0].substring(5),10)*.95-16e3:void 0,n.forEach((e=>{e.maxBitrate=l}))),n},t.parseRtcpParameters=function(e){const n={},r=t.matchPrefix(e,"a=ssrc:").map((e=>t.parseSsrcMedia(e))).filter((e=>"cname"===e.attribute))[0];r&&(n.cname=r.value,n.ssrc=r.ssrc);const i=t.matchPrefix(e,"a=rtcp-rsize");n.reducedSize=i.length>0,n.compound=0===i.length;const s=t.matchPrefix(e,"a=rtcp-mux");return n.mux=s.length>0,n},t.writeRtcpParameters=function(e){let t="";return e.reducedSize&&(t+="a=rtcp-rsize\r\n"),e.mux&&(t+="a=rtcp-mux\r\n"),void 0!==e.ssrc&&e.cname&&(t+="a=ssrc:"+e.ssrc+" cname:"+e.cname+"\r\n"),t},t.parseMsid=function(e){let n;const r=t.matchPrefix(e,"a=msid:");if(1===r.length)return n=r[0].substring(7).split(" "),{stream:n[0],track:n[1]};const i=t.matchPrefix(e,"a=ssrc:").map((e=>t.parseSsrcMedia(e))).filter((e=>"msid"===e.attribute));return i.length>0?(n=i[0].value.split(" "),{stream:n[0],track:n[1]}):void 0},t.parseSctpDescription=function(e){const n=t.parseMLine(e),r=t.matchPrefix(e,"a=max-message-size:");let i;r.length>0&&(i=parseInt(r[0].substring(19),10)),isNaN(i)&&(i=65536);const s=t.matchPrefix(e,"a=sctp-port:");if(s.length>0)return{port:parseInt(s[0].substring(12),10),protocol:n.fmt,maxMessageSize:i};const a=t.matchPrefix(e,"a=sctpmap:");if(a.length>0){const e=a[0].substring(10).split(" ");return{port:parseInt(e[0],10),protocol:e[1],maxMessageSize:i}}},t.writeSctpDescription=function(e,t){let n=[];return n="DTLS/SCTP"!==e.protocol?["m="+e.kind+" 9 "+e.protocol+" "+t.protocol+"\r\n","c=IN IP4 0.0.0.0\r\n","a=sctp-port:"+t.port+"\r\n"]:["m="+e.kind+" 9 "+e.protocol+" "+t.port+"\r\n","c=IN IP4 0.0.0.0\r\n","a=sctpmap:"+t.port+" "+t.protocol+" 65535\r\n"],void 0!==t.maxMessageSize&&n.push("a=max-message-size:"+t.maxMessageSize+"\r\n"),n.join("")},t.generateSessionId=function(){return Math.random().toString().substr(2,22)},t.writeSessionBoilerplate=function(e,n,r){let i;const s=void 0!==n?n:2;return i=e||t.generateSessionId(),"v=0\r\no="+(r||"thisisadapterortc")+" "+i+" "+s+" IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\n"},t.getDirection=function(e,n){const r=t.splitLines(e);for(let e=0;e {\n if (e.message && e.message.indexOf(\"timed out\") > -1) {\n console.error(\"web socket timed out\");\n NAF.connection.adapter.reconnect();\n } else {\n throw(e);\n }\n });\n}\n\nvar sdpUtils = require(\"sdp\");\n//var debug = require(\"debug\")(\"naf-janus-adapter:debug\");\n//var warn = require(\"debug\")(\"naf-janus-adapter:warn\");\n//var error = require(\"debug\")(\"naf-janus-adapter:error\");\nvar debug = console.log;\nvar warn = console.warn;\nvar error = console.error;\nvar isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);\n\nconst SUBSCRIBE_TIMEOUT_MS = 15000;\n\nfunction debounce(fn) {\n var curr = Promise.resolve();\n return function() {\n var args = Array.prototype.slice.call(arguments);\n curr = curr.then(_ => fn.apply(this, args));\n };\n}\n\nfunction randomUint() {\n return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);\n}\n\nfunction untilDataChannelOpen(dataChannel) {\n return new Promise((resolve, reject) => {\n if (dataChannel.readyState === \"open\") {\n resolve();\n } else {\n let resolver, rejector;\n\n const clear = () => {\n dataChannel.removeEventListener(\"open\", resolver);\n dataChannel.removeEventListener(\"error\", rejector);\n };\n\n resolver = () => {\n clear();\n resolve();\n };\n rejector = () => {\n clear();\n reject();\n };\n\n dataChannel.addEventListener(\"open\", resolver);\n dataChannel.addEventListener(\"error\", rejector);\n }\n });\n}\n\nconst isH264VideoSupported = (() => {\n const video = document.createElement(\"video\");\n return video.canPlayType('video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"') !== \"\";\n})();\n\nconst OPUS_PARAMETERS = {\n // indicates that we want to enable DTX to elide silence packets\n usedtx: 1,\n // indicates that we prefer to receive mono audio (important for voip profile)\n stereo: 0,\n // indicates that we prefer to send mono audio (important for voip profile)\n \"sprop-stereo\": 0\n};\n\nconst DEFAULT_PEER_CONNECTION_CONFIG = {\n iceServers: [{ urls: \"stun:stun1.l.google.com:19302\" }, { urls: \"stun:stun2.l.google.com:19302\" }]\n};\n\nconst WS_NORMAL_CLOSURE = 1000;\n\nclass JanusAdapter {\n constructor() {\n this.room = null;\n // We expect the consumer to set a client id before connecting.\n this.clientId = null;\n this.joinToken = null;\n\n this.serverUrl = null;\n this.webRtcOptions = {};\n this.peerConnectionConfig = null;\n this.ws = null;\n this.session = null;\n this.reliableTransport = \"datachannel\";\n this.unreliableTransport = \"datachannel\";\n\n // In the event the server restarts and all clients lose connection, reconnect with\n // some random jitter added to prevent simultaneous reconnection requests.\n this.initialReconnectionDelay = 1000 * Math.random();\n this.reconnectionDelay = this.initialReconnectionDelay;\n this.reconnectionTimeout = null;\n this.maxReconnectionAttempts = 10;\n this.reconnectionAttempts = 0;\n\n this.publisher = null;\n this.occupants = {};\n this.leftOccupants = new Set();\n this.mediaStreams = {};\n this.localMediaStream = null;\n this.pendingMediaRequests = new Map();\n\n this.blockedClients = new Map();\n this.frozenUpdates = new Map();\n\n this.timeOffsets = [];\n this.serverTimeRequests = 0;\n this.avgTimeOffset = 0;\n\n this.onWebsocketOpen = this.onWebsocketOpen.bind(this);\n this.onWebsocketClose = this.onWebsocketClose.bind(this);\n this.onWebsocketMessage = this.onWebsocketMessage.bind(this);\n this.onDataChannelMessage = this.onDataChannelMessage.bind(this);\n this.onData = this.onData.bind(this);\n }\n\n setServerUrl(url) {\n this.serverUrl = url;\n }\n\n setApp(app) {}\n\n setRoom(roomName) {\n this.room = roomName;\n }\n\n setJoinToken(joinToken) {\n this.joinToken = joinToken;\n }\n\n setClientId(clientId) {\n this.clientId = clientId;\n }\n\n setWebRtcOptions(options) {\n this.webRtcOptions = options;\n }\n\n setPeerConnectionConfig(peerConnectionConfig) {\n this.peerConnectionConfig = peerConnectionConfig;\n }\n\n setServerConnectListeners(successListener, failureListener) {\n this.connectSuccess = successListener;\n this.connectFailure = failureListener;\n }\n\n setRoomOccupantListener(occupantListener) {\n this.onOccupantsChanged = occupantListener;\n }\n\n setDataChannelListeners(openListener, closedListener, messageListener) {\n this.onOccupantConnected = openListener;\n this.onOccupantDisconnected = closedListener;\n this.onOccupantMessage = messageListener;\n }\n\n setReconnectionListeners(reconnectingListener, reconnectedListener, reconnectionErrorListener) {\n // onReconnecting is called with the number of milliseconds until the next reconnection attempt\n this.onReconnecting = reconnectingListener;\n // onReconnected is called when the connection has been reestablished\n this.onReconnected = reconnectedListener;\n // onReconnectionError is called with an error when maxReconnectionAttempts has been reached\n this.onReconnectionError = reconnectionErrorListener;\n }\n\n connect() {\n debug(`connecting to ${this.serverUrl}`);\n\n const websocketConnection = new Promise((resolve, reject) => {\n this.ws = new WebSocket(this.serverUrl, \"janus-protocol\");\n\n this.session = new mj.JanusSession(this.ws.send.bind(this.ws), { timeoutMs: 40000 });\n\n this.ws.addEventListener(\"close\", this.onWebsocketClose);\n this.ws.addEventListener(\"message\", this.onWebsocketMessage);\n\n this.wsOnOpen = () => {\n this.ws.removeEventListener(\"open\", this.wsOnOpen);\n this.onWebsocketOpen()\n .then(resolve)\n .catch(reject);\n };\n\n this.ws.addEventListener(\"open\", this.wsOnOpen);\n });\n\n return Promise.all([websocketConnection, this.updateTimeOffset()]);\n }\n\n disconnect() {\n debug(`disconnecting`);\n\n clearTimeout(this.reconnectionTimeout);\n\n this.removeAllOccupants();\n this.leftOccupants = new Set();\n\n if (this.publisher) {\n // Close the publisher peer connection. Which also detaches the plugin handle.\n this.publisher.conn.close();\n this.publisher = null;\n }\n\n if (this.session) {\n this.session.dispose();\n this.session = null;\n }\n\n if (this.ws) {\n this.ws.removeEventListener(\"open\", this.wsOnOpen);\n this.ws.removeEventListener(\"close\", this.onWebsocketClose);\n this.ws.removeEventListener(\"message\", this.onWebsocketMessage);\n this.ws.close();\n this.ws = null;\n }\n\n // Now that all RTCPeerConnection closed, be sure to not call\n // reconnect() again via performDelayedReconnect if previous\n // RTCPeerConnection was in the failed state.\n if (this.delayedReconnectTimeout) {\n clearTimeout(this.delayedReconnectTimeout);\n this.delayedReconnectTimeout = null;\n }\n }\n\n isDisconnected() {\n return this.ws === null;\n }\n\n async onWebsocketOpen() {\n // Create the Janus Session\n await this.session.create();\n\n // Attach the SFU Plugin and create a RTCPeerConnection for the publisher.\n // The publisher sends audio and opens two bidirectional data channels.\n // One reliable datachannel and one unreliable.\n this.publisher = await this.createPublisher();\n\n // Call the naf connectSuccess callback before we start receiving WebRTC messages.\n this.connectSuccess(this.clientId);\n\n const addOccupantPromises = [];\n\n for (let i = 0; i < this.publisher.initialOccupants.length; i++) {\n const occupantId = this.publisher.initialOccupants[i];\n if (occupantId === this.clientId) continue; // Happens during non-graceful reconnects due to zombie sessions\n addOccupantPromises.push(this.addOccupant(occupantId));\n }\n\n await Promise.all(addOccupantPromises);\n }\n\n onWebsocketClose(event) {\n // The connection was closed successfully. Don't try to reconnect.\n if (event.code === WS_NORMAL_CLOSURE) {\n return;\n }\n\n console.warn(\"Janus websocket closed unexpectedly.\");\n if (this.onReconnecting) {\n this.onReconnecting(this.reconnectionDelay);\n }\n\n this.reconnectionTimeout = setTimeout(() => this.reconnect(), this.reconnectionDelay);\n }\n\n reconnect() {\n // Dispose of all networked entities and other resources tied to the session.\n this.disconnect();\n\n this.connect()\n .then(() => {\n this.reconnectionDelay = this.initialReconnectionDelay;\n this.reconnectionAttempts = 0;\n\n if (this.onReconnected) {\n this.onReconnected();\n }\n })\n .catch(error => {\n this.reconnectionDelay += 1000;\n this.reconnectionAttempts++;\n\n if (this.reconnectionAttempts > this.maxReconnectionAttempts && this.onReconnectionError) {\n return this.onReconnectionError(\n new Error(\"Connection could not be reestablished, exceeded maximum number of reconnection attempts.\")\n );\n }\n\n console.warn(\"Error during reconnect, retrying.\");\n console.warn(error);\n\n if (this.onReconnecting) {\n this.onReconnecting(this.reconnectionDelay);\n }\n\n this.reconnectionTimeout = setTimeout(() => this.reconnect(), this.reconnectionDelay);\n });\n }\n\n performDelayedReconnect() {\n if (this.delayedReconnectTimeout) {\n clearTimeout(this.delayedReconnectTimeout);\n }\n\n this.delayedReconnectTimeout = setTimeout(() => {\n this.delayedReconnectTimeout = null;\n this.reconnect();\n }, 10000);\n }\n\n onWebsocketMessage(event) {\n this.session.receive(JSON.parse(event.data));\n }\n\n async addOccupant(occupantId) {\n if (this.occupants[occupantId]) {\n this.removeOccupant(occupantId);\n }\n\n this.leftOccupants.delete(occupantId);\n\n var subscriber = await this.createSubscriber(occupantId);\n\n if (!subscriber) return;\n\n this.occupants[occupantId] = subscriber;\n\n this.setMediaStream(occupantId, subscriber.mediaStream);\n\n // Call the Networked AFrame callbacks for the new occupant.\n this.onOccupantConnected(occupantId);\n this.onOccupantsChanged(this.occupants);\n\n return subscriber;\n }\n\n removeAllOccupants() {\n for (const occupantId of Object.getOwnPropertyNames(this.occupants)) {\n this.removeOccupant(occupantId);\n }\n }\n\n removeOccupant(occupantId) {\n this.leftOccupants.add(occupantId);\n\n if (this.occupants[occupantId]) {\n // Close the subscriber peer connection. Which also detaches the plugin handle.\n this.occupants[occupantId].conn.close();\n delete this.occupants[occupantId];\n }\n\n if (this.mediaStreams[occupantId]) {\n delete this.mediaStreams[occupantId];\n }\n\n if (this.pendingMediaRequests.has(occupantId)) {\n const msg = \"The user disconnected before the media stream was resolved.\";\n this.pendingMediaRequests.get(occupantId).audio.reject(msg);\n this.pendingMediaRequests.get(occupantId).video.reject(msg);\n this.pendingMediaRequests.delete(occupantId);\n }\n\n // Call the Networked AFrame callbacks for the removed occupant.\n this.onOccupantDisconnected(occupantId);\n this.onOccupantsChanged(this.occupants);\n }\n\n associate(conn, handle) {\n conn.addEventListener(\"icecandidate\", ev => {\n handle.sendTrickle(ev.candidate || null).catch(e => error(\"Error trickling ICE: %o\", e));\n });\n conn.addEventListener(\"iceconnectionstatechange\", ev => {\n if (conn.iceConnectionState === \"connected\") {\n console.log(\"ICE state changed to connected\");\n }\n if (conn.iceConnectionState === \"disconnected\") {\n console.warn(\"ICE state changed to disconnected\");\n }\n if (conn.iceConnectionState === \"failed\") {\n console.warn(\"ICE failure detected. Reconnecting in 10s.\");\n this.performDelayedReconnect();\n }\n })\n\n // we have to debounce these because janus gets angry if you send it a new SDP before\n // it's finished processing an existing SDP. in actuality, it seems like this is maybe\n // too liberal and we need to wait some amount of time after an offer before sending another,\n // but we don't currently know any good way of detecting exactly how long :(\n conn.addEventListener(\n \"negotiationneeded\",\n debounce(ev => {\n debug(\"Sending new offer for handle: %o\", handle);\n var offer = conn.createOffer().then(this.configurePublisherSdp).then(this.fixSafariIceUFrag);\n var local = offer.then(o => conn.setLocalDescription(o));\n var remote = offer;\n\n remote = remote\n .then(this.fixSafariIceUFrag)\n .then(j => handle.sendJsep(j))\n .then(r => conn.setRemoteDescription(r.jsep));\n return Promise.all([local, remote]).catch(e => error(\"Error negotiating offer: %o\", e));\n })\n );\n handle.on(\n \"event\",\n debounce(ev => {\n var jsep = ev.jsep;\n if (jsep && jsep.type == \"offer\") {\n debug(\"Accepting new offer for handle: %o\", handle);\n var answer = conn\n .setRemoteDescription(this.configureSubscriberSdp(jsep))\n .then(_ => conn.createAnswer())\n .then(this.fixSafariIceUFrag);\n var local = answer.then(a => conn.setLocalDescription(a));\n var remote = answer.then(j => handle.sendJsep(j));\n return Promise.all([local, remote]).catch(e => error(\"Error negotiating answer: %o\", e));\n } else {\n // some other kind of event, nothing to do\n return null;\n }\n })\n );\n }\n\n async createPublisher() {\n var handle = new mj.JanusPluginHandle(this.session);\n var conn = new RTCPeerConnection(this.peerConnectionConfig || DEFAULT_PEER_CONNECTION_CONFIG);\n\n debug(\"pub waiting for sfu\");\n await handle.attach(\"janus.plugin.sfu\");\n\n this.associate(conn, handle);\n\n debug(\"pub waiting for data channels & webrtcup\");\n var webrtcup = new Promise(resolve => handle.on(\"webrtcup\", resolve));\n\n // Unreliable datachannel: sending and receiving component updates.\n // Reliable datachannel: sending and recieving entity instantiations.\n var reliableChannel = conn.createDataChannel(\"reliable\", { ordered: true });\n var unreliableChannel = conn.createDataChannel(\"unreliable\", {\n ordered: false,\n maxRetransmits: 0\n });\n\n reliableChannel.addEventListener(\"message\", e => this.onDataChannelMessage(e, \"janus-reliable\"));\n unreliableChannel.addEventListener(\"message\", e => this.onDataChannelMessage(e, \"janus-unreliable\"));\n\n await webrtcup;\n await untilDataChannelOpen(reliableChannel);\n await untilDataChannelOpen(unreliableChannel);\n\n // doing this here is sort of a hack around chrome renegotiation weirdness --\n // if we do it prior to webrtcup, chrome on gear VR will sometimes put a\n // renegotiation offer in flight while the first offer was still being\n // processed by janus. we should find some more principled way to figure out\n // when janus is done in the future.\n if (this.localMediaStream) {\n this.localMediaStream.getTracks().forEach(track => {\n conn.addTrack(track, this.localMediaStream);\n });\n }\n\n // Handle all of the join and leave events.\n handle.on(\"event\", ev => {\n var data = ev.plugindata.data;\n if (data.event == \"join\" && data.room_id == this.room) {\n if (this.delayedReconnectTimeout) {\n // Don't create a new RTCPeerConnection, all RTCPeerConnection will be closed in less than 10s.\n return;\n }\n this.addOccupant(data.user_id);\n } else if (data.event == \"leave\" && data.room_id == this.room) {\n this.removeOccupant(data.user_id);\n } else if (data.event == \"blocked\") {\n document.body.dispatchEvent(new CustomEvent(\"blocked\", { detail: { clientId: data.by } }));\n } else if (data.event == \"unblocked\") {\n document.body.dispatchEvent(new CustomEvent(\"unblocked\", { detail: { clientId: data.by } }));\n } else if (data.event === \"data\") {\n this.onData(JSON.parse(data.body), \"janus-event\");\n }\n });\n\n debug(\"pub waiting for join\");\n\n // Send join message to janus. Listen for join/leave messages. Automatically subscribe to all users' WebRTC data.\n var message = await this.sendJoin(handle, {\n notifications: true,\n data: true\n });\n\n if (!message.plugindata.data.success) {\n const err = message.plugindata.data.error;\n console.error(err);\n // We may get here because of an expired JWT.\n // Close the connection ourself otherwise janus will close it after\n // session_timeout because we didn't send any keepalive and this will\n // trigger a delayed reconnect because of the iceconnectionstatechange\n // listener for failure state.\n // Even if the app code calls disconnect in case of error, disconnect\n // won't close the peer connection because this.publisher is not set.\n conn.close();\n throw err;\n }\n\n var initialOccupants = message.plugindata.data.response.users[this.room] || [];\n\n if (initialOccupants.includes(this.clientId)) {\n console.warn(\"Janus still has previous session for this client. Reconnecting in 10s.\");\n this.performDelayedReconnect();\n }\n\n debug(\"publisher ready\");\n return {\n handle,\n initialOccupants,\n reliableChannel,\n unreliableChannel,\n conn\n };\n }\n\n configurePublisherSdp(jsep) {\n jsep.sdp = jsep.sdp.replace(/a=fmtp:(109|111).*\\r\\n/g, (line, pt) => {\n const parameters = Object.assign(sdpUtils.parseFmtp(line), OPUS_PARAMETERS);\n return sdpUtils.writeFmtp({ payloadType: pt, parameters: parameters });\n });\n return jsep;\n }\n\n configureSubscriberSdp(jsep) {\n // todo: consider cleaning up these hacks to use sdputils\n if (!isH264VideoSupported) {\n if (navigator.userAgent.indexOf(\"HeadlessChrome\") !== -1) {\n // HeadlessChrome (e.g. puppeteer) doesn't support webrtc video streams, so we remove those lines from the SDP.\n jsep.sdp = jsep.sdp.replace(/m=video[^]*m=/, \"m=\");\n }\n }\n\n // TODO: Hack to get video working on Chrome for Android. https://groups.google.com/forum/#!topic/mozilla.dev.media/Ye29vuMTpo8\n if (navigator.userAgent.indexOf(\"Android\") === -1) {\n jsep.sdp = jsep.sdp.replace(\n \"a=rtcp-fb:107 goog-remb\\r\\n\",\n \"a=rtcp-fb:107 goog-remb\\r\\na=rtcp-fb:107 transport-cc\\r\\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\\r\\n\"\n );\n } else {\n jsep.sdp = jsep.sdp.replace(\n \"a=rtcp-fb:107 goog-remb\\r\\n\",\n \"a=rtcp-fb:107 goog-remb\\r\\na=rtcp-fb:107 transport-cc\\r\\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f\\r\\n\"\n );\n }\n return jsep;\n }\n\n async fixSafariIceUFrag(jsep) {\n // Safari produces a \\n instead of an \\r\\n for the ice-ufrag. See https://github.com/meetecho/janus-gateway/issues/1818\n jsep.sdp = jsep.sdp.replace(/[^\\r]\\na=ice-ufrag/g, \"\\r\\na=ice-ufrag\");\n return jsep\n }\n\n async createSubscriber(occupantId, maxRetries = 5) {\n if (this.leftOccupants.has(occupantId)) {\n console.warn(occupantId + \": cancelled occupant connection, occupant left before subscription negotation.\");\n return null;\n }\n\n var handle = new mj.JanusPluginHandle(this.session);\n var conn = new RTCPeerConnection(this.peerConnectionConfig || DEFAULT_PEER_CONNECTION_CONFIG);\n\n debug(occupantId + \": sub waiting for sfu\");\n await handle.attach(\"janus.plugin.sfu\");\n\n this.associate(conn, handle);\n\n debug(occupantId + \": sub waiting for join\");\n\n if (this.leftOccupants.has(occupantId)) {\n conn.close();\n console.warn(occupantId + \": cancelled occupant connection, occupant left after attach\");\n return null;\n }\n\n let webrtcFailed = false;\n\n const webrtcup = new Promise(resolve => {\n const leftInterval = setInterval(() => {\n if (this.leftOccupants.has(occupantId)) {\n clearInterval(leftInterval);\n resolve();\n }\n }, 1000);\n\n const timeout = setTimeout(() => {\n clearInterval(leftInterval);\n webrtcFailed = true;\n resolve();\n }, SUBSCRIBE_TIMEOUT_MS);\n\n handle.on(\"webrtcup\", () => {\n clearTimeout(timeout);\n clearInterval(leftInterval);\n resolve();\n });\n });\n\n // Send join message to janus. Don't listen for join/leave messages. Subscribe to the occupant's media.\n // Janus should send us an offer for this occupant's media in response to this.\n await this.sendJoin(handle, { media: occupantId });\n\n if (this.leftOccupants.has(occupantId)) {\n conn.close();\n console.warn(occupantId + \": cancelled occupant connection, occupant left after join\");\n return null;\n }\n\n debug(occupantId + \": sub waiting for webrtcup\");\n await webrtcup;\n\n if (this.leftOccupants.has(occupantId)) {\n conn.close();\n console.warn(occupantId + \": cancel occupant connection, occupant left during or after webrtcup\");\n return null;\n }\n\n if (webrtcFailed) {\n conn.close();\n if (maxRetries > 0) {\n console.warn(occupantId + \": webrtc up timed out, retrying\");\n return this.createSubscriber(occupantId, maxRetries - 1);\n } else {\n console.warn(occupantId + \": webrtc up timed out\");\n return null;\n }\n }\n\n if (isSafari && !this._iOSHackDelayedInitialPeer) {\n // HACK: the first peer on Safari during page load can fail to work if we don't\n // wait some time before continuing here. See: https://github.com/mozilla/hubs/pull/1692\n await (new Promise((resolve) => setTimeout(resolve, 3000)));\n this._iOSHackDelayedInitialPeer = true;\n }\n\n var mediaStream = new MediaStream();\n var receivers = conn.getReceivers();\n receivers.forEach(receiver => {\n if (receiver.track) {\n mediaStream.addTrack(receiver.track);\n }\n });\n if (mediaStream.getTracks().length === 0) {\n mediaStream = null;\n }\n\n debug(occupantId + \": subscriber ready\");\n return {\n handle,\n mediaStream,\n conn\n };\n }\n\n sendJoin(handle, subscribe) {\n return handle.sendMessage({\n kind: \"join\",\n room_id: this.room,\n user_id: this.clientId,\n subscribe,\n token: this.joinToken\n });\n }\n\n toggleFreeze() {\n if (this.frozen) {\n this.unfreeze();\n } else {\n this.freeze();\n }\n }\n\n freeze() {\n this.frozen = true;\n }\n\n unfreeze() {\n this.frozen = false;\n this.flushPendingUpdates();\n }\n\n dataForUpdateMultiMessage(networkId, message) {\n // \"d\" is an array of entity datas, where each item in the array represents a unique entity and contains\n // metadata for the entity, and an array of components that have been updated on the entity.\n // This method finds the data corresponding to the given networkId.\n for (let i = 0, l = message.data.d.length; i < l; i++) {\n const data = message.data.d[i];\n\n if (data.networkId === networkId) {\n return data;\n }\n }\n\n return null;\n }\n\n getPendingData(networkId, message) {\n if (!message) return null;\n\n let data = message.dataType === \"um\" ? this.dataForUpdateMultiMessage(networkId, message) : message.data;\n\n // Ignore messages relating to users who have disconnected since freezing, their entities\n // will have aleady been removed by NAF.\n // Note that delete messages have no \"owner\" so we have to check for that as well.\n if (data.owner && !this.occupants[data.owner]) return null;\n\n // Ignore messages from users that we may have blocked while frozen.\n if (data.owner && this.blockedClients.has(data.owner)) return null;\n\n return data\n }\n\n // Used externally\n getPendingDataForNetworkId(networkId) {\n return this.getPendingData(networkId, this.frozenUpdates.get(networkId));\n }\n\n flushPendingUpdates() {\n for (const [networkId, message] of this.frozenUpdates) {\n let data = this.getPendingData(networkId, message);\n if (!data) continue;\n\n // Override the data type on \"um\" messages types, since we extract entity updates from \"um\" messages into\n // individual frozenUpdates in storeSingleMessage.\n const dataType = message.dataType === \"um\" ? \"u\" : message.dataType;\n\n this.onOccupantMessage(null, dataType, data, message.source);\n }\n this.frozenUpdates.clear();\n }\n\n storeMessage(message) {\n if (message.dataType === \"um\") { // UpdateMulti\n for (let i = 0, l = message.data.d.length; i < l; i++) {\n this.storeSingleMessage(message, i);\n }\n } else {\n this.storeSingleMessage(message);\n }\n }\n\n storeSingleMessage(message, index) {\n const data = index !== undefined ? message.data.d[index] : message.data;\n const dataType = message.dataType;\n const source = message.source;\n\n const networkId = data.networkId;\n\n if (!this.frozenUpdates.has(networkId)) {\n this.frozenUpdates.set(networkId, message);\n } else {\n const storedMessage = this.frozenUpdates.get(networkId);\n const storedData = storedMessage.dataType === \"um\" ? this.dataForUpdateMultiMessage(networkId, storedMessage) : storedMessage.data;\n\n // Avoid updating components if the entity data received did not come from the current owner.\n const isOutdatedMessage = data.lastOwnerTime < storedData.lastOwnerTime;\n const isContemporaneousMessage = data.lastOwnerTime === storedData.lastOwnerTime;\n if (isOutdatedMessage || (isContemporaneousMessage && storedData.owner > data.owner)) {\n return;\n }\n\n if (dataType === \"r\") {\n const createdWhileFrozen = storedData && storedData.isFirstSync;\n if (createdWhileFrozen) {\n // If the entity was created and deleted while frozen, don't bother conveying anything to the consumer.\n this.frozenUpdates.delete(networkId);\n } else {\n // Delete messages override any other messages for this entity\n this.frozenUpdates.set(networkId, message);\n }\n } else {\n // merge in component updates\n if (storedData.components && data.components) {\n Object.assign(storedData.components, data.components);\n }\n }\n }\n }\n\n onDataChannelMessage(e, source) {\n this.onData(JSON.parse(e.data), source);\n }\n\n onData(message, source) {\n if (debug.enabled) {\n debug(`DC in: ${message}`);\n }\n\n if (!message.dataType) return;\n\n message.source = source;\n\n if (this.frozen) {\n this.storeMessage(message);\n } else {\n this.onOccupantMessage(null, message.dataType, message.data, message.source);\n }\n }\n\n shouldStartConnectionTo(client) {\n return true;\n }\n\n startStreamConnection(client) {}\n\n closeStreamConnection(client) {}\n\n getConnectStatus(clientId) {\n return this.occupants[clientId] ? NAF.adapters.IS_CONNECTED : NAF.adapters.NOT_CONNECTED;\n }\n\n async updateTimeOffset() {\n if (this.isDisconnected()) return;\n\n const clientSentTime = Date.now();\n\n const res = await fetch(document.location.href, {\n method: \"HEAD\",\n cache: \"no-cache\"\n });\n\n const precision = 1000;\n const serverReceivedTime = new Date(res.headers.get(\"Date\")).getTime() + precision / 2;\n const clientReceivedTime = Date.now();\n const serverTime = serverReceivedTime + (clientReceivedTime - clientSentTime) / 2;\n const timeOffset = serverTime - clientReceivedTime;\n\n this.serverTimeRequests++;\n\n if (this.serverTimeRequests <= 10) {\n this.timeOffsets.push(timeOffset);\n } else {\n this.timeOffsets[this.serverTimeRequests % 10] = timeOffset;\n }\n\n this.avgTimeOffset = this.timeOffsets.reduce((acc, offset) => (acc += offset), 0) / this.timeOffsets.length;\n\n if (this.serverTimeRequests > 10) {\n debug(`new server time offset: ${this.avgTimeOffset}ms`);\n setTimeout(() => this.updateTimeOffset(), 5 * 60 * 1000); // Sync clock every 5 minutes.\n } else {\n this.updateTimeOffset();\n }\n }\n\n getServerTime() {\n return Date.now() + this.avgTimeOffset;\n }\n\n getMediaStream(clientId, type = \"audio\") {\n if (this.mediaStreams[clientId]) {\n debug(`Already had ${type} for ${clientId}`);\n return Promise.resolve(this.mediaStreams[clientId][type]);\n } else {\n debug(`Waiting on ${type} for ${clientId}`);\n if (!this.pendingMediaRequests.has(clientId)) {\n this.pendingMediaRequests.set(clientId, {});\n\n const audioPromise = new Promise((resolve, reject) => {\n this.pendingMediaRequests.get(clientId).audio = { resolve, reject };\n });\n const videoPromise = new Promise((resolve, reject) => {\n this.pendingMediaRequests.get(clientId).video = { resolve, reject };\n });\n\n this.pendingMediaRequests.get(clientId).audio.promise = audioPromise;\n this.pendingMediaRequests.get(clientId).video.promise = videoPromise;\n\n audioPromise.catch(e => console.warn(`${clientId} getMediaStream Audio Error`, e));\n videoPromise.catch(e => console.warn(`${clientId} getMediaStream Video Error`, e));\n }\n return this.pendingMediaRequests.get(clientId)[type].promise;\n }\n }\n\n setMediaStream(clientId, stream) {\n // Safari doesn't like it when you use single a mixed media stream where one of the tracks is inactive, so we\n // split the tracks into two streams.\n const audioStream = new MediaStream();\n try {\n stream.getAudioTracks().forEach(track => audioStream.addTrack(track));\n\n } catch(e) {\n console.warn(`${clientId} setMediaStream Audio Error`, e);\n }\n const videoStream = new MediaStream();\n try {\n stream.getVideoTracks().forEach(track => videoStream.addTrack(track));\n\n } catch (e) {\n console.warn(`${clientId} setMediaStream Video Error`, e);\n }\n\n this.mediaStreams[clientId] = { audio: audioStream, video: videoStream };\n\n // Resolve the promise for the user's media stream if it exists.\n if (this.pendingMediaRequests.has(clientId)) {\n this.pendingMediaRequests.get(clientId).audio.resolve(audioStream);\n this.pendingMediaRequests.get(clientId).video.resolve(videoStream);\n }\n }\n\n async setLocalMediaStream(stream) {\n // our job here is to make sure the connection winds up with RTP senders sending the stuff in this stream,\n // and not the stuff that isn't in this stream. strategy is to replace existing tracks if we can, add tracks\n // that we can't replace, and disable tracks that don't exist anymore.\n\n // note that we don't ever remove a track from the stream -- since Janus doesn't support Unified Plan, we absolutely\n // can't wind up with a SDP that has >1 audio or >1 video tracks, even if one of them is inactive (what you get if\n // you remove a track from an existing stream.)\n if (this.publisher && this.publisher.conn) {\n const existingSenders = this.publisher.conn.getSenders();\n const newSenders = [];\n const tracks = stream.getTracks();\n\n for (let i = 0; i < tracks.length; i++) {\n const t = tracks[i];\n const sender = existingSenders.find(s => s.track != null && s.track.kind == t.kind);\n\n if (sender != null) {\n if (sender.replaceTrack) {\n await sender.replaceTrack(t);\n\n // Workaround https://bugzilla.mozilla.org/show_bug.cgi?id=1576771\n if (t.kind === \"video\" && t.enabled && navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {\n t.enabled = false;\n setTimeout(() => t.enabled = true, 1000);\n }\n } else {\n // Fallback for browsers that don't support replaceTrack. At this time of this writing\n // most browsers support it, and testing this code path seems to not work properly\n // in Chrome anymore.\n stream.removeTrack(sender.track);\n stream.addTrack(t);\n }\n newSenders.push(sender);\n } else {\n newSenders.push(this.publisher.conn.addTrack(t, stream));\n }\n }\n existingSenders.forEach(s => {\n if (!newSenders.includes(s)) {\n s.track.enabled = false;\n }\n });\n }\n this.localMediaStream = stream;\n this.setMediaStream(this.clientId, stream);\n }\n\n enableMicrophone(enabled) {\n if (this.publisher && this.publisher.conn) {\n this.publisher.conn.getSenders().forEach(s => {\n if (s.track.kind == \"audio\") {\n s.track.enabled = enabled;\n }\n });\n }\n }\n\n sendData(clientId, dataType, data) {\n if (!this.publisher) {\n console.warn(\"sendData called without a publisher\");\n } else {\n switch (this.unreliableTransport) {\n case \"websocket\":\n this.publisher.handle.sendMessage({ kind: \"data\", body: JSON.stringify({ dataType, data }), whom: clientId });\n break;\n case \"datachannel\":\n this.publisher.unreliableChannel.send(JSON.stringify({ clientId, dataType, data }));\n break;\n default:\n this.unreliableTransport(clientId, dataType, data);\n break;\n }\n }\n }\n\n sendDataGuaranteed(clientId, dataType, data) {\n if (!this.publisher) {\n console.warn(\"sendDataGuaranteed called without a publisher\");\n } else {\n switch (this.reliableTransport) {\n case \"websocket\":\n this.publisher.handle.sendMessage({ kind: \"data\", body: JSON.stringify({ dataType, data }), whom: clientId });\n break;\n case \"datachannel\":\n this.publisher.reliableChannel.send(JSON.stringify({ clientId, dataType, data }));\n break;\n default:\n this.reliableTransport(clientId, dataType, data);\n break;\n }\n }\n }\n\n broadcastData(dataType, data) {\n if (!this.publisher) {\n console.warn(\"broadcastData called without a publisher\");\n } else {\n switch (this.unreliableTransport) {\n case \"websocket\":\n this.publisher.handle.sendMessage({ kind: \"data\", body: JSON.stringify({ dataType, data }) });\n break;\n case \"datachannel\":\n this.publisher.unreliableChannel.send(JSON.stringify({ dataType, data }));\n break;\n default:\n this.unreliableTransport(undefined, dataType, data);\n break;\n }\n }\n }\n\n broadcastDataGuaranteed(dataType, data) {\n if (!this.publisher) {\n console.warn(\"broadcastDataGuaranteed called without a publisher\");\n } else {\n switch (this.reliableTransport) {\n case \"websocket\":\n this.publisher.handle.sendMessage({ kind: \"data\", body: JSON.stringify({ dataType, data }) });\n break;\n case \"datachannel\":\n this.publisher.reliableChannel.send(JSON.stringify({ dataType, data }));\n break;\n default:\n this.reliableTransport(undefined, dataType, data);\n break;\n }\n }\n }\n\n kick(clientId, permsToken) {\n return this.publisher.handle.sendMessage({ kind: \"kick\", room_id: this.room, user_id: clientId, token: permsToken }).then(() => {\n document.body.dispatchEvent(new CustomEvent(\"kicked\", { detail: { clientId: clientId } }));\n });\n }\n\n block(clientId) {\n return this.publisher.handle.sendMessage({ kind: \"block\", whom: clientId }).then(() => {\n this.blockedClients.set(clientId, true);\n document.body.dispatchEvent(new CustomEvent(\"blocked\", { detail: { clientId: clientId } }));\n });\n }\n\n unblock(clientId) {\n return this.publisher.handle.sendMessage({ kind: \"unblock\", whom: clientId }).then(() => {\n this.blockedClients.delete(clientId);\n document.body.dispatchEvent(new CustomEvent(\"unblocked\", { detail: { clientId: clientId } }));\n });\n }\n}\n\nNAF.adapters.register(\"janus\", JanusAdapter);\n\nmodule.exports = JanusAdapter;\n","/**\n * Represents a handle to a single Janus plugin on a Janus session. Each WebRTC connection to the Janus server will be\n * associated with a single handle. Once attached to the server, this handle will be given a unique ID which should be\n * used to associate it with future signalling messages.\n *\n * See https://janus.conf.meetecho.com/docs/rest.html#handles.\n **/\nfunction JanusPluginHandle(session) {\n this.session = session;\n this.id = undefined;\n}\n\n/** Attaches this handle to the Janus server and sets its ID. **/\nJanusPluginHandle.prototype.attach = function(plugin) {\n var payload = { plugin: plugin, \"force-bundle\": true, \"force-rtcp-mux\": true };\n return this.session.send(\"attach\", payload).then(resp => {\n this.id = resp.data.id;\n return resp;\n });\n};\n\n/** Detaches this handle. **/\nJanusPluginHandle.prototype.detach = function() {\n return this.send(\"detach\");\n};\n\n/** Registers a callback to be fired upon the reception of any incoming Janus signals for this plugin handle with the\n * `janus` attribute equal to `ev`.\n **/\nJanusPluginHandle.prototype.on = function(ev, callback) {\n return this.session.on(ev, signal => {\n if (signal.sender == this.id) {\n callback(signal);\n }\n });\n};\n\n/**\n * Sends a signal associated with this handle. Signals should be JSON-serializable objects. Returns a promise that will\n * be resolved or rejected when a response to this signal is received, or when no response is received within the\n * session timeout.\n **/\nJanusPluginHandle.prototype.send = function(type, signal) {\n return this.session.send(type, Object.assign({ handle_id: this.id }, signal));\n};\n\n/** Sends a plugin-specific message associated with this handle. **/\nJanusPluginHandle.prototype.sendMessage = function(body) {\n return this.send(\"message\", { body: body });\n};\n\n/** Sends a JSEP offer or answer associated with this handle. **/\nJanusPluginHandle.prototype.sendJsep = function(jsep) {\n return this.send(\"message\", { body: {}, jsep: jsep });\n};\n\n/** Sends an ICE trickle candidate associated with this handle. **/\nJanusPluginHandle.prototype.sendTrickle = function(candidate) {\n return this.send(\"trickle\", { candidate: candidate });\n};\n\n/**\n * Represents a Janus session -- a Janus context from within which you can open multiple handles and connections. Once\n * created, this session will be given a unique ID which should be used to associate it with future signalling messages.\n *\n * See https://janus.conf.meetecho.com/docs/rest.html#sessions.\n **/\nfunction JanusSession(output, options) {\n this.output = output;\n this.id = undefined;\n this.nextTxId = 0;\n this.txns = {};\n this.eventHandlers = {};\n this.options = Object.assign({\n verbose: false,\n timeoutMs: 10000,\n keepaliveMs: 30000\n }, options);\n}\n\n/** Creates this session on the Janus server and sets its ID. **/\nJanusSession.prototype.create = function() {\n return this.send(\"create\").then(resp => {\n this.id = resp.data.id;\n return resp;\n });\n};\n\n/**\n * Destroys this session. Note that upon destruction, Janus will also close the signalling transport (if applicable) and\n * any open WebRTC connections.\n **/\nJanusSession.prototype.destroy = function() {\n return this.send(\"destroy\").then((resp) => {\n this.dispose();\n return resp;\n });\n};\n\n/**\n * Disposes of this session in a way such that no further incoming signalling messages will be processed.\n * Outstanding transactions will be rejected.\n **/\nJanusSession.prototype.dispose = function() {\n this._killKeepalive();\n this.eventHandlers = {};\n for (var txId in this.txns) {\n if (this.txns.hasOwnProperty(txId)) {\n var txn = this.txns[txId];\n clearTimeout(txn.timeout);\n txn.reject(new Error(\"Janus session was disposed.\"));\n delete this.txns[txId];\n }\n }\n};\n\n/**\n * Whether this signal represents an error, and the associated promise (if any) should be rejected.\n * Users should override this to handle any custom plugin-specific error conventions.\n **/\nJanusSession.prototype.isError = function(signal) {\n return signal.janus === \"error\";\n};\n\n/** Registers a callback to be fired upon the reception of any incoming Janus signals for this session with the\n * `janus` attribute equal to `ev`.\n **/\nJanusSession.prototype.on = function(ev, callback) {\n var handlers = this.eventHandlers[ev];\n if (handlers == null) {\n handlers = this.eventHandlers[ev] = [];\n }\n handlers.push(callback);\n};\n\n/**\n * Callback for receiving JSON signalling messages pertinent to this session. If the signals are responses to previously\n * sent signals, the promises for the outgoing signals will be resolved or rejected appropriately with this signal as an\n * argument.\n *\n * External callers should call this function every time a new signal arrives on the transport; for example, in a\n * WebSocket's `message` event, or when a new datum shows up in an HTTP long-polling response.\n **/\nJanusSession.prototype.receive = function(signal) {\n if (this.options.verbose) {\n this._logIncoming(signal);\n }\n if (signal.session_id != this.id) {\n console.warn(\"Incorrect session ID received in Janus signalling message: was \" + signal.session_id + \", expected \" + this.id + \".\");\n }\n\n var responseType = signal.janus;\n var handlers = this.eventHandlers[responseType];\n if (handlers != null) {\n for (var i = 0; i < handlers.length; i++) {\n handlers[i](signal);\n }\n }\n\n if (signal.transaction != null) {\n var txn = this.txns[signal.transaction];\n if (txn == null) {\n // this is a response to a transaction that wasn't caused via JanusSession.send, or a plugin replied twice to a\n // single request, or the session was disposed, or something else that isn't under our purview; that's fine\n return;\n }\n\n if (responseType === \"ack\" && txn.type == \"message\") {\n // this is an ack of an asynchronously-processed plugin request, we should wait to resolve the promise until the\n // actual response comes in\n return;\n }\n\n clearTimeout(txn.timeout);\n\n delete this.txns[signal.transaction];\n (this.isError(signal) ? txn.reject : txn.resolve)(signal);\n }\n};\n\n/**\n * Sends a signal associated with this session, beginning a new transaction. Returns a promise that will be resolved or\n * rejected when a response is received in the same transaction, or when no response is received within the session\n * timeout.\n **/\nJanusSession.prototype.send = function(type, signal) {\n signal = Object.assign({ transaction: (this.nextTxId++).toString() }, signal);\n return new Promise((resolve, reject) => {\n var timeout = null;\n if (this.options.timeoutMs) {\n timeout = setTimeout(() => {\n delete this.txns[signal.transaction];\n reject(new Error(\"Signalling transaction with txid \" + signal.transaction + \" timed out.\"));\n }, this.options.timeoutMs);\n }\n this.txns[signal.transaction] = { resolve: resolve, reject: reject, timeout: timeout, type: type };\n this._transmit(type, signal);\n });\n};\n\nJanusSession.prototype._transmit = function(type, signal) {\n signal = Object.assign({ janus: type }, signal);\n\n if (this.id != null) { // this.id is undefined in the special case when we're sending the session create message\n signal = Object.assign({ session_id: this.id }, signal);\n }\n\n if (this.options.verbose) {\n this._logOutgoing(signal);\n }\n\n this.output(JSON.stringify(signal));\n this._resetKeepalive();\n};\n\nJanusSession.prototype._logOutgoing = function(signal) {\n var kind = signal.janus;\n if (kind === \"message\" && signal.jsep) {\n kind = signal.jsep.type;\n }\n var message = \"> Outgoing Janus \" + (kind || \"signal\") + \" (#\" + signal.transaction + \"): \";\n console.debug(\"%c\" + message, \"color: #040\", signal);\n};\n\nJanusSession.prototype._logIncoming = function(signal) {\n var kind = signal.janus;\n var message = signal.transaction ?\n \"< Incoming Janus \" + (kind || \"signal\") + \" (#\" + signal.transaction + \"): \" :\n \"< Incoming Janus \" + (kind || \"signal\") + \": \";\n console.debug(\"%c\" + message, \"color: #004\", signal);\n};\n\nJanusSession.prototype._sendKeepalive = function() {\n return this.send(\"keepalive\");\n};\n\nJanusSession.prototype._killKeepalive = function() {\n clearTimeout(this.keepaliveTimeout);\n};\n\nJanusSession.prototype._resetKeepalive = function() {\n this._killKeepalive();\n if (this.options.keepaliveMs) {\n this.keepaliveTimeout = setTimeout(() => {\n this._sendKeepalive().catch(e => console.error(\"Error received from keepalive: \", e));\n }, this.options.keepaliveMs);\n }\n};\n\nmodule.exports = {\n JanusPluginHandle,\n JanusSession\n};\n","/* eslint-env node */\n'use strict';\n\n// SDP helpers.\nconst SDPUtils = {};\n\n// Generate an alphanumeric identifier for cname or mids.\n// TODO: use UUIDs instead? https://gist.github.com/jed/982883\nSDPUtils.generateIdentifier = function() {\n return Math.random().toString(36).substr(2, 10);\n};\n\n// The RTCP CNAME used by all peerconnections from the same JS.\nSDPUtils.localCName = SDPUtils.generateIdentifier();\n\n// Splits SDP into lines, dealing with both CRLF and LF.\nSDPUtils.splitLines = function(blob) {\n return blob.trim().split('\\n').map(line => line.trim());\n};\n// Splits SDP into sessionpart and mediasections. Ensures CRLF.\nSDPUtils.splitSections = function(blob) {\n const parts = blob.split('\\nm=');\n return parts.map((part, index) => (index > 0 ?\n 'm=' + part : part).trim() + '\\r\\n');\n};\n\n// Returns the session description.\nSDPUtils.getDescription = function(blob) {\n const sections = SDPUtils.splitSections(blob);\n return sections && sections[0];\n};\n\n// Returns the individual media sections.\nSDPUtils.getMediaSections = function(blob) {\n const sections = SDPUtils.splitSections(blob);\n sections.shift();\n return sections;\n};\n\n// Returns lines that start with a certain prefix.\nSDPUtils.matchPrefix = function(blob, prefix) {\n return SDPUtils.splitLines(blob).filter(line => line.indexOf(prefix) === 0);\n};\n\n// Parses an ICE candidate line. Sample input:\n// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8\n// rport 55996\"\n// Input can be prefixed with a=.\nSDPUtils.parseCandidate = function(line) {\n let parts;\n // Parse both variants.\n if (line.indexOf('a=candidate:') === 0) {\n parts = line.substring(12).split(' ');\n } else {\n parts = line.substring(10).split(' ');\n }\n\n const candidate = {\n foundation: parts[0],\n component: {1: 'rtp', 2: 'rtcp'}[parts[1]] || parts[1],\n protocol: parts[2].toLowerCase(),\n priority: parseInt(parts[3], 10),\n ip: parts[4],\n address: parts[4], // address is an alias for ip.\n port: parseInt(parts[5], 10),\n // skip parts[6] == 'typ'\n type: parts[7],\n };\n\n for (let i = 8; i < parts.length; i += 2) {\n switch (parts[i]) {\n case 'raddr':\n candidate.relatedAddress = parts[i + 1];\n break;\n case 'rport':\n candidate.relatedPort = parseInt(parts[i + 1], 10);\n break;\n case 'tcptype':\n candidate.tcpType = parts[i + 1];\n break;\n case 'ufrag':\n candidate.ufrag = parts[i + 1]; // for backward compatibility.\n candidate.usernameFragment = parts[i + 1];\n break;\n default: // extension handling, in particular ufrag. Don't overwrite.\n if (candidate[parts[i]] === undefined) {\n candidate[parts[i]] = parts[i + 1];\n }\n break;\n }\n }\n return candidate;\n};\n\n// Translates a candidate object into SDP candidate attribute.\n// This does not include the a= prefix!\nSDPUtils.writeCandidate = function(candidate) {\n const sdp = [];\n sdp.push(candidate.foundation);\n\n const component = candidate.component;\n if (component === 'rtp') {\n sdp.push(1);\n } else if (component === 'rtcp') {\n sdp.push(2);\n } else {\n sdp.push(component);\n }\n sdp.push(candidate.protocol.toUpperCase());\n sdp.push(candidate.priority);\n sdp.push(candidate.address || candidate.ip);\n sdp.push(candidate.port);\n\n const type = candidate.type;\n sdp.push('typ');\n sdp.push(type);\n if (type !== 'host' && candidate.relatedAddress &&\n candidate.relatedPort) {\n sdp.push('raddr');\n sdp.push(candidate.relatedAddress);\n sdp.push('rport');\n sdp.push(candidate.relatedPort);\n }\n if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {\n sdp.push('tcptype');\n sdp.push(candidate.tcpType);\n }\n if (candidate.usernameFragment || candidate.ufrag) {\n sdp.push('ufrag');\n sdp.push(candidate.usernameFragment || candidate.ufrag);\n }\n return 'candidate:' + sdp.join(' ');\n};\n\n// Parses an ice-options line, returns an array of option tags.\n// Sample input:\n// a=ice-options:foo bar\nSDPUtils.parseIceOptions = function(line) {\n return line.substr(14).split(' ');\n};\n\n// Parses a rtpmap line, returns RTCRtpCoddecParameters. Sample input:\n// a=rtpmap:111 opus/48000/2\nSDPUtils.parseRtpMap = function(line) {\n let parts = line.substr(9).split(' ');\n const parsed = {\n payloadType: parseInt(parts.shift(), 10), // was: id\n };\n\n parts = parts[0].split('/');\n\n parsed.name = parts[0];\n parsed.clockRate = parseInt(parts[1], 10); // was: clockrate\n parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1;\n // legacy alias, got renamed back to channels in ORTC.\n parsed.numChannels = parsed.channels;\n return parsed;\n};\n\n// Generates a rtpmap line from RTCRtpCodecCapability or\n// RTCRtpCodecParameters.\nSDPUtils.writeRtpMap = function(codec) {\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n const channels = codec.channels || codec.numChannels || 1;\n return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +\n (channels !== 1 ? '/' + channels : '') + '\\r\\n';\n};\n\n// Parses a extmap line (headerextension from RFC 5285). Sample input:\n// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\n// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset\nSDPUtils.parseExtmap = function(line) {\n const parts = line.substr(9).split(' ');\n return {\n id: parseInt(parts[0], 10),\n direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',\n uri: parts[1],\n };\n};\n\n// Generates an extmap line from RTCRtpHeaderExtensionParameters or\n// RTCRtpHeaderExtension.\nSDPUtils.writeExtmap = function(headerExtension) {\n return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +\n (headerExtension.direction && headerExtension.direction !== 'sendrecv'\n ? '/' + headerExtension.direction\n : '') +\n ' ' + headerExtension.uri + '\\r\\n';\n};\n\n// Parses a fmtp line, returns dictionary. Sample input:\n// a=fmtp:96 vbr=on;cng=on\n// Also deals with vbr=on; cng=on\nSDPUtils.parseFmtp = function(line) {\n const parsed = {};\n let kv;\n const parts = line.substr(line.indexOf(' ') + 1).split(';');\n for (let j = 0; j < parts.length; j++) {\n kv = parts[j].trim().split('=');\n parsed[kv[0].trim()] = kv[1];\n }\n return parsed;\n};\n\n// Generates a fmtp line from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeFmtp = function(codec) {\n let line = '';\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n if (codec.parameters && Object.keys(codec.parameters).length) {\n const params = [];\n Object.keys(codec.parameters).forEach(param => {\n if (codec.parameters[param] !== undefined) {\n params.push(param + '=' + codec.parameters[param]);\n } else {\n params.push(param);\n }\n });\n line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\\r\\n';\n }\n return line;\n};\n\n// Parses a rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:\n// a=rtcp-fb:98 nack rpsi\nSDPUtils.parseRtcpFb = function(line) {\n const parts = line.substr(line.indexOf(' ') + 1).split(' ');\n return {\n type: parts.shift(),\n parameter: parts.join(' '),\n };\n};\n\n// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeRtcpFb = function(codec) {\n let lines = '';\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n if (codec.rtcpFeedback && codec.rtcpFeedback.length) {\n // FIXME: special handling for trr-int?\n codec.rtcpFeedback.forEach(fb => {\n lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +\n (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +\n '\\r\\n';\n });\n }\n return lines;\n};\n\n// Parses a RFC 5576 ssrc media attribute. Sample input:\n// a=ssrc:3735928559 cname:something\nSDPUtils.parseSsrcMedia = function(line) {\n const sp = line.indexOf(' ');\n const parts = {\n ssrc: parseInt(line.substr(7, sp - 7), 10),\n };\n const colon = line.indexOf(':', sp);\n if (colon > -1) {\n parts.attribute = line.substr(sp + 1, colon - sp - 1);\n parts.value = line.substr(colon + 1);\n } else {\n parts.attribute = line.substr(sp + 1);\n }\n return parts;\n};\n\n// Parse a ssrc-group line (see RFC 5576). Sample input:\n// a=ssrc-group:semantics 12 34\nSDPUtils.parseSsrcGroup = function(line) {\n const parts = line.substr(13).split(' ');\n return {\n semantics: parts.shift(),\n ssrcs: parts.map(ssrc => parseInt(ssrc, 10)),\n };\n};\n\n// Extracts the MID (RFC 5888) from a media section.\n// Returns the MID or undefined if no mid line was found.\nSDPUtils.getMid = function(mediaSection) {\n const mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];\n if (mid) {\n return mid.substr(6);\n }\n};\n\n// Parses a fingerprint line for DTLS-SRTP.\nSDPUtils.parseFingerprint = function(line) {\n const parts = line.substr(14).split(' ');\n return {\n algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.\n value: parts[1].toUpperCase(), // the definition is upper-case in RFC 4572.\n };\n};\n\n// Extracts DTLS parameters from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n// get the fingerprint line as input. See also getIceParameters.\nSDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {\n const lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=fingerprint:');\n // Note: a=setup line is ignored since we use the 'auto' role in Edge.\n return {\n role: 'auto',\n fingerprints: lines.map(SDPUtils.parseFingerprint),\n };\n};\n\n// Serializes DTLS parameters to SDP.\nSDPUtils.writeDtlsParameters = function(params, setupType) {\n let sdp = 'a=setup:' + setupType + '\\r\\n';\n params.fingerprints.forEach(fp => {\n sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\\r\\n';\n });\n return sdp;\n};\n\n// Parses a=crypto lines into\n// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members\nSDPUtils.parseCryptoLine = function(line) {\n const parts = line.substr(9).split(' ');\n return {\n tag: parseInt(parts[0], 10),\n cryptoSuite: parts[1],\n keyParams: parts[2],\n sessionParams: parts.slice(3),\n };\n};\n\nSDPUtils.writeCryptoLine = function(parameters) {\n return 'a=crypto:' + parameters.tag + ' ' +\n parameters.cryptoSuite + ' ' +\n (typeof parameters.keyParams === 'object'\n ? SDPUtils.writeCryptoKeyParams(parameters.keyParams)\n : parameters.keyParams) +\n (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') +\n '\\r\\n';\n};\n\n// Parses the crypto key parameters into\n// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam*\nSDPUtils.parseCryptoKeyParams = function(keyParams) {\n if (keyParams.indexOf('inline:') !== 0) {\n return null;\n }\n const parts = keyParams.substr(7).split('|');\n return {\n keyMethod: 'inline',\n keySalt: parts[0],\n lifeTime: parts[1],\n mkiValue: parts[2] ? parts[2].split(':')[0] : undefined,\n mkiLength: parts[2] ? parts[2].split(':')[1] : undefined,\n };\n};\n\nSDPUtils.writeCryptoKeyParams = function(keyParams) {\n return keyParams.keyMethod + ':'\n + keyParams.keySalt +\n (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') +\n (keyParams.mkiValue && keyParams.mkiLength\n ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength\n : '');\n};\n\n// Extracts all SDES parameters.\nSDPUtils.getCryptoParameters = function(mediaSection, sessionpart) {\n const lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=crypto:');\n return lines.map(SDPUtils.parseCryptoLine);\n};\n\n// Parses ICE information from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n// get the ice-ufrag and ice-pwd lines as input.\nSDPUtils.getIceParameters = function(mediaSection, sessionpart) {\n const ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=ice-ufrag:')[0];\n const pwd = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=ice-pwd:')[0];\n if (!(ufrag && pwd)) {\n return null;\n }\n return {\n usernameFragment: ufrag.substr(12),\n password: pwd.substr(10),\n };\n};\n\n// Serializes ICE parameters to SDP.\nSDPUtils.writeIceParameters = function(params) {\n let sdp = 'a=ice-ufrag:' + params.usernameFragment + '\\r\\n' +\n 'a=ice-pwd:' + params.password + '\\r\\n';\n if (params.iceLite) {\n sdp += 'a=ice-lite\\r\\n';\n }\n return sdp;\n};\n\n// Parses the SDP media section and returns RTCRtpParameters.\nSDPUtils.parseRtpParameters = function(mediaSection) {\n const description = {\n codecs: [],\n headerExtensions: [],\n fecMechanisms: [],\n rtcp: [],\n };\n const lines = SDPUtils.splitLines(mediaSection);\n const mline = lines[0].split(' ');\n for (let i = 3; i < mline.length; i++) { // find all codecs from mline[3..]\n const pt = mline[i];\n const rtpmapline = SDPUtils.matchPrefix(\n mediaSection, 'a=rtpmap:' + pt + ' ')[0];\n if (rtpmapline) {\n const codec = SDPUtils.parseRtpMap(rtpmapline);\n const fmtps = SDPUtils.matchPrefix(\n mediaSection, 'a=fmtp:' + pt + ' ');\n // Only the first a=fmtp: is considered.\n codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};\n codec.rtcpFeedback = SDPUtils.matchPrefix(\n mediaSection, 'a=rtcp-fb:' + pt + ' ')\n .map(SDPUtils.parseRtcpFb);\n description.codecs.push(codec);\n // parse FEC mechanisms from rtpmap lines.\n switch (codec.name.toUpperCase()) {\n case 'RED':\n case 'ULPFEC':\n description.fecMechanisms.push(codec.name.toUpperCase());\n break;\n default: // only RED and ULPFEC are recognized as FEC mechanisms.\n break;\n }\n }\n }\n SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(line => {\n description.headerExtensions.push(SDPUtils.parseExtmap(line));\n });\n // FIXME: parse rtcp.\n return description;\n};\n\n// Generates parts of the SDP media section describing the capabilities /\n// parameters.\nSDPUtils.writeRtpDescription = function(kind, caps) {\n let sdp = '';\n\n // Build the mline.\n sdp += 'm=' + kind + ' ';\n sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.\n sdp += ' UDP/TLS/RTP/SAVPF ';\n sdp += caps.codecs.map(codec => {\n if (codec.preferredPayloadType !== undefined) {\n return codec.preferredPayloadType;\n }\n return codec.payloadType;\n }).join(' ') + '\\r\\n';\n\n sdp += 'c=IN IP4 0.0.0.0\\r\\n';\n sdp += 'a=rtcp:9 IN IP4 0.0.0.0\\r\\n';\n\n // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.\n caps.codecs.forEach(codec => {\n sdp += SDPUtils.writeRtpMap(codec);\n sdp += SDPUtils.writeFmtp(codec);\n sdp += SDPUtils.writeRtcpFb(codec);\n });\n let maxptime = 0;\n caps.codecs.forEach(codec => {\n if (codec.maxptime > maxptime) {\n maxptime = codec.maxptime;\n }\n });\n if (maxptime > 0) {\n sdp += 'a=maxptime:' + maxptime + '\\r\\n';\n }\n\n if (caps.headerExtensions) {\n caps.headerExtensions.forEach(extension => {\n sdp += SDPUtils.writeExtmap(extension);\n });\n }\n // FIXME: write fecMechanisms.\n return sdp;\n};\n\n// Parses the SDP media section and returns an array of\n// RTCRtpEncodingParameters.\nSDPUtils.parseRtpEncodingParameters = function(mediaSection) {\n const encodingParameters = [];\n const description = SDPUtils.parseRtpParameters(mediaSection);\n const hasRed = description.fecMechanisms.indexOf('RED') !== -1;\n const hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;\n\n // filter a=ssrc:... cname:, ignore PlanB-msid\n const ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(parts => parts.attribute === 'cname');\n const primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;\n let secondarySsrc;\n\n const flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')\n .map(line => {\n const parts = line.substr(17).split(' ');\n return parts.map(part => parseInt(part, 10));\n });\n if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {\n secondarySsrc = flows[0][1];\n }\n\n description.codecs.forEach(codec => {\n if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {\n let encParam = {\n ssrc: primarySsrc,\n codecPayloadType: parseInt(codec.parameters.apt, 10),\n };\n if (primarySsrc && secondarySsrc) {\n encParam.rtx = {ssrc: secondarySsrc};\n }\n encodingParameters.push(encParam);\n if (hasRed) {\n encParam = JSON.parse(JSON.stringify(encParam));\n encParam.fec = {\n ssrc: primarySsrc,\n mechanism: hasUlpfec ? 'red+ulpfec' : 'red',\n };\n encodingParameters.push(encParam);\n }\n }\n });\n if (encodingParameters.length === 0 && primarySsrc) {\n encodingParameters.push({\n ssrc: primarySsrc,\n });\n }\n\n // we support both b=AS and b=TIAS but interpret AS as TIAS.\n let bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');\n if (bandwidth.length) {\n if (bandwidth[0].indexOf('b=TIAS:') === 0) {\n bandwidth = parseInt(bandwidth[0].substr(7), 10);\n } else if (bandwidth[0].indexOf('b=AS:') === 0) {\n // use formula from JSEP to convert b=AS to TIAS value.\n bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95\n - (50 * 40 * 8);\n } else {\n bandwidth = undefined;\n }\n encodingParameters.forEach(params => {\n params.maxBitrate = bandwidth;\n });\n }\n return encodingParameters;\n};\n\n// parses http://draft.ortc.org/#rtcrtcpparameters*\nSDPUtils.parseRtcpParameters = function(mediaSection) {\n const rtcpParameters = {};\n\n // Gets the first SSRC. Note that with RTX there might be multiple\n // SSRCs.\n const remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(obj => obj.attribute === 'cname')[0];\n if (remoteSsrc) {\n rtcpParameters.cname = remoteSsrc.value;\n rtcpParameters.ssrc = remoteSsrc.ssrc;\n }\n\n // Edge uses the compound attribute instead of reducedSize\n // compound is !reducedSize\n const rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');\n rtcpParameters.reducedSize = rsize.length > 0;\n rtcpParameters.compound = rsize.length === 0;\n\n // parses the rtcp-mux attrŅ–bute.\n // Note that Edge does not support unmuxed RTCP.\n const mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');\n rtcpParameters.mux = mux.length > 0;\n\n return rtcpParameters;\n};\n\nSDPUtils.writeRtcpParameters = function(rtcpParameters) {\n let sdp = '';\n if (rtcpParameters.reducedSize) {\n sdp += 'a=rtcp-rsize\\r\\n';\n }\n if (rtcpParameters.mux) {\n sdp += 'a=rtcp-mux\\r\\n';\n }\n if (rtcpParameters.ssrc !== undefined && rtcpParameters.cname) {\n sdp += 'a=ssrc:' + rtcpParameters.ssrc +\n ' cname:' + rtcpParameters.cname + '\\r\\n';\n }\n return sdp;\n};\n\n\n// parses either a=msid: or a=ssrc:... msid lines and returns\n// the id of the MediaStream and MediaStreamTrack.\nSDPUtils.parseMsid = function(mediaSection) {\n let parts;\n const spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');\n if (spec.length === 1) {\n parts = spec[0].substr(7).split(' ');\n return {stream: parts[0], track: parts[1]};\n }\n const planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(msidParts => msidParts.attribute === 'msid');\n if (planB.length > 0) {\n parts = planB[0].value.split(' ');\n return {stream: parts[0], track: parts[1]};\n }\n};\n\n// SCTP\n// parses draft-ietf-mmusic-sctp-sdp-26 first and falls back\n// to draft-ietf-mmusic-sctp-sdp-05\nSDPUtils.parseSctpDescription = function(mediaSection) {\n const mline = SDPUtils.parseMLine(mediaSection);\n const maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:');\n let maxMessageSize;\n if (maxSizeLine.length > 0) {\n maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10);\n }\n if (isNaN(maxMessageSize)) {\n maxMessageSize = 65536;\n }\n const sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:');\n if (sctpPort.length > 0) {\n return {\n port: parseInt(sctpPort[0].substr(12), 10),\n protocol: mline.fmt,\n maxMessageSize,\n };\n }\n const sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:');\n if (sctpMapLines.length > 0) {\n const parts = sctpMapLines[0]\n .substr(10)\n .split(' ');\n return {\n port: parseInt(parts[0], 10),\n protocol: parts[1],\n maxMessageSize,\n };\n }\n};\n\n// SCTP\n// outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers\n// support by now receiving in this format, unless we originally parsed\n// as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line\n// protocol of DTLS/SCTP -- without UDP/ or TCP/)\nSDPUtils.writeSctpDescription = function(media, sctp) {\n let output = [];\n if (media.protocol !== 'DTLS/SCTP') {\n output = [\n 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\\r\\n',\n 'c=IN IP4 0.0.0.0\\r\\n',\n 'a=sctp-port:' + sctp.port + '\\r\\n',\n ];\n } else {\n output = [\n 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\\r\\n',\n 'c=IN IP4 0.0.0.0\\r\\n',\n 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\\r\\n',\n ];\n }\n if (sctp.maxMessageSize !== undefined) {\n output.push('a=max-message-size:' + sctp.maxMessageSize + '\\r\\n');\n }\n return output.join('');\n};\n\n// Generate a session ID for SDP.\n// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1\n// recommends using a cryptographically random +ve 64-bit value\n// but right now this should be acceptable and within the right range\nSDPUtils.generateSessionId = function() {\n return Math.random().toString().substr(2, 21);\n};\n\n// Write boiler plate for start of SDP\n// sessId argument is optional - if not supplied it will\n// be generated randomly\n// sessVersion is optional and defaults to 2\n// sessUser is optional and defaults to 'thisisadapterortc'\nSDPUtils.writeSessionBoilerplate = function(sessId, sessVer, sessUser) {\n let sessionId;\n const version = sessVer !== undefined ? sessVer : 2;\n if (sessId) {\n sessionId = sessId;\n } else {\n sessionId = SDPUtils.generateSessionId();\n }\n const user = sessUser || 'thisisadapterortc';\n // FIXME: sess-id should be an NTP timestamp.\n return 'v=0\\r\\n' +\n 'o=' + user + ' ' + sessionId + ' ' + version +\n ' IN IP4 127.0.0.1\\r\\n' +\n 's=-\\r\\n' +\n 't=0 0\\r\\n';\n};\n\n// Gets the direction from the mediaSection or the sessionpart.\nSDPUtils.getDirection = function(mediaSection, sessionpart) {\n // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.\n const lines = SDPUtils.splitLines(mediaSection);\n for (let i = 0; i < lines.length; i++) {\n switch (lines[i]) {\n case 'a=sendrecv':\n case 'a=sendonly':\n case 'a=recvonly':\n case 'a=inactive':\n return lines[i].substr(2);\n default:\n // FIXME: What should happen here?\n }\n }\n if (sessionpart) {\n return SDPUtils.getDirection(sessionpart);\n }\n return 'sendrecv';\n};\n\nSDPUtils.getKind = function(mediaSection) {\n const lines = SDPUtils.splitLines(mediaSection);\n const mline = lines[0].split(' ');\n return mline[0].substr(2);\n};\n\nSDPUtils.isRejected = function(mediaSection) {\n return mediaSection.split(' ', 2)[1] === '0';\n};\n\nSDPUtils.parseMLine = function(mediaSection) {\n const lines = SDPUtils.splitLines(mediaSection);\n const parts = lines[0].substr(2).split(' ');\n return {\n kind: parts[0],\n port: parseInt(parts[1], 10),\n protocol: parts[2],\n fmt: parts.slice(3).join(' '),\n };\n};\n\nSDPUtils.parseOLine = function(mediaSection) {\n const line = SDPUtils.matchPrefix(mediaSection, 'o=')[0];\n const parts = line.substr(2).split(' ');\n return {\n username: parts[0],\n sessionId: parts[1],\n sessionVersion: parseInt(parts[2], 10),\n netType: parts[3],\n addressType: parts[4],\n address: parts[5],\n };\n};\n\n// a very naive interpretation of a valid SDP.\nSDPUtils.isValidSDP = function(blob) {\n if (typeof blob !== 'string' || blob.length === 0) {\n return false;\n }\n const lines = SDPUtils.splitLines(blob);\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].length < 2 || lines[i].charAt(1) !== '=') {\n return false;\n }\n // TODO: check the modifier a bit more.\n }\n return true;\n};\n\n// Expose public methods.\nif (typeof module === 'object') {\n module.exports = SDPUtils;\n}\n"],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"naf-janus-adapter.min.js","mappings":"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","sources":["webpack://@networked-aframe/naf-janus-adapter/./node_modules/@networked-aframe/minijanus/minijanus.js","webpack://@networked-aframe/naf-janus-adapter/./src/index.js","webpack://@networked-aframe/naf-janus-adapter/./node_modules/sdp/sdp.js","webpack://@networked-aframe/naf-janus-adapter/webpack/bootstrap","webpack://@networked-aframe/naf-janus-adapter/webpack/startup"],"sourcesContent":["/**\n * Represents a handle to a single Janus plugin on a Janus session. Each WebRTC connection to the Janus server will be\n * associated with a single handle. Once attached to the server, this handle will be given a unique ID which should be\n * used to associate it with future signalling messages.\n *\n * See https://janus.conf.meetecho.com/docs/rest.html#handles.\n **/\nfunction JanusPluginHandle(session) {\n this.session = session;\n this.id = undefined;\n}\n\n/** Attaches this handle to the Janus server and sets its ID. **/\nJanusPluginHandle.prototype.attach = function(plugin, loop_index) {\n var payload = { plugin: plugin, loop_index: loop_index, \"force-bundle\": true, \"force-rtcp-mux\": true };\n return this.session.send(\"attach\", payload).then(resp => {\n this.id = resp.data.id;\n return resp;\n });\n};\n\n/** Detaches this handle. **/\nJanusPluginHandle.prototype.detach = function() {\n return this.send(\"detach\");\n};\n\n/** Registers a callback to be fired upon the reception of any incoming Janus signals for this plugin handle with the\n * `janus` attribute equal to `ev`.\n **/\nJanusPluginHandle.prototype.on = function(ev, callback) {\n return this.session.on(ev, signal => {\n if (signal.sender == this.id) {\n callback(signal);\n }\n });\n};\n\n/**\n * Sends a signal associated with this handle. Signals should be JSON-serializable objects. Returns a promise that will\n * be resolved or rejected when a response to this signal is received, or when no response is received within the\n * session timeout.\n **/\nJanusPluginHandle.prototype.send = function(type, signal) {\n return this.session.send(type, Object.assign({ handle_id: this.id }, signal));\n};\n\n/** Sends a plugin-specific message associated with this handle. **/\nJanusPluginHandle.prototype.sendMessage = function(body) {\n return this.send(\"message\", { body: body });\n};\n\n/** Sends a JSEP offer or answer associated with this handle. **/\nJanusPluginHandle.prototype.sendJsep = function(jsep) {\n return this.send(\"message\", { body: {}, jsep: jsep });\n};\n\n/** Sends an ICE trickle candidate associated with this handle. **/\nJanusPluginHandle.prototype.sendTrickle = function(candidate) {\n return this.send(\"trickle\", { candidate: candidate });\n};\n\n/**\n * Represents a Janus session -- a Janus context from within which you can open multiple handles and connections. Once\n * created, this session will be given a unique ID which should be used to associate it with future signalling messages.\n *\n * See https://janus.conf.meetecho.com/docs/rest.html#sessions.\n **/\nfunction JanusSession(output, options) {\n this.output = output;\n this.id = undefined;\n this.nextTxId = 0;\n this.txns = {};\n this.eventHandlers = {};\n this.options = Object.assign({\n verbose: false,\n timeoutMs: 10000,\n keepaliveMs: 30000\n }, options);\n}\n\n/** Creates this session on the Janus server and sets its ID. **/\nJanusSession.prototype.create = function() {\n return this.send(\"create\").then(resp => {\n this.id = resp.data.id;\n return resp;\n });\n};\n\n/**\n * Destroys this session. Note that upon destruction, Janus will also close the signalling transport (if applicable) and\n * any open WebRTC connections.\n **/\nJanusSession.prototype.destroy = function() {\n return this.send(\"destroy\").then((resp) => {\n this.dispose();\n return resp;\n });\n};\n\n/**\n * Disposes of this session in a way such that no further incoming signalling messages will be processed.\n * Outstanding transactions will be rejected.\n **/\nJanusSession.prototype.dispose = function() {\n this._killKeepalive();\n this.eventHandlers = {};\n for (var txId in this.txns) {\n if (this.txns.hasOwnProperty(txId)) {\n var txn = this.txns[txId];\n clearTimeout(txn.timeout);\n txn.reject(new Error(\"Janus session was disposed.\"));\n delete this.txns[txId];\n }\n }\n};\n\n/**\n * Whether this signal represents an error, and the associated promise (if any) should be rejected.\n * Users should override this to handle any custom plugin-specific error conventions.\n **/\nJanusSession.prototype.isError = function(signal) {\n return signal.janus === \"error\";\n};\n\n/** Registers a callback to be fired upon the reception of any incoming Janus signals for this session with the\n * `janus` attribute equal to `ev`.\n **/\nJanusSession.prototype.on = function(ev, callback) {\n var handlers = this.eventHandlers[ev];\n if (handlers == null) {\n handlers = this.eventHandlers[ev] = [];\n }\n handlers.push(callback);\n};\n\n/**\n * Callback for receiving JSON signalling messages pertinent to this session. If the signals are responses to previously\n * sent signals, the promises for the outgoing signals will be resolved or rejected appropriately with this signal as an\n * argument.\n *\n * External callers should call this function every time a new signal arrives on the transport; for example, in a\n * WebSocket's `message` event, or when a new datum shows up in an HTTP long-polling response.\n **/\nJanusSession.prototype.receive = function(signal) {\n if (this.options.verbose) {\n this._logIncoming(signal);\n }\n if (signal.session_id != this.id) {\n console.warn(\"Incorrect session ID received in Janus signalling message: was \" + signal.session_id + \", expected \" + this.id + \".\");\n }\n\n var responseType = signal.janus;\n var handlers = this.eventHandlers[responseType];\n if (handlers != null) {\n for (var i = 0; i < handlers.length; i++) {\n handlers[i](signal);\n }\n }\n\n if (signal.transaction != null) {\n var txn = this.txns[signal.transaction];\n if (txn == null) {\n // this is a response to a transaction that wasn't caused via JanusSession.send, or a plugin replied twice to a\n // single request, or the session was disposed, or something else that isn't under our purview; that's fine\n return;\n }\n\n if (responseType === \"ack\" && txn.type == \"message\") {\n // this is an ack of an asynchronously-processed plugin request, we should wait to resolve the promise until the\n // actual response comes in\n return;\n }\n\n clearTimeout(txn.timeout);\n\n delete this.txns[signal.transaction];\n (this.isError(signal) ? txn.reject : txn.resolve)(signal);\n }\n};\n\n/**\n * Sends a signal associated with this session, beginning a new transaction. Returns a promise that will be resolved or\n * rejected when a response is received in the same transaction, or when no response is received within the session\n * timeout.\n **/\nJanusSession.prototype.send = function(type, signal) {\n signal = Object.assign({ transaction: (this.nextTxId++).toString() }, signal);\n return new Promise((resolve, reject) => {\n var timeout = null;\n if (this.options.timeoutMs) {\n timeout = setTimeout(() => {\n delete this.txns[signal.transaction];\n reject(new Error(\"Signalling transaction with txid \" + signal.transaction + \" timed out.\"));\n }, this.options.timeoutMs);\n }\n this.txns[signal.transaction] = { resolve: resolve, reject: reject, timeout: timeout, type: type };\n this._transmit(type, signal);\n });\n};\n\nJanusSession.prototype._transmit = function(type, signal) {\n signal = Object.assign({ janus: type }, signal);\n\n if (this.id != null) { // this.id is undefined in the special case when we're sending the session create message\n signal = Object.assign({ session_id: this.id }, signal);\n }\n\n if (this.options.verbose) {\n this._logOutgoing(signal);\n }\n\n this.output(JSON.stringify(signal));\n this._resetKeepalive();\n};\n\nJanusSession.prototype._logOutgoing = function(signal) {\n var kind = signal.janus;\n if (kind === \"message\" && signal.jsep) {\n kind = signal.jsep.type;\n }\n var message = \"> Outgoing Janus \" + (kind || \"signal\") + \" (#\" + signal.transaction + \"): \";\n console.debug(\"%c\" + message, \"color: #040\", signal);\n};\n\nJanusSession.prototype._logIncoming = function(signal) {\n var kind = signal.janus;\n var message = signal.transaction ?\n \"< Incoming Janus \" + (kind || \"signal\") + \" (#\" + signal.transaction + \"): \" :\n \"< Incoming Janus \" + (kind || \"signal\") + \": \";\n console.debug(\"%c\" + message, \"color: #004\", signal);\n};\n\nJanusSession.prototype._sendKeepalive = function() {\n return this.send(\"keepalive\");\n};\n\nJanusSession.prototype._killKeepalive = function() {\n clearTimeout(this.keepaliveTimeout);\n};\n\nJanusSession.prototype._resetKeepalive = function() {\n this._killKeepalive();\n if (this.options.keepaliveMs) {\n this.keepaliveTimeout = setTimeout(() => {\n this._sendKeepalive().catch(e => console.error(\"Error received from keepalive: \", e));\n }, this.options.keepaliveMs);\n }\n};\n\nmodule.exports = {\n JanusPluginHandle,\n JanusSession\n};\n","/* global NAF */\nvar mj = require(\"@networked-aframe/minijanus\");\nmj.JanusSession.prototype.sendOriginal = mj.JanusSession.prototype.send;\nmj.JanusSession.prototype.send = function(type, signal) {\n return this.sendOriginal(type, signal).catch((e) => {\n if (e.message && e.message.indexOf(\"timed out\") > -1) {\n console.error(\"web socket timed out\");\n NAF.connection.adapter.reconnect();\n } else {\n throw(e);\n }\n });\n}\n\nvar sdpUtils = require(\"sdp\");\n//var debug = require(\"debug\")(\"naf-janus-adapter:debug\");\n//var warn = require(\"debug\")(\"naf-janus-adapter:warn\");\n//var error = require(\"debug\")(\"naf-janus-adapter:error\");\nvar debug = console.log;\nvar warn = console.warn;\nvar error = console.error;\nvar isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);\n\nconst SUBSCRIBE_TIMEOUT_MS = 15000;\n\nfunction debounce(fn) {\n var curr = Promise.resolve();\n return function() {\n var args = Array.prototype.slice.call(arguments);\n curr = curr.then(_ => fn.apply(this, args));\n };\n}\n\nfunction randomUint() {\n return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);\n}\n\nfunction untilDataChannelOpen(dataChannel) {\n return new Promise((resolve, reject) => {\n if (dataChannel.readyState === \"open\") {\n resolve();\n } else {\n let resolver, rejector;\n\n const clear = () => {\n dataChannel.removeEventListener(\"open\", resolver);\n dataChannel.removeEventListener(\"error\", rejector);\n };\n\n resolver = () => {\n clear();\n resolve();\n };\n rejector = () => {\n clear();\n reject();\n };\n\n dataChannel.addEventListener(\"open\", resolver);\n dataChannel.addEventListener(\"error\", rejector);\n }\n });\n}\n\nconst isH264VideoSupported = (() => {\n const video = document.createElement(\"video\");\n return video.canPlayType('video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"') !== \"\";\n})();\n\nconst OPUS_PARAMETERS = {\n // indicates that we want to enable DTX to elide silence packets\n usedtx: 1,\n // indicates that we prefer to receive mono audio (important for voip profile)\n stereo: 0,\n // indicates that we prefer to send mono audio (important for voip profile)\n \"sprop-stereo\": 0\n};\n\nconst DEFAULT_PEER_CONNECTION_CONFIG = {\n iceServers: [{ urls: \"stun:stun1.l.google.com:19302\" }, { urls: \"stun:stun2.l.google.com:19302\" }]\n};\n\nconst WS_NORMAL_CLOSURE = 1000;\n\nclass JanusAdapter {\n constructor() {\n this.room = null;\n // We expect the consumer to set a client id before connecting.\n this.clientId = null;\n this.joinToken = null;\n\n this.serverUrl = null;\n this.webRtcOptions = {};\n this.peerConnectionConfig = null;\n this.ws = null;\n this.session = null;\n this.reliableTransport = \"datachannel\";\n this.unreliableTransport = \"datachannel\";\n\n // In the event the server restarts and all clients lose connection, reconnect with\n // some random jitter added to prevent simultaneous reconnection requests.\n this.initialReconnectionDelay = 1000 * Math.random();\n this.reconnectionDelay = this.initialReconnectionDelay;\n this.reconnectionTimeout = null;\n this.maxReconnectionAttempts = 10;\n this.reconnectionAttempts = 0;\n\n this.publisher = null;\n this.occupants = {};\n this.leftOccupants = new Set();\n this.mediaStreams = {};\n this.localMediaStream = null;\n this.pendingMediaRequests = new Map();\n\n this.blockedClients = new Map();\n this.frozenUpdates = new Map();\n\n this.timeOffsets = [];\n this.serverTimeRequests = 0;\n this.avgTimeOffset = 0;\n\n this.onWebsocketOpen = this.onWebsocketOpen.bind(this);\n this.onWebsocketClose = this.onWebsocketClose.bind(this);\n this.onWebsocketMessage = this.onWebsocketMessage.bind(this);\n this.onDataChannelMessage = this.onDataChannelMessage.bind(this);\n this.onData = this.onData.bind(this);\n }\n\n setServerUrl(url) {\n this.serverUrl = url;\n }\n\n setApp(app) {}\n\n setRoom(roomName) {\n this.room = roomName;\n }\n\n setJoinToken(joinToken) {\n this.joinToken = joinToken;\n }\n\n setClientId(clientId) {\n this.clientId = clientId;\n }\n\n setWebRtcOptions(options) {\n this.webRtcOptions = options;\n }\n\n setPeerConnectionConfig(peerConnectionConfig) {\n this.peerConnectionConfig = peerConnectionConfig;\n }\n\n setServerConnectListeners(successListener, failureListener) {\n this.connectSuccess = successListener;\n this.connectFailure = failureListener;\n }\n\n setRoomOccupantListener(occupantListener) {\n this.onOccupantsChanged = occupantListener;\n }\n\n setDataChannelListeners(openListener, closedListener, messageListener) {\n this.onOccupantConnected = openListener;\n this.onOccupantDisconnected = closedListener;\n this.onOccupantMessage = messageListener;\n }\n\n setReconnectionListeners(reconnectingListener, reconnectedListener, reconnectionErrorListener) {\n // onReconnecting is called with the number of milliseconds until the next reconnection attempt\n this.onReconnecting = reconnectingListener;\n // onReconnected is called when the connection has been reestablished\n this.onReconnected = reconnectedListener;\n // onReconnectionError is called with an error when maxReconnectionAttempts has been reached\n this.onReconnectionError = reconnectionErrorListener;\n }\n\n setEventLoops(loops) {\n this.loops = loops;\n }\n\n connect() {\n debug(`connecting to ${this.serverUrl}`);\n\n const websocketConnection = new Promise((resolve, reject) => {\n this.ws = new WebSocket(this.serverUrl, \"janus-protocol\");\n\n this.session = new mj.JanusSession(this.ws.send.bind(this.ws), { timeoutMs: 40000 });\n\n this.ws.addEventListener(\"close\", this.onWebsocketClose);\n this.ws.addEventListener(\"message\", this.onWebsocketMessage);\n\n this.wsOnOpen = () => {\n this.ws.removeEventListener(\"open\", this.wsOnOpen);\n this.onWebsocketOpen()\n .then(resolve)\n .catch(reject);\n };\n\n this.ws.addEventListener(\"open\", this.wsOnOpen);\n });\n\n return Promise.all([websocketConnection, this.updateTimeOffset()]);\n }\n\n disconnect() {\n debug(`disconnecting`);\n\n clearTimeout(this.reconnectionTimeout);\n\n this.removeAllOccupants();\n this.leftOccupants = new Set();\n\n if (this.publisher) {\n // Close the publisher peer connection. Which also detaches the plugin handle.\n this.publisher.conn.close();\n this.publisher = null;\n }\n\n if (this.session) {\n this.session.dispose();\n this.session = null;\n }\n\n if (this.ws) {\n this.ws.removeEventListener(\"open\", this.wsOnOpen);\n this.ws.removeEventListener(\"close\", this.onWebsocketClose);\n this.ws.removeEventListener(\"message\", this.onWebsocketMessage);\n this.ws.close();\n this.ws = null;\n }\n\n // Now that all RTCPeerConnection closed, be sure to not call\n // reconnect() again via performDelayedReconnect if previous\n // RTCPeerConnection was in the failed state.\n if (this.delayedReconnectTimeout) {\n clearTimeout(this.delayedReconnectTimeout);\n this.delayedReconnectTimeout = null;\n }\n }\n\n isDisconnected() {\n return this.ws === null;\n }\n\n async onWebsocketOpen() {\n // Create the Janus Session\n await this.session.create();\n\n // Attach the SFU Plugin and create a RTCPeerConnection for the publisher.\n // The publisher sends audio and opens two bidirectional data channels.\n // One reliable datachannel and one unreliable.\n this.publisher = await this.createPublisher();\n\n // Call the naf connectSuccess callback before we start receiving WebRTC messages.\n this.connectSuccess(this.clientId);\n\n const addOccupantPromises = [];\n\n for (let i = 0; i < this.publisher.initialOccupants.length; i++) {\n const occupantId = this.publisher.initialOccupants[i];\n if (occupantId === this.clientId) continue; // Happens during non-graceful reconnects due to zombie sessions\n addOccupantPromises.push(this.addOccupant(occupantId));\n }\n\n await Promise.all(addOccupantPromises);\n }\n\n onWebsocketClose(event) {\n // The connection was closed successfully. Don't try to reconnect.\n if (event.code === WS_NORMAL_CLOSURE) {\n return;\n }\n\n console.warn(\"Janus websocket closed unexpectedly.\");\n if (this.onReconnecting) {\n this.onReconnecting(this.reconnectionDelay);\n }\n\n this.reconnectionTimeout = setTimeout(() => this.reconnect(), this.reconnectionDelay);\n }\n\n reconnect() {\n // Dispose of all networked entities and other resources tied to the session.\n this.disconnect();\n\n this.connect()\n .then(() => {\n this.reconnectionDelay = this.initialReconnectionDelay;\n this.reconnectionAttempts = 0;\n\n if (this.onReconnected) {\n this.onReconnected();\n }\n })\n .catch(error => {\n this.reconnectionDelay += 1000;\n this.reconnectionAttempts++;\n\n if (this.reconnectionAttempts > this.maxReconnectionAttempts && this.onReconnectionError) {\n return this.onReconnectionError(\n new Error(\"Connection could not be reestablished, exceeded maximum number of reconnection attempts.\")\n );\n }\n\n console.warn(\"Error during reconnect, retrying.\");\n console.warn(error);\n\n if (this.onReconnecting) {\n this.onReconnecting(this.reconnectionDelay);\n }\n\n this.reconnectionTimeout = setTimeout(() => this.reconnect(), this.reconnectionDelay);\n });\n }\n\n performDelayedReconnect() {\n if (this.delayedReconnectTimeout) {\n clearTimeout(this.delayedReconnectTimeout);\n }\n\n this.delayedReconnectTimeout = setTimeout(() => {\n this.delayedReconnectTimeout = null;\n this.reconnect();\n }, 10000);\n }\n\n onWebsocketMessage(event) {\n this.session.receive(JSON.parse(event.data));\n }\n\n async addOccupant(occupantId) {\n if (this.occupants[occupantId]) {\n this.removeOccupant(occupantId);\n }\n\n this.leftOccupants.delete(occupantId);\n\n var subscriber = await this.createSubscriber(occupantId);\n\n if (!subscriber) return;\n\n this.occupants[occupantId] = subscriber;\n\n this.setMediaStream(occupantId, subscriber.mediaStream);\n\n // Call the Networked AFrame callbacks for the new occupant.\n this.onOccupantConnected(occupantId);\n this.onOccupantsChanged(this.occupants);\n\n return subscriber;\n }\n\n removeAllOccupants() {\n for (const occupantId of Object.getOwnPropertyNames(this.occupants)) {\n this.removeOccupant(occupantId);\n }\n }\n\n removeOccupant(occupantId) {\n this.leftOccupants.add(occupantId);\n\n if (this.occupants[occupantId]) {\n // Close the subscriber peer connection. Which also detaches the plugin handle.\n this.occupants[occupantId].conn.close();\n delete this.occupants[occupantId];\n }\n\n if (this.mediaStreams[occupantId]) {\n delete this.mediaStreams[occupantId];\n }\n\n if (this.pendingMediaRequests.has(occupantId)) {\n const msg = \"The user disconnected before the media stream was resolved.\";\n this.pendingMediaRequests.get(occupantId).audio.reject(msg);\n this.pendingMediaRequests.get(occupantId).video.reject(msg);\n this.pendingMediaRequests.delete(occupantId);\n }\n\n // Call the Networked AFrame callbacks for the removed occupant.\n this.onOccupantDisconnected(occupantId);\n this.onOccupantsChanged(this.occupants);\n }\n\n associate(conn, handle) {\n conn.addEventListener(\"icecandidate\", ev => {\n handle.sendTrickle(ev.candidate || null).catch(e => error(\"Error trickling ICE: %o\", e));\n });\n conn.addEventListener(\"iceconnectionstatechange\", ev => {\n if (conn.iceConnectionState === \"connected\") {\n console.log(\"ICE state changed to connected\");\n }\n if (conn.iceConnectionState === \"disconnected\") {\n console.warn(\"ICE state changed to disconnected\");\n }\n if (conn.iceConnectionState === \"failed\") {\n console.warn(\"ICE failure detected. Reconnecting in 10s.\");\n this.performDelayedReconnect();\n }\n })\n\n // we have to debounce these because janus gets angry if you send it a new SDP before\n // it's finished processing an existing SDP. in actuality, it seems like this is maybe\n // too liberal and we need to wait some amount of time after an offer before sending another,\n // but we don't currently know any good way of detecting exactly how long :(\n conn.addEventListener(\n \"negotiationneeded\",\n debounce(ev => {\n debug(\"Sending new offer for handle: %o\", handle);\n var offer = conn.createOffer().then(this.configurePublisherSdp).then(this.fixSafariIceUFrag);\n var local = offer.then(o => conn.setLocalDescription(o));\n var remote = offer;\n\n remote = remote\n .then(this.fixSafariIceUFrag)\n .then(j => handle.sendJsep(j))\n .then(r => conn.setRemoteDescription(r.jsep));\n return Promise.all([local, remote]).catch(e => error(\"Error negotiating offer: %o\", e));\n })\n );\n handle.on(\n \"event\",\n debounce(ev => {\n var jsep = ev.jsep;\n if (jsep && jsep.type == \"offer\") {\n debug(\"Accepting new offer for handle: %o\", handle);\n var answer = conn\n .setRemoteDescription(this.configureSubscriberSdp(jsep))\n .then(_ => conn.createAnswer())\n .then(this.fixSafariIceUFrag);\n var local = answer.then(a => conn.setLocalDescription(a));\n var remote = answer.then(j => handle.sendJsep(j));\n return Promise.all([local, remote]).catch(e => error(\"Error negotiating answer: %o\", e));\n } else {\n // some other kind of event, nothing to do\n return null;\n }\n })\n );\n }\n\n async createPublisher() {\n var handle = new mj.JanusPluginHandle(this.session);\n var conn = new RTCPeerConnection(this.peerConnectionConfig || DEFAULT_PEER_CONNECTION_CONFIG);\n\n debug(\"pub waiting for sfu\");\n await handle.attach(\"janus.plugin.sfu\", this.loops && this.clientId ? parseInt(this.clientId) % this.loops : undefined);\n\n this.associate(conn, handle);\n\n debug(\"pub waiting for data channels & webrtcup\");\n var webrtcup = new Promise(resolve => handle.on(\"webrtcup\", resolve));\n\n // Unreliable datachannel: sending and receiving component updates.\n // Reliable datachannel: sending and recieving entity instantiations.\n var reliableChannel = conn.createDataChannel(\"reliable\", { ordered: true });\n var unreliableChannel = conn.createDataChannel(\"unreliable\", {\n ordered: false,\n maxRetransmits: 0\n });\n\n reliableChannel.addEventListener(\"message\", e => this.onDataChannelMessage(e, \"janus-reliable\"));\n unreliableChannel.addEventListener(\"message\", e => this.onDataChannelMessage(e, \"janus-unreliable\"));\n\n await webrtcup;\n await untilDataChannelOpen(reliableChannel);\n await untilDataChannelOpen(unreliableChannel);\n\n // doing this here is sort of a hack around chrome renegotiation weirdness --\n // if we do it prior to webrtcup, chrome on gear VR will sometimes put a\n // renegotiation offer in flight while the first offer was still being\n // processed by janus. we should find some more principled way to figure out\n // when janus is done in the future.\n if (this.localMediaStream) {\n this.localMediaStream.getTracks().forEach(track => {\n conn.addTrack(track, this.localMediaStream);\n });\n }\n\n // Handle all of the join and leave events.\n handle.on(\"event\", ev => {\n var data = ev.plugindata.data;\n if (data.event == \"join\" && data.room_id == this.room) {\n if (this.delayedReconnectTimeout) {\n // Don't create a new RTCPeerConnection, all RTCPeerConnection will be closed in less than 10s.\n return;\n }\n this.addOccupant(data.user_id);\n } else if (data.event == \"leave\" && data.room_id == this.room) {\n this.removeOccupant(data.user_id);\n } else if (data.event == \"blocked\") {\n document.body.dispatchEvent(new CustomEvent(\"blocked\", { detail: { clientId: data.by } }));\n } else if (data.event == \"unblocked\") {\n document.body.dispatchEvent(new CustomEvent(\"unblocked\", { detail: { clientId: data.by } }));\n } else if (data.event === \"data\") {\n this.onData(JSON.parse(data.body), \"janus-event\");\n }\n });\n\n debug(\"pub waiting for join\");\n\n // Send join message to janus. Listen for join/leave messages. Automatically subscribe to all users' WebRTC data.\n var message = await this.sendJoin(handle, {\n notifications: true,\n data: true\n });\n\n if (!message.plugindata.data.success) {\n const err = message.plugindata.data.error;\n console.error(err);\n // We may get here because of an expired JWT.\n // Close the connection ourself otherwise janus will close it after\n // session_timeout because we didn't send any keepalive and this will\n // trigger a delayed reconnect because of the iceconnectionstatechange\n // listener for failure state.\n // Even if the app code calls disconnect in case of error, disconnect\n // won't close the peer connection because this.publisher is not set.\n conn.close();\n throw err;\n }\n\n var initialOccupants = message.plugindata.data.response.users[this.room] || [];\n\n if (initialOccupants.includes(this.clientId)) {\n console.warn(\"Janus still has previous session for this client. Reconnecting in 10s.\");\n this.performDelayedReconnect();\n }\n\n debug(\"publisher ready\");\n return {\n handle,\n initialOccupants,\n reliableChannel,\n unreliableChannel,\n conn\n };\n }\n\n configurePublisherSdp(jsep) {\n jsep.sdp = jsep.sdp.replace(/a=fmtp:(109|111).*\\r\\n/g, (line, pt) => {\n const parameters = Object.assign(sdpUtils.parseFmtp(line), OPUS_PARAMETERS);\n return sdpUtils.writeFmtp({ payloadType: pt, parameters: parameters });\n });\n return jsep;\n }\n\n configureSubscriberSdp(jsep) {\n // todo: consider cleaning up these hacks to use sdputils\n if (!isH264VideoSupported) {\n if (navigator.userAgent.indexOf(\"HeadlessChrome\") !== -1) {\n // HeadlessChrome (e.g. puppeteer) doesn't support webrtc video streams, so we remove those lines from the SDP.\n jsep.sdp = jsep.sdp.replace(/m=video[^]*m=/, \"m=\");\n }\n }\n\n // TODO: Hack to get video working on Chrome for Android. https://groups.google.com/forum/#!topic/mozilla.dev.media/Ye29vuMTpo8\n if (navigator.userAgent.indexOf(\"Android\") === -1) {\n jsep.sdp = jsep.sdp.replace(\n \"a=rtcp-fb:107 goog-remb\\r\\n\",\n \"a=rtcp-fb:107 goog-remb\\r\\na=rtcp-fb:107 transport-cc\\r\\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\\r\\n\"\n );\n } else {\n jsep.sdp = jsep.sdp.replace(\n \"a=rtcp-fb:107 goog-remb\\r\\n\",\n \"a=rtcp-fb:107 goog-remb\\r\\na=rtcp-fb:107 transport-cc\\r\\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f\\r\\n\"\n );\n }\n return jsep;\n }\n\n async fixSafariIceUFrag(jsep) {\n // Safari produces a \\n instead of an \\r\\n for the ice-ufrag. See https://github.com/meetecho/janus-gateway/issues/1818\n jsep.sdp = jsep.sdp.replace(/[^\\r]\\na=ice-ufrag/g, \"\\r\\na=ice-ufrag\");\n return jsep\n }\n\n async createSubscriber(occupantId, maxRetries = 5) {\n if (this.leftOccupants.has(occupantId)) {\n console.warn(occupantId + \": cancelled occupant connection, occupant left before subscription negotation.\");\n return null;\n }\n\n var handle = new mj.JanusPluginHandle(this.session);\n var conn = new RTCPeerConnection(this.peerConnectionConfig || DEFAULT_PEER_CONNECTION_CONFIG);\n\n debug(occupantId + \": sub waiting for sfu\");\n await handle.attach(\"janus.plugin.sfu\", this.loops ? parseInt(occupantId) % this.loops : undefined);\n\n this.associate(conn, handle);\n\n debug(occupantId + \": sub waiting for join\");\n\n if (this.leftOccupants.has(occupantId)) {\n conn.close();\n console.warn(occupantId + \": cancelled occupant connection, occupant left after attach\");\n return null;\n }\n\n let webrtcFailed = false;\n\n const webrtcup = new Promise(resolve => {\n const leftInterval = setInterval(() => {\n if (this.leftOccupants.has(occupantId)) {\n clearInterval(leftInterval);\n resolve();\n }\n }, 1000);\n\n const timeout = setTimeout(() => {\n clearInterval(leftInterval);\n webrtcFailed = true;\n resolve();\n }, SUBSCRIBE_TIMEOUT_MS);\n\n handle.on(\"webrtcup\", () => {\n clearTimeout(timeout);\n clearInterval(leftInterval);\n resolve();\n });\n });\n\n // Send join message to janus. Don't listen for join/leave messages. Subscribe to the occupant's media.\n // Janus should send us an offer for this occupant's media in response to this.\n await this.sendJoin(handle, { media: occupantId });\n\n if (this.leftOccupants.has(occupantId)) {\n conn.close();\n console.warn(occupantId + \": cancelled occupant connection, occupant left after join\");\n return null;\n }\n\n debug(occupantId + \": sub waiting for webrtcup\");\n await webrtcup;\n\n if (this.leftOccupants.has(occupantId)) {\n conn.close();\n console.warn(occupantId + \": cancel occupant connection, occupant left during or after webrtcup\");\n return null;\n }\n\n if (webrtcFailed) {\n conn.close();\n if (maxRetries > 0) {\n console.warn(occupantId + \": webrtc up timed out, retrying\");\n return this.createSubscriber(occupantId, maxRetries - 1);\n } else {\n console.warn(occupantId + \": webrtc up timed out\");\n return null;\n }\n }\n\n if (isSafari && !this._iOSHackDelayedInitialPeer) {\n // HACK: the first peer on Safari during page load can fail to work if we don't\n // wait some time before continuing here. See: https://github.com/mozilla/hubs/pull/1692\n await (new Promise((resolve) => setTimeout(resolve, 3000)));\n this._iOSHackDelayedInitialPeer = true;\n }\n\n var mediaStream = new MediaStream();\n var receivers = conn.getReceivers();\n receivers.forEach(receiver => {\n if (receiver.track) {\n mediaStream.addTrack(receiver.track);\n }\n });\n if (mediaStream.getTracks().length === 0) {\n mediaStream = null;\n }\n\n debug(occupantId + \": subscriber ready\");\n return {\n handle,\n mediaStream,\n conn\n };\n }\n\n sendJoin(handle, subscribe) {\n return handle.sendMessage({\n kind: \"join\",\n room_id: this.room,\n user_id: this.clientId,\n subscribe,\n token: this.joinToken\n });\n }\n\n toggleFreeze() {\n if (this.frozen) {\n this.unfreeze();\n } else {\n this.freeze();\n }\n }\n\n freeze() {\n this.frozen = true;\n }\n\n unfreeze() {\n this.frozen = false;\n this.flushPendingUpdates();\n }\n\n dataForUpdateMultiMessage(networkId, message) {\n // \"d\" is an array of entity datas, where each item in the array represents a unique entity and contains\n // metadata for the entity, and an array of components that have been updated on the entity.\n // This method finds the data corresponding to the given networkId.\n for (let i = 0, l = message.data.d.length; i < l; i++) {\n const data = message.data.d[i];\n\n if (data.networkId === networkId) {\n return data;\n }\n }\n\n return null;\n }\n\n getPendingData(networkId, message) {\n if (!message) return null;\n\n let data = message.dataType === \"um\" ? this.dataForUpdateMultiMessage(networkId, message) : message.data;\n\n // Ignore messages relating to users who have disconnected since freezing, their entities\n // will have aleady been removed by NAF.\n // Note that delete messages have no \"owner\" so we have to check for that as well.\n if (data.owner && !this.occupants[data.owner]) return null;\n\n // Ignore messages from users that we may have blocked while frozen.\n if (data.owner && this.blockedClients.has(data.owner)) return null;\n\n return data\n }\n\n // Used externally\n getPendingDataForNetworkId(networkId) {\n return this.getPendingData(networkId, this.frozenUpdates.get(networkId));\n }\n\n flushPendingUpdates() {\n for (const [networkId, message] of this.frozenUpdates) {\n let data = this.getPendingData(networkId, message);\n if (!data) continue;\n\n // Override the data type on \"um\" messages types, since we extract entity updates from \"um\" messages into\n // individual frozenUpdates in storeSingleMessage.\n const dataType = message.dataType === \"um\" ? \"u\" : message.dataType;\n\n this.onOccupantMessage(null, dataType, data, message.source);\n }\n this.frozenUpdates.clear();\n }\n\n storeMessage(message) {\n if (message.dataType === \"um\") { // UpdateMulti\n for (let i = 0, l = message.data.d.length; i < l; i++) {\n this.storeSingleMessage(message, i);\n }\n } else {\n this.storeSingleMessage(message);\n }\n }\n\n storeSingleMessage(message, index) {\n const data = index !== undefined ? message.data.d[index] : message.data;\n const dataType = message.dataType;\n const source = message.source;\n\n const networkId = data.networkId;\n\n if (!this.frozenUpdates.has(networkId)) {\n this.frozenUpdates.set(networkId, message);\n } else {\n const storedMessage = this.frozenUpdates.get(networkId);\n const storedData = storedMessage.dataType === \"um\" ? this.dataForUpdateMultiMessage(networkId, storedMessage) : storedMessage.data;\n\n // Avoid updating components if the entity data received did not come from the current owner.\n const isOutdatedMessage = data.lastOwnerTime < storedData.lastOwnerTime;\n const isContemporaneousMessage = data.lastOwnerTime === storedData.lastOwnerTime;\n if (isOutdatedMessage || (isContemporaneousMessage && storedData.owner > data.owner)) {\n return;\n }\n\n if (dataType === \"r\") {\n const createdWhileFrozen = storedData && storedData.isFirstSync;\n if (createdWhileFrozen) {\n // If the entity was created and deleted while frozen, don't bother conveying anything to the consumer.\n this.frozenUpdates.delete(networkId);\n } else {\n // Delete messages override any other messages for this entity\n this.frozenUpdates.set(networkId, message);\n }\n } else {\n // merge in component updates\n if (storedData.components && data.components) {\n Object.assign(storedData.components, data.components);\n }\n }\n }\n }\n\n onDataChannelMessage(e, source) {\n this.onData(JSON.parse(e.data), source);\n }\n\n onData(message, source) {\n if (debug.enabled) {\n debug(`DC in: ${message}`);\n }\n\n if (!message.dataType) return;\n\n message.source = source;\n\n if (this.frozen) {\n this.storeMessage(message);\n } else {\n this.onOccupantMessage(null, message.dataType, message.data, message.source);\n }\n }\n\n shouldStartConnectionTo(client) {\n return true;\n }\n\n startStreamConnection(client) {}\n\n closeStreamConnection(client) {}\n\n getConnectStatus(clientId) {\n return this.occupants[clientId] ? NAF.adapters.IS_CONNECTED : NAF.adapters.NOT_CONNECTED;\n }\n\n async updateTimeOffset() {\n if (this.isDisconnected()) return;\n\n const clientSentTime = Date.now();\n\n const res = await fetch(document.location.href, {\n method: \"HEAD\",\n cache: \"no-cache\"\n });\n\n const precision = 1000;\n const serverReceivedTime = new Date(res.headers.get(\"Date\")).getTime() + precision / 2;\n const clientReceivedTime = Date.now();\n const serverTime = serverReceivedTime + (clientReceivedTime - clientSentTime) / 2;\n const timeOffset = serverTime - clientReceivedTime;\n\n this.serverTimeRequests++;\n\n if (this.serverTimeRequests <= 10) {\n this.timeOffsets.push(timeOffset);\n } else {\n this.timeOffsets[this.serverTimeRequests % 10] = timeOffset;\n }\n\n this.avgTimeOffset = this.timeOffsets.reduce((acc, offset) => (acc += offset), 0) / this.timeOffsets.length;\n\n if (this.serverTimeRequests > 10) {\n debug(`new server time offset: ${this.avgTimeOffset}ms`);\n setTimeout(() => this.updateTimeOffset(), 5 * 60 * 1000); // Sync clock every 5 minutes.\n } else {\n this.updateTimeOffset();\n }\n }\n\n getServerTime() {\n return Date.now() + this.avgTimeOffset;\n }\n\n getMediaStream(clientId, type = \"audio\") {\n if (this.mediaStreams[clientId]) {\n debug(`Already had ${type} for ${clientId}`);\n return Promise.resolve(this.mediaStreams[clientId][type]);\n } else {\n debug(`Waiting on ${type} for ${clientId}`);\n if (!this.pendingMediaRequests.has(clientId)) {\n this.pendingMediaRequests.set(clientId, {});\n\n const audioPromise = new Promise((resolve, reject) => {\n this.pendingMediaRequests.get(clientId).audio = { resolve, reject };\n });\n const videoPromise = new Promise((resolve, reject) => {\n this.pendingMediaRequests.get(clientId).video = { resolve, reject };\n });\n\n this.pendingMediaRequests.get(clientId).audio.promise = audioPromise;\n this.pendingMediaRequests.get(clientId).video.promise = videoPromise;\n\n audioPromise.catch(e => console.warn(`${clientId} getMediaStream Audio Error`, e));\n videoPromise.catch(e => console.warn(`${clientId} getMediaStream Video Error`, e));\n }\n return this.pendingMediaRequests.get(clientId)[type].promise;\n }\n }\n\n setMediaStream(clientId, stream) {\n // Safari doesn't like it when you use single a mixed media stream where one of the tracks is inactive, so we\n // split the tracks into two streams.\n const audioStream = new MediaStream();\n try {\n stream.getAudioTracks().forEach(track => audioStream.addTrack(track));\n\n } catch(e) {\n console.warn(`${clientId} setMediaStream Audio Error`, e);\n }\n const videoStream = new MediaStream();\n try {\n stream.getVideoTracks().forEach(track => videoStream.addTrack(track));\n\n } catch (e) {\n console.warn(`${clientId} setMediaStream Video Error`, e);\n }\n\n this.mediaStreams[clientId] = { audio: audioStream, video: videoStream };\n\n // Resolve the promise for the user's media stream if it exists.\n if (this.pendingMediaRequests.has(clientId)) {\n this.pendingMediaRequests.get(clientId).audio.resolve(audioStream);\n this.pendingMediaRequests.get(clientId).video.resolve(videoStream);\n }\n }\n\n async setLocalMediaStream(stream) {\n // our job here is to make sure the connection winds up with RTP senders sending the stuff in this stream,\n // and not the stuff that isn't in this stream. strategy is to replace existing tracks if we can, add tracks\n // that we can't replace, and disable tracks that don't exist anymore.\n\n // note that we don't ever remove a track from the stream -- since Janus doesn't support Unified Plan, we absolutely\n // can't wind up with a SDP that has >1 audio or >1 video tracks, even if one of them is inactive (what you get if\n // you remove a track from an existing stream.)\n if (this.publisher && this.publisher.conn) {\n const existingSenders = this.publisher.conn.getSenders();\n const newSenders = [];\n const tracks = stream.getTracks();\n\n for (let i = 0; i < tracks.length; i++) {\n const t = tracks[i];\n const sender = existingSenders.find(s => s.track != null && s.track.kind == t.kind);\n\n if (sender != null) {\n if (sender.replaceTrack) {\n await sender.replaceTrack(t);\n\n // Workaround https://bugzilla.mozilla.org/show_bug.cgi?id=1576771\n if (t.kind === \"video\" && t.enabled && navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {\n t.enabled = false;\n setTimeout(() => t.enabled = true, 1000);\n }\n } else {\n // Fallback for browsers that don't support replaceTrack. At this time of this writing\n // most browsers support it, and testing this code path seems to not work properly\n // in Chrome anymore.\n stream.removeTrack(sender.track);\n stream.addTrack(t);\n }\n newSenders.push(sender);\n } else {\n newSenders.push(this.publisher.conn.addTrack(t, stream));\n }\n }\n existingSenders.forEach(s => {\n if (!newSenders.includes(s)) {\n s.track.enabled = false;\n }\n });\n }\n this.localMediaStream = stream;\n this.setMediaStream(this.clientId, stream);\n }\n\n enableMicrophone(enabled) {\n if (this.publisher && this.publisher.conn) {\n this.publisher.conn.getSenders().forEach(s => {\n if (s.track.kind == \"audio\") {\n s.track.enabled = enabled;\n }\n });\n }\n }\n\n sendData(clientId, dataType, data) {\n if (!this.publisher) {\n console.warn(\"sendData called without a publisher\");\n } else {\n switch (this.unreliableTransport) {\n case \"websocket\":\n this.publisher.handle.sendMessage({ kind: \"data\", body: JSON.stringify({ dataType, data }), whom: clientId });\n break;\n case \"datachannel\":\n this.publisher.unreliableChannel.send(JSON.stringify({ clientId, dataType, data }));\n break;\n default:\n this.unreliableTransport(clientId, dataType, data);\n break;\n }\n }\n }\n\n sendDataGuaranteed(clientId, dataType, data) {\n if (!this.publisher) {\n console.warn(\"sendDataGuaranteed called without a publisher\");\n } else {\n switch (this.reliableTransport) {\n case \"websocket\":\n this.publisher.handle.sendMessage({ kind: \"data\", body: JSON.stringify({ dataType, data }), whom: clientId });\n break;\n case \"datachannel\":\n this.publisher.reliableChannel.send(JSON.stringify({ clientId, dataType, data }));\n break;\n default:\n this.reliableTransport(clientId, dataType, data);\n break;\n }\n }\n }\n\n broadcastData(dataType, data) {\n if (!this.publisher) {\n console.warn(\"broadcastData called without a publisher\");\n } else {\n switch (this.unreliableTransport) {\n case \"websocket\":\n this.publisher.handle.sendMessage({ kind: \"data\", body: JSON.stringify({ dataType, data }) });\n break;\n case \"datachannel\":\n this.publisher.unreliableChannel.send(JSON.stringify({ dataType, data }));\n break;\n default:\n this.unreliableTransport(undefined, dataType, data);\n break;\n }\n }\n }\n\n broadcastDataGuaranteed(dataType, data) {\n if (!this.publisher) {\n console.warn(\"broadcastDataGuaranteed called without a publisher\");\n } else {\n switch (this.reliableTransport) {\n case \"websocket\":\n this.publisher.handle.sendMessage({ kind: \"data\", body: JSON.stringify({ dataType, data }) });\n break;\n case \"datachannel\":\n this.publisher.reliableChannel.send(JSON.stringify({ dataType, data }));\n break;\n default:\n this.reliableTransport(undefined, dataType, data);\n break;\n }\n }\n }\n\n kick(clientId, permsToken) {\n return this.publisher.handle.sendMessage({ kind: \"kick\", room_id: this.room, user_id: clientId, token: permsToken }).then(() => {\n document.body.dispatchEvent(new CustomEvent(\"kicked\", { detail: { clientId: clientId } }));\n });\n }\n\n block(clientId) {\n return this.publisher.handle.sendMessage({ kind: \"block\", whom: clientId }).then(() => {\n this.blockedClients.set(clientId, true);\n document.body.dispatchEvent(new CustomEvent(\"blocked\", { detail: { clientId: clientId } }));\n });\n }\n\n unblock(clientId) {\n return this.publisher.handle.sendMessage({ kind: \"unblock\", whom: clientId }).then(() => {\n this.blockedClients.delete(clientId);\n document.body.dispatchEvent(new CustomEvent(\"unblocked\", { detail: { clientId: clientId } }));\n });\n }\n}\n\nNAF.adapters.register(\"janus\", JanusAdapter);\n\nmodule.exports = JanusAdapter;\n","/* eslint-env node */\n'use strict';\n\n// SDP helpers.\nconst SDPUtils = {};\n\n// Generate an alphanumeric identifier for cname or mids.\n// TODO: use UUIDs instead? https://gist.github.com/jed/982883\nSDPUtils.generateIdentifier = function() {\n return Math.random().toString(36).substring(2, 12);\n};\n\n// The RTCP CNAME used by all peerconnections from the same JS.\nSDPUtils.localCName = SDPUtils.generateIdentifier();\n\n// Splits SDP into lines, dealing with both CRLF and LF.\nSDPUtils.splitLines = function(blob) {\n return blob.trim().split('\\n').map(line => line.trim());\n};\n// Splits SDP into sessionpart and mediasections. Ensures CRLF.\nSDPUtils.splitSections = function(blob) {\n const parts = blob.split('\\nm=');\n return parts.map((part, index) => (index > 0 ?\n 'm=' + part : part).trim() + '\\r\\n');\n};\n\n// Returns the session description.\nSDPUtils.getDescription = function(blob) {\n const sections = SDPUtils.splitSections(blob);\n return sections && sections[0];\n};\n\n// Returns the individual media sections.\nSDPUtils.getMediaSections = function(blob) {\n const sections = SDPUtils.splitSections(blob);\n sections.shift();\n return sections;\n};\n\n// Returns lines that start with a certain prefix.\nSDPUtils.matchPrefix = function(blob, prefix) {\n return SDPUtils.splitLines(blob).filter(line => line.indexOf(prefix) === 0);\n};\n\n// Parses an ICE candidate line. Sample input:\n// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8\n// rport 55996\"\n// Input can be prefixed with a=.\nSDPUtils.parseCandidate = function(line) {\n let parts;\n // Parse both variants.\n if (line.indexOf('a=candidate:') === 0) {\n parts = line.substring(12).split(' ');\n } else {\n parts = line.substring(10).split(' ');\n }\n\n const candidate = {\n foundation: parts[0],\n component: {1: 'rtp', 2: 'rtcp'}[parts[1]] || parts[1],\n protocol: parts[2].toLowerCase(),\n priority: parseInt(parts[3], 10),\n ip: parts[4],\n address: parts[4], // address is an alias for ip.\n port: parseInt(parts[5], 10),\n // skip parts[6] == 'typ'\n type: parts[7],\n };\n\n for (let i = 8; i < parts.length; i += 2) {\n switch (parts[i]) {\n case 'raddr':\n candidate.relatedAddress = parts[i + 1];\n break;\n case 'rport':\n candidate.relatedPort = parseInt(parts[i + 1], 10);\n break;\n case 'tcptype':\n candidate.tcpType = parts[i + 1];\n break;\n case 'ufrag':\n candidate.ufrag = parts[i + 1]; // for backward compatibility.\n candidate.usernameFragment = parts[i + 1];\n break;\n default: // extension handling, in particular ufrag. Don't overwrite.\n if (candidate[parts[i]] === undefined) {\n candidate[parts[i]] = parts[i + 1];\n }\n break;\n }\n }\n return candidate;\n};\n\n// Translates a candidate object into SDP candidate attribute.\n// This does not include the a= prefix!\nSDPUtils.writeCandidate = function(candidate) {\n const sdp = [];\n sdp.push(candidate.foundation);\n\n const component = candidate.component;\n if (component === 'rtp') {\n sdp.push(1);\n } else if (component === 'rtcp') {\n sdp.push(2);\n } else {\n sdp.push(component);\n }\n sdp.push(candidate.protocol.toUpperCase());\n sdp.push(candidate.priority);\n sdp.push(candidate.address || candidate.ip);\n sdp.push(candidate.port);\n\n const type = candidate.type;\n sdp.push('typ');\n sdp.push(type);\n if (type !== 'host' && candidate.relatedAddress &&\n candidate.relatedPort) {\n sdp.push('raddr');\n sdp.push(candidate.relatedAddress);\n sdp.push('rport');\n sdp.push(candidate.relatedPort);\n }\n if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {\n sdp.push('tcptype');\n sdp.push(candidate.tcpType);\n }\n if (candidate.usernameFragment || candidate.ufrag) {\n sdp.push('ufrag');\n sdp.push(candidate.usernameFragment || candidate.ufrag);\n }\n return 'candidate:' + sdp.join(' ');\n};\n\n// Parses an ice-options line, returns an array of option tags.\n// Sample input:\n// a=ice-options:foo bar\nSDPUtils.parseIceOptions = function(line) {\n return line.substring(14).split(' ');\n};\n\n// Parses a rtpmap line, returns RTCRtpCoddecParameters. Sample input:\n// a=rtpmap:111 opus/48000/2\nSDPUtils.parseRtpMap = function(line) {\n let parts = line.substring(9).split(' ');\n const parsed = {\n payloadType: parseInt(parts.shift(), 10), // was: id\n };\n\n parts = parts[0].split('/');\n\n parsed.name = parts[0];\n parsed.clockRate = parseInt(parts[1], 10); // was: clockrate\n parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1;\n // legacy alias, got renamed back to channels in ORTC.\n parsed.numChannels = parsed.channels;\n return parsed;\n};\n\n// Generates a rtpmap line from RTCRtpCodecCapability or\n// RTCRtpCodecParameters.\nSDPUtils.writeRtpMap = function(codec) {\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n const channels = codec.channels || codec.numChannels || 1;\n return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +\n (channels !== 1 ? '/' + channels : '') + '\\r\\n';\n};\n\n// Parses a extmap line (headerextension from RFC 5285). Sample input:\n// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\n// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset\nSDPUtils.parseExtmap = function(line) {\n const parts = line.substring(9).split(' ');\n return {\n id: parseInt(parts[0], 10),\n direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',\n uri: parts[1],\n attributes: parts.slice(2).join(' '),\n };\n};\n\n// Generates an extmap line from RTCRtpHeaderExtensionParameters or\n// RTCRtpHeaderExtension.\nSDPUtils.writeExtmap = function(headerExtension) {\n return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +\n (headerExtension.direction && headerExtension.direction !== 'sendrecv'\n ? '/' + headerExtension.direction\n : '') +\n ' ' + headerExtension.uri +\n (headerExtension.attributes ? ' ' + headerExtension.attributes : '') +\n '\\r\\n';\n};\n\n// Parses a fmtp line, returns dictionary. Sample input:\n// a=fmtp:96 vbr=on;cng=on\n// Also deals with vbr=on; cng=on\nSDPUtils.parseFmtp = function(line) {\n const parsed = {};\n let kv;\n const parts = line.substring(line.indexOf(' ') + 1).split(';');\n for (let j = 0; j < parts.length; j++) {\n kv = parts[j].trim().split('=');\n parsed[kv[0].trim()] = kv[1];\n }\n return parsed;\n};\n\n// Generates a fmtp line from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeFmtp = function(codec) {\n let line = '';\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n if (codec.parameters && Object.keys(codec.parameters).length) {\n const params = [];\n Object.keys(codec.parameters).forEach(param => {\n if (codec.parameters[param] !== undefined) {\n params.push(param + '=' + codec.parameters[param]);\n } else {\n params.push(param);\n }\n });\n line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\\r\\n';\n }\n return line;\n};\n\n// Parses a rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:\n// a=rtcp-fb:98 nack rpsi\nSDPUtils.parseRtcpFb = function(line) {\n const parts = line.substring(line.indexOf(' ') + 1).split(' ');\n return {\n type: parts.shift(),\n parameter: parts.join(' '),\n };\n};\n\n// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeRtcpFb = function(codec) {\n let lines = '';\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n if (codec.rtcpFeedback && codec.rtcpFeedback.length) {\n // FIXME: special handling for trr-int?\n codec.rtcpFeedback.forEach(fb => {\n lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +\n (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +\n '\\r\\n';\n });\n }\n return lines;\n};\n\n// Parses a RFC 5576 ssrc media attribute. Sample input:\n// a=ssrc:3735928559 cname:something\nSDPUtils.parseSsrcMedia = function(line) {\n const sp = line.indexOf(' ');\n const parts = {\n ssrc: parseInt(line.substring(7, sp), 10),\n };\n const colon = line.indexOf(':', sp);\n if (colon > -1) {\n parts.attribute = line.substring(sp + 1, colon);\n parts.value = line.substring(colon + 1);\n } else {\n parts.attribute = line.substring(sp + 1);\n }\n return parts;\n};\n\n// Parse a ssrc-group line (see RFC 5576). Sample input:\n// a=ssrc-group:semantics 12 34\nSDPUtils.parseSsrcGroup = function(line) {\n const parts = line.substring(13).split(' ');\n return {\n semantics: parts.shift(),\n ssrcs: parts.map(ssrc => parseInt(ssrc, 10)),\n };\n};\n\n// Extracts the MID (RFC 5888) from a media section.\n// Returns the MID or undefined if no mid line was found.\nSDPUtils.getMid = function(mediaSection) {\n const mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];\n if (mid) {\n return mid.substring(6);\n }\n};\n\n// Parses a fingerprint line for DTLS-SRTP.\nSDPUtils.parseFingerprint = function(line) {\n const parts = line.substring(14).split(' ');\n return {\n algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.\n value: parts[1].toUpperCase(), // the definition is upper-case in RFC 4572.\n };\n};\n\n// Extracts DTLS parameters from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n// get the fingerprint line as input. See also getIceParameters.\nSDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {\n const lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=fingerprint:');\n // Note: a=setup line is ignored since we use the 'auto' role in Edge.\n return {\n role: 'auto',\n fingerprints: lines.map(SDPUtils.parseFingerprint),\n };\n};\n\n// Serializes DTLS parameters to SDP.\nSDPUtils.writeDtlsParameters = function(params, setupType) {\n let sdp = 'a=setup:' + setupType + '\\r\\n';\n params.fingerprints.forEach(fp => {\n sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\\r\\n';\n });\n return sdp;\n};\n\n// Parses a=crypto lines into\n// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members\nSDPUtils.parseCryptoLine = function(line) {\n const parts = line.substring(9).split(' ');\n return {\n tag: parseInt(parts[0], 10),\n cryptoSuite: parts[1],\n keyParams: parts[2],\n sessionParams: parts.slice(3),\n };\n};\n\nSDPUtils.writeCryptoLine = function(parameters) {\n return 'a=crypto:' + parameters.tag + ' ' +\n parameters.cryptoSuite + ' ' +\n (typeof parameters.keyParams === 'object'\n ? SDPUtils.writeCryptoKeyParams(parameters.keyParams)\n : parameters.keyParams) +\n (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') +\n '\\r\\n';\n};\n\n// Parses the crypto key parameters into\n// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam*\nSDPUtils.parseCryptoKeyParams = function(keyParams) {\n if (keyParams.indexOf('inline:') !== 0) {\n return null;\n }\n const parts = keyParams.substring(7).split('|');\n return {\n keyMethod: 'inline',\n keySalt: parts[0],\n lifeTime: parts[1],\n mkiValue: parts[2] ? parts[2].split(':')[0] : undefined,\n mkiLength: parts[2] ? parts[2].split(':')[1] : undefined,\n };\n};\n\nSDPUtils.writeCryptoKeyParams = function(keyParams) {\n return keyParams.keyMethod + ':'\n + keyParams.keySalt +\n (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') +\n (keyParams.mkiValue && keyParams.mkiLength\n ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength\n : '');\n};\n\n// Extracts all SDES parameters.\nSDPUtils.getCryptoParameters = function(mediaSection, sessionpart) {\n const lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=crypto:');\n return lines.map(SDPUtils.parseCryptoLine);\n};\n\n// Parses ICE information from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n// get the ice-ufrag and ice-pwd lines as input.\nSDPUtils.getIceParameters = function(mediaSection, sessionpart) {\n const ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=ice-ufrag:')[0];\n const pwd = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=ice-pwd:')[0];\n if (!(ufrag && pwd)) {\n return null;\n }\n return {\n usernameFragment: ufrag.substring(12),\n password: pwd.substring(10),\n };\n};\n\n// Serializes ICE parameters to SDP.\nSDPUtils.writeIceParameters = function(params) {\n let sdp = 'a=ice-ufrag:' + params.usernameFragment + '\\r\\n' +\n 'a=ice-pwd:' + params.password + '\\r\\n';\n if (params.iceLite) {\n sdp += 'a=ice-lite\\r\\n';\n }\n return sdp;\n};\n\n// Parses the SDP media section and returns RTCRtpParameters.\nSDPUtils.parseRtpParameters = function(mediaSection) {\n const description = {\n codecs: [],\n headerExtensions: [],\n fecMechanisms: [],\n rtcp: [],\n };\n const lines = SDPUtils.splitLines(mediaSection);\n const mline = lines[0].split(' ');\n description.profile = mline[2];\n for (let i = 3; i < mline.length; i++) { // find all codecs from mline[3..]\n const pt = mline[i];\n const rtpmapline = SDPUtils.matchPrefix(\n mediaSection, 'a=rtpmap:' + pt + ' ')[0];\n if (rtpmapline) {\n const codec = SDPUtils.parseRtpMap(rtpmapline);\n const fmtps = SDPUtils.matchPrefix(\n mediaSection, 'a=fmtp:' + pt + ' ');\n // Only the first a=fmtp: is considered.\n codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};\n codec.rtcpFeedback = SDPUtils.matchPrefix(\n mediaSection, 'a=rtcp-fb:' + pt + ' ')\n .map(SDPUtils.parseRtcpFb);\n description.codecs.push(codec);\n // parse FEC mechanisms from rtpmap lines.\n switch (codec.name.toUpperCase()) {\n case 'RED':\n case 'ULPFEC':\n description.fecMechanisms.push(codec.name.toUpperCase());\n break;\n default: // only RED and ULPFEC are recognized as FEC mechanisms.\n break;\n }\n }\n }\n SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(line => {\n description.headerExtensions.push(SDPUtils.parseExtmap(line));\n });\n const wildcardRtcpFb = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-fb:* ')\n .map(SDPUtils.parseRtcpFb);\n description.codecs.forEach(codec => {\n wildcardRtcpFb.forEach(fb=> {\n const duplicate = codec.rtcpFeedback.find(existingFeedback => {\n return existingFeedback.type === fb.type &&\n existingFeedback.parameter === fb.parameter;\n });\n if (!duplicate) {\n codec.rtcpFeedback.push(fb);\n }\n });\n });\n // FIXME: parse rtcp.\n return description;\n};\n\n// Generates parts of the SDP media section describing the capabilities /\n// parameters.\nSDPUtils.writeRtpDescription = function(kind, caps) {\n let sdp = '';\n\n // Build the mline.\n sdp += 'm=' + kind + ' ';\n sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.\n sdp += ' ' + (caps.profile || 'UDP/TLS/RTP/SAVPF') + ' ';\n sdp += caps.codecs.map(codec => {\n if (codec.preferredPayloadType !== undefined) {\n return codec.preferredPayloadType;\n }\n return codec.payloadType;\n }).join(' ') + '\\r\\n';\n\n sdp += 'c=IN IP4 0.0.0.0\\r\\n';\n sdp += 'a=rtcp:9 IN IP4 0.0.0.0\\r\\n';\n\n // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.\n caps.codecs.forEach(codec => {\n sdp += SDPUtils.writeRtpMap(codec);\n sdp += SDPUtils.writeFmtp(codec);\n sdp += SDPUtils.writeRtcpFb(codec);\n });\n let maxptime = 0;\n caps.codecs.forEach(codec => {\n if (codec.maxptime > maxptime) {\n maxptime = codec.maxptime;\n }\n });\n if (maxptime > 0) {\n sdp += 'a=maxptime:' + maxptime + '\\r\\n';\n }\n\n if (caps.headerExtensions) {\n caps.headerExtensions.forEach(extension => {\n sdp += SDPUtils.writeExtmap(extension);\n });\n }\n // FIXME: write fecMechanisms.\n return sdp;\n};\n\n// Parses the SDP media section and returns an array of\n// RTCRtpEncodingParameters.\nSDPUtils.parseRtpEncodingParameters = function(mediaSection) {\n const encodingParameters = [];\n const description = SDPUtils.parseRtpParameters(mediaSection);\n const hasRed = description.fecMechanisms.indexOf('RED') !== -1;\n const hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;\n\n // filter a=ssrc:... cname:, ignore PlanB-msid\n const ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(parts => parts.attribute === 'cname');\n const primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;\n let secondarySsrc;\n\n const flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')\n .map(line => {\n const parts = line.substring(17).split(' ');\n return parts.map(part => parseInt(part, 10));\n });\n if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {\n secondarySsrc = flows[0][1];\n }\n\n description.codecs.forEach(codec => {\n if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {\n let encParam = {\n ssrc: primarySsrc,\n codecPayloadType: parseInt(codec.parameters.apt, 10),\n };\n if (primarySsrc && secondarySsrc) {\n encParam.rtx = {ssrc: secondarySsrc};\n }\n encodingParameters.push(encParam);\n if (hasRed) {\n encParam = JSON.parse(JSON.stringify(encParam));\n encParam.fec = {\n ssrc: primarySsrc,\n mechanism: hasUlpfec ? 'red+ulpfec' : 'red',\n };\n encodingParameters.push(encParam);\n }\n }\n });\n if (encodingParameters.length === 0 && primarySsrc) {\n encodingParameters.push({\n ssrc: primarySsrc,\n });\n }\n\n // we support both b=AS and b=TIAS but interpret AS as TIAS.\n let bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');\n if (bandwidth.length) {\n if (bandwidth[0].indexOf('b=TIAS:') === 0) {\n bandwidth = parseInt(bandwidth[0].substring(7), 10);\n } else if (bandwidth[0].indexOf('b=AS:') === 0) {\n // use formula from JSEP to convert b=AS to TIAS value.\n bandwidth = parseInt(bandwidth[0].substring(5), 10) * 1000 * 0.95\n - (50 * 40 * 8);\n } else {\n bandwidth = undefined;\n }\n encodingParameters.forEach(params => {\n params.maxBitrate = bandwidth;\n });\n }\n return encodingParameters;\n};\n\n// parses http://draft.ortc.org/#rtcrtcpparameters*\nSDPUtils.parseRtcpParameters = function(mediaSection) {\n const rtcpParameters = {};\n\n // Gets the first SSRC. Note that with RTX there might be multiple\n // SSRCs.\n const remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(obj => obj.attribute === 'cname')[0];\n if (remoteSsrc) {\n rtcpParameters.cname = remoteSsrc.value;\n rtcpParameters.ssrc = remoteSsrc.ssrc;\n }\n\n // Edge uses the compound attribute instead of reducedSize\n // compound is !reducedSize\n const rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');\n rtcpParameters.reducedSize = rsize.length > 0;\n rtcpParameters.compound = rsize.length === 0;\n\n // parses the rtcp-mux attrŅ–bute.\n // Note that Edge does not support unmuxed RTCP.\n const mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');\n rtcpParameters.mux = mux.length > 0;\n\n return rtcpParameters;\n};\n\nSDPUtils.writeRtcpParameters = function(rtcpParameters) {\n let sdp = '';\n if (rtcpParameters.reducedSize) {\n sdp += 'a=rtcp-rsize\\r\\n';\n }\n if (rtcpParameters.mux) {\n sdp += 'a=rtcp-mux\\r\\n';\n }\n if (rtcpParameters.ssrc !== undefined && rtcpParameters.cname) {\n sdp += 'a=ssrc:' + rtcpParameters.ssrc +\n ' cname:' + rtcpParameters.cname + '\\r\\n';\n }\n return sdp;\n};\n\n\n// parses either a=msid: or a=ssrc:... msid lines and returns\n// the id of the MediaStream and MediaStreamTrack.\nSDPUtils.parseMsid = function(mediaSection) {\n let parts;\n const spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');\n if (spec.length === 1) {\n parts = spec[0].substring(7).split(' ');\n return {stream: parts[0], track: parts[1]};\n }\n const planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(msidParts => msidParts.attribute === 'msid');\n if (planB.length > 0) {\n parts = planB[0].value.split(' ');\n return {stream: parts[0], track: parts[1]};\n }\n};\n\n// SCTP\n// parses draft-ietf-mmusic-sctp-sdp-26 first and falls back\n// to draft-ietf-mmusic-sctp-sdp-05\nSDPUtils.parseSctpDescription = function(mediaSection) {\n const mline = SDPUtils.parseMLine(mediaSection);\n const maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:');\n let maxMessageSize;\n if (maxSizeLine.length > 0) {\n maxMessageSize = parseInt(maxSizeLine[0].substring(19), 10);\n }\n if (isNaN(maxMessageSize)) {\n maxMessageSize = 65536;\n }\n const sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:');\n if (sctpPort.length > 0) {\n return {\n port: parseInt(sctpPort[0].substring(12), 10),\n protocol: mline.fmt,\n maxMessageSize,\n };\n }\n const sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:');\n if (sctpMapLines.length > 0) {\n const parts = sctpMapLines[0]\n .substring(10)\n .split(' ');\n return {\n port: parseInt(parts[0], 10),\n protocol: parts[1],\n maxMessageSize,\n };\n }\n};\n\n// SCTP\n// outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers\n// support by now receiving in this format, unless we originally parsed\n// as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line\n// protocol of DTLS/SCTP -- without UDP/ or TCP/)\nSDPUtils.writeSctpDescription = function(media, sctp) {\n let output = [];\n if (media.protocol !== 'DTLS/SCTP') {\n output = [\n 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\\r\\n',\n 'c=IN IP4 0.0.0.0\\r\\n',\n 'a=sctp-port:' + sctp.port + '\\r\\n',\n ];\n } else {\n output = [\n 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\\r\\n',\n 'c=IN IP4 0.0.0.0\\r\\n',\n 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\\r\\n',\n ];\n }\n if (sctp.maxMessageSize !== undefined) {\n output.push('a=max-message-size:' + sctp.maxMessageSize + '\\r\\n');\n }\n return output.join('');\n};\n\n// Generate a session ID for SDP.\n// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1\n// recommends using a cryptographically random +ve 64-bit value\n// but right now this should be acceptable and within the right range\nSDPUtils.generateSessionId = function() {\n return Math.random().toString().substr(2, 22);\n};\n\n// Write boiler plate for start of SDP\n// sessId argument is optional - if not supplied it will\n// be generated randomly\n// sessVersion is optional and defaults to 2\n// sessUser is optional and defaults to 'thisisadapterortc'\nSDPUtils.writeSessionBoilerplate = function(sessId, sessVer, sessUser) {\n let sessionId;\n const version = sessVer !== undefined ? sessVer : 2;\n if (sessId) {\n sessionId = sessId;\n } else {\n sessionId = SDPUtils.generateSessionId();\n }\n const user = sessUser || 'thisisadapterortc';\n // FIXME: sess-id should be an NTP timestamp.\n return 'v=0\\r\\n' +\n 'o=' + user + ' ' + sessionId + ' ' + version +\n ' IN IP4 127.0.0.1\\r\\n' +\n 's=-\\r\\n' +\n 't=0 0\\r\\n';\n};\n\n// Gets the direction from the mediaSection or the sessionpart.\nSDPUtils.getDirection = function(mediaSection, sessionpart) {\n // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.\n const lines = SDPUtils.splitLines(mediaSection);\n for (let i = 0; i < lines.length; i++) {\n switch (lines[i]) {\n case 'a=sendrecv':\n case 'a=sendonly':\n case 'a=recvonly':\n case 'a=inactive':\n return lines[i].substring(2);\n default:\n // FIXME: What should happen here?\n }\n }\n if (sessionpart) {\n return SDPUtils.getDirection(sessionpart);\n }\n return 'sendrecv';\n};\n\nSDPUtils.getKind = function(mediaSection) {\n const lines = SDPUtils.splitLines(mediaSection);\n const mline = lines[0].split(' ');\n return mline[0].substring(2);\n};\n\nSDPUtils.isRejected = function(mediaSection) {\n return mediaSection.split(' ', 2)[1] === '0';\n};\n\nSDPUtils.parseMLine = function(mediaSection) {\n const lines = SDPUtils.splitLines(mediaSection);\n const parts = lines[0].substring(2).split(' ');\n return {\n kind: parts[0],\n port: parseInt(parts[1], 10),\n protocol: parts[2],\n fmt: parts.slice(3).join(' '),\n };\n};\n\nSDPUtils.parseOLine = function(mediaSection) {\n const line = SDPUtils.matchPrefix(mediaSection, 'o=')[0];\n const parts = line.substring(2).split(' ');\n return {\n username: parts[0],\n sessionId: parts[1],\n sessionVersion: parseInt(parts[2], 10),\n netType: parts[3],\n addressType: parts[4],\n address: parts[5],\n };\n};\n\n// a very naive interpretation of a valid SDP.\nSDPUtils.isValidSDP = function(blob) {\n if (typeof blob !== 'string' || blob.length === 0) {\n return false;\n }\n const lines = SDPUtils.splitLines(blob);\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].length < 2 || lines[i].charAt(1) !== '=') {\n return false;\n }\n // TODO: check the modifier a bit more.\n }\n return true;\n};\n\n// Expose public methods.\nif (typeof module === 'object') {\n module.exports = SDPUtils;\n}\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// startup\n// Load entry module and return exports\n// This entry module is referenced by other modules so it can't be inlined\nvar __webpack_exports__ = __webpack_require__(579);\n"],"names":["JanusPluginHandle","session","this","id","undefined","JanusSession","output","options","nextTxId","txns","eventHandlers","Object","assign","verbose","timeoutMs","keepaliveMs","prototype","attach","plugin","loop_index","payload","send","then","resp","data","detach","on","ev","callback","signal","sender","type","handle_id","sendMessage","body","sendJsep","jsep","sendTrickle","candidate","create","destroy","dispose","txId","_killKeepalive","hasOwnProperty","txn","clearTimeout","timeout","reject","Error","isError","janus","handlers","push","receive","_logIncoming","session_id","console","warn","responseType","i","length","transaction","resolve","toString","Promise","setTimeout","_transmit","_logOutgoing","JSON","stringify","_resetKeepalive","kind","message","debug","_sendKeepalive","keepaliveTimeout","catch","e","error","module","exports","_regeneratorRuntime","Op","hasOwn","defineProperty","obj","key","desc","value","$Symbol","Symbol","iteratorSymbol","iterator","asyncIteratorSymbol","asyncIterator","toStringTagSymbol","toStringTag","define","enumerable","configurable","writable","err","wrap","innerFn","outerFn","self","tryLocsList","protoGenerator","Generator","generator","context","Context","makeInvokeMethod","tryCatch","fn","arg","call","ContinueSentinel","GeneratorFunction","GeneratorFunctionPrototype","IteratorPrototype","getProto","getPrototypeOf","NativeIteratorPrototype","values","Gp","defineIteratorMethods","forEach","method","_invoke","AsyncIterator","PromiseImpl","invoke","record","result","_typeof","__await","unwrapped","previousPromise","callInvokeWithMethodAndArg","state","done","delegate","delegateResult","maybeInvokeDelegate","sent","_sent","dispatchException","abrupt","methodName","TypeError","info","resultName","next","nextLoc","pushTryEntry","locs","entry","tryLoc","catchLoc","finallyLoc","afterLoc","tryEntries","resetTryEntry","completion","reset","iterable","iteratorMethod","isNaN","doneResult","displayName","isGeneratorFunction","genFun","ctor","constructor","name","mark","setPrototypeOf","__proto__","awrap","async","iter","keys","val","object","reverse","pop","skipTempReset","prev","charAt","slice","stop","rootRecord","rval","exception","handle","loc","caught","hasCatch","hasFinally","finallyEntry","complete","finish","thrown","delegateYield","asyncGeneratorStep","gen","_next","_throw","_asyncToGenerator","args","arguments","apply","_defineProperties","target","props","descriptor","input","hint","prim","toPrimitive","res","String","_toPrimitive","mj","require","sendOriginal","indexOf","NAF","connection","adapter","reconnect","sdpUtils","log","isSafari","test","navigator","userAgent","debounce","curr","_this","Array","_","untilDataChannelOpen","dataChannel","readyState","resolver","rejector","clear","removeEventListener","addEventListener","isH264VideoSupported","document","createElement","canPlayType","OPUS_PARAMETERS","usedtx","stereo","DEFAULT_PEER_CONNECTION_CONFIG","iceServers","urls","JanusAdapter","instance","Constructor","_classCallCheck","room","clientId","joinToken","serverUrl","webRtcOptions","peerConnectionConfig","ws","reliableTransport","unreliableTransport","initialReconnectionDelay","Math","random","reconnectionDelay","reconnectionTimeout","maxReconnectionAttempts","reconnectionAttempts","publisher","occupants","leftOccupants","Set","mediaStreams","localMediaStream","pendingMediaRequests","Map","blockedClients","frozenUpdates","timeOffsets","serverTimeRequests","avgTimeOffset","onWebsocketOpen","bind","onWebsocketClose","onWebsocketMessage","onDataChannelMessage","onData","protoProps","_setLocalMediaStream","_updateTimeOffset","_createSubscriber","_fixSafariIceUFrag","_createPublisher","_addOccupant","_onWebsocketOpen","url","app","roomName","successListener","failureListener","connectSuccess","connectFailure","occupantListener","onOccupantsChanged","openListener","closedListener","messageListener","onOccupantConnected","onOccupantDisconnected","onOccupantMessage","reconnectingListener","reconnectedListener","reconnectionErrorListener","onReconnecting","onReconnected","onReconnectionError","loops","_this2","concat","websocketConnection","WebSocket","wsOnOpen","all","updateTimeOffset","removeAllOccupants","conn","close","delayedReconnectTimeout","_callee","addOccupantPromises","occupantId","_context","createPublisher","initialOccupants","addOccupant","event","_this3","code","_this4","disconnect","connect","_this5","parse","_callee2","subscriber","_context2","removeOccupant","createSubscriber","setMediaStream","mediaStream","_x","_step","_iterator","_createForOfIteratorHelper","getOwnPropertyNames","s","n","f","add","has","msg","get","audio","video","_this6","iceConnectionState","performDelayedReconnect","offer","createOffer","configurePublisherSdp","fixSafariIceUFrag","local","o","setLocalDescription","remote","j","r","setRemoteDescription","answer","configureSubscriberSdp","createAnswer","a","_callee3","webrtcup","reliableChannel","unreliableChannel","_this7","_context3","RTCPeerConnection","parseInt","associate","createDataChannel","ordered","maxRetransmits","getTracks","track","addTrack","plugindata","room_id","user_id","dispatchEvent","CustomEvent","detail","by","sendJoin","notifications","success","response","users","includes","sdp","replace","line","pt","parameters","parseFmtp","writeFmtp","payloadType","_callee4","_context4","_x2","_callee5","maxRetries","webrtcFailed","_this8","_args5","_context5","leftInterval","setInterval","clearInterval","media","_iOSHackDelayedInitialPeer","MediaStream","getReceivers","receiver","_x3","subscribe","token","frozen","unfreeze","freeze","flushPendingUpdates","networkId","l","d","dataType","dataForUpdateMultiMessage","owner","getPendingData","_step2","_iterator2","_step2$value","source","storeSingleMessage","index","storedMessage","storedData","isOutdatedMessage","lastOwnerTime","isContemporaneousMessage","isFirstSync","set","components","enabled","storeMessage","client","adapters","IS_CONNECTED","NOT_CONNECTED","_callee6","clientSentTime","serverReceivedTime","clientReceivedTime","timeOffset","_this9","_context6","isDisconnected","Date","now","fetch","location","href","cache","headers","getTime","precision","reduce","acc","offset","_this10","audioPromise","videoPromise","promise","stream","audioStream","getAudioTracks","videoStream","getVideoTracks","_callee7","existingSenders","newSenders","tracks","_loop","_this11","_context8","getSenders","t","_context7","find","replaceTrack","toLowerCase","removeTrack","_x4","whom","permsToken","_this12","_this13","register","SDPUtils","substring","localCName","generateIdentifier","splitLines","blob","trim","split","map","splitSections","part","getDescription","sections","getMediaSections","shift","matchPrefix","prefix","filter","parseCandidate","parts","foundation","component","protocol","priority","ip","address","port","relatedAddress","relatedPort","tcpType","ufrag","usernameFragment","writeCandidate","toUpperCase","join","parseIceOptions","parseRtpMap","parsed","clockRate","channels","numChannels","writeRtpMap","codec","preferredPayloadType","parseExtmap","direction","uri","attributes","writeExtmap","headerExtension","preferredId","kv","params","param","parseRtcpFb","parameter","writeRtcpFb","lines","rtcpFeedback","fb","parseSsrcMedia","sp","ssrc","colon","attribute","parseSsrcGroup","semantics","ssrcs","getMid","mediaSection","mid","parseFingerprint","algorithm","getDtlsParameters","sessionpart","role","fingerprints","writeDtlsParameters","setupType","fp","parseCryptoLine","tag","cryptoSuite","keyParams","sessionParams","writeCryptoLine","writeCryptoKeyParams","parseCryptoKeyParams","keyMethod","keySalt","lifeTime","mkiValue","mkiLength","getCryptoParameters","getIceParameters","pwd","password","writeIceParameters","iceLite","parseRtpParameters","description","codecs","headerExtensions","fecMechanisms","rtcp","mline","profile","rtpmapline","fmtps","wildcardRtcpFb","existingFeedback","writeRtpDescription","caps","maxptime","extension","parseRtpEncodingParameters","encodingParameters","hasRed","hasUlpfec","primarySsrc","secondarySsrc","flows","apt","encParam","codecPayloadType","rtx","fec","mechanism","bandwidth","maxBitrate","parseRtcpParameters","rtcpParameters","remoteSsrc","cname","rsize","reducedSize","compound","mux","writeRtcpParameters","parseMsid","spec","planB","msidParts","parseSctpDescription","parseMLine","maxSizeLine","maxMessageSize","sctpPort","fmt","sctpMapLines","writeSctpDescription","sctp","generateSessionId","substr","writeSessionBoilerplate","sessId","sessVer","sessUser","sessionId","version","getDirection","getKind","isRejected","parseOLine","username","sessionVersion","netType","addressType","isValidSDP","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","__webpack_modules__"],"sourceRoot":""} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ca32061..c051b02 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "naf-janus-adapter", + "name": "@networked-aframe/naf-janus-adapter", "version": "3.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "naf-janus-adapter", + "name": "@networked-aframe/naf-janus-adapter", "version": "3.1.0", "license": "MPL-2.0", "dependencies": {